xref: /rk3399_rockchip-uboot/drivers/mtd/onenand/onenand_base.c (revision 1714f51a2009baaecf3d4f6e3bd8c4e93a8d3f23)
159829cc1SJean-Christophe PLAGNIOL-VILLARD /*
259829cc1SJean-Christophe PLAGNIOL-VILLARD  *  linux/drivers/mtd/onenand/onenand_base.c
359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
459829cc1SJean-Christophe PLAGNIOL-VILLARD  *  Copyright (C) 2005-2007 Samsung Electronics
559829cc1SJean-Christophe PLAGNIOL-VILLARD  *  Kyungmin Park <kyungmin.park@samsung.com>
659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
7bfd7f386SKyungmin Park  *  Credits:
8bfd7f386SKyungmin Park  *      Adrian Hunter <ext-adrian.hunter@nokia.com>:
9bfd7f386SKyungmin Park  *      auto-placement support, read-while load support, various fixes
10bfd7f386SKyungmin Park  *      Copyright (C) Nokia Corporation, 2007
11bfd7f386SKyungmin Park  *
1259829cc1SJean-Christophe PLAGNIOL-VILLARD  * This program is free software; you can redistribute it and/or modify
1359829cc1SJean-Christophe PLAGNIOL-VILLARD  * it under the terms of the GNU General Public License version 2 as
1459829cc1SJean-Christophe PLAGNIOL-VILLARD  * published by the Free Software Foundation.
1559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1759829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
1859829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/compat.h>
1959829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/mtd.h>
2059829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/onenand.h>
2159829cc1SJean-Christophe PLAGNIOL-VILLARD 
2259829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h>
2359829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/errno.h>
24195ccfc5SFathi BOUDRA #include <malloc.h>
2559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2677e475ccSKyungmin Park /* It should access 16-bit instead of 8-bit */
27d2c6fbecSWolfgang Denk static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
2877e475ccSKyungmin Park {
2977e475ccSKyungmin Park 	void *ret = dst;
3077e475ccSKyungmin Park 	short *d = dst;
3177e475ccSKyungmin Park 	const short *s = src;
3277e475ccSKyungmin Park 
3377e475ccSKyungmin Park 	len >>= 1;
3477e475ccSKyungmin Park 	while (len-- > 0)
3577e475ccSKyungmin Park 		*d++ = *s++;
3677e475ccSKyungmin Park 	return ret;
3777e475ccSKyungmin Park }
3877e475ccSKyungmin Park 
3959829cc1SJean-Christophe PLAGNIOL-VILLARD static const unsigned char ffchars[] = {
4059829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4159829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 16 */
4259829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4359829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 32 */
4459829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4559829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 48 */
4659829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4759829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 64 */
4859829cc1SJean-Christophe PLAGNIOL-VILLARD };
4959829cc1SJean-Christophe PLAGNIOL-VILLARD 
5059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
5159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_readw - [OneNAND Interface] Read OneNAND register
5259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to read
5359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
5459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read OneNAND register
5559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
5659829cc1SJean-Christophe PLAGNIOL-VILLARD static unsigned short onenand_readw(void __iomem * addr)
5759829cc1SJean-Christophe PLAGNIOL-VILLARD {
5859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return readw(addr);
5959829cc1SJean-Christophe PLAGNIOL-VILLARD }
6059829cc1SJean-Christophe PLAGNIOL-VILLARD 
6159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
6259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_writew - [OneNAND Interface] Write OneNAND register with value
6359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param value		value to write
6459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to write
6559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
6659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write OneNAND register with value
6759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
6859829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_writew(unsigned short value, void __iomem * addr)
6959829cc1SJean-Christophe PLAGNIOL-VILLARD {
7059829cc1SJean-Christophe PLAGNIOL-VILLARD 	writew(value, addr);
7159829cc1SJean-Christophe PLAGNIOL-VILLARD }
7259829cc1SJean-Christophe PLAGNIOL-VILLARD 
7359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
7459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_address - [DEFAULT] Get block address
7559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
7659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
7759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		translated block address if DDP, otherwise same
7859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
7959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 1 Register (F100h)
8059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
81ef0921d6SKyungmin Park static int onenand_block_address(struct onenand_chip *this, int block)
8259829cc1SJean-Christophe PLAGNIOL-VILLARD {
8359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device Flash Core select, NAND Flash Block Address */
84ef0921d6SKyungmin Park 	if (block & this->density_mask)
85ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
8659829cc1SJean-Christophe PLAGNIOL-VILLARD 
8759829cc1SJean-Christophe PLAGNIOL-VILLARD 	return block;
8859829cc1SJean-Christophe PLAGNIOL-VILLARD }
8959829cc1SJean-Christophe PLAGNIOL-VILLARD 
9059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
9159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_address - [DEFAULT] Get bufferram address
9259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
9359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
9459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		set DBS value if DDP, otherwise 0
9559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
9659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 2 Register (F101h) for DDP
9759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
98ef0921d6SKyungmin Park static int onenand_bufferram_address(struct onenand_chip *this, int block)
9959829cc1SJean-Christophe PLAGNIOL-VILLARD {
10059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device BufferRAM Select */
101ef0921d6SKyungmin Park 	if (block & this->density_mask)
102ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1;
10359829cc1SJean-Christophe PLAGNIOL-VILLARD 
104ef0921d6SKyungmin Park 	return ONENAND_DDP_CHIP0;
10559829cc1SJean-Christophe PLAGNIOL-VILLARD }
10659829cc1SJean-Christophe PLAGNIOL-VILLARD 
10759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
10859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_page_address - [DEFAULT] Get page address
10959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param page		the page address
11059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sector	the sector address
11159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		combined page and sector address
11259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
11359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 8 Register (F107h)
11459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
11559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_page_address(int page, int sector)
11659829cc1SJean-Christophe PLAGNIOL-VILLARD {
11759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash Page Address, Flash Sector Address */
11859829cc1SJean-Christophe PLAGNIOL-VILLARD 	int fpa, fsa;
11959829cc1SJean-Christophe PLAGNIOL-VILLARD 
12059829cc1SJean-Christophe PLAGNIOL-VILLARD 	fpa = page & ONENAND_FPA_MASK;
12159829cc1SJean-Christophe PLAGNIOL-VILLARD 	fsa = sector & ONENAND_FSA_MASK;
12259829cc1SJean-Christophe PLAGNIOL-VILLARD 
12359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((fpa << ONENAND_FPA_SHIFT) | fsa);
12459829cc1SJean-Christophe PLAGNIOL-VILLARD }
12559829cc1SJean-Christophe PLAGNIOL-VILLARD 
12659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
12759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_buffer_address - [DEFAULT] Get buffer address
12859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param dataram1	DataRAM index
12959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sectors	the sector address
13059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		the number of sectors
13159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		the start buffer value
13259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
13359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Buffer Register (F200h)
13459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
13559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_buffer_address(int dataram1, int sectors, int count)
13659829cc1SJean-Christophe PLAGNIOL-VILLARD {
13759829cc1SJean-Christophe PLAGNIOL-VILLARD 	int bsa, bsc;
13859829cc1SJean-Christophe PLAGNIOL-VILLARD 
13959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Address */
14059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsa = sectors & ONENAND_BSA_MASK;
14159829cc1SJean-Christophe PLAGNIOL-VILLARD 
14259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (dataram1)
14359829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM1;	/* DataRAM1 */
14459829cc1SJean-Christophe PLAGNIOL-VILLARD 	else
14559829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM0;	/* DataRAM0 */
14659829cc1SJean-Christophe PLAGNIOL-VILLARD 
14759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Count */
14859829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsc = count & ONENAND_BSC_MASK;
14959829cc1SJean-Christophe PLAGNIOL-VILLARD 
15059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((bsa << ONENAND_BSA_SHIFT) | bsc);
15159829cc1SJean-Christophe PLAGNIOL-VILLARD }
15259829cc1SJean-Christophe PLAGNIOL-VILLARD 
15359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
154ef0921d6SKyungmin Park  * onenand_get_density - [DEFAULT] Get OneNAND density
155ef0921d6SKyungmin Park  * @param dev_id        OneNAND device ID
156ef0921d6SKyungmin Park  *
157ef0921d6SKyungmin Park  * Get OneNAND density from device ID
158ef0921d6SKyungmin Park  */
159ef0921d6SKyungmin Park static inline int onenand_get_density(int dev_id)
160ef0921d6SKyungmin Park {
161ef0921d6SKyungmin Park 	int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
162ef0921d6SKyungmin Park 	return (density & ONENAND_DEVICE_DENSITY_MASK);
163ef0921d6SKyungmin Park }
164ef0921d6SKyungmin Park 
165ef0921d6SKyungmin Park /**
16659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_command - [DEFAULT] Send command to OneNAND device
16759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
16859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param cmd		the command to be sent
16959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		offset to read from or write to
17059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read or write
17159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
17259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Send command to OneNAND device. This function is used for middle/large page
17359829cc1SJean-Christophe PLAGNIOL-VILLARD  * devices (1KB/2KB Bytes per page)
17459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
17559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
17659829cc1SJean-Christophe PLAGNIOL-VILLARD 			   size_t len)
17759829cc1SJean-Christophe PLAGNIOL-VILLARD {
17859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
17959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int value, readcmd = 0;
18059829cc1SJean-Christophe PLAGNIOL-VILLARD 	int block, page;
18159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Now we use page size operation */
18259829cc1SJean-Christophe PLAGNIOL-VILLARD 	int sectors = 4, count = 4;
18359829cc1SJean-Christophe PLAGNIOL-VILLARD 
18459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Address translation */
18559829cc1SJean-Christophe PLAGNIOL-VILLARD 	switch (cmd) {
18659829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_UNLOCK:
18759829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK:
18859829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK_TIGHT:
189ef0921d6SKyungmin Park 	case ONENAND_CMD_UNLOCK_ALL:
19059829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = -1;
19159829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
19259829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
19359829cc1SJean-Christophe PLAGNIOL-VILLARD 
19459829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_ERASE:
19559829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_BUFFERRAM:
19659829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = (int)(addr >> this->erase_shift);
19759829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
19859829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
19959829cc1SJean-Christophe PLAGNIOL-VILLARD 
20059829cc1SJean-Christophe PLAGNIOL-VILLARD 	default:
20159829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = (int)(addr >> this->erase_shift);
20259829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = (int)(addr >> this->page_shift);
20359829cc1SJean-Christophe PLAGNIOL-VILLARD 		page &= this->page_mask;
20459829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
20559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
20659829cc1SJean-Christophe PLAGNIOL-VILLARD 
20759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* NOTE: The setting order of the registers is very important! */
20859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (cmd == ONENAND_CMD_BUFFERRAM) {
20959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Select DataRAM for DDP */
210ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
21159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
21259829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS2);
21359829cc1SJean-Christophe PLAGNIOL-VILLARD 
21459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Switch to the next data buffer */
21559829cc1SJean-Christophe PLAGNIOL-VILLARD 		ONENAND_SET_NEXT_BUFFERRAM(this);
21659829cc1SJean-Christophe PLAGNIOL-VILLARD 
21759829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
21859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
21959829cc1SJean-Christophe PLAGNIOL-VILLARD 
22059829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (block != -1) {
22159829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'DFS, FBA' of Flash */
222ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
22359829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
22459829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS1);
225ef0921d6SKyungmin Park 
226ef0921d6SKyungmin Park 		/* Write 'DFS, FBA' of Flash */
227ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
228ef0921d6SKyungmin Park 		this->write_word(value,
229ef0921d6SKyungmin Park 				 this->base + ONENAND_REG_START_ADDRESS2);
23059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
23159829cc1SJean-Christophe PLAGNIOL-VILLARD 
23259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (page != -1) {
23359829cc1SJean-Christophe PLAGNIOL-VILLARD 		int dataram;
23459829cc1SJean-Christophe PLAGNIOL-VILLARD 
23559829cc1SJean-Christophe PLAGNIOL-VILLARD 		switch (cmd) {
23659829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READ:
23759829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READOOB:
23859829cc1SJean-Christophe PLAGNIOL-VILLARD 			dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
23959829cc1SJean-Christophe PLAGNIOL-VILLARD 			readcmd = 1;
24059829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
24159829cc1SJean-Christophe PLAGNIOL-VILLARD 
24259829cc1SJean-Christophe PLAGNIOL-VILLARD 		default:
24359829cc1SJean-Christophe PLAGNIOL-VILLARD 			dataram = ONENAND_CURRENT_BUFFERRAM(this);
24459829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
24559829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
24659829cc1SJean-Christophe PLAGNIOL-VILLARD 
24759829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'FPA, FSA' of Flash */
24859829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_page_address(page, sectors);
24959829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
25059829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS8);
25159829cc1SJean-Christophe PLAGNIOL-VILLARD 
25259829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'BSA, BSC' of DataRAM */
25359829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_buffer_address(dataram, sectors, count);
25459829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
25559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
25659829cc1SJean-Christophe PLAGNIOL-VILLARD 
25759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Interrupt clear */
25859829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
25959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Write command */
26059829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
26159829cc1SJean-Christophe PLAGNIOL-VILLARD 
26259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
26359829cc1SJean-Christophe PLAGNIOL-VILLARD }
26459829cc1SJean-Christophe PLAGNIOL-VILLARD 
26559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
26659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_wait - [DEFAULT] wait until the command is done
26759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
26859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param state		state to select the max. timeout value
26959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
27059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Wait for command done. This applies to all OneNAND command
27159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read can take up to 30us, erase up to 2ms and program up to 350us
27259829cc1SJean-Christophe PLAGNIOL-VILLARD  * according to general OneNAND specs
27359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
27459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_wait(struct mtd_info *mtd, int state)
27559829cc1SJean-Christophe PLAGNIOL-VILLARD {
27659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
27759829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int flags = ONENAND_INT_MASTER;
27859829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int interrupt = 0;
27959829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int ctrl, ecc;
28059829cc1SJean-Christophe PLAGNIOL-VILLARD 
28159829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (1) {
28259829cc1SJean-Christophe PLAGNIOL-VILLARD 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
28359829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (interrupt & flags)
28459829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
28559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
28659829cc1SJean-Christophe PLAGNIOL-VILLARD 
28759829cc1SJean-Christophe PLAGNIOL-VILLARD 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
28859829cc1SJean-Christophe PLAGNIOL-VILLARD 
28959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ctrl & ONENAND_CTRL_ERROR) {
290ef0921d6SKyungmin Park 		printk("onenand_wait: controller error = 0x%04x\n", ctrl);
291ef0921d6SKyungmin Park 		if (ctrl & ONENAND_CTRL_LOCK)
292ef0921d6SKyungmin Park 			printk("onenand_wait: it's locked error = 0x%04x\n",
293ef0921d6SKyungmin Park 				ctrl);
29459829cc1SJean-Christophe PLAGNIOL-VILLARD 
29559829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EIO;
29659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
29759829cc1SJean-Christophe PLAGNIOL-VILLARD 
29859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (interrupt & ONENAND_INT_READ) {
29959829cc1SJean-Christophe PLAGNIOL-VILLARD 		ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
30059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ecc & ONENAND_ECC_2BIT_ALL) {
3013167c538SScott Wood 			MTDDEBUG (MTD_DEBUG_LEVEL0,
30259829cc1SJean-Christophe PLAGNIOL-VILLARD 				  "onenand_wait: ECC error = 0x%04x\n", ecc);
30359829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EBADMSG;
30459829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
30559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
30659829cc1SJean-Christophe PLAGNIOL-VILLARD 
30759829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
30859829cc1SJean-Christophe PLAGNIOL-VILLARD }
30959829cc1SJean-Christophe PLAGNIOL-VILLARD 
31059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
31159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
31259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
31359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
31459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		offset given area
31559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
31659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Return BufferRAM offset given area
31759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
31859829cc1SJean-Christophe PLAGNIOL-VILLARD static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
31959829cc1SJean-Christophe PLAGNIOL-VILLARD {
32059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
32159829cc1SJean-Christophe PLAGNIOL-VILLARD 
32259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ONENAND_CURRENT_BUFFERRAM(this)) {
32359829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_DATARAM)
324d438d508SKyungmin Park 			return mtd->writesize;
32559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_SPARERAM)
32659829cc1SJean-Christophe PLAGNIOL-VILLARD 			return mtd->oobsize;
32759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
32859829cc1SJean-Christophe PLAGNIOL-VILLARD 
32959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
33059829cc1SJean-Christophe PLAGNIOL-VILLARD }
33159829cc1SJean-Christophe PLAGNIOL-VILLARD 
33259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
33359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
33459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
33559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
33659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
33759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
33859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
33959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
34059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area
34159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
342ef0921d6SKyungmin Park static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
34359829cc1SJean-Christophe PLAGNIOL-VILLARD 				  unsigned char *buffer, int offset,
34459829cc1SJean-Christophe PLAGNIOL-VILLARD 				  size_t count)
34559829cc1SJean-Christophe PLAGNIOL-VILLARD {
34659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
34759829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
34859829cc1SJean-Christophe PLAGNIOL-VILLARD 
34959829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
35059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
35159829cc1SJean-Christophe PLAGNIOL-VILLARD 
352d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
35359829cc1SJean-Christophe PLAGNIOL-VILLARD 
35459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
35559829cc1SJean-Christophe PLAGNIOL-VILLARD }
35659829cc1SJean-Christophe PLAGNIOL-VILLARD 
35759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
35859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
35959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
36059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
36159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
36259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
36359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
36459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
36559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area with Sync. Burst Mode
36659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
367ef0921d6SKyungmin Park static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
36859829cc1SJean-Christophe PLAGNIOL-VILLARD 				       unsigned char *buffer, int offset,
36959829cc1SJean-Christophe PLAGNIOL-VILLARD 				       size_t count)
37059829cc1SJean-Christophe PLAGNIOL-VILLARD {
37159829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
37259829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
37359829cc1SJean-Christophe PLAGNIOL-VILLARD 
37459829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
37559829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
37659829cc1SJean-Christophe PLAGNIOL-VILLARD 
37759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
37859829cc1SJean-Christophe PLAGNIOL-VILLARD 
379d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
38059829cc1SJean-Christophe PLAGNIOL-VILLARD 
38159829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, 0);
38259829cc1SJean-Christophe PLAGNIOL-VILLARD 
38359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
38459829cc1SJean-Christophe PLAGNIOL-VILLARD }
38559829cc1SJean-Christophe PLAGNIOL-VILLARD 
38659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
38759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
38859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
38959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
39059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
39159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
39259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
39359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
39459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write the BufferRAM area
39559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
396ef0921d6SKyungmin Park static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area,
39759829cc1SJean-Christophe PLAGNIOL-VILLARD 				   const unsigned char *buffer, int offset,
39859829cc1SJean-Christophe PLAGNIOL-VILLARD 				   size_t count)
39959829cc1SJean-Christophe PLAGNIOL-VILLARD {
40059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
40159829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
40259829cc1SJean-Christophe PLAGNIOL-VILLARD 
40359829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
40459829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
40559829cc1SJean-Christophe PLAGNIOL-VILLARD 
406d2c6fbecSWolfgang Denk 	memcpy_16(bufferram + offset, buffer, count);
40759829cc1SJean-Christophe PLAGNIOL-VILLARD 
40859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
40959829cc1SJean-Christophe PLAGNIOL-VILLARD }
41059829cc1SJean-Christophe PLAGNIOL-VILLARD 
41159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
4124fca3310SStefan Roese  * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
4134fca3310SStefan Roese  * @param mtd		MTD data structure
4144fca3310SStefan Roese  * @param addr		address to check
4154fca3310SStefan Roese  * @return		blockpage address
4164fca3310SStefan Roese  *
4174fca3310SStefan Roese  * Get blockpage address at 2x program mode
4184fca3310SStefan Roese  */
4194fca3310SStefan Roese static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
4204fca3310SStefan Roese {
4214fca3310SStefan Roese 	struct onenand_chip *this = mtd->priv;
4224fca3310SStefan Roese 	int blockpage, block, page;
4234fca3310SStefan Roese 
4244fca3310SStefan Roese 	/* Calculate the even block number */
4254fca3310SStefan Roese 	block = (int) (addr >> this->erase_shift) & ~1;
4264fca3310SStefan Roese 	/* Is it the odd plane? */
4274fca3310SStefan Roese 	if (addr & this->writesize)
4284fca3310SStefan Roese 		block++;
4294fca3310SStefan Roese 	page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
4304fca3310SStefan Roese 	blockpage = (block << 7) | page;
4314fca3310SStefan Roese 
4324fca3310SStefan Roese 	return blockpage;
4334fca3310SStefan Roese }
4344fca3310SStefan Roese 
4354fca3310SStefan Roese /**
43659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_bufferram - [GENERIC] Check BufferRAM information
43759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
43859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to check
43959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		1 if there are valid data, otherwise 0
44059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
44159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check bufferram if there is data we required
44259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
44359829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
44459829cc1SJean-Christophe PLAGNIOL-VILLARD {
44559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
446ef0921d6SKyungmin Park 	int blockpage, found = 0;
447ef0921d6SKyungmin Park 	unsigned int i;
44859829cc1SJean-Christophe PLAGNIOL-VILLARD 
449ef0921d6SKyungmin Park #ifdef CONFIG_S3C64XX
450ef0921d6SKyungmin Park 	return 0;
451ef0921d6SKyungmin Park #endif
45259829cc1SJean-Christophe PLAGNIOL-VILLARD 
453ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
454ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
455ef0921d6SKyungmin Park 	else
456ef0921d6SKyungmin Park 		blockpage = (int) (addr >> this->page_shift);
45759829cc1SJean-Christophe PLAGNIOL-VILLARD 
45859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Is there valid data? */
459ef0921d6SKyungmin Park 	i = ONENAND_CURRENT_BUFFERRAM(this);
460ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
461ef0921d6SKyungmin Park 		found = 1;
462ef0921d6SKyungmin Park 	else {
463ef0921d6SKyungmin Park 		/* Check another BufferRAM */
464ef0921d6SKyungmin Park 		i = ONENAND_NEXT_BUFFERRAM(this);
465ef0921d6SKyungmin Park 		if (this->bufferram[i].blockpage == blockpage) {
466ef0921d6SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
467ef0921d6SKyungmin Park 			found = 1;
468ef0921d6SKyungmin Park 		}
469ef0921d6SKyungmin Park 	}
47059829cc1SJean-Christophe PLAGNIOL-VILLARD 
471ef0921d6SKyungmin Park 	if (found && ONENAND_IS_DDP(this)) {
472ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
473ef0921d6SKyungmin Park 		int block = (int) (addr >> this->erase_shift);
474ef0921d6SKyungmin Park 		int value = onenand_bufferram_address(this, block);
475ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
476ef0921d6SKyungmin Park 	}
477ef0921d6SKyungmin Park 
478ef0921d6SKyungmin Park 	return found;
47959829cc1SJean-Christophe PLAGNIOL-VILLARD }
48059829cc1SJean-Christophe PLAGNIOL-VILLARD 
48159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
48259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_update_bufferram - [GENERIC] Update BufferRAM information
48359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
48459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to update
48559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param valid		valid flag
48659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
48759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Update BufferRAM information
48859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
48959829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
49059829cc1SJean-Christophe PLAGNIOL-VILLARD 				    int valid)
49159829cc1SJean-Christophe PLAGNIOL-VILLARD {
49259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
493ef0921d6SKyungmin Park 	int blockpage;
494ef0921d6SKyungmin Park 	unsigned int i;
49559829cc1SJean-Christophe PLAGNIOL-VILLARD 
496ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
497ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
498ef0921d6SKyungmin Park 	else
499ef0921d6SKyungmin Park 		blockpage = (int)(addr >> this->page_shift);
50059829cc1SJean-Christophe PLAGNIOL-VILLARD 
501ef0921d6SKyungmin Park 	/* Invalidate another BufferRAM */
502ef0921d6SKyungmin Park 	i = ONENAND_NEXT_BUFFERRAM(this);
503ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
504ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
50559829cc1SJean-Christophe PLAGNIOL-VILLARD 
50659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Update BufferRAM */
50759829cc1SJean-Christophe PLAGNIOL-VILLARD 	i = ONENAND_CURRENT_BUFFERRAM(this);
508ef0921d6SKyungmin Park 	if (valid)
509ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = blockpage;
510ef0921d6SKyungmin Park 	else
511ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
51259829cc1SJean-Christophe PLAGNIOL-VILLARD 
51359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
51459829cc1SJean-Christophe PLAGNIOL-VILLARD }
51559829cc1SJean-Christophe PLAGNIOL-VILLARD 
51659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
517d438d508SKyungmin Park  * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
518d438d508SKyungmin Park  * @param mtd           MTD data structure
519d438d508SKyungmin Park  * @param addr          start address to invalidate
520d438d508SKyungmin Park  * @param len           length to invalidate
521d438d508SKyungmin Park  *
522d438d508SKyungmin Park  * Invalidate BufferRAM information
523d438d508SKyungmin Park  */
524d438d508SKyungmin Park static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
525d438d508SKyungmin Park 					 unsigned int len)
526d438d508SKyungmin Park {
527d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
528d438d508SKyungmin Park 	int i;
529d438d508SKyungmin Park 	loff_t end_addr = addr + len;
530d438d508SKyungmin Park 
531d438d508SKyungmin Park 	/* Invalidate BufferRAM */
532d438d508SKyungmin Park 	for (i = 0; i < MAX_BUFFERRAM; i++) {
533ef0921d6SKyungmin Park 		loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
534d438d508SKyungmin Park 
535d438d508SKyungmin Park 		if (buf_addr >= addr && buf_addr < end_addr)
536ef0921d6SKyungmin Park 			this->bufferram[i].blockpage = -1;
537d438d508SKyungmin Park 	}
538d438d508SKyungmin Park }
539d438d508SKyungmin Park 
540d438d508SKyungmin Park /**
54159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_get_device - [GENERIC] Get chip for selected access
54259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
54359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param new_state	the state which is requested
54459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
54559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Get the device and lock it for exclusive access
54659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
54759829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_get_device(struct mtd_info *mtd, int new_state)
54859829cc1SJean-Christophe PLAGNIOL-VILLARD {
54959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
55059829cc1SJean-Christophe PLAGNIOL-VILLARD }
55159829cc1SJean-Christophe PLAGNIOL-VILLARD 
55259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
55359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release_device - [GENERIC] release chip
55459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
55559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
55659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Deselect, release chip lock and wake up anyone waiting on the device
55759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
55859829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_release_device(struct mtd_info *mtd)
55959829cc1SJean-Christophe PLAGNIOL-VILLARD {
56059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
56159829cc1SJean-Christophe PLAGNIOL-VILLARD }
56259829cc1SJean-Christophe PLAGNIOL-VILLARD 
56359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
564bfd7f386SKyungmin Park  * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
56559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
566bfd7f386SKyungmin Park  * @param buf		destination address
567bfd7f386SKyungmin Park  * @param column	oob offset to read from
568bfd7f386SKyungmin Park  * @param thislen	oob length to read
56959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
570bfd7f386SKyungmin Park static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
571bfd7f386SKyungmin Park 					int column, int thislen)
57259829cc1SJean-Christophe PLAGNIOL-VILLARD {
57359829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
574bfd7f386SKyungmin Park 	struct nand_oobfree *free;
575bfd7f386SKyungmin Park 	int readcol = column;
576bfd7f386SKyungmin Park 	int readend = column + thislen;
577bfd7f386SKyungmin Park 	int lastgap = 0;
578bfd7f386SKyungmin Park 	unsigned int i;
579bfd7f386SKyungmin Park 	uint8_t *oob_buf = this->oob_buf;
58059829cc1SJean-Christophe PLAGNIOL-VILLARD 
581bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
582bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
583bfd7f386SKyungmin Park 		if (readcol >= lastgap)
584bfd7f386SKyungmin Park 			readcol += free->offset - lastgap;
585bfd7f386SKyungmin Park 		if (readend >= lastgap)
586bfd7f386SKyungmin Park 			readend += free->offset - lastgap;
587bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
588bfd7f386SKyungmin Park 	}
589ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
590bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
591bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
592bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
593bfd7f386SKyungmin Park 		if (free->offset < readend && free_end > readcol) {
594bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,readcol);
595bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,readend);
596bfd7f386SKyungmin Park 			int n = ed - st;
597bfd7f386SKyungmin Park 			memcpy(buf, oob_buf + st, n);
598bfd7f386SKyungmin Park 			buf += n;
599bfd7f386SKyungmin Park 		} else if (column == 0)
600bfd7f386SKyungmin Park 			break;
601bfd7f386SKyungmin Park 	}
602bfd7f386SKyungmin Park 	return 0;
603bfd7f386SKyungmin Park }
604bfd7f386SKyungmin Park 
605bfd7f386SKyungmin Park /**
606bfd7f386SKyungmin Park  * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
607bfd7f386SKyungmin Park  * @param mtd		MTD device structure
608bfd7f386SKyungmin Park  * @param from		offset to read from
609bfd7f386SKyungmin Park  * @param ops		oob operation description structure
610bfd7f386SKyungmin Park  *
611bfd7f386SKyungmin Park  * OneNAND read main and/or out-of-band data
612bfd7f386SKyungmin Park  */
613bfd7f386SKyungmin Park static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
614bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
615bfd7f386SKyungmin Park {
616bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
617bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
618bfd7f386SKyungmin Park 	size_t len = ops->len;
619bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
620bfd7f386SKyungmin Park 	u_char *buf = ops->datbuf;
621bfd7f386SKyungmin Park 	u_char *oobbuf = ops->oobbuf;
622bfd7f386SKyungmin Park 	int read = 0, column, thislen;
623bfd7f386SKyungmin Park 	int oobread = 0, oobcolumn, thisooblen, oobsize;
624bfd7f386SKyungmin Park 	int ret = 0, boundary = 0;
625bfd7f386SKyungmin Park 	int writesize = this->writesize;
626bfd7f386SKyungmin Park 
627ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
62859829cc1SJean-Christophe PLAGNIOL-VILLARD 
629bfd7f386SKyungmin Park 	if (ops->mode == MTD_OOB_AUTO)
630bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
631bfd7f386SKyungmin Park 	else
632bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
633bfd7f386SKyungmin Park 
634bfd7f386SKyungmin Park 	oobcolumn = from & (mtd->oobsize - 1);
635bfd7f386SKyungmin Park 
63659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
63759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((from + len) > mtd->size) {
638bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n");
639bfd7f386SKyungmin Park 		ops->retlen = 0;
640bfd7f386SKyungmin Park 		ops->oobretlen = 0;
64159829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
64259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
64359829cc1SJean-Christophe PLAGNIOL-VILLARD 
644bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
64559829cc1SJean-Christophe PLAGNIOL-VILLARD 
646bfd7f386SKyungmin Park 	/* Read-while-load method */
64759829cc1SJean-Christophe PLAGNIOL-VILLARD 
648bfd7f386SKyungmin Park 	/* Do first load to bufferRAM */
649bfd7f386SKyungmin Park 	if (read < len) {
65059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!onenand_check_bufferram(mtd, from)) {
651ef0921d6SKyungmin Park 			this->main_buf = buf;
652bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
65359829cc1SJean-Christophe PLAGNIOL-VILLARD 			ret = this->wait(mtd, FL_READING);
654bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
655bfd7f386SKyungmin Park 			if (ret == -EBADMSG)
656bfd7f386SKyungmin Park 				ret = 0;
657bfd7f386SKyungmin Park 		}
65859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
65959829cc1SJean-Christophe PLAGNIOL-VILLARD 
660bfd7f386SKyungmin Park 	thislen = min_t(int, writesize, len - read);
661bfd7f386SKyungmin Park 	column = from & (writesize - 1);
662bfd7f386SKyungmin Park 	if (column + thislen > writesize)
663bfd7f386SKyungmin Park 		thislen = writesize - column;
66459829cc1SJean-Christophe PLAGNIOL-VILLARD 
665bfd7f386SKyungmin Park 	while (!ret) {
666bfd7f386SKyungmin Park 		/* If there is more to load then start next load */
667bfd7f386SKyungmin Park 		from += thislen;
668bfd7f386SKyungmin Park 		if (read + thislen < len) {
669ef0921d6SKyungmin Park 			this->main_buf = buf + thislen;
670bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
671bfd7f386SKyungmin Park 			/*
672bfd7f386SKyungmin Park 			 * Chip boundary handling in DDP
673bfd7f386SKyungmin Park 			 * Now we issued chip 1 read and pointed chip 1
674bfd7f386SKyungmin Park 			 * bufferam so we have to point chip 0 bufferam.
675bfd7f386SKyungmin Park 			 */
676bfd7f386SKyungmin Park 			if (ONENAND_IS_DDP(this) &&
677bfd7f386SKyungmin Park 					unlikely(from == (this->chipsize >> 1))) {
678bfd7f386SKyungmin Park 				this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
679bfd7f386SKyungmin Park 				boundary = 1;
680bfd7f386SKyungmin Park 			} else
681bfd7f386SKyungmin Park 				boundary = 0;
682bfd7f386SKyungmin Park 			ONENAND_SET_PREV_BUFFERRAM(this);
683bfd7f386SKyungmin Park 		}
684bfd7f386SKyungmin Park 
685bfd7f386SKyungmin Park 		/* While load is going, read from last bufferRAM */
686ef0921d6SKyungmin Park 		this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen);
687bfd7f386SKyungmin Park 
688bfd7f386SKyungmin Park 		/* Read oob area if needed */
689bfd7f386SKyungmin Park 		if (oobbuf) {
690bfd7f386SKyungmin Park 			thisooblen = oobsize - oobcolumn;
691bfd7f386SKyungmin Park 			thisooblen = min_t(int, thisooblen, ooblen - oobread);
692bfd7f386SKyungmin Park 
693bfd7f386SKyungmin Park 			if (ops->mode == MTD_OOB_AUTO)
694bfd7f386SKyungmin Park 				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
695bfd7f386SKyungmin Park 			else
696ef0921d6SKyungmin Park 				this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
697bfd7f386SKyungmin Park 			oobread += thisooblen;
698bfd7f386SKyungmin Park 			oobbuf += thisooblen;
699bfd7f386SKyungmin Park 			oobcolumn = 0;
700bfd7f386SKyungmin Park 		}
701bfd7f386SKyungmin Park 
702bfd7f386SKyungmin Park 		/* See if we are done */
70359829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
70459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
70559829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
706bfd7f386SKyungmin Park 		/* Set up for next read from bufferRAM */
707bfd7f386SKyungmin Park 		if (unlikely(boundary))
708bfd7f386SKyungmin Park 			this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
709bfd7f386SKyungmin Park 		ONENAND_SET_NEXT_BUFFERRAM(this);
71059829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
711bfd7f386SKyungmin Park 		thislen = min_t(int, writesize, len - read);
712bfd7f386SKyungmin Park 		column = 0;
71359829cc1SJean-Christophe PLAGNIOL-VILLARD 
714bfd7f386SKyungmin Park 		/* Now wait for load */
715bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_READING);
716bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, from, !ret);
717bfd7f386SKyungmin Park 		if (ret == -EBADMSG)
718bfd7f386SKyungmin Park 			ret = 0;
719bfd7f386SKyungmin Park 	}
72059829cc1SJean-Christophe PLAGNIOL-VILLARD 
72159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/*
72259829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * Return success, if no ECC failures, else -EBADMSG
72359829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * fs driver will take care of that, because
72459829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * retlen == desired len and result == -EBADMSG
72559829cc1SJean-Christophe PLAGNIOL-VILLARD 	 */
726bfd7f386SKyungmin Park 	ops->retlen = read;
727bfd7f386SKyungmin Park 	ops->oobretlen = oobread;
728bfd7f386SKyungmin Park 
729bfd7f386SKyungmin Park 	if (ret)
73059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return ret;
731bfd7f386SKyungmin Park 
732bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
733bfd7f386SKyungmin Park 		return -EBADMSG;
734bfd7f386SKyungmin Park 
735bfd7f386SKyungmin Park 	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
736bfd7f386SKyungmin Park }
737bfd7f386SKyungmin Park 
738bfd7f386SKyungmin Park /**
739bfd7f386SKyungmin Park  * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
740bfd7f386SKyungmin Park  * @param mtd		MTD device structure
741bfd7f386SKyungmin Park  * @param from		offset to read from
742bfd7f386SKyungmin Park  * @param ops		oob operation description structure
743bfd7f386SKyungmin Park  *
744bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area
745bfd7f386SKyungmin Park  */
746bfd7f386SKyungmin Park static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
747bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
748bfd7f386SKyungmin Park {
749bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
750bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
751bfd7f386SKyungmin Park 	int read = 0, thislen, column, oobsize;
752bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
753bfd7f386SKyungmin Park 	mtd_oob_mode_t mode = ops->mode;
754bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
755bfd7f386SKyungmin Park 	int ret = 0;
756bfd7f386SKyungmin Park 
757bfd7f386SKyungmin Park 	from += ops->ooboffs;
758bfd7f386SKyungmin Park 
759ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
760bfd7f386SKyungmin Park 
761bfd7f386SKyungmin Park 	/* Initialize return length value */
762bfd7f386SKyungmin Park 	ops->oobretlen = 0;
763bfd7f386SKyungmin Park 
764bfd7f386SKyungmin Park 	if (mode == MTD_OOB_AUTO)
765bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
766bfd7f386SKyungmin Park 	else
767bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
768bfd7f386SKyungmin Park 
769bfd7f386SKyungmin Park 	column = from & (mtd->oobsize - 1);
770bfd7f386SKyungmin Park 
771bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
772bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
773bfd7f386SKyungmin Park 		return -EINVAL;
774bfd7f386SKyungmin Park 	}
775bfd7f386SKyungmin Park 
776bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
777bfd7f386SKyungmin Park 	if (unlikely(from >= mtd->size ||
778bfd7f386SKyungmin Park 		column + len > ((mtd->size >> this->page_shift) -
779bfd7f386SKyungmin Park 				(from >> this->page_shift)) * oobsize)) {
780bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n");
781bfd7f386SKyungmin Park 		return -EINVAL;
782bfd7f386SKyungmin Park 	}
783bfd7f386SKyungmin Park 
784bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
785bfd7f386SKyungmin Park 
786bfd7f386SKyungmin Park 	while (read < len) {
787bfd7f386SKyungmin Park 		thislen = oobsize - column;
788bfd7f386SKyungmin Park 		thislen = min_t(int, thislen, len);
789bfd7f386SKyungmin Park 
790ef0921d6SKyungmin Park 		this->spare_buf = buf;
791bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
792bfd7f386SKyungmin Park 
793bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, from, 0);
794bfd7f386SKyungmin Park 
795bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_READING);
796bfd7f386SKyungmin Park 		if (ret && ret != -EBADMSG) {
797bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
798bfd7f386SKyungmin Park 			break;
799bfd7f386SKyungmin Park 		}
800bfd7f386SKyungmin Park 
801bfd7f386SKyungmin Park 		if (mode == MTD_OOB_AUTO)
802bfd7f386SKyungmin Park 			onenand_transfer_auto_oob(mtd, buf, column, thislen);
803bfd7f386SKyungmin Park 		else
804ef0921d6SKyungmin Park 			this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
805bfd7f386SKyungmin Park 
806bfd7f386SKyungmin Park 		read += thislen;
807bfd7f386SKyungmin Park 
808bfd7f386SKyungmin Park 		if (read == len)
809bfd7f386SKyungmin Park 			break;
810bfd7f386SKyungmin Park 
811bfd7f386SKyungmin Park 		buf += thislen;
812bfd7f386SKyungmin Park 
813bfd7f386SKyungmin Park 		/* Read more? */
814bfd7f386SKyungmin Park 		if (read < len) {
815bfd7f386SKyungmin Park 			/* Page size */
816bfd7f386SKyungmin Park 			from += mtd->writesize;
817bfd7f386SKyungmin Park 			column = 0;
818bfd7f386SKyungmin Park 		}
819bfd7f386SKyungmin Park 	}
820bfd7f386SKyungmin Park 
821bfd7f386SKyungmin Park 	ops->oobretlen = read;
822bfd7f386SKyungmin Park 
823bfd7f386SKyungmin Park 	if (ret)
824bfd7f386SKyungmin Park 		return ret;
825bfd7f386SKyungmin Park 
826bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
827bfd7f386SKyungmin Park 		return -EBADMSG;
828bfd7f386SKyungmin Park 
829bfd7f386SKyungmin Park 	return 0;
83059829cc1SJean-Christophe PLAGNIOL-VILLARD }
83159829cc1SJean-Christophe PLAGNIOL-VILLARD 
83259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
83359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc
83459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
83559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
83659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read
83759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of read bytes
83859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the databuffer to put data
83959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
84059829cc1SJean-Christophe PLAGNIOL-VILLARD  * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
84159829cc1SJean-Christophe PLAGNIOL-VILLARD */
84259829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
84359829cc1SJean-Christophe PLAGNIOL-VILLARD 		 size_t * retlen, u_char * buf)
84459829cc1SJean-Christophe PLAGNIOL-VILLARD {
845bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
846bfd7f386SKyungmin Park 		.len    = len,
847bfd7f386SKyungmin Park 		.ooblen = 0,
848bfd7f386SKyungmin Park 		.datbuf = buf,
849bfd7f386SKyungmin Park 		.oobbuf = NULL,
850bfd7f386SKyungmin Park 	};
851bfd7f386SKyungmin Park 	int ret;
852bfd7f386SKyungmin Park 
853bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
854bfd7f386SKyungmin Park 	ret = onenand_read_ops_nolock(mtd, from, &ops);
855bfd7f386SKyungmin Park 	onenand_release_device(mtd);
856bfd7f386SKyungmin Park 
857bfd7f386SKyungmin Park 	*retlen = ops.retlen;
858bfd7f386SKyungmin Park 	return ret;
85959829cc1SJean-Christophe PLAGNIOL-VILLARD }
86059829cc1SJean-Christophe PLAGNIOL-VILLARD 
86159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
86259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
86359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
86459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
865bfd7f386SKyungmin Park  * @param ops		oob operations description structure
86659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
867bfd7f386SKyungmin Park  * OneNAND main and/or out-of-band
86859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
869bfd7f386SKyungmin Park int onenand_read_oob(struct mtd_info *mtd, loff_t from,
870bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
871bfd7f386SKyungmin Park {
872bfd7f386SKyungmin Park 	int ret;
873bfd7f386SKyungmin Park 
874bfd7f386SKyungmin Park 	switch (ops->mode) {
875bfd7f386SKyungmin Park 	case MTD_OOB_PLACE:
876bfd7f386SKyungmin Park 	case MTD_OOB_AUTO:
877bfd7f386SKyungmin Park 		break;
878bfd7f386SKyungmin Park 	case MTD_OOB_RAW:
879bfd7f386SKyungmin Park 		/* Not implemented yet */
880bfd7f386SKyungmin Park 	default:
881bfd7f386SKyungmin Park 		return -EINVAL;
882bfd7f386SKyungmin Park 	}
883bfd7f386SKyungmin Park 
884bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
885bfd7f386SKyungmin Park 	if (ops->datbuf)
886bfd7f386SKyungmin Park 		ret = onenand_read_ops_nolock(mtd, from, ops);
887bfd7f386SKyungmin Park 	else
888bfd7f386SKyungmin Park 		ret = onenand_read_oob_nolock(mtd, from, ops);
889bfd7f386SKyungmin Park 	onenand_release_device(mtd);
890bfd7f386SKyungmin Park 
891bfd7f386SKyungmin Park 	return ret;
892bfd7f386SKyungmin Park }
893bfd7f386SKyungmin Park 
894bfd7f386SKyungmin Park /**
895bfd7f386SKyungmin Park  * onenand_bbt_wait - [DEFAULT] wait until the command is done
896bfd7f386SKyungmin Park  * @param mtd		MTD device structure
897bfd7f386SKyungmin Park  * @param state		state to select the max. timeout value
898bfd7f386SKyungmin Park  *
899bfd7f386SKyungmin Park  * Wait for command done.
900bfd7f386SKyungmin Park  */
901bfd7f386SKyungmin Park static int onenand_bbt_wait(struct mtd_info *mtd, int state)
902bfd7f386SKyungmin Park {
903bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
904bfd7f386SKyungmin Park 	unsigned int flags = ONENAND_INT_MASTER;
905bfd7f386SKyungmin Park 	unsigned int interrupt;
906bfd7f386SKyungmin Park 	unsigned int ctrl;
907bfd7f386SKyungmin Park 
908bfd7f386SKyungmin Park 	while (1) {
909bfd7f386SKyungmin Park 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
910bfd7f386SKyungmin Park 		if (interrupt & flags)
911bfd7f386SKyungmin Park 			break;
912bfd7f386SKyungmin Park 	}
913bfd7f386SKyungmin Park 
914bfd7f386SKyungmin Park 	/* To get correct interrupt status in timeout case */
915bfd7f386SKyungmin Park 	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
916bfd7f386SKyungmin Park 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
917bfd7f386SKyungmin Park 
918bfd7f386SKyungmin Park 	if (interrupt & ONENAND_INT_READ) {
919bfd7f386SKyungmin Park 		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
920bfd7f386SKyungmin Park 		if (ecc & ONENAND_ECC_2BIT_ALL)
921bfd7f386SKyungmin Park 			return ONENAND_BBT_READ_ERROR;
922bfd7f386SKyungmin Park 	} else {
923bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_wait: read timeout!"
924bfd7f386SKyungmin Park 				"ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
925bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
926bfd7f386SKyungmin Park 	}
927bfd7f386SKyungmin Park 
928ef0921d6SKyungmin Park 	/* Initial bad block case: 0x2400 or 0x0400 */
929ef0921d6SKyungmin Park 	if (ctrl & ONENAND_CTRL_ERROR) {
930ef0921d6SKyungmin Park 		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
931ef0921d6SKyungmin Park 		return ONENAND_BBT_READ_ERROR;
932ef0921d6SKyungmin Park 	}
933ef0921d6SKyungmin Park 
934bfd7f386SKyungmin Park 	return 0;
935bfd7f386SKyungmin Park }
936bfd7f386SKyungmin Park 
937bfd7f386SKyungmin Park /**
938bfd7f386SKyungmin Park  * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
939bfd7f386SKyungmin Park  * @param mtd		MTD device structure
940bfd7f386SKyungmin Park  * @param from		offset to read from
941bfd7f386SKyungmin Park  * @param ops		oob operation description structure
942bfd7f386SKyungmin Park  *
943bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area for bbt scan
944bfd7f386SKyungmin Park  */
945bfd7f386SKyungmin Park int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
946bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
94759829cc1SJean-Christophe PLAGNIOL-VILLARD {
94859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
94959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int read = 0, thislen, column;
95059829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
951bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
952bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
95359829cc1SJean-Christophe PLAGNIOL-VILLARD 
954ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
95559829cc1SJean-Christophe PLAGNIOL-VILLARD 
956bfd7f386SKyungmin Park 	/* Initialize return value */
957bfd7f386SKyungmin Park 	ops->oobretlen = 0;
95859829cc1SJean-Christophe PLAGNIOL-VILLARD 
95959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
96059829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((from + len) > mtd->size)) {
961bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
962bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
96359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
96459829cc1SJean-Christophe PLAGNIOL-VILLARD 
96559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
96659829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_READING);
96759829cc1SJean-Christophe PLAGNIOL-VILLARD 
96859829cc1SJean-Christophe PLAGNIOL-VILLARD 	column = from & (mtd->oobsize - 1);
96959829cc1SJean-Christophe PLAGNIOL-VILLARD 
97059829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (read < len) {
971bfd7f386SKyungmin Park 
97259829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = mtd->oobsize - column;
97359829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = min_t(int, thislen, len);
97459829cc1SJean-Christophe PLAGNIOL-VILLARD 
975ef0921d6SKyungmin Park 		this->spare_buf = buf;
97659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
97759829cc1SJean-Christophe PLAGNIOL-VILLARD 
97859829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, from, 0);
97959829cc1SJean-Christophe PLAGNIOL-VILLARD 
980ef0921d6SKyungmin Park 		ret = this->bbt_wait(mtd, FL_READING);
981bfd7f386SKyungmin Park 		if (ret)
982bfd7f386SKyungmin Park 			break;
98359829cc1SJean-Christophe PLAGNIOL-VILLARD 
984ef0921d6SKyungmin Park 		this->read_spareram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
98559829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
98659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
98759829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
98859829cc1SJean-Christophe PLAGNIOL-VILLARD 
98959829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
990bfd7f386SKyungmin Park 
99159829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Read more? */
99259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read < len) {
993bfd7f386SKyungmin Park 			/* Update Page size */
994bfd7f386SKyungmin Park 			from += this->writesize;
99559829cc1SJean-Christophe PLAGNIOL-VILLARD 			column = 0;
99659829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
99759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
99859829cc1SJean-Christophe PLAGNIOL-VILLARD 
99959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
100059829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
100159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1002bfd7f386SKyungmin Park 	ops->oobretlen = read;
100359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
100459829cc1SJean-Christophe PLAGNIOL-VILLARD }
100559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1006bfd7f386SKyungmin Park 
100759829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
100859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1009bfd7f386SKyungmin Park  * onenand_verify_oob - [GENERIC] verify the oob contents after a write
101059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
101159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf           the databuffer to verify
1012bfd7f386SKyungmin Park  * @param to            offset to read from
101359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1014bfd7f386SKyungmin Park static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
101559829cc1SJean-Christophe PLAGNIOL-VILLARD {
101659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1017bfd7f386SKyungmin Park 	u_char *oob_buf = this->oob_buf;
1018bfd7f386SKyungmin Park 	int status, i;
101959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1020bfd7f386SKyungmin Park 	this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
1021bfd7f386SKyungmin Park 	onenand_update_bufferram(mtd, to, 0);
1022bfd7f386SKyungmin Park 	status = this->wait(mtd, FL_READING);
1023bfd7f386SKyungmin Park 	if (status)
1024bfd7f386SKyungmin Park 		return status;
1025bfd7f386SKyungmin Park 
1026ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
1027bfd7f386SKyungmin Park 	for (i = 0; i < mtd->oobsize; i++)
1028bfd7f386SKyungmin Park 		if (buf[i] != 0xFF && buf[i] != oob_buf[i])
1029bfd7f386SKyungmin Park 			return -EBADMSG;
1030bfd7f386SKyungmin Park 
1031bfd7f386SKyungmin Park 	return 0;
1032bfd7f386SKyungmin Park }
1033bfd7f386SKyungmin Park 
1034bfd7f386SKyungmin Park /**
1035bfd7f386SKyungmin Park  * onenand_verify - [GENERIC] verify the chip contents after a write
1036bfd7f386SKyungmin Park  * @param mtd          MTD device structure
1037bfd7f386SKyungmin Park  * @param buf          the databuffer to verify
1038bfd7f386SKyungmin Park  * @param addr         offset to read from
1039bfd7f386SKyungmin Park  * @param len          number of bytes to read and compare
1040bfd7f386SKyungmin Park  */
1041bfd7f386SKyungmin Park static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
1042bfd7f386SKyungmin Park {
1043bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1044bfd7f386SKyungmin Park 	void __iomem *dataram;
1045bfd7f386SKyungmin Park 	int ret = 0;
1046bfd7f386SKyungmin Park 	int thislen, column;
1047bfd7f386SKyungmin Park 
1048bfd7f386SKyungmin Park 	while (len != 0) {
1049bfd7f386SKyungmin Park 		thislen = min_t(int, this->writesize, len);
1050bfd7f386SKyungmin Park 		column = addr & (this->writesize - 1);
1051bfd7f386SKyungmin Park 		if (column + thislen > this->writesize)
1052bfd7f386SKyungmin Park 			thislen = this->writesize - column;
1053bfd7f386SKyungmin Park 
1054bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
1055bfd7f386SKyungmin Park 
1056bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, addr, 0);
105759829cc1SJean-Christophe PLAGNIOL-VILLARD 
105859829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_READING);
105959829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret)
106059829cc1SJean-Christophe PLAGNIOL-VILLARD 			return ret;
106159829cc1SJean-Christophe PLAGNIOL-VILLARD 
106259829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, addr, 1);
106359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1064bfd7f386SKyungmin Park 		dataram = this->base + ONENAND_DATARAM;
1065bfd7f386SKyungmin Park 		dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
106659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1067bfd7f386SKyungmin Park 		if (memcmp(buf, dataram + column, thislen))
106859829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EBADMSG;
106959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1070bfd7f386SKyungmin Park 		len -= thislen;
1071bfd7f386SKyungmin Park 		buf += thislen;
1072bfd7f386SKyungmin Park 		addr += thislen;
1073bfd7f386SKyungmin Park 	}
1074bfd7f386SKyungmin Park 
107559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
107659829cc1SJean-Christophe PLAGNIOL-VILLARD }
107759829cc1SJean-Christophe PLAGNIOL-VILLARD #else
1078bfd7f386SKyungmin Park #define onenand_verify(...)             (0)
1079bfd7f386SKyungmin Park #define onenand_verify_oob(...)         (0)
108059829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
108159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1082d438d508SKyungmin Park #define NOTALIGNED(x)	((x & (mtd->writesize - 1)) != 0)
108359829cc1SJean-Christophe PLAGNIOL-VILLARD 
108459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1085bfd7f386SKyungmin Park  * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
108659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
1087bfd7f386SKyungmin Park  * @param oob_buf       oob buffer
1088bfd7f386SKyungmin Park  * @param buf           source address
1089bfd7f386SKyungmin Park  * @param column        oob offset to write to
1090bfd7f386SKyungmin Park  * @param thislen       oob length to write
109159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1092bfd7f386SKyungmin Park static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
1093bfd7f386SKyungmin Park 		const u_char *buf, int column, int thislen)
109459829cc1SJean-Christophe PLAGNIOL-VILLARD {
109559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1096bfd7f386SKyungmin Park 	struct nand_oobfree *free;
1097bfd7f386SKyungmin Park 	int writecol = column;
1098bfd7f386SKyungmin Park 	int writeend = column + thislen;
1099bfd7f386SKyungmin Park 	int lastgap = 0;
1100bfd7f386SKyungmin Park 	unsigned int i;
1101bfd7f386SKyungmin Park 
1102bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1103bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1104bfd7f386SKyungmin Park 		if (writecol >= lastgap)
1105bfd7f386SKyungmin Park 			writecol += free->offset - lastgap;
1106bfd7f386SKyungmin Park 		if (writeend >= lastgap)
1107bfd7f386SKyungmin Park 			writeend += free->offset - lastgap;
1108bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
1109bfd7f386SKyungmin Park 	}
1110bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1111bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1112bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
1113bfd7f386SKyungmin Park 		if (free->offset < writeend && free_end > writecol) {
1114bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,writecol);
1115bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,writeend);
1116bfd7f386SKyungmin Park 			int n = ed - st;
1117bfd7f386SKyungmin Park 			memcpy(oob_buf + st, buf, n);
1118bfd7f386SKyungmin Park 			buf += n;
1119bfd7f386SKyungmin Park 		} else if (column == 0)
1120bfd7f386SKyungmin Park 			break;
1121bfd7f386SKyungmin Park 	}
1122bfd7f386SKyungmin Park 	return 0;
1123bfd7f386SKyungmin Park }
1124bfd7f386SKyungmin Park 
1125bfd7f386SKyungmin Park /**
1126bfd7f386SKyungmin Park  * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
1127bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1128bfd7f386SKyungmin Park  * @param to            offset to write to
1129bfd7f386SKyungmin Park  * @param ops           oob operation description structure
1130bfd7f386SKyungmin Park  *
1131bfd7f386SKyungmin Park  * Write main and/or oob with ECC
1132bfd7f386SKyungmin Park  */
1133bfd7f386SKyungmin Park static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
1134bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1135bfd7f386SKyungmin Park {
1136bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1137bfd7f386SKyungmin Park 	int written = 0, column, thislen, subpage;
1138bfd7f386SKyungmin Park 	int oobwritten = 0, oobcolumn, thisooblen, oobsize;
1139bfd7f386SKyungmin Park 	size_t len = ops->len;
1140bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
1141bfd7f386SKyungmin Park 	const u_char *buf = ops->datbuf;
1142bfd7f386SKyungmin Park 	const u_char *oob = ops->oobbuf;
1143bfd7f386SKyungmin Park 	u_char *oobbuf;
114459829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
114559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1146ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
114759829cc1SJean-Christophe PLAGNIOL-VILLARD 
114859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Initialize retlen, in case of early exit */
1149bfd7f386SKyungmin Park 	ops->retlen = 0;
1150bfd7f386SKyungmin Park 	ops->oobretlen = 0;
115159829cc1SJean-Christophe PLAGNIOL-VILLARD 
115259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow writes past end of device */
115359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((to + len) > mtd->size)) {
1154bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
115559829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
115659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
115759829cc1SJean-Christophe PLAGNIOL-VILLARD 
115859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reject writes, which are not page aligned */
1159bfd7f386SKyungmin Park 	if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
1160bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
116159829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
116259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
116359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1164bfd7f386SKyungmin Park 	if (ops->mode == MTD_OOB_AUTO)
1165bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1166bfd7f386SKyungmin Park 	else
1167bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1168bfd7f386SKyungmin Park 
1169bfd7f386SKyungmin Park 	oobcolumn = to & (mtd->oobsize - 1);
1170bfd7f386SKyungmin Park 
1171bfd7f386SKyungmin Park 	column = to & (mtd->writesize - 1);
117259829cc1SJean-Christophe PLAGNIOL-VILLARD 
117359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop until all data write */
117459829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (written < len) {
1175bfd7f386SKyungmin Park 		u_char *wbuf = (u_char *) buf;
117659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1177bfd7f386SKyungmin Park 		thislen = min_t(int, mtd->writesize - column, len - written);
1178bfd7f386SKyungmin Park 		thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
117959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1180bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
1181bfd7f386SKyungmin Park 
1182bfd7f386SKyungmin Park 		/* Partial page write */
1183bfd7f386SKyungmin Park 		subpage = thislen < mtd->writesize;
1184bfd7f386SKyungmin Park 		if (subpage) {
1185bfd7f386SKyungmin Park 			memset(this->page_buf, 0xff, mtd->writesize);
1186bfd7f386SKyungmin Park 			memcpy(this->page_buf + column, buf, thislen);
1187bfd7f386SKyungmin Park 			wbuf = this->page_buf;
1188bfd7f386SKyungmin Park 		}
1189bfd7f386SKyungmin Park 
1190ef0921d6SKyungmin Park 		this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
1191bfd7f386SKyungmin Park 
1192bfd7f386SKyungmin Park 		if (oob) {
1193bfd7f386SKyungmin Park 			oobbuf = this->oob_buf;
1194bfd7f386SKyungmin Park 
1195bfd7f386SKyungmin Park 			/* We send data to spare ram with oobsize
1196bfd7f386SKyungmin Park 			 *                          * to prevent byte access */
1197bfd7f386SKyungmin Park 			memset(oobbuf, 0xff, mtd->oobsize);
1198bfd7f386SKyungmin Park 			if (ops->mode == MTD_OOB_AUTO)
1199bfd7f386SKyungmin Park 				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
1200bfd7f386SKyungmin Park 			else
1201bfd7f386SKyungmin Park 				memcpy(oobbuf + oobcolumn, oob, thisooblen);
1202bfd7f386SKyungmin Park 
1203bfd7f386SKyungmin Park 			oobwritten += thisooblen;
1204bfd7f386SKyungmin Park 			oob += thisooblen;
1205bfd7f386SKyungmin Park 			oobcolumn = 0;
1206bfd7f386SKyungmin Park 		} else
1207bfd7f386SKyungmin Park 			oobbuf = (u_char *) ffchars;
1208bfd7f386SKyungmin Park 
1209ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
121059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1211d438d508SKyungmin Park 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
121259829cc1SJean-Christophe PLAGNIOL-VILLARD 
121359829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_WRITING);
1214bfd7f386SKyungmin Park 
1215bfd7f386SKyungmin Park 		/* In partial page write we don't update bufferram */
1216bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, !ret && !subpage);
1217bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1218bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1219bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
1220bfd7f386SKyungmin Park 		}
1221bfd7f386SKyungmin Park 
122259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
1223bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
1224bfd7f386SKyungmin Park 			break;
1225bfd7f386SKyungmin Park 		}
1226bfd7f386SKyungmin Park 
1227bfd7f386SKyungmin Park 		/* Only check verify write turn on */
1228bfd7f386SKyungmin Park 		ret = onenand_verify(mtd, buf, to, thislen);
1229bfd7f386SKyungmin Park 		if (ret) {
1230bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
123159829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
123259829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
123359829cc1SJean-Christophe PLAGNIOL-VILLARD 
123459829cc1SJean-Christophe PLAGNIOL-VILLARD 		written += thislen;
123559829cc1SJean-Christophe PLAGNIOL-VILLARD 
123659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (written == len)
123759829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
123859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1239bfd7f386SKyungmin Park 		column = 0;
124059829cc1SJean-Christophe PLAGNIOL-VILLARD 		to += thislen;
124159829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
124259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
124359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1244bfd7f386SKyungmin Park 	ops->retlen = written;
124559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1246bfd7f386SKyungmin Park 	return ret;
1247bfd7f386SKyungmin Park }
1248bfd7f386SKyungmin Park 
1249bfd7f386SKyungmin Park /**
1250bfd7f386SKyungmin Park  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
1251bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1252bfd7f386SKyungmin Park  * @param to            offset to write to
1253bfd7f386SKyungmin Park  * @param len           number of bytes to write
1254bfd7f386SKyungmin Park  * @param retlen        pointer to variable to store the number of written bytes
1255bfd7f386SKyungmin Park  * @param buf           the data to write
1256bfd7f386SKyungmin Park  * @param mode          operation mode
1257bfd7f386SKyungmin Park  *
1258bfd7f386SKyungmin Park  * OneNAND write out-of-band
1259bfd7f386SKyungmin Park  */
1260bfd7f386SKyungmin Park static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
1261bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1262bfd7f386SKyungmin Park {
1263bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1264bfd7f386SKyungmin Park 	int column, ret = 0, oobsize;
1265bfd7f386SKyungmin Park 	int written = 0;
1266bfd7f386SKyungmin Park 	u_char *oobbuf;
1267bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1268bfd7f386SKyungmin Park 	const u_char *buf = ops->oobbuf;
1269bfd7f386SKyungmin Park 	mtd_oob_mode_t mode = ops->mode;
1270bfd7f386SKyungmin Park 
1271bfd7f386SKyungmin Park 	to += ops->ooboffs;
1272bfd7f386SKyungmin Park 
1273ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
1274bfd7f386SKyungmin Park 
1275bfd7f386SKyungmin Park 	/* Initialize retlen, in case of early exit */
1276bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1277bfd7f386SKyungmin Park 
1278bfd7f386SKyungmin Park 	if (mode == MTD_OOB_AUTO)
1279bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1280bfd7f386SKyungmin Park 	else
1281bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1282bfd7f386SKyungmin Park 
1283bfd7f386SKyungmin Park 	column = to & (mtd->oobsize - 1);
1284bfd7f386SKyungmin Park 
1285bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1286bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");
1287bfd7f386SKyungmin Park 		return -EINVAL;
1288bfd7f386SKyungmin Park 	}
1289bfd7f386SKyungmin Park 
1290bfd7f386SKyungmin Park 	/* For compatibility with NAND: Do not allow write past end of page */
1291bfd7f386SKyungmin Park 	if (unlikely(column + len > oobsize)) {
1292bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: "
1293bfd7f386SKyungmin Park 				"Attempt to write past end of page\n");
1294bfd7f386SKyungmin Park 		return -EINVAL;
1295bfd7f386SKyungmin Park 	}
1296bfd7f386SKyungmin Park 
1297bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1298bfd7f386SKyungmin Park 	if (unlikely(to >= mtd->size ||
1299bfd7f386SKyungmin Park 				column + len > ((mtd->size >> this->page_shift) -
1300bfd7f386SKyungmin Park 					(to >> this->page_shift)) * oobsize)) {
1301bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n");
1302bfd7f386SKyungmin Park 		return -EINVAL;
1303bfd7f386SKyungmin Park 	}
1304bfd7f386SKyungmin Park 
1305bfd7f386SKyungmin Park 	oobbuf = this->oob_buf;
1306bfd7f386SKyungmin Park 
1307bfd7f386SKyungmin Park 	/* Loop until all data write */
1308bfd7f386SKyungmin Park 	while (written < len) {
1309bfd7f386SKyungmin Park 		int thislen = min_t(int, oobsize, len - written);
1310bfd7f386SKyungmin Park 
1311bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
1312bfd7f386SKyungmin Park 
1313bfd7f386SKyungmin Park 		/* We send data to spare ram with oobsize
1314bfd7f386SKyungmin Park 		 * to prevent byte access */
1315bfd7f386SKyungmin Park 		memset(oobbuf, 0xff, mtd->oobsize);
1316bfd7f386SKyungmin Park 		if (mode == MTD_OOB_AUTO)
1317bfd7f386SKyungmin Park 			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
1318bfd7f386SKyungmin Park 		else
1319bfd7f386SKyungmin Park 			memcpy(oobbuf + column, buf, thislen);
1320ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
1321bfd7f386SKyungmin Park 
1322bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
1323bfd7f386SKyungmin Park 
1324bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, 0);
1325bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1326bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1327bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, 0);
1328bfd7f386SKyungmin Park 		}
1329bfd7f386SKyungmin Park 
1330bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_WRITING);
1331bfd7f386SKyungmin Park 		if (ret) {
1332bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret);
1333bfd7f386SKyungmin Park 			break;
1334bfd7f386SKyungmin Park 		}
1335bfd7f386SKyungmin Park 
1336bfd7f386SKyungmin Park 		ret = onenand_verify_oob(mtd, oobbuf, to);
1337bfd7f386SKyungmin Park 		if (ret) {
1338bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret);
1339bfd7f386SKyungmin Park 			break;
1340bfd7f386SKyungmin Park 		}
1341bfd7f386SKyungmin Park 
1342bfd7f386SKyungmin Park 		written += thislen;
1343bfd7f386SKyungmin Park 		if (written == len)
1344bfd7f386SKyungmin Park 			break;
1345bfd7f386SKyungmin Park 
1346bfd7f386SKyungmin Park 		to += mtd->writesize;
1347bfd7f386SKyungmin Park 		buf += thislen;
1348bfd7f386SKyungmin Park 		column = 0;
1349bfd7f386SKyungmin Park 	}
1350bfd7f386SKyungmin Park 
1351bfd7f386SKyungmin Park 	ops->oobretlen = written;
135259829cc1SJean-Christophe PLAGNIOL-VILLARD 
135359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
135459829cc1SJean-Christophe PLAGNIOL-VILLARD }
135559829cc1SJean-Christophe PLAGNIOL-VILLARD 
135659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
135759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write - [MTD Interface] compability function for onenand_write_ecc
135859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
135959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
136059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to write
136159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of written bytes
136259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the data to write
136359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1364bfd7f386SKyungmin Park  * Write with ECC
136559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
136659829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
136759829cc1SJean-Christophe PLAGNIOL-VILLARD 		  size_t * retlen, const u_char * buf)
136859829cc1SJean-Christophe PLAGNIOL-VILLARD {
1369bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1370bfd7f386SKyungmin Park 		.len    = len,
1371bfd7f386SKyungmin Park 		.ooblen = 0,
1372bfd7f386SKyungmin Park 		.datbuf = (u_char *) buf,
1373bfd7f386SKyungmin Park 		.oobbuf = NULL,
1374bfd7f386SKyungmin Park 	};
1375bfd7f386SKyungmin Park 	int ret;
1376bfd7f386SKyungmin Park 
1377bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_WRITING);
1378bfd7f386SKyungmin Park 	ret = onenand_write_ops_nolock(mtd, to, &ops);
1379bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1380bfd7f386SKyungmin Park 
1381bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1382bfd7f386SKyungmin Park 	return ret;
138359829cc1SJean-Christophe PLAGNIOL-VILLARD }
138459829cc1SJean-Christophe PLAGNIOL-VILLARD 
138559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
138659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
138759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
138859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
1389bfd7f386SKyungmin Park  * @param ops		oob operation description structure
139059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1391bfd7f386SKyungmin Park  * OneNAND write main and/or out-of-band
139259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1393bfd7f386SKyungmin Park int onenand_write_oob(struct mtd_info *mtd, loff_t to,
1394bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
139559829cc1SJean-Christophe PLAGNIOL-VILLARD {
1396bfd7f386SKyungmin Park 	int ret;
139759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1398bfd7f386SKyungmin Park 	switch (ops->mode) {
1399bfd7f386SKyungmin Park 	case MTD_OOB_PLACE:
1400bfd7f386SKyungmin Park 	case MTD_OOB_AUTO:
1401bfd7f386SKyungmin Park 		break;
1402bfd7f386SKyungmin Park 	case MTD_OOB_RAW:
1403bfd7f386SKyungmin Park 		/* Not implemented yet */
1404bfd7f386SKyungmin Park 	default:
140559829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
140659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
140759829cc1SJean-Christophe PLAGNIOL-VILLARD 
140859829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_WRITING);
1409bfd7f386SKyungmin Park 	if (ops->datbuf)
1410bfd7f386SKyungmin Park 		ret = onenand_write_ops_nolock(mtd, to, ops);
1411bfd7f386SKyungmin Park 	else
1412bfd7f386SKyungmin Park 		ret = onenand_write_oob_nolock(mtd, to, ops);
141359829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
141459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1415bfd7f386SKyungmin Park 	return ret;
141659829cc1SJean-Christophe PLAGNIOL-VILLARD 
141759829cc1SJean-Christophe PLAGNIOL-VILLARD }
141859829cc1SJean-Christophe PLAGNIOL-VILLARD 
141959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1420d438d508SKyungmin Park  * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
1421d438d508SKyungmin Park  * @param mtd		MTD device structure
1422d438d508SKyungmin Park  * @param ofs		offset from device start
1423d438d508SKyungmin Park  * @param allowbbt	1, if its allowed to access the bbt area
1424d438d508SKyungmin Park  *
1425d438d508SKyungmin Park  * Check, if the block is bad, Either by reading the bad block table or
1426d438d508SKyungmin Park  * calling of the scan function.
1427d438d508SKyungmin Park  */
1428d438d508SKyungmin Park static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
1429d438d508SKyungmin Park {
1430d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1431d438d508SKyungmin Park 	struct bbm_info *bbm = this->bbm;
1432d438d508SKyungmin Park 
1433d438d508SKyungmin Park 	/* Return info from the table */
1434d438d508SKyungmin Park 	return bbm->isbad_bbt(mtd, ofs, allowbbt);
1435d438d508SKyungmin Park }
1436d438d508SKyungmin Park 
1437d438d508SKyungmin Park 
1438d438d508SKyungmin Park /**
143959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_erase - [MTD Interface] erase block(s)
144059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
144159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param instr		erase instruction
144259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
144359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Erase one ore more blocks
144459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
144559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
144659829cc1SJean-Christophe PLAGNIOL-VILLARD {
144759829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
144859829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int block_size;
144959829cc1SJean-Christophe PLAGNIOL-VILLARD 	loff_t addr;
145059829cc1SJean-Christophe PLAGNIOL-VILLARD 	int len;
145159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
145259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1453bfd7f386SKyungmin Park 	MTDDEBUG (MTD_DEBUG_LEVEL3,
1454bfd7f386SKyungmin Park 		 "onenand_erase: start = 0x%08x, len = %i\n",
145559829cc1SJean-Christophe PLAGNIOL-VILLARD 		 (unsigned int)instr->addr, (unsigned int)instr->len);
145659829cc1SJean-Christophe PLAGNIOL-VILLARD 
145759829cc1SJean-Christophe PLAGNIOL-VILLARD 	block_size = (1 << this->erase_shift);
145859829cc1SJean-Christophe PLAGNIOL-VILLARD 
145959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Start address must align on block boundary */
146059829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely(instr->addr & (block_size - 1))) {
14613167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
14623167c538SScott Wood 			 "onenand_erase: Unaligned address\n");
146359829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
146459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
146559829cc1SJean-Christophe PLAGNIOL-VILLARD 
146659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Length must align on block boundary */
146759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely(instr->len & (block_size - 1))) {
14683167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
146959829cc1SJean-Christophe PLAGNIOL-VILLARD 			 "onenand_erase: Length not block aligned\n");
147059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
147159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
147259829cc1SJean-Christophe PLAGNIOL-VILLARD 
147359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow erase past end of device */
147459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((instr->len + instr->addr) > mtd->size)) {
14753167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
147659829cc1SJean-Christophe PLAGNIOL-VILLARD 			 "onenand_erase: Erase past end of device\n");
147759829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
147859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
147959829cc1SJean-Christophe PLAGNIOL-VILLARD 
148059829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->fail_addr = 0xffffffff;
148159829cc1SJean-Christophe PLAGNIOL-VILLARD 
148259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
148359829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_ERASING);
148459829cc1SJean-Christophe PLAGNIOL-VILLARD 
148559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop throught the pages */
148659829cc1SJean-Christophe PLAGNIOL-VILLARD 	len = instr->len;
148759829cc1SJean-Christophe PLAGNIOL-VILLARD 	addr = instr->addr;
148859829cc1SJean-Christophe PLAGNIOL-VILLARD 
148959829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASING;
149059829cc1SJean-Christophe PLAGNIOL-VILLARD 
149159829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (len) {
149259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1493ef0921d6SKyungmin Park 		/* Check if we have a bad block, we do not erase bad blocks */
1494ef0921d6SKyungmin Park 		if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) {
1495ef0921d6SKyungmin Park 			printk(KERN_WARNING "onenand_erase: attempt to erase"
1496ef0921d6SKyungmin Park 				" a bad block at addr 0x%08x\n",
1497ef0921d6SKyungmin Park 				(unsigned int) addr);
1498ef0921d6SKyungmin Park 			instr->state = MTD_ERASE_FAILED;
1499ef0921d6SKyungmin Park 			goto erase_exit;
1500ef0921d6SKyungmin Park 		}
150159829cc1SJean-Christophe PLAGNIOL-VILLARD 
150259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
150359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1504d438d508SKyungmin Park 		onenand_invalidate_bufferram(mtd, addr, block_size);
1505d438d508SKyungmin Park 
150659829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_ERASING);
150759829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check, if it is write protected */
150859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
150959829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (ret == -EPERM)
15103167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
15113167c538SScott Wood 					  "Device is write protected!!!\n");
151259829cc1SJean-Christophe PLAGNIOL-VILLARD 			else
15133167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
15143167c538SScott Wood 					  "Failed erase, block %d\n",
151559829cc1SJean-Christophe PLAGNIOL-VILLARD 					  (unsigned)(addr >> this->erase_shift));
1516ef0921d6SKyungmin Park 			if (ret == -EPERM)
1517ef0921d6SKyungmin Park 				printk("onenand_erase: "
1518ef0921d6SKyungmin Park 					  "Device is write protected!!!\n");
1519ef0921d6SKyungmin Park 			else
1520ef0921d6SKyungmin Park 				printk("onenand_erase: "
1521ef0921d6SKyungmin Park 					  "Failed erase, block %d\n",
1522ef0921d6SKyungmin Park 					  (unsigned)(addr >> this->erase_shift));
152359829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->state = MTD_ERASE_FAILED;
152459829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->fail_addr = addr;
1525ef0921d6SKyungmin Park 
152659829cc1SJean-Christophe PLAGNIOL-VILLARD 			goto erase_exit;
152759829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
152859829cc1SJean-Christophe PLAGNIOL-VILLARD 
152959829cc1SJean-Christophe PLAGNIOL-VILLARD 		len -= block_size;
153059829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr += block_size;
153159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
153259829cc1SJean-Christophe PLAGNIOL-VILLARD 
153359829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASE_DONE;
153459829cc1SJean-Christophe PLAGNIOL-VILLARD 
153559829cc1SJean-Christophe PLAGNIOL-VILLARD erase_exit:
153659829cc1SJean-Christophe PLAGNIOL-VILLARD 
153759829cc1SJean-Christophe PLAGNIOL-VILLARD 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
153859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do call back function */
153959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!ret)
154059829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd_erase_callback(instr);
154159829cc1SJean-Christophe PLAGNIOL-VILLARD 
154259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
154359829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
154459829cc1SJean-Christophe PLAGNIOL-VILLARD 
154559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
154659829cc1SJean-Christophe PLAGNIOL-VILLARD }
154759829cc1SJean-Christophe PLAGNIOL-VILLARD 
154859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
154959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync - [MTD Interface] sync
155059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
155159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
155259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Sync is actually a wait for chip ready function
155359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
155459829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_sync(struct mtd_info *mtd)
155559829cc1SJean-Christophe PLAGNIOL-VILLARD {
15563167c538SScott Wood 	MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
155759829cc1SJean-Christophe PLAGNIOL-VILLARD 
155859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
155959829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_SYNCING);
156059829cc1SJean-Christophe PLAGNIOL-VILLARD 
156159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Release it and go back */
156259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
156359829cc1SJean-Christophe PLAGNIOL-VILLARD }
156459829cc1SJean-Christophe PLAGNIOL-VILLARD 
156559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
156659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
156759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
156859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1569d438d508SKyungmin Park  *
1570d438d508SKyungmin Park  * Check whether the block is bad
157159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
157259829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
157359829cc1SJean-Christophe PLAGNIOL-VILLARD {
1574d438d508SKyungmin Park 	int ret;
1575d438d508SKyungmin Park 
1576d438d508SKyungmin Park 	/* Check for invalid offset */
1577d438d508SKyungmin Park 	if (ofs > mtd->size)
1578d438d508SKyungmin Park 		return -EINVAL;
1579d438d508SKyungmin Park 
1580d438d508SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1581d438d508SKyungmin Park 	ret = onenand_block_isbad_nolock(mtd,ofs, 0);
1582d438d508SKyungmin Park 	onenand_release_device(mtd);
1583d438d508SKyungmin Park 	return ret;
158459829cc1SJean-Christophe PLAGNIOL-VILLARD }
158559829cc1SJean-Christophe PLAGNIOL-VILLARD 
158659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1587*1714f51aSKyungmin Park  * onenand_default_block_markbad - [DEFAULT] mark a block bad
1588*1714f51aSKyungmin Park  * @param mtd           MTD device structure
1589*1714f51aSKyungmin Park  * @param ofs           offset from device start
1590*1714f51aSKyungmin Park  *
1591*1714f51aSKyungmin Park  * This is the default implementation, which can be overridden by
1592*1714f51aSKyungmin Park  * a hardware specific driver.
1593*1714f51aSKyungmin Park  */
1594*1714f51aSKyungmin Park static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
1595*1714f51aSKyungmin Park {
1596*1714f51aSKyungmin Park 	struct onenand_chip *this = mtd->priv;
1597*1714f51aSKyungmin Park 	struct bbm_info *bbm = this->bbm;
1598*1714f51aSKyungmin Park 	u_char buf[2] = {0, 0};
1599*1714f51aSKyungmin Park 	struct mtd_oob_ops ops = {
1600*1714f51aSKyungmin Park 		.mode = MTD_OOB_PLACE,
1601*1714f51aSKyungmin Park 		.ooblen = 2,
1602*1714f51aSKyungmin Park 		.oobbuf = buf,
1603*1714f51aSKyungmin Park 		.ooboffs = 0,
1604*1714f51aSKyungmin Park 	};
1605*1714f51aSKyungmin Park 	int block;
1606*1714f51aSKyungmin Park 
1607*1714f51aSKyungmin Park 	/* Get block number */
1608*1714f51aSKyungmin Park 	block = ((int) ofs) >> bbm->bbt_erase_shift;
1609*1714f51aSKyungmin Park 	if (bbm->bbt)
1610*1714f51aSKyungmin Park 		bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
1611*1714f51aSKyungmin Park 
1612*1714f51aSKyungmin Park 	/* We write two bytes, so we dont have to mess with 16 bit access */
1613*1714f51aSKyungmin Park 	ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
1614*1714f51aSKyungmin Park 	return onenand_write_oob_nolock(mtd, ofs, &ops);
1615*1714f51aSKyungmin Park }
1616*1714f51aSKyungmin Park 
1617*1714f51aSKyungmin Park /**
161859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
161959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
162059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1621d438d508SKyungmin Park  *
1622d438d508SKyungmin Park  * Mark the block as bad
162359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
162459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
162559829cc1SJean-Christophe PLAGNIOL-VILLARD {
1626d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1627d438d508SKyungmin Park 	int ret;
1628d438d508SKyungmin Park 
1629d438d508SKyungmin Park 	ret = onenand_block_isbad(mtd, ofs);
1630d438d508SKyungmin Park 	if (ret) {
1631d438d508SKyungmin Park 		/* If it was bad already, return success and do nothing */
1632d438d508SKyungmin Park 		if (ret > 0)
163359829cc1SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1634d438d508SKyungmin Park 		return ret;
1635d438d508SKyungmin Park 	}
1636d438d508SKyungmin Park 
1637d438d508SKyungmin Park 	ret = this->block_markbad(mtd, ofs);
1638d438d508SKyungmin Park 	return ret;
163959829cc1SJean-Christophe PLAGNIOL-VILLARD }
164059829cc1SJean-Christophe PLAGNIOL-VILLARD 
164159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1642ef0921d6SKyungmin Park  * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
164359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
164459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs           offset relative to mtd start
1645ef0921d6SKyungmin Park  * @param len           number of bytes to lock or unlock
1646ef0921d6SKyungmin Park  * @param cmd           lock or unlock command
164759829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1648ef0921d6SKyungmin Park  * Lock or unlock one or more blocks
164959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1650ef0921d6SKyungmin Park static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
165159829cc1SJean-Christophe PLAGNIOL-VILLARD {
165259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
165359829cc1SJean-Christophe PLAGNIOL-VILLARD 	int start, end, block, value, status;
1654ef0921d6SKyungmin Park 	int wp_status_mask;
165559829cc1SJean-Christophe PLAGNIOL-VILLARD 
165659829cc1SJean-Christophe PLAGNIOL-VILLARD 	start = ofs >> this->erase_shift;
165759829cc1SJean-Christophe PLAGNIOL-VILLARD 	end = len >> this->erase_shift;
165859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1659ef0921d6SKyungmin Park 	if (cmd == ONENAND_CMD_LOCK)
1660ef0921d6SKyungmin Park 		wp_status_mask = ONENAND_WP_LS;
1661ef0921d6SKyungmin Park 	else
1662ef0921d6SKyungmin Park 		wp_status_mask = ONENAND_WP_US;
1663ef0921d6SKyungmin Park 
166459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Continuous lock scheme */
1665ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK) {
166659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
166759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(start,
166859829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
166959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set end block address */
167059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(end - 1,
167159829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_END_BLOCK_ADDRESS);
167259829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
1673ef0921d6SKyungmin Park 		this->command(mtd, cmd, 0, 0);
167459829cc1SJean-Christophe PLAGNIOL-VILLARD 
167559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
167659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
167759829cc1SJean-Christophe PLAGNIOL-VILLARD 
167859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
167959829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
168059829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
168159829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
168259829cc1SJean-Christophe PLAGNIOL-VILLARD 
168359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
168459829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
168559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
168659829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "wp status = 0x%x\n", status);
168759829cc1SJean-Christophe PLAGNIOL-VILLARD 
168859829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
168959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
169059829cc1SJean-Christophe PLAGNIOL-VILLARD 
169159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Block lock scheme */
1692ef0921d6SKyungmin Park 	for (block = start; block < start + end; block++) {
1693ef0921d6SKyungmin Park 		/* Set block address */
1694ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
1695ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
1696ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
1697ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
1698ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
1699ef0921d6SKyungmin Park 
170059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
170159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(block,
170259829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
170359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
170459829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
170559829cc1SJean-Christophe PLAGNIOL-VILLARD 
170659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
170759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
170859829cc1SJean-Christophe PLAGNIOL-VILLARD 
170959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
171059829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
171159829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
171259829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
171359829cc1SJean-Christophe PLAGNIOL-VILLARD 
171459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
171559829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
171659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
171759829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "block = %d, wp status = 0x%x\n",
171859829cc1SJean-Christophe PLAGNIOL-VILLARD 			       block, status);
171959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
172059829cc1SJean-Christophe PLAGNIOL-VILLARD 
172159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
172259829cc1SJean-Christophe PLAGNIOL-VILLARD }
172359829cc1SJean-Christophe PLAGNIOL-VILLARD 
17244fca3310SStefan Roese #ifdef ONENAND_LINUX
172559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1726ef0921d6SKyungmin Park  * onenand_lock - [MTD Interface] Lock block(s)
1727ef0921d6SKyungmin Park  * @param mtd           MTD device structure
1728ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
1729ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
1730ef0921d6SKyungmin Park  *
1731ef0921d6SKyungmin Park  * Lock one or more blocks
1732ef0921d6SKyungmin Park  */
1733ef0921d6SKyungmin Park static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
1734ef0921d6SKyungmin Park {
1735ef0921d6SKyungmin Park 	int ret;
1736ef0921d6SKyungmin Park 
1737ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
1738ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
1739ef0921d6SKyungmin Park 	onenand_release_device(mtd);
1740ef0921d6SKyungmin Park 	return ret;
1741ef0921d6SKyungmin Park }
1742ef0921d6SKyungmin Park 
1743ef0921d6SKyungmin Park /**
1744ef0921d6SKyungmin Park  * onenand_unlock - [MTD Interface] Unlock block(s)
1745ef0921d6SKyungmin Park  * @param mtd           MTD device structure
1746ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
1747ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
1748ef0921d6SKyungmin Park  *
1749ef0921d6SKyungmin Park  * Unlock one or more blocks
1750ef0921d6SKyungmin Park  */
1751ef0921d6SKyungmin Park static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
1752ef0921d6SKyungmin Park {
1753ef0921d6SKyungmin Park 	int ret;
1754ef0921d6SKyungmin Park 
1755ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
1756ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
1757ef0921d6SKyungmin Park 	onenand_release_device(mtd);
1758ef0921d6SKyungmin Park 	return ret;
1759ef0921d6SKyungmin Park }
17604fca3310SStefan Roese #endif
1761ef0921d6SKyungmin Park 
1762ef0921d6SKyungmin Park /**
1763ef0921d6SKyungmin Park  * onenand_check_lock_status - [OneNAND Interface] Check lock status
1764ef0921d6SKyungmin Park  * @param this          onenand chip data structure
1765ef0921d6SKyungmin Park  *
1766ef0921d6SKyungmin Park  * Check lock status
1767ef0921d6SKyungmin Park  */
1768ef0921d6SKyungmin Park static int onenand_check_lock_status(struct onenand_chip *this)
1769ef0921d6SKyungmin Park {
1770ef0921d6SKyungmin Park 	unsigned int value, block, status;
1771ef0921d6SKyungmin Park 	unsigned int end;
1772ef0921d6SKyungmin Park 
1773ef0921d6SKyungmin Park 	end = this->chipsize >> this->erase_shift;
1774ef0921d6SKyungmin Park 	for (block = 0; block < end; block++) {
1775ef0921d6SKyungmin Park 		/* Set block address */
1776ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
1777ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
1778ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
1779ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
1780ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
1781ef0921d6SKyungmin Park 		/* Set start block address */
1782ef0921d6SKyungmin Park 		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
1783ef0921d6SKyungmin Park 
1784ef0921d6SKyungmin Park 		/* Check lock status */
1785ef0921d6SKyungmin Park 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
1786ef0921d6SKyungmin Park 		if (!(status & ONENAND_WP_US)) {
1787ef0921d6SKyungmin Park 			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
1788ef0921d6SKyungmin Park 			return 0;
1789ef0921d6SKyungmin Park 		}
1790ef0921d6SKyungmin Park 	}
1791ef0921d6SKyungmin Park 
1792ef0921d6SKyungmin Park 	return 1;
1793ef0921d6SKyungmin Park }
1794ef0921d6SKyungmin Park 
1795ef0921d6SKyungmin Park /**
1796ef0921d6SKyungmin Park  * onenand_unlock_all - [OneNAND Interface] unlock all blocks
1797ef0921d6SKyungmin Park  * @param mtd           MTD device structure
1798ef0921d6SKyungmin Park  *
1799ef0921d6SKyungmin Park  * Unlock all blocks
1800ef0921d6SKyungmin Park  */
1801ef0921d6SKyungmin Park static void onenand_unlock_all(struct mtd_info *mtd)
1802ef0921d6SKyungmin Park {
1803ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1804ef0921d6SKyungmin Park 	loff_t ofs = 0;
1805ef0921d6SKyungmin Park 	size_t len = this->chipsize;
1806ef0921d6SKyungmin Park 
1807ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
1808ef0921d6SKyungmin Park 		/* Set start block address */
1809ef0921d6SKyungmin Park 		this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
1810ef0921d6SKyungmin Park 		/* Write unlock command */
1811ef0921d6SKyungmin Park 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
1812ef0921d6SKyungmin Park 
1813ef0921d6SKyungmin Park 		/* There's no return value */
1814ef0921d6SKyungmin Park 		this->wait(mtd, FL_LOCKING);
1815ef0921d6SKyungmin Park 
1816ef0921d6SKyungmin Park 		/* Sanity check */
1817ef0921d6SKyungmin Park 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
1818ef0921d6SKyungmin Park 				& ONENAND_CTRL_ONGO)
1819ef0921d6SKyungmin Park 			continue;
1820ef0921d6SKyungmin Park 
1821ef0921d6SKyungmin Park 		return;
1822ef0921d6SKyungmin Park 
1823ef0921d6SKyungmin Park 		/* Check lock status */
1824ef0921d6SKyungmin Park 		if (onenand_check_lock_status(this))
1825ef0921d6SKyungmin Park 			return;
1826ef0921d6SKyungmin Park 
1827ef0921d6SKyungmin Park 		/* Workaround for all block unlock in DDP */
1828ef0921d6SKyungmin Park 		if (ONENAND_IS_DDP(this)) {
1829ef0921d6SKyungmin Park 			/* All blocks on another chip */
1830ef0921d6SKyungmin Park 			ofs = this->chipsize >> 1;
1831ef0921d6SKyungmin Park 			len = this->chipsize >> 1;
1832ef0921d6SKyungmin Park 		}
1833ef0921d6SKyungmin Park 	}
1834ef0921d6SKyungmin Park 
1835ef0921d6SKyungmin Park 	onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
1836ef0921d6SKyungmin Park }
1837ef0921d6SKyungmin Park 
1838ef0921d6SKyungmin Park 
1839ef0921d6SKyungmin Park /**
1840ef0921d6SKyungmin Park  * onenand_check_features - Check and set OneNAND features
1841ef0921d6SKyungmin Park  * @param mtd           MTD data structure
1842ef0921d6SKyungmin Park  *
1843ef0921d6SKyungmin Park  * Check and set OneNAND features
1844ef0921d6SKyungmin Park  * - lock scheme
1845ef0921d6SKyungmin Park  * - two plane
1846ef0921d6SKyungmin Park  */
1847ef0921d6SKyungmin Park static void onenand_check_features(struct mtd_info *mtd)
1848ef0921d6SKyungmin Park {
1849ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1850ef0921d6SKyungmin Park 	unsigned int density, process;
1851ef0921d6SKyungmin Park 
1852ef0921d6SKyungmin Park 	/* Lock scheme depends on density and process */
1853ef0921d6SKyungmin Park 	density = onenand_get_density(this->device_id);
1854ef0921d6SKyungmin Park 	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
1855ef0921d6SKyungmin Park 
1856ef0921d6SKyungmin Park 	/* Lock scheme */
1857ef0921d6SKyungmin Park 	switch (density) {
1858ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_4Gb:
1859ef0921d6SKyungmin Park 		this->options |= ONENAND_HAS_2PLANE;
1860ef0921d6SKyungmin Park 
1861ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_2Gb:
1862ef0921d6SKyungmin Park 		/* 2Gb DDP don't have 2 plane */
1863ef0921d6SKyungmin Park 		if (!ONENAND_IS_DDP(this))
1864ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
1865ef0921d6SKyungmin Park 		this->options |= ONENAND_HAS_UNLOCK_ALL;
1866ef0921d6SKyungmin Park 
1867ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_1Gb:
1868ef0921d6SKyungmin Park 		/* A-Die has all block unlock */
1869ef0921d6SKyungmin Park 		if (process)
1870ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_UNLOCK_ALL;
1871ef0921d6SKyungmin Park 		break;
1872ef0921d6SKyungmin Park 
1873ef0921d6SKyungmin Park 	default:
1874ef0921d6SKyungmin Park 		/* Some OneNAND has continuous lock scheme */
1875ef0921d6SKyungmin Park 		if (!process)
1876ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_CONT_LOCK;
1877ef0921d6SKyungmin Park 		break;
1878ef0921d6SKyungmin Park 	}
1879ef0921d6SKyungmin Park 
1880ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK)
1881ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
1882ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL)
1883ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip support all block unlock\n");
1884ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_2PLANE)
1885ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip has 2 plane\n");
1886ef0921d6SKyungmin Park }
1887ef0921d6SKyungmin Park 
1888ef0921d6SKyungmin Park /**
188959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_print_device_info - Print device ID
189059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device        device ID
189159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
189259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Print device ID
189359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1894ef0921d6SKyungmin Park char *onenand_print_device_info(int device, int version)
189559829cc1SJean-Christophe PLAGNIOL-VILLARD {
189659829cc1SJean-Christophe PLAGNIOL-VILLARD 	int vcc, demuxed, ddp, density;
1897195ccfc5SFathi BOUDRA 	char *dev_info = malloc(80);
1898ef0921d6SKyungmin Park 	char *p = dev_info;
189959829cc1SJean-Christophe PLAGNIOL-VILLARD 
190059829cc1SJean-Christophe PLAGNIOL-VILLARD 	vcc = device & ONENAND_DEVICE_VCC_MASK;
190159829cc1SJean-Christophe PLAGNIOL-VILLARD 	demuxed = device & ONENAND_DEVICE_IS_DEMUX;
190259829cc1SJean-Christophe PLAGNIOL-VILLARD 	ddp = device & ONENAND_DEVICE_IS_DDP;
190359829cc1SJean-Christophe PLAGNIOL-VILLARD 	density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
1904ef0921d6SKyungmin Park 	p += sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
190559829cc1SJean-Christophe PLAGNIOL-VILLARD 	       demuxed ? "" : "Muxed ",
190659829cc1SJean-Christophe PLAGNIOL-VILLARD 	       ddp ? "(DDP)" : "",
190759829cc1SJean-Christophe PLAGNIOL-VILLARD 	       (16 << density), vcc ? "2.65/3.3" : "1.8", device);
1908195ccfc5SFathi BOUDRA 
1909ef0921d6SKyungmin Park 	sprintf(p, "\nOneNAND version = 0x%04x", version);
1910ef0921d6SKyungmin Park 	printk("%s\n", dev_info);
1911ef0921d6SKyungmin Park 
1912195ccfc5SFathi BOUDRA 	return dev_info;
191359829cc1SJean-Christophe PLAGNIOL-VILLARD }
191459829cc1SJean-Christophe PLAGNIOL-VILLARD 
191559829cc1SJean-Christophe PLAGNIOL-VILLARD static const struct onenand_manufacturers onenand_manuf_ids[] = {
191659829cc1SJean-Christophe PLAGNIOL-VILLARD 	{ONENAND_MFR_SAMSUNG, "Samsung"},
191759829cc1SJean-Christophe PLAGNIOL-VILLARD };
191859829cc1SJean-Christophe PLAGNIOL-VILLARD 
191959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
192059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_maf - Check manufacturer ID
192159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param manuf         manufacturer ID
192259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
192359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check manufacturer ID
192459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
192559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_maf(int manuf)
192659829cc1SJean-Christophe PLAGNIOL-VILLARD {
1927ef0921d6SKyungmin Park 	int size = ARRAY_SIZE(onenand_manuf_ids);
1928ef0921d6SKyungmin Park 	char *name;
192959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i;
193059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1931ef0921d6SKyungmin Park 	for (i = 0; size; i++)
193259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (manuf == onenand_manuf_ids[i].id)
193359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
1934ef0921d6SKyungmin Park 
1935ef0921d6SKyungmin Park 	if (i < size)
1936ef0921d6SKyungmin Park 		name = onenand_manuf_ids[i].name;
1937ef0921d6SKyungmin Park 	else
1938ef0921d6SKyungmin Park 		name = "Unknown";
193959829cc1SJean-Christophe PLAGNIOL-VILLARD 
194059829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef ONENAND_DEBUG
1941ef0921d6SKyungmin Park 	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
194259829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
194359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1944ef0921d6SKyungmin Park 	return i == size;
194559829cc1SJean-Christophe PLAGNIOL-VILLARD }
194659829cc1SJean-Christophe PLAGNIOL-VILLARD 
194759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
194859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_probe - [OneNAND Interface] Probe the OneNAND device
194959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
195059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
195159829cc1SJean-Christophe PLAGNIOL-VILLARD  * OneNAND detection method:
195259829cc1SJean-Christophe PLAGNIOL-VILLARD  *   Compare the the values from command with ones from register
195359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
195459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_probe(struct mtd_info *mtd)
195559829cc1SJean-Christophe PLAGNIOL-VILLARD {
195659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1957ef0921d6SKyungmin Park 	int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
195859829cc1SJean-Christophe PLAGNIOL-VILLARD 	int density;
1959ef0921d6SKyungmin Park 	int syscfg;
1960ef0921d6SKyungmin Park 
1961ef0921d6SKyungmin Park 	/* Save system configuration 1 */
1962ef0921d6SKyungmin Park 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
1963ef0921d6SKyungmin Park 	/* Clear Sync. Burst Read mode to read BootRAM */
1964ef0921d6SKyungmin Park 	this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
196559829cc1SJean-Christophe PLAGNIOL-VILLARD 
196659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Send the command for reading device ID from BootRAM */
196759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
196859829cc1SJean-Christophe PLAGNIOL-VILLARD 
196959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from BootRAM */
197059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
197159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
197259829cc1SJean-Christophe PLAGNIOL-VILLARD 
197359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reset OneNAND to read default register values */
197459829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
197559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1976d438d508SKyungmin Park 	/* Wait reset */
1977d438d508SKyungmin Park 	this->wait(mtd, FL_RESETING);
197859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1979ef0921d6SKyungmin Park 	/* Restore system configuration 1 */
1980ef0921d6SKyungmin Park 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
1981ef0921d6SKyungmin Park 
1982ef0921d6SKyungmin Park 	/* Check manufacturer ID */
1983ef0921d6SKyungmin Park 	if (onenand_check_maf(bram_maf_id))
1984ef0921d6SKyungmin Park 		return -ENXIO;
1985ef0921d6SKyungmin Park 
198659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from Register */
198759829cc1SJean-Christophe PLAGNIOL-VILLARD 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
198859829cc1SJean-Christophe PLAGNIOL-VILLARD 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
1989ef0921d6SKyungmin Park 	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
199059829cc1SJean-Christophe PLAGNIOL-VILLARD 
199159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Check OneNAND device */
199259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (maf_id != bram_maf_id || dev_id != bram_dev_id)
199359829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
199459829cc1SJean-Christophe PLAGNIOL-VILLARD 
19951bb707c3SKyungmin Park 	/* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */
19961bb707c3SKyungmin Park 	if (dev_id & (1 << 9)) {
19971bb707c3SKyungmin Park 		printk("Not yet support Flex-OneNAND\n");
19981bb707c3SKyungmin Park 		return -ENXIO;
19991bb707c3SKyungmin Park 	}
20001bb707c3SKyungmin Park 
200159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash device information */
2002ef0921d6SKyungmin Park 	mtd->name = onenand_print_device_info(dev_id, ver_id);
200359829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->device_id = dev_id;
20048cf11f3aSStefan Roese 	this->version_id = ver_id;
200559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2006ef0921d6SKyungmin Park 	density = onenand_get_density(dev_id);
200759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->chipsize = (16 << density) << 20;
2008ef0921d6SKyungmin Park 	/* Set density mask. it is used for DDP */
2009ef0921d6SKyungmin Park 	if (ONENAND_IS_DDP(this))
2010ef0921d6SKyungmin Park 		this->density_mask = (1 << (density + 6));
2011ef0921d6SKyungmin Park 	else
2012ef0921d6SKyungmin Park 		this->density_mask = 0;
201359829cc1SJean-Christophe PLAGNIOL-VILLARD 
201459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* OneNAND page size & block size */
201559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* The data buffer size is equal to page size */
2016d438d508SKyungmin Park 	mtd->writesize =
201759829cc1SJean-Christophe PLAGNIOL-VILLARD 	    this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
2018d438d508SKyungmin Park 	mtd->oobsize = mtd->writesize >> 5;
201959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Pagers per block is always 64 in OneNAND */
2020d438d508SKyungmin Park 	mtd->erasesize = mtd->writesize << 6;
202159829cc1SJean-Christophe PLAGNIOL-VILLARD 
202259829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->erase_shift = ffs(mtd->erasesize) - 1;
2023d438d508SKyungmin Park 	this->page_shift = ffs(mtd->writesize) - 1;
202459829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->ppb_shift = (this->erase_shift - this->page_shift);
2025d438d508SKyungmin Park 	this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
2026bfd7f386SKyungmin Park 	/* It's real page size */
2027bfd7f386SKyungmin Park 	this->writesize = mtd->writesize;
202859829cc1SJean-Christophe PLAGNIOL-VILLARD 
202959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* REVIST: Multichip handling */
203059829cc1SJean-Christophe PLAGNIOL-VILLARD 
203159829cc1SJean-Christophe PLAGNIOL-VILLARD 	mtd->size = this->chipsize;
203259829cc1SJean-Christophe PLAGNIOL-VILLARD 
2033ef0921d6SKyungmin Park 	/* Check OneNAND features */
2034ef0921d6SKyungmin Park 	onenand_check_features(mtd);
203559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2036d438d508SKyungmin Park 	mtd->flags = MTD_CAP_NANDFLASH;
2037195ccfc5SFathi BOUDRA 	mtd->erase = onenand_erase;
2038195ccfc5SFathi BOUDRA 	mtd->read = onenand_read;
2039195ccfc5SFathi BOUDRA 	mtd->write = onenand_write;
2040195ccfc5SFathi BOUDRA 	mtd->read_oob = onenand_read_oob;
2041195ccfc5SFathi BOUDRA 	mtd->write_oob = onenand_write_oob;
2042195ccfc5SFathi BOUDRA 	mtd->sync = onenand_sync;
2043195ccfc5SFathi BOUDRA 	mtd->block_isbad = onenand_block_isbad;
2044195ccfc5SFathi BOUDRA 	mtd->block_markbad = onenand_block_markbad;
2045195ccfc5SFathi BOUDRA 
204659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
204759829cc1SJean-Christophe PLAGNIOL-VILLARD }
204859829cc1SJean-Christophe PLAGNIOL-VILLARD 
204959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
205059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
205159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
205259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param maxchips	Number of chips to scan for
205359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
205459829cc1SJean-Christophe PLAGNIOL-VILLARD  * This fills out all the not initialized function pointers
205559829cc1SJean-Christophe PLAGNIOL-VILLARD  * with the defaults.
205659829cc1SJean-Christophe PLAGNIOL-VILLARD  * The flash ID is read and the mtd/chip structures are
205759829cc1SJean-Christophe PLAGNIOL-VILLARD  * filled with the appropriate values.
205859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
205959829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_scan(struct mtd_info *mtd, int maxchips)
206059829cc1SJean-Christophe PLAGNIOL-VILLARD {
206159829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
206259829cc1SJean-Christophe PLAGNIOL-VILLARD 
206359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_word)
206459829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_word = onenand_readw;
206559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_word)
206659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word = onenand_writew;
206759829cc1SJean-Christophe PLAGNIOL-VILLARD 
206859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->command)
206959829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command = onenand_command;
207059829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->wait)
207159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait = onenand_wait;
2072ef0921d6SKyungmin Park 	if (!this->bbt_wait)
2073ef0921d6SKyungmin Park 		this->bbt_wait = onenand_bbt_wait;
207459829cc1SJean-Christophe PLAGNIOL-VILLARD 
207559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_bufferram)
207659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_read_bufferram;
2077ef0921d6SKyungmin Park 	if (!this->read_spareram)
2078ef0921d6SKyungmin Park 		this->read_spareram = onenand_read_bufferram;
207959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_bufferram)
208059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_bufferram = onenand_write_bufferram;
208159829cc1SJean-Christophe PLAGNIOL-VILLARD 
2082*1714f51aSKyungmin Park 	if (!this->block_markbad)
2083*1714f51aSKyungmin Park 		this->block_markbad = onenand_default_block_markbad;
2084ef0921d6SKyungmin Park 	if (!this->scan_bbt)
2085ef0921d6SKyungmin Park 		this->scan_bbt = onenand_default_bbt;
2086ef0921d6SKyungmin Park 
208759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (onenand_probe(mtd))
208859829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
208959829cc1SJean-Christophe PLAGNIOL-VILLARD 
209059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Set Sync. Burst Read after probing */
209159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (this->mmcontrol) {
209259829cc1SJean-Christophe PLAGNIOL-VILLARD 		printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
209359829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_sync_read_bufferram;
209459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
209559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2096bfd7f386SKyungmin Park 	/* Allocate buffers, if necessary */
2097bfd7f386SKyungmin Park 	if (!this->page_buf) {
2098bfd7f386SKyungmin Park 		this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
2099bfd7f386SKyungmin Park 		if (!this->page_buf) {
2100bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
2101bfd7f386SKyungmin Park 			return -ENOMEM;
2102bfd7f386SKyungmin Park 		}
2103bfd7f386SKyungmin Park 		this->options |= ONENAND_PAGEBUF_ALLOC;
2104bfd7f386SKyungmin Park 	}
2105bfd7f386SKyungmin Park 	if (!this->oob_buf) {
2106bfd7f386SKyungmin Park 		this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
2107bfd7f386SKyungmin Park 		if (!this->oob_buf) {
2108bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n");
2109bfd7f386SKyungmin Park 			if (this->options & ONENAND_PAGEBUF_ALLOC) {
2110bfd7f386SKyungmin Park 				this->options &= ~ONENAND_PAGEBUF_ALLOC;
2111bfd7f386SKyungmin Park 				kfree(this->page_buf);
2112bfd7f386SKyungmin Park 			}
2113bfd7f386SKyungmin Park 			return -ENOMEM;
2114bfd7f386SKyungmin Park 		}
2115bfd7f386SKyungmin Park 		this->options |= ONENAND_OOBBUF_ALLOC;
2116bfd7f386SKyungmin Park 	}
2117bfd7f386SKyungmin Park 
2118ef0921d6SKyungmin Park 	/* Unlock whole block */
2119ef0921d6SKyungmin Park 	onenand_unlock_all(mtd);
212059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2121ef0921d6SKyungmin Park 	return this->scan_bbt(mtd);
212259829cc1SJean-Christophe PLAGNIOL-VILLARD }
212359829cc1SJean-Christophe PLAGNIOL-VILLARD 
212459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
212559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
212659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
212759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
212859829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_release(struct mtd_info *mtd)
212959829cc1SJean-Christophe PLAGNIOL-VILLARD {
213059829cc1SJean-Christophe PLAGNIOL-VILLARD }
2131