diff options
Diffstat (limited to 'drivers/mtd/nand/raw/nand_jedec.c')
-rw-r--r-- | drivers/mtd/nand/raw/nand_jedec.c | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/drivers/mtd/nand/raw/nand_jedec.c b/drivers/mtd/nand/raw/nand_jedec.c new file mode 100644 index 000000000..85b6d9372 --- /dev/null +++ b/drivers/mtd/nand/raw/nand_jedec.c @@ -0,0 +1,139 @@ +// 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) || + !nand_read_data_op(chip, p, sizeof(*p), true, true)) + 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; +} |