diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-07 21:17:30 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-07 21:17:30 +0400 |
commit | c29aa153ef0469cddf0146d41ce6494bd76be78b (patch) | |
tree | b49af4e6cf54e9988dd45640a86c4de7f49ddf6d /drivers | |
parent | 2b3a8fd735f86ebeb2b9d061054003000c36b654 (diff) | |
parent | 4a4163caccae97a23d97c29032664ee7b7a498d0 (diff) | |
download | linux-c29aa153ef0469cddf0146d41ce6494bd76be78b.tar.xz |
Merge tag 'for-linus-20140405' of git://git.infradead.org/linux-mtd
Pull MTD updates from Brian Norris:
- A few SPI NOR ID definitions
- Kill the NAND "max pagesize" restriction
- Fix some x16 bus-width NAND support
- Add NAND JEDEC parameter page support
- DT bindings for NAND ECC
- GPMI NAND updates (subpage reads)
- More OMAP NAND refactoring
- New STMicro SPI NOR driver (now in 40 patches!)
- A few other random bugfixes
* tag 'for-linus-20140405' of git://git.infradead.org/linux-mtd: (120 commits)
Fix index regression in nand_read_subpage
mtd: diskonchip: mem resource name is not optional
mtd: nand: fix mention to CONFIG_MTD_NAND_ECC_BCH
mtd: nand: fix GET/SET_FEATURES address on 16-bit devices
mtd: omap2: Use devm_ioremap_resource()
mtd: denali_dt: Use devm_ioremap_resource()
mtd: devices: elm: update DRIVER_NAME as "omap-elm"
mtd: devices: elm: configure parallel channels based on ecc_steps
mtd: devices: elm: clean elm_load_syndrome
mtd: devices: elm: check for hardware engine's design constraints
mtd: st_spi_fsm: Succinctly reorganise .remove()
mtd: st_spi_fsm: Allow loop to run at least once before giving up CPU
mtd: st_spi_fsm: Correct vendor name spelling issue - missing "M"
mtd: st_spi_fsm: Avoid duplicating MTD core code
mtd: st_spi_fsm: Remove useless consts from function arguments
mtd: st_spi_fsm: Convert ST SPI FSM (NOR) Flash driver to new DT partitions
mtd: st_spi_fsm: Move runtime configurable msg sequences into device's struct
mtd: st_spi_fsm: Supply the W25Qxxx chip specific configuration call-back
mtd: st_spi_fsm: Supply the S25FLxxx chip specific configuration call-back
mtd: st_spi_fsm: Supply the MX25xxx chip specific configuration call-back
...
Diffstat (limited to 'drivers')
72 files changed, 2975 insertions, 603 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 5ebcda39f554..5d49a2129618 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -150,7 +150,7 @@ config MTD_BCM63XX_PARTS config MTD_BCM47XX_PARTS tristate "BCM47XX partitioning support" - depends on BCM47XX + depends on BCM47XX || ARCH_BCM_5301X help This provides partitions parser for devices based on BCM47xx boards. diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index de1eb92e42f5..adfa74c1bc45 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -14,7 +14,6 @@ #include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> -#include <bcm47xx_nvram.h> /* 10 parts were found on sflash on Netgear WNDR4500 */ #define BCM47XXPART_MAX_PARTS 12 @@ -30,6 +29,7 @@ #define BOARD_DATA_MAGIC2 0xBD0D0BBD #define CFE_MAGIC 0x43464531 /* 1EFC */ #define FACTORY_MAGIC 0x59544346 /* FCTY */ +#define NVRAM_HEADER 0x48534C46 /* FLSH */ #define POT_MAGIC1 0x54544f50 /* POTT */ #define POT_MAGIC2 0x504f /* OP */ #define ML_MAGIC1 0x39685a42 @@ -91,7 +91,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, if (offset >= 0x2000000) break; - if (curr_part > BCM47XXPART_MAX_PARTS) { + if (curr_part >= BCM47XXPART_MAX_PARTS) { pr_warn("Reached maximum number of partitions, scanning stopped!\n"); break; } @@ -147,6 +147,11 @@ static int bcm47xxpart_parse(struct mtd_info *master, /* TRX */ if (buf[0x000 / 4] == TRX_MAGIC) { + if (BCM47XXPART_MAX_PARTS - curr_part < 4) { + pr_warn("Not enough partitions left to register trx, scanning stopped!\n"); + break; + } + trx = (struct trx_header *)buf; trx_part = curr_part; @@ -212,7 +217,7 @@ static int bcm47xxpart_parse(struct mtd_info *master, /* Look for NVRAM at the end of the last block. */ for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) { - if (curr_part > BCM47XXPART_MAX_PARTS) { + if (curr_part >= BCM47XXPART_MAX_PARTS) { pr_warn("Reached maximum number of partitions, scanning stopped!\n"); break; } diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 77514430f1fe..e4ec355704a6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -21,7 +21,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/init.h> #include <asm/io.h> #include <asm/byteorder.h> @@ -69,10 +68,10 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, s static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); -static int cfi_intelext_get_fact_prot_info (struct mtd_info *, - struct otp_info *, size_t); -static int cfi_intelext_get_user_prot_info (struct mtd_info *, - struct otp_info *, size_t); +static int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t, + size_t *, struct otp_info *); +static int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t, + size_t *, struct otp_info *); #endif static int cfi_intelext_suspend (struct mtd_info *); static void cfi_intelext_resume (struct mtd_info *); @@ -435,10 +434,8 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) int i; mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) { - printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + if (!mtd) return NULL; - } mtd->priv = map; mtd->type = MTD_NORFLASH; @@ -564,10 +561,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); - if (!mtd->eraseregions) { - printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); + if (!mtd->eraseregions) goto setup_err; - } for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { unsigned long ernum, ersize; @@ -2399,24 +2394,19 @@ static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, NULL, do_otp_lock, 1); } -static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) -{ - size_t retlen; - int ret; +static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) - ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0); - return ret ? : retlen; +{ + return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf, + NULL, 0); } -static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { - size_t retlen; - int ret; - - ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1); - return ret ? : retlen; + return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf, + NULL, 1); } #endif diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 89b9d6891532..e21fde9d4d7e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -24,7 +24,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/init.h> #include <asm/io.h> #include <asm/byteorder.h> @@ -507,10 +506,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) int i; mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) { - printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); + if (!mtd) return NULL; - } mtd->priv = map; mtd->type = MTD_NORFLASH; @@ -661,10 +658,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); - if (!mtd->eraseregions) { - printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); + if (!mtd->eraseregions) goto setup_err; - } for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { unsigned long ernum, ersize; diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 096993f9711e..6293855fb5ee 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -22,7 +22,6 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> -#include <linux/init.h> #include <asm/io.h> #include <asm/byteorder.h> @@ -176,7 +175,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); if (!mtd) { - printk(KERN_ERR "Failed to allocate memory for MTD device\n"); kfree(cfi->cmdset_priv); return NULL; } @@ -189,7 +187,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map) mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); if (!mtd->eraseregions) { - printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); kfree(cfi->cmdset_priv); kfree(mtd); return NULL; diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index d25535279404..e8d0164498b0 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -168,10 +168,8 @@ static int __xipram cfi_chip_setup(struct map_info *map, return 0; cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL); - if (!cfi->cfiq) { - printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name); + if (!cfi->cfiq) return 0; - } memset(cfi->cfiq,0,sizeof(struct cfi_ident)); diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index f992418f40a8..08049f6eea60 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -116,10 +116,8 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr); extp = kmalloc(size, GFP_KERNEL); - if (!extp) { - printk(KERN_ERR "Failed to allocate memory\n"); + if (!extp) goto out; - } #ifdef CONFIG_MTD_XIP local_irq_disable(); diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index ffb36ba8a6e0..b57ceea21513 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -114,7 +114,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi mapsize = sizeof(long) * DIV_ROUND_UP(max_chips, BITS_PER_LONG); chip_map = kzalloc(mapsize, GFP_KERNEL); if (!chip_map) { - printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); kfree(cfi.cfiq); return NULL; } @@ -139,7 +138,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL); if (!retcfi) { - printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); kfree(cfi.cfiq); kfree(chip_map); return NULL; diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 01281382180b..1210bc2923b7 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -210,6 +210,14 @@ config MTD_DOCG3 M-Systems and now Sandisk. The support is very experimental, and doesn't give access to any write operations. +config MTD_ST_SPI_FSM + tristate "ST Microelectronics SPI FSM Serial Flash Controller" + depends on ARM || SH + help + This provides an MTD device driver for the ST Microelectronics + SPI Fast Sequence Mode (FSM) Serial Flash Controller and support + for a subset of connected Serial Flash devices. + if MTD_DOCG3 config BCH_CONST_M default 14 diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index d83bd73096f6..c68868f60588 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_OMAP_BCH) += elm.o obj-$(CONFIG_MTD_SPEAR_SMI) += spear_smi.o obj-$(CONFIG_MTD_SST25L) += sst25l.o obj-$(CONFIG_MTD_BCM47XXSFLASH) += bcm47xxsflash.o +obj-$(CONFIG_MTD_ST_SPI_FSM) += st_spi_fsm.o CFLAGS_docg3.o += -I$(src) diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index d9fd87a4c8dc..66f0405f7e53 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -209,7 +209,6 @@ static void block2mtd_free_device(struct block2mtd_dev *dev) } -/* FIXME: ensure that mtd->size % erase_size == 0 */ static struct block2mtd_dev *add_device(char *devname, int erase_size) { const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL; @@ -240,13 +239,18 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) if (IS_ERR(bdev)) { pr_err("error: cannot open device %s\n", devname); - goto devinit_err; + goto err_free_block2mtd; } dev->blkdev = bdev; if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { pr_err("attempting to use an MTD device as a block device\n"); - goto devinit_err; + goto err_free_block2mtd; + } + + if ((long)dev->blkdev->bd_inode->i_size % erase_size) { + pr_err("erasesize must be a divisor of device size\n"); + goto err_free_block2mtd; } mutex_init(&dev->write_mutex); @@ -255,7 +259,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* make the name contain the block device in */ name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname); if (!name) - goto devinit_err; + goto err_destroy_mutex; dev->mtd.name = name; @@ -274,7 +278,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) if (mtd_device_register(&dev->mtd, NULL, 0)) { /* Device didn't get added, so free the entry */ - goto devinit_err; + goto err_destroy_mutex; } list_add(&dev->list, &blkmtd_device_list); pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", @@ -283,7 +287,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) dev->mtd.erasesize >> 10, dev->mtd.erasesize); return dev; -devinit_err: +err_destroy_mutex: + mutex_destroy(&dev->write_mutex); +err_free_block2mtd: block2mtd_free_device(dev); return NULL; } @@ -448,6 +454,7 @@ static void block2mtd_exit(void) struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); block2mtd_sync(&dev->mtd); mtd_device_unregister(&dev->mtd); + mutex_destroy(&dev->write_mutex); pr_info("mtd%d: [%s] removed\n", dev->mtd.index, dev->mtd.name + strlen("block2mtd: ")); diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index d1dd6a33a050..1fd4a0f77967 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -15,6 +15,8 @@ * */ +#define DRIVER_NAME "omap-elm" + #include <linux/platform_device.h> #include <linux/module.h> #include <linux/interrupt.h> @@ -84,6 +86,8 @@ struct elm_info { struct list_head list; enum bch_ecc bch_type; struct elm_registers elm_regs; + int ecc_steps; + int ecc_syndrome_size; }; static LIST_HEAD(elm_devices); @@ -103,7 +107,8 @@ static u32 elm_read_reg(struct elm_info *info, int offset) * @dev: ELM device * @bch_type: Type of BCH ecc */ -int elm_config(struct device *dev, enum bch_ecc bch_type) +int elm_config(struct device *dev, enum bch_ecc bch_type, + int ecc_steps, int ecc_step_size, int ecc_syndrome_size) { u32 reg_val; struct elm_info *info = dev_get_drvdata(dev); @@ -112,10 +117,22 @@ int elm_config(struct device *dev, enum bch_ecc bch_type) dev_err(dev, "Unable to configure elm - device not probed?\n"); return -ENODEV; } + /* ELM cannot detect ECC errors for chunks > 1KB */ + if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) { + dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size); + return -EINVAL; + } + /* ELM support 8 error syndrome process */ + if (ecc_steps > ERROR_VECTOR_MAX) { + dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps); + return -EINVAL; + } reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16); elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val); - info->bch_type = bch_type; + info->bch_type = bch_type; + info->ecc_steps = ecc_steps; + info->ecc_syndrome_size = ecc_syndrome_size; return 0; } @@ -157,17 +174,15 @@ static void elm_load_syndrome(struct elm_info *info, int i, offset; u32 val; - for (i = 0; i < ERROR_VECTOR_MAX; i++) { + for (i = 0; i < info->ecc_steps; i++) { /* Check error reported */ if (err_vec[i].error_reported) { elm_configure_page_mode(info, i, true); offset = ELM_SYNDROME_FRAGMENT_0 + SYNDROME_FRAGMENT_REG_SIZE * i; - - /* BCH8 */ - if (info->bch_type) { - + switch (info->bch_type) { + case BCH8_ECC: /* syndrome fragment 0 = ecc[9-12B] */ val = cpu_to_be32(*(u32 *) &ecc[9]); elm_write_reg(info, offset, val); @@ -186,7 +201,8 @@ static void elm_load_syndrome(struct elm_info *info, offset += 4; val = ecc[0]; elm_write_reg(info, offset, val); - } else { + break; + case BCH4_ECC: /* syndrome fragment 0 = ecc[20-52b] bits */ val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) | ((ecc[2] & 0xf) << 28); @@ -196,11 +212,14 @@ static void elm_load_syndrome(struct elm_info *info, offset += 4; val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12; elm_write_reg(info, offset, val); + break; + default: + pr_err("invalid config bch_type\n"); } } /* Update ecc pointer with ecc byte size */ - ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE; + ecc += info->ecc_syndrome_size; } } @@ -223,7 +242,7 @@ static void elm_start_processing(struct elm_info *info, * Set syndrome vector valid, so that ELM module * will process it for vectors error is reported */ - for (i = 0; i < ERROR_VECTOR_MAX; i++) { + for (i = 0; i < info->ecc_steps; i++) { if (err_vec[i].error_reported) { offset = ELM_SYNDROME_FRAGMENT_6 + SYNDROME_FRAGMENT_REG_SIZE * i; @@ -252,7 +271,7 @@ static void elm_error_correction(struct elm_info *info, int offset; u32 reg_val; - for (i = 0; i < ERROR_VECTOR_MAX; i++) { + for (i = 0; i < info->ecc_steps; i++) { /* Check error reported */ if (err_vec[i].error_reported) { @@ -354,10 +373,8 @@ static int elm_probe(struct platform_device *pdev) struct elm_info *info; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); - if (!info) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + if (!info) return -ENOMEM; - } info->dev = &pdev->dev; @@ -380,7 +397,7 @@ static int elm_probe(struct platform_device *pdev) } pm_runtime_enable(&pdev->dev); - if (pm_runtime_get_sync(&pdev->dev)) { + if (pm_runtime_get_sync(&pdev->dev) < 0) { ret = -EINVAL; pm_runtime_disable(&pdev->dev); dev_err(&pdev->dev, "can't enable clock\n"); @@ -505,7 +522,7 @@ MODULE_DEVICE_TABLE(of, elm_of_match); static struct platform_driver elm_driver = { .driver = { - .name = "elm", + .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(elm_of_match), .pm = &elm_pm_ops, diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index ad1913909702..524dab3ac938 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -15,7 +15,6 @@ * */ -#include <linux/init.h> #include <linux/err.h> #include <linux/errno.h> #include <linux/module.h> @@ -41,7 +40,8 @@ #define OPCODE_WRSR 0x01 /* Write status register 1 byte */ #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ -#define OPCODE_QUAD_READ 0x6b /* Read data bytes */ +#define OPCODE_DUAL_READ 0x3b /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ 0x6b /* Read data bytes (Quad SPI) */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ #define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ @@ -54,7 +54,8 @@ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ -#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes */ +#define OPCODE_DUAL_READ_4B 0x3c /* Read data bytes (Dual SPI) */ +#define OPCODE_QUAD_READ_4B 0x6c /* Read data bytes (Quad SPI) */ #define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ @@ -95,6 +96,7 @@ enum read_type { M25P80_NORMAL = 0, M25P80_FAST, + M25P80_DUAL, M25P80_QUAD, }; @@ -479,6 +481,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash) { switch (flash->flash_read) { case M25P80_FAST: + case M25P80_DUAL: case M25P80_QUAD: return 1; case M25P80_NORMAL: @@ -492,6 +495,8 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash) static inline unsigned int m25p80_rx_nbits(const struct m25p *flash) { switch (flash->flash_read) { + case M25P80_DUAL: + return 2; case M25P80_QUAD: return 4; default: @@ -855,7 +860,8 @@ struct flash_info { #define SST_WRITE 0x04 /* use SST byte programming */ #define M25P_NO_FR 0x08 /* Can't do fastread */ #define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ -#define M25P80_QUAD_READ 0x20 /* Flash supports Quad Read */ +#define M25P80_DUAL_READ 0x20 /* Flash supports Dual Read */ +#define M25P80_QUAD_READ 0x40 /* Flash supports Quad Read */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -934,6 +940,7 @@ static const struct spi_device_id m25p_ids[] = { { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) }, { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) }, { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) }, + { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) }, /* Micron */ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, @@ -953,8 +960,8 @@ static const struct spi_device_id m25p_ids[] = { { "s25sl032p", INFO(0x010215, 0x4d00, 64 * 1024, 64, 0) }, { "s25sl064p", INFO(0x010216, 0x4d00, 64 * 1024, 128, 0) }, { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) }, - { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_QUAD_READ) }, - { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_QUAD_READ) }, + { "s25fl256s1", INFO(0x010219, 0x4d01, 64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) }, + { "s25fl512s", INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) }, { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) }, { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) }, { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) }, @@ -965,6 +972,7 @@ static const struct spi_device_id m25p_ids[] = { { "s25sl016a", INFO(0x010214, 0, 64 * 1024, 32, 0) }, { "s25sl032a", INFO(0x010215, 0, 64 * 1024, 64, 0) }, { "s25sl064a", INFO(0x010216, 0, 64 * 1024, 128, 0) }, + { "s25fl008k", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "s25fl016k", INFO(0xef4015, 0, 64 * 1024, 32, SECT_4K) }, { "s25fl064k", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, @@ -1072,9 +1080,8 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi) for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) { info = (void *)m25p_ids[tmp].driver_data; if (info->jedec_id == jedec) { - if (info->ext_id != 0 && info->ext_id != ext_jedec) - continue; - return &m25p_ids[tmp]; + if (info->ext_id == 0 || info->ext_id == ext_jedec) + return &m25p_ids[tmp]; } } dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); @@ -1226,7 +1233,7 @@ static int m25p_probe(struct spi_device *spi) if (info->flags & M25P_NO_FR) flash->flash_read = M25P80_NORMAL; - /* Quad-read mode takes precedence over fast/normal */ + /* Quad/Dual-read mode takes precedence over fast/normal */ if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) { ret = set_quad_mode(flash, info->jedec_id); if (ret) { @@ -1234,6 +1241,8 @@ static int m25p_probe(struct spi_device *spi) return ret; } flash->flash_read = M25P80_QUAD; + } else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) { + flash->flash_read = M25P80_DUAL; } /* Default commands */ @@ -1241,6 +1250,9 @@ static int m25p_probe(struct spi_device *spi) case M25P80_QUAD: flash->read_opcode = OPCODE_QUAD_READ; break; + case M25P80_DUAL: + flash->read_opcode = OPCODE_DUAL_READ; + break; case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ; break; @@ -1265,6 +1277,9 @@ static int m25p_probe(struct spi_device *spi) case M25P80_QUAD: flash->read_opcode = OPCODE_QUAD_READ_4B; break; + case M25P80_DUAL: + flash->read_opcode = OPCODE_DUAL_READ_4B; + break; case M25P80_FAST: flash->read_opcode = OPCODE_FAST_READ_4B; break; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 624069de4f28..dd22ce2cc9ad 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -10,7 +10,6 @@ * 2 of the License, or (at your option) any later version. */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/device.h> @@ -440,8 +439,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, #ifdef CONFIG_MTD_DATAFLASH_OTP -static int dataflash_get_otp_info(struct mtd_info *mtd, - struct otp_info *info, size_t len) +static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *info) { /* Report both blocks as identical: bytes 0..64, locked. * Unless the user block changed from all-ones, we can't @@ -450,7 +449,8 @@ static int dataflash_get_otp_info(struct mtd_info *mtd, info->start = 0; info->length = 64; info->locked = 1; - return sizeof(*info); + *retlen = sizeof(*info); + return 0; } static ssize_t otp_read(struct spi_device *spi, unsigned base, @@ -542,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd, struct dataflash *priv = mtd->priv; int status; - if (len > 64) - return -EINVAL; + if (from >= 64) { + /* + * Attempting to write beyond the end of OTP memory, + * no data can be written. + */ + *retlen = 0; + return 0; + } - /* Strictly speaking, we *could* truncate the write ... but - * let's not do that for the only write that's ever possible. - */ + /* Truncate the write to fit into OTP memory. */ if ((from + len) > 64) - return -EINVAL; + len = 64 - from; /* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes * IN: ignore all diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index e1f2aebaa489..2cceebfb251e 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -205,6 +205,8 @@ static inline void kill_final_newline(char *str) return 1; \ } while (0) +#ifndef MODULE +static int phram_init_called; /* * This shall contain the module parameter if any. It is of the form: * - phram=<device>,<address>,<size> for module case @@ -213,9 +215,10 @@ static inline void kill_final_newline(char *str) * size. * Example: phram.phram=rootfs,0xa0000000,512Mi */ -static __initdata char phram_paramline[64 + 20 + 20]; +static char phram_paramline[64 + 20 + 20]; +#endif -static int __init phram_setup(const char *val) +static int phram_setup(const char *val) { char buf[64 + 20 + 20], *str = buf; char *token[3]; @@ -264,17 +267,36 @@ static int __init phram_setup(const char *val) return ret; } -static int __init phram_param_call(const char *val, struct kernel_param *kp) +static int phram_param_call(const char *val, struct kernel_param *kp) { +#ifdef MODULE + return phram_setup(val); +#else /* - * This function is always called before 'init_phram()', whether - * built-in or module. + * If more parameters are later passed in via + * /sys/module/phram/parameters/phram + * and init_phram() has already been called, + * we can parse the argument now. */ + + if (phram_init_called) + return phram_setup(val); + + /* + * During early boot stage, we only save the parameters + * here. We must parse them later: if the param passed + * from kernel boot command line, phram_param_call() is + * called so early that it is not possible to resolve + * the device (even kmalloc() fails). Defer that work to + * phram_setup(). + */ + if (strlen(val) >= sizeof(phram_paramline)) return -ENOSPC; strcpy(phram_paramline, val); return 0; +#endif } module_param_call(phram, phram_param_call, NULL, NULL, 000); @@ -283,10 +305,15 @@ MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\" static int __init init_phram(void) { + int ret = 0; + +#ifndef MODULE if (phram_paramline[0]) - return phram_setup(phram_paramline); + ret = phram_setup(phram_paramline); + phram_init_called = 1; +#endif - return 0; + return ret; } static void __exit cleanup_phram(void) diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 0c51b988e1f8..f02603e1bfeb 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -725,16 +725,11 @@ static int __init init_pmc551(void) } mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!mtd) { - printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " - "device.\n"); + if (!mtd) break; - } priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL); if (!priv) { - printk(KERN_NOTICE "pmc551: Cannot allocate new MTD " - "device.\n"); kfree(mtd); break; } diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h new file mode 100644 index 000000000000..4f0c2c7c898e --- /dev/null +++ b/drivers/mtd/devices/serial_flash_cmds.h @@ -0,0 +1,81 @@ +/* + * Generic/SFDP Flash Commands and Device Capabilities + * + * Copyright (C) 2013 Lee Jones <lee.jones@lianro.org> + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _MTD_SERIAL_FLASH_CMDS_H +#define _MTD_SERIAL_FLASH_CMDS_H + +/* Generic Flash Commands/OPCODEs */ +#define FLASH_CMD_WREN 0x06 +#define FLASH_CMD_WRDI 0x04 +#define FLASH_CMD_RDID 0x9f +#define FLASH_CMD_RDSR 0x05 +#define FLASH_CMD_RDSR2 0x35 +#define FLASH_CMD_WRSR 0x01 +#define FLASH_CMD_SE_4K 0x20 +#define FLASH_CMD_SE_32K 0x52 +#define FLASH_CMD_SE 0xd8 +#define FLASH_CMD_CHIPERASE 0xc7 +#define FLASH_CMD_WRVCR 0x81 +#define FLASH_CMD_RDVCR 0x85 + +/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ +#define FLASH_CMD_READ 0x03 /* READ */ +#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */ +#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */ +#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */ +#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */ +#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */ + +#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */ +#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ +#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ +#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ +#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ + +#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */ +#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */ + +/* READ commands with 32-bit addressing */ +#define FLASH_CMD_READ4 0x13 +#define FLASH_CMD_READ4_FAST 0x0c +#define FLASH_CMD_READ4_1_1_2 0x3c +#define FLASH_CMD_READ4_1_2_2 0xbc +#define FLASH_CMD_READ4_1_1_4 0x6c +#define FLASH_CMD_READ4_1_4_4 0xec + +/* Configuration flags */ +#define FLASH_FLAG_SINGLE 0x000000ff +#define FLASH_FLAG_READ_WRITE 0x00000001 +#define FLASH_FLAG_READ_FAST 0x00000002 +#define FLASH_FLAG_SE_4K 0x00000004 +#define FLASH_FLAG_SE_32K 0x00000008 +#define FLASH_FLAG_CE 0x00000010 +#define FLASH_FLAG_32BIT_ADDR 0x00000020 +#define FLASH_FLAG_RESET 0x00000040 +#define FLASH_FLAG_DYB_LOCKING 0x00000080 + +#define FLASH_FLAG_DUAL 0x0000ff00 +#define FLASH_FLAG_READ_1_1_2 0x00000100 +#define FLASH_FLAG_READ_1_2_2 0x00000200 +#define FLASH_FLAG_READ_2_2_2 0x00000400 +#define FLASH_FLAG_WRITE_1_1_2 0x00001000 +#define FLASH_FLAG_WRITE_1_2_2 0x00002000 +#define FLASH_FLAG_WRITE_2_2_2 0x00004000 + +#define FLASH_FLAG_QUAD 0x00ff0000 +#define FLASH_FLAG_READ_1_1_4 0x00010000 +#define FLASH_FLAG_READ_1_4_4 0x00020000 +#define FLASH_FLAG_READ_4_4_4 0x00040000 +#define FLASH_FLAG_WRITE_1_1_4 0x00100000 +#define FLASH_FLAG_WRITE_1_4_4 0x00200000 +#define FLASH_FLAG_WRITE_4_4_4 0x00400000 + +#endif /* _MTD_SERIAL_FLASH_CMDS_H */ diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 423821412062..363da96e6891 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -913,7 +913,6 @@ static int spear_smi_probe(struct platform_device *pdev) if (np) { pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { - pr_err("%s: ERROR: no memory", __func__); ret = -ENOMEM; goto err; } @@ -943,7 +942,6 @@ static int spear_smi_probe(struct platform_device *pdev) dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC); if (!dev) { ret = -ENOMEM; - dev_err(&pdev->dev, "mem alloc fail\n"); goto err; } diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 687bf27ec850..c63ecbcad0b7 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -15,7 +15,6 @@ * */ -#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/mutex.h> diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c new file mode 100644 index 000000000000..1957d7c8e185 --- /dev/null +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -0,0 +1,2108 @@ +/* + * st_spi_fsm.c - ST Fast Sequence Mode (FSM) Serial Flash Controller + * + * Author: Angus Clark <angus.clark@st.com> + * + * Copyright (C) 2010-2014 STMicroelectronics Limited + * + * JEDEC probe based on drivers/mtd/devices/m25p80.c + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/mfd/syscon.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/of.h> + +#include "serial_flash_cmds.h" + +/* + * FSM SPI Controller Registers + */ +#define SPI_CLOCKDIV 0x0010 +#define SPI_MODESELECT 0x0018 +#define SPI_CONFIGDATA 0x0020 +#define SPI_STA_MODE_CHANGE 0x0028 +#define SPI_FAST_SEQ_TRANSFER_SIZE 0x0100 +#define SPI_FAST_SEQ_ADD1 0x0104 +#define SPI_FAST_SEQ_ADD2 0x0108 +#define SPI_FAST_SEQ_ADD_CFG 0x010c +#define SPI_FAST_SEQ_OPC1 0x0110 +#define SPI_FAST_SEQ_OPC2 0x0114 +#define SPI_FAST_SEQ_OPC3 0x0118 +#define SPI_FAST_SEQ_OPC4 0x011c +#define SPI_FAST_SEQ_OPC5 0x0120 +#define SPI_MODE_BITS 0x0124 +#define SPI_DUMMY_BITS 0x0128 +#define SPI_FAST_SEQ_FLASH_STA_DATA 0x012c +#define SPI_FAST_SEQ_1 0x0130 +#define SPI_FAST_SEQ_2 0x0134 +#define SPI_FAST_SEQ_3 0x0138 +#define SPI_FAST_SEQ_4 0x013c +#define SPI_FAST_SEQ_CFG 0x0140 +#define SPI_FAST_SEQ_STA 0x0144 +#define SPI_QUAD_BOOT_SEQ_INIT_1 0x0148 +#define SPI_QUAD_BOOT_SEQ_INIT_2 0x014c +#define SPI_QUAD_BOOT_READ_SEQ_1 0x0150 +#define SPI_QUAD_BOOT_READ_SEQ_2 0x0154 +#define SPI_PROGRAM_ERASE_TIME 0x0158 +#define SPI_MULT_PAGE_REPEAT_SEQ_1 0x015c +#define SPI_MULT_PAGE_REPEAT_SEQ_2 0x0160 +#define SPI_STATUS_WR_TIME_REG 0x0164 +#define SPI_FAST_SEQ_DATA_REG 0x0300 + +/* + * Register: SPI_MODESELECT + */ +#define SPI_MODESELECT_CONTIG 0x01 +#define SPI_MODESELECT_FASTREAD 0x02 +#define SPI_MODESELECT_DUALIO 0x04 +#define SPI_MODESELECT_FSM 0x08 +#define SPI_MODESELECT_QUADBOOT 0x10 + +/* + * Register: SPI_CONFIGDATA + */ +#define SPI_CFG_DEVICE_ST 0x1 +#define SPI_CFG_DEVICE_ATMEL 0x4 +#define SPI_CFG_MIN_CS_HIGH(x) (((x) & 0xfff) << 4) +#define SPI_CFG_CS_SETUPHOLD(x) (((x) & 0xff) << 16) +#define SPI_CFG_DATA_HOLD(x) (((x) & 0xff) << 24) + +#define SPI_CFG_DEFAULT_MIN_CS_HIGH SPI_CFG_MIN_CS_HIGH(0x0AA) +#define SPI_CFG_DEFAULT_CS_SETUPHOLD SPI_CFG_CS_SETUPHOLD(0xA0) +#define SPI_CFG_DEFAULT_DATA_HOLD SPI_CFG_DATA_HOLD(0x00) + +/* + * Register: SPI_FAST_SEQ_TRANSFER_SIZE + */ +#define TRANSFER_SIZE(x) ((x) * 8) + +/* + * Register: SPI_FAST_SEQ_ADD_CFG + */ +#define ADR_CFG_CYCLES_ADD1(x) ((x) << 0) +#define ADR_CFG_PADS_1_ADD1 (0x0 << 6) +#define ADR_CFG_PADS_2_ADD1 (0x1 << 6) +#define ADR_CFG_PADS_4_ADD1 (0x3 << 6) +#define ADR_CFG_CSDEASSERT_ADD1 (1 << 8) +#define ADR_CFG_CYCLES_ADD2(x) ((x) << (0+16)) +#define ADR_CFG_PADS_1_ADD2 (0x0 << (6+16)) +#define ADR_CFG_PADS_2_ADD2 (0x1 << (6+16)) +#define ADR_CFG_PADS_4_ADD2 (0x3 << (6+16)) +#define ADR_CFG_CSDEASSERT_ADD2 (1 << (8+16)) + +/* + * Register: SPI_FAST_SEQ_n + */ +#define SEQ_OPC_OPCODE(x) ((x) << 0) +#define SEQ_OPC_CYCLES(x) ((x) << 8) +#define SEQ_OPC_PADS_1 (0x0 << 14) +#define SEQ_OPC_PADS_2 (0x1 << 14) +#define SEQ_OPC_PADS_4 (0x3 << 14) +#define SEQ_OPC_CSDEASSERT (1 << 16) + +/* + * Register: SPI_FAST_SEQ_CFG + */ +#define SEQ_CFG_STARTSEQ (1 << 0) +#define SEQ_CFG_SWRESET (1 << 5) +#define SEQ_CFG_CSDEASSERT (1 << 6) +#define SEQ_CFG_READNOTWRITE (1 << 7) +#define SEQ_CFG_ERASE (1 << 8) +#define SEQ_CFG_PADS_1 (0x0 << 16) +#define SEQ_CFG_PADS_2 (0x1 << 16) +#define SEQ_CFG_PADS_4 (0x3 << 16) + +/* + * Register: SPI_MODE_BITS + */ +#define MODE_DATA(x) (x & 0xff) +#define MODE_CYCLES(x) ((x & 0x3f) << 16) +#define MODE_PADS_1 (0x0 << 22) +#define MODE_PADS_2 (0x1 << 22) +#define MODE_PADS_4 (0x3 << 22) +#define DUMMY_CSDEASSERT (1 << 24) + +/* + * Register: SPI_DUMMY_BITS + */ +#define DUMMY_CYCLES(x) ((x & 0x3f) << 16) +#define DUMMY_PADS_1 (0x0 << 22) +#define DUMMY_PADS_2 (0x1 << 22) +#define DUMMY_PADS_4 (0x3 << 22) +#define DUMMY_CSDEASSERT (1 << 24) + +/* + * Register: SPI_FAST_SEQ_FLASH_STA_DATA + */ +#define STA_DATA_BYTE1(x) ((x & 0xff) << 0) +#define STA_DATA_BYTE2(x) ((x & 0xff) << 8) +#define STA_PADS_1 (0x0 << 16) +#define STA_PADS_2 (0x1 << 16) +#define STA_PADS_4 (0x3 << 16) +#define STA_CSDEASSERT (0x1 << 20) +#define STA_RDNOTWR (0x1 << 21) + +/* + * FSM SPI Instruction Opcodes + */ +#define STFSM_OPC_CMD 0x1 +#define STFSM_OPC_ADD 0x2 +#define STFSM_OPC_STA 0x3 +#define STFSM_OPC_MODE 0x4 +#define STFSM_OPC_DUMMY 0x5 +#define STFSM_OPC_DATA 0x6 +#define STFSM_OPC_WAIT 0x7 +#define STFSM_OPC_JUMP 0x8 +#define STFSM_OPC_GOTO 0x9 +#define STFSM_OPC_STOP 0xF + +/* + * FSM SPI Instructions (== opcode + operand). + */ +#define STFSM_INSTR(cmd, op) ((cmd) | ((op) << 4)) + +#define STFSM_INST_CMD1 STFSM_INSTR(STFSM_OPC_CMD, 1) +#define STFSM_INST_CMD2 STFSM_INSTR(STFSM_OPC_CMD, 2) +#define STFSM_INST_CMD3 STFSM_INSTR(STFSM_OPC_CMD, 3) +#define STFSM_INST_CMD4 STFSM_INSTR(STFSM_OPC_CMD, 4) +#define STFSM_INST_CMD5 STFSM_INSTR(STFSM_OPC_CMD, 5) +#define STFSM_INST_ADD1 STFSM_INSTR(STFSM_OPC_ADD, 1) +#define STFSM_INST_ADD2 STFSM_INSTR(STFSM_OPC_ADD, 2) + +#define STFSM_INST_DATA_WRITE STFSM_INSTR(STFSM_OPC_DATA, 1) +#define STFSM_INST_DATA_READ STFSM_INSTR(STFSM_OPC_DATA, 2) + +#define STFSM_INST_STA_RD1 STFSM_INSTR(STFSM_OPC_STA, 0x1) +#define STFSM_INST_STA_WR1 STFSM_INSTR(STFSM_OPC_STA, 0x1) +#define STFSM_INST_STA_RD2 STFSM_INSTR(STFSM_OPC_STA, 0x2) +#define STFSM_INST_STA_WR1_2 STFSM_INSTR(STFSM_OPC_STA, 0x3) + +#define STFSM_INST_MODE STFSM_INSTR(STFSM_OPC_MODE, 0) +#define STFSM_INST_DUMMY STFSM_INSTR(STFSM_OPC_DUMMY, 0) +#define STFSM_INST_WAIT STFSM_INSTR(STFSM_OPC_WAIT, 0) +#define STFSM_INST_STOP STFSM_INSTR(STFSM_OPC_STOP, 0) + +#define STFSM_DEFAULT_EMI_FREQ 100000000UL /* 100 MHz */ +#define STFSM_DEFAULT_WR_TIME (STFSM_DEFAULT_EMI_FREQ * (15/1000)) /* 15ms */ + +#define STFSM_FLASH_SAFE_FREQ 10000000UL /* 10 MHz */ + +#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */ + +/* Flash Commands */ +#define FLASH_CMD_WREN 0x06 +#define FLASH_CMD_WRDI 0x04 +#define FLASH_CMD_RDID 0x9f +#define FLASH_CMD_RDSR 0x05 +#define FLASH_CMD_RDSR2 0x35 +#define FLASH_CMD_WRSR 0x01 +#define FLASH_CMD_SE_4K 0x20 +#define FLASH_CMD_SE_32K 0x52 +#define FLASH_CMD_SE 0xd8 +#define FLASH_CMD_CHIPERASE 0xc7 +#define FLASH_CMD_WRVCR 0x81 +#define FLASH_CMD_RDVCR 0x85 + +#define FLASH_CMD_READ 0x03 /* READ */ +#define FLASH_CMD_READ_FAST 0x0b /* FAST READ */ +#define FLASH_CMD_READ_1_1_2 0x3b /* DUAL OUTPUT READ */ +#define FLASH_CMD_READ_1_2_2 0xbb /* DUAL I/O READ */ +#define FLASH_CMD_READ_1_1_4 0x6b /* QUAD OUTPUT READ */ +#define FLASH_CMD_READ_1_4_4 0xeb /* QUAD I/O READ */ + +#define FLASH_CMD_WRITE 0x02 /* PAGE PROGRAM */ +#define FLASH_CMD_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ +#define FLASH_CMD_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ +#define FLASH_CMD_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ +#define FLASH_CMD_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ + +#define FLASH_CMD_EN4B_ADDR 0xb7 /* Enter 4-byte address mode */ +#define FLASH_CMD_EX4B_ADDR 0xe9 /* Exit 4-byte address mode */ + +/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */ +#define FLASH_CMD_READ4 0x13 +#define FLASH_CMD_READ4_FAST 0x0c +#define FLASH_CMD_READ4_1_1_2 0x3c +#define FLASH_CMD_READ4_1_2_2 0xbc +#define FLASH_CMD_READ4_1_1_4 0x6c +#define FLASH_CMD_READ4_1_4_4 0xec + +/* S25FLxxxS commands */ +#define S25FL_CMD_WRITE4_1_1_4 0x34 +#define S25FL_CMD_SE4 0xdc +#define S25FL_CMD_CLSR 0x30 +#define S25FL_CMD_DYBWR 0xe1 +#define S25FL_CMD_DYBRD 0xe0 +#define S25FL_CMD_WRITE4 0x12 /* Note, opcode clashes with + * 'FLASH_CMD_WRITE_1_4_4' + * as found on N25Qxxx devices! */ + +/* Status register */ +#define FLASH_STATUS_BUSY 0x01 +#define FLASH_STATUS_WEL 0x02 +#define FLASH_STATUS_BP0 0x04 +#define FLASH_STATUS_BP1 0x08 +#define FLASH_STATUS_BP2 0x10 +#define FLASH_STATUS_SRWP0 0x80 +#define FLASH_STATUS_TIMEOUT 0xff +/* S25FL Error Flags */ +#define S25FL_STATUS_E_ERR 0x20 +#define S25FL_STATUS_P_ERR 0x40 + +#define FLASH_PAGESIZE 256 /* In Bytes */ +#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */ +#define FLASH_MAX_BUSY_WAIT (300 * HZ) /* Maximum 'CHIPERASE' time */ + +/* + * Flags to tweak operation of default read/write/erase routines + */ +#define CFG_READ_TOGGLE_32BIT_ADDR 0x00000001 +#define CFG_WRITE_TOGGLE_32BIT_ADDR 0x00000002 +#define CFG_WRITE_EX_32BIT_ADDR_DELAY 0x00000004 +#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008 +#define CFG_S25FL_CHECK_ERROR_FLAGS 0x00000010 + +struct stfsm_seq { + uint32_t data_size; + uint32_t addr1; + uint32_t addr2; + uint32_t addr_cfg; + uint32_t seq_opc[5]; + uint32_t mode; + uint32_t dummy; + uint32_t status; + uint8_t seq[16]; + uint32_t seq_cfg; +} __packed __aligned(4); + +struct stfsm { + struct device *dev; + void __iomem *base; + struct resource *region; + struct mtd_info mtd; + struct mutex lock; + struct flash_info *info; + + uint32_t configuration; + uint32_t fifo_dir_delay; + bool booted_from_spi; + bool reset_signal; + bool reset_por; + + struct stfsm_seq stfsm_seq_read; + struct stfsm_seq stfsm_seq_write; + struct stfsm_seq stfsm_seq_en_32bit_addr; +}; + +/* Parameters to configure a READ or WRITE FSM sequence */ +struct seq_rw_config { + uint32_t flags; /* flags to support config */ + uint8_t cmd; /* FLASH command */ + int write; /* Write Sequence */ + uint8_t addr_pads; /* No. of addr pads (MODE & DUMMY) */ + uint8_t data_pads; /* No. of data pads */ + uint8_t mode_data; /* MODE data */ + uint8_t mode_cycles; /* No. of MODE cycles */ + uint8_t dummy_cycles; /* No. of DUMMY cycles */ +}; + +/* SPI Flash Device Table */ +struct flash_info { + char *name; + /* + * JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + u16 ext_id; + /* + * The size listed here is what works with FLASH_CMD_SE, which isn't + * necessarily called a "sector" by the vendor. + */ + unsigned sector_size; + u16 n_sectors; + u32 flags; + /* + * Note, where FAST_READ is supported, freq_max specifies the + * FAST_READ frequency, not the READ frequency. + */ + u32 max_freq; + int (*config)(struct stfsm *); +}; + +static int stfsm_n25q_config(struct stfsm *fsm); +static int stfsm_mx25_config(struct stfsm *fsm); +static int stfsm_s25fl_config(struct stfsm *fsm); +static int stfsm_w25q_config(struct stfsm *fsm); + +static struct flash_info flash_types[] = { + /* + * ST Microelectronics/Numonyx -- + * (newer production versions may have feature updates + * (eg faster operating frequency) + */ +#define M25P_FLAG (FLASH_FLAG_READ_WRITE | FLASH_FLAG_READ_FAST) + { "m25p40", 0x202013, 0, 64 * 1024, 8, M25P_FLAG, 25, NULL }, + { "m25p80", 0x202014, 0, 64 * 1024, 16, M25P_FLAG, 25, NULL }, + { "m25p16", 0x202015, 0, 64 * 1024, 32, M25P_FLAG, 25, NULL }, + { "m25p32", 0x202016, 0, 64 * 1024, 64, M25P_FLAG, 50, NULL }, + { "m25p64", 0x202017, 0, 64 * 1024, 128, M25P_FLAG, 50, NULL }, + { "m25p128", 0x202018, 0, 256 * 1024, 64, M25P_FLAG, 50, NULL }, + +#define M25PX_FLAG (FLASH_FLAG_READ_WRITE | \ + FLASH_FLAG_READ_FAST | \ + FLASH_FLAG_READ_1_1_2 | \ + FLASH_FLAG_WRITE_1_1_2) + { "m25px32", 0x207116, 0, 64 * 1024, 64, M25PX_FLAG, 75, NULL }, + { "m25px64", 0x207117, 0, 64 * 1024, 128, M25PX_FLAG, 75, NULL }, + +#define MX25_FLAG (FLASH_FLAG_READ_WRITE | \ + FLASH_FLAG_READ_FAST | \ + FLASH_FLAG_READ_1_1_2 | \ + FLASH_FLAG_READ_1_2_2 | \ + FLASH_FLAG_READ_1_1_4 | \ + FLASH_FLAG_READ_1_4_4 | \ + FLASH_FLAG_SE_4K | \ + FLASH_FLAG_SE_32K) + { "mx25l25635e", 0xc22019, 0, 64*1024, 512, + (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, + stfsm_mx25_config }, + +#define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \ + FLASH_FLAG_READ_FAST | \ + FLASH_FLAG_READ_1_1_2 | \ + FLASH_FLAG_READ_1_2_2 | \ + FLASH_FLAG_READ_1_1_4 | \ + FLASH_FLAG_READ_1_4_4 | \ + FLASH_FLAG_WRITE_1_1_2 | \ + FLASH_FLAG_WRITE_1_2_2 | \ + FLASH_FLAG_WRITE_1_1_4 | \ + FLASH_FLAG_WRITE_1_4_4) + { "n25q128", 0x20ba18, 0, 64 * 1024, 256, N25Q_FLAG, 108, + stfsm_n25q_config }, + { "n25q256", 0x20ba19, 0, 64 * 1024, 512, + N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, stfsm_n25q_config }, + + /* + * Spansion S25FLxxxP + * - 256KiB and 64KiB sector variants (identified by ext. JEDEC) + */ +#define S25FLXXXP_FLAG (FLASH_FLAG_READ_WRITE | \ + FLASH_FLAG_READ_1_1_2 | \ + FLASH_FLAG_READ_1_2_2 | \ + FLASH_FLAG_READ_1_1_4 | \ + FLASH_FLAG_READ_1_4_4 | \ + FLASH_FLAG_WRITE_1_1_4 | \ + FLASH_FLAG_READ_FAST) + { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80, + stfsm_s25fl_config }, + { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80, + stfsm_s25fl_config }, + + /* + * Spansion S25FLxxxS + * - 256KiB and 64KiB sector variants (identified by ext. JEDEC) + * - RESET# signal supported by die but not bristled out on all + * package types. The package type is a function of board design, + * so this information is captured in the board's flags. + * - Supports 'DYB' sector protection. Depending on variant, sectors + * may default to locked state on power-on. + */ +#define S25FLXXXS_FLAG (S25FLXXXP_FLAG | \ + FLASH_FLAG_RESET | \ + FLASH_FLAG_DYB_LOCKING) + { "s25fl128s0", 0x012018, 0x0300, 256 * 1024, 64, S25FLXXXS_FLAG, 80, + stfsm_s25fl_config }, + { "s25fl128s1", 0x012018, 0x0301, 64 * 1024, 256, S25FLXXXS_FLAG, 80, + stfsm_s25fl_config }, + { "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, + S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config }, + { "s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512, + S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ +#define W25X_FLAG (FLASH_FLAG_READ_WRITE | \ + FLASH_FLAG_READ_FAST | \ + FLASH_FLAG_READ_1_1_2 | \ + FLASH_FLAG_WRITE_1_1_2) + { "w25x40", 0xef3013, 0, 64 * 1024, 8, W25X_FLAG, 75, NULL }, + { "w25x80", 0xef3014, 0, 64 * 1024, 16, W25X_FLAG, 75, NULL }, + { "w25x16", 0xef3015, 0, 64 * 1024, 32, W25X_FLAG, 75, NULL }, + { "w25x32", 0xef3016, 0, 64 * 1024, 64, W25X_FLAG, 75, NULL }, + { "w25x64", 0xef3017, 0, 64 * 1024, 128, W25X_FLAG, 75, NULL }, + + /* Winbond -- w25q "blocks" are 64K, "sectors" are 4KiB */ +#define W25Q_FLAG (FLASH_FLAG_READ_WRITE | \ + FLASH_FLAG_READ_FAST | \ + FLASH_FLAG_READ_1_1_2 | \ + FLASH_FLAG_READ_1_2_2 | \ + FLASH_FLAG_READ_1_1_4 | \ + FLASH_FLAG_READ_1_4_4 | \ + FLASH_FLAG_WRITE_1_1_4) + { "w25q80", 0xef4014, 0, 64 * 1024, 16, W25Q_FLAG, 80, + stfsm_w25q_config }, + { "w25q16", 0xef4015, 0, 64 * 1024, 32, W25Q_FLAG, 80, + stfsm_w25q_config }, + { "w25q32", 0xef4016, 0, 64 * 1024, 64, W25Q_FLAG, 80, + stfsm_w25q_config }, + { "w25q64", 0xef4017, 0, 64 * 1024, 128, W25Q_FLAG, 80, + stfsm_w25q_config }, + + /* Sentinel */ + { NULL, 0x000000, 0, 0, 0, 0, 0, NULL }, +}; + +/* + * FSM message sequence configurations: + * + * All configs are presented in order of preference + */ + +/* Default READ configurations, in order of preference */ +static struct seq_rw_config default_read_configs[] = { + {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 2, 4}, + {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 4, 0}, + {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 4, 0}, + {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, +}; + +/* Default WRITE configurations */ +static struct seq_rw_config default_write_configs[] = { + {FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0}, + {FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0}, + {FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0}, + {FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0}, + {FLASH_FLAG_READ_WRITE, FLASH_CMD_WRITE, 1, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [N25Qxxx] Configuration + */ +#define N25Q_VCR_DUMMY_CYCLES(x) (((x) & 0xf) << 4) +#define N25Q_VCR_XIP_DISABLED ((uint8_t)0x1 << 3) +#define N25Q_VCR_WRAP_CONT 0x3 + +/* N25Q 3-byte Address READ configurations + * - 'FAST' variants configured for 8 dummy cycles. + * + * Note, the number of dummy cycles used for 'FAST' READ operations is + * configurable and would normally be tuned according to the READ command and + * operating frequency. However, this applies universally to all 'FAST' READ + * commands, including those used by the SPIBoot controller, and remains in + * force until the device is power-cycled. Since the SPIBoot controller is + * hard-wired to use 8 dummy cycles, we must configure the device to also use 8 + * cycles. + */ +static struct seq_rw_config n25q_read3_configs[] = { + {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4, 0, 4, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2, 0, 2, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, FLASH_CMD_READ_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ, 0, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, +}; + +/* N25Q 4-byte Address READ configurations + * - use special 4-byte address READ commands (reduces overheads, and + * reduces risk of hitting watchdog reset issues). + * - 'FAST' variants configured for 8 dummy cycles (see note above.) + */ +static struct seq_rw_config n25q_read4_configs[] = { + {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [MX25xxx] Configuration + */ +#define MX25_STATUS_QE (0x1 << 6) + +static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) +{ + seq->seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) | + SEQ_OPC_CSDEASSERT); + + seq->seq[0] = STFSM_INST_CMD1; + seq->seq[1] = STFSM_INST_WAIT; + seq->seq[2] = STFSM_INST_STOP; + + seq->seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_ERASE | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ); + + return 0; +} + +/* + * [S25FLxxx] Configuration + */ +#define STFSM_S25FL_CONFIG_QE (0x1 << 1) + +/* + * S25FLxxxS devices provide three ways of supporting 32-bit addressing: Bank + * Register, Extended Address Modes, and a 32-bit address command set. The + * 32-bit address command set is used here, since it avoids any problems with + * entering a state that is incompatible with the SPIBoot Controller. + */ +static struct seq_rw_config stfsm_s25fl_read4_configs[] = { + {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4}, + {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, + {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0}, + {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, + {FLASH_FLAG_READ_FAST, FLASH_CMD_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, + {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4, 0, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, +}; + +static struct seq_rw_config stfsm_s25fl_write4_configs[] = { + {FLASH_FLAG_WRITE_1_1_4, S25FL_CMD_WRITE4_1_1_4, 1, 1, 4, 0x00, 0, 0}, + {FLASH_FLAG_READ_WRITE, S25FL_CMD_WRITE4, 1, 1, 1, 0x00, 0, 0}, + {0x00, 0, 0, 0, 0, 0x00, 0, 0}, +}; + +/* + * [W25Qxxx] Configuration + */ +#define W25Q_STATUS_QE (0x1 << 9) + +static struct stfsm_seq stfsm_seq_read_jedec = { + .data_size = TRANSFER_SIZE(8), + .seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_RDID)), + .seq = { + STFSM_INST_CMD1, + STFSM_INST_DATA_READ, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_read_status_fifo = { + .data_size = TRANSFER_SIZE(4), + .seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_RDSR)), + .seq = { + STFSM_INST_CMD1, + STFSM_INST_DATA_READ, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_erase_sector = { + /* 'addr_cfg' configured during initialisation */ + .seq_opc = { + (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), + + (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_SE)), + }, + .seq = { + STFSM_INST_CMD1, + STFSM_INST_CMD2, + STFSM_INST_ADD1, + STFSM_INST_ADD2, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_erase_chip = { + .seq_opc = { + (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), + + (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT), + }, + .seq = { + STFSM_INST_CMD1, + STFSM_INST_CMD2, + STFSM_INST_WAIT, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_ERASE | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_write_status = { + .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), + .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WRSR)), + .seq = { + STFSM_INST_CMD1, + STFSM_INST_CMD2, + STFSM_INST_STA_WR1, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + +static struct stfsm_seq stfsm_seq_wrvcr = { + .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), + .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)), + .seq = { + STFSM_INST_CMD1, + STFSM_INST_CMD2, + STFSM_INST_STA_WR1, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), +}; + +static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq) +{ + seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR)); + seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WREN) | + SEQ_OPC_CSDEASSERT); + + seq->seq[0] = STFSM_INST_CMD2; + seq->seq[1] = STFSM_INST_CMD1; + seq->seq[2] = STFSM_INST_WAIT; + seq->seq[3] = STFSM_INST_STOP; + + seq->seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_ERASE | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ); + + return 0; +} + +static inline int stfsm_is_idle(struct stfsm *fsm) +{ + return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10; +} + +static inline uint32_t stfsm_fifo_available(struct stfsm *fsm) +{ + return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f; +} + +static void stfsm_clear_fifo(struct stfsm *fsm) +{ + uint32_t avail; + + for (;;) { + avail = stfsm_fifo_available(fsm); + if (!avail) + break; + + while (avail) { + readl(fsm->base + SPI_FAST_SEQ_DATA_REG); + avail--; + } + } +} + +static inline void stfsm_load_seq(struct stfsm *fsm, + const struct stfsm_seq *seq) +{ + void __iomem *dst = fsm->base + SPI_FAST_SEQ_TRANSFER_SIZE; + const uint32_t *src = (const uint32_t *)seq; + int words = sizeof(*seq) / sizeof(*src); + + BUG_ON(!stfsm_is_idle(fsm)); + + while (words--) { + writel(*src, dst); + src++; + dst += 4; + } +} + +static void stfsm_wait_seq(struct stfsm *fsm) +{ + unsigned long deadline; + int timeout = 0; + + deadline = jiffies + msecs_to_jiffies(STFSM_MAX_WAIT_SEQ_MS); + + while (!timeout) { + if (time_after_eq(jiffies, deadline)) + timeout = 1; + + if (stfsm_is_idle(fsm)) + return; + + cond_resched(); + } + + dev_err(fsm->dev, "timeout on sequence completion\n"); +} + +static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) +{ + uint32_t remaining = size >> 2; + uint32_t avail; + uint32_t words; + + dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size); + + BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3)); + + while (remaining) { + for (;;) { + avail = stfsm_fifo_available(fsm); + if (avail) + break; + udelay(1); + } + words = min(avail, remaining); + remaining -= words; + + readsl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); + buf += words; + } +} + +static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, + uint32_t size) +{ + uint32_t words = size >> 2; + + dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size); + + BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3)); + + writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words); + + return size; +} + +static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) +{ + struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr; + uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR; + + seq->seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(cmd) | + SEQ_OPC_CSDEASSERT); + + stfsm_load_seq(fsm, seq); + + stfsm_wait_seq(fsm); + + return 0; +} + +static uint8_t stfsm_wait_busy(struct stfsm *fsm) +{ + struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; + unsigned long deadline; + uint32_t status; + int timeout = 0; + + /* Use RDRS1 */ + seq->seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_RDSR)); + + /* Load read_status sequence */ + stfsm_load_seq(fsm, seq); + + /* + * Repeat until busy bit is deasserted, or timeout, or error (S25FLxxxS) + */ + deadline = jiffies + FLASH_MAX_BUSY_WAIT; + while (!timeout) { + if (time_after_eq(jiffies, deadline)) + timeout = 1; + + stfsm_wait_seq(fsm); + + stfsm_read_fifo(fsm, &status, 4); + + if ((status & FLASH_STATUS_BUSY) == 0) + return 0; + + if ((fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) && + ((status & S25FL_STATUS_P_ERR) || + (status & S25FL_STATUS_E_ERR))) + return (uint8_t)(status & 0xff); + + if (!timeout) + /* Restart */ + writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG); + + cond_resched(); + } + + dev_err(fsm->dev, "timeout on wait_busy\n"); + + return FLASH_STATUS_TIMEOUT; +} + +static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd, + uint8_t *status) +{ + struct stfsm_seq *seq = &stfsm_seq_read_status_fifo; + uint32_t tmp; + + dev_dbg(fsm->dev, "reading STA[%s]\n", + (cmd == FLASH_CMD_RDSR) ? "1" : "2"); + + seq->seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(cmd)), + + stfsm_load_seq(fsm, seq); + + stfsm_read_fifo(fsm, &tmp, 4); + + *status = (uint8_t)(tmp >> 24); + + stfsm_wait_seq(fsm); + + return 0; +} + +static int stfsm_write_status(struct stfsm *fsm, uint16_t status, + int sta_bytes) +{ + struct stfsm_seq *seq = &stfsm_seq_write_status; + + dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n", + (sta_bytes == 1) ? "1" : "1+2", status); + + seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT; + seq->seq[2] = (sta_bytes == 1) ? + STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2; + + stfsm_load_seq(fsm, seq); + + stfsm_wait_seq(fsm); + + return 0; +}; + +static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data) +{ + struct stfsm_seq *seq = &stfsm_seq_wrvcr; + + dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data); + + seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT); + + stfsm_load_seq(fsm, seq); + + stfsm_wait_seq(fsm); + + return 0; +} + +/* + * SoC reset on 'boot-from-spi' systems + * + * Certain modes of operation cause the Flash device to enter a particular state + * for a period of time (e.g. 'Erase Sector', 'Quad Enable', and 'Enter 32-bit + * Addr' commands). On boot-from-spi systems, it is important to consider what + * happens if a warm reset occurs during this period. The SPIBoot controller + * assumes that Flash device is in its default reset state, 24-bit address mode, + * and ready to accept commands. This can be achieved using some form of + * on-board logic/controller to force a device POR in response to a SoC-level + * reset or by making use of the device reset signal if available (limited + * number of devices only). + * + * Failure to take such precautions can cause problems following a warm reset. + * For some operations (e.g. ERASE), there is little that can be done. For + * other modes of operation (e.g. 32-bit addressing), options are often + * available that can help minimise the window in which a reset could cause a + * problem. + * + */ +static bool stfsm_can_handle_soc_reset(struct stfsm *fsm) +{ + /* Reset signal is available on the board and supported by the device */ + if (fsm->reset_signal && fsm->info->flags & FLASH_FLAG_RESET) + return true; + + /* Board-level logic forces a power-on-reset */ + if (fsm->reset_por) + return true; + + /* Reset is not properly handled and may result in failure to reboot */ + return false; +} + +/* Configure 'addr_cfg' according to addressing mode */ +static void stfsm_prepare_erasesec_seq(struct stfsm *fsm, + struct stfsm_seq *seq) +{ + int addr1_cycles = fsm->info->flags & FLASH_FLAG_32BIT_ADDR ? 16 : 8; + + seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(addr1_cycles) | + ADR_CFG_PADS_1_ADD1 | + ADR_CFG_CYCLES_ADD2(16) | + ADR_CFG_PADS_1_ADD2 | + ADR_CFG_CSDEASSERT_ADD2); +} + +/* Search for preferred configuration based on available flags */ +static struct seq_rw_config * +stfsm_search_seq_rw_configs(struct stfsm *fsm, + struct seq_rw_config cfgs[]) +{ + struct seq_rw_config *config; + int flags = fsm->info->flags; + + for (config = cfgs; config->cmd != 0; config++) + if ((config->flags & flags) == config->flags) + return config; + + return NULL; +} + +/* Prepare a READ/WRITE sequence according to configuration parameters */ +static void stfsm_prepare_rw_seq(struct stfsm *fsm, + struct stfsm_seq *seq, + struct seq_rw_config *cfg) +{ + int addr1_cycles, addr2_cycles; + int i = 0; + + memset(seq, 0, sizeof(*seq)); + + /* Add READ/WRITE OPC */ + seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(cfg->cmd)); + + /* Add WREN OPC for a WRITE sequence */ + if (cfg->write) + seq->seq_opc[i++] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WREN) | + SEQ_OPC_CSDEASSERT); + + /* Address configuration (24 or 32-bit addresses) */ + addr1_cycles = (fsm->info->flags & FLASH_FLAG_32BIT_ADDR) ? 16 : 8; + addr1_cycles /= cfg->addr_pads; + addr2_cycles = 16 / cfg->addr_pads; + seq->addr_cfg = ((addr1_cycles & 0x3f) << 0 | /* ADD1 cycles */ + (cfg->addr_pads - 1) << 6 | /* ADD1 pads */ + (addr2_cycles & 0x3f) << 16 | /* ADD2 cycles */ + ((cfg->addr_pads - 1) << 22)); /* ADD2 pads */ + + /* Data/Sequence configuration */ + seq->seq_cfg = ((cfg->data_pads - 1) << 16 | + SEQ_CFG_STARTSEQ | + SEQ_CFG_CSDEASSERT); + if (!cfg->write) + seq->seq_cfg |= SEQ_CFG_READNOTWRITE; + + /* Mode configuration (no. of pads taken from addr cfg) */ + seq->mode = ((cfg->mode_data & 0xff) << 0 | /* data */ + (cfg->mode_cycles & 0x3f) << 16 | /* cycles */ + (cfg->addr_pads - 1) << 22); /* pads */ + + /* Dummy configuration (no. of pads taken from addr cfg) */ + seq->dummy = ((cfg->dummy_cycles & 0x3f) << 16 | /* cycles */ + (cfg->addr_pads - 1) << 22); /* pads */ + + + /* Instruction sequence */ + i = 0; + if (cfg->write) + seq->seq[i++] = STFSM_INST_CMD2; + + seq->seq[i++] = STFSM_INST_CMD1; + + seq->seq[i++] = STFSM_INST_ADD1; + seq->seq[i++] = STFSM_INST_ADD2; + + if (cfg->mode_cycles) + seq->seq[i++] = STFSM_INST_MODE; + + if (cfg->dummy_cycles) + seq->seq[i++] = STFSM_INST_DUMMY; + + seq->seq[i++] = + cfg->write ? STFSM_INST_DATA_WRITE : STFSM_INST_DATA_READ; + seq->seq[i++] = STFSM_INST_STOP; +} + +static int stfsm_search_prepare_rw_seq(struct stfsm *fsm, + struct stfsm_seq *seq, + struct seq_rw_config *cfgs) +{ + struct seq_rw_config *config; + + config = stfsm_search_seq_rw_configs(fsm, cfgs); + if (!config) { + dev_err(fsm->dev, "failed to find suitable config\n"); + return -EINVAL; + } + + stfsm_prepare_rw_seq(fsm, seq, config); + + return 0; +} + +/* Prepare a READ/WRITE/ERASE 'default' sequences */ +static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm) +{ + uint32_t flags = fsm->info->flags; + int ret; + + /* Configure 'READ' sequence */ + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, + default_read_configs); + if (ret) { + dev_err(fsm->dev, + "failed to prep READ sequence with flags [0x%08x]\n", + flags); + return ret; + } + + /* Configure 'WRITE' sequence */ + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, + default_write_configs); + if (ret) { + dev_err(fsm->dev, + "failed to prep WRITE sequence with flags [0x%08x]\n", + flags); + return ret; + } + + /* Configure 'ERASE_SECTOR' sequence */ + stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector); + + return 0; +} + +static int stfsm_mx25_config(struct stfsm *fsm) +{ + uint32_t flags = fsm->info->flags; + uint32_t data_pads; + uint8_t sta; + int ret; + bool soc_reset; + + /* + * Use default READ/WRITE sequences + */ + ret = stfsm_prepare_rwe_seqs_default(fsm); + if (ret) + return ret; + + /* + * Configure 32-bit Address Support + */ + if (flags & FLASH_FLAG_32BIT_ADDR) { + /* Configure 'enter_32bitaddr' FSM sequence */ + stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); + + soc_reset = stfsm_can_handle_soc_reset(fsm); + if (soc_reset || !fsm->booted_from_spi) { + /* If we can handle SoC resets, we enable 32-bit address + * mode pervasively */ + stfsm_enter_32bit_addr(fsm, 1); + + } else { + /* Else, enable/disable 32-bit addressing before/after + * each operation */ + fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR | + CFG_WRITE_TOGGLE_32BIT_ADDR | + CFG_ERASESEC_TOGGLE_32BIT_ADDR); + /* It seems a small delay is required after exiting + * 32-bit mode following a write operation. The issue + * is under investigation. + */ + fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY; + } + } + + /* For QUAD mode, set 'QE' STATUS bit */ + data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; + if (data_pads == 4) { + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta); + sta |= MX25_STATUS_QE; + stfsm_write_status(fsm, sta, 1); + } + + return 0; +} + +static int stfsm_n25q_config(struct stfsm *fsm) +{ + uint32_t flags = fsm->info->flags; + uint8_t vcr; + int ret = 0; + bool soc_reset; + + /* Configure 'READ' sequence */ + if (flags & FLASH_FLAG_32BIT_ADDR) + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, + n25q_read4_configs); + else + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, + n25q_read3_configs); + if (ret) { + dev_err(fsm->dev, + "failed to prepare READ sequence with flags [0x%08x]\n", + flags); + return ret; + } + + /* Configure 'WRITE' sequence (default configs) */ + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, + default_write_configs); + if (ret) { + dev_err(fsm->dev, + "preparing WRITE sequence using flags [0x%08x] failed\n", + flags); + return ret; + } + + /* * Configure 'ERASE_SECTOR' sequence */ + stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector); + + /* Configure 32-bit address support */ + if (flags & FLASH_FLAG_32BIT_ADDR) { + stfsm_n25q_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr); + + soc_reset = stfsm_can_handle_soc_reset(fsm); + if (soc_reset || !fsm->booted_from_spi) { + /* + * If we can handle SoC resets, we enable 32-bit + * address mode pervasively + */ + stfsm_enter_32bit_addr(fsm, 1); + } else { + /* + * If not, enable/disable for WRITE and ERASE + * operations (READ uses special commands) + */ + fsm->configuration = (CFG_WRITE_TOGGLE_32BIT_ADDR | + CFG_ERASESEC_TOGGLE_32BIT_ADDR); + } + } + + /* + * Configure device to use 8 dummy cycles + */ + vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED | + N25Q_VCR_WRAP_CONT); + stfsm_wrvcr(fsm, vcr); + + return 0; +} + +static void stfsm_s25fl_prepare_erasesec_seq_32(struct stfsm_seq *seq) +{ + seq->seq_opc[1] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(S25FL_CMD_SE4)); + + seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | + ADR_CFG_PADS_1_ADD1 | + ADR_CFG_CYCLES_ADD2(16) | + ADR_CFG_PADS_1_ADD2 | + ADR_CFG_CSDEASSERT_ADD2); +} + +static void stfsm_s25fl_read_dyb(struct stfsm *fsm, uint32_t offs, uint8_t *dby) +{ + uint32_t tmp; + struct stfsm_seq seq = { + .data_size = TRANSFER_SIZE(4), + .seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(S25FL_CMD_DYBRD)), + .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | + ADR_CFG_PADS_1_ADD1 | + ADR_CFG_CYCLES_ADD2(16) | + ADR_CFG_PADS_1_ADD2), + .addr1 = (offs >> 16) & 0xffff, + .addr2 = offs & 0xffff, + .seq = { + STFSM_INST_CMD1, + STFSM_INST_ADD1, + STFSM_INST_ADD2, + STFSM_INST_DATA_READ, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), + }; + + stfsm_load_seq(fsm, &seq); + + stfsm_read_fifo(fsm, &tmp, 4); + + *dby = (uint8_t)(tmp >> 24); + + stfsm_wait_seq(fsm); +} + +static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby) +{ + struct stfsm_seq seq = { + .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WREN) | + SEQ_OPC_CSDEASSERT), + .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)), + .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) | + ADR_CFG_PADS_1_ADD1 | + ADR_CFG_CYCLES_ADD2(16) | + ADR_CFG_PADS_1_ADD2), + .status = (uint32_t)dby | STA_PADS_1 | STA_CSDEASSERT, + .addr1 = (offs >> 16) & 0xffff, + .addr2 = offs & 0xffff, + .seq = { + STFSM_INST_CMD1, + STFSM_INST_CMD2, + STFSM_INST_ADD1, + STFSM_INST_ADD2, + STFSM_INST_STA_WR1, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), + }; + + stfsm_load_seq(fsm, &seq); + stfsm_wait_seq(fsm); + + stfsm_wait_busy(fsm); +} + +static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm) +{ + struct stfsm_seq seq = { + .seq_opc[0] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(S25FL_CMD_CLSR) | + SEQ_OPC_CSDEASSERT), + .seq_opc[1] = (SEQ_OPC_PADS_1 | + SEQ_OPC_CYCLES(8) | + SEQ_OPC_OPCODE(FLASH_CMD_WRDI) | + SEQ_OPC_CSDEASSERT), + .seq = { + STFSM_INST_CMD1, + STFSM_INST_CMD2, + STFSM_INST_WAIT, + STFSM_INST_STOP, + }, + .seq_cfg = (SEQ_CFG_PADS_1 | + SEQ_CFG_ERASE | + SEQ_CFG_READNOTWRITE | + SEQ_CFG_CSDEASSERT | + SEQ_CFG_STARTSEQ), + }; + + stfsm_load_seq(fsm, &seq); + + stfsm_wait_seq(fsm); + + return 0; +} + +static int stfsm_s25fl_config(struct stfsm *fsm) +{ + struct flash_info *info = fsm->info; + uint32_t flags = info->flags; + uint32_t data_pads; + uint32_t offs; + uint16_t sta_wr; + uint8_t sr1, cr1, dyb; + int ret; + + if (flags & FLASH_FLAG_32BIT_ADDR) { + /* + * Prepare Read/Write/Erase sequences according to S25FLxxx + * 32-bit address command set + */ + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, + stfsm_s25fl_read4_configs); + if (ret) + return ret; + + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, + stfsm_s25fl_write4_configs); + if (ret) + return ret; + + stfsm_s25fl_prepare_erasesec_seq_32(&stfsm_seq_erase_sector); + + } else { + /* Use default configurations for 24-bit addressing */ + ret = stfsm_prepare_rwe_seqs_default(fsm); + if (ret) + return ret; + } + + /* + * For devices that support 'DYB' sector locking, check lock status and + * unlock sectors if necessary (some variants power-on with sectors + * locked by default) + */ + if (flags & FLASH_FLAG_DYB_LOCKING) { + offs = 0; + for (offs = 0; offs < info->sector_size * info->n_sectors;) { + stfsm_s25fl_read_dyb(fsm, offs, &dyb); + if (dyb == 0x00) + stfsm_s25fl_write_dyb(fsm, offs, 0xff); + + /* Handle bottom/top 4KiB parameter sectors */ + if ((offs < info->sector_size * 2) || + (offs >= (info->sector_size - info->n_sectors * 4))) + offs += 0x1000; + else + offs += 0x10000; + } + } + + /* Check status of 'QE' bit */ + data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; + stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1); + if (data_pads == 4) { + if (!(cr1 & STFSM_S25FL_CONFIG_QE)) { + /* Set 'QE' */ + cr1 |= STFSM_S25FL_CONFIG_QE; + + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1); + sta_wr = ((uint16_t)cr1 << 8) | sr1; + + stfsm_write_status(fsm, sta_wr, 2); + + stfsm_wait_busy(fsm); + } + } else { + if ((cr1 & STFSM_S25FL_CONFIG_QE)) { + /* Clear 'QE' */ + cr1 &= ~STFSM_S25FL_CONFIG_QE; + + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1); + sta_wr = ((uint16_t)cr1 << 8) | sr1; + + stfsm_write_status(fsm, sta_wr, 2); + + stfsm_wait_busy(fsm); + } + + } + + /* + * S25FLxxx devices support Program and Error error flags. + * Configure driver to check flags and clear if necessary. + */ + fsm->configuration |= CFG_S25FL_CHECK_ERROR_FLAGS; + + return 0; +} + +static int stfsm_w25q_config(struct stfsm *fsm) +{ + uint32_t data_pads; + uint16_t sta_wr; + uint8_t sta1, sta2; + int ret; + + ret = stfsm_prepare_rwe_seqs_default(fsm); + if (ret) + return ret; + + /* If using QUAD mode, set QE STATUS bit */ + data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; + if (data_pads == 4) { + stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1); + stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2); + + sta_wr = ((uint16_t)sta2 << 8) | sta1; + + sta_wr |= W25Q_STATUS_QE; + + stfsm_write_status(fsm, sta_wr, 2); + + stfsm_wait_busy(fsm); + } + + return 0; +} + +static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, + uint32_t offset) +{ + struct stfsm_seq *seq = &fsm->stfsm_seq_read; + uint32_t data_pads; + uint32_t read_mask; + uint32_t size_ub; + uint32_t size_lb; + uint32_t size_mop; + uint32_t tmp[4]; + uint32_t page_buf[FLASH_PAGESIZE_32]; + uint8_t *p; + + dev_dbg(fsm->dev, "reading %d bytes from 0x%08x\n", size, offset); + + /* Enter 32-bit address mode, if required */ + if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR) + stfsm_enter_32bit_addr(fsm, 1); + + /* Must read in multiples of 32 cycles (or 32*pads/8 Bytes) */ + data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1; + read_mask = (data_pads << 2) - 1; + + /* Handle non-aligned buf */ + p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf; + + /* Handle non-aligned size */ + size_ub = (size + read_mask) & ~read_mask; + size_lb = size & ~read_mask; + size_mop = size & read_mask; + + seq->data_size = TRANSFER_SIZE(size_ub); + seq->addr1 = (offset >> 16) & 0xffff; + seq->addr2 = offset & 0xffff; + + stfsm_load_seq(fsm, seq); + + if (size_lb) + stfsm_read_fifo(fsm, (uint32_t *)p, size_lb); + + if (size_mop) { + stfsm_read_fifo(fsm, tmp, read_mask + 1); + memcpy(p + size_lb, &tmp, size_mop); + } + + /* Handle non-aligned buf */ + if ((uint32_t)buf & 0x3) + memcpy(buf, page_buf, size); + + /* Wait for sequence to finish */ + stfsm_wait_seq(fsm); + + stfsm_clear_fifo(fsm); + + /* Exit 32-bit address mode, if required */ + if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR) + stfsm_enter_32bit_addr(fsm, 0); + + return 0; +} + +static int stfsm_write(struct stfsm *fsm, const uint8_t *buf, + uint32_t size, uint32_t offset) +{ + struct stfsm_seq *seq = &fsm->stfsm_seq_write; + uint32_t data_pads; + uint32_t write_mask; + uint32_t size_ub; + uint32_t size_lb; + uint32_t size_mop; + uint32_t tmp[4]; + uint32_t page_buf[FLASH_PAGESIZE_32]; + uint8_t *t = (uint8_t *)&tmp; + const uint8_t *p; + int ret; + int i; + + dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset); + + /* Enter 32-bit address mode, if required */ + if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) + stfsm_enter_32bit_addr(fsm, 1); + + /* Must write in multiples of 32 cycles (or 32*pads/8 bytes) */ + data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1; + write_mask = (data_pads << 2) - 1; + + /* Handle non-aligned buf */ + if ((uint32_t)buf & 0x3) { + memcpy(page_buf, buf, size); + p = (uint8_t *)page_buf; + } else { + p = buf; + } + + /* Handle non-aligned size */ + size_ub = (size + write_mask) & ~write_mask; + size_lb = size & ~write_mask; + size_mop = size & write_mask; + + seq->data_size = TRANSFER_SIZE(size_ub); + seq->addr1 = (offset >> 16) & 0xffff; + seq->addr2 = offset & 0xffff; + + /* Need to set FIFO to write mode, before writing data to FIFO (see + * GNBvb79594) + */ + writel(0x00040000, fsm->base + SPI_FAST_SEQ_CFG); + + /* + * Before writing data to the FIFO, apply a small delay to allow a + * potential change of FIFO direction to complete. + */ + if (fsm->fifo_dir_delay == 0) + readl(fsm->base + SPI_FAST_SEQ_CFG); + else + udelay(fsm->fifo_dir_delay); + + + /* Write data to FIFO, before starting sequence (see GNBvd79593) */ + if (size_lb) { + stfsm_write_fifo(fsm, (uint32_t *)p, size_lb); + p += size_lb; + } + + /* Handle non-aligned size */ + if (size_mop) { + memset(t, 0xff, write_mask + 1); /* fill with 0xff's */ + for (i = 0; i < size_mop; i++) + t[i] = *p++; + + stfsm_write_fifo(fsm, tmp, write_mask + 1); + } + + /* Start sequence */ + stfsm_load_seq(fsm, seq); + + /* Wait for sequence to finish */ + stfsm_wait_seq(fsm); + + /* Wait for completion */ + ret = stfsm_wait_busy(fsm); + if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) + stfsm_s25fl_clear_status_reg(fsm); + + /* Exit 32-bit address mode, if required */ + if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) { + stfsm_enter_32bit_addr(fsm, 0); + if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY) + udelay(1); + } + + return 0; +} + +/* + * Read an address range from the flash chip. The address range + * may be any size provided it is within the physical boundaries. + */ +static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); + uint32_t bytes; + + dev_dbg(fsm->dev, "%s from 0x%08x, len %zd\n", + __func__, (u32)from, len); + + mutex_lock(&fsm->lock); + + while (len > 0) { + bytes = min_t(size_t, len, FLASH_PAGESIZE); + + stfsm_read(fsm, buf, bytes, from); + + buf += bytes; + from += bytes; + len -= bytes; + + *retlen += bytes; + } + + mutex_unlock(&fsm->lock); + + return 0; +} + +static int stfsm_erase_sector(struct stfsm *fsm, uint32_t offset) +{ + struct stfsm_seq *seq = &stfsm_seq_erase_sector; + int ret; + + dev_dbg(fsm->dev, "erasing sector at 0x%08x\n", offset); + + /* Enter 32-bit address mode, if required */ + if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR) + stfsm_enter_32bit_addr(fsm, 1); + + seq->addr1 = (offset >> 16) & 0xffff; + seq->addr2 = offset & 0xffff; + + stfsm_load_seq(fsm, seq); + + stfsm_wait_seq(fsm); + + /* Wait for completion */ + ret = stfsm_wait_busy(fsm); + if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) + stfsm_s25fl_clear_status_reg(fsm); + + /* Exit 32-bit address mode, if required */ + if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR) + stfsm_enter_32bit_addr(fsm, 0); + + return ret; +} + +static int stfsm_erase_chip(struct stfsm *fsm) +{ + const struct stfsm_seq *seq = &stfsm_seq_erase_chip; + + dev_dbg(fsm->dev, "erasing chip\n"); + + stfsm_load_seq(fsm, seq); + + stfsm_wait_seq(fsm); + + return stfsm_wait_busy(fsm); +} + +/* + * Write an address range to the flash chip. Data must be written in + * FLASH_PAGESIZE chunks. The address range may be any size provided + * it is within the physical boundaries. + */ +static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); + + u32 page_offs; + u32 bytes; + uint8_t *b = (uint8_t *)buf; + int ret = 0; + + dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len); + + /* Offset within page */ + page_offs = to % FLASH_PAGESIZE; + + mutex_lock(&fsm->lock); + + while (len) { + /* Write up to page boundary */ + bytes = min(FLASH_PAGESIZE - page_offs, len); + + ret = stfsm_write(fsm, b, bytes, to); + if (ret) + goto out1; + + b += bytes; + len -= bytes; + to += bytes; + + /* We are now page-aligned */ + page_offs = 0; + + *retlen += bytes; + + } + +out1: + mutex_unlock(&fsm->lock); + + return ret; +} + +/* + * Erase an address range on the flash chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent); + u32 addr, len; + int ret; + + dev_dbg(fsm->dev, "%s at 0x%llx, len %lld\n", __func__, + (long long)instr->addr, (long long)instr->len); + + addr = instr->addr; + len = instr->len; + + mutex_lock(&fsm->lock); + + /* Whole-chip erase? */ + if (len == mtd->size) { + ret = stfsm_erase_chip(fsm); + if (ret) + goto out1; + } else { + while (len) { + ret = stfsm_erase_sector(fsm, addr); + if (ret) + goto out1; + + addr += mtd->erasesize; + len -= mtd->erasesize; + } + } + + mutex_unlock(&fsm->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; + +out1: + instr->state = MTD_ERASE_FAILED; + mutex_unlock(&fsm->lock); + + return ret; +} + +static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *jedec) +{ + const struct stfsm_seq *seq = &stfsm_seq_read_jedec; + uint32_t tmp[2]; + + stfsm_load_seq(fsm, seq); + + stfsm_read_fifo(fsm, tmp, 8); + + memcpy(jedec, tmp, 5); + + stfsm_wait_seq(fsm); +} + +static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm) +{ + struct flash_info *info; + u16 ext_jedec; + u32 jedec; + u8 id[5]; + + stfsm_read_jedec(fsm, id); + + jedec = id[0] << 16 | id[1] << 8 | id[2]; + /* + * JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + ext_jedec = id[3] << 8 | id[4]; + + dev_dbg(fsm->dev, "JEDEC = 0x%08x [%02x %02x %02x %02x %02x]\n", + jedec, id[0], id[1], id[2], id[3], id[4]); + + for (info = flash_types; info->name; info++) { + if (info->jedec_id == jedec) { + if (info->ext_id && info->ext_id != ext_jedec) + continue; + return info; + } + } + dev_err(fsm->dev, "Unrecognized JEDEC id %06x\n", jedec); + + return NULL; +} + +static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode) +{ + int ret, timeout = 10; + + /* Wait for controller to accept mode change */ + while (--timeout) { + ret = readl(fsm->base + SPI_STA_MODE_CHANGE); + if (ret & 0x1) + break; + udelay(1); + } + + if (!timeout) + return -EBUSY; + + writel(mode, fsm->base + SPI_MODESELECT); + + return 0; +} + +static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq) +{ + uint32_t emi_freq; + uint32_t clk_div; + + /* TODO: Make this dynamic */ + emi_freq = STFSM_DEFAULT_EMI_FREQ; + + /* + * Calculate clk_div - values between 2 and 128 + * Multiple of 2, rounded up + */ + clk_div = 2 * DIV_ROUND_UP(emi_freq, 2 * spi_freq); + if (clk_div < 2) + clk_div = 2; + else if (clk_div > 128) + clk_div = 128; + + /* + * Determine a suitable delay for the IP to complete a change of + * direction of the FIFO. The required delay is related to the clock + * divider used. The following heuristics are based on empirical tests, + * using a 100MHz EMI clock. + */ + if (clk_div <= 4) + fsm->fifo_dir_delay = 0; + else if (clk_div <= 10) + fsm->fifo_dir_delay = 1; + else + fsm->fifo_dir_delay = DIV_ROUND_UP(clk_div, 10); + + dev_dbg(fsm->dev, "emi_clk = %uHZ, spi_freq = %uHZ, clk_div = %u\n", + emi_freq, spi_freq, clk_div); + + writel(clk_div, fsm->base + SPI_CLOCKDIV); +} + +static int stfsm_init(struct stfsm *fsm) +{ + int ret; + + /* Perform a soft reset of the FSM controller */ + writel(SEQ_CFG_SWRESET, fsm->base + SPI_FAST_SEQ_CFG); + udelay(1); + writel(0, fsm->base + SPI_FAST_SEQ_CFG); + + /* Set clock to 'safe' frequency initially */ + stfsm_set_freq(fsm, STFSM_FLASH_SAFE_FREQ); + + /* Switch to FSM */ + ret = stfsm_set_mode(fsm, SPI_MODESELECT_FSM); + if (ret) + return ret; + + /* Set timing parameters */ + writel(SPI_CFG_DEVICE_ST | + SPI_CFG_DEFAULT_MIN_CS_HIGH | + SPI_CFG_DEFAULT_CS_SETUPHOLD | + SPI_CFG_DEFAULT_DATA_HOLD, + fsm->base + SPI_CONFIGDATA); + writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG); + + /* Clear FIFO, just in case */ + stfsm_clear_fifo(fsm); + + return 0; +} + +static void stfsm_fetch_platform_configs(struct platform_device *pdev) +{ + struct stfsm *fsm = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; + struct regmap *regmap; + uint32_t boot_device_reg; + uint32_t boot_device_spi; + uint32_t boot_device; /* Value we read from *boot_device_reg */ + int ret; + + /* Booting from SPI NOR Flash is the default */ + fsm->booted_from_spi = true; + + regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(regmap)) + goto boot_device_fail; + + fsm->reset_signal = of_property_read_bool(np, "st,reset-signal"); + + fsm->reset_por = of_property_read_bool(np, "st,reset-por"); + + /* Where in the syscon the boot device information lives */ + ret = of_property_read_u32(np, "st,boot-device-reg", &boot_device_reg); + if (ret) + goto boot_device_fail; + + /* Boot device value when booted from SPI NOR */ + ret = of_property_read_u32(np, "st,boot-device-spi", &boot_device_spi); + if (ret) + goto boot_device_fail; + + ret = regmap_read(regmap, boot_device_reg, &boot_device); + if (ret) + goto boot_device_fail; + + if (boot_device != boot_device_spi) + fsm->booted_from_spi = false; + + return; + +boot_device_fail: + dev_warn(&pdev->dev, + "failed to fetch boot device, assuming boot from SPI\n"); +} + +static int stfsm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mtd_part_parser_data ppdata; + struct flash_info *info; + struct resource *res; + struct stfsm *fsm; + int ret; + + if (!np) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + ppdata.of_node = np; + + fsm = devm_kzalloc(&pdev->dev, sizeof(*fsm), GFP_KERNEL); + if (!fsm) + return -ENOMEM; + + fsm->dev = &pdev->dev; + + platform_set_drvdata(pdev, fsm); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Resource not found\n"); + return -ENODEV; + } + + fsm->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsm->base)) { + dev_err(&pdev->dev, + "Failed to reserve memory region %pR\n", res); + return PTR_ERR(fsm->base); + } + + mutex_init(&fsm->lock); + + ret = stfsm_init(fsm); + if (ret) { + dev_err(&pdev->dev, "Failed to initialise FSM Controller\n"); + return ret; + } + + stfsm_fetch_platform_configs(pdev); + + /* Detect SPI FLASH device */ + info = stfsm_jedec_probe(fsm); + if (!info) + return -ENODEV; + fsm->info = info; + + /* Use device size to determine address width */ + if (info->sector_size * info->n_sectors > 0x1000000) + info->flags |= FLASH_FLAG_32BIT_ADDR; + + /* + * Configure READ/WRITE/ERASE sequences according to platform and + * device flags. + */ + if (info->config) { + ret = info->config(fsm); + if (ret) + return ret; + } else { + ret = stfsm_prepare_rwe_seqs_default(fsm); + if (ret) + return ret; + } + + fsm->mtd.name = info->name; + fsm->mtd.dev.parent = &pdev->dev; + fsm->mtd.type = MTD_NORFLASH; + fsm->mtd.writesize = 4; + fsm->mtd.writebufsize = fsm->mtd.writesize; + fsm->mtd.flags = MTD_CAP_NORFLASH; + fsm->mtd.size = info->sector_size * info->n_sectors; + fsm->mtd.erasesize = info->sector_size; + + fsm->mtd._read = stfsm_mtd_read; + fsm->mtd._write = stfsm_mtd_write; + fsm->mtd._erase = stfsm_mtd_erase; + + dev_info(&pdev->dev, + "Found serial flash device: %s\n" + " size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n", + info->name, + (long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20), + fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10)); + + return mtd_device_parse_register(&fsm->mtd, NULL, &ppdata, NULL, 0); +} + +static int stfsm_remove(struct platform_device *pdev) +{ + struct stfsm *fsm = platform_get_drvdata(pdev); + + return mtd_device_unregister(&fsm->mtd); +} + +static struct of_device_id stfsm_match[] = { + { .compatible = "st,spi-fsm", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stfsm_match); + +static struct platform_driver stfsm_driver = { + .probe = stfsm_probe, + .remove = stfsm_remove, + .driver = { + .name = "st-spi-fsm", + .owner = THIS_MODULE, + .of_match_table = stfsm_match, + }, +}; +module_platform_driver(stfsm_driver); + +MODULE_AUTHOR("Angus Clark <angus.clark@st.com>"); +MODULE_DESCRIPTION("ST SPI FSM driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 4adc0374fb6b..487e64f411a5 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -30,7 +30,6 @@ #include <asm/uaccess.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nftl.h> #include <linux/mtd/inftl.h> diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index d38b6460d505..018c75faadb3 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -55,10 +55,8 @@ struct mtd_info *lpddr_cmdset(struct map_info *map) int i, j; mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) { - printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + if (!mtd) return NULL; - } mtd->priv = map; mtd->type = MTD_NORFLASH; diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c index 45abed67f1ef..69f2112340b1 100644 --- a/drivers/mtd/lpddr/qinfo_probe.c +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -135,11 +135,8 @@ static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr) { lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL); - if (!lpddr->qinfo) { - printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n", - map->name); + if (!lpddr->qinfo) return 0; - } /* Get the ManuID */ lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID)); diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 310dc7c93425..fce23fe043f7 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -66,11 +66,11 @@ config MTD_PHYSMAP_BANKWIDTH used internally by the CFI drivers. config MTD_PHYSMAP_OF - tristate "Flash device in physical memory map based on OF description" - depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM) + tristate "Memory device in physical memory map based on OF description" + depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM) help - This provides a 'mapping' driver which allows the NOR Flash and - ROM driver code to communicate with chips which are mapped + This provides a 'mapping' driver which allows the NOR Flash, ROM + and RAM driver code to communicate with chips which are mapped physically into the CPU's memory. The mapping description here is taken from OF device tree. @@ -124,7 +124,7 @@ config MTD_NETSC520 config MTD_TS5500 tristate "JEDEC Flash device mapped on Technologic Systems TS-5500" - depends on X86 + depends on TS5500 || COMPILE_TEST select MTD_JEDECPROBE select MTD_CFI_AMDSTD help diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 5434d8ded015..6ea51e549045 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -14,7 +14,6 @@ * Licensed under the GPL-2 or later. */ -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c index 1adba86474a5..a4c477b9fdd6 100644 --- a/drivers/mtd/maps/gpio-addr-flash.c +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -14,7 +14,6 @@ */ #include <linux/gpio.h> -#include <linux/init.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index 46d195fca942..5ab71f0e1bcd 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -31,7 +31,6 @@ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index d6b2451eab1d..6a589f1e2880 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -16,7 +16,6 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/slab.h> diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c index 93c507a6f862..7aa682cd4d7e 100644 --- a/drivers/mtd/maps/lantiq-flash.c +++ b/drivers/mtd/maps/lantiq-flash.c @@ -13,7 +13,6 @@ #include <linux/kernel.h> #include <linux/io.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c index 98bb5d5375d7..cadfbe051873 100644 --- a/drivers/mtd/maps/latch-addr-flash.c +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -10,7 +10,6 @@ * kind, whether express or implied. */ -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 36da518915b5..eb0242e0b2d9 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/pci.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index d11109762ac5..217c25d7381b 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -15,7 +15,6 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 10196f5a897d..d597e89f2692 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -23,7 +23,6 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> @@ -138,7 +137,6 @@ static int platram_probe(struct platform_device *pdev) info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { - dev_err(&pdev->dev, "no memory for flash info\n"); err = -ENOMEM; goto exit_error; } diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 9aad854fe912..cb4d92eea9fe 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -13,7 +13,6 @@ #include <linux/types.h> #include <linux/slab.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index 93525121d69d..146b6047ed2b 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -13,7 +13,6 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/device.h> #include <linux/platform_device.h> diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 3051c4c36240..b7a22a612a46 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -47,7 +47,6 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> -#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 39cc4181f025..b6f1aac3510c 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h> -#include <linux/init.h> #include <linux/ioport.h> #include <linux/of.h> #include <linux/of_device.h> diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 5073cbc796d8..0b2ccb68c0d0 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -30,7 +30,6 @@ #include <linux/blkpg.h> #include <linux/spinlock.h> #include <linux/hdreg.h> -#include <linux/init.h> #include <linux/mutex.h> #include <asm/uaccess.h> diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 2147e733533b..7d4e7b9da3a1 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -324,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c default: ret = mtd_write(mtd, *ppos, len, &retlen, kbuf); } + + /* + * Return -ENOSPC only if no data could be written at all. + * Otherwise just return the number of bytes that actually + * have been written. + */ + if ((ret == -ENOSPC) && (total_retlen)) + break; + if (!ret) { *ppos += retlen; total_retlen += retlen; @@ -889,25 +898,26 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) case OTPGETREGIONINFO: { struct otp_info *buf = kmalloc(4096, GFP_KERNEL); + size_t retlen; if (!buf) return -ENOMEM; switch (mfi->mode) { case MTD_FILE_MODE_OTP_FACTORY: - ret = mtd_get_fact_prot_info(mtd, buf, 4096); + ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf); break; case MTD_FILE_MODE_OTP_USER: - ret = mtd_get_user_prot_info(mtd, buf, 4096); + ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf); break; default: ret = -EINVAL; break; } - if (ret >= 0) { + if (!ret) { if (cmd == OTPGETREGIONCOUNT) { - int nbr = ret / sizeof(struct otp_info); + int nbr = retlen / sizeof(struct otp_info); ret = copy_to_user(argp, &nbr, sizeof(int)); } else - ret = copy_to_user(argp, buf, ret); + ret = copy_to_user(argp, buf, retlen); if (ret) ret = -EFAULT; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 34c0b16aed5c..d201feeb3ca6 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -883,14 +883,14 @@ EXPORT_SYMBOL_GPL(mtd_read_oob); * devices. The user data is one time programmable but the factory data is read * only. */ -int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len) +int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, + struct otp_info *buf) { if (!mtd->_get_fact_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_fact_prot_info(mtd, buf, len); + return mtd->_get_fact_prot_info(mtd, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info); @@ -906,14 +906,14 @@ int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, } EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg); -int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len) +int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, + struct otp_info *buf) { if (!mtd->_get_user_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_user_prot_info(mtd, buf, len); + return mtd->_get_user_prot_info(mtd, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_get_user_prot_info); @@ -932,12 +932,22 @@ EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg); int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, u_char *buf) { + int ret; + *retlen = 0; if (!mtd->_write_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); + ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); + if (ret) + return ret; + + /* + * If no data could be written at all, we are out of memory and + * must return -ENOSPC. + */ + return (*retlen) ? 0 : -ENOSPC; } EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 3c7d6d7623c1..1ca9aec141ff 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -150,11 +150,12 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, retlen, buf); } -static int part_get_user_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { struct mtd_part *part = PART(mtd); - return part->master->_get_user_prot_info(part->master, buf, len); + return part->master->_get_user_prot_info(part->master, len, retlen, + buf); } static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, @@ -165,11 +166,12 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, retlen, buf); } -static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, - size_t len) +static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { struct mtd_part *part = PART(mtd); - return part->master->_get_fact_prot_info(part->master, buf, len); + return part->master->_get_fact_prot_info(part->master, len, retlen, + buf); } static int part_write(struct mtd_info *mtd, loff_t to, size_t len, diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index a4bee41ad5cb..f1cf503517fd 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -460,6 +460,8 @@ config MTD_NAND_MXC config MTD_NAND_SH_FLCTL tristate "Support for NAND on Renesas SuperH FLCTL" depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST + depends on HAS_IOMEM + depends on HAS_DMA help Several Renesas SuperH CPU has FLCTL. This option enables support for NAND Flash using FLCTL. diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index 8611eb4b45fc..4936e9e0002f 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -17,7 +17,6 @@ */ #include <linux/slab.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c36e9b84487c..4ce181a35bcd 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -430,7 +430,7 @@ err_dma: dma_unmap_single(dma_dev->dev, phys_addr, len, dir); err_buf: if (err != 0) - dev_warn(host->dev, "Fall back to CPU I/O\n"); + dev_dbg(host->dev, "Fall back to CPU I/O\n"); return err; } @@ -1220,6 +1220,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev, goto err; } + nand_chip->options |= NAND_NO_SUBPAGE_WRITE; nand_chip->ecc.read_page = atmel_nand_pmecc_read_page; nand_chip->ecc.write_page = atmel_nand_pmecc_write_page; @@ -1659,8 +1660,8 @@ static void nfc_select_chip(struct mtd_info *mtd, int chip) nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE); } -static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr, - unsigned int *addr1234, unsigned int *cycle0) +static int nfc_make_addr(struct mtd_info *mtd, int command, int column, + int page_addr, unsigned int *addr1234, unsigned int *cycle0) { struct nand_chip *chip = mtd->priv; @@ -1674,7 +1675,8 @@ static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr, *addr1234 = 0; if (column != -1) { - if (chip->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; addr_bytes[acycle++] = column & 0xff; if (mtd->writesize > 512) @@ -1787,8 +1789,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, } if (do_addr) - acycle = nfc_make_addr(mtd, column, page_addr, &addr1234, - &cycle0); + acycle = nfc_make_addr(mtd, command, column, page_addr, + &addr1234, &cycle0); nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr; nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 2880d888cfc5..bc5c518828d2 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -11,7 +11,6 @@ #include <linux/slab.h> #include <linux/gpio.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/mtd/mtd.h> @@ -308,7 +307,8 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) + if (this->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; ctx->write_byte(mtd, column); } diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 94f55dbde995..b7a24946ca26 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -37,7 +37,6 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index f2f64addb5e8..4e66726da9aa 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -627,6 +627,8 @@ static int cafe_nand_probe(struct pci_dev *pdev, struct cafe_priv *cafe; uint32_t ctrl; int err = 0; + int old_dma; + struct nand_buffers *nbuf; /* Very old versions shared the same PCI ident for all three functions on the chip. Verify the class too... */ @@ -655,13 +657,6 @@ static int cafe_nand_probe(struct pci_dev *pdev, err = -ENOMEM; goto out_free_mtd; } - cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers), - &cafe->dmaaddr, GFP_KERNEL); - if (!cafe->dmabuf) { - err = -ENOMEM; - goto out_ior; - } - cafe->nand.buffers = (void *)cafe->dmabuf + 2112; cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8); if (!cafe->rs) { @@ -721,7 +716,7 @@ static int cafe_nand_probe(struct pci_dev *pdev, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - goto out_free_dma; + goto out_ior; } /* Disable master reset, enable NAND clock */ @@ -735,6 +730,32 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe_writel(cafe, 0x7006, GLOBAL_CTRL); cafe_writel(cafe, 0x700a, GLOBAL_CTRL); + /* Enable NAND IRQ in global IRQ mask register */ + cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); + cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", + cafe_readl(cafe, GLOBAL_CTRL), + cafe_readl(cafe, GLOBAL_IRQ_MASK)); + + /* Do not use the DMA for the nand_scan_ident() */ + old_dma = usedma; + usedma = 0; + + /* Scan to find existence of the device */ + if (nand_scan_ident(mtd, 2, NULL)) { + err = -ENXIO; + goto out_irq; + } + + cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, + 2112 + sizeof(struct nand_buffers) + + mtd->writesize + mtd->oobsize, + &cafe->dmaaddr, GFP_KERNEL); + if (!cafe->dmabuf) { + err = -ENOMEM; + goto out_irq; + } + cafe->nand.buffers = nbuf = (void *)cafe->dmabuf + 2112; + /* Set up DMA address */ cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); if (sizeof(cafe->dmaaddr) > 4) @@ -746,16 +767,13 @@ static int cafe_nand_probe(struct pci_dev *pdev, cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n", cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf); - /* Enable NAND IRQ in global IRQ mask register */ - cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); - cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", - cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); + /* this driver does not need the @ecccalc and @ecccode */ + nbuf->ecccalc = NULL; + nbuf->ecccode = NULL; + nbuf->databuf = (uint8_t *)(nbuf + 1); - /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 2, NULL)) { - err = -ENXIO; - goto out_irq; - } + /* Restore the DMA flag */ + usedma = old_dma; cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */ if (mtd->writesize == 2048) @@ -773,7 +791,7 @@ static int cafe_nand_probe(struct pci_dev *pdev, } else { printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n", mtd->writesize); - goto out_irq; + goto out_free_dma; } cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME; cafe->nand.ecc.size = mtd->writesize; @@ -790,7 +808,7 @@ static int cafe_nand_probe(struct pci_dev *pdev, err = nand_scan_tail(mtd); if (err) - goto out_irq; + goto out_free_dma; pci_set_drvdata(pdev, mtd); @@ -799,12 +817,15 @@ static int cafe_nand_probe(struct pci_dev *pdev, goto out; + out_free_dma: + dma_free_coherent(&cafe->pdev->dev, + 2112 + sizeof(struct nand_buffers) + + mtd->writesize + mtd->oobsize, + cafe->dmabuf, cafe->dmaaddr); out_irq: /* Disable NAND IRQ in global IRQ mask register */ cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK); free_irq(pdev->irq, mtd); - out_free_dma: - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); out_ior: pci_iounmap(pdev, cafe->mmio); out_free_mtd: @@ -824,7 +845,10 @@ static void cafe_nand_remove(struct pci_dev *pdev) nand_release(mtd); free_rs(cafe->rs); pci_iounmap(pdev, cafe->mmio); - dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr); + dma_free_coherent(&cafe->pdev->dev, + 2112 + sizeof(struct nand_buffers) + + mtd->writesize + mtd->oobsize, + cafe->dmabuf, cafe->dmaaddr); kfree(mtd); } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 8eb6a36f125a..4615d79fc93f 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -24,7 +24,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/err.h> diff --git a/drivers/mtd/nand/denali_dt.c b/drivers/mtd/nand/denali_dt.c index babb02c4b220..35cb17f57800 100644 --- a/drivers/mtd/nand/denali_dt.c +++ b/drivers/mtd/nand/denali_dt.c @@ -30,24 +30,6 @@ struct denali_dt { struct clk *clk; }; -static void __iomem *request_and_map(struct device *dev, - const struct resource *res) -{ - void __iomem *ptr; - - if (!devm_request_mem_region(dev, res->start, resource_size(res), - "denali-dt")) { - dev_err(dev, "unable to request %s\n", res->name); - return NULL; - } - - ptr = devm_ioremap_nocache(dev, res->start, resource_size(res)); - if (!ptr) - dev_err(dev, "ioremap_nocache of %s failed!", res->name); - - return ptr; -} - static const struct of_device_id denali_nand_dt_ids[] = { { .compatible = "denali,denali-nand-dt" }, { /* sentinel */ } @@ -78,13 +60,6 @@ static int denali_dt_probe(struct platform_device *ofdev) return -ENOMEM; denali = &dt->denali; - denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg"); - nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data"); - if (!denali_reg || !nand_data) { - dev_err(&ofdev->dev, "resources not completely defined\n"); - return -EINVAL; - } - denali->platform = DT; denali->dev = &ofdev->dev; denali->irq = platform_get_irq(ofdev, 0); @@ -93,13 +68,15 @@ static int denali_dt_probe(struct platform_device *ofdev) return denali->irq; } - denali->flash_reg = request_and_map(&ofdev->dev, denali_reg); - if (!denali->flash_reg) - return -ENOMEM; + denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg"); + denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg); + if (IS_ERR(denali->flash_reg)) + return PTR_ERR(denali->flash_reg); - denali->flash_mem = request_and_map(&ofdev->dev, nand_data); - if (!denali->flash_mem) - return -ENOMEM; + nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data"); + denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data); + if (IS_ERR(denali->flash_mem)) + return PTR_ERR(denali->flash_mem); if (!of_property_read_u32(ofdev->dev.of_node, "dma-mask", (u32 *)&denali_dma_mask)) { diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index fec31d71b84e..f68a7bccecdc 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -698,7 +698,8 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) + if (this->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; WriteDOC(column, docptr, Mplus_FlashAddress); } @@ -1438,7 +1439,7 @@ static int __init doc_probe(unsigned long physadr) int reg, len, numchips; int ret = 0; - if (!request_mem_region(physadr, DOC_IOREMAP_LEN, NULL)) + if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip")) return -EBUSY; virtadr = ioremap(physadr, DOC_IOREMAP_LEN); if (!virtadr) { diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index bcf60800c3ce..ec549cd9849f 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -24,7 +24,6 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 50d9161c4faf..cb45d2f8e208 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -22,7 +22,6 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/of_address.h> #include <linux/slab.h> diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 8e6148aa4539..117ce333fdd4 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -18,7 +18,6 @@ #include <linux/kernel.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index ca6369fe91ff..bb77f750e75a 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -27,6 +27,7 @@ #include <linux/of_device.h> #include <linux/of_mtd.h> #include "gpmi-nand.h" +#include "bch-regs.h" /* Resource names for the GPMI NAND driver. */ #define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME "gpmi-nand" @@ -985,7 +986,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, int ret; dev_dbg(this->dev, "page number is : %d\n", page); - ret = read_page_prepare(this, buf, mtd->writesize, + ret = read_page_prepare(this, buf, nfc_geo->payload_size, this->payload_virt, this->payload_phys, nfc_geo->payload_size, &payload_virt, &payload_phys); @@ -999,7 +1000,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, /* go! */ ret = gpmi_read_page(this, payload_phys, auxiliary_phys); - read_page_end(this, buf, mtd->writesize, + read_page_end(this, buf, nfc_geo->payload_size, this->payload_virt, this->payload_phys, nfc_geo->payload_size, payload_virt, payload_phys); @@ -1041,7 +1042,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0]; } - read_page_swap_end(this, buf, mtd->writesize, + read_page_swap_end(this, buf, nfc_geo->payload_size, this->payload_virt, this->payload_phys, nfc_geo->payload_size, payload_virt, payload_phys); @@ -1049,6 +1050,90 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, return max_bitflips; } +/* Fake a virtual small page for the subpage read */ +static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offs, uint32_t len, uint8_t *buf, int page) +{ + struct gpmi_nand_data *this = chip->priv; + void __iomem *bch_regs = this->resources.bch_regs; + struct bch_geometry old_geo = this->bch_geometry; + struct bch_geometry *geo = &this->bch_geometry; + int size = chip->ecc.size; /* ECC chunk size */ + int meta, n, page_size; + u32 r1_old, r2_old, r1_new, r2_new; + unsigned int max_bitflips; + int first, last, marker_pos; + int ecc_parity_size; + int col = 0; + + /* The size of ECC parity */ + ecc_parity_size = geo->gf_len * geo->ecc_strength / 8; + + /* Align it with the chunk size */ + first = offs / size; + last = (offs + len - 1) / size; + + /* + * Find the chunk which contains the Block Marker. If this chunk is + * in the range of [first, last], we have to read out the whole page. + * Why? since we had swapped the data at the position of Block Marker + * to the metadata which is bound with the chunk 0. + */ + marker_pos = geo->block_mark_byte_offset / size; + if (last >= marker_pos && first <= marker_pos) { + dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n", + page, first, last, marker_pos); + return gpmi_ecc_read_page(mtd, chip, buf, 0, page); + } + + meta = geo->metadata_size; + if (first) { + col = meta + (size + ecc_parity_size) * first; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1); + + meta = 0; + buf = buf + first * size; + } + + /* Save the old environment */ + r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0); + r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1); + + /* change the BCH registers and bch_geometry{} */ + n = last - first + 1; + page_size = meta + (size + ecc_parity_size) * n; + + r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS | + BM_BCH_FLASH0LAYOUT0_META_SIZE); + r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1) + | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta); + writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0); + + r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE; + r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size); + writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1); + + geo->ecc_chunk_count = n; + geo->payload_size = n * size; + geo->page_size = page_size; + geo->auxiliary_status_offset = ALIGN(meta, 4); + + dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n", + page, offs, len, col, first, n, page_size); + + /* Read the subpage now */ + this->swap_block_mark = false; + max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page); + + /* Restore */ + writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0); + writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1); + this->bch_geometry = old_geo; + this->swap_block_mark = true; + + return max_bitflips; +} + static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required) { @@ -1566,6 +1651,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this) ecc->layout = &gpmi_hw_ecclayout; /* + * We only enable the subpage read when: + * (1) the chip is imx6, and + * (2) the size of the ECC parity is byte aligned. + */ + if (GPMI_IS_MX6Q(this) && + ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) { + ecc->read_subpage = gpmi_ecc_read_subpage; + chip->options |= NAND_SUBPAGE_READ; + } + + /* * Can we enable the extra features? such as EDO or Sync mode. * * We do not check the return value now. That's means if we fail in diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 31ee7cfbc12b..e78841a2dcc3 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -30,7 +30,6 @@ #include <linux/gfp.h> #include <linux/delay.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index e9a4835c4dd9..dba262bf766f 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1501,6 +1501,8 @@ static int mxcnd_probe(struct platform_device *pdev) init_completion(&host->op_completion); host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) + return host->irq; /* * Use host->devtype_data->irq_control() here instead of irq_control() diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9715a7ba164a..9d01c4df838c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -589,7 +589,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command, /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); ctrl &= ~NAND_CTRL_CHANGE; @@ -680,7 +681,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); ctrl &= ~NAND_CTRL_CHANGE; @@ -1160,9 +1162,11 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * @data_offs: offset of requested data within the page * @readlen: data length * @bufpoi: buffer to store read data + * @page: page number to read */ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) + uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, + int page) { int start_step, end_step, num_steps; uint32_t *eccpos = chip->ecc.layout->eccpos; @@ -1170,13 +1174,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, int data_col_addr, i, gaps = 0; int datafrag_len, eccfrag_len, aligned_len, aligned_pos; int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; - int index = 0; + int index; unsigned int max_bitflips = 0; /* Column address within the page aligned to ECC size (256bytes) */ start_step = data_offs / chip->ecc.size; end_step = (data_offs + readlen - 1) / chip->ecc.size; num_steps = end_step - start_step + 1; + index = start_step * chip->ecc.bytes; /* Data size aligned to ECC ecc.size */ datafrag_len = num_steps * chip->ecc.size; @@ -1213,8 +1218,6 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, * Send the command to read the particular ECC bytes take care * about buswidth alignment in read_buf. */ - index = start_step * chip->ecc.bytes; - aligned_pos = eccpos[index] & ~(busw - 1); aligned_len = eccfrag_len; if (eccpos[index] & (busw - 1)) @@ -1538,7 +1541,8 @@ read_retry: else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd, chip, - col, bytes, bufpoi); + col, bytes, bufpoi, + page); else ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); @@ -2000,7 +2004,7 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, oob += chip->ecc.prepad; } - chip->read_buf(mtd, oob, eccbytes); + chip->write_buf(mtd, oob, eccbytes); oob += eccbytes; if (chip->ecc.postpad) { @@ -3063,7 +3067,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int *busw) { struct nand_onfi_params *p = &chip->onfi_params; - int i; + int i, j; int val; /* Try ONFI for unknown chip or LP */ @@ -3072,18 +3076,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') return 0; - /* - * ONFI must be probed in 8-bit mode or with NAND_BUSWIDTH_AUTO, not - * with NAND_BUSWIDTH_16 - */ - if (chip->options & NAND_BUSWIDTH_16) { - pr_err("ONFI cannot be probed in 16-bit mode; aborting\n"); - return 0; - } - chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); for (i = 0; i < 3; i++) { - chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); + for (j = 0; j < sizeof(*p); j++) + ((uint8_t *)p)[j] = chip->read_byte(mtd); if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { break; @@ -3169,6 +3165,87 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, } /* + * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise. + */ +static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + struct nand_jedec_params *p = &chip->jedec_params; + struct jedec_ecc_info *ecc; + int val; + int i, j; + + /* Try JEDEC for unknown chip or LP */ + chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1); + if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' || + chip->read_byte(mtd) != 'C') + return 0; + + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1); + for (i = 0; i < 3; i++) { + for (j = 0; j < sizeof(*p); j++) + ((uint8_t *)p)[j] = chip->read_byte(mtd); + + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == + le16_to_cpu(p->crc)) + break; + } + + if (i == 3) { + pr_err("Could not find valid JEDEC parameter page; aborting\n"); + return 0; + } + + /* Check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 2)) + chip->jedec_version = 10; + else if (val & (1 << 1)) + chip->jedec_version = 1; /* vendor specific version */ + + if (!chip->jedec_version) { + pr_info("unsupported JEDEC version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + + mtd->writesize = le32_to_cpu(p->byte_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + + /* Please reference to the comment for nand_flash_detect_onfi. */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); + chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; + chip->bits_per_cell = p->bits_per_cell; + + if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS) + *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + /* ECC info */ + ecc = &p->ecc_info[0]; + + if (ecc->codeword_size >= 9) { + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + } else { + pr_warn("Invalid codeword size\n"); + } + + return 1; +} + +/* * nand_id_has_period - Check if an ID string has a given wraparound period * @id_data: the ID string * @arrlen: the length of the @id_data array @@ -3474,10 +3551,10 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, */ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, - int busw, int *maf_id, int *dev_id, struct nand_flash_dev *type) { + int busw; int i, maf_idx; u8 id_data[8]; @@ -3533,6 +3610,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Check is chip is ONFI compliant */ if (nand_flash_detect_onfi(mtd, chip, &busw)) goto ident_done; + + /* Check if the chip is JEDEC compliant */ + if (nand_flash_detect_jedec(mtd, chip, &busw)) + goto ident_done; } if (!type->name) @@ -3612,8 +3693,17 @@ ident_done: pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", *maf_id, *dev_id); - pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, - chip->onfi_version ? chip->onfi_params.model : type->name); + + if (chip->onfi_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->onfi_params.model); + else if (chip->jedec_version) + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + chip->jedec_params.model); + else + pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, + type->name); + pr_info("%dMiB, %s, page size: %d, OOB size: %d\n", (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", mtd->writesize, mtd->oobsize); @@ -3634,18 +3724,16 @@ ident_done: int nand_scan_ident(struct mtd_info *mtd, int maxchips, struct nand_flash_dev *table) { - int i, busw, nand_maf_id, nand_dev_id; + int i, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; - /* Get buswidth to select the correct functions */ - busw = chip->options & NAND_BUSWIDTH_16; /* Set the default functions */ - nand_set_defaults(chip, busw); + nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, - &nand_maf_id, &nand_dev_id, table); + type = nand_get_flash_type(mtd, chip, &nand_maf_id, + &nand_dev_id, table); if (IS_ERR(type)) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) @@ -3696,15 +3784,26 @@ int nand_scan_tail(struct mtd_info *mtd) int i; struct nand_chip *chip = mtd->priv; struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_buffers *nbuf; /* New bad blocks should be marked in OOB, flash-based BBT, or both */ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && !(chip->bbt_options & NAND_BBT_USE_FLASH)); - if (!(chip->options & NAND_OWN_BUFFERS)) - chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); - if (!chip->buffers) - return -ENOMEM; + if (!(chip->options & NAND_OWN_BUFFERS)) { + nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + + mtd->oobsize * 3, GFP_KERNEL); + if (!nbuf) + return -ENOMEM; + nbuf->ecccalc = (uint8_t *)(nbuf + 1); + nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; + nbuf->databuf = nbuf->ecccode + mtd->oobsize; + + chip->buffers = nbuf; + } else { + if (!chip->buffers) + return -ENOMEM; + } /* Set the internal oob buffer location, just after the page data */ chip->oob_poi = chip->buffers->databuf + mtd->writesize; @@ -3825,7 +3924,7 @@ int nand_scan_tail(struct mtd_info *mtd) case NAND_ECC_SOFT_BCH: if (!mtd_nand_has_bch()) { - pr_warn("CONFIG_MTD_ECC_BCH not enabled\n"); + pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n"); BUG(); } ecc->calculate = nand_bch_calculate_ecc; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index daa2faacd7d0..3d7c89fc1031 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -43,6 +43,9 @@ struct nand_flash_dev nand_flash_ids[] = { {"TC58NVG6D2 64G 3.3V 8-bit", { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, + {"SDTNRGAMA 64G 3.3V 8-bit", + { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} }, + SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) }, LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 9ee09a8177c6..e8a5fffd6ab2 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -10,7 +10,6 @@ */ #include <linux/slab.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -152,7 +151,8 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, if (column != -1 || page_addr != -1) { if (column != -1) { - if (chip->options & NAND_BUSWIDTH_16) + if (chip->options & NAND_BUSWIDTH_16 && + !nand_opcode_8bits(command)) column >>= 1; write_addr_reg(nand, column); write_addr_reg(nand, column >> 8 | ENDADDR); @@ -225,7 +225,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand) val = __raw_readl(nand->reg + REG_FMICSR); if (!(val & NAND_EN)) - __raw_writel(val | NAND_EN, REG_FMICSR); + __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR); val = __raw_readl(nand->reg + REG_SMCSR); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index bf642ceef681..1ff49b80bdaf 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -118,14 +118,9 @@ #define OMAP24XX_DMA_GPMC 4 -#define BCH8_MAX_ERROR 8 /* upto 8 bit correctable */ -#define BCH4_MAX_ERROR 4 /* upto 4 bit correctable */ - #define SECTOR_BYTES 512 /* 4 bit padding to make byte aligned, 56 = 52 + 4 */ #define BCH4_BIT_PAD 4 -#define BCH8_ECC_MAX ((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8) -#define BCH4_ECC_MAX ((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8) /* GPMC ecc engine settings for read */ #define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */ @@ -159,7 +154,7 @@ struct omap_nand_info { int gpmc_cs; unsigned long phys_base; - unsigned long mem_size; + enum omap_ecc ecc_opt; struct completion comp; struct dma_chan *dma; int gpmc_irq_fifo; @@ -172,7 +167,6 @@ struct omap_nand_info { int buf_len; struct gpmc_nand_regs reg; /* fields specific for BCHx_HW ECC scheme */ - bool is_elm_used; struct device *elm_dev; struct device_node *of_node; }; @@ -1043,9 +1037,8 @@ static int omap_dev_ready(struct mtd_info *mtd) } } -#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH) /** - * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction + * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation * @mtd: MTD device structure * @mode: Read/Write mode * @@ -1056,50 +1049,73 @@ static int omap_dev_ready(struct mtd_info *mtd) * eccsize0 = 0 (no additional protected byte in spare area) * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) */ -static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) +static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) { - int nerrors; + unsigned int bch_type; unsigned int dev_width, nsectors; struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); + enum omap_ecc ecc_opt = info->ecc_opt; struct nand_chip *chip = mtd->priv; u32 val, wr_mode; unsigned int ecc_size1, ecc_size0; - /* Using wrapping mode 6 for writing */ - wr_mode = BCH_WRAPMODE_6; - - /* - * ECC engine enabled for valid ecc_size0 nibbles - * and disabled for ecc_size1 nibbles. - */ - ecc_size0 = BCH_ECC_SIZE0; - ecc_size1 = BCH_ECC_SIZE1; - - /* Perform ecc calculation on 512-byte sector */ - nsectors = 1; - - /* Update number of error correction */ - nerrors = info->nand.ecc.strength; - - /* Multi sector reading/writing for NAND flash with page size < 4096 */ - if (info->is_elm_used && (mtd->writesize <= 4096)) { + /* GPMC configurations for calculating ECC */ + switch (ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + bch_type = 0; + nsectors = 1; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + case OMAP_ECC_BCH4_CODE_HW: + bch_type = 0; + nsectors = chip->ecc.steps; if (mode == NAND_ECC_READ) { - /* Using wrapping mode 1 for reading */ - wr_mode = BCH_WRAPMODE_1; - - /* - * ECC engine enabled for ecc_size0 nibbles - * and disabled for ecc_size1 nibbles. - */ - ecc_size0 = (nerrors == 8) ? - BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0; - ecc_size1 = (nerrors == 8) ? - BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1; + wr_mode = BCH_WRAPMODE_1; + ecc_size0 = BCH4R_ECC_SIZE0; + ecc_size1 = BCH4R_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; } - - /* Perform ecc calculation for one page (< 4096) */ - nsectors = info->nand.ecc.steps; + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + bch_type = 1; + nsectors = 1; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + case OMAP_ECC_BCH8_CODE_HW: + bch_type = 1; + nsectors = chip->ecc.steps; + if (mode == NAND_ECC_READ) { + wr_mode = BCH_WRAPMODE_1; + ecc_size0 = BCH8R_ECC_SIZE0; + ecc_size1 = BCH8R_ECC_SIZE1; + } else { + wr_mode = BCH_WRAPMODE_6; + ecc_size0 = BCH_ECC_SIZE0; + ecc_size1 = BCH_ECC_SIZE1; + } + break; + default: + return; } writel(ECC1, info->reg.gpmc_ecc_control); @@ -1112,7 +1128,7 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) /* BCH configuration */ val = ((1 << 16) | /* enable BCH */ - (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ + (bch_type << 12) | /* BCH4/BCH8/BCH16 */ (wr_mode << 8) | /* wrap mode */ (dev_width << 7) | /* bus width */ (((nsectors-1) & 0x7) << 4) | /* number of sectors */ @@ -1124,132 +1140,40 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) /* Clear ecc and enable bits */ writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); } -#endif - -#ifdef CONFIG_MTD_NAND_ECC_BCH -/** - * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes - * @mtd: MTD device structure - * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer - */ -static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - unsigned long nsectors, val1, val2; - int i; - - nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; - - for (i = 0; i < nsectors; i++) { - /* Read hw-computed remainder */ - val1 = readl(info->reg.gpmc_bch_result0[i]); - val2 = readl(info->reg.gpmc_bch_result1[i]); - - /* - * Add constant polynomial to remainder, in order to get an ecc - * sequence of 0xFFs for a buffer filled with 0xFFs; and - * left-justify the resulting polynomial. - */ - *ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF); - *ecc_code++ = 0x13 ^ ((val2 >> 4) & 0xFF); - *ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF)); - *ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF); - *ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF); - *ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF); - *ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4); - } - - return 0; -} +static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; +static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, + 0x97, 0x79, 0xe5, 0x24, 0xb5}; /** - * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes - * @mtd: MTD device structure - * @dat: The pointer to data on which ecc is computed - * @ecc_code: The ecc_code buffer - */ -static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) -{ - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - unsigned long nsectors, val1, val2, val3, val4; - int i; - - nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; - - for (i = 0; i < nsectors; i++) { - - /* Read hw-computed remainder */ - val1 = readl(info->reg.gpmc_bch_result0[i]); - val2 = readl(info->reg.gpmc_bch_result1[i]); - val3 = readl(info->reg.gpmc_bch_result2[i]); - val4 = readl(info->reg.gpmc_bch_result3[i]); - - /* - * Add constant polynomial to remainder, in order to get an ecc - * sequence of 0xFFs for a buffer filled with 0xFFs. - */ - *ecc_code++ = 0xef ^ (val4 & 0xFF); - *ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF); - *ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF); - *ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF); - *ecc_code++ = 0xed ^ (val3 & 0xFF); - *ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF); - *ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF); - *ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF); - *ecc_code++ = 0x97 ^ (val2 & 0xFF); - *ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF); - *ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF); - *ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF); - *ecc_code++ = 0xb5 ^ (val1 & 0xFF); - } - - return 0; -} -#endif /* CONFIG_MTD_NAND_ECC_BCH */ - -#ifdef CONFIG_MTD_NAND_OMAP_BCH -/** - * omap3_calculate_ecc_bch - Generate bytes of ECC bytes + * omap_calculate_ecc_bch - Generate bytes of ECC bytes * @mtd: MTD device structure * @dat: The pointer to data on which ecc is computed * @ecc_code: The ecc_code buffer * * Support calculating of BCH4/8 ecc vectors for the page */ -static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) +static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_calc) { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); + int eccbytes = info->nand.ecc.bytes; + struct gpmc_nand_regs *gpmc_regs = &info->reg; + u8 *ecc_code; unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4; - int i, eccbchtsel; + int i; nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1; - /* - * find BCH scheme used - * 0 -> BCH4 - * 1 -> BCH8 - */ - eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3); - for (i = 0; i < nsectors; i++) { - - /* Read hw-computed remainder */ - bch_val1 = readl(info->reg.gpmc_bch_result0[i]); - bch_val2 = readl(info->reg.gpmc_bch_result1[i]); - if (eccbchtsel) { - bch_val3 = readl(info->reg.gpmc_bch_result2[i]); - bch_val4 = readl(info->reg.gpmc_bch_result3[i]); - } - - if (eccbchtsel) { - /* BCH8 ecc scheme */ + ecc_code = ecc_calc; + switch (info->ecc_opt) { + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH8_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); + bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]); + bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]); *ecc_code++ = (bch_val4 & 0xFF); *ecc_code++ = ((bch_val3 >> 24) & 0xFF); *ecc_code++ = ((bch_val3 >> 16) & 0xFF); @@ -1263,14 +1187,11 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat, *ecc_code++ = ((bch_val1 >> 16) & 0xFF); *ecc_code++ = ((bch_val1 >> 8) & 0xFF); *ecc_code++ = (bch_val1 & 0xFF); - /* - * Setting 14th byte to zero to handle - * erased page & maintain compatibility - * with RBL - */ - *ecc_code++ = 0x0; - } else { - /* BCH4 ecc scheme */ + break; + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + case OMAP_ECC_BCH4_CODE_HW: + bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]); + bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]); *ecc_code++ = ((bch_val2 >> 12) & 0xFF); *ecc_code++ = ((bch_val2 >> 4) & 0xFF); *ecc_code++ = ((bch_val2 & 0xF) << 4) | @@ -1279,12 +1200,38 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat, *ecc_code++ = ((bch_val1 >> 12) & 0xFF); *ecc_code++ = ((bch_val1 >> 4) & 0xFF); *ecc_code++ = ((bch_val1 & 0xF) << 4); - /* - * Setting 8th byte to zero to handle - * erased page - */ - *ecc_code++ = 0x0; + break; + default: + return -EINVAL; } + + /* ECC scheme specific syndrome customizations */ + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back */ + for (i = 0; i < eccbytes; i++) + ecc_calc[i] ^= bch4_polynomial[i]; + break; + case OMAP_ECC_BCH4_CODE_HW: + /* Set 8th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + /* Add constant polynomial to remainder, so that + * ECC of blank pages results in 0x0 on reading back */ + for (i = 0; i < eccbytes; i++) + ecc_calc[i] ^= bch8_polynomial[i]; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* Set 14th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + break; + default: + return -EINVAL; + } + + ecc_calc += eccbytes; } return 0; @@ -1329,6 +1276,7 @@ static int erased_sector_bitflips(u_char *data, u_char *oob, return flip_bits; } +#ifdef CONFIG_MTD_NAND_OMAP_BCH /** * omap_elm_correct_data - corrects page data area in case error reported * @mtd: MTD device structure @@ -1337,55 +1285,46 @@ static int erased_sector_bitflips(u_char *data, u_char *oob, * @calc_ecc: ecc read from HW ECC registers * * Calculated ecc vector reported as zero in case of non-error pages. - * In case of error/erased pages non-zero error vector is reported. - * In case of non-zero ecc vector, check read_ecc at fixed offset - * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not. - * To handle bit flips in this data, count the number of 0's in - * read_ecc[x] and check if it greater than 4. If it is less, it is - * programmed page, else erased page. - * - * 1. If page is erased, check with standard ecc vector (ecc vector - * for erased page to find any bit flip). If check fails, bit flip - * is present in erased page. Count the bit flips in erased page and - * if it falls under correctable level, report page with 0xFF and - * update the correctable bit information. - * 2. If error is reported on programmed page, update elm error - * vector and correct the page with ELM error correction routine. - * + * In case of non-zero ecc vector, first filter out erased-pages, and + * then process data via ELM to detect bit-flips. */ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, u_char *read_ecc, u_char *calc_ecc) { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); + struct nand_ecc_ctrl *ecc = &info->nand.ecc; int eccsteps = info->nand.ecc.steps; int i , j, stat = 0; - int eccsize, eccflag, ecc_vector_size; + int eccflag, actual_eccbytes; struct elm_errorvec err_vec[ERROR_VECTOR_MAX]; u_char *ecc_vec = calc_ecc; u_char *spare_ecc = read_ecc; u_char *erased_ecc_vec; - enum bch_ecc type; + u_char *buf; + int bitflip_count; bool is_error_reported = false; + u32 bit_pos, byte_pos, error_max, pos; + int err; - /* Initialize elm error vector to zero */ - memset(err_vec, 0, sizeof(err_vec)); - - if (info->nand.ecc.strength == BCH8_MAX_ERROR) { - type = BCH8_ECC; - erased_ecc_vec = bch8_vector; - } else { - type = BCH4_ECC; + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + /* omit 7th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; erased_ecc_vec = bch4_vector; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* omit 14th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; + erased_ecc_vec = bch8_vector; + break; + default: + pr_err("invalid driver configuration\n"); + return -EINVAL; } - ecc_vector_size = info->nand.ecc.bytes; - - /* - * Remove extra byte padding for BCH8 RBL - * compatibility and erased page handling - */ - eccsize = ecc_vector_size - 1; + /* Initialize elm error vector to zero */ + memset(err_vec, 0, sizeof(err_vec)); for (i = 0; i < eccsteps ; i++) { eccflag = 0; /* initialize eccflag */ @@ -1394,8 +1333,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, * Check any error reported, * In case of error, non zero ecc reported. */ - - for (j = 0; (j < eccsize); j++) { + for (j = 0; j < actual_eccbytes; j++) { if (calc_ecc[j] != 0) { eccflag = 1; /* non zero ecc, error present */ break; @@ -1403,50 +1341,43 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, } if (eccflag == 1) { - /* - * Set threshold to minimum of 4, half of ecc.strength/2 - * to allow max bit flip in byte to 4 - */ - unsigned int threshold = min_t(unsigned int, 4, - info->nand.ecc.strength / 2); - - /* - * Check data area is programmed by counting - * number of 0's at fixed offset in spare area. - * Checking count of 0's against threshold. - * In case programmed page expects at least threshold - * zeros in byte. - * If zeros are less than threshold for programmed page/ - * zeros are more than threshold erased page, either - * case page reported as uncorrectable. - */ - if (hweight8(~read_ecc[eccsize]) >= threshold) { + if (memcmp(calc_ecc, erased_ecc_vec, + actual_eccbytes) == 0) { /* - * Update elm error vector as - * data area is programmed + * calc_ecc[] matches pattern for ECC(all 0xff) + * so this is definitely an erased-page */ - err_vec[i].error_reported = true; - is_error_reported = true; } else { - /* Error reported in erased page */ - int bitflip_count; - u_char *buf = &data[info->nand.ecc.size * i]; - - if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) { - bitflip_count = erased_sector_bitflips( - buf, read_ecc, info); - - if (bitflip_count) - stat += bitflip_count; - else - return -EINVAL; + buf = &data[info->nand.ecc.size * i]; + /* + * count number of 0-bits in read_buf. + * This check can be removed once a similar + * check is introduced in generic NAND driver + */ + bitflip_count = erased_sector_bitflips( + buf, read_ecc, info); + if (bitflip_count) { + /* + * number of 0-bits within ECC limits + * So this may be an erased-page + */ + stat += bitflip_count; + } else { + /* + * Too many 0-bits. It may be a + * - programmed-page, OR + * - erased-page with many bit-flips + * So this page requires check by ELM + */ + err_vec[i].error_reported = true; + is_error_reported = true; } } } /* Update the ecc vector */ - calc_ecc += ecc_vector_size; - read_ecc += ecc_vector_size; + calc_ecc += ecc->bytes; + read_ecc += ecc->bytes; } /* Check if any error reported */ @@ -1456,23 +1387,26 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, /* Decode BCH error using ELM module */ elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec); + err = 0; for (i = 0; i < eccsteps; i++) { - if (err_vec[i].error_reported) { + if (err_vec[i].error_uncorrectable) { + pr_err("nand: uncorrectable bit-flips found\n"); + err = -EBADMSG; + } else if (err_vec[i].error_reported) { for (j = 0; j < err_vec[i].error_count; j++) { - u32 bit_pos, byte_pos, error_max, pos; - - if (type == BCH8_ECC) - error_max = BCH8_ECC_MAX; - else - error_max = BCH4_ECC_MAX; - - if (info->nand.ecc.strength == BCH8_MAX_ERROR) - pos = err_vec[i].error_loc[j]; - else - /* Add 4 to take care 4 bit padding */ + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + /* Add 4 bits to take care of padding */ pos = err_vec[i].error_loc[j] + BCH4_BIT_PAD; - + break; + case OMAP_ECC_BCH8_CODE_HW: + pos = err_vec[i].error_loc[j]; + break; + default: + return -EINVAL; + } + error_max = (ecc->size + actual_eccbytes) * 8; /* Calculate bit position of error */ bit_pos = pos % 8; @@ -1480,13 +1414,22 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, byte_pos = (error_max - pos - 1) / 8; if (pos < error_max) { - if (byte_pos < 512) + if (byte_pos < 512) { + pr_debug("bitflip@dat[%d]=%x\n", + byte_pos, data[byte_pos]); data[byte_pos] ^= 1 << bit_pos; - else + } else { + pr_debug("bitflip@oob[%d]=%x\n", + (byte_pos - 512), + spare_ecc[byte_pos - 512]); spare_ecc[byte_pos - 512] ^= 1 << bit_pos; + } + } else { + pr_err("invalid bit-flip @ %d:%d\n", + byte_pos, bit_pos); + err = -EBADMSG; } - /* else, not interested to correct ecc */ } } @@ -1494,16 +1437,11 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, stat += err_vec[i].error_count; /* Update page data with sector size */ - data += info->nand.ecc.size; - spare_ecc += ecc_vector_size; + data += ecc->size; + spare_ecc += ecc->bytes; } - for (i = 0; i < eccsteps; i++) - /* Return error if uncorrectable error present */ - if (err_vec[i].error_uncorrectable) - return -EINVAL; - - return stat; + return (err) ? err : stat; } /** @@ -1601,7 +1539,8 @@ static int is_elm_present(struct omap_nand_info *info, struct device_node *elm_node, enum bch_ecc bch_type) { struct platform_device *pdev; - info->is_elm_used = false; + struct nand_ecc_ctrl *ecc = &info->nand.ecc; + int err; /* check whether elm-id is passed via DT */ if (!elm_node) { pr_err("nand: error: ELM DT node not found\n"); @@ -1615,10 +1554,10 @@ static int is_elm_present(struct omap_nand_info *info, } /* ELM module available, now configure it */ info->elm_dev = &pdev->dev; - if (elm_config(info->elm_dev, bch_type)) - return -ENODEV; - info->is_elm_used = true; - return 0; + err = elm_config(info->elm_dev, bch_type, + (info->mtd.writesize / ecc->size), ecc->size, ecc->bytes); + + return err; } #endif /* CONFIG_MTD_NAND_ECC_BCH */ @@ -1657,6 +1596,7 @@ static int omap_nand_probe(struct platform_device *pdev) info->gpmc_cs = pdata->cs; info->reg = pdata->reg; info->of_node = pdata->of_node; + info->ecc_opt = pdata->ecc_opt; mtd = &info->mtd; mtd->priv = &info->nand; mtd->name = dev_name(&pdev->dev); @@ -1666,27 +1606,11 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->options |= NAND_SKIP_BBTSCAN; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - err = -EINVAL; - dev_err(&pdev->dev, "error getting memory resource\n"); - goto return_error; - } + nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(nand_chip->IO_ADDR_R)) + return PTR_ERR(nand_chip->IO_ADDR_R); info->phys_base = res->start; - info->mem_size = resource_size(res); - - if (!devm_request_mem_region(&pdev->dev, info->phys_base, - info->mem_size, pdev->dev.driver->name)) { - err = -EBUSY; - goto return_error; - } - - nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base, - info->mem_size); - if (!nand_chip->IO_ADDR_R) { - err = -ENOMEM; - goto return_error; - } nand_chip->controller = &info->controller; @@ -1812,7 +1736,7 @@ static int omap_nand_probe(struct platform_device *pdev) /* populate MTD interface based on ECC scheme */ nand_chip->ecc.layout = &omap_oobinfo; ecclayout = &omap_oobinfo; - switch (pdata->ecc_opt) { + switch (info->ecc_opt) { case OMAP_ECC_HAM1_CODE_HW: pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); nand_chip->ecc.mode = NAND_ECC_HW; @@ -1844,9 +1768,9 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.size = 512; nand_chip->ecc.bytes = 7; nand_chip->ecc.strength = 4; - nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap3_calculate_ecc_bch4; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; /* define ECC layout */ ecclayout->eccbytes = nand_chip->ecc.bytes * (mtd->writesize / @@ -1884,9 +1808,9 @@ static int omap_nand_probe(struct platform_device *pdev) /* 14th bit is kept reserved for ROM-code compatibility */ nand_chip->ecc.bytes = 7 + 1; nand_chip->ecc.strength = 4; - nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.calculate = omap3_calculate_ecc_bch; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; /* define ECC layout */ @@ -1919,9 +1843,9 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.size = 512; nand_chip->ecc.bytes = 13; nand_chip->ecc.strength = 8; - nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = nand_bch_correct_data; - nand_chip->ecc.calculate = omap3_calculate_ecc_bch8; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; /* define ECC layout */ ecclayout->eccbytes = nand_chip->ecc.bytes * (mtd->writesize / @@ -1960,9 +1884,9 @@ static int omap_nand_probe(struct platform_device *pdev) /* 14th bit is kept reserved for ROM-code compatibility */ nand_chip->ecc.bytes = 13 + 1; nand_chip->ecc.strength = 8; - nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.hwctl = omap_enable_hwecc_bch; nand_chip->ecc.correct = omap_elm_correct_data; - nand_chip->ecc.calculate = omap3_calculate_ecc_bch; + nand_chip->ecc.calculate = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; nand_chip->ecc.write_page = omap_write_page_bch; /* This ECC scheme requires ELM H/W block */ diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 90f871acb0ef..2c98f9da7471 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -23,7 +23,6 @@ #undef DEBUG #include <linux/slab.h> -#include <linux/init.h> #include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 2a7a0b27ac38..7588fe2c127f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -38,7 +38,6 @@ #include <linux/platform_data/mtd-nand-pxa3xx.h> -#define NAND_DEV_READY_TIMEOUT 50 #define CHIP_DELAY_TIMEOUT (2 * HZ/10) #define NAND_STOP_DELAY (2 * HZ/50) #define PAGE_CHUNK_SIZE (2048) @@ -1531,7 +1530,7 @@ KEEP_CONFIG: if (!ret) { dev_err(&info->pdev->dev, "ECC strength %d at page size %d is not supported\n", - chip->ecc_strength_ds, mtd->writesize); + ecc_strength, mtd->writesize); return -ENODEV; } diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index f0918e7411d9..79acbb8691b5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -29,7 +29,6 @@ #include <linux/module.h> #include <linux/types.h> -#include <linux/init.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/io.h> diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 8e1919b6f074..093c29ac1a13 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -13,7 +13,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 6547c84afc3a..d945473c3882 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -25,7 +25,6 @@ #include <linux/device.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> #include <linux/mtd/partitions.h> diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 1de33b5d3903..635ee0027691 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -24,7 +24,6 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/slab.h> -#include <linux/init.h> #include <linux/sched.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -3238,20 +3237,17 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, /** * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info * @param mtd MTD device structure - * @param buf the databuffer to put/get data * @param len number of bytes to read + * @param retlen pointer to variable to store the number of read bytes + * @param buf the databuffer to put/get data * * Read factory OTP info. */ -static int onenand_get_fact_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { - size_t retlen; - int ret; - - ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY); - - return ret ? : retlen; + return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL, + MTD_OTP_FACTORY); } /** @@ -3273,20 +3269,17 @@ static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, /** * onenand_get_user_prot_info - [MTD Interface] Read user OTP info * @param mtd MTD device structure - * @param buf the databuffer to put/get data + * @param retlen pointer to variable to store the number of read bytes * @param len number of bytes to read + * @param buf the databuffer to put/get data * * Read user OTP info. */ -static int onenand_get_user_prot_info(struct mtd_info *mtd, - struct otp_info *buf, size_t len) +static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf) { - size_t retlen; - int ret; - - ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER); - - return ret ? : retlen; + return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL, + MTD_OTP_USER); } /** @@ -3995,11 +3988,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) /* Allocate buffers, if necessary */ if (!this->page_buf) { this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); - if (!this->page_buf) { - printk(KERN_ERR "%s: Can't allocate page_buf\n", - __func__); + if (!this->page_buf) return -ENOMEM; - } #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL); if (!this->verify_buf) { @@ -4012,8 +4002,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->oob_buf) { this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); if (!this->oob_buf) { - printk(KERN_ERR "%s: Can't allocate oob_buf\n", - __func__); if (this->options & ONENAND_PAGEBUF_ALLOC) { this->options &= ~ONENAND_PAGEBUF_ALLOC; kfree(this->page_buf); diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index df7400dd4df8..b1a792fd1c23 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -872,10 +872,8 @@ static int s3c_onenand_probe(struct platform_device *pdev) size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); mtd = kzalloc(size, GFP_KERNEL); - if (!mtd) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + if (!mtd) return -ENOMEM; - } onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL); if (!onenand) { diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 233b946e5d66..d1cbf26db2c0 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -602,8 +602,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr) if (rc) { printk(KERN_ERR PREFIX "error writing '%s' at " "0x%lx\n", part->mbd.mtd->name, addr); - if (rc) - goto err; + goto err; } if (block == part->current_block) part->header_cache[offset + HEADER_MAP_OFFSET] = del; @@ -675,8 +674,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, if (rc) { printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n", part->mbd.mtd->name, addr); - if (rc) - goto err; + goto err; } part->sector_map[sector] = addr; @@ -695,8 +693,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, if (rc) { printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n", part->mbd.mtd->name, addr); - if (rc) - goto err; + goto err; } block->used_sectors++; block->free_sectors--; diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 4b8e89583f2a..cf49c22673b9 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -59,15 +59,12 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) struct attribute_group *attr_group; struct attribute **attributes; struct sm_sysfs_attribute *vendor_attribute; + char *vendor; - int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, - SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET); - - char *vendor = kmalloc(vendor_len, GFP_KERNEL); + vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, + SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL); if (!vendor) goto error1; - memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len); - vendor[vendor_len] = 0; /* Initialize sysfs attributes */ vendor_attribute = @@ -78,7 +75,7 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) sysfs_attr_init(&vendor_attribute->dev_attr.attr); vendor_attribute->data = vendor; - vendor_attribute->len = vendor_len; + vendor_attribute->len = strlen(vendor); vendor_attribute->dev_attr.attr.name = "vendor"; vendor_attribute->dev_attr.attr.mode = S_IRUGO; vendor_attribute->dev_attr.show = sm_attr_show; diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c index c818a63532e7..111ee46a7428 100644 --- a/drivers/mtd/tests/mtd_test.c +++ b/drivers/mtd/tests/mtd_test.c @@ -1,6 +1,5 @@ #define pr_fmt(fmt) "mtd_test: " fmt -#include <linux/init.h> #include <linux/module.h> #include <linux/sched.h> #include <linux/printk.h> diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 0ba8b0a28838..7bf416329c19 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -22,7 +22,6 @@ #ifndef __UBI_UBI_H__ #define __UBI_UBI_H__ -#include <linux/init.h> #include <linux/types.h> #include <linux/list.h> #include <linux/rbtree.h> diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c index a27ec94877e4..b7361ed70537 100644 --- a/drivers/of/of_mtd.c +++ b/drivers/of/of_mtd.c @@ -50,6 +50,40 @@ int of_get_nand_ecc_mode(struct device_node *np) EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode); /** + * of_get_nand_ecc_step_size - Get ECC step size associated to + * the required ECC strength (see below). + * @np: Pointer to the given device_node + * + * return the ECC step size, or errno in error case. + */ +int of_get_nand_ecc_step_size(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-step-size", &val); + return ret ? ret : val; +} +EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size); + +/** + * of_get_nand_ecc_strength - Get required ECC strength over the + * correspnding step size as defined by 'nand-ecc-size' + * @np: Pointer to the given device_node + * + * return the ECC strength, or errno in error case. + */ +int of_get_nand_ecc_strength(struct device_node *np) +{ + int ret; + u32 val; + + ret = of_property_read_u32(np, "nand-ecc-strength", &val); + return ret ? ret : val; +} +EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength); + +/** * of_get_nand_bus_width - Get nand bus witdh for given device_node * @np: Pointer to the given device_node * |