diff options
author | Rafał Miłecki <zajec5@gmail.com> | 2012-11-12 13:03:24 +0100 |
---|---|---|
committer | Artem Bityutskiy <artem.bityutskiy@linux.intel.com> | 2012-11-22 09:32:45 +0200 |
commit | 3c01d4cb7576ff79e13ba7d7fb884e63e6f07bbd (patch) | |
tree | 3235466e2bb387b2737be3f1355685a78adb0411 /drivers/mtd | |
parent | 0fbc5991551fa2024de7db79421e9a16ca9fe542 (diff) |
mtd: bcm47xxnflash: implement reading
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c | 106 |
2 files changed, 107 insertions, 0 deletions
diff --git a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h index cc71400de116..264aec448fc4 100644 --- a/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h +++ b/drivers/mtd/nand/bcm47xxnflash/bcm47xxnflash.h @@ -11,6 +11,7 @@ struct bcm47xxnflash { struct mtd_info mtd; unsigned curr_command; + int curr_page_addr; int curr_column; u8 id_data[8]; diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c index 7de190e9a788..e42e18208abc 100644 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c @@ -20,6 +20,8 @@ * shown 164 retries as maxiumum. */ #define NFLASH_READY_RETRIES 1000 +#define NFLASH_SECTOR_SIZE 512 + /************************************************** * Various helpers **************************************************/ @@ -47,6 +49,80 @@ static int bcm47xxnflash_ops_bcm4706_ctl_cmd(struct bcma_drv_cc *cc, u32 code) return 0; } +static int bcm47xxnflash_ops_bcm4706_poll(struct bcma_drv_cc *cc) +{ + int i; + + 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) { + pr_err("Error on polling\n"); + return -EBUSY; + } else { + return 0; + } + } + } + + pr_err("Polling timeout!\n"); + return -EBUSY; +} + +/************************************************** + * R/W + **************************************************/ + +static void bcm47xxnflash_ops_bcm4706_read(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *nand_chip = (struct nand_chip *)mtd->priv; + struct bcm47xxnflash *b47n = (struct bcm47xxnflash *)nand_chip->priv; + + u32 ctlcode; + u32 *dest = (u32 *)buf; + int i; + int toread; + + BUG_ON(b47n->curr_page_addr & ~nand_chip->pagemask); + /* Don't validate column using nand_chip->page_shift, it may be bigger + * when accessing OOB */ + + while (len) { + /* We can read maximum of 0x200 bytes at once */ + toread = min(len, 0x200); + + /* Set page and column */ + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_COL_ADDR, + b47n->curr_column); + bcma_cc_write32(b47n->cc, BCMA_CC_NFLASH_ROW_ADDR, + b47n->curr_page_addr); + + /* Prepare to read */ + ctlcode = 0x40000000 | 0x00080000 | 0x00040000 | 0x00020000 | + 0x00010000; + ctlcode |= NAND_CMD_READSTART << 8; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, ctlcode)) + return; + if (bcm47xxnflash_ops_bcm4706_poll(b47n->cc)) + return; + + /* Eventually read some data :) */ + for (i = 0; i < toread; i += 4, dest++) { + ctlcode = 0x40000000 | 0x30000000 | 0x00100000; + if (i == toread - 4) /* Last read goes without that */ + ctlcode &= ~0x40000000; + if (bcm47xxnflash_ops_bcm4706_ctl_cmd(b47n->cc, + ctlcode)) + return; + *dest = bcma_cc_read32(b47n->cc, BCMA_CC_NFLASH_DATA); + } + + b47n->curr_column += toread; + len -= toread; + } +} + /************************************************** * NAND chip ops **************************************************/ @@ -76,6 +152,8 @@ static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd, if (column != -1) b47n->curr_column = column; + if (page_addr != -1) + b47n->curr_page_addr = page_addr; switch (command) { case NAND_CMD_RESET: @@ -109,6 +187,12 @@ static void bcm47xxnflash_ops_bcm4706_cmdfunc(struct mtd_info *mtd, } break; + case NAND_CMD_READ0: + break; + case NAND_CMD_READOOB: + if (page_addr != -1) + b47n->curr_column += mtd->writesize; + break; default: pr_err("Command 0x%X unsupported\n", command); break; @@ -120,6 +204,7 @@ 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; + u32 tmp = 0; switch (b47n->curr_command) { case NAND_CMD_READID: @@ -129,12 +214,31 @@ static u8 bcm47xxnflash_ops_bcm4706_read_byte(struct mtd_info *mtd) return 0; } return b47n->id_data[b47n->curr_column++]; + case NAND_CMD_READOOB: + bcm47xxnflash_ops_bcm4706_read(mtd, (u8 *)&tmp, 4); + return tmp & 0xFF; } pr_err("Invalid command for byte read: 0x%X\n", b47n->curr_command); return 0; } +static void bcm47xxnflash_ops_bcm4706_read_buf(struct mtd_info *mtd, + uint8_t *buf, int len) +{ + 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_READ0: + case NAND_CMD_READOOB: + bcm47xxnflash_ops_bcm4706_read(mtd, buf, len); + return; + } + + pr_err("Invalid command for buf read: 0x%X\n", b47n->curr_command); +} + /************************************************** * Init **************************************************/ @@ -153,6 +257,8 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n) 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.read_buf = bcm47xxnflash_ops_bcm4706_read_buf; + b47n->nand_chip.bbt_options = NAND_BBT_USE_FLASH; b47n->nand_chip.ecc.mode = NAND_ECC_NONE; /* TODO: implement ECC */ /* Enable NAND flash access */ |