Instantiate the onfi_helper object for raw NAND and turn ONFI support to generic. Later this generic ONFI code will be used by SPI NAND as well. Signed-off-by: Shivamurthy Shastri <sshivamurthy@xxxxxxxxxx> --- drivers/mtd/nand/raw/nand_base.c | 218 ++++++++++++++++++++++++++++++- drivers/mtd/nand/raw/nand_onfi.c | 183 +++----------------------- 2 files changed, 232 insertions(+), 169 deletions(-) diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index c28ed2da733a..f2f0d3cfa333 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4624,6 +4624,222 @@ nand_manufacturer_name(const struct nand_manufacturer *manufacturer) return manufacturer ? manufacturer->name : "Unknown"; } +/* Parse the ONFI parameter Page */ +int nand_onfi_read_op(struct nand_device *base, u8 page, void *buf, + unsigned int len) +{ + struct nand_chip *chip = device_to_nand(base); + struct nand_onfi_params *p = buf; + int ret; + unsigned int i; + + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) + return ret; + + for (i = 0; i < 3; i++) { + ret = nand_read_data_op(chip, &p[i], sizeof(*p), true); + if (ret) + return ret; + } + + return 0; +} + +/* Parse the Extended Parameter Page. */ +static int nand_ext_param_page(struct nand_chip *chip, + struct nand_onfi_params *p) +{ + struct onfi_ext_param_page *ep; + struct onfi_ext_section *s; + struct onfi_ext_ecc_info *ecc; + u8 *cursor; + int ret; + int len; + int i; + + /* + * The nand_flash_detect_ext_param_page() uses the + * Change Read Column command which maybe not supported + * by the chip->legacy.cmdfunc. So try to update the + * chip->legacy.cmdfunc now. We do not replace user supplied + * command function. + */ + nand_legacy_adjust_cmdfunc(chip); + + len = le16_to_cpu(p->ext_param_page_length) * 16; + ep = kmalloc(len, GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Send our own NAND_CMD_PARAM. */ + ret = nand_read_param_page_op(chip, 0, NULL, 0); + if (ret) + goto ext_out; + + /* Use the Change Read Column command to skip the ONFI param pages. */ + ret = nand_change_read_column_op(chip, + sizeof(*p) * p->num_of_param_pages, + ep, len, true); + if (ret) + goto ext_out; + + ret = -EINVAL; + if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) + != le16_to_cpu(ep->crc))) { + pr_debug("fail in the CRC.\n"); + goto ext_out; + } + + /* + * Check the signature. + * Do not strictly follow the ONFI spec, maybe changed in future. + */ + if (strncmp(ep->sig, "EPPS", 4)) { + pr_debug("The signature is invalid.\n"); + goto ext_out; + } + + /* find the ECC section. */ + cursor = (uint8_t *)(ep + 1); + for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { + s = ep->sections + i; + if (s->type == ONFI_SECTION_TYPE_2) + break; + cursor += s->length * 16; + } + if (i == ONFI_EXT_SECTION_MAX) { + pr_debug("We can not find the ECC section.\n"); + goto ext_out; + } + + /* get the info we want. */ + ecc = (struct onfi_ext_ecc_info *)cursor; + + if (!ecc->codeword_size) { + pr_debug("Invalid codeword size\n"); + goto ext_out; + } + + chip->base.eccreq.strength = ecc->ecc_bits; + chip->base.eccreq.step_size = 1 << ecc->codeword_size; + ret = 0; + +ext_out: + kfree(ep); + return ret; +} + +static int check_onfi_version(struct nand_device *base, + struct nand_onfi_params *p, int *onfi_version) +{ + struct nand_chip *chip = device_to_nand(base); + int val; + + if (chip->manufacturer.desc && chip->manufacturer.desc->ops && + chip->manufacturer.desc->ops->fixup_onfi_param_page) + chip->manufacturer.desc->ops->fixup_onfi_param_page(chip, p); + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & ONFI_VERSION_2_3) + *onfi_version = 23; + else if (val & ONFI_VERSION_2_2) + *onfi_version = 22; + else if (val & ONFI_VERSION_2_1) + *onfi_version = 21; + else if (val & ONFI_VERSION_2_0) + *onfi_version = 20; + else if (val & ONFI_VERSION_1_0) + *onfi_version = 10; + + if (!(*onfi_version)) { + pr_info("unsupported ONFI version: %d\n", val); + return -1; + } + + return 0; +} + +static int nand_intf_data(struct nand_device *base, struct nand_onfi_params *p) +{ + struct nand_chip *chip = device_to_nand(base); + struct onfi_params *onfi; + int onfi_version; + int ret; + + check_onfi_version(base, p, &onfi_version); + + /* The Extended Parameter Page is supported since ONFI 2.1. */ + if (onfi_version >= 21 && + (le16_to_cpu(p->features) & + ONFI_FEATURE_EXT_PARAM_PAGE)) { + if (nand_ext_param_page(chip, p)) + pr_warn("Failed to detect ONFI extended param page\n"); + } + + chip->parameters.model = kstrdup(p->model, GFP_KERNEL); + if (!chip->parameters.model) + return -ENOMEM; + + if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS) + chip->options |= NAND_BUSWIDTH_16; + + /* Save some parameters from the parameter page for future use */ + if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) { + chip->parameters.supports_set_get_features = true; + bitmap_set(chip->parameters.get_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + bitmap_set(chip->parameters.set_feature_list, + ONFI_FEATURE_ADDR_TIMING_MODE, 1); + } + + onfi = kzalloc(sizeof(*onfi), GFP_KERNEL); + if (!onfi) { + return ret = -ENOMEM; + goto free_model; + } + + check_onfi_version(base, p, &onfi_version); + onfi->version = onfi_version; + onfi->tPROG = le16_to_cpu(p->t_prog); + onfi->tBERS = le16_to_cpu(p->t_bers); + onfi->tR = le16_to_cpu(p->t_r); + onfi->tCCS = le16_to_cpu(p->t_ccs); + onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode); + onfi->vendor_revision = le16_to_cpu(p->vendor_revision); + memcpy(onfi->vendor, p->vendor, sizeof(p->vendor)); + chip->parameters.onfi = onfi; + + return 0; + +free_model: + kfree(chip->parameters.model); + + return ret; +} + +/* + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. + */ +int rawnand_onfi_detect(struct nand_chip *chip) +{ + char id[4]; + int ret; + + /* Try ONFI for unknown chip or LP */ + ret = nand_readid_op(chip, 0x20, id, sizeof(id)); + if (ret || strncmp(id, "ONFI", 4)) + return 0; + + chip->base.helper.page = 0; + chip->base.helper.check_revision = check_onfi_version; + chip->base.helper.parameter_page_read = nand_onfi_read_op; + chip->base.helper.init_intf_data = nand_intf_data; + + return nand_onfi_detect(&chip->base); +} + /* * Get the flash and manufacturer id and lookup if the type is supported. */ @@ -4719,7 +4935,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type) if (!type->name || !type->pagesize) { /* Check if the chip is ONFI compliant */ - ret = nand_onfi_detect(&chip->base); + ret = rawnand_onfi_detect(chip); if (ret < 0) return ret; else if (ret) diff --git a/drivers/mtd/nand/raw/nand_onfi.c b/drivers/mtd/nand/raw/nand_onfi.c index e5b9a27aa4e3..05592f815949 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -15,8 +15,6 @@ #include <linux/slab.h> #include <linux/mtd/nand.h> -#include "internals.h" - u16 onfi_crc16(u16 crc, u8 const *p, size_t len) { int i; @@ -29,81 +27,6 @@ u16 onfi_crc16(u16 crc, u8 const *p, size_t len) return crc; } -/* Parse the Extended Parameter Page. */ -static int nand_flash_detect_ext_param_page(struct nand_chip *chip, - struct nand_onfi_params *p) -{ - struct onfi_ext_param_page *ep; - struct onfi_ext_section *s; - struct onfi_ext_ecc_info *ecc; - uint8_t *cursor; - int ret; - int len; - int i; - - len = le16_to_cpu(p->ext_param_page_length) * 16; - ep = kmalloc(len, GFP_KERNEL); - if (!ep) - return -ENOMEM; - - /* Send our own NAND_CMD_PARAM. */ - ret = nand_read_param_page_op(chip, 0, NULL, 0); - if (ret) - goto ext_out; - - /* Use the Change Read Column command to skip the ONFI param pages. */ - ret = nand_change_read_column_op(chip, - sizeof(*p) * p->num_of_param_pages, - ep, len, true); - if (ret) - goto ext_out; - - ret = -EINVAL; - if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) - != le16_to_cpu(ep->crc))) { - pr_debug("fail in the CRC.\n"); - goto ext_out; - } - - /* - * Check the signature. - * Do not strictly follow the ONFI spec, maybe changed in future. - */ - if (strncmp(ep->sig, "EPPS", 4)) { - pr_debug("The signature is invalid.\n"); - goto ext_out; - } - - /* find the ECC section. */ - cursor = (uint8_t *)(ep + 1); - for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { - s = ep->sections + i; - if (s->type == ONFI_SECTION_TYPE_2) - break; - cursor += s->length * 16; - } - if (i == ONFI_EXT_SECTION_MAX) { - pr_debug("We can not find the ECC section.\n"); - goto ext_out; - } - - /* get the info we want. */ - ecc = (struct onfi_ext_ecc_info *)cursor; - - if (!ecc->codeword_size) { - pr_debug("Invalid codeword size\n"); - goto ext_out; - } - - chip->base.eccreq.strength = ecc->ecc_bits; - chip->base.eccreq.step_size = 1 << ecc->codeword_size; - ret = 0; - -ext_out: - kfree(ep); - return ret; -} - /* * Recover data with bit-wise majority */ @@ -158,40 +81,34 @@ void sanitize_string(u8 *s, size_t len) */ int nand_onfi_detect(struct nand_device *base) { - struct nand_chip *chip = device_to_nand(base); struct mtd_info *mtd = &base->mtd; struct nand_memory_organization *memorg; struct nand_onfi_params *p; - struct onfi_params *onfi; int onfi_version = 0; - char id[4]; - int i, ret, val; + int i, ret; memorg = nanddev_get_memorg(base); - /* Try ONFI for unknown chip or LP */ - ret = nand_readid_op(chip, 0x20, id, sizeof(id)); - if (ret || strncmp(id, "ONFI", 4)) + /* return 0, if ONFI helper functions are not defined */ + if (!base->helper.parameter_page_read && + !base->helper.check_revision && + !base->helper.init_intf_data) { return 0; + } /* ONFI chip: allocate a buffer to hold its parameter page */ - p = kzalloc((sizeof(*p) * 3), GFP_KERNEL); + p = kzalloc(sizeof(*p) * 3, GFP_KERNEL); if (!p) return -ENOMEM; - ret = nand_read_param_page_op(chip, 0, NULL, 0); + ret = base->helper.parameter_page_read(base, base->helper.page, + p, sizeof(*p) * 3); if (ret) { ret = 0; goto free_onfi_param_page; } for (i = 0; i < 3; i++) { - ret = nand_read_data_op(chip, &p[i], sizeof(*p), true); - if (ret) { - ret = 0; - goto free_onfi_param_page; - } - if (onfi_crc16(ONFI_CRC_BASE, (u8 *)&p[i], 254) == le16_to_cpu(p->crc)) { if (i) @@ -214,35 +131,14 @@ int nand_onfi_detect(struct nand_device *base) } } - if (chip->manufacturer.desc && chip->manufacturer.desc->ops && - chip->manufacturer.desc->ops->fixup_onfi_param_page) - chip->manufacturer.desc->ops->fixup_onfi_param_page(chip, p); - - /* Check version */ - val = le16_to_cpu(p->revision); - if (val & ONFI_VERSION_2_3) - onfi_version = 23; - else if (val & ONFI_VERSION_2_2) - onfi_version = 22; - else if (val & ONFI_VERSION_2_1) - onfi_version = 21; - else if (val & ONFI_VERSION_2_0) - onfi_version = 20; - else if (val & ONFI_VERSION_1_0) - onfi_version = 10; - - if (!onfi_version) { - pr_info("unsupported ONFI version: %d\n", val); + ret = base->helper.check_revision(base, p, &onfi_version); + if (ret) { + ret = 0; goto free_onfi_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_onfi_param_page; - } memorg->pagesize = le32_to_cpu(p->byte_per_page); mtd->writesize = memorg->pagesize; @@ -268,63 +164,14 @@ int nand_onfi_detect(struct nand_device *base) memorg->max_bad_eraseblocks_per_lun = le32_to_cpu(p->blocks_per_lun); memorg->bits_per_cell = p->bits_per_cell; - if (le16_to_cpu(p->features) & ONFI_FEATURE_16_BIT_BUS) - chip->options |= NAND_BUSWIDTH_16; - if (p->ecc_bits != 0xff) { - chip->base.eccreq.strength = p->ecc_bits; - chip->base.eccreq.step_size = 512; - } else if (onfi_version >= 21 && - (le16_to_cpu(p->features) & ONFI_FEATURE_EXT_PARAM_PAGE)) { - - /* - * The nand_flash_detect_ext_param_page() uses the - * Change Read Column command which maybe not supported - * by the chip->legacy.cmdfunc. So try to update the - * chip->legacy.cmdfunc now. We do not replace user supplied - * command function. - */ - nand_legacy_adjust_cmdfunc(chip); - - /* The Extended Parameter Page is supported since ONFI 2.1. */ - if (nand_flash_detect_ext_param_page(chip, p)) - pr_warn("Failed to detect ONFI extended param page\n"); - } else { - pr_warn("Could not retrieve ONFI ECC requirements\n"); + base->eccreq.strength = p->ecc_bits; + base->eccreq.step_size = 512; } - /* Save some parameters from the parameter page for future use */ - if (le16_to_cpu(p->opt_cmd) & ONFI_OPT_CMD_SET_GET_FEATURES) { - chip->parameters.supports_set_get_features = true; - bitmap_set(chip->parameters.get_feature_list, - ONFI_FEATURE_ADDR_TIMING_MODE, 1); - bitmap_set(chip->parameters.set_feature_list, - ONFI_FEATURE_ADDR_TIMING_MODE, 1); - } - - onfi = kzalloc(sizeof(*onfi), GFP_KERNEL); - if (!onfi) { - ret = -ENOMEM; - goto free_model; - } - - onfi->version = onfi_version; - onfi->tPROG = le16_to_cpu(p->t_prog); - onfi->tBERS = le16_to_cpu(p->t_bers); - onfi->tR = le16_to_cpu(p->t_r); - onfi->tCCS = le16_to_cpu(p->t_ccs); - onfi->async_timing_mode = le16_to_cpu(p->async_timing_mode); - onfi->vendor_revision = le16_to_cpu(p->vendor_revision); - memcpy(onfi->vendor, p->vendor, sizeof(p->vendor)); - chip->parameters.onfi = onfi; + ret = base->helper.init_intf_data(base, p); /* Identification done, free the full ONFI parameter page and exit */ - kfree(p); - - return 1; - -free_model: - kfree(chip->parameters.model); free_onfi_param_page: kfree(p); -- 2.17.1 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/