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