xref: /rk3399_rockchip-uboot/drivers/mtd/onenand/onenand_base.c (revision ce3277a6f2c082f39596d3d3d88dd0a5bc91439d)
159829cc1SJean-Christophe PLAGNIOL-VILLARD /*
259829cc1SJean-Christophe PLAGNIOL-VILLARD  *  linux/drivers/mtd/onenand/onenand_base.c
359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
459829cc1SJean-Christophe PLAGNIOL-VILLARD  *  Copyright (C) 2005-2007 Samsung Electronics
559829cc1SJean-Christophe PLAGNIOL-VILLARD  *  Kyungmin Park <kyungmin.park@samsung.com>
659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
7bfd7f386SKyungmin Park  *  Credits:
8bfd7f386SKyungmin Park  *      Adrian Hunter <ext-adrian.hunter@nokia.com>:
9bfd7f386SKyungmin Park  *      auto-placement support, read-while load support, various fixes
10bfd7f386SKyungmin Park  *      Copyright (C) Nokia Corporation, 2007
11bfd7f386SKyungmin Park  *
1259829cc1SJean-Christophe PLAGNIOL-VILLARD  * This program is free software; you can redistribute it and/or modify
1359829cc1SJean-Christophe PLAGNIOL-VILLARD  * it under the terms of the GNU General Public License version 2 as
1459829cc1SJean-Christophe PLAGNIOL-VILLARD  * published by the Free Software Foundation.
1559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1759829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
1859829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/compat.h>
1959829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/mtd.h>
2059829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/onenand.h>
2159829cc1SJean-Christophe PLAGNIOL-VILLARD 
2259829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h>
2359829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/errno.h>
24195ccfc5SFathi BOUDRA #include <malloc.h>
2559829cc1SJean-Christophe PLAGNIOL-VILLARD 
2677e475ccSKyungmin Park /* It should access 16-bit instead of 8-bit */
27d2c6fbecSWolfgang Denk static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
2877e475ccSKyungmin Park {
2977e475ccSKyungmin Park 	void *ret = dst;
3077e475ccSKyungmin Park 	short *d = dst;
3177e475ccSKyungmin Park 	const short *s = src;
3277e475ccSKyungmin Park 
3377e475ccSKyungmin Park 	len >>= 1;
3477e475ccSKyungmin Park 	while (len-- > 0)
3577e475ccSKyungmin Park 		*d++ = *s++;
3677e475ccSKyungmin Park 	return ret;
3777e475ccSKyungmin Park }
3877e475ccSKyungmin Park 
391ae39862SStefan Roese /**
401ae39862SStefan Roese  * onenand_oob_64 - oob info for large (2KB) page
411ae39862SStefan Roese  */
421ae39862SStefan Roese static struct nand_ecclayout onenand_oob_64 = {
431ae39862SStefan Roese 	.eccbytes	= 20,
441ae39862SStefan Roese 	.eccpos		= {
451ae39862SStefan Roese 		8, 9, 10, 11, 12,
461ae39862SStefan Roese 		24, 25, 26, 27, 28,
471ae39862SStefan Roese 		40, 41, 42, 43, 44,
481ae39862SStefan Roese 		56, 57, 58, 59, 60,
491ae39862SStefan Roese 		},
501ae39862SStefan Roese 	.oobfree	= {
511ae39862SStefan Roese 		{2, 3}, {14, 2}, {18, 3}, {30, 2},
521ae39862SStefan Roese 		{34, 3}, {46, 2}, {50, 3}, {62, 2}
531ae39862SStefan Roese 	}
541ae39862SStefan Roese };
551ae39862SStefan Roese 
561ae39862SStefan Roese /**
571ae39862SStefan Roese  * onenand_oob_32 - oob info for middle (1KB) page
581ae39862SStefan Roese  */
591ae39862SStefan Roese static struct nand_ecclayout onenand_oob_32 = {
601ae39862SStefan Roese 	.eccbytes	= 10,
611ae39862SStefan Roese 	.eccpos		= {
621ae39862SStefan Roese 		8, 9, 10, 11, 12,
631ae39862SStefan Roese 		24, 25, 26, 27, 28,
641ae39862SStefan Roese 		},
651ae39862SStefan Roese 	.oobfree	= { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
661ae39862SStefan Roese };
671ae39862SStefan Roese 
6859829cc1SJean-Christophe PLAGNIOL-VILLARD static const unsigned char ffchars[] = {
6959829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
7059829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 16 */
7159829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
7259829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 32 */
7359829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
7459829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 48 */
7559829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
7659829cc1SJean-Christophe PLAGNIOL-VILLARD 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	/* 64 */
7759829cc1SJean-Christophe PLAGNIOL-VILLARD };
7859829cc1SJean-Christophe PLAGNIOL-VILLARD 
7959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
8059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_readw - [OneNAND Interface] Read OneNAND register
8159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to read
8259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
8359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read OneNAND register
8459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
8559829cc1SJean-Christophe PLAGNIOL-VILLARD static unsigned short onenand_readw(void __iomem * addr)
8659829cc1SJean-Christophe PLAGNIOL-VILLARD {
8759829cc1SJean-Christophe PLAGNIOL-VILLARD 	return readw(addr);
8859829cc1SJean-Christophe PLAGNIOL-VILLARD }
8959829cc1SJean-Christophe PLAGNIOL-VILLARD 
9059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
9159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_writew - [OneNAND Interface] Write OneNAND register with value
9259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param value		value to write
9359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to write
9459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
9559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write OneNAND register with value
9659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
9759829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_writew(unsigned short value, void __iomem * addr)
9859829cc1SJean-Christophe PLAGNIOL-VILLARD {
9959829cc1SJean-Christophe PLAGNIOL-VILLARD 	writew(value, addr);
10059829cc1SJean-Christophe PLAGNIOL-VILLARD }
10159829cc1SJean-Christophe PLAGNIOL-VILLARD 
10259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
10359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_address - [DEFAULT] Get block address
10459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
10559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
10659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		translated block address if DDP, otherwise same
10759829cc1SJean-Christophe PLAGNIOL-VILLARD  *
10859829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 1 Register (F100h)
10959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
110ef0921d6SKyungmin Park static int onenand_block_address(struct onenand_chip *this, int block)
11159829cc1SJean-Christophe PLAGNIOL-VILLARD {
11259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device Flash Core select, NAND Flash Block Address */
113ef0921d6SKyungmin Park 	if (block & this->density_mask)
114ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1 | (block ^ this->density_mask);
11559829cc1SJean-Christophe PLAGNIOL-VILLARD 
11659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return block;
11759829cc1SJean-Christophe PLAGNIOL-VILLARD }
11859829cc1SJean-Christophe PLAGNIOL-VILLARD 
11959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
12059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_address - [DEFAULT] Get bufferram address
12159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device	the device id
12259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param block		the block
12359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		set DBS value if DDP, otherwise 0
12459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
12559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 2 Register (F101h) for DDP
12659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
127ef0921d6SKyungmin Park static int onenand_bufferram_address(struct onenand_chip *this, int block)
12859829cc1SJean-Christophe PLAGNIOL-VILLARD {
12959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Device BufferRAM Select */
130ef0921d6SKyungmin Park 	if (block & this->density_mask)
131ef0921d6SKyungmin Park 		return ONENAND_DDP_CHIP1;
13259829cc1SJean-Christophe PLAGNIOL-VILLARD 
133ef0921d6SKyungmin Park 	return ONENAND_DDP_CHIP0;
13459829cc1SJean-Christophe PLAGNIOL-VILLARD }
13559829cc1SJean-Christophe PLAGNIOL-VILLARD 
13659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
13759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_page_address - [DEFAULT] Get page address
13859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param page		the page address
13959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sector	the sector address
14059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		combined page and sector address
14159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
14259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Address 8 Register (F107h)
14359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
14459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_page_address(int page, int sector)
14559829cc1SJean-Christophe PLAGNIOL-VILLARD {
14659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash Page Address, Flash Sector Address */
14759829cc1SJean-Christophe PLAGNIOL-VILLARD 	int fpa, fsa;
14859829cc1SJean-Christophe PLAGNIOL-VILLARD 
14959829cc1SJean-Christophe PLAGNIOL-VILLARD 	fpa = page & ONENAND_FPA_MASK;
15059829cc1SJean-Christophe PLAGNIOL-VILLARD 	fsa = sector & ONENAND_FSA_MASK;
15159829cc1SJean-Christophe PLAGNIOL-VILLARD 
15259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((fpa << ONENAND_FPA_SHIFT) | fsa);
15359829cc1SJean-Christophe PLAGNIOL-VILLARD }
15459829cc1SJean-Christophe PLAGNIOL-VILLARD 
15559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
15659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_buffer_address - [DEFAULT] Get buffer address
15759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param dataram1	DataRAM index
15859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param sectors	the sector address
15959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		the number of sectors
16059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		the start buffer value
16159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
16259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Setup Start Buffer Register (F200h)
16359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
16459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_buffer_address(int dataram1, int sectors, int count)
16559829cc1SJean-Christophe PLAGNIOL-VILLARD {
16659829cc1SJean-Christophe PLAGNIOL-VILLARD 	int bsa, bsc;
16759829cc1SJean-Christophe PLAGNIOL-VILLARD 
16859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Address */
16959829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsa = sectors & ONENAND_BSA_MASK;
17059829cc1SJean-Christophe PLAGNIOL-VILLARD 
17159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (dataram1)
17259829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM1;	/* DataRAM1 */
17359829cc1SJean-Christophe PLAGNIOL-VILLARD 	else
17459829cc1SJean-Christophe PLAGNIOL-VILLARD 		bsa |= ONENAND_BSA_DATARAM0;	/* DataRAM0 */
17559829cc1SJean-Christophe PLAGNIOL-VILLARD 
17659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* BufferRAM Sector Count */
17759829cc1SJean-Christophe PLAGNIOL-VILLARD 	bsc = count & ONENAND_BSC_MASK;
17859829cc1SJean-Christophe PLAGNIOL-VILLARD 
17959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ((bsa << ONENAND_BSA_SHIFT) | bsc);
18059829cc1SJean-Christophe PLAGNIOL-VILLARD }
18159829cc1SJean-Christophe PLAGNIOL-VILLARD 
18259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
183ef0921d6SKyungmin Park  * onenand_get_density - [DEFAULT] Get OneNAND density
184ef0921d6SKyungmin Park  * @param dev_id        OneNAND device ID
185ef0921d6SKyungmin Park  *
186ef0921d6SKyungmin Park  * Get OneNAND density from device ID
187ef0921d6SKyungmin Park  */
188ef0921d6SKyungmin Park static inline int onenand_get_density(int dev_id)
189ef0921d6SKyungmin Park {
190ef0921d6SKyungmin Park 	int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
191ef0921d6SKyungmin Park 	return (density & ONENAND_DEVICE_DENSITY_MASK);
192ef0921d6SKyungmin Park }
193ef0921d6SKyungmin Park 
194ef0921d6SKyungmin Park /**
19559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_command - [DEFAULT] Send command to OneNAND device
19659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
19759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param cmd		the command to be sent
19859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		offset to read from or write to
19959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read or write
20059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
20159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Send command to OneNAND device. This function is used for middle/large page
20259829cc1SJean-Christophe PLAGNIOL-VILLARD  * devices (1KB/2KB Bytes per page)
20359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
20459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
20559829cc1SJean-Christophe PLAGNIOL-VILLARD 			   size_t len)
20659829cc1SJean-Christophe PLAGNIOL-VILLARD {
20759829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
20859829cc1SJean-Christophe PLAGNIOL-VILLARD 	int value, readcmd = 0;
20959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int block, page;
21059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Now we use page size operation */
21159829cc1SJean-Christophe PLAGNIOL-VILLARD 	int sectors = 4, count = 4;
21259829cc1SJean-Christophe PLAGNIOL-VILLARD 
21359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Address translation */
21459829cc1SJean-Christophe PLAGNIOL-VILLARD 	switch (cmd) {
21559829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_UNLOCK:
21659829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK:
21759829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_LOCK_TIGHT:
218ef0921d6SKyungmin Park 	case ONENAND_CMD_UNLOCK_ALL:
21959829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = -1;
22059829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
22159829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
22259829cc1SJean-Christophe PLAGNIOL-VILLARD 
22359829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_ERASE:
22459829cc1SJean-Christophe PLAGNIOL-VILLARD 	case ONENAND_CMD_BUFFERRAM:
22559829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = (int)(addr >> this->erase_shift);
22659829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = -1;
22759829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
22859829cc1SJean-Christophe PLAGNIOL-VILLARD 
22959829cc1SJean-Christophe PLAGNIOL-VILLARD 	default:
23059829cc1SJean-Christophe PLAGNIOL-VILLARD 		block = (int)(addr >> this->erase_shift);
23159829cc1SJean-Christophe PLAGNIOL-VILLARD 		page = (int)(addr >> this->page_shift);
23259829cc1SJean-Christophe PLAGNIOL-VILLARD 		page &= this->page_mask;
23359829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
23459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
23559829cc1SJean-Christophe PLAGNIOL-VILLARD 
23659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* NOTE: The setting order of the registers is very important! */
23759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (cmd == ONENAND_CMD_BUFFERRAM) {
23859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Select DataRAM for DDP */
239ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
24059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
24159829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS2);
24259829cc1SJean-Christophe PLAGNIOL-VILLARD 
24359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Switch to the next data buffer */
24459829cc1SJean-Christophe PLAGNIOL-VILLARD 		ONENAND_SET_NEXT_BUFFERRAM(this);
24559829cc1SJean-Christophe PLAGNIOL-VILLARD 
24659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
24759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
24859829cc1SJean-Christophe PLAGNIOL-VILLARD 
24959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (block != -1) {
25059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'DFS, FBA' of Flash */
251ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
25259829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
25359829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS1);
254ef0921d6SKyungmin Park 
255ef0921d6SKyungmin Park 		/* Write 'DFS, FBA' of Flash */
256ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
257ef0921d6SKyungmin Park 		this->write_word(value,
258ef0921d6SKyungmin Park 				 this->base + ONENAND_REG_START_ADDRESS2);
25959829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
26059829cc1SJean-Christophe PLAGNIOL-VILLARD 
26159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (page != -1) {
26259829cc1SJean-Christophe PLAGNIOL-VILLARD 		int dataram;
26359829cc1SJean-Christophe PLAGNIOL-VILLARD 
26459829cc1SJean-Christophe PLAGNIOL-VILLARD 		switch (cmd) {
26559829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READ:
26659829cc1SJean-Christophe PLAGNIOL-VILLARD 		case ONENAND_CMD_READOOB:
26759829cc1SJean-Christophe PLAGNIOL-VILLARD 			dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
26859829cc1SJean-Christophe PLAGNIOL-VILLARD 			readcmd = 1;
26959829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
27059829cc1SJean-Christophe PLAGNIOL-VILLARD 
27159829cc1SJean-Christophe PLAGNIOL-VILLARD 		default:
27259829cc1SJean-Christophe PLAGNIOL-VILLARD 			dataram = ONENAND_CURRENT_BUFFERRAM(this);
27359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
27459829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
27559829cc1SJean-Christophe PLAGNIOL-VILLARD 
27659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'FPA, FSA' of Flash */
27759829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_page_address(page, sectors);
27859829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value,
27959829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_ADDRESS8);
28059829cc1SJean-Christophe PLAGNIOL-VILLARD 
28159829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write 'BSA, BSC' of DataRAM */
28259829cc1SJean-Christophe PLAGNIOL-VILLARD 		value = onenand_buffer_address(dataram, sectors, count);
28359829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
28459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
28559829cc1SJean-Christophe PLAGNIOL-VILLARD 
28659829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Interrupt clear */
28759829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT);
28859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Write command */
28959829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(cmd, this->base + ONENAND_REG_COMMAND);
29059829cc1SJean-Christophe PLAGNIOL-VILLARD 
29159829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
29259829cc1SJean-Christophe PLAGNIOL-VILLARD }
29359829cc1SJean-Christophe PLAGNIOL-VILLARD 
29459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
29559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_wait - [DEFAULT] wait until the command is done
29659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
29759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param state		state to select the max. timeout value
29859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
29959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Wait for command done. This applies to all OneNAND command
30059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read can take up to 30us, erase up to 2ms and program up to 350us
30159829cc1SJean-Christophe PLAGNIOL-VILLARD  * according to general OneNAND specs
30259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
30359829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_wait(struct mtd_info *mtd, int state)
30459829cc1SJean-Christophe PLAGNIOL-VILLARD {
30559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
30659829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int flags = ONENAND_INT_MASTER;
30759829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int interrupt = 0;
30859829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int ctrl, ecc;
30959829cc1SJean-Christophe PLAGNIOL-VILLARD 
31059829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (1) {
31159829cc1SJean-Christophe PLAGNIOL-VILLARD 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
31259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (interrupt & flags)
31359829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
31459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
31559829cc1SJean-Christophe PLAGNIOL-VILLARD 
31659829cc1SJean-Christophe PLAGNIOL-VILLARD 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
31759829cc1SJean-Christophe PLAGNIOL-VILLARD 
31859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ctrl & ONENAND_CTRL_ERROR) {
319ef0921d6SKyungmin Park 		printk("onenand_wait: controller error = 0x%04x\n", ctrl);
320ef0921d6SKyungmin Park 		if (ctrl & ONENAND_CTRL_LOCK)
321ef0921d6SKyungmin Park 			printk("onenand_wait: it's locked error = 0x%04x\n",
322ef0921d6SKyungmin Park 				ctrl);
32359829cc1SJean-Christophe PLAGNIOL-VILLARD 
32459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EIO;
32559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
32659829cc1SJean-Christophe PLAGNIOL-VILLARD 
32759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (interrupt & ONENAND_INT_READ) {
32859829cc1SJean-Christophe PLAGNIOL-VILLARD 		ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
32959829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ecc & ONENAND_ECC_2BIT_ALL) {
3303167c538SScott Wood 			MTDDEBUG (MTD_DEBUG_LEVEL0,
33159829cc1SJean-Christophe PLAGNIOL-VILLARD 				  "onenand_wait: ECC error = 0x%04x\n", ecc);
33259829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EBADMSG;
33359829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
33459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
33559829cc1SJean-Christophe PLAGNIOL-VILLARD 
33659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
33759829cc1SJean-Christophe PLAGNIOL-VILLARD }
33859829cc1SJean-Christophe PLAGNIOL-VILLARD 
33959829cc1SJean-Christophe PLAGNIOL-VILLARD /**
34059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_bufferram_offset - [DEFAULT] BufferRAM offset
34159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
34259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
34359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		offset given area
34459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
34559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Return BufferRAM offset given area
34659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
34759829cc1SJean-Christophe PLAGNIOL-VILLARD static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area)
34859829cc1SJean-Christophe PLAGNIOL-VILLARD {
34959829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
35059829cc1SJean-Christophe PLAGNIOL-VILLARD 
35159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (ONENAND_CURRENT_BUFFERRAM(this)) {
35259829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_DATARAM)
353d438d508SKyungmin Park 			return mtd->writesize;
35459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (area == ONENAND_SPARERAM)
35559829cc1SJean-Christophe PLAGNIOL-VILLARD 			return mtd->oobsize;
35659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
35759829cc1SJean-Christophe PLAGNIOL-VILLARD 
35859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
35959829cc1SJean-Christophe PLAGNIOL-VILLARD }
36059829cc1SJean-Christophe PLAGNIOL-VILLARD 
36159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
36259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area
36359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
36459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
36559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
36659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
36759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
36859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
36959829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area
37059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
371ef0921d6SKyungmin Park static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
37259829cc1SJean-Christophe PLAGNIOL-VILLARD 				  unsigned char *buffer, int offset,
37359829cc1SJean-Christophe PLAGNIOL-VILLARD 				  size_t count)
37459829cc1SJean-Christophe PLAGNIOL-VILLARD {
37559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
37659829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
37759829cc1SJean-Christophe PLAGNIOL-VILLARD 
37859829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
37959829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
38059829cc1SJean-Christophe PLAGNIOL-VILLARD 
381d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
38259829cc1SJean-Christophe PLAGNIOL-VILLARD 
38359829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
38459829cc1SJean-Christophe PLAGNIOL-VILLARD }
38559829cc1SJean-Christophe PLAGNIOL-VILLARD 
38659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
38759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
38859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
38959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
39059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
39159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
39259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
39359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
39459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Read the BufferRAM area with Sync. Burst Mode
39559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
396ef0921d6SKyungmin Park static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area,
39759829cc1SJean-Christophe PLAGNIOL-VILLARD 				       unsigned char *buffer, int offset,
39859829cc1SJean-Christophe PLAGNIOL-VILLARD 				       size_t count)
39959829cc1SJean-Christophe PLAGNIOL-VILLARD {
40059829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
40159829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
40259829cc1SJean-Christophe PLAGNIOL-VILLARD 
40359829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
40459829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
40559829cc1SJean-Christophe PLAGNIOL-VILLARD 
40659829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
40759829cc1SJean-Christophe PLAGNIOL-VILLARD 
408d2c6fbecSWolfgang Denk 	memcpy_16(buffer, bufferram + offset, count);
40959829cc1SJean-Christophe PLAGNIOL-VILLARD 
41059829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->mmcontrol(mtd, 0);
41159829cc1SJean-Christophe PLAGNIOL-VILLARD 
41259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
41359829cc1SJean-Christophe PLAGNIOL-VILLARD }
41459829cc1SJean-Christophe PLAGNIOL-VILLARD 
41559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
41659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
41759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
41859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param area		BufferRAM area
41959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buffer	the databuffer to put/get data
42059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param offset	offset to read from or write to
42159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param count		number of bytes to read/write
42259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
42359829cc1SJean-Christophe PLAGNIOL-VILLARD  * Write the BufferRAM area
42459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
425ef0921d6SKyungmin Park static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area,
42659829cc1SJean-Christophe PLAGNIOL-VILLARD 				   const unsigned char *buffer, int offset,
42759829cc1SJean-Christophe PLAGNIOL-VILLARD 				   size_t count)
42859829cc1SJean-Christophe PLAGNIOL-VILLARD {
42959829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
43059829cc1SJean-Christophe PLAGNIOL-VILLARD 	void __iomem *bufferram;
43159829cc1SJean-Christophe PLAGNIOL-VILLARD 
43259829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram = this->base + area;
43359829cc1SJean-Christophe PLAGNIOL-VILLARD 	bufferram += onenand_bufferram_offset(mtd, area);
43459829cc1SJean-Christophe PLAGNIOL-VILLARD 
435d2c6fbecSWolfgang Denk 	memcpy_16(bufferram + offset, buffer, count);
43659829cc1SJean-Christophe PLAGNIOL-VILLARD 
43759829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
43859829cc1SJean-Christophe PLAGNIOL-VILLARD }
43959829cc1SJean-Christophe PLAGNIOL-VILLARD 
44059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
4414fca3310SStefan Roese  * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode
4424fca3310SStefan Roese  * @param mtd		MTD data structure
4434fca3310SStefan Roese  * @param addr		address to check
4444fca3310SStefan Roese  * @return		blockpage address
4454fca3310SStefan Roese  *
4464fca3310SStefan Roese  * Get blockpage address at 2x program mode
4474fca3310SStefan Roese  */
4484fca3310SStefan Roese static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr)
4494fca3310SStefan Roese {
4504fca3310SStefan Roese 	struct onenand_chip *this = mtd->priv;
4514fca3310SStefan Roese 	int blockpage, block, page;
4524fca3310SStefan Roese 
4534fca3310SStefan Roese 	/* Calculate the even block number */
4544fca3310SStefan Roese 	block = (int) (addr >> this->erase_shift) & ~1;
4554fca3310SStefan Roese 	/* Is it the odd plane? */
4564fca3310SStefan Roese 	if (addr & this->writesize)
4574fca3310SStefan Roese 		block++;
4584fca3310SStefan Roese 	page = (int) (addr >> (this->page_shift + 1)) & this->page_mask;
4594fca3310SStefan Roese 	blockpage = (block << 7) | page;
4604fca3310SStefan Roese 
4614fca3310SStefan Roese 	return blockpage;
4624fca3310SStefan Roese }
4634fca3310SStefan Roese 
4644fca3310SStefan Roese /**
46559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_bufferram - [GENERIC] Check BufferRAM information
46659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
46759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to check
46859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @return		1 if there are valid data, otherwise 0
46959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
47059829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check bufferram if there is data we required
47159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
47259829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
47359829cc1SJean-Christophe PLAGNIOL-VILLARD {
47459829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
475ef0921d6SKyungmin Park 	int blockpage, found = 0;
476ef0921d6SKyungmin Park 	unsigned int i;
47759829cc1SJean-Christophe PLAGNIOL-VILLARD 
478ef0921d6SKyungmin Park #ifdef CONFIG_S3C64XX
479ef0921d6SKyungmin Park 	return 0;
480ef0921d6SKyungmin Park #endif
48159829cc1SJean-Christophe PLAGNIOL-VILLARD 
482ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
483ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
484ef0921d6SKyungmin Park 	else
485ef0921d6SKyungmin Park 		blockpage = (int) (addr >> this->page_shift);
48659829cc1SJean-Christophe PLAGNIOL-VILLARD 
48759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Is there valid data? */
488ef0921d6SKyungmin Park 	i = ONENAND_CURRENT_BUFFERRAM(this);
489ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
490ef0921d6SKyungmin Park 		found = 1;
491ef0921d6SKyungmin Park 	else {
492ef0921d6SKyungmin Park 		/* Check another BufferRAM */
493ef0921d6SKyungmin Park 		i = ONENAND_NEXT_BUFFERRAM(this);
494ef0921d6SKyungmin Park 		if (this->bufferram[i].blockpage == blockpage) {
495ef0921d6SKyungmin Park 			ONENAND_SET_NEXT_BUFFERRAM(this);
496ef0921d6SKyungmin Park 			found = 1;
497ef0921d6SKyungmin Park 		}
498ef0921d6SKyungmin Park 	}
49959829cc1SJean-Christophe PLAGNIOL-VILLARD 
500ef0921d6SKyungmin Park 	if (found && ONENAND_IS_DDP(this)) {
501ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
502ef0921d6SKyungmin Park 		int block = (int) (addr >> this->erase_shift);
503ef0921d6SKyungmin Park 		int value = onenand_bufferram_address(this, block);
504ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
505ef0921d6SKyungmin Park 	}
506ef0921d6SKyungmin Park 
507ef0921d6SKyungmin Park 	return found;
50859829cc1SJean-Christophe PLAGNIOL-VILLARD }
50959829cc1SJean-Christophe PLAGNIOL-VILLARD 
51059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
51159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_update_bufferram - [GENERIC] Update BufferRAM information
51259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD data structure
51359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param addr		address to update
51459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param valid		valid flag
51559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
51659829cc1SJean-Christophe PLAGNIOL-VILLARD  * Update BufferRAM information
51759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
51859829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr,
51959829cc1SJean-Christophe PLAGNIOL-VILLARD 				    int valid)
52059829cc1SJean-Christophe PLAGNIOL-VILLARD {
52159829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
522ef0921d6SKyungmin Park 	int blockpage;
523ef0921d6SKyungmin Park 	unsigned int i;
52459829cc1SJean-Christophe PLAGNIOL-VILLARD 
525ef0921d6SKyungmin Park 	if (ONENAND_IS_2PLANE(this))
526ef0921d6SKyungmin Park 		blockpage = onenand_get_2x_blockpage(mtd, addr);
527ef0921d6SKyungmin Park 	else
528ef0921d6SKyungmin Park 		blockpage = (int)(addr >> this->page_shift);
52959829cc1SJean-Christophe PLAGNIOL-VILLARD 
530ef0921d6SKyungmin Park 	/* Invalidate another BufferRAM */
531ef0921d6SKyungmin Park 	i = ONENAND_NEXT_BUFFERRAM(this);
532ef0921d6SKyungmin Park 	if (this->bufferram[i].blockpage == blockpage)
533ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
53459829cc1SJean-Christophe PLAGNIOL-VILLARD 
53559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Update BufferRAM */
53659829cc1SJean-Christophe PLAGNIOL-VILLARD 	i = ONENAND_CURRENT_BUFFERRAM(this);
537ef0921d6SKyungmin Park 	if (valid)
538ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = blockpage;
539ef0921d6SKyungmin Park 	else
540ef0921d6SKyungmin Park 		this->bufferram[i].blockpage = -1;
54159829cc1SJean-Christophe PLAGNIOL-VILLARD 
54259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
54359829cc1SJean-Christophe PLAGNIOL-VILLARD }
54459829cc1SJean-Christophe PLAGNIOL-VILLARD 
54559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
546d438d508SKyungmin Park  * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information
547d438d508SKyungmin Park  * @param mtd           MTD data structure
548d438d508SKyungmin Park  * @param addr          start address to invalidate
549d438d508SKyungmin Park  * @param len           length to invalidate
550d438d508SKyungmin Park  *
551d438d508SKyungmin Park  * Invalidate BufferRAM information
552d438d508SKyungmin Park  */
553d438d508SKyungmin Park static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr,
554d438d508SKyungmin Park 					 unsigned int len)
555d438d508SKyungmin Park {
556d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
557d438d508SKyungmin Park 	int i;
558d438d508SKyungmin Park 	loff_t end_addr = addr + len;
559d438d508SKyungmin Park 
560d438d508SKyungmin Park 	/* Invalidate BufferRAM */
561d438d508SKyungmin Park 	for (i = 0; i < MAX_BUFFERRAM; i++) {
562ef0921d6SKyungmin Park 		loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift;
563d438d508SKyungmin Park 
564d438d508SKyungmin Park 		if (buf_addr >= addr && buf_addr < end_addr)
565ef0921d6SKyungmin Park 			this->bufferram[i].blockpage = -1;
566d438d508SKyungmin Park 	}
567d438d508SKyungmin Park }
568d438d508SKyungmin Park 
569d438d508SKyungmin Park /**
57059829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_get_device - [GENERIC] Get chip for selected access
57159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
57259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param new_state	the state which is requested
57359829cc1SJean-Christophe PLAGNIOL-VILLARD  *
57459829cc1SJean-Christophe PLAGNIOL-VILLARD  * Get the device and lock it for exclusive access
57559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
57659829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_get_device(struct mtd_info *mtd, int new_state)
57759829cc1SJean-Christophe PLAGNIOL-VILLARD {
57859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
57959829cc1SJean-Christophe PLAGNIOL-VILLARD }
58059829cc1SJean-Christophe PLAGNIOL-VILLARD 
58159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
58259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release_device - [GENERIC] release chip
58359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
58459829cc1SJean-Christophe PLAGNIOL-VILLARD  *
58559829cc1SJean-Christophe PLAGNIOL-VILLARD  * Deselect, release chip lock and wake up anyone waiting on the device
58659829cc1SJean-Christophe PLAGNIOL-VILLARD  */
58759829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_release_device(struct mtd_info *mtd)
58859829cc1SJean-Christophe PLAGNIOL-VILLARD {
58959829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do nothing */
59059829cc1SJean-Christophe PLAGNIOL-VILLARD }
59159829cc1SJean-Christophe PLAGNIOL-VILLARD 
59259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
593bfd7f386SKyungmin Park  * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
59459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
595bfd7f386SKyungmin Park  * @param buf		destination address
596bfd7f386SKyungmin Park  * @param column	oob offset to read from
597bfd7f386SKyungmin Park  * @param thislen	oob length to read
59859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
599bfd7f386SKyungmin Park static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
600bfd7f386SKyungmin Park 					int column, int thislen)
60159829cc1SJean-Christophe PLAGNIOL-VILLARD {
60259829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
603bfd7f386SKyungmin Park 	struct nand_oobfree *free;
604bfd7f386SKyungmin Park 	int readcol = column;
605bfd7f386SKyungmin Park 	int readend = column + thislen;
606bfd7f386SKyungmin Park 	int lastgap = 0;
607bfd7f386SKyungmin Park 	unsigned int i;
608bfd7f386SKyungmin Park 	uint8_t *oob_buf = this->oob_buf;
60959829cc1SJean-Christophe PLAGNIOL-VILLARD 
610bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
611bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
612bfd7f386SKyungmin Park 		if (readcol >= lastgap)
613bfd7f386SKyungmin Park 			readcol += free->offset - lastgap;
614bfd7f386SKyungmin Park 		if (readend >= lastgap)
615bfd7f386SKyungmin Park 			readend += free->offset - lastgap;
616bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
617bfd7f386SKyungmin Park 	}
618ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
619bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
620bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
621bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
622bfd7f386SKyungmin Park 		if (free->offset < readend && free_end > readcol) {
623bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,readcol);
624bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,readend);
625bfd7f386SKyungmin Park 			int n = ed - st;
626bfd7f386SKyungmin Park 			memcpy(buf, oob_buf + st, n);
627bfd7f386SKyungmin Park 			buf += n;
628bfd7f386SKyungmin Park 		} else if (column == 0)
629bfd7f386SKyungmin Park 			break;
630bfd7f386SKyungmin Park 	}
631bfd7f386SKyungmin Park 	return 0;
632bfd7f386SKyungmin Park }
633bfd7f386SKyungmin Park 
634bfd7f386SKyungmin Park /**
635bfd7f386SKyungmin Park  * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
636bfd7f386SKyungmin Park  * @param mtd		MTD device structure
637bfd7f386SKyungmin Park  * @param from		offset to read from
638bfd7f386SKyungmin Park  * @param ops		oob operation description structure
639bfd7f386SKyungmin Park  *
640bfd7f386SKyungmin Park  * OneNAND read main and/or out-of-band data
641bfd7f386SKyungmin Park  */
642bfd7f386SKyungmin Park static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
643bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
644bfd7f386SKyungmin Park {
645bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
646bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
647bfd7f386SKyungmin Park 	size_t len = ops->len;
648bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
649bfd7f386SKyungmin Park 	u_char *buf = ops->datbuf;
650bfd7f386SKyungmin Park 	u_char *oobbuf = ops->oobbuf;
651bfd7f386SKyungmin Park 	int read = 0, column, thislen;
652bfd7f386SKyungmin Park 	int oobread = 0, oobcolumn, thisooblen, oobsize;
653bfd7f386SKyungmin Park 	int ret = 0, boundary = 0;
654bfd7f386SKyungmin Park 	int writesize = this->writesize;
655bfd7f386SKyungmin Park 
656ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
65759829cc1SJean-Christophe PLAGNIOL-VILLARD 
658bfd7f386SKyungmin Park 	if (ops->mode == MTD_OOB_AUTO)
659bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
660bfd7f386SKyungmin Park 	else
661bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
662bfd7f386SKyungmin Park 
663bfd7f386SKyungmin Park 	oobcolumn = from & (mtd->oobsize - 1);
664bfd7f386SKyungmin Park 
66559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
66659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if ((from + len) > mtd->size) {
667bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n");
668bfd7f386SKyungmin Park 		ops->retlen = 0;
669bfd7f386SKyungmin Park 		ops->oobretlen = 0;
67059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
67159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
67259829cc1SJean-Christophe PLAGNIOL-VILLARD 
673bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
67459829cc1SJean-Christophe PLAGNIOL-VILLARD 
675bfd7f386SKyungmin Park 	/* Read-while-load method */
67659829cc1SJean-Christophe PLAGNIOL-VILLARD 
677bfd7f386SKyungmin Park 	/* Do first load to bufferRAM */
678bfd7f386SKyungmin Park 	if (read < len) {
67959829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!onenand_check_bufferram(mtd, from)) {
680ef0921d6SKyungmin Park 			this->main_buf = buf;
681bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
68259829cc1SJean-Christophe PLAGNIOL-VILLARD 			ret = this->wait(mtd, FL_READING);
683bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, from, !ret);
684bfd7f386SKyungmin Park 			if (ret == -EBADMSG)
685bfd7f386SKyungmin Park 				ret = 0;
686bfd7f386SKyungmin Park 		}
68759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
68859829cc1SJean-Christophe PLAGNIOL-VILLARD 
689bfd7f386SKyungmin Park 	thislen = min_t(int, writesize, len - read);
690bfd7f386SKyungmin Park 	column = from & (writesize - 1);
691bfd7f386SKyungmin Park 	if (column + thislen > writesize)
692bfd7f386SKyungmin Park 		thislen = writesize - column;
69359829cc1SJean-Christophe PLAGNIOL-VILLARD 
694bfd7f386SKyungmin Park 	while (!ret) {
695bfd7f386SKyungmin Park 		/* If there is more to load then start next load */
696bfd7f386SKyungmin Park 		from += thislen;
697bfd7f386SKyungmin Park 		if (read + thislen < len) {
698ef0921d6SKyungmin Park 			this->main_buf = buf + thislen;
699bfd7f386SKyungmin Park 			this->command(mtd, ONENAND_CMD_READ, from, writesize);
700bfd7f386SKyungmin Park 			/*
701bfd7f386SKyungmin Park 			 * Chip boundary handling in DDP
702bfd7f386SKyungmin Park 			 * Now we issued chip 1 read and pointed chip 1
703bfd7f386SKyungmin Park 			 * bufferam so we have to point chip 0 bufferam.
704bfd7f386SKyungmin Park 			 */
705bfd7f386SKyungmin Park 			if (ONENAND_IS_DDP(this) &&
706bfd7f386SKyungmin Park 					unlikely(from == (this->chipsize >> 1))) {
707bfd7f386SKyungmin Park 				this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2);
708bfd7f386SKyungmin Park 				boundary = 1;
709bfd7f386SKyungmin Park 			} else
710bfd7f386SKyungmin Park 				boundary = 0;
711bfd7f386SKyungmin Park 			ONENAND_SET_PREV_BUFFERRAM(this);
712bfd7f386SKyungmin Park 		}
713bfd7f386SKyungmin Park 
714bfd7f386SKyungmin Park 		/* While load is going, read from last bufferRAM */
715ef0921d6SKyungmin Park 		this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen);
716bfd7f386SKyungmin Park 
717bfd7f386SKyungmin Park 		/* Read oob area if needed */
718bfd7f386SKyungmin Park 		if (oobbuf) {
719bfd7f386SKyungmin Park 			thisooblen = oobsize - oobcolumn;
720bfd7f386SKyungmin Park 			thisooblen = min_t(int, thisooblen, ooblen - oobread);
721bfd7f386SKyungmin Park 
722bfd7f386SKyungmin Park 			if (ops->mode == MTD_OOB_AUTO)
723bfd7f386SKyungmin Park 				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
724bfd7f386SKyungmin Park 			else
725ef0921d6SKyungmin Park 				this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
726bfd7f386SKyungmin Park 			oobread += thisooblen;
727bfd7f386SKyungmin Park 			oobbuf += thisooblen;
728bfd7f386SKyungmin Park 			oobcolumn = 0;
729bfd7f386SKyungmin Park 		}
730bfd7f386SKyungmin Park 
731bfd7f386SKyungmin Park 		/* See if we are done */
73259829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
73359829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
73459829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
735bfd7f386SKyungmin Park 		/* Set up for next read from bufferRAM */
736bfd7f386SKyungmin Park 		if (unlikely(boundary))
737bfd7f386SKyungmin Park 			this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
738bfd7f386SKyungmin Park 		ONENAND_SET_NEXT_BUFFERRAM(this);
73959829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
740bfd7f386SKyungmin Park 		thislen = min_t(int, writesize, len - read);
741bfd7f386SKyungmin Park 		column = 0;
74259829cc1SJean-Christophe PLAGNIOL-VILLARD 
743bfd7f386SKyungmin Park 		/* Now wait for load */
744bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_READING);
745bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, from, !ret);
746bfd7f386SKyungmin Park 		if (ret == -EBADMSG)
747bfd7f386SKyungmin Park 			ret = 0;
748bfd7f386SKyungmin Park 	}
74959829cc1SJean-Christophe PLAGNIOL-VILLARD 
75059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/*
75159829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * Return success, if no ECC failures, else -EBADMSG
75259829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * fs driver will take care of that, because
75359829cc1SJean-Christophe PLAGNIOL-VILLARD 	 * retlen == desired len and result == -EBADMSG
75459829cc1SJean-Christophe PLAGNIOL-VILLARD 	 */
755bfd7f386SKyungmin Park 	ops->retlen = read;
756bfd7f386SKyungmin Park 	ops->oobretlen = oobread;
757bfd7f386SKyungmin Park 
758bfd7f386SKyungmin Park 	if (ret)
75959829cc1SJean-Christophe PLAGNIOL-VILLARD 		return ret;
760bfd7f386SKyungmin Park 
761bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
762bfd7f386SKyungmin Park 		return -EBADMSG;
763bfd7f386SKyungmin Park 
764bfd7f386SKyungmin Park 	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
765bfd7f386SKyungmin Park }
766bfd7f386SKyungmin Park 
767bfd7f386SKyungmin Park /**
768bfd7f386SKyungmin Park  * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band
769bfd7f386SKyungmin Park  * @param mtd		MTD device structure
770bfd7f386SKyungmin Park  * @param from		offset to read from
771bfd7f386SKyungmin Park  * @param ops		oob operation description structure
772bfd7f386SKyungmin Park  *
773bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area
774bfd7f386SKyungmin Park  */
775bfd7f386SKyungmin Park static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
776bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
777bfd7f386SKyungmin Park {
778bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
779bfd7f386SKyungmin Park 	struct mtd_ecc_stats stats;
780bfd7f386SKyungmin Park 	int read = 0, thislen, column, oobsize;
781bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
782bfd7f386SKyungmin Park 	mtd_oob_mode_t mode = ops->mode;
783bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
784bfd7f386SKyungmin Park 	int ret = 0;
785bfd7f386SKyungmin Park 
786bfd7f386SKyungmin Park 	from += ops->ooboffs;
787bfd7f386SKyungmin Park 
788ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
789bfd7f386SKyungmin Park 
790bfd7f386SKyungmin Park 	/* Initialize return length value */
791bfd7f386SKyungmin Park 	ops->oobretlen = 0;
792bfd7f386SKyungmin Park 
793bfd7f386SKyungmin Park 	if (mode == MTD_OOB_AUTO)
794bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
795bfd7f386SKyungmin Park 	else
796bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
797bfd7f386SKyungmin Park 
798bfd7f386SKyungmin Park 	column = from & (mtd->oobsize - 1);
799bfd7f386SKyungmin Park 
800bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
801bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n");
802bfd7f386SKyungmin Park 		return -EINVAL;
803bfd7f386SKyungmin Park 	}
804bfd7f386SKyungmin Park 
805bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
806bfd7f386SKyungmin Park 	if (unlikely(from >= mtd->size ||
807bfd7f386SKyungmin Park 		column + len > ((mtd->size >> this->page_shift) -
808bfd7f386SKyungmin Park 				(from >> this->page_shift)) * oobsize)) {
809bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n");
810bfd7f386SKyungmin Park 		return -EINVAL;
811bfd7f386SKyungmin Park 	}
812bfd7f386SKyungmin Park 
813bfd7f386SKyungmin Park 	stats = mtd->ecc_stats;
814bfd7f386SKyungmin Park 
815bfd7f386SKyungmin Park 	while (read < len) {
816bfd7f386SKyungmin Park 		thislen = oobsize - column;
817bfd7f386SKyungmin Park 		thislen = min_t(int, thislen, len);
818bfd7f386SKyungmin Park 
819ef0921d6SKyungmin Park 		this->spare_buf = buf;
820bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
821bfd7f386SKyungmin Park 
822bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, from, 0);
823bfd7f386SKyungmin Park 
824bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_READING);
825bfd7f386SKyungmin Park 		if (ret && ret != -EBADMSG) {
826bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
827bfd7f386SKyungmin Park 			break;
828bfd7f386SKyungmin Park 		}
829bfd7f386SKyungmin Park 
830bfd7f386SKyungmin Park 		if (mode == MTD_OOB_AUTO)
831bfd7f386SKyungmin Park 			onenand_transfer_auto_oob(mtd, buf, column, thislen);
832bfd7f386SKyungmin Park 		else
833ef0921d6SKyungmin Park 			this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
834bfd7f386SKyungmin Park 
835bfd7f386SKyungmin Park 		read += thislen;
836bfd7f386SKyungmin Park 
837bfd7f386SKyungmin Park 		if (read == len)
838bfd7f386SKyungmin Park 			break;
839bfd7f386SKyungmin Park 
840bfd7f386SKyungmin Park 		buf += thislen;
841bfd7f386SKyungmin Park 
842bfd7f386SKyungmin Park 		/* Read more? */
843bfd7f386SKyungmin Park 		if (read < len) {
844bfd7f386SKyungmin Park 			/* Page size */
845bfd7f386SKyungmin Park 			from += mtd->writesize;
846bfd7f386SKyungmin Park 			column = 0;
847bfd7f386SKyungmin Park 		}
848bfd7f386SKyungmin Park 	}
849bfd7f386SKyungmin Park 
850bfd7f386SKyungmin Park 	ops->oobretlen = read;
851bfd7f386SKyungmin Park 
852bfd7f386SKyungmin Park 	if (ret)
853bfd7f386SKyungmin Park 		return ret;
854bfd7f386SKyungmin Park 
855bfd7f386SKyungmin Park 	if (mtd->ecc_stats.failed - stats.failed)
856bfd7f386SKyungmin Park 		return -EBADMSG;
857bfd7f386SKyungmin Park 
858bfd7f386SKyungmin Park 	return 0;
85959829cc1SJean-Christophe PLAGNIOL-VILLARD }
86059829cc1SJean-Christophe PLAGNIOL-VILLARD 
86159829cc1SJean-Christophe PLAGNIOL-VILLARD /**
86259829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc
86359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
86459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
86559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to read
86659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of read bytes
86759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the databuffer to put data
86859829cc1SJean-Christophe PLAGNIOL-VILLARD  *
86959829cc1SJean-Christophe PLAGNIOL-VILLARD  * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL
87059829cc1SJean-Christophe PLAGNIOL-VILLARD */
87159829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
87259829cc1SJean-Christophe PLAGNIOL-VILLARD 		 size_t * retlen, u_char * buf)
87359829cc1SJean-Christophe PLAGNIOL-VILLARD {
874bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
875bfd7f386SKyungmin Park 		.len    = len,
876bfd7f386SKyungmin Park 		.ooblen = 0,
877bfd7f386SKyungmin Park 		.datbuf = buf,
878bfd7f386SKyungmin Park 		.oobbuf = NULL,
879bfd7f386SKyungmin Park 	};
880bfd7f386SKyungmin Park 	int ret;
881bfd7f386SKyungmin Park 
882bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
883bfd7f386SKyungmin Park 	ret = onenand_read_ops_nolock(mtd, from, &ops);
884bfd7f386SKyungmin Park 	onenand_release_device(mtd);
885bfd7f386SKyungmin Park 
886bfd7f386SKyungmin Park 	*retlen = ops.retlen;
887bfd7f386SKyungmin Park 	return ret;
88859829cc1SJean-Christophe PLAGNIOL-VILLARD }
88959829cc1SJean-Christophe PLAGNIOL-VILLARD 
89059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
89159829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_read_oob - [MTD Interface] OneNAND read out-of-band
89259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
89359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param from		offset to read from
894bfd7f386SKyungmin Park  * @param ops		oob operations description structure
89559829cc1SJean-Christophe PLAGNIOL-VILLARD  *
896bfd7f386SKyungmin Park  * OneNAND main and/or out-of-band
89759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
898bfd7f386SKyungmin Park int onenand_read_oob(struct mtd_info *mtd, loff_t from,
899bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
900bfd7f386SKyungmin Park {
901bfd7f386SKyungmin Park 	int ret;
902bfd7f386SKyungmin Park 
903bfd7f386SKyungmin Park 	switch (ops->mode) {
904bfd7f386SKyungmin Park 	case MTD_OOB_PLACE:
905bfd7f386SKyungmin Park 	case MTD_OOB_AUTO:
906bfd7f386SKyungmin Park 		break;
907bfd7f386SKyungmin Park 	case MTD_OOB_RAW:
908bfd7f386SKyungmin Park 		/* Not implemented yet */
909bfd7f386SKyungmin Park 	default:
910bfd7f386SKyungmin Park 		return -EINVAL;
911bfd7f386SKyungmin Park 	}
912bfd7f386SKyungmin Park 
913bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_READING);
914bfd7f386SKyungmin Park 	if (ops->datbuf)
915bfd7f386SKyungmin Park 		ret = onenand_read_ops_nolock(mtd, from, ops);
916bfd7f386SKyungmin Park 	else
917bfd7f386SKyungmin Park 		ret = onenand_read_oob_nolock(mtd, from, ops);
918bfd7f386SKyungmin Park 	onenand_release_device(mtd);
919bfd7f386SKyungmin Park 
920bfd7f386SKyungmin Park 	return ret;
921bfd7f386SKyungmin Park }
922bfd7f386SKyungmin Park 
923bfd7f386SKyungmin Park /**
924bfd7f386SKyungmin Park  * onenand_bbt_wait - [DEFAULT] wait until the command is done
925bfd7f386SKyungmin Park  * @param mtd		MTD device structure
926bfd7f386SKyungmin Park  * @param state		state to select the max. timeout value
927bfd7f386SKyungmin Park  *
928bfd7f386SKyungmin Park  * Wait for command done.
929bfd7f386SKyungmin Park  */
930bfd7f386SKyungmin Park static int onenand_bbt_wait(struct mtd_info *mtd, int state)
931bfd7f386SKyungmin Park {
932bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
933bfd7f386SKyungmin Park 	unsigned int flags = ONENAND_INT_MASTER;
934bfd7f386SKyungmin Park 	unsigned int interrupt;
935bfd7f386SKyungmin Park 	unsigned int ctrl;
936bfd7f386SKyungmin Park 
937bfd7f386SKyungmin Park 	while (1) {
938bfd7f386SKyungmin Park 		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
939bfd7f386SKyungmin Park 		if (interrupt & flags)
940bfd7f386SKyungmin Park 			break;
941bfd7f386SKyungmin Park 	}
942bfd7f386SKyungmin Park 
943bfd7f386SKyungmin Park 	/* To get correct interrupt status in timeout case */
944bfd7f386SKyungmin Park 	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
945bfd7f386SKyungmin Park 	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
946bfd7f386SKyungmin Park 
947bfd7f386SKyungmin Park 	if (interrupt & ONENAND_INT_READ) {
948bfd7f386SKyungmin Park 		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
949bfd7f386SKyungmin Park 		if (ecc & ONENAND_ECC_2BIT_ALL)
950bfd7f386SKyungmin Park 			return ONENAND_BBT_READ_ERROR;
951bfd7f386SKyungmin Park 	} else {
952bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_wait: read timeout!"
953bfd7f386SKyungmin Park 				"ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
954bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
955bfd7f386SKyungmin Park 	}
956bfd7f386SKyungmin Park 
957ef0921d6SKyungmin Park 	/* Initial bad block case: 0x2400 or 0x0400 */
958ef0921d6SKyungmin Park 	if (ctrl & ONENAND_CTRL_ERROR) {
959ef0921d6SKyungmin Park 		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
960ef0921d6SKyungmin Park 		return ONENAND_BBT_READ_ERROR;
961ef0921d6SKyungmin Park 	}
962ef0921d6SKyungmin Park 
963bfd7f386SKyungmin Park 	return 0;
964bfd7f386SKyungmin Park }
965bfd7f386SKyungmin Park 
966bfd7f386SKyungmin Park /**
967bfd7f386SKyungmin Park  * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan
968bfd7f386SKyungmin Park  * @param mtd		MTD device structure
969bfd7f386SKyungmin Park  * @param from		offset to read from
970bfd7f386SKyungmin Park  * @param ops		oob operation description structure
971bfd7f386SKyungmin Park  *
972bfd7f386SKyungmin Park  * OneNAND read out-of-band data from the spare area for bbt scan
973bfd7f386SKyungmin Park  */
974bfd7f386SKyungmin Park int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
975bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
97659829cc1SJean-Christophe PLAGNIOL-VILLARD {
97759829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
97859829cc1SJean-Christophe PLAGNIOL-VILLARD 	int read = 0, thislen, column;
97959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
980bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
981bfd7f386SKyungmin Park 	u_char *buf = ops->oobbuf;
98259829cc1SJean-Christophe PLAGNIOL-VILLARD 
983ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
98459829cc1SJean-Christophe PLAGNIOL-VILLARD 
985bfd7f386SKyungmin Park 	/* Initialize return value */
986bfd7f386SKyungmin Park 	ops->oobretlen = 0;
98759829cc1SJean-Christophe PLAGNIOL-VILLARD 
98859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow reads past end of device */
98959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((from + len) > mtd->size)) {
990bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");
991bfd7f386SKyungmin Park 		return ONENAND_BBT_READ_FATAL_ERROR;
99259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
99359829cc1SJean-Christophe PLAGNIOL-VILLARD 
99459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
99559829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_READING);
99659829cc1SJean-Christophe PLAGNIOL-VILLARD 
99759829cc1SJean-Christophe PLAGNIOL-VILLARD 	column = from & (mtd->oobsize - 1);
99859829cc1SJean-Christophe PLAGNIOL-VILLARD 
99959829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (read < len) {
1000bfd7f386SKyungmin Park 
100159829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = mtd->oobsize - column;
100259829cc1SJean-Christophe PLAGNIOL-VILLARD 		thislen = min_t(int, thislen, len);
100359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1004ef0921d6SKyungmin Park 		this->spare_buf = buf;
100559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
100659829cc1SJean-Christophe PLAGNIOL-VILLARD 
100759829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, from, 0);
100859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1009ef0921d6SKyungmin Park 		ret = this->bbt_wait(mtd, FL_READING);
1010bfd7f386SKyungmin Park 		if (ret)
1011bfd7f386SKyungmin Park 			break;
101259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1013*ce3277a6SKyungmin Park 		this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
101459829cc1SJean-Christophe PLAGNIOL-VILLARD 		read += thislen;
101559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read == len)
101659829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
101759829cc1SJean-Christophe PLAGNIOL-VILLARD 
101859829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
1019bfd7f386SKyungmin Park 
102059829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Read more? */
102159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (read < len) {
1022bfd7f386SKyungmin Park 			/* Update Page size */
1023bfd7f386SKyungmin Park 			from += this->writesize;
102459829cc1SJean-Christophe PLAGNIOL-VILLARD 			column = 0;
102559829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
102659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
102759829cc1SJean-Christophe PLAGNIOL-VILLARD 
102859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
102959829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
103059829cc1SJean-Christophe PLAGNIOL-VILLARD 
1031bfd7f386SKyungmin Park 	ops->oobretlen = read;
103259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
103359829cc1SJean-Christophe PLAGNIOL-VILLARD }
103459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1035bfd7f386SKyungmin Park 
103659829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
103759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1038bfd7f386SKyungmin Park  * onenand_verify_oob - [GENERIC] verify the oob contents after a write
103959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
104059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf           the databuffer to verify
1041bfd7f386SKyungmin Park  * @param to            offset to read from
104259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1043bfd7f386SKyungmin Park static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
104459829cc1SJean-Christophe PLAGNIOL-VILLARD {
104559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1046bfd7f386SKyungmin Park 	u_char *oob_buf = this->oob_buf;
1047bfd7f386SKyungmin Park 	int status, i;
104859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1049bfd7f386SKyungmin Park 	this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
1050bfd7f386SKyungmin Park 	onenand_update_bufferram(mtd, to, 0);
1051bfd7f386SKyungmin Park 	status = this->wait(mtd, FL_READING);
1052bfd7f386SKyungmin Park 	if (status)
1053bfd7f386SKyungmin Park 		return status;
1054bfd7f386SKyungmin Park 
1055ef0921d6SKyungmin Park 	this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
1056bfd7f386SKyungmin Park 	for (i = 0; i < mtd->oobsize; i++)
1057bfd7f386SKyungmin Park 		if (buf[i] != 0xFF && buf[i] != oob_buf[i])
1058bfd7f386SKyungmin Park 			return -EBADMSG;
1059bfd7f386SKyungmin Park 
1060bfd7f386SKyungmin Park 	return 0;
1061bfd7f386SKyungmin Park }
1062bfd7f386SKyungmin Park 
1063bfd7f386SKyungmin Park /**
1064bfd7f386SKyungmin Park  * onenand_verify - [GENERIC] verify the chip contents after a write
1065bfd7f386SKyungmin Park  * @param mtd          MTD device structure
1066bfd7f386SKyungmin Park  * @param buf          the databuffer to verify
1067bfd7f386SKyungmin Park  * @param addr         offset to read from
1068bfd7f386SKyungmin Park  * @param len          number of bytes to read and compare
1069bfd7f386SKyungmin Park  */
1070bfd7f386SKyungmin Park static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len)
1071bfd7f386SKyungmin Park {
1072bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1073bfd7f386SKyungmin Park 	void __iomem *dataram;
1074bfd7f386SKyungmin Park 	int ret = 0;
1075bfd7f386SKyungmin Park 	int thislen, column;
1076bfd7f386SKyungmin Park 
1077bfd7f386SKyungmin Park 	while (len != 0) {
1078bfd7f386SKyungmin Park 		thislen = min_t(int, this->writesize, len);
1079bfd7f386SKyungmin Park 		column = addr & (this->writesize - 1);
1080bfd7f386SKyungmin Park 		if (column + thislen > this->writesize)
1081bfd7f386SKyungmin Park 			thislen = this->writesize - column;
1082bfd7f386SKyungmin Park 
1083bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
1084bfd7f386SKyungmin Park 
1085bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, addr, 0);
108659829cc1SJean-Christophe PLAGNIOL-VILLARD 
108759829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_READING);
108859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret)
108959829cc1SJean-Christophe PLAGNIOL-VILLARD 			return ret;
109059829cc1SJean-Christophe PLAGNIOL-VILLARD 
109159829cc1SJean-Christophe PLAGNIOL-VILLARD 		onenand_update_bufferram(mtd, addr, 1);
109259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1093bfd7f386SKyungmin Park 		dataram = this->base + ONENAND_DATARAM;
1094bfd7f386SKyungmin Park 		dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);
109559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1096bfd7f386SKyungmin Park 		if (memcmp(buf, dataram + column, thislen))
109759829cc1SJean-Christophe PLAGNIOL-VILLARD 			return -EBADMSG;
109859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1099bfd7f386SKyungmin Park 		len -= thislen;
1100bfd7f386SKyungmin Park 		buf += thislen;
1101bfd7f386SKyungmin Park 		addr += thislen;
1102bfd7f386SKyungmin Park 	}
1103bfd7f386SKyungmin Park 
110459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
110559829cc1SJean-Christophe PLAGNIOL-VILLARD }
110659829cc1SJean-Christophe PLAGNIOL-VILLARD #else
1107bfd7f386SKyungmin Park #define onenand_verify(...)             (0)
1108bfd7f386SKyungmin Park #define onenand_verify_oob(...)         (0)
110959829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
111059829cc1SJean-Christophe PLAGNIOL-VILLARD 
11111ae39862SStefan Roese #define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)
111259829cc1SJean-Christophe PLAGNIOL-VILLARD 
111359829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1114bfd7f386SKyungmin Park  * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
111559829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
1116bfd7f386SKyungmin Park  * @param oob_buf       oob buffer
1117bfd7f386SKyungmin Park  * @param buf           source address
1118bfd7f386SKyungmin Park  * @param column        oob offset to write to
1119bfd7f386SKyungmin Park  * @param thislen       oob length to write
112059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1121bfd7f386SKyungmin Park static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
1122bfd7f386SKyungmin Park 		const u_char *buf, int column, int thislen)
112359829cc1SJean-Christophe PLAGNIOL-VILLARD {
112459829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1125bfd7f386SKyungmin Park 	struct nand_oobfree *free;
1126bfd7f386SKyungmin Park 	int writecol = column;
1127bfd7f386SKyungmin Park 	int writeend = column + thislen;
1128bfd7f386SKyungmin Park 	int lastgap = 0;
1129bfd7f386SKyungmin Park 	unsigned int i;
1130bfd7f386SKyungmin Park 
1131bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1132bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1133bfd7f386SKyungmin Park 		if (writecol >= lastgap)
1134bfd7f386SKyungmin Park 			writecol += free->offset - lastgap;
1135bfd7f386SKyungmin Park 		if (writeend >= lastgap)
1136bfd7f386SKyungmin Park 			writeend += free->offset - lastgap;
1137bfd7f386SKyungmin Park 		lastgap = free->offset + free->length;
1138bfd7f386SKyungmin Park 	}
1139bfd7f386SKyungmin Park 	free = this->ecclayout->oobfree;
1140bfd7f386SKyungmin Park 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
1141bfd7f386SKyungmin Park 		int free_end = free->offset + free->length;
1142bfd7f386SKyungmin Park 		if (free->offset < writeend && free_end > writecol) {
1143bfd7f386SKyungmin Park 			int st = max_t(int,free->offset,writecol);
1144bfd7f386SKyungmin Park 			int ed = min_t(int,free_end,writeend);
1145bfd7f386SKyungmin Park 			int n = ed - st;
1146bfd7f386SKyungmin Park 			memcpy(oob_buf + st, buf, n);
1147bfd7f386SKyungmin Park 			buf += n;
1148bfd7f386SKyungmin Park 		} else if (column == 0)
1149bfd7f386SKyungmin Park 			break;
1150bfd7f386SKyungmin Park 	}
1151bfd7f386SKyungmin Park 	return 0;
1152bfd7f386SKyungmin Park }
1153bfd7f386SKyungmin Park 
1154bfd7f386SKyungmin Park /**
1155bfd7f386SKyungmin Park  * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band
1156bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1157bfd7f386SKyungmin Park  * @param to            offset to write to
1158bfd7f386SKyungmin Park  * @param ops           oob operation description structure
1159bfd7f386SKyungmin Park  *
1160bfd7f386SKyungmin Park  * Write main and/or oob with ECC
1161bfd7f386SKyungmin Park  */
1162bfd7f386SKyungmin Park static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
1163bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1164bfd7f386SKyungmin Park {
1165bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1166bfd7f386SKyungmin Park 	int written = 0, column, thislen, subpage;
1167bfd7f386SKyungmin Park 	int oobwritten = 0, oobcolumn, thisooblen, oobsize;
1168bfd7f386SKyungmin Park 	size_t len = ops->len;
1169bfd7f386SKyungmin Park 	size_t ooblen = ops->ooblen;
1170bfd7f386SKyungmin Park 	const u_char *buf = ops->datbuf;
1171bfd7f386SKyungmin Park 	const u_char *oob = ops->oobbuf;
1172bfd7f386SKyungmin Park 	u_char *oobbuf;
117359829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
117459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1175ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
117659829cc1SJean-Christophe PLAGNIOL-VILLARD 
117759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Initialize retlen, in case of early exit */
1178bfd7f386SKyungmin Park 	ops->retlen = 0;
1179bfd7f386SKyungmin Park 	ops->oobretlen = 0;
118059829cc1SJean-Christophe PLAGNIOL-VILLARD 
118159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow writes past end of device */
118259829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((to + len) > mtd->size)) {
1183bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
118459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
118559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
118659829cc1SJean-Christophe PLAGNIOL-VILLARD 
118759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reject writes, which are not page aligned */
1188bfd7f386SKyungmin Park 	if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
1189bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
119059829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
119159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
119259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1193bfd7f386SKyungmin Park 	if (ops->mode == MTD_OOB_AUTO)
1194bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1195bfd7f386SKyungmin Park 	else
1196bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1197bfd7f386SKyungmin Park 
1198bfd7f386SKyungmin Park 	oobcolumn = to & (mtd->oobsize - 1);
1199bfd7f386SKyungmin Park 
1200bfd7f386SKyungmin Park 	column = to & (mtd->writesize - 1);
120159829cc1SJean-Christophe PLAGNIOL-VILLARD 
120259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop until all data write */
120359829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (written < len) {
1204bfd7f386SKyungmin Park 		u_char *wbuf = (u_char *) buf;
120559829cc1SJean-Christophe PLAGNIOL-VILLARD 
1206bfd7f386SKyungmin Park 		thislen = min_t(int, mtd->writesize - column, len - written);
1207bfd7f386SKyungmin Park 		thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
120859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1209bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
1210bfd7f386SKyungmin Park 
1211bfd7f386SKyungmin Park 		/* Partial page write */
1212bfd7f386SKyungmin Park 		subpage = thislen < mtd->writesize;
1213bfd7f386SKyungmin Park 		if (subpage) {
1214bfd7f386SKyungmin Park 			memset(this->page_buf, 0xff, mtd->writesize);
1215bfd7f386SKyungmin Park 			memcpy(this->page_buf + column, buf, thislen);
1216bfd7f386SKyungmin Park 			wbuf = this->page_buf;
1217bfd7f386SKyungmin Park 		}
1218bfd7f386SKyungmin Park 
1219ef0921d6SKyungmin Park 		this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
1220bfd7f386SKyungmin Park 
1221bfd7f386SKyungmin Park 		if (oob) {
1222bfd7f386SKyungmin Park 			oobbuf = this->oob_buf;
1223bfd7f386SKyungmin Park 
1224bfd7f386SKyungmin Park 			/* We send data to spare ram with oobsize
1225bfd7f386SKyungmin Park 			 *                          * to prevent byte access */
1226bfd7f386SKyungmin Park 			memset(oobbuf, 0xff, mtd->oobsize);
1227bfd7f386SKyungmin Park 			if (ops->mode == MTD_OOB_AUTO)
1228bfd7f386SKyungmin Park 				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
1229bfd7f386SKyungmin Park 			else
1230bfd7f386SKyungmin Park 				memcpy(oobbuf + oobcolumn, oob, thisooblen);
1231bfd7f386SKyungmin Park 
1232bfd7f386SKyungmin Park 			oobwritten += thisooblen;
1233bfd7f386SKyungmin Park 			oob += thisooblen;
1234bfd7f386SKyungmin Park 			oobcolumn = 0;
1235bfd7f386SKyungmin Park 		} else
1236bfd7f386SKyungmin Park 			oobbuf = (u_char *) ffchars;
1237bfd7f386SKyungmin Park 
1238ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
123959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1240d438d508SKyungmin Park 		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
124159829cc1SJean-Christophe PLAGNIOL-VILLARD 
124259829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_WRITING);
1243bfd7f386SKyungmin Park 
1244bfd7f386SKyungmin Park 		/* In partial page write we don't update bufferram */
1245bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, !ret && !subpage);
1246bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1247bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1248bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
1249bfd7f386SKyungmin Park 		}
1250bfd7f386SKyungmin Park 
125159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
1252bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
1253bfd7f386SKyungmin Park 			break;
1254bfd7f386SKyungmin Park 		}
1255bfd7f386SKyungmin Park 
1256bfd7f386SKyungmin Park 		/* Only check verify write turn on */
1257bfd7f386SKyungmin Park 		ret = onenand_verify(mtd, buf, to, thislen);
1258bfd7f386SKyungmin Park 		if (ret) {
1259bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
126059829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
126159829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
126259829cc1SJean-Christophe PLAGNIOL-VILLARD 
126359829cc1SJean-Christophe PLAGNIOL-VILLARD 		written += thislen;
126459829cc1SJean-Christophe PLAGNIOL-VILLARD 
126559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (written == len)
126659829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
126759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1268bfd7f386SKyungmin Park 		column = 0;
126959829cc1SJean-Christophe PLAGNIOL-VILLARD 		to += thislen;
127059829cc1SJean-Christophe PLAGNIOL-VILLARD 		buf += thislen;
127159829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
127259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1273bfd7f386SKyungmin Park 	ops->retlen = written;
127459829cc1SJean-Christophe PLAGNIOL-VILLARD 
1275bfd7f386SKyungmin Park 	return ret;
1276bfd7f386SKyungmin Park }
1277bfd7f386SKyungmin Park 
1278bfd7f386SKyungmin Park /**
1279bfd7f386SKyungmin Park  * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
1280bfd7f386SKyungmin Park  * @param mtd           MTD device structure
1281bfd7f386SKyungmin Park  * @param to            offset to write to
1282bfd7f386SKyungmin Park  * @param len           number of bytes to write
1283bfd7f386SKyungmin Park  * @param retlen        pointer to variable to store the number of written bytes
1284bfd7f386SKyungmin Park  * @param buf           the data to write
1285bfd7f386SKyungmin Park  * @param mode          operation mode
1286bfd7f386SKyungmin Park  *
1287bfd7f386SKyungmin Park  * OneNAND write out-of-band
1288bfd7f386SKyungmin Park  */
1289bfd7f386SKyungmin Park static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
1290bfd7f386SKyungmin Park 		struct mtd_oob_ops *ops)
1291bfd7f386SKyungmin Park {
1292bfd7f386SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1293bfd7f386SKyungmin Park 	int column, ret = 0, oobsize;
1294bfd7f386SKyungmin Park 	int written = 0;
1295bfd7f386SKyungmin Park 	u_char *oobbuf;
1296bfd7f386SKyungmin Park 	size_t len = ops->ooblen;
1297bfd7f386SKyungmin Park 	const u_char *buf = ops->oobbuf;
1298bfd7f386SKyungmin Park 	mtd_oob_mode_t mode = ops->mode;
1299bfd7f386SKyungmin Park 
1300bfd7f386SKyungmin Park 	to += ops->ooboffs;
1301bfd7f386SKyungmin Park 
1302ef0921d6SKyungmin Park 	MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
1303bfd7f386SKyungmin Park 
1304bfd7f386SKyungmin Park 	/* Initialize retlen, in case of early exit */
1305bfd7f386SKyungmin Park 	ops->oobretlen = 0;
1306bfd7f386SKyungmin Park 
1307bfd7f386SKyungmin Park 	if (mode == MTD_OOB_AUTO)
1308bfd7f386SKyungmin Park 		oobsize = this->ecclayout->oobavail;
1309bfd7f386SKyungmin Park 	else
1310bfd7f386SKyungmin Park 		oobsize = mtd->oobsize;
1311bfd7f386SKyungmin Park 
1312bfd7f386SKyungmin Park 	column = to & (mtd->oobsize - 1);
1313bfd7f386SKyungmin Park 
1314bfd7f386SKyungmin Park 	if (unlikely(column >= oobsize)) {
1315bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");
1316bfd7f386SKyungmin Park 		return -EINVAL;
1317bfd7f386SKyungmin Park 	}
1318bfd7f386SKyungmin Park 
1319bfd7f386SKyungmin Park 	/* For compatibility with NAND: Do not allow write past end of page */
1320bfd7f386SKyungmin Park 	if (unlikely(column + len > oobsize)) {
1321bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: "
1322bfd7f386SKyungmin Park 				"Attempt to write past end of page\n");
1323bfd7f386SKyungmin Park 		return -EINVAL;
1324bfd7f386SKyungmin Park 	}
1325bfd7f386SKyungmin Park 
1326bfd7f386SKyungmin Park 	/* Do not allow reads past end of device */
1327bfd7f386SKyungmin Park 	if (unlikely(to >= mtd->size ||
1328bfd7f386SKyungmin Park 				column + len > ((mtd->size >> this->page_shift) -
1329bfd7f386SKyungmin Park 					(to >> this->page_shift)) * oobsize)) {
1330bfd7f386SKyungmin Park 		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n");
1331bfd7f386SKyungmin Park 		return -EINVAL;
1332bfd7f386SKyungmin Park 	}
1333bfd7f386SKyungmin Park 
1334bfd7f386SKyungmin Park 	oobbuf = this->oob_buf;
1335bfd7f386SKyungmin Park 
1336bfd7f386SKyungmin Park 	/* Loop until all data write */
1337bfd7f386SKyungmin Park 	while (written < len) {
1338bfd7f386SKyungmin Park 		int thislen = min_t(int, oobsize, len - written);
1339bfd7f386SKyungmin Park 
1340bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize);
1341bfd7f386SKyungmin Park 
1342bfd7f386SKyungmin Park 		/* We send data to spare ram with oobsize
1343bfd7f386SKyungmin Park 		 * to prevent byte access */
1344bfd7f386SKyungmin Park 		memset(oobbuf, 0xff, mtd->oobsize);
1345bfd7f386SKyungmin Park 		if (mode == MTD_OOB_AUTO)
1346bfd7f386SKyungmin Park 			onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
1347bfd7f386SKyungmin Park 		else
1348bfd7f386SKyungmin Park 			memcpy(oobbuf + column, buf, thislen);
1349ef0921d6SKyungmin Park 		this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
1350bfd7f386SKyungmin Park 
1351bfd7f386SKyungmin Park 		this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
1352bfd7f386SKyungmin Park 
1353bfd7f386SKyungmin Park 		onenand_update_bufferram(mtd, to, 0);
1354bfd7f386SKyungmin Park 		if (ONENAND_IS_2PLANE(this)) {
1355bfd7f386SKyungmin Park 			ONENAND_SET_BUFFERRAM1(this);
1356bfd7f386SKyungmin Park 			onenand_update_bufferram(mtd, to + this->writesize, 0);
1357bfd7f386SKyungmin Park 		}
1358bfd7f386SKyungmin Park 
1359bfd7f386SKyungmin Park 		ret = this->wait(mtd, FL_WRITING);
1360bfd7f386SKyungmin Park 		if (ret) {
1361bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret);
1362bfd7f386SKyungmin Park 			break;
1363bfd7f386SKyungmin Park 		}
1364bfd7f386SKyungmin Park 
1365bfd7f386SKyungmin Park 		ret = onenand_verify_oob(mtd, oobbuf, to);
1366bfd7f386SKyungmin Park 		if (ret) {
1367bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret);
1368bfd7f386SKyungmin Park 			break;
1369bfd7f386SKyungmin Park 		}
1370bfd7f386SKyungmin Park 
1371bfd7f386SKyungmin Park 		written += thislen;
1372bfd7f386SKyungmin Park 		if (written == len)
1373bfd7f386SKyungmin Park 			break;
1374bfd7f386SKyungmin Park 
1375bfd7f386SKyungmin Park 		to += mtd->writesize;
1376bfd7f386SKyungmin Park 		buf += thislen;
1377bfd7f386SKyungmin Park 		column = 0;
1378bfd7f386SKyungmin Park 	}
1379bfd7f386SKyungmin Park 
1380bfd7f386SKyungmin Park 	ops->oobretlen = written;
138159829cc1SJean-Christophe PLAGNIOL-VILLARD 
138259829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
138359829cc1SJean-Christophe PLAGNIOL-VILLARD }
138459829cc1SJean-Christophe PLAGNIOL-VILLARD 
138559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
138659829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write - [MTD Interface] compability function for onenand_write_ecc
138759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
138859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
138959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param len		number of bytes to write
139059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param retlen	pointer to variable to store the number of written bytes
139159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param buf		the data to write
139259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1393bfd7f386SKyungmin Park  * Write with ECC
139459829cc1SJean-Christophe PLAGNIOL-VILLARD  */
139559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_write(struct mtd_info *mtd, loff_t to, size_t len,
139659829cc1SJean-Christophe PLAGNIOL-VILLARD 		  size_t * retlen, const u_char * buf)
139759829cc1SJean-Christophe PLAGNIOL-VILLARD {
1398bfd7f386SKyungmin Park 	struct mtd_oob_ops ops = {
1399bfd7f386SKyungmin Park 		.len    = len,
1400bfd7f386SKyungmin Park 		.ooblen = 0,
1401bfd7f386SKyungmin Park 		.datbuf = (u_char *) buf,
1402bfd7f386SKyungmin Park 		.oobbuf = NULL,
1403bfd7f386SKyungmin Park 	};
1404bfd7f386SKyungmin Park 	int ret;
1405bfd7f386SKyungmin Park 
1406bfd7f386SKyungmin Park 	onenand_get_device(mtd, FL_WRITING);
1407bfd7f386SKyungmin Park 	ret = onenand_write_ops_nolock(mtd, to, &ops);
1408bfd7f386SKyungmin Park 	onenand_release_device(mtd);
1409bfd7f386SKyungmin Park 
1410bfd7f386SKyungmin Park 	*retlen = ops.retlen;
1411bfd7f386SKyungmin Park 	return ret;
141259829cc1SJean-Christophe PLAGNIOL-VILLARD }
141359829cc1SJean-Christophe PLAGNIOL-VILLARD 
141459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
141559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_write_oob - [MTD Interface] OneNAND write out-of-band
141659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
141759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param to		offset to write to
1418bfd7f386SKyungmin Park  * @param ops		oob operation description structure
141959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1420bfd7f386SKyungmin Park  * OneNAND write main and/or out-of-band
142159829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1422bfd7f386SKyungmin Park int onenand_write_oob(struct mtd_info *mtd, loff_t to,
1423bfd7f386SKyungmin Park 			struct mtd_oob_ops *ops)
142459829cc1SJean-Christophe PLAGNIOL-VILLARD {
1425bfd7f386SKyungmin Park 	int ret;
142659829cc1SJean-Christophe PLAGNIOL-VILLARD 
1427bfd7f386SKyungmin Park 	switch (ops->mode) {
1428bfd7f386SKyungmin Park 	case MTD_OOB_PLACE:
1429bfd7f386SKyungmin Park 	case MTD_OOB_AUTO:
1430bfd7f386SKyungmin Park 		break;
1431bfd7f386SKyungmin Park 	case MTD_OOB_RAW:
1432bfd7f386SKyungmin Park 		/* Not implemented yet */
1433bfd7f386SKyungmin Park 	default:
143459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
143559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
143659829cc1SJean-Christophe PLAGNIOL-VILLARD 
143759829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_WRITING);
1438bfd7f386SKyungmin Park 	if (ops->datbuf)
1439bfd7f386SKyungmin Park 		ret = onenand_write_ops_nolock(mtd, to, ops);
1440bfd7f386SKyungmin Park 	else
1441bfd7f386SKyungmin Park 		ret = onenand_write_oob_nolock(mtd, to, ops);
144259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
144359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1444bfd7f386SKyungmin Park 	return ret;
144559829cc1SJean-Christophe PLAGNIOL-VILLARD 
144659829cc1SJean-Christophe PLAGNIOL-VILLARD }
144759829cc1SJean-Christophe PLAGNIOL-VILLARD 
144859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1449d438d508SKyungmin Park  * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad
1450d438d508SKyungmin Park  * @param mtd		MTD device structure
1451d438d508SKyungmin Park  * @param ofs		offset from device start
1452d438d508SKyungmin Park  * @param allowbbt	1, if its allowed to access the bbt area
1453d438d508SKyungmin Park  *
1454d438d508SKyungmin Park  * Check, if the block is bad, Either by reading the bad block table or
1455d438d508SKyungmin Park  * calling of the scan function.
1456d438d508SKyungmin Park  */
1457d438d508SKyungmin Park static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt)
1458d438d508SKyungmin Park {
1459d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1460d438d508SKyungmin Park 	struct bbm_info *bbm = this->bbm;
1461d438d508SKyungmin Park 
1462d438d508SKyungmin Park 	/* Return info from the table */
1463d438d508SKyungmin Park 	return bbm->isbad_bbt(mtd, ofs, allowbbt);
1464d438d508SKyungmin Park }
1465d438d508SKyungmin Park 
1466d438d508SKyungmin Park 
1467d438d508SKyungmin Park /**
146859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_erase - [MTD Interface] erase block(s)
146959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
147059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param instr		erase instruction
147159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
147259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Erase one ore more blocks
147359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
147459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
147559829cc1SJean-Christophe PLAGNIOL-VILLARD {
147659829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
147759829cc1SJean-Christophe PLAGNIOL-VILLARD 	unsigned int block_size;
147859829cc1SJean-Christophe PLAGNIOL-VILLARD 	loff_t addr;
147959829cc1SJean-Christophe PLAGNIOL-VILLARD 	int len;
148059829cc1SJean-Christophe PLAGNIOL-VILLARD 	int ret = 0;
148159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1482bfd7f386SKyungmin Park 	MTDDEBUG (MTD_DEBUG_LEVEL3,
1483bfd7f386SKyungmin Park 		 "onenand_erase: start = 0x%08x, len = %i\n",
148459829cc1SJean-Christophe PLAGNIOL-VILLARD 		 (unsigned int)instr->addr, (unsigned int)instr->len);
148559829cc1SJean-Christophe PLAGNIOL-VILLARD 
148659829cc1SJean-Christophe PLAGNIOL-VILLARD 	block_size = (1 << this->erase_shift);
148759829cc1SJean-Christophe PLAGNIOL-VILLARD 
148859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Start address must align on block boundary */
148959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely(instr->addr & (block_size - 1))) {
14903167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
14913167c538SScott Wood 			 "onenand_erase: Unaligned address\n");
149259829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
149359829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
149459829cc1SJean-Christophe PLAGNIOL-VILLARD 
149559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Length must align on block boundary */
149659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely(instr->len & (block_size - 1))) {
14973167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
149859829cc1SJean-Christophe PLAGNIOL-VILLARD 			 "onenand_erase: Length not block aligned\n");
149959829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
150059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
150159829cc1SJean-Christophe PLAGNIOL-VILLARD 
150259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do not allow erase past end of device */
150359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (unlikely((instr->len + instr->addr) > mtd->size)) {
15043167c538SScott Wood 		MTDDEBUG (MTD_DEBUG_LEVEL0,
150559829cc1SJean-Christophe PLAGNIOL-VILLARD 			 "onenand_erase: Erase past end of device\n");
150659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -EINVAL;
150759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
150859829cc1SJean-Christophe PLAGNIOL-VILLARD 
150959829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->fail_addr = 0xffffffff;
151059829cc1SJean-Christophe PLAGNIOL-VILLARD 
151159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
151259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_ERASING);
151359829cc1SJean-Christophe PLAGNIOL-VILLARD 
151459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Loop throught the pages */
151559829cc1SJean-Christophe PLAGNIOL-VILLARD 	len = instr->len;
151659829cc1SJean-Christophe PLAGNIOL-VILLARD 	addr = instr->addr;
151759829cc1SJean-Christophe PLAGNIOL-VILLARD 
151859829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASING;
151959829cc1SJean-Christophe PLAGNIOL-VILLARD 
152059829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (len) {
152159829cc1SJean-Christophe PLAGNIOL-VILLARD 
1522ef0921d6SKyungmin Park 		/* Check if we have a bad block, we do not erase bad blocks */
1523ef0921d6SKyungmin Park 		if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) {
1524ef0921d6SKyungmin Park 			printk(KERN_WARNING "onenand_erase: attempt to erase"
1525ef0921d6SKyungmin Park 				" a bad block at addr 0x%08x\n",
1526ef0921d6SKyungmin Park 				(unsigned int) addr);
1527ef0921d6SKyungmin Park 			instr->state = MTD_ERASE_FAILED;
1528ef0921d6SKyungmin Park 			goto erase_exit;
1529ef0921d6SKyungmin Park 		}
153059829cc1SJean-Christophe PLAGNIOL-VILLARD 
153159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
153259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1533d438d508SKyungmin Park 		onenand_invalidate_bufferram(mtd, addr, block_size);
1534d438d508SKyungmin Park 
153559829cc1SJean-Christophe PLAGNIOL-VILLARD 		ret = this->wait(mtd, FL_ERASING);
153659829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check, if it is write protected */
153759829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (ret) {
153859829cc1SJean-Christophe PLAGNIOL-VILLARD 			if (ret == -EPERM)
15393167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
15403167c538SScott Wood 					  "Device is write protected!!!\n");
154159829cc1SJean-Christophe PLAGNIOL-VILLARD 			else
15423167c538SScott Wood 				MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
15433167c538SScott Wood 					  "Failed erase, block %d\n",
154459829cc1SJean-Christophe PLAGNIOL-VILLARD 					  (unsigned)(addr >> this->erase_shift));
1545ef0921d6SKyungmin Park 			if (ret == -EPERM)
1546ef0921d6SKyungmin Park 				printk("onenand_erase: "
1547ef0921d6SKyungmin Park 					  "Device is write protected!!!\n");
1548ef0921d6SKyungmin Park 			else
1549ef0921d6SKyungmin Park 				printk("onenand_erase: "
1550ef0921d6SKyungmin Park 					  "Failed erase, block %d\n",
1551ef0921d6SKyungmin Park 					  (unsigned)(addr >> this->erase_shift));
155259829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->state = MTD_ERASE_FAILED;
155359829cc1SJean-Christophe PLAGNIOL-VILLARD 			instr->fail_addr = addr;
1554ef0921d6SKyungmin Park 
155559829cc1SJean-Christophe PLAGNIOL-VILLARD 			goto erase_exit;
155659829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
155759829cc1SJean-Christophe PLAGNIOL-VILLARD 
155859829cc1SJean-Christophe PLAGNIOL-VILLARD 		len -= block_size;
155959829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr += block_size;
156059829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
156159829cc1SJean-Christophe PLAGNIOL-VILLARD 
156259829cc1SJean-Christophe PLAGNIOL-VILLARD 	instr->state = MTD_ERASE_DONE;
156359829cc1SJean-Christophe PLAGNIOL-VILLARD 
156459829cc1SJean-Christophe PLAGNIOL-VILLARD erase_exit:
156559829cc1SJean-Christophe PLAGNIOL-VILLARD 
156659829cc1SJean-Christophe PLAGNIOL-VILLARD 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
156759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Do call back function */
156859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!ret)
156959829cc1SJean-Christophe PLAGNIOL-VILLARD 		mtd_erase_callback(instr);
157059829cc1SJean-Christophe PLAGNIOL-VILLARD 
157159829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Deselect and wake up anyone waiting on the device */
157259829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
157359829cc1SJean-Christophe PLAGNIOL-VILLARD 
157459829cc1SJean-Christophe PLAGNIOL-VILLARD 	return ret;
157559829cc1SJean-Christophe PLAGNIOL-VILLARD }
157659829cc1SJean-Christophe PLAGNIOL-VILLARD 
157759829cc1SJean-Christophe PLAGNIOL-VILLARD /**
157859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_sync - [MTD Interface] sync
157959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
158059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
158159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Sync is actually a wait for chip ready function
158259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
158359829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_sync(struct mtd_info *mtd)
158459829cc1SJean-Christophe PLAGNIOL-VILLARD {
15853167c538SScott Wood 	MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n");
158659829cc1SJean-Christophe PLAGNIOL-VILLARD 
158759829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Grab the lock and see if the device is available */
158859829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_get_device(mtd, FL_SYNCING);
158959829cc1SJean-Christophe PLAGNIOL-VILLARD 
159059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Release it and go back */
159159829cc1SJean-Christophe PLAGNIOL-VILLARD 	onenand_release_device(mtd);
159259829cc1SJean-Christophe PLAGNIOL-VILLARD }
159359829cc1SJean-Christophe PLAGNIOL-VILLARD 
159459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
159559829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
159659829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
159759829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1598d438d508SKyungmin Park  *
1599d438d508SKyungmin Park  * Check whether the block is bad
160059829cc1SJean-Christophe PLAGNIOL-VILLARD  */
160159829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
160259829cc1SJean-Christophe PLAGNIOL-VILLARD {
1603d438d508SKyungmin Park 	int ret;
1604d438d508SKyungmin Park 
1605d438d508SKyungmin Park 	/* Check for invalid offset */
1606d438d508SKyungmin Park 	if (ofs > mtd->size)
1607d438d508SKyungmin Park 		return -EINVAL;
1608d438d508SKyungmin Park 
1609d438d508SKyungmin Park 	onenand_get_device(mtd, FL_READING);
1610d438d508SKyungmin Park 	ret = onenand_block_isbad_nolock(mtd,ofs, 0);
1611d438d508SKyungmin Park 	onenand_release_device(mtd);
1612d438d508SKyungmin Park 	return ret;
161359829cc1SJean-Christophe PLAGNIOL-VILLARD }
161459829cc1SJean-Christophe PLAGNIOL-VILLARD 
161559829cc1SJean-Christophe PLAGNIOL-VILLARD /**
16161714f51aSKyungmin Park  * onenand_default_block_markbad - [DEFAULT] mark a block bad
16171714f51aSKyungmin Park  * @param mtd           MTD device structure
16181714f51aSKyungmin Park  * @param ofs           offset from device start
16191714f51aSKyungmin Park  *
16201714f51aSKyungmin Park  * This is the default implementation, which can be overridden by
16211714f51aSKyungmin Park  * a hardware specific driver.
16221714f51aSKyungmin Park  */
16231714f51aSKyungmin Park static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
16241714f51aSKyungmin Park {
16251714f51aSKyungmin Park 	struct onenand_chip *this = mtd->priv;
16261714f51aSKyungmin Park 	struct bbm_info *bbm = this->bbm;
16271714f51aSKyungmin Park 	u_char buf[2] = {0, 0};
16281714f51aSKyungmin Park 	struct mtd_oob_ops ops = {
16291714f51aSKyungmin Park 		.mode = MTD_OOB_PLACE,
16301714f51aSKyungmin Park 		.ooblen = 2,
16311714f51aSKyungmin Park 		.oobbuf = buf,
16321714f51aSKyungmin Park 		.ooboffs = 0,
16331714f51aSKyungmin Park 	};
16341714f51aSKyungmin Park 	int block;
16351714f51aSKyungmin Park 
16361714f51aSKyungmin Park 	/* Get block number */
16371714f51aSKyungmin Park 	block = ((int) ofs) >> bbm->bbt_erase_shift;
16381714f51aSKyungmin Park 	if (bbm->bbt)
16391714f51aSKyungmin Park 		bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
16401714f51aSKyungmin Park 
16411714f51aSKyungmin Park 	/* We write two bytes, so we dont have to mess with 16 bit access */
16421714f51aSKyungmin Park 	ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
16431714f51aSKyungmin Park 	return onenand_write_oob_nolock(mtd, ofs, &ops);
16441714f51aSKyungmin Park }
16451714f51aSKyungmin Park 
16461714f51aSKyungmin Park /**
164759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
164859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
164959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs		offset relative to mtd start
1650d438d508SKyungmin Park  *
1651d438d508SKyungmin Park  * Mark the block as bad
165259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
165359829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
165459829cc1SJean-Christophe PLAGNIOL-VILLARD {
1655d438d508SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1656d438d508SKyungmin Park 	int ret;
1657d438d508SKyungmin Park 
1658d438d508SKyungmin Park 	ret = onenand_block_isbad(mtd, ofs);
1659d438d508SKyungmin Park 	if (ret) {
1660d438d508SKyungmin Park 		/* If it was bad already, return success and do nothing */
1661d438d508SKyungmin Park 		if (ret > 0)
166259829cc1SJean-Christophe PLAGNIOL-VILLARD 			return 0;
1663d438d508SKyungmin Park 		return ret;
1664d438d508SKyungmin Park 	}
1665d438d508SKyungmin Park 
1666d438d508SKyungmin Park 	ret = this->block_markbad(mtd, ofs);
1667d438d508SKyungmin Park 	return ret;
166859829cc1SJean-Christophe PLAGNIOL-VILLARD }
166959829cc1SJean-Christophe PLAGNIOL-VILLARD 
167059829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1671ef0921d6SKyungmin Park  * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s)
167259829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd           MTD device structure
167359829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param ofs           offset relative to mtd start
1674ef0921d6SKyungmin Park  * @param len           number of bytes to lock or unlock
1675ef0921d6SKyungmin Park  * @param cmd           lock or unlock command
167659829cc1SJean-Christophe PLAGNIOL-VILLARD  *
1677ef0921d6SKyungmin Park  * Lock or unlock one or more blocks
167859829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1679ef0921d6SKyungmin Park static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd)
168059829cc1SJean-Christophe PLAGNIOL-VILLARD {
168159829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
168259829cc1SJean-Christophe PLAGNIOL-VILLARD 	int start, end, block, value, status;
1683ef0921d6SKyungmin Park 	int wp_status_mask;
168459829cc1SJean-Christophe PLAGNIOL-VILLARD 
168559829cc1SJean-Christophe PLAGNIOL-VILLARD 	start = ofs >> this->erase_shift;
168659829cc1SJean-Christophe PLAGNIOL-VILLARD 	end = len >> this->erase_shift;
168759829cc1SJean-Christophe PLAGNIOL-VILLARD 
1688ef0921d6SKyungmin Park 	if (cmd == ONENAND_CMD_LOCK)
1689ef0921d6SKyungmin Park 		wp_status_mask = ONENAND_WP_LS;
1690ef0921d6SKyungmin Park 	else
1691ef0921d6SKyungmin Park 		wp_status_mask = ONENAND_WP_US;
1692ef0921d6SKyungmin Park 
169359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Continuous lock scheme */
1694ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK) {
169559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
169659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(start,
169759829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
169859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set end block address */
169959829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(end - 1,
170059829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_END_BLOCK_ADDRESS);
170159829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
1702ef0921d6SKyungmin Park 		this->command(mtd, cmd, 0, 0);
170359829cc1SJean-Christophe PLAGNIOL-VILLARD 
170459829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
170559829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
170659829cc1SJean-Christophe PLAGNIOL-VILLARD 
170759829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
170859829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
170959829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
171059829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
171159829cc1SJean-Christophe PLAGNIOL-VILLARD 
171259829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
171359829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
171459829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
171559829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "wp status = 0x%x\n", status);
171659829cc1SJean-Christophe PLAGNIOL-VILLARD 
171759829cc1SJean-Christophe PLAGNIOL-VILLARD 		return 0;
171859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
171959829cc1SJean-Christophe PLAGNIOL-VILLARD 
172059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Block lock scheme */
1721ef0921d6SKyungmin Park 	for (block = start; block < start + end; block++) {
1722ef0921d6SKyungmin Park 		/* Set block address */
1723ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
1724ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
1725ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
1726ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
1727ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
1728ef0921d6SKyungmin Park 
172959829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Set start block address */
173059829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word(block,
173159829cc1SJean-Christophe PLAGNIOL-VILLARD 				 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
173259829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Write unlock command */
173359829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
173459829cc1SJean-Christophe PLAGNIOL-VILLARD 
173559829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* There's no return value */
173659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait(mtd, FL_UNLOCKING);
173759829cc1SJean-Christophe PLAGNIOL-VILLARD 
173859829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Sanity check */
173959829cc1SJean-Christophe PLAGNIOL-VILLARD 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
174059829cc1SJean-Christophe PLAGNIOL-VILLARD 		       & ONENAND_CTRL_ONGO)
174159829cc1SJean-Christophe PLAGNIOL-VILLARD 			continue;
174259829cc1SJean-Christophe PLAGNIOL-VILLARD 
174359829cc1SJean-Christophe PLAGNIOL-VILLARD 		/* Check lock status */
174459829cc1SJean-Christophe PLAGNIOL-VILLARD 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
174559829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (!(status & ONENAND_WP_US))
174659829cc1SJean-Christophe PLAGNIOL-VILLARD 			printk(KERN_ERR "block = %d, wp status = 0x%x\n",
174759829cc1SJean-Christophe PLAGNIOL-VILLARD 			       block, status);
174859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
174959829cc1SJean-Christophe PLAGNIOL-VILLARD 
175059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
175159829cc1SJean-Christophe PLAGNIOL-VILLARD }
175259829cc1SJean-Christophe PLAGNIOL-VILLARD 
17534fca3310SStefan Roese #ifdef ONENAND_LINUX
175459829cc1SJean-Christophe PLAGNIOL-VILLARD /**
1755ef0921d6SKyungmin Park  * onenand_lock - [MTD Interface] Lock block(s)
1756ef0921d6SKyungmin Park  * @param mtd           MTD device structure
1757ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
1758ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
1759ef0921d6SKyungmin Park  *
1760ef0921d6SKyungmin Park  * Lock one or more blocks
1761ef0921d6SKyungmin Park  */
1762ef0921d6SKyungmin Park static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
1763ef0921d6SKyungmin Park {
1764ef0921d6SKyungmin Park 	int ret;
1765ef0921d6SKyungmin Park 
1766ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
1767ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK);
1768ef0921d6SKyungmin Park 	onenand_release_device(mtd);
1769ef0921d6SKyungmin Park 	return ret;
1770ef0921d6SKyungmin Park }
1771ef0921d6SKyungmin Park 
1772ef0921d6SKyungmin Park /**
1773ef0921d6SKyungmin Park  * onenand_unlock - [MTD Interface] Unlock block(s)
1774ef0921d6SKyungmin Park  * @param mtd           MTD device structure
1775ef0921d6SKyungmin Park  * @param ofs           offset relative to mtd start
1776ef0921d6SKyungmin Park  * @param len           number of bytes to unlock
1777ef0921d6SKyungmin Park  *
1778ef0921d6SKyungmin Park  * Unlock one or more blocks
1779ef0921d6SKyungmin Park  */
1780ef0921d6SKyungmin Park static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
1781ef0921d6SKyungmin Park {
1782ef0921d6SKyungmin Park 	int ret;
1783ef0921d6SKyungmin Park 
1784ef0921d6SKyungmin Park 	onenand_get_device(mtd, FL_LOCKING);
1785ef0921d6SKyungmin Park 	ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
1786ef0921d6SKyungmin Park 	onenand_release_device(mtd);
1787ef0921d6SKyungmin Park 	return ret;
1788ef0921d6SKyungmin Park }
17894fca3310SStefan Roese #endif
1790ef0921d6SKyungmin Park 
1791ef0921d6SKyungmin Park /**
1792ef0921d6SKyungmin Park  * onenand_check_lock_status - [OneNAND Interface] Check lock status
1793ef0921d6SKyungmin Park  * @param this          onenand chip data structure
1794ef0921d6SKyungmin Park  *
1795ef0921d6SKyungmin Park  * Check lock status
1796ef0921d6SKyungmin Park  */
1797ef0921d6SKyungmin Park static int onenand_check_lock_status(struct onenand_chip *this)
1798ef0921d6SKyungmin Park {
1799ef0921d6SKyungmin Park 	unsigned int value, block, status;
1800ef0921d6SKyungmin Park 	unsigned int end;
1801ef0921d6SKyungmin Park 
1802ef0921d6SKyungmin Park 	end = this->chipsize >> this->erase_shift;
1803ef0921d6SKyungmin Park 	for (block = 0; block < end; block++) {
1804ef0921d6SKyungmin Park 		/* Set block address */
1805ef0921d6SKyungmin Park 		value = onenand_block_address(this, block);
1806ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
1807ef0921d6SKyungmin Park 		/* Select DataRAM for DDP */
1808ef0921d6SKyungmin Park 		value = onenand_bufferram_address(this, block);
1809ef0921d6SKyungmin Park 		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
1810ef0921d6SKyungmin Park 		/* Set start block address */
1811ef0921d6SKyungmin Park 		this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
1812ef0921d6SKyungmin Park 
1813ef0921d6SKyungmin Park 		/* Check lock status */
1814ef0921d6SKyungmin Park 		status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
1815ef0921d6SKyungmin Park 		if (!(status & ONENAND_WP_US)) {
1816ef0921d6SKyungmin Park 			printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
1817ef0921d6SKyungmin Park 			return 0;
1818ef0921d6SKyungmin Park 		}
1819ef0921d6SKyungmin Park 	}
1820ef0921d6SKyungmin Park 
1821ef0921d6SKyungmin Park 	return 1;
1822ef0921d6SKyungmin Park }
1823ef0921d6SKyungmin Park 
1824ef0921d6SKyungmin Park /**
1825ef0921d6SKyungmin Park  * onenand_unlock_all - [OneNAND Interface] unlock all blocks
1826ef0921d6SKyungmin Park  * @param mtd           MTD device structure
1827ef0921d6SKyungmin Park  *
1828ef0921d6SKyungmin Park  * Unlock all blocks
1829ef0921d6SKyungmin Park  */
1830ef0921d6SKyungmin Park static void onenand_unlock_all(struct mtd_info *mtd)
1831ef0921d6SKyungmin Park {
1832ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1833ef0921d6SKyungmin Park 	loff_t ofs = 0;
1834ef0921d6SKyungmin Park 	size_t len = this->chipsize;
1835ef0921d6SKyungmin Park 
1836ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL) {
1837ef0921d6SKyungmin Park 		/* Set start block address */
1838ef0921d6SKyungmin Park 		this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
1839ef0921d6SKyungmin Park 		/* Write unlock command */
1840ef0921d6SKyungmin Park 		this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
1841ef0921d6SKyungmin Park 
1842ef0921d6SKyungmin Park 		/* There's no return value */
1843ef0921d6SKyungmin Park 		this->wait(mtd, FL_LOCKING);
1844ef0921d6SKyungmin Park 
1845ef0921d6SKyungmin Park 		/* Sanity check */
1846ef0921d6SKyungmin Park 		while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
1847ef0921d6SKyungmin Park 				& ONENAND_CTRL_ONGO)
1848ef0921d6SKyungmin Park 			continue;
1849ef0921d6SKyungmin Park 
1850ef0921d6SKyungmin Park 		return;
1851ef0921d6SKyungmin Park 
1852ef0921d6SKyungmin Park 		/* Check lock status */
1853ef0921d6SKyungmin Park 		if (onenand_check_lock_status(this))
1854ef0921d6SKyungmin Park 			return;
1855ef0921d6SKyungmin Park 
1856ef0921d6SKyungmin Park 		/* Workaround for all block unlock in DDP */
1857ef0921d6SKyungmin Park 		if (ONENAND_IS_DDP(this)) {
1858ef0921d6SKyungmin Park 			/* All blocks on another chip */
1859ef0921d6SKyungmin Park 			ofs = this->chipsize >> 1;
1860ef0921d6SKyungmin Park 			len = this->chipsize >> 1;
1861ef0921d6SKyungmin Park 		}
1862ef0921d6SKyungmin Park 	}
1863ef0921d6SKyungmin Park 
1864ef0921d6SKyungmin Park 	onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK);
1865ef0921d6SKyungmin Park }
1866ef0921d6SKyungmin Park 
1867ef0921d6SKyungmin Park 
1868ef0921d6SKyungmin Park /**
1869ef0921d6SKyungmin Park  * onenand_check_features - Check and set OneNAND features
1870ef0921d6SKyungmin Park  * @param mtd           MTD data structure
1871ef0921d6SKyungmin Park  *
1872ef0921d6SKyungmin Park  * Check and set OneNAND features
1873ef0921d6SKyungmin Park  * - lock scheme
1874ef0921d6SKyungmin Park  * - two plane
1875ef0921d6SKyungmin Park  */
1876ef0921d6SKyungmin Park static void onenand_check_features(struct mtd_info *mtd)
1877ef0921d6SKyungmin Park {
1878ef0921d6SKyungmin Park 	struct onenand_chip *this = mtd->priv;
1879ef0921d6SKyungmin Park 	unsigned int density, process;
1880ef0921d6SKyungmin Park 
1881ef0921d6SKyungmin Park 	/* Lock scheme depends on density and process */
1882ef0921d6SKyungmin Park 	density = onenand_get_density(this->device_id);
1883ef0921d6SKyungmin Park 	process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
1884ef0921d6SKyungmin Park 
1885ef0921d6SKyungmin Park 	/* Lock scheme */
1886ef0921d6SKyungmin Park 	switch (density) {
1887ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_4Gb:
1888ef0921d6SKyungmin Park 		this->options |= ONENAND_HAS_2PLANE;
1889ef0921d6SKyungmin Park 
1890ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_2Gb:
1891ef0921d6SKyungmin Park 		/* 2Gb DDP don't have 2 plane */
1892ef0921d6SKyungmin Park 		if (!ONENAND_IS_DDP(this))
1893ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_2PLANE;
1894ef0921d6SKyungmin Park 		this->options |= ONENAND_HAS_UNLOCK_ALL;
1895ef0921d6SKyungmin Park 
1896ef0921d6SKyungmin Park 	case ONENAND_DEVICE_DENSITY_1Gb:
1897ef0921d6SKyungmin Park 		/* A-Die has all block unlock */
1898ef0921d6SKyungmin Park 		if (process)
1899ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_UNLOCK_ALL;
1900ef0921d6SKyungmin Park 		break;
1901ef0921d6SKyungmin Park 
1902ef0921d6SKyungmin Park 	default:
1903ef0921d6SKyungmin Park 		/* Some OneNAND has continuous lock scheme */
1904ef0921d6SKyungmin Park 		if (!process)
1905ef0921d6SKyungmin Park 			this->options |= ONENAND_HAS_CONT_LOCK;
1906ef0921d6SKyungmin Park 		break;
1907ef0921d6SKyungmin Park 	}
1908ef0921d6SKyungmin Park 
1909ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_CONT_LOCK)
1910ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
1911ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_UNLOCK_ALL)
1912ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip support all block unlock\n");
1913ef0921d6SKyungmin Park 	if (this->options & ONENAND_HAS_2PLANE)
1914ef0921d6SKyungmin Park 		printk(KERN_DEBUG "Chip has 2 plane\n");
1915ef0921d6SKyungmin Park }
1916ef0921d6SKyungmin Park 
1917ef0921d6SKyungmin Park /**
191859829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_print_device_info - Print device ID
191959829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param device        device ID
192059829cc1SJean-Christophe PLAGNIOL-VILLARD  *
192159829cc1SJean-Christophe PLAGNIOL-VILLARD  * Print device ID
192259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1923ef0921d6SKyungmin Park char *onenand_print_device_info(int device, int version)
192459829cc1SJean-Christophe PLAGNIOL-VILLARD {
192559829cc1SJean-Christophe PLAGNIOL-VILLARD 	int vcc, demuxed, ddp, density;
1926195ccfc5SFathi BOUDRA 	char *dev_info = malloc(80);
1927ef0921d6SKyungmin Park 	char *p = dev_info;
192859829cc1SJean-Christophe PLAGNIOL-VILLARD 
192959829cc1SJean-Christophe PLAGNIOL-VILLARD 	vcc = device & ONENAND_DEVICE_VCC_MASK;
193059829cc1SJean-Christophe PLAGNIOL-VILLARD 	demuxed = device & ONENAND_DEVICE_IS_DEMUX;
193159829cc1SJean-Christophe PLAGNIOL-VILLARD 	ddp = device & ONENAND_DEVICE_IS_DDP;
193259829cc1SJean-Christophe PLAGNIOL-VILLARD 	density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
1933ef0921d6SKyungmin Park 	p += sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
193459829cc1SJean-Christophe PLAGNIOL-VILLARD 	       demuxed ? "" : "Muxed ",
193559829cc1SJean-Christophe PLAGNIOL-VILLARD 	       ddp ? "(DDP)" : "",
193659829cc1SJean-Christophe PLAGNIOL-VILLARD 	       (16 << density), vcc ? "2.65/3.3" : "1.8", device);
1937195ccfc5SFathi BOUDRA 
1938ef0921d6SKyungmin Park 	sprintf(p, "\nOneNAND version = 0x%04x", version);
1939ef0921d6SKyungmin Park 	printk("%s\n", dev_info);
1940ef0921d6SKyungmin Park 
1941195ccfc5SFathi BOUDRA 	return dev_info;
194259829cc1SJean-Christophe PLAGNIOL-VILLARD }
194359829cc1SJean-Christophe PLAGNIOL-VILLARD 
194459829cc1SJean-Christophe PLAGNIOL-VILLARD static const struct onenand_manufacturers onenand_manuf_ids[] = {
194559829cc1SJean-Christophe PLAGNIOL-VILLARD 	{ONENAND_MFR_SAMSUNG, "Samsung"},
194659829cc1SJean-Christophe PLAGNIOL-VILLARD };
194759829cc1SJean-Christophe PLAGNIOL-VILLARD 
194859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
194959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_check_maf - Check manufacturer ID
195059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param manuf         manufacturer ID
195159829cc1SJean-Christophe PLAGNIOL-VILLARD  *
195259829cc1SJean-Christophe PLAGNIOL-VILLARD  * Check manufacturer ID
195359829cc1SJean-Christophe PLAGNIOL-VILLARD  */
195459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_maf(int manuf)
195559829cc1SJean-Christophe PLAGNIOL-VILLARD {
1956ef0921d6SKyungmin Park 	int size = ARRAY_SIZE(onenand_manuf_ids);
1957ef0921d6SKyungmin Park 	char *name;
195859829cc1SJean-Christophe PLAGNIOL-VILLARD 	int i;
195959829cc1SJean-Christophe PLAGNIOL-VILLARD 
1960ef0921d6SKyungmin Park 	for (i = 0; size; i++)
196159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (manuf == onenand_manuf_ids[i].id)
196259829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
1963ef0921d6SKyungmin Park 
1964ef0921d6SKyungmin Park 	if (i < size)
1965ef0921d6SKyungmin Park 		name = onenand_manuf_ids[i].name;
1966ef0921d6SKyungmin Park 	else
1967ef0921d6SKyungmin Park 		name = "Unknown";
196859829cc1SJean-Christophe PLAGNIOL-VILLARD 
196959829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef ONENAND_DEBUG
1970ef0921d6SKyungmin Park 	printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
197159829cc1SJean-Christophe PLAGNIOL-VILLARD #endif
197259829cc1SJean-Christophe PLAGNIOL-VILLARD 
1973ef0921d6SKyungmin Park 	return i == size;
197459829cc1SJean-Christophe PLAGNIOL-VILLARD }
197559829cc1SJean-Christophe PLAGNIOL-VILLARD 
197659829cc1SJean-Christophe PLAGNIOL-VILLARD /**
197759829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_probe - [OneNAND Interface] Probe the OneNAND device
197859829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
197959829cc1SJean-Christophe PLAGNIOL-VILLARD  *
198059829cc1SJean-Christophe PLAGNIOL-VILLARD  * OneNAND detection method:
198159829cc1SJean-Christophe PLAGNIOL-VILLARD  *   Compare the the values from command with ones from register
198259829cc1SJean-Christophe PLAGNIOL-VILLARD  */
198359829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_probe(struct mtd_info *mtd)
198459829cc1SJean-Christophe PLAGNIOL-VILLARD {
198559829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
1986ef0921d6SKyungmin Park 	int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
198759829cc1SJean-Christophe PLAGNIOL-VILLARD 	int density;
1988ef0921d6SKyungmin Park 	int syscfg;
1989ef0921d6SKyungmin Park 
1990ef0921d6SKyungmin Park 	/* Save system configuration 1 */
1991ef0921d6SKyungmin Park 	syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
1992ef0921d6SKyungmin Park 	/* Clear Sync. Burst Read mode to read BootRAM */
1993ef0921d6SKyungmin Park 	this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
199459829cc1SJean-Christophe PLAGNIOL-VILLARD 
199559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Send the command for reading device ID from BootRAM */
199659829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
199759829cc1SJean-Christophe PLAGNIOL-VILLARD 
199859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from BootRAM */
199959829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
200059829cc1SJean-Christophe PLAGNIOL-VILLARD 	bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
200159829cc1SJean-Christophe PLAGNIOL-VILLARD 
200259829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Reset OneNAND to read default register values */
200359829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
200459829cc1SJean-Christophe PLAGNIOL-VILLARD 
2005d438d508SKyungmin Park 	/* Wait reset */
2006d438d508SKyungmin Park 	this->wait(mtd, FL_RESETING);
200759829cc1SJean-Christophe PLAGNIOL-VILLARD 
2008ef0921d6SKyungmin Park 	/* Restore system configuration 1 */
2009ef0921d6SKyungmin Park 	this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
2010ef0921d6SKyungmin Park 
2011ef0921d6SKyungmin Park 	/* Check manufacturer ID */
2012ef0921d6SKyungmin Park 	if (onenand_check_maf(bram_maf_id))
2013ef0921d6SKyungmin Park 		return -ENXIO;
2014ef0921d6SKyungmin Park 
201559829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Read manufacturer and device IDs from Register */
201659829cc1SJean-Christophe PLAGNIOL-VILLARD 	maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
201759829cc1SJean-Christophe PLAGNIOL-VILLARD 	dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
2018ef0921d6SKyungmin Park 	ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
201959829cc1SJean-Christophe PLAGNIOL-VILLARD 
202059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Check OneNAND device */
202159829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (maf_id != bram_maf_id || dev_id != bram_dev_id)
202259829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
202359829cc1SJean-Christophe PLAGNIOL-VILLARD 
20241bb707c3SKyungmin Park 	/* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */
20251bb707c3SKyungmin Park 	if (dev_id & (1 << 9)) {
20261bb707c3SKyungmin Park 		printk("Not yet support Flex-OneNAND\n");
20271bb707c3SKyungmin Park 		return -ENXIO;
20281bb707c3SKyungmin Park 	}
20291bb707c3SKyungmin Park 
203059829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Flash device information */
2031ef0921d6SKyungmin Park 	mtd->name = onenand_print_device_info(dev_id, ver_id);
203259829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->device_id = dev_id;
20338cf11f3aSStefan Roese 	this->version_id = ver_id;
203459829cc1SJean-Christophe PLAGNIOL-VILLARD 
2035ef0921d6SKyungmin Park 	density = onenand_get_density(dev_id);
203659829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->chipsize = (16 << density) << 20;
2037ef0921d6SKyungmin Park 	/* Set density mask. it is used for DDP */
2038ef0921d6SKyungmin Park 	if (ONENAND_IS_DDP(this))
2039ef0921d6SKyungmin Park 		this->density_mask = (1 << (density + 6));
2040ef0921d6SKyungmin Park 	else
2041ef0921d6SKyungmin Park 		this->density_mask = 0;
204259829cc1SJean-Christophe PLAGNIOL-VILLARD 
204359829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* OneNAND page size & block size */
204459829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* The data buffer size is equal to page size */
2045d438d508SKyungmin Park 	mtd->writesize =
204659829cc1SJean-Christophe PLAGNIOL-VILLARD 	    this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
2047d438d508SKyungmin Park 	mtd->oobsize = mtd->writesize >> 5;
204859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Pagers per block is always 64 in OneNAND */
2049d438d508SKyungmin Park 	mtd->erasesize = mtd->writesize << 6;
205059829cc1SJean-Christophe PLAGNIOL-VILLARD 
205159829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->erase_shift = ffs(mtd->erasesize) - 1;
2052d438d508SKyungmin Park 	this->page_shift = ffs(mtd->writesize) - 1;
205359829cc1SJean-Christophe PLAGNIOL-VILLARD 	this->ppb_shift = (this->erase_shift - this->page_shift);
2054d438d508SKyungmin Park 	this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
2055bfd7f386SKyungmin Park 	/* It's real page size */
2056bfd7f386SKyungmin Park 	this->writesize = mtd->writesize;
205759829cc1SJean-Christophe PLAGNIOL-VILLARD 
205859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* REVIST: Multichip handling */
205959829cc1SJean-Christophe PLAGNIOL-VILLARD 
206059829cc1SJean-Christophe PLAGNIOL-VILLARD 	mtd->size = this->chipsize;
206159829cc1SJean-Christophe PLAGNIOL-VILLARD 
2062ef0921d6SKyungmin Park 	/* Check OneNAND features */
2063ef0921d6SKyungmin Park 	onenand_check_features(mtd);
206459829cc1SJean-Christophe PLAGNIOL-VILLARD 
2065d438d508SKyungmin Park 	mtd->flags = MTD_CAP_NANDFLASH;
2066195ccfc5SFathi BOUDRA 	mtd->erase = onenand_erase;
2067195ccfc5SFathi BOUDRA 	mtd->read = onenand_read;
2068195ccfc5SFathi BOUDRA 	mtd->write = onenand_write;
2069195ccfc5SFathi BOUDRA 	mtd->read_oob = onenand_read_oob;
2070195ccfc5SFathi BOUDRA 	mtd->write_oob = onenand_write_oob;
2071195ccfc5SFathi BOUDRA 	mtd->sync = onenand_sync;
2072195ccfc5SFathi BOUDRA 	mtd->block_isbad = onenand_block_isbad;
2073195ccfc5SFathi BOUDRA 	mtd->block_markbad = onenand_block_markbad;
2074195ccfc5SFathi BOUDRA 
207559829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
207659829cc1SJean-Christophe PLAGNIOL-VILLARD }
207759829cc1SJean-Christophe PLAGNIOL-VILLARD 
207859829cc1SJean-Christophe PLAGNIOL-VILLARD /**
207959829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_scan - [OneNAND Interface] Scan for the OneNAND device
208059829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
208159829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param maxchips	Number of chips to scan for
208259829cc1SJean-Christophe PLAGNIOL-VILLARD  *
208359829cc1SJean-Christophe PLAGNIOL-VILLARD  * This fills out all the not initialized function pointers
208459829cc1SJean-Christophe PLAGNIOL-VILLARD  * with the defaults.
208559829cc1SJean-Christophe PLAGNIOL-VILLARD  * The flash ID is read and the mtd/chip structures are
208659829cc1SJean-Christophe PLAGNIOL-VILLARD  * filled with the appropriate values.
208759829cc1SJean-Christophe PLAGNIOL-VILLARD  */
208859829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_scan(struct mtd_info *mtd, int maxchips)
208959829cc1SJean-Christophe PLAGNIOL-VILLARD {
20901ae39862SStefan Roese 	int i;
209159829cc1SJean-Christophe PLAGNIOL-VILLARD 	struct onenand_chip *this = mtd->priv;
209259829cc1SJean-Christophe PLAGNIOL-VILLARD 
209359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_word)
209459829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_word = onenand_readw;
209559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_word)
209659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_word = onenand_writew;
209759829cc1SJean-Christophe PLAGNIOL-VILLARD 
209859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->command)
209959829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->command = onenand_command;
210059829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->wait)
210159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->wait = onenand_wait;
2102ef0921d6SKyungmin Park 	if (!this->bbt_wait)
2103ef0921d6SKyungmin Park 		this->bbt_wait = onenand_bbt_wait;
210459829cc1SJean-Christophe PLAGNIOL-VILLARD 
210559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->read_bufferram)
210659829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_read_bufferram;
210759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (!this->write_bufferram)
210859829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->write_bufferram = onenand_write_bufferram;
210959829cc1SJean-Christophe PLAGNIOL-VILLARD 
21101714f51aSKyungmin Park 	if (!this->block_markbad)
21111714f51aSKyungmin Park 		this->block_markbad = onenand_default_block_markbad;
2112ef0921d6SKyungmin Park 	if (!this->scan_bbt)
2113ef0921d6SKyungmin Park 		this->scan_bbt = onenand_default_bbt;
2114ef0921d6SKyungmin Park 
211559829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (onenand_probe(mtd))
211659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -ENXIO;
211759829cc1SJean-Christophe PLAGNIOL-VILLARD 
211859829cc1SJean-Christophe PLAGNIOL-VILLARD 	/* Set Sync. Burst Read after probing */
211959829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (this->mmcontrol) {
212059829cc1SJean-Christophe PLAGNIOL-VILLARD 		printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
212159829cc1SJean-Christophe PLAGNIOL-VILLARD 		this->read_bufferram = onenand_sync_read_bufferram;
212259829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
212359829cc1SJean-Christophe PLAGNIOL-VILLARD 
2124bfd7f386SKyungmin Park 	/* Allocate buffers, if necessary */
2125bfd7f386SKyungmin Park 	if (!this->page_buf) {
2126bfd7f386SKyungmin Park 		this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
2127bfd7f386SKyungmin Park 		if (!this->page_buf) {
2128bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n");
2129bfd7f386SKyungmin Park 			return -ENOMEM;
2130bfd7f386SKyungmin Park 		}
2131bfd7f386SKyungmin Park 		this->options |= ONENAND_PAGEBUF_ALLOC;
2132bfd7f386SKyungmin Park 	}
2133bfd7f386SKyungmin Park 	if (!this->oob_buf) {
2134bfd7f386SKyungmin Park 		this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
2135bfd7f386SKyungmin Park 		if (!this->oob_buf) {
2136bfd7f386SKyungmin Park 			printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n");
2137bfd7f386SKyungmin Park 			if (this->options & ONENAND_PAGEBUF_ALLOC) {
2138bfd7f386SKyungmin Park 				this->options &= ~ONENAND_PAGEBUF_ALLOC;
2139bfd7f386SKyungmin Park 				kfree(this->page_buf);
2140bfd7f386SKyungmin Park 			}
2141bfd7f386SKyungmin Park 			return -ENOMEM;
2142bfd7f386SKyungmin Park 		}
2143bfd7f386SKyungmin Park 		this->options |= ONENAND_OOBBUF_ALLOC;
2144bfd7f386SKyungmin Park 	}
2145bfd7f386SKyungmin Park 
21461ae39862SStefan Roese 	this->state = FL_READY;
21471ae39862SStefan Roese 
21481ae39862SStefan Roese 	/*
21491ae39862SStefan Roese 	 * Allow subpage writes up to oobsize.
21501ae39862SStefan Roese 	 */
21511ae39862SStefan Roese 	switch (mtd->oobsize) {
21521ae39862SStefan Roese 	case 64:
21531ae39862SStefan Roese 		this->ecclayout = &onenand_oob_64;
21541ae39862SStefan Roese 		mtd->subpage_sft = 2;
21551ae39862SStefan Roese 		break;
21561ae39862SStefan Roese 
21571ae39862SStefan Roese 	case 32:
21581ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
21591ae39862SStefan Roese 		mtd->subpage_sft = 1;
21601ae39862SStefan Roese 		break;
21611ae39862SStefan Roese 
21621ae39862SStefan Roese 	default:
21631ae39862SStefan Roese 		printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n",
21641ae39862SStefan Roese 			mtd->oobsize);
21651ae39862SStefan Roese 		mtd->subpage_sft = 0;
21661ae39862SStefan Roese 		/* To prevent kernel oops */
21671ae39862SStefan Roese 		this->ecclayout = &onenand_oob_32;
21681ae39862SStefan Roese 		break;
21691ae39862SStefan Roese 	}
21701ae39862SStefan Roese 
21711ae39862SStefan Roese 	this->subpagesize = mtd->writesize >> mtd->subpage_sft;
21721ae39862SStefan Roese 
21731ae39862SStefan Roese 	/*
21741ae39862SStefan Roese 	 * The number of bytes available for a client to place data into
21751ae39862SStefan Roese 	 * the out of band area
21761ae39862SStefan Roese 	 */
21771ae39862SStefan Roese 	this->ecclayout->oobavail = 0;
21781ae39862SStefan Roese 	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
21791ae39862SStefan Roese 	    this->ecclayout->oobfree[i].length; i++)
21801ae39862SStefan Roese 		this->ecclayout->oobavail +=
21811ae39862SStefan Roese 			this->ecclayout->oobfree[i].length;
21821ae39862SStefan Roese 	mtd->oobavail = this->ecclayout->oobavail;
21831ae39862SStefan Roese 
21841ae39862SStefan Roese 	mtd->ecclayout = this->ecclayout;
21851ae39862SStefan Roese 
2186ef0921d6SKyungmin Park 	/* Unlock whole block */
2187ef0921d6SKyungmin Park 	onenand_unlock_all(mtd);
218859829cc1SJean-Christophe PLAGNIOL-VILLARD 
2189ef0921d6SKyungmin Park 	return this->scan_bbt(mtd);
219059829cc1SJean-Christophe PLAGNIOL-VILLARD }
219159829cc1SJean-Christophe PLAGNIOL-VILLARD 
219259829cc1SJean-Christophe PLAGNIOL-VILLARD /**
219359829cc1SJean-Christophe PLAGNIOL-VILLARD  * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device
219459829cc1SJean-Christophe PLAGNIOL-VILLARD  * @param mtd		MTD device structure
219559829cc1SJean-Christophe PLAGNIOL-VILLARD  */
219659829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_release(struct mtd_info *mtd)
219759829cc1SJean-Christophe PLAGNIOL-VILLARD {
219859829cc1SJean-Christophe PLAGNIOL-VILLARD }
2199