From dbbafb74239e8296bc20f86366b3f38e13650900 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 21 Jan 2014 13:59:18 +0100 Subject: mtd: m25p80: Add dual read support Add support for Dual SPI read transfers, which is supported by some Spansion SPI FLASHes. Signed-off-by: Geert Uytterhoeven Acked-by: Marek Vasut Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index ad1913909702..73bf661100f7 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -41,7 +41,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 +55,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 +97,7 @@ enum read_type { M25P80_NORMAL = 0, M25P80_FAST, + M25P80_DUAL, M25P80_QUAD, }; @@ -479,6 +482,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 +496,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 +861,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) \ @@ -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; -- cgit v1.2.3 From f5e00838e83f6fc93f42c7a01b0c612031955b31 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 21 Jan 2014 13:59:19 +0100 Subject: mtd: m25p80: Enable Dual SPI read transfers for s25fl256s1 and s25fl512s Spansion s25fl256s1 and s25fl512s support Dual SPI transfers, hence set the M25P80_DUAL_READ flag. Signed-off-by: Geert Uytterhoeven Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 73bf661100f7..f0871a2e449d 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -960,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) }, -- cgit v1.2.3 From b4c233057771581698a13694ab6f33b48ce837dc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 5 Dec 2013 17:53:50 +0300 Subject: mtd: sm_ftl: heap corruption in sm_create_sysfs_attributes() We always put a NUL terminator one space past the end of the "vendor" buffer. Walter Harms also pointed out that this should just use kstrndup(). Fixes: 7d17c02a01a1 ('mtd: Add new SmartMedia/xD FTL') Signed-off-by: Dan Carpenter Signed-off-by: Brian Norris --- drivers/mtd/sm_ftl.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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; -- cgit v1.2.3 From 6f6b9feece0066695d87ed6df5bc0d8080c0c96c Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 14 Oct 2013 18:52:23 +0200 Subject: mtd: phram: Repair multiple instances support Commit b2a2a84d35e0f42ad26e326ec4258f6a8b8eecbe (mtd: phram: dot not crash when built-in and passing boot param) claims to be "based on Ville Herva's similar patch to block2mtd" (c4e7fb313771ac03dfdca26d30e8b721731c562b), but it has missed the crucial point of the original path: all these "if(n)def MODULE". It has broken the possibility to create several phram instances when phram is compiled as module. The possibility to add instances via /sys writes to /sys/module/phram/parameters/phram was also broken with mentioned patch. Proposed patch takes the idea of original block2mtd patch to its full extent. Assumption "This function is always called before 'init_phram()'" was also incorrect, so removed the comment. This patch effectively reverts also b11ec57fc6e6d4882ef01a0c09a1dde58f50492e (mtd: phram: fix section mismatch for phram_setup). Signed-off-by: Alexander Sverdlin [Brian: remove static assigment = 0] Signed-off-by: Brian Norris --- drivers/mtd/devices/phram.c | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) 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=,
, 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=,,\" 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) -- cgit v1.2.3 From 4bed207cd0b2805b0afa3835a0803d2eb27cbc9e Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Sat, 25 Jan 2014 10:45:08 +0800 Subject: mtd: block2mtd: Add mutex_destroy mutex_destroy added on each device in block2mtd_exit and add_device failure Signed-off-by: Fabian Frederick Signed-off-by: Brian Norris --- drivers/mtd/devices/block2mtd.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index d9fd87a4c8dc..3e12234b7565 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -240,13 +240,13 @@ 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; } mutex_init(&dev->write_mutex); @@ -255,7 +255,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 +274,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 +283,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 +450,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: ")); -- cgit v1.2.3 From 3ea5b037e750274659648b58fb97426566a90373 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Tue, 21 Jan 2014 16:22:52 -0500 Subject: mtd: delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Cc: David Woodhouse Cc: Brian Norris Cc: linux-mtd@lists.infradead.org Signed-off-by: Paul Gortmaker [Brian: dropped one incorrect hunk] Signed-off-by: Brian Norris --- drivers/mtd/chips/cfi_cmdset_0001.c | 1 - drivers/mtd/chips/cfi_cmdset_0002.c | 1 - drivers/mtd/chips/cfi_cmdset_0020.c | 1 - drivers/mtd/devices/m25p80.c | 1 - drivers/mtd/devices/mtd_dataflash.c | 1 - drivers/mtd/devices/sst25l.c | 1 - drivers/mtd/inftlmount.c | 1 - drivers/mtd/maps/bfin-async-flash.c | 1 - drivers/mtd/maps/gpio-addr-flash.c | 1 - drivers/mtd/maps/intel_vr_nor.c | 1 - drivers/mtd/maps/ixp4xx.c | 1 - drivers/mtd/maps/lantiq-flash.c | 1 - drivers/mtd/maps/latch-addr-flash.c | 1 - drivers/mtd/maps/pci.c | 1 - drivers/mtd/maps/physmap_of.c | 1 - drivers/mtd/maps/plat-ram.c | 1 - drivers/mtd/maps/pxa2xx-flash.c | 1 - drivers/mtd/maps/rbtx4939-flash.c | 1 - drivers/mtd/maps/scb2_flash.c | 1 - drivers/mtd/maps/sun_uflash.c | 1 - drivers/mtd/mtd_blkdevs.c | 1 - drivers/mtd/nand/ams-delta.c | 1 - drivers/mtd/nand/au1550nd.c | 1 - drivers/mtd/nand/bf5xx_nand.c | 1 - drivers/mtd/nand/davinci_nand.c | 1 - drivers/mtd/nand/fsl_elbc_nand.c | 1 - drivers/mtd/nand/fsl_ifc_nand.c | 1 - drivers/mtd/nand/gpio.c | 1 - drivers/mtd/nand/mpc5121_nfc.c | 1 - drivers/mtd/nand/nuc900_nand.c | 1 - drivers/mtd/nand/pasemi_nand.c | 1 - drivers/mtd/nand/s3c2410.c | 1 - drivers/mtd/onenand/generic.c | 1 - drivers/mtd/onenand/omap2.c | 1 - drivers/mtd/onenand/onenand_base.c | 1 - drivers/mtd/tests/mtd_test.c | 1 - drivers/mtd/ubi/ubi.h | 1 - 37 files changed, 37 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 77514430f1fe..a19719e00a69 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 89b9d6891532..718244d1211a 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 096993f9711e..88529422c401 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index f0871a2e449d..1e147a849c35 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -15,7 +15,6 @@ * */ -#include #include #include #include diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 624069de4f28..8b278d2b15bb 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 -#include #include #include #include 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 #include #include #include 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 #include #include -#include #include #include #include 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 #include #include #include 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 -#include #include #include #include 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 #include #include -#include #include #include #include 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 #include #include -#include #include #include #include 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 #include #include -#include #include #include #include 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 #include #include #include 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 #include #include -#include #include #include 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 #include -#include #include #include #include diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 10196f5a897d..76ace85ccdc6 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -23,7 +23,6 @@ #include #include -#include #include #include #include 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 #include #include -#include #include #include #include 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 #include #include -#include #include #include #include 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 #include #include -#include #include #include #include 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 #include #include -#include #include #include #include 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 #include #include -#include #include #include 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 -#include #include #include #include diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 2880d888cfc5..7d84c4e4bf43 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -11,7 +11,6 @@ #include #include -#include #include #include #include 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 #include -#include #include #include #include diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index a4989ec6292e..4f5f3223aeef 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -24,7 +24,6 @@ */ #include -#include #include #include #include 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 #include -#include #include #include #include diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index 90ca7e75d6f0..f8c77e311766 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #include 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 #include -#include #include #include #include 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 #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 9ee09a8177c6..90c99ea5184a 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -10,7 +10,6 @@ */ #include -#include #include #include #include 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 -#include #include #include #include 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 #include -#include #include #include #include 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 -#include #include #include #include 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 #include -#include #include #include #include diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 1de33b5d3903..531ccbcdc04b 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include 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 #include #include #include diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 8ea6297a208f..41763223f7ea 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 #include #include #include -- cgit v1.2.3 From f02ea4e6a47d50a38f5baadbe87f5087dd337db0 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Mon, 13 Jan 2014 14:27:12 +0800 Subject: mtd: nand: kill the the NAND_MAX_PAGESIZE/NAND_MAX_OOBSIZE for nand_buffers{} The patch converts the arrays to buffer pointers for nand_buffers{}. The cafe_nand.c is the only NAND_OWN_BUFFERS user which allocates nand_buffers{} itself. This patch disables the DMA for nand_scan_ident, and restores the DMA status after we finish the nand_scan_ident. This way, we can get page size and OOB size and use them to allocate cafe->dmabuf. Since the cafe_nand.c uses the NAND_ECC_HW_SYNDROME ECC mode, we do not allocate the buffers for @ecccalc and @ecccode. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/cafe_nand.c | 68 ++++++++++++++++++++++++++++++-------------- drivers/mtd/nand/nand_base.c | 19 ++++++++++--- include/linux/mtd/nand.h | 12 ++++---- 3 files changed, 67 insertions(+), 32 deletions(-) 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/nand_base.c b/drivers/mtd/nand/nand_base.c index 9715a7ba164a..deeaa1cc4a85 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3696,15 +3696,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; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 32f8612469d8..520ebca11f5d 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -435,17 +435,17 @@ struct nand_ecc_ctrl { /** * struct nand_buffers - buffer structure for read/write - * @ecccalc: buffer for calculated ECC - * @ecccode: buffer for ECC read from flash - * @databuf: buffer for data - dynamically sized + * @ecccalc: buffer pointer for calculated ECC, size is oobsize. + * @ecccode: buffer pointer for ECC read from flash, size is oobsize. + * @databuf: buffer pointer for data, size is (page size + oobsize). * * Do not change the order of buffers. databuf and oobrbuf must be in * consecutive order. */ struct nand_buffers { - uint8_t ecccalc[NAND_MAX_OOBSIZE]; - uint8_t ecccode[NAND_MAX_OOBSIZE]; - uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE]; + uint8_t *ecccalc; + uint8_t *ecccode; + uint8_t *databuf; }; /** -- cgit v1.2.3 From 3f172cbdfbb799e35cc972fc9f7e43d0e971577b Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Sat, 21 Dec 2013 00:02:30 +0800 Subject: mtd: nand: remove the NAND_MAX_PAGESIZE/NAND_MAX_OOBSIZE There is no reference to these two macros now. Just remove them. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 520ebca11f5d..a719686c9cce 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -51,14 +51,6 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 -/* - * This constant declares the max. oobsize / page, which - * is supported now. If you add a chip with bigger oobsize/page - * adjust this accordingly. - */ -#define NAND_MAX_OOBSIZE 744 -#define NAND_MAX_PAGESIZE 8192 - /* * Constants for hardware specific CLE/ALE/NCE function * -- cgit v1.2.3 From 55e571bd0707fb6516d0e38598c9e51683e03ee9 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 3 Jan 2014 13:37:03 +0800 Subject: mtd: nand: add support for SanDisk SDTNRGAMA-008G The datasheet does not tell us how to parse out the ID data, so handle it as a full ID nand. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_ids.c | 3 +++ 1 file changed, 3 insertions(+) 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), -- cgit v1.2.3 From 3dad2344e92c6e1aeae42df1c4824f307c51bcc7 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 29 Jan 2014 14:08:12 -0800 Subject: mtd: nand: force NAND_CMD_READID onto 8-bit bus The NAND command helpers tend to automatically shift the column address for x16 bus devices, since most commands expect a word address, not a byte address. The Read ID command, however, expects an 8-bit address (i.e., 0x00, 0x20, or 0x40 should not be translated to 0x00, 0x10, or 0x20). This fixes the column address for a few drivers which imitate the nand_base defaults. Note that I don't touch sh_flctl.c, since it already handles this problem slightly differently (note its comment "READID is always performed using an 8-bit bus"). I have not tested this patch, as I only have x8 parts up for testing at this point. Hopefully that can change soon... Signed-off-by: Brian Norris Tested-by: Ezequiel Garcia Tested-By: Pekon Gupta --- drivers/mtd/nand/atmel_nand.c | 11 ++++++----- drivers/mtd/nand/au1550nd.c | 3 ++- drivers/mtd/nand/diskonchip.c | 3 ++- drivers/mtd/nand/nand_base.c | 6 ++++-- drivers/mtd/nand/nuc900_nand.c | 3 ++- include/linux/mtd/nand.h | 10 ++++++++++ 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c36e9b84487c..1955389c8fa6 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1659,8 +1659,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 +1674,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 +1788,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 7d84c4e4bf43..bc5c518828d2 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -307,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/diskonchip.c b/drivers/mtd/nand/diskonchip.c index fec31d71b84e..b9b4db607850 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); } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index deeaa1cc4a85..6281151e4cb7 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; diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 90c99ea5184a..331fccbdde61 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -151,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); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index a719686c9cce..c034dc4224cb 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -832,4 +832,14 @@ static inline bool nand_is_slc(struct nand_chip *chip) { return chip->bits_per_cell == 1; } + +/** + * Check if the opcode's address should be sent only on the lower 8 bits + * @command: opcode to check + */ +static inline int nand_opcode_8bits(unsigned int command) +{ + return command == NAND_CMD_READID; +} + #endif /* __LINUX_MTD_NAND_H */ -- cgit v1.2.3 From bd9c6e99b58255b9de1982711ac9487c9a2f18be Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Fri, 29 Nov 2013 22:04:28 -0800 Subject: mtd: nand: don't use read_buf for 8-bit ONFI transfers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a repeated read_byte() instead of read_buf(), since for x16 buswidth devices, we need to avoid the upper I/O[16:9] bits. See the following commit for reference: commit 05f7835975dad6b3b517f9e23415985e648fb875 Author: Uwe Kleine-König Date: Thu Dec 5 22:22:04 2013 +0100 mtd: nand: don't use {read,write}_buf for 8-bit transfers Now, I think that all barriers to probing ONFI on x16 devices are removed, so remove the check from nand_flash_detect_onfi(). Tested on 8-bit ONFI NAND (Micron MT29F32G08CBADAWP). Signed-off-by: Brian Norris Tested-by: Ezequiel Garcia Tested-By: Pekon Gupta --- drivers/mtd/nand/nand_base.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 6281151e4cb7..79ed8ccf5065 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3065,7 +3065,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 */ @@ -3074,18 +3074,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; -- cgit v1.2.3 From b2fda1296bb8e213a6bad3937326ae98c4c4773c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 21 Jan 2014 15:56:34 +0800 Subject: mtd: m25p80: Use positive logic to check JEDEC ID For slightly better readability. Signed-off-by: Axel Lin Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 1e147a849c35..c6e6d8e8ef7b 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1078,9 +1078,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); -- cgit v1.2.3 From 3d44dc235ec16fd3db61e1468adabff131d440bc Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Fri, 31 Jan 2014 13:39:07 +0100 Subject: mtd: nand: flctl: Add dependency on HAS_IOMEM and HAS_DMA On archs like S390 or um this driver cannot build nor work. Make it depend on HAS_IOMEM and HAS_DMA to bypass build failures. drivers/built-in.o: In function `flctl_probe': drivers/mtd/nand/sh_flctl.c:1097: undefined reference to `devm_ioremap_resource' drivers/built-in.o: In function `flctl_dma_fifo0_transfer': drivers/mtd/nand/sh_flctl.c:368: undefined reference to `dma_map_single' drivers/mtd/nand/sh_flctl.c:407: undefined reference to `dma_unmap_single' Signed-off-by: Richard Weinberger Signed-off-by: Brian Norris --- drivers/mtd/nand/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 90ff447bf043..a5bb73865616 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -459,6 +459,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. -- cgit v1.2.3 From 60c3bc1fd6f1fa40b415ef5b83e2948a89a3d79c Mon Sep 17 00:00:00 2001 From: Boris BREZILLON Date: Sat, 1 Feb 2014 19:10:28 +0100 Subject: mtd: nand: fix erroneous read_buf call in nand_write_page_raw_syndrome read_buf is called in place of write_buf in the nand_write_page_raw_syndrome function. Signed-off-by: Boris BREZILLON Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 79ed8ccf5065..3cb1cefcd926 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2002,7 +2002,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) { -- cgit v1.2.3 From 0a21552a6e8ab9d3bacd490f5b94a178ce4d661d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20S=C3=B8rensen?= Date: Mon, 3 Feb 2014 15:54:09 +0100 Subject: mtd: elm: Use correct check on return value of pm_runtime_get_sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ELM driver incorrectly reagard any non-zero return value from pm_runtime_get_sync as an error, but it may return 1 if the device was already active. Fix to only error when return value is negative. Signed-off-by: Stefan Sørensen Signed-off-by: Brian Norris --- drivers/mtd/devices/elm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index d1dd6a33a050..e2c073c5a7cc 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -380,7 +380,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"); -- cgit v1.2.3 From 834fa8a56593044f98a0da68bc21d1a31d05124f Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:08:53 +0900 Subject: mtd: devices: elm: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Acked-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/devices/elm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index e2c073c5a7cc..f160d2c6cd59 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -354,10 +354,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; -- cgit v1.2.3 From bb339decb33684fcd9a88b62d2abe8bc95f68269 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:10:10 +0900 Subject: mtd: spear_smi: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Acked-by: Viresh Kumar Signed-off-by: Brian Norris --- drivers/mtd/devices/spear_smi.c | 2 -- 1 file changed, 2 deletions(-) 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; } -- cgit v1.2.3 From c00cb1b7748aa0e6d2efeb8f4486d788ae61765e Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:11:09 +0900 Subject: mtd: pmc551: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris --- drivers/mtd/devices/pmc551.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) 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; } -- cgit v1.2.3 From e61e4f40b1f3a34fa370f7fda1bff6d66032516d Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:12:15 +0900 Subject: mtd: plat-ram: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris --- drivers/mtd/maps/plat-ram.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 76ace85ccdc6..d597e89f2692 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -137,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; } -- cgit v1.2.3 From 54f5a57e266318d72f84fda95805099986a7e201 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:14:33 +0900 Subject: mtd: lpddr: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris --- drivers/mtd/lpddr/lpddr_cmds.c | 4 +--- drivers/mtd/lpddr/qinfo_probe.c | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) 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)); -- cgit v1.2.3 From e4eec195f3e8106362da72c9d94dd6fcf598ddb7 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:15:38 +0900 Subject: mtd: onenand: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris --- drivers/mtd/onenand/onenand_base.c | 7 +------ drivers/mtd/onenand/samsung.c | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 531ccbcdc04b..e886d7af868f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -3994,11 +3994,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) { @@ -4011,8 +4008,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) { -- cgit v1.2.3 From 5c8b1fbb2e1bfaffbaf9b8d8c47bb65470787de6 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:19:35 +0900 Subject: mtd: cfi: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris --- drivers/mtd/chips/cfi_cmdset_0001.c | 8 ++------ drivers/mtd/chips/cfi_cmdset_0002.c | 8 ++------ drivers/mtd/chips/cfi_cmdset_0020.c | 2 -- drivers/mtd/chips/cfi_probe.c | 4 +--- drivers/mtd/chips/cfi_util.c | 4 +--- 5 files changed, 6 insertions(+), 20 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index a19719e00a69..5e74c860e532 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -434,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; @@ -563,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; icfiq->NumEraseRegions; i++) { unsigned long ernum, ersize; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 718244d1211a..e21fde9d4d7e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -506,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; @@ -660,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; icfiq->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 88529422c401..6293855fb5ee 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -175,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; } @@ -188,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(); -- cgit v1.2.3 From c039bef73b80911d938d2f8fce0fccfb9dda7346 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 6 Feb 2014 15:20:31 +0900 Subject: mtd: gen_probe: Remove unnecessary OOM messages The site-specific OOM messages are unnecessary, because they duplicate the MM subsystem generic OOM message. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris --- drivers/mtd/chips/gen_probe.c | 2 -- 1 file changed, 2 deletions(-) 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; -- cgit v1.2.3 From bec44c45c245b38662f1e61bf0bde95fac1e7fb5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 11 Feb 2014 09:51:18 +0100 Subject: mtd: m25p80: add support for the Spansion s25fl008k chip Signed-off-by: Yusuke Goda Signed-off-by: Kuninori Morimoto Signed-off-by: Geert Uytterhoeven Signed-off-by: Brian Norris --- drivers/mtd/devices/m25p80.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index c6e6d8e8ef7b..882b72072ae7 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -971,6 +971,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) }, -- cgit v1.2.3 From 74414a945ac992c71766bbf76718c7fddbcbd016 Mon Sep 17 00:00:00 2001 From: Nicolas Ferre Date: Wed, 12 Feb 2014 12:26:54 +0100 Subject: mtd: atmel_nand: change log level PIO fall back is not an issue, so don't make this much noise. Signed-off-by: Nicolas Ferre Signed-off-by: Brian Norris --- drivers/mtd/nand/atmel_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 1955389c8fa6..1f719e03c073 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; } -- cgit v1.2.3 From da22b89386e8d4dc89525801dfe60f5f8c29668d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 13 Feb 2014 14:21:44 +0300 Subject: mtd: remove some duplicative checks "rc" is an error code here, no need to check it a second time. Signed-off-by: Dan Carpenter Signed-off-by: Brian Norris --- drivers/mtd/rfd_ftl.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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--; -- cgit v1.2.3 From 26fbf48b7a04d585d89709d9e6f1e66b8bfc5dc2 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 14 Feb 2014 01:09:34 -0200 Subject: mtd: mxc_nand: Propagate the error if platform_get_irq() fails Check the return value from platform_get_irq() and propagate it in the case of error. Signed-off-by: Fabio Estevam Signed-off-by: Brian Norris --- drivers/mtd/nand/mxc_nand.c | 2 ++ 1 file changed, 2 insertions(+) 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() -- cgit v1.2.3 From 91e165030deedc612c153dfeaba294d49632ade3 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 14 Feb 2014 12:16:38 +0530 Subject: mtd: nand: s3c2410: Trivial cleanup in header file Commit 436d42c61c3e ("ARM: samsung: move platform_data definitions") moved the files to the current location but forgot to remove the pointer to its previous location. Clean it up. While at it also add the header file protection macros appropriately. Signed-off-by: Sachin Kamat Signed-off-by: Brian Norris --- include/linux/platform_data/mtd-nand-s3c2410.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/linux/platform_data/mtd-nand-s3c2410.h b/include/linux/platform_data/mtd-nand-s3c2410.h index b64115fa93a4..36bb92172f47 100644 --- a/include/linux/platform_data/mtd-nand-s3c2410.h +++ b/include/linux/platform_data/mtd-nand-s3c2410.h @@ -1,5 +1,4 @@ -/* arch/arm/mach-s3c2410/include/mach/nand.h - * +/* * Copyright (c) 2004 Simtec Electronics * Ben Dooks * @@ -10,6 +9,9 @@ * published by the Free Software Foundation. */ +#ifndef __MTD_NAND_S3C2410_H +#define __MTD_NAND_S3C2410_H + /** * struct s3c2410_nand_set - define a set of one or more nand chips * @disable_ecc: Entirely disable ECC - Dangerous @@ -65,3 +67,5 @@ struct s3c2410_platform_nand { * it with the s3c_device_nand. This allows @nand to be __initdata. */ extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand); + +#endif /*__MTD_NAND_S3C2410_H */ -- cgit v1.2.3 From afbfff03d611de22b1ec7127ad56920e02936d5e Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 21 Feb 2014 13:39:37 +0800 Subject: mtd: nand: add the data structures for JEDEC parameter page Create the nand_jedec_params{} and jedec_ecc_info{} according to the JESD230A (Revision of JESD230, October 2012). Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index c034dc4224cb..588f8a4a27af 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -342,6 +342,81 @@ struct nand_onfi_vendor_micron { u8 param_revision; } __packed; +struct jedec_ecc_info { + u8 ecc_bits; + u8 codeword_size; + __le16 bb_per_lun; + __le16 block_endurance; + u8 reserved[2]; +} __packed; + +struct nand_jedec_params { + /* rev info and features block */ + /* 'J' 'E' 'S' 'D' */ + u8 sig[4]; + __le16 revision; + __le16 features; + u8 opt_cmd[3]; + __le16 sec_cmd; + u8 num_of_param_pages; + u8 reserved0[18]; + + /* manufacturer information block */ + char manufacturer[12]; + char model[20]; + u8 jedec_id[6]; + u8 reserved1[10]; + + /* memory organization block */ + __le32 byte_per_page; + __le16 spare_bytes_per_page; + u8 reserved2[6]; + __le32 pages_per_block; + __le32 blocks_per_lun; + u8 lun_count; + u8 addr_cycles; + u8 bits_per_cell; + u8 programs_per_page; + u8 multi_plane_addr; + u8 multi_plane_op_attr; + u8 reserved3[38]; + + /* electrical parameter block */ + __le16 async_sdr_speed_grade; + __le16 toggle_ddr_speed_grade; + __le16 sync_ddr_speed_grade; + u8 async_sdr_features; + u8 toggle_ddr_features; + u8 sync_ddr_features; + __le16 t_prog; + __le16 t_bers; + __le16 t_r; + __le16 t_r_multi_plane; + __le16 t_ccs; + __le16 io_pin_capacitance_typ; + __le16 input_pin_capacitance_typ; + __le16 clk_pin_capacitance_typ; + u8 driver_strength_support; + __le16 t_ald; + u8 reserved4[36]; + + /* ECC and endurance block */ + u8 guaranteed_good_blocks; + __le16 guaranteed_block_endurance; + struct jedec_ecc_info ecc_info[4]; + u8 reserved5[29]; + + /* reserved */ + u8 reserved6[148]; + + /* vendor */ + __le16 vendor_rev_num; + u8 reserved7[88]; + + /* CRC for Parameter Page */ + __le16 crc; +} __packed; + /** * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices * @lock: protection lock -- cgit v1.2.3 From d94abba7605d3c15123eb3b331a1872ef17d29e0 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 21 Feb 2014 13:39:38 +0800 Subject: mtd: nand: add fields for JEDEC in nand_chip Add the jedec_version field, and add an anonymous union which contains the nand_onfi_params and nand_jedec_params. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 588f8a4a27af..f9af7564118a 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -590,8 +590,12 @@ struct nand_buffers { * @subpagesize: [INTERN] holds the subpagesize * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), * non 0 if ONFI supported. + * @jedec_version: [INTERN] holds the chip JEDEC version (BCD encoded), + * non 0 if JEDEC supported. * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is * supported, 0 otherwise. + * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is + * supported, 0 otherwise. * @read_retries: [INTERN] the number of read retry modes supported * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand @@ -664,7 +668,11 @@ struct nand_chip { int badblockbits; int onfi_version; - struct nand_onfi_params onfi_params; + int jedec_version; + union { + struct nand_onfi_params onfi_params; + struct nand_jedec_params jedec_params; + }; int read_retries; -- cgit v1.2.3 From 7852f8962f0f022b11fc56d63de06226a9f70d88 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 21 Feb 2014 13:39:39 +0800 Subject: mtd: nand: add a helper to get the supported features for JEDEC Add a helper to get the supported features for JEDEC compliant NAND. Also add a macro JEDEC_FEATURE_16_BIT_BUS. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index f9af7564118a..afd1cf9b5eaf 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -350,6 +350,9 @@ struct jedec_ecc_info { u8 reserved[2]; } __packed; +/* JEDEC features */ +#define JEDEC_FEATURE_16_BIT_BUS (1 << 0) + struct nand_jedec_params { /* rev info and features block */ /* 'J' 'E' 'S' 'D' */ @@ -925,4 +928,10 @@ static inline int nand_opcode_8bits(unsigned int command) return command == NAND_CMD_READID; } +/* return the supported JEDEC features. */ +static inline int jedec_feature(struct nand_chip *chip) +{ + return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features) + : 0; +} #endif /* __LINUX_MTD_NAND_H */ -- cgit v1.2.3 From 913618185e51ee1fcbc193b2f121a9d072405619 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 21 Feb 2014 13:39:40 +0800 Subject: mtd: nand: parse out the JEDEC compliant NAND This patch adds the parsing code for the JEDEC compliant NAND. Since we need the 0x40 as the column address, this patch also makes the NAND_CMD_PARAM to use the 8-bit address only. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 85 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 2 +- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 3cb1cefcd926..30416ecf44ef 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3162,6 +3162,87 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, return 1; } +/* + * 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 @@ -3527,6 +3608,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) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index afd1cf9b5eaf..aa005e87d161 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -925,7 +925,7 @@ static inline bool nand_is_slc(struct nand_chip *chip) */ static inline int nand_opcode_8bits(unsigned int command) { - return command == NAND_CMD_READID; + return command == NAND_CMD_READID || command == NAND_CMD_PARAM; } /* return the supported JEDEC features. */ -- cgit v1.2.3 From ffdac6cdd9f4c561fc49192c1ea1570f475659e9 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 21 Feb 2014 13:39:41 +0800 Subject: mtd: nand: print out the right information for JEDEC compliant NAND Check the chip->jedec_version, and print out the right information for JEDEC compliant NAND. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 30416ecf44ef..62e5d26464f0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3691,8 +3691,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); -- cgit v1.2.3 From c69dbbf3335a21aae74376d7e5db50a486d52439 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 17 Feb 2014 23:03:08 +0300 Subject: mtd: nuc900_nand: NULL dereference in nuc900_nand_enable() Instead of writing to "nand->reg + REG_FMICSR" we write to "REG_FMICSR" which is NULL and not a valid register. Fixes: 8bff82cbc308 ('mtd: add nand support for w90p910 (v2)') Signed-off-by: Dan Carpenter Signed-off-by: Brian Norris --- drivers/mtd/nand/nuc900_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index 331fccbdde61..e8a5fffd6ab2 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -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); -- cgit v1.2.3 From f4f6a0be01498f42b023d5aa71275bc05478d331 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 23 Feb 2014 10:32:01 +0100 Subject: mtd: ts5500: Add dependency There is no point in displaying the TS5500-specific driver entries if TS5500 board support itself isn't enabled. Signed-off-by: Jean Delvare Cc: David Woodhouse Cc: Vivien Didelot Signed-off-by: Brian Norris --- drivers/mtd/maps/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 310dc7c93425..179c6e124701 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -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 -- cgit v1.2.3 From 3ead9578443b66ddb3d50ed4f53af8a0c0298ec5 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 12 Feb 2014 12:44:57 -0800 Subject: jffs2: remove from wait queue after schedule() @wait is a local variable, so if we don't remove it from the wait queue list, later wake_up() may end up accessing invalid memory. This was spotted by eyes. Signed-off-by: Li Zefan Cc: David Woodhouse Cc: Artem Bityutskiy Cc: Signed-off-by: Andrew Morton Signed-off-by: Brian Norris --- fs/jffs2/nodemgmt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 03310721712f..bbae5b1a833a 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -179,6 +179,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, spin_unlock(&c->erase_completion_lock); schedule(); + remove_wait_queue(&c->erase_wait, &wait); } else spin_unlock(&c->erase_completion_lock); } else if (ret) -- cgit v1.2.3 From 13b546d96207c131eeae15dc7b26c6e7d0f1cad7 Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Wed, 12 Feb 2014 12:44:56 -0800 Subject: jffs2: avoid soft-lockup in jffs2_reserve_space_gc() We triggered soft-lockup under stress test on 2.6.34 kernel. BUG: soft lockup - CPU#1 stuck for 60009ms! [lockf2.test:14488] ... [] (jffs2_do_reserve_space+0x420/0x440 [jffs2]) [] (jffs2_reserve_space_gc+0x34/0x78 [jffs2]) [] (jffs2_garbage_collect_dnode.isra.3+0x264/0x478 [jffs2]) [] (jffs2_garbage_collect_pass+0x9c0/0xe4c [jffs2]) [] (jffs2_reserve_space+0x104/0x2a8 [jffs2]) [] (jffs2_write_inode_range+0x5c/0x4d4 [jffs2]) [] (jffs2_write_end+0x198/0x2c0 [jffs2]) [] (generic_file_buffered_write+0x158/0x200) [] (__generic_file_aio_write+0x3a4/0x414) [] (generic_file_aio_write+0x5c/0xbc) [] (do_sync_write+0x98/0xd4) [] (vfs_write+0xa8/0x150) [] (sys_write+0x3c/0xc0)] Fix this by adding a cond_resched() in the while loop. [akpm@linux-foundation.org: don't initialize `ret'] Signed-off-by: Li Zefan Cc: David Woodhouse Cc: Artem Bityutskiy Cc: Signed-off-by: Andrew Morton Signed-off-by: Brian Norris --- fs/jffs2/nodemgmt.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index bbae5b1a833a..b6bd4affd9ad 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -212,20 +212,25 @@ out: int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *len, uint32_t sumsize) { - int ret = -EAGAIN; + int ret; minsize = PAD(minsize); jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize); - spin_lock(&c->erase_completion_lock); - while(ret == -EAGAIN) { + while (true) { + spin_lock(&c->erase_completion_lock); ret = jffs2_do_reserve_space(c, minsize, len, sumsize); if (ret) { jffs2_dbg(1, "%s(): looping, ret is %d\n", __func__, ret); } + spin_unlock(&c->erase_completion_lock); + + if (ret == -EAGAIN) + cond_resched(); + else + break; } - spin_unlock(&c->erase_completion_lock); if (!ret) ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1); -- cgit v1.2.3 From 01887a3a2353f1c2fc7488b871d6df8055acb109 Mon Sep 17 00:00:00 2001 From: Wang Guoli Date: Wed, 12 Feb 2014 12:44:54 -0800 Subject: jffs2: unlock f->sem on error in jffs2_new_inode() If jffs2_new_inode() succeeds, it returns with f->sem held, and the caller is responsible for releasing the lock. If it fails, it still returns with the lock held, but the caller won't release the lock, which will lead to deadlock. Fix it by releasing the lock in jffs2_new_inode() on error. Signed-off-by: Wang Guoli Signed-off-by: Wang Nan Cc: Artem Bityutskiy Cc: David Woodhouse Cc: Wang Guoli Signed-off-by: Andrew Morton [Brian: not marked for stable; no one observed deadlock, and I don't think it can happen here] Signed-off-by: Brian Norris --- fs/jffs2/fs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index a69e426435dd..560821bff038 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -457,12 +457,14 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r The umask is only applied if there's no default ACL */ ret = jffs2_init_acl_pre(dir_i, inode, &mode); if (ret) { - make_bad_inode(inode); - iput(inode); - return ERR_PTR(ret); + mutex_unlock(&f->sem); + make_bad_inode(inode); + iput(inode); + return ERR_PTR(ret); } ret = jffs2_do_new_inode (c, f, mode, ri); if (ret) { + mutex_unlock(&f->sem); make_bad_inode(inode); iput(inode); return ERR_PTR(ret); @@ -479,6 +481,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r inode->i_size = 0; if (insert_inode_locked(inode) < 0) { + mutex_unlock(&f->sem); make_bad_inode(inode); iput(inode); return ERR_PTR(-EINVAL); -- cgit v1.2.3 From 3367da5610c50e6b83f86d366d72b41b350b06a2 Mon Sep 17 00:00:00 2001 From: Kamlakant Patel Date: Mon, 6 Jan 2014 19:06:54 +0530 Subject: jffs2: Fix segmentation fault found in stress test Creating a large file on a JFFS2 partition sometimes crashes with this call trace: [ 306.476000] CPU 13 Unable to handle kernel paging request at virtual address c0000000dfff8002, epc == ffffffffc03a80a8, ra == ffffffffc03a8044 [ 306.488000] Oops[#1]: [ 306.488000] Cpu 13 [ 306.492000] $ 0 : 0000000000000000 0000000000000000 0000000000008008 0000000000008007 [ 306.500000] $ 4 : c0000000dfff8002 000000000000009f c0000000e0007cde c0000000ee95fa58 [ 306.508000] $ 8 : 0000000000000001 0000000000008008 0000000000010000 ffffffffffff8002 [ 306.516000] $12 : 0000000000007fa9 000000000000ff0e 000000000000ff0f 80e55930aebb92bb [ 306.524000] $16 : c0000000e0000000 c0000000ee95fa5c c0000000efc80000 ffffffffc09edd70 [ 306.532000] $20 : ffffffffc2b60000 c0000000ee95fa58 0000000000000000 c0000000efc80000 [ 306.540000] $24 : 0000000000000000 0000000000000004 [ 306.548000] $28 : c0000000ee950000 c0000000ee95f738 0000000000000000 ffffffffc03a8044 [ 306.556000] Hi : 00000000000574a5 [ 306.560000] Lo : 6193b7a7e903d8c9 [ 306.564000] epc : ffffffffc03a80a8 jffs2_rtime_compress+0x98/0x198 [ 306.568000] Tainted: G W [ 306.572000] ra : ffffffffc03a8044 jffs2_rtime_compress+0x34/0x198 [ 306.580000] Status: 5000f8e3 KX SX UX KERNEL EXL IE [ 306.584000] Cause : 00800008 [ 306.588000] BadVA : c0000000dfff8002 [ 306.592000] PrId : 000c1100 (Netlogic XLP) [ 306.596000] Modules linked in: [ 306.596000] Process dd (pid: 170, threadinfo=c0000000ee950000, task=c0000000ee6e0858, tls=0000000000c47490) [ 306.608000] Stack : 7c547f377ddc7ee4 7ffc7f967f5d7fae 7f617f507fc37ff4 7e7d7f817f487f5f 7d8e7fec7ee87eb3 7e977ff27eec7f9e 7d677ec67f917f67 7f3d7e457f017ed7 7fd37f517f867eb2 7fed7fd17ca57e1d 7e5f7fe87f257f77 7fd77f0d7ede7fdb 7fba7fef7e197f99 7fde7fe07ee37eb5 7f5c7f8c7fc67f65 7f457fb87f847e93 7f737f3e7d137cd9 7f8e7e9c7fc47d25 7dbb7fac7fb67e52 7ff17f627da97f64 7f6b7df77ffa7ec5 80057ef17f357fb3 7f767fa27dfc7fd5 7fe37e8e7fd07e53 7e227fcf7efb7fa1 7f547e787fa87fcc 7fcb7fc57f5a7ffb 7fc07f6c7ea97e80 7e2d7ed17e587ee0 7fb17f9d7feb7f31 7f607e797e887faa 7f757fdd7c607ff3 7e877e657ef37fbd 7ec17fd67fe67ff7 7ff67f797ff87dc4 7eef7f3a7c337fa6 7fe57fc97ed87f4b 7ebe7f097f0b8003 7fe97e2a7d997cba 7f587f987f3c7fa9 ... [ 306.676000] Call Trace: [ 306.680000] [] jffs2_rtime_compress+0x98/0x198 [ 306.684000] [] jffs2_selected_compress+0x110/0x230 [ 306.692000] [] jffs2_compress+0x5c/0x388 [ 306.696000] [] jffs2_write_inode_range+0xd8/0x388 [ 306.704000] [] jffs2_write_end+0x16c/0x2d0 [ 306.708000] [] generic_file_buffered_write+0xf8/0x2b8 [ 306.716000] [] __generic_file_aio_write+0x1ac/0x350 [ 306.720000] [] generic_file_aio_write+0x80/0x168 [ 306.728000] [] do_sync_write+0x94/0xf8 [ 306.732000] [] vfs_write+0xa4/0x1a0 [ 306.736000] [] SyS_write+0x50/0x90 [ 306.744000] [] handle_sys+0x180/0x1a0 [ 306.748000] [ 306.748000] Code: 020b202d 0205282d 90a50000 <90840000> 14a40038 00000000 0060602d 0000282d 016c5823 [ 306.760000] ---[ end trace 79dd088435be02d0 ]--- Segmentation fault This crash is caused because the 'positions' is declared as an array of signed short. The value of position is in the range 0..65535, and will be converted to a negative number when the position is greater than 32767 and causes a corruption and crash. Changing the definition to 'unsigned short' fixes this issue Signed-off-by: Jayachandran C Signed-off-by: Kamlakant Patel Cc: Signed-off-by: Brian Norris --- fs/jffs2/compr_rtime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 16a5047903a6..406d9cc84ba8 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -33,7 +33,7 @@ static int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) { - short positions[256]; + unsigned short positions[256]; int outpos = 0; int pos=0; @@ -74,7 +74,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen) { - short positions[256]; + unsigned short positions[256]; int outpos = 0; int pos=0; -- cgit v1.2.3 From 41bf1a24c1001f4d0d41a78e1ac575d2f14789d7 Mon Sep 17 00:00:00 2001 From: Ajesh Kunhipurayil Vijayan Date: Mon, 6 Jan 2014 19:06:55 +0530 Subject: jffs2: Fix crash due to truncation of csize mounting JFFS2 partition sometimes crashes with this call trace: [ 1322.240000] Kernel bug detected[#1]: [ 1322.244000] Cpu 2 [ 1322.244000] $ 0 : 0000000000000000 0000000000000018 000000003ff00070 0000000000000001 [ 1322.252000] $ 4 : 0000000000000000 c0000000f3980150 0000000000000000 0000000000010000 [ 1322.260000] $ 8 : ffffffffc09cd5f8 0000000000000001 0000000000000088 c0000000ed300de8 [ 1322.268000] $12 : e5e19d9c5f613a45 ffffffffc046d464 0000000000000000 66227ba5ea67b74e [ 1322.276000] $16 : c0000000f1769c00 c0000000ed1e0200 c0000000f3980150 0000000000000000 [ 1322.284000] $20 : c0000000f3a80000 00000000fffffffc c0000000ed2cfbd8 c0000000f39818f0 [ 1322.292000] $24 : 0000000000000004 0000000000000000 [ 1322.300000] $28 : c0000000ed2c0000 c0000000ed2cfab8 0000000000010000 ffffffffc039c0b0 [ 1322.308000] Hi : 000000000000023c [ 1322.312000] Lo : 000000000003f802 [ 1322.316000] epc : ffffffffc039a9f8 check_tn_node+0x88/0x3b0 [ 1322.320000] Not tainted [ 1322.324000] ra : ffffffffc039c0b0 jffs2_do_read_inode_internal+0x1250/0x1e48 [ 1322.332000] Status: 5400f8e3 KX SX UX KERNEL EXL IE [ 1322.336000] Cause : 00800034 [ 1322.340000] PrId : 000c1004 (Netlogic XLP) [ 1322.344000] Modules linked in: [ 1322.348000] Process jffs2_gcd_mtd7 (pid: 264, threadinfo=c0000000ed2c0000, task=c0000000f0e68dd8, tls=0000000000000000) [ 1322.356000] Stack : c0000000f1769e30 c0000000ed010780 c0000000ed010780 c0000000ed300000 c0000000f1769c00 c0000000f3980150 c0000000f3a80000 00000000fffffffc c0000000ed2cfbd8 ffffffffc039c0b0 ffffffffc09c6340 0000000000001000 0000000000000dec ffffffffc016c9d8 c0000000f39805a0 c0000000f3980180 0000008600000000 0000000000000000 0000000000000000 0000000000000000 0001000000000dec c0000000f1769d98 c0000000ed2cfb18 0000000000010000 0000000000010000 0000000000000044 c0000000f3a80000 c0000000f1769c00 c0000000f3d207a8 c0000000f1769d98 c0000000f1769de0 ffffffffc076f9c0 0000000000000009 0000000000000000 0000000000000000 ffffffffc039cf90 0000000000000017 ffffffffc013fbdc 0000000000000001 000000010003e61c ... [ 1322.424000] Call Trace: [ 1322.428000] [] check_tn_node+0x88/0x3b0 [ 1322.432000] [] jffs2_do_read_inode_internal+0x1250/0x1e48 [ 1322.440000] [] jffs2_do_crccheck_inode+0x70/0xd0 [ 1322.448000] [] jffs2_garbage_collect_pass+0x160/0x870 [ 1322.452000] [] jffs2_garbage_collect_thread+0xdc/0x1f0 [ 1322.460000] [] kthread+0xb8/0xc0 [ 1322.464000] [] kernel_thread_helper+0x10/0x18 [ 1322.472000] [ 1322.472000] Code: 67bd0050 94a4002c 2c830001 <00038036> de050218 2403fffc 0080a82d 00431824 24630044 [ 1322.480000] ---[ end trace b052bb90e97dfbf5 ]--- The variable csize in structure jffs2_tmp_dnode_info is of type uint16_t, but it is used to hold the compressed data length(csize) which is declared as uint32_t. So, when the value of csize exceeds 16bits, it gets truncated when assigned to tn->csize. This is causing a kernel BUG. Changing the definition of csize in jffs2_tmp_dnode_info to uint32_t fixes the issue. Signed-off-by: Ajesh Kunhipurayil Vijayan Signed-off-by: Kamlakant Patel Cc: Signed-off-by: Brian Norris --- fs/jffs2/nodelist.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index e4619b00f7c5..fa35ff79ab35 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -231,7 +231,7 @@ struct jffs2_tmp_dnode_info uint32_t version; uint32_t data_crc; uint32_t partial_crc; - uint16_t csize; + uint32_t csize; uint16_t overlapped; }; -- cgit v1.2.3 From 4b78fc42f3e3f07687dc27efc1153d29e360afa1 Mon Sep 17 00:00:00 2001 From: Christian Riesch Date: Tue, 28 Jan 2014 09:29:44 +0100 Subject: mtd: Add a retlen parameter to _get_{fact,user}_prot_info Signed-off-by: Christian Riesch Cc: Artem Bityutskiy Signed-off-by: Brian Norris --- drivers/mtd/chips/cfi_cmdset_0001.c | 31 +++++++++++++------------------ drivers/mtd/devices/mtd_dataflash.c | 7 ++++--- drivers/mtd/mtdchar.c | 11 ++++++----- drivers/mtd/mtdcore.c | 12 ++++++------ drivers/mtd/mtdpart.c | 14 ++++++++------ drivers/mtd/onenand/onenand_base.c | 30 ++++++++++++------------------ include/linux/mtd/mtd.h | 16 ++++++++-------- 7 files changed, 57 insertions(+), 64 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 5e74c860e532..e4ec355704a6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -68,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 *); @@ -2394,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/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 8b278d2b15bb..a6fdbe83bad6 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -439,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 @@ -449,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, diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 2147e733533b..250798cf76aa 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -889,25 +889,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..0a7d77e65335 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); 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/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index e886d7af868f..635ee0027691 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -3237,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); } /** @@ -3272,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); } /** diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 8cc0e2fb6894..a1b0b4c8fd79 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -204,12 +204,12 @@ struct mtd_info { struct mtd_oob_ops *ops); int (*_write_oob) (struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); - int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, - size_t len); + int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf); int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); - int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, - size_t len); + int (*_get_user_prot_info) (struct mtd_info *mtd, size_t len, + size_t *retlen, struct otp_info *buf); int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to, @@ -278,12 +278,12 @@ static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to, return mtd->_write_oob(mtd, to, ops); } -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); int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); -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); int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, -- cgit v1.2.3 From 6d9434ebb76157071164b32ad03fbed165c74382 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 24 Feb 2014 19:24:48 -0300 Subject: of_mtd: Add helpers to get ECC strength and ECC step size This commit adds simple helpers to obtain the devicetree properties that specify the ECC strength and ECC step size to use on a given NAND controller. Acked-by: Boris BREZILLON Signed-off-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/of/of_mtd.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/of_mtd.h | 12 ++++++++++++ 2 files changed, 46 insertions(+) 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 @@ -49,6 +49,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 diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h index cb32d9c1e8dc..e266caa36402 100644 --- a/include/linux/of_mtd.h +++ b/include/linux/of_mtd.h @@ -13,6 +13,8 @@ #include int of_get_nand_ecc_mode(struct device_node *np); +int of_get_nand_ecc_step_size(struct device_node *np); +int of_get_nand_ecc_strength(struct device_node *np); int of_get_nand_bus_width(struct device_node *np); bool of_get_nand_on_flash_bbt(struct device_node *np); @@ -23,6 +25,16 @@ static inline int of_get_nand_ecc_mode(struct device_node *np) return -ENOSYS; } +static inline int of_get_nand_ecc_step_size(struct device_node *np) +{ + return -ENOSYS; +} + +static inline int of_get_nand_ecc_strength(struct device_node *np) +{ + return -ENOSYS; +} + static inline int of_get_nand_bus_width(struct device_node *np) { return -ENOSYS; -- cgit v1.2.3 From 8dd49165ef5f46b5ad9ba296c559ccff315f9421 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Mon, 24 Feb 2014 19:24:49 -0300 Subject: mtd: nand: Add a devicetree binding for ECC strength and ECC step size Some flashes can only be properly accessed when the ECC mode is specified, so a way to describe such mode is required. Together, the ECC strength and step size define the correction capability, so that we say we will correct "{strength} bit errors per {size} bytes". The interpretation of these parameters is implementation-defined, but they often have ramifications on the formation, interpretation, and placement of correction metadata on the flash. Not all implementations must support all possible combinations. Implementations are encouraged to further define the value(s) they support. Acked-by: Boris BREZILLON Acked-by: Grant Likely Signed-off-by: Ezequiel Garcia Signed-off-by: Brian Norris --- Documentation/devicetree/bindings/mtd/nand.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt index 03855c8c492a..b53f92e252d4 100644 --- a/Documentation/devicetree/bindings/mtd/nand.txt +++ b/Documentation/devicetree/bindings/mtd/nand.txt @@ -5,3 +5,17 @@ "soft_bch". - nand-bus-width : 8 or 16 bus width if not present 8 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false + +- nand-ecc-strength: integer representing the number of bits to correct + per ECC step. + +- nand-ecc-step-size: integer representing the number of data bytes + that are covered by a single ECC step. + +The ECC strength and ECC step size properties define the correction capability +of a controller. Together, they say a controller can correct "{strength} bit +errors per {size} bytes". + +The interpretation of these parameters is implementation-defined, so not all +implementations must support all possible combinations. However, implementations +are encouraged to further specify the value(s) they support. -- cgit v1.2.3 From 6f7db7f3203a0bd48170807adeb53dd401d29110 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 29 Jan 2014 13:39:43 -0800 Subject: mtd: m25p80: add Macronix mx66l1g55g 1Gbit SPI flash Signed-off-by: Brian Norris Acked-by: Marek Vasut --- drivers/mtd/devices/m25p80.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 882b72072ae7..524dab3ac938 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -940,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) }, -- cgit v1.2.3 From 670b46aa6cad88e126c5e7d0f37ed2b41693869d Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Thu, 6 Mar 2014 00:00:00 +0100 Subject: mtd: allow CONFIG_MTD_PHYSMAP_OF also for CONFIG_MTD_RAM Up to now mtd-ram devices described in device trees were only accessible if mtd-flash or mtd-rom were also configured at linux configuration time, because MTD_PHYSMAP_OF was only available if (MTD_CFI || MTD_JEDECPROBE || MTD_ROM). Allow MTD_PHYSMAP_OF selection also when only MTD_RAM is set. Signed-off-by: Philippe De Muyter Signed-off-by: Brian Norris --- drivers/mtd/maps/Kconfig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 179c6e124701..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. -- cgit v1.2.3 From 00b79860eb5f72462016046d3841b19ebff6e846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 26 Feb 2014 14:02:06 +0100 Subject: mtd: bcm47xxpart: fix off by one in partitions limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/bcm47xxpart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index de1eb92e42f5..e388e69d853e 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -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; } @@ -212,7 +212,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; } -- cgit v1.2.3 From 108ebcd81907cd4818feb3bc1eabcc4a5373da32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 26 Feb 2014 14:30:34 +0100 Subject: mtd: bcm47xxpart: avoid overflowing when registering trx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our code parsing "trx" header registers few partitions at once (in one loop iteration). Add extra check in that place. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/bcm47xxpart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index e388e69d853e..23d712209b98 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -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; -- cgit v1.2.3 From 9e3afa5f5c7db81f5ff44d877dda6f5ffce0da19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 28 Feb 2014 18:02:01 +0100 Subject: mtd: bcm47xxpart: allow enabling on ARCH_BCM_5301X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Home routers based on SoCs like BCM53010 (AKA BCM4708) use flashes which can be nicely partitioned with bcm47xxpart. Header bcm47xx_nvram.h is not available on bcm53xx, so don't include it. Signed-off-by: Rafał Miłecki Signed-off-by: Brian Norris --- drivers/mtd/Kconfig | 2 +- drivers/mtd/bcm47xxpart.c | 2 +- 2 files changed, 2 insertions(+), 2 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 23d712209b98..adfa74c1bc45 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -14,7 +14,6 @@ #include #include #include -#include /* 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 -- cgit v1.2.3 From 2a565f56ed0436c5345a79c5468bebfd3340fb10 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 27 Feb 2014 14:13:25 -0300 Subject: mtd: nand: pxa3xx: Remove unused macro This macro is not used so it's safe to remove it. Signed-off-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 2a7a0b27ac38..1e136a6d03d9 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -38,7 +38,6 @@ #include -#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) -- cgit v1.2.3 From e634ce51baa52c131e4a35c01bba9e596a0eb86d Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 27 Feb 2014 14:13:26 -0300 Subject: mtd: nand: pxa3xx: Print actual ECC strength in error message The actual ECC strength used to select the ECC scheme is 'ecc_strength'. Use it in the error message. Signed-off-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/nand/pxa3xx_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 1e136a6d03d9..7588fe2c127f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1530,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; } -- cgit v1.2.3 From bb77082fa2584f0855e614e99b5ffd40b9cf4251 Mon Sep 17 00:00:00 2001 From: Cai Zhiyong Date: Wed, 25 Dec 2013 20:11:15 +0800 Subject: mtd: nand: remove unused function input parameter The nand_get_flash_type parameter "busw" input value is not used by any branch, and it is updated before use it in the function, so remove it, define the "busw" as an internal variable. Signed-off-by: Cai Zhiyong Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 62e5d26464f0..71c5c7a77053 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3549,10 +3549,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]; @@ -3722,18 +3722,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)) -- cgit v1.2.3 From e004debdadf1a661dcd24e2a654e56b0a113e29e Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 3 Jan 2014 11:01:40 +0800 Subject: mtd: nand: add "page" argument for read_subpage hook Add the "page" argument for the read_subpage hook. With this argument, the implementation of this hook could prints out more accurate information for debugging. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 7 +++++-- include/linux/mtd/nand.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 71c5c7a77053..5826da39216e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1162,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; @@ -1540,7 +1542,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); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index aa005e87d161..0747fef2bfc6 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -488,7 +488,7 @@ struct nand_ecc_ctrl { int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page); int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip, - uint32_t offs, uint32_t len, uint8_t *buf); + uint32_t offs, uint32_t len, uint8_t *buf, int page); int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, uint32_t data_len, const uint8_t *data_buf, int oob_required); -- cgit v1.2.3 From 4a57d670a9edb56d7cbc8d9075c7b242e0670ecf Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 3 Jan 2014 11:01:41 +0800 Subject: mtd: gpmi: do not use the mtd->writesize The nfc_geo->payload_size is equal to the mtd->writesize now, use the nfc_geo->payload_size to replace the mtd->writesize. This patch makes preparation for the gpmi's subpage read support. In the subpage support, the nfc_geo->payload_size maybe smaller then the mtd->writesize. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index ca6369fe91ff..5aaa7f541ea1 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -985,7 +985,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 +999,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 +1041,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); -- cgit v1.2.3 From b8e2931d168724ca95d275f4f825636bff82a9e8 Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Fri, 3 Jan 2014 11:01:42 +0800 Subject: mtd: gpmi: add subpage read support 1) Why add the subpage read support? The page size of the nand chip becomes larger and larger, the imx6 has to supports the 16K page or even bigger page. But sometimes, the upper layer only needs a small part of the page, such as 512 bytes or less. For example, ubiattach may only read 64 bytes per page. 2) We only enable the subpage read support when it meets the conditions: <1> the chip is imx6 (or later chips) which can supports large nand page. <2> the size of ECC parity is byte aligned. If the size of ECC parity is not byte aligned, the calling of NAND_CMD_RNDOUT will fail. 3) What does this patch do? This patch will fake a virtual small page for the subpage read, and call the gpmi_ecc_read_page() to do the real work. In order to fake a virtual small page, the patch changes the BCH registers and the bch_geometry{}. After the subpage read finished, we will restore them back. 4) Performace: 4.1) Tested with Toshiba TC58NVG2S0F(4096 + 224) with the following command: #ubiattach /dev/ubi_ctrl -m 4 The detail information of /dev/mtd4 shows below: -------------------------------------------------------------- #mtdinfo /dev/mtd4 mtd4 Name: test Type: nand Eraseblock size: 262144 bytes, 256.0 KiB Amount of eraseblocks: 1856 (486539264 bytes, 464.0 MiB) Minimum input/output unit size: 4096 bytes Sub-page size: 4096 bytes OOB size: 224 bytes Character device major/minor: 90:8 Bad blocks are allowed: true Device is writable: true -------------------------------------------------------------- 4.2) Before this patch: -------------------------------------------------------------- [ 94.530495] UBI: attaching mtd4 to ubi0 [ 98.928850] UBI: scanning is finished [ 98.953594] UBI: attached mtd4 (name "test", size 464 MiB) to ubi0 [ 98.958562] UBI: PEB size: 262144 bytes (256 KiB), LEB size: 253952 bytes [ 98.964076] UBI: min./max. I/O unit sizes: 4096/4096, sub-page size 4096 [ 98.969518] UBI: VID header offset: 4096 (aligned 4096), data offset: 8192 [ 98.975128] UBI: good PEBs: 1856, bad PEBs: 0, corrupted PEBs: 0 [ 98.979843] UBI: user volume: 1, internal volumes: 1, max. volumes count: 128 [ 98.985878] UBI: max/mean erase counter: 2/1, WL threshold: 4096, image sequence number: 2024916145 [ 98.993635] UBI: available PEBs: 0, total reserved PEBs: 1856, PEBs reserved for bad PEB handling: 40 [ 99.001807] UBI: background thread "ubi_bgt0d" started, PID 831 -------------------------------------------------------------- The attach time is about 98.9 - 94.5 = 4.4s 4.3) After this patch: -------------------------------------------------------------- [ 286.464906] UBI: attaching mtd4 to ubi0 [ 289.186129] UBI: scanning is finished [ 289.211416] UBI: attached mtd4 (name "test", size 464 MiB) to ubi0 [ 289.216360] UBI: PEB size: 262144 bytes (256 KiB), LEB size: 253952 bytes [ 289.221858] UBI: min./max. I/O unit sizes: 4096/4096, sub-page size 4096 [ 289.227293] UBI: VID header offset: 4096 (aligned 4096), data offset: 8192 [ 289.232878] UBI: good PEBs: 1856, bad PEBs: 0, corrupted PEBs: 0 [ 289.237628] UBI: user volume: 0, internal volumes: 1, max. volumes count: 128 [ 289.243553] UBI: max/mean erase counter: 1/1, WL threshold: 4096, image sequence number: 2024916145 [ 289.251348] UBI: available PEBs: 1812, total reserved PEBs: 44, PEBs reserved for bad PEB handling: 40 [ 289.259417] UBI: background thread "ubi_bgt0d" started, PID 847 -------------------------------------------------------------- The attach time is about 289.18 - 286.46 = 2.7s 4.4) The conclusion: We achieve (4.4 - 2.7) / 4.4 = 38.6% faster in the ubiattach. Signed-off-by: Huang Shijie Signed-off-by: Brian Norris --- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 96 ++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 5aaa7f541ea1..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 #include #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" @@ -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) { @@ -1565,6 +1650,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this) ecc->strength = bch_geo->ecc_strength; 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. * -- cgit v1.2.3 From 90445ff6241e2a13445310803e2efa606c61f276 Mon Sep 17 00:00:00 2001 From: Herve Codina Date: Mon, 3 Mar 2014 12:15:29 +0100 Subject: mtd: atmel_nand: Disable subpage NAND write when using Atmel PMECC Crash detected on sam5d35 and its pmecc nand ecc controller. The problem was a call to chip->ecc.hwctl from nand_write_subpage_hwecc (nand_base.c) when we write a sub page. chip->ecc.hwctl function is not set when we are using PMECC controller. As a workaround, set NAND_NO_SUBPAGE_WRITE for PMECC controller in order to disable sub page access in nand_write_page. Signed-off-by: Herve Codina Acked-by: Josh Wu Cc: stable@vger.kernel.org Signed-off-by: Brian Norris --- drivers/mtd/nand/atmel_nand.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 1f719e03c073..4ce181a35bcd 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -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; -- cgit v1.2.3 From ea6d833a3fddcd1d60414d48f34c7f4fbe88608f Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Thu, 6 Mar 2014 18:04:22 +0800 Subject: mtd: block2mtd: check device size fixme applied : check device size is a multiple of erasesize. Signed-off-by: Fabian Frederick Signed-off-by: Brian Norris --- drivers/mtd/devices/block2mtd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 3e12234b7565..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; @@ -249,6 +248,11 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) 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); /* Setup the MTD structure */ -- cgit v1.2.3 From 9a78bc83b4c31f67202b7b0a77fa25da732f44a3 Mon Sep 17 00:00:00 2001 From: Christian Riesch Date: Thu, 6 Mar 2014 12:42:37 +0100 Subject: mtd: Fix the behavior of OTP write if there is not enough room for data If a write to one time programmable memory (OTP) hits the end of this memory area, no more data can be written. The count variable in mtdchar_write() in drivers/mtd/mtdchar.c is not decreased anymore. We are trapped in the loop forever, mtdchar_write() will never return in this case. The desired behavior of a write in such a case is described in [1]: - Try to write as much data as possible, truncate the write to fit into the available memory and return the number of bytes that actually have been written. - If no data could be written at all, return -ENOSPC. This patch fixes the behavior of OTP write if there is not enough space for all data: 1) mtd_write_user_prot_reg() in drivers/mtd/mtdcore.c is modified to return -ENOSPC if no data could be written at all. 2) mtdchar_write() is modified to handle -ENOSPC correctly. Exit if a write returned -ENOSPC and yield the correct return value, either then number of bytes that could be written, or -ENOSPC, if no data could be written at all. Furthermore the patch harmonizes the behavior of the OTP memory write in drivers/mtd/devices/mtd_dataflash.c with the other implementations and the requirements from [1]. Instead of returning -EINVAL if the data does not fit into the OTP memory, we try to write as much data as possible/truncate the write. [1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html Signed-off-by: Christian Riesch Signed-off-by: Brian Norris --- drivers/mtd/devices/mtd_dataflash.c | 16 ++++++++++------ drivers/mtd/mtdchar.c | 9 +++++++++ drivers/mtd/mtdcore.c | 12 +++++++++++- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index a6fdbe83bad6..dd22ce2cc9ad 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -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/mtdchar.c b/drivers/mtd/mtdchar.c index 250798cf76aa..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; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 0a7d77e65335..d201feeb3ca6 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -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); -- cgit v1.2.3 From 4e558072ef91f0ca681a74c3aef9d6a30c116f2a Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Tue, 18 Mar 2014 18:56:42 +0530 Subject: mtd: nand: omap: add field to indicate current ecc-scheme in 'struct omap_nand_info' Information of currently selected ECC scheme 'enum omap_ecc ecc_opt' should available outside platform-data, so that single nand_chip->ecc callback can support multiple ecc-scheme configurations. Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index bf642ceef681..403afa71bbf3 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -160,6 +160,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; @@ -1657,6 +1658,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); @@ -1812,7 +1814,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; -- cgit v1.2.3 From de0a4d69e6758bfa5792e7c723a9220eb5be5a32 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Tue, 18 Mar 2014 18:56:43 +0530 Subject: mtd: nand: omap: ecc.correct: omap_elm_correct_data: rename ambiguous variable 'eccsize' and 'ecc_vector_size' renaming following variables as they cause confusion due to resemblence to another similar field in 'struct nand_ecc_ctrl' (nand_chip->ecc.size). renaming: ecc_vector_size --> ecc->bytes (info->nand.ecc.bytes) renaming: eccsize --> actual_eccbytes (info->nand.ecc.bytes - 1) for BCH4 and BCH8 Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 403afa71bbf3..4bf2f76d7d28 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1359,9 +1359,10 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, { 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; @@ -1369,6 +1370,20 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, enum bch_ecc type; bool is_error_reported = false; + switch (info->ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW: + /* omit 7th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; + break; + case OMAP_ECC_BCH8_CODE_HW: + /* omit 14th ECC byte reserved for ROM code compatibility */ + actual_eccbytes = ecc->bytes - 1; + break; + default: + pr_err("invalid driver configuration\n"); + return -EINVAL; + } + /* Initialize elm error vector to zero */ memset(err_vec, 0, sizeof(err_vec)); @@ -1380,14 +1395,6 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, erased_ecc_vec = bch4_vector; } - ecc_vector_size = info->nand.ecc.bytes; - - /* - * Remove extra byte padding for BCH8 RBL - * compatibility and erased page handling - */ - eccsize = ecc_vector_size - 1; - for (i = 0; i < eccsteps ; i++) { eccflag = 0; /* initialize eccflag */ @@ -1395,8 +1402,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; @@ -1421,7 +1427,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, * zeros are more than threshold erased page, either * case page reported as uncorrectable. */ - if (hweight8(~read_ecc[eccsize]) >= threshold) { + if (hweight8(~read_ecc[actual_eccbytes]) >= threshold) { /* * Update elm error vector as * data area is programmed @@ -1433,7 +1439,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, int bitflip_count; u_char *buf = &data[info->nand.ecc.size * i]; - if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) { + if (memcmp(calc_ecc, erased_ecc_vec, + actual_eccbytes)) { bitflip_count = erased_sector_bitflips( buf, read_ecc, info); @@ -1446,8 +1453,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, } /* 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 */ @@ -1496,7 +1503,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, /* Update page data with sector size */ data += info->nand.ecc.size; - spare_ecc += ecc_vector_size; + spare_ecc += ecc->bytes; } for (i = 0; i < eccsteps; i++) -- cgit v1.2.3 From 78f43c53835076f342133e6d2d6e577d6ae06480 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Tue, 18 Mar 2014 18:56:44 +0530 Subject: mtd: nand: omap: ecc.correct: omap_elm_correct_data: fix erased-page detection for BCHx_HW ECC schemes As erased-pages do not have ECC stored in their OOB area, so they need to be seperated out from programmed-pages, before doing BCH ECC correction. In current implementation of omap_elm_correct_data() which does ECC correction for BCHx ECC schemes, this erased-pages are detected based on specific marker byte (reserved as 0x00) in ecc-layout. However, this approach has some limitation like; 1) All ecc-scheme layouts do not have such Reserved byte marker to differentiate between erased-page v/s programmed-page. Thus this is a customized solution. 2) Reserved marker byte can itself be subjected to bit-flips causing erased-page to be misunderstood as programmed-page. This patch removes dependency on any marker byte in ecc-layout, instead it compares calc_ecc[] with pattern of ECC-of-all(0xff). This implicitely means that both 'data + oob == all(0xff). Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 85 +++++++++++++++++++----------------------------- 1 file changed, 33 insertions(+), 52 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 4bf2f76d7d28..a6c979f3c61f 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1338,21 +1338,8 @@ 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) @@ -1367,6 +1354,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, u_char *ecc_vec = calc_ecc; u_char *spare_ecc = read_ecc; u_char *erased_ecc_vec; + u_char *buf; + int bitflip_count; enum bch_ecc type; bool is_error_reported = false; @@ -1374,10 +1363,12 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, 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"); @@ -1389,10 +1380,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, if (info->nand.ecc.strength == BCH8_MAX_ERROR) { type = BCH8_ECC; - erased_ecc_vec = bch8_vector; } else { type = BCH4_ECC; - erased_ecc_vec = bch4_vector; } for (i = 0; i < eccsteps ; i++) { @@ -1410,44 +1399,36 @@ 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[actual_eccbytes]) >= 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, - actual_eccbytes)) { - 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; } } } -- cgit v1.2.3 From b08e1f632c79e9e75bf2ae58107df27d0e35b459 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Tue, 18 Mar 2014 18:56:45 +0530 Subject: mtd: nand: omap: ecc.correct: omap_elm_correct_data: cleanup for future enhancements Current omap_elm_correct_data() code is not scalable for future ecc-schemes due to presence of tweaks and hard-coded macros for BCH4_ECC and BCH8_ECC ecc-schemes at multiple places. This patch: - replaces 'ecc_opt' with '(info->nand.ecc.strength == BCH8_MAX_ERROR) used to differentiate between BCH8_HW and BCH4_SW - replaces macros (defining magic number for specific ecc-scheme) with generic variables - removes dependency on macros defined in elm.h (like BCHx_ECC_OOB_BYTES) Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 38 +++++++++++++------------------------- 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index a6c979f3c61f..39e3f50023d5 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 */ @@ -1356,8 +1351,8 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, u_char *erased_ecc_vec; u_char *buf; int bitflip_count; - enum bch_ecc type; bool is_error_reported = false; + u32 bit_pos, byte_pos, error_max, pos; switch (info->ecc_opt) { case OMAP_ECC_BCH4_CODE_HW: @@ -1378,12 +1373,6 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, /* Initialize elm error vector to zero */ memset(err_vec, 0, sizeof(err_vec)); - if (info->nand.ecc.strength == BCH8_MAX_ERROR) { - type = BCH8_ECC; - } else { - type = BCH4_ECC; - } - for (i = 0; i < eccsteps ; i++) { eccflag = 0; /* initialize eccflag */ @@ -1448,20 +1437,19 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, for (i = 0; i < eccsteps; i++) { 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; @@ -1483,7 +1471,7 @@ 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; + data += ecc->size; spare_ecc += ecc->bytes; } -- cgit v1.2.3 From 13fbe0641ef4132cac936ac352194d2fe9ddf824 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Tue, 18 Mar 2014 18:56:46 +0530 Subject: mtd: nand: omap: ecc.correct: omap_elm_correct_data: fix programmed-page bit-flip correction logic This patch updates following checks when bit-flips are detected by ELM: - Do not evaluate bit-flips when un-correctable bit-flips is reported by ELM, because as per [1] when ELM reports an un-correctable bit-flips, 'number of error' field in its ELM_LOCATION_STATUS register is also invalid. - Return with error-code '-EBADMSG' on detection of un-correctable bit-flip. - Return with error-code '-EBADMSG' when bit-flips position is outside current Sector and OOB area. [1] ELM IP spec Table-25 ELM_LOCATION_STATUS Register. ELM_LOCATION_STATUS[8] = ECC_CORRECTABLE: Error location process exit status 0x0: ECC error location process failed. Number of errors and error locations are invalid. 0x1: all errors were successfully located. Number of errors and error locations are valid. Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 39e3f50023d5..4f067f0f4e52 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1353,6 +1353,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, int bitflip_count; bool is_error_reported = false; u32 bit_pos, byte_pos, error_max, pos; + int err; switch (info->ecc_opt) { case OMAP_ECC_BCH4_CODE_HW: @@ -1434,8 +1435,12 @@ 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++) { switch (info->ecc_opt) { case OMAP_ECC_BCH4_CODE_HW: @@ -1457,13 +1462,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 */ } } @@ -1475,12 +1489,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, 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; } /** -- cgit v1.2.3 From a4c7ca004d984306c33df6d89c76ccd232317c21 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Wed, 26 Feb 2014 15:53:11 +0530 Subject: mtd: nand: omap: ecc.calculate: rename omap3_calculate_ecc_bch to omap_calculate_ecc_bch rename omap3_calculate_ecc_bch -> omap_calculate_ecc_bch to keep nomenclature independent of any device family. Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 4f067f0f4e52..4b98ee52e5e1 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1211,15 +1211,15 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, #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_code) { struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); @@ -1873,7 +1873,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 4; nand_chip->ecc.hwctl = omap3_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 */ @@ -1949,7 +1949,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 8; nand_chip->ecc.hwctl = omap3_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 */ -- cgit v1.2.3 From f5dc06fb71ae10c4f205a08f5ef26fd90fc122fc Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Wed, 26 Feb 2014 15:53:12 +0530 Subject: mtd: nand: omap: ecc.calculate: refactor omap_calculate_ecc_bch for BCHx_HW ecc-scheme OMAP NAND driver supports multiple flavours of BCH4 and BCH8 ECC algorithms. +------+------------------------------------+---------------+---------------+ | Algo | ECC scheme |ECC calculation|Error detection| +------+------------------------------------+---------------+---------------+ | |OMAP_ECC_BCH4_CODE_HW_DETECTION_SW |H/W (GPMC) |S/W | | BCH4 |OMAP_ECC_BCH4_CODE_HW |H/W (GPMC) |H/W (ELM) | +------+------------------------------------+---------------+---------------+ | |OMAP_ECC_BCH8_CODE_HW_DETECTION_SW |H/W (GPMC) |S/W | | BCH8 |OMAP_ECC_BCH8_CODE_HW |H/W (GPMC) |H/W (ELM) | +------+------------------------------------+---------------+---------------+ This patch refactors omap_calculate_ecc_bch() so that - separate out ecc-scheme specific code so that common-code can be reused between different implementations of same ECC algorithm. - new ecc-schemes can be added with ease in future. Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 68 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 4b98ee52e5e1..2ccd557bf77d 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1219,33 +1219,25 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, * Support calculating of BCH4/8 ecc vectors for the page */ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, - const u_char *dat, u_char *ecc_code) + 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: + 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); @@ -1259,14 +1251,10 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, *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: + 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) | @@ -1275,12 +1263,26 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, *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: + /* Set 8th ECC byte as 0x0 for ROM compatibility */ + ecc_calc[eccbytes - 1] = 0x0; + 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; -- cgit v1.2.3 From 2c9f2365d1e1d0e318b068f683f18c99515b80f8 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Wed, 26 Feb 2014 15:53:13 +0530 Subject: mtd: nand: omap: ecc.calculate: merge omap3_calculate_ecc_bch4 in omap_calculate_ecc_bch merges omap3_calculate_ecc_bch4() into omap_calculate_ecc_bch() so that common callback can be used for both OMAP_ECC_BCH4_CODE_HW and OMAP_ECC_BCH4_CODE_HW_DETECTION_SW ecc-schemes +---------------------+-------------------------------------------------------+ |ecc-scheme | nand_chip->calculate() after this patch | +---------------------+-------------------------------------------------------+ |HAM1_ECC | omap_calculate_ecc() | +---------------------+-------------------------------------------------------+ |BCH4_HW_DETECTION_SW | omap3_calculate_ecc_bch4() -> omap_calculate_ecc_bch()| |BCH4_HW | omap_calculate_ecc_bch() | |BCH8_HW_DETECTION_SW | omap3_calculate_ecc_bch8() | |BCH8_HW | omap_calculate_ecc_bch() | +---------------------+-------------------------------------------------------+ Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 51 ++++++++++-------------------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 2ccd557bf77d..e536267231e2 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1121,47 +1121,9 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); } #endif +static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; #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; -} - /** * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes * @mtd: MTD device structure @@ -1209,7 +1171,6 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, } #endif /* CONFIG_MTD_NAND_ECC_BCH */ -#ifdef CONFIG_MTD_NAND_OMAP_BCH /** * omap_calculate_ecc_bch - Generate bytes of ECC bytes * @mtd: MTD device structure @@ -1252,6 +1213,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, *ecc_code++ = ((bch_val1 >> 8) & 0xFF); *ecc_code++ = (bch_val1 & 0xFF); 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]); @@ -1270,6 +1232,12 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, /* 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; @@ -1327,6 +1295,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 @@ -1835,7 +1804,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 4; nand_chip->ecc.hwctl = omap3_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 / -- cgit v1.2.3 From 7bcd1dca1d587ad29f9825ba4414620440e8c8da Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Wed, 26 Feb 2014 15:53:14 +0530 Subject: mtd: nand: omap: ecc.calculate: merge omap3_calculate_ecc_bch8 in omap_calculate_ecc_bch merge omap3_calculate_ecc_bch8() into omap_calculate_ecc_bch() so that common callback can be used for both OMAP_ECC_BCH8_CODE_HW and OMAP_ECC_BCH8_CODE_HW_DETECTION_SW +---------------------+-------------------------------------------------------+ |ecc-scheme | nand_chip->calculate() after this patch | +---------------------+-------------------------------------------------------+ |HAM1_ECC | omap_calculate_ecc() | +---------------------+-------------------------------------------------------+ |BCH4_HW_DETECTION_SW | omap_calculate_ecc_bch() | |BCH4_HW | omap_calculate_ecc_bch() | |BCH8_HW_DETECTION_SW | omap3_calculate_ecc_bch8() -> omap_calculate_ecc_bch()| |BCH8_HW | omap_calculate_ecc_bch() | +---------------------+-------------------------------------------------------+ Tested-by: Stefan Roese Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 59 ++++++++---------------------------------------- 1 file changed, 10 insertions(+), 49 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index e536267231e2..fa8143624384 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1122,54 +1122,8 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) } #endif static u8 bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f}; - -#ifdef CONFIG_MTD_NAND_ECC_BCH -/** - * 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 */ +static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2, + 0x97, 0x79, 0xe5, 0x24, 0xb5}; /** * omap_calculate_ecc_bch - Generate bytes of ECC bytes @@ -1194,6 +1148,7 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, for (i = 0; i < nsectors; i++) { 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]); @@ -1242,6 +1197,12 @@ static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd, /* 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; @@ -1879,7 +1840,7 @@ static int omap_nand_probe(struct platform_device *pdev) nand_chip->ecc.strength = 8; nand_chip->ecc.hwctl = omap3_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 / -- cgit v1.2.3 From 7c977c3eb29837191f4154d819e552786bc42003 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Mon, 3 Mar 2014 15:38:30 +0530 Subject: mtd: nand: omap: ecc.hwctl: rename omap3_enable_hwecc_bch to omap_enable_hwecc_bch This patch - renames omap3_enable_hwecc_bch -> omap_enable_hwecc_bch to keep nomenclature independent of any device family. - using '__maybe_unused' instead of `ifdef based conditional compilation to suppress warning for un-used functions Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index fa8143624384..fad1d946c449 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1039,9 +1039,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 * @@ -1052,7 +1051,7 @@ 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 dev_width, nsectors; @@ -1120,7 +1119,7 @@ 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 + 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}; @@ -1763,7 +1762,7 @@ 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 = omap_calculate_ecc_bch; /* define ECC layout */ @@ -1803,7 +1802,7 @@ 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 = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; @@ -1838,7 +1837,7 @@ 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 = omap_calculate_ecc_bch; /* define ECC layout */ @@ -1879,7 +1878,7 @@ 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 = omap_calculate_ecc_bch; nand_chip->ecc.read_page = omap_read_page_bch; -- cgit v1.2.3 From c5957a320122bed3262da7a3a57d6a89aaeb4358 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Mon, 3 Mar 2014 15:38:31 +0530 Subject: mtd: nand: omap: ecc.hwctl: refactor omap_enable_hwecc_bch for ECC related GPMC configs Lots of if..then..else conditions in omap_enable_hwecc_bch() can be avoided if code is refactored based on ecc-scheme. Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 74 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index fad1d946c449..cabc4b67041c 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1057,20 +1057,11 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) 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; @@ -1079,23 +1070,58 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) /* Multi sector reading/writing for NAND flash with page size < 4096 */ if (info->is_elm_used && (mtd->writesize <= 4096)) { - 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; - } - /* Perform ecc calculation for one page (< 4096) */ nsectors = info->nand.ecc.steps; } + /* GPMC configurations for calculating ECC */ + switch (ecc_opt) { + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: + 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: + if (mode == NAND_ECC_READ) { + 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; + } + break; + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: + 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: + 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); -- cgit v1.2.3 From 16e69322c521c44e1c95c744fd1cca23549aa103 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Mon, 3 Mar 2014 15:38:32 +0530 Subject: mtd: nand: omap: ecc.hwctl: clean omap_enable_hwecc_bch for redundant ECC configs This patch - refactors GPMC configurations based on ecc-scheme - removed dependency on is_elm_present() flag, which is implicitely taken care by selecting appropriate ecc-scheme Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index cabc4b67041c..9962380dbe9a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1053,7 +1053,7 @@ static int omap_dev_ready(struct mtd_info *mtd) */ 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); @@ -1062,20 +1062,11 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) u32 val, wr_mode; unsigned int ecc_size1, ecc_size0; - /* 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)) { - /* Perform ecc calculation for one page (< 4096) */ - nsectors = info->nand.ecc.steps; - } /* 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; @@ -1087,6 +1078,8 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) } break; case OMAP_ECC_BCH4_CODE_HW: + bch_type = 0; + nsectors = chip->ecc.steps; if (mode == NAND_ECC_READ) { wr_mode = BCH_WRAPMODE_1; ecc_size0 = BCH4R_ECC_SIZE0; @@ -1098,6 +1091,8 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) } 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; @@ -1109,6 +1104,8 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode) } 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; @@ -1133,7 +1130,7 @@ static void __maybe_unused omap_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 */ -- cgit v1.2.3 From c7b05e97010ee134d586ec093c96f4a32d17e552 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Mon, 3 Mar 2014 15:38:33 +0530 Subject: mtd: nand: omap: remove is_elm_present flag 'is_elm_present' flag is not used anywhere. This check is implicitely taken care while selecting appropriate ecc-scheme via DT or board-file. Signed-off-by: Pekon Gupta Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 9962380dbe9a..ab9c472456d9 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -168,7 +168,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; }; @@ -1541,7 +1540,6 @@ 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; /* check whether elm-id is passed via DT */ if (!elm_node) { pr_err("nand: error: ELM DT node not found\n"); @@ -1557,7 +1555,6 @@ static int is_elm_present(struct omap_nand_info *info, info->elm_dev = &pdev->dev; if (elm_config(info->elm_dev, bch_type)) return -ENODEV; - info->is_elm_used = true; return 0; } #endif /* CONFIG_MTD_NAND_ECC_BCH */ -- cgit v1.2.3 From d90db4a074292ec07d09e54c70d62b3ab6f21591 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:33 +0000 Subject: mtd: st_spi_fsm: Allocate resources and register with MTD framework This is a new driver. It's used to communicate with a special type of optimised Serial Flash Controller called the FSM. The FSM uses a subset of the SPI protocol to communicate with supported NOR-Flash devices. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/Kconfig | 8 +++ drivers/mtd/devices/Makefile | 1 + drivers/mtd/devices/st_spi_fsm.c | 106 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 drivers/mtd/devices/st_spi_fsm.c 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/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c new file mode 100644 index 000000000000..b1e67406d367 --- /dev/null +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -0,0 +1,106 @@ +/* + * st_spi_fsm.c - ST Fast Sequence Mode (FSM) Serial Flash Controller + * + * Author: Angus Clark + * + * Copyright (C) 2010-2014 STicroelectronics 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 +#include +#include +#include +#include +#include +#include +#include + +struct stfsm { + struct device *dev; + void __iomem *base; + struct resource *region; + struct mtd_info mtd; + struct mutex lock; +}; + +static int stfsm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct resource *res; + struct stfsm *fsm; + + if (!np) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + + 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); + + 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; + + return mtd_device_parse_register(&fsm->mtd, NULL, NULL, NULL, 0); +} + +static int stfsm_remove(struct platform_device *pdev) +{ + struct stfsm *fsm = platform_get_drvdata(pdev); + int err; + + err = mtd_device_unregister(&fsm->mtd); + if (err) + return err; + + return 0; +} + +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 "); +MODULE_DESCRIPTION("ST SPI FSM driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From bc09fb5756b90df65b1776cf38d67bbb306b72fd Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:34 +0000 Subject: mtd: st_spi_fsm: Supply all register address and bit logic defines Here we provide the FSM's register addresses, register bit names/offsets and some commands which will prove useful as we start bulk the FMS's driver out with functionality. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 164 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index b1e67406d367..000bc25edd19 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -21,6 +21,170 @@ #include #include +/* + * 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) + +/* + * 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) + struct stfsm { struct device *dev; void __iomem *base; -- cgit v1.2.3 From 86f309fd8fb291039a3776593a0b2a9d86c895ab Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:35 +0000 Subject: mtd: st_spi_fsm: Initialise and configure the FSM for normal working conditions This patch uses default values to initialise a connected flash chip. This includes; a device soft reset, setting of a safe working frequency, a switch into Fast Sequencing Mode, configuring of timing data and a purge of the FIFO. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 127 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 000bc25edd19..7048ea75bf27 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -74,6 +74,10 @@ #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 */ @@ -185,19 +189,136 @@ #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 */ + struct stfsm { struct device *dev; void __iomem *base; struct resource *region; struct mtd_info mtd; struct mutex lock; + + uint32_t fifo_dir_delay; }; +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 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 int stfsm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct resource *res; struct stfsm *fsm; + int ret; if (!np) { dev_err(&pdev->dev, "No DT found\n"); @@ -227,6 +348,12 @@ static int stfsm_probe(struct platform_device *pdev) mutex_init(&fsm->lock); + ret = stfsm_init(fsm); + if (ret) { + dev_err(&pdev->dev, "Failed to initialise FSM Controller\n"); + return ret; + } + fsm->mtd.dev.parent = &pdev->dev; fsm->mtd.type = MTD_NORFLASH; fsm->mtd.writesize = 4; -- cgit v1.2.3 From 3c8b85b340c7a6b47d50c12c03661c23a5858b88 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:36 +0000 Subject: mtd: st_spi_fsm: Supply framework for device requests The FSM hardware works by setting a predetermined sequence of register writes. Rather than open coding them inside each functional block we're going to define them in a series of formatted 'sequence structures'. This patch provides the framework which shall be used for every action. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 7048ea75bf27..5e22c8688197 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -194,6 +194,8 @@ #define STFSM_FLASH_SAFE_FREQ 10000000UL /* 10 MHz */ +#define STFSM_MAX_WAIT_SEQ_MS 1000 /* FSM execution time */ + struct stfsm { struct device *dev; void __iomem *base; @@ -204,6 +206,24 @@ struct stfsm { uint32_t fifo_dir_delay; }; +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); + +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; @@ -225,6 +245,42 @@ static void stfsm_clear_fifo(struct stfsm *fsm) } } +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 int stfsm_set_mode(struct stfsm *fsm, uint32_t mode) { int ret, timeout = 10; -- cgit v1.2.3 From 030e82dc6386f23bcd05876b452d2618dd8046b4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:37 +0000 Subject: mtd: st_spi_fsm: Supply a method to read from the FSM's FIFO When invoked the driver will attempt to read any available data from the FSM's data register. Any data collected from this FIFO would have originated from the flash chip. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 5e22c8688197..73e0f2731bcb 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -281,6 +281,32 @@ static void stfsm_wait_seq(struct stfsm *fsm) dev_err(fsm->dev, "timeout on sequence completion\n"); } +static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, + const 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_set_mode(struct stfsm *fsm, uint32_t mode) { int ret, timeout = 10; -- cgit v1.2.3 From 1bd512b562dde92dae90036c4e6fd91f3870e44f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:38 +0000 Subject: mtd: st_spi_fsm: Add support for JEDEC ID extraction Once we start supporting devices it will be handy go detect them dynamically. This will be done using the chip's unique JEDEC ID. This patch allows us to extract a device's JEDEC ID using the a predefined FSM register write sequence. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 73e0f2731bcb..9fc0b1911fe1 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -219,6 +219,22 @@ struct stfsm_seq { uint32_t seq_cfg; } __packed __aligned(4); +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 inline int stfsm_is_idle(struct stfsm *fsm) { return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10; @@ -307,6 +323,42 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, } } +static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const 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) +{ + 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]); + + return NULL; +} + static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode) { int ret, timeout = 10; @@ -436,6 +488,9 @@ static int stfsm_probe(struct platform_device *pdev) return ret; } + /* Detect SPI FLASH device */ + stfsm_jedec_probe(fsm); + fsm->mtd.dev.parent = &pdev->dev; fsm->mtd.type = MTD_NORFLASH; fsm->mtd.writesize = 4; -- cgit v1.2.3 From 5549fbd514b6002073437ee543deb89904045078 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:39 +0000 Subject: mtd: devices: Provide header for shared OPCODEs and SFDP commands JEDEC have helped to standardise a great deal of the commands which can be issued to a Serial Flash devices. Many of the Serial Flash Discoverable Parameters (SFDP) commands are generic across devices. This patch provides a shared point where these commands can be defined. Suggested-by: Mark Brown Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/serial_flash_cmds.h | 81 +++++++++++++++++++++++++++++++++ drivers/mtd/devices/st_spi_fsm.c | 2 + 2 files changed, 83 insertions(+) create mode 100644 drivers/mtd/devices/serial_flash_cmds.h 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 + * + * 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/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 9fc0b1911fe1..1eea9494d6b7 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -21,6 +21,8 @@ #include #include +#include "serial_flash_cmds.h" + /* * FSM SPI Controller Registers */ -- cgit v1.2.3 From 11d7f826639baa25e7930d2b34c84c2d67f85ddc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:40 +0000 Subject: mtd: st_spi_fsm: Provide device look-up table Supply a lookup table of all the devices we intend to support. This table is used to store device information such as; a human readable device name, their JEDEC ID (plus the extended version), sector size and amount, a bit store of a device's capabilities, its maximum running frequency and possible use of a per-device configuration call-back. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 136 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 1eea9494d6b7..29da52284dfd 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -221,6 +221,142 @@ struct stfsm_seq { uint32_t seq_cfg; } __packed __aligned(4); +/* 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 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, NULL } + +#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, NULL }, + { "n25q256", 0x20ba19, 0, 64 * 1024, 512, + N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, NULL }, + + /* + * 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, + NULL }, + { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80, + NULL }, + + /* + * 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, + NULL }, + { "s25fl128s1", 0x012018, 0x0301, 64 * 1024, 256, S25FLXXXS_FLAG, 80, + NULL }, + { "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, + S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, NULL }, + { "s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512, + S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, NULL }, + + /* 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, NULL }, + { "w25q16", 0xef4015, 0, 64 * 1024, 32, W25Q_FLAG, 80, NULL }, + { "w25q32", 0xef4016, 0, 64 * 1024, 64, W25Q_FLAG, 80, NULL }, + { "w25q64", 0xef4017, 0, 64 * 1024, 128, W25Q_FLAG, 80, NULL }, + + /* Sentinel */ + { NULL, 0x000000, 0, 0, 0, 0, 0, NULL }, +}; + static struct stfsm_seq stfsm_seq_read_jedec = { .data_size = TRANSFER_SIZE(8), .seq_opc[0] = (SEQ_OPC_PADS_1 | -- cgit v1.2.3 From 24fec651d1cc396e6e24631386e0d5dd5f329fbc Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:41 +0000 Subject: mtd: st_spi_fsm: Dynamically setup flash device based on JEDEC ID Using previously added infrastructure we can now extract a device's JEDEC ID, compare it to a list of known and supported devices and make assumptions based on known characteristics of a given chip. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 29da52284dfd..9467585fcfda 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -204,6 +204,7 @@ struct stfsm { struct resource *region; struct mtd_info mtd; struct mutex lock; + struct flash_info *info; uint32_t fifo_dir_delay; }; @@ -477,6 +478,7 @@ static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec) static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm) { + struct flash_info *info; u16 ext_jedec; u32 jedec; u8 id[5]; @@ -494,6 +496,15 @@ static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm) 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; } @@ -588,6 +599,7 @@ static int stfsm_init(struct stfsm *fsm) static int stfsm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; + struct flash_info *info; struct resource *res; struct stfsm *fsm; int ret; @@ -627,13 +639,25 @@ static int stfsm_probe(struct platform_device *pdev) } /* Detect SPI FLASH device */ - stfsm_jedec_probe(fsm); + info = stfsm_jedec_probe(fsm); + if (!info) + return -ENODEV; + fsm->info = info; 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; + + dev_err(&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, NULL, NULL, 0); } -- cgit v1.2.3 From 089812740d42104d7b9eef803a34a3940d03bcd4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:42 +0000 Subject: mtd: st_spi_fsm: Search for preferred FSM message sequence configurations Here we provide a means to traverse though all supplied FSM message sequence configurations and pick one based on our chip's capabilities. The first one we match will be the preferred one, as they are presented in order of preference. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 9467585fcfda..2740ae195ef5 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -222,6 +222,18 @@ struct stfsm_seq { uint32_t seq_cfg; } __packed __aligned(4); +/* 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; @@ -462,6 +474,21 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, } } +/* 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; +} + static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; -- cgit v1.2.3 From 3b5d1981933b25973b02684dccb17d4282fd4d6d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:43 +0000 Subject: mtd: st_spi_fsm: Use device size to determine address width Take some known parameters, namely size and number of sectors and use them to determine weather a device can support 32bit addressing or not. If it can, set the associated flash capability flag for latter use. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 2740ae195ef5..5a1b470ce908 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -671,6 +671,10 @@ static int stfsm_probe(struct platform_device *pdev) 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; + fsm->mtd.dev.parent = &pdev->dev; fsm->mtd.type = MTD_NORFLASH; fsm->mtd.writesize = 4; -- cgit v1.2.3 From 97ccf2d253cd211d3922a30bde5d4adf9080fb8a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:44 +0000 Subject: mtd: st_spi_fsm: Prepare the read/write FSM message sequence(s) The FSM Serial Flash Controller is driven by issuing a standard set of register writes we call a message sequence. This patch supplies a method to prepare read/write FSM message sequence(s) based on chip capability and configuration. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 69 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 5a1b470ce908..83cf9dfd02e7 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -489,6 +489,75 @@ stfsm_search_seq_rw_configs(struct stfsm *fsm, 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 void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; -- cgit v1.2.3 From e209e1e8e330b55feaa16ed62836e3665026e406 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:45 +0000 Subject: mtd: st_spi_fsm: Add device-tree binding documentation Cc: devicetree@vger.kernel.org Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- Documentation/devicetree/bindings/mtd/st-fsm.txt | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/st-fsm.txt diff --git a/Documentation/devicetree/bindings/mtd/st-fsm.txt b/Documentation/devicetree/bindings/mtd/st-fsm.txt new file mode 100644 index 000000000000..c2489391c437 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/st-fsm.txt @@ -0,0 +1,26 @@ +* ST-Microelectronics SPI FSM Serial (NOR) Flash Controller + +Required properties: + - compatible : Should be "st,spi-fsm" + - reg : Contains register's location and length. + - reg-names : Should contain the reg names "spi-fsm" + - interrupts : The interrupt number + - pinctrl-0 : Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt) + +Optional properties: + - st,syscfg : Phandle to boot-device system configuration registers + - st,boot-device-reg : Address of the aforementioned boot-device register(s) + - st,boot-device-spi : Expected boot-device value if booted via this device + +Example: + spifsm: spifsm@fe902000{ + compatible = "st,spi-fsm"; + reg = <0xfe902000 0x1000>; + reg-names = "spi-fsm"; + pinctrl-0 = <&pinctrl_fsm>; + st,syscfg = <&syscfg_rear>; + st,boot-device-reg = <0x958>; + st,boot-device-spi = <0x1a>; + status = "okay"; + }; + -- cgit v1.2.3 From a63984c18a6186a323987817f01095d07503bda1 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:46 +0000 Subject: mtd: st_spi_fsm: Fetch boot-device from mode pins It's important for us to determine which device was used to boot from in order to make some correct decisions surrounding Power Management. On each of the platforms which support the FSM this is communicated via a set of mode pins held in the system configuration area. This patch determine the boot device and stores the result. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 83cf9dfd02e7..6be267277756 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -14,7 +14,9 @@ */ #include #include +#include #include +#include #include #include #include @@ -207,6 +209,7 @@ struct stfsm { struct flash_info *info; uint32_t fifo_dir_delay; + bool booted_from_spi; }; struct stfsm_seq { @@ -692,6 +695,47 @@ static int stfsm_init(struct stfsm *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; + + /* 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; @@ -734,6 +778,8 @@ static int stfsm_probe(struct platform_device *pdev) return ret; } + stfsm_fetch_platform_configs(pdev); + /* Detect SPI FLASH device */ info = stfsm_jedec_probe(fsm); if (!info) -- cgit v1.2.3 From fa5ba3af200db0b9c2922dc463ecdf122a93012b Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:47 +0000 Subject: mtd: st_spi_fsm: Provide the erase one sector sequence The FSM Serial Flash Controller is driven by issuing a standard set of register writes we call a message sequence. This patch supplies a method to prepare the message sequence responsible for erasing a single sector. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 6be267277756..06e6a5282044 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -389,6 +389,28 @@ static struct stfsm_seq stfsm_seq_read_jedec = { 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 inline int stfsm_is_idle(struct stfsm *fsm) { return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10; @@ -477,6 +499,19 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, } } +/* 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, -- cgit v1.2.3 From 6bd2960080bc5842159d89a8d476a9044225788c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:48 +0000 Subject: mtd: st_spi_fsm: Provide the sequence for enabling 32bit addressing mode The FSM Serial Flash Controller is driven by issuing a standard set of register writes we call a message sequence. This patch supplies a method to prepare the message sequence responsible for setting 32bit addressing mode on the Flash chip. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 06e6a5282044..9d1edf05701a 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -411,6 +411,28 @@ static struct stfsm_seq stfsm_seq_erase_sector = { 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; -- cgit v1.2.3 From 88cccb89117dfe258c5d8abb55db47e0a2a7daec Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:49 +0000 Subject: mtd: st_spi_fsm: Prepare read/write sequences according to configuration Firstly we search for our preference read/write configuration based on a given chip's capabilities. Then we actually set up the message sequence accordingly. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 9d1edf05701a..3e13d579aef8 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -618,6 +618,23 @@ static void stfsm_prepare_rw_seq(struct stfsm *fsm, 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; +} + static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; -- cgit v1.2.3 From 0ea7d70693431842b6b5c1808f59aa49e3fca6b1 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:50 +0000 Subject: mtd: st_spi_fsm: Add a check to if the chip can handle an SoC reset Based on information we can obtain though platform specific data and/or chip capabilities we are able to determine whether or not we can handle a SoC reset or not. To find out why this is important please read the comment provided in the patch. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 3e13d579aef8..7cc4425e7f1f 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -210,6 +210,8 @@ struct stfsm { uint32_t fifo_dir_delay; bool booted_from_spi; + bool reset_signal; + bool reset_por; }; struct stfsm_seq { @@ -521,6 +523,40 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, } } +/* + * 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) @@ -786,6 +822,10 @@ static void stfsm_fetch_platform_configs(struct platform_device *pdev) 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) -- cgit v1.2.3 From 0de08e43cdda9a2acbd3310491a7882ec43b24b3 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:51 +0000 Subject: mtd: st_spi_fsm: Provide a method to put the chip into 32bit addressing mode Most Serial Flash chips support 24bit addressing as a default but more recent incarnations can support 32bit. Based on information provided though platform specific data and capabilities we can determine whether or not our current chip can. This patch provides a means to setup the FSM message sequence to put the chip into 32bit mode. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 7cc4425e7f1f..0a5b70290b1b 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -375,6 +375,8 @@ static struct flash_info flash_types[] = { { NULL, 0x000000, 0, 0, 0, 0, 0, NULL }, }; +static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ + static struct stfsm_seq stfsm_seq_read_jedec = { .data_size = TRANSFER_SIZE(8), .seq_opc[0] = (SEQ_OPC_PADS_1 | @@ -523,6 +525,23 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, } } +static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) +{ + struct stfsm_seq *seq = &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; +} + /* * SoC reset on 'boot-from-spi' systems * -- cgit v1.2.3 From 249516c9c3c8665527db89ac871f55a07ecc89b2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:52 +0000 Subject: mtd: st_spi_fsm: Update the flash Volatile Configuration Register The FSM Serial Flash Controller is driven by issuing a standard set of register writes we call a message sequence. This patch supplies a method to prepare the message sequence responsible for updating a chip's VCR. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 0a5b70290b1b..5683443e7261 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -415,6 +415,23 @@ static struct stfsm_seq stfsm_seq_erase_sector = { 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) | @@ -542,6 +559,21 @@ static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) 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 * -- cgit v1.2.3 From a37b2f5aee630b35deccb4841a4d60fa45c15b3e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:53 +0000 Subject: mtd: st_spi_fsm: Provide the default read/write configurations Message sequences can vary depending on how many pads (lines) are required to address the chip (mode & dummy), how many data pads (lines) are required to write out to the chip which will determine speed amongst other things which are detailed by the SFDP specification. We are able to use multiple configurations for each chip, but they need to me matched to a device's capabilities. These configurations are listed in preference order - most preferred first. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 5683443e7261..03b49a44970d 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -375,6 +375,33 @@ static struct flash_info flash_types[] = { { 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}, +}; + static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ static struct stfsm_seq stfsm_seq_read_jedec = { -- cgit v1.2.3 From e85a619676a503d7622c17d38c2cc9f26b59f2be Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:54 +0000 Subject: mtd: st_spi_fsm: Supply the N25Qxxx specific read configurations The N25Qxxx Serial Flash devices required different sequence configurations depending on whether they're running in 24bit (3Byte) or 32bit (4Byte) mode. We provide those here. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 91 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 03b49a44970d..c902d5bc69a4 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -200,6 +200,53 @@ #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 + +/* + * 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 { struct device *dev; void __iomem *base; @@ -208,6 +255,7 @@ struct stfsm { struct mutex lock; struct flash_info *info; + uint32_t configuration; uint32_t fifo_dir_delay; bool booted_from_spi; bool reset_signal; @@ -402,6 +450,49 @@ static struct seq_rw_config default_write_configs[] = { {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}, +}; + static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ static struct stfsm_seq stfsm_seq_read_jedec = { -- cgit v1.2.3 From 218b870f906b609d37737ee176dc4fcba86163d4 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:55 +0000 Subject: mtd: st_spi_fsm: Supply the N25Qxxx chip specific configuration call-back In the FSM driver we handle chip differences by providing the possibility of calling back into a chip specific initialisation routine. In this patch we provide one for the N25Qxxx series, which endeavours to setup things like the read, write and erase sequences, as they differ from the default. We also configure 32bit support and the amount of dummy cycles to use. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 84 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index c902d5bc69a4..b4d2a188672e 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -312,6 +312,8 @@ struct flash_info { int (*config)(struct stfsm *); }; +static int stfsm_n25q_config(struct stfsm *fsm); + static struct flash_info flash_types[] = { /* * ST Microelectronics/Numonyx -- @@ -354,9 +356,10 @@ static struct flash_info flash_types[] = { 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, NULL }, + { "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, NULL }, + N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, stfsm_n25q_config }, /* * Spansion S25FLxxxP @@ -493,6 +496,8 @@ static struct seq_rw_config n25q_read4_configs[] = { {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; +static struct stfsm_seq stfsm_seq_read; /* Dynamically populated */ +static struct stfsm_seq stfsm_seq_write; /* Dynamically populated */ static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ static struct stfsm_seq stfsm_seq_read_jedec = { @@ -840,6 +845,71 @@ static int stfsm_search_prepare_rw_seq(struct stfsm *fsm, 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, &stfsm_seq_read, + n25q_read4_configs); + else + ret = stfsm_search_prepare_rw_seq(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, &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(&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_read_jedec(struct stfsm *fsm, uint8_t *const jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; @@ -1073,6 +1143,16 @@ static int stfsm_probe(struct platform_device *pdev) 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; + } + fsm->mtd.dev.parent = &pdev->dev; fsm->mtd.type = MTD_NORFLASH; fsm->mtd.writesize = 4; -- cgit v1.2.3 From 4eb3f0d8f70b667f8a59cc4f74003884562ef17f Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:56 +0000 Subject: mtd: st_spi_fsm: Prepare default sequences for read/write/erase Most chips require a predefined set of FSM message sequences for read, write and erase operations. This patch provides a way to set them up, which it will do so if a chip specific initialisation routine isn't been provided. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index b4d2a188672e..b4afee0e0e33 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -845,6 +845,38 @@ static int stfsm_search_prepare_rw_seq(struct stfsm *fsm, 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, &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, &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_n25q_config(struct stfsm *fsm) { uint32_t flags = fsm->info->flags; @@ -1151,6 +1183,10 @@ static int stfsm_probe(struct platform_device *pdev) ret = info->config(fsm); if (ret) return ret; + } else { + ret = stfsm_prepare_rwe_seqs_default(fsm); + if (ret) + return ret; } fsm->mtd.dev.parent = &pdev->dev; -- cgit v1.2.3 From e514f10578ede5b5b07213cfebb57a2907e13f65 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:57 +0000 Subject: mtd: st_spi_fsm: Add the ability to read from a Serial Flash device When a read is issued by userspace the MTD framework calls back into the driver to conduct the actual command issue and data extraction. Here we provide the routines which do exactly that. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index b4afee0e0e33..b1a65c1359d5 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -238,6 +238,9 @@ #define FLASH_CMD_READ4_1_1_4 0x6c #define FLASH_CMD_READ4_1_4_4 0xec +#define FLASH_PAGESIZE 256 /* In Bytes */ +#define FLASH_PAGESIZE_32 (FLASH_PAGESIZE / 4) /* In uint32_t */ + /* * Flags to tweak operation of default read/write/erase routines */ @@ -942,6 +945,99 @@ static int stfsm_n25q_config(struct stfsm *fsm) return 0; } +static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, + uint32_t offset) +{ + struct stfsm_seq *seq = &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; +} + +/* + * 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 void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; @@ -1197,6 +1293,8 @@ static int stfsm_probe(struct platform_device *pdev) fsm->mtd.size = info->sector_size * info->n_sectors; fsm->mtd.erasesize = info->sector_size; + fsm->mtd._read = stfsm_mtd_read; + dev_err(&pdev->dev, "Found serial flash device: %s\n" " size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n", -- cgit v1.2.3 From 30ca64f9f9db014755d632acffd0d2b507d7af6d Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:58 +0000 Subject: mtd: st_spi_fsm: Write to Flash via the FSM FIFO When we write data to the FIFO the FSM Controller subsequently writes that data out to the Serial Flash chip. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index b1a65c1359d5..58b8761dc30e 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -668,6 +668,20 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, } } +static int stfsm_write_fifo(struct stfsm *fsm, + const uint32_t *buf, const 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 = &stfsm_seq_en_32bit_addr; -- cgit v1.2.3 From 176b437762a310d5ac7ed3c015ece9dd65c7e240 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:20:59 +0000 Subject: mtd: st_spi_fsm: Supply a busy wait for post-write status When we write data to the Serial Flash chip we'll wait a predetermined period of time before giving up. During that period of time we poll the status register until completion. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 221 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 58b8761dc30e..99e08b01a32d 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -238,8 +238,18 @@ #define FLASH_CMD_READ4_1_1_4 0x6c #define FLASH_CMD_READ4_1_4_4 0xec +/* 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 + #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 @@ -519,6 +529,22 @@ static struct stfsm_seq stfsm_seq_read_jedec = { 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 = { @@ -699,6 +725,53 @@ static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) 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) { + cond_resched(); + + 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); + } + + dev_err(fsm->dev, "timeout on wait_busy\n"); + + return FLASH_STATUS_TIMEOUT; +} + static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data) { struct stfsm_seq *seq = &stfsm_seq_wrvcr; @@ -1020,6 +1093,98 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, return 0; } +static int stfsm_write(struct stfsm *fsm, const uint8_t *const buf, + const uint32_t size, const uint32_t offset) +{ + struct stfsm_seq *seq = &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); + + /* 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. @@ -1052,6 +1217,61 @@ static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, return 0; } +/* + * 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); + + *retlen = 0; + + if (!len) + return 0; + + if (to + len > mtd->size) + return -EINVAL; + + /* 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; +} + static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; @@ -1308,6 +1528,7 @@ static int stfsm_probe(struct platform_device *pdev) fsm->mtd.erasesize = info->sector_size; fsm->mtd._read = stfsm_mtd_read; + fsm->mtd._write = stfsm_mtd_write; dev_err(&pdev->dev, "Found serial flash device: %s\n" -- cgit v1.2.3 From 4a341fe758657850cbf6d2c36ee9029c0210e3c5 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:00 +0000 Subject: mtd: st_spi_fsm: Erase partly or as a whole a Serial Flash device When an erase is requested by userspace the MTD framework calls back into the driver to conduct the actual command issue. Here we provide the routines which do exactly that. We can choose to either do an entire chip erase or by sector. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 113 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 99e08b01a32d..34aac3f49ba9 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -567,6 +567,27 @@ static struct stfsm_seq stfsm_seq_erase_sector = { 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_wrvcr = { .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) | SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT), @@ -1217,6 +1238,47 @@ static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, return 0; } +static int stfsm_erase_sector(struct stfsm *fsm, const 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); + + /* 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 @@ -1272,6 +1334,54 @@ out1: 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 *const jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; @@ -1529,8 +1639,9 @@ static int stfsm_probe(struct platform_device *pdev) fsm->mtd._read = stfsm_mtd_read; fsm->mtd._write = stfsm_mtd_write; + fsm->mtd._erase = stfsm_mtd_erase; - dev_err(&pdev->dev, + dev_info(&pdev->dev, "Found serial flash device: %s\n" " size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n", info->name, -- cgit v1.2.3 From ac94dbcb6779d184f89bde0f8e2c7368d7179dd8 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:01 +0000 Subject: mtd: st_spi_fsm: Add the ability to read the FSM's status Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 34aac3f49ba9..5e315e31a839 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -793,6 +793,30 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm) 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_wrvcr(struct stfsm *fsm, uint8_t data) { struct stfsm_seq *seq = &stfsm_seq_wrvcr; -- cgit v1.2.3 From 150571b74ad92485b1443d0802dc740e194883f1 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:02 +0000 Subject: mtd: st_spi_fsm: Add the ability to write to FSM's status register Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 5e315e31a839..0eb2c09dd004 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -588,6 +588,23 @@ static struct stfsm_seq stfsm_seq_erase_chip = { 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), @@ -817,6 +834,25 @@ static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd, 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; -- cgit v1.2.3 From 898180665fd9e4cefbf33fdd410e7ce3fe6b75c0 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:03 +0000 Subject: mtd: st_spi_fsm: Supply the MX25xxx chip specific configuration call-back Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 82 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 0eb2c09dd004..89d5e0bcbbb3 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -326,6 +326,7 @@ struct flash_info { }; static int stfsm_n25q_config(struct stfsm *fsm); +static int stfsm_mx25_config(struct stfsm *fsm); static struct flash_info flash_types[] = { /* @@ -357,7 +358,8 @@ static struct flash_info flash_types[] = { FLASH_FLAG_SE_4K | \ FLASH_FLAG_SE_32K) { "mx25l25635e", 0xc22019, 0, 64*1024, 512, - (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, NULL } + (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70, + stfsm_mx25_config }, #define N25Q_FLAG (FLASH_FLAG_READ_WRITE | \ FLASH_FLAG_READ_FAST | \ @@ -509,6 +511,31 @@ static struct seq_rw_config n25q_read4_configs[] = { {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; +} + static struct stfsm_seq stfsm_seq_read; /* Dynamically populated */ static struct stfsm_seq stfsm_seq_write; /* Dynamically populated */ static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ @@ -1048,6 +1075,59 @@ static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm) 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(&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 = ((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; -- cgit v1.2.3 From 5343a1234513443edcb3fb55969f4ae415fc41a2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:04 +0000 Subject: mtd: st_spi_fsm: Supply the S25FLxxx chip specific configuration call-back This patch allows us to prepare some of the message sequences which will be required to talk to the S25FLxxx family of Serial Flash devices. It also allows us to do some required extra operations after any busy wait failures. Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 266 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 260 insertions(+), 6 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 89d5e0bcbbb3..9db564106e3e 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -238,6 +238,16 @@ #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 @@ -246,6 +256,9 @@ #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 */ @@ -327,6 +340,7 @@ struct flash_info { 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 struct flash_info flash_types[] = { /* @@ -388,9 +402,9 @@ static struct flash_info flash_types[] = { FLASH_FLAG_WRITE_1_1_4 | \ FLASH_FLAG_READ_FAST) { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, S25FLXXXP_FLAG, 80, - NULL }, + stfsm_s25fl_config }, { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, S25FLXXXP_FLAG, 80, - NULL }, + stfsm_s25fl_config }, /* * Spansion S25FLxxxS @@ -405,13 +419,13 @@ static struct flash_info flash_types[] = { FLASH_FLAG_RESET | \ FLASH_FLAG_DYB_LOCKING) { "s25fl128s0", 0x012018, 0x0300, 256 * 1024, 64, S25FLXXXS_FLAG, 80, - NULL }, + stfsm_s25fl_config }, { "s25fl128s1", 0x012018, 0x0301, 64 * 1024, 256, S25FLXXXS_FLAG, 80, - NULL }, + stfsm_s25fl_config }, { "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128, - S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, NULL }, + S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config }, { "s25fl256s1", 0x010219, 0x4d01, 64 * 1024, 512, - S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, NULL }, + 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 | \ @@ -536,6 +550,33 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) 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}, +}; + static struct stfsm_seq stfsm_seq_read; /* Dynamically populated */ static struct stfsm_seq stfsm_seq_write; /* Dynamically populated */ static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ @@ -1193,6 +1234,215 @@ static int stfsm_n25q_config(struct stfsm *fsm) 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, &stfsm_seq_read, + stfsm_s25fl_read4_configs); + if (ret) + return ret; + + ret = stfsm_search_prepare_rw_seq(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 = ((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_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, uint32_t offset) { @@ -1335,6 +1585,8 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *const buf, /* 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) { @@ -1398,6 +1650,8 @@ static int stfsm_erase_sector(struct stfsm *fsm, const uint32_t offset) /* 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) -- cgit v1.2.3 From cd7cac9ec3b691e6e0aca92f7c97e5cbb390d17c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:05 +0000 Subject: mtd: st_spi_fsm: Supply the W25Qxxx chip specific configuration call-back Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 47 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 9db564106e3e..dc9412a93abd 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -341,6 +341,7 @@ struct flash_info { 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[] = { /* @@ -446,10 +447,14 @@ static struct flash_info flash_types[] = { 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, NULL }, - { "w25q16", 0xef4015, 0, 64 * 1024, 32, W25Q_FLAG, 80, NULL }, - { "w25q32", 0xef4016, 0, 64 * 1024, 64, W25Q_FLAG, 80, NULL }, - { "w25q64", 0xef4017, 0, 64 * 1024, 128, W25Q_FLAG, 80, NULL }, + { "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 }, @@ -577,6 +582,11 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = { {0x00, 0, 0, 0, 0, 0x00, 0, 0}, }; +/* + * [W25Qxxx] Configuration + */ +#define W25Q_STATUS_QE (0x1 << 9) + static struct stfsm_seq stfsm_seq_read; /* Dynamically populated */ static struct stfsm_seq stfsm_seq_write; /* Dynamically populated */ static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ @@ -1443,6 +1453,35 @@ static int stfsm_s25fl_config(struct stfsm *fsm) 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 = ((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) { -- cgit v1.2.3 From e6b1bb4e18919b969cd876d48b40733993c2a7c7 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:06 +0000 Subject: mtd: st_spi_fsm: Move runtime configurable msg sequences into device's struct Until now the dynamically configurable message sequences for read, write and enable 32bit addressing have been global. Brian makes a good point why this should not be the case. If there are ever two FSM's located on the same platform, we could be potentially introducing a race condition on "needlessly shared data". Suggested-by: Brian Norris Acked-by Angus Clark Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 64 ++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index dc9412a93abd..dccbac013340 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -273,6 +273,19 @@ #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; @@ -286,20 +299,11 @@ struct stfsm { bool booted_from_spi; bool reset_signal; bool reset_por; -}; -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_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 { @@ -587,10 +591,6 @@ static struct seq_rw_config stfsm_s25fl_write4_configs[] = { */ #define W25Q_STATUS_QE (0x1 << 9) -static struct stfsm_seq stfsm_seq_read; /* Dynamically populated */ -static struct stfsm_seq stfsm_seq_write; /* Dynamically populated */ -static struct stfsm_seq stfsm_seq_en_32bit_addr;/* Dynamically populated */ - static struct stfsm_seq stfsm_seq_read_jedec = { .data_size = TRANSFER_SIZE(8), .seq_opc[0] = (SEQ_OPC_PADS_1 | @@ -826,7 +826,7 @@ static int stfsm_write_fifo(struct stfsm *fsm, static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter) { - struct stfsm_seq *seq = &stfsm_seq_en_32bit_addr; + 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 | @@ -1101,7 +1101,7 @@ static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm) int ret; /* Configure 'READ' sequence */ - ret = stfsm_search_prepare_rw_seq(fsm, &stfsm_seq_read, + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, default_read_configs); if (ret) { dev_err(fsm->dev, @@ -1111,7 +1111,7 @@ static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm) } /* Configure 'WRITE' sequence */ - ret = stfsm_search_prepare_rw_seq(fsm, &stfsm_seq_write, + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, default_write_configs); if (ret) { dev_err(fsm->dev, @@ -1146,7 +1146,7 @@ static int stfsm_mx25_config(struct stfsm *fsm) */ if (flags & FLASH_FLAG_32BIT_ADDR) { /* Configure 'enter_32bitaddr' FSM sequence */ - stfsm_mx25_en_32bit_addr_seq(&stfsm_seq_en_32bit_addr); + 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) { @@ -1169,7 +1169,7 @@ static int stfsm_mx25_config(struct stfsm *fsm) } /* For QUAD mode, set 'QE' STATUS bit */ - data_pads = ((stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; + 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; @@ -1188,10 +1188,10 @@ static int stfsm_n25q_config(struct stfsm *fsm) /* Configure 'READ' sequence */ if (flags & FLASH_FLAG_32BIT_ADDR) - ret = stfsm_search_prepare_rw_seq(fsm, &stfsm_seq_read, + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, n25q_read4_configs); else - ret = stfsm_search_prepare_rw_seq(fsm, &stfsm_seq_read, + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read, n25q_read3_configs); if (ret) { dev_err(fsm->dev, @@ -1201,7 +1201,7 @@ static int stfsm_n25q_config(struct stfsm *fsm) } /* Configure 'WRITE' sequence (default configs) */ - ret = stfsm_search_prepare_rw_seq(fsm, &stfsm_seq_write, + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, default_write_configs); if (ret) { dev_err(fsm->dev, @@ -1215,7 +1215,7 @@ static int stfsm_n25q_config(struct stfsm *fsm) /* Configure 32-bit address support */ if (flags & FLASH_FLAG_32BIT_ADDR) { - stfsm_n25q_en_32bit_addr_seq(&stfsm_seq_en_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) { @@ -1374,12 +1374,12 @@ static int stfsm_s25fl_config(struct stfsm *fsm) * Prepare Read/Write/Erase sequences according to S25FLxxx * 32-bit address command set */ - ret = stfsm_search_prepare_rw_seq(fsm, &stfsm_seq_read, + 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, &stfsm_seq_write, + ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write, stfsm_s25fl_write4_configs); if (ret) return ret; @@ -1415,7 +1415,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm) } /* Check status of 'QE' bit */ - data_pads = ((stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; + 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)) { @@ -1465,7 +1465,7 @@ static int stfsm_w25q_config(struct stfsm *fsm) return ret; /* If using QUAD mode, set QE STATUS bit */ - data_pads = ((stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1; + 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); @@ -1485,7 +1485,7 @@ static int stfsm_w25q_config(struct stfsm *fsm) static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, uint32_t offset) { - struct stfsm_seq *seq = &stfsm_seq_read; + struct stfsm_seq *seq = &fsm->stfsm_seq_read; uint32_t data_pads; uint32_t read_mask; uint32_t size_ub; @@ -1546,7 +1546,7 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, static int stfsm_write(struct stfsm *fsm, const uint8_t *const buf, const uint32_t size, const uint32_t offset) { - struct stfsm_seq *seq = &stfsm_seq_write; + struct stfsm_seq *seq = &fsm->stfsm_seq_write; uint32_t data_pads; uint32_t write_mask; uint32_t size_ub; -- cgit v1.2.3 From 221cff13cacc55b495bac33bf0f79abe59a6f265 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 09:21:07 +0000 Subject: mtd: st_spi_fsm: Convert ST SPI FSM (NOR) Flash driver to new DT partitions The old API expected a "partitions" property provided a phandle to a separate partitions node, which itself contained yet more nodes each representing one partition. The new API rids the requirement for the superfluous intermediary partitions node. This patch provides the added information required for automatic parsing by the core. Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index dccbac013340..aefd48db7ade 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1997,6 +1998,7 @@ boot_device_fail: 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; @@ -2006,6 +2008,7 @@ static int stfsm_probe(struct platform_device *pdev) 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) @@ -2062,6 +2065,7 @@ static int stfsm_probe(struct platform_device *pdev) return ret; } + fsm->mtd.name = info->name; fsm->mtd.dev.parent = &pdev->dev; fsm->mtd.type = MTD_NORFLASH; fsm->mtd.writesize = 4; @@ -2081,7 +2085,7 @@ static int stfsm_probe(struct platform_device *pdev) (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, NULL, NULL, 0); + return mtd_device_parse_register(&fsm->mtd, NULL, &ppdata, NULL, 0); } static int stfsm_remove(struct platform_device *pdev) -- cgit v1.2.3 From 3f9d720a4d29834b8362d820537acb042c17e33c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 11:11:43 +0000 Subject: mtd: st_spi_fsm: Remove useless consts from function arguments Reported-by: Brian Norris Signed-off-by: Lee Jones [Brian: tweaked a bit] Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index aefd48db7ade..d1fb1e55aff7 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -785,8 +785,7 @@ static void stfsm_wait_seq(struct stfsm *fsm) dev_err(fsm->dev, "timeout on sequence completion\n"); } -static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, - const uint32_t size) +static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size) { uint32_t remaining = size >> 2; uint32_t avail; @@ -811,8 +810,8 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, } } -static int stfsm_write_fifo(struct stfsm *fsm, - const uint32_t *buf, const uint32_t size) +static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf, + uint32_t size) { uint32_t words = size >> 2; @@ -1544,8 +1543,8 @@ static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size, return 0; } -static int stfsm_write(struct stfsm *fsm, const uint8_t *const buf, - const uint32_t size, const uint32_t offset) +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; @@ -1670,7 +1669,7 @@ static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, return 0; } -static int stfsm_erase_sector(struct stfsm *fsm, const uint32_t offset) +static int stfsm_erase_sector(struct stfsm *fsm, uint32_t offset) { struct stfsm_seq *seq = &stfsm_seq_erase_sector; int ret; @@ -1816,7 +1815,7 @@ out1: return ret; } -static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *const jedec) +static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *jedec) { const struct stfsm_seq *seq = &stfsm_seq_read_jedec; uint32_t tmp[2]; -- cgit v1.2.3 From 0419a203f865603654953f407cf062309330f37e Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 11:11:44 +0000 Subject: mtd: st_spi_fsm: Avoid duplicating MTD core code Reported-by: Brian Norris Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index d1fb1e55aff7..80088e4880aa 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -1729,14 +1729,6 @@ static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len); - *retlen = 0; - - if (!len) - return 0; - - if (to + len > mtd->size) - return -EINVAL; - /* Offset within page */ page_offs = to % FLASH_PAGESIZE; -- cgit v1.2.3 From f1919cb852c8f9e7fa667a446f253258a8a599f2 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 11:11:45 +0000 Subject: mtd: st_spi_fsm: Correct vendor name spelling issue - missing "M" Reported-by: Brian Norris Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 80088e4880aa..ef9f9b84dbfb 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -3,7 +3,7 @@ * * Author: Angus Clark * - * Copyright (C) 2010-2014 STicroelectronics Limited + * Copyright (C) 2010-2014 STMicroelectronics Limited * * JEDEC probe based on drivers/mtd/devices/m25p80.c * -- cgit v1.2.3 From ea7864bf44e1638c34b9eef95477a72ae68fbdc6 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 11:11:46 +0000 Subject: mtd: st_spi_fsm: Allow loop to run at least once before giving up CPU Reported-by: Brian Norris Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index ef9f9b84dbfb..4d6b58c976c7 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -861,8 +861,6 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm) */ deadline = jiffies + FLASH_MAX_BUSY_WAIT; while (!timeout) { - cond_resched(); - if (time_after_eq(jiffies, deadline)) timeout = 1; @@ -881,6 +879,8 @@ static uint8_t stfsm_wait_busy(struct stfsm *fsm) if (!timeout) /* Restart */ writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG); + + cond_resched(); } dev_err(fsm->dev, "timeout on wait_busy\n"); -- cgit v1.2.3 From d9ba46d32c0edc5207fec424230dfa786a24a117 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 20 Mar 2014 11:11:47 +0000 Subject: mtd: st_spi_fsm: Succinctly reorganise .remove() Reported-by: Brian Norris Signed-off-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/devices/st_spi_fsm.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c index 4d6b58c976c7..1957d7c8e185 100644 --- a/drivers/mtd/devices/st_spi_fsm.c +++ b/drivers/mtd/devices/st_spi_fsm.c @@ -2082,13 +2082,8 @@ static int stfsm_probe(struct platform_device *pdev) static int stfsm_remove(struct platform_device *pdev) { struct stfsm *fsm = platform_get_drvdata(pdev); - int err; - err = mtd_device_unregister(&fsm->mtd); - if (err) - return err; - - return 0; + return mtd_device_unregister(&fsm->mtd); } static struct of_device_id stfsm_match[] = { -- cgit v1.2.3 From 3f4eb14bdbe148fcc3a8e02f506ccc9b8c955ad4 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Thu, 20 Mar 2014 18:48:34 +0530 Subject: mtd: devices: elm: check for hardware engine's design constraints ELM hardware engine is used by BCH ecc-schemes for detecting and locating ECC errors. This patch adds the following checks for ELM hardware engine: - ELM internal buffers are of 1K, so it cannot process data with ecc-step-size > 1K. - ELM engine can execute upto maximum of 8 threads in parallel, so in *page-mode* (when complete page is processed in single iteration), ELM cannot support ecc-steps > 8. Signed-off-by: Pekon Gupta Reviewed-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/devices/elm.c | 13 ++++++++++++- drivers/mtd/nand/omap2.c | 9 ++++++--- include/linux/platform_data/elm.h | 3 ++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index f160d2c6cd59..c51752ad072e 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -103,7 +103,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,6 +113,16 @@ 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); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ab9c472456d9..6f9b339aed74 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1540,6 +1540,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; + 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"); @@ -1553,9 +1555,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; - 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 */ diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h index bf0a83b7ed9d..6e37156b0902 100644 --- a/include/linux/platform_data/elm.h +++ b/include/linux/platform_data/elm.h @@ -50,5 +50,6 @@ struct elm_errorvec { void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc, struct elm_errorvec *err_vec); -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); #endif /* __ELM_H */ -- cgit v1.2.3 From ea0760244d235688b5fae4e5cdd9412c1fb1c2fe Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Thu, 20 Mar 2014 18:48:35 +0530 Subject: mtd: devices: elm: clean elm_load_syndrome This patch refactors elm_load_syndrome() to make it scalable for newer ECC schemes by removing scheme specific macros (like ECC_BYTES*xx), and instead using ECC control information passed during elm_config. Signed-off-by: Pekon Gupta Reviewed-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/devices/elm.c | 18 +++++++++++------- include/linux/platform_data/elm.h | 7 ------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index c51752ad072e..4fbfaf65fabd 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -84,6 +84,7 @@ struct elm_info { struct list_head list; enum bch_ecc bch_type; struct elm_registers elm_regs; + int ecc_syndrome_size; }; static LIST_HEAD(elm_devices); @@ -126,7 +127,8 @@ int elm_config(struct device *dev, enum bch_ecc bch_type, 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_syndrome_size = ecc_syndrome_size; return 0; } @@ -175,10 +177,8 @@ static void elm_load_syndrome(struct elm_info *info, 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); @@ -197,7 +197,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); @@ -207,11 +208,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; } } diff --git a/include/linux/platform_data/elm.h b/include/linux/platform_data/elm.h index 6e37156b0902..4edb40676b3f 100644 --- a/include/linux/platform_data/elm.h +++ b/include/linux/platform_data/elm.h @@ -26,13 +26,6 @@ enum bch_ecc { /* ELM support 8 error syndrome process */ #define ERROR_VECTOR_MAX 8 -#define BCH8_ECC_OOB_BYTES 13 -#define BCH4_ECC_OOB_BYTES 7 -/* RBL requires 14 byte even though BCH8 uses only 13 byte */ -#define BCH8_SIZE (BCH8_ECC_OOB_BYTES + 1) -/* Uses 1 extra byte to handle erased pages */ -#define BCH4_SIZE (BCH4_ECC_OOB_BYTES + 1) - /** * struct elm_errorvec - error vector for elm * @error_reported: set true for vectors error is reported -- cgit v1.2.3 From 79f013518d435e238e6bddd0073de631bbefb556 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Thu, 20 Mar 2014 18:48:36 +0530 Subject: mtd: devices: elm: configure parallel channels based on ecc_steps ELM hardware can process up to maximum of 8 hannels in parallel for ECC error detection. Currently the number of channels getting configured for processing is static determined by macro ERROR_VECTOR_MAX. However, the actual number of channels that need to be processed is the ECC step number. This patch just avoids configuring extra unused channels. Signed-off-by: Pekon Gupta Reviewed-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/devices/elm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index 4fbfaf65fabd..26df41f20c1f 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -84,6 +84,7 @@ struct elm_info { struct list_head list; enum bch_ecc bch_type; struct elm_registers elm_regs; + int ecc_steps; int ecc_syndrome_size; }; @@ -128,6 +129,7 @@ int elm_config(struct device *dev, enum bch_ecc bch_type, 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->ecc_steps = ecc_steps; info->ecc_syndrome_size = ecc_syndrome_size; return 0; @@ -170,7 +172,7 @@ 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) { @@ -238,7 +240,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; @@ -267,7 +269,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) { -- cgit v1.2.3 From c7ea95a377921efd0f0fa3e64852f5cc3d7ffc47 Mon Sep 17 00:00:00 2001 From: Pekon Gupta Date: Thu, 20 Mar 2014 18:48:37 +0530 Subject: mtd: devices: elm: update DRIVER_NAME as "omap-elm" use "omap-elm" as DRIVER_NAME Signed-off-by: Pekon Gupta Reviewed-by: Ezequiel Garcia Signed-off-by: Brian Norris --- drivers/mtd/devices/elm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index 26df41f20c1f..1fd4a0f77967 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -15,6 +15,8 @@ * */ +#define DRIVER_NAME "omap-elm" + #include #include #include @@ -520,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, -- cgit v1.2.3 From 7995204e3c89820c4621af90b0e5f9ca0b375471 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 12 Feb 2014 11:29:42 +0900 Subject: mtd: denali_dt: Use devm_ioremap_resource() Use devm_ioremap_resource() in order to make the code simpler, and remove redundant return value check of platform_get_resource_byname() because the value is checked by devm_ioremap_resource(). Signed-off-by: Jingoo Han Tested-by: Dinh Nguyen Signed-off-by: Brian Norris --- drivers/mtd/nand/denali_dt.c | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-) 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)) { -- cgit v1.2.3 From 00d09891eb947f8c4a7cb4d16bf3de305a65de77 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 12 Feb 2014 11:34:37 +0900 Subject: mtd: omap2: Use devm_ioremap_resource() Use devm_ioremap_resource() in order to make the code simpler, and remove redundant return value check of platform_get_resource() because the value is checked by devm_ioremap_resource(). Also, 'unsigned long mem_size' is removed from 'struct omap_nand_info', because the 'mem_size' variable is not necessary anymore. Signed-off-by: Jingoo Han Signed-off-by: Brian Norris --- drivers/mtd/nand/omap2.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 6f9b339aed74..1ff49b80bdaf 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -154,7 +154,6 @@ 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; @@ -1607,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; -- cgit v1.2.3 From e34fcb07a6d57411de6e15a47724fbe92c5caa42 Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Fri, 21 Mar 2014 16:05:10 -0600 Subject: mtd: nand: fix GET/SET_FEATURES address on 16-bit devices GET_FEATURES and SET_FEATURES also need byte-addressing on 16-bit devices. Signed-off-by: David Mosberger Signed-off-by: Brian Norris --- include/linux/mtd/nand.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0747fef2bfc6..450d61ec7f06 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -925,7 +925,16 @@ static inline bool nand_is_slc(struct nand_chip *chip) */ static inline int nand_opcode_8bits(unsigned int command) { - return command == NAND_CMD_READID || command == NAND_CMD_PARAM; + switch (command) { + case NAND_CMD_READID: + case NAND_CMD_PARAM: + case NAND_CMD_GET_FEATURES: + case NAND_CMD_SET_FEATURES: + return 1; + default: + break; + } + return 0; } /* return the supported JEDEC features. */ -- cgit v1.2.3 From 148256fa0bb8e05a00db3f204b36e19e6312102c Mon Sep 17 00:00:00 2001 From: Erico Nunes Date: Tue, 11 Mar 2014 01:31:26 -0300 Subject: mtd: nand: fix mention to CONFIG_MTD_NAND_ECC_BCH Mention to CONFIG_MTD_ECC_BCH in the warning message can be confusing as this doesn't match the exact name of the configuration option. This warning showed up once to me when I was starting to set up BCH. After checking my .config file, it took a moment before realizing it is CONFIG_MTD_NAND_ECC_BCH instead of CONFIG_MTD_ECC_BCH. Signed-off-by: Erico Nunes Acked-by: Lee Jones Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5826da39216e..01946b7c3a8d 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3925,7 +3925,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; -- cgit v1.2.3 From 86e4bbc766b9456f583f2fc3c4f6c623b422af88 Mon Sep 17 00:00:00 2001 From: Sasha Levin Date: Wed, 19 Mar 2014 18:24:37 -0400 Subject: mtd: diskonchip: mem resource name is not optional Passing a name to request_mem_region() isn't optional and can't just be NULL. Passing NULL causes a NULL ptr deref later in the boot process. Signed-off-by: Sasha Levin Tested-by: Alexander Shiyan Cc: # 3.14 Signed-off-by: Brian Norris --- drivers/mtd/nand/diskonchip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index b9b4db607850..f68a7bccecdc 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1439,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) { -- cgit v1.2.3 From 4a4163caccae97a23d97c29032664ee7b7a498d0 Mon Sep 17 00:00:00 2001 From: Ron Date: Sun, 16 Mar 2014 04:01:07 +1030 Subject: Fix index regression in nand_read_subpage Commit 7351d3a5dbf42ba3299af71db3296be447bc1516 added an index variable as part of fixing checkpatch warnings, presumably as a tool to make some long lines shorter, however it only set that index in the case of there being no gaps in eccpos for the fragment being read. Which means the later step of filling ecccode from oob_poi will use the wrong indexing into eccpos in that case. This patch restores the behaviour that existed prior to that change. Signed-off-by: Ron Lee Signed-off-by: Brian Norris --- drivers/mtd/nand/nand_base.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 01946b7c3a8d..9d01c4df838c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1174,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; @@ -1217,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)) -- cgit v1.2.3