📄 nand_base.c
字号:
/* Walk through the autoplace chunks */ for (i = 0; oobsel->oobfree[i][1]; i++) { int from = oobsel->oobfree[i][0]; int num = oobsel->oobfree[i][1]; memcpy(&oob_buf[oob], &oob_data[from], num); oob += num; } break; case MTD_NANDECC_PLACE: /* YAFFS1 legacy mode */ oob_data += this->eccsteps * sizeof (int); default: oob_data += mtd->oobsize; } } readdata: /* Partial page read, transfer data into fs buffer */ if (!aligned) { for (j = col; j < end && read < len; j++) buf[read++] = data_poi[j]; this->pagebuf = realpage; } else read += mtd->oobblock; /* Apply delay or wait for ready/busy pin * Do this before the AUTOINCR check, so no problems * arise if a chip which does auto increment * is marked as NOAUTOINCR by the board driver. */ if (!this->dev_ready) udelay (this->chip_delay); else nand_wait_ready(mtd); if (read == len) break; /* For subsequent reads align to page boundary. */ col = 0; /* Increment page address */ realpage++; page = realpage & this->pagemask; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); } /* Check, if the chip supports auto page increment * or if we have hit a block boundary. */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) sndcmd = 1; } /* Deselect and wake up anyone waiting on the device */ if (flags & NAND_GET_DEVICE) nand_release_device(mtd); /* * Return success, if no ECC failures, else -EBADMSG * fs driver will take care of that, because * retlen == desired len and result == -EBADMSG */ *retlen = read; return ecc_failed ? -EBADMSG : 0;}/** * nand_read_oob - [MTD Interface] NAND read out-of-band * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read * @retlen: pointer to variable to store the number of read bytes * @buf: the databuffer to put data * * NAND read out-of-band data from the spare area */static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * oob_buf){ int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1, reallen = 0; int read = 0; struct nand_chip *this = mtd->priv; u_char *oob_data = oob_buf; int eccsteps; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; DEBUG (MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n", __FUNCTION__, (unsigned int) from, (int) len); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_device (this, mtd, FL_READING); /* Select the NAND device */ chipnr = (int)(from >> this->chip_shift); this->select_chip(mtd, chipnr); /* First we calculate the starting page */ realpage = (int) (from >> this->page_shift); page = realpage & this->pagemask; /* Get raw starting column */ col = from & (mtd->oobblock - 1); end = mtd->oobblock; ecc = this->eccsize; /* Loop until all data read */ while (read < len) { if (this->eccmode == NAND_ECC_SOFT || this->eccmode == NAND_ECC_NONE) { int thislen = mtd->oobsize - col; if (sndcmd) { this->cmdfunc (mtd, NAND_CMD_READOOB, col, page); col = 0; sndcmd = 0; } thislen = min_t(int, thislen, len); this->read_buf(mtd, &oob_buf[read], thislen); read += thislen; } else { /* Check, if we must send the read command */ if (sndcmd) { this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; } eccsteps = this->eccsteps; for (j = 0; this->layout[j].length; j++) { i = 0; switch (this->layout[j].type) { case ITEM_TYPE_DATA: DEBUG (MTD_DEBUG_LEVEL3, "%s: dummy data read\n", __FUNCTION__); reallen += this->layout[j].length; if (this->options & NAND_BUSWIDTH_16) this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page); else this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page); break; case ITEM_TYPE_ECC: case ITEM_TYPE_OOB: DEBUG (MTD_DEBUG_LEVEL3, "%s: %s bytes read\n", __FUNCTION__, this->layout[j].type == ITEM_TYPE_ECC ? "ecc" : "oob"); i = min_t(int, col, this->layout[j].length); if (i) { reallen += i; if (this->options & NAND_BUSWIDTH_16) this->cmdfunc (mtd, NAND_CMD_READ0, reallen & ~1, page); else this->cmdfunc (mtd, NAND_CMD_READ0, reallen, page); } col -= i; if (this->layout[j].type == ITEM_TYPE_ECC) this->enable_hwecc(mtd, NAND_ECC_READSYN); else this->enable_hwecc(mtd, NAND_ECC_READOOB); i = min_t(int, len - read, this->layout[j].length - i); if (i) { if (this->options & NAND_BUSWIDTH_16) { if (reallen & 1) { oob_data[0] = cpu_to_le16(this->read_word(mtd)) >> 8; oob_data++; i--; reallen++; } if (i & 1) this->read_buf(mtd, oob_data, i - 1); else this->read_buf(mtd, oob_data, i); } else this->read_buf(mtd, oob_data, i); reallen += i; } if (oob_buf + len == oob_data + i) { read += i; goto out; } break; } read += i; oob_data += i; } }out: /* Apply delay or wait for ready/busy pin * Do this before the AUTOINCR check, so no problems * arise if a chip which does auto increment * is marked as NOAUTOINCR by the board driver. */ if (!this->dev_ready) udelay (this->chip_delay); else nand_wait_ready(mtd); if (read == len) break; /* For subsequent reads align to page boundary. */ reallen = col = 0; /* Increment page address */ realpage++; page = realpage & this->pagemask; /* Check, if we cross a chip boundary */ if (!page) { chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); } /* Check, if the chip supports auto page increment * or if we have hit a block boundary. */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) sndcmd = 1; } /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); *retlen = read; /* * Return success */ return 0;}/** * nand_read_raw - [GENERIC] Read raw data including oob into buffer * @mtd: MTD device structure * @buf: temporary buffer * @from: offset to read from * @len: number of bytes to read * @ooblen: number of oob data bytes to read * * Read raw data including oob into buffer */int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen){ struct nand_chip *this = mtd->priv; int page = (int) (from >> this->page_shift); int chip = (int) (from >> this->chip_shift); int sndcmd = 1; int cnt = 0; int pagesize = mtd->oobblock + mtd->oobsize; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_device (this, mtd , FL_READING); this->select_chip (mtd, chip); /* Add requested oob length */ len += ooblen; while (len) { if (sndcmd) this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask); sndcmd = 0; this->read_buf (mtd, &buf[cnt], pagesize); len -= pagesize; cnt += pagesize; page++; if (!this->dev_ready) udelay (this->chip_delay); else nand_wait_ready(mtd); /* Check, if the chip supports auto page increment */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) sndcmd = 1; } /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); return 0;}/** * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer * @mtd: MTD device structure * @fsbuf: buffer given by fs driver * @oobsel: out of band selection structre * @autoplace: 1 = place given buffer into the oob bytes * @numpages: number of pages to prepare * * Return: * 1. Filesystem buffer available and autoplacement is off, * return filesystem buffer * 2. No filesystem buffer or autoplace is off, return internal * buffer * 3. Filesystem buffer is given and autoplace selected * put data from fs buffer into internal buffer and * retrun internal buffer * * Note: The internal buffer is filled with 0xff. This must * be done only once, when no autoplacement happens * Autoplacement sets the buffer dirty flag, which * forces the 0xff fill before using the buffer again. **/static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel, int autoplace, int numpages){ struct nand_chip *this = mtd->priv; int i, len, ofs; /* Zero copy fs supplied buffer */ if (fsbuf && !autoplace) return fsbuf; /* Check, if the buffer must be filled with ff again */ if (this->oobdirty) { memset (this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift)); this->oobdirty = 0; } /* If we have no autoplacement or no fs buffer use the internal one */ if (!autoplace || !fsbuf) return this->oob_buf; /* Walk through the pages and place the data */ this->oobdirty = 1; ofs = 0; while (numpages--) { for (i = 0, len = 0; len < mtd->oobavail; i++) { int to = ofs + oobsel->oobfree[i][0]; int num = oobsel->oobfree[i][1]; memcpy (&this->oob_buf[to], fsbuf, num); len += num; fsbuf += num; } ofs += mtd->oobavail; } return this->oob_buf;}#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0/** * nand_write - [MTD Interface] compability function for nand_write_ecc * @mtd: MTD device structure * @to: offset to write to * @len: number of bytes to write * @retlen: pointer to variable to store the number of written bytes * @buf: the data to write * * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL **/static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf){ return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));}/** * nand_write_ecc - [MTD Interface] NAND write with ECC * @mtd: MTD device structure * @to: offset to write to * @len: number of bytes to write * @retlen: pointer to variable to store the number of written bytes * @buf: the data to write * @eccbuf: filesystem supplied oob data buffer * @oobsel: oob selection structure * * NAND write with ECC */static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel){ int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr; int autoplace = 0, numpages, totalpages; struct nand_chip *this = mtd->priv; u_char *oobbuf, *bufstart; int ppblock = (1 << (this->phys_erase_shift - this->page_shift)); DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ *retlen = 0; /* Do not allow write past end of device */ if ((to + len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); return -EINVAL; } /* reject writes, which are not page aligned */ if (NOTALIGNED (to) || NOTALIGNED(len)) { printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_device (this, mtd, FL_WRITING); /* Calculate chipnr */ chipnr = (int)(to >> this->chip_shift); /* Select the NAND device */ this->select_chip(mtd, chipnr); /* Check, if it is write protected */ if (nand_check_wp(mtd)) goto out; /* if oobsel is NULL, use chip defaults */ if (oobsel == NULL) oobsel = &mtd->oobinfo; /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) { oobsel = this->autooob; autoplace = 1; } if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) autoplace = 1; /* Setup variables and oob buffer */ totalpages = len >> this->page_shift; page = (int) (to >> this->page_shift); /* Invalidate the page cache, if we write to the cached page */ if (page <= this->pagebuf && this->pagebuf < (page + totalpages)) this->pagebuf = -1; /* Set it relative to chip */ page &= this->pagemask; startpage = page; /* Calc number of pages we can write in one go */ numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages); oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -