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