|
@@ -0,0 +1,109 @@
|
|
|
+/*
|
|
|
+ * BCM47XX NAND flash driver
|
|
|
+ *
|
|
|
+ * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com>
|
|
|
+ *
|
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
|
+ * published by the Free Software Foundation.
|
|
|
+ *
|
|
|
+ */
|
|
|
+
|
|
|
+#include <linux/module.h>
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/bcma/bcma.h>
|
|
|
+
|
|
|
+#include "bcm47xxnflash.h"
|
|
|
+
|
|
|
+/**************************************************
|
|
|
+ * Various helpers
|
|
|
+ **************************************************/
|
|
|
+
|
|
|
+static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock)
|
|
|
+{
|
|
|
+ return ((ns * 1000 * clock) / 1000000) + 1;
|
|
|
+}
|
|
|
+
|
|
|
+/**************************************************
|
|
|
+ * NAND chip ops
|
|
|
+ **************************************************/
|
|
|
+
|
|
|
+/* Default nand_select_chip calls cmd_ctrl, which is not used in BCM4706 */
|
|
|
+static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd,
|
|
|
+ int chip)
|
|
|
+{
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+/**************************************************
|
|
|
+ * Init
|
|
|
+ **************************************************/
|
|
|
+
|
|
|
+int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ u32 freq;
|
|
|
+ u16 clock;
|
|
|
+ u8 w0, w1, w2, w3, w4;
|
|
|
+
|
|
|
+ unsigned long chipsize; /* MiB */
|
|
|
+ u8 tbits, col_bits, col_size, row_bits, row_bsize;
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip;
|
|
|
+ b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */
|
|
|
+
|
|
|
+ /* Enable NAND flash access */
|
|
|
+ bcma_cc_set32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
|
|
|
+ BCMA_CC_4706_FLASHSCFG_NF1);
|
|
|
+
|
|
|
+ /* Configure wait counters */
|
|
|
+ if (b47n->cc->status & BCMA_CC_CHIPST_4706_PKG_OPTION) {
|
|
|
+ freq = 100000000;
|
|
|
+ } else {
|
|
|
+ freq = bcma_chipco_pll_read(b47n->cc, 4);
|
|
|
+ freq = (freq * 0xFFF) >> 3;
|
|
|
+ freq = (freq * 25000000) >> 3;
|
|
|
+ }
|
|
|
+ clock = freq / 1000000;
|
|
|
+ w0 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(15, clock);
|
|
|
+ w1 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(20, clock);
|
|
|
+ w2 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
|
|
|
+ w3 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(10, clock);
|
|
|
+ w4 = bcm47xxnflash_ops_bcm4706_ns_to_cycle(100, clock);
|
|
|
+ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_WAITCNT0,
|
|
|
+ (w4 << 24 | w3 << 18 | w2 << 12 | w1 << 6 | w0));
|
|
|
+
|
|
|
+ /* Scan NAND */
|
|
|
+ err = nand_scan(&b47n->mtd, 1);
|
|
|
+ if (err) {
|
|
|
+ pr_err("Could not scan NAND flash: %d\n", err);
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Configure FLASH */
|
|
|
+ chipsize = b47n->nand_chip.chipsize >> 20;
|
|
|
+ tbits = ffs(chipsize); /* find first bit set */
|
|
|
+ if (!tbits || tbits != fls(chipsize)) {
|
|
|
+ pr_err("Invalid flash size: 0x%lX\n", chipsize);
|
|
|
+ err = -ENOTSUPP;
|
|
|
+ goto exit;
|
|
|
+ }
|
|
|
+ tbits += 19; /* Broadcom increases *index* by 20, we increase *pos* */
|
|
|
+
|
|
|
+ col_bits = b47n->nand_chip.page_shift + 1;
|
|
|
+ col_size = (col_bits + 7) / 8;
|
|
|
+
|
|
|
+ row_bits = tbits - col_bits + 1;
|
|
|
+ row_bsize = (row_bits + 7) / 8;
|
|
|
+
|
|
|
+ val = ((row_bsize - 1) << 6) | ((col_size - 1) << 4) | 2;
|
|
|
+ bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_CONF, val);
|
|
|
+
|
|
|
+exit:
|
|
|
+ if (err)
|
|
|
+ bcma_cc_mask32(b47n->cc, BCMA_CC_4706_FLASHSCFG,
|
|
|
+ ~BCMA_CC_4706_FLASHSCFG_NF1);
|
|
|
+ return err;
|
|
|
+}
|