xref: /openbmc/u-boot/drivers/mtd/onenand/onenand_base.c (revision 592cd5defd4f71d34ffcbd8dd3326bc10f662e20)
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>
23d9098ee5SLadislav Michl #include <watchdog.h>
247b15e2bbSMike Frysinger #include <linux/compat.h>
2559829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/mtd.h>
26ff94bc40SHeiko Schocher #include "linux/mtd/flashchip.h"
2759829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/onenand.h>
2859829cc1SJean-Christophe PLAGNIOL-VILLARD 
2959829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h>
301221ce45SMasahiro Yamada #include <linux/errno.h>
31195ccfc5SFathi BOUDRA #include <malloc.h>
3259829cc1SJean-Christophe PLAGNIOL-VILLARD 
3377e475ccSKyungmin Park /* It should access 16-bit instead of 8-bit */
memcpy_16(void * dst,const void * src,unsigned int len)34cacbe919SAmul Kumar Saha static void *memcpy_16(void *dst, const void *src, unsigned int len)
3577e475ccSKyungmin Park {
3677e475ccSKyungmin Park 	void *ret = dst;
3777e475ccSKyungmin Park 	short *d = dst;
3877e475ccSKyungmin Park 	const short *s = src;
3977e475ccSKyungmin Park 
4077e475ccSKyungmin Park 	len >>= 1;
4177e475ccSKyungmin Park 	while (len-- > 0)
4277e475ccSKyungmin Park 		*d++ = *s++;
4377e475ccSKyungmin Park 	return ret;
4477e475ccSKyungmin Park }
4577e475ccSKyungmin Park 
461ae39862SStefan Roese /**
47cacbe919SAmul Kumar Saha  *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
48cacbe919SAmul Kumar Saha  *  For now, we expose only 64 out of 80 ecc bytes
49cacbe919SAmul Kumar Saha  */
50cacbe919SAmul Kumar Saha static struct nand_ecclayout onenand_oob_128 = {
51cacbe919SAmul Kumar Saha 	.eccbytes	= 64,
52cacbe919SAmul Kumar Saha 	.eccpos		= {
53cacbe919SAmul Kumar Saha 		6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
54cacbe919SAmul Kumar Saha 		22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
55cacbe919SAmul Kumar Saha 		38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
56cacbe919SAmul Kumar Saha 		54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
57cacbe919SAmul Kumar Saha 		70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
58cacbe919SAmul Kumar Saha 		86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
59cacbe919SAmul Kumar Saha 		102, 103, 104, 105
60cacbe919SAmul Kumar Saha 		},
61cacbe919SAmul Kumar Saha 	.oobfree	= {
62cacbe919SAmul Kumar Saha 		{2, 4}, {18, 4}, {34, 4}, {50, 4},
63cacbe919SAmul Kumar Saha 		{66, 4}, {82, 4}, {98, 4}, {114, 4}
64cacbe919SAmul Kumar Saha 	}
65cacbe919SAmul Kumar Saha };
66cacbe919SAmul Kumar Saha 
67cacbe919SAmul Kumar Saha /**
681ae39862SStefan Roese  * onenand_oob_64 - oob info for large (2KB) page
691ae39862SStefan Roese  */
701ae39862SStefan Roese static struct nand_ecclayout onenand_oob_64 = {
711ae39862SStefan Roese 	.eccbytes	= 20,
721ae39862SStefan Roese 	.eccpos		= {
731ae39862SStefan Roese 		8, 9, 10, 11, 12,
741ae39862SStefan Roese 		24, 25, 26, 27, 28,
751ae39862SStefan Roese 		40, 41, 42, 43, 44,
761ae39862SStefan Roese 		56, 57, 58, 59, 60,
771ae39862SStefan Roese 		},
781ae39862SStefan Roese 	.oobfree	= {
791ae39862SStefan Roese 		{2, 3}, {14, 2}, {18, 3}, {30, 2},
801ae39862SStefan Roese 		{34, 3}, {46, 2}, {50, 3}, {62, 2}
811ae39862SStefan Roese 	}
821ae39862SStefan Roese };
831ae39862SStefan Roese 
841ae39862SStefan Roese /**
851ae39862SStefan Roese  * onenand_oob_32 - oob info for middle (1KB) page
861ae39862SStefan Roese  */
871ae39862SStefan Roese static struct nand_ecclayout onenand_oob_32 = {
881ae39862SStefan Roese 	.eccbytes	= 10,
891ae39862SStefan Roese 	.eccpos		= {
901ae39862SStefan Roese 		8, 9, 10, 11, 12,
911ae39862SStefan Roese 		24, 25, 26, 27, 28,
921ae39862SStefan Roese 		},
931ae39862SStefan Roese 	.oobfree	= { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
941ae39862SStefan Roese };
951ae39862SStefan Roese 
969b56942fSMarek Vasut /*
979b56942fSMarek Vasut  * Warning! This array is used with the memcpy_16() function, thus
989b56942fSMarek Vasut  * it must be aligned to 2 bytes. GCC can make this array unaligned
999b56942fSMarek Vasut  * as the array is made of unsigned char, which memcpy16() doesn't
1009b56942fSMarek Vasut  * like and will cause unaligned access.
1019b56942fSMarek Vasut  */
1029b56942fSMarek Vasut static const unsigned char __aligned(2) ffchars[] = {
10359829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
10459829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 16 */
10559829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
10659829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 32 */
10759829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
10859829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 48 */
10959829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
11059829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 64 */
111cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
112cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 80 */
113cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
114cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 96 */
115cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
116cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 112 */
117cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
118cacbe919SAmul Kumar Saha 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 128 */
11959829cc1SJean-Christophe PLAGNIOL-VILLARD };
12059829cc1SJean-Christophe PLAGNIOL-VILLARD 
12159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
12259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_readw - [OneNAND Interface] Read OneNAND register
12359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to read
12459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
12559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read OneNAND register
12659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_readw(void __iomem * addr)12759829cc1SJean-Christophe PLAGNIOL-VILLARD static unsigned short onenand_readw(void __iomem * addr)
12859829cc1SJean-Christophe PLAGNIOL-VILLARD {
12959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return readw(addr);
13059829cc1SJean-Christophe PLAGNIOL-VILLARD }
13159829cc1SJean-Christophe PLAGNIOL-VILLARD 
13259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
13359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_writew - [OneNAND Interface] Write OneNAND register with value
13459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param value		value to write
13559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to write
13659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
13759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write OneNAND register with value
13859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_writew(unsigned short value,void __iomem * addr)13959829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_writew(unsigned short value, void __iomem * addr)
14059829cc1SJean-Christophe PLAGNIOL-VILLARD {
14159829cc1SJean-Christophe PLAGNIOL-VILLARD 	writew(value, addr);
14259829cc1SJean-Christophe PLAGNIOL-VILLARD }
14359829cc1SJean-Christophe PLAGNIOL-VILLARD 
14459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
14559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_address - [DEFAULT] Get block address
14659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
14759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
14859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		translated block address if DDP, otherwise same
14959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
15059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 1 Register (F100h)
15159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_block_address(struct onenand_chip * this,int block)152ef0921d6SKyungmin Park static int onenand_block_address(struct onenand_chip *this, int block)
15359829cc1SJean-Christophe PLAGNIOL-VILLARD {
15459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device Flash Core select, NAND Flash Block Address */
155ef0921d6SKyungmin Park 	if (block & this->density_mask)
156ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
15759829cc1SJean-Christophe PLAGNIOL-VILLARD 
15859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return block;
15959829cc1SJean-Christophe PLAGNIOL-VILLARD }
16059829cc1SJean-Christophe PLAGNIOL-VILLARD 
16159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
16259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_address - [DEFAULT] Get bufferram address
16359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
16459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
16559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		set DBS value if DDP, otherwise 0
16659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
16759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 2 Register (F101h) for DDP
16859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_bufferram_address(struct onenand_chip * this,int block)169ef0921d6SKyungmin Park static int onenand_bufferram_address(struct onenand_chip *this, int block)
17059829cc1SJean-Christophe PLAGNIOL-VILLARD {
17159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device BufferRAM Select */
172ef0921d6SKyungmin Park 	if (block & this->density_mask)
173ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1;
17459829cc1SJean-Christophe PLAGNIOL-VILLARD 
175ef0921d6SKyungmin Park 	return ONENAND_DDP_CHIP0;
17659829cc1SJean-Christophe PLAGNIOL-VILLARD }
17759829cc1SJean-Christophe PLAGNIOL-VILLARD 
17859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
17959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_page_address - [DEFAULT] Get page address
18059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param page		the page address
18159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sector	the sector address
18259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		combined page and sector address
18359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
18459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 8 Register (F107h)
18559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_page_address(int page,int sector)18659829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_page_address(int page, int sector)
18759829cc1SJean-Christophe PLAGNIOL-VILLARD {
18859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash Page Address, Flash Sector Address */
18959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int fpa, fsa;
19059829cc1SJean-Christophe PLAGNIOL-VILLARD 
19159829cc1SJean-Christophe PLAGNIOL-VILLARD 	fpa = page & ONENAND_FPA_MASK;
19259829cc1SJean-Christophe PLAGNIOL-VILLARD 	fsa = sector & ONENAND_FSA_MASK;
19359829cc1SJean-Christophe PLAGNIOL-VILLARD 
19459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((fpa << ONENAND_FPA_SHIFT) | fsa);
19559829cc1SJean-Christophe PLAGNIOL-VILLARD }
19659829cc1SJean-Christophe PLAGNIOL-VILLARD 
19759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
19859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_buffer_address - [DEFAULT] Get buffer address
19959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param dataram1	DataRAM index
20059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sectors	the sector address
20159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		the number of sectors
20259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		the start buffer value
20359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
20459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Buffer Register (F200h)
20559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_buffer_address(int dataram1,int sectors,int count)20659829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_buffer_address(int dataram1, int sectors, int count)
20759829cc1SJean-Christophe PLAGNIOL-VILLARD {
20859829cc1SJean-Christophe PLAGNIOL-VILLARD 	int bsa, bsc;
20959829cc1SJean-Christophe PLAGNIOL-VILLARD 
21059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Address */
21159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsa = sectors & ONENAND_BSA_MASK;
21259829cc1SJean-Christophe PLAGNIOL-VILLARD 
21359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (dataram1)
21459829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM1;	/* DataRAM1 */
21559829cc1SJean-Christophe PLAGNIOL-VILLARD 	else
21659829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM0;	/* DataRAM0 */
21759829cc1SJean-Christophe PLAGNIOL-VILLARD 
21859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Count */
21959829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsc = count & ONENAND_BSC_MASK;
22059829cc1SJean-Christophe PLAGNIOL-VILLARD 
22159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((bsa << ONENAND_BSA_SHIFT) | bsc);
22259829cc1SJean-Christophe PLAGNIOL-VILLARD }
22359829cc1SJean-Christophe PLAGNIOL-VILLARD 
22459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
225cacbe919SAmul Kumar Saha  * flexonenand_block - Return block number for flash address
226cacbe919SAmul Kumar Saha  * @param this		- OneNAND device structure
227cacbe919SAmul Kumar Saha  * @param addr		- Address for which block number is needed
228cacbe919SAmul Kumar Saha  */
flexonenand_block(struct onenand_chip * this,loff_t addr)229cacbe919SAmul Kumar Saha static unsigned int flexonenand_block(struct onenand_chip *this, loff_t addr)
230cacbe919SAmul Kumar Saha {
231cacbe919SAmul Kumar Saha 	unsigned int boundary, blk, die = 0;
232cacbe919SAmul Kumar Saha 
233cacbe919SAmul Kumar Saha 	if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
234cacbe919SAmul Kumar Saha 		die = 1;
235cacbe919SAmul Kumar Saha 		addr -= this->diesize[0];
236cacbe919SAmul Kumar Saha 	}
237cacbe919SAmul Kumar Saha 
238cacbe919SAmul Kumar Saha 	boundary = this->boundary[die];
239cacbe919SAmul Kumar Saha 
240cacbe919SAmul Kumar Saha 	blk = addr >> (this->erase_shift - 1);
241cacbe919SAmul Kumar Saha 	if (blk > boundary)
242cacbe919SAmul Kumar Saha 		blk = (blk + boundary + 1) >> 1;
243cacbe919SAmul Kumar Saha 
244cacbe919SAmul Kumar Saha 	blk += die ? this->density_mask : 0;
245cacbe919SAmul Kumar Saha 	return blk;
246cacbe919SAmul Kumar Saha }
247cacbe919SAmul Kumar Saha 
onenand_block(struct onenand_chip * this,loff_t addr)248cacbe919SAmul Kumar Saha unsigned int onenand_block(struct onenand_chip *this, loff_t addr)
249cacbe919SAmul Kumar Saha {
250cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
251cacbe919SAmul Kumar Saha 		return addr >> this->erase_shift;
252cacbe919SAmul Kumar Saha 	return flexonenand_block(this, addr);
253cacbe919SAmul Kumar Saha }
254cacbe919SAmul Kumar Saha 
255cacbe919SAmul Kumar Saha /**
256cacbe919SAmul Kumar Saha  * flexonenand_addr - Return address of the block
257cacbe919SAmul Kumar Saha  * @this:		OneNAND device structure
258cacbe919SAmul Kumar Saha  * @block:		Block number on Flex-OneNAND
259cacbe919SAmul Kumar Saha  *
260cacbe919SAmul Kumar Saha  * Return address of the block
261cacbe919SAmul Kumar Saha  */
flexonenand_addr(struct onenand_chip * this,int block)262cacbe919SAmul Kumar Saha static loff_t flexonenand_addr(struct onenand_chip *this, int block)
263cacbe919SAmul Kumar Saha {
264cacbe919SAmul Kumar Saha 	loff_t ofs = 0;
265cacbe919SAmul Kumar Saha 	int die = 0, boundary;
266cacbe919SAmul Kumar Saha 
267cacbe919SAmul Kumar Saha 	if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
268cacbe919SAmul Kumar Saha 		block -= this->density_mask;
269cacbe919SAmul Kumar Saha 		die = 1;
270cacbe919SAmul Kumar Saha 		ofs = this->diesize[0];
271cacbe919SAmul Kumar Saha 	}
272cacbe919SAmul Kumar Saha 
273cacbe919SAmul Kumar Saha 	boundary = this->boundary[die];
274cacbe919SAmul Kumar Saha 	ofs += (loff_t) block << (this->erase_shift - 1);
275cacbe919SAmul Kumar Saha 	if (block > (boundary + 1))
276cacbe919SAmul Kumar Saha 		ofs += (loff_t) (block - boundary - 1)
277cacbe919SAmul Kumar Saha 			<< (this->erase_shift - 1);
278cacbe919SAmul Kumar Saha 	return ofs;
279cacbe919SAmul Kumar Saha }
280cacbe919SAmul Kumar Saha 
onenand_addr(struct onenand_chip * this,int block)281cacbe919SAmul Kumar Saha loff_t onenand_addr(struct onenand_chip *this, int block)
282cacbe919SAmul Kumar Saha {
283cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
284cacbe919SAmul Kumar Saha 		return (loff_t) block << this->erase_shift;
285cacbe919SAmul Kumar Saha 	return flexonenand_addr(this, block);
286cacbe919SAmul Kumar Saha }
287cacbe919SAmul Kumar Saha 
288cacbe919SAmul Kumar Saha /**
289cacbe919SAmul Kumar Saha  * flexonenand_region - [Flex-OneNAND] Return erase region of addr
290cacbe919SAmul Kumar Saha  * @param mtd		MTD device structure
291cacbe919SAmul Kumar Saha  * @param addr		address whose erase region needs to be identified
292cacbe919SAmul Kumar Saha  */
flexonenand_region(struct mtd_info * mtd,loff_t addr)293cacbe919SAmul Kumar Saha int flexonenand_region(struct mtd_info *mtd, loff_t addr)
294cacbe919SAmul Kumar Saha {
295cacbe919SAmul Kumar Saha 	int i;
296cacbe919SAmul Kumar Saha 
297cacbe919SAmul Kumar Saha 	for (i = 0; i < mtd->numeraseregions; i++)
298cacbe919SAmul Kumar Saha 		if (addr < mtd->eraseregions[i].offset)
299cacbe919SAmul Kumar Saha 			break;
300cacbe919SAmul Kumar Saha 	return i - 1;
301cacbe919SAmul Kumar Saha }
302cacbe919SAmul Kumar Saha 
303cacbe919SAmul Kumar Saha /**
304ef0921d6SKyungmin Park  * onenand_get_density - [DEFAULT] Get OneNAND density
305ef0921d6SKyungmin Park  * @param dev_id        OneNAND device ID
306ef0921d6SKyungmin Park  *
307ef0921d6SKyungmin Park  * Get OneNAND density from device ID
308ef0921d6SKyungmin Park  */
onenand_get_density(int dev_id)309ef0921d6SKyungmin Park static inline int onenand_get_density(int dev_id)
310ef0921d6SKyungmin Park {
311ef0921d6SKyungmin Park 	int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
312ef0921d6SKyungmin Park 	return (density & ONENAND_DEVICE_DENSITY_MASK);
313ef0921d6SKyungmin Park }
314ef0921d6SKyungmin Park 
315ef0921d6SKyungmin Park /**
31659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_command - [DEFAULT] Send command to OneNAND device
31759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
31859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param cmd		the command to be sent
31959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		offset to read from or write to
32059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read or write
32159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
32259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Send command to OneNAND device. This function is used for middle/large page
32359829cc1SJean-Christophe PLAGNIOL-VILLARD  * devices (1KB/2KB Bytes per page)
32459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_command(struct mtd_info * mtd,int cmd,loff_t addr,size_t len)32559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
32659829cc1SJean-Christophe PLAGNIOL-VILLARD 			   size_t len)
32759829cc1SJean-Christophe PLAGNIOL-VILLARD {
32859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
329cacbe919SAmul Kumar Saha 	int value;
33059829cc1SJean-Christophe PLAGNIOL-VILLARD 	int block, page;
331cacbe919SAmul Kumar Saha 
33259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Now we use page size operation */
333cacbe919SAmul Kumar Saha 	int sectors = 0, count = 0;
33459829cc1SJean-Christophe PLAGNIOL-VILLARD 
33559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Address translation */
33659829cc1SJean-Christophe PLAGNIOL-VILLARD 	switch (cmd) {
33759829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_UNLOCK:
33859829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK:
33959829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK_TIGHT:
340ef0921d6SKyungmin Park 	case ONENAND_CMD_UNLOCK_ALL:
34159829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = -1;
34259829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
34359829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
34459829cc1SJean-Christophe PLAGNIOL-VILLARD 
345cacbe919SAmul Kumar Saha 	case FLEXONENAND_CMD_PI_ACCESS:
346cacbe919SAmul Kumar Saha 		/* addr contains die index */
347cacbe919SAmul Kumar Saha 		block = addr * this->density_mask;
34859829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
34959829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
35059829cc1SJean-Christophe PLAGNIOL-VILLARD 
351cacbe919SAmul Kumar Saha 	case ONENAND_CMD_ERASE:
352cacbe919SAmul Kumar Saha 	case ONENAND_CMD_BUFFERRAM:
353cacbe919SAmul Kumar Saha 		block = onenand_block(this, addr);
354cacbe919SAmul Kumar Saha 		page = -1;
355cacbe919SAmul Kumar Saha 		break;
356cacbe919SAmul Kumar Saha 
357cacbe919SAmul Kumar Saha 	case FLEXONENAND_CMD_READ_PI:
358cacbe919SAmul Kumar Saha 		cmd = ONENAND_CMD_READ;
359cacbe919SAmul Kumar Saha 		block = addr * this->density_mask;
360cacbe919SAmul Kumar Saha 		page = 0;
361cacbe919SAmul Kumar Saha 		break;
362cacbe919SAmul Kumar Saha 
36359829cc1SJean-Christophe PLAGNIOL-VILLARD 	default:
364cacbe919SAmul Kumar Saha 		block = onenand_block(this, addr);
365cacbe919SAmul Kumar Saha 		page = (int) (addr
366cacbe919SAmul Kumar Saha 			- onenand_addr(this, block)) >> this->page_shift;
36759829cc1SJean-Christophe PLAGNIOL-VILLARD 		page &= this->page_mask;
36859829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
36959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
37059829cc1SJean-Christophe PLAGNIOL-VILLARD 
37159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* NOTE: The setting order of the registers is very important! */
37259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (cmd == ONENAND_CMD_BUFFERRAM) {
37359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Select DataRAM for DDP */
374ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
37559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
37659829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS2);
37759829cc1SJean-Christophe PLAGNIOL-VILLARD 
378e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this))
379cacbe919SAmul Kumar Saha 			ONENAND_SET_BUFFERRAM0(this);
380cacbe919SAmul Kumar Saha 		else
38159829cc1SJean-Christophe PLAGNIOL-VILLARD 			/* Switch to the next data buffer */
38259829cc1SJean-Christophe PLAGNIOL-VILLARD 			ONENAND_SET_NEXT_BUFFERRAM(this);
38359829cc1SJean-Christophe PLAGNIOL-VILLARD 
38459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
38559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
38659829cc1SJean-Christophe PLAGNIOL-VILLARD 
38759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (block != -1) {
38859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'DFS, FBA' of Flash */
389ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
39059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
39159829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS1);
392ef0921d6SKyungmin Park 
393cacbe919SAmul Kumar Saha 		/* Select DataRAM for DDP */
394ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
395ef0921d6SKyungmin Park 		this->write_word(value,
396ef0921d6SKyungmin Park 				 this->base + ONENAND_REG_START_ADDRESS2);
39759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
39859829cc1SJean-Christophe PLAGNIOL-VILLARD 
39959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (page != -1) {
40059829cc1SJean-Christophe PLAGNIOL-VILLARD 		int dataram;
40159829cc1SJean-Christophe PLAGNIOL-VILLARD 
40259829cc1SJean-Christophe PLAGNIOL-VILLARD 		switch (cmd) {
403cacbe919SAmul Kumar Saha 		case FLEXONENAND_CMD_RECOVER_LSB:
40459829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READ:
40559829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READOOB:
406e26fd3d3SLukasz Majewski 			if (ONENAND_IS_4KB_PAGE(this))
407cacbe919SAmul Kumar Saha 				dataram = ONENAND_SET_BUFFERRAM0(this);
408cacbe919SAmul Kumar Saha 			else
40959829cc1SJean-Christophe PLAGNIOL-VILLARD 				dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
410cacbe919SAmul Kumar Saha 
41159829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
41259829cc1SJean-Christophe PLAGNIOL-VILLARD 
41359829cc1SJean-Christophe PLAGNIOL-VILLARD 		default:
41459829cc1SJean-Christophe PLAGNIOL-VILLARD 			dataram = ONENAND_CURRENT_BUFFERRAM(this);
41559829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
41659829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
41759829cc1SJean-Christophe PLAGNIOL-VILLARD 
41859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'FPA, FSA' of Flash */
41959829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_page_address(page, sectors);
42059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
42159829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS8);
42259829cc1SJean-Christophe PLAGNIOL-VILLARD 
42359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'BSA, BSC' of DataRAM */
42459829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_buffer_address(dataram, sectors, count);
42559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
42659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
42759829cc1SJean-Christophe PLAGNIOL-VILLARD 
42859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Interrupt clear */
42959829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
43059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Write command */
43159829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
43259829cc1SJean-Christophe PLAGNIOL-VILLARD 
43359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
43459829cc1SJean-Christophe PLAGNIOL-VILLARD }
43559829cc1SJean-Christophe PLAGNIOL-VILLARD 
43659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
437cacbe919SAmul Kumar Saha  * onenand_read_ecc - return ecc status
438cacbe919SAmul Kumar Saha  * @param this		onenand chip structure
439cacbe919SAmul Kumar Saha  */
onenand_read_ecc(struct onenand_chip * this)440cacbe919SAmul Kumar Saha static int onenand_read_ecc(struct onenand_chip *this)
441cacbe919SAmul Kumar Saha {
442cacbe919SAmul Kumar Saha 	int ecc, i;
443cacbe919SAmul Kumar Saha 
444cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
445cacbe919SAmul Kumar Saha 		return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
446cacbe919SAmul Kumar Saha 
447cacbe919SAmul Kumar Saha 	for (i = 0; i < 4; i++) {
448cacbe919SAmul Kumar Saha 		ecc = this->read_word(this->base
449cacbe919SAmul Kumar Saha 				+ ((ONENAND_REG_ECC_STATUS + i) << 1));
450cacbe919SAmul Kumar Saha 		if (likely(!ecc))
451cacbe919SAmul Kumar Saha 			continue;
452cacbe919SAmul Kumar Saha 		if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
453cacbe919SAmul Kumar Saha 			return ONENAND_ECC_2BIT_ALL;
454cacbe919SAmul Kumar Saha 	}
455cacbe919SAmul Kumar Saha 
456cacbe919SAmul Kumar Saha 	return 0;
457cacbe919SAmul Kumar Saha }
458cacbe919SAmul Kumar Saha 
459cacbe919SAmul Kumar Saha /**
46059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_wait - [DEFAULT] wait until the command is done
46159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
46259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param state		state to select the max. timeout value
46359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
46459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Wait for command done. This applies to all OneNAND command
46559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read can take up to 30us, erase up to 2ms and program up to 350us
46659829cc1SJean-Christophe PLAGNIOL-VILLARD  * according to general OneNAND specs
46759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_wait(struct mtd_info * mtd,int state)46859829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_wait(struct mtd_info *mtd, int state)
46959829cc1SJean-Christophe PLAGNIOL-VILLARD {
47059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
47159829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int interrupt = 0;
472cacbe919SAmul Kumar Saha 	unsigned int ctrl;
47359829cc1SJean-Christophe PLAGNIOL-VILLARD 
474d9098ee5SLadislav Michl 	/* Wait at most 20ms ... */
475d9098ee5SLadislav Michl 	u32 timeo = (CONFIG_SYS_HZ * 20) / 1000;
476d9098ee5SLadislav Michl 	u32 time_start = get_timer(0);
477d9098ee5SLadislav Michl 	do {
478d9098ee5SLadislav Michl 		WATCHDOG_RESET();
479d9098ee5SLadislav Michl 		if (get_timer(time_start) > timeo)
480d9098ee5SLadislav Michl 			return -EIO;
48159829cc1SJean-Christophe PLAGNIOL-VILLARD 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
482d9098ee5SLadislav Michl 	} while ((interrupt & ONENAND_INT_MASTER) == 0);
48359829cc1SJean-Christophe PLAGNIOL-VILLARD 
48459829cc1SJean-Christophe PLAGNIOL-VILLARD 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
48559829cc1SJean-Christophe PLAGNIOL-VILLARD 
486cacbe919SAmul Kumar Saha 	if (interrupt & ONENAND_INT_READ) {
487cacbe919SAmul Kumar Saha 		int ecc = onenand_read_ecc(this);
488cacbe919SAmul Kumar Saha 		if (ecc & ONENAND_ECC_2BIT_ALL) {
489cacbe919SAmul Kumar Saha 			printk("onenand_wait: ECC error = 0x%04x\n", ecc);
490cacbe919SAmul Kumar Saha 			return -EBADMSG;
491cacbe919SAmul Kumar Saha 		}
492cacbe919SAmul Kumar Saha 	}
493cacbe919SAmul Kumar Saha 
49459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ctrl & ONENAND_CTRL_ERROR) {
495ef0921d6SKyungmin Park 		printk("onenand_wait: controller error = 0x%04x\n", ctrl);
496ef0921d6SKyungmin Park 		if (ctrl & ONENAND_CTRL_LOCK)
497ef0921d6SKyungmin Park 			printk("onenand_wait: it's locked error = 0x%04x\n",
498ef0921d6SKyungmin Park 				ctrl);
49959829cc1SJean-Christophe PLAGNIOL-VILLARD 
50059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EIO;
50159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
50259829cc1SJean-Christophe PLAGNIOL-VILLARD 
50359829cc1SJean-Christophe PLAGNIOL-VILLARD 
50459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
50559829cc1SJean-Christophe PLAGNIOL-VILLARD }
50659829cc1SJean-Christophe PLAGNIOL-VILLARD 
50759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
50859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
50959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
51059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
51159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		offset given area
51259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
51359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Return BufferRAM offset given area
51459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_bufferram_offset(struct mtd_info * mtd,int area)51559829cc1SJean-Christophe PLAGNIOL-VILLARD static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
51659829cc1SJean-Christophe PLAGNIOL-VILLARD {
51759829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
51859829cc1SJean-Christophe PLAGNIOL-VILLARD 
51959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ONENAND_CURRENT_BUFFERRAM(this)) {
52059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_DATARAM)
521d438d508SKyungmin Park 			return mtd->writesize;
52259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_SPARERAM)
52359829cc1SJean-Christophe PLAGNIOL-VILLARD 			return mtd->oobsize;
52459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
52559829cc1SJean-Christophe PLAGNIOL-VILLARD 
52659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
52759829cc1SJean-Christophe PLAGNIOL-VILLARD }
52859829cc1SJean-Christophe PLAGNIOL-VILLARD 
52959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
53059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
53159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
53259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
53359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
53459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
53559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
53659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
53759829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area
53859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_read_bufferram(struct mtd_info * mtd,loff_t addr,int area,unsigned char * buffer,int offset,size_t count)539ef0921d6SKyungmin Park static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
54059829cc1SJean-Christophe PLAGNIOL-VILLARD 				  unsigned char *buffer, int offset,
54159829cc1SJean-Christophe PLAGNIOL-VILLARD 				  size_t count)
54259829cc1SJean-Christophe PLAGNIOL-VILLARD {
54359829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
54459829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
54559829cc1SJean-Christophe PLAGNIOL-VILLARD 
54659829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
54759829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
54859829cc1SJean-Christophe PLAGNIOL-VILLARD 
549d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
55059829cc1SJean-Christophe PLAGNIOL-VILLARD 
55159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
55259829cc1SJean-Christophe PLAGNIOL-VILLARD }
55359829cc1SJean-Christophe PLAGNIOL-VILLARD 
55459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
55559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
55659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
55759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
55859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
55959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
56059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
56159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
56259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area with Sync. Burst Mode
56359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_sync_read_bufferram(struct mtd_info * mtd,loff_t addr,int area,unsigned char * buffer,int offset,size_t count)564ef0921d6SKyungmin Park static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
56559829cc1SJean-Christophe PLAGNIOL-VILLARD 				       unsigned char *buffer, int offset,
56659829cc1SJean-Christophe PLAGNIOL-VILLARD 				       size_t count)
56759829cc1SJean-Christophe PLAGNIOL-VILLARD {
56859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
56959829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
57059829cc1SJean-Christophe PLAGNIOL-VILLARD 
57159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
57259829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
57359829cc1SJean-Christophe PLAGNIOL-VILLARD 
57459829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
57559829cc1SJean-Christophe PLAGNIOL-VILLARD 
576d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
57759829cc1SJean-Christophe PLAGNIOL-VILLARD 
57859829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, 0);
57959829cc1SJean-Christophe PLAGNIOL-VILLARD 
58059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
58159829cc1SJean-Christophe PLAGNIOL-VILLARD }
58259829cc1SJean-Christophe PLAGNIOL-VILLARD 
58359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
58459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
58559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
58659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
58759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
58859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
58959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
59059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
59159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write the BufferRAM area
59259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_write_bufferram(struct mtd_info * mtd,loff_t addr,int area,const unsigned char * buffer,int offset,size_t count)593ef0921d6SKyungmin Park static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area,
59459829cc1SJean-Christophe PLAGNIOL-VILLARD 				   const unsigned char *buffer, int offset,
59559829cc1SJean-Christophe PLAGNIOL-VILLARD 				   size_t count)
59659829cc1SJean-Christophe PLAGNIOL-VILLARD {
59759829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
59859829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
59959829cc1SJean-Christophe PLAGNIOL-VILLARD 
60059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
60159829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
60259829cc1SJean-Christophe PLAGNIOL-VILLARD 
603d2c6fbecSWolfgang Denk 	memcpy_16(bufferram + offset, buffer, count);
60459829cc1SJean-Christophe PLAGNIOL-VILLARD 
60559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
60659829cc1SJean-Christophe PLAGNIOL-VILLARD }
60759829cc1SJean-Christophe PLAGNIOL-VILLARD 
60859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
6094fca3310SStefan Roese  * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
6104fca3310SStefan Roese  * @param mtd		MTD data structure
6114fca3310SStefan Roese  * @param addr		address to check
6124fca3310SStefan Roese  * @return		blockpage address
6134fca3310SStefan Roese  *
6144fca3310SStefan Roese  * Get blockpage address at 2x program mode
6154fca3310SStefan Roese  */
onenand_get_2x_blockpage(struct mtd_info * mtd,loff_t addr)6164fca3310SStefan Roese static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
6174fca3310SStefan Roese {
6184fca3310SStefan Roese 	struct onenand_chip *this = mtd->priv;
6194fca3310SStefan Roese 	int blockpage, block, page;
6204fca3310SStefan Roese 
6214fca3310SStefan Roese 	/* Calculate the even block number */
6224fca3310SStefan Roese 	block = (int) (addr >> this->erase_shift) & ~1;
6234fca3310SStefan Roese 	/* Is it the odd plane? */
6244fca3310SStefan Roese 	if (addr & this->writesize)
6254fca3310SStefan Roese 		block++;
6264fca3310SStefan Roese 	page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
6274fca3310SStefan Roese 	blockpage = (block << 7) | page;
6284fca3310SStefan Roese 
6294fca3310SStefan Roese 	return blockpage;
6304fca3310SStefan Roese }
6314fca3310SStefan Roese 
6324fca3310SStefan Roese /**
63359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_bufferram - [GENERIC] Check BufferRAM information
63459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
63559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to check
63659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		1 if there are valid data, otherwise 0
63759829cc1SJean-Christophe PLAGNIOL-VILLARD  *
63859829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check bufferram if there is data we required
63959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_check_bufferram(struct mtd_info * mtd,loff_t addr)64059829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
64159829cc1SJean-Christophe PLAGNIOL-VILLARD {
64259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
643ef0921d6SKyungmin Park 	int blockpage, found = 0;
644ef0921d6SKyungmin Park 	unsigned int i;
64559829cc1SJean-Christophe PLAGNIOL-VILLARD 
646ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
647ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
648ef0921d6SKyungmin Park 	else
649ef0921d6SKyungmin Park 		blockpage = (int) (addr >> this->page_shift);
65059829cc1SJean-Christophe PLAGNIOL-VILLARD 
65159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Is there valid data? */
652ef0921d6SKyungmin Park 	i = ONENAND_CURRENT_BUFFERRAM(this);
653ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
654ef0921d6SKyungmin Park 		found = 1;
655ef0921d6SKyungmin Park 	else {
656ef0921d6SKyungmin Park 		/* Check another BufferRAM */
657ef0921d6SKyungmin Park 		i = ONENAND_NEXT_BUFFERRAM(this);
658ef0921d6SKyungmin Park 		if (this->bufferram[i].blockpage == blockpage) {
659ef0921d6SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
660ef0921d6SKyungmin Park 			found = 1;
661ef0921d6SKyungmin Park 		}
662ef0921d6SKyungmin Park 	}
66359829cc1SJean-Christophe PLAGNIOL-VILLARD 
664ef0921d6SKyungmin Park 	if (found && ONENAND_IS_DDP(this)) {
665ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
666cacbe919SAmul Kumar Saha 		int block = onenand_block(this, addr);
667ef0921d6SKyungmin Park 		int value = onenand_bufferram_address(this, block);
668ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
669ef0921d6SKyungmin Park 	}
670ef0921d6SKyungmin Park 
671ef0921d6SKyungmin Park 	return found;
67259829cc1SJean-Christophe PLAGNIOL-VILLARD }
67359829cc1SJean-Christophe PLAGNIOL-VILLARD 
67459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
67559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_update_bufferram - [GENERIC] Update BufferRAM information
67659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
67759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to update
67859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param valid		valid flag
67959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
68059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Update BufferRAM information
68159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_update_bufferram(struct mtd_info * mtd,loff_t addr,int valid)68259829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
68359829cc1SJean-Christophe PLAGNIOL-VILLARD 				    int valid)
68459829cc1SJean-Christophe PLAGNIOL-VILLARD {
68559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
686ef0921d6SKyungmin Park 	int blockpage;
687ef0921d6SKyungmin Park 	unsigned int i;
68859829cc1SJean-Christophe PLAGNIOL-VILLARD 
689ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
690ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
691ef0921d6SKyungmin Park 	else
692ef0921d6SKyungmin Park 		blockpage = (int)(addr >> this->page_shift);
69359829cc1SJean-Christophe PLAGNIOL-VILLARD 
694ef0921d6SKyungmin Park 	/* Invalidate another BufferRAM */
695ef0921d6SKyungmin Park 	i = ONENAND_NEXT_BUFFERRAM(this);
696ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
697ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
69859829cc1SJean-Christophe PLAGNIOL-VILLARD 
69959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Update BufferRAM */
70059829cc1SJean-Christophe PLAGNIOL-VILLARD 	i = ONENAND_CURRENT_BUFFERRAM(this);
701ef0921d6SKyungmin Park 	if (valid)
702ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = blockpage;
703ef0921d6SKyungmin Park 	else
704ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
70559829cc1SJean-Christophe PLAGNIOL-VILLARD 
70659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
70759829cc1SJean-Christophe PLAGNIOL-VILLARD }
70859829cc1SJean-Christophe PLAGNIOL-VILLARD 
70959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
710d438d508SKyungmin Park  * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
711d438d508SKyungmin Park  * @param mtd           MTD data structure
712d438d508SKyungmin Park  * @param addr          start address to invalidate
713d438d508SKyungmin Park  * @param len           length to invalidate
714d438d508SKyungmin Park  *
715d438d508SKyungmin Park  * Invalidate BufferRAM information
716d438d508SKyungmin Park  */
onenand_invalidate_bufferram(struct mtd_info * mtd,loff_t addr,unsigned int len)717d438d508SKyungmin Park static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
718d438d508SKyungmin Park 					 unsigned int len)
719d438d508SKyungmin Park {
720d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
721d438d508SKyungmin Park 	int i;
722d438d508SKyungmin Park 	loff_t end_addr = addr + len;
723d438d508SKyungmin Park 
724d438d508SKyungmin Park 	/* Invalidate BufferRAM */
725d438d508SKyungmin Park 	for (i = 0; i < MAX_BUFFERRAM; i++) {
726ef0921d6SKyungmin Park 		loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
727d438d508SKyungmin Park 
728d438d508SKyungmin Park 		if (buf_addr >= addr && buf_addr < end_addr)
729ef0921d6SKyungmin Park 			this->bufferram[i].blockpage = -1;
730d438d508SKyungmin Park 	}
731d438d508SKyungmin Park }
732d438d508SKyungmin Park 
733d438d508SKyungmin Park /**
73459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_get_device - [GENERIC] Get chip for selected access
73559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
73659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param new_state	the state which is requested
73759829cc1SJean-Christophe PLAGNIOL-VILLARD  *
73859829cc1SJean-Christophe PLAGNIOL-VILLARD  * Get the device and lock it for exclusive access
73959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_get_device(struct mtd_info * mtd,int new_state)74059829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_get_device(struct mtd_info *mtd, int new_state)
74159829cc1SJean-Christophe PLAGNIOL-VILLARD {
74259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
74359829cc1SJean-Christophe PLAGNIOL-VILLARD }
74459829cc1SJean-Christophe PLAGNIOL-VILLARD 
74559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
74659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release_device - [GENERIC] release chip
74759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
74859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
74959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Deselect, release chip lock and wake up anyone waiting on the device
75059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_release_device(struct mtd_info * mtd)75159829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_release_device(struct mtd_info *mtd)
75259829cc1SJean-Christophe PLAGNIOL-VILLARD {
75359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
75459829cc1SJean-Christophe PLAGNIOL-VILLARD }
75559829cc1SJean-Christophe PLAGNIOL-VILLARD 
75659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
757dfe64e2cSSergey Lapin  * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
75859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
759bfd7f386SKyungmin Park  * @param buf		destination address
760bfd7f386SKyungmin Park  * @param column	oob offset to read from
761bfd7f386SKyungmin Park  * @param thislen	oob length to read
76259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_transfer_auto_oob(struct mtd_info * mtd,uint8_t * buf,int column,int thislen)763bfd7f386SKyungmin Park static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
764bfd7f386SKyungmin Park 					int column, int thislen)
76559829cc1SJean-Christophe PLAGNIOL-VILLARD {
76659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
767bfd7f386SKyungmin Park 	struct nand_oobfree *free;
768bfd7f386SKyungmin Park 	int readcol = column;
769bfd7f386SKyungmin Park 	int readend = column + thislen;
770bfd7f386SKyungmin Park 	int lastgap = 0;
771bfd7f386SKyungmin Park 	unsigned int i;
772bfd7f386SKyungmin Park 	uint8_t *oob_buf = this->oob_buf;
77359829cc1SJean-Christophe PLAGNIOL-VILLARD 
774bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
77568ec9c85SPrabhakar Kushwaha 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
77668ec9c85SPrabhakar Kushwaha 	     i++, free++) {
777bfd7f386SKyungmin Park 		if (readcol >= lastgap)
778bfd7f386SKyungmin Park 			readcol += free->offset - lastgap;
779bfd7f386SKyungmin Park 		if (readend >= lastgap)
780bfd7f386SKyungmin Park 			readend += free->offset - lastgap;
781bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
782bfd7f386SKyungmin Park 	}
783ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
784bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
78568ec9c85SPrabhakar Kushwaha 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
78668ec9c85SPrabhakar Kushwaha 	     i++, free++) {
787bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
788bfd7f386SKyungmin Park 		if (free->offset < readend && free_end > readcol) {
789bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,readcol);
790bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,readend);
791bfd7f386SKyungmin Park 			int n = ed - st;
792bfd7f386SKyungmin Park 			memcpy(buf, oob_buf + st, n);
793bfd7f386SKyungmin Park 			buf += n;
794bfd7f386SKyungmin Park 		} else if (column == 0)
795bfd7f386SKyungmin Park 			break;
796bfd7f386SKyungmin Park 	}
797bfd7f386SKyungmin Park 	return 0;
798bfd7f386SKyungmin Park }
799bfd7f386SKyungmin Park 
800bfd7f386SKyungmin Park /**
801cacbe919SAmul Kumar Saha  * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
802cacbe919SAmul Kumar Saha  * @param mtd		MTD device structure
803cacbe919SAmul Kumar Saha  * @param addr		address to recover
804cacbe919SAmul Kumar Saha  * @param status	return value from onenand_wait
805cacbe919SAmul Kumar Saha  *
806cacbe919SAmul Kumar Saha  * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
807cacbe919SAmul Kumar Saha  * lower page address and MSB page has higher page address in paired pages.
808cacbe919SAmul Kumar Saha  * If power off occurs during MSB page program, the paired LSB page data can
809cacbe919SAmul Kumar Saha  * become corrupt. LSB page recovery read is a way to read LSB page though page
810cacbe919SAmul Kumar Saha  * data are corrupted. When uncorrectable error occurs as a result of LSB page
811cacbe919SAmul Kumar Saha  * read after power up, issue LSB page recovery read.
812cacbe919SAmul Kumar Saha  */
onenand_recover_lsb(struct mtd_info * mtd,loff_t addr,int status)813cacbe919SAmul Kumar Saha static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
814cacbe919SAmul Kumar Saha {
815cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
816cacbe919SAmul Kumar Saha 	int i;
817cacbe919SAmul Kumar Saha 
818cacbe919SAmul Kumar Saha 	/* Recovery is only for Flex-OneNAND */
819cacbe919SAmul Kumar Saha 	if (!FLEXONENAND(this))
820cacbe919SAmul Kumar Saha 		return status;
821cacbe919SAmul Kumar Saha 
822cacbe919SAmul Kumar Saha 	/* check if we failed due to uncorrectable error */
823dfe64e2cSSergey Lapin 	if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
824cacbe919SAmul Kumar Saha 		return status;
825cacbe919SAmul Kumar Saha 
826cacbe919SAmul Kumar Saha 	/* check if address lies in MLC region */
827cacbe919SAmul Kumar Saha 	i = flexonenand_region(mtd, addr);
828cacbe919SAmul Kumar Saha 	if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
829cacbe919SAmul Kumar Saha 		return status;
830cacbe919SAmul Kumar Saha 
831cacbe919SAmul Kumar Saha 	printk("onenand_recover_lsb:"
832cacbe919SAmul Kumar Saha 		"Attempting to recover from uncorrectable read\n");
833cacbe919SAmul Kumar Saha 
834cacbe919SAmul Kumar Saha 	/* Issue the LSB page recovery command */
835cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
836cacbe919SAmul Kumar Saha 	return this->wait(mtd, FL_READING);
837cacbe919SAmul Kumar Saha }
838cacbe919SAmul Kumar Saha 
839cacbe919SAmul Kumar Saha /**
840bfd7f386SKyungmin Park  * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
841bfd7f386SKyungmin Park  * @param mtd		MTD device structure
842bfd7f386SKyungmin Park  * @param from		offset to read from
843bfd7f386SKyungmin Park  * @param ops		oob operation description structure
844bfd7f386SKyungmin Park  *
845bfd7f386SKyungmin Park  * OneNAND read main and/or out-of-band data
846bfd7f386SKyungmin Park  */
onenand_read_ops_nolock(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)847bfd7f386SKyungmin Park static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
848bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
849bfd7f386SKyungmin Park {
850bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
851bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
852bfd7f386SKyungmin Park 	size_t len = ops->len;
853bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
854bfd7f386SKyungmin Park 	u_char *buf = ops->datbuf;
855bfd7f386SKyungmin Park 	u_char *oobbuf = ops->oobbuf;
856bfd7f386SKyungmin Park 	int read = 0, column, thislen;
857bfd7f386SKyungmin Park 	int oobread = 0, oobcolumn, thisooblen, oobsize;
858bfd7f386SKyungmin Park 	int ret = 0, boundary = 0;
859bfd7f386SKyungmin Park 	int writesize = this->writesize;
860bfd7f386SKyungmin Park 
861*166cae20SMasahiro Yamada 	pr_debug("onenand_read_ops_nolock: from = 0x%08x, len = %i\n",
862*166cae20SMasahiro Yamada 		 (unsigned int) from, (int) len);
86359829cc1SJean-Christophe PLAGNIOL-VILLARD 
864dfe64e2cSSergey Lapin 	if (ops->mode == MTD_OPS_AUTO_OOB)
865bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
866bfd7f386SKyungmin Park 	else
867bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
868bfd7f386SKyungmin Park 
869bfd7f386SKyungmin Park 	oobcolumn = from & (mtd->oobsize - 1);
870bfd7f386SKyungmin Park 
87159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
87259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((from + len) > mtd->size) {
873bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n");
874bfd7f386SKyungmin Park 		ops->retlen = 0;
875bfd7f386SKyungmin Park 		ops->oobretlen = 0;
87659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
87759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
87859829cc1SJean-Christophe PLAGNIOL-VILLARD 
879bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
88059829cc1SJean-Christophe PLAGNIOL-VILLARD 
881bfd7f386SKyungmin Park 	/* Read-while-load method */
882cacbe919SAmul Kumar Saha 	/* Note: We can't use this feature in MLC */
88359829cc1SJean-Christophe PLAGNIOL-VILLARD 
884bfd7f386SKyungmin Park 	/* Do first load to bufferRAM */
885bfd7f386SKyungmin Park 	if (read < len) {
88659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!onenand_check_bufferram(mtd, from)) {
887ef0921d6SKyungmin Park 			this->main_buf = buf;
888bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
88959829cc1SJean-Christophe PLAGNIOL-VILLARD 			ret = this->wait(mtd, FL_READING);
890cacbe919SAmul Kumar Saha 			if (unlikely(ret))
891cacbe919SAmul Kumar Saha 				ret = onenand_recover_lsb(mtd, from, ret);
892bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
893bfd7f386SKyungmin Park 			if (ret == -EBADMSG)
894bfd7f386SKyungmin Park 				ret = 0;
895bfd7f386SKyungmin Park 		}
89659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
89759829cc1SJean-Christophe PLAGNIOL-VILLARD 
898bfd7f386SKyungmin Park 	thislen = min_t(int, writesize, len - read);
899bfd7f386SKyungmin Park 	column = from & (writesize - 1);
900bfd7f386SKyungmin Park 	if (column + thislen > writesize)
901bfd7f386SKyungmin Park 		thislen = writesize - column;
90259829cc1SJean-Christophe PLAGNIOL-VILLARD 
903bfd7f386SKyungmin Park 	while (!ret) {
904bfd7f386SKyungmin Park 		/* If there is more to load then start next load */
905bfd7f386SKyungmin Park 		from += thislen;
906e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this) && read + thislen < len) {
907ef0921d6SKyungmin Park 			this->main_buf = buf + thislen;
908bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
909bfd7f386SKyungmin Park 			/*
910bfd7f386SKyungmin Park 			 * Chip boundary handling in DDP
911bfd7f386SKyungmin Park 			 * Now we issued chip 1 read and pointed chip 1
912bfd7f386SKyungmin Park 			 * bufferam so we have to point chip 0 bufferam.
913bfd7f386SKyungmin Park 			 */
914bfd7f386SKyungmin Park 			if (ONENAND_IS_DDP(this) &&
915bfd7f386SKyungmin Park 					unlikely(from == (this->chipsize >> 1))) {
916bfd7f386SKyungmin Park 				this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
917bfd7f386SKyungmin Park 				boundary = 1;
918bfd7f386SKyungmin Park 			} else
919bfd7f386SKyungmin Park 				boundary = 0;
920bfd7f386SKyungmin Park 			ONENAND_SET_PREV_BUFFERRAM(this);
921bfd7f386SKyungmin Park 		}
922bfd7f386SKyungmin Park 
923bfd7f386SKyungmin Park 		/* While load is going, read from last bufferRAM */
924ef0921d6SKyungmin Park 		this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen);
925bfd7f386SKyungmin Park 
926bfd7f386SKyungmin Park 		/* Read oob area if needed */
927bfd7f386SKyungmin Park 		if (oobbuf) {
928bfd7f386SKyungmin Park 			thisooblen = oobsize - oobcolumn;
929bfd7f386SKyungmin Park 			thisooblen = min_t(int, thisooblen, ooblen - oobread);
930bfd7f386SKyungmin Park 
931dfe64e2cSSergey Lapin 			if (ops->mode == MTD_OPS_AUTO_OOB)
932bfd7f386SKyungmin Park 				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
933bfd7f386SKyungmin Park 			else
934ef0921d6SKyungmin Park 				this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
935bfd7f386SKyungmin Park 			oobread += thisooblen;
936bfd7f386SKyungmin Park 			oobbuf += thisooblen;
937bfd7f386SKyungmin Park 			oobcolumn = 0;
938bfd7f386SKyungmin Park 		}
939bfd7f386SKyungmin Park 
940e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this) && (read + thislen < len)) {
941cacbe919SAmul Kumar Saha 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
942cacbe919SAmul Kumar Saha 			ret = this->wait(mtd, FL_READING);
943cacbe919SAmul Kumar Saha 			if (unlikely(ret))
944cacbe919SAmul Kumar Saha 				ret = onenand_recover_lsb(mtd, from, ret);
945cacbe919SAmul Kumar Saha 			onenand_update_bufferram(mtd, from, !ret);
946dfe64e2cSSergey Lapin 			if (mtd_is_eccerr(ret))
947cacbe919SAmul Kumar Saha 				ret = 0;
948cacbe919SAmul Kumar Saha 		}
949cacbe919SAmul Kumar Saha 
950bfd7f386SKyungmin Park 		/* See if we are done */
95159829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
95259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
95359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
954bfd7f386SKyungmin Park 		/* Set up for next read from bufferRAM */
955bfd7f386SKyungmin Park 		if (unlikely(boundary))
956bfd7f386SKyungmin Park 			this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
957e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this))
958bfd7f386SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
95959829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
960bfd7f386SKyungmin Park 		thislen = min_t(int, writesize, len - read);
961bfd7f386SKyungmin Park 		column = 0;
96259829cc1SJean-Christophe PLAGNIOL-VILLARD 
963e26fd3d3SLukasz Majewski 		if (!ONENAND_IS_4KB_PAGE(this)) {
964bfd7f386SKyungmin Park 			/* Now wait for load */
965bfd7f386SKyungmin Park 			ret = this->wait(mtd, FL_READING);
966bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
967dfe64e2cSSergey Lapin 			if (mtd_is_eccerr(ret))
968bfd7f386SKyungmin Park 				ret = 0;
969bfd7f386SKyungmin Park 		}
970cacbe919SAmul Kumar Saha 	}
97159829cc1SJean-Christophe PLAGNIOL-VILLARD 
97259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/*
97359829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * Return success, if no ECC failures, else -EBADMSG
97459829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * fs driver will take care of that, because
97559829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * retlen == desired len and result == -EBADMSG
97659829cc1SJean-Christophe PLAGNIOL-VILLARD 	 */
977bfd7f386SKyungmin Park 	ops->retlen = read;
978bfd7f386SKyungmin Park 	ops->oobretlen = oobread;
979bfd7f386SKyungmin Park 
980bfd7f386SKyungmin Park 	if (ret)
98159829cc1SJean-Christophe PLAGNIOL-VILLARD 		return ret;
982bfd7f386SKyungmin Park 
983bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
984bfd7f386SKyungmin Park 		return -EBADMSG;
985bfd7f386SKyungmin Park 
98640462e54SPaul Burton 	/* return max bitflips per ecc step; ONENANDs correct 1 bit only */
98740462e54SPaul Burton 	return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
988bfd7f386SKyungmin Park }
989bfd7f386SKyungmin Park 
990bfd7f386SKyungmin Park /**
991bfd7f386SKyungmin Park  * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
992bfd7f386SKyungmin Park  * @param mtd		MTD device structure
993bfd7f386SKyungmin Park  * @param from		offset to read from
994bfd7f386SKyungmin Park  * @param ops		oob operation description structure
995bfd7f386SKyungmin Park  *
996bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area
997bfd7f386SKyungmin Park  */
onenand_read_oob_nolock(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)998bfd7f386SKyungmin Park static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
999bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1000bfd7f386SKyungmin Park {
1001bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1002bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
1003bfd7f386SKyungmin Park 	int read = 0, thislen, column, oobsize;
1004bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1005dfe64e2cSSergey Lapin 	unsigned int mode = ops->mode;
1006bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
1007cacbe919SAmul Kumar Saha 	int ret = 0, readcmd;
1008bfd7f386SKyungmin Park 
1009bfd7f386SKyungmin Park 	from += ops->ooboffs;
1010bfd7f386SKyungmin Park 
1011*166cae20SMasahiro Yamada 	pr_debug("onenand_read_oob_nolock: from = 0x%08x, len = %i\n",
1012*166cae20SMasahiro Yamada 		 (unsigned int) from, (int) len);
1013bfd7f386SKyungmin Park 
1014bfd7f386SKyungmin Park 	/* Initialize return length value */
1015bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1016bfd7f386SKyungmin Park 
1017dfe64e2cSSergey Lapin 	if (mode == MTD_OPS_AUTO_OOB)
1018bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1019bfd7f386SKyungmin Park 	else
1020bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1021bfd7f386SKyungmin Park 
1022bfd7f386SKyungmin Park 	column = from & (mtd->oobsize - 1);
1023bfd7f386SKyungmin Park 
1024bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1025bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
1026bfd7f386SKyungmin Park 		return -EINVAL;
1027bfd7f386SKyungmin Park 	}
1028bfd7f386SKyungmin Park 
1029bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1030bfd7f386SKyungmin Park 	if (unlikely(from >= mtd->size ||
1031bfd7f386SKyungmin Park 		column + len > ((mtd->size >> this->page_shift) -
1032bfd7f386SKyungmin Park 				(from >> this->page_shift)) * oobsize)) {
1033bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n");
1034bfd7f386SKyungmin Park 		return -EINVAL;
1035bfd7f386SKyungmin Park 	}
1036bfd7f386SKyungmin Park 
1037bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
1038bfd7f386SKyungmin Park 
1039e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1040e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1041cacbe919SAmul Kumar Saha 
1042bfd7f386SKyungmin Park 	while (read < len) {
1043bfd7f386SKyungmin Park 		thislen = oobsize - column;
1044bfd7f386SKyungmin Park 		thislen = min_t(int, thislen, len);
1045bfd7f386SKyungmin Park 
1046ef0921d6SKyungmin Park 		this->spare_buf = buf;
1047cacbe919SAmul Kumar Saha 		this->command(mtd, readcmd, from, mtd->oobsize);
1048bfd7f386SKyungmin Park 
1049bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, from, 0);
1050bfd7f386SKyungmin Park 
1051bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_READING);
1052cacbe919SAmul Kumar Saha 		if (unlikely(ret))
1053cacbe919SAmul Kumar Saha 			ret = onenand_recover_lsb(mtd, from, ret);
1054cacbe919SAmul Kumar Saha 
1055bfd7f386SKyungmin Park 		if (ret && ret != -EBADMSG) {
1056bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
1057bfd7f386SKyungmin Park 			break;
1058bfd7f386SKyungmin Park 		}
1059bfd7f386SKyungmin Park 
1060dfe64e2cSSergey Lapin 		if (mode == MTD_OPS_AUTO_OOB)
1061bfd7f386SKyungmin Park 			onenand_transfer_auto_oob(mtd, buf, column, thislen);
1062bfd7f386SKyungmin Park 		else
1063ef0921d6SKyungmin Park 			this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
1064bfd7f386SKyungmin Park 
1065bfd7f386SKyungmin Park 		read += thislen;
1066bfd7f386SKyungmin Park 
1067bfd7f386SKyungmin Park 		if (read == len)
1068bfd7f386SKyungmin Park 			break;
1069bfd7f386SKyungmin Park 
1070bfd7f386SKyungmin Park 		buf += thislen;
1071bfd7f386SKyungmin Park 
1072bfd7f386SKyungmin Park 		/* Read more? */
1073bfd7f386SKyungmin Park 		if (read < len) {
1074bfd7f386SKyungmin Park 			/* Page size */
1075bfd7f386SKyungmin Park 			from += mtd->writesize;
1076bfd7f386SKyungmin Park 			column = 0;
1077bfd7f386SKyungmin Park 		}
1078bfd7f386SKyungmin Park 	}
1079bfd7f386SKyungmin Park 
1080bfd7f386SKyungmin Park 	ops->oobretlen = read;
1081bfd7f386SKyungmin Park 
1082bfd7f386SKyungmin Park 	if (ret)
1083bfd7f386SKyungmin Park 		return ret;
1084bfd7f386SKyungmin Park 
1085bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
1086bfd7f386SKyungmin Park 		return -EBADMSG;
1087bfd7f386SKyungmin Park 
1088bfd7f386SKyungmin Park 	return 0;
108959829cc1SJean-Christophe PLAGNIOL-VILLARD }
109059829cc1SJean-Christophe PLAGNIOL-VILLARD 
109159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
109259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc
109359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
109459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
109559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read
109659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of read bytes
109759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the databuffer to put data
109859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
109959829cc1SJean-Christophe PLAGNIOL-VILLARD  * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
110059829cc1SJean-Christophe PLAGNIOL-VILLARD */
onenand_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)110159829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
110259829cc1SJean-Christophe PLAGNIOL-VILLARD 		 size_t * retlen, u_char * buf)
110359829cc1SJean-Christophe PLAGNIOL-VILLARD {
1104bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1105bfd7f386SKyungmin Park 		.len    = len,
1106bfd7f386SKyungmin Park 		.ooblen = 0,
1107bfd7f386SKyungmin Park 		.datbuf = buf,
1108bfd7f386SKyungmin Park 		.oobbuf = NULL,
1109bfd7f386SKyungmin Park 	};
1110bfd7f386SKyungmin Park 	int ret;
1111bfd7f386SKyungmin Park 
1112bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1113bfd7f386SKyungmin Park 	ret = onenand_read_ops_nolock(mtd, from, &ops);
1114bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1115bfd7f386SKyungmin Park 
1116bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1117bfd7f386SKyungmin Park 	return ret;
111859829cc1SJean-Christophe PLAGNIOL-VILLARD }
111959829cc1SJean-Christophe PLAGNIOL-VILLARD 
112059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
112159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
112259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
112359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
1124bfd7f386SKyungmin Park  * @param ops		oob operations description structure
112559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1126bfd7f386SKyungmin Park  * OneNAND main and/or out-of-band
112759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_read_oob(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)1128bfd7f386SKyungmin Park int onenand_read_oob(struct mtd_info *mtd, loff_t from,
1129bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
1130bfd7f386SKyungmin Park {
1131bfd7f386SKyungmin Park 	int ret;
1132bfd7f386SKyungmin Park 
1133bfd7f386SKyungmin Park 	switch (ops->mode) {
1134dfe64e2cSSergey Lapin 	case MTD_OPS_PLACE_OOB:
1135dfe64e2cSSergey Lapin 	case MTD_OPS_AUTO_OOB:
1136bfd7f386SKyungmin Park 		break;
1137dfe64e2cSSergey Lapin 	case MTD_OPS_RAW:
1138bfd7f386SKyungmin Park 		/* Not implemented yet */
1139bfd7f386SKyungmin Park 	default:
1140bfd7f386SKyungmin Park 		return -EINVAL;
1141bfd7f386SKyungmin Park 	}
1142bfd7f386SKyungmin Park 
1143bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1144bfd7f386SKyungmin Park 	if (ops->datbuf)
1145bfd7f386SKyungmin Park 		ret = onenand_read_ops_nolock(mtd, from, ops);
1146bfd7f386SKyungmin Park 	else
1147bfd7f386SKyungmin Park 		ret = onenand_read_oob_nolock(mtd, from, ops);
1148bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1149bfd7f386SKyungmin Park 
1150bfd7f386SKyungmin Park 	return ret;
1151bfd7f386SKyungmin Park }
1152bfd7f386SKyungmin Park 
1153bfd7f386SKyungmin Park /**
1154bfd7f386SKyungmin Park  * onenand_bbt_wait - [DEFAULT] wait until the command is done
1155bfd7f386SKyungmin Park  * @param mtd		MTD device structure
1156bfd7f386SKyungmin Park  * @param state		state to select the max. timeout value
1157bfd7f386SKyungmin Park  *
1158bfd7f386SKyungmin Park  * Wait for command done.
1159bfd7f386SKyungmin Park  */
onenand_bbt_wait(struct mtd_info * mtd,int state)1160bfd7f386SKyungmin Park static int onenand_bbt_wait(struct mtd_info *mtd, int state)
1161bfd7f386SKyungmin Park {
1162bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1163bfd7f386SKyungmin Park 	unsigned int interrupt;
1164bfd7f386SKyungmin Park 	unsigned int ctrl;
1165bfd7f386SKyungmin Park 
1166d9098ee5SLadislav Michl 	/* Wait at most 20ms ... */
1167d9098ee5SLadislav Michl 	u32 timeo = (CONFIG_SYS_HZ * 20) / 1000;
1168d9098ee5SLadislav Michl 	u32 time_start = get_timer(0);
1169d9098ee5SLadislav Michl 	do {
1170d9098ee5SLadislav Michl 		WATCHDOG_RESET();
1171d9098ee5SLadislav Michl 		if (get_timer(time_start) > timeo)
1172d9098ee5SLadislav Michl 			return ONENAND_BBT_READ_FATAL_ERROR;
1173bfd7f386SKyungmin Park 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
1174d9098ee5SLadislav Michl 	} while ((interrupt & ONENAND_INT_MASTER) == 0);
1175bfd7f386SKyungmin Park 
1176bfd7f386SKyungmin Park 	/* To get correct interrupt status in timeout case */
1177bfd7f386SKyungmin Park 	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
1178bfd7f386SKyungmin Park 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
1179bfd7f386SKyungmin Park 
1180bfd7f386SKyungmin Park 	if (interrupt & ONENAND_INT_READ) {
1181cacbe919SAmul Kumar Saha 		int ecc = onenand_read_ecc(this);
1182cacbe919SAmul Kumar Saha 		if (ecc & ONENAND_ECC_2BIT_ALL) {
1183cacbe919SAmul Kumar Saha 			printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
1184cacbe919SAmul Kumar Saha 				", controller = 0x%04x\n", ecc, ctrl);
1185bfd7f386SKyungmin Park 			return ONENAND_BBT_READ_ERROR;
1186cacbe919SAmul Kumar Saha 		}
1187bfd7f386SKyungmin Park 	} else {
1188bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_wait: read timeout!"
1189bfd7f386SKyungmin Park 				"ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
1190bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
1191bfd7f386SKyungmin Park 	}
1192bfd7f386SKyungmin Park 
1193ef0921d6SKyungmin Park 	/* Initial bad block case: 0x2400 or 0x0400 */
1194ef0921d6SKyungmin Park 	if (ctrl & ONENAND_CTRL_ERROR) {
1195ef0921d6SKyungmin Park 		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
1196ef0921d6SKyungmin Park 		return ONENAND_BBT_READ_ERROR;
1197ef0921d6SKyungmin Park 	}
1198ef0921d6SKyungmin Park 
1199bfd7f386SKyungmin Park 	return 0;
1200bfd7f386SKyungmin Park }
1201bfd7f386SKyungmin Park 
1202bfd7f386SKyungmin Park /**
1203bfd7f386SKyungmin Park  * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
1204bfd7f386SKyungmin Park  * @param mtd		MTD device structure
1205bfd7f386SKyungmin Park  * @param from		offset to read from
1206bfd7f386SKyungmin Park  * @param ops		oob operation description structure
1207bfd7f386SKyungmin Park  *
1208bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area for bbt scan
1209bfd7f386SKyungmin Park  */
onenand_bbt_read_oob(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)1210bfd7f386SKyungmin Park int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
1211bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
121259829cc1SJean-Christophe PLAGNIOL-VILLARD {
121359829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
121459829cc1SJean-Christophe PLAGNIOL-VILLARD 	int read = 0, thislen, column;
1215cacbe919SAmul Kumar Saha 	int ret = 0, readcmd;
1216bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1217bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
121859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1219*166cae20SMasahiro Yamada 	pr_debug("onenand_bbt_read_oob: from = 0x%08x, len = %zi\n",
1220*166cae20SMasahiro Yamada 		 (unsigned int) from, len);
122159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1222e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1223e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1224cacbe919SAmul Kumar Saha 
1225bfd7f386SKyungmin Park 	/* Initialize return value */
1226bfd7f386SKyungmin Park 	ops->oobretlen = 0;
122759829cc1SJean-Christophe PLAGNIOL-VILLARD 
122859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
122959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((from + len) > mtd->size)) {
1230bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
1231bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
123259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
123359829cc1SJean-Christophe PLAGNIOL-VILLARD 
123459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
123559829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_READING);
123659829cc1SJean-Christophe PLAGNIOL-VILLARD 
123759829cc1SJean-Christophe PLAGNIOL-VILLARD 	column = from & (mtd->oobsize - 1);
123859829cc1SJean-Christophe PLAGNIOL-VILLARD 
123959829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (read < len) {
1240bfd7f386SKyungmin Park 
124159829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = mtd->oobsize - column;
124259829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = min_t(int, thislen, len);
124359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1244ef0921d6SKyungmin Park 		this->spare_buf = buf;
1245cacbe919SAmul Kumar Saha 		this->command(mtd, readcmd, from, mtd->oobsize);
124659829cc1SJean-Christophe PLAGNIOL-VILLARD 
124759829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, from, 0);
124859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1249ef0921d6SKyungmin Park 		ret = this->bbt_wait(mtd, FL_READING);
1250cacbe919SAmul Kumar Saha 		if (unlikely(ret))
1251cacbe919SAmul Kumar Saha 			ret = onenand_recover_lsb(mtd, from, ret);
1252cacbe919SAmul Kumar Saha 
1253bfd7f386SKyungmin Park 		if (ret)
1254bfd7f386SKyungmin Park 			break;
125559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1256ce3277a6SKyungmin Park 		this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
125759829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
125859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
125959829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
126059829cc1SJean-Christophe PLAGNIOL-VILLARD 
126159829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
1262bfd7f386SKyungmin Park 
126359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Read more? */
126459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read < len) {
1265bfd7f386SKyungmin Park 			/* Update Page size */
1266bfd7f386SKyungmin Park 			from += this->writesize;
126759829cc1SJean-Christophe PLAGNIOL-VILLARD 			column = 0;
126859829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
126959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
127059829cc1SJean-Christophe PLAGNIOL-VILLARD 
127159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
127259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
127359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1274bfd7f386SKyungmin Park 	ops->oobretlen = read;
127559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
127659829cc1SJean-Christophe PLAGNIOL-VILLARD }
127759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1278bfd7f386SKyungmin Park 
127959829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
128059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1281bfd7f386SKyungmin Park  * onenand_verify_oob - [GENERIC] verify the oob contents after a write
128259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
128359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf           the databuffer to verify
1284bfd7f386SKyungmin Park  * @param to            offset to read from
128559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_verify_oob(struct mtd_info * mtd,const u_char * buf,loff_t to)1286bfd7f386SKyungmin Park static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
128759829cc1SJean-Christophe PLAGNIOL-VILLARD {
128859829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1289bfd7f386SKyungmin Park 	u_char *oob_buf = this->oob_buf;
1290cacbe919SAmul Kumar Saha 	int status, i, readcmd;
129159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1292e26fd3d3SLukasz Majewski 	readcmd = ONENAND_IS_4KB_PAGE(this) ?
1293e26fd3d3SLukasz Majewski 		ONENAND_CMD_READ : ONENAND_CMD_READOOB;
1294cacbe919SAmul Kumar Saha 
1295cacbe919SAmul Kumar Saha 	this->command(mtd, readcmd, to, mtd->oobsize);
1296bfd7f386SKyungmin Park 	onenand_update_bufferram(mtd, to, 0);
1297bfd7f386SKyungmin Park 	status = this->wait(mtd, FL_READING);
1298bfd7f386SKyungmin Park 	if (status)
1299bfd7f386SKyungmin Park 		return status;
1300bfd7f386SKyungmin Park 
1301ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
1302bfd7f386SKyungmin Park 	for (i = 0; i < mtd->oobsize; i++)
1303bfd7f386SKyungmin Park 		if (buf[i] != 0xFF && buf[i] != oob_buf[i])
1304bfd7f386SKyungmin Park 			return -EBADMSG;
1305bfd7f386SKyungmin Park 
1306bfd7f386SKyungmin Park 	return 0;
1307bfd7f386SKyungmin Park }
1308bfd7f386SKyungmin Park 
1309bfd7f386SKyungmin Park /**
1310bfd7f386SKyungmin Park  * onenand_verify - [GENERIC] verify the chip contents after a write
1311bfd7f386SKyungmin Park  * @param mtd          MTD device structure
1312bfd7f386SKyungmin Park  * @param buf          the databuffer to verify
1313bfd7f386SKyungmin Park  * @param addr         offset to read from
1314bfd7f386SKyungmin Park  * @param len          number of bytes to read and compare
1315bfd7f386SKyungmin Park  */
onenand_verify(struct mtd_info * mtd,const u_char * buf,loff_t addr,size_t len)1316bfd7f386SKyungmin Park static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
1317bfd7f386SKyungmin Park {
1318bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1319bfd7f386SKyungmin Park 	void __iomem *dataram;
1320bfd7f386SKyungmin Park 	int ret = 0;
1321bfd7f386SKyungmin Park 	int thislen, column;
1322bfd7f386SKyungmin Park 
1323bfd7f386SKyungmin Park 	while (len != 0) {
1324bfd7f386SKyungmin Park 		thislen = min_t(int, this->writesize, len);
1325bfd7f386SKyungmin Park 		column = addr & (this->writesize - 1);
1326bfd7f386SKyungmin Park 		if (column + thislen > this->writesize)
1327bfd7f386SKyungmin Park 			thislen = this->writesize - column;
1328bfd7f386SKyungmin Park 
1329bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
1330bfd7f386SKyungmin Park 
1331bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, addr, 0);
133259829cc1SJean-Christophe PLAGNIOL-VILLARD 
133359829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_READING);
133459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret)
133559829cc1SJean-Christophe PLAGNIOL-VILLARD 			return ret;
133659829cc1SJean-Christophe PLAGNIOL-VILLARD 
133759829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, addr, 1);
133859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1339bfd7f386SKyungmin Park 		dataram = this->base + ONENAND_DATARAM;
1340bfd7f386SKyungmin Park 		dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
134159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1342bfd7f386SKyungmin Park 		if (memcmp(buf, dataram + column, thislen))
134359829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EBADMSG;
134459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1345bfd7f386SKyungmin Park 		len -= thislen;
1346bfd7f386SKyungmin Park 		buf += thislen;
1347bfd7f386SKyungmin Park 		addr += thislen;
1348bfd7f386SKyungmin Park 	}
1349bfd7f386SKyungmin Park 
135059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
135159829cc1SJean-Christophe PLAGNIOL-VILLARD }
135259829cc1SJean-Christophe PLAGNIOL-VILLARD #else
1353bfd7f386SKyungmin Park #define onenand_verify(...)             (0)
1354bfd7f386SKyungmin Park #define onenand_verify_oob(...)         (0)
135559829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
135659829cc1SJean-Christophe PLAGNIOL-VILLARD 
13571ae39862SStefan Roese #define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)
135859829cc1SJean-Christophe PLAGNIOL-VILLARD 
135959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1360dfe64e2cSSergey Lapin  * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
136159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
1362bfd7f386SKyungmin Park  * @param oob_buf       oob buffer
1363bfd7f386SKyungmin Park  * @param buf           source address
1364bfd7f386SKyungmin Park  * @param column        oob offset to write to
1365bfd7f386SKyungmin Park  * @param thislen       oob length to write
136659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_fill_auto_oob(struct mtd_info * mtd,u_char * oob_buf,const u_char * buf,int column,int thislen)1367bfd7f386SKyungmin Park static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
1368bfd7f386SKyungmin Park 		const u_char *buf, int column, int thislen)
136959829cc1SJean-Christophe PLAGNIOL-VILLARD {
137059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1371bfd7f386SKyungmin Park 	struct nand_oobfree *free;
1372bfd7f386SKyungmin Park 	int writecol = column;
1373bfd7f386SKyungmin Park 	int writeend = column + thislen;
1374bfd7f386SKyungmin Park 	int lastgap = 0;
1375bfd7f386SKyungmin Park 	unsigned int i;
1376bfd7f386SKyungmin Park 
1377bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
137868ec9c85SPrabhakar Kushwaha 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
137968ec9c85SPrabhakar Kushwaha 	     i++, free++) {
1380bfd7f386SKyungmin Park 		if (writecol >= lastgap)
1381bfd7f386SKyungmin Park 			writecol += free->offset - lastgap;
1382bfd7f386SKyungmin Park 		if (writeend >= lastgap)
1383bfd7f386SKyungmin Park 			writeend += free->offset - lastgap;
1384bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
1385bfd7f386SKyungmin Park 	}
1386bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
138768ec9c85SPrabhakar Kushwaha 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
138868ec9c85SPrabhakar Kushwaha 	     i++, free++) {
1389bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
1390bfd7f386SKyungmin Park 		if (free->offset < writeend && free_end > writecol) {
1391bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,writecol);
1392bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,writeend);
1393bfd7f386SKyungmin Park 			int n = ed - st;
1394bfd7f386SKyungmin Park 			memcpy(oob_buf + st, buf, n);
1395bfd7f386SKyungmin Park 			buf += n;
1396bfd7f386SKyungmin Park 		} else if (column == 0)
1397bfd7f386SKyungmin Park 			break;
1398bfd7f386SKyungmin Park 	}
1399bfd7f386SKyungmin Park 	return 0;
1400bfd7f386SKyungmin Park }
1401bfd7f386SKyungmin Park 
1402bfd7f386SKyungmin Park /**
1403bfd7f386SKyungmin Park  * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
1404bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1405bfd7f386SKyungmin Park  * @param to            offset to write to
1406bfd7f386SKyungmin Park  * @param ops           oob operation description structure
1407bfd7f386SKyungmin Park  *
1408bfd7f386SKyungmin Park  * Write main and/or oob with ECC
1409bfd7f386SKyungmin Park  */
onenand_write_ops_nolock(struct mtd_info * mtd,loff_t to,struct mtd_oob_ops * ops)1410bfd7f386SKyungmin Park static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
1411bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1412bfd7f386SKyungmin Park {
1413bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1414bfd7f386SKyungmin Park 	int written = 0, column, thislen, subpage;
1415bfd7f386SKyungmin Park 	int oobwritten = 0, oobcolumn, thisooblen, oobsize;
1416bfd7f386SKyungmin Park 	size_t len = ops->len;
1417bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
1418bfd7f386SKyungmin Park 	const u_char *buf = ops->datbuf;
1419bfd7f386SKyungmin Park 	const u_char *oob = ops->oobbuf;
1420bfd7f386SKyungmin Park 	u_char *oobbuf;
142159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
142259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1423*166cae20SMasahiro Yamada 	pr_debug("onenand_write_ops_nolock: to = 0x%08x, len = %i\n",
1424*166cae20SMasahiro Yamada 		 (unsigned int) to, (int) len);
142559829cc1SJean-Christophe PLAGNIOL-VILLARD 
142659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Initialize retlen, in case of early exit */
1427bfd7f386SKyungmin Park 	ops->retlen = 0;
1428bfd7f386SKyungmin Park 	ops->oobretlen = 0;
142959829cc1SJean-Christophe PLAGNIOL-VILLARD 
143059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reject writes, which are not page aligned */
1431bfd7f386SKyungmin Park 	if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
1432bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
143359829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
143459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
143559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1436dfe64e2cSSergey Lapin 	if (ops->mode == MTD_OPS_AUTO_OOB)
1437bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1438bfd7f386SKyungmin Park 	else
1439bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1440bfd7f386SKyungmin Park 
1441bfd7f386SKyungmin Park 	oobcolumn = to & (mtd->oobsize - 1);
1442bfd7f386SKyungmin Park 
1443bfd7f386SKyungmin Park 	column = to & (mtd->writesize - 1);
144459829cc1SJean-Christophe PLAGNIOL-VILLARD 
144559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop until all data write */
144659829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (written < len) {
1447bfd7f386SKyungmin Park 		u_char *wbuf = (u_char *) buf;
144859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1449bfd7f386SKyungmin Park 		thislen = min_t(int, mtd->writesize - column, len - written);
1450bfd7f386SKyungmin Park 		thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
145159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1452bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
1453bfd7f386SKyungmin Park 
1454bfd7f386SKyungmin Park 		/* Partial page write */
1455bfd7f386SKyungmin Park 		subpage = thislen < mtd->writesize;
1456bfd7f386SKyungmin Park 		if (subpage) {
1457bfd7f386SKyungmin Park 			memset(this->page_buf, 0xff, mtd->writesize);
1458bfd7f386SKyungmin Park 			memcpy(this->page_buf + column, buf, thislen);
1459bfd7f386SKyungmin Park 			wbuf = this->page_buf;
1460bfd7f386SKyungmin Park 		}
1461bfd7f386SKyungmin Park 
1462ef0921d6SKyungmin Park 		this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
1463bfd7f386SKyungmin Park 
1464bfd7f386SKyungmin Park 		if (oob) {
1465bfd7f386SKyungmin Park 			oobbuf = this->oob_buf;
1466bfd7f386SKyungmin Park 
1467bfd7f386SKyungmin Park 			/* We send data to spare ram with oobsize
1468bfd7f386SKyungmin Park 			 *                          * to prevent byte access */
1469bfd7f386SKyungmin Park 			memset(oobbuf, 0xff, mtd->oobsize);
1470dfe64e2cSSergey Lapin 			if (ops->mode == MTD_OPS_AUTO_OOB)
1471bfd7f386SKyungmin Park 				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
1472bfd7f386SKyungmin Park 			else
1473bfd7f386SKyungmin Park 				memcpy(oobbuf + oobcolumn, oob, thisooblen);
1474bfd7f386SKyungmin Park 
1475bfd7f386SKyungmin Park 			oobwritten += thisooblen;
1476bfd7f386SKyungmin Park 			oob += thisooblen;
1477bfd7f386SKyungmin Park 			oobcolumn = 0;
1478bfd7f386SKyungmin Park 		} else
1479bfd7f386SKyungmin Park 			oobbuf = (u_char *) ffchars;
1480bfd7f386SKyungmin Park 
1481ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
148259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1483d438d508SKyungmin Park 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
148459829cc1SJean-Christophe PLAGNIOL-VILLARD 
148559829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_WRITING);
1486bfd7f386SKyungmin Park 
1487bfd7f386SKyungmin Park 		/* In partial page write we don't update bufferram */
1488bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, !ret && !subpage);
1489bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1490bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1491bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
1492bfd7f386SKyungmin Park 		}
1493bfd7f386SKyungmin Park 
149459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
1495bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
1496bfd7f386SKyungmin Park 			break;
1497bfd7f386SKyungmin Park 		}
1498bfd7f386SKyungmin Park 
1499bfd7f386SKyungmin Park 		/* Only check verify write turn on */
1500bfd7f386SKyungmin Park 		ret = onenand_verify(mtd, buf, to, thislen);
1501bfd7f386SKyungmin Park 		if (ret) {
1502bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
150359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
150459829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
150559829cc1SJean-Christophe PLAGNIOL-VILLARD 
150659829cc1SJean-Christophe PLAGNIOL-VILLARD 		written += thislen;
150759829cc1SJean-Christophe PLAGNIOL-VILLARD 
150859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (written == len)
150959829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
151059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1511bfd7f386SKyungmin Park 		column = 0;
151259829cc1SJean-Christophe PLAGNIOL-VILLARD 		to += thislen;
151359829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
151459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
151559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1516bfd7f386SKyungmin Park 	ops->retlen = written;
151759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1518bfd7f386SKyungmin Park 	return ret;
1519bfd7f386SKyungmin Park }
1520bfd7f386SKyungmin Park 
1521bfd7f386SKyungmin Park /**
1522dfe64e2cSSergey Lapin  * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
1523bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1524bfd7f386SKyungmin Park  * @param to            offset to write to
1525bfd7f386SKyungmin Park  * @param len           number of bytes to write
1526bfd7f386SKyungmin Park  * @param retlen        pointer to variable to store the number of written bytes
1527bfd7f386SKyungmin Park  * @param buf           the data to write
1528bfd7f386SKyungmin Park  * @param mode          operation mode
1529bfd7f386SKyungmin Park  *
1530bfd7f386SKyungmin Park  * OneNAND write out-of-band
1531bfd7f386SKyungmin Park  */
onenand_write_oob_nolock(struct mtd_info * mtd,loff_t to,struct mtd_oob_ops * ops)1532bfd7f386SKyungmin Park static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
1533bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1534bfd7f386SKyungmin Park {
1535bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1536bfd7f386SKyungmin Park 	int column, ret = 0, oobsize;
1537cacbe919SAmul Kumar Saha 	int written = 0, oobcmd;
1538bfd7f386SKyungmin Park 	u_char *oobbuf;
1539bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1540bfd7f386SKyungmin Park 	const u_char *buf = ops->oobbuf;
1541dfe64e2cSSergey Lapin 	unsigned int mode = ops->mode;
1542bfd7f386SKyungmin Park 
1543bfd7f386SKyungmin Park 	to += ops->ooboffs;
1544bfd7f386SKyungmin Park 
1545*166cae20SMasahiro Yamada 	pr_debug("onenand_write_oob_nolock: to = 0x%08x, len = %i\n",
1546*166cae20SMasahiro Yamada 		 (unsigned int) to, (int) len);
1547bfd7f386SKyungmin Park 
1548bfd7f386SKyungmin Park 	/* Initialize retlen, in case of early exit */
1549bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1550bfd7f386SKyungmin Park 
1551dfe64e2cSSergey Lapin 	if (mode == MTD_OPS_AUTO_OOB)
1552bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1553bfd7f386SKyungmin Park 	else
1554bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1555bfd7f386SKyungmin Park 
1556bfd7f386SKyungmin Park 	column = to & (mtd->oobsize - 1);
1557bfd7f386SKyungmin Park 
1558bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1559bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");
1560bfd7f386SKyungmin Park 		return -EINVAL;
1561bfd7f386SKyungmin Park 	}
1562bfd7f386SKyungmin Park 
1563bfd7f386SKyungmin Park 	/* For compatibility with NAND: Do not allow write past end of page */
1564bfd7f386SKyungmin Park 	if (unlikely(column + len > oobsize)) {
1565bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: "
1566bfd7f386SKyungmin Park 				"Attempt to write past end of page\n");
1567bfd7f386SKyungmin Park 		return -EINVAL;
1568bfd7f386SKyungmin Park 	}
1569bfd7f386SKyungmin Park 
1570bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1571bfd7f386SKyungmin Park 	if (unlikely(to >= mtd->size ||
1572bfd7f386SKyungmin Park 				column + len > ((mtd->size >> this->page_shift) -
1573bfd7f386SKyungmin Park 					(to >> this->page_shift)) * oobsize)) {
1574bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n");
1575bfd7f386SKyungmin Park 		return -EINVAL;
1576bfd7f386SKyungmin Park 	}
1577bfd7f386SKyungmin Park 
1578bfd7f386SKyungmin Park 	oobbuf = this->oob_buf;
1579bfd7f386SKyungmin Park 
1580e26fd3d3SLukasz Majewski 	oobcmd = ONENAND_IS_4KB_PAGE(this) ?
1581e26fd3d3SLukasz Majewski 		ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
1582cacbe919SAmul Kumar Saha 
1583bfd7f386SKyungmin Park 	/* Loop until all data write */
1584bfd7f386SKyungmin Park 	while (written < len) {
1585bfd7f386SKyungmin Park 		int thislen = min_t(int, oobsize, len - written);
1586bfd7f386SKyungmin Park 
1587bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
1588bfd7f386SKyungmin Park 
1589bfd7f386SKyungmin Park 		/* We send data to spare ram with oobsize
1590bfd7f386SKyungmin Park 		 * to prevent byte access */
1591bfd7f386SKyungmin Park 		memset(oobbuf, 0xff, mtd->oobsize);
1592dfe64e2cSSergey Lapin 		if (mode == MTD_OPS_AUTO_OOB)
1593bfd7f386SKyungmin Park 			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
1594bfd7f386SKyungmin Park 		else
1595bfd7f386SKyungmin Park 			memcpy(oobbuf + column, buf, thislen);
1596ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
1597bfd7f386SKyungmin Park 
1598e26fd3d3SLukasz Majewski 		if (ONENAND_IS_4KB_PAGE(this)) {
1599cacbe919SAmul Kumar Saha 			/* Set main area of DataRAM to 0xff*/
1600cacbe919SAmul Kumar Saha 			memset(this->page_buf, 0xff, mtd->writesize);
1601cacbe919SAmul Kumar Saha 			this->write_bufferram(mtd, 0, ONENAND_DATARAM,
1602cacbe919SAmul Kumar Saha 				this->page_buf,	0, mtd->writesize);
1603cacbe919SAmul Kumar Saha 		}
1604cacbe919SAmul Kumar Saha 
1605cacbe919SAmul Kumar Saha 		this->command(mtd, oobcmd, to, mtd->oobsize);
1606bfd7f386SKyungmin Park 
1607bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, 0);
1608bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1609bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1610bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, 0);
1611bfd7f386SKyungmin Park 		}
1612bfd7f386SKyungmin Park 
1613bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_WRITING);
1614bfd7f386SKyungmin Park 		if (ret) {
1615bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret);
1616bfd7f386SKyungmin Park 			break;
1617bfd7f386SKyungmin Park 		}
1618bfd7f386SKyungmin Park 
1619bfd7f386SKyungmin Park 		ret = onenand_verify_oob(mtd, oobbuf, to);
1620bfd7f386SKyungmin Park 		if (ret) {
1621bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret);
1622bfd7f386SKyungmin Park 			break;
1623bfd7f386SKyungmin Park 		}
1624bfd7f386SKyungmin Park 
1625bfd7f386SKyungmin Park 		written += thislen;
1626bfd7f386SKyungmin Park 		if (written == len)
1627bfd7f386SKyungmin Park 			break;
1628bfd7f386SKyungmin Park 
1629bfd7f386SKyungmin Park 		to += mtd->writesize;
1630bfd7f386SKyungmin Park 		buf += thislen;
1631bfd7f386SKyungmin Park 		column = 0;
1632bfd7f386SKyungmin Park 	}
1633bfd7f386SKyungmin Park 
1634bfd7f386SKyungmin Park 	ops->oobretlen = written;
163559829cc1SJean-Christophe PLAGNIOL-VILLARD 
163659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
163759829cc1SJean-Christophe PLAGNIOL-VILLARD }
163859829cc1SJean-Christophe PLAGNIOL-VILLARD 
163959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
164059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write - [MTD Interface] compability function for onenand_write_ecc
164159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
164259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
164359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to write
164459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of written bytes
164559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the data to write
164659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1647bfd7f386SKyungmin Park  * Write with ECC
164859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)164959829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
165059829cc1SJean-Christophe PLAGNIOL-VILLARD 		  size_t * retlen, const u_char * buf)
165159829cc1SJean-Christophe PLAGNIOL-VILLARD {
1652bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1653bfd7f386SKyungmin Park 		.len    = len,
1654bfd7f386SKyungmin Park 		.ooblen = 0,
1655bfd7f386SKyungmin Park 		.datbuf = (u_char *) buf,
1656bfd7f386SKyungmin Park 		.oobbuf = NULL,
1657bfd7f386SKyungmin Park 	};
1658bfd7f386SKyungmin Park 	int ret;
1659bfd7f386SKyungmin Park 
1660bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_WRITING);
1661bfd7f386SKyungmin Park 	ret = onenand_write_ops_nolock(mtd, to, &ops);
1662bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1663bfd7f386SKyungmin Park 
1664bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1665bfd7f386SKyungmin Park 	return ret;
166659829cc1SJean-Christophe PLAGNIOL-VILLARD }
166759829cc1SJean-Christophe PLAGNIOL-VILLARD 
166859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
166959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
167059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
167159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
1672bfd7f386SKyungmin Park  * @param ops		oob operation description structure
167359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1674bfd7f386SKyungmin Park  * OneNAND write main and/or out-of-band
167559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_write_oob(struct mtd_info * mtd,loff_t to,struct mtd_oob_ops * ops)1676bfd7f386SKyungmin Park int onenand_write_oob(struct mtd_info *mtd, loff_t to,
1677bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
167859829cc1SJean-Christophe PLAGNIOL-VILLARD {
1679bfd7f386SKyungmin Park 	int ret;
168059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1681bfd7f386SKyungmin Park 	switch (ops->mode) {
1682dfe64e2cSSergey Lapin 	case MTD_OPS_PLACE_OOB:
1683dfe64e2cSSergey Lapin 	case MTD_OPS_AUTO_OOB:
1684bfd7f386SKyungmin Park 		break;
1685dfe64e2cSSergey Lapin 	case MTD_OPS_RAW:
1686bfd7f386SKyungmin Park 		/* Not implemented yet */
1687bfd7f386SKyungmin Park 	default:
168859829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
168959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
169059829cc1SJean-Christophe PLAGNIOL-VILLARD 
169159829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_WRITING);
1692bfd7f386SKyungmin Park 	if (ops->datbuf)
1693bfd7f386SKyungmin Park 		ret = onenand_write_ops_nolock(mtd, to, ops);
1694bfd7f386SKyungmin Park 	else
1695bfd7f386SKyungmin Park 		ret = onenand_write_oob_nolock(mtd, to, ops);
169659829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
169759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1698bfd7f386SKyungmin Park 	return ret;
169959829cc1SJean-Christophe PLAGNIOL-VILLARD 
170059829cc1SJean-Christophe PLAGNIOL-VILLARD }
170159829cc1SJean-Christophe PLAGNIOL-VILLARD 
170259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1703d438d508SKyungmin Park  * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
1704d438d508SKyungmin Park  * @param mtd		MTD device structure
1705d438d508SKyungmin Park  * @param ofs		offset from device start
1706d438d508SKyungmin Park  * @param allowbbt	1, if its allowed to access the bbt area
1707d438d508SKyungmin Park  *
1708d438d508SKyungmin Park  * Check, if the block is bad, Either by reading the bad block table or
1709d438d508SKyungmin Park  * calling of the scan function.
1710d438d508SKyungmin Park  */
onenand_block_isbad_nolock(struct mtd_info * mtd,loff_t ofs,int allowbbt)1711d438d508SKyungmin Park static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
1712d438d508SKyungmin Park {
1713d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1714d438d508SKyungmin Park 	struct bbm_info *bbm = this->bbm;
1715d438d508SKyungmin Park 
1716d438d508SKyungmin Park 	/* Return info from the table */
1717d438d508SKyungmin Park 	return bbm->isbad_bbt(mtd, ofs, allowbbt);
1718d438d508SKyungmin Park }
1719d438d508SKyungmin Park 
1720d438d508SKyungmin Park 
1721d438d508SKyungmin Park /**
172259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_erase - [MTD Interface] erase block(s)
172359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
172459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param instr		erase instruction
172559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
172659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Erase one ore more blocks
172759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_erase(struct mtd_info * mtd,struct erase_info * instr)172859829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
172959829cc1SJean-Christophe PLAGNIOL-VILLARD {
173059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
173159829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int block_size;
1732cacbe919SAmul Kumar Saha 	loff_t addr = instr->addr;
1733cacbe919SAmul Kumar Saha 	unsigned int len = instr->len;
1734cacbe919SAmul Kumar Saha 	int ret = 0, i;
1735cacbe919SAmul Kumar Saha 	struct mtd_erase_region_info *region = NULL;
1736cacbe919SAmul Kumar Saha 	unsigned int region_end = 0;
173759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1738*166cae20SMasahiro Yamada 	pr_debug("onenand_erase: start = 0x%08x, len = %i\n",
1739cacbe919SAmul Kumar Saha 			(unsigned int) addr, len);
174059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1741cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
1742cacbe919SAmul Kumar Saha 		/* Find the eraseregion of this address */
1743cacbe919SAmul Kumar Saha 		i = flexonenand_region(mtd, addr);
1744cacbe919SAmul Kumar Saha 		region = &mtd->eraseregions[i];
1745cacbe919SAmul Kumar Saha 
1746cacbe919SAmul Kumar Saha 		block_size = region->erasesize;
1747cacbe919SAmul Kumar Saha 		region_end = region->offset
1748cacbe919SAmul Kumar Saha 			+ region->erasesize * region->numblocks;
1749cacbe919SAmul Kumar Saha 
1750cacbe919SAmul Kumar Saha 		/* Start address within region must align on block boundary.
1751cacbe919SAmul Kumar Saha 		 * Erase region's start offset is always block start address.
1752cacbe919SAmul Kumar Saha 		 */
1753cacbe919SAmul Kumar Saha 		if (unlikely((addr - region->offset) & (block_size - 1))) {
1754*166cae20SMasahiro Yamada 			pr_debug("onenand_erase:" " Unaligned address\n");
1755cacbe919SAmul Kumar Saha 			return -EINVAL;
1756cacbe919SAmul Kumar Saha 		}
1757cacbe919SAmul Kumar Saha 	} else {
1758cacbe919SAmul Kumar Saha 		block_size = 1 << this->erase_shift;
175959829cc1SJean-Christophe PLAGNIOL-VILLARD 
176059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Start address must align on block boundary */
1761cacbe919SAmul Kumar Saha 		if (unlikely(addr & (block_size - 1))) {
1762*166cae20SMasahiro Yamada 			pr_debug("onenand_erase:" "Unaligned address\n");
176359829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EINVAL;
176459829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
1765cacbe919SAmul Kumar Saha 	}
176659829cc1SJean-Christophe PLAGNIOL-VILLARD 
176759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Length must align on block boundary */
1768cacbe919SAmul Kumar Saha 	if (unlikely(len & (block_size - 1))) {
1769*166cae20SMasahiro Yamada 		pr_debug("onenand_erase: Length not block aligned\n");
177059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
177159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
177259829cc1SJean-Christophe PLAGNIOL-VILLARD 
177359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
177459829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_ERASING);
177559829cc1SJean-Christophe PLAGNIOL-VILLARD 
177659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop throught the pages */
177759829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASING;
177859829cc1SJean-Christophe PLAGNIOL-VILLARD 
177959829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (len) {
178059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1781ef0921d6SKyungmin Park 		/* Check if we have a bad block, we do not erase bad blocks */
1782ef0921d6SKyungmin Park 		if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) {
1783ef0921d6SKyungmin Park 			printk(KERN_WARNING "onenand_erase: attempt to erase"
1784ef0921d6SKyungmin Park 				" a bad block at addr 0x%08x\n",
1785ef0921d6SKyungmin Park 				(unsigned int) addr);
1786ef0921d6SKyungmin Park 			instr->state = MTD_ERASE_FAILED;
1787ef0921d6SKyungmin Park 			goto erase_exit;
1788ef0921d6SKyungmin Park 		}
178959829cc1SJean-Christophe PLAGNIOL-VILLARD 
179059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
179159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1792d438d508SKyungmin Park 		onenand_invalidate_bufferram(mtd, addr, block_size);
1793d438d508SKyungmin Park 
179459829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_ERASING);
179559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check, if it is write protected */
179659829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
179759829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (ret == -EPERM)
1798*166cae20SMasahiro Yamada 				pr_debug("onenand_erase: "
17993167c538SScott Wood 					 "Device is write protected!!!\n");
180059829cc1SJean-Christophe PLAGNIOL-VILLARD 			else
1801*166cae20SMasahiro Yamada 				pr_debug("onenand_erase: "
18023167c538SScott Wood 					 "Failed erase, block %d\n",
1803cacbe919SAmul Kumar Saha 					 onenand_block(this, addr));
180459829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->state = MTD_ERASE_FAILED;
180559829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->fail_addr = addr;
1806ef0921d6SKyungmin Park 
180759829cc1SJean-Christophe PLAGNIOL-VILLARD 			goto erase_exit;
180859829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
180959829cc1SJean-Christophe PLAGNIOL-VILLARD 
181059829cc1SJean-Christophe PLAGNIOL-VILLARD 		len -= block_size;
181159829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr += block_size;
1812cacbe919SAmul Kumar Saha 
1813cacbe919SAmul Kumar Saha 		if (addr == region_end) {
1814cacbe919SAmul Kumar Saha 			if (!len)
1815cacbe919SAmul Kumar Saha 				break;
1816cacbe919SAmul Kumar Saha 			region++;
1817cacbe919SAmul Kumar Saha 
1818cacbe919SAmul Kumar Saha 			block_size = region->erasesize;
1819cacbe919SAmul Kumar Saha 			region_end = region->offset
1820cacbe919SAmul Kumar Saha 				+ region->erasesize * region->numblocks;
1821cacbe919SAmul Kumar Saha 
1822cacbe919SAmul Kumar Saha 			if (len & (block_size - 1)) {
1823cacbe919SAmul Kumar Saha 				/* This has been checked at MTD
1824cacbe919SAmul Kumar Saha 				 * partitioning level. */
1825cacbe919SAmul Kumar Saha 				printk("onenand_erase: Unaligned address\n");
1826cacbe919SAmul Kumar Saha 				goto erase_exit;
1827cacbe919SAmul Kumar Saha 			}
1828cacbe919SAmul Kumar Saha 		}
182959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
183059829cc1SJean-Christophe PLAGNIOL-VILLARD 
183159829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASE_DONE;
183259829cc1SJean-Christophe PLAGNIOL-VILLARD 
183359829cc1SJean-Christophe PLAGNIOL-VILLARD erase_exit:
183459829cc1SJean-Christophe PLAGNIOL-VILLARD 
183559829cc1SJean-Christophe PLAGNIOL-VILLARD 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
183659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do call back function */
183759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!ret)
183859829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd_erase_callback(instr);
183959829cc1SJean-Christophe PLAGNIOL-VILLARD 
184059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
184159829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
184259829cc1SJean-Christophe PLAGNIOL-VILLARD 
184359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
184459829cc1SJean-Christophe PLAGNIOL-VILLARD }
184559829cc1SJean-Christophe PLAGNIOL-VILLARD 
184659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
184759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync - [MTD Interface] sync
184859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
184959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
185059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Sync is actually a wait for chip ready function
185159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_sync(struct mtd_info * mtd)185259829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_sync(struct mtd_info *mtd)
185359829cc1SJean-Christophe PLAGNIOL-VILLARD {
1854*166cae20SMasahiro Yamada 	pr_debug("onenand_sync: called\n");
185559829cc1SJean-Christophe PLAGNIOL-VILLARD 
185659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
185759829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_SYNCING);
185859829cc1SJean-Christophe PLAGNIOL-VILLARD 
185959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Release it and go back */
186059829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
186159829cc1SJean-Christophe PLAGNIOL-VILLARD }
186259829cc1SJean-Christophe PLAGNIOL-VILLARD 
186359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
186459829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
186559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
186659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1867d438d508SKyungmin Park  *
1868d438d508SKyungmin Park  * Check whether the block is bad
186959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_block_isbad(struct mtd_info * mtd,loff_t ofs)187059829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
187159829cc1SJean-Christophe PLAGNIOL-VILLARD {
1872d438d508SKyungmin Park 	int ret;
1873d438d508SKyungmin Park 
1874d438d508SKyungmin Park 	/* Check for invalid offset */
1875d438d508SKyungmin Park 	if (ofs > mtd->size)
1876d438d508SKyungmin Park 		return -EINVAL;
1877d438d508SKyungmin Park 
1878d438d508SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1879d438d508SKyungmin Park 	ret = onenand_block_isbad_nolock(mtd,ofs, 0);
1880d438d508SKyungmin Park 	onenand_release_device(mtd);
1881d438d508SKyungmin Park 	return ret;
188259829cc1SJean-Christophe PLAGNIOL-VILLARD }
188359829cc1SJean-Christophe PLAGNIOL-VILLARD 
188459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
18851714f51aSKyungmin Park  * onenand_default_block_markbad - [DEFAULT] mark a block bad
18861714f51aSKyungmin Park  * @param mtd           MTD device structure
18871714f51aSKyungmin Park  * @param ofs           offset from device start
18881714f51aSKyungmin Park  *
18891714f51aSKyungmin Park  * This is the default implementation, which can be overridden by
18901714f51aSKyungmin Park  * a hardware specific driver.
18911714f51aSKyungmin Park  */
onenand_default_block_markbad(struct mtd_info * mtd,loff_t ofs)18921714f51aSKyungmin Park static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
18931714f51aSKyungmin Park {
18941714f51aSKyungmin Park 	struct onenand_chip *this = mtd->priv;
18951714f51aSKyungmin Park 	struct bbm_info *bbm = this->bbm;
18961714f51aSKyungmin Park 	u_char buf[2] = {0, 0};
18971714f51aSKyungmin Park 	struct mtd_oob_ops ops = {
1898dfe64e2cSSergey Lapin 		.mode = MTD_OPS_PLACE_OOB,
18991714f51aSKyungmin Park 		.ooblen = 2,
19001714f51aSKyungmin Park 		.oobbuf = buf,
19011714f51aSKyungmin Park 		.ooboffs = 0,
19021714f51aSKyungmin Park 	};
19031714f51aSKyungmin Park 	int block;
19041714f51aSKyungmin Park 
19051714f51aSKyungmin Park 	/* Get block number */
1906cacbe919SAmul Kumar Saha 	block = onenand_block(this, ofs);
19071714f51aSKyungmin Park 	if (bbm->bbt)
19081714f51aSKyungmin Park 		bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
19091714f51aSKyungmin Park 
19101714f51aSKyungmin Park 	/* We write two bytes, so we dont have to mess with 16 bit access */
19111714f51aSKyungmin Park 	ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
19121714f51aSKyungmin Park 	return onenand_write_oob_nolock(mtd, ofs, &ops);
19131714f51aSKyungmin Park }
19141714f51aSKyungmin Park 
19151714f51aSKyungmin Park /**
191659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
191759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
191859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1919d438d508SKyungmin Park  *
1920d438d508SKyungmin Park  * Mark the block as bad
192159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_block_markbad(struct mtd_info * mtd,loff_t ofs)192259829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
192359829cc1SJean-Christophe PLAGNIOL-VILLARD {
19244e118ce6SLadislav Michl 	struct onenand_chip *this = mtd->priv;
1925d438d508SKyungmin Park 	int ret;
1926d438d508SKyungmin Park 
1927d438d508SKyungmin Park 	ret = onenand_block_isbad(mtd, ofs);
1928d438d508SKyungmin Park 	if (ret) {
1929d438d508SKyungmin Park 		/* If it was bad already, return success and do nothing */
1930d438d508SKyungmin Park 		if (ret > 0)
193159829cc1SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1932d438d508SKyungmin Park 		return ret;
1933d438d508SKyungmin Park 	}
1934d438d508SKyungmin Park 
19354e118ce6SLadislav Michl 	onenand_get_device(mtd, FL_WRITING);
19364e118ce6SLadislav Michl 	ret = this->block_markbad(mtd, ofs);
19374e118ce6SLadislav Michl 	onenand_release_device(mtd);
19384e118ce6SLadislav Michl 
1939d438d508SKyungmin Park 	return ret;
194059829cc1SJean-Christophe PLAGNIOL-VILLARD }
194159829cc1SJean-Christophe PLAGNIOL-VILLARD 
194259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1943ef0921d6SKyungmin Park  * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
194459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
194559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs           offset relative to mtd start
1946ef0921d6SKyungmin Park  * @param len           number of bytes to lock or unlock
1947ef0921d6SKyungmin Park  * @param cmd           lock or unlock command
194859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1949ef0921d6SKyungmin Park  * Lock or unlock one or more blocks
195059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_do_lock_cmd(struct mtd_info * mtd,loff_t ofs,size_t len,int cmd)1951ef0921d6SKyungmin Park static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
195259829cc1SJean-Christophe PLAGNIOL-VILLARD {
195359829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
195459829cc1SJean-Christophe PLAGNIOL-VILLARD 	int start, end, block, value, status;
195559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1956cacbe919SAmul Kumar Saha 	start = onenand_block(this, ofs);
1957cacbe919SAmul Kumar Saha 	end = onenand_block(this, ofs + len);
195859829cc1SJean-Christophe PLAGNIOL-VILLARD 
195959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Continuous lock scheme */
1960ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK) {
196159829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
196259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(start,
196359829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
196459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set end block address */
196559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(end - 1,
196659829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_END_BLOCK_ADDRESS);
196759829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
1968ef0921d6SKyungmin Park 		this->command(mtd, cmd, 0, 0);
196959829cc1SJean-Christophe PLAGNIOL-VILLARD 
197059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
197159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
197259829cc1SJean-Christophe PLAGNIOL-VILLARD 
197359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
197459829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
197559829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
197659829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
197759829cc1SJean-Christophe PLAGNIOL-VILLARD 
197859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
197959829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
198059829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
198159829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "wp status = 0x%x\n", status);
198259829cc1SJean-Christophe PLAGNIOL-VILLARD 
198359829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
198459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
198559829cc1SJean-Christophe PLAGNIOL-VILLARD 
198659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Block lock scheme */
1987cacbe919SAmul Kumar Saha 	for (block = start; block < end; block++) {
1988ef0921d6SKyungmin Park 		/* Set block address */
1989ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
1990ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
1991ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
1992ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
1993ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
1994ef0921d6SKyungmin Park 
199559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
199659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(block,
199759829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
199859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
199959829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
200059829cc1SJean-Christophe PLAGNIOL-VILLARD 
200159829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
200259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
200359829cc1SJean-Christophe PLAGNIOL-VILLARD 
200459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
200559829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
200659829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
200759829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
200859829cc1SJean-Christophe PLAGNIOL-VILLARD 
200959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
201059829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
201159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
201259829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "block = %d, wp status = 0x%x\n",
201359829cc1SJean-Christophe PLAGNIOL-VILLARD 			       block, status);
201459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
201559829cc1SJean-Christophe PLAGNIOL-VILLARD 
201659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
201759829cc1SJean-Christophe PLAGNIOL-VILLARD }
201859829cc1SJean-Christophe PLAGNIOL-VILLARD 
20194fca3310SStefan Roese #ifdef ONENAND_LINUX
202059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
2021ef0921d6SKyungmin Park  * onenand_lock - [MTD Interface] Lock block(s)
2022ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2023ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
2024ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
2025ef0921d6SKyungmin Park  *
2026ef0921d6SKyungmin Park  * Lock one or more blocks
2027ef0921d6SKyungmin Park  */
onenand_lock(struct mtd_info * mtd,loff_t ofs,size_t len)2028ef0921d6SKyungmin Park static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
2029ef0921d6SKyungmin Park {
2030ef0921d6SKyungmin Park 	int ret;
2031ef0921d6SKyungmin Park 
2032ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
2033ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
2034ef0921d6SKyungmin Park 	onenand_release_device(mtd);
2035ef0921d6SKyungmin Park 	return ret;
2036ef0921d6SKyungmin Park }
2037ef0921d6SKyungmin Park 
2038ef0921d6SKyungmin Park /**
2039ef0921d6SKyungmin Park  * onenand_unlock - [MTD Interface] Unlock block(s)
2040ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2041ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
2042ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
2043ef0921d6SKyungmin Park  *
2044ef0921d6SKyungmin Park  * Unlock one or more blocks
2045ef0921d6SKyungmin Park  */
onenand_unlock(struct mtd_info * mtd,loff_t ofs,size_t len)2046ef0921d6SKyungmin Park static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
2047ef0921d6SKyungmin Park {
2048ef0921d6SKyungmin Park 	int ret;
2049ef0921d6SKyungmin Park 
2050ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
2051ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
2052ef0921d6SKyungmin Park 	onenand_release_device(mtd);
2053ef0921d6SKyungmin Park 	return ret;
2054ef0921d6SKyungmin Park }
20554fca3310SStefan Roese #endif
2056ef0921d6SKyungmin Park 
2057ef0921d6SKyungmin Park /**
2058ef0921d6SKyungmin Park  * onenand_check_lock_status - [OneNAND Interface] Check lock status
2059ef0921d6SKyungmin Park  * @param this          onenand chip data structure
2060ef0921d6SKyungmin Park  *
2061ef0921d6SKyungmin Park  * Check lock status
2062ef0921d6SKyungmin Park  */
onenand_check_lock_status(struct onenand_chip * this)2063ef0921d6SKyungmin Park static int onenand_check_lock_status(struct onenand_chip *this)
2064ef0921d6SKyungmin Park {
2065ef0921d6SKyungmin Park 	unsigned int value, block, status;
2066ef0921d6SKyungmin Park 	unsigned int end;
2067ef0921d6SKyungmin Park 
2068ef0921d6SKyungmin Park 	end = this->chipsize >> this->erase_shift;
2069ef0921d6SKyungmin Park 	for (block = 0; block < end; block++) {
2070ef0921d6SKyungmin Park 		/* Set block address */
2071ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
2072ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
2073ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
2074ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
2075ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
2076ef0921d6SKyungmin Park 		/* Set start block address */
2077ef0921d6SKyungmin Park 		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
2078ef0921d6SKyungmin Park 
2079ef0921d6SKyungmin Park 		/* Check lock status */
2080ef0921d6SKyungmin Park 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
2081ef0921d6SKyungmin Park 		if (!(status & ONENAND_WP_US)) {
2082ef0921d6SKyungmin Park 			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
2083ef0921d6SKyungmin Park 			return 0;
2084ef0921d6SKyungmin Park 		}
2085ef0921d6SKyungmin Park 	}
2086ef0921d6SKyungmin Park 
2087ef0921d6SKyungmin Park 	return 1;
2088ef0921d6SKyungmin Park }
2089ef0921d6SKyungmin Park 
2090ef0921d6SKyungmin Park /**
2091ef0921d6SKyungmin Park  * onenand_unlock_all - [OneNAND Interface] unlock all blocks
2092ef0921d6SKyungmin Park  * @param mtd           MTD device structure
2093ef0921d6SKyungmin Park  *
2094ef0921d6SKyungmin Park  * Unlock all blocks
2095ef0921d6SKyungmin Park  */
onenand_unlock_all(struct mtd_info * mtd)2096ef0921d6SKyungmin Park static void onenand_unlock_all(struct mtd_info *mtd)
2097ef0921d6SKyungmin Park {
2098ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
2099ef0921d6SKyungmin Park 	loff_t ofs = 0;
2100cacbe919SAmul Kumar Saha 	size_t len = mtd->size;
2101ef0921d6SKyungmin Park 
2102ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
2103ef0921d6SKyungmin Park 		/* Set start block address */
2104ef0921d6SKyungmin Park 		this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
2105ef0921d6SKyungmin Park 		/* Write unlock command */
2106ef0921d6SKyungmin Park 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
2107ef0921d6SKyungmin Park 
2108ef0921d6SKyungmin Park 		/* There's no return value */
2109ef0921d6SKyungmin Park 		this->wait(mtd, FL_LOCKING);
2110ef0921d6SKyungmin Park 
2111ef0921d6SKyungmin Park 		/* Sanity check */
2112ef0921d6SKyungmin Park 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
2113ef0921d6SKyungmin Park 				& ONENAND_CTRL_ONGO)
2114ef0921d6SKyungmin Park 			continue;
2115ef0921d6SKyungmin Park 
2116ef0921d6SKyungmin Park 		/* Check lock status */
2117ef0921d6SKyungmin Park 		if (onenand_check_lock_status(this))
2118ef0921d6SKyungmin Park 			return;
2119ef0921d6SKyungmin Park 
2120ef0921d6SKyungmin Park 		/* Workaround for all block unlock in DDP */
2121cacbe919SAmul Kumar Saha 		if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
2122ef0921d6SKyungmin Park 			/* All blocks on another chip */
2123ef0921d6SKyungmin Park 			ofs = this->chipsize >> 1;
2124ef0921d6SKyungmin Park 			len = this->chipsize >> 1;
2125ef0921d6SKyungmin Park 		}
2126ef0921d6SKyungmin Park 	}
2127ef0921d6SKyungmin Park 
2128ef0921d6SKyungmin Park 	onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
2129ef0921d6SKyungmin Park }
2130ef0921d6SKyungmin Park 
2131ef0921d6SKyungmin Park 
2132ef0921d6SKyungmin Park /**
2133ef0921d6SKyungmin Park  * onenand_check_features - Check and set OneNAND features
2134ef0921d6SKyungmin Park  * @param mtd           MTD data structure
2135ef0921d6SKyungmin Park  *
2136ef0921d6SKyungmin Park  * Check and set OneNAND features
2137ef0921d6SKyungmin Park  * - lock scheme
2138ef0921d6SKyungmin Park  * - two plane
2139ef0921d6SKyungmin Park  */
onenand_check_features(struct mtd_info * mtd)2140ef0921d6SKyungmin Park static void onenand_check_features(struct mtd_info *mtd)
2141ef0921d6SKyungmin Park {
2142ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
2143ef0921d6SKyungmin Park 	unsigned int density, process;
2144ef0921d6SKyungmin Park 
2145ef0921d6SKyungmin Park 	/* Lock scheme depends on density and process */
2146ef0921d6SKyungmin Park 	density = onenand_get_density(this->device_id);
2147ef0921d6SKyungmin Park 	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
2148ef0921d6SKyungmin Park 
2149ef0921d6SKyungmin Park 	/* Lock scheme */
2150ef0921d6SKyungmin Park 	switch (density) {
2151ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_4Gb:
2152e26fd3d3SLukasz Majewski 		if (ONENAND_IS_DDP(this))
2153ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
2154e26fd3d3SLukasz Majewski 		else
2155e26fd3d3SLukasz Majewski 			this->options |= ONENAND_HAS_4KB_PAGE;
2156ef0921d6SKyungmin Park 
2157ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_2Gb:
2158ef0921d6SKyungmin Park 		/* 2Gb DDP don't have 2 plane */
2159ef0921d6SKyungmin Park 		if (!ONENAND_IS_DDP(this))
2160ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
2161ef0921d6SKyungmin Park 		this->options |= ONENAND_HAS_UNLOCK_ALL;
2162ef0921d6SKyungmin Park 
2163ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_1Gb:
2164ef0921d6SKyungmin Park 		/* A-Die has all block unlock */
2165ef0921d6SKyungmin Park 		if (process)
2166ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_UNLOCK_ALL;
2167ef0921d6SKyungmin Park 		break;
2168ef0921d6SKyungmin Park 
2169ef0921d6SKyungmin Park 	default:
2170ef0921d6SKyungmin Park 		/* Some OneNAND has continuous lock scheme */
2171ef0921d6SKyungmin Park 		if (!process)
2172ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_CONT_LOCK;
2173ef0921d6SKyungmin Park 		break;
2174ef0921d6SKyungmin Park 	}
2175ef0921d6SKyungmin Park 
2176cacbe919SAmul Kumar Saha 	if (ONENAND_IS_MLC(this))
2177e26fd3d3SLukasz Majewski 		this->options |= ONENAND_HAS_4KB_PAGE;
2178e26fd3d3SLukasz Majewski 
2179e26fd3d3SLukasz Majewski 	if (ONENAND_IS_4KB_PAGE(this))
2180cacbe919SAmul Kumar Saha 		this->options &= ~ONENAND_HAS_2PLANE;
2181cacbe919SAmul Kumar Saha 
2182cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
2183cacbe919SAmul Kumar Saha 		this->options &= ~ONENAND_HAS_CONT_LOCK;
2184cacbe919SAmul Kumar Saha 		this->options |= ONENAND_HAS_UNLOCK_ALL;
2185cacbe919SAmul Kumar Saha 	}
2186cacbe919SAmul Kumar Saha 
2187ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK)
2188ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
2189ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL)
2190ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip support all block unlock\n");
2191ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_2PLANE)
2192ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip has 2 plane\n");
2193e26fd3d3SLukasz Majewski 	if (this->options & ONENAND_HAS_4KB_PAGE)
2194e26fd3d3SLukasz Majewski 		printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
2195e26fd3d3SLukasz Majewski 
2196ef0921d6SKyungmin Park }
2197ef0921d6SKyungmin Park 
2198ef0921d6SKyungmin Park /**
219959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_print_device_info - Print device ID
220059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device        device ID
220159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
220259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Print device ID
220359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_print_device_info(int device,int version)2204ef0921d6SKyungmin Park char *onenand_print_device_info(int device, int version)
220559829cc1SJean-Christophe PLAGNIOL-VILLARD {
2206cacbe919SAmul Kumar Saha 	int vcc, demuxed, ddp, density, flexonenand;
2207195ccfc5SFathi BOUDRA 	char *dev_info = malloc(80);
2208ef0921d6SKyungmin Park 	char *p = dev_info;
220959829cc1SJean-Christophe PLAGNIOL-VILLARD 
221059829cc1SJean-Christophe PLAGNIOL-VILLARD 	vcc = device & ONENAND_DEVICE_VCC_MASK;
221159829cc1SJean-Christophe PLAGNIOL-VILLARD 	demuxed = device & ONENAND_DEVICE_IS_DEMUX;
221259829cc1SJean-Christophe PLAGNIOL-VILLARD 	ddp = device & ONENAND_DEVICE_IS_DDP;
2213cacbe919SAmul Kumar Saha 	density = onenand_get_density(device);
2214cacbe919SAmul Kumar Saha 	flexonenand = device & DEVICE_IS_FLEXONENAND;
2215cacbe919SAmul Kumar Saha 	p += sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
221659829cc1SJean-Christophe PLAGNIOL-VILLARD 	       demuxed ? "" : "Muxed ",
2217cacbe919SAmul Kumar Saha 	       flexonenand ? "Flex-" : "",
221859829cc1SJean-Christophe PLAGNIOL-VILLARD 	       ddp ? "(DDP)" : "",
221959829cc1SJean-Christophe PLAGNIOL-VILLARD 	       (16 << density), vcc ? "2.65/3.3" : "1.8", device);
2220195ccfc5SFathi BOUDRA 
2221ef0921d6SKyungmin Park 	sprintf(p, "\nOneNAND version = 0x%04x", version);
2222ef0921d6SKyungmin Park 	printk("%s\n", dev_info);
2223ef0921d6SKyungmin Park 
2224195ccfc5SFathi BOUDRA 	return dev_info;
222559829cc1SJean-Christophe PLAGNIOL-VILLARD }
222659829cc1SJean-Christophe PLAGNIOL-VILLARD 
222759829cc1SJean-Christophe PLAGNIOL-VILLARD static const struct onenand_manufacturers onenand_manuf_ids[] = {
2228456be17dSEnric Balletbo i Serra 	{ONENAND_MFR_NUMONYX, "Numonyx"},
222959829cc1SJean-Christophe PLAGNIOL-VILLARD 	{ONENAND_MFR_SAMSUNG, "Samsung"},
223059829cc1SJean-Christophe PLAGNIOL-VILLARD };
223159829cc1SJean-Christophe PLAGNIOL-VILLARD 
223259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
223359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_maf - Check manufacturer ID
223459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param manuf         manufacturer ID
223559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
223659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check manufacturer ID
223759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_check_maf(int manuf)223859829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_maf(int manuf)
223959829cc1SJean-Christophe PLAGNIOL-VILLARD {
2240ef0921d6SKyungmin Park 	int size = ARRAY_SIZE(onenand_manuf_ids);
224159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i;
224224ccca5eSMarek Vasut #ifdef ONENAND_DEBUG
224324ccca5eSMarek Vasut 	char *name;
224424ccca5eSMarek Vasut #endif
224559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2246cacbe919SAmul Kumar Saha 	for (i = 0; i < size; i++)
224759829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (manuf == onenand_manuf_ids[i].id)
224859829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
2249ef0921d6SKyungmin Park 
225024ccca5eSMarek Vasut #ifdef ONENAND_DEBUG
2251ef0921d6SKyungmin Park 	if (i < size)
2252ef0921d6SKyungmin Park 		name = onenand_manuf_ids[i].name;
2253ef0921d6SKyungmin Park 	else
2254ef0921d6SKyungmin Park 		name = "Unknown";
225559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2256ef0921d6SKyungmin Park 	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
225759829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
225859829cc1SJean-Christophe PLAGNIOL-VILLARD 
2259ef0921d6SKyungmin Park 	return i == size;
226059829cc1SJean-Christophe PLAGNIOL-VILLARD }
226159829cc1SJean-Christophe PLAGNIOL-VILLARD 
226259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
2263cacbe919SAmul Kumar Saha * flexonenand_get_boundary	- Reads the SLC boundary
2264cacbe919SAmul Kumar Saha * @param onenand_info		- onenand info structure
2265cacbe919SAmul Kumar Saha *
2266cacbe919SAmul Kumar Saha * Fill up boundary[] field in onenand_chip
2267cacbe919SAmul Kumar Saha **/
flexonenand_get_boundary(struct mtd_info * mtd)2268cacbe919SAmul Kumar Saha static int flexonenand_get_boundary(struct mtd_info *mtd)
2269cacbe919SAmul Kumar Saha {
2270cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2271cacbe919SAmul Kumar Saha 	unsigned int die, bdry;
227224ccca5eSMarek Vasut 	int syscfg, locked;
2273cacbe919SAmul Kumar Saha 
2274cacbe919SAmul Kumar Saha 	/* Disable ECC */
2275cacbe919SAmul Kumar Saha 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
2276cacbe919SAmul Kumar Saha 	this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
2277cacbe919SAmul Kumar Saha 
2278cacbe919SAmul Kumar Saha 	for (die = 0; die < this->dies; die++) {
2279cacbe919SAmul Kumar Saha 		this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
2280cacbe919SAmul Kumar Saha 		this->wait(mtd, FL_SYNCING);
2281cacbe919SAmul Kumar Saha 
2282cacbe919SAmul Kumar Saha 		this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
228324ccca5eSMarek Vasut 		this->wait(mtd, FL_READING);
2284cacbe919SAmul Kumar Saha 
2285cacbe919SAmul Kumar Saha 		bdry = this->read_word(this->base + ONENAND_DATARAM);
2286cacbe919SAmul Kumar Saha 		if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
2287cacbe919SAmul Kumar Saha 			locked = 0;
2288cacbe919SAmul Kumar Saha 		else
2289cacbe919SAmul Kumar Saha 			locked = 1;
2290cacbe919SAmul Kumar Saha 		this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
2291cacbe919SAmul Kumar Saha 
2292cacbe919SAmul Kumar Saha 		this->command(mtd, ONENAND_CMD_RESET, 0, 0);
229324ccca5eSMarek Vasut 		this->wait(mtd, FL_RESETING);
2294cacbe919SAmul Kumar Saha 
2295cacbe919SAmul Kumar Saha 		printk(KERN_INFO "Die %d boundary: %d%s\n", die,
2296cacbe919SAmul Kumar Saha 		       this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
2297cacbe919SAmul Kumar Saha 	}
2298cacbe919SAmul Kumar Saha 
2299cacbe919SAmul Kumar Saha 	/* Enable ECC */
2300cacbe919SAmul Kumar Saha 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
2301cacbe919SAmul Kumar Saha 	return 0;
2302cacbe919SAmul Kumar Saha }
2303cacbe919SAmul Kumar Saha 
2304cacbe919SAmul Kumar Saha /**
2305cacbe919SAmul Kumar Saha  * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
2306cacbe919SAmul Kumar Saha  * 			  boundary[], diesize[], mtd->size, mtd->erasesize,
2307cacbe919SAmul Kumar Saha  * 			  mtd->eraseregions
2308cacbe919SAmul Kumar Saha  * @param mtd		- MTD device structure
2309cacbe919SAmul Kumar Saha  */
flexonenand_get_size(struct mtd_info * mtd)2310cacbe919SAmul Kumar Saha static void flexonenand_get_size(struct mtd_info *mtd)
2311cacbe919SAmul Kumar Saha {
2312cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2313cacbe919SAmul Kumar Saha 	int die, i, eraseshift, density;
2314cacbe919SAmul Kumar Saha 	int blksperdie, maxbdry;
2315cacbe919SAmul Kumar Saha 	loff_t ofs;
2316cacbe919SAmul Kumar Saha 
2317cacbe919SAmul Kumar Saha 	density = onenand_get_density(this->device_id);
2318cacbe919SAmul Kumar Saha 	blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
2319cacbe919SAmul Kumar Saha 	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
2320cacbe919SAmul Kumar Saha 	maxbdry = blksperdie - 1;
2321cacbe919SAmul Kumar Saha 	eraseshift = this->erase_shift - 1;
2322cacbe919SAmul Kumar Saha 
2323cacbe919SAmul Kumar Saha 	mtd->numeraseregions = this->dies << 1;
2324cacbe919SAmul Kumar Saha 
2325cacbe919SAmul Kumar Saha 	/* This fills up the device boundary */
2326cacbe919SAmul Kumar Saha 	flexonenand_get_boundary(mtd);
2327cacbe919SAmul Kumar Saha 	die = 0;
2328cacbe919SAmul Kumar Saha 	ofs = 0;
2329cacbe919SAmul Kumar Saha 	i = -1;
2330cacbe919SAmul Kumar Saha 	for (; die < this->dies; die++) {
2331cacbe919SAmul Kumar Saha 		if (!die || this->boundary[die-1] != maxbdry) {
2332cacbe919SAmul Kumar Saha 			i++;
2333cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].offset = ofs;
2334cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize = 1 << eraseshift;
2335cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks =
2336cacbe919SAmul Kumar Saha 							this->boundary[die] + 1;
2337cacbe919SAmul Kumar Saha 			ofs += mtd->eraseregions[i].numblocks << eraseshift;
2338cacbe919SAmul Kumar Saha 			eraseshift++;
2339cacbe919SAmul Kumar Saha 		} else {
2340cacbe919SAmul Kumar Saha 			mtd->numeraseregions -= 1;
2341cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks +=
2342cacbe919SAmul Kumar Saha 							this->boundary[die] + 1;
2343cacbe919SAmul Kumar Saha 			ofs += (this->boundary[die] + 1) << (eraseshift - 1);
2344cacbe919SAmul Kumar Saha 		}
2345cacbe919SAmul Kumar Saha 		if (this->boundary[die] != maxbdry) {
2346cacbe919SAmul Kumar Saha 			i++;
2347cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].offset = ofs;
2348cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize = 1 << eraseshift;
2349cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks = maxbdry ^
2350cacbe919SAmul Kumar Saha 							 this->boundary[die];
2351cacbe919SAmul Kumar Saha 			ofs += mtd->eraseregions[i].numblocks << eraseshift;
2352cacbe919SAmul Kumar Saha 			eraseshift--;
2353cacbe919SAmul Kumar Saha 		} else
2354cacbe919SAmul Kumar Saha 			mtd->numeraseregions -= 1;
2355cacbe919SAmul Kumar Saha 	}
2356cacbe919SAmul Kumar Saha 
2357cacbe919SAmul Kumar Saha 	/* Expose MLC erase size except when all blocks are SLC */
2358cacbe919SAmul Kumar Saha 	mtd->erasesize = 1 << this->erase_shift;
2359cacbe919SAmul Kumar Saha 	if (mtd->numeraseregions == 1)
2360cacbe919SAmul Kumar Saha 		mtd->erasesize >>= 1;
2361cacbe919SAmul Kumar Saha 
2362cacbe919SAmul Kumar Saha 	printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
2363cacbe919SAmul Kumar Saha 	for (i = 0; i < mtd->numeraseregions; i++)
2364cacbe919SAmul Kumar Saha 		printk(KERN_INFO "[offset: 0x%08llx, erasesize: 0x%05x,"
2365cacbe919SAmul Kumar Saha 			" numblocks: %04u]\n", mtd->eraseregions[i].offset,
2366cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].erasesize,
2367cacbe919SAmul Kumar Saha 			mtd->eraseregions[i].numblocks);
2368cacbe919SAmul Kumar Saha 
2369cacbe919SAmul Kumar Saha 	for (die = 0, mtd->size = 0; die < this->dies; die++) {
2370cacbe919SAmul Kumar Saha 		this->diesize[die] = (loff_t) (blksperdie << this->erase_shift);
2371cacbe919SAmul Kumar Saha 		this->diesize[die] -= (loff_t) (this->boundary[die] + 1)
2372cacbe919SAmul Kumar Saha 						 << (this->erase_shift - 1);
2373cacbe919SAmul Kumar Saha 		mtd->size += this->diesize[die];
2374cacbe919SAmul Kumar Saha 	}
2375cacbe919SAmul Kumar Saha }
2376cacbe919SAmul Kumar Saha 
2377cacbe919SAmul Kumar Saha /**
2378cacbe919SAmul Kumar Saha  * flexonenand_check_blocks_erased - Check if blocks are erased
2379cacbe919SAmul Kumar Saha  * @param mtd_info	- mtd info structure
2380cacbe919SAmul Kumar Saha  * @param start		- first erase block to check
2381cacbe919SAmul Kumar Saha  * @param end		- last erase block to check
2382cacbe919SAmul Kumar Saha  *
2383cacbe919SAmul Kumar Saha  * Converting an unerased block from MLC to SLC
2384cacbe919SAmul Kumar Saha  * causes byte values to change. Since both data and its ECC
2385cacbe919SAmul Kumar Saha  * have changed, reads on the block give uncorrectable error.
2386cacbe919SAmul Kumar Saha  * This might lead to the block being detected as bad.
2387cacbe919SAmul Kumar Saha  *
2388cacbe919SAmul Kumar Saha  * Avoid this by ensuring that the block to be converted is
2389cacbe919SAmul Kumar Saha  * erased.
2390cacbe919SAmul Kumar Saha  */
flexonenand_check_blocks_erased(struct mtd_info * mtd,int start,int end)2391cacbe919SAmul Kumar Saha static int flexonenand_check_blocks_erased(struct mtd_info *mtd,
2392cacbe919SAmul Kumar Saha 					int start, int end)
2393cacbe919SAmul Kumar Saha {
2394cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2395cacbe919SAmul Kumar Saha 	int i, ret;
2396cacbe919SAmul Kumar Saha 	int block;
2397cacbe919SAmul Kumar Saha 	struct mtd_oob_ops ops = {
2398dfe64e2cSSergey Lapin 		.mode = MTD_OPS_PLACE_OOB,
2399cacbe919SAmul Kumar Saha 		.ooboffs = 0,
2400cacbe919SAmul Kumar Saha 		.ooblen	= mtd->oobsize,
2401cacbe919SAmul Kumar Saha 		.datbuf	= NULL,
2402cacbe919SAmul Kumar Saha 		.oobbuf	= this->oob_buf,
2403cacbe919SAmul Kumar Saha 	};
2404cacbe919SAmul Kumar Saha 	loff_t addr;
2405cacbe919SAmul Kumar Saha 
2406cacbe919SAmul Kumar Saha 	printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
2407cacbe919SAmul Kumar Saha 
2408cacbe919SAmul Kumar Saha 	for (block = start; block <= end; block++) {
2409cacbe919SAmul Kumar Saha 		addr = flexonenand_addr(this, block);
2410cacbe919SAmul Kumar Saha 		if (onenand_block_isbad_nolock(mtd, addr, 0))
2411cacbe919SAmul Kumar Saha 			continue;
2412cacbe919SAmul Kumar Saha 
2413cacbe919SAmul Kumar Saha 		/*
2414cacbe919SAmul Kumar Saha 		 * Since main area write results in ECC write to spare,
2415cacbe919SAmul Kumar Saha 		 * it is sufficient to check only ECC bytes for change.
2416cacbe919SAmul Kumar Saha 		 */
2417cacbe919SAmul Kumar Saha 		ret = onenand_read_oob_nolock(mtd, addr, &ops);
2418cacbe919SAmul Kumar Saha 		if (ret)
2419cacbe919SAmul Kumar Saha 			return ret;
2420cacbe919SAmul Kumar Saha 
2421cacbe919SAmul Kumar Saha 		for (i = 0; i < mtd->oobsize; i++)
2422cacbe919SAmul Kumar Saha 			if (this->oob_buf[i] != 0xff)
2423cacbe919SAmul Kumar Saha 				break;
2424cacbe919SAmul Kumar Saha 
2425cacbe919SAmul Kumar Saha 		if (i != mtd->oobsize) {
2426cacbe919SAmul Kumar Saha 			printk(KERN_WARNING "Block %d not erased.\n", block);
2427cacbe919SAmul Kumar Saha 			return 1;
2428cacbe919SAmul Kumar Saha 		}
2429cacbe919SAmul Kumar Saha 	}
2430cacbe919SAmul Kumar Saha 
2431cacbe919SAmul Kumar Saha 	return 0;
2432cacbe919SAmul Kumar Saha }
2433cacbe919SAmul Kumar Saha 
2434cacbe919SAmul Kumar Saha /**
2435cacbe919SAmul Kumar Saha  * flexonenand_set_boundary	- Writes the SLC boundary
2436cacbe919SAmul Kumar Saha  * @param mtd			- mtd info structure
2437cacbe919SAmul Kumar Saha  */
flexonenand_set_boundary(struct mtd_info * mtd,int die,int boundary,int lock)2438cacbe919SAmul Kumar Saha int flexonenand_set_boundary(struct mtd_info *mtd, int die,
2439cacbe919SAmul Kumar Saha 				    int boundary, int lock)
2440cacbe919SAmul Kumar Saha {
2441cacbe919SAmul Kumar Saha 	struct onenand_chip *this = mtd->priv;
2442cacbe919SAmul Kumar Saha 	int ret, density, blksperdie, old, new, thisboundary;
2443cacbe919SAmul Kumar Saha 	loff_t addr;
2444cacbe919SAmul Kumar Saha 
2445cacbe919SAmul Kumar Saha 	if (die >= this->dies)
2446cacbe919SAmul Kumar Saha 		return -EINVAL;
2447cacbe919SAmul Kumar Saha 
2448cacbe919SAmul Kumar Saha 	if (boundary == this->boundary[die])
2449cacbe919SAmul Kumar Saha 		return 0;
2450cacbe919SAmul Kumar Saha 
2451cacbe919SAmul Kumar Saha 	density = onenand_get_density(this->device_id);
2452cacbe919SAmul Kumar Saha 	blksperdie = ((16 << density) << 20) >> this->erase_shift;
2453cacbe919SAmul Kumar Saha 	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
2454cacbe919SAmul Kumar Saha 
2455cacbe919SAmul Kumar Saha 	if (boundary >= blksperdie) {
2456cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2457cacbe919SAmul Kumar Saha 			"Invalid boundary value. "
2458cacbe919SAmul Kumar Saha 			"Boundary not changed.\n");
2459cacbe919SAmul Kumar Saha 		return -EINVAL;
2460cacbe919SAmul Kumar Saha 	}
2461cacbe919SAmul Kumar Saha 
2462cacbe919SAmul Kumar Saha 	/* Check if converting blocks are erased */
2463cacbe919SAmul Kumar Saha 	old = this->boundary[die] + (die * this->density_mask);
2464cacbe919SAmul Kumar Saha 	new = boundary + (die * this->density_mask);
2465cacbe919SAmul Kumar Saha 	ret = flexonenand_check_blocks_erased(mtd, min(old, new)
2466cacbe919SAmul Kumar Saha 						+ 1, max(old, new));
2467cacbe919SAmul Kumar Saha 	if (ret) {
2468cacbe919SAmul Kumar Saha 		printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n");
2469cacbe919SAmul Kumar Saha 		return ret;
2470cacbe919SAmul Kumar Saha 	}
2471cacbe919SAmul Kumar Saha 
2472cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
2473cacbe919SAmul Kumar Saha 	this->wait(mtd, FL_SYNCING);
2474cacbe919SAmul Kumar Saha 
2475cacbe919SAmul Kumar Saha 	/* Check is boundary is locked */
2476cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
2477cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_READING);
2478cacbe919SAmul Kumar Saha 
2479cacbe919SAmul Kumar Saha 	thisboundary = this->read_word(this->base + ONENAND_DATARAM);
2480cacbe919SAmul Kumar Saha 	if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
2481cacbe919SAmul Kumar Saha 		printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n");
2482cacbe919SAmul Kumar Saha 		goto out;
2483cacbe919SAmul Kumar Saha 	}
2484cacbe919SAmul Kumar Saha 
2485cacbe919SAmul Kumar Saha 	printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n",
2486cacbe919SAmul Kumar Saha 			die, boundary, lock ? "(Locked)" : "(Unlocked)");
2487cacbe919SAmul Kumar Saha 
2488cacbe919SAmul Kumar Saha 	boundary &= FLEXONENAND_PI_MASK;
2489cacbe919SAmul Kumar Saha 	boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
2490cacbe919SAmul Kumar Saha 
2491cacbe919SAmul Kumar Saha 	addr = die ? this->diesize[0] : 0;
2492cacbe919SAmul Kumar Saha 	this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
2493cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_ERASING);
2494cacbe919SAmul Kumar Saha 	if (ret) {
2495cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2496cacbe919SAmul Kumar Saha 			"Failed PI erase for Die %d\n", die);
2497cacbe919SAmul Kumar Saha 		goto out;
2498cacbe919SAmul Kumar Saha 	}
2499cacbe919SAmul Kumar Saha 
2500cacbe919SAmul Kumar Saha 	this->write_word(boundary, this->base + ONENAND_DATARAM);
2501cacbe919SAmul Kumar Saha 	this->command(mtd, ONENAND_CMD_PROG, addr, 0);
2502cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_WRITING);
2503cacbe919SAmul Kumar Saha 	if (ret) {
2504cacbe919SAmul Kumar Saha 		printk("flexonenand_set_boundary:"
2505cacbe919SAmul Kumar Saha 			"Failed PI write for Die %d\n", die);
2506cacbe919SAmul Kumar Saha 		goto out;
2507cacbe919SAmul Kumar Saha 	}
2508cacbe919SAmul Kumar Saha 
2509cacbe919SAmul Kumar Saha 	this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
2510cacbe919SAmul Kumar Saha 	ret = this->wait(mtd, FL_WRITING);
2511cacbe919SAmul Kumar Saha out:
2512cacbe919SAmul Kumar Saha 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
2513cacbe919SAmul Kumar Saha 	this->wait(mtd, FL_RESETING);
2514cacbe919SAmul Kumar Saha 	if (!ret)
2515cacbe919SAmul Kumar Saha 		/* Recalculate device size on boundary change*/
2516cacbe919SAmul Kumar Saha 		flexonenand_get_size(mtd);
2517cacbe919SAmul Kumar Saha 
2518cacbe919SAmul Kumar Saha 	return ret;
2519cacbe919SAmul Kumar Saha }
2520cacbe919SAmul Kumar Saha 
2521cacbe919SAmul Kumar Saha /**
25226b3967bbSLukasz Majewski  * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND chip
252359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
252459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
252559829cc1SJean-Christophe PLAGNIOL-VILLARD  * OneNAND detection method:
252659829cc1SJean-Christophe PLAGNIOL-VILLARD  *   Compare the the values from command with ones from register
252759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_chip_probe(struct mtd_info * mtd)25286b3967bbSLukasz Majewski static int onenand_chip_probe(struct mtd_info *mtd)
252959829cc1SJean-Christophe PLAGNIOL-VILLARD {
253059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
25316b3967bbSLukasz Majewski 	int bram_maf_id, bram_dev_id, maf_id, dev_id;
2532ef0921d6SKyungmin Park 	int syscfg;
2533ef0921d6SKyungmin Park 
2534ef0921d6SKyungmin Park 	/* Save system configuration 1 */
2535ef0921d6SKyungmin Park 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
25366b3967bbSLukasz Majewski 
2537ef0921d6SKyungmin Park 	/* Clear Sync. Burst Read mode to read BootRAM */
25386b3967bbSLukasz Majewski 	this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ),
25396b3967bbSLukasz Majewski 			 this->base + ONENAND_REG_SYS_CFG1);
254059829cc1SJean-Christophe PLAGNIOL-VILLARD 
254159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Send the command for reading device ID from BootRAM */
254259829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
254359829cc1SJean-Christophe PLAGNIOL-VILLARD 
254459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from BootRAM */
254559829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
254659829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
254759829cc1SJean-Christophe PLAGNIOL-VILLARD 
254859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reset OneNAND to read default register values */
254959829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
255059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2551d438d508SKyungmin Park 	/* Wait reset */
2552d9098ee5SLadislav Michl 	if (this->wait(mtd, FL_RESETING))
2553d9098ee5SLadislav Michl 		return -ENXIO;
255459829cc1SJean-Christophe PLAGNIOL-VILLARD 
2555ef0921d6SKyungmin Park 	/* Restore system configuration 1 */
2556ef0921d6SKyungmin Park 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
2557ef0921d6SKyungmin Park 
2558ef0921d6SKyungmin Park 	/* Check manufacturer ID */
2559ef0921d6SKyungmin Park 	if (onenand_check_maf(bram_maf_id))
2560ef0921d6SKyungmin Park 		return -ENXIO;
2561ef0921d6SKyungmin Park 
256259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from Register */
256359829cc1SJean-Christophe PLAGNIOL-VILLARD 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
256459829cc1SJean-Christophe PLAGNIOL-VILLARD 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
256559829cc1SJean-Christophe PLAGNIOL-VILLARD 
256659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Check OneNAND device */
256759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (maf_id != bram_maf_id || dev_id != bram_dev_id)
256859829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
256959829cc1SJean-Christophe PLAGNIOL-VILLARD 
25706b3967bbSLukasz Majewski 	return 0;
25716b3967bbSLukasz Majewski }
25726b3967bbSLukasz Majewski 
25736b3967bbSLukasz Majewski /**
25746b3967bbSLukasz Majewski  * onenand_probe - [OneNAND Interface] Probe the OneNAND device
25756b3967bbSLukasz Majewski  * @param mtd		MTD device structure
25766b3967bbSLukasz Majewski  *
25776b3967bbSLukasz Majewski  * OneNAND detection method:
25786b3967bbSLukasz Majewski  *   Compare the the values from command with ones from register
25796b3967bbSLukasz Majewski  */
onenand_probe(struct mtd_info * mtd)25806b3967bbSLukasz Majewski int onenand_probe(struct mtd_info *mtd)
25816b3967bbSLukasz Majewski {
25826b3967bbSLukasz Majewski 	struct onenand_chip *this = mtd->priv;
25831432c763SWolfgang Denk 	int dev_id, ver_id;
25846b3967bbSLukasz Majewski 	int density;
25856b3967bbSLukasz Majewski 	int ret;
25866b3967bbSLukasz Majewski 
25876b3967bbSLukasz Majewski 	ret = this->chip_probe(mtd);
25886b3967bbSLukasz Majewski 	if (ret)
25896b3967bbSLukasz Majewski 		return ret;
25906b3967bbSLukasz Majewski 
25911432c763SWolfgang Denk 	/* Read device IDs from Register */
25926b3967bbSLukasz Majewski 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
25936b3967bbSLukasz Majewski 	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
25946b3967bbSLukasz Majewski 	this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
25956b3967bbSLukasz Majewski 
259659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash device information */
2597ef0921d6SKyungmin Park 	mtd->name = onenand_print_device_info(dev_id, ver_id);
259859829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->device_id = dev_id;
25998cf11f3aSStefan Roese 	this->version_id = ver_id;
260059829cc1SJean-Christophe PLAGNIOL-VILLARD 
2601e26fd3d3SLukasz Majewski 	/* Check OneNAND features */
2602e26fd3d3SLukasz Majewski 	onenand_check_features(mtd);
2603e26fd3d3SLukasz Majewski 
2604ef0921d6SKyungmin Park 	density = onenand_get_density(dev_id);
2605cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this)) {
2606cacbe919SAmul Kumar Saha 		this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
2607cacbe919SAmul Kumar Saha 		/* Maximum possible erase regions */
2608cacbe919SAmul Kumar Saha 		mtd->numeraseregions = this->dies << 1;
2609cacbe919SAmul Kumar Saha 		mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info)
2610cacbe919SAmul Kumar Saha 					* (this->dies << 1));
2611cacbe919SAmul Kumar Saha 		if (!mtd->eraseregions)
2612cacbe919SAmul Kumar Saha 			return -ENOMEM;
2613cacbe919SAmul Kumar Saha 	}
2614cacbe919SAmul Kumar Saha 
2615cacbe919SAmul Kumar Saha 	/*
2616cacbe919SAmul Kumar Saha 	 * For Flex-OneNAND, chipsize represents maximum possible device size.
2617cacbe919SAmul Kumar Saha 	 * mtd->size represents the actual device size.
2618cacbe919SAmul Kumar Saha 	 */
261959829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->chipsize = (16 << density) << 20;
262059829cc1SJean-Christophe PLAGNIOL-VILLARD 
262159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* OneNAND page size & block size */
262259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* The data buffer size is equal to page size */
2623d438d508SKyungmin Park 	mtd->writesize =
262459829cc1SJean-Christophe PLAGNIOL-VILLARD 	    this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
2625cacbe919SAmul Kumar Saha 	/* We use the full BufferRAM */
2626e26fd3d3SLukasz Majewski 	if (ONENAND_IS_4KB_PAGE(this))
2627cacbe919SAmul Kumar Saha 		mtd->writesize <<= 1;
2628cacbe919SAmul Kumar Saha 
2629d438d508SKyungmin Park 	mtd->oobsize = mtd->writesize >> 5;
263059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Pagers per block is always 64 in OneNAND */
2631d438d508SKyungmin Park 	mtd->erasesize = mtd->writesize << 6;
2632cacbe919SAmul Kumar Saha 	/*
2633cacbe919SAmul Kumar Saha 	 * Flex-OneNAND SLC area has 64 pages per block.
2634cacbe919SAmul Kumar Saha 	 * Flex-OneNAND MLC area has 128 pages per block.
2635cacbe919SAmul Kumar Saha 	 * Expose MLC erase size to find erase_shift and page_mask.
2636cacbe919SAmul Kumar Saha 	 */
2637cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this))
2638cacbe919SAmul Kumar Saha 		mtd->erasesize <<= 1;
263959829cc1SJean-Christophe PLAGNIOL-VILLARD 
264059829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->erase_shift = ffs(mtd->erasesize) - 1;
2641d438d508SKyungmin Park 	this->page_shift = ffs(mtd->writesize) - 1;
264259829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->ppb_shift = (this->erase_shift - this->page_shift);
2643d438d508SKyungmin Park 	this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
2644cacbe919SAmul Kumar Saha 	/* Set density mask. it is used for DDP */
2645cacbe919SAmul Kumar Saha 	if (ONENAND_IS_DDP(this))
2646cacbe919SAmul Kumar Saha 		this->density_mask = this->chipsize >> (this->erase_shift + 1);
2647bfd7f386SKyungmin Park 	/* It's real page size */
2648bfd7f386SKyungmin Park 	this->writesize = mtd->writesize;
264959829cc1SJean-Christophe PLAGNIOL-VILLARD 
265059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* REVIST: Multichip handling */
265159829cc1SJean-Christophe PLAGNIOL-VILLARD 
2652cacbe919SAmul Kumar Saha 	if (FLEXONENAND(this))
2653cacbe919SAmul Kumar Saha 		flexonenand_get_size(mtd);
2654cacbe919SAmul Kumar Saha 	else
265559829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd->size = this->chipsize;
265659829cc1SJean-Christophe PLAGNIOL-VILLARD 
2657d438d508SKyungmin Park 	mtd->flags = MTD_CAP_NANDFLASH;
2658dfe64e2cSSergey Lapin 	mtd->_erase = onenand_erase;
2659dfe64e2cSSergey Lapin 	mtd->_read_oob = onenand_read_oob;
2660dfe64e2cSSergey Lapin 	mtd->_write_oob = onenand_write_oob;
2661dfe64e2cSSergey Lapin 	mtd->_sync = onenand_sync;
2662dfe64e2cSSergey Lapin 	mtd->_block_isbad = onenand_block_isbad;
2663dfe64e2cSSergey Lapin 	mtd->_block_markbad = onenand_block_markbad;
266452486927SLadislav Michl 	mtd->writebufsize = mtd->writesize;
2665195ccfc5SFathi BOUDRA 
266659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
266759829cc1SJean-Christophe PLAGNIOL-VILLARD }
266859829cc1SJean-Christophe PLAGNIOL-VILLARD 
266959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
267059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
267159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
267259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param maxchips	Number of chips to scan for
267359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
267459829cc1SJean-Christophe PLAGNIOL-VILLARD  * This fills out all the not initialized function pointers
267559829cc1SJean-Christophe PLAGNIOL-VILLARD  * with the defaults.
267659829cc1SJean-Christophe PLAGNIOL-VILLARD  * The flash ID is read and the mtd/chip structures are
267759829cc1SJean-Christophe PLAGNIOL-VILLARD  * filled with the appropriate values.
267859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_scan(struct mtd_info * mtd,int maxchips)267959829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_scan(struct mtd_info *mtd, int maxchips)
268059829cc1SJean-Christophe PLAGNIOL-VILLARD {
26811ae39862SStefan Roese 	int i;
268259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
268359829cc1SJean-Christophe PLAGNIOL-VILLARD 
268459829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_word)
268559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_word = onenand_readw;
268659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_word)
268759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word = onenand_writew;
268859829cc1SJean-Christophe PLAGNIOL-VILLARD 
268959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->command)
269059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command = onenand_command;
269159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->wait)
269259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait = onenand_wait;
2693ef0921d6SKyungmin Park 	if (!this->bbt_wait)
2694ef0921d6SKyungmin Park 		this->bbt_wait = onenand_bbt_wait;
269559829cc1SJean-Christophe PLAGNIOL-VILLARD 
269659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_bufferram)
269759829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_read_bufferram;
269859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_bufferram)
269959829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_bufferram = onenand_write_bufferram;
270059829cc1SJean-Christophe PLAGNIOL-VILLARD 
27016b3967bbSLukasz Majewski 	if (!this->chip_probe)
27026b3967bbSLukasz Majewski 		this->chip_probe = onenand_chip_probe;
27036b3967bbSLukasz Majewski 
27041714f51aSKyungmin Park 	if (!this->block_markbad)
27051714f51aSKyungmin Park 		this->block_markbad = onenand_default_block_markbad;
2706ef0921d6SKyungmin Park 	if (!this->scan_bbt)
2707ef0921d6SKyungmin Park 		this->scan_bbt = onenand_default_bbt;
2708ef0921d6SKyungmin Park 
270959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (onenand_probe(mtd))
271059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
271159829cc1SJean-Christophe PLAGNIOL-VILLARD 
271259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Set Sync. Burst Read after probing */
271359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (this->mmcontrol) {
271459829cc1SJean-Christophe PLAGNIOL-VILLARD 		printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
271559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_sync_read_bufferram;
271659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
271759829cc1SJean-Christophe PLAGNIOL-VILLARD 
2718bfd7f386SKyungmin Park 	/* Allocate buffers, if necessary */
2719bfd7f386SKyungmin Park 	if (!this->page_buf) {
2720bfd7f386SKyungmin Park 		this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
2721bfd7f386SKyungmin Park 		if (!this->page_buf) {
2722bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
2723bfd7f386SKyungmin Park 			return -ENOMEM;
2724bfd7f386SKyungmin Park 		}
2725bfd7f386SKyungmin Park 		this->options |= ONENAND_PAGEBUF_ALLOC;
2726bfd7f386SKyungmin Park 	}
2727bfd7f386SKyungmin Park 	if (!this->oob_buf) {
2728bfd7f386SKyungmin Park 		this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
2729bfd7f386SKyungmin Park 		if (!this->oob_buf) {
2730bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n");
2731bfd7f386SKyungmin Park 			if (this->options & ONENAND_PAGEBUF_ALLOC) {
2732bfd7f386SKyungmin Park 				this->options &= ~ONENAND_PAGEBUF_ALLOC;
2733bfd7f386SKyungmin Park 				kfree(this->page_buf);
2734bfd7f386SKyungmin Park 			}
2735bfd7f386SKyungmin Park 			return -ENOMEM;
2736bfd7f386SKyungmin Park 		}
2737bfd7f386SKyungmin Park 		this->options |= ONENAND_OOBBUF_ALLOC;
2738bfd7f386SKyungmin Park 	}
2739bfd7f386SKyungmin Park 
27401ae39862SStefan Roese 	this->state = FL_READY;
27411ae39862SStefan Roese 
27421ae39862SStefan Roese 	/*
27431ae39862SStefan Roese 	 * Allow subpage writes up to oobsize.
27441ae39862SStefan Roese 	 */
27451ae39862SStefan Roese 	switch (mtd->oobsize) {
2746cacbe919SAmul Kumar Saha 	case 128:
2747cacbe919SAmul Kumar Saha 		this->ecclayout = &onenand_oob_128;
2748cacbe919SAmul Kumar Saha 		mtd->subpage_sft = 0;
2749cacbe919SAmul Kumar Saha 		break;
2750cacbe919SAmul Kumar Saha 
27511ae39862SStefan Roese 	case 64:
27521ae39862SStefan Roese 		this->ecclayout = &onenand_oob_64;
27531ae39862SStefan Roese 		mtd->subpage_sft = 2;
27541ae39862SStefan Roese 		break;
27551ae39862SStefan Roese 
27561ae39862SStefan Roese 	case 32:
27571ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
27581ae39862SStefan Roese 		mtd->subpage_sft = 1;
27591ae39862SStefan Roese 		break;
27601ae39862SStefan Roese 
27611ae39862SStefan Roese 	default:
27621ae39862SStefan Roese 		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
27631ae39862SStefan Roese 			mtd->oobsize);
27641ae39862SStefan Roese 		mtd->subpage_sft = 0;
27651ae39862SStefan Roese 		/* To prevent kernel oops */
27661ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
27671ae39862SStefan Roese 		break;
27681ae39862SStefan Roese 	}
27691ae39862SStefan Roese 
27701ae39862SStefan Roese 	this->subpagesize = mtd->writesize >> mtd->subpage_sft;
27711ae39862SStefan Roese 
27721ae39862SStefan Roese 	/*
27731ae39862SStefan Roese 	 * The number of bytes available for a client to place data into
27741ae39862SStefan Roese 	 * the out of band area
27751ae39862SStefan Roese 	 */
27761ae39862SStefan Roese 	this->ecclayout->oobavail = 0;
277768ec9c85SPrabhakar Kushwaha 
277868ec9c85SPrabhakar Kushwaha 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE &&
27791ae39862SStefan Roese 	    this->ecclayout->oobfree[i].length; i++)
27801ae39862SStefan Roese 		this->ecclayout->oobavail +=
27811ae39862SStefan Roese 			this->ecclayout->oobfree[i].length;
27821ae39862SStefan Roese 	mtd->oobavail = this->ecclayout->oobavail;
27831ae39862SStefan Roese 
27841ae39862SStefan Roese 	mtd->ecclayout = this->ecclayout;
27851ae39862SStefan Roese 
2786ef0921d6SKyungmin Park 	/* Unlock whole block */
2787ef0921d6SKyungmin Park 	onenand_unlock_all(mtd);
278859829cc1SJean-Christophe PLAGNIOL-VILLARD 
2789ef0921d6SKyungmin Park 	return this->scan_bbt(mtd);
279059829cc1SJean-Christophe PLAGNIOL-VILLARD }
279159829cc1SJean-Christophe PLAGNIOL-VILLARD 
279259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
279359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
279459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
279559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
onenand_release(struct mtd_info * mtd)279659829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_release(struct mtd_info *mtd)
279759829cc1SJean-Christophe PLAGNIOL-VILLARD {
279859829cc1SJean-Christophe PLAGNIOL-VILLARD }
2799