Unverified Commit 6a9eda34 authored by Takahiro Kuwano's avatar Takahiro Kuwano Committed by Tudor Ambarus
Browse files

mtd: spi-nor: core: set mtd->eraseregions for non-uniform erase map



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: default avatarTakahiro Kuwano <Takahiro.Kuwano@infineon.com>
Reviewed-by: default avatarMichael Walle <mwalle@kernel.org>
Link: https://lore.kernel.org/r/35d0962986e493b06c13bdf7ada8130a9966dc02.1708404584.git.Takahiro.Kuwano@infineon.com


Signed-off-by: default avatarTudor Ambarus <tudor.ambarus@linaro.org>
parent 2865ed0e
Loading
Loading
Loading
Loading
+56 −2
Original line number Diff line number Diff line
@@ -3369,7 +3369,54 @@ 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 u32
spi_nor_get_region_erasesize(const struct spi_nor_erase_region *region,
			     const struct spi_nor_erase_type *erase_type)
{
	u8 i;

	if (region->overlaid)
		return region->size;

	for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
		if (region->erase_mask & BIT(i))
			return erase_type[i].size;
	}

	return 0;
}

static int spi_nor_set_mtd_eraseregions(struct spi_nor *nor)
{
	const struct spi_nor_erase_map *map = &nor->params->erase_map;
	const struct spi_nor_erase_region *region = map->regions;
	struct mtd_erase_region_info *mtd_region;
	struct mtd_info *mtd = &nor->mtd;
	u32 erasesize, i;

	mtd_region = devm_kcalloc(nor->dev, map->n_regions, sizeof(*mtd_region),
				  GFP_KERNEL);
	if (!mtd_region)
		return -ENOMEM;

	for (i = 0; i < map->n_regions; i++) {
		erasesize = spi_nor_get_region_erasesize(&region[i],
							 map->erase_type);
		if (!erasesize)
			return -EINVAL;

		mtd_region[i].erasesize = erasesize;
		mtd_region[i].numblocks = div64_ul(region[i].size, erasesize);
		mtd_region[i].offset = region[i].offset;
	}

	mtd->numeraseregions = map->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;
@@ -3400,6 +3447,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 spi_nor_set_mtd_eraseregions(nor);

	return 0;
}

static int spi_nor_hw_reset(struct spi_nor *nor)
@@ -3490,7 +3542,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_dbg(dev, "Manufacturer and device ID: %*phN\n",
		SPI_NOR_MAX_ID_LEN, nor->id);