diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-07 09:11:16 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-07 09:11:16 -0800 | 
| commit | e0d65113a70f1dc514e625cc4e7a7485a4bf72df (patch) | |
| tree | 7320a130dc304623f5cf4b5dd8f67fb1776225ca /drivers/mtd/nand/pxa3xx_nand.c | |
| parent | cf5e15fbd72c13977720aa15b7b7e00e1d8fd8f2 (diff) | |
| parent | 48e546b7f281f251893baa40769581fd15f085fb (diff) | |
| download | olio-linux-3.10-e0d65113a70f1dc514e625cc4e7a7485a4bf72df.tar.xz olio-linux-3.10-e0d65113a70f1dc514e625cc4e7a7485a4bf72df.zip  | |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (226 commits)
  mtd: tests: annotate as DANGEROUS in Kconfig
  mtd: tests: don't use mtd0 as a default
  mtd: clean up usage of MTD_DOCPROBE_ADDRESS
  jffs2: add compr=lzo and compr=zlib options
  jffs2: implement mount option parsing and compression overriding
  mtd: nand: initialize ops.mode
  mtd: provide an alias for the redboot module name
  mtd: m25p80: don't probe device which has status of 'disabled'
  mtd: nand_h1900 never worked
  mtd: Add DiskOnChip G3 support
  mtd: m25p80: add EON flash EN25Q32B into spi flash id table
  mtd: mark block device queue as non-rotational
  mtd: r852: make r852_pm_ops static
  mtd: m25p80: add support for at25df321a spi data flash
  mtd: mxc_nand: preset_v1_v2: unlock all NAND flash blocks
  mtd: nand: switch `check_pattern()' to standard `memcmp()'
  mtd: nand: invalidate cache on unaligned reads
  mtd: nand: do not scan bad blocks with NAND_BBT_NO_OOB set
  mtd: nand: wait to set BBT version
  mtd: nand: scrub BBT on ECC errors
  ...
Fix up trivial conflicts:
 - arch/arm/mach-at91/board-usb-a9260.c
	Merged into board-usb-a926x.c
 - drivers/mtd/maps/lantiq-flash.c
	add_mtd_partitions -> mtd_device_register vs changed to use
	mtd_device_parse_register.
