diff options
author | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-11-30 10:53:47 +0000 |
---|---|---|
committer | florian <florian@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2012-11-30 10:53:47 +0000 |
commit | f0692768a51f613c06c30eb249464ff9567fbc93 (patch) | |
tree | 6967781d60eafa96b0aaa18407425f79ae663398 /target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c | |
parent | ccbddd608389f38fc9cdd2398babdf04da54126c (diff) |
[ubicom32] remove target
This target is unused, does not support any off the shelf hardware and
has been a maintenance burden for quite some time now.
Signed-off-by: Florian Fainelli <florian@openwrt.org>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@34430 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c')
-rw-r--r-- | target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c | 1017 |
1 files changed, 0 insertions, 1017 deletions
diff --git a/target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c b/target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c deleted file mode 100644 index 73938c882..000000000 --- a/target/linux/ubicom32/files/drivers/mtd/devices/nand-spi-er.c +++ /dev/null @@ -1,1017 +0,0 @@ -/* - * Micron SPI-ER NAND Flash Memory - * - * (C) Copyright 2009, Ubicom, Inc. - * - * This file is part of the Ubicom32 Linux Kernel Port. - * - * The Ubicom32 Linux Kernel Port is free software: you can redistribute - * it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 2 of the - * License, or (at your option) any later version. - * - * The Ubicom32 Linux Kernel Port is distributed in the hope that it - * will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See - * the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with the Ubicom32 Linux Kernel Port. If not, - * see <http://www.gnu.org/licenses/>. -*/ -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/mutex.h> -#include <linux/err.h> - -#include <linux/spi/spi.h> -#include <linux/spi/flash.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> - -#define NAND_SPI_ER_BLOCK_FROM_ROW(row) (row >> 6) - -#define NAND_SPI_ER_STATUS_P_FAIL (1 << 3) -#define NAND_SPI_ER_STATUS_E_FAIL (1 << 2) -#define NAND_SPI_ER_STATUS_OIP (1 << 0) - -#define NAND_SPI_ER_LAST_ROW_INVALID 0xFFFFFFFF -#define NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET 0x08 - -struct nand_spi_er_device { - const char *name; - - uint8_t id0; - uint8_t id1; - - unsigned int blocks; - unsigned int pages_per_block; - unsigned int page_size; - unsigned int write_size; - unsigned int erase_size; -}; - -struct nand_spi_er { - char name[24]; - - const struct nand_spi_er_device *device; - - struct mutex lock; - struct spi_device *spi; - - struct mtd_info mtd; - - unsigned int last_row; /* the last row we fetched */ - - /* - * Bad block table (MUST be last in strcuture) - */ - unsigned long nbb; - unsigned long bbt[0]; -}; - -const struct nand_spi_er_device nand_spi_er_devices[] = { - { - name: "MT29F1G01ZDC", - id0: 0x2C, - id1: 0x12, - blocks: 1024, - pages_per_block: 64, - page_size: 2048, - write_size: 512, - erase_size: 64 * 2048, - }, - { - name: "MT29F1G01ZDC", - id0: 0x2C, - id1: 0x13, - blocks: 1024, - pages_per_block: 64, - page_size: 2048, - write_size: 512, - erase_size: 64 * 2048, - }, -}; - -static int read_only = 0; -module_param(read_only, int, 0); -MODULE_PARM_DESC(read_only, "Leave device locked"); - -/* - * nand_spi_er_get_feature - * Get Feature register - */ -static int nand_spi_er_get_feature(struct nand_spi_er *chip, int reg, uint8_t *data) -{ - uint8_t txbuf[2]; - uint8_t rxbuf[1]; - int res; - - txbuf[0] = 0x0F; - txbuf[1] = reg; - res = spi_write_then_read(chip->spi, txbuf, 2, rxbuf, 1); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed get feature res=%d\n", chip->name, res); - return res; - } - *data = rxbuf[0]; - return 0; -} - -/* - * nand_spi_er_busywait - * Wait until the chip is not busy - */ -static int nand_spi_er_busywait(struct nand_spi_er *chip, uint8_t *data) -{ - int i; - - for (i = 0; i < 100; i++) { - int res = nand_spi_er_get_feature(chip, 0xC0, data); - if (res) { - return res; - } - if (!(*data & NAND_SPI_ER_STATUS_OIP)) { - break; - } - } - - return 0; -} - -/* - * nand_spi_er_erase - * Erase a block, parameters must be block aligned - */ -static int nand_spi_er_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct nand_spi_er *chip = mtd->priv; - struct spi_device *spi = chip->spi; - uint8_t txbuf[4]; - int res; - - DEBUG(MTD_DEBUG_LEVEL3, "%s: erase addr:%x len:%x\n", chip->name, instr->addr, instr->len); - - if ((instr->addr + instr->len) > mtd->size) { - return -EINVAL; - } - - if (instr->addr & (chip->device->erase_size - 1)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: erase address is not aligned %x\n", chip->name, instr->addr); - return -EINVAL; - } - - if (instr->len & (chip->device->erase_size - 1)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: erase len is not aligned %x\n", chip->name, instr->len); - return -EINVAL; - } - - mutex_lock(&chip->lock); - chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; - - while (instr->len) { - uint32_t block = instr->addr >> 17; - uint32_t row = block << 6; - uint8_t stat; - DEBUG(MTD_DEBUG_LEVEL3, "%s: block erase row:%x block:%x addr:%x rem:%x\n", chip->name, row, block, instr->addr, instr->len); - - /* - * Write enable - */ - txbuf[0] = 0x06; - res = spi_write(spi, txbuf, 1); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); - mutex_unlock(&chip->lock); - return res; - } - - /* - * Test for bad block - */ - if (test_bit(block, chip->bbt)) { - instr->fail_addr = block << 17; - instr->state = MTD_ERASE_FAILED; - res = -EBADMSG; - goto done; - } - - /* - * Block erase - */ - txbuf[0] = 0xD8; - txbuf[1] = 0x00; - txbuf[2] = row >> 8; - txbuf[3] = row & 0xFF; - res = spi_write(spi, txbuf, 4); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed block erase res=%d\n", chip->name, res); - instr->fail_addr = block << 17; - instr->state = MTD_ERASE_FAILED; - goto done; - } - - /* - * Wait - */ - res = nand_spi_er_busywait(chip, &stat); - if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { - instr->fail_addr = block << 17; - instr->state = MTD_ERASE_FAILED; - DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); - if (res) { - goto done; - } - - /* - * Chip is stuck? - */ - res = -EIO; - goto done; - } - - /* - * Check the status register - */ - if (stat & NAND_SPI_ER_STATUS_E_FAIL) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: E_FAIL signalled (%02x)\n", chip->name, stat); - instr->fail_addr = block << 17; - instr->state = MTD_ERASE_FAILED; - goto done; - } - - /* - * Next - */ - block++; - instr->len -= chip->device->erase_size; - instr->addr += chip->device->erase_size; - } - - instr->state = MTD_ERASE_DONE; - - mutex_unlock(&chip->lock); - return 0; - -done: - /* - * Write disable - */ - txbuf[0] = 0x04; - res = spi_write(spi, txbuf, 1); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); - } - - mutex_unlock(&chip->lock); - - mtd_erase_callback(instr); - return 0; -} - -/* - * nand_spi_er_read - * - * return -EUCLEAN: ecc error recovered - * return -EBADMSG: ecc error not recovered -*/ -static int nand_spi_er_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct nand_spi_er *chip = mtd->priv; - struct spi_device *spi = chip->spi; - - uint32_t row; - uint32_t column; - int retval = 0; - - *retlen = 0; - DEBUG(MTD_DEBUG_LEVEL2, "%s: read block from %llx len %d into %p\n", chip->name, from, len, buf); - - /* - * Zero length reads, nothing to do - */ - if (len == 0) { - return 0; - } - - /* - * Reject reads which go over the end of the flash - */ - if ((from + len) > mtd->size) { - return -EINVAL; - } - - /* - * Get the row and column address to start at - */ - row = from >> 11; - column = from & 0x7FF; - DEBUG(MTD_DEBUG_LEVEL3, "%s: row=%x %d column=%x %d last_row=%x %d\n", chip->name, row, row, column, column, chip->last_row, chip->last_row); - - /* - * Read the data from the chip - */ - mutex_lock(&chip->lock); - while (len) { - uint8_t stat; - uint8_t txbuf[4]; - struct spi_message message; - struct spi_transfer x[2]; - int res; - size_t toread; - - /* - * Figure out how much to read - * - * If we are reading from the middle of a page then the most we - * can read is to the end of the page - */ - toread = len; - if (toread > (chip->device->page_size - column)) { - toread = chip->device->page_size - column; - } - - DEBUG(MTD_DEBUG_LEVEL3, "%s: buf=%p toread=%x row=%x column=%x last_row=%x\n", chip->name, buf, toread, row, column, chip->last_row); - - if (chip->last_row != row) { - /* - * Check if the block is bad - */ - if (test_bit(NAND_SPI_ER_BLOCK_FROM_ROW(row), chip->bbt)) { - mutex_unlock(&chip->lock); - return -EBADMSG; - } - - /* - * Load the appropriate page - */ - txbuf[0] = 0x13; - txbuf[1] = 0x00; - txbuf[2] = row >> 8; - txbuf[3] = row & 0xFF; - res = spi_write(spi, txbuf, 4); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed page load res=%d\n", chip->name, res); - mutex_unlock(&chip->lock); - return res; - } - - /* - * Wait - */ - res = nand_spi_er_busywait(chip, &stat); - if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); - if (res) { - mutex_unlock(&chip->lock); - return res; - } - - /* - * Chip is stuck? - */ - mutex_unlock(&chip->lock); - return -EIO; - } - - /* - * Check the ECC bits - */ - stat >>= 4; - if (stat == 1) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: ECC recovered, row=%x\n", chip->name, row); - retval = -EUCLEAN; - } - if (stat == 2) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: failed ECC, row=%x\n", chip->name, row); - chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; - mutex_unlock(&chip->lock); - return -EBADMSG; - } - - } - - chip->last_row = row; - - /* - * Read out the data - */ - spi_message_init(&message); - memset(x, 0, sizeof(x)); - - txbuf[0] = 0x03; - txbuf[1] = column >> 8; - txbuf[2] = column & 0xFF; - txbuf[3] = 0; - x[0].tx_buf = txbuf; - x[0].len = 4; - spi_message_add_tail(&x[0], &message); - - x[1].rx_buf = buf; - x[1].len = toread; - spi_message_add_tail(&x[1], &message); - - res = spi_sync(spi, &message); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed data read res=%d\n", chip->name, res); - mutex_unlock(&chip->lock); - return res; - } - buf += toread; - len -= toread; - *retlen += toread; - - /* - * For the next page, increment the row and always start at column 0 - */ - column = 0; - row++; - } - - mutex_unlock(&chip->lock); - return retval; -} - -/* - * nand_spi_er_write - */ -#define NOT_ALIGNED(x) ((x & (device->write_size - 1)) != 0) -static int nand_spi_er_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct nand_spi_er *chip = mtd->priv; - struct spi_device *spi = chip->spi; - const struct nand_spi_er_device *device = chip->device; - uint32_t row; - uint32_t col; - uint8_t txbuf[4]; - int res; - size_t towrite; - - DEBUG(MTD_DEBUG_LEVEL2, "%s: write block to %llx len %d from %p\n", chip->name, to, len, buf); - - *retlen = 0; - - /* - * nothing to write - */ - if (!len) { - return 0; - } - - /* - * Reject writes which go over the end of the flash - */ - if ((to + len) > mtd->size) { - return -EINVAL; - } - - /* - * Check to see if everything is page aligned - */ - if (NOT_ALIGNED(to) || NOT_ALIGNED(len)) { - printk(KERN_NOTICE "nand_spi_er_write: Attempt to write non page aligned data\n"); - return -EINVAL; - } - - mutex_lock(&chip->lock); - chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; - - /* - * If the first write is a partial write then write at most the number of - * bytes to get us page aligned and then the remainder will be - * page aligned. The last bit may be a partial page as well. - */ - col = to & (device->page_size - 1); - towrite = device->page_size - col; - if (towrite > len) { - towrite = len; - } - - /* - * Write the data - */ - row = to >> 11; - while (len) { - struct spi_message message; - struct spi_transfer x[2]; - uint8_t stat; - - DEBUG(MTD_DEBUG_LEVEL3, "%s: write %p to row:%x col:%x len:%x rem:%x\n", chip->name, buf, row, col, towrite, len); - - /* - * Write enable - */ - txbuf[0] = 0x06; - res = spi_write(spi, txbuf, 1); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); - mutex_unlock(&chip->lock); - return res; - } - - /* - * Write the data into the cache - */ - spi_message_init(&message); - memset(x, 0, sizeof(x)); - txbuf[0] = 0x02; - txbuf[1] = col >> 8; - txbuf[2] = col & 0xFF; - x[0].tx_buf = txbuf; - x[0].len = 3; - spi_message_add_tail(&x[0], &message); - x[1].tx_buf = buf; - x[1].len = towrite; - spi_message_add_tail(&x[1], &message); - res = spi_sync(spi, &message); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed cache write res=%d\n", chip->name, res); - goto done; - } - - /* - * Program execute - */ - txbuf[0] = 0x10; - txbuf[1] = 0x00; - txbuf[2] = row >> 8; - txbuf[3] = row & 0xFF; - res = spi_write(spi, txbuf, 4); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed prog execute res=%d\n", chip->name, res); - goto done; - } - - /* - * Wait - */ - res = nand_spi_er_busywait(chip, &stat); - if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); - if (res) { - goto done; - } - - /* - * Chip is stuck? - */ - res = -EIO; - goto done; - } - - if (stat & (1 << 3)) { - res = -EBADMSG; - goto done; - } - - row++; - buf += towrite; - len -= towrite; - *retlen += towrite; - - /* - * At this point, we are always page aligned so start at column 0. - * Note we may not have a full page to write at the end, hence the - * check if towrite > len. - */ - col = 0; - towrite = device->page_size; - if (towrite > len) { - towrite = len; - } - } - - mutex_unlock(&chip->lock); - return res; - -done: - /* - * Write disable - */ - txbuf[0] = 0x04; - res = spi_write(spi, txbuf, 1); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); - } - - mutex_unlock(&chip->lock); - - return res; -} - -/* - * nand_spi_er_isbad - */ -static int nand_spi_er_isbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_spi_er *chip = mtd->priv; - uint32_t block; - - if (ofs & (chip->device->erase_size - 1)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); - return -EINVAL; - } - - block = ofs >> 17; - - return test_bit(block, chip->bbt); -} - -/* - * nand_spi_er_markbad - */ -static int nand_spi_er_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct nand_spi_er *chip = mtd->priv; - struct spi_device *spi = chip->spi; - uint32_t block; - uint32_t row; - uint8_t txbuf[7]; - int res; - uint8_t stat; - - if (ofs & (chip->device->erase_size - 1)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: address not aligned %llx\n", chip->name, ofs); - return -EINVAL; - } - - block = ofs >> 17; - - /* - * If it's already marked bad, no need to mark it - */ - if (test_bit(block, chip->bbt)) { - return 0; - } - - /* - * Mark it in our cache - */ - __set_bit(block, chip->bbt); - - /* - * Write the user bad block mark. If it fails, then we really - * can't do anything about it. - */ - mutex_lock(&chip->lock); - chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; - - /* - * Write enable - */ - txbuf[0] = 0x06; - res = spi_write(spi, txbuf, 1); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write enable res=%d\n", chip->name, res); - mutex_unlock(&chip->lock); - return res; - } - - /* - * Write the mark - */ - txbuf[0] = 0x84; - txbuf[1] = 0x08; - txbuf[2] = NAND_SPI_ER_BAD_BLOCK_MARK_OFFSET; - txbuf[3] = 0xde; - txbuf[4] = 0xad; - txbuf[5] = 0xbe; - txbuf[6] = 0xef; - res = spi_write(spi, txbuf, 7); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write mark res=%d\n", chip->name, res); - goto done; - } - - /* - * Program execute - */ - row = ofs >> 11; - txbuf[0] = 0x10; - txbuf[1] = 0x00; - txbuf[2] = row >> 8; - txbuf[3] = row & 0xFF; - res = spi_write(spi, txbuf, 4); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed program execute res=%d\n", chip->name, res); - goto done; - } - - /* - * Wait - */ - res = nand_spi_er_busywait(chip, &stat); - if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); - if (res) { - goto done; - } - - /* - * Chip is stuck? - */ - res = -EIO; - goto done; - } - - if (stat & (1 << 3)) { - res = -EBADMSG; - } - -done: - /* - * Write disable - */ - txbuf[0] = 0x04; - res = spi_write(spi, txbuf, 1); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed write disable res=%d\n", chip->name, res); - } - - mutex_unlock(&chip->lock); - - return res; -} - -/* - * nand_spi_er_read_bbt - */ -static int nand_spi_er_read_bbt(struct nand_spi_er *chip) -{ - int j; - for (j = 0; j < chip->device->blocks; j++) { - uint8_t txbuf[4]; - uint8_t rxbuf[16]; - uint32_t bbmark; - int res; - unsigned short row = j << 6; - uint8_t stat; - - /* - * Read Page - */ - txbuf[0] = 0x13; - txbuf[1] = 0x00; - txbuf[2] = row >> 8; - txbuf[3] = row & 0xFF; - res = spi_write(chip->spi, txbuf, 4); - if (res) { - return res; - } - - /* - * Wait - */ - res = nand_spi_er_busywait(chip, &stat); - if (res || (stat & NAND_SPI_ER_STATUS_OIP)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: chip is busy or nonresponsive res=%d stat=%02x\n", chip->name, res, stat); - if (res) { - return res; - } - - /* - * Chip is stuck? - */ - return -EIO; - } - - /* - * Check factory bad block mark - */ - txbuf[0] = 0x03; - txbuf[1] = 0x08; - txbuf[2] = 0x00; - txbuf[3] = 0x00; - res = spi_write_then_read(chip->spi, txbuf, 4, rxbuf, 16); - if (res) { - return res; - } - if (rxbuf[0] != 0xFF) { - chip->nbb++; - __set_bit(j, chip->bbt); - continue; - } - - memcpy(&bbmark, &rxbuf[8], 4); - if (bbmark == 0xdeadbeef) { - chip->nbb++; - __set_bit(j, chip->bbt); - } - } - -#if defined(CONFIG_MTD_DEBUG) && (MTD_DEBUG_LEVEL3 <= CONFIG_MTD_DEBUG_VERBOSE) - printk("%s: Bad Block Table:", chip->name); - for (j = 0; j < chip->device->blocks; j++) { - if ((j % 64) == 0) { - printk("\n%s: block %03x: ", chip->name, j); - } - printk("%c", test_bit(j, chip->bbt) ? 'X' : '.'); - } - printk("\n%s: Bad Block Numbers: ", chip->name); - for (j = 0; j < chip->device->blocks; j++) { - if (test_bit(j, chip->bbt)) { - printk("%x ", j); - } - } - printk("\n"); -#endif - - return 0; -} - -#ifndef MODULE -/* - * Called at boot time: - * - * nand_spi_er=read_only - * if read_only specified then do not unlock device - */ -static int __init nand_spi_er_setup(char *str) -{ - if (str && (strncasecmp(str, "read_only", 9) == 0)) { - read_only = 1; - } - return 0; -} - -__setup("nand_spi_er=", nand_spi_er_setup); -#endif - -/* - * nand_spi_er_probe - * Detect and initialize nand_spi_er device. - */ -static int __devinit nand_spi_er_probe(struct spi_device *spi) -{ - uint8_t txbuf[3]; - uint8_t rxbuf[2]; - int i; - int res; - size_t bbt_bytes; - struct nand_spi_er *chip; - const struct nand_spi_er_device *device; - - res = spi_setup(spi); - if (res) { - return res; - } - - /* - * Reset - */ - for (i = 0; i < 2; i++) { - txbuf[0] = 0xFF; - res = spi_write(spi, txbuf, 1); - if (res) { - return res; - } - udelay(250); - } - udelay(1000); - - /* - * Read ID - */ - txbuf[0] = 0x9F; - txbuf[1] = 0x00; - res = spi_write_then_read(spi, txbuf, 2, rxbuf, 2); - if (res) { - return res; - } - - device = nand_spi_er_devices; - for (i = 0; i < ARRAY_SIZE(nand_spi_er_devices); i++) { - if ((device->id0 == rxbuf[0]) && (device->id1 == rxbuf[1])) { - break; - } - device++; - } - if (i == ARRAY_SIZE(nand_spi_er_devices)) { - return -ENODEV; - } - - /* - * Initialize our chip structure - */ - bbt_bytes = DIV_ROUND_UP(device->blocks, BITS_PER_BYTE); - chip = kzalloc(sizeof(struct nand_spi_er) + bbt_bytes, GFP_KERNEL); - if (!chip) { - return -ENOMEM; - } - snprintf(chip->name, sizeof(chip->name), "%s.%d.%d", device->name, spi->master->bus_num, spi->chip_select); - - chip->spi = spi; - chip->device = device; - chip->last_row = NAND_SPI_ER_LAST_ROW_INVALID; - - mutex_init(&chip->lock); - - chip->mtd.type = MTD_NANDFLASH; - chip->mtd.flags = MTD_WRITEABLE; - - /* - * #blocks * block size * n blocks - */ - chip->mtd.size = device->blocks * device->pages_per_block * device->page_size; - chip->mtd.erasesize = device->erase_size; - - /* - * 1 page, optionally we can support partial write (512) - */ - chip->mtd.writesize = device->write_size; - chip->mtd.name = device->name; - chip->mtd.erase = nand_spi_er_erase; - chip->mtd.read = nand_spi_er_read; - chip->mtd.write = nand_spi_er_write; - chip->mtd.block_isbad = nand_spi_er_isbad; - chip->mtd.block_markbad = nand_spi_er_markbad; - chip->mtd.priv = chip; - - /* - * Cache the bad block table - */ - res = nand_spi_er_read_bbt(chip); - if (res) { - kfree(chip); - return res; - } - - /* - * Un/lock the chip - */ - txbuf[0] = 0x1F; - txbuf[1] = 0xA0; - if (read_only) { - txbuf[2] = 0x38; - } else { - txbuf[2] = 0x00; - } - res = spi_write(spi, txbuf, 3); - if (res) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: failed lock operation res=%d\n", chip->name, res); - mutex_unlock(&chip->lock); - return res; - } - - spi_set_drvdata(spi, chip); - - printk(KERN_INFO "%s: added device %s size: %u KBytes %u bad blocks %s\n", spi->dev.bus_id, chip->mtd.name, DIV_ROUND_UP(chip->mtd.size, 1024), chip->nbb, read_only ? "[read only]" : ""); - return add_mtd_device(&chip->mtd); -} - -/* - * nand_spi_er_remove - */ -static int __devexit nand_spi_er_remove(struct spi_device *spi) -{ - struct nand_spi_er *chip = spi_get_drvdata(spi); - int status = 0; - - DEBUG(MTD_DEBUG_LEVEL1, "%s: remove\n", spi->dev.bus_id); - status = del_mtd_device(&chip->mtd); - if (status == 0) - kfree(chip); - return status; -} - -static struct spi_driver nand_spi_er_driver = { - .driver = { - .name = "nand-spi-er", - .bus = &spi_bus_type, - .owner = THIS_MODULE, - }, - - .probe = nand_spi_er_probe, - .remove = __devexit_p(nand_spi_er_remove), - - /* FIXME: investigate suspend and resume... */ -}; - -/* - * nand_spi_er_init - */ -static int __init nand_spi_er_init(void) -{ - return spi_register_driver(&nand_spi_er_driver); -} -module_init(nand_spi_er_init); - -/* - * nand_spi_er_exit - */ -static void __exit nand_spi_er_exit(void) -{ - spi_unregister_driver(&nand_spi_er_driver); -} -module_exit(nand_spi_er_exit); - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Patrick Tjin"); -MODULE_DESCRIPTION("MTD nand_spi_er driver"); |