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: Michael Walle <michael@walle.cc>
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Tested-by: Heiko Thiery <heiko.thiery@gmail.com>
Reviewed-by: Pratyush Yadav <p.yadav@ti.com>
This commit is contained in:
Michael Walle 2021-05-03 17:56:50 +02:00 committed by Vignesh Raghavendra
parent c6ec3e1e3a
commit 65b6d89d45
3 changed files with 70 additions and 0 deletions

View File

@ -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;

View File

@ -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.

View File

@ -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;