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. -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEHUIqys8OyG1eHf7fS1VPR6WNFOkFAmWD2u0ACgkQS1VPR6WN FOnX4wgAoWAHaoGjY6adQirIf+vdO7fgwsDU6mNp3ynTAbUQeke/Kd+cleotV1Hv Iz8HvWZdYni1lCEKJaUiFbBniSAuY0YwAYko+KzZRF5mmqx6MBXbGBYU8Nf+tODq SxvmjoGc/Au82SUDm7ZbgNsn3pI7RoqpF93qWUOibFfcYU5kbPDsV2Vmazi5nrDM 4gLvRMaXzY/28GHDEg6MZ+ZLKy37iZ0hlr3qARFbxCzLJqensSh8wlAwVl1rb49G GcSYXh9aW6JAIWa+wU5eJjnpUxl6YRQtkMjHZS0s9CUkCq5/4ZDA7MXNCum8wEPQ tAQ7HTecw1jJbyiKrPdPNR/aqBwJYg== =AprT -----END PGP SIGNATURE----- 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.
This commit is contained in:
commit
6762966707
@ -25,6 +25,9 @@ KernelVersion: 5.14
|
||||
Contact: linux-mtd@lists.infradead.org
|
||||
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
|
||||
Date: April 2021
|
||||
|
@ -2,64 +2,204 @@
|
||||
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
|
||||
controller operates agnostic of the specific device attached. However, some
|
||||
controllers (such as Freescale's QuadSPI controller) cannot easily handle
|
||||
arbitrary streams of bytes, but rather are designed specifically for SPI NOR.
|
||||
Most SPI NOR flashes comply with the JEDEC JESD216
|
||||
Serial Flash Discoverable Parameter (SFDP) standard. SFDP describes
|
||||
the functional and feature capabilities of serial flash devices in a
|
||||
standard set of internal read-only parameter tables.
|
||||
|
||||
In particular, Freescale's QuadSPI controller must know the NOR commands to
|
||||
find the right LUT sequence. Unfortunately, the SPI subsystem has no notion of
|
||||
opcodes, addresses, or data payloads; a SPI controller simply knows to send or
|
||||
receive bytes (Tx and Rx). Therefore, we must define a new layering scheme under
|
||||
which the controller driver is aware of the opcodes, addressing, and other
|
||||
details of the SPI NOR protocol.
|
||||
The SPI NOR driver queries the SFDP tables in order to determine the
|
||||
flash's parameters and settings. If the flash defines the SFDP tables
|
||||
it's likely that you won't need a flash entry at all, and instead
|
||||
rely on the generic flash driver which probes the flash solely based
|
||||
on its SFDP data. All one has to do is to specify the "jedec,spi-nor"
|
||||
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
|
||||
when the flash contains mangled SFDP data. If the later, one needs
|
||||
to implement the ``spi_nor_fixups`` hooks in order to amend the SFDP
|
||||
parameters with the correct values.
|
||||
|
||||
This framework just adds a new layer between the MTD and the SPI bus driver.
|
||||
With this new layer, the SPI NOR controller driver does not depend on the
|
||||
m25p80 code anymore.
|
||||
Minimum testing requirements
|
||||
-----------------------------
|
||||
|
||||
Before this framework, the layer is like::
|
||||
Do all the tests from below and paste them in the commit's comments
|
||||
section, after the ``---`` marker.
|
||||
|
||||
MTD
|
||||
------------------------
|
||||
m25p80
|
||||
------------------------
|
||||
SPI bus driver
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
1) Specify the controller that you used to test the flash and specify
|
||||
the frequency at which the flash was operated, e.g.::
|
||||
|
||||
After this framework, the layer is like::
|
||||
This flash is populated on the X board and was tested at Y
|
||||
frequency using the Z (put compatible) SPI controller.
|
||||
|
||||
MTD
|
||||
------------------------
|
||||
SPI NOR framework
|
||||
------------------------
|
||||
m25p80
|
||||
------------------------
|
||||
SPI bus driver
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
2) Dump the sysfs entries and print the md5/sha1/sha256 SFDP checksum::
|
||||
|
||||
With the SPI NOR controller driver (Freescale QuadSPI), it looks like::
|
||||
root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/partname
|
||||
sst26vf064b
|
||||
root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id
|
||||
bf2643
|
||||
root@1:~# cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer
|
||||
sst
|
||||
root@1:~# xxd -p /sys/bus/spi/devices/spi0.0/spi-nor/sfdp
|
||||
53464450060102ff00060110300000ff81000106000100ffbf0001180002
|
||||
0001fffffffffffffffffffffffffffffffffd20f1ffffffff0344eb086b
|
||||
083b80bbfeffffffffff00ffffff440b0c200dd80fd810d820914824806f
|
||||
1d81ed0f773830b030b0f7ffffff29c25cfff030c080ffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffff0004fff37f0000f57f0000f9ff
|
||||
7d00f57f0000f37f0000ffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
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
|
||||
|
||||
MTD
|
||||
------------------------
|
||||
SPI NOR framework
|
||||
------------------------
|
||||
fsl-quadSPI
|
||||
------------------------
|
||||
SPI NOR chip
|
||||
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.
|
||||
|
||||
Part III - How can drivers use the framework?
|
||||
---------------------------------------------
|
||||
3) Dump debugfs data::
|
||||
|
||||
The main API is spi_nor_scan(). Before you call the hook, a driver should
|
||||
initialize the necessary fields for spi_nor{}. Please see
|
||||
drivers/mtd/spi-nor/spi-nor.c for detail. Please also refer to spi-fsl-qspi.c
|
||||
when you want to write a new driver for a SPI NOR controller.
|
||||
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
|
||||
F: tools/testing/selftests/gpio/
|
||||
|
||||
GPIO REGMAP
|
||||
M: Michael Walle <michael@walle.cc>
|
||||
M: Michael Walle <mwalle@kernel.org>
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-regmap.c
|
||||
F: include/linux/gpio/regmap.h
|
||||
@ -19901,7 +19901,7 @@ W: http://www.winischhofer.at/linuxsisusbvga.shtml
|
||||
F: drivers/usb/misc/sisusbvga/
|
||||
|
||||
SL28 CPLD MFD DRIVER
|
||||
M: Michael Walle <michael@walle.cc>
|
||||
M: Michael Walle <mwalle@kernel.org>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/gpio/kontron,sl28cpld-gpio.yaml
|
||||
F: Documentation/devicetree/bindings/hwmon/kontron,sl28cpld-hwmon.yaml
|
||||
@ -19916,7 +19916,7 @@ F: drivers/pwm/pwm-sl28cpld.c
|
||||
F: drivers/watchdog/sl28cpld_wdt.c
|
||||
|
||||
SL28 VPD NVMEM LAYOUT DRIVER
|
||||
M: Michael Walle <michael@walle.cc>
|
||||
M: Michael Walle <mwalle@kernel.org>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/nvmem/layouts/kontron,sl28-vpd.yaml
|
||||
F: drivers/nvmem/layouts/sl28vpd.c
|
||||
@ -20426,7 +20426,7 @@ F: drivers/pinctrl/spear/
|
||||
SPI NOR SUBSYSTEM
|
||||
M: Tudor Ambarus <tudor.ambarus@linaro.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
|
||||
S: Maintained
|
||||
W: http://www.linux-mtd.infradead.org/
|
||||
|
@ -16,12 +16,12 @@
|
||||
* is to unlock the whole flash array on startup. Therefore, we have to support
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -37,7 +37,7 @@ static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
|
||||
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;
|
||||
}
|
||||
@ -69,7 +69,7 @@ static const struct spi_nor_fixups at25fs_nor_fixups = {
|
||||
* Return: 0 on success, -error otherwise.
|
||||
*/
|
||||
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;
|
||||
u8 sr;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs,
|
||||
uint64_t len)
|
||||
static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len)
|
||||
{
|
||||
return atmel_nor_set_global_protection(nor, ofs, len, true);
|
||||
}
|
||||
|
||||
static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs,
|
||||
uint64_t len)
|
||||
static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len)
|
||||
{
|
||||
return atmel_nor_set_global_protection(nor, ofs, len, false);
|
||||
}
|
||||
|
||||
static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs,
|
||||
uint64_t len)
|
||||
u64 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -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'.
|
||||
* @addr: address of the die.
|
||||
* @die_size: size of the die.
|
||||
*
|
||||
* 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;
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
ret = spi_mem_exec_op(nor->spimem, &op);
|
||||
} else {
|
||||
if (multi_die)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = spi_nor_controller_ops_write_reg(nor,
|
||||
SPINOR_OP_CHIP_ERASE,
|
||||
NULL, 0);
|
||||
@ -1792,6 +1800,51 @@ destroy_erase_cmd_list:
|
||||
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
|
||||
* one or more erase sectors. Return an error if there is a problem erasing.
|
||||
@ -1799,8 +1852,10 @@ destroy_erase_cmd_list:
|
||||
static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
u32 addr, len;
|
||||
uint32_t rem;
|
||||
u8 n_dice = nor->params->n_dice;
|
||||
bool multi_die_erase = false;
|
||||
u32 addr, len, rem;
|
||||
size_t die_size;
|
||||
int ret;
|
||||
|
||||
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)
|
||||
addr = instr->addr;
|
||||
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);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* whole-chip erase? */
|
||||
if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
|
||||
unsigned long timeout;
|
||||
|
||||
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);
|
||||
/* chip (die) erase? */
|
||||
if ((len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) ||
|
||||
multi_die_erase) {
|
||||
ret = spi_nor_erase_dice(nor, addr, len, die_size);
|
||||
if (ret)
|
||||
goto erase_err;
|
||||
|
||||
@ -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)) {
|
||||
page_offset = addr & (page_size - 1);
|
||||
} else {
|
||||
uint64_t aux = addr;
|
||||
u64 aux = addr;
|
||||
|
||||
page_offset = do_div(aux, page_size);
|
||||
}
|
||||
@ -2850,9 +2888,6 @@ static void spi_nor_init_flags(struct spi_nor *nor)
|
||||
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 &&
|
||||
!nor->controller_ops)
|
||||
nor->flags |= SNOR_F_RWW;
|
||||
@ -2897,17 +2932,22 @@ static int spi_nor_late_init_params(struct spi_nor *nor)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Needed by some flashes late_init hooks. */
|
||||
spi_nor_init_flags(nor);
|
||||
|
||||
if (nor->info->fixups && nor->info->fixups->late_init) {
|
||||
ret = nor->info->fixups->late_init(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!nor->params->die_erase_opcode)
|
||||
nor->params->die_erase_opcode = SPINOR_OP_CHIP_ERASE;
|
||||
|
||||
/* Default method kept for backward compatibility. */
|
||||
if (!params->set_4byte_addr_mode)
|
||||
params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
|
||||
|
||||
spi_nor_init_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)
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
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);
|
||||
if (ret && ret != -ENOTSUPP)
|
||||
return ret;
|
||||
@ -3193,20 +3245,8 @@ static int spi_nor_init(struct spi_nor *nor)
|
||||
|
||||
if (nor->addr_nbytes == 4 &&
|
||||
nor->read_proto != SNOR_PROTO_8_8_8_DTR &&
|
||||
!(nor->flags & SNOR_F_4B_OPCODES)) {
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
!(nor->flags & SNOR_F_4B_OPCODES))
|
||||
return spi_nor_set_4byte_addr_mode(nor, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3452,9 +3492,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
{
|
||||
const struct flash_info *info;
|
||||
struct device *dev = nor->dev;
|
||||
struct mtd_info *mtd = &nor->mtd;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
ret = spi_nor_check(nor);
|
||||
if (ret)
|
||||
@ -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. */
|
||||
spi_nor_set_mtd_info(nor);
|
||||
|
||||
dev_info(dev, "%s (%lld Kbytes)\n", info->name,
|
||||
(long long)mtd->size >> 10);
|
||||
dev_dbg(dev, "Manufacturer and device ID: %*phN\n",
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_nor_scan);
|
||||
|
@ -85,9 +85,9 @@
|
||||
SPI_MEM_OP_NO_DUMMY, \
|
||||
SPI_MEM_OP_NO_DATA)
|
||||
|
||||
#define SPI_NOR_CHIP_ERASE_OP \
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 0), \
|
||||
SPI_MEM_OP_NO_ADDR, \
|
||||
#define SPI_NOR_DIE_ERASE_OP(opcode, addr_nbytes, addr, dice) \
|
||||
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
|
||||
SPI_MEM_OP_ADDR(dice ? addr_nbytes : 0, addr, 0), \
|
||||
SPI_MEM_OP_NO_DUMMY, \
|
||||
SPI_MEM_OP_NO_DATA)
|
||||
|
||||
@ -293,9 +293,9 @@ struct spi_nor_erase_map {
|
||||
* @is_locked: check if a region of the SPI NOR is completely locked
|
||||
*/
|
||||
struct spi_nor_locking_ops {
|
||||
int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||
int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||
int (*is_locked)(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, u64 len);
|
||||
int (*is_locked)(struct spi_nor *nor, loff_t ofs, u64 len);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -362,6 +362,7 @@ struct spi_nor_otp {
|
||||
* command in octal DTR mode.
|
||||
* @n_banks: number of banks.
|
||||
* @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.
|
||||
* @hwcaps: describes the read and page program hardware
|
||||
* capabilities.
|
||||
@ -399,6 +400,7 @@ struct spi_nor_flash_parameter {
|
||||
u8 rdsr_addr_nbytes;
|
||||
u8 n_banks;
|
||||
u8 n_dice;
|
||||
u8 die_erase_opcode;
|
||||
u32 *vreg_offset;
|
||||
|
||||
struct spi_nor_hwcaps hwcaps;
|
||||
@ -463,7 +465,7 @@ struct spi_nor_id {
|
||||
* struct flash_info - SPI NOR flash_info entry.
|
||||
* @id: pointer to struct spi_nor_id or NULL, which means "no ID" (mostly
|
||||
* 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.
|
||||
* @sector_size: (optional) the size listed here is what works with
|
||||
* SPINOR_OP_SE, which isn't necessarily called a "sector" by
|
||||
@ -487,7 +489,6 @@ struct spi_nor_id {
|
||||
* Usually these will power-up in a write-protected
|
||||
* state.
|
||||
* 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_QUAD_PP: flash supports Quad Input Page Program.
|
||||
* SPI_NOR_RWW: flash supports reads while write.
|
||||
@ -537,10 +538,9 @@ struct flash_info {
|
||||
#define SPI_NOR_BP3_SR_BIT6 BIT(4)
|
||||
#define SPI_NOR_SWP_IS_VOLATILE BIT(5)
|
||||
#define SPI_NOR_NO_ERASE BIT(6)
|
||||
#define NO_CHIP_ERASE BIT(7)
|
||||
#define SPI_NOR_NO_FR BIT(8)
|
||||
#define SPI_NOR_QUAD_PP BIT(9)
|
||||
#define SPI_NOR_RWW BIT(10)
|
||||
#define SPI_NOR_NO_FR BIT(7)
|
||||
#define SPI_NOR_QUAD_PP BIT(8)
|
||||
#define SPI_NOR_RWW BIT(9)
|
||||
|
||||
u8 no_sfdp_flags;
|
||||
#define SPI_NOR_SKIP_SFDP BIT(0)
|
||||
|
@ -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)) {
|
||||
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");
|
||||
|
@ -11,6 +11,7 @@
|
||||
/* flash_info mfr_flag. Used to read proprietary FSR register. */
|
||||
#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_CLFSR 0x50 /* Clear flag status register */
|
||||
#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */
|
||||
@ -192,6 +193,50 @@ static struct spi_nor_fixups mt25qu512a_fixups = {
|
||||
.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[] = {
|
||||
{
|
||||
.name = "m25p05-nonjedec",
|
||||
@ -366,16 +411,17 @@ static const struct flash_info st_nor_parts[] = {
|
||||
.name = "n25q00",
|
||||
.size = SZ_128M,
|
||||
.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,
|
||||
.mfr_flags = USE_FSR,
|
||||
.fixups = &n25q00_fixups,
|
||||
}, {
|
||||
.id = SNOR_ID(0x20, 0xba, 0x22),
|
||||
.name = "mt25ql02g",
|
||||
.size = SZ_256M,
|
||||
.flags = NO_CHIP_ERASE,
|
||||
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
|
||||
.mfr_flags = USE_FSR,
|
||||
.fixups = &mt25q02_fixups,
|
||||
}, {
|
||||
.id = SNOR_ID(0x20, 0xbb, 0x15),
|
||||
.name = "n25q016a",
|
||||
@ -429,20 +475,25 @@ static const struct flash_info st_nor_parts[] = {
|
||||
SPI_NOR_BP3_SR_BIT6,
|
||||
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
|
||||
.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),
|
||||
.name = "n25q00a",
|
||||
.size = SZ_128M,
|
||||
.flags = NO_CHIP_ERASE,
|
||||
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
|
||||
.mfr_flags = USE_FSR,
|
||||
.fixups = &n25q00_fixups,
|
||||
}, {
|
||||
.id = SNOR_ID(0x20, 0xbb, 0x22),
|
||||
.name = "mt25qu02g",
|
||||
.size = SZ_256M,
|
||||
.flags = NO_CHIP_ERASE,
|
||||
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
|
||||
.mfr_flags = USE_FSR,
|
||||
.fixups = &mt25q02_fixups,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -446,6 +446,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
|
||||
u32 dword;
|
||||
u16 half;
|
||||
u8 erase_mask;
|
||||
u8 wait_states, mode_clocks, opcode;
|
||||
|
||||
/* JESD216 Basic Flash Parameter Table length is at least 9 DWORDs. */
|
||||
if (bfpt_header->length < BFPT_DWORD_MAX_JESD216)
|
||||
@ -631,6 +632,32 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
|
||||
if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B)
|
||||
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(¶ms->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(¶ms->reads[SNOR_CMD_READ_1_8_8],
|
||||
mode_clocks, wait_states, opcode,
|
||||
SNOR_PROTO_1_8_8);
|
||||
}
|
||||
|
||||
/* 8D-8D-8D command extension. */
|
||||
switch (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) {
|
||||
case BFPT_DWORD18_CMD_EXT_REP:
|
||||
@ -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_2_2_DTR, BIT(14) },
|
||||
{ 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[] = {
|
||||
{ SNOR_HWCAPS_PP, BIT(6) },
|
||||
|
@ -118,6 +118,13 @@ struct sfdp_bfpt {
|
||||
(BFPT_DWORD16_EN4B_EN4B | BFPT_DWORD16_EX4B_EX4B)
|
||||
#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_REP (0x0UL << 29) /* Repeat */
|
||||
#define BFPT_DWORD18_CMD_EXT_INV (0x1UL << 29) /* Invert */
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#define SPINOR_OP_CLSR 0x30 /* Clear status register 1 */
|
||||
#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_WR_ANY_REG 0x71 /* Write any register */
|
||||
#define SPINOR_REG_CYPRESS_VREG 0x00800000
|
||||
@ -644,6 +645,7 @@ 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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -933,7 +935,6 @@ static const struct flash_info spansion_nor_parts[] = {
|
||||
.id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90),
|
||||
.name = "s25hl02gt",
|
||||
.mfr_flags = USE_CLPEF,
|
||||
.flags = NO_CHIP_ERASE,
|
||||
.fixups = &s25hx_t_fixups
|
||||
}, {
|
||||
.id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90),
|
||||
@ -954,7 +955,6 @@ static const struct flash_info spansion_nor_parts[] = {
|
||||
.id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90),
|
||||
.name = "s25hs02gt",
|
||||
.mfr_flags = USE_CLPEF,
|
||||
.flags = NO_CHIP_ERASE,
|
||||
.fixups = &s25hx_t_fixups
|
||||
}, {
|
||||
.id = SNOR_ID(0x34, 0x5a, 0x1a),
|
||||
|
@ -13,12 +13,12 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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,
|
||||
uint64_t *len)
|
||||
u64 *len)
|
||||
{
|
||||
struct mtd_info *mtd = &nor->mtd;
|
||||
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,
|
||||
* (if @locked is false); false otherwise.
|
||||
*/
|
||||
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;
|
||||
uint64_t lock_len;
|
||||
u64 lock_len;
|
||||
|
||||
if (!len)
|
||||
return true;
|
||||
@ -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);
|
||||
}
|
||||
|
||||
static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len,
|
||||
u8 sr)
|
||||
static bool spi_nor_is_locked_sr(struct spi_nor *nor, loff_t ofs, u64 len, u8 sr)
|
||||
{
|
||||
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,
|
||||
uint64_t len, u8 sr)
|
||||
static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len,
|
||||
u8 sr)
|
||||
{
|
||||
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,
|
||||
*
|
||||
* 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;
|
||||
u64 min_prot_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.
|
||||
*/
|
||||
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;
|
||||
u64 min_prot_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
|
||||
* 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;
|
||||
|
||||
@ -353,7 +352,7 @@ void spi_nor_init_default_locking_ops(struct spi_nor *nor)
|
||||
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);
|
||||
int ret;
|
||||
@ -368,7 +367,7 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
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);
|
||||
int ret;
|
||||
@ -383,7 +382,7 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
||||
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);
|
||||
int ret;
|
||||
|
@ -78,6 +78,8 @@ static umode_t spi_nor_sysfs_is_visible(struct kobject *kobj,
|
||||
|
||||
if (attr == &dev_attr_manufacturer.attr && !nor->manufacturer)
|
||||
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)
|
||||
return 0;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user