diff mbox

[BROKEN,PROOF,DONT,APPLY,2/2] bcm47nflash driver (WIP, BROKEN)

Message ID 1344769686-3541-2-git-send-email-zajec5@gmail.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Rafał Miłecki Aug. 12, 2012, 11:08 a.m. UTC
This is proof of concept for BCMA NAND flash driver. With that patch I
get:
NAND device: Manufacturer ID: 0xec, Chip ID: 0xf1 (Samsung NAND 128MiB
3,3V 8-bit), page size: 2048, OOB size: 64
---
 drivers/bcma/driver_chipcommon_pmu.c        |    2 +-
 drivers/mtd/nand/Kconfig                    |    4 +
 drivers/mtd/nand/Makefile                   |    1 +
 drivers/mtd/nand/bcm47nflash.c              |  293 +++++++++++++++++++++++++++
 include/linux/bcma/bcma_driver_chipcommon.h |    1 +
 5 files changed, 300 insertions(+), 1 deletions(-)
 create mode 100644 drivers/mtd/nand/bcm47nflash.c
diff mbox

Patch

diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index 8b8f2f3..da8b8e7 100644
--- a/drivers/bcma/driver_chipcommon_pmu.c
+++ b/drivers/bcma/driver_chipcommon_pmu.c
@@ -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);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 31bb7e5..4b8ae37 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -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
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index d4b4d87..879681f 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -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
diff --git a/drivers/mtd/nand/bcm47nflash.c b/drivers/mtd/nand/bcm47nflash.c
new file mode 100644
index 0000000..df7f373
--- /dev/null
+++ b/drivers/mtd/nand/bcm47nflash.c
@@ -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);
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index b9d7eeb..8507f51 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -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,