diff options
Diffstat (limited to 'drivers/mtd/onenand/onenand_base.c')
| -rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 508 | 
1 files changed, 357 insertions, 151 deletions
| diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 9b7bf3aa3..66214e86d 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -78,20 +78,11 @@ static void onenand_writew(unsigned short value, void __iomem * addr)   *   * Setup Start Address 1 Register (F100h)   */ -static int onenand_block_address(int device, int block) +static int onenand_block_address(struct onenand_chip *this, int block)  { -	if (device & ONENAND_DEVICE_IS_DDP) { -		/* Device Flash Core select, NAND Flash Block Address */ -		int dfs = 0, density, mask; - -		density = device >> ONENAND_DEVICE_DENSITY_SHIFT; -		mask = (1 << (density + 6)); - -		if (block & mask) -			dfs = 1; - -		return (dfs << ONENAND_DDP_SHIFT) | (block & (mask - 1)); -	} +	/* Device Flash Core select, NAND Flash Block Address */ +	if (block & this->density_mask) +		return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);  	return block;  } @@ -104,22 +95,13 @@ static int onenand_block_address(int device, int block)   *   * Setup Start Address 2 Register (F101h) for DDP   */ -static int onenand_bufferram_address(int device, int block) +static int onenand_bufferram_address(struct onenand_chip *this, int block)  { -	if (device & ONENAND_DEVICE_IS_DDP) { -		/* Device BufferRAM Select */ -		int dbs = 0, density, mask; - -		density = device >> ONENAND_DEVICE_DENSITY_SHIFT; -		mask = (1 << (density + 6)); - -		if (block & mask) -			dbs = 1; +	/* Device BufferRAM Select */ +	if (block & this->density_mask) +		return ONENAND_DDP_CHIP1; -		return (dbs << ONENAND_DDP_SHIFT); -	} - -	return 0; +	return ONENAND_DDP_CHIP0;  }  /** @@ -169,6 +151,18 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)  }  /** + * onenand_get_density - [DEFAULT] Get OneNAND density + * @param dev_id        OneNAND device ID + * + * Get OneNAND density from device ID + */ +static inline int onenand_get_density(int dev_id) +{ +	int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; +	return (density & ONENAND_DEVICE_DENSITY_MASK); +} + +/**   * onenand_command - [DEFAULT] Send command to OneNAND device   * @param mtd		MTD device structure   * @param cmd		the command to be sent @@ -192,6 +186,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,  	case ONENAND_CMD_UNLOCK:  	case ONENAND_CMD_LOCK:  	case ONENAND_CMD_LOCK_TIGHT: +	case ONENAND_CMD_UNLOCK_ALL:  		block = -1;  		page = -1;  		break; @@ -212,7 +207,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,  	/* NOTE: The setting order of the registers is very important! */  	if (cmd == ONENAND_CMD_BUFFERRAM) {  		/* Select DataRAM for DDP */ -		value = onenand_bufferram_address(this->device_id, block); +		value = onenand_bufferram_address(this, block);  		this->write_word(value,  				 this->base + ONENAND_REG_START_ADDRESS2); @@ -224,9 +219,14 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,  	if (block != -1) {  		/* Write 'DFS, FBA' of Flash */ -		value = onenand_block_address(this->device_id, block); +		value = onenand_block_address(this, block);  		this->write_word(value,  				 this->base + ONENAND_REG_START_ADDRESS1); + +		/* Write 'DFS, FBA' of Flash */ +		value = onenand_bufferram_address(this, block); +		this->write_word(value, +				 this->base + ONENAND_REG_START_ADDRESS2);  	}  	if (page != -1) { @@ -252,15 +252,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,  		/* Write 'BSA, BSC' of DataRAM */  		value = onenand_buffer_address(dataram, sectors, count);  		this->write_word(value, this->base + ONENAND_REG_START_BUFFER); - -		if (readcmd) { -			/* Select DataRAM for DDP */ -			value = -			    onenand_bufferram_address(this->device_id, block); -			this->write_word(value, -					 this->base + -					 ONENAND_REG_START_ADDRESS2); -		}  	}  	/* Interrupt clear */ @@ -296,14 +287,11 @@ static int onenand_wait(struct mtd_info *mtd, int state)  	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);  	if (ctrl & ONENAND_CTRL_ERROR) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, -			  "onenand_wait: controller error = 0x%04x\n", ctrl); -		return -EAGAIN; -	} +		printk("onenand_wait: controller error = 0x%04x\n", ctrl); +		if (ctrl & ONENAND_CTRL_LOCK) +			printk("onenand_wait: it's locked error = 0x%04x\n", +				ctrl); -	if (ctrl & ONENAND_CTRL_LOCK) { -		MTDDEBUG (MTD_DEBUG_LEVEL0, -			  "onenand_wait: it's locked error = 0x%04x\n", ctrl);  		return -EIO;  	} @@ -351,7 +339,7 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)   *   * Read the BufferRAM area   */ -static int onenand_read_bufferram(struct mtd_info *mtd, int area, +static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,  				  unsigned char *buffer, int offset,  				  size_t count)  { @@ -376,7 +364,7 @@ static int onenand_read_bufferram(struct mtd_info *mtd, int area,   *   * Read the BufferRAM area with Sync. Burst Mode   */ -static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area, +static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,  				       unsigned char *buffer, int offset,  				       size_t count)  { @@ -405,7 +393,7 @@ static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,   *   * Write the BufferRAM area   */ -static int onenand_write_bufferram(struct mtd_info *mtd, int area, +static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area,  				   const unsigned char *buffer, int offset,  				   size_t count)  { @@ -431,21 +419,39 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area,  static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)  {  	struct onenand_chip *this = mtd->priv; -	int block, page; -	int i; +	int blockpage, found = 0; +	unsigned int i; -	block = (int)(addr >> this->erase_shift); -	page = (int)(addr >> this->page_shift); -	page &= this->page_mask; +#ifdef CONFIG_S3C64XX +	return 0; +#endif -	i = ONENAND_CURRENT_BUFFERRAM(this); +	if (ONENAND_IS_2PLANE(this)) +		blockpage = onenand_get_2x_blockpage(mtd, addr); +	else +		blockpage = (int) (addr >> this->page_shift);  	/* Is there valid data? */ -	if (this->bufferram[i].block == block && -	    this->bufferram[i].page == page && this->bufferram[i].valid) -		return 1; +	i = ONENAND_CURRENT_BUFFERRAM(this); +	if (this->bufferram[i].blockpage == blockpage) +		found = 1; +	else { +		/* Check another BufferRAM */ +		i = ONENAND_NEXT_BUFFERRAM(this); +		if (this->bufferram[i].blockpage == blockpage) { +			ONENAND_SET_NEXT_BUFFERRAM(this); +			found = 1; +		} +	} -	return 0; +	if (found && ONENAND_IS_DDP(this)) { +		/* Select DataRAM for DDP */ +		int block = (int) (addr >> this->erase_shift); +		int value = onenand_bufferram_address(this, block); +		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); +	} + +	return found;  }  /** @@ -460,25 +466,25 @@ static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,  				    int valid)  {  	struct onenand_chip *this = mtd->priv; -	int block, page; -	int i; +	int blockpage; +	unsigned int i; -	block = (int)(addr >> this->erase_shift); -	page = (int)(addr >> this->page_shift); -	page &= this->page_mask; +	if (ONENAND_IS_2PLANE(this)) +		blockpage = onenand_get_2x_blockpage(mtd, addr); +	else +		blockpage = (int)(addr >> this->page_shift); -	/* Invalidate BufferRAM */ -	for (i = 0; i < MAX_BUFFERRAM; i++) { -		if (this->bufferram[i].block == block && -		    this->bufferram[i].page == page) -			this->bufferram[i].valid = 0; -	} +	/* Invalidate another BufferRAM */ +	i = ONENAND_NEXT_BUFFERRAM(this); +	if (this->bufferram[i].blockpage == blockpage) +		this->bufferram[i].blockpage = -1;  	/* Update BufferRAM */  	i = ONENAND_CURRENT_BUFFERRAM(this); -	this->bufferram[i].block = block; -	this->bufferram[i].page = page; -	this->bufferram[i].valid = valid; +	if (valid) +		this->bufferram[i].blockpage = blockpage; +	else +		this->bufferram[i].blockpage = -1;  	return 0;  } @@ -500,10 +506,10 @@ static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,  	/* Invalidate BufferRAM */  	for (i = 0; i < MAX_BUFFERRAM; i++) { -		loff_t buf_addr = this->bufferram[i].block << this->erase_shift; +		loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;  		if (buf_addr >= addr && buf_addr < end_addr) -			this->bufferram[i].valid = 0; +			this->bufferram[i].blockpage = -1;  	}  } @@ -556,7 +562,7 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,  			readend += free->offset - lastgap;  		lastgap = free->offset + free->length;  	} -	this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); +	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);  	free = this->ecclayout->oobfree;  	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {  		int free_end = free->offset + free->length; @@ -594,9 +600,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  	int ret = 0, boundary = 0;  	int writesize = this->writesize; -	MTDDEBUG(MTD_DEBUG_LEVEL3, -		"onenand_read_ops_nolock: from = 0x%08x, len = %i\n", -		(unsigned int) from, (int) len); +	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);  	if (ops->mode == MTD_OOB_AUTO)  		oobsize = this->ecclayout->oobavail; @@ -620,6 +624,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  	/* Do first load to bufferRAM */  	if (read < len) {  		if (!onenand_check_bufferram(mtd, from)) { +			this->main_buf = buf;  			this->command(mtd, ONENAND_CMD_READ, from, writesize);  			ret = this->wait(mtd, FL_READING);  			onenand_update_bufferram(mtd, from, !ret); @@ -637,6 +642,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  		/* If there is more to load then start next load */  		from += thislen;  		if (read + thislen < len) { +			this->main_buf = buf + thislen;  			this->command(mtd, ONENAND_CMD_READ, from, writesize);  			/*  			 * Chip boundary handling in DDP @@ -653,7 +659,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  		}  		/* While load is going, read from last bufferRAM */ -		this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); +		this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen);  		/* Read oob area if needed */  		if (oobbuf) { @@ -663,7 +669,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,  			if (ops->mode == MTD_OOB_AUTO)  				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);  			else -				this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); +				this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);  			oobread += thisooblen;  			oobbuf += thisooblen;  			oobcolumn = 0; @@ -726,9 +732,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,  	from += ops->ooboffs; -	MTDDEBUG(MTD_DEBUG_LEVEL3, -		"onenand_read_oob_nolock: from = 0x%08x, len = %i\n", -		(unsigned int) from, (int) len); +	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);  	/* Initialize return length value */  	ops->oobretlen = 0; @@ -759,6 +763,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,  		thislen = oobsize - column;  		thislen = min_t(int, thislen, len); +		this->spare_buf = buf;  		this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);  		onenand_update_bufferram(mtd, from, 0); @@ -772,7 +777,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,  		if (mode == MTD_OOB_AUTO)  			onenand_transfer_auto_oob(mtd, buf, column, thislen);  		else -			this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); +			this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);  		read += thislen; @@ -886,12 +891,6 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)  	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);  	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); -	/* Initial bad block case: 0x2400 or 0x0400 */ -	if (ctrl & ONENAND_CTRL_ERROR) { -		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); -		return ONENAND_BBT_READ_ERROR; -	} -  	if (interrupt & ONENAND_INT_READ) {  		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);  		if (ecc & ONENAND_ECC_2BIT_ALL) @@ -902,6 +901,12 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)  		return ONENAND_BBT_READ_FATAL_ERROR;  	} +	/* Initial bad block case: 0x2400 or 0x0400 */ +	if (ctrl & ONENAND_CTRL_ERROR) { +		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); +		return ONENAND_BBT_READ_ERROR; +	} +  	return 0;  } @@ -922,9 +927,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,  	size_t len = ops->ooblen;  	u_char *buf = ops->oobbuf; -	MTDDEBUG(MTD_DEBUG_LEVEL3, -		"onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", -		(unsigned int) from, len); +	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);  	/* Initialize return value */  	ops->oobretlen = 0; @@ -945,15 +948,16 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,  		thislen = mtd->oobsize - column;  		thislen = min_t(int, thislen, len); +		this->spare_buf = buf;  		this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);  		onenand_update_bufferram(mtd, from, 0); -		ret = onenand_bbt_wait(mtd, FL_READING); +		ret = this->bbt_wait(mtd, FL_READING);  		if (ret)  			break; -		this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); +		this->read_spareram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);  		read += thislen;  		if (read == len)  			break; @@ -995,7 +999,7 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to  	if (status)  		return status; -	this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); +	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);  	for (i = 0; i < mtd->oobsize; i++)  		if (buf[i] != 0xFF && buf[i] != oob_buf[i])  			return -EBADMSG; @@ -1115,9 +1119,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,  	u_char *oobbuf;  	int ret = 0; -	MTDDEBUG(MTD_DEBUG_LEVEL3, -		"onenand_write_ops_nolock: to = 0x%08x, len = %i\n", -		(unsigned int) to, (int) len); +	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);  	/* Initialize retlen, in case of early exit */  	ops->retlen = 0; @@ -1161,7 +1163,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,  			wbuf = this->page_buf;  		} -		this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); +		this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize);  		if (oob) {  			oobbuf = this->oob_buf; @@ -1180,7 +1182,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,  		} else  			oobbuf = (u_char *) ffchars; -		this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); +		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);  		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); @@ -1244,9 +1246,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,  	to += ops->ooboffs; -	MTDDEBUG(MTD_DEBUG_LEVEL3, -		"onenand_write_oob_nolock: to = 0x%08x, len = %i\n", -		(unsigned int) to, (int) len); +	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);  	/* Initialize retlen, in case of early exit */  	ops->oobretlen = 0; @@ -1293,7 +1293,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,  			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);  		else  			memcpy(oobbuf + column, buf, thislen); -		this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); +		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);  		this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); @@ -1466,7 +1466,14 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)  	while (len) { -		/* TODO Check badblock */ +		/* Check if we have a bad block, we do not erase bad blocks */ +		if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) { +			printk(KERN_WARNING "onenand_erase: attempt to erase" +				" a bad block at addr 0x%08x\n", +				(unsigned int) addr); +			instr->state = MTD_ERASE_FAILED; +			goto erase_exit; +		}  		this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); @@ -1482,8 +1489,16 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)  				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "  					  "Failed erase, block %d\n",  					  (unsigned)(addr >> this->erase_shift)); +			if (ret == -EPERM) +				printk("onenand_erase: " +					  "Device is write protected!!!\n"); +			else +				printk("onenand_erase: " +					  "Failed erase, block %d\n", +					  (unsigned)(addr >> this->erase_shift));  			instr->state = MTD_ERASE_FAILED;  			instr->fail_addr = addr; +  			goto erase_exit;  		} @@ -1493,7 +1508,7 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)  	instr->state = MTD_ERASE_DONE; -      erase_exit: +erase_exit:  	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;  	/* Do call back function */ @@ -1569,23 +1584,30 @@ int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)  }  /** - * onenand_unlock - [MTD Interface] Unlock block(s) - * @param mtd		MTD device structure - * @param ofs		offset relative to mtd start - * @param len		number of bytes to unlock + * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) + * @param mtd           MTD device structure + * @param ofs           offset relative to mtd start + * @param len           number of bytes to lock or unlock + * @param cmd           lock or unlock command   * - * Unlock one or more blocks + * Lock or unlock one or more blocks   */ -int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)  {  	struct onenand_chip *this = mtd->priv;  	int start, end, block, value, status; +	int wp_status_mask;  	start = ofs >> this->erase_shift;  	end = len >> this->erase_shift; +	if (cmd == ONENAND_CMD_LOCK) +		wp_status_mask = ONENAND_WP_LS; +	else +		wp_status_mask = ONENAND_WP_US; +  	/* Continuous lock scheme */ -	if (this->options & ONENAND_CONT_LOCK) { +	if (this->options & ONENAND_HAS_CONT_LOCK) {  		/* Set start block address */  		this->write_word(start,  				 this->base + ONENAND_REG_START_BLOCK_ADDRESS); @@ -1593,7 +1615,7 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)  		this->write_word(end - 1,  				 this->base + ONENAND_REG_END_BLOCK_ADDRESS);  		/* Write unlock command */ -		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); +		this->command(mtd, cmd, 0, 0);  		/* There's no return value */  		this->wait(mtd, FL_UNLOCKING); @@ -1612,7 +1634,14 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)  	}  	/* Block lock scheme */ -	for (block = start; block < end; block++) { +	for (block = start; block < start + end; block++) { +		/* Set block address */ +		value = onenand_block_address(this, block); +		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); +		/* Select DataRAM for DDP */ +		value = onenand_bufferram_address(this, block); +		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); +  		/* Set start block address */  		this->write_word(block,  				 this->base + ONENAND_REG_START_BLOCK_ADDRESS); @@ -1627,11 +1656,6 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)  		       & ONENAND_CTRL_ONGO)  			continue; -		/* Set block address for read block status */ -		value = onenand_block_address(this->device_id, block); -		this->write_word(value, -				 this->base + ONENAND_REG_START_ADDRESS1); -  		/* Check lock status */  		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);  		if (!(status & ONENAND_WP_US)) @@ -1643,31 +1667,196 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)  }  /** + * onenand_lock - [MTD Interface] Lock block(s) + * @param mtd           MTD device structure + * @param ofs           offset relative to mtd start + * @param len           number of bytes to unlock + * + * Lock one or more blocks + */ +static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ +	int ret; + +	onenand_get_device(mtd, FL_LOCKING); +	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); +	onenand_release_device(mtd); +	return ret; +} + +/** + * onenand_unlock - [MTD Interface] Unlock block(s) + * @param mtd           MTD device structure + * @param ofs           offset relative to mtd start + * @param len           number of bytes to unlock + * + * Unlock one or more blocks + */ +static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ +	int ret; + +	onenand_get_device(mtd, FL_LOCKING); +	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); +	onenand_release_device(mtd); +	return ret; +} + +/** + * onenand_check_lock_status - [OneNAND Interface] Check lock status + * @param this          onenand chip data structure + * + * Check lock status + */ +static int onenand_check_lock_status(struct onenand_chip *this) +{ +	unsigned int value, block, status; +	unsigned int end; + +	end = this->chipsize >> this->erase_shift; +	for (block = 0; block < end; block++) { +		/* Set block address */ +		value = onenand_block_address(this, block); +		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); +		/* Select DataRAM for DDP */ +		value = onenand_bufferram_address(this, block); +		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); +		/* Set start block address */ +		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); + +		/* Check lock status */ +		status = this->read_word(this->base + ONENAND_REG_WP_STATUS); +		if (!(status & ONENAND_WP_US)) { +			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); +			return 0; +		} +	} + +	return 1; +} + +/** + * onenand_unlock_all - [OneNAND Interface] unlock all blocks + * @param mtd           MTD device structure + * + * Unlock all blocks + */ +static void onenand_unlock_all(struct mtd_info *mtd) +{ +	struct onenand_chip *this = mtd->priv; +	loff_t ofs = 0; +	size_t len = this->chipsize; + +	if (this->options & ONENAND_HAS_UNLOCK_ALL) { +		/* Set start block address */ +		this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); +		/* Write unlock command */ +		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + +		/* There's no return value */ +		this->wait(mtd, FL_LOCKING); + +		/* Sanity check */ +		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) +				& ONENAND_CTRL_ONGO) +			continue; + +		return; + +		/* Check lock status */ +		if (onenand_check_lock_status(this)) +			return; + +		/* Workaround for all block unlock in DDP */ +		if (ONENAND_IS_DDP(this)) { +			/* All blocks on another chip */ +			ofs = this->chipsize >> 1; +			len = this->chipsize >> 1; +		} +	} + +	onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); +} + + +/** + * onenand_check_features - Check and set OneNAND features + * @param mtd           MTD data structure + * + * Check and set OneNAND features + * - lock scheme + * - two plane + */ +static void onenand_check_features(struct mtd_info *mtd) +{ +	struct onenand_chip *this = mtd->priv; +	unsigned int density, process; + +	/* Lock scheme depends on density and process */ +	density = onenand_get_density(this->device_id); +	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; + +	/* Lock scheme */ +	switch (density) { +	case ONENAND_DEVICE_DENSITY_4Gb: +		this->options |= ONENAND_HAS_2PLANE; + +	case ONENAND_DEVICE_DENSITY_2Gb: +		/* 2Gb DDP don't have 2 plane */ +		if (!ONENAND_IS_DDP(this)) +			this->options |= ONENAND_HAS_2PLANE; +		this->options |= ONENAND_HAS_UNLOCK_ALL; + +	case ONENAND_DEVICE_DENSITY_1Gb: +		/* A-Die has all block unlock */ +		if (process) +			this->options |= ONENAND_HAS_UNLOCK_ALL; +		break; + +	default: +		/* Some OneNAND has continuous lock scheme */ +		if (!process) +			this->options |= ONENAND_HAS_CONT_LOCK; +		break; +	} + +	if (this->options & ONENAND_HAS_CONT_LOCK) +		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); +	if (this->options & ONENAND_HAS_UNLOCK_ALL) +		printk(KERN_DEBUG "Chip support all block unlock\n"); +	if (this->options & ONENAND_HAS_2PLANE) +		printk(KERN_DEBUG "Chip has 2 plane\n"); +} + +/**   * onenand_print_device_info - Print device ID   * @param device        device ID   *   * Print device ID   */ -char * onenand_print_device_info(int device) +char *onenand_print_device_info(int device, int version)  {  	int vcc, demuxed, ddp, density;  	char *dev_info = malloc(80); +	char *p = dev_info;  	vcc = device & ONENAND_DEVICE_VCC_MASK;  	demuxed = device & ONENAND_DEVICE_IS_DEMUX;  	ddp = device & ONENAND_DEVICE_IS_DDP;  	density = device >> ONENAND_DEVICE_DENSITY_SHIFT; -	sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)", +	p += sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",  	       demuxed ? "" : "Muxed ",  	       ddp ? "(DDP)" : "",  	       (16 << density), vcc ? "2.65/3.3" : "1.8", device); +	sprintf(p, "\nOneNAND version = 0x%04x", version); +	printk("%s\n", dev_info); +  	return dev_info;  }  static const struct onenand_manufacturers onenand_manuf_ids[] = {  	{ONENAND_MFR_SAMSUNG, "Samsung"}, -	{ONENAND_MFR_UNKNOWN, "Unknown"}  };  /** @@ -1678,19 +1867,24 @@ static const struct onenand_manufacturers onenand_manuf_ids[] = {   */  static int onenand_check_maf(int manuf)  { +	int size = ARRAY_SIZE(onenand_manuf_ids); +	char *name;  	int i; -	for (i = 0; onenand_manuf_ids[i].id; i++) { +	for (i = 0; size; i++)  		if (manuf == onenand_manuf_ids[i].id)  			break; -	} + +	if (i < size) +		name = onenand_manuf_ids[i].name; +	else +		name = "Unknown";  #ifdef ONENAND_DEBUG -	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", -	       onenand_manuf_ids[i].name, manuf); +	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);  #endif -	return (i != ONENAND_MFR_UNKNOWN); +	return i == size;  }  /** @@ -1703,9 +1897,14 @@ static int onenand_check_maf(int manuf)  static int onenand_probe(struct mtd_info *mtd)  {  	struct onenand_chip *this = mtd->priv; -	int bram_maf_id, bram_dev_id, maf_id, dev_id; -	int version_id; +	int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;  	int density; +	int syscfg; + +	/* Save system configuration 1 */ +	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); +	/* Clear Sync. Burst Read mode to read BootRAM */ +	this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);  	/* Send the command for reading device ID from BootRAM */  	this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); @@ -1714,19 +1913,23 @@ static int onenand_probe(struct mtd_info *mtd)  	bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);  	bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); -	/* Check manufacturer ID */ -	if (onenand_check_maf(bram_maf_id)) -		return -ENXIO; -  	/* Reset OneNAND to read default register values */  	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);  	/* Wait reset */  	this->wait(mtd, FL_RESETING); +	/* Restore system configuration 1 */ +	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + +	/* Check manufacturer ID */ +	if (onenand_check_maf(bram_maf_id)) +		return -ENXIO; +  	/* Read manufacturer and device IDs from Register */  	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);  	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); +	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);  	/* Check OneNAND device */  	if (maf_id != bram_maf_id || dev_id != bram_dev_id) @@ -1739,11 +1942,16 @@ static int onenand_probe(struct mtd_info *mtd)  	}  	/* Flash device information */ -	mtd->name = onenand_print_device_info(dev_id); +	mtd->name = onenand_print_device_info(dev_id, ver_id);  	this->device_id = dev_id; -	density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; +	density = onenand_get_density(dev_id);  	this->chipsize = (16 << density) << 20; +	/* Set density mask. it is used for DDP */ +	if (ONENAND_IS_DDP(this)) +		this->density_mask = (1 << (density + 6)); +	else +		this->density_mask = 0;  	/* OneNAND page size & block size */  	/* The data buffer size is equal to page size */ @@ -1764,18 +1972,8 @@ static int onenand_probe(struct mtd_info *mtd)  	mtd->size = this->chipsize; -	/* Version ID */ -	version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); -#ifdef ONENAND_DEBUG -	printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id); -#endif - -	/* Lock scheme */ -	if (density <= ONENAND_DEVICE_DENSITY_512Mb && -	    !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) { -		printk(KERN_INFO "Lock scheme is Continues Lock\n"); -		this->options |= ONENAND_CONT_LOCK; -	} +	/* Check OneNAND features */ +	onenand_check_features(mtd);  	mtd->flags = MTD_CAP_NANDFLASH;  	mtd->erase = onenand_erase; @@ -1813,12 +2011,19 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)  		this->command = onenand_command;  	if (!this->wait)  		this->wait = onenand_wait; +	if (!this->bbt_wait) +		this->bbt_wait = onenand_bbt_wait;  	if (!this->read_bufferram)  		this->read_bufferram = onenand_read_bufferram; +	if (!this->read_spareram) +		this->read_spareram = onenand_read_bufferram;  	if (!this->write_bufferram)  		this->write_bufferram = onenand_write_bufferram; +	if (!this->scan_bbt) +		this->scan_bbt = onenand_default_bbt; +  	if (onenand_probe(mtd))  		return -ENXIO; @@ -1850,9 +2055,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)  		this->options |= ONENAND_OOBBUF_ALLOC;  	} -	onenand_unlock(mtd, 0, mtd->size); +	/* Unlock whole block */ +	onenand_unlock_all(mtd); -	return onenand_default_bbt(mtd); +	return this->scan_bbt(mtd);  }  /** |