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