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