Commit 67629667 authored by Miquel Raynal's avatar Miquel Raynal

Merge tag 'spi-nor/for-6.8' into mtd/next

SPI NOR comes with die erase support for multi die flashes, with new
octal protocols (1-1-8 and 1-8-8) parsed from SFDP and with an updated
documentation about what the contributors shall consider when proposing
flash additions or updates. Michael Walle stepped out from the reviewer
role to maintainer.
parents 828f6df1 3c0e1dfa
...@@ -25,6 +25,9 @@ KernelVersion: 5.14 ...@@ -25,6 +25,9 @@ KernelVersion: 5.14
Contact: linux-mtd@lists.infradead.org Contact: linux-mtd@lists.infradead.org
Description: (RO) Part name of the SPI NOR flash. Description: (RO) Part name of the SPI NOR flash.
The attribute is optional. User space should not rely on
it to be present or even correct. Instead, user space
should read the jedec_id attribute.
What: /sys/bus/spi/devices/.../spi-nor/sfdp What: /sys/bus/spi/devices/.../spi-nor/sfdp
Date: April 2021 Date: April 2021
......
...@@ -2,64 +2,204 @@ ...@@ -2,64 +2,204 @@
SPI NOR framework SPI NOR framework
================= =================
Part I - Why do we need this framework? How to propose a new flash addition
--------------------------------------- -----------------------------------
SPI bus controllers (drivers/spi/) only deal with streams of bytes; the bus Most SPI NOR flashes comply with the JEDEC JESD216
controller operates agnostic of the specific device attached. However, some Serial Flash Discoverable Parameter (SFDP) standard. SFDP describes
controllers (such as Freescale's QuadSPI controller) cannot easily handle the functional and feature capabilities of serial flash devices in a
arbitrary streams of bytes, but rather are designed specifically for SPI NOR. standard set of internal read-only parameter tables.
In particular, Freescale's QuadSPI controller must know the NOR commands to The SPI NOR driver queries the SFDP tables in order to determine the
find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of flash's parameters and settings. If the flash defines the SFDP tables
opcodes, addresses, or data payloads; a SPI controller simply knows to send or it's likely that you won't need a flash entry at all, and instead
receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under rely on the generic flash driver which probes the flash solely based
which the controller driver is aware of the opcodes, addressing, and other on its SFDP data. All one has to do is to specify the "jedec,spi-nor"
details of the SPI NOR protocol. compatible in the device tree.
Part II - How does the framework work? There are cases however where you need to define an explicit flash
-------------------------------------- entry. This typically happens when the flash has settings or support
that is not covered by the SFDP tables (e.g. Block Protection), or
This framework just adds a new layer between the MTD and the SPI bus driver. when the flash contains mangled SFDP data. If the later, one needs
With this new layer, the SPI NOR controller driver does not depend on the to implement the ``spi_nor_fixups`` hooks in order to amend the SFDP
m25p80 code anymore. parameters with the correct values.
Before this framework, the layer is like:: Minimum testing requirements
-----------------------------
MTD
------------------------ Do all the tests from below and paste them in the commit's comments
m25p80 section, after the ``---`` marker.
------------------------
SPI bus driver 1) Specify the controller that you used to test the flash and specify
------------------------ the frequency at which the flash was operated, e.g.::
SPI NOR chip
This flash is populated on the X board and was tested at Y
After this framework, the layer is like:: frequency using the Z (put compatible) SPI controller.
MTD 2) Dump the sysfs entries and print the md5/sha1/sha256 SFDP checksum::
------------------------
SPI NOR framework root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
------------------------ sst26vf064b
m25p80 root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
------------------------ bf2643
SPI bus driver root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
------------------------ sst
SPI NOR chip root@1:~# xxd -p /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
53464450060102ff00060110300000ff81000106000100ffbf0001180002
With the SPI NOR controller driver (Freescale QuadSPI), it looks like:: 0001fffffffffffffffffffffffffffffffffd20f1ffffffff0344eb086b
083b80bbfeffffffffff00ffffff440b0c200dd80fd810d820914824806f
MTD 1d81ed0f773830b030b0f7ffffff29c25cfff030c080ffffffffffffffff
------------------------ ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
SPI NOR framework ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
------------------------ ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
fsl-quadSPI ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
------------------------ ffffffffffffffffffffffffffffffffff0004fff37f0000f57f0000f9ff
SPI NOR chip 7d00f57f0000f37f0000ffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Part III - How can drivers use the framework? ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
--------------------------------------------- ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
The main API is spi_nor_scan(). Before you call the hook, a driver should ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
initialize the necessary fields for spi_nor{}. Please see ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to spi-fsl-qspi.c ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
when you want to write a new driver for a SPI NOR controller. ffffbf2643ffb95ffdff30f260f332ff0a122346ff0f19320f1919ffffff
ffffffff00669938ff05013506040232b03072428de89888a585c09faf5a
ffff06ec060c0003080bffffffffff07ffff0202ff060300fdfd040700fc
0300fefe0202070e
root@1:~# sha256sum /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
428f34d0461876f189ac97f93e68a05fa6428c6650b3b7baf736a921e5898ed1 /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
Please dump the SFDP tables using ``xxd -p``. It enables us to do
the reverse operation and convert the hexdump to binary with
``xxd -rp``. Dumping the SFDP data with ``hexdump -Cv`` is accepted,
but less desirable.
3) Dump debugfs data::
root@1:~# cat /sys/kernel/debug/spi-nor/spi0.0/capabilities
Supported read modes by the flash
1S-1S-1S
opcode 0x03
mode cycles 0
dummy cycles 0
1S-1S-1S (fast read)
opcode 0x0b
mode cycles 0
dummy cycles 8
1S-1S-2S
opcode 0x3b
mode cycles 0
dummy cycles 8
1S-2S-2S
opcode 0xbb
mode cycles 4
dummy cycles 0
1S-1S-4S
opcode 0x6b
mode cycles 0
dummy cycles 8
1S-4S-4S
opcode 0xeb
mode cycles 2
dummy cycles 4
4S-4S-4S
opcode 0x0b
mode cycles 2
dummy cycles 4
Supported page program modes by the flash
1S-1S-1S
opcode 0x02
root@1:~# cat /sys/kernel/debug/spi-nor/spi0.0/params
name sst26vf064b
id bf 26 43 bf 26 43
size 8.00 MiB
write size 1
page size 256
address nbytes 3
flags HAS_LOCK | HAS_16BIT_SR | SOFT_RESET | SWP_IS_VOLATILE
opcodes
read 0xeb
dummy cycles 6
erase 0x20
program 0x02
8D extension none
protocols
read 1S-4S-4S
write 1S-1S-1S
register 1S-1S-1S
erase commands
20 (4.00 KiB) [0]
d8 (8.00 KiB) [1]
d8 (32.0 KiB) [2]
d8 (64.0 KiB) [3]
c7 (8.00 MiB)
sector map
region (in hex) | erase mask | flags
------------------+------------+----------
00000000-00007fff | [01 ] |
00008000-0000ffff | [0 2 ] |
00010000-007effff | [0 3] |
007f0000-007f7fff | [0 2 ] |
007f8000-007fffff | [01 ] |
4) Use `mtd-utils <https://git.infradead.org/mtd-utils.git>`__
and verify that erase, read and page program operations work fine::
root@1:~# dd if=/dev/urandom of=./spi_test bs=1M count=2
2+0 records in
2+0 records out
2097152 bytes (2.1 MB, 2.0 MiB) copied, 0.848566 s, 2.5 MB/s
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:~# hexdump spi_read
0000000 ffff ffff ffff ffff ffff ffff ffff ffff
*
0200000
root@1:~# sha256sum spi_read
4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5 spi_read
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
If the flash comes erased by default and the previous erase was ignored,
we won't catch it, thus test the erase again::
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*
4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5 spi_read
c444216a6ba2a4a66cccd60a0dd062bce4b865dd52b200ef5e21838c4b899ac8 spi_test
Dump some other relevant data::
root@1:~# mtd_debug info /dev/mtd0
mtd.type = MTD_NORFLASH
mtd.flags = MTD_CAP_NORFLASH
mtd.size = 8388608 (8M)
mtd.erasesize = 4096 (4K)
mtd.writesize = 1
mtd.oobsize = 0
regions = 0
...@@ -9045,7 +9045,7 @@ F: drivers/gpio/gpio-mockup.c ...@@ -9045,7 +9045,7 @@ F: drivers/gpio/gpio-mockup.c
F: tools/testing/selftests/gpio/ F: tools/testing/selftests/gpio/
GPIO REGMAP GPIO REGMAP
M: Michael Walle <michael@walle.cc> M: Michael Walle <mwalle@kernel.org>
S: Maintained S: Maintained
F: drivers/gpio/gpio-regmap.c F: drivers/gpio/gpio-regmap.c
F: include/linux/gpio/regmap.h F: include/linux/gpio/regmap.h
...@@ -19901,7 +19901,7 @@ W: http://www.winischhofer.at/linuxsisusbvga.shtml ...@@ -19901,7 +19901,7 @@ W: http://www.winischhofer.at/linuxsisusbvga.shtml
F: drivers/usb/misc/sisusbvga/ F: drivers/usb/misc/sisusbvga/
SL28 CPLD MFD DRIVER SL28 CPLD MFD DRIVER
M: Michael Walle <michael@walle.cc> M: Michael Walle <mwalle@kernel.org>
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml F: Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml
F: Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml F: Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml
...@@ -19916,7 +19916,7 @@ F: drivers/pwm/pwm-sl28cpld.c ...@@ -19916,7 +19916,7 @@ F: drivers/pwm/pwm-sl28cpld.c
F: drivers/watchdog/sl28cpld_wdt.c F: drivers/watchdog/sl28cpld_wdt.c
SL28 VPD NVMEM LAYOUT DRIVER SL28 VPD NVMEM LAYOUT DRIVER
M: Michael Walle <michael@walle.cc> M: Michael Walle <mwalle@kernel.org>
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml F: Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml
F: drivers/nvmem/layouts/sl28vpd.c F: drivers/nvmem/layouts/sl28vpd.c
...@@ -20426,7 +20426,7 @@ F: drivers/pinctrl/spear/ ...@@ -20426,7 +20426,7 @@ F: drivers/pinctrl/spear/
SPI NOR SUBSYSTEM SPI NOR SUBSYSTEM
M: Tudor Ambarus <tudor.ambarus@linaro.org> M: Tudor Ambarus <tudor.ambarus@linaro.org>
M: Pratyush Yadav <pratyush@kernel.org> M: Pratyush Yadav <pratyush@kernel.org>
R: Michael Walle <michael@walle.cc> M: Michael Walle <mwalle@kernel.org>
L: linux-mtd@lists.infradead.org L: linux-mtd@lists.infradead.org
S: Maintained S: Maintained
W: http://www.linux-mtd.infradead.org/ W: http://www.linux-mtd.infradead.org/
......
...@@ -16,12 +16,12 @@ ...@@ -16,12 +16,12 @@
* is to unlock the whole flash array on startup. Therefore, we have to support * is to unlock the whole flash array on startup. Therefore, we have to support
* exactly this operation. * exactly this operation.
*/ */
static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
int ret; int ret;
...@@ -37,7 +37,7 @@ static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -37,7 +37,7 @@ static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return ret; return ret;
} }
static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -69,7 +69,7 @@ static const struct spi_nor_fixups at25fs_nor_fixups = { ...@@ -69,7 +69,7 @@ static const struct spi_nor_fixups at25fs_nor_fixups = {
* Return: 0 on success, -error otherwise. * Return: 0 on success, -error otherwise.
*/ */
static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
uint64_t len, bool is_protect) u64 len, bool is_protect)
{ {
int ret; int ret;
u8 sr; u8 sr;
...@@ -118,20 +118,18 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs, ...@@ -118,20 +118,18 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
return spi_nor_write_sr(nor, nor->bouncebuf, 1); return spi_nor_write_sr(nor, nor->bouncebuf, 1);
} }
static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len)
uint64_t len)
{ {
return atmel_nor_set_global_protection(nor, ofs, len, true); return atmel_nor_set_global_protection(nor, ofs, len, true);
} }
static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len)
uint64_t len)
{ {
return atmel_nor_set_global_protection(nor, ofs, len, false); return atmel_nor_set_global_protection(nor, ofs, len, false);
} }
static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs, static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs,
uint64_t len) u64 len)
{ {
int ret; int ret;
......
...@@ -1060,24 +1060,32 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2) ...@@ -1060,24 +1060,32 @@ static int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2)
} }
/** /**
* spi_nor_erase_chip() - Erase the entire flash memory. * spi_nor_erase_die() - Erase the entire die.
* @nor: pointer to 'struct spi_nor'. * @nor: pointer to 'struct spi_nor'.
* @addr: address of the die.
* @die_size: size of the die.
* *
* Return: 0 on success, -errno otherwise. * Return: 0 on success, -errno otherwise.
*/ */
static int spi_nor_erase_chip(struct spi_nor *nor) static int spi_nor_erase_die(struct spi_nor *nor, loff_t addr, size_t die_size)
{ {
bool multi_die = nor->mtd.size != die_size;
int ret; int ret;
dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd.size >> 10)); dev_dbg(nor->dev, " %lldKiB\n", (long long)(die_size >> 10));
if (nor->spimem) { if (nor->spimem) {
struct spi_mem_op op = SPI_NOR_CHIP_ERASE_OP; struct spi_mem_op op =
SPI_NOR_DIE_ERASE_OP(nor->params->die_erase_opcode,
nor->addr_nbytes, addr, multi_die);
spi_nor_spimem_setup_op(nor, &op, nor->reg_proto); spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
ret = spi_mem_exec_op(nor->spimem, &op); ret = spi_mem_exec_op(nor->spimem, &op);
} else { } else {
if (multi_die)
return -EOPNOTSUPP;
ret = spi_nor_controller_ops_write_reg(nor, ret = spi_nor_controller_ops_write_reg(nor,
SPINOR_OP_CHIP_ERASE, SPINOR_OP_CHIP_ERASE,
NULL, 0); NULL, 0);
...@@ -1792,6 +1800,51 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len) ...@@ -1792,6 +1800,51 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
return ret; return ret;
} }
static int spi_nor_erase_dice(struct spi_nor *nor, loff_t addr,
size_t len, size_t die_size)
{
unsigned long timeout;
int ret;
/*
* Scale the timeout linearly with the size of the flash, with
* a minimum calibrated to an old 2MB flash. We could try to
* pull these from CFI/SFDP, but these values should be good
* enough for now.
*/
timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
(unsigned long)(nor->mtd.size / SZ_2M));
do {
ret = spi_nor_lock_device(nor);
if (ret)
return ret;
ret = spi_nor_write_enable(nor);
if (ret) {
spi_nor_unlock_device(nor);
return ret;
}
ret = spi_nor_erase_die(nor, addr, die_size);
spi_nor_unlock_device(nor);
if (ret)
return ret;
ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
if (ret)
return ret;
addr += die_size;
len -= die_size;
} while (len);
return 0;
}
/* /*
* Erase an address range on the nor chip. The address range may extend * Erase an address range on the nor chip. The address range may extend
* one or more erase sectors. Return an error if there is a problem erasing. * one or more erase sectors. Return an error if there is a problem erasing.
...@@ -1799,8 +1852,10 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len) ...@@ -1799,8 +1852,10 @@ static int spi_nor_erase_multi_sectors(struct spi_nor *nor, u64 addr, u32 len)
static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
struct spi_nor *nor = mtd_to_spi_nor(mtd); struct spi_nor *nor = mtd_to_spi_nor(mtd);
u32 addr, len; u8 n_dice = nor->params->n_dice;
uint32_t rem; bool multi_die_erase = false;
u32 addr, len, rem;
size_t die_size;
int ret; int ret;
dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr, dev_dbg(nor->dev, "at 0x%llx, len %lld\n", (long long)instr->addr,
...@@ -1815,39 +1870,22 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1815,39 +1870,22 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
addr = instr->addr; addr = instr->addr;
len = instr->len; len = instr->len;
if (n_dice) {
die_size = div_u64(mtd->size, n_dice);
if (!(len & (die_size - 1)) && !(addr & (die_size - 1)))
multi_die_erase = true;
} else {
die_size = mtd->size;
}
ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len); ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len);
if (ret) if (ret)
return ret; return ret;
/* whole-chip erase? */ /* chip (die) erase? */
if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { if ((len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) ||
unsigned long timeout; multi_die_erase) {
ret = spi_nor_erase_dice(nor, addr, len, die_size);
ret = spi_nor_lock_device(nor);
if (ret)
goto erase_err;
ret = spi_nor_write_enable(nor);
if (ret) {
spi_nor_unlock_device(nor);
goto erase_err;
}
ret = spi_nor_erase_chip(nor);
spi_nor_unlock_device(nor);
if (ret)
goto erase_err;
/*
* Scale the timeout linearly with the size of the flash, with
* a minimum calibrated to an old 2MB flash. We could try to
* pull these from CFI/SFDP, but these values should be good
* enough for now.
*/
timeout = max(CHIP_ERASE_2MB_READY_WAIT_JIFFIES,
CHIP_ERASE_2MB_READY_WAIT_JIFFIES *
(unsigned long)(mtd->size / SZ_2M));
ret = spi_nor_wait_till_ready_with_timeout(nor, timeout);
if (ret) if (ret)
goto erase_err; goto erase_err;
...@@ -2146,7 +2184,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -2146,7 +2184,7 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
if (is_power_of_2(page_size)) { if (is_power_of_2(page_size)) {
page_offset = addr & (page_size - 1); page_offset = addr & (page_size - 1);
} else { } else {
uint64_t aux = addr; u64 aux = addr;
page_offset = do_div(aux, page_size); page_offset = do_div(aux, page_size);
} }
...@@ -2850,9 +2888,6 @@ static void spi_nor_init_flags(struct spi_nor *nor) ...@@ -2850,9 +2888,6 @@ static void spi_nor_init_flags(struct spi_nor *nor)
nor->flags |= SNOR_F_HAS_SR_BP3_BIT6; nor->flags |= SNOR_F_HAS_SR_BP3_BIT6;
} }
if (flags & NO_CHIP_ERASE)
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
if (flags & SPI_NOR_RWW && nor->params->n_banks > 1 && if (flags & SPI_NOR_RWW && nor->params->n_banks > 1 &&
!nor->controller_ops) !nor->controller_ops)
nor->flags |= SNOR_F_RWW; nor->flags |= SNOR_F_RWW;
...@@ -2897,17 +2932,22 @@ static int spi_nor_late_init_params(struct spi_nor *nor) ...@@ -2897,17 +2932,22 @@ static int spi_nor_late_init_params(struct spi_nor *nor)
return ret; return ret;
} }
/* Needed by some flashes late_init hooks. */
spi_nor_init_flags(nor);
if (nor->info->fixups && nor->info->fixups->late_init) { if (nor->info->fixups && nor->info->fixups->late_init) {
ret = nor->info->fixups->late_init(nor); ret = nor->info->fixups->late_init(nor);
if (ret) if (ret)
return ret; return ret;
} }
if (!nor->params->die_erase_opcode)
nor->params->die_erase_opcode = SPINOR_OP_CHIP_ERASE;
/* Default method kept for backward compatibility. */ /* Default method kept for backward compatibility. */
if (!params->set_4byte_addr_mode) if (!params->set_4byte_addr_mode)
params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr; params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
spi_nor_init_flags(nor);
spi_nor_init_fixup_flags(nor); spi_nor_init_fixup_flags(nor);
/* /*
...@@ -3145,6 +3185,18 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable) ...@@ -3145,6 +3185,18 @@ int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
struct spi_nor_flash_parameter *params = nor->params; struct spi_nor_flash_parameter *params = nor->params;
int ret; int ret;
if (enable) {
/*
* If the RESET# pin isn't hooked up properly, or the system
* otherwise doesn't perform a reset command in the boot
* sequence, it's impossible to 100% protect against unexpected
* reboots (e.g., crashes). Warn the user (or hopefully, system
* designer) that this is bad.
*/
WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
"enabling reset hack; may not recover from unexpected reboots\n");
}
ret = params->set_4byte_addr_mode(nor, enable); ret = params->set_4byte_addr_mode(nor, enable);
if (ret && ret != -ENOTSUPP) if (ret && ret != -ENOTSUPP)
return ret; return ret;
...@@ -3193,20 +3245,8 @@ static int spi_nor_init(struct spi_nor *nor) ...@@ -3193,20 +3245,8 @@ static int spi_nor_init(struct spi_nor *nor)
if (nor->addr_nbytes == 4 && if (nor->addr_nbytes == 4 &&
nor->read_proto != SNOR_PROTO_8_8_8_DTR && nor->read_proto != SNOR_PROTO_8_8_8_DTR &&
!(nor->flags & SNOR_F_4B_OPCODES)) { !(nor->flags & SNOR_F_4B_OPCODES))
/* return spi_nor_set_4byte_addr_mode(nor, true);
* If the RESET# pin isn't hooked up properly, or the system
* otherwise doesn't perform a reset command in the boot
* sequence, it's impossible to 100% protect against unexpected
* reboots (e.g., crashes). Warn the user (or hopefully, system
* designer) that this is bad.
*/
WARN_ONCE(nor->flags & SNOR_F_BROKEN_RESET,
"enabling reset hack; may not recover from unexpected reboots\n");
err = spi_nor_set_4byte_addr_mode(nor, true);
if (err)
return err;
}
return 0; return 0;
} }
...@@ -3452,9 +3492,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, ...@@ -3452,9 +3492,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
{ {
const struct flash_info *info; const struct flash_info *info;
struct device *dev = nor->dev; struct device *dev = nor->dev;
struct mtd_info *mtd = &nor->mtd;
int ret; int ret;
int i;
ret = spi_nor_check(nor); ret = spi_nor_check(nor);
if (ret) if (ret)
...@@ -3518,25 +3556,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, ...@@ -3518,25 +3556,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
/* No mtd_info fields should be used up to this point. */ /* No mtd_info fields should be used up to this point. */
spi_nor_set_mtd_info(nor); spi_nor_set_mtd_info(nor);
dev_info(dev, "%s (%lld Kbytes)\n", info->name, dev_dbg(dev, "Manufacturer and device ID: %*phN\n",
(long long)mtd->size >> 10); SPI_NOR_MAX_ID_LEN, nor->id);
dev_dbg(dev,
"mtd .name = %s, .size = 0x%llx (%lldMiB), "
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
mtd->name, (long long)mtd->size, (long long)(mtd->size >> 20),
mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions);
if (mtd->numeraseregions)
for (i = 0; i < mtd->numeraseregions; i++)
dev_dbg(dev,
"mtd.eraseregions[%d] = { .offset = 0x%llx, "
".erasesize = 0x%.8x (%uKiB), "
".numblocks = %d }\n",
i, (long long)mtd->eraseregions[i].offset,
mtd->eraseregions[i].erasesize,
mtd->eraseregions[i].erasesize / 1024,
mtd->eraseregions[i].numblocks);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(spi_nor_scan); EXPORT_SYMBOL_GPL(spi_nor_scan);
......
...@@ -85,9 +85,9 @@ ...@@ -85,9 +85,9 @@
SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA) SPI_MEM_OP_NO_DATA)
#define SPI_NOR_CHIP_ERASE_OP \ #define SPI_NOR_DIE_ERASE_OP(opcode, addr_nbytes, addr, dice) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), \ SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_ADDR(dice ? addr_nbytes : 0, addr, 0), \
SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA) SPI_MEM_OP_NO_DATA)
...@@ -293,9 +293,9 @@ struct spi_nor_erase_map { ...@@ -293,9 +293,9 @@ struct spi_nor_erase_map {
* @is_locked: check if a region of the SPI NOR is completely locked * @is_locked: check if a region of the SPI NOR is completely locked
*/ */
struct spi_nor_locking_ops { struct spi_nor_locking_ops {
int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*lock)(struct spi_nor *nor, loff_t ofs, u64 len);
int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*unlock)(struct spi_nor *nor, loff_t ofs, u64 len);
int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len); int (*is_locked)(struct spi_nor *nor, loff_t ofs, u64 len);
}; };
/** /**
...@@ -362,6 +362,7 @@ struct spi_nor_otp { ...@@ -362,6 +362,7 @@ struct spi_nor_otp {
* command in octal DTR mode. * command in octal DTR mode.
* @n_banks: number of banks. * @n_banks: number of banks.
* @n_dice: number of dice in the flash memory. * @n_dice: number of dice in the flash memory.
* @die_erase_opcode: die erase opcode. Defaults to SPINOR_OP_CHIP_ERASE.
* @vreg_offset: volatile register offset for each die. * @vreg_offset: volatile register offset for each die.
* @hwcaps: describes the read and page program hardware * @hwcaps: describes the read and page program hardware
* capabilities. * capabilities.
...@@ -399,6 +400,7 @@ struct spi_nor_flash_parameter { ...@@ -399,6 +400,7 @@ struct spi_nor_flash_parameter {
u8 rdsr_addr_nbytes; u8 rdsr_addr_nbytes;
u8 n_banks; u8 n_banks;
u8 n_dice; u8 n_dice;
u8 die_erase_opcode;
u32 *vreg_offset; u32 *vreg_offset;
struct spi_nor_hwcaps hwcaps; struct spi_nor_hwcaps hwcaps;
...@@ -463,7 +465,7 @@ struct spi_nor_id { ...@@ -463,7 +465,7 @@ struct spi_nor_id {
* struct flash_info - SPI NOR flash_info entry. * struct flash_info - SPI NOR flash_info entry.
* @id: pointer to struct spi_nor_id or NULL, which means "no ID" (mostly * @id: pointer to struct spi_nor_id or NULL, which means "no ID" (mostly
* older chips). * older chips).
* @name: the name of the flash. * @name: (obsolete) the name of the flash. Do not set it for new additions.
* @size: the size of the flash in bytes. * @size: the size of the flash in bytes.
* @sector_size: (optional) the size listed here is what works with * @sector_size: (optional) the size listed here is what works with
* SPINOR_OP_SE, which isn't necessarily called a "sector" by * SPINOR_OP_SE, which isn't necessarily called a "sector" by
...@@ -487,7 +489,6 @@ struct spi_nor_id { ...@@ -487,7 +489,6 @@ struct spi_nor_id {
* Usually these will power-up in a write-protected * Usually these will power-up in a write-protected
* state. * state.
* SPI_NOR_NO_ERASE: no erase command needed. * SPI_NOR_NO_ERASE: no erase command needed.
* NO_CHIP_ERASE: chip does not support chip erase.
* SPI_NOR_NO_FR: can't do fastread. * SPI_NOR_NO_FR: can't do fastread.
* SPI_NOR_QUAD_PP: flash supports Quad Input Page Program. * SPI_NOR_QUAD_PP: flash supports Quad Input Page Program.
* SPI_NOR_RWW: flash supports reads while write. * SPI_NOR_RWW: flash supports reads while write.
...@@ -537,10 +538,9 @@ struct flash_info { ...@@ -537,10 +538,9 @@ struct flash_info {
#define SPI_NOR_BP3_SR_BIT6 BIT(4) #define SPI_NOR_BP3_SR_BIT6 BIT(4)
#define SPI_NOR_SWP_IS_VOLATILE BIT(5) #define SPI_NOR_SWP_IS_VOLATILE BIT(5)
#define SPI_NOR_NO_ERASE BIT(6) #define SPI_NOR_NO_ERASE BIT(6)
#define NO_CHIP_ERASE BIT(7) #define SPI_NOR_NO_FR BIT(7)
#define SPI_NOR_NO_FR BIT(8) #define SPI_NOR_QUAD_PP BIT(8)
#define SPI_NOR_QUAD_PP BIT(9) #define SPI_NOR_RWW BIT(9)
#define SPI_NOR_RWW BIT(10)
u8 no_sfdp_flags; u8 no_sfdp_flags;
#define SPI_NOR_SKIP_SFDP BIT(0) #define SPI_NOR_SKIP_SFDP BIT(0)
......
...@@ -138,7 +138,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data) ...@@ -138,7 +138,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { if (!(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf)); string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
seq_printf(s, " %02x (%s)\n", SPINOR_OP_CHIP_ERASE, buf); seq_printf(s, " %02x (%s)\n", nor->params->die_erase_opcode, buf);
} }
seq_puts(s, "\nsector map\n"); seq_puts(s, "\nsector map\n");
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
/* flash_info mfr_flag. Used to read proprietary FSR register. */ /* flash_info mfr_flag. Used to read proprietary FSR register. */
#define USE_FSR BIT(0) #define USE_FSR BIT(0)
#define SPINOR_OP_MT_DIE_ERASE 0xc4 /* Chip (die) erase opcode */
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ #define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */ #define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */
...@@ -192,6 +193,50 @@ static struct spi_nor_fixups mt25qu512a_fixups = { ...@@ -192,6 +193,50 @@ static struct spi_nor_fixups mt25qu512a_fixups = {
.post_bfpt = mt25qu512a_post_bfpt_fixup, .post_bfpt = mt25qu512a_post_bfpt_fixup,
}; };
static int st_nor_four_die_late_init(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
params->n_dice = 4;
/*
* Unfortunately the die erase opcode does not have a 4-byte opcode
* correspondent for these flashes. The SFDP 4BAIT table fails to
* consider the die erase too. We're forced to enter in the 4 byte
* address mode in order to benefit of the die erase.
*/
return spi_nor_set_4byte_addr_mode(nor, true);
}
static int st_nor_two_die_late_init(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
params->die_erase_opcode = SPINOR_OP_MT_DIE_ERASE;
params->n_dice = 2;
/*
* Unfortunately the die erase opcode does not have a 4-byte opcode
* correspondent for these flashes. The SFDP 4BAIT table fails to
* consider the die erase too. We're forced to enter in the 4 byte
* address mode in order to benefit of the die erase.
*/
return spi_nor_set_4byte_addr_mode(nor, true);
}
static struct spi_nor_fixups n25q00_fixups = {
.late_init = st_nor_four_die_late_init,
};
static struct spi_nor_fixups mt25q01_fixups = {
.late_init = st_nor_two_die_late_init,
};
static struct spi_nor_fixups mt25q02_fixups = {
.late_init = st_nor_four_die_late_init,
};
static const struct flash_info st_nor_parts[] = { static const struct flash_info st_nor_parts[] = {
{ {
.name = "m25p05-nonjedec", .name = "m25p05-nonjedec",
...@@ -366,16 +411,17 @@ static const struct flash_info st_nor_parts[] = { ...@@ -366,16 +411,17 @@ static const struct flash_info st_nor_parts[] = {
.name = "n25q00", .name = "n25q00",
.size = SZ_128M, .size = SZ_128M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP | .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
SPI_NOR_BP3_SR_BIT6 | NO_CHIP_ERASE, SPI_NOR_BP3_SR_BIT6,
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
.mfr_flags = USE_FSR, .mfr_flags = USE_FSR,
.fixups = &n25q00_fixups,
}, { }, {
.id = SNOR_ID(0x20, 0xba, 0x22), .id = SNOR_ID(0x20, 0xba, 0x22),
.name = "mt25ql02g", .name = "mt25ql02g",
.size = SZ_256M, .size = SZ_256M,
.flags = NO_CHIP_ERASE,
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
.mfr_flags = USE_FSR, .mfr_flags = USE_FSR,
.fixups = &mt25q02_fixups,
}, { }, {
.id = SNOR_ID(0x20, 0xbb, 0x15), .id = SNOR_ID(0x20, 0xbb, 0x15),
.name = "n25q016a", .name = "n25q016a",
...@@ -429,20 +475,25 @@ static const struct flash_info st_nor_parts[] = { ...@@ -429,20 +475,25 @@ static const struct flash_info st_nor_parts[] = {
SPI_NOR_BP3_SR_BIT6, SPI_NOR_BP3_SR_BIT6,
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
.mfr_flags = USE_FSR, .mfr_flags = USE_FSR,
}, {
.id = SNOR_ID(0x20, 0xbb, 0x21, 0x10, 0x44, 0x00),
.name = "mt25qu01g",
.mfr_flags = USE_FSR,
.fixups = &mt25q01_fixups,
}, { }, {
.id = SNOR_ID(0x20, 0xbb, 0x21), .id = SNOR_ID(0x20, 0xbb, 0x21),
.name = "n25q00a", .name = "n25q00a",
.size = SZ_128M, .size = SZ_128M,
.flags = NO_CHIP_ERASE,
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ, .no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
.mfr_flags = USE_FSR, .mfr_flags = USE_FSR,
.fixups = &n25q00_fixups,
}, { }, {
.id = SNOR_ID(0x20, 0xbb, 0x22), .id = SNOR_ID(0x20, 0xbb, 0x22),
.name = "mt25qu02g", .name = "mt25qu02g",
.size = SZ_256M, .size = SZ_256M,
.flags = NO_CHIP_ERASE,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.mfr_flags = USE_FSR, .mfr_flags = USE_FSR,
.fixups = &mt25q02_fixups,
} }
}; };
......
...@@ -446,6 +446,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, ...@@ -446,6 +446,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
u32 dword; u32 dword;
u16 half; u16 half;
u8 erase_mask; u8 erase_mask;
u8 wait_states, mode_clocks, opcode;
/* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */ /* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
if (bfpt_header->length < BFPT_DWORD_MAX_JESD216) if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
...@@ -631,6 +632,32 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor, ...@@ -631,6 +632,32 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B) if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B)
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt); return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt);
/* Parse 1-1-8 read instruction */
opcode = FIELD_GET(BFPT_DWORD17_RD_1_1_8_CMD, bfpt.dwords[SFDP_DWORD(17)]);
if (opcode) {
mode_clocks = FIELD_GET(BFPT_DWORD17_RD_1_1_8_MODE_CLOCKS,
bfpt.dwords[SFDP_DWORD(17)]);
wait_states = FIELD_GET(BFPT_DWORD17_RD_1_1_8_WAIT_STATES,
bfpt.dwords[SFDP_DWORD(17)]);
params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_1_8],
mode_clocks, wait_states, opcode,
SNOR_PROTO_1_1_8);
}
/* Parse 1-8-8 read instruction */
opcode = FIELD_GET(BFPT_DWORD17_RD_1_8_8_CMD, bfpt.dwords[SFDP_DWORD(17)]);
if (opcode) {
mode_clocks = FIELD_GET(BFPT_DWORD17_RD_1_8_8_MODE_CLOCKS,
bfpt.dwords[SFDP_DWORD(17)]);
wait_states = FIELD_GET(BFPT_DWORD17_RD_1_8_8_WAIT_STATES,
bfpt.dwords[SFDP_DWORD(17)]);
params->hwcaps.mask |= SNOR_HWCAPS_READ_1_8_8;
spi_nor_set_read_settings(&params->reads[SNOR_CMD_READ_1_8_8],
mode_clocks, wait_states, opcode,
SNOR_PROTO_1_8_8);
}
/* 8D-8D-8D command extension. */ /* 8D-8D-8D command extension. */
switch (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) { switch (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) {
case BFPT_DWORD18_CMD_EXT_REP: case BFPT_DWORD18_CMD_EXT_REP:
...@@ -968,6 +995,8 @@ static int spi_nor_parse_4bait(struct spi_nor *nor, ...@@ -968,6 +995,8 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
{ SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) }, { SNOR_HWCAPS_READ_1_1_1_DTR, BIT(13) },
{ SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) }, { SNOR_HWCAPS_READ_1_2_2_DTR, BIT(14) },
{ SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) }, { SNOR_HWCAPS_READ_1_4_4_DTR, BIT(15) },
{ SNOR_HWCAPS_READ_1_1_8, BIT(20) },
{ SNOR_HWCAPS_READ_1_8_8, BIT(21) },
}; };
static const struct sfdp_4bait programs[] = { static const struct sfdp_4bait programs[] = {
{ SNOR_HWCAPS_PP, BIT(6) }, { SNOR_HWCAPS_PP, BIT(6) },
......
...@@ -118,6 +118,13 @@ struct sfdp_bfpt { ...@@ -118,6 +118,13 @@ struct sfdp_bfpt {
(BFPT_DWORD16_EN4B_EN4B | BFPT_DWORD16_EX4B_EX4B) (BFPT_DWORD16_EN4B_EN4B | BFPT_DWORD16_EX4B_EX4B)
#define BFPT_DWORD16_SWRST_EN_RST BIT(12) #define BFPT_DWORD16_SWRST_EN_RST BIT(12)
#define BFPT_DWORD17_RD_1_1_8_CMD GENMASK(31, 24)
#define BFPT_DWORD17_RD_1_1_8_MODE_CLOCKS GENMASK(23, 21)
#define BFPT_DWORD17_RD_1_1_8_WAIT_STATES GENMASK(20, 16)
#define BFPT_DWORD17_RD_1_8_8_CMD GENMASK(15, 8)
#define BFPT_DWORD17_RD_1_8_8_MODE_CLOCKS GENMASK(7, 5)
#define BFPT_DWORD17_RD_1_8_8_WAIT_STATES GENMASK(4, 0)
#define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29) #define BFPT_DWORD18_CMD_EXT_MASK GENMASK(30, 29)
#define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */ #define BFPT_DWORD18_CMD_EXT_REP (0x0UL << 29) /* Repeat */
#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */ #define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */ #define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
#define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */ #define SPINOR_OP_CLPEF 0x82 /* Clear program/erase failure flags */
#define SPINOR_OP_CYPRESS_DIE_ERASE 0x61 /* Chip (die) erase */
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */ #define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */ #define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
#define SPINOR_REG_CYPRESS_VREG 0x00800000 #define SPINOR_REG_CYPRESS_VREG 0x00800000
...@@ -644,6 +645,7 @@ static int s25hx_t_late_init(struct spi_nor *nor) ...@@ -644,6 +645,7 @@ static int s25hx_t_late_init(struct spi_nor *nor)
params->ready = cypress_nor_sr_ready_and_clear; params->ready = cypress_nor_sr_ready_and_clear;
cypress_nor_ecc_init(nor); cypress_nor_ecc_init(nor);
params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
return 0; return 0;
} }
...@@ -933,7 +935,6 @@ static const struct flash_info spansion_nor_parts[] = { ...@@ -933,7 +935,6 @@ static const struct flash_info spansion_nor_parts[] = {
.id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90),
.name = "s25hl02gt", .name = "s25hl02gt",
.mfr_flags = USE_CLPEF, .mfr_flags = USE_CLPEF,
.flags = NO_CHIP_ERASE,
.fixups = &s25hx_t_fixups .fixups = &s25hx_t_fixups
}, { }, {
.id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90),
...@@ -954,7 +955,6 @@ static const struct flash_info spansion_nor_parts[] = { ...@@ -954,7 +955,6 @@ static const struct flash_info spansion_nor_parts[] = {
.id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90),
.name = "s25hs02gt", .name = "s25hs02gt",
.mfr_flags = USE_CLPEF, .mfr_flags = USE_CLPEF,
.flags = NO_CHIP_ERASE,
.fixups = &s25hx_t_fixups .fixups = &s25hx_t_fixups
}, { }, {
.id = SNOR_ID(0x34, 0x5a, 0x1a), .id = SNOR_ID(0x34, 0x5a, 0x1a),
......
...@@ -13,12 +13,12 @@ ...@@ -13,12 +13,12 @@
#define SST26VF_CR_BPNV BIT(3) #define SST26VF_CR_BPNV BIT(3)
static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
int ret; int ret;
...@@ -38,7 +38,7 @@ static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -38,7 +38,7 @@ static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
return spi_nor_global_block_unlock(nor); return spi_nor_global_block_unlock(nor);
} }
static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) static int sst26vf_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -53,7 +53,7 @@ static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor) ...@@ -53,7 +53,7 @@ static u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
} }
static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
uint64_t *len) u64 *len)
{ {
struct mtd_info *mtd = &nor->mtd; struct mtd_info *mtd = &nor->mtd;
u64 min_prot_len; u64 min_prot_len;
...@@ -90,10 +90,10 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs, ...@@ -90,10 +90,10 @@ static void spi_nor_get_locked_range_sr(struct spi_nor *nor, u8 sr, loff_t *ofs,
* (if @locked is false); false otherwise. * (if @locked is false); false otherwise.
*/ */
static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs,
uint64_t len, u8 sr, bool locked) u64 len, u8 sr, bool locked)
{ {
loff_t lock_offs, lock_offs_max, offs_max; loff_t lock_offs, lock_offs_max, offs_max;
uint64_t lock_len; u64 lock_len;
if (!len) if (!len)
return true; return true;
...@@ -111,14 +111,13 @@ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs, ...@@ -111,14 +111,13 @@ static bool spi_nor_check_lock_status_sr(struct spi_nor *nor, loff_t ofs,
return (ofs >= lock_offs_max) || (offs_max <= lock_offs); return (ofs >= lock_offs_max) || (offs_max <= lock_offs);
} }
static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, u8 sr)
u8 sr)
{ {
return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true); return spi_nor_check_lock_status_sr(nor, ofs, len, sr, true);
} }
static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len,
uint64_t len, u8 sr) u8 sr)
{ {
return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false); return spi_nor_check_lock_status_sr(nor, ofs, len, sr, false);
} }
...@@ -156,7 +155,7 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, ...@@ -156,7 +155,7 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs,
* *
* Returns negative on errors, 0 on success. * Returns negative on errors, 0 on success.
*/ */
static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
struct mtd_info *mtd = &nor->mtd; struct mtd_info *mtd = &nor->mtd;
u64 min_prot_len; u64 min_prot_len;
...@@ -246,7 +245,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -246,7 +245,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
* *
* Returns negative on errors, 0 on success. * Returns negative on errors, 0 on success.
*/ */
static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
struct mtd_info *mtd = &nor->mtd; struct mtd_info *mtd = &nor->mtd;
u64 min_prot_len; u64 min_prot_len;
...@@ -331,7 +330,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) ...@@ -331,7 +330,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
* Returns 1 if entire region is locked, 0 if any portion is unlocked, and * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
* negative on errors. * negative on errors.
*/ */
static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len) static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
{ {
int ret; int ret;
...@@ -353,7 +352,7 @@ void spi_nor_init_default_locking_ops(struct spi_nor *nor) ...@@ -353,7 +352,7 @@ void spi_nor_init_default_locking_ops(struct spi_nor *nor)
nor->params->locking_ops = &spi_nor_sr_locking_ops; nor->params->locking_ops = &spi_nor_sr_locking_ops;
} }
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, u64 len)
{ {
struct spi_nor *nor = mtd_to_spi_nor(mtd); struct spi_nor *nor = mtd_to_spi_nor(mtd);
int ret; int ret;
...@@ -368,7 +367,7 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -368,7 +367,7 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
return ret; return ret;
} }
static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, u64 len)
{ {
struct spi_nor *nor = mtd_to_spi_nor(mtd); struct spi_nor *nor = mtd_to_spi_nor(mtd);
int ret; int ret;
...@@ -383,7 +382,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -383,7 +382,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
return ret; return ret;
} }
static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, u64 len)
{ {
struct spi_nor *nor = mtd_to_spi_nor(mtd); struct spi_nor *nor = mtd_to_spi_nor(mtd);
int ret; int ret;
......
...@@ -78,6 +78,8 @@ static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj, ...@@ -78,6 +78,8 @@ static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj,
if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer) if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer)
return 0; return 0;
if (attr == &dev_attr_partname.attr && !nor->info->name)
return 0;
if (attr == &dev_attr_jedec_id.attr && !nor->info->id && !nor->id) if (attr == &dev_attr_jedec_id.attr && !nor->info->id && !nor->id)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment