This pull-request contains the following changes for MTD:

MTD core changes:
 - add debugfs nodes for querying the flash name and id
 - mtd parser reorganization
 
 SPI NOR core changes:
 - always use bounce buffer for register read/writes
 - move m25p80 code in spi-nor.c
 - rework hwcaps selection for the spi-mem case
 - rework the core in order to move the manufacturer specific code
   out of it:
     - regroup flash parameters in 'struct spi_nor_flash_parameter'
     - add default_init() and post_sfdp() hooks to tweak the flash
       parameters
     - introduce the ->set_4byte(), ->convert_addr() and ->setup()
       methods, to deal with manufacturer specific code
     - rework the SPI NOR lock/unlock logic
 - fix an error code in spi_nor_read_raw()
 - fix a memory leak bug
 - enable the debugfs for the partname and partid
 - add support for few flashes
 
 SPI NOR controller drivers changes:
 - intel-spi:
     - Whitelist 4B read commands
     - Add support for Intel Tiger Lake SPI serial flash
 - aspeed-smc: Add of_node_put()
 - hisi-sfc: Add of_node_put()
 - cadence-quadspi: Fix QSPI RCU Schedule Stall
 
 NAND core:
 - Fixing typos
 - Adding missing of_node_put() in various drivers
 
 Raw NAND controller drivers:
 
 - Macronix: new controller driver
 - Omap2: Fixing the number of bitflips returned
 - Brcmnand: Fix a pointer not iterating over all the page chunks
 - W90x900: Driver removed
 - Onenand: Fix a memory leak
 - Sharpsl: Missing include guard
 - STM32: Avoid warnings when building with W=1
 - Ingenic: Fix a coccinelle warning
 - r852: Call a helper to simplify the code
 
 CFI core:
 - Kill useless initializer in mtd_do_chip_probe()
 - Fix a rare write failure seen on some cfi_cmdset_0002 compliant
   Parallel NORs
 - Bunch of cleanups for cfi_cmdset_0002 driver's write functions by
   Tokunori Ikegami <ikegami.t@gmail.com>
 -----BEGIN PGP SIGNATURE-----
 
 iQJKBAABCAA0FiEEdgfidid8lnn52cLTZvlZhesYu8EFAl2FyfUWHHJpY2hhcmRA
 c2lnbWEtc3Rhci5hdAAKCRBm+VmF6xi7wQ+2D/9CzeK9eK27kwC5P1JUDyAVCqQL
 09QDJ5/NF9Enrs7wtiLfZmadqo1SOFn98HVy9KCI9g0UkW9YzZjpAH3FfJCgYeaA
 t4wcDx6IXH8sKoqihyKnGN2IBPXCHzW6W81OUUmArpQ5ShqgJROQ9oo4tGukzDpw
 vnEShdHsrCe65/jqRTghl17i+AfzsD/IEjJLp/unBLsxa4sJqOjE7x3oizBtNi9H
 icBdJP1LocVEUDIxSFRgUgftWTW4xhIEmlS/BSBcX8dQ0cs0AeQ0Nr0gMw+sLzyq
 FFFwaRY7nedib7Q551HEzvd5YrWNc3/J//Qc+/aJMG8aE0sBSXx3fqpKHr9SPZRl
 7lYeT2lbvT3V1m2HVPIfJAR2rX9D5kTOWb+5TLkdVW3DKxIO+nsEIjc1koZYLu/A
 KM0KoqdVny+TsFOnDA3PcEzF+wiqxq/+TYj5EP8f8RfeGdQD1evbA3wwDVcPexQ6
 B0WEIrgAA4DWTcD34X1Fp8EOclRMgreQIb3/+Mi/MAUbIwmMPdHKJxW9dYWyC9cd
 /vCjJ+nvRg9OjFrox3E52JCMN8CeaCEXMwtzJDgBslSGLCkyg4EqetjVr7nkp1qE
 6u3EM+tzA18h9ltG3s2SNHK9kaQCW1kGbx58Mkh9N8he6Aa7shVH9O1sZnNF4xmz
 gR43ERplKI/KGARTrQ==
 =fMNy
 -----END PGP SIGNATURE-----

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

Pull MTD updates from Richard Weinberger:
 "MTD core changes:
   - add debugfs nodes for querying the flash name and id
   - mtd parser reorganization

  SPI NOR core changes:
   - always use bounce buffer for register read/writes
   - move m25p80 code in spi-nor.c
   - rework hwcaps selection for the spi-mem case
   - rework the core in order to move the manufacturer specific code out
     of it:
       - regroup flash parameters in 'struct spi_nor_flash_parameter'
       - add default_init() and post_sfdp() hooks to tweak the flash
         parameters
       - introduce the ->set_4byte(), ->convert_addr() and ->setup()
         methods, to deal with manufacturer specific code
       - rework the SPI NOR lock/unlock logic
   - fix an error code in spi_nor_read_raw()
   - fix a memory leak bug
   - enable the debugfs for the partname and partid
   - add support for few flashes

  SPI NOR controller drivers changes:
   - intel-spi:
       - Whitelist 4B read commands
       - Add support for Intel Tiger Lake SPI serial flash
   - aspeed-smc: Add of_node_put()
   - hisi-sfc: add of_node_put()
   - cadence-quadspi: Fix QSPI RCU Schedule Stall

  NAND core:
   - Fixing typos
   - Adding missing of_node_put() in various drivers

  Raw NAND controller drivers:
   - Macronix: new controller driver
   - Omap2: fix the number of bitflips returned
   - Brcmnand: fix a pointer not iterating over all the page chunks
   - W90x900: driver removed
   - Onenand: fix a memory leak
   - Sharpsl: missing include guard
   - STM32: avoid warnings when building with W=1
   - Ingenic: fix a coccinelle warning
   - r852: call a helper to simplify the code

  CFI core:
   - Kill useless initializer in mtd_do_chip_probe()
   - Fix a rare write failure seen on some cfi_cmdset_0002 compliant
     Parallel NORs
   - Bunch of cleanups for cfi_cmdset_0002 driver's write functions by
     Tokunori Ikegami"

* tag 'mtd/for-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (77 commits)
  mtd: pmc551: Remove set but not used variable 'soff_lo'
  mtd: cfi_cmdset_0002: Fix do_erase_chip() to get chip as erasing mode
  mtd: sm_ftl: Fix memory leak in sm_init_zone() error path
  mtd: parsers: Move CMDLINE parser
  mtd: parsers: Move OF parser
  mtd: parsers: Move BCM63xx parser
  mtd: parsers: Move BCM47xx parser
  mtd: parsers: Move TI AR7 parser
  mtd: pismo: Simplify getting the adapter of a client
  mtd: phram: Module parameters add writable permissions
  mtd: pxa2xx: Use ioremap_cache insted of ioremap_cached
  mtd: spi-nor: Rename "n25q512a" to "mt25qu512a (n25q512a)"
  mtd: spi-nor: Add support for mt35xu02g
  mtd: rawnand: omap2: Fix number of bitflips reporting with ELM
  mtd: rawnand: brcmnand: Fix ecc chunk calculation for erased page bitfips
  mtd: spi-nor: remove superfluous pass of nor->info->sector_size
  mtd: spi-nor: enable the debugfs for the partname and partid
  mtd: mtdcore: add debugfs nodes for querying the flash name and id
  mtd: spi-nor: hisi-sfc: Add of_node_put() before break
  mtd: spi-nor: aspeed-smc: Add of_node_put()
  ...
This commit is contained in:
Linus Torvalds 2019-09-21 10:59:54 -07:00
commit 4553d469d6
48 changed files with 2590 additions and 1560 deletions

View File

@ -0,0 +1,36 @@
Macronix Raw NAND Controller Device Tree Bindings
-------------------------------------------------
Required properties:
- compatible: should be "mxic,multi-itfc-v009-nand-controller"
- reg: should contain 1 entry for the registers
- #address-cells: should be set to 1
- #size-cells: should be set to 0
- interrupts: interrupt line connected to this raw NAND controller
- clock-names: should contain "ps", "send" and "send_dly"
- clocks: should contain 3 phandles for the "ps", "send" and
"send_dly" clocks
Children nodes:
- children nodes represent the available NAND chips.
See Documentation/devicetree/bindings/mtd/nand-controller.yaml
for more details on generic bindings.
Example:
nand: nand-controller@43c30000 {
compatible = "mxic,multi-itfc-v009-nand-controller";
reg = <0x43c30000 0x10000>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 0x1d IRQ_TYPE_EDGE_RISING>;
clocks = <&clkwizard 0>, <&clkwizard 1>, <&clkc 15>;
clock-names = "send", "send_dly", "ps";
nand@0 {
reg = <0>;
nand-ecc-mode = "soft";
nand-ecc-algo = "bch";
};
};

View File

@ -23,73 +23,6 @@ config MTD_TESTS
WARNING: some of the tests will ERASE entire MTD device which they WARNING: some of the tests will ERASE entire MTD device which they
test. Do not use these tests unless you really know what you do. test. Do not use these tests unless you really know what you do.
config MTD_CMDLINE_PARTS
tristate "Command line partition table parsing"
depends on MTD
help
Allow generic configuration of the MTD partition tables via the kernel
command line. Multiple flash resources are supported for hardware where
different kinds of flash memory are available.
You will still need the parsing functions to be called by the driver
for your particular device. It won't happen automatically. The
SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
example.
The format for the command line is as follows:
mtdparts=<mtddef>[;<mtddef]
<mtddef> := <mtd-id>:<partdef>[,<partdef>]
<partdef> := <size>[@offset][<name>][ro]
<mtd-id> := unique id used in mapping driver/device
<size> := standard linux memsize OR "-" to denote all
remaining space
<name> := (NAME)
Due to the way Linux handles the command line, no spaces are
allowed in the partition definition, including mtd id's and partition
names.
Examples:
1 flash resource (mtd-id "sa1100"), with 1 single writable partition:
mtdparts=sa1100:-
Same flash, but 2 named partitions, the first one being read-only:
mtdparts=sa1100:256k(ARMboot)ro,-(root)
If unsure, say 'N'.
config MTD_OF_PARTS
tristate "OpenFirmware partitioning information support"
default y
depends on OF
help
This provides a partition parsing function which derives
the partition map from the children of the flash node,
as described in Documentation/devicetree/bindings/mtd/partition.txt.
config MTD_AR7_PARTS
tristate "TI AR7 partitioning support"
help
TI AR7 partitioning support
config MTD_BCM63XX_PARTS
tristate "BCM63XX CFE partitioning support"
depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
select CRC32
select MTD_PARSER_IMAGETAG
help
This provides partition parsing for BCM63xx devices with CFE
bootloaders.
config MTD_BCM47XX_PARTS
tristate "BCM47XX partitioning support"
depends on BCM47XX || ARCH_BCM_5301X
help
This provides partitions parser for devices based on BCM47xx
boards.
menu "Partition parsers" menu "Partition parsers"
source "drivers/mtd/parsers/Kconfig" source "drivers/mtd/parsers/Kconfig"
endmenu endmenu

View File

@ -7,11 +7,6 @@
obj-$(CONFIG_MTD) += mtd.o obj-$(CONFIG_MTD) += mtd.o
mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o mtd-y := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o
obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
obj-y += parsers/ obj-y += parsers/
# 'Users' - code which presents functionality to userspace. # 'Users' - code which presents functionality to userspace.

View File

@ -61,7 +61,9 @@
static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
#if !FORCE_WORD_WRITE
static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
#endif
static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *);
static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *);
static void cfi_amdstd_sync (struct mtd_info *); static void cfi_amdstd_sync (struct mtd_info *);
@ -256,6 +258,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd)
} }
#endif #endif
#if !FORCE_WORD_WRITE
static void fixup_use_write_buffers(struct mtd_info *mtd) static void fixup_use_write_buffers(struct mtd_info *mtd)
{ {
struct map_info *map = mtd->priv; struct map_info *map = mtd->priv;
@ -265,6 +268,7 @@ static void fixup_use_write_buffers(struct mtd_info *mtd)
mtd->_write = cfi_amdstd_write_buffers; mtd->_write = cfi_amdstd_write_buffers;
} }
} }
#endif /* !FORCE_WORD_WRITE */
/* Atmel chips don't use the same PRI format as AMD chips */ /* Atmel chips don't use the same PRI format as AMD chips */
static void fixup_convert_atmel_pri(struct mtd_info *mtd) static void fixup_convert_atmel_pri(struct mtd_info *mtd)
@ -1637,11 +1641,11 @@ static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
do_otp_lock, 1); do_otp_lock, 1);
} }
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, static int __xipram do_write_oneword_once(struct map_info *map,
unsigned long adr, map_word datum, struct flchip *chip,
int mode) unsigned long adr, map_word datum,
int mode, struct cfi_private *cfi)
{ {
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ; unsigned long timeo = jiffies + HZ;
/* /*
* We use a 1ms + 1 jiffies generic timeout for writes (most devices * We use a 1ms + 1 jiffies generic timeout for writes (most devices
@ -1654,42 +1658,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
*/ */
unsigned long uWriteTimeout = (HZ / 1000) + 1; unsigned long uWriteTimeout = (HZ / 1000) + 1;
int ret = 0; int ret = 0;
map_word oldd;
int retry_cnt = 0;
adr += chip->start;
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, adr, mode);
if (ret) {
mutex_unlock(&chip->mutex);
return ret;
}
pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
__func__, adr, datum.x[0]);
if (mode == FL_OTP_WRITE)
otp_enter(map, chip, adr, map_bankwidth(map));
/*
* Check for a NOP for the case when the datum to write is already
* present - it saves time and works around buggy chips that corrupt
* data at other locations when 0xff is written to a location that
* already contains 0xff.
*/
oldd = map_read(map, adr);
if (map_word_equal(map, oldd, datum)) {
pr_debug("MTD %s(): NOP\n",
__func__);
goto op_done;
}
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map);
xip_disable(map, chip, adr);
retry:
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@ -1717,40 +1686,125 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
continue; continue;
} }
/*
* We check "time_after" and "!chip_good" before checking
* "chip_good" to avoid the failure due to scheduling.
*/
if (time_after(jiffies, timeo) && if (time_after(jiffies, timeo) &&
!chip_ready(map, chip, adr)) { !chip_good(map, chip, adr, datum)) {
xip_enable(map, chip, adr); xip_enable(map, chip, adr);
printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
xip_disable(map, chip, adr); xip_disable(map, chip, adr);
ret = -EIO;
break; break;
} }
if (chip_ready(map, chip, adr)) if (chip_good(map, chip, adr, datum))
break; break;
/* Latency issues. Drop the lock, wait a while and retry */ /* Latency issues. Drop the lock, wait a while and retry */
UDELAY(map, chip, adr, 1); UDELAY(map, chip, adr, 1);
} }
/* Did we succeed? */
if (!chip_good(map, chip, adr, datum)) { return ret;
}
static int __xipram do_write_oneword_start(struct map_info *map,
struct flchip *chip,
unsigned long adr, int mode)
{
int ret = 0;
mutex_lock(&chip->mutex);
ret = get_chip(map, chip, adr, mode);
if (ret) {
mutex_unlock(&chip->mutex);
return ret;
}
if (mode == FL_OTP_WRITE)
otp_enter(map, chip, adr, map_bankwidth(map));
return ret;
}
static void __xipram do_write_oneword_done(struct map_info *map,
struct flchip *chip,
unsigned long adr, int mode)
{
if (mode == FL_OTP_WRITE)
otp_exit(map, chip, adr, map_bankwidth(map));
chip->state = FL_READY;
DISABLE_VPP(map);
put_chip(map, chip, adr);
mutex_unlock(&chip->mutex);
}
static int __xipram do_write_oneword_retry(struct map_info *map,
struct flchip *chip,
unsigned long adr, map_word datum,
int mode)
{
struct cfi_private *cfi = map->fldrv_priv;
int ret = 0;
map_word oldd;
int retry_cnt = 0;
/*
* Check for a NOP for the case when the datum to write is already
* present - it saves time and works around buggy chips that corrupt
* data at other locations when 0xff is written to a location that
* already contains 0xff.
*/
oldd = map_read(map, adr);
if (map_word_equal(map, oldd, datum)) {
pr_debug("MTD %s(): NOP\n", __func__);
return ret;
}
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map);
xip_disable(map, chip, adr);
retry:
ret = do_write_oneword_once(map, chip, adr, datum, mode, cfi);
if (ret) {
/* reset on all failures. */ /* reset on all failures. */
cfi_check_err_status(map, chip, adr); cfi_check_err_status(map, chip, adr);
map_write(map, CMD(0xF0), chip->start); map_write(map, CMD(0xF0), chip->start);
/* FIXME - should have reset delay before continuing */ /* FIXME - should have reset delay before continuing */
if (++retry_cnt <= MAX_RETRIES) if (++retry_cnt <= MAX_RETRIES) {
ret = 0;
goto retry; goto retry;
}
ret = -EIO;
} }
xip_enable(map, chip, adr); xip_enable(map, chip, adr);
op_done:
if (mode == FL_OTP_WRITE) return ret;
otp_exit(map, chip, adr, map_bankwidth(map)); }
chip->state = FL_READY;
DISABLE_VPP(map); static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
put_chip(map, chip, adr); unsigned long adr, map_word datum,
mutex_unlock(&chip->mutex); int mode)
{
int ret = 0;
adr += chip->start;
pr_debug("MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", __func__, adr,
datum.x[0]);
ret = do_write_oneword_start(map, chip, adr, mode);
if (ret)
return ret;
ret = do_write_oneword_retry(map, chip, adr, datum, mode);
do_write_oneword_done(map, chip, adr, mode);
return ret; return ret;
} }
@ -1879,6 +1933,78 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
return 0; return 0;
} }
#if !FORCE_WORD_WRITE
static int __xipram do_write_buffer_wait(struct map_info *map,
struct flchip *chip, unsigned long adr,
map_word datum)
{
unsigned long timeo;
unsigned long u_write_timeout;
int ret = 0;
/*
* Timeout is calculated according to CFI data, if available.
* See more comments in cfi_cmdset_0002().
*/
u_write_timeout = usecs_to_jiffies(chip->buffer_write_time_max);
timeo = jiffies + u_write_timeout;
for (;;) {
if (chip->state != FL_WRITING) {
/* Someone's suspended the write. Sleep */
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + (HZ / 2); /* FIXME */
mutex_lock(&chip->mutex);
continue;
}
/*
* We check "time_after" and "!chip_good" before checking
* "chip_good" to avoid the failure due to scheduling.
*/
if (time_after(jiffies, timeo) &&
!chip_good(map, chip, adr, datum)) {
ret = -EIO;
break;
}
if (chip_good(map, chip, adr, datum))
break;
/* Latency issues. Drop the lock, wait a while and retry */
UDELAY(map, chip, adr, 1);
}
return ret;
}
static void __xipram do_write_buffer_reset(struct map_info *map,
struct flchip *chip,
struct cfi_private *cfi)
{
/*
* Recovery from write-buffer programming failures requires
* the write-to-buffer-reset sequence. Since the last part
* of the sequence also works as a normal reset, we can run
* the same commands regardless of why we are here.
* See e.g.
* http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
*/
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
cfi->device_type, NULL);
cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
/* FIXME - should have reset delay before continuing */
}
/* /*
* FIXME: interleaved mode not tested, and probably not supported! * FIXME: interleaved mode not tested, and probably not supported!
@ -1888,13 +2014,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
int len) int len)
{ {
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
/*
* Timeout is calculated according to CFI data, if available.
* See more comments in cfi_cmdset_0002().
*/
unsigned long uWriteTimeout =
usecs_to_jiffies(chip->buffer_write_time_max);
int ret = -EIO; int ret = -EIO;
unsigned long cmd_adr; unsigned long cmd_adr;
int z, words; int z, words;
@ -1951,63 +2070,16 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
adr, map_bankwidth(map), adr, map_bankwidth(map),
chip->word_write_time); chip->word_write_time);
timeo = jiffies + uWriteTimeout; ret = do_write_buffer_wait(map, chip, adr, datum);
if (ret) {
for (;;) { cfi_check_err_status(map, chip, adr);
if (chip->state != FL_WRITING) { do_write_buffer_reset(map, chip, cfi);
/* Someone's suspended the write. Sleep */ pr_err("MTD %s(): software timeout, address:0x%.8lx.\n",
DECLARE_WAITQUEUE(wait, current); __func__, adr);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
mutex_unlock(&chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + (HZ / 2); /* FIXME */
mutex_lock(&chip->mutex);
continue;
}
/*
* We check "time_after" and "!chip_good" before checking "chip_good" to avoid
* the failure due to scheduling.
*/
if (time_after(jiffies, timeo) &&
!chip_good(map, chip, adr, datum))
break;
if (chip_good(map, chip, adr, datum)) {
xip_enable(map, chip, adr);
goto op_done;
}
/* Latency issues. Drop the lock, wait a while and retry */
UDELAY(map, chip, adr, 1);
} }
/*
* Recovery from write-buffer programming failures requires
* the write-to-buffer-reset sequence. Since the last part
* of the sequence also works as a normal reset, we can run
* the same commands regardless of why we are here.
* See e.g.
* http://www.spansion.com/Support/Application%20Notes/MirrorBit_Write_Buffer_Prog_Page_Buffer_Read_AN.pdf
*/
cfi_check_err_status(map, chip, adr);
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi,
cfi->device_type, NULL);
cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, chip->start, map, cfi,
cfi->device_type, NULL);
xip_enable(map, chip, adr); xip_enable(map, chip, adr);
/* FIXME - should have reset delay before continuing */
printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n",
__func__, adr);
ret = -EIO;
op_done:
chip->state = FL_READY; chip->state = FL_READY;
DISABLE_VPP(map); DISABLE_VPP(map);
put_chip(map, chip, adr); put_chip(map, chip, adr);
@ -2091,6 +2163,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
return 0; return 0;
} }
#endif /* !FORCE_WORD_WRITE */
/* /*
* Wait for the flash chip to become ready to write data * Wait for the flash chip to become ready to write data
@ -2344,7 +2417,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
adr = cfi->addr_unlock1; adr = cfi->addr_unlock1;
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING); ret = get_chip(map, chip, adr, FL_ERASING);
if (ret) { if (ret) {
mutex_unlock(&chip->mutex); mutex_unlock(&chip->mutex);
return ret; return ret;

View File

@ -20,7 +20,7 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
{ {
struct mtd_info *mtd = NULL; struct mtd_info *mtd;
struct cfi_private *cfi; struct cfi_private *cfi;
/* First probe the map to see if we have CFI stuff there. */ /* First probe the map to see if we have CFI stuff there. */

View File

@ -79,24 +79,6 @@ config MTD_DATAFLASH_OTP
other key product data. The second half is programmed with a other key product data. The second half is programmed with a
unique-to-each-chip bit pattern at the factory. unique-to-each-chip bit pattern at the factory.
config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI_MASTER && MTD_SPI_NOR
select SPI_MEM
help
This enables access to most modern SPI flash chips, used for
program and data storage. Series supported include Atmel AT26DF,
Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips
are supported as well. See the driver source for the current list,
or to add other chips.
Note that the original DataFlash chips (AT45 series, not AT26DF),
need an entirely different driver.
Set up your spi devices with the right board-specific platform data,
if you want to specify device partitioning or to use a device which
doesn't support the JEDEC ID instruction.
config MTD_MCHP23K256 config MTD_MCHP23K256
tristate "Microchip 23K256 SRAM" tristate "Microchip 23K256 SRAM"
depends on SPI_MASTER depends on SPI_MASTER

View File

@ -12,7 +12,6 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o obj-$(CONFIG_MTD_MCHP23K256) += mchp23k256.o
obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o
obj-$(CONFIG_MTD_SST25L) += sst25l.o obj-$(CONFIG_MTD_SST25L) += sst25l.o

View File

