[PATCH 22/25] pxa3xx_nand: reimplement the read oob logic
Lei Wen
leiwen at marvell.com
Tue Jun 8 04:37:35 EDT 2010
For readoob command only need the content of oob part, it certainly
don't need the controller transfer the data part, which greatly downgrade
the performance.
Signed-off-by: Lei Wen <leiwen at marvell.com>
---
drivers/mtd/nand/pxa3xx_nand.c | 182 +++++++++++++++++++++-------------------
1 files changed, 96 insertions(+), 86 deletions(-)
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 7f8bae7..e76264c 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -244,30 +244,28 @@ struct pxa3xx_nand {
uint32_t command;
uint16_t data_size; /* data size in FIFO */
uint16_t oob_size;
- uint32_t bad_count;
unsigned char *dma_buff;
unsigned char *data_buff;
unsigned char *oob_buff;
+ uint8_t chip_select;
+ uint8_t total_cmds;
uint32_t buf_start;
uint32_t buf_count;
+
+ uint32_t state;
+ uint32_t bad_count;
uint16_t data_column;
uint16_t oob_column;
-
- /* relate to the command */
- uint8_t chip_select;
- uint8_t ecc_strength;
- unsigned int state;
int use_dma; /* use DMA ? */
int retcode;
+ uint8_t ecc_strength;
/* cached register value */
uint8_t cmd_seqs;
- uint8_t total_cmds;
uint8_t wait_ready[CMD_POOL_SIZE];
uint32_t ndcb0[CMD_POOL_SIZE];
- uint32_t ndcb1;
+ uint32_t ndcb1[CMD_POOL_SIZE];
uint32_t ndcb2;
- uint32_t ndcb3[CMD_POOL_SIZE];
};
static int use_dma = 1;
@@ -396,8 +394,8 @@ static void pxa3xx_set_datasize(struct
pxa3xx_nand_info *info)
struct pxa3xx_nand *nand = info->nand_data;
int oob_enable = info->reg_ndcr & NDCR_SPARE_EN;
- if (info->page_size >= PAGE_CHUNK_SIZE) {
- nand->data_size = PAGE_CHUNK_SIZE;
+ if (info->page_size < PAGE_CHUNK_SIZE) {
+ nand->data_size = 512;
if (!oob_enable) {
nand->oob_size = 0;
return;
@@ -405,32 +403,54 @@ static void pxa3xx_set_datasize(struct
pxa3xx_nand_info *info)
switch (nand->ecc_strength) {
case 0:
- nand->oob_size = 64;
+ nand->oob_size = 16;
break;
case HAMMING_STRENGTH:
- nand->oob_size = 40;
+ nand->oob_size = 8;
break;
default:
- nand->oob_size = 32;
- }
- } else {
- nand->data_size = 512;
- if (!oob_enable) {
- nand->oob_size = 0;
- return;
+ printk("Don't support BCH on small page device!!!\n");
+ BUG();
}
+ return;
+ }
+ nand->data_size = PAGE_CHUNK_SIZE;
+ if (!oob_enable) {
+ nand->oob_size = 0;
+ return;
+ }
- switch (nand->ecc_strength) {
- case 0:
- nand->oob_size = 16;
- break;
+ if (nand->command == NAND_CMD_READOOB) {
+ switch (info->ecc_strength) {
case HAMMING_STRENGTH:
- nand->oob_size = 8;
+ nand->ndcb1[3] = 2 * PAGE_CHUNK_SIZE + OOB_CHUNK_SIZE;
+ nand->data_size = 40;
+ break;
+ case BCH_STRENGTH:
+ nand->ndcb1[3] = 2 * PAGE_CHUNK_SIZE + OOB_CHUNK_SIZE - 2;
+ nand->data_size = 32;
break;
default:
- printk("Don't support BCH on small page device!!!\n");
BUG();
}
+ if (info->reg_ndcr & NDCR_DWIDTH_M) {
+ nand->ndcb1[1] = PAGE_CHUNK_SIZE / 2;
+ nand->ndcb1[3] /= 2;
+ }
+ else
+ nand->ndcb1[1] = PAGE_CHUNK_SIZE;
+ nand->oob_size = 0;
+ return;
+ }
+ switch (nand->ecc_strength) {
+ case 0:
+ nand->oob_size = 64;
+ break;
+ case HAMMING_STRENGTH:
+ nand->oob_size = 40;
+ break;
+ default:
+ nand->oob_size = 32;
}
}
@@ -514,9 +534,8 @@ static void nand_error_dump(struct pxa3xx_nand *nand)
printk(KERN_ERR "Totally %d command for sending\n",
nand->total_cmds);
for (i = 0; i < nand->total_cmds; i ++)
- printk(KERN_ERR "NDCB0:%d: %x\n",
- i, nand->ndcb0[i]);
- printk(KERN_ERR "NDCB1: %x; NDCB2 %x\n", nand->ndcb1, nand->ndcb2);
+ printk(KERN_ERR "%d::NDCB0: %x, NDCB1: %x, NDCB2: %x\n",
+ i, nand->ndcb0[i], nand->ndcb1[i], nand->ndcb2);
printk(KERN_ERR "\nRegister DUMPing ##############\n");
printk(KERN_ERR "NDCR %x\n"
@@ -623,8 +642,8 @@ static void pxa3xx_nand_data_dma_irq(int channel,
void *data)
static int pxa3xx_nand_transaction(struct pxa3xx_nand *nand)
{
struct pxa3xx_nand_info *info;
- unsigned int status, is_completed = 0, cs, cmd_seqs, ndcb1, ndcb2;
- unsigned int ready, cmd_done, page_done, badblock_detect;
+ unsigned int status, is_completed = 0, cs, cmd_seqs;
+ unsigned int ready, cmd_done, page_done, badblock_detect, ndcb2;
cs = nand->chip_select;
ready = (cs) ? NDSR_RDY : NDSR_FLASH_RDY;
@@ -686,22 +705,19 @@ static int pxa3xx_nand_transaction(struct
pxa3xx_nand *nand)
nand_writel(nand, NDSR, NDSR_WRCMDREQ);
if (cmd_seqs < nand->total_cmds) {
- nand->cmd_seqs ++;
- if (cmd_seqs == 0) {
- ndcb1 = nand->ndcb1;
+ if (cmd_seqs == 0)
ndcb2 = nand->ndcb2;
- }
- else {
- ndcb1 = 0;
+ else
ndcb2 = 0;
- }
+ nand->cmd_seqs ++;
nand->state &= ~STATE_MASK;
nand->state |= STATE_CMD_WAIT_DONE;
nand_writel(nand, NDCB0, nand->ndcb0[cmd_seqs]);
- nand_writel(nand, NDCB0, ndcb1);
+ nand_writel(nand, NDCB0, nand->ndcb1[cmd_seqs]);
nand_writel(nand, NDCB0, ndcb2);
- if (nand->ndcb3[cmd_seqs])
- nand_writel(nand, NDCB0, nand->ndcb3[cmd_seqs]);
+ if (nand->ndcb0[cmd_seqs] & NDCB0_LEN_OVRD)
+ nand_writel(nand, NDCB0, nand->data_size
+ + nand->oob_size);
}
else
is_completed = 1;
@@ -760,24 +776,15 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
exec_cmd = 1;
/* reset data and oob column point to handle data */
- nand->data_column = 0;
- nand->oob_column = 0;
- nand->total_cmds = 1;
- nand->cmd_seqs = 0;
- nand->data_size = 0;
- nand->oob_size = 0;
- nand->use_dma = 0;
- nand->state = 0;
- nand->bad_count = 0;
- nand->retcode = ERR_NONE;
- nand->buf_start = column;
+ nand->total_cmds = 1;
+ nand->buf_start = column;
switch (command) {
case NAND_CMD_PAGEPROG:
case NAND_CMD_RNDOUT:
pxa3xx_set_datasize(info);
nand->use_dma = use_dma;
- chunks = info->page_size / nand->data_size;
+ chunks = info->page_size / PAGE_CHUNK_SIZE;
if (info->ecc_strength > BCH_STRENGTH) {
i = info->ecc_strength / BCH_STRENGTH;
nand->data_size /= i;
@@ -792,9 +799,8 @@ static int prepare_command_pool(struct pxa3xx_nand
*nand, int command,
BUG();
}
default:
- nand->ndcb1 = 0;
- nand->ndcb2 = 0;
- nand->ecc_strength = 0;
+ i = (uint32_t)(&nand->state) - (uint32_t)nand;
+ memset(&nand->state, 0, sizeof(struct pxa3xx_nand) - i);
break;
}
if (nand->use_dma) {
@@ -803,11 +809,8 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
}
/* clear the command buffer */
- for (i = 0; i < CMD_POOL_SIZE; i ++) {
+ for (i = 0; i < CMD_POOL_SIZE; i ++)
nand->ndcb0[i] = ndcb0;
- nand->wait_ready[i] = 0;
- nand->ndcb3[i] = (ndcb0 & NDCB0_LEN_OVRD)? nand->data_size : 0;
- }
addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
+ info->col_addr_cycles);
@@ -822,10 +825,10 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
info->page_addr = page_addr;
/* small page addr setting */
if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
- nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
+ nand->ndcb1[0] = ((page_addr & 0xFFFFFF) << 8)
| (column & 0xFF);
else {
- nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
+ nand->ndcb1[0] = ((page_addr & 0xFFFF) << 16)
| (column & 0xFFFF);
if (page_addr & 0xFF0000)
@@ -835,11 +838,6 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
case NAND_CMD_RNDOUT:
cmd = cmdset.read1;
- if (nand->command == NAND_CMD_READOOB) {
- nand->buf_start = mtd->writesize + column;
- nand->buf_count = mtd->oobsize;
- }
-
if (unlikely(info->page_size < PAGE_CHUNK_SIZE)
|| !(pdata->controller_attrs & PXA3XX_NAKED_CMD_EN)) {
if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
@@ -851,10 +849,18 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
| NDCB0_DBC
| addr_cycle
| cmd;
+ if (nand->command == NAND_CMD_READOOB) {
+ nand->buf_start = mtd->writesize + column;
+ nand->buf_count = mtd->oobsize;
+ }
break;
}
- nand->total_cmds = chunks + 1;
+ if (nand->command == NAND_CMD_READOOB)
+ nand->total_cmds = 2 * chunks + 1;
+ else
+ nand->total_cmds = chunks + 1;
+
nand->ndcb0[0] |= NDCB0_CMD_XTYPE(0x6)
| NDCB0_CMD_TYPE(0)
| NDCB0_DBC
@@ -862,15 +868,23 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
| addr_cycle
| cmd;
nand->ndcb0[0] &= ~NDCB0_LEN_OVRD;
- nand->ndcb3[0] = 0;
- nand->ndcb0[1] |= NDCB0_CMD_XTYPE(0x5)
- | NDCB0_NC
- | addr_cycle;
- for (i = 2; i <= chunks; i ++)
- nand->ndcb0[i] = nand->ndcb0[1];
+ for (i = 1; i <= nand->total_cmds - 1;) {
+ if (nand->command == NAND_CMD_READOOB) {
+ nand->ndcb0[i ++] |= NDCB0_CMD_XTYPE(0x6)
+ | NDCB0_CMD_TYPE(0)
+ | NDCB0_ADDR_CYC(info->col_addr_cycles)
+ | NDCB0_DBC
+ | NDCB0_NC
+ | (NAND_CMD_RNDOUTSTART << 8)
+ | NAND_CMD_RNDOUT;
+ nand->ndcb0[i] |= NDCB0_LEN_OVRD;
+ }
+ nand->ndcb0[i ++] |= NDCB0_CMD_XTYPE(0x5)
+ | NDCB0_NC;
+ }
- nand->ndcb0[chunks] &= ~NDCB0_NC;
+ nand->ndcb0[nand->total_cmds - 1] &= ~NDCB0_NC;
/* we should wait RnB go high again
* before read out data*/
nand->wait_ready[1] = 1;
@@ -908,18 +922,15 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
nand->ndcb0[i] |= NDCB0_CMD_XTYPE(0x5)
| NDCB0_NC
| NDCB0_AUTO_RS
- | NDCB0_CMD_TYPE(0x1)
- | addr_cycle;
+ | NDCB0_CMD_TYPE(0x1);
nand->ndcb0[chunks] |= NDCB0_CMD_XTYPE(0x3)
- | NDCB0_CMD_TYPE(0x1)
- | NDCB0_ST_ROW_EN
- | NDCB0_DBC
- | (cmd & NDCB0_CMD2_MASK)
- | NDCB0_CMD1_MASK
- | addr_cycle;
+ | NDCB0_CMD_TYPE(0x1)
+ | NDCB0_ST_ROW_EN
+ | NDCB0_DBC
+ | (cmd & NDCB0_CMD2_MASK)
+ | NDCB0_CMD1_MASK;
nand->ndcb0[chunks] &= ~NDCB0_LEN_OVRD;
- nand->ndcb3[chunks] = 0;
/* we should wait for RnB goes high which
* indicate the data has been written succesfully*/
nand->wait_ready[nand->total_cmds] = 1;
@@ -953,8 +964,7 @@ static int prepare_command_pool(struct pxa3xx_nand
*nand, int command,
| NDCB0_ADDR_CYC(3)
| NDCB0_DBC
| cmd;
- nand->ndcb1 = page_addr;
- nand->ndcb2 = 0;
+ nand->ndcb1[0] = page_addr;
break;
case NAND_CMD_RESET:
--
1.7.0.4
More information about the linux-mtd
mailing list