Core MTD:

- Some important fixes were brought to the virt-concat driver.
 - An INT0800 firmware-flash map driver was added.
 - mtdblock: prevent reclaim I/O during request processing.
 - cfi_cmdset_0002: cap the write-buffer chunk at 256 bytes on an x8 device
 
 Raw NAND:
 - The Qualcomm controller driver has been cleaned up again.
 - Arasan and IBM have been improved/created.
 - Reflect that ESMT and GigaDevice share ID 0xc8, and disable broken
   timings feature on GD9FU2G8F3A.
 - Platform data support has been dropped from the lpc32xx drivers.
 
 SPI NAND:
 - Heyangtek uses two bytes for the BBM like every other device.
 - Winbond: Add support for W25N04LW and W25N08LW.
 - Support for ISSI SPI NAND flashes.
 
 SPI NOR:
 - Support for new Macronix and (lots of) Winbond flash devices
 - The addition of the Winbond flashes was accompanied by a big rewrite
   of the status register handling. There were many small helper
   functions to enable quad I/O mode in many different ways. That's now
   consolidated in one generic function.
 - The SFPD handling was reworked. It used to be only parsed for flashes
   present in the in-kernel database which had any of the multi-I/O bit
   set - or with the addition of generic flash driver for any unknown
   flashes. This left a gap where SFDP wasn't handled and it turns out
   newer flashes (with SFDP support) will reused IDs of just these old
   flashed. With the rework, SFDP is tried to be parsed for any flashes
   and will amend the information which is already present in the
   in-kernel database.
 - SFDP size limit was increased to 16KiB. There are flashes which have
   some larger gaps between tables and thus exceeds the former 4KiB
   limit.
 - Flash fixups are getting more and more common. Due to this, the fixups
   are now a separate list (per manufacturer) instead of being attached
   to a specific flash. A new .match() callback makes them more flexible,
   when one is applied.
 - 1-1-8 and 1-8-8 modes are now supported with larger flashes which
   needs 4byte addresses.
 
 Then there is the load of API updates and misc AI fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEE9HuaYnbmDhq/XIDIJWrqGEe9VoQFAmrbkigACgkQJWrqGEe9
 VoRBUwf7BuPGKsMuEp7FEIaXEWUjW7vjRY5LTSuRvFzx7TvcY8tJuApaJYKL8MVp
 x9pyr1bBKqO4f2GjvfjjqSZEX1ZcoPrhvyXs50yX4s+BHyRgEo3beYQ/hMn90/uL
 6CN24MrlaGAnvrci7ZKY2ueZ882y7Zdqfgl9rrDXV6wyAM/O2um6Fy4eGogKAy9M
 TqmD5EMXb9qwFGqcEeZ2FS/xZGa1Rea+2gxa9E2W0ZoWjC/W9yYJ/skvxNcC4mFv
 Ey8o6KMVHMEVcdD949A7Vls45PXi5vkn/FYwoWxOpaInjKW/u/QNB9Vd6cmhBWIY
 3TgO9TBMUC10JxDjyifcTb6LsZ0Cqg==
 =5LzC
 -----END PGP SIGNATURE-----

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

Pull MTD updates from Miquel Raynal:
 "Core MTD:
   - Fixes to the virt-concat driver
   - Added INT0800 firmware-flash map driver
   - mtdblock: prevent reclaim I/O during request processing
   - cfi_cmdset_0002: cap the write-buffer chunk at 256 bytes on an x8 device

  Raw NAND:
   - Clean up the Qualcomm controller driver again
   - Improve / create Arasan and IBM
   - Reflect that ESMT and GigaDevice share ID 0xc8, and disable broken
     timings feature on GD9FU2G8F3A
   - Drop platform data support from the lpc32xx drivers

  SPI NAND:
   - Heyangtek uses two bytes for the BBM like every other device
   - Winbond: Add support for W25N04LW and W25N08LW
   - Support for ISSI SPI NAND flashes

  SPI NOR:
   - Support for new Macronix and (lots of) Winbond flash devices
   - Rewrite the status register handling. There were many small helper
     functions to enable quad I/O mode in many different ways. That's
     now consolidated in one generic function.
   - Rework the SFPD handling. It used to be only parsed for flashes
     present in the in-kernel database which had any of the multi-I/O
     bit set - or with the addition of generic flash driver for any
     unknown flashes. This left a gap where SFDP wasn't handled and it
     turns out newer flashes (with SFDP support) will reused IDs of just
     these old flashed. With the rework, SFDP is tried to be parsed for
     any flashes and will amend the information which is already present
     in the in-kernel database.
   - Increase SFDP size limit to 16KiB. There are flashes which have
     some larger gaps between tables and thus exceeds the former 4KiB
     limit.
   - Flash fixups are getting more and more common. Due to this, the
     fixups are now a separate list (per manufacturer) instead of being
     attached to a specific flash. A new .match() callback makes them
     more flexible, when one is applied.
   - Support 1-1-8 and 1-8-8 modes with larger flashes which need 4byte
     addresses

  Then there is the load of API updates and misc AI fixes"

* tag 'mtd/for-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (155 commits)
  mtd: spi-nor: sfdp: reduce stack usage in spi_nor_parse_sfdp()
  mtd: spi-nor: sfdp: fix missing kernel-doc
  mtd: parsers: qcom: fix offset/size overflow on large partitions
  mtd: spinand: heyangtek: Reserve both bad block marker bytes
  mtd: rawnand: pl353: fix clk reference leak in pl35x_nfc_setup_interface()
  mtd: nand: use dmaengine_submit() instead of ->tx_submit()
  dt-bindings: mtd: convert IBM NDFC binding to YAML
  mtd: rawnand: fsl_ifc: allocate shared ctrl with devm_kzalloc
  mtd: rawnand: qcom: fix read location register usage in config_nand_cw_read()
  mtd: rawnand: qcom: move common code to the 'qpic_common' module
  mtd: rawnand: qcom: rename 'nand_stats' structure
  mtd: rawnand: qcom: make nandc_set_read_loc_last() more generic
  mtd: rawnand: qcom: make nandc_set_read_loc_first() more generic
  mtd: rawnand: qcom: make config_nand_cw_write() more generic
  mtd: rawnand: qcom: make config_nand_page_write() more generic
  mtd: rawnand: qcom: make config_nand_page_read() more generic
  mtd: rawnand: qcom: make check_flash_errors() more generic
  dt-bindings: mtd: arasan: Add "power-domains" for ZynqMP
  mtd: virt-concat: unlink discarded items before freeing
  mtd: virt-concat: unlink concat node before freeing it
  ...
This commit is contained in:
Linus Torvalds 2026-10-24 19:49:58 -10:00
commit 44388c8f3e
80 changed files with 2240 additions and 1505 deletions

View File

@ -35,6 +35,9 @@ properties:
interrupts:
maxItems: 1
power-domains:
maxItems: 1
required:
- compatible
- reg

View File

@ -1,39 +0,0 @@
AMCC NDFC (NanD Flash Controller)
Required properties:
- compatible : "ibm,ndfc".
- reg : should specify chip select and size used for the chip (0x2000).
Optional properties:
- ccr : NDFC config and control register value (default 0).
- bank-settings : NDFC bank configuration register value (default 0).
Notes:
- partition(s) - follows the OF MTD standard for partitions
Example:
ndfc@1,0 {
compatible = "ibm,ndfc";
reg = <0x00000001 0x00000000 0x00002000>;
ccr = <0x00001000>;
bank-settings = <0x80002222>;
#address-cells = <1>;
#size-cells = <1>;
nand {
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel";
reg = <0x00000000 0x00200000>;
};
partition@200000 {
label = "root";
reg = <0x00200000 0x03E00000>;
};
};
};

View File

@ -0,0 +1,128 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mtd/ibm,ndfc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IBM/AMCC NDFC (NAND Flash Controller)
maintainers:
- Miquel Raynal <miquel.raynal@bootlin.com>
- Richard Weinberger <richard@nod.at>
description: |
The NDFC is the NAND flash controller integrated into the IBM/AMCC
PowerPC 44x SoCs.
allOf:
- $ref: nand-controller-legacy.yaml#
properties:
compatible:
const: ibm,ndfc
reg:
description: Chip select and size used for the chip.
maxItems: 1
ccr:
description: NDFC config and control register value.
$ref: /schemas/types.yaml#/definitions/uint32
bank-settings:
description: NDFC bank configuration register value.
$ref: /schemas/types.yaml#/definitions/uint32
nand:
type: object
description: |
NAND chip attached to the controller. The driver picks the first
child of the controller node, so this node is not required to be
named "nand@<cs>".
allOf:
- $ref: mtd.yaml#
- $ref: nand-property.yaml#
- $ref: raw-nand-property.yaml#
properties:
"#address-cells":
const: 1
"#size-cells":
const: 1
patternProperties:
"^partition@[0-9a-f]+$":
$ref: /schemas/mtd/partitions/partition.yaml#/$defs/partition-node
deprecated: true
unevaluatedProperties: false
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
ebc {
#address-cells = <2>;
#size-cells = <1>;
ndfc@1,0 {
compatible = "ibm,ndfc";
reg = <0x00000001 0x00000000 0x00002000>;
ccr = <0x00001000>;
bank-settings = <0x80002222>;
#address-cells = <1>;
#size-cells = <1>;
nand {
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel";
reg = <0x00000000 0x00200000>;
};
partition@200000 {
label = "root";
reg = <0x00200000 0x03e00000>;
};
};
};
};
- |
ebc {
#address-cells = <2>;
#size-cells = <1>;
ndfc@1,0 {
compatible = "ibm,ndfc";
reg = <0x00000001 0x00000000 0x00002000>;
#address-cells = <1>;
#size-cells = <1>;
nand {
#address-cells = <1>;
#size-cells = <1>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel";
reg = <0x00000000 0x00200000>;
};
partition@200000 {
label = "root";
reg = <0x00200000 0x03e00000>;
};
};
};
};
};
...

View File

@ -118,7 +118,7 @@ section, after the ``---`` marker.
write size 1
page size 256
address nbytes 3
flags HAS_LOCK | HAS_16BIT_SR | SOFT_RESET | SWP_IS_VOLATILE
flags HAS_LOCK | SOFT_RESET | SWP_IS_VOLATILE
opcodes
read 0xeb

View File

@ -2520,10 +2520,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd)
status = cfi_varsize_frob(mtd,
do_getlockstatus_oneblock, adr, len, NULL);
if (status)
set_bit(block, region->lockmap);
else
clear_bit(block, region->lockmap);
assign_bit(block, region->lockmap, status);
}
}
}

View File

@ -283,6 +283,21 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
pr_debug("Using buffer write method\n");
mtd->_write = cfi_amdstd_write_buffers;
}
/*
* The word count of the Write to Buffer command is a single bus
* word per device, so an x8 device can be told to program at most
* 256 bytes however large a buffer it advertises - including when
* several of them are interleaved on a wider bus, where CMD()
* replicates the count into each device's lane and it is truncated
* there.
*/
if (cfi->device_type == CFI_DEVICETYPE_X8 &&
cfi->cfiq->MaxBufWriteSize > 8) {
cfi->cfiq->MaxBufWriteSize = 8;
mtd->writebufsize = cfi_interleave(cfi) <<
cfi->cfiq->MaxBufWriteSize;
}
}
#endif /* !FORCE_WORD_WRITE */

View File

@ -1884,7 +1884,7 @@ static void doc_release_device(struct mtd_info *mtd)
/**
* docg3_resume - Awakens docg3 floor
* @pdev: platfrom device
* @pdev: platform device
*
* Returns 0 (always successful)
*/
@ -1963,7 +1963,7 @@ static int docg3_suspend(struct platform_device *pdev, pm_message_t state)
* @pdev: platform device
*
* Probes for a G3 chip at the specified IO space in the platform data
* ressources. The floor 0 must be available.
* resources. The floor 0 must be available.
*
* Returns 0 on success, -ENOMEM, -ENXIO on error
*/
@ -1971,25 +1971,15 @@ static int __init docg3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtd_info *mtd;
struct resource *ress;
void __iomem *base;
int ret, floor;
struct docg3_cascade *cascade;
ret = -ENXIO;
ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!ress) {
dev_err(dev, "No I/O memory resource defined\n");
return ret;
}
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
ret = -ENOMEM;
base = devm_ioremap(dev, ress->start, DOC_IOSPACE_SIZE);
if (!base) {
dev_err(dev, "devm_ioremap dev failed\n");
return ret;
}
cascade = devm_kcalloc(dev, DOC_MAX_NBFLOORS, sizeof(*cascade),
GFP_KERNEL);
if (!cascade)

View File

@ -263,7 +263,7 @@ static int powernv_flash_probe(struct platform_device *pdev)
}
/**
* op_release - Release the driver
* powernv_flash_release - Release the driver
* @pdev: the platform device
*
* Returns 0

View File

@ -78,14 +78,15 @@ static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to,
struct dma_chan *rx_chan = priv->rx_chan;
struct dma_async_tx_descriptor *tx;
dma_addr_t dma_dst, dma_src;
struct device *rx_dev = dmaengine_get_dma_device(rx_chan);
dma_cookie_t cookie;
int ret;
if (!priv->rx_chan || !virt_addr_valid(to) || object_is_on_stack(to))
return -EINVAL;
dma_dst = dma_map_single(rx_chan->device->dev, to, len, DMA_FROM_DEVICE);
if (dma_mapping_error(rx_chan->device->dev, dma_dst)) {
dma_dst = dma_map_single(rx_dev, to, len, DMA_FROM_DEVICE);
if (dma_mapping_error(rx_dev, dma_dst)) {
dev_dbg(priv->ctlr->dev, "DMA mapping failed\n");
return -EIO;
}
@ -117,7 +118,7 @@ static int am654_hbmc_dma_read(struct am654_hbmc_device_priv *priv, void *to,
}
unmap_dma:
dma_unmap_single(rx_chan->device->dev, dma_dst, len, DMA_FROM_DEVICE);
dma_unmap_single(rx_dev, dma_dst, len, DMA_FROM_DEVICE);
return ret;
}

View File

@ -161,6 +161,23 @@ config MTD_AMD76XROM
BE VERY CAREFUL.
config MTD_INT0800
tristate "Read-only BIOS/firmware flash via ACPI INT0800"
depends on X86 && ACPI
select MTD_ROM
help
Support for reading the system firmware (BIOS/EFI) flash chip
through the memory window described by the ACPI INT0800
"82802 firmware hub" device, present on most x86 machines.
The flash is exposed read-only via the ROM chip driver, e.g.
for firmware analysis or extracting option ROMs (such as
video BIOS images embedded in EFI firmware volumes). There is
no write or erase support.
To compile this driver as a module, choose M here: the
module will be called int0800.
config MTD_ICHXROM
tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5"
depends on X86 && MTD_JEDECPROBE

View File

@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_L440GX) += l440gx.o
obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
obj-$(CONFIG_MTD_INT0800) += int0800.o
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o

View File

@ -152,6 +152,8 @@ static int amd76xrom_init_one(struct pci_dev *pdev,
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource %pR - kernel bug?\n",
__func__, &window->rsrc);
pci_dev_put(window->pdev);
window->pdev = NULL;
return -EBUSY;
}

115
drivers/mtd/maps/int0800.c Normal file
View File

@ -0,0 +1,115 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Read-only MTD access to the system firmware flash behind the ACPI
* INT0800 "Intel 82802 firmware hub" device.
*
* On x86 systems the boot flash is decoded into the physical address
* space below 4 GB, so a plain ioremap() is sufficient to read it -
* no SPI or LPC controller access is required. The declared _CRS
* window may be larger than the real flash (the whole top-16MiB
* decode range is commonly claimed); undecoded holes read as 0xff.
*
* The device is exposed read-only via the ROM chip driver; there is
* deliberately no write or erase support.
*/
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
/* top-of-4GB firmware decode, used when _CRS reports no window */
#define INT0800_DEFAULT_PHYS 0xffe00000UL
#define INT0800_DEFAULT_SIZE SZ_2M
struct int0800 {
struct map_info map;
struct mtd_info *mtd;
};
static int int0800_probe(struct platform_device *pdev)
{
struct resource *res;
struct int0800 *fw;
resource_size_t end;
int ret;
fw = devm_kzalloc(&pdev->dev, sizeof(*fw), GFP_KERNEL);
if (!fw)
return -ENOMEM;
fw->map.name = dev_name(&pdev->dev);
fw->map.bankwidth = 1;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) {
fw->map.phys = res->start;
fw->map.size = resource_size(res);
} else {
fw->map.phys = INT0800_DEFAULT_PHYS;
fw->map.size = INT0800_DEFAULT_SIZE;
}
/*
* Plain ioremap on purpose: the window is already claimed by the
* ACPI/pnp resource reservation, so devm_ioremap_resource() would
* fail with -EBUSY.
*/
fw->map.virt = devm_ioremap(&pdev->dev, fw->map.phys, fw->map.size);
if (!fw->map.virt)
return -ENOMEM;
simple_map_init(&fw->map);
fw->mtd = do_map_probe("map_rom", &fw->map);
if (!fw->mtd)
return -ENODEV;
fw->mtd->dev.parent = &pdev->dev;
platform_set_drvdata(pdev, fw);
end = fw->map.phys + fw->map.size - 1;
dev_info(&pdev->dev, "mapped firmware window %pa-%pa\n",
&fw->map.phys, &end);
ret = mtd_device_register(fw->mtd, NULL, 0);
if (ret)
map_destroy(fw->mtd);
return ret;
}
static void int0800_remove(struct platform_device *pdev)
{
struct int0800 *fw = platform_get_drvdata(pdev);
mtd_device_unregister(fw->mtd);
map_destroy(fw->mtd);
}
static const struct acpi_device_id int0800_ids[] = {
{ "INT0800", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, int0800_ids);
static struct platform_driver int0800_driver = {
.probe = int0800_probe,
.remove = int0800_remove,
.driver = {
.name = "int0800",
.acpi_match_table = int0800_ids,
/*
* remove() frees the mtd_info and unmaps the window even
* while /dev/mtdX fds are still open; the only safe
* teardown path is module unload, which open fds block
* via mtd->owner.
*/
.suppress_bind_attrs = true,
},
};
module_platform_driver(int0800_driver);
MODULE_AUTHOR("Stephen Bancroft <stevereaver@gmail.com>");
MODULE_DESCRIPTION("Read-only MTD map over the INT0800 firmware flash window");
MODULE_LICENSE("GPL");

View File

@ -110,8 +110,6 @@ static int __init init_l440gx(void)
/* Allocate the resource region */
if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
pci_dev_put(dev);
pci_dev_put(pm_dev);
printk(KERN_WARNING "Could not allocate pm iobase resource\n");
iounmap(l440gx_map.virt);
return -ENXIO;

View File

@ -250,7 +250,7 @@ static int mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
struct mtd_info *mtd = NULL;
int err;
err = pci_enable_device(dev);
err = pcim_enable_device(dev);
if (err)
goto out;

View File

@ -18,6 +18,7 @@
#include <linux/spinlock.h>
#include <linux/hdreg.h>
#include <linux/mutex.h>
#include <linux/sched/mm.h>
#include <linux/uaccess.h>
#include "mtdcore.h"
@ -167,6 +168,7 @@ static blk_status_t mtd_queue_rq(struct blk_mq_hw_ctx *hctx,
const struct blk_mq_queue_data *bd)
{
struct mtd_blktrans_dev *dev;
unsigned int noio_flags;
dev = hctx->queue->queuedata;
if (!dev) {
@ -174,10 +176,13 @@ static blk_status_t mtd_queue_rq(struct blk_mq_hw_ctx *hctx,
return BLK_STS_IOERR;
}
/* Reclaim must not recurse into I/O while processing requests. */
noio_flags = memalloc_noio_save();
spin_lock_irq(&dev->queue_lock);
list_add_tail(&bd->rq->queuelist, &dev->rq_list);
mtd_blktrans_work(dev);
spin_unlock_irq(&dev->queue_lock);
memalloc_noio_restore(noio_flags);
return BLK_STS_OK;
}

View File

@ -130,6 +130,7 @@ int mtd_virt_concat_destroy(struct mtd_info *mtd)
mtd_concat_destroy(&concat->mtd);
}
list_del(&item->head);
for (idx = 0; idx < item->count; idx++)
of_node_put(item->nodes[idx]);
@ -201,6 +202,7 @@ void mtd_virt_concat_destroy_items(void)
int i;
list_for_each_entry_safe(item, temp, &concat_node_list, head) {
list_del(&item->head);
for (i = 0; i < item->count; i++)
of_node_put(item->nodes[i]);

View File

@ -31,7 +31,7 @@
* Given a pointer to the MTD object in the mtd_concat structure,
* we can retrieve the pointer to that structure with this macro.
*/
#define CONCAT(x) ((struct mtd_concat *)(x))
#define CONCAT(x) container_of_const(x, struct mtd_concat, mtd)
/*
* MTD methods which look up the relevant subdevice, translate the

View File

@ -563,7 +563,7 @@ int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
return -EINVAL;
if (master->pairing && master->pairing->get_wunit)
return mtd->pairing->get_wunit(master, info);
return master->pairing->get_wunit(master, info);
return info->pair;
}

View File

@ -31,8 +31,10 @@ static int mtd_get_sb(struct fs_context *fc,
int ret;
sb = sget_dev(fc, MKDEV(MTD_BLOCK_MAJOR, mtd->index));
if (IS_ERR(sb))
if (IS_ERR(sb)) {
put_mtd_device(mtd);
return PTR_ERR(sb);
}
if (sb->s_root) {
/* new mountpoint for an already mounted superblock */

View File

@ -3279,7 +3279,7 @@ static void onenand_check_features(struct mtd_info *mtd)
*
* Now it has as following
* KFM4G16Q4M has NOP 4 with version ID 0x0131
* KFM4G16Q5M has NOP 1 with versoin ID 0x013e
* KFM4G16Q5M has NOP 1 with version ID 0x013e
*/
if ((this->version_id & 0xf) == 0xe)
this->options |= ONENAND_HAS_NOP_1;

View File

@ -340,9 +340,9 @@ static inline int omap2_onenand_dma_transfer(struct omap2_onenand *c,
tx->callback = omap2_onenand_dma_complete_func;
tx->callback_param = &c->dma_done;
cookie = tx->tx_submit(tx);
cookie = dmaengine_submit(tx);
if (dma_submit_error(cookie)) {
dev_err(&c->pdev->dev, "Failed to do DMA tx_submit\n");
dev_err(&c->pdev->dev, "Failed to do dmaengine_submit\n");
return -EIO;
}

View File

@ -775,5 +775,136 @@ int qcom_nandc_alloc(struct qcom_nand_controller *nandc)
}
EXPORT_SYMBOL(qcom_nandc_alloc);
/* reads back FLASH_STATUS register set by the controller */
int qcom_nandc_check_flash_errors(struct qcom_nand_controller *nandc,
int cw_cnt)
{
int i;
qcom_nandc_dev_to_mem(nandc, true);
for (i = 0; i < cw_cnt; i++) {
u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
if (flash & (FS_OP_ERR | FS_MPU_ERR))
return -EIO;
}
return 0;
}
EXPORT_SYMBOL(qcom_nandc_check_flash_errors);
/*
* Helper to prepare DMA descriptors for configuring registers
* before reading a NAND page.
*/
void qcom_nandc_config_page_read(struct qcom_nand_controller *nandc)
{
qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
if (!nandc->props->qpic_version2)
qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
NAND_ERASED_CW_DETECT_CFG, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
}
EXPORT_SYMBOL(qcom_nandc_config_page_read);
/*
* Helper to prepare DMA descriptors used to configure registers needed for
* before writing a NAND page.
*/
void qcom_nandc_config_page_write(struct qcom_nand_controller *nandc)
{
qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
if (!nandc->props->qpic_version2)
qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
NAND_BAM_NEXT_SGL);
}
EXPORT_SYMBOL(qcom_nandc_config_page_write);
/*
* Helper to prepare DMA descriptors for configuring registers
* before writing each codeword in NAND page.
*/
void qcom_nandc_config_cw_write(struct qcom_nand_controller *nandc)
{
qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1,
NAND_BAM_NEXT_SGL);
}
EXPORT_SYMBOL(qcom_nandc_config_cw_write);
/**
* qcom_nandc_set_read_loc_first() - to set read location first register
* @nandc: NAND controller's private data
* @reg_base: location register base
* @cw_offset: code word offset
* @read_size: code word read length
* @is_last_read_loc: is this the last read location
*
* This function will set location register value
*/
void qcom_nandc_set_read_loc_first(struct qcom_nand_controller *nandc,
int reg_base, u32 cw_offset,
u32 read_size, u32 is_last_read_loc)
{
__le32 locreg_val;
u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) |
FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) |
FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc);
locreg_val = cpu_to_le32(val);
if (reg_base == NAND_READ_LOCATION_0)
nandc->regs->read_location0 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_1)
nandc->regs->read_location1 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_2)
nandc->regs->read_location2 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_3)
nandc->regs->read_location3 = locreg_val;
}
EXPORT_SYMBOL(qcom_nandc_set_read_loc_first);
/**
* qcom_nandc_set_read_loc_last - to set read location last register
* @nandc: NAND controller's private data
* @reg_base: location register base
* @cw_offset: code word offset
* @read_size: code word read length
* @is_last_read_loc: is this the last read location
*
* This function will set location last register value
*/
void qcom_nandc_set_read_loc_last(struct qcom_nand_controller *nandc,
int reg_base, u32 cw_offset,
u32 read_size, u32 is_last_read_loc)
{
__le32 locreg_val;
u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) |
FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) |
FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc);
locreg_val = cpu_to_le32(val);
if (reg_base == NAND_READ_LOCATION_LAST_CW_0)
nandc->regs->read_location_last0 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_LAST_CW_1)
nandc->regs->read_location_last1 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_LAST_CW_2)
nandc->regs->read_location_last2 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_LAST_CW_3)
nandc->regs->read_location_last3 = locreg_val;
}
EXPORT_SYMBOL(qcom_nandc_set_read_loc_last);
MODULE_DESCRIPTION("QPIC controller common api");
MODULE_LICENSE("GPL");

View File

@ -2085,7 +2085,7 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
if (!np) {
dev_err(dev, "Missing or invalid atmel,smc property\n");
ret = -EINVAL;
goto out_release_dma;
goto out_put_mck;
}
nc->smc = syscon_node_to_regmap(np);
@ -2093,11 +2093,13 @@ static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
if (IS_ERR(nc->smc)) {
ret = PTR_ERR(nc->smc);
dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
goto out_release_dma;
goto out_put_mck;
}
return 0;
out_put_mck:
clk_put(nc->mck);
out_release_dma:
if (nc->dmac)
dma_release_channel(nc->dmac);

View File

@ -3272,6 +3272,8 @@ static int brcmnand_edu_setup(struct platform_device *pdev)
ctrl->edu_irq = platform_get_irq_optional(pdev, 1);
if (ctrl->edu_irq < 0) {
if (ctrl->edu_irq != -ENXIO)
return ctrl->edu_irq;
dev_warn(dev,
"FLASH EDU enabled, using ctlrdy irq\n");
} else {

View File

@ -3229,7 +3229,7 @@ static void cadence_nand_remove(struct cdns_nand_ctrl *cdns_ctrl)
{
cadence_nand_chips_cleanup(cdns_ctrl);
if (cdns_ctrl->dmac)
dma_unmap_resource(cdns_ctrl->dmac->device->dev,
dma_unmap_resource(dmaengine_get_dma_device(cdns_ctrl->dmac),
cdns_ctrl->io.iova_dma, cdns_ctrl->io.size,
DMA_BIDIRECTIONAL, 0);
cadence_nand_irq_cleanup(cdns_ctrl->irq, cdns_ctrl);

View File

@ -12,7 +12,6 @@
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
@ -50,7 +49,6 @@ struct fsl_ifc_nand_ctrl {
unsigned int index; /* Pointer to next byte to 'read' */
unsigned int oob; /* Non zero if operating on OOB data */
unsigned int eccread; /* Non zero for a full-page ECC read */
unsigned int counter; /* counter for the initializations */
unsigned int max_bitflips; /* Saved during READ0 cmd */
};
@ -965,13 +963,6 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv)
{
struct mtd_info *mtd = nand_to_mtd(&priv->chip);
kfree(mtd->name);
if (priv->vbase)
iounmap(priv->vbase);
ifc_nand_ctrl->chips[priv->bank] = NULL;
return 0;
@ -996,12 +987,12 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
{
struct fsl_ifc_runtime __iomem *ifc;
struct fsl_ifc_mtd *priv;
struct resource res;
struct resource *res;
void __iomem *vbase;
static const char *part_probe_types[]
= { "cmdlinepart", "RedBoot", "ofpart", NULL };
int ret;
int bank;
struct device_node *node = dev->dev.of_node;
struct mtd_info *mtd;
if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->rregs)
@ -1009,15 +1000,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
ifc = fsl_ifc_ctrl_dev->rregs;
/* get, allocate and map the memory resource */
ret = of_address_to_resource(node, 0, &res);
if (ret) {
dev_err(&dev->dev, "%s: failed to get resource\n", __func__);
return ret;
}
vbase = devm_platform_get_and_ioremap_resource(dev, 0, &res);
if (IS_ERR(vbase))
return PTR_ERR(vbase);
/* find which chip select it is connected to */
for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) {
if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res.start))
if (match_bank(fsl_ifc_ctrl_dev->gregs, bank, res->start))
break;
}
@ -1033,15 +1022,13 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
mutex_lock(&fsl_ifc_nand_mutex);
if (!fsl_ifc_ctrl_dev->nand) {
ifc_nand_ctrl = kzalloc_obj(*ifc_nand_ctrl);
ifc_nand_ctrl = devm_kzalloc(fsl_ifc_ctrl_dev->dev, sizeof(*ifc_nand_ctrl),
GFP_KERNEL);
if (!ifc_nand_ctrl) {
mutex_unlock(&fsl_ifc_nand_mutex);
return -ENOMEM;
}
ifc_nand_ctrl->read_bytes = 0;
ifc_nand_ctrl->index = 0;
ifc_nand_ctrl->addr = NULL;
fsl_ifc_ctrl_dev->nand = ifc_nand_ctrl;
nand_controller_init(&ifc_nand_ctrl->controller);
@ -1054,13 +1041,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
priv->bank = bank;
priv->ctrl = fsl_ifc_ctrl_dev;
priv->dev = &dev->dev;
priv->vbase = ioremap(res.start, resource_size(&res));
if (!priv->vbase) {
dev_err(priv->dev, "%s: failed to map chip region\n", __func__);
ret = -ENOMEM;
goto err;
}
priv->vbase = vbase;
dev_set_drvdata(priv->dev, priv);
@ -1076,7 +1057,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
&ifc->ifc_nand.nand_evter_intr_en);
mtd = nand_to_mtd(&priv->chip);
mtd->name = kasprintf(GFP_KERNEL, "%llx.flash", (u64)res.start);
mtd->name = devm_kasprintf(priv->dev, GFP_KERNEL, "%llx.flash", (u64)res->start);
if (!mtd->name) {
ret = -ENOMEM;
goto err;
@ -1098,7 +1079,7 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
goto cleanup_nand;
dev_info(priv->dev, "IFC NAND device at 0x%llx, bank %d\n",
(unsigned long long)res.start, priv->bank);
(unsigned long long)res->start, priv->bank);
return 0;
@ -1121,14 +1102,6 @@ static void fsl_ifc_nand_remove(struct platform_device *dev)
nand_cleanup(chip);
fsl_ifc_chip_remove(priv);
mutex_lock(&fsl_ifc_nand_mutex);
ifc_nand_ctrl->counter--;
if (!ifc_nand_ctrl->counter) {
fsl_ifc_ctrl_dev->nand = NULL;
kfree(ifc_nand_ctrl);
}
mutex_unlock(&fsl_ifc_nand_mutex);
}
static const struct of_device_id fsl_ifc_nand_match[] = {

View File

@ -461,7 +461,7 @@ static int fsmc_correct_ecc1(struct nand_chip *chip,
chip->ecc.size, sm_order);
}
/* Count the number of 0's in buff upto a max of max_bits */
/* Count the number of 0's in buff up to a max of max_bits */
static int count_written_bits(u8 *buff, int size, int max_bits)
{
int k, written_bits = 0;
@ -524,7 +524,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
tx->callback = dma_complete;
tx->callback_param = host;
cookie = tx->tx_submit(tx);
cookie = dmaengine_submit(tx);
ret = dma_submit_error(cookie);
if (ret) {

View File

@ -2271,7 +2271,7 @@ static int nand_boot_init(struct gpmi_nand_data *this)
{
nand_boot_set_geometry(this);
/* This is ROM arch-specific initilization before the BBT scanning. */
/* This is ROM arch-specific initialization before the BBT scanning. */
if (GPMI_IS_MX23(this))
return mx23_boot_init(this);
return 0;

View File

@ -290,6 +290,7 @@ static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
struct dma_async_tx_descriptor *tx;
struct completion *dma_completion;
dma_async_tx_callback callback;
struct device *dma_dev;
struct dma_chan *chan;
dma_cookie_t cookie;
unsigned long flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
@ -307,8 +308,9 @@ static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
callback = ebu_dma_tx_callback;
}
buf_dma = dma_map_single(chan->device->dev, (void *)buf, len, dir);
if (dma_mapping_error(chan->device->dev, buf_dma)) {
dma_dev = dmaengine_get_dma_device(chan);
buf_dma = dma_map_single(dma_dev, (void *)buf, len, dir);
if (dma_mapping_error(dma_dev, buf_dma)) {
dev_err(ebu_host->dev, "Failed to map DMA buffer\n");
ret = -EIO;
goto err_unmap;
@ -322,7 +324,7 @@ static int ebu_dma_start(struct ebu_nand_controller *ebu_host, u32 dir,
tx->callback = callback;
tx->callback_param = ebu_host;
cookie = tx->tx_submit(tx);
cookie = dmaengine_submit(tx);
ret = dma_submit_error(cookie);
if (ret) {

View File

@ -19,6 +19,7 @@
#define NAND_MFR_AMD 0x01
#define NAND_MFR_ATO 0x9b
#define NAND_MFR_EON 0x92
/* ESMT and GigaDevice share the same manufacturer ID. */
#define NAND_MFR_ESMT 0xc8
#define NAND_MFR_FUJITSU 0x04
#define NAND_MFR_HYNIX 0xad

View File

@ -391,7 +391,7 @@ static void loongson_nand_dma_callback(void *data)
{
struct loongson_nand_host *host = (struct loongson_nand_host *)data;
struct dma_chan *chan = host->dma_chan;
struct device *dev = chan->device->dev;
struct device *dev = dmaengine_get_dma_device(chan);
enum dma_status status;
status = dmaengine_tx_status(chan, host->dma_cookie, NULL);
@ -407,7 +407,7 @@ static int loongson_nand_dma_transfer(struct loongson_nand_host *host, struct lo
{
struct nand_chip *chip = &host->chip;
struct dma_chan *chan = host->dma_chan;
struct device *dev = chan->device->dev;
struct device *dev = dmaengine_get_dma_device(chan);
struct dma_async_tx_descriptor *desc;
enum dma_data_direction data_dir = op->is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
enum dma_transfer_direction xfer_dir = op->is_write ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;

View File

@ -26,7 +26,6 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/gpio/consumer.h>
#include <linux/mtd/lpc32xx_mlc.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
@ -174,7 +173,6 @@ static struct nand_bbt_descr lpc32xx_nand_bbt_mirror = {
struct lpc32xx_nand_host {
struct platform_device *pdev;
struct nand_chip nand_chip;
struct lpc32xx_mlc_platform_data *pdata;
struct clk *clk;
struct gpio_desc *wp_gpio;
void __iomem *io_base;
@ -394,6 +392,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
struct device *dma_dev = dmaengine_get_dma_device(host->dma_chan);
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
unsigned long time_left;
@ -401,8 +400,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
sg_init_one(&host->sgl, mem, len);
res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
res = dma_map_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL);
if (res != 1) {
dev_err(mtd->dev.parent, "Failed to map sg list\n");
return -ENXIO;
@ -430,12 +428,10 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
goto out1;
}
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL);
return 0;
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL);
return res;
}
@ -580,24 +576,11 @@ static void lpc32xx_ecc_enable(struct nand_chip *chip, int mode)
static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host)
{
struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
dma_cap_mask_t mask;
host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx");
if (IS_ERR(host->dma_chan)) {
/* fallback to request using platform data */
if (!host->pdata || !host->pdata->dma_filter) {
dev_err(mtd->dev.parent, "no DMA platform data\n");
return -ENOENT;
}
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, "nand-mlc");
if (!host->dma_chan) {
dev_err(mtd->dev.parent, "Failed to request DMA channel\n");
return -EBUSY;
}
dev_err(mtd->dev.parent, "Failed to request DMA channel\n");
return PTR_ERR(host->dma_chan);
}
/*
@ -736,8 +719,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
host->pdata = dev_get_platdata(&pdev->dev);
/* link the private data structures */
nand_set_controller_data(nand_chip, host);
nand_set_flash_node(nand_chip, pdev->dev.of_node);

View File

@ -25,7 +25,6 @@
#include <linux/dmaengine.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/mtd/lpc32xx_slc.h>
#define LPC32XX_MODNAME "lpc32xx-nand"
@ -213,7 +212,6 @@ struct lpc32xx_nand_cfg_slc {
struct lpc32xx_nand_host {
struct nand_chip nand_chip;
struct lpc32xx_slc_platform_data *pdata;
struct clk *clk;
struct gpio_desc *wp_gpio;
void __iomem *io_base;
@ -427,6 +425,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
{
struct nand_chip *chip = mtd_to_nand(mtd);
struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
struct device *dma_dev = dmaengine_get_dma_device(host->dma_chan);
struct dma_async_tx_descriptor *desc;
int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
int res;
@ -448,8 +447,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
sg_init_one(&host->sgl, mem, len);
res = dma_map_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
res = dma_map_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL);
if (res != 1) {
dev_err(mtd->dev.parent, "Failed to map sg list\n");
return -ENXIO;
@ -477,13 +475,11 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, dma_addr_t dma,
res = 0;
}
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL);
return res;
out1:
dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
DMA_BIDIRECTIONAL);
dma_unmap_sg(dma_dev, &host->sgl, 1, DMA_BIDIRECTIONAL);
return -ENXIO;
}
@ -727,24 +723,11 @@ static int lpc32xx_nand_write_page_raw_syndrome(struct nand_chip *chip,
static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host)
{
struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
dma_cap_mask_t mask;
host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx");
if (IS_ERR(host->dma_chan)) {
/* fallback to request using platform data */
if (!host->pdata || !host->pdata->dma_filter) {
dev_err(mtd->dev.parent, "no DMA platform data\n");
return -ENOENT;
}
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, "nand-slc");
if (!host->dma_chan) {
dev_err(mtd->dev.parent, "Failed to request DMA channel\n");
return -EBUSY;
}
dev_err(mtd->dev.parent, "Failed to request DMA channel\n");
return PTR_ERR(host->dma_chan);
}
return 0;
@ -873,8 +856,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
gpiod_set_consumer_name(host->wp_gpio, "NAND WP");
host->pdata = dev_get_platdata(&pdev->dev);
chip = &host->nand_chip;
mtd = nand_to_mtd(chip);
nand_set_controller_data(chip, host);

View File

@ -868,6 +868,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
unsigned int len)
{
unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE);
struct device *dma_dev = dmaengine_get_dma_device(nfc->dma_chan);
struct dma_async_tx_descriptor *tx;
struct scatterlist sg;
dma_cookie_t cookie;
@ -876,7 +877,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
marvell_nfc_enable_dma(nfc);
/* Prepare the DMA transfer */
sg_init_one(&sg, nfc->dma_buf, dma_len);
ret = dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
ret = dma_map_sg(dma_dev, &sg, 1, direction);
if (!ret) {
dev_err(nfc->dev, "Could not map DMA S/G list\n");
return -ENXIO;
@ -888,7 +889,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
DMA_PREP_INTERRUPT);
if (!tx) {
dev_err(nfc->dev, "Could not prepare DMA S/G list\n");
dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
dma_unmap_sg(dma_dev, &sg, 1, direction);
return -ENXIO;
}
@ -900,7 +901,7 @@ static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
dma_async_issue_pending(nfc->dma_chan);
ret = marvell_nfc_wait_cmdd(nfc->selected_chip);
dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
dma_unmap_sg(dma_dev, &sg, 1, direction);
marvell_nfc_disable_dma(nfc);
if (ret) {
dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n",

View File

@ -42,7 +42,7 @@
#define CON_BRD BIT(8) /* burst read */
#define CON_BWR BIT(9) /* burst write */
#define CON_SEC_SHIFT (12)
/* Timming control register */
/* Timing control register */
#define NFI_ACCCON (0x0C)
#define NFI_INTR_EN (0x10)
#define INTR_AHB_DONE_EN BIT(6)

View File

@ -6205,7 +6205,7 @@ static int nand_scan_tail(struct nand_chip *chip)
/*
* FIXME: some NAND manufacturer drivers expect the first die to be
* selected when manufacturer->init() is called. They should be fixed
* to explictly select the relevant die when interacting with the NAND
* to explicitly select the relevant die when interacting with the NAND
* chip.
*/
nand_select_target(chip, 0);

View File

@ -3,6 +3,9 @@
* Copyright (C) 2018 Toradex AG
*
* Author: Marcel Ziswiler <marcel.ziswiler@toradex.com>
*
* ESMT and GigaDevice share the same JEDEC manufacturer ID (0xc8), so this
* file handles chips from both vendors.
*/
#include <linux/mtd/rawnand.h>
@ -38,6 +41,35 @@ static void esmt_nand_decode_id(struct nand_chip *chip)
nanddev_set_ecc_requirements(base, &requirements);
}
/*
* GigaDevice GD9FU2G8F3A does not implement the timing mode feature:
* GET_FEATURES does not report the mode that was written, and
* SET_FEATURES is ignored, even though the parameter page advertises
* support. The vendor confirmed the chip works at every advertised
* timing mode without being told, so unflag the feature and configure
* the host side only.
*/
static void esmt_nand_fix_broken_get_timings(struct nand_chip *chip)
{
int i;
static const char * const broken_get_timings[] = {
"GD9FU2G8F3A",
};
if (!chip->parameters.supports_set_get_features)
return;
i = match_string(broken_get_timings, ARRAY_SIZE(broken_get_timings),
chip->parameters.model);
if (i < 0)
return;
bitmap_clear(chip->parameters.get_feature_list,
ONFI_FEATURE_ADDR_TIMING_MODE, 1);
bitmap_clear(chip->parameters.set_feature_list,
ONFI_FEATURE_ADDR_TIMING_MODE, 1);
}
static int esmt_nand_init(struct nand_chip *chip)
{
if (nand_is_slc(chip))
@ -50,6 +82,8 @@ static int esmt_nand_init(struct nand_chip *chip)
chip->options |= NAND_BBM_FIRSTPAGE | NAND_BBM_SECONDPAGE |
NAND_BBM_LASTPAGE;
esmt_nand_fix_broken_get_timings(chip);
return 0;
}

View File

@ -185,7 +185,7 @@ static const struct nand_manufacturer_desc nand_manufacturer_descs[] = {
{NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops},
{NAND_MFR_ATO, "ATO"},
{NAND_MFR_EON, "Eon"},
{NAND_MFR_ESMT, "ESMT", &esmt_nand_manuf_ops},
{NAND_MFR_ESMT, "ESMT/GigaDevice", &esmt_nand_manuf_ops},
{NAND_MFR_FUJITSU, "Fujitsu"},
{NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops},
{NAND_MFR_INTEL, "Intel"},

View File

@ -319,7 +319,7 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
NAND_CTRL_CLE | NAND_CTRL_CHANGE);
chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
/* EZ-NAND can take upto 250ms as per ONFi v4.0 */
/* EZ-NAND can take up to 250ms as per ONFi v4.0 */
nand_wait_status_ready(chip, 250);
return;
@ -459,7 +459,7 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
/* EZ-NAND can take upto 250ms as per ONFi v4.0 */
/* EZ-NAND can take up to 250ms as per ONFi v4.0 */
nand_wait_status_ready(chip, 250);
return;

View File

@ -162,8 +162,8 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
return -ENODEV;
nand_set_flash_node(chip, flash_np);
mtd->name = kasprintf(GFP_KERNEL, "%s.%pOFn", dev_name(&ndfc->ofdev->dev),
flash_np);
mtd->name = devm_kasprintf(&ndfc->ofdev->dev, GFP_KERNEL, "%s.%pOFn",
dev_name(&ndfc->ofdev->dev), flash_np);
if (!mtd->name) {
ret = -ENOMEM;
goto err;
@ -177,18 +177,21 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
err:
of_node_put(flash_np);
if (ret)
kfree(mtd->name);
return ret;
}
static int ndfc_probe(struct platform_device *ofdev)
{
struct ndfc_controller *ndfc;
void __iomem *ndfcbase;
const __be32 *reg;
u32 ccr;
u32 cs;
int err, len = 0;
int len = 0;
ndfcbase = devm_platform_ioremap_resource(ofdev, 0);
if (IS_ERR(ndfcbase))
return PTR_ERR(ndfcbase);
/* Read the reg property to get the chip select */
reg = of_get_property(ofdev->dev.of_node, "reg", &len);
@ -210,11 +213,7 @@ static int ndfc_probe(struct platform_device *ofdev)
ndfc->ofdev = ofdev;
dev_set_drvdata(&ofdev->dev, ndfc);
ndfc->ndfcbase = of_iomap(ofdev->dev.of_node, 0);
if (!ndfc->ndfcbase) {
dev_err(&ofdev->dev, "failed to get memory\n");
return -EIO;
}
ndfc->ndfcbase = ndfcbase;
ccr = NDFC_CCR_BS(ndfc->chip_select);
@ -232,13 +231,7 @@ static int ndfc_probe(struct platform_device *ofdev)
iowrite32be(be32_to_cpup(reg), ndfc->ndfcbase + offset);
}
err = ndfc_chip_init(ndfc, ofdev->dev.of_node);
if (err) {
iounmap(ndfc->ndfcbase);
return err;
}
return 0;
return ndfc_chip_init(ndfc, ofdev->dev.of_node);
}
static void ndfc_remove(struct platform_device *ofdev)
@ -251,7 +244,6 @@ static void ndfc_remove(struct platform_device *ofdev)
ret = mtd_device_unregister(mtd);
WARN_ON(ret);
nand_cleanup(chip);
kfree(mtd->name);
}
static const struct of_device_id ndfc_match[] = {

View File

@ -993,7 +993,6 @@ static int ma35_nand_probe(struct platform_device *pdev)
ret = ma35_nand_chips_init(&pdev->dev, nand);
if (ret) {
dev_err(&pdev->dev, "failed to init NAND chips\n");
clk_disable(nand->clk);
return ret;
}

View File

@ -380,8 +380,8 @@ static inline int omap_nand_dma_transfer(struct nand_chip *chip,
{
struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
struct dma_async_tx_descriptor *tx;
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
DMA_FROM_DEVICE;
enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
struct device *dma_dev = dmaengine_get_dma_device(info->dma);
struct scatterlist sg;
unsigned long tim, limit;
unsigned n;
@ -392,7 +392,7 @@ static inline int omap_nand_dma_transfer(struct nand_chip *chip,
goto out_copy;
sg_init_one(&sg, addr, len);
n = dma_map_sg(info->dma->device->dev, &sg, 1, dir);
n = dma_map_sg(dma_dev, &sg, 1, dir);
if (n == 0) {
dev_err(&info->pdev->dev,
"Couldn't DMA map a %d byte buffer\n", len);
@ -434,11 +434,11 @@ static inline int omap_nand_dma_transfer(struct nand_chip *chip,
/* disable and stop the PFPW engine */
omap_prefetch_reset(info->gpmc_cs, info);
dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
dma_unmap_sg(dma_dev, &sg, 1, dir);
return 0;
out_copy_unmap:
dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
dma_unmap_sg(dma_dev, &sg, 1, dir);
out_copy:
is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false)
: omap_nand_data_out(chip, addr, len, false);
@ -666,9 +666,7 @@ static void gen_true_ecc(u8 *ecc_buf)
* If there is no error, %0 is returned. If there is an error but it
* was corrected, %1 is returned. Otherwise, %-1 is returned.
*/
static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */
u8 *ecc_data2, /* read from register */
u8 *page_data)
static int omap_compare_ecc(u8 *ecc_data1, u8 *ecc_data2, u8 *page_data)
{
uint i;
u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8];

View File

@ -807,6 +807,7 @@ static int pl35x_nfc_setup_interface(struct nand_chip *chip, int cs,
* macro to convert from one to the other.
*/
period_ns = NSEC_PER_SEC / clk_get_rate(mclk);
clk_put(mclk);
/*
* PL35X SMC needs one extra read cycle in SDR Mode 5. This is not

View File

@ -149,70 +149,6 @@ static bool qcom_nandc_is_last_cw(struct nand_ecc_ctrl *ecc, int cw)
return cw == (ecc->steps - 1);
}
/**
* nandc_set_read_loc_first() - to set read location first register
* @chip: NAND Private Flash Chip Data
* @reg_base: location register base
* @cw_offset: code word offset
* @read_size: code word read length
* @is_last_read_loc: is this the last read location
*
* This function will set location register value
*/
static void nandc_set_read_loc_first(struct nand_chip *chip,
int reg_base, u32 cw_offset,
u32 read_size, u32 is_last_read_loc)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
__le32 locreg_val;
u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) |
FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) |
FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc);
locreg_val = cpu_to_le32(val);
if (reg_base == NAND_READ_LOCATION_0)
nandc->regs->read_location0 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_1)
nandc->regs->read_location1 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_2)
nandc->regs->read_location2 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_3)
nandc->regs->read_location3 = locreg_val;
}
/**
* nandc_set_read_loc_last - to set read location last register
* @chip: NAND Private Flash Chip Data
* @reg_base: location register base
* @cw_offset: code word offset
* @read_size: code word read length
* @is_last_read_loc: is this the last read location
*
* This function will set location last register value
*/
static void nandc_set_read_loc_last(struct nand_chip *chip,
int reg_base, u32 cw_offset,
u32 read_size, u32 is_last_read_loc)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
__le32 locreg_val;
u32 val = FIELD_PREP(READ_LOCATION_OFFSET_MASK, cw_offset) |
FIELD_PREP(READ_LOCATION_SIZE_MASK, read_size) |
FIELD_PREP(READ_LOCATION_LAST_MASK, is_last_read_loc);
locreg_val = cpu_to_le32(val);
if (reg_base == NAND_READ_LOCATION_LAST_CW_0)
nandc->regs->read_location_last0 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_LAST_CW_1)
nandc->regs->read_location_last1 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_LAST_CW_2)
nandc->regs->read_location_last2 = locreg_val;
else if (reg_base == NAND_READ_LOCATION_LAST_CW_3)
nandc->regs->read_location_last3 = locreg_val;
}
/* helper to configure location register values */
static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg,
u32 cw_offset, u32 read_size, u32 is_last_read_loc)
@ -227,11 +163,13 @@ static void nandc_set_read_loc(struct nand_chip *chip, int cw, int reg,
reg_base += reg * 4;
if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
return nandc_set_read_loc_last(chip, reg_base, cw_offset,
read_size, is_last_read_loc);
return qcom_nandc_set_read_loc_last(nandc, reg_base, cw_offset,
read_size,
is_last_read_loc);
else
return nandc_set_read_loc_first(chip, reg_base, cw_offset,
read_size, is_last_read_loc);
return qcom_nandc_set_read_loc_first(nandc, reg_base, cw_offset,
read_size,
is_last_read_loc);
}
/* helper to configure address register values */
@ -301,24 +239,6 @@ static void update_rw_regs(struct qcom_nand_host *host, int num_cw, bool read, i
host->cw_data : host->cw_size, 1);
}
/*
* Helper to prepare DMA descriptors for configuring registers
* before reading a NAND page.
*/
static void config_nand_page_read(struct nand_chip *chip)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
if (!nandc->props->qpic_version2)
qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_clr,
NAND_ERASED_CW_DETECT_CFG, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->erased_cw_detect_cfg_set,
NAND_ERASED_CW_DETECT_CFG, 1, NAND_ERASED_CW_SET | NAND_BAM_NEXT_SGL);
}
/*
* Helper to prepare DMA descriptors for configuring registers
* before reading each codeword in NAND page.
@ -328,14 +248,16 @@ config_nand_cw_read(struct nand_chip *chip, bool use_ecc, int cw)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
__le32 *reg = &nandc->regs->read_location0;
int reg_base = NAND_READ_LOCATION_0;
if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw))
if (nandc->props->qpic_version2 && qcom_nandc_is_last_cw(ecc, cw)) {
reg = &nandc->regs->read_location_last0;
reg_base = NAND_READ_LOCATION_LAST_CW_0;
}
if (nandc->props->supports_bam)
qcom_write_reg_dma(nandc, reg, NAND_READ_LOCATION_0, 4, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, reg, reg_base, 4, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
@ -357,43 +279,12 @@ static void
config_nand_single_cw_page_read(struct nand_chip *chip,
bool use_ecc, int cw)
{
config_nand_page_read(chip);
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
qcom_nandc_config_page_read(nandc);
config_nand_cw_read(chip, use_ecc, cw);
}
/*
* Helper to prepare DMA descriptors used to configure registers needed for
* before writing a NAND page.
*/
static void config_nand_page_write(struct nand_chip *chip)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
qcom_write_reg_dma(nandc, &nandc->regs->addr0, NAND_ADDR0, 2, 0);
qcom_write_reg_dma(nandc, &nandc->regs->cfg0, NAND_DEV0_CFG0, 3, 0);
if (!nandc->props->qpic_version2)
qcom_write_reg_dma(nandc, &nandc->regs->ecc_buf_cfg, NAND_EBI2_ECC_BUF_CFG, 1,
NAND_BAM_NEXT_SGL);
}
/*
* Helper to prepare DMA descriptors for configuring registers
* before writing each codeword in NAND page.
*/
static void config_nand_cw_write(struct nand_chip *chip)
{
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
qcom_write_reg_dma(nandc, &nandc->regs->cmd, NAND_FLASH_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL);
qcom_read_reg_dma(nandc, NAND_FLASH_STATUS, 1, NAND_BAM_NEXT_SGL);
qcom_write_reg_dma(nandc, &nandc->regs->clrflashstatus, NAND_FLASH_STATUS, 1, 0);
qcom_write_reg_dma(nandc, &nandc->regs->clrreadstatus, NAND_READ_STATUS, 1,
NAND_BAM_NEXT_SGL);
}
/*
* when using BCH ECC, the HW flags an error in NAND_FLASH_STATUS if it read
* an erased CW, and reports an erased CW in NAND_ERASED_CW_DETECT_STATUS.
@ -442,31 +333,6 @@ static bool erased_chunk_check_and_fixup(u8 *data_buf, int data_len)
return true;
}
struct read_stats {
__le32 flash;
__le32 buffer;
__le32 erased_cw;
};
/* reads back FLASH_STATUS register set by the controller */
static int check_flash_errors(struct qcom_nand_host *host, int cw_cnt)
{
struct nand_chip *chip = &host->chip;
struct qcom_nand_controller *nandc = get_qcom_nand_controller(chip);
int i;
qcom_nandc_dev_to_mem(nandc, true);
for (i = 0; i < cw_cnt; i++) {
u32 flash = le32_to_cpu(nandc->reg_read_buf[i]);
if (flash & (FS_OP_ERR | FS_MPU_ERR))
return -EIO;
}
return 0;
}
/* performs raw read for one codeword */
static int
qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
@ -491,7 +357,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
qcom_clear_bam_transaction(nandc);
set_address(host, host->cw_size * cw, page);
update_rw_regs(host, 1, true, raw_cw);
config_nand_page_read(chip);
qcom_nandc_config_page_read(nandc);
data_size1 = mtd->writesize - host->cw_size * (ecc->steps - 1);
oob_size1 = host->bbm_size;
@ -538,7 +404,7 @@ qcom_nandc_read_cw_raw(struct mtd_info *mtd, struct nand_chip *chip,
return ret;
}
return check_flash_errors(host, 1);
return qcom_nandc_check_flash_errors(nandc, 1);
}
/*
@ -624,12 +490,12 @@ static int parse_read_errors(struct qcom_nand_host *host, u8 *data_buf,
struct mtd_info *mtd = nand_to_mtd(chip);
struct nand_ecc_ctrl *ecc = &chip->ecc;
unsigned int max_bitflips = 0, uncorrectable_cws = 0;
struct read_stats *buf;
struct qcom_nandc_read_stats *buf;
bool flash_op_err = false, erased;
int i;
u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
buf = (struct read_stats *)nandc->reg_read_buf;
buf = (struct qcom_nandc_read_stats *)nandc->reg_read_buf;
qcom_nandc_dev_to_mem(nandc, true);
for (i = 0; i < ecc->steps; i++, buf++) {
@ -728,7 +594,7 @@ static int read_page_ecc(struct qcom_nand_host *host, u8 *data_buf,
u8 *data_buf_start = data_buf, *oob_buf_start = oob_buf;
int i, ret;
config_nand_page_read(chip);
qcom_nandc_config_page_read(nandc);
/* queue cmd descs for each codeword */
for (i = 0; i < ecc->steps; i++) {
@ -990,7 +856,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const u8 *buf,
host->use_ecc = true;
update_rw_regs(host, ecc->steps, false, 0);
config_nand_page_write(chip);
qcom_nandc_config_page_write(nandc);
for (i = 0; i < ecc->steps; i++) {
int data_size, oob_size;
@ -1021,7 +887,7 @@ static int qcom_nandc_write_page(struct nand_chip *chip, const u8 *buf,
oob_buf, oob_size, 0);
}
config_nand_cw_write(chip);
qcom_nandc_config_cw_write(nandc);
data_buf += data_size;
oob_buf += oob_size;
@ -1060,7 +926,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
host->use_ecc = false;
update_rw_regs(host, ecc->steps, false, 0);
config_nand_page_write(chip);
qcom_nandc_config_page_write(nandc);
for (i = 0; i < ecc->steps; i++) {
int data_size1, data_size2, oob_size1, oob_size2;
@ -1097,7 +963,7 @@ static int qcom_nandc_write_page_raw(struct nand_chip *chip,
qcom_write_data_dma(nandc, reg_off, oob_buf, oob_size2, 0);
oob_buf += oob_size2;
config_nand_cw_write(chip);
qcom_nandc_config_cw_write(nandc);
}
ret = qcom_submit_descs(nandc);
@ -1144,10 +1010,10 @@ static int qcom_nandc_write_oob(struct nand_chip *chip, int page)
set_address(host, host->cw_size * (ecc->steps - 1), page);
update_rw_regs(host, 1, false, 0);
config_nand_page_write(chip);
qcom_nandc_config_page_write(nandc);
qcom_write_data_dma(nandc, FLASH_BUF_ACC,
nandc->data_buffer, data_size + oob_size, 0);
config_nand_cw_write(chip);
qcom_nandc_config_cw_write(nandc);
ret = qcom_submit_descs(nandc);
if (ret) {
@ -1181,7 +1047,7 @@ static int qcom_nandc_block_bad(struct nand_chip *chip, loff_t ofs)
if (ret)
goto err;
if (check_flash_errors(host, 1)) {
if (qcom_nandc_check_flash_errors(nandc, 1)) {
dev_warn(nandc->dev, "error when trying to read BBM\n");
goto err;
}
@ -1220,10 +1086,10 @@ static int qcom_nandc_block_markbad(struct nand_chip *chip, loff_t ofs)
set_address(host, host->cw_size * (ecc->steps - 1), page);
update_rw_regs(host, 1, false, ecc->steps - 1);
config_nand_page_write(chip);
qcom_nandc_config_page_write(nandc);
qcom_write_data_dma(nandc, FLASH_BUF_ACC,
nandc->data_buffer, host->cw_size, 0);
config_nand_cw_write(chip);
qcom_nandc_config_cw_write(nandc);
ret = qcom_submit_descs(nandc);
if (ret) {
@ -1921,9 +1787,9 @@ static int qcom_param_page_type_exec(struct nand_chip *chip, const struct nand_
len = nand_subop_get_data_len(subop, op_id);
if (nandc->props->qpic_version2)
nandc_set_read_loc_last(chip, reg_base, 0, len, 1);
qcom_nandc_set_read_loc_last(nandc, reg_base, 0, len, 1);
else
nandc_set_read_loc_first(chip, reg_base, 0, len, 1);
qcom_nandc_set_read_loc_first(nandc, reg_base, 0, len, 1);
if (!nandc->props->has_onfi_read_op) {
qcom_write_reg_dma(nandc, &nandc->regs->vld, NAND_DEV_CMD_VLD, 1, 0);

View File

@ -378,6 +378,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
int len, enum dma_data_direction dir)
{
struct dma_async_tx_descriptor *desc = NULL;
struct device *dma_dev;
struct dma_chan *chan;
enum dma_transfer_direction tr_dir;
dma_addr_t dma_addr;
@ -394,9 +395,10 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
tr_dir = DMA_MEM_TO_DEV;
}
dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
dma_dev = dmaengine_get_dma_device(chan);
dma_addr = dma_map_single(dma_dev, buf, len, dir);
if (!dma_mapping_error(chan->device->dev, dma_addr))
if (!dma_mapping_error(dma_dev, dma_addr))
desc = dmaengine_prep_slave_single(chan, dma_addr, len,
tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@ -440,7 +442,7 @@ static int flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
reg &= ~DREQ0EN;
writel(reg, FLINTDMACR(flctl));
dma_unmap_single(chan->device->dev, dma_addr, len, dir);
dma_unmap_single(dma_dev, dma_addr, len, dir);
/* ret == 0 is success */
return ret;

View File

@ -2076,7 +2076,7 @@ static void stm32_fmc2_nfc_remove(struct platform_device *pdev)
stm32_fmc2_nfc_wp_enable(nand);
}
static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev)
static int stm32_fmc2_nfc_suspend(struct device *dev)
{
struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev);
struct stm32_fmc2_nand *nand = &nfc->nand;
@ -2090,7 +2090,7 @@ static int __maybe_unused stm32_fmc2_nfc_suspend(struct device *dev)
return 0;
}
static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
static int stm32_fmc2_nfc_resume(struct device *dev)
{
struct stm32_fmc2_nfc *nfc = dev_get_drvdata(dev);
struct stm32_fmc2_nand *nand = &nfc->nand;
@ -2118,7 +2118,7 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend,
static DEFINE_SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend,
stm32_fmc2_nfc_resume);
static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp1_data = {
@ -2153,7 +2153,7 @@ static struct platform_driver stm32_fmc2_nfc_driver = {
.driver = {
.name = "stm32_fmc2_nfc",
.of_match_table = stm32_fmc2_nfc_match,
.pm = &stm32_fmc2_nfc_pm_ops,
.pm = pm_sleep_ptr(&stm32_fmc2_nfc_pm_ops),
},
};
module_platform_driver(stm32_fmc2_nfc_driver);

View File

@ -222,6 +222,7 @@
* USER_DATA_LEN registers.
*/
#define USER_DATA_SZ 4
#define SUNXI_NFC_MAX_USER_DATA_SZ 32
/**
* struct sunxi_nand_chip_sel - stores information related to NAND Chip Select
@ -1003,11 +1004,10 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller);
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step);
u8 *user_data = NULL;
u8 user_data[SUNXI_NFC_MAX_USER_DATA_SZ] = {};
/* Randomize the Bad Block Marker. */
if (bbm && (nand->options & NAND_NEED_SCRAMBLING)) {
user_data = kmalloc(user_data_sz, GFP_KERNEL);
memcpy(user_data, oob, user_data_sz);
sunxi_nfc_randomize_bbm(nand, page, user_data);
oob = user_data;
@ -1040,7 +1040,6 @@ static void sunxi_nfc_hw_ecc_set_prot_oob_bytes(struct nand_chip *nand,
}
}
kfree(user_data);
}
static void sunxi_nfc_hw_ecc_update_stats(struct nand_chip *nand,
@ -1991,7 +1990,7 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
struct nand_chip *nand = mtd_to_nand(mtd);
struct nand_ecc_ctrl *ecc = &nand->ecc;
struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section);
unsigned int user_data_sz;
/*
* The controller does not provide access to OOB bytes
@ -2000,6 +1999,8 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
if (section >= ecc->steps)
return -ERANGE;
user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, section);
/*
* The first 2 bytes are used for BB markers, hence we
* only have user_data_sz - 2 bytes available in the first user data
@ -2007,12 +2008,12 @@ static int sunxi_nand_ooblayout_free(struct mtd_info *mtd, int section,
*/
if (section == 0) {
oobregion->offset = 2;
oobregion->length = user_data_sz - 2;
oobregion->length = user_data_sz > 2 ? user_data_sz - 2 : 0;
return 0;
}
oobregion->offset = sunxi_get_ecc_offset(sunxi_nand, ecc, section);
oobregion->offset = sunxi_get_oob_offset(sunxi_nand, ecc, section);
oobregion->length = user_data_sz;
return 0;
@ -2041,6 +2042,9 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize
int remaining_bytes = oobsize - (ecc_bytes * nsectors);
int i, step;
if (nsectors <= 0)
return -EINVAL;
sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors,
GFP_KERNEL);
if (!sunxi_nand->user_data_bytes)
@ -2056,6 +2060,8 @@ static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize
if (sunxi_nand->user_data_bytes[step] == 0)
break;
}
if (sunxi_nand->user_data_bytes[0] < USER_DATA_SZ)
return -EINVAL;
return 0;
}
@ -2072,11 +2078,15 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
int total_user_data_sz = 0;
int nsectors;
int ecc_mode;
int ret;
int i;
if (nanddev->ecc.user_conf.flags & NAND_ECC_MAXIMIZE_STRENGTH) {
int bytes = mtd->oobsize;
if (mtd->writesize < 1024)
return -EINVAL;
ecc->size = 1024;
nsectors = mtd->writesize / ecc->size;
@ -2102,10 +2112,10 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
bytes -= total_user_data_sz;
} else {
/*
* remove at least the BBM size before computing the
* max ECC
* User-data lengths are encoded in four-byte units. Reserve
* the first word because it contains the two BBM bytes.
*/
bytes -= 2;
bytes -= USER_DATA_SZ;
}
/*
@ -2169,9 +2179,12 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct nand_chip *nand,
* The rationale for variable data length is to prioritize maximum ECC
* strength, and then use the remaining space for user data.
*/
if (nfc->caps->reg_user_data_len)
sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes,
nsectors);
if (nfc->caps->reg_user_data_len) {
ret = sunxi_nfc_maximize_user_data(nand, mtd->oobsize,
ecc->bytes, nsectors);
if (ret)
return ret;
}
if (total_user_data_sz == 0)
for (i = 0; i < nsectors; i++)

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
spinand-objs := core.o otp.o
spinand-objs += alliancememory.o ato.o dosilicon.o esmt.o fmsh.o foresee.o gigadevice.o heyangtek.o
spinand-objs += macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o
spinand-objs += issi.o macronix.o micron.o paragon.o skyhigh.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o

View File

@ -1376,6 +1376,7 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = {
&foresee_spinand_manufacturer,
&gigadevice_spinand_manufacturer,
&heyangtek_spinand_manufacturer,
&issi_spinand_manufacturer,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
&paragon_spinand_manufacturer,

View File

@ -35,8 +35,8 @@ static SPINAND_OP_VARIANTS(update_cache_variants,
/*
* HYF1GQ4UDACAE is a GD5F1GQ4-compatible die, so the OOB layout is taken
* from gd5fxgq4xa: the on-die ECC parity occupies bytes 8..15 of each
* 16-byte section, the bad block marker sits in byte 0 and the remaining
* bytes are exposed as free.
* 16-byte section, and the remaining bytes are exposed as free, except the
* two bytes that spinand_isbad() reads as the bad block marker.
*/
static int hyf1gq4_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
@ -60,9 +60,9 @@ static int hyf1gq4_ooblayout_free(struct mtd_info *mtd, int section,
region->offset = 16 * section;
region->length = 8;
} else {
/* section 0 has one byte reserved for the bad block marker */
region->offset = 1;
region->length = 7;
/* section 0 has two bytes reserved for the bad block marker */
region->offset = 2;
region->length = 6;
}
return 0;

187
drivers/mtd/nand/spi/issi.c Normal file
View File

@ -0,0 +1,187 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2026 ISSI
*
* Authors:
* Bill Lee <blee@issi.com>
* Jeff Kim <jekim@issi.com>
* Co-Author:
* Han Xu <han.xu@nxp.com>
*/
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/mtd/spinand.h>
#define SPINAND_MFR_ISSI 0x9d
#define ISSI_STATUS_ECC_MASK GENMASK(6, 4)
#define ISSI_STATUS_ECC_NO_BITFLIPS 0
#define ISSI_STATUS_ECC_1TO3_BITFLIPS 1
#define ISSI_STATUS_ECC_UNCOR_ERROR 2
#define ISSI_STATUS_ECC_4TO6_BITFLIPS 3
#define ISSI_STATUS_ECC_7TO8_BITFLIPS 5
/*
* As per datasheet, die selection is done by the 7th bit of Drive
* Strength Register (Address 0xD0).
*/
#define ISSI_DIE_SELECT_REG 0xD0
#define ISSI_SELECT_DIE_MASK BIT(7)
#define ISSI_SELECT_DIE(x) ((x) << 7)
static SPINAND_OP_VARIANTS(
quadio_read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
static SPINAND_OP_VARIANTS(x4_write_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
static SPINAND_OP_VARIANTS(x4_update_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(false, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(false, 0, NULL, 0));
static int issi_8_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
region->offset = mtd->oobsize / 2;
region->length = mtd->oobsize / 2;
return 0;
}
static int issi_8_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section)
return -ERANGE;
/* Reserve 2 bytes for the BBM. */
region->offset = 2;
region->length = (mtd->oobsize / 2) - 2;
return 0;
}
static const struct mtd_ooblayout_ops issi_8_ooblayout = {
.ecc = issi_8_ooblayout_ecc,
.free = issi_8_ooblayout_free,
};
static int issi_select_target(struct spinand_device *spinand,
unsigned int target)
{
int ret;
u8 regval;
if (target > 1)
return -EINVAL;
ret = spinand_read_reg_op(spinand, ISSI_DIE_SELECT_REG, &regval);
if (ret)
return ret;
regval &= ~ISSI_SELECT_DIE_MASK;
regval |= ISSI_SELECT_DIE(target);
return spinand_write_reg_op(spinand, ISSI_DIE_SELECT_REG, regval);
}
static int issi_8_ecc_get_status(struct spinand_device *spinand, u8 status)
{
switch (FIELD_GET(ISSI_STATUS_ECC_MASK, status)) {
case ISSI_STATUS_ECC_NO_BITFLIPS:
return 0;
case ISSI_STATUS_ECC_1TO3_BITFLIPS:
return 3;
case ISSI_STATUS_ECC_4TO6_BITFLIPS:
return 6;
case ISSI_STATUS_ECC_7TO8_BITFLIPS:
return 8;
case ISSI_STATUS_ECC_UNCOR_ERROR:
return -EBADMSG;
default:
break;
}
return -EINVAL;
}
static const struct spinand_info issi_spinand_table[] = {
/* IS37/38SMW01G8B 1Gb 1.8V */
SPINAND_INFO("IS37/38SMW01G8B",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x15),
NAND_MEMORG(1, 2048, 128, 64, 1024, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
&x4_write_cache_variants,
&x4_update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
SPINAND_SELECT_TARGET(issi_select_target)),
/* IS37/38SMW02G8B 2Gb 1.8V */
SPINAND_INFO("IS37/38SMW02G8B",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x25),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
&x4_write_cache_variants,
&x4_update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
SPINAND_SELECT_TARGET(issi_select_target)),
/* IS37/38SML04G8B 4Gb 3.3V */
SPINAND_INFO("IS37/38SML04G8",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x34),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
&x4_write_cache_variants,
&x4_update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
SPINAND_SELECT_TARGET(issi_select_target)),
/* IS37/38SMW04G8B 4Gb 1.8V */
SPINAND_INFO("IS37/38SMW04G8B",
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x35),
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 2),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&quadio_read_cache_variants,
&x4_write_cache_variants,
&x4_update_cache_variants),
SPINAND_HAS_QE_BIT,
SPINAND_ECCINFO(&issi_8_ooblayout, issi_8_ecc_get_status),
SPINAND_SELECT_TARGET(issi_select_target)),
};
static int issi_spinand_init(struct spinand_device *spinand)
{
return 0;
}
static const struct spinand_manufacturer_ops issi_spinand_manuf_ops = {
.init = issi_spinand_init,
};
const struct spinand_manufacturer issi_spinand_manufacturer = {
.id = SPINAND_MFR_ISSI,
.name = "ISSI",
.chips = issi_spinand_table,
.nchips = ARRAY_SIZE(issi_spinand_table),
.ops = &issi_spinand_manuf_ops,
};

