diff options
Diffstat (limited to 'target/linux/brcm47xx/patches-3.9/052-mtd-add-serial-flash-driver.patch')
-rw-r--r-- | target/linux/brcm47xx/patches-3.9/052-mtd-add-serial-flash-driver.patch | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/target/linux/brcm47xx/patches-3.9/052-mtd-add-serial-flash-driver.patch b/target/linux/brcm47xx/patches-3.9/052-mtd-add-serial-flash-driver.patch new file mode 100644 index 0000000000..844f89d4b3 --- /dev/null +++ b/target/linux/brcm47xx/patches-3.9/052-mtd-add-serial-flash-driver.patch @@ -0,0 +1,345 @@ +--- a/drivers/mtd/devices/bcm47xxsflash.c ++++ b/drivers/mtd/devices/bcm47xxsflash.c +@@ -1,47 +1,153 @@ +-#include <linux/kernel.h> ++/* ++ * Broadcom SiliconBackplane chipcommon serial flash interface ++ * ++ * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de> ++ * Copyright 2006, Broadcom Corporation ++ * All Rights Reserved. ++ * ++ * Licensed under the GNU/GPL. See COPYING for details. ++ */ ++ ++#define pr_fmt(fmt) "bcm47xxsflash: " fmt + #include <linux/module.h> + #include <linux/slab.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> + #include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/errno.h> ++#include <linux/delay.h> + #include <linux/platform_device.h> +-#include <linux/bcma/bcma.h> +- +-#include "bcm47xxsflash.h" ++#include <linux/mtd/bcm47xxsflash.h> + + MODULE_LICENSE("GPL"); +-MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); ++MODULE_DESCRIPTION("BCM47XX serial flash driver"); + + static const char *probes[] = { "bcm47xxpart", NULL }; + ++static int ++sflash_mtd_poll(struct bcm47xxsflash *sflash, unsigned int offset, int timeout) ++{ ++ unsigned long now = jiffies; ++ ++ for (;;) { ++ if (!sflash->poll(sflash, offset)) { ++ break; ++ } ++ if (time_after(jiffies, now + timeout)) { ++ pr_err("timeout while polling\n"); ++ return -ETIMEDOUT; ++ ++ } ++ cpu_relax(); ++ udelay(1); ++ } ++ ++ return 0; ++} ++ + static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) + { +- struct bcm47xxsflash *b47s = mtd->priv; ++ struct bcm47xxsflash *sflash = (struct bcm47xxsflash *)mtd->priv; + + /* Check address range */ ++ if (!len) ++ return 0; ++ + if ((from + len) > mtd->size) + return -EINVAL; + +- memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(b47s->window + from), ++ memcpy_fromio(buf, (void __iomem *)KSEG0ADDR(sflash->window + from), + len); + *retlen = len; + + return len; + } + +-static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) ++static int ++sflash_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) + { +- struct mtd_info *mtd = &b47s->mtd; ++ int bytes; ++ int ret; ++ struct bcm47xxsflash *sflash = (struct bcm47xxsflash *)mtd->priv; + +- mtd->priv = b47s; +- mtd->name = "bcm47xxsflash"; +- mtd->owner = THIS_MODULE; +- mtd->type = MTD_ROM; +- mtd->size = b47s->size; +- mtd->_read = bcm47xxsflash_read; ++ /* Check address range */ ++ if (!len) ++ return 0; ++ ++ if ((to + len) > mtd->size) ++ return -EINVAL; ++ ++ *retlen = 0; ++ while (len) { ++ ret = sflash->write(sflash, to, len, buf); ++ if (ret < 0) ++ return ret; ++ ++ bytes = ret; ++ ++ ret = sflash_mtd_poll(sflash, (unsigned int) to, HZ / 10); ++ if (ret) ++ return ret; ++ ++ to += (loff_t) bytes; ++ len -= bytes; ++ buf += bytes; ++ *retlen += bytes; ++ } + +- /* TODO: implement writing support and verify/change following code */ +- mtd->flags = MTD_CAP_ROM; +- mtd->writebufsize = mtd->writesize = 1; ++ return 0; ++} ++ ++static int ++sflash_mtd_erase(struct mtd_info *mtd, struct erase_info *erase) ++{ ++ struct bcm47xxsflash *sflash = (struct bcm47xxsflash *) mtd->priv; ++ int i, j, ret = 0; ++ unsigned int addr, len; ++ ++ /* Check address range */ ++ if (!erase->len) ++ return 0; ++ if ((erase->addr + erase->len) > mtd->size) ++ return -EINVAL; ++ ++ addr = erase->addr; ++ len = erase->len; ++ ++ /* Ensure that requested regions are aligned */ ++ for (i = 0; i < mtd->numeraseregions; i++) { ++ for (j = 0; j < mtd->eraseregions[i].numblocks; j++) { ++ if (addr == mtd->eraseregions[i].offset + ++ mtd->eraseregions[i].erasesize * j && ++ len >= mtd->eraseregions[i].erasesize) { ++ ret = sflash->erase(sflash, addr); ++ if (ret < 0) ++ break; ++ ret = sflash_mtd_poll(sflash, addr, 10 * HZ); ++ if (ret) ++ break; ++ addr += mtd->eraseregions[i].erasesize; ++ len -= mtd->eraseregions[i].erasesize; ++ } ++ } ++ if (ret) ++ break; ++ } ++ ++ /* Set erase status */ ++ if (ret) ++ erase->state = MTD_ERASE_FAILED; ++ else ++ erase->state = MTD_ERASE_DONE; ++ ++ /* Call erase callback */ ++ if (erase->callback) ++ erase->callback(erase); ++ ++ return ret; + } + + /************************************************** +@@ -50,53 +156,94 @@ static void bcm47xxsflash_fill_mtd(struc + + static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) + { +- struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); +- struct bcm47xxsflash *b47s; +- int err; ++ struct bcm47xxsflash *sflash = dev_get_platdata(&pdev->dev); ++ struct mtd_info *mtd; ++ struct mtd_erase_region_info *eraseregions; ++ int ret = 0; ++ ++ mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ if (!mtd){ ++ ret = -ENOMEM; ++ goto err_out; ++ } + +- b47s = kzalloc(sizeof(*b47s), GFP_KERNEL); +- if (!b47s) { +- err = -ENOMEM; +- goto out; +- } +- sflash->priv = b47s; +- +- b47s->window = sflash->window; +- b47s->blocksize = sflash->blocksize; +- b47s->numblocks = sflash->numblocks; +- b47s->size = sflash->size; +- bcm47xxsflash_fill_mtd(b47s); +- +- 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; ++ eraseregions = kzalloc(sizeof(struct mtd_erase_region_info), GFP_KERNEL); ++ if (!eraseregions) { ++ ret = -ENOMEM; ++ goto err_free_mtd; + } + ++ pr_info("found serial flash: blocksize=%dKB, numblocks=%d, size=%dKB\n", ++ sflash->blocksize / 1024, sflash->numblocks, sflash->size / 1024); ++ ++ /* Setup region info */ ++ eraseregions->offset = 0; ++ eraseregions->erasesize = sflash->blocksize; ++ eraseregions->numblocks = sflash->numblocks; ++ if (eraseregions->erasesize > mtd->erasesize) ++ mtd->erasesize = eraseregions->erasesize; ++ mtd->size = sflash->size; ++ mtd->numeraseregions = 1; ++ ++ /* Register with MTD */ ++ mtd->name = "bcm47xx-sflash"; ++ mtd->type = MTD_NORFLASH; ++ mtd->flags = MTD_CAP_NORFLASH; ++ mtd->eraseregions = eraseregions; ++ mtd->_erase = sflash_mtd_erase; ++ mtd->_read = bcm47xxsflash_read; ++ mtd->_write = sflash_mtd_write; ++ mtd->writesize = 1; ++ mtd->priv = sflash; ++ ret = dev_set_drvdata(&pdev->dev, mtd); ++ mtd->owner = THIS_MODULE; ++ if (ret) { ++ pr_err("adding private data failed\n"); ++ goto err_free_eraseregions; ++ } ++ ++ ret = mtd_device_parse_register(mtd, probes, NULL, NULL, 0); ++ ++ if (ret) { ++ pr_err("mtd_device_register failed\n"); ++ goto err_free_eraseregions; ++ } + return 0; + +-err_dev_reg: +- kfree(&b47s->mtd); +-out: +- return err; ++err_free_eraseregions: ++ kfree(eraseregions); ++err_free_mtd: ++ kfree(mtd); ++err_out: ++ return ret; + } + + static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) + { +- struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); +- struct bcm47xxsflash *b47s = sflash->priv; +- +- mtd_device_unregister(&b47s->mtd); +- kfree(b47s); ++ struct mtd_info *mtd = dev_get_drvdata(&pdev->dev); + ++ if (mtd) { ++ mtd_device_unregister(mtd); ++ map_destroy(mtd); ++ kfree(mtd->eraseregions); ++ kfree(mtd); ++ dev_set_drvdata(&pdev->dev, NULL); ++ } + return 0; + } + ++static const struct platform_device_id bcm47xxsflash_table[] = { ++ { "bcm47xx-sflash", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(platform, bcm47xxsflash_table); ++ + static struct platform_driver bcma_sflash_driver = { ++ .id_table = bcm47xxsflash_table, + .probe = bcm47xxsflash_bcma_probe, + .remove = bcm47xxsflash_bcma_remove, + .driver = { +- .name = "bcma_sflash", ++ .name = "bcm47xx-sflash", + .owner = THIS_MODULE, + }, + }; +@@ -111,8 +258,7 @@ static int __init bcm47xxsflash_init(voi + + err = platform_driver_register(&bcma_sflash_driver); + if (err) +- pr_err("Failed to register BCMA serial flash driver: %d\n", +- err); ++ pr_err("error registering platform driver: %i\n", err); + + return err; + } +--- /dev/null ++++ b/include/linux/mtd/bcm47xxsflash.h +@@ -0,0 +1,33 @@ ++#ifndef LINUX_MTD_BCM47XX_SFLASH_H_ ++#define LINUX_MTD_BCM47XX_SFLASH_H_ ++ ++#include <linux/mtd/mtd.h> ++ ++enum bcm47xxsflash_type { ++ BCM47XX_SFLASH_SSB, ++ BCM47XX_SFLASH_BCMA, ++}; ++ ++struct ssb_chipcommon; ++struct bcma_drv_cc; ++ ++struct bcm47xxsflash { ++ enum bcm47xxsflash_type type; ++ union { ++ struct ssb_chipcommon *scc; ++ struct bcma_drv_cc *bcc; ++ }; ++ ++ bool present; ++ u16 numblocks; ++ u32 window; ++ u32 blocksize; ++ u32 size; ++ ++ int (*poll)(struct bcm47xxsflash *dev, u32 offset); ++ int (*write)(struct bcm47xxsflash *dev, u32 offset, u32 len, const u8 *buf); ++ int (*erase)(struct bcm47xxsflash *dev, u32 offset); ++ ++ struct mtd_info *mtd; ++}; ++#endif /* LINUX_MTD_BCM47XX_SFLASH_H_ */ |