All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] spi-nor: sfdp: Allow configuring unknown flashes using SFDP
@ 2021-05-20 16:07 Petr Malat
  2021-05-21  9:55 ` Pratyush Yadav
  0 siblings, 1 reply; 7+ messages in thread
From: Petr Malat @ 2021-05-20 16:07 UTC (permalink / raw)
  To: linux-mtd
  Cc: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Rob Herring, Tudor Ambarus, Petr Malat

This change allows adding a support for flashes with correct SFDP
without recompilation of the kernel by setting sfdp-compatible property
in their node. Alternatively, sfdp_compatible module option can be used
to list JEDEC IDs of flashes, whose SFDP can be trusted. Star "*" can
be used to match all JEDEC IDs.

Signed-off-by: Petr Malat <oss@malat.biz>
---
 .../devicetree/bindings/mtd/jedec,spi-nor.txt |  3 +
 drivers/mtd/spi-nor/core.c                    | 86 +++++++++++--------
 drivers/mtd/spi-nor/core.h                    |  4 +
 drivers/mtd/spi-nor/sfdp.c                    | 66 ++++++++++++++
 drivers/mtd/spi-nor/sfdp.h                    |  2 +
 5 files changed, 126 insertions(+), 35 deletions(-)

diff --git a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
index f03be904d3c2..b641b3e24a07 100644
--- a/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
+++ b/Documentation/devicetree/bindings/mtd/jedec,spi-nor.txt
@@ -78,6 +78,9 @@ Optional properties:
 		   cannot reboot properly if the flash is left in the "wrong"
 		   state. This boolean flag can be used on such systems, to
 		   denote the absence of a reliable reset mechanism.
+- sfdp-compatible : Trust information in SFDP and instantiate the device even if
+		   its JEDEC ID isn't known to the driver. All the configuration
+		   is read from SFDP and no workarounds are applied.
 
 Example:
 
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 20df44b753da..47f8108e971e 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -2193,7 +2193,8 @@ spi_nor_search_part_by_id(const struct flash_info *parts, unsigned int nparts,
 	return NULL;
 }
 
-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
+static const struct flash_info *spi_nor_read_id(struct spi_nor *nor,
+						bool autodetect)
 {
 	const struct flash_info *info;
 	u8 *id = nor->bouncebuf;
@@ -2227,8 +2228,15 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor)
 		}
 	}
 
-	dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
-		SPI_NOR_MAX_ID_LEN, id);
+	if (autodetect) {
+		info = spi_nor_sfdp_generic_info(nor, id);
+		if (!IS_ERR_OR_NULL(info))
+			return info;
+	}
+
+	dev_warn(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
+		 SPI_NOR_MAX_ID_LEN, id);
+
 	return ERR_PTR(-ENODEV);
 }
 
@@ -2849,19 +2857,25 @@ static void spi_nor_manufacturer_init_params(struct spi_nor *nor)
  * @nor:	pointer to a 'struct spi_nor'.
  *
  * The method has a roll-back mechanism: in case the SFDP parsing fails, the
- * legacy flash parameters and settings will be restored.
+ * legacy flash parameters and settings will be restored unless configuration
+ * based solely on SFDP was requested.
  */
-static void spi_nor_sfdp_init_params(struct spi_nor *nor)
+static int spi_nor_sfdp_init_params(struct spi_nor *nor)
 {
 	struct spi_nor_flash_parameter sfdp_params;
+	int rtn;
 
 	memcpy(&sfdp_params, nor->params, sizeof(sfdp_params));
 
-	if (spi_nor_parse_sfdp(nor, nor->params)) {
+	rtn = spi_nor_parse_sfdp(nor, nor->params);
+	if (rtn && !(nor->info->flags & SPI_NOR_SFDP_AUTODETECT)) {
 		memcpy(nor->params, &sfdp_params, sizeof(*nor->params));
 		nor->addr_width = 0;
 		nor->flags &= ~SNOR_F_4B_OPCODES;
+		return 0;
 	}
+
+	return rtn;
 }
 
 /**
@@ -3051,6 +3065,8 @@ static void spi_nor_late_init_params(struct spi_nor *nor)
  */
 static int spi_nor_init_params(struct spi_nor *nor)
 {
+	int rtn;
+
 	nor->params = devm_kzalloc(nor->dev, sizeof(*nor->params), GFP_KERNEL);
 	if (!nor->params)
 		return -ENOMEM;
@@ -3060,9 +3076,13 @@ static int spi_nor_init_params(struct spi_nor *nor)
 	spi_nor_manufacturer_init_params(nor);
 
 	if ((nor->info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
-				 SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ)) &&
-	    !(nor->info->flags & SPI_NOR_SKIP_SFDP))
-		spi_nor_sfdp_init_params(nor);
+				 SPI_NOR_OCTAL_READ | SPI_NOR_OCTAL_DTR_READ |
+				 SPI_NOR_SFDP_AUTODETECT)) &&
+	    !(nor->info->flags & SPI_NOR_SKIP_SFDP)) {
+		rtn = spi_nor_sfdp_init_params(nor);
+		if (rtn)
+			return rtn;
+	}
 
 	spi_nor_post_sfdp_fixups(nor);
 
@@ -3349,39 +3369,35 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
 {
 	const struct flash_info *info = NULL;
 
-	if (name)
+	if (name) {
 		info = spi_nor_match_id(nor, name);
-	/* Try to auto-detect if chip name wasn't specified or not found */
-	if (!info)
-		info = spi_nor_read_id(nor);
-	if (IS_ERR_OR_NULL(info))
-		return ERR_PTR(-ENOENT);
-
-	/*
-	 * If caller has specified name of flash model that can normally be
-	 * detected using JEDEC, let's verify it.
-	 */
-	if (name && info->id_len) {
-		const struct flash_info *jinfo;
-
-		jinfo = spi_nor_read_id(nor);
-		if (IS_ERR(jinfo)) {
-			return jinfo;
-		} else if (jinfo != info) {
+		if (info) {
 			/*
-			 * JEDEC knows better, so overwrite platform ID. We
-			 * can't trust partitions any longer, but we'll let
-			 * mtd apply them anyway, since some partitions may be
-			 * marked read-only, and we don't want to lose that
+			 * If caller has specified name of flash model that can
+			 * normally be detected using JEDEC, we revert to JEDEC
+			 * detection. If different flash is detected, we can't
+			 * trust partitions any longer, but we'll let mtd apply
+			 * them anyway, since some partitions may be marked
+			 * read-only, and we don't want to lose that
 			 * information, even if it's not 100% accurate.
 			 */
-			dev_warn(nor->dev, "found %s, expected %s\n",
-				 jinfo->name, info->name);
-			info = jinfo;
+			if (info->id_len) {
+				const struct flash_info *jinfo;
+
+				jinfo = spi_nor_read_id(nor, false);
+				if (jinfo != info) {
+					dev_warn(nor->dev,
+						 "found %s, expected %s\n",
+						 jinfo->name, info->name);
+					info = jinfo;
+				}
+			}
+			return info;
 		}
 	}
 
-	return info;
+	/* Try to auto-detect if chip name wasn't specified or not found */
+	return spi_nor_read_id(nor, true);
 }
 
 int spi_nor_scan(struct spi_nor *nor, const char *name,
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index d631ee299de3..96742dc6a91e 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -338,6 +338,10 @@ struct flash_info {
 					 * protection bits. Usually these will
 					 * power-up in a write-protected state.
 					 */
+#define SPI_NOR_SFDP_AUTODETECT	BIT(23)	/*
+					 * Flashinfo was dynamically created and
+					 * all parameters come from SFDP
+					 */
 
 	/* Part specific fixup hooks. */
 	const struct spi_nor_fixups *fixups;
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c
index 6ee7719e5903..3e5c138b0143 100644
--- a/drivers/mtd/spi-nor/sfdp.c
+++ b/drivers/mtd/spi-nor/sfdp.c
@@ -7,6 +7,7 @@
 #include <linux/bitfield.h>
 #include <linux/slab.h>
 #include <linux/sort.h>
+#include <linux/moduleparam.h>
 #include <linux/mtd/spi-nor.h>
 
 #include "core.h"
@@ -1373,3 +1374,68 @@ int spi_nor_parse_sfdp(struct spi_nor *nor,
 	kfree(param_headers);
 	return err;
 }
+
+static char *sfdp_compatible[8];
+static int num_sfdp_compatible;
+module_param_array(sfdp_compatible, charp, &num_sfdp_compatible, 0444);
+MODULE_PARM_DESC(sfdp_compatible, "JEDEC IDs of devices with trusted SFDP which"
+		" the kernel instantiates without them being explicitly listed "
+		"in the driver. Use * to trust SFDP of all devices.");
+
+static bool spi_nor_is_sfdp_trusted(struct spi_nor *nor, const u8 *id)
+{
+	struct device_node *np = spi_nor_get_flash_node(nor);
+	char strid[SPI_NOR_MAX_ID_LEN * 2 + 1];
+	int i;
+
+	if (np && of_property_read_bool(np, "sfdp-compatible"))
+		return 1;
+
+	snprintf(strid, sizeof(strid), "%*phN", SPI_NOR_MAX_ID_LEN, id);
+
+	for (i = 0; i < num_sfdp_compatible; i++) {
+		if (!strcmp("*", sfdp_compatible[i]))
+			return 1;
+		if (!strcmp(strid, sfdp_compatible[i]))
+			return 1;
+	}
+
+	return 0;
+}
+
+struct flash_info *spi_nor_sfdp_generic_info(struct spi_nor *nor, const u8 *id)
+{
+	struct flash_info *info;
+	__le32 sfdp_sig = 0;
+	int err;
+
+	if (!spi_nor_is_sfdp_trusted(nor, id)) {
+		dev_info(nor->dev, "SFDP configuration of JEDEC id %*ph is not"
+				" enabled\n", SPI_NOR_MAX_ID_LEN, id);
+		return ERR_PTR(-ENODEV);
+	}
+
+	/* Sanity check - read SFDP_SIGNATURE */
+	err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(sfdp_sig), &sfdp_sig);
+	if (err || le32_to_cpu(sfdp_sig) != SFDP_SIGNATURE) {
+		dev_err(nor->dev, "Failed to read SFDP signature: %d\n", err);
+		return ERR_PTR(err ?: -ENODEV);
+	}
+
+	/* Allocate info */
+	info = devm_kzalloc(nor->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return ERR_PTR(-ENOMEM);
+
+	memcpy(info->id, id, SPI_NOR_MAX_ID_LEN);
+	info->id_len = SPI_NOR_MAX_ID_LEN;
+	info->name = "sfdp-compatible";
+	info->flags = SPI_NOR_SFDP_AUTODETECT;
+	info->page_size = 256;
+	nor->info = info;
+
+	dev_info(nor->dev, "Using SFDP to configure JEDEC id %*ph\n",
+		 SPI_NOR_MAX_ID_LEN, id);
+
+	return info;
+}
diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h
index 89152ae1cf3e..e9f6c4abd0c3 100644
--- a/drivers/mtd/spi-nor/sfdp.h
+++ b/drivers/mtd/spi-nor/sfdp.h
@@ -110,4 +110,6 @@ struct sfdp_parameter_header {
 int spi_nor_parse_sfdp(struct spi_nor *nor,
 		       struct spi_nor_flash_parameter *params);
 
+struct flash_info *spi_nor_sfdp_generic_info(struct spi_nor *nor, const u8 *id);
+
 #endif /* __LINUX_MTD_SFDP_H */
-- 
2.20.1


______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2021-05-28 12:11 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-20 16:07 [PATCH] spi-nor: sfdp: Allow configuring unknown flashes using SFDP Petr Malat
2021-05-21  9:55 ` Pratyush Yadav
2021-05-21 11:53   ` Petr Malat
2021-05-27 13:52     ` Vignesh Raghavendra
2021-05-27 13:54       ` Pratyush Yadav
2021-05-27 15:45       ` Petr Malat
2021-05-28 11:56         ` Vignesh Raghavendra

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.