linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V2] bcma: add basic NAND flash driver
@ 2012-08-05 20:03 Rafał Miłecki
  2012-08-05 20:40 ` Florian Fainelli
  2012-08-06  9:12 ` Florian Fainelli
  0 siblings, 2 replies; 7+ messages in thread
From: Rafał Miłecki @ 2012-08-05 20:03 UTC (permalink / raw)
  To: linux-wireless, John W. Linville
  Cc: Rafał Miłecki, Larry Finger, Hauke Mehrtens

This is basic driver for NAND flash memory that allows reading it's
content. It was succesfully tested on Netgear WNDR4500 which is BCM4706
based router.

This driver has been written using specs at:
http://bcm-v4.sipsolutions.net/NAND_Flash

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
CC: Larry Finger <Larry.Finger@lwfinger.net>
CC: Hauke Mehrtens <hauke@hauke-m.de>
---
V2: Split bcma_nflash_read into three functions as suggested by Hauke
    Use bcma_* for printing
---
 drivers/bcma/Kconfig                        |    4 +-
 drivers/bcma/bcma_private.h                 |    2 +-
 drivers/bcma/driver_chipcommon_nflash.c     |  402 ++++++++++++++++++++++++++-
 drivers/bcma/driver_chipcommon_pmu.c        |    2 +-
 include/linux/bcma/bcma_driver_chipcommon.h |   88 ++++++
 5 files changed, 490 insertions(+), 8 deletions(-)

diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
index 8d1f777..a58d7a9 100644
--- a/drivers/bcma/Kconfig
+++ b/drivers/bcma/Kconfig
@@ -53,8 +53,8 @@ config BCMA_SFLASH
 	default y
 
 config BCMA_NFLASH
-	bool
-	depends on BCMA_DRIVER_MIPS && BROKEN
+	bool "Support for NAND flash"
+	depends on BCMA_DRIVER_MIPS
 	default y
 
 config BCMA_DRIVER_GMAC_CMN
diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
index 3cf9cc9..9895c7e 100644
--- a/drivers/bcma/bcma_private.h
+++ b/drivers/bcma/bcma_private.h
@@ -68,7 +68,7 @@ int bcma_nflash_init(struct bcma_drv_cc *cc);
 #else
 static inline int bcma_nflash_init(struct bcma_drv_cc *cc)
 {
-	bcma_err(cc->core->bus, "NAND flash not supported\n");
+	bcma_err(cc->core->bus, "NAND flash support not enabled\n");
 	return 0;
 }
 #endif /* CONFIG_BCMA_NFLASH */
diff --git a/drivers/bcma/driver_chipcommon_nflash.c b/drivers/bcma/driver_chipcommon_nflash.c
index 574d624..0d11e6f 100644
--- a/drivers/bcma/driver_chipcommon_nflash.c
+++ b/drivers/bcma/driver_chipcommon_nflash.c
@@ -2,18 +2,412 @@
  * Broadcom specific AMBA
  * ChipCommon NAND flash interface
  *
+ * Copyright 2012, Rafał Miłecki <zajec5@gmail.com>
+ *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
+#include "bcma_private.h"
 #include <linux/bcma/bcma.h>
 #include <linux/bcma/bcma_driver_chipcommon.h>
 #include <linux/delay.h>
 
-#include "bcma_private.h"
+/* 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
 
-/* Initialize NAND flash access */
-int bcma_nflash_init(struct bcma_drv_cc *cc)
+/**************************************************
+ * 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_REV38_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) {
+		bcma_err(cc->core->bus, "NFLASH control command not ready!\n");
+		return -EBUSY;
+	}
+	return 0;
+}
+
+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) {
+					bcma_err(cc->core->bus, "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;
+		}
+	}
+	bcma_err(cc->core->bus, "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);
+}
+
+static char *bcma_nflash_check_id(u8 *id)
+{
+	switch (id[0]) {
+	case 0x01:
+		return "AMD";
+	case 0x20:
+		return "Numonyx";
+	case 0x2c:
+		return "Micron";
+	case 0x98:
+		return "Toshiba";
+	case 0xad:
+		return "Hynix";
+	case 0xec:
+		return "Samsung";
+	}
+	return NULL;
+}
+
+/**************************************************
+ * Initialize NAND flash access
+ **************************************************/
+
+static int bcma_nflash_init_bcm4706(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+	struct bcma_nflash *nflash = &cc->nflash;
+
+	u32 freq, code, val, tmp;
+	u16 clock;
+	u8 w0, w1, w2, w3, w4;
+	u8 cbits, tbits;
+	u8 csize, rbits, bsize;
+	char *name;
+	int i;
+
+	/* 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));
+	if (bcma_nflash_ctl_cmd(cc, 0x40000000 | 0x01000000 | 0x00080000 |
+				    0x00010000 | 0x00000090))
+		return -EBUSY;
+
+	for (i = 0; i < 5; i++) {
+		/* Useless Broadcom condition dropped */
+		code = 0x40000000 | 0x00100000;
+		if (bcma_nflash_ctl_cmd(cc, code))
+			return -EBUSY;
+		else
+			nflash->id[i] =
+				bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xFF;
+	}
+
+	name = bcma_nflash_check_id(nflash->id);
+	if (name == NULL) {
+		bcma_err(bus, "Invalid flash id: 0x%X\n", nflash->id[0]);
+		/* TODO: Clear bit 8 of Register flashstrconfig */
+		return -ENOTSUPP;
+	}
+
+	nflash->type = nflash->id[0];
+	nflash->pagesize = 1024 << (nflash->id[3] & 0x3);
+	nflash->blocksize = (64 * 1024) << ((nflash->id[3] >> 4) & 0x3);
+	tmp = (8 << ((nflash->id[3] >> 2) & 0x1));
+	nflash->oobsize = tmp * nflash->pagesize / 512;
+	nflash->size = (8 << ((nflash->id[4] >> 4) & 0x7)) *
+		       (1 << ((nflash->id[4] >> 2) & 0x3));
+
+	tbits = ffs(nflash->size); /* find first bit set */
+	if (!tbits || tbits != fls(nflash->size)) {
+		bcma_err(bus, "Invalid flash size 0x%X\n", nflash->size);
+		return -ENOTSUPP;
+	}
+	tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */
+
+	cbits = ffs(nflash->pagesize);
+	if (!cbits || cbits != fls(nflash->pagesize)) {
+		bcma_err(bus, "Invalid flash pagesize 0x%X\n",
+			 nflash->pagesize);
+		return -ENOTSUPP;
+	}
+
+	nflash->col_mask = (1 << cbits) - 1;
+	nflash->row_shift = cbits;
+	csize = (cbits + 7) / 8;
+	rbits = tbits - cbits + 1;
+	bsize = (rbits + 7) / 8;
+	val = ((bsize - 1) << 6) | ((csize - 1) << 4) | 2;
+	bcma_cc_write32(cc, BCMA_CC_NFLASH_CONF, val);
+
+	bcma_info(bus, "Found %s %dMiB NAND flash, pagesize: %dB, blocksize: %dKiB\n",
+		  name, nflash->size, nflash->pagesize,
+		  nflash->blocksize / 1024);
+	nflash->present = true;
+	return 0;
+}
+
+static int bcma_nflash_init_default(struct bcma_drv_cc *cc)
 {
-	bcma_err(cc->core->bus, "NAND flash support is broken\n");
+	struct bcma_bus *bus = cc->core->bus;
+	struct bcma_nflash *nflash = &cc->nflash;
+	u32 id, id2, ncfg, control, val;
+	char *name;
+	int i;
+
+	bcma_nflash_enable(cc, true);
+	bcma_nflash_cmd(cc, 7);
+	if (bcma_nflash_poll(cc) < 0) {
+		bcma_nflash_enable(cc, false);
+		return -EBUSY;
+	}
+	bcma_nflash_enable(cc, false);
+
+	id = bcma_cc_read32(cc, BCMA_CC_NAND_DEVID);
+	id2 = bcma_cc_read32(cc, BCMA_CC_NAND_DEVID_X);
+	for (i = 0; i < 5; i++) {
+		if (i < 4)
+			nflash->id[i] = (id >> (8 * i)) & 0xff;
+		else
+			nflash->id[i] = id2 & 0xff;
+	}
+
+	name = bcma_nflash_check_id(nflash->id);
+	if (name == NULL) {
+		bcma_err(bus, "Invalid flash id: 0x%X\n", nflash->id[0]);
+		return -ENOTSUPP;
+	}
+
+	nflash->type = nflash->id[0];
+
+	ncfg = bcma_cc_read32(cc, BCMA_CC_NAND_CONFIG);
+	switch ((ncfg >> 20) & 3) {
+	case 0:
+		nflash->pagesize = 0x200;
+		break;
+	case 1:
+		nflash->pagesize = 0x800;
+		break;
+	case 2:
+		nflash->pagesize = 0x1000;
+		break;
+	default:
+		nflash->pagesize = 0x2000;
+		break;
+	}
+	val = (ncfg >> 28) & 7;
+	switch (val) {
+	case 0:
+		nflash->blocksize = 0x4000;
+		break;
+	case 1:
+		nflash->blocksize = 0x20000;
+		break;
+	case 2:
+		nflash->blocksize = 0x2000;
+		break;
+	case 3:
+		nflash->blocksize = 0x80000;
+		break;
+	case 4:
+		nflash->blocksize = 0x40000;
+		break;
+	default:
+		nflash->blocksize = 0;
+		bcma_err(bus, "Unknown blocksize\n");
+		return -ENOTSUPP;
+	}
+	nflash->size = (1 << (val - 1)) * 8;
+
+	control = bcma_cc_read32(cc, BCMA_CC_NAND_ACC_CONTROL);
+	nflash->ecclevel = (control & 0x000f0000) >> 16;
+	nflash->ecclevel0 = (control & 0x00f00000) >> 20;
+	if (nflash->ecclevel != nflash->ecclevel0) {
+		control = control & ~(0x00ff0000);
+		control = control | (nflash->ecclevel0 << 20) |
+				(nflash->ecclevel0 << 16);
+		bcma_cc_write32(cc, BCMA_CC_NAND_ACC_CONTROL, control);
+		nflash->ecclevel = nflash->ecclevel0;
+	}
+	nflash->numblocks = (nflash->size * (1 << 10)) /
+			    (nflash->blocksize >> 10);
+	if (nflash->size == 0) {
+		bcma_err(bus, "Invalid flash size\n");
+		return -ENOTSUPP;
+	}
+
+	bcma_info(bus, "Found %s %dMiB NAND flash, pagesize: %dB, blocksize: %dKiB\n",
+		  name, nflash->size, nflash->pagesize,
+		  nflash->blocksize / 1024);
+	nflash->present = true;
 	return 0;
 }
+
+int bcma_nflash_init(struct bcma_drv_cc *cc)
+{
+	struct bcma_bus *bus = cc->core->bus;
+
+	if (cc->core->bus->chipinfo.id != BCMA_CHIP_ID_BCM4706 &&
+	    cc->core->id.rev != 0x38) {
+		bcma_err(bus, "NAND flash on unsupported board!\n");
+		return -ENOTSUPP;
+	}
+
+	if (!(cc->capabilities & BCMA_CC_CAP_NFLASH)) {
+		bcma_err(bus, "NAND flash not present according to ChipCommon\n");
+		return -ENODEV;
+	}
+
+	if (cc->nflash.present) {
+		bcma_warn(bus, "NAND flash already initialized");
+		return 0;
+	}
+
+	if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
+		return bcma_nflash_init_bcm4706(cc);
+	else
+		return bcma_nflash_init_default(cc);
+}
+
+/**************************************************
+ * R/W ops
+ **************************************************/
+
+static int bcma_nflash_read_bcm4706(struct bcma_drv_cc *cc, uint offset,
+				    uint len, u8 *buf)
+{
+	struct bcma_nflash *nflash = &cc->nflash;
+	u32 page_offset, page_addr, ctrlcode;
+	u32 *dest = (u32 *)buf;
+	uint remains = len;
+	int i;
+
+	while (remains > 0) {
+		page_offset = offset & (nflash->pagesize - 1);
+		page_addr = (offset & ~(nflash->pagesize - 1)) *
+			    2 + page_offset;
+		bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR,
+				page_addr & nflash->col_mask);
+		bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
+				page_addr >> nflash->row_shift);
+
+		ctrlcode = 0x40000000 | 0x00080000 | 0x00040000 | 0x00020000 |
+			   0x00010000 | 0x3000;
+		if (bcma_nflash_ctl_cmd(cc, ctrlcode))
+			break;
+		if (bcma_nflash_poll(cc) < 0)
+			break;
+		for (i = 0; i < 512; i += 4, dest++) {
+			if (i < 508)
+				ctrlcode = 0x40000000 | 0x30000000 | 0x00100000;
+			else
+				ctrlcode = 0x30000000 | 0x00100000;
+			if (bcma_nflash_ctl_cmd(cc, ctrlcode))
+				return len - remains;
+			*dest = bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA);
+		}
+		remains -= 512;
+		offset += 512;
+	}
+	return len - remains;
+}
+
+static int bcma_nflash_read_default(struct bcma_drv_cc *cc, uint offset,
+				    uint len, u8 *buf)
+{
+	u32 *dest = (u32 *)buf;
+	uint remains = len;
+	int i;
+
+	bcma_nflash_enable(cc, true);
+	while (remains > 0) {
+		bcma_cc_write32(cc, BCMA_CC_NAND_CMD_ADDR, offset);
+		bcma_nflash_cmd(cc, 1);
+		if (bcma_nflash_poll(cc) < 0)
+			break;
+		if (!(bcma_cc_read32(cc, BCMA_CC_NAND_INTFC_STATUS) &
+			0x20000000))
+			break;
+		for (i = 0; i < 512; i += 4, dest++)
+			*dest = bcma_cc_read32(cc, BCMA_CC_NAND_CACHE_DATA);
+		remains -= 512;
+		offset += 512;
+	}
+	bcma_nflash_enable(cc, false);
+	return len - remains;
+}
+
+int bcma_nflash_read(struct bcma_drv_cc *cc, uint offset, uint len, u8 *buf)
+{
+	struct bcma_nflash *nflash = &cc->nflash;
+	u32 mask = 0x1FF;
+	u32 tmp;
+
+	if ((offset & mask) != 0 || (len & mask) != 0)
+		return 0;
+
+	tmp = (offset + len) >> 20;
+	if (tmp > nflash->size)
+		return 0;
+	if (tmp == nflash->size && ((offset + len) & ((1 << 20) - 1)) != 0)
+		return 0;
+
+	if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
+		return bcma_nflash_read_bcm4706(cc, offset, len, buf);
+	else
+		return bcma_nflash_read_default(cc, offset, len, buf);
+}
diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c
index 4432617..fd6620e 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/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 3c80885..c80d8f1 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -94,6 +94,7 @@
 #define  BCMA_CC_CHIPST_4706_SFLASH_TYPE	BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */
 #define  BCMA_CC_CHIPST_4706_MIPS_BENDIAN	BIT(3) /* 0: little, 1: big endian */
 #define  BCMA_CC_CHIPST_4706_PCIE1_DISABLE	BIT(5) /* PCIE1 enable strap pin */
+#define  BCMA_CC_CHIPST_REV38_NAND_BOOT		BIT(4) /* NAND boot, valid for CC rev 38 */
 #define BCMA_CC_JCMD			0x0030		/* Rev >= 10 only */
 #define  BCMA_CC_JCMD_START		0x80000000
 #define  BCMA_CC_JCMD_BUSY		0x80000000
@@ -260,6 +261,14 @@
 #define  BCMA_CC_SROM_CONTROL_SIZE_16K	0x00000004
 #define  BCMA_CC_SROM_CONTROL_SIZE_SHIFT	1
 #define  BCMA_CC_SROM_CONTROL_PRESENT	0x00000001
+/* NAND flash registers for BCM4706 (corerev = 31) */
+#define BCMA_CC_NFLASH_CTL		0x01A0
+#define  BCMA_CC_NFLASH_CTL_ERR		0x08000000
+#define BCMA_CC_NFLASH_CONF		0x01A4
+#define BCMA_CC_NFLASH_COL_ADDR		0x01A8
+#define BCMA_CC_NFLASH_ROW_ADDR		0x01AC
+#define BCMA_CC_NFLASH_DATA		0x01B0
+#define BCMA_CC_NFLASH_WAITCNT0		0x01B4
 /* 0x1E0 is defined as shared BCMA_CLKCTLST */
 #define BCMA_CC_HW_WORKAROUND		0x01E4 /* Hardware workaround (rev >= 20) */
 #define BCMA_CC_UART0_DATA		0x0300
@@ -319,6 +328,60 @@
 #define BCMA_CC_PLLCTL_ADDR		0x0660
 #define BCMA_CC_PLLCTL_DATA		0x0664
 #define BCMA_CC_SPROM			0x0800 /* SPROM beginning */
+/* NAND flash MLC controller registers (corerev >= 38) */
+#define BCMA_CC_NAND_REVISION		0x0C00
+#define BCMA_CC_NAND_CMD_START		0x0C04
+#define BCMA_CC_NAND_CMD_ADDR_X		0x0C08
+#define BCMA_CC_NAND_CMD_ADDR		0x0C0C
+#define BCMA_CC_NAND_CMD_END_ADDR	0x0C10
+#define BCMA_CC_NAND_CS_NAND_SELECT	0x0C14
+#define BCMA_CC_NAND_CS_NAND_XOR	0x0C18
+#define BCMA_CC_NAND_SPARE_RD0		0x0C20
+#define BCMA_CC_NAND_SPARE_RD4		0x0C24
+#define BCMA_CC_NAND_SPARE_RD8		0x0C28
+#define BCMA_CC_NAND_SPARE_RD12		0x0C2C
+#define BCMA_CC_NAND_SPARE_WR0		0x0C30
+#define BCMA_CC_NAND_SPARE_WR4		0x0C34
+#define BCMA_CC_NAND_SPARE_WR8		0x0C38
+#define BCMA_CC_NAND_SPARE_WR12		0x0C3C
+#define BCMA_CC_NAND_ACC_CONTROL	0x0C40
+#define BCMA_CC_NAND_CONFIG		0x0C48
+#define BCMA_CC_NAND_TIMING_1		0x0C50
+#define BCMA_CC_NAND_TIMING_2		0x0C54
+#define BCMA_CC_NAND_SEMAPHORE		0x0C58
+#define BCMA_CC_NAND_DEVID		0x0C60
+#define BCMA_CC_NAND_DEVID_X		0x0C64
+#define BCMA_CC_NAND_BLOCK_LOCK_STATUS	0x0C68
+#define BCMA_CC_NAND_INTFC_STATUS	0x0C6C
+#define BCMA_CC_NAND_ECC_CORR_ADDR_X	0x0C70
+#define BCMA_CC_NAND_ECC_CORR_ADDR	0x0C74
+#define BCMA_CC_NAND_ECC_UNC_ADDR_X	0x0C78
+#define BCMA_CC_NAND_ECC_UNC_ADDR	0x0C7C
+#define BCMA_CC_NAND_READ_ERROR_COUNT	0x0C80
+#define BCMA_CC_NAND_CORR_STAT_THRESHOLD	0x0C84
+#define BCMA_CC_NAND_READ_ADDR_X	0x0C90
+#define BCMA_CC_NAND_READ_ADDR		0x0C94
+#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR_X	0x0C98
+#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR	0x0C9C
+#define BCMA_CC_NAND_COPY_BACK_ADDR_X	0x0CA0
+#define BCMA_CC_NAND_COPY_BACK_ADDR	0x0CA4
+#define BCMA_CC_NAND_BLOCK_ERASE_ADDR_X	0x0CA8
+#define BCMA_CC_NAND_BLOCK_ERASE_ADDR	0x0CAC
+#define BCMA_CC_NAND_INV_READ_ADDR_X	0x0CB0
+#define BCMA_CC_NAND_INV_READ_ADDR	0x0CB4
+#define BCMA_CC_NAND_BLK_WR_PROTECT	0x0CC0
+#define BCMA_CC_NAND_ACC_CONTROL_CS1	0x0CD0
+#define BCMA_CC_NAND_CONFIG_CS1		0x0CD4
+#define BCMA_CC_NAND_TIMING_1_CS1	0x0CD8
+#define BCMA_CC_NAND_TIMING_2_CS1	0x0CDC
+#define BCMA_CC_NAND_SPARE_RD16		0x0D30
+#define BCMA_CC_NAND_SPARE_RD20		0x0D34
+#define BCMA_CC_NAND_SPARE_RD24		0x0D38
+#define BCMA_CC_NAND_SPARE_RD28		0x0D3C
+#define BCMA_CC_NAND_CACHE_ADDR		0x0D40
+#define BCMA_CC_NAND_CACHE_DATA		0x0D44
+#define BCMA_CC_NAND_CTRL_CONFIG	0x0D48
+#define BCMA_CC_NAND_CTRL_STATUS	0x0D4C
 
 /* Divider allocation in 4716/47162/5356 */
 #define BCMA_CC_PMU5_MAINPLL_CPU	1
@@ -424,6 +487,26 @@ struct bcma_pflash {
 	u32 window_size;
 };
 
+#ifdef CONFIG_BCMA_NFLASH
+struct bcma_nflash {
+	bool present;
+
+	uint blocksize; /* Block size */
+	uint pagesize; /* Page size */
+	uint oobsize; /* OOB size per page */
+	uint numblocks; /* Number of blocks */
+	u32 type; /* Type */
+	uint size; /* Total size */
+	u8 id[5];
+	uint ecclevel; /* ECC algorithm for blocks other than block 0 */
+	uint ecclevel0; /* ECC algorithm for blocks 0 */
+
+	/* BCM4706 specific */
+	u32 col_mask;
+	u32 row_shift;
+};
+#endif
+
 struct bcma_serial_port {
 	void *regs;
 	unsigned long clockspeed;
@@ -442,8 +525,12 @@ struct bcma_drv_cc {
 	/* Fast Powerup Delay constant */
 	u16 fast_pwrup_delay;
 	struct bcma_chipcommon_pmu pmu;
+
 #ifdef CONFIG_BCMA_DRIVER_MIPS
 	struct bcma_pflash pflash;
+#ifdef CONFIG_BCMA_NFLASH
+	struct bcma_nflash nflash;
+#endif
 
 	int nr_serial_ports;
 	struct bcma_serial_port serial_ports[4];
@@ -488,6 +575,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);
 
+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,
-- 
1.7.7


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH V2] bcma: add basic NAND flash driver
  2012-08-05 20:03 [PATCH V2] bcma: add basic NAND flash driver Rafał Miłecki
