diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-09 21:33:19 +0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-09 21:33:19 +0400 |
commit | ef9a61bef917e38f8e096f6df303329aed6cf467 (patch) | |
tree | 31cfe2444d0270e77ff8ef792df11591fed6075c /drivers/mtd/devices | |
parent | b5f0998cae3d7ea56d3d8377e46328fe972b9546 (diff) | |
parent | 6c3b88970175e18a67eb8e55c4eba10614d0d5dc (diff) | |
download | linux-ef9a61bef917e38f8e096f6df303329aed6cf467.tar.xz |
Merge tag 'for-linus-20130909' of git://git.infradead.org/linux-mtd
Pull mtd updates from David Woodhouse:
- factor out common code from MTD tests
- nand-gpio cleanup and portability to non-ARM
- m25p80 support for 4-byte addressing chips, other new chips
- pxa3xx cleanup and support for new platforms
- remove obsolete alauda, octagon-5066 drivers
- erase/write support for bcm47xxsflash
- improve detection of ECC requirements for NAND, controller setup
- NFC acceleration support for atmel-nand, read/write via SRAM
- etc
* tag 'for-linus-20130909' of git://git.infradead.org/linux-mtd: (184 commits)
mtd: chips: Add support for PMC SPI Flash chips in m25p80.c
mtd: ofpart: use for_each_child_of_node() macro
mtd: mtdswap: replace strict_strtoul() with kstrtoul()
mtd cs553x_nand: use kzalloc() instead of memset
mtd: atmel_nand: fix error return code in atmel_nand_probe()
mtd: bcm47xxsflash: writing support
mtd: bcm47xxsflash: implement erasing support
mtd: bcm47xxsflash: convert to module_platform_driver instead of init/exit
mtd: bcm47xxsflash: convert kzalloc to avoid invalid access
mtd: remove alauda driver
mtd: nand: mxc_nand: mark 'const' properly
mtd: maps: cfi_flagadm: add missing __iomem annotation
mtd: spear_smi: add missing __iomem annotation
mtd: r852: Staticize local symbols
mtd: nandsim: Staticize local symbols
mtd: impa7: add missing __iomem annotation
mtd: sm_ftl: Staticize local symbols
mtd: m25p80: add support for mr25h10
mtd: m25p80: make CONFIG_M25PXX_USE_FAST_READ safe to enable
mtd: m25p80: Pass flags through CAT25_INFO macro
...
Diffstat (limited to 'drivers/mtd/devices')
-rw-r--r-- | drivers/mtd/devices/Kconfig | 55 | ||||
-rw-r--r-- | drivers/mtd/devices/bcm47xxsflash.c | 275 | ||||
-rw-r--r-- | drivers/mtd/devices/bcm47xxsflash.h | 2 | ||||
-rw-r--r-- | drivers/mtd/devices/block2mtd.c | 58 | ||||
-rw-r--r-- | drivers/mtd/devices/elm.c | 129 | ||||
-rw-r--r-- | drivers/mtd/devices/m25p80.c | 81 | ||||
-rw-r--r-- | drivers/mtd/devices/mtd_dataflash.c | 10 | ||||
-rw-r--r-- | drivers/mtd/devices/spear_smi.c | 19 | ||||
-rw-r--r-- | drivers/mtd/devices/sst25l.c | 8 |
9 files changed, 468 insertions, 169 deletions
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 2a4d55e4b362..74ab4b7e523e 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -224,59 +224,4 @@ config BCH_CONST_T default 4 endif -config MTD_DOCPROBE - tristate - select MTD_DOCECC - -config MTD_DOCECC - tristate - -config MTD_DOCPROBE_ADVANCED - bool "Advanced detection options for DiskOnChip" - depends on MTD_DOCPROBE - help - This option allows you to specify nonstandard address at which to - probe for a DiskOnChip, or to change the detection options. You - are unlikely to need any of this unless you are using LinuxBIOS. - Say 'N'. - -config MTD_DOCPROBE_ADDRESS - hex "Physical address of DiskOnChip" if MTD_DOCPROBE_ADVANCED - depends on MTD_DOCPROBE - default "0x0" - ---help--- - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option allows you to specify a single address at which to probe - for the device, which is useful if you have other devices in that - range which get upset when they are probed. - - (Note that on PowerPC, the normal probe will only check at - 0xE4000000.) - - Normally, you should leave this set to zero, to allow the probe at - the normal addresses. - -config MTD_DOCPROBE_HIGH - bool "Probe high addresses" - depends on MTD_DOCPROBE_ADVANCED - help - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option changes to make it probe between 0xFFFC8000 and - 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be - useful to you. Say 'N'. - -config MTD_DOCPROBE_55AA - bool "Probe for 0x55 0xAA BIOS Extension Signature" - depends on MTD_DOCPROBE_ADVANCED - help - Check for the 0x55 0xAA signature of a DiskOnChip, and do not - continue with probing if it is absent. The signature will always be - present for a DiskOnChip 2000 or a normal DiskOnChip Millennium. - Only if you have overwritten the first block of a DiskOnChip - Millennium will it be absent. Enable this option if you are using - LinuxBIOS or if you need to recover a DiskOnChip Millennium on which - you have managed to wipe the first block. - endmenu diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 18e7761137a3..77de29bc02ba 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -1,6 +1,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/mtd/mtd.h> #include <linux/platform_device.h> #include <linux/bcma/bcma.h> @@ -12,6 +13,93 @@ MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); static const char * const probes[] = { "bcm47xxpart", NULL }; +/************************************************** + * Various helpers + **************************************************/ + +static void bcm47xxsflash_cmd(struct bcm47xxsflash *b47s, u32 opcode) +{ + int i; + + b47s->cc_write(b47s, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode); + for (i = 0; i < 1000; i++) { + if (!(b47s->cc_read(b47s, BCMA_CC_FLASHCTL) & + BCMA_CC_FLASHCTL_BUSY)) + return; + cpu_relax(); + } + pr_err("Control command failed (timeout)!\n"); +} + +static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout) +{ + unsigned long deadline = jiffies + timeout; + + do { + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + bcm47xxsflash_cmd(b47s, OPCODE_ST_RDSR); + if (!(b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & + SR_ST_WIP)) + return 0; + break; + case BCM47XXSFLASH_TYPE_ATMEL: + bcm47xxsflash_cmd(b47s, OPCODE_AT_STATUS); + if (b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & + SR_AT_READY) + return 0; + break; + } + + cpu_relax(); + udelay(1); + } while (!time_after_eq(jiffies, deadline)); + + pr_err("Timeout waiting for flash to be ready!\n"); + + return -EBUSY; +} + +/************************************************** + * MTD ops + **************************************************/ + +static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int err; + + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr); + /* Newer flashes have "sub-sectors" which can be erased + * independently with a new command: ST_SSE. The ST_SE command + * erases 64KB just as before. + */ + if (b47s->blocksize < (64 * 1024)) + bcm47xxsflash_cmd(b47s, OPCODE_ST_SSE); + else + bcm47xxsflash_cmd(b47s, OPCODE_ST_SE); + break; + case BCM47XXSFLASH_TYPE_ATMEL: + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr << 1); + bcm47xxsflash_cmd(b47s, OPCODE_AT_PAGE_ERASE); + break; + } + + err = bcm47xxsflash_poll(b47s, HZ); + if (err) + erase->state = MTD_ERASE_FAILED; + else + erase->state = MTD_ERASE_DONE; + + if (erase->callback) + erase->callback(erase); + + return err; +} + static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -28,6 +116,127 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, return len; } +static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len, + const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int written = 0; + + /* Enable writes */ + bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); + + /* Write first byte */ + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, offset); + b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); + + /* Program page */ + if (b47s->bcma_cc->core->id.rev < 20) { + bcm47xxsflash_cmd(b47s, OPCODE_ST_PP); + return 1; /* 1B written */ + } + + /* Program page and set CSA (on newer chips we can continue writing) */ + bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | OPCODE_ST_PP); + offset++; + len--; + written++; + + while (len > 0) { + /* Page boundary, another function call is needed */ + if ((offset & 0xFF) == 0) + break; + + bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | *buf++); + offset++; + len--; + written++; + } + + /* All done, drop CSA & poll */ + b47s->cc_write(b47s, BCMA_CC_FLASHCTL, 0); + udelay(1); + if (bcm47xxsflash_poll(b47s, HZ / 10)) + pr_err("Flash rejected dropping CSA\n"); + + return written; +} + +static int bcm47xxsflash_write_at(struct mtd_info *mtd, u32 offset, size_t len, + const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + u32 mask = b47s->blocksize - 1; + u32 page = (offset & ~mask) << 1; + u32 byte = offset & mask; + int written = 0; + + /* If we don't overwrite whole page, read it to the buffer first */ + if (byte || (len < b47s->blocksize)) { + int err; + + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_LOAD); + /* 250 us for AT45DB321B */ + err = bcm47xxsflash_poll(b47s, HZ / 1000); + if (err) { + pr_err("Timeout reading page 0x%X info buffer\n", page); + return err; + } + } + + /* Change buffer content with our data */ + while (len > 0) { + /* Page boundary, another function call is needed */ + if (byte == b47s->blocksize) + break; + + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, byte++); + b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_WRITE); + len--; + written++; + } + + /* Program page with the buffer content */ + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_PROGRAM); + + return written; +} + +static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int written; + + /* Writing functions can return without writing all passed data, for + * example when the hardware is too old or when we git page boundary. + */ + while (len > 0) { + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + written = bcm47xxsflash_write_st(mtd, to, len, buf); + break; + case BCM47XXSFLASH_TYPE_ATMEL: + written = bcm47xxsflash_write_at(mtd, to, len, buf); + break; + default: + BUG_ON(1); + } + if (written < 0) { + pr_err("Error writing at offset 0x%llX\n", to); + return written; + } + to += (loff_t)written; + len -= written; + *retlen += written; + buf += written; + } + + return 0; +} + static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) { struct mtd_info *mtd = &b47s->mtd; @@ -35,33 +244,48 @@ static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) mtd->priv = b47s; mtd->name = "bcm47xxsflash"; mtd->owner = THIS_MODULE; - mtd->type = MTD_ROM; + + mtd->type = MTD_NORFLASH; + mtd->flags = MTD_CAP_NORFLASH; mtd->size = b47s->size; - mtd->_read = bcm47xxsflash_read; + mtd->erasesize = b47s->blocksize; + mtd->writesize = 1; + mtd->writebufsize = 1; - /* TODO: implement writing support and verify/change following code */ - mtd->flags = MTD_CAP_ROM; - mtd->writebufsize = mtd->writesize = 1; + mtd->_erase = bcm47xxsflash_erase; + mtd->_read = bcm47xxsflash_read; + mtd->_write = bcm47xxsflash_write; } /************************************************** * BCMA **************************************************/ +static int bcm47xxsflash_bcma_cc_read(struct bcm47xxsflash *b47s, u16 offset) +{ + return bcma_cc_read32(b47s->bcma_cc, offset); +} + +static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset, + u32 value) +{ + bcma_cc_write32(b47s->bcma_cc, offset, value); +} + static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) { struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); struct bcm47xxsflash *b47s; int err; - b47s = kzalloc(sizeof(*b47s), GFP_KERNEL); - if (!b47s) { - err = -ENOMEM; - goto out; - } + b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL); + if (!b47s) + return -ENOMEM; sflash->priv = b47s; b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); + b47s->cc_read = bcm47xxsflash_bcma_cc_read; + b47s->cc_write = bcm47xxsflash_bcma_cc_write; switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: @@ -81,15 +305,13 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); if (err) { pr_err("Failed to register MTD device: %d\n", err); - goto err_dev_reg; + return err; } - return 0; + if (bcm47xxsflash_poll(b47s, HZ / 10)) + pr_warn("Serial flash busy\n"); -err_dev_reg: - kfree(&b47s->mtd); -out: - return err; + return 0; } static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) @@ -98,7 +320,6 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) struct bcm47xxsflash *b47s = sflash->priv; mtd_device_unregister(&b47s->mtd); - kfree(b47s); return 0; } @@ -116,22 +337,4 @@ static struct platform_driver bcma_sflash_driver = { * Init **************************************************/ -static int __init bcm47xxsflash_init(void) -{ - int err; - - err = platform_driver_register(&bcma_sflash_driver); - if (err) - pr_err("Failed to register BCMA serial flash driver: %d\n", - err); - - return err; -} - -static void __exit bcm47xxsflash_exit(void) -{ - platform_driver_unregister(&bcma_sflash_driver); -} - -module_init(bcm47xxsflash_init); -module_exit(bcm47xxsflash_exit); +module_platform_driver(bcma_sflash_driver); diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h index f22f8c46dfc0..fe93daf4f489 100644 --- a/drivers/mtd/devices/bcm47xxsflash.h +++ b/drivers/mtd/devices/bcm47xxsflash.h @@ -60,6 +60,8 @@ enum bcm47xxsflash_type { struct bcm47xxsflash { struct bcma_drv_cc *bcma_cc; + int (*cc_read)(struct bcm47xxsflash *b47s, u16 offset); + void (*cc_write)(struct bcm47xxsflash *b47s, u16 offset, u32 value); enum bcm47xxsflash_type type; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index e081bfeaaf7d..5cb4c04726b2 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -6,6 +6,9 @@ * * Licence: GPL */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/fs.h> #include <linux/blkdev.h> @@ -18,10 +21,6 @@ #include <linux/mount.h> #include <linux/slab.h> -#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) -#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) - - /* Info for the block device */ struct block2mtd_dev { struct list_head list; @@ -84,7 +83,7 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr) err = _block2mtd_erase(dev, from, len); mutex_unlock(&dev->write_mutex); if (err) { - ERROR("erase failed err = %d", err); + pr_err("erase failed err = %d\n", err); instr->state = MTD_ERASE_FAILED; } else instr->state = MTD_ERASE_DONE; @@ -239,13 +238,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) #endif if (IS_ERR(bdev)) { - ERROR("error: cannot open device %s", devname); + pr_err("error: cannot open device %s\n", devname); goto devinit_err; } dev->blkdev = bdev; if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { - ERROR("attempting to use an MTD device as a block device"); + pr_err("attempting to use an MTD device as a block device\n"); goto devinit_err; } @@ -277,9 +276,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) goto devinit_err; } list_add(&dev->list, &blkmtd_device_list); - INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, - dev->mtd.name + strlen("block2mtd: "), - dev->mtd.erasesize >> 10, dev->mtd.erasesize); + pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: "), + dev->mtd.erasesize >> 10, dev->mtd.erasesize); return dev; devinit_err: @@ -339,17 +339,11 @@ static inline void kill_final_newline(char *str) } -#define parse_err(fmt, args...) do { \ - ERROR(fmt, ## args); \ - return 0; \ -} while (0) - #ifndef MODULE static int block2mtd_init_called = 0; static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */ #endif - static int block2mtd_setup2(const char *val) { char buf[80 + 12]; /* 80 for device, 12 for erase size */ @@ -359,8 +353,10 @@ static int block2mtd_setup2(const char *val) size_t erase_size = PAGE_SIZE; int i, ret; - if (strnlen(val, sizeof(buf)) >= sizeof(buf)) - parse_err("parameter too long"); + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) { + pr_err("parameter too long\n"); + return 0; + } strcpy(str, val); kill_final_newline(str); @@ -368,20 +364,27 @@ static int block2mtd_setup2(const char *val) for (i = 0; i < 2; i++) token[i] = strsep(&str, ","); - if (str) - parse_err("too many arguments"); + if (str) { + pr_err("too many arguments\n"); + return 0; + } - if (!token[0]) - parse_err("no argument"); + if (!token[0]) { + pr_err("no argument\n"); + return 0; + } name = token[0]; - if (strlen(name) + 1 > 80) - parse_err("device name too long"); + if (strlen(name) + 1 > 80) { + pr_err("device name too long\n"); + return 0; + } if (token[1]) { ret = parse_num(&erase_size, token[1]); if (ret) { - parse_err("illegal erase size"); + pr_err("illegal erase size\n"); + return 0; } } @@ -444,8 +447,9 @@ static void block2mtd_exit(void) struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); block2mtd_sync(&dev->mtd); mtd_device_unregister(&dev->mtd); - INFO("mtd%d: [%s] removed", dev->mtd.index, - dev->mtd.name + strlen("block2mtd: ")); + pr_info("mtd%d: [%s] removed\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: ")); list_del(&dev->list); block2mtd_free_device(dev); } diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index dccef9fdc1f2..d1dd6a33a050 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -20,14 +20,21 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/sched.h> #include <linux/pm_runtime.h> #include <linux/platform_data/elm.h> +#define ELM_SYSCONFIG 0x010 #define ELM_IRQSTATUS 0x018 #define ELM_IRQENABLE 0x01c #define ELM_LOCATION_CONFIG 0x020 #define ELM_PAGE_CTRL 0x080 #define ELM_SYNDROME_FRAGMENT_0 0x400 +#define ELM_SYNDROME_FRAGMENT_1 0x404 +#define ELM_SYNDROME_FRAGMENT_2 0x408 +#define ELM_SYNDROME_FRAGMENT_3 0x40c +#define ELM_SYNDROME_FRAGMENT_4 0x410 +#define ELM_SYNDROME_FRAGMENT_5 0x414 #define ELM_SYNDROME_FRAGMENT_6 0x418 #define ELM_LOCATION_STATUS 0x800 #define ELM_ERROR_LOCATION_0 0x880 @@ -56,12 +63,27 @@ #define SYNDROME_FRAGMENT_REG_SIZE 0x40 #define ERROR_LOCATION_SIZE 0x100 +struct elm_registers { + u32 elm_irqenable; + u32 elm_sysconfig; + u32 elm_location_config; + u32 elm_page_ctrl; + u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; +}; + struct elm_info { struct device *dev; void __iomem *elm_base; struct completion elm_completion; struct list_head list; enum bch_ecc bch_type; + struct elm_registers elm_regs; }; static LIST_HEAD(elm_devices); @@ -346,14 +368,9 @@ static int elm_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no memory resource defined\n"); - return -ENODEV; - } - - info->elm_base = devm_request_and_ioremap(&pdev->dev, res); - if (!info->elm_base) - return -EADDRNOTAVAIL; + info->elm_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->elm_base)) + return PTR_ERR(info->elm_base); ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, pdev->name, info); @@ -381,10 +398,103 @@ static int elm_remove(struct platform_device *pdev) { pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - platform_set_drvdata(pdev, NULL); return 0; } +/** + * elm_context_save + * saves ELM configurations to preserve them across Hardware powered-down + */ +static int elm_context_save(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE); + regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG); + regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); + regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH8_ECC: + regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_3 + offset); + regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_2 + offset); + case BCH4_ECC: + regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_1 + offset); + regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_0 + offset); + default: + return -EINVAL; + } + /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs + * to be saved for all BCH schemes*/ + regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_6 + offset); + } + return 0; +} + +/** + * elm_context_restore + * writes configurations saved duing power-down back into ELM registers + */ +static int elm_context_restore(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable); + elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig); + elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); + elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH8_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, + regs->elm_syndrome_fragment_3[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, + regs->elm_syndrome_fragment_2[i]); + case BCH4_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, + regs->elm_syndrome_fragment_1[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, + regs->elm_syndrome_fragment_0[i]); + default: + return -EINVAL; + } + /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, + regs->elm_syndrome_fragment_6[i] & + ELM_SYNDROME_VALID); + } + return 0; +} + +static int elm_suspend(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + elm_context_save(info); + pm_runtime_put_sync(dev); + return 0; +} + +static int elm_resume(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + pm_runtime_get_sync(dev); + elm_context_restore(info); + return 0; +} + +static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); + #ifdef CONFIG_OF static const struct of_device_id elm_of_match[] = { { .compatible = "ti,am3352-elm" }, @@ -398,6 +508,7 @@ static struct platform_driver elm_driver = { .name = "elm", .owner = THIS_MODULE, .of_match_table = of_match_ptr(elm_of_match), + .pm = &elm_pm_ops, }, .probe = elm_probe, .remove = elm_remove, diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 2f3d2a5ff349..26b14f9fcac6 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -43,17 +43,24 @@ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ #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 */ #define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ #define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ +/* 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_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + /* Used for SST flashes only. */ #define OPCODE_BP 0x02 /* Byte program */ #define OPCODE_WRDI 0x04 /* Write disable */ #define OPCODE_AAI_WP 0xad /* Auto address increment word program */ -/* Used for Macronix flashes only. */ +/* Used for Macronix and Winbond flashes. */ #define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ #define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ @@ -84,6 +91,8 @@ struct m25p { u16 page_size; u16 addr_width; u8 erase_opcode; + u8 read_opcode; + u8 program_opcode; u8 *command; bool fast_read; }; @@ -161,6 +170,7 @@ static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) { switch (JEDEC_MFR(jedec_id)) { case CFI_MFR_MACRONIX: + case CFI_MFR_ST: /* Micron, actually */ case 0xEF /* winbond */: flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; return spi_write(flash->spi, flash->command, 1); @@ -371,7 +381,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, */ /* Set up the write data buffer. */ - opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ; + opcode = flash->read_opcode; flash->command[0] = opcode; m25p_addr2cmd(flash, from, flash->command); @@ -422,7 +432,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, write_enable(flash); /* Set up the opcode in the write buffer. */ - flash->command[0] = OPCODE_PP; + flash->command[0] = flash->program_opcode; m25p_addr2cmd(flash, to, flash->command); page_offset = to & (flash->page_size - 1); @@ -682,6 +692,8 @@ struct flash_info { #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ #define M25P_NO_ERASE 0x02 /* No erase command needed */ #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 INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -694,13 +706,13 @@ struct flash_info { .flags = (_flags), \ }) -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ ((kernel_ulong_t)&(struct flash_info) { \ .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ .addr_width = (_addr_width), \ - .flags = M25P_NO_ERASE, \ + .flags = (_flags), \ }) /* NOTE: double check command sets and memory organization when you add @@ -732,7 +744,8 @@ static const struct spi_device_id m25p_ids[] = { { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, @@ -762,6 +775,11 @@ static const struct spi_device_id m25p_ids[] = { { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, + /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ @@ -840,17 +858,18 @@ static const struct spi_device_id m25p_ids[] = { { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2) }, + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) }, { }, }; MODULE_DEVICE_TABLE(spi, m25p_ids); @@ -920,7 +939,7 @@ static int m25p_probe(struct spi_device *spi) * a chip ID, try the JEDEC id commands; they'll work for most * newer chips, even if we don't recognize the particular chip. */ - data = spi->dev.platform_data; + data = dev_get_platdata(&spi->dev); if (data && data->type) { const struct spi_device_id *plat_id; @@ -972,7 +991,7 @@ static int m25p_probe(struct spi_device *spi) flash->spi = spi; mutex_init(&flash->lock); - dev_set_drvdata(&spi->dev, flash); + spi_set_drvdata(spi, flash); /* * Atmel, SST and Intel/Numonyx serial flash tend to power @@ -1014,6 +1033,9 @@ static int m25p_probe(struct spi_device *spi) if (info->flags & SECT_4K) { flash->erase_opcode = OPCODE_BE_4K; flash->mtd.erasesize = 4096; + } else if (info->flags & SECT_4K_PMC) { + flash->erase_opcode = OPCODE_BE_4K_PMC; + flash->mtd.erasesize = 4096; } else { flash->erase_opcode = OPCODE_SE; flash->mtd.erasesize = info->sector_size; @@ -1028,24 +1050,41 @@ static int m25p_probe(struct spi_device *spi) flash->mtd.writebufsize = flash->page_size; flash->fast_read = false; -#ifdef CONFIG_OF if (np && of_property_read_bool(np, "m25p,fast-read")) flash->fast_read = true; -#endif #ifdef CONFIG_M25PXX_USE_FAST_READ flash->fast_read = true; #endif + if (info->flags & M25P_NO_FR) + flash->fast_read = false; + + /* Default commands */ + if (flash->fast_read) + flash->read_opcode = OPCODE_FAST_READ; + else + flash->read_opcode = OPCODE_NORM_READ; + + flash->program_opcode = OPCODE_PP; if (info->addr_width) flash->addr_width = info->addr_width; - else { + else if (flash->mtd.size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ - if (flash->mtd.size > 0x1000000) { - flash->addr_width = 4; - set_4byte(flash, info->jedec_id, 1); + flash->addr_width = 4; + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + flash->read_opcode = flash->fast_read ? + OPCODE_FAST_READ_4B : + OPCODE_NORM_READ_4B; + flash->program_opcode = OPCODE_PP_4B; + /* No small sector erase for 4-byte command set */ + flash->erase_opcode = OPCODE_SE_4B; + flash->mtd.erasesize = info->sector_size; } else - flash->addr_width = 3; + set_4byte(flash, info->jedec_id, 1); + } else { + flash->addr_width = 3; } dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, @@ -1080,7 +1119,7 @@ static int m25p_probe(struct spi_device *spi) static int m25p_remove(struct spi_device *spi) { - struct m25p *flash = dev_get_drvdata(&spi->dev); + struct m25p *flash = spi_get_drvdata(spi); int status; /* Clean up MTD stuff. */ diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 28779b6dfcd9..0e8cbfeba11e 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -622,7 +622,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, struct dataflash *priv; struct mtd_info *device; struct mtd_part_parser_data ppdata; - struct flash_platform_data *pdata = spi->dev.platform_data; + struct flash_platform_data *pdata = dev_get_platdata(&spi->dev); char *otp_tag = ""; int err = 0; @@ -661,7 +661,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n", name, (long long)((device->size + 1023) >> 10), pagesize, otp_tag); - dev_set_drvdata(&spi->dev, priv); + spi_set_drvdata(spi, priv); ppdata.of_node = spi->dev.of_node; err = mtd_device_parse_register(device, NULL, &ppdata, @@ -671,7 +671,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, if (!err) return 0; - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); kfree(priv); return err; } @@ -895,14 +895,14 @@ static int dataflash_probe(struct spi_device *spi) static int dataflash_remove(struct spi_device *spi) { - struct dataflash *flash = dev_get_drvdata(&spi->dev); + struct dataflash *flash = spi_get_drvdata(spi); int status; pr_debug("%s: remove\n", dev_name(&spi->dev)); status = mtd_device_unregister(&flash->mtd); if (status == 0) { - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); kfree(flash); } return status; diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 8a82b8bc21e1..423821412062 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -550,7 +550,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, { struct spear_snor_flash *flash = get_flash_data(mtd); struct spear_smi *dev = mtd->priv; - void *src; + void __iomem *src; u32 ctrlreg1, val; int ret; @@ -583,7 +583,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, writel(val, dev->io_base + SMI_CR1); - memcpy_fromio(buf, (u8 *)src, len); + memcpy_fromio(buf, src, len); /* restore ctrl reg1 */ writel(ctrlreg1, dev->io_base + SMI_CR1); @@ -596,7 +596,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, } static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, - void *dest, const void *src, size_t len) + void __iomem *dest, const void *src, size_t len) { int ret; u32 ctrlreg1; @@ -643,7 +643,7 @@ static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, { struct spear_snor_flash *flash = get_flash_data(mtd); struct spear_smi *dev = mtd->priv; - void *dest; + void __iomem *dest; u32 page_offset, page_size; int ret; @@ -995,14 +995,12 @@ static int spear_smi_probe(struct platform_device *pdev) ret = spear_smi_setup_banks(pdev, i, pdata->np[i]); if (ret) { dev_err(&dev->pdev->dev, "bank setup failed\n"); - goto err_bank_setup; + goto err_irq; } } return 0; -err_bank_setup: - platform_set_drvdata(pdev, NULL); err_irq: clk_disable_unprepare(dev->clk); err: @@ -1040,12 +1038,11 @@ static int spear_smi_remove(struct platform_device *pdev) } clk_disable_unprepare(dev->clk); - platform_set_drvdata(pdev, NULL); return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int spear_smi_suspend(struct device *dev) { struct spear_smi *sdev = dev_get_drvdata(dev); @@ -1068,9 +1065,9 @@ static int spear_smi_resume(struct device *dev) spear_smi_hw_init(sdev); return ret; } +#endif static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume); -#endif #ifdef CONFIG_OF static const struct of_device_id spear_smi_id_table[] = { @@ -1086,9 +1083,7 @@ static struct platform_driver spear_smi_driver = { .bus = &platform_bus_type, .owner = THIS_MODULE, .of_match_table = of_match_ptr(spear_smi_id_table), -#ifdef CONFIG_PM .pm = &spear_smi_pm_ops, -#endif }, .probe = spear_smi_probe, .remove = spear_smi_remove, diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 8091b0163694..a42f1f0e7281 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -370,9 +370,9 @@ static int sst25l_probe(struct spi_device *spi) flash->spi = spi; mutex_init(&flash->lock); - dev_set_drvdata(&spi->dev, flash); + spi_set_drvdata(spi, flash); - data = spi->dev.platform_data; + data = dev_get_platdata(&spi->dev); if (data && data->name) flash->mtd.name = data->name; else @@ -404,7 +404,7 @@ static int sst25l_probe(struct spi_device *spi) data ? data->nr_parts : 0); if (ret) { kfree(flash); - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); return -ENODEV; } @@ -413,7 +413,7 @@ static int sst25l_probe(struct spi_device *spi) static int sst25l_remove(struct spi_device *spi) { - struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); + struct sst25l_flash *flash = spi_get_drvdata(spi); int ret; ret = mtd_device_unregister(&flash->mtd); |