xref: /rk3399_rockchip-uboot/drivers/mtd/onenand/onenand_base.c (revision dfe64e2c89731a3f9950d7acd8681b68df2bae03)
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>
237b15e2bbSMike Frysinger #include <linux/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 
370e26fd3d3SLukasz 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:
398e26fd3d3SLukasz 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 	if (ONENAND_IS_2PLANE(this))
636ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
637ef0921d6SKyungmin Park 	else
638ef0921d6SKyungmin Park 		blockpage = (int) (addr >> this->page_shift);
63959829cc1SJean-Christophe PLAGNIOL-VILLARD 
64059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Is there valid data? */
641ef0921d6SKyungmin Park 	i = ONENAND_CURRENT_BUFFERRAM(this);
642ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
643ef0921d6SKyungmin Park 		found = 1;
644ef0921d6SKyungmin Park 	else {
645ef0921d6SKyungmin Park 		/* Check another BufferRAM */
646ef0921d6SKyungmin Park 		i = ONENAND_NEXT_BUFFERRAM(this);
647ef0921d6SKyungmin Park 		if (this->bufferram[i].blockpage == blockpage) {
648ef0921d6SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
649ef0921d6SKyungmin Park 			found = 1;
650ef0921d6SKyungmin Park 		}
651ef0921d6SKyungmin Park 	}
65259829cc1SJean-Christophe PLAGNIOL-VILLARD 
653ef0921d6SKyungmin Park 	if (found && ONENAND_IS_DDP(this)) {
654ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
655cacbe919SAmul Kumar Saha 		int block = onenand_block(this, addr);
656ef0921d6SKyungmin Park 		int value = onenand_bufferram_address(this, block);
657ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
658ef0921d6SKyungmin Park 	}
659ef0921d6SKyungmin Park 
660ef0921d6SKyungmin Park 	return found;
66159829cc1SJean-Christophe PLAGNIOL-VILLARD }
66259829cc1SJean-Christophe PLAGNIOL-VILLARD 
66359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
66459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_update_bufferram - [GENERIC] Update BufferRAM information
66559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
66659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to update
66759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param valid		valid flag
66859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
66959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Update BufferRAM information
67059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
67159829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
67259829cc1SJean-Christophe PLAGNIOL-VILLARD 				    int valid)
67359829cc1SJean-Christophe PLAGNIOL-VILLARD {
67459829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
675ef0921d6SKyungmin Park 	int blockpage;
676ef0921d6SKyungmin Park 	unsigned int i;
67759829cc1SJean-Christophe PLAGNIOL-VILLARD 
678ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
679ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
680ef0921d6SKyungmin Park 	else
681ef0921d6SKyungmin Park 		blockpage = (int)(addr >> this->page_shift);
68259829cc1SJean-Christophe PLAGNIOL-VILLARD 
683ef0921d6SKyungmin Park 	/* Invalidate another BufferRAM */
684ef0921d6SKyungmin Park 	i = ONENAND_NEXT_BUFFERRAM(this);
685ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
686ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
68759829cc1SJean-Christophe PLAGNIOL-VILLARD 
68859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Update BufferRAM */
68959829cc1SJean-Christophe PLAGNIOL-VILLARD 	i = ONENAND_CURRENT_BUFFERRAM(this);
690ef0921d6SKyungmin Park 	if (valid)
691ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = blockpage;
692ef0921d6SKyungmin Park 	else
693ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
69459829cc1SJean-Christophe PLAGNIOL-VILLARD 
69559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
69659829cc1SJean-Christophe PLAGNIOL-VILLARD }
69759829cc1SJean-Christophe PLAGNIOL-VILLARD 
69859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
699d438d508SKyungmin Park  * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
700d438d508SKyungmin Park  * @param mtd           MTD data structure
701d438d508SKyungmin Park  * @param addr          start address to invalidate
702d438d508SKyungmin Park  * @param len           length to invalidate
703d438d508SKyungmin Park  *
704d438d508SKyungmin Park  * Invalidate BufferRAM information
705d438d508SKyungmin Park  */
706d438d508SKyungmin Park static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
707d438d508SKyungmin Park 					 unsigned int len)
708d438d508SKyungmin Park {
709d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
710d438d508SKyungmin Park 	int i;
711d438d508SKyungmin Park 	loff_t end_addr = addr + len;
712d438d508SKyungmin Park 
713d438d508SKyungmin Park 	/* Invalidate BufferRAM */
714d438d508SKyungmin Park 	for (i = 0; i < MAX_BUFFERRAM; i++) {
715ef0921d6SKyungmin Park 		loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
716d438d508SKyungmin Park 
717d438d508SKyungmin Park 		if (buf_addr >= addr && buf_addr < end_addr)
718ef0921d6SKyungmin Park 			this->bufferram[i].blockpage = -1;
719d438d508SKyungmin Park 	}
720d438d508SKyungmin Park }
721d438d508SKyungmin Park 
722d438d508SKyungmin Park /**
72359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_get_device - [GENERIC] Get chip for selected access
72459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
72559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param new_state	the state which is requested
72659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
72759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Get the device and lock it for exclusive access
72859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
72959829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_get_device(struct mtd_info *mtd, int new_state)
73059829cc1SJean-Christophe PLAGNIOL-VILLARD {
73159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
73259829cc1SJean-Christophe PLAGNIOL-VILLARD }
73359829cc1SJean-Christophe PLAGNIOL-VILLARD 
73459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
73559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release_device - [GENERIC] release chip
73659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
73759829cc1SJean-Christophe PLAGNIOL-VILLARD  *
73859829cc1SJean-Christophe PLAGNIOL-VILLARD  * Deselect, release chip lock and wake up anyone waiting on the device
73959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
74059829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_release_device(struct mtd_info *mtd)
74159829cc1SJean-Christophe PLAGNIOL-VILLARD {
74259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
74359829cc1SJean-Christophe PLAGNIOL-VILLARD }
74459829cc1SJean-Christophe PLAGNIOL-VILLARD 
74559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
746*dfe64e2cSSergey Lapin  * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
74759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
748bfd7f386SKyungmin Park  * @param buf		destination address
749bfd7f386SKyungmin Park  * @param column	oob offset to read from
750bfd7f386SKyungmin Park  * @param thislen	oob length to read
75159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
752bfd7f386SKyungmin Park static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
753bfd7f386SKyungmin Park 					int column, int thislen)
75459829cc1SJean-Christophe PLAGNIOL-VILLARD {
75559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
756bfd7f386SKyungmin Park 	struct nand_oobfree *free;
757bfd7f386SKyungmin Park 	int readcol = column;
758bfd7f386SKyungmin Park 	int readend = column + thislen;
759bfd7f386SKyungmin Park 	int lastgap = 0;
760bfd7f386SKyungmin Park 	unsigned int i;
761bfd7f386SKyungmin Park 	uint8_t *oob_buf = this->oob_buf;
76259829cc1SJean-Christophe PLAGNIOL-VILLARD 
763bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
764bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
765bfd7f386SKyungmin Park 		if (readcol >= lastgap)
766bfd7f386SKyungmin Park 			readcol += free->offset - lastgap;
767bfd7f386SKyungmin Park 		if (readend >= lastgap)
768bfd7f386SKyungmin Park 			readend += free->offset - lastgap;
769bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
770bfd7f386SKyungmin Park 	}
771ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
772bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
773bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
774bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
775bfd7f386SKyungmin Park 		if (free->offset < readend && free_end > readcol) {
776bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,readcol);
777bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,readend);
778bfd7f386SKyungmin Park 			int n = ed - st;
779bfd7f386SKyungmin Park 			memcpy(buf, oob_buf + st, n);
780bfd7f386SKyungmin Park 			buf += n;
781bfd7f386SKyungmin Park 		} else if (column == 0)
782bfd7f386SKyungmin Park 			break;
783bfd7f386SKyungmin Park 	}
784bfd7f386SKyungmin Park 	return 0;
785bfd7f386SKyungmin Park }
786bfd7f386SKyungmin Park 
787bfd7f386SKyungmin Park /**
788cacbe919SAmul Kumar Saha  * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
789cacbe919SAmul Kumar Saha  * @param mtd		MTD device structure
790cacbe919SAmul Kumar Saha  * @param addr		address to recover
791cacbe919SAmul Kumar Saha  * @param status	return value from onenand_wait
792cacbe919SAmul Kumar Saha  *
793cacbe919SAmul Kumar Saha  * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
794cacbe919SAmul Kumar Saha  * lower page address and MSB page has higher page address in paired pages.
795cacbe919SAmul Kumar Saha  * If power off occurs during MSB page program, the paired LSB page data can
796cacbe919SAmul Kumar Saha  * become corrupt. LSB page recovery read is a way to read LSB page though page
797cacbe919SAmul Kumar Saha  * data are corrupted. When uncorrectable error occurs as a result of LSB page
798cacbe919SAmul Kumar Saha  * read after power up, issue LSB page recovery read.
799cacbe919SAmul Kumar Saha  */
800cacbe919SAmul Kumar Saha static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
801cacbe919SAmul Kumar Saha {
802cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
803cacbe919SAmul Kumar Saha 	int i;
804cacbe919SAmul Kumar Saha 
805cacbe919SAmul Kumar Saha 	/* Recovery is only for Flex-OneNAND */
806cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
807cacbe919SAmul Kumar Saha 		return status;
808cacbe919SAmul Kumar Saha 
809cacbe919SAmul Kumar Saha 	/* check if we failed due to uncorrectable error */
810*dfe64e2cSSergey Lapin 	if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
811cacbe919SAmul Kumar Saha 		return status;
812cacbe919SAmul Kumar Saha 
813cacbe919SAmul Kumar Saha 	/* check if address lies in MLC region */
814cacbe919SAmul Kumar Saha 	i = flexonenand_region(mtd, addr);
815cacbe919SAmul Kumar Saha 	if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
816cacbe919SAmul Kumar Saha 		return status;
817cacbe919SAmul Kumar Saha 
818cacbe919SAmul Kumar Saha 	printk("onenand_recover_lsb:"
819cacbe919SAmul Kumar Saha 		"Attempting to recover from uncorrectable read\n");
820cacbe919SAmul Kumar Saha 
821cacbe919SAmul Kumar Saha 	/* Issue the LSB page recovery command */
822cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
823cacbe919SAmul Kumar Saha 	return this->wait(mtd, FL_READING);
824cacbe919SAmul Kumar Saha }
825cacbe919SAmul Kumar Saha 
826cacbe919SAmul Kumar Saha /**
827bfd7f386SKyungmin Park  * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
828bfd7f386SKyungmin Park  * @param mtd		MTD device structure
829bfd7f386SKyungmin Park  * @param from		offset to read from
830bfd7f386SKyungmin Park  * @param ops		oob operation description structure
831bfd7f386SKyungmin Park  *
832bfd7f386SKyungmin Park  * OneNAND read main and/or out-of-band data
833bfd7f386SKyungmin Park  */
834bfd7f386SKyungmin Park static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
835bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
836bfd7f386SKyungmin Park {
837bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
838bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
839bfd7f386SKyungmin Park 	size_t len = ops->len;
840bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
841bfd7f386SKyungmin Park 	u_char *buf = ops->datbuf;
842bfd7f386SKyungmin Park 	u_char *oobbuf = ops->oobbuf;
843bfd7f386SKyungmin Park 	int read = 0, column, thislen;
844bfd7f386SKyungmin Park 	int oobread = 0, oobcolumn, thisooblen, oobsize;
845bfd7f386SKyungmin Park 	int ret = 0, boundary = 0;
846bfd7f386SKyungmin Park 	int writesize = this->writesize;
847bfd7f386SKyungmin Park 
848ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
84959829cc1SJean-Christophe PLAGNIOL-VILLARD 
850*dfe64e2cSSergey Lapin 	if (ops->mode == MTD_OPS_AUTO_OOB)
851bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
852bfd7f386SKyungmin Park 	else
853bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
854bfd7f386SKyungmin Park 
855bfd7f386SKyungmin Park 	oobcolumn = from & (mtd->oobsize - 1);
856bfd7f386SKyungmin Park 
85759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
85859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((from + len) > mtd->size) {
859bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n");
860bfd7f386SKyungmin Park 		ops->retlen = 0;
861bfd7f386SKyungmin Park 		ops->oobretlen = 0;
86259829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
86359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
86459829cc1SJean-Christophe PLAGNIOL-VILLARD 
865bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
86659829cc1SJean-Christophe PLAGNIOL-VILLARD 
867bfd7f386SKyungmin Park 	/* Read-while-load method */
868cacbe919SAmul Kumar Saha 	/* Note: We can't use this feature in MLC */
86959829cc1SJean-Christophe PLAGNIOL-VILLARD 
870bfd7f386SKyungmin Park 	/* Do first load to bufferRAM */
871bfd7f386SKyungmin Park 	if (read < len) {
87259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!onenand_check_bufferram(mtd, from)) {
873ef0921d6SKyungmin Park 			this->main_buf = buf;
874bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
87559829cc1SJean-Christophe PLAGNIOL-VILLARD 			ret = this->wait(mtd, FL_READING);
876cacbe919SAmul Kumar Saha 			if (unlikely(ret))
877cacbe919SAmul Kumar Saha 				ret = onenand_recover_lsb(mtd, from, ret);
878bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
879bfd7f386SKyungmin Park 			if (ret == -EBADMSG)
880bfd7f386SKyungmin Park 				ret = 0;
881bfd7f386SKyungmin Park 		}
88259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
88359829cc1SJean-Christophe PLAGNIOL-VILLARD 
884bfd7f386SKyungmin Park 	thislen = min_t(int, writesize, len - read);
885bfd7f386SKyungmin Park 	column = from & (writesize - 1);
886bfd7f386SKyungmin Park 	if (column + thislen > writesize)
887bfd7f386SKyungmin Park 		thislen = writesize - column;
88859829cc1SJean-Christophe PLAGNIOL-VILLARD 
889bfd7f386SKyungmin Park 	while (!ret) {
890bfd7f386SKyungmin Park 		/* If there is more to load then start next load */
891bfd7f386SKyungmin Park 		from += thislen;
892e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this) && read + thislen < len) {
893ef0921d6SKyungmin Park 			this->main_buf = buf + thislen;
894bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
895bfd7f386SKyungmin Park 			/*
896bfd7f386SKyungmin Park 			 * Chip boundary handling in DDP
897bfd7f386SKyungmin Park 			 * Now we issued chip 1 read and pointed chip 1
898bfd7f386SKyungmin Park 			 * bufferam so we have to point chip 0 bufferam.
899bfd7f386SKyungmin Park 			 */
900bfd7f386SKyungmin Park 			if (ONENAND_IS_DDP(this) &&
901bfd7f386SKyungmin Park 					unlikely(from == (this->chipsize >> 1))) {
902bfd7f386SKyungmin Park 				this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
903bfd7f386SKyungmin Park 				boundary = 1;
904bfd7f386SKyungmin Park 			} else
905bfd7f386SKyungmin Park 				boundary = 0;
906bfd7f386SKyungmin Park 			ONENAND_SET_PREV_BUFFERRAM(this);
907bfd7f386SKyungmin Park 		}
908bfd7f386SKyungmin Park 
909bfd7f386SKyungmin Park 		/* While load is going, read from last bufferRAM */
910ef0921d6SKyungmin Park 		this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen);
911bfd7f386SKyungmin Park 
912bfd7f386SKyungmin Park 		/* Read oob area if needed */
913bfd7f386SKyungmin Park 		if (oobbuf) {
914bfd7f386SKyungmin Park 			thisooblen = oobsize - oobcolumn;
915bfd7f386SKyungmin Park 			thisooblen = min_t(int, thisooblen, ooblen - oobread);
916bfd7f386SKyungmin Park 
917*dfe64e2cSSergey Lapin 			if (ops->mode == MTD_OPS_AUTO_OOB)
918bfd7f386SKyungmin Park 				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
919bfd7f386SKyungmin Park 			else
920ef0921d6SKyungmin Park 				this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
921bfd7f386SKyungmin Park 			oobread += thisooblen;
922bfd7f386SKyungmin Park 			oobbuf += thisooblen;
923bfd7f386SKyungmin Park 			oobcolumn = 0;
924bfd7f386SKyungmin Park 		}
925bfd7f386SKyungmin Park 
926e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this) && (read + thislen < len)) {
927cacbe919SAmul Kumar Saha 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
928cacbe919SAmul Kumar Saha 			ret = this->wait(mtd, FL_READING);
929cacbe919SAmul Kumar Saha 			if (unlikely(ret))
930cacbe919SAmul Kumar Saha 				ret = onenand_recover_lsb(mtd, from, ret);
931cacbe919SAmul Kumar Saha 			onenand_update_bufferram(mtd, from, !ret);
932*dfe64e2cSSergey Lapin 			if (mtd_is_eccerr(ret))
933cacbe919SAmul Kumar Saha 				ret = 0;
934cacbe919SAmul Kumar Saha 		}
935cacbe919SAmul Kumar Saha 
936bfd7f386SKyungmin Park 		/* See if we are done */
93759829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
93859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
93959829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
940bfd7f386SKyungmin Park 		/* Set up for next read from bufferRAM */
941bfd7f386SKyungmin Park 		if (unlikely(boundary))
942bfd7f386SKyungmin Park 			this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
943e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this))
944bfd7f386SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
94559829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
946bfd7f386SKyungmin Park 		thislen = min_t(int, writesize, len - read);
947bfd7f386SKyungmin Park 		column = 0;
94859829cc1SJean-Christophe PLAGNIOL-VILLARD 
949e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this)) {
950bfd7f386SKyungmin Park 			/* Now wait for load */
951bfd7f386SKyungmin Park 			ret = this->wait(mtd, FL_READING);
952bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
953*dfe64e2cSSergey Lapin 			if (mtd_is_eccerr(ret))
954bfd7f386SKyungmin Park 				ret = 0;
955bfd7f386SKyungmin Park 		}
956cacbe919SAmul Kumar Saha 	}
95759829cc1SJean-Christophe PLAGNIOL-VILLARD 
95859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/*
95959829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * Return success, if no ECC failures, else -EBADMSG
96059829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * fs driver will take care of that, because
96159829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * retlen == desired len and result == -EBADMSG
96259829cc1SJean-Christophe PLAGNIOL-VILLARD 	 */
963bfd7f386SKyungmin Park 	ops->retlen = read;
964bfd7f386SKyungmin Park 	ops->oobretlen = oobread;
965bfd7f386SKyungmin Park 
966bfd7f386SKyungmin Park 	if (ret)
96759829cc1SJean-Christophe PLAGNIOL-VILLARD 		return ret;
968bfd7f386SKyungmin Park 
969bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
970bfd7f386SKyungmin Park 		return -EBADMSG;
971bfd7f386SKyungmin Park 
972bfd7f386SKyungmin Park 	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
973bfd7f386SKyungmin Park }
974bfd7f386SKyungmin Park 
975bfd7f386SKyungmin Park /**
976bfd7f386SKyungmin Park  * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
977bfd7f386SKyungmin Park  * @param mtd		MTD device structure
978bfd7f386SKyungmin Park  * @param from		offset to read from
979bfd7f386SKyungmin Park  * @param ops		oob operation description structure
980bfd7f386SKyungmin Park  *
981bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area
982bfd7f386SKyungmin Park  */
983bfd7f386SKyungmin Park static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
984bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
985bfd7f386SKyungmin Park {
986bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
987bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
988bfd7f386SKyungmin Park 	int read = 0, thislen, column, oobsize;
989bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
990*dfe64e2cSSergey Lapin 	unsigned int mode = ops->mode;
991bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
992cacbe919SAmul Kumar Saha 	int ret = 0, readcmd;
993bfd7f386SKyungmin Park 
994bfd7f386SKyungmin Park 	from += ops->ooboffs;
995bfd7f386SKyungmin Park 
996ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
997bfd7f386SKyungmin Park 
998bfd7f386SKyungmin Park 	/* Initialize return length value */
999bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1000bfd7f386SKyungmin Park 
1001*dfe64e2cSSergey Lapin 	if (mode == MTD_OPS_AUTO_OOB)
1002bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1003bfd7f386SKyungmin Park 	else
1004bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1005bfd7f386SKyungmin Park 
1006bfd7f386SKyungmin Park 	column = from & (mtd->oobsize - 1);
1007bfd7f386SKyungmin Park 
1008bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1009bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
1010bfd7f386SKyungmin Park 		return -EINVAL;
1011bfd7f386SKyungmin Park 	}
1012bfd7f386SKyungmin Park 
1013bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1014bfd7f386SKyungmin Park 	if (unlikely(from >= mtd->size ||
1015bfd7f386SKyungmin Park 		column + len > ((mtd->size >> this->page_shift) -
1016bfd7f386SKyungmin Park 				(from >> this->page_shift)) * oobsize)) {
1017bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n");
1018bfd7f386SKyungmin Park 		return -EINVAL;
1019bfd7f386SKyungmin Park 	}
1020bfd7f386SKyungmin Park 
1021bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
1022bfd7f386SKyungmin Park 
1023e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1024e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1025cacbe919SAmul Kumar Saha 
1026bfd7f386SKyungmin Park 	while (read < len) {
1027bfd7f386SKyungmin Park 		thislen = oobsize - column;
1028bfd7f386SKyungmin Park 		thislen = min_t(int, thislen, len);
1029bfd7f386SKyungmin Park 
1030ef0921d6SKyungmin Park 		this->spare_buf = buf;
1031cacbe919SAmul Kumar Saha 		this->command(mtd, readcmd, from, mtd->oobsize);
1032bfd7f386SKyungmin Park 
1033bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, from, 0);
1034bfd7f386SKyungmin Park 
1035bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_READING);
1036cacbe919SAmul Kumar Saha 		if (unlikely(ret))
1037cacbe919SAmul Kumar Saha 			ret = onenand_recover_lsb(mtd, from, ret);
1038cacbe919SAmul Kumar Saha 
1039bfd7f386SKyungmin Park 		if (ret && ret != -EBADMSG) {
1040bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
1041bfd7f386SKyungmin Park 			break;
1042bfd7f386SKyungmin Park 		}
1043bfd7f386SKyungmin Park 
1044*dfe64e2cSSergey Lapin 		if (mode == MTD_OPS_AUTO_OOB)
1045bfd7f386SKyungmin Park 			onenand_transfer_auto_oob(mtd, buf, column, thislen);
1046bfd7f386SKyungmin Park 		else
1047ef0921d6SKyungmin Park 			this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
1048bfd7f386SKyungmin Park 
1049bfd7f386SKyungmin Park 		read += thislen;
1050bfd7f386SKyungmin Park 
1051bfd7f386SKyungmin Park 		if (read == len)
1052bfd7f386SKyungmin Park 			break;
1053bfd7f386SKyungmin Park 
1054bfd7f386SKyungmin Park 		buf += thislen;
1055bfd7f386SKyungmin Park 
1056bfd7f386SKyungmin Park 		/* Read more? */
1057bfd7f386SKyungmin Park 		if (read < len) {
1058bfd7f386SKyungmin Park 			/* Page size */
1059bfd7f386SKyungmin Park 			from += mtd->writesize;
1060bfd7f386SKyungmin Park 			column = 0;
1061bfd7f386SKyungmin Park 		}
1062bfd7f386SKyungmin Park 	}
1063bfd7f386SKyungmin Park 
1064bfd7f386SKyungmin Park 	ops->oobretlen = read;
1065bfd7f386SKyungmin Park 
1066bfd7f386SKyungmin Park 	if (ret)
1067bfd7f386SKyungmin Park 		return ret;
1068bfd7f386SKyungmin Park 
1069bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
1070bfd7f386SKyungmin Park 		return -EBADMSG;
1071bfd7f386SKyungmin Park 
1072bfd7f386SKyungmin Park 	return 0;
107359829cc1SJean-Christophe PLAGNIOL-VILLARD }
107459829cc1SJean-Christophe PLAGNIOL-VILLARD 
107559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
107659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc
107759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
107859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
107959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read
108059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of read bytes
108159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the databuffer to put data
108259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
108359829cc1SJean-Christophe PLAGNIOL-VILLARD  * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
108459829cc1SJean-Christophe PLAGNIOL-VILLARD */
108559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
108659829cc1SJean-Christophe PLAGNIOL-VILLARD 		 size_t * retlen, u_char * buf)
108759829cc1SJean-Christophe PLAGNIOL-VILLARD {
1088bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1089bfd7f386SKyungmin Park 		.len    = len,
1090bfd7f386SKyungmin Park 		.ooblen = 0,
1091bfd7f386SKyungmin Park 		.datbuf = buf,
1092bfd7f386SKyungmin Park 		.oobbuf = NULL,
1093bfd7f386SKyungmin Park 	};
1094bfd7f386SKyungmin Park 	int ret;
1095bfd7f386SKyungmin Park 
1096bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1097bfd7f386SKyungmin Park 	ret = onenand_read_ops_nolock(mtd, from, &ops);
1098bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1099bfd7f386SKyungmin Park 
1100bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1101bfd7f386SKyungmin Park 	return ret;
110259829cc1SJean-Christophe PLAGNIOL-VILLARD }
110359829cc1SJean-Christophe PLAGNIOL-VILLARD 
110459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
110559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
110659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
110759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
1108bfd7f386SKyungmin Park  * @param ops		oob operations description structure
110959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1110bfd7f386SKyungmin Park  * OneNAND main and/or out-of-band
111159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1112bfd7f386SKyungmin Park int onenand_read_oob(struct mtd_info *mtd, loff_t from,
1113bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
1114bfd7f386SKyungmin Park {
1115bfd7f386SKyungmin Park 	int ret;
1116bfd7f386SKyungmin Park 
1117bfd7f386SKyungmin Park 	switch (ops->mode) {
1118*dfe64e2cSSergey Lapin 	case MTD_OPS_PLACE_OOB:
1119*dfe64e2cSSergey Lapin 	case MTD_OPS_AUTO_OOB:
1120bfd7f386SKyungmin Park 		break;
1121*dfe64e2cSSergey Lapin 	case MTD_OPS_RAW:
1122bfd7f386SKyungmin Park 		/* Not implemented yet */
1123bfd7f386SKyungmin Park 	default:
1124bfd7f386SKyungmin Park 		return -EINVAL;
1125bfd7f386SKyungmin Park 	}
1126bfd7f386SKyungmin Park 
1127bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1128bfd7f386SKyungmin Park 	if (ops->datbuf)
1129bfd7f386SKyungmin Park 		ret = onenand_read_ops_nolock(mtd, from, ops);
1130bfd7f386SKyungmin Park 	else
1131bfd7f386SKyungmin Park 		ret = onenand_read_oob_nolock(mtd, from, ops);
1132bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1133bfd7f386SKyungmin Park 
1134bfd7f386SKyungmin Park 	return ret;
1135bfd7f386SKyungmin Park }
1136bfd7f386SKyungmin Park 
1137bfd7f386SKyungmin Park /**
1138bfd7f386SKyungmin Park  * onenand_bbt_wait - [DEFAULT] wait until the command is done
1139bfd7f386SKyungmin Park  * @param mtd		MTD device structure
1140bfd7f386SKyungmin Park  * @param state		state to select the max. timeout value
1141bfd7f386SKyungmin Park  *
1142bfd7f386SKyungmin Park  * Wait for command done.
1143bfd7f386SKyungmin Park  */
1144bfd7f386SKyungmin Park static int onenand_bbt_wait(struct mtd_info *mtd, int state)
1145bfd7f386SKyungmin Park {
1146bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1147bfd7f386SKyungmin Park 	unsigned int flags = ONENAND_INT_MASTER;
1148bfd7f386SKyungmin Park 	unsigned int interrupt;
1149bfd7f386SKyungmin Park 	unsigned int ctrl;
1150bfd7f386SKyungmin Park 
1151bfd7f386SKyungmin Park 	while (1) {
1152bfd7f386SKyungmin Park 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
1153bfd7f386SKyungmin Park 		if (interrupt & flags)
1154bfd7f386SKyungmin Park 			break;
1155bfd7f386SKyungmin Park 	}
1156bfd7f386SKyungmin Park 
1157bfd7f386SKyungmin Park 	/* To get correct interrupt status in timeout case */
1158bfd7f386SKyungmin Park 	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
1159bfd7f386SKyungmin Park 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
1160bfd7f386SKyungmin Park 
1161bfd7f386SKyungmin Park 	if (interrupt & ONENAND_INT_READ) {
1162cacbe919SAmul Kumar Saha 		int ecc = onenand_read_ecc(this);
1163cacbe919SAmul Kumar Saha 		if (ecc & ONENAND_ECC_2BIT_ALL) {
1164cacbe919SAmul Kumar Saha 			printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
1165cacbe919SAmul Kumar Saha 				", controller = 0x%04x\n", ecc, ctrl);
1166bfd7f386SKyungmin Park 			return ONENAND_BBT_READ_ERROR;
1167cacbe919SAmul Kumar Saha 		}
1168bfd7f386SKyungmin Park 	} else {
1169bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_wait: read timeout!"
1170bfd7f386SKyungmin Park 				"ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
1171bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
1172bfd7f386SKyungmin Park 	}
1173bfd7f386SKyungmin Park 
1174ef0921d6SKyungmin Park 	/* Initial bad block case: 0x2400 or 0x0400 */
1175ef0921d6SKyungmin Park 	if (ctrl & ONENAND_CTRL_ERROR) {
1176ef0921d6SKyungmin Park 		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
1177ef0921d6SKyungmin Park 		return ONENAND_BBT_READ_ERROR;
1178ef0921d6SKyungmin Park 	}
1179ef0921d6SKyungmin Park 
1180bfd7f386SKyungmin Park 	return 0;
1181bfd7f386SKyungmin Park }
1182bfd7f386SKyungmin Park 
1183bfd7f386SKyungmin Park /**
1184bfd7f386SKyungmin Park  * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
1185bfd7f386SKyungmin Park  * @param mtd		MTD device structure
1186bfd7f386SKyungmin Park  * @param from		offset to read from
1187bfd7f386SKyungmin Park  * @param ops		oob operation description structure
1188bfd7f386SKyungmin Park  *
1189bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area for bbt scan
1190bfd7f386SKyungmin Park  */
1191bfd7f386SKyungmin Park int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
1192bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
119359829cc1SJean-Christophe PLAGNIOL-VILLARD {
119459829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
119559829cc1SJean-Christophe PLAGNIOL-VILLARD 	int read = 0, thislen, column;
1196cacbe919SAmul Kumar Saha 	int ret = 0, readcmd;
1197bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1198bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
119959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1200ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
120159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1202e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1203e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1204cacbe919SAmul Kumar Saha 
1205bfd7f386SKyungmin Park 	/* Initialize return value */
1206bfd7f386SKyungmin Park 	ops->oobretlen = 0;
120759829cc1SJean-Christophe PLAGNIOL-VILLARD 
120859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
120959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((from + len) > mtd->size)) {
1210bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
1211bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
121259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
121359829cc1SJean-Christophe PLAGNIOL-VILLARD 
121459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
121559829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_READING);
121659829cc1SJean-Christophe PLAGNIOL-VILLARD 
121759829cc1SJean-Christophe PLAGNIOL-VILLARD 	column = from & (mtd->oobsize - 1);
121859829cc1SJean-Christophe PLAGNIOL-VILLARD 
121959829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (read < len) {
1220bfd7f386SKyungmin Park 
122159829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = mtd->oobsize - column;
122259829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = min_t(int, thislen, len);
122359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1224ef0921d6SKyungmin Park 		this->spare_buf = buf;
1225cacbe919SAmul Kumar Saha 		this->command(mtd, readcmd, from, mtd->oobsize);
122659829cc1SJean-Christophe PLAGNIOL-VILLARD 
122759829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, from, 0);
122859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1229ef0921d6SKyungmin Park 		ret = this->bbt_wait(mtd, FL_READING);
1230cacbe919SAmul Kumar Saha 		if (unlikely(ret))
1231cacbe919SAmul Kumar Saha 			ret = onenand_recover_lsb(mtd, from, ret);
1232cacbe919SAmul Kumar Saha 
1233bfd7f386SKyungmin Park 		if (ret)
1234bfd7f386SKyungmin Park 			break;
123559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1236ce3277a6SKyungmin Park 		this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
123759829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
123859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
123959829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
124059829cc1SJean-Christophe PLAGNIOL-VILLARD 
124159829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
1242bfd7f386SKyungmin Park 
124359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Read more? */
124459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read < len) {
1245bfd7f386SKyungmin Park 			/* Update Page size */
1246bfd7f386SKyungmin Park 			from += this->writesize;
124759829cc1SJean-Christophe PLAGNIOL-VILLARD 			column = 0;
124859829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
124959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
125059829cc1SJean-Christophe PLAGNIOL-VILLARD 
125159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
125259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
125359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1254bfd7f386SKyungmin Park 	ops->oobretlen = read;
125559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
125659829cc1SJean-Christophe PLAGNIOL-VILLARD }
125759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1258bfd7f386SKyungmin Park 
125959829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
126059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1261bfd7f386SKyungmin Park  * onenand_verify_oob - [GENERIC] verify the oob contents after a write
126259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
126359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf           the databuffer to verify
1264bfd7f386SKyungmin Park  * @param to            offset to read from
126559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1266bfd7f386SKyungmin Park static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
126759829cc1SJean-Christophe PLAGNIOL-VILLARD {
126859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1269bfd7f386SKyungmin Park 	u_char *oob_buf = this->oob_buf;
1270cacbe919SAmul Kumar Saha 	int status, i, readcmd;
127159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1272e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1273e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1274cacbe919SAmul Kumar Saha 
1275cacbe919SAmul Kumar Saha 	this->command(mtd, readcmd, to, mtd->oobsize);
1276bfd7f386SKyungmin Park 	onenand_update_bufferram(mtd, to, 0);
1277bfd7f386SKyungmin Park 	status = this->wait(mtd, FL_READING);
1278bfd7f386SKyungmin Park 	if (status)
1279bfd7f386SKyungmin Park 		return status;
1280bfd7f386SKyungmin Park 
1281ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
1282bfd7f386SKyungmin Park 	for (i = 0; i < mtd->oobsize; i++)
1283bfd7f386SKyungmin Park 		if (buf[i] != 0xFF && buf[i] != oob_buf[i])
1284bfd7f386SKyungmin Park 			return -EBADMSG;
1285bfd7f386SKyungmin Park 
1286bfd7f386SKyungmin Park 	return 0;
1287bfd7f386SKyungmin Park }
1288bfd7f386SKyungmin Park 
1289bfd7f386SKyungmin Park /**
1290bfd7f386SKyungmin Park  * onenand_verify - [GENERIC] verify the chip contents after a write
1291bfd7f386SKyungmin Park  * @param mtd          MTD device structure
1292bfd7f386SKyungmin Park  * @param buf          the databuffer to verify
1293bfd7f386SKyungmin Park  * @param addr         offset to read from
1294bfd7f386SKyungmin Park  * @param len          number of bytes to read and compare
1295bfd7f386SKyungmin Park  */
1296bfd7f386SKyungmin Park static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
1297bfd7f386SKyungmin Park {
1298bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1299bfd7f386SKyungmin Park 	void __iomem *dataram;
1300bfd7f386SKyungmin Park 	int ret = 0;
1301bfd7f386SKyungmin Park 	int thislen, column;
1302bfd7f386SKyungmin Park 
1303bfd7f386SKyungmin Park 	while (len != 0) {
1304bfd7f386SKyungmin Park 		thislen = min_t(int, this->writesize, len);
1305bfd7f386SKyungmin Park 		column = addr & (this->writesize - 1);
1306bfd7f386SKyungmin Park 		if (column + thislen > this->writesize)
1307bfd7f386SKyungmin Park 			thislen = this->writesize - column;
1308bfd7f386SKyungmin Park 
1309bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
1310bfd7f386SKyungmin Park 
1311bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, addr, 0);
131259829cc1SJean-Christophe PLAGNIOL-VILLARD 
131359829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_READING);
131459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret)
131559829cc1SJean-Christophe PLAGNIOL-VILLARD 			return ret;
131659829cc1SJean-Christophe PLAGNIOL-VILLARD 
131759829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, addr, 1);
131859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1319bfd7f386SKyungmin Park 		dataram = this->base + ONENAND_DATARAM;
1320bfd7f386SKyungmin Park 		dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
132159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1322bfd7f386SKyungmin Park 		if (memcmp(buf, dataram + column, thislen))
132359829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EBADMSG;
132459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1325bfd7f386SKyungmin Park 		len -= thislen;
1326bfd7f386SKyungmin Park 		buf += thislen;
1327bfd7f386SKyungmin Park 		addr += thislen;
1328bfd7f386SKyungmin Park 	}
1329bfd7f386SKyungmin Park 
133059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
133159829cc1SJean-Christophe PLAGNIOL-VILLARD }
133259829cc1SJean-Christophe PLAGNIOL-VILLARD #else
1333bfd7f386SKyungmin Park #define onenand_verify(...)             (0)
1334bfd7f386SKyungmin Park #define onenand_verify_oob(...)         (0)
133559829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
133659829cc1SJean-Christophe PLAGNIOL-VILLARD 
13371ae39862SStefan Roese #define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)
133859829cc1SJean-Christophe PLAGNIOL-VILLARD 
133959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1340*dfe64e2cSSergey Lapin  * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
134159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
1342bfd7f386SKyungmin Park  * @param oob_buf       oob buffer
1343bfd7f386SKyungmin Park  * @param buf           source address
1344bfd7f386SKyungmin Park  * @param column        oob offset to write to
1345bfd7f386SKyungmin Park  * @param thislen       oob length to write
134659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1347bfd7f386SKyungmin Park static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
1348bfd7f386SKyungmin Park 		const u_char *buf, int column, int thislen)
134959829cc1SJean-Christophe PLAGNIOL-VILLARD {
135059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1351bfd7f386SKyungmin Park 	struct nand_oobfree *free;
1352bfd7f386SKyungmin Park 	int writecol = column;
1353bfd7f386SKyungmin Park 	int writeend = column + thislen;
1354bfd7f386SKyungmin Park 	int lastgap = 0;
1355bfd7f386SKyungmin Park 	unsigned int i;
1356bfd7f386SKyungmin Park 
1357bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1358bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1359bfd7f386SKyungmin Park 		if (writecol >= lastgap)
1360bfd7f386SKyungmin Park 			writecol += free->offset - lastgap;
1361bfd7f386SKyungmin Park 		if (writeend >= lastgap)
1362bfd7f386SKyungmin Park 			writeend += free->offset - lastgap;
1363bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
1364bfd7f386SKyungmin Park 	}
1365bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1366bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1367bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
1368bfd7f386SKyungmin Park 		if (free->offset < writeend && free_end > writecol) {
1369bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,writecol);
1370bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,writeend);
1371bfd7f386SKyungmin Park 			int n = ed - st;
1372bfd7f386SKyungmin Park 			memcpy(oob_buf + st, buf, n);
1373bfd7f386SKyungmin Park 			buf += n;
1374bfd7f386SKyungmin Park 		} else if (column == 0)
1375bfd7f386SKyungmin Park 			break;
1376bfd7f386SKyungmin Park 	}
1377bfd7f386SKyungmin Park 	return 0;
1378bfd7f386SKyungmin Park }
1379bfd7f386SKyungmin Park 
1380bfd7f386SKyungmin Park /**
1381bfd7f386SKyungmin Park  * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
1382bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1383bfd7f386SKyungmin Park  * @param to            offset to write to
1384bfd7f386SKyungmin Park  * @param ops           oob operation description structure
1385bfd7f386SKyungmin Park  *
1386bfd7f386SKyungmin Park  * Write main and/or oob with ECC
1387bfd7f386SKyungmin Park  */
1388bfd7f386SKyungmin Park static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
1389bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1390bfd7f386SKyungmin Park {
1391bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1392bfd7f386SKyungmin Park 	int written = 0, column, thislen, subpage;
1393bfd7f386SKyungmin Park 	int oobwritten = 0, oobcolumn, thisooblen, oobsize;
1394bfd7f386SKyungmin Park 	size_t len = ops->len;
1395bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
1396bfd7f386SKyungmin Park 	const u_char *buf = ops->datbuf;
1397bfd7f386SKyungmin Park 	const u_char *oob = ops->oobbuf;
1398bfd7f386SKyungmin Park 	u_char *oobbuf;
139959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
140059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1401ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
140259829cc1SJean-Christophe PLAGNIOL-VILLARD 
140359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Initialize retlen, in case of early exit */
1404bfd7f386SKyungmin Park 	ops->retlen = 0;
1405bfd7f386SKyungmin Park 	ops->oobretlen = 0;
140659829cc1SJean-Christophe PLAGNIOL-VILLARD 
140759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reject writes, which are not page aligned */
1408bfd7f386SKyungmin Park 	if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
1409bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
141059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
141159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
141259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1413*dfe64e2cSSergey Lapin 	if (ops->mode == MTD_OPS_AUTO_OOB)
1414bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1415bfd7f386SKyungmin Park 	else
1416bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1417bfd7f386SKyungmin Park 
1418bfd7f386SKyungmin Park 	oobcolumn = to & (mtd->oobsize - 1);
1419bfd7f386SKyungmin Park 
1420bfd7f386SKyungmin Park 	column = to & (mtd->writesize - 1);
142159829cc1SJean-Christophe PLAGNIOL-VILLARD 
142259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop until all data write */
142359829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (written < len) {
1424bfd7f386SKyungmin Park 		u_char *wbuf = (u_char *) buf;
142559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1426bfd7f386SKyungmin Park 		thislen = min_t(int, mtd->writesize - column, len - written);
1427bfd7f386SKyungmin Park 		thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
142859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1429bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
1430bfd7f386SKyungmin Park 
1431bfd7f386SKyungmin Park 		/* Partial page write */
1432bfd7f386SKyungmin Park 		subpage = thislen < mtd->writesize;
1433bfd7f386SKyungmin Park 		if (subpage) {
1434bfd7f386SKyungmin Park 			memset(this->page_buf, 0xff, mtd->writesize);
1435bfd7f386SKyungmin Park 			memcpy(this->page_buf + column, buf, thislen);
1436bfd7f386SKyungmin Park 			wbuf = this->page_buf;
1437bfd7f386SKyungmin Park 		}
1438bfd7f386SKyungmin Park 
1439ef0921d6SKyungmin Park 		this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
1440bfd7f386SKyungmin Park 
1441bfd7f386SKyungmin Park 		if (oob) {
1442bfd7f386SKyungmin Park 			oobbuf = this->oob_buf;
1443bfd7f386SKyungmin Park 
1444bfd7f386SKyungmin Park 			/* We send data to spare ram with oobsize
1445bfd7f386SKyungmin Park 			 *                          * to prevent byte access */
1446bfd7f386SKyungmin Park 			memset(oobbuf, 0xff, mtd->oobsize);
1447*dfe64e2cSSergey Lapin 			if (ops->mode == MTD_OPS_AUTO_OOB)
1448bfd7f386SKyungmin Park 				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
1449bfd7f386SKyungmin Park 			else
1450bfd7f386SKyungmin Park 				memcpy(oobbuf + oobcolumn, oob, thisooblen);
1451bfd7f386SKyungmin Park 
1452bfd7f386SKyungmin Park 			oobwritten += thisooblen;
1453bfd7f386SKyungmin Park 			oob += thisooblen;
1454bfd7f386SKyungmin Park 			oobcolumn = 0;
1455bfd7f386SKyungmin Park 		} else
1456bfd7f386SKyungmin Park 			oobbuf = (u_char *) ffchars;
1457bfd7f386SKyungmin Park 
1458ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
145959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1460d438d508SKyungmin Park 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
146159829cc1SJean-Christophe PLAGNIOL-VILLARD 
146259829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_WRITING);
1463bfd7f386SKyungmin Park 
1464bfd7f386SKyungmin Park 		/* In partial page write we don't update bufferram */
1465bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, !ret && !subpage);
1466bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1467bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1468bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
1469bfd7f386SKyungmin Park 		}
1470bfd7f386SKyungmin Park 
147159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
1472bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
1473bfd7f386SKyungmin Park 			break;
1474bfd7f386SKyungmin Park 		}
1475bfd7f386SKyungmin Park 
1476bfd7f386SKyungmin Park 		/* Only check verify write turn on */
1477bfd7f386SKyungmin Park 		ret = onenand_verify(mtd, buf, to, thislen);
1478bfd7f386SKyungmin Park 		if (ret) {
1479bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
148059829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
148159829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
148259829cc1SJean-Christophe PLAGNIOL-VILLARD 
148359829cc1SJean-Christophe PLAGNIOL-VILLARD 		written += thislen;
148459829cc1SJean-Christophe PLAGNIOL-VILLARD 
148559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (written == len)
148659829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
148759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1488bfd7f386SKyungmin Park 		column = 0;
148959829cc1SJean-Christophe PLAGNIOL-VILLARD 		to += thislen;
149059829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
149159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
149259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1493bfd7f386SKyungmin Park 	ops->retlen = written;
149459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1495bfd7f386SKyungmin Park 	return ret;
1496bfd7f386SKyungmin Park }
1497bfd7f386SKyungmin Park 
1498bfd7f386SKyungmin Park /**
1499*dfe64e2cSSergey Lapin  * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
1500bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1501bfd7f386SKyungmin Park  * @param to            offset to write to
1502bfd7f386SKyungmin Park  * @param len           number of bytes to write
1503bfd7f386SKyungmin Park  * @param retlen        pointer to variable to store the number of written bytes
1504bfd7f386SKyungmin Park  * @param buf           the data to write
1505bfd7f386SKyungmin Park  * @param mode          operation mode
1506bfd7f386SKyungmin Park  *
1507bfd7f386SKyungmin Park  * OneNAND write out-of-band
1508bfd7f386SKyungmin Park  */
1509bfd7f386SKyungmin Park static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
1510bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1511bfd7f386SKyungmin Park {
1512bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1513bfd7f386SKyungmin Park 	int column, ret = 0, oobsize;
1514cacbe919SAmul Kumar Saha 	int written = 0, oobcmd;
1515bfd7f386SKyungmin Park 	u_char *oobbuf;
1516bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1517bfd7f386SKyungmin Park 	const u_char *buf = ops->oobbuf;
1518*dfe64e2cSSergey Lapin 	unsigned int mode = ops->mode;
1519bfd7f386SKyungmin Park 
1520bfd7f386SKyungmin Park 	to += ops->ooboffs;
1521bfd7f386SKyungmin Park 
1522ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
1523bfd7f386SKyungmin Park 
1524bfd7f386SKyungmin Park 	/* Initialize retlen, in case of early exit */
1525bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1526bfd7f386SKyungmin Park 
1527*dfe64e2cSSergey Lapin 	if (mode == MTD_OPS_AUTO_OOB)
1528bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1529bfd7f386SKyungmin Park 	else
1530bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1531bfd7f386SKyungmin Park 
1532bfd7f386SKyungmin Park 	column = to & (mtd->oobsize - 1);
1533bfd7f386SKyungmin Park 
1534bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1535bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");
1536bfd7f386SKyungmin Park 		return -EINVAL;
1537bfd7f386SKyungmin Park 	}
1538bfd7f386SKyungmin Park 
1539bfd7f386SKyungmin Park 	/* For compatibility with NAND: Do not allow write past end of page */
1540bfd7f386SKyungmin Park 	if (unlikely(column + len > oobsize)) {
1541bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: "
1542bfd7f386SKyungmin Park 				"Attempt to write past end of page\n");
1543bfd7f386SKyungmin Park 		return -EINVAL;
1544bfd7f386SKyungmin Park 	}
1545bfd7f386SKyungmin Park 
1546bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1547bfd7f386SKyungmin Park 	if (unlikely(to >= mtd->size ||
1548bfd7f386SKyungmin Park 				column + len > ((mtd->size >> this->page_shift) -
1549bfd7f386SKyungmin Park 					(to >> this->page_shift)) * oobsize)) {
1550bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n");
1551bfd7f386SKyungmin Park 		return -EINVAL;
1552bfd7f386SKyungmin Park 	}
1553bfd7f386SKyungmin Park 
1554bfd7f386SKyungmin Park 	oobbuf = this->oob_buf;
1555bfd7f386SKyungmin Park 
1556e26fd3d3SLukasz Majewski 	oobcmd = ONENAND_IS_4KB_PAGE(this) ?
1557e26fd3d3SLukasz Majewski 		ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
1558cacbe919SAmul Kumar Saha 
1559bfd7f386SKyungmin Park 	/* Loop until all data write */
1560bfd7f386SKyungmin Park 	while (written < len) {
1561bfd7f386SKyungmin Park 		int thislen = min_t(int, oobsize, len - written);
1562bfd7f386SKyungmin Park 
1563bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
1564bfd7f386SKyungmin Park 
1565bfd7f386SKyungmin Park 		/* We send data to spare ram with oobsize
1566bfd7f386SKyungmin Park 		 * to prevent byte access */
1567bfd7f386SKyungmin Park 		memset(oobbuf, 0xff, mtd->oobsize);
1568*dfe64e2cSSergey Lapin 		if (mode == MTD_OPS_AUTO_OOB)
1569bfd7f386SKyungmin Park 			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
1570bfd7f386SKyungmin Park 		else
1571bfd7f386SKyungmin Park 			memcpy(oobbuf + column, buf, thislen);
1572ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
1573bfd7f386SKyungmin Park 
1574e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this)) {
1575cacbe919SAmul Kumar Saha 			/* Set main area of DataRAM to 0xff*/
1576cacbe919SAmul Kumar Saha 			memset(this->page_buf, 0xff, mtd->writesize);
1577cacbe919SAmul Kumar Saha 			this->write_bufferram(mtd, 0, ONENAND_DATARAM,
1578cacbe919SAmul Kumar Saha 				this->page_buf,	0, mtd->writesize);
1579cacbe919SAmul Kumar Saha 		}
1580cacbe919SAmul Kumar Saha 
1581cacbe919SAmul Kumar Saha 		this->command(mtd, oobcmd, to, mtd->oobsize);
1582bfd7f386SKyungmin Park 
1583bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, 0);
1584bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1585bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1586bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, 0);
1587bfd7f386SKyungmin Park 		}
1588bfd7f386SKyungmin Park 
1589bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_WRITING);
1590bfd7f386SKyungmin Park 		if (ret) {
1591bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret);
1592bfd7f386SKyungmin Park 			break;
1593bfd7f386SKyungmin Park 		}
1594bfd7f386SKyungmin Park 
1595bfd7f386SKyungmin Park 		ret = onenand_verify_oob(mtd, oobbuf, to);
1596bfd7f386SKyungmin Park 		if (ret) {
1597bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret);
1598bfd7f386SKyungmin Park 			break;
1599bfd7f386SKyungmin Park 		}
1600bfd7f386SKyungmin Park 
1601bfd7f386SKyungmin Park 		written += thislen;
1602bfd7f386SKyungmin Park 		if (written == len)
1603bfd7f386SKyungmin Park 			break;
1604bfd7f386SKyungmin Park 
1605bfd7f386SKyungmin Park 		to += mtd->writesize;
1606bfd7f386SKyungmin Park 		buf += thislen;
1607bfd7f386SKyungmin Park 		column = 0;
1608bfd7f386SKyungmin Park 	}
1609bfd7f386SKyungmin Park 
1610bfd7f386SKyungmin Park 	ops->oobretlen = written;
161159829cc1SJean-Christophe PLAGNIOL-VILLARD 
161259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
161359829cc1SJean-Christophe PLAGNIOL-VILLARD }
161459829cc1SJean-Christophe PLAGNIOL-VILLARD 
161559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
161659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write - [MTD Interface] compability function for onenand_write_ecc
161759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
161859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
161959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to write
162059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of written bytes
162159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the data to write
162259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1623bfd7f386SKyungmin Park  * Write with ECC
162459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
162559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
162659829cc1SJean-Christophe PLAGNIOL-VILLARD 		  size_t * retlen, const u_char * buf)
162759829cc1SJean-Christophe PLAGNIOL-VILLARD {
1628bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1629bfd7f386SKyungmin Park 		.len    = len,
1630bfd7f386SKyungmin Park 		.ooblen = 0,
1631bfd7f386SKyungmin Park 		.datbuf = (u_char *) buf,
1632bfd7f386SKyungmin Park 		.oobbuf = NULL,
1633bfd7f386SKyungmin Park 	};
1634bfd7f386SKyungmin Park 	int ret;
1635bfd7f386SKyungmin Park 
1636bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_WRITING);
1637bfd7f386SKyungmin Park 	ret = onenand_write_ops_nolock(mtd, to, &ops);
1638bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1639bfd7f386SKyungmin Park 
1640bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1641bfd7f386SKyungmin Park 	return ret;
164259829cc1SJean-Christophe PLAGNIOL-VILLARD }
164359829cc1SJean-Christophe PLAGNIOL-VILLARD 
164459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
164559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
164659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
164759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
1648bfd7f386SKyungmin Park  * @param ops		oob operation description structure
164959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1650bfd7f386SKyungmin Park  * OneNAND write main and/or out-of-band
165159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1652bfd7f386SKyungmin Park int onenand_write_oob(struct mtd_info *mtd, loff_t to,
1653bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
165459829cc1SJean-Christophe PLAGNIOL-VILLARD {
1655bfd7f386SKyungmin Park 	int ret;
165659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1657bfd7f386SKyungmin Park 	switch (ops->mode) {
1658*dfe64e2cSSergey Lapin 	case MTD_OPS_PLACE_OOB:
1659*dfe64e2cSSergey Lapin 	case MTD_OPS_AUTO_OOB:
1660bfd7f386SKyungmin Park 		break;
1661*dfe64e2cSSergey Lapin 	case MTD_OPS_RAW:
1662bfd7f386SKyungmin Park 		/* Not implemented yet */
1663bfd7f386SKyungmin Park 	default:
166459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
166559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
166659829cc1SJean-Christophe PLAGNIOL-VILLARD 
166759829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_WRITING);
1668bfd7f386SKyungmin Park 	if (ops->datbuf)
1669bfd7f386SKyungmin Park 		ret = onenand_write_ops_nolock(mtd, to, ops);
1670bfd7f386SKyungmin Park 	else
1671bfd7f386SKyungmin Park 		ret = onenand_write_oob_nolock(mtd, to, ops);
167259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
167359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1674bfd7f386SKyungmin Park 	return ret;
167559829cc1SJean-Christophe PLAGNIOL-VILLARD 
167659829cc1SJean-Christophe PLAGNIOL-VILLARD }
167759829cc1SJean-Christophe PLAGNIOL-VILLARD 
167859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1679d438d508SKyungmin Park  * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
1680d438d508SKyungmin Park  * @param mtd		MTD device structure
1681d438d508SKyungmin Park  * @param ofs		offset from device start
1682d438d508SKyungmin Park  * @param allowbbt	1, if its allowed to access the bbt area
1683d438d508SKyungmin Park  *
1684d438d508SKyungmin Park  * Check, if the block is bad, Either by reading the bad block table or
1685d438d508SKyungmin Park  * calling of the scan function.
1686d438d508SKyungmin Park  */
1687d438d508SKyungmin Park static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
1688d438d508SKyungmin Park {
1689d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1690d438d508SKyungmin Park 	struct bbm_info *bbm = this->bbm;
1691d438d508SKyungmin Park 
1692d438d508SKyungmin Park 	/* Return info from the table */
1693d438d508SKyungmin Park 	return bbm->isbad_bbt(mtd, ofs, allowbbt);
1694d438d508SKyungmin Park }
1695d438d508SKyungmin Park 
1696d438d508SKyungmin Park 
1697d438d508SKyungmin Park /**
169859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_erase - [MTD Interface] erase block(s)
169959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
170059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param instr		erase instruction
170159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
170259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Erase one ore more blocks
170359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
170459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
170559829cc1SJean-Christophe PLAGNIOL-VILLARD {
170659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
170759829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int block_size;
1708cacbe919SAmul Kumar Saha 	loff_t addr = instr->addr;
1709cacbe919SAmul Kumar Saha 	unsigned int len = instr->len;
1710cacbe919SAmul Kumar Saha 	int ret = 0, i;
1711cacbe919SAmul Kumar Saha 	struct mtd_erase_region_info *region = NULL;
1712cacbe919SAmul Kumar Saha 	unsigned int region_end = 0;
171359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1714cacbe919SAmul Kumar Saha 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
1715cacbe919SAmul Kumar Saha 			(unsigned int) addr, len);
171659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1717cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
1718cacbe919SAmul Kumar Saha 		/* Find the eraseregion of this address */
1719cacbe919SAmul Kumar Saha 		i = flexonenand_region(mtd, addr);
1720cacbe919SAmul Kumar Saha 		region = &mtd->eraseregions[i];
1721cacbe919SAmul Kumar Saha 
1722cacbe919SAmul Kumar Saha 		block_size = region->erasesize;
1723cacbe919SAmul Kumar Saha 		region_end = region->offset
1724cacbe919SAmul Kumar Saha 			+ region->erasesize * region->numblocks;
1725cacbe919SAmul Kumar Saha 
1726cacbe919SAmul Kumar Saha 		/* Start address within region must align on block boundary.
1727cacbe919SAmul Kumar Saha 		 * Erase region's start offset is always block start address.
1728cacbe919SAmul Kumar Saha 		 */
1729cacbe919SAmul Kumar Saha 		if (unlikely((addr - region->offset) & (block_size - 1))) {
1730cacbe919SAmul Kumar Saha 			MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
1731cacbe919SAmul Kumar Saha 				" Unaligned address\n");
1732cacbe919SAmul Kumar Saha 			return -EINVAL;
1733cacbe919SAmul Kumar Saha 		}
1734cacbe919SAmul Kumar Saha 	} else {
1735cacbe919SAmul Kumar Saha 		block_size = 1 << this->erase_shift;
173659829cc1SJean-Christophe PLAGNIOL-VILLARD 
173759829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Start address must align on block boundary */
1738cacbe919SAmul Kumar Saha 		if (unlikely(addr & (block_size - 1))) {
1739cacbe919SAmul Kumar Saha 			MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
1740cacbe919SAmul Kumar Saha 						"Unaligned address\n");
174159829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EINVAL;
174259829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
1743cacbe919SAmul Kumar Saha 	}
174459829cc1SJean-Christophe PLAGNIOL-VILLARD 
174559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Length must align on block boundary */
1746cacbe919SAmul Kumar Saha 	if (unlikely(len & (block_size - 1))) {
17473167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
174859829cc1SJean-Christophe PLAGNIOL-VILLARD 			 "onenand_erase: Length not block aligned\n");
174959829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
175059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
175159829cc1SJean-Christophe PLAGNIOL-VILLARD 
175259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
175359829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_ERASING);
175459829cc1SJean-Christophe PLAGNIOL-VILLARD 
175559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop throught the pages */
175659829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASING;
175759829cc1SJean-Christophe PLAGNIOL-VILLARD 
175859829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (len) {
175959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1760ef0921d6SKyungmin Park 		/* Check if we have a bad block, we do not erase bad blocks */
1761ef0921d6SKyungmin Park 		if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) {
1762ef0921d6SKyungmin Park 			printk(KERN_WARNING "onenand_erase: attempt to erase"
1763ef0921d6SKyungmin Park 				" a bad block at addr 0x%08x\n",
1764ef0921d6SKyungmin Park 				(unsigned int) addr);
1765ef0921d6SKyungmin Park 			instr->state = MTD_ERASE_FAILED;
1766ef0921d6SKyungmin Park 			goto erase_exit;
1767ef0921d6SKyungmin Park 		}
176859829cc1SJean-Christophe PLAGNIOL-VILLARD 
176959829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
177059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1771d438d508SKyungmin Park 		onenand_invalidate_bufferram(mtd, addr, block_size);
1772d438d508SKyungmin Park 
177359829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_ERASING);
177459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check, if it is write protected */
177559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
177659829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (ret == -EPERM)
17773167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
17783167c538SScott Wood 					  "Device is write protected!!!\n");
177959829cc1SJean-Christophe PLAGNIOL-VILLARD 			else
17803167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
17813167c538SScott Wood 					  "Failed erase, block %d\n",
1782cacbe919SAmul Kumar Saha 					onenand_block(this, addr));
178359829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->state = MTD_ERASE_FAILED;
178459829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->fail_addr = addr;
1785ef0921d6SKyungmin Park 
178659829cc1SJean-Christophe PLAGNIOL-VILLARD 			goto erase_exit;
178759829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
178859829cc1SJean-Christophe PLAGNIOL-VILLARD 
178959829cc1SJean-Christophe PLAGNIOL-VILLARD 		len -= block_size;
179059829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr += block_size;
1791cacbe919SAmul Kumar Saha 
1792cacbe919SAmul Kumar Saha 		if (addr == region_end) {
1793cacbe919SAmul Kumar Saha 			if (!len)
1794cacbe919SAmul Kumar Saha 				break;
1795cacbe919SAmul Kumar Saha 			region++;
1796cacbe919SAmul Kumar Saha 
1797cacbe919SAmul Kumar Saha 			block_size = region->erasesize;
1798cacbe919SAmul Kumar Saha 			region_end = region->offset
1799cacbe919SAmul Kumar Saha 				+ region->erasesize * region->numblocks;
1800cacbe919SAmul Kumar Saha 
1801cacbe919SAmul Kumar Saha 			if (len & (block_size - 1)) {
1802cacbe919SAmul Kumar Saha 				/* This has been checked at MTD
1803cacbe919SAmul Kumar Saha 				 * partitioning level. */
1804cacbe919SAmul Kumar Saha 				printk("onenand_erase: Unaligned address\n");
1805cacbe919SAmul Kumar Saha 				goto erase_exit;
1806cacbe919SAmul Kumar Saha 			}
1807cacbe919SAmul Kumar Saha 		}
180859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
180959829cc1SJean-Christophe PLAGNIOL-VILLARD 
181059829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASE_DONE;
181159829cc1SJean-Christophe PLAGNIOL-VILLARD 
181259829cc1SJean-Christophe PLAGNIOL-VILLARD erase_exit:
181359829cc1SJean-Christophe PLAGNIOL-VILLARD 
181459829cc1SJean-Christophe PLAGNIOL-VILLARD 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
181559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do call back function */
181659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!ret)
181759829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd_erase_callback(instr);
181859829cc1SJean-Christophe PLAGNIOL-VILLARD 
181959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
182059829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
182159829cc1SJean-Christophe PLAGNIOL-VILLARD 
182259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
182359829cc1SJean-Christophe PLAGNIOL-VILLARD }
182459829cc1SJean-Christophe PLAGNIOL-VILLARD 
182559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
182659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync - [MTD Interface] sync
182759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
182859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
182959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Sync is actually a wait for chip ready function
183059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
183159829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_sync(struct mtd_info *mtd)
183259829cc1SJean-Christophe PLAGNIOL-VILLARD {
18333167c538SScott Wood 	MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
183459829cc1SJean-Christophe PLAGNIOL-VILLARD 
183559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
183659829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_SYNCING);
183759829cc1SJean-Christophe PLAGNIOL-VILLARD 
183859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Release it and go back */
183959829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
184059829cc1SJean-Christophe PLAGNIOL-VILLARD }
184159829cc1SJean-Christophe PLAGNIOL-VILLARD 
184259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
184359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
184459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
184559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1846d438d508SKyungmin Park  *
1847d438d508SKyungmin Park  * Check whether the block is bad
184859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
184959829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
185059829cc1SJean-Christophe PLAGNIOL-VILLARD {
1851d438d508SKyungmin Park 	int ret;
1852d438d508SKyungmin Park 
1853d438d508SKyungmin Park 	/* Check for invalid offset */
1854d438d508SKyungmin Park 	if (ofs > mtd->size)
1855d438d508SKyungmin Park 		return -EINVAL;
1856d438d508SKyungmin Park 
1857d438d508SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1858d438d508SKyungmin Park 	ret = onenand_block_isbad_nolock(mtd,ofs, 0);
1859d438d508SKyungmin Park 	onenand_release_device(mtd);
1860d438d508SKyungmin Park 	return ret;
186159829cc1SJean-Christophe PLAGNIOL-VILLARD }
186259829cc1SJean-Christophe PLAGNIOL-VILLARD 
186359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
18641714f51aSKyungmin Park  * onenand_default_block_markbad - [DEFAULT] mark a block bad
18651714f51aSKyungmin Park  * @param mtd           MTD device structure
18661714f51aSKyungmin Park  * @param ofs           offset from device start
18671714f51aSKyungmin Park  *
18681714f51aSKyungmin Park  * This is the default implementation, which can be overridden by
18691714f51aSKyungmin Park  * a hardware specific driver.
18701714f51aSKyungmin Park  */
18711714f51aSKyungmin Park static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
18721714f51aSKyungmin Park {
18731714f51aSKyungmin Park 	struct onenand_chip *this = mtd->priv;
18741714f51aSKyungmin Park 	struct bbm_info *bbm = this->bbm;
18751714f51aSKyungmin Park 	u_char buf[2] = {0, 0};
18761714f51aSKyungmin Park 	struct mtd_oob_ops ops = {
1877*dfe64e2cSSergey Lapin 		.mode = MTD_OPS_PLACE_OOB,
18781714f51aSKyungmin Park 		.ooblen = 2,
18791714f51aSKyungmin Park 		.oobbuf = buf,
18801714f51aSKyungmin Park 		.ooboffs = 0,
18811714f51aSKyungmin Park 	};
18821714f51aSKyungmin Park 	int block;
18831714f51aSKyungmin Park 
18841714f51aSKyungmin Park 	/* Get block number */
1885cacbe919SAmul Kumar Saha 	block = onenand_block(this, ofs);
18861714f51aSKyungmin Park 	if (bbm->bbt)
18871714f51aSKyungmin Park 		bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
18881714f51aSKyungmin Park 
18891714f51aSKyungmin Park 	/* We write two bytes, so we dont have to mess with 16 bit access */
18901714f51aSKyungmin Park 	ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
18911714f51aSKyungmin Park 	return onenand_write_oob_nolock(mtd, ofs, &ops);
18921714f51aSKyungmin Park }
18931714f51aSKyungmin Park 
18941714f51aSKyungmin Park /**
189559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
189659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
189759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1898d438d508SKyungmin Park  *
1899d438d508SKyungmin Park  * Mark the block as bad
190059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
190159829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
190259829cc1SJean-Christophe PLAGNIOL-VILLARD {
1903d438d508SKyungmin Park 	int ret;
1904d438d508SKyungmin Park 
1905d438d508SKyungmin Park 	ret = onenand_block_isbad(mtd, ofs);
1906d438d508SKyungmin Park 	if (ret) {
1907d438d508SKyungmin Park 		/* If it was bad already, return success and do nothing */
1908d438d508SKyungmin Park 		if (ret > 0)
190959829cc1SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1910d438d508SKyungmin Park 		return ret;
1911d438d508SKyungmin Park 	}
1912d438d508SKyungmin Park 
1913*dfe64e2cSSergey Lapin 	ret = mtd_block_markbad(mtd, ofs);
1914d438d508SKyungmin Park 	return ret;
191559829cc1SJean-Christophe PLAGNIOL-VILLARD }
191659829cc1SJean-Christophe PLAGNIOL-VILLARD 
191759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1918ef0921d6SKyungmin Park  * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
191959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
192059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs           offset relative to mtd start
1921ef0921d6SKyungmin Park  * @param len           number of bytes to lock or unlock
1922ef0921d6SKyungmin Park  * @param cmd           lock or unlock command
192359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1924ef0921d6SKyungmin Park  * Lock or unlock one or more blocks
192559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1926ef0921d6SKyungmin Park static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
192759829cc1SJean-Christophe PLAGNIOL-VILLARD {
192859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
192959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int start, end, block, value, status;
193059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1931cacbe919SAmul Kumar Saha 	start = onenand_block(this, ofs);
1932cacbe919SAmul Kumar Saha 	end = onenand_block(this, ofs + len);
193359829cc1SJean-Christophe PLAGNIOL-VILLARD 
193459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Continuous lock scheme */
1935ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK) {
193659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
193759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(start,
193859829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
193959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set end block address */
194059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(end - 1,
194159829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_END_BLOCK_ADDRESS);
194259829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
1943ef0921d6SKyungmin Park 		this->command(mtd, cmd, 0, 0);
194459829cc1SJean-Christophe PLAGNIOL-VILLARD 
194559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
194659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
194759829cc1SJean-Christophe PLAGNIOL-VILLARD 
194859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
194959829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
195059829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
195159829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
195259829cc1SJean-Christophe PLAGNIOL-VILLARD 
195359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
195459829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
195559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
195659829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "wp status = 0x%x\n", status);
195759829cc1SJean-Christophe PLAGNIOL-VILLARD 
195859829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
195959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
196059829cc1SJean-Christophe PLAGNIOL-VILLARD 
196159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Block lock scheme */
1962cacbe919SAmul Kumar Saha 	for (block = start; block < end; block++) {
1963ef0921d6SKyungmin Park 		/* Set block address */
1964ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
1965ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
1966ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
1967ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
1968ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
1969ef0921d6SKyungmin Park 
197059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
197159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(block,
197259829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
197359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
197459829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
197559829cc1SJean-Christophe PLAGNIOL-VILLARD 
197659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
197759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
197859829cc1SJean-Christophe PLAGNIOL-VILLARD 
197959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
198059829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
198159829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
198259829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
198359829cc1SJean-Christophe PLAGNIOL-VILLARD 
198459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
198559829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
198659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
198759829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "block = %d, wp status = 0x%x\n",
198859829cc1SJean-Christophe PLAGNIOL-VILLARD 			       block, status);
198959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
199059829cc1SJean-Christophe PLAGNIOL-VILLARD 
199159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
199259829cc1SJean-Christophe PLAGNIOL-VILLARD }
199359829cc1SJean-Christophe PLAGNIOL-VILLARD 
19944fca3310SStefan Roese #ifdef ONENAND_LINUX
199559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1996ef0921d6SKyungmin Park  * onenand_lock - [MTD Interface] Lock block(s)
1997ef0921d6SKyungmin Park  * @param mtd           MTD device structure
1998ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
1999ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
2000ef0921d6SKyungmin Park  *
2001ef0921d6SKyungmin Park  * Lock one or more blocks
2002ef0921d6SKyungmin Park  */
2003ef0921d6SKyungmin Park static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
2004ef0921d6SKyungmin Park {
2005ef0921d6SKyungmin Park 	int ret;
2006ef0921d6SKyungmin Park 
2007ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
2008ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
2009ef0921d6SKyungmin Park 	onenand_release_device(mtd);
2010ef0921d6SKyungmin Park 	return ret;
2011ef0921d6SKyungmin Park }
2012ef0921d6SKyungmin Park 
2013ef0921d6SKyungmin Park /**
2014ef0921d6SKyungmin Park  * onenand_unlock - [MTD Interface] Unlock block(s)
2015ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2016ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
2017ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
2018ef0921d6SKyungmin Park  *
2019ef0921d6SKyungmin Park  * Unlock one or more blocks
2020ef0921d6SKyungmin Park  */
2021ef0921d6SKyungmin Park static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
2022ef0921d6SKyungmin Park {
2023ef0921d6SKyungmin Park 	int ret;
2024ef0921d6SKyungmin Park 
2025ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
2026ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
2027ef0921d6SKyungmin Park 	onenand_release_device(mtd);
2028ef0921d6SKyungmin Park 	return ret;
2029ef0921d6SKyungmin Park }
20304fca3310SStefan Roese #endif
2031ef0921d6SKyungmin Park 
2032ef0921d6SKyungmin Park /**
2033ef0921d6SKyungmin Park  * onenand_check_lock_status - [OneNAND Interface] Check lock status
2034ef0921d6SKyungmin Park  * @param this          onenand chip data structure
2035ef0921d6SKyungmin Park  *
2036ef0921d6SKyungmin Park  * Check lock status
2037ef0921d6SKyungmin Park  */
2038ef0921d6SKyungmin Park static int onenand_check_lock_status(struct onenand_chip *this)
2039ef0921d6SKyungmin Park {
2040ef0921d6SKyungmin Park 	unsigned int value, block, status;
2041ef0921d6SKyungmin Park 	unsigned int end;
2042ef0921d6SKyungmin Park 
2043ef0921d6SKyungmin Park 	end = this->chipsize >> this->erase_shift;
2044ef0921d6SKyungmin Park 	for (block = 0; block < end; block++) {
2045ef0921d6SKyungmin Park 		/* Set block address */
2046ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
2047ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
2048ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
2049ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
2050ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
2051ef0921d6SKyungmin Park 		/* Set start block address */
2052ef0921d6SKyungmin Park 		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
2053ef0921d6SKyungmin Park 
2054ef0921d6SKyungmin Park 		/* Check lock status */
2055ef0921d6SKyungmin Park 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
2056ef0921d6SKyungmin Park 		if (!(status & ONENAND_WP_US)) {
2057ef0921d6SKyungmin Park 			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
2058ef0921d6SKyungmin Park 			return 0;
2059ef0921d6SKyungmin Park 		}
2060ef0921d6SKyungmin Park 	}
2061ef0921d6SKyungmin Park 
2062ef0921d6SKyungmin Park 	return 1;
2063ef0921d6SKyungmin Park }
2064ef0921d6SKyungmin Park 
2065ef0921d6SKyungmin Park /**
2066ef0921d6SKyungmin Park  * onenand_unlock_all - [OneNAND Interface] unlock all blocks
2067ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2068ef0921d6SKyungmin Park  *
2069ef0921d6SKyungmin Park  * Unlock all blocks
2070ef0921d6SKyungmin Park  */
2071ef0921d6SKyungmin Park static void onenand_unlock_all(struct mtd_info *mtd)
2072ef0921d6SKyungmin Park {
2073ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
2074ef0921d6SKyungmin Park 	loff_t ofs = 0;
2075cacbe919SAmul Kumar Saha 	size_t len = mtd->size;
2076ef0921d6SKyungmin Park 
2077ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
2078ef0921d6SKyungmin Park 		/* Set start block address */
2079ef0921d6SKyungmin Park 		this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
2080ef0921d6SKyungmin Park 		/* Write unlock command */
2081ef0921d6SKyungmin Park 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
2082ef0921d6SKyungmin Park 
2083ef0921d6SKyungmin Park 		/* There's no return value */
2084ef0921d6SKyungmin Park 		this->wait(mtd, FL_LOCKING);
2085ef0921d6SKyungmin Park 
2086ef0921d6SKyungmin Park 		/* Sanity check */
2087ef0921d6SKyungmin Park 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
2088ef0921d6SKyungmin Park 				& ONENAND_CTRL_ONGO)
2089ef0921d6SKyungmin Park 			continue;
2090ef0921d6SKyungmin Park 
2091ef0921d6SKyungmin Park 		/* Check lock status */
2092ef0921d6SKyungmin Park 		if (onenand_check_lock_status(this))
2093ef0921d6SKyungmin Park 			return;
2094ef0921d6SKyungmin Park 
2095ef0921d6SKyungmin Park 		/* Workaround for all block unlock in DDP */
2096cacbe919SAmul Kumar Saha 		if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
2097ef0921d6SKyungmin Park 			/* All blocks on another chip */
2098ef0921d6SKyungmin Park 			ofs = this->chipsize >> 1;
2099ef0921d6SKyungmin Park 			len = this->chipsize >> 1;
2100ef0921d6SKyungmin Park 		}
2101ef0921d6SKyungmin Park 	}
2102ef0921d6SKyungmin Park 
2103ef0921d6SKyungmin Park 	onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
2104ef0921d6SKyungmin Park }
2105ef0921d6SKyungmin Park 
2106ef0921d6SKyungmin Park 
2107ef0921d6SKyungmin Park /**
2108ef0921d6SKyungmin Park  * onenand_check_features - Check and set OneNAND features
2109ef0921d6SKyungmin Park  * @param mtd           MTD data structure
2110ef0921d6SKyungmin Park  *
2111ef0921d6SKyungmin Park  * Check and set OneNAND features
2112ef0921d6SKyungmin Park  * - lock scheme
2113ef0921d6SKyungmin Park  * - two plane
2114ef0921d6SKyungmin Park  */
2115ef0921d6SKyungmin Park static void onenand_check_features(struct mtd_info *mtd)
2116ef0921d6SKyungmin Park {
2117ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
2118ef0921d6SKyungmin Park 	unsigned int density, process;
2119ef0921d6SKyungmin Park 
2120ef0921d6SKyungmin Park 	/* Lock scheme depends on density and process */
2121ef0921d6SKyungmin Park 	density = onenand_get_density(this->device_id);
2122ef0921d6SKyungmin Park 	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
2123ef0921d6SKyungmin Park 
2124ef0921d6SKyungmin Park 	/* Lock scheme */
2125ef0921d6SKyungmin Park 	switch (density) {
2126ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_4Gb:
2127e26fd3d3SLukasz Majewski 		if (ONENAND_IS_DDP(this))
2128ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
2129e26fd3d3SLukasz Majewski 		else
2130e26fd3d3SLukasz Majewski 			this->options |= ONENAND_HAS_4KB_PAGE;
2131ef0921d6SKyungmin Park 
2132ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_2Gb:
2133ef0921d6SKyungmin Park 		/* 2Gb DDP don't have 2 plane */
2134ef0921d6SKyungmin Park 		if (!ONENAND_IS_DDP(this))
2135ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
2136ef0921d6SKyungmin Park 		this->options |= ONENAND_HAS_UNLOCK_ALL;
2137ef0921d6SKyungmin Park 
2138ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_1Gb:
2139ef0921d6SKyungmin Park 		/* A-Die has all block unlock */
2140ef0921d6SKyungmin Park 		if (process)
2141ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_UNLOCK_ALL;
2142ef0921d6SKyungmin Park 		break;
2143ef0921d6SKyungmin Park 
2144ef0921d6SKyungmin Park 	default:
2145ef0921d6SKyungmin Park 		/* Some OneNAND has continuous lock scheme */
2146ef0921d6SKyungmin Park 		if (!process)
2147ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_CONT_LOCK;
2148ef0921d6SKyungmin Park 		break;
2149ef0921d6SKyungmin Park 	}
2150ef0921d6SKyungmin Park 
2151cacbe919SAmul Kumar Saha 	if (ONENAND_IS_MLC(this))
2152e26fd3d3SLukasz Majewski 		this->options |= ONENAND_HAS_4KB_PAGE;
2153e26fd3d3SLukasz Majewski 
2154e26fd3d3SLukasz Majewski 	if (ONENAND_IS_4KB_PAGE(this))
2155cacbe919SAmul Kumar Saha 		this->options &= ~ONENAND_HAS_2PLANE;
2156cacbe919SAmul Kumar Saha 
2157cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
2158cacbe919SAmul Kumar Saha 		this->options &= ~ONENAND_HAS_CONT_LOCK;
2159cacbe919SAmul Kumar Saha 		this->options |= ONENAND_HAS_UNLOCK_ALL;
2160cacbe919SAmul Kumar Saha 	}
2161cacbe919SAmul Kumar Saha 
2162ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK)
2163ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
2164ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL)
2165ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip support all block unlock\n");
2166ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_2PLANE)
2167ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip has 2 plane\n");
2168e26fd3d3SLukasz Majewski 	if (this->options & ONENAND_HAS_4KB_PAGE)
2169e26fd3d3SLukasz Majewski 		printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
2170e26fd3d3SLukasz Majewski 
2171ef0921d6SKyungmin Park }
2172ef0921d6SKyungmin Park 
2173ef0921d6SKyungmin Park /**
217459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_print_device_info - Print device ID
217559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device        device ID
217659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
217759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Print device ID
217859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
2179ef0921d6SKyungmin Park char *onenand_print_device_info(int device, int version)
218059829cc1SJean-Christophe PLAGNIOL-VILLARD {
2181cacbe919SAmul Kumar Saha 	int vcc, demuxed, ddp, density, flexonenand;
2182195ccfc5SFathi BOUDRA 	char *dev_info = malloc(80);
2183ef0921d6SKyungmin Park 	char *p = dev_info;
218459829cc1SJean-Christophe PLAGNIOL-VILLARD 
218559829cc1SJean-Christophe PLAGNIOL-VILLARD 	vcc = device & ONENAND_DEVICE_VCC_MASK;
218659829cc1SJean-Christophe PLAGNIOL-VILLARD 	demuxed = device & ONENAND_DEVICE_IS_DEMUX;
218759829cc1SJean-Christophe PLAGNIOL-VILLARD 	ddp = device & ONENAND_DEVICE_IS_DDP;
2188cacbe919SAmul Kumar Saha 	density = onenand_get_density(device);
2189cacbe919SAmul Kumar Saha 	flexonenand = device & DEVICE_IS_FLEXONENAND;
2190cacbe919SAmul Kumar Saha 	p += sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
219159829cc1SJean-Christophe PLAGNIOL-VILLARD 	       demuxed ? "" : "Muxed ",
2192cacbe919SAmul Kumar Saha 	       flexonenand ? "Flex-" : "",
219359829cc1SJean-Christophe PLAGNIOL-VILLARD 	       ddp ? "(DDP)" : "",
219459829cc1SJean-Christophe PLAGNIOL-VILLARD 	       (16 << density), vcc ? "2.65/3.3" : "1.8", device);
2195195ccfc5SFathi BOUDRA 
2196ef0921d6SKyungmin Park 	sprintf(p, "\nOneNAND version = 0x%04x", version);
2197ef0921d6SKyungmin Park 	printk("%s\n", dev_info);
2198ef0921d6SKyungmin Park 
2199195ccfc5SFathi BOUDRA 	return dev_info;
220059829cc1SJean-Christophe PLAGNIOL-VILLARD }
220159829cc1SJean-Christophe PLAGNIOL-VILLARD 
220259829cc1SJean-Christophe PLAGNIOL-VILLARD static const struct onenand_manufacturers onenand_manuf_ids[] = {
2203456be17dSEnric Balletbo i Serra 	{ONENAND_MFR_NUMONYX, "Numonyx"},
220459829cc1SJean-Christophe PLAGNIOL-VILLARD 	{ONENAND_MFR_SAMSUNG, "Samsung"},
220559829cc1SJean-Christophe PLAGNIOL-VILLARD };
220659829cc1SJean-Christophe PLAGNIOL-VILLARD 
220759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
220859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_maf - Check manufacturer ID
220959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param manuf         manufacturer ID
221059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
221159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check manufacturer ID
221259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
221359829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_maf(int manuf)
221459829cc1SJean-Christophe PLAGNIOL-VILLARD {
2215ef0921d6SKyungmin Park 	int size = ARRAY_SIZE(onenand_manuf_ids);
221659829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i;
221724ccca5eSMarek Vasut #ifdef ONENAND_DEBUG
221824ccca5eSMarek Vasut 	char *name;
221924ccca5eSMarek Vasut #endif
222059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2221cacbe919SAmul Kumar Saha 	for (i = 0; i < size; i++)
222259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (manuf == onenand_manuf_ids[i].id)
222359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
2224ef0921d6SKyungmin Park 
222524ccca5eSMarek Vasut #ifdef ONENAND_DEBUG
2226ef0921d6SKyungmin Park 	if (i < size)
2227ef0921d6SKyungmin Park 		name = onenand_manuf_ids[i].name;
2228ef0921d6SKyungmin Park 	else
2229ef0921d6SKyungmin Park 		name = "Unknown";
223059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2231ef0921d6SKyungmin Park 	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
223259829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
223359829cc1SJean-Christophe PLAGNIOL-VILLARD 
2234ef0921d6SKyungmin Park 	return i == size;
223559829cc1SJean-Christophe PLAGNIOL-VILLARD }
223659829cc1SJean-Christophe PLAGNIOL-VILLARD 
223759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
2238cacbe919SAmul Kumar Saha * flexonenand_get_boundary	- Reads the SLC boundary
2239cacbe919SAmul Kumar Saha * @param onenand_info		- onenand info structure
2240cacbe919SAmul Kumar Saha *
2241cacbe919SAmul Kumar Saha * Fill up boundary[] field in onenand_chip
2242cacbe919SAmul Kumar Saha **/
2243cacbe919SAmul Kumar Saha static int flexonenand_get_boundary(struct mtd_info *mtd)
2244cacbe919SAmul Kumar Saha {
2245cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2246cacbe919SAmul Kumar Saha 	unsigned int die, bdry;
224724ccca5eSMarek Vasut 	int syscfg, locked;
2248cacbe919SAmul Kumar Saha 
2249cacbe919SAmul Kumar Saha 	/* Disable ECC */
2250cacbe919SAmul Kumar Saha 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
2251cacbe919SAmul Kumar Saha 	this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
2252cacbe919SAmul Kumar Saha 
2253cacbe919SAmul Kumar Saha 	for (die = 0; die < this->dies; die++) {
2254cacbe919SAmul Kumar Saha 		this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
2255cacbe919SAmul Kumar Saha 		this->wait(mtd, FL_SYNCING);
2256cacbe919SAmul Kumar Saha 
2257cacbe919SAmul Kumar Saha 		this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
225824ccca5eSMarek Vasut 		this->wait(mtd, FL_READING);
2259cacbe919SAmul Kumar Saha 
2260cacbe919SAmul Kumar Saha 		bdry = this->read_word(this->base + ONENAND_DATARAM);
2261cacbe919SAmul Kumar Saha 		if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
2262cacbe919SAmul Kumar Saha 			locked = 0;
2263cacbe919SAmul Kumar Saha 		else
2264cacbe919SAmul Kumar Saha 			locked = 1;
2265cacbe919SAmul Kumar Saha 		this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
2266cacbe919SAmul Kumar Saha 
2267cacbe919SAmul Kumar Saha 		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
226824ccca5eSMarek Vasut 		this->wait(mtd, FL_RESETING);
2269cacbe919SAmul Kumar Saha 
2270cacbe919SAmul Kumar Saha 		printk(KERN_INFO "Die %d boundary: %d%s\n", die,
2271cacbe919SAmul Kumar Saha 		       this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
2272cacbe919SAmul Kumar Saha 	}
2273cacbe919SAmul Kumar Saha 
2274cacbe919SAmul Kumar Saha 	/* Enable ECC */
2275cacbe919SAmul Kumar Saha 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
2276cacbe919SAmul Kumar Saha 	return 0;
2277cacbe919SAmul Kumar Saha }
2278cacbe919SAmul Kumar Saha 
2279cacbe919SAmul Kumar Saha /**
2280cacbe919SAmul Kumar Saha  * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
2281cacbe919SAmul Kumar Saha  * 			  boundary[], diesize[], mtd->size, mtd->erasesize,
2282cacbe919SAmul Kumar Saha  * 			  mtd->eraseregions
2283cacbe919SAmul Kumar Saha  * @param mtd		- MTD device structure
2284cacbe919SAmul Kumar Saha  */
2285cacbe919SAmul Kumar Saha static void flexonenand_get_size(struct mtd_info *mtd)
2286cacbe919SAmul Kumar Saha {
2287cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2288cacbe919SAmul Kumar Saha 	int die, i, eraseshift, density;
2289cacbe919SAmul Kumar Saha 	int blksperdie, maxbdry;
2290cacbe919SAmul Kumar Saha 	loff_t ofs;
2291cacbe919SAmul Kumar Saha 
2292cacbe919SAmul Kumar Saha 	density = onenand_get_density(this->device_id);
2293cacbe919SAmul Kumar Saha 	blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
2294cacbe919SAmul Kumar Saha 	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
2295cacbe919SAmul Kumar Saha 	maxbdry = blksperdie - 1;
2296cacbe919SAmul Kumar Saha 	eraseshift = this->erase_shift - 1;
2297cacbe919SAmul Kumar Saha 
2298cacbe919SAmul Kumar Saha 	mtd->numeraseregions = this->dies << 1;
2299cacbe919SAmul Kumar Saha 
2300cacbe919SAmul Kumar Saha 	/* This fills up the device boundary */
2301cacbe919SAmul Kumar Saha 	flexonenand_get_boundary(mtd);
2302cacbe919SAmul Kumar Saha 	die = 0;
2303cacbe919SAmul Kumar Saha 	ofs = 0;
2304cacbe919SAmul Kumar Saha 	i = -1;
2305cacbe919SAmul Kumar Saha 	for (; die < this->dies; die++) {
2306cacbe919SAmul Kumar Saha 		if (!die || this->boundary[die-1] != maxbdry) {
2307cacbe919SAmul Kumar Saha 			i++;
2308cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].offset = ofs;
2309cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize = 1 << eraseshift;
2310cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks =
2311cacbe919SAmul Kumar Saha 							this->boundary[die] + 1;
2312cacbe919SAmul Kumar Saha 			ofs += mtd->eraseregions[i].numblocks << eraseshift;
2313cacbe919SAmul Kumar Saha 			eraseshift++;
2314cacbe919SAmul Kumar Saha 		} else {
2315cacbe919SAmul Kumar Saha 			mtd->numeraseregions -= 1;
2316cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks +=
2317cacbe919SAmul Kumar Saha 							this->boundary[die] + 1;
2318cacbe919SAmul Kumar Saha 			ofs += (this->boundary[die] + 1) << (eraseshift - 1);
2319cacbe919SAmul Kumar Saha 		}
2320cacbe919SAmul Kumar Saha 		if (this->boundary[die] != maxbdry) {
2321cacbe919SAmul Kumar Saha 			i++;
2322cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].offset = ofs;
2323cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize = 1 << eraseshift;
2324cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks = maxbdry ^
2325cacbe919SAmul Kumar Saha 							 this->boundary[die];
2326cacbe919SAmul Kumar Saha 			ofs += mtd->eraseregions[i].numblocks << eraseshift;
2327cacbe919SAmul Kumar Saha 			eraseshift--;
2328cacbe919SAmul Kumar Saha 		} else
2329cacbe919SAmul Kumar Saha 			mtd->numeraseregions -= 1;
2330cacbe919SAmul Kumar Saha 	}
2331cacbe919SAmul Kumar Saha 
2332cacbe919SAmul Kumar Saha 	/* Expose MLC erase size except when all blocks are SLC */
2333cacbe919SAmul Kumar Saha 	mtd->erasesize = 1 << this->erase_shift;
2334cacbe919SAmul Kumar Saha 	if (mtd->numeraseregions == 1)
2335cacbe919SAmul Kumar Saha 		mtd->erasesize >>= 1;
2336cacbe919SAmul Kumar Saha 
2337cacbe919SAmul Kumar Saha 	printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
2338cacbe919SAmul Kumar Saha 	for (i = 0; i < mtd->numeraseregions; i++)
2339cacbe919SAmul Kumar Saha 		printk(KERN_INFO "[offset: 0x%08llx, erasesize: 0x%05x,"
2340cacbe919SAmul Kumar Saha 			" numblocks: %04u]\n", mtd->eraseregions[i].offset,
2341cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize,
2342cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks);
2343cacbe919SAmul Kumar Saha 
2344cacbe919SAmul Kumar Saha 	for (die = 0, mtd->size = 0; die < this->dies; die++) {
2345cacbe919SAmul Kumar Saha 		this->diesize[die] = (loff_t) (blksperdie << this->erase_shift);
2346cacbe919SAmul Kumar Saha 		this->diesize[die] -= (loff_t) (this->boundary[die] + 1)
2347cacbe919SAmul Kumar Saha 						 << (this->erase_shift - 1);
2348cacbe919SAmul Kumar Saha 		mtd->size += this->diesize[die];
2349cacbe919SAmul Kumar Saha 	}
2350cacbe919SAmul Kumar Saha }
2351cacbe919SAmul Kumar Saha 
2352cacbe919SAmul Kumar Saha /**
2353cacbe919SAmul Kumar Saha  * flexonenand_check_blocks_erased - Check if blocks are erased
2354cacbe919SAmul Kumar Saha  * @param mtd_info	- mtd info structure
2355cacbe919SAmul Kumar Saha  * @param start		- first erase block to check
2356cacbe919SAmul Kumar Saha  * @param end		- last erase block to check
2357cacbe919SAmul Kumar Saha  *
2358cacbe919SAmul Kumar Saha  * Converting an unerased block from MLC to SLC
2359cacbe919SAmul Kumar Saha  * causes byte values to change. Since both data and its ECC
2360cacbe919SAmul Kumar Saha  * have changed, reads on the block give uncorrectable error.
2361cacbe919SAmul Kumar Saha  * This might lead to the block being detected as bad.
2362cacbe919SAmul Kumar Saha  *
2363cacbe919SAmul Kumar Saha  * Avoid this by ensuring that the block to be converted is
2364cacbe919SAmul Kumar Saha  * erased.
2365cacbe919SAmul Kumar Saha  */
2366cacbe919SAmul Kumar Saha static int flexonenand_check_blocks_erased(struct mtd_info *mtd,
2367cacbe919SAmul Kumar Saha 					int start, int end)
2368cacbe919SAmul Kumar Saha {
2369cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2370cacbe919SAmul Kumar Saha 	int i, ret;
2371cacbe919SAmul Kumar Saha 	int block;
2372cacbe919SAmul Kumar Saha 	struct mtd_oob_ops ops = {
2373*dfe64e2cSSergey Lapin 		.mode = MTD_OPS_PLACE_OOB,
2374cacbe919SAmul Kumar Saha 		.ooboffs = 0,
2375cacbe919SAmul Kumar Saha 		.ooblen	= mtd->oobsize,
2376cacbe919SAmul Kumar Saha 		.datbuf	= NULL,
2377cacbe919SAmul Kumar Saha 		.oobbuf	= this->oob_buf,
2378cacbe919SAmul Kumar Saha 	};
2379cacbe919SAmul Kumar Saha 	loff_t addr;
2380cacbe919SAmul Kumar Saha 
2381cacbe919SAmul Kumar Saha 	printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
2382cacbe919SAmul Kumar Saha 
2383cacbe919SAmul Kumar Saha 	for (block = start; block <= end; block++) {
2384cacbe919SAmul Kumar Saha 		addr = flexonenand_addr(this, block);
2385cacbe919SAmul Kumar Saha 		if (onenand_block_isbad_nolock(mtd, addr, 0))
2386cacbe919SAmul Kumar Saha 			continue;
2387cacbe919SAmul Kumar Saha 
2388cacbe919SAmul Kumar Saha 		/*
2389cacbe919SAmul Kumar Saha 		 * Since main area write results in ECC write to spare,
2390cacbe919SAmul Kumar Saha 		 * it is sufficient to check only ECC bytes for change.
2391cacbe919SAmul Kumar Saha 		 */
2392cacbe919SAmul Kumar Saha 		ret = onenand_read_oob_nolock(mtd, addr, &ops);
2393cacbe919SAmul Kumar Saha 		if (ret)
2394cacbe919SAmul Kumar Saha 			return ret;
2395cacbe919SAmul Kumar Saha 
2396cacbe919SAmul Kumar Saha 		for (i = 0; i < mtd->oobsize; i++)
2397cacbe919SAmul Kumar Saha 			if (this->oob_buf[i] != 0xff)
2398cacbe919SAmul Kumar Saha 				break;
2399cacbe919SAmul Kumar Saha 
2400cacbe919SAmul Kumar Saha 		if (i != mtd->oobsize) {
2401cacbe919SAmul Kumar Saha 			printk(KERN_WARNING "Block %d not erased.\n", block);
2402cacbe919SAmul Kumar Saha 			return 1;
2403cacbe919SAmul Kumar Saha 		}
2404cacbe919SAmul Kumar Saha 	}
2405cacbe919SAmul Kumar Saha 
2406cacbe919SAmul Kumar Saha 	return 0;
2407cacbe919SAmul Kumar Saha }
2408cacbe919SAmul Kumar Saha 
2409cacbe919SAmul Kumar Saha /**
2410cacbe919SAmul Kumar Saha  * flexonenand_set_boundary	- Writes the SLC boundary
2411cacbe919SAmul Kumar Saha  * @param mtd			- mtd info structure
2412cacbe919SAmul Kumar Saha  */
2413cacbe919SAmul Kumar Saha int flexonenand_set_boundary(struct mtd_info *mtd, int die,
2414cacbe919SAmul Kumar Saha 				    int boundary, int lock)
2415cacbe919SAmul Kumar Saha {
2416cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2417cacbe919SAmul Kumar Saha 	int ret, density, blksperdie, old, new, thisboundary;
2418cacbe919SAmul Kumar Saha 	loff_t addr;
2419cacbe919SAmul Kumar Saha 
2420cacbe919SAmul Kumar Saha 	if (die >= this->dies)
2421cacbe919SAmul Kumar Saha 		return -EINVAL;
2422cacbe919SAmul Kumar Saha 
2423cacbe919SAmul Kumar Saha 	if (boundary == this->boundary[die])
2424cacbe919SAmul Kumar Saha 		return 0;
2425cacbe919SAmul Kumar Saha 
2426cacbe919SAmul Kumar Saha 	density = onenand_get_density(this->device_id);
2427cacbe919SAmul Kumar Saha 	blksperdie = ((16 << density) << 20) >> this->erase_shift;
2428cacbe919SAmul Kumar Saha 	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
2429cacbe919SAmul Kumar Saha 
2430cacbe919SAmul Kumar Saha 	if (boundary >= blksperdie) {
2431cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2432cacbe919SAmul Kumar Saha 			"Invalid boundary value. "
2433cacbe919SAmul Kumar Saha 			"Boundary not changed.\n");
2434cacbe919SAmul Kumar Saha 		return -EINVAL;
2435cacbe919SAmul Kumar Saha 	}
2436cacbe919SAmul Kumar Saha 
2437cacbe919SAmul Kumar Saha 	/* Check if converting blocks are erased */
2438cacbe919SAmul Kumar Saha 	old = this->boundary[die] + (die * this->density_mask);
2439cacbe919SAmul Kumar Saha 	new = boundary + (die * this->density_mask);
2440cacbe919SAmul Kumar Saha 	ret = flexonenand_check_blocks_erased(mtd, min(old, new)
2441cacbe919SAmul Kumar Saha 						+ 1, max(old, new));
2442cacbe919SAmul Kumar Saha 	if (ret) {
2443cacbe919SAmul Kumar Saha 		printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n");
2444cacbe919SAmul Kumar Saha 		return ret;
2445cacbe919SAmul Kumar Saha 	}
2446cacbe919SAmul Kumar Saha 
2447cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
2448cacbe919SAmul Kumar Saha 	this->wait(mtd, FL_SYNCING);
2449cacbe919SAmul Kumar Saha 
2450cacbe919SAmul Kumar Saha 	/* Check is boundary is locked */
2451cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
2452cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_READING);
2453cacbe919SAmul Kumar Saha 
2454cacbe919SAmul Kumar Saha 	thisboundary = this->read_word(this->base + ONENAND_DATARAM);
2455cacbe919SAmul Kumar Saha 	if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
2456cacbe919SAmul Kumar Saha 		printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n");
2457cacbe919SAmul Kumar Saha 		goto out;
2458cacbe919SAmul Kumar Saha 	}
2459cacbe919SAmul Kumar Saha 
2460cacbe919SAmul Kumar Saha 	printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n",
2461cacbe919SAmul Kumar Saha 			die, boundary, lock ? "(Locked)" : "(Unlocked)");
2462cacbe919SAmul Kumar Saha 
2463cacbe919SAmul Kumar Saha 	boundary &= FLEXONENAND_PI_MASK;
2464cacbe919SAmul Kumar Saha 	boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
2465cacbe919SAmul Kumar Saha 
2466cacbe919SAmul Kumar Saha 	addr = die ? this->diesize[0] : 0;
2467cacbe919SAmul Kumar Saha 	this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
2468cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_ERASING);
2469cacbe919SAmul Kumar Saha 	if (ret) {
2470cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2471cacbe919SAmul Kumar Saha 			"Failed PI erase for Die %d\n", die);
2472cacbe919SAmul Kumar Saha 		goto out;
2473cacbe919SAmul Kumar Saha 	}
2474cacbe919SAmul Kumar Saha 
2475cacbe919SAmul Kumar Saha 	this->write_word(boundary, this->base + ONENAND_DATARAM);
2476cacbe919SAmul Kumar Saha 	this->command(mtd, ONENAND_CMD_PROG, addr, 0);
2477cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_WRITING);
2478cacbe919SAmul Kumar Saha 	if (ret) {
2479cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2480cacbe919SAmul Kumar Saha 			"Failed PI write for Die %d\n", die);
2481cacbe919SAmul Kumar Saha 		goto out;
2482cacbe919SAmul Kumar Saha 	}
2483cacbe919SAmul Kumar Saha 
2484cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
2485cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_WRITING);
2486cacbe919SAmul Kumar Saha out:
2487cacbe919SAmul Kumar Saha 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
2488cacbe919SAmul Kumar Saha 	this->wait(mtd, FL_RESETING);
2489cacbe919SAmul Kumar Saha 	if (!ret)
2490cacbe919SAmul Kumar Saha 		/* Recalculate device size on boundary change*/
2491cacbe919SAmul Kumar Saha 		flexonenand_get_size(mtd);
2492cacbe919SAmul Kumar Saha 
2493cacbe919SAmul Kumar Saha 	return ret;
2494cacbe919SAmul Kumar Saha }
2495cacbe919SAmul Kumar Saha 
2496cacbe919SAmul Kumar Saha /**
24976b3967bbSLukasz Majewski  * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND chip
249859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
249959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
250059829cc1SJean-Christophe PLAGNIOL-VILLARD  * OneNAND detection method:
250159829cc1SJean-Christophe PLAGNIOL-VILLARD  *   Compare the the values from command with ones from register
250259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
25036b3967bbSLukasz Majewski static int onenand_chip_probe(struct mtd_info *mtd)
250459829cc1SJean-Christophe PLAGNIOL-VILLARD {
250559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
25066b3967bbSLukasz Majewski 	int bram_maf_id, bram_dev_id, maf_id, dev_id;
2507ef0921d6SKyungmin Park 	int syscfg;
2508ef0921d6SKyungmin Park 
2509ef0921d6SKyungmin Park 	/* Save system configuration 1 */
2510ef0921d6SKyungmin Park 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
25116b3967bbSLukasz Majewski 
2512ef0921d6SKyungmin Park 	/* Clear Sync. Burst Read mode to read BootRAM */
25136b3967bbSLukasz Majewski 	this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ),
25146b3967bbSLukasz Majewski 			 this->base + ONENAND_REG_SYS_CFG1);
251559829cc1SJean-Christophe PLAGNIOL-VILLARD 
251659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Send the command for reading device ID from BootRAM */
251759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
251859829cc1SJean-Christophe PLAGNIOL-VILLARD 
251959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from BootRAM */
252059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
252159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
252259829cc1SJean-Christophe PLAGNIOL-VILLARD 
252359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reset OneNAND to read default register values */
252459829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
252559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2526d438d508SKyungmin Park 	/* Wait reset */
2527d438d508SKyungmin Park 	this->wait(mtd, FL_RESETING);
252859829cc1SJean-Christophe PLAGNIOL-VILLARD 
2529ef0921d6SKyungmin Park 	/* Restore system configuration 1 */
2530ef0921d6SKyungmin Park 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
2531ef0921d6SKyungmin Park 
2532ef0921d6SKyungmin Park 	/* Check manufacturer ID */
2533ef0921d6SKyungmin Park 	if (onenand_check_maf(bram_maf_id))
2534ef0921d6SKyungmin Park 		return -ENXIO;
2535ef0921d6SKyungmin Park 
253659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from Register */
253759829cc1SJean-Christophe PLAGNIOL-VILLARD 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
253859829cc1SJean-Christophe PLAGNIOL-VILLARD 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
253959829cc1SJean-Christophe PLAGNIOL-VILLARD 
254059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Check OneNAND device */
254159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (maf_id != bram_maf_id || dev_id != bram_dev_id)
254259829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
254359829cc1SJean-Christophe PLAGNIOL-VILLARD 
25446b3967bbSLukasz Majewski 	return 0;
25456b3967bbSLukasz Majewski }
25466b3967bbSLukasz Majewski 
25476b3967bbSLukasz Majewski /**
25486b3967bbSLukasz Majewski  * onenand_probe - [OneNAND Interface] Probe the OneNAND device
25496b3967bbSLukasz Majewski  * @param mtd		MTD device structure
25506b3967bbSLukasz Majewski  *
25516b3967bbSLukasz Majewski  * OneNAND detection method:
25526b3967bbSLukasz Majewski  *   Compare the the values from command with ones from register
25536b3967bbSLukasz Majewski  */
25546b3967bbSLukasz Majewski int onenand_probe(struct mtd_info *mtd)
25556b3967bbSLukasz Majewski {
25566b3967bbSLukasz Majewski 	struct onenand_chip *this = mtd->priv;
25571432c763SWolfgang Denk 	int dev_id, ver_id;
25586b3967bbSLukasz Majewski 	int density;
25596b3967bbSLukasz Majewski 	int ret;
25606b3967bbSLukasz Majewski 
25616b3967bbSLukasz Majewski 	ret = this->chip_probe(mtd);
25626b3967bbSLukasz Majewski 	if (ret)
25636b3967bbSLukasz Majewski 		return ret;
25646b3967bbSLukasz Majewski 
25651432c763SWolfgang Denk 	/* Read device IDs from Register */
25666b3967bbSLukasz Majewski 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
25676b3967bbSLukasz Majewski 	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
25686b3967bbSLukasz Majewski 	this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
25696b3967bbSLukasz Majewski 
257059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash device information */
2571ef0921d6SKyungmin Park 	mtd->name = onenand_print_device_info(dev_id, ver_id);
257259829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->device_id = dev_id;
25738cf11f3aSStefan Roese 	this->version_id = ver_id;
257459829cc1SJean-Christophe PLAGNIOL-VILLARD 
2575e26fd3d3SLukasz Majewski 	/* Check OneNAND features */
2576e26fd3d3SLukasz Majewski 	onenand_check_features(mtd);
2577e26fd3d3SLukasz Majewski 
2578ef0921d6SKyungmin Park 	density = onenand_get_density(dev_id);
2579cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
2580cacbe919SAmul Kumar Saha 		this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
2581cacbe919SAmul Kumar Saha 		/* Maximum possible erase regions */
2582cacbe919SAmul Kumar Saha 		mtd->numeraseregions = this->dies << 1;
2583cacbe919SAmul Kumar Saha 		mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info)
2584cacbe919SAmul Kumar Saha 					* (this->dies << 1));
2585cacbe919SAmul Kumar Saha 		if (!mtd->eraseregions)
2586cacbe919SAmul Kumar Saha 			return -ENOMEM;
2587cacbe919SAmul Kumar Saha 	}
2588cacbe919SAmul Kumar Saha 
2589cacbe919SAmul Kumar Saha 	/*
2590cacbe919SAmul Kumar Saha 	 * For Flex-OneNAND, chipsize represents maximum possible device size.
2591cacbe919SAmul Kumar Saha 	 * mtd->size represents the actual device size.
2592cacbe919SAmul Kumar Saha 	 */
259359829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->chipsize = (16 << density) << 20;
259459829cc1SJean-Christophe PLAGNIOL-VILLARD 
259559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* OneNAND page size & block size */
259659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* The data buffer size is equal to page size */
2597d438d508SKyungmin Park 	mtd->writesize =
259859829cc1SJean-Christophe PLAGNIOL-VILLARD 	    this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
2599cacbe919SAmul Kumar Saha 	/* We use the full BufferRAM */
2600e26fd3d3SLukasz Majewski 	if (ONENAND_IS_4KB_PAGE(this))
2601cacbe919SAmul Kumar Saha 		mtd->writesize <<= 1;
2602cacbe919SAmul Kumar Saha 
2603d438d508SKyungmin Park 	mtd->oobsize = mtd->writesize >> 5;
260459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Pagers per block is always 64 in OneNAND */
2605d438d508SKyungmin Park 	mtd->erasesize = mtd->writesize << 6;
2606cacbe919SAmul Kumar Saha 	/*
2607cacbe919SAmul Kumar Saha 	 * Flex-OneNAND SLC area has 64 pages per block.
2608cacbe919SAmul Kumar Saha 	 * Flex-OneNAND MLC area has 128 pages per block.
2609cacbe919SAmul Kumar Saha 	 * Expose MLC erase size to find erase_shift and page_mask.
2610cacbe919SAmul Kumar Saha 	 */
2611cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this))
2612cacbe919SAmul Kumar Saha 		mtd->erasesize <<= 1;
261359829cc1SJean-Christophe PLAGNIOL-VILLARD 
261459829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->erase_shift = ffs(mtd->erasesize) - 1;
2615d438d508SKyungmin Park 	this->page_shift = ffs(mtd->writesize) - 1;
261659829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->ppb_shift = (this->erase_shift - this->page_shift);
2617d438d508SKyungmin Park 	this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
2618cacbe919SAmul Kumar Saha 	/* Set density mask. it is used for DDP */
2619cacbe919SAmul Kumar Saha 	if (ONENAND_IS_DDP(this))
2620cacbe919SAmul Kumar Saha 		this->density_mask = this->chipsize >> (this->erase_shift + 1);
2621bfd7f386SKyungmin Park 	/* It's real page size */
2622bfd7f386SKyungmin Park 	this->writesize = mtd->writesize;
262359829cc1SJean-Christophe PLAGNIOL-VILLARD 
262459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* REVIST: Multichip handling */
262559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2626cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this))
2627cacbe919SAmul Kumar Saha 		flexonenand_get_size(mtd);
2628cacbe919SAmul Kumar Saha 	else
262959829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd->size = this->chipsize;
263059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2631d438d508SKyungmin Park 	mtd->flags = MTD_CAP_NANDFLASH;
2632*dfe64e2cSSergey Lapin 	mtd->_erase = onenand_erase;
2633*dfe64e2cSSergey Lapin 	mtd->_read = onenand_read;
2634*dfe64e2cSSergey Lapin 	mtd->_write = onenand_write;
2635*dfe64e2cSSergey Lapin 	mtd->_read_oob = onenand_read_oob;
2636*dfe64e2cSSergey Lapin 	mtd->_write_oob = onenand_write_oob;
2637*dfe64e2cSSergey Lapin 	mtd->_sync = onenand_sync;
2638*dfe64e2cSSergey Lapin 	mtd->_block_isbad = onenand_block_isbad;
2639*dfe64e2cSSergey Lapin 	mtd->_block_markbad = onenand_block_markbad;
2640195ccfc5SFathi BOUDRA 
264159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
264259829cc1SJean-Christophe PLAGNIOL-VILLARD }
264359829cc1SJean-Christophe PLAGNIOL-VILLARD 
264459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
264559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
264659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
264759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param maxchips	Number of chips to scan for
264859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
264959829cc1SJean-Christophe PLAGNIOL-VILLARD  * This fills out all the not initialized function pointers
265059829cc1SJean-Christophe PLAGNIOL-VILLARD  * with the defaults.
265159829cc1SJean-Christophe PLAGNIOL-VILLARD  * The flash ID is read and the mtd/chip structures are
265259829cc1SJean-Christophe PLAGNIOL-VILLARD  * filled with the appropriate values.
265359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
265459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_scan(struct mtd_info *mtd, int maxchips)
265559829cc1SJean-Christophe PLAGNIOL-VILLARD {
26561ae39862SStefan Roese 	int i;
265759829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
265859829cc1SJean-Christophe PLAGNIOL-VILLARD 
265959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_word)
266059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_word = onenand_readw;
266159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_word)
266259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word = onenand_writew;
266359829cc1SJean-Christophe PLAGNIOL-VILLARD 
266459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->command)
266559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command = onenand_command;
266659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->wait)
266759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait = onenand_wait;
2668ef0921d6SKyungmin Park 	if (!this->bbt_wait)
2669ef0921d6SKyungmin Park 		this->bbt_wait = onenand_bbt_wait;
267059829cc1SJean-Christophe PLAGNIOL-VILLARD 
267159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_bufferram)
267259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_read_bufferram;
267359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_bufferram)
267459829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_bufferram = onenand_write_bufferram;
267559829cc1SJean-Christophe PLAGNIOL-VILLARD 
26766b3967bbSLukasz Majewski 	if (!this->chip_probe)
26776b3967bbSLukasz Majewski 		this->chip_probe = onenand_chip_probe;
26786b3967bbSLukasz Majewski 
26791714f51aSKyungmin Park 	if (!this->block_markbad)
26801714f51aSKyungmin Park 		this->block_markbad = onenand_default_block_markbad;
2681ef0921d6SKyungmin Park 	if (!this->scan_bbt)
2682ef0921d6SKyungmin Park 		this->scan_bbt = onenand_default_bbt;
2683ef0921d6SKyungmin Park 
268459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (onenand_probe(mtd))
268559829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
268659829cc1SJean-Christophe PLAGNIOL-VILLARD 
268759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Set Sync. Burst Read after probing */
268859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (this->mmcontrol) {
268959829cc1SJean-Christophe PLAGNIOL-VILLARD 		printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
269059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_sync_read_bufferram;
269159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
269259829cc1SJean-Christophe PLAGNIOL-VILLARD 
2693bfd7f386SKyungmin Park 	/* Allocate buffers, if necessary */
2694bfd7f386SKyungmin Park 	if (!this->page_buf) {
2695bfd7f386SKyungmin Park 		this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
2696bfd7f386SKyungmin Park 		if (!this->page_buf) {
2697bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
2698bfd7f386SKyungmin Park 			return -ENOMEM;
2699bfd7f386SKyungmin Park 		}
2700bfd7f386SKyungmin Park 		this->options |= ONENAND_PAGEBUF_ALLOC;
2701bfd7f386SKyungmin Park 	}
2702bfd7f386SKyungmin Park 	if (!this->oob_buf) {
2703bfd7f386SKyungmin Park 		this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
2704bfd7f386SKyungmin Park 		if (!this->oob_buf) {
2705bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n");
2706bfd7f386SKyungmin Park 			if (this->options & ONENAND_PAGEBUF_ALLOC) {
2707bfd7f386SKyungmin Park 				this->options &= ~ONENAND_PAGEBUF_ALLOC;
2708bfd7f386SKyungmin Park 				kfree(this->page_buf);
2709bfd7f386SKyungmin Park 			}
2710bfd7f386SKyungmin Park 			return -ENOMEM;
2711bfd7f386SKyungmin Park 		}
2712bfd7f386SKyungmin Park 		this->options |= ONENAND_OOBBUF_ALLOC;
2713bfd7f386SKyungmin Park 	}
2714bfd7f386SKyungmin Park 
27151ae39862SStefan Roese 	this->state = FL_READY;
27161ae39862SStefan Roese 
27171ae39862SStefan Roese 	/*
27181ae39862SStefan Roese 	 * Allow subpage writes up to oobsize.
27191ae39862SStefan Roese 	 */
27201ae39862SStefan Roese 	switch (mtd->oobsize) {
2721cacbe919SAmul Kumar Saha 	case 128:
2722cacbe919SAmul Kumar Saha 		this->ecclayout = &onenand_oob_128;
2723cacbe919SAmul Kumar Saha 		mtd->subpage_sft = 0;
2724cacbe919SAmul Kumar Saha 		break;
2725cacbe919SAmul Kumar Saha 
27261ae39862SStefan Roese 	case 64:
27271ae39862SStefan Roese 		this->ecclayout = &onenand_oob_64;
27281ae39862SStefan Roese 		mtd->subpage_sft = 2;
27291ae39862SStefan Roese 		break;
27301ae39862SStefan Roese 
27311ae39862SStefan Roese 	case 32:
27321ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
27331ae39862SStefan Roese 		mtd->subpage_sft = 1;
27341ae39862SStefan Roese 		break;
27351ae39862SStefan Roese 
27361ae39862SStefan Roese 	default:
27371ae39862SStefan Roese 		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
27381ae39862SStefan Roese 			mtd->oobsize);
27391ae39862SStefan Roese 		mtd->subpage_sft = 0;
27401ae39862SStefan Roese 		/* To prevent kernel oops */
27411ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
27421ae39862SStefan Roese 		break;
27431ae39862SStefan Roese 	}
27441ae39862SStefan Roese 
27451ae39862SStefan Roese 	this->subpagesize = mtd->writesize >> mtd->subpage_sft;
27461ae39862SStefan Roese 
27471ae39862SStefan Roese 	/*
27481ae39862SStefan Roese 	 * The number of bytes available for a client to place data into
27491ae39862SStefan Roese 	 * the out of band area
27501ae39862SStefan Roese 	 */
27511ae39862SStefan Roese 	this->ecclayout->oobavail = 0;
27521ae39862SStefan Roese 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
27531ae39862SStefan Roese 	    this->ecclayout->oobfree[i].length; i++)
27541ae39862SStefan Roese 		this->ecclayout->oobavail +=
27551ae39862SStefan Roese 			this->ecclayout->oobfree[i].length;
27561ae39862SStefan Roese 	mtd->oobavail = this->ecclayout->oobavail;
27571ae39862SStefan Roese 
27581ae39862SStefan Roese 	mtd->ecclayout = this->ecclayout;
27591ae39862SStefan Roese 
2760ef0921d6SKyungmin Park 	/* Unlock whole block */
2761ef0921d6SKyungmin Park 	onenand_unlock_all(mtd);
276259829cc1SJean-Christophe PLAGNIOL-VILLARD 
2763ef0921d6SKyungmin Park 	return this->scan_bbt(mtd);
276459829cc1SJean-Christophe PLAGNIOL-VILLARD }
276559829cc1SJean-Christophe PLAGNIOL-VILLARD 
276659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
276759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
276859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
276959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
277059829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_release(struct mtd_info *mtd)
277159829cc1SJean-Christophe PLAGNIOL-VILLARD {
277259829cc1SJean-Christophe PLAGNIOL-VILLARD }
2773