@@ -13,7 +13,7 @@
#include <linux/export.h>
#include <linux/bcma/bcma.h>
-static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
+u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset)
{
bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset);
bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR);
@@ -607,4 +607,8 @@ config MTD_NAND_FSMC
Enables support for NAND Flash chips on the ST Microelectronics
Flexible Static Memory Controller (FSMC)
+config MTD_NAND_BCM47NFLASH
+ bool "Broadcom NAND flash driver"
+ depends on BCMA && BCMA_NFLASH
+
endif # MTD_NAND
@@ -51,5 +51,6 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o
obj-$(CONFIG_MTD_NAND_RICOH) += r852.o
obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
+obj-$(CONFIG_MTD_NAND_BCM47NFLASH) += bcm47nflash.o
nand-objs := nand_base.o nand_bbt.o
new file mode 100644
@@ -0,0 +1,293 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/platform_device.h>
+#include <linux/bcma/bcma.h>
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Broadcom NAND flash driver");
+
+/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
+ * shown 6 retries were enough. */
+#define NFLASH_READY_RETRIES 100
+
+int devid_column = 0;
+unsigned curr_command = 0;
+
+/**************************************************
+ * Various helpers
+ **************************************************/
+
+static inline u8 bcma_nflash_ns_to_cycle(u16 ns, u16 clock)
+{
+ return ((ns * 1000 * clock) / 1000000) + 1;
+}
+
+static void bcma_nflash_enable(struct bcma_drv_cc *cc, bool enable)
+{
+ if (cc->core->id.rev == 38) {
+ if (cc->status & BCMA_CC_CHIPST_5357_NAND_BOOT)
+ return;
+ if (enable)
+ bcma_chipco_chipctl_maskset(cc, 1, ~0, 0x10000);
+ else
+ bcma_chipco_chipctl_maskset(cc, 1, ~0x10000, 0);
+ }
+}
+
+static int bcma_nflash_ctl_cmd(struct bcma_drv_cc *cc, u32 code)
+{
+ int i = 0;
+
+ bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, 0x80000000 | code);
+ for (i = 0; i < NFLASH_READY_RETRIES; i++) {
+ if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & 0x80000000)) {
+ i = 0;
+ break;
+ }
+ }
+ if (i) {
+ pr_err("NFLASH control command not ready!\n");
+ return -EBUSY;
+ }
+ return 0;
+}
+
+static int bcma_nflash_poll(struct bcma_drv_cc *cc)
+{
+ int i;
+ u32 tmp;
+
+ if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
+ for (i = 0; i < NFLASH_READY_RETRIES; i++) {
+ if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) &
+ 0x04000000) {
+ if (bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) &
+ BCMA_CC_NFLASH_CTL_ERR) {
+ pr_err("Error on polling\n");
+ return -EBUSY;
+ } else {
+ return 0;
+ }
+ }
+ }
+ } else {
+ for (i = 0; i < NFLASH_READY_RETRIES; i++) {
+ tmp = bcma_cc_read32(cc, BCMA_CC_NAND_INTFC_STATUS);
+ if ((tmp & 0xC0000000) == 0xC0000000)
+ return 0;
+ }
+ }
+ pr_err("Polling timeout!\n");
+ return -EBUSY;
+}
+
+static void bcma_nflash_cmd(struct bcma_drv_cc *cc, u32 code)
+{
+ bcma_cc_write32(cc, BCMA_CC_NAND_CMD_START, code);
+ bcma_cc_read32(cc, BCMA_CC_NAND_CMD_START);
+}
+
+/**************************************************
+ * MTD ops
+ **************************************************/
+
+static void bcma_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+ return;
+}
+
+static void bcma_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
+ struct bcma_nflash *nflash = (struct bcma_nflash *)nand_chip->priv;
+ struct bcma_drv_cc *cc = container_of(nflash, struct bcma_drv_cc, nflash);
+
+ switch (command) {
+ case NAND_CMD_RESET:
+ break;
+ case NAND_CMD_READID:
+ if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
+ if (bcma_nflash_ctl_cmd(cc, 0x40000000 | 0x01000000 |
+ 0x00080000 | 0x00010000 | 0x00000090) < 0)
+ pr_err("READID error\n");
+ } else {
+ bcma_nflash_enable(cc, true);
+ bcma_nflash_cmd(cc, 7);
+ if (bcma_nflash_poll(cc) < 0) {
+ bcma_nflash_enable(cc, false);
+ pr_err("READID error\n");
+ }
+ bcma_nflash_enable(cc, false);
+ }
+ devid_column = 0;
+ break;
+ default:
+ pr_err("Command 0x%X unsupported\n", command);
+ break;
+ }
+ curr_command = command;
+ pr_info("Set curr_command to 0x%X\n", curr_command);
+}
+
+static u8 bcma_nand_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
+ struct bcma_nflash *nflash = (struct bcma_nflash *)nand_chip->priv;
+ struct bcma_drv_cc *cc = container_of(nflash, struct bcma_drv_cc, nflash);
+ u32 code;
+
+ switch (curr_command) {
+ case NAND_CMD_READID:
+ if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) {
+ u32 tmp;
+ if (devid_column <= 4)
+ code = 0x40000000 | 0x00100000;
+ else
+ code = 0x00100000;
+ if (bcma_nflash_ctl_cmd(cc, code))
+ return 0xff;
+ else
+ tmp = bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xFF;
+ pr_info("Returning 0x%X\n", tmp);
+ return tmp;
+ } else {
+ u32 tmp = 0;
+ if (devid_column < 4)
+ tmp = bcma_cc_read32(cc, BCMA_CC_NAND_DEVID);
+ else if (devid_column < 8)
+ tmp = bcma_cc_read32(cc, BCMA_CC_NAND_DEVID_X);
+ else
+ WARN_ON(1);
+ tmp = (tmp >> (8 * curr_command)) & 0xff;
+ devid_column++;
+ return tmp;
+ }
+ break;
+ }
+
+ pr_err("Invalid curr_command: 0x%X\n", curr_command);
+ return 0;
+}
+
+/**************************************************
+ * Init & exit
+ **************************************************/
+
+static int bcma_nflash_init_bcm4706(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
+ struct bcma_nflash *nflash = (struct bcma_nflash *)nand_chip->priv;
+ struct bcma_drv_cc *cc = container_of(nflash, struct bcma_drv_cc, nflash);
+
+ u32 freq;
+ u16 clock;
+ u8 w0, w1, w2, w3, w4;
+
+ /* TODO: Set bit 0x8 in CC flashstrconfig Register */
+
+ if (cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) {
+ freq = 100000000;
+ } else {
+ freq = bcma_chipco_pll_read(cc, 4);
+ freq = (freq * 0xFFF) >> 3;
+ freq = (freq * 25000000) >> 3;
+ }
+ clock = freq / 1000000;
+ w0 = bcma_nflash_ns_to_cycle(15, clock);
+ w1 = bcma_nflash_ns_to_cycle(20, clock);
+ w2 = bcma_nflash_ns_to_cycle(10, clock);
+ w3 = bcma_nflash_ns_to_cycle(10, clock);
+ w4 = bcma_nflash_ns_to_cycle(100, clock);
+ bcma_cc_write32(cc, BCMA_CC_NFLASH_WAITCNT0,
+ (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0));
+
+ pr_info("Scanning: %d\n", nand_scan_ident(mtd, 1, NULL));
+ pr_info("pagemask: 0x%X\n", nand_chip->pagemask);
+ pr_info("page_shift: %d\n", nand_chip->page_shift);
+ pr_info("tail: %d\n", nand_scan_tail(mtd));
+
+ /* TODO: bcma_cc_write32(cc, BCMA_CC_NFLASH_CONF, val); */
+
+ return 0;
+}
+
+static int bcm47nflash_probe(struct platform_device *pdev)
+{
+ struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
+ struct bcma_drv_cc *cc = container_of(nflash, struct bcma_drv_cc, nflash);
+ struct mtd_info *mtd = nflash->mtd;
+ struct nand_chip *nand_chip;
+
+ mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
+ if (!mtd)
+ return -ENOMEM;
+
+ nand_chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+ if (!nand_chip)
+ return -ENOMEM;
+
+ nand_chip->priv = nflash;
+ nand_chip->select_chip = bcma_nand_select_chip;
+ nand_chip->cmdfunc = bcma_nand_cmdfunc;
+ nand_chip->read_byte = bcma_nand_read_byte;
+ nand_chip->options = NAND_SKIP_BBTSCAN;
+
+ mtd->priv = nand_chip;
+ mtd->owner = THIS_MODULE;
+
+ if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
+ bcma_nflash_init_bcm4706(mtd);
+ //else
+ // bcma_nflash_init_default(mtd);
+
+#if 0
+ err = mtd_device_parse_register(nflash->mtd, probes, NULL, NULL, 0);
+ if (err) {
+ pr_err("Failed to register MTD device: %d\n", err);
+ return err;
+ }
+#endif
+
+ return 0;
+}
+
+static int __devexit bcm47nflash_remove(struct platform_device *pdev)
+{
+ struct bcma_nflash *nflash = dev_get_platdata(&pdev->dev);
+
+ if (nflash->mtd)
+ mtd_device_unregister(nflash->mtd);
+
+ return 0;
+}
+
+static struct platform_driver bcma_nflash_driver = {
+ .remove = __devexit_p(bcm47nflash_remove),
+ .driver = {
+ .name = "bcma_nflash",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init init_bcm47nflash(void)
+{
+ int err;
+
+ err = platform_driver_probe(&bcma_nflash_driver, bcm47nflash_probe);
+ if (err)
+ pr_err("Failed to register serial flash driver: %d\n", err);
+
+ return err;
+}
+
+static void __exit exit_bcm47nflash(void)
+{
+ platform_driver_unregister(&bcma_nflash_driver);
+}
+
+module_init(init_bcm47nflash);
+module_exit(exit_bcm47nflash);
@@ -606,6 +606,7 @@ u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value);
/* PMU support */
extern void bcma_pmu_init(struct bcma_drv_cc *cc);
+extern u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset);
extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset,
u32 value);
extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset,