📄 diskonchip.c
字号:
/* * drivers/mtd/nand/diskonchip.c * * (C) 2003 Red Hat, Inc. * (C) 2004 Dan Brown <dan_brown@ieee.org> * (C) 2004 Kalev Lember <kalev@smartlink.ee> * * Author: David Woodhouse <dwmw2@infradead.org> * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org> * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee> * * Error correction code lifted from the old docecc code * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de> * * Interface to generic NAND code for M-Systems DiskOnChip devices */#include <common.h>#if !defined(CONFIG_NAND_LEGACY)#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/rslib.h>#include <linux/moduleparam.h>#include <asm/io.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/doc2000.h>#include <linux/mtd/compatmac.h>#include <linux/mtd/partitions.h>#include <linux/mtd/inftl.h>/* Where to look for the devices? */#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0#endifstatic unsigned long __initdata doc_locations[] = {#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,#else /* CONFIG_MTD_DOCPROBE_HIGH */ 0xc8000, 0xca000, 0xcc000, 0xce000, 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000, 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000,#endif /* CONFIG_MTD_DOCPROBE_HIGH */#else#warning Unknown architecture for DiskOnChip. No default probe locations defined#endif 0xffffffff };static struct mtd_info *doclist = NULL;struct doc_priv { void __iomem *virtadr; unsigned long physadr; u_char ChipID; u_char CDSNControl; int chips_per_floor; /* The number of chips detected on each floor */ int curfloor; int curchip; int mh0_page; int mh1_page; struct mtd_info *nextdoc;};/* This is the syndrome computed by the HW ecc generator upon reading an empty page, one with all 0xff for data and stored ecc code. */static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };/* This is the ecc value computed by the HW ecc generator upon writing an empty page, one with all 0xff for data. */static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };#define INFTL_BBT_RESERVED_BLOCKS 4#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32)#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask);static void doc200x_select_chip(struct mtd_info *mtd, int chip);static int debug = 0;module_param(debug, int, 0);static int try_dword = 1;module_param(try_dword, int, 0);static int no_ecc_failures = 0;module_param(no_ecc_failures, int, 0);static int no_autopart = 0;module_param(no_autopart, int, 0);static int show_firmware_partition = 0;module_param(show_firmware_partition, int, 0);#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITEstatic int inftl_bbt_write = 1;#elsestatic int inftl_bbt_write = 0;#endifmodule_param(inftl_bbt_write, int, 0);static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;module_param(doc_config_location, ulong, 0);MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");/* Sector size for HW ECC */#define SECTOR_SIZE 512/* The sector bytes are packed into NB_DATA 10 bit words */#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10)/* Number of roots */#define NROOTS 4/* First consective root */#define FCR 510/* Number of symbols */#define NN 1023/* the Reed Solomon control structure */static struct rs_control *rs_decoder;/* * The HW decoder in the DoC ASIC's provides us a error syndrome, * which we must convert to a standard syndrom usable by the generic * Reed-Solomon library code. * * Fabrice Bellard figured this out in the old docecc code. I added * some comments, improved a minor bit and converted it to make use * of the generic Reed-Solomon libary. tglx */static int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc){ int i, j, nerr, errpos[8]; uint8_t parity; uint16_t ds[4], s[5], tmp, errval[8], syn[4]; /* Convert the ecc bytes into words */ ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); parity = ecc[1]; /* Initialize the syndrom buffer */ for (i = 0; i < NROOTS; i++) s[i] = ds[0]; /* * Evaluate * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] * where x = alpha^(FCR + i) */ for (j = 1; j < NROOTS; j++) { if (ds[j] == 0) continue; tmp = rs->index_of[ds[j]]; for (i = 0; i < NROOTS; i++) s[i] ^= rs->alpha_to[rs_modnn(rs, tmp + (FCR + i) * j)]; } /* Calc s[i] = s[i] / alpha^(v + i) */ for (i = 0; i < NROOTS; i++) { if (syn[i]) syn[i] = rs_modnn(rs, rs->index_of[s[i]] + (NN - FCR - i)); } /* Call the decoder library */ nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); /* Incorrectable errors ? */ if (nerr < 0) return nerr; /* * Correct the errors. The bitpositions are a bit of magic, * but they are given by the design of the de/encoder circuit * in the DoC ASIC's. */ for (i = 0; i < nerr; i++) { int index, bitpos, pos = 1015 - errpos[i]; uint8_t val; if (pos >= NB_DATA && pos < 1019) continue; if (pos < NB_DATA) { /* extract bit position (MSB first) */ pos = 10 * (NB_DATA - 1 - pos) - 6; /* now correct the following 10 bits. At most two bytes can be modified since pos is even */ index = (pos >> 3) ^ 1; bitpos = pos & 7; if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { val = (uint8_t) (errval[i] >> (2 + bitpos)); parity ^= val; if (index < SECTOR_SIZE) data[index] ^= val; } index = ((pos >> 3) + 1) ^ 1; bitpos = (bitpos + 10) & 7; if (bitpos == 0) bitpos = 8; if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { val = (uint8_t) (errval[i] << (8 - bitpos)); parity ^= val; if (index < SECTOR_SIZE) data[index] ^= val; } } } /* If the parity is wrong, no rescue possible */ return parity ? -EBADMSG : nerr;}static void DoC_Delay(struct doc_priv *doc, unsigned short cycles){ volatile char dummy; int i; for (i = 0; i < cycles; i++) { if (DoC_is_Millennium(doc)) dummy = ReadDOC(doc->virtadr, NOP); else if (DoC_is_MillenniumPlus(doc)) dummy = ReadDOC(doc->virtadr, Mplus_NOP); else dummy = ReadDOC(doc->virtadr, DOCStatus); }}#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */static int _DoC_WaitReady(struct doc_priv *doc){ void __iomem *docptr = doc->virtadr; unsigned long timeo = jiffies + (HZ * 10); if (debug) printk("_DoC_WaitReady...\n"); /* Out-of-line routine to wait for chip response */ if (DoC_is_MillenniumPlus(doc)) { while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { if (time_after(jiffies, timeo)) { printk("_DoC_WaitReady timed out.\n"); return -EIO; } udelay(1); cond_resched(); } } else { while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { if (time_after(jiffies, timeo)) { printk("_DoC_WaitReady timed out.\n"); return -EIO; } udelay(1); cond_resched(); } } return 0;}static inline int DoC_WaitReady(struct doc_priv *doc){ void __iomem *docptr = doc->virtadr; int ret = 0; if (DoC_is_MillenniumPlus(doc)) { DoC_Delay(doc, 4); if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) /* Call the out-of-line routine to wait */ ret = _DoC_WaitReady(doc); } else { DoC_Delay(doc, 4); if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) /* Call the out-of-line routine to wait */ ret = _DoC_WaitReady(doc); DoC_Delay(doc, 2); } if (debug) printk("DoC_WaitReady OK\n"); return ret;}static void doc2000_write_byte(struct mtd_info *mtd, u_char datum){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; if (debug) printk("write_byte %02x\n", datum); WriteDOC(datum, docptr, CDSNSlowIO); WriteDOC(datum, docptr, 2k_CDSN_IO);}static u_char doc2000_read_byte(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; u_char ret; ReadDOC(docptr, CDSNSlowIO); DoC_Delay(doc, 2); ret = ReadDOC(docptr, 2k_CDSN_IO); if (debug) printk("read_byte returns %02x\n", ret); return ret;}static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; if (debug) printk("writebuf of %d bytes: ", len); for (i = 0; i < len; i++) { WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); if (debug && i < 16) printk("%02x ", buf[i]); } if (debug) printk("\n");}static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; if (debug) printk("readbuf of %d bytes: ", len); for (i = 0; i < len; i++) { buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); }}static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; if (debug) printk("readbuf_dword of %d bytes: ", len); if (unlikely((((unsigned long)buf) | len) & 3)) { for (i = 0; i < len; i++) { *(uint8_t *) (&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); } } else { for (i = 0; i < len; i += 4) { *(uint32_t*) (&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); } }}static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; for (i = 0; i < len; i++) if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) return -EFAULT; return 0;}static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; uint16_t ret; doc200x_select_chip(mtd, nr); doc200x_hwcontrol(mtd, NAND_CMD_READID, NAND_CTRL_CLE | NAND_CTRL_CHANGE); doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* We cant' use dev_ready here, but at least we wait for the * command to complete */ udelay(50); ret = this->read_byte(mtd) << 8; ret |= this->read_byte(mtd); if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { /* First chip probe. See if we get same results by 32-bit access */ union { uint32_t dword; uint8_t byte[4]; } ident; void __iomem *docptr = doc->virtadr; doc200x_hwcontrol(mtd, NAND_CMD_READID, NAND_CTRL_CLE | NAND_CTRL_CHANGE); doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE); doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); udelay(50); ident.dword = readl(docptr + DoC_2k_CDSN_IO); if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); this->read_buf = &doc2000_readbuf_dword; } } return ret;}static void __init doc2000_count_chips(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; uint16_t mfrid; int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -