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