linux-mtd.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 05/12] mtd: rawnand: turn ONFI support to generic
@ 2019-06-03 12:43 Shivamurthy Shastri (sshivamurthy)
  0 siblings, 0 replies; only message in thread
From: Shivamurthy Shastri (sshivamurthy) @ 2019-06-03 12:43 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, David Woodhouse, Brian Norris,
	Marek Vasut, Vignesh Raghavendra, Boris Brezillon, Yixun Lan,
	Lucas Stach, Anders Roxell, Stefan Agner, Marcel Ziswiler,
	Bean Huo (beanhuo),
	Paul Cercueil, Frieder Schrempf, Chuanhong Guo, linux-mtd,
	linux-kernel

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@micron.com>
---
 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/

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2019-06-03 12:54 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-03 12:43 [PATCH v3 05/12] mtd: rawnand: turn ONFI support to generic Shivamurthy Shastri (sshivamurthy)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).