1
0
Fork 0
mirror of synced 2025-03-06 20:59:54 +01:00
linux/drivers/mtd/nand/raw/nand_jedec.c
Miquel Raynal 9f820fc065 mtd: rawnand: Check the data only read pattern only once
Instead of checking if a pattern is supported each time we need it,
let's create a bitfield that only the core would be allowed to fill at
startup time. The core and the individual drivers may then use it in
order to check what operation they should use. This bitfield is supposed
to grow over time.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Liao Jaime <jaimeliao.tw@gmail.com>
Link: https://lore.kernel.org/linux-mtd/20230112093637.987838-2-miquel.raynal@bootlin.com
2023-01-13 17:35:26 +01:00

138 lines
3.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* 2002-2006 Thomas Gleixner (tglx@linutronix.de)
*
* Credits:
* David Woodhouse for adding multichip support
*
* Aleph One Ltd. and Toby Churchill Ltd. for supporting the
* rework for 2K page size chips
*
* This file contains all ONFI helpers.
*/
#include <linux/slab.h>
#include "internals.h"
#define JEDEC_PARAM_PAGES 3
/*
* Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
*/
int nand_jedec_detect(struct nand_chip *chip)
{
struct nand_device *base = &chip->base;
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_memory_organization *memorg;
struct nand_jedec_params *p;
struct jedec_ecc_info *ecc;
bool use_datain = false;
int jedec_version = 0;
char id[5];
int i, val, ret;
u16 crc;
memorg = nanddev_get_memorg(&chip->base);
/* Try JEDEC for unknown chip or LP */
ret = nand_readid_op(chip, 0x40, id, sizeof(id));
if (ret || strncmp(id, "JEDEC", sizeof(id)))
return 0;
/* JEDEC chip: allocate a buffer to hold its parameter page */
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return -ENOMEM;
if (!nand_has_exec_op(chip) || chip->controller->supported_op.data_only_read)
use_datain = true;
for (i = 0; i < JEDEC_PARAM_PAGES; i++) {
if (!i)
ret = nand_read_param_page_op(chip, 0x40, p,
sizeof(*p));
else if (use_datain)
ret = nand_read_data_op(chip, p, sizeof(*p), true,
false);
else
ret = nand_change_read_column_op(chip, sizeof(*p) * i,
p, sizeof(*p), true);
if (ret) {
ret = 0;
goto free_jedec_param_page;
}
crc = onfi_crc16(ONFI_CRC_BASE, (u8 *)p, 510);
if (crc == le16_to_cpu(p->crc))
break;
}
if (i == JEDEC_PARAM_PAGES) {
pr_err("Could not find valid JEDEC parameter page; aborting\n");
goto free_jedec_param_page;
}
/* Check version */
val = le16_to_cpu(p->revision);
if (val & (1 << 2))
jedec_version = 10;
else if (val & (1 << 1))
jedec_version = 1; /* vendor specific version */
if (!jedec_version) {
pr_info("unsupported JEDEC version: %d\n", val);
goto free_jedec_param_page;
}
sanitize_string(p->manufacturer, sizeof(p->manufacturer));
sanitize_string(p->model, sizeof(p->model));
chip->parameters.model = kstrdup(p->model, GFP_KERNEL);
if (!chip->parameters.model) {
ret = -ENOMEM;
goto free_jedec_param_page;
}
memorg->pagesize = le32_to_cpu(p->byte_per_page);
mtd->writesize = memorg->pagesize;
/* Please reference to the comment for nand_flash_detect_onfi. */
memorg->pages_per_eraseblock =
1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
mtd->erasesize = memorg->pages_per_eraseblock * memorg->pagesize;
memorg->oobsize = le16_to_cpu(p->spare_bytes_per_page);
mtd->oobsize = memorg->oobsize;
memorg->luns_per_target = p->lun_count;
memorg->planes_per_lun = 1 << p->multi_plane_addr;
/* Please reference to the comment for nand_flash_detect_onfi. */
memorg->eraseblocks_per_lun =
1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
memorg->bits_per_cell = p->bits_per_cell;
if (le16_to_cpu(p->features) & JEDEC_FEATURE_16_BIT_BUS)
chip->options |= NAND_BUSWIDTH_16;
/* ECC info */
ecc = &p->ecc_info[0];
if (ecc->codeword_size >= 9) {
struct nand_ecc_props requirements = {
.strength = ecc->ecc_bits,
.step_size = 1 << ecc->codeword_size,
};
nanddev_set_ecc_requirements(base, &requirements);
} else {
pr_warn("Invalid codeword size\n");
}
ret = 1;
free_jedec_param_page:
kfree(p);
return ret;
}