Commit 65b6d89d authored by Michael Walle's avatar Michael Walle Committed by Vignesh Raghavendra
Browse files

mtd: spi-nor: sfdp: save a copy of the SFDP data



Due to possible mode switching to 8D-8D-8D, it might not be possible to
read the SFDP after the initial probe. To be able to dump the SFDP via
sysfs afterwards, make a complete copy of it.

Signed-off-by: default avatarMichael Walle <michael@walle.cc>
Signed-off-by: default avatarVignesh Raghavendra <vigneshr@ti.com>
Tested-by: default avatarHeiko Thiery <heiko.thiery@gmail.com>
Reviewed-by: default avatarPratyush Yadav <p.yadav@ti.com>
parent c6ec3e1e
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -461,6 +461,16 @@ struct spi_nor_manufacturer {
	const struct spi_nor_fixups *fixups;
};

/**
 * struct sfdp - SFDP data
 * @num_dwords: number of entries in the dwords array
 * @dwords: array of double words of the SFDP data
 */
struct sfdp {
	size_t	num_dwords;
	u32	*dwords;
};

/* Manufacturer drivers. */
extern const struct spi_nor_manufacturer spi_nor_atmel;
extern const struct spi_nor_manufacturer spi_nor_catalyst;
+58 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
	(((p)->parameter_table_pointer[2] << 16) | \
	 ((p)->parameter_table_pointer[1] <<  8) | \
	 ((p)->parameter_table_pointer[0] <<  0))
#define SFDP_PARAM_HEADER_PARAM_LEN(p) ((p)->length * 4)

#define SFDP_BFPT_ID		0xff00	/* Basic Flash Parameter Table */
#define SFDP_SECTOR_MAP_ID	0xff81	/* Sector Map Table */
@@ -1245,6 +1246,8 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
	struct sfdp_parameter_header *param_headers = NULL;
	struct sfdp_header header;
	struct device *dev = nor->dev;
	struct sfdp *sfdp;
	size_t sfdp_size;
	size_t psize;
	int i, err;

@@ -1267,6 +1270,9 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
	    bfpt_header->major != SFDP_JESD216_MAJOR)
		return -EINVAL;

	sfdp_size = SFDP_PARAM_HEADER_PTP(bfpt_header) +
		    SFDP_PARAM_HEADER_PARAM_LEN(bfpt_header);

	/*
	 * Allocate memory then read all parameter headers with a single
	 * Read SFDP command. These parameter headers will actually be parsed
@@ -1293,6 +1299,58 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
		}
	}

	/*
	 * Cache the complete SFDP data. It is not (easily) possible to fetch
	 * SFDP after probe time and we need it for the sysfs access.
	 */
	for (i = 0; i < header.nph; i++) {
		param_header = &param_headers[i];
		sfdp_size = max_t(size_t, sfdp_size,
				  SFDP_PARAM_HEADER_PTP(param_header) +
				  SFDP_PARAM_HEADER_PARAM_LEN(param_header));
	}

	/*
	 * Limit the total size to a reasonable value to avoid allocating too
	 * much memory just of because the flash returned some insane values.
	 */
	if (sfdp_size > PAGE_SIZE) {
		dev_dbg(dev, "SFDP data (%zu) too big, truncating\n",
			sfdp_size);
		sfdp_size = PAGE_SIZE;
	}

	sfdp = devm_kzalloc(dev, sizeof(*sfdp), GFP_KERNEL);
	if (!sfdp) {
		err = -ENOMEM;
		goto exit;
	}

	/*
	 * The SFDP is organized in chunks of DWORDs. Thus, in theory, the
	 * sfdp_size should be a multiple of DWORDs. But in case a flash
	 * is not spec compliant, make sure that we have enough space to store
	 * the complete SFDP data.
	 */
	sfdp->num_dwords = DIV_ROUND_UP(sfdp_size, sizeof(*sfdp->dwords));
	sfdp->dwords = devm_kcalloc(dev, sfdp->num_dwords,
				    sizeof(*sfdp->dwords), GFP_KERNEL);
	if (!sfdp->dwords) {
		err = -ENOMEM;
		devm_kfree(dev, sfdp);
		goto exit;
	}

	err = spi_nor_read_sfdp(nor, 0, sfdp_size, sfdp->dwords);
	if (err < 0) {
		dev_dbg(dev, "failed to read SFDP data\n");
		devm_kfree(dev, sfdp->dwords);
		devm_kfree(dev, sfdp);
		goto exit;
	}

	nor->sfdp = sfdp;

	/*
	 * Check other parameter headers to get the latest revision of
	 * the basic flash parameter table.
+2 −0
Original line number Diff line number Diff line
@@ -383,6 +383,7 @@ struct spi_nor_flash_parameter;
 * @read_proto:		the SPI protocol for read operations
 * @write_proto:	the SPI protocol for write operations
 * @reg_proto:		the SPI protocol for read_reg/write_reg/erase operations
 * @sfdp:		the SFDP data of the flash
 * @controller_ops:	SPI NOR controller driver specific operations.
 * @params:		[FLASH-SPECIFIC] SPI NOR flash parameters and settings.
 *                      The structure includes legacy flash parameters and
@@ -412,6 +413,7 @@ struct spi_nor {
	bool			sst_write_second;
	u32			flags;
	enum spi_nor_cmd_ext	cmd_ext_type;
	struct sfdp		*sfdp;

	const struct spi_nor_controller_ops *controller_ops;