mtd: bcm47xxnflash: support for NAND_CMD_READID command

Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
This commit is contained in:
Rafał Miłecki 2012-11-12 13:03:23 +01:00 committed by Artem Bityutskiy
parent 19c0921c84
commit 0fbc599155
2 changed files with 106 additions and 0 deletions

View 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 */

View File

@ -16,6 +16,10 @@
#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 */