View File

@ -211,6 +211,13 @@ static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));
static SPINAND_OP_VARIANTS(cont_read_cache_variants,
WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 0),
WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0),
WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 0),
WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0),
WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0));
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
@ -314,10 +321,18 @@ static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section,
static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
int num_sections = 4;
int offset = 64;
if (mtd->oobsize == 256) {
num_sections = 8;
offset = 128;
}
if (section >= num_sections)
return -ERANGE;
region->offset = 64 + (16 * section);
region->offset = offset + (16 * section);
region->length = 13;
return 0;
@ -326,9 +341,21 @@ static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 3)
int num_sections = 4;
if (mtd->oobsize == 256)
num_sections = 8;
if (section >= num_sections)
return -ERANGE;
/*
* For at least W25N04KW and W25N04LW, this region is actually
* split in two:
* ECC-protected "User Data I" at + 4 offset, length 12
* unprotected "User Data II" at + 2 offset, length 2
* This returns the full region for backwards compatibility.
*/
region->offset = (16 * section) + 2;
region->length = 14;
@ -345,6 +372,39 @@ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
.free = w25n02kv_ooblayout_free,
};
static int w25n08lw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
/*
* With ecc enabled the parity bits are not accessible. So we can
* only see page + 128. Without ecc the full page + 256 is accessible.
* To make it simple just return the area as not accessible.
*/
return -ERANGE;
}
static int w25n08lw_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
if (section > 7)
return -ERANGE;
/*
* Only expose the ECC protected "User Data I" bytes. The "User Data II"
* bytes at offset + 2 are not covered by the on-die ECC engine, and the
* first two bytes of the section hold the BBM.
*/
region->offset = (16 * section) + 4;
region->length = 12;
return 0;
}
static const struct mtd_ooblayout_ops w25n08lw_ooblayout = {
.ecc = w25n08lw_ooblayout_ecc,
.free = w25n08lw_ooblayout_free,
};
static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@ -768,6 +828,15 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
SPINAND_INFO("W25N04LW", /* 1.8V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb2, 0x23),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
&write_cache_variants,
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
SPINAND_INFO("W35N04JW", /* 1.8V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1),
@ -781,6 +850,18 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
/* 8G-bit densities */
SPINAND_INFO("W25N08LW", /* 2x4G-bit 1.8V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb3, 0x24),
NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 2, 1),
NAND_ECCREQ(8, 512),
SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_variants,
&write_cache_variants,
&update_cache_variants,
&cont_read_cache_variants),
0,
SPINAND_ECCINFO(&w25n08lw_ooblayout, w25n02kv_ecc_get_status),
SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
};
static int winbond_spinand_init(struct spinand_device *spinand)

View File

@ -143,9 +143,9 @@ static int parse_qcomsmem_part(struct mtd_info *mtd,
*c = tolower(*c);
parts[j].name = name;
parts[j].offset = le32_to_cpu(pentry->offset) * mtd->erasesize;
parts[j].offset = (u64)le32_to_cpu(pentry->offset) * mtd->erasesize;
parts[j].mask_flags = pentry->attr;
parts[j].size = le32_to_cpu(pentry->length) * mtd->erasesize;
parts[j].size = (u64)le32_to_cpu(pentry->length) * mtd->erasesize;
pr_debug("%d: %s offs=0x%08x size=0x%08x attr:0x%08x\n",
i, pentry->name, le32_to_cpu(pentry->offset),
le32_to_cpu(pentry->length), pentry->attr);

View File

@ -23,18 +23,28 @@ static int at25fs_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len)
static int at25fs_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
{
/* Write 0x00 to the status register to disable write protection */
u8 sr = 0;
int ret;
/* We only support unlocking the whole flash array */
if (ofs || len != nor->params->size)
return -EINVAL;
/* Write 0x00 to the status register to disable write protection */
ret = spi_nor_write_sr_and_check(nor, 0);
ret = spi_nor_write_sr1(nor, &sr);
if (ret)
dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n");
return ret;
return ret;
ret = spi_nor_read_sr1(nor, &sr);
if (ret)
return ret;
if (sr) {
dev_dbg(nor->dev, "unable to clear BP bits, WP# asserted?\n");
return -EIO;
}
return 0;
}
static int at25fs_nor_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
@ -50,6 +60,7 @@ static const struct spi_nor_locking_ops at25fs_nor_locking_ops = {
static int at25fs_nor_late_init(struct spi_nor *nor)
{
nor->params->opcodes.write_sr1 = SPINOR_OP_WRSR;
nor->params->locking_ops = &at25fs_nor_locking_ops;
return 0;
@ -69,6 +80,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,
u64 len, bool is_protect)
{
int ret;
@ -78,19 +90,24 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
if (ofs || len != nor->params->size)
return -EINVAL;
ret = spi_nor_read_sr(nor, nor->bouncebuf);
ret = spi_nor_read_sr1(nor, &sr);
if (ret)
return ret;
sr = nor->bouncebuf[0];
/* SRWD bit needs to be cleared, otherwise the protection doesn't change */
if (sr & SR_SRWD) {
sr &= ~SR_SRWD;
ret = spi_nor_write_sr_and_check(nor, sr);
if (ret) {
dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n");
ret = spi_nor_write_sr1(nor, &sr);
if (ret)
return ret;
ret = spi_nor_read_sr1(nor, &sr);
if (ret)
return ret;
if (sr & SR_SRWD) {
dev_dbg(nor->dev, "unable to clear SRWD bit, WP# asserted?\n");
return -EIO;
}
}
@ -108,14 +125,7 @@ static int atmel_nor_set_global_protection(struct spi_nor *nor, loff_t ofs,
sr &= ~ATMEL_SR_GLOBAL_PROTECT_MASK;
}
nor->bouncebuf[0] = sr;
/*
* We cannot use the spi_nor_write_sr_and_check() because this command
* isn't really setting any bits, instead it is an pseudo command for
* "Global Unprotect" or "Global Protect"
*/
return spi_nor_write_sr(nor, nor->bouncebuf, 1);
return spi_nor_write_sr1(nor, &sr);
}
static int atmel_nor_global_protect(struct spi_nor *nor, loff_t ofs, u64 len)
@ -131,16 +141,17 @@ static int atmel_nor_global_unprotect(struct spi_nor *nor, loff_t ofs, u64 len)
static int atmel_nor_is_global_protected(struct spi_nor *nor, loff_t ofs,
u64 len)
{
u8 sr;
int ret;
if (ofs >= nor->params->size || (ofs + len) > nor->params->size)
return -EINVAL;
ret = spi_nor_read_sr(nor, nor->bouncebuf);
ret = spi_nor_read_sr1(nor, &sr);
if (ret)
return ret;
return ((nor->bouncebuf[0] & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
return ((sr & ATMEL_SR_GLOBAL_PROTECT_MASK) == ATMEL_SR_GLOBAL_PROTECT_MASK);
}
static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = {
@ -151,6 +162,7 @@ static const struct spi_nor_locking_ops atmel_nor_global_protection_ops = {
static int atmel_nor_global_protection_late_init(struct spi_nor *nor)
{
nor->params->opcodes.write_sr1 = SPINOR_OP_WRSR;
nor->params->locking_ops = &atmel_nor_global_protection_ops;
return 0;
@ -182,47 +194,40 @@ static const struct flash_info atmel_nor_parts[] = {
.size = SZ_512K,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.no_sfdp_flags = SECT_4K,
.fixups = &atmel_nor_global_protection_fixups,
}, {
.id = SNOR_ID(0x1f, 0x45, 0x01),
.name = "at26df081a",
.size = SZ_1M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.no_sfdp_flags = SECT_4K,
.fixups = &atmel_nor_global_protection_fixups
}, {
.id = SNOR_ID(0x1f, 0x46, 0x01),
.name = "at26df161a",
.size = SZ_2M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.no_sfdp_flags = SECT_4K,
.fixups = &atmel_nor_global_protection_fixups
}, {
.id = SNOR_ID(0x1f, 0x47, 0x00),
.name = "at25df321",
.size = SZ_4M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.no_sfdp_flags = SECT_4K,
.fixups = &atmel_nor_global_protection_fixups
}, {
.id = SNOR_ID(0x1f, 0x47, 0x01),
.name = "at25df321a",
.size = SZ_4M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.no_sfdp_flags = SECT_4K,
.fixups = &atmel_nor_global_protection_fixups
}, {
.id = SNOR_ID(0x1f, 0x47, 0x08),
.name = "at25ff321a",
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.fixups = &atmel_nor_global_protection_fixups
}, {
.id = SNOR_ID(0x1f, 0x48, 0x00),
.name = "at25df641",
.size = SZ_8M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.no_sfdp_flags = SECT_4K,
.fixups = &atmel_nor_global_protection_fixups
}, {
.id = SNOR_ID(0x1f, 0x66, 0x01),
.name = "at25fs010",
@ -230,14 +235,12 @@ static const struct flash_info atmel_nor_parts[] = {
.size = SZ_128K,
.flags = SPI_NOR_HAS_LOCK,
.no_sfdp_flags = SECT_4K,
.fixups = &at25fs_nor_fixups
}, {
.id = SNOR_ID(0x1f, 0x66, 0x04),
.name = "at25fs040",
.size = SZ_512K,
.flags = SPI_NOR_HAS_LOCK,
.no_sfdp_flags = SECT_4K,
.fixups = &at25fs_nor_fixups
}, {
.id = SNOR_ID(0x1f, 0x87, 0x01),
.size = SZ_4M,
@ -245,8 +248,22 @@ static const struct flash_info atmel_nor_parts[] = {
},
};
static const struct spi_nor_fixup atmel_fixups[] = {
{ .id = SNOR_ID(0x1f, 0x44, 0x01), .fixups = &atmel_nor_global_protection_fixups },
{ .id = SNOR_ID(0x1f, 0x45, 0x01), .fixups = &atmel_nor_global_protection_fixups },
{ .id = SNOR_ID(0x1f, 0x46, 0x01), .fixups = &atmel_nor_global_protection_fixups },
{ .id = SNOR_ID(0x1f, 0x47, 0x00), .fixups = &atmel_nor_global_protection_fixups },
{ .id = SNOR_ID(0x1f, 0x47, 0x01), .fixups = &atmel_nor_global_protection_fixups },
{ .id = SNOR_ID(0x1f, 0x47, 0x08), .fixups = &atmel_nor_global_protection_fixups },
{ .id = SNOR_ID(0x1f, 0x48, 0x00), .fixups = &atmel_nor_global_protection_fixups },
{ .id = SNOR_ID(0x1f, 0x66, 0x01), .fixups = &at25fs_nor_fixups },
{ .id = SNOR_ID(0x1f, 0x66, 0x04), .fixups = &at25fs_nor_fixups },
};
const struct spi_nor_manufacturer spi_nor_atmel = {
.name = "atmel",
.parts = atmel_nor_parts,
.nparts = ARRAY_SIZE(atmel_nor_parts),
.fixups = atmel_fixups,
.nfixups = ARRAY_SIZE(atmel_fixups),
};

File diff suppressed because it is too large Load Diff

View File

@ -37,36 +37,18 @@
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPI_NOR_RDSR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR, 0), \
#define SPI_NOR_RDSR_OP(opcode, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, buf, 0))
SPI_MEM_OP_DATA_IN(len, buf, 0))
#define SPI_NOR_WRSR_OP(buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR, 0), \
#define SPI_NOR_WRSR_OP(opcode, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 0))
#define SPI_NOR_RDSR2_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDSR2, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 0))
#define SPI_NOR_WRSR2_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_WRSR2, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 0))
#define SPI_NOR_RDCR_OP(buf) \
SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_RDCR, 0), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, buf, 0))
#define SPI_NOR_EN4B_EX4B_OP(enable) \
SPI_MEM_OP(SPI_MEM_OP_CMD(enable ? SPINOR_OP_EN4B : SPINOR_OP_EX4B, 0), \
SPI_MEM_OP_NO_ADDR, \
@ -121,16 +103,16 @@
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DATA)
/* Keep these in sync with the list in debugfs.c */
/*
* This could contain holes, if adding a new flag use the first free spot.
* Keep the flags in sync with the list in debugfs.c
*/
enum spi_nor_option_flags {
SNOR_F_HAS_SR_TB = BIT(0),
SNOR_F_NO_OP_CHIP_ERASE = BIT(1),
SNOR_F_BROKEN_RESET = BIT(2),
SNOR_F_4B_OPCODES = BIT(3),
SNOR_F_HAS_4BAIT = BIT(4),
SNOR_F_HAS_LOCK = BIT(5),
SNOR_F_HAS_16BIT_SR = BIT(6),
SNOR_F_NO_READ_CR = BIT(7),
SNOR_F_HAS_SR_TB_BIT6 = BIT(8),
SNOR_F_HAS_4BIT_BP = BIT(9),
SNOR_F_HAS_SR_BP3_BIT6 = BIT(10),
@ -340,6 +322,26 @@ struct spi_nor_otp {
const struct spi_nor_otp_ops *ops;
};
/**
* struct spi_nor_opcodes - SPI NOR flash specific opcodes.
* List of variable opcodes used by the chip.
*
* @die_erase: opcode for erasing a die, defaults to SPINOR_OP_CHIP_ERASE
* @read_sr1: opcode for reading SR1 alone
* @read_sr2: opcode for reading SR2 alone
* @write_sr1: opcode for writing SR1 alone
* @write_sr2: opcode for writing SR2 alone
* @write_sr1_and_sr2: opcode for writing SR1 then SR2 in one operation
*/
struct spi_nor_opcodes {
u8 die_erase;
u8 read_sr1;
u8 read_sr2;
u8 write_sr1;
u8 write_sr2;
u8 write_sr1_and_sr2;
};
/**
* struct spi_nor_flash_parameter - SPI NOR flash parameters and settings.
* Includes legacy flash parameters and settings that can be overwritten
@ -348,6 +350,7 @@ struct spi_nor_otp {
*
* @bank_size: the flash memory bank density in bytes.
* @size: the total flash memory density in bytes.
* @flags: flag options for the current SPI NOR (SNOR_F_*)
* @writesize Minimal writable flash unit size. Defaults to 1. Set to
* ECC unit size for ECC-ed flashes.
* @page_size: the page size of the SPI NOR flash memory.
@ -362,7 +365,6 @@ 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.
@ -370,11 +372,13 @@ struct spi_nor_otp {
* in the array, the higher priority.
* @page_programs: page program capabilities ordered by priority: the
* higher index in the array, the higher priority.
* @cmd_ext_type: the command opcode extension type for DTR mode.
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
* Table.
* @otp: SPI NOR OTP info.
* @set_octal_dtr: enables or disables SPI NOR octal DTR mode.
* @quad_enable: enables SPI NOR quad mode.
* @qe_mask: two bytes mask used to set/clear the QE bit
* @set_4byte_addr_mode: puts the SPI NOR in 4 byte addressing mode.
* @ready: (optional) flashes might use a different mechanism
* than reading the status register to indicate they
@ -385,6 +389,7 @@ struct spi_nor_otp {
struct spi_nor_flash_parameter {
u64 bank_size;
u64 size;
u32 flags;
u32 writesize;
u32 page_size;
u8 addr_nbytes;
@ -393,18 +398,20 @@ 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;
struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX];
enum spi_nor_cmd_ext cmd_ext_type;
struct spi_nor_opcodes opcodes;
struct spi_nor_erase_map erase_map;
struct spi_nor_otp otp;
int (*set_octal_dtr)(struct spi_nor *nor, bool enable);
int (*quad_enable)(struct spi_nor *nor);
u8 qe_mask[2];
int (*set_4byte_addr_mode)(struct spi_nor *nor, bool enable);
int (*ready)(struct spi_nor *nor);
@ -445,6 +452,33 @@ struct spi_nor_fixups {
int (*late_init)(struct spi_nor *nor);
};
/**
* struct spi_nor_fixup - SPI NOR fixup registration.
* @id: (optional) flash ID this fixup applies to, may only match the
* ID prefix, eg. just the first few bytes to match a whole family
* @match: (optional) custom match function (can be used together with @id)
* @fixup_flags: flags that indicate support that can be discovered via SFDP
* ideally, but can not be discovered for this particular flash
* because the SFDP table that indicates this support is not
* defined by the flash. In case the table for this support is
* defined but has wrong values, one should instead use a
* post_sfdp() hook to set the SNOR_F equivalent flag.
*
* SPI_NOR_4B_OPCODES: use dedicated 4byte address op codes to support
* memory size above 128Mib.
* SPI_NOR_IO_MODE_EN_VOLATILE: flash enables the best available I/O mode
* via a volatile bit.
* @fixups: the fixup hooks to apply when this entry matches
*/
struct spi_nor_fixup {
const struct spi_nor_id *id;
bool (*match)(const struct spi_nor *nor);
u8 fixup_flags;
#define SPI_NOR_4B_OPCODES BIT(0)
#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(1)
const struct spi_nor_fixups *fixups;
};
/**
* struct spi_nor_id - SPI NOR flash ID.
*
@ -496,9 +530,7 @@ struct spi_nor_id {
* be used with SPI_NOR_HAS_LOCK.
*
* @no_sfdp_flags: flags that indicate support that can be discovered via SFDP.
* Used when SFDP tables are not defined in the flash. These
* flags are used together with the SPI_NOR_SKIP_SFDP flag.
* SPI_NOR_SKIP_SFDP: skip parsing of SFDP tables.
* Used when SFDP tables are not defined in the flash.
* SECT_4K: SPINOR_OP_BE_4K works uniformly.
* SPI_NOR_DUAL_READ: flash supports Dual Read.
* SPI_NOR_QUAD_READ: flash supports Quad Read.
@ -506,22 +538,10 @@ struct spi_nor_id {
* SPI_NOR_OCTAL_DTR_READ: flash supports octal DTR Read.
* SPI_NOR_OCTAL_DTR_PP: flash supports Octal DTR Page Program.
*
* @fixup_flags: flags that indicate support that can be discovered via SFDP
* ideally, but can not be discovered for this particular flash
* because the SFDP table that indicates this support is not
* defined by the flash. In case the table for this support is
* defined but has wrong values, one should instead use a
* post_sfdp() hook to set the SNOR_F equivalent flag.
*
* SPI_NOR_4B_OPCODES: use dedicated 4byte address op codes to support
* memory size above 128Mib.
* SPI_NOR_IO_MODE_EN_VOLATILE: flash enables the best available I/O mode
* via a volatile bit.
* @mfr_flags: manufacturer private flags. Used in the manufacturer fixup
* hooks to differentiate support between flashes of the same
* manufacturer.
* @otp_org: flash's OTP organization.
* @fixups: part specific fixup hooks.
*/
struct flash_info {
char *name;
@ -545,7 +565,6 @@ struct flash_info {
#define SPI_NOR_HAS_CMP BIT(10)
u8 no_sfdp_flags;
#define SPI_NOR_SKIP_SFDP BIT(0)
#define SECT_4K BIT(1)
#define SPI_NOR_DUAL_READ BIT(3)
#define SPI_NOR_QUAD_READ BIT(4)
@ -553,14 +572,9 @@ struct flash_info {
#define SPI_NOR_OCTAL_DTR_READ BIT(6)
#define SPI_NOR_OCTAL_DTR_PP BIT(7)
u8 fixup_flags;
#define SPI_NOR_4B_OPCODES BIT(0)
#define SPI_NOR_IO_MODE_EN_VOLATILE BIT(1)
u8 mfr_flags;
const struct spi_nor_otp_organization *otp;
const struct spi_nor_fixups *fixups;
};
#define SNOR_ID(...) \
@ -582,13 +596,16 @@ struct flash_info {
* @name: manufacturer name
* @parts: array of parts supported by this manufacturer
* @nparts: number of entries in the parts array
* @fixups: hooks called at various points in time during spi_nor_scan()
* @fixups: list of fixups, each matched by ID (or a custom match function),
* applied to any part of this manufacturer.
* @nfixups: number of entries in the fixups array
*/
struct spi_nor_manufacturer {
const char *name;
const struct flash_info *parts;
unsigned int nparts;
const struct spi_nor_fixups *fixups;
const struct spi_nor_fixup *fixups;
unsigned int nfixups;
};
/**
@ -619,6 +636,8 @@ extern const struct spi_nor_manufacturer spi_nor_xmc;
extern const struct attribute_group *spi_nor_sysfs_groups[];
bool spi_nor_fixup_match(const struct spi_nor *nor,
const struct spi_nor_fixup *fixup);
void spi_nor_spimem_setup_op(const struct spi_nor *nor,
struct spi_mem_op *op,
const enum spi_nor_protocol proto);
@ -633,18 +652,17 @@ int spi_nor_wait_till_ready(struct spi_nor *nor);
int spi_nor_global_block_unlock(struct spi_nor *nor);
int spi_nor_prep_and_lock(struct spi_nor *nor);
void spi_nor_unlock_and_unprep(struct spi_nor *nor);
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
int spi_nor_read_id(struct spi_nor *nor, u8 naddr, u8 ndummy, u8 *id,
enum spi_nor_protocol reg_proto);
int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
int spi_nor_sr_ready(struct spi_nor *nor);
int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
int spi_nor_write_16bit_cr_and_check(struct spi_nor *nor, u8 cr);
int spi_nor_write_sr_cr_and_check(struct spi_nor *nor, const u8 *regs);
int spi_nor_read_sr_ll(struct spi_nor *nor, u8 opcode, u8 *sr,
unsigned int len);
int spi_nor_read_sr1(struct spi_nor *nor, u8 *sr1);
int spi_nor_read_sr2(struct spi_nor *nor, u8 *sr2);
int spi_nor_read_sr1_and_sr2(struct spi_nor *nor, u8 *sr);
int spi_nor_write_sr1(struct spi_nor *nor, const u8 *sr1);
int spi_nor_write_sr2(struct spi_nor *nor, const u8 *sr2);
int spi_nor_write_sr1_and_sr2_and_check(struct spi_nor *nor, const u8 *sr);
ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
u8 *buf);
@ -680,10 +698,6 @@ void spi_nor_mask_erase_type(struct spi_nor_erase_type *erase);
void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
u8 erase_mask, u64 flash_size);
int spi_nor_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt);
void spi_nor_init_default_locking_ops(struct spi_nor *nor);
bool spi_nor_has_default_locking_ops(struct spi_nor *nor);
void spi_nor_try_unlock_all(struct spi_nor *nor);

View File

@ -14,13 +14,10 @@
#define SNOR_F_NAME(name) [ilog2(SNOR_F_##name)] = #name
static const char *const snor_f_names[] = {
SNOR_F_NAME(HAS_SR_TB),
SNOR_F_NAME(NO_OP_CHIP_ERASE),
SNOR_F_NAME(BROKEN_RESET),
SNOR_F_NAME(4B_OPCODES),
SNOR_F_NAME(HAS_4BAIT),
SNOR_F_NAME(HAS_LOCK),
SNOR_F_NAME(HAS_16BIT_SR),
SNOR_F_NAME(NO_READ_CR),
SNOR_F_NAME(HAS_SR_TB_BIT6),
SNOR_F_NAME(HAS_4BIT_BP),
SNOR_F_NAME(HAS_SR_BP3_BIT6),
@ -84,13 +81,12 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
struct spi_nor_flash_parameter *params = nor->params;
struct spi_nor_erase_map *erase_map = &params->erase_map;
struct spi_nor_erase_region *region = erase_map->regions;
const struct flash_info *info = nor->info;
char buf[16], *str;
loff_t lock_start;
u64 lock_length;
unsigned int i;
seq_printf(s, "name\t\t%s\n", info->name);
seq_printf(s, "name\t\t%s\n", nor->partname);
seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id);
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
seq_printf(s, "size\t\t%s\n", buf);
@ -99,7 +95,7 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
seq_printf(s, "address nbytes\t%u\n", nor->addr_nbytes);
seq_puts(s, "flags\t\t");
spi_nor_print_flags(s, nor->flags, snor_f_names,
spi_nor_print_flags(s, params->flags, snor_f_names,
ARRAY_SIZE(snor_f_names));
seq_puts(s, "\n");
@ -108,8 +104,23 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
seq_printf(s, " dummy cycles\t%u\n", nor->read_dummy);
seq_printf(s, " erase\t\t0x%02x\n", nor->erase_opcode);
seq_printf(s, " program\t0x%02x\n", nor->program_opcode);
seq_printf(s, " SR1 read\t0x%02x\n", params->opcodes.read_sr1);
if (params->opcodes.read_sr2)
seq_printf(s, " SR2 read\t0x%02x\n", params->opcodes.read_sr2);
if (params->opcodes.write_sr1)
seq_printf(s, " SR1 write\t0x%02x\n", params->opcodes.write_sr1);
if (params->opcodes.write_sr2)
seq_printf(s, " SR2 write\t0x%02x\n", params->opcodes.write_sr2);
if (params->opcodes.write_sr1_and_sr2)
seq_printf(s, " SR1+SR2 write\t0x%02x\n",
params->opcodes.write_sr1_and_sr2);
switch (nor->cmd_ext_type) {
if (params->qe_mask[0] || params->qe_mask[1])
seq_printf(s, " QE\t\tSR%d bit %d\n",
params->qe_mask[0] ? 1 : 2,
ffs(params->qe_mask[0] | params->qe_mask[1]) - 1);
switch (params->cmd_ext_type) {
case SPI_NOR_EXT_NONE:
str = "none";
break;
@ -144,10 +155,8 @@ 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", params->die_erase_opcode, buf);
}
string_get_size(params->size, 1, STRING_UNITS_2, buf, sizeof(buf));
seq_printf(s, " %02x (%s)\n", params->opcodes.die_erase, buf);
seq_puts(s, "\nsector map\n");
seq_puts(s, " region (in hex) | erase mask | overlaid\n");

View File

@ -44,9 +44,14 @@ static const struct spi_nor_fixups everspin_nor_fixups = {
.default_init = everspin_nor_default_init,
};
static const struct spi_nor_fixup everspin_fixups[] = {
{ .fixups = &everspin_nor_fixups },
};
const struct spi_nor_manufacturer spi_nor_everspin = {
.name = "everspin",
.parts = everspin_nor_parts,
.nparts = ARRAY_SIZE(everspin_nor_parts),
.fixups = &everspin_nor_fixups,
.fixups = everspin_fixups,
.nfixups = ARRAY_SIZE(everspin_fixups),
};

View File

@ -23,8 +23,10 @@ gd25q256_post_bfpt(struct spi_nor *nor,
* GD25Q256E | SFDP_JESD216_MAJOR | SFDP_JESD216B_MINOR
*/
if (bfpt_header->major == SFDP_JESD216_MAJOR &&
bfpt_header->minor == SFDP_JESD216_MINOR)
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
bfpt_header->minor == SFDP_JESD216_MINOR) {
nor->params->qe_mask[0] = BIT(6);
nor->params->qe_mask[1] = 0;
}
return 0;
}
@ -62,8 +64,6 @@ static const struct flash_info gigadevice_nor_parts[] = {
.id = SNOR_ID(0xc8, 0x40, 0x19),
.name = "gd25q256",
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6,
.fixups = &gd25q256_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES,
}, {
.id = SNOR_ID(0xc8, 0x60, 0x16),
.name = "gd25lq32",
@ -85,8 +85,15 @@ static const struct flash_info gigadevice_nor_parts[] = {
},
};
static const struct spi_nor_fixup gigadevice_fixups[] = {
{ .id = SNOR_ID(0xc8, 0x40, 0x19), .fixups = &gd25q256_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES },
};
const struct spi_nor_manufacturer spi_nor_gigadevice = {
.name = "gigadevice",
.parts = gigadevice_nor_parts,
.nparts = ARRAY_SIZE(gigadevice_nor_parts),
.fixups = gigadevice_fixups,
.nfixups = ARRAY_SIZE(gigadevice_fixups),
};

View File

@ -52,13 +52,11 @@ static const struct flash_info issi_nor_parts[] = {
.sector_size = SZ_32K,
.size = SZ_64K,
.no_sfdp_flags = SECT_4K,
.fixups = &pm25lv_nor_fixups
}, {
.name = "pm25lv010",
.sector_size = SZ_32K,
.size = SZ_128K,
.no_sfdp_flags = SECT_4K,
.fixups = &pm25lv_nor_fixups
}, {
.id = SNOR_ID(0x7f, 0x9d, 0x20),
.name = "is25cd512",
@ -103,8 +101,6 @@ static const struct flash_info issi_nor_parts[] = {
}, {
.id = SNOR_ID(0x9d, 0x60, 0x19),
.name = "is25lp256",
.fixups = &is25lp256_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES,
}, {
.id = SNOR_ID(0x9d, 0x70, 0x16),
.name = "is25wp032",
@ -124,23 +120,40 @@ static const struct flash_info issi_nor_parts[] = {
.id = SNOR_ID(0x9d, 0x70, 0x19),
.name = "is25wp256",
.flags = SPI_NOR_QUAD_PP,
.fixups = &is25lp256_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES,
}
};
static void issi_nor_default_init(struct spi_nor *nor)
{
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
nor->params->qe_mask[0] = BIT(6);
nor->params->qe_mask[1] = 0;
}
static const struct spi_nor_fixups issi_fixups = {
.default_init = issi_nor_default_init,
};
/* PM25LV parts have no JEDEC ID and are likely matched by name */
static bool issi_pm25lv_match(const struct spi_nor *nor)
{
const char *name = nor->info ? nor->info->name : NULL;
return name && strstarts(name, "pm25lv");
}
static const struct spi_nor_fixup issi_fixup_list[] = {
{ .fixups = &issi_fixups },
{ .match = issi_pm25lv_match, .fixups = &pm25lv_nor_fixups },
{ .id = SNOR_ID(0x9d, 0x60, 0x19), .fixups = &is25lp256_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0x9d, 0x70, 0x19), .fixups = &is25lp256_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES },
};
const struct spi_nor_manufacturer spi_nor_issi = {
.name = "issi",
.parts = issi_nor_parts,
.nparts = ARRAY_SIZE(issi_nor_parts),
.fixups = &issi_fixups,
.fixups = issi_fixup_list,
.nfixups = ARRAY_SIZE(issi_fixup_list),
};

View File

@ -40,7 +40,7 @@ mx25l25635_post_bfpt_fixups(struct spi_nor *nor,
* its BFPT table.
*/
if (bfpt->dwords[SFDP_DWORD(5)] & BFPT_DWORD5_FAST_READ_4_4_4)
nor->flags |= SNOR_F_4B_OPCODES;
nor->params->flags |= SNOR_F_4B_OPCODES;
return 0;
}
@ -65,11 +65,12 @@ mx25l3255e_late_init_fixups(struct spi_nor *nor)
/*
* SFDP of MX25L3255E is JESD216, which does not include the Quad
* Enable bit Requirement in BFPT. As a result, during BFPT parsing,
* the quad_enable method is not set to spi_nor_sr1_bit6_quad_enable.
* Therefore, it is necessary to correct this setting by late_init.
* Enable bit Requirement in BFPT. As a result, during BFPT parsing
* the quad_enable mask is reset. Therefore, it is necessary to
* correct this setting by late_init.
*/
params->quad_enable = spi_nor_sr1_bit6_quad_enable;
params->qe_mask[0] = BIT(6);
params->qe_mask[1] = 0;
/*
* In addition, MX25L3255E also supports 1-4-4 page program in 3-byte
@ -83,6 +84,25 @@ mx25l3255e_late_init_fixups(struct spi_nor *nor)
return 0;
}
static int
mx25l12805d_4pp3b_post_sfdp_fixups(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
/*
* JEDEC ID 0xc22018 is shared by MX25L12805D (no SFDP, no 4PP) and
* MX25L12833F/MX25L12845G (support SFDP and 4PP in 3-byte mode).
* The legacy 05D lacks SFDP and will not execute this hook. For
* the newer flashes, 3-byte 1-4-4 PP is not defined in SFDP, so
* we safely enable it here.
*/
params->hwcaps.mask |= SNOR_HWCAPS_PP_1_4_4;
spi_nor_set_pp_settings(&params->page_programs[SNOR_CMD_PP_1_4_4],
SPINOR_OP_PP_1_4_4, SNOR_PROTO_1_4_4);
return 0;
}
static const struct spi_nor_fixups mx25l25635_fixups = {
.post_bfpt = mx25l25635_post_bfpt_fixups,
.post_sfdp = macronix_qpp4b_post_sfdp_fixups,
@ -96,6 +116,10 @@ static const struct spi_nor_fixups mx25l3255e_fixups = {
.late_init = mx25l3255e_late_init_fixups,
};
static const struct spi_nor_fixups mx25l12805d_4pp3b_fixups = {
.post_sfdp = mx25l12805d_4pp3b_post_sfdp_fixups,
};
static const struct flash_info macronix_nor_parts[] = {
{
.id = SNOR_ID(0xc2, 0x20, 0x10),
@ -130,26 +154,23 @@ static const struct flash_info macronix_nor_parts[] = {
.size = SZ_8M,
.no_sfdp_flags = SECT_4K,
}, {
/* MX25L12805D */
/* MX25L12805D, MX25L12833F, MX25L12845G */
.id = SNOR_ID(0xc2, 0x20, 0x18),
.size = SZ_16M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP,
.no_sfdp_flags = SECT_4K,
}, {
/* MX25L25635E, MX25L25645G */
.id = SNOR_ID(0xc2, 0x20, 0x19),
.fixups = &mx25l25635_fixups
}, {
/* MX66L51235F */
/* MX25L51245G, MX25L51273G, MX66L51235F */
.id = SNOR_ID(0xc2, 0x20, 0x1a),
.fixup_flags = SPI_NOR_4B_OPCODES,
.fixups = &macronix_qpp4b_fixups,
}, {
/* MX66L1G45G */
.id = SNOR_ID(0xc2, 0x20, 0x1b),
.fixups = &macronix_qpp4b_fixups,
}, {
/* MX66L2G45G */
.id = SNOR_ID(0xc2, 0x20, 0x1c),
.fixups = &macronix_qpp4b_fixups,
}, {
.id = SNOR_ID(0xc2, 0x23, 0x14),
.name = "mx25v8035f",
@ -186,17 +207,14 @@ static const struct flash_info macronix_nor_parts[] = {
.size = SZ_16M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* MX25U51245G */
/* MX25U51245G, MX25U51293G */
.id = SNOR_ID(0xc2, 0x25, 0x3a),
.fixups = &macronix_qpp4b_fixups,
}, {
/* MX66U1G45G */
/* MX66U1G45G, MX66U1G93G */
.id = SNOR_ID(0xc2, 0x25, 0x3b),
.fixups = &macronix_qpp4b_fixups,
}, {
/* MX66U2G45G */
.id = SNOR_ID(0xc2, 0x25, 0x3c),
.fixups = &macronix_qpp4b_fixups,
}, {
.id = SNOR_ID(0xc2, 0x26, 0x18),
.name = "mx25l12855e",
@ -228,7 +246,6 @@ static const struct flash_info macronix_nor_parts[] = {
}, {
/* MX25L3255E */
.id = SNOR_ID(0xc2, 0x9e, 0x16),
.fixups = &mx25l3255e_fixups,
},
/*
* This spares us of adding new flash entries for flashes that can be
@ -314,7 +331,8 @@ static int macronix_nor_set_octal_dtr(struct spi_nor *nor, bool enable)
static void macronix_nor_default_init(struct spi_nor *nor)
{
nor->params->quad_enable = spi_nor_sr1_bit6_quad_enable;
nor->params->qe_mask[0] = BIT(6);
nor->params->qe_mask[1] = 0;
}
static int macronix_nor_late_init(struct spi_nor *nor)
@ -331,9 +349,24 @@ static const struct spi_nor_fixups macronix_nor_fixups = {
.late_init = macronix_nor_late_init,
};
static const struct spi_nor_fixup macronix_fixups[] = {
{ .fixups = &macronix_nor_fixups },
{ .id = SNOR_ID(0xc2, 0x20, 0x18), .fixups = &mx25l12805d_4pp3b_fixups },
{ .id = SNOR_ID(0xc2, 0x20, 0x19), .fixups = &mx25l25635_fixups },
{ .id = SNOR_ID(0xc2, 0x20, 0x1a), .fixups = &macronix_qpp4b_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0xc2, 0x20, 0x1b), .fixups = &macronix_qpp4b_fixups },
{ .id = SNOR_ID(0xc2, 0x20, 0x1c), .fixups = &macronix_qpp4b_fixups },
{ .id = SNOR_ID(0xc2, 0x25, 0x3a), .fixups = &macronix_qpp4b_fixups },
{ .id = SNOR_ID(0xc2, 0x25, 0x3b), .fixups = &macronix_qpp4b_fixups },
{ .id = SNOR_ID(0xc2, 0x25, 0x3c), .fixups = &macronix_qpp4b_fixups },
{ .id = SNOR_ID(0xc2, 0x9e, 0x16), .fixups = &mx25l3255e_fixups },
};
const struct spi_nor_manufacturer spi_nor_macronix = {
.name = "macronix",
.parts = macronix_nor_parts,
.nparts = ARRAY_SIZE(macronix_nor_parts),
.fixups = &macronix_nor_fixups,
.fixups = macronix_fixups,
.nfixups = ARRAY_SIZE(macronix_fixups),
};

View File

@ -131,7 +131,7 @@ static int micron_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->opcodes.die_erase = SPINOR_OP_MT_DIE_ERASE;
params->n_dice = 4;
/*
@ -147,7 +147,7 @@ static int micron_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->opcodes.die_erase = SPINOR_OP_MT_DIE_ERASE;
params->n_dice = 2;
/*
@ -177,7 +177,7 @@ static int mt35xu512aba_post_sfdp_fixup(struct spi_nor *nor)
spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_8_8_8_DTR],
SPINOR_OP_PP_4B, SNOR_PROTO_8_8_8_DTR);
nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
nor->params->cmd_ext_type = SPI_NOR_EXT_REPEAT;
nor->params->rdsr_dummy = 8;
nor->params->rdsr_addr_nbytes = 0;
@ -205,14 +205,10 @@ static const struct flash_info micron_nor_parts[] = {
/* MT35XU512ABA */
.id = SNOR_ID(0x2c, 0x5b, 0x1a),
.mfr_flags = USE_FSR,
.fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE,
.fixups = &mt35xu512aba_fixups,
}, {
/* MT35XU01GBBA */
.id = SNOR_ID(0x2c, 0x5b, 0x1b),
.mfr_flags = USE_FSR,
.fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE,
.fixups = &mt35_two_die_fixups,
}, {
.id = SNOR_ID(0x2c, 0x5b, 0x1c),
.name = "mt35xu02g",
@ -220,8 +216,6 @@ static const struct flash_info micron_nor_parts[] = {
.size = SZ_256M,
.no_sfdp_flags = SECT_4K | SPI_NOR_OCTAL_READ,
.mfr_flags = USE_FSR,
.fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE,
.fixups = &mt35_two_die_fixups,
},
};
@ -229,10 +223,21 @@ static int mt25qu512a_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
nor->flags &= ~SNOR_F_HAS_16BIT_SR;
nor->params->opcodes.write_sr1 = 0;
return 0;
}
/*
* n25q00a parts share the first same 3 ID bytes with mt25qu01g.
* In order to not mix the fixups, further filter out using the part name.
*/
static bool n25q00a_match(const struct spi_nor *nor)
{
const char *name = nor->info ? nor->info->name : NULL;
return name && !strncmp(name, "n25q00a", 7);
}
static const struct spi_nor_fixups mt25qu512a_fixups = {
.post_bfpt = mt25qu512a_post_bfpt_fixup,
};
@ -395,7 +400,6 @@ static const struct flash_info st_nor_parts[] = {
.name = "mt25ql256a",
.size = SZ_32M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixup_flags = SPI_NOR_4B_OPCODES,
.mfr_flags = USE_FSR,
}, {
.id = SNOR_ID(0x20, 0xba, 0x19),
@ -408,7 +412,6 @@ static const struct flash_info st_nor_parts[] = {
.name = "mt25ql512a",
.size = SZ_64M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixup_flags = SPI_NOR_4B_OPCODES,
.mfr_flags = USE_FSR,
}, {
.id = SNOR_ID(0x20, 0xba, 0x20),
@ -426,14 +429,12 @@ 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,
.fixups = &n25q00_fixups,
}, {
.id = SNOR_ID(0x20, 0xba, 0x22),
.name = "mt25ql02g",
.size = SZ_256M,
.no_sfdp_flags = SECT_4K | SPI_NOR_QUAD_READ,
.mfr_flags = USE_FSR,
.fixups = &mt25q02_fixups,
}, {
.id = SNOR_ID(0x20, 0xbb, 0x15),
.name = "n25q016a",
@ -466,7 +467,6 @@ static const struct flash_info st_nor_parts[] = {
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
SPI_NOR_BP3_SR_BIT6,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixup_flags = SPI_NOR_4B_OPCODES,
.mfr_flags = USE_FSR,
}, {
.id = SNOR_ID(0x20, 0xbb, 0x19),
@ -480,7 +480,6 @@ static const struct flash_info st_nor_parts[] = {
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_4BIT_BP |
SPI_NOR_BP3_SR_BIT6,
.mfr_flags = USE_FSR,
.fixups = &mt25qu512a_fixups,
}, {
.id = SNOR_ID(0x20, 0xbb, 0x20),
.name = "n25q512a",
@ -493,21 +492,18 @@ static const struct flash_info st_nor_parts[] = {
.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,
.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,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.mfr_flags = USE_FSR,
.fixups = &mt25q02_fixups,
}
};
@ -632,8 +628,8 @@ static int micron_st_nor_ready(struct spi_nor *nor)
static void micron_st_nor_default_init(struct spi_nor *nor)
{
nor->flags |= SNOR_F_HAS_LOCK;
nor->flags &= ~SNOR_F_HAS_16BIT_SR;
nor->params->flags |= SNOR_F_HAS_LOCK;
nor->params->opcodes.write_sr1 = 0;
nor->params->quad_enable = NULL;
}
@ -657,16 +653,44 @@ static const struct spi_nor_fixups micron_st_nor_fixups = {
.late_init = micron_st_nor_late_init,
};
static const struct spi_nor_fixup micron_fixups[] = {
{ .fixups = &micron_st_nor_fixups },
{ .id = SNOR_ID(0x2c, 0x5b, 0x1a), .fixups = &mt35xu512aba_fixups,
.fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE },
{ .id = SNOR_ID(0x2c, 0x5b, 0x1b), .fixups = &mt35_two_die_fixups,
.fixup_flags = SPI_NOR_IO_MODE_EN_VOLATILE },
{ .id = SNOR_ID(0x2c, 0x5b, 0x1c), .fixups = &mt35_two_die_fixups,
.fixup_flags = SPI_NOR_4B_OPCODES | SPI_NOR_IO_MODE_EN_VOLATILE },
};
static const struct spi_nor_fixup st_fixups[] = {
{ .fixups = &micron_st_nor_fixups },
{ .id = SNOR_ID(0x20, 0xba, 0x19, 0x10, 0x44, 0x00),
.fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0x20, 0xba, 0x20, 0x10, 0x44, 0x00),
.fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0x20, 0xbb, 0x19, 0x10, 0x44, 0x00),
.fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0x20, 0xba, 0x21), .fixups = &n25q00_fixups },
{ .id = SNOR_ID(0x20, 0xba, 0x22), .fixups = &mt25q02_fixups },
{ .id = SNOR_ID(0x20, 0xbb, 0x20, 0x10, 0x44, 0x00), .fixups = &mt25qu512a_fixups },
{ .id = SNOR_ID(0x20, 0xbb, 0x21, 0x10, 0x44, 0x00), .fixups = &mt25q01_fixups },
{ .id = SNOR_ID(0x20, 0xbb, 0x21), .match = n25q00a_match, .fixups = &n25q00_fixups },
{ .id = SNOR_ID(0x20, 0xbb, 0x22), .fixups = &mt25q02_fixups },
};
const struct spi_nor_manufacturer spi_nor_micron = {
.name = "micron",
.parts = micron_nor_parts,
.nparts = ARRAY_SIZE(micron_nor_parts),
.fixups = &micron_st_nor_fixups,
.fixups = micron_fixups,
.nfixups = ARRAY_SIZE(micron_fixups),
};
const struct spi_nor_manufacturer spi_nor_st = {
.name = "st",
.parts = st_nor_parts,
.nparts = ARRAY_SIZE(st_nor_parts),
.fixups = &micron_st_nor_fixups,
.fixups = st_fixups,
.nfixups = ARRAY_SIZE(st_fixups),
};

View File

@ -25,7 +25,7 @@
* Read a security register by using the SPINOR_OP_RSECR commands.
*
* In Winbond/GigaDevice datasheets the term "security register" stands for
* an one-time-programmable memory area, consisting of multiple bytes (usually
* a one-time-programmable memory area, consisting of multiple bytes (usually
* 256). Thus one "security register" maps to one OTP region.
*
* This method is used on GigaDevice and Winbond flashes.
@ -175,24 +175,24 @@ static int spi_nor_otp_lock_bit_cr(unsigned int region)
*/
int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region)
{
u8 *cr = nor->bouncebuf;
int ret, lock_bit;
u8 sr[2];
lock_bit = spi_nor_otp_lock_bit_cr(region);
if (lock_bit < 0)
return lock_bit;
ret = spi_nor_read_cr(nor, cr);
ret = spi_nor_read_sr1_and_sr2(nor, sr);
if (ret)
return ret;
/* no need to write the register if region is already locked */
if (cr[0] & lock_bit)
if (sr[1] & lock_bit)
return 0;
cr[0] |= lock_bit;
sr[1] |= lock_bit;
return spi_nor_write_16bit_cr_and_check(nor, cr[0]);
return spi_nor_write_sr1_and_sr2_and_check(nor, sr);
}
/**
@ -207,18 +207,18 @@ int spi_nor_otp_lock_sr2(struct spi_nor *nor, unsigned int region)
*/
int spi_nor_otp_is_locked_sr2(struct spi_nor *nor, unsigned int region)
{
u8 *cr = nor->bouncebuf;
int ret, lock_bit;
u8 sr2;
lock_bit = spi_nor_otp_lock_bit_cr(region);
if (lock_bit < 0)
return lock_bit;
ret = spi_nor_read_cr(nor, cr);
ret = spi_nor_read_sr2(nor, &sr2);
if (ret)
return ret;
return cr[0] & lock_bit;
return sr2 & lock_bit;
}
static loff_t spi_nor_otp_region_start(const struct spi_nor *nor, unsigned int region)

View File

@ -34,17 +34,6 @@
#define SFDP_SIGNATURE 0x50444653U
struct sfdp_header {
u32 signature; /* Ox50444653U <=> "SFDP" */
u8 minor;
u8 major;
u8 nph; /* 0-base number of parameter headers */
u8 unused;
/* Basic Flash Parameter Table. */
struct sfdp_parameter_header bfpt_header;
};
/* Fast Read settings. */
struct sfdp_bfpt_read {
/* The Fast Read x-y-z hardware capability in params->hwcaps.mask. */
@ -126,6 +115,12 @@ struct sfdp_bfpt_erase {
#define SFDP_4BAIT_DWORD_MAX 2
/*
* Limit the total size of SFDP to a reasonable value to avoid allocating too
* much memory just of because the flash returned some insane values.
*/
#define SFDP_MAX_SIZE SZ_16K
struct sfdp_4bait {
/* The hardware capability. */
u32 hwcaps;
@ -401,6 +396,45 @@ static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map)
}
}
/**
* spi_nor_post_bfpt_fixups() - Updates the flash's parameters and settings
* after BFPT has been parsed. Called only for flashes that define JESD216 SFDP
* tables.
* @nor: pointer to a 'struct spi_nor'
* @bfpt_header: pointer to the 'struct sfdp_parameter_header' describing
* the Basic Flash Parameter Table length and version
* @bfpt: pointer to the 'struct sfdp_bfpt' to be fixed up
*
* Used to tweak various flash parameters when information provided by the SFDP
* tables are wrong.
*
* Return: 0 on success, -errno otherwise.
*/
static int spi_nor_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
const struct spi_nor_fixup *fixups;
int ret;
unsigned int i;
if (!nor->manufacturer || !nor->manufacturer->fixups)
return 0;
fixups = nor->manufacturer->fixups;
for (i = 0; i < nor->manufacturer->nfixups; i++) {
if (fixups[i].fixups && fixups[i].fixups->post_bfpt &&
spi_nor_fixup_match(nor, &fixups[i])) {
ret = fixups[i].fixups->post_bfpt(nor, bfpt_header, bfpt);
if (ret)
return ret;
}
}
return 0;
}
/**
* spi_nor_parse_bfpt() - read and parse the Basic Flash Parameter Table.
* @nor: pointer to a 'struct spi_nor'
@ -453,7 +487,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
bfpt_header->length * sizeof(u32));
addr = SFDP_PARAM_HEADER_PTP(bfpt_header);
memset(&bfpt, 0, sizeof(bfpt));
err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt);
err = spi_nor_read_sfdp_dma_unsafe(nor, addr, len, &bfpt);
if (err < 0)
return err;
@ -561,34 +595,50 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
val >>= BFPT_DWORD11_PAGE_SIZE_SHIFT;
params->page_size = 1U << val;
/*
* The standard declares various read and write status methods, some of
* them will be overloaded based on the QER field.
*/
params->opcodes.read_sr1 = SPINOR_OP_RDSR;
params->opcodes.read_sr2 = SPINOR_OP_RDCR;
params->opcodes.write_sr1 = SPINOR_OP_WRSR;
params->opcodes.write_sr1_and_sr2 = SPINOR_OP_WRSR;
params->qe_mask[0] = 0;
params->qe_mask[1] = 0;
/* Quad Enable Requirements. */
switch (bfpt.dwords[SFDP_DWORD(15)] & BFPT_DWORD15_QER_MASK) {
case BFPT_DWORD15_QER_NONE:
params->quad_enable = NULL;
break;
case BFPT_DWORD15_QER_SR2_BIT1_BUGGY:
case BFPT_DWORD15_QER_SR2_BIT1_NO_1B_WR:
/*
* Writing only one byte to the Status Register has the
* side-effect of clearing Status Register 2.
*/
fallthrough;
case BFPT_DWORD15_QER_SR2_BIT1_NO_RD:
/*
* Read Configuration Register (35h) instruction is not
* supported.
* supported. 16-bit writes expected.
*/
nor->flags |= SNOR_F_HAS_16BIT_SR | SNOR_F_NO_READ_CR;
params->quad_enable = spi_nor_sr2_bit1_quad_enable;
params->opcodes.read_sr2 = 0;
params->opcodes.write_sr1 = 0;
params->qe_mask[1] = BIT(1);
break;
case BFPT_DWORD15_QER_SR1_BIT6:
nor->flags &= ~SNOR_F_HAS_16BIT_SR;
params->quad_enable = spi_nor_sr1_bit6_quad_enable;
params->opcodes.read_sr2 = 0;
params->opcodes.write_sr1_and_sr2 = 0;
params->qe_mask[0] = BIT(6);
break;
case BFPT_DWORD15_QER_SR2_BIT7:
nor->flags &= ~SNOR_F_HAS_16BIT_SR;
params->quad_enable = spi_nor_sr2_bit7_quad_enable;
params->opcodes.read_sr2 = SPINOR_OP_RDSR2;
params->opcodes.write_sr1_and_sr2 = 0;
params->opcodes.write_sr2 = SPINOR_OP_WRSR2;
params->qe_mask[1] = BIT(7);
break;
case BFPT_DWORD15_QER_SR2_BIT1:
@ -598,9 +648,14 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
* Register 2, so let's be cautious and keep the default
* assumption of a 16-bit Write Status (01h) command.
*/
nor->flags |= SNOR_F_HAS_16BIT_SR;
params->opcodes.write_sr1 = 0;
params->qe_mask[1] = BIT(1);
break;
params->quad_enable = spi_nor_sr2_bit1_quad_enable;
case BFPT_DWORD15_QER_SR2_BIT1_1B:
params->qe_mask[1] = BIT(1);
params->opcodes.write_sr1_and_sr2 = 0;
params->opcodes.write_sr2 = SPINOR_OP_WRSR2_ALT;
break;
default:
@ -608,6 +663,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
break;
}
/* opcodes sanity check */
WARN_ON(!params->opcodes.read_sr1 ||
(!params->opcodes.write_sr1 && !params->opcodes.write_sr1_and_sr2) ||
(!params->opcodes.read_sr2 && params->opcodes.write_sr2));
dword = bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_4B_ADDR_MODE_MASK;
if (SFDP_MASK_CHECK(dword, BFPT_DWORD16_4B_ADDR_MODE_BRWR))
params->set_4byte_addr_mode = spi_nor_set_4byte_addr_mode_brwr;
@ -620,7 +680,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
/* Soft Reset support. */
if (bfpt.dwords[SFDP_DWORD(16)] & BFPT_DWORD16_SWRST_EN_RST)
nor->flags |= SNOR_F_SOFT_RESET;
params->flags |= SNOR_F_SOFT_RESET;
/* Stop here if not JESD216 rev C or later. */
if (bfpt_header->length == BFPT_DWORD_MAX_JESD216B)
@ -655,11 +715,11 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
/* 8D-8D-8D command extension. */
switch (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_CMD_EXT_MASK) {
case BFPT_DWORD18_CMD_EXT_REP:
nor->cmd_ext_type = SPI_NOR_EXT_REPEAT;
params->cmd_ext_type = SPI_NOR_EXT_REPEAT;
break;
case BFPT_DWORD18_CMD_EXT_INV:
nor->cmd_ext_type = SPI_NOR_EXT_INVERT;
params->cmd_ext_type = SPI_NOR_EXT_INVERT;
break;
case BFPT_DWORD18_CMD_EXT_RES:
@ -673,7 +733,7 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
/* Byte order in 8D-8D-8D mode */
if (bfpt.dwords[SFDP_DWORD(18)] & BFPT_DWORD18_BYTE_ORDER_SWAPPED)
nor->flags |= SNOR_F_SWAP16;
params->flags |= SNOR_F_SWAP16;
return spi_nor_post_bfpt_fixups(nor, bfpt_header, &bfpt);
}
@ -702,12 +762,19 @@ static u8 spi_nor_smpt_addr_nbytes(const struct spi_nor *nor, const u32 settings
static void spi_nor_smpt_read_dummy_fixups(const struct spi_nor *nor,
u8 *read_dummy)
{
if (nor->manufacturer && nor->manufacturer->fixups &&
nor->manufacturer->fixups->smpt_read_dummy)
nor->manufacturer->fixups->smpt_read_dummy(nor, read_dummy);
const struct spi_nor_fixup *fixups;
unsigned int i;
if (nor->info->fixups && nor->info->fixups->smpt_read_dummy)
nor->info->fixups->smpt_read_dummy(nor, read_dummy);
if (!nor->manufacturer || !nor->manufacturer->fixups)
return;
fixups = nor->manufacturer->fixups;
for (i = 0; i < nor->manufacturer->nfixups; i++) {
if (fixups[i].fixups && fixups[i].fixups->smpt_read_dummy &&
spi_nor_fixup_match(nor, &fixups[i]))
fixups[i].fixups->smpt_read_dummy(nor, read_dummy);
}
}
/**
@ -732,12 +799,19 @@ static u8 spi_nor_smpt_read_dummy(const struct spi_nor *nor, const u32 settings)
static void spi_nor_smpt_map_id_fixups(const struct spi_nor *nor, u8 *map_id)
{
if (nor->manufacturer && nor->manufacturer->fixups &&
nor->manufacturer->fixups->smpt_map_id)
nor->manufacturer->fixups->smpt_map_id(nor, map_id);
const struct spi_nor_fixup *fixups;
unsigned int i;
if (nor->info->fixups && nor->info->fixups->smpt_map_id)
nor->info->fixups->smpt_map_id(nor, map_id);
if (!nor->manufacturer || !nor->manufacturer->fixups)
return;
fixups = nor->manufacturer->fixups;
for (i = 0; i < nor->manufacturer->nfixups; i++) {
if (fixups[i].fixups && fixups[i].fixups->smpt_map_id &&
spi_nor_fixup_match(nor, &fixups[i]))
fixups[i].fixups->smpt_map_id(nor, map_id);
}
}
/**
@ -1017,6 +1091,8 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
{ SNOR_HWCAPS_PP, BIT(6) },
{ SNOR_HWCAPS_PP_1_1_4, BIT(7) },
{ SNOR_HWCAPS_PP_1_4_4, BIT(8) },
{ SNOR_HWCAPS_PP_1_1_8, BIT(23) },
{ SNOR_HWCAPS_PP_1_8_8, BIT(24) },
};
static const struct sfdp_4bait erases[SNOR_ERASE_TYPE_MAX] = {
{ 0u /* not used */, BIT(9) },
@ -1143,6 +1219,14 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_4_4],
SPINOR_OP_PP_1_4_4_4B,
SNOR_PROTO_1_4_4);
if (pp_hwcaps & SNOR_HWCAPS_PP_1_1_8)
spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_1_8],
SPINOR_OP_PP_1_1_8_4B,
SNOR_PROTO_1_1_8);
if (pp_hwcaps & SNOR_HWCAPS_PP_1_8_8)
spi_nor_set_pp_settings(&params_pp[SNOR_CMD_PP_1_8_8],
SPINOR_OP_PP_1_8_8_4B,
SNOR_PROTO_1_8_8);
for (i = 0; i < SNOR_ERASE_TYPE_MAX; i++) {
if (erase_mask & BIT(i))
@ -1160,7 +1244,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
* SFDP compliant memories.
*/
params->addr_nbytes = 4;
nor->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT;
params->flags |= SNOR_F_4B_OPCODES | SNOR_F_HAS_4BAIT;
/* fall through */
out:
@ -1175,6 +1259,7 @@ static int spi_nor_parse_4bait(struct spi_nor *nor,
#define PROFILE1_DWORD5_DUMMY_166MHZ GENMASK(31, 27)
#define PROFILE1_DWORD5_DUMMY_133MHZ GENMASK(21, 17)
#define PROFILE1_DWORD5_DUMMY_100MHZ GENMASK(11, 7)
#define SFDP_PROFILE1_DWORD_MIN 5
/**
* spi_nor_parse_profile1() - parse the xSPI Profile 1.0 table
@ -1192,6 +1277,9 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
int ret;
u8 dummy, opcode;
if (profile1_header->length < SFDP_PROFILE1_DWORD_MIN)
return -EINVAL;
len = profile1_header->length * sizeof(*dwords);
dwords = kmalloc(len, GFP_KERNEL);
if (!dwords)
@ -1262,6 +1350,7 @@ static int spi_nor_parse_profile1(struct spi_nor *nor,
}
#define SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE BIT(31)
#define SFDP_SCCR_DWORD_MIN 22
/**
* spi_nor_parse_sccr() - Parse the Status, Control and Configuration Register
@ -1280,6 +1369,9 @@ static int spi_nor_parse_sccr(struct spi_nor *nor,
size_t len;
int ret;
if (sccr_header->length < SFDP_SCCR_DWORD_MIN)
return -EINVAL;
len = sccr_header->length * sizeof(*dwords);
dwords = kmalloc(len, GFP_KERNEL);
if (!dwords)
@ -1306,7 +1398,7 @@ static int spi_nor_parse_sccr(struct spi_nor *nor,
if (FIELD_GET(SCCR_DWORD22_OCTAL_DTR_EN_VOLATILE,
dwords[SFDP_DWORD(22)]))
nor->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
params->flags |= SNOR_F_IO_MODE_EN_VOLATILE;
out:
kfree(dwords);
@ -1381,17 +1473,23 @@ static int spi_nor_parse_sccr_mc(struct spi_nor *nor,
*/
static int spi_nor_post_sfdp_fixups(struct spi_nor *nor)
{
const struct spi_nor_fixup *fixups;
unsigned int i;
int ret;
if (nor->manufacturer && nor->manufacturer->fixups &&
nor->manufacturer->fixups->post_sfdp) {
ret = nor->manufacturer->fixups->post_sfdp(nor);
if (ret)
return ret;
}
if (!nor->manufacturer || !nor->manufacturer->fixups)
return 0;
if (nor->info->fixups && nor->info->fixups->post_sfdp)
return nor->info->fixups->post_sfdp(nor);
fixups = nor->manufacturer->fixups;
for (i = 0; i < nor->manufacturer->nfixups; i++) {
if (fixups[i].fixups && fixups[i].fixups->post_sfdp &&
spi_nor_fixup_match(nor, &fixups[i])) {
ret = fixups[i].fixups->post_sfdp(nor);
if (ret)
return ret;
}
}
return 0;
}
@ -1433,12 +1531,16 @@ int spi_nor_check_sfdp_signature(struct spi_nor *nor)
* runtime the main parameters needed to perform basic SPI flash operations such
* as Fast Read, Page Program or Sector Erase commands.
*
* Because the parsing is optional, all the settings have to be reverted. IOW,
* nothing of struct spi_nor shall be changed.
*
* Return: 0 on success, -errno otherwise.
*/
int spi_nor_parse_sfdp(struct spi_nor *nor)
{
const struct sfdp_parameter_header *param_header, *bfpt_header;
struct sfdp_parameter_header *param_headers = NULL;
struct spi_nor_flash_parameter *save_params;
struct sfdp_header header;
struct device *dev = nor->dev;
struct sfdp *sfdp;
@ -1446,15 +1548,26 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
size_t psize;
int i, err;
/*
* Get a backup of all the parameter to roll back to in case of an
* error.
*/
save_params = kmalloc_objs(*save_params, 2);
if (!save_params)
return -ENOMEM;
save_params[0] = *nor->params;
/* Get the SFDP header. */
err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(header), &header);
if (err < 0)
return err;
goto free_save_params;
/* Check the SFDP header version. */
if (le32_to_cpu(header.signature) != SFDP_SIGNATURE ||
header.major != SFDP_JESD216_MAJOR)
return -EINVAL;
header.major != SFDP_JESD216_MAJOR) {
err = -EINVAL;
goto free_save_params;
}
/*
* Verify that the first and only mandatory parameter header is a
@ -1462,8 +1575,10 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
*/
bfpt_header = &header.bfpt_header;
if (SFDP_PARAM_HEADER_ID(bfpt_header) != SFDP_BFPT_ID ||
bfpt_header->major != SFDP_JESD216_MAJOR)
return -EINVAL;
bfpt_header->major != SFDP_JESD216_MAJOR) {
err = -EINVAL;
goto free_save_params;
}
sfdp_size = SFDP_PARAM_HEADER_PTP(bfpt_header) +
SFDP_PARAM_HEADER_PARAM_LEN(bfpt_header);
@ -1483,14 +1598,16 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
psize = header.nph * sizeof(*param_headers);
param_headers = kmalloc(psize, GFP_KERNEL);
if (!param_headers)
return -ENOMEM;
if (!param_headers) {
err = -ENOMEM;
goto free_save_params;
}
err = spi_nor_read_sfdp(nor, sizeof(header),
psize, param_headers);
if (err < 0) {
dev_dbg(dev, "failed to read SFDP parameter headers\n");
goto exit;
goto free_param_headers;
}
}
@ -1505,20 +1622,16 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
SFDP_PARAM_HEADER_PARAM_LEN(param_header));
}
/*
* Limit the total size to a reasonable value to avoid allocating too
* much memory just of because the flash returned some insane values.
*/
if (sfdp_size > PAGE_SIZE) {
if (sfdp_size > SFDP_MAX_SIZE) {
dev_dbg(dev, "SFDP data (%zu) too big, truncating\n",
sfdp_size);
sfdp_size = PAGE_SIZE;
sfdp_size = SFDP_MAX_SIZE;
}
sfdp = devm_kzalloc(dev, sizeof(*sfdp), GFP_KERNEL);
if (!sfdp) {
err = -ENOMEM;
goto exit;
goto free_param_headers;
}
/*
@ -1532,16 +1645,13 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
sizeof(*sfdp->dwords), GFP_KERNEL);
if (!sfdp->dwords) {
err = -ENOMEM;
devm_kfree(dev, sfdp);
goto exit;
goto free_sfdp;
}
err = spi_nor_read_sfdp(nor, 0, sfdp_size, sfdp->dwords);
if (err < 0) {
dev_dbg(dev, "failed to read SFDP data\n");
devm_kfree(dev, sfdp->dwords);
devm_kfree(dev, sfdp);
goto exit;
goto free_dwords;
}
nor->sfdp = sfdp;
@ -1563,10 +1673,11 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
err = spi_nor_parse_bfpt(nor, bfpt_header);
if (err)
goto exit;
goto clear_sfdp_ptr;
/* Parse optional parameter tables. */
for (i = 0; i < header.nph; i++) {
save_params[1] = *nor->params;
param_header = &param_headers[i];
switch (SFDP_PARAM_HEADER_ID(param_header)) {
@ -1600,15 +1711,46 @@ int spi_nor_parse_sfdp(struct spi_nor *nor)
/*
* Let's not drop all information we extracted so far
* if optional table parsers fail. In case of failing,
* each optional parser is responsible to roll back to
* the previously known spi_nor data.
* roll back to the previously known
* spi_nor_flash_parameter data.
*/
err = 0;
*nor->params = save_params[1];
}
}
err = spi_nor_post_sfdp_fixups(nor);
exit:
if (err)
goto clear_sfdp_ptr;
kfree(param_headers);
kfree(save_params);
return 0;
clear_sfdp_ptr:
nor->sfdp = NULL;
free_dwords:
devm_kfree(dev, sfdp->dwords);
free_sfdp:
devm_kfree(dev, sfdp);
free_param_headers:
kfree(param_headers);
if (err)
*nor->params = save_params[0];
free_save_params:
kfree(save_params);
return err;
}
/**
* spi_nor_sfdp_get_header() - retrieves the SFDP header
* @nor: pointer to a 'struct spi_nor' with a valid SFDP field
*
* Return: the cached SFDP header.
*/
struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor)
{
return (struct sfdp_header *)nor->sfdp->dwords;
}

View File

@ -12,6 +12,7 @@
#define SFDP_JESD216_MINOR 0
#define SFDP_JESD216A_MINOR 5
#define SFDP_JESD216B_MINOR 6
#define SFDP_JESD216F_MINOR 10
/* SFDP DWORDS are indexed from 1 but C arrays are indexed from 0. */
#define SFDP_DWORD(i) ((i) - 1)
@ -81,14 +82,21 @@ struct sfdp_bfpt {
* instruction 35h. QE is set via Write Status instruction 01h with
* two data bytes where bit 1 of the second byte is one.
* [...]
* (from JESD216 rev F)
* - 110b: QE is bit 1 of status register 2. Status register 1 is read using
* Read Status instruction 05h. Status register2 is read using
* instruction 35h, and status register 3 is read using instruction 15h.
* QE is set via Write Status Register instruction 31h with one data
* byte.
*/
#define BFPT_DWORD15_QER_MASK GENMASK(22, 20)
#define BFPT_DWORD15_QER_NONE (0x0UL << 20) /* Micron */
#define BFPT_DWORD15_QER_SR2_BIT1_BUGGY (0x1UL << 20)
#define BFPT_DWORD15_QER_SR2_BIT1_NO_1B_WR (0x1UL << 20)
#define BFPT_DWORD15_QER_SR1_BIT6 (0x2UL << 20) /* Macronix */
#define BFPT_DWORD15_QER_SR2_BIT7 (0x3UL << 20)
#define BFPT_DWORD15_QER_SR2_BIT1_NO_RD (0x4UL << 20)
#define BFPT_DWORD15_QER_SR2_BIT1 (0x5UL << 20) /* Spansion */
#define BFPT_DWORD15_QER_SR2_BIT1_1B (0x6UL << 20) /* Winbond */
#define BFPT_DWORD16_EN4B_MASK GENMASK(31, 24)
#define BFPT_DWORD16_EN4B_ALWAYS_4B BIT(30)
@ -141,4 +149,17 @@ struct sfdp_parameter_header {
u8 id_msb;
};
struct sfdp_header {
u32 signature; /* Ox50444653U <=> "SFDP" */
u8 minor;
u8 major;
u8 nph; /* 0-base number of parameter headers */
u8 unused;
/* Basic Flash Parameter Table. */
struct sfdp_parameter_header bfpt_header;
};
struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor);
#endif /* __LINUX_MTD_SFDP_H */

