|
@@ -28,6 +28,7 @@
|
|
|
|
|
|
#define CHIP_DELAY_TIMEOUT (2 * HZ/10)
|
|
|
#define NAND_STOP_DELAY (2 * HZ/50)
|
|
|
+#define PAGE_CHUNK_SIZE (2048)
|
|
|
|
|
|
/* registers and bit definitions */
|
|
|
#define NDCR (0x00) /* Control register */
|
|
@@ -77,6 +78,7 @@
|
|
|
#define NDSR_RDDREQ (0x1 << 1)
|
|
|
#define NDSR_WRCMDREQ (0x1)
|
|
|
|
|
|
+#define NDCB0_ST_ROW_EN (0x1 << 26)
|
|
|
#define NDCB0_AUTO_RS (0x1 << 25)
|
|
|
#define NDCB0_CSEL (0x1 << 24)
|
|
|
#define NDCB0_CMD_TYPE_MASK (0x7 << 21)
|
|
@@ -319,66 +321,6 @@ static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info)
|
|
|
nand_writel(info, NDSR, NDSR_MASK);
|
|
|
}
|
|
|
|
|
|
-static void prepare_read_prog_cmd(struct pxa3xx_nand_info *info,
|
|
|
- uint16_t cmd, int column, int page_addr)
|
|
|
-{
|
|
|
- const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
|
|
|
- pxa3xx_set_datasize(info);
|
|
|
-
|
|
|
- /* generate values for NDCBx registers */
|
|
|
- info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
|
|
|
- info->ndcb1 = 0;
|
|
|
- info->ndcb2 = 0;
|
|
|
- info->ndcb0 |= NDCB0_ADDR_CYC(info->row_addr_cycles + info->col_addr_cycles);
|
|
|
-
|
|
|
- if (info->col_addr_cycles == 2) {
|
|
|
- /* large block, 2 cycles for column address
|
|
|
- * row address starts from 3rd cycle
|
|
|
- */
|
|
|
- info->ndcb1 |= page_addr << 16;
|
|
|
- if (info->row_addr_cycles == 3)
|
|
|
- info->ndcb2 = (page_addr >> 16) & 0xff;
|
|
|
- } else
|
|
|
- /* small block, 1 cycles for column address
|
|
|
- * row address starts from 2nd cycle
|
|
|
- */
|
|
|
- info->ndcb1 = page_addr << 8;
|
|
|
-
|
|
|
- if (cmd == cmdset->program)
|
|
|
- info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
|
|
|
-}
|
|
|
-
|
|
|
-static void prepare_erase_cmd(struct pxa3xx_nand_info *info,
|
|
|
- uint16_t cmd, int page_addr)
|
|
|
-{
|
|
|
- info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
|
|
|
- info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3);
|
|
|
- info->ndcb1 = page_addr;
|
|
|
- info->ndcb2 = 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd)
|
|
|
-{
|
|
|
- const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
|
|
|
-
|
|
|
- info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0);
|
|
|
- info->ndcb1 = 0;
|
|
|
- info->ndcb2 = 0;
|
|
|
-
|
|
|
- info->oob_size = 0;
|
|
|
- if (cmd == cmdset->read_id) {
|
|
|
- info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1);
|
|
|
- info->data_size = 8;
|
|
|
- } else if (cmd == cmdset->read_status) {
|
|
|
- info->ndcb0 |= NDCB0_CMD_TYPE(4);
|
|
|
- info->data_size = 8;
|
|
|
- } else if (cmd == cmdset->reset || cmd == cmdset->lock ||
|
|
|
- cmd == cmdset->unlock) {
|
|
|
- info->ndcb0 |= NDCB0_CMD_TYPE(5);
|
|
|
- } else
|
|
|
- BUG();
|
|
|
-}
|
|
|
-
|
|
|
static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask)
|
|
|
{
|
|
|
uint32_t ndcr;
|
|
@@ -529,81 +471,167 @@ static inline int is_buf_blank(uint8_t *buf, size_t len)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
|
|
- int column, int page_addr)
|
|
|
+static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,
|
|
|
+ uint16_t column, int page_addr)
|
|
|
{
|
|
|
- struct pxa3xx_nand_info *info = mtd->priv;
|
|
|
- const struct pxa3xx_nand_cmdset *cmdset = info->cmdset;
|
|
|
- int ret, exec_cmd = 0;
|
|
|
+ uint16_t cmd;
|
|
|
+ int addr_cycle, exec_cmd, ndcb0;
|
|
|
+ struct mtd_info *mtd = info->mtd;
|
|
|
+
|
|
|
+ ndcb0 = 0;
|
|
|
+ addr_cycle = 0;
|
|
|
+ exec_cmd = 1;
|
|
|
|
|
|
- info->use_dma = (use_dma) ? 1 : 0;
|
|
|
- info->use_ecc = 0;
|
|
|
- info->data_size = 0;
|
|
|
- info->state = 0;
|
|
|
- info->retcode = ERR_NONE;
|
|
|
+ /* reset data and oob column point to handle data */
|
|
|
+ info->buf_start = 0;
|
|
|
+ info->buf_count = 0;
|
|
|
+ info->oob_size = 0;
|
|
|
+ info->use_ecc = 0;
|
|
|
+ info->retcode = ERR_NONE;
|
|
|
|
|
|
switch (command) {
|
|
|
+ case NAND_CMD_READ0:
|
|
|
+ case NAND_CMD_PAGEPROG:
|
|
|
+ info->use_ecc = 1;
|
|
|
case NAND_CMD_READOOB:
|
|
|
- /* disable HW ECC to get all the OOB data */
|
|
|
- info->buf_count = mtd->writesize + mtd->oobsize;
|
|
|
- info->buf_start = mtd->writesize + column;
|
|
|
- memset(info->data_buff, 0xFF, info->buf_count);
|
|
|
-
|
|
|
- prepare_read_prog_cmd(info, cmdset->read1, column, page_addr);
|
|
|
- exec_cmd = 1;
|
|
|
+ pxa3xx_set_datasize(info);
|
|
|
+ break;
|
|
|
+ case NAND_CMD_SEQIN:
|
|
|
+ exec_cmd = 0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ info->ndcb1 = 0;
|
|
|
+ info->ndcb2 = 0;
|
|
|
break;
|
|
|
+ }
|
|
|
+
|
|
|
+ info->ndcb0 = ndcb0;
|
|
|
+ addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles
|
|
|
+ + info->col_addr_cycles);
|
|
|
|
|
|
+ switch (command) {
|
|
|
+ case NAND_CMD_READOOB:
|
|
|
case NAND_CMD_READ0:
|
|
|
- info->use_ecc = 1;
|
|
|
- info->buf_start = column;
|
|
|
- info->buf_count = mtd->writesize + mtd->oobsize;
|
|
|
- memset(info->data_buff, 0xFF, info->buf_count);
|
|
|
+ cmd = info->cmdset->read1;
|
|
|
+ if (command == NAND_CMD_READOOB)
|
|
|
+ info->buf_start = mtd->writesize + column;
|
|
|
+ else
|
|
|
+ info->buf_start = column;
|
|
|
+
|
|
|
+ if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
|
|
|
+ info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
|
|
+ | addr_cycle
|
|
|
+ | (cmd & NDCB0_CMD1_MASK);
|
|
|
+ else
|
|
|
+ info->ndcb0 |= NDCB0_CMD_TYPE(0)
|
|
|
+ | NDCB0_DBC
|
|
|
+ | addr_cycle
|
|
|
+ | cmd;
|
|
|
|
|
|
- prepare_read_prog_cmd(info, cmdset->read1, column, page_addr);
|
|
|
- exec_cmd = 1;
|
|
|
- break;
|
|
|
case NAND_CMD_SEQIN:
|
|
|
- info->buf_start = column;
|
|
|
+ /* small page addr setting */
|
|
|
+ if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
|
|
|
+ info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
|
|
|
+ | (column & 0xFF);
|
|
|
+
|
|
|
+ info->ndcb2 = 0;
|
|
|
+ } else {
|
|
|
+ info->ndcb1 = ((page_addr & 0xFFFF) << 16)
|
|
|
+ | (column & 0xFFFF);
|
|
|
+
|
|
|
+ if (page_addr & 0xFF0000)
|
|
|
+ info->ndcb2 = (page_addr & 0xFF0000) >> 16;
|
|
|
+ else
|
|
|
+ info->ndcb2 = 0;
|
|
|
+ }
|
|
|
+
|
|
|
info->buf_count = mtd->writesize + mtd->oobsize;
|
|
|
- memset(info->data_buff, 0xff, info->buf_count);
|
|
|
+ memset(info->data_buff, 0xFF, info->buf_count);
|
|
|
|
|
|
- /* save column/page_addr for next CMD_PAGEPROG */
|
|
|
- info->seqin_column = column;
|
|
|
- info->seqin_page_addr = page_addr;
|
|
|
break;
|
|
|
+
|
|
|
case NAND_CMD_PAGEPROG:
|
|
|
- info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1;
|
|
|
+ if (is_buf_blank(info->data_buff,
|
|
|
+ (mtd->writesize + mtd->oobsize))) {
|
|
|
+ exec_cmd = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- prepare_read_prog_cmd(info, cmdset->program,
|
|
|
- info->seqin_column, info->seqin_page_addr);
|
|
|
- exec_cmd = 1;
|
|
|
- break;
|
|
|
- case NAND_CMD_ERASE1:
|
|
|
- prepare_erase_cmd(info, cmdset->erase, page_addr);
|
|
|
- exec_cmd = 1;
|
|
|
- break;
|
|
|
- case NAND_CMD_ERASE2:
|
|
|
+ cmd = info->cmdset->program;
|
|
|
+ info->ndcb0 |= NDCB0_CMD_TYPE(0x1)
|
|
|
+ | NDCB0_AUTO_RS
|
|
|
+ | NDCB0_ST_ROW_EN
|
|
|
+ | NDCB0_DBC
|
|
|
+ | cmd
|
|
|
+ | addr_cycle;
|
|
|
break;
|
|
|
+
|
|
|
case NAND_CMD_READID:
|
|
|
+ cmd = info->cmdset->read_id;
|
|
|
+ info->buf_count = info->read_id_bytes;
|
|
|
+ info->ndcb0 |= NDCB0_CMD_TYPE(3)
|
|
|
+ | NDCB0_ADDR_CYC(1)
|
|
|
+ | cmd;
|
|
|
+
|
|
|
+ info->data_size = 8;
|
|
|
+ break;
|
|
|
case NAND_CMD_STATUS:
|
|
|
- info->use_dma = 0; /* force PIO read */
|
|
|
- info->buf_start = 0;
|
|
|
- info->buf_count = (command == NAND_CMD_READID) ?
|
|
|
- info->read_id_bytes : 1;
|
|
|
-
|
|
|
- prepare_other_cmd(info, (command == NAND_CMD_READID) ?
|
|
|
- cmdset->read_id : cmdset->read_status);
|
|
|
- exec_cmd = 1;
|
|
|
+ cmd = info->cmdset->read_status;
|
|
|
+ info->buf_count = 1;
|
|
|
+ info->ndcb0 |= NDCB0_CMD_TYPE(4)
|
|
|
+ | NDCB0_ADDR_CYC(1)
|
|
|
+ | cmd;
|
|
|
+
|
|
|
+ info->data_size = 8;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NAND_CMD_ERASE1:
|
|
|
+ cmd = info->cmdset->erase;
|
|
|
+ info->ndcb0 |= NDCB0_CMD_TYPE(2)
|
|
|
+ | NDCB0_AUTO_RS
|
|
|
+ | NDCB0_ADDR_CYC(3)
|
|
|
+ | NDCB0_DBC
|
|
|
+ | cmd;
|
|
|
+ info->ndcb1 = page_addr;
|
|
|
+ info->ndcb2 = 0;
|
|
|
+
|
|
|
break;
|
|
|
case NAND_CMD_RESET:
|
|
|
- prepare_other_cmd(info, cmdset->reset);
|
|
|
- exec_cmd = 1;
|
|
|
+ cmd = info->cmdset->reset;
|
|
|
+ info->ndcb0 |= NDCB0_CMD_TYPE(5)
|
|
|
+ | cmd;
|
|
|
+
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NAND_CMD_ERASE2:
|
|
|
+ exec_cmd = 0;
|
|
|
break;
|
|
|
+
|
|
|
default:
|
|
|
- printk(KERN_ERR "non-supported command.\n");
|
|
|
+ exec_cmd = 0;
|
|
|
+ printk(KERN_ERR "pxa3xx-nand: non-supported"
|
|
|
+ " command %x\n", command);
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ return exec_cmd;
|
|
|
+}
|
|
|
+
|
|
|
+static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
|
|
|
+ int column, int page_addr)
|
|
|
+{
|
|
|
+ struct pxa3xx_nand_info *info = mtd->priv;
|
|
|
+ int ret, exec_cmd;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * if this is a x16 device ,then convert the input
|
|
|
+ * "byte" address into a "word" address appropriate
|
|
|
+ * for indexing a word-oriented device
|
|
|
+ */
|
|
|
+ if (info->reg_ndcr & NDCR_DWIDTH_M)
|
|
|
+ column /= 2;
|
|
|
+
|
|
|
+ exec_cmd = prepare_command_pool(info, command, column, page_addr);
|
|
|
if (exec_cmd) {
|
|
|
init_completion(&info->cmd_complete);
|
|
|
pxa3xx_nand_start(info);
|
|
@@ -919,6 +947,7 @@ static void pxa3xx_nand_init_mtd(struct mtd_info *mtd,
|
|
|
struct nand_chip *this = &info->nand_chip;
|
|
|
|
|
|
this->options = (info->reg_ndcr & NDCR_DWIDTH_C) ? NAND_BUSWIDTH_16: 0;
|
|
|
+ this->options |= NAND_NO_AUTOINCR;
|
|
|
|
|
|
this->waitfunc = pxa3xx_nand_waitfunc;
|
|
|
this->select_chip = pxa3xx_nand_select_chip;
|