xref: /rk3399_rockchip-uboot/drivers/mtd/onenand/onenand_base.c (revision e26fd3d3bfcf9a425b57defeb91b4e088392e3d9)
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  *
12cacbe919SAmul Kumar Saha  *      Rohit Hagargundgi <h.rohit at samsung.com>,
13cacbe919SAmul Kumar Saha  *      Amul Kumar Saha <amul.saha@samsung.com>:
14cacbe919SAmul Kumar Saha  *      Flex-OneNAND support
15cacbe919SAmul Kumar Saha  *      Copyright (C) Samsung Electronics, 2009
16cacbe919SAmul Kumar Saha  *
1759829cc1SJean-Christophe PLAGNIOL-VILLARD  * This program is free software; you can redistribute it and/or modify
1859829cc1SJean-Christophe PLAGNIOL-VILLARD  * it under the terms of the GNU General Public License version 2 as
1959829cc1SJean-Christophe PLAGNIOL-VILLARD  * published by the Free Software Foundation.
2059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
2159829cc1SJean-Christophe PLAGNIOL-VILLARD 
2259829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
2359829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/compat.h>
2459829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/mtd.h>
2559829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/onenand.h>
2659829cc1SJean-Christophe PLAGNIOL-VILLARD 
2759829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h>
2859829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/errno.h>
29195ccfc5SFathi BOUDRA #include <malloc.h>
3059829cc1SJean-Christophe PLAGNIOL-VILLARD 
3177e475ccSKyungmin Park /* It should access 16-bit instead of 8-bit */
32cacbe919SAmul Kumar Saha static void *memcpy_16(void *dst, const void *src, unsigned int len)
3377e475ccSKyungmin Park {
3477e475ccSKyungmin Park 	void *ret = dst;
3577e475ccSKyungmin Park 	short *d = dst;
3677e475ccSKyungmin Park 	const short *s = src;
3777e475ccSKyungmin Park 
3877e475ccSKyungmin Park 	len >>= 1;
3977e475ccSKyungmin Park 	while (len-- > 0)
4077e475ccSKyungmin Park 		*d++ = *s++;
4177e475ccSKyungmin Park 	return ret;
4277e475ccSKyungmin Park }
4377e475ccSKyungmin Park 
441ae39862SStefan Roese /**
45cacbe919SAmul Kumar Saha  *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
46cacbe919SAmul Kumar Saha  *  For now, we expose only 64 out of 80 ecc bytes
47cacbe919SAmul Kumar Saha  */
48cacbe919SAmul Kumar Saha static struct nand_ecclayout onenand_oob_128 = {
49cacbe919SAmul Kumar Saha 	.eccbytes	= 64,
50cacbe919SAmul Kumar Saha 	.eccpos		= {
51cacbe919SAmul Kumar Saha 		6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
52cacbe919SAmul Kumar Saha 		22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
53cacbe919SAmul Kumar Saha 		38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
54cacbe919SAmul Kumar Saha 		54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
55cacbe919SAmul Kumar Saha 		70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
56cacbe919SAmul Kumar Saha 		86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
57cacbe919SAmul Kumar Saha 		102, 103, 104, 105
58cacbe919SAmul Kumar Saha 		},
59cacbe919SAmul Kumar Saha 	.oobfree	= {
60cacbe919SAmul Kumar Saha 		{2, 4}, {18, 4}, {34, 4}, {50, 4},
61cacbe919SAmul Kumar Saha 		{66, 4}, {82, 4}, {98, 4}, {114, 4}
62cacbe919SAmul Kumar Saha 	}
63cacbe919SAmul Kumar Saha };
64cacbe919SAmul Kumar Saha 
65cacbe919SAmul Kumar Saha /**
661ae39862SStefan Roese  * onenand_oob_64 - oob info for large (2KB) page
671ae39862SStefan Roese  */
681ae39862SStefan Roese static struct nand_ecclayout onenand_oob_64 = {
691ae39862SStefan Roese 	.eccbytes	= 20,
701ae39862SStefan Roese 	.eccpos		= {
711ae39862SStefan Roese 		8, 9, 10, 11, 12,
721ae39862SStefan Roese 		24, 25, 26, 27, 28,
731ae39862SStefan Roese 		40, 41, 42, 43, 44,
741ae39862SStefan Roese 		56, 57, 58, 59, 60,
751ae39862SStefan Roese 		},
761ae39862SStefan Roese 	.oobfree	= {
771ae39862SStefan Roese 		{2, 3}, {14, 2}, {18, 3}, {30, 2},
781ae39862SStefan Roese 		{34, 3}, {46, 2}, {50, 3}, {62, 2}
791ae39862SStefan Roese 	}
801ae39862SStefan Roese };
811ae39862SStefan Roese 
821ae39862SStefan Roese /**
831ae39862SStefan Roese  * onenand_oob_32 - oob info for middle (1KB) page
841ae39862SStefan Roese  */
851ae39862SStefan Roese static struct nand_ecclayout onenand_oob_32 = {
861ae39862SStefan Roese 	.eccbytes	= 10,
871ae39862SStefan Roese 	.eccpos		= {
881ae39862SStefan Roese 		8, 9, 10, 11, 12,
891ae39862SStefan Roese 		24, 25, 26, 27, 28,
901ae39862SStefan Roese 		},
911ae39862SStefan Roese 	.oobfree	= { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
921ae39862SStefan Roese };
931ae39862SStefan Roese 
9459829cc1SJean-Christophe PLAGNIOL-VILLARD static const unsigned char ffchars[] = {
9559829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
9659829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 16 */
9759829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
9859829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 32 */
9959829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
10059829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 48 */
10159829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
10259829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 64 */
103cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
104cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 80 */
105cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
106cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 96 */
107cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
108cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 112 */
109cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
110cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 128 */
11159829cc1SJean-Christophe PLAGNIOL-VILLARD };
11259829cc1SJean-Christophe PLAGNIOL-VILLARD 
11359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
11459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_readw - [OneNAND Interface] Read OneNAND register
11559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to read
11659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
11759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read OneNAND register
11859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
11959829cc1SJean-Christophe PLAGNIOL-VILLARD static unsigned short onenand_readw(void __iomem * addr)
12059829cc1SJean-Christophe PLAGNIOL-VILLARD {
12159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return readw(addr);
12259829cc1SJean-Christophe PLAGNIOL-VILLARD }
12359829cc1SJean-Christophe PLAGNIOL-VILLARD 
12459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
12559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_writew - [OneNAND Interface] Write OneNAND register with value
12659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param value		value to write
12759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to write
12859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
12959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write OneNAND register with value
13059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
13159829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_writew(unsigned short value, void __iomem * addr)
13259829cc1SJean-Christophe PLAGNIOL-VILLARD {
13359829cc1SJean-Christophe PLAGNIOL-VILLARD 	writew(value, addr);
13459829cc1SJean-Christophe PLAGNIOL-VILLARD }
13559829cc1SJean-Christophe PLAGNIOL-VILLARD 
13659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
13759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_address - [DEFAULT] Get block address
13859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
13959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
14059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		translated block address if DDP, otherwise same
14159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
14259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 1 Register (F100h)
14359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
144ef0921d6SKyungmin Park static int onenand_block_address(struct onenand_chip *this, int block)
14559829cc1SJean-Christophe PLAGNIOL-VILLARD {
14659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device Flash Core select, NAND Flash Block Address */
147ef0921d6SKyungmin Park 	if (block & this->density_mask)
148ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
14959829cc1SJean-Christophe PLAGNIOL-VILLARD 
15059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return block;
15159829cc1SJean-Christophe PLAGNIOL-VILLARD }
15259829cc1SJean-Christophe PLAGNIOL-VILLARD 
15359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
15459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_address - [DEFAULT] Get bufferram address
15559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
15659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
15759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		set DBS value if DDP, otherwise 0
15859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
15959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 2 Register (F101h) for DDP
16059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
161ef0921d6SKyungmin Park static int onenand_bufferram_address(struct onenand_chip *this, int block)
16259829cc1SJean-Christophe PLAGNIOL-VILLARD {
16359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device BufferRAM Select */
164ef0921d6SKyungmin Park 	if (block & this->density_mask)
165ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1;
16659829cc1SJean-Christophe PLAGNIOL-VILLARD 
167ef0921d6SKyungmin Park 	return ONENAND_DDP_CHIP0;
16859829cc1SJean-Christophe PLAGNIOL-VILLARD }
16959829cc1SJean-Christophe PLAGNIOL-VILLARD 
17059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
17159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_page_address - [DEFAULT] Get page address
17259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param page		the page address
17359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sector	the sector address
17459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		combined page and sector address
17559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
17659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 8 Register (F107h)
17759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
17859829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_page_address(int page, int sector)
17959829cc1SJean-Christophe PLAGNIOL-VILLARD {
18059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash Page Address, Flash Sector Address */
18159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int fpa, fsa;
18259829cc1SJean-Christophe PLAGNIOL-VILLARD 
18359829cc1SJean-Christophe PLAGNIOL-VILLARD 	fpa = page & ONENAND_FPA_MASK;
18459829cc1SJean-Christophe PLAGNIOL-VILLARD 	fsa = sector & ONENAND_FSA_MASK;
18559829cc1SJean-Christophe PLAGNIOL-VILLARD 
18659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((fpa << ONENAND_FPA_SHIFT) | fsa);
18759829cc1SJean-Christophe PLAGNIOL-VILLARD }
18859829cc1SJean-Christophe PLAGNIOL-VILLARD 
18959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
19059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_buffer_address - [DEFAULT] Get buffer address
19159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param dataram1	DataRAM index
19259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sectors	the sector address
19359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		the number of sectors
19459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		the start buffer value
19559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
19659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Buffer Register (F200h)
19759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
19859829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_buffer_address(int dataram1, int sectors, int count)
19959829cc1SJean-Christophe PLAGNIOL-VILLARD {
20059829cc1SJean-Christophe PLAGNIOL-VILLARD 	int bsa, bsc;
20159829cc1SJean-Christophe PLAGNIOL-VILLARD 
20259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Address */
20359829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsa = sectors & ONENAND_BSA_MASK;
20459829cc1SJean-Christophe PLAGNIOL-VILLARD 
20559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (dataram1)
20659829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM1;	/* DataRAM1 */
20759829cc1SJean-Christophe PLAGNIOL-VILLARD 	else
20859829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM0;	/* DataRAM0 */
20959829cc1SJean-Christophe PLAGNIOL-VILLARD 
21059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Count */
21159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsc = count & ONENAND_BSC_MASK;
21259829cc1SJean-Christophe PLAGNIOL-VILLARD 
21359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((bsa << ONENAND_BSA_SHIFT) | bsc);
21459829cc1SJean-Christophe PLAGNIOL-VILLARD }
21559829cc1SJean-Christophe PLAGNIOL-VILLARD 
21659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
217cacbe919SAmul Kumar Saha  * flexonenand_block - Return block number for flash address
218cacbe919SAmul Kumar Saha  * @param this		- OneNAND device structure
219cacbe919SAmul Kumar Saha  * @param addr		- Address for which block number is needed
220cacbe919SAmul Kumar Saha  */
221cacbe919SAmul Kumar Saha static unsigned int flexonenand_block(struct onenand_chip *this, loff_t addr)
222cacbe919SAmul Kumar Saha {
223cacbe919SAmul Kumar Saha 	unsigned int boundary, blk, die = 0;
224cacbe919SAmul Kumar Saha 
225cacbe919SAmul Kumar Saha 	if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
226cacbe919SAmul Kumar Saha 		die = 1;
227cacbe919SAmul Kumar Saha 		addr -= this->diesize[0];
228cacbe919SAmul Kumar Saha 	}
229cacbe919SAmul Kumar Saha 
230cacbe919SAmul Kumar Saha 	boundary = this->boundary[die];
231cacbe919SAmul Kumar Saha 
232cacbe919SAmul Kumar Saha 	blk = addr >> (this->erase_shift - 1);
233cacbe919SAmul Kumar Saha 	if (blk > boundary)
234cacbe919SAmul Kumar Saha 		blk = (blk + boundary + 1) >> 1;
235cacbe919SAmul Kumar Saha 
236cacbe919SAmul Kumar Saha 	blk += die ? this->density_mask : 0;
237cacbe919SAmul Kumar Saha 	return blk;
238cacbe919SAmul Kumar Saha }
239cacbe919SAmul Kumar Saha 
240cacbe919SAmul Kumar Saha unsigned int onenand_block(struct onenand_chip *this, loff_t addr)
241cacbe919SAmul Kumar Saha {
242cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
243cacbe919SAmul Kumar Saha 		return addr >> this->erase_shift;
244cacbe919SAmul Kumar Saha 	return flexonenand_block(this, addr);
245cacbe919SAmul Kumar Saha }
246cacbe919SAmul Kumar Saha 
247cacbe919SAmul Kumar Saha /**
248cacbe919SAmul Kumar Saha  * flexonenand_addr - Return address of the block
249cacbe919SAmul Kumar Saha  * @this:		OneNAND device structure
250cacbe919SAmul Kumar Saha  * @block:		Block number on Flex-OneNAND
251cacbe919SAmul Kumar Saha  *
252cacbe919SAmul Kumar Saha  * Return address of the block
253cacbe919SAmul Kumar Saha  */
254cacbe919SAmul Kumar Saha static loff_t flexonenand_addr(struct onenand_chip *this, int block)
255cacbe919SAmul Kumar Saha {
256cacbe919SAmul Kumar Saha 	loff_t ofs = 0;
257cacbe919SAmul Kumar Saha 	int die = 0, boundary;
258cacbe919SAmul Kumar Saha 
259cacbe919SAmul Kumar Saha 	if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
260cacbe919SAmul Kumar Saha 		block -= this->density_mask;
261cacbe919SAmul Kumar Saha 		die = 1;
262cacbe919SAmul Kumar Saha 		ofs = this->diesize[0];
263cacbe919SAmul Kumar Saha 	}
264cacbe919SAmul Kumar Saha 
265cacbe919SAmul Kumar Saha 	boundary = this->boundary[die];
266cacbe919SAmul Kumar Saha 	ofs += (loff_t) block << (this->erase_shift - 1);
267cacbe919SAmul Kumar Saha 	if (block > (boundary + 1))
268cacbe919SAmul Kumar Saha 		ofs += (loff_t) (block - boundary - 1)
269cacbe919SAmul Kumar Saha 			<< (this->erase_shift - 1);
270cacbe919SAmul Kumar Saha 	return ofs;
271cacbe919SAmul Kumar Saha }
272cacbe919SAmul Kumar Saha 
273cacbe919SAmul Kumar Saha loff_t onenand_addr(struct onenand_chip *this, int block)
274cacbe919SAmul Kumar Saha {
275cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
276cacbe919SAmul Kumar Saha 		return (loff_t) block << this->erase_shift;
277cacbe919SAmul Kumar Saha 	return flexonenand_addr(this, block);
278cacbe919SAmul Kumar Saha }
279cacbe919SAmul Kumar Saha 
280cacbe919SAmul Kumar Saha /**
281cacbe919SAmul Kumar Saha  * flexonenand_region - [Flex-OneNAND] Return erase region of addr
282cacbe919SAmul Kumar Saha  * @param mtd		MTD device structure
283cacbe919SAmul Kumar Saha  * @param addr		address whose erase region needs to be identified
284cacbe919SAmul Kumar Saha  */
285cacbe919SAmul Kumar Saha int flexonenand_region(struct mtd_info *mtd, loff_t addr)
286cacbe919SAmul Kumar Saha {
287cacbe919SAmul Kumar Saha 	int i;
288cacbe919SAmul Kumar Saha 
289cacbe919SAmul Kumar Saha 	for (i = 0; i < mtd->numeraseregions; i++)
290cacbe919SAmul Kumar Saha 		if (addr < mtd->eraseregions[i].offset)
291cacbe919SAmul Kumar Saha 			break;
292cacbe919SAmul Kumar Saha 	return i - 1;
293cacbe919SAmul Kumar Saha }
294cacbe919SAmul Kumar Saha 
295cacbe919SAmul Kumar Saha /**
296ef0921d6SKyungmin Park  * onenand_get_density - [DEFAULT] Get OneNAND density
297ef0921d6SKyungmin Park  * @param dev_id        OneNAND device ID
298ef0921d6SKyungmin Park  *
299ef0921d6SKyungmin Park  * Get OneNAND density from device ID
300ef0921d6SKyungmin Park  */
301ef0921d6SKyungmin Park static inline int onenand_get_density(int dev_id)
302ef0921d6SKyungmin Park {
303ef0921d6SKyungmin Park 	int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
304ef0921d6SKyungmin Park 	return (density & ONENAND_DEVICE_DENSITY_MASK);
305ef0921d6SKyungmin Park }
306ef0921d6SKyungmin Park 
307ef0921d6SKyungmin Park /**
30859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_command - [DEFAULT] Send command to OneNAND device
30959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
31059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param cmd		the command to be sent
31159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		offset to read from or write to
31259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read or write
31359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
31459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Send command to OneNAND device. This function is used for middle/large page
31559829cc1SJean-Christophe PLAGNIOL-VILLARD  * devices (1KB/2KB Bytes per page)
31659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
31759829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
31859829cc1SJean-Christophe PLAGNIOL-VILLARD 			   size_t len)
31959829cc1SJean-Christophe PLAGNIOL-VILLARD {
32059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
321cacbe919SAmul Kumar Saha 	int value;
32259829cc1SJean-Christophe PLAGNIOL-VILLARD 	int block, page;
323cacbe919SAmul Kumar Saha 
32459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Now we use page size operation */
325cacbe919SAmul Kumar Saha 	int sectors = 0, count = 0;
32659829cc1SJean-Christophe PLAGNIOL-VILLARD 
32759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Address translation */
32859829cc1SJean-Christophe PLAGNIOL-VILLARD 	switch (cmd) {
32959829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_UNLOCK:
33059829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK:
33159829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK_TIGHT:
332ef0921d6SKyungmin Park 	case ONENAND_CMD_UNLOCK_ALL:
33359829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = -1;
33459829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
33559829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
33659829cc1SJean-Christophe PLAGNIOL-VILLARD 
337cacbe919SAmul Kumar Saha 	case FLEXONENAND_CMD_PI_ACCESS:
338cacbe919SAmul Kumar Saha 		/* addr contains die index */
339cacbe919SAmul Kumar Saha 		block = addr * this->density_mask;
34059829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
34159829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
34259829cc1SJean-Christophe PLAGNIOL-VILLARD 
343cacbe919SAmul Kumar Saha 	case ONENAND_CMD_ERASE:
344cacbe919SAmul Kumar Saha 	case ONENAND_CMD_BUFFERRAM:
345cacbe919SAmul Kumar Saha 		block = onenand_block(this, addr);
346cacbe919SAmul Kumar Saha 		page = -1;
347cacbe919SAmul Kumar Saha 		break;
348cacbe919SAmul Kumar Saha 
349cacbe919SAmul Kumar Saha 	case FLEXONENAND_CMD_READ_PI:
350cacbe919SAmul Kumar Saha 		cmd = ONENAND_CMD_READ;
351cacbe919SAmul Kumar Saha 		block = addr * this->density_mask;
352cacbe919SAmul Kumar Saha 		page = 0;
353cacbe919SAmul Kumar Saha 		break;
354cacbe919SAmul Kumar Saha 
35559829cc1SJean-Christophe PLAGNIOL-VILLARD 	default:
356cacbe919SAmul Kumar Saha 		block = onenand_block(this, addr);
357cacbe919SAmul Kumar Saha 		page = (int) (addr
358cacbe919SAmul Kumar Saha 			- onenand_addr(this, block)) >> this->page_shift;
35959829cc1SJean-Christophe PLAGNIOL-VILLARD 		page &= this->page_mask;
36059829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
36159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
36259829cc1SJean-Christophe PLAGNIOL-VILLARD 
36359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* NOTE: The setting order of the registers is very important! */
36459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (cmd == ONENAND_CMD_BUFFERRAM) {
36559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Select DataRAM for DDP */
366ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
36759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
36859829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS2);
36959829cc1SJean-Christophe PLAGNIOL-VILLARD 
370*e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this))
371cacbe919SAmul Kumar Saha 			ONENAND_SET_BUFFERRAM0(this);
372cacbe919SAmul Kumar Saha 		else
37359829cc1SJean-Christophe PLAGNIOL-VILLARD 			/* Switch to the next data buffer */
37459829cc1SJean-Christophe PLAGNIOL-VILLARD 			ONENAND_SET_NEXT_BUFFERRAM(this);
37559829cc1SJean-Christophe PLAGNIOL-VILLARD 
37659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
37759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
37859829cc1SJean-Christophe PLAGNIOL-VILLARD 
37959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (block != -1) {
38059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'DFS, FBA' of Flash */
381ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
38259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
38359829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS1);
384ef0921d6SKyungmin Park 
385cacbe919SAmul Kumar Saha 		/* Select DataRAM for DDP */
386ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
387ef0921d6SKyungmin Park 		this->write_word(value,
388ef0921d6SKyungmin Park 				 this->base + ONENAND_REG_START_ADDRESS2);
38959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
39059829cc1SJean-Christophe PLAGNIOL-VILLARD 
39159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (page != -1) {
39259829cc1SJean-Christophe PLAGNIOL-VILLARD 		int dataram;
39359829cc1SJean-Christophe PLAGNIOL-VILLARD 
39459829cc1SJean-Christophe PLAGNIOL-VILLARD 		switch (cmd) {
395cacbe919SAmul Kumar Saha 		case FLEXONENAND_CMD_RECOVER_LSB:
39659829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READ:
39759829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READOOB:
398*e26fd3d3SLukasz Majewski 			if (ONENAND_IS_4KB_PAGE(this))
399cacbe919SAmul Kumar Saha 				dataram = ONENAND_SET_BUFFERRAM0(this);
400cacbe919SAmul Kumar Saha 			else
40159829cc1SJean-Christophe PLAGNIOL-VILLARD 				dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
402cacbe919SAmul Kumar Saha 
40359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
40459829cc1SJean-Christophe PLAGNIOL-VILLARD 
40559829cc1SJean-Christophe PLAGNIOL-VILLARD 		default:
40659829cc1SJean-Christophe PLAGNIOL-VILLARD 			dataram = ONENAND_CURRENT_BUFFERRAM(this);
40759829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
40859829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
40959829cc1SJean-Christophe PLAGNIOL-VILLARD 
41059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'FPA, FSA' of Flash */
41159829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_page_address(page, sectors);
41259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
41359829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS8);
41459829cc1SJean-Christophe PLAGNIOL-VILLARD 
41559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'BSA, BSC' of DataRAM */
41659829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_buffer_address(dataram, sectors, count);
41759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
41859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
41959829cc1SJean-Christophe PLAGNIOL-VILLARD 
42059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Interrupt clear */
42159829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
42259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Write command */
42359829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
42459829cc1SJean-Christophe PLAGNIOL-VILLARD 
42559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
42659829cc1SJean-Christophe PLAGNIOL-VILLARD }
42759829cc1SJean-Christophe PLAGNIOL-VILLARD 
42859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
429cacbe919SAmul Kumar Saha  * onenand_read_ecc - return ecc status
430cacbe919SAmul Kumar Saha  * @param this		onenand chip structure
431cacbe919SAmul Kumar Saha  */
432cacbe919SAmul Kumar Saha static int onenand_read_ecc(struct onenand_chip *this)
433cacbe919SAmul Kumar Saha {
434cacbe919SAmul Kumar Saha 	int ecc, i;
435cacbe919SAmul Kumar Saha 
436cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
437cacbe919SAmul Kumar Saha 		return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
438cacbe919SAmul Kumar Saha 
439cacbe919SAmul Kumar Saha 	for (i = 0; i < 4; i++) {
440cacbe919SAmul Kumar Saha 		ecc = this->read_word(this->base
441cacbe919SAmul Kumar Saha 				+ ((ONENAND_REG_ECC_STATUS + i) << 1));
442cacbe919SAmul Kumar Saha 		if (likely(!ecc))
443cacbe919SAmul Kumar Saha 			continue;
444cacbe919SAmul Kumar Saha 		if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
445cacbe919SAmul Kumar Saha 			return ONENAND_ECC_2BIT_ALL;
446cacbe919SAmul Kumar Saha 	}
447cacbe919SAmul Kumar Saha 
448cacbe919SAmul Kumar Saha 	return 0;
449cacbe919SAmul Kumar Saha }
450cacbe919SAmul Kumar Saha 
451cacbe919SAmul Kumar Saha /**
45259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_wait - [DEFAULT] wait until the command is done
45359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
45459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param state		state to select the max. timeout value
45559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
45659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Wait for command done. This applies to all OneNAND command
45759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read can take up to 30us, erase up to 2ms and program up to 350us
45859829cc1SJean-Christophe PLAGNIOL-VILLARD  * according to general OneNAND specs
45959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
46059829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_wait(struct mtd_info *mtd, int state)
46159829cc1SJean-Christophe PLAGNIOL-VILLARD {
46259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
46359829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int flags = ONENAND_INT_MASTER;
46459829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int interrupt = 0;
465cacbe919SAmul Kumar Saha 	unsigned int ctrl;
46659829cc1SJean-Christophe PLAGNIOL-VILLARD 
46759829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (1) {
46859829cc1SJean-Christophe PLAGNIOL-VILLARD 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
46959829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (interrupt & flags)
47059829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
47159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
47259829cc1SJean-Christophe PLAGNIOL-VILLARD 
47359829cc1SJean-Christophe PLAGNIOL-VILLARD 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
47459829cc1SJean-Christophe PLAGNIOL-VILLARD 
475cacbe919SAmul Kumar Saha 	if (interrupt & ONENAND_INT_READ) {
476cacbe919SAmul Kumar Saha 		int ecc = onenand_read_ecc(this);
477cacbe919SAmul Kumar Saha 		if (ecc & ONENAND_ECC_2BIT_ALL) {
478cacbe919SAmul Kumar Saha 			printk("onenand_wait: ECC error = 0x%04x\n", ecc);
479cacbe919SAmul Kumar Saha 			return -EBADMSG;
480cacbe919SAmul Kumar Saha 		}
481cacbe919SAmul Kumar Saha 	}
482cacbe919SAmul Kumar Saha 
48359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ctrl & ONENAND_CTRL_ERROR) {
484ef0921d6SKyungmin Park 		printk("onenand_wait: controller error = 0x%04x\n", ctrl);
485ef0921d6SKyungmin Park 		if (ctrl & ONENAND_CTRL_LOCK)
486ef0921d6SKyungmin Park 			printk("onenand_wait: it's locked error = 0x%04x\n",
487ef0921d6SKyungmin Park 				ctrl);
48859829cc1SJean-Christophe PLAGNIOL-VILLARD 
48959829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EIO;
49059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
49159829cc1SJean-Christophe PLAGNIOL-VILLARD 
49259829cc1SJean-Christophe PLAGNIOL-VILLARD 
49359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
49459829cc1SJean-Christophe PLAGNIOL-VILLARD }
49559829cc1SJean-Christophe PLAGNIOL-VILLARD 
49659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
49759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
49859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
49959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
50059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		offset given area
50159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
50259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Return BufferRAM offset given area
50359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
50459829cc1SJean-Christophe PLAGNIOL-VILLARD static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
50559829cc1SJean-Christophe PLAGNIOL-VILLARD {
50659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
50759829cc1SJean-Christophe PLAGNIOL-VILLARD 
50859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ONENAND_CURRENT_BUFFERRAM(this)) {
50959829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_DATARAM)
510d438d508SKyungmin Park 			return mtd->writesize;
51159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_SPARERAM)
51259829cc1SJean-Christophe PLAGNIOL-VILLARD 			return mtd->oobsize;
51359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
51459829cc1SJean-Christophe PLAGNIOL-VILLARD 
51559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
51659829cc1SJean-Christophe PLAGNIOL-VILLARD }
51759829cc1SJean-Christophe PLAGNIOL-VILLARD 
51859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
51959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
52059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
52159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
52259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
52359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
52459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
52559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
52659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area
52759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
528ef0921d6SKyungmin Park static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
52959829cc1SJean-Christophe PLAGNIOL-VILLARD 				  unsigned char *buffer, int offset,
53059829cc1SJean-Christophe PLAGNIOL-VILLARD 				  size_t count)
53159829cc1SJean-Christophe PLAGNIOL-VILLARD {
53259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
53359829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
53459829cc1SJean-Christophe PLAGNIOL-VILLARD 
53559829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
53659829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
53759829cc1SJean-Christophe PLAGNIOL-VILLARD 
538d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
53959829cc1SJean-Christophe PLAGNIOL-VILLARD 
54059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
54159829cc1SJean-Christophe PLAGNIOL-VILLARD }
54259829cc1SJean-Christophe PLAGNIOL-VILLARD 
54359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
54459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
54559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
54659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
54759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
54859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
54959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
55059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
55159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area with Sync. Burst Mode
55259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
553ef0921d6SKyungmin Park static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
55459829cc1SJean-Christophe PLAGNIOL-VILLARD 				       unsigned char *buffer, int offset,
55559829cc1SJean-Christophe PLAGNIOL-VILLARD 				       size_t count)
55659829cc1SJean-Christophe PLAGNIOL-VILLARD {
55759829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
55859829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
55959829cc1SJean-Christophe PLAGNIOL-VILLARD 
56059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
56159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
56259829cc1SJean-Christophe PLAGNIOL-VILLARD 
56359829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
56459829cc1SJean-Christophe PLAGNIOL-VILLARD 
565d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
56659829cc1SJean-Christophe PLAGNIOL-VILLARD 
56759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, 0);
56859829cc1SJean-Christophe PLAGNIOL-VILLARD 
56959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
57059829cc1SJean-Christophe PLAGNIOL-VILLARD }
57159829cc1SJean-Christophe PLAGNIOL-VILLARD 
57259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
57359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
57459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
57559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
57659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
57759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
57859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
57959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
58059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write the BufferRAM area
58159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
582ef0921d6SKyungmin Park static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area,
58359829cc1SJean-Christophe PLAGNIOL-VILLARD 				   const unsigned char *buffer, int offset,
58459829cc1SJean-Christophe PLAGNIOL-VILLARD 				   size_t count)
58559829cc1SJean-Christophe PLAGNIOL-VILLARD {
58659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
58759829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
58859829cc1SJean-Christophe PLAGNIOL-VILLARD 
58959829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
59059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
59159829cc1SJean-Christophe PLAGNIOL-VILLARD 
592d2c6fbecSWolfgang Denk 	memcpy_16(bufferram + offset, buffer, count);
59359829cc1SJean-Christophe PLAGNIOL-VILLARD 
59459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
59559829cc1SJean-Christophe PLAGNIOL-VILLARD }
59659829cc1SJean-Christophe PLAGNIOL-VILLARD 
59759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
5984fca3310SStefan Roese  * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
5994fca3310SStefan Roese  * @param mtd		MTD data structure
6004fca3310SStefan Roese  * @param addr		address to check
6014fca3310SStefan Roese  * @return		blockpage address
6024fca3310SStefan Roese  *
6034fca3310SStefan Roese  * Get blockpage address at 2x program mode
6044fca3310SStefan Roese  */
6054fca3310SStefan Roese static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
6064fca3310SStefan Roese {
6074fca3310SStefan Roese 	struct onenand_chip *this = mtd->priv;
6084fca3310SStefan Roese 	int blockpage, block, page;
6094fca3310SStefan Roese 
6104fca3310SStefan Roese 	/* Calculate the even block number */
6114fca3310SStefan Roese 	block = (int) (addr >> this->erase_shift) & ~1;
6124fca3310SStefan Roese 	/* Is it the odd plane? */
6134fca3310SStefan Roese 	if (addr & this->writesize)
6144fca3310SStefan Roese 		block++;
6154fca3310SStefan Roese 	page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
6164fca3310SStefan Roese 	blockpage = (block << 7) | page;
6174fca3310SStefan Roese 
6184fca3310SStefan Roese 	return blockpage;
6194fca3310SStefan Roese }
6204fca3310SStefan Roese 
6214fca3310SStefan Roese /**
62259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_bufferram - [GENERIC] Check BufferRAM information
62359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
62459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to check
62559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		1 if there are valid data, otherwise 0
62659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
62759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check bufferram if there is data we required
62859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
62959829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
63059829cc1SJean-Christophe PLAGNIOL-VILLARD {
63159829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
632ef0921d6SKyungmin Park 	int blockpage, found = 0;
633ef0921d6SKyungmin Park 	unsigned int i;
63459829cc1SJean-Christophe PLAGNIOL-VILLARD 
635ef0921d6SKyungmin Park #ifdef CONFIG_S3C64XX
636ef0921d6SKyungmin Park 	return 0;
637ef0921d6SKyungmin Park #endif
63859829cc1SJean-Christophe PLAGNIOL-VILLARD 
639ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
640ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
641ef0921d6SKyungmin Park 	else
642ef0921d6SKyungmin Park 		blockpage = (int) (addr >> this->page_shift);
64359829cc1SJean-Christophe PLAGNIOL-VILLARD 
64459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Is there valid data? */
645ef0921d6SKyungmin Park 	i = ONENAND_CURRENT_BUFFERRAM(this);
646ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
647ef0921d6SKyungmin Park 		found = 1;
648ef0921d6SKyungmin Park 	else {
649ef0921d6SKyungmin Park 		/* Check another BufferRAM */
650ef0921d6SKyungmin Park 		i = ONENAND_NEXT_BUFFERRAM(this);
651ef0921d6SKyungmin Park 		if (this->bufferram[i].blockpage == blockpage) {
652ef0921d6SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
653ef0921d6SKyungmin Park 			found = 1;
654ef0921d6SKyungmin Park 		}
655ef0921d6SKyungmin Park 	}
65659829cc1SJean-Christophe PLAGNIOL-VILLARD 
657ef0921d6SKyungmin Park 	if (found && ONENAND_IS_DDP(this)) {
658ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
659cacbe919SAmul Kumar Saha 		int block = onenand_block(this, addr);
660ef0921d6SKyungmin Park 		int value = onenand_bufferram_address(this, block);
661ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
662ef0921d6SKyungmin Park 	}
663ef0921d6SKyungmin Park 
664ef0921d6SKyungmin Park 	return found;
66559829cc1SJean-Christophe PLAGNIOL-VILLARD }
66659829cc1SJean-Christophe PLAGNIOL-VILLARD 
66759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
66859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_update_bufferram - [GENERIC] Update BufferRAM information
66959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
67059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to update
67159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param valid		valid flag
67259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
67359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Update BufferRAM information
67459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
67559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
67659829cc1SJean-Christophe PLAGNIOL-VILLARD 				    int valid)
67759829cc1SJean-Christophe PLAGNIOL-VILLARD {
67859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
679ef0921d6SKyungmin Park 	int blockpage;
680ef0921d6SKyungmin Park 	unsigned int i;
68159829cc1SJean-Christophe PLAGNIOL-VILLARD 
682ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
683ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
684ef0921d6SKyungmin Park 	else
685ef0921d6SKyungmin Park 		blockpage = (int)(addr >> this->page_shift);
68659829cc1SJean-Christophe PLAGNIOL-VILLARD 
687ef0921d6SKyungmin Park 	/* Invalidate another BufferRAM */
688ef0921d6SKyungmin Park 	i = ONENAND_NEXT_BUFFERRAM(this);
689ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
690ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
69159829cc1SJean-Christophe PLAGNIOL-VILLARD 
69259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Update BufferRAM */
69359829cc1SJean-Christophe PLAGNIOL-VILLARD 	i = ONENAND_CURRENT_BUFFERRAM(this);
694ef0921d6SKyungmin Park 	if (valid)
695ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = blockpage;
696ef0921d6SKyungmin Park 	else
697ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
69859829cc1SJean-Christophe PLAGNIOL-VILLARD 
69959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
70059829cc1SJean-Christophe PLAGNIOL-VILLARD }
70159829cc1SJean-Christophe PLAGNIOL-VILLARD 
70259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
703d438d508SKyungmin Park  * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
704d438d508SKyungmin Park  * @param mtd           MTD data structure
705d438d508SKyungmin Park  * @param addr          start address to invalidate
706d438d508SKyungmin Park  * @param len           length to invalidate
707d438d508SKyungmin Park  *
708d438d508SKyungmin Park  * Invalidate BufferRAM information
709d438d508SKyungmin Park  */
710d438d508SKyungmin Park static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
711d438d508SKyungmin Park 					 unsigned int len)
712d438d508SKyungmin Park {
713d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
714d438d508SKyungmin Park 	int i;
715d438d508SKyungmin Park 	loff_t end_addr = addr + len;
716d438d508SKyungmin Park 
717d438d508SKyungmin Park 	/* Invalidate BufferRAM */
718d438d508SKyungmin Park 	for (i = 0; i < MAX_BUFFERRAM; i++) {
719ef0921d6SKyungmin Park 		loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
720d438d508SKyungmin Park 
721d438d508SKyungmin Park 		if (buf_addr >= addr && buf_addr < end_addr)
722ef0921d6SKyungmin Park 			this->bufferram[i].blockpage = -1;
723d438d508SKyungmin Park 	}
724d438d508SKyungmin Park }
725d438d508SKyungmin Park 
726d438d508SKyungmin Park /**
72759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_get_device - [GENERIC] Get chip for selected access
72859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
72959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param new_state	the state which is requested
73059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
73159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Get the device and lock it for exclusive access
73259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
73359829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_get_device(struct mtd_info *mtd, int new_state)
73459829cc1SJean-Christophe PLAGNIOL-VILLARD {
73559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
73659829cc1SJean-Christophe PLAGNIOL-VILLARD }
73759829cc1SJean-Christophe PLAGNIOL-VILLARD 
73859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
73959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release_device - [GENERIC] release chip
74059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
74159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
74259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Deselect, release chip lock and wake up anyone waiting on the device
74359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
74459829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_release_device(struct mtd_info *mtd)
74559829cc1SJean-Christophe PLAGNIOL-VILLARD {
74659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
74759829cc1SJean-Christophe PLAGNIOL-VILLARD }
74859829cc1SJean-Christophe PLAGNIOL-VILLARD 
74959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
750bfd7f386SKyungmin Park  * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
75159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
752bfd7f386SKyungmin Park  * @param buf		destination address
753bfd7f386SKyungmin Park  * @param column	oob offset to read from
754bfd7f386SKyungmin Park  * @param thislen	oob length to read
75559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
756bfd7f386SKyungmin Park static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
757bfd7f386SKyungmin Park 					int column, int thislen)
75859829cc1SJean-Christophe PLAGNIOL-VILLARD {
75959829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
760bfd7f386SKyungmin Park 	struct nand_oobfree *free;
761bfd7f386SKyungmin Park 	int readcol = column;
762bfd7f386SKyungmin Park 	int readend = column + thislen;
763bfd7f386SKyungmin Park 	int lastgap = 0;
764bfd7f386SKyungmin Park 	unsigned int i;
765bfd7f386SKyungmin Park 	uint8_t *oob_buf = this->oob_buf;
76659829cc1SJean-Christophe PLAGNIOL-VILLARD 
767bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
768bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
769bfd7f386SKyungmin Park 		if (readcol >= lastgap)
770bfd7f386SKyungmin Park 			readcol += free->offset - lastgap;
771bfd7f386SKyungmin Park 		if (readend >= lastgap)
772bfd7f386SKyungmin Park 			readend += free->offset - lastgap;
773bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
774bfd7f386SKyungmin Park 	}
775ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
776bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
777bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
778bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
779bfd7f386SKyungmin Park 		if (free->offset < readend && free_end > readcol) {
780bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,readcol);
781bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,readend);
782bfd7f386SKyungmin Park 			int n = ed - st;
783bfd7f386SKyungmin Park 			memcpy(buf, oob_buf + st, n);
784bfd7f386SKyungmin Park 			buf += n;
785bfd7f386SKyungmin Park 		} else if (column == 0)
786bfd7f386SKyungmin Park 			break;
787bfd7f386SKyungmin Park 	}
788bfd7f386SKyungmin Park 	return 0;
789bfd7f386SKyungmin Park }
790bfd7f386SKyungmin Park 
791bfd7f386SKyungmin Park /**
792cacbe919SAmul Kumar Saha  * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
793cacbe919SAmul Kumar Saha  * @param mtd		MTD device structure
794cacbe919SAmul Kumar Saha  * @param addr		address to recover
795cacbe919SAmul Kumar Saha  * @param status	return value from onenand_wait
796cacbe919SAmul Kumar Saha  *
797cacbe919SAmul Kumar Saha  * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
798cacbe919SAmul Kumar Saha  * lower page address and MSB page has higher page address in paired pages.
799cacbe919SAmul Kumar Saha  * If power off occurs during MSB page program, the paired LSB page data can
800cacbe919SAmul Kumar Saha  * become corrupt. LSB page recovery read is a way to read LSB page though page
801cacbe919SAmul Kumar Saha  * data are corrupted. When uncorrectable error occurs as a result of LSB page
802cacbe919SAmul Kumar Saha  * read after power up, issue LSB page recovery read.
803cacbe919SAmul Kumar Saha  */
804cacbe919SAmul Kumar Saha static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
805cacbe919SAmul Kumar Saha {
806cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
807cacbe919SAmul Kumar Saha 	int i;
808cacbe919SAmul Kumar Saha 
809cacbe919SAmul Kumar Saha 	/* Recovery is only for Flex-OneNAND */
810cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
811cacbe919SAmul Kumar Saha 		return status;
812cacbe919SAmul Kumar Saha 
813cacbe919SAmul Kumar Saha 	/* check if we failed due to uncorrectable error */
814cacbe919SAmul Kumar Saha 	if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR)
815cacbe919SAmul Kumar Saha 		return status;
816cacbe919SAmul Kumar Saha 
817cacbe919SAmul Kumar Saha 	/* check if address lies in MLC region */
818cacbe919SAmul Kumar Saha 	i = flexonenand_region(mtd, addr);
819cacbe919SAmul Kumar Saha 	if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
820cacbe919SAmul Kumar Saha 		return status;
821cacbe919SAmul Kumar Saha 
822cacbe919SAmul Kumar Saha 	printk("onenand_recover_lsb:"
823cacbe919SAmul Kumar Saha 		"Attempting to recover from uncorrectable read\n");
824cacbe919SAmul Kumar Saha 
825cacbe919SAmul Kumar Saha 	/* Issue the LSB page recovery command */
826cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
827cacbe919SAmul Kumar Saha 	return this->wait(mtd, FL_READING);
828cacbe919SAmul Kumar Saha }
829cacbe919SAmul Kumar Saha 
830cacbe919SAmul Kumar Saha /**
831bfd7f386SKyungmin Park  * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
832bfd7f386SKyungmin Park  * @param mtd		MTD device structure
833bfd7f386SKyungmin Park  * @param from		offset to read from
834bfd7f386SKyungmin Park  * @param ops		oob operation description structure
835bfd7f386SKyungmin Park  *
836bfd7f386SKyungmin Park  * OneNAND read main and/or out-of-band data
837bfd7f386SKyungmin Park  */
838bfd7f386SKyungmin Park static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
839bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
840bfd7f386SKyungmin Park {
841bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
842bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
843bfd7f386SKyungmin Park 	size_t len = ops->len;
844bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
845bfd7f386SKyungmin Park 	u_char *buf = ops->datbuf;
846bfd7f386SKyungmin Park 	u_char *oobbuf = ops->oobbuf;
847bfd7f386SKyungmin Park 	int read = 0, column, thislen;
848bfd7f386SKyungmin Park 	int oobread = 0, oobcolumn, thisooblen, oobsize;
849bfd7f386SKyungmin Park 	int ret = 0, boundary = 0;
850bfd7f386SKyungmin Park 	int writesize = this->writesize;
851bfd7f386SKyungmin Park 
852ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
85359829cc1SJean-Christophe PLAGNIOL-VILLARD 
854bfd7f386SKyungmin Park 	if (ops->mode == MTD_OOB_AUTO)
855bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
856bfd7f386SKyungmin Park 	else
857bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
858bfd7f386SKyungmin Park 
859bfd7f386SKyungmin Park 	oobcolumn = from & (mtd->oobsize - 1);
860bfd7f386SKyungmin Park 
86159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
86259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((from + len) > mtd->size) {
863bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n");
864bfd7f386SKyungmin Park 		ops->retlen = 0;
865bfd7f386SKyungmin Park 		ops->oobretlen = 0;
86659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
86759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
86859829cc1SJean-Christophe PLAGNIOL-VILLARD 
869bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
87059829cc1SJean-Christophe PLAGNIOL-VILLARD 
871bfd7f386SKyungmin Park 	/* Read-while-load method */
872cacbe919SAmul Kumar Saha 	/* Note: We can't use this feature in MLC */
87359829cc1SJean-Christophe PLAGNIOL-VILLARD 
874bfd7f386SKyungmin Park 	/* Do first load to bufferRAM */
875bfd7f386SKyungmin Park 	if (read < len) {
87659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!onenand_check_bufferram(mtd, from)) {
877ef0921d6SKyungmin Park 			this->main_buf = buf;
878bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
87959829cc1SJean-Christophe PLAGNIOL-VILLARD 			ret = this->wait(mtd, FL_READING);
880cacbe919SAmul Kumar Saha 			if (unlikely(ret))
881cacbe919SAmul Kumar Saha 				ret = onenand_recover_lsb(mtd, from, ret);
882bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
883bfd7f386SKyungmin Park 			if (ret == -EBADMSG)
884bfd7f386SKyungmin Park 				ret = 0;
885bfd7f386SKyungmin Park 		}
88659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
88759829cc1SJean-Christophe PLAGNIOL-VILLARD 
888bfd7f386SKyungmin Park 	thislen = min_t(int, writesize, len - read);
889bfd7f386SKyungmin Park 	column = from & (writesize - 1);
890bfd7f386SKyungmin Park 	if (column + thislen > writesize)
891bfd7f386SKyungmin Park 		thislen = writesize - column;
89259829cc1SJean-Christophe PLAGNIOL-VILLARD 
893bfd7f386SKyungmin Park 	while (!ret) {
894bfd7f386SKyungmin Park 		/* If there is more to load then start next load */
895bfd7f386SKyungmin Park 		from += thislen;
896*e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this) && read + thislen < len) {
897ef0921d6SKyungmin Park 			this->main_buf = buf + thislen;
898bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
899bfd7f386SKyungmin Park 			/*
900bfd7f386SKyungmin Park 			 * Chip boundary handling in DDP
901bfd7f386SKyungmin Park 			 * Now we issued chip 1 read and pointed chip 1
902bfd7f386SKyungmin Park 			 * bufferam so we have to point chip 0 bufferam.
903bfd7f386SKyungmin Park 			 */
904bfd7f386SKyungmin Park 			if (ONENAND_IS_DDP(this) &&
905bfd7f386SKyungmin Park 					unlikely(from == (this->chipsize >> 1))) {
906bfd7f386SKyungmin Park 				this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
907bfd7f386SKyungmin Park 				boundary = 1;
908bfd7f386SKyungmin Park 			} else
909bfd7f386SKyungmin Park 				boundary = 0;
910bfd7f386SKyungmin Park 			ONENAND_SET_PREV_BUFFERRAM(this);
911bfd7f386SKyungmin Park 		}
912bfd7f386SKyungmin Park 
913bfd7f386SKyungmin Park 		/* While load is going, read from last bufferRAM */
914ef0921d6SKyungmin Park 		this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen);
915bfd7f386SKyungmin Park 
916bfd7f386SKyungmin Park 		/* Read oob area if needed */
917bfd7f386SKyungmin Park 		if (oobbuf) {
918bfd7f386SKyungmin Park 			thisooblen = oobsize - oobcolumn;
919bfd7f386SKyungmin Park 			thisooblen = min_t(int, thisooblen, ooblen - oobread);
920bfd7f386SKyungmin Park 
921bfd7f386SKyungmin Park 			if (ops->mode == MTD_OOB_AUTO)
922bfd7f386SKyungmin Park 				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
923bfd7f386SKyungmin Park 			else
924ef0921d6SKyungmin Park 				this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
925bfd7f386SKyungmin Park 			oobread += thisooblen;
926bfd7f386SKyungmin Park 			oobbuf += thisooblen;
927bfd7f386SKyungmin Park 			oobcolumn = 0;
928bfd7f386SKyungmin Park 		}
929bfd7f386SKyungmin Park 
930*e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this) && (read + thislen < len)) {
931cacbe919SAmul Kumar Saha 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
932cacbe919SAmul Kumar Saha 			ret = this->wait(mtd, FL_READING);
933cacbe919SAmul Kumar Saha 			if (unlikely(ret))
934cacbe919SAmul Kumar Saha 				ret = onenand_recover_lsb(mtd, from, ret);
935cacbe919SAmul Kumar Saha 			onenand_update_bufferram(mtd, from, !ret);
936cacbe919SAmul Kumar Saha 			if (ret == -EBADMSG)
937cacbe919SAmul Kumar Saha 				ret = 0;
938cacbe919SAmul Kumar Saha 		}
939cacbe919SAmul Kumar Saha 
940bfd7f386SKyungmin Park 		/* See if we are done */
94159829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
94259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
94359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
944bfd7f386SKyungmin Park 		/* Set up for next read from bufferRAM */
945bfd7f386SKyungmin Park 		if (unlikely(boundary))
946bfd7f386SKyungmin Park 			this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
947*e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this))
948bfd7f386SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
94959829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
950bfd7f386SKyungmin Park 		thislen = min_t(int, writesize, len - read);
951bfd7f386SKyungmin Park 		column = 0;
95259829cc1SJean-Christophe PLAGNIOL-VILLARD 
953*e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this)) {
954bfd7f386SKyungmin Park 			/* Now wait for load */
955bfd7f386SKyungmin Park 			ret = this->wait(mtd, FL_READING);
956bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
957bfd7f386SKyungmin Park 			if (ret == -EBADMSG)
958bfd7f386SKyungmin Park 				ret = 0;
959bfd7f386SKyungmin Park 		}
960cacbe919SAmul Kumar Saha 	}
96159829cc1SJean-Christophe PLAGNIOL-VILLARD 
96259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/*
96359829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * Return success, if no ECC failures, else -EBADMSG
96459829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * fs driver will take care of that, because
96559829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * retlen == desired len and result == -EBADMSG
96659829cc1SJean-Christophe PLAGNIOL-VILLARD 	 */
967bfd7f386SKyungmin Park 	ops->retlen = read;
968bfd7f386SKyungmin Park 	ops->oobretlen = oobread;
969bfd7f386SKyungmin Park 
970bfd7f386SKyungmin Park 	if (ret)
97159829cc1SJean-Christophe PLAGNIOL-VILLARD 		return ret;
972bfd7f386SKyungmin Park 
973bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
974bfd7f386SKyungmin Park 		return -EBADMSG;
975bfd7f386SKyungmin Park 
976bfd7f386SKyungmin Park 	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
977bfd7f386SKyungmin Park }
978bfd7f386SKyungmin Park 
979bfd7f386SKyungmin Park /**
980bfd7f386SKyungmin Park  * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
981bfd7f386SKyungmin Park  * @param mtd		MTD device structure
982bfd7f386SKyungmin Park  * @param from		offset to read from
983bfd7f386SKyungmin Park  * @param ops		oob operation description structure
984bfd7f386SKyungmin Park  *
985bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area
986bfd7f386SKyungmin Park  */
987bfd7f386SKyungmin Park static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
988bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
989bfd7f386SKyungmin Park {
990bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
991bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
992bfd7f386SKyungmin Park 	int read = 0, thislen, column, oobsize;
993bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
994bfd7f386SKyungmin Park 	mtd_oob_mode_t mode = ops->mode;
995bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
996cacbe919SAmul Kumar Saha 	int ret = 0, readcmd;
997bfd7f386SKyungmin Park 
998bfd7f386SKyungmin Park 	from += ops->ooboffs;
999bfd7f386SKyungmin Park 
1000ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
1001bfd7f386SKyungmin Park 
1002bfd7f386SKyungmin Park 	/* Initialize return length value */
1003bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1004bfd7f386SKyungmin Park 
1005bfd7f386SKyungmin Park 	if (mode == MTD_OOB_AUTO)
1006bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1007bfd7f386SKyungmin Park 	else
1008bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1009bfd7f386SKyungmin Park 
1010bfd7f386SKyungmin Park 	column = from & (mtd->oobsize - 1);
1011bfd7f386SKyungmin Park 
1012bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1013bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
1014bfd7f386SKyungmin Park 		return -EINVAL;
1015bfd7f386SKyungmin Park 	}
1016bfd7f386SKyungmin Park 
1017bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1018bfd7f386SKyungmin Park 	if (unlikely(from >= mtd->size ||
1019bfd7f386SKyungmin Park 		column + len > ((mtd->size >> this->page_shift) -
1020bfd7f386SKyungmin Park 				(from >> this->page_shift)) * oobsize)) {
1021bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n");
1022bfd7f386SKyungmin Park 		return -EINVAL;
1023bfd7f386SKyungmin Park 	}
1024bfd7f386SKyungmin Park 
1025bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
1026bfd7f386SKyungmin Park 
1027*e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1028*e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1029cacbe919SAmul Kumar Saha 
1030bfd7f386SKyungmin Park 	while (read < len) {
1031bfd7f386SKyungmin Park 		thislen = oobsize - column;
1032bfd7f386SKyungmin Park 		thislen = min_t(int, thislen, len);
1033bfd7f386SKyungmin Park 
1034ef0921d6SKyungmin Park 		this->spare_buf = buf;
1035cacbe919SAmul Kumar Saha 		this->command(mtd, readcmd, from, mtd->oobsize);
1036bfd7f386SKyungmin Park 
1037bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, from, 0);
1038bfd7f386SKyungmin Park 
1039bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_READING);
1040cacbe919SAmul Kumar Saha 		if (unlikely(ret))
1041cacbe919SAmul Kumar Saha 			ret = onenand_recover_lsb(mtd, from, ret);
1042cacbe919SAmul Kumar Saha 
1043bfd7f386SKyungmin Park 		if (ret && ret != -EBADMSG) {
1044bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
1045bfd7f386SKyungmin Park 			break;
1046bfd7f386SKyungmin Park 		}
1047bfd7f386SKyungmin Park 
1048bfd7f386SKyungmin Park 		if (mode == MTD_OOB_AUTO)
1049bfd7f386SKyungmin Park 			onenand_transfer_auto_oob(mtd, buf, column, thislen);
1050bfd7f386SKyungmin Park 		else
1051ef0921d6SKyungmin Park 			this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
1052bfd7f386SKyungmin Park 
1053bfd7f386SKyungmin Park 		read += thislen;
1054bfd7f386SKyungmin Park 
1055bfd7f386SKyungmin Park 		if (read == len)
1056bfd7f386SKyungmin Park 			break;
1057bfd7f386SKyungmin Park 
1058bfd7f386SKyungmin Park 		buf += thislen;
1059bfd7f386SKyungmin Park 
1060bfd7f386SKyungmin Park 		/* Read more? */
1061bfd7f386SKyungmin Park 		if (read < len) {
1062bfd7f386SKyungmin Park 			/* Page size */
1063bfd7f386SKyungmin Park 			from += mtd->writesize;
1064bfd7f386SKyungmin Park 			column = 0;
1065bfd7f386SKyungmin Park 		}
1066bfd7f386SKyungmin Park 	}
1067bfd7f386SKyungmin Park 
1068bfd7f386SKyungmin Park 	ops->oobretlen = read;
1069bfd7f386SKyungmin Park 
1070bfd7f386SKyungmin Park 	if (ret)
1071bfd7f386SKyungmin Park 		return ret;
1072bfd7f386SKyungmin Park 
1073bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
1074bfd7f386SKyungmin Park 		return -EBADMSG;
1075bfd7f386SKyungmin Park 
1076bfd7f386SKyungmin Park 	return 0;
107759829cc1SJean-Christophe PLAGNIOL-VILLARD }
107859829cc1SJean-Christophe PLAGNIOL-VILLARD 
107959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
108059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc
108159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
108259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
108359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read
108459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of read bytes
108559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the databuffer to put data
108659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
108759829cc1SJean-Christophe PLAGNIOL-VILLARD  * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
108859829cc1SJean-Christophe PLAGNIOL-VILLARD */
108959829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
109059829cc1SJean-Christophe PLAGNIOL-VILLARD 		 size_t * retlen, u_char * buf)
109159829cc1SJean-Christophe PLAGNIOL-VILLARD {
1092bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1093bfd7f386SKyungmin Park 		.len    = len,
1094bfd7f386SKyungmin Park 		.ooblen = 0,
1095bfd7f386SKyungmin Park 		.datbuf = buf,
1096bfd7f386SKyungmin Park 		.oobbuf = NULL,
1097bfd7f386SKyungmin Park 	};
1098bfd7f386SKyungmin Park 	int ret;
1099bfd7f386SKyungmin Park 
1100bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1101bfd7f386SKyungmin Park 	ret = onenand_read_ops_nolock(mtd, from, &ops);
1102bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1103bfd7f386SKyungmin Park 
1104bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1105bfd7f386SKyungmin Park 	return ret;
110659829cc1SJean-Christophe PLAGNIOL-VILLARD }
110759829cc1SJean-Christophe PLAGNIOL-VILLARD 
110859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
110959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
111059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
111159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
1112bfd7f386SKyungmin Park  * @param ops		oob operations description structure
111359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1114bfd7f386SKyungmin Park  * OneNAND main and/or out-of-band
111559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1116bfd7f386SKyungmin Park int onenand_read_oob(struct mtd_info *mtd, loff_t from,
1117bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
1118bfd7f386SKyungmin Park {
1119bfd7f386SKyungmin Park 	int ret;
1120bfd7f386SKyungmin Park 
1121bfd7f386SKyungmin Park 	switch (ops->mode) {
1122bfd7f386SKyungmin Park 	case MTD_OOB_PLACE:
1123bfd7f386SKyungmin Park 	case MTD_OOB_AUTO:
1124bfd7f386SKyungmin Park 		break;
1125bfd7f386SKyungmin Park 	case MTD_OOB_RAW:
1126bfd7f386SKyungmin Park 		/* Not implemented yet */
1127bfd7f386SKyungmin Park 	default:
1128bfd7f386SKyungmin Park 		return -EINVAL;
1129bfd7f386SKyungmin Park 	}
1130bfd7f386SKyungmin Park 
1131bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1132bfd7f386SKyungmin Park 	if (ops->datbuf)
1133bfd7f386SKyungmin Park 		ret = onenand_read_ops_nolock(mtd, from, ops);
1134bfd7f386SKyungmin Park 	else
1135bfd7f386SKyungmin Park 		ret = onenand_read_oob_nolock(mtd, from, ops);
1136bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1137bfd7f386SKyungmin Park 
1138bfd7f386SKyungmin Park 	return ret;
1139bfd7f386SKyungmin Park }
1140bfd7f386SKyungmin Park 
1141bfd7f386SKyungmin Park /**
1142bfd7f386SKyungmin Park  * onenand_bbt_wait - [DEFAULT] wait until the command is done
1143bfd7f386SKyungmin Park  * @param mtd		MTD device structure
1144bfd7f386SKyungmin Park  * @param state		state to select the max. timeout value
1145bfd7f386SKyungmin Park  *
1146bfd7f386SKyungmin Park  * Wait for command done.
1147bfd7f386SKyungmin Park  */
1148bfd7f386SKyungmin Park static int onenand_bbt_wait(struct mtd_info *mtd, int state)
1149bfd7f386SKyungmin Park {
1150bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1151bfd7f386SKyungmin Park 	unsigned int flags = ONENAND_INT_MASTER;
1152bfd7f386SKyungmin Park 	unsigned int interrupt;
1153bfd7f386SKyungmin Park 	unsigned int ctrl;
1154bfd7f386SKyungmin Park 
1155bfd7f386SKyungmin Park 	while (1) {
1156bfd7f386SKyungmin Park 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
1157bfd7f386SKyungmin Park 		if (interrupt & flags)
1158bfd7f386SKyungmin Park 			break;
1159bfd7f386SKyungmin Park 	}
1160bfd7f386SKyungmin Park 
1161bfd7f386SKyungmin Park 	/* To get correct interrupt status in timeout case */
1162bfd7f386SKyungmin Park 	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
1163bfd7f386SKyungmin Park 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
1164bfd7f386SKyungmin Park 
1165bfd7f386SKyungmin Park 	if (interrupt & ONENAND_INT_READ) {
1166cacbe919SAmul Kumar Saha 		int ecc = onenand_read_ecc(this);
1167cacbe919SAmul Kumar Saha 		if (ecc & ONENAND_ECC_2BIT_ALL) {
1168cacbe919SAmul Kumar Saha 			printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
1169cacbe919SAmul Kumar Saha 				", controller = 0x%04x\n", ecc, ctrl);
1170bfd7f386SKyungmin Park 			return ONENAND_BBT_READ_ERROR;
1171cacbe919SAmul Kumar Saha 		}
1172bfd7f386SKyungmin Park 	} else {
1173bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_wait: read timeout!"
1174bfd7f386SKyungmin Park 				"ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
1175bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
1176bfd7f386SKyungmin Park 	}
1177bfd7f386SKyungmin Park 
1178ef0921d6SKyungmin Park 	/* Initial bad block case: 0x2400 or 0x0400 */
1179ef0921d6SKyungmin Park 	if (ctrl & ONENAND_CTRL_ERROR) {
1180ef0921d6SKyungmin Park 		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
1181ef0921d6SKyungmin Park 		return ONENAND_BBT_READ_ERROR;
1182ef0921d6SKyungmin Park 	}
1183ef0921d6SKyungmin Park 
1184bfd7f386SKyungmin Park 	return 0;
1185bfd7f386SKyungmin Park }
1186bfd7f386SKyungmin Park 
1187bfd7f386SKyungmin Park /**
1188bfd7f386SKyungmin Park  * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
1189bfd7f386SKyungmin Park  * @param mtd		MTD device structure
1190bfd7f386SKyungmin Park  * @param from		offset to read from
1191bfd7f386SKyungmin Park  * @param ops		oob operation description structure
1192bfd7f386SKyungmin Park  *
1193bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area for bbt scan
1194bfd7f386SKyungmin Park  */
1195bfd7f386SKyungmin Park int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
1196bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
119759829cc1SJean-Christophe PLAGNIOL-VILLARD {
119859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
119959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int read = 0, thislen, column;
1200cacbe919SAmul Kumar Saha 	int ret = 0, readcmd;
1201bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1202bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
120359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1204ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
120559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1206*e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1207*e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1208cacbe919SAmul Kumar Saha 
1209bfd7f386SKyungmin Park 	/* Initialize return value */
1210bfd7f386SKyungmin Park 	ops->oobretlen = 0;
121159829cc1SJean-Christophe PLAGNIOL-VILLARD 
121259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
121359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((from + len) > mtd->size)) {
1214bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
1215bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
121659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
121759829cc1SJean-Christophe PLAGNIOL-VILLARD 
121859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
121959829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_READING);
122059829cc1SJean-Christophe PLAGNIOL-VILLARD 
122159829cc1SJean-Christophe PLAGNIOL-VILLARD 	column = from & (mtd->oobsize - 1);
122259829cc1SJean-Christophe PLAGNIOL-VILLARD 
122359829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (read < len) {
1224bfd7f386SKyungmin Park 
122559829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = mtd->oobsize - column;
122659829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = min_t(int, thislen, len);
122759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1228ef0921d6SKyungmin Park 		this->spare_buf = buf;
1229cacbe919SAmul Kumar Saha 		this->command(mtd, readcmd, from, mtd->oobsize);
123059829cc1SJean-Christophe PLAGNIOL-VILLARD 
123159829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, from, 0);
123259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1233ef0921d6SKyungmin Park 		ret = this->bbt_wait(mtd, FL_READING);
1234cacbe919SAmul Kumar Saha 		if (unlikely(ret))
1235cacbe919SAmul Kumar Saha 			ret = onenand_recover_lsb(mtd, from, ret);
1236cacbe919SAmul Kumar Saha 
1237bfd7f386SKyungmin Park 		if (ret)
1238bfd7f386SKyungmin Park 			break;
123959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1240ce3277a6SKyungmin Park 		this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
124159829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
124259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
124359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
124459829cc1SJean-Christophe PLAGNIOL-VILLARD 
124559829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
1246bfd7f386SKyungmin Park 
124759829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Read more? */
124859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read < len) {
1249bfd7f386SKyungmin Park 			/* Update Page size */
1250bfd7f386SKyungmin Park 			from += this->writesize;
125159829cc1SJean-Christophe PLAGNIOL-VILLARD 			column = 0;
125259829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
125359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
125459829cc1SJean-Christophe PLAGNIOL-VILLARD 
125559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
125659829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
125759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1258bfd7f386SKyungmin Park 	ops->oobretlen = read;
125959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
126059829cc1SJean-Christophe PLAGNIOL-VILLARD }
126159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1262bfd7f386SKyungmin Park 
126359829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
126459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1265bfd7f386SKyungmin Park  * onenand_verify_oob - [GENERIC] verify the oob contents after a write
126659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
126759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf           the databuffer to verify
1268bfd7f386SKyungmin Park  * @param to            offset to read from
126959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1270bfd7f386SKyungmin Park static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
127159829cc1SJean-Christophe PLAGNIOL-VILLARD {
127259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1273bfd7f386SKyungmin Park 	u_char *oob_buf = this->oob_buf;
1274cacbe919SAmul Kumar Saha 	int status, i, readcmd;
127559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1276*e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1277*e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1278cacbe919SAmul Kumar Saha 
1279cacbe919SAmul Kumar Saha 	this->command(mtd, readcmd, to, mtd->oobsize);
1280bfd7f386SKyungmin Park 	onenand_update_bufferram(mtd, to, 0);
1281bfd7f386SKyungmin Park 	status = this->wait(mtd, FL_READING);
1282bfd7f386SKyungmin Park 	if (status)
1283bfd7f386SKyungmin Park 		return status;
1284bfd7f386SKyungmin Park 
1285ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
1286bfd7f386SKyungmin Park 	for (i = 0; i < mtd->oobsize; i++)
1287bfd7f386SKyungmin Park 		if (buf[i] != 0xFF && buf[i] != oob_buf[i])
1288bfd7f386SKyungmin Park 			return -EBADMSG;
1289bfd7f386SKyungmin Park 
1290bfd7f386SKyungmin Park 	return 0;
1291bfd7f386SKyungmin Park }
1292bfd7f386SKyungmin Park 
1293bfd7f386SKyungmin Park /**
1294bfd7f386SKyungmin Park  * onenand_verify - [GENERIC] verify the chip contents after a write
1295bfd7f386SKyungmin Park  * @param mtd          MTD device structure
1296bfd7f386SKyungmin Park  * @param buf          the databuffer to verify
1297bfd7f386SKyungmin Park  * @param addr         offset to read from
1298bfd7f386SKyungmin Park  * @param len          number of bytes to read and compare
1299bfd7f386SKyungmin Park  */
1300bfd7f386SKyungmin Park static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
1301bfd7f386SKyungmin Park {
1302bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1303bfd7f386SKyungmin Park 	void __iomem *dataram;
1304bfd7f386SKyungmin Park 	int ret = 0;
1305bfd7f386SKyungmin Park 	int thislen, column;
1306bfd7f386SKyungmin Park 
1307bfd7f386SKyungmin Park 	while (len != 0) {
1308bfd7f386SKyungmin Park 		thislen = min_t(int, this->writesize, len);
1309bfd7f386SKyungmin Park 		column = addr & (this->writesize - 1);
1310bfd7f386SKyungmin Park 		if (column + thislen > this->writesize)
1311bfd7f386SKyungmin Park 			thislen = this->writesize - column;
1312bfd7f386SKyungmin Park 
1313bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
1314bfd7f386SKyungmin Park 
1315bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, addr, 0);
131659829cc1SJean-Christophe PLAGNIOL-VILLARD 
131759829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_READING);
131859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret)
131959829cc1SJean-Christophe PLAGNIOL-VILLARD 			return ret;
132059829cc1SJean-Christophe PLAGNIOL-VILLARD 
132159829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, addr, 1);
132259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1323bfd7f386SKyungmin Park 		dataram = this->base + ONENAND_DATARAM;
1324bfd7f386SKyungmin Park 		dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
132559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1326bfd7f386SKyungmin Park 		if (memcmp(buf, dataram + column, thislen))
132759829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EBADMSG;
132859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1329bfd7f386SKyungmin Park 		len -= thislen;
1330bfd7f386SKyungmin Park 		buf += thislen;
1331bfd7f386SKyungmin Park 		addr += thislen;
1332bfd7f386SKyungmin Park 	}
1333bfd7f386SKyungmin Park 
133459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
133559829cc1SJean-Christophe PLAGNIOL-VILLARD }
133659829cc1SJean-Christophe PLAGNIOL-VILLARD #else
1337bfd7f386SKyungmin Park #define onenand_verify(...)             (0)
1338bfd7f386SKyungmin Park #define onenand_verify_oob(...)         (0)
133959829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
134059829cc1SJean-Christophe PLAGNIOL-VILLARD 
13411ae39862SStefan Roese #define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)
134259829cc1SJean-Christophe PLAGNIOL-VILLARD 
134359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1344bfd7f386SKyungmin Park  * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
134559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
1346bfd7f386SKyungmin Park  * @param oob_buf       oob buffer
1347bfd7f386SKyungmin Park  * @param buf           source address
1348bfd7f386SKyungmin Park  * @param column        oob offset to write to
1349bfd7f386SKyungmin Park  * @param thislen       oob length to write
135059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1351bfd7f386SKyungmin Park static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
1352bfd7f386SKyungmin Park 		const u_char *buf, int column, int thislen)
135359829cc1SJean-Christophe PLAGNIOL-VILLARD {
135459829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1355bfd7f386SKyungmin Park 	struct nand_oobfree *free;
1356bfd7f386SKyungmin Park 	int writecol = column;
1357bfd7f386SKyungmin Park 	int writeend = column + thislen;
1358bfd7f386SKyungmin Park 	int lastgap = 0;
1359bfd7f386SKyungmin Park 	unsigned int i;
1360bfd7f386SKyungmin Park 
1361bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1362bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1363bfd7f386SKyungmin Park 		if (writecol >= lastgap)
1364bfd7f386SKyungmin Park 			writecol += free->offset - lastgap;
1365bfd7f386SKyungmin Park 		if (writeend >= lastgap)
1366bfd7f386SKyungmin Park 			writeend += free->offset - lastgap;
1367bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
1368bfd7f386SKyungmin Park 	}
1369bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1370bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1371bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
1372bfd7f386SKyungmin Park 		if (free->offset < writeend && free_end > writecol) {
1373bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,writecol);
1374bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,writeend);
1375bfd7f386SKyungmin Park 			int n = ed - st;
1376bfd7f386SKyungmin Park 			memcpy(oob_buf + st, buf, n);
1377bfd7f386SKyungmin Park 			buf += n;
1378bfd7f386SKyungmin Park 		} else if (column == 0)
1379bfd7f386SKyungmin Park 			break;
1380bfd7f386SKyungmin Park 	}
1381bfd7f386SKyungmin Park 	return 0;
1382bfd7f386SKyungmin Park }
1383bfd7f386SKyungmin Park 
1384bfd7f386SKyungmin Park /**
1385bfd7f386SKyungmin Park  * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
1386bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1387bfd7f386SKyungmin Park  * @param to            offset to write to
1388bfd7f386SKyungmin Park  * @param ops           oob operation description structure
1389bfd7f386SKyungmin Park  *
1390bfd7f386SKyungmin Park  * Write main and/or oob with ECC
1391bfd7f386SKyungmin Park  */
1392bfd7f386SKyungmin Park static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
1393bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1394bfd7f386SKyungmin Park {
1395bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1396bfd7f386SKyungmin Park 	int written = 0, column, thislen, subpage;
1397bfd7f386SKyungmin Park 	int oobwritten = 0, oobcolumn, thisooblen, oobsize;
1398bfd7f386SKyungmin Park 	size_t len = ops->len;
1399bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
1400bfd7f386SKyungmin Park 	const u_char *buf = ops->datbuf;
1401bfd7f386SKyungmin Park 	const u_char *oob = ops->oobbuf;
1402bfd7f386SKyungmin Park 	u_char *oobbuf;
140359829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
140459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1405ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
140659829cc1SJean-Christophe PLAGNIOL-VILLARD 
140759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Initialize retlen, in case of early exit */
1408bfd7f386SKyungmin Park 	ops->retlen = 0;
1409bfd7f386SKyungmin Park 	ops->oobretlen = 0;
141059829cc1SJean-Christophe PLAGNIOL-VILLARD 
141159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow writes past end of device */
141259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((to + len) > mtd->size)) {
1413bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
141459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
141559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
141659829cc1SJean-Christophe PLAGNIOL-VILLARD 
141759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reject writes, which are not page aligned */
1418bfd7f386SKyungmin Park 	if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
1419bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
142059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
142159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
142259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1423bfd7f386SKyungmin Park 	if (ops->mode == MTD_OOB_AUTO)
1424bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1425bfd7f386SKyungmin Park 	else
1426bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1427bfd7f386SKyungmin Park 
1428bfd7f386SKyungmin Park 	oobcolumn = to & (mtd->oobsize - 1);
1429bfd7f386SKyungmin Park 
1430bfd7f386SKyungmin Park 	column = to & (mtd->writesize - 1);
143159829cc1SJean-Christophe PLAGNIOL-VILLARD 
143259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop until all data write */
143359829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (written < len) {
1434bfd7f386SKyungmin Park 		u_char *wbuf = (u_char *) buf;
143559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1436bfd7f386SKyungmin Park 		thislen = min_t(int, mtd->writesize - column, len - written);
1437bfd7f386SKyungmin Park 		thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
143859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1439bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
1440bfd7f386SKyungmin Park 
1441bfd7f386SKyungmin Park 		/* Partial page write */
1442bfd7f386SKyungmin Park 		subpage = thislen < mtd->writesize;
1443bfd7f386SKyungmin Park 		if (subpage) {
1444bfd7f386SKyungmin Park 			memset(this->page_buf, 0xff, mtd->writesize);
1445bfd7f386SKyungmin Park 			memcpy(this->page_buf + column, buf, thislen);
1446bfd7f386SKyungmin Park 			wbuf = this->page_buf;
1447bfd7f386SKyungmin Park 		}
1448bfd7f386SKyungmin Park 
1449ef0921d6SKyungmin Park 		this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
1450bfd7f386SKyungmin Park 
1451bfd7f386SKyungmin Park 		if (oob) {
1452bfd7f386SKyungmin Park 			oobbuf = this->oob_buf;
1453bfd7f386SKyungmin Park 
1454bfd7f386SKyungmin Park 			/* We send data to spare ram with oobsize
1455bfd7f386SKyungmin Park 			 *                          * to prevent byte access */
1456bfd7f386SKyungmin Park 			memset(oobbuf, 0xff, mtd->oobsize);
1457bfd7f386SKyungmin Park 			if (ops->mode == MTD_OOB_AUTO)
1458bfd7f386SKyungmin Park 				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
1459bfd7f386SKyungmin Park 			else
1460bfd7f386SKyungmin Park 				memcpy(oobbuf + oobcolumn, oob, thisooblen);
1461bfd7f386SKyungmin Park 
1462bfd7f386SKyungmin Park 			oobwritten += thisooblen;
1463bfd7f386SKyungmin Park 			oob += thisooblen;
1464bfd7f386SKyungmin Park 			oobcolumn = 0;
1465bfd7f386SKyungmin Park 		} else
1466bfd7f386SKyungmin Park 			oobbuf = (u_char *) ffchars;
1467bfd7f386SKyungmin Park 
1468ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
146959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1470d438d508SKyungmin Park 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
147159829cc1SJean-Christophe PLAGNIOL-VILLARD 
147259829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_WRITING);
1473bfd7f386SKyungmin Park 
1474bfd7f386SKyungmin Park 		/* In partial page write we don't update bufferram */
1475bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, !ret && !subpage);
1476bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1477bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1478bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
1479bfd7f386SKyungmin Park 		}
1480bfd7f386SKyungmin Park 
148159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
1482bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
1483bfd7f386SKyungmin Park 			break;
1484bfd7f386SKyungmin Park 		}
1485bfd7f386SKyungmin Park 
1486bfd7f386SKyungmin Park 		/* Only check verify write turn on */
1487bfd7f386SKyungmin Park 		ret = onenand_verify(mtd, buf, to, thislen);
1488bfd7f386SKyungmin Park 		if (ret) {
1489bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
149059829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
149159829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
149259829cc1SJean-Christophe PLAGNIOL-VILLARD 
149359829cc1SJean-Christophe PLAGNIOL-VILLARD 		written += thislen;
149459829cc1SJean-Christophe PLAGNIOL-VILLARD 
149559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (written == len)
149659829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
149759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1498bfd7f386SKyungmin Park 		column = 0;
149959829cc1SJean-Christophe PLAGNIOL-VILLARD 		to += thislen;
150059829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
150159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
150259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1503bfd7f386SKyungmin Park 	ops->retlen = written;
150459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1505bfd7f386SKyungmin Park 	return ret;
1506bfd7f386SKyungmin Park }
1507bfd7f386SKyungmin Park 
1508bfd7f386SKyungmin Park /**
1509bfd7f386SKyungmin Park  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
1510bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1511bfd7f386SKyungmin Park  * @param to            offset to write to
1512bfd7f386SKyungmin Park  * @param len           number of bytes to write
1513bfd7f386SKyungmin Park  * @param retlen        pointer to variable to store the number of written bytes
1514bfd7f386SKyungmin Park  * @param buf           the data to write
1515bfd7f386SKyungmin Park  * @param mode          operation mode
1516bfd7f386SKyungmin Park  *
1517bfd7f386SKyungmin Park  * OneNAND write out-of-band
1518bfd7f386SKyungmin Park  */
1519bfd7f386SKyungmin Park static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
1520bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1521bfd7f386SKyungmin Park {
1522bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1523bfd7f386SKyungmin Park 	int column, ret = 0, oobsize;
1524cacbe919SAmul Kumar Saha 	int written = 0, oobcmd;
1525bfd7f386SKyungmin Park 	u_char *oobbuf;
1526bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1527bfd7f386SKyungmin Park 	const u_char *buf = ops->oobbuf;
1528bfd7f386SKyungmin Park 	mtd_oob_mode_t mode = ops->mode;
1529bfd7f386SKyungmin Park 
1530bfd7f386SKyungmin Park 	to += ops->ooboffs;
1531bfd7f386SKyungmin Park 
1532ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
1533bfd7f386SKyungmin Park 
1534bfd7f386SKyungmin Park 	/* Initialize retlen, in case of early exit */
1535bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1536bfd7f386SKyungmin Park 
1537bfd7f386SKyungmin Park 	if (mode == MTD_OOB_AUTO)
1538bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1539bfd7f386SKyungmin Park 	else
1540bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1541bfd7f386SKyungmin Park 
1542bfd7f386SKyungmin Park 	column = to & (mtd->oobsize - 1);
1543bfd7f386SKyungmin Park 
1544bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1545bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");
1546bfd7f386SKyungmin Park 		return -EINVAL;
1547bfd7f386SKyungmin Park 	}
1548bfd7f386SKyungmin Park 
1549bfd7f386SKyungmin Park 	/* For compatibility with NAND: Do not allow write past end of page */
1550bfd7f386SKyungmin Park 	if (unlikely(column + len > oobsize)) {
1551bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: "
1552bfd7f386SKyungmin Park 				"Attempt to write past end of page\n");
1553bfd7f386SKyungmin Park 		return -EINVAL;
1554bfd7f386SKyungmin Park 	}
1555bfd7f386SKyungmin Park 
1556bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1557bfd7f386SKyungmin Park 	if (unlikely(to >= mtd->size ||
1558bfd7f386SKyungmin Park 				column + len > ((mtd->size >> this->page_shift) -
1559bfd7f386SKyungmin Park 					(to >> this->page_shift)) * oobsize)) {
1560bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n");
1561bfd7f386SKyungmin Park 		return -EINVAL;
1562bfd7f386SKyungmin Park 	}
1563bfd7f386SKyungmin Park 
1564bfd7f386SKyungmin Park 	oobbuf = this->oob_buf;
1565bfd7f386SKyungmin Park 
1566*e26fd3d3SLukasz Majewski 	oobcmd = ONENAND_IS_4KB_PAGE(this) ?
1567*e26fd3d3SLukasz Majewski 		ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
1568cacbe919SAmul Kumar Saha 
1569bfd7f386SKyungmin Park 	/* Loop until all data write */
1570bfd7f386SKyungmin Park 	while (written < len) {
1571bfd7f386SKyungmin Park 		int thislen = min_t(int, oobsize, len - written);
1572bfd7f386SKyungmin Park 
1573bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
1574bfd7f386SKyungmin Park 
1575bfd7f386SKyungmin Park 		/* We send data to spare ram with oobsize
1576bfd7f386SKyungmin Park 		 * to prevent byte access */
1577bfd7f386SKyungmin Park 		memset(oobbuf, 0xff, mtd->oobsize);
1578bfd7f386SKyungmin Park 		if (mode == MTD_OOB_AUTO)
1579bfd7f386SKyungmin Park 			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
1580bfd7f386SKyungmin Park 		else
1581bfd7f386SKyungmin Park 			memcpy(oobbuf + column, buf, thislen);
1582ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
1583bfd7f386SKyungmin Park 
1584*e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this)) {
1585cacbe919SAmul Kumar Saha 			/* Set main area of DataRAM to 0xff*/
1586cacbe919SAmul Kumar Saha 			memset(this->page_buf, 0xff, mtd->writesize);
1587cacbe919SAmul Kumar Saha 			this->write_bufferram(mtd, 0, ONENAND_DATARAM,
1588cacbe919SAmul Kumar Saha 				this->page_buf,	0, mtd->writesize);
1589cacbe919SAmul Kumar Saha 		}
1590cacbe919SAmul Kumar Saha 
1591cacbe919SAmul Kumar Saha 		this->command(mtd, oobcmd, to, mtd->oobsize);
1592bfd7f386SKyungmin Park 
1593bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, 0);
1594bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1595bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1596bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, 0);
1597bfd7f386SKyungmin Park 		}
1598bfd7f386SKyungmin Park 
1599bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_WRITING);
1600bfd7f386SKyungmin Park 		if (ret) {
1601bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret);
1602bfd7f386SKyungmin Park 			break;
1603bfd7f386SKyungmin Park 		}
1604bfd7f386SKyungmin Park 
1605bfd7f386SKyungmin Park 		ret = onenand_verify_oob(mtd, oobbuf, to);
1606bfd7f386SKyungmin Park 		if (ret) {
1607bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret);
1608bfd7f386SKyungmin Park 			break;
1609bfd7f386SKyungmin Park 		}
1610bfd7f386SKyungmin Park 
1611bfd7f386SKyungmin Park 		written += thislen;
1612bfd7f386SKyungmin Park 		if (written == len)
1613bfd7f386SKyungmin Park 			break;
1614bfd7f386SKyungmin Park 
1615bfd7f386SKyungmin Park 		to += mtd->writesize;
1616bfd7f386SKyungmin Park 		buf += thislen;
1617bfd7f386SKyungmin Park 		column = 0;
1618bfd7f386SKyungmin Park 	}
1619bfd7f386SKyungmin Park 
1620bfd7f386SKyungmin Park 	ops->oobretlen = written;
162159829cc1SJean-Christophe PLAGNIOL-VILLARD 
162259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
162359829cc1SJean-Christophe PLAGNIOL-VILLARD }
162459829cc1SJean-Christophe PLAGNIOL-VILLARD 
162559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
162659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write - [MTD Interface] compability function for onenand_write_ecc
162759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
162859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
162959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to write
163059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of written bytes
163159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the data to write
163259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1633bfd7f386SKyungmin Park  * Write with ECC
163459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
163559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
163659829cc1SJean-Christophe PLAGNIOL-VILLARD 		  size_t * retlen, const u_char * buf)
163759829cc1SJean-Christophe PLAGNIOL-VILLARD {
1638bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1639bfd7f386SKyungmin Park 		.len    = len,
1640bfd7f386SKyungmin Park 		.ooblen = 0,
1641bfd7f386SKyungmin Park 		.datbuf = (u_char *) buf,
1642bfd7f386SKyungmin Park 		.oobbuf = NULL,
1643bfd7f386SKyungmin Park 	};
1644bfd7f386SKyungmin Park 	int ret;
1645bfd7f386SKyungmin Park 
1646bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_WRITING);
1647bfd7f386SKyungmin Park 	ret = onenand_write_ops_nolock(mtd, to, &ops);
1648bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1649bfd7f386SKyungmin Park 
1650bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1651bfd7f386SKyungmin Park 	return ret;
165259829cc1SJean-Christophe PLAGNIOL-VILLARD }
165359829cc1SJean-Christophe PLAGNIOL-VILLARD 
165459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
165559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
165659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
165759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
1658bfd7f386SKyungmin Park  * @param ops		oob operation description structure
165959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1660bfd7f386SKyungmin Park  * OneNAND write main and/or out-of-band
166159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1662bfd7f386SKyungmin Park int onenand_write_oob(struct mtd_info *mtd, loff_t to,
1663bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
166459829cc1SJean-Christophe PLAGNIOL-VILLARD {
1665bfd7f386SKyungmin Park 	int ret;
166659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1667bfd7f386SKyungmin Park 	switch (ops->mode) {
1668bfd7f386SKyungmin Park 	case MTD_OOB_PLACE:
1669bfd7f386SKyungmin Park 	case MTD_OOB_AUTO:
1670bfd7f386SKyungmin Park 		break;
1671bfd7f386SKyungmin Park 	case MTD_OOB_RAW:
1672bfd7f386SKyungmin Park 		/* Not implemented yet */
1673bfd7f386SKyungmin Park 	default:
167459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
167559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
167659829cc1SJean-Christophe PLAGNIOL-VILLARD 
167759829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_WRITING);
1678bfd7f386SKyungmin Park 	if (ops->datbuf)
1679bfd7f386SKyungmin Park 		ret = onenand_write_ops_nolock(mtd, to, ops);
1680bfd7f386SKyungmin Park 	else
1681bfd7f386SKyungmin Park 		ret = onenand_write_oob_nolock(mtd, to, ops);
168259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
168359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1684bfd7f386SKyungmin Park 	return ret;
168559829cc1SJean-Christophe PLAGNIOL-VILLARD 
168659829cc1SJean-Christophe PLAGNIOL-VILLARD }
168759829cc1SJean-Christophe PLAGNIOL-VILLARD 
168859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1689d438d508SKyungmin Park  * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
1690d438d508SKyungmin Park  * @param mtd		MTD device structure
1691d438d508SKyungmin Park  * @param ofs		offset from device start
1692d438d508SKyungmin Park  * @param allowbbt	1, if its allowed to access the bbt area
1693d438d508SKyungmin Park  *
1694d438d508SKyungmin Park  * Check, if the block is bad, Either by reading the bad block table or
1695d438d508SKyungmin Park  * calling of the scan function.
1696d438d508SKyungmin Park  */
1697d438d508SKyungmin Park static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
1698d438d508SKyungmin Park {
1699d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1700d438d508SKyungmin Park 	struct bbm_info *bbm = this->bbm;
1701d438d508SKyungmin Park 
1702d438d508SKyungmin Park 	/* Return info from the table */
1703d438d508SKyungmin Park 	return bbm->isbad_bbt(mtd, ofs, allowbbt);
1704d438d508SKyungmin Park }
1705d438d508SKyungmin Park 
1706d438d508SKyungmin Park 
1707d438d508SKyungmin Park /**
170859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_erase - [MTD Interface] erase block(s)
170959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
171059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param instr		erase instruction
171159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
171259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Erase one ore more blocks
171359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
171459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
171559829cc1SJean-Christophe PLAGNIOL-VILLARD {
171659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
171759829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int block_size;
1718cacbe919SAmul Kumar Saha 	loff_t addr = instr->addr;
1719cacbe919SAmul Kumar Saha 	unsigned int len = instr->len;
1720cacbe919SAmul Kumar Saha 	int ret = 0, i;
1721cacbe919SAmul Kumar Saha 	struct mtd_erase_region_info *region = NULL;
1722cacbe919SAmul Kumar Saha 	unsigned int region_end = 0;
172359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1724cacbe919SAmul Kumar Saha 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
1725cacbe919SAmul Kumar Saha 			(unsigned int) addr, len);
172659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1727cacbe919SAmul Kumar Saha 	/* Do not allow erase past end of device */
1728cacbe919SAmul Kumar Saha 	if (unlikely((len + addr) > mtd->size)) {
1729cacbe919SAmul Kumar Saha 		MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
1730cacbe919SAmul Kumar Saha 					"Erase past end of device\n");
1731cacbe919SAmul Kumar Saha 		return -EINVAL;
1732cacbe919SAmul Kumar Saha 	}
1733cacbe919SAmul Kumar Saha 
1734cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
1735cacbe919SAmul Kumar Saha 		/* Find the eraseregion of this address */
1736cacbe919SAmul Kumar Saha 		i = flexonenand_region(mtd, addr);
1737cacbe919SAmul Kumar Saha 		region = &mtd->eraseregions[i];
1738cacbe919SAmul Kumar Saha 
1739cacbe919SAmul Kumar Saha 		block_size = region->erasesize;
1740cacbe919SAmul Kumar Saha 		region_end = region->offset
1741cacbe919SAmul Kumar Saha 			+ region->erasesize * region->numblocks;
1742cacbe919SAmul Kumar Saha 
1743cacbe919SAmul Kumar Saha 		/* Start address within region must align on block boundary.
1744cacbe919SAmul Kumar Saha 		 * Erase region's start offset is always block start address.
1745cacbe919SAmul Kumar Saha 		 */
1746cacbe919SAmul Kumar Saha 		if (unlikely((addr - region->offset) & (block_size - 1))) {
1747cacbe919SAmul Kumar Saha 			MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
1748cacbe919SAmul Kumar Saha 				" Unaligned address\n");
1749cacbe919SAmul Kumar Saha 			return -EINVAL;
1750cacbe919SAmul Kumar Saha 		}
1751cacbe919SAmul Kumar Saha 	} else {
1752cacbe919SAmul Kumar Saha 		block_size = 1 << this->erase_shift;
175359829cc1SJean-Christophe PLAGNIOL-VILLARD 
175459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Start address must align on block boundary */
1755cacbe919SAmul Kumar Saha 		if (unlikely(addr & (block_size - 1))) {
1756cacbe919SAmul Kumar Saha 			MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
1757cacbe919SAmul Kumar Saha 						"Unaligned address\n");
175859829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EINVAL;
175959829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
1760cacbe919SAmul Kumar Saha 	}
176159829cc1SJean-Christophe PLAGNIOL-VILLARD 
176259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Length must align on block boundary */
1763cacbe919SAmul Kumar Saha 	if (unlikely(len & (block_size - 1))) {
17643167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
176559829cc1SJean-Christophe PLAGNIOL-VILLARD 			 "onenand_erase: Length not block aligned\n");
176659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
176759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
176859829cc1SJean-Christophe PLAGNIOL-VILLARD 
176959829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->fail_addr = 0xffffffff;
177059829cc1SJean-Christophe PLAGNIOL-VILLARD 
177159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
177259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_ERASING);
177359829cc1SJean-Christophe PLAGNIOL-VILLARD 
177459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop throught the pages */
177559829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASING;
177659829cc1SJean-Christophe PLAGNIOL-VILLARD 
177759829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (len) {
177859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1779ef0921d6SKyungmin Park 		/* Check if we have a bad block, we do not erase bad blocks */
1780ef0921d6SKyungmin Park 		if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) {
1781ef0921d6SKyungmin Park 			printk(KERN_WARNING "onenand_erase: attempt to erase"
1782ef0921d6SKyungmin Park 				" a bad block at addr 0x%08x\n",
1783ef0921d6SKyungmin Park 				(unsigned int) addr);
1784ef0921d6SKyungmin Park 			instr->state = MTD_ERASE_FAILED;
1785ef0921d6SKyungmin Park 			goto erase_exit;
1786ef0921d6SKyungmin Park 		}
178759829cc1SJean-Christophe PLAGNIOL-VILLARD 
178859829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
178959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1790d438d508SKyungmin Park 		onenand_invalidate_bufferram(mtd, addr, block_size);
1791d438d508SKyungmin Park 
179259829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_ERASING);
179359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check, if it is write protected */
179459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
179559829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (ret == -EPERM)
17963167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
17973167c538SScott Wood 					  "Device is write protected!!!\n");
179859829cc1SJean-Christophe PLAGNIOL-VILLARD 			else
17993167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
18003167c538SScott Wood 					  "Failed erase, block %d\n",
1801cacbe919SAmul Kumar Saha 					onenand_block(this, addr));
180259829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->state = MTD_ERASE_FAILED;
180359829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->fail_addr = addr;
1804ef0921d6SKyungmin Park 
180559829cc1SJean-Christophe PLAGNIOL-VILLARD 			goto erase_exit;
180659829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
180759829cc1SJean-Christophe PLAGNIOL-VILLARD 
180859829cc1SJean-Christophe PLAGNIOL-VILLARD 		len -= block_size;
180959829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr += block_size;
1810cacbe919SAmul Kumar Saha 
1811cacbe919SAmul Kumar Saha 		if (addr == region_end) {
1812cacbe919SAmul Kumar Saha 			if (!len)
1813cacbe919SAmul Kumar Saha 				break;
1814cacbe919SAmul Kumar Saha 			region++;
1815cacbe919SAmul Kumar Saha 
1816cacbe919SAmul Kumar Saha 			block_size = region->erasesize;
1817cacbe919SAmul Kumar Saha 			region_end = region->offset
1818cacbe919SAmul Kumar Saha 				+ region->erasesize * region->numblocks;
1819cacbe919SAmul Kumar Saha 
1820cacbe919SAmul Kumar Saha 			if (len & (block_size - 1)) {
1821cacbe919SAmul Kumar Saha 				/* This has been checked at MTD
1822cacbe919SAmul Kumar Saha 				 * partitioning level. */
1823cacbe919SAmul Kumar Saha 				printk("onenand_erase: Unaligned address\n");
1824cacbe919SAmul Kumar Saha 				goto erase_exit;
1825cacbe919SAmul Kumar Saha 			}
1826cacbe919SAmul Kumar Saha 		}
182759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
182859829cc1SJean-Christophe PLAGNIOL-VILLARD 
182959829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASE_DONE;
183059829cc1SJean-Christophe PLAGNIOL-VILLARD 
183159829cc1SJean-Christophe PLAGNIOL-VILLARD erase_exit:
183259829cc1SJean-Christophe PLAGNIOL-VILLARD 
183359829cc1SJean-Christophe PLAGNIOL-VILLARD 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
183459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do call back function */
183559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!ret)
183659829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd_erase_callback(instr);
183759829cc1SJean-Christophe PLAGNIOL-VILLARD 
183859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
183959829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
184059829cc1SJean-Christophe PLAGNIOL-VILLARD 
184159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
184259829cc1SJean-Christophe PLAGNIOL-VILLARD }
184359829cc1SJean-Christophe PLAGNIOL-VILLARD 
184459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
184559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync - [MTD Interface] sync
184659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
184759829cc1SJean-Christophe PLAGNIOL-VILLARD  *
184859829cc1SJean-Christophe PLAGNIOL-VILLARD  * Sync is actually a wait for chip ready function
184959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
185059829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_sync(struct mtd_info *mtd)
185159829cc1SJean-Christophe PLAGNIOL-VILLARD {
18523167c538SScott Wood 	MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
185359829cc1SJean-Christophe PLAGNIOL-VILLARD 
185459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
185559829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_SYNCING);
185659829cc1SJean-Christophe PLAGNIOL-VILLARD 
185759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Release it and go back */
185859829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
185959829cc1SJean-Christophe PLAGNIOL-VILLARD }
186059829cc1SJean-Christophe PLAGNIOL-VILLARD 
186159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
186259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
186359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
186459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1865d438d508SKyungmin Park  *
1866d438d508SKyungmin Park  * Check whether the block is bad
186759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
186859829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
186959829cc1SJean-Christophe PLAGNIOL-VILLARD {
1870d438d508SKyungmin Park 	int ret;
1871d438d508SKyungmin Park 
1872d438d508SKyungmin Park 	/* Check for invalid offset */
1873d438d508SKyungmin Park 	if (ofs > mtd->size)
1874d438d508SKyungmin Park 		return -EINVAL;
1875d438d508SKyungmin Park 
1876d438d508SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1877d438d508SKyungmin Park 	ret = onenand_block_isbad_nolock(mtd,ofs, 0);
1878d438d508SKyungmin Park 	onenand_release_device(mtd);
1879d438d508SKyungmin Park 	return ret;
188059829cc1SJean-Christophe PLAGNIOL-VILLARD }
188159829cc1SJean-Christophe PLAGNIOL-VILLARD 
188259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
18831714f51aSKyungmin Park  * onenand_default_block_markbad - [DEFAULT] mark a block bad
18841714f51aSKyungmin Park  * @param mtd           MTD device structure
18851714f51aSKyungmin Park  * @param ofs           offset from device start
18861714f51aSKyungmin Park  *
18871714f51aSKyungmin Park  * This is the default implementation, which can be overridden by
18881714f51aSKyungmin Park  * a hardware specific driver.
18891714f51aSKyungmin Park  */
18901714f51aSKyungmin Park static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
18911714f51aSKyungmin Park {
18921714f51aSKyungmin Park 	struct onenand_chip *this = mtd->priv;
18931714f51aSKyungmin Park 	struct bbm_info *bbm = this->bbm;
18941714f51aSKyungmin Park 	u_char buf[2] = {0, 0};
18951714f51aSKyungmin Park 	struct mtd_oob_ops ops = {
18961714f51aSKyungmin Park 		.mode = MTD_OOB_PLACE,
18971714f51aSKyungmin Park 		.ooblen = 2,
18981714f51aSKyungmin Park 		.oobbuf = buf,
18991714f51aSKyungmin Park 		.ooboffs = 0,
19001714f51aSKyungmin Park 	};
19011714f51aSKyungmin Park 	int block;
19021714f51aSKyungmin Park 
19031714f51aSKyungmin Park 	/* Get block number */
1904cacbe919SAmul Kumar Saha 	block = onenand_block(this, ofs);
19051714f51aSKyungmin Park 	if (bbm->bbt)
19061714f51aSKyungmin Park 		bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
19071714f51aSKyungmin Park 
19081714f51aSKyungmin Park 	/* We write two bytes, so we dont have to mess with 16 bit access */
19091714f51aSKyungmin Park 	ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
19101714f51aSKyungmin Park 	return onenand_write_oob_nolock(mtd, ofs, &ops);
19111714f51aSKyungmin Park }
19121714f51aSKyungmin Park 
19131714f51aSKyungmin Park /**
191459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
191559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
191659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1917d438d508SKyungmin Park  *
1918d438d508SKyungmin Park  * Mark the block as bad
191959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
192059829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
192159829cc1SJean-Christophe PLAGNIOL-VILLARD {
1922d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1923d438d508SKyungmin Park 	int ret;
1924d438d508SKyungmin Park 
1925d438d508SKyungmin Park 	ret = onenand_block_isbad(mtd, ofs);
1926d438d508SKyungmin Park 	if (ret) {
1927d438d508SKyungmin Park 		/* If it was bad already, return success and do nothing */
1928d438d508SKyungmin Park 		if (ret > 0)
192959829cc1SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1930d438d508SKyungmin Park 		return ret;
1931d438d508SKyungmin Park 	}
1932d438d508SKyungmin Park 
1933d438d508SKyungmin Park 	ret = this->block_markbad(mtd, ofs);
1934d438d508SKyungmin Park 	return ret;
193559829cc1SJean-Christophe PLAGNIOL-VILLARD }
193659829cc1SJean-Christophe PLAGNIOL-VILLARD 
193759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1938ef0921d6SKyungmin Park  * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
193959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
194059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs           offset relative to mtd start
1941ef0921d6SKyungmin Park  * @param len           number of bytes to lock or unlock
1942ef0921d6SKyungmin Park  * @param cmd           lock or unlock command
194359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1944ef0921d6SKyungmin Park  * Lock or unlock one or more blocks
194559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1946ef0921d6SKyungmin Park static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
194759829cc1SJean-Christophe PLAGNIOL-VILLARD {
194859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
194959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int start, end, block, value, status;
195059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1951cacbe919SAmul Kumar Saha 	start = onenand_block(this, ofs);
1952cacbe919SAmul Kumar Saha 	end = onenand_block(this, ofs + len);
195359829cc1SJean-Christophe PLAGNIOL-VILLARD 
195459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Continuous lock scheme */
1955ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK) {
195659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
195759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(start,
195859829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
195959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set end block address */
196059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(end - 1,
196159829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_END_BLOCK_ADDRESS);
196259829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
1963ef0921d6SKyungmin Park 		this->command(mtd, cmd, 0, 0);
196459829cc1SJean-Christophe PLAGNIOL-VILLARD 
196559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
196659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
196759829cc1SJean-Christophe PLAGNIOL-VILLARD 
196859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
196959829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
197059829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
197159829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
197259829cc1SJean-Christophe PLAGNIOL-VILLARD 
197359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
197459829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
197559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
197659829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "wp status = 0x%x\n", status);
197759829cc1SJean-Christophe PLAGNIOL-VILLARD 
197859829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
197959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
198059829cc1SJean-Christophe PLAGNIOL-VILLARD 
198159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Block lock scheme */
1982cacbe919SAmul Kumar Saha 	for (block = start; block < end; block++) {
1983ef0921d6SKyungmin Park 		/* Set block address */
1984ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
1985ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
1986ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
1987ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
1988ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
1989ef0921d6SKyungmin Park 
199059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
199159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(block,
199259829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
199359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
199459829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
199559829cc1SJean-Christophe PLAGNIOL-VILLARD 
199659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
199759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
199859829cc1SJean-Christophe PLAGNIOL-VILLARD 
199959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
200059829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
200159829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
200259829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
200359829cc1SJean-Christophe PLAGNIOL-VILLARD 
200459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
200559829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
200659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
200759829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "block = %d, wp status = 0x%x\n",
200859829cc1SJean-Christophe PLAGNIOL-VILLARD 			       block, status);
200959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
201059829cc1SJean-Christophe PLAGNIOL-VILLARD 
201159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
201259829cc1SJean-Christophe PLAGNIOL-VILLARD }
201359829cc1SJean-Christophe PLAGNIOL-VILLARD 
20144fca3310SStefan Roese #ifdef ONENAND_LINUX
201559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
2016ef0921d6SKyungmin Park  * onenand_lock - [MTD Interface] Lock block(s)
2017ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2018ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
2019ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
2020ef0921d6SKyungmin Park  *
2021ef0921d6SKyungmin Park  * Lock one or more blocks
2022ef0921d6SKyungmin Park  */
2023ef0921d6SKyungmin Park static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
2024ef0921d6SKyungmin Park {
2025ef0921d6SKyungmin Park 	int ret;
2026ef0921d6SKyungmin Park 
2027ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
2028ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
2029ef0921d6SKyungmin Park 	onenand_release_device(mtd);
2030ef0921d6SKyungmin Park 	return ret;
2031ef0921d6SKyungmin Park }
2032ef0921d6SKyungmin Park 
2033ef0921d6SKyungmin Park /**
2034ef0921d6SKyungmin Park  * onenand_unlock - [MTD Interface] Unlock block(s)
2035ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2036ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
2037ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
2038ef0921d6SKyungmin Park  *
2039ef0921d6SKyungmin Park  * Unlock one or more blocks
2040ef0921d6SKyungmin Park  */
2041ef0921d6SKyungmin Park static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
2042ef0921d6SKyungmin Park {
2043ef0921d6SKyungmin Park 	int ret;
2044ef0921d6SKyungmin Park 
2045ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
2046ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
2047ef0921d6SKyungmin Park 	onenand_release_device(mtd);
2048ef0921d6SKyungmin Park 	return ret;
2049ef0921d6SKyungmin Park }
20504fca3310SStefan Roese #endif
2051ef0921d6SKyungmin Park 
2052ef0921d6SKyungmin Park /**
2053ef0921d6SKyungmin Park  * onenand_check_lock_status - [OneNAND Interface] Check lock status
2054ef0921d6SKyungmin Park  * @param this          onenand chip data structure
2055ef0921d6SKyungmin Park  *
2056ef0921d6SKyungmin Park  * Check lock status
2057ef0921d6SKyungmin Park  */
2058ef0921d6SKyungmin Park static int onenand_check_lock_status(struct onenand_chip *this)
2059ef0921d6SKyungmin Park {
2060ef0921d6SKyungmin Park 	unsigned int value, block, status;
2061ef0921d6SKyungmin Park 	unsigned int end;
2062ef0921d6SKyungmin Park 
2063ef0921d6SKyungmin Park 	end = this->chipsize >> this->erase_shift;
2064ef0921d6SKyungmin Park 	for (block = 0; block < end; block++) {
2065ef0921d6SKyungmin Park 		/* Set block address */
2066ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
2067ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
2068ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
2069ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
2070ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
2071ef0921d6SKyungmin Park 		/* Set start block address */
2072ef0921d6SKyungmin Park 		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
2073ef0921d6SKyungmin Park 
2074ef0921d6SKyungmin Park 		/* Check lock status */
2075ef0921d6SKyungmin Park 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
2076ef0921d6SKyungmin Park 		if (!(status & ONENAND_WP_US)) {
2077ef0921d6SKyungmin Park 			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
2078ef0921d6SKyungmin Park 			return 0;
2079ef0921d6SKyungmin Park 		}
2080ef0921d6SKyungmin Park 	}
2081ef0921d6SKyungmin Park 
2082ef0921d6SKyungmin Park 	return 1;
2083ef0921d6SKyungmin Park }
2084ef0921d6SKyungmin Park 
2085ef0921d6SKyungmin Park /**
2086ef0921d6SKyungmin Park  * onenand_unlock_all - [OneNAND Interface] unlock all blocks
2087ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2088ef0921d6SKyungmin Park  *
2089ef0921d6SKyungmin Park  * Unlock all blocks
2090ef0921d6SKyungmin Park  */
2091ef0921d6SKyungmin Park static void onenand_unlock_all(struct mtd_info *mtd)
2092ef0921d6SKyungmin Park {
2093ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
2094ef0921d6SKyungmin Park 	loff_t ofs = 0;
2095cacbe919SAmul Kumar Saha 	size_t len = mtd->size;
2096ef0921d6SKyungmin Park 
2097ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
2098ef0921d6SKyungmin Park 		/* Set start block address */
2099ef0921d6SKyungmin Park 		this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
2100ef0921d6SKyungmin Park 		/* Write unlock command */
2101ef0921d6SKyungmin Park 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
2102ef0921d6SKyungmin Park 
2103ef0921d6SKyungmin Park 		/* There's no return value */
2104ef0921d6SKyungmin Park 		this->wait(mtd, FL_LOCKING);
2105ef0921d6SKyungmin Park 
2106ef0921d6SKyungmin Park 		/* Sanity check */
2107ef0921d6SKyungmin Park 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
2108ef0921d6SKyungmin Park 				& ONENAND_CTRL_ONGO)
2109ef0921d6SKyungmin Park 			continue;
2110ef0921d6SKyungmin Park 
2111ef0921d6SKyungmin Park 		/* Check lock status */
2112ef0921d6SKyungmin Park 		if (onenand_check_lock_status(this))
2113ef0921d6SKyungmin Park 			return;
2114ef0921d6SKyungmin Park 
2115ef0921d6SKyungmin Park 		/* Workaround for all block unlock in DDP */
2116cacbe919SAmul Kumar Saha 		if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
2117ef0921d6SKyungmin Park 			/* All blocks on another chip */
2118ef0921d6SKyungmin Park 			ofs = this->chipsize >> 1;
2119ef0921d6SKyungmin Park 			len = this->chipsize >> 1;
2120ef0921d6SKyungmin Park 		}
2121ef0921d6SKyungmin Park 	}
2122ef0921d6SKyungmin Park 
2123ef0921d6SKyungmin Park 	onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
2124ef0921d6SKyungmin Park }
2125ef0921d6SKyungmin Park 
2126ef0921d6SKyungmin Park 
2127ef0921d6SKyungmin Park /**
2128ef0921d6SKyungmin Park  * onenand_check_features - Check and set OneNAND features
2129ef0921d6SKyungmin Park  * @param mtd           MTD data structure
2130ef0921d6SKyungmin Park  *
2131ef0921d6SKyungmin Park  * Check and set OneNAND features
2132ef0921d6SKyungmin Park  * - lock scheme
2133ef0921d6SKyungmin Park  * - two plane
2134ef0921d6SKyungmin Park  */
2135ef0921d6SKyungmin Park static void onenand_check_features(struct mtd_info *mtd)
2136ef0921d6SKyungmin Park {
2137ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
2138ef0921d6SKyungmin Park 	unsigned int density, process;
2139ef0921d6SKyungmin Park 
2140ef0921d6SKyungmin Park 	/* Lock scheme depends on density and process */
2141ef0921d6SKyungmin Park 	density = onenand_get_density(this->device_id);
2142ef0921d6SKyungmin Park 	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
2143ef0921d6SKyungmin Park 
2144ef0921d6SKyungmin Park 	/* Lock scheme */
2145ef0921d6SKyungmin Park 	switch (density) {
2146ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_4Gb:
2147*e26fd3d3SLukasz Majewski 		if (ONENAND_IS_DDP(this))
2148ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
2149*e26fd3d3SLukasz Majewski 		else
2150*e26fd3d3SLukasz Majewski 			this->options |= ONENAND_HAS_4KB_PAGE;
2151ef0921d6SKyungmin Park 
2152ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_2Gb:
2153ef0921d6SKyungmin Park 		/* 2Gb DDP don't have 2 plane */
2154ef0921d6SKyungmin Park 		if (!ONENAND_IS_DDP(this))
2155ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
2156ef0921d6SKyungmin Park 		this->options |= ONENAND_HAS_UNLOCK_ALL;
2157ef0921d6SKyungmin Park 
2158ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_1Gb:
2159ef0921d6SKyungmin Park 		/* A-Die has all block unlock */
2160ef0921d6SKyungmin Park 		if (process)
2161ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_UNLOCK_ALL;
2162ef0921d6SKyungmin Park 		break;
2163ef0921d6SKyungmin Park 
2164ef0921d6SKyungmin Park 	default:
2165ef0921d6SKyungmin Park 		/* Some OneNAND has continuous lock scheme */
2166ef0921d6SKyungmin Park 		if (!process)
2167ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_CONT_LOCK;
2168ef0921d6SKyungmin Park 		break;
2169ef0921d6SKyungmin Park 	}
2170ef0921d6SKyungmin Park 
2171cacbe919SAmul Kumar Saha 	if (ONENAND_IS_MLC(this))
2172*e26fd3d3SLukasz Majewski 		this->options |= ONENAND_HAS_4KB_PAGE;
2173*e26fd3d3SLukasz Majewski 
2174*e26fd3d3SLukasz Majewski 	if (ONENAND_IS_4KB_PAGE(this))
2175cacbe919SAmul Kumar Saha 		this->options &= ~ONENAND_HAS_2PLANE;
2176cacbe919SAmul Kumar Saha 
2177cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
2178cacbe919SAmul Kumar Saha 		this->options &= ~ONENAND_HAS_CONT_LOCK;
2179cacbe919SAmul Kumar Saha 		this->options |= ONENAND_HAS_UNLOCK_ALL;
2180cacbe919SAmul Kumar Saha 	}
2181cacbe919SAmul Kumar Saha 
2182ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK)
2183ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
2184ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL)
2185ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip support all block unlock\n");
2186ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_2PLANE)
2187ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip has 2 plane\n");
2188*e26fd3d3SLukasz Majewski 	if (this->options & ONENAND_HAS_4KB_PAGE)
2189*e26fd3d3SLukasz Majewski 		printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
2190*e26fd3d3SLukasz Majewski 
2191ef0921d6SKyungmin Park }
2192ef0921d6SKyungmin Park 
2193ef0921d6SKyungmin Park /**
219459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_print_device_info - Print device ID
219559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device        device ID
219659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
219759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Print device ID
219859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
2199ef0921d6SKyungmin Park char *onenand_print_device_info(int device, int version)
220059829cc1SJean-Christophe PLAGNIOL-VILLARD {
2201cacbe919SAmul Kumar Saha 	int vcc, demuxed, ddp, density, flexonenand;
2202195ccfc5SFathi BOUDRA 	char *dev_info = malloc(80);
2203ef0921d6SKyungmin Park 	char *p = dev_info;
220459829cc1SJean-Christophe PLAGNIOL-VILLARD 
220559829cc1SJean-Christophe PLAGNIOL-VILLARD 	vcc = device & ONENAND_DEVICE_VCC_MASK;
220659829cc1SJean-Christophe PLAGNIOL-VILLARD 	demuxed = device & ONENAND_DEVICE_IS_DEMUX;
220759829cc1SJean-Christophe PLAGNIOL-VILLARD 	ddp = device & ONENAND_DEVICE_IS_DDP;
2208cacbe919SAmul Kumar Saha 	density = onenand_get_density(device);
2209cacbe919SAmul Kumar Saha 	flexonenand = device & DEVICE_IS_FLEXONENAND;
2210cacbe919SAmul Kumar Saha 	p += sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
221159829cc1SJean-Christophe PLAGNIOL-VILLARD 	       demuxed ? "" : "Muxed ",
2212cacbe919SAmul Kumar Saha 	       flexonenand ? "Flex-" : "",
221359829cc1SJean-Christophe PLAGNIOL-VILLARD 	       ddp ? "(DDP)" : "",
221459829cc1SJean-Christophe PLAGNIOL-VILLARD 	       (16 << density), vcc ? "2.65/3.3" : "1.8", device);
2215195ccfc5SFathi BOUDRA 
2216ef0921d6SKyungmin Park 	sprintf(p, "\nOneNAND version = 0x%04x", version);
2217ef0921d6SKyungmin Park 	printk("%s\n", dev_info);
2218ef0921d6SKyungmin Park 
2219195ccfc5SFathi BOUDRA 	return dev_info;
222059829cc1SJean-Christophe PLAGNIOL-VILLARD }
222159829cc1SJean-Christophe PLAGNIOL-VILLARD 
222259829cc1SJean-Christophe PLAGNIOL-VILLARD static const struct onenand_manufacturers onenand_manuf_ids[] = {
2223456be17dSEnric Balletbo i Serra 	{ONENAND_MFR_NUMONYX, "Numonyx"},
222459829cc1SJean-Christophe PLAGNIOL-VILLARD 	{ONENAND_MFR_SAMSUNG, "Samsung"},
222559829cc1SJean-Christophe PLAGNIOL-VILLARD };
222659829cc1SJean-Christophe PLAGNIOL-VILLARD 
222759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
222859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_maf - Check manufacturer ID
222959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param manuf         manufacturer ID
223059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
223159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check manufacturer ID
223259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
223359829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_maf(int manuf)
223459829cc1SJean-Christophe PLAGNIOL-VILLARD {
2235ef0921d6SKyungmin Park 	int size = ARRAY_SIZE(onenand_manuf_ids);
223659829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i;
223724ccca5eSMarek Vasut #ifdef ONENAND_DEBUG
223824ccca5eSMarek Vasut 	char *name;
223924ccca5eSMarek Vasut #endif
224059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2241cacbe919SAmul Kumar Saha 	for (i = 0; i < size; i++)
224259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (manuf == onenand_manuf_ids[i].id)
224359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
2244ef0921d6SKyungmin Park 
224524ccca5eSMarek Vasut #ifdef ONENAND_DEBUG
2246ef0921d6SKyungmin Park 	if (i < size)
2247ef0921d6SKyungmin Park 		name = onenand_manuf_ids[i].name;
2248ef0921d6SKyungmin Park 	else
2249ef0921d6SKyungmin Park 		name = "Unknown";
225059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2251ef0921d6SKyungmin Park 	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
225259829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
225359829cc1SJean-Christophe PLAGNIOL-VILLARD 
2254ef0921d6SKyungmin Park 	return i == size;
225559829cc1SJean-Christophe PLAGNIOL-VILLARD }
225659829cc1SJean-Christophe PLAGNIOL-VILLARD 
225759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
2258cacbe919SAmul Kumar Saha * flexonenand_get_boundary	- Reads the SLC boundary
2259cacbe919SAmul Kumar Saha * @param onenand_info		- onenand info structure
2260cacbe919SAmul Kumar Saha *
2261cacbe919SAmul Kumar Saha * Fill up boundary[] field in onenand_chip
2262cacbe919SAmul Kumar Saha **/
2263cacbe919SAmul Kumar Saha static int flexonenand_get_boundary(struct mtd_info *mtd)
2264cacbe919SAmul Kumar Saha {
2265cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2266cacbe919SAmul Kumar Saha 	unsigned int die, bdry;
226724ccca5eSMarek Vasut 	int syscfg, locked;
2268cacbe919SAmul Kumar Saha 
2269cacbe919SAmul Kumar Saha 	/* Disable ECC */
2270cacbe919SAmul Kumar Saha 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
2271cacbe919SAmul Kumar Saha 	this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
2272cacbe919SAmul Kumar Saha 
2273cacbe919SAmul Kumar Saha 	for (die = 0; die < this->dies; die++) {
2274cacbe919SAmul Kumar Saha 		this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
2275cacbe919SAmul Kumar Saha 		this->wait(mtd, FL_SYNCING);
2276cacbe919SAmul Kumar Saha 
2277cacbe919SAmul Kumar Saha 		this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
227824ccca5eSMarek Vasut 		this->wait(mtd, FL_READING);
2279cacbe919SAmul Kumar Saha 
2280cacbe919SAmul Kumar Saha 		bdry = this->read_word(this->base + ONENAND_DATARAM);
2281cacbe919SAmul Kumar Saha 		if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
2282cacbe919SAmul Kumar Saha 			locked = 0;
2283cacbe919SAmul Kumar Saha 		else
2284cacbe919SAmul Kumar Saha 			locked = 1;
2285cacbe919SAmul Kumar Saha 		this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
2286cacbe919SAmul Kumar Saha 
2287cacbe919SAmul Kumar Saha 		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
228824ccca5eSMarek Vasut 		this->wait(mtd, FL_RESETING);
2289cacbe919SAmul Kumar Saha 
2290cacbe919SAmul Kumar Saha 		printk(KERN_INFO "Die %d boundary: %d%s\n", die,
2291cacbe919SAmul Kumar Saha 		       this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
2292cacbe919SAmul Kumar Saha 	}
2293cacbe919SAmul Kumar Saha 
2294cacbe919SAmul Kumar Saha 	/* Enable ECC */
2295cacbe919SAmul Kumar Saha 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
2296cacbe919SAmul Kumar Saha 	return 0;
2297cacbe919SAmul Kumar Saha }
2298cacbe919SAmul Kumar Saha 
2299cacbe919SAmul Kumar Saha /**
2300cacbe919SAmul Kumar Saha  * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
2301cacbe919SAmul Kumar Saha  * 			  boundary[], diesize[], mtd->size, mtd->erasesize,
2302cacbe919SAmul Kumar Saha  * 			  mtd->eraseregions
2303cacbe919SAmul Kumar Saha  * @param mtd		- MTD device structure
2304cacbe919SAmul Kumar Saha  */
2305cacbe919SAmul Kumar Saha static void flexonenand_get_size(struct mtd_info *mtd)
2306cacbe919SAmul Kumar Saha {
2307cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2308cacbe919SAmul Kumar Saha 	int die, i, eraseshift, density;
2309cacbe919SAmul Kumar Saha 	int blksperdie, maxbdry;
2310cacbe919SAmul Kumar Saha 	loff_t ofs;
2311cacbe919SAmul Kumar Saha 
2312cacbe919SAmul Kumar Saha 	density = onenand_get_density(this->device_id);
2313cacbe919SAmul Kumar Saha 	blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
2314cacbe919SAmul Kumar Saha 	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
2315cacbe919SAmul Kumar Saha 	maxbdry = blksperdie - 1;
2316cacbe919SAmul Kumar Saha 	eraseshift = this->erase_shift - 1;
2317cacbe919SAmul Kumar Saha 
2318cacbe919SAmul Kumar Saha 	mtd->numeraseregions = this->dies << 1;
2319cacbe919SAmul Kumar Saha 
2320cacbe919SAmul Kumar Saha 	/* This fills up the device boundary */
2321cacbe919SAmul Kumar Saha 	flexonenand_get_boundary(mtd);
2322cacbe919SAmul Kumar Saha 	die = 0;
2323cacbe919SAmul Kumar Saha 	ofs = 0;
2324cacbe919SAmul Kumar Saha 	i = -1;
2325cacbe919SAmul Kumar Saha 	for (; die < this->dies; die++) {
2326cacbe919SAmul Kumar Saha 		if (!die || this->boundary[die-1] != maxbdry) {
2327cacbe919SAmul Kumar Saha 			i++;
2328cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].offset = ofs;
2329cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize = 1 << eraseshift;
2330cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks =
2331cacbe919SAmul Kumar Saha 							this->boundary[die] + 1;
2332cacbe919SAmul Kumar Saha 			ofs += mtd->eraseregions[i].numblocks << eraseshift;
2333cacbe919SAmul Kumar Saha 			eraseshift++;
2334cacbe919SAmul Kumar Saha 		} else {
2335cacbe919SAmul Kumar Saha 			mtd->numeraseregions -= 1;
2336cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks +=
2337cacbe919SAmul Kumar Saha 							this->boundary[die] + 1;
2338cacbe919SAmul Kumar Saha 			ofs += (this->boundary[die] + 1) << (eraseshift - 1);
2339cacbe919SAmul Kumar Saha 		}
2340cacbe919SAmul Kumar Saha 		if (this->boundary[die] != maxbdry) {
2341cacbe919SAmul Kumar Saha 			i++;
2342cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].offset = ofs;
2343cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize = 1 << eraseshift;
2344cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks = maxbdry ^
2345cacbe919SAmul Kumar Saha 							 this->boundary[die];
2346cacbe919SAmul Kumar Saha 			ofs += mtd->eraseregions[i].numblocks << eraseshift;
2347cacbe919SAmul Kumar Saha 			eraseshift--;
2348cacbe919SAmul Kumar Saha 		} else
2349cacbe919SAmul Kumar Saha 			mtd->numeraseregions -= 1;
2350cacbe919SAmul Kumar Saha 	}
2351cacbe919SAmul Kumar Saha 
2352cacbe919SAmul Kumar Saha 	/* Expose MLC erase size except when all blocks are SLC */
2353cacbe919SAmul Kumar Saha 	mtd->erasesize = 1 << this->erase_shift;
2354cacbe919SAmul Kumar Saha 	if (mtd->numeraseregions == 1)
2355cacbe919SAmul Kumar Saha 		mtd->erasesize >>= 1;
2356cacbe919SAmul Kumar Saha 
2357cacbe919SAmul Kumar Saha 	printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
2358cacbe919SAmul Kumar Saha 	for (i = 0; i < mtd->numeraseregions; i++)
2359cacbe919SAmul Kumar Saha 		printk(KERN_INFO "[offset: 0x%08llx, erasesize: 0x%05x,"
2360cacbe919SAmul Kumar Saha 			" numblocks: %04u]\n", mtd->eraseregions[i].offset,
2361cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize,
2362cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks);
2363cacbe919SAmul Kumar Saha 
2364cacbe919SAmul Kumar Saha 	for (die = 0, mtd->size = 0; die < this->dies; die++) {
2365cacbe919SAmul Kumar Saha 		this->diesize[die] = (loff_t) (blksperdie << this->erase_shift);
2366cacbe919SAmul Kumar Saha 		this->diesize[die] -= (loff_t) (this->boundary[die] + 1)
2367cacbe919SAmul Kumar Saha 						 << (this->erase_shift - 1);
2368cacbe919SAmul Kumar Saha 		mtd->size += this->diesize[die];
2369cacbe919SAmul Kumar Saha 	}
2370cacbe919SAmul Kumar Saha }
2371cacbe919SAmul Kumar Saha 
2372cacbe919SAmul Kumar Saha /**
2373cacbe919SAmul Kumar Saha  * flexonenand_check_blocks_erased - Check if blocks are erased
2374cacbe919SAmul Kumar Saha  * @param mtd_info	- mtd info structure
2375cacbe919SAmul Kumar Saha  * @param start		- first erase block to check
2376cacbe919SAmul Kumar Saha  * @param end		- last erase block to check
2377cacbe919SAmul Kumar Saha  *
2378cacbe919SAmul Kumar Saha  * Converting an unerased block from MLC to SLC
2379cacbe919SAmul Kumar Saha  * causes byte values to change. Since both data and its ECC
2380cacbe919SAmul Kumar Saha  * have changed, reads on the block give uncorrectable error.
2381cacbe919SAmul Kumar Saha  * This might lead to the block being detected as bad.
2382cacbe919SAmul Kumar Saha  *
2383cacbe919SAmul Kumar Saha  * Avoid this by ensuring that the block to be converted is
2384cacbe919SAmul Kumar Saha  * erased.
2385cacbe919SAmul Kumar Saha  */
2386cacbe919SAmul Kumar Saha static int flexonenand_check_blocks_erased(struct mtd_info *mtd,
2387cacbe919SAmul Kumar Saha 					int start, int end)
2388cacbe919SAmul Kumar Saha {
2389cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2390cacbe919SAmul Kumar Saha 	int i, ret;
2391cacbe919SAmul Kumar Saha 	int block;
2392cacbe919SAmul Kumar Saha 	struct mtd_oob_ops ops = {
2393cacbe919SAmul Kumar Saha 		.mode = MTD_OOB_PLACE,
2394cacbe919SAmul Kumar Saha 		.ooboffs = 0,
2395cacbe919SAmul Kumar Saha 		.ooblen	= mtd->oobsize,
2396cacbe919SAmul Kumar Saha 		.datbuf	= NULL,
2397cacbe919SAmul Kumar Saha 		.oobbuf	= this->oob_buf,
2398cacbe919SAmul Kumar Saha 	};
2399cacbe919SAmul Kumar Saha 	loff_t addr;
2400cacbe919SAmul Kumar Saha 
2401cacbe919SAmul Kumar Saha 	printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
2402cacbe919SAmul Kumar Saha 
2403cacbe919SAmul Kumar Saha 	for (block = start; block <= end; block++) {
2404cacbe919SAmul Kumar Saha 		addr = flexonenand_addr(this, block);
2405cacbe919SAmul Kumar Saha 		if (onenand_block_isbad_nolock(mtd, addr, 0))
2406cacbe919SAmul Kumar Saha 			continue;
2407cacbe919SAmul Kumar Saha 
2408cacbe919SAmul Kumar Saha 		/*
2409cacbe919SAmul Kumar Saha 		 * Since main area write results in ECC write to spare,
2410cacbe919SAmul Kumar Saha 		 * it is sufficient to check only ECC bytes for change.
2411cacbe919SAmul Kumar Saha 		 */
2412cacbe919SAmul Kumar Saha 		ret = onenand_read_oob_nolock(mtd, addr, &ops);
2413cacbe919SAmul Kumar Saha 		if (ret)
2414cacbe919SAmul Kumar Saha 			return ret;
2415cacbe919SAmul Kumar Saha 
2416cacbe919SAmul Kumar Saha 		for (i = 0; i < mtd->oobsize; i++)
2417cacbe919SAmul Kumar Saha 			if (this->oob_buf[i] != 0xff)
2418cacbe919SAmul Kumar Saha 				break;
2419cacbe919SAmul Kumar Saha 
2420cacbe919SAmul Kumar Saha 		if (i != mtd->oobsize) {
2421cacbe919SAmul Kumar Saha 			printk(KERN_WARNING "Block %d not erased.\n", block);
2422cacbe919SAmul Kumar Saha 			return 1;
2423cacbe919SAmul Kumar Saha 		}
2424cacbe919SAmul Kumar Saha 	}
2425cacbe919SAmul Kumar Saha 
2426cacbe919SAmul Kumar Saha 	return 0;
2427cacbe919SAmul Kumar Saha }
2428cacbe919SAmul Kumar Saha 
2429cacbe919SAmul Kumar Saha /**
2430cacbe919SAmul Kumar Saha  * flexonenand_set_boundary	- Writes the SLC boundary
2431cacbe919SAmul Kumar Saha  * @param mtd			- mtd info structure
2432cacbe919SAmul Kumar Saha  */
2433cacbe919SAmul Kumar Saha int flexonenand_set_boundary(struct mtd_info *mtd, int die,
2434cacbe919SAmul Kumar Saha 				    int boundary, int lock)
2435cacbe919SAmul Kumar Saha {
2436cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2437cacbe919SAmul Kumar Saha 	int ret, density, blksperdie, old, new, thisboundary;
2438cacbe919SAmul Kumar Saha 	loff_t addr;
2439cacbe919SAmul Kumar Saha 
2440cacbe919SAmul Kumar Saha 	if (die >= this->dies)
2441cacbe919SAmul Kumar Saha 		return -EINVAL;
2442cacbe919SAmul Kumar Saha 
2443cacbe919SAmul Kumar Saha 	if (boundary == this->boundary[die])
2444cacbe919SAmul Kumar Saha 		return 0;
2445cacbe919SAmul Kumar Saha 
2446cacbe919SAmul Kumar Saha 	density = onenand_get_density(this->device_id);
2447cacbe919SAmul Kumar Saha 	blksperdie = ((16 << density) << 20) >> this->erase_shift;
2448cacbe919SAmul Kumar Saha 	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
2449cacbe919SAmul Kumar Saha 
2450cacbe919SAmul Kumar Saha 	if (boundary >= blksperdie) {
2451cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2452cacbe919SAmul Kumar Saha 			"Invalid boundary value. "
2453cacbe919SAmul Kumar Saha 			"Boundary not changed.\n");
2454cacbe919SAmul Kumar Saha 		return -EINVAL;
2455cacbe919SAmul Kumar Saha 	}
2456cacbe919SAmul Kumar Saha 
2457cacbe919SAmul Kumar Saha 	/* Check if converting blocks are erased */
2458cacbe919SAmul Kumar Saha 	old = this->boundary[die] + (die * this->density_mask);
2459cacbe919SAmul Kumar Saha 	new = boundary + (die * this->density_mask);
2460cacbe919SAmul Kumar Saha 	ret = flexonenand_check_blocks_erased(mtd, min(old, new)
2461cacbe919SAmul Kumar Saha 						+ 1, max(old, new));
2462cacbe919SAmul Kumar Saha 	if (ret) {
2463cacbe919SAmul Kumar Saha 		printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n");
2464cacbe919SAmul Kumar Saha 		return ret;
2465cacbe919SAmul Kumar Saha 	}
2466cacbe919SAmul Kumar Saha 
2467cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
2468cacbe919SAmul Kumar Saha 	this->wait(mtd, FL_SYNCING);
2469cacbe919SAmul Kumar Saha 
2470cacbe919SAmul Kumar Saha 	/* Check is boundary is locked */
2471cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
2472cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_READING);
2473cacbe919SAmul Kumar Saha 
2474cacbe919SAmul Kumar Saha 	thisboundary = this->read_word(this->base + ONENAND_DATARAM);
2475cacbe919SAmul Kumar Saha 	if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
2476cacbe919SAmul Kumar Saha 		printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n");
2477cacbe919SAmul Kumar Saha 		goto out;
2478cacbe919SAmul Kumar Saha 	}
2479cacbe919SAmul Kumar Saha 
2480cacbe919SAmul Kumar Saha 	printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n",
2481cacbe919SAmul Kumar Saha 			die, boundary, lock ? "(Locked)" : "(Unlocked)");
2482cacbe919SAmul Kumar Saha 
2483cacbe919SAmul Kumar Saha 	boundary &= FLEXONENAND_PI_MASK;
2484cacbe919SAmul Kumar Saha 	boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
2485cacbe919SAmul Kumar Saha 
2486cacbe919SAmul Kumar Saha 	addr = die ? this->diesize[0] : 0;
2487cacbe919SAmul Kumar Saha 	this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
2488cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_ERASING);
2489cacbe919SAmul Kumar Saha 	if (ret) {
2490cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2491cacbe919SAmul Kumar Saha 			"Failed PI erase for Die %d\n", die);
2492cacbe919SAmul Kumar Saha 		goto out;
2493cacbe919SAmul Kumar Saha 	}
2494cacbe919SAmul Kumar Saha 
2495cacbe919SAmul Kumar Saha 	this->write_word(boundary, this->base + ONENAND_DATARAM);
2496cacbe919SAmul Kumar Saha 	this->command(mtd, ONENAND_CMD_PROG, addr, 0);
2497cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_WRITING);
2498cacbe919SAmul Kumar Saha 	if (ret) {
2499cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2500cacbe919SAmul Kumar Saha 			"Failed PI write for Die %d\n", die);
2501cacbe919SAmul Kumar Saha 		goto out;
2502cacbe919SAmul Kumar Saha 	}
2503cacbe919SAmul Kumar Saha 
2504cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
2505cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_WRITING);
2506cacbe919SAmul Kumar Saha out:
2507cacbe919SAmul Kumar Saha 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
2508cacbe919SAmul Kumar Saha 	this->wait(mtd, FL_RESETING);
2509cacbe919SAmul Kumar Saha 	if (!ret)
2510cacbe919SAmul Kumar Saha 		/* Recalculate device size on boundary change*/
2511cacbe919SAmul Kumar Saha 		flexonenand_get_size(mtd);
2512cacbe919SAmul Kumar Saha 
2513cacbe919SAmul Kumar Saha 	return ret;
2514cacbe919SAmul Kumar Saha }
2515cacbe919SAmul Kumar Saha 
2516cacbe919SAmul Kumar Saha /**
25176b3967bbSLukasz Majewski  * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND chip
251859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
251959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
252059829cc1SJean-Christophe PLAGNIOL-VILLARD  * OneNAND detection method:
252159829cc1SJean-Christophe PLAGNIOL-VILLARD  *   Compare the the values from command with ones from register
252259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
25236b3967bbSLukasz Majewski static int onenand_chip_probe(struct mtd_info *mtd)
252459829cc1SJean-Christophe PLAGNIOL-VILLARD {
252559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
25266b3967bbSLukasz Majewski 	int bram_maf_id, bram_dev_id, maf_id, dev_id;
2527ef0921d6SKyungmin Park 	int syscfg;
2528ef0921d6SKyungmin Park 
2529ef0921d6SKyungmin Park 	/* Save system configuration 1 */
2530ef0921d6SKyungmin Park 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
25316b3967bbSLukasz Majewski 
2532ef0921d6SKyungmin Park 	/* Clear Sync. Burst Read mode to read BootRAM */
25336b3967bbSLukasz Majewski 	this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ),
25346b3967bbSLukasz Majewski 			 this->base + ONENAND_REG_SYS_CFG1);
253559829cc1SJean-Christophe PLAGNIOL-VILLARD 
253659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Send the command for reading device ID from BootRAM */
253759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
253859829cc1SJean-Christophe PLAGNIOL-VILLARD 
253959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from BootRAM */
254059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
254159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
254259829cc1SJean-Christophe PLAGNIOL-VILLARD 
254359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reset OneNAND to read default register values */
254459829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
254559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2546d438d508SKyungmin Park 	/* Wait reset */
2547d438d508SKyungmin Park 	this->wait(mtd, FL_RESETING);
254859829cc1SJean-Christophe PLAGNIOL-VILLARD 
2549ef0921d6SKyungmin Park 	/* Restore system configuration 1 */
2550ef0921d6SKyungmin Park 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
2551ef0921d6SKyungmin Park 
2552ef0921d6SKyungmin Park 	/* Check manufacturer ID */
2553ef0921d6SKyungmin Park 	if (onenand_check_maf(bram_maf_id))
2554ef0921d6SKyungmin Park 		return -ENXIO;
2555ef0921d6SKyungmin Park 
255659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from Register */
255759829cc1SJean-Christophe PLAGNIOL-VILLARD 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
255859829cc1SJean-Christophe PLAGNIOL-VILLARD 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
255959829cc1SJean-Christophe PLAGNIOL-VILLARD 
256059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Check OneNAND device */
256159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (maf_id != bram_maf_id || dev_id != bram_dev_id)
256259829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
256359829cc1SJean-Christophe PLAGNIOL-VILLARD 
25646b3967bbSLukasz Majewski 	return 0;
25656b3967bbSLukasz Majewski }
25666b3967bbSLukasz Majewski 
25676b3967bbSLukasz Majewski /**
25686b3967bbSLukasz Majewski  * onenand_probe - [OneNAND Interface] Probe the OneNAND device
25696b3967bbSLukasz Majewski  * @param mtd		MTD device structure
25706b3967bbSLukasz Majewski  *
25716b3967bbSLukasz Majewski  * OneNAND detection method:
25726b3967bbSLukasz Majewski  *   Compare the the values from command with ones from register
25736b3967bbSLukasz Majewski  */
25746b3967bbSLukasz Majewski int onenand_probe(struct mtd_info *mtd)
25756b3967bbSLukasz Majewski {
25766b3967bbSLukasz Majewski 	struct onenand_chip *this = mtd->priv;
25776b3967bbSLukasz Majewski 	int maf_id, dev_id, ver_id;
25786b3967bbSLukasz Majewski 	int density;
25796b3967bbSLukasz Majewski 	int ret;
25806b3967bbSLukasz Majewski 
25816b3967bbSLukasz Majewski 	ret = this->chip_probe(mtd);
25826b3967bbSLukasz Majewski 	if (ret)
25836b3967bbSLukasz Majewski 		return ret;
25846b3967bbSLukasz Majewski 
25856b3967bbSLukasz Majewski 	/* Read manufacturer and device IDs from Register */
25866b3967bbSLukasz Majewski 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
25876b3967bbSLukasz Majewski 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
25886b3967bbSLukasz Majewski 	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
25896b3967bbSLukasz Majewski 	this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
25906b3967bbSLukasz Majewski 
259159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash device information */
2592ef0921d6SKyungmin Park 	mtd->name = onenand_print_device_info(dev_id, ver_id);
259359829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->device_id = dev_id;
25948cf11f3aSStefan Roese 	this->version_id = ver_id;
259559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2596*e26fd3d3SLukasz Majewski 	/* Check OneNAND features */
2597*e26fd3d3SLukasz Majewski 	onenand_check_features(mtd);
2598*e26fd3d3SLukasz Majewski 
2599ef0921d6SKyungmin Park 	density = onenand_get_density(dev_id);
2600cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
2601cacbe919SAmul Kumar Saha 		this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
2602cacbe919SAmul Kumar Saha 		/* Maximum possible erase regions */
2603cacbe919SAmul Kumar Saha 		mtd->numeraseregions = this->dies << 1;
2604cacbe919SAmul Kumar Saha 		mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info)
2605cacbe919SAmul Kumar Saha 					* (this->dies << 1));
2606cacbe919SAmul Kumar Saha 		if (!mtd->eraseregions)
2607cacbe919SAmul Kumar Saha 			return -ENOMEM;
2608cacbe919SAmul Kumar Saha 	}
2609cacbe919SAmul Kumar Saha 
2610cacbe919SAmul Kumar Saha 	/*
2611cacbe919SAmul Kumar Saha 	 * For Flex-OneNAND, chipsize represents maximum possible device size.
2612cacbe919SAmul Kumar Saha 	 * mtd->size represents the actual device size.
2613cacbe919SAmul Kumar Saha 	 */
261459829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->chipsize = (16 << density) << 20;
261559829cc1SJean-Christophe PLAGNIOL-VILLARD 
261659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* OneNAND page size & block size */
261759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* The data buffer size is equal to page size */
2618d438d508SKyungmin Park 	mtd->writesize =
261959829cc1SJean-Christophe PLAGNIOL-VILLARD 	    this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
2620cacbe919SAmul Kumar Saha 	/* We use the full BufferRAM */
2621*e26fd3d3SLukasz Majewski 	if (ONENAND_IS_4KB_PAGE(this))
2622cacbe919SAmul Kumar Saha 		mtd->writesize <<= 1;
2623cacbe919SAmul Kumar Saha 
2624d438d508SKyungmin Park 	mtd->oobsize = mtd->writesize >> 5;
262559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Pagers per block is always 64 in OneNAND */
2626d438d508SKyungmin Park 	mtd->erasesize = mtd->writesize << 6;
2627cacbe919SAmul Kumar Saha 	/*
2628cacbe919SAmul Kumar Saha 	 * Flex-OneNAND SLC area has 64 pages per block.
2629cacbe919SAmul Kumar Saha 	 * Flex-OneNAND MLC area has 128 pages per block.
2630cacbe919SAmul Kumar Saha 	 * Expose MLC erase size to find erase_shift and page_mask.
2631cacbe919SAmul Kumar Saha 	 */
2632cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this))
2633cacbe919SAmul Kumar Saha 		mtd->erasesize <<= 1;
263459829cc1SJean-Christophe PLAGNIOL-VILLARD 
263559829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->erase_shift = ffs(mtd->erasesize) - 1;
2636d438d508SKyungmin Park 	this->page_shift = ffs(mtd->writesize) - 1;
263759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->ppb_shift = (this->erase_shift - this->page_shift);
2638d438d508SKyungmin Park 	this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
2639cacbe919SAmul Kumar Saha 	/* Set density mask. it is used for DDP */
2640cacbe919SAmul Kumar Saha 	if (ONENAND_IS_DDP(this))
2641cacbe919SAmul Kumar Saha 		this->density_mask = this->chipsize >> (this->erase_shift + 1);
2642bfd7f386SKyungmin Park 	/* It's real page size */
2643bfd7f386SKyungmin Park 	this->writesize = mtd->writesize;
264459829cc1SJean-Christophe PLAGNIOL-VILLARD 
264559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* REVIST: Multichip handling */
264659829cc1SJean-Christophe PLAGNIOL-VILLARD 
2647cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this))
2648cacbe919SAmul Kumar Saha 		flexonenand_get_size(mtd);
2649cacbe919SAmul Kumar Saha 	else
265059829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd->size = this->chipsize;
265159829cc1SJean-Christophe PLAGNIOL-VILLARD 
2652d438d508SKyungmin Park 	mtd->flags = MTD_CAP_NANDFLASH;
2653195ccfc5SFathi BOUDRA 	mtd->erase = onenand_erase;
2654195ccfc5SFathi BOUDRA 	mtd->read = onenand_read;
2655195ccfc5SFathi BOUDRA 	mtd->write = onenand_write;
2656195ccfc5SFathi BOUDRA 	mtd->read_oob = onenand_read_oob;
2657195ccfc5SFathi BOUDRA 	mtd->write_oob = onenand_write_oob;
2658195ccfc5SFathi BOUDRA 	mtd->sync = onenand_sync;
2659195ccfc5SFathi BOUDRA 	mtd->block_isbad = onenand_block_isbad;
2660195ccfc5SFathi BOUDRA 	mtd->block_markbad = onenand_block_markbad;
2661195ccfc5SFathi BOUDRA 
266259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
266359829cc1SJean-Christophe PLAGNIOL-VILLARD }
266459829cc1SJean-Christophe PLAGNIOL-VILLARD 
266559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
266659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
266759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
266859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param maxchips	Number of chips to scan for
266959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
267059829cc1SJean-Christophe PLAGNIOL-VILLARD  * This fills out all the not initialized function pointers
267159829cc1SJean-Christophe PLAGNIOL-VILLARD  * with the defaults.
267259829cc1SJean-Christophe PLAGNIOL-VILLARD  * The flash ID is read and the mtd/chip structures are
267359829cc1SJean-Christophe PLAGNIOL-VILLARD  * filled with the appropriate values.
267459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
267559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_scan(struct mtd_info *mtd, int maxchips)
267659829cc1SJean-Christophe PLAGNIOL-VILLARD {
26771ae39862SStefan Roese 	int i;
267859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
267959829cc1SJean-Christophe PLAGNIOL-VILLARD 
268059829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_word)
268159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_word = onenand_readw;
268259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_word)
268359829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word = onenand_writew;
268459829cc1SJean-Christophe PLAGNIOL-VILLARD 
268559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->command)
268659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command = onenand_command;
268759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->wait)
268859829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait = onenand_wait;
2689ef0921d6SKyungmin Park 	if (!this->bbt_wait)
2690ef0921d6SKyungmin Park 		this->bbt_wait = onenand_bbt_wait;
269159829cc1SJean-Christophe PLAGNIOL-VILLARD 
269259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_bufferram)
269359829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_read_bufferram;
269459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_bufferram)
269559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_bufferram = onenand_write_bufferram;
269659829cc1SJean-Christophe PLAGNIOL-VILLARD 
26976b3967bbSLukasz Majewski 	if (!this->chip_probe)
26986b3967bbSLukasz Majewski 		this->chip_probe = onenand_chip_probe;
26996b3967bbSLukasz Majewski 
27001714f51aSKyungmin Park 	if (!this->block_markbad)
27011714f51aSKyungmin Park 		this->block_markbad = onenand_default_block_markbad;
2702ef0921d6SKyungmin Park 	if (!this->scan_bbt)
2703ef0921d6SKyungmin Park 		this->scan_bbt = onenand_default_bbt;
2704ef0921d6SKyungmin Park 
270559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (onenand_probe(mtd))
270659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
270759829cc1SJean-Christophe PLAGNIOL-VILLARD 
270859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Set Sync. Burst Read after probing */
270959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (this->mmcontrol) {
271059829cc1SJean-Christophe PLAGNIOL-VILLARD 		printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
271159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_sync_read_bufferram;
271259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
271359829cc1SJean-Christophe PLAGNIOL-VILLARD 
2714bfd7f386SKyungmin Park 	/* Allocate buffers, if necessary */
2715bfd7f386SKyungmin Park 	if (!this->page_buf) {
2716bfd7f386SKyungmin Park 		this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
2717bfd7f386SKyungmin Park 		if (!this->page_buf) {
2718bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
2719bfd7f386SKyungmin Park 			return -ENOMEM;
2720bfd7f386SKyungmin Park 		}
2721bfd7f386SKyungmin Park 		this->options |= ONENAND_PAGEBUF_ALLOC;
2722bfd7f386SKyungmin Park 	}
2723bfd7f386SKyungmin Park 	if (!this->oob_buf) {
2724bfd7f386SKyungmin Park 		this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
2725bfd7f386SKyungmin Park 		if (!this->oob_buf) {
2726bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n");
2727bfd7f386SKyungmin Park 			if (this->options & ONENAND_PAGEBUF_ALLOC) {
2728bfd7f386SKyungmin Park 				this->options &= ~ONENAND_PAGEBUF_ALLOC;
2729bfd7f386SKyungmin Park 				kfree(this->page_buf);
2730bfd7f386SKyungmin Park 			}
2731bfd7f386SKyungmin Park 			return -ENOMEM;
2732bfd7f386SKyungmin Park 		}
2733bfd7f386SKyungmin Park 		this->options |= ONENAND_OOBBUF_ALLOC;
2734bfd7f386SKyungmin Park 	}
2735bfd7f386SKyungmin Park 
27361ae39862SStefan Roese 	this->state = FL_READY;
27371ae39862SStefan Roese 
27381ae39862SStefan Roese 	/*
27391ae39862SStefan Roese 	 * Allow subpage writes up to oobsize.
27401ae39862SStefan Roese 	 */
27411ae39862SStefan Roese 	switch (mtd->oobsize) {
2742cacbe919SAmul Kumar Saha 	case 128:
2743cacbe919SAmul Kumar Saha 		this->ecclayout = &onenand_oob_128;
2744cacbe919SAmul Kumar Saha 		mtd->subpage_sft = 0;
2745cacbe919SAmul Kumar Saha 		break;
2746cacbe919SAmul Kumar Saha 
27471ae39862SStefan Roese 	case 64:
27481ae39862SStefan Roese 		this->ecclayout = &onenand_oob_64;
27491ae39862SStefan Roese 		mtd->subpage_sft = 2;
27501ae39862SStefan Roese 		break;
27511ae39862SStefan Roese 
27521ae39862SStefan Roese 	case 32:
27531ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
27541ae39862SStefan Roese 		mtd->subpage_sft = 1;
27551ae39862SStefan Roese 		break;
27561ae39862SStefan Roese 
27571ae39862SStefan Roese 	default:
27581ae39862SStefan Roese 		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
27591ae39862SStefan Roese 			mtd->oobsize);
27601ae39862SStefan Roese 		mtd->subpage_sft = 0;
27611ae39862SStefan Roese 		/* To prevent kernel oops */
27621ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
27631ae39862SStefan Roese 		break;
27641ae39862SStefan Roese 	}
27651ae39862SStefan Roese 
27661ae39862SStefan Roese 	this->subpagesize = mtd->writesize >> mtd->subpage_sft;
27671ae39862SStefan Roese 
27681ae39862SStefan Roese 	/*
27691ae39862SStefan Roese 	 * The number of bytes available for a client to place data into
27701ae39862SStefan Roese 	 * the out of band area
27711ae39862SStefan Roese 	 */
27721ae39862SStefan Roese 	this->ecclayout->oobavail = 0;
27731ae39862SStefan Roese 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
27741ae39862SStefan Roese 	    this->ecclayout->oobfree[i].length; i++)
27751ae39862SStefan Roese 		this->ecclayout->oobavail +=
27761ae39862SStefan Roese 			this->ecclayout->oobfree[i].length;
27771ae39862SStefan Roese 	mtd->oobavail = this->ecclayout->oobavail;
27781ae39862SStefan Roese 
27791ae39862SStefan Roese 	mtd->ecclayout = this->ecclayout;
27801ae39862SStefan Roese 
2781ef0921d6SKyungmin Park 	/* Unlock whole block */
2782ef0921d6SKyungmin Park 	onenand_unlock_all(mtd);
278359829cc1SJean-Christophe PLAGNIOL-VILLARD 
2784ef0921d6SKyungmin Park 	return this->scan_bbt(mtd);
278559829cc1SJean-Christophe PLAGNIOL-VILLARD }
278659829cc1SJean-Christophe PLAGNIOL-VILLARD 
278759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
278859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
278959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
279059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
279159829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_release(struct mtd_info *mtd)
279259829cc1SJean-Christophe PLAGNIOL-VILLARD {
279359829cc1SJean-Christophe PLAGNIOL-VILLARD }
2794