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