diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2006-09-28 09:38:36 -0400 |
---|---|---|
committer | Artem Bityutskiy <dedekind@infradead.org> | 2006-11-29 10:03:52 -0500 |
commit | 29072b96078ffde36f03d51e6b5d0cff1ba8c7df (patch) | |
tree | 1353a27ae7e594044c521e28abf558e3796f98c9 /drivers/mtd/nand | |
parent | f6a7ecb18dabd88bd9f28e7bece564cabe8ffe82 (diff) |
[MTD] NAND: add subpage write support
Many SLC NANDs support up to 4 writes at one NAND page. Add support
of this feature.
Signed-off-by: Artem Bityutskiy <dedekind@infradead.org>
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 53 |
1 files changed, 44 insertions, 9 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5dcb2e066ce7..eed3271b99cc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -1590,7 +1590,7 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, | |||
1590 | return NULL; | 1590 | return NULL; |
1591 | } | 1591 | } |
1592 | 1592 | ||
1593 | #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0 | 1593 | #define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0 |
1594 | 1594 | ||
1595 | /** | 1595 | /** |
1596 | * nand_do_write_ops - [Internal] NAND write with ECC | 1596 | * nand_do_write_ops - [Internal] NAND write with ECC |
@@ -1603,15 +1603,16 @@ static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, | |||
1603 | static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | 1603 | static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, |
1604 | struct mtd_oob_ops *ops) | 1604 | struct mtd_oob_ops *ops) |
1605 | { | 1605 | { |
1606 | int chipnr, realpage, page, blockmask; | 1606 | int chipnr, realpage, page, blockmask, column; |
1607 | struct nand_chip *chip = mtd->priv; | 1607 | struct nand_chip *chip = mtd->priv; |
1608 | uint32_t writelen = ops->len; | 1608 | uint32_t writelen = ops->len; |
1609 | uint8_t *oob = ops->oobbuf; | 1609 | uint8_t *oob = ops->oobbuf; |
1610 | uint8_t *buf = ops->datbuf; | 1610 | uint8_t *buf = ops->datbuf; |
1611 | int bytes = mtd->writesize; | 1611 | int ret, subpage; |
1612 | int ret; | ||
1613 | 1612 | ||
1614 | ops->retlen = 0; | 1613 | ops->retlen = 0; |
1614 | if (!writelen) | ||
1615 | return 0; | ||
1615 | 1616 | ||
1616 | /* reject writes, which are not page aligned */ | 1617 | /* reject writes, which are not page aligned */ |
1617 | if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { | 1618 | if (NOTALIGNED(to) || NOTALIGNED(ops->len)) { |
@@ -1620,8 +1621,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
1620 | return -EINVAL; | 1621 | return -EINVAL; |
1621 | } | 1622 | } |
1622 | 1623 | ||
1623 | if (!writelen) | 1624 | column = to & (mtd->writesize - 1); |
1624 | return 0; | 1625 | subpage = column || (writelen & (mtd->writesize - 1)); |
1626 | |||
1627 | if (subpage && oob) | ||
1628 | return -EINVAL; | ||
1625 | 1629 | ||
1626 | chipnr = (int)(to >> chip->chip_shift); | 1630 | chipnr = (int)(to >> chip->chip_shift); |
1627 | chip->select_chip(mtd, chipnr); | 1631 | chip->select_chip(mtd, chipnr); |
@@ -1644,12 +1648,24 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
1644 | memset(chip->oob_poi, 0xff, mtd->oobsize); | 1648 | memset(chip->oob_poi, 0xff, mtd->oobsize); |
1645 | 1649 | ||
1646 | while(1) { | 1650 | while(1) { |
1651 | int bytes = mtd->writesize; | ||
1647 | int cached = writelen > bytes && page != blockmask; | 1652 | int cached = writelen > bytes && page != blockmask; |
1653 | uint8_t *wbuf = buf; | ||
1654 | |||
1655 | /* Partial page write ? */ | ||
1656 | if (unlikely(column || writelen < (mtd->writesize - 1))) { | ||
1657 | cached = 0; | ||
1658 | bytes = min_t(int, bytes - column, (int) writelen); | ||
1659 | chip->pagebuf = -1; | ||
1660 | memset(chip->buffers->databuf, 0xff, mtd->writesize); | ||
1661 | memcpy(&chip->buffers->databuf[column], buf, bytes); | ||
1662 | wbuf = chip->buffers->databuf; | ||
1663 | } | ||
1648 | 1664 | ||
1649 | if (unlikely(oob)) | 1665 | if (unlikely(oob)) |
1650 | oob = nand_fill_oob(chip, oob, ops); | 1666 | oob = nand_fill_oob(chip, oob, ops); |
1651 | 1667 | ||
1652 | ret = chip->write_page(mtd, chip, buf, page, cached, | 1668 | ret = chip->write_page(mtd, chip, wbuf, page, cached, |
1653 | (ops->mode == MTD_OOB_RAW)); | 1669 | (ops->mode == MTD_OOB_RAW)); |
1654 | if (ret) | 1670 | if (ret) |
1655 | break; | 1671 | break; |
@@ -1658,6 +1674,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, | |||
1658 | if (!writelen) | 1674 | if (!writelen) |
1659 | break; | 1675 | break; |
1660 | 1676 | ||
1677 | column = 0; | ||
1661 | buf += bytes; | 1678 | buf += bytes; |
1662 | realpage++; | 1679 | realpage++; |
1663 | 1680 | ||
@@ -2201,8 +2218,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, | |||
2201 | /* Newer devices have all the information in additional id bytes */ | 2218 | /* Newer devices have all the information in additional id bytes */ |
2202 | if (!type->pagesize) { | 2219 | if (!type->pagesize) { |
2203 | int extid; | 2220 | int extid; |
2204 | /* The 3rd id byte contains non relevant data ATM */ | 2221 | /* The 3rd id byte holds MLC / multichip data */ |
2205 | extid = chip->read_byte(mtd); | 2222 | chip->cellinfo = chip->read_byte(mtd); |
2206 | /* The 4th id byte is the important one */ | 2223 | /* The 4th id byte is the important one */ |
2207 | extid = chip->read_byte(mtd); | 2224 | extid = chip->read_byte(mtd); |
2208 | /* Calc pagesize */ | 2225 | /* Calc pagesize */ |
@@ -2482,6 +2499,24 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2482 | } | 2499 | } |
2483 | chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; | 2500 | chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; |
2484 | 2501 | ||
2502 | /* | ||
2503 | * Allow subpage writes up to ecc.steps. Not possible for MLC | ||
2504 | * FLASH. | ||
2505 | */ | ||
2506 | if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && | ||
2507 | !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { | ||
2508 | switch(chip->ecc.steps) { | ||
2509 | case 2: | ||
2510 | mtd->subpage_sft = 1; | ||
2511 | break; | ||
2512 | case 4: | ||
2513 | case 8: | ||
2514 | mtd->subpage_sft = 2; | ||
2515 | break; | ||
2516 | } | ||
2517 | } | ||
2518 | chip->subpagesize = mtd->writesize >> mtd->subpage_sft; | ||
2519 | |||
2485 | /* Initialize state */ | 2520 | /* Initialize state */ |
2486 | chip->state = FL_READY; | 2521 | chip->state = FL_READY; |
2487 | 2522 | ||