@ -1,347 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MTD SPI driver for ST M25Pxx (and similar) serial flash chips
*
* Author: Mike Lavender, mike@steroidmicros.com
*
* Copyright (c) 2005, Intec Automation Inc.
*
* Some parts are based on lart.c by Abraham Van Der Merwe
*
* Cleaned up and generalized based on mtd_dataflash.c
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
struct m25p {
struct spi_mem *spimem;
struct spi_nor spi_nor;
};
static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
struct m25p *flash = nor->priv;
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(len, NULL, 1));
void *scratchbuf;
int ret;
scratchbuf = kmalloc(len, GFP_KERNEL);
if (!scratchbuf)
return -ENOMEM;
op.data.buf.in = scratchbuf;
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret < 0)
dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
code);
else
memcpy(val, scratchbuf, len);
kfree(scratchbuf);
return ret;
}
static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, NULL, 1));
void *scratchbuf;
int ret;
scratchbuf = kmemdup(buf, len, GFP_KERNEL);
if (!scratchbuf)
return -ENOMEM;
op.data.buf.out = scratchbuf;
ret = spi_mem_exec_op(flash->spimem, &op);
kfree(scratchbuf);
return ret;
}
static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *buf)
{
struct m25p *flash = nor->priv;
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, buf, 1));
int ret;
/* get transfer protocols. */
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
op.addr.nbytes = 0;
ret = spi_mem_adjust_op_size(flash->spimem, &op);
if (ret)
return ret;
op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret)
return ret;
return op.data.nbytes;
}
/*
* Read an address range from the nor chip. The address range
* may be any size provided it is within the physical boundaries.
*/
static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *buf)
{
struct m25p *flash = nor->priv;
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
SPI_MEM_OP_DATA_IN(len, buf, 1));
size_t remaining = len;
int ret;
/* get transfer protocols. */
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
op.dummy.buswidth = op.addr.buswidth;
op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
/* convert the dummy cycles to the number of bytes */
op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
while (remaining) {
op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
ret = spi_mem_adjust_op_size(flash->spimem, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret)
return ret;
op.addr.val += op.data.nbytes;
remaining -= op.data.nbytes;
op.data.buf.in += op.data.nbytes;
}
return len;
}
/*
* board specific setup should have ensured the SPI clock used here
* matches what the READ command supports, at least until this driver
* understands FAST_READ (for clocks over 25 MHz).
*/
static int m25p_probe(struct spi_mem *spimem)
{
struct spi_device *spi = spimem->spi;
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
struct spi_nor_hwcaps hwcaps = {
.mask = SNOR_HWCAPS_READ |
SNOR_HWCAPS_READ_FAST |
SNOR_HWCAPS_PP,
};
char *flash_name;
int ret;
data = dev_get_platdata(&spimem->spi->dev);
flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL);
if (!flash)
return -ENOMEM;
nor = &flash->spi_nor;
/* install the hooks */
nor->read = m25p80_read;
nor->write = m25p80_write;
nor->write_reg = m25p80_write_reg;
nor->read_reg = m25p80_read_reg;
nor->dev = &spimem->spi->dev;
spi_nor_set_flash_node(nor, spi->dev.of_node);
nor->priv = flash;
spi_mem_set_drvdata(spimem, flash);
flash->spimem = spimem;
if (spi->mode & SPI_RX_OCTAL) {
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_8;
if (spi->mode & SPI_TX_OCTAL)
hwcaps.mask |= (SNOR_HWCAPS_READ_1_8_8 |
SNOR_HWCAPS_PP_1_1_8 |
SNOR_HWCAPS_PP_1_8_8);
} else if (spi->mode & SPI_RX_QUAD) {
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
if (spi->mode & SPI_TX_QUAD)
hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
SNOR_HWCAPS_PP_1_1_4 |
SNOR_HWCAPS_PP_1_4_4);
} else if (spi->mode & SPI_RX_DUAL) {
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
if (spi->mode & SPI_TX_DUAL)
hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
}
if (data && data->name)
nor->mtd.name = data->name;
if (!nor->mtd.name)
nor->mtd.name = spi_mem_get_name(spimem);
/* For some (historical?) reason many platforms provide two different
* names in flash_platform_data: "name" and "type". Quite often name is
* set to "m25p80" and then "type" provides a real chip name.
* If that's the case, respect "type" and ignore a "name".
*/
if (data && data->type)
flash_name = data->type;
else if (!strcmp(spi->modalias, "spi-nor"))
flash_name = NULL; /* auto-detect */
else
flash_name = spi->modalias;
ret = spi_nor_scan(nor, flash_name, &hwcaps);
if (ret)
return ret;
return mtd_device_register(&nor->mtd, data ? data->parts : NULL,
data ? data->nr_parts : 0);
}
static int m25p_remove(struct spi_mem *spimem)
{
struct m25p *flash = spi_mem_get_drvdata(spimem);
spi_nor_restore(&flash->spi_nor);
/* Clean up MTD stuff. */
return mtd_device_unregister(&flash->spi_nor.mtd);
}
static void m25p_shutdown(struct spi_mem *spimem)
{
struct m25p *flash = spi_mem_get_drvdata(spimem);
spi_nor_restore(&flash->spi_nor);
}
/*
* Do NOT add to this array without reading the following:
*
* Historically, many flash devices are bound to this driver by their name. But
* since most of these flash are compatible to some extent, and their
* differences can often be differentiated by the JEDEC read-ID command, we
* encourage new users to add support to the spi-nor library, and simply bind
* against a generic string here (e.g., "jedec,spi-nor").
*
* Many flash names are kept here in this list (as well as in spi-nor.c) to
* keep them available as module aliases for existing platforms.
*/
static const struct spi_device_id m25p_ids[] = {
/*
* Allow non-DT platform devices to bind to the "spi-nor" modalias, and
* hack around the fact that the SPI core does not provide uevent
* matching for .of_match_table
*/
{"spi-nor"},
/*
* Entries not used in DTs that should be safe to drop after replacing
* them with "spi-nor" in platform data.
*/
{"s25sl064a"}, {"w25x16"}, {"m25p10"}, {"m25px64"},
/*
* Entries that were used in DTs without "jedec,spi-nor" fallback and
* should be kept for backward compatibility.
*/
{"at25df321a"}, {"at25df641"}, {"at26df081a"},
{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
{"mx25l25635e"},{"mx66l51235l"},
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
{"s25fl256s1"}, {"s25fl512s"}, {"s25sl12801"}, {"s25fl008k"},
{"s25fl064k"},
{"sst25vf040b"},{"sst25vf016b"},{"sst25vf032b"},{"sst25wf040"},
{"m25p40"}, {"m25p80"}, {"m25p16"}, {"m25p32"},
{"m25p64"}, {"m25p128"},
{"w25x80"}, {"w25x32"}, {"w25q32"}, {"w25q32dw"},
{"w25q80bl"}, {"w25q128"}, {"w25q256"},
/* Flashes that can't be detected using JEDEC */
{"m25p05-nonjedec"}, {"m25p10-nonjedec"}, {"m25p20-nonjedec"},
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
/* Everspin MRAMs (non-JEDEC) */
{ "mr25h128" }, /* 128 Kib, 40 MHz */
{ "mr25h256" }, /* 256 Kib, 40 MHz */
{ "mr25h10" }, /* 1 Mib, 40 MHz */
{ "mr25h40" }, /* 4 Mib, 40 MHz */
{ },
};
MODULE_DEVICE_TABLE(spi, m25p_ids);
static const struct of_device_id m25p_of_table[] = {
/*
* Generic compatibility for SPI NOR that can be identified by the
* JEDEC READ ID opcode (0x9F). Use this, if possible.
*/
{ .compatible = "jedec,spi-nor" },
{}
};
MODULE_DEVICE_TABLE(of, m25p_of_table);
static struct spi_mem_driver m25p80_driver = {
.spidrv = {
.driver = {
.name = "m25p80",
.of_match_table = m25p_of_table,
},
.id_table = m25p_ids,
},
.probe = m25p_probe,
.remove = m25p_remove,
.shutdown = m25p_shutdown,
/* REVISIT: many of these chips have deep power-down modes, which
* should clearly be entered on suspend() to minimize power use.
* And also when they're otherwise idle...
*/
};
module_spi_mem_driver(m25p80_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Lavender");
MODULE_DESCRIPTION("MTD SPI driver for ST M25Pxx flash chips");

View File

@ -294,7 +294,7 @@ static int phram_param_call(const char *val, const struct kernel_param *kp)
#endif #endif
} }
module_param_call(phram, phram_param_call, NULL, NULL, 000); module_param_call(phram, phram_param_call, NULL, NULL, 0200);
MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\""); MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"");

View File

@ -135,7 +135,7 @@ static int pmc551_point(struct mtd_info *mtd, loff_t from, size_t len,
static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr) static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
struct mypriv *priv = mtd->priv; struct mypriv *priv = mtd->priv;
u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 soff_hi; /* start address offset hi */
u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */
unsigned long end; unsigned long end;
u_char *ptr; u_char *ptr;
@ -150,7 +150,6 @@ static int pmc551_erase(struct mtd_info *mtd, struct erase_info *instr)
eoff_hi = end & ~(priv->asize - 1); eoff_hi = end & ~(priv->asize - 1);
soff_hi = instr->addr & ~(priv->asize - 1); soff_hi = instr->addr & ~(priv->asize - 1);
eoff_lo = end & (priv->asize - 1); eoff_lo = end & (priv->asize - 1);
soff_lo = instr->addr & (priv->asize - 1);
pmc551_point(mtd, instr->addr, instr->len, &retlen, pmc551_point(mtd, instr->addr, instr->len, &retlen,
(void **)&ptr, NULL); (void **)&ptr, NULL);
@ -225,7 +224,7 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf) size_t * retlen, u_char * buf)
{ {
struct mypriv *priv = mtd->priv; struct mypriv *priv = mtd->priv;
u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 soff_hi; /* start address offset hi */
u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */
unsigned long end; unsigned long end;
u_char *ptr; u_char *ptr;
@ -239,7 +238,6 @@ static int pmc551_read(struct mtd_info *mtd, loff_t from, size_t len,
end = from + len - 1; end = from + len - 1;
soff_hi = from & ~(priv->asize - 1); soff_hi = from & ~(priv->asize - 1);
eoff_hi = end & ~(priv->asize - 1); eoff_hi = end & ~(priv->asize - 1);
soff_lo = from & (priv->asize - 1);
eoff_lo = end & (priv->asize - 1); eoff_lo = end & (priv->asize - 1);
pmc551_point(mtd, from, len, retlen, (void **)&ptr, NULL); pmc551_point(mtd, from, len, retlen, (void **)&ptr, NULL);
@ -282,7 +280,7 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t * retlen, const u_char * buf) size_t * retlen, const u_char * buf)
{ {
struct mypriv *priv = mtd->priv; struct mypriv *priv = mtd->priv;
u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 soff_hi; /* start address offset hi */
u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */
unsigned long end; unsigned long end;
u_char *ptr; u_char *ptr;
@ -296,7 +294,6 @@ static int pmc551_write(struct mtd_info *mtd, loff_t to, size_t len,
end = to + len - 1; end = to + len - 1;
soff_hi = to & ~(priv->asize - 1); soff_hi = to & ~(priv->asize - 1);
eoff_hi = end & ~(priv->asize - 1); eoff_hi = end & ~(priv->asize - 1);
soff_lo = to & (priv->asize - 1);
eoff_lo = end & (priv->asize - 1); eoff_lo = end & (priv->asize - 1);
pmc551_point(mtd, to, len, retlen, (void **)&ptr, NULL); pmc551_point(mtd, to, len, retlen, (void **)&ptr, NULL);

View File

@ -211,13 +211,12 @@ static int pismo_remove(struct i2c_client *client)
static int pismo_probe(struct i2c_client *client, static int pismo_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct pismo_pdata *pdata = client->dev.platform_data; struct pismo_pdata *pdata = client->dev.platform_data;
struct pismo_eeprom eeprom; struct pismo_eeprom eeprom;
struct pismo_data *pismo; struct pismo_data *pismo;
int ret, i; int ret, i;
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "functionality mismatch\n"); dev_err(&client->dev, "functionality mismatch\n");
return -EIO; return -EIO;
} }

View File

@ -68,8 +68,7 @@ static int pxa2xx_flash_probe(struct platform_device *pdev)
info->map.name); info->map.name);
return -ENOMEM; return -ENOMEM;
} }
info->map.cached = info->map.cached = ioremap_cache(info->map.phys, info->map.size);
ioremap_cached(info->map.phys, info->map.size);
if (!info->map.cached) if (!info->map.cached)
printk(KERN_WARNING "Failed to ioremap cached %s\n", printk(KERN_WARNING "Failed to ioremap cached %s\n",
info->map.name); info->map.name);

View File

@ -335,6 +335,82 @@ static const struct device_type mtd_devtype = {
.release = mtd_release, .release = mtd_release,
}; };
static int mtd_partid_show(struct seq_file *s, void *p)
{
struct mtd_info *mtd = s->private;
seq_printf(s, "%s\n", mtd->dbg.partid);
return 0;
}
static int mtd_partid_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, mtd_partid_show, inode->i_private);
}
static const struct file_operations mtd_partid_debug_fops = {
.open = mtd_partid_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int mtd_partname_show(struct seq_file *s, void *p)
{
struct mtd_info *mtd = s->private;
seq_printf(s, "%s\n", mtd->dbg.partname);
return 0;
}
static int mtd_partname_debugfs_open(struct inode *inode, struct file *file)
{
return single_open(file, mtd_partname_show, inode->i_private);
}
static const struct file_operations mtd_partname_debug_fops = {
.open = mtd_partname_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static struct dentry *dfs_dir_mtd;
static void mtd_debugfs_populate(struct mtd_info *mtd)
{
struct device *dev = &mtd->dev;
struct dentry *root, *dent;
if (IS_ERR_OR_NULL(dfs_dir_mtd))
return;
root = debugfs_create_dir(dev_name(dev), dfs_dir_mtd);
if (IS_ERR_OR_NULL(root)) {
dev_dbg(dev, "won't show data in debugfs\n");
return;
}
mtd->dbg.dfs_dir = root;
if (mtd->dbg.partid) {
dent = debugfs_create_file("partid", 0400, root, mtd,
&mtd_partid_debug_fops);
if (IS_ERR_OR_NULL(dent))
dev_err(dev, "can't create debugfs entry for partid\n");
}
if (mtd->dbg.partname) {
dent = debugfs_create_file("partname", 0400, root, mtd,
&mtd_partname_debug_fops);
if (IS_ERR_OR_NULL(dent))
dev_err(dev,
"can't create debugfs entry for partname\n");
}
}
#ifndef CONFIG_MMU #ifndef CONFIG_MMU
unsigned mtd_mmap_capabilities(struct mtd_info *mtd) unsigned mtd_mmap_capabilities(struct mtd_info *mtd)
{ {
@ -512,8 +588,6 @@ static int mtd_nvmem_add(struct mtd_info *mtd)
return 0; return 0;
} }
static struct dentry *dfs_dir_mtd;
/** /**
* add_mtd_device - register an MTD device * add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure * @mtd: pointer to new MTD device info structure
@ -607,13 +681,7 @@ int add_mtd_device(struct mtd_info *mtd)
if (error) if (error)
goto fail_nvmem_add; goto fail_nvmem_add;
if (!IS_ERR_OR_NULL(dfs_dir_mtd)) { mtd_debugfs_populate(mtd);
mtd->dbg.dfs_dir = debugfs_create_dir(dev_name(&mtd->dev), dfs_dir_mtd);
if (IS_ERR_OR_NULL(mtd->dbg.dfs_dir)) {
pr_debug("mtd device %s won't show data in debugfs\n",
dev_name(&mtd->dev));
}
}
device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL, device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
"mtd%dro", i); "mtd%dro", i);

View File

@ -3880,6 +3880,9 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
if (!this->oob_buf) { if (!this->oob_buf) {
if (this->options & ONENAND_PAGEBUF_ALLOC) { if (this->options & ONENAND_PAGEBUF_ALLOC) {
this->options &= ~ONENAND_PAGEBUF_ALLOC; this->options &= ~ONENAND_PAGEBUF_ALLOC;
#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
kfree(this->verify_buf);
#endif
kfree(this->page_buf); kfree(this->page_buf);
} }
return -ENOMEM; return -ENOMEM;

View File

@ -351,14 +351,6 @@ config MTD_NAND_SOCRATES
help help
Enables support for NAND Flash chips wired onto Socrates board. Enables support for NAND Flash chips wired onto Socrates board.
config MTD_NAND_NUC900
tristate "Nuvoton NUC9xx/w90p910 NAND controller"
depends on ARCH_W90X900 || COMPILE_TEST
depends on HAS_IOMEM
help
This enables the driver for the NAND Flash on evaluation board based
on w90p910 / NUC9xx.
source "drivers/mtd/nand/raw/ingenic/Kconfig" source "drivers/mtd/nand/raw/ingenic/Kconfig"
config MTD_NAND_FSMC config MTD_NAND_FSMC
@ -407,6 +399,12 @@ config MTD_NAND_MTK
Enables support for NAND controller on MTK SoCs. Enables support for NAND controller on MTK SoCs.
This controller is found on mt27xx, mt81xx, mt65xx SoCs. This controller is found on mt27xx, mt81xx, mt65xx SoCs.
config MTD_NAND_MXIC
tristate "Macronix raw NAND controller"
depends on HAS_IOMEM || COMPILE_TEST
help
This selects the Macronix raw NAND controller driver.
config MTD_NAND_TEGRA config MTD_NAND_TEGRA
tristate "NVIDIA Tegra NAND controller" tristate "NVIDIA Tegra NAND controller"
depends on ARCH_TEGRA || COMPILE_TEST depends on ARCH_TEGRA || COMPILE_TEST

View File

@ -41,7 +41,6 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o
obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o
obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o
obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o
obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o
obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_MTD_NAND_VF610_NFC) += vf610_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
@ -54,6 +53,7 @@ obj-$(CONFIG_MTD_NAND_HISI504) += hisi504_nand.o
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/ obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand/
obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o obj-$(CONFIG_MTD_NAND_QCOM) += qcom_nandc.o
obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o obj-$(CONFIG_MTD_NAND_MTK) += mtk_ecc.o mtk_nand.o
obj-$(CONFIG_MTD_NAND_MXIC) += mxic_nand.o
obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o obj-$(CONFIG_MTD_NAND_TEGRA) += tegra_nand.o
obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o obj-$(CONFIG_MTD_NAND_STM32_FMC2) += stm32_fmc2_nand.o
obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o obj-$(CONFIG_MTD_NAND_MESON) += meson_nand.o

View File

@ -1792,6 +1792,7 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
int bitflips = 0; int bitflips = 0;
int page = addr >> chip->page_shift; int page = addr >> chip->page_shift;
int ret; int ret;
void *ecc_chunk;
if (!buf) if (!buf)
buf = nand_get_data_buf(chip); buf = nand_get_data_buf(chip);
@ -1804,7 +1805,9 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd,
return ret; return ret;
for (i = 0; i < chip->ecc.steps; i++, oob += sas) { for (i = 0; i < chip->ecc.steps; i++, oob += sas) {
ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, ecc_chunk = buf + chip->ecc.size * i;
ret = nand_check_erased_ecc_chunk(ecc_chunk,
chip->ecc.size,
oob, sas, NULL, 0, oob, sas, NULL, 0,
chip->ecc.strength); chip->ecc.strength);
if (ret < 0) if (ret < 0)

View File

@ -310,7 +310,6 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct ingenic_nand *nand; struct ingenic_nand *nand;
struct ingenic_nand_cs *cs; struct ingenic_nand_cs *cs;
struct resource *res;
struct nand_chip *chip; struct nand_chip *chip;
struct mtd_info *mtd; struct mtd_info *mtd;
const __be32 *reg; const __be32 *reg;
@ -326,8 +325,7 @@ static int ingenic_nand_init_chip(struct platform_device *pdev,
jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND); jz4780_nemc_set_type(nfc->dev, cs->bank, JZ4780_NEMC_BANK_NAND);
res = platform_get_resource(pdev, IORESOURCE_MEM, chipnr); cs->base = devm_platform_ioremap_resource(pdev, chipnr);
cs->base = devm_ioremap_resource(dev, res);
if (IS_ERR(cs->base)) if (IS_ERR(cs->base))
return PTR_ERR(cs->base); return PTR_ERR(cs->base);
@ -418,6 +416,7 @@ static int ingenic_nand_init_chips(struct ingenic_nfc *nfc,
ret = ingenic_nand_init_chip(pdev, nfc, np, i); ret = ingenic_nand_init_chip(pdev, nfc, np, i);
if (ret) { if (ret) {
ingenic_nand_cleanup_chips(nfc); ingenic_nand_cleanup_chips(nfc);
of_node_put(np);
return ret; return ret;
} }

View File

@ -1320,6 +1320,7 @@ static int meson_nfc_nand_chips_init(struct device *dev,
ret = meson_nfc_nand_chip_init(dev, nfc, nand_np); ret = meson_nfc_nand_chip_init(dev, nfc, nand_np);
if (ret) { if (ret) {
meson_nfc_nand_chip_cleanup(nfc); meson_nfc_nand_chip_cleanup(nfc);
of_node_put(nand_np);
return ret; return ret;
} }
} }

View File

@ -0,0 +1,582 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 Macronix International Co., Ltd.
*
* Author:
* Mason Yang <masonccyang@mxic.com.tw>
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/platform_device.h>
#include "internals.h"
#define HC_CFG 0x0
#define HC_CFG_IF_CFG(x) ((x) << 27)
#define HC_CFG_DUAL_SLAVE BIT(31)
#define HC_CFG_INDIVIDUAL BIT(30)
#define HC_CFG_NIO(x) (((x) / 4) << 27)
#define HC_CFG_TYPE(s, t) ((t) << (23 + ((s) * 2)))
#define HC_CFG_TYPE_SPI_NOR 0
#define HC_CFG_TYPE_SPI_NAND 1
#define HC_CFG_TYPE_SPI_RAM 2
#define HC_CFG_TYPE_RAW_NAND 3
#define HC_CFG_SLV_ACT(x) ((x) << 21)
#define HC_CFG_CLK_PH_EN BIT(20)
#define HC_CFG_CLK_POL_INV BIT(19)
#define HC_CFG_BIG_ENDIAN BIT(18)
#define HC_CFG_DATA_PASS BIT(17)
#define HC_CFG_IDLE_SIO_LVL(x) ((x) << 16)
#define HC_CFG_MAN_START_EN BIT(3)
#define HC_CFG_MAN_START BIT(2)
#define HC_CFG_MAN_CS_EN BIT(1)
#define HC_CFG_MAN_CS_ASSERT BIT(0)
#define INT_STS 0x4
#define INT_STS_EN 0x8
#define INT_SIG_EN 0xc
#define INT_STS_ALL GENMASK(31, 0)
#define INT_RDY_PIN BIT(26)
#define INT_RDY_SR BIT(25)
#define INT_LNR_SUSP BIT(24)
#define INT_ECC_ERR BIT(17)
#define INT_CRC_ERR BIT(16)
#define INT_LWR_DIS BIT(12)
#define INT_LRD_DIS BIT(11)
#define INT_SDMA_INT BIT(10)
#define INT_DMA_FINISH BIT(9)
#define INT_RX_NOT_FULL BIT(3)
#define INT_RX_NOT_EMPTY BIT(2)
#define INT_TX_NOT_FULL BIT(1)
#define INT_TX_EMPTY BIT(0)
#define HC_EN 0x10
#define HC_EN_BIT BIT(0)
#define TXD(x) (0x14 + ((x) * 4))
#define RXD 0x24
#define SS_CTRL(s) (0x30 + ((s) * 4))
#define LRD_CFG 0x44
#define LWR_CFG 0x80
#define RWW_CFG 0x70
#define OP_READ BIT(23)
#define OP_DUMMY_CYC(x) ((x) << 17)
#define OP_ADDR_BYTES(x) ((x) << 14)
#define OP_CMD_BYTES(x) (((x) - 1) << 13)
#define OP_OCTA_CRC_EN BIT(12)
#define OP_DQS_EN BIT(11)
#define OP_ENHC_EN BIT(10)
#define OP_PREAMBLE_EN BIT(9)
#define OP_DATA_DDR BIT(8)
#define OP_DATA_BUSW(x) ((x) << 6)
#define OP_ADDR_DDR BIT(5)
#define OP_ADDR_BUSW(x) ((x) << 3)
#define OP_CMD_DDR BIT(2)
#define OP_CMD_BUSW(x) (x)
#define OP_BUSW_1 0
#define OP_BUSW_2 1
#define OP_BUSW_4 2
#define OP_BUSW_8 3
#define OCTA_CRC 0x38
#define OCTA_CRC_IN_EN(s) BIT(3 + ((s) * 16))
#define OCTA_CRC_CHUNK(s, x) ((fls((x) / 32)) << (1 + ((s) * 16)))
#define OCTA_CRC_OUT_EN(s) BIT(0 + ((s) * 16))
#define ONFI_DIN_CNT(s) (0x3c + (s))
#define LRD_CTRL 0x48
#define RWW_CTRL 0x74
#define LWR_CTRL 0x84
#define LMODE_EN BIT(31)
#define LMODE_SLV_ACT(x) ((x) << 21)
#define LMODE_CMD1(x) ((x) << 8)
#define LMODE_CMD0(x) (x)
#define LRD_ADDR 0x4c
#define LWR_ADDR 0x88
#define LRD_RANGE 0x50
#define LWR_RANGE 0x8c
#define AXI_SLV_ADDR 0x54
#define DMAC_RD_CFG 0x58
#define DMAC_WR_CFG 0x94
#define DMAC_CFG_PERIPH_EN BIT(31)
#define DMAC_CFG_ALLFLUSH_EN BIT(30)
#define DMAC_CFG_LASTFLUSH_EN BIT(29)
#define DMAC_CFG_QE(x) (((x) + 1) << 16)
#define DMAC_CFG_BURST_LEN(x) (((x) + 1) << 12)
#define DMAC_CFG_BURST_SZ(x) ((x) << 8)
#define DMAC_CFG_DIR_READ BIT(1)
#define DMAC_CFG_START BIT(0)
#define DMAC_RD_CNT 0x5c
#define DMAC_WR_CNT 0x98
#define SDMA_ADDR 0x60
#define DMAM_CFG 0x64
#define DMAM_CFG_START BIT(31)
#define DMAM_CFG_CONT BIT(30)
#define DMAM_CFG_SDMA_GAP(x) (fls((x) / 8192) << 2)
#define DMAM_CFG_DIR_READ BIT(1)
#define DMAM_CFG_EN BIT(0)
#define DMAM_CNT 0x68
#define LNR_TIMER_TH 0x6c
#define RDM_CFG0 0x78
#define RDM_CFG0_POLY(x) (x)
#define RDM_CFG1 0x7c
#define RDM_CFG1_RDM_EN BIT(31)
#define RDM_CFG1_SEED(x) (x)
#define LWR_SUSP_CTRL 0x90
#define LWR_SUSP_CTRL_EN BIT(31)
#define DMAS_CTRL 0x9c
#define DMAS_CTRL_EN BIT(31)
#define DMAS_CTRL_DIR_READ BIT(30)
#define DATA_STROB 0xa0
#define DATA_STROB_EDO_EN BIT(2)
#define DATA_STROB_INV_POL BIT(1)
#define DATA_STROB_DELAY_2CYC BIT(0)
#define IDLY_CODE(x) (0xa4 + ((x) * 4))
#define IDLY_CODE_VAL(x, v) ((v) << (((x) % 4) * 8))
#define GPIO 0xc4
#define GPIO_PT(x) BIT(3 + ((x) * 16))
#define GPIO_RESET(x) BIT(2 + ((x) * 16))
#define GPIO_HOLDB(x) BIT(1 + ((x) * 16))
#define GPIO_WPB(x) BIT((x) * 16)
#define HC_VER 0xd0
#define HW_TEST(x) (0xe0 + ((x) * 4))
#define MXIC_NFC_MAX_CLK_HZ 50000000
#define IRQ_TIMEOUT 1000
struct mxic_nand_ctlr {
struct clk *ps_clk;
struct clk *send_clk;
struct clk *send_dly_clk;
struct completion complete;
void __iomem *regs;
struct nand_controller controller;
struct device *dev;
struct nand_chip chip;
};
static int mxic_nfc_clk_enable(struct mxic_nand_ctlr *nfc)
{
int ret;
ret = clk_prepare_enable(nfc->ps_clk);
if (ret)
return ret;
ret = clk_prepare_enable(nfc->send_clk);
if (ret)
goto err_ps_clk;
ret = clk_prepare_enable(nfc->send_dly_clk);
if (ret)
goto err_send_dly_clk;
return ret;
err_send_dly_clk:
clk_disable_unprepare(nfc->send_clk);
err_ps_clk:
clk_disable_unprepare(nfc->ps_clk);
return ret;
}
static void mxic_nfc_clk_disable(struct mxic_nand_ctlr *nfc)
{
clk_disable_unprepare(nfc->send_clk);
clk_disable_unprepare(nfc->send_dly_clk);
clk_disable_unprepare(nfc->ps_clk);
}
static void mxic_nfc_set_input_delay(struct mxic_nand_ctlr *nfc, u8 idly_code)
{
writel(IDLY_CODE_VAL(0, idly_code) |
IDLY_CODE_VAL(1, idly_code) |
IDLY_CODE_VAL(2, idly_code) |
IDLY_CODE_VAL(3, idly_code),
nfc->regs + IDLY_CODE(0));
writel(IDLY_CODE_VAL(4, idly_code) |
IDLY_CODE_VAL(5, idly_code) |
IDLY_CODE_VAL(6, idly_code) |
IDLY_CODE_VAL(7, idly_code),
nfc->regs + IDLY_CODE(1));
}
static int mxic_nfc_clk_setup(struct mxic_nand_ctlr *nfc, unsigned long freq)
{
int ret;
ret = clk_set_rate(nfc->send_clk, freq);
if (ret)
return ret;
ret = clk_set_rate(nfc->send_dly_clk, freq);
if (ret)
return ret;
/*
* A constant delay range from 0x0 ~ 0x1F for input delay,
* the unit is 78 ps, the max input delay is 2.418 ns.
*/
mxic_nfc_set_input_delay(nfc, 0xf);
/*
* Phase degree = 360 * freq * output-delay
* where output-delay is a constant value 1 ns in FPGA.
*
* Get Phase degree = 360 * freq * 1 ns
* = 360 * freq * 1 sec / 1000000000
* = 9 * freq / 25000000
*/
ret = clk_set_phase(nfc->send_dly_clk, 9 * freq / 25000000);
if (ret)
return ret;
return 0;
}
static int mxic_nfc_set_freq(struct mxic_nand_ctlr *nfc, unsigned long freq)
{
int ret;
if (freq > MXIC_NFC_MAX_CLK_HZ)
freq = MXIC_NFC_MAX_CLK_HZ;
mxic_nfc_clk_disable(nfc);
ret = mxic_nfc_clk_setup(nfc, freq);
if (ret)
return ret;
ret = mxic_nfc_clk_enable(nfc);
if (ret)
return ret;
return 0;
}
static irqreturn_t mxic_nfc_isr(int irq, void *dev_id)
{
struct mxic_nand_ctlr *nfc = dev_id;
u32 sts;
sts = readl(nfc->regs + INT_STS);
if (sts & INT_RDY_PIN)
complete(&nfc->complete);
else
return IRQ_NONE;
return IRQ_HANDLED;
}
static void mxic_nfc_hw_init(struct mxic_nand_ctlr *nfc)
{
writel(HC_CFG_NIO(8) | HC_CFG_TYPE(1, HC_CFG_TYPE_RAW_NAND) |
HC_CFG_SLV_ACT(0) | HC_CFG_MAN_CS_EN |
HC_CFG_IDLE_SIO_LVL(1), nfc->regs + HC_CFG);
writel(INT_STS_ALL, nfc->regs + INT_STS_EN);
writel(INT_RDY_PIN, nfc->regs + INT_SIG_EN);
writel(0x0, nfc->regs + ONFI_DIN_CNT(0));
writel(0, nfc->regs + LRD_CFG);
writel(0, nfc->regs + LRD_CTRL);
writel(0x0, nfc->regs + HC_EN);
}
static void mxic_nfc_cs_enable(struct mxic_nand_ctlr *nfc)
{
writel(readl(nfc->regs + HC_CFG) | HC_CFG_MAN_CS_EN,
nfc->regs + HC_CFG);
writel(HC_CFG_MAN_CS_ASSERT | readl(nfc->regs + HC_CFG),
nfc->regs + HC_CFG);
}
static void mxic_nfc_cs_disable(struct mxic_nand_ctlr *nfc)
{
writel(~HC_CFG_MAN_CS_ASSERT & readl(nfc->regs + HC_CFG),
nfc->regs + HC_CFG);
}
static int mxic_nfc_wait_ready(struct nand_chip *chip)
{
struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip);
int ret;
ret = wait_for_completion_timeout(&nfc->complete,
msecs_to_jiffies(IRQ_TIMEOUT));
if (!ret) {
dev_err(nfc->dev, "nand device timeout\n");
return -ETIMEDOUT;
}
return 0;
}
static int mxic_nfc_data_xfer(struct mxic_nand_ctlr *nfc, const void *txbuf,
void *rxbuf, unsigned int len)
{
unsigned int pos = 0;
while (pos < len) {
unsigned int nbytes = len - pos;
u32 data = 0xffffffff;
u32 sts;
int ret;
if (nbytes > 4)
nbytes = 4;
if (txbuf)
memcpy(&data, txbuf + pos, nbytes);
ret = readl_poll_timeout(nfc->regs + INT_STS, sts,
sts & INT_TX_EMPTY, 0, USEC_PER_SEC);
if (ret)
return ret;
writel(data, nfc->regs + TXD(nbytes % 4));
ret = readl_poll_timeout(nfc->regs + INT_STS, sts,
sts & INT_TX_EMPTY, 0, USEC_PER_SEC);
if (ret)
return ret;
ret = readl_poll_timeout(nfc->regs + INT_STS, sts,
sts & INT_RX_NOT_EMPTY, 0,
USEC_PER_SEC);
if (ret)
return ret;
data = readl(nfc->regs + RXD);
if (rxbuf) {
data >>= (8 * (4 - nbytes));
memcpy(rxbuf + pos, &data, nbytes);
}
if (readl(nfc->regs + INT_STS) & INT_RX_NOT_EMPTY)
dev_warn(nfc->dev, "RX FIFO not empty\n");
pos += nbytes;
}
return 0;
}
static int mxic_nfc_exec_op(struct nand_chip *chip,
const struct nand_operation *op, bool check_only)
{
struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip);
const struct nand_op_instr *instr = NULL;
int ret = 0;
unsigned int op_id;
mxic_nfc_cs_enable(nfc);
init_completion(&nfc->complete);
for (op_id = 0; op_id < op->ninstrs; op_id++) {
instr = &op->instrs[op_id];
switch (instr->type) {
case NAND_OP_CMD_INSTR:
writel(0, nfc->regs + HC_EN);
writel(HC_EN_BIT, nfc->regs + HC_EN);
writel(OP_CMD_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) |
OP_CMD_BYTES(0), nfc->regs + SS_CTRL(0));
ret = mxic_nfc_data_xfer(nfc,
&instr->ctx.cmd.opcode,
NULL, 1);
break;
case NAND_OP_ADDR_INSTR:
writel(OP_ADDR_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) |
OP_ADDR_BYTES(instr->ctx.addr.naddrs),
nfc->regs + SS_CTRL(0));
ret = mxic_nfc_data_xfer(nfc,
instr->ctx.addr.addrs, NULL,
instr->ctx.addr.naddrs);
break;
case NAND_OP_DATA_IN_INSTR:
writel(0x0, nfc->regs + ONFI_DIN_CNT(0));
writel(OP_DATA_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F) |
OP_READ, nfc->regs + SS_CTRL(0));
ret = mxic_nfc_data_xfer(nfc, NULL,
instr->ctx.data.buf.in,
instr->ctx.data.len);
break;
case NAND_OP_DATA_OUT_INSTR:
writel(instr->ctx.data.len,
nfc->regs + ONFI_DIN_CNT(0));
writel(OP_DATA_BUSW(OP_BUSW_8) | OP_DUMMY_CYC(0x3F),
nfc->regs + SS_CTRL(0));
ret = mxic_nfc_data_xfer(nfc,
instr->ctx.data.buf.out, NULL,
instr->ctx.data.len);
break;
case NAND_OP_WAITRDY_INSTR:
ret = mxic_nfc_wait_ready(chip);
break;
}
}
mxic_nfc_cs_disable(nfc);
return ret;
}
static int mxic_nfc_setup_data_interface(struct nand_chip *chip, int chipnr,
const struct nand_data_interface *conf)
{
struct mxic_nand_ctlr *nfc = nand_get_controller_data(chip);
const struct nand_sdr_timings *sdr;
unsigned long freq;
int ret;
sdr = nand_get_sdr_timings(conf);
if (IS_ERR(sdr))
return PTR_ERR(sdr);
if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
return 0;
freq = NSEC_PER_SEC / (sdr->tRC_min / 1000);
ret = mxic_nfc_set_freq(nfc, freq);
if (ret)
dev_err(nfc->dev, "set freq:%ld failed\n", freq);
if (sdr->tRC_min < 30000)
writel(DATA_STROB_EDO_EN, nfc->regs + DATA_STROB);
return 0;
}
static const struct nand_controller_ops mxic_nand_controller_ops = {
.exec_op = mxic_nfc_exec_op,
.setup_data_interface = mxic_nfc_setup_data_interface,
};
static int mxic_nfc_probe(struct platform_device *pdev)
{
struct device_node *nand_np, *np = pdev->dev.of_node;
struct mtd_info *mtd;
struct mxic_nand_ctlr *nfc;
struct nand_chip *nand_chip;
int err;
int irq;
nfc = devm_kzalloc(&pdev->dev, sizeof(struct mxic_nand_ctlr),
GFP_KERNEL);
if (!nfc)
return -ENOMEM;
nfc->ps_clk = devm_clk_get(&pdev->dev, "ps");
if (IS_ERR(nfc->ps_clk))
return PTR_ERR(nfc->ps_clk);
nfc->send_clk = devm_clk_get(&pdev->dev, "send");
if (IS_ERR(nfc->send_clk))
return PTR_ERR(nfc->send_clk);
nfc->send_dly_clk = devm_clk_get(&pdev->dev, "send_dly");
if (IS_ERR(nfc->send_dly_clk))
return PTR_ERR(nfc->send_dly_clk);
nfc->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(nfc->regs))
return PTR_ERR(nfc->regs);
nand_chip = &nfc->chip;
mtd = nand_to_mtd(nand_chip);
mtd->dev.parent = &pdev->dev;
for_each_child_of_node(np, nand_np)
nand_set_flash_node(nand_chip, nand_np);
nand_chip->priv = nfc;
nfc->dev = &pdev->dev;
nfc->controller.ops = &mxic_nand_controller_ops;
nand_controller_init(&nfc->controller);
nand_chip->controller = &nfc->controller;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to retrieve irq\n");
return irq;
}
mxic_nfc_hw_init(nfc);
err = devm_request_irq(&pdev->dev, irq, mxic_nfc_isr,
0, "mxic-nfc", nfc);
if (err)
goto fail;
err = nand_scan(nand_chip, 1);
if (err)
goto fail;
err = mtd_device_register(mtd, NULL, 0);
if (err)
goto fail;
platform_set_drvdata(pdev, nfc);
return 0;
fail:
mxic_nfc_clk_disable(nfc);
return err;
}
static int mxic_nfc_remove(struct platform_device *pdev)
{
struct mxic_nand_ctlr *nfc = platform_get_drvdata(pdev);
nand_release(&nfc->chip);
mxic_nfc_clk_disable(nfc);
return 0;
}
static const struct of_device_id mxic_nfc_of_ids[] = {
{ .compatible = "mxic,multi-itfc-v009-nand-controller", },
{},
};
MODULE_DEVICE_TABLE(of, mxic_nfc_of_ids);
static struct platform_driver mxic_nfc_driver = {
.probe = mxic_nfc_probe,
.remove = mxic_nfc_remove,
.driver = {
.name = "mxic-nfc",
.of_match_table = mxic_nfc_of_ids,
},
};
module_platform_driver(mxic_nfc_driver);
MODULE_AUTHOR("Mason Yang <masonccyang@mxic.com.tw>");
MODULE_DESCRIPTION("Macronix raw NAND controller driver");
MODULE_LICENSE("GPL v2");

View File

@ -4112,7 +4112,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
struct nand_chip *chip = mtd_to_nand(mtd); struct nand_chip *chip = mtd_to_nand(mtd);
int ret = -ENOTSUPP; int ret;
ops->retlen = 0; ops->retlen = 0;

View File

@ -1232,7 +1232,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
if (!td) { if (!td) {
if ((res = nand_memory_bbt(this, bd))) { if ((res = nand_memory_bbt(this, bd))) {
pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n"); pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
goto err; goto err_free_bbt;
} }
return 0; return 0;
} }
@ -1245,7 +1245,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
buf = vmalloc(len); buf = vmalloc(len);
if (!buf) { if (!buf) {
res = -ENOMEM; res = -ENOMEM;
goto err; goto err_free_bbt;
} }
/* Is the bbt at a given page? */ /* Is the bbt at a given page? */
@ -1258,7 +1258,7 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
res = check_create(this, buf, bd); res = check_create(this, buf, bd);
if (res) if (res)
goto err; goto err_free_buf;
/* Prevent the bbt regions from erasing / writing */ /* Prevent the bbt regions from erasing / writing */
mark_bbt_region(this, td); mark_bbt_region(this, td);
@ -1268,7 +1268,9 @@ static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
vfree(buf); vfree(buf);
return 0; return 0;
err: err_free_buf:
vfree(buf);
err_free_bbt:
kfree(this->bbt); kfree(this->bbt);
this->bbt = NULL; this->bbt = NULL;
return res; return res;

View File

@ -1,304 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright © 2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.com>
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/rawnand.h>
#include <linux/mtd/partitions.h>
#define REG_FMICSR 0x00
#define REG_SMCSR 0xa0
#define REG_SMISR 0xac
#define REG_SMCMD 0xb0
#define REG_SMADDR 0xb4
#define REG_SMDATA 0xb8
#define RESET_FMI 0x01
#define NAND_EN 0x08
#define READYBUSY (0x01 << 18)
#define SWRST 0x01
#define PSIZE (0x01 << 3)
#define DMARWEN (0x03 << 1)
#define BUSWID (0x01 << 4)
#define ECC4EN (0x01 << 5)
#define WP (0x01 << 24)
#define NANDCS (0x01 << 25)
#define ENDADDR (0x01 << 31)
#define read_data_reg(dev) \
__raw_readl((dev)->reg + REG_SMDATA)
#define write_data_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMDATA)
#define write_cmd_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMCMD)
#define write_addr_reg(dev, val) \
__raw_writel((val), (dev)->reg + REG_SMADDR)
struct nuc900_nand {
struct nand_chip chip;
void __iomem *reg;
struct clk *clk;
spinlock_t lock;
};
static inline struct nuc900_nand *mtd_to_nuc900(struct mtd_info *mtd)
{
return container_of(mtd_to_nand(mtd), struct nuc900_nand, chip);
}
static const struct mtd_partition partitions[] = {
{
.name = "NAND FS 0",
.offset = 0,
.size = 8 * 1024 * 1024
},
{
.name = "NAND FS 1",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
};
static unsigned char nuc900_nand_read_byte(struct nand_chip *chip)
{
unsigned char ret;
struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip));
ret = (unsigned char)read_data_reg(nand);
return ret;
}
static void nuc900_nand_read_buf(struct nand_chip *chip,
unsigned char *buf, int len)
{
int i;
struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip));
for (i = 0; i < len; i++)
buf[i] = (unsigned char)read_data_reg(nand);
}
static void nuc900_nand_write_buf(struct nand_chip *chip,
const unsigned char *buf, int len)
{
int i;
struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip));
for (i = 0; i < len; i++)
write_data_reg(nand, buf[i]);
}
static int nuc900_check_rb(struct nuc900_nand *nand)
{
unsigned int val;
spin_lock(&nand->lock);
val = __raw_readl(nand->reg + REG_SMISR);
val &= READYBUSY;
spin_unlock(&nand->lock);
return val;
}
static int nuc900_nand_devready(struct nand_chip *chip)
{
struct nuc900_nand *nand = mtd_to_nuc900(nand_to_mtd(chip));
int ready;
ready = (nuc900_check_rb(nand)) ? 1 : 0;
return ready;
}
static void nuc900_nand_command_lp(struct nand_chip *chip,
unsigned int command,
int column, int page_addr)
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct nuc900_nand *nand = mtd_to_nuc900(mtd);
if (command == NAND_CMD_READOOB) {
column += mtd->writesize;
command = NAND_CMD_READ0;
}
write_cmd_reg(nand, command & 0xff);
if (column != -1 || page_addr != -1) {
if (column != -1) {
if (chip->options & NAND_BUSWIDTH_16 &&
!nand_opcode_8bits(command))
column >>= 1;
write_addr_reg(nand, column);
write_addr_reg(nand, column >> 8 | ENDADDR);
}
if (page_addr != -1) {
write_addr_reg(nand, page_addr);
if (chip->options & NAND_ROW_ADDR_3) {
write_addr_reg(nand, page_addr >> 8);
write_addr_reg(nand, page_addr >> 16 | ENDADDR);
} else {
write_addr_reg(nand, page_addr >> 8 | ENDADDR);
}
}
}
switch (command) {
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_RNDIN:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (chip->legacy.dev_ready)
break;
udelay(chip->legacy.chip_delay);
write_cmd_reg(nand, NAND_CMD_STATUS);
write_cmd_reg(nand, command);
while (!nuc900_check_rb(nand))
;
return;
case NAND_CMD_RNDOUT:
write_cmd_reg(nand, NAND_CMD_RNDOUTSTART);
return;
case NAND_CMD_READ0:
write_cmd_reg(nand, NAND_CMD_READSTART);
/* fall through */
default:
if (!chip->legacy.dev_ready) {
udelay(chip->legacy.chip_delay);
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay(100);
while (!chip->legacy.dev_ready(chip))
;
}
static void nuc900_nand_enable(struct nuc900_nand *nand)
{
unsigned int val;
spin_lock(&nand->lock);
__raw_writel(RESET_FMI, (nand->reg + REG_FMICSR));
val = __raw_readl(nand->reg + REG_FMICSR);
if (!(val & NAND_EN))
__raw_writel(val | NAND_EN, nand->reg + REG_FMICSR);
val = __raw_readl(nand->reg + REG_SMCSR);
val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS);
val |= WP;
__raw_writel(val, nand->reg + REG_SMCSR);
spin_unlock(&nand->lock);
}
static int nuc900_nand_probe(struct platform_device *pdev)
{
struct nuc900_nand *nuc900_nand;
struct nand_chip *chip;
struct mtd_info *mtd;
struct resource *res;
nuc900_nand = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_nand),
GFP_KERNEL);
if (!nuc900_nand)
return -ENOMEM;
chip = &(nuc900_nand->chip);
mtd = nand_to_mtd(chip);
mtd->dev.parent = &pdev->dev;
spin_lock_init(&nuc900_nand->lock);
nuc900_nand->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(nuc900_nand->clk))
return -ENOENT;
clk_enable(nuc900_nand->clk);
chip->legacy.cmdfunc = nuc900_nand_command_lp;
chip->legacy.dev_ready = nuc900_nand_devready;
chip->legacy.read_byte = nuc900_nand_read_byte;
chip->legacy.write_buf = nuc900_nand_write_buf;
chip->legacy.read_buf = nuc900_nand_read_buf;
chip->legacy.chip_delay = 50;
chip->options = 0;
chip->ecc.mode = NAND_ECC_SOFT;
chip->ecc.algo = NAND_ECC_HAMMING;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
nuc900_nand->reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(nuc900_nand->reg))
return PTR_ERR(nuc900_nand->reg);
nuc900_nand_enable(nuc900_nand);
if (nand_scan(chip, 1))
return -ENXIO;
mtd_device_register(mtd, partitions, ARRAY_SIZE(partitions));
platform_set_drvdata(pdev, nuc900_nand);
return 0;
}
static int nuc900_nand_remove(struct platform_device *pdev)
{
struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev);
nand_release(&nuc900_nand->chip);
clk_disable(nuc900_nand->clk);
return 0;
}
static struct platform_driver nuc900_nand_driver = {
.probe = nuc900_nand_probe,
.remove = nuc900_nand_remove,
.driver = {
.name = "nuc900-fmi",
},
};
module_platform_driver(nuc900_nand_driver);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:nuc900-fmi");

View File

@ -1501,7 +1501,7 @@ static int omap_elm_correct_data(struct nand_chip *chip, u_char *data,
} }
/* Update number of correctable errors */ /* Update number of correctable errors */
stat += err_vec[i].error_count; stat = max_t(unsigned int, stat, err_vec[i].error_count);
/* Update page data with sector size */ /* Update page data with sector size */
data += ecc->size; data += ecc->size;

View File

@ -116,7 +116,7 @@ static int oxnas_nand_probe(struct platform_device *pdev)
GFP_KERNEL); GFP_KERNEL);
if (!chip) { if (!chip) {
err = -ENOMEM; err = -ENOMEM;
goto err_clk_unprepare; goto err_release_child;
} }
chip->controller = &oxnas->base; chip->controller = &oxnas->base;
@ -137,12 +137,12 @@ static int oxnas_nand_probe(struct platform_device *pdev)
/* Scan to find existence of the device */ /* Scan to find existence of the device */
err = nand_scan(chip, 1); err = nand_scan(chip, 1);
if (err) if (err)
goto err_clk_unprepare; goto err_release_child;
err = mtd_device_register(mtd, NULL, 0); err = mtd_device_register(mtd, NULL, 0);
if (err) { if (err) {
nand_release(chip); nand_release(chip);
goto err_clk_unprepare; goto err_release_child;
} }
oxnas->chips[nchips] = chip; oxnas->chips[nchips] = chip;
@ -159,6 +159,8 @@ static int oxnas_nand_probe(struct platform_device *pdev)
return 0; return 0;
err_release_child:
of_node_put(nand_np);
err_clk_unprepare: err_clk_unprepare:
clk_disable_unprepare(oxnas->clk); clk_disable_unprepare(oxnas->clk);
return err; return err;

View File

