162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) 2003 Red Hat, Inc. 462306a36Sopenharmony_ci * (C) 2004 Dan Brown <dan_brown@ieee.org> 562306a36Sopenharmony_ci * (C) 2004 Kalev Lember <kalev@smartlink.ee> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: David Woodhouse <dwmw2@infradead.org> 862306a36Sopenharmony_ci * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org> 962306a36Sopenharmony_ci * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Error correction code lifted from the old docecc code 1262306a36Sopenharmony_ci * Author: Fabrice Bellard (fabrice.bellard@netgem.com) 1362306a36Sopenharmony_ci * Copyright (C) 2000 Netgem S.A. 1462306a36Sopenharmony_ci * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Interface to generic NAND code for M-Systems DiskOnChip devices 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/kernel.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/sched.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/rslib.h> 2462306a36Sopenharmony_ci#include <linux/moduleparam.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <linux/io.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 2962306a36Sopenharmony_ci#include <linux/mtd/rawnand.h> 3062306a36Sopenharmony_ci#include <linux/mtd/doc2000.h> 3162306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 3262306a36Sopenharmony_ci#include <linux/mtd/inftl.h> 3362306a36Sopenharmony_ci#include <linux/module.h> 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* Where to look for the devices? */ 3662306a36Sopenharmony_ci#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 3762306a36Sopenharmony_ci#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 3862306a36Sopenharmony_ci#endif 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic unsigned long doc_locations[] __initdata = { 4162306a36Sopenharmony_ci#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) 4262306a36Sopenharmony_ci#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 4362306a36Sopenharmony_ci 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 4462306a36Sopenharmony_ci 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, 4562306a36Sopenharmony_ci 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 4662306a36Sopenharmony_ci 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 4762306a36Sopenharmony_ci 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, 4862306a36Sopenharmony_ci#else 4962306a36Sopenharmony_ci 0xc8000, 0xca000, 0xcc000, 0xce000, 5062306a36Sopenharmony_ci 0xd0000, 0xd2000, 0xd4000, 0xd6000, 5162306a36Sopenharmony_ci 0xd8000, 0xda000, 0xdc000, 0xde000, 5262306a36Sopenharmony_ci 0xe0000, 0xe2000, 0xe4000, 0xe6000, 5362306a36Sopenharmony_ci 0xe8000, 0xea000, 0xec000, 0xee000, 5462306a36Sopenharmony_ci#endif 5562306a36Sopenharmony_ci#endif 5662306a36Sopenharmony_ci 0xffffffff }; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic struct mtd_info *doclist = NULL; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct doc_priv { 6162306a36Sopenharmony_ci struct nand_controller base; 6262306a36Sopenharmony_ci void __iomem *virtadr; 6362306a36Sopenharmony_ci unsigned long physadr; 6462306a36Sopenharmony_ci u_char ChipID; 6562306a36Sopenharmony_ci u_char CDSNControl; 6662306a36Sopenharmony_ci int chips_per_floor; /* The number of chips detected on each floor */ 6762306a36Sopenharmony_ci int curfloor; 6862306a36Sopenharmony_ci int curchip; 6962306a36Sopenharmony_ci int mh0_page; 7062306a36Sopenharmony_ci int mh1_page; 7162306a36Sopenharmony_ci struct rs_control *rs_decoder; 7262306a36Sopenharmony_ci struct mtd_info *nextdoc; 7362306a36Sopenharmony_ci bool supports_32b_reads; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci /* Handle the last stage of initialization (BBT scan, partitioning) */ 7662306a36Sopenharmony_ci int (*late_init)(struct mtd_info *mtd); 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* This is the ecc value computed by the HW ecc generator upon writing an empty 8062306a36Sopenharmony_ci page, one with all 0xff for data. */ 8162306a36Sopenharmony_cistatic u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define INFTL_BBT_RESERVED_BLOCKS 4 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32) 8662306a36Sopenharmony_ci#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) 8762306a36Sopenharmony_ci#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int debug = 0; 9062306a36Sopenharmony_cimodule_param(debug, int, 0); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int try_dword = 1; 9362306a36Sopenharmony_cimodule_param(try_dword, int, 0); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int no_ecc_failures = 0; 9662306a36Sopenharmony_cimodule_param(no_ecc_failures, int, 0); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int no_autopart = 0; 9962306a36Sopenharmony_cimodule_param(no_autopart, int, 0); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int show_firmware_partition = 0; 10262306a36Sopenharmony_cimodule_param(show_firmware_partition, int, 0); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE 10562306a36Sopenharmony_cistatic int inftl_bbt_write = 1; 10662306a36Sopenharmony_ci#else 10762306a36Sopenharmony_cistatic int inftl_bbt_write = 0; 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_cimodule_param(inftl_bbt_write, int, 0); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; 11262306a36Sopenharmony_cimodule_param(doc_config_location, ulong, 0); 11362306a36Sopenharmony_ciMODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Sector size for HW ECC */ 11662306a36Sopenharmony_ci#define SECTOR_SIZE 512 11762306a36Sopenharmony_ci/* The sector bytes are packed into NB_DATA 10 bit words */ 11862306a36Sopenharmony_ci#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) 11962306a36Sopenharmony_ci/* Number of roots */ 12062306a36Sopenharmony_ci#define NROOTS 4 12162306a36Sopenharmony_ci/* First consective root */ 12262306a36Sopenharmony_ci#define FCR 510 12362306a36Sopenharmony_ci/* Number of symbols */ 12462306a36Sopenharmony_ci#define NN 1023 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * The HW decoder in the DoC ASIC's provides us a error syndrome, 12862306a36Sopenharmony_ci * which we must convert to a standard syndrome usable by the generic 12962306a36Sopenharmony_ci * Reed-Solomon library code. 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * Fabrice Bellard figured this out in the old docecc code. I added 13262306a36Sopenharmony_ci * some comments, improved a minor bit and converted it to make use 13362306a36Sopenharmony_ci * of the generic Reed-Solomon library. tglx 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic int doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci int i, j, nerr, errpos[8]; 13862306a36Sopenharmony_ci uint8_t parity; 13962306a36Sopenharmony_ci uint16_t ds[4], s[5], tmp, errval[8], syn[4]; 14062306a36Sopenharmony_ci struct rs_codec *cd = rs->codec; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci memset(syn, 0, sizeof(syn)); 14362306a36Sopenharmony_ci /* Convert the ecc bytes into words */ 14462306a36Sopenharmony_ci ds[0] = ((ecc[4] & 0xff) >> 0) | ((ecc[5] & 0x03) << 8); 14562306a36Sopenharmony_ci ds[1] = ((ecc[5] & 0xfc) >> 2) | ((ecc[2] & 0x0f) << 6); 14662306a36Sopenharmony_ci ds[2] = ((ecc[2] & 0xf0) >> 4) | ((ecc[3] & 0x3f) << 4); 14762306a36Sopenharmony_ci ds[3] = ((ecc[3] & 0xc0) >> 6) | ((ecc[0] & 0xff) << 2); 14862306a36Sopenharmony_ci parity = ecc[1]; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* Initialize the syndrome buffer */ 15162306a36Sopenharmony_ci for (i = 0; i < NROOTS; i++) 15262306a36Sopenharmony_ci s[i] = ds[0]; 15362306a36Sopenharmony_ci /* 15462306a36Sopenharmony_ci * Evaluate 15562306a36Sopenharmony_ci * s[i] = ds[3]x^3 + ds[2]x^2 + ds[1]x^1 + ds[0] 15662306a36Sopenharmony_ci * where x = alpha^(FCR + i) 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci for (j = 1; j < NROOTS; j++) { 15962306a36Sopenharmony_ci if (ds[j] == 0) 16062306a36Sopenharmony_ci continue; 16162306a36Sopenharmony_ci tmp = cd->index_of[ds[j]]; 16262306a36Sopenharmony_ci for (i = 0; i < NROOTS; i++) 16362306a36Sopenharmony_ci s[i] ^= cd->alpha_to[rs_modnn(cd, tmp + (FCR + i) * j)]; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Calc syn[i] = s[i] / alpha^(v + i) */ 16762306a36Sopenharmony_ci for (i = 0; i < NROOTS; i++) { 16862306a36Sopenharmony_ci if (s[i]) 16962306a36Sopenharmony_ci syn[i] = rs_modnn(cd, cd->index_of[s[i]] + (NN - FCR - i)); 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci /* Call the decoder library */ 17262306a36Sopenharmony_ci nerr = decode_rs16(rs, NULL, NULL, 1019, syn, 0, errpos, 0, errval); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Incorrectable errors ? */ 17562306a36Sopenharmony_ci if (nerr < 0) 17662306a36Sopenharmony_ci return nerr; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * Correct the errors. The bitpositions are a bit of magic, 18062306a36Sopenharmony_ci * but they are given by the design of the de/encoder circuit 18162306a36Sopenharmony_ci * in the DoC ASIC's. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci for (i = 0; i < nerr; i++) { 18462306a36Sopenharmony_ci int index, bitpos, pos = 1015 - errpos[i]; 18562306a36Sopenharmony_ci uint8_t val; 18662306a36Sopenharmony_ci if (pos >= NB_DATA && pos < 1019) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci if (pos < NB_DATA) { 18962306a36Sopenharmony_ci /* extract bit position (MSB first) */ 19062306a36Sopenharmony_ci pos = 10 * (NB_DATA - 1 - pos) - 6; 19162306a36Sopenharmony_ci /* now correct the following 10 bits. At most two bytes 19262306a36Sopenharmony_ci can be modified since pos is even */ 19362306a36Sopenharmony_ci index = (pos >> 3) ^ 1; 19462306a36Sopenharmony_ci bitpos = pos & 7; 19562306a36Sopenharmony_ci if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { 19662306a36Sopenharmony_ci val = (uint8_t) (errval[i] >> (2 + bitpos)); 19762306a36Sopenharmony_ci parity ^= val; 19862306a36Sopenharmony_ci if (index < SECTOR_SIZE) 19962306a36Sopenharmony_ci data[index] ^= val; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci index = ((pos >> 3) + 1) ^ 1; 20262306a36Sopenharmony_ci bitpos = (bitpos + 10) & 7; 20362306a36Sopenharmony_ci if (bitpos == 0) 20462306a36Sopenharmony_ci bitpos = 8; 20562306a36Sopenharmony_ci if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) { 20662306a36Sopenharmony_ci val = (uint8_t) (errval[i] << (8 - bitpos)); 20762306a36Sopenharmony_ci parity ^= val; 20862306a36Sopenharmony_ci if (index < SECTOR_SIZE) 20962306a36Sopenharmony_ci data[index] ^= val; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci /* If the parity is wrong, no rescue possible */ 21462306a36Sopenharmony_ci return parity ? -EBADMSG : nerr; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void DoC_Delay(struct doc_priv *doc, unsigned short cycles) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci volatile char __always_unused dummy; 22062306a36Sopenharmony_ci int i; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci for (i = 0; i < cycles; i++) { 22362306a36Sopenharmony_ci if (DoC_is_Millennium(doc)) 22462306a36Sopenharmony_ci dummy = ReadDOC(doc->virtadr, NOP); 22562306a36Sopenharmony_ci else if (DoC_is_MillenniumPlus(doc)) 22662306a36Sopenharmony_ci dummy = ReadDOC(doc->virtadr, Mplus_NOP); 22762306a36Sopenharmony_ci else 22862306a36Sopenharmony_ci dummy = ReadDOC(doc->virtadr, DOCStatus); 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ 23662306a36Sopenharmony_cistatic int _DoC_WaitReady(struct doc_priv *doc) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 23962306a36Sopenharmony_ci unsigned long timeo = jiffies + (HZ * 10); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (debug) 24262306a36Sopenharmony_ci printk("_DoC_WaitReady...\n"); 24362306a36Sopenharmony_ci /* Out-of-line routine to wait for chip response */ 24462306a36Sopenharmony_ci if (DoC_is_MillenniumPlus(doc)) { 24562306a36Sopenharmony_ci while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { 24662306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 24762306a36Sopenharmony_ci printk("_DoC_WaitReady timed out.\n"); 24862306a36Sopenharmony_ci return -EIO; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci udelay(1); 25162306a36Sopenharmony_ci cond_resched(); 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci } else { 25462306a36Sopenharmony_ci while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { 25562306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 25662306a36Sopenharmony_ci printk("_DoC_WaitReady timed out.\n"); 25762306a36Sopenharmony_ci return -EIO; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci udelay(1); 26062306a36Sopenharmony_ci cond_resched(); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic inline int DoC_WaitReady(struct doc_priv *doc) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 27062306a36Sopenharmony_ci int ret = 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (DoC_is_MillenniumPlus(doc)) { 27362306a36Sopenharmony_ci DoC_Delay(doc, 4); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) 27662306a36Sopenharmony_ci /* Call the out-of-line routine to wait */ 27762306a36Sopenharmony_ci ret = _DoC_WaitReady(doc); 27862306a36Sopenharmony_ci } else { 27962306a36Sopenharmony_ci DoC_Delay(doc, 4); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) 28262306a36Sopenharmony_ci /* Call the out-of-line routine to wait */ 28362306a36Sopenharmony_ci ret = _DoC_WaitReady(doc); 28462306a36Sopenharmony_ci DoC_Delay(doc, 2); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (debug) 28862306a36Sopenharmony_ci printk("DoC_WaitReady OK\n"); 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void doc2000_write_byte(struct nand_chip *this, u_char datum) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 29562306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (debug) 29862306a36Sopenharmony_ci printk("write_byte %02x\n", datum); 29962306a36Sopenharmony_ci WriteDOC(datum, docptr, CDSNSlowIO); 30062306a36Sopenharmony_ci WriteDOC(datum, docptr, 2k_CDSN_IO); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic void doc2000_writebuf(struct nand_chip *this, const u_char *buf, 30462306a36Sopenharmony_ci int len) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 30762306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 30862306a36Sopenharmony_ci int i; 30962306a36Sopenharmony_ci if (debug) 31062306a36Sopenharmony_ci printk("writebuf of %d bytes: ", len); 31162306a36Sopenharmony_ci for (i = 0; i < len; i++) { 31262306a36Sopenharmony_ci WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); 31362306a36Sopenharmony_ci if (debug && i < 16) 31462306a36Sopenharmony_ci printk("%02x ", buf[i]); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci if (debug) 31762306a36Sopenharmony_ci printk("\n"); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void doc2000_readbuf(struct nand_chip *this, u_char *buf, int len) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 32362306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 32462306a36Sopenharmony_ci u32 *buf32 = (u32 *)buf; 32562306a36Sopenharmony_ci int i; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (debug) 32862306a36Sopenharmony_ci printk("readbuf of %d bytes: ", len); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!doc->supports_32b_reads || 33162306a36Sopenharmony_ci ((((unsigned long)buf) | len) & 3)) { 33262306a36Sopenharmony_ci for (i = 0; i < len; i++) 33362306a36Sopenharmony_ci buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); 33462306a36Sopenharmony_ci } else { 33562306a36Sopenharmony_ci for (i = 0; i < len / 4; i++) 33662306a36Sopenharmony_ci buf32[i] = readl(docptr + DoC_2k_CDSN_IO + i); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci/* 34162306a36Sopenharmony_ci * We need our own readid() here because it's called before the NAND chip 34262306a36Sopenharmony_ci * has been initialized, and calling nand_op_readid() would lead to a NULL 34362306a36Sopenharmony_ci * pointer exception when dereferencing the NAND timings. 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_cistatic void doc200x_readid(struct nand_chip *this, unsigned int cs, u8 *id) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci u8 addr = 0; 34862306a36Sopenharmony_ci struct nand_op_instr instrs[] = { 34962306a36Sopenharmony_ci NAND_OP_CMD(NAND_CMD_READID, 0), 35062306a36Sopenharmony_ci NAND_OP_ADDR(1, &addr, 50), 35162306a36Sopenharmony_ci NAND_OP_8BIT_DATA_IN(2, id, 0), 35262306a36Sopenharmony_ci }; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci struct nand_operation op = NAND_OPERATION(cs, instrs); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!id) 35762306a36Sopenharmony_ci op.ninstrs--; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci this->controller->ops->exec_op(this, &op, false); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 36562306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 36662306a36Sopenharmony_ci uint16_t ret; 36762306a36Sopenharmony_ci u8 id[2]; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci doc200x_readid(this, nr, id); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci ret = ((u16)id[0] << 8) | id[1]; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { 37462306a36Sopenharmony_ci /* First chip probe. See if we get same results by 32-bit access */ 37562306a36Sopenharmony_ci union { 37662306a36Sopenharmony_ci uint32_t dword; 37762306a36Sopenharmony_ci uint8_t byte[4]; 37862306a36Sopenharmony_ci } ident; 37962306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci doc200x_readid(this, nr, NULL); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ident.dword = readl(docptr + DoC_2k_CDSN_IO); 38462306a36Sopenharmony_ci if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { 38562306a36Sopenharmony_ci pr_info("DiskOnChip 2000 responds to DWORD access\n"); 38662306a36Sopenharmony_ci doc->supports_32b_reads = true; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void __init doc2000_count_chips(struct mtd_info *mtd) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 39662306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 39762306a36Sopenharmony_ci uint16_t mfrid; 39862306a36Sopenharmony_ci int i; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Max 4 chips per floor on DiskOnChip 2000 */ 40162306a36Sopenharmony_ci doc->chips_per_floor = 4; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Find out what the first chip is */ 40462306a36Sopenharmony_ci mfrid = doc200x_ident_chip(mtd, 0); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* Find how many chips in each floor. */ 40762306a36Sopenharmony_ci for (i = 1; i < 4; i++) { 40862306a36Sopenharmony_ci if (doc200x_ident_chip(mtd, i) != mfrid) 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci doc->chips_per_floor = i; 41262306a36Sopenharmony_ci pr_debug("Detected %d chips per floor.\n", i); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void doc2001_write_byte(struct nand_chip *this, u_char datum) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 41862306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci WriteDOC(datum, docptr, CDSNSlowIO); 42162306a36Sopenharmony_ci WriteDOC(datum, docptr, Mil_CDSN_IO); 42262306a36Sopenharmony_ci WriteDOC(datum, docptr, WritePipeTerm); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic void doc2001_writebuf(struct nand_chip *this, const u_char *buf, int len) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 42862306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 42962306a36Sopenharmony_ci int i; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci for (i = 0; i < len; i++) 43262306a36Sopenharmony_ci WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); 43362306a36Sopenharmony_ci /* Terminate write pipeline */ 43462306a36Sopenharmony_ci WriteDOC(0x00, docptr, WritePipeTerm); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void doc2001_readbuf(struct nand_chip *this, u_char *buf, int len) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 44062306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 44162306a36Sopenharmony_ci int i; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Start read pipeline */ 44462306a36Sopenharmony_ci ReadDOC(docptr, ReadPipeInit); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci for (i = 0; i < len - 1; i++) 44762306a36Sopenharmony_ci buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff)); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Terminate read pipeline */ 45062306a36Sopenharmony_ci buf[i] = ReadDOC(docptr, LastDataRead); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void doc2001plus_writebuf(struct nand_chip *this, const u_char *buf, int len) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 45662306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 45762306a36Sopenharmony_ci int i; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (debug) 46062306a36Sopenharmony_ci printk("writebuf of %d bytes: ", len); 46162306a36Sopenharmony_ci for (i = 0; i < len; i++) { 46262306a36Sopenharmony_ci WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); 46362306a36Sopenharmony_ci if (debug && i < 16) 46462306a36Sopenharmony_ci printk("%02x ", buf[i]); 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci if (debug) 46762306a36Sopenharmony_ci printk("\n"); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic void doc2001plus_readbuf(struct nand_chip *this, u_char *buf, int len) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 47362306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 47462306a36Sopenharmony_ci int i; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (debug) 47762306a36Sopenharmony_ci printk("readbuf of %d bytes: ", len); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* Start read pipeline */ 48062306a36Sopenharmony_ci ReadDOC(docptr, Mplus_ReadPipeInit); 48162306a36Sopenharmony_ci ReadDOC(docptr, Mplus_ReadPipeInit); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci for (i = 0; i < len - 2; i++) { 48462306a36Sopenharmony_ci buf[i] = ReadDOC(docptr, Mil_CDSN_IO); 48562306a36Sopenharmony_ci if (debug && i < 16) 48662306a36Sopenharmony_ci printk("%02x ", buf[i]); 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Terminate read pipeline */ 49062306a36Sopenharmony_ci if (len >= 2) { 49162306a36Sopenharmony_ci buf[len - 2] = ReadDOC(docptr, Mplus_LastDataRead); 49262306a36Sopenharmony_ci if (debug && i < 16) 49362306a36Sopenharmony_ci printk("%02x ", buf[len - 2]); 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead); 49762306a36Sopenharmony_ci if (debug && i < 16) 49862306a36Sopenharmony_ci printk("%02x ", buf[len - 1]); 49962306a36Sopenharmony_ci if (debug) 50062306a36Sopenharmony_ci printk("\n"); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void doc200x_write_control(struct doc_priv *doc, u8 value) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci WriteDOC(value, doc->virtadr, CDSNControl); 50662306a36Sopenharmony_ci /* 11.4.3 -- 4 NOPs after CSDNControl write */ 50762306a36Sopenharmony_ci DoC_Delay(doc, 4); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic void doc200x_exec_instr(struct nand_chip *this, 51162306a36Sopenharmony_ci const struct nand_op_instr *instr) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 51462306a36Sopenharmony_ci unsigned int i; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci switch (instr->type) { 51762306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 51862306a36Sopenharmony_ci doc200x_write_control(doc, CDSN_CTRL_CE | CDSN_CTRL_CLE); 51962306a36Sopenharmony_ci doc2000_write_byte(this, instr->ctx.cmd.opcode); 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 52362306a36Sopenharmony_ci doc200x_write_control(doc, CDSN_CTRL_CE | CDSN_CTRL_ALE); 52462306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) { 52562306a36Sopenharmony_ci u8 addr = instr->ctx.addr.addrs[i]; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (DoC_is_2000(doc)) 52862306a36Sopenharmony_ci doc2000_write_byte(this, addr); 52962306a36Sopenharmony_ci else 53062306a36Sopenharmony_ci doc2001_write_byte(this, addr); 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 53562306a36Sopenharmony_ci doc200x_write_control(doc, CDSN_CTRL_CE); 53662306a36Sopenharmony_ci if (DoC_is_2000(doc)) 53762306a36Sopenharmony_ci doc2000_readbuf(this, instr->ctx.data.buf.in, 53862306a36Sopenharmony_ci instr->ctx.data.len); 53962306a36Sopenharmony_ci else 54062306a36Sopenharmony_ci doc2001_readbuf(this, instr->ctx.data.buf.in, 54162306a36Sopenharmony_ci instr->ctx.data.len); 54262306a36Sopenharmony_ci break; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 54562306a36Sopenharmony_ci doc200x_write_control(doc, CDSN_CTRL_CE); 54662306a36Sopenharmony_ci if (DoC_is_2000(doc)) 54762306a36Sopenharmony_ci doc2000_writebuf(this, instr->ctx.data.buf.out, 54862306a36Sopenharmony_ci instr->ctx.data.len); 54962306a36Sopenharmony_ci else 55062306a36Sopenharmony_ci doc2001_writebuf(this, instr->ctx.data.buf.out, 55162306a36Sopenharmony_ci instr->ctx.data.len); 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 55562306a36Sopenharmony_ci DoC_WaitReady(doc); 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (instr->delay_ns) 56062306a36Sopenharmony_ci ndelay(instr->delay_ns); 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic int doc200x_exec_op(struct nand_chip *this, 56462306a36Sopenharmony_ci const struct nand_operation *op, 56562306a36Sopenharmony_ci bool check_only) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 56862306a36Sopenharmony_ci unsigned int i; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (check_only) 57162306a36Sopenharmony_ci return true; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci doc->curchip = op->cs % doc->chips_per_floor; 57462306a36Sopenharmony_ci doc->curfloor = op->cs / doc->chips_per_floor; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci WriteDOC(doc->curfloor, doc->virtadr, FloorSelect); 57762306a36Sopenharmony_ci WriteDOC(doc->curchip, doc->virtadr, CDSNDeviceSelect); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Assert CE pin */ 58062306a36Sopenharmony_ci doc200x_write_control(doc, CDSN_CTRL_CE); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) 58362306a36Sopenharmony_ci doc200x_exec_instr(this, &op->instrs[i]); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* De-assert CE pin */ 58662306a36Sopenharmony_ci doc200x_write_control(doc, 0); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void doc2001plus_write_pipe_term(struct doc_priv *doc) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci WriteDOC(0x00, doc->virtadr, Mplus_WritePipeTerm); 59462306a36Sopenharmony_ci WriteDOC(0x00, doc->virtadr, Mplus_WritePipeTerm); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic void doc2001plus_exec_instr(struct nand_chip *this, 59862306a36Sopenharmony_ci const struct nand_op_instr *instr) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 60162306a36Sopenharmony_ci unsigned int i; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci switch (instr->type) { 60462306a36Sopenharmony_ci case NAND_OP_CMD_INSTR: 60562306a36Sopenharmony_ci WriteDOC(instr->ctx.cmd.opcode, doc->virtadr, Mplus_FlashCmd); 60662306a36Sopenharmony_ci doc2001plus_write_pipe_term(doc); 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci case NAND_OP_ADDR_INSTR: 61062306a36Sopenharmony_ci for (i = 0; i < instr->ctx.addr.naddrs; i++) { 61162306a36Sopenharmony_ci u8 addr = instr->ctx.addr.addrs[i]; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci WriteDOC(addr, doc->virtadr, Mplus_FlashAddress); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci doc2001plus_write_pipe_term(doc); 61662306a36Sopenharmony_ci /* deassert ALE */ 61762306a36Sopenharmony_ci WriteDOC(0, doc->virtadr, Mplus_FlashControl); 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci case NAND_OP_DATA_IN_INSTR: 62162306a36Sopenharmony_ci doc2001plus_readbuf(this, instr->ctx.data.buf.in, 62262306a36Sopenharmony_ci instr->ctx.data.len); 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci case NAND_OP_DATA_OUT_INSTR: 62562306a36Sopenharmony_ci doc2001plus_writebuf(this, instr->ctx.data.buf.out, 62662306a36Sopenharmony_ci instr->ctx.data.len); 62762306a36Sopenharmony_ci doc2001plus_write_pipe_term(doc); 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci case NAND_OP_WAITRDY_INSTR: 63062306a36Sopenharmony_ci DoC_WaitReady(doc); 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (instr->delay_ns) 63562306a36Sopenharmony_ci ndelay(instr->delay_ns); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic int doc2001plus_exec_op(struct nand_chip *this, 63962306a36Sopenharmony_ci const struct nand_operation *op, 64062306a36Sopenharmony_ci bool check_only) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 64362306a36Sopenharmony_ci unsigned int i; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (check_only) 64662306a36Sopenharmony_ci return true; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci doc->curchip = op->cs % doc->chips_per_floor; 64962306a36Sopenharmony_ci doc->curfloor = op->cs / doc->chips_per_floor; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* Assert ChipEnable and deassert WriteProtect */ 65262306a36Sopenharmony_ci WriteDOC(DOC_FLASH_CE, doc->virtadr, Mplus_FlashSelect); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci for (i = 0; i < op->ninstrs; i++) 65562306a36Sopenharmony_ci doc2001plus_exec_instr(this, &op->instrs[i]); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* De-assert ChipEnable */ 65862306a36Sopenharmony_ci WriteDOC(0, doc->virtadr, Mplus_FlashSelect); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return 0; 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_cistatic void doc200x_enable_hwecc(struct nand_chip *this, int mode) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 66662306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Prime the ECC engine */ 66962306a36Sopenharmony_ci switch (mode) { 67062306a36Sopenharmony_ci case NAND_ECC_READ: 67162306a36Sopenharmony_ci WriteDOC(DOC_ECC_RESET, docptr, ECCConf); 67262306a36Sopenharmony_ci WriteDOC(DOC_ECC_EN, docptr, ECCConf); 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci case NAND_ECC_WRITE: 67562306a36Sopenharmony_ci WriteDOC(DOC_ECC_RESET, docptr, ECCConf); 67662306a36Sopenharmony_ci WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf); 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void doc2001plus_enable_hwecc(struct nand_chip *this, int mode) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 68462306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* Prime the ECC engine */ 68762306a36Sopenharmony_ci switch (mode) { 68862306a36Sopenharmony_ci case NAND_ECC_READ: 68962306a36Sopenharmony_ci WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 69062306a36Sopenharmony_ci WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci case NAND_ECC_WRITE: 69362306a36Sopenharmony_ci WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); 69462306a36Sopenharmony_ci WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); 69562306a36Sopenharmony_ci break; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* This code is only called on write */ 70062306a36Sopenharmony_cistatic int doc200x_calculate_ecc(struct nand_chip *this, const u_char *dat, 70162306a36Sopenharmony_ci unsigned char *ecc_code) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 70462306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 70562306a36Sopenharmony_ci int i; 70662306a36Sopenharmony_ci int __always_unused emptymatch = 1; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* flush the pipeline */ 70962306a36Sopenharmony_ci if (DoC_is_2000(doc)) { 71062306a36Sopenharmony_ci WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl); 71162306a36Sopenharmony_ci WriteDOC(0, docptr, 2k_CDSN_IO); 71262306a36Sopenharmony_ci WriteDOC(0, docptr, 2k_CDSN_IO); 71362306a36Sopenharmony_ci WriteDOC(0, docptr, 2k_CDSN_IO); 71462306a36Sopenharmony_ci WriteDOC(doc->CDSNControl, docptr, CDSNControl); 71562306a36Sopenharmony_ci } else if (DoC_is_MillenniumPlus(doc)) { 71662306a36Sopenharmony_ci WriteDOC(0, docptr, Mplus_NOP); 71762306a36Sopenharmony_ci WriteDOC(0, docptr, Mplus_NOP); 71862306a36Sopenharmony_ci WriteDOC(0, docptr, Mplus_NOP); 71962306a36Sopenharmony_ci } else { 72062306a36Sopenharmony_ci WriteDOC(0, docptr, NOP); 72162306a36Sopenharmony_ci WriteDOC(0, docptr, NOP); 72262306a36Sopenharmony_ci WriteDOC(0, docptr, NOP); 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 72662306a36Sopenharmony_ci if (DoC_is_MillenniumPlus(doc)) 72762306a36Sopenharmony_ci ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); 72862306a36Sopenharmony_ci else 72962306a36Sopenharmony_ci ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); 73062306a36Sopenharmony_ci if (ecc_code[i] != empty_write_ecc[i]) 73162306a36Sopenharmony_ci emptymatch = 0; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci if (DoC_is_MillenniumPlus(doc)) 73462306a36Sopenharmony_ci WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); 73562306a36Sopenharmony_ci else 73662306a36Sopenharmony_ci WriteDOC(DOC_ECC_DIS, docptr, ECCConf); 73762306a36Sopenharmony_ci#if 0 73862306a36Sopenharmony_ci /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ 73962306a36Sopenharmony_ci if (emptymatch) { 74062306a36Sopenharmony_ci /* Note: this somewhat expensive test should not be triggered 74162306a36Sopenharmony_ci often. It could be optimized away by examining the data in 74262306a36Sopenharmony_ci the writebuf routine, and remembering the result. */ 74362306a36Sopenharmony_ci for (i = 0; i < 512; i++) { 74462306a36Sopenharmony_ci if (dat[i] == 0xff) 74562306a36Sopenharmony_ci continue; 74662306a36Sopenharmony_ci emptymatch = 0; 74762306a36Sopenharmony_ci break; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci } 75062306a36Sopenharmony_ci /* If emptymatch still =1, we do have an all-0xff data buffer. 75162306a36Sopenharmony_ci Return all-0xff ecc value instead of the computed one, so 75262306a36Sopenharmony_ci it'll look just like a freshly-erased page. */ 75362306a36Sopenharmony_ci if (emptymatch) 75462306a36Sopenharmony_ci memset(ecc_code, 0xff, 6); 75562306a36Sopenharmony_ci#endif 75662306a36Sopenharmony_ci return 0; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int doc200x_correct_data(struct nand_chip *this, u_char *dat, 76062306a36Sopenharmony_ci u_char *read_ecc, u_char *isnull) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci int i, ret = 0; 76362306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 76462306a36Sopenharmony_ci void __iomem *docptr = doc->virtadr; 76562306a36Sopenharmony_ci uint8_t calc_ecc[6]; 76662306a36Sopenharmony_ci volatile u_char dummy; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* flush the pipeline */ 76962306a36Sopenharmony_ci if (DoC_is_2000(doc)) { 77062306a36Sopenharmony_ci dummy = ReadDOC(docptr, 2k_ECCStatus); 77162306a36Sopenharmony_ci dummy = ReadDOC(docptr, 2k_ECCStatus); 77262306a36Sopenharmony_ci dummy = ReadDOC(docptr, 2k_ECCStatus); 77362306a36Sopenharmony_ci } else if (DoC_is_MillenniumPlus(doc)) { 77462306a36Sopenharmony_ci dummy = ReadDOC(docptr, Mplus_ECCConf); 77562306a36Sopenharmony_ci dummy = ReadDOC(docptr, Mplus_ECCConf); 77662306a36Sopenharmony_ci dummy = ReadDOC(docptr, Mplus_ECCConf); 77762306a36Sopenharmony_ci } else { 77862306a36Sopenharmony_ci dummy = ReadDOC(docptr, ECCConf); 77962306a36Sopenharmony_ci dummy = ReadDOC(docptr, ECCConf); 78062306a36Sopenharmony_ci dummy = ReadDOC(docptr, ECCConf); 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Error occurred ? */ 78462306a36Sopenharmony_ci if (dummy & 0x80) { 78562306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 78662306a36Sopenharmony_ci if (DoC_is_MillenniumPlus(doc)) 78762306a36Sopenharmony_ci calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); 78862306a36Sopenharmony_ci else 78962306a36Sopenharmony_ci calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci ret = doc_ecc_decode(doc->rs_decoder, dat, calc_ecc); 79362306a36Sopenharmony_ci if (ret > 0) 79462306a36Sopenharmony_ci pr_err("doc200x_correct_data corrected %d errors\n", 79562306a36Sopenharmony_ci ret); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci if (DoC_is_MillenniumPlus(doc)) 79862306a36Sopenharmony_ci WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); 79962306a36Sopenharmony_ci else 80062306a36Sopenharmony_ci WriteDOC(DOC_ECC_DIS, docptr, ECCConf); 80162306a36Sopenharmony_ci if (no_ecc_failures && mtd_is_eccerr(ret)) { 80262306a36Sopenharmony_ci pr_err("suppressing ECC failure\n"); 80362306a36Sopenharmony_ci ret = 0; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci return ret; 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci//u_char mydatabuf[528]; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic int doc200x_ooblayout_ecc(struct mtd_info *mtd, int section, 81162306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci if (section) 81462306a36Sopenharmony_ci return -ERANGE; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci oobregion->offset = 0; 81762306a36Sopenharmony_ci oobregion->length = 6; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci return 0; 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int doc200x_ooblayout_free(struct mtd_info *mtd, int section, 82362306a36Sopenharmony_ci struct mtd_oob_region *oobregion) 82462306a36Sopenharmony_ci{ 82562306a36Sopenharmony_ci if (section > 1) 82662306a36Sopenharmony_ci return -ERANGE; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* 82962306a36Sopenharmony_ci * The strange out-of-order free bytes definition is a (possibly 83062306a36Sopenharmony_ci * unneeded) attempt to retain compatibility. It used to read: 83162306a36Sopenharmony_ci * .oobfree = { {8, 8} } 83262306a36Sopenharmony_ci * Since that leaves two bytes unusable, it was changed. But the 83362306a36Sopenharmony_ci * following scheme might affect existing jffs2 installs by moving the 83462306a36Sopenharmony_ci * cleanmarker: 83562306a36Sopenharmony_ci * .oobfree = { {6, 10} } 83662306a36Sopenharmony_ci * jffs2 seems to handle the above gracefully, but the current scheme 83762306a36Sopenharmony_ci * seems safer. The only problem with it is that any code retrieving 83862306a36Sopenharmony_ci * free bytes position must be able to handle out-of-order segments. 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci if (!section) { 84162306a36Sopenharmony_ci oobregion->offset = 8; 84262306a36Sopenharmony_ci oobregion->length = 8; 84362306a36Sopenharmony_ci } else { 84462306a36Sopenharmony_ci oobregion->offset = 6; 84562306a36Sopenharmony_ci oobregion->length = 2; 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic const struct mtd_ooblayout_ops doc200x_ooblayout_ops = { 85262306a36Sopenharmony_ci .ecc = doc200x_ooblayout_ecc, 85362306a36Sopenharmony_ci .free = doc200x_ooblayout_free, 85462306a36Sopenharmony_ci}; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci/* Find the (I)NFTL Media Header, and optionally also the mirror media header. 85762306a36Sopenharmony_ci On successful return, buf will contain a copy of the media header for 85862306a36Sopenharmony_ci further processing. id is the string to scan for, and will presumably be 85962306a36Sopenharmony_ci either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media 86062306a36Sopenharmony_ci header. The page #s of the found media headers are placed in mh0_page and 86162306a36Sopenharmony_ci mh1_page in the DOC private structure. */ 86262306a36Sopenharmony_cistatic int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror) 86362306a36Sopenharmony_ci{ 86462306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 86562306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 86662306a36Sopenharmony_ci unsigned offs; 86762306a36Sopenharmony_ci int ret; 86862306a36Sopenharmony_ci size_t retlen; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { 87162306a36Sopenharmony_ci ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); 87262306a36Sopenharmony_ci if (retlen != mtd->writesize) 87362306a36Sopenharmony_ci continue; 87462306a36Sopenharmony_ci if (ret) { 87562306a36Sopenharmony_ci pr_warn("ECC error scanning DOC at 0x%x\n", offs); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci if (memcmp(buf, id, 6)) 87862306a36Sopenharmony_ci continue; 87962306a36Sopenharmony_ci pr_info("Found DiskOnChip %s Media Header at 0x%x\n", id, offs); 88062306a36Sopenharmony_ci if (doc->mh0_page == -1) { 88162306a36Sopenharmony_ci doc->mh0_page = offs >> this->page_shift; 88262306a36Sopenharmony_ci if (!findmirror) 88362306a36Sopenharmony_ci return 1; 88462306a36Sopenharmony_ci continue; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci doc->mh1_page = offs >> this->page_shift; 88762306a36Sopenharmony_ci return 2; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci if (doc->mh0_page == -1) { 89062306a36Sopenharmony_ci pr_warn("DiskOnChip %s Media Header not found.\n", id); 89162306a36Sopenharmony_ci return 0; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci /* Only one mediaheader was found. We want buf to contain a 89462306a36Sopenharmony_ci mediaheader on return, so we'll have to re-read the one we found. */ 89562306a36Sopenharmony_ci offs = doc->mh0_page << this->page_shift; 89662306a36Sopenharmony_ci ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); 89762306a36Sopenharmony_ci if (retlen != mtd->writesize) { 89862306a36Sopenharmony_ci /* Insanity. Give up. */ 89962306a36Sopenharmony_ci pr_err("Read DiskOnChip Media Header once, but can't reread it???\n"); 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci return 1; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 90862306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 90962306a36Sopenharmony_ci struct nand_memory_organization *memorg; 91062306a36Sopenharmony_ci int ret = 0; 91162306a36Sopenharmony_ci u_char *buf; 91262306a36Sopenharmony_ci struct NFTLMediaHeader *mh; 91362306a36Sopenharmony_ci const unsigned psize = 1 << this->page_shift; 91462306a36Sopenharmony_ci int numparts = 0; 91562306a36Sopenharmony_ci unsigned blocks, maxblocks; 91662306a36Sopenharmony_ci int offs, numheaders; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci memorg = nanddev_get_memorg(&this->base); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci buf = kmalloc(mtd->writesize, GFP_KERNEL); 92162306a36Sopenharmony_ci if (!buf) { 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci } 92462306a36Sopenharmony_ci if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1))) 92562306a36Sopenharmony_ci goto out; 92662306a36Sopenharmony_ci mh = (struct NFTLMediaHeader *)buf; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci le16_to_cpus(&mh->NumEraseUnits); 92962306a36Sopenharmony_ci le16_to_cpus(&mh->FirstPhysicalEUN); 93062306a36Sopenharmony_ci le32_to_cpus(&mh->FormattedSize); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci pr_info(" DataOrgID = %s\n" 93362306a36Sopenharmony_ci " NumEraseUnits = %d\n" 93462306a36Sopenharmony_ci " FirstPhysicalEUN = %d\n" 93562306a36Sopenharmony_ci " FormattedSize = %d\n" 93662306a36Sopenharmony_ci " UnitSizeFactor = %d\n", 93762306a36Sopenharmony_ci mh->DataOrgID, mh->NumEraseUnits, 93862306a36Sopenharmony_ci mh->FirstPhysicalEUN, mh->FormattedSize, 93962306a36Sopenharmony_ci mh->UnitSizeFactor); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci blocks = mtd->size >> this->phys_erase_shift; 94262306a36Sopenharmony_ci maxblocks = min(32768U, mtd->erasesize - psize); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (mh->UnitSizeFactor == 0x00) { 94562306a36Sopenharmony_ci /* Auto-determine UnitSizeFactor. The constraints are: 94662306a36Sopenharmony_ci - There can be at most 32768 virtual blocks. 94762306a36Sopenharmony_ci - There can be at most (virtual block size - page size) 94862306a36Sopenharmony_ci virtual blocks (because MediaHeader+BBT must fit in 1). 94962306a36Sopenharmony_ci */ 95062306a36Sopenharmony_ci mh->UnitSizeFactor = 0xff; 95162306a36Sopenharmony_ci while (blocks > maxblocks) { 95262306a36Sopenharmony_ci blocks >>= 1; 95362306a36Sopenharmony_ci maxblocks = min(32768U, (maxblocks << 1) + psize); 95462306a36Sopenharmony_ci mh->UnitSizeFactor--; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci pr_warn("UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor); 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* NOTE: The lines below modify internal variables of the NAND and MTD 96062306a36Sopenharmony_ci layers; variables with have already been configured by nand_scan. 96162306a36Sopenharmony_ci Unfortunately, we didn't know before this point what these values 96262306a36Sopenharmony_ci should be. Thus, this code is somewhat dependent on the exact 96362306a36Sopenharmony_ci implementation of the NAND layer. */ 96462306a36Sopenharmony_ci if (mh->UnitSizeFactor != 0xff) { 96562306a36Sopenharmony_ci this->bbt_erase_shift += (0xff - mh->UnitSizeFactor); 96662306a36Sopenharmony_ci memorg->pages_per_eraseblock <<= (0xff - mh->UnitSizeFactor); 96762306a36Sopenharmony_ci mtd->erasesize <<= (0xff - mh->UnitSizeFactor); 96862306a36Sopenharmony_ci pr_info("Setting virtual erase size to %d\n", mtd->erasesize); 96962306a36Sopenharmony_ci blocks = mtd->size >> this->bbt_erase_shift; 97062306a36Sopenharmony_ci maxblocks = min(32768U, mtd->erasesize - psize); 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (blocks > maxblocks) { 97462306a36Sopenharmony_ci pr_err("UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); 97562306a36Sopenharmony_ci goto out; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci /* Skip past the media headers. */ 97962306a36Sopenharmony_ci offs = max(doc->mh0_page, doc->mh1_page); 98062306a36Sopenharmony_ci offs <<= this->page_shift; 98162306a36Sopenharmony_ci offs += mtd->erasesize; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (show_firmware_partition == 1) { 98462306a36Sopenharmony_ci parts[0].name = " DiskOnChip Firmware / Media Header partition"; 98562306a36Sopenharmony_ci parts[0].offset = 0; 98662306a36Sopenharmony_ci parts[0].size = offs; 98762306a36Sopenharmony_ci numparts = 1; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci parts[numparts].name = " DiskOnChip BDTL partition"; 99162306a36Sopenharmony_ci parts[numparts].offset = offs; 99262306a36Sopenharmony_ci parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci offs += parts[numparts].size; 99562306a36Sopenharmony_ci numparts++; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (offs < mtd->size) { 99862306a36Sopenharmony_ci parts[numparts].name = " DiskOnChip Remainder partition"; 99962306a36Sopenharmony_ci parts[numparts].offset = offs; 100062306a36Sopenharmony_ci parts[numparts].size = mtd->size - offs; 100162306a36Sopenharmony_ci numparts++; 100262306a36Sopenharmony_ci } 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci ret = numparts; 100562306a36Sopenharmony_ci out: 100662306a36Sopenharmony_ci kfree(buf); 100762306a36Sopenharmony_ci return ret; 100862306a36Sopenharmony_ci} 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci/* This is a stripped-down copy of the code in inftlmount.c */ 101162306a36Sopenharmony_cistatic inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) 101262306a36Sopenharmony_ci{ 101362306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 101462306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 101562306a36Sopenharmony_ci int ret = 0; 101662306a36Sopenharmony_ci u_char *buf; 101762306a36Sopenharmony_ci struct INFTLMediaHeader *mh; 101862306a36Sopenharmony_ci struct INFTLPartition *ip; 101962306a36Sopenharmony_ci int numparts = 0; 102062306a36Sopenharmony_ci int blocks; 102162306a36Sopenharmony_ci int vshift, lastvunit = 0; 102262306a36Sopenharmony_ci int i; 102362306a36Sopenharmony_ci int end = mtd->size; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (inftl_bbt_write) 102662306a36Sopenharmony_ci end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci buf = kmalloc(mtd->writesize, GFP_KERNEL); 102962306a36Sopenharmony_ci if (!buf) { 103062306a36Sopenharmony_ci return 0; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci if (!find_media_headers(mtd, buf, "BNAND", 0)) 103462306a36Sopenharmony_ci goto out; 103562306a36Sopenharmony_ci doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); 103662306a36Sopenharmony_ci mh = (struct INFTLMediaHeader *)buf; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci le32_to_cpus(&mh->NoOfBootImageBlocks); 103962306a36Sopenharmony_ci le32_to_cpus(&mh->NoOfBinaryPartitions); 104062306a36Sopenharmony_ci le32_to_cpus(&mh->NoOfBDTLPartitions); 104162306a36Sopenharmony_ci le32_to_cpus(&mh->BlockMultiplierBits); 104262306a36Sopenharmony_ci le32_to_cpus(&mh->FormatFlags); 104362306a36Sopenharmony_ci le32_to_cpus(&mh->PercentUsed); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci pr_info(" bootRecordID = %s\n" 104662306a36Sopenharmony_ci " NoOfBootImageBlocks = %d\n" 104762306a36Sopenharmony_ci " NoOfBinaryPartitions = %d\n" 104862306a36Sopenharmony_ci " NoOfBDTLPartitions = %d\n" 104962306a36Sopenharmony_ci " BlockMultiplierBits = %d\n" 105062306a36Sopenharmony_ci " FormatFlgs = %d\n" 105162306a36Sopenharmony_ci " OsakVersion = %d.%d.%d.%d\n" 105262306a36Sopenharmony_ci " PercentUsed = %d\n", 105362306a36Sopenharmony_ci mh->bootRecordID, mh->NoOfBootImageBlocks, 105462306a36Sopenharmony_ci mh->NoOfBinaryPartitions, 105562306a36Sopenharmony_ci mh->NoOfBDTLPartitions, 105662306a36Sopenharmony_ci mh->BlockMultiplierBits, mh->FormatFlags, 105762306a36Sopenharmony_ci ((unsigned char *) &mh->OsakVersion)[0] & 0xf, 105862306a36Sopenharmony_ci ((unsigned char *) &mh->OsakVersion)[1] & 0xf, 105962306a36Sopenharmony_ci ((unsigned char *) &mh->OsakVersion)[2] & 0xf, 106062306a36Sopenharmony_ci ((unsigned char *) &mh->OsakVersion)[3] & 0xf, 106162306a36Sopenharmony_ci mh->PercentUsed); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci vshift = this->phys_erase_shift + mh->BlockMultiplierBits; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci blocks = mtd->size >> vshift; 106662306a36Sopenharmony_ci if (blocks > 32768) { 106762306a36Sopenharmony_ci pr_err("BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); 106862306a36Sopenharmony_ci goto out; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); 107262306a36Sopenharmony_ci if (inftl_bbt_write && (blocks > mtd->erasesize)) { 107362306a36Sopenharmony_ci pr_err("Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); 107462306a36Sopenharmony_ci goto out; 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* Scan the partitions */ 107862306a36Sopenharmony_ci for (i = 0; (i < 4); i++) { 107962306a36Sopenharmony_ci ip = &(mh->Partitions[i]); 108062306a36Sopenharmony_ci le32_to_cpus(&ip->virtualUnits); 108162306a36Sopenharmony_ci le32_to_cpus(&ip->firstUnit); 108262306a36Sopenharmony_ci le32_to_cpus(&ip->lastUnit); 108362306a36Sopenharmony_ci le32_to_cpus(&ip->flags); 108462306a36Sopenharmony_ci le32_to_cpus(&ip->spareUnits); 108562306a36Sopenharmony_ci le32_to_cpus(&ip->Reserved0); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci pr_info(" PARTITION[%d] ->\n" 108862306a36Sopenharmony_ci " virtualUnits = %d\n" 108962306a36Sopenharmony_ci " firstUnit = %d\n" 109062306a36Sopenharmony_ci " lastUnit = %d\n" 109162306a36Sopenharmony_ci " flags = 0x%x\n" 109262306a36Sopenharmony_ci " spareUnits = %d\n", 109362306a36Sopenharmony_ci i, ip->virtualUnits, ip->firstUnit, 109462306a36Sopenharmony_ci ip->lastUnit, ip->flags, 109562306a36Sopenharmony_ci ip->spareUnits); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci if ((show_firmware_partition == 1) && 109862306a36Sopenharmony_ci (i == 0) && (ip->firstUnit > 0)) { 109962306a36Sopenharmony_ci parts[0].name = " DiskOnChip IPL / Media Header partition"; 110062306a36Sopenharmony_ci parts[0].offset = 0; 110162306a36Sopenharmony_ci parts[0].size = mtd->erasesize * ip->firstUnit; 110262306a36Sopenharmony_ci numparts = 1; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (ip->flags & INFTL_BINARY) 110662306a36Sopenharmony_ci parts[numparts].name = " DiskOnChip BDK partition"; 110762306a36Sopenharmony_ci else 110862306a36Sopenharmony_ci parts[numparts].name = " DiskOnChip BDTL partition"; 110962306a36Sopenharmony_ci parts[numparts].offset = ip->firstUnit << vshift; 111062306a36Sopenharmony_ci parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; 111162306a36Sopenharmony_ci numparts++; 111262306a36Sopenharmony_ci if (ip->lastUnit > lastvunit) 111362306a36Sopenharmony_ci lastvunit = ip->lastUnit; 111462306a36Sopenharmony_ci if (ip->flags & INFTL_LAST) 111562306a36Sopenharmony_ci break; 111662306a36Sopenharmony_ci } 111762306a36Sopenharmony_ci lastvunit++; 111862306a36Sopenharmony_ci if ((lastvunit << vshift) < end) { 111962306a36Sopenharmony_ci parts[numparts].name = " DiskOnChip Remainder partition"; 112062306a36Sopenharmony_ci parts[numparts].offset = lastvunit << vshift; 112162306a36Sopenharmony_ci parts[numparts].size = end - parts[numparts].offset; 112262306a36Sopenharmony_ci numparts++; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci ret = numparts; 112562306a36Sopenharmony_ci out: 112662306a36Sopenharmony_ci kfree(buf); 112762306a36Sopenharmony_ci return ret; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic int __init nftl_scan_bbt(struct mtd_info *mtd) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci int ret, numparts; 113362306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 113462306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 113562306a36Sopenharmony_ci struct mtd_partition parts[2]; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci memset((char *)parts, 0, sizeof(parts)); 113862306a36Sopenharmony_ci /* On NFTL, we have to find the media headers before we can read the 113962306a36Sopenharmony_ci BBTs, since they're stored in the media header eraseblocks. */ 114062306a36Sopenharmony_ci numparts = nftl_partscan(mtd, parts); 114162306a36Sopenharmony_ci if (!numparts) 114262306a36Sopenharmony_ci return -EIO; 114362306a36Sopenharmony_ci this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | 114462306a36Sopenharmony_ci NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | 114562306a36Sopenharmony_ci NAND_BBT_VERSION; 114662306a36Sopenharmony_ci this->bbt_td->veroffs = 7; 114762306a36Sopenharmony_ci this->bbt_td->pages[0] = doc->mh0_page + 1; 114862306a36Sopenharmony_ci if (doc->mh1_page != -1) { 114962306a36Sopenharmony_ci this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | 115062306a36Sopenharmony_ci NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | 115162306a36Sopenharmony_ci NAND_BBT_VERSION; 115262306a36Sopenharmony_ci this->bbt_md->veroffs = 7; 115362306a36Sopenharmony_ci this->bbt_md->pages[0] = doc->mh1_page + 1; 115462306a36Sopenharmony_ci } else { 115562306a36Sopenharmony_ci this->bbt_md = NULL; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci ret = nand_create_bbt(this); 115962306a36Sopenharmony_ci if (ret) 116062306a36Sopenharmony_ci return ret; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic int __init inftl_scan_bbt(struct mtd_info *mtd) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci int ret, numparts; 116862306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 116962306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 117062306a36Sopenharmony_ci struct mtd_partition parts[5]; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (nanddev_ntargets(&this->base) > doc->chips_per_floor) { 117362306a36Sopenharmony_ci pr_err("Multi-floor INFTL devices not yet supported.\n"); 117462306a36Sopenharmony_ci return -EIO; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (DoC_is_MillenniumPlus(doc)) { 117862306a36Sopenharmony_ci this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE; 117962306a36Sopenharmony_ci if (inftl_bbt_write) 118062306a36Sopenharmony_ci this->bbt_td->options |= NAND_BBT_WRITE; 118162306a36Sopenharmony_ci this->bbt_td->pages[0] = 2; 118262306a36Sopenharmony_ci this->bbt_md = NULL; 118362306a36Sopenharmony_ci } else { 118462306a36Sopenharmony_ci this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; 118562306a36Sopenharmony_ci if (inftl_bbt_write) 118662306a36Sopenharmony_ci this->bbt_td->options |= NAND_BBT_WRITE; 118762306a36Sopenharmony_ci this->bbt_td->offs = 8; 118862306a36Sopenharmony_ci this->bbt_td->len = 8; 118962306a36Sopenharmony_ci this->bbt_td->veroffs = 7; 119062306a36Sopenharmony_ci this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS; 119162306a36Sopenharmony_ci this->bbt_td->reserved_block_code = 0x01; 119262306a36Sopenharmony_ci this->bbt_td->pattern = "MSYS_BBT"; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION; 119562306a36Sopenharmony_ci if (inftl_bbt_write) 119662306a36Sopenharmony_ci this->bbt_md->options |= NAND_BBT_WRITE; 119762306a36Sopenharmony_ci this->bbt_md->offs = 8; 119862306a36Sopenharmony_ci this->bbt_md->len = 8; 119962306a36Sopenharmony_ci this->bbt_md->veroffs = 7; 120062306a36Sopenharmony_ci this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS; 120162306a36Sopenharmony_ci this->bbt_md->reserved_block_code = 0x01; 120262306a36Sopenharmony_ci this->bbt_md->pattern = "TBB_SYSM"; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci ret = nand_create_bbt(this); 120662306a36Sopenharmony_ci if (ret) 120762306a36Sopenharmony_ci return ret; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci memset((char *)parts, 0, sizeof(parts)); 121062306a36Sopenharmony_ci numparts = inftl_partscan(mtd, parts); 121162306a36Sopenharmony_ci /* At least for now, require the INFTL Media Header. We could probably 121262306a36Sopenharmony_ci do without it for non-INFTL use, since all it gives us is 121362306a36Sopenharmony_ci autopartitioning, but I want to give it more thought. */ 121462306a36Sopenharmony_ci if (!numparts) 121562306a36Sopenharmony_ci return -EIO; 121662306a36Sopenharmony_ci return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts); 121762306a36Sopenharmony_ci} 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_cistatic inline int __init doc2000_init(struct mtd_info *mtd) 122062306a36Sopenharmony_ci{ 122162306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 122262306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci doc->late_init = nftl_scan_bbt; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO; 122762306a36Sopenharmony_ci doc2000_count_chips(mtd); 122862306a36Sopenharmony_ci mtd->name = "DiskOnChip 2000 (NFTL Model)"; 122962306a36Sopenharmony_ci return (4 * doc->chips_per_floor); 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic inline int __init doc2001_init(struct mtd_info *mtd) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 123562306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci ReadDOC(doc->virtadr, ChipID); 123862306a36Sopenharmony_ci ReadDOC(doc->virtadr, ChipID); 123962306a36Sopenharmony_ci ReadDOC(doc->virtadr, ChipID); 124062306a36Sopenharmony_ci if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { 124162306a36Sopenharmony_ci /* It's not a Millennium; it's one of the newer 124262306a36Sopenharmony_ci DiskOnChip 2000 units with a similar ASIC. 124362306a36Sopenharmony_ci Treat it like a Millennium, except that it 124462306a36Sopenharmony_ci can have multiple chips. */ 124562306a36Sopenharmony_ci doc2000_count_chips(mtd); 124662306a36Sopenharmony_ci mtd->name = "DiskOnChip 2000 (INFTL Model)"; 124762306a36Sopenharmony_ci doc->late_init = inftl_scan_bbt; 124862306a36Sopenharmony_ci return (4 * doc->chips_per_floor); 124962306a36Sopenharmony_ci } else { 125062306a36Sopenharmony_ci /* Bog-standard Millennium */ 125162306a36Sopenharmony_ci doc->chips_per_floor = 1; 125262306a36Sopenharmony_ci mtd->name = "DiskOnChip Millennium"; 125362306a36Sopenharmony_ci doc->late_init = nftl_scan_bbt; 125462306a36Sopenharmony_ci return 1; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci} 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_cistatic inline int __init doc2001plus_init(struct mtd_info *mtd) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct nand_chip *this = mtd_to_nand(mtd); 126162306a36Sopenharmony_ci struct doc_priv *doc = nand_get_controller_data(this); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci doc->late_init = inftl_scan_bbt; 126462306a36Sopenharmony_ci this->ecc.hwctl = doc2001plus_enable_hwecc; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci doc->chips_per_floor = 1; 126762306a36Sopenharmony_ci mtd->name = "DiskOnChip Millennium Plus"; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci return 1; 127062306a36Sopenharmony_ci} 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_cistatic int doc200x_attach_chip(struct nand_chip *chip) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci if (chip->ecc.engine_type != NAND_ECC_ENGINE_TYPE_ON_HOST) 127562306a36Sopenharmony_ci return 0; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci chip->ecc.placement = NAND_ECC_PLACEMENT_INTERLEAVED; 127862306a36Sopenharmony_ci chip->ecc.size = 512; 127962306a36Sopenharmony_ci chip->ecc.bytes = 6; 128062306a36Sopenharmony_ci chip->ecc.strength = 2; 128162306a36Sopenharmony_ci chip->ecc.options = NAND_ECC_GENERIC_ERASED_CHECK; 128262306a36Sopenharmony_ci chip->ecc.hwctl = doc200x_enable_hwecc; 128362306a36Sopenharmony_ci chip->ecc.calculate = doc200x_calculate_ecc; 128462306a36Sopenharmony_ci chip->ecc.correct = doc200x_correct_data; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci return 0; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic const struct nand_controller_ops doc200x_ops = { 129062306a36Sopenharmony_ci .exec_op = doc200x_exec_op, 129162306a36Sopenharmony_ci .attach_chip = doc200x_attach_chip, 129262306a36Sopenharmony_ci}; 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic const struct nand_controller_ops doc2001plus_ops = { 129562306a36Sopenharmony_ci .exec_op = doc2001plus_exec_op, 129662306a36Sopenharmony_ci .attach_chip = doc200x_attach_chip, 129762306a36Sopenharmony_ci}; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic int __init doc_probe(unsigned long physadr) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct nand_chip *nand = NULL; 130262306a36Sopenharmony_ci struct doc_priv *doc = NULL; 130362306a36Sopenharmony_ci unsigned char ChipID; 130462306a36Sopenharmony_ci struct mtd_info *mtd; 130562306a36Sopenharmony_ci void __iomem *virtadr; 130662306a36Sopenharmony_ci unsigned char save_control; 130762306a36Sopenharmony_ci unsigned char tmp, tmpb, tmpc; 130862306a36Sopenharmony_ci int reg, len, numchips; 130962306a36Sopenharmony_ci int ret = 0; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip")) 131262306a36Sopenharmony_ci return -EBUSY; 131362306a36Sopenharmony_ci virtadr = ioremap(physadr, DOC_IOREMAP_LEN); 131462306a36Sopenharmony_ci if (!virtadr) { 131562306a36Sopenharmony_ci pr_err("Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", 131662306a36Sopenharmony_ci DOC_IOREMAP_LEN, physadr); 131762306a36Sopenharmony_ci ret = -EIO; 131862306a36Sopenharmony_ci goto error_ioremap; 131962306a36Sopenharmony_ci } 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci /* It's not possible to cleanly detect the DiskOnChip - the 132262306a36Sopenharmony_ci * bootup procedure will put the device into reset mode, and 132362306a36Sopenharmony_ci * it's not possible to talk to it without actually writing 132462306a36Sopenharmony_ci * to the DOCControl register. So we store the current contents 132562306a36Sopenharmony_ci * of the DOCControl register's location, in case we later decide 132662306a36Sopenharmony_ci * that it's not a DiskOnChip, and want to put it back how we 132762306a36Sopenharmony_ci * found it. 132862306a36Sopenharmony_ci */ 132962306a36Sopenharmony_ci save_control = ReadDOC(virtadr, DOCControl); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci /* Reset the DiskOnChip ASIC */ 133262306a36Sopenharmony_ci WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); 133362306a36Sopenharmony_ci WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* Enable the DiskOnChip ASIC */ 133662306a36Sopenharmony_ci WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); 133762306a36Sopenharmony_ci WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci ChipID = ReadDOC(virtadr, ChipID); 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci switch (ChipID) { 134262306a36Sopenharmony_ci case DOC_ChipID_Doc2k: 134362306a36Sopenharmony_ci reg = DoC_2k_ECCStatus; 134462306a36Sopenharmony_ci break; 134562306a36Sopenharmony_ci case DOC_ChipID_DocMil: 134662306a36Sopenharmony_ci reg = DoC_ECCConf; 134762306a36Sopenharmony_ci break; 134862306a36Sopenharmony_ci case DOC_ChipID_DocMilPlus16: 134962306a36Sopenharmony_ci case DOC_ChipID_DocMilPlus32: 135062306a36Sopenharmony_ci case 0: 135162306a36Sopenharmony_ci /* Possible Millennium Plus, need to do more checks */ 135262306a36Sopenharmony_ci /* Possibly release from power down mode */ 135362306a36Sopenharmony_ci for (tmp = 0; (tmp < 4); tmp++) 135462306a36Sopenharmony_ci ReadDOC(virtadr, Mplus_Power); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci /* Reset the Millennium Plus ASIC */ 135762306a36Sopenharmony_ci tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; 135862306a36Sopenharmony_ci WriteDOC(tmp, virtadr, Mplus_DOCControl); 135962306a36Sopenharmony_ci WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci usleep_range(1000, 2000); 136262306a36Sopenharmony_ci /* Enable the Millennium Plus ASIC */ 136362306a36Sopenharmony_ci tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT; 136462306a36Sopenharmony_ci WriteDOC(tmp, virtadr, Mplus_DOCControl); 136562306a36Sopenharmony_ci WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); 136662306a36Sopenharmony_ci usleep_range(1000, 2000); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci ChipID = ReadDOC(virtadr, ChipID); 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci switch (ChipID) { 137162306a36Sopenharmony_ci case DOC_ChipID_DocMilPlus16: 137262306a36Sopenharmony_ci reg = DoC_Mplus_Toggle; 137362306a36Sopenharmony_ci break; 137462306a36Sopenharmony_ci case DOC_ChipID_DocMilPlus32: 137562306a36Sopenharmony_ci pr_err("DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n"); 137662306a36Sopenharmony_ci fallthrough; 137762306a36Sopenharmony_ci default: 137862306a36Sopenharmony_ci ret = -ENODEV; 137962306a36Sopenharmony_ci goto notfound; 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci break; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci default: 138462306a36Sopenharmony_ci ret = -ENODEV; 138562306a36Sopenharmony_ci goto notfound; 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci /* Check the TOGGLE bit in the ECC register */ 138862306a36Sopenharmony_ci tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; 138962306a36Sopenharmony_ci tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; 139062306a36Sopenharmony_ci tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; 139162306a36Sopenharmony_ci if ((tmp == tmpb) || (tmp != tmpc)) { 139262306a36Sopenharmony_ci pr_warn("Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr); 139362306a36Sopenharmony_ci ret = -ENODEV; 139462306a36Sopenharmony_ci goto notfound; 139562306a36Sopenharmony_ci } 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci for (mtd = doclist; mtd; mtd = doc->nextdoc) { 139862306a36Sopenharmony_ci unsigned char oldval; 139962306a36Sopenharmony_ci unsigned char newval; 140062306a36Sopenharmony_ci nand = mtd_to_nand(mtd); 140162306a36Sopenharmony_ci doc = nand_get_controller_data(nand); 140262306a36Sopenharmony_ci /* Use the alias resolution register to determine if this is 140362306a36Sopenharmony_ci in fact the same DOC aliased to a new address. If writes 140462306a36Sopenharmony_ci to one chip's alias resolution register change the value on 140562306a36Sopenharmony_ci the other chip, they're the same chip. */ 140662306a36Sopenharmony_ci if (ChipID == DOC_ChipID_DocMilPlus16) { 140762306a36Sopenharmony_ci oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); 140862306a36Sopenharmony_ci newval = ReadDOC(virtadr, Mplus_AliasResolution); 140962306a36Sopenharmony_ci } else { 141062306a36Sopenharmony_ci oldval = ReadDOC(doc->virtadr, AliasResolution); 141162306a36Sopenharmony_ci newval = ReadDOC(virtadr, AliasResolution); 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci if (oldval != newval) 141462306a36Sopenharmony_ci continue; 141562306a36Sopenharmony_ci if (ChipID == DOC_ChipID_DocMilPlus16) { 141662306a36Sopenharmony_ci WriteDOC(~newval, virtadr, Mplus_AliasResolution); 141762306a36Sopenharmony_ci oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); 141862306a36Sopenharmony_ci WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it 141962306a36Sopenharmony_ci } else { 142062306a36Sopenharmony_ci WriteDOC(~newval, virtadr, AliasResolution); 142162306a36Sopenharmony_ci oldval = ReadDOC(doc->virtadr, AliasResolution); 142262306a36Sopenharmony_ci WriteDOC(newval, virtadr, AliasResolution); // restore it 142362306a36Sopenharmony_ci } 142462306a36Sopenharmony_ci newval = ~newval; 142562306a36Sopenharmony_ci if (oldval == newval) { 142662306a36Sopenharmony_ci pr_debug("Found alias of DOC at 0x%lx to 0x%lx\n", 142762306a36Sopenharmony_ci doc->physadr, physadr); 142862306a36Sopenharmony_ci goto notfound; 142962306a36Sopenharmony_ci } 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci pr_notice("DiskOnChip found at 0x%lx\n", physadr); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci len = sizeof(struct nand_chip) + sizeof(struct doc_priv) + 143562306a36Sopenharmony_ci (2 * sizeof(struct nand_bbt_descr)); 143662306a36Sopenharmony_ci nand = kzalloc(len, GFP_KERNEL); 143762306a36Sopenharmony_ci if (!nand) { 143862306a36Sopenharmony_ci ret = -ENOMEM; 143962306a36Sopenharmony_ci goto fail; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci /* 144362306a36Sopenharmony_ci * Allocate a RS codec instance 144462306a36Sopenharmony_ci * 144562306a36Sopenharmony_ci * Symbolsize is 10 (bits) 144662306a36Sopenharmony_ci * Primitve polynomial is x^10+x^3+1 144762306a36Sopenharmony_ci * First consecutive root is 510 144862306a36Sopenharmony_ci * Primitve element to generate roots = 1 144962306a36Sopenharmony_ci * Generator polinomial degree = 4 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_ci doc = (struct doc_priv *) (nand + 1); 145262306a36Sopenharmony_ci doc->rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); 145362306a36Sopenharmony_ci if (!doc->rs_decoder) { 145462306a36Sopenharmony_ci pr_err("DiskOnChip: Could not create a RS codec\n"); 145562306a36Sopenharmony_ci ret = -ENOMEM; 145662306a36Sopenharmony_ci goto fail; 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci nand_controller_init(&doc->base); 146062306a36Sopenharmony_ci if (ChipID == DOC_ChipID_DocMilPlus16) 146162306a36Sopenharmony_ci doc->base.ops = &doc2001plus_ops; 146262306a36Sopenharmony_ci else 146362306a36Sopenharmony_ci doc->base.ops = &doc200x_ops; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci mtd = nand_to_mtd(nand); 146662306a36Sopenharmony_ci nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); 146762306a36Sopenharmony_ci nand->bbt_md = nand->bbt_td + 1; 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_ci mtd->owner = THIS_MODULE; 147062306a36Sopenharmony_ci mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci nand->controller = &doc->base; 147362306a36Sopenharmony_ci nand_set_controller_data(nand, doc); 147462306a36Sopenharmony_ci nand->bbt_options = NAND_BBT_USE_FLASH; 147562306a36Sopenharmony_ci /* Skip the automatic BBT scan so we can run it manually */ 147662306a36Sopenharmony_ci nand->options |= NAND_SKIP_BBTSCAN | NAND_NO_BBM_QUIRK; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci doc->physadr = physadr; 147962306a36Sopenharmony_ci doc->virtadr = virtadr; 148062306a36Sopenharmony_ci doc->ChipID = ChipID; 148162306a36Sopenharmony_ci doc->curfloor = -1; 148262306a36Sopenharmony_ci doc->curchip = -1; 148362306a36Sopenharmony_ci doc->mh0_page = -1; 148462306a36Sopenharmony_ci doc->mh1_page = -1; 148562306a36Sopenharmony_ci doc->nextdoc = doclist; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci if (ChipID == DOC_ChipID_Doc2k) 148862306a36Sopenharmony_ci numchips = doc2000_init(mtd); 148962306a36Sopenharmony_ci else if (ChipID == DOC_ChipID_DocMilPlus16) 149062306a36Sopenharmony_ci numchips = doc2001plus_init(mtd); 149162306a36Sopenharmony_ci else 149262306a36Sopenharmony_ci numchips = doc2001_init(mtd); 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if ((ret = nand_scan(nand, numchips)) || (ret = doc->late_init(mtd))) { 149562306a36Sopenharmony_ci /* DBB note: i believe nand_cleanup is necessary here, as 149662306a36Sopenharmony_ci buffers may have been allocated in nand_base. Check with 149762306a36Sopenharmony_ci Thomas. FIX ME! */ 149862306a36Sopenharmony_ci nand_cleanup(nand); 149962306a36Sopenharmony_ci goto fail; 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci /* Success! */ 150362306a36Sopenharmony_ci doclist = mtd; 150462306a36Sopenharmony_ci return 0; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci notfound: 150762306a36Sopenharmony_ci /* Put back the contents of the DOCControl register, in case it's not 150862306a36Sopenharmony_ci actually a DiskOnChip. */ 150962306a36Sopenharmony_ci WriteDOC(save_control, virtadr, DOCControl); 151062306a36Sopenharmony_ci fail: 151162306a36Sopenharmony_ci if (doc) 151262306a36Sopenharmony_ci free_rs(doc->rs_decoder); 151362306a36Sopenharmony_ci kfree(nand); 151462306a36Sopenharmony_ci iounmap(virtadr); 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_cierror_ioremap: 151762306a36Sopenharmony_ci release_mem_region(physadr, DOC_IOREMAP_LEN); 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci return ret; 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cistatic void release_nanddoc(void) 152362306a36Sopenharmony_ci{ 152462306a36Sopenharmony_ci struct mtd_info *mtd, *nextmtd; 152562306a36Sopenharmony_ci struct nand_chip *nand; 152662306a36Sopenharmony_ci struct doc_priv *doc; 152762306a36Sopenharmony_ci int ret; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci for (mtd = doclist; mtd; mtd = nextmtd) { 153062306a36Sopenharmony_ci nand = mtd_to_nand(mtd); 153162306a36Sopenharmony_ci doc = nand_get_controller_data(nand); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci nextmtd = doc->nextdoc; 153462306a36Sopenharmony_ci ret = mtd_device_unregister(mtd); 153562306a36Sopenharmony_ci WARN_ON(ret); 153662306a36Sopenharmony_ci nand_cleanup(nand); 153762306a36Sopenharmony_ci iounmap(doc->virtadr); 153862306a36Sopenharmony_ci release_mem_region(doc->physadr, DOC_IOREMAP_LEN); 153962306a36Sopenharmony_ci free_rs(doc->rs_decoder); 154062306a36Sopenharmony_ci kfree(nand); 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci} 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic int __init init_nanddoc(void) 154562306a36Sopenharmony_ci{ 154662306a36Sopenharmony_ci int i, ret = 0; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (doc_config_location) { 154962306a36Sopenharmony_ci pr_info("Using configured DiskOnChip probe address 0x%lx\n", 155062306a36Sopenharmony_ci doc_config_location); 155162306a36Sopenharmony_ci ret = doc_probe(doc_config_location); 155262306a36Sopenharmony_ci if (ret < 0) 155362306a36Sopenharmony_ci return ret; 155462306a36Sopenharmony_ci } else { 155562306a36Sopenharmony_ci for (i = 0; (doc_locations[i] != 0xffffffff); i++) { 155662306a36Sopenharmony_ci doc_probe(doc_locations[i]); 155762306a36Sopenharmony_ci } 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci /* No banner message any more. Print a message if no DiskOnChip 156062306a36Sopenharmony_ci found, so the user knows we at least tried. */ 156162306a36Sopenharmony_ci if (!doclist) { 156262306a36Sopenharmony_ci pr_info("No valid DiskOnChip devices found\n"); 156362306a36Sopenharmony_ci ret = -ENODEV; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci return ret; 156662306a36Sopenharmony_ci} 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_cistatic void __exit cleanup_nanddoc(void) 156962306a36Sopenharmony_ci{ 157062306a36Sopenharmony_ci /* Cleanup the nand/DoC resources */ 157162306a36Sopenharmony_ci release_nanddoc(); 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_cimodule_init(init_nanddoc); 157562306a36Sopenharmony_cimodule_exit(cleanup_nanddoc); 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 157862306a36Sopenharmony_ciMODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 157962306a36Sopenharmony_ciMODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver"); 1580