Fix headers to make way for adding helper functions. Add onfi helper structure. Add helper functions in raw NAND core, which later will be used during ONFI detection. Signed-off-by: Shivamurthy Shastri <sshivamurthy@xxxxxxxxxx> --- drivers/mtd/nand/raw/internals.h | 6 +- drivers/mtd/nand/raw/nand_base.c | 236 ++++++++++++++++++++++++++++--- drivers/mtd/nand/raw/nand_onfi.c | 215 +++++----------------------- include/linux/mtd/nand.h | 30 ++++ include/linux/mtd/rawnand.h | 5 + 5 files changed, 289 insertions(+), 203 deletions(-) diff --git a/drivers/mtd/nand/raw/internals.h b/drivers/mtd/nand/raw/internals.h index a204f9d7e123..eb34dece4754 100644 --- a/drivers/mtd/nand/raw/internals.h +++ b/drivers/mtd/nand/raw/internals.h @@ -90,7 +90,7 @@ int nand_write_page_raw_notsupp(struct nand_chip *chip, const u8 *buf, int oob_required, int page); int nand_exit_status_op(struct nand_chip *chip); int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, - unsigned int len); + unsigned int len); void nand_decode_ext_id(struct nand_chip *chip); void panic_nand_wait(struct nand_chip *chip, unsigned long timeo); void sanitize_string(uint8_t *s, size_t len); @@ -138,10 +138,6 @@ void nand_legacy_set_defaults(struct nand_chip *chip); void nand_legacy_adjust_cmdfunc(struct nand_chip *chip); int nand_legacy_check_hooks(struct nand_chip *chip); -/* ONFI functions */ -u16 onfi_crc16(u16 crc, u8 const *p, size_t len); -int nand_onfi_detect(struct nand_chip *chip); - /* JEDEC functions */ int nand_jedec_detect(struct nand_chip *chip); diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 0bc898bdb6e1..fc2c7d6ea4f2 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4194,24 +4194,6 @@ static void nand_set_defaults(struct nand_chip *chip) chip->buf_align = 1; } -/* Sanitize ONFI strings so we can safely print them */ -void sanitize_string(uint8_t *s, size_t len) -{ - ssize_t i; - - /* Null terminate */ - s[len - 1] = 0; - - /* Remove non printable chars */ - for (i = 0; i < len - 1; i++) { - if (s[i] < ' ' || s[i] > 127) - s[i] = '?'; - } - - /* Remove trailing spaces */ - strim(s); -} - /* * nand_id_has_period - Check if an ID string has a given wraparound period * @id_data: the ID string @@ -4461,6 +4443,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 0; + + for (i = 0; i < 3; i++) { + ret = nand_read_data_op(chip, &p[i], sizeof(*p), true); + if (ret) + return 0; + } + + return 1; +} + +/* 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.ecc.requirements.strength = ecc->ecc_bits; + chip->base.ecc.requirements.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 0; + } + + return 1; +} + +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 1; + +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. */ @@ -4556,7 +4754,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); + 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 9d21b47ebef1..61fcb6994b45 100644 --- a/drivers/mtd/nand/raw/nand_onfi.c +++ b/drivers/mtd/nand/raw/nand_onfi.c @@ -13,8 +13,7 @@ */ #include <linux/slab.h> - -#include "internals.h" +#include <linux/mtd/nand.h> u16 onfi_crc16(u16 crc, u8 const *p, size_t len) { @@ -28,88 +27,13 @@ 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.ecc.requirements.strength = ecc->ecc_bits; - chip->base.ecc.requirements.step_size = 1 << ecc->codeword_size; - ret = 0; - -ext_out: - kfree(ep); - return ret; -} - /* * Recover data with bit-wise majority */ -static void nand_bit_wise_majority(const void **srcbufs, - unsigned int nsrcbufs, - void *dstbuf, - unsigned int bufsize) +void nand_bit_wise_majority(const void **srcbufs, + unsigned int nsrcbufs, + void *dstbuf, + unsigned int bufsize) { int i, j, k; @@ -134,44 +58,49 @@ static void nand_bit_wise_majority(const void **srcbufs, } } +/* Sanitize ONFI strings so we can safely print them */ +void sanitize_string(u8 *s, size_t len) +{ + ssize_t i; + + /* Null terminate */ + s[len - 1] = 0; + + /* Remove non printable chars */ + for (i = 0; i < len - 1; i++) { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + + /* Remove trailing spaces */ + strim(s); +} + /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. */ -int nand_onfi_detect(struct nand_chip *chip) +int nand_onfi_detect(struct nand_device *base) { - struct mtd_info *mtd = nand_to_mtd(chip); + 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; - - memorg = nanddev_get_memorg(&chip->base); + int i, 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; + memorg = nanddev_get_memorg(base); /* 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); - if (ret) { - ret = 0; + ret = base->helper.parameter_page_read(base, base->helper.page, + p, sizeof(*p) * 3); + if (!ret) { 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) @@ -194,35 +123,12 @@ int nand_onfi_detect(struct nand_chip *chip) } } - 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) 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; @@ -248,63 +154,14 @@ int nand_onfi_detect(struct nand_chip *chip) 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.ecc.requirements.strength = p->ecc_bits; - chip->base.ecc.requirements.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->ecc.requirements.strength = p->ecc_bits; + base->ecc.requirements.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); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index b3a5506a891b..3f7e2b495e93 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -13,6 +13,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand-ecc-sw-hamming.h> #include <linux/mtd/nand-ecc-sw-bch.h> +#include <linux/mtd/onfi.h> struct nand_device; @@ -269,6 +270,24 @@ struct nand_ecc { struct nand_ecc_engine *engine; }; +/** + * struct onfi_helper - ONFI helper functions that should be implemented by + * specialized layers (raw NAND, SPI NAND, etc.) + * @page: Page number for ONFI parameter table + * @check_revision: Check ONFI revision number + * @parameter_page_read: Function to read parameter pages + * @init_intf_data: Initialize interface specific data or fixups + */ +struct onfi_helper { + u8 page; + int (*check_revision)(struct nand_device *base, + struct nand_onfi_params *p, int *onfi_version); + int (*parameter_page_read)(struct nand_device *base, u8 page, + void *buf, unsigned int len); + int (*init_intf_data)(struct nand_device *base, + struct nand_onfi_params *p); +}; + /** * struct nand_device - NAND device * @mtd: MTD instance attached to the NAND device @@ -276,6 +295,7 @@ struct nand_ecc { * @rowconv: position to row address converter * @bbt: bad block table info * @ops: NAND operations attached to the NAND device + * @helper: Helper functions to detect and initialize ONFI NAND * * Generic NAND object. Specialized NAND layers (raw NAND, SPI NAND, OneNAND) * should declare their own NAND object embedding a nand_device struct (that's @@ -294,6 +314,7 @@ struct nand_device { struct nand_row_converter rowconv; struct nand_bbt bbt; const struct nand_ops *ops; + struct onfi_helper helper; }; /** @@ -888,4 +909,13 @@ static inline bool nanddev_bbt_is_initialized(struct nand_device *nand) int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo); int nanddev_mtd_max_bad_blocks(struct mtd_info *mtd, loff_t offs, size_t len); +/* ONFI functions */ +u16 onfi_crc16(u16 crc, u8 const *p, size_t len); +void nand_bit_wise_majority(const void **srcbufs, + unsigned int nsrcbufs, + void *dstbuf, + unsigned int bufsize); +void sanitize_string(u8 *s, size_t len); +int nand_onfi_detect(struct nand_device *base); + #endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 174d2d27200f..bcb18cade2e8 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1079,6 +1079,11 @@ static inline struct mtd_info *nand_to_mtd(struct nand_chip *chip) return &chip->base.mtd; } +static inline struct nand_chip *device_to_nand(struct nand_device *base) +{ + return container_of(base, struct nand_chip, base); +} + static inline void *nand_get_controller_data(struct nand_chip *chip) { return chip->priv; -- 2.17.1 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/