Diffstat (limited to 'drivers/mtd/nand/pxa3xx_nand.c')
| -rw-r--r-- | drivers/mtd/nand/pxa3xx_nand.c | 469 | 
1 files changed, 290 insertions, 179 deletions
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 1fb3b3a8058..9eb7f879969 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -110,6 +110,7 @@ enum {  enum {  	STATE_IDLE = 0, +	STATE_PREPARED,  	STATE_CMD_HANDLE,  	STATE_DMA_READING,  	STATE_DMA_WRITING, @@ -120,21 +121,40 @@ enum {  	STATE_READY,  }; -struct pxa3xx_nand_info { -	struct nand_chip	nand_chip; +struct pxa3xx_nand_host { +	struct nand_chip	chip; +	struct pxa3xx_nand_cmdset *cmdset; +	struct mtd_info         *mtd; +	void			*info_data; + +	/* page size of attached chip */ +	unsigned int		page_size; +	int			use_ecc; +	int			cs; +	/* calculated from pxa3xx_nand_flash data */ +	unsigned int		col_addr_cycles; +	unsigned int		row_addr_cycles; +	size_t			read_id_bytes; + +	/* cached register value */ +	uint32_t		reg_ndcr; +	uint32_t		ndtr0cs0; +	uint32_t		ndtr1cs0; +}; + +struct pxa3xx_nand_info {  	struct nand_hw_control	controller;  	struct platform_device	 *pdev; -	struct pxa3xx_nand_cmdset *cmdset;  	struct clk		*clk;  	void __iomem		*mmio_base;  	unsigned long		mmio_phys; +	struct completion	cmd_complete;  	unsigned int 		buf_start;  	unsigned int		buf_count; -	struct mtd_info         *mtd;  	/* DMA information */  	int			drcmr_dat;  	int			drcmr_cmd; @@ -142,44 +162,27 @@ struct pxa3xx_nand_info {  	unsigned char		*data_buff;  	unsigned char		*oob_buff;  	dma_addr_t 		data_buff_phys; -	size_t			data_buff_size;  	int 			data_dma_ch;  	struct pxa_dma_desc	*data_desc;  	dma_addr_t 		data_desc_addr; -	uint32_t		reg_ndcr; - -	/* saved column/page_addr during CMD_SEQIN */ -	int			seqin_column; -	int			seqin_page_addr; - -	/* relate to the command */ +	struct pxa3xx_nand_host *host[NUM_CHIP_SELECT];  	unsigned int		state; +	int			cs;  	int			use_ecc;	/* use HW ECC ? */  	int			use_dma;	/* use DMA ? */  	int			is_ready;  	unsigned int		page_size;	/* page size of attached chip */  	unsigned int		data_size;	/* data size in FIFO */ +	unsigned int		oob_size;  	int 			retcode; -	struct completion 	cmd_complete;  	/* generated NDCBx register values */  	uint32_t		ndcb0;  	uint32_t		ndcb1;  	uint32_t		ndcb2; - -	/* timing calcuted from setting */ -	uint32_t		ndtr0cs0; -	uint32_t		ndtr1cs0; - -	/* calculated from pxa3xx_nand_flash data */ -	size_t		oob_size; -	size_t		read_id_bytes; - -	unsigned int	col_addr_cycles; -	unsigned int	row_addr_cycles;  };  static int use_dma = 1; @@ -225,7 +228,7 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = {  /* Define a default flash type setting serve as flash detecting only */  #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0]) -const char *mtd_names[] = {"pxa3xx_nand-0", NULL}; +const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL};  #define NDTR0_tCH(c)	(min((c), 7) << 19)  #define NDTR0_tCS(c)	(min((c), 7) << 16) @@ -241,9 +244,10 @@ const char *mtd_names[] = {"pxa3xx_nand-0", NULL};  /* convert nano-seconds to nand flash controller clock cycles */  #define ns2cycle(ns, clk)	(int)((ns) * (clk / 1000000) / 1000) -static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, +static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host,  				   const struct pxa3xx_nand_timing *t)  { +	struct pxa3xx_nand_info *info = host->info_data;  	unsigned long nand_clk = clk_get_rate(info->clk);  	uint32_t ndtr0, ndtr1; @@ -258,23 +262,24 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,  		NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) |  		NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); -	info->ndtr0cs0 = ndtr0; -	info->ndtr1cs0 = ndtr1; +	host->ndtr0cs0 = ndtr0; +	host->ndtr1cs0 = ndtr1;  	nand_writel(info, NDTR0CS0, ndtr0);  	nand_writel(info, NDTR1CS0, ndtr1);  }  static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)  { -	int oob_enable = info->reg_ndcr & NDCR_SPARE_EN; +	struct pxa3xx_nand_host *host = info->host[info->cs]; +	int oob_enable = host->reg_ndcr & NDCR_SPARE_EN; -	info->data_size = info->page_size; +	info->data_size = host->page_size;  	if (!oob_enable) {  		info->oob_size = 0;  		return;  	} -	switch (info->page_size) { +	switch (host->page_size) {  	case 2048:  		info->oob_size = (info->use_ecc) ? 40 : 64;  		break; @@ -292,9 +297,10 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info)   */  static void pxa3xx_nand_start(struct pxa3xx_nand_info *info)  { +	struct pxa3xx_nand_host *host = info->host[info->cs];  	uint32_t ndcr; -	ndcr = info->reg_ndcr; +	ndcr = host->reg_ndcr;  	ndcr |= info->use_ecc ? NDCR_ECC_EN : 0;  	ndcr |= info->use_dma ? NDCR_DMA_EN : 0;  	ndcr |= NDCR_ND_RUN; @@ -359,7 +365,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info)  					DIV_ROUND_UP(info->oob_size, 4));  		break;  	default: -		printk(KERN_ERR "%s: invalid state %d\n", __func__, +		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,  				info->state);  		BUG();  	} @@ -385,7 +391,7 @@ static void start_data_dma(struct pxa3xx_nand_info *info)  		desc->dcmd |= DCMD_INCTRGADDR | DCMD_FLOWSRC;  		break;  	default: -		printk(KERN_ERR "%s: invalid state %d\n", __func__, +		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,  				info->state);  		BUG();  	} @@ -416,6 +422,15 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)  {  	struct pxa3xx_nand_info *info = devid;  	unsigned int status, is_completed = 0; +	unsigned int ready, cmd_done; + +	if (info->cs == 0) { +		ready           = NDSR_FLASH_RDY; +		cmd_done        = NDSR_CS0_CMDD; +	} else { +		ready           = NDSR_RDY; +		cmd_done        = NDSR_CS1_CMDD; +	}  	status = nand_readl(info, NDSR); @@ -437,11 +452,11 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)  			handle_data_pio(info);  		}  	} -	if (status & NDSR_CS0_CMDD) { +	if (status & cmd_done) {  		info->state = STATE_CMD_DONE;  		is_completed = 1;  	} -	if (status & NDSR_FLASH_RDY) { +	if (status & ready) {  		info->is_ready = 1;  		info->state = STATE_READY;  	} @@ -463,12 +478,6 @@ NORMAL_IRQ_EXIT:  	return IRQ_HANDLED;  } -static int pxa3xx_nand_dev_ready(struct mtd_info *mtd) -{ -	struct pxa3xx_nand_info *info = mtd->priv; -	return (nand_readl(info, NDSR) & NDSR_RDY) ? 1 : 0; -} -  static inline int is_buf_blank(uint8_t *buf, size_t len)  {  	for (; len > 0; len--) @@ -481,10 +490,12 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  		uint16_t column, int page_addr)  {  	uint16_t cmd; -	int addr_cycle, exec_cmd, ndcb0; -	struct mtd_info *mtd = info->mtd; +	int addr_cycle, exec_cmd; +	struct pxa3xx_nand_host *host; +	struct mtd_info *mtd; -	ndcb0 = 0; +	host = info->host[info->cs]; +	mtd = host->mtd;  	addr_cycle = 0;  	exec_cmd = 1; @@ -495,6 +506,10 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  	info->use_ecc		= 0;  	info->is_ready		= 0;  	info->retcode		= ERR_NONE; +	if (info->cs != 0) +		info->ndcb0 = NDCB0_CSEL; +	else +		info->ndcb0 = 0;  	switch (command) {  	case NAND_CMD_READ0: @@ -512,20 +527,19 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  		break;  	} -	info->ndcb0 = ndcb0; -	addr_cycle = NDCB0_ADDR_CYC(info->row_addr_cycles -				    + info->col_addr_cycles); +	addr_cycle = NDCB0_ADDR_CYC(host->row_addr_cycles +				    + host->col_addr_cycles);  	switch (command) {  	case NAND_CMD_READOOB:  	case NAND_CMD_READ0: -		cmd = info->cmdset->read1; +		cmd = host->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)) +		if (unlikely(host->page_size < PAGE_CHUNK_SIZE))  			info->ndcb0 |= NDCB0_CMD_TYPE(0)  					| addr_cycle  					| (cmd & NDCB0_CMD1_MASK); @@ -537,7 +551,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  	case NAND_CMD_SEQIN:  		/* small page addr setting */ -		if (unlikely(info->page_size < PAGE_CHUNK_SIZE)) { +		if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) {  			info->ndcb1 = ((page_addr & 0xFFFFFF) << 8)  					| (column & 0xFF); @@ -564,7 +578,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  			break;  		} -		cmd = info->cmdset->program; +		cmd = host->cmdset->program;  		info->ndcb0 |= NDCB0_CMD_TYPE(0x1)  				| NDCB0_AUTO_RS  				| NDCB0_ST_ROW_EN @@ -574,8 +588,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  		break;  	case NAND_CMD_READID: -		cmd = info->cmdset->read_id; -		info->buf_count = info->read_id_bytes; +		cmd = host->cmdset->read_id; +		info->buf_count = host->read_id_bytes;  		info->ndcb0 |= NDCB0_CMD_TYPE(3)  				| NDCB0_ADDR_CYC(1)  				| cmd; @@ -583,7 +597,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  		info->data_size = 8;  		break;  	case NAND_CMD_STATUS: -		cmd = info->cmdset->read_status; +		cmd = host->cmdset->read_status;  		info->buf_count = 1;  		info->ndcb0 |= NDCB0_CMD_TYPE(4)  				| NDCB0_ADDR_CYC(1) @@ -593,7 +607,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  		break;  	case NAND_CMD_ERASE1: -		cmd = info->cmdset->erase; +		cmd = host->cmdset->erase;  		info->ndcb0 |= NDCB0_CMD_TYPE(2)  				| NDCB0_AUTO_RS  				| NDCB0_ADDR_CYC(3) @@ -604,7 +618,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  		break;  	case NAND_CMD_RESET: -		cmd = info->cmdset->reset; +		cmd = host->cmdset->reset;  		info->ndcb0 |= NDCB0_CMD_TYPE(5)  				| cmd; @@ -616,8 +630,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  	default:  		exec_cmd = 0; -		printk(KERN_ERR "pxa3xx-nand: non-supported" -			" command %x\n", command); +		dev_err(&info->pdev->dev, "non-supported command %x\n", +				command);  		break;  	} @@ -627,7 +641,8 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command,  static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,  				int column, int page_addr)  { -	struct pxa3xx_nand_info *info = mtd->priv; +	struct pxa3xx_nand_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	int ret, exec_cmd;  	/* @@ -635,9 +650,21 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,  	 * "byte" address into a "word" address appropriate  	 * for indexing a word-oriented device  	 */ -	if (info->reg_ndcr & NDCR_DWIDTH_M) +	if (host->reg_ndcr & NDCR_DWIDTH_M)  		column /= 2; +	/* +	 * There may be different NAND chip hooked to +	 * different chip select, so check whether +	 * chip select has been changed, if yes, reset the timing +	 */ +	if (info->cs != host->cs) { +		info->cs = host->cs; +		nand_writel(info, NDTR0CS0, host->ndtr0cs0); +		nand_writel(info, NDTR1CS0, host->ndtr1cs0); +	} + +	info->state = STATE_PREPARED;  	exec_cmd = prepare_command_pool(info, command, column, page_addr);  	if (exec_cmd) {  		init_completion(&info->cmd_complete); @@ -646,12 +673,12 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,  		ret = wait_for_completion_timeout(&info->cmd_complete,  				CHIP_DELAY_TIMEOUT);  		if (!ret) { -			printk(KERN_ERR "Wait time out!!!\n"); +			dev_err(&info->pdev->dev, "Wait time out!!!\n");  			/* Stop State Machine for next command cycle */  			pxa3xx_nand_stop(info);  		} -		info->state = STATE_IDLE;  	} +	info->state = STATE_IDLE;  }  static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd, @@ -664,7 +691,8 @@ static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,  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; +	struct pxa3xx_nand_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	chip->read_buf(mtd, buf, mtd->writesize);  	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -685,6 +713,8 @@ static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,  		 * OOB, ignore such double bit errors  		 */  		if (is_buf_blank(buf, mtd->writesize)) +			info->retcode = ERR_NONE; +		else  			mtd->ecc_stats.failed++;  	} @@ -693,7 +723,8 @@ static int pxa3xx_nand_read_page_hwecc(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_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	char retval = 0xFF;  	if (info->buf_start < info->buf_count) @@ -705,7 +736,8 @@ static uint8_t pxa3xx_nand_read_byte(struct mtd_info *mtd)  static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)  { -	struct pxa3xx_nand_info *info = mtd->priv; +	struct pxa3xx_nand_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	u16 retval = 0xFFFF;  	if (!(info->buf_start & 0x01) && info->buf_start < info->buf_count) { @@ -717,7 +749,8 @@ static u16 pxa3xx_nand_read_word(struct mtd_info *mtd)  static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)  { -	struct pxa3xx_nand_info *info = mtd->priv; +	struct pxa3xx_nand_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);  	memcpy(buf, info->data_buff + info->buf_start, real_len); @@ -727,7 +760,8 @@ static void pxa3xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)  static void pxa3xx_nand_write_buf(struct mtd_info *mtd,  		const uint8_t *buf, int len)  { -	struct pxa3xx_nand_info *info = mtd->priv; +	struct pxa3xx_nand_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	int real_len = min_t(size_t, len, info->buf_count - info->buf_start);  	memcpy(info->data_buff + info->buf_start, buf, real_len); @@ -747,7 +781,8 @@ static void pxa3xx_nand_select_chip(struct mtd_info *mtd, int chip)  static int pxa3xx_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)  { -	struct pxa3xx_nand_info *info = mtd->priv; +	struct pxa3xx_nand_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	/* pxa3xx_nand_send_command has waited for command complete */  	if (this->state == FL_WRITING || this->state == FL_ERASING) { @@ -770,54 +805,70 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,  {  	struct platform_device *pdev = info->pdev;  	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; +	struct pxa3xx_nand_host *host = info->host[info->cs];  	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) { +		dev_err(&pdev->dev, "Current only support 2048 and 512 size\n");  		return -EINVAL; +	} -	if (f->flash_width != 16 && f->flash_width != 8) +	if (f->flash_width != 16 && f->flash_width != 8) { +		dev_err(&pdev->dev, "Only support 8bit and 16 bit!\n");  		return -EINVAL; +	}  	/* calculate flash information */ -	info->cmdset = &default_cmdset; -	info->page_size = f->page_size; -	info->read_id_bytes = (f->page_size == 2048) ? 4 : 2; +	host->cmdset = &default_cmdset; +	host->page_size = f->page_size; +	host->read_id_bytes = (f->page_size == 2048) ? 4 : 2;  	/* calculate addressing information */ -	info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; +	host->col_addr_cycles = (f->page_size == 2048) ? 2 : 1;  	if (f->num_blocks * f->page_per_block > 65536) -		info->row_addr_cycles = 3; +		host->row_addr_cycles = 3;  	else -		info->row_addr_cycles = 2; +		host->row_addr_cycles = 2;  	ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; -	ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0; +	ndcr |= (host->col_addr_cycles == 2) ? NDCR_RA_START : 0;  	ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0;  	ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0;  	ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0;  	ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; -	ndcr |= NDCR_RD_ID_CNT(info->read_id_bytes); +	ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes);  	ndcr |= NDCR_SPARE_EN; /* enable spare by default */ -	info->reg_ndcr = ndcr; +	host->reg_ndcr = ndcr; -	pxa3xx_nand_set_timing(info, f->timing); +	pxa3xx_nand_set_timing(host, f->timing);  	return 0;  }  static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)  { +	/* +	 * We set 0 by hard coding here, for we don't support keep_config +	 * when there is more than one chip attached to the controller +	 */ +	struct pxa3xx_nand_host *host = info->host[0];  	uint32_t ndcr = nand_readl(info, NDCR); -	info->page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; -	/* set info fields needed to read id */ -	info->read_id_bytes = (info->page_size == 2048) ? 4 : 2; -	info->reg_ndcr = ndcr; -	info->cmdset = &default_cmdset; -	info->ndtr0cs0 = nand_readl(info, NDTR0CS0); -	info->ndtr1cs0 = nand_readl(info, NDTR1CS0); +	if (ndcr & NDCR_PAGE_SZ) { +		host->page_size = 2048; +		host->read_id_bytes = 4; +	} else { +		host->page_size = 512; +		host->read_id_bytes = 2; +	} + +	host->reg_ndcr = ndcr & ~NDCR_INT_MASK; +	host->cmdset = &default_cmdset; + +	host->ndtr0cs0 = nand_readl(info, NDTR0CS0); +	host->ndtr1cs0 = nand_readl(info, NDTR1CS0);  	return 0;  } @@ -847,7 +898,6 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)  		return -ENOMEM;  	} -	info->data_buff_size = MAX_BUFF_SIZE;  	info->data_desc = (void *)info->data_buff + data_desc_offset;  	info->data_desc_addr = info->data_buff_phys + data_desc_offset; @@ -855,7 +905,7 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)  				pxa3xx_nand_data_dma_irq, info);  	if (info->data_dma_ch < 0) {  		dev_err(&pdev->dev, "failed to request data dma\n"); -		dma_free_coherent(&pdev->dev, info->data_buff_size, +		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,  				info->data_buff, info->data_buff_phys);  		return info->data_dma_ch;  	} @@ -865,24 +915,28 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info)  static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info)  { -	struct mtd_info *mtd = info->mtd; -	struct nand_chip *chip = mtd->priv; - +	struct mtd_info *mtd; +	int ret; +	mtd = info->host[info->cs]->mtd;  	/* use the common timing to make a try */ -	pxa3xx_nand_config_flash(info, &builtin_flash_types[0]); -	chip->cmdfunc(mtd, NAND_CMD_RESET, 0, 0); +	ret = pxa3xx_nand_config_flash(info, &builtin_flash_types[0]); +	if (ret) +		return ret; + +	pxa3xx_nand_cmdfunc(mtd, NAND_CMD_RESET, 0, 0);  	if (info->is_ready) -		return 1; -	else  		return 0; + +	return -ENODEV;  }  static int pxa3xx_nand_scan(struct mtd_info *mtd)  { -	struct pxa3xx_nand_info *info = mtd->priv; +	struct pxa3xx_nand_host *host = mtd->priv; +	struct pxa3xx_nand_info *info = host->info_data;  	struct platform_device *pdev = info->pdev;  	struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; -	struct nand_flash_dev pxa3xx_flash_ids[2] = { {NULL,}, {NULL,} }; +	struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL;  	const struct pxa3xx_nand_flash *f = NULL;  	struct nand_chip *chip = mtd->priv;  	uint32_t id = -1; @@ -893,22 +947,20 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)  		goto KEEP_CONFIG;  	ret = pxa3xx_nand_sensing(info); -	if (!ret) { -		kfree(mtd); -		info->mtd = NULL; -		printk(KERN_INFO "There is no nand chip on cs 0!\n"); +	if (ret) { +		dev_info(&info->pdev->dev, "There is no chip on cs %d!\n", +			 info->cs); -		return -EINVAL; +		return ret;  	}  	chip->cmdfunc(mtd, NAND_CMD_READID, 0, 0);  	id = *((uint16_t *)(info->data_buff));  	if (id != 0) -		printk(KERN_INFO "Detect a flash id %x\n", id); +		dev_info(&info->pdev->dev, "Detect a flash id %x\n", id);  	else { -		kfree(mtd); -		info->mtd = NULL; -		printk(KERN_WARNING "Read out ID 0, potential timing set wrong!!\n"); +		dev_warn(&info->pdev->dev, +			 "Read out ID 0, potential timing set wrong!!\n");  		return -EINVAL;  	} @@ -926,14 +978,17 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)  	}  	if (i >= (ARRAY_SIZE(builtin_flash_types) + pdata->num_flash - 1)) { -		kfree(mtd); -		info->mtd = NULL; -		printk(KERN_ERR "ERROR!! flash not defined!!!\n"); +		dev_err(&info->pdev->dev, "ERROR!! flash not defined!!!\n");  		return -EINVAL;  	} -	pxa3xx_nand_config_flash(info, f); +	ret = pxa3xx_nand_config_flash(info, f); +	if (ret) { +		dev_err(&info->pdev->dev, "ERROR! Configure failed\n"); +		return ret; +	} +  	pxa3xx_flash_ids[0].name = f->name;  	pxa3xx_flash_ids[0].id = (f->chip_id >> 8) & 0xffff;  	pxa3xx_flash_ids[0].pagesize = f->page_size; @@ -942,62 +997,78 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd)  	pxa3xx_flash_ids[0].erasesize = f->page_size * f->page_per_block;  	if (f->flash_width == 16)  		pxa3xx_flash_ids[0].options = NAND_BUSWIDTH_16; +	pxa3xx_flash_ids[1].name = NULL; +	def = pxa3xx_flash_ids;  KEEP_CONFIG: -	if (nand_scan_ident(mtd, 1, pxa3xx_flash_ids)) +	chip->ecc.mode = NAND_ECC_HW; +	chip->ecc.size = host->page_size; + +	chip->options = NAND_NO_AUTOINCR; +	chip->options |= NAND_NO_READRDY; +	if (host->reg_ndcr & NDCR_DWIDTH_M) +		chip->options |= NAND_BUSWIDTH_16; + +	if (nand_scan_ident(mtd, 1, def))  		return -ENODEV;  	/* calculate addressing information */ -	info->col_addr_cycles = (mtd->writesize >= 2048) ? 2 : 1; +	if (mtd->writesize >= 2048) +		host->col_addr_cycles = 2; +	else +		host->col_addr_cycles = 1; +  	info->oob_buff = info->data_buff + mtd->writesize;  	if ((mtd->size >> chip->page_shift) > 65536) -		info->row_addr_cycles = 3; +		host->row_addr_cycles = 3;  	else -		info->row_addr_cycles = 2; -	mtd->name = mtd_names[0]; -	chip->ecc.mode = NAND_ECC_HW; -	chip->ecc.size = f->page_size; - -	chip->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16 : 0; -	chip->options |= NAND_NO_AUTOINCR; -	chip->options |= NAND_NO_READRDY; +		host->row_addr_cycles = 2; +	mtd->name = mtd_names[0];  	return nand_scan_tail(mtd);  } -static -struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev) +static int alloc_nand_resource(struct platform_device *pdev)  { +	struct pxa3xx_nand_platform_data *pdata;  	struct pxa3xx_nand_info *info; +	struct pxa3xx_nand_host *host;  	struct nand_chip *chip;  	struct mtd_info *mtd;  	struct resource *r; -	int ret, irq; +	int ret, irq, cs; -	mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct pxa3xx_nand_info), -			GFP_KERNEL); -	if (!mtd) { +	pdata = pdev->dev.platform_data; +	info = kzalloc(sizeof(*info) + (sizeof(*mtd) + +		       sizeof(*host)) * pdata->num_cs, GFP_KERNEL); +	if (!info) {  		dev_err(&pdev->dev, "failed to allocate memory\n"); -		return NULL; +		return -ENOMEM;  	} -	info = (struct pxa3xx_nand_info *)(&mtd[1]); -	chip = (struct nand_chip *)(&mtd[1]);  	info->pdev = pdev; -	info->mtd = mtd; -	mtd->priv = info; -	mtd->owner = THIS_MODULE; +	for (cs = 0; cs < pdata->num_cs; cs++) { +		mtd = (struct mtd_info *)((unsigned int)&info[1] + +		      (sizeof(*mtd) + sizeof(*host)) * cs); +		chip = (struct nand_chip *)(&mtd[1]); +		host = (struct pxa3xx_nand_host *)chip; +		info->host[cs] = host; +		host->mtd = mtd; +		host->cs = cs; +		host->info_data = info; +		mtd->priv = host; +		mtd->owner = THIS_MODULE; -	chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc; -	chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc; -	chip->controller        = &info->controller; -	chip->waitfunc		= pxa3xx_nand_waitfunc; -	chip->select_chip	= pxa3xx_nand_select_chip; -	chip->dev_ready		= pxa3xx_nand_dev_ready; -	chip->cmdfunc		= pxa3xx_nand_cmdfunc; -	chip->read_word		= pxa3xx_nand_read_word; -	chip->read_byte		= pxa3xx_nand_read_byte; -	chip->read_buf		= pxa3xx_nand_read_buf; -	chip->write_buf		= pxa3xx_nand_write_buf; -	chip->verify_buf	= pxa3xx_nand_verify_buf; +		chip->ecc.read_page	= pxa3xx_nand_read_page_hwecc; +		chip->ecc.write_page	= pxa3xx_nand_write_page_hwecc; +		chip->controller        = &info->controller; +		chip->waitfunc		= pxa3xx_nand_waitfunc; +		chip->select_chip	= pxa3xx_nand_select_chip; +		chip->cmdfunc		= pxa3xx_nand_cmdfunc; +		chip->read_word		= pxa3xx_nand_read_word; +		chip->read_byte		= pxa3xx_nand_read_byte; +		chip->read_buf		= pxa3xx_nand_read_buf; +		chip->write_buf		= pxa3xx_nand_write_buf; +		chip->verify_buf	= pxa3xx_nand_verify_buf; +	}  	spin_lock_init(&chip->controller->lock);  	init_waitqueue_head(&chip->controller->wq); @@ -1070,13 +1141,13 @@ struct pxa3xx_nand_info *alloc_nand_resource(struct platform_device *pdev)  	platform_set_drvdata(pdev, info); -	return info; +	return 0;  fail_free_buf:  	free_irq(irq, info);  	if (use_dma) {  		pxa_free_dma(info->data_dma_ch); -		dma_free_coherent(&pdev->dev, info->data_buff_size, +		dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE,  			info->data_buff, info->data_buff_phys);  	} else  		kfree(info->data_buff); @@ -1088,17 +1159,21 @@ fail_put_clk:  	clk_disable(info->clk);  	clk_put(info->clk);  fail_free_mtd: -	kfree(mtd); -	return NULL; +	kfree(info); +	return ret;  }  static int pxa3xx_nand_remove(struct platform_device *pdev)  {  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); -	struct mtd_info *mtd = info->mtd; +	struct pxa3xx_nand_platform_data *pdata;  	struct resource *r; -	int irq; +	int irq, cs; +	if (!info) +		return 0; + +	pdata = pdev->dev.platform_data;  	platform_set_drvdata(pdev, NULL);  	irq = platform_get_irq(pdev, 0); @@ -1106,7 +1181,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)  		free_irq(irq, info);  	if (use_dma) {  		pxa_free_dma(info->data_dma_ch); -		dma_free_writecombine(&pdev->dev, info->data_buff_size, +		dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE,  				info->data_buff, info->data_buff_phys);  	} else  		kfree(info->data_buff); @@ -1118,10 +1193,9 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)  	clk_disable(info->clk);  	clk_put(info->clk); -	if (mtd) { -		mtd_device_unregister(mtd); -		kfree(mtd); -	} +	for (cs = 0; cs < pdata->num_cs; cs++) +		nand_release(info->host[cs]->mtd); +	kfree(info);  	return 0;  } @@ -1129,6 +1203,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)  {  	struct pxa3xx_nand_platform_data *pdata;  	struct pxa3xx_nand_info *info; +	int ret, cs, probe_success;  	pdata = pdev->dev.platform_data;  	if (!pdata) { @@ -1136,52 +1211,88 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)  		return -ENODEV;  	} -	info = alloc_nand_resource(pdev); -	if (info == NULL) -		return -ENOMEM; - -	if (pxa3xx_nand_scan(info->mtd)) { -		dev_err(&pdev->dev, "failed to scan nand\n"); -		pxa3xx_nand_remove(pdev); -		return -ENODEV; +	ret = alloc_nand_resource(pdev); +	if (ret) { +		dev_err(&pdev->dev, "alloc nand resource failed\n"); +		return ret;  	} -	if (mtd_has_cmdlinepart()) { -		const char *probes[] = { "cmdlinepart", NULL }; -		struct mtd_partition *parts; -		int nr_parts; +	info = platform_get_drvdata(pdev); +	probe_success = 0; +	for (cs = 0; cs < pdata->num_cs; cs++) { +		info->cs = cs; +		ret = pxa3xx_nand_scan(info->host[cs]->mtd); +		if (ret) { +			dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", +				cs); +			continue; +		} -		nr_parts = parse_mtd_partitions(info->mtd, probes, &parts, 0); +		ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, 0, +				pdata->parts[cs], pdata->nr_parts[cs]); +		if (!ret) +			probe_success = 1; +	} -		if (nr_parts) -			return mtd_device_register(info->mtd, parts, nr_parts); +	if (!probe_success) { +		pxa3xx_nand_remove(pdev); +		return -ENODEV;  	} -	return mtd_device_register(info->mtd, pdata->parts, pdata->nr_parts); +	return 0;  }  #ifdef CONFIG_PM  static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state)  {  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); -	struct mtd_info *mtd = info->mtd; +	struct pxa3xx_nand_platform_data *pdata; +	struct mtd_info *mtd; +	int cs; +	pdata = pdev->dev.platform_data;  	if (info->state) {  		dev_err(&pdev->dev, "driver busy, state = %d\n", info->state);  		return -EAGAIN;  	} +	for (cs = 0; cs < pdata->num_cs; cs++) { +		mtd = info->host[cs]->mtd; +		mtd->suspend(mtd); +	} +  	return 0;  }  static int pxa3xx_nand_resume(struct platform_device *pdev)  {  	struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); -	struct mtd_info *mtd = info->mtd; +	struct pxa3xx_nand_platform_data *pdata; +	struct mtd_info *mtd; +	int cs; -	nand_writel(info, NDTR0CS0, info->ndtr0cs0); -	nand_writel(info, NDTR1CS0, info->ndtr1cs0); -	clk_enable(info->clk); +	pdata = pdev->dev.platform_data; +	/* We don't want to handle interrupt without calling mtd routine */ +	disable_int(info, NDCR_INT_MASK); + +	/* +	 * Directly set the chip select to a invalid value, +	 * then the driver would reset the timing according +	 * to current chip select at the beginning of cmdfunc +	 */ +	info->cs = 0xff; + +	/* +	 * As the spec says, the NDSR would be updated to 0x1800 when +	 * doing the nand_clk disable/enable. +	 * To prevent it damaging state machine of the driver, clear +	 * all status before resume +	 */ +	nand_writel(info, NDSR, NDSR_MASK); +	for (cs = 0; cs < pdata->num_cs; cs++) { +		mtd = info->host[cs]->mtd; +		mtd->resume(mtd); +	}  	return 0;  }  |