From 5c26d52c9e5c9a22d04b805470e1143716b69789 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 May 2021 17:13:43 +0300 Subject: mtd: spi-nor: nxp-spifi: Use SPI_MODE_X_MASK Use SPI_MODE_X_MASK instead of open coded variant. Signed-off-by: Andy Shevchenko Signed-off-by: Vignesh Raghavendra Reviewed-by: Michael Walle --- drivers/mtd/spi-nor/controllers/nxp-spifi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/controllers/nxp-spifi.c b/drivers/mtd/spi-nor/controllers/nxp-spifi.c index 5703e8313980..2635c80231bb 100644 --- a/drivers/mtd/spi-nor/controllers/nxp-spifi.c +++ b/drivers/mtd/spi-nor/controllers/nxp-spifi.c @@ -326,7 +326,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, ctrl |= SPIFI_CTRL_DUAL; } - switch (mode & (SPI_CPHA | SPI_CPOL)) { + switch (mode & SPI_MODE_X_MASK) { case SPI_MODE_0: ctrl &= ~SPIFI_CTRL_MODE3; break; -- cgit v1.2.3 From ccfb7cf18f9680958e76991c8f15562ff42f4bc9 Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Tue, 11 May 2021 15:09:58 +0530 Subject: mtd: spi-nor: Add documentation for spi_nor_soft_reset() Document what the function does and that it should only be used when it is known that the device supports it. This will avoid unaware programmers thinking that they can arbitrarily use it to reset the device. Suggested-by: Michael Walle Signed-off-by: Pratyush Yadav Signed-off-by: Vignesh Raghavendra Reviewed-by: Michael Walle --- drivers/mtd/spi-nor/core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index bd2c7717eb10..f6a6ef2d8bd8 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2839,6 +2839,21 @@ static int spi_nor_init(struct spi_nor *nor) return 0; } +/** + * spi_nor_soft_reset() - Perform a software reset + * @nor: pointer to 'struct spi_nor' + * + * Performs a "Soft Reset and Enter Default Protocol Mode" sequence which resets + * the device to its power-on-reset state. This is useful when the software has + * made some changes to device (volatile) registers and needs to reset it before + * shutting down, for example. + * + * Not every flash supports this sequence. The same set of opcodes might be used + * for some other operation on a flash that does not support this. Support for + * this sequence can be discovered via SFDP in the BFPT table. + * + * Return: 0 on success, -errno otherwise. + */ static void spi_nor_soft_reset(struct spi_nor *nor) { struct spi_mem_op op; -- cgit v1.2.3 From a6e2cd4dd28effab117ddce7a62c5a411b282d2e Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Wed, 5 May 2021 22:00:17 +0200 Subject: mtd: spi-nor: otp: fix kerneldoc typos Use the correct argument names in the kerneldoc. Fixes: cad3193fe9d1 ("mtd: spi-nor: implement OTP support for Winbond and similar flashes") Reported-by: Pratyush Yadav Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Pratyush Yadav --- drivers/mtd/spi-nor/otp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index fcf38d260345..61036c716abb 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -17,7 +17,7 @@ /** * spi_nor_otp_read_secr() - read OTP data * @nor: pointer to 'struct spi_nor' - * @from: offset to read from + * @addr: offset to read from * @len: number of bytes to read * @buf: pointer to dst buffer * @@ -59,7 +59,7 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) /** * spi_nor_otp_write_secr() - write OTP data * @nor: pointer to 'struct spi_nor' - * @to: offset to write to + * @addr: offset to write to * @len: number of bytes to write * @buf: pointer to src buffer * -- cgit v1.2.3 From 854955ae96dbd436ba4719dd1cedb7c1c40bd303 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 15 Apr 2021 13:11:38 +0300 Subject: mtd: spi-nor: intel-spi: Add support for Intel Alder Lake-M SPI serial flash Intel Alder Lake-M has the same SPI serial flash controller as Alder Lake-S. Add Alder Lake-M PCI ID to the driver list of supported devices. Signed-off-by: Mika Westerberg Signed-off-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/controllers/intel-spi-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c index 825610a2e9dc..1bc53b8bb88a 100644 --- a/drivers/mtd/spi-nor/controllers/intel-spi-pci.c +++ b/drivers/mtd/spi-nor/controllers/intel-spi-pci.c @@ -74,6 +74,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = { { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x4da4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x51a4), (unsigned long)&cnl_info }, + { PCI_VDEVICE(INTEL, 0x54a4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0x7aa4), (unsigned long)&cnl_info }, { PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, -- cgit v1.2.3 From d406f49b05e547d1e1ff1e9e0e0133fa2538b2fc Mon Sep 17 00:00:00 2001 From: Tudor Ambarus Date: Fri, 2 Apr 2021 11:20:31 +0300 Subject: mtd: spi-nor: macronix: Fix name for mx66l51235f According to macronix website, there is no mx66l51235l part number. The chip detected as such is actually mx66l51235f. Rename the flash. Do not update the mx66l51235l name from the spi_nor_dev_ids[], since there are dt that are using this compatible. Signed-off-by: Tudor Ambarus Signed-off-by: Vignesh Raghavendra Reviewed-by: Vignesh Raghavendra --- drivers/mtd/spi-nor/macronix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index 42c2cf31702e..c8167de55e55 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -72,7 +72,7 @@ static const struct flash_info macronix_parts[] = { SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, - { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, + { "mx66l51235f", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, -- cgit v1.2.3 From 7ea40b54e83baed17d85567cfae56175def39a55 Mon Sep 17 00:00:00 2001 From: David Bauer Date: Mon, 10 May 2021 15:33:40 +0200 Subject: mtd: spi-nor: enable locking support for MX25L12805D Macronix MX25L12805D supports locking with 4 block protection bits in its status register. Add the corresponding flag in order to clear these bits when unloking the flash. Otherwise, the flash might not be writable depending on the state left by the bootloader. Tested-on: Ubiquiti UniFi AC Lite (ath79) Signed-off-by: David Bauer Signed-off-by: Vignesh Raghavendra Reviewed-by: Michael Walle --- drivers/mtd/spi-nor/macronix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index c8167de55e55..27498ed0cc0d 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -49,7 +49,8 @@ static const struct flash_info macronix_parts[] = { { "mx25u4035", INFO(0xc22533, 0, 64 * 1024, 8, SECT_4K) }, { "mx25u8035", INFO(0xc22534, 0, 64 * 1024, 16, SECT_4K) }, { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, - { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, SECT_4K) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, SECT_4K | + SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP) }, { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, { "mx25r1635f", INFO(0xc22815, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_DUAL_READ | -- cgit v1.2.3 From b97b1a769849beb6b40b740817b06f1a50e1c589 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 7 Jun 2021 13:27:41 +0200 Subject: mtd: spi-nor: otp: fix access to security registers in 4 byte mode The security registers either take a 3 byte or a 4 byte address offset, depending on the address mode of the flash. Thus just leave the nor->addr_width as is. Fixes: cad3193fe9d1 ("mtd: spi-nor: implement OTP support for Winbond and similar flashes") Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Tudor Ambarus Acked-by: Pratyush Yadav --- drivers/mtd/spi-nor/otp.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 61036c716abb..91a4c510ed51 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -40,7 +40,6 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) rdesc = nor->dirmap.rdesc; nor->read_opcode = SPINOR_OP_RSECR; - nor->addr_width = 3; nor->read_dummy = 8; nor->read_proto = SNOR_PROTO_1_1_1; nor->dirmap.rdesc = NULL; @@ -84,7 +83,6 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, wdesc = nor->dirmap.wdesc; nor->program_opcode = SPINOR_OP_PSECR; - nor->addr_width = 3; nor->write_proto = SNOR_PROTO_1_1_1; nor->dirmap.wdesc = NULL; -- cgit v1.2.3 From d5b813e484721dfcb84410ec6883c7b05156d9d3 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 7 Jun 2021 13:27:42 +0200 Subject: mtd: spi-nor: otp: use more consistent wording Use the wording as used in the datasheet to describe the access methods of the security registers (aka OTP storage). This will also match the function names. Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Pratyush Yadav Reviewed-by: Tudor Ambarus --- drivers/mtd/spi-nor/otp.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 91a4c510ed51..3898ed67ba1c 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -15,14 +15,21 @@ #define spi_nor_otp_n_regions(nor) ((nor)->params->otp.org->n_regions) /** - * spi_nor_otp_read_secr() - read OTP data + * spi_nor_otp_read_secr() - read security register * @nor: pointer to 'struct spi_nor' * @addr: offset to read from * @len: number of bytes to read * @buf: pointer to dst buffer * - * Read OTP data from one region by using the SPINOR_OP_RSECR commands. This - * method is used on GigaDevice and Winbond flashes. + * Read a security register by using the SPINOR_OP_RSECR commands. + * + * In Winbond/GigaDevice datasheets the term "security register" stands for + * an one-time-programmable memory area, consisting of multiple bytes (usually + * 256). Thus one "security register" maps to one OTP region. + * + * This method is used on GigaDevice and Winbond flashes. + * + * Please note, the read must not span multiple registers. * * Return: number of bytes read successfully, -errno otherwise */ @@ -56,16 +63,20 @@ int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf) } /** - * spi_nor_otp_write_secr() - write OTP data + * spi_nor_otp_write_secr() - write security register * @nor: pointer to 'struct spi_nor' * @addr: offset to write to * @len: number of bytes to write * @buf: pointer to src buffer * - * Write OTP data to one region by using the SPINOR_OP_PSECR commands. This - * method is used on GigaDevice and Winbond flashes. + * Write a security register by using the SPINOR_OP_PSECR commands. + * + * For more information on the term "security register", see the documentation + * of spi_nor_otp_read_secr(). + * + * This method is used on GigaDevice and Winbond flashes. * - * Please note, the write must not span multiple OTP regions. + * Please note, the write must not span multiple registers. * * Return: number of bytes written successfully, -errno otherwise */ @@ -88,7 +99,7 @@ int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, /* * We only support a write to one single page. For now all winbond - * flashes only have one page per OTP region. + * flashes only have one page per security register. */ ret = spi_nor_write_enable(nor); if (ret) -- cgit v1.2.3 From 388161ca45c911f566b71716bce5ff0119fb5522 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 7 Jun 2021 13:27:43 +0200 Subject: mtd: spi-nor: otp: return -EROFS if region is read-only SPI NOR flashes will just ignore program commands if the OTP region is locked. Thus, a user might not notice that the intended write didn't end up in the flash. Return -EROFS to the user in this case. From what I can tell, chips/cfi_cmdset_0001.c also return this error code. One could optimize spi_nor_mtd_otp_range_is_locked() to read the status register only once and not for every OTP region, but for that we would need some more invasive changes. Given that this is one-time-programmable memory and the normal access mode is reading, we just live with the small overhead. By moving the code around a bit, we can just check the length before calling spi_nor_mtd_otp_range_is_locked() and avoid an underflow there if a len is 0. This way we don't need to take the lock either. We also skip the "*retlen = 0" assignment, mtdcore already takes care of that for us. Fixes: 069089acf88b ("mtd: spi-nor: add OTP support") Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Pratyush Yadav Reviewed-by: Tudor Ambarus --- drivers/mtd/spi-nor/otp.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 3898ed67ba1c..89fe52e3851a 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -249,6 +249,29 @@ out: return ret; } +static int spi_nor_mtd_otp_range_is_locked(struct spi_nor *nor, loff_t ofs, + size_t len) +{ + const struct spi_nor_otp_ops *ops = nor->params->otp.ops; + unsigned int region; + int locked; + + /* + * If any of the affected OTP regions are locked the entire range is + * considered locked. + */ + for (region = spi_nor_otp_offset_to_region(nor, ofs); + region <= spi_nor_otp_offset_to_region(nor, ofs + len - 1); + region++) { + locked = ops->is_locked(nor, region); + /* take the branch it is locked or in case of an error */ + if (locked) + return locked; + } + + return 0; +} + static int spi_nor_mtd_otp_read_write(struct mtd_info *mtd, loff_t ofs, size_t total_len, size_t *retlen, const u8 *buf, bool is_write) @@ -264,14 +287,26 @@ static int spi_nor_mtd_otp_read_write(struct mtd_info *mtd, loff_t ofs, if (ofs < 0 || ofs >= spi_nor_otp_size(nor)) return 0; + /* don't access beyond the end */ + total_len = min_t(size_t, total_len, spi_nor_otp_size(nor) - ofs); + + if (!total_len) + return 0; + ret = spi_nor_lock_and_prep(nor); if (ret) return ret; - /* don't access beyond the end */ - total_len = min_t(size_t, total_len, spi_nor_otp_size(nor) - ofs); + if (is_write) { + ret = spi_nor_mtd_otp_range_is_locked(nor, ofs, total_len); + if (ret < 0) { + goto out; + } else if (ret) { + ret = -EROFS; + goto out; + } + } - *retlen = 0; while (total_len) { /* * The OTP regions are mapped into a contiguous area starting -- cgit v1.2.3 From c6ec3e1e3a853f9469c7d07b0fde0be4da8c3ba1 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 7 Jun 2021 13:27:44 +0200 Subject: mtd: spi-nor: otp: implement erase for Winbond and similar flashes Winbond flashes with OTP support provide a command to erase the OTP data. This might come in handy during development. This was tested with a Winbond W25Q32JW on a LS1028A SoC with the NXP FSPI controller. Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Reviewed-by: Pratyush Yadav Reviewed-by: Tudor Ambarus --- drivers/mtd/spi-nor/core.c | 2 +- drivers/mtd/spi-nor/core.h | 4 ++ drivers/mtd/spi-nor/otp.c | 86 +++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/spi-nor/winbond.c | 1 + 4 files changed, 92 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index f6a6ef2d8bd8..a21b0085de05 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1318,7 +1318,7 @@ static u32 spi_nor_convert_addr(struct spi_nor *nor, loff_t addr) /* * Initiate the erasure of a single sector */ -static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) +int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) { int i; diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 28a2e0be97a3..9398a8738857 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -207,6 +207,7 @@ struct spi_nor_otp_organization { * @read: read from the SPI NOR OTP area. * @write: write to the SPI NOR OTP area. * @lock: lock an OTP region. + * @erase: erase an OTP region. * @is_locked: check if an OTP region of the SPI NOR is locked. */ struct spi_nor_otp_ops { @@ -214,6 +215,7 @@ struct spi_nor_otp_ops { int (*write)(struct spi_nor *nor, loff_t addr, size_t len, const u8 *buf); int (*lock)(struct spi_nor *nor, unsigned int region); + int (*erase)(struct spi_nor *nor, loff_t addr); int (*is_locked)(struct spi_nor *nor, unsigned int region); }; @@ -503,10 +505,12 @@ ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len, u8 *buf); ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len, const u8 *buf); +int spi_nor_erase_sector(struct spi_nor *nor, u32 addr); int spi_nor_otp_read_secr(struct spi_nor *nor, loff_t addr, size_t len, u8 *buf); int spi_nor_otp_write_secr(struct spi_nor *nor, loff_t addr, size_t len, const u8 *buf); +int spi_nor_otp_erase_secr(struct spi_nor *nor, loff_t addr); int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region); int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region); diff --git a/drivers/mtd/spi-nor/otp.c b/drivers/mtd/spi-nor/otp.c index 89fe52e3851a..983e40b19134 100644 --- a/drivers/mtd/spi-nor/otp.c +++ b/drivers/mtd/spi-nor/otp.c @@ -120,6 +120,38 @@ out: return ret ?: written; } +/** + * spi_nor_otp_erase_secr() - erase a security register + * @nor: pointer to 'struct spi_nor' + * @addr: offset of the security register to be erased + * + * Erase a security register by using the SPINOR_OP_ESECR command. + * + * For more information on the term "security register", see the documentation + * of spi_nor_otp_read_secr(). + * + * This method is used on GigaDevice and Winbond flashes. + * + * Return: 0 on success, -errno otherwise + */ +int spi_nor_otp_erase_secr(struct spi_nor *nor, loff_t addr) +{ + u8 erase_opcode = nor->erase_opcode; + int ret; + + ret = spi_nor_write_enable(nor); + if (ret) + return ret; + + nor->erase_opcode = SPINOR_OP_ESECR; + ret = spi_nor_erase_sector(nor, addr); + nor->erase_opcode = erase_opcode; + if (ret) + return ret; + + return spi_nor_wait_till_ready(nor); +} + static int spi_nor_otp_lock_bit_cr(unsigned int region) { static const int lock_bits[] = { SR2_LB1, SR2_LB2, SR2_LB3 }; @@ -360,6 +392,59 @@ static int spi_nor_mtd_otp_write(struct mtd_info *mtd, loff_t to, size_t len, return spi_nor_mtd_otp_read_write(mtd, to, len, retlen, buf, true); } +static int spi_nor_mtd_otp_erase(struct mtd_info *mtd, loff_t from, size_t len) +{ + struct spi_nor *nor = mtd_to_spi_nor(mtd); + const struct spi_nor_otp_ops *ops = nor->params->otp.ops; + const size_t rlen = spi_nor_otp_region_len(nor); + unsigned int region; + loff_t rstart; + int ret; + + /* OTP erase is optional */ + if (!ops->erase) + return -EOPNOTSUPP; + + if (!len) + return 0; + + if (from < 0 || (from + len) > spi_nor_otp_size(nor)) + return -EINVAL; + + /* the user has to explicitly ask for whole regions */ + if (!IS_ALIGNED(len, rlen) || !IS_ALIGNED(from, rlen)) + return -EINVAL; + + ret = spi_nor_lock_and_prep(nor); + if (ret) + return ret; + + ret = spi_nor_mtd_otp_range_is_locked(nor, from, len); + if (ret < 0) { + goto out; + } else if (ret) { + ret = -EROFS; + goto out; + } + + while (len) { + region = spi_nor_otp_offset_to_region(nor, from); + rstart = spi_nor_otp_region_start(nor, region); + + ret = ops->erase(nor, rstart); + if (ret) + goto out; + + len -= rlen; + from += rlen; + } + +out: + spi_nor_unlock_and_unprep(nor); + + return ret; +} + static int spi_nor_mtd_otp_lock(struct mtd_info *mtd, loff_t from, size_t len) { struct spi_nor *nor = mtd_to_spi_nor(mtd); @@ -418,4 +503,5 @@ void spi_nor_otp_init(struct spi_nor *nor) mtd->_read_user_prot_reg = spi_nor_mtd_otp_read; mtd->_write_user_prot_reg = spi_nor_mtd_otp_write; mtd->_lock_user_prot_reg = spi_nor_mtd_otp_lock; + mtd->_erase_user_prot_reg = spi_nor_mtd_otp_erase; } diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 9a81c67a60c6..96573f61caf5 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -139,6 +139,7 @@ static int winbond_set_4byte_addr_mode(struct spi_nor *nor, bool enable) static const struct spi_nor_otp_ops winbond_otp_ops = { .read = spi_nor_otp_read_secr, .write = spi_nor_otp_write_secr, + .erase = spi_nor_otp_erase_secr, .lock = spi_nor_otp_lock_sr2, .is_locked = spi_nor_otp_is_locked_sr2, }; -- cgit v1.2.3 From 65b6d89d45a77256b743f421d109d469baefa688 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 3 May 2021 17:56:50 +0200 Subject: 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 Signed-off-by: Vignesh Raghavendra Tested-by: Heiko Thiery Reviewed-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.h | 10 ++++++++ drivers/mtd/spi-nor/sfdp.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index 9398a8738857..f6dc28091c6d 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -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; diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 23c28e91f698..c500c2118a5d 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -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 = ¶m_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. -- cgit v1.2.3 From 36ac0228626585ba718186b9db2e5986a198152c Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Mon, 3 May 2021 17:56:51 +0200 Subject: mtd: spi-nor: add initial sysfs support Add support to show the manufacturer, the partname and JEDEC identifier as well as to dump the SFDP table. Not all flashes list their SFDP table contents in their datasheet. So having that is useful. It might also be helpful in bug reports from users. Signed-off-by: Michael Walle Signed-off-by: Vignesh Raghavendra Acked-by: Pratyush Yadav --- drivers/mtd/spi-nor/Makefile | 2 +- drivers/mtd/spi-nor/core.c | 1 + drivers/mtd/spi-nor/core.h | 2 + drivers/mtd/spi-nor/sysfs.c | 93 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/spi-nor/sysfs.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile index 136f245c91dc..6b904e439372 100644 --- a/drivers/mtd/spi-nor/Makefile +++ b/drivers/mtd/spi-nor/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -spi-nor-objs := core.o sfdp.o swp.o otp.o +spi-nor-objs := core.o sfdp.o swp.o otp.o sysfs.o spi-nor-objs += atmel.o spi-nor-objs += catalyst.o spi-nor-objs += eon.o diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index a21b0085de05..970ed6e3f3ba 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -3459,6 +3459,7 @@ static struct spi_mem_driver spi_nor_driver = { .driver = { .name = "spi-nor", .of_match_table = spi_nor_of_table, + .dev_groups = spi_nor_sysfs_groups, }, .id_table = spi_nor_dev_ids, }, diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h index f6dc28091c6d..3348e1dd1445 100644 --- a/drivers/mtd/spi-nor/core.h +++ b/drivers/mtd/spi-nor/core.h @@ -490,6 +490,8 @@ extern const struct spi_nor_manufacturer spi_nor_winbond; extern const struct spi_nor_manufacturer spi_nor_xilinx; extern const struct spi_nor_manufacturer spi_nor_xmc; +extern const struct attribute_group *spi_nor_sysfs_groups[]; + void spi_nor_spimem_setup_op(const struct spi_nor *nor, struct spi_mem_op *op, const enum spi_nor_protocol proto); diff --git a/drivers/mtd/spi-nor/sysfs.c b/drivers/mtd/spi-nor/sysfs.c new file mode 100644 index 000000000000..9aec9d8a98ad --- /dev/null +++ b/drivers/mtd/spi-nor/sysfs.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#include "core.h" + +static ssize_t manufacturer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_mem *spimem = spi_get_drvdata(spi); + struct spi_nor *nor = spi_mem_get_drvdata(spimem); + + return sysfs_emit(buf, "%s\n", nor->manufacturer->name); +} +static DEVICE_ATTR_RO(manufacturer); + +static ssize_t partname_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_mem *spimem = spi_get_drvdata(spi); + struct spi_nor *nor = spi_mem_get_drvdata(spimem); + + return sysfs_emit(buf, "%s\n", nor->info->name); +} +static DEVICE_ATTR_RO(partname); + +static ssize_t jedec_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_device *spi = to_spi_device(dev); + struct spi_mem *spimem = spi_get_drvdata(spi); + struct spi_nor *nor = spi_mem_get_drvdata(spimem); + + return sysfs_emit(buf, "%*phN\n", nor->info->id_len, nor->info->id); +} +static DEVICE_ATTR_RO(jedec_id); + +static struct attribute *spi_nor_sysfs_entries[] = { + &dev_attr_manufacturer.attr, + &dev_attr_partname.attr, + &dev_attr_jedec_id.attr, + NULL +}; + +static ssize_t sfdp_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct spi_device *spi = to_spi_device(kobj_to_dev(kobj)); + struct spi_mem *spimem = spi_get_drvdata(spi); + struct spi_nor *nor = spi_mem_get_drvdata(spimem); + struct sfdp *sfdp = nor->sfdp; + size_t sfdp_size = sfdp->num_dwords * sizeof(*sfdp->dwords); + + return memory_read_from_buffer(buf, count, &off, nor->sfdp->dwords, + sfdp_size); +} +static BIN_ATTR_RO(sfdp, 0); + +static struct bin_attribute *spi_nor_sysfs_bin_entries[] = { + &bin_attr_sfdp, + NULL +}; + +static umode_t spi_nor_sysfs_is_bin_visible(struct kobject *kobj, + struct bin_attribute *attr, int n) +{ + struct spi_device *spi = to_spi_device(kobj_to_dev(kobj)); + struct spi_mem *spimem = spi_get_drvdata(spi); + struct spi_nor *nor = spi_mem_get_drvdata(spimem); + + if (attr == &bin_attr_sfdp && nor->sfdp) + return 0444; + + return 0; +} + +static const struct attribute_group spi_nor_sysfs_group = { + .name = "spi-nor", + .is_bin_visible = spi_nor_sysfs_is_bin_visible, + .attrs = spi_nor_sysfs_entries, + .bin_attrs = spi_nor_sysfs_bin_entries, +}; + +const struct attribute_group *spi_nor_sysfs_groups[] = { + &spi_nor_sysfs_group, + NULL +}; -- cgit v1.2.3 From c17e5c85b32f8809135f3211ba2525fb98b5c09f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 18 Jun 2021 10:33:31 +0100 Subject: mtd: spi-nor: remove redundant continue statement The continue statement at the end of a for-loop has no effect, invert the if expression and remove the continue. Addresses-Coverity: ("Continue has no effect") Signed-off-by: Colin Ian King Signed-off-by: Vignesh Raghavendra Reviewed-by: Michael Walle Reviewed-by: Pratyush Yadav --- drivers/mtd/spi-nor/core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 970ed6e3f3ba..cc08bd707378 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -1411,9 +1411,7 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map, continue; spi_nor_div_by_erase_size(erase, addr, &rem); - if (rem) - continue; - else + if (!rem) return erase; } -- cgit v1.2.3