[PATCH] mtd: spi-nor: core: Set mtd->eraseregions for non-uniform erase map

tkuw584924 at gmail.com tkuw584924 at gmail.com
Mon Dec 25 00:03:49 PST 2023


From: Takahiro Kuwano <Takahiro.Kuwano at infineon.com>

Some of Infineon SPI NOR flash devices support hybrid sector layout that
overlays 4KB sectors on a 256KB sector and SPI NOR framework recognizes
that by parsing SMPT and construct params->erase_map. The hybrid sector
layout is similar to CFI flash devices that have small sectors on top
and/or bottom address. In case of CFI flash devices, the erase map
information is parsed through CFI table and populated into
mtd->eraseregions so that users can create MTD partitions that aligned with
small sector boundaries. This patch provides the same capability to SPI
NOR flash devices that have non-uniform erase map.

Signed-off-by: Takahiro Kuwano <Takahiro.Kuwano at infineon.com>
---
The s25hs512t has 32 x 4KB, 1 x 128KB, and 255 x 256KB sectors by default.
Let's think about creating three MTD partitions that align to region
boundary.

BEFORE applying this patch, 1st and 2nd region are forced to read-only:

---kernel log---
...
spi-nor spi0.0: s25hs512t (65536 Kbytes)
3 fixed-partitions partitions found on MTD device spi0.0
Creating 3 MTD partitions on "spi0.0":
0x000000000000-0x000000020000 : "4KB x 32"
mtd: partition "4KB x 32" doesn't end on an erase/write block -- force read-only
0x000000020000-0x000000040000 : "128KB x 1"
mtd: partition "128KB x 1" doesn't start on an erase/write block boundary -- force read-only
0x000000040000-0x000004000000 : "256KB x 255"
...

---MTD Info---
zynq> mtd_debug info /dev/mtd0
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_ROM
mtd.size = 131072 (128K)
mtd.erasesize = 262144 (256K)
mtd.writesize = 16
mtd.oobsize = 0
regions = 0

zynq> mtd_debug info /dev/mtd1
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_ROM
mtd.size = 131072 (128K)
mtd.erasesize = 262144 (256K)
mtd.writesize = 16
mtd.oobsize = 0
regions = 0

zynq> mtd_debug info /dev/mtd2
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NANDFLASH
mtd.size = 66846720 (63M)
mtd.erasesize = 262144 (256K)
mtd.writesize = 16
mtd.oobsize = 0
regions = 0



AFTER applying this patch, erasesize is correctly informed and no read-only:

---kernel log---
...
spi-nor spi0.0: s25hs512t (65536 Kbytes)
3 fixed-partitions partitions found on MTD device spi0.0
Creating 3 MTD partitions on "spi0.0":
0x000000000000-0x000000020000 : "4KB x 32"
0x000000020000-0x000000040000 : "128KB x 1"
0x000000040000-0x000004000000 : "256KB x 255"
...

---MTD Info---
zynq> mtd_debug info /dev/mtd0
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NANDFLASH
mtd.size = 131072 (128K)
mtd.erasesize = 4096 (4K)
mtd.writesize = 16
mtd.oobsize = 0
regions = 0

zynq> mtd_debug info /dev/mtd1
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NANDFLASH
mtd.size = 131072 (128K)
mtd.erasesize = 131072 (128K)
mtd.writesize = 16
mtd.oobsize = 0
regions = 0

zynq> mtd_debug info /dev/mtd2
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NANDFLASH
mtd.size = 66846720 (63M)
mtd.erasesize = 262144 (256K)
mtd.writesize = 16
mtd.oobsize = 0
regions = 0

---
 drivers/mtd/spi-nor/core.c | 55 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 53 insertions(+), 2 deletions(-)

diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 1b0c6770c14e..e512491733a8 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -3408,7 +3408,51 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
 	return info;
 }
 
-static void spi_nor_set_mtd_info(struct spi_nor *nor)
+static int spi_nor_set_mtd_eraseregions(struct spi_nor *nor)
+{
+	struct spi_nor_erase_map *map = &nor->params->erase_map;
+	struct spi_nor_erase_region *region = map->regions;
+	struct mtd_info *mtd = &nor->mtd;
+	struct mtd_erase_region_info *mtd_region;
+	u32 erase_size;
+	u8 erase_mask;
+	int n_regions, i, j;
+
+	for (i = 0; !spi_nor_region_is_last(&region[i]); i++)
+		;
+
+	n_regions = i + 1;
+	mtd_region = devm_kcalloc(nor->dev, n_regions, sizeof(*mtd_region),
+				  GFP_KERNEL);
+	if (!mtd_region)
+		return -ENOMEM;
+
+	for (i = 0; i < n_regions; i++) {
+		if (region[i].offset & SNOR_OVERLAID_REGION) {
+			erase_size = region[i].size;
+		} else {
+			erase_mask = region[i].offset & SNOR_ERASE_TYPE_MASK;
+
+			for (j = SNOR_ERASE_TYPE_MAX - 1; j >= 0; j--) {
+				if (erase_mask & BIT(j)) {
+					erase_size = map->erase_type[j].size;
+					break;
+				}
+			}
+		}
+		mtd_region[i].erasesize = erase_size;
+		mtd_region[i].numblocks = div64_ul(region[i].size, erase_size);
+		mtd_region[i].offset = region[i].offset &
+				       ~SNOR_ERASE_FLAGS_MASK;
+	}
+
+	mtd->numeraseregions = n_regions;
+	mtd->eraseregions = mtd_region;
+
+	return 0;
+}
+
+static int spi_nor_set_mtd_info(struct spi_nor *nor)
 {
 	struct mtd_info *mtd = &nor->mtd;
 	struct device *dev = nor->dev;
@@ -3439,6 +3483,11 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
 	mtd->_resume = spi_nor_resume;
 	mtd->_get_device = spi_nor_get_device;
 	mtd->_put_device = spi_nor_put_device;
+
+	if (spi_nor_has_uniform_erase(nor))
+		return 0;
+
+	return spi_nor_set_mtd_eraseregions(nor);
 }
 
 static int spi_nor_hw_reset(struct spi_nor *nor)
@@ -3531,7 +3580,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
 		return ret;
 
 	/* No mtd_info fields should be used up to this point. */
-	spi_nor_set_mtd_info(nor);
+	ret = spi_nor_set_mtd_info(nor);
+	if (ret)
+		return ret;
 
 	dev_info(dev, "%s (%lld Kbytes)\n", info->name,
 			(long long)mtd->size >> 10);
-- 
2.34.1




More information about the linux-mtd mailing list