View File

@ -399,8 +399,9 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor,
nor->bouncebuf);
bool is3byte, is4byte;
int ret;
u8 sr;
ret = spi_nor_read_sr(nor, &nor->bouncebuf[1]);
ret = spi_nor_read_sr1(nor, &sr);
if (ret)
return ret;
@ -408,7 +409,7 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor,
if (ret)
return ret;
is3byte = (nor->bouncebuf[0] == nor->bouncebuf[1]);
is3byte = (nor->bouncebuf[0] == sr);
op = (struct spi_mem_op)
CYPRESS_NOR_RD_ANY_REG_OP(4, SPINOR_REG_CYPRESS_STR1V, 0,
@ -417,7 +418,7 @@ static int cypress_nor_determine_addr_mode_by_sr1(struct spi_nor *nor,
if (ret)
return ret;
is4byte = (nor->bouncebuf[0] == nor->bouncebuf[1]);
is4byte = (nor->bouncebuf[0] == sr);
if (is3byte == is4byte)
return -EIO;
@ -537,7 +538,7 @@ static void cypress_nor_ecc_init(struct spi_nor *nor)
* same ECC data unit without an erase are not allowed.
*/
nor->params->writesize = 16;
nor->flags |= SNOR_F_ECC;
nor->params->flags |= SNOR_F_ECC;
}
static int
@ -545,7 +546,6 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
struct spi_mem_op op;
int ret;
/* Assign 4-byte address mode method that is not determined in BFPT */
@ -555,19 +555,6 @@ s25fs256t_post_bfpt_fixup(struct spi_nor *nor,
if (ret)
return ret;
/* Read Architecture Configuration Register (ARCFN) */
op = (struct spi_mem_op)
CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes,
SPINOR_REG_CYPRESS_ARCFN, 1,
nor->bouncebuf);
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
/* ARCFN value must be 0 if uniform sector is selected */
if (nor->bouncebuf[0])
return -ENODEV;
return 0;
}
@ -598,6 +585,22 @@ static int s25fs256t_post_sfdp_fixup(struct spi_nor *nor)
static int s25fs256t_late_init(struct spi_nor *nor)
{
struct spi_mem_op op;
int ret;
/* Read Architecture Configuration Register (ARCFN) */
op = (struct spi_mem_op)
CYPRESS_NOR_RD_ANY_REG_OP(nor->params->addr_mode_nbytes,
SPINOR_REG_CYPRESS_ARCFN, 1,
nor->bouncebuf);
ret = spi_nor_read_any_reg(nor, &op, nor->reg_proto);
if (ret)
return ret;
/* ARCFN value must be 0 if uniform sector is selected */
if (nor->bouncebuf[0])
return -ENODEV;
cypress_nor_ecc_init(nor);
return 0;
@ -675,7 +678,7 @@ static int s25hx_t_late_init(struct spi_nor *nor)
cypress_nor_ecc_init(nor);
if (params->n_dice > 1)
params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
params->opcodes.die_erase = SPINOR_OP_CYPRESS_DIE_ERASE;
return 0;
}
@ -763,7 +766,7 @@ static int s28hx_t_late_init(struct spi_nor *nor)
cypress_nor_ecc_init(nor);
if (params->n_dice > 1)
params->die_erase_opcode = SPINOR_OP_CYPRESS_DIE_ERASE;
params->opcodes.die_erase = SPINOR_OP_CYPRESS_DIE_ERASE;
return 0;
}
@ -868,13 +871,6 @@ static const struct flash_info spansion_nor_parts[] = {
.name = "s25fl256s0",
.size = SZ_32M,
.sector_size = SZ_256K,
.no_sfdp_flags = SPI_NOR_SKIP_SFDP | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.mfr_flags = USE_CLSR,
}, {
.id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x00, 0x81),
.name = "s25fs256s0",
.size = SZ_32M,
.sector_size = SZ_256K,
.no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.mfr_flags = USE_CLSR,
}, {
@ -904,7 +900,6 @@ static const struct flash_info spansion_nor_parts[] = {
.sector_size = SZ_256K,
.no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.mfr_flags = USE_CLSR,
.fixups = &s25fs_s_nor_fixups,
}, {
.id = SNOR_ID(0x01, 0x20, 0x18, 0x03, 0x00),
.name = "s25sl12800",
@ -940,7 +935,6 @@ static const struct flash_info spansion_nor_parts[] = {
.size = SZ_16M,
.no_sfdp_flags = SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.mfr_flags = USE_CLSR,
.fixups = &s25fs_s_nor_fixups,
}, {
.id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01),
.name = "s25fl129p1",
@ -977,19 +971,16 @@ static const struct flash_info spansion_nor_parts[] = {
.name = "s25fl064l",
.size = SZ_8M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixup_flags = SPI_NOR_4B_OPCODES,
}, {
.id = SNOR_ID(0x01, 0x60, 0x18),
.name = "s25fl128l",
.size = SZ_16M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixup_flags = SPI_NOR_4B_OPCODES,
}, {
.id = SNOR_ID(0x01, 0x60, 0x19),
.name = "s25fl256l",
.size = SZ_32M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixup_flags = SPI_NOR_4B_OPCODES,
}, {
.id = SNOR_ID(0x04, 0x2c, 0xc2, 0x7f, 0x7f, 0x7f),
.name = "cy15x104q",
@ -1000,76 +991,61 @@ static const struct flash_info spansion_nor_parts[] = {
.id = SNOR_ID(0x34, 0x2a, 0x1a, 0x0f, 0x03, 0x90),
.name = "s25hl512t",
.mfr_flags = USE_CLPEF,
.fixups = &s25hx_t_fixups
}, {
.id = SNOR_ID(0x34, 0x2a, 0x1b, 0x0f, 0x03, 0x90),
.name = "s25hl01gt",
.mfr_flags = USE_CLPEF,
.fixups = &s25hx_t_fixups
}, {
.id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90),
.name = "s25hl02gt",
.mfr_flags = USE_CLPEF,
.fixups = &s25hx_t_fixups
}, {
.id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90),
.name = "s25fs256t",
.mfr_flags = USE_CLPEF,
.fixups = &s25fs256t_fixups
}, {
.id = SNOR_ID(0x34, 0x2b, 0x1a, 0x0f, 0x03, 0x90),
.name = "s25hs512t",
.mfr_flags = USE_CLPEF,
.fixups = &s25hx_t_fixups
}, {
.id = SNOR_ID(0x34, 0x2b, 0x1b, 0x0f, 0x03, 0x90),
.name = "s25hs01gt",
.mfr_flags = USE_CLPEF,
.fixups = &s25hx_t_fixups
}, {
.id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90),
.name = "s25hs02gt",
.mfr_flags = USE_CLPEF,
.fixups = &s25hx_t_fixups
}, {
/* S28HL256T */
.id = SNOR_ID(0x34, 0x5a, 0x19),
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
.id = SNOR_ID(0x34, 0x5a, 0x1a),
.name = "s28hl512t",
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
.id = SNOR_ID(0x34, 0x5a, 0x1b),
.name = "s28hl01gt",
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
/* S28HL02GT */
.id = SNOR_ID(0x34, 0x5a, 0x1c),
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
.id = SNOR_ID(0x34, 0x5b, 0x19),
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
.id = SNOR_ID(0x34, 0x5b, 0x1a),
.name = "s28hs512t",
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
.id = SNOR_ID(0x34, 0x5b, 0x1b),
.name = "s28hs01gt",
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
.id = SNOR_ID(0x34, 0x5b, 0x1c),
.name = "s28hs02gt",
.mfr_flags = USE_CLPEF,
.fixups = &s28hx_t_fixups,
}, {
.id = SNOR_ID(0xef, 0x40, 0x13),
.name = "s25fl004k",
@ -1103,13 +1079,14 @@ static const struct flash_info spansion_nor_parts[] = {
static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor)
{
int ret;
u8 sr;
ret = spi_nor_read_sr(nor, nor->bouncebuf);
ret = spi_nor_read_sr1(nor, &sr);
if (ret)
return ret;
if (nor->bouncebuf[0] & (SR_E_ERR | SR_P_ERR)) {
if (nor->bouncebuf[0] & SR_E_ERR)
if (sr & (SR_E_ERR | SR_P_ERR)) {
if (sr & SR_E_ERR)
dev_err(nor->dev, "Erase Error occurred\n");
else
dev_err(nor->dev, "Programming Error occurred\n");
@ -1129,7 +1106,7 @@ static int spansion_nor_sr_ready_and_clear(struct spi_nor *nor)
return -EIO;
}
return !(nor->bouncebuf[0] & SR_WIP);
return !(sr & SR_WIP);
}
static int spansion_nor_late_init(struct spi_nor *nor)
@ -1139,7 +1116,7 @@ static int spansion_nor_late_init(struct spi_nor *nor)
u8 mfr_flags = nor->info->mfr_flags;
if (params->size > SZ_16M) {
nor->flags |= SNOR_F_4B_OPCODES;
params->flags |= SNOR_F_4B_OPCODES;
/* No small sector erase for 4-byte command set */
nor->erase_opcode = SPINOR_OP_SE;
nor->mtd.erasesize = nor->info->sector_size ?:
@ -1168,9 +1145,35 @@ static const struct spi_nor_fixups spansion_nor_fixups = {
.late_init = spansion_nor_late_init,
};
static const struct spi_nor_fixup spansion_fixups[] = {
{ .fixups = &spansion_nor_fixups },
{ .id = SNOR_ID(0x01, 0x02, 0x20, 0x4d, 0x00, 0x81), .fixups = &s25fs_s_nor_fixups },
{ .id = SNOR_ID(0x01, 0x02, 0x19, 0x4d, 0x01, 0x81), .fixups = &s25fs_s_nor_fixups },
{ .id = SNOR_ID(0x01, 0x20, 0x18, 0x4d, 0x01, 0x81), .fixups = &s25fs_s_nor_fixups },
{ .id = SNOR_ID(0x01, 0x60, 0x17), .fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0x01, 0x60, 0x18), .fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0x01, 0x60, 0x19), .fixup_flags = SPI_NOR_4B_OPCODES },
{ .id = SNOR_ID(0x34, 0x2a, 0x1a, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x2a, 0x1b, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x2a, 0x1c, 0x0f, 0x00, 0x90), .fixups = &s25hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x2b, 0x19, 0x0f, 0x08, 0x90), .fixups = &s25fs256t_fixups },
{ .id = SNOR_ID(0x34, 0x2b, 0x1a, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x2b, 0x1b, 0x0f, 0x03, 0x90), .fixups = &s25hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x2b, 0x1c, 0x0f, 0x00, 0x90), .fixups = &s25hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5a, 0x19), .fixups = &s28hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5a, 0x1a), .fixups = &s28hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5a, 0x1b), .fixups = &s28hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5a, 0x1c), .fixups = &s28hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5b, 0x19), .fixups = &s28hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5b, 0x1a), .fixups = &s28hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5b, 0x1b), .fixups = &s28hx_t_fixups },
{ .id = SNOR_ID(0x34, 0x5b, 0x1c), .fixups = &s28hx_t_fixups },
};
const struct spi_nor_manufacturer spi_nor_spansion = {
.name = "spansion",
.parts = spansion_nor_parts,
.nparts = ARRAY_SIZE(spansion_nor_parts),
.fixups = &spansion_nor_fixups,
.fixups = spansion_fixups,
.nfixups = ARRAY_SIZE(spansion_fixups),
};