@ 2012-08-05 20:40 ` Florian Fainelli
  2012-08-06  6:50   ` Rafał Miłecki
  2012-08-06  9:12 ` Florian Fainelli
  1 sibling, 1 reply; 7+ messages in thread
From: Florian Fainelli @ 2012-08-05 20:40 UTC (permalink / raw)
  To: Rafał Miłecki
  Cc: linux-wireless, John W. Linville, Larry Finger, Hauke Mehrtens

Hi Rafal,

Le dimanche 05 août 2012 22:03:07, Rafał Miłecki a écrit :
> This is basic driver for NAND flash memory that allows reading it's
> content. It was succesfully tested on Netgear WNDR4500 which is BCM4706
> based router.
> 
> This driver has been written using specs at:
> http://bcm-v4.sipsolutions.net/NAND_Flash

The big problem that I see with your driver is that it does not interface with 
the MTD subsystem, and therefore:

- does not conform to the MTD API for reading pages, blocks etc...
- duplicates NAND flash detection in a manner which is far less robust than 
what the MTD NAND subsystem already does

I guess that this driver enables you do some stuff on your router, but clearly 
you should aim at writing a real MTD driver instead of having such an ad-hoc 
solution.

> 
> Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
> CC: Larry Finger <Larry.Finger@lwfinger.net>
> CC: Hauke Mehrtens <hauke@hauke-m.de>
> ---
> V2: Split bcma_nflash_read into three functions as suggested by Hauke
>     Use bcma_* for printing
> ---
>  drivers/bcma/Kconfig                        |    4 +-
>  drivers/bcma/bcma_private.h                 |    2 +-
>  drivers/bcma/driver_chipcommon_nflash.c     |  402
> ++++++++++++++++++++++++++- drivers/bcma/driver_chipcommon_pmu.c        | 
>   2 +-
>  include/linux/bcma/bcma_driver_chipcommon.h |   88 ++++++
>  5 files changed, 490 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
> index 8d1f777..a58d7a9 100644
> --- a/drivers/bcma/Kconfig
> +++ b/drivers/bcma/Kconfig
> @@ -53,8 +53,8 @@ config BCMA_SFLASH
>  	default y
> 
>  config BCMA_NFLASH
> -	bool
> -	depends on BCMA_DRIVER_MIPS && BROKEN
> +	bool "Support for NAND flash"
> +	depends on BCMA_DRIVER_MIPS
>  	default y
> 
>  config BCMA_DRIVER_GMAC_CMN
> diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
> index 3cf9cc9..9895c7e 100644
> --- a/drivers/bcma/bcma_private.h
> +++ b/drivers/bcma/bcma_private.h
> @@ -68,7 +68,7 @@ int bcma_nflash_init(struct bcma_drv_cc *cc);
>  #else
>  static inline int bcma_nflash_init(struct bcma_drv_cc *cc)
>  {
> -	bcma_err(cc->core->bus, "NAND flash not supported\n");
> +	bcma_err(cc->core->bus, "NAND flash support not enabled\n");
>  	return 0;
>  }
>  #endif /* CONFIG_BCMA_NFLASH */
> diff --git a/drivers/bcma/driver_chipcommon_nflash.c
> b/drivers/bcma/driver_chipcommon_nflash.c index 574d624..0d11e6f 100644
> --- a/drivers/bcma/driver_chipcommon_nflash.c
> +++ b/drivers/bcma/driver_chipcommon_nflash.c
> @@ -2,18 +2,412 @@
>   * Broadcom specific AMBA
>   * ChipCommon NAND flash interface
>   *
> + * Copyright 2012, Rafał Miłecki <zajec5@gmail.com>
> + *
>   * Licensed under the GNU/GPL. See COPYING for details.
>   */
> 
> +#include "bcma_private.h"
>  #include <linux/bcma/bcma.h>
>  #include <linux/bcma/bcma_driver_chipcommon.h>
>  #include <linux/delay.h>
> 
> -#include "bcma_private.h"
> +/* 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
> 
> -/* Initialize NAND flash access */
> -int bcma_nflash_init(struct bcma_drv_cc *cc)
> +/**************************************************
> + * 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_REV38_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) {
> +		bcma_err(cc->core->bus, "NFLASH control command not ready!\n");
> +		return -EBUSY;
> +	}
> +	return 0;
> +}
> +
> +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) {
> +					bcma_err(cc->core->bus, "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;
> +		}
> +	}
> +	bcma_err(cc->core->bus, "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);
> +}
> +
> +static char *bcma_nflash_check_id(u8 *id)
> +{
> +	switch (id[0]) {
> +	case 0x01:
> +		return "AMD";
> +	case 0x20:
> +		return "Numonyx";
> +	case 0x2c:
> +		return "Micron";
> +	case 0x98:
> +		return "Toshiba";
> +	case 0xad:
> +		return "Hynix";
> +	case 0xec:
> +		return "Samsung";
> +	}
> +	return NULL;
> +}
> +
> +/**************************************************
> + * Initialize NAND flash access
> + **************************************************/
> +
> +static int bcma_nflash_init_bcm4706(struct bcma_drv_cc *cc)
> +{
> +	struct bcma_bus *bus = cc->core->bus;
> +	struct bcma_nflash *nflash = &cc->nflash;
> +
> +	u32 freq, code, val, tmp;
> +	u16 clock;
> +	u8 w0, w1, w2, w3, w4;
> +	u8 cbits, tbits;
> +	u8 csize, rbits, bsize;
> +	char *name;
> +	int i;
> +
> +	/* 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));
> +	if (bcma_nflash_ctl_cmd(cc, 0x40000000 | 0x01000000 | 0x00080000 |
> +				    0x00010000 | 0x00000090))
> +		return -EBUSY;
> +
> +	for (i = 0; i < 5; i++) {
> +		/* Useless Broadcom condition dropped */
> +		code = 0x40000000 | 0x00100000;
> +		if (bcma_nflash_ctl_cmd(cc, code))
> +			return -EBUSY;
> +		else
> +			nflash->id[i] =
> +				bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA) & 0xFF;
> +	}
> +
> +	name = bcma_nflash_check_id(nflash->id);
> +	if (name == NULL) {
> +		bcma_err(bus, "Invalid flash id: 0x%X\n", nflash->id[0]);
> +		/* TODO: Clear bit 8 of Register flashstrconfig */
> +		return -ENOTSUPP;
> +	}
> +
> +	nflash->type = nflash->id[0];
> +	nflash->pagesize = 1024 << (nflash->id[3] & 0x3);
> +	nflash->blocksize = (64 * 1024) << ((nflash->id[3] >> 4) & 0x3);
> +	tmp = (8 << ((nflash->id[3] >> 2) & 0x1));
> +	nflash->oobsize = tmp * nflash->pagesize / 512;
> +	nflash->size = (8 << ((nflash->id[4] >> 4) & 0x7)) *
> +		       (1 << ((nflash->id[4] >> 2) & 0x3));
> +
> +	tbits = ffs(nflash->size); /* find first bit set */
> +	if (!tbits || tbits != fls(nflash->size)) {
> +		bcma_err(bus, "Invalid flash size 0x%X\n", nflash->size);
> +		return -ENOTSUPP;
> +	}
> +	tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */
> +
> +	cbits = ffs(nflash->pagesize);
> +	if (!cbits || cbits != fls(nflash->pagesize)) {
> +		bcma_err(bus, "Invalid flash pagesize 0x%X\n",
> +			 nflash->pagesize);
> +		return -ENOTSUPP;
> +	}
> +
> +	nflash->col_mask = (1 << cbits) - 1;
> +	nflash->row_shift = cbits;
> +	csize = (cbits + 7) / 8;
> +	rbits = tbits - cbits + 1;
> +	bsize = (rbits + 7) / 8;
> +	val = ((bsize - 1) << 6) | ((csize - 1) << 4) | 2;
> +	bcma_cc_write32(cc, BCMA_CC_NFLASH_CONF, val);
> +
> +	bcma_info(bus, "Found %s %dMiB NAND flash, pagesize: %dB, blocksize:
> %dKiB\n", +		  name, nflash->size, nflash->pagesize,
> +		  nflash->blocksize / 1024);
> +	nflash->present = true;
> +	return 0;
> +}
> +
> +static int bcma_nflash_init_default(struct bcma_drv_cc *cc)
>  {
> -	bcma_err(cc->core->bus, "NAND flash support is broken\n");
> +	struct bcma_bus *bus = cc->core->bus;
> +	struct bcma_nflash *nflash = &cc->nflash;
> +	u32 id, id2, ncfg, control, val;
> +	char *name;
> +	int i;
> +
> +	bcma_nflash_enable(cc, true);
> +	bcma_nflash_cmd(cc, 7);
> +	if (bcma_nflash_poll(cc) < 0) {
> +		bcma_nflash_enable(cc, false);
> +		return -EBUSY;
> +	}
> +	bcma_nflash_enable(cc, false);
> +
> +	id = bcma_cc_read32(cc, BCMA_CC_NAND_DEVID);
> +	id2 = bcma_cc_read32(cc, BCMA_CC_NAND_DEVID_X);
> +	for (i = 0; i < 5; i++) {
> +		if (i < 4)
> +			nflash->id[i] = (id >> (8 * i)) & 0xff;
> +		else
> +			nflash->id[i] = id2 & 0xff;
> +	}
> +
> +	name = bcma_nflash_check_id(nflash->id);
> +	if (name == NULL) {
> +		bcma_err(bus, "Invalid flash id: 0x%X\n", nflash->id[0]);
> +		return -ENOTSUPP;
> +	}
> +
> +	nflash->type = nflash->id[0];
> +
> +	ncfg = bcma_cc_read32(cc, BCMA_CC_NAND_CONFIG);
> +	switch ((ncfg >> 20) & 3) {
> +	case 0:
> +		nflash->pagesize = 0x200;
> +		break;
> +	case 1:
> +		nflash->pagesize = 0x800;
> +		break;
> +	case 2:
> +		nflash->pagesize = 0x1000;
> +		break;
> +	default:
> +		nflash->pagesize = 0x2000;
> +		break;
> +	}
> +	val = (ncfg >> 28) & 7;
> +	switch (val) {
> +	case 0:
> +		nflash->blocksize = 0x4000;
> +		break;
> +	case 1:
> +		nflash->blocksize = 0x20000;
> +		break;
> +	case 2:
> +		nflash->blocksize = 0x2000;
> +		break;
> +	case 3:
> +		nflash->blocksize = 0x80000;
> +		break;
> +	case 4:
> +		nflash->blocksize = 0x40000;
> +		break;
> +	default:
> +		nflash->blocksize = 0;
> +		bcma_err(bus, "Unknown blocksize\n");
> +		return -ENOTSUPP;
> +	}
> +	nflash->size = (1 << (val - 1)) * 8;
> +
> +	control = bcma_cc_read32(cc, BCMA_CC_NAND_ACC_CONTROL);
> +	nflash->ecclevel = (control & 0x000f0000) >> 16;
> +	nflash->ecclevel0 = (control & 0x00f00000) >> 20;
> +	if (nflash->ecclevel != nflash->ecclevel0) {
> +		control = control & ~(0x00ff0000);
> +		control = control | (nflash->ecclevel0 << 20) |
> +				(nflash->ecclevel0 << 16);
> +		bcma_cc_write32(cc, BCMA_CC_NAND_ACC_CONTROL, control);
> +		nflash->ecclevel = nflash->ecclevel0;
> +	}
> +	nflash->numblocks = (nflash->size * (1 << 10)) /
> +			    (nflash->blocksize >> 10);
> +	if (nflash->size == 0) {
> +		bcma_err(bus, "Invalid flash size\n");
> +		return -ENOTSUPP;
> +	}
> +
> +	bcma_info(bus, "Found %s %dMiB NAND flash, pagesize: %dB, blocksize:
> %dKiB\n", +		  name, nflash->size, nflash->pagesize,
> +		  nflash->blocksize / 1024);
> +	nflash->present = true;
>  	return 0;
>  }
> +
> +int bcma_nflash_init(struct bcma_drv_cc *cc)
> +{
> +	struct bcma_bus *bus = cc->core->bus;
> +
> +	if (cc->core->bus->chipinfo.id != BCMA_CHIP_ID_BCM4706 &&
> +	    cc->core->id.rev != 0x38) {
> +		bcma_err(bus, "NAND flash on unsupported board!\n");
> +		return -ENOTSUPP;
> +	}
> +
> +	if (!(cc->capabilities & BCMA_CC_CAP_NFLASH)) {
> +		bcma_err(bus, "NAND flash not present according to ChipCommon\n");
> +		return -ENODEV;
> +	}
> +
> +	if (cc->nflash.present) {
> +		bcma_warn(bus, "NAND flash already initialized");
> +		return 0;
> +	}
> +
> +	if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
> +		return bcma_nflash_init_bcm4706(cc);
> +	else
> +		return bcma_nflash_init_default(cc);
> +}
> +
> +/**************************************************
> + * R/W ops
> + **************************************************/
> +
> +static int bcma_nflash_read_bcm4706(struct bcma_drv_cc *cc, uint offset,
> +				    uint len, u8 *buf)
> +{
> +	struct bcma_nflash *nflash = &cc->nflash;
> +	u32 page_offset, page_addr, ctrlcode;
> +	u32 *dest = (u32 *)buf;
> +	uint remains = len;
> +	int i;
> +
> +	while (remains > 0) {
> +		page_offset = offset & (nflash->pagesize - 1);
> +		page_addr = (offset & ~(nflash->pagesize - 1)) *
> +			    2 + page_offset;
> +		bcma_cc_write32(cc, BCMA_CC_NFLASH_COL_ADDR,
> +				page_addr & nflash->col_mask);
> +		bcma_cc_write32(cc, BCMA_CC_NFLASH_ROW_ADDR,
> +				page_addr >> nflash->row_shift);
> +
> +		ctrlcode = 0x40000000 | 0x00080000 | 0x00040000 | 0x00020000 |
> +			   0x00010000 | 0x3000;
> +		if (bcma_nflash_ctl_cmd(cc, ctrlcode))
> +			break;
> +		if (bcma_nflash_poll(cc) < 0)
> +			break;
> +		for (i = 0; i < 512; i += 4, dest++) {
> +			if (i < 508)
> +				ctrlcode = 0x40000000 | 0x30000000 | 0x00100000;
> +			else
> +				ctrlcode = 0x30000000 | 0x00100000;
> +			if (bcma_nflash_ctl_cmd(cc, ctrlcode))
> +				return len - remains;
> +			*dest = bcma_cc_read32(cc, BCMA_CC_NFLASH_DATA);
> +		}
> +		remains -= 512;
> +		offset += 512;
> +	}
> +	return len - remains;
> +}
> +
> +static int bcma_nflash_read_default(struct bcma_drv_cc *cc, uint offset,
> +				    uint len, u8 *buf)
> +{
> +	u32 *dest = (u32 *)buf;
> +	uint remains = len;
> +	int i;
> +
> +	bcma_nflash_enable(cc, true);
> +	while (remains > 0) {
> +		bcma_cc_write32(cc, BCMA_CC_NAND_CMD_ADDR, offset);
> +		bcma_nflash_cmd(cc, 1);
> +		if (bcma_nflash_poll(cc) < 0)
> +			break;
> +		if (!(bcma_cc_read32(cc, BCMA_CC_NAND_INTFC_STATUS) &
> +			0x20000000))
> +			break;
> +		for (i = 0; i < 512; i += 4, dest++)
> +			*dest = bcma_cc_read32(cc, BCMA_CC_NAND_CACHE_DATA);
> +		remains -= 512;
> +		offset += 512;
> +	}
> +	bcma_nflash_enable(cc, false);
> +	return len - remains;
> +}
> +
> +int bcma_nflash_read(struct bcma_drv_cc *cc, uint offset, uint len, u8
> *buf) +{
> +	struct bcma_nflash *nflash = &cc->nflash;
> +	u32 mask = 0x1FF;
> +	u32 tmp;
> +
> +	if ((offset & mask) != 0 || (len & mask) != 0)
> +		return 0;
> +
> +	tmp = (offset + len) >> 20;
> +	if (tmp > nflash->size)
> +		return 0;
> +	if (tmp == nflash->size && ((offset + len) & ((1 << 20) - 1)) != 0)
> +		return 0;
> +
> +	if (cc->core->bus->chipinfo.id == BCMA_CHIP_ID_BCM4706)
> +		return bcma_nflash_read_bcm4706(cc, offset, len, buf);
> +	else
> +		return bcma_nflash_read_default(cc, offset, len, buf);
> +}
> diff --git a/drivers/bcma/driver_chipcommon_pmu.c
> b/drivers/bcma/driver_chipcommon_pmu.c index 4432617..fd6620e 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/include/linux/bcma/bcma_driver_chipcommon.h
> b/include/linux/bcma/bcma_driver_chipcommon.h index 3c80885..c80d8f1
> 100644
> --- a/include/linux/bcma/bcma_driver_chipcommon.h
> +++ b/include/linux/bcma/bcma_driver_chipcommon.h
> @@ -94,6 +94,7 @@
>  #define  BCMA_CC_CHIPST_4706_SFLASH_TYPE	BIT(2) /* 0: 8b-p/ST-s flash, 1:
> 16b-p/Atmal-s flash */ #define  BCMA_CC_CHIPST_4706_MIPS_BENDIAN	BIT(3) /*
> 0: little, 1: big endian */ #define 
> BCMA_CC_CHIPST_4706_PCIE1_DISABLE	BIT(5) /* PCIE1 enable strap pin */
> +#define  BCMA_CC_CHIPST_REV38_NAND_BOOT		BIT(4) /* NAND boot, valid 
for
> CC rev 38 */ #define BCMA_CC_JCMD			0x0030		/* Rev >= 10 only */
>  #define  BCMA_CC_JCMD_START		0x80000000
>  #define  BCMA_CC_JCMD_BUSY		0x80000000
> @@ -260,6 +261,14 @@
>  #define  BCMA_CC_SROM_CONTROL_SIZE_16K	0x00000004
>  #define  BCMA_CC_SROM_CONTROL_SIZE_SHIFT	1
>  #define  BCMA_CC_SROM_CONTROL_PRESENT	0x00000001
> +/* NAND flash registers for BCM4706 (corerev = 31) */
> +#define BCMA_CC_NFLASH_CTL		0x01A0
> +#define  BCMA_CC_NFLASH_CTL_ERR		0x08000000
> +#define BCMA_CC_NFLASH_CONF		0x01A4
> +#define BCMA_CC_NFLASH_COL_ADDR		0x01A8
> +#define BCMA_CC_NFLASH_ROW_ADDR		0x01AC
> +#define BCMA_CC_NFLASH_DATA		0x01B0
> +#define BCMA_CC_NFLASH_WAITCNT0		0x01B4
>  /* 0x1E0 is defined as shared BCMA_CLKCTLST */
>  #define BCMA_CC_HW_WORKAROUND		0x01E4 /* Hardware workaround (rev >= 
20)
> */ #define BCMA_CC_UART0_DATA		0x0300
> @@ -319,6 +328,60 @@
>  #define BCMA_CC_PLLCTL_ADDR		0x0660
>  #define BCMA_CC_PLLCTL_DATA		0x0664
>  #define BCMA_CC_SPROM			0x0800 /* SPROM beginning */
> +/* NAND flash MLC controller registers (corerev >= 38) */
> +#define BCMA_CC_NAND_REVISION		0x0C00
> +#define BCMA_CC_NAND_CMD_START		0x0C04
> +#define BCMA_CC_NAND_CMD_ADDR_X		0x0C08
> +#define BCMA_CC_NAND_CMD_ADDR		0x0C0C
> +#define BCMA_CC_NAND_CMD_END_ADDR	0x0C10
> +#define BCMA_CC_NAND_CS_NAND_SELECT	0x0C14
> +#define BCMA_CC_NAND_CS_NAND_XOR	0x0C18
> +#define BCMA_CC_NAND_SPARE_RD0		0x0C20
> +#define BCMA_CC_NAND_SPARE_RD4		0x0C24
> +#define BCMA_CC_NAND_SPARE_RD8		0x0C28
> +#define BCMA_CC_NAND_SPARE_RD12		0x0C2C
> +#define BCMA_CC_NAND_SPARE_WR0		0x0C30
> +#define BCMA_CC_NAND_SPARE_WR4		0x0C34
> +#define BCMA_CC_NAND_SPARE_WR8		0x0C38
> +#define BCMA_CC_NAND_SPARE_WR12		0x0C3C
> +#define BCMA_CC_NAND_ACC_CONTROL	0x0C40
> +#define BCMA_CC_NAND_CONFIG		0x0C48
> +#define BCMA_CC_NAND_TIMING_1		0x0C50
> +#define BCMA_CC_NAND_TIMING_2		0x0C54
> +#define BCMA_CC_NAND_SEMAPHORE		0x0C58
> +#define BCMA_CC_NAND_DEVID		0x0C60
> +#define BCMA_CC_NAND_DEVID_X		0x0C64
> +#define BCMA_CC_NAND_BLOCK_LOCK_STATUS	0x0C68
> +#define BCMA_CC_NAND_INTFC_STATUS	0x0C6C
> +#define BCMA_CC_NAND_ECC_CORR_ADDR_X	0x0C70
> +#define BCMA_CC_NAND_ECC_CORR_ADDR	0x0C74
> +#define BCMA_CC_NAND_ECC_UNC_ADDR_X	0x0C78
> +#define BCMA_CC_NAND_ECC_UNC_ADDR	0x0C7C
> +#define BCMA_CC_NAND_READ_ERROR_COUNT	0x0C80
> +#define BCMA_CC_NAND_CORR_STAT_THRESHOLD	0x0C84
> +#define BCMA_CC_NAND_READ_ADDR_X	0x0C90
> +#define BCMA_CC_NAND_READ_ADDR		0x0C94
> +#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR_X	0x0C98
> +#define BCMA_CC_NAND_PAGE_PROGRAM_ADDR	0x0C9C
> +#define BCMA_CC_NAND_COPY_BACK_ADDR_X	0x0CA0
> +#define BCMA_CC_NAND_COPY_BACK_ADDR	0x0CA4
> +#define BCMA_CC_NAND_BLOCK_ERASE_ADDR_X	0x0CA8
> +#define BCMA_CC_NAND_BLOCK_ERASE_ADDR	0x0CAC
> +#define BCMA_CC_NAND_INV_READ_ADDR_X	0x0CB0
> +#define BCMA_CC_NAND_INV_READ_ADDR	0x0CB4
> +#define BCMA_CC_NAND_BLK_WR_PROTECT	0x0CC0
> +#define BCMA_CC_NAND_ACC_CONTROL_CS1	0x0CD0
> +#define BCMA_CC_NAND_CONFIG_CS1		0x0CD4
> +#define BCMA_CC_NAND_TIMING_1_CS1	0x0CD8
> +#define BCMA_CC_NAND_TIMING_2_CS1	0x0CDC
> +#define BCMA_CC_NAND_SPARE_RD16		0x0D30
> +#define BCMA_CC_NAND_SPARE_RD20		0x0D34
> +#define BCMA_CC_NAND_SPARE_RD24		0x0D38
> +#define BCMA_CC_NAND_SPARE_RD28		0x0D3C
> +#define BCMA_CC_NAND_CACHE_ADDR		0x0D40
> +#define BCMA_CC_NAND_CACHE_DATA		0x0D44
> +#define BCMA_CC_NAND_CTRL_CONFIG	0x0D48
> +#define BCMA_CC_NAND_CTRL_STATUS	0x0D4C
> 
>  /* Divider allocation in 4716/47162/5356 */
>  #define BCMA_CC_PMU5_MAINPLL_CPU	1
> @@ -424,6 +487,26 @@ struct bcma_pflash {
>  	u32 window_size;
>  };
> 
> +#ifdef CONFIG_BCMA_NFLASH
> +struct bcma_nflash {
> +	bool present;
> +
> +	uint blocksize; /* Block size */
> +	uint pagesize; /* Page size */
> +	uint oobsize; /* OOB size per page */
> +	uint numblocks; /* Number of blocks */
> +	u32 type; /* Type */
> +	uint size; /* Total size */
> +	u8 id[5];
> +	uint ecclevel; /* ECC algorithm for blocks other than block 0 */
> +	uint ecclevel0; /* ECC algorithm for blocks 0 */
> +
> +	/* BCM4706 specific */
> +	u32 col_mask;
> +	u32 row_shift;
> +};
> +#endif
> +
>  struct bcma_serial_port {
>  	void *regs;
>  	unsigned long clockspeed;
> @@ -442,8 +525,12 @@ struct bcma_drv_cc {
>  	/* Fast Powerup Delay constant */
>  	u16 fast_pwrup_delay;
>  	struct bcma_chipcommon_pmu pmu;
> +
>  #ifdef CONFIG_BCMA_DRIVER_MIPS
>  	struct bcma_pflash pflash;
> +#ifdef CONFIG_BCMA_NFLASH
> +	struct bcma_nflash nflash;
> +#endif
> 
>  	int nr_serial_ports;
>  	struct bcma_serial_port serial_ports[4];
> @@ -488,6 +575,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);
> 
> +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,

-- 
Florian

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2] bcma: add basic NAND flash driver
  2012-08-05 20:40 ` Florian Fainelli
