159829cc1SJean-Christophe PLAGNIOL-VILLARD /* 259829cc1SJean-Christophe PLAGNIOL-VILLARD * linux/drivers/mtd/onenand/onenand_base.c 359829cc1SJean-Christophe PLAGNIOL-VILLARD * 459829cc1SJean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2005-2007 Samsung Electronics 559829cc1SJean-Christophe PLAGNIOL-VILLARD * Kyungmin Park <kyungmin.park@samsung.com> 659829cc1SJean-Christophe PLAGNIOL-VILLARD * 7bfd7f386SKyungmin Park * Credits: 8bfd7f386SKyungmin Park * Adrian Hunter <ext-adrian.hunter@nokia.com>: 9bfd7f386SKyungmin Park * auto-placement support, read-while load support, various fixes 10bfd7f386SKyungmin Park * Copyright (C) Nokia Corporation, 2007 11bfd7f386SKyungmin Park * 12cacbe919SAmul Kumar Saha * Rohit Hagargundgi <h.rohit at samsung.com>, 13cacbe919SAmul Kumar Saha * Amul Kumar Saha <amul.saha@samsung.com>: 14cacbe919SAmul Kumar Saha * Flex-OneNAND support 15cacbe919SAmul Kumar Saha * Copyright (C) Samsung Electronics, 2009 16cacbe919SAmul Kumar Saha * 1759829cc1SJean-Christophe PLAGNIOL-VILLARD * This program is free software; you can redistribute it and/or modify 1859829cc1SJean-Christophe PLAGNIOL-VILLARD * it under the terms of the GNU General Public License version 2 as 1959829cc1SJean-Christophe PLAGNIOL-VILLARD * published by the Free Software Foundation. 2059829cc1SJean-Christophe PLAGNIOL-VILLARD */ 2159829cc1SJean-Christophe PLAGNIOL-VILLARD 2259829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h> 237b15e2bbSMike Frysinger #include <linux/compat.h> 2459829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/mtd.h> 2559829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/onenand.h> 2659829cc1SJean-Christophe PLAGNIOL-VILLARD 2759829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h> 2859829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/errno.h> 29195ccfc5SFathi BOUDRA #include <malloc.h> 3059829cc1SJean-Christophe PLAGNIOL-VILLARD 3177e475ccSKyungmin Park /* It should access 16-bit instead of 8-bit */ 32cacbe919SAmul Kumar Saha static void *memcpy_16(void *dst, const void *src, unsigned int len) 3377e475ccSKyungmin Park { 3477e475ccSKyungmin Park void *ret = dst; 3577e475ccSKyungmin Park short *d = dst; 3677e475ccSKyungmin Park const short *s = src; 3777e475ccSKyungmin Park 3877e475ccSKyungmin Park len >>= 1; 3977e475ccSKyungmin Park while (len-- > 0) 4077e475ccSKyungmin Park *d++ = *s++; 4177e475ccSKyungmin Park return ret; 4277e475ccSKyungmin Park } 4377e475ccSKyungmin Park 441ae39862SStefan Roese /** 45cacbe919SAmul Kumar Saha * onenand_oob_128 - oob info for Flex-Onenand with 4KB page 46cacbe919SAmul Kumar Saha * For now, we expose only 64 out of 80 ecc bytes 47cacbe919SAmul Kumar Saha */ 48cacbe919SAmul Kumar Saha static struct nand_ecclayout onenand_oob_128 = { 49cacbe919SAmul Kumar Saha .eccbytes = 64, 50cacbe919SAmul Kumar Saha .eccpos = { 51cacbe919SAmul Kumar Saha 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 52cacbe919SAmul Kumar Saha 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 53cacbe919SAmul Kumar Saha 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 54cacbe919SAmul Kumar Saha 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 55cacbe919SAmul Kumar Saha 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 56cacbe919SAmul Kumar Saha 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 57cacbe919SAmul Kumar Saha 102, 103, 104, 105 58cacbe919SAmul Kumar Saha }, 59cacbe919SAmul Kumar Saha .oobfree = { 60cacbe919SAmul Kumar Saha {2, 4}, {18, 4}, {34, 4}, {50, 4}, 61cacbe919SAmul Kumar Saha {66, 4}, {82, 4}, {98, 4}, {114, 4} 62cacbe919SAmul Kumar Saha } 63cacbe919SAmul Kumar Saha }; 64cacbe919SAmul Kumar Saha 65cacbe919SAmul Kumar Saha /** 661ae39862SStefan Roese * onenand_oob_64 - oob info for large (2KB) page 671ae39862SStefan Roese */ 681ae39862SStefan Roese static struct nand_ecclayout onenand_oob_64 = { 691ae39862SStefan Roese .eccbytes = 20, 701ae39862SStefan Roese .eccpos = { 711ae39862SStefan Roese 8, 9, 10, 11, 12, 721ae39862SStefan Roese 24, 25, 26, 27, 28, 731ae39862SStefan Roese 40, 41, 42, 43, 44, 741ae39862SStefan Roese 56, 57, 58, 59, 60, 751ae39862SStefan Roese }, 761ae39862SStefan Roese .oobfree = { 771ae39862SStefan Roese {2, 3}, {14, 2}, {18, 3}, {30, 2}, 781ae39862SStefan Roese {34, 3}, {46, 2}, {50, 3}, {62, 2} 791ae39862SStefan Roese } 801ae39862SStefan Roese }; 811ae39862SStefan Roese 821ae39862SStefan Roese /** 831ae39862SStefan Roese * onenand_oob_32 - oob info for middle (1KB) page 841ae39862SStefan Roese */ 851ae39862SStefan Roese static struct nand_ecclayout onenand_oob_32 = { 861ae39862SStefan Roese .eccbytes = 10, 871ae39862SStefan Roese .eccpos = { 881ae39862SStefan Roese 8, 9, 10, 11, 12, 891ae39862SStefan Roese 24, 25, 26, 27, 28, 901ae39862SStefan Roese }, 911ae39862SStefan Roese .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} } 921ae39862SStefan Roese }; 931ae39862SStefan Roese 9459829cc1SJean-Christophe PLAGNIOL-VILLARD static const unsigned char ffchars[] = { 9559829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 9659829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ 9759829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 9859829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ 9959829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 10059829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 10159829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 10259829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ 103cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 104cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ 105cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 106cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ 107cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 108cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ 109cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 110cacbe919SAmul Kumar Saha 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ 11159829cc1SJean-Christophe PLAGNIOL-VILLARD }; 11259829cc1SJean-Christophe PLAGNIOL-VILLARD 11359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 11459829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_readw - [OneNAND Interface] Read OneNAND register 11559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to read 11659829cc1SJean-Christophe PLAGNIOL-VILLARD * 11759829cc1SJean-Christophe PLAGNIOL-VILLARD * Read OneNAND register 11859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 11959829cc1SJean-Christophe PLAGNIOL-VILLARD static unsigned short onenand_readw(void __iomem * addr) 12059829cc1SJean-Christophe PLAGNIOL-VILLARD { 12159829cc1SJean-Christophe PLAGNIOL-VILLARD return readw(addr); 12259829cc1SJean-Christophe PLAGNIOL-VILLARD } 12359829cc1SJean-Christophe PLAGNIOL-VILLARD 12459829cc1SJean-Christophe PLAGNIOL-VILLARD /** 12559829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_writew - [OneNAND Interface] Write OneNAND register with value 12659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param value value to write 12759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to write 12859829cc1SJean-Christophe PLAGNIOL-VILLARD * 12959829cc1SJean-Christophe PLAGNIOL-VILLARD * Write OneNAND register with value 13059829cc1SJean-Christophe PLAGNIOL-VILLARD */ 13159829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_writew(unsigned short value, void __iomem * addr) 13259829cc1SJean-Christophe PLAGNIOL-VILLARD { 13359829cc1SJean-Christophe PLAGNIOL-VILLARD writew(value, addr); 13459829cc1SJean-Christophe PLAGNIOL-VILLARD } 13559829cc1SJean-Christophe PLAGNIOL-VILLARD 13659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 13759829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_block_address - [DEFAULT] Get block address 13859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param device the device id 13959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param block the block 14059829cc1SJean-Christophe PLAGNIOL-VILLARD * @return translated block address if DDP, otherwise same 14159829cc1SJean-Christophe PLAGNIOL-VILLARD * 14259829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Address 1 Register (F100h) 14359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 144ef0921d6SKyungmin Park static int onenand_block_address(struct onenand_chip *this, int block) 14559829cc1SJean-Christophe PLAGNIOL-VILLARD { 14659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Device Flash Core select, NAND Flash Block Address */ 147ef0921d6SKyungmin Park if (block & this->density_mask) 148ef0921d6SKyungmin Park return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); 14959829cc1SJean-Christophe PLAGNIOL-VILLARD 15059829cc1SJean-Christophe PLAGNIOL-VILLARD return block; 15159829cc1SJean-Christophe PLAGNIOL-VILLARD } 15259829cc1SJean-Christophe PLAGNIOL-VILLARD 15359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 15459829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_bufferram_address - [DEFAULT] Get bufferram address 15559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param device the device id 15659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param block the block 15759829cc1SJean-Christophe PLAGNIOL-VILLARD * @return set DBS value if DDP, otherwise 0 15859829cc1SJean-Christophe PLAGNIOL-VILLARD * 15959829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Address 2 Register (F101h) for DDP 16059829cc1SJean-Christophe PLAGNIOL-VILLARD */ 161ef0921d6SKyungmin Park static int onenand_bufferram_address(struct onenand_chip *this, int block) 16259829cc1SJean-Christophe PLAGNIOL-VILLARD { 16359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Device BufferRAM Select */ 164ef0921d6SKyungmin Park if (block & this->density_mask) 165ef0921d6SKyungmin Park return ONENAND_DDP_CHIP1; 16659829cc1SJean-Christophe PLAGNIOL-VILLARD 167ef0921d6SKyungmin Park return ONENAND_DDP_CHIP0; 16859829cc1SJean-Christophe PLAGNIOL-VILLARD } 16959829cc1SJean-Christophe PLAGNIOL-VILLARD 17059829cc1SJean-Christophe PLAGNIOL-VILLARD /** 17159829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_page_address - [DEFAULT] Get page address 17259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param page the page address 17359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param sector the sector address 17459829cc1SJean-Christophe PLAGNIOL-VILLARD * @return combined page and sector address 17559829cc1SJean-Christophe PLAGNIOL-VILLARD * 17659829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Address 8 Register (F107h) 17759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 17859829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_page_address(int page, int sector) 17959829cc1SJean-Christophe PLAGNIOL-VILLARD { 18059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Flash Page Address, Flash Sector Address */ 18159829cc1SJean-Christophe PLAGNIOL-VILLARD int fpa, fsa; 18259829cc1SJean-Christophe PLAGNIOL-VILLARD 18359829cc1SJean-Christophe PLAGNIOL-VILLARD fpa = page & ONENAND_FPA_MASK; 18459829cc1SJean-Christophe PLAGNIOL-VILLARD fsa = sector & ONENAND_FSA_MASK; 18559829cc1SJean-Christophe PLAGNIOL-VILLARD 18659829cc1SJean-Christophe PLAGNIOL-VILLARD return ((fpa << ONENAND_FPA_SHIFT) | fsa); 18759829cc1SJean-Christophe PLAGNIOL-VILLARD } 18859829cc1SJean-Christophe PLAGNIOL-VILLARD 18959829cc1SJean-Christophe PLAGNIOL-VILLARD /** 19059829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_buffer_address - [DEFAULT] Get buffer address 19159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param dataram1 DataRAM index 19259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param sectors the sector address 19359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count the number of sectors 19459829cc1SJean-Christophe PLAGNIOL-VILLARD * @return the start buffer value 19559829cc1SJean-Christophe PLAGNIOL-VILLARD * 19659829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Buffer Register (F200h) 19759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 19859829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_buffer_address(int dataram1, int sectors, int count) 19959829cc1SJean-Christophe PLAGNIOL-VILLARD { 20059829cc1SJean-Christophe PLAGNIOL-VILLARD int bsa, bsc; 20159829cc1SJean-Christophe PLAGNIOL-VILLARD 20259829cc1SJean-Christophe PLAGNIOL-VILLARD /* BufferRAM Sector Address */ 20359829cc1SJean-Christophe PLAGNIOL-VILLARD bsa = sectors & ONENAND_BSA_MASK; 20459829cc1SJean-Christophe PLAGNIOL-VILLARD 20559829cc1SJean-Christophe PLAGNIOL-VILLARD if (dataram1) 20659829cc1SJean-Christophe PLAGNIOL-VILLARD bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ 20759829cc1SJean-Christophe PLAGNIOL-VILLARD else 20859829cc1SJean-Christophe PLAGNIOL-VILLARD bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ 20959829cc1SJean-Christophe PLAGNIOL-VILLARD 21059829cc1SJean-Christophe PLAGNIOL-VILLARD /* BufferRAM Sector Count */ 21159829cc1SJean-Christophe PLAGNIOL-VILLARD bsc = count & ONENAND_BSC_MASK; 21259829cc1SJean-Christophe PLAGNIOL-VILLARD 21359829cc1SJean-Christophe PLAGNIOL-VILLARD return ((bsa << ONENAND_BSA_SHIFT) | bsc); 21459829cc1SJean-Christophe PLAGNIOL-VILLARD } 21559829cc1SJean-Christophe PLAGNIOL-VILLARD 21659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 217cacbe919SAmul Kumar Saha * flexonenand_block - Return block number for flash address 218cacbe919SAmul Kumar Saha * @param this - OneNAND device structure 219cacbe919SAmul Kumar Saha * @param addr - Address for which block number is needed 220cacbe919SAmul Kumar Saha */ 221cacbe919SAmul Kumar Saha static unsigned int flexonenand_block(struct onenand_chip *this, loff_t addr) 222cacbe919SAmul Kumar Saha { 223cacbe919SAmul Kumar Saha unsigned int boundary, blk, die = 0; 224cacbe919SAmul Kumar Saha 225cacbe919SAmul Kumar Saha if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { 226cacbe919SAmul Kumar Saha die = 1; 227cacbe919SAmul Kumar Saha addr -= this->diesize[0]; 228cacbe919SAmul Kumar Saha } 229cacbe919SAmul Kumar Saha 230cacbe919SAmul Kumar Saha boundary = this->boundary[die]; 231cacbe919SAmul Kumar Saha 232cacbe919SAmul Kumar Saha blk = addr >> (this->erase_shift - 1); 233cacbe919SAmul Kumar Saha if (blk > boundary) 234cacbe919SAmul Kumar Saha blk = (blk + boundary + 1) >> 1; 235cacbe919SAmul Kumar Saha 236cacbe919SAmul Kumar Saha blk += die ? this->density_mask : 0; 237cacbe919SAmul Kumar Saha return blk; 238cacbe919SAmul Kumar Saha } 239cacbe919SAmul Kumar Saha 240cacbe919SAmul Kumar Saha unsigned int onenand_block(struct onenand_chip *this, loff_t addr) 241cacbe919SAmul Kumar Saha { 242cacbe919SAmul Kumar Saha if (!FLEXONENAND(this)) 243cacbe919SAmul Kumar Saha return addr >> this->erase_shift; 244cacbe919SAmul Kumar Saha return flexonenand_block(this, addr); 245cacbe919SAmul Kumar Saha } 246cacbe919SAmul Kumar Saha 247cacbe919SAmul Kumar Saha /** 248cacbe919SAmul Kumar Saha * flexonenand_addr - Return address of the block 249cacbe919SAmul Kumar Saha * @this: OneNAND device structure 250cacbe919SAmul Kumar Saha * @block: Block number on Flex-OneNAND 251cacbe919SAmul Kumar Saha * 252cacbe919SAmul Kumar Saha * Return address of the block 253cacbe919SAmul Kumar Saha */ 254cacbe919SAmul Kumar Saha static loff_t flexonenand_addr(struct onenand_chip *this, int block) 255cacbe919SAmul Kumar Saha { 256cacbe919SAmul Kumar Saha loff_t ofs = 0; 257cacbe919SAmul Kumar Saha int die = 0, boundary; 258cacbe919SAmul Kumar Saha 259cacbe919SAmul Kumar Saha if (ONENAND_IS_DDP(this) && block >= this->density_mask) { 260cacbe919SAmul Kumar Saha block -= this->density_mask; 261cacbe919SAmul Kumar Saha die = 1; 262cacbe919SAmul Kumar Saha ofs = this->diesize[0]; 263cacbe919SAmul Kumar Saha } 264cacbe919SAmul Kumar Saha 265cacbe919SAmul Kumar Saha boundary = this->boundary[die]; 266cacbe919SAmul Kumar Saha ofs += (loff_t) block << (this->erase_shift - 1); 267cacbe919SAmul Kumar Saha if (block > (boundary + 1)) 268cacbe919SAmul Kumar Saha ofs += (loff_t) (block - boundary - 1) 269cacbe919SAmul Kumar Saha << (this->erase_shift - 1); 270cacbe919SAmul Kumar Saha return ofs; 271cacbe919SAmul Kumar Saha } 272cacbe919SAmul Kumar Saha 273cacbe919SAmul Kumar Saha loff_t onenand_addr(struct onenand_chip *this, int block) 274cacbe919SAmul Kumar Saha { 275cacbe919SAmul Kumar Saha if (!FLEXONENAND(this)) 276cacbe919SAmul Kumar Saha return (loff_t) block << this->erase_shift; 277cacbe919SAmul Kumar Saha return flexonenand_addr(this, block); 278cacbe919SAmul Kumar Saha } 279cacbe919SAmul Kumar Saha 280cacbe919SAmul Kumar Saha /** 281cacbe919SAmul Kumar Saha * flexonenand_region - [Flex-OneNAND] Return erase region of addr 282cacbe919SAmul Kumar Saha * @param mtd MTD device structure 283cacbe919SAmul Kumar Saha * @param addr address whose erase region needs to be identified 284cacbe919SAmul Kumar Saha */ 285cacbe919SAmul Kumar Saha int flexonenand_region(struct mtd_info *mtd, loff_t addr) 286cacbe919SAmul Kumar Saha { 287cacbe919SAmul Kumar Saha int i; 288cacbe919SAmul Kumar Saha 289cacbe919SAmul Kumar Saha for (i = 0; i < mtd->numeraseregions; i++) 290cacbe919SAmul Kumar Saha if (addr < mtd->eraseregions[i].offset) 291cacbe919SAmul Kumar Saha break; 292cacbe919SAmul Kumar Saha return i - 1; 293cacbe919SAmul Kumar Saha } 294cacbe919SAmul Kumar Saha 295cacbe919SAmul Kumar Saha /** 296ef0921d6SKyungmin Park * onenand_get_density - [DEFAULT] Get OneNAND density 297ef0921d6SKyungmin Park * @param dev_id OneNAND device ID 298ef0921d6SKyungmin Park * 299ef0921d6SKyungmin Park * Get OneNAND density from device ID 300ef0921d6SKyungmin Park */ 301ef0921d6SKyungmin Park static inline int onenand_get_density(int dev_id) 302ef0921d6SKyungmin Park { 303ef0921d6SKyungmin Park int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; 304ef0921d6SKyungmin Park return (density & ONENAND_DEVICE_DENSITY_MASK); 305ef0921d6SKyungmin Park } 306ef0921d6SKyungmin Park 307ef0921d6SKyungmin Park /** 30859829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_command - [DEFAULT] Send command to OneNAND device 30959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 31059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param cmd the command to be sent 31159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr offset to read from or write to 31259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param len number of bytes to read or write 31359829cc1SJean-Christophe PLAGNIOL-VILLARD * 31459829cc1SJean-Christophe PLAGNIOL-VILLARD * Send command to OneNAND device. This function is used for middle/large page 31559829cc1SJean-Christophe PLAGNIOL-VILLARD * devices (1KB/2KB Bytes per page) 31659829cc1SJean-Christophe PLAGNIOL-VILLARD */ 31759829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, 31859829cc1SJean-Christophe PLAGNIOL-VILLARD size_t len) 31959829cc1SJean-Christophe PLAGNIOL-VILLARD { 32059829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 321cacbe919SAmul Kumar Saha int value; 32259829cc1SJean-Christophe PLAGNIOL-VILLARD int block, page; 323cacbe919SAmul Kumar Saha 32459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Now we use page size operation */ 325cacbe919SAmul Kumar Saha int sectors = 0, count = 0; 32659829cc1SJean-Christophe PLAGNIOL-VILLARD 32759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Address translation */ 32859829cc1SJean-Christophe PLAGNIOL-VILLARD switch (cmd) { 32959829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_UNLOCK: 33059829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_LOCK: 33159829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_LOCK_TIGHT: 332ef0921d6SKyungmin Park case ONENAND_CMD_UNLOCK_ALL: 33359829cc1SJean-Christophe PLAGNIOL-VILLARD block = -1; 33459829cc1SJean-Christophe PLAGNIOL-VILLARD page = -1; 33559829cc1SJean-Christophe PLAGNIOL-VILLARD break; 33659829cc1SJean-Christophe PLAGNIOL-VILLARD 337cacbe919SAmul Kumar Saha case FLEXONENAND_CMD_PI_ACCESS: 338cacbe919SAmul Kumar Saha /* addr contains die index */ 339cacbe919SAmul Kumar Saha block = addr * this->density_mask; 34059829cc1SJean-Christophe PLAGNIOL-VILLARD page = -1; 34159829cc1SJean-Christophe PLAGNIOL-VILLARD break; 34259829cc1SJean-Christophe PLAGNIOL-VILLARD 343cacbe919SAmul Kumar Saha case ONENAND_CMD_ERASE: 344cacbe919SAmul Kumar Saha case ONENAND_CMD_BUFFERRAM: 345cacbe919SAmul Kumar Saha block = onenand_block(this, addr); 346cacbe919SAmul Kumar Saha page = -1; 347cacbe919SAmul Kumar Saha break; 348cacbe919SAmul Kumar Saha 349cacbe919SAmul Kumar Saha case FLEXONENAND_CMD_READ_PI: 350cacbe919SAmul Kumar Saha cmd = ONENAND_CMD_READ; 351cacbe919SAmul Kumar Saha block = addr * this->density_mask; 352cacbe919SAmul Kumar Saha page = 0; 353cacbe919SAmul Kumar Saha break; 354cacbe919SAmul Kumar Saha 35559829cc1SJean-Christophe PLAGNIOL-VILLARD default: 356cacbe919SAmul Kumar Saha block = onenand_block(this, addr); 357cacbe919SAmul Kumar Saha page = (int) (addr 358cacbe919SAmul Kumar Saha - onenand_addr(this, block)) >> this->page_shift; 35959829cc1SJean-Christophe PLAGNIOL-VILLARD page &= this->page_mask; 36059829cc1SJean-Christophe PLAGNIOL-VILLARD break; 36159829cc1SJean-Christophe PLAGNIOL-VILLARD } 36259829cc1SJean-Christophe PLAGNIOL-VILLARD 36359829cc1SJean-Christophe PLAGNIOL-VILLARD /* NOTE: The setting order of the registers is very important! */ 36459829cc1SJean-Christophe PLAGNIOL-VILLARD if (cmd == ONENAND_CMD_BUFFERRAM) { 36559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Select DataRAM for DDP */ 366ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 36759829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, 36859829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_ADDRESS2); 36959829cc1SJean-Christophe PLAGNIOL-VILLARD 370e26fd3d3SLukasz Majewski if (ONENAND_IS_4KB_PAGE(this)) 371cacbe919SAmul Kumar Saha ONENAND_SET_BUFFERRAM0(this); 372cacbe919SAmul Kumar Saha else 37359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Switch to the next data buffer */ 37459829cc1SJean-Christophe PLAGNIOL-VILLARD ONENAND_SET_NEXT_BUFFERRAM(this); 37559829cc1SJean-Christophe PLAGNIOL-VILLARD 37659829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 37759829cc1SJean-Christophe PLAGNIOL-VILLARD } 37859829cc1SJean-Christophe PLAGNIOL-VILLARD 37959829cc1SJean-Christophe PLAGNIOL-VILLARD if (block != -1) { 38059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write 'DFS, FBA' of Flash */ 381ef0921d6SKyungmin Park value = onenand_block_address(this, block); 38259829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, 38359829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_ADDRESS1); 384ef0921d6SKyungmin Park 385cacbe919SAmul Kumar Saha /* Select DataRAM for DDP */ 386ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 387ef0921d6SKyungmin Park this->write_word(value, 388ef0921d6SKyungmin Park this->base + ONENAND_REG_START_ADDRESS2); 38959829cc1SJean-Christophe PLAGNIOL-VILLARD } 39059829cc1SJean-Christophe PLAGNIOL-VILLARD 39159829cc1SJean-Christophe PLAGNIOL-VILLARD if (page != -1) { 39259829cc1SJean-Christophe PLAGNIOL-VILLARD int dataram; 39359829cc1SJean-Christophe PLAGNIOL-VILLARD 39459829cc1SJean-Christophe PLAGNIOL-VILLARD switch (cmd) { 395cacbe919SAmul Kumar Saha case FLEXONENAND_CMD_RECOVER_LSB: 39659829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_READ: 39759829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_READOOB: 398e26fd3d3SLukasz Majewski if (ONENAND_IS_4KB_PAGE(this)) 399cacbe919SAmul Kumar Saha dataram = ONENAND_SET_BUFFERRAM0(this); 400cacbe919SAmul Kumar Saha else 40159829cc1SJean-Christophe PLAGNIOL-VILLARD dataram = ONENAND_SET_NEXT_BUFFERRAM(this); 402cacbe919SAmul Kumar Saha 40359829cc1SJean-Christophe PLAGNIOL-VILLARD break; 40459829cc1SJean-Christophe PLAGNIOL-VILLARD 40559829cc1SJean-Christophe PLAGNIOL-VILLARD default: 40659829cc1SJean-Christophe PLAGNIOL-VILLARD dataram = ONENAND_CURRENT_BUFFERRAM(this); 40759829cc1SJean-Christophe PLAGNIOL-VILLARD break; 40859829cc1SJean-Christophe PLAGNIOL-VILLARD } 40959829cc1SJean-Christophe PLAGNIOL-VILLARD 41059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write 'FPA, FSA' of Flash */ 41159829cc1SJean-Christophe PLAGNIOL-VILLARD value = onenand_page_address(page, sectors); 41259829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, 41359829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_ADDRESS8); 41459829cc1SJean-Christophe PLAGNIOL-VILLARD 41559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write 'BSA, BSC' of DataRAM */ 41659829cc1SJean-Christophe PLAGNIOL-VILLARD value = onenand_buffer_address(dataram, sectors, count); 41759829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, this->base + ONENAND_REG_START_BUFFER); 41859829cc1SJean-Christophe PLAGNIOL-VILLARD } 41959829cc1SJean-Christophe PLAGNIOL-VILLARD 42059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Interrupt clear */ 42159829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); 42259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write command */ 42359829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(cmd, this->base + ONENAND_REG_COMMAND); 42459829cc1SJean-Christophe PLAGNIOL-VILLARD 42559829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 42659829cc1SJean-Christophe PLAGNIOL-VILLARD } 42759829cc1SJean-Christophe PLAGNIOL-VILLARD 42859829cc1SJean-Christophe PLAGNIOL-VILLARD /** 429cacbe919SAmul Kumar Saha * onenand_read_ecc - return ecc status 430cacbe919SAmul Kumar Saha * @param this onenand chip structure 431cacbe919SAmul Kumar Saha */ 432cacbe919SAmul Kumar Saha static int onenand_read_ecc(struct onenand_chip *this) 433cacbe919SAmul Kumar Saha { 434cacbe919SAmul Kumar Saha int ecc, i; 435cacbe919SAmul Kumar Saha 436cacbe919SAmul Kumar Saha if (!FLEXONENAND(this)) 437cacbe919SAmul Kumar Saha return this->read_word(this->base + ONENAND_REG_ECC_STATUS); 438cacbe919SAmul Kumar Saha 439cacbe919SAmul Kumar Saha for (i = 0; i < 4; i++) { 440cacbe919SAmul Kumar Saha ecc = this->read_word(this->base 441cacbe919SAmul Kumar Saha + ((ONENAND_REG_ECC_STATUS + i) << 1)); 442cacbe919SAmul Kumar Saha if (likely(!ecc)) 443cacbe919SAmul Kumar Saha continue; 444cacbe919SAmul Kumar Saha if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) 445cacbe919SAmul Kumar Saha return ONENAND_ECC_2BIT_ALL; 446cacbe919SAmul Kumar Saha } 447cacbe919SAmul Kumar Saha 448cacbe919SAmul Kumar Saha return 0; 449cacbe919SAmul Kumar Saha } 450cacbe919SAmul Kumar Saha 451cacbe919SAmul Kumar Saha /** 45259829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_wait - [DEFAULT] wait until the command is done 45359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 45459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param state state to select the max. timeout value 45559829cc1SJean-Christophe PLAGNIOL-VILLARD * 45659829cc1SJean-Christophe PLAGNIOL-VILLARD * Wait for command done. This applies to all OneNAND command 45759829cc1SJean-Christophe PLAGNIOL-VILLARD * Read can take up to 30us, erase up to 2ms and program up to 350us 45859829cc1SJean-Christophe PLAGNIOL-VILLARD * according to general OneNAND specs 45959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 46059829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_wait(struct mtd_info *mtd, int state) 46159829cc1SJean-Christophe PLAGNIOL-VILLARD { 46259829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 46359829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned int flags = ONENAND_INT_MASTER; 46459829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned int interrupt = 0; 465cacbe919SAmul Kumar Saha unsigned int ctrl; 46659829cc1SJean-Christophe PLAGNIOL-VILLARD 46759829cc1SJean-Christophe PLAGNIOL-VILLARD while (1) { 46859829cc1SJean-Christophe PLAGNIOL-VILLARD interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); 46959829cc1SJean-Christophe PLAGNIOL-VILLARD if (interrupt & flags) 47059829cc1SJean-Christophe PLAGNIOL-VILLARD break; 47159829cc1SJean-Christophe PLAGNIOL-VILLARD } 47259829cc1SJean-Christophe PLAGNIOL-VILLARD 47359829cc1SJean-Christophe PLAGNIOL-VILLARD ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); 47459829cc1SJean-Christophe PLAGNIOL-VILLARD 475cacbe919SAmul Kumar Saha if (interrupt & ONENAND_INT_READ) { 476cacbe919SAmul Kumar Saha int ecc = onenand_read_ecc(this); 477cacbe919SAmul Kumar Saha if (ecc & ONENAND_ECC_2BIT_ALL) { 478cacbe919SAmul Kumar Saha printk("onenand_wait: ECC error = 0x%04x\n", ecc); 479cacbe919SAmul Kumar Saha return -EBADMSG; 480cacbe919SAmul Kumar Saha } 481cacbe919SAmul Kumar Saha } 482cacbe919SAmul Kumar Saha 48359829cc1SJean-Christophe PLAGNIOL-VILLARD if (ctrl & ONENAND_CTRL_ERROR) { 484ef0921d6SKyungmin Park printk("onenand_wait: controller error = 0x%04x\n", ctrl); 485ef0921d6SKyungmin Park if (ctrl & ONENAND_CTRL_LOCK) 486ef0921d6SKyungmin Park printk("onenand_wait: it's locked error = 0x%04x\n", 487ef0921d6SKyungmin Park ctrl); 48859829cc1SJean-Christophe PLAGNIOL-VILLARD 48959829cc1SJean-Christophe PLAGNIOL-VILLARD return -EIO; 49059829cc1SJean-Christophe PLAGNIOL-VILLARD } 49159829cc1SJean-Christophe PLAGNIOL-VILLARD 49259829cc1SJean-Christophe PLAGNIOL-VILLARD 49359829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 49459829cc1SJean-Christophe PLAGNIOL-VILLARD } 49559829cc1SJean-Christophe PLAGNIOL-VILLARD 49659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 49759829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_bufferram_offset - [DEFAULT] BufferRAM offset 49859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 49959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 50059829cc1SJean-Christophe PLAGNIOL-VILLARD * @return offset given area 50159829cc1SJean-Christophe PLAGNIOL-VILLARD * 50259829cc1SJean-Christophe PLAGNIOL-VILLARD * Return BufferRAM offset given area 50359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 50459829cc1SJean-Christophe PLAGNIOL-VILLARD static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) 50559829cc1SJean-Christophe PLAGNIOL-VILLARD { 50659829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 50759829cc1SJean-Christophe PLAGNIOL-VILLARD 50859829cc1SJean-Christophe PLAGNIOL-VILLARD if (ONENAND_CURRENT_BUFFERRAM(this)) { 50959829cc1SJean-Christophe PLAGNIOL-VILLARD if (area == ONENAND_DATARAM) 510d438d508SKyungmin Park return mtd->writesize; 51159829cc1SJean-Christophe PLAGNIOL-VILLARD if (area == ONENAND_SPARERAM) 51259829cc1SJean-Christophe PLAGNIOL-VILLARD return mtd->oobsize; 51359829cc1SJean-Christophe PLAGNIOL-VILLARD } 51459829cc1SJean-Christophe PLAGNIOL-VILLARD 51559829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 51659829cc1SJean-Christophe PLAGNIOL-VILLARD } 51759829cc1SJean-Christophe PLAGNIOL-VILLARD 51859829cc1SJean-Christophe PLAGNIOL-VILLARD /** 51959829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area 52059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 52159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 52259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buffer the databuffer to put/get data 52359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param offset offset to read from or write to 52459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count number of bytes to read/write 52559829cc1SJean-Christophe PLAGNIOL-VILLARD * 52659829cc1SJean-Christophe PLAGNIOL-VILLARD * Read the BufferRAM area 52759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 528ef0921d6SKyungmin Park static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, 52959829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned char *buffer, int offset, 53059829cc1SJean-Christophe PLAGNIOL-VILLARD size_t count) 53159829cc1SJean-Christophe PLAGNIOL-VILLARD { 53259829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 53359829cc1SJean-Christophe PLAGNIOL-VILLARD void __iomem *bufferram; 53459829cc1SJean-Christophe PLAGNIOL-VILLARD 53559829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram = this->base + area; 53659829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram += onenand_bufferram_offset(mtd, area); 53759829cc1SJean-Christophe PLAGNIOL-VILLARD 538d2c6fbecSWolfgang Denk memcpy_16(buffer, bufferram + offset, count); 53959829cc1SJean-Christophe PLAGNIOL-VILLARD 54059829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 54159829cc1SJean-Christophe PLAGNIOL-VILLARD } 54259829cc1SJean-Christophe PLAGNIOL-VILLARD 54359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 54459829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode 54559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 54659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 54759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buffer the databuffer to put/get data 54859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param offset offset to read from or write to 54959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count number of bytes to read/write 55059829cc1SJean-Christophe PLAGNIOL-VILLARD * 55159829cc1SJean-Christophe PLAGNIOL-VILLARD * Read the BufferRAM area with Sync. Burst Mode 55259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 553ef0921d6SKyungmin Park static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, 55459829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned char *buffer, int offset, 55559829cc1SJean-Christophe PLAGNIOL-VILLARD size_t count) 55659829cc1SJean-Christophe PLAGNIOL-VILLARD { 55759829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 55859829cc1SJean-Christophe PLAGNIOL-VILLARD void __iomem *bufferram; 55959829cc1SJean-Christophe PLAGNIOL-VILLARD 56059829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram = this->base + area; 56159829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram += onenand_bufferram_offset(mtd, area); 56259829cc1SJean-Christophe PLAGNIOL-VILLARD 56359829cc1SJean-Christophe PLAGNIOL-VILLARD this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); 56459829cc1SJean-Christophe PLAGNIOL-VILLARD 565d2c6fbecSWolfgang Denk memcpy_16(buffer, bufferram + offset, count); 56659829cc1SJean-Christophe PLAGNIOL-VILLARD 56759829cc1SJean-Christophe PLAGNIOL-VILLARD this->mmcontrol(mtd, 0); 56859829cc1SJean-Christophe PLAGNIOL-VILLARD 56959829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 57059829cc1SJean-Christophe PLAGNIOL-VILLARD } 57159829cc1SJean-Christophe PLAGNIOL-VILLARD 57259829cc1SJean-Christophe PLAGNIOL-VILLARD /** 57359829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area 57459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 57559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 57659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buffer the databuffer to put/get data 57759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param offset offset to read from or write to 57859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count number of bytes to read/write 57959829cc1SJean-Christophe PLAGNIOL-VILLARD * 58059829cc1SJean-Christophe PLAGNIOL-VILLARD * Write the BufferRAM area 58159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 582ef0921d6SKyungmin Park static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area, 58359829cc1SJean-Christophe PLAGNIOL-VILLARD const unsigned char *buffer, int offset, 58459829cc1SJean-Christophe PLAGNIOL-VILLARD size_t count) 58559829cc1SJean-Christophe PLAGNIOL-VILLARD { 58659829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 58759829cc1SJean-Christophe PLAGNIOL-VILLARD void __iomem *bufferram; 58859829cc1SJean-Christophe PLAGNIOL-VILLARD 58959829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram = this->base + area; 59059829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram += onenand_bufferram_offset(mtd, area); 59159829cc1SJean-Christophe PLAGNIOL-VILLARD 592d2c6fbecSWolfgang Denk memcpy_16(bufferram + offset, buffer, count); 59359829cc1SJean-Christophe PLAGNIOL-VILLARD 59459829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 59559829cc1SJean-Christophe PLAGNIOL-VILLARD } 59659829cc1SJean-Christophe PLAGNIOL-VILLARD 59759829cc1SJean-Christophe PLAGNIOL-VILLARD /** 5984fca3310SStefan Roese * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode 5994fca3310SStefan Roese * @param mtd MTD data structure 6004fca3310SStefan Roese * @param addr address to check 6014fca3310SStefan Roese * @return blockpage address 6024fca3310SStefan Roese * 6034fca3310SStefan Roese * Get blockpage address at 2x program mode 6044fca3310SStefan Roese */ 6054fca3310SStefan Roese static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) 6064fca3310SStefan Roese { 6074fca3310SStefan Roese struct onenand_chip *this = mtd->priv; 6084fca3310SStefan Roese int blockpage, block, page; 6094fca3310SStefan Roese 6104fca3310SStefan Roese /* Calculate the even block number */ 6114fca3310SStefan Roese block = (int) (addr >> this->erase_shift) & ~1; 6124fca3310SStefan Roese /* Is it the odd plane? */ 6134fca3310SStefan Roese if (addr & this->writesize) 6144fca3310SStefan Roese block++; 6154fca3310SStefan Roese page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; 6164fca3310SStefan Roese blockpage = (block << 7) | page; 6174fca3310SStefan Roese 6184fca3310SStefan Roese return blockpage; 6194fca3310SStefan Roese } 6204fca3310SStefan Roese 6214fca3310SStefan Roese /** 62259829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_check_bufferram - [GENERIC] Check BufferRAM information 62359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 62459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to check 62559829cc1SJean-Christophe PLAGNIOL-VILLARD * @return 1 if there are valid data, otherwise 0 62659829cc1SJean-Christophe PLAGNIOL-VILLARD * 62759829cc1SJean-Christophe PLAGNIOL-VILLARD * Check bufferram if there is data we required 62859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 62959829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) 63059829cc1SJean-Christophe PLAGNIOL-VILLARD { 63159829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 632ef0921d6SKyungmin Park int blockpage, found = 0; 633ef0921d6SKyungmin Park unsigned int i; 63459829cc1SJean-Christophe PLAGNIOL-VILLARD 635ef0921d6SKyungmin Park if (ONENAND_IS_2PLANE(this)) 636ef0921d6SKyungmin Park blockpage = onenand_get_2x_blockpage(mtd, addr); 637ef0921d6SKyungmin Park else 638ef0921d6SKyungmin Park blockpage = (int) (addr >> this->page_shift); 63959829cc1SJean-Christophe PLAGNIOL-VILLARD 64059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Is there valid data? */ 641ef0921d6SKyungmin Park i = ONENAND_CURRENT_BUFFERRAM(this); 642ef0921d6SKyungmin Park if (this->bufferram[i].blockpage == blockpage) 643ef0921d6SKyungmin Park found = 1; 644ef0921d6SKyungmin Park else { 645ef0921d6SKyungmin Park /* Check another BufferRAM */ 646ef0921d6SKyungmin Park i = ONENAND_NEXT_BUFFERRAM(this); 647ef0921d6SKyungmin Park if (this->bufferram[i].blockpage == blockpage) { 648ef0921d6SKyungmin Park ONENAND_SET_NEXT_BUFFERRAM(this); 649ef0921d6SKyungmin Park found = 1; 650ef0921d6SKyungmin Park } 651ef0921d6SKyungmin Park } 65259829cc1SJean-Christophe PLAGNIOL-VILLARD 653ef0921d6SKyungmin Park if (found && ONENAND_IS_DDP(this)) { 654ef0921d6SKyungmin Park /* Select DataRAM for DDP */ 655cacbe919SAmul Kumar Saha int block = onenand_block(this, addr); 656ef0921d6SKyungmin Park int value = onenand_bufferram_address(this, block); 657ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); 658ef0921d6SKyungmin Park } 659ef0921d6SKyungmin Park 660ef0921d6SKyungmin Park return found; 66159829cc1SJean-Christophe PLAGNIOL-VILLARD } 66259829cc1SJean-Christophe PLAGNIOL-VILLARD 66359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 66459829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_update_bufferram - [GENERIC] Update BufferRAM information 66559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 66659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to update 66759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param valid valid flag 66859829cc1SJean-Christophe PLAGNIOL-VILLARD * 66959829cc1SJean-Christophe PLAGNIOL-VILLARD * Update BufferRAM information 67059829cc1SJean-Christophe PLAGNIOL-VILLARD */ 67159829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, 67259829cc1SJean-Christophe PLAGNIOL-VILLARD int valid) 67359829cc1SJean-Christophe PLAGNIOL-VILLARD { 67459829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 675ef0921d6SKyungmin Park int blockpage; 676ef0921d6SKyungmin Park unsigned int i; 67759829cc1SJean-Christophe PLAGNIOL-VILLARD 678ef0921d6SKyungmin Park if (ONENAND_IS_2PLANE(this)) 679ef0921d6SKyungmin Park blockpage = onenand_get_2x_blockpage(mtd, addr); 680ef0921d6SKyungmin Park else 681ef0921d6SKyungmin Park blockpage = (int)(addr >> this->page_shift); 68259829cc1SJean-Christophe PLAGNIOL-VILLARD 683ef0921d6SKyungmin Park /* Invalidate another BufferRAM */ 684ef0921d6SKyungmin Park i = ONENAND_NEXT_BUFFERRAM(this); 685ef0921d6SKyungmin Park if (this->bufferram[i].blockpage == blockpage) 686ef0921d6SKyungmin Park this->bufferram[i].blockpage = -1; 68759829cc1SJean-Christophe PLAGNIOL-VILLARD 68859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Update BufferRAM */ 68959829cc1SJean-Christophe PLAGNIOL-VILLARD i = ONENAND_CURRENT_BUFFERRAM(this); 690ef0921d6SKyungmin Park if (valid) 691ef0921d6SKyungmin Park this->bufferram[i].blockpage = blockpage; 692ef0921d6SKyungmin Park else 693ef0921d6SKyungmin Park this->bufferram[i].blockpage = -1; 69459829cc1SJean-Christophe PLAGNIOL-VILLARD 69559829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 69659829cc1SJean-Christophe PLAGNIOL-VILLARD } 69759829cc1SJean-Christophe PLAGNIOL-VILLARD 69859829cc1SJean-Christophe PLAGNIOL-VILLARD /** 699d438d508SKyungmin Park * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information 700d438d508SKyungmin Park * @param mtd MTD data structure 701d438d508SKyungmin Park * @param addr start address to invalidate 702d438d508SKyungmin Park * @param len length to invalidate 703d438d508SKyungmin Park * 704d438d508SKyungmin Park * Invalidate BufferRAM information 705d438d508SKyungmin Park */ 706d438d508SKyungmin Park static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, 707d438d508SKyungmin Park unsigned int len) 708d438d508SKyungmin Park { 709d438d508SKyungmin Park struct onenand_chip *this = mtd->priv; 710d438d508SKyungmin Park int i; 711d438d508SKyungmin Park loff_t end_addr = addr + len; 712d438d508SKyungmin Park 713d438d508SKyungmin Park /* Invalidate BufferRAM */ 714d438d508SKyungmin Park for (i = 0; i < MAX_BUFFERRAM; i++) { 715ef0921d6SKyungmin Park loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; 716d438d508SKyungmin Park 717d438d508SKyungmin Park if (buf_addr >= addr && buf_addr < end_addr) 718ef0921d6SKyungmin Park this->bufferram[i].blockpage = -1; 719d438d508SKyungmin Park } 720d438d508SKyungmin Park } 721d438d508SKyungmin Park 722d438d508SKyungmin Park /** 72359829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_get_device - [GENERIC] Get chip for selected access 72459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 72559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param new_state the state which is requested 72659829cc1SJean-Christophe PLAGNIOL-VILLARD * 72759829cc1SJean-Christophe PLAGNIOL-VILLARD * Get the device and lock it for exclusive access 72859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 72959829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_get_device(struct mtd_info *mtd, int new_state) 73059829cc1SJean-Christophe PLAGNIOL-VILLARD { 73159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do nothing */ 73259829cc1SJean-Christophe PLAGNIOL-VILLARD } 73359829cc1SJean-Christophe PLAGNIOL-VILLARD 73459829cc1SJean-Christophe PLAGNIOL-VILLARD /** 73559829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_release_device - [GENERIC] release chip 73659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 73759829cc1SJean-Christophe PLAGNIOL-VILLARD * 73859829cc1SJean-Christophe PLAGNIOL-VILLARD * Deselect, release chip lock and wake up anyone waiting on the device 73959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 74059829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_release_device(struct mtd_info *mtd) 74159829cc1SJean-Christophe PLAGNIOL-VILLARD { 74259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do nothing */ 74359829cc1SJean-Christophe PLAGNIOL-VILLARD } 74459829cc1SJean-Christophe PLAGNIOL-VILLARD 74559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 746*dfe64e2cSSergey Lapin * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer 74759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 748bfd7f386SKyungmin Park * @param buf destination address 749bfd7f386SKyungmin Park * @param column oob offset to read from 750bfd7f386SKyungmin Park * @param thislen oob length to read 75159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 752bfd7f386SKyungmin Park static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, 753bfd7f386SKyungmin Park int column, int thislen) 75459829cc1SJean-Christophe PLAGNIOL-VILLARD { 75559829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 756bfd7f386SKyungmin Park struct nand_oobfree *free; 757bfd7f386SKyungmin Park int readcol = column; 758bfd7f386SKyungmin Park int readend = column + thislen; 759bfd7f386SKyungmin Park int lastgap = 0; 760bfd7f386SKyungmin Park unsigned int i; 761bfd7f386SKyungmin Park uint8_t *oob_buf = this->oob_buf; 76259829cc1SJean-Christophe PLAGNIOL-VILLARD 763bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 764bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 765bfd7f386SKyungmin Park if (readcol >= lastgap) 766bfd7f386SKyungmin Park readcol += free->offset - lastgap; 767bfd7f386SKyungmin Park if (readend >= lastgap) 768bfd7f386SKyungmin Park readend += free->offset - lastgap; 769bfd7f386SKyungmin Park lastgap = free->offset + free->length; 770bfd7f386SKyungmin Park } 771ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); 772bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 773bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 774bfd7f386SKyungmin Park int free_end = free->offset + free->length; 775bfd7f386SKyungmin Park if (free->offset < readend && free_end > readcol) { 776bfd7f386SKyungmin Park int st = max_t(int,free->offset,readcol); 777bfd7f386SKyungmin Park int ed = min_t(int,free_end,readend); 778bfd7f386SKyungmin Park int n = ed - st; 779bfd7f386SKyungmin Park memcpy(buf, oob_buf + st, n); 780bfd7f386SKyungmin Park buf += n; 781bfd7f386SKyungmin Park } else if (column == 0) 782bfd7f386SKyungmin Park break; 783bfd7f386SKyungmin Park } 784bfd7f386SKyungmin Park return 0; 785bfd7f386SKyungmin Park } 786bfd7f386SKyungmin Park 787bfd7f386SKyungmin Park /** 788cacbe919SAmul Kumar Saha * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data 789cacbe919SAmul Kumar Saha * @param mtd MTD device structure 790cacbe919SAmul Kumar Saha * @param addr address to recover 791cacbe919SAmul Kumar Saha * @param status return value from onenand_wait 792cacbe919SAmul Kumar Saha * 793cacbe919SAmul Kumar Saha * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has 794cacbe919SAmul Kumar Saha * lower page address and MSB page has higher page address in paired pages. 795cacbe919SAmul Kumar Saha * If power off occurs during MSB page program, the paired LSB page data can 796cacbe919SAmul Kumar Saha * become corrupt. LSB page recovery read is a way to read LSB page though page 797cacbe919SAmul Kumar Saha * data are corrupted. When uncorrectable error occurs as a result of LSB page 798cacbe919SAmul Kumar Saha * read after power up, issue LSB page recovery read. 799cacbe919SAmul Kumar Saha */ 800cacbe919SAmul Kumar Saha static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) 801cacbe919SAmul Kumar Saha { 802cacbe919SAmul Kumar Saha struct onenand_chip *this = mtd->priv; 803cacbe919SAmul Kumar Saha int i; 804cacbe919SAmul Kumar Saha 805cacbe919SAmul Kumar Saha /* Recovery is only for Flex-OneNAND */ 806cacbe919SAmul Kumar Saha if (!FLEXONENAND(this)) 807cacbe919SAmul Kumar Saha return status; 808cacbe919SAmul Kumar Saha 809cacbe919SAmul Kumar Saha /* check if we failed due to uncorrectable error */ 810*dfe64e2cSSergey Lapin if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR) 811cacbe919SAmul Kumar Saha return status; 812cacbe919SAmul Kumar Saha 813cacbe919SAmul Kumar Saha /* check if address lies in MLC region */ 814cacbe919SAmul Kumar Saha i = flexonenand_region(mtd, addr); 815cacbe919SAmul Kumar Saha if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) 816cacbe919SAmul Kumar Saha return status; 817cacbe919SAmul Kumar Saha 818cacbe919SAmul Kumar Saha printk("onenand_recover_lsb:" 819cacbe919SAmul Kumar Saha "Attempting to recover from uncorrectable read\n"); 820cacbe919SAmul Kumar Saha 821cacbe919SAmul Kumar Saha /* Issue the LSB page recovery command */ 822cacbe919SAmul Kumar Saha this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); 823cacbe919SAmul Kumar Saha return this->wait(mtd, FL_READING); 824cacbe919SAmul Kumar Saha } 825cacbe919SAmul Kumar Saha 826cacbe919SAmul Kumar Saha /** 827bfd7f386SKyungmin Park * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band 828bfd7f386SKyungmin Park * @param mtd MTD device structure 829bfd7f386SKyungmin Park * @param from offset to read from 830bfd7f386SKyungmin Park * @param ops oob operation description structure 831bfd7f386SKyungmin Park * 832bfd7f386SKyungmin Park * OneNAND read main and/or out-of-band data 833bfd7f386SKyungmin Park */ 834bfd7f386SKyungmin Park static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, 835bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 836bfd7f386SKyungmin Park { 837bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 838bfd7f386SKyungmin Park struct mtd_ecc_stats stats; 839bfd7f386SKyungmin Park size_t len = ops->len; 840bfd7f386SKyungmin Park size_t ooblen = ops->ooblen; 841bfd7f386SKyungmin Park u_char *buf = ops->datbuf; 842bfd7f386SKyungmin Park u_char *oobbuf = ops->oobbuf; 843bfd7f386SKyungmin Park int read = 0, column, thislen; 844bfd7f386SKyungmin Park int oobread = 0, oobcolumn, thisooblen, oobsize; 845bfd7f386SKyungmin Park int ret = 0, boundary = 0; 846bfd7f386SKyungmin Park int writesize = this->writesize; 847bfd7f386SKyungmin Park 848ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); 84959829cc1SJean-Christophe PLAGNIOL-VILLARD 850*dfe64e2cSSergey Lapin if (ops->mode == MTD_OPS_AUTO_OOB) 851bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 852bfd7f386SKyungmin Park else 853bfd7f386SKyungmin Park oobsize = mtd->oobsize; 854bfd7f386SKyungmin Park 855bfd7f386SKyungmin Park oobcolumn = from & (mtd->oobsize - 1); 856bfd7f386SKyungmin Park 85759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do not allow reads past end of device */ 85859829cc1SJean-Christophe PLAGNIOL-VILLARD if ((from + len) > mtd->size) { 859bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); 860bfd7f386SKyungmin Park ops->retlen = 0; 861bfd7f386SKyungmin Park ops->oobretlen = 0; 86259829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 86359829cc1SJean-Christophe PLAGNIOL-VILLARD } 86459829cc1SJean-Christophe PLAGNIOL-VILLARD 865bfd7f386SKyungmin Park stats = mtd->ecc_stats; 86659829cc1SJean-Christophe PLAGNIOL-VILLARD 867bfd7f386SKyungmin Park /* Read-while-load method */ 868cacbe919SAmul Kumar Saha /* Note: We can't use this feature in MLC */ 86959829cc1SJean-Christophe PLAGNIOL-VILLARD 870bfd7f386SKyungmin Park /* Do first load to bufferRAM */ 871bfd7f386SKyungmin Park if (read < len) { 87259829cc1SJean-Christophe PLAGNIOL-VILLARD if (!onenand_check_bufferram(mtd, from)) { 873ef0921d6SKyungmin Park this->main_buf = buf; 874bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READ, from, writesize); 87559829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_READING); 876cacbe919SAmul Kumar Saha if (unlikely(ret)) 877cacbe919SAmul Kumar Saha ret = onenand_recover_lsb(mtd, from, ret); 878bfd7f386SKyungmin Park onenand_update_bufferram(mtd, from, !ret); 879bfd7f386SKyungmin Park if (ret == -EBADMSG) 880bfd7f386SKyungmin Park ret = 0; 881bfd7f386SKyungmin Park } 88259829cc1SJean-Christophe PLAGNIOL-VILLARD } 88359829cc1SJean-Christophe PLAGNIOL-VILLARD 884bfd7f386SKyungmin Park thislen = min_t(int, writesize, len - read); 885bfd7f386SKyungmin Park column = from & (writesize - 1); 886bfd7f386SKyungmin Park if (column + thislen > writesize) 887bfd7f386SKyungmin Park thislen = writesize - column; 88859829cc1SJean-Christophe PLAGNIOL-VILLARD 889bfd7f386SKyungmin Park while (!ret) { 890bfd7f386SKyungmin Park /* If there is more to load then start next load */ 891bfd7f386SKyungmin Park from += thislen; 892e26fd3d3SLukasz Majewski if (!ONENAND_IS_4KB_PAGE(this) && read + thislen < len) { 893ef0921d6SKyungmin Park this->main_buf = buf + thislen; 894bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READ, from, writesize); 895bfd7f386SKyungmin Park /* 896bfd7f386SKyungmin Park * Chip boundary handling in DDP 897bfd7f386SKyungmin Park * Now we issued chip 1 read and pointed chip 1 898bfd7f386SKyungmin Park * bufferam so we have to point chip 0 bufferam. 899bfd7f386SKyungmin Park */ 900bfd7f386SKyungmin Park if (ONENAND_IS_DDP(this) && 901bfd7f386SKyungmin Park unlikely(from == (this->chipsize >> 1))) { 902bfd7f386SKyungmin Park this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); 903bfd7f386SKyungmin Park boundary = 1; 904bfd7f386SKyungmin Park } else 905bfd7f386SKyungmin Park boundary = 0; 906bfd7f386SKyungmin Park ONENAND_SET_PREV_BUFFERRAM(this); 907bfd7f386SKyungmin Park } 908bfd7f386SKyungmin Park 909bfd7f386SKyungmin Park /* While load is going, read from last bufferRAM */ 910ef0921d6SKyungmin Park this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen); 911bfd7f386SKyungmin Park 912bfd7f386SKyungmin Park /* Read oob area if needed */ 913bfd7f386SKyungmin Park if (oobbuf) { 914bfd7f386SKyungmin Park thisooblen = oobsize - oobcolumn; 915bfd7f386SKyungmin Park thisooblen = min_t(int, thisooblen, ooblen - oobread); 916bfd7f386SKyungmin Park 917*dfe64e2cSSergey Lapin if (ops->mode == MTD_OPS_AUTO_OOB) 918bfd7f386SKyungmin Park onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); 919bfd7f386SKyungmin Park else 920ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); 921bfd7f386SKyungmin Park oobread += thisooblen; 922bfd7f386SKyungmin Park oobbuf += thisooblen; 923bfd7f386SKyungmin Park oobcolumn = 0; 924bfd7f386SKyungmin Park } 925bfd7f386SKyungmin Park 926e26fd3d3SLukasz Majewski if (ONENAND_IS_4KB_PAGE(this) && (read + thislen < len)) { 927cacbe919SAmul Kumar Saha this->command(mtd, ONENAND_CMD_READ, from, writesize); 928cacbe919SAmul Kumar Saha ret = this->wait(mtd, FL_READING); 929cacbe919SAmul Kumar Saha if (unlikely(ret)) 930cacbe919SAmul Kumar Saha ret = onenand_recover_lsb(mtd, from, ret); 931cacbe919SAmul Kumar Saha onenand_update_bufferram(mtd, from, !ret); 932*dfe64e2cSSergey Lapin if (mtd_is_eccerr(ret)) 933cacbe919SAmul Kumar Saha ret = 0; 934cacbe919SAmul Kumar Saha } 935cacbe919SAmul Kumar Saha 936bfd7f386SKyungmin Park /* See if we are done */ 93759829cc1SJean-Christophe PLAGNIOL-VILLARD read += thislen; 93859829cc1SJean-Christophe PLAGNIOL-VILLARD if (read == len) 93959829cc1SJean-Christophe PLAGNIOL-VILLARD break; 940bfd7f386SKyungmin Park /* Set up for next read from bufferRAM */ 941bfd7f386SKyungmin Park if (unlikely(boundary)) 942bfd7f386SKyungmin Park this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); 943e26fd3d3SLukasz Majewski if (!ONENAND_IS_4KB_PAGE(this)) 944bfd7f386SKyungmin Park ONENAND_SET_NEXT_BUFFERRAM(this); 94559829cc1SJean-Christophe PLAGNIOL-VILLARD buf += thislen; 946bfd7f386SKyungmin Park thislen = min_t(int, writesize, len - read); 947bfd7f386SKyungmin Park column = 0; 94859829cc1SJean-Christophe PLAGNIOL-VILLARD 949e26fd3d3SLukasz Majewski if (!ONENAND_IS_4KB_PAGE(this)) { 950bfd7f386SKyungmin Park /* Now wait for load */ 951bfd7f386SKyungmin Park ret = this->wait(mtd, FL_READING); 952bfd7f386SKyungmin Park onenand_update_bufferram(mtd, from, !ret); 953*dfe64e2cSSergey Lapin if (mtd_is_eccerr(ret)) 954bfd7f386SKyungmin Park ret = 0; 955bfd7f386SKyungmin Park } 956cacbe919SAmul Kumar Saha } 95759829cc1SJean-Christophe PLAGNIOL-VILLARD 95859829cc1SJean-Christophe PLAGNIOL-VILLARD /* 95959829cc1SJean-Christophe PLAGNIOL-VILLARD * Return success, if no ECC failures, else -EBADMSG 96059829cc1SJean-Christophe PLAGNIOL-VILLARD * fs driver will take care of that, because 96159829cc1SJean-Christophe PLAGNIOL-VILLARD * retlen == desired len and result == -EBADMSG 96259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 963bfd7f386SKyungmin Park ops->retlen = read; 964bfd7f386SKyungmin Park ops->oobretlen = oobread; 965bfd7f386SKyungmin Park 966bfd7f386SKyungmin Park if (ret) 96759829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 968bfd7f386SKyungmin Park 969bfd7f386SKyungmin Park if (mtd->ecc_stats.failed - stats.failed) 970bfd7f386SKyungmin Park return -EBADMSG; 971bfd7f386SKyungmin Park 972bfd7f386SKyungmin Park return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; 973bfd7f386SKyungmin Park } 974bfd7f386SKyungmin Park 975bfd7f386SKyungmin Park /** 976bfd7f386SKyungmin Park * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band 977bfd7f386SKyungmin Park * @param mtd MTD device structure 978bfd7f386SKyungmin Park * @param from offset to read from 979bfd7f386SKyungmin Park * @param ops oob operation description structure 980bfd7f386SKyungmin Park * 981bfd7f386SKyungmin Park * OneNAND read out-of-band data from the spare area 982bfd7f386SKyungmin Park */ 983bfd7f386SKyungmin Park static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, 984bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 985bfd7f386SKyungmin Park { 986bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 987bfd7f386SKyungmin Park struct mtd_ecc_stats stats; 988bfd7f386SKyungmin Park int read = 0, thislen, column, oobsize; 989bfd7f386SKyungmin Park size_t len = ops->ooblen; 990*dfe64e2cSSergey Lapin unsigned int mode = ops->mode; 991bfd7f386SKyungmin Park u_char *buf = ops->oobbuf; 992cacbe919SAmul Kumar Saha int ret = 0, readcmd; 993bfd7f386SKyungmin Park 994bfd7f386SKyungmin Park from += ops->ooboffs; 995bfd7f386SKyungmin Park 996ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); 997bfd7f386SKyungmin Park 998bfd7f386SKyungmin Park /* Initialize return length value */ 999bfd7f386SKyungmin Park ops->oobretlen = 0; 1000bfd7f386SKyungmin Park 1001*dfe64e2cSSergey Lapin if (mode == MTD_OPS_AUTO_OOB) 1002bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 1003bfd7f386SKyungmin Park else 1004bfd7f386SKyungmin Park oobsize = mtd->oobsize; 1005bfd7f386SKyungmin Park 1006bfd7f386SKyungmin Park column = from & (mtd->oobsize - 1); 1007bfd7f386SKyungmin Park 1008bfd7f386SKyungmin Park if (unlikely(column >= oobsize)) { 1009bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); 1010bfd7f386SKyungmin Park return -EINVAL; 1011bfd7f386SKyungmin Park } 1012bfd7f386SKyungmin Park 1013bfd7f386SKyungmin Park /* Do not allow reads past end of device */ 1014bfd7f386SKyungmin Park if (unlikely(from >= mtd->size || 1015bfd7f386SKyungmin Park column + len > ((mtd->size >> this->page_shift) - 1016bfd7f386SKyungmin Park (from >> this->page_shift)) * oobsize)) { 1017bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); 1018bfd7f386SKyungmin Park return -EINVAL; 1019bfd7f386SKyungmin Park } 1020bfd7f386SKyungmin Park 1021bfd7f386SKyungmin Park stats = mtd->ecc_stats; 1022bfd7f386SKyungmin Park 1023e26fd3d3SLukasz Majewski readcmd = ONENAND_IS_4KB_PAGE(this) ? 1024e26fd3d3SLukasz Majewski ONENAND_CMD_READ : ONENAND_CMD_READOOB; 1025cacbe919SAmul Kumar Saha 1026bfd7f386SKyungmin Park while (read < len) { 1027bfd7f386SKyungmin Park thislen = oobsize - column; 1028bfd7f386SKyungmin Park thislen = min_t(int, thislen, len); 1029bfd7f386SKyungmin Park 1030ef0921d6SKyungmin Park this->spare_buf = buf; 1031cacbe919SAmul Kumar Saha this->command(mtd, readcmd, from, mtd->oobsize); 1032bfd7f386SKyungmin Park 1033bfd7f386SKyungmin Park onenand_update_bufferram(mtd, from, 0); 1034bfd7f386SKyungmin Park 1035bfd7f386SKyungmin Park ret = this->wait(mtd, FL_READING); 1036cacbe919SAmul Kumar Saha if (unlikely(ret)) 1037cacbe919SAmul Kumar Saha ret = onenand_recover_lsb(mtd, from, ret); 1038cacbe919SAmul Kumar Saha 1039bfd7f386SKyungmin Park if (ret && ret != -EBADMSG) { 1040bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); 1041bfd7f386SKyungmin Park break; 1042bfd7f386SKyungmin Park } 1043bfd7f386SKyungmin Park 1044*dfe64e2cSSergey Lapin if (mode == MTD_OPS_AUTO_OOB) 1045bfd7f386SKyungmin Park onenand_transfer_auto_oob(mtd, buf, column, thislen); 1046bfd7f386SKyungmin Park else 1047ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); 1048bfd7f386SKyungmin Park 1049bfd7f386SKyungmin Park read += thislen; 1050bfd7f386SKyungmin Park 1051bfd7f386SKyungmin Park if (read == len) 1052bfd7f386SKyungmin Park break; 1053bfd7f386SKyungmin Park 1054bfd7f386SKyungmin Park buf += thislen; 1055bfd7f386SKyungmin Park 1056bfd7f386SKyungmin Park /* Read more? */ 1057bfd7f386SKyungmin Park if (read < len) { 1058bfd7f386SKyungmin Park /* Page size */ 1059bfd7f386SKyungmin Park from += mtd->writesize; 1060bfd7f386SKyungmin Park column = 0; 1061bfd7f386SKyungmin Park } 1062bfd7f386SKyungmin Park } 1063bfd7f386SKyungmin Park 1064bfd7f386SKyungmin Park ops->oobretlen = read; 1065bfd7f386SKyungmin Park 1066bfd7f386SKyungmin Park if (ret) 1067bfd7f386SKyungmin Park return ret; 1068bfd7f386SKyungmin Park 1069bfd7f386SKyungmin Park if (mtd->ecc_stats.failed - stats.failed) 1070bfd7f386SKyungmin Park return -EBADMSG; 1071bfd7f386SKyungmin Park 1072bfd7f386SKyungmin Park return 0; 107359829cc1SJean-Christophe PLAGNIOL-VILLARD } 107459829cc1SJean-Christophe PLAGNIOL-VILLARD 107559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 107659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc 107759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 107859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param from offset to read from 107959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param len number of bytes to read 108059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param retlen pointer to variable to store the number of read bytes 108159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buf the databuffer to put data 108259829cc1SJean-Christophe PLAGNIOL-VILLARD * 108359829cc1SJean-Christophe PLAGNIOL-VILLARD * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL 108459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 108559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, 108659829cc1SJean-Christophe PLAGNIOL-VILLARD size_t * retlen, u_char * buf) 108759829cc1SJean-Christophe PLAGNIOL-VILLARD { 1088bfd7f386SKyungmin Park struct mtd_oob_ops ops = { 1089bfd7f386SKyungmin Park .len = len, 1090bfd7f386SKyungmin Park .ooblen = 0, 1091bfd7f386SKyungmin Park .datbuf = buf, 1092bfd7f386SKyungmin Park .oobbuf = NULL, 1093bfd7f386SKyungmin Park }; 1094bfd7f386SKyungmin Park int ret; 1095bfd7f386SKyungmin Park 1096bfd7f386SKyungmin Park onenand_get_device(mtd, FL_READING); 1097bfd7f386SKyungmin Park ret = onenand_read_ops_nolock(mtd, from, &ops); 1098bfd7f386SKyungmin Park onenand_release_device(mtd); 1099bfd7f386SKyungmin Park 1100bfd7f386SKyungmin Park *retlen = ops.retlen; 1101bfd7f386SKyungmin Park return ret; 110259829cc1SJean-Christophe PLAGNIOL-VILLARD } 110359829cc1SJean-Christophe PLAGNIOL-VILLARD 110459829cc1SJean-Christophe PLAGNIOL-VILLARD /** 110559829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_read_oob - [MTD Interface] OneNAND read out-of-band 110659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 110759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param from offset to read from 1108bfd7f386SKyungmin Park * @param ops oob operations description structure 110959829cc1SJean-Christophe PLAGNIOL-VILLARD * 1110bfd7f386SKyungmin Park * OneNAND main and/or out-of-band 111159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1112bfd7f386SKyungmin Park int onenand_read_oob(struct mtd_info *mtd, loff_t from, 1113bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 1114bfd7f386SKyungmin Park { 1115bfd7f386SKyungmin Park int ret; 1116bfd7f386SKyungmin Park 1117bfd7f386SKyungmin Park switch (ops->mode) { 1118*dfe64e2cSSergey Lapin case MTD_OPS_PLACE_OOB: 1119*dfe64e2cSSergey Lapin case MTD_OPS_AUTO_OOB: 1120bfd7f386SKyungmin Park break; 1121*dfe64e2cSSergey Lapin case MTD_OPS_RAW: 1122bfd7f386SKyungmin Park /* Not implemented yet */ 1123bfd7f386SKyungmin Park default: 1124bfd7f386SKyungmin Park return -EINVAL; 1125bfd7f386SKyungmin Park } 1126bfd7f386SKyungmin Park 1127bfd7f386SKyungmin Park onenand_get_device(mtd, FL_READING); 1128bfd7f386SKyungmin Park if (ops->datbuf) 1129bfd7f386SKyungmin Park ret = onenand_read_ops_nolock(mtd, from, ops); 1130bfd7f386SKyungmin Park else 1131bfd7f386SKyungmin Park ret = onenand_read_oob_nolock(mtd, from, ops); 1132bfd7f386SKyungmin Park onenand_release_device(mtd); 1133bfd7f386SKyungmin Park 1134bfd7f386SKyungmin Park return ret; 1135bfd7f386SKyungmin Park } 1136bfd7f386SKyungmin Park 1137bfd7f386SKyungmin Park /** 1138bfd7f386SKyungmin Park * onenand_bbt_wait - [DEFAULT] wait until the command is done 1139bfd7f386SKyungmin Park * @param mtd MTD device structure 1140bfd7f386SKyungmin Park * @param state state to select the max. timeout value 1141bfd7f386SKyungmin Park * 1142bfd7f386SKyungmin Park * Wait for command done. 1143bfd7f386SKyungmin Park */ 1144bfd7f386SKyungmin Park static int onenand_bbt_wait(struct mtd_info *mtd, int state) 1145bfd7f386SKyungmin Park { 1146bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 1147bfd7f386SKyungmin Park unsigned int flags = ONENAND_INT_MASTER; 1148bfd7f386SKyungmin Park unsigned int interrupt; 1149bfd7f386SKyungmin Park unsigned int ctrl; 1150bfd7f386SKyungmin Park 1151bfd7f386SKyungmin Park while (1) { 1152bfd7f386SKyungmin Park interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); 1153bfd7f386SKyungmin Park if (interrupt & flags) 1154bfd7f386SKyungmin Park break; 1155bfd7f386SKyungmin Park } 1156bfd7f386SKyungmin Park 1157bfd7f386SKyungmin Park /* To get correct interrupt status in timeout case */ 1158bfd7f386SKyungmin Park interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); 1159bfd7f386SKyungmin Park ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); 1160bfd7f386SKyungmin Park 1161bfd7f386SKyungmin Park if (interrupt & ONENAND_INT_READ) { 1162cacbe919SAmul Kumar Saha int ecc = onenand_read_ecc(this); 1163cacbe919SAmul Kumar Saha if (ecc & ONENAND_ECC_2BIT_ALL) { 1164cacbe919SAmul Kumar Saha printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" 1165cacbe919SAmul Kumar Saha ", controller = 0x%04x\n", ecc, ctrl); 1166bfd7f386SKyungmin Park return ONENAND_BBT_READ_ERROR; 1167cacbe919SAmul Kumar Saha } 1168bfd7f386SKyungmin Park } else { 1169bfd7f386SKyungmin Park printk(KERN_ERR "onenand_bbt_wait: read timeout!" 1170bfd7f386SKyungmin Park "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); 1171bfd7f386SKyungmin Park return ONENAND_BBT_READ_FATAL_ERROR; 1172bfd7f386SKyungmin Park } 1173bfd7f386SKyungmin Park 1174ef0921d6SKyungmin Park /* Initial bad block case: 0x2400 or 0x0400 */ 1175ef0921d6SKyungmin Park if (ctrl & ONENAND_CTRL_ERROR) { 1176ef0921d6SKyungmin Park printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); 1177ef0921d6SKyungmin Park return ONENAND_BBT_READ_ERROR; 1178ef0921d6SKyungmin Park } 1179ef0921d6SKyungmin Park 1180bfd7f386SKyungmin Park return 0; 1181bfd7f386SKyungmin Park } 1182bfd7f386SKyungmin Park 1183bfd7f386SKyungmin Park /** 1184bfd7f386SKyungmin Park * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan 1185bfd7f386SKyungmin Park * @param mtd MTD device structure 1186bfd7f386SKyungmin Park * @param from offset to read from 1187bfd7f386SKyungmin Park * @param ops oob operation description structure 1188bfd7f386SKyungmin Park * 1189bfd7f386SKyungmin Park * OneNAND read out-of-band data from the spare area for bbt scan 1190bfd7f386SKyungmin Park */ 1191bfd7f386SKyungmin Park int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, 1192bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 119359829cc1SJean-Christophe PLAGNIOL-VILLARD { 119459829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 119559829cc1SJean-Christophe PLAGNIOL-VILLARD int read = 0, thislen, column; 1196cacbe919SAmul Kumar Saha int ret = 0, readcmd; 1197bfd7f386SKyungmin Park size_t len = ops->ooblen; 1198bfd7f386SKyungmin Park u_char *buf = ops->oobbuf; 119959829cc1SJean-Christophe PLAGNIOL-VILLARD 1200ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); 120159829cc1SJean-Christophe PLAGNIOL-VILLARD 1202e26fd3d3SLukasz Majewski readcmd = ONENAND_IS_4KB_PAGE(this) ? 1203e26fd3d3SLukasz Majewski ONENAND_CMD_READ : ONENAND_CMD_READOOB; 1204cacbe919SAmul Kumar Saha 1205bfd7f386SKyungmin Park /* Initialize return value */ 1206bfd7f386SKyungmin Park ops->oobretlen = 0; 120759829cc1SJean-Christophe PLAGNIOL-VILLARD 120859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do not allow reads past end of device */ 120959829cc1SJean-Christophe PLAGNIOL-VILLARD if (unlikely((from + len) > mtd->size)) { 1210bfd7f386SKyungmin Park printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); 1211bfd7f386SKyungmin Park return ONENAND_BBT_READ_FATAL_ERROR; 121259829cc1SJean-Christophe PLAGNIOL-VILLARD } 121359829cc1SJean-Christophe PLAGNIOL-VILLARD 121459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Grab the lock and see if the device is available */ 121559829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_READING); 121659829cc1SJean-Christophe PLAGNIOL-VILLARD 121759829cc1SJean-Christophe PLAGNIOL-VILLARD column = from & (mtd->oobsize - 1); 121859829cc1SJean-Christophe PLAGNIOL-VILLARD 121959829cc1SJean-Christophe PLAGNIOL-VILLARD while (read < len) { 1220bfd7f386SKyungmin Park 122159829cc1SJean-Christophe PLAGNIOL-VILLARD thislen = mtd->oobsize - column; 122259829cc1SJean-Christophe PLAGNIOL-VILLARD thislen = min_t(int, thislen, len); 122359829cc1SJean-Christophe PLAGNIOL-VILLARD 1224ef0921d6SKyungmin Park this->spare_buf = buf; 1225cacbe919SAmul Kumar Saha this->command(mtd, readcmd, from, mtd->oobsize); 122659829cc1SJean-Christophe PLAGNIOL-VILLARD 122759829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_update_bufferram(mtd, from, 0); 122859829cc1SJean-Christophe PLAGNIOL-VILLARD 1229ef0921d6SKyungmin Park ret = this->bbt_wait(mtd, FL_READING); 1230cacbe919SAmul Kumar Saha if (unlikely(ret)) 1231cacbe919SAmul Kumar Saha ret = onenand_recover_lsb(mtd, from, ret); 1232cacbe919SAmul Kumar Saha 1233bfd7f386SKyungmin Park if (ret) 1234bfd7f386SKyungmin Park break; 123559829cc1SJean-Christophe PLAGNIOL-VILLARD 1236ce3277a6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); 123759829cc1SJean-Christophe PLAGNIOL-VILLARD read += thislen; 123859829cc1SJean-Christophe PLAGNIOL-VILLARD if (read == len) 123959829cc1SJean-Christophe PLAGNIOL-VILLARD break; 124059829cc1SJean-Christophe PLAGNIOL-VILLARD 124159829cc1SJean-Christophe PLAGNIOL-VILLARD buf += thislen; 1242bfd7f386SKyungmin Park 124359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Read more? */ 124459829cc1SJean-Christophe PLAGNIOL-VILLARD if (read < len) { 1245bfd7f386SKyungmin Park /* Update Page size */ 1246bfd7f386SKyungmin Park from += this->writesize; 124759829cc1SJean-Christophe PLAGNIOL-VILLARD column = 0; 124859829cc1SJean-Christophe PLAGNIOL-VILLARD } 124959829cc1SJean-Christophe PLAGNIOL-VILLARD } 125059829cc1SJean-Christophe PLAGNIOL-VILLARD 125159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Deselect and wake up anyone waiting on the device */ 125259829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 125359829cc1SJean-Christophe PLAGNIOL-VILLARD 1254bfd7f386SKyungmin Park ops->oobretlen = read; 125559829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 125659829cc1SJean-Christophe PLAGNIOL-VILLARD } 125759829cc1SJean-Christophe PLAGNIOL-VILLARD 1258bfd7f386SKyungmin Park 125959829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE 126059829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1261bfd7f386SKyungmin Park * onenand_verify_oob - [GENERIC] verify the oob contents after a write 126259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 126359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buf the databuffer to verify 1264bfd7f386SKyungmin Park * @param to offset to read from 126559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1266bfd7f386SKyungmin Park static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) 126759829cc1SJean-Christophe PLAGNIOL-VILLARD { 126859829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 1269bfd7f386SKyungmin Park u_char *oob_buf = this->oob_buf; 1270cacbe919SAmul Kumar Saha int status, i, readcmd; 127159829cc1SJean-Christophe PLAGNIOL-VILLARD 1272e26fd3d3SLukasz Majewski readcmd = ONENAND_IS_4KB_PAGE(this) ? 1273e26fd3d3SLukasz Majewski ONENAND_CMD_READ : ONENAND_CMD_READOOB; 1274cacbe919SAmul Kumar Saha 1275cacbe919SAmul Kumar Saha this->command(mtd, readcmd, to, mtd->oobsize); 1276bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to, 0); 1277bfd7f386SKyungmin Park status = this->wait(mtd, FL_READING); 1278bfd7f386SKyungmin Park if (status) 1279bfd7f386SKyungmin Park return status; 1280bfd7f386SKyungmin Park 1281ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); 1282bfd7f386SKyungmin Park for (i = 0; i < mtd->oobsize; i++) 1283bfd7f386SKyungmin Park if (buf[i] != 0xFF && buf[i] != oob_buf[i]) 1284bfd7f386SKyungmin Park return -EBADMSG; 1285bfd7f386SKyungmin Park 1286bfd7f386SKyungmin Park return 0; 1287bfd7f386SKyungmin Park } 1288bfd7f386SKyungmin Park 1289bfd7f386SKyungmin Park /** 1290bfd7f386SKyungmin Park * onenand_verify - [GENERIC] verify the chip contents after a write 1291bfd7f386SKyungmin Park * @param mtd MTD device structure 1292bfd7f386SKyungmin Park * @param buf the databuffer to verify 1293bfd7f386SKyungmin Park * @param addr offset to read from 1294bfd7f386SKyungmin Park * @param len number of bytes to read and compare 1295bfd7f386SKyungmin Park */ 1296bfd7f386SKyungmin Park static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) 1297bfd7f386SKyungmin Park { 1298bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 1299bfd7f386SKyungmin Park void __iomem *dataram; 1300bfd7f386SKyungmin Park int ret = 0; 1301bfd7f386SKyungmin Park int thislen, column; 1302bfd7f386SKyungmin Park 1303bfd7f386SKyungmin Park while (len != 0) { 1304bfd7f386SKyungmin Park thislen = min_t(int, this->writesize, len); 1305bfd7f386SKyungmin Park column = addr & (this->writesize - 1); 1306bfd7f386SKyungmin Park if (column + thislen > this->writesize) 1307bfd7f386SKyungmin Park thislen = this->writesize - column; 1308bfd7f386SKyungmin Park 1309bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); 1310bfd7f386SKyungmin Park 1311bfd7f386SKyungmin Park onenand_update_bufferram(mtd, addr, 0); 131259829cc1SJean-Christophe PLAGNIOL-VILLARD 131359829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_READING); 131459829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret) 131559829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 131659829cc1SJean-Christophe PLAGNIOL-VILLARD 131759829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_update_bufferram(mtd, addr, 1); 131859829cc1SJean-Christophe PLAGNIOL-VILLARD 1319bfd7f386SKyungmin Park dataram = this->base + ONENAND_DATARAM; 1320bfd7f386SKyungmin Park dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); 132159829cc1SJean-Christophe PLAGNIOL-VILLARD 1322bfd7f386SKyungmin Park if (memcmp(buf, dataram + column, thislen)) 132359829cc1SJean-Christophe PLAGNIOL-VILLARD return -EBADMSG; 132459829cc1SJean-Christophe PLAGNIOL-VILLARD 1325bfd7f386SKyungmin Park len -= thislen; 1326bfd7f386SKyungmin Park buf += thislen; 1327bfd7f386SKyungmin Park addr += thislen; 1328bfd7f386SKyungmin Park } 1329bfd7f386SKyungmin Park 133059829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 133159829cc1SJean-Christophe PLAGNIOL-VILLARD } 133259829cc1SJean-Christophe PLAGNIOL-VILLARD #else 1333bfd7f386SKyungmin Park #define onenand_verify(...) (0) 1334bfd7f386SKyungmin Park #define onenand_verify_oob(...) (0) 133559829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 133659829cc1SJean-Christophe PLAGNIOL-VILLARD 13371ae39862SStefan Roese #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) 133859829cc1SJean-Christophe PLAGNIOL-VILLARD 133959829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1340*dfe64e2cSSergey Lapin * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer 134159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 1342bfd7f386SKyungmin Park * @param oob_buf oob buffer 1343bfd7f386SKyungmin Park * @param buf source address 1344bfd7f386SKyungmin Park * @param column oob offset to write to 1345bfd7f386SKyungmin Park * @param thislen oob length to write 134659829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1347bfd7f386SKyungmin Park static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, 1348bfd7f386SKyungmin Park const u_char *buf, int column, int thislen) 134959829cc1SJean-Christophe PLAGNIOL-VILLARD { 135059829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 1351bfd7f386SKyungmin Park struct nand_oobfree *free; 1352bfd7f386SKyungmin Park int writecol = column; 1353bfd7f386SKyungmin Park int writeend = column + thislen; 1354bfd7f386SKyungmin Park int lastgap = 0; 1355bfd7f386SKyungmin Park unsigned int i; 1356bfd7f386SKyungmin Park 1357bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 1358bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 1359bfd7f386SKyungmin Park if (writecol >= lastgap) 1360bfd7f386SKyungmin Park writecol += free->offset - lastgap; 1361bfd7f386SKyungmin Park if (writeend >= lastgap) 1362bfd7f386SKyungmin Park writeend += free->offset - lastgap; 1363bfd7f386SKyungmin Park lastgap = free->offset + free->length; 1364bfd7f386SKyungmin Park } 1365bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 1366bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 1367bfd7f386SKyungmin Park int free_end = free->offset + free->length; 1368bfd7f386SKyungmin Park if (free->offset < writeend && free_end > writecol) { 1369bfd7f386SKyungmin Park int st = max_t(int,free->offset,writecol); 1370bfd7f386SKyungmin Park int ed = min_t(int,free_end,writeend); 1371bfd7f386SKyungmin Park int n = ed - st; 1372bfd7f386SKyungmin Park memcpy(oob_buf + st, buf, n); 1373bfd7f386SKyungmin Park buf += n; 1374bfd7f386SKyungmin Park } else if (column == 0) 1375bfd7f386SKyungmin Park break; 1376bfd7f386SKyungmin Park } 1377bfd7f386SKyungmin Park return 0; 1378bfd7f386SKyungmin Park } 1379bfd7f386SKyungmin Park 1380bfd7f386SKyungmin Park /** 1381bfd7f386SKyungmin Park * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band 1382bfd7f386SKyungmin Park * @param mtd MTD device structure 1383bfd7f386SKyungmin Park * @param to offset to write to 1384bfd7f386SKyungmin Park * @param ops oob operation description structure 1385bfd7f386SKyungmin Park * 1386bfd7f386SKyungmin Park * Write main and/or oob with ECC 1387bfd7f386SKyungmin Park */ 1388bfd7f386SKyungmin Park static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, 1389bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 1390bfd7f386SKyungmin Park { 1391bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 1392bfd7f386SKyungmin Park int written = 0, column, thislen, subpage; 1393bfd7f386SKyungmin Park int oobwritten = 0, oobcolumn, thisooblen, oobsize; 1394bfd7f386SKyungmin Park size_t len = ops->len; 1395bfd7f386SKyungmin Park size_t ooblen = ops->ooblen; 1396bfd7f386SKyungmin Park const u_char *buf = ops->datbuf; 1397bfd7f386SKyungmin Park const u_char *oob = ops->oobbuf; 1398bfd7f386SKyungmin Park u_char *oobbuf; 139959829cc1SJean-Christophe PLAGNIOL-VILLARD int ret = 0; 140059829cc1SJean-Christophe PLAGNIOL-VILLARD 1401ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); 140259829cc1SJean-Christophe PLAGNIOL-VILLARD 140359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Initialize retlen, in case of early exit */ 1404bfd7f386SKyungmin Park ops->retlen = 0; 1405bfd7f386SKyungmin Park ops->oobretlen = 0; 140659829cc1SJean-Christophe PLAGNIOL-VILLARD 140759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Reject writes, which are not page aligned */ 1408bfd7f386SKyungmin Park if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { 1409bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); 141059829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 141159829cc1SJean-Christophe PLAGNIOL-VILLARD } 141259829cc1SJean-Christophe PLAGNIOL-VILLARD 1413*dfe64e2cSSergey Lapin if (ops->mode == MTD_OPS_AUTO_OOB) 1414bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 1415bfd7f386SKyungmin Park else 1416bfd7f386SKyungmin Park oobsize = mtd->oobsize; 1417bfd7f386SKyungmin Park 1418bfd7f386SKyungmin Park oobcolumn = to & (mtd->oobsize - 1); 1419bfd7f386SKyungmin Park 1420bfd7f386SKyungmin Park column = to & (mtd->writesize - 1); 142159829cc1SJean-Christophe PLAGNIOL-VILLARD 142259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Loop until all data write */ 142359829cc1SJean-Christophe PLAGNIOL-VILLARD while (written < len) { 1424bfd7f386SKyungmin Park u_char *wbuf = (u_char *) buf; 142559829cc1SJean-Christophe PLAGNIOL-VILLARD 1426bfd7f386SKyungmin Park thislen = min_t(int, mtd->writesize - column, len - written); 1427bfd7f386SKyungmin Park thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); 142859829cc1SJean-Christophe PLAGNIOL-VILLARD 1429bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); 1430bfd7f386SKyungmin Park 1431bfd7f386SKyungmin Park /* Partial page write */ 1432bfd7f386SKyungmin Park subpage = thislen < mtd->writesize; 1433bfd7f386SKyungmin Park if (subpage) { 1434bfd7f386SKyungmin Park memset(this->page_buf, 0xff, mtd->writesize); 1435bfd7f386SKyungmin Park memcpy(this->page_buf + column, buf, thislen); 1436bfd7f386SKyungmin Park wbuf = this->page_buf; 1437bfd7f386SKyungmin Park } 1438bfd7f386SKyungmin Park 1439ef0921d6SKyungmin Park this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize); 1440bfd7f386SKyungmin Park 1441bfd7f386SKyungmin Park if (oob) { 1442bfd7f386SKyungmin Park oobbuf = this->oob_buf; 1443bfd7f386SKyungmin Park 1444bfd7f386SKyungmin Park /* We send data to spare ram with oobsize 1445bfd7f386SKyungmin Park * * to prevent byte access */ 1446bfd7f386SKyungmin Park memset(oobbuf, 0xff, mtd->oobsize); 1447*dfe64e2cSSergey Lapin if (ops->mode == MTD_OPS_AUTO_OOB) 1448bfd7f386SKyungmin Park onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); 1449bfd7f386SKyungmin Park else 1450bfd7f386SKyungmin Park memcpy(oobbuf + oobcolumn, oob, thisooblen); 1451bfd7f386SKyungmin Park 1452bfd7f386SKyungmin Park oobwritten += thisooblen; 1453bfd7f386SKyungmin Park oob += thisooblen; 1454bfd7f386SKyungmin Park oobcolumn = 0; 1455bfd7f386SKyungmin Park } else 1456bfd7f386SKyungmin Park oobbuf = (u_char *) ffchars; 1457bfd7f386SKyungmin Park 1458ef0921d6SKyungmin Park this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); 145959829cc1SJean-Christophe PLAGNIOL-VILLARD 1460d438d508SKyungmin Park this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); 146159829cc1SJean-Christophe PLAGNIOL-VILLARD 146259829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_WRITING); 1463bfd7f386SKyungmin Park 1464bfd7f386SKyungmin Park /* In partial page write we don't update bufferram */ 1465bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to, !ret && !subpage); 1466bfd7f386SKyungmin Park if (ONENAND_IS_2PLANE(this)) { 1467bfd7f386SKyungmin Park ONENAND_SET_BUFFERRAM1(this); 1468bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); 1469bfd7f386SKyungmin Park } 1470bfd7f386SKyungmin Park 147159829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret) { 1472bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); 1473bfd7f386SKyungmin Park break; 1474bfd7f386SKyungmin Park } 1475bfd7f386SKyungmin Park 1476bfd7f386SKyungmin Park /* Only check verify write turn on */ 1477bfd7f386SKyungmin Park ret = onenand_verify(mtd, buf, to, thislen); 1478bfd7f386SKyungmin Park if (ret) { 1479bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); 148059829cc1SJean-Christophe PLAGNIOL-VILLARD break; 148159829cc1SJean-Christophe PLAGNIOL-VILLARD } 148259829cc1SJean-Christophe PLAGNIOL-VILLARD 148359829cc1SJean-Christophe PLAGNIOL-VILLARD written += thislen; 148459829cc1SJean-Christophe PLAGNIOL-VILLARD 148559829cc1SJean-Christophe PLAGNIOL-VILLARD if (written == len) 148659829cc1SJean-Christophe PLAGNIOL-VILLARD break; 148759829cc1SJean-Christophe PLAGNIOL-VILLARD 1488bfd7f386SKyungmin Park column = 0; 148959829cc1SJean-Christophe PLAGNIOL-VILLARD to += thislen; 149059829cc1SJean-Christophe PLAGNIOL-VILLARD buf += thislen; 149159829cc1SJean-Christophe PLAGNIOL-VILLARD } 149259829cc1SJean-Christophe PLAGNIOL-VILLARD 1493bfd7f386SKyungmin Park ops->retlen = written; 149459829cc1SJean-Christophe PLAGNIOL-VILLARD 1495bfd7f386SKyungmin Park return ret; 1496bfd7f386SKyungmin Park } 1497bfd7f386SKyungmin Park 1498bfd7f386SKyungmin Park /** 1499*dfe64e2cSSergey Lapin * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band 1500bfd7f386SKyungmin Park * @param mtd MTD device structure 1501bfd7f386SKyungmin Park * @param to offset to write to 1502bfd7f386SKyungmin Park * @param len number of bytes to write 1503bfd7f386SKyungmin Park * @param retlen pointer to variable to store the number of written bytes 1504bfd7f386SKyungmin Park * @param buf the data to write 1505bfd7f386SKyungmin Park * @param mode operation mode 1506bfd7f386SKyungmin Park * 1507bfd7f386SKyungmin Park * OneNAND write out-of-band 1508bfd7f386SKyungmin Park */ 1509bfd7f386SKyungmin Park static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, 1510bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 1511bfd7f386SKyungmin Park { 1512bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 1513bfd7f386SKyungmin Park int column, ret = 0, oobsize; 1514cacbe919SAmul Kumar Saha int written = 0, oobcmd; 1515bfd7f386SKyungmin Park u_char *oobbuf; 1516bfd7f386SKyungmin Park size_t len = ops->ooblen; 1517bfd7f386SKyungmin Park const u_char *buf = ops->oobbuf; 1518*dfe64e2cSSergey Lapin unsigned int mode = ops->mode; 1519bfd7f386SKyungmin Park 1520bfd7f386SKyungmin Park to += ops->ooboffs; 1521bfd7f386SKyungmin Park 1522ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); 1523bfd7f386SKyungmin Park 1524bfd7f386SKyungmin Park /* Initialize retlen, in case of early exit */ 1525bfd7f386SKyungmin Park ops->oobretlen = 0; 1526bfd7f386SKyungmin Park 1527*dfe64e2cSSergey Lapin if (mode == MTD_OPS_AUTO_OOB) 1528bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 1529bfd7f386SKyungmin Park else 1530bfd7f386SKyungmin Park oobsize = mtd->oobsize; 1531bfd7f386SKyungmin Park 1532bfd7f386SKyungmin Park column = to & (mtd->oobsize - 1); 1533bfd7f386SKyungmin Park 1534bfd7f386SKyungmin Park if (unlikely(column >= oobsize)) { 1535bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); 1536bfd7f386SKyungmin Park return -EINVAL; 1537bfd7f386SKyungmin Park } 1538bfd7f386SKyungmin Park 1539bfd7f386SKyungmin Park /* For compatibility with NAND: Do not allow write past end of page */ 1540bfd7f386SKyungmin Park if (unlikely(column + len > oobsize)) { 1541bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: " 1542bfd7f386SKyungmin Park "Attempt to write past end of page\n"); 1543bfd7f386SKyungmin Park return -EINVAL; 1544bfd7f386SKyungmin Park } 1545bfd7f386SKyungmin Park 1546bfd7f386SKyungmin Park /* Do not allow reads past end of device */ 1547bfd7f386SKyungmin Park if (unlikely(to >= mtd->size || 1548bfd7f386SKyungmin Park column + len > ((mtd->size >> this->page_shift) - 1549bfd7f386SKyungmin Park (to >> this->page_shift)) * oobsize)) { 1550bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); 1551bfd7f386SKyungmin Park return -EINVAL; 1552bfd7f386SKyungmin Park } 1553bfd7f386SKyungmin Park 1554bfd7f386SKyungmin Park oobbuf = this->oob_buf; 1555bfd7f386SKyungmin Park 1556e26fd3d3SLukasz Majewski oobcmd = ONENAND_IS_4KB_PAGE(this) ? 1557e26fd3d3SLukasz Majewski ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; 1558cacbe919SAmul Kumar Saha 1559bfd7f386SKyungmin Park /* Loop until all data write */ 1560bfd7f386SKyungmin Park while (written < len) { 1561bfd7f386SKyungmin Park int thislen = min_t(int, oobsize, len - written); 1562bfd7f386SKyungmin Park 1563bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); 1564bfd7f386SKyungmin Park 1565bfd7f386SKyungmin Park /* We send data to spare ram with oobsize 1566bfd7f386SKyungmin Park * to prevent byte access */ 1567bfd7f386SKyungmin Park memset(oobbuf, 0xff, mtd->oobsize); 1568*dfe64e2cSSergey Lapin if (mode == MTD_OPS_AUTO_OOB) 1569bfd7f386SKyungmin Park onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); 1570bfd7f386SKyungmin Park else 1571bfd7f386SKyungmin Park memcpy(oobbuf + column, buf, thislen); 1572ef0921d6SKyungmin Park this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); 1573bfd7f386SKyungmin Park 1574e26fd3d3SLukasz Majewski if (ONENAND_IS_4KB_PAGE(this)) { 1575cacbe919SAmul Kumar Saha /* Set main area of DataRAM to 0xff*/ 1576cacbe919SAmul Kumar Saha memset(this->page_buf, 0xff, mtd->writesize); 1577cacbe919SAmul Kumar Saha this->write_bufferram(mtd, 0, ONENAND_DATARAM, 1578cacbe919SAmul Kumar Saha this->page_buf, 0, mtd->writesize); 1579cacbe919SAmul Kumar Saha } 1580cacbe919SAmul Kumar Saha 1581cacbe919SAmul Kumar Saha this->command(mtd, oobcmd, to, mtd->oobsize); 1582bfd7f386SKyungmin Park 1583bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to, 0); 1584bfd7f386SKyungmin Park if (ONENAND_IS_2PLANE(this)) { 1585bfd7f386SKyungmin Park ONENAND_SET_BUFFERRAM1(this); 1586bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to + this->writesize, 0); 1587bfd7f386SKyungmin Park } 1588bfd7f386SKyungmin Park 1589bfd7f386SKyungmin Park ret = this->wait(mtd, FL_WRITING); 1590bfd7f386SKyungmin Park if (ret) { 1591bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); 1592bfd7f386SKyungmin Park break; 1593bfd7f386SKyungmin Park } 1594bfd7f386SKyungmin Park 1595bfd7f386SKyungmin Park ret = onenand_verify_oob(mtd, oobbuf, to); 1596bfd7f386SKyungmin Park if (ret) { 1597bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); 1598bfd7f386SKyungmin Park break; 1599bfd7f386SKyungmin Park } 1600bfd7f386SKyungmin Park 1601bfd7f386SKyungmin Park written += thislen; 1602bfd7f386SKyungmin Park if (written == len) 1603bfd7f386SKyungmin Park break; 1604bfd7f386SKyungmin Park 1605bfd7f386SKyungmin Park to += mtd->writesize; 1606bfd7f386SKyungmin Park buf += thislen; 1607bfd7f386SKyungmin Park column = 0; 1608bfd7f386SKyungmin Park } 1609bfd7f386SKyungmin Park 1610bfd7f386SKyungmin Park ops->oobretlen = written; 161159829cc1SJean-Christophe PLAGNIOL-VILLARD 161259829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 161359829cc1SJean-Christophe PLAGNIOL-VILLARD } 161459829cc1SJean-Christophe PLAGNIOL-VILLARD 161559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 161659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_write - [MTD Interface] compability function for onenand_write_ecc 161759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 161859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param to offset to write to 161959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param len number of bytes to write 162059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param retlen pointer to variable to store the number of written bytes 162159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buf the data to write 162259829cc1SJean-Christophe PLAGNIOL-VILLARD * 1623bfd7f386SKyungmin Park * Write with ECC 162459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 162559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, 162659829cc1SJean-Christophe PLAGNIOL-VILLARD size_t * retlen, const u_char * buf) 162759829cc1SJean-Christophe PLAGNIOL-VILLARD { 1628bfd7f386SKyungmin Park struct mtd_oob_ops ops = { 1629bfd7f386SKyungmin Park .len = len, 1630bfd7f386SKyungmin Park .ooblen = 0, 1631bfd7f386SKyungmin Park .datbuf = (u_char *) buf, 1632bfd7f386SKyungmin Park .oobbuf = NULL, 1633bfd7f386SKyungmin Park }; 1634bfd7f386SKyungmin Park int ret; 1635bfd7f386SKyungmin Park 1636bfd7f386SKyungmin Park onenand_get_device(mtd, FL_WRITING); 1637bfd7f386SKyungmin Park ret = onenand_write_ops_nolock(mtd, to, &ops); 1638bfd7f386SKyungmin Park onenand_release_device(mtd); 1639bfd7f386SKyungmin Park 1640bfd7f386SKyungmin Park *retlen = ops.retlen; 1641bfd7f386SKyungmin Park return ret; 164259829cc1SJean-Christophe PLAGNIOL-VILLARD } 164359829cc1SJean-Christophe PLAGNIOL-VILLARD 164459829cc1SJean-Christophe PLAGNIOL-VILLARD /** 164559829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_write_oob - [MTD Interface] OneNAND write out-of-band 164659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 164759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param to offset to write to 1648bfd7f386SKyungmin Park * @param ops oob operation description structure 164959829cc1SJean-Christophe PLAGNIOL-VILLARD * 1650bfd7f386SKyungmin Park * OneNAND write main and/or out-of-band 165159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1652bfd7f386SKyungmin Park int onenand_write_oob(struct mtd_info *mtd, loff_t to, 1653bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 165459829cc1SJean-Christophe PLAGNIOL-VILLARD { 1655bfd7f386SKyungmin Park int ret; 165659829cc1SJean-Christophe PLAGNIOL-VILLARD 1657bfd7f386SKyungmin Park switch (ops->mode) { 1658*dfe64e2cSSergey Lapin case MTD_OPS_PLACE_OOB: 1659*dfe64e2cSSergey Lapin case MTD_OPS_AUTO_OOB: 1660bfd7f386SKyungmin Park break; 1661*dfe64e2cSSergey Lapin case MTD_OPS_RAW: 1662bfd7f386SKyungmin Park /* Not implemented yet */ 1663bfd7f386SKyungmin Park default: 166459829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 166559829cc1SJean-Christophe PLAGNIOL-VILLARD } 166659829cc1SJean-Christophe PLAGNIOL-VILLARD 166759829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_WRITING); 1668bfd7f386SKyungmin Park if (ops->datbuf) 1669bfd7f386SKyungmin Park ret = onenand_write_ops_nolock(mtd, to, ops); 1670bfd7f386SKyungmin Park else 1671bfd7f386SKyungmin Park ret = onenand_write_oob_nolock(mtd, to, ops); 167259829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 167359829cc1SJean-Christophe PLAGNIOL-VILLARD 1674bfd7f386SKyungmin Park return ret; 167559829cc1SJean-Christophe PLAGNIOL-VILLARD 167659829cc1SJean-Christophe PLAGNIOL-VILLARD } 167759829cc1SJean-Christophe PLAGNIOL-VILLARD 167859829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1679d438d508SKyungmin Park * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad 1680d438d508SKyungmin Park * @param mtd MTD device structure 1681d438d508SKyungmin Park * @param ofs offset from device start 1682d438d508SKyungmin Park * @param allowbbt 1, if its allowed to access the bbt area 1683d438d508SKyungmin Park * 1684d438d508SKyungmin Park * Check, if the block is bad, Either by reading the bad block table or 1685d438d508SKyungmin Park * calling of the scan function. 1686d438d508SKyungmin Park */ 1687d438d508SKyungmin Park static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) 1688d438d508SKyungmin Park { 1689d438d508SKyungmin Park struct onenand_chip *this = mtd->priv; 1690d438d508SKyungmin Park struct bbm_info *bbm = this->bbm; 1691d438d508SKyungmin Park 1692d438d508SKyungmin Park /* Return info from the table */ 1693d438d508SKyungmin Park return bbm->isbad_bbt(mtd, ofs, allowbbt); 1694d438d508SKyungmin Park } 1695d438d508SKyungmin Park 1696d438d508SKyungmin Park 1697d438d508SKyungmin Park /** 169859829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_erase - [MTD Interface] erase block(s) 169959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 170059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param instr erase instruction 170159829cc1SJean-Christophe PLAGNIOL-VILLARD * 170259829cc1SJean-Christophe PLAGNIOL-VILLARD * Erase one ore more blocks 170359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 170459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) 170559829cc1SJean-Christophe PLAGNIOL-VILLARD { 170659829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 170759829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned int block_size; 1708cacbe919SAmul Kumar Saha loff_t addr = instr->addr; 1709cacbe919SAmul Kumar Saha unsigned int len = instr->len; 1710cacbe919SAmul Kumar Saha int ret = 0, i; 1711cacbe919SAmul Kumar Saha struct mtd_erase_region_info *region = NULL; 1712cacbe919SAmul Kumar Saha unsigned int region_end = 0; 171359829cc1SJean-Christophe PLAGNIOL-VILLARD 1714cacbe919SAmul Kumar Saha MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", 1715cacbe919SAmul Kumar Saha (unsigned int) addr, len); 171659829cc1SJean-Christophe PLAGNIOL-VILLARD 1717cacbe919SAmul Kumar Saha if (FLEXONENAND(this)) { 1718cacbe919SAmul Kumar Saha /* Find the eraseregion of this address */ 1719cacbe919SAmul Kumar Saha i = flexonenand_region(mtd, addr); 1720cacbe919SAmul Kumar Saha region = &mtd->eraseregions[i]; 1721cacbe919SAmul Kumar Saha 1722cacbe919SAmul Kumar Saha block_size = region->erasesize; 1723cacbe919SAmul Kumar Saha region_end = region->offset 1724cacbe919SAmul Kumar Saha + region->erasesize * region->numblocks; 1725cacbe919SAmul Kumar Saha 1726cacbe919SAmul Kumar Saha /* Start address within region must align on block boundary. 1727cacbe919SAmul Kumar Saha * Erase region's start offset is always block start address. 1728cacbe919SAmul Kumar Saha */ 1729cacbe919SAmul Kumar Saha if (unlikely((addr - region->offset) & (block_size - 1))) { 1730cacbe919SAmul Kumar Saha MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" 1731cacbe919SAmul Kumar Saha " Unaligned address\n"); 1732cacbe919SAmul Kumar Saha return -EINVAL; 1733cacbe919SAmul Kumar Saha } 1734cacbe919SAmul Kumar Saha } else { 1735cacbe919SAmul Kumar Saha block_size = 1 << this->erase_shift; 173659829cc1SJean-Christophe PLAGNIOL-VILLARD 173759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Start address must align on block boundary */ 1738cacbe919SAmul Kumar Saha if (unlikely(addr & (block_size - 1))) { 1739cacbe919SAmul Kumar Saha MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" 1740cacbe919SAmul Kumar Saha "Unaligned address\n"); 174159829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 174259829cc1SJean-Christophe PLAGNIOL-VILLARD } 1743cacbe919SAmul Kumar Saha } 174459829cc1SJean-Christophe PLAGNIOL-VILLARD 174559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Length must align on block boundary */ 1746cacbe919SAmul Kumar Saha if (unlikely(len & (block_size - 1))) { 17473167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, 174859829cc1SJean-Christophe PLAGNIOL-VILLARD "onenand_erase: Length not block aligned\n"); 174959829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 175059829cc1SJean-Christophe PLAGNIOL-VILLARD } 175159829cc1SJean-Christophe PLAGNIOL-VILLARD 175259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Grab the lock and see if the device is available */ 175359829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_ERASING); 175459829cc1SJean-Christophe PLAGNIOL-VILLARD 175559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Loop throught the pages */ 175659829cc1SJean-Christophe PLAGNIOL-VILLARD instr->state = MTD_ERASING; 175759829cc1SJean-Christophe PLAGNIOL-VILLARD 175859829cc1SJean-Christophe PLAGNIOL-VILLARD while (len) { 175959829cc1SJean-Christophe PLAGNIOL-VILLARD 1760ef0921d6SKyungmin Park /* Check if we have a bad block, we do not erase bad blocks */ 1761ef0921d6SKyungmin Park if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) { 1762ef0921d6SKyungmin Park printk(KERN_WARNING "onenand_erase: attempt to erase" 1763ef0921d6SKyungmin Park " a bad block at addr 0x%08x\n", 1764ef0921d6SKyungmin Park (unsigned int) addr); 1765ef0921d6SKyungmin Park instr->state = MTD_ERASE_FAILED; 1766ef0921d6SKyungmin Park goto erase_exit; 1767ef0921d6SKyungmin Park } 176859829cc1SJean-Christophe PLAGNIOL-VILLARD 176959829cc1SJean-Christophe PLAGNIOL-VILLARD this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); 177059829cc1SJean-Christophe PLAGNIOL-VILLARD 1771d438d508SKyungmin Park onenand_invalidate_bufferram(mtd, addr, block_size); 1772d438d508SKyungmin Park 177359829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_ERASING); 177459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check, if it is write protected */ 177559829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret) { 177659829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret == -EPERM) 17773167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " 17783167c538SScott Wood "Device is write protected!!!\n"); 177959829cc1SJean-Christophe PLAGNIOL-VILLARD else 17803167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " 17813167c538SScott Wood "Failed erase, block %d\n", 1782cacbe919SAmul Kumar Saha onenand_block(this, addr)); 178359829cc1SJean-Christophe PLAGNIOL-VILLARD instr->state = MTD_ERASE_FAILED; 178459829cc1SJean-Christophe PLAGNIOL-VILLARD instr->fail_addr = addr; 1785ef0921d6SKyungmin Park 178659829cc1SJean-Christophe PLAGNIOL-VILLARD goto erase_exit; 178759829cc1SJean-Christophe PLAGNIOL-VILLARD } 178859829cc1SJean-Christophe PLAGNIOL-VILLARD 178959829cc1SJean-Christophe PLAGNIOL-VILLARD len -= block_size; 179059829cc1SJean-Christophe PLAGNIOL-VILLARD addr += block_size; 1791cacbe919SAmul Kumar Saha 1792cacbe919SAmul Kumar Saha if (addr == region_end) { 1793cacbe919SAmul Kumar Saha if (!len) 1794cacbe919SAmul Kumar Saha break; 1795cacbe919SAmul Kumar Saha region++; 1796cacbe919SAmul Kumar Saha 1797cacbe919SAmul Kumar Saha block_size = region->erasesize; 1798cacbe919SAmul Kumar Saha region_end = region->offset 1799cacbe919SAmul Kumar Saha + region->erasesize * region->numblocks; 1800cacbe919SAmul Kumar Saha 1801cacbe919SAmul Kumar Saha if (len & (block_size - 1)) { 1802cacbe919SAmul Kumar Saha /* This has been checked at MTD 1803cacbe919SAmul Kumar Saha * partitioning level. */ 1804cacbe919SAmul Kumar Saha printk("onenand_erase: Unaligned address\n"); 1805cacbe919SAmul Kumar Saha goto erase_exit; 1806cacbe919SAmul Kumar Saha } 1807cacbe919SAmul Kumar Saha } 180859829cc1SJean-Christophe PLAGNIOL-VILLARD } 180959829cc1SJean-Christophe PLAGNIOL-VILLARD 181059829cc1SJean-Christophe PLAGNIOL-VILLARD instr->state = MTD_ERASE_DONE; 181159829cc1SJean-Christophe PLAGNIOL-VILLARD 181259829cc1SJean-Christophe PLAGNIOL-VILLARD erase_exit: 181359829cc1SJean-Christophe PLAGNIOL-VILLARD 181459829cc1SJean-Christophe PLAGNIOL-VILLARD ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; 181559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do call back function */ 181659829cc1SJean-Christophe PLAGNIOL-VILLARD if (!ret) 181759829cc1SJean-Christophe PLAGNIOL-VILLARD mtd_erase_callback(instr); 181859829cc1SJean-Christophe PLAGNIOL-VILLARD 181959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Deselect and wake up anyone waiting on the device */ 182059829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 182159829cc1SJean-Christophe PLAGNIOL-VILLARD 182259829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 182359829cc1SJean-Christophe PLAGNIOL-VILLARD } 182459829cc1SJean-Christophe PLAGNIOL-VILLARD 182559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 182659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_sync - [MTD Interface] sync 182759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 182859829cc1SJean-Christophe PLAGNIOL-VILLARD * 182959829cc1SJean-Christophe PLAGNIOL-VILLARD * Sync is actually a wait for chip ready function 183059829cc1SJean-Christophe PLAGNIOL-VILLARD */ 183159829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_sync(struct mtd_info *mtd) 183259829cc1SJean-Christophe PLAGNIOL-VILLARD { 18333167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); 183459829cc1SJean-Christophe PLAGNIOL-VILLARD 183559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Grab the lock and see if the device is available */ 183659829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_SYNCING); 183759829cc1SJean-Christophe PLAGNIOL-VILLARD 183859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Release it and go back */ 183959829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 184059829cc1SJean-Christophe PLAGNIOL-VILLARD } 184159829cc1SJean-Christophe PLAGNIOL-VILLARD 184259829cc1SJean-Christophe PLAGNIOL-VILLARD /** 184359829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad 184459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 184559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param ofs offset relative to mtd start 1846d438d508SKyungmin Park * 1847d438d508SKyungmin Park * Check whether the block is bad 184859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 184959829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) 185059829cc1SJean-Christophe PLAGNIOL-VILLARD { 1851d438d508SKyungmin Park int ret; 1852d438d508SKyungmin Park 1853d438d508SKyungmin Park /* Check for invalid offset */ 1854d438d508SKyungmin Park if (ofs > mtd->size) 1855d438d508SKyungmin Park return -EINVAL; 1856d438d508SKyungmin Park 1857d438d508SKyungmin Park onenand_get_device(mtd, FL_READING); 1858d438d508SKyungmin Park ret = onenand_block_isbad_nolock(mtd,ofs, 0); 1859d438d508SKyungmin Park onenand_release_device(mtd); 1860d438d508SKyungmin Park return ret; 186159829cc1SJean-Christophe PLAGNIOL-VILLARD } 186259829cc1SJean-Christophe PLAGNIOL-VILLARD 186359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 18641714f51aSKyungmin Park * onenand_default_block_markbad - [DEFAULT] mark a block bad 18651714f51aSKyungmin Park * @param mtd MTD device structure 18661714f51aSKyungmin Park * @param ofs offset from device start 18671714f51aSKyungmin Park * 18681714f51aSKyungmin Park * This is the default implementation, which can be overridden by 18691714f51aSKyungmin Park * a hardware specific driver. 18701714f51aSKyungmin Park */ 18711714f51aSKyungmin Park static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) 18721714f51aSKyungmin Park { 18731714f51aSKyungmin Park struct onenand_chip *this = mtd->priv; 18741714f51aSKyungmin Park struct bbm_info *bbm = this->bbm; 18751714f51aSKyungmin Park u_char buf[2] = {0, 0}; 18761714f51aSKyungmin Park struct mtd_oob_ops ops = { 1877*dfe64e2cSSergey Lapin .mode = MTD_OPS_PLACE_OOB, 18781714f51aSKyungmin Park .ooblen = 2, 18791714f51aSKyungmin Park .oobbuf = buf, 18801714f51aSKyungmin Park .ooboffs = 0, 18811714f51aSKyungmin Park }; 18821714f51aSKyungmin Park int block; 18831714f51aSKyungmin Park 18841714f51aSKyungmin Park /* Get block number */ 1885cacbe919SAmul Kumar Saha block = onenand_block(this, ofs); 18861714f51aSKyungmin Park if (bbm->bbt) 18871714f51aSKyungmin Park bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); 18881714f51aSKyungmin Park 18891714f51aSKyungmin Park /* We write two bytes, so we dont have to mess with 16 bit access */ 18901714f51aSKyungmin Park ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); 18911714f51aSKyungmin Park return onenand_write_oob_nolock(mtd, ofs, &ops); 18921714f51aSKyungmin Park } 18931714f51aSKyungmin Park 18941714f51aSKyungmin Park /** 189559829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad 189659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 189759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param ofs offset relative to mtd start 1898d438d508SKyungmin Park * 1899d438d508SKyungmin Park * Mark the block as bad 190059829cc1SJean-Christophe PLAGNIOL-VILLARD */ 190159829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) 190259829cc1SJean-Christophe PLAGNIOL-VILLARD { 1903d438d508SKyungmin Park int ret; 1904d438d508SKyungmin Park 1905d438d508SKyungmin Park ret = onenand_block_isbad(mtd, ofs); 1906d438d508SKyungmin Park if (ret) { 1907d438d508SKyungmin Park /* If it was bad already, return success and do nothing */ 1908d438d508SKyungmin Park if (ret > 0) 190959829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 1910d438d508SKyungmin Park return ret; 1911d438d508SKyungmin Park } 1912d438d508SKyungmin Park 1913*dfe64e2cSSergey Lapin ret = mtd_block_markbad(mtd, ofs); 1914d438d508SKyungmin Park return ret; 191559829cc1SJean-Christophe PLAGNIOL-VILLARD } 191659829cc1SJean-Christophe PLAGNIOL-VILLARD 191759829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1918ef0921d6SKyungmin Park * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) 191959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 192059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param ofs offset relative to mtd start 1921ef0921d6SKyungmin Park * @param len number of bytes to lock or unlock 1922ef0921d6SKyungmin Park * @param cmd lock or unlock command 192359829cc1SJean-Christophe PLAGNIOL-VILLARD * 1924ef0921d6SKyungmin Park * Lock or unlock one or more blocks 192559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1926ef0921d6SKyungmin Park static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) 192759829cc1SJean-Christophe PLAGNIOL-VILLARD { 192859829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 192959829cc1SJean-Christophe PLAGNIOL-VILLARD int start, end, block, value, status; 193059829cc1SJean-Christophe PLAGNIOL-VILLARD 1931cacbe919SAmul Kumar Saha start = onenand_block(this, ofs); 1932cacbe919SAmul Kumar Saha end = onenand_block(this, ofs + len); 193359829cc1SJean-Christophe PLAGNIOL-VILLARD 193459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Continuous lock scheme */ 1935ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_CONT_LOCK) { 193659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set start block address */ 193759829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(start, 193859829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_BLOCK_ADDRESS); 193959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set end block address */ 194059829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(end - 1, 194159829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_END_BLOCK_ADDRESS); 194259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write unlock command */ 1943ef0921d6SKyungmin Park this->command(mtd, cmd, 0, 0); 194459829cc1SJean-Christophe PLAGNIOL-VILLARD 194559829cc1SJean-Christophe PLAGNIOL-VILLARD /* There's no return value */ 194659829cc1SJean-Christophe PLAGNIOL-VILLARD this->wait(mtd, FL_UNLOCKING); 194759829cc1SJean-Christophe PLAGNIOL-VILLARD 194859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Sanity check */ 194959829cc1SJean-Christophe PLAGNIOL-VILLARD while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) 195059829cc1SJean-Christophe PLAGNIOL-VILLARD & ONENAND_CTRL_ONGO) 195159829cc1SJean-Christophe PLAGNIOL-VILLARD continue; 195259829cc1SJean-Christophe PLAGNIOL-VILLARD 195359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check lock status */ 195459829cc1SJean-Christophe PLAGNIOL-VILLARD status = this->read_word(this->base + ONENAND_REG_WP_STATUS); 195559829cc1SJean-Christophe PLAGNIOL-VILLARD if (!(status & ONENAND_WP_US)) 195659829cc1SJean-Christophe PLAGNIOL-VILLARD printk(KERN_ERR "wp status = 0x%x\n", status); 195759829cc1SJean-Christophe PLAGNIOL-VILLARD 195859829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 195959829cc1SJean-Christophe PLAGNIOL-VILLARD } 196059829cc1SJean-Christophe PLAGNIOL-VILLARD 196159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Block lock scheme */ 1962cacbe919SAmul Kumar Saha for (block = start; block < end; block++) { 1963ef0921d6SKyungmin Park /* Set block address */ 1964ef0921d6SKyungmin Park value = onenand_block_address(this, block); 1965ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); 1966ef0921d6SKyungmin Park /* Select DataRAM for DDP */ 1967ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 1968ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); 1969ef0921d6SKyungmin Park 197059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set start block address */ 197159829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(block, 197259829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_BLOCK_ADDRESS); 197359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write unlock command */ 197459829cc1SJean-Christophe PLAGNIOL-VILLARD this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); 197559829cc1SJean-Christophe PLAGNIOL-VILLARD 197659829cc1SJean-Christophe PLAGNIOL-VILLARD /* There's no return value */ 197759829cc1SJean-Christophe PLAGNIOL-VILLARD this->wait(mtd, FL_UNLOCKING); 197859829cc1SJean-Christophe PLAGNIOL-VILLARD 197959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Sanity check */ 198059829cc1SJean-Christophe PLAGNIOL-VILLARD while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) 198159829cc1SJean-Christophe PLAGNIOL-VILLARD & ONENAND_CTRL_ONGO) 198259829cc1SJean-Christophe PLAGNIOL-VILLARD continue; 198359829cc1SJean-Christophe PLAGNIOL-VILLARD 198459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check lock status */ 198559829cc1SJean-Christophe PLAGNIOL-VILLARD status = this->read_word(this->base + ONENAND_REG_WP_STATUS); 198659829cc1SJean-Christophe PLAGNIOL-VILLARD if (!(status & ONENAND_WP_US)) 198759829cc1SJean-Christophe PLAGNIOL-VILLARD printk(KERN_ERR "block = %d, wp status = 0x%x\n", 198859829cc1SJean-Christophe PLAGNIOL-VILLARD block, status); 198959829cc1SJean-Christophe PLAGNIOL-VILLARD } 199059829cc1SJean-Christophe PLAGNIOL-VILLARD 199159829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 199259829cc1SJean-Christophe PLAGNIOL-VILLARD } 199359829cc1SJean-Christophe PLAGNIOL-VILLARD 19944fca3310SStefan Roese #ifdef ONENAND_LINUX 199559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1996ef0921d6SKyungmin Park * onenand_lock - [MTD Interface] Lock block(s) 1997ef0921d6SKyungmin Park * @param mtd MTD device structure 1998ef0921d6SKyungmin Park * @param ofs offset relative to mtd start 1999ef0921d6SKyungmin Park * @param len number of bytes to unlock 2000ef0921d6SKyungmin Park * 2001ef0921d6SKyungmin Park * Lock one or more blocks 2002ef0921d6SKyungmin Park */ 2003ef0921d6SKyungmin Park static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) 2004ef0921d6SKyungmin Park { 2005ef0921d6SKyungmin Park int ret; 2006ef0921d6SKyungmin Park 2007ef0921d6SKyungmin Park onenand_get_device(mtd, FL_LOCKING); 2008ef0921d6SKyungmin Park ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); 2009ef0921d6SKyungmin Park onenand_release_device(mtd); 2010ef0921d6SKyungmin Park return ret; 2011ef0921d6SKyungmin Park } 2012ef0921d6SKyungmin Park 2013ef0921d6SKyungmin Park /** 2014ef0921d6SKyungmin Park * onenand_unlock - [MTD Interface] Unlock block(s) 2015ef0921d6SKyungmin Park * @param mtd MTD device structure 2016ef0921d6SKyungmin Park * @param ofs offset relative to mtd start 2017ef0921d6SKyungmin Park * @param len number of bytes to unlock 2018ef0921d6SKyungmin Park * 2019ef0921d6SKyungmin Park * Unlock one or more blocks 2020ef0921d6SKyungmin Park */ 2021ef0921d6SKyungmin Park static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) 2022ef0921d6SKyungmin Park { 2023ef0921d6SKyungmin Park int ret; 2024ef0921d6SKyungmin Park 2025ef0921d6SKyungmin Park onenand_get_device(mtd, FL_LOCKING); 2026ef0921d6SKyungmin Park ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); 2027ef0921d6SKyungmin Park onenand_release_device(mtd); 2028ef0921d6SKyungmin Park return ret; 2029ef0921d6SKyungmin Park } 20304fca3310SStefan Roese #endif 2031ef0921d6SKyungmin Park 2032ef0921d6SKyungmin Park /** 2033ef0921d6SKyungmin Park * onenand_check_lock_status - [OneNAND Interface] Check lock status 2034ef0921d6SKyungmin Park * @param this onenand chip data structure 2035ef0921d6SKyungmin Park * 2036ef0921d6SKyungmin Park * Check lock status 2037ef0921d6SKyungmin Park */ 2038ef0921d6SKyungmin Park static int onenand_check_lock_status(struct onenand_chip *this) 2039ef0921d6SKyungmin Park { 2040ef0921d6SKyungmin Park unsigned int value, block, status; 2041ef0921d6SKyungmin Park unsigned int end; 2042ef0921d6SKyungmin Park 2043ef0921d6SKyungmin Park end = this->chipsize >> this->erase_shift; 2044ef0921d6SKyungmin Park for (block = 0; block < end; block++) { 2045ef0921d6SKyungmin Park /* Set block address */ 2046ef0921d6SKyungmin Park value = onenand_block_address(this, block); 2047ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); 2048ef0921d6SKyungmin Park /* Select DataRAM for DDP */ 2049ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 2050ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); 2051ef0921d6SKyungmin Park /* Set start block address */ 2052ef0921d6SKyungmin Park this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); 2053ef0921d6SKyungmin Park 2054ef0921d6SKyungmin Park /* Check lock status */ 2055ef0921d6SKyungmin Park status = this->read_word(this->base + ONENAND_REG_WP_STATUS); 2056ef0921d6SKyungmin Park if (!(status & ONENAND_WP_US)) { 2057ef0921d6SKyungmin Park printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); 2058ef0921d6SKyungmin Park return 0; 2059ef0921d6SKyungmin Park } 2060ef0921d6SKyungmin Park } 2061ef0921d6SKyungmin Park 2062ef0921d6SKyungmin Park return 1; 2063ef0921d6SKyungmin Park } 2064ef0921d6SKyungmin Park 2065ef0921d6SKyungmin Park /** 2066ef0921d6SKyungmin Park * onenand_unlock_all - [OneNAND Interface] unlock all blocks 2067ef0921d6SKyungmin Park * @param mtd MTD device structure 2068ef0921d6SKyungmin Park * 2069ef0921d6SKyungmin Park * Unlock all blocks 2070ef0921d6SKyungmin Park */ 2071ef0921d6SKyungmin Park static void onenand_unlock_all(struct mtd_info *mtd) 2072ef0921d6SKyungmin Park { 2073ef0921d6SKyungmin Park struct onenand_chip *this = mtd->priv; 2074ef0921d6SKyungmin Park loff_t ofs = 0; 2075cacbe919SAmul Kumar Saha size_t len = mtd->size; 2076ef0921d6SKyungmin Park 2077ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_UNLOCK_ALL) { 2078ef0921d6SKyungmin Park /* Set start block address */ 2079ef0921d6SKyungmin Park this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); 2080ef0921d6SKyungmin Park /* Write unlock command */ 2081ef0921d6SKyungmin Park this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); 2082ef0921d6SKyungmin Park 2083ef0921d6SKyungmin Park /* There's no return value */ 2084ef0921d6SKyungmin Park this->wait(mtd, FL_LOCKING); 2085ef0921d6SKyungmin Park 2086ef0921d6SKyungmin Park /* Sanity check */ 2087ef0921d6SKyungmin Park while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) 2088ef0921d6SKyungmin Park & ONENAND_CTRL_ONGO) 2089ef0921d6SKyungmin Park continue; 2090ef0921d6SKyungmin Park 2091ef0921d6SKyungmin Park /* Check lock status */ 2092ef0921d6SKyungmin Park if (onenand_check_lock_status(this)) 2093ef0921d6SKyungmin Park return; 2094ef0921d6SKyungmin Park 2095ef0921d6SKyungmin Park /* Workaround for all block unlock in DDP */ 2096cacbe919SAmul Kumar Saha if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { 2097ef0921d6SKyungmin Park /* All blocks on another chip */ 2098ef0921d6SKyungmin Park ofs = this->chipsize >> 1; 2099ef0921d6SKyungmin Park len = this->chipsize >> 1; 2100ef0921d6SKyungmin Park } 2101ef0921d6SKyungmin Park } 2102ef0921d6SKyungmin Park 2103ef0921d6SKyungmin Park onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); 2104ef0921d6SKyungmin Park } 2105ef0921d6SKyungmin Park 2106ef0921d6SKyungmin Park 2107ef0921d6SKyungmin Park /** 2108ef0921d6SKyungmin Park * onenand_check_features - Check and set OneNAND features 2109ef0921d6SKyungmin Park * @param mtd MTD data structure 2110ef0921d6SKyungmin Park * 2111ef0921d6SKyungmin Park * Check and set OneNAND features 2112ef0921d6SKyungmin Park * - lock scheme 2113ef0921d6SKyungmin Park * - two plane 2114ef0921d6SKyungmin Park */ 2115ef0921d6SKyungmin Park static void onenand_check_features(struct mtd_info *mtd) 2116ef0921d6SKyungmin Park { 2117ef0921d6SKyungmin Park struct onenand_chip *this = mtd->priv; 2118ef0921d6SKyungmin Park unsigned int density, process; 2119ef0921d6SKyungmin Park 2120ef0921d6SKyungmin Park /* Lock scheme depends on density and process */ 2121ef0921d6SKyungmin Park density = onenand_get_density(this->device_id); 2122ef0921d6SKyungmin Park process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; 2123ef0921d6SKyungmin Park 2124ef0921d6SKyungmin Park /* Lock scheme */ 2125ef0921d6SKyungmin Park switch (density) { 2126ef0921d6SKyungmin Park case ONENAND_DEVICE_DENSITY_4Gb: 2127e26fd3d3SLukasz Majewski if (ONENAND_IS_DDP(this)) 2128ef0921d6SKyungmin Park this->options |= ONENAND_HAS_2PLANE; 2129e26fd3d3SLukasz Majewski else 2130e26fd3d3SLukasz Majewski this->options |= ONENAND_HAS_4KB_PAGE; 2131ef0921d6SKyungmin Park 2132ef0921d6SKyungmin Park case ONENAND_DEVICE_DENSITY_2Gb: 2133ef0921d6SKyungmin Park /* 2Gb DDP don't have 2 plane */ 2134ef0921d6SKyungmin Park if (!ONENAND_IS_DDP(this)) 2135ef0921d6SKyungmin Park this->options |= ONENAND_HAS_2PLANE; 2136ef0921d6SKyungmin Park this->options |= ONENAND_HAS_UNLOCK_ALL; 2137ef0921d6SKyungmin Park 2138ef0921d6SKyungmin Park case ONENAND_DEVICE_DENSITY_1Gb: 2139ef0921d6SKyungmin Park /* A-Die has all block unlock */ 2140ef0921d6SKyungmin Park if (process) 2141ef0921d6SKyungmin Park this->options |= ONENAND_HAS_UNLOCK_ALL; 2142ef0921d6SKyungmin Park break; 2143ef0921d6SKyungmin Park 2144ef0921d6SKyungmin Park default: 2145ef0921d6SKyungmin Park /* Some OneNAND has continuous lock scheme */ 2146ef0921d6SKyungmin Park if (!process) 2147ef0921d6SKyungmin Park this->options |= ONENAND_HAS_CONT_LOCK; 2148ef0921d6SKyungmin Park break; 2149ef0921d6SKyungmin Park } 2150ef0921d6SKyungmin Park 2151cacbe919SAmul Kumar Saha if (ONENAND_IS_MLC(this)) 2152e26fd3d3SLukasz Majewski this->options |= ONENAND_HAS_4KB_PAGE; 2153e26fd3d3SLukasz Majewski 2154e26fd3d3SLukasz Majewski if (ONENAND_IS_4KB_PAGE(this)) 2155cacbe919SAmul Kumar Saha this->options &= ~ONENAND_HAS_2PLANE; 2156cacbe919SAmul Kumar Saha 2157cacbe919SAmul Kumar Saha if (FLEXONENAND(this)) { 2158cacbe919SAmul Kumar Saha this->options &= ~ONENAND_HAS_CONT_LOCK; 2159cacbe919SAmul Kumar Saha this->options |= ONENAND_HAS_UNLOCK_ALL; 2160cacbe919SAmul Kumar Saha } 2161cacbe919SAmul Kumar Saha 2162ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_CONT_LOCK) 2163ef0921d6SKyungmin Park printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); 2164ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_UNLOCK_ALL) 2165ef0921d6SKyungmin Park printk(KERN_DEBUG "Chip support all block unlock\n"); 2166ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_2PLANE) 2167ef0921d6SKyungmin Park printk(KERN_DEBUG "Chip has 2 plane\n"); 2168e26fd3d3SLukasz Majewski if (this->options & ONENAND_HAS_4KB_PAGE) 2169e26fd3d3SLukasz Majewski printk(KERN_DEBUG "Chip has 4KiB pagesize\n"); 2170e26fd3d3SLukasz Majewski 2171ef0921d6SKyungmin Park } 2172ef0921d6SKyungmin Park 2173ef0921d6SKyungmin Park /** 217459829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_print_device_info - Print device ID 217559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param device device ID 217659829cc1SJean-Christophe PLAGNIOL-VILLARD * 217759829cc1SJean-Christophe PLAGNIOL-VILLARD * Print device ID 217859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 2179ef0921d6SKyungmin Park char *onenand_print_device_info(int device, int version) 218059829cc1SJean-Christophe PLAGNIOL-VILLARD { 2181cacbe919SAmul Kumar Saha int vcc, demuxed, ddp, density, flexonenand; 2182195ccfc5SFathi BOUDRA char *dev_info = malloc(80); 2183ef0921d6SKyungmin Park char *p = dev_info; 218459829cc1SJean-Christophe PLAGNIOL-VILLARD 218559829cc1SJean-Christophe PLAGNIOL-VILLARD vcc = device & ONENAND_DEVICE_VCC_MASK; 218659829cc1SJean-Christophe PLAGNIOL-VILLARD demuxed = device & ONENAND_DEVICE_IS_DEMUX; 218759829cc1SJean-Christophe PLAGNIOL-VILLARD ddp = device & ONENAND_DEVICE_IS_DDP; 2188cacbe919SAmul Kumar Saha density = onenand_get_density(device); 2189cacbe919SAmul Kumar Saha flexonenand = device & DEVICE_IS_FLEXONENAND; 2190cacbe919SAmul Kumar Saha p += sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)", 219159829cc1SJean-Christophe PLAGNIOL-VILLARD demuxed ? "" : "Muxed ", 2192cacbe919SAmul Kumar Saha flexonenand ? "Flex-" : "", 219359829cc1SJean-Christophe PLAGNIOL-VILLARD ddp ? "(DDP)" : "", 219459829cc1SJean-Christophe PLAGNIOL-VILLARD (16 << density), vcc ? "2.65/3.3" : "1.8", device); 2195195ccfc5SFathi BOUDRA 2196ef0921d6SKyungmin Park sprintf(p, "\nOneNAND version = 0x%04x", version); 2197ef0921d6SKyungmin Park printk("%s\n", dev_info); 2198ef0921d6SKyungmin Park 2199195ccfc5SFathi BOUDRA return dev_info; 220059829cc1SJean-Christophe PLAGNIOL-VILLARD } 220159829cc1SJean-Christophe PLAGNIOL-VILLARD 220259829cc1SJean-Christophe PLAGNIOL-VILLARD static const struct onenand_manufacturers onenand_manuf_ids[] = { 2203456be17dSEnric Balletbo i Serra {ONENAND_MFR_NUMONYX, "Numonyx"}, 220459829cc1SJean-Christophe PLAGNIOL-VILLARD {ONENAND_MFR_SAMSUNG, "Samsung"}, 220559829cc1SJean-Christophe PLAGNIOL-VILLARD }; 220659829cc1SJean-Christophe PLAGNIOL-VILLARD 220759829cc1SJean-Christophe PLAGNIOL-VILLARD /** 220859829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_check_maf - Check manufacturer ID 220959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param manuf manufacturer ID 221059829cc1SJean-Christophe PLAGNIOL-VILLARD * 221159829cc1SJean-Christophe PLAGNIOL-VILLARD * Check manufacturer ID 221259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 221359829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_maf(int manuf) 221459829cc1SJean-Christophe PLAGNIOL-VILLARD { 2215ef0921d6SKyungmin Park int size = ARRAY_SIZE(onenand_manuf_ids); 221659829cc1SJean-Christophe PLAGNIOL-VILLARD int i; 221724ccca5eSMarek Vasut #ifdef ONENAND_DEBUG 221824ccca5eSMarek Vasut char *name; 221924ccca5eSMarek Vasut #endif 222059829cc1SJean-Christophe PLAGNIOL-VILLARD 2221cacbe919SAmul Kumar Saha for (i = 0; i < size; i++) 222259829cc1SJean-Christophe PLAGNIOL-VILLARD if (manuf == onenand_manuf_ids[i].id) 222359829cc1SJean-Christophe PLAGNIOL-VILLARD break; 2224ef0921d6SKyungmin Park 222524ccca5eSMarek Vasut #ifdef ONENAND_DEBUG 2226ef0921d6SKyungmin Park if (i < size) 2227ef0921d6SKyungmin Park name = onenand_manuf_ids[i].name; 2228ef0921d6SKyungmin Park else 2229ef0921d6SKyungmin Park name = "Unknown"; 223059829cc1SJean-Christophe PLAGNIOL-VILLARD 2231ef0921d6SKyungmin Park printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf); 223259829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 223359829cc1SJean-Christophe PLAGNIOL-VILLARD 2234ef0921d6SKyungmin Park return i == size; 223559829cc1SJean-Christophe PLAGNIOL-VILLARD } 223659829cc1SJean-Christophe PLAGNIOL-VILLARD 223759829cc1SJean-Christophe PLAGNIOL-VILLARD /** 2238cacbe919SAmul Kumar Saha * flexonenand_get_boundary - Reads the SLC boundary 2239cacbe919SAmul Kumar Saha * @param onenand_info - onenand info structure 2240cacbe919SAmul Kumar Saha * 2241cacbe919SAmul Kumar Saha * Fill up boundary[] field in onenand_chip 2242cacbe919SAmul Kumar Saha **/ 2243cacbe919SAmul Kumar Saha static int flexonenand_get_boundary(struct mtd_info *mtd) 2244cacbe919SAmul Kumar Saha { 2245cacbe919SAmul Kumar Saha struct onenand_chip *this = mtd->priv; 2246cacbe919SAmul Kumar Saha unsigned int die, bdry; 224724ccca5eSMarek Vasut int syscfg, locked; 2248cacbe919SAmul Kumar Saha 2249cacbe919SAmul Kumar Saha /* Disable ECC */ 2250cacbe919SAmul Kumar Saha syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); 2251cacbe919SAmul Kumar Saha this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); 2252cacbe919SAmul Kumar Saha 2253cacbe919SAmul Kumar Saha for (die = 0; die < this->dies; die++) { 2254cacbe919SAmul Kumar Saha this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); 2255cacbe919SAmul Kumar Saha this->wait(mtd, FL_SYNCING); 2256cacbe919SAmul Kumar Saha 2257cacbe919SAmul Kumar Saha this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); 225824ccca5eSMarek Vasut this->wait(mtd, FL_READING); 2259cacbe919SAmul Kumar Saha 2260cacbe919SAmul Kumar Saha bdry = this->read_word(this->base + ONENAND_DATARAM); 2261cacbe919SAmul Kumar Saha if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) 2262cacbe919SAmul Kumar Saha locked = 0; 2263cacbe919SAmul Kumar Saha else 2264cacbe919SAmul Kumar Saha locked = 1; 2265cacbe919SAmul Kumar Saha this->boundary[die] = bdry & FLEXONENAND_PI_MASK; 2266cacbe919SAmul Kumar Saha 2267cacbe919SAmul Kumar Saha this->command(mtd, ONENAND_CMD_RESET, 0, 0); 226824ccca5eSMarek Vasut this->wait(mtd, FL_RESETING); 2269cacbe919SAmul Kumar Saha 2270cacbe919SAmul Kumar Saha printk(KERN_INFO "Die %d boundary: %d%s\n", die, 2271cacbe919SAmul Kumar Saha this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); 2272cacbe919SAmul Kumar Saha } 2273cacbe919SAmul Kumar Saha 2274cacbe919SAmul Kumar Saha /* Enable ECC */ 2275cacbe919SAmul Kumar Saha this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); 2276cacbe919SAmul Kumar Saha return 0; 2277cacbe919SAmul Kumar Saha } 2278cacbe919SAmul Kumar Saha 2279cacbe919SAmul Kumar Saha /** 2280cacbe919SAmul Kumar Saha * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info 2281cacbe919SAmul Kumar Saha * boundary[], diesize[], mtd->size, mtd->erasesize, 2282cacbe919SAmul Kumar Saha * mtd->eraseregions 2283cacbe919SAmul Kumar Saha * @param mtd - MTD device structure 2284cacbe919SAmul Kumar Saha */ 2285cacbe919SAmul Kumar Saha static void flexonenand_get_size(struct mtd_info *mtd) 2286cacbe919SAmul Kumar Saha { 2287cacbe919SAmul Kumar Saha struct onenand_chip *this = mtd->priv; 2288cacbe919SAmul Kumar Saha int die, i, eraseshift, density; 2289cacbe919SAmul Kumar Saha int blksperdie, maxbdry; 2290cacbe919SAmul Kumar Saha loff_t ofs; 2291cacbe919SAmul Kumar Saha 2292cacbe919SAmul Kumar Saha density = onenand_get_density(this->device_id); 2293cacbe919SAmul Kumar Saha blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); 2294cacbe919SAmul Kumar Saha blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; 2295cacbe919SAmul Kumar Saha maxbdry = blksperdie - 1; 2296cacbe919SAmul Kumar Saha eraseshift = this->erase_shift - 1; 2297cacbe919SAmul Kumar Saha 2298cacbe919SAmul Kumar Saha mtd->numeraseregions = this->dies << 1; 2299cacbe919SAmul Kumar Saha 2300cacbe919SAmul Kumar Saha /* This fills up the device boundary */ 2301cacbe919SAmul Kumar Saha flexonenand_get_boundary(mtd); 2302cacbe919SAmul Kumar Saha die = 0; 2303cacbe919SAmul Kumar Saha ofs = 0; 2304cacbe919SAmul Kumar Saha i = -1; 2305cacbe919SAmul Kumar Saha for (; die < this->dies; die++) { 2306cacbe919SAmul Kumar Saha if (!die || this->boundary[die-1] != maxbdry) { 2307cacbe919SAmul Kumar Saha i++; 2308cacbe919SAmul Kumar Saha mtd->eraseregions[i].offset = ofs; 2309cacbe919SAmul Kumar Saha mtd->eraseregions[i].erasesize = 1 << eraseshift; 2310cacbe919SAmul Kumar Saha mtd->eraseregions[i].numblocks = 2311cacbe919SAmul Kumar Saha this->boundary[die] + 1; 2312cacbe919SAmul Kumar Saha ofs += mtd->eraseregions[i].numblocks << eraseshift; 2313cacbe919SAmul Kumar Saha eraseshift++; 2314cacbe919SAmul Kumar Saha } else { 2315cacbe919SAmul Kumar Saha mtd->numeraseregions -= 1; 2316cacbe919SAmul Kumar Saha mtd->eraseregions[i].numblocks += 2317cacbe919SAmul Kumar Saha this->boundary[die] + 1; 2318cacbe919SAmul Kumar Saha ofs += (this->boundary[die] + 1) << (eraseshift - 1); 2319cacbe919SAmul Kumar Saha } 2320cacbe919SAmul Kumar Saha if (this->boundary[die] != maxbdry) { 2321cacbe919SAmul Kumar Saha i++; 2322cacbe919SAmul Kumar Saha mtd->eraseregions[i].offset = ofs; 2323cacbe919SAmul Kumar Saha mtd->eraseregions[i].erasesize = 1 << eraseshift; 2324cacbe919SAmul Kumar Saha mtd->eraseregions[i].numblocks = maxbdry ^ 2325cacbe919SAmul Kumar Saha this->boundary[die]; 2326cacbe919SAmul Kumar Saha ofs += mtd->eraseregions[i].numblocks << eraseshift; 2327cacbe919SAmul Kumar Saha eraseshift--; 2328cacbe919SAmul Kumar Saha } else 2329cacbe919SAmul Kumar Saha mtd->numeraseregions -= 1; 2330cacbe919SAmul Kumar Saha } 2331cacbe919SAmul Kumar Saha 2332cacbe919SAmul Kumar Saha /* Expose MLC erase size except when all blocks are SLC */ 2333cacbe919SAmul Kumar Saha mtd->erasesize = 1 << this->erase_shift; 2334cacbe919SAmul Kumar Saha if (mtd->numeraseregions == 1) 2335cacbe919SAmul Kumar Saha mtd->erasesize >>= 1; 2336cacbe919SAmul Kumar Saha 2337cacbe919SAmul Kumar Saha printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); 2338cacbe919SAmul Kumar Saha for (i = 0; i < mtd->numeraseregions; i++) 2339cacbe919SAmul Kumar Saha printk(KERN_INFO "[offset: 0x%08llx, erasesize: 0x%05x," 2340cacbe919SAmul Kumar Saha " numblocks: %04u]\n", mtd->eraseregions[i].offset, 2341cacbe919SAmul Kumar Saha mtd->eraseregions[i].erasesize, 2342cacbe919SAmul Kumar Saha mtd->eraseregions[i].numblocks); 2343cacbe919SAmul Kumar Saha 2344cacbe919SAmul Kumar Saha for (die = 0, mtd->size = 0; die < this->dies; die++) { 2345cacbe919SAmul Kumar Saha this->diesize[die] = (loff_t) (blksperdie << this->erase_shift); 2346cacbe919SAmul Kumar Saha this->diesize[die] -= (loff_t) (this->boundary[die] + 1) 2347cacbe919SAmul Kumar Saha << (this->erase_shift - 1); 2348cacbe919SAmul Kumar Saha mtd->size += this->diesize[die]; 2349cacbe919SAmul Kumar Saha } 2350cacbe919SAmul Kumar Saha } 2351cacbe919SAmul Kumar Saha 2352cacbe919SAmul Kumar Saha /** 2353cacbe919SAmul Kumar Saha * flexonenand_check_blocks_erased - Check if blocks are erased 2354cacbe919SAmul Kumar Saha * @param mtd_info - mtd info structure 2355cacbe919SAmul Kumar Saha * @param start - first erase block to check 2356cacbe919SAmul Kumar Saha * @param end - last erase block to check 2357cacbe919SAmul Kumar Saha * 2358cacbe919SAmul Kumar Saha * Converting an unerased block from MLC to SLC 2359cacbe919SAmul Kumar Saha * causes byte values to change. Since both data and its ECC 2360cacbe919SAmul Kumar Saha * have changed, reads on the block give uncorrectable error. 2361cacbe919SAmul Kumar Saha * This might lead to the block being detected as bad. 2362cacbe919SAmul Kumar Saha * 2363cacbe919SAmul Kumar Saha * Avoid this by ensuring that the block to be converted is 2364cacbe919SAmul Kumar Saha * erased. 2365cacbe919SAmul Kumar Saha */ 2366cacbe919SAmul Kumar Saha static int flexonenand_check_blocks_erased(struct mtd_info *mtd, 2367cacbe919SAmul Kumar Saha int start, int end) 2368cacbe919SAmul Kumar Saha { 2369cacbe919SAmul Kumar Saha struct onenand_chip *this = mtd->priv; 2370cacbe919SAmul Kumar Saha int i, ret; 2371cacbe919SAmul Kumar Saha int block; 2372cacbe919SAmul Kumar Saha struct mtd_oob_ops ops = { 2373*dfe64e2cSSergey Lapin .mode = MTD_OPS_PLACE_OOB, 2374cacbe919SAmul Kumar Saha .ooboffs = 0, 2375cacbe919SAmul Kumar Saha .ooblen = mtd->oobsize, 2376cacbe919SAmul Kumar Saha .datbuf = NULL, 2377cacbe919SAmul Kumar Saha .oobbuf = this->oob_buf, 2378cacbe919SAmul Kumar Saha }; 2379cacbe919SAmul Kumar Saha loff_t addr; 2380cacbe919SAmul Kumar Saha 2381cacbe919SAmul Kumar Saha printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); 2382cacbe919SAmul Kumar Saha 2383cacbe919SAmul Kumar Saha for (block = start; block <= end; block++) { 2384cacbe919SAmul Kumar Saha addr = flexonenand_addr(this, block); 2385cacbe919SAmul Kumar Saha if (onenand_block_isbad_nolock(mtd, addr, 0)) 2386cacbe919SAmul Kumar Saha continue; 2387cacbe919SAmul Kumar Saha 2388cacbe919SAmul Kumar Saha /* 2389cacbe919SAmul Kumar Saha * Since main area write results in ECC write to spare, 2390cacbe919SAmul Kumar Saha * it is sufficient to check only ECC bytes for change. 2391cacbe919SAmul Kumar Saha */ 2392cacbe919SAmul Kumar Saha ret = onenand_read_oob_nolock(mtd, addr, &ops); 2393cacbe919SAmul Kumar Saha if (ret) 2394cacbe919SAmul Kumar Saha return ret; 2395cacbe919SAmul Kumar Saha 2396cacbe919SAmul Kumar Saha for (i = 0; i < mtd->oobsize; i++) 2397cacbe919SAmul Kumar Saha if (this->oob_buf[i] != 0xff) 2398cacbe919SAmul Kumar Saha break; 2399cacbe919SAmul Kumar Saha 2400cacbe919SAmul Kumar Saha if (i != mtd->oobsize) { 2401cacbe919SAmul Kumar Saha printk(KERN_WARNING "Block %d not erased.\n", block); 2402cacbe919SAmul Kumar Saha return 1; 2403cacbe919SAmul Kumar Saha } 2404cacbe919SAmul Kumar Saha } 2405cacbe919SAmul Kumar Saha 2406cacbe919SAmul Kumar Saha return 0; 2407cacbe919SAmul Kumar Saha } 2408cacbe919SAmul Kumar Saha 2409cacbe919SAmul Kumar Saha /** 2410cacbe919SAmul Kumar Saha * flexonenand_set_boundary - Writes the SLC boundary 2411cacbe919SAmul Kumar Saha * @param mtd - mtd info structure 2412cacbe919SAmul Kumar Saha */ 2413cacbe919SAmul Kumar Saha int flexonenand_set_boundary(struct mtd_info *mtd, int die, 2414cacbe919SAmul Kumar Saha int boundary, int lock) 2415cacbe919SAmul Kumar Saha { 2416cacbe919SAmul Kumar Saha struct onenand_chip *this = mtd->priv; 2417cacbe919SAmul Kumar Saha int ret, density, blksperdie, old, new, thisboundary; 2418cacbe919SAmul Kumar Saha loff_t addr; 2419cacbe919SAmul Kumar Saha 2420cacbe919SAmul Kumar Saha if (die >= this->dies) 2421cacbe919SAmul Kumar Saha return -EINVAL; 2422cacbe919SAmul Kumar Saha 2423cacbe919SAmul Kumar Saha if (boundary == this->boundary[die]) 2424cacbe919SAmul Kumar Saha return 0; 2425cacbe919SAmul Kumar Saha 2426cacbe919SAmul Kumar Saha density = onenand_get_density(this->device_id); 2427cacbe919SAmul Kumar Saha blksperdie = ((16 << density) << 20) >> this->erase_shift; 2428cacbe919SAmul Kumar Saha blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; 2429cacbe919SAmul Kumar Saha 2430cacbe919SAmul Kumar Saha if (boundary >= blksperdie) { 2431cacbe919SAmul Kumar Saha printk("flexonenand_set_boundary:" 2432cacbe919SAmul Kumar Saha "Invalid boundary value. " 2433cacbe919SAmul Kumar Saha "Boundary not changed.\n"); 2434cacbe919SAmul Kumar Saha return -EINVAL; 2435cacbe919SAmul Kumar Saha } 2436cacbe919SAmul Kumar Saha 2437cacbe919SAmul Kumar Saha /* Check if converting blocks are erased */ 2438cacbe919SAmul Kumar Saha old = this->boundary[die] + (die * this->density_mask); 2439cacbe919SAmul Kumar Saha new = boundary + (die * this->density_mask); 2440cacbe919SAmul Kumar Saha ret = flexonenand_check_blocks_erased(mtd, min(old, new) 2441cacbe919SAmul Kumar Saha + 1, max(old, new)); 2442cacbe919SAmul Kumar Saha if (ret) { 2443cacbe919SAmul Kumar Saha printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); 2444cacbe919SAmul Kumar Saha return ret; 2445cacbe919SAmul Kumar Saha } 2446cacbe919SAmul Kumar Saha 2447cacbe919SAmul Kumar Saha this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); 2448cacbe919SAmul Kumar Saha this->wait(mtd, FL_SYNCING); 2449cacbe919SAmul Kumar Saha 2450cacbe919SAmul Kumar Saha /* Check is boundary is locked */ 2451cacbe919SAmul Kumar Saha this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); 2452cacbe919SAmul Kumar Saha ret = this->wait(mtd, FL_READING); 2453cacbe919SAmul Kumar Saha 2454cacbe919SAmul Kumar Saha thisboundary = this->read_word(this->base + ONENAND_DATARAM); 2455cacbe919SAmul Kumar Saha if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { 2456cacbe919SAmul Kumar Saha printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); 2457cacbe919SAmul Kumar Saha goto out; 2458cacbe919SAmul Kumar Saha } 2459cacbe919SAmul Kumar Saha 2460cacbe919SAmul Kumar Saha printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", 2461cacbe919SAmul Kumar Saha die, boundary, lock ? "(Locked)" : "(Unlocked)"); 2462cacbe919SAmul Kumar Saha 2463cacbe919SAmul Kumar Saha boundary &= FLEXONENAND_PI_MASK; 2464cacbe919SAmul Kumar Saha boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); 2465cacbe919SAmul Kumar Saha 2466cacbe919SAmul Kumar Saha addr = die ? this->diesize[0] : 0; 2467cacbe919SAmul Kumar Saha this->command(mtd, ONENAND_CMD_ERASE, addr, 0); 2468cacbe919SAmul Kumar Saha ret = this->wait(mtd, FL_ERASING); 2469cacbe919SAmul Kumar Saha if (ret) { 2470cacbe919SAmul Kumar Saha printk("flexonenand_set_boundary:" 2471cacbe919SAmul Kumar Saha "Failed PI erase for Die %d\n", die); 2472cacbe919SAmul Kumar Saha goto out; 2473cacbe919SAmul Kumar Saha } 2474cacbe919SAmul Kumar Saha 2475cacbe919SAmul Kumar Saha this->write_word(boundary, this->base + ONENAND_DATARAM); 2476cacbe919SAmul Kumar Saha this->command(mtd, ONENAND_CMD_PROG, addr, 0); 2477cacbe919SAmul Kumar Saha ret = this->wait(mtd, FL_WRITING); 2478cacbe919SAmul Kumar Saha if (ret) { 2479cacbe919SAmul Kumar Saha printk("flexonenand_set_boundary:" 2480cacbe919SAmul Kumar Saha "Failed PI write for Die %d\n", die); 2481cacbe919SAmul Kumar Saha goto out; 2482cacbe919SAmul Kumar Saha } 2483cacbe919SAmul Kumar Saha 2484cacbe919SAmul Kumar Saha this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); 2485cacbe919SAmul Kumar Saha ret = this->wait(mtd, FL_WRITING); 2486cacbe919SAmul Kumar Saha out: 2487cacbe919SAmul Kumar Saha this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); 2488cacbe919SAmul Kumar Saha this->wait(mtd, FL_RESETING); 2489cacbe919SAmul Kumar Saha if (!ret) 2490cacbe919SAmul Kumar Saha /* Recalculate device size on boundary change*/ 2491cacbe919SAmul Kumar Saha flexonenand_get_size(mtd); 2492cacbe919SAmul Kumar Saha 2493cacbe919SAmul Kumar Saha return ret; 2494cacbe919SAmul Kumar Saha } 2495cacbe919SAmul Kumar Saha 2496cacbe919SAmul Kumar Saha /** 24976b3967bbSLukasz Majewski * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND chip 249859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 249959829cc1SJean-Christophe PLAGNIOL-VILLARD * 250059829cc1SJean-Christophe PLAGNIOL-VILLARD * OneNAND detection method: 250159829cc1SJean-Christophe PLAGNIOL-VILLARD * Compare the the values from command with ones from register 250259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 25036b3967bbSLukasz Majewski static int onenand_chip_probe(struct mtd_info *mtd) 250459829cc1SJean-Christophe PLAGNIOL-VILLARD { 250559829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 25066b3967bbSLukasz Majewski int bram_maf_id, bram_dev_id, maf_id, dev_id; 2507ef0921d6SKyungmin Park int syscfg; 2508ef0921d6SKyungmin Park 2509ef0921d6SKyungmin Park /* Save system configuration 1 */ 2510ef0921d6SKyungmin Park syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); 25116b3967bbSLukasz Majewski 2512ef0921d6SKyungmin Park /* Clear Sync. Burst Read mode to read BootRAM */ 25136b3967bbSLukasz Majewski this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), 25146b3967bbSLukasz Majewski this->base + ONENAND_REG_SYS_CFG1); 251559829cc1SJean-Christophe PLAGNIOL-VILLARD 251659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Send the command for reading device ID from BootRAM */ 251759829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); 251859829cc1SJean-Christophe PLAGNIOL-VILLARD 251959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Read manufacturer and device IDs from BootRAM */ 252059829cc1SJean-Christophe PLAGNIOL-VILLARD bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); 252159829cc1SJean-Christophe PLAGNIOL-VILLARD bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); 252259829cc1SJean-Christophe PLAGNIOL-VILLARD 252359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Reset OneNAND to read default register values */ 252459829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); 252559829cc1SJean-Christophe PLAGNIOL-VILLARD 2526d438d508SKyungmin Park /* Wait reset */ 2527d438d508SKyungmin Park this->wait(mtd, FL_RESETING); 252859829cc1SJean-Christophe PLAGNIOL-VILLARD 2529ef0921d6SKyungmin Park /* Restore system configuration 1 */ 2530ef0921d6SKyungmin Park this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); 2531ef0921d6SKyungmin Park 2532ef0921d6SKyungmin Park /* Check manufacturer ID */ 2533ef0921d6SKyungmin Park if (onenand_check_maf(bram_maf_id)) 2534ef0921d6SKyungmin Park return -ENXIO; 2535ef0921d6SKyungmin Park 253659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Read manufacturer and device IDs from Register */ 253759829cc1SJean-Christophe PLAGNIOL-VILLARD maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); 253859829cc1SJean-Christophe PLAGNIOL-VILLARD dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); 253959829cc1SJean-Christophe PLAGNIOL-VILLARD 254059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check OneNAND device */ 254159829cc1SJean-Christophe PLAGNIOL-VILLARD if (maf_id != bram_maf_id || dev_id != bram_dev_id) 254259829cc1SJean-Christophe PLAGNIOL-VILLARD return -ENXIO; 254359829cc1SJean-Christophe PLAGNIOL-VILLARD 25446b3967bbSLukasz Majewski return 0; 25456b3967bbSLukasz Majewski } 25466b3967bbSLukasz Majewski 25476b3967bbSLukasz Majewski /** 25486b3967bbSLukasz Majewski * onenand_probe - [OneNAND Interface] Probe the OneNAND device 25496b3967bbSLukasz Majewski * @param mtd MTD device structure 25506b3967bbSLukasz Majewski * 25516b3967bbSLukasz Majewski * OneNAND detection method: 25526b3967bbSLukasz Majewski * Compare the the values from command with ones from register 25536b3967bbSLukasz Majewski */ 25546b3967bbSLukasz Majewski int onenand_probe(struct mtd_info *mtd) 25556b3967bbSLukasz Majewski { 25566b3967bbSLukasz Majewski struct onenand_chip *this = mtd->priv; 25571432c763SWolfgang Denk int dev_id, ver_id; 25586b3967bbSLukasz Majewski int density; 25596b3967bbSLukasz Majewski int ret; 25606b3967bbSLukasz Majewski 25616b3967bbSLukasz Majewski ret = this->chip_probe(mtd); 25626b3967bbSLukasz Majewski if (ret) 25636b3967bbSLukasz Majewski return ret; 25646b3967bbSLukasz Majewski 25651432c763SWolfgang Denk /* Read device IDs from Register */ 25666b3967bbSLukasz Majewski dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); 25676b3967bbSLukasz Majewski ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); 25686b3967bbSLukasz Majewski this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); 25696b3967bbSLukasz Majewski 257059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Flash device information */ 2571ef0921d6SKyungmin Park mtd->name = onenand_print_device_info(dev_id, ver_id); 257259829cc1SJean-Christophe PLAGNIOL-VILLARD this->device_id = dev_id; 25738cf11f3aSStefan Roese this->version_id = ver_id; 257459829cc1SJean-Christophe PLAGNIOL-VILLARD 2575e26fd3d3SLukasz Majewski /* Check OneNAND features */ 2576e26fd3d3SLukasz Majewski onenand_check_features(mtd); 2577e26fd3d3SLukasz Majewski 2578ef0921d6SKyungmin Park density = onenand_get_density(dev_id); 2579cacbe919SAmul Kumar Saha if (FLEXONENAND(this)) { 2580cacbe919SAmul Kumar Saha this->dies = ONENAND_IS_DDP(this) ? 2 : 1; 2581cacbe919SAmul Kumar Saha /* Maximum possible erase regions */ 2582cacbe919SAmul Kumar Saha mtd->numeraseregions = this->dies << 1; 2583cacbe919SAmul Kumar Saha mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) 2584cacbe919SAmul Kumar Saha * (this->dies << 1)); 2585cacbe919SAmul Kumar Saha if (!mtd->eraseregions) 2586cacbe919SAmul Kumar Saha return -ENOMEM; 2587cacbe919SAmul Kumar Saha } 2588cacbe919SAmul Kumar Saha 2589cacbe919SAmul Kumar Saha /* 2590cacbe919SAmul Kumar Saha * For Flex-OneNAND, chipsize represents maximum possible device size. 2591cacbe919SAmul Kumar Saha * mtd->size represents the actual device size. 2592cacbe919SAmul Kumar Saha */ 259359829cc1SJean-Christophe PLAGNIOL-VILLARD this->chipsize = (16 << density) << 20; 259459829cc1SJean-Christophe PLAGNIOL-VILLARD 259559829cc1SJean-Christophe PLAGNIOL-VILLARD /* OneNAND page size & block size */ 259659829cc1SJean-Christophe PLAGNIOL-VILLARD /* The data buffer size is equal to page size */ 2597d438d508SKyungmin Park mtd->writesize = 259859829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); 2599cacbe919SAmul Kumar Saha /* We use the full BufferRAM */ 2600e26fd3d3SLukasz Majewski if (ONENAND_IS_4KB_PAGE(this)) 2601cacbe919SAmul Kumar Saha mtd->writesize <<= 1; 2602cacbe919SAmul Kumar Saha 2603d438d508SKyungmin Park mtd->oobsize = mtd->writesize >> 5; 260459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Pagers per block is always 64 in OneNAND */ 2605d438d508SKyungmin Park mtd->erasesize = mtd->writesize << 6; 2606cacbe919SAmul Kumar Saha /* 2607cacbe919SAmul Kumar Saha * Flex-OneNAND SLC area has 64 pages per block. 2608cacbe919SAmul Kumar Saha * Flex-OneNAND MLC area has 128 pages per block. 2609cacbe919SAmul Kumar Saha * Expose MLC erase size to find erase_shift and page_mask. 2610cacbe919SAmul Kumar Saha */ 2611cacbe919SAmul Kumar Saha if (FLEXONENAND(this)) 2612cacbe919SAmul Kumar Saha mtd->erasesize <<= 1; 261359829cc1SJean-Christophe PLAGNIOL-VILLARD 261459829cc1SJean-Christophe PLAGNIOL-VILLARD this->erase_shift = ffs(mtd->erasesize) - 1; 2615d438d508SKyungmin Park this->page_shift = ffs(mtd->writesize) - 1; 261659829cc1SJean-Christophe PLAGNIOL-VILLARD this->ppb_shift = (this->erase_shift - this->page_shift); 2617d438d508SKyungmin Park this->page_mask = (mtd->erasesize / mtd->writesize) - 1; 2618cacbe919SAmul Kumar Saha /* Set density mask. it is used for DDP */ 2619cacbe919SAmul Kumar Saha if (ONENAND_IS_DDP(this)) 2620cacbe919SAmul Kumar Saha this->density_mask = this->chipsize >> (this->erase_shift + 1); 2621bfd7f386SKyungmin Park /* It's real page size */ 2622bfd7f386SKyungmin Park this->writesize = mtd->writesize; 262359829cc1SJean-Christophe PLAGNIOL-VILLARD 262459829cc1SJean-Christophe PLAGNIOL-VILLARD /* REVIST: Multichip handling */ 262559829cc1SJean-Christophe PLAGNIOL-VILLARD 2626cacbe919SAmul Kumar Saha if (FLEXONENAND(this)) 2627cacbe919SAmul Kumar Saha flexonenand_get_size(mtd); 2628cacbe919SAmul Kumar Saha else 262959829cc1SJean-Christophe PLAGNIOL-VILLARD mtd->size = this->chipsize; 263059829cc1SJean-Christophe PLAGNIOL-VILLARD 2631d438d508SKyungmin Park mtd->flags = MTD_CAP_NANDFLASH; 2632*dfe64e2cSSergey Lapin mtd->_erase = onenand_erase; 2633*dfe64e2cSSergey Lapin mtd->_read = onenand_read; 2634*dfe64e2cSSergey Lapin mtd->_write = onenand_write; 2635*dfe64e2cSSergey Lapin mtd->_read_oob = onenand_read_oob; 2636*dfe64e2cSSergey Lapin mtd->_write_oob = onenand_write_oob; 2637*dfe64e2cSSergey Lapin mtd->_sync = onenand_sync; 2638*dfe64e2cSSergey Lapin mtd->_block_isbad = onenand_block_isbad; 2639*dfe64e2cSSergey Lapin mtd->_block_markbad = onenand_block_markbad; 2640195ccfc5SFathi BOUDRA 264159829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 264259829cc1SJean-Christophe PLAGNIOL-VILLARD } 264359829cc1SJean-Christophe PLAGNIOL-VILLARD 264459829cc1SJean-Christophe PLAGNIOL-VILLARD /** 264559829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_scan - [OneNAND Interface] Scan for the OneNAND device 264659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 264759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param maxchips Number of chips to scan for 264859829cc1SJean-Christophe PLAGNIOL-VILLARD * 264959829cc1SJean-Christophe PLAGNIOL-VILLARD * This fills out all the not initialized function pointers 265059829cc1SJean-Christophe PLAGNIOL-VILLARD * with the defaults. 265159829cc1SJean-Christophe PLAGNIOL-VILLARD * The flash ID is read and the mtd/chip structures are 265259829cc1SJean-Christophe PLAGNIOL-VILLARD * filled with the appropriate values. 265359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 265459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_scan(struct mtd_info *mtd, int maxchips) 265559829cc1SJean-Christophe PLAGNIOL-VILLARD { 26561ae39862SStefan Roese int i; 265759829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 265859829cc1SJean-Christophe PLAGNIOL-VILLARD 265959829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->read_word) 266059829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_word = onenand_readw; 266159829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->write_word) 266259829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word = onenand_writew; 266359829cc1SJean-Christophe PLAGNIOL-VILLARD 266459829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->command) 266559829cc1SJean-Christophe PLAGNIOL-VILLARD this->command = onenand_command; 266659829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->wait) 266759829cc1SJean-Christophe PLAGNIOL-VILLARD this->wait = onenand_wait; 2668ef0921d6SKyungmin Park if (!this->bbt_wait) 2669ef0921d6SKyungmin Park this->bbt_wait = onenand_bbt_wait; 267059829cc1SJean-Christophe PLAGNIOL-VILLARD 267159829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->read_bufferram) 267259829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_bufferram = onenand_read_bufferram; 267359829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->write_bufferram) 267459829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_bufferram = onenand_write_bufferram; 267559829cc1SJean-Christophe PLAGNIOL-VILLARD 26766b3967bbSLukasz Majewski if (!this->chip_probe) 26776b3967bbSLukasz Majewski this->chip_probe = onenand_chip_probe; 26786b3967bbSLukasz Majewski 26791714f51aSKyungmin Park if (!this->block_markbad) 26801714f51aSKyungmin Park this->block_markbad = onenand_default_block_markbad; 2681ef0921d6SKyungmin Park if (!this->scan_bbt) 2682ef0921d6SKyungmin Park this->scan_bbt = onenand_default_bbt; 2683ef0921d6SKyungmin Park 268459829cc1SJean-Christophe PLAGNIOL-VILLARD if (onenand_probe(mtd)) 268559829cc1SJean-Christophe PLAGNIOL-VILLARD return -ENXIO; 268659829cc1SJean-Christophe PLAGNIOL-VILLARD 268759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set Sync. Burst Read after probing */ 268859829cc1SJean-Christophe PLAGNIOL-VILLARD if (this->mmcontrol) { 268959829cc1SJean-Christophe PLAGNIOL-VILLARD printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); 269059829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_bufferram = onenand_sync_read_bufferram; 269159829cc1SJean-Christophe PLAGNIOL-VILLARD } 269259829cc1SJean-Christophe PLAGNIOL-VILLARD 2693bfd7f386SKyungmin Park /* Allocate buffers, if necessary */ 2694bfd7f386SKyungmin Park if (!this->page_buf) { 2695bfd7f386SKyungmin Park this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); 2696bfd7f386SKyungmin Park if (!this->page_buf) { 2697bfd7f386SKyungmin Park printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); 2698bfd7f386SKyungmin Park return -ENOMEM; 2699bfd7f386SKyungmin Park } 2700bfd7f386SKyungmin Park this->options |= ONENAND_PAGEBUF_ALLOC; 2701bfd7f386SKyungmin Park } 2702bfd7f386SKyungmin Park if (!this->oob_buf) { 2703bfd7f386SKyungmin Park this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); 2704bfd7f386SKyungmin Park if (!this->oob_buf) { 2705bfd7f386SKyungmin Park printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n"); 2706bfd7f386SKyungmin Park if (this->options & ONENAND_PAGEBUF_ALLOC) { 2707bfd7f386SKyungmin Park this->options &= ~ONENAND_PAGEBUF_ALLOC; 2708bfd7f386SKyungmin Park kfree(this->page_buf); 2709bfd7f386SKyungmin Park } 2710bfd7f386SKyungmin Park return -ENOMEM; 2711bfd7f386SKyungmin Park } 2712bfd7f386SKyungmin Park this->options |= ONENAND_OOBBUF_ALLOC; 2713bfd7f386SKyungmin Park } 2714bfd7f386SKyungmin Park 27151ae39862SStefan Roese this->state = FL_READY; 27161ae39862SStefan Roese 27171ae39862SStefan Roese /* 27181ae39862SStefan Roese * Allow subpage writes up to oobsize. 27191ae39862SStefan Roese */ 27201ae39862SStefan Roese switch (mtd->oobsize) { 2721cacbe919SAmul Kumar Saha case 128: 2722cacbe919SAmul Kumar Saha this->ecclayout = &onenand_oob_128; 2723cacbe919SAmul Kumar Saha mtd->subpage_sft = 0; 2724cacbe919SAmul Kumar Saha break; 2725cacbe919SAmul Kumar Saha 27261ae39862SStefan Roese case 64: 27271ae39862SStefan Roese this->ecclayout = &onenand_oob_64; 27281ae39862SStefan Roese mtd->subpage_sft = 2; 27291ae39862SStefan Roese break; 27301ae39862SStefan Roese 27311ae39862SStefan Roese case 32: 27321ae39862SStefan Roese this->ecclayout = &onenand_oob_32; 27331ae39862SStefan Roese mtd->subpage_sft = 1; 27341ae39862SStefan Roese break; 27351ae39862SStefan Roese 27361ae39862SStefan Roese default: 27371ae39862SStefan Roese printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", 27381ae39862SStefan Roese mtd->oobsize); 27391ae39862SStefan Roese mtd->subpage_sft = 0; 27401ae39862SStefan Roese /* To prevent kernel oops */ 27411ae39862SStefan Roese this->ecclayout = &onenand_oob_32; 27421ae39862SStefan Roese break; 27431ae39862SStefan Roese } 27441ae39862SStefan Roese 27451ae39862SStefan Roese this->subpagesize = mtd->writesize >> mtd->subpage_sft; 27461ae39862SStefan Roese 27471ae39862SStefan Roese /* 27481ae39862SStefan Roese * The number of bytes available for a client to place data into 27491ae39862SStefan Roese * the out of band area 27501ae39862SStefan Roese */ 27511ae39862SStefan Roese this->ecclayout->oobavail = 0; 27521ae39862SStefan Roese for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && 27531ae39862SStefan Roese this->ecclayout->oobfree[i].length; i++) 27541ae39862SStefan Roese this->ecclayout->oobavail += 27551ae39862SStefan Roese this->ecclayout->oobfree[i].length; 27561ae39862SStefan Roese mtd->oobavail = this->ecclayout->oobavail; 27571ae39862SStefan Roese 27581ae39862SStefan Roese mtd->ecclayout = this->ecclayout; 27591ae39862SStefan Roese 2760ef0921d6SKyungmin Park /* Unlock whole block */ 2761ef0921d6SKyungmin Park onenand_unlock_all(mtd); 276259829cc1SJean-Christophe PLAGNIOL-VILLARD 2763ef0921d6SKyungmin Park return this->scan_bbt(mtd); 276459829cc1SJean-Christophe PLAGNIOL-VILLARD } 276559829cc1SJean-Christophe PLAGNIOL-VILLARD 276659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 276759829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device 276859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 276959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 277059829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_release(struct mtd_info *mtd) 277159829cc1SJean-Christophe PLAGNIOL-VILLARD { 277259829cc1SJean-Christophe PLAGNIOL-VILLARD } 2773