mtd: bcm47xxnflash: support for NAND_CMD_READID command
authorRafał Miłecki <zajec5@gmail.com>
Mon, 12 Nov 2012 12:03:23 +0000 (13:03 +0100)
committerArtem Bityutskiy <artem.bityutskiy@linux.intel.com>
Thu, 22 Nov 2012 07:32:41 +0000 (09:32 +0200)
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h
drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c

index 2e8864a891e2486d4cef0810b445599a1df67cee..cc71400de1169d169fdd25fa9472c25d647abc21 100644 (file)
@@ -9,6 +9,11 @@ struct bcm47xxnflash {
 
        struct nand_chip nand_chip;
        struct mtd_info mtd;
+
+       unsigned curr_command;
+       int curr_column;
+
+       u8 id_data[8];
 };
 
 #endif /* BCM47XXNFLASH */
index ae8a79387c27ee5a884218d318fb1b75faf3326b..7de190e9a788a58a83386cec16c18bf68a8d198a 100644 (file)
 
 #include "bcm47xxnflash.h"
 
+/* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has
+ * shown 164 retries as maxiumum. */
+#define NFLASH_READY_RETRIES           1000
+
 /**************************************************
  * Various helpers
  **************************************************/
@@ -25,6 +29,24 @@ static inline u8 bcm47xxnflash_ops_bcm4706_ns_to_cycle(u16 ns, u16 clock)
        return ((ns * 1000 * clock) / 1000000) + 1;
 }
 
+static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code)
+{
+       int i = 0;
+
+       bcma_cc_write32(cc, BCMA_CC_NFLASH_CTL, 0x80000000 | code);
+       for (i = 0; i < NFLASH_READY_RETRIES; i++) {
+               if (!(bcma_cc_read32(cc, BCMA_CC_NFLASH_CTL) & 0x80000000)) {
+                       i = 0;
+                       break;
+               }
+       }
+       if (i) {
+               pr_err("NFLASH control command not ready!\n");
+               return -EBUSY;
+       }
+       return 0;
+}
+
 /**************************************************
  * NAND chip ops
  **************************************************/
@@ -36,6 +58,83 @@ static void bcm47xxnflash_ops_bcm4706_select_chip(struct mtd_info *mtd,
        return;
 }
 
+/*
+ * Default nand_command and nand_command_lp don't match BCM4706 hardware layout.
+ * For example, reading chip id is performed in a non-standard way.
+ * Setting column and page is also handled differently, we use a special
+ * registers of ChipCommon core. Hacking cmd_ctrl to understand and convert
+ * standard commands would be much more complicated.
+ */
+static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd,
+                                             unsigned command, int column,
+                                             int page_addr)
+{
+       struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
+       struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+       u32 ctlcode;
+       int i;
+
+       if (column != -1)
+               b47n->curr_column = column;
+
+       switch (command) {
+       case NAND_CMD_RESET:
+               pr_warn("Chip reset not implemented yet\n");
+               break;
+       case NAND_CMD_READID:
+               ctlcode = 0x40000000 | 0x01000000 | 0x00080000 | 0x00010000;
+               ctlcode |= NAND_CMD_READID;
+               if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) {
+                       pr_err("READID error\n");
+                       break;
+               }
+
+               /*
+                * Reading is specific, last one has to go without 0x40000000
+                * bit. We don't know how many reads NAND subsystem is going
+                * to perform, so cache everything.
+                */
+               for (i = 0; i < ARRAY_SIZE(b47n->id_data); i++) {
+                       ctlcode = 0x40000000 | 0x00100000;
+                       if (i == ARRAY_SIZE(b47n->id_data) - 1)
+                               ctlcode &= ~0x40000000;
+                       if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc,
+                                                             ctlcode)) {
+                               pr_err("READID error\n");
+                               break;
+                       }
+                       b47n->id_data[i] =
+                               bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA)
+                               & 0xFF;
+               }
+
+               break;
+       default:
+               pr_err("Command 0x%X unsupported\n", command);
+               break;
+       }
+       b47n->curr_command = command;
+}
+
+static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv;
+       struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv;
+
+       switch (b47n->curr_command) {
+       case NAND_CMD_READID:
+               if (b47n->curr_column >= ARRAY_SIZE(b47n->id_data)) {
+                       pr_err("Requested invalid id_data: %d\n",
+                              b47n->curr_column);
+                       return 0;
+               }
+               return b47n->id_data[b47n->curr_column++];
+       }
+
+       pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command);
+       return 0;
+}
+
 /**************************************************
  * Init
  **************************************************/
@@ -52,6 +151,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
        u32 val;
 
        b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip;
+       b47n->nand_chip.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc;
+       b47n->nand_chip.read_byte = bcm47xxnflash_ops_bcm4706_read_byte;
        b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */
 
        /* Enable NAND flash access */