mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'spi-nor/for-7.2' into mtd/next
SPI NOR changes for 7.2 Notable changes: - Big set of cleanups and improvements to the locking support. This series contains some cleanups and bug fixes for code and documentation around write protection. Then support is added for complement locking, which allows finer grained configuration of what is considered locked and unlocked. Then complement locking is enabled on a bunch of Winbond W25 flashes. - Fix die erase support on Spansion flashes. Die erase is only supported on multi-die flashes, but the die erase opcode was set for all. When the opcode is set, it overrides the default chip erase opcode which should be used for single-die flashes. Only set the opcode on multi-die flashes. Also, the opcode was not set on multi-die s28hx-t flashes. Set it so they can use die-erase correctly.
This commit is contained in:
@@ -203,3 +203,173 @@ section, after the ``---`` marker.
|
||||
mtd.writesize = 1
|
||||
mtd.oobsize = 0
|
||||
regions = 0
|
||||
|
||||
5) If your flash supports locking, please go through the following test
|
||||
procedure to make sure it correctly behaves. The below example
|
||||
expects the typical situation where eraseblocks and lock sectors have
|
||||
the same size. In case you enabled MTD_SPI_NOR_USE_4K_SECTORS, you
|
||||
must adapt `bs` accordingly.
|
||||
|
||||
Warning: These tests may hard lock your device! Make sure:
|
||||
|
||||
- The device is not hard locked already (#WP strapped to low and
|
||||
SR_SRWD bit set)
|
||||
- If you have a WPn pin, you may want to set `no-wp` in your DT for
|
||||
the time of the test, to only make use of software protection.
|
||||
Otherwise, clearing the locking state depends on the WPn
|
||||
signal and if it is tied to low, the flash will be permanently
|
||||
locked.
|
||||
|
||||
Test full chip locking and make sure expectations, the MEMISLOCKED
|
||||
ioctl output, the debugfs output and experimental results are all
|
||||
aligned::
|
||||
|
||||
root@1:~# alias show_sectors='grep -A4 "locked sectors" /sys/kernel/debug/spi-nor/spi0.0/params'
|
||||
root@1:~# flash_lock -u /dev/mtd0
|
||||
root@1:~# flash_lock -i /dev/mtd0
|
||||
Device: /dev/mtd0
|
||||
Start: 0
|
||||
Len: 0x4000000
|
||||
Lock status: unlocked
|
||||
Return code: 0
|
||||
root@1:~# mtd_debug erase /dev/mtd0 0 2097152
|
||||
Erased 2097152 bytes from address 0x00000000 in flash
|
||||
root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test
|
||||
Copied 2097152 bytes from spi_test to address 0x00000000 in flash
|
||||
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
|
||||
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
|
||||
root@1:~# sha256sum spi*
|
||||
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
|
||||
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-03ffffff | unlocked | 1024
|
||||
|
||||
root@1:~# flash_lock -l /dev/mtd0
|
||||
root@1:~# flash_lock -i /dev/mtd0
|
||||
Device: /dev/mtd0
|
||||
Start: 0
|
||||
Len: 0x4000000
|
||||
Lock status: locked
|
||||
Return code: 1
|
||||
root@1:~# mtd_debug erase /dev/mtd0 0 2097152
|
||||
Erased 2097152 bytes from address 0x00000000 in flash
|
||||
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read
|
||||
Copied 2097152 bytes from address 0x00000000 in flash to spi_read
|
||||
root@1:~# sha256sum spi*
|
||||
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
|
||||
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
|
||||
root@1:~# dd if=/dev/urandom of=./spi_test2 bs=1M count=2
|
||||
2+0 records in
|
||||
2+0 records out
|
||||
root@1:~# mtd_debug write /dev/mtd0 0 2097152 spi_test2
|
||||
Copied 2097152 bytes from spi_test2 to address 0x00000000 in flash
|
||||
root@1:~# mtd_debug read /dev/mtd0 0 2097152 spi_read2
|
||||
Copied 2097152 bytes from address 0x00000000 in flash to spi_read2
|
||||
root@1:~# sha256sum spi*
|
||||
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read
|
||||
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_read2
|
||||
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
|
||||
bea9334df51c620440f86751cba0799214a016329f1736f9456d40cf40efdc88 spi_test2
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-03ffffff | locked | 1024
|
||||
root@1:~# flash_lock -u /dev/mtd0
|
||||
|
||||
Once we trust the debugfs output we can use it to test various
|
||||
situations. Check top locking/unlocking (end of the device)::
|
||||
|
||||
root@1:~# size=$(cat /sys/class/mtd/mtd0/size)
|
||||
root@1:~# bs=$(cat /sys/class/mtd/mtd0/erasesize)
|
||||
root@1:~# nsectors=$(grep unlocked /sys/kernel/debug/spi-nor/spi0.0/params | sed -e 's/.*unlocked | //')
|
||||
root@1:~# ss=$(($size / $nsectors))
|
||||
root@1:~# bps=$(($ss / $bs))
|
||||
|
||||
root@1:~# flash_lock -u /dev/mtd0
|
||||
root@1:~# flash_lock -l /dev/mtd0 $(($size - (2 * $ss))) $((2 * $bps)) # last two
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-03fdffff | unlocked | 1022
|
||||
03fe0000-03ffffff | locked | 2
|
||||
root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $((1 * $bps)) # last one
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-03feffff | unlocked | 1023
|
||||
03ff0000-03ffffff | locked | 1
|
||||
|
||||
If the flash features 4 block protection bits (BP), we can protect
|
||||
more than 4MB (typically 128 64kiB-blocks or more), with a finer
|
||||
grain than locking the entire device::
|
||||
|
||||
root@1:~# flash_lock -u /dev/mtd0
|
||||
root@1:~# flash_lock -l /dev/mtd0 $(($size - (2**7 * $ss))) $((2**7 * $bps))
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-037fffff | unlocked | 896
|
||||
03800000-03ffffff | locked | 128
|
||||
|
||||
If the flash features a Top/Bottom (TB) bit, we can protect the
|
||||
beginning of the flash::
|
||||
|
||||
root@1:~# flash_lock -u /dev/mtd0
|
||||
root@1:~# flash_lock -l /dev/mtd0 0 $((2 * $bps)) # first two
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-0001ffff | locked | 2
|
||||
00020000-03ffffff | unlocked | 1022
|
||||
root@1:~# flash_lock -u /dev/mtd0 $ss $((1 * $bps)) # first one
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-0000ffff | locked | 1
|
||||
00010000-03ffffff | unlocked | 1023
|
||||
|
||||
If the flash features a Complement (CMP) bit, we can protect with
|
||||
more granularity above half of the capacity. Let's lock all but one
|
||||
block, then unlock one more block::
|
||||
|
||||
root@1:~# all_but_one=$((($size / $bs) - ($ss / $bs)))
|
||||
|
||||
root@1:~# flash_lock -u /dev/mtd0
|
||||
root@1:~# flash_lock -l /dev/mtd0 $ss $all_but_one # all but the first
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-0000ffff | unlocked | 1
|
||||
00010000-03ffffff | locked | 1023
|
||||
root@1:~# flash_lock -u /dev/mtd0 $ss $(($ss / $bs)) # all but the two first
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-0001ffff | unlocked | 2
|
||||
00020000-03ffffff | locked | 1022
|
||||
root@1:~# flash_lock -u /dev/mtd0
|
||||
root@1:~# flash_lock -l /dev/mtd0 0 $all_but_one # same from the other side
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-03feffff | locked | 1023
|
||||
03ff0000-03ffffff | unlocked | 1
|
||||
root@1:~# flash_lock -u /dev/mtd0 $(($size - (2 * $ss))) $(($ss / $bs)) # all but two
|
||||
root@1:~# show_sectors
|
||||
software locked sectors
|
||||
region (in hex) | status | #sectors
|
||||
------------------+----------+---------
|
||||
00000000-03fdffff | locked | 1022
|
||||
03fe0000-03ffffff | unlocked | 2
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
menuconfig MTD_SPI_NOR
|
||||
tristate "SPI NOR device support"
|
||||
depends on MTD
|
||||
depends on MTD && SPI_MASTER
|
||||
select SPI_MEM
|
||||
help
|
||||
|
||||
@@ -869,8 +869,8 @@ static int spi_nor_write_16bit_sr_and_check(struct spi_nor *nor, u8 sr1)
|
||||
ret = spi_nor_read_cr(nor, &sr_cr[1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (spi_nor_get_protocol_width(nor->read_proto) == 4 &&
|
||||
spi_nor_get_protocol_width(nor->write_proto) == 4 &&
|
||||
} else if ((spi_nor_get_protocol_width(nor->read_proto) == 4 ||
|
||||
spi_nor_get_protocol_width(nor->write_proto) == 4) &&
|
||||
nor->params->quad_enable) {
|
||||
/*
|
||||
* If the Status Register 2 Read command (35h) is not
|
||||
@@ -976,6 +976,54 @@ int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_write_16bit_sr_cr_and_check() - Write the Status Register 1 and the
|
||||
* Configuration Register in one shot. Ensure that the bytes written in both
|
||||
* registers match the received value.
|
||||
* @nor: pointer to a 'struct spi_nor'.
|
||||
* @regs: two-byte array with values to be written to the status and
|
||||
* configuration registers.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
static int spi_nor_write_16bit_sr_cr_and_check(struct spi_nor *nor, const u8 *regs)
|
||||
{
|
||||
u8 written_regs[2];
|
||||
int ret;
|
||||
|
||||
written_regs[0] = regs[0];
|
||||
written_regs[1] = regs[1];
|
||||
nor->bouncebuf[0] = regs[0];
|
||||
nor->bouncebuf[1] = regs[1];
|
||||
|
||||
ret = spi_nor_write_sr(nor, nor->bouncebuf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = spi_nor_read_sr(nor, &nor->bouncebuf[0]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (written_regs[0] != nor->bouncebuf[0]) {
|
||||
dev_dbg(nor->dev, "SR: Read back test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (nor->flags & SNOR_F_NO_READ_CR)
|
||||
return 0;
|
||||
|
||||
ret = spi_nor_read_cr(nor, &nor->bouncebuf[1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (written_regs[1] != nor->bouncebuf[1]) {
|
||||
dev_dbg(nor->dev, "CR: read back test failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_write_sr_and_check() - Write the Status Register 1 and ensure that
|
||||
* the byte written match the received value without affecting other bits in the
|
||||
@@ -993,6 +1041,23 @@ int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1)
|
||||
return spi_nor_write_sr1_and_check(nor, sr1);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_write_sr_cr_and_check() - Write the Status Register 1 and ensure that
|
||||
* the byte written match the received value. Same for the Control Register if
|
||||
* available.
|
||||
* @nor: pointer to a 'struct spi_nor'.
|
||||
* @regs: byte array to be written to the registers.
|
||||
*
|
||||
* Return: 0 on success, -errno otherwise.
|
||||
*/
|
||||
int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs)
|
||||
{
|
||||
if (nor->flags & SNOR_F_HAS_16BIT_SR)
|
||||
return spi_nor_write_16bit_sr_cr_and_check(nor, regs);
|
||||
|
||||
return spi_nor_write_sr1_and_check(nor, regs[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_write_sr2() - Write the Status Register 2 using the
|
||||
* SPINOR_OP_WRSR2 (3eh) command.
|
||||
@@ -2909,6 +2974,9 @@ static void spi_nor_init_flags(struct spi_nor *nor)
|
||||
nor->flags |= SNOR_F_HAS_SR_BP3_BIT6;
|
||||
}
|
||||
|
||||
if (flags & SPI_NOR_HAS_CMP)
|
||||
nor->flags |= SNOR_F_HAS_SR2_CMP_BIT6;
|
||||
|
||||
if (flags & SPI_NOR_RWW && nor->params->n_banks > 1 &&
|
||||
!nor->controller_ops)
|
||||
nor->flags |= SNOR_F_RWW;
|
||||
@@ -3261,10 +3329,12 @@ static int spi_nor_init(struct spi_nor *nor)
|
||||
* protection bits are volatile. The latter is indicated by
|
||||
* SNOR_F_SWP_IS_VOLATILE.
|
||||
*/
|
||||
spi_nor_cache_sr_lock_bits(nor, NULL);
|
||||
if (IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE) ||
|
||||
(IS_ENABLED(CONFIG_MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE) &&
|
||||
nor->flags & SNOR_F_SWP_IS_VOLATILE))
|
||||
nor->flags & SNOR_F_SWP_IS_VOLATILE)) {
|
||||
spi_nor_try_unlock_all(nor);
|
||||
}
|
||||
|
||||
if (nor->addr_nbytes == 4 &&
|
||||
nor->read_proto != SNOR_PROTO_8_8_8_DTR &&
|
||||
|
||||
@@ -141,6 +141,7 @@ enum spi_nor_option_flags {
|
||||
SNOR_F_ECC = BIT(15),
|
||||
SNOR_F_NO_WP = BIT(16),
|
||||
SNOR_F_SWAP16 = BIT(17),
|
||||
SNOR_F_HAS_SR2_CMP_BIT6 = BIT(18),
|
||||
};
|
||||
|
||||
struct spi_nor_read_command {
|
||||
@@ -279,9 +280,17 @@ struct spi_nor_erase_map {
|
||||
|
||||
/**
|
||||
* struct spi_nor_locking_ops - SPI NOR locking methods
|
||||
* @lock: lock a region of the SPI NOR.
|
||||
* @unlock: unlock a region of the SPI NOR.
|
||||
* @is_locked: check if a region of the SPI NOR is completely locked
|
||||
* @lock: lock a region of the SPI NOR, never locks more than what is
|
||||
* requested, ie. may lock less.
|
||||
* @unlock: unlock a region of the SPI NOR, may unlock more than what is
|
||||
* requested.
|
||||
* @is_locked: check if a region of the SPI NOR is completely locked, returns
|
||||
* false otherwise. This feedback may be misleading because users
|
||||
* may get an "unlocked" status even though a subpart of the region
|
||||
* is effectively locked.
|
||||
*
|
||||
* If in doubt during development, check-out the debugfs output which tries to
|
||||
* be more user friendly.
|
||||
*/
|
||||
struct spi_nor_locking_ops {
|
||||
int (*lock)(struct spi_nor *nor, loff_t ofs, u64 len);
|
||||
@@ -483,6 +492,8 @@ struct spi_nor_id {
|
||||
* SPI_NOR_NO_ERASE: no erase command needed.
|
||||
* SPI_NOR_QUAD_PP: flash supports Quad Input Page Program.
|
||||
* SPI_NOR_RWW: flash supports reads while write.
|
||||
* SPI_NOR_HAS_CMP: flash SR2 has complement (CMP) protect bit. Must
|
||||
* be used with SPI_NOR_HAS_LOCK.
|
||||
*
|
||||
* @no_sfdp_flags: flags that indicate support that can be discovered via SFDP.
|
||||
* Used when SFDP tables are not defined in the flash. These
|
||||
@@ -531,6 +542,7 @@ struct flash_info {
|
||||
#define SPI_NOR_NO_ERASE BIT(6)
|
||||
#define SPI_NOR_QUAD_PP BIT(8)
|
||||
#define SPI_NOR_RWW BIT(9)
|
||||
#define SPI_NOR_HAS_CMP BIT(10)
|
||||
|
||||
u8 no_sfdp_flags;
|
||||
#define SPI_NOR_SKIP_SFDP BIT(0)
|
||||
@@ -632,6 +644,7 @@ int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
|
||||
int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
|
||||
int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
|
||||
int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr);
|
||||
int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs);
|
||||
|
||||
ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
|
||||
u8 *buf);
|
||||
@@ -672,7 +685,9 @@ int spi_nor_post_bfpt_fixups(struct spi_nor *nor,
|
||||
const struct sfdp_bfpt *bfpt);
|
||||
|
||||
void spi_nor_init_default_locking_ops(struct spi_nor *nor);
|
||||
bool spi_nor_has_default_locking_ops(struct spi_nor *nor);
|
||||
void spi_nor_try_unlock_all(struct spi_nor *nor);
|
||||
void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr);
|
||||
void spi_nor_set_mtd_locking_ops(struct spi_nor *nor);
|
||||
void spi_nor_set_mtd_otp_ops(struct spi_nor *nor);
|
||||
|
||||
@@ -705,6 +720,10 @@ static inline bool spi_nor_needs_sfdp(const struct spi_nor *nor)
|
||||
return !nor->info->size;
|
||||
}
|
||||
|
||||
u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor);
|
||||
void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs, u64 *len);
|
||||
bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, const u8 *sr);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void spi_nor_debugfs_register(struct spi_nor *nor);
|
||||
void spi_nor_debugfs_shutdown(void);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/spi-mem.h>
|
||||
@@ -28,6 +29,8 @@ static const char *const snor_f_names[] = {
|
||||
SNOR_F_NAME(RWW),
|
||||
SNOR_F_NAME(ECC),
|
||||
SNOR_F_NAME(NO_WP),
|
||||
SNOR_F_NAME(SWAP16),
|
||||
SNOR_F_NAME(HAS_SR2_CMP_BIT6),
|
||||
};
|
||||
#undef SNOR_F_NAME
|
||||
|
||||
@@ -76,11 +79,14 @@ static void spi_nor_print_flags(struct seq_file *s, unsigned long flags,
|
||||
static int spi_nor_params_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct spi_nor *nor = s->private;
|
||||
u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
struct spi_nor_erase_map *erase_map = ¶ms->erase_map;
|
||||
struct spi_nor_erase_region *region = erase_map->regions;
|
||||
const struct flash_info *info = nor->info;
|
||||
char buf[16], *str;
|
||||
loff_t lock_start;
|
||||
u64 lock_length;
|
||||
unsigned int i;
|
||||
|
||||
seq_printf(s, "name\t\t%s\n", info->name);
|
||||
@@ -138,12 +144,12 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
|
||||
|
||||
if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
|
||||
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
|
||||
seq_printf(s, " %02x (%s)\n", nor->params->die_erase_opcode, buf);
|
||||
seq_printf(s, " %02x (%s)\n", params->die_erase_opcode, buf);
|
||||
}
|
||||
|
||||
seq_puts(s, "\nsector map\n");
|
||||
seq_puts(s, " region (in hex) | erase mask | overlaid\n");
|
||||
seq_puts(s, " ------------------+------------+----------\n");
|
||||
seq_puts(s, " ------------------+------------+---------\n");
|
||||
for (i = 0; i < erase_map->n_regions; i++) {
|
||||
u64 start = region[i].offset;
|
||||
u64 end = start + region[i].size - 1;
|
||||
@@ -158,10 +164,69 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
|
||||
region[i].overlaid ? "yes" : "no");
|
||||
}
|
||||
|
||||
if (!spi_nor_has_default_locking_ops(nor))
|
||||
return 0;
|
||||
|
||||
seq_puts(s, "\nlocked sectors\n");
|
||||
seq_puts(s, " region (in hex) | status | #sectors\n");
|
||||
seq_puts(s, " ------------------+----------+---------\n");
|
||||
|
||||
spi_nor_get_locked_range_sr(nor, nor->dfs_sr_cache, &lock_start, &lock_length);
|
||||
if (!lock_length || lock_length == params->size) {
|
||||
seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, params->size - 1,
|
||||
lock_length ? " locked" : "unlocked",
|
||||
div_u64(params->size, min_prot_len));
|
||||
} else if (!lock_start) {
|
||||
seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, lock_length - 1,
|
||||
" locked", div_u64(lock_length, min_prot_len));
|
||||
seq_printf(s, " %08llx-%08llx | %s | %llu\n", lock_length, params->size - 1,
|
||||
"unlocked", div_u64(params->size - lock_length, min_prot_len));
|
||||
} else {
|
||||
seq_printf(s, " %08llx-%08llx | %s | %llu\n", 0ULL, lock_start - 1,
|
||||
"unlocked", div_u64(lock_start, min_prot_len));
|
||||
seq_printf(s, " %08llx-%08llx | %s | %llu\n", lock_start, params->size - 1,
|
||||
" locked", div_u64(lock_length, min_prot_len));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(spi_nor_params);
|
||||
|
||||
static int spi_nor_locked_sectors_map_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct spi_nor *nor = s->private;
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
|
||||
unsigned int sector = 0;
|
||||
u64 offset = 0;
|
||||
bool locked;
|
||||
int i;
|
||||
|
||||
seq_printf(s, "Locked sectors map (x: locked, .: unlocked, unit: %lldkiB)\n",
|
||||
min_prot_len / 1024);
|
||||
while (offset < params->size) {
|
||||
seq_printf(s, " 0x%08llx (#%5d): ", offset, sector);
|
||||
for (i = 0; i < 64 && offset < params->size; i++) {
|
||||
locked = spi_nor_is_locked_sr(nor, offset, min_prot_len,
|
||||
nor->dfs_sr_cache);
|
||||
if (locked)
|
||||
seq_puts(s, "x");
|
||||
else
|
||||
seq_puts(s, ".");
|
||||
|
||||
if (((i + 1) % 16) == 0)
|
||||
seq_puts(s, " ");
|
||||
|
||||
offset += min_prot_len;
|
||||
sector++;
|
||||
}
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(spi_nor_locked_sectors_map);
|
||||
|
||||
static void spi_nor_print_read_cmd(struct seq_file *s, u32 cap,
|
||||
struct spi_nor_read_command *cmd)
|
||||
{
|
||||
@@ -247,6 +312,9 @@ void spi_nor_debugfs_register(struct spi_nor *nor)
|
||||
debugfs_create_file("params", 0444, d, nor, &spi_nor_params_fops);
|
||||
debugfs_create_file("capabilities", 0444, d, nor,
|
||||
&spi_nor_capabilities_fops);
|
||||
if (spi_nor_has_default_locking_ops(nor))
|
||||
debugfs_create_file("locked-sectors-map", 0444, d, nor,
|
||||
&spi_nor_locked_sectors_map_fops);
|
||||
}
|
||||
|
||||
void spi_nor_debugfs_shutdown(void)
|
||||
|
||||
@@ -674,7 +674,9 @@ static int s25hx_t_late_init(struct spi_nor *nor)
|
||||
params->ready = cypress_nor_sr_ready_and_clear;
|
||||
cypress_nor_ecc_init(nor);
|
||||
|
||||
params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
|
||||
if (params->n_dice > 1)
|
||||
params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -760,6 +762,9 @@ static int s28hx_t_late_init(struct spi_nor *nor)
|
||||
params->ready = cypress_nor_sr_ready_and_clear;
|
||||
cypress_nor_ecc_init(nor);
|
||||
|
||||
if (params->n_dice > 1)
|
||||
params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+290
-73
File diff suppressed because it is too large
Load Diff
@@ -73,6 +73,26 @@ static const struct spi_nor_fixups w25q256_fixups = {
|
||||
.post_bfpt = w25q256_post_bfpt_fixups,
|
||||
};
|
||||
|
||||
static int
|
||||
winbond_rdcr_post_bfpt_fixup(struct spi_nor *nor,
|
||||
const struct sfdp_parameter_header *bfpt_header,
|
||||
const struct sfdp_bfpt *bfpt)
|
||||
{
|
||||
/*
|
||||
* W25H02NW, unlike its W25H512NW nor W25H01NW cousins, improperly sets
|
||||
* the QE BFPT configuration bits, indicating a non readable CR. This is
|
||||
* both incorrect and impractical, as the chip features a CMP bit for its
|
||||
* locking scheme that lays in the Control Register, and needs to be read.
|
||||
*/
|
||||
nor->flags &= ~SNOR_F_NO_READ_CR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_nor_fixups winbond_rdcr_fixup = {
|
||||
.post_bfpt = winbond_rdcr_post_bfpt_fixup,
|
||||
};
|
||||
|
||||
/**
|
||||
* winbond_nor_select_die() - Set active die.
|
||||
* @nor: pointer to 'struct spi_nor'.
|
||||
@@ -348,27 +368,36 @@ static const struct flash_info winbond_nor_parts[] = {
|
||||
}, {
|
||||
/* W25Q01NWxxIQ */
|
||||
.id = SNOR_ID(0xef, 0x60, 0x21),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
|
||||
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
|
||||
.fixups = &winbond_rdcr_fixup,
|
||||
}, {
|
||||
/* W25Q01NWxxIM */
|
||||
.id = SNOR_ID(0xef, 0x80, 0x21),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
|
||||
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
|
||||
}, {
|
||||
/* W25Q02NWxxIM */
|
||||
.id = SNOR_ID(0xef, 0x80, 0x22),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
|
||||
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
|
||||
.fixups = &winbond_rdcr_fixup,
|
||||
}, {
|
||||
/* W25H512NWxxAM */
|
||||
.id = SNOR_ID(0xef, 0xa0, 0x20),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
|
||||
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
|
||||
}, {
|
||||
/* W25H01NWxxAM */
|
||||
.id = SNOR_ID(0xef, 0xa0, 0x21),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
|
||||
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
|
||||
}, {
|
||||
/* W25H02NWxxAM */
|
||||
.id = SNOR_ID(0xef, 0xa0, 0x22),
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
|
||||
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
|
||||
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
|
||||
.fixups = &winbond_rdcr_fixup,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
/* Flash opcodes. */
|
||||
#define SPINOR_OP_WRDI 0x04 /* Write disable */
|
||||
#define SPINOR_OP_WREN 0x06 /* Write enable */
|
||||
#define SPINOR_OP_RDSR 0x05 /* Read status register */
|
||||
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
|
||||
#define SPINOR_OP_RDSR 0x05 /* Read status register 1 */
|
||||
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 */
|
||||
#define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */
|
||||
#define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */
|
||||
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
|
||||
@@ -125,6 +125,7 @@
|
||||
#define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */
|
||||
#define SR2_LB2 BIT(4) /* Security Register Lock Bit 2 */
|
||||
#define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */
|
||||
#define SR2_CMP_BIT6 BIT(6)
|
||||
#define SR2_QUAD_EN_BIT7 BIT(7)
|
||||
|
||||
/* Supported SPI protocols */
|
||||
@@ -371,6 +372,7 @@ struct spi_nor_flash_parameter;
|
||||
* @reg_proto: the SPI protocol for read_reg/write_reg/erase operations
|
||||
* @sfdp: the SFDP data of the flash
|
||||
* @debugfs_root: pointer to the debugfs directory
|
||||
* @dfs_sr_cache: Status Register cached value for debugfs use only
|
||||
* @controller_ops: SPI NOR controller driver specific operations.
|
||||
* @params: [FLASH-SPECIFIC] SPI NOR flash parameters and settings.
|
||||
* The structure includes legacy flash parameters and
|
||||
@@ -409,6 +411,7 @@ struct spi_nor {
|
||||
enum spi_nor_cmd_ext cmd_ext_type;
|
||||
struct sfdp *sfdp;
|
||||
struct dentry *debugfs_root;
|
||||
u8 dfs_sr_cache[2];
|
||||
|
||||
const struct spi_nor_controller_ops *controller_ops;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user