View File

@ -21,16 +21,17 @@ static int sst26vf_nor_lock(struct spi_nor *nor, loff_t ofs, u64 len)
static int sst26vf_nor_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
{
int ret;
u8 cr;
/* We only support unlocking the entire flash array. */
if (ofs != 0 || len != nor->params->size)
return -EINVAL;
ret = spi_nor_read_cr(nor, nor->bouncebuf);
ret = spi_nor_read_sr2(nor, &cr);
if (ret)
return ret;
if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) {
if (!(cr & SST26VF_CR_BPNV)) {
dev_dbg(nor->dev, "Any block has been permanently locked\n");
return -EINVAL;
}
@ -151,14 +152,12 @@ static const struct flash_info sst_nor_parts[] = {
.id = SNOR_ID(0xbf, 0x26, 0x42),
.name = "sst26vf032b",
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.fixups = &sst26vf_nor_fixups,
}, {
.id = SNOR_ID(0xbf, 0x26, 0x43),
.name = "sst26vf064b",
.size = SZ_8M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixups = &sst26vf_nor_fixups,
}, {
.id = SNOR_ID(0xbf, 0x26, 0x51),
.name = "sst26wf016b",
@ -277,9 +276,16 @@ static const struct spi_nor_fixups sst_nor_fixups = {
.late_init = sst_nor_late_init,
};
static const struct spi_nor_fixup sst_fixups[] = {
{ .fixups = &sst_nor_fixups },
{ .id = SNOR_ID(0xbf, 0x26, 0x42), .fixups = &sst26vf_nor_fixups },
{ .id = SNOR_ID(0xbf, 0x26, 0x43), .fixups = &sst26vf_nor_fixups },
};
const struct spi_nor_manufacturer spi_nor_sst = {
.name = "sst",
.parts = sst_nor_parts,
.nparts = ARRAY_SIZE(sst_nor_parts),
.fixups = &sst_nor_fixups,
.fixups = sst_fixups,
.nfixups = ARRAY_SIZE(sst_fixups),
};

View File

@ -13,12 +13,13 @@
static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6)
if (params->flags & SNOR_F_HAS_SR_BP3_BIT6)
return mask | SR_BP3_BIT6;
if (nor->flags & SNOR_F_HAS_4BIT_BP)
if (params->flags & SNOR_F_HAS_4BIT_BP)
return mask | SR_BP3;
return mask;
@ -26,9 +27,11 @@ static u8 spi_nor_get_sr_bp_mask(struct spi_nor *nor)
static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor)
{
if (nor->flags & SNOR_F_HAS_SR_TB_BIT6)
struct spi_nor_flash_parameter *params = nor->params;
if (params->flags & SNOR_F_HAS_SR_TB_BIT6)
return SR_TB_BIT6;
else if (nor->flags & SNOR_F_HAS_SR_TB)
else if (params->flags & SNOR_F_HAS_SR_TB)
return SR_TB_BIT5;
else
return 0;
@ -36,8 +39,10 @@ static u8 spi_nor_get_sr_tb_mask(struct spi_nor *nor)
static u8 spi_nor_get_sr_cmp_mask(struct spi_nor *nor)
{
if (!(nor->flags & SNOR_F_NO_READ_CR) &&
nor->flags & SNOR_F_HAS_SR2_CMP_BIT6)
struct spi_nor_flash_parameter *params = nor->params;
if (params->opcodes.read_sr2 &&
params->flags & SNOR_F_HAS_SR2_CMP_BIT6)
return SR2_CMP_BIT6;
else
return 0;
@ -67,15 +72,16 @@ u64 spi_nor_get_min_prot_length_sr(struct spi_nor *nor)
void spi_nor_get_locked_range_sr(struct spi_nor *nor, const u8 *sr, loff_t *ofs,
u64 *len)
{
struct spi_nor_flash_parameter *params = nor->params;
u64 min_prot_len;
u8 bp_mask = spi_nor_get_sr_bp_mask(nor);
u8 tb_mask = spi_nor_get_sr_tb_mask(nor);
u8 cmp_mask = spi_nor_get_sr_cmp_mask(nor);
u8 bp, val = sr[0] & bp_mask;
bool tb = (nor->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0;
bool tb = (params->flags & SNOR_F_HAS_SR_TB) ? sr[0] & tb_mask : 0;
bool cmp = sr[1] & cmp_mask;
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6)
if (params->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3_BIT6)
val = (val & ~SR_BP3_BIT6) | SR_BP3;
bp = val >> SR_BP_SHIFT;
@ -153,10 +159,11 @@ static bool spi_nor_is_unlocked_sr(struct spi_nor *nor, loff_t ofs, u64 len,
static int spi_nor_sr_set_bp_mask(struct spi_nor *nor, u8 *sr, u8 pow)
{
struct spi_nor_flash_parameter *params = nor->params;
u8 mask = spi_nor_get_sr_bp_mask(nor);
u8 val = pow << SR_BP_SHIFT;
if (nor->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
if (params->flags & SNOR_F_HAS_SR_BP3_BIT6 && val & SR_BP3)
val = (val & ~SR_BP3) | SR_BP3_BIT6;
if (val & ~mask)
@ -194,6 +201,45 @@ static int spi_nor_build_sr(struct spi_nor *nor, const u8 *old_sr, u8 *new_sr,
return 0;
}
/*
* Make sure we do our best to guess SR2. This has historically only be needed
* for swp.c, so let's keep this extra carefulness in this file.
*/
static int spi_nor_read_sr2_careful(struct spi_nor *nor, u8 *sr2)
{
struct spi_nor_flash_parameter *params = nor->params;
int ret;
if (params->opcodes.read_sr2) {
ret = spi_nor_read_sr_ll(nor, params->opcodes.read_sr2, sr2, 1);
if (ret)
return ret;
} else if ((spi_nor_get_protocol_width(nor->read_proto) == 4 ||
spi_nor_get_protocol_width(nor->write_proto) == 4) &&
nor->params->quad_enable) {
/*
* Make sure the QE bit is persistently kept. qe_mask[1] will be
* 0 if the QE bit is in SR1.
*/
*sr2 = params->qe_mask[1];
} else {
return 0;
}
return 0;
}
static int spi_nor_read_sr1_and_sr2_careful(struct spi_nor *nor, u8 *sr)
{
int ret;
ret = spi_nor_read_sr1(nor, &sr[0]);
if (ret)
return ret;
return spi_nor_read_sr2_careful(nor, &sr[1]);
}
/*
* Keep a local cache containing all lock-related bits for debugfs use only.
* This way, debugfs never needs to access the flash directly.
@ -207,17 +253,9 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr)
if (!sr) {
if (spi_nor_read_sr(nor, nor->bouncebuf))
if (spi_nor_read_sr1_and_sr2_careful(nor, sr_cr))
return;
sr_cr[0] = nor->bouncebuf[0];
if (!(nor->flags & SNOR_F_NO_READ_CR)) {
if (spi_nor_read_cr(nor, nor->bouncebuf))
return;
}
sr_cr[1] = nor->bouncebuf[0];
sr = sr_cr;
}
@ -261,32 +299,23 @@ void spi_nor_cache_sr_lock_bits(struct spi_nor *nor, u8 *sr)
*/
static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
{
struct spi_nor_flash_parameter *params = nor->params;
u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {};
u8 *best_status_new = status_new;
loff_t ofs_old, ofs_new, ofs_new_cmp;
u64 len_old, len_new, len_new_cmp;
loff_t lock_len;
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB,
bool can_be_top = true, can_be_bottom = params->flags & SNOR_F_HAS_SR_TB,
can_be_cmp = spi_nor_get_sr_cmp_mask(nor);
bool use_top;
int ret;
u8 pow;
ret = spi_nor_read_sr(nor, nor->bouncebuf);
ret = spi_nor_read_sr1_and_sr2_careful(nor, status_old);
if (ret)
return ret;
status_old[0] = nor->bouncebuf[0];
if (!(nor->flags & SNOR_F_NO_READ_CR)) {
ret = spi_nor_read_cr(nor, nor->bouncebuf);
if (ret)
return ret;
status_old[1] = nor->bouncebuf[0];
}
/* If nothing in our range is unlocked, we don't need to do anything */
if (spi_nor_is_locked_sr(nor, ofs, len, status_old))
return 0;
@ -313,7 +342,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
lock_len = ofs + len;
if (lock_len == nor->params->size)
pow = (nor->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0);
pow = (params->flags & SNOR_F_HAS_4BIT_BP) ? GENMASK(3, 0) : GENMASK(2, 0);
else
pow = ilog2(lock_len) - ilog2(min_prot_len) + 1;
@ -359,7 +388,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
* wrongly tied to GND (that includes internal pull-downs).
* WP# pin hard strapped to GND can be a valid use case.
*/
if (!(nor->flags & SNOR_F_NO_WP))
if (!(params->flags & SNOR_F_NO_WP))
best_status_new[0] |= SR_SRWD;
spi_nor_get_locked_range_sr(nor, status_old, &ofs_old, &len_old);
@ -378,10 +407,7 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
(ofs_old < ofs_new || (ofs_new + len_new) < (ofs_old + len_old)))
return -EINVAL;
if (nor->flags & SNOR_F_NO_READ_CR)
ret = spi_nor_write_sr_and_check(nor, best_status_new[0]);
else
ret = spi_nor_write_sr_cr_and_check(nor, best_status_new);
ret = spi_nor_write_sr1_and_sr2_and_check(nor, best_status_new);
if (ret)
return ret;
@ -397,32 +423,23 @@ static int spi_nor_sr_lock(struct spi_nor *nor, loff_t ofs, u64 len)
*/
static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
{
struct spi_nor_flash_parameter *params = nor->params;
u64 min_prot_len = spi_nor_get_min_prot_length_sr(nor);
u8 status_old[2] = {}, status_new[2] = {}, status_new_cmp[2] = {};
u8 *best_status_new = status_new;
loff_t ofs_old, ofs_new, ofs_new_cmp;
u64 len_old, len_new, len_new_cmp;
loff_t lock_len;
bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB,
bool can_be_top = true, can_be_bottom = params->flags & SNOR_F_HAS_SR_TB,
can_be_cmp = spi_nor_get_sr_cmp_mask(nor);
bool use_top;
int ret;
u8 pow;
ret = spi_nor_read_sr(nor, nor->bouncebuf);
ret = spi_nor_read_sr1_and_sr2_careful(nor, status_old);
if (ret)
return ret;
status_old[0] = nor->bouncebuf[0];
if (!(nor->flags & SNOR_F_NO_READ_CR)) {
ret = spi_nor_read_cr(nor, nor->bouncebuf);
if (ret)
return ret;
status_old[1] = nor->bouncebuf[0];
}
/* If nothing in our range is locked, we don't need to do anything */
if (spi_nor_is_unlocked_sr(nor, ofs, len, status_old))
return 0;
@ -512,10 +529,7 @@ static int spi_nor_sr_unlock(struct spi_nor *nor, loff_t ofs, u64 len)
(ofs_new < ofs_old || (ofs_old + len_old) < (ofs_new + len_new)))
return -EINVAL;
if (nor->flags & SNOR_F_NO_READ_CR)
ret = spi_nor_write_sr_and_check(nor, best_status_new[0]);
else
ret = spi_nor_write_sr_cr_and_check(nor, best_status_new);
ret = spi_nor_write_sr1_and_sr2_and_check(nor, best_status_new);
if (ret)
return ret;
@ -536,20 +550,10 @@ static int spi_nor_sr_is_locked(struct spi_nor *nor, loff_t ofs, u64 len)
u8 sr_cr[2] = {};
int ret;
ret = spi_nor_read_sr(nor, nor->bouncebuf);
ret = spi_nor_read_sr1_and_sr2_careful(nor, sr_cr);
if (ret)
return ret;
sr_cr[0] = nor->bouncebuf[0];
if (!(nor->flags & SNOR_F_NO_READ_CR)) {
ret = spi_nor_read_cr(nor, nor->bouncebuf);
if (ret)
return ret;
sr_cr[1] = nor->bouncebuf[0];
}
return spi_nor_is_locked_sr(nor, ofs, len, sr_cr);
}
@ -628,9 +632,10 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, u64 len)
*/
void spi_nor_try_unlock_all(struct spi_nor *nor)
{
struct spi_nor_flash_parameter *params = nor->params;
int ret;
if (!(nor->flags & SNOR_F_HAS_LOCK))
if (!(params->flags & SNOR_F_HAS_LOCK))
return;
dev_dbg(nor->dev, "Unprotecting entire flash array\n");

View File

@ -25,7 +25,7 @@ static ssize_t partname_show(struct device *dev,
struct spi_mem *spimem = spi_get_drvdata(spi);
struct spi_nor *nor = spi_mem_get_drvdata(spimem);
return sysfs_emit(buf, "%s\n", nor->info->name);
return sysfs_emit(buf, "%s\n", nor->partname);
}
static DEVICE_ATTR_RO(partname);
@ -78,7 +78,7 @@ 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)
if (attr == &dev_attr_partname.attr && !nor->partname)
return 0;
if (attr == &dev_attr_jedec_id.attr && !nor->info->id && !nor->id)
return 0;

View File

@ -24,20 +24,94 @@
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, buf, 0))
static int
w25q128_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
static bool is_w25qxxrv(const struct spi_nor *nor)
{
struct sfdp_header *sfdp_h = spi_nor_sfdp_get_header(nor);
/*
* W25QxxRV chips re-use the same ID as the W25QxxJV family.
*
* Chips are very similar, W25QxxRV brings mostly performance and power
* consumption improvements. The RV family does not require the multi
* die fixup.
*
* They can be distinguished based on their SFDP minor revision:
* W25QxxJV: JESD216A, minor revision == 05h
* W25Q512/01/02JV: JESD216B, minor revision == 06h
* W25QxxRV: JESD216F, minor revision >= 0Ah
*/
return sfdp_h->minor >= SFDP_JESD216F_MINOR;
}
static bool is_zd25q128c(const struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header)
{
/*
* Zetta ZD25Q128C is a clone of the Winbond device. But the encoded
* size is really wrong. It seems that they confused Mbit with MiB.
* Thus the flash is discovered as a 2MiB device.
*/
if (bfpt_header->major == SFDP_JESD216_MAJOR &&
bfpt_header->minor == SFDP_JESD216_MINOR &&
nor->params->size == SZ_2M &&
nor->params->erase_map.regions[0].size == SZ_2M) {
return bfpt_header->major == SFDP_JESD216_MAJOR &&
bfpt_header->minor == SFDP_JESD216_MINOR &&
nor->params->size == SZ_2M &&
nor->params->erase_map.regions[0].size == SZ_2M;
}
/*
* Since SFDP is populated after ->default_init(), the match functions using
* nor->sfdp as discriminant cannot be used for this specific early fixup.
*/
static bool winbond_jv_match(const struct spi_nor *nor)
{
return !nor->sfdp || !is_w25qxxrv(nor);
}
static bool winbond_rv_match(const struct spi_nor *nor)
{
return nor->sfdp && is_w25qxxrv(nor);
}
static bool is_w25qxxpw(const struct spi_nor *nor)
{
struct sfdp_header *sfdp_h = spi_nor_sfdp_get_header(nor);
/*
* W25QxxPW chips re-use the same ID as the W25QxxJW/NW family.
*
* Chips are very similar, W25QxxPW brings mostly performance and power
* consumption improvements. One key difference in behaviour is the
* automatic 16-byte based error correction.
*
* They can be distinguished based on their SFDP minor revision:
* W25QxxJW: JESD216B, minor revision == 06h
* W25Q51/01/02NW: JESD216B, minor revision == 06h
* W25QxxPW: JESD216F, minor revision >= 0Ah
*/
return sfdp_h->minor >= SFDP_JESD216F_MINOR;
}
static bool winbond_pw_match(const struct spi_nor *nor)
{
return nor->sfdp && is_w25qxxpw(nor);
}
static bool winbond_pw_with_ecc_match(const struct spi_nor *nor)
{
const struct spi_nor_id *id = nor->info->id;
if (!winbond_pw_match(nor))
return false;
/* W25QxxPW densities <= 32 Mbit (id[2] <= 0x16) do not have built-in ECC support */
return id->len == 3 && id->bytes[2] >= 0x17;
}
static int
w25q128_post_bfpt_fixups(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
if (is_zd25q128c(nor, bfpt_header) || winbond_rv_match(nor)) {
nor->params->size = SZ_16M;
nor->params->erase_map.regions[0].size = SZ_16M;
}
@ -64,7 +138,7 @@ w25q256_post_bfpt_fixups(struct spi_nor *nor,
*/
if (bfpt_header->major == SFDP_JESD216_MAJOR &&
bfpt_header->minor == SFDP_JESD216A_MINOR)
nor->flags |= SNOR_F_4B_OPCODES;
nor->params->flags |= SNOR_F_4B_OPCODES;
return 0;
}
@ -73,26 +147,6 @@ static const struct spi_nor_fixups w25q256_fixups = {
.post_bfpt = w25q256_post_bfpt_fixups,
};
static int
winbond_rdcr_post_bfpt_fixup(struct spi_nor *nor,
const struct sfdp_parameter_header *bfpt_header,
const struct sfdp_bfpt *bfpt)
{
/*
* W25H02NW, unlike its W25H512NW nor W25H01NW cousins, improperly sets
* the QE BFPT configuration bits, indicating a non readable CR. This is
* both incorrect and impractical, as the chip features a CMP bit for its
* locking scheme that lays in the Control Register, and needs to be read.
*/
nor->flags &= ~SNOR_F_NO_READ_CR;
return 0;
}
static const struct spi_nor_fixups winbond_rdcr_fixup = {
.post_bfpt = winbond_rdcr_post_bfpt_fixup,
};
/**
* winbond_nor_select_die() - Set active die.
* @nor: pointer to 'struct spi_nor'.
@ -166,6 +220,38 @@ static const struct spi_nor_fixups winbond_nor_multi_die_fixups = {
.post_sfdp = winbond_nor_multi_die_post_sfdp_fixups,
};
static int winbond_nor_partname_post_sfdp_fixups(struct spi_nor *nor)
{
/*
* W25QxxRV parts re-use the JEDEC IDs of the JV family. Their name
* being a legacy field, it is kept for the already established JV parts
* but must not be exposed by the newer RV ones.
*/
nor->partname = NULL;
return 0;
}
static const struct spi_nor_fixups winbond_nor_partname_fixups = {
.post_sfdp = winbond_nor_partname_post_sfdp_fixups,
};
static int winbond_nor_ecc_configuration_post_sfdp_fixups(struct spi_nor *nor)
{
/*
* PW chips feature automatic error correction. Non 16-byte aligned
* writes work, but disable error correction on the region until next erase.
*/
nor->params->writesize = 16;
nor->params->flags |= SNOR_F_ECC;
return 0;
}
static const struct spi_nor_fixups winbond_nor_ecc_configuration_fixups = {
.post_sfdp = winbond_nor_ecc_configuration_post_sfdp_fixups,
};
static const struct flash_info winbond_nor_parts[] = {
{
.id = SNOR_ID(0xef, 0x30, 0x10),
@ -218,38 +304,53 @@ static const struct flash_info winbond_nor_parts[] = {
.size = SZ_1M,
.no_sfdp_flags = SECT_4K,
}, {
/* W25Q32JV-Q/N, W25Q32RV-Q/N */
.id = SNOR_ID(0xef, 0x40, 0x16),
.name = "w25q32",
.size = SZ_4M,
.no_sfdp_flags = SECT_4K,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
}, {
/* W25Q64JV-Q/N, W25Q64RV-Q/N */
.id = SNOR_ID(0xef, 0x40, 0x17),
.name = "w25q64",
.size = SZ_8M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
}, {
/* W25Q128JV-Q/N, W25Q12RV-Q/N */
.id = SNOR_ID(0xef, 0x40, 0x18),
/* Flavors w/ and w/o SFDP. */
.name = "w25q128",
.size = SZ_16M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixups = &w25q128_fixups,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
}, {
/* W25Q256JV-Q/N, W25Q25RV-Q/N */
.id = SNOR_ID(0xef, 0x40, 0x19),
.name = "w25q256",
.size = SZ_32M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.fixups = &w25q256_fixups,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB |
SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q512JV-Q/N, W25Q51RV-Q/N */
.id = SNOR_ID(0xef, 0x40, 0x20),
.name = "w25q512jvq",
.size = SZ_64M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q01JV */
/* W25Q01JV-Q/N, W25Q01RV-Q/N */
.id = SNOR_ID(0xef, 0x40, 0x21),
.fixups = &winbond_nor_multi_die_fixups,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q02RV-Q/N */
.id = SNOR_ID(0xef, 0x40, 0x22),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
.id = SNOR_ID(0xef, 0x50, 0x12),
.name = "w25q20bw",
@ -272,133 +373,166 @@ static const struct flash_info winbond_nor_parts[] = {
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q32DW-P/G, W25Q32FW-G/Q, W25Q32JW-Q/N, W25Q33PW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x16),
.name = "w25q32dw",
.size = SZ_4M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
}, {
/* W25Q64DW-P/G, W25Q64FW-G/Q, W25Q64JW-Q/N, W25Q64PW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x17),
.name = "w25q64dw",
.size = SZ_8M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q128FW-G/Q, W25Q128JW-Q/N, W25Q12PW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x18),
.name = "w25q128fw",
.size = SZ_16M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q256JW-Q/N, W25Q25PW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x19),
.name = "w25q256jw",
.size = SZ_32M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q512NW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x20),
.name = "w25q512nwq",
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
}, {
/* W25Q01NW-Q/N */
.id = SNOR_ID(0xef, 0x60, 0x21),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q16JV-M */
.id = SNOR_ID(0xef, 0x70, 0x15),
.name = "w25q16jv-im/jm",
.size = SZ_2M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
}, {
/* W25Q32JV-M, W25Q32RV-M */
.id = SNOR_ID(0xef, 0x70, 0x16),
.name = "w25q32jv",
.size = SZ_4M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
}, {
/* W25Q64JV-M, W25Q64RV-M */
.id = SNOR_ID(0xef, 0x70, 0x17),
.name = "w25q64jvm",
.size = SZ_8M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.no_sfdp_flags = SECT_4K,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
}, {
/* W25Q128JV-M, W25Q12RV-M */
.id = SNOR_ID(0xef, 0x70, 0x18),
.name = "w25q128jv",
.size = SZ_16M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
}, {
/* W25Q256JV-M, W25Q25RV-M */
.id = SNOR_ID(0xef, 0x70, 0x19),
.name = "w25q256jvm",
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB |
SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q02JV */
/* W25Q512JV-M, W25Q51RV-M */
.id = SNOR_ID(0xef, 0x70, 0x20),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q01JV-M, W25Q01RV-M */
.id = SNOR_ID(0xef, 0x70, 0x21),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q02JV-M, W25Q02RV-M */
.id = SNOR_ID(0xef, 0x70, 0x22),
.fixups = &winbond_nor_multi_die_fixups,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
.id = SNOR_ID(0xef, 0x71, 0x19),
.name = "w25m512jv",
.size = SZ_64M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q32JW-M, W25Q33PW-M */
.id = SNOR_ID(0xef, 0x80, 0x16),
.name = "w25q32jwm",
.size = SZ_4M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
}, {
/* W25Q64JW-M, W25Q64PW-M */
.id = SNOR_ID(0xef, 0x80, 0x17),
.name = "w25q64jwm",
.size = SZ_8M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q128JW-M, W25Q12PW-M */
.id = SNOR_ID(0xef, 0x80, 0x18),
.name = "w25q128jwm",
.size = SZ_16M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
.flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q256JW-M, W25Q25PW-M */
.id = SNOR_ID(0xef, 0x80, 0x19),
.name = "w25q256jwm",
.size = SZ_32M,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 | SPI_NOR_4BIT_BP,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
}, {
/* W25Q512NW-M */
.id = SNOR_ID(0xef, 0x80, 0x20),
.name = "w25q512nwm",
.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
}, {
/* W25Q01NWxxIQ */
.id = SNOR_ID(0xef, 0x60, 0x21),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
.fixups = &winbond_rdcr_fixup,
}, {
/* W25Q01NWxxIM */
/* W25Q01NW-M */
.id = SNOR_ID(0xef, 0x80, 0x21),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25Q02NWxxIM */
/* W25Q02NW-M */
.id = SNOR_ID(0xef, 0x80, 0x22),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
.fixups = &winbond_rdcr_fixup,
}, {
/* W25H512NWxxAM */
/* W25H512NW-M */
.id = SNOR_ID(0xef, 0xa0, 0x20),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25H01NWxxAM */
/* W25H01NW-M */
.id = SNOR_ID(0xef, 0xa0, 0x21),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
}, {
/* W25H02NWxxAM */
/* W25H02NW-M */
.id = SNOR_ID(0xef, 0xa0, 0x22),
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6 |
SPI_NOR_4BIT_BP | SPI_NOR_HAS_CMP,
.fixups = &winbond_rdcr_fixup,
},
}, {
/*
* Catch all entry to make sure all chips solely relying
* on SFDP still go through the manufacturer hooks.
*/
.id = SNOR_ID(0xef),
}
};
/**
@ -490,6 +624,38 @@ static int winbond_nor_late_init(struct spi_nor *nor)
*/
params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
/*
* All W25Q/W25H chips do set the BFPT_DWORD15_QER_SR2_BIT1_NO_RD bit in
* their SFDP tables. The historical spi-nor assumption in this case has
* been to declare CR reads as unsupported, whereas the Jedec
* specification doesn't clearly state that. In practice, all these
* chips do support reading back the CR, which is needed for SWP support,
* so make sure that capability remains enabled.
* In practice, only exclude the old W25X family (JEDEC ID: EF 30 xx)
* which actually does not support this feature.
*/
if (nor->id[1] > 0x30)
params->opcodes.read_sr2 = SPINOR_OP_RDCR;
/*
* Winbond has reused many IDs, up to four times at this
* stage. In general, most of the chips with SFDP support are
* correctly described by the ID table, but the non-SFDP chips,
* however, are known to not feature as many capabilities. Make
* sure we filter out those capabilities to keep backward
* compatibility with these devices manufactured until ~2016.
*/
if (!nor->sfdp) {
struct spi_nor_flash_parameter *p = nor->params;
/* SPI_NOR_QUAD_PP was unsupported */
p->hwcaps.mask &= ~SNOR_HWCAPS_PP_1_1_4;
spi_nor_set_pp_settings(&p->page_programs[SNOR_CMD_PP_1_1_4], 0, 0);
/* SPI_NOR_HAS_CMP was unsupported */
nor->params->flags &= ~SNOR_F_HAS_SR2_CMP_BIT6;
}
return 0;
}
@ -497,9 +663,37 @@ static const struct spi_nor_fixups winbond_nor_fixups = {
.late_init = winbond_nor_late_init,
};
static const struct spi_nor_fixup winbond_fixups[] = {
{ .fixups = &winbond_nor_fixups },
{ .id = SNOR_ID(0xef, 0x40, 0x18), .fixups = &w25q128_fixups },
{ .id = SNOR_ID(0xef, 0x40, 0x19), .fixups = &w25q256_fixups },
{ .id = SNOR_ID(0xef, 0x40), .match = winbond_rv_match,
.fixups = &winbond_nor_partname_fixups },
{ .id = SNOR_ID(0xef, 0x40, 0x21), .match = winbond_jv_match,
.fixups = &winbond_nor_multi_die_fixups },
{ .id = SNOR_ID(0xef, 0x40, 0x22), .match = winbond_jv_match,
.fixups = &winbond_nor_multi_die_fixups },
{ .id = SNOR_ID(0xef, 0x60), .match = winbond_pw_with_ecc_match,
.fixups = &winbond_nor_ecc_configuration_fixups },
{ .id = SNOR_ID(0xef, 0x60), .match = winbond_pw_match,
.fixups = &winbond_nor_partname_fixups },
{ .id = SNOR_ID(0xef, 0x70), .match = winbond_rv_match,
.fixups = &winbond_nor_partname_fixups },
{ .id = SNOR_ID(0xef, 0x70, 0x18), .fixups = &w25q128_fixups },
{ .id = SNOR_ID(0xef, 0x70, 0x21), .match = winbond_jv_match,
.fixups = &winbond_nor_multi_die_fixups },
{ .id = SNOR_ID(0xef, 0x70, 0x22), .match = winbond_jv_match,
.fixups = &winbond_nor_multi_die_fixups },
{ .id = SNOR_ID(0xef, 0x80), .match = winbond_pw_with_ecc_match,
.fixups = &winbond_nor_ecc_configuration_fixups },
{ .id = SNOR_ID(0xef, 0x80), .match = winbond_pw_match,
.fixups = &winbond_nor_partname_fixups },
};
const struct spi_nor_manufacturer spi_nor_winbond = {
.name = "winbond",
.parts = winbond_nor_parts,
.nparts = ARRAY_SIZE(winbond_nor_parts),
.fixups = &winbond_nor_fixups,
.fixups = winbond_fixups,
.nfixups = ARRAY_SIZE(winbond_fixups),
};

View File

@ -19,6 +19,7 @@ static const struct flash_info xmc_nor_parts[] = {
.name = "XM25QH128A",
.size = SZ_16M,
.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
.flags = SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP | SPI_NOR_HAS_TB,
},
};

View File

@ -75,7 +75,7 @@ struct nand_bbt_descr {
* with NAND_BBT_CREATE.
*/
#define NAND_BBT_CREATE_EMPTY 0x00000400
/* Write bbt if neccecary */
/* Write bbt if necessary */
#define NAND_BBT_WRITE 0x00002000
/* Read and write back block contents when writing bbt */
#define NAND_BBT_SAVECONTENT 0x00004000

View File

@ -1,17 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Platform data for LPC32xx SoC MLC NAND controller
*
* Copyright © 2012 Roland Stigge
*/
#ifndef __LINUX_MTD_LPC32XX_MLC_H
#define __LINUX_MTD_LPC32XX_MLC_H
#include <linux/dmaengine.h>
struct lpc32xx_mlc_platform_data {
dma_filter_fn dma_filter;
};
#endif /* __LINUX_MTD_LPC32XX_MLC_H */

View File

@ -1,17 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Platform data for LPC32xx SoC SLC NAND controller
*
* Copyright © 2012 Roland Stigge
*/
#ifndef __LINUX_MTD_LPC32XX_SLC_H
#define __LINUX_MTD_LPC32XX_SLC_H
#include <linux/dmaengine.h>
struct lpc32xx_slc_platform_data {
dma_filter_fn dma_filter;
};
#endif /* __LINUX_MTD_LPC32XX_SLC_H */

View File

@ -262,7 +262,6 @@ struct bam_transaction {
u32 tx_sgl_start;
u32 rx_sgl_pos;
u32 rx_sgl_start;
);
};
@ -458,6 +457,12 @@ struct qcom_nandc_props {
bool use_codeword_fixup;
};
struct qcom_nandc_read_stats {
__le32 flash;
__le32 buffer;
__le32 erased_cw;
};
void qcom_free_bam_transaction(struct qcom_nand_controller *nandc);
struct bam_transaction *qcom_alloc_bam_transaction(struct qcom_nand_controller *nandc);
void qcom_clear_bam_transaction(struct qcom_nand_controller *nandc);
@ -483,5 +488,16 @@ int qcom_submit_descs(struct qcom_nand_controller *nandc);
void qcom_clear_read_regs(struct qcom_nand_controller *nandc);
void qcom_nandc_unalloc(struct qcom_nand_controller *nandc);
int qcom_nandc_alloc(struct qcom_nand_controller *nandc);
int qcom_nandc_check_flash_errors(struct qcom_nand_controller *nandc,
int cw_cnt);
void qcom_nandc_config_page_read(struct qcom_nand_controller *nandc);
void qcom_nandc_config_page_write(struct qcom_nand_controller *nandc);
void qcom_nandc_config_cw_write(struct qcom_nand_controller *nandc);
void qcom_nandc_set_read_loc_first(struct qcom_nand_controller *nandc,
int reg_base, u32 cw_offset,
u32 read_size, u32 is_last_read_loc);
void qcom_nandc_set_read_loc_last(struct qcom_nand_controller *nandc,
int reg_base, u32 cw_offset,
u32 read_size, u32 is_last_read_loc);
#endif

View File

@ -1592,7 +1592,7 @@ void nand_wait_ready(struct nand_chip *chip);
/*
* Free resources held by the NAND device, must be called on error after a
* sucessful nand_scan().
* successful nand_scan().
*/
void nand_cleanup(struct nand_chip *chip);

View File

@ -60,8 +60,8 @@
* Some hardware uses bits called PULSEx instead of FCKSEL_E and QTSEL_E
* to control the clock divider used between the High-Speed Peripheral Clock
* and the FLCTL internal clock. If so, use CLK_8_BIT_xxx for connecting 8 bit
* and CLK_16_BIT_xxx for connecting 16 bit bus bandwith NAND chips. For the 16
* bit version the divider is seperate for the pulse width of high and low
* and CLK_16_BIT_xxx for connecting 16 bit bus bandwidth NAND chips. For the 16
* bit version the divider is separate for the pulse width of high and low
* signals.
*/
#define PULSE3 (0x1 << 27)

View File

@ -25,6 +25,7 @@
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 */
#define SPINOR_OP_RDSR2 0x3f /* Read status register 2 */
#define SPINOR_OP_WRSR2 0x3e /* Write status register 2 */
#define SPINOR_OP_WRSR2_ALT 0x31 /* Write status register 2 (alternative) */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
@ -113,20 +114,16 @@
#define SR_E_ERR BIT(5)
#define SR_P_ERR BIT(6)
#define SR1_QUAD_EN_BIT6 BIT(6)
#define SR_BP_SHIFT 2
/* Enhanced Volatile Configuration Register bits */
#define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */
/* Status Register 2 bits. */
#define SR2_QUAD_EN_BIT1 BIT(1)
#define SR2_LB1 BIT(3) /* Security Register Lock Bit 1 */
#define SR2_LB2 BIT(4) /* Security Register Lock Bit 2 */
#define SR2_LB3 BIT(5) /* Security Register Lock Bit 3 */
#define SR2_CMP_BIT6 BIT(6)
#define SR2_QUAD_EN_BIT7 BIT(7)
/* Supported SPI protocols */
#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
@ -365,8 +362,6 @@ struct spi_nor_flash_parameter;
* @read_dummy: the dummy needed by the read operation
* @program_opcode: the program opcode
* @sst_write_second: used by the SST write operation
* @flags: flag options for the current SPI NOR (SNOR_F_*)
* @cmd_ext_type: the command opcode extension type for DTR mode.
* @read_proto: the SPI protocol for read operations
* @write_proto: the SPI protocol for write operations
* @reg_proto: the SPI protocol for read_reg/write_reg/erase operations
@ -398,6 +393,7 @@ struct spi_nor {
u8 *id;
const struct flash_info *info;
const struct spi_nor_manufacturer *manufacturer;
const char *partname;
u8 addr_nbytes;
u8 erase_opcode;
u8 read_opcode;
@ -407,8 +403,6 @@ struct spi_nor {
enum spi_nor_protocol write_proto;
enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
enum spi_nor_cmd_ext cmd_ext_type;
struct sfdp *sfdp;
struct dentry *debugfs_root;
u8 dfs_sr_cache[2];

View File

@ -399,7 +399,7 @@ struct spinand_devid {
};
/**
* struct manufacurer_ops - SPI NAND manufacturer specific operations
* struct spinand_manufacturer_ops - SPI NAND manufacturer specific operations
* @init: initialize a SPI NAND device
* @cleanup: cleanup a SPI NAND device
*
@ -438,6 +438,7 @@ extern const struct spinand_manufacturer fmsh_spinand_manufacturer;
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer heyangtek_spinand_manufacturer;
extern const struct spinand_manufacturer issi_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer paragon_spinand_manufacturer;