159829cc1SJean-Christophe PLAGNIOL-VILLARD /* 259829cc1SJean-Christophe PLAGNIOL-VILLARD * linux/drivers/mtd/onenand/onenand_base.c 359829cc1SJean-Christophe PLAGNIOL-VILLARD * 459829cc1SJean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2005-2007 Samsung Electronics 559829cc1SJean-Christophe PLAGNIOL-VILLARD * Kyungmin Park <kyungmin.park@samsung.com> 659829cc1SJean-Christophe PLAGNIOL-VILLARD * 7bfd7f386SKyungmin Park * Credits: 8bfd7f386SKyungmin Park * Adrian Hunter <ext-adrian.hunter@nokia.com>: 9bfd7f386SKyungmin Park * auto-placement support, read-while load support, various fixes 10bfd7f386SKyungmin Park * Copyright (C) Nokia Corporation, 2007 11bfd7f386SKyungmin Park * 1259829cc1SJean-Christophe PLAGNIOL-VILLARD * This program is free software; you can redistribute it and/or modify 1359829cc1SJean-Christophe PLAGNIOL-VILLARD * it under the terms of the GNU General Public License version 2 as 1459829cc1SJean-Christophe PLAGNIOL-VILLARD * published by the Free Software Foundation. 1559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1659829cc1SJean-Christophe PLAGNIOL-VILLARD 1759829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h> 1859829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/compat.h> 1959829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/mtd.h> 2059829cc1SJean-Christophe PLAGNIOL-VILLARD #include <linux/mtd/onenand.h> 2159829cc1SJean-Christophe PLAGNIOL-VILLARD 2259829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/io.h> 2359829cc1SJean-Christophe PLAGNIOL-VILLARD #include <asm/errno.h> 24195ccfc5SFathi BOUDRA #include <malloc.h> 2559829cc1SJean-Christophe PLAGNIOL-VILLARD 2677e475ccSKyungmin Park /* It should access 16-bit instead of 8-bit */ 27d2c6fbecSWolfgang Denk static inline void *memcpy_16(void *dst, const void *src, unsigned int len) 2877e475ccSKyungmin Park { 2977e475ccSKyungmin Park void *ret = dst; 3077e475ccSKyungmin Park short *d = dst; 3177e475ccSKyungmin Park const short *s = src; 3277e475ccSKyungmin Park 3377e475ccSKyungmin Park len >>= 1; 3477e475ccSKyungmin Park while (len-- > 0) 3577e475ccSKyungmin Park *d++ = *s++; 3677e475ccSKyungmin Park return ret; 3777e475ccSKyungmin Park } 3877e475ccSKyungmin Park 3959829cc1SJean-Christophe PLAGNIOL-VILLARD static const unsigned char ffchars[] = { 4059829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 4159829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ 4259829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 4359829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ 4459829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 4559829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 4659829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 4759829cc1SJean-Christophe PLAGNIOL-VILLARD 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ 4859829cc1SJean-Christophe PLAGNIOL-VILLARD }; 4959829cc1SJean-Christophe PLAGNIOL-VILLARD 5059829cc1SJean-Christophe PLAGNIOL-VILLARD /** 5159829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_readw - [OneNAND Interface] Read OneNAND register 5259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to read 5359829cc1SJean-Christophe PLAGNIOL-VILLARD * 5459829cc1SJean-Christophe PLAGNIOL-VILLARD * Read OneNAND register 5559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 5659829cc1SJean-Christophe PLAGNIOL-VILLARD static unsigned short onenand_readw(void __iomem * addr) 5759829cc1SJean-Christophe PLAGNIOL-VILLARD { 5859829cc1SJean-Christophe PLAGNIOL-VILLARD return readw(addr); 5959829cc1SJean-Christophe PLAGNIOL-VILLARD } 6059829cc1SJean-Christophe PLAGNIOL-VILLARD 6159829cc1SJean-Christophe PLAGNIOL-VILLARD /** 6259829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_writew - [OneNAND Interface] Write OneNAND register with value 6359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param value value to write 6459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to write 6559829cc1SJean-Christophe PLAGNIOL-VILLARD * 6659829cc1SJean-Christophe PLAGNIOL-VILLARD * Write OneNAND register with value 6759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 6859829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_writew(unsigned short value, void __iomem * addr) 6959829cc1SJean-Christophe PLAGNIOL-VILLARD { 7059829cc1SJean-Christophe PLAGNIOL-VILLARD writew(value, addr); 7159829cc1SJean-Christophe PLAGNIOL-VILLARD } 7259829cc1SJean-Christophe PLAGNIOL-VILLARD 7359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 7459829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_block_address - [DEFAULT] Get block address 7559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param device the device id 7659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param block the block 7759829cc1SJean-Christophe PLAGNIOL-VILLARD * @return translated block address if DDP, otherwise same 7859829cc1SJean-Christophe PLAGNIOL-VILLARD * 7959829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Address 1 Register (F100h) 8059829cc1SJean-Christophe PLAGNIOL-VILLARD */ 81ef0921d6SKyungmin Park static int onenand_block_address(struct onenand_chip *this, int block) 8259829cc1SJean-Christophe PLAGNIOL-VILLARD { 8359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Device Flash Core select, NAND Flash Block Address */ 84ef0921d6SKyungmin Park if (block & this->density_mask) 85ef0921d6SKyungmin Park return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); 8659829cc1SJean-Christophe PLAGNIOL-VILLARD 8759829cc1SJean-Christophe PLAGNIOL-VILLARD return block; 8859829cc1SJean-Christophe PLAGNIOL-VILLARD } 8959829cc1SJean-Christophe PLAGNIOL-VILLARD 9059829cc1SJean-Christophe PLAGNIOL-VILLARD /** 9159829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_bufferram_address - [DEFAULT] Get bufferram address 9259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param device the device id 9359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param block the block 9459829cc1SJean-Christophe PLAGNIOL-VILLARD * @return set DBS value if DDP, otherwise 0 9559829cc1SJean-Christophe PLAGNIOL-VILLARD * 9659829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Address 2 Register (F101h) for DDP 9759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 98ef0921d6SKyungmin Park static int onenand_bufferram_address(struct onenand_chip *this, int block) 9959829cc1SJean-Christophe PLAGNIOL-VILLARD { 10059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Device BufferRAM Select */ 101ef0921d6SKyungmin Park if (block & this->density_mask) 102ef0921d6SKyungmin Park return ONENAND_DDP_CHIP1; 10359829cc1SJean-Christophe PLAGNIOL-VILLARD 104ef0921d6SKyungmin Park return ONENAND_DDP_CHIP0; 10559829cc1SJean-Christophe PLAGNIOL-VILLARD } 10659829cc1SJean-Christophe PLAGNIOL-VILLARD 10759829cc1SJean-Christophe PLAGNIOL-VILLARD /** 10859829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_page_address - [DEFAULT] Get page address 10959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param page the page address 11059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param sector the sector address 11159829cc1SJean-Christophe PLAGNIOL-VILLARD * @return combined page and sector address 11259829cc1SJean-Christophe PLAGNIOL-VILLARD * 11359829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Address 8 Register (F107h) 11459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 11559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_page_address(int page, int sector) 11659829cc1SJean-Christophe PLAGNIOL-VILLARD { 11759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Flash Page Address, Flash Sector Address */ 11859829cc1SJean-Christophe PLAGNIOL-VILLARD int fpa, fsa; 11959829cc1SJean-Christophe PLAGNIOL-VILLARD 12059829cc1SJean-Christophe PLAGNIOL-VILLARD fpa = page & ONENAND_FPA_MASK; 12159829cc1SJean-Christophe PLAGNIOL-VILLARD fsa = sector & ONENAND_FSA_MASK; 12259829cc1SJean-Christophe PLAGNIOL-VILLARD 12359829cc1SJean-Christophe PLAGNIOL-VILLARD return ((fpa << ONENAND_FPA_SHIFT) | fsa); 12459829cc1SJean-Christophe PLAGNIOL-VILLARD } 12559829cc1SJean-Christophe PLAGNIOL-VILLARD 12659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 12759829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_buffer_address - [DEFAULT] Get buffer address 12859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param dataram1 DataRAM index 12959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param sectors the sector address 13059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count the number of sectors 13159829cc1SJean-Christophe PLAGNIOL-VILLARD * @return the start buffer value 13259829cc1SJean-Christophe PLAGNIOL-VILLARD * 13359829cc1SJean-Christophe PLAGNIOL-VILLARD * Setup Start Buffer Register (F200h) 13459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 13559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_buffer_address(int dataram1, int sectors, int count) 13659829cc1SJean-Christophe PLAGNIOL-VILLARD { 13759829cc1SJean-Christophe PLAGNIOL-VILLARD int bsa, bsc; 13859829cc1SJean-Christophe PLAGNIOL-VILLARD 13959829cc1SJean-Christophe PLAGNIOL-VILLARD /* BufferRAM Sector Address */ 14059829cc1SJean-Christophe PLAGNIOL-VILLARD bsa = sectors & ONENAND_BSA_MASK; 14159829cc1SJean-Christophe PLAGNIOL-VILLARD 14259829cc1SJean-Christophe PLAGNIOL-VILLARD if (dataram1) 14359829cc1SJean-Christophe PLAGNIOL-VILLARD bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ 14459829cc1SJean-Christophe PLAGNIOL-VILLARD else 14559829cc1SJean-Christophe PLAGNIOL-VILLARD bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ 14659829cc1SJean-Christophe PLAGNIOL-VILLARD 14759829cc1SJean-Christophe PLAGNIOL-VILLARD /* BufferRAM Sector Count */ 14859829cc1SJean-Christophe PLAGNIOL-VILLARD bsc = count & ONENAND_BSC_MASK; 14959829cc1SJean-Christophe PLAGNIOL-VILLARD 15059829cc1SJean-Christophe PLAGNIOL-VILLARD return ((bsa << ONENAND_BSA_SHIFT) | bsc); 15159829cc1SJean-Christophe PLAGNIOL-VILLARD } 15259829cc1SJean-Christophe PLAGNIOL-VILLARD 15359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 154ef0921d6SKyungmin Park * onenand_get_density - [DEFAULT] Get OneNAND density 155ef0921d6SKyungmin Park * @param dev_id OneNAND device ID 156ef0921d6SKyungmin Park * 157ef0921d6SKyungmin Park * Get OneNAND density from device ID 158ef0921d6SKyungmin Park */ 159ef0921d6SKyungmin Park static inline int onenand_get_density(int dev_id) 160ef0921d6SKyungmin Park { 161ef0921d6SKyungmin Park int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; 162ef0921d6SKyungmin Park return (density & ONENAND_DEVICE_DENSITY_MASK); 163ef0921d6SKyungmin Park } 164ef0921d6SKyungmin Park 165ef0921d6SKyungmin Park /** 16659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_command - [DEFAULT] Send command to OneNAND device 16759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 16859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param cmd the command to be sent 16959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr offset to read from or write to 17059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param len number of bytes to read or write 17159829cc1SJean-Christophe PLAGNIOL-VILLARD * 17259829cc1SJean-Christophe PLAGNIOL-VILLARD * Send command to OneNAND device. This function is used for middle/large page 17359829cc1SJean-Christophe PLAGNIOL-VILLARD * devices (1KB/2KB Bytes per page) 17459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 17559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, 17659829cc1SJean-Christophe PLAGNIOL-VILLARD size_t len) 17759829cc1SJean-Christophe PLAGNIOL-VILLARD { 17859829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 17959829cc1SJean-Christophe PLAGNIOL-VILLARD int value, readcmd = 0; 18059829cc1SJean-Christophe PLAGNIOL-VILLARD int block, page; 18159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Now we use page size operation */ 18259829cc1SJean-Christophe PLAGNIOL-VILLARD int sectors = 4, count = 4; 18359829cc1SJean-Christophe PLAGNIOL-VILLARD 18459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Address translation */ 18559829cc1SJean-Christophe PLAGNIOL-VILLARD switch (cmd) { 18659829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_UNLOCK: 18759829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_LOCK: 18859829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_LOCK_TIGHT: 189ef0921d6SKyungmin Park case ONENAND_CMD_UNLOCK_ALL: 19059829cc1SJean-Christophe PLAGNIOL-VILLARD block = -1; 19159829cc1SJean-Christophe PLAGNIOL-VILLARD page = -1; 19259829cc1SJean-Christophe PLAGNIOL-VILLARD break; 19359829cc1SJean-Christophe PLAGNIOL-VILLARD 19459829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_ERASE: 19559829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_BUFFERRAM: 19659829cc1SJean-Christophe PLAGNIOL-VILLARD block = (int)(addr >> this->erase_shift); 19759829cc1SJean-Christophe PLAGNIOL-VILLARD page = -1; 19859829cc1SJean-Christophe PLAGNIOL-VILLARD break; 19959829cc1SJean-Christophe PLAGNIOL-VILLARD 20059829cc1SJean-Christophe PLAGNIOL-VILLARD default: 20159829cc1SJean-Christophe PLAGNIOL-VILLARD block = (int)(addr >> this->erase_shift); 20259829cc1SJean-Christophe PLAGNIOL-VILLARD page = (int)(addr >> this->page_shift); 20359829cc1SJean-Christophe PLAGNIOL-VILLARD page &= this->page_mask; 20459829cc1SJean-Christophe PLAGNIOL-VILLARD break; 20559829cc1SJean-Christophe PLAGNIOL-VILLARD } 20659829cc1SJean-Christophe PLAGNIOL-VILLARD 20759829cc1SJean-Christophe PLAGNIOL-VILLARD /* NOTE: The setting order of the registers is very important! */ 20859829cc1SJean-Christophe PLAGNIOL-VILLARD if (cmd == ONENAND_CMD_BUFFERRAM) { 20959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Select DataRAM for DDP */ 210ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 21159829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, 21259829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_ADDRESS2); 21359829cc1SJean-Christophe PLAGNIOL-VILLARD 21459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Switch to the next data buffer */ 21559829cc1SJean-Christophe PLAGNIOL-VILLARD ONENAND_SET_NEXT_BUFFERRAM(this); 21659829cc1SJean-Christophe PLAGNIOL-VILLARD 21759829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 21859829cc1SJean-Christophe PLAGNIOL-VILLARD } 21959829cc1SJean-Christophe PLAGNIOL-VILLARD 22059829cc1SJean-Christophe PLAGNIOL-VILLARD if (block != -1) { 22159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write 'DFS, FBA' of Flash */ 222ef0921d6SKyungmin Park value = onenand_block_address(this, block); 22359829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, 22459829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_ADDRESS1); 225ef0921d6SKyungmin Park 226ef0921d6SKyungmin Park /* Write 'DFS, FBA' of Flash */ 227ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 228ef0921d6SKyungmin Park this->write_word(value, 229ef0921d6SKyungmin Park this->base + ONENAND_REG_START_ADDRESS2); 23059829cc1SJean-Christophe PLAGNIOL-VILLARD } 23159829cc1SJean-Christophe PLAGNIOL-VILLARD 23259829cc1SJean-Christophe PLAGNIOL-VILLARD if (page != -1) { 23359829cc1SJean-Christophe PLAGNIOL-VILLARD int dataram; 23459829cc1SJean-Christophe PLAGNIOL-VILLARD 23559829cc1SJean-Christophe PLAGNIOL-VILLARD switch (cmd) { 23659829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_READ: 23759829cc1SJean-Christophe PLAGNIOL-VILLARD case ONENAND_CMD_READOOB: 23859829cc1SJean-Christophe PLAGNIOL-VILLARD dataram = ONENAND_SET_NEXT_BUFFERRAM(this); 23959829cc1SJean-Christophe PLAGNIOL-VILLARD readcmd = 1; 24059829cc1SJean-Christophe PLAGNIOL-VILLARD break; 24159829cc1SJean-Christophe PLAGNIOL-VILLARD 24259829cc1SJean-Christophe PLAGNIOL-VILLARD default: 24359829cc1SJean-Christophe PLAGNIOL-VILLARD dataram = ONENAND_CURRENT_BUFFERRAM(this); 24459829cc1SJean-Christophe PLAGNIOL-VILLARD break; 24559829cc1SJean-Christophe PLAGNIOL-VILLARD } 24659829cc1SJean-Christophe PLAGNIOL-VILLARD 24759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write 'FPA, FSA' of Flash */ 24859829cc1SJean-Christophe PLAGNIOL-VILLARD value = onenand_page_address(page, sectors); 24959829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, 25059829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_ADDRESS8); 25159829cc1SJean-Christophe PLAGNIOL-VILLARD 25259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write 'BSA, BSC' of DataRAM */ 25359829cc1SJean-Christophe PLAGNIOL-VILLARD value = onenand_buffer_address(dataram, sectors, count); 25459829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(value, this->base + ONENAND_REG_START_BUFFER); 25559829cc1SJean-Christophe PLAGNIOL-VILLARD } 25659829cc1SJean-Christophe PLAGNIOL-VILLARD 25759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Interrupt clear */ 25859829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); 25959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write command */ 26059829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(cmd, this->base + ONENAND_REG_COMMAND); 26159829cc1SJean-Christophe PLAGNIOL-VILLARD 26259829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 26359829cc1SJean-Christophe PLAGNIOL-VILLARD } 26459829cc1SJean-Christophe PLAGNIOL-VILLARD 26559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 26659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_wait - [DEFAULT] wait until the command is done 26759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 26859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param state state to select the max. timeout value 26959829cc1SJean-Christophe PLAGNIOL-VILLARD * 27059829cc1SJean-Christophe PLAGNIOL-VILLARD * Wait for command done. This applies to all OneNAND command 27159829cc1SJean-Christophe PLAGNIOL-VILLARD * Read can take up to 30us, erase up to 2ms and program up to 350us 27259829cc1SJean-Christophe PLAGNIOL-VILLARD * according to general OneNAND specs 27359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 27459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_wait(struct mtd_info *mtd, int state) 27559829cc1SJean-Christophe PLAGNIOL-VILLARD { 27659829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 27759829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned int flags = ONENAND_INT_MASTER; 27859829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned int interrupt = 0; 27959829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned int ctrl, ecc; 28059829cc1SJean-Christophe PLAGNIOL-VILLARD 28159829cc1SJean-Christophe PLAGNIOL-VILLARD while (1) { 28259829cc1SJean-Christophe PLAGNIOL-VILLARD interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); 28359829cc1SJean-Christophe PLAGNIOL-VILLARD if (interrupt & flags) 28459829cc1SJean-Christophe PLAGNIOL-VILLARD break; 28559829cc1SJean-Christophe PLAGNIOL-VILLARD } 28659829cc1SJean-Christophe PLAGNIOL-VILLARD 28759829cc1SJean-Christophe PLAGNIOL-VILLARD ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); 28859829cc1SJean-Christophe PLAGNIOL-VILLARD 28959829cc1SJean-Christophe PLAGNIOL-VILLARD if (ctrl & ONENAND_CTRL_ERROR) { 290ef0921d6SKyungmin Park printk("onenand_wait: controller error = 0x%04x\n", ctrl); 291ef0921d6SKyungmin Park if (ctrl & ONENAND_CTRL_LOCK) 292ef0921d6SKyungmin Park printk("onenand_wait: it's locked error = 0x%04x\n", 293ef0921d6SKyungmin Park ctrl); 29459829cc1SJean-Christophe PLAGNIOL-VILLARD 29559829cc1SJean-Christophe PLAGNIOL-VILLARD return -EIO; 29659829cc1SJean-Christophe PLAGNIOL-VILLARD } 29759829cc1SJean-Christophe PLAGNIOL-VILLARD 29859829cc1SJean-Christophe PLAGNIOL-VILLARD if (interrupt & ONENAND_INT_READ) { 29959829cc1SJean-Christophe PLAGNIOL-VILLARD ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); 30059829cc1SJean-Christophe PLAGNIOL-VILLARD if (ecc & ONENAND_ECC_2BIT_ALL) { 3013167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, 30259829cc1SJean-Christophe PLAGNIOL-VILLARD "onenand_wait: ECC error = 0x%04x\n", ecc); 30359829cc1SJean-Christophe PLAGNIOL-VILLARD return -EBADMSG; 30459829cc1SJean-Christophe PLAGNIOL-VILLARD } 30559829cc1SJean-Christophe PLAGNIOL-VILLARD } 30659829cc1SJean-Christophe PLAGNIOL-VILLARD 30759829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 30859829cc1SJean-Christophe PLAGNIOL-VILLARD } 30959829cc1SJean-Christophe PLAGNIOL-VILLARD 31059829cc1SJean-Christophe PLAGNIOL-VILLARD /** 31159829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_bufferram_offset - [DEFAULT] BufferRAM offset 31259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 31359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 31459829cc1SJean-Christophe PLAGNIOL-VILLARD * @return offset given area 31559829cc1SJean-Christophe PLAGNIOL-VILLARD * 31659829cc1SJean-Christophe PLAGNIOL-VILLARD * Return BufferRAM offset given area 31759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 31859829cc1SJean-Christophe PLAGNIOL-VILLARD static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) 31959829cc1SJean-Christophe PLAGNIOL-VILLARD { 32059829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 32159829cc1SJean-Christophe PLAGNIOL-VILLARD 32259829cc1SJean-Christophe PLAGNIOL-VILLARD if (ONENAND_CURRENT_BUFFERRAM(this)) { 32359829cc1SJean-Christophe PLAGNIOL-VILLARD if (area == ONENAND_DATARAM) 324d438d508SKyungmin Park return mtd->writesize; 32559829cc1SJean-Christophe PLAGNIOL-VILLARD if (area == ONENAND_SPARERAM) 32659829cc1SJean-Christophe PLAGNIOL-VILLARD return mtd->oobsize; 32759829cc1SJean-Christophe PLAGNIOL-VILLARD } 32859829cc1SJean-Christophe PLAGNIOL-VILLARD 32959829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 33059829cc1SJean-Christophe PLAGNIOL-VILLARD } 33159829cc1SJean-Christophe PLAGNIOL-VILLARD 33259829cc1SJean-Christophe PLAGNIOL-VILLARD /** 33359829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area 33459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 33559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 33659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buffer the databuffer to put/get data 33759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param offset offset to read from or write to 33859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count number of bytes to read/write 33959829cc1SJean-Christophe PLAGNIOL-VILLARD * 34059829cc1SJean-Christophe PLAGNIOL-VILLARD * Read the BufferRAM area 34159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 342ef0921d6SKyungmin Park static int onenand_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, 34359829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned char *buffer, int offset, 34459829cc1SJean-Christophe PLAGNIOL-VILLARD size_t count) 34559829cc1SJean-Christophe PLAGNIOL-VILLARD { 34659829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 34759829cc1SJean-Christophe PLAGNIOL-VILLARD void __iomem *bufferram; 34859829cc1SJean-Christophe PLAGNIOL-VILLARD 34959829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram = this->base + area; 35059829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram += onenand_bufferram_offset(mtd, area); 35159829cc1SJean-Christophe PLAGNIOL-VILLARD 352d2c6fbecSWolfgang Denk memcpy_16(buffer, bufferram + offset, count); 35359829cc1SJean-Christophe PLAGNIOL-VILLARD 35459829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 35559829cc1SJean-Christophe PLAGNIOL-VILLARD } 35659829cc1SJean-Christophe PLAGNIOL-VILLARD 35759829cc1SJean-Christophe PLAGNIOL-VILLARD /** 35859829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode 35959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 36059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 36159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buffer the databuffer to put/get data 36259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param offset offset to read from or write to 36359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count number of bytes to read/write 36459829cc1SJean-Christophe PLAGNIOL-VILLARD * 36559829cc1SJean-Christophe PLAGNIOL-VILLARD * Read the BufferRAM area with Sync. Burst Mode 36659829cc1SJean-Christophe PLAGNIOL-VILLARD */ 367ef0921d6SKyungmin Park static int onenand_sync_read_bufferram(struct mtd_info *mtd, loff_t addr, int area, 36859829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned char *buffer, int offset, 36959829cc1SJean-Christophe PLAGNIOL-VILLARD size_t count) 37059829cc1SJean-Christophe PLAGNIOL-VILLARD { 37159829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 37259829cc1SJean-Christophe PLAGNIOL-VILLARD void __iomem *bufferram; 37359829cc1SJean-Christophe PLAGNIOL-VILLARD 37459829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram = this->base + area; 37559829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram += onenand_bufferram_offset(mtd, area); 37659829cc1SJean-Christophe PLAGNIOL-VILLARD 37759829cc1SJean-Christophe PLAGNIOL-VILLARD this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ); 37859829cc1SJean-Christophe PLAGNIOL-VILLARD 379d2c6fbecSWolfgang Denk memcpy_16(buffer, bufferram + offset, count); 38059829cc1SJean-Christophe PLAGNIOL-VILLARD 38159829cc1SJean-Christophe PLAGNIOL-VILLARD this->mmcontrol(mtd, 0); 38259829cc1SJean-Christophe PLAGNIOL-VILLARD 38359829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 38459829cc1SJean-Christophe PLAGNIOL-VILLARD } 38559829cc1SJean-Christophe PLAGNIOL-VILLARD 38659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 38759829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area 38859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 38959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param area BufferRAM area 39059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buffer the databuffer to put/get data 39159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param offset offset to read from or write to 39259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param count number of bytes to read/write 39359829cc1SJean-Christophe PLAGNIOL-VILLARD * 39459829cc1SJean-Christophe PLAGNIOL-VILLARD * Write the BufferRAM area 39559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 396ef0921d6SKyungmin Park static int onenand_write_bufferram(struct mtd_info *mtd, loff_t addr, int area, 39759829cc1SJean-Christophe PLAGNIOL-VILLARD const unsigned char *buffer, int offset, 39859829cc1SJean-Christophe PLAGNIOL-VILLARD size_t count) 39959829cc1SJean-Christophe PLAGNIOL-VILLARD { 40059829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 40159829cc1SJean-Christophe PLAGNIOL-VILLARD void __iomem *bufferram; 40259829cc1SJean-Christophe PLAGNIOL-VILLARD 40359829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram = this->base + area; 40459829cc1SJean-Christophe PLAGNIOL-VILLARD bufferram += onenand_bufferram_offset(mtd, area); 40559829cc1SJean-Christophe PLAGNIOL-VILLARD 406d2c6fbecSWolfgang Denk memcpy_16(bufferram + offset, buffer, count); 40759829cc1SJean-Christophe PLAGNIOL-VILLARD 40859829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 40959829cc1SJean-Christophe PLAGNIOL-VILLARD } 41059829cc1SJean-Christophe PLAGNIOL-VILLARD 41159829cc1SJean-Christophe PLAGNIOL-VILLARD /** 4124fca3310SStefan Roese * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode 4134fca3310SStefan Roese * @param mtd MTD data structure 4144fca3310SStefan Roese * @param addr address to check 4154fca3310SStefan Roese * @return blockpage address 4164fca3310SStefan Roese * 4174fca3310SStefan Roese * Get blockpage address at 2x program mode 4184fca3310SStefan Roese */ 4194fca3310SStefan Roese static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) 4204fca3310SStefan Roese { 4214fca3310SStefan Roese struct onenand_chip *this = mtd->priv; 4224fca3310SStefan Roese int blockpage, block, page; 4234fca3310SStefan Roese 4244fca3310SStefan Roese /* Calculate the even block number */ 4254fca3310SStefan Roese block = (int) (addr >> this->erase_shift) & ~1; 4264fca3310SStefan Roese /* Is it the odd plane? */ 4274fca3310SStefan Roese if (addr & this->writesize) 4284fca3310SStefan Roese block++; 4294fca3310SStefan Roese page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; 4304fca3310SStefan Roese blockpage = (block << 7) | page; 4314fca3310SStefan Roese 4324fca3310SStefan Roese return blockpage; 4334fca3310SStefan Roese } 4344fca3310SStefan Roese 4354fca3310SStefan Roese /** 43659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_check_bufferram - [GENERIC] Check BufferRAM information 43759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 43859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to check 43959829cc1SJean-Christophe PLAGNIOL-VILLARD * @return 1 if there are valid data, otherwise 0 44059829cc1SJean-Christophe PLAGNIOL-VILLARD * 44159829cc1SJean-Christophe PLAGNIOL-VILLARD * Check bufferram if there is data we required 44259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 44359829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) 44459829cc1SJean-Christophe PLAGNIOL-VILLARD { 44559829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 446ef0921d6SKyungmin Park int blockpage, found = 0; 447ef0921d6SKyungmin Park unsigned int i; 44859829cc1SJean-Christophe PLAGNIOL-VILLARD 449ef0921d6SKyungmin Park #ifdef CONFIG_S3C64XX 450ef0921d6SKyungmin Park return 0; 451ef0921d6SKyungmin Park #endif 45259829cc1SJean-Christophe PLAGNIOL-VILLARD 453ef0921d6SKyungmin Park if (ONENAND_IS_2PLANE(this)) 454ef0921d6SKyungmin Park blockpage = onenand_get_2x_blockpage(mtd, addr); 455ef0921d6SKyungmin Park else 456ef0921d6SKyungmin Park blockpage = (int) (addr >> this->page_shift); 45759829cc1SJean-Christophe PLAGNIOL-VILLARD 45859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Is there valid data? */ 459ef0921d6SKyungmin Park i = ONENAND_CURRENT_BUFFERRAM(this); 460ef0921d6SKyungmin Park if (this->bufferram[i].blockpage == blockpage) 461ef0921d6SKyungmin Park found = 1; 462ef0921d6SKyungmin Park else { 463ef0921d6SKyungmin Park /* Check another BufferRAM */ 464ef0921d6SKyungmin Park i = ONENAND_NEXT_BUFFERRAM(this); 465ef0921d6SKyungmin Park if (this->bufferram[i].blockpage == blockpage) { 466ef0921d6SKyungmin Park ONENAND_SET_NEXT_BUFFERRAM(this); 467ef0921d6SKyungmin Park found = 1; 468ef0921d6SKyungmin Park } 469ef0921d6SKyungmin Park } 47059829cc1SJean-Christophe PLAGNIOL-VILLARD 471ef0921d6SKyungmin Park if (found && ONENAND_IS_DDP(this)) { 472ef0921d6SKyungmin Park /* Select DataRAM for DDP */ 473ef0921d6SKyungmin Park int block = (int) (addr >> this->erase_shift); 474ef0921d6SKyungmin Park int value = onenand_bufferram_address(this, block); 475ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); 476ef0921d6SKyungmin Park } 477ef0921d6SKyungmin Park 478ef0921d6SKyungmin Park return found; 47959829cc1SJean-Christophe PLAGNIOL-VILLARD } 48059829cc1SJean-Christophe PLAGNIOL-VILLARD 48159829cc1SJean-Christophe PLAGNIOL-VILLARD /** 48259829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_update_bufferram - [GENERIC] Update BufferRAM information 48359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD data structure 48459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param addr address to update 48559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param valid valid flag 48659829cc1SJean-Christophe PLAGNIOL-VILLARD * 48759829cc1SJean-Christophe PLAGNIOL-VILLARD * Update BufferRAM information 48859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 48959829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, 49059829cc1SJean-Christophe PLAGNIOL-VILLARD int valid) 49159829cc1SJean-Christophe PLAGNIOL-VILLARD { 49259829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 493ef0921d6SKyungmin Park int blockpage; 494ef0921d6SKyungmin Park unsigned int i; 49559829cc1SJean-Christophe PLAGNIOL-VILLARD 496ef0921d6SKyungmin Park if (ONENAND_IS_2PLANE(this)) 497ef0921d6SKyungmin Park blockpage = onenand_get_2x_blockpage(mtd, addr); 498ef0921d6SKyungmin Park else 499ef0921d6SKyungmin Park blockpage = (int)(addr >> this->page_shift); 50059829cc1SJean-Christophe PLAGNIOL-VILLARD 501ef0921d6SKyungmin Park /* Invalidate another BufferRAM */ 502ef0921d6SKyungmin Park i = ONENAND_NEXT_BUFFERRAM(this); 503ef0921d6SKyungmin Park if (this->bufferram[i].blockpage == blockpage) 504ef0921d6SKyungmin Park this->bufferram[i].blockpage = -1; 50559829cc1SJean-Christophe PLAGNIOL-VILLARD 50659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Update BufferRAM */ 50759829cc1SJean-Christophe PLAGNIOL-VILLARD i = ONENAND_CURRENT_BUFFERRAM(this); 508ef0921d6SKyungmin Park if (valid) 509ef0921d6SKyungmin Park this->bufferram[i].blockpage = blockpage; 510ef0921d6SKyungmin Park else 511ef0921d6SKyungmin Park this->bufferram[i].blockpage = -1; 51259829cc1SJean-Christophe PLAGNIOL-VILLARD 51359829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 51459829cc1SJean-Christophe PLAGNIOL-VILLARD } 51559829cc1SJean-Christophe PLAGNIOL-VILLARD 51659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 517d438d508SKyungmin Park * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information 518d438d508SKyungmin Park * @param mtd MTD data structure 519d438d508SKyungmin Park * @param addr start address to invalidate 520d438d508SKyungmin Park * @param len length to invalidate 521d438d508SKyungmin Park * 522d438d508SKyungmin Park * Invalidate BufferRAM information 523d438d508SKyungmin Park */ 524d438d508SKyungmin Park static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, 525d438d508SKyungmin Park unsigned int len) 526d438d508SKyungmin Park { 527d438d508SKyungmin Park struct onenand_chip *this = mtd->priv; 528d438d508SKyungmin Park int i; 529d438d508SKyungmin Park loff_t end_addr = addr + len; 530d438d508SKyungmin Park 531d438d508SKyungmin Park /* Invalidate BufferRAM */ 532d438d508SKyungmin Park for (i = 0; i < MAX_BUFFERRAM; i++) { 533ef0921d6SKyungmin Park loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; 534d438d508SKyungmin Park 535d438d508SKyungmin Park if (buf_addr >= addr && buf_addr < end_addr) 536ef0921d6SKyungmin Park this->bufferram[i].blockpage = -1; 537d438d508SKyungmin Park } 538d438d508SKyungmin Park } 539d438d508SKyungmin Park 540d438d508SKyungmin Park /** 54159829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_get_device - [GENERIC] Get chip for selected access 54259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 54359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param new_state the state which is requested 54459829cc1SJean-Christophe PLAGNIOL-VILLARD * 54559829cc1SJean-Christophe PLAGNIOL-VILLARD * Get the device and lock it for exclusive access 54659829cc1SJean-Christophe PLAGNIOL-VILLARD */ 54759829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_get_device(struct mtd_info *mtd, int new_state) 54859829cc1SJean-Christophe PLAGNIOL-VILLARD { 54959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do nothing */ 55059829cc1SJean-Christophe PLAGNIOL-VILLARD } 55159829cc1SJean-Christophe PLAGNIOL-VILLARD 55259829cc1SJean-Christophe PLAGNIOL-VILLARD /** 55359829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_release_device - [GENERIC] release chip 55459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 55559829cc1SJean-Christophe PLAGNIOL-VILLARD * 55659829cc1SJean-Christophe PLAGNIOL-VILLARD * Deselect, release chip lock and wake up anyone waiting on the device 55759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 55859829cc1SJean-Christophe PLAGNIOL-VILLARD static void onenand_release_device(struct mtd_info *mtd) 55959829cc1SJean-Christophe PLAGNIOL-VILLARD { 56059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do nothing */ 56159829cc1SJean-Christophe PLAGNIOL-VILLARD } 56259829cc1SJean-Christophe PLAGNIOL-VILLARD 56359829cc1SJean-Christophe PLAGNIOL-VILLARD /** 564bfd7f386SKyungmin Park * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer 56559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 566bfd7f386SKyungmin Park * @param buf destination address 567bfd7f386SKyungmin Park * @param column oob offset to read from 568bfd7f386SKyungmin Park * @param thislen oob length to read 56959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 570bfd7f386SKyungmin Park static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, 571bfd7f386SKyungmin Park int column, int thislen) 57259829cc1SJean-Christophe PLAGNIOL-VILLARD { 57359829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 574bfd7f386SKyungmin Park struct nand_oobfree *free; 575bfd7f386SKyungmin Park int readcol = column; 576bfd7f386SKyungmin Park int readend = column + thislen; 577bfd7f386SKyungmin Park int lastgap = 0; 578bfd7f386SKyungmin Park unsigned int i; 579bfd7f386SKyungmin Park uint8_t *oob_buf = this->oob_buf; 58059829cc1SJean-Christophe PLAGNIOL-VILLARD 581bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 582bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 583bfd7f386SKyungmin Park if (readcol >= lastgap) 584bfd7f386SKyungmin Park readcol += free->offset - lastgap; 585bfd7f386SKyungmin Park if (readend >= lastgap) 586bfd7f386SKyungmin Park readend += free->offset - lastgap; 587bfd7f386SKyungmin Park lastgap = free->offset + free->length; 588bfd7f386SKyungmin Park } 589ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); 590bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 591bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 592bfd7f386SKyungmin Park int free_end = free->offset + free->length; 593bfd7f386SKyungmin Park if (free->offset < readend && free_end > readcol) { 594bfd7f386SKyungmin Park int st = max_t(int,free->offset,readcol); 595bfd7f386SKyungmin Park int ed = min_t(int,free_end,readend); 596bfd7f386SKyungmin Park int n = ed - st; 597bfd7f386SKyungmin Park memcpy(buf, oob_buf + st, n); 598bfd7f386SKyungmin Park buf += n; 599bfd7f386SKyungmin Park } else if (column == 0) 600bfd7f386SKyungmin Park break; 601bfd7f386SKyungmin Park } 602bfd7f386SKyungmin Park return 0; 603bfd7f386SKyungmin Park } 604bfd7f386SKyungmin Park 605bfd7f386SKyungmin Park /** 606bfd7f386SKyungmin Park * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band 607bfd7f386SKyungmin Park * @param mtd MTD device structure 608bfd7f386SKyungmin Park * @param from offset to read from 609bfd7f386SKyungmin Park * @param ops oob operation description structure 610bfd7f386SKyungmin Park * 611bfd7f386SKyungmin Park * OneNAND read main and/or out-of-band data 612bfd7f386SKyungmin Park */ 613bfd7f386SKyungmin Park static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, 614bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 615bfd7f386SKyungmin Park { 616bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 617bfd7f386SKyungmin Park struct mtd_ecc_stats stats; 618bfd7f386SKyungmin Park size_t len = ops->len; 619bfd7f386SKyungmin Park size_t ooblen = ops->ooblen; 620bfd7f386SKyungmin Park u_char *buf = ops->datbuf; 621bfd7f386SKyungmin Park u_char *oobbuf = ops->oobbuf; 622bfd7f386SKyungmin Park int read = 0, column, thislen; 623bfd7f386SKyungmin Park int oobread = 0, oobcolumn, thisooblen, oobsize; 624bfd7f386SKyungmin Park int ret = 0, boundary = 0; 625bfd7f386SKyungmin Park int writesize = this->writesize; 626bfd7f386SKyungmin Park 627ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); 62859829cc1SJean-Christophe PLAGNIOL-VILLARD 629bfd7f386SKyungmin Park if (ops->mode == MTD_OOB_AUTO) 630bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 631bfd7f386SKyungmin Park else 632bfd7f386SKyungmin Park oobsize = mtd->oobsize; 633bfd7f386SKyungmin Park 634bfd7f386SKyungmin Park oobcolumn = from & (mtd->oobsize - 1); 635bfd7f386SKyungmin Park 63659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do not allow reads past end of device */ 63759829cc1SJean-Christophe PLAGNIOL-VILLARD if ((from + len) > mtd->size) { 638bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); 639bfd7f386SKyungmin Park ops->retlen = 0; 640bfd7f386SKyungmin Park ops->oobretlen = 0; 64159829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 64259829cc1SJean-Christophe PLAGNIOL-VILLARD } 64359829cc1SJean-Christophe PLAGNIOL-VILLARD 644bfd7f386SKyungmin Park stats = mtd->ecc_stats; 64559829cc1SJean-Christophe PLAGNIOL-VILLARD 646bfd7f386SKyungmin Park /* Read-while-load method */ 64759829cc1SJean-Christophe PLAGNIOL-VILLARD 648bfd7f386SKyungmin Park /* Do first load to bufferRAM */ 649bfd7f386SKyungmin Park if (read < len) { 65059829cc1SJean-Christophe PLAGNIOL-VILLARD if (!onenand_check_bufferram(mtd, from)) { 651ef0921d6SKyungmin Park this->main_buf = buf; 652bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READ, from, writesize); 65359829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_READING); 654bfd7f386SKyungmin Park onenand_update_bufferram(mtd, from, !ret); 655bfd7f386SKyungmin Park if (ret == -EBADMSG) 656bfd7f386SKyungmin Park ret = 0; 657bfd7f386SKyungmin Park } 65859829cc1SJean-Christophe PLAGNIOL-VILLARD } 65959829cc1SJean-Christophe PLAGNIOL-VILLARD 660bfd7f386SKyungmin Park thislen = min_t(int, writesize, len - read); 661bfd7f386SKyungmin Park column = from & (writesize - 1); 662bfd7f386SKyungmin Park if (column + thislen > writesize) 663bfd7f386SKyungmin Park thislen = writesize - column; 66459829cc1SJean-Christophe PLAGNIOL-VILLARD 665bfd7f386SKyungmin Park while (!ret) { 666bfd7f386SKyungmin Park /* If there is more to load then start next load */ 667bfd7f386SKyungmin Park from += thislen; 668bfd7f386SKyungmin Park if (read + thislen < len) { 669ef0921d6SKyungmin Park this->main_buf = buf + thislen; 670bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READ, from, writesize); 671bfd7f386SKyungmin Park /* 672bfd7f386SKyungmin Park * Chip boundary handling in DDP 673bfd7f386SKyungmin Park * Now we issued chip 1 read and pointed chip 1 674bfd7f386SKyungmin Park * bufferam so we have to point chip 0 bufferam. 675bfd7f386SKyungmin Park */ 676bfd7f386SKyungmin Park if (ONENAND_IS_DDP(this) && 677bfd7f386SKyungmin Park unlikely(from == (this->chipsize >> 1))) { 678bfd7f386SKyungmin Park this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); 679bfd7f386SKyungmin Park boundary = 1; 680bfd7f386SKyungmin Park } else 681bfd7f386SKyungmin Park boundary = 0; 682bfd7f386SKyungmin Park ONENAND_SET_PREV_BUFFERRAM(this); 683bfd7f386SKyungmin Park } 684bfd7f386SKyungmin Park 685bfd7f386SKyungmin Park /* While load is going, read from last bufferRAM */ 686ef0921d6SKyungmin Park this->read_bufferram(mtd, from - thislen, ONENAND_DATARAM, buf, column, thislen); 687bfd7f386SKyungmin Park 688bfd7f386SKyungmin Park /* Read oob area if needed */ 689bfd7f386SKyungmin Park if (oobbuf) { 690bfd7f386SKyungmin Park thisooblen = oobsize - oobcolumn; 691bfd7f386SKyungmin Park thisooblen = min_t(int, thisooblen, ooblen - oobread); 692bfd7f386SKyungmin Park 693bfd7f386SKyungmin Park if (ops->mode == MTD_OOB_AUTO) 694bfd7f386SKyungmin Park onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); 695bfd7f386SKyungmin Park else 696ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); 697bfd7f386SKyungmin Park oobread += thisooblen; 698bfd7f386SKyungmin Park oobbuf += thisooblen; 699bfd7f386SKyungmin Park oobcolumn = 0; 700bfd7f386SKyungmin Park } 701bfd7f386SKyungmin Park 702bfd7f386SKyungmin Park /* See if we are done */ 70359829cc1SJean-Christophe PLAGNIOL-VILLARD read += thislen; 70459829cc1SJean-Christophe PLAGNIOL-VILLARD if (read == len) 70559829cc1SJean-Christophe PLAGNIOL-VILLARD break; 706bfd7f386SKyungmin Park /* Set up for next read from bufferRAM */ 707bfd7f386SKyungmin Park if (unlikely(boundary)) 708bfd7f386SKyungmin Park this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); 709bfd7f386SKyungmin Park ONENAND_SET_NEXT_BUFFERRAM(this); 71059829cc1SJean-Christophe PLAGNIOL-VILLARD buf += thislen; 711bfd7f386SKyungmin Park thislen = min_t(int, writesize, len - read); 712bfd7f386SKyungmin Park column = 0; 71359829cc1SJean-Christophe PLAGNIOL-VILLARD 714bfd7f386SKyungmin Park /* Now wait for load */ 715bfd7f386SKyungmin Park ret = this->wait(mtd, FL_READING); 716bfd7f386SKyungmin Park onenand_update_bufferram(mtd, from, !ret); 717bfd7f386SKyungmin Park if (ret == -EBADMSG) 718bfd7f386SKyungmin Park ret = 0; 719bfd7f386SKyungmin Park } 72059829cc1SJean-Christophe PLAGNIOL-VILLARD 72159829cc1SJean-Christophe PLAGNIOL-VILLARD /* 72259829cc1SJean-Christophe PLAGNIOL-VILLARD * Return success, if no ECC failures, else -EBADMSG 72359829cc1SJean-Christophe PLAGNIOL-VILLARD * fs driver will take care of that, because 72459829cc1SJean-Christophe PLAGNIOL-VILLARD * retlen == desired len and result == -EBADMSG 72559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 726bfd7f386SKyungmin Park ops->retlen = read; 727bfd7f386SKyungmin Park ops->oobretlen = oobread; 728bfd7f386SKyungmin Park 729bfd7f386SKyungmin Park if (ret) 73059829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 731bfd7f386SKyungmin Park 732bfd7f386SKyungmin Park if (mtd->ecc_stats.failed - stats.failed) 733bfd7f386SKyungmin Park return -EBADMSG; 734bfd7f386SKyungmin Park 735bfd7f386SKyungmin Park return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; 736bfd7f386SKyungmin Park } 737bfd7f386SKyungmin Park 738bfd7f386SKyungmin Park /** 739bfd7f386SKyungmin Park * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band 740bfd7f386SKyungmin Park * @param mtd MTD device structure 741bfd7f386SKyungmin Park * @param from offset to read from 742bfd7f386SKyungmin Park * @param ops oob operation description structure 743bfd7f386SKyungmin Park * 744bfd7f386SKyungmin Park * OneNAND read out-of-band data from the spare area 745bfd7f386SKyungmin Park */ 746bfd7f386SKyungmin Park static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, 747bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 748bfd7f386SKyungmin Park { 749bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 750bfd7f386SKyungmin Park struct mtd_ecc_stats stats; 751bfd7f386SKyungmin Park int read = 0, thislen, column, oobsize; 752bfd7f386SKyungmin Park size_t len = ops->ooblen; 753bfd7f386SKyungmin Park mtd_oob_mode_t mode = ops->mode; 754bfd7f386SKyungmin Park u_char *buf = ops->oobbuf; 755bfd7f386SKyungmin Park int ret = 0; 756bfd7f386SKyungmin Park 757bfd7f386SKyungmin Park from += ops->ooboffs; 758bfd7f386SKyungmin Park 759ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); 760bfd7f386SKyungmin Park 761bfd7f386SKyungmin Park /* Initialize return length value */ 762bfd7f386SKyungmin Park ops->oobretlen = 0; 763bfd7f386SKyungmin Park 764bfd7f386SKyungmin Park if (mode == MTD_OOB_AUTO) 765bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 766bfd7f386SKyungmin Park else 767bfd7f386SKyungmin Park oobsize = mtd->oobsize; 768bfd7f386SKyungmin Park 769bfd7f386SKyungmin Park column = from & (mtd->oobsize - 1); 770bfd7f386SKyungmin Park 771bfd7f386SKyungmin Park if (unlikely(column >= oobsize)) { 772bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); 773bfd7f386SKyungmin Park return -EINVAL; 774bfd7f386SKyungmin Park } 775bfd7f386SKyungmin Park 776bfd7f386SKyungmin Park /* Do not allow reads past end of device */ 777bfd7f386SKyungmin Park if (unlikely(from >= mtd->size || 778bfd7f386SKyungmin Park column + len > ((mtd->size >> this->page_shift) - 779bfd7f386SKyungmin Park (from >> this->page_shift)) * oobsize)) { 780bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); 781bfd7f386SKyungmin Park return -EINVAL; 782bfd7f386SKyungmin Park } 783bfd7f386SKyungmin Park 784bfd7f386SKyungmin Park stats = mtd->ecc_stats; 785bfd7f386SKyungmin Park 786bfd7f386SKyungmin Park while (read < len) { 787bfd7f386SKyungmin Park thislen = oobsize - column; 788bfd7f386SKyungmin Park thislen = min_t(int, thislen, len); 789bfd7f386SKyungmin Park 790ef0921d6SKyungmin Park this->spare_buf = buf; 791bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); 792bfd7f386SKyungmin Park 793bfd7f386SKyungmin Park onenand_update_bufferram(mtd, from, 0); 794bfd7f386SKyungmin Park 795bfd7f386SKyungmin Park ret = this->wait(mtd, FL_READING); 796bfd7f386SKyungmin Park if (ret && ret != -EBADMSG) { 797bfd7f386SKyungmin Park printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); 798bfd7f386SKyungmin Park break; 799bfd7f386SKyungmin Park } 800bfd7f386SKyungmin Park 801bfd7f386SKyungmin Park if (mode == MTD_OOB_AUTO) 802bfd7f386SKyungmin Park onenand_transfer_auto_oob(mtd, buf, column, thislen); 803bfd7f386SKyungmin Park else 804ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); 805bfd7f386SKyungmin Park 806bfd7f386SKyungmin Park read += thislen; 807bfd7f386SKyungmin Park 808bfd7f386SKyungmin Park if (read == len) 809bfd7f386SKyungmin Park break; 810bfd7f386SKyungmin Park 811bfd7f386SKyungmin Park buf += thislen; 812bfd7f386SKyungmin Park 813bfd7f386SKyungmin Park /* Read more? */ 814bfd7f386SKyungmin Park if (read < len) { 815bfd7f386SKyungmin Park /* Page size */ 816bfd7f386SKyungmin Park from += mtd->writesize; 817bfd7f386SKyungmin Park column = 0; 818bfd7f386SKyungmin Park } 819bfd7f386SKyungmin Park } 820bfd7f386SKyungmin Park 821bfd7f386SKyungmin Park ops->oobretlen = read; 822bfd7f386SKyungmin Park 823bfd7f386SKyungmin Park if (ret) 824bfd7f386SKyungmin Park return ret; 825bfd7f386SKyungmin Park 826bfd7f386SKyungmin Park if (mtd->ecc_stats.failed - stats.failed) 827bfd7f386SKyungmin Park return -EBADMSG; 828bfd7f386SKyungmin Park 829bfd7f386SKyungmin Park return 0; 83059829cc1SJean-Christophe PLAGNIOL-VILLARD } 83159829cc1SJean-Christophe PLAGNIOL-VILLARD 83259829cc1SJean-Christophe PLAGNIOL-VILLARD /** 83359829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_read - [MTD Interface] MTD compability function for onenand_read_ecc 83459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 83559829cc1SJean-Christophe PLAGNIOL-VILLARD * @param from offset to read from 83659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param len number of bytes to read 83759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param retlen pointer to variable to store the number of read bytes 83859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buf the databuffer to put data 83959829cc1SJean-Christophe PLAGNIOL-VILLARD * 84059829cc1SJean-Christophe PLAGNIOL-VILLARD * This function simply calls onenand_read_ecc with oob buffer and oobsel = NULL 84159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 84259829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, 84359829cc1SJean-Christophe PLAGNIOL-VILLARD size_t * retlen, u_char * buf) 84459829cc1SJean-Christophe PLAGNIOL-VILLARD { 845bfd7f386SKyungmin Park struct mtd_oob_ops ops = { 846bfd7f386SKyungmin Park .len = len, 847bfd7f386SKyungmin Park .ooblen = 0, 848bfd7f386SKyungmin Park .datbuf = buf, 849bfd7f386SKyungmin Park .oobbuf = NULL, 850bfd7f386SKyungmin Park }; 851bfd7f386SKyungmin Park int ret; 852bfd7f386SKyungmin Park 853bfd7f386SKyungmin Park onenand_get_device(mtd, FL_READING); 854bfd7f386SKyungmin Park ret = onenand_read_ops_nolock(mtd, from, &ops); 855bfd7f386SKyungmin Park onenand_release_device(mtd); 856bfd7f386SKyungmin Park 857bfd7f386SKyungmin Park *retlen = ops.retlen; 858bfd7f386SKyungmin Park return ret; 85959829cc1SJean-Christophe PLAGNIOL-VILLARD } 86059829cc1SJean-Christophe PLAGNIOL-VILLARD 86159829cc1SJean-Christophe PLAGNIOL-VILLARD /** 86259829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_read_oob - [MTD Interface] OneNAND read out-of-band 86359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 86459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param from offset to read from 865bfd7f386SKyungmin Park * @param ops oob operations description structure 86659829cc1SJean-Christophe PLAGNIOL-VILLARD * 867bfd7f386SKyungmin Park * OneNAND main and/or out-of-band 86859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 869bfd7f386SKyungmin Park int onenand_read_oob(struct mtd_info *mtd, loff_t from, 870bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 871bfd7f386SKyungmin Park { 872bfd7f386SKyungmin Park int ret; 873bfd7f386SKyungmin Park 874bfd7f386SKyungmin Park switch (ops->mode) { 875bfd7f386SKyungmin Park case MTD_OOB_PLACE: 876bfd7f386SKyungmin Park case MTD_OOB_AUTO: 877bfd7f386SKyungmin Park break; 878bfd7f386SKyungmin Park case MTD_OOB_RAW: 879bfd7f386SKyungmin Park /* Not implemented yet */ 880bfd7f386SKyungmin Park default: 881bfd7f386SKyungmin Park return -EINVAL; 882bfd7f386SKyungmin Park } 883bfd7f386SKyungmin Park 884bfd7f386SKyungmin Park onenand_get_device(mtd, FL_READING); 885bfd7f386SKyungmin Park if (ops->datbuf) 886bfd7f386SKyungmin Park ret = onenand_read_ops_nolock(mtd, from, ops); 887bfd7f386SKyungmin Park else 888bfd7f386SKyungmin Park ret = onenand_read_oob_nolock(mtd, from, ops); 889bfd7f386SKyungmin Park onenand_release_device(mtd); 890bfd7f386SKyungmin Park 891bfd7f386SKyungmin Park return ret; 892bfd7f386SKyungmin Park } 893bfd7f386SKyungmin Park 894bfd7f386SKyungmin Park /** 895bfd7f386SKyungmin Park * onenand_bbt_wait - [DEFAULT] wait until the command is done 896bfd7f386SKyungmin Park * @param mtd MTD device structure 897bfd7f386SKyungmin Park * @param state state to select the max. timeout value 898bfd7f386SKyungmin Park * 899bfd7f386SKyungmin Park * Wait for command done. 900bfd7f386SKyungmin Park */ 901bfd7f386SKyungmin Park static int onenand_bbt_wait(struct mtd_info *mtd, int state) 902bfd7f386SKyungmin Park { 903bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 904bfd7f386SKyungmin Park unsigned int flags = ONENAND_INT_MASTER; 905bfd7f386SKyungmin Park unsigned int interrupt; 906bfd7f386SKyungmin Park unsigned int ctrl; 907bfd7f386SKyungmin Park 908bfd7f386SKyungmin Park while (1) { 909bfd7f386SKyungmin Park interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); 910bfd7f386SKyungmin Park if (interrupt & flags) 911bfd7f386SKyungmin Park break; 912bfd7f386SKyungmin Park } 913bfd7f386SKyungmin Park 914bfd7f386SKyungmin Park /* To get correct interrupt status in timeout case */ 915bfd7f386SKyungmin Park interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); 916bfd7f386SKyungmin Park ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); 917bfd7f386SKyungmin Park 918bfd7f386SKyungmin Park if (interrupt & ONENAND_INT_READ) { 919bfd7f386SKyungmin Park int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); 920bfd7f386SKyungmin Park if (ecc & ONENAND_ECC_2BIT_ALL) 921bfd7f386SKyungmin Park return ONENAND_BBT_READ_ERROR; 922bfd7f386SKyungmin Park } else { 923bfd7f386SKyungmin Park printk(KERN_ERR "onenand_bbt_wait: read timeout!" 924bfd7f386SKyungmin Park "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); 925bfd7f386SKyungmin Park return ONENAND_BBT_READ_FATAL_ERROR; 926bfd7f386SKyungmin Park } 927bfd7f386SKyungmin Park 928ef0921d6SKyungmin Park /* Initial bad block case: 0x2400 or 0x0400 */ 929ef0921d6SKyungmin Park if (ctrl & ONENAND_CTRL_ERROR) { 930ef0921d6SKyungmin Park printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); 931ef0921d6SKyungmin Park return ONENAND_BBT_READ_ERROR; 932ef0921d6SKyungmin Park } 933ef0921d6SKyungmin Park 934bfd7f386SKyungmin Park return 0; 935bfd7f386SKyungmin Park } 936bfd7f386SKyungmin Park 937bfd7f386SKyungmin Park /** 938bfd7f386SKyungmin Park * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan 939bfd7f386SKyungmin Park * @param mtd MTD device structure 940bfd7f386SKyungmin Park * @param from offset to read from 941bfd7f386SKyungmin Park * @param ops oob operation description structure 942bfd7f386SKyungmin Park * 943bfd7f386SKyungmin Park * OneNAND read out-of-band data from the spare area for bbt scan 944bfd7f386SKyungmin Park */ 945bfd7f386SKyungmin Park int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, 946bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 94759829cc1SJean-Christophe PLAGNIOL-VILLARD { 94859829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 94959829cc1SJean-Christophe PLAGNIOL-VILLARD int read = 0, thislen, column; 95059829cc1SJean-Christophe PLAGNIOL-VILLARD int ret = 0; 951bfd7f386SKyungmin Park size_t len = ops->ooblen; 952bfd7f386SKyungmin Park u_char *buf = ops->oobbuf; 95359829cc1SJean-Christophe PLAGNIOL-VILLARD 954ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); 95559829cc1SJean-Christophe PLAGNIOL-VILLARD 956bfd7f386SKyungmin Park /* Initialize return value */ 957bfd7f386SKyungmin Park ops->oobretlen = 0; 95859829cc1SJean-Christophe PLAGNIOL-VILLARD 95959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do not allow reads past end of device */ 96059829cc1SJean-Christophe PLAGNIOL-VILLARD if (unlikely((from + len) > mtd->size)) { 961bfd7f386SKyungmin Park printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); 962bfd7f386SKyungmin Park return ONENAND_BBT_READ_FATAL_ERROR; 96359829cc1SJean-Christophe PLAGNIOL-VILLARD } 96459829cc1SJean-Christophe PLAGNIOL-VILLARD 96559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Grab the lock and see if the device is available */ 96659829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_READING); 96759829cc1SJean-Christophe PLAGNIOL-VILLARD 96859829cc1SJean-Christophe PLAGNIOL-VILLARD column = from & (mtd->oobsize - 1); 96959829cc1SJean-Christophe PLAGNIOL-VILLARD 97059829cc1SJean-Christophe PLAGNIOL-VILLARD while (read < len) { 971bfd7f386SKyungmin Park 97259829cc1SJean-Christophe PLAGNIOL-VILLARD thislen = mtd->oobsize - column; 97359829cc1SJean-Christophe PLAGNIOL-VILLARD thislen = min_t(int, thislen, len); 97459829cc1SJean-Christophe PLAGNIOL-VILLARD 975ef0921d6SKyungmin Park this->spare_buf = buf; 97659829cc1SJean-Christophe PLAGNIOL-VILLARD this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); 97759829cc1SJean-Christophe PLAGNIOL-VILLARD 97859829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_update_bufferram(mtd, from, 0); 97959829cc1SJean-Christophe PLAGNIOL-VILLARD 980ef0921d6SKyungmin Park ret = this->bbt_wait(mtd, FL_READING); 981bfd7f386SKyungmin Park if (ret) 982bfd7f386SKyungmin Park break; 98359829cc1SJean-Christophe PLAGNIOL-VILLARD 984ef0921d6SKyungmin Park this->read_spareram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen); 98559829cc1SJean-Christophe PLAGNIOL-VILLARD read += thislen; 98659829cc1SJean-Christophe PLAGNIOL-VILLARD if (read == len) 98759829cc1SJean-Christophe PLAGNIOL-VILLARD break; 98859829cc1SJean-Christophe PLAGNIOL-VILLARD 98959829cc1SJean-Christophe PLAGNIOL-VILLARD buf += thislen; 990bfd7f386SKyungmin Park 99159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Read more? */ 99259829cc1SJean-Christophe PLAGNIOL-VILLARD if (read < len) { 993bfd7f386SKyungmin Park /* Update Page size */ 994bfd7f386SKyungmin Park from += this->writesize; 99559829cc1SJean-Christophe PLAGNIOL-VILLARD column = 0; 99659829cc1SJean-Christophe PLAGNIOL-VILLARD } 99759829cc1SJean-Christophe PLAGNIOL-VILLARD } 99859829cc1SJean-Christophe PLAGNIOL-VILLARD 99959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Deselect and wake up anyone waiting on the device */ 100059829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 100159829cc1SJean-Christophe PLAGNIOL-VILLARD 1002bfd7f386SKyungmin Park ops->oobretlen = read; 100359829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 100459829cc1SJean-Christophe PLAGNIOL-VILLARD } 100559829cc1SJean-Christophe PLAGNIOL-VILLARD 1006bfd7f386SKyungmin Park 100759829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE 100859829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1009bfd7f386SKyungmin Park * onenand_verify_oob - [GENERIC] verify the oob contents after a write 101059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 101159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buf the databuffer to verify 1012bfd7f386SKyungmin Park * @param to offset to read from 101359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1014bfd7f386SKyungmin Park static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) 101559829cc1SJean-Christophe PLAGNIOL-VILLARD { 101659829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 1017bfd7f386SKyungmin Park u_char *oob_buf = this->oob_buf; 1018bfd7f386SKyungmin Park int status, i; 101959829cc1SJean-Christophe PLAGNIOL-VILLARD 1020bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); 1021bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to, 0); 1022bfd7f386SKyungmin Park status = this->wait(mtd, FL_READING); 1023bfd7f386SKyungmin Park if (status) 1024bfd7f386SKyungmin Park return status; 1025bfd7f386SKyungmin Park 1026ef0921d6SKyungmin Park this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); 1027bfd7f386SKyungmin Park for (i = 0; i < mtd->oobsize; i++) 1028bfd7f386SKyungmin Park if (buf[i] != 0xFF && buf[i] != oob_buf[i]) 1029bfd7f386SKyungmin Park return -EBADMSG; 1030bfd7f386SKyungmin Park 1031bfd7f386SKyungmin Park return 0; 1032bfd7f386SKyungmin Park } 1033bfd7f386SKyungmin Park 1034bfd7f386SKyungmin Park /** 1035bfd7f386SKyungmin Park * onenand_verify - [GENERIC] verify the chip contents after a write 1036bfd7f386SKyungmin Park * @param mtd MTD device structure 1037bfd7f386SKyungmin Park * @param buf the databuffer to verify 1038bfd7f386SKyungmin Park * @param addr offset to read from 1039bfd7f386SKyungmin Park * @param len number of bytes to read and compare 1040bfd7f386SKyungmin Park */ 1041bfd7f386SKyungmin Park static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) 1042bfd7f386SKyungmin Park { 1043bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 1044bfd7f386SKyungmin Park void __iomem *dataram; 1045bfd7f386SKyungmin Park int ret = 0; 1046bfd7f386SKyungmin Park int thislen, column; 1047bfd7f386SKyungmin Park 1048bfd7f386SKyungmin Park while (len != 0) { 1049bfd7f386SKyungmin Park thislen = min_t(int, this->writesize, len); 1050bfd7f386SKyungmin Park column = addr & (this->writesize - 1); 1051bfd7f386SKyungmin Park if (column + thislen > this->writesize) 1052bfd7f386SKyungmin Park thislen = this->writesize - column; 1053bfd7f386SKyungmin Park 1054bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); 1055bfd7f386SKyungmin Park 1056bfd7f386SKyungmin Park onenand_update_bufferram(mtd, addr, 0); 105759829cc1SJean-Christophe PLAGNIOL-VILLARD 105859829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_READING); 105959829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret) 106059829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 106159829cc1SJean-Christophe PLAGNIOL-VILLARD 106259829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_update_bufferram(mtd, addr, 1); 106359829cc1SJean-Christophe PLAGNIOL-VILLARD 1064bfd7f386SKyungmin Park dataram = this->base + ONENAND_DATARAM; 1065bfd7f386SKyungmin Park dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); 106659829cc1SJean-Christophe PLAGNIOL-VILLARD 1067bfd7f386SKyungmin Park if (memcmp(buf, dataram + column, thislen)) 106859829cc1SJean-Christophe PLAGNIOL-VILLARD return -EBADMSG; 106959829cc1SJean-Christophe PLAGNIOL-VILLARD 1070bfd7f386SKyungmin Park len -= thislen; 1071bfd7f386SKyungmin Park buf += thislen; 1072bfd7f386SKyungmin Park addr += thislen; 1073bfd7f386SKyungmin Park } 1074bfd7f386SKyungmin Park 107559829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 107659829cc1SJean-Christophe PLAGNIOL-VILLARD } 107759829cc1SJean-Christophe PLAGNIOL-VILLARD #else 1078bfd7f386SKyungmin Park #define onenand_verify(...) (0) 1079bfd7f386SKyungmin Park #define onenand_verify_oob(...) (0) 108059829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 108159829cc1SJean-Christophe PLAGNIOL-VILLARD 1082d438d508SKyungmin Park #define NOTALIGNED(x) ((x & (mtd->writesize - 1)) != 0) 108359829cc1SJean-Christophe PLAGNIOL-VILLARD 108459829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1085bfd7f386SKyungmin Park * onenand_fill_auto_oob - [Internal] oob auto-placement transfer 108659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 1087bfd7f386SKyungmin Park * @param oob_buf oob buffer 1088bfd7f386SKyungmin Park * @param buf source address 1089bfd7f386SKyungmin Park * @param column oob offset to write to 1090bfd7f386SKyungmin Park * @param thislen oob length to write 109159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1092bfd7f386SKyungmin Park static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, 1093bfd7f386SKyungmin Park const u_char *buf, int column, int thislen) 109459829cc1SJean-Christophe PLAGNIOL-VILLARD { 109559829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 1096bfd7f386SKyungmin Park struct nand_oobfree *free; 1097bfd7f386SKyungmin Park int writecol = column; 1098bfd7f386SKyungmin Park int writeend = column + thislen; 1099bfd7f386SKyungmin Park int lastgap = 0; 1100bfd7f386SKyungmin Park unsigned int i; 1101bfd7f386SKyungmin Park 1102bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 1103bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 1104bfd7f386SKyungmin Park if (writecol >= lastgap) 1105bfd7f386SKyungmin Park writecol += free->offset - lastgap; 1106bfd7f386SKyungmin Park if (writeend >= lastgap) 1107bfd7f386SKyungmin Park writeend += free->offset - lastgap; 1108bfd7f386SKyungmin Park lastgap = free->offset + free->length; 1109bfd7f386SKyungmin Park } 1110bfd7f386SKyungmin Park free = this->ecclayout->oobfree; 1111bfd7f386SKyungmin Park for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { 1112bfd7f386SKyungmin Park int free_end = free->offset + free->length; 1113bfd7f386SKyungmin Park if (free->offset < writeend && free_end > writecol) { 1114bfd7f386SKyungmin Park int st = max_t(int,free->offset,writecol); 1115bfd7f386SKyungmin Park int ed = min_t(int,free_end,writeend); 1116bfd7f386SKyungmin Park int n = ed - st; 1117bfd7f386SKyungmin Park memcpy(oob_buf + st, buf, n); 1118bfd7f386SKyungmin Park buf += n; 1119bfd7f386SKyungmin Park } else if (column == 0) 1120bfd7f386SKyungmin Park break; 1121bfd7f386SKyungmin Park } 1122bfd7f386SKyungmin Park return 0; 1123bfd7f386SKyungmin Park } 1124bfd7f386SKyungmin Park 1125bfd7f386SKyungmin Park /** 1126bfd7f386SKyungmin Park * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band 1127bfd7f386SKyungmin Park * @param mtd MTD device structure 1128bfd7f386SKyungmin Park * @param to offset to write to 1129bfd7f386SKyungmin Park * @param ops oob operation description structure 1130bfd7f386SKyungmin Park * 1131bfd7f386SKyungmin Park * Write main and/or oob with ECC 1132bfd7f386SKyungmin Park */ 1133bfd7f386SKyungmin Park static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, 1134bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 1135bfd7f386SKyungmin Park { 1136bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 1137bfd7f386SKyungmin Park int written = 0, column, thislen, subpage; 1138bfd7f386SKyungmin Park int oobwritten = 0, oobcolumn, thisooblen, oobsize; 1139bfd7f386SKyungmin Park size_t len = ops->len; 1140bfd7f386SKyungmin Park size_t ooblen = ops->ooblen; 1141bfd7f386SKyungmin Park const u_char *buf = ops->datbuf; 1142bfd7f386SKyungmin Park const u_char *oob = ops->oobbuf; 1143bfd7f386SKyungmin Park u_char *oobbuf; 114459829cc1SJean-Christophe PLAGNIOL-VILLARD int ret = 0; 114559829cc1SJean-Christophe PLAGNIOL-VILLARD 1146ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); 114759829cc1SJean-Christophe PLAGNIOL-VILLARD 114859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Initialize retlen, in case of early exit */ 1149bfd7f386SKyungmin Park ops->retlen = 0; 1150bfd7f386SKyungmin Park ops->oobretlen = 0; 115159829cc1SJean-Christophe PLAGNIOL-VILLARD 115259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do not allow writes past end of device */ 115359829cc1SJean-Christophe PLAGNIOL-VILLARD if (unlikely((to + len) > mtd->size)) { 1154bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); 115559829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 115659829cc1SJean-Christophe PLAGNIOL-VILLARD } 115759829cc1SJean-Christophe PLAGNIOL-VILLARD 115859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Reject writes, which are not page aligned */ 1159bfd7f386SKyungmin Park if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) { 1160bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); 116159829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 116259829cc1SJean-Christophe PLAGNIOL-VILLARD } 116359829cc1SJean-Christophe PLAGNIOL-VILLARD 1164bfd7f386SKyungmin Park if (ops->mode == MTD_OOB_AUTO) 1165bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 1166bfd7f386SKyungmin Park else 1167bfd7f386SKyungmin Park oobsize = mtd->oobsize; 1168bfd7f386SKyungmin Park 1169bfd7f386SKyungmin Park oobcolumn = to & (mtd->oobsize - 1); 1170bfd7f386SKyungmin Park 1171bfd7f386SKyungmin Park column = to & (mtd->writesize - 1); 117259829cc1SJean-Christophe PLAGNIOL-VILLARD 117359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Loop until all data write */ 117459829cc1SJean-Christophe PLAGNIOL-VILLARD while (written < len) { 1175bfd7f386SKyungmin Park u_char *wbuf = (u_char *) buf; 117659829cc1SJean-Christophe PLAGNIOL-VILLARD 1177bfd7f386SKyungmin Park thislen = min_t(int, mtd->writesize - column, len - written); 1178bfd7f386SKyungmin Park thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); 117959829cc1SJean-Christophe PLAGNIOL-VILLARD 1180bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); 1181bfd7f386SKyungmin Park 1182bfd7f386SKyungmin Park /* Partial page write */ 1183bfd7f386SKyungmin Park subpage = thislen < mtd->writesize; 1184bfd7f386SKyungmin Park if (subpage) { 1185bfd7f386SKyungmin Park memset(this->page_buf, 0xff, mtd->writesize); 1186bfd7f386SKyungmin Park memcpy(this->page_buf + column, buf, thislen); 1187bfd7f386SKyungmin Park wbuf = this->page_buf; 1188bfd7f386SKyungmin Park } 1189bfd7f386SKyungmin Park 1190ef0921d6SKyungmin Park this->write_bufferram(mtd, to, ONENAND_DATARAM, wbuf, 0, mtd->writesize); 1191bfd7f386SKyungmin Park 1192bfd7f386SKyungmin Park if (oob) { 1193bfd7f386SKyungmin Park oobbuf = this->oob_buf; 1194bfd7f386SKyungmin Park 1195bfd7f386SKyungmin Park /* We send data to spare ram with oobsize 1196bfd7f386SKyungmin Park * * to prevent byte access */ 1197bfd7f386SKyungmin Park memset(oobbuf, 0xff, mtd->oobsize); 1198bfd7f386SKyungmin Park if (ops->mode == MTD_OOB_AUTO) 1199bfd7f386SKyungmin Park onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); 1200bfd7f386SKyungmin Park else 1201bfd7f386SKyungmin Park memcpy(oobbuf + oobcolumn, oob, thisooblen); 1202bfd7f386SKyungmin Park 1203bfd7f386SKyungmin Park oobwritten += thisooblen; 1204bfd7f386SKyungmin Park oob += thisooblen; 1205bfd7f386SKyungmin Park oobcolumn = 0; 1206bfd7f386SKyungmin Park } else 1207bfd7f386SKyungmin Park oobbuf = (u_char *) ffchars; 1208bfd7f386SKyungmin Park 1209ef0921d6SKyungmin Park this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); 121059829cc1SJean-Christophe PLAGNIOL-VILLARD 1211d438d508SKyungmin Park this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); 121259829cc1SJean-Christophe PLAGNIOL-VILLARD 121359829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_WRITING); 1214bfd7f386SKyungmin Park 1215bfd7f386SKyungmin Park /* In partial page write we don't update bufferram */ 1216bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to, !ret && !subpage); 1217bfd7f386SKyungmin Park if (ONENAND_IS_2PLANE(this)) { 1218bfd7f386SKyungmin Park ONENAND_SET_BUFFERRAM1(this); 1219bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); 1220bfd7f386SKyungmin Park } 1221bfd7f386SKyungmin Park 122259829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret) { 1223bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); 1224bfd7f386SKyungmin Park break; 1225bfd7f386SKyungmin Park } 1226bfd7f386SKyungmin Park 1227bfd7f386SKyungmin Park /* Only check verify write turn on */ 1228bfd7f386SKyungmin Park ret = onenand_verify(mtd, buf, to, thislen); 1229bfd7f386SKyungmin Park if (ret) { 1230bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); 123159829cc1SJean-Christophe PLAGNIOL-VILLARD break; 123259829cc1SJean-Christophe PLAGNIOL-VILLARD } 123359829cc1SJean-Christophe PLAGNIOL-VILLARD 123459829cc1SJean-Christophe PLAGNIOL-VILLARD written += thislen; 123559829cc1SJean-Christophe PLAGNIOL-VILLARD 123659829cc1SJean-Christophe PLAGNIOL-VILLARD if (written == len) 123759829cc1SJean-Christophe PLAGNIOL-VILLARD break; 123859829cc1SJean-Christophe PLAGNIOL-VILLARD 1239bfd7f386SKyungmin Park column = 0; 124059829cc1SJean-Christophe PLAGNIOL-VILLARD to += thislen; 124159829cc1SJean-Christophe PLAGNIOL-VILLARD buf += thislen; 124259829cc1SJean-Christophe PLAGNIOL-VILLARD } 124359829cc1SJean-Christophe PLAGNIOL-VILLARD 1244bfd7f386SKyungmin Park ops->retlen = written; 124559829cc1SJean-Christophe PLAGNIOL-VILLARD 1246bfd7f386SKyungmin Park return ret; 1247bfd7f386SKyungmin Park } 1248bfd7f386SKyungmin Park 1249bfd7f386SKyungmin Park /** 1250bfd7f386SKyungmin Park * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band 1251bfd7f386SKyungmin Park * @param mtd MTD device structure 1252bfd7f386SKyungmin Park * @param to offset to write to 1253bfd7f386SKyungmin Park * @param len number of bytes to write 1254bfd7f386SKyungmin Park * @param retlen pointer to variable to store the number of written bytes 1255bfd7f386SKyungmin Park * @param buf the data to write 1256bfd7f386SKyungmin Park * @param mode operation mode 1257bfd7f386SKyungmin Park * 1258bfd7f386SKyungmin Park * OneNAND write out-of-band 1259bfd7f386SKyungmin Park */ 1260bfd7f386SKyungmin Park static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, 1261bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 1262bfd7f386SKyungmin Park { 1263bfd7f386SKyungmin Park struct onenand_chip *this = mtd->priv; 1264bfd7f386SKyungmin Park int column, ret = 0, oobsize; 1265bfd7f386SKyungmin Park int written = 0; 1266bfd7f386SKyungmin Park u_char *oobbuf; 1267bfd7f386SKyungmin Park size_t len = ops->ooblen; 1268bfd7f386SKyungmin Park const u_char *buf = ops->oobbuf; 1269bfd7f386SKyungmin Park mtd_oob_mode_t mode = ops->mode; 1270bfd7f386SKyungmin Park 1271bfd7f386SKyungmin Park to += ops->ooboffs; 1272bfd7f386SKyungmin Park 1273ef0921d6SKyungmin Park MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); 1274bfd7f386SKyungmin Park 1275bfd7f386SKyungmin Park /* Initialize retlen, in case of early exit */ 1276bfd7f386SKyungmin Park ops->oobretlen = 0; 1277bfd7f386SKyungmin Park 1278bfd7f386SKyungmin Park if (mode == MTD_OOB_AUTO) 1279bfd7f386SKyungmin Park oobsize = this->ecclayout->oobavail; 1280bfd7f386SKyungmin Park else 1281bfd7f386SKyungmin Park oobsize = mtd->oobsize; 1282bfd7f386SKyungmin Park 1283bfd7f386SKyungmin Park column = to & (mtd->oobsize - 1); 1284bfd7f386SKyungmin Park 1285bfd7f386SKyungmin Park if (unlikely(column >= oobsize)) { 1286bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); 1287bfd7f386SKyungmin Park return -EINVAL; 1288bfd7f386SKyungmin Park } 1289bfd7f386SKyungmin Park 1290bfd7f386SKyungmin Park /* For compatibility with NAND: Do not allow write past end of page */ 1291bfd7f386SKyungmin Park if (unlikely(column + len > oobsize)) { 1292bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: " 1293bfd7f386SKyungmin Park "Attempt to write past end of page\n"); 1294bfd7f386SKyungmin Park return -EINVAL; 1295bfd7f386SKyungmin Park } 1296bfd7f386SKyungmin Park 1297bfd7f386SKyungmin Park /* Do not allow reads past end of device */ 1298bfd7f386SKyungmin Park if (unlikely(to >= mtd->size || 1299bfd7f386SKyungmin Park column + len > ((mtd->size >> this->page_shift) - 1300bfd7f386SKyungmin Park (to >> this->page_shift)) * oobsize)) { 1301bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); 1302bfd7f386SKyungmin Park return -EINVAL; 1303bfd7f386SKyungmin Park } 1304bfd7f386SKyungmin Park 1305bfd7f386SKyungmin Park oobbuf = this->oob_buf; 1306bfd7f386SKyungmin Park 1307bfd7f386SKyungmin Park /* Loop until all data write */ 1308bfd7f386SKyungmin Park while (written < len) { 1309bfd7f386SKyungmin Park int thislen = min_t(int, oobsize, len - written); 1310bfd7f386SKyungmin Park 1311bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); 1312bfd7f386SKyungmin Park 1313bfd7f386SKyungmin Park /* We send data to spare ram with oobsize 1314bfd7f386SKyungmin Park * to prevent byte access */ 1315bfd7f386SKyungmin Park memset(oobbuf, 0xff, mtd->oobsize); 1316bfd7f386SKyungmin Park if (mode == MTD_OOB_AUTO) 1317bfd7f386SKyungmin Park onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); 1318bfd7f386SKyungmin Park else 1319bfd7f386SKyungmin Park memcpy(oobbuf + column, buf, thislen); 1320ef0921d6SKyungmin Park this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); 1321bfd7f386SKyungmin Park 1322bfd7f386SKyungmin Park this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); 1323bfd7f386SKyungmin Park 1324bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to, 0); 1325bfd7f386SKyungmin Park if (ONENAND_IS_2PLANE(this)) { 1326bfd7f386SKyungmin Park ONENAND_SET_BUFFERRAM1(this); 1327bfd7f386SKyungmin Park onenand_update_bufferram(mtd, to + this->writesize, 0); 1328bfd7f386SKyungmin Park } 1329bfd7f386SKyungmin Park 1330bfd7f386SKyungmin Park ret = this->wait(mtd, FL_WRITING); 1331bfd7f386SKyungmin Park if (ret) { 1332bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); 1333bfd7f386SKyungmin Park break; 1334bfd7f386SKyungmin Park } 1335bfd7f386SKyungmin Park 1336bfd7f386SKyungmin Park ret = onenand_verify_oob(mtd, oobbuf, to); 1337bfd7f386SKyungmin Park if (ret) { 1338bfd7f386SKyungmin Park printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); 1339bfd7f386SKyungmin Park break; 1340bfd7f386SKyungmin Park } 1341bfd7f386SKyungmin Park 1342bfd7f386SKyungmin Park written += thislen; 1343bfd7f386SKyungmin Park if (written == len) 1344bfd7f386SKyungmin Park break; 1345bfd7f386SKyungmin Park 1346bfd7f386SKyungmin Park to += mtd->writesize; 1347bfd7f386SKyungmin Park buf += thislen; 1348bfd7f386SKyungmin Park column = 0; 1349bfd7f386SKyungmin Park } 1350bfd7f386SKyungmin Park 1351bfd7f386SKyungmin Park ops->oobretlen = written; 135259829cc1SJean-Christophe PLAGNIOL-VILLARD 135359829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 135459829cc1SJean-Christophe PLAGNIOL-VILLARD } 135559829cc1SJean-Christophe PLAGNIOL-VILLARD 135659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 135759829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_write - [MTD Interface] compability function for onenand_write_ecc 135859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 135959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param to offset to write to 136059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param len number of bytes to write 136159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param retlen pointer to variable to store the number of written bytes 136259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param buf the data to write 136359829cc1SJean-Christophe PLAGNIOL-VILLARD * 1364bfd7f386SKyungmin Park * Write with ECC 136559829cc1SJean-Christophe PLAGNIOL-VILLARD */ 136659829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, 136759829cc1SJean-Christophe PLAGNIOL-VILLARD size_t * retlen, const u_char * buf) 136859829cc1SJean-Christophe PLAGNIOL-VILLARD { 1369bfd7f386SKyungmin Park struct mtd_oob_ops ops = { 1370bfd7f386SKyungmin Park .len = len, 1371bfd7f386SKyungmin Park .ooblen = 0, 1372bfd7f386SKyungmin Park .datbuf = (u_char *) buf, 1373bfd7f386SKyungmin Park .oobbuf = NULL, 1374bfd7f386SKyungmin Park }; 1375bfd7f386SKyungmin Park int ret; 1376bfd7f386SKyungmin Park 1377bfd7f386SKyungmin Park onenand_get_device(mtd, FL_WRITING); 1378bfd7f386SKyungmin Park ret = onenand_write_ops_nolock(mtd, to, &ops); 1379bfd7f386SKyungmin Park onenand_release_device(mtd); 1380bfd7f386SKyungmin Park 1381bfd7f386SKyungmin Park *retlen = ops.retlen; 1382bfd7f386SKyungmin Park return ret; 138359829cc1SJean-Christophe PLAGNIOL-VILLARD } 138459829cc1SJean-Christophe PLAGNIOL-VILLARD 138559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 138659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_write_oob - [MTD Interface] OneNAND write out-of-band 138759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 138859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param to offset to write to 1389bfd7f386SKyungmin Park * @param ops oob operation description structure 139059829cc1SJean-Christophe PLAGNIOL-VILLARD * 1391bfd7f386SKyungmin Park * OneNAND write main and/or out-of-band 139259829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1393bfd7f386SKyungmin Park int onenand_write_oob(struct mtd_info *mtd, loff_t to, 1394bfd7f386SKyungmin Park struct mtd_oob_ops *ops) 139559829cc1SJean-Christophe PLAGNIOL-VILLARD { 1396bfd7f386SKyungmin Park int ret; 139759829cc1SJean-Christophe PLAGNIOL-VILLARD 1398bfd7f386SKyungmin Park switch (ops->mode) { 1399bfd7f386SKyungmin Park case MTD_OOB_PLACE: 1400bfd7f386SKyungmin Park case MTD_OOB_AUTO: 1401bfd7f386SKyungmin Park break; 1402bfd7f386SKyungmin Park case MTD_OOB_RAW: 1403bfd7f386SKyungmin Park /* Not implemented yet */ 1404bfd7f386SKyungmin Park default: 140559829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 140659829cc1SJean-Christophe PLAGNIOL-VILLARD } 140759829cc1SJean-Christophe PLAGNIOL-VILLARD 140859829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_WRITING); 1409bfd7f386SKyungmin Park if (ops->datbuf) 1410bfd7f386SKyungmin Park ret = onenand_write_ops_nolock(mtd, to, ops); 1411bfd7f386SKyungmin Park else 1412bfd7f386SKyungmin Park ret = onenand_write_oob_nolock(mtd, to, ops); 141359829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 141459829cc1SJean-Christophe PLAGNIOL-VILLARD 1415bfd7f386SKyungmin Park return ret; 141659829cc1SJean-Christophe PLAGNIOL-VILLARD 141759829cc1SJean-Christophe PLAGNIOL-VILLARD } 141859829cc1SJean-Christophe PLAGNIOL-VILLARD 141959829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1420d438d508SKyungmin Park * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad 1421d438d508SKyungmin Park * @param mtd MTD device structure 1422d438d508SKyungmin Park * @param ofs offset from device start 1423d438d508SKyungmin Park * @param allowbbt 1, if its allowed to access the bbt area 1424d438d508SKyungmin Park * 1425d438d508SKyungmin Park * Check, if the block is bad, Either by reading the bad block table or 1426d438d508SKyungmin Park * calling of the scan function. 1427d438d508SKyungmin Park */ 1428d438d508SKyungmin Park static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) 1429d438d508SKyungmin Park { 1430d438d508SKyungmin Park struct onenand_chip *this = mtd->priv; 1431d438d508SKyungmin Park struct bbm_info *bbm = this->bbm; 1432d438d508SKyungmin Park 1433d438d508SKyungmin Park /* Return info from the table */ 1434d438d508SKyungmin Park return bbm->isbad_bbt(mtd, ofs, allowbbt); 1435d438d508SKyungmin Park } 1436d438d508SKyungmin Park 1437d438d508SKyungmin Park 1438d438d508SKyungmin Park /** 143959829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_erase - [MTD Interface] erase block(s) 144059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 144159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param instr erase instruction 144259829cc1SJean-Christophe PLAGNIOL-VILLARD * 144359829cc1SJean-Christophe PLAGNIOL-VILLARD * Erase one ore more blocks 144459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 144559829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) 144659829cc1SJean-Christophe PLAGNIOL-VILLARD { 144759829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 144859829cc1SJean-Christophe PLAGNIOL-VILLARD unsigned int block_size; 144959829cc1SJean-Christophe PLAGNIOL-VILLARD loff_t addr; 145059829cc1SJean-Christophe PLAGNIOL-VILLARD int len; 145159829cc1SJean-Christophe PLAGNIOL-VILLARD int ret = 0; 145259829cc1SJean-Christophe PLAGNIOL-VILLARD 1453bfd7f386SKyungmin Park MTDDEBUG (MTD_DEBUG_LEVEL3, 1454bfd7f386SKyungmin Park "onenand_erase: start = 0x%08x, len = %i\n", 145559829cc1SJean-Christophe PLAGNIOL-VILLARD (unsigned int)instr->addr, (unsigned int)instr->len); 145659829cc1SJean-Christophe PLAGNIOL-VILLARD 145759829cc1SJean-Christophe PLAGNIOL-VILLARD block_size = (1 << this->erase_shift); 145859829cc1SJean-Christophe PLAGNIOL-VILLARD 145959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Start address must align on block boundary */ 146059829cc1SJean-Christophe PLAGNIOL-VILLARD if (unlikely(instr->addr & (block_size - 1))) { 14613167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, 14623167c538SScott Wood "onenand_erase: Unaligned address\n"); 146359829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 146459829cc1SJean-Christophe PLAGNIOL-VILLARD } 146559829cc1SJean-Christophe PLAGNIOL-VILLARD 146659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Length must align on block boundary */ 146759829cc1SJean-Christophe PLAGNIOL-VILLARD if (unlikely(instr->len & (block_size - 1))) { 14683167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, 146959829cc1SJean-Christophe PLAGNIOL-VILLARD "onenand_erase: Length not block aligned\n"); 147059829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 147159829cc1SJean-Christophe PLAGNIOL-VILLARD } 147259829cc1SJean-Christophe PLAGNIOL-VILLARD 147359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do not allow erase past end of device */ 147459829cc1SJean-Christophe PLAGNIOL-VILLARD if (unlikely((instr->len + instr->addr) > mtd->size)) { 14753167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, 147659829cc1SJean-Christophe PLAGNIOL-VILLARD "onenand_erase: Erase past end of device\n"); 147759829cc1SJean-Christophe PLAGNIOL-VILLARD return -EINVAL; 147859829cc1SJean-Christophe PLAGNIOL-VILLARD } 147959829cc1SJean-Christophe PLAGNIOL-VILLARD 148059829cc1SJean-Christophe PLAGNIOL-VILLARD instr->fail_addr = 0xffffffff; 148159829cc1SJean-Christophe PLAGNIOL-VILLARD 148259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Grab the lock and see if the device is available */ 148359829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_ERASING); 148459829cc1SJean-Christophe PLAGNIOL-VILLARD 148559829cc1SJean-Christophe PLAGNIOL-VILLARD /* Loop throught the pages */ 148659829cc1SJean-Christophe PLAGNIOL-VILLARD len = instr->len; 148759829cc1SJean-Christophe PLAGNIOL-VILLARD addr = instr->addr; 148859829cc1SJean-Christophe PLAGNIOL-VILLARD 148959829cc1SJean-Christophe PLAGNIOL-VILLARD instr->state = MTD_ERASING; 149059829cc1SJean-Christophe PLAGNIOL-VILLARD 149159829cc1SJean-Christophe PLAGNIOL-VILLARD while (len) { 149259829cc1SJean-Christophe PLAGNIOL-VILLARD 1493ef0921d6SKyungmin Park /* Check if we have a bad block, we do not erase bad blocks */ 1494ef0921d6SKyungmin Park if (instr->priv == 0 && onenand_block_isbad_nolock(mtd, addr, 0)) { 1495ef0921d6SKyungmin Park printk(KERN_WARNING "onenand_erase: attempt to erase" 1496ef0921d6SKyungmin Park " a bad block at addr 0x%08x\n", 1497ef0921d6SKyungmin Park (unsigned int) addr); 1498ef0921d6SKyungmin Park instr->state = MTD_ERASE_FAILED; 1499ef0921d6SKyungmin Park goto erase_exit; 1500ef0921d6SKyungmin Park } 150159829cc1SJean-Christophe PLAGNIOL-VILLARD 150259829cc1SJean-Christophe PLAGNIOL-VILLARD this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); 150359829cc1SJean-Christophe PLAGNIOL-VILLARD 1504d438d508SKyungmin Park onenand_invalidate_bufferram(mtd, addr, block_size); 1505d438d508SKyungmin Park 150659829cc1SJean-Christophe PLAGNIOL-VILLARD ret = this->wait(mtd, FL_ERASING); 150759829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check, if it is write protected */ 150859829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret) { 150959829cc1SJean-Christophe PLAGNIOL-VILLARD if (ret == -EPERM) 15103167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " 15113167c538SScott Wood "Device is write protected!!!\n"); 151259829cc1SJean-Christophe PLAGNIOL-VILLARD else 15133167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " 15143167c538SScott Wood "Failed erase, block %d\n", 151559829cc1SJean-Christophe PLAGNIOL-VILLARD (unsigned)(addr >> this->erase_shift)); 1516ef0921d6SKyungmin Park if (ret == -EPERM) 1517ef0921d6SKyungmin Park printk("onenand_erase: " 1518ef0921d6SKyungmin Park "Device is write protected!!!\n"); 1519ef0921d6SKyungmin Park else 1520ef0921d6SKyungmin Park printk("onenand_erase: " 1521ef0921d6SKyungmin Park "Failed erase, block %d\n", 1522ef0921d6SKyungmin Park (unsigned)(addr >> this->erase_shift)); 152359829cc1SJean-Christophe PLAGNIOL-VILLARD instr->state = MTD_ERASE_FAILED; 152459829cc1SJean-Christophe PLAGNIOL-VILLARD instr->fail_addr = addr; 1525ef0921d6SKyungmin Park 152659829cc1SJean-Christophe PLAGNIOL-VILLARD goto erase_exit; 152759829cc1SJean-Christophe PLAGNIOL-VILLARD } 152859829cc1SJean-Christophe PLAGNIOL-VILLARD 152959829cc1SJean-Christophe PLAGNIOL-VILLARD len -= block_size; 153059829cc1SJean-Christophe PLAGNIOL-VILLARD addr += block_size; 153159829cc1SJean-Christophe PLAGNIOL-VILLARD } 153259829cc1SJean-Christophe PLAGNIOL-VILLARD 153359829cc1SJean-Christophe PLAGNIOL-VILLARD instr->state = MTD_ERASE_DONE; 153459829cc1SJean-Christophe PLAGNIOL-VILLARD 153559829cc1SJean-Christophe PLAGNIOL-VILLARD erase_exit: 153659829cc1SJean-Christophe PLAGNIOL-VILLARD 153759829cc1SJean-Christophe PLAGNIOL-VILLARD ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; 153859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Do call back function */ 153959829cc1SJean-Christophe PLAGNIOL-VILLARD if (!ret) 154059829cc1SJean-Christophe PLAGNIOL-VILLARD mtd_erase_callback(instr); 154159829cc1SJean-Christophe PLAGNIOL-VILLARD 154259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Deselect and wake up anyone waiting on the device */ 154359829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 154459829cc1SJean-Christophe PLAGNIOL-VILLARD 154559829cc1SJean-Christophe PLAGNIOL-VILLARD return ret; 154659829cc1SJean-Christophe PLAGNIOL-VILLARD } 154759829cc1SJean-Christophe PLAGNIOL-VILLARD 154859829cc1SJean-Christophe PLAGNIOL-VILLARD /** 154959829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_sync - [MTD Interface] sync 155059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 155159829cc1SJean-Christophe PLAGNIOL-VILLARD * 155259829cc1SJean-Christophe PLAGNIOL-VILLARD * Sync is actually a wait for chip ready function 155359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 155459829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_sync(struct mtd_info *mtd) 155559829cc1SJean-Christophe PLAGNIOL-VILLARD { 15563167c538SScott Wood MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_sync: called\n"); 155759829cc1SJean-Christophe PLAGNIOL-VILLARD 155859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Grab the lock and see if the device is available */ 155959829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_get_device(mtd, FL_SYNCING); 156059829cc1SJean-Christophe PLAGNIOL-VILLARD 156159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Release it and go back */ 156259829cc1SJean-Christophe PLAGNIOL-VILLARD onenand_release_device(mtd); 156359829cc1SJean-Christophe PLAGNIOL-VILLARD } 156459829cc1SJean-Christophe PLAGNIOL-VILLARD 156559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 156659829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad 156759829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 156859829cc1SJean-Christophe PLAGNIOL-VILLARD * @param ofs offset relative to mtd start 1569d438d508SKyungmin Park * 1570d438d508SKyungmin Park * Check whether the block is bad 157159829cc1SJean-Christophe PLAGNIOL-VILLARD */ 157259829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) 157359829cc1SJean-Christophe PLAGNIOL-VILLARD { 1574d438d508SKyungmin Park int ret; 1575d438d508SKyungmin Park 1576d438d508SKyungmin Park /* Check for invalid offset */ 1577d438d508SKyungmin Park if (ofs > mtd->size) 1578d438d508SKyungmin Park return -EINVAL; 1579d438d508SKyungmin Park 1580d438d508SKyungmin Park onenand_get_device(mtd, FL_READING); 1581d438d508SKyungmin Park ret = onenand_block_isbad_nolock(mtd,ofs, 0); 1582d438d508SKyungmin Park onenand_release_device(mtd); 1583d438d508SKyungmin Park return ret; 158459829cc1SJean-Christophe PLAGNIOL-VILLARD } 158559829cc1SJean-Christophe PLAGNIOL-VILLARD 158659829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1587*1714f51aSKyungmin Park * onenand_default_block_markbad - [DEFAULT] mark a block bad 1588*1714f51aSKyungmin Park * @param mtd MTD device structure 1589*1714f51aSKyungmin Park * @param ofs offset from device start 1590*1714f51aSKyungmin Park * 1591*1714f51aSKyungmin Park * This is the default implementation, which can be overridden by 1592*1714f51aSKyungmin Park * a hardware specific driver. 1593*1714f51aSKyungmin Park */ 1594*1714f51aSKyungmin Park static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) 1595*1714f51aSKyungmin Park { 1596*1714f51aSKyungmin Park struct onenand_chip *this = mtd->priv; 1597*1714f51aSKyungmin Park struct bbm_info *bbm = this->bbm; 1598*1714f51aSKyungmin Park u_char buf[2] = {0, 0}; 1599*1714f51aSKyungmin Park struct mtd_oob_ops ops = { 1600*1714f51aSKyungmin Park .mode = MTD_OOB_PLACE, 1601*1714f51aSKyungmin Park .ooblen = 2, 1602*1714f51aSKyungmin Park .oobbuf = buf, 1603*1714f51aSKyungmin Park .ooboffs = 0, 1604*1714f51aSKyungmin Park }; 1605*1714f51aSKyungmin Park int block; 1606*1714f51aSKyungmin Park 1607*1714f51aSKyungmin Park /* Get block number */ 1608*1714f51aSKyungmin Park block = ((int) ofs) >> bbm->bbt_erase_shift; 1609*1714f51aSKyungmin Park if (bbm->bbt) 1610*1714f51aSKyungmin Park bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); 1611*1714f51aSKyungmin Park 1612*1714f51aSKyungmin Park /* We write two bytes, so we dont have to mess with 16 bit access */ 1613*1714f51aSKyungmin Park ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); 1614*1714f51aSKyungmin Park return onenand_write_oob_nolock(mtd, ofs, &ops); 1615*1714f51aSKyungmin Park } 1616*1714f51aSKyungmin Park 1617*1714f51aSKyungmin Park /** 161859829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad 161959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 162059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param ofs offset relative to mtd start 1621d438d508SKyungmin Park * 1622d438d508SKyungmin Park * Mark the block as bad 162359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 162459829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) 162559829cc1SJean-Christophe PLAGNIOL-VILLARD { 1626d438d508SKyungmin Park struct onenand_chip *this = mtd->priv; 1627d438d508SKyungmin Park int ret; 1628d438d508SKyungmin Park 1629d438d508SKyungmin Park ret = onenand_block_isbad(mtd, ofs); 1630d438d508SKyungmin Park if (ret) { 1631d438d508SKyungmin Park /* If it was bad already, return success and do nothing */ 1632d438d508SKyungmin Park if (ret > 0) 163359829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 1634d438d508SKyungmin Park return ret; 1635d438d508SKyungmin Park } 1636d438d508SKyungmin Park 1637d438d508SKyungmin Park ret = this->block_markbad(mtd, ofs); 1638d438d508SKyungmin Park return ret; 163959829cc1SJean-Christophe PLAGNIOL-VILLARD } 164059829cc1SJean-Christophe PLAGNIOL-VILLARD 164159829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1642ef0921d6SKyungmin Park * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) 164359829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 164459829cc1SJean-Christophe PLAGNIOL-VILLARD * @param ofs offset relative to mtd start 1645ef0921d6SKyungmin Park * @param len number of bytes to lock or unlock 1646ef0921d6SKyungmin Park * @param cmd lock or unlock command 164759829cc1SJean-Christophe PLAGNIOL-VILLARD * 1648ef0921d6SKyungmin Park * Lock or unlock one or more blocks 164959829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1650ef0921d6SKyungmin Park static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) 165159829cc1SJean-Christophe PLAGNIOL-VILLARD { 165259829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 165359829cc1SJean-Christophe PLAGNIOL-VILLARD int start, end, block, value, status; 1654ef0921d6SKyungmin Park int wp_status_mask; 165559829cc1SJean-Christophe PLAGNIOL-VILLARD 165659829cc1SJean-Christophe PLAGNIOL-VILLARD start = ofs >> this->erase_shift; 165759829cc1SJean-Christophe PLAGNIOL-VILLARD end = len >> this->erase_shift; 165859829cc1SJean-Christophe PLAGNIOL-VILLARD 1659ef0921d6SKyungmin Park if (cmd == ONENAND_CMD_LOCK) 1660ef0921d6SKyungmin Park wp_status_mask = ONENAND_WP_LS; 1661ef0921d6SKyungmin Park else 1662ef0921d6SKyungmin Park wp_status_mask = ONENAND_WP_US; 1663ef0921d6SKyungmin Park 166459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Continuous lock scheme */ 1665ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_CONT_LOCK) { 166659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set start block address */ 166759829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(start, 166859829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_BLOCK_ADDRESS); 166959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set end block address */ 167059829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(end - 1, 167159829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_END_BLOCK_ADDRESS); 167259829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write unlock command */ 1673ef0921d6SKyungmin Park this->command(mtd, cmd, 0, 0); 167459829cc1SJean-Christophe PLAGNIOL-VILLARD 167559829cc1SJean-Christophe PLAGNIOL-VILLARD /* There's no return value */ 167659829cc1SJean-Christophe PLAGNIOL-VILLARD this->wait(mtd, FL_UNLOCKING); 167759829cc1SJean-Christophe PLAGNIOL-VILLARD 167859829cc1SJean-Christophe PLAGNIOL-VILLARD /* Sanity check */ 167959829cc1SJean-Christophe PLAGNIOL-VILLARD while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) 168059829cc1SJean-Christophe PLAGNIOL-VILLARD & ONENAND_CTRL_ONGO) 168159829cc1SJean-Christophe PLAGNIOL-VILLARD continue; 168259829cc1SJean-Christophe PLAGNIOL-VILLARD 168359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check lock status */ 168459829cc1SJean-Christophe PLAGNIOL-VILLARD status = this->read_word(this->base + ONENAND_REG_WP_STATUS); 168559829cc1SJean-Christophe PLAGNIOL-VILLARD if (!(status & ONENAND_WP_US)) 168659829cc1SJean-Christophe PLAGNIOL-VILLARD printk(KERN_ERR "wp status = 0x%x\n", status); 168759829cc1SJean-Christophe PLAGNIOL-VILLARD 168859829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 168959829cc1SJean-Christophe PLAGNIOL-VILLARD } 169059829cc1SJean-Christophe PLAGNIOL-VILLARD 169159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Block lock scheme */ 1692ef0921d6SKyungmin Park for (block = start; block < start + end; block++) { 1693ef0921d6SKyungmin Park /* Set block address */ 1694ef0921d6SKyungmin Park value = onenand_block_address(this, block); 1695ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); 1696ef0921d6SKyungmin Park /* Select DataRAM for DDP */ 1697ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 1698ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); 1699ef0921d6SKyungmin Park 170059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set start block address */ 170159829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(block, 170259829cc1SJean-Christophe PLAGNIOL-VILLARD this->base + ONENAND_REG_START_BLOCK_ADDRESS); 170359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Write unlock command */ 170459829cc1SJean-Christophe PLAGNIOL-VILLARD this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); 170559829cc1SJean-Christophe PLAGNIOL-VILLARD 170659829cc1SJean-Christophe PLAGNIOL-VILLARD /* There's no return value */ 170759829cc1SJean-Christophe PLAGNIOL-VILLARD this->wait(mtd, FL_UNLOCKING); 170859829cc1SJean-Christophe PLAGNIOL-VILLARD 170959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Sanity check */ 171059829cc1SJean-Christophe PLAGNIOL-VILLARD while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) 171159829cc1SJean-Christophe PLAGNIOL-VILLARD & ONENAND_CTRL_ONGO) 171259829cc1SJean-Christophe PLAGNIOL-VILLARD continue; 171359829cc1SJean-Christophe PLAGNIOL-VILLARD 171459829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check lock status */ 171559829cc1SJean-Christophe PLAGNIOL-VILLARD status = this->read_word(this->base + ONENAND_REG_WP_STATUS); 171659829cc1SJean-Christophe PLAGNIOL-VILLARD if (!(status & ONENAND_WP_US)) 171759829cc1SJean-Christophe PLAGNIOL-VILLARD printk(KERN_ERR "block = %d, wp status = 0x%x\n", 171859829cc1SJean-Christophe PLAGNIOL-VILLARD block, status); 171959829cc1SJean-Christophe PLAGNIOL-VILLARD } 172059829cc1SJean-Christophe PLAGNIOL-VILLARD 172159829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 172259829cc1SJean-Christophe PLAGNIOL-VILLARD } 172359829cc1SJean-Christophe PLAGNIOL-VILLARD 17244fca3310SStefan Roese #ifdef ONENAND_LINUX 172559829cc1SJean-Christophe PLAGNIOL-VILLARD /** 1726ef0921d6SKyungmin Park * onenand_lock - [MTD Interface] Lock block(s) 1727ef0921d6SKyungmin Park * @param mtd MTD device structure 1728ef0921d6SKyungmin Park * @param ofs offset relative to mtd start 1729ef0921d6SKyungmin Park * @param len number of bytes to unlock 1730ef0921d6SKyungmin Park * 1731ef0921d6SKyungmin Park * Lock one or more blocks 1732ef0921d6SKyungmin Park */ 1733ef0921d6SKyungmin Park static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) 1734ef0921d6SKyungmin Park { 1735ef0921d6SKyungmin Park int ret; 1736ef0921d6SKyungmin Park 1737ef0921d6SKyungmin Park onenand_get_device(mtd, FL_LOCKING); 1738ef0921d6SKyungmin Park ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); 1739ef0921d6SKyungmin Park onenand_release_device(mtd); 1740ef0921d6SKyungmin Park return ret; 1741ef0921d6SKyungmin Park } 1742ef0921d6SKyungmin Park 1743ef0921d6SKyungmin Park /** 1744ef0921d6SKyungmin Park * onenand_unlock - [MTD Interface] Unlock block(s) 1745ef0921d6SKyungmin Park * @param mtd MTD device structure 1746ef0921d6SKyungmin Park * @param ofs offset relative to mtd start 1747ef0921d6SKyungmin Park * @param len number of bytes to unlock 1748ef0921d6SKyungmin Park * 1749ef0921d6SKyungmin Park * Unlock one or more blocks 1750ef0921d6SKyungmin Park */ 1751ef0921d6SKyungmin Park static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) 1752ef0921d6SKyungmin Park { 1753ef0921d6SKyungmin Park int ret; 1754ef0921d6SKyungmin Park 1755ef0921d6SKyungmin Park onenand_get_device(mtd, FL_LOCKING); 1756ef0921d6SKyungmin Park ret = onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); 1757ef0921d6SKyungmin Park onenand_release_device(mtd); 1758ef0921d6SKyungmin Park return ret; 1759ef0921d6SKyungmin Park } 17604fca3310SStefan Roese #endif 1761ef0921d6SKyungmin Park 1762ef0921d6SKyungmin Park /** 1763ef0921d6SKyungmin Park * onenand_check_lock_status - [OneNAND Interface] Check lock status 1764ef0921d6SKyungmin Park * @param this onenand chip data structure 1765ef0921d6SKyungmin Park * 1766ef0921d6SKyungmin Park * Check lock status 1767ef0921d6SKyungmin Park */ 1768ef0921d6SKyungmin Park static int onenand_check_lock_status(struct onenand_chip *this) 1769ef0921d6SKyungmin Park { 1770ef0921d6SKyungmin Park unsigned int value, block, status; 1771ef0921d6SKyungmin Park unsigned int end; 1772ef0921d6SKyungmin Park 1773ef0921d6SKyungmin Park end = this->chipsize >> this->erase_shift; 1774ef0921d6SKyungmin Park for (block = 0; block < end; block++) { 1775ef0921d6SKyungmin Park /* Set block address */ 1776ef0921d6SKyungmin Park value = onenand_block_address(this, block); 1777ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); 1778ef0921d6SKyungmin Park /* Select DataRAM for DDP */ 1779ef0921d6SKyungmin Park value = onenand_bufferram_address(this, block); 1780ef0921d6SKyungmin Park this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); 1781ef0921d6SKyungmin Park /* Set start block address */ 1782ef0921d6SKyungmin Park this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); 1783ef0921d6SKyungmin Park 1784ef0921d6SKyungmin Park /* Check lock status */ 1785ef0921d6SKyungmin Park status = this->read_word(this->base + ONENAND_REG_WP_STATUS); 1786ef0921d6SKyungmin Park if (!(status & ONENAND_WP_US)) { 1787ef0921d6SKyungmin Park printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); 1788ef0921d6SKyungmin Park return 0; 1789ef0921d6SKyungmin Park } 1790ef0921d6SKyungmin Park } 1791ef0921d6SKyungmin Park 1792ef0921d6SKyungmin Park return 1; 1793ef0921d6SKyungmin Park } 1794ef0921d6SKyungmin Park 1795ef0921d6SKyungmin Park /** 1796ef0921d6SKyungmin Park * onenand_unlock_all - [OneNAND Interface] unlock all blocks 1797ef0921d6SKyungmin Park * @param mtd MTD device structure 1798ef0921d6SKyungmin Park * 1799ef0921d6SKyungmin Park * Unlock all blocks 1800ef0921d6SKyungmin Park */ 1801ef0921d6SKyungmin Park static void onenand_unlock_all(struct mtd_info *mtd) 1802ef0921d6SKyungmin Park { 1803ef0921d6SKyungmin Park struct onenand_chip *this = mtd->priv; 1804ef0921d6SKyungmin Park loff_t ofs = 0; 1805ef0921d6SKyungmin Park size_t len = this->chipsize; 1806ef0921d6SKyungmin Park 1807ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_UNLOCK_ALL) { 1808ef0921d6SKyungmin Park /* Set start block address */ 1809ef0921d6SKyungmin Park this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); 1810ef0921d6SKyungmin Park /* Write unlock command */ 1811ef0921d6SKyungmin Park this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); 1812ef0921d6SKyungmin Park 1813ef0921d6SKyungmin Park /* There's no return value */ 1814ef0921d6SKyungmin Park this->wait(mtd, FL_LOCKING); 1815ef0921d6SKyungmin Park 1816ef0921d6SKyungmin Park /* Sanity check */ 1817ef0921d6SKyungmin Park while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) 1818ef0921d6SKyungmin Park & ONENAND_CTRL_ONGO) 1819ef0921d6SKyungmin Park continue; 1820ef0921d6SKyungmin Park 1821ef0921d6SKyungmin Park return; 1822ef0921d6SKyungmin Park 1823ef0921d6SKyungmin Park /* Check lock status */ 1824ef0921d6SKyungmin Park if (onenand_check_lock_status(this)) 1825ef0921d6SKyungmin Park return; 1826ef0921d6SKyungmin Park 1827ef0921d6SKyungmin Park /* Workaround for all block unlock in DDP */ 1828ef0921d6SKyungmin Park if (ONENAND_IS_DDP(this)) { 1829ef0921d6SKyungmin Park /* All blocks on another chip */ 1830ef0921d6SKyungmin Park ofs = this->chipsize >> 1; 1831ef0921d6SKyungmin Park len = this->chipsize >> 1; 1832ef0921d6SKyungmin Park } 1833ef0921d6SKyungmin Park } 1834ef0921d6SKyungmin Park 1835ef0921d6SKyungmin Park onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); 1836ef0921d6SKyungmin Park } 1837ef0921d6SKyungmin Park 1838ef0921d6SKyungmin Park 1839ef0921d6SKyungmin Park /** 1840ef0921d6SKyungmin Park * onenand_check_features - Check and set OneNAND features 1841ef0921d6SKyungmin Park * @param mtd MTD data structure 1842ef0921d6SKyungmin Park * 1843ef0921d6SKyungmin Park * Check and set OneNAND features 1844ef0921d6SKyungmin Park * - lock scheme 1845ef0921d6SKyungmin Park * - two plane 1846ef0921d6SKyungmin Park */ 1847ef0921d6SKyungmin Park static void onenand_check_features(struct mtd_info *mtd) 1848ef0921d6SKyungmin Park { 1849ef0921d6SKyungmin Park struct onenand_chip *this = mtd->priv; 1850ef0921d6SKyungmin Park unsigned int density, process; 1851ef0921d6SKyungmin Park 1852ef0921d6SKyungmin Park /* Lock scheme depends on density and process */ 1853ef0921d6SKyungmin Park density = onenand_get_density(this->device_id); 1854ef0921d6SKyungmin Park process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; 1855ef0921d6SKyungmin Park 1856ef0921d6SKyungmin Park /* Lock scheme */ 1857ef0921d6SKyungmin Park switch (density) { 1858ef0921d6SKyungmin Park case ONENAND_DEVICE_DENSITY_4Gb: 1859ef0921d6SKyungmin Park this->options |= ONENAND_HAS_2PLANE; 1860ef0921d6SKyungmin Park 1861ef0921d6SKyungmin Park case ONENAND_DEVICE_DENSITY_2Gb: 1862ef0921d6SKyungmin Park /* 2Gb DDP don't have 2 plane */ 1863ef0921d6SKyungmin Park if (!ONENAND_IS_DDP(this)) 1864ef0921d6SKyungmin Park this->options |= ONENAND_HAS_2PLANE; 1865ef0921d6SKyungmin Park this->options |= ONENAND_HAS_UNLOCK_ALL; 1866ef0921d6SKyungmin Park 1867ef0921d6SKyungmin Park case ONENAND_DEVICE_DENSITY_1Gb: 1868ef0921d6SKyungmin Park /* A-Die has all block unlock */ 1869ef0921d6SKyungmin Park if (process) 1870ef0921d6SKyungmin Park this->options |= ONENAND_HAS_UNLOCK_ALL; 1871ef0921d6SKyungmin Park break; 1872ef0921d6SKyungmin Park 1873ef0921d6SKyungmin Park default: 1874ef0921d6SKyungmin Park /* Some OneNAND has continuous lock scheme */ 1875ef0921d6SKyungmin Park if (!process) 1876ef0921d6SKyungmin Park this->options |= ONENAND_HAS_CONT_LOCK; 1877ef0921d6SKyungmin Park break; 1878ef0921d6SKyungmin Park } 1879ef0921d6SKyungmin Park 1880ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_CONT_LOCK) 1881ef0921d6SKyungmin Park printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); 1882ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_UNLOCK_ALL) 1883ef0921d6SKyungmin Park printk(KERN_DEBUG "Chip support all block unlock\n"); 1884ef0921d6SKyungmin Park if (this->options & ONENAND_HAS_2PLANE) 1885ef0921d6SKyungmin Park printk(KERN_DEBUG "Chip has 2 plane\n"); 1886ef0921d6SKyungmin Park } 1887ef0921d6SKyungmin Park 1888ef0921d6SKyungmin Park /** 188959829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_print_device_info - Print device ID 189059829cc1SJean-Christophe PLAGNIOL-VILLARD * @param device device ID 189159829cc1SJean-Christophe PLAGNIOL-VILLARD * 189259829cc1SJean-Christophe PLAGNIOL-VILLARD * Print device ID 189359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 1894ef0921d6SKyungmin Park char *onenand_print_device_info(int device, int version) 189559829cc1SJean-Christophe PLAGNIOL-VILLARD { 189659829cc1SJean-Christophe PLAGNIOL-VILLARD int vcc, demuxed, ddp, density; 1897195ccfc5SFathi BOUDRA char *dev_info = malloc(80); 1898ef0921d6SKyungmin Park char *p = dev_info; 189959829cc1SJean-Christophe PLAGNIOL-VILLARD 190059829cc1SJean-Christophe PLAGNIOL-VILLARD vcc = device & ONENAND_DEVICE_VCC_MASK; 190159829cc1SJean-Christophe PLAGNIOL-VILLARD demuxed = device & ONENAND_DEVICE_IS_DEMUX; 190259829cc1SJean-Christophe PLAGNIOL-VILLARD ddp = device & ONENAND_DEVICE_IS_DDP; 190359829cc1SJean-Christophe PLAGNIOL-VILLARD density = device >> ONENAND_DEVICE_DENSITY_SHIFT; 1904ef0921d6SKyungmin Park p += sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)", 190559829cc1SJean-Christophe PLAGNIOL-VILLARD demuxed ? "" : "Muxed ", 190659829cc1SJean-Christophe PLAGNIOL-VILLARD ddp ? "(DDP)" : "", 190759829cc1SJean-Christophe PLAGNIOL-VILLARD (16 << density), vcc ? "2.65/3.3" : "1.8", device); 1908195ccfc5SFathi BOUDRA 1909ef0921d6SKyungmin Park sprintf(p, "\nOneNAND version = 0x%04x", version); 1910ef0921d6SKyungmin Park printk("%s\n", dev_info); 1911ef0921d6SKyungmin Park 1912195ccfc5SFathi BOUDRA return dev_info; 191359829cc1SJean-Christophe PLAGNIOL-VILLARD } 191459829cc1SJean-Christophe PLAGNIOL-VILLARD 191559829cc1SJean-Christophe PLAGNIOL-VILLARD static const struct onenand_manufacturers onenand_manuf_ids[] = { 191659829cc1SJean-Christophe PLAGNIOL-VILLARD {ONENAND_MFR_SAMSUNG, "Samsung"}, 191759829cc1SJean-Christophe PLAGNIOL-VILLARD }; 191859829cc1SJean-Christophe PLAGNIOL-VILLARD 191959829cc1SJean-Christophe PLAGNIOL-VILLARD /** 192059829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_check_maf - Check manufacturer ID 192159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param manuf manufacturer ID 192259829cc1SJean-Christophe PLAGNIOL-VILLARD * 192359829cc1SJean-Christophe PLAGNIOL-VILLARD * Check manufacturer ID 192459829cc1SJean-Christophe PLAGNIOL-VILLARD */ 192559829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_check_maf(int manuf) 192659829cc1SJean-Christophe PLAGNIOL-VILLARD { 1927ef0921d6SKyungmin Park int size = ARRAY_SIZE(onenand_manuf_ids); 1928ef0921d6SKyungmin Park char *name; 192959829cc1SJean-Christophe PLAGNIOL-VILLARD int i; 193059829cc1SJean-Christophe PLAGNIOL-VILLARD 1931ef0921d6SKyungmin Park for (i = 0; size; i++) 193259829cc1SJean-Christophe PLAGNIOL-VILLARD if (manuf == onenand_manuf_ids[i].id) 193359829cc1SJean-Christophe PLAGNIOL-VILLARD break; 1934ef0921d6SKyungmin Park 1935ef0921d6SKyungmin Park if (i < size) 1936ef0921d6SKyungmin Park name = onenand_manuf_ids[i].name; 1937ef0921d6SKyungmin Park else 1938ef0921d6SKyungmin Park name = "Unknown"; 193959829cc1SJean-Christophe PLAGNIOL-VILLARD 194059829cc1SJean-Christophe PLAGNIOL-VILLARD #ifdef ONENAND_DEBUG 1941ef0921d6SKyungmin Park printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf); 194259829cc1SJean-Christophe PLAGNIOL-VILLARD #endif 194359829cc1SJean-Christophe PLAGNIOL-VILLARD 1944ef0921d6SKyungmin Park return i == size; 194559829cc1SJean-Christophe PLAGNIOL-VILLARD } 194659829cc1SJean-Christophe PLAGNIOL-VILLARD 194759829cc1SJean-Christophe PLAGNIOL-VILLARD /** 194859829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_probe - [OneNAND Interface] Probe the OneNAND device 194959829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 195059829cc1SJean-Christophe PLAGNIOL-VILLARD * 195159829cc1SJean-Christophe PLAGNIOL-VILLARD * OneNAND detection method: 195259829cc1SJean-Christophe PLAGNIOL-VILLARD * Compare the the values from command with ones from register 195359829cc1SJean-Christophe PLAGNIOL-VILLARD */ 195459829cc1SJean-Christophe PLAGNIOL-VILLARD static int onenand_probe(struct mtd_info *mtd) 195559829cc1SJean-Christophe PLAGNIOL-VILLARD { 195659829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 1957ef0921d6SKyungmin Park int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id; 195859829cc1SJean-Christophe PLAGNIOL-VILLARD int density; 1959ef0921d6SKyungmin Park int syscfg; 1960ef0921d6SKyungmin Park 1961ef0921d6SKyungmin Park /* Save system configuration 1 */ 1962ef0921d6SKyungmin Park syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); 1963ef0921d6SKyungmin Park /* Clear Sync. Burst Read mode to read BootRAM */ 1964ef0921d6SKyungmin Park this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1); 196559829cc1SJean-Christophe PLAGNIOL-VILLARD 196659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Send the command for reading device ID from BootRAM */ 196759829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); 196859829cc1SJean-Christophe PLAGNIOL-VILLARD 196959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Read manufacturer and device IDs from BootRAM */ 197059829cc1SJean-Christophe PLAGNIOL-VILLARD bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0); 197159829cc1SJean-Christophe PLAGNIOL-VILLARD bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2); 197259829cc1SJean-Christophe PLAGNIOL-VILLARD 197359829cc1SJean-Christophe PLAGNIOL-VILLARD /* Reset OneNAND to read default register values */ 197459829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM); 197559829cc1SJean-Christophe PLAGNIOL-VILLARD 1976d438d508SKyungmin Park /* Wait reset */ 1977d438d508SKyungmin Park this->wait(mtd, FL_RESETING); 197859829cc1SJean-Christophe PLAGNIOL-VILLARD 1979ef0921d6SKyungmin Park /* Restore system configuration 1 */ 1980ef0921d6SKyungmin Park this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); 1981ef0921d6SKyungmin Park 1982ef0921d6SKyungmin Park /* Check manufacturer ID */ 1983ef0921d6SKyungmin Park if (onenand_check_maf(bram_maf_id)) 1984ef0921d6SKyungmin Park return -ENXIO; 1985ef0921d6SKyungmin Park 198659829cc1SJean-Christophe PLAGNIOL-VILLARD /* Read manufacturer and device IDs from Register */ 198759829cc1SJean-Christophe PLAGNIOL-VILLARD maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); 198859829cc1SJean-Christophe PLAGNIOL-VILLARD dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); 1989ef0921d6SKyungmin Park ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); 199059829cc1SJean-Christophe PLAGNIOL-VILLARD 199159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Check OneNAND device */ 199259829cc1SJean-Christophe PLAGNIOL-VILLARD if (maf_id != bram_maf_id || dev_id != bram_dev_id) 199359829cc1SJean-Christophe PLAGNIOL-VILLARD return -ENXIO; 199459829cc1SJean-Christophe PLAGNIOL-VILLARD 19951bb707c3SKyungmin Park /* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */ 19961bb707c3SKyungmin Park if (dev_id & (1 << 9)) { 19971bb707c3SKyungmin Park printk("Not yet support Flex-OneNAND\n"); 19981bb707c3SKyungmin Park return -ENXIO; 19991bb707c3SKyungmin Park } 20001bb707c3SKyungmin Park 200159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Flash device information */ 2002ef0921d6SKyungmin Park mtd->name = onenand_print_device_info(dev_id, ver_id); 200359829cc1SJean-Christophe PLAGNIOL-VILLARD this->device_id = dev_id; 20048cf11f3aSStefan Roese this->version_id = ver_id; 200559829cc1SJean-Christophe PLAGNIOL-VILLARD 2006ef0921d6SKyungmin Park density = onenand_get_density(dev_id); 200759829cc1SJean-Christophe PLAGNIOL-VILLARD this->chipsize = (16 << density) << 20; 2008ef0921d6SKyungmin Park /* Set density mask. it is used for DDP */ 2009ef0921d6SKyungmin Park if (ONENAND_IS_DDP(this)) 2010ef0921d6SKyungmin Park this->density_mask = (1 << (density + 6)); 2011ef0921d6SKyungmin Park else 2012ef0921d6SKyungmin Park this->density_mask = 0; 201359829cc1SJean-Christophe PLAGNIOL-VILLARD 201459829cc1SJean-Christophe PLAGNIOL-VILLARD /* OneNAND page size & block size */ 201559829cc1SJean-Christophe PLAGNIOL-VILLARD /* The data buffer size is equal to page size */ 2016d438d508SKyungmin Park mtd->writesize = 201759829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); 2018d438d508SKyungmin Park mtd->oobsize = mtd->writesize >> 5; 201959829cc1SJean-Christophe PLAGNIOL-VILLARD /* Pagers per block is always 64 in OneNAND */ 2020d438d508SKyungmin Park mtd->erasesize = mtd->writesize << 6; 202159829cc1SJean-Christophe PLAGNIOL-VILLARD 202259829cc1SJean-Christophe PLAGNIOL-VILLARD this->erase_shift = ffs(mtd->erasesize) - 1; 2023d438d508SKyungmin Park this->page_shift = ffs(mtd->writesize) - 1; 202459829cc1SJean-Christophe PLAGNIOL-VILLARD this->ppb_shift = (this->erase_shift - this->page_shift); 2025d438d508SKyungmin Park this->page_mask = (mtd->erasesize / mtd->writesize) - 1; 2026bfd7f386SKyungmin Park /* It's real page size */ 2027bfd7f386SKyungmin Park this->writesize = mtd->writesize; 202859829cc1SJean-Christophe PLAGNIOL-VILLARD 202959829cc1SJean-Christophe PLAGNIOL-VILLARD /* REVIST: Multichip handling */ 203059829cc1SJean-Christophe PLAGNIOL-VILLARD 203159829cc1SJean-Christophe PLAGNIOL-VILLARD mtd->size = this->chipsize; 203259829cc1SJean-Christophe PLAGNIOL-VILLARD 2033ef0921d6SKyungmin Park /* Check OneNAND features */ 2034ef0921d6SKyungmin Park onenand_check_features(mtd); 203559829cc1SJean-Christophe PLAGNIOL-VILLARD 2036d438d508SKyungmin Park mtd->flags = MTD_CAP_NANDFLASH; 2037195ccfc5SFathi BOUDRA mtd->erase = onenand_erase; 2038195ccfc5SFathi BOUDRA mtd->read = onenand_read; 2039195ccfc5SFathi BOUDRA mtd->write = onenand_write; 2040195ccfc5SFathi BOUDRA mtd->read_oob = onenand_read_oob; 2041195ccfc5SFathi BOUDRA mtd->write_oob = onenand_write_oob; 2042195ccfc5SFathi BOUDRA mtd->sync = onenand_sync; 2043195ccfc5SFathi BOUDRA mtd->block_isbad = onenand_block_isbad; 2044195ccfc5SFathi BOUDRA mtd->block_markbad = onenand_block_markbad; 2045195ccfc5SFathi BOUDRA 204659829cc1SJean-Christophe PLAGNIOL-VILLARD return 0; 204759829cc1SJean-Christophe PLAGNIOL-VILLARD } 204859829cc1SJean-Christophe PLAGNIOL-VILLARD 204959829cc1SJean-Christophe PLAGNIOL-VILLARD /** 205059829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_scan - [OneNAND Interface] Scan for the OneNAND device 205159829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 205259829cc1SJean-Christophe PLAGNIOL-VILLARD * @param maxchips Number of chips to scan for 205359829cc1SJean-Christophe PLAGNIOL-VILLARD * 205459829cc1SJean-Christophe PLAGNIOL-VILLARD * This fills out all the not initialized function pointers 205559829cc1SJean-Christophe PLAGNIOL-VILLARD * with the defaults. 205659829cc1SJean-Christophe PLAGNIOL-VILLARD * The flash ID is read and the mtd/chip structures are 205759829cc1SJean-Christophe PLAGNIOL-VILLARD * filled with the appropriate values. 205859829cc1SJean-Christophe PLAGNIOL-VILLARD */ 205959829cc1SJean-Christophe PLAGNIOL-VILLARD int onenand_scan(struct mtd_info *mtd, int maxchips) 206059829cc1SJean-Christophe PLAGNIOL-VILLARD { 206159829cc1SJean-Christophe PLAGNIOL-VILLARD struct onenand_chip *this = mtd->priv; 206259829cc1SJean-Christophe PLAGNIOL-VILLARD 206359829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->read_word) 206459829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_word = onenand_readw; 206559829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->write_word) 206659829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_word = onenand_writew; 206759829cc1SJean-Christophe PLAGNIOL-VILLARD 206859829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->command) 206959829cc1SJean-Christophe PLAGNIOL-VILLARD this->command = onenand_command; 207059829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->wait) 207159829cc1SJean-Christophe PLAGNIOL-VILLARD this->wait = onenand_wait; 2072ef0921d6SKyungmin Park if (!this->bbt_wait) 2073ef0921d6SKyungmin Park this->bbt_wait = onenand_bbt_wait; 207459829cc1SJean-Christophe PLAGNIOL-VILLARD 207559829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->read_bufferram) 207659829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_bufferram = onenand_read_bufferram; 2077ef0921d6SKyungmin Park if (!this->read_spareram) 2078ef0921d6SKyungmin Park this->read_spareram = onenand_read_bufferram; 207959829cc1SJean-Christophe PLAGNIOL-VILLARD if (!this->write_bufferram) 208059829cc1SJean-Christophe PLAGNIOL-VILLARD this->write_bufferram = onenand_write_bufferram; 208159829cc1SJean-Christophe PLAGNIOL-VILLARD 2082*1714f51aSKyungmin Park if (!this->block_markbad) 2083*1714f51aSKyungmin Park this->block_markbad = onenand_default_block_markbad; 2084ef0921d6SKyungmin Park if (!this->scan_bbt) 2085ef0921d6SKyungmin Park this->scan_bbt = onenand_default_bbt; 2086ef0921d6SKyungmin Park 208759829cc1SJean-Christophe PLAGNIOL-VILLARD if (onenand_probe(mtd)) 208859829cc1SJean-Christophe PLAGNIOL-VILLARD return -ENXIO; 208959829cc1SJean-Christophe PLAGNIOL-VILLARD 209059829cc1SJean-Christophe PLAGNIOL-VILLARD /* Set Sync. Burst Read after probing */ 209159829cc1SJean-Christophe PLAGNIOL-VILLARD if (this->mmcontrol) { 209259829cc1SJean-Christophe PLAGNIOL-VILLARD printk(KERN_INFO "OneNAND Sync. Burst Read support\n"); 209359829cc1SJean-Christophe PLAGNIOL-VILLARD this->read_bufferram = onenand_sync_read_bufferram; 209459829cc1SJean-Christophe PLAGNIOL-VILLARD } 209559829cc1SJean-Christophe PLAGNIOL-VILLARD 2096bfd7f386SKyungmin Park /* Allocate buffers, if necessary */ 2097bfd7f386SKyungmin Park if (!this->page_buf) { 2098bfd7f386SKyungmin Park this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); 2099bfd7f386SKyungmin Park if (!this->page_buf) { 2100bfd7f386SKyungmin Park printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); 2101bfd7f386SKyungmin Park return -ENOMEM; 2102bfd7f386SKyungmin Park } 2103bfd7f386SKyungmin Park this->options |= ONENAND_PAGEBUF_ALLOC; 2104bfd7f386SKyungmin Park } 2105bfd7f386SKyungmin Park if (!this->oob_buf) { 2106bfd7f386SKyungmin Park this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); 2107bfd7f386SKyungmin Park if (!this->oob_buf) { 2108bfd7f386SKyungmin Park printk(KERN_ERR "onenand_scan: Can't allocate oob_buf\n"); 2109bfd7f386SKyungmin Park if (this->options & ONENAND_PAGEBUF_ALLOC) { 2110bfd7f386SKyungmin Park this->options &= ~ONENAND_PAGEBUF_ALLOC; 2111bfd7f386SKyungmin Park kfree(this->page_buf); 2112bfd7f386SKyungmin Park } 2113bfd7f386SKyungmin Park return -ENOMEM; 2114bfd7f386SKyungmin Park } 2115bfd7f386SKyungmin Park this->options |= ONENAND_OOBBUF_ALLOC; 2116bfd7f386SKyungmin Park } 2117bfd7f386SKyungmin Park 2118ef0921d6SKyungmin Park /* Unlock whole block */ 2119ef0921d6SKyungmin Park onenand_unlock_all(mtd); 212059829cc1SJean-Christophe PLAGNIOL-VILLARD 2121ef0921d6SKyungmin Park return this->scan_bbt(mtd); 212259829cc1SJean-Christophe PLAGNIOL-VILLARD } 212359829cc1SJean-Christophe PLAGNIOL-VILLARD 212459829cc1SJean-Christophe PLAGNIOL-VILLARD /** 212559829cc1SJean-Christophe PLAGNIOL-VILLARD * onenand_release - [OneNAND Interface] Free resources held by the OneNAND device 212659829cc1SJean-Christophe PLAGNIOL-VILLARD * @param mtd MTD device structure 212759829cc1SJean-Christophe PLAGNIOL-VILLARD */ 212859829cc1SJean-Christophe PLAGNIOL-VILLARD void onenand_release(struct mtd_info *mtd) 212959829cc1SJean-Christophe PLAGNIOL-VILLARD { 213059829cc1SJean-Christophe PLAGNIOL-VILLARD } 2131