@ -998,7 +998,7 @@ static void r852_shutdown(struct pci_dev *pci_dev)
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int r852_suspend(struct device *device) static int r852_suspend(struct device *device)
{ {
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); struct r852_device *dev = dev_get_drvdata(device);
if (dev->ctlreg & R852_CTL_CARDENABLE) if (dev->ctlreg & R852_CTL_CARDENABLE)
return -EBUSY; return -EBUSY;
@ -1019,7 +1019,7 @@ static int r852_suspend(struct device *device)
static int r852_resume(struct device *device) static int r852_resume(struct device *device)
{ {
struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); struct r852_device *dev = dev_get_drvdata(device);
r852_disable_irqs(dev); r852_disable_irqs(dev);
r852_card_update_present(dev); r852_card_update_present(dev);

View File

@ -1427,21 +1427,16 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip,
struct stm32_fmc2_timings *tims = &nand->timings; struct stm32_fmc2_timings *tims = &nand->timings;
unsigned long hclk = clk_get_rate(fmc2->clk); unsigned long hclk = clk_get_rate(fmc2->clk);
unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000); unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
int tar, tclr, thiz, twait, tset_mem, tset_att, thold_mem, thold_att; unsigned long timing, tar, tclr, thiz, twait;
unsigned long tset_mem, tset_att, thold_mem, thold_att;
tar = hclkp; tar = max_t(unsigned long, hclkp, sdrt->tAR_min);
if (tar < sdrt->tAR_min) timing = DIV_ROUND_UP(tar, hclkp) - 1;
tar = sdrt->tAR_min; tims->tar = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK);
tims->tar = DIV_ROUND_UP(tar, hclkp) - 1;
if (tims->tar > FMC2_PCR_TIMING_MASK)
tims->tar = FMC2_PCR_TIMING_MASK;
tclr = hclkp; tclr = max_t(unsigned long, hclkp, sdrt->tCLR_min);
if (tclr < sdrt->tCLR_min) timing = DIV_ROUND_UP(tclr, hclkp) - 1;
tclr = sdrt->tCLR_min; tims->tclr = min_t(unsigned long, timing, FMC2_PCR_TIMING_MASK);
tims->tclr = DIV_ROUND_UP(tclr, hclkp) - 1;
if (tims->tclr > FMC2_PCR_TIMING_MASK)
tims->tclr = FMC2_PCR_TIMING_MASK;
tims->thiz = FMC2_THIZ; tims->thiz = FMC2_THIZ;
thiz = (tims->thiz + 1) * hclkp; thiz = (tims->thiz + 1) * hclkp;
@ -1451,18 +1446,11 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip,
* tWAIT > tWP * tWAIT > tWP
* tWAIT > tREA + tIO * tWAIT > tREA + tIO
*/ */
twait = hclkp; twait = max_t(unsigned long, hclkp, sdrt->tRP_min);
if (twait < sdrt->tRP_min) twait = max_t(unsigned long, twait, sdrt->tWP_min);
twait = sdrt->tRP_min; twait = max_t(unsigned long, twait, sdrt->tREA_max + FMC2_TIO);
if (twait < sdrt->tWP_min) timing = DIV_ROUND_UP(twait, hclkp);
twait = sdrt->tWP_min; tims->twait = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
if (twait < sdrt->tREA_max + FMC2_TIO)
twait = sdrt->tREA_max + FMC2_TIO;
tims->twait = DIV_ROUND_UP(twait, hclkp);
if (tims->twait == 0)
tims->twait = 1;
else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK)
tims->twait = FMC2_PMEM_PATT_TIMING_MASK;
/* /*
* tSETUP_MEM > tCS - tWAIT * tSETUP_MEM > tCS - tWAIT
@ -1477,20 +1465,15 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip,
if (twait > thiz && (sdrt->tDS_min > twait - thiz) && if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
(tset_mem < sdrt->tDS_min - (twait - thiz))) (tset_mem < sdrt->tDS_min - (twait - thiz)))
tset_mem = sdrt->tDS_min - (twait - thiz); tset_mem = sdrt->tDS_min - (twait - thiz);
tims->tset_mem = DIV_ROUND_UP(tset_mem, hclkp); timing = DIV_ROUND_UP(tset_mem, hclkp);
if (tims->tset_mem == 0) tims->tset_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
tims->tset_mem = 1;
else if (tims->tset_mem > FMC2_PMEM_PATT_TIMING_MASK)
tims->tset_mem = FMC2_PMEM_PATT_TIMING_MASK;
/* /*
* tHOLD_MEM > tCH * tHOLD_MEM > tCH
* tHOLD_MEM > tREH - tSETUP_MEM * tHOLD_MEM > tREH - tSETUP_MEM
* tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT) * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT)
*/ */
thold_mem = hclkp; thold_mem = max_t(unsigned long, hclkp, sdrt->tCH_min);
if (thold_mem < sdrt->tCH_min)
thold_mem = sdrt->tCH_min;
if (sdrt->tREH_min > tset_mem && if (sdrt->tREH_min > tset_mem &&
(thold_mem < sdrt->tREH_min - tset_mem)) (thold_mem < sdrt->tREH_min - tset_mem))
thold_mem = sdrt->tREH_min - tset_mem; thold_mem = sdrt->tREH_min - tset_mem;
@ -1500,11 +1483,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip,
if ((sdrt->tWC_min > tset_mem + twait) && if ((sdrt->tWC_min > tset_mem + twait) &&
(thold_mem < sdrt->tWC_min - (tset_mem + twait))) (thold_mem < sdrt->tWC_min - (tset_mem + twait)))
thold_mem = sdrt->tWC_min - (tset_mem + twait); thold_mem = sdrt->tWC_min - (tset_mem + twait);
tims->thold_mem = DIV_ROUND_UP(thold_mem, hclkp); timing = DIV_ROUND_UP(thold_mem, hclkp);
if (tims->thold_mem == 0) tims->thold_mem = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
tims->thold_mem = 1;
else if (tims->thold_mem > FMC2_PMEM_PATT_TIMING_MASK)
tims->thold_mem = FMC2_PMEM_PATT_TIMING_MASK;
/* /*
* tSETUP_ATT > tCS - tWAIT * tSETUP_ATT > tCS - tWAIT
@ -1526,11 +1506,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip,
if (twait > thiz && (sdrt->tDS_min > twait - thiz) && if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
(tset_att < sdrt->tDS_min - (twait - thiz))) (tset_att < sdrt->tDS_min - (twait - thiz)))
tset_att = sdrt->tDS_min - (twait - thiz); tset_att = sdrt->tDS_min - (twait - thiz);
tims->tset_att = DIV_ROUND_UP(tset_att, hclkp); timing = DIV_ROUND_UP(tset_att, hclkp);
if (tims->tset_att == 0) tims->tset_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
tims->tset_att = 1;
else if (tims->tset_att > FMC2_PMEM_PATT_TIMING_MASK)
tims->tset_att = FMC2_PMEM_PATT_TIMING_MASK;
/* /*
* tHOLD_ATT > tALH * tHOLD_ATT > tALH
@ -1545,17 +1522,11 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip,
* tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT) * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT)
* tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT) * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT)
*/ */
thold_att = hclkp; thold_att = max_t(unsigned long, hclkp, sdrt->tALH_min);
if (thold_att < sdrt->tALH_min) thold_att = max_t(unsigned long, thold_att, sdrt->tCH_min);
thold_att = sdrt->tALH_min; thold_att = max_t(unsigned long, thold_att, sdrt->tCLH_min);
if (thold_att < sdrt->tCH_min) thold_att = max_t(unsigned long, thold_att, sdrt->tCOH_min);
thold_att = sdrt->tCH_min; thold_att = max_t(unsigned long, thold_att, sdrt->tDH_min);
if (thold_att < sdrt->tCLH_min)
thold_att = sdrt->tCLH_min;
if (thold_att < sdrt->tCOH_min)
thold_att = sdrt->tCOH_min;
if (thold_att < sdrt->tDH_min)
thold_att = sdrt->tDH_min;
if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) && if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) &&
(thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem)) (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem))
thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem; thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem;
@ -1574,11 +1545,8 @@ static void stm32_fmc2_calc_timings(struct nand_chip *chip,
if ((sdrt->tWC_min > tset_att + twait) && if ((sdrt->tWC_min > tset_att + twait) &&
(thold_att < sdrt->tWC_min - (tset_att + twait))) (thold_att < sdrt->tWC_min - (tset_att + twait)))
thold_att = sdrt->tWC_min - (tset_att + twait); thold_att = sdrt->tWC_min - (tset_att + twait);
tims->thold_att = DIV_ROUND_UP(thold_att, hclkp); timing = DIV_ROUND_UP(thold_att, hclkp);
if (tims->thold_att == 0) tims->thold_att = clamp_val(timing, 1, FMC2_PMEM_PATT_TIMING_MASK);
tims->thold_att = 1;
else if (tims->thold_att > FMC2_PMEM_PATT_TIMING_MASK)
tims->thold_att = FMC2_PMEM_PATT_TIMING_MASK;
} }
static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr, static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr,

View File

@ -659,6 +659,7 @@ static int tango_nand_probe(struct platform_device *pdev)
err = chip_init(&pdev->dev, np); err = chip_init(&pdev->dev, np);
if (err) { if (err) {
tango_nand_remove(pdev); tango_nand_remove(pdev);
of_node_put(np);
return err; return err;
} }
} }

View File

@ -862,6 +862,7 @@ static int vf610_nfc_probe(struct platform_device *pdev)
dev_err(nfc->dev, dev_err(nfc->dev,
"Only one NAND chip supported!\n"); "Only one NAND chip supported!\n");
err = -EINVAL; err = -EINVAL;
of_node_put(child);
goto err_disable_clk; goto err_disable_clk;
} }

View File

@ -1,4 +1,72 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config MTD_AR7_PARTS
tristate "TI AR7 partitioning parser"
help
TI AR7 partitioning parser support
config MTD_BCM47XX_PARTS
tristate "BCM47XX partitioning parser"
depends on BCM47XX || ARCH_BCM_5301X
help
This provides partitions parser for devices based on BCM47xx
boards.
config MTD_BCM63XX_PARTS
tristate "BCM63XX CFE partitioning parser"
depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
select CRC32
select MTD_PARSER_IMAGETAG
help
This provides partition parsing for BCM63xx devices with CFE
bootloaders.
config MTD_CMDLINE_PARTS
tristate "Command line partition table parsing"
depends on MTD
help
Allow generic configuration of the MTD partition tables via the kernel
command line. Multiple flash resources are supported for hardware where
different kinds of flash memory are available.
You will still need the parsing functions to be called by the driver
for your particular device. It won't happen automatically. The
SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
example.
The format for the command line is as follows:
mtdparts=<mtddef>[;<mtddef]
<mtddef> := <mtd-id>:<partdef>[,<partdef>]
<partdef> := <size>[@offset][<name>][ro]
<mtd-id> := unique id used in mapping driver/device
<size> := standard linux memsize OR "-" to denote all
remaining space
<name> := (NAME)
Due to the way Linux handles the command line, no spaces are
allowed in the partition definition, including mtd id's and partition
names.
Examples:
1 flash resource (mtd-id "sa1100"), with 1 single writable partition:
mtdparts=sa1100:-
Same flash, but 2 named partitions, the first one being read-only:
mtdparts=sa1100:256k(ARMboot)ro,-(root)
If unsure, say 'N'.
config MTD_OF_PARTS
tristate "OpenFirmware (device tree) partitioning parser"
default y
depends on OF
help
This provides a open firmware device tree partition parser
which derives the partition map from the children of the
flash memory node, as described in
Documentation/devicetree/bindings/mtd/partition.txt.
config MTD_PARSER_IMAGETAG config MTD_PARSER_IMAGETAG
tristate "Parser for BCM963XX Image Tag format partitions" tristate "Parser for BCM963XX Image Tag format partitions"
depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST

View File

@ -1,4 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o

View File

@ -774,8 +774,11 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num)
continue; continue;
/* Read the oob of first sector */ /* Read the oob of first sector */
if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) {
kfifo_free(&zone->free_sectors);
kfree(zone->lba_to_phys_table);
return -EIO; return -EIO;
}
/* Test to see if block is erased. It is enough to test /* Test to see if block is erased. It is enough to test
first sector, because erase happens in one shot */ first sector, because erase happens in one shot */

View File

@ -2,6 +2,8 @@
menuconfig MTD_SPI_NOR menuconfig MTD_SPI_NOR
tristate "SPI-NOR device support" tristate "SPI-NOR device support"
depends on MTD depends on MTD
depends on MTD && SPI_MASTER
select SPI_MEM
help help
This is the framework for the SPI NOR which can be used by the SPI This is the framework for the SPI NOR which can be used by the SPI
device drivers and the SPI-NOR device driver. device drivers and the SPI-NOR device driver.

View File

@ -836,8 +836,10 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
controller->chips[cs] = chip; controller->chips[cs] = chip;
} }
if (ret) if (ret) {
of_node_put(child);
aspeed_smc_unregister(controller); aspeed_smc_unregister(controller);
}
return ret; return ret;
} }

View File

@ -13,6 +13,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
@ -241,23 +242,13 @@ struct cqspi_driver_platdata {
#define CQSPI_IRQ_STATUS_MASK 0x1FFFF #define CQSPI_IRQ_STATUS_MASK 0x1FFFF
static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear) static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clr)
{ {
unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS);
u32 val; u32 val;
while (1) { return readl_relaxed_poll_timeout(reg, val,
val = readl(reg); (((clr ? ~val : val) & mask) == mask),
if (clear) 10, CQSPI_TIMEOUT_MS * 1000);
val = ~val;
val &= mask;
if (val == mask)
return 0;
if (time_after(jiffies, end))
return -ETIMEDOUT;
}
} }
static bool cqspi_is_idle(struct cqspi_st *cqspi) static bool cqspi_is_idle(struct cqspi_st *cqspi)

View File

@ -401,6 +401,7 @@ static int hisi_spi_nor_register_all(struct hifmc_host *host)
if (host->num_chip == HIFMC_MAX_CHIP_NUM) { if (host->num_chip == HIFMC_MAX_CHIP_NUM) {
dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n"); dev_warn(dev, "Flash device number exceeds the maximum chipselect number\n");
of_node_put(np);
break; break;
} }
} }

View File

@ -65,6 +65,7 @@ static const struct pci_device_id intel_spi_pci_ids[] = {
{ PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x19e0), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x34a4), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0x4b24), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0xa0a4), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa1a4), (unsigned long)&bxt_info },
{ PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info }, { PCI_VDEVICE(INTEL, 0xa224), (unsigned long)&bxt_info },
{ }, { },

View File

@ -621,6 +621,8 @@ static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
switch (nor->read_opcode) { switch (nor->read_opcode) {
case SPINOR_OP_READ: case SPINOR_OP_READ:
case SPINOR_OP_READ_FAST: case SPINOR_OP_READ_FAST:
case SPINOR_OP_READ_4B:
case SPINOR_OP_READ_FAST_4B:
break; break;
default: default:
return -EINVAL; return -EINVAL;

File diff suppressed because it is too large Load Diff

View File

@ -189,6 +189,9 @@ struct module; /* only needed for owner field in mtd_info */
*/ */
struct mtd_debug_info { struct mtd_debug_info {
struct dentry *dfs_dir; struct dentry *dfs_dir;
const char *partname;
const char *partid;
}; };
struct mtd_info { struct mtd_info {

View File

@ -346,7 +346,7 @@ static inline unsigned int nanddev_ntargets(const struct nand_device *nand)
} }
/** /**
* nanddev_neraseblocks() - Get the total number of erasablocks * nanddev_neraseblocks() - Get the total number of eraseblocks
* @nand: NAND device * @nand: NAND device
* *
* Return: the total number of eraseblocks exposed by @nand. * Return: the total number of eraseblocks exposed by @nand.

View File

@ -5,6 +5,9 @@
* Copyright (C) 2008 Dmitry Baryshkov * Copyright (C) 2008 Dmitry Baryshkov
*/ */
#ifndef _MTD_SHARPSL_H
#define _MTD_SHARPSL_H
#include <linux/mtd/rawnand.h> #include <linux/mtd/rawnand.h>
#include <linux/mtd/nand_ecc.h> #include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
@ -16,3 +19,5 @@ struct sharpsl_nand_platform_data {
unsigned int nr_partitions; unsigned int nr_partitions;
const char *const *part_parsers; const char *const *part_parsers;
}; };
#endif /* _MTD_SHARPSL_H */

View File

@ -9,6 +9,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/mtd/cfi.h> #include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/spi/spi-mem.h>
/* /*
* Manufacturer IDs * Manufacturer IDs
@ -224,7 +225,6 @@ static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
return spi_nor_get_protocol_data_nbits(proto); return spi_nor_get_protocol_data_nbits(proto);
} }
#define SPI_NOR_MAX_CMD_SIZE 8
enum spi_nor_ops { enum spi_nor_ops {
SPI_NOR_OPS_READ = 0, SPI_NOR_OPS_READ = 0,
SPI_NOR_OPS_WRITE, SPI_NOR_OPS_WRITE,
@ -237,12 +237,12 @@ enum spi_nor_option_flags {
SNOR_F_USE_FSR = BIT(0), SNOR_F_USE_FSR = BIT(0),
SNOR_F_HAS_SR_TB = BIT(1), SNOR_F_HAS_SR_TB = BIT(1),
SNOR_F_NO_OP_CHIP_ERASE = BIT(2), SNOR_F_NO_OP_CHIP_ERASE = BIT(2),
SNOR_F_S3AN_ADDR_DEFAULT = BIT(3), SNOR_F_READY_XSR_RDY = BIT(3),
SNOR_F_READY_XSR_RDY = BIT(4), SNOR_F_USE_CLSR = BIT(4),
SNOR_F_USE_CLSR = BIT(5), SNOR_F_BROKEN_RESET = BIT(5),
SNOR_F_BROKEN_RESET = BIT(6), SNOR_F_4B_OPCODES = BIT(6),
SNOR_F_4B_OPCODES = BIT(7), SNOR_F_HAS_4BAIT = BIT(7),
SNOR_F_HAS_4BAIT = BIT(8), SNOR_F_HAS_LOCK = BIT(8),
}; };
/** /**
@ -333,130 +333,6 @@ struct spi_nor_erase_map {
u8 uniform_erase_type; u8 uniform_erase_type;
}; };
/**
* struct flash_info - Forward declaration of a structure used internally by
* spi_nor_scan()
*/
struct flash_info;
/**
* struct spi_nor - Structure for defining a the SPI NOR layer
* @mtd: point to a mtd_info structure
* @lock: the lock for the read/write/erase/lock/unlock operations
* @dev: point to a spi device, or a spi nor controller device.
* @info: spi-nor part JDEC MFR id and other info
* @page_size: the page size of the SPI NOR
* @addr_width: number of address bytes
* @erase_opcode: the opcode for erasing a sector
* @read_opcode: the read opcode
* @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_*)
* @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
* @cmd_buf: used by the write_reg
* @erase_map: the erase map of the SPI NOR
* @prepare: [OPTIONAL] do some preparations for the
* read/write/erase/lock/unlock operations
* @unprepare: [OPTIONAL] do some post work after the
* read/write/erase/lock/unlock operations
* @read_reg: [DRIVER-SPECIFIC] read out the register
* @write_reg: [DRIVER-SPECIFIC] write data to the register
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
* at the offset @offs; if not provided by the driver,
* spi-nor will send the erase opcode via write_reg()
* @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR
* @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
* @clear_sr_bp: [FLASH-SPECIFIC] clears the Block Protection Bits from
* the SPI NOR Status Register.
* completely locked
* @priv: the private data
*/
struct spi_nor {
struct mtd_info mtd;
struct mutex lock;
struct device *dev;
const struct flash_info *info;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
enum spi_nor_protocol read_proto;
enum spi_nor_protocol write_proto;
enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
struct spi_nor_erase_map erase_map;
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
ssize_t (*read)(struct spi_nor *nor, loff_t from,
size_t len, u_char *read_buf);
ssize_t (*write)(struct spi_nor *nor, loff_t to,
size_t len, const u_char *write_buf);
int (*erase)(struct spi_nor *nor, loff_t offs);
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*quad_enable)(struct spi_nor *nor);
int (*clear_sr_bp)(struct spi_nor *nor);
void *priv;
};
static u64 __maybe_unused
spi_nor_region_is_last(const struct spi_nor_erase_region *region)
{
return region->offset & SNOR_LAST_REGION;
}
static u64 __maybe_unused
spi_nor_region_end(const struct spi_nor_erase_region *region)
{
return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
}
static void __maybe_unused
spi_nor_region_mark_end(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_LAST_REGION;
}
static void __maybe_unused
spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_OVERLAID_REGION;
}
static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
return !!nor->erase_map.uniform_erase_type;
}
static inline void spi_nor_set_flash_node(struct spi_nor *nor,
struct device_node *np)
{
mtd_set_of_node(&nor->mtd, np);
}
static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
{
return mtd_get_of_node(&nor->mtd);
}
/** /**
* struct spi_nor_hwcaps - Structure for describing the hardware capabilies * struct spi_nor_hwcaps - Structure for describing the hardware capabilies
* supported by the SPI controller (bus master). * supported by the SPI controller (bus master).
@ -518,6 +394,257 @@ struct spi_nor_hwcaps {
#define SNOR_HWCAPS_PP_1_8_8 BIT(21) #define SNOR_HWCAPS_PP_1_8_8 BIT(21)
#define SNOR_HWCAPS_PP_8_8_8 BIT(22) #define SNOR_HWCAPS_PP_8_8_8 BIT(22)
#define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \
SNOR_HWCAPS_READ_4_4_4 | \
SNOR_HWCAPS_READ_8_8_8 | \
SNOR_HWCAPS_PP_4_4_4 | \
SNOR_HWCAPS_PP_8_8_8)
#define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \
SNOR_HWCAPS_READ_1_2_2_DTR | \
SNOR_HWCAPS_READ_1_4_4_DTR | \
SNOR_HWCAPS_READ_1_8_8_DTR)
#define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \
SNOR_HWCAPS_PP_MASK)
struct spi_nor_read_command {
u8 num_mode_clocks;
u8 num_wait_states;
u8 opcode;
enum spi_nor_protocol proto;
};
struct spi_nor_pp_command {
u8 opcode;
enum spi_nor_protocol proto;
};
enum spi_nor_read_command_index {
SNOR_CMD_READ,
SNOR_CMD_READ_FAST,
SNOR_CMD_READ_1_1_1_DTR,
/* Dual SPI */
SNOR_CMD_READ_1_1_2,
SNOR_CMD_READ_1_2_2,
SNOR_CMD_READ_2_2_2,
SNOR_CMD_READ_1_2_2_DTR,
/* Quad SPI */
SNOR_CMD_READ_1_1_4,
SNOR_CMD_READ_1_4_4,
SNOR_CMD_READ_4_4_4,
SNOR_CMD_READ_1_4_4_DTR,
/* Octal SPI */
SNOR_CMD_READ_1_1_8,
SNOR_CMD_READ_1_8_8,
SNOR_CMD_READ_8_8_8,
SNOR_CMD_READ_1_8_8_DTR,
SNOR_CMD_READ_MAX
};
enum spi_nor_pp_command_index {
SNOR_CMD_PP,
/* Quad SPI */
SNOR_CMD_PP_1_1_4,
SNOR_CMD_PP_1_4_4,
SNOR_CMD_PP_4_4_4,
/* Octal SPI */
SNOR_CMD_PP_1_1_8,
SNOR_CMD_PP_1_8_8,
SNOR_CMD_PP_8_8_8,
SNOR_CMD_PP_MAX
};
/* Forward declaration that will be used in 'struct spi_nor_flash_parameter' */
struct spi_nor;
/**
* struct spi_nor_locking_ops - SPI NOR locking methods
* @lock: lock a region of the SPI NOR.
* @unlock: unlock a region of the SPI NOR.
* @is_locked: check if a region of the SPI NOR is completely locked
*/
struct spi_nor_locking_ops {
int (*lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
int (*is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
};
/**
* struct spi_nor_flash_parameter - SPI NOR flash parameters and settings.
* Includes legacy flash parameters and settings that can be overwritten
* by the spi_nor_fixups hooks, or dynamically when parsing the JESD216
* Serial Flash Discoverable Parameters (SFDP) tables.
*
* @size: the flash memory density in bytes.
* @page_size: the page size of the SPI NOR flash memory.
* @hwcaps: describes the read and page program hardware
* capabilities.
* @reads: read capabilities ordered by priority: the higher index
* in the array, the higher priority.
* @page_programs: page program capabilities ordered by priority: the
* higher index in the array, the higher priority.
* @erase_map: the erase map parsed from the SFDP Sector Map Parameter
* Table.
* @quad_enable: enables SPI NOR quad mode.
* @set_4byte: puts the SPI NOR in 4 byte addressing mode.
* @convert_addr: converts an absolute address into something the flash
* will understand. Particularly useful when pagesize is
* not a power-of-2.
* @setup: configures the SPI NOR memory. Useful for SPI NOR
* flashes that have peculiarities to the SPI NOR standard
* e.g. different opcodes, specific address calculation,
* page size, etc.
* @locking_ops: SPI NOR locking methods.
*/
struct spi_nor_flash_parameter {
u64 size;
u32 page_size;
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];
struct spi_nor_erase_map erase_map;
int (*quad_enable)(struct spi_nor *nor);
int (*set_4byte)(struct spi_nor *nor, bool enable);
u32 (*convert_addr)(struct spi_nor *nor, u32 addr);
int (*setup)(struct spi_nor *nor, const struct spi_nor_hwcaps *hwcaps);
const struct spi_nor_locking_ops *locking_ops;
};
/**
* struct flash_info - Forward declaration of a structure used internally by
* spi_nor_scan()
*/
struct flash_info;
/**
* struct spi_nor - Structure for defining a the SPI NOR layer
* @mtd: point to a mtd_info structure
* @lock: the lock for the read/write/erase/lock/unlock operations
* @dev: point to a spi device, or a spi nor controller device.
* @spimem: point to the spi mem device
* @bouncebuf: bounce buffer used when the buffer passed by the MTD
* layer is not DMA-able
* @bouncebuf_size: size of the bounce buffer
* @info: spi-nor part JDEC MFR id and other info
* @page_size: the page size of the SPI NOR
* @addr_width: number of address bytes
* @erase_opcode: the opcode for erasing a sector
* @read_opcode: the read opcode
* @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_*)
* @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
* @prepare: [OPTIONAL] do some preparations for the
* read/write/erase/lock/unlock operations
* @unprepare: [OPTIONAL] do some post work after the
* read/write/erase/lock/unlock operations
* @read_reg: [DRIVER-SPECIFIC] read out the register
* @write_reg: [DRIVER-SPECIFIC] write data to the register
* @read: [DRIVER-SPECIFIC] read data from the SPI NOR
* @write: [DRIVER-SPECIFIC] write data to the SPI NOR
* @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR
* at the offset @offs; if not provided by the driver,
* spi-nor will send the erase opcode via write_reg()
* @clear_sr_bp: [FLASH-SPECIFIC] clears the Block Protection Bits from
* the SPI NOR Status Register.
* @params: [FLASH-SPECIFIC] SPI-NOR flash parameters and settings.
* The structure includes legacy flash parameters and
* settings that can be overwritten by the spi_nor_fixups
* hooks, or dynamically when parsing the SFDP tables.
* @priv: the private data
*/
struct spi_nor {
struct mtd_info mtd;
struct mutex lock;
struct device *dev;
struct spi_mem *spimem;
u8 *bouncebuf;
size_t bouncebuf_size;
const struct flash_info *info;
u32 page_size;
u8 addr_width;
u8 erase_opcode;
u8 read_opcode;
u8 read_dummy;
u8 program_opcode;
enum spi_nor_protocol read_proto;
enum spi_nor_protocol write_proto;
enum spi_nor_protocol reg_proto;
bool sst_write_second;
u32 flags;
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
ssize_t (*read)(struct spi_nor *nor, loff_t from,
size_t len, u_char *read_buf);
ssize_t (*write)(struct spi_nor *nor, loff_t to,
size_t len, const u_char *write_buf);
int (*erase)(struct spi_nor *nor, loff_t offs);
int (*clear_sr_bp)(struct spi_nor *nor);
struct spi_nor_flash_parameter params;
void *priv;
};
static u64 __maybe_unused
spi_nor_region_is_last(const struct spi_nor_erase_region *region)
{
return region->offset & SNOR_LAST_REGION;
}
static u64 __maybe_unused
spi_nor_region_end(const struct spi_nor_erase_region *region)
{
return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
}
static void __maybe_unused
spi_nor_region_mark_end(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_LAST_REGION;
}
static void __maybe_unused
spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
{
region->offset |= SNOR_OVERLAID_REGION;
}
static bool __maybe_unused spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
return !!nor->params.erase_map.uniform_erase_type;
}
static inline void spi_nor_set_flash_node(struct spi_nor *nor,
struct device_node *np)
{
mtd_set_of_node(&nor->mtd, np);
}
static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
{
return mtd_get_of_node(&nor->mtd);
}
/** /**
* spi_nor_scan() - scan the SPI NOR * spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure * @nor: the spi_nor structure