@ 2012-08-06  6:50   ` Rafał Miłecki
  2012-08-06  8:50     ` Florian Fainelli
  0 siblings, 1 reply; 7+ messages in thread
From: Rafał Miłecki @ 2012-08-06  6:50 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: linux-wireless, John W. Linville, Larry Finger, Hauke Mehrtens

Thanks for looking at this!

2012/8/5 Florian Fainelli <florian@openwrt.org>:
> Le dimanche 05 août 2012 22:03:07, Rafał Miłecki a écrit :
>> This is basic driver for NAND flash memory that allows reading it's
>> content. It was succesfully tested on Netgear WNDR4500 which is BCM4706
>> based router.
>>
>> This driver has been written using specs at:
>> http://bcm-v4.sipsolutions.net/NAND_Flash
>
> The big problem that I see with your driver is that it does not interface with
> the MTD subsystem, and therefore:
>
> - does not conform to the MTD API for reading pages, blocks etc...

My idea for bcma responsibilities consists of:
1) Detecting hardware
2) Providing basic access to it
This is what (I believe) I provided with that submitted patch.

I'm not registering MTD driver directly in bcma, just like we don't
register ieee80211 device for 80211 core or netdev for ETH core.

After providing basic/low-level access in bcma my plan is to write
real MTD driver. In this driver I could make use of functions from
bcma_chipcommon_nflash.[ch].

Does it sound better now?


> - duplicates NAND flash detection in a manner which is far less robust than
> what the MTD NAND subsystem already does

Hm, do you mean there is some MTD driver detecting NAND flash on BCMA?
I wasn't aware of that. Grepping drivers/mtd for "bcma" didn't give me
anything. Can you say something more about this?


> I guess that this driver enables you do some stuff on your router, but clearly
> you should aim at writing a real MTD driver instead of having such an ad-hoc
> solution.

That "do some stuff" means almost nothing right now. My plan (as
explained earlier) is to write MTD driver.

-- 
Rafał

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2] bcma: add basic NAND flash driver
  2012-08-06  6:50   ` Rafał Miłecki
@ 2012-08-06  8:50     ` Florian Fainelli
  2012-08-06 12:08       ` Rafał Miłecki
  0 siblings, 1 reply; 7+ messages in thread
From: Florian Fainelli @ 2012-08-06  8:50 UTC (permalink / raw)
  To: Rafał Miłecki
  Cc: linux-wireless, John W. Linville, Larry Finger, Hauke Mehrtens

Hello Rafal,

On Monday 06 August 2012 08:50:35 Rafał Miłecki wrote:
> Thanks for looking at this!
> 
> 2012/8/5 Florian Fainelli <florian@openwrt.org>:
> > Le dimanche 05 août 2012 22:03:07, Rafał Miłecki a écrit :
> >> This is basic driver for NAND flash memory that allows reading it's
> >> content. It was succesfully tested on Netgear WNDR4500 which is BCM4706
> >> based router.
> >>
> >> This driver has been written using specs at:
> >> http://bcm-v4.sipsolutions.net/NAND_Flash
> >
> > The big problem that I see with your driver is that it does not interface 
with
> > the MTD subsystem, and therefore:
> >
> > - does not conform to the MTD API for reading pages, blocks etc...
> 
> My idea for bcma responsibilities consists of:
> 1) Detecting hardware
> 2) Providing basic access to it
> This is what (I believe) I provided with that submitted patch.
> 
> I'm not registering MTD driver directly in bcma, just like we don't
> register ieee80211 device for 80211 core or netdev for ETH core.
> 
> After providing basic/low-level access in bcma my plan is to write
> real MTD driver. In this driver I could make use of functions from
> bcma_chipcommon_nflash.[ch].
> 
> Does it sound better now?

A little, but I still wonder why this would be needed at all, unless you 
cannot rely on MTD because you are doing stuff very early with the Flash.
I find perfectly legitimate to export some bcma-specific symbols for accessing 
the NAND flash controller, but am a little more dubious about duplicating the 
reading/detection.

> 
> 
> > - duplicates NAND flash detection in a manner which is far less robust than
> > what the MTD NAND subsystem already does
> 
> Hm, do you mean there is some MTD driver detecting NAND flash on BCMA?
> I wasn't aware of that. Grepping drivers/mtd for "bcma" didn't give me
> anything. Can you say something more about this?

No I meant that all the ID code decoding to determine the chip size, 
manufacturer etc ... is already well handled within MTD.

> 
> 
> > I guess that this driver enables you do some stuff on your router, but 
clearly
> > you should aim at writing a real MTD driver instead of having such an ad-
hoc
> > solution.
> 
> That "do some stuff" means almost nothing right now. My plan (as
> explained earlier) is to write MTD driver.

Ok, well maybe I should just let you write your MTD driver and we see how that 
interfaces with this current patch.

> 
> -- 
> Rafał

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2] bcma: add basic NAND flash driver
  2012-08-05 20:03 [PATCH V2] bcma: add basic NAND flash driver Rafał Miłecki
  2012-08-05 20:40 ` Florian Fainelli
@ 2012-08-06  9:12 ` Florian Fainelli
  1 sibling, 0 replies; 7+ messages in thread
From: Florian Fainelli @ 2012-08-06  9:12 UTC (permalink / raw)
  To: Rafał Miłecki
  Cc: linux-wireless, John W. Linville, Larry Finger, Hauke Mehrtens

Hello Rafal,

Some minor comments below.

On Sunday 05 August 2012 22:03:07 Rafał Miłecki wrote:
> This is basic driver for NAND flash memory that allows reading it's
> content. It was succesfully tested on Netgear WNDR4500 which is BCM4706
> based router.
> 
> This driver has been written using specs at:
> http://bcm-v4.sipsolutions.net/NAND_Flash
> 
> Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
> CC: Larry Finger <Larry.Finger@lwfinger.net>
> CC: Hauke Mehrtens <hauke@hauke-m.de>
> ---
> V2: Split bcma_nflash_read into three functions as suggested by Hauke
>     Use bcma_* for printing
> ---
>  drivers/bcma/Kconfig                        |    4 +-
>  drivers/bcma/bcma_private.h                 |    2 +-
>  drivers/bcma/driver_chipcommon_nflash.c     |  402 
++++++++++++++++++++++++++-
>  drivers/bcma/driver_chipcommon_pmu.c        |    2 +-
>  include/linux/bcma/bcma_driver_chipcommon.h |   88 ++++++
>  5 files changed, 490 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/bcma/Kconfig b/drivers/bcma/Kconfig
> index 8d1f777..a58d7a9 100644
> --- a/drivers/bcma/Kconfig
> +++ b/drivers/bcma/Kconfig
> @@ -53,8 +53,8 @@ config BCMA_SFLASH
>  	default y
>  
>  config BCMA_NFLASH
> -	bool
> -	depends on BCMA_DRIVER_MIPS && BROKEN
> +	bool "Support for NAND flash"
> +	depends on BCMA_DRIVER_MIPS
>  	default y
>  
>  config BCMA_DRIVER_GMAC_CMN
> diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h
> index 3cf9cc9..9895c7e 100644
> --- a/drivers/bcma/bcma_private.h
> +++ b/drivers/bcma/bcma_private.h
> @@ -68,7 +68,7 @@ int bcma_nflash_init(struct bcma_drv_cc *cc);
>  #else
>  static inline int bcma_nflash_init(struct bcma_drv_cc *cc)
>  {
> -	bcma_err(cc->core->bus, "NAND flash not supported\n");
> +	bcma_err(cc->core->bus, "NAND flash support not enabled\n");
>  	return 0;
>  }
>  #endif /* CONFIG_BCMA_NFLASH */
> diff --git a/drivers/bcma/driver_chipcommon_nflash.c 
b/drivers/bcma/driver_chipcommon_nflash.c
> index 574d624..0d11e6f 100644
> --- a/drivers/bcma/driver_chipcommon_nflash.c
> +++ b/drivers/bcma/driver_chipcommon_nflash.c
> @@ -2,18 +2,412 @@
>   * Broadcom specific AMBA
>   * ChipCommon NAND flash interface
>   *
> + * Copyright 2012, Rafał Miłecki <zajec5@gmail.com>
> + *
>   * Licensed under the GNU/GPL. See COPYING for details.
>   */
>  
> +#include "bcma_private.h"
>  #include <linux/bcma/bcma.h>
>  #include <linux/bcma/bcma_driver_chipcommon.h>
>  #include <linux/delay.h>
>  
> -#include "bcma_private.h"
> +/* 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
>  
> -/* Initialize NAND flash access */
> -int bcma_nflash_init(struct bcma_drv_cc *cc)
> +/**************************************************
> + * 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_REV38_NAND_BOOT)
> +			return;
> +		if (enable)
> +			bcma_chipco_chipctl_maskset(cc, 1, ~0, 0x10000);
> +		else
> +			bcma_chipco_chipctl_maskset(cc, 1, ~0x10000, 0);
> +	}
> +}

Can you define a constant for this bit?

> +
> +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) {
> +		bcma_err(cc->core->bus, "NFLASH control command not ready!\n");
> +		return -EBUSY;
> +	}
> +	return 0;

Would not it be simple to check for i == NFLASH_READY_RETRIES instead of 
setting the value of i in the for loop? That sounds more natural. You might 
also want to add a cpu_relax() right after reading the NFLASH_CTL register.

> +}
> +
> +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) {
> +					bcma_err(cc->core->bus, "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;
> +		}
> +	}

Use constants here too.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2] bcma: add basic NAND flash driver
  2012-08-06  8:50     ` Florian Fainelli
@ 2012-08-06 12:08       ` Rafał Miłecki
  2012-08-06 23:27         ` Hauke Mehrtens
  0 siblings, 1 reply; 7+ messages in thread
From: Rafał Miłecki @ 2012-08-06 12:08 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: linux-wireless, John W. Linville, Larry Finger, Hauke Mehrtens

2012/8/6 Florian Fainelli <florian@openwrt.org>:
> On Monday 06 August 2012 08:50:35 Rafał Miłecki wrote:
>> Thanks for looking at this!
>>
>> 2012/8/5 Florian Fainelli <florian@openwrt.org>:
>> > Le dimanche 05 août 2012 22:03:07, Rafał Miłecki a écrit :
>> >> This is basic driver for NAND flash memory that allows reading it's
>> >> content. It was succesfully tested on Netgear WNDR4500 which is BCM4706
>> >> based router.
>> >>
>> >> This driver has been written using specs at:
>> >> http://bcm-v4.sipsolutions.net/NAND_Flash
>> >
>> > The big problem that I see with your driver is that it does not interface
> with
>> > the MTD subsystem, and therefore:
>> >
>> > - does not conform to the MTD API for reading pages, blocks etc...
>>
>> My idea for bcma responsibilities consists of:
>> 1) Detecting hardware
>> 2) Providing basic access to it
>> This is what (I believe) I provided with that submitted patch.
>>
>> I'm not registering MTD driver directly in bcma, just like we don't
>> register ieee80211 device for 80211 core or netdev for ETH core.
>>
>> After providing basic/low-level access in bcma my plan is to write
>> real MTD driver. In this driver I could make use of functions from
>> bcma_chipcommon_nflash.[ch].
>>
>> Does it sound better now?
>
> A little, but I still wonder why this would be needed at all, unless you
> cannot rely on MTD because you are doing stuff very early with the Flash.
> I find perfectly legitimate to export some bcma-specific symbols for accessing
> the NAND flash controller, but am a little more dubious about duplicating the
> reading/detection.

I've tested nand_scan_ident and it seems to be working for NAND flash
in my BCM4706. I'm pretty sure we can use it for MTD driver without
re-implementing all that detecting stuff.

Unfortunately I'm not sure what to do about early init. Do you know if
we may need NAND flash access during early init? Is reading nvram
during early init important? I guess devices with nvram placed on NAND
flash will become common sooner or later.

If we want to read nvram during early init, we may not be able to rely
on MTD driver. Oh and it would cause additional problems like lacking
kzalloc and maybe lacking sleep on some devices. Should we call
nand_scan_ident from bcma then? I'm not sure if I like this idea.

Any comments/ideas? Hauke?


>> > - duplicates NAND flash detection in a manner which is far less robust than
>> > what the MTD NAND subsystem already does
>>
>> Hm, do you mean there is some MTD driver detecting NAND flash on BCMA?
>> I wasn't aware of that. Grepping drivers/mtd for "bcma" didn't give me
>> anything. Can you say something more about this?
>
> No I meant that all the ID code decoding to determine the chip size,
> manufacturer etc ... is already well handled within MTD.

Yeah, it detected my flash more or less correctly:

NAND device: Manufacturer ID: 0xec, Chip ID: 0xf1 (Samsung NAND 128MiB
3,3V 8-bit), page size: 2048, OOB size: 64


>> > I guess that this driver enables you do some stuff on your router, but
> clearly
>> > you should aim at writing a real MTD driver instead of having such an ad-
> hoc
>> > solution.
>>
>> That "do some stuff" means almost nothing right now. My plan (as
>> explained earlier) is to write MTD driver.
>
> Ok, well maybe I should just let you write your MTD driver and we see how that
> interfaces with this current patch.

-- 
Rafał

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH V2] bcma: add basic NAND flash driver
  2012-08-06 12:08       ` Rafał Miłecki
@ 2012-08-06 23:27         ` Hauke Mehrtens
  0 siblings, 0 replies; 7+ messages in thread
From: Hauke Mehrtens @ 2012-08-06 23:27 UTC (permalink / raw)
  To: Rafał Miłecki
  Cc: Florian Fainelli, linux-wireless, John W. Linville, Larry Finger

On 08/06/2012 02:08 PM, Rafał Miłecki wrote:
> 2012/8/6 Florian Fainelli <florian@openwrt.org>:
>> On Monday 06 August 2012 08:50:35 Rafał Miłecki wrote:
>>> Thanks for looking at this!
>>>
>>> 2012/8/5 Florian Fainelli <florian@openwrt.org>:
>>>> Le dimanche 05 août 2012 22:03:07, Rafał Miłecki a écrit :
>>>>> This is basic driver for NAND flash memory that allows reading it's
>>>>> content. It was succesfully tested on Netgear WNDR4500 which is BCM4706
>>>>> based router.
>>>>>
>>>>> This driver has been written using specs at:
>>>>> http://bcm-v4.sipsolutions.net/NAND_Flash
>>>>
>>>> The big problem that I see with your driver is that it does not interface
>> with
>>>> the MTD subsystem, and therefore:
>>>>
>>>> - does not conform to the MTD API for reading pages, blocks etc...
>>>
>>> My idea for bcma responsibilities consists of:
>>> 1) Detecting hardware
>>> 2) Providing basic access to it
>>> This is what (I believe) I provided with that submitted patch.
>>>
>>> I'm not registering MTD driver directly in bcma, just like we don't
>>> register ieee80211 device for 80211 core or netdev for ETH core.
>>>
>>> After providing basic/low-level access in bcma my plan is to write
>>> real MTD driver. In this driver I could make use of functions from
>>> bcma_chipcommon_nflash.[ch].
>>>
>>> Does it sound better now?
>>
>> A little, but I still wonder why this would be needed at all, unless you
>> cannot rely on MTD because you are doing stuff very early with the Flash.
>> I find perfectly legitimate to export some bcma-specific symbols for accessing
>> the NAND flash controller, but am a little more dubious about duplicating the
>> reading/detection.
> 
> I've tested nand_scan_ident and it seems to be working for NAND flash
> in my BCM4706. I'm pretty sure we can use it for MTD driver without
> re-implementing all that detecting stuff.
> 
> Unfortunately I'm not sure what to do about early init. Do you know if
> we may need NAND flash access during early init? Is reading nvram
> during early init important? I guess devices with nvram placed on NAND
> flash will become common sooner or later.
> 
> If we want to read nvram during early init, we may not be able to rely
> on MTD driver. Oh and it would cause additional problems like lacking
> kzalloc and maybe lacking sleep on some devices. Should we call
> nand_scan_ident from bcma then? I'm not sure if I like this idea.
> 
> Any comments/ideas? Hauke?

We need access to the nvram to get the sprom and for a router model
detection to run some special cases for some devices. This router model
is currently needed for the flash partition parsing for serial and
parallel flash, as there is no partition table and the factory firmware
has the partition layout stored on the kernel image itself. In addition
we need the router module for the Ethernet drivers and some other driver
loaded later.

We can not use normal *alloc functions in early boot, like when the bcma
subsystem is first initiated, I do not know if sleep is not working but
udelay should still work at that time.

Just devices with chip common core rev 38 and not the BCM4706 are
supporting booting form nand flash according to the Broadcom Source
code. The code needed to implement nflash_checkbadb() to check if the
block is ok just for cc rev 38 is not big, we could duplicate it. The
code needed to detect the blocksize is bigger, but we could also use the
smallest block size possible (8K) and to more checks than necessary
(512) which is also not so many.

For code sharing we could also export the two needed functions from the
mtd driver and abuse them in the arch mode, if reading nvram from nand
flash is only possible when the mtd driver is also installed nobody will
care, but the code would not look nicely, but we should use the nand
flash functions the kernel already provides.

Does anybody have a bcm47xx device which boots from nand flash? The
WNR3500Lv2 is the only device with nand boot I know of.

I am for writing a mtd driver using the nand flash functions the kernel
provides and store all the code in drivers/mtd/ for that. The arch code
should then use two of these functions for nvram read if possible.

>>>> - duplicates NAND flash detection in a manner which is far less robust than
>>>> what the MTD NAND subsystem already does
>>>
>>> Hm, do you mean there is some MTD driver detecting NAND flash on BCMA?
>>> I wasn't aware of that. Grepping drivers/mtd for "bcma" didn't give me
>>> anything. Can you say something more about this?
>>
>> No I meant that all the ID code decoding to determine the chip size,
>> manufacturer etc ... is already well handled within MTD.
> 
> Yeah, it detected my flash more or less correctly:
> 
> NAND device: Manufacturer ID: 0xec, Chip ID: 0xf1 (Samsung NAND 128MiB
> 3,3V 8-bit), page size: 2048, OOB size: 64
> 
> 
>>>> I guess that this driver enables you do some stuff on your router, but
>> clearly
>>>> you should aim at writing a real MTD driver instead of having such an ad-
>> hoc
>>>> solution.
>>>
>>> That "do some stuff" means almost nothing right now. My plan (as
>>> explained earlier) is to write MTD driver.
>>
>> Ok, well maybe I should just let you write your MTD driver and we see how that
>> interfaces with this current patch.
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2012-08-06 23:27 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-08-05 20:03 [PATCH V2] bcma: add basic NAND flash driver Rafał Miłecki
2012-08-05 20:40 ` Florian Fainelli
2012-08-06  6:50   ` Rafał Miłecki
2012-08-06  8:50     ` Florian Fainelli
2012-08-06 12:08       ` Rafał Miłecki
2012-08-06 23:27         ` Hauke Mehrtens
2012-08-06  9:12 ` Florian Fainelli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).