mtd: pxa3xx_nand: rework irq logic
Enable all irq when we start the nand controller, and put all the transaction logic in the pxa3xx_nand_irq. By doing this way, we could dramatically increase the performance by avoid unnecessary delay. Signed-off-by: Lei Wen <leiwen@marvell.com> Signed-off-by: Haojian Zhuang <haojian.zhuang@marvell.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
This commit is contained in:
		
					parent
					
						
							
								e353a20afa
							
						
					
				
			
			
				commit
				
					
						f8155a404d
					
				
			
		
					 1 changed files with 188 additions and 213 deletions
				
			
		|  | @ -27,6 +27,7 @@ | ||||||
| #include <plat/pxa3xx_nand.h> | #include <plat/pxa3xx_nand.h> | ||||||
| 
 | 
 | ||||||
| #define	CHIP_DELAY_TIMEOUT	(2 * HZ/10) | #define	CHIP_DELAY_TIMEOUT	(2 * HZ/10) | ||||||
|  | #define NAND_STOP_DELAY		(2 * HZ/50) | ||||||
| 
 | 
 | ||||||
| /* registers and bit definitions */ | /* registers and bit definitions */ | ||||||
| #define NDCR		(0x00) /* Control register */ | #define NDCR		(0x00) /* Control register */ | ||||||
|  | @ -52,16 +53,18 @@ | ||||||
| #define NDCR_ND_MODE		(0x3 << 21) | #define NDCR_ND_MODE		(0x3 << 21) | ||||||
| #define NDCR_NAND_MODE   	(0x0) | #define NDCR_NAND_MODE   	(0x0) | ||||||
| #define NDCR_CLR_PG_CNT		(0x1 << 20) | #define NDCR_CLR_PG_CNT		(0x1 << 20) | ||||||
| #define NDCR_CLR_ECC		(0x1 << 19) | #define NDCR_STOP_ON_UNCOR	(0x1 << 19) | ||||||
| #define NDCR_RD_ID_CNT_MASK	(0x7 << 16) | #define NDCR_RD_ID_CNT_MASK	(0x7 << 16) | ||||||
| #define NDCR_RD_ID_CNT(x)	(((x) << 16) & NDCR_RD_ID_CNT_MASK) | #define NDCR_RD_ID_CNT(x)	(((x) << 16) & NDCR_RD_ID_CNT_MASK) | ||||||
| 
 | 
 | ||||||
| #define NDCR_RA_START		(0x1 << 15) | #define NDCR_RA_START		(0x1 << 15) | ||||||
| #define NDCR_PG_PER_BLK		(0x1 << 14) | #define NDCR_PG_PER_BLK		(0x1 << 14) | ||||||
| #define NDCR_ND_ARB_EN		(0x1 << 12) | #define NDCR_ND_ARB_EN		(0x1 << 12) | ||||||
|  | #define NDCR_INT_MASK           (0xFFF) | ||||||
| 
 | 
 | ||||||
| #define NDSR_MASK		(0xfff) | #define NDSR_MASK		(0xfff) | ||||||
| #define NDSR_RDY		(0x1 << 11) | #define NDSR_RDY                (0x1 << 12) | ||||||
|  | #define NDSR_FLASH_RDY          (0x1 << 11) | ||||||
| #define NDSR_CS0_PAGED		(0x1 << 10) | #define NDSR_CS0_PAGED		(0x1 << 10) | ||||||
| #define NDSR_CS1_PAGED		(0x1 << 9) | #define NDSR_CS1_PAGED		(0x1 << 9) | ||||||
| #define NDSR_CS0_CMDD		(0x1 << 8) | #define NDSR_CS0_CMDD		(0x1 << 8) | ||||||
|  | @ -104,13 +107,15 @@ enum { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum { | enum { | ||||||
| 	STATE_READY	= 0, | 	STATE_IDLE = 0, | ||||||
| 	STATE_CMD_HANDLE, | 	STATE_CMD_HANDLE, | ||||||
| 	STATE_DMA_READING, | 	STATE_DMA_READING, | ||||||
| 	STATE_DMA_WRITING, | 	STATE_DMA_WRITING, | ||||||
| 	STATE_DMA_DONE, | 	STATE_DMA_DONE, | ||||||
| 	STATE_PIO_READING, | 	STATE_PIO_READING, | ||||||
| 	STATE_PIO_WRITING, | 	STATE_PIO_WRITING, | ||||||
|  | 	STATE_CMD_DONE, | ||||||
|  | 	STATE_READY, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct pxa3xx_nand_info { | struct pxa3xx_nand_info { | ||||||
|  | @ -292,7 +297,48 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, | /**
 | ||||||
|  |  * NOTE: it is a must to set ND_RUN firstly, then write | ||||||
|  |  * command buffer, otherwise, it does not work. | ||||||
|  |  * We enable all the interrupt at the same time, and | ||||||
|  |  * let pxa3xx_nand_irq to handle all logic. | ||||||
|  |  */ | ||||||
|  | static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) | ||||||
|  | { | ||||||
|  | 	uint32_t ndcr; | ||||||
|  | 
 | ||||||
|  | 	ndcr = info->reg_ndcr; | ||||||
|  | 	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; | ||||||
|  | 	ndcr |= info->use_dma ? NDCR_DMA_EN : 0; | ||||||
|  | 	ndcr |= NDCR_ND_RUN; | ||||||
|  | 
 | ||||||
|  | 	/* clear status bits and run */ | ||||||
|  | 	nand_writel(info, NDCR, 0); | ||||||
|  | 	nand_writel(info, NDSR, NDSR_MASK); | ||||||
|  | 	nand_writel(info, NDCR, ndcr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) | ||||||
|  | { | ||||||
|  | 	uint32_t ndcr; | ||||||
|  | 	int timeout = NAND_STOP_DELAY; | ||||||
|  | 
 | ||||||
|  | 	/* wait RUN bit in NDCR become 0 */ | ||||||
|  | 	ndcr = nand_readl(info, NDCR); | ||||||
|  | 	while ((ndcr & NDCR_ND_RUN) && (timeout-- > 0)) { | ||||||
|  | 		ndcr = nand_readl(info, NDCR); | ||||||
|  | 		udelay(1); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (timeout <= 0) { | ||||||
|  | 		ndcr &= ~NDCR_ND_RUN; | ||||||
|  | 		nand_writel(info, NDCR, ndcr); | ||||||
|  | 	} | ||||||
|  | 	/* clear status bits */ | ||||||
|  | 	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) | 		uint16_t cmd, int column, int page_addr) | ||||||
| { | { | ||||||
| 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | ||||||
|  | @ -319,21 +365,18 @@ static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, | ||||||
| 
 | 
 | ||||||
| 	if (cmd == cmdset->program) | 	if (cmd == cmdset->program) | ||||||
| 		info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; | 		info->ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS; | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int prepare_erase_cmd(struct pxa3xx_nand_info *info, | static void prepare_erase_cmd(struct pxa3xx_nand_info *info, | ||||||
| 			uint16_t cmd, int page_addr) | 			uint16_t cmd, int page_addr) | ||||||
| { | { | ||||||
| 	info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); | 	info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); | ||||||
| 	info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); | 	info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3); | ||||||
| 	info->ndcb1 = page_addr; | 	info->ndcb1 = page_addr; | ||||||
| 	info->ndcb2 = 0; | 	info->ndcb2 = 0; | ||||||
| 	return 0; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) | static void prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) | ||||||
| { | { | ||||||
| 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | ||||||
| 
 | 
 | ||||||
|  | @ -343,7 +386,7 @@ static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) | ||||||
| 
 | 
 | ||||||
| 	info->oob_size = 0; | 	info->oob_size = 0; | ||||||
| 	if (cmd == cmdset->read_id) { | 	if (cmd == cmdset->read_id) { | ||||||
| 		info->ndcb0 |= NDCB0_CMD_TYPE(3); | 		info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1); | ||||||
| 		info->data_size = 8; | 		info->data_size = 8; | ||||||
| 	} else if (cmd == cmdset->read_status) { | 	} else if (cmd == cmdset->read_status) { | ||||||
| 		info->ndcb0 |= NDCB0_CMD_TYPE(4); | 		info->ndcb0 |= NDCB0_CMD_TYPE(4); | ||||||
|  | @ -352,9 +395,7 @@ static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) | ||||||
| 		   cmd == cmdset->unlock) { | 		   cmd == cmdset->unlock) { | ||||||
| 		info->ndcb0 |= NDCB0_CMD_TYPE(5); | 		info->ndcb0 |= NDCB0_CMD_TYPE(5); | ||||||
| 	} else | 	} else | ||||||
| 		return -EINVAL; | 		BUG(); | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) | static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) | ||||||
|  | @ -402,10 +443,8 @@ static int write_cmd(struct pxa3xx_nand_info *info) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int handle_data_pio(struct pxa3xx_nand_info *info) | static void handle_data_pio(struct pxa3xx_nand_info *info) | ||||||
| { | { | ||||||
| 	int ret, timeout = CHIP_DELAY_TIMEOUT; |  | ||||||
| 
 |  | ||||||
| 	switch (info->state) { | 	switch (info->state) { | ||||||
| 	case STATE_PIO_WRITING: | 	case STATE_PIO_WRITING: | ||||||
| 		__raw_writesl(info->mmio_base + NDDB, info->data_buff, | 		__raw_writesl(info->mmio_base + NDDB, info->data_buff, | ||||||
|  | @ -413,14 +452,6 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) | ||||||
| 		if (info->oob_size > 0) | 		if (info->oob_size > 0) | ||||||
| 			__raw_writesl(info->mmio_base + NDDB, info->oob_buff, | 			__raw_writesl(info->mmio_base + NDDB, info->oob_buff, | ||||||
| 					DIV_ROUND_UP(info->oob_size, 4)); | 					DIV_ROUND_UP(info->oob_size, 4)); | ||||||
| 
 |  | ||||||
| 		enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); |  | ||||||
| 
 |  | ||||||
| 		ret = wait_for_completion_timeout(&info->cmd_complete, timeout); |  | ||||||
| 		if (!ret) { |  | ||||||
| 			printk(KERN_ERR "program command time out\n"); |  | ||||||
| 			return -1; |  | ||||||
| 		} |  | ||||||
| 		break; | 		break; | ||||||
| 	case STATE_PIO_READING: | 	case STATE_PIO_READING: | ||||||
| 		__raw_readsl(info->mmio_base + NDDB, info->data_buff, | 		__raw_readsl(info->mmio_base + NDDB, info->data_buff, | ||||||
|  | @ -432,14 +463,11 @@ static int handle_data_pio(struct pxa3xx_nand_info *info) | ||||||
| 	default: | 	default: | ||||||
| 		printk(KERN_ERR "%s: invalid state %d\n", __func__, | 		printk(KERN_ERR "%s: invalid state %d\n", __func__, | ||||||
| 				info->state); | 				info->state); | ||||||
| 		return -EINVAL; | 		BUG(); | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	info->state = STATE_READY; |  | ||||||
| 	return 0; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) | static void start_data_dma(struct pxa3xx_nand_info *info) | ||||||
| { | { | ||||||
| 	struct pxa_dma_desc *desc = info->data_desc; | 	struct pxa_dma_desc *desc = info->data_desc; | ||||||
| 	int dma_len = ALIGN(info->data_size + info->oob_size, 32); | 	int dma_len = ALIGN(info->data_size + info->oob_size, 32); | ||||||
|  | @ -447,14 +475,21 @@ static void start_data_dma(struct pxa3xx_nand_info *info, int dir_out) | ||||||
| 	desc->ddadr = DDADR_STOP; | 	desc->ddadr = DDADR_STOP; | ||||||
| 	desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; | 	desc->dcmd = DCMD_ENDIRQEN | DCMD_WIDTH4 | DCMD_BURST32 | dma_len; | ||||||
| 
 | 
 | ||||||
| 	if (dir_out) { | 	switch (info->state) { | ||||||
|  | 	case STATE_DMA_WRITING: | ||||||
| 		desc->dsadr = info->data_buff_phys; | 		desc->dsadr = info->data_buff_phys; | ||||||
| 		desc->dtadr = info->mmio_phys + NDDB; | 		desc->dtadr = info->mmio_phys + NDDB; | ||||||
| 		desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; | 		desc->dcmd |= DCMD_INCSRCADDR | DCMD_FLOWTRG; | ||||||
| 	} else { | 		break; | ||||||
|  | 	case STATE_DMA_READING: | ||||||
| 		desc->dtadr = info->data_buff_phys; | 		desc->dtadr = info->data_buff_phys; | ||||||
| 		desc->dsadr = info->mmio_phys + NDDB; | 		desc->dsadr = info->mmio_phys + NDDB; | ||||||
| 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; | 		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		printk(KERN_ERR "%s: invalid state %d\n", __func__, | ||||||
|  | 				info->state); | ||||||
|  | 		BUG(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch; | 	DRCMR(info->drcmr_dat) = DRCMR_MAPVLD | info->data_dma_ch; | ||||||
|  | @ -472,95 +507,62 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data) | ||||||
| 
 | 
 | ||||||
| 	if (dcsr & DCSR_BUSERR) { | 	if (dcsr & DCSR_BUSERR) { | ||||||
| 		info->retcode = ERR_DMABUSERR; | 		info->retcode = ERR_DMABUSERR; | ||||||
| 		complete(&info->cmd_complete); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (info->state == STATE_DMA_WRITING) { | 	info->state = STATE_DMA_DONE; | ||||||
| 		info->state = STATE_DMA_DONE; | 	enable_int(info, NDCR_INT_MASK); | ||||||
| 		enable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); | 	nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); | ||||||
| 	} else { |  | ||||||
| 		info->state = STATE_READY; |  | ||||||
| 		complete(&info->cmd_complete); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) | static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) | ||||||
| { | { | ||||||
| 	struct pxa3xx_nand_info *info = devid; | 	struct pxa3xx_nand_info *info = devid; | ||||||
| 	unsigned int status; | 	unsigned int status, is_completed = 0; | ||||||
| 
 | 
 | ||||||
| 	status = nand_readl(info, NDSR); | 	status = nand_readl(info, NDSR); | ||||||
| 
 | 
 | ||||||
| 	if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) { | 	if (status & NDSR_DBERR) | ||||||
| 		if (status & NDSR_DBERR) | 		info->retcode = ERR_DBERR; | ||||||
| 			info->retcode = ERR_DBERR; | 	if (status & NDSR_SBERR) | ||||||
| 		else if (status & NDSR_SBERR) | 		info->retcode = ERR_SBERR; | ||||||
| 			info->retcode = ERR_SBERR; | 	if (status & (NDSR_RDDREQ | NDSR_WRDREQ)) { | ||||||
| 
 | 		/* whether use dma to transfer data */ | ||||||
| 		disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |  | ||||||
| 
 |  | ||||||
| 		if (info->use_dma) { | 		if (info->use_dma) { | ||||||
| 			info->state = STATE_DMA_READING; | 			disable_int(info, NDCR_INT_MASK); | ||||||
| 			start_data_dma(info, 0); | 			info->state = (status & NDSR_RDDREQ) ? | ||||||
|  | 				      STATE_DMA_READING : STATE_DMA_WRITING; | ||||||
|  | 			start_data_dma(info); | ||||||
|  | 			goto NORMAL_IRQ_EXIT; | ||||||
| 		} else { | 		} else { | ||||||
| 			info->state = STATE_PIO_READING; | 			info->state = (status & NDSR_RDDREQ) ? | ||||||
| 			complete(&info->cmd_complete); | 				      STATE_PIO_READING : STATE_PIO_WRITING; | ||||||
|  | 			handle_data_pio(info); | ||||||
| 		} | 		} | ||||||
| 	} else if (status & NDSR_WRDREQ) { | 	} | ||||||
| 		disable_int(info, NDSR_WRDREQ); | 	if (status & NDSR_CS0_CMDD) { | ||||||
| 		if (info->use_dma) { | 		info->state = STATE_CMD_DONE; | ||||||
| 			info->state = STATE_DMA_WRITING; | 		is_completed = 1; | ||||||
| 			start_data_dma(info, 1); | 	} | ||||||
| 		} else { | 	if (status & NDSR_FLASH_RDY) | ||||||
| 			info->state = STATE_PIO_WRITING; |  | ||||||
| 			complete(&info->cmd_complete); |  | ||||||
| 		} |  | ||||||
| 	} else if (status & (NDSR_CS0_BBD | NDSR_CS0_CMDD)) { |  | ||||||
| 		if (status & NDSR_CS0_BBD) |  | ||||||
| 			info->retcode = ERR_BBERR; |  | ||||||
| 
 |  | ||||||
| 		disable_int(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); |  | ||||||
| 		info->state = STATE_READY; | 		info->state = STATE_READY; | ||||||
| 		complete(&info->cmd_complete); | 
 | ||||||
|  | 	if (status & NDSR_WRCMDREQ) { | ||||||
|  | 		nand_writel(info, NDSR, NDSR_WRCMDREQ); | ||||||
|  | 		status &= ~NDSR_WRCMDREQ; | ||||||
|  | 		info->state = STATE_CMD_HANDLE; | ||||||
|  | 		nand_writel(info, NDCB0, info->ndcb0); | ||||||
|  | 		nand_writel(info, NDCB0, info->ndcb1); | ||||||
|  | 		nand_writel(info, NDCB0, info->ndcb2); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	/* clear NDSR to let the controller exit the IRQ */ | ||||||
| 	nand_writel(info, NDSR, status); | 	nand_writel(info, NDSR, status); | ||||||
|  | 	if (is_completed) | ||||||
|  | 		complete(&info->cmd_complete); | ||||||
|  | NORMAL_IRQ_EXIT: | ||||||
| 	return IRQ_HANDLED; | 	return IRQ_HANDLED; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int pxa3xx_nand_do_cmd(struct pxa3xx_nand_info *info, uint32_t event) |  | ||||||
| { |  | ||||||
| 	uint32_t ndcr; |  | ||||||
| 	int ret, timeout = CHIP_DELAY_TIMEOUT; |  | ||||||
| 
 |  | ||||||
| 	if (write_cmd(info)) { |  | ||||||
| 		info->retcode = ERR_SENDCMD; |  | ||||||
| 		goto fail_stop; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	info->state = STATE_CMD_HANDLE; |  | ||||||
| 
 |  | ||||||
| 	enable_int(info, event); |  | ||||||
| 
 |  | ||||||
| 	ret = wait_for_completion_timeout(&info->cmd_complete, timeout); |  | ||||||
| 	if (!ret) { |  | ||||||
| 		printk(KERN_ERR "command execution timed out\n"); |  | ||||||
| 		info->retcode = ERR_SENDCMD; |  | ||||||
| 		goto fail_stop; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (info->use_dma == 0 && info->data_size > 0) |  | ||||||
| 		if (handle_data_pio(info)) |  | ||||||
| 			goto fail_stop; |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| 
 |  | ||||||
| fail_stop: |  | ||||||
| 	ndcr = nand_readl(info, NDCR); |  | ||||||
| 	nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); |  | ||||||
| 	udelay(10); |  | ||||||
| 	return -ETIMEDOUT; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) | static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) | ||||||
| { | { | ||||||
| 	struct pxa3xx_nand_info *info = mtd->priv; | 	struct pxa3xx_nand_info *info = mtd->priv; | ||||||
|  | @ -580,14 +582,13 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | ||||||
| { | { | ||||||
| 	struct pxa3xx_nand_info *info = mtd->priv; | 	struct pxa3xx_nand_info *info = mtd->priv; | ||||||
| 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | ||||||
| 	int ret; | 	int ret, exec_cmd = 0; | ||||||
| 
 | 
 | ||||||
| 	info->use_dma = (use_dma) ? 1 : 0; | 	info->use_dma = (use_dma) ? 1 : 0; | ||||||
| 	info->use_ecc = 0; | 	info->use_ecc = 0; | ||||||
| 	info->data_size = 0; | 	info->data_size = 0; | ||||||
| 	info->state = STATE_READY; | 	info->state = 0; | ||||||
| 
 | 	info->retcode = ERR_NONE; | ||||||
| 	init_completion(&info->cmd_complete); |  | ||||||
| 
 | 
 | ||||||
| 	switch (command) { | 	switch (command) { | ||||||
| 	case NAND_CMD_READOOB: | 	case NAND_CMD_READOOB: | ||||||
|  | @ -596,36 +597,18 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | ||||||
| 		info->buf_start = mtd->writesize + column; | 		info->buf_start = mtd->writesize + column; | ||||||
| 		memset(info->data_buff, 0xFF, info->buf_count); | 		memset(info->data_buff, 0xFF, info->buf_count); | ||||||
| 
 | 
 | ||||||
| 		if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) | 		prepare_read_prog_cmd(info, cmdset->read1, column, page_addr); | ||||||
| 			break; | 		exec_cmd = 1; | ||||||
| 
 |  | ||||||
| 		pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |  | ||||||
| 
 |  | ||||||
| 		/* We only are OOB, so if the data has error, does not matter */ |  | ||||||
| 		if (info->retcode == ERR_DBERR) |  | ||||||
| 			info->retcode = ERR_NONE; |  | ||||||
| 		break; | 		break; | ||||||
| 
 | 
 | ||||||
| 	case NAND_CMD_READ0: | 	case NAND_CMD_READ0: | ||||||
| 		info->use_ecc = 1; | 		info->use_ecc = 1; | ||||||
| 		info->retcode = ERR_NONE; |  | ||||||
| 		info->buf_start = column; | 		info->buf_start = column; | ||||||
| 		info->buf_count = mtd->writesize + mtd->oobsize; | 		info->buf_count = mtd->writesize + mtd->oobsize; | ||||||
| 		memset(info->data_buff, 0xFF, info->buf_count); | 		memset(info->data_buff, 0xFF, info->buf_count); | ||||||
| 
 | 
 | ||||||
| 		if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr)) | 		prepare_read_prog_cmd(info, cmdset->read1, column, page_addr); | ||||||
| 			break; | 		exec_cmd = 1; | ||||||
| 
 |  | ||||||
| 		pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR); |  | ||||||
| 
 |  | ||||||
| 		if (info->retcode == ERR_DBERR) { |  | ||||||
| 			/* for blank page (all 0xff), HW will calculate its ECC as
 |  | ||||||
| 			 * 0, which is different from the ECC information within |  | ||||||
| 			 * OOB, ignore such double bit errors |  | ||||||
| 			 */ |  | ||||||
| 			if (is_buf_blank(info->data_buff, mtd->writesize)) |  | ||||||
| 				info->retcode = ERR_NONE; |  | ||||||
| 		} |  | ||||||
| 		break; | 		break; | ||||||
| 	case NAND_CMD_SEQIN: | 	case NAND_CMD_SEQIN: | ||||||
| 		info->buf_start = column; | 		info->buf_start = column; | ||||||
|  | @ -639,17 +622,13 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | ||||||
| 	case NAND_CMD_PAGEPROG: | 	case NAND_CMD_PAGEPROG: | ||||||
| 		info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; | 		info->use_ecc = (info->seqin_column >= mtd->writesize) ? 0 : 1; | ||||||
| 
 | 
 | ||||||
| 		if (prepare_read_prog_cmd(info, cmdset->program, | 		prepare_read_prog_cmd(info, cmdset->program, | ||||||
| 				info->seqin_column, info->seqin_page_addr)) | 				info->seqin_column, info->seqin_page_addr); | ||||||
| 			break; | 		exec_cmd = 1; | ||||||
| 
 |  | ||||||
| 		pxa3xx_nand_do_cmd(info, NDSR_WRDREQ); |  | ||||||
| 		break; | 		break; | ||||||
| 	case NAND_CMD_ERASE1: | 	case NAND_CMD_ERASE1: | ||||||
| 		if (prepare_erase_cmd(info, cmdset->erase, page_addr)) | 		prepare_erase_cmd(info, cmdset->erase, page_addr); | ||||||
| 			break; | 		exec_cmd = 1; | ||||||
| 
 |  | ||||||
| 		pxa3xx_nand_do_cmd(info, NDSR_CS0_BBD | NDSR_CS0_CMDD); |  | ||||||
| 		break; | 		break; | ||||||
| 	case NAND_CMD_ERASE2: | 	case NAND_CMD_ERASE2: | ||||||
| 		break; | 		break; | ||||||
|  | @ -660,42 +639,71 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, | ||||||
| 		info->buf_count = (command == NAND_CMD_READID) ? | 		info->buf_count = (command == NAND_CMD_READID) ? | ||||||
| 				info->read_id_bytes : 1; | 				info->read_id_bytes : 1; | ||||||
| 
 | 
 | ||||||
| 		if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? | 		prepare_other_cmd(info, (command == NAND_CMD_READID) ? | ||||||
| 				cmdset->read_id : cmdset->read_status)) | 				cmdset->read_id : cmdset->read_status); | ||||||
| 			break; | 		exec_cmd = 1; | ||||||
| 
 |  | ||||||
| 		pxa3xx_nand_do_cmd(info, NDSR_RDDREQ); |  | ||||||
| 		break; | 		break; | ||||||
| 	case NAND_CMD_RESET: | 	case NAND_CMD_RESET: | ||||||
| 		if (prepare_other_cmd(info, cmdset->reset)) | 		prepare_other_cmd(info, cmdset->reset); | ||||||
| 			break; | 		exec_cmd = 1; | ||||||
| 
 |  | ||||||
| 		ret = pxa3xx_nand_do_cmd(info, NDSR_CS0_CMDD); |  | ||||||
| 		if (ret == 0) { |  | ||||||
| 			int timeout = 2; |  | ||||||
| 			uint32_t ndcr; |  | ||||||
| 
 |  | ||||||
| 			while (timeout--) { |  | ||||||
| 				if (nand_readl(info, NDSR) & NDSR_RDY) |  | ||||||
| 					break; |  | ||||||
| 				msleep(10); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			ndcr = nand_readl(info, NDCR); |  | ||||||
| 			nand_writel(info, NDCR, ndcr & ~NDCR_ND_RUN); |  | ||||||
| 		} |  | ||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		printk(KERN_ERR "non-supported command.\n"); | 		printk(KERN_ERR "non-supported command.\n"); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (info->retcode == ERR_DBERR) { | 	if (exec_cmd) { | ||||||
| 		printk(KERN_ERR "double bit error @ page %08x\n", page_addr); | 		init_completion(&info->cmd_complete); | ||||||
| 		info->retcode = ERR_NONE; | 		pxa3xx_nand_start(info); | ||||||
|  | 
 | ||||||
|  | 		ret = wait_for_completion_timeout(&info->cmd_complete, | ||||||
|  | 				CHIP_DELAY_TIMEOUT); | ||||||
|  | 		if (!ret) { | ||||||
|  | 			printk(KERN_ERR "Wait time out!!!\n"); | ||||||
|  | 			/* Stop State Machine for next command cycle */ | ||||||
|  | 			pxa3xx_nand_stop(info); | ||||||
|  | 		} | ||||||
|  | 		info->state = STATE_IDLE; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd, | ||||||
|  | 		struct nand_chip *chip, uint8_t *buf, int page) | ||||||
|  | { | ||||||
|  | 	struct pxa3xx_nand_info *info = mtd->priv; | ||||||
|  | 
 | ||||||
|  | 	chip->read_buf(mtd, buf, mtd->writesize); | ||||||
|  | 	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); | ||||||
|  | 
 | ||||||
|  | 	if (info->retcode == ERR_SBERR) { | ||||||
|  | 		switch (info->use_ecc) { | ||||||
|  | 		case 1: | ||||||
|  | 			mtd->ecc_stats.corrected++; | ||||||
|  | 			break; | ||||||
|  | 		case 0: | ||||||
|  | 		default: | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} else if (info->retcode == ERR_DBERR) { | ||||||
|  | 		/*
 | ||||||
|  | 		 * for blank page (all 0xff), HW will calculate its ECC as | ||||||
|  | 		 * 0, which is different from the ECC information within | ||||||
|  | 		 * OOB, ignore such double bit errors | ||||||
|  | 		 */ | ||||||
|  | 		if (is_buf_blank(buf, mtd->writesize)) | ||||||
|  | 			mtd->ecc_stats.failed++; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) | static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd) | ||||||
| { | { | ||||||
| 	struct pxa3xx_nand_info *info = mtd->priv; | 	struct pxa3xx_nand_info *info = mtd->priv; | ||||||
|  | @ -770,47 +778,13 @@ static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void pxa3xx_nand_ecc_hwctl(struct mtd_info *mtd, int mode) |  | ||||||
| { |  | ||||||
| 	return; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int pxa3xx_nand_ecc_calculate(struct mtd_info *mtd, |  | ||||||
| 		const uint8_t *dat, uint8_t *ecc_code) |  | ||||||
| { |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, |  | ||||||
| 		uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) |  | ||||||
| { |  | ||||||
| 	struct pxa3xx_nand_info *info = mtd->priv; |  | ||||||
| 	/*
 |  | ||||||
| 	 * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we |  | ||||||
| 	 * consider it as a ecc error which will tell the caller the |  | ||||||
| 	 * read fail We have distinguish all the errors, but the |  | ||||||
| 	 * nand_read_ecc only check this function return value |  | ||||||
| 	 * |  | ||||||
| 	 * Corrected (single-bit) errors must also be noted. |  | ||||||
| 	 */ |  | ||||||
| 	if (info->retcode == ERR_SBERR) |  | ||||||
| 		return 1; |  | ||||||
| 	else if (info->retcode != ERR_NONE) |  | ||||||
| 		return -1; |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) | static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) | ||||||
| { | { | ||||||
| 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | 	const struct pxa3xx_nand_cmdset *cmdset = info->cmdset; | ||||||
| 	uint32_t ndcr; | 	uint32_t ndcr; | ||||||
| 	uint8_t  id_buff[8]; | 	uint8_t  id_buff[8]; | ||||||
| 
 | 
 | ||||||
| 	if (prepare_other_cmd(info, cmdset->read_id)) { | 	prepare_other_cmd(info, cmdset->read_id); | ||||||
| 		printk(KERN_ERR "failed to prepare command\n"); |  | ||||||
| 		return -EINVAL; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	/* Send command */ | 	/* Send command */ | ||||||
| 	if (write_cmd(info)) | 	if (write_cmd(info)) | ||||||
|  | @ -836,7 +810,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, | ||||||
| { | { | ||||||
| 	struct platform_device *pdev = info->pdev; | 	struct platform_device *pdev = info->pdev; | ||||||
| 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; | 	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; | ||||||
| 	uint32_t ndcr = 0x00000FFF; /* disable all interrupts */ | 	uint32_t ndcr = 0x0; /* enable all interrupts */ | ||||||
| 
 | 
 | ||||||
| 	if (f->page_size != 2048 && f->page_size != 512) | 	if (f->page_size != 2048 && f->page_size != 512) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
|  | @ -888,11 +862,12 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) | ||||||
| 	info->reg_ndcr = ndcr; | 	info->reg_ndcr = ndcr; | ||||||
| 	info->cmdset = &default_cmdset; | 	info->cmdset = &default_cmdset; | ||||||
| 
 | 
 | ||||||
| 	if (__readid(info, &id)) | 	pxa3xx_nand_cmdfunc(info->mtd, NAND_CMD_READID, 0, 0); | ||||||
|  | 	id = *((uint16_t *)(info->data_buff)); | ||||||
|  | 	if (id == 0) | ||||||
| 		return -ENODEV; | 		return -ENODEV; | ||||||
| 
 | 
 | ||||||
| 	/* Lookup the flash id */ | 	/* Lookup the flash id */ | ||||||
| 	id = (id >> 8) & 0xff;		/* device id is byte 2 */ |  | ||||||
| 	for (i = 0; nand_flash_ids[i].name != NULL; i++) { | 	for (i = 0; nand_flash_ids[i].name != NULL; i++) { | ||||||
| 		if (id == nand_flash_ids[i].id) { | 		if (id == nand_flash_ids[i].id) { | ||||||
| 			type =  &nand_flash_ids[i]; | 			type =  &nand_flash_ids[i]; | ||||||
|  | @ -935,8 +910,8 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, | ||||||
| 	/* we use default timing to detect id */ | 	/* we use default timing to detect id */ | ||||||
| 	f = DEFAULT_FLASH_TYPE; | 	f = DEFAULT_FLASH_TYPE; | ||||||
| 	pxa3xx_nand_config_flash(info, f); | 	pxa3xx_nand_config_flash(info, f); | ||||||
| 	if (__readid(info, &id)) | 	pxa3xx_nand_cmdfunc(info->mtd, NAND_CMD_READID, 0, 0); | ||||||
| 		goto fail_detect; | 	id = *((uint16_t *)(info->data_buff)); | ||||||
| 
 | 
 | ||||||
| 	for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) { | 	for (i=0; i<ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1; i++) { | ||||||
| 		/* we first choose the flash definition from platfrom */ | 		/* we first choose the flash definition from platfrom */ | ||||||
|  | @ -954,7 +929,6 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, | ||||||
| 	dev_warn(&info->pdev->dev, | 	dev_warn(&info->pdev->dev, | ||||||
| 		 "failed to detect configured nand flash; found %04x instead of\n", | 		 "failed to detect configured nand flash; found %04x instead of\n", | ||||||
| 		 id); | 		 id); | ||||||
| fail_detect: |  | ||||||
| 	return -ENODEV; | 	return -ENODEV; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1025,6 +999,8 @@ static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, | ||||||
| 	this->select_chip	= pxa3xx_nand_select_chip; | 	this->select_chip	= pxa3xx_nand_select_chip; | ||||||
| 	this->dev_ready		= pxa3xx_nand_dev_ready; | 	this->dev_ready		= pxa3xx_nand_dev_ready; | ||||||
| 	this->cmdfunc		= pxa3xx_nand_cmdfunc; | 	this->cmdfunc		= pxa3xx_nand_cmdfunc; | ||||||
|  | 	this->ecc.read_page	= pxa3xx_nand_read_page_hwecc; | ||||||
|  | 	this->ecc.write_page	= pxa3xx_nand_write_page_hwecc; | ||||||
| 	this->read_word		= pxa3xx_nand_read_word; | 	this->read_word		= pxa3xx_nand_read_word; | ||||||
| 	this->read_byte		= pxa3xx_nand_read_byte; | 	this->read_byte		= pxa3xx_nand_read_byte; | ||||||
| 	this->read_buf		= pxa3xx_nand_read_buf; | 	this->read_buf		= pxa3xx_nand_read_buf; | ||||||
|  | @ -1032,9 +1008,6 @@ static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, | ||||||
| 	this->verify_buf	= pxa3xx_nand_verify_buf; | 	this->verify_buf	= pxa3xx_nand_verify_buf; | ||||||
| 
 | 
 | ||||||
| 	this->ecc.mode		= NAND_ECC_HW; | 	this->ecc.mode		= NAND_ECC_HW; | ||||||
| 	this->ecc.hwctl		= pxa3xx_nand_ecc_hwctl; |  | ||||||
| 	this->ecc.calculate	= pxa3xx_nand_ecc_calculate; |  | ||||||
| 	this->ecc.correct	= pxa3xx_nand_ecc_correct; |  | ||||||
| 	this->ecc.size		= info->page_size; | 	this->ecc.size		= info->page_size; | ||||||
| 
 | 
 | ||||||
| 	if (info->page_size == 2048) | 	if (info->page_size == 2048) | ||||||
|  | @ -1177,10 +1150,6 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) | ||||||
| 
 | 
 | ||||||
| 	platform_set_drvdata(pdev, NULL); | 	platform_set_drvdata(pdev, NULL); | ||||||
| 
 | 
 | ||||||
| 	del_mtd_device(mtd); |  | ||||||
| #ifdef CONFIG_MTD_PARTITIONS |  | ||||||
| 	del_mtd_partitions(mtd); |  | ||||||
| #endif |  | ||||||
| 	irq = platform_get_irq(pdev, 0); | 	irq = platform_get_irq(pdev, 0); | ||||||
| 	if (irq >= 0) | 	if (irq >= 0) | ||||||
| 		free_irq(irq, info); | 		free_irq(irq, info); | ||||||
|  | @ -1198,7 +1167,13 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) | ||||||
| 	clk_disable(info->clk); | 	clk_disable(info->clk); | ||||||
| 	clk_put(info->clk); | 	clk_put(info->clk); | ||||||
| 
 | 
 | ||||||
| 	kfree(mtd); | 	if (mtd) { | ||||||
|  | 		del_mtd_device(mtd); | ||||||
|  | #ifdef CONFIG_MTD_PARTITIONS | ||||||
|  | 		del_mtd_partitions(mtd); | ||||||
|  | #endif | ||||||
|  | 		kfree(mtd); | ||||||
|  | 	} | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1232,10 +1207,10 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) | ||||||
| 		nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0); | 		nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0); | ||||||
| 
 | 
 | ||||||
| 		if (nr_parts) | 		if (nr_parts) | ||||||
| 			return add_mtd_partitions(mtd, parts, nr_parts); | 			return add_mtd_partitions(info->mtd, parts, nr_parts); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); | 	return add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts); | ||||||
| #else | #else | ||||||
| 	return 0; | 	return 0; | ||||||
| #endif | #endif | ||||||
|  | @ -1247,7 +1222,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) | ||||||
| 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); | 	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); | ||||||
| 	struct mtd_info *mtd = info->mtd; | 	struct mtd_info *mtd = info->mtd; | ||||||
| 
 | 
 | ||||||
| 	if (info->state != STATE_READY) { | 	if (info->state) { | ||||||
| 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); | 		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); | ||||||
| 		return -EAGAIN; | 		return -EAGAIN; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Lei Wen
				Lei Wen