* access
*/
ofs += mtd->oobsize;
- chip->ops.len = 2;
+ chip->ops.len = chip->ops.ooblen = 2;
chip->ops.datbuf = NULL;
chip->ops.oobbuf = buf;
chip->ops.ooboffs = chip->badblockpos & ~0x01;
* Wait for the ready pin, after a command
* The timeout is catched later.
*/
-static void nand_wait_ready(struct mtd_info *mtd)
+void nand_wait_ready(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
unsigned long timeo = jiffies + 2;
} while (time_before(jiffies, timeo));
led_trigger_event(nand_led_trigger, LED_OFF);
}
+EXPORT_SYMBOL_GPL(nand_wait_ready);
/**
* nand_command - [DEFAULT] Send command to NAND device
}
/**
- * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
+ * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
- uint8_t *ecc_code = chip->buffers.ecccode;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
int *eccpos = chip->ecc.layout->eccpos;
nand_read_page_raw(mtd, chip, buf);
}
/**
- * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function
+ * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
- uint8_t *ecc_code = chip->buffers.ecccode;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
int *eccpos = chip->ecc.layout->eccpos;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
}
/**
- * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
+ * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
* @chip: nand chip structure
* @oob: oob destination address
* @ops: oob ops structure
+ * @len: size of oob to transfer
*/
static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
- struct mtd_oob_ops *ops)
+ struct mtd_oob_ops *ops, size_t len)
{
- size_t len = ops->ooblen;
-
switch(ops->mode) {
case MTD_OOB_PLACE:
int sndcmd = 1;
int ret = 0;
uint32_t readlen = ops->len;
+ uint32_t oobreadlen = ops->ooblen;
uint8_t *bufpoi, *oob, *buf;
stats = mtd->ecc_stats;
page = realpage & chip->pagemask;
col = (int)(from & (mtd->writesize - 1));
- chip->oob_poi = chip->buffers.oobrbuf;
buf = ops->datbuf;
oob = ops->oobbuf;
/* Is the current page in the buffer ? */
if (realpage != chip->pagebuf || oob) {
- bufpoi = aligned ? buf : chip->buffers.databuf;
+ bufpoi = aligned ? buf : chip->buffers->databuf;
if (likely(sndcmd)) {
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
}
/* Now read the page into the buffer */
- ret = chip->ecc.read_page(mtd, chip, bufpoi);
+ if (unlikely(ops->mode == MTD_OOB_RAW))
+ ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
+ else
+ ret = chip->ecc.read_page(mtd, chip, bufpoi);
if (ret < 0)
break;
/* Transfer not aligned data */
if (!aligned) {
chip->pagebuf = realpage;
- memcpy(buf, chip->buffers.databuf + col, bytes);
+ memcpy(buf, chip->buffers->databuf + col, bytes);
}
buf += bytes;
if (unlikely(oob)) {
/* Raw mode does data:oob:data:oob */
- if (ops->mode != MTD_OOB_RAW)
- oob = nand_transfer_oob(chip, oob, ops);
- else
- buf = nand_transfer_oob(chip, buf, ops);
+ if (ops->mode != MTD_OOB_RAW) {
+ int toread = min(oobreadlen,
+ chip->ecc.layout->oobavail);
+ if (toread) {
+ oob = nand_transfer_oob(chip,
+ oob, ops, toread);
+ oobreadlen -= toread;
+ }
+ } else
+ buf = nand_transfer_oob(chip,
+ buf, ops, mtd->oobsize);
}
if (!(chip->options & NAND_NO_READRDY)) {
nand_wait_ready(mtd);
}
} else {
- memcpy(buf, chip->buffers.databuf + col, bytes);
+ memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes;
}
}
ops->retlen = ops->len - (size_t) readlen;
+ if (oob)
+ ops->oobretlen = ops->ooblen - oobreadlen;
if (ret)
return ret;
int page, realpage, chipnr, sndcmd = 1;
struct nand_chip *chip = mtd->priv;
int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
- int readlen = ops->len;
+ int readlen = ops->ooblen;
+ int len;
uint8_t *buf = ops->oobbuf;
DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
(unsigned long long)from, readlen);
+ if (ops->mode == MTD_OOB_RAW)
+ len = mtd->oobsize;
+ else
+ len = chip->ecc.layout->oobavail;
+
chipnr = (int)(from >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask;
- chip->oob_poi = chip->buffers.oobrbuf;
-
while(1) {
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
- buf = nand_transfer_oob(chip, buf, ops);
+
+ len = min(len, readlen);
+ buf = nand_transfer_oob(chip, buf, ops, len);
if (!(chip->options & NAND_NO_READRDY)) {
/*
nand_wait_ready(mtd);
}
- readlen -= ops->ooblen;
+ readlen -= len;
if (!readlen)
break;
sndcmd = 1;
}
- ops->retlen = ops->len;
+ ops->oobretlen = ops->ooblen;
return 0;
}
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf) = NULL;
struct nand_chip *chip = mtd->priv;
int ret = -ENOTSUPP;
ops->retlen = 0;
/* Do not allow reads past end of device */
- if ((from + ops->len) > mtd->size) {
+ if (ops->datbuf && (from + ops->len) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
"Attempt read beyond end of device\n");
return -EINVAL;
switch(ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_AUTO:
- break;
-
case MTD_OOB_RAW:
- /* Replace the read_page algorithm temporary */
- read_page = chip->ecc.read_page;
- chip->ecc.read_page = nand_read_page_raw;
break;
default:
else
ret = nand_do_read_ops(mtd, from, ops);
- if (unlikely(ops->mode == MTD_OOB_RAW))
- chip->ecc.read_page = read_page;
out:
nand_release_device(mtd);
return ret;
}
/**
- * nand_write_page_swecc - {REPLACABLE] software ecc based page write function
+ * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
int *eccpos = chip->ecc.layout->eccpos;
}
/**
- * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function
+ * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
int *eccpos = chip->ecc.layout->eccpos;
}
/**
- * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write
+ * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
}
/**
- * nand_write_page - [INTERNAL] write one page
+ * nand_write_page - [REPLACEABLE] write one page
* @mtd: MTD device structure
* @chip: NAND chip descriptor
* @buf: the data to write
* @page: page number to write
* @cached: cached programming
+ * @raw: use _raw version of write_page
*/
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int page, int cached)
+ const uint8_t *buf, int page, int cached, int raw)
{
int status;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
- chip->ecc.write_page(mtd, chip, buf);
+ if (unlikely(raw))
+ chip->ecc.write_page_raw(mtd, chip, buf);
+ else
+ chip->ecc.write_page(mtd, chip, buf);
/*
* Cached progamming disabled for now, Not sure if its worth the
return NULL;
}
-#define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
+#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
/**
* nand_do_write_ops - [Internal] NAND write with ECC
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- int chipnr, realpage, page, blockmask;
+ int chipnr, realpage, page, blockmask, column;
struct nand_chip *chip = mtd->priv;
uint32_t writelen = ops->len;
uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf;
- int bytes = mtd->writesize;
- int ret;
+ int ret, subpage;
ops->retlen = 0;
+ if (!writelen)
+ return 0;
/* reject writes, which are not page aligned */
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
return -EINVAL;
}
- if (!writelen)
- return 0;
+ column = to & (mtd->writesize - 1);
+ subpage = column || (writelen & (mtd->writesize - 1));
+
+ if (subpage && oob)
+ return -EINVAL;
chipnr = (int)(to >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
(chip->pagebuf << chip->page_shift) < (to + ops->len))
chip->pagebuf = -1;
- chip->oob_poi = chip->buffers.oobwbuf;
+ /* If we're not given explicit OOB data, let it be 0xFF */
+ if (likely(!oob))
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
while(1) {
+ int bytes = mtd->writesize;
int cached = writelen > bytes && page != blockmask;
+ uint8_t *wbuf = buf;
+
+ /* Partial page write ? */
+ if (unlikely(column || writelen < (mtd->writesize - 1))) {
+ cached = 0;
+ bytes = min_t(int, bytes - column, (int) writelen);
+ chip->pagebuf = -1;
+ memset(chip->buffers->databuf, 0xff, mtd->writesize);
+ memcpy(&chip->buffers->databuf[column], buf, bytes);
+ wbuf = chip->buffers->databuf;
+ }
if (unlikely(oob))
oob = nand_fill_oob(chip, oob, ops);
- ret = nand_write_page(mtd, chip, buf, page, cached);
+ ret = chip->write_page(mtd, chip, wbuf, page, cached,
+ (ops->mode == MTD_OOB_RAW));
if (ret)
break;
if (!writelen)
break;
+ column = 0;
buf += bytes;
realpage++;
}
}
- if (unlikely(oob))
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
ops->retlen = ops->len - writelen;
+ if (unlikely(oob))
+ ops->oobretlen = ops->ooblen;
return ret;
}
struct nand_chip *chip = mtd->priv;
DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
- (unsigned int)to, (int)ops->len);
+ (unsigned int)to, (int)ops->ooblen);
/* Do not allow write past end of page */
- if ((ops->ooboffs + ops->len) > mtd->oobsize) {
+ if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
"Attempt to write past end of page\n");
return -EINVAL;
if (page == chip->pagebuf)
chip->pagebuf = -1;
- chip->oob_poi = chip->buffers.oobwbuf;
memset(chip->oob_poi, 0xff, mtd->oobsize);
nand_fill_oob(chip, ops->oobbuf, ops);
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
if (status)
return status;
- ops->retlen = ops->len;
+ ops->oobretlen = ops->ooblen;
return 0;
}
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf) = NULL;
struct nand_chip *chip = mtd->priv;
int ret = -ENOTSUPP;
ops->retlen = 0;
/* Do not allow writes past end of device */
- if ((to + ops->len) > mtd->size) {
+ if (ops->datbuf && (to + ops->len) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
"Attempt read beyond end of device\n");
return -EINVAL;
switch(ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_AUTO:
- break;
-
case MTD_OOB_RAW:
- /* Replace the write_page algorithm temporary */
- write_page = chip->ecc.write_page;
- chip->ecc.write_page = nand_write_page_raw;
break;
default:
else
ret = nand_do_write_ops(mtd, to, ops);
- if (unlikely(ops->mode == MTD_OOB_RAW))
- chip->ecc.write_page = write_page;
out:
nand_release_device(mtd);
return ret;
/* Newer devices have all the information in additional id bytes */
if (!type->pagesize) {
int extid;
- /* The 3rd id byte contains non relevant data ATM */
- extid = chip->read_byte(mtd);
+ /* The 3rd id byte holds MLC / multichip data */
+ chip->cellinfo = chip->read_byte(mtd);
/* The 4th id byte is the important one */
extid = chip->read_byte(mtd);
/* Calc pagesize */
return type;
}
-/* module_text_address() isn't exported, and it's mostly a pointless
- test if this is a module _anyway_ -- they'd have to try _really_ hard
- to call us from in-kernel code if the core NAND support is modular. */
-#ifdef MODULE
-#define caller_is_module() (1)
-#else
-#define caller_is_module() \
- module_text_address((unsigned long)__builtin_return_address(0))
-#endif
-
/**
- * nand_scan - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: Number of chips to scan for
+ * nand_scan_ident - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: Number of chips to scan for
*
- * This fills out all the uninitialized function pointers
- * with the defaults.
- * The flash ID is read and the mtd/chip structures are
- * filled with the appropriate values.
- * The mtd->owner field must be set to the module of the caller
+ * This is the first phase of the normal nand_scan() function. It
+ * reads the flash ID and sets up MTD fields accordingly.
*
+ * The mtd->owner field must be set to the module of the caller.
*/
-int nand_scan(struct mtd_info *mtd, int maxchips)
+int nand_scan_ident(struct mtd_info *mtd, int maxchips)
{
int i, busw, nand_maf_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
- /* Many callers got this wrong, so check for it for a while... */
- if (!mtd->owner && caller_is_module()) {
- printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
- BUG();
- }
-
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
chip->numchips = i;
mtd->size = i * chip->chipsize;
- /* Preset the internal oob write buffer */
- memset(chip->buffers.oobwbuf, 0xff, mtd->oobsize);
+ return 0;
+}
+
+
+/**
+ * nand_scan_tail - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: Number of chips to scan for
+ *
+ * This is the second phase of the normal nand_scan() function. It
+ * fills out all the uninitialized function pointers with the defaults
+ * and scans for a bad block table if appropriate.
+ */
+int nand_scan_tail(struct mtd_info *mtd)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+
+ if (!(chip->options & NAND_OWN_BUFFERS))
+ chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
+ if (!chip->buffers)
+ return -ENOMEM;
+
+ /* Set the internal oob buffer location, just after the page data */
+ chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/*
* If no default placement scheme is given, select an appropriate one
}
}
+ if (!chip->write_page)
+ chip->write_page = nand_write_page;
+
/*
* check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
+ if (!chip->ecc.read_page_raw)
+ chip->ecc.read_page_raw = nand_read_page_raw;
+ if (!chip->ecc.write_page_raw)
+ chip->ecc.write_page_raw = nand_write_page_raw;
+
switch (chip->ecc.mode) {
case NAND_ECC_HW:
/* Use standard hwecc read page function ? */
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0;
break;
+
default:
printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
chip->ecc.mode);
}
chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
+ /*
+ * Allow subpage writes up to ecc.steps. Not possible for MLC
+ * FLASH.
+ */
+ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
+ !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
+ switch(chip->ecc.steps) {
+ case 2:
+ mtd->subpage_sft = 1;
+ break;
+ case 4:
+ case 8:
+ mtd->subpage_sft = 2;
+ break;
+ }
+ }
+ chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
/* Initialize state */
chip->state = FL_READY;
return chip->scan_bbt(mtd);
}
+/* module_text_address() isn't exported, and it's mostly a pointless
+ test if this is a module _anyway_ -- they'd have to try _really_ hard
+ to call us from in-kernel code if the core NAND support is modular. */
+#ifdef MODULE
+#define caller_is_module() (1)
+#else
+#define caller_is_module() \
+ module_text_address((unsigned long)__builtin_return_address(0))
+#endif
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: Number of chips to scan for
+ *
+ * This fills out all the uninitialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ * The mtd->owner field must be set to the module of the caller
+ *
+ */
+int nand_scan(struct mtd_info *mtd, int maxchips)
+{
+ int ret;
+
+ /* Many callers got this wrong, so check for it for a while... */
+ if (!mtd->owner && caller_is_module()) {
+ printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
+ BUG();
+ }
+
+ ret = nand_scan_ident(mtd, maxchips);
+ if (!ret)
+ ret = nand_scan_tail(mtd);
+ return ret;
+}
+
/**
* nand_release - [NAND Interface] Free resources held by the NAND device
* @mtd: MTD device structure
/* Free bad block table memory */
kfree(chip->bbt);
+ if (!(chip->options & NAND_OWN_BUFFERS))
+ kfree(chip->buffers);
}
EXPORT_SYMBOL_GPL(nand_scan);
+EXPORT_SYMBOL_GPL(nand_scan_ident);
+EXPORT_SYMBOL_GPL(nand_scan_tail);
EXPORT_SYMBOL_GPL(nand_release);
static int __init nand_base_init(void)