Commit 35f11a37 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mtd/for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux

Pull mtd updates from Miquel Raynal:
 "MTD:

   - Apart from preventing the mtdblk to run on top of ftl or ubiblk
     (which may cause security issues and has no meaning anyway), there
     are a few misc fixes.

  Raw NAND:

   - Two meaningful changes this time. The conversion of the brcmnand
     driver to the ->exec_op() API, this series brought additional
     changes to the core in order to help controller drivers to handle
     themselves the WP pin during destructive operations when relevant.

   - There is also a series bringing important fixes to the sequential
     read feature.

   - As always, there is as well a whole bunch of miscellaneous W=1
     fixes, together with a few runtime fixes (double free, timeout
     value, OOB layout, missing register initialization) and the usual
     load of remove callbacks turned into void (which led to switch the
     txx9ndfmc driver to use module_platform_driver()).

  SPI NOR:

   - 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"

* tag 'mtd/for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (39 commits)
  mtd: rawnand: Clarify conditions to enable continuous reads
  mtd: rawnand: Prevent sequential reads with on-die ECC engines
  mtd: rawnand: Fix core interference with sequential reads
  mtd: rawnand: Prevent crossing LUN boundaries during sequential reads
  mtd: Fix gluebi NULL pointer dereference caused by ftl notifier
  dt-bindings: mtd: partitions: u-boot: Fix typo
  mtd: rawnand: s3c2410: fix Excess struct member description kernel-doc warnings
  MAINTAINERS: change my mail to the kernel.org one
  mtd: spi-nor: sfdp: get the 1-1-8 and 1-8-8 protocol from SFDP
  mtd: spi-nor: drop superfluous debug prints
  mtd: spi-nor: sysfs: hide the flash name if not set
  mtd: spi-nor: mark the flash name as obsolete
  mtd: spi-nor: print flash ID instead of name
  mtd: maps: vmu-flash: Fix the (mtd core) switch to ref counters
  mtd: ssfdc: Remove an unused variable
  mtd: rawnand: diskonchip: fix a potential double free in doc_probe
  mtd: rawnand: rockchip: Add missing title to a kernel doc comment
  mtd: rawnand: rockchip: Rename a structure
  mtd: rawnand: pl353: Fix kernel doc
  mtd: spi-nor: micron-st: Add support for mt25qu01g
  ...
parents 30194002 98d4fda8
...@@ -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
......
...@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# ...@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: U-Boot bootloader partition title: U-Boot bootloader partition
description: | description: |
U-Boot is a bootlodaer commonly used in embedded devices. It's almost always U-Boot is a bootloader commonly used in embedded devices. It's almost always
located on some kind of flash device. located on some kind of flash device.
Device configuration is stored as a set of environment variables that are Device configuration is stored as a set of environment variables that are
......
...@@ -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
...@@ -9026,7 +9026,7 @@ F: drivers/gpio/gpio-mockup.c ...@@ -9026,7 +9026,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
...@@ -19868,7 +19868,7 @@ W: http://www.winischhofer.at/linuxsisusbvga.shtml ...@@ -19868,7 +19868,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
...@@ -19883,7 +19883,7 @@ F: drivers/pwm/pwm-sl28cpld.c ...@@ -19883,7 +19883,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
...@@ -20393,7 +20393,7 @@ F: drivers/pinctrl/spear/ ...@@ -20393,7 +20393,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/
......
...@@ -719,7 +719,7 @@ static int vmu_can_unload(struct maple_device *mdev) ...@@ -719,7 +719,7 @@ static int vmu_can_unload(struct maple_device *mdev)
card = maple_get_drvdata(mdev); card = maple_get_drvdata(mdev);
for (x = 0; x < card->partitions; x++) { for (x = 0; x < card->partitions; x++) {
mtd = &((card->mtd)[x]); mtd = &((card->mtd)[x]);
if (mtd->usecount > 0) if (kref_read(&mtd->refcnt))
return 0; return 0;
} }
return 1; return 1;
......
...@@ -463,7 +463,7 @@ static void blktrans_notify_add(struct mtd_info *mtd) ...@@ -463,7 +463,7 @@ static void blktrans_notify_add(struct mtd_info *mtd)
{ {
struct mtd_blktrans_ops *tr; struct mtd_blktrans_ops *tr;
if (mtd->type == MTD_ABSENT) if (mtd->type == MTD_ABSENT || mtd->type == MTD_UBIVOLUME)
return; return;
list_for_each_entry(tr, &blktrans_majors, list) list_for_each_entry(tr, &blktrans_majors, list)
...@@ -503,7 +503,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) ...@@ -503,7 +503,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
mutex_lock(&mtd_table_mutex); mutex_lock(&mtd_table_mutex);
list_add(&tr->list, &blktrans_majors); list_add(&tr->list, &blktrans_majors);
mtd_for_each_device(mtd) mtd_for_each_device(mtd)
if (mtd->type != MTD_ABSENT) if (mtd->type != MTD_ABSENT && mtd->type != MTD_UBIVOLUME)
tr->add_mtd(tr, mtd); tr->add_mtd(tr, mtd);
mutex_unlock(&mtd_table_mutex); mutex_unlock(&mtd_table_mutex);
return 0; return 0;
......
...@@ -85,7 +85,7 @@ MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match); ...@@ -85,7 +85,7 @@ MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
static struct platform_driver bcm63138_nand_driver = { static struct platform_driver bcm63138_nand_driver = {
.probe = bcm63138_nand_probe, .probe = bcm63138_nand_probe,
.remove = brcmnand_remove, .remove_new = brcmnand_remove,
.driver = { .driver = {
.name = "bcm63138_nand", .name = "bcm63138_nand",
.pm = &brcmnand_pm_ops, .pm = &brcmnand_pm_ops,
......
...@@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, bcm6368_nand_of_match); ...@@ -117,7 +117,7 @@ MODULE_DEVICE_TABLE(of, bcm6368_nand_of_match);
static struct platform_driver bcm6368_nand_driver = { static struct platform_driver bcm6368_nand_driver = {
.probe = bcm6368_nand_probe, .probe = bcm6368_nand_probe,
.remove = brcmnand_remove, .remove_new = brcmnand_remove,
.driver = { .driver = {
.name = "bcm6368_nand", .name = "bcm6368_nand",
.pm = &brcmnand_pm_ops, .pm = &brcmnand_pm_ops,
......
...@@ -119,7 +119,7 @@ static int brcmnand_bcma_nand_probe(struct platform_device *pdev) ...@@ -119,7 +119,7 @@ static int brcmnand_bcma_nand_probe(struct platform_device *pdev)
static struct platform_driver brcmnand_bcma_nand_driver = { static struct platform_driver brcmnand_bcma_nand_driver = {
.probe = brcmnand_bcma_nand_probe, .probe = brcmnand_bcma_nand_probe,
.remove = brcmnand_remove, .remove_new = brcmnand_remove,
.driver = { .driver = {
.name = "bcma_brcmnand", .name = "bcma_brcmnand",
.pm = &brcmnand_pm_ops, .pm = &brcmnand_pm_ops,
......
This diff is collapsed.
...@@ -88,7 +88,7 @@ static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val, ...@@ -88,7 +88,7 @@ static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val,
} }
int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc); int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
int brcmnand_remove(struct platform_device *pdev); void brcmnand_remove(struct platform_device *pdev);
extern const struct dev_pm_ops brcmnand_pm_ops; extern const struct dev_pm_ops brcmnand_pm_ops;
......
...@@ -23,7 +23,7 @@ static int brcmstb_nand_probe(struct platform_device *pdev) ...@@ -23,7 +23,7 @@ static int brcmstb_nand_probe(struct platform_device *pdev)
static struct platform_driver brcmstb_nand_driver = { static struct platform_driver brcmstb_nand_driver = {
.probe = brcmstb_nand_probe, .probe = brcmstb_nand_probe,
.remove = brcmnand_remove, .remove_new = brcmnand_remove,
.driver = { .driver = {
.name = "brcmstb_nand", .name = "brcmstb_nand",
.pm = &brcmnand_pm_ops, .pm = &brcmnand_pm_ops,
......
...@@ -134,7 +134,7 @@ MODULE_DEVICE_TABLE(of, iproc_nand_of_match); ...@@ -134,7 +134,7 @@ MODULE_DEVICE_TABLE(of, iproc_nand_of_match);
static struct platform_driver iproc_nand_driver = { static struct platform_driver iproc_nand_driver = {
.probe = iproc_nand_probe, .probe = iproc_nand_probe,
.remove = brcmnand_remove, .remove_new = brcmnand_remove,
.driver = { .driver = {
.name = "iproc_nand", .name = "iproc_nand",
.pm = &brcmnand_pm_ops, .pm = &brcmnand_pm_ops,
......
...@@ -1491,10 +1491,12 @@ static int __init doc_probe(unsigned long physadr) ...@@ -1491,10 +1491,12 @@ static int __init doc_probe(unsigned long physadr)
else else
numchips = doc2001_init(mtd); numchips = doc2001_init(mtd);
if ((ret = nand_scan(nand, numchips)) || (ret = doc->late_init(mtd))) { ret = nand_scan(nand, numchips);
/* DBB note: i believe nand_cleanup is necessary here, as if (ret)
buffers may have been allocated in nand_base. Check with goto fail;
Thomas. FIX ME! */
ret = doc->late_init(mtd);
if (ret) {
nand_cleanup(nand); nand_cleanup(nand);
goto fail; goto fail;
} }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#define ERR_BYTE 0xFF /* Value returned for read #define ERR_BYTE 0xFF /* Value returned for read
bytes when read failed */ bytes when read failed */
#define IFC_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait #define IFC_TIMEOUT_MSECS 1000 /* Maximum timeout to wait
for IFC NAND Machine */ for IFC NAND Machine */
struct fsl_ifc_ctrl; struct fsl_ifc_ctrl;
......
...@@ -90,6 +90,8 @@ ...@@ -90,6 +90,8 @@
/* eMMC clock register, misc control */ /* eMMC clock register, misc control */
#define CLK_SELECT_NAND BIT(31) #define CLK_SELECT_NAND BIT(31)
#define CLK_ALWAYS_ON_NAND BIT(24)
#define CLK_SELECT_FIX_PLL2 BIT(6)
#define NFC_CLK_CYCLE 6 #define NFC_CLK_CYCLE 6
...@@ -509,7 +511,7 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf) ...@@ -509,7 +511,7 @@ static void meson_nfc_set_user_byte(struct nand_chip *nand, u8 *oob_buf)
__le64 *info; __le64 *info;
int i, count; int i, count;
for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) {
info = &meson_chip->info_buf[i]; info = &meson_chip->info_buf[i];
*info |= oob_buf[count]; *info |= oob_buf[count];
*info |= oob_buf[count + 1] << 8; *info |= oob_buf[count + 1] << 8;
...@@ -522,7 +524,7 @@ static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf) ...@@ -522,7 +524,7 @@ static void meson_nfc_get_user_byte(struct nand_chip *nand, u8 *oob_buf)
__le64 *info; __le64 *info;
int i, count; int i, count;
for (i = 0, count = 0; i < nand->ecc.steps; i++, count += 2) { for (i = 0, count = 0; i < nand->ecc.steps; i++, count += (2 + nand->ecc.bytes)) {
info = &meson_chip->info_buf[i]; info = &meson_chip->info_buf[i];
oob_buf[count] = *info; oob_buf[count] = *info;
oob_buf[count + 1] = *info >> 8; oob_buf[count + 1] = *info >> 8;
...@@ -1154,7 +1156,7 @@ static int meson_nfc_clk_init(struct meson_nfc *nfc) ...@@ -1154,7 +1156,7 @@ static int meson_nfc_clk_init(struct meson_nfc *nfc)
return PTR_ERR(nfc->nand_clk); return PTR_ERR(nfc->nand_clk);
/* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
writel(CLK_SELECT_NAND | readl(nfc->reg_clk), writel(CLK_ALWAYS_ON_NAND | CLK_SELECT_NAND | CLK_SELECT_FIX_PLL2,
nfc->reg_clk); nfc->reg_clk);
ret = clk_prepare_enable(nfc->core_clk); ret = clk_prepare_enable(nfc->core_clk);
......
...@@ -366,6 +366,10 @@ static int nand_check_wp(struct nand_chip *chip) ...@@ -366,6 +366,10 @@ static int nand_check_wp(struct nand_chip *chip)
if (chip->options & NAND_BROKEN_XD) if (chip->options & NAND_BROKEN_XD)
return 0; return 0;
/* controller responsible for NAND write protect */
if (chip->controller->controller_wp)
return 0;
/* Check the WP bit */ /* Check the WP bit */
ret = nand_status_op(chip, &status); ret = nand_status_op(chip, &status);
if (ret) if (ret)
...@@ -1207,6 +1211,23 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page, ...@@ -1207,6 +1211,23 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
return nand_exec_op(chip, &op); return nand_exec_op(chip, &op);
} }
static void rawnand_cap_cont_reads(struct nand_chip *chip)
{
struct nand_memory_organization *memorg;
unsigned int pages_per_lun, first_lun, last_lun;
memorg = nanddev_get_memorg(&chip->base);
pages_per_lun = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
first_lun = chip->cont_read.first_page / pages_per_lun;
last_lun = chip->cont_read.last_page / pages_per_lun;
/* Prevent sequential cache reads across LUN boundaries */
if (first_lun != last_lun)
chip->cont_read.pause_page = first_lun * pages_per_lun + pages_per_lun - 1;
else
chip->cont_read.pause_page = chip->cont_read.last_page;
}
static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page, static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
unsigned int offset_in_page, void *buf, unsigned int offset_in_page, void *buf,
unsigned int len, bool check_only) unsigned int len, bool check_only)
...@@ -1225,7 +1246,7 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p ...@@ -1225,7 +1246,7 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p
NAND_OP_DATA_IN(len, buf, 0), NAND_OP_DATA_IN(len, buf, 0),
}; };
struct nand_op_instr cont_instrs[] = { struct nand_op_instr cont_instrs[] = {
NAND_OP_CMD(page == chip->cont_read.last_page ? NAND_OP_CMD(page == chip->cont_read.pause_page ?
NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ, NAND_CMD_READCACHEEND : NAND_CMD_READCACHESEQ,
NAND_COMMON_TIMING_NS(conf, tWB_max)), NAND_COMMON_TIMING_NS(conf, tWB_max)),
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max), NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tR_max),
...@@ -1262,16 +1283,29 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p ...@@ -1262,16 +1283,29 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p
} }
if (page == chip->cont_read.first_page) if (page == chip->cont_read.first_page)
return nand_exec_op(chip, &start_op); ret = nand_exec_op(chip, &start_op);
else else
return nand_exec_op(chip, &cont_op); ret = nand_exec_op(chip, &cont_op);
if (ret)
return ret;
if (!chip->cont_read.ongoing)
return 0;
if (page == chip->cont_read.pause_page &&
page != chip->cont_read.last_page) {
chip->cont_read.first_page = chip->cont_read.pause_page + 1;
rawnand_cap_cont_reads(chip);
} else if (page == chip->cont_read.last_page) {
chip->cont_read.ongoing = false;
}
return 0;
} }
static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page) static bool rawnand_cont_read_ongoing(struct nand_chip *chip, unsigned int page)
{ {
return chip->cont_read.ongoing && return chip->cont_read.ongoing && page >= chip->cont_read.first_page;
page >= chip->cont_read.first_page &&
page <= chip->cont_read.last_page;
} }
/** /**
...@@ -1493,7 +1527,8 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page, ...@@ -1493,7 +1527,8 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
NAND_COMMON_TIMING_NS(conf, tWB_max)), NAND_COMMON_TIMING_NS(conf, tWB_max)),
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), 0), NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tPROG_max), 0),
}; };
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
instrs);
int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page); int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
if (naddrs < 0) if (naddrs < 0)
...@@ -1916,7 +1951,8 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) ...@@ -1916,7 +1951,8 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tBERS_max), NAND_OP_WAIT_RDY(NAND_COMMON_TIMING_MS(conf, tBERS_max),
0), 0),
}; };
struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs); struct nand_operation op = NAND_DESTRUCTIVE_OPERATION(chip->cur_cs,
instrs);
if (chip->options & NAND_ROW_ADDR_3) if (chip->options & NAND_ROW_ADDR_3)
instrs[1].ctx.addr.naddrs++; instrs[1].ctx.addr.naddrs++;
...@@ -3430,21 +3466,42 @@ static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page, ...@@ -3430,21 +3466,42 @@ static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page,
u32 readlen, int col) u32 readlen, int col)
{ {
struct mtd_info *mtd = nand_to_mtd(chip); struct mtd_info *mtd = nand_to_mtd(chip);
unsigned int end_page, end_col;
chip->cont_read.ongoing = false;
if (!chip->controller->supported_op.cont_read) if (!chip->controller->supported_op.cont_read)
return; return;
if ((col && col + readlen < (3 * mtd->writesize)) || end_page = DIV_ROUND_UP(col + readlen, mtd->writesize);
(!col && readlen < (2 * mtd->writesize))) { end_col = (col + readlen) % mtd->writesize;
chip->cont_read.ongoing = false;
if (col)
page++;
if (end_col && end_page)
end_page--;
if (page + 1 > end_page)
return; return;
}
chip->cont_read.ongoing = true;
chip->cont_read.first_page = page; chip->cont_read.first_page = page;
if (col) chip->cont_read.last_page = end_page;
chip->cont_read.ongoing = true;
rawnand_cap_cont_reads(chip);
}
static void rawnand_cont_read_skip_first_page(struct nand_chip *chip, unsigned int page)
{
if (!chip->cont_read.ongoing || page != chip->cont_read.first_page)
return;
chip->cont_read.first_page++;
if (chip->cont_read.first_page == chip->cont_read.pause_page)
chip->cont_read.first_page++; chip->cont_read.first_page++;
chip->cont_read.last_page = page + ((readlen >> chip->page_shift) & chip->pagemask); if (chip->cont_read.first_page >= chip->cont_read.last_page)
chip->cont_read.ongoing = false;
} }
/** /**
...@@ -3621,6 +3678,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from, ...@@ -3621,6 +3678,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
buf += bytes; buf += bytes;
max_bitflips = max_t(unsigned int, max_bitflips, max_bitflips = max_t(unsigned int, max_bitflips,
chip->pagecache.bitflips); chip->pagecache.bitflips);
rawnand_cont_read_skip_first_page(chip, page);
} }
readlen -= bytes; readlen -= bytes;
...@@ -5125,6 +5184,14 @@ static void rawnand_late_check_supported_ops(struct nand_chip *chip) ...@@ -5125,6 +5184,14 @@ static void rawnand_late_check_supported_ops(struct nand_chip *chip)
/* The supported_op fields should not be set by individual drivers */ /* The supported_op fields should not be set by individual drivers */
WARN_ON_ONCE(chip->controller->supported_op.cont_read); WARN_ON_ONCE(chip->controller->supported_op.cont_read);
/*
* Too many devices do not support sequential cached reads with on-die
* ECC correction enabled, so in this case refuse to perform the
* automation.
*/
if (chip->ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_DIE)
return;
if (!nand_has_exec_op(chip)) if (!nand_has_exec_op(chip))
return; return;
......
...@@ -128,7 +128,7 @@ struct pl35x_nand { ...@@ -128,7 +128,7 @@ struct pl35x_nand {
* @conf_regs: SMC configuration registers for command phase * @conf_regs: SMC configuration registers for command phase
* @io_regs: NAND data registers for data phase * @io_regs: NAND data registers for data phase
* @controller: Core NAND controller structure * @controller: Core NAND controller structure
* @chip: NAND chip information structure * @chips: List of connected NAND chips
* @selected_chip: NAND chip currently selected by the controller * @selected_chip: NAND chip currently selected by the controller
* @assigned_cs: List of assigned CS * @assigned_cs: List of assigned CS
* @ecc_buf: Temporary buffer to extract ECC bytes * @ecc_buf: Temporary buffer to extract ECC bytes
......
...@@ -98,7 +98,7 @@ enum nfc_type { ...@@ -98,7 +98,7 @@ enum nfc_type {
* @high: ECC count high bit index at register. * @high: ECC count high bit index at register.
* @high_mask: mask bit * @high_mask: mask bit
*/ */
struct ecc_cnt_status { struct rk_ecc_cnt_status {
u8 err_flag_bit; u8 err_flag_bit;
u8 low; u8 low;
u8 low_mask; u8 low_mask;
...@@ -108,6 +108,7 @@ struct ecc_cnt_status { ...@@ -108,6 +108,7 @@ struct ecc_cnt_status {
}; };
/** /**
* struct nfc_cfg: Rockchip NAND controller configuration
* @type: NFC version * @type: NFC version
* @ecc_strengths: ECC strengths * @ecc_strengths: ECC strengths
* @ecc_cfgs: ECC config values * @ecc_cfgs: ECC config values
...@@ -144,8 +145,8 @@ struct nfc_cfg { ...@@ -144,8 +145,8 @@ struct nfc_cfg {
u32 int_st_off; u32 int_st_off;
u32 oob0_off; u32 oob0_off;
u32 oob1_off; u32 oob1_off;
struct ecc_cnt_status ecc0; struct rk_ecc_cnt_status ecc0;
struct ecc_cnt_status ecc1; struct rk_ecc_cnt_status ecc1;
}; };
struct rk_nfc_nand_chip { struct rk_nfc_nand_chip {
......
...@@ -105,7 +105,6 @@ struct s3c2410_nand_info; ...@@ -105,7 +105,6 @@ struct s3c2410_nand_info;
/** /**
* struct s3c2410_nand_mtd - driver MTD structure * struct s3c2410_nand_mtd - driver MTD structure
* @mtd: The MTD instance to pass to the MTD layer.
* @chip: The NAND chip information. * @chip: The NAND chip information.
* @set: The platform information supplied for this set of NAND chips. * @set: The platform information supplied for this set of NAND chips.
* @info: Link back to the hardware information. * @info: Link back to the hardware information.
...@@ -145,7 +144,6 @@ enum s3c_nand_clk_state { ...@@ -145,7 +144,6 @@ enum s3c_nand_clk_state {
* @clk_rate: The clock rate from @clk. * @clk_rate: The clock rate from @clk.
* @clk_state: The current clock state. * @clk_state: The current clock state.
* @cpu_type: The exact type of this controller. * @cpu_type: The exact type of this controller.
* @freq_transition: CPUFreq notifier block
*/ */
struct s3c2410_nand_info { struct s3c2410_nand_info {
/* mtd info */ /* mtd info */
......
...@@ -276,7 +276,7 @@ static const struct nand_controller_ops txx9ndfmc_controller_ops = { ...@@ -276,7 +276,7 @@ static const struct nand_controller_ops txx9ndfmc_controller_ops = {
.attach_chip = txx9ndfmc_attach_chip, .attach_chip = txx9ndfmc_attach_chip,
}; };
static int __init txx9ndfmc_probe(struct platform_device *dev) static int txx9ndfmc_probe(struct platform_device *dev)
{ {
struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev);
int hold, spw; int hold, spw;
...@@ -369,13 +369,11 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) ...@@ -369,13 +369,11 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
return 0; return 0;
} }
static int __exit txx9ndfmc_remove(struct platform_device *dev) static void txx9ndfmc_remove(struct platform_device *dev)
{ {
struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
int ret, i; int ret, i;
if (!drvdata)
return 0;
for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
struct mtd_info *mtd = drvdata->mtds[i]; struct mtd_info *mtd = drvdata->mtds[i];
struct nand_chip *chip; struct nand_chip *chip;
...@@ -392,7 +390,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) ...@@ -392,7 +390,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev)
kfree(txx9_priv->mtdname); kfree(txx9_priv->mtdname);
kfree(txx9_priv); kfree(txx9_priv);
} }
return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -407,14 +404,14 @@ static int txx9ndfmc_resume(struct platform_device *dev) ...@@ -407,14 +404,14 @@ static int txx9ndfmc_resume(struct platform_device *dev)
#endif #endif
static struct platform_driver txx9ndfmc_driver = { static struct platform_driver txx9ndfmc_driver = {
.remove = __exit_p(txx9ndfmc_remove), .probe = txx9ndfmc_probe,
.remove_new = txx9ndfmc_remove,
.resume = txx9ndfmc_resume, .resume = txx9ndfmc_resume,
.driver = { .driver = {
.name = "txx9ndfmc", .name = "txx9ndfmc",
}, },
}; };
module_platform_driver(txx9ndfmc_driver);
module_platform_driver_probe(txx9ndfmc_driver, txx9ndfmc_probe);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver");
......
...@@ -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;
ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len); if (n_dice) {
if (ret) die_size = div_u64(mtd->size, n_dice);
return ret; if (!(len & (die_size - 1)) && !(addr & (die_size - 1)))
multi_die_erase = true;
/* whole-chip erase? */ } else {
if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) { die_size = mtd->size;
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); ret = spi_nor_prep_and_lock_pe(nor, instr->addr, instr->len);
spi_nor_unlock_device(nor);
if (ret) if (ret)
goto erase_err; return ret;
/* /* chip (die) erase? */
* Scale the timeout linearly with the size of the flash, with if ((len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) ||
* a minimum calibrated to an old 2MB flash. We could try to multi_die_erase) {
* pull these from CFI/SFDP, but these values should be good ret = spi_nor_erase_dice(nor, addr, len, die_size);
* 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 != -EOPNOTSUPP) if (ret && ret != -EOPNOTSUPP)
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;
} }
...@@ -3453,9 +3493,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, ...@@ -3453,9 +3493,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)
...@@ -3519,25 +3557,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, ...@@ -3519,25 +3557,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;
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
struct ssfdcr_record { struct ssfdcr_record {
struct mtd_blktrans_dev mbd; struct mtd_blktrans_dev mbd;
int usecount;
unsigned char heads; unsigned char heads;
unsigned char sectors; unsigned char sectors;
unsigned short cylinders; unsigned short cylinders;
......
...@@ -1003,6 +1003,8 @@ struct nand_op_parser { ...@@ -1003,6 +1003,8 @@ struct nand_op_parser {
/** /**
* struct nand_operation - NAND operation descriptor * struct nand_operation - NAND operation descriptor
* @cs: the CS line to select for this NAND operation * @cs: the CS line to select for this NAND operation
* @deassert_wp: set to true when the operation requires the WP pin to be
* de-asserted (ERASE, PROG, ...)
* @instrs: array of instructions to execute * @instrs: array of instructions to execute
* @ninstrs: length of the @instrs array * @ninstrs: length of the @instrs array
* *
...@@ -1010,6 +1012,7 @@ struct nand_op_parser { ...@@ -1010,6 +1012,7 @@ struct nand_op_parser {
*/ */
struct nand_operation { struct nand_operation {
unsigned int cs; unsigned int cs;
bool deassert_wp;
const struct nand_op_instr *instrs; const struct nand_op_instr *instrs;
unsigned int ninstrs; unsigned int ninstrs;
}; };
...@@ -1021,6 +1024,14 @@ struct nand_operation { ...@@ -1021,6 +1024,14 @@ struct nand_operation {
.ninstrs = ARRAY_SIZE(_instrs), \ .ninstrs = ARRAY_SIZE(_instrs), \
} }
#define NAND_DESTRUCTIVE_OPERATION(_cs, _instrs) \
{ \
.cs = _cs, \
.deassert_wp = true, \
.instrs = _instrs, \
.ninstrs = ARRAY_SIZE(_instrs), \
}
int nand_op_parser_exec_op(struct nand_chip *chip, int nand_op_parser_exec_op(struct nand_chip *chip,
const struct nand_op_parser *parser, const struct nand_op_parser *parser,
const struct nand_operation *op, bool check_only); const struct nand_operation *op, bool check_only);
...@@ -1104,6 +1115,7 @@ struct nand_controller_ops { ...@@ -1104,6 +1115,7 @@ struct nand_controller_ops {
* the bus without restarting an entire read operation nor * the bus without restarting an entire read operation nor
* changing the column. * changing the column.
* @supported_op.cont_read: The controller supports sequential cache reads. * @supported_op.cont_read: The controller supports sequential cache reads.
* @controller_wp: the controller is in charge of handling the WP pin.
*/ */
struct nand_controller { struct nand_controller {
struct mutex lock; struct mutex lock;
...@@ -1112,6 +1124,7 @@ struct nand_controller { ...@@ -1112,6 +1124,7 @@ struct nand_controller {
unsigned int data_only_read: 1; unsigned int data_only_read: 1;
unsigned int cont_read: 1; unsigned int cont_read: 1;
} supported_op; } supported_op;
bool controller_wp;
}; };
static inline void nand_controller_init(struct nand_controller *nfc) static inline void nand_controller_init(struct nand_controller *nfc)
...@@ -1265,6 +1278,7 @@ struct nand_secure_region { ...@@ -1265,6 +1278,7 @@ struct nand_secure_region {
* @cont_read: Sequential page read internals * @cont_read: Sequential page read internals
* @cont_read.ongoing: Whether a continuous read is ongoing or not * @cont_read.ongoing: Whether a continuous read is ongoing or not
* @cont_read.first_page: Start of the continuous read operation * @cont_read.first_page: Start of the continuous read operation
* @cont_read.pause_page: End of the current sequential cache read operation
* @cont_read.last_page: End of the continuous read operation * @cont_read.last_page: End of the continuous read operation
* @controller: The hardware controller structure which is shared among multiple * @controller: The hardware controller structure which is shared among multiple
* independent devices * independent devices
...@@ -1321,6 +1335,7 @@ struct nand_chip { ...@@ -1321,6 +1335,7 @@ struct nand_chip {
struct { struct {
bool ongoing; bool ongoing;
unsigned int first_page; unsigned int first_page;
unsigned int pause_page;
unsigned int last_page; unsigned int last_page;
} cont_read; } cont_read;
......
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