11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Linux driver for Disk-On-Chip Millennium Plus 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com> 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 2002-2003 SnapGear Inc 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 1999 Machine Vision Holdings, Inc. 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Released under GPL 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/errno.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h> 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h> 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bitops.h> 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/nand.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/doc2000.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* #define ECC_DEBUG */ 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* I have no idea why some DoC chips can not use memcop_form|to_io(). 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This may be due to the different revisions of the ASIC controller built-in or 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this:*/ 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef USE_MEMCPY 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_read(struct mtd_info *mtd, loff_t from, size_t len, 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, u_char *buf); 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_write(struct mtd_info *mtd, loff_t to, size_t len, 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, const u_char *buf); 398593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixnerstatic int doc_read_oob(struct mtd_info *mtd, loff_t ofs, 408593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner struct mtd_oob_ops *ops); 418593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixnerstatic int doc_write_oob(struct mtd_info *mtd, loff_t ofs, 428593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner struct mtd_oob_ops *ops); 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_erase (struct mtd_info *mtd, struct erase_info *instr); 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct mtd_info *docmilpluslist = NULL; 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Perform the required delay cycles by writing to the NOP register */ 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void DoC_Delay(void __iomem * docptr, int cycles) 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; (i < cycles); i++) 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_NOP); 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int _DoC_WaitReady(void __iomem * docptr) 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int c = 0xffff; 631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 64289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("_DoC_WaitReady called for out-of-line wait\n"); 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Out-of-line routine to wait for chip response */ 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ; 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (c == 0) 71289c05222172b51401dbbb017115655f241d94abBrian Norris pr_debug("_DoC_WaitReady timed out.\n"); 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (c == 0); 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int DoC_WaitReady(void __iomem * docptr) 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* This is inline, to optimise the common case, where it's ready instantly */ 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* read form NOP register should be issued prior to the read from CDSNControl 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 2. */ 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Delay(docptr, 4); 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Call the out-of-line routine to wait */ 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = _DoC_WaitReady(docptr); 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9225985edcedea6396277003854657b5f3cb31a628Lucas De Marchi/* For some reason the Millennium Plus seems to occasionally put itself 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * into reset mode. For me this happens randomly, with no pattern that I 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * can detect. M-systems suggest always check this on any block level 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * operation and setting to normal mode if in reset mode. 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void DoC_CheckASIC(void __iomem * docptr) 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Make sure the DoC is in normal mode */ 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* DoC_Command: Send a flash command to the flash chip through the Flash 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * command register. Need 2 Write Pipeline Terminates to complete send. 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 109858119e159384308a5dde67776691a2ebf70df0fArjan van de Venstatic void DoC_Command(void __iomem * docptr, unsigned char command, 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char xtraflags) 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(command, docptr, Mplus_FlashCmd); 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(command, docptr, Mplus_WritePipeTerm); 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(command, docptr, Mplus_WritePipeTerm); 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* DoC_Address: Set the current address for the flash chip through the Flash 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Address register. Need 2 Write Pipeline Terminates to complete send. 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void DoC_Address(struct DiskOnChip *doc, int numbytes, 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long ofs, unsigned char xtraflags1, 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char xtraflags2) 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = doc->virtadr; 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Allow for possible Mill Plus internal flash interleaving */ 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs >>= doc->interleave; 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (numbytes) { 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 1: 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Send single byte, bits 0-7. */ 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 2: 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Send bits 9-16 followed by 17-23 */ 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 3: 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Send 0-7, 9-16, then 17-23 */ 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* DoC_SelectChip: Select a given flash chip within the current floor */ 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int DoC_SelectChip(void __iomem * docptr, int chip) 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* No choice for flash chip on Millennium Plus */ 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int DoC_SelectFloor(void __iomem * docptr, int floor) 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Translate the given offset into the appropriate command and offset. 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This does the mapping using the 16bit interleave layout defined by 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * M-Systems, and looks like this for a sector pair: 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * +-----------+-------+-------+-------+--------------+---------+-----------+ 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * +-----------+-------+-------+-------+--------------+---------+-----------+ 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * +-----------+-------+-------+-------+--------------+---------+-----------+ 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* FIXME: This lives in INFTL not here. Other users of flash devices 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds may not want it */ 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->interleave) { 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int ofs = *from & 0x3ff; 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int cmd; 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ofs < 512) { 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd = NAND_CMD_READ0; 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs &= 0x1ff; 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (ofs < 1014) { 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd = NAND_CMD_READ1; 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = (ofs & 0x1ff) + 10; 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd = NAND_CMD_READOOB; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = ofs - 1014; 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *from = (*from & ~0x3ff) | ofs; 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return cmd; 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* No interleave */ 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((*from) & 0x100) 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NAND_CMD_READ1; 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return NAND_CMD_READ0; 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int ofs, cmd; 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (*from & 0x200) { 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd = NAND_CMD_READOOB; 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = 10 + (*from & 0xf); 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd = NAND_CMD_READ1; 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = (*from & 0xf); 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *from = (*from & ~0x3ff) | ofs; 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return cmd; 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int ofs, cmd; 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd = NAND_CMD_READ1; 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = (*from & 0x200) ? 8 : 6; 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *from = (*from & ~0x3ff) | ofs; 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return cmd; 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) 2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int ofs, cmd; 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cmd = NAND_CMD_READOOB; 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs = (*from & 0x200) ? 24 : 16; 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *from = (*from & ~0x3ff) | ofs; 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return cmd; 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void MemReadDOC(void __iomem * docptr, unsigned char *buf, int len) 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef USE_MEMCPY 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < len; i++) 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void MemWriteDOC(void __iomem * docptr, unsigned char *buf, int len) 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef USE_MEMCPY 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < len; i++) 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else 2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int mfr, id, i, j; 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = doc->virtadr; 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Page in the required floor/chip */ 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, floor); 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, chip); 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 284e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner /* Read the NAND chip ID: 1. Send ReadID command */ 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_READID, 0); 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 287e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner /* Read the NAND chip ID: 2. Send address byte zero */ 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(doc, 1, 0x00, 0, 0x00); 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashControl); 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the manufacturer and device id codes of the flash device through 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds CDSN IO register see Software Requirement 11.4 item 5.*/ 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mfr = ReadDOC(docptr, Mil_CDSN_IO); 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (doc->interleave) 3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds id = ReadDOC(docptr, Mil_CDSN_IO); 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (doc->interleave) 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* No response - return failure */ 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (mfr == 0xff || mfr == 0) 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; nand_flash_ids[i].name != NULL; i++) { 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (id == nand_flash_ids[i].id) { 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Try to identify manufacturer */ 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (nand_manuf_ids[j].id == mfr) 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "Chip ID: %2.2X (%s:%s)\n", mfr, id, 3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nand_manuf_ids[j].name, nand_flash_ids[i].name); 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds doc->mfr = mfr; 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds doc->id = id; 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1; 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (nand_flash_ids[i].name == NULL) 3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void DoC_ScanChips(struct DiskOnChip *this) 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int floor, chip; 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int numchips[MAX_FLOORS_MPLUS]; 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret; 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->numchips = 0; 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->mfr = 0; 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->id = 0; 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Work out the intended interleave setting */ 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->interleave = 0; 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->ChipID == DOC_ChipID_DocMilPlus32) 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->interleave = 1; 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check the ASIC agrees */ 356e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner if ( (this->interleave << 2) != 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->interleave?"on (16-bit)":"off (8-bit)"); 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds conf ^= 4; 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(conf, this->virtadr, Mplus_Configuration); 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* For each floor, find the number of valid chips it contains */ 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds numchips[floor] = 0; 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = DoC_IdentChip(this, floor, chip); 3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ret) { 3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds numchips[floor]++; 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->numchips++; 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* If there are none at all that we recognise, bail */ 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->numchips) { 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("No flash chips recognised.\n"); 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Allocate an array to hold the information for each chip */ 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->chips){ 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("MTD: No memory for allocating chip info structures\n"); 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 389e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner /* Fill out the chip array with {floor, chipno} for each 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * detected chip in the device. */ 3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (chip = 0 ; chip < numchips[floor] ; chip++) { 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->chips[ret].floor = floor; 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->chips[ret].chip = chip; 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->chips[ret].curadr = 0; 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->chips[ret].curmode = 0x50; 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret++; 3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Calculate and print the total size of the device */ 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->totlen = this->numchips * (1 << this->chipshift); 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->numchips ,this->totlen >> 20); 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) 4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int tmp1, tmp2, retval; 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (doc1->physadr == doc2->physadr) 4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Use the alias resolution register which was set aside for this 4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * purpose. If it's value is the same on both chips, they might 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * be the same chip, and we write to one and check for a change in 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the other. It's unclear if this register is usuable in the 4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * DoC 2000 (it's in the Millennium docs), but it seems to work. */ 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (tmp1 != tmp2) 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 423e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (tmp2 == (tmp1+1) % 0xff) 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = 1; 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds retval = 0; 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Restore register contents. May not be necessary, but do it just to 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * be safe. */ 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return retval; 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4385e535429a9117b8b6219da0e0cb087f52c0c9597David Woodhouse/* This routine is found from the docprobe code by symbol_get(), 4395e535429a9117b8b6219da0e0cb087f52c0c9597David Woodhouse * which will bump the use count of this module. */ 4405e535429a9117b8b6219da0e0cb087f52c0c9597David Woodhousevoid DoCMilPlus_init(struct mtd_info *mtd) 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *old = NULL; 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We must avoid being called twice for the same device. */ 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (docmilpluslist) 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds old = docmilpluslist->priv; 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (old) { 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (DoCMilPlus_is_alias(this, old)) { 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "Plus at 0x%lX - already configured\n", 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->physadr); 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iounmap(this->virtadr); 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (old->nextdoc) 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds old = old->nextdoc->priv; 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds old = NULL; 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->name = "DiskOnChip Millennium Plus"; 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "address 0x%lX\n", this->physadr); 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->type = MTD_NANDFLASH; 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->flags = MTD_CAP_NANDFLASH; 47028318776a80bc3261f9af91ef79e6e38bb9f5becJoern Engel mtd->writesize = 512; 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->oobsize = 16; 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->owner = THIS_MODULE; 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->erase = doc_erase; 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->read = doc_read; 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->write = doc_write; 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->read_oob = doc_read_oob; 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->write_oob = doc_write_oob; 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = -1; 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = -1; 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Ident all the chips present. */ 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_ScanChips(this); 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->totlen) { 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iounmap(this->virtadr); 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->nextdoc = docmilpluslist; 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds docmilpluslist = mtd; 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->size = this->totlen; 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->erasesize = this->erasesize; 492ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles mtd_device_register(mtd, NULL, 0); 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 496b04ecae3d62edea2af49fd40ab12435b5ad8a492David WoodhouseEXPORT_SYMBOL_GPL(DoCMilPlus_init); 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_dumpblk(struct mtd_info *mtd, loff_t from) 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs; 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[from >> (this->chipshift)]; 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *bp, buf[1056]; 5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char c[32]; 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds from &= ~0x3ff; 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow read past end of device */ 5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (from >= this->totlen) 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = from; 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashControl); 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* disable the ECC engine */ 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the data via the internal pipeline through CDSN IO 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register, see Pipelined Read Operations 11.3 */ 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemReadDOC(docptr, buf, 1054); 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); 5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(&c[0], 0, sizeof(c)); 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("DUMP OFFSET=%x:\n", (int)from); 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0, bp = &buf[0]; (i < 1056); i++) { 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((i % 16) == 0) 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%08x: ", i); 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" %02x", *bp); 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds bp++; 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (((i + 1) % 16) == 0) 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" %s\n", c); 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("\n"); 5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_read(struct mtd_info *mtd, loff_t from, size_t len, 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, u_char *buf) 5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret, i; 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs; 5797f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner unsigned char syndrome[6], eccbuf[6]; 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[from >> (this->chipshift)]; 5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow read past end of device */ 5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (from >= this->totlen) 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow a single read to cross a 512-byte block boundary */ 589e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner if (from + len > ((from | 0x1ff) + 1)) 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = ((from | 0x1ff) + 1) - from; 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = from; 6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); 6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashControl); 6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6177f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ 6187f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 6197f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); 6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let the caller know we completed it */ 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = len; 6237f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner ret = 0; 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6287f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Read the data via the internal pipeline through CDSN IO 6297f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner register, see Pipelined Read Operations 11.3 */ 6307f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner MemReadDOC(docptr, buf, len); 6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6327f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Read the ECC data following raw data */ 6337f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner MemReadDOC(docptr, eccbuf, 4); 6347f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); 6357f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); 6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6377f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Flush the pipeline */ 6387f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner dummy = ReadDOC(docptr, Mplus_ECCConf); 6397f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner dummy = ReadDOC(docptr, Mplus_ECCConf); 6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6417f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Check the ECC Status */ 6427f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { 6437f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner int nb_errors; 6447f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* There was an ECC error */ 6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECC_DEBUG 6467f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6487854d3f7495b11be1570cd3e2318674d8f9ed797Brian Norris /* Read the ECC syndrome through the DiskOnChip ECC logic. 6497f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner These syndrome will be all ZERO when there is no error */ 6507f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner for (i = 0; i < 6; i++) 6517f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6537f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner nb_errors = doc_decode_ecc(buf, syndrome); 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECC_DEBUG 6557f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner printk("ECC Errors corrected: %x\n", nb_errors); 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6577f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner if (nb_errors < 0) { 6587f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* We return error, but have actually done the 6597f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner read. Not that this can be told to user-space, via 6607f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner sys_read(), but at least MTD-aware stuff can know 6617f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner about it by checking *retlen */ 6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECC_DEBUG 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __FILE__, __LINE__, (int)from); 6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "%02x\n", 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds syndrome[0], syndrome[1], syndrome[2], 6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds syndrome[3], syndrome[4], syndrome[5]); 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:" 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "%02x\n", 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[0], eccbuf[1], eccbuf[2], 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[3], eccbuf[4], eccbuf[5]); 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6767f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner } 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef PSYCHO_DEBUG 6797f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 6807f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], 6817f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner eccbuf[4], eccbuf[5]); 6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6837f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* disable the ECC engine */ 6847f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_write(struct mtd_info *mtd, loff_t to, size_t len, 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, const u_char *buf) 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i, before, ret = 0; 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fto; 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 6987f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner char eccbuf[6]; 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[to >> (this->chipshift)]; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow write past end of device */ 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (to >= this->totlen) 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow writes which aren't exactly one block (512 bytes) */ 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((to & 0x1ff) || (len != 0x200)) 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Determine position of OOB flags, before or after data */ 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds before = (this->interleave && (to & 0x200)); 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Set device to appropriate plane of flash */ 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fto = to; 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); 7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* On interleaved devices the flags for 2nd half 512 are before data */ 73853fb84a0695ffeeeadf8ae92db28cbccf5325531Samuel Tardieu if (before) 7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fto -= 2; 7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* issue the Serial Data In command to initial the Page Program process */ 7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fto, 0x00, 0x00); 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable the ECC engine */ 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7487f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner if (before) { 7497f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Write the block status BLOCK_USED (0x5555) */ 7507f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(0x55, docptr, Mil_CDSN_IO); 7517f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(0x55, docptr, Mil_CDSN_IO); 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7547f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ 7557f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); 7567f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemWriteDOC(docptr, (unsigned char *) buf, len); 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7597f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Write ECC data to flash, the ECC info is generated by 7607f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ 7617f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner DoC_Delay(docptr, 3); 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7637f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Read the ECC data through the DiskOnChip ECC logic */ 7647f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner for (i = 0; i < 6; i++) 7657f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7677f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* disable the ECC engine */ 7687f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7707f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Write the ECC data to flash */ 7717f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner MemWriteDOC(docptr, eccbuf, 6); 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7737f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner if (!before) { 7747f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner /* Write the block status BLOCK_USED (0x5555) */ 7757f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(0x55, docptr, Mil_CDSN_IO+6); 7767f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner WriteDOC(0x55, docptr, Mil_CDSN_IO+7); 7777f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner } 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef PSYCHO_DEBUG 7807f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 7817f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], 7827f8a894066b826a4baea49c2a3adbba0a56a192fThomas Gleixner eccbuf[4], eccbuf[5]); 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Commit the Page Program command and wait for ready 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 1.*/ 7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the status of the flash device through CDSN IO register 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 5.*/ 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_STATUS, 0); 7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Delay(docptr, 2); 7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { 8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Error in programming 8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds FIXME: implement Bad Block Replacement (in nftl.c ??) */ 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = 0; 8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let the caller know we completed it */ 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = len; 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8178593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixnerstatic int doc_read_oob(struct mtd_info *mtd, loff_t ofs, 8188593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner struct mtd_oob_ops *ops) 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs, base; 8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[ofs >> this->chipshift]; 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t i, size, got, want; 8258593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner uint8_t *buf = ops->oobbuf; 8268593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner size_t len = ops->len; 8278593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner 8280612b9ddc2eeda014dd805c87c752b342d8f80f0Brian Norris BUG_ON(ops->mode != MTD_OPS_PLACE_OOB); 8298593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner 8308593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner ofs += ops->ooboffs; 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* disable the ECC engine */ 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Maximum of 16 bytes in the OOB region, so limit read to that */ 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len > 16) 8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = 16; 8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got = 0; 8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want = len; 8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; ((i < 3) && (want > 0)); i++) { 8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Figure out which region we are accessing... */ 8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = ofs; 8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds base = ofs & 0xf; 8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->interleave) { 8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_READOOB, 0); 8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 6) { 8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); 8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 6 - base; 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 8) { 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); 8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 8 - base; 8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); 8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size > want) 8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = want; 8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Issue read command */ 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashControl); 8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemReadDOC(docptr, &buf[got], size - 2); 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs += size; 8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got += size; 8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want -= size; 8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8968593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner ops->retlen = len; 8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9008593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixnerstatic int doc_write_oob(struct mtd_info *mtd, loff_t ofs, 9018593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner struct mtd_oob_ops *ops) 9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs, base; 9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[ofs >> this->chipshift]; 9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t i, size, got, want; 9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 9108593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner uint8_t *buf = ops->oobbuf; 9118593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner size_t len = ops->len; 9128593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner 9130612b9ddc2eeda014dd805c87c752b342d8f80f0Brian Norris BUG_ON(ops->mode != MTD_OPS_PLACE_OOB); 9148593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner 9158593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner ofs += ops->ooboffs; 9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); 9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Maximum of 16 bytes in the OOB region, so limit write to that */ 9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len > 16) 9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = 16; 9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got = 0; 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want = len; 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; ((i < 3) && (want > 0)); i++) { 9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Figure out which region we are accessing... */ 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = ofs; 9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds base = ofs & 0x0f; 9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->interleave) { 9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); 9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 6) { 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); 9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 6 - base; 9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 8) { 9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); 9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 8 - base; 9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); 9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size > want) 9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = want; 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Issue the Serial Data In command to initial the Page Program process */ 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); 9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable the ECC engine */ 9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write the data via the internal pipeline through CDSN IO 9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register, see Pipelined Write Operations 11.2 */ 9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemWriteDOC(docptr, (unsigned char *) &buf[got], size); 9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Commit the Page Program command and wait for ready 9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 1.*/ 9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the status of the flash device through CDSN IO register 9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 5.*/ 9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_STATUS, 0x00); 9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Delay(docptr, 2); 9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { 9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("MTD: Error 0x%x programming oob at 0x%x\n", 9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy, (int)ofs); 9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME: implement Bad Block Replacement */ 9918593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner ops->retlen = 0; 9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs += size; 9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got += size; 9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want -= size; 9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10048593fbc68b0df1168995de76d1af38eb62fd6b62Thomas Gleixner ops->retlen = len; 10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint doc_erase(struct mtd_info *mtd, struct erase_info *instr) 10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __u32 ofs = instr->addr; 10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __u32 len = instr->len; 10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[ofs >> this->chipshift]; 10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1019e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner if (len != mtd->erasesize) 10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", 10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len, mtd->erasesize); 10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASE_PENDING; 10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); 10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0x00); 10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_ERASE1, 0); 10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 2, ofs, 0, 0x00); 10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_ERASE2, 0); 10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASING; 10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the status of the flash device through CDSN IO register 10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 5. */ 10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_STATUS, 0); 10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { 10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); 10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ 10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASE_FAILED; 10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASE_DONE; 10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd_erase_callback(instr); 10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**************************************************************************** 10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Module stuff 10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ****************************************************************************/ 10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup_doc2001plus(void) 10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd; 10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this; 10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while ((mtd=docmilpluslist)) { 10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this = mtd->priv; 10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds docmilpluslist = this->nextdoc; 1083e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner 1084ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles mtd_device_unregister(mtd); 1085e5580fbe8a950131b9ccccce0f962811dfb9ef43Thomas Gleixner 10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iounmap(this->virtadr); 10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(this->chips); 10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_doc2001plus); 10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al."); 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); 1097