📄 diskonchip.c
字号:
memset((char *)parts, 0, sizeof(parts)); /* On NFTL, we have to find the media headers before we can read the BBTs, since they're stored in the media header eraseblocks. */ numparts = nftl_partscan(mtd, parts); if (!numparts) return -EIO; this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | NAND_BBT_VERSION; this->bbt_td->veroffs = 7; this->bbt_td->pages[0] = doc->mh0_page + 1; if (doc->mh1_page != -1) { this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | NAND_BBT_VERSION; this->bbt_md->veroffs = 7; this->bbt_md->pages[0] = doc->mh1_page + 1; } else { this->bbt_md = NULL; } /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. At least as nand_bbt.c is currently written. */ if ((ret = nand_scan_bbt(mtd, NULL))) return ret; add_mtd_device(mtd);#ifdef CONFIG_MTD_PARTITIONS if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);#endif return 0;}static int __init inftl_scan_bbt(struct mtd_info *mtd){ int ret, numparts; struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; struct mtd_partition parts[5]; if (this->numchips > doc->chips_per_floor) { printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n"); return -EIO; } if (DoC_is_MillenniumPlus(doc)) { this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; if (inftl_bbt_write) this->bbt_td->options |= NAND_BBT_WRITE; this->bbt_td->pages[0] = 2; this->bbt_md = NULL; } else { this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; if (inftl_bbt_write) this->bbt_td->options |= NAND_BBT_WRITE; this->bbt_td->offs = 8; this->bbt_td->len = 8; this->bbt_td->veroffs = 7; this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; this->bbt_td->reserved_block_code = 0x01; this->bbt_td->pattern = "MSYS_BBT"; this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; if (inftl_bbt_write) this->bbt_md->options |= NAND_BBT_WRITE; this->bbt_md->offs = 8; this->bbt_md->len = 8; this->bbt_md->veroffs = 7; this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; this->bbt_md->reserved_block_code = 0x01; this->bbt_md->pattern = "TBB_SYSM"; } /* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set. At least as nand_bbt.c is currently written. */ if ((ret = nand_scan_bbt(mtd, NULL))) return ret; memset((char *)parts, 0, sizeof(parts)); numparts = inftl_partscan(mtd, parts); /* At least for now, require the INFTL Media Header. We could probably do without it for non-INFTL use, since all it gives us is autopartitioning, but I want to give it more thought. */ if (!numparts) return -EIO; add_mtd_device(mtd);#ifdef CONFIG_MTD_PARTITIONS if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);#endif return 0;}static inline int __init doc2000_init(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; this->read_byte = doc2000_read_byte; this->write_buf = doc2000_writebuf; this->read_buf = doc2000_readbuf; this->verify_buf = doc2000_verifybuf; this->scan_bbt = nftl_scan_bbt; doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; doc2000_count_chips(mtd); mtd->name = "DiskOnChip 2000 (NFTL Model)"; return (4 * doc->chips_per_floor);}static inline int __init doc2001_init(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; this->read_byte = doc2001_read_byte; this->write_buf = doc2001_writebuf; this->read_buf = doc2001_readbuf; this->verify_buf = doc2001_verifybuf; ReadDOC(doc->virtadr, ChipID); ReadDOC(doc->virtadr, ChipID); ReadDOC(doc->virtadr, ChipID); if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { /* It's not a Millennium; it's one of the newer DiskOnChip 2000 units with a similar ASIC. Treat it like a Millennium, except that it can have multiple chips. */ doc2000_count_chips(mtd); mtd->name = "DiskOnChip 2000 (INFTL Model)"; this->scan_bbt = inftl_scan_bbt; return (4 * doc->chips_per_floor); } else { /* Bog-standard Millennium */ doc->chips_per_floor = 1; mtd->name = "DiskOnChip Millennium"; this->scan_bbt = nftl_scan_bbt; return 1; }}static inline int __init doc2001plus_init(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; this->read_byte = doc2001plus_read_byte; this->write_buf = doc2001plus_writebuf; this->read_buf = doc2001plus_readbuf; this->verify_buf = doc2001plus_verifybuf; this->scan_bbt = inftl_scan_bbt; this->cmd_ctrl = NULL; this->select_chip = doc2001plus_select_chip; this->cmdfunc = doc2001plus_command; this->ecc.hwctl = doc2001plus_enable_hwecc; doc->chips_per_floor = 1; mtd->name = "DiskOnChip Millennium Plus"; return 1;}static int __init doc_probe(unsigned long physadr){ unsigned char ChipID; struct mtd_info *mtd; struct nand_chip *nand; struct doc_priv *doc; void __iomem *virtadr; unsigned char save_control; unsigned char tmp, tmpb, tmpc; int reg, len, numchips; int ret = 0; virtadr = ioremap(physadr, DOC_IOREMAP_LEN); if (!virtadr) { printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr); return -EIO; } /* It's not possible to cleanly detect the DiskOnChip - the * bootup procedure will put the device into reset mode, and * it's not possible to talk to it without actually writing * to the DOCControl register. So we store the current contents * of the DOCControl register's location, in case we later decide * that it's not a DiskOnChip, and want to put it back how we * found it. */ save_control = ReadDOC(virtadr, DOCControl); /* Reset the DiskOnChip ASIC */ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); /* Enable the DiskOnChip ASIC */ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); ChipID = ReadDOC(virtadr, ChipID); switch (ChipID) { case DOC_ChipID_Doc2k: reg = DoC_2k_ECCStatus; break; case DOC_ChipID_DocMil: reg = DoC_ECCConf; break; case DOC_ChipID_DocMilPlus16: case DOC_ChipID_DocMilPlus32: case 0: /* Possible Millennium Plus, need to do more checks */ /* Possibly release from power down mode */ for (tmp = 0; (tmp < 4); tmp++) ReadDOC(virtadr, Mplus_Power); /* Reset the Millennium Plus ASIC */ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); mdelay(1); /* Enable the Millennium Plus ASIC */ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); mdelay(1); ChipID = ReadDOC(virtadr, ChipID); switch (ChipID) { case DOC_ChipID_DocMilPlus16: reg = DoC_Mplus_Toggle; break; case DOC_ChipID_DocMilPlus32: printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); default: ret = -ENODEV; goto notfound; } break; default: ret = -ENODEV; goto notfound; } /* Check the TOGGLE bit in the ECC register */ tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; if ((tmp == tmpb) || (tmp != tmpc)) { printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); ret = -ENODEV; goto notfound; } for (mtd = doclist; mtd; mtd = doc->nextdoc) { unsigned char oldval; unsigned char newval; nand = mtd->priv; doc = nand->priv; /* Use the alias resolution register to determine if this is in fact the same DOC aliased to a new address. If writes to one chip's alias resolution register change the value on the other chip, they're the same chip. */ if (ChipID == DOC_ChipID_DocMilPlus16) { oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); newval = ReadDOC(virtadr, Mplus_AliasResolution); } else { oldval = ReadDOC(doc->virtadr, AliasResolution); newval = ReadDOC(virtadr, AliasResolution); } if (oldval != newval) continue; if (ChipID == DOC_ChipID_DocMilPlus16) { WriteDOC(~newval, virtadr, Mplus_AliasResolution); oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it } else { WriteDOC(~newval, virtadr, AliasResolution); oldval = ReadDOC(doc->virtadr, AliasResolution); WriteDOC(newval, virtadr, AliasResolution); // restore it } newval = ~newval; if (oldval == newval) { printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr); goto notfound; } } printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); len = sizeof(struct mtd_info) + sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr)); mtd = kzalloc(len, GFP_KERNEL); if (!mtd) { printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); ret = -ENOMEM; goto fail; } nand = (struct nand_chip *) (mtd + 1); doc = (struct doc_priv *) (nand + 1); nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); nand->bbt_md = nand->bbt_td + 1; mtd->priv = nand; mtd->owner = THIS_MODULE; nand->priv = doc; nand->select_chip = doc200x_select_chip; nand->cmd_ctrl = doc200x_hwcontrol; nand->dev_ready = doc200x_dev_ready; nand->waitfunc = doc200x_wait; nand->block_bad = doc200x_block_bad; nand->ecc.hwctl = doc200x_enable_hwecc; nand->ecc.calculate = doc200x_calculate_ecc; nand->ecc.correct = doc200x_correct_data; nand->ecc.layout = &doc200x_oobinfo; nand->ecc.mode = NAND_ECC_HW_SYNDROME; nand->ecc.size = 512; nand->ecc.bytes = 6; nand->options = NAND_USE_FLASH_BBT; doc->physadr = physadr; doc->virtadr = virtadr; doc->ChipID = ChipID; doc->curfloor = -1; doc->curchip = -1; doc->mh0_page = -1; doc->mh1_page = -1; doc->nextdoc = doclist; if (ChipID == DOC_ChipID_Doc2k) numchips = doc2000_init(mtd); else if (ChipID == DOC_ChipID_DocMilPlus16) numchips = doc2001plus_init(mtd); else numchips = doc2001_init(mtd); if ((ret = nand_scan(mtd, numchips))) { /* DBB note: i believe nand_release is necessary here, as buffers may have been allocated in nand_base. Check with Thomas. FIX ME! */ /* nand_release will call del_mtd_device, but we haven't yet added it. This is handled without incident by del_mtd_device, as far as I can tell. */ nand_release(mtd); kfree(mtd); goto fail; } /* Success! */ doclist = mtd; return 0; notfound: /* Put back the contents of the DOCControl register, in case it's not actually a DiskOnChip. */ WriteDOC(save_control, virtadr, DOCControl); fail: iounmap(virtadr); return ret;}static void release_nanddoc(void){ struct mtd_info *mtd, *nextmtd; struct nand_chip *nand; struct doc_priv *doc; for (mtd = doclist; mtd; mtd = nextmtd) { nand = mtd->priv; doc = nand->priv; nextmtd = doc->nextdoc; nand_release(mtd); iounmap(doc->virtadr); kfree(mtd); }}static int __init init_nanddoc(void){ int i, ret = 0; /* We could create the decoder on demand, if memory is a concern. * This way we have it handy, if an error happens * * Symbolsize is 10 (bits) * Primitve polynomial is x^10+x^3+1 * first consecutive root is 510 * primitve element to generate roots = 1 * generator polinomial degree = 4 */ rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); if (!rs_decoder) { printk(KERN_ERR "DiskOnChip: Could not create a RS decoder\n"); return -ENOMEM; } if (doc_config_location) { printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); ret = doc_probe(doc_config_location); if (ret < 0) goto outerr; } else { for (i = 0; (doc_locations[i] != 0xffffffff); i++) { doc_probe(doc_locations[i]); } } /* No banner message any more. Print a message if no DiskOnChip found, so the user knows we at least tried. */ if (!doclist) { printk(KERN_INFO "No valid DiskOnChip devices found\n"); ret = -ENODEV; goto outerr; } return 0; outerr: free_rs(rs_decoder); return ret;}static void __exit cleanup_nanddoc(void){ /* Cleanup the nand/DoC resources */ release_nanddoc(); /* Free the reed solomon resources */ if (rs_decoder) { free_rs(rs_decoder); }}module_init(init_nanddoc);module_exit(cleanup_nanddoc);MODULE_LICENSE("GPL");MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -