Loading drivers/mtd/onenand/generic.c +1 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ static int __devinit generic_onenand_probe(struct device *dev) } info->onenand.mmcontrol = pdata->mmcontrol; info->onenand.irq = platform_get_irq(pdev, 0); info->mtd.name = pdev->dev.bus_id; info->mtd.priv = &info->onenand; Loading drivers/mtd/onenand/onenand_base.c +167 −21 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> Loading Loading @@ -330,15 +331,123 @@ static int onenand_wait(struct mtd_info *mtd, int state) if (interrupt & ONENAND_INT_READ) { ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc & ONENAND_ECC_2BIT_ALL) { if (ecc) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); return -EBADMSG; if (ecc & ONENAND_ECC_2BIT_ALL) mtd->ecc_stats.failed++; else if (ecc & ONENAND_ECC_1BIT_ALL) mtd->ecc_stats.corrected++; } } return 0; } /* * onenand_interrupt - [DEFAULT] onenand interrupt handler * @param irq onenand interrupt number * @param dev_id interrupt data * * complete the work */ static irqreturn_t onenand_interrupt(int irq, void *data) { struct onenand_chip *this = (struct onenand_chip *) data; /* To handle shared interrupt */ if (!this->complete.done) complete(&this->complete); return IRQ_HANDLED; } /* * onenand_interrupt_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. */ static int onenand_interrupt_wait(struct mtd_info *mtd, int state) { struct onenand_chip *this = mtd->priv; /* To prevent soft lockup */ touch_softlockup_watchdog(); wait_for_completion(&this->complete); return onenand_wait(mtd, state); } /* * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait * @param mtd MTD device structure * @param state state to select the max. timeout value * * Try interrupt based wait (It is used one-time) */ static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state) { struct onenand_chip *this = mtd->priv; unsigned long remain, timeout; /* We use interrupt wait first */ this->wait = onenand_interrupt_wait; /* To prevent soft lockup */ touch_softlockup_watchdog(); timeout = msecs_to_jiffies(100); remain = wait_for_completion_timeout(&this->complete, timeout); if (!remain) { printk(KERN_INFO "OneNAND: There's no interrupt. " "We use the normal wait\n"); /* Release the irq */ free_irq(this->irq, this); this->wait = onenand_wait; } return onenand_wait(mtd, state); } /* * onenand_setup_wait - [OneNAND Interface] setup onenand wait method * @param mtd MTD device structure * * There's two method to wait onenand work * 1. polling - read interrupt status register * 2. interrupt - use the kernel interrupt method */ static void onenand_setup_wait(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; int syscfg; init_completion(&this->complete); if (this->irq <= 0) { this->wait = onenand_wait; return; } if (request_irq(this->irq, &onenand_interrupt, IRQF_SHARED, "onenand", this)) { /* If we can't get irq, use the normal wait */ this->wait = onenand_wait; return; } /* Enable interrupt */ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); syscfg |= ONENAND_SYS_CFG1_IOBE; this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); this->wait = onenand_try_interrupt_wait; } /** * onenand_bufferram_offset - [DEFAULT] BufferRAM offset * @param mtd MTD data structure Loading Loading @@ -609,6 +718,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; struct mtd_ecc_stats stats; int read = 0, column; int thislen; int ret = 0; Loading @@ -627,6 +737,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* TODO handling oob */ stats = mtd->ecc_stats; while (read < len) { thislen = min_t(int, mtd->writesize, len - read); Loading Loading @@ -668,7 +779,11 @@ out: * retlen == desired len and result == -EBADMSG */ *retlen = read; return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } /** Loading Loading @@ -1129,7 +1244,6 @@ static void onenand_sync(struct mtd_info *mtd) onenand_release_device(mtd); } /** * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad * @param mtd MTD device structure Loading Loading @@ -1196,32 +1310,38 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) } /** * onenand_unlock - [MTD Interface] Unlock block(s) * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * @param len number of bytes to lock or unlock * * Unlock one or more blocks * Lock or unlock one or more blocks */ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) { struct onenand_chip *this = mtd->priv; int start, end, block, value, status; int wp_status_mask; start = ofs >> this->erase_shift; end = len >> this->erase_shift; if (cmd == ONENAND_CMD_LOCK) wp_status_mask = ONENAND_WP_LS; else wp_status_mask = ONENAND_WP_US; /* Continuous lock scheme */ if (this->options & ONENAND_HAS_CONT_LOCK) { /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); /* Write lock command */ this->command(mtd, cmd, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) Loading @@ -1230,7 +1350,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & ONENAND_WP_US)) if (!(status & wp_status_mask)) printk(KERN_ERR "wp status = 0x%x\n", status); return 0; Loading @@ -1246,11 +1366,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); /* Set start block address */ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); /* Write lock command */ this->command(mtd, cmd, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) Loading @@ -1259,13 +1379,39 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & ONENAND_WP_US)) if (!(status & wp_status_mask)) printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); } return 0; } /** * onenand_lock - [MTD Interface] Lock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * * Lock one or more blocks */ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) { return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); } /** * onenand_unlock - [MTD Interface] Unlock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * * Unlock one or more blocks */ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) { return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); } /** * onenand_check_lock_status - [OneNAND Interface] Check lock status * @param this onenand chip data structure Loading Loading @@ -1310,7 +1456,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) Loading @@ -1334,7 +1480,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) return 0; } mtd->unlock(mtd, 0x0, this->chipsize); onenand_unlock(mtd, 0x0, this->chipsize); return 0; } Loading Loading @@ -1846,7 +1992,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->command) this->command = onenand_command; if (!this->wait) this->wait = onenand_wait; onenand_setup_wait(mtd); if (!this->read_bufferram) this->read_bufferram = onenand_read_bufferram; Loading Loading @@ -1922,7 +2068,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->lock_user_prot_reg = onenand_lock_user_prot_reg; #endif mtd->sync = onenand_sync; mtd->lock = NULL; mtd->lock = onenand_lock; mtd->unlock = onenand_unlock; mtd->suspend = onenand_suspend; mtd->resume = onenand_resume; Loading drivers/mtd/onenand/onenand_bbt.c +1 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); mtd->ecc_stats.badblocks++; break; } } Loading include/linux/mtd/onenand.h +4 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #define __LINUX_MTD_ONENAND_H #include <linux/spinlock.h> #include <linux/completion.h> #include <linux/mtd/onenand_regs.h> #include <linux/mtd/bbm.h> Loading @@ -33,7 +34,6 @@ typedef enum { FL_WRITING, FL_ERASING, FL_SYNCING, FL_UNLOCKING, FL_LOCKING, FL_RESETING, FL_OTPING, Loading Loading @@ -120,6 +120,9 @@ struct onenand_chip { int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); int (*scan_bbt)(struct mtd_info *mtd); struct completion complete; int irq; spinlock_t chip_lock; wait_queue_head_t wq; onenand_state_t state; Loading include/linux/mtd/onenand_regs.h +1 −0 Original line number Diff line number Diff line Loading @@ -179,6 +179,7 @@ * ECC Status Reigser FF00h (R) */ #define ONENAND_ECC_1BIT (1 << 0) #define ONENAND_ECC_1BIT_ALL (0x5555) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) Loading Loading
drivers/mtd/onenand/generic.c +1 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ static int __devinit generic_onenand_probe(struct device *dev) } info->onenand.mmcontrol = pdata->mmcontrol; info->onenand.irq = platform_get_irq(pdev, 0); info->mtd.name = pdev->dev.bus_id; info->mtd.priv = &info->onenand; Loading
drivers/mtd/onenand/onenand_base.c +167 −21 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/jiffies.h> #include <linux/mtd/mtd.h> #include <linux/mtd/onenand.h> Loading Loading @@ -330,15 +331,123 @@ static int onenand_wait(struct mtd_info *mtd, int state) if (interrupt & ONENAND_INT_READ) { ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc & ONENAND_ECC_2BIT_ALL) { if (ecc) { DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); return -EBADMSG; if (ecc & ONENAND_ECC_2BIT_ALL) mtd->ecc_stats.failed++; else if (ecc & ONENAND_ECC_1BIT_ALL) mtd->ecc_stats.corrected++; } } return 0; } /* * onenand_interrupt - [DEFAULT] onenand interrupt handler * @param irq onenand interrupt number * @param dev_id interrupt data * * complete the work */ static irqreturn_t onenand_interrupt(int irq, void *data) { struct onenand_chip *this = (struct onenand_chip *) data; /* To handle shared interrupt */ if (!this->complete.done) complete(&this->complete); return IRQ_HANDLED; } /* * onenand_interrupt_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. */ static int onenand_interrupt_wait(struct mtd_info *mtd, int state) { struct onenand_chip *this = mtd->priv; /* To prevent soft lockup */ touch_softlockup_watchdog(); wait_for_completion(&this->complete); return onenand_wait(mtd, state); } /* * onenand_try_interrupt_wait - [DEFAULT] try interrupt wait * @param mtd MTD device structure * @param state state to select the max. timeout value * * Try interrupt based wait (It is used one-time) */ static int onenand_try_interrupt_wait(struct mtd_info *mtd, int state) { struct onenand_chip *this = mtd->priv; unsigned long remain, timeout; /* We use interrupt wait first */ this->wait = onenand_interrupt_wait; /* To prevent soft lockup */ touch_softlockup_watchdog(); timeout = msecs_to_jiffies(100); remain = wait_for_completion_timeout(&this->complete, timeout); if (!remain) { printk(KERN_INFO "OneNAND: There's no interrupt. " "We use the normal wait\n"); /* Release the irq */ free_irq(this->irq, this); this->wait = onenand_wait; } return onenand_wait(mtd, state); } /* * onenand_setup_wait - [OneNAND Interface] setup onenand wait method * @param mtd MTD device structure * * There's two method to wait onenand work * 1. polling - read interrupt status register * 2. interrupt - use the kernel interrupt method */ static void onenand_setup_wait(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; int syscfg; init_completion(&this->complete); if (this->irq <= 0) { this->wait = onenand_wait; return; } if (request_irq(this->irq, &onenand_interrupt, IRQF_SHARED, "onenand", this)) { /* If we can't get irq, use the normal wait */ this->wait = onenand_wait; return; } /* Enable interrupt */ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); syscfg |= ONENAND_SYS_CFG1_IOBE; this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); this->wait = onenand_try_interrupt_wait; } /** * onenand_bufferram_offset - [DEFAULT] BufferRAM offset * @param mtd MTD data structure Loading Loading @@ -609,6 +718,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; struct mtd_ecc_stats stats; int read = 0, column; int thislen; int ret = 0; Loading @@ -627,6 +737,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* TODO handling oob */ stats = mtd->ecc_stats; while (read < len) { thislen = min_t(int, mtd->writesize, len - read); Loading Loading @@ -668,7 +779,11 @@ out: * retlen == desired len and result == -EBADMSG */ *retlen = read; return ret; if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } /** Loading Loading @@ -1129,7 +1244,6 @@ static void onenand_sync(struct mtd_info *mtd) onenand_release_device(mtd); } /** * onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad * @param mtd MTD device structure Loading Loading @@ -1196,32 +1310,38 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) } /** * onenand_unlock - [MTD Interface] Unlock block(s) * onenand_do_lock_cmd - [OneNAND Interface] Lock or unlock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * @param len number of bytes to lock or unlock * * Unlock one or more blocks * Lock or unlock one or more blocks */ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int cmd) { struct onenand_chip *this = mtd->priv; int start, end, block, value, status; int wp_status_mask; start = ofs >> this->erase_shift; end = len >> this->erase_shift; if (cmd == ONENAND_CMD_LOCK) wp_status_mask = ONENAND_WP_LS; else wp_status_mask = ONENAND_WP_US; /* Continuous lock scheme */ if (this->options & ONENAND_HAS_CONT_LOCK) { /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); /* Write lock command */ this->command(mtd, cmd, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) Loading @@ -1230,7 +1350,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & ONENAND_WP_US)) if (!(status & wp_status_mask)) printk(KERN_ERR "wp status = 0x%x\n", status); return 0; Loading @@ -1246,11 +1366,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); /* Set start block address */ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); /* Write lock command */ this->command(mtd, cmd, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) Loading @@ -1259,13 +1379,39 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) /* Check lock status */ status = this->read_word(this->base + ONENAND_REG_WP_STATUS); if (!(status & ONENAND_WP_US)) if (!(status & wp_status_mask)) printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status); } return 0; } /** * onenand_lock - [MTD Interface] Lock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * * Lock one or more blocks */ static int onenand_lock(struct mtd_info *mtd, loff_t ofs, size_t len) { return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_LOCK); } /** * onenand_unlock - [MTD Interface] Unlock block(s) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to unlock * * Unlock one or more blocks */ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) { return onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); } /** * onenand_check_lock_status - [OneNAND Interface] Check lock status * @param this onenand chip data structure Loading Loading @@ -1310,7 +1456,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); /* There's no return value */ this->wait(mtd, FL_UNLOCKING); this->wait(mtd, FL_LOCKING); /* Sanity check */ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS) Loading @@ -1334,7 +1480,7 @@ static int onenand_unlock_all(struct mtd_info *mtd) return 0; } mtd->unlock(mtd, 0x0, this->chipsize); onenand_unlock(mtd, 0x0, this->chipsize); return 0; } Loading Loading @@ -1846,7 +1992,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) if (!this->command) this->command = onenand_command; if (!this->wait) this->wait = onenand_wait; onenand_setup_wait(mtd); if (!this->read_bufferram) this->read_bufferram = onenand_read_bufferram; Loading Loading @@ -1922,7 +2068,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->lock_user_prot_reg = onenand_lock_user_prot_reg; #endif mtd->sync = onenand_sync; mtd->lock = NULL; mtd->lock = onenand_lock; mtd->unlock = onenand_unlock; mtd->suspend = onenand_suspend; mtd->resume = onenand_resume; Loading
drivers/mtd/onenand/onenand_bbt.c +1 −0 Original line number Diff line number Diff line Loading @@ -100,6 +100,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); mtd->ecc_stats.badblocks++; break; } } Loading
include/linux/mtd/onenand.h +4 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,7 @@ #define __LINUX_MTD_ONENAND_H #include <linux/spinlock.h> #include <linux/completion.h> #include <linux/mtd/onenand_regs.h> #include <linux/mtd/bbm.h> Loading @@ -33,7 +34,6 @@ typedef enum { FL_WRITING, FL_ERASING, FL_SYNCING, FL_UNLOCKING, FL_LOCKING, FL_RESETING, FL_OTPING, Loading Loading @@ -120,6 +120,9 @@ struct onenand_chip { int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); int (*scan_bbt)(struct mtd_info *mtd); struct completion complete; int irq; spinlock_t chip_lock; wait_queue_head_t wq; onenand_state_t state; Loading
include/linux/mtd/onenand_regs.h +1 −0 Original line number Diff line number Diff line Loading @@ -179,6 +179,7 @@ * ECC Status Reigser FF00h (R) */ #define ONENAND_ECC_1BIT (1 << 0) #define ONENAND_ECC_1BIT_ALL (0x5555) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) Loading