📄 onenand_base.c
字号:
/* * linux/drivers/mtd/onenand/onenand_base.c * * Copyright (C) 2005 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/mtd/mtd.h>#include <linux/mtd/onenand.h>#include <linux/mtd/partitions.h>#include <asm/io.h>/** * onenand_oob_64 - oob info for large (2KB) page */static struct nand_oobinfo onenand_oob_64 = { .useecc = MTD_NANDECC_AUTOPLACE, .eccbytes = 20, .eccpos = { 8, 9, 10, 11, 12, 24, 25, 26, 27, 28, 40, 41, 42, 43, 44, 56, 57, 58, 59, 60, }, .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2}, {24, 3}, {46, 2}, {40, 3}, {62, 2} }};/** * onenand_oob_32 - oob info for middle (1KB) page */static struct nand_oobinfo onenand_oob_32 = { .useecc = MTD_NANDECC_AUTOPLACE, .eccbytes = 10, .eccpos = { 8, 9, 10, 11, 12, 24, 25, 26, 27, 28, }, .oobfree = { {2, 3}, {14, 2}, {18, 3}, {30, 2} }};static const unsigned char ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 32 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */};/** * onenand_readw - [OneNAND Interface] Read OneNAND register * @param addr address to read * * Read OneNAND register */static unsigned short onenand_readw(void __iomem *addr){ return readw(addr);}/** * onenand_writew - [OneNAND Interface] Write OneNAND register with value * @param value value to write * @param addr address to write * * Write OneNAND register with value */static void onenand_writew(unsigned short value, void __iomem *addr){ writew(value, addr);}/** * onenand_block_address - [DEFAULT] Get block address * @param device the device id * @param block the block * @return translated block address if DDP, otherwise same * * Setup Start Address 1 Register (F100h) */static int onenand_block_address(int device, int block){ if (device & ONENAND_DEVICE_IS_DDP) { /* Device Flash Core select, NAND Flash Block Address */ int dfs = 0, density, mask; density = device >> ONENAND_DEVICE_DENSITY_SHIFT; mask = (1 << (density + 6)); if (block & mask) dfs = 1; return (dfs << ONENAND_DDP_SHIFT) | (block & (mask - 1)); } return block;}/** * onenand_bufferram_address - [DEFAULT] Get bufferram address * @param device the device id * @param block the block * @return set DBS value if DDP, otherwise 0 * * Setup Start Address 2 Register (F101h) for DDP */static int onenand_bufferram_address(int device, int block){ if (device & ONENAND_DEVICE_IS_DDP) { /* Device BufferRAM Select */ int dbs = 0, density, mask; density = device >> ONENAND_DEVICE_DENSITY_SHIFT; mask = (1 << (density + 6)); if (block & mask) dbs = 1; return (dbs << ONENAND_DDP_SHIFT); } return 0;}/** * onenand_page_address - [DEFAULT] Get page address * @param page the page address * @param sector the sector address * @return combined page and sector address * * Setup Start Address 8 Register (F107h) */static int onenand_page_address(int page, int sector){ /* Flash Page Address, Flash Sector Address */ int fpa, fsa; fpa = page & ONENAND_FPA_MASK; fsa = sector & ONENAND_FSA_MASK; return ((fpa << ONENAND_FPA_SHIFT) | fsa);}/** * onenand_buffer_address - [DEFAULT] Get buffer address * @param dataram1 DataRAM index * @param sectors the sector address * @param count the number of sectors * @return the start buffer value * * Setup Start Buffer Register (F200h) */static int onenand_buffer_address(int dataram1, int sectors, int count){ int bsa, bsc; /* BufferRAM Sector Address */ bsa = sectors & ONENAND_BSA_MASK; if (dataram1) bsa |= ONENAND_BSA_DATARAM1; /* DataRAM1 */ else bsa |= ONENAND_BSA_DATARAM0; /* DataRAM0 */ /* BufferRAM Sector Count */ bsc = count & ONENAND_BSC_MASK; return ((bsa << ONENAND_BSA_SHIFT) | bsc);}/** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd MTD device structure * @param cmd the command to be sent * @param addr offset to read from or write to * @param len number of bytes to read or write * * Send command to OneNAND device. This function is used for middle/large page * devices (1KB/2KB Bytes per page) */static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len){ struct onenand_chip *this = mtd->priv; int value, readcmd = 0; int block, page; /* Now we use page size operation */ int sectors = 4, count = 4; /* Address translation */ switch (cmd) { case ONENAND_CMD_UNLOCK: case ONENAND_CMD_LOCK: case ONENAND_CMD_LOCK_TIGHT: block = -1; page = -1; break; case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: block = (int) (addr >> this->erase_shift); page = -1; break; default: block = (int) (addr >> this->erase_shift); page = (int) (addr >> this->page_shift); page &= this->page_mask; break; } /* NOTE: The setting order of the registers is very important! */ if (cmd == ONENAND_CMD_BUFFERRAM) { /* Select DataRAM for DDP */ value = onenand_bufferram_address(this->device_id, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); /* Switch to the next data buffer */ ONENAND_SET_NEXT_BUFFERRAM(this); return 0; } if (block != -1) { /* Write 'DFS, FBA' of Flash */ value = onenand_block_address(this->device_id, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); } if (page != -1) { int dataram; switch (cmd) { case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: dataram = ONENAND_SET_NEXT_BUFFERRAM(this); readcmd = 1; break; default: dataram = ONENAND_CURRENT_BUFFERRAM(this); break; } /* Write 'FPA, FSA' of Flash */ value = onenand_page_address(page, sectors); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS8); /* Write 'BSA, BSC' of DataRAM */ value = onenand_buffer_address(dataram, sectors, count); this->write_word(value, this->base + ONENAND_REG_START_BUFFER); if (readcmd) { /* Select DataRAM for DDP */ value = onenand_bufferram_address(this->device_id, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); } } /* Interrupt clear */ this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); /* Write command */ this->write_word(cmd, this->base + ONENAND_REG_COMMAND); return 0;}/** * onenand_wait - [DEFAULT] wait until the command is done * @param mtd MTD device structure * @param state state to select the max. timeout value * * Wait for command done. This applies to all OneNAND command * Read can take up to 30us, erase up to 2ms and program up to 350us * according to general OneNAND specs */static int onenand_wait(struct mtd_info *mtd, int state){ struct onenand_chip * this = mtd->priv; unsigned long timeout; unsigned int flags = ONENAND_INT_MASTER; unsigned int interrupt = 0; unsigned int ctrl, ecc; /* The 20 msec is enough */ timeout = jiffies + msecs_to_jiffies(20); while (time_before(jiffies, timeout)) { interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); if (interrupt & flags) break; if (state != FL_READING) cond_resched(); } /* To get correct interrupt status in timeout case */ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (ctrl & ONENAND_CTRL_ERROR) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x", ctrl); return -EIO; } if (ctrl & ONENAND_CTRL_LOCK) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl); return -EIO; } if (interrupt & ONENAND_INT_READ) { ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc & ONENAND_ECC_2BIT_ALL) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x", ecc); return -EBADMSG; } } return 0;}/** * onenand_bufferram_offset - [DEFAULT] BufferRAM offset * @param mtd MTD data structure * @param area BufferRAM area * @return offset given area * * Return BufferRAM offset given area */static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area){ struct onenand_chip *this = mtd->priv; if (ONENAND_CURRENT_BUFFERRAM(this)) { if (area == ONENAND_DATARAM) return mtd->oobblock; if (area == ONENAND_SPARERAM) return mtd->oobsize; } return 0;}/** * onenand_read_bufferram - [OneNAND Interface] Read the bufferram area * @param mtd MTD data structure * @param area BufferRAM area * @param buffer the databuffer to put/get data * @param offset offset to read from or write to * @param count number of bytes to read/write * * Read the BufferRAM area */static int onenand_read_bufferram(struct mtd_info *mtd, int area, unsigned char *buffer, int offset, size_t count){ struct onenand_chip *this = mtd->priv; void __iomem *bufferram; bufferram = this->base + area; bufferram += onenand_bufferram_offset(mtd, area); memcpy(buffer, bufferram + offset, count); return 0;}/** * onenand_write_bufferram - [OneNAND Interface] Write the bufferram area * @param mtd MTD data structure * @param area BufferRAM area * @param buffer the databuffer to put/get data * @param offset offset to read from or write to * @param count number of bytes to read/write * * Write the BufferRAM area */static int onenand_write_bufferram(struct mtd_info *mtd, int area, const unsigned char *buffer, int offset, size_t count){ struct onenand_chip *this = mtd->priv; void __iomem *bufferram; bufferram = this->base + area; bufferram += onenand_bufferram_offset(mtd, area); memcpy(bufferram + offset, buffer, count); return 0;}/** * onenand_check_bufferram - [GENERIC] Check BufferRAM information * @param mtd MTD data structure * @param addr address to check * @return 1 if there are valid data, otherwise 0 * * Check bufferram if there is data we required */static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr){ struct onenand_chip *this = mtd->priv; int block, page; int i; block = (int) (addr >> this->erase_shift); page = (int) (addr >> this->page_shift); page &= this->page_mask; i = ONENAND_CURRENT_BUFFERRAM(this); /* Is there valid data? */ if (this->bufferram[i].block == block && this->bufferram[i].page == page && this->bufferram[i].valid) return 1; return 0;}/** * onenand_update_bufferram - [GENERIC] Update BufferRAM information * @param mtd MTD data structure * @param addr address to update * @param valid valid flag * * Update BufferRAM information */static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int valid){ struct onenand_chip *this = mtd->priv; int block, page; int i; block = (int) (addr >> this->erase_shift); page = (int) (addr >> this->page_shift); page &= this->page_mask; /* Invalidate BufferRAM */ for (i = 0; i < MAX_BUFFERRAM; i++) { if (this->bufferram[i].block == block && this->bufferram[i].page == page) this->bufferram[i].valid = 0; } /* Update BufferRAM */ i = ONENAND_CURRENT_BUFFERRAM(this); this->bufferram[i].block = block; this->bufferram[i].page = page; this->bufferram[i].valid = valid; return 0;}/** * onenand_get_device - [GENERIC] Get chip for selected access * @param mtd MTD device structure * @param new_state the state which is requested * * Get the device and lock it for exclusive access */static void onenand_get_device(struct mtd_info *mtd, int new_state){ struct onenand_chip *this = mtd->priv; DECLARE_WAITQUEUE(wait, current); /* * Grab the lock and see if the device is available */ while (1) { spin_lock(&this->chip_lock); if (this->state == FL_READY) { this->state = new_state;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -