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 <[EMAIL PROTECTED]> > > 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 <[EMAIL PROTECTED]> --- 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 */ _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot