[PATCH 20/29] pxa3xx_nand: use the buff passed from upper

Lei Wen leiwen at marvell.com
Tue Jun 22 10:47:13 EDT 2010


layer if not use dma

For dma enabling case, we couldn't avoid the memcpy, for the buffer
in the upper layer may not be the physical consecutive.

Signed-off-by: Lei Wen <leiwen at marvell.com>
---
 drivers/mtd/nand/pxa3xx_nand.c |  297 ++++++++++++++++++++++++----------------
 1 files changed, 181 insertions(+), 116 deletions(-)

diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 50f653b..048b576 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -158,6 +158,7 @@ struct pxa3xx_nand_info {
 	struct nand_chip	nand_chip;
 	struct pxa3xx_nand_cmdset *cmdset;
 	/* page size of attached chip */
+	int			page_addr;
 	uint16_t		page_size;
 	uint8_t			chip_select;
 	uint8_t			use_ecc;
@@ -186,8 +187,8 @@ struct pxa3xx_nand {
 	int			drcmr_dat;
 	int			drcmr_cmd;
 	int 			data_dma_ch;
-	dma_addr_t 		data_buff_phys;
-	dma_addr_t 		data_desc_addr;
+	dma_addr_t 		dma_buff_phys;
+	dma_addr_t 		dma_desc_addr;
 	struct pxa_dma_desc	*data_desc;

 	struct pxa3xx_nand_info *info[NUM_CHIP_SELECT];
@@ -496,25 +497,25 @@ static void start_data_dma(struct pxa3xx_nand
*nand, int dir_out)
 	desc_oob->ddadr = desc->ddadr = DDADR_STOP;
 	desc_oob->dcmd = desc->dcmd = DCMD_WIDTH4 | DCMD_BURST32;
 	if (dir_out) {
-		desc->dsadr = nand->data_buff_phys + nand->data_column;
+		desc->dsadr = nand->dma_buff_phys + nand->data_column;
 		desc->dtadr = nand->mmio_phys + NDDB;
 		desc->dcmd |= DCMD_ENDIRQEN | DCMD_INCSRCADDR | DCMD_FLOWTRG |
(data_len + oob_len);
 	} else {
 		if (nand->oob_size > 0) {
-			desc_oob->dtadr = nand->data_buff_phys
+			desc_oob->dtadr = nand->dma_buff_phys
 					+ info->page_size + nand->oob_column;
 			desc_oob->dcmd |= DCMD_ENDIRQEN | DCMD_INCTRGADDR | DCMD_FLOWSRC | oob_len;
-			desc->ddadr = nand->data_desc_addr + sizeof(struct pxa_dma_desc);
+			desc->ddadr = nand->dma_desc_addr + sizeof(struct pxa_dma_desc);
 			desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC | data_len;
 		}
 		else
 			desc->dcmd |= DCMD_ENDIRQEN | DCMD_INCTRGADDR | DCMD_FLOWSRC | data_len;
-		desc->dtadr = nand->data_buff_phys + nand->data_column;
+		desc->dtadr = nand->dma_buff_phys + nand->data_column;
 		desc_oob->dsadr = desc->dsadr = nand->mmio_phys + NDDB;
 	}

 	DRCMR(nand->drcmr_dat) = DRCMR_MAPVLD | nand->data_dma_ch;
-	DDADR(nand->data_dma_ch) = nand->data_desc_addr;
+	DDADR(nand->data_dma_ch) = nand->dma_desc_addr;
 	DCSR(nand->data_dma_ch) |= DCSR_RUN;
 }

@@ -638,6 +639,8 @@ static int prepare_command_pool(struct pxa3xx_nand
*nand, int command,
 	int addr_cycle, exec_cmd, ndcb0, i, chunks = 0;
 	struct mtd_info *mtd;
 	struct pxa3xx_nand_info *info = nand->info[nand->chip_select];
+	struct platform_device *pdev = nand->pdev;
+	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data;

 	mtd = get_mtd_by_info(info);
 	ndcb0 = (nand->chip_select) ? NDCB0_CSEL : 0;;
@@ -647,8 +650,6 @@ static int prepare_command_pool(struct pxa3xx_nand
*nand, int command,
 	/* reset data and oob column point to handle data */
 	nand->data_column	= 0;
 	nand->oob_column	= 0;
-	nand->buf_start		= 0;
-	nand->buf_count		= 0;
 	nand->total_cmds	= 1;
 	nand->cmd_seqs		= 0;
 	nand->data_size		= 0;
@@ -658,26 +659,25 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
 	nand->state		= 0;
 	nand->bad_count		= 0;
 	nand->retcode		= ERR_NONE;
-	nand->command		= command;
+	nand->buf_start		= column;

 	switch (command) {
-	case NAND_CMD_READ0:
 	case NAND_CMD_PAGEPROG:
-		nand->use_ecc = info->use_ecc;
-	case NAND_CMD_READOOB:
+	case NAND_CMD_RNDOUT:
 		pxa3xx_set_datasize(info);
-		nand->oob_buff = nand->data_buff + nand->data_size;
 		nand->use_dma = use_dma;
 		chunks = info->page_size / nand->data_size;
 		break;
-	case NAND_CMD_SEQIN:
-		exec_cmd = 0;
-		break;
 	default:
 		nand->ndcb1 = 0;
 		nand->ndcb2 = 0;
+		nand->use_ecc = ECC_NONE;
 		break;
 	}
+	if (nand->use_dma) {
+		nand->data_buff = nand->dma_buff;
+		nand->oob_buff = nand->dma_buff + mtd->writesize;
+	}

 	/* clear the command buffer */
 	for (i = 0; i < CMD_POOL_SIZE; i ++) {
@@ -688,68 +688,67 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,
 			+ info->col_addr_cycles);

 	switch (command) {
-	case NAND_CMD_READOOB:
 	case NAND_CMD_READ0:
-		cmd  = info->cmdset->read1;
-		if (command == NAND_CMD_READOOB)
-			nand->buf_start = mtd->writesize + column;
-		else
-			nand->buf_start = column;
-
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
-			nand->ndcb0[0] |= NDCB0_CMD_TYPE(0)
-					| addr_cycle
-					| (cmd & NDCB0_CMD1_MASK);
-		else {
-			if (chunks == 1)
-				nand->ndcb0[0] |= NDCB0_CMD_TYPE(0)
-						| NDCB0_DBC
-						| addr_cycle
-						| cmd;
-			else {
-				nand->total_cmds = chunks + 1;
-				nand->ndcb0[0] |= NDCB0_CMD_XTYPE(0x6)
-						| NDCB0_CMD_TYPE(0)
-						| NDCB0_DBC
-						| NDCB0_NC
-						| addr_cycle
-						| cmd;
-
-				nand->ndcb0[1] |= NDCB0_CMD_XTYPE(0x5)
-						| NDCB0_NC
-						| addr_cycle;
-
-				for (i = 2; i <= chunks; i ++)
-					nand->ndcb0[i] = nand->ndcb0[1];
-
-				nand->ndcb0[chunks] &= ~NDCB0_NC;
-				/* we should wait RnB go high again
-				 * before read out data*/
-				nand->wait_ready[1] = 1;
-			}
-		}
-
 	case NAND_CMD_SEQIN:
+		nand->use_ecc = info->use_ecc;
+	case NAND_CMD_READOOB:
+		memset(nand->data_buff, 0xff, column);
+		nand->buf_count = mtd->writesize + mtd->oobsize;
+		exec_cmd = 0;
+		info->page_addr = page_addr;
 		/* small page addr setting */
-		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) {
+		if (unlikely(info->page_size < PAGE_CHUNK_SIZE))
 			nand->ndcb1 = ((page_addr & 0xFFFFFF) << 8)
 					| (column & 0xFF);
-
-			nand->ndcb2 = 0;
-		}
 		else {
 			nand->ndcb1 = ((page_addr & 0xFFFF) << 16)
 					| (column & 0xFFFF);

 			if (page_addr & 0xFF0000)
 				nand->ndcb2 = (page_addr & 0xFF0000) >> 16;
+		}
+		break;
+
+	case NAND_CMD_RNDOUT:
+		cmd = info->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))
+				nand->ndcb0[0] |= NDCB0_CMD_TYPE(0)
+						| addr_cycle
+						| (cmd & NDCB0_CMD1_MASK);
 			else
-				nand->ndcb2 = 0;
+				nand->ndcb0[0] |= NDCB0_CMD_TYPE(0)
+						| NDCB0_DBC
+						| addr_cycle
+						| cmd;
+			break;
 		}

-		nand->buf_count = mtd->writesize + mtd->oobsize;
-		memset(nand->data_buff, 0xFF, nand->buf_count);
+		nand->total_cmds = chunks + 1;
+		nand->ndcb0[0] |= NDCB0_CMD_XTYPE(0x6)
+				| NDCB0_CMD_TYPE(0)
+				| NDCB0_DBC
+				| NDCB0_NC
+				| addr_cycle
+				| cmd;
+
+		nand->ndcb0[1] |= NDCB0_CMD_XTYPE(0x5)
+				| NDCB0_NC
+				| addr_cycle;
+
+		for (i = 2; i <= chunks; i ++)
+			nand->ndcb0[i] = nand->ndcb0[1];

+		nand->ndcb0[chunks] &= ~NDCB0_NC;
+		/* we should wait RnB go high again
+		 * before read out data*/
+		nand->wait_ready[1] = 1;
 		break;

 	case NAND_CMD_PAGEPROG:
@@ -760,40 +759,42 @@ static int prepare_command_pool(struct
pxa3xx_nand *nand, int command,

 		cmd = info->cmdset->program;
 		nand->state |= STATE_IS_WRITE;
-		if (chunks == 1)
+		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)
+			|| !(pdata->controller_attrs & PXA3XX_NAKED_CMD_EN)) {
 			nand->ndcb0[0] |= NDCB0_CMD_TYPE(0x1)
 					| NDCB0_AUTO_RS
 					| NDCB0_ST_ROW_EN
 					| NDCB0_DBC
 					| cmd
 					| addr_cycle;
-		else {
-			nand->total_cmds = chunks + 1;
-			nand->ndcb0[0] |= NDCB0_CMD_XTYPE(0x4)
-					| NDCB0_CMD_TYPE(0x1)
+			break;
+		}
+
+		nand->total_cmds = chunks + 1;
+		nand->ndcb0[0] |= NDCB0_CMD_XTYPE(0x4)
+				| NDCB0_CMD_TYPE(0x1)
+				| NDCB0_NC
+				| NDCB0_AUTO_RS
+				| (cmd & NDCB0_CMD1_MASK)
+				| addr_cycle;
+
+		for (i = 1; i < chunks; i ++)
+			nand->ndcb0[i] |= NDCB0_CMD_XTYPE(0x5)
 					| NDCB0_NC
 					| NDCB0_AUTO_RS
-					| (cmd & NDCB0_CMD1_MASK)
+					| NDCB0_CMD_TYPE(0x1)
 					| addr_cycle;

-			for (i = 1; i < chunks; i ++)
-				nand->ndcb0[i] |= NDCB0_CMD_XTYPE(0x5)
-						| NDCB0_NC
-						| NDCB0_AUTO_RS
-						| NDCB0_CMD_TYPE(0x1)
-						| addr_cycle;
-
-			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;
-			/* we should wait for RnB goes high which
-			 * indicate the data has been written succesfully*/
-			nand->wait_ready[nand->total_cmds] = 1;
-		}
+		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;
+		/* we should wait for RnB goes high which
+		 * indicate the data has been written succesfully*/
+		nand->wait_ready[nand->total_cmds] = 1;
 		break;

 	case NAND_CMD_READID:
@@ -807,6 +808,7 @@ static int prepare_command_pool(struct pxa3xx_nand
*nand, int command,
 		break;
 	case NAND_CMD_STATUS:
 		cmd = info->cmdset->read_status;
+		nand->data_buff = nand->dma_buff;
 		nand->buf_count = 1;
 		nand->ndcb0[0] |= NDCB0_CMD_TYPE(4)
 				| NDCB0_ADDR_CYC(1)
@@ -843,6 +845,7 @@ static int prepare_command_pool(struct pxa3xx_nand
*nand, int command,
 		break;
 	}

+	nand->command = command;
 	return exec_cmd;
 }

@@ -1076,44 +1079,104 @@ static void free_cs_resource(struct
pxa3xx_nand_info *info, int cs)
 	nand->info[cs] = NULL;
 }

-static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
-		struct nand_chip *chip, uint8_t *buf, int page)
+static void pxa3xx_read_page(struct mtd_info *mtd, uint8_t *buf)
 {
 	struct pxa3xx_nand_info *info = mtd->priv;
+	struct nand_chip *chip = mtd->priv;
 	struct pxa3xx_nand *nand = info->nand_data;
+	int buf_blank;

-	chip->read_buf(mtd, buf, mtd->writesize);
-	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
-
-	if (nand->retcode == ERR_SBERR) {
+	nand->data_buff = buf;
+	nand->oob_buff = chip->oob_poi;
+	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RNDOUT, 0, info->page_addr);
+	switch (nand->retcode) {
+	case ERR_SBERR:
 		switch (nand->use_ecc) {
 		case ECC_BCH:
 			if (nand->bad_count > BCH_THRESHOLD)
 				mtd->ecc_stats.corrected +=
 					(nand->bad_count - BCH_THRESHOLD);
 			break;
+
 		case ECC_HAMMIN:
 			mtd->ecc_stats.corrected ++;
+			break;
+
 		case ECC_NONE:
 		default:
 			break;
 		}
-	} else if (nand->retcode == ERR_DBERR) {
-		int buf_blank;
-
-		buf_blank = is_buf_blank(buf, mtd->writesize);
+		break;
+	case ERR_DBERR:
+		buf_blank = is_buf_blank(nand->data_buff, mtd->writesize);
 		if (!buf_blank)
 			mtd->ecc_stats.failed++;
+		break;
+	case ERR_NONE:
+		break;
+	default:
+		mtd->ecc_stats.failed++;
+		break;
+	}
+}
+
+static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
+		struct nand_chip *chip, uint8_t *buf, int page)
+{
+	pxa3xx_read_page(mtd, buf);
+	if (use_dma) {
+		chip->read_buf(mtd, buf, mtd->writesize);
+		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 	}
+	return 0;
+}

+static int pxa3xx_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+		int page, int sndcmd)
+{
+	if (sndcmd) {
+		pxa3xx_nand_cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+		pxa3xx_read_page(mtd, chip->oob_poi);
+	}
+	if (use_dma)
+		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 	return 0;
 }

 static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
 		struct nand_chip *chip, const uint8_t *buf)
 {
-	chip->write_buf(mtd, buf, mtd->writesize);
-	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
+	if (use_dma) {
+		chip->write_buf(mtd, buf, mtd->writesize);
+		chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+	}
+	else {
+		nand->data_buff = (uint8_t *)buf;
+		nand->oob_buff = chip->oob_poi;
+	}
+}
+
+static int pxa3xx_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			      int page)
+{
+	struct pxa3xx_nand_info *info = mtd->priv;
+	struct pxa3xx_nand *nand = info->nand_data;
+	int status = 0;
+
+	nand->data_buff = nand->dma_buff;
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+	if (use_dma)
+		chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+	else
+		nand->oob_buff = chip->oob_poi;
+	/* Send command to program the OOB data */
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+	status = chip->waitfunc(mtd, chip);
+
+	return status & NAND_STATUS_FAIL ? -EIO : 0;
 }

 static void pxa3xx_nand_erase_cmd(struct mtd_info *mtd, int page)
@@ -1162,8 +1225,8 @@ static int __devinit pxa3xx_nand_scan(struct
mtd_info *mtd)
 		return -EINVAL;
 	}

+	nand->data_buff = (unsigned char *)&id;
 	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);
-	id = *((uint16_t *)(nand->data_buff));
 	if (id != 0)
 		dev_info(&nand->pdev->dev, "Detect a flash id %x\n", id);
 	else {
@@ -1321,7 +1384,11 @@ static int alloc_nand_resource(struct
platform_device *pdev)
 		chip = (struct nand_chip *)(&mtd[1]);
 		chip->controller        = &nand->controller;
 		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc;
+		chip->ecc.read_page_raw = pxa3xx_nand_read_page_hwecc;
+		chip->ecc.read_oob      = pxa3xx_nand_read_oob;
 		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc;
+		chip->ecc.write_page_raw= pxa3xx_nand_write_page_hwecc;
+		chip->ecc.write_oob     = pxa3xx_nand_write_oob;
 		chip->waitfunc		= pxa3xx_nand_waitfunc;
 		chip->select_chip	= pxa3xx_nand_select_chip;
 		chip->cmdfunc		= pxa3xx_nand_cmdfunc;
@@ -1333,25 +1400,16 @@ static int alloc_nand_resource(struct
platform_device *pdev)
 		chip->erase_cmd		= pxa3xx_nand_erase_cmd;
 	}

-	if (use_dma == 0) {
-		nand->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL);
-		if (nand->data_buff == NULL) {
-			ret = -ENOMEM;
-			goto fail_free_buf;
-		}
-		goto success_exit;
-	}
-
-	nand->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
-			&nand->data_buff_phys, GFP_KERNEL);
-	if (nand->data_buff == NULL) {
+	nand->dma_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE,
+			&nand->dma_buff_phys, GFP_KERNEL);
+	if (nand->dma_buff == NULL) {
 		dev_err(&pdev->dev, "failed to allocate dma buffer\n");
 		ret = -ENOMEM;
 		goto fail_free_buf;
 	}

-	nand->data_desc = (void *)nand->data_buff + data_desc_offset;
-	nand->data_desc_addr = nand->data_buff_phys + data_desc_offset;
+	nand->data_desc = (void *)nand->dma_buff + data_desc_offset;
+	nand->dma_desc_addr = nand->dma_buff_phys + data_desc_offset;
 	nand->data_dma_ch = pxa_request_dma("nand-data", DMA_PRIO_LOW,
 			pxa3xx_nand_data_dma_irq, nand);
 	if (nand->data_dma_ch < 0) {
@@ -1359,7 +1417,6 @@ static int alloc_nand_resource(struct
platform_device *pdev)
 		ret = -ENXIO;
 		goto fail_free_buf;
 	}
-success_exit:
 	return 0;

 fail_free_buf:
@@ -1409,6 +1466,14 @@ static int pxa3xx_nand_remove(struct
platform_device *pdev)
 		del_mtd_device(mtd);
 		free_cs_resource(info, cs);
 	}
+	if (nand->dma_buff_phys) {
+		if (nand->data_dma_ch >= 0)
+			pxa_free_dma(nand->data_dma_ch);
+		if (nand->dma_buff)
+			dma_free_coherent(&nand->pdev->dev, MAX_BUFF_SIZE,
+					nand->dma_buff, nand->dma_buff_phys);
+		nand->dma_buff_phys = 0;
+	}
 	return 0;
 }

-- 
1.7.0.4



More information about the linux-mtd mailing list