[U-Boot] [PATCH] Flex-OneNAND driver

Hi All, This patch adds support for Samsung Flex-OneNAND devices.
Flex-OneNAND combines SLC and MLC technologies into a single device. SLC area provides increased reliability and speed, suitable for storing code and data, such as bootloader, kernel and root file system. MLC area provides high density and is best used for storing user data. Users can configure the size of SLC and MLC regions through 'onenand setboundary' command.
Signed-off-by: Rohit Hagargundgi h.rohit@samsung.com --- diff --git a/common/cmd_onenand.c b/common/cmd_onenand.c index 8d87b78..7260017 100644 --- a/common/cmd_onenand.c +++ b/common/cmd_onenand.c @@ -20,9 +20,64 @@
extern struct mtd_info onenand_mtd; extern struct onenand_chip onenand_chip; +loff_t flexonenand_get_addr(int block) +{ + struct mtd_info *mtd = &onenand_mtd; + struct onenand_chip *this = mtd->priv; + loff_t ofs; + int die = 0, boundary; + + ofs = 0; + if (this->dies == 2 && block >= this->density_mask) { + block -= this->density_mask; + die = 1; + ofs = this->diesize[0]; + } + boundary = this->boundary[die]; + ofs += block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs += (block - boundary - 1) << (this->erase_shift - 1); + return ofs; +} + +static int do_erase(ulong start, ulong end) +{ + struct mtd_info *mtd = &onenand_mtd; + struct onenand_chip *this = mtd->priv; + struct erase_info instr = { + .callback = NULL, + }; + int i, ret; + ulong block; + + printf("Erase block from %lu to %lu\n", start, end); + + for (block = start; block <= end; block++) { + if (FLEXONENAND(this)) + instr.addr = flexonenand_get_addr(block); + else + instr.addr = block << onenand_chip.erase_shift; + + if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) { + for (i = 0; i < mtd->numeraseregions && + mtd->eraseregions[i].offset <= instr.addr; i++) + ; + i--; + instr.len = + mtd->eraseregions[i].erasesize; + } else + instr.len = mtd->erasesize; + ret = onenand_erase(&onenand_mtd, &instr); + if (ret) + printf("erase failed %lu\n", block); + } + return 0; +}
int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { + struct mtd_info *mtd = &onenand_mtd; + struct onenand_chip *this = mtd->priv; int ret = 0;
switch (argc) { @@ -42,11 +97,7 @@ default: /* At least 4 args */ if (strncmp(argv[1], "erase", 5) == 0) { - struct erase_info instr = { - .callback = NULL, - }; ulong start, end; - ulong block; char *endtail;
if (strncmp(argv[2], "block", 5) == 0) { @@ -57,28 +108,18 @@ start = simple_strtoul(argv[2], NULL, 10); end = simple_strtoul(argv[3], NULL, 10);
- start >>= onenand_chip.erase_shift; - end >>= onenand_chip.erase_shift; + start = onenand_get_block(&onenand_mtd, + start, NULL); + end = onenand_get_block(&onenand_mtd, + end, NULL); /* Don't include the end block */ - end--; + if (end > 0) + end--; }
if (!end || end < 0) end = start; - - printf("Erase block from %lu to %lu\n", start, end); - - for (block = start; block <= end; block++) { - instr.addr = block << onenand_chip.erase_shift; - instr.len = 1 << onenand_chip.erase_shift; - ret = onenand_erase(&onenand_mtd, &instr); - if (ret) { - printf("erase failed %lu\n", block); - break; - } - } - - return 0; + return do_erase(start, end); }
if (strncmp(argv[1], "read", 4) == 0) { @@ -134,15 +175,18 @@ ops.mode = MTD_OOB_PLACE;
- ofs = block << onenand_chip.erase_shift; + if (FLEXONENAND(this)) + ofs = flexonenand_get_addr(block); + else + ofs = block << onenand_chip.erase_shift; if (page) ofs += page << onenand_chip.page_shift;
if (!len) { if (oob) - ops.ooblen = 64; + ops.ooblen = FLEXONENAND(this) ? 128 : 64; else - ops.len = 512; + ops.len = FLEXONENAND(this) ? 4096 : 512; }
if (oob) { @@ -158,6 +202,39 @@ return 0; }
+ if (strncmp(argv[1], "setboundary", 11) == 0) { + unsigned die = simple_strtoul(argv[2], NULL, 0); + unsigned bdry = simple_strtoul(argv[3], NULL, 0); + int lock = 0, old; + + if (!FLEXONENAND(this)) { + printf("Flex-OneNAND not found.\n"); + return -1; + } + + if (argc == 5 && strncmp(argv[4], "LOCK", 4) == 0) + lock = 1; + + if (die >= this->dies) { + printf("Invalid die index\n"); + return -1; + } + + if (!(bdry % 2)) { + printf("Attempt to set even boundary value.\n"); + bdry += 1; + printf("Setting boundary to %d\n", bdry); + } + + old = this->boundary[die] + (die * this->density_mask); + ret = flexonenand_set_boundary(mtd, die, bdry, lock); + if (!ret) { + int new = this->boundary[die] + + (die * this->density_mask); + do_erase(min(old, new) + 1, max(old, new)); + } + return 0; + } break; }
@@ -172,5 +249,7 @@ "onenand write addr ofs len - write data at ofs with len from addr\n" "onenand erase saddr eaddr - erase block start addr to end addr\n" "onenand block[.oob] addr block [page] [len] - " - "read data with (block [, page]) to addr" + "read data with (block [, page]) to addr\n" + "onenand setboundary DIE BOUNDARY [LOCK] - " + "Change SLC boundary of Flex-OneNAND" ); diff --git a/common/env_onenand.c b/common/env_onenand.c --- a/common/env_onenand.c +++ b/common/env_onenand.c @@ -58,11 +58,14 @@
void env_relocate_spec(void) { + struct onenand_chip *this = &onenand_chip; unsigned long env_addr; int use_default = 0; size_t retlen;
env_addr = CONFIG_ENV_ADDR; + if (FLEXONENAND(this)) + env_addr = CONFIG_ENV_ADDR_FLEX;
/* Check OneNAND exist */ if (onenand_mtd.writesize) @@ -89,6 +92,7 @@
int saveenv(void) { + struct onenand_chip *this = &onenand_chip; unsigned long env_addr = CONFIG_ENV_ADDR; struct erase_info instr = { .callback = NULL, @@ -96,6 +100,12 @@ size_t retlen;
instr.len = CONFIG_ENV_SIZE; + if (FLEXONENAND(this)) { + env_addr = CONFIG_ENV_ADDR_FLEX; + instr.len = CONFIG_ENV_SIZE_FLEX; + instr.len <<= onenand_mtd.eraseregions[0].numblocks == 1 ? + 1 : 0; + } instr.addr = env_addr; if (onenand_erase(&onenand_mtd, &instr)) { printf("OneNAND: erase failed at 0x%08lx\n", env_addr); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -45,6 +45,14 @@ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ };
/** @@ -83,9 +91,11 @@ if (device & ONENAND_DEVICE_IS_DDP) { /* Device Flash Core select, NAND Flash Block Address */ int dfs = 0, density, mask; + int flex = device & DEVICE_IS_FLEXONENAND;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - mask = (1 << (density + 6)); + density &= ONENAND_DEVICE_DENSITY_MASK; + mask = (1 << (density + (flex ? 4 : 6)));
if (block & mask) dfs = 1; @@ -109,9 +119,11 @@ if (device & ONENAND_DEVICE_IS_DDP) { /* Device BufferRAM Select */ int dbs = 0, density, mask; + int flex = device & DEVICE_IS_FLEXONENAND;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - mask = (1 << (density + 6)); + density &= ONENAND_DEVICE_DENSITY_MASK; + mask = (1 << (density + (flex ? 4 : 6)));
if (block & mask) dbs = 1; @@ -168,6 +180,41 @@ return ((bsa << ONENAND_BSA_SHIFT) | bsc); }
+unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr, + unsigned *isblkslc) +{ + struct onenand_chip *this = mtd->priv; + unsigned boundary, blk, die = 0; + + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + + if (this->chipsize == 0) { + /* We have been called by read_boundary + * addr contains die index in this case + */ + blk = addr * this->density_mask; + return blk; + } + + if (addr >= this->diesize[0]) { + die = 1; + addr -= this->diesize[0]; + } + + boundary = this->boundary[die]; + + blk = addr >> (this->erase_shift - 1); + if (blk > boundary) + blk = (blk + boundary + 1) >> 1; + + if (isblkslc) + *isblkslc = (blk <= boundary) ? 1 : 0; + + blk += die ? this->density_mask : 0; + return blk; +} + /** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd MTD device structure @@ -182,30 +229,36 @@ size_t len) { struct onenand_chip *this = mtd->priv; - int value, readcmd = 0; + int value; int block, page; + unsigned slc = 0; + /* Now we use page size operation */ - int sectors = 4, count = 4; + int sectors = 0, count = 0;
/* Address translation */ switch (cmd) { case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_UNLOCK_ALL: case ONENAND_CMD_LOCK: case ONENAND_CMD_LOCK_TIGHT: block = -1; page = -1; break;
+ case FLEXONENAND_CMD_PI_ACCESS: case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: - block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, NULL); page = -1; break;
default: - block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, &slc); page = (int)(addr >> this->page_shift); page &= this->page_mask; + if (FLEXONENAND(this) && slc) + page &= (this->page_mask >> 1); break; }
@@ -216,8 +269,11 @@ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_MLC(this)) + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this);
return 0; } @@ -227,6 +283,10 @@ value = onenand_block_address(this->device_id, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS2); }
if (page != -1) { @@ -235,8 +295,11 @@ switch (cmd) { case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - dataram = ONENAND_SET_NEXT_BUFFERRAM(this); - readcmd = 1; + if (ONENAND_IS_MLC(this)) + dataram = ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); break;
default: @@ -253,14 +316,6 @@ value = onenand_buffer_address(dataram, sectors, count); this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
- if (readcmd) { - /* Select DataRAM for DDP */ - value = - onenand_bufferram_address(this->device_id, block); - this->write_word(value, - this->base + - ONENAND_REG_START_ADDRESS2); - } }
/* Interrupt clear */ @@ -272,6 +327,29 @@ }
/** + * onenand_get_ecc - return ecc status + * @param mtd MTD device structure + */ +int onenand_get_ecc(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int ecc[4]; + int i, result = 0; + + for (i = 0; i < 4; i++) { + ecc[i] = this->read_word(this->base + ((0xFF00 + i) << 1)); + if (!FLEXONENAND(this)) + return ecc[i]; + if (ecc[i] & FLEXONENAND_UNCORRECTABLE_ERROR) { + result = ONENAND_ECC_2BIT_ALL; + break; + } + } + + return result; +} + +/** * onenand_wait - [DEFAULT] wait until the command is done * @param mtd MTD device structure * @param state state to select the max. timeout value @@ -295,6 +373,15 @@
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+ if (interrupt & ONENAND_INT_READ) { + ecc = onenand_get_ecc(mtd); + if (ecc & ONENAND_ECC_2BIT_ALL) { + MTDDEBUG(MTD_DEBUG_LEVEL0, + "onenand_wait: ECC error = 0x%04x\n", ecc); + return -EBADMSG; + } + } + if (ctrl & ONENAND_CTRL_ERROR) { MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); @@ -307,15 +394,6 @@ return -EIO; }
- if (interrupt & ONENAND_INT_READ) { - ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_wait: ECC error = 0x%04x\n", ecc); - return -EBADMSG; - } - } - return 0; }
@@ -433,10 +511,13 @@ struct onenand_chip *this = mtd->priv; int block, page; int i; + unsigned slc = 0;
- block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, &slc); page = (int)(addr >> this->page_shift); page &= this->page_mask; + if (FLEXONENAND(this) && slc) + page &= (this->page_mask >> 1);
i = ONENAND_CURRENT_BUFFERRAM(this);
@@ -462,10 +543,13 @@ struct onenand_chip *this = mtd->priv; int block, page; int i; + unsigned slc = 0;
- block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, &slc); page = (int)(addr >> this->page_shift); page &= this->page_mask; + if (FLEXONENAND(this) && slc) + page &= (this->page_mask >> 1);
/* Invalidate BufferRAM */ for (i = 0; i < MAX_BUFFERRAM; i++) { @@ -573,6 +657,39 @@ }
/** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param mtd MTD device structure + * @param addr address to recover + * @param status return value from onenand_wait + * + * Issue recovery command when read fails on MLC area. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) +{ + struct onenand_chip *this = mtd->priv; + unsigned slc = 0; + + /* check if we failed due to uncorrectable error */ + if (status != (-EBADMSG) && status != (ONENAND_BBT_READ_ECC_ERROR)) + return status; + + /* check if address lies in MLC region */ + onenand_get_block(mtd, addr, &slc); + if (slc) + return status; + + /* We are attempting to reread, so decrement stats.failed + * which was incremented by onenand_wait due to read failure + */ + printk(KERN_DEBUG "Attempting to recover from uncorrectable read\n"); + mtd->ecc_stats.failed--; + + /* Issue the LSB page recovery command */ + this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); + return this->wait(mtd, FL_READING); +} + +/** * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure * @param from offset to read from @@ -616,12 +733,15 @@ stats = mtd->ecc_stats;
/* Read-while-load method */ + /* Note: We can't use this feature in MLC */
/* Do first load to bufferRAM */ if (read < len) { if (!onenand_check_bufferram(mtd, from)) { this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); + ret = (FLEXONENAND(this) && ret) ? + onenand_recover_lsb(mtd, from, ret) : ret; onenand_update_bufferram(mtd, from, !ret); if (ret == -EBADMSG) ret = 0; @@ -636,7 +756,7 @@ while (!ret) { /* If there is more to load then start next load */ from += thislen; - if (read + thislen < len) { + if (!ONENAND_IS_MLC(this) && read + thislen < len) { this->command(mtd, ONENAND_CMD_READ, from, writesize); /* * Chip boundary handling in DDP @@ -669,6 +789,16 @@ oobcolumn = 0; }
+ if (ONENAND_IS_MLC(this) && (read + thislen < len)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + ret = this->wait(mtd, FL_READING); + ret = (FLEXONENAND(this) && ret) ? + onenand_recover_lsb(mtd, from, ret) : ret; + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } + /* See if we are done */ read += thislen; if (read == len) @@ -676,16 +806,19 @@ /* Set up for next read from bufferRAM */ if (unlikely(boundary)) this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); - ONENAND_SET_NEXT_BUFFERRAM(this); + if (!ONENAND_IS_MLC(this)) + ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, writesize, len - read); column = 0;
- /* Now wait for load */ - ret = this->wait(mtd, FL_READING); - onenand_update_bufferram(mtd, from, !ret); - if (ret == -EBADMSG) - ret = 0; + if (!ONENAND_IS_MLC(this)) { + /* Now wait for load */ + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } }
/* @@ -722,7 +855,7 @@ size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; u_char *buf = ops->oobbuf; - int ret = 0; + int ret = 0, readcmd;
from += ops->ooboffs;
@@ -755,15 +888,19 @@
stats = mtd->ecc_stats;
+ readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + while (read < len) { thislen = oobsize - column; thislen = min_t(int, thislen, len);
- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
ret = this->wait(mtd, FL_READING); + ret = (FLEXONENAND(this) && ret) ? + onenand_recover_lsb(mtd, from, ret) : ret; if (ret && ret != -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); break; @@ -886,22 +1023,26 @@ interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
- /* Initial bad block case: 0x2400 or 0x0400 */ - if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); - return ONENAND_BBT_READ_ERROR; - } - if (interrupt & ONENAND_INT_READ) { - int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) + int ecc = onenand_get_ecc(mtd); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" + ", controller = 0x%04x\n", ecc, ctrl); return ONENAND_BBT_READ_ERROR; + } } else { printk(KERN_ERR "onenand_bbt_wait: read timeout!" "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); return ONENAND_BBT_READ_FATAL_ERROR; }
+ /* Initial bad block case: 0x2400 or 0x0400 */ + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "onenand_bbt_wait: controller error" + " = 0x%04x\n", ctrl); + return ONENAND_BBT_READ_ERROR; + } + return 0; }
@@ -918,7 +1059,7 @@ { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; - int ret = 0; + int ret = 0, readcmd; size_t len = ops->ooblen; u_char *buf = ops->oobbuf;
@@ -926,6 +1067,8 @@ "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
+ readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + /* Initialize return value */ ops->oobretlen = 0;
@@ -945,7 +1088,7 @@ thislen = mtd->oobsize - column; thislen = min_t(int, thislen, len);
- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
@@ -987,9 +1130,11 @@ { struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; - int status, i; + int status, i, readcmd; + + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
- this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); + this->command(mtd, readcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_READING); if (status) @@ -1236,7 +1381,7 @@ { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; - int written = 0; + int written = 0, oobcmd; u_char *oobbuf; size_t len = ops->ooblen; const u_char *buf = ops->oobbuf; @@ -1280,6 +1425,8 @@
oobbuf = this->oob_buf;
+ oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; + /* Loop until all data write */ while (written < len) { int thislen = min_t(int, oobsize, len - written); @@ -1295,7 +1442,14 @@ memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
- this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + if (ONENAND_IS_MLC(this)) { + /* Set main area of DataRAM to 0xff*/ + memset(this->page_buf, 0xff, mtd->writesize); + this->write_bufferram(mtd, ONENAND_DATARAM, + this->page_buf, 0, mtd->writesize); + } + + this->command(mtd, oobcmd, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { @@ -1424,19 +1578,30 @@ unsigned int block_size; loff_t addr; int len; - int ret = 0; + int ret = 0, i = 0;
MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int)instr->addr, (unsigned int)ins tr->len);
- block_size = (1 << this->erase_shift); + if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) { + for (; i < mtd->numeraseregions && + instr->addr >= mtd->eraseregions[i].offset; i++) + ; + i--; + block_size = mtd->eraseregions[i].erasesize; + } else + block_size = mtd->erasesize;
/* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_erase: Unaligned address\n"); - return -EINVAL; + /* We come here if boundary is even value. Just skip for now.*/ + if (!FLEXONENAND(this) || + (instr->addr & ((block_size >> 1) - 1))) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" + " Unaligned address\n"); + return -EINVAL; + } }
/* Length must align on block boundary */ @@ -1466,7 +1631,13 @@
while (len) {
- /* TODO Check badblock */ + /* Check if we have a bad block, we do not erase bad blocks */ + if (onenand_block_isbad_nolock(mtd, addr, 0)) { + printk(KERN_WARNING "onenand_erase: attempt to erase" + "bad block at addr 0x%08x\n", (unsigned int) addr); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + }
this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
@@ -1481,7 +1652,7 @@ else MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " "Failed erase, block %d\n", - (unsigned)(addr >> this->erase_shift)); + onenand_get_block(mtd, addr, NULL)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -1489,6 +1660,12 @@
len -= block_size; addr += block_size; + if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) { + if ((i < (mtd->numeraseregions - 1)) && + (addr == mtd->eraseregions[i + 1].offset)) + i++; + block_size = mtd->eraseregions[i].erasesize; + } }
instr->state = MTD_ERASE_DONE; @@ -1539,7 +1716,7 @@ return -EINVAL;
onenand_get_device(mtd, FL_READING); - ret = onenand_block_isbad_nolock(mtd,ofs, 0); + ret = onenand_block_isbad_nolock(mtd, ofs, 0); onenand_release_device(mtd); return ret; } @@ -1581,8 +1758,8 @@ struct onenand_chip *this = mtd->priv; int start, end, block, value, status;
- start = ofs >> this->erase_shift; - end = len >> this->erase_shift; + start = onenand_get_block(mtd, ofs, NULL); + end = onenand_get_block(mtd, ofs + len, NULL) - 1;
/* Continuous lock scheme */ if (this->options & ONENAND_CONT_LOCK) { @@ -1590,7 +1767,7 @@ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ - this->write_word(end - 1, + this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); @@ -1612,12 +1789,23 @@ }
/* Block lock scheme */ - for (block = start; block < end; block++) { + for (block = start; block < end + 1; block++) { + /* Set block address */ + value = onenand_block_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS2); /* Set start block address */ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ - this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); + if (FLEXONENAND(this)) + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + else + this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
/* There's no return value */ this->wait(mtd, FL_UNLOCKING); @@ -1643,6 +1831,25 @@ }
/** + * flexonenand_unlock_all - [FlexOneNAND Interface] unlock all blocks + * @param mtd MTD device structure + * + * Unlock all blocks + */ +static int flexonenand_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + size_t len = mtd->erasesize; + + if (mtd->numeraseregions > 1) + len >>= 1; + onenand_unlock(mtd, 0, len); + if (this->device_id & ONENAND_DEVICE_IS_DDP) + onenand_unlock(mtd, this->diesize[0], len); + return 0; +} + +/** * onenand_print_device_info - Print device ID * @param device device ID * @@ -1650,15 +1857,18 @@ */ char * onenand_print_device_info(int device) { - int vcc, demuxed, ddp, density; + int vcc, demuxed, ddp, density, flexonenand; char *dev_info = malloc(80);
vcc = device & ONENAND_DEVICE_VCC_MASK; demuxed = device & ONENAND_DEVICE_IS_DEMUX; ddp = device & ONENAND_DEVICE_IS_DDP; density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)", - demuxed ? "" : "Muxed ", + density &= ONENAND_DEVICE_DENSITY_MASK; + flexonenand = device & DEVICE_IS_FLEXONENAND; + sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)", + flexonenand ? "Flex-" : "", + demuxed ? "" : "Mux", ddp ? "(DDP)" : "", (16 << density), vcc ? "2.65/3.3" : "1.8", device);
@@ -1694,6 +1904,174 @@ }
/** +* flexonenand_get_boundary - Reads the SLC boundary +* @param onenand_info onenand info structure +**/ +static int flexonenand_get_boundary(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned die, bdry; + int ret, syscfg, unlocked; + + /* Disable ECC */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); + + for (die = 0; die < this->dies; die++) { + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, ONENAND_CMD_READ, die, 0); + ret = this->wait(mtd, FL_READING); + + bdry = this->read_word(this->base + ONENAND_DATARAM); + unlocked = bdry >> FLEXONENAND_PI_UNLOCK_SHIFT; + unlocked = (unlocked == 0x3) ? 1 : 0; + this->boundary[die] = bdry & FLEXONENAND_PI_MASK; + + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + ret = this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], + unlocked ? "(Unlocked)" : "(Locked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * get_flexonenand_size - Fill up fields in onenand_chip + * boundary[], diesize[], chipsize + * @param mtd - MTD device structure + */ +void get_flexonenand_size(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int die, ofs, i, eraseshift; + unsigned blksperdie = 1024; + unsigned maxbdry = blksperdie - 1; + + eraseshift = this->erase_shift - 1; + + this->chipsize = 0; + mtd->numeraseregions = this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die = ofs = 0; + i = -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = this->boundary[die] + 1; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -= 1; + mtd->eraseregions[i].numblocks += this->boundary[die] + 1; + ofs += (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = + maxbdry ^ this->boundary[die]; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -= 1; + } + + mtd->erasesize = 1 << (this->erase_shift); + if (mtd->numeraseregions == 1) + mtd->erasesize >>= 1; + + printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); + for (i = 0; i < mtd->numeraseregions; i++) + printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," + " numblocks: %04u]\n", mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + + eraseshift = this->erase_shift; + for (die = 0, mtd->size = 0; die < this->dies; die++) { + this->diesize[die] = (blksperdie << eraseshift); + this->diesize[die] -= (this->boundary[die] + 1) + << (eraseshift - 1); + mtd->size += this->diesize[die]; + } + /* this->chipsize represents maximum possible chip size */ + this->chipsize = (blksperdie << eraseshift) << (this->dies - 1); +} + +/** +* flexonenand_set_boundary - Writes the SLC boundary +* @param onenand_info onenand info structure +**/ +int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die, + int boundary, int lock) +{ + struct onenand_chip *this = mtd->priv; + int ret, density, blksperdie; + unsigned addr; + + density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density &= ONENAND_DEVICE_DENSITY_MASK; + + blksperdie = ((16 << density) << 20) >> this->erase_shift; + blksperdie >>= (this->device_id & ONENAND_DEVICE_IS_DDP) ? 1 : 0; + + addr = die ? this->diesize[0] : 0; + + if (this->boundary[die] == boundary) + return -1; + + printk(KERN_INFO "Changing boundary: %d%s\n", boundary, lock ? + "(Locked)" : "(Unlocked)"); + if (boundary >= blksperdie) { + printk(KERN_ERR "Invalid boundary value.\n"); + return -1; + } + + boundary &= FLEXONENAND_PI_MASK; + boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); + + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, addr, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, ONENAND_CMD_ERASE, addr, 0); + this->wait(mtd, FL_ERASING); + + this->write_word(boundary, this->base + ONENAND_DATARAM); + this->command(mtd, ONENAND_CMD_PROG, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "Failed PI write for Die %d\n", die); + goto out; + } + + this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) + printk(KERN_ERR "Failed PI update for Die %d\n", die); + else + printk(KERN_INFO "Done\n"); +out: + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); + this->wait(mtd, FL_RESETING); + if (!ret) + /* Recalculate device size on boundary change*/ + get_flexonenand_size(mtd); + return ret; +} + +/** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd MTD device structure * @@ -1727,31 +2105,47 @@ /* Read manufacturer and device IDs from Register */ maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
/* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) return -ENXIO;
- /* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */ - if (dev_id & (1 << 9)) { - printk("Not yet support Flex-OneNAND\n"); - return -ENXIO; - } - /* Flash device information */ mtd->name = onenand_print_device_info(dev_id); this->device_id = dev_id;
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; - this->chipsize = (16 << density) << 20; + density &= ONENAND_DEVICE_DENSITY_MASK; + if (FLEXONENAND(this)) { + this->dies = (dev_id & ONENAND_DEVICE_IS_DDP) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions = this->dies << 1; + mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1)); + if (!mtd->eraseregions) + return -ENOMEM; + } + this->chipsize = FLEXONENAND(this) ? 0 : (16 << density) << 20; + /* Set density mask. it is used for DDP */ + if (dev_id & ONENAND_DEVICE_IS_DDP) + this->density_mask = (1 << (density + (FLEXONENAND(this) ? + 4 : 6))); + else + this->density_mask = 0;
/* OneNAND page size & block size */ /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + /* We use the full BufferRAM */ + if (ONENAND_IS_MLC(this)) + mtd->writesize <<= 1; + mtd->oobsize = mtd->writesize >> 5; /* Pagers per block is always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; + mtd->erasesize <<= FLEXONENAND(this) ? 1 : 0;
this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; @@ -1762,7 +2156,10 @@
/* REVIST: Multichip handling */
- mtd->size = this->chipsize; + if (FLEXONENAND(this)) + get_flexonenand_size(mtd); + else + mtd->size = this->chipsize;
/* Version ID */ version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); @@ -1787,6 +2184,11 @@ mtd->block_isbad = onenand_block_isbad; mtd->block_markbad = onenand_block_markbad;
+ if (FLEXONENAND(this)) { + this->options &= ~ONENAND_CONT_LOCK; + this->options |= ONENAND_UNLOCK_ALL; + } + return 0; }
@@ -1850,7 +2252,8 @@ this->options |= ONENAND_OOBBUF_ALLOC; }
- onenand_unlock(mtd, 0, mtd->size); + FLEXONENAND(this) ? flexonenand_unlock_all(mtd) : + onenand_unlock(mtd, 0, mtd->size);
return onenand_default_bbt(mtd); } diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -66,6 +66,7 @@ struct bbm_info *bbm = this->bbm; int i, j, numblocks, len, scanlen; int startblock; + unsigned slc; loff_t from; size_t readlen, ooblen; struct mtd_oob_ops ops; @@ -82,7 +83,7 @@ /* Note that numblocks is 2 * (real numblocks) here; * see i += 2 below as it makses shifting and masking less painful */ - numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); + numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); startblock = 0; from = 0;
@@ -115,7 +116,13 @@ } } i += 2; - from += (1 << bbm->bbt_erase_shift); + if (FLEXONENAND(this)) { + onenand_get_block(mtd, from, &slc); + from += (1 << bbm->bbt_erase_shift) >> 1; + if (!slc) + from += (1 << bbm->bbt_erase_shift) >> 1; + } else + from += (1 << bbm->bbt_erase_shift); }
return 0; @@ -152,7 +159,7 @@ uint8_t res;
/* Get block number * 2 */ - block = (int)(offs >> (bbm->bbt_erase_shift - 1)); + block = (int) (onenand_get_block(mtd, offs, NULL) << 1); res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
MTDDEBUG (MTD_DEBUG_LEVEL2, @@ -191,7 +198,7 @@ struct bbm_info *bbm = this->bbm; int len, ret = 0;
- len = mtd->size >> (this->erase_shift + 2); + len = this->chipsize >> (this->erase_shift + 2); /* Allocate memory (2bit per block) */ bbm->bbt = malloc(len); if (!bbm->bbt) { diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c --- a/drivers/mtd/onenand/onenand_uboot.c +++ b/drivers/mtd/onenand/onenand_uboot.c @@ -31,6 +31,8 @@
onenand_scan(&onenand_mtd, 1);
+ if (onenand_chip.device_id & DEVICE_IS_FLEXONENAND) + puts("Flex-"); puts("OneNAND: "); - print_size(onenand_mtd.size, "\n"); + print_size(onenand_chip.chipsize, "\n"); } diff --git a/include/configs/apollon.h b/include/configs/apollon.h --- a/include/configs/apollon.h +++ b/include/configs/apollon.h @@ -73,6 +73,7 @@ * Size of malloc() pool */ #define CONFIG_ENV_SIZE SZ_128K /* Total Size of Environment Sector */ +#define CONFIG_ENV_SIZE_FLEX SZ_256K /* Can change to 512K */ #define CFG_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K) #define CFG_GBL_DATA_SIZE 128 /* bytes reserved for initial data */
@@ -227,5 +228,5 @@ #define CFG_ONENAND_BASE 0x00000000 #define CONFIG_ENV_IS_IN_ONENAND 1 #define CONFIG_ENV_ADDR 0x00020000 - +#define CONFIG_ENV_ADDR_FLEX 0x00040000 #endif /* __CONFIG_H */ diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -20,6 +20,7 @@ #include <linux/mtd/compat.h> #include <linux/mtd/bbm.h>
+#define MAX_DIES 2 #define MAX_BUFFERRAM 2 #define MAX_ONENAND_PAGESIZE (2048 + 64)
@@ -43,6 +44,9 @@ /** * struct onenand_chip - OneNAND Private Flash Chip Data * @param base [BOARDSPECIFIC] address to access OneNAND + * @dies: [INTERN][FLEXONENAND] number of dies on chip + * @boundary: [INTERN][FLEXONENAND] Boundary of the dies + * @diesize: [INTERN][FLEXONENAND] Size of the dies * @param chipsize [INTERN] the size of one chip for multichip arrays * @param device_id [INTERN] device ID * @param verstion_id [INTERN] version ID @@ -67,8 +71,13 @@ */ struct onenand_chip { void __iomem *base; + unsigned int dies; + unsigned int boundary[MAX_DIES]; + unsigned int diesize[MAX_DIES]; unsigned int chipsize; unsigned int device_id; + unsigned int technology; + unsigned int density_mask; unsigned int options;
unsigned int erase_shift; @@ -116,6 +125,8 @@ #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
+#define FLEXONENAND(this) (this->device_id & DEVICE_IS_FLEXONENAND) +#define ONENAND_IS_MLC(this) (this->technology & ONENAND_TECHNOLOGY_IS_MLC) #define ONENAND_IS_DDP(this) \ (this->device_id & ONENAND_DEVICE_IS_DDP)
@@ -127,6 +138,7 @@ #define ONENAND_CONT_LOCK (0x0001) #define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_OOBBUF_ALLOC (0x2000) +#define ONENAND_UNLOCK_ALL (0x0002)
/* * OneNAND Flash Manufacturer ID Codes @@ -147,4 +159,8 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
+unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr, + unsigned *isblkslc); +int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die, + int bdry, int lock); #endif /* __LINUX_MTD_ONENAND_H */ diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -67,6 +67,10 @@ /* * Device ID Register F001h (R) */ +#define DEVICE_IS_FLEXONENAND (1 << 9) +#define FLEXONENAND_PI_MASK (0x3ff) +#define FLEXONENAND_PI_UNLOCK_SHIFT (14) +#define ONENAND_DEVICE_DENSITY_MASK (0xf) #define ONENAND_DEVICE_DENSITY_SHIFT (4) #define ONENAND_DEVICE_IS_DDP (1 << 3) #define ONENAND_DEVICE_IS_DEMUX (1 << 2) @@ -80,6 +84,11 @@ #define ONENAND_VERSION_PROCESS_SHIFT (8)
/* + * Technology Register F006h (R) + */ +#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0) + +/* * Start Address 1 F100h (R/W) */ #define ONENAND_DDP_SHIFT (15) @@ -89,7 +98,7 @@ /* * Start Address 8 F107h (R/W) */ -#define ONENAND_FPA_MASK (0x3f) +#define ONENAND_FPA_MASK (0x7f) #define ONENAND_FPA_SHIFT (2) #define ONENAND_FSA_MASK (0x03)
@@ -101,7 +110,7 @@ #define ONENAND_BSA_BOOTRAM (0 << 2) #define ONENAND_BSA_DATARAM0 (2 << 2) #define ONENAND_BSA_DATARAM1 (3 << 2) -#define ONENAND_BSC_MASK (0x03) +#define ONENAND_BSC_MASK (0x07)
/* * Command Register F220h (R/W) @@ -113,9 +122,14 @@ #define ONENAND_CMD_UNLOCK (0x23) #define ONENAND_CMD_LOCK (0x2A) #define ONENAND_CMD_LOCK_TIGHT (0x2C) +#define ONENAND_CMD_UNLOCK_ALL (0x27) #define ONENAND_CMD_ERASE (0x94) #define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_READID (0x90) +#define FLEXONENAND_CMD_RESET (0xF3) +#define FLEXONENAND_CMD_PI_UPDATE (0x05) +#define FLEXONENAND_CMD_PI_ACCESS (0x66) +#define FLEXONENAND_CMD_RECOVER_LSB (0x05)
/* NOTE: Those are not *REAL* commands */ #define ONENAND_CMD_BUFFERRAM (0x1978) @@ -179,5 +193,6 @@ #define ONENAND_ECC_1BIT (1 << 0) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) +#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010)
#endif /* __ONENAND_REG_H */

Dear Scott,
In message 001101c91c7c$7b800640$3dd66c6b@sisodomain.com apgmoorthy wrote:
This patch adds support for Samsung Flex-OneNAND devices.
Flex-OneNAND combines SLC and MLC technologies into a single device. SLC area provides increased reliability and speed, suitable for storing code and data, such as bootloader, kernel and root file system. MLC area provides high density and is best used for storing user data. Users can configure the size of SLC and MLC regions through 'onenand setboundary' command.
Signed-off-by: Rohit Hagargundgi h.rohit@samsung.com
Do you intend to apply this to the NAND repo?
Best regards,
Wolfgang Denk

On Tue, Oct 14, 2008 at 01:30:03PM +0200, Wolfgang Denk wrote:
Dear Scott,
In message 001101c91c7c$7b800640$3dd66c6b@sisodomain.com apgmoorthy wrote:
This patch adds support for Samsung Flex-OneNAND devices.
Flex-OneNAND combines SLC and MLC technologies into a single device. SLC area provides increased reliability and speed, suitable for storing code and data, such as bootloader, kernel and root file system. MLC area provides high density and is best used for storing user data. Users can configure the size of SLC and MLC regions through 'onenand setboundary' command.
Signed-off-by: Rohit Hagargundgi h.rohit@samsung.com
Do you intend to apply this to the NAND repo?
I'll take a look at it.
-Scott

On Mon, Sep 22, 2008 at 11:58:51AM +0530, apgmoorthy wrote:
Hi All, This patch adds support for Samsung Flex-OneNAND devices.
Flex-OneNAND combines SLC and MLC technologies into a single device. SLC area provides increased reliability and speed, suitable for storing code and data, such as bootloader, kernel and root file system. MLC area provides high density and is best used for storing user data. Users can configure the size of SLC and MLC regions through 'onenand setboundary' command.
Signed-off-by: Rohit Hagargundgi h.rohit@samsung.com
Sorry for the late reply...
extern struct mtd_info onenand_mtd; extern struct onenand_chip onenand_chip; +loff_t flexonenand_get_addr(int block)
Space before function declarations.
- for (block = start; block <= end; block++) {
if (FLEXONENAND(this))
instr.addr = flexonenand_get_addr(block);
else
instr.addr = block << onenand_chip.erase_shift;
if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) {
for (i = 0; i < mtd->numeraseregions &&
mtd->eraseregions[i].offset <= instr.addr;
i++)
Patch is line-wrapped.
Can some of this be abstracted through the driver interface, rather than putting a bunch of stuff into what should be a relatively straightforward command-line wrapper?
Perhaps the two regions should be exposed as separate devices.
-Scott

Hi Scott, Thanks for the comments.
Scott Wood wrote:
On Mon, Sep 22, 2008 at 11:58:51AM +0530, apgmoorthy wrote:
Hi All, This patch adds support for Samsung Flex-OneNAND devices.
Flex-OneNAND combines SLC and MLC technologies into a single device. SLC area provides increased reliability and speed, suitable for storing code and data, such as bootloader, kernel and root file system. MLC area provides high density and is best used for storing user data. Users can configure the size of SLC and MLC regions through 'onenand setboundary' command.
Signed-off-by: Rohit Hagargundgi h.rohit@samsung.com
Sorry for the late reply...
extern struct mtd_info onenand_mtd; extern struct onenand_chip onenand_chip; +loff_t flexonenand_get_addr(int block)
Space before function declarations.
Ok.
- for (block = start; block <= end; block++) {
if (FLEXONENAND(this))
instr.addr = flexonenand_get_addr(block);
else
instr.addr = block << onenand_chip.erase_shift;
if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) {
for (i = 0; i < mtd->numeraseregions &&
mtd->eraseregions[i].offset <= instr.addr;
i++)
Patch is line-wrapped.
Sorry. Fixed it now.
Can some of this be abstracted through the driver interface, rather than putting a bunch of stuff into what should be a relatively straightforward command-line wrapper?
Ok. Now it is simplified.
Perhaps the two regions should be exposed as separate devices.
On DDP Flex-OneNAND, regions can be 1, 2, 3 or 4 based on boundary setting. Exposing as separate devices will be bit complex in these scenarios.
Also, comments from MTD mailing list have been included.
Thanks, Rohit
Signed-off-by: Rohit Hagargundgi h.rohit@samsung.com --- diff --git a/common/cmd_onenand.c b/common/cmd_onenand.c index 8d87b78..59f047a 100644 --- a/common/cmd_onenand.c +++ b/common/cmd_onenand.c @@ -21,8 +21,71 @@ extern struct mtd_info onenand_mtd; extern struct onenand_chip onenand_chip;
+static loff_t flexonenand_get_addr(int block) +{ + struct mtd_info *mtd = &onenand_mtd; + struct onenand_chip *this = mtd->priv; + loff_t ofs; + int die = 0, boundary; + + ofs = 0; + if (this->dies == 2 && block >= this->density_mask) { + block -= this->density_mask; + die = 1; + ofs = this->diesize[0]; + } + boundary = this->boundary[die]; + ofs += block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs += (block - boundary - 1) << (this->erase_shift - 1); + return ofs; +} + +static inline loff_t onenand_get_addr(int block) +{ + struct mtd_info *mtd = &onenand_mtd; + struct onenand_chip *this = mtd->priv; + + if (!FLEXONENAND(this)) + return block << onenand_chip.erase_shift; + return flexonenand_get_addr(block); +} + +static int do_erase(ulong start, ulong end) +{ + struct mtd_info *mtd = &onenand_mtd; + struct erase_info instr = { + .callback = NULL, + }; + int i, ret; + ulong block; + + printf("Erase block from %lu to %lu\n", start, end); + + for (block = start; block <= end; block++) { + instr.addr = onenand_get_addr(block); + if (mtd->numeraseregions > 1) { + i = flexonenand_region(mtd, instr.addr); + instr.len = mtd->eraseregions[i].erasesize; + } else + instr.len = mtd->erasesize; + + if (mtd->block_isbad(mtd, instr.addr)) { + printf("Skipping bad block %lu\n", block); + continue; + } + + ret = mtd->erase(&onenand_mtd, &instr); + if (ret) + printf("erase failed %lu\n", block); + } + return 0; +} + int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) { + struct mtd_info *mtd = &onenand_mtd; + struct onenand_chip *this = mtd->priv; int ret = 0;
switch (argc) { @@ -42,11 +105,7 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) default: /* At least 4 args */ if (strncmp(argv[1], "erase", 5) == 0) { - struct erase_info instr = { - .callback = NULL, - }; ulong start, end; - ulong block; char *endtail;
if (strncmp(argv[2], "block", 5) == 0) { @@ -57,28 +116,18 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) start = simple_strtoul(argv[2], NULL, 10); end = simple_strtoul(argv[3], NULL, 10);
- start >>= onenand_chip.erase_shift; - end >>= onenand_chip.erase_shift; + start = onenand_get_block(&onenand_mtd, + start, NULL); + end = onenand_get_block(&onenand_mtd, + end, NULL); /* Don't include the end block */ - end--; + if (end > 0) + end--; }
if (!end || end < 0) end = start; - - printf("Erase block from %lu to %lu\n", start, end); - - for (block = start; block <= end; block++) { - instr.addr = block << onenand_chip.erase_shift; - instr.len = 1 << onenand_chip.erase_shift; - ret = onenand_erase(&onenand_mtd, &instr); - if (ret) { - printf("erase failed %lu\n", block); - break; - } - } - - return 0; + return do_erase(start, end); }
if (strncmp(argv[1], "read", 4) == 0) { @@ -134,15 +183,15 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) ops.mode = MTD_OOB_PLACE;
- ofs = block << onenand_chip.erase_shift; + ofs = onenand_get_addr(block); if (page) ofs += page << onenand_chip.page_shift;
if (!len) { if (oob) - ops.ooblen = 64; + ops.ooblen = FLEXONENAND(this) ? 128 : 64; else - ops.len = 512; + ops.len = FLEXONENAND(this) ? 4096 : 512; }
if (oob) { @@ -158,6 +207,39 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) return 0; }
+ if (strncmp(argv[1], "setboundary", 11) == 0) { + unsigned die = simple_strtoul(argv[2], NULL, 0); + unsigned bdry = simple_strtoul(argv[3], NULL, 0); + int lock = 0, old; + + if (!FLEXONENAND(this)) { + printf("Flex-OneNAND not found.\n"); + return -1; + } + + if (argc == 5 && strncmp(argv[4], "LOCK", 4) == 0) + lock = 1; + + if (die >= this->dies) { + printf("Invalid die index\n"); + return -1; + } + + if (!(bdry % 2)) { + printf("Attempt to set odd number of SLC blocks.\n"); + bdry += 1; + printf("Setting boundary to %d\n", bdry); + } + + old = this->boundary[die] + (die * this->density_mask); + ret = flexonenand_set_boundary(mtd, die, bdry, lock); + if (!ret) { + int new = this->boundary[die] + + (die * this->density_mask); + do_erase(min(old, new) + 1, max(old, new)); + } + return 0; + } break; }
@@ -172,5 +254,7 @@ U_BOOT_CMD( "onenand write addr ofs len - write data at ofs with len from addr\n" "onenand erase saddr eaddr - erase block start addr to end addr\n" "onenand block[.oob] addr block [page] [len] - " - "read data with (block [, page]) to addr" + "read data with (block [, page]) to addr\n" + "onenand setboundary DIE BOUNDARY [LOCK] - " + "Change SLC boundary of Flex-OneNAND" ); diff --git a/common/env_onenand.c b/common/env_onenand.c index 3c65b3e..b7d0bbf 100644 --- a/common/env_onenand.c +++ b/common/env_onenand.c @@ -58,11 +58,14 @@ uchar env_get_char_spec(int index)
void env_relocate_spec(void) { + struct onenand_chip *this = &onenand_chip; unsigned long env_addr; int use_default = 0; size_t retlen;
env_addr = CONFIG_ENV_ADDR; + if (FLEXONENAND(this)) + env_addr <<= 1;
/* Check OneNAND exist */ if (onenand_mtd.writesize) @@ -89,6 +92,7 @@ void env_relocate_spec(void)
int saveenv(void) { + struct onenand_chip *this = &onenand_chip; unsigned long env_addr = CONFIG_ENV_ADDR; struct erase_info instr = { .callback = NULL, @@ -96,6 +100,12 @@ int saveenv(void) size_t retlen;
instr.len = CONFIG_ENV_SIZE; + if (FLEXONENAND(this)) { + env_addr <<= 1; + instr.len = CONFIG_ENV_SIZE_FLEX; + instr.len <<= onenand_mtd.eraseregions[0].numblocks == 1 ? + 1 : 0; + } instr.addr = env_addr; if (onenand_erase(&onenand_mtd, &instr)) { printf("OneNAND: erase failed at 0x%08lx\n", env_addr); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 460e9c7..35d182e 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -45,6 +45,14 @@ static const unsigned char ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ };
/** @@ -83,9 +91,11 @@ static int onenand_block_address(int device, int block) if (device & ONENAND_DEVICE_IS_DDP) { /* Device Flash Core select, NAND Flash Block Address */ int dfs = 0, density, mask; + int flex = device & DEVICE_IS_FLEXONENAND;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - mask = (1 << (density + 6)); + density &= ONENAND_DEVICE_DENSITY_MASK; + mask = (1 << (density + (flex ? 4 : 6)));
if (block & mask) dfs = 1; @@ -109,9 +119,11 @@ static int onenand_bufferram_address(int device, int block) if (device & ONENAND_DEVICE_IS_DDP) { /* Device BufferRAM Select */ int dbs = 0, density, mask; + int flex = device & DEVICE_IS_FLEXONENAND;
density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - mask = (1 << (density + 6)); + density &= ONENAND_DEVICE_DENSITY_MASK; + mask = (1 << (density + (flex ? 4 : 6)));
if (block & mask) dbs = 1; @@ -169,6 +181,68 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) }
/** + * flexonenand_get_block- For given address return block number and if slc + * @param mtd - MTD device structure + * @param addr - Address for which block number is needed + * @return isblkslc - Block is an SLC block or not + */ +static unsigned flexonenand_get_block(struct mtd_info *mtd, loff_t addr, + unsigned *isblkslc) +{ + struct onenand_chip *this = mtd->priv; + unsigned boundary, blk, die = 0; + + if (unlikely(this->chipsize == 0)) + /* We have been called by flexonenand_get_boundary. + * addr contains die index in this case. + */ + return addr * this->density_mask; + + if (addr >= this->diesize[0]) { + die = 1; + addr -= this->diesize[0]; + } + + boundary = this->boundary[die]; + + blk = addr >> (this->erase_shift - 1); + if (blk > boundary) + blk = (blk + boundary + 1) >> 1; + + if (isblkslc) + *isblkslc = (blk <= boundary) ? 1 : 0; + + blk += die ? this->density_mask : 0; + return blk; +} + +inline unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr, + unsigned *isblkslc) +{ + struct onenand_chip *this = mtd->priv; + + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + return flexonenand_get_block(mtd, addr, isblkslc); +} + +/** + * flexonenand_region - Get erase region of addr + * @param mtd MTD device structure + * @param addr address whose erase region needs to be identified + */ +inline int flexonenand_region(struct mtd_info *mtd, loff_t addr) +{ + int i; + + for (i = 0; i < mtd->numeraseregions && + addr >= mtd->eraseregions[i].offset; i++) + ; + i--; + return i; +} + +/** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd MTD device structure * @param cmd the command to be sent @@ -182,10 +256,12 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - int value, readcmd = 0; + int value; int block, page; + unsigned slc = 0; + /* Now we use page size operation */ - int sectors = 4, count = 4; + int sectors = 0, count = 0;
/* Address translation */ switch (cmd) { @@ -196,16 +272,19 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, page = -1; break;
+ case FLEXONENAND_CMD_PI_ACCESS: case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: - block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, NULL); page = -1; break;
default: - block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, &slc); page = (int)(addr >> this->page_shift); page &= this->page_mask; + if (slc) + page &= (this->page_mask >> 1); break; }
@@ -216,8 +295,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_MLC(this)) + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this);
return 0; } @@ -227,6 +309,10 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, value = onenand_block_address(this->device_id, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS2); }
if (page != -1) { @@ -235,8 +321,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, switch (cmd) { case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - dataram = ONENAND_SET_NEXT_BUFFERRAM(this); - readcmd = 1; + if (ONENAND_IS_MLC(this)) + dataram = ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); break;
default: @@ -253,14 +342,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, value = onenand_buffer_address(dataram, sectors, count); this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
- if (readcmd) { - /* Select DataRAM for DDP */ - value = - onenand_bufferram_address(this->device_id, block); - this->write_word(value, - this->base + - ONENAND_REG_START_ADDRESS2); - } }
/* Interrupt clear */ @@ -272,6 +353,29 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, }
/** + * onenand_get_ecc - return ecc status + * @param mtd MTD device structure + */ +static inline int onenand_get_ecc(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int ecc[4]; + int i, result = 0; + + for (i = 0; i < 4; i++) { + ecc[i] = this->read_word(this->base + ((0xFF00 + i) << 1)); + if (!FLEXONENAND(this)) + return ecc[i]; + if (ecc[i] & FLEXONENAND_UNCORRECTABLE_ERROR) { + result = ONENAND_ECC_2BIT_ALL; + break; + } + } + + return result; +} + +/** * onenand_wait - [DEFAULT] wait until the command is done * @param mtd MTD device structure * @param state state to select the max. timeout value @@ -295,6 +399,15 @@ static int onenand_wait(struct mtd_info *mtd, int state)
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+ if (interrupt & ONENAND_INT_READ) { + ecc = onenand_get_ecc(mtd); + if (ecc & ONENAND_ECC_2BIT_ALL) { + MTDDEBUG(MTD_DEBUG_LEVEL0, + "onenand_wait: ECC error = 0x%04x\n", ecc); + return -EBADMSG; + } + } + if (ctrl & ONENAND_CTRL_ERROR) { MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); @@ -307,15 +420,6 @@ static int onenand_wait(struct mtd_info *mtd, int state) return -EIO; }
- if (interrupt & ONENAND_INT_READ) { - ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_wait: ECC error = 0x%04x\n", ecc); - return -EBADMSG; - } - } - return 0; }
@@ -433,10 +537,13 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) struct onenand_chip *this = mtd->priv; int block, page; int i; + unsigned slc = 0;
- block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, &slc); page = (int)(addr >> this->page_shift); page &= this->page_mask; + if (slc) + page &= (this->page_mask >> 1);
i = ONENAND_CURRENT_BUFFERRAM(this);
@@ -462,10 +569,13 @@ static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, struct onenand_chip *this = mtd->priv; int block, page; int i; + unsigned slc = 0;
- block = (int)(addr >> this->erase_shift); + block = onenand_get_block(mtd, addr, &slc); page = (int)(addr >> this->page_shift); page &= this->page_mask; + if (FLEXONENAND(this) && slc) + page &= (this->page_mask >> 1);
/* Invalidate BufferRAM */ for (i = 0; i < MAX_BUFFERRAM; i++) { @@ -573,6 +683,43 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, }
/** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param mtd MTD device structure + * @param addr address to recover + * @param status return value from onenand_wait + * + * Issue recovery command when read fails on MLC area. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) +{ + struct onenand_chip *this = mtd->priv; + unsigned slc = 0; + + /* Recovery is only for Flex-OneNAND */ + if (!FLEXONENAND(this)) + return status; + + /* check if we failed due to uncorrectable error */ + if (status != (-EBADMSG) && status != (ONENAND_BBT_READ_ECC_ERROR)) + return status; + + /* check if address lies in MLC region */ + onenand_get_block(mtd, addr, &slc); + if (slc) + return status; + + /* We are attempting to reread, so decrement stats.failed + * which was incremented by onenand_wait due to read failure + */ + printk(KERN_DEBUG "Attempting to recover from uncorrectable read\n"); + mtd->ecc_stats.failed--; + + /* Issue the LSB page recovery command */ + this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); + return this->wait(mtd, FL_READING); +} + +/** * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure * @param from offset to read from @@ -616,12 +763,14 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats;
/* Read-while-load method */ + /* Note: We can't use this feature in MLC */
/* Do first load to bufferRAM */ if (read < len) { if (!onenand_check_bufferram(mtd, from)) { this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); + ret = ret ? onenand_recover_lsb(mtd, from, ret) : ret; onenand_update_bufferram(mtd, from, !ret); if (ret == -EBADMSG) ret = 0; @@ -636,7 +785,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, while (!ret) { /* If there is more to load then start next load */ from += thislen; - if (read + thislen < len) { + if (!ONENAND_IS_MLC(this) && read + thislen < len) { this->command(mtd, ONENAND_CMD_READ, from, writesize); /* * Chip boundary handling in DDP @@ -669,6 +818,15 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, oobcolumn = 0; }
+ if (ONENAND_IS_MLC(this) && (read + thislen < len)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + ret = this->wait(mtd, FL_READING); + ret = ret ? onenand_recover_lsb(mtd, from, ret) : ret; + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } + /* See if we are done */ read += thislen; if (read == len) @@ -676,16 +834,19 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, /* Set up for next read from bufferRAM */ if (unlikely(boundary)) this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); - ONENAND_SET_NEXT_BUFFERRAM(this); + if (!ONENAND_IS_MLC(this)) + ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, writesize, len - read); column = 0;
- /* Now wait for load */ - ret = this->wait(mtd, FL_READING); - onenand_update_bufferram(mtd, from, !ret); - if (ret == -EBADMSG) - ret = 0; + if (!ONENAND_IS_MLC(this)) { + /* Now wait for load */ + ret = this->wait(mtd, FL_READING); + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } }
/* @@ -722,7 +883,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; u_char *buf = ops->oobbuf; - int ret = 0; + int ret = 0, readcmd;
from += ops->ooboffs;
@@ -755,15 +916,18 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
stats = mtd->ecc_stats;
+ readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + while (read < len) { thislen = oobsize - column; thislen = min_t(int, thislen, len);
- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
ret = this->wait(mtd, FL_READING); + ret = ret ? onenand_recover_lsb(mtd, from, ret) : ret; if (ret && ret != -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); break; @@ -886,22 +1050,26 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
- /* Initial bad block case: 0x2400 or 0x0400 */ - if (ctrl & ONENAND_CTRL_ERROR) { - printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); - return ONENAND_BBT_READ_ERROR; - } - if (interrupt & ONENAND_INT_READ) { - int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); - if (ecc & ONENAND_ECC_2BIT_ALL) + int ecc = onenand_get_ecc(mtd); + if (ecc & ONENAND_ECC_2BIT_ALL) { + printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" + ", controller = 0x%04x\n", ecc, ctrl); return ONENAND_BBT_READ_ERROR; + } } else { printk(KERN_ERR "onenand_bbt_wait: read timeout!" "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); return ONENAND_BBT_READ_FATAL_ERROR; }
+ /* Initial bad block case: 0x2400 or 0x0400 */ + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "onenand_bbt_wait: controller error" + " = 0x%04x\n", ctrl); + return ONENAND_BBT_READ_ERROR; + } + return 0; }
@@ -918,7 +1086,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; - int ret = 0; + int ret = 0, readcmd; size_t len = ops->ooblen; u_char *buf = ops->oobbuf;
@@ -926,6 +1094,8 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
+ readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + /* Initialize return value */ ops->oobretlen = 0;
@@ -945,7 +1115,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, thislen = mtd->oobsize - column; thislen = min_t(int, thislen, len);
- this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize);
onenand_update_bufferram(mtd, from, 0);
@@ -987,9 +1157,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to { struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; - int status, i; + int status, i, readcmd; + + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
- this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); + this->command(mtd, readcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_READING); if (status) @@ -1236,7 +1408,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; - int written = 0; + int written = 0, oobcmd; u_char *oobbuf; size_t len = ops->ooblen; const u_char *buf = ops->oobbuf; @@ -1280,6 +1452,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
oobbuf = this->oob_buf;
+ oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; + /* Loop until all data write */ while (written < len) { int thislen = min_t(int, oobsize, len - written); @@ -1295,7 +1469,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
- this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + if (ONENAND_IS_MLC(this)) { + /* Set main area of DataRAM to 0xff*/ + memset(this->page_buf, 0xff, mtd->writesize); + this->write_bufferram(mtd, ONENAND_DATARAM, + this->page_buf, 0, mtd->writesize); + } + + this->command(mtd, oobcmd, to, mtd->oobsize);
onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { @@ -1424,19 +1605,27 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) unsigned int block_size; loff_t addr; int len; - int ret = 0; + int ret = 0, i = 0;
MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int)instr->addr, (unsigned int)ins tr->len);
- block_size = (1 << this->erase_shift); + if (mtd->numeraseregions > 1) { + i = flexonenand_region(mtd, instr->addr); + block_size = mtd->eraseregions[i].erasesize; + } else + block_size = mtd->erasesize;
/* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { - MTDDEBUG (MTD_DEBUG_LEVEL0, - "onenand_erase: Unaligned address\n"); - return -EINVAL; + /* We come here if boundary is even value. Just skip for now.*/ + if (!FLEXONENAND(this) || + (instr->addr & ((block_size >> 1) - 1))) { + MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:" + " Unaligned address\n"); + return -EINVAL; + } }
/* Length must align on block boundary */ @@ -1466,7 +1655,13 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
while (len) {
- /* TODO Check badblock */ + /* Check if we have a bad block, we do not erase bad blocks */ + if (onenand_block_isbad_nolock(mtd, addr, 0)) { + printk(KERN_WARNING "onenand_erase: attempt to erase" + "bad block at addr 0x%08x\n", (unsigned int) addr); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + }
this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
@@ -1481,7 +1676,7 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) else MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: " "Failed erase, block %d\n", - (unsigned)(addr >> this->erase_shift)); + onenand_get_block(mtd, addr, NULL)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -1489,6 +1684,12 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
len -= block_size; addr += block_size; + if (mtd->numeraseregions > 1) { + if ((i < (mtd->numeraseregions - 1)) && + (addr == mtd->eraseregions[i + 1].offset)) + i++; + block_size = mtd->eraseregions[i].erasesize; + } }
instr->state = MTD_ERASE_DONE; @@ -1539,7 +1740,7 @@ int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) return -EINVAL;
onenand_get_device(mtd, FL_READING); - ret = onenand_block_isbad_nolock(mtd,ofs, 0); + ret = onenand_block_isbad_nolock(mtd, ofs, 0); onenand_release_device(mtd); return ret; } @@ -1612,8 +1813,8 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) struct onenand_chip *this = mtd->priv; int start, end, block, value, status;
- start = ofs >> this->erase_shift; - end = len >> this->erase_shift; + start = onenand_get_block(mtd, ofs, NULL); + end = onenand_get_block(mtd, ofs + len - 1, NULL);
/* Continuous lock scheme */ if (this->options & ONENAND_CONT_LOCK) { @@ -1621,7 +1822,7 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ - this->write_word(end - 1, + this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0); @@ -1643,7 +1844,17 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) }
/* Block lock scheme */ - for (block = start; block < end; block++) { + for (block = start; block < end + 1; block++) { + /* Set block address */ + value = onenand_block_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS1); + + /* Select DataRAM for DDP */ + value = onenand_bufferram_address(this->device_id, block); + this->write_word(value, + this->base + ONENAND_REG_START_ADDRESS2); + /* Set start block address */ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS); @@ -1681,15 +1892,18 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) */ char * onenand_print_device_info(int device) { - int vcc, demuxed, ddp, density; + int vcc, demuxed, ddp, density, flexonenand; char *dev_info = malloc(80);
vcc = device & ONENAND_DEVICE_VCC_MASK; demuxed = device & ONENAND_DEVICE_IS_DEMUX; ddp = device & ONENAND_DEVICE_IS_DDP; density = device >> ONENAND_DEVICE_DENSITY_SHIFT; - sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)", - demuxed ? "" : "Muxed ", + density &= ONENAND_DEVICE_DENSITY_MASK; + flexonenand = device & DEVICE_IS_FLEXONENAND; + sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)", + demuxed ? "" : "Muxed ", + flexonenand ? "Flex-" : "", ddp ? "(DDP)" : "", (16 << density), vcc ? "2.65/3.3" : "1.8", device);
@@ -1725,6 +1939,174 @@ static int onenand_check_maf(int manuf) }
/** +* flexonenand_get_boundary - Reads the SLC boundary +* @param onenand_info onenand info structure +**/ +static int flexonenand_get_boundary(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned die, bdry; + int ret, syscfg, unlocked; + + /* Disable ECC */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); + + for (die = 0; die < this->dies; die++) { + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, ONENAND_CMD_READ, die, 0); + ret = this->wait(mtd, FL_READING); + + bdry = this->read_word(this->base + ONENAND_DATARAM); + unlocked = bdry >> FLEXONENAND_PI_UNLOCK_SHIFT; + unlocked = (unlocked == 0x3) ? 1 : 0; + this->boundary[die] = bdry & FLEXONENAND_PI_MASK; + + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + ret = this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], + unlocked ? "(Unlocked)" : "(Locked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * get_flexonenand_size - Fill up fields in onenand_chip + * boundary[], diesize[], chipsize + * @param mtd - MTD device structure + */ +static void get_flexonenand_size(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int die, ofs, i, eraseshift; + unsigned blksperdie = 1024; + unsigned maxbdry = blksperdie - 1; + + eraseshift = this->erase_shift - 1; + + this->chipsize = 0; + mtd->numeraseregions = this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die = ofs = 0; + i = -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = this->boundary[die] + 1; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -= 1; + mtd->eraseregions[i].numblocks += this->boundary[die] + 1; + ofs += (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = + maxbdry ^ this->boundary[die]; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -= 1; + } + + mtd->erasesize = 1 << (this->erase_shift); + if (mtd->numeraseregions == 1) + mtd->erasesize >>= 1; + + printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); + for (i = 0; i < mtd->numeraseregions; i++) + printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," + " numblocks: %04u]\n", mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + + eraseshift = this->erase_shift; + for (die = 0, mtd->size = 0; die < this->dies; die++) { + this->diesize[die] = (blksperdie << eraseshift); + this->diesize[die] -= (this->boundary[die] + 1) + << (eraseshift - 1); + mtd->size += this->diesize[die]; + } + /* this->chipsize represents maximum possible chip size */ + this->chipsize = (blksperdie << eraseshift) << (this->dies - 1); +} + +/** +* flexonenand_set_boundary - Writes the SLC boundary +* @param onenand_info onenand info structure +**/ +int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die, + int boundary, int lock) +{ + struct onenand_chip *this = mtd->priv; + int ret, density, blksperdie; + unsigned addr; + + density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density &= ONENAND_DEVICE_DENSITY_MASK; + + blksperdie = ((16 << density) << 20) >> this->erase_shift; + blksperdie >>= (this->device_id & ONENAND_DEVICE_IS_DDP) ? 1 : 0; + + addr = die ? this->diesize[0] : 0; + + if (this->boundary[die] == boundary) + return -1; + + printk(KERN_INFO "Changing boundary: %d%s\n", boundary, lock ? + "(Locked)" : "(Unlocked)"); + if (boundary >= blksperdie) { + printk(KERN_ERR "Invalid boundary value.\n"); + return -1; + } + + boundary &= FLEXONENAND_PI_MASK; + boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); + + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, addr, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, ONENAND_CMD_ERASE, addr, 0); + this->wait(mtd, FL_ERASING); + + this->write_word(boundary, this->base + ONENAND_DATARAM); + this->command(mtd, ONENAND_CMD_PROG, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "Failed PI write for Die %d\n", die); + goto out; + } + + this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) + printk(KERN_ERR "Failed PI update for Die %d\n", die); + else + printk(KERN_INFO "Done\n"); +out: + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); + this->wait(mtd, FL_RESETING); + if (!ret) + /* Recalculate device size on boundary change*/ + get_flexonenand_size(mtd); + return ret; +} + +/** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd MTD device structure * @@ -1758,31 +2140,48 @@ static int onenand_probe(struct mtd_info *mtd) /* Read manufacturer and device IDs from Register */ maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
/* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) return -ENXIO;
- /* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */ - if (dev_id & (1 << 9)) { - printk("Not yet support Flex-OneNAND\n"); - return -ENXIO; - } - /* Flash device information */ mtd->name = onenand_print_device_info(dev_id); this->device_id = dev_id;
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; - this->chipsize = (16 << density) << 20; + density &= ONENAND_DEVICE_DENSITY_MASK; + if (FLEXONENAND(this)) { + this->dies = (dev_id & ONENAND_DEVICE_IS_DDP) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions = this->dies << 1; + mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1)); + if (!mtd->eraseregions) + return -ENOMEM; + } + this->chipsize = FLEXONENAND(this) ? 0 : (16 << density) << 20; + /* Set density mask. it is used for DDP */ + if (dev_id & ONENAND_DEVICE_IS_DDP) + this->density_mask = (1 << (density + (FLEXONENAND(this) ? + 4 : 6))); + else + this->density_mask = 0;
/* OneNAND page size & block size */ /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + /* We use the full BufferRAM */ + if (ONENAND_IS_MLC(this)) + mtd->writesize <<= 1; + mtd->oobsize = mtd->writesize >> 5; /* Pagers per block is always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; + /* Flex-OneNAND always has 128 pages per block */ + mtd->erasesize <<= FLEXONENAND(this) ? 1 : 0;
this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; @@ -1793,7 +2192,10 @@ static int onenand_probe(struct mtd_info *mtd)
/* REVIST: Multichip handling */
- mtd->size = this->chipsize; + if (FLEXONENAND(this)) + get_flexonenand_size(mtd); + else + mtd->size = this->chipsize;
/* Version ID */ version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); @@ -1818,6 +2220,9 @@ static int onenand_probe(struct mtd_info *mtd) mtd->block_isbad = onenand_block_isbad; mtd->block_markbad = onenand_block_markbad;
+ if (FLEXONENAND(this)) + this->options &= ~ONENAND_CONT_LOCK; + return 0; }
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index f6092b9..e46fd29 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -66,6 +66,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf, struct bbm_info *bbm = this->bbm; int i, j, numblocks, len, scanlen; int startblock; + unsigned slc; loff_t from; size_t readlen, ooblen; struct mtd_oob_ops ops; @@ -82,7 +83,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf, /* Note that numblocks is 2 * (real numblocks) here; * see i += 2 below as it makses shifting and masking less painful */ - numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); + numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); startblock = 0; from = 0;
@@ -115,7 +116,13 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf, } } i += 2; - from += (1 << bbm->bbt_erase_shift); + if (FLEXONENAND(this)) { + onenand_get_block(mtd, from, &slc); + from += (1 << bbm->bbt_erase_shift) >> 1; + if (!slc) + from += (1 << bbm->bbt_erase_shift) >> 1; + } else + from += (1 << bbm->bbt_erase_shift); }
return 0; @@ -152,7 +159,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) uint8_t res;
/* Get block number * 2 */ - block = (int)(offs >> (bbm->bbt_erase_shift - 1)); + block = (int) (onenand_get_block(mtd, offs, NULL) << 1); res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
MTDDEBUG (MTD_DEBUG_LEVEL2, @@ -191,7 +198,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) struct bbm_info *bbm = this->bbm; int len, ret = 0;
- len = mtd->size >> (this->erase_shift + 2); + len = this->chipsize >> (this->erase_shift + 2); /* Allocate memory (2bit per block) */ bbm->bbt = malloc(len); if (!bbm->bbt) { diff --git a/drivers/mtd/onenand/onenand_uboot.c b/drivers/mtd/onenand/onenand_uboot.c index 08082f3..419db34 100644 --- a/drivers/mtd/onenand/onenand_uboot.c +++ b/drivers/mtd/onenand/onenand_uboot.c @@ -31,6 +31,8 @@ void onenand_init(void)
onenand_scan(&onenand_mtd, 1);
+ if (onenand_chip.device_id & DEVICE_IS_FLEXONENAND) + puts("Flex-"); puts("OneNAND: "); - print_size(onenand_mtd.size, "\n"); + print_size(onenand_chip.chipsize, "\n"); } diff --git a/include/configs/apollon.h b/include/configs/apollon.h index d71ed44..bad86d1 100644 --- a/include/configs/apollon.h +++ b/include/configs/apollon.h @@ -73,6 +73,7 @@ * Size of malloc() pool */ #define CONFIG_ENV_SIZE SZ_128K /* Total Size of Environment Sector */ +#define CONFIG_ENV_SIZE_FLEX SZ_256K /* Can change to 512K */ #define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + SZ_128K) #define CONFIG_SYS_GBL_DATA_SIZE 128 /* bytes reserved for initial data */
@@ -227,5 +228,4 @@ #define CONFIG_SYS_ONENAND_BASE 0x00000000 #define CONFIG_ENV_IS_IN_ONENAND 1 #define CONFIG_ENV_ADDR 0x00020000 - #endif /* __CONFIG_H */ diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 420eb14..6d367ea 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -20,6 +20,7 @@ #include <linux/mtd/compat.h> #include <linux/mtd/bbm.h>
+#define MAX_DIES 2 #define MAX_BUFFERRAM 2 #define MAX_ONENAND_PAGESIZE (2048 + 64)
@@ -43,6 +44,9 @@ struct onenand_bufferram { /** * struct onenand_chip - OneNAND Private Flash Chip Data * @param base [BOARDSPECIFIC] address to access OneNAND + * @dies: [INTERN][FLEXONENAND] number of dies on chip + * @boundary: [INTERN][FLEXONENAND] Boundary of the dies + * @diesize: [INTERN][FLEXONENAND] Size of the dies * @param chipsize [INTERN] the size of one chip for multichip arrays * @param device_id [INTERN] device ID * @param verstion_id [INTERN] version ID @@ -67,8 +71,13 @@ struct onenand_bufferram { */ struct onenand_chip { void __iomem *base; + unsigned int dies; + unsigned int boundary[MAX_DIES]; + unsigned int diesize[MAX_DIES]; unsigned int chipsize; unsigned int device_id; + unsigned int technology; + unsigned int density_mask; unsigned int options;
unsigned int erase_shift; @@ -116,6 +125,8 @@ struct onenand_chip { #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
+#define FLEXONENAND(this) (this->device_id & DEVICE_IS_FLEXONENAND) +#define ONENAND_IS_MLC(this) (this->technology & ONENAND_TECHNOLOGY_IS_MLC) #define ONENAND_IS_DDP(this) \ (this->device_id & ONENAND_DEVICE_IS_DDP)
@@ -147,4 +158,9 @@ struct onenand_manufacturers { int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);
+unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr, + unsigned *isblkslc); +int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die, + int bdry, int lock); +int flexonenand_region(struct mtd_info *mtd, loff_t addr); #endif /* __LINUX_MTD_ONENAND_H */ diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 6a8aa28..0108ad9 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -67,6 +67,10 @@ /* * Device ID Register F001h (R) */ +#define DEVICE_IS_FLEXONENAND (1 << 9) +#define FLEXONENAND_PI_MASK (0x3ff) +#define FLEXONENAND_PI_UNLOCK_SHIFT (14) +#define ONENAND_DEVICE_DENSITY_MASK (0xf) #define ONENAND_DEVICE_DENSITY_SHIFT (4) #define ONENAND_DEVICE_IS_DDP (1 << 3) #define ONENAND_DEVICE_IS_DEMUX (1 << 2) @@ -80,6 +84,11 @@ #define ONENAND_VERSION_PROCESS_SHIFT (8)
/* + * Technology Register F006h (R) + */ +#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0) + +/* * Start Address 1 F100h (R/W) */ #define ONENAND_DDP_SHIFT (15) @@ -89,7 +98,7 @@ /* * Start Address 8 F107h (R/W) */ -#define ONENAND_FPA_MASK (0x3f) +#define ONENAND_FPA_MASK (0x7f) #define ONENAND_FPA_SHIFT (2) #define ONENAND_FSA_MASK (0x03)
@@ -101,7 +110,7 @@ #define ONENAND_BSA_BOOTRAM (0 << 2) #define ONENAND_BSA_DATARAM0 (2 << 2) #define ONENAND_BSA_DATARAM1 (3 << 2) -#define ONENAND_BSC_MASK (0x03) +#define ONENAND_BSC_MASK (0x07)
/* * Command Register F220h (R/W) @@ -116,6 +125,10 @@ #define ONENAND_CMD_ERASE (0x94) #define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_READID (0x90) +#define FLEXONENAND_CMD_RESET (0xF3) +#define FLEXONENAND_CMD_PI_UPDATE (0x05) +#define FLEXONENAND_CMD_PI_ACCESS (0x66) +#define FLEXONENAND_CMD_RECOVER_LSB (0x05)
/* NOTE: Those are not *REAL* commands */ #define ONENAND_CMD_BUFFERRAM (0x1978) @@ -179,5 +192,6 @@ #define ONENAND_ECC_1BIT (1 << 0) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) +#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010)
#endif /* __ONENAND_REG_H */

On Fri, Oct 24, 2008 at 07:20:12PM +0530, Rohit wrote:
On DDP Flex-OneNAND, regions can be 1, 2, 3 or 4 based on boundary setting. Exposing as separate devices will be bit complex in these scenarios.
Why? If it's dynamic changes to the boundary that concern you, you could tear down the device(s) and re-initialize.
Of course, I may simply be missing something about how this hardware works.
Also, comments from MTD mailing list have been included.
If this is going to go into Linux, perhaps we should wait until it is merged there and then import the result?
At least, I'd like Kyungmin Park's ack for OneNAND stuff.
-Scott

Why? If it's dynamic changes to the boundary that concern you, you could tear down the device(s) and re-initialize.
Okay consider that add reset or reprobe command to do this
Of course, I may simply be missing something about how this hardware works.
Also, comments from MTD mailing list have been included.
If this is going to go into Linux, perhaps we should wait until it is merged there and then import the result?
It's almost done and wait to merge into MTD tree. As I mentioned. First merge MTD tree and then U-Boot tree.
At least, I'd like Kyungmin Park's ack for OneNAND stuff.
Basically I acked this patch.
Thank you, Kyungmin Park

On Tue, Oct 28, 2008 at 01:53:24PM +0900, Kyungmin Park wrote:
Of course, I may simply be missing something about how this hardware works.
Also, comments from MTD mailing list have been included.
If this is going to go into Linux, perhaps we should wait until it is merged there and then import the result?
It's almost done and wait to merge into MTD tree. As I mentioned. First merge MTD tree and then U-Boot tree.
At least, I'd like Kyungmin Park's ack for OneNAND stuff.
Basically I acked this patch.
OK, I missed it. I assume someone will resubmit once it's been merged in Linux?
-Scott

Hi,
Kyungmin Park wrote:
Why? If it's dynamic changes to the boundary that concern you, you could tear down the device(s) and re-initialize.
Okay consider that add reset or reprobe command to do this
We can expose Flex-OneNAND as a SLC device and a MLC device by having two mtd_info structures. The difference between the two devices is block size only.
1. This makes user aware of SLC and MLC areas. 2. We expose two devices where as there is a single physical device. Also, MTD provides erase regions capability for such devices ie which have many erase regions.
Exposing as two devices reduces code by removing code for erase regions, but only little. Also other support for Flex-OneNAND is still needed ie - all 4KB dataram is used during read/write. - read/write oob-only commands are not present. - LSB recovery for MLC area read failure - 4 ecc registers
Of course, I may simply be missing something about how this hardware works.
Also, comments from MTD mailing list have been included.
If this is going to go into Linux, perhaps we should wait until it is merged there and then import the result?
It's almost done and wait to merge into MTD tree. As I mentioned. First merge MTD tree and then U-Boot tree.
At least, I'd like Kyungmin Park's ack for OneNAND stuff.
Basically I acked this patch.
I'll repost after MTD merge.
Thanks, Rohit

Hi
We can provide two device registration for SLC and MLC are but I don't know how useful it is because FlexOneNand provides boundary settings and user can configure SLC and MLC area and other point is still device registration is separate how user will get any gain as it will be on same die.
Regards Amit
----- Original Message ----- From: "Scott Wood" scottwood@freescale.com To: "apgmoorthy" moorthy.apg@samsung.com Cc: u-boot@lists.denx.de Sent: Thursday, October 23, 2008 4:39 AM Subject: Re: [U-Boot] [PATCH] Flex-OneNAND driver
On Mon, Sep 22, 2008 at 11:58:51AM +0530, apgmoorthy wrote:
Hi All, This patch adds support for Samsung Flex-OneNAND devices.
Flex-OneNAND combines SLC and MLC technologies into a single device. SLC area provides increased reliability and speed, suitable for storing code and data, such as bootloader, kernel and root file system. MLC area provides high density and is best used for storing user data. Users can configure the size of SLC and MLC regions through 'onenand setboundary' command.
Signed-off-by: Rohit Hagargundgi h.rohit@samsung.com
Sorry for the late reply...
extern struct mtd_info onenand_mtd; extern struct onenand_chip onenand_chip; +loff_t flexonenand_get_addr(int block)
Space before function declarations.
- for (block = start; block <= end; block++) {
- if (FLEXONENAND(this))
- instr.addr = flexonenand_get_addr(block);
- else
- instr.addr = block << onenand_chip.erase_shift;
- if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) {
- for (i = 0; i < mtd->numeraseregions &&
- mtd->eraseregions[i].offset <= instr.addr;
i++)
Patch is line-wrapped.
Can some of this be abstracted through the driver interface, rather than putting a bunch of stuff into what should be a relatively straightforward command-line wrapper?
Perhaps the two regions should be exposed as separate devices.
-Scott _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot

On Thu, Nov 06, 2008 at 08:59:06AM +0530, Amit Kumar Sharma wrote:
We can provide two device registration for SLC and MLC are but I don't know how useful it is because FlexOneNand provides boundary settings and user can configure SLC and MLC area and other point is still device registration is separate how user will get any gain as it will be on same die.
The intent was to avoid having the command-line wrapper handle implementation details such as boundary calculations. I don't really care whether it's one device or two, but the front-end user of the flash shouldn't have to know such details.
-Scott
participants (7)
-
Amit Kumar Sharma
-
apgmoorthy
-
Kyungmin Park
-
Rohit
-
Scott Wood
-
Scott Wood
-
Wolfgang Denk