// SPDX-License-Identifier: GPL-2.0-only /* * (C) 2003 Red Hat, Inc. * (C) 2004 Dan Brown <dan_brown@ieee.org> * (C) 2004 Kalev Lember <kalev@smartlink.ee> * * Author: David Woodhouse <dwmw2@infradead.org> * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org> * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee> * * Error correction code lifted from the old docecc code * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de> * * Interface to generic NAND code for M-Systems DiskOnChip devices
*/
struct doc_priv { struct nand_controller base; void __iomem *virtadr; unsignedlong physadr;
u_char ChipID;
u_char CDSNControl; int chips_per_floor; /* The number of chips detected on each floor */ int curfloor; int curchip; int mh0_page; int mh1_page; struct rs_control *rs_decoder; struct mtd_info *nextdoc; bool supports_32b_reads;
/* Handle the last stage of initialization (BBT scan, partitioning) */ int (*late_init)(struct mtd_info *mtd);
};
/* This is the ecc value computed by the HW ecc generator upon writing an empty
page, one with all 0xff for data. */ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
staticunsignedlong doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;
module_param(doc_config_location, ulong, 0);
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
/* Sector size for HW ECC */ #define SECTOR_SIZE 512 /* The sector bytes are packed into NB_DATA 10 bit words */ #define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / 10) /* Number of roots */ #define NROOTS 4 /* First consective root */ #define FCR 510 /* Number of symbols */ #define NN 1023
/* * The HW decoder in the DoC ASIC's provides us a error syndrome, * which we must convert to a standard syndrome usable by the generic * Reed-Solomon library code. * * Fabrice Bellard figured this out in the old docecc code. I added * some comments, improved a minor bit and converted it to make use * of the generic Reed-Solomon library. tglx
*/ staticint doc_ecc_decode(struct rs_control *rs, uint8_t *data, uint8_t *ecc)
{ int i, j, nerr, errpos[8];
uint8_t parity;
uint16_t ds[4], s[5], tmp, errval[8], syn[4]; struct rs_codec *cd = rs->codec;
/* * Correct the errors. The bitpositions are a bit of magic, * but they are given by the design of the de/encoder circuit * in the DoC ASIC's.
*/ for (i = 0; i < nerr; i++) { int index, bitpos, pos = 1015 - errpos[i];
uint8_t val; if (pos >= NB_DATA && pos < 1019) continue; if (pos < NB_DATA) { /* extract bit position (MSB first) */
pos = 10 * (NB_DATA - 1 - pos) - 6; /* now correct the following 10 bits. At most two bytes
can be modified since pos is even */
index = (pos >> 3) ^ 1;
bitpos = pos & 7; if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
val = (uint8_t) (errval[i] >> (2 + bitpos));
parity ^= val; if (index < SECTOR_SIZE)
data[index] ^= val;
}
index = ((pos >> 3) + 1) ^ 1;
bitpos = (bitpos + 10) & 7; if (bitpos == 0)
bitpos = 8; if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
val = (uint8_t) (errval[i] << (8 - bitpos));
parity ^= val; if (index < SECTOR_SIZE)
data[index] ^= val;
}
}
} /* If the parity is wrong, no rescue possible */ return parity ? -EBADMSG : nerr;
}
if (!doc->supports_32b_reads ||
((((unsignedlong)buf) | len) & 3)) { for (i = 0; i < len; i++)
buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
} else { for (i = 0; i < len / 4; i++)
buf32[i] = readl(docptr + DoC_2k_CDSN_IO + i);
}
}
/* * We need our own readid() here because it's called before the NAND chip * has been initialized, and calling nand_op_readid() would lead to a NULL * pointer exception when dereferencing the NAND timings.
*/ staticvoid doc200x_readid(struct nand_chip *this, unsignedint cs, u8 *id)
{
u8 addr = 0; struct nand_op_instr instrs[] = {
NAND_OP_CMD(NAND_CMD_READID, 0),
NAND_OP_ADDR(1, &addr, 50),
NAND_OP_8BIT_DATA_IN(2, id, 0),
};
struct nand_operation op = NAND_OPERATION(cs, instrs);
if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { /* First chip probe. See if we get same results by 32-bit access */ union {
uint32_t dword;
uint8_t byte[4];
} ident; void __iomem *docptr = doc->virtadr;
/* Max 4 chips per floor on DiskOnChip 2000 */
doc->chips_per_floor = 4;
/* Find out what the first chip is */
mfrid = doc200x_ident_chip(mtd, 0);
/* Find how many chips in each floor. */ for (i = 1; i < 4; i++) { if (doc200x_ident_chip(mtd, i) != mfrid) break;
}
doc->chips_per_floor = i;
pr_debug("Detected %d chips per floor.\n", i);
}
if (debug)
printk("writebuf of %d bytes: ", len); for (i = 0; i < len; i++) {
WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); if (debug && i < 16)
printk("%02x ", buf[i]);
} if (debug)
printk("\n");
}
case NAND_OP_ADDR_INSTR:
doc200x_write_control(doc, CDSN_CTRL_CE | CDSN_CTRL_ALE); for (i = 0; i < instr->ctx.addr.naddrs; i++) {
u8 addr = instr->ctx.addr.addrs[i];
if (DoC_is_2000(doc))
doc2000_write_byte(this, addr); else
doc2001_write_byte(this, addr);
} break;
case NAND_OP_DATA_IN_INSTR:
doc200x_write_control(doc, CDSN_CTRL_CE); if (DoC_is_2000(doc))
doc2000_readbuf(this, instr->ctx.data.buf.in,
instr->ctx.data.len); else
doc2001_readbuf(this, instr->ctx.data.buf.in,
instr->ctx.data.len); break;
case NAND_OP_DATA_OUT_INSTR:
doc200x_write_control(doc, CDSN_CTRL_CE); if (DoC_is_2000(doc))
doc2000_writebuf(this, instr->ctx.data.buf.out,
instr->ctx.data.len); else
doc2001_writebuf(this, instr->ctx.data.buf.out,
instr->ctx.data.len); break;
case NAND_OP_WAITRDY_INSTR:
DoC_WaitReady(doc); break;
}
for (i = 0; i < 6; i++) { if (DoC_is_MillenniumPlus(doc))
ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); else
ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i); if (ecc_code[i] != empty_write_ecc[i])
emptymatch = 0;
} if (DoC_is_MillenniumPlus(doc))
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); else
WriteDOC(DOC_ECC_DIS, docptr, ECCConf); #if 0 /* If emptymatch=1, we might have an all-0xff data buffer. Check. */ if (emptymatch) { /* Note: this somewhat expensive test should not be triggered often. It could be optimized away by examining the data in
the writebuf routine, and remembering the result. */ for (i = 0; i < 512; i++) { if (dat[i] == 0xff) continue;
emptymatch = 0; break;
}
} /* If emptymatch still =1, we do have an all-0xff data buffer. Return all-0xff ecc value instead of the computed one, so
it'll look just like a freshly-erased page. */ if (emptymatch)
memset(ecc_code, 0xff, 6); #endif return 0;
}
/* Error occurred ? */ if (dummy & 0x80) { for (i = 0; i < 6; i++) { if (DoC_is_MillenniumPlus(doc))
calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i); else
calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
}
ret = doc_ecc_decode(doc->rs_decoder, dat, calc_ecc); if (ret > 0)
pr_err("doc200x_correct_data corrected %d errors\n",
ret);
} if (DoC_is_MillenniumPlus(doc))
WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); else
WriteDOC(DOC_ECC_DIS, docptr, ECCConf); if (no_ecc_failures && mtd_is_eccerr(ret)) {
pr_err("suppressing ECC failure\n");
ret = 0;
} return ret;
}
//u_char mydatabuf[528];
staticint doc200x_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion)
{ if (section) return -ERANGE;
oobregion->offset = 0;
oobregion->length = 6;
return 0;
}
staticint doc200x_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *oobregion)
{ if (section > 1) return -ERANGE;
/* * The strange out-of-order free bytes definition is a (possibly * unneeded) attempt to retain compatibility. It used to read: * .oobfree = { {8, 8} } * Since that leaves two bytes unusable, it was changed. But the * following scheme might affect existing jffs2 installs by moving the * cleanmarker: * .oobfree = { {6, 10} } * jffs2 seems to handle the above gracefully, but the current scheme * seems safer. The only problem with it is that any code retrieving * free bytes position must be able to handle out-of-order segments.
*/ if (!section) {
oobregion->offset = 8;
oobregion->length = 8;
} else {
oobregion->offset = 6;
oobregion->length = 2;
}
/* Find the (I)NFTL Media Header, and optionally also the mirror media header. On successful return, buf will contain a copy of the media header for further processing. id is the string to scan for, and will presumably be either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media header. The page #s of the found media headers are placed in mh0_page and
mh1_page in the DOC private structure. */ staticint __init find_media_headers(struct mtd_info *mtd, u_char *buf, constchar *id, intfindmirror)
{ struct nand_chip *this = mtd_to_nand(mtd); struct doc_priv *doc = nand_get_controller_data(this); unsigned offs; int ret;
size_t retlen;
for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); if (retlen != mtd->writesize) continue; if (ret) {
pr_warn("ECC error scanning DOC at 0x%x\n", offs);
} if (memcmp(buf, id, 6)) continue;
pr_info("Found DiskOnChip %s Media Header at 0x%x\n", id, offs); if (doc->mh0_page == -1) {
doc->mh0_page = offs >> this->page_shift; if (!findmirror) return 1; continue;
}
doc->mh1_page = offs >> this->page_shift; return 2;
} if (doc->mh0_page == -1) {
pr_warn("DiskOnChip %s Media Header not found.\n", id); return 0;
} /* Only one mediaheader was found. We want buf to contain a
mediaheader on return, so we'll have to re-read the one we found. */
offs = doc->mh0_page << this->page_shift;
ret = mtd_read(mtd, offs, mtd->writesize, &retlen, buf); if (retlen != mtd->writesize) { /* Insanity. Give up. */
pr_err("Read DiskOnChip Media Header once, but can't reread it???\n"); return 0;
} return 1;
}
if (mh->UnitSizeFactor == 0x00) { /* Auto-determine UnitSizeFactor. The constraints are: - There can be at most 32768 virtual blocks. - There can be at most (virtual block size - page size) virtual blocks (because MediaHeader+BBT must fit in 1).
*/
mh->UnitSizeFactor = 0xff; while (blocks > maxblocks) {
blocks >>= 1;
maxblocks = min(32768U, (maxblocks << 1) + psize);
mh->UnitSizeFactor--;
}
pr_warn("UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
}
/* NOTE: The lines below modify internal variables of the NAND and MTD layers; variables with have already been configured by nand_scan. Unfortunately, we didn't know before this point what these values should be. Thus, this code is somewhat dependent on the exact
implementation of the NAND layer. */ if (mh->UnitSizeFactor != 0xff) {
this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
memorg->pages_per_eraseblock <<= (0xff - mh->UnitSizeFactor);
mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
pr_info("Setting virtual erase size to %d\n", mtd->erasesize);
blocks = mtd->size >> this->bbt_erase_shift;
maxblocks = min(32768U, mtd->erasesize - psize);
}
if (blocks > maxblocks) {
pr_err("UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor); goto out;
}
/* Skip past the media headers. */
offs = max(doc->mh0_page, doc->mh1_page);
offs <<= this->page_shift;
offs += mtd->erasesize;
/* This is a stripped-down copy of the code in inftlmount.c */ staticinlineint __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
{ struct nand_chip *this = mtd_to_nand(mtd); struct doc_priv *doc = nand_get_controller_data(this); int ret = 0;
u_char *buf; struct INFTLMediaHeader *mh; struct INFTLPartition *ip; int numparts = 0; int blocks; int vshift, lastvunit = 0; int i; int end = mtd->size;
if (inftl_bbt_write)
end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
buf = kmalloc(mtd->writesize, GFP_KERNEL); if (!buf) { return 0;
}
blocks = mtd->size >> vshift; if (blocks > 32768) {
pr_err("BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits); goto out;
}
blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift); if (inftl_bbt_write && (blocks > mtd->erasesize)) {
pr_err("Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n"); goto out;
}
/* Scan the partitions */ for (i = 0; (i < 4); i++) {
ip = &(mh->Partitions[i]);
le32_to_cpus(&ip->virtualUnits);
le32_to_cpus(&ip->firstUnit);
le32_to_cpus(&ip->lastUnit);
le32_to_cpus(&ip->flags);
le32_to_cpus(&ip->spareUnits);
le32_to_cpus(&ip->Reserved0);
memset((char *)parts, 0, sizeof(parts));
numparts = inftl_partscan(mtd, parts); /* At least for now, require the INFTL Media Header. We could probably do without it for non-INFTL use, since all it gives us is
autopartitioning, but I want to give it more thought. */ if (!numparts) return -EIO; return mtd_device_register(mtd, parts, no_autopart ? 0 : numparts);
}
ReadDOC(doc->virtadr, ChipID);
ReadDOC(doc->virtadr, ChipID);
ReadDOC(doc->virtadr, ChipID); if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) { /* It's not a Millennium; it's one of the newer DiskOnChip 2000 units with a similar ASIC. Treat it like a Millennium, except that it
can have multiple chips. */
doc2000_count_chips(mtd);
mtd->name = "DiskOnChip 2000 (INFTL Model)";
doc->late_init = inftl_scan_bbt; return (4 * doc->chips_per_floor);
} else { /* Bog-standard Millennium */
doc->chips_per_floor = 1;
mtd->name = "DiskOnChip Millennium";
doc->late_init = nftl_scan_bbt; return 1;
}
}
if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip")) return -EBUSY;
virtadr = ioremap(physadr, DOC_IOREMAP_LEN); if (!virtadr) {
pr_err("Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n",
DOC_IOREMAP_LEN, physadr);
ret = -EIO; goto error_ioremap;
}
/* It's not possible to cleanly detect the DiskOnChip - the * bootup procedure will put the device into reset mode, and * it's not possible to talk to it without actually writing * to the DOCControl register. So we store the current contents * of the DOCControl register's location, in case we later decide * that it's not a DiskOnChip, and want to put it back how we * found it.
*/
save_control = ReadDOC(virtadr, DOCControl);
switch (ChipID) { case DOC_ChipID_Doc2k:
reg = DoC_2k_ECCStatus; break; case DOC_ChipID_DocMil:
reg = DoC_ECCConf; break; case DOC_ChipID_DocMilPlus16: case DOC_ChipID_DocMilPlus32: case 0: /* Possible Millennium Plus, need to do more checks */ /* Possibly release from power down mode */ for (tmp = 0; (tmp < 4); tmp++)
ReadDOC(virtadr, Mplus_Power);
switch (ChipID) { case DOC_ChipID_DocMilPlus16:
reg = DoC_Mplus_Toggle; break; case DOC_ChipID_DocMilPlus32:
pr_err("DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
fallthrough; default:
ret = -ENODEV; goto notfound;
} break;
default:
ret = -ENODEV; goto notfound;
} /* Check the TOGGLE bit in the ECC register */
tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT; if ((tmp == tmpb) || (tmp != tmpc)) {
pr_warn("Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
ret = -ENODEV; goto notfound;
}
for (mtd = doclist; mtd; mtd = doc->nextdoc) { unsignedchar oldval; unsignedchar newval;
nand = mtd_to_nand(mtd);
doc = nand_get_controller_data(nand); /* Use the alias resolution register to determine if this is in fact the same DOC aliased to a new address. If writes to one chip's alias resolution register change the value on
the other chip, they're the same chip. */ if (ChipID == DOC_ChipID_DocMilPlus16) {
oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
newval = ReadDOC(virtadr, Mplus_AliasResolution);
} else {
oldval = ReadDOC(doc->virtadr, AliasResolution);
newval = ReadDOC(virtadr, AliasResolution);
} if (oldval != newval) continue; if (ChipID == DOC_ChipID_DocMilPlus16) {
WriteDOC(~newval, virtadr, Mplus_AliasResolution);
oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it
} else {
WriteDOC(~newval, virtadr, AliasResolution);
oldval = ReadDOC(doc->virtadr, AliasResolution);
WriteDOC(newval, virtadr, AliasResolution); // restore it
}
newval = ~newval; if (oldval == newval) {
pr_debug("Found alias of DOC at 0x%lx to 0x%lx\n",
doc->physadr, physadr); goto notfound;
}
}
pr_notice("DiskOnChip found at 0x%lx\n", physadr);
len = sizeof(struct nand_chip) + sizeof(struct doc_priv) +
(2 * sizeof(struct nand_bbt_descr));
nand = kzalloc(len, GFP_KERNEL); if (!nand) {
ret = -ENOMEM; goto fail;
}
/* * Allocate a RS codec instance * * Symbolsize is 10 (bits) * Primitve polynomial is x^10+x^3+1 * First consecutive root is 510 * Primitve element to generate roots = 1 * Generator polinomial degree = 4
*/
doc = (struct doc_priv *) (nand + 1);
doc->rs_decoder = init_rs(10, 0x409, FCR, 1, NROOTS); if (!doc->rs_decoder) {
pr_err("DiskOnChip: Could not create a RS codec\n");
ret = -ENOMEM; goto fail;
}
nand->controller = &doc->base;
nand_set_controller_data(nand, doc);
nand->bbt_options = NAND_BBT_USE_FLASH; /* Skip the automatic BBT scan so we can run it manually */
nand->options |= NAND_SKIP_BBTSCAN | NAND_NO_BBM_QUIRK;
ret = nand_scan(nand, numchips); if (ret) goto fail;
ret = doc->late_init(mtd); if (ret) {
nand_cleanup(nand); goto fail;
}
/* Success! */
doclist = mtd; return 0;
notfound: /* Put back the contents of the DOCControl register, in case it's not
actually a DiskOnChip. */
WriteDOC(save_control, virtadr, DOCControl);
fail: if (doc)
free_rs(doc->rs_decoder);
kfree(nand);
iounmap(virtadr);
staticint __init init_nanddoc(void)
{ int i, ret = 0;
if (doc_config_location) {
pr_info("Using configured DiskOnChip probe address 0x%lx\n",
doc_config_location);
ret = doc_probe(doc_config_location); if (ret < 0) return ret;
} else { for (i = 0; i < ARRAY_SIZE(doc_locations); i++) {
doc_probe(doc_locations[i]);
}
} /* No banner message any more. Print a message if no DiskOnChip
found, so the user knows we at least tried. */ if (!doclist) {
pr_info("No valid DiskOnChip devices found\n");
ret = -ENODEV;
} return ret;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.