doc2001plus.c revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Linux driver for Disk-On-Chip Millennium Plus 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com> 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 2002-2003 SnapGear Inc 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 1999 Machine Vision Holdings, Inc. 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * $Id: doc2001plus.c,v 1.13 2005/01/05 18:05:12 dwmw2 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 */ 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline 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 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the NAND chip ID: 1. Send ReadID command */ 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_READID, 0); 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 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 */ 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 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 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 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; 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 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 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const char im_name[] = "DoCMilPlus_init"; 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This routine is made available to other mtd code via 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * inter_module_register. It must only be accessed through 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * inter_module_get which will bump the use count of this module. The 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * addresses passed back in mtd are valid as long as the use count of 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * this module is non-zero, i.e. between inter_module_get and 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void DoCMilPlus_init(struct mtd_info *mtd) 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *old = NULL; 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We must avoid being called twice for the same device. */ 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (docmilpluslist) 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds old = docmilpluslist->priv; 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while (old) { 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (DoCMilPlus_is_alias(this, old)) { 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "Plus at 0x%lX - already configured\n", 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->physadr); 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iounmap(this->virtadr); 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (old->nextdoc) 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds old = old->nextdoc->priv; 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds old = NULL; 4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->name = "DiskOnChip Millennium Plus"; 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "address 0x%lX\n", this->physadr); 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->type = MTD_NANDFLASH; 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->flags = MTD_CAP_NANDFLASH; 4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->ecctype = MTD_ECC_RS_DiskOnChip; 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->size = 0; 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->erasesize = 0; 4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->oobblock = 512; 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->oobsize = 16; 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->owner = THIS_MODULE; 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->erase = doc_erase; 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->point = NULL; 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->unpoint = NULL; 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->read = doc_read; 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->write = doc_write; 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->read_ecc = doc_read_ecc; 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->write_ecc = doc_write_ecc; 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->read_oob = doc_read_oob; 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->write_oob = doc_write_oob; 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->sync = NULL; 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->totlen = 0; 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->numchips = 0; 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = -1; 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = -1; 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Ident all the chips present. */ 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_ScanChips(this); 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->totlen) { 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iounmap(this->virtadr); 5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->nextdoc = docmilpluslist; 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds docmilpluslist = mtd; 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->size = this->totlen; 5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd->erasesize = this->erasesize; 5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds add_mtd_device(mtd); 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0 5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_dumpblk(struct mtd_info *mtd, loff_t from) 5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs; 5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[from >> (this->chipshift)]; 5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char *bp, buf[1056]; 5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char c[32]; 5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds from &= ~0x3ff; 5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow read past end of device */ 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (from >= this->totlen) 5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = from; 5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); 5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashControl); 5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* disable the ECC engine */ 5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the data via the internal pipeline through CDSN IO 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register, see Pipelined Read Operations 11.3 */ 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemReadDOC(docptr, buf, 1054); 5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); 5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(&c[0], 0, sizeof(c)); 5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("DUMP OFFSET=%x:\n", (int)from); 5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0, bp = &buf[0]; (i < 1056); i++) { 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((i % 16) == 0) 5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%08x: ", i); 5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" %02x", *bp); 5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds bp++; 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (((i + 1) % 16) == 0) 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" %s\n", c); 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("\n"); 5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_read(struct mtd_info *mtd, loff_t from, size_t len, 6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, u_char *buf) 6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Just a special case of doc_read_ecc */ 6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); 6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, 6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, u_char *buf, u_char *eccbuf, 6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nand_oobinfo *oobsel) 6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret, i; 6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs; 6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char syndrome[6]; 6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[from >> (this->chipshift)]; 6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow read past end of device */ 6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (from >= this->totlen) 6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow a single read to cross a 512-byte block boundary */ 6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (from + len > ((from | 0x1ff) + 1)) 6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = ((from | 0x1ff) + 1) - from; 6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); 6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = from; 6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); 6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashControl); 6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (eccbuf) { 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ 6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); 6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* disable the ECC engine */ 6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let the caller know we completed it */ 6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = len; 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = 0; 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (eccbuf) { 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the data via the internal pipeline through CDSN IO 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register, see Pipelined Read Operations 11.3 */ 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemReadDOC(docptr, buf, len); 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the ECC data following raw data */ 6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemReadDOC(docptr, eccbuf, 4); 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); 6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Flush the pipeline */ 6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ECCConf); 6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ECCConf); 6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Check the ECC Status */ 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int nb_errors; 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* There was an ECC error */ 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECC_DEBUG 6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); 6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the ECC syndrom through the DiskOnChip ECC logic. 6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds These syndrome will be all ZERO when there is no error */ 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < 6; i++) 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nb_errors = doc_decode_ecc(buf, syndrome); 6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECC_DEBUG 6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("ECC Errors corrected: %x\n", nb_errors); 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (nb_errors < 0) { 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We return error, but have actually done the read. Not that 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this can be told to user-space, via sys_read(), but at least 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MTD-aware stuff can know about it by checking *retlen */ 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ECC_DEBUG 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __FILE__, __LINE__, (int)from); 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "%02x\n", 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds syndrome[0], syndrome[1], syndrome[2], 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds syndrome[3], syndrome[4], syndrome[5]); 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:" 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds "%02x\n", 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[0], eccbuf[1], eccbuf[2], 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[3], eccbuf[4], eccbuf[5]); 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef PSYCHO_DEBUG 7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], 7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[4], eccbuf[5]); 7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* disable the ECC engine */ 7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the data via the internal pipeline through CDSN IO 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register, see Pipelined Read Operations 11.3 */ 7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemReadDOC(docptr, buf, len-2); 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); 7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_write(struct mtd_info *mtd, loff_t to, size_t len, 7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, const u_char *buf) 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char eccbuf[6]; 7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); 7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, 7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, const u_char *buf, u_char *eccbuf, 7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct nand_oobinfo *oobsel) 7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i, before, ret = 0; 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fto; 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[to >> (this->chipshift)]; 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow write past end of device */ 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (to >= this->totlen) 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Don't allow writes which aren't exactly one block (512 bytes) */ 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((to & 0x1ff) || (len != 0x200)) 7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Determine position of OOB flags, before or after data */ 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds before = (this->interleave && (to & 0x200)); 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Set device to appropriate plane of flash */ 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fto = to; 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* On interleaved devices the flags for 2nd half 512 are before data */ 7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (eccbuf && before) 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fto -= 2; 7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* issue the Serial Data In command to initial the Page Program process */ 8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fto, 0x00, 0x00); 8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable the ECC engine */ 8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (eccbuf) { 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (before) { 8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write the block status BLOCK_USED (0x5555) */ 8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x55, docptr, Mil_CDSN_IO); 8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x55, docptr, Mil_CDSN_IO); 8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ 8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); 8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemWriteDOC(docptr, (unsigned char *) buf, len); 8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (eccbuf) { 8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write ECC data to flash, the ECC info is generated by 8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ 8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Delay(docptr, 3); 8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the ECC data through the DiskOnChip ECC logic */ 8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; i < 6; i++) 8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); 8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* disable the ECC engine */ 8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); 8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write the ECC data to flash */ 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemWriteDOC(docptr, eccbuf, 6); 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!before) { 8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write the block status BLOCK_USED (0x5555) */ 8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x55, docptr, Mil_CDSN_IO+6); 8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x55, docptr, Mil_CDSN_IO+7); 8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef PSYCHO_DEBUG 8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", 8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], 8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds eccbuf[4], eccbuf[5]); 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Commit the Page Program command and wait for ready 8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 1.*/ 8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); 8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the status of the flash device through CDSN IO register 8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 5.*/ 8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_STATUS, 0); 8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Delay(docptr, 2); 8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { 8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); 8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Error in programming 8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds FIXME: implement Bad Block Replacement (in nftl.c ??) */ 8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = 0; 8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Let the caller know we completed it */ 8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = len; 8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, 8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, u_char *buf) 8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs, base; 8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[ofs >> this->chipshift]; 8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t i, size, got, want; 8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); 9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* disable the ECC engine */ 9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Maximum of 16 bytes in the OOB region, so limit read to that */ 9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len > 16) 9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = 16; 9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got = 0; 9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want = len; 9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; ((i < 3) && (want > 0)); i++) { 9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Figure out which region we are accessing... */ 9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = ofs; 9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds base = ofs & 0xf; 9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->interleave) { 9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_READOOB, 0); 9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 6) { 9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); 9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 6 - base; 9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 8) { 9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); 9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 8 - base; 9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); 9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size > want) 9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = want; 9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Issue read command */ 9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashControl); 9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ReadDOC(docptr, Mplus_ReadPipeInit); 9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemReadDOC(docptr, &buf[got], size - 2); 9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); 9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); 9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs += size; 9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got += size; 9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want -= size; 9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = len; 9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, 9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t *retlen, const u_char *buf) 9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds loff_t fofs, base; 9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[ofs >> this->chipshift]; 9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size_t i, size, got, want; 9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); 9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Maximum of 16 bytes in the OOB region, so limit write to that */ 9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len > 16) 9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len = 16; 9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got = 0; 9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want = len; 9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (i = 0; ((i < 3) && (want > 0)); i++) { 9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Reset the chip, see Software Requirement 11.4 item 1. */ 9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0); 9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Figure out which region we are accessing... */ 9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds fofs = ofs; 9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds base = ofs & 0x0f; 9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!this->interleave) { 9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); 9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 6) { 10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); 10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 6 - base; 10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (base < 8) { 10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); 10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 8 - base; 10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); 10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = 16 - base; 10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (size > want) 10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds size = want; 10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Issue the Serial Data In command to initial the Page Program process */ 10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); 10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 3, fofs, 0, 0x00); 10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable the ECC engine */ 10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Write the data via the internal pipeline through CDSN IO 10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register, see Pipelined Write Operations 11.2 */ 10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MemWriteDOC(docptr, (unsigned char *) &buf[got], size); 10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0x00, docptr, Mplus_WritePipeTerm); 10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Commit the Page Program command and wait for ready 10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 1.*/ 10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); 10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the status of the flash device through CDSN IO register 10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 5.*/ 10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_STATUS, 0x00); 10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Delay(docptr, 2); 10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { 10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("MTD: Error 0x%x programming oob at 0x%x\n", 10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy, (int)ofs); 10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME: implement Bad Block Replacement */ 10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = 0; 10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -EIO; 10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ofs += size; 10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds got += size; 10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds want -= size; 10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *retlen = len; 10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsint doc_erase(struct mtd_info *mtd, struct erase_info *instr) 10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds volatile char dummy; 10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this = mtd->priv; 10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __u32 ofs = instr->addr; 10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds __u32 len = instr->len; 10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem * docptr = this->virtadr; 10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct Nand *mychip = &this->chips[ofs >> this->chipshift]; 10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_CheckASIC(docptr); 10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (len != mtd->erasesize) 10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", 10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds len, mtd->erasesize); 10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Find the chip which is to be used and select it */ 10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (this->curfloor != mychip->floor) { 10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectFloor(docptr, mychip->floor); 10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else if (this->curchip != mychip->chip) { 10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_SelectChip(docptr, mychip->chip); 10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curfloor = mychip->floor; 10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this->curchip = mychip->chip; 10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASE_PENDING; 10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ 10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); 10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_RESET, 0x00); 10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_ERASE1, 0); 10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Address(this, 2, ofs, 0, 0x00); 10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_ERASE2, 0); 10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_WaitReady(docptr); 10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASING; 10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Read the status of the flash device through CDSN IO register 10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds see Software Requirement 11.4 item 5. */ 10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds DoC_Command(docptr, NAND_CMD_STATUS, 0); 11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_ReadPipeInit); 11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { 11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); 11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ 11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASE_FAILED; 11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds instr->state = MTD_ERASE_DONE; 11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds dummy = ReadDOC(docptr, Mplus_LastDataRead); 11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable flash internally */ 11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds WriteDOC(0, docptr, Mplus_FlashSelect); 11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mtd_erase_callback(instr); 11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**************************************************************************** 11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Module stuff 11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ****************************************************************************/ 11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_doc2001plus(void) 11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init); 11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup_doc2001plus(void) 11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd; 11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct DiskOnChip *this; 11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while ((mtd=docmilpluslist)) { 11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds this = mtd->priv; 11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds docmilpluslist = this->nextdoc; 11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds del_mtd_device(mtd); 11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iounmap(this->virtadr); 11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(this->chips); 11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(mtd); 11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds inter_module_unregister(im_name); 11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_doc2001plus); 11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_doc2001plus); 11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al."); 11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); 1155