bcm53xx: update the NAND driver

This adds some updates to the NAND driver and refreshed the config.

Most of these changes are done in preparation for mainling it.

Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>

SVN-Revision: 43546
This commit is contained in:
Hauke Mehrtens 2014-12-07 21:55:21 +00:00
parent 664ae86dde
commit 7a2f186d70
2 changed files with 112 additions and 132 deletions

View File

@ -22,10 +22,9 @@
nand-objs := nand_base.o nand_bbt.o nand-objs := nand_base.o nand_bbt.o
--- /dev/null --- /dev/null
+++ b/drivers/mtd/nand/bcm_nand.c +++ b/drivers/mtd/nand/bcm_nand.c
@@ -0,0 +1,1590 @@ @@ -0,0 +1,1580 @@
+/* +/*
+ * Nortstar NAND controller driver + * Nortstar NAND controller driver
+ * for Linux NAND library and MTD interface
+ * + *
+ * (c) Broadcom, Inc. 2012 All Rights Reserved. + * (c) Broadcom, Inc. 2012 All Rights Reserved.
+ * Copyright 2014 Hauke Mehrtens <hauke@hauke-m.de> + * Copyright 2014 Hauke Mehrtens <hauke@hauke-m.de>
@ -68,9 +67,6 @@
+ +
+#define NANDC_MAX_CHIPS 2 /* Only 2 CSn supported in NorthStar */ +#define NANDC_MAX_CHIPS 2 /* Only 2 CSn supported in NorthStar */
+ +
+#define DRV_NAME "bcmnand"
+#define DRV_DESC "Northstar on-chip NAND Flash Controller driver"
+
+/* +/*
+ * Driver private control structure + * Driver private control structure
+ */ + */
@ -421,6 +417,7 @@
+static int bcmnand_hw_ecc_layout(struct bcmnand_ctrl *ctrl) +static int bcmnand_hw_ecc_layout(struct bcmnand_ctrl *ctrl)
+{ +{
+ struct nand_ecclayout *layout; + struct nand_ecclayout *layout;
+ struct device *dev = &ctrl->core->dev;
+ unsigned int i, j, k; + unsigned int i, j, k;
+ unsigned int ecc_per_sec, oob_per_sec; + unsigned int ecc_per_sec, oob_per_sec;
+ unsigned int bbm_pos = ctrl->nand.badblockpos; + unsigned int bbm_pos = ctrl->nand.badblockpos;
@ -453,8 +450,8 @@
+ /* Return an error if calculated ECC leaves no room for OOB */ + /* Return an error if calculated ECC leaves no room for OOB */
+ if ((ctrl->sec_per_page_shift != 0 && ecc_per_sec >= oob_per_sec) || + if ((ctrl->sec_per_page_shift != 0 && ecc_per_sec >= oob_per_sec) ||
+ (ctrl->sec_per_page_shift == 0 && ecc_per_sec >= (oob_per_sec - 1))) { + (ctrl->sec_per_page_shift == 0 && ecc_per_sec >= (oob_per_sec - 1))) {
+ pr_err("%s: ECC level %d too high, leaves no room for OOB data\n", + dev_err(dev, "ECC level %d too high, leaves no room for OOB data\n",
+ DRV_NAME, ctrl->ecc_level); + ctrl->ecc_level);
+ return -EINVAL; + return -EINVAL;
+ } + }
+ +
@ -486,8 +483,8 @@
+ +
+ /* Check that HW ECC does not overlap bad-block marker */ + /* Check that HW ECC does not overlap bad-block marker */
+ if (bbm_pos == layout->eccpos[i]) { + if (bbm_pos == layout->eccpos[i]) {
+ pr_err("%s: ECC level %d too high, HW ECC collides with bad-block marker position\n", + dev_err(dev, "ECC level %d too high, HW ECC collides with bad-block marker position\n",
+ DRV_NAME, ctrl->ecc_level); + ctrl->ecc_level);
+ return -EINVAL; + return -EINVAL;
+ } + }
+ } + }
@ -530,18 +527,17 @@
+ ctrl->nand.ecc.layout = layout; + ctrl->nand.ecc.layout = layout;
+ +
+ /* Output layout for debugging */ + /* Output layout for debugging */
+ pr_debug("%s: Spare area=%d eccbytes %d, ecc bytes located at:\n", + dev_dbg(dev, "Spare area=%d eccbytes %d, ecc bytes located at:\n",
+ DRV_NAME, ctrl->mtd.oobsize, layout->eccbytes); + ctrl->mtd.oobsize, layout->eccbytes);
+ for (i = j = 0; + for (i = j = 0;
+ i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes; i++) + i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes; i++)
+ pr_debug(" %d", layout->eccpos[i]); + pr_debug(" %d", layout->eccpos[i]);
+ pr_debug("\n");
+ +
+ pr_debug("\n%s: Available %d bytes at (off,len):\n", DRV_NAME, + dev_dbg(dev, "Available %d bytes at (off,len):\n", layout->oobavail);
+ layout->oobavail);
+ for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++) + for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++)
+ pr_debug("(%d,%d) ", layout->oobfree[i].offset, + pr_debug("(%d,%d) ", layout->oobfree[i].offset,
+ layout->oobfree[i].length); + layout->oobfree[i].length);
+
+ pr_debug("\n"); + pr_debug("\n");
+ +
+ return 0; + return 0;
@ -811,8 +807,7 @@
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
+ +
+ memcpy(chip->oob_poi + sector * spare_per_sec, + memcpy(chip->oob_poi + sector * spare_per_sec,
+ ctrl_spare, + ctrl_spare, spare_per_sec);
+ spare_per_sec);
+ +
+ /* Return to Big Endian mode for commands etc */ + /* Return to Big Endian mode for commands etc */
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
@ -861,8 +856,7 @@
+ /* Set controller to Little Endian mode for copying */ + /* Set controller to Little Endian mode for copying */
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
+ +
+ memcpy(ctrl_spare, + memcpy(ctrl_spare, chip->oob_poi + sector * spare_per_sec,
+ chip->oob_poi + sector * spare_per_sec,
+ spare_per_sec); + spare_per_sec);
+ +
+ /* Return to Big Endian mode for commands etc */ + /* Return to Big Endian mode for commands etc */
@ -1149,6 +1143,7 @@
+{ +{
+ struct nand_chip *nand = mtd->priv; + struct nand_chip *nand = mtd->priv;
+ struct bcmnand_ctrl *ctrl = nand->priv; + struct bcmnand_ctrl *ctrl = nand->priv;
+ struct device *dev = &ctrl->core->dev;
+ uint8_t b = ~0; + uint8_t b = ~0;
+ +
+ switch (ctrl->last_cmd) { + switch (ctrl->last_cmd) {
@ -1167,8 +1162,8 @@
+ b = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS); + b = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS);
+ break; + break;
+ default: + default:
+ pr_err("%s: got unkown command: 0x%x in %s\n", DRV_NAME, + dev_err(dev, "got unkown command: 0x%x in read_byte\n",
+ ctrl->last_cmd, __func__); + ctrl->last_cmd);
+ } + }
+ return b; + return b;
+} +}
@ -1212,6 +1207,7 @@
+{ +{
+ struct nand_chip *nand = mtd->priv; + struct nand_chip *nand = mtd->priv;
+ struct bcmnand_ctrl *ctrl = nand->priv; + struct bcmnand_ctrl *ctrl = nand->priv;
+ struct device *dev = &ctrl->core->dev;
+ u64 nand_addr; + u64 nand_addr;
+ unsigned int to = 1; + unsigned int to = 1;
+ +
@ -1291,8 +1287,8 @@
+ return; + return;
+ +
+ default: + default:
+ pr_err("%s: got unkown command: 0x%x in %s\n", DRV_NAME, + dev_err(dev, "got unkown command: 0x%x in cmdfunc\n",
+ ctrl->last_cmd, __func__); + ctrl->last_cmd);
+ } + }
+ +
+ /* Wait for command to complete */ + /* Wait for command to complete */
@ -1304,6 +1300,7 @@
+{ +{
+ struct nand_chip *nand = mtd->priv; + struct nand_chip *nand = mtd->priv;
+ struct bcmnand_ctrl *ctrl = nand->priv; + struct bcmnand_ctrl *ctrl = nand->priv;
+ struct device *dev = &ctrl->core->dev;
+ bool sector_1k = false; + bool sector_1k = false;
+ unsigned int chip_num = 0; + unsigned int chip_num = 0;
+ int ecc_level = 0; + int ecc_level = 0;
@ -1341,13 +1338,13 @@
+ return -ENOENT; + return -ENOENT;
+ +
+ if ((ctrl->sector_size_shift > 9) != (sector_1k == 1)) { + if ((ctrl->sector_size_shift > 9) != (sector_1k == 1)) {
+ pr_info("%s: sector size adjusted to 1k\n", DRV_NAME); + dev_info(dev, "sector size adjusted to 1k\n");
+ sector_1k = 1; + sector_1k = 1;
+ } + }
+ +
+ if (ecc_level != ctrl->ecc_level) { + if (ecc_level != ctrl->ecc_level) {
+ pr_info("%s: ECC level adjusted from %u to %u\n", + dev_info(dev, "ECC level adjusted from %u to %u\n",
+ DRV_NAME, ecc_level, ctrl->ecc_level); + ecc_level, ctrl->ecc_level);
+ ecc_level = ctrl->ecc_level; + ecc_level = ctrl->ecc_level;
+ } + }
+ +
@ -1384,8 +1381,7 @@
+ if (!(nand->options & NAND_ROM)) + if (!(nand->options & NAND_ROM))
+ bcmnand_reg_write(ctrl, NANDC_CS_NAND_WP, 0); + bcmnand_reg_write(ctrl, NANDC_CS_NAND_WP, 0);
+ +
+ pr_debug("%s: layout.oobavail=%d\n", DRV_NAME, + dev_dbg(dev, "layout.oobavail=%d\n", nand->ecc.layout->oobavail);
+ nand->ecc.layout->oobavail);
+ +
+ ret = nand_scan_tail(mtd); + ret = nand_scan_tail(mtd);
+ +
@ -1395,8 +1391,8 @@
+ return -EIO; + return -EIO;
+ +
+ /* Spit out some key chip parameters as detected by nand_base */ + /* Spit out some key chip parameters as detected by nand_base */
+ pr_debug("%s: erasesize=%d writesize=%d oobsize=%d page_shift=%d badblockpos=%d badblockbits=%d\n", + dev_dbg(dev, "erasesize=%d writesize=%d oobsize=%d page_shift=%d badblockpos=%d badblockbits=%d\n",
+ DRV_NAME, mtd->erasesize, mtd->writesize, mtd->oobsize, + mtd->erasesize, mtd->writesize, mtd->oobsize,
+ nand->page_shift, nand->badblockpos, nand->badblockbits); + nand->page_shift, nand->badblockpos, nand->badblockbits);
+ +
+ return ret; + return ret;
@ -1410,7 +1406,7 @@
+ unsigned int chip; + unsigned int chip;
+ struct nand_chip *nand; + struct nand_chip *nand;
+ struct mtd_info *mtd; + struct mtd_info *mtd;
+ unsigned int n = 0; + struct device *dev = &ctrl->core->dev;
+ int ret; + int ret;
+ +
+ /* Software variables init */ + /* Software variables init */
@ -1421,14 +1417,14 @@
+ +
+ mtd->priv = nand; + mtd->priv = nand;
+ mtd->owner = THIS_MODULE; + mtd->owner = THIS_MODULE;
+ mtd->name = DRV_NAME; + mtd->name = KBUILD_MODNAME;
+ +
+ nand->priv = ctrl; + nand->priv = ctrl;
+ +
+ nand->chip_delay = 5; /* not used */ + nand->chip_delay = 5; /* not used */
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)~0L; + nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)~0L;
+ +
+ if (bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_WIDTH(n))) + if (bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_WIDTH(0)))
+ nand->options |= NAND_BUSWIDTH_16; + nand->options |= NAND_BUSWIDTH_16;
+ nand->options |= NAND_SKIP_BBTSCAN; /* Dont need BBTs */ + nand->options |= NAND_SKIP_BBTSCAN; /* Dont need BBTs */
+ +
@ -1456,38 +1452,39 @@
+ +
+ /* Print out current chip config */ + /* Print out current chip config */
+ for (chip = 0; chip < NANDC_MAX_CHIPS; chip++) { + for (chip = 0; chip < NANDC_MAX_CHIPS; chip++) {
+ pr_debug("%s: chip[%d]: size=%#x block=%#x page=%#x ecc_level=%#x\n", + dev_dbg(dev, "chip[%d]: size=%#x block=%#x page=%#x ecc_level=%#x\n",
+ DRV_NAME, chip, + chip,
+ bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_SIZE(chip)), + bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_SIZE(chip)),
+ bcmnand_reg_read(ctrl, NANDC_CONFIG_BLK_SIZE(chip)), + bcmnand_reg_read(ctrl, NANDC_CONFIG_BLK_SIZE(chip)),
+ bcmnand_reg_read(ctrl, NANDC_CONFIG_PAGE_SIZE(chip)), + bcmnand_reg_read(ctrl, NANDC_CONFIG_PAGE_SIZE(chip)),
+ bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip))); + bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip)));
+ } + }
+ +
+ pr_debug("%s: Nand controller is reads=%d\n", DRV_NAME, + dev_dbg(dev, "Nand controller is reads=%d\n",
+ bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY)); + bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY));
+ +
+ ret = bcmnand_scan(mtd); + ret = bcmnand_scan(mtd);
+ if (ret) { + if (ret) {
+ pr_err("%s: scanning the nand flash chip failed with %i\n", + dev_err(dev, "scanning the nand flash chip failed with %i\n",
+ DRV_NAME, ret); + ret);
+ return ret; + return ret;
+ } + }
+ +
+ return 0; + return 0;
+} +}
+ +
+static int __init bcmnand_idm_init(struct bcmnand_ctrl *ctrl) +static int bcmnand_idm_init(struct bcmnand_ctrl *ctrl)
+{ +{
+ int irq_off; + int irq_off;
+ unsigned int retries = 0x1000; + unsigned int retries = 0x1000;
+ struct device *dev = &ctrl->core->dev;
+ +
+ if (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) + if (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET))
+ pr_err("%s: stuck in reset\n", DRV_NAME); + dev_info(dev, "stuck in reset\n");
+ +
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 1); + bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 1);
+ if (!bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) { + if (!bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) {
+ pr_err("%s: reset of failed\n", DRV_NAME); + dev_err(dev, "reset of failed\n");
+ return -EIO; + return -EIO;
+ } + }
+ +
@ -1496,8 +1493,7 @@
+ cpu_relax(); + cpu_relax();
+ usleep_range(100, 150); + usleep_range(100, 150);
+ if (!(retries--)) { + if (!(retries--)) {
+ pr_err("%s: did not came back from reset\n", + dev_err(dev, "did not came back from reset\n");
+ DRV_NAME);
+ return -ETIMEDOUT; + return -ETIMEDOUT;
+ } + }
+ } + }
@ -1506,7 +1502,7 @@
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
+ udelay(10); + udelay(10);
+ +
+ pr_info("%s: NAND Controller rev %d.%d\n", DRV_NAME, + dev_info(dev, "NAND Controller rev %d.%d\n",
+ bcmnand_reg_read(ctrl, NANDC_REV_MAJOR), + bcmnand_reg_read(ctrl, NANDC_REV_MAJOR),
+ bcmnand_reg_read(ctrl, NANDC_REV_MINOR)); + bcmnand_reg_read(ctrl, NANDC_REV_MINOR));
+ +
@ -1543,14 +1539,14 @@
+ for (i = 0; i < NANDC_IRQ_NUM; i++) { + for (i = 0; i < NANDC_IRQ_NUM; i++) {
+ irq = bcma_core_irq(core, i); + irq = bcma_core_irq(core, i);
+ if (!irq) { + if (!irq) {
+ pr_err("%s: IRQ no available irq: %i (idx: %i)\n", + dev_err(dev, "IRQ idx %i not available\n", i);
+ DRV_NAME, irq, i); + return -ENOENT;
+ return res;
+ } + }
+ res = devm_request_irq(dev, irq, bcmnand_isr, 0, DRV_NAME, ctrl); + res = devm_request_irq(dev, irq, bcmnand_isr, 0,
+ KBUILD_MODNAME, ctrl);
+ if (res < 0) { + if (res < 0) {
+ pr_err("%s: problem requesting irq: %i (idx: %i)\n", + dev_err(dev, "problem requesting irq: %i (idx: %i)\n",
+ DRV_NAME, irq, i); + irq, i);
+ return res; + return res;
+ } + }
+ } + }
@ -1565,7 +1561,7 @@
+ +
+ res = mtd_device_parse_register(&ctrl->mtd, part_probes, NULL, NULL, 0); + res = mtd_device_parse_register(&ctrl->mtd, part_probes, NULL, NULL, 0);
+ if (res) { + if (res) {
+ pr_err("%s: Failed to register MTD device: %d\n", DRV_NAME, res); + dev_err(dev, "Failed to register MTD device: %d\n", res);
+ return res; + return res;
+ } + }
+ return 0; + return 0;
@ -1593,14 +1589,7 @@
+ +
+static int __init bcmnand_init(void) +static int __init bcmnand_init(void)
+{ +{
+ int err; + return bcma_driver_register(&bcmnand_bcma_driver);
+
+ err = bcma_driver_register(&bcmnand_bcma_driver);
+ if (err)
+ return err;
+ pr_info("%s: Broadcom NAND Controller driver loaded\n", DRV_NAME);
+
+ return 0;
+} +}
+ +
+static void __exit bcmnand_exit(void) +static void __exit bcmnand_exit(void)
@ -1612,4 +1601,5 @@
+module_exit(bcmnand_exit) +module_exit(bcmnand_exit)
+ +
+MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRV_DESC); +MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Northstar on-chip NAND Flash Controller driver");

View File

@ -22,10 +22,9 @@
nand-objs := nand_base.o nand_bbt.o nand_timings.o nand-objs := nand_base.o nand_bbt.o nand_timings.o
--- /dev/null --- /dev/null
+++ b/drivers/mtd/nand/bcm_nand.c +++ b/drivers/mtd/nand/bcm_nand.c
@@ -0,0 +1,1590 @@ @@ -0,0 +1,1580 @@
+/* +/*
+ * Nortstar NAND controller driver + * Nortstar NAND controller driver
+ * for Linux NAND library and MTD interface
+ * + *
+ * (c) Broadcom, Inc. 2012 All Rights Reserved. + * (c) Broadcom, Inc. 2012 All Rights Reserved.
+ * Copyright 2014 Hauke Mehrtens <hauke@hauke-m.de> + * Copyright 2014 Hauke Mehrtens <hauke@hauke-m.de>
@ -68,9 +67,6 @@
+ +
+#define NANDC_MAX_CHIPS 2 /* Only 2 CSn supported in NorthStar */ +#define NANDC_MAX_CHIPS 2 /* Only 2 CSn supported in NorthStar */
+ +
+#define DRV_NAME "bcmnand"
+#define DRV_DESC "Northstar on-chip NAND Flash Controller driver"
+
+/* +/*
+ * Driver private control structure + * Driver private control structure
+ */ + */
@ -421,6 +417,7 @@
+static int bcmnand_hw_ecc_layout(struct bcmnand_ctrl *ctrl) +static int bcmnand_hw_ecc_layout(struct bcmnand_ctrl *ctrl)
+{ +{
+ struct nand_ecclayout *layout; + struct nand_ecclayout *layout;
+ struct device *dev = &ctrl->core->dev;
+ unsigned int i, j, k; + unsigned int i, j, k;
+ unsigned int ecc_per_sec, oob_per_sec; + unsigned int ecc_per_sec, oob_per_sec;
+ unsigned int bbm_pos = ctrl->nand.badblockpos; + unsigned int bbm_pos = ctrl->nand.badblockpos;
@ -453,8 +450,8 @@
+ /* Return an error if calculated ECC leaves no room for OOB */ + /* Return an error if calculated ECC leaves no room for OOB */
+ if ((ctrl->sec_per_page_shift != 0 && ecc_per_sec >= oob_per_sec) || + if ((ctrl->sec_per_page_shift != 0 && ecc_per_sec >= oob_per_sec) ||
+ (ctrl->sec_per_page_shift == 0 && ecc_per_sec >= (oob_per_sec - 1))) { + (ctrl->sec_per_page_shift == 0 && ecc_per_sec >= (oob_per_sec - 1))) {
+ pr_err("%s: ECC level %d too high, leaves no room for OOB data\n", + dev_err(dev, "ECC level %d too high, leaves no room for OOB data\n",
+ DRV_NAME, ctrl->ecc_level); + ctrl->ecc_level);
+ return -EINVAL; + return -EINVAL;
+ } + }
+ +
@ -486,8 +483,8 @@
+ +
+ /* Check that HW ECC does not overlap bad-block marker */ + /* Check that HW ECC does not overlap bad-block marker */
+ if (bbm_pos == layout->eccpos[i]) { + if (bbm_pos == layout->eccpos[i]) {
+ pr_err("%s: ECC level %d too high, HW ECC collides with bad-block marker position\n", + dev_err(dev, "ECC level %d too high, HW ECC collides with bad-block marker position\n",
+ DRV_NAME, ctrl->ecc_level); + ctrl->ecc_level);
+ return -EINVAL; + return -EINVAL;
+ } + }
+ } + }
@ -530,18 +527,17 @@
+ ctrl->nand.ecc.layout = layout; + ctrl->nand.ecc.layout = layout;
+ +
+ /* Output layout for debugging */ + /* Output layout for debugging */
+ pr_debug("%s: Spare area=%d eccbytes %d, ecc bytes located at:\n", + dev_dbg(dev, "Spare area=%d eccbytes %d, ecc bytes located at:\n",
+ DRV_NAME, ctrl->mtd.oobsize, layout->eccbytes); + ctrl->mtd.oobsize, layout->eccbytes);
+ for (i = j = 0; + for (i = j = 0;
+ i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes; i++) + i < ARRAY_SIZE(layout->eccpos) && i < layout->eccbytes; i++)
+ pr_debug(" %d", layout->eccpos[i]); + pr_debug(" %d", layout->eccpos[i]);
+ pr_debug("\n");
+ +
+ pr_debug("\n%s: Available %d bytes at (off,len):\n", DRV_NAME, + dev_dbg(dev, "Available %d bytes at (off,len):\n", layout->oobavail);
+ layout->oobavail);
+ for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++) + for (i = 0; i < ARRAY_SIZE(layout->oobfree); i++)
+ pr_debug("(%d,%d) ", layout->oobfree[i].offset, + pr_debug("(%d,%d) ", layout->oobfree[i].offset,
+ layout->oobfree[i].length); + layout->oobfree[i].length);
+
+ pr_debug("\n"); + pr_debug("\n");
+ +
+ return 0; + return 0;
@ -811,8 +807,7 @@
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
+ +
+ memcpy(chip->oob_poi + sector * spare_per_sec, + memcpy(chip->oob_poi + sector * spare_per_sec,
+ ctrl_spare, + ctrl_spare, spare_per_sec);
+ spare_per_sec);
+ +
+ /* Return to Big Endian mode for commands etc */ + /* Return to Big Endian mode for commands etc */
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
@ -861,8 +856,7 @@
+ /* Set controller to Little Endian mode for copying */ + /* Set controller to Little Endian mode for copying */
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 1);
+ +
+ memcpy(ctrl_spare, + memcpy(ctrl_spare, chip->oob_poi + sector * spare_per_sec,
+ chip->oob_poi + sector * spare_per_sec,
+ spare_per_sec); + spare_per_sec);
+ +
+ /* Return to Big Endian mode for commands etc */ + /* Return to Big Endian mode for commands etc */
@ -1149,6 +1143,7 @@
+{ +{
+ struct nand_chip *nand = mtd->priv; + struct nand_chip *nand = mtd->priv;
+ struct bcmnand_ctrl *ctrl = nand->priv; + struct bcmnand_ctrl *ctrl = nand->priv;
+ struct device *dev = &ctrl->core->dev;
+ uint8_t b = ~0; + uint8_t b = ~0;
+ +
+ switch (ctrl->last_cmd) { + switch (ctrl->last_cmd) {
@ -1167,8 +1162,8 @@
+ b = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS); + b = bcmnand_reg_read(ctrl, NANDC_INT_STAT_FLASH_STATUS);
+ break; + break;
+ default: + default:
+ pr_err("%s: got unkown command: 0x%x in %s\n", DRV_NAME, + dev_err(dev, "got unkown command: 0x%x in read_byte\n",
+ ctrl->last_cmd, __func__); + ctrl->last_cmd);
+ } + }
+ return b; + return b;
+} +}
@ -1212,6 +1207,7 @@
+{ +{
+ struct nand_chip *nand = mtd->priv; + struct nand_chip *nand = mtd->priv;
+ struct bcmnand_ctrl *ctrl = nand->priv; + struct bcmnand_ctrl *ctrl = nand->priv;
+ struct device *dev = &ctrl->core->dev;
+ u64 nand_addr; + u64 nand_addr;
+ unsigned int to = 1; + unsigned int to = 1;
+ +
@ -1291,8 +1287,8 @@
+ return; + return;
+ +
+ default: + default:
+ pr_err("%s: got unkown command: 0x%x in %s\n", DRV_NAME, + dev_err(dev, "got unkown command: 0x%x in cmdfunc\n",
+ ctrl->last_cmd, __func__); + ctrl->last_cmd);
+ } + }
+ +
+ /* Wait for command to complete */ + /* Wait for command to complete */
@ -1304,6 +1300,7 @@
+{ +{
+ struct nand_chip *nand = mtd->priv; + struct nand_chip *nand = mtd->priv;
+ struct bcmnand_ctrl *ctrl = nand->priv; + struct bcmnand_ctrl *ctrl = nand->priv;
+ struct device *dev = &ctrl->core->dev;
+ bool sector_1k = false; + bool sector_1k = false;
+ unsigned int chip_num = 0; + unsigned int chip_num = 0;
+ int ecc_level = 0; + int ecc_level = 0;
@ -1341,13 +1338,13 @@
+ return -ENOENT; + return -ENOENT;
+ +
+ if ((ctrl->sector_size_shift > 9) != (sector_1k == 1)) { + if ((ctrl->sector_size_shift > 9) != (sector_1k == 1)) {
+ pr_info("%s: sector size adjusted to 1k\n", DRV_NAME); + dev_info(dev, "sector size adjusted to 1k\n");
+ sector_1k = 1; + sector_1k = 1;
+ } + }
+ +
+ if (ecc_level != ctrl->ecc_level) { + if (ecc_level != ctrl->ecc_level) {
+ pr_info("%s: ECC level adjusted from %u to %u\n", + dev_info(dev, "ECC level adjusted from %u to %u\n",
+ DRV_NAME, ecc_level, ctrl->ecc_level); + ecc_level, ctrl->ecc_level);
+ ecc_level = ctrl->ecc_level; + ecc_level = ctrl->ecc_level;
+ } + }
+ +
@ -1384,8 +1381,7 @@
+ if (!(nand->options & NAND_ROM)) + if (!(nand->options & NAND_ROM))
+ bcmnand_reg_write(ctrl, NANDC_CS_NAND_WP, 0); + bcmnand_reg_write(ctrl, NANDC_CS_NAND_WP, 0);
+ +
+ pr_debug("%s: layout.oobavail=%d\n", DRV_NAME, + dev_dbg(dev, "layout.oobavail=%d\n", nand->ecc.layout->oobavail);
+ nand->ecc.layout->oobavail);
+ +
+ ret = nand_scan_tail(mtd); + ret = nand_scan_tail(mtd);
+ +
@ -1395,8 +1391,8 @@
+ return -EIO; + return -EIO;
+ +
+ /* Spit out some key chip parameters as detected by nand_base */ + /* Spit out some key chip parameters as detected by nand_base */
+ pr_debug("%s: erasesize=%d writesize=%d oobsize=%d page_shift=%d badblockpos=%d badblockbits=%d\n", + dev_dbg(dev, "erasesize=%d writesize=%d oobsize=%d page_shift=%d badblockpos=%d badblockbits=%d\n",
+ DRV_NAME, mtd->erasesize, mtd->writesize, mtd->oobsize, + mtd->erasesize, mtd->writesize, mtd->oobsize,
+ nand->page_shift, nand->badblockpos, nand->badblockbits); + nand->page_shift, nand->badblockpos, nand->badblockbits);
+ +
+ return ret; + return ret;
@ -1410,7 +1406,7 @@
+ unsigned int chip; + unsigned int chip;
+ struct nand_chip *nand; + struct nand_chip *nand;
+ struct mtd_info *mtd; + struct mtd_info *mtd;
+ unsigned int n = 0; + struct device *dev = &ctrl->core->dev;
+ int ret; + int ret;
+ +
+ /* Software variables init */ + /* Software variables init */
@ -1421,14 +1417,14 @@
+ +
+ mtd->priv = nand; + mtd->priv = nand;
+ mtd->owner = THIS_MODULE; + mtd->owner = THIS_MODULE;
+ mtd->name = DRV_NAME; + mtd->name = KBUILD_MODNAME;
+ +
+ nand->priv = ctrl; + nand->priv = ctrl;
+ +
+ nand->chip_delay = 5; /* not used */ + nand->chip_delay = 5; /* not used */
+ nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)~0L; + nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)~0L;
+ +
+ if (bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_WIDTH(n))) + if (bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_WIDTH(0)))
+ nand->options |= NAND_BUSWIDTH_16; + nand->options |= NAND_BUSWIDTH_16;
+ nand->options |= NAND_SKIP_BBTSCAN; /* Dont need BBTs */ + nand->options |= NAND_SKIP_BBTSCAN; /* Dont need BBTs */
+ +
@ -1456,38 +1452,39 @@
+ +
+ /* Print out current chip config */ + /* Print out current chip config */
+ for (chip = 0; chip < NANDC_MAX_CHIPS; chip++) { + for (chip = 0; chip < NANDC_MAX_CHIPS; chip++) {
+ pr_debug("%s: chip[%d]: size=%#x block=%#x page=%#x ecc_level=%#x\n", + dev_dbg(dev, "chip[%d]: size=%#x block=%#x page=%#x ecc_level=%#x\n",
+ DRV_NAME, chip, + chip,
+ bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_SIZE(chip)), + bcmnand_reg_read(ctrl, NANDC_CONFIG_CHIP_SIZE(chip)),
+ bcmnand_reg_read(ctrl, NANDC_CONFIG_BLK_SIZE(chip)), + bcmnand_reg_read(ctrl, NANDC_CONFIG_BLK_SIZE(chip)),
+ bcmnand_reg_read(ctrl, NANDC_CONFIG_PAGE_SIZE(chip)), + bcmnand_reg_read(ctrl, NANDC_CONFIG_PAGE_SIZE(chip)),
+ bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip))); + bcmnand_reg_read(ctrl, NANDC_ACC_CTRL_ECC_LEVEL(chip)));
+ } + }
+ +
+ pr_debug("%s: Nand controller is reads=%d\n", DRV_NAME, + dev_dbg(dev, "Nand controller is reads=%d\n",
+ bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY)); + bcmnand_reg_aread(ctrl, NANDC_IDM_IO_CTRL_RDY));
+ +
+ ret = bcmnand_scan(mtd); + ret = bcmnand_scan(mtd);
+ if (ret) { + if (ret) {
+ pr_err("%s: scanning the nand flash chip failed with %i\n", + dev_err(dev, "scanning the nand flash chip failed with %i\n",
+ DRV_NAME, ret); + ret);
+ return ret; + return ret;
+ } + }
+ +
+ return 0; + return 0;
+} +}
+ +
+static int __init bcmnand_idm_init(struct bcmnand_ctrl *ctrl) +static int bcmnand_idm_init(struct bcmnand_ctrl *ctrl)
+{ +{
+ int irq_off; + int irq_off;
+ unsigned int retries = 0x1000; + unsigned int retries = 0x1000;
+ struct device *dev = &ctrl->core->dev;
+ +
+ if (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) + if (bcmnand_reg_aread(ctrl, NANDC_IDM_RESET))
+ pr_err("%s: stuck in reset\n", DRV_NAME); + dev_info(dev, "stuck in reset\n");
+ +
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 1); + bcmnand_reg_awrite(ctrl, NANDC_IDM_RESET, 1);
+ if (!bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) { + if (!bcmnand_reg_aread(ctrl, NANDC_IDM_RESET)) {
+ pr_err("%s: reset of failed\n", DRV_NAME); + dev_err(dev, "reset of failed\n");
+ return -EIO; + return -EIO;
+ } + }
+ +
@ -1496,8 +1493,7 @@
+ cpu_relax(); + cpu_relax();
+ usleep_range(100, 150); + usleep_range(100, 150);
+ if (!(retries--)) { + if (!(retries--)) {
+ pr_err("%s: did not came back from reset\n", + dev_err(dev, "did not came back from reset\n");
+ DRV_NAME);
+ return -ETIMEDOUT; + return -ETIMEDOUT;
+ } + }
+ } + }
@ -1506,7 +1502,7 @@
+ bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0); + bcmnand_reg_awrite(ctrl, NANDC_IDM_APB_LITTLE_ENDIAN, 0);
+ udelay(10); + udelay(10);
+ +
+ pr_info("%s: NAND Controller rev %d.%d\n", DRV_NAME, + dev_info(dev, "NAND Controller rev %d.%d\n",
+ bcmnand_reg_read(ctrl, NANDC_REV_MAJOR), + bcmnand_reg_read(ctrl, NANDC_REV_MAJOR),
+ bcmnand_reg_read(ctrl, NANDC_REV_MINOR)); + bcmnand_reg_read(ctrl, NANDC_REV_MINOR));
+ +
@ -1543,14 +1539,14 @@
+ for (i = 0; i < NANDC_IRQ_NUM; i++) { + for (i = 0; i < NANDC_IRQ_NUM; i++) {
+ irq = bcma_core_irq(core, i); + irq = bcma_core_irq(core, i);
+ if (!irq) { + if (!irq) {
+ pr_err("%s: IRQ no available irq: %i (idx: %i)\n", + dev_err(dev, "IRQ idx %i not available\n", i);
+ DRV_NAME, irq, i); + return -ENOENT;
+ return res;
+ } + }
+ res = devm_request_irq(dev, irq, bcmnand_isr, 0, DRV_NAME, ctrl); + res = devm_request_irq(dev, irq, bcmnand_isr, 0,
+ KBUILD_MODNAME, ctrl);
+ if (res < 0) { + if (res < 0) {
+ pr_err("%s: problem requesting irq: %i (idx: %i)\n", + dev_err(dev, "problem requesting irq: %i (idx: %i)\n",
+ DRV_NAME, irq, i); + irq, i);
+ return res; + return res;
+ } + }
+ } + }
@ -1565,7 +1561,7 @@
+ +
+ res = mtd_device_parse_register(&ctrl->mtd, part_probes, NULL, NULL, 0); + res = mtd_device_parse_register(&ctrl->mtd, part_probes, NULL, NULL, 0);
+ if (res) { + if (res) {
+ pr_err("%s: Failed to register MTD device: %d\n", DRV_NAME, res); + dev_err(dev, "Failed to register MTD device: %d\n", res);
+ return res; + return res;
+ } + }
+ return 0; + return 0;
@ -1593,14 +1589,7 @@
+ +
+static int __init bcmnand_init(void) +static int __init bcmnand_init(void)
+{ +{
+ int err; + return bcma_driver_register(&bcmnand_bcma_driver);
+
+ err = bcma_driver_register(&bcmnand_bcma_driver);
+ if (err)
+ return err;
+ pr_info("%s: Broadcom NAND Controller driver loaded\n", DRV_NAME);
+
+ return 0;
+} +}
+ +
+static void __exit bcmnand_exit(void) +static void __exit bcmnand_exit(void)
@ -1612,4 +1601,5 @@
+module_exit(bcmnand_exit) +module_exit(bcmnand_exit)
+ +
+MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRV_DESC); +MODULE_AUTHOR("Hauke Mehrtens");
+MODULE_DESCRIPTION("Northstar on-chip NAND Flash Controller driver");