📄 nand_base.c
字号:
/* * drivers/mtd/nand.c * * Overview: * This is the generic MTD driver for NAND flash devices. It should be * capable of working with almost all NAND chips currently available. * Basic support for AG-AND chips is provided. * * Additional technical information is available on * http://www.linux-mtd.infradead.org/doc/nand.html * * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * 2002-2006 Thomas Gleixner (tglx@linutronix.de) * * Credits: * David Woodhouse for adding multichip support * * Aleph One Ltd. and Toby Churchill Ltd. for supporting the * rework for 2K page size chips * * TODO: * Enable cached programming for 2k page size chips * Check, if mtd->ecctype should be set to MTD_ECC_HW * if we have HW ecc support. * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. * BBT table is not serialized, has to be fixed * * 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/module.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nand_ecc.h>#include <linux/mtd/compatmac.h>#include <linux/interrupt.h>#include <linux/bitops.h>#include <linux/leds.h>#include <asm/io.h>#ifdef CONFIG_MTD_PARTITIONS#include <linux/mtd/partitions.h>#endif/* Define default oob placement schemes for large and small page devices */static struct nand_ecclayout nand_oob_8 = { .eccbytes = 3, .eccpos = {0, 1, 2}, .oobfree = { {.offset = 3, .length = 2}, {.offset = 6, .length = 2}}};static struct nand_ecclayout nand_oob_16 = { .eccbytes = 6, .eccpos = {0, 1, 2, 3, 6, 7}, .oobfree = { {.offset = 8, . length = 8}}};static struct nand_ecclayout nand_oob_64 = { .eccbytes = 24, .eccpos = { 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, .oobfree = { {.offset = 2, .length = 38}}};static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state);static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops);/* * For devices which display every fart in the system on a separate LED. Is * compiled away when LED support is disabled. */DEFINE_LED_TRIGGER(nand_led_trigger);/** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure * * Deselect, release chip lock and wake up anyone waiting on the device */static void nand_release_device(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; /* De-select the NAND device */ chip->select_chip(mtd, -1); /* Release the controller and the chip */ spin_lock(&chip->controller->lock); chip->controller->active = NULL; chip->state = FL_READY; wake_up(&chip->controller->wq); spin_unlock(&chip->controller->lock);}/** * nand_read_byte - [DEFAULT] read one byte from the chip * @mtd: MTD device structure * * Default read function for 8bit buswith */static uint8_t nand_read_byte(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; return readb(chip->IO_ADDR_R);}/** * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip * @mtd: MTD device structure * * Default read function for 16bit buswith with * endianess conversion */static uint8_t nand_read_byte16(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));}/** * nand_read_word - [DEFAULT] read one word from the chip * @mtd: MTD device structure * * Default read function for 16bit buswith without * endianess conversion */static u16 nand_read_word(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; return readw(chip->IO_ADDR_R);}/** * nand_select_chip - [DEFAULT] control CE line * @mtd: MTD device structure * @chipnr: chipnumber to select, -1 for deselect * * Default select function for 1 chip devices. */static void nand_select_chip(struct mtd_info *mtd, int chipnr){ struct nand_chip *chip = mtd->priv; switch (chipnr) { case -1: chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); break; case 0: break; default: BUG(); }}/** * nand_write_buf - [DEFAULT] write buffer to chip * @mtd: MTD device structure * @buf: data buffer * @len: number of bytes to write * * Default write function for 8bit buswith */static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len){ int i; struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) writeb(buf[i], chip->IO_ADDR_W);}/** * nand_read_buf - [DEFAULT] read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date * @len: number of bytes to read * * Default read function for 8bit buswith */static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len){ int i; struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) buf[i] = readb(chip->IO_ADDR_R);}/** * nand_verify_buf - [DEFAULT] Verify chip data against buffer * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare * * Default verify function for 8bit buswith */static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len){ int i; struct nand_chip *chip = mtd->priv; for (i = 0; i < len; i++) if (buf[i] != readb(chip->IO_ADDR_R)) return -EFAULT; return 0;}/** * nand_write_buf16 - [DEFAULT] write buffer to chip * @mtd: MTD device structure * @buf: data buffer * @len: number of bytes to write * * Default write function for 16bit buswith */static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len){ int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; len >>= 1; for (i = 0; i < len; i++) writew(p[i], chip->IO_ADDR_W);}/** * nand_read_buf16 - [DEFAULT] read chip data into buffer * @mtd: MTD device structure * @buf: buffer to store date * @len: number of bytes to read * * Default read function for 16bit buswith */static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len){ int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; len >>= 1; for (i = 0; i < len; i++) p[i] = readw(chip->IO_ADDR_R);}/** * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer * @mtd: MTD device structure * @buf: buffer containing the data to compare * @len: number of bytes to compare * * Default verify function for 16bit buswith */static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len){ int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; len >>= 1; for (i = 0; i < len; i++) if (p[i] != readw(chip->IO_ADDR_R)) return -EFAULT; return 0;}/** * nand_block_bad - [DEFAULT] Read bad block marker from the chip * @mtd: MTD device structure * @ofs: offset from device start * @getchip: 0, if the chip is already selected * * Check, if the block is bad. */static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip){ int page, chipnr, res = 0; struct nand_chip *chip = mtd->priv; u16 bad; page = (int)(ofs >> chip->page_shift) & chip->pagemask; if (getchip) { chipnr = (int)(ofs >> chip->chip_shift); nand_get_device(chip, mtd, FL_READING); /* Select the NAND device */ chip->select_chip(mtd, chipnr); } if (chip->options & NAND_BUSWIDTH_16) { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, page); bad = cpu_to_le16(chip->read_word(mtd)); if (chip->badblockpos & 0x1) bad >>= 8; if ((bad & 0xFF) != 0xff) res = 1; } else { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); if (chip->read_byte(mtd) != 0xff) res = 1; } if (getchip) nand_release_device(mtd); return res;}/** * nand_default_block_markbad - [DEFAULT] mark a block bad * @mtd: MTD device structure * @ofs: offset from device start * * This is the default implementation, which can be overridden by * a hardware specific driver.*/static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs){ struct nand_chip *chip = mtd->priv; uint8_t buf[2] = { 0, 0 }; int block, ret; /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); if (chip->bbt) chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* Do we have a flash based bad block table ? */ if (chip->options & NAND_USE_FLASH_BBT) ret = nand_update_bbt(mtd, ofs); else { /* We write two bytes, so we dont have to mess with 16 bit * access */ nand_get_device(chip, mtd, FL_WRITING); ofs += mtd->oobsize; chip->ops.len = chip->ops.ooblen = 2; chip->ops.datbuf = NULL; chip->ops.oobbuf = buf; chip->ops.ooboffs = chip->badblockpos & ~0x01; ret = nand_do_write_oob(mtd, ofs, &chip->ops); nand_release_device(mtd); } if (!ret) mtd->ecc_stats.badblocks++; return ret;}/** * nand_check_wp - [GENERIC] check if the chip is write protected * @mtd: MTD device structure * Check, if the device is write protected * * The function expects, that the device is already selected */static int nand_check_wp(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; /* Check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;}/** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd: MTD device structure * @ofs: offset from device start * @getchip: 0, if the chip is already selected * @allowbbt: 1, if its allowed to access the bbt area * * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt){ struct nand_chip *chip = mtd->priv; if (!chip->bbt) return chip->block_bad(mtd, ofs, getchip); /* Return info from the table */ return nand_isbad_bbt(mtd, ofs, allowbbt);}/* * Wait for the ready pin, after a command * The timeout is catched later. */void nand_wait_ready(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; unsigned long timeo = jiffies + 2; led_trigger_event(nand_led_trigger, LED_FULL); /* wait until command is processed or timeout occures */ do { if (chip->dev_ready(mtd)) break; touch_softlockup_watchdog(); } while (time_before(jiffies, timeo)); led_trigger_event(nand_led_trigger, LED_OFF);}EXPORT_SYMBOL_GPL(nand_wait_ready);/** * nand_command - [DEFAULT] Send command to NAND device * @mtd: MTD device structure * @command: the command to be sent * @column: the column address for this command, -1 if none * @page_addr: the page address for this command, -1 if none * * Send command to NAND device. This function is used for small page * devices (256/512 Bytes per page) */static void nand_command(struct mtd_info *mtd, unsigned int command, int column, int page_addr){ register struct nand_chip *chip = mtd->priv; int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE; /* * Write out the command to the device. */ if (command == NAND_CMD_SEQIN) { int readcmd; if (column >= mtd->writesize) { /* OOB area */ column -= mtd->writesize; readcmd = NAND_CMD_READOOB; } else if (column < 256) { /* First 256 bytes --> READ0 */ readcmd = NAND_CMD_READ0; } else { column -= 256; readcmd = NAND_CMD_READ1; } chip->cmd_ctrl(mtd, readcmd, ctrl); ctrl &= ~NAND_CTRL_CHANGE; } chip->cmd_ctrl(mtd, command, ctrl); /* * Address cycle, when necessary */ ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE; /* Serially input address */ if (column != -1) { /* Adjust columns for 16 bit buswidth */ if (chip->options & NAND_BUSWIDTH_16) column >>= 1; chip->cmd_ctrl(mtd, column, ctrl); ctrl &= ~NAND_CTRL_CHANGE; } if (page_addr != -1) { chip->cmd_ctrl(mtd, page_addr, ctrl); ctrl &= ~NAND_CTRL_CHANGE; chip->cmd_ctrl(mtd, page_addr >> 8, ctrl); /* One more address cycle for devices > 32MiB */ if (chip->chipsize > (32 << 20)) chip->cmd_ctrl(mtd, page_addr >> 16, ctrl); } chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* * program and erase have their own busy handlers * status and sequential in needs no delay */ switch (command) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -