From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailout2.samsung.com ([203.254.224.25]) by bombadil.infradead.org with esmtp (Exim 4.68 #1 (Red Hat Linux)) id 1Kwwgm-0007Ne-32 for linux-mtd@lists.infradead.org; Mon, 03 Nov 2008 10:29:42 +0000 Received: from epmmp1 (mailout2.samsung.com [203.254.224.25]) by mailout2.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTP id <0K9R00IL06HBGI@mailout2.samsung.com> for linux-mtd@lists.infradead.org; Mon, 03 Nov 2008 19:29:35 +0900 (KST) Received: from apgmoorthy ([107.108.214.61]) by mmp1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0K9R002F16H8P3@mmp1.samsung.com> for linux-mtd@lists.infradead.org; Mon, 03 Nov 2008 19:29:35 +0900 (KST) Date: Mon, 03 Nov 2008 16:02:33 +0530 From: apgmoorthy Subject: RE: [ANNOUNCE] [PATCH] [MTD] Flex-OneNAND MTD Driver available. In-reply-to: <4901DB27.4020401@nokia.com> To: 'Adrian Hunter' Message-id: <000901c93d9f$7c7ed0f0$3dd66c6b@sisodomain.com> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-transfer-encoding: quoted-printable References: <19198934.52871221827459790.JavaMail.weblogic@epml16> <9c9fda240809220011w49c0875q804f836550ff3476@mail.gmail.com> <000001c91e42$1aa86c50$3dd66c6b@sisodomain.com> <9c9fda240809251731y48272a63j988e0001dc50d78@mail.gmail.com> <1222404711.5012.5.camel@sauron> <1222417176.5012.17.camel@sauron> <000b01c92215$ae62d4e0$3dd66c6b@sisodomain.com> <9c9fda240810091557j529495d5q284a8ab0e0d752dd@mail.gmail.com> <1224331498.6770.1362.camel@macbook.infradead.org> <000001c93271$5c6ab4c0$3dd66c6b@sisodomain.com> <1224482040.4466.73.camel@sauron> <000301c93411$74627df0$3dd66c6b@sisodomain.com> <4901DB27.4020401@nokia.com> Cc: 'David Woodhouse' , 'Kyungmin Park' , linux-mtd@lists.infradead.org List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , =20 Hi Adrian, Appologize for the Late reply , was on a vacation. Thanks for your valid comments. On Friday, October 24, 2008 7:57 PM Adrian Wrote : >> - LSB recovery for MLC area read failure >> - 4 ecc registers >> - different OTP lock position >So if FLEXONENAND(this) is true, does ONENAND_IS_MLC(this) also have to = be true? =20 -Correct. >Did you consider making MLC and Flex-OneNAND support optional via a = config option? -Most of the functionalities are same for OneNAND and Flex-OneNAND and = not much overhead for exsisting OneNAND functioning , So it seems okay to skip config = option.It can be added if needed. > There are a couple more comments below, but in general it seems like = you could > still try to have fewer FLEXONENAND(this) and ONENAND_IS_MLC(this). >=20 - Comments are absorbed. Please do find the updated patch. drivers/mtd/onenand/onenand_base.c | 684 = +++++++++++++++++++++++++++++++++---- drivers/mtd/onenand/onenand_bbt.c | 15=20 drivers/mtd/onenand/onenand_sim.c | 105 +++++ include/linux/mtd/onenand.h | 33 + include/linux/mtd/onenand_regs.h | 19 - 5 files changed, 781 insertions(+), 75 deletions(-) --- diff --git a/drivers/mtd/onenand/onenand_base.c = b/drivers/mtd/onenand/onenand_base.c index 90ed319..b9d40a5 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -9,6 +9,10 @@ * auto-placement support, read-while load support, various fixes * Copyright (C) Nokia Corporation, 2007 * + * Vishak G , Rohit Hagargundgi + * Flex-OneNAND support + * Copyright (C) Samsung Electronics, 2008 + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -28,6 +32,27 @@ #include =20 /** + * onenand_oob_128 - oob info for Flex-Onenand with 4KB page + * For now, we expose only 64 out of 80 ecc bytes + */ +static struct nand_ecclayout onenand_oob_128 =3D { + .eccbytes =3D 64, + .eccpos =3D { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 102, 103, 104, 105 + }, + .oobfree =3D { + {2, 4}, {18, 4}, {34, 4}, {50, 4}, + {66, 4}, {82, 4}, {98, 4}, {114, 4} + } +}; + +/** * onenand_oob_64 - oob info for large (2KB) page */ static struct nand_ecclayout onenand_oob_64 =3D { @@ -65,6 +90,14 @@ static const unsigned char ffchars[] =3D { 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 */ }; =20 /** @@ -171,6 +204,49 @@ static int onenand_buffer_address(int dataram1, int = sectors, int count) } =20 /** + * flexonenand_get_block- For given address return block number and if = slc + * @param this - OneNAND 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 onenand_chip *this, loff_t = addr, + unsigned *isblkslc) +{ + unsigned boundary, blk, die =3D 0; + + if (unlikely(this->chipsize =3D=3D 0)) + /* We have been called by flexonenand_get_boundary. + * addr contains die index in this case. + */ + return addr * this->density_mask; + + if (addr >=3D this->diesize[0]) { + die =3D 1; + addr -=3D this->diesize[0]; + } + + boundary =3D this->boundary[die]; + + blk =3D addr >> (this->erase_shift - 1); + if (blk > boundary) + blk =3D (blk + boundary + 1) >> 1; + + if (isblkslc) + *isblkslc =3D (blk <=3D boundary) ? 1 : 0; + + blk +=3D die ? this->density_mask : 0; + return blk; +} + +inline unsigned onenand_get_block(struct onenand_chip *this, loff_t = addr, + unsigned *isblkslc) +{ + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + return flexonenand_get_block(this, addr, isblkslc); +} + +/** * onenand_get_density - [DEFAULT] Get OneNAND density * @param dev_id OneNAND device ID * @@ -196,6 +272,7 @@ static int onenand_command(struct mtd_info *mtd, int = cmd, loff_t addr, size_t le { struct onenand_chip *this =3D mtd->priv; int value, block, page; + unsigned slc =3D 0; =20 /* Address translation */ switch (cmd) { @@ -207,15 +284,16 @@ static int onenand_command(struct mtd_info *mtd, = int cmd, loff_t addr, size_t le page =3D -1; break; =20 + case FLEXONENAND_CMD_PI_ACCESS: case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_OTP_ACCESS: - block =3D (int) (addr >> this->erase_shift); + block =3D onenand_get_block(this, addr, NULL); page =3D -1; break; =20 default: - block =3D (int) (addr >> this->erase_shift); + block =3D onenand_get_block(this, addr, &slc); page =3D (int) (addr >> this->page_shift); =20 if (ONENAND_IS_2PLANE(this)) { @@ -227,6 +305,8 @@ static int onenand_command(struct mtd_info *mtd, int = cmd, loff_t addr, size_t le page >>=3D 1; } page &=3D this->page_mask; + if (slc) + page &=3D (this->page_mask >> 1); break; } =20 @@ -236,7 +316,7 @@ static int onenand_command(struct mtd_info *mtd, int = cmd, loff_t addr, size_t le value =3D onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); =20 - if (ONENAND_IS_2PLANE(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this)) /* It is always BufferRAM0 */ ONENAND_SET_BUFFERRAM0(this); else @@ -258,13 +338,18 @@ static int onenand_command(struct mtd_info *mtd, = int cmd, loff_t addr, size_t le =20 if (page !=3D -1) { /* Now we use page size operation */ - int sectors =3D 4, count =3D 4; + int sectors =3D 0, count =3D 0; int dataram; =20 switch (cmd) { + case FLEXONENAND_CMD_RECOVER_LSB: case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - dataram =3D ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_MLC(this)) + /* It is always BufferRAM0 */ + dataram =3D ONENAND_SET_BUFFERRAM0(this); + else + dataram =3D ONENAND_SET_NEXT_BUFFERRAM(this); break; =20 default: @@ -293,6 +378,30 @@ static int onenand_command(struct mtd_info *mtd, = int cmd, loff_t addr, size_t le } =20 /** + * onenand_read_ecc - return ecc status + * @param this onenand chip structure + */ +static inline int onenand_read_ecc(struct onenand_chip *this) +{ + int ecc, i, result =3D 0; + + if (!FLEXONENAND(this)) + return this->read_word(this->base + ONENAND_REG_ECC_STATUS); + + for (i =3D 0; i < 4; i++) { + ecc =3D this->read_word(this->base + ONENAND_REG_ECC_STATUS + i); + if(likely(!ecc)) + continue; + if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) + return ONENAND_ECC_2BIT_ALL; + else + result =3D ONENAND_ECC_1BIT_ALL; + } + + 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 @@ -331,14 +440,14 @@ static int onenand_wait(struct mtd_info *mtd, int = state) * power off recovery (POR) test, it should read ECC status first */ if (interrupt & ONENAND_INT_READ) { - int ecc =3D this->read_word(this->base + ONENAND_REG_ECC_STATUS); + int ecc =3D onenand_read_ecc(this); if (ecc) { if (ecc & ONENAND_ECC_2BIT_ALL) { printk(KERN_ERR "onenand_wait: ECC error =3D 0x%04x\n", ecc); mtd->ecc_stats.failed++; return -EBADMSG; } else if (ecc & ONENAND_ECC_1BIT_ALL) { - printk(KERN_INFO "onenand_wait: correctable ECC error =3D = 0x%04x\n", ecc); + printk(KERN_DEBUG "onenand_wait: correctable ECC error =3D = 0x%04x\n", ecc); mtd->ecc_stats.corrected++; } } @@ -656,7 +765,7 @@ static int onenand_check_bufferram(struct mtd_info = *mtd, loff_t addr) =20 if (found && ONENAND_IS_DDP(this)) { /* Select DataRAM for DDP */ - int block =3D (int) (addr >> this->erase_shift); + int block =3D onenand_get_block(this, addr, NULL); int value =3D onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); } @@ -816,6 +925,148 @@ static int onenand_transfer_auto_oob(struct = mtd_info *mtd, uint8_t *buf, int col } =20 /** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param this onenand chip device structure + * @param addr address to recover + * @param status return value from onenand_wait / onenand_bbt_wait + * + * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB = page has + * lower page address and MSB page has higher page address in paired = pages. + * If power off occurs during MSB page program, the paired LSB page = data can + * become corrupt. LSB page recovery read is a way to read LSB page = though page + * data are corrupted. When uncorrectable error occurs as a result of = LSB page + * read after power up, issue LSB page recovery read. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int = status) +{ + struct onenand_chip *this =3D mtd->priv; + unsigned slc =3D 0; + + /* Recovery is only for Flex-OneNAND */ + if (!FLEXONENAND(this)) + return status; + + /* check if we failed due to uncorrectable error */ + if (status !=3D (-EBADMSG) && status !=3D = (ONENAND_BBT_READ_ECC_ERROR)) + return status; + + /* check if address lies in MLC region */ + onenand_get_block(this, 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_INFO "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_mlc_read_ops_nolock - MLC OneNAND read main and/or = out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops: oob operation description structure + * + * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram. + * So, read-while-load is not present. + */ +static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t = from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this =3D mtd->priv; + struct mtd_ecc_stats stats; + size_t len =3D ops->len; + size_t ooblen =3D ops->ooblen; + u_char *buf =3D ops->datbuf; + u_char *oobbuf =3D ops->oobbuf; + int read =3D 0, column, thislen; + int oobread =3D 0, oobcolumn, thisooblen, oobsize; + int ret =3D 0; + int writesize =3D this->writesize; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from =3D 0x%08x, = len =3D %i\n", (unsigned int) from, (int) len); + + if (ops->mode =3D=3D MTD_OOB_AUTO) + oobsize =3D this->ecclayout->oobavail; + else + oobsize =3D mtd->oobsize; + + oobcolumn =3D from & (mtd->oobsize - 1); + + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end = of device\n"); + ops->retlen =3D 0; + ops->oobretlen =3D 0; + return -EINVAL; + } + + stats =3D mtd->ecc_stats; + + while (read < len) { + cond_resched(); + + thislen =3D min_t(int, writesize, len - read); + + column =3D from & (writesize - 1); + if (column + thislen > writesize) + thislen =3D writesize - column; + + if (!onenand_check_bufferram(mtd, from)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + + ret =3D this->wait(mtd, FL_READING); + ret =3D unlikely(ret) ? onenand_recover_lsb(mtd, from, ret) : ret; + onenand_update_bufferram(mtd, from, !ret); + if (ret =3D=3D -EBADMSG) + ret =3D 0; + } + + this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); + if (oobbuf) { + thisooblen =3D oobsize - oobcolumn; + thisooblen =3D min_t(int, thisooblen, ooblen - oobread); + + if (ops->mode =3D=3D MTD_OOB_AUTO) + onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, = thisooblen); + oobread +=3D thisooblen; + oobbuf +=3D thisooblen; + oobcolumn =3D 0; + } + + read +=3D thislen; + if (read =3D=3D len) + break; + + from +=3D thislen; + buf +=3D thislen; + } + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen =3D=3D desired len and result =3D=3D -EBADMSG + */ + ops->retlen =3D read; + ops->oobretlen =3D oobread; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} + +/** * 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 @@ -962,7 +1213,7 @@ static int onenand_read_oob_nolock(struct mtd_info = *mtd, loff_t from, size_t len =3D ops->ooblen; mtd_oob_mode_t mode =3D ops->mode; u_char *buf =3D ops->oobbuf; - int ret =3D 0; + int ret =3D 0, readcmd; =20 from +=3D ops->ooboffs; =20 @@ -993,17 +1244,21 @@ static int onenand_read_oob_nolock(struct = mtd_info *mtd, loff_t from, =20 stats =3D mtd->ecc_stats; =20 + readcmd =3D ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : = ONENAND_CMD_READOOB; + while (read < len) { cond_resched(); =20 thislen =3D oobsize - column; thislen =3D min_t(int, thislen, len); =20 - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); =20 onenand_update_bufferram(mtd, from, 0); =20 ret =3D this->wait(mtd, FL_READING); + ret =3D unlikely(ret) ? onenand_recover_lsb(mtd, from, ret) : ret; + if (ret && ret !=3D -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed =3D 0x%x\n", = ret); break; @@ -1053,6 +1308,7 @@ static int onenand_read_oob_nolock(struct mtd_info = *mtd, loff_t from, static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + struct onenand_chip *this =3D mtd->priv; struct mtd_oob_ops ops =3D { .len =3D len, .ooblen =3D 0, @@ -1062,7 +1318,9 @@ static int onenand_read(struct mtd_info *mtd, = loff_t from, size_t len, int ret; =20 onenand_get_device(mtd, FL_READING); - ret =3D onenand_read_ops_nolock(mtd, from, &ops); + ret =3D ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, &ops) : + onenand_read_ops_nolock(mtd, from, &ops); onenand_release_device(mtd); =20 *retlen =3D ops.retlen; @@ -1080,6 +1338,7 @@ static int onenand_read(struct mtd_info *mtd, = loff_t from, size_t len, static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + struct onenand_chip *this =3D mtd->priv; int ret; =20 switch (ops->mode) { @@ -1094,7 +1353,9 @@ static int onenand_read_oob(struct mtd_info *mtd, = loff_t from, =20 onenand_get_device(mtd, FL_READING); if (ops->datbuf) - ret =3D onenand_read_ops_nolock(mtd, from, ops); + ret =3D ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, ops) : + onenand_read_ops_nolock(mtd, from, ops); else ret =3D onenand_read_oob_nolock(mtd, from, ops); onenand_release_device(mtd); @@ -1128,11 +1389,11 @@ static int onenand_bbt_wait(struct mtd_info = *mtd, int state) ctrl =3D this->read_word(this->base + ONENAND_REG_CTRL_STATUS); =20 if (interrupt & ONENAND_INT_READ) { - int ecc =3D this->read_word(this->base + ONENAND_REG_ECC_STATUS); + int ecc =3D onenand_read_ecc(this); if (ecc & ONENAND_ECC_2BIT_ALL) { printk(KERN_INFO "onenand_bbt_wait: ecc error =3D 0x%04x" ", controller error 0x%04x\n", ecc, ctrl); - return ONENAND_BBT_READ_ERROR; + return ONENAND_BBT_READ_ECC_ERROR; } } else { printk(KERN_ERR "onenand_bbt_wait: read timeout!" @@ -1163,7 +1424,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, = loff_t from, { struct onenand_chip *this =3D mtd->priv; int read =3D 0, thislen, column; - int ret =3D 0; + int ret =3D 0, readcmd; size_t len =3D ops->ooblen; u_char *buf =3D ops->oobbuf; =20 @@ -1183,17 +1444,21 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, = loff_t from, =20 column =3D from & (mtd->oobsize - 1); =20 + readcmd =3D ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : = ONENAND_CMD_READOOB; + while (read < len) { cond_resched(); =20 thislen =3D mtd->oobsize - column; thislen =3D min_t(int, thislen, len); =20 - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); =20 onenand_update_bufferram(mtd, from, 0); =20 ret =3D onenand_bbt_wait(mtd, FL_READING); + ret =3D unlikely(ret) ? onenand_recover_lsb(mtd, from, ret) : ret; + if (ret) break; =20 @@ -1230,9 +1495,11 @@ static int onenand_verify_oob(struct mtd_info = *mtd, const u_char *buf, loff_t to { struct onenand_chip *this =3D mtd->priv; u_char *oob_buf =3D this->oob_buf; - int status, i; + int status, i, readcmd; + + readcmd =3D ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : = ONENAND_CMD_READOOB; =20 - this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); + this->command(mtd, readcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status =3D this->wait(mtd, FL_READING); if (status) @@ -1586,7 +1853,7 @@ static int onenand_write_oob_nolock(struct = mtd_info *mtd, loff_t to, { struct onenand_chip *this =3D mtd->priv; int column, ret =3D 0, oobsize; - int written =3D 0; + int written =3D 0, oobcmd; u_char *oobbuf; size_t len =3D ops->ooblen; const u_char *buf =3D ops->oobbuf; @@ -1628,6 +1895,8 @@ static int onenand_write_oob_nolock(struct = mtd_info *mtd, loff_t to, =20 oobbuf =3D this->oob_buf; =20 + oobcmd =3D ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : = ONENAND_CMD_PROGOOB; + /* Loop until all data write */ while (written < len) { int thislen =3D min_t(int, oobsize, len - written); @@ -1645,7 +1914,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); =20 - 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); =20 onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { @@ -1770,11 +2046,32 @@ static int onenand_erase(struct mtd_info *mtd, = struct erase_info *instr) unsigned int block_size; loff_t addr; int len; - int ret =3D 0; + int ret =3D 0, i =3D 0; =20 DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start =3D 0x%08x, len =3D = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); =20 - block_size =3D (1 << this->erase_shift); + /* Do not allow erase past end of device */ + if (unlikely((instr->len + instr->addr) > mtd->size)) { + printk(KERN_ERR "onenand_erase: Erase past end of device\n"); + return -EINVAL; + } + + if (mtd->numeraseregions > 1) { + /* Find the eraseregion of this address */ + for (; i < mtd->numeraseregions && + instr->addr >=3D mtd->eraseregions[i].offset; i++) + ; + i--; + block_size =3D mtd->eraseregions[i].erasesize; + + /* Start address should be aligned on erase region boundary */ + if (unlikely((instr->addr - mtd->eraseregions[i].offset) & + (block_size - 1))) { + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + return -EINVAL; + } + } else + block_size =3D mtd->erasesize; =20 /* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { @@ -1788,12 +2085,6 @@ static int onenand_erase(struct mtd_info *mtd, = struct erase_info *instr) return -EINVAL; } =20 - /* Do not allow erase past end of device */ - if (unlikely((instr->len + instr->addr) > mtd->size)) { - printk(KERN_ERR "onenand_erase: Erase past end of device\n"); - return -EINVAL; - } - instr->fail_addr =3D MTD_FAIL_ADDR_UNKNOWN; =20 /* Grab the lock and see if the device is available */ @@ -1822,7 +2113,8 @@ static int onenand_erase(struct mtd_info *mtd, = struct erase_info *instr) ret =3D this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { - printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", = (unsigned) (addr >> this->erase_shift)); + printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", + (unsigned)onenand_get_block(this, addr, NULL)); instr->state =3D MTD_ERASE_FAILED; instr->fail_addr =3D addr; goto erase_exit; @@ -1830,6 +2122,19 @@ static int onenand_erase(struct mtd_info *mtd, = struct erase_info *instr) =20 len -=3D block_size; addr +=3D block_size; + if (mtd->numeraseregions > 1) { + if ((i < (mtd->numeraseregions - 1)) && + (addr =3D=3D mtd->eraseregions[i + 1].offset)) + i++; + block_size =3D mtd->eraseregions[i].erasesize; + if (len & (block_size - 1)) { + /* This should be handled at MTD partitioning + * level. + */ + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + goto erase_exit; + } + } } =20 instr->state =3D MTD_ERASE_DONE; @@ -1908,13 +2213,17 @@ static int onenand_default_block_markbad(struct = mtd_info *mtd, loff_t ofs) int block; =20 /* Get block number */ - block =3D ((int) ofs) >> bbm->bbt_erase_shift; + block =3D onenand_get_block(this, ofs, NULL); if (bbm->bbt) bbm->bbt[block >> 2] |=3D 0x01 << ((block & 0x03) << = 1); =20 /* We write two bytes, so we dont have to mess with 16 bit = access */ ofs +=3D mtd->oobsize + (bbm->badblockpos & ~0x01); - return onenand_write_oob_nolock(mtd, ofs, &ops); + /* FIXME : What to do when marking SLC block in partition + * with MLC erasesize? For now, it is not advisable to + * create partitions containing both SLC and MLC regions. + */ + return onenand_write_oob_nolock(mtd, ofs, &ops); } =20 /** @@ -1958,8 +2267,8 @@ static int onenand_do_lock_cmd(struct mtd_info = *mtd, loff_t ofs, size_t len, int int start, end, block, value, status; int wp_status_mask; =20 - start =3D ofs >> this->erase_shift; - end =3D len >> this->erase_shift; + start =3D onenand_get_block(this, ofs, NULL); + end =3D onenand_get_block(this, ofs + len, NULL) - 1; =20 if (cmd =3D=3D ONENAND_CMD_LOCK) wp_status_mask =3D ONENAND_WP_LS; @@ -1971,7 +2280,7 @@ static int onenand_do_lock_cmd(struct mtd_info = *mtd, loff_t ofs, size_t len, int /* Set start block address */ this->write_word(start, this->base + = ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ - this->write_word(start + end - 1, this->base + = ONENAND_REG_END_BLOCK_ADDRESS); + this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write lock command */ this->command(mtd, cmd, 0, 0); =20 @@ -1992,7 +2301,7 @@ static int onenand_do_lock_cmd(struct mtd_info = *mtd, loff_t ofs, size_t len, int } =20 /* Block lock scheme */ - for (block =3D start; block < start + end; block++) { + for (block =3D start; block < end + 1; block++) { /* Set block address */ value =3D onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); @@ -2086,7 +2395,6 @@ static int onenand_check_lock_status(struct = onenand_chip *this) return 0; } } - return 1; } =20 @@ -2100,7 +2408,7 @@ static void onenand_unlock_all(struct mtd_info = *mtd) { struct onenand_chip *this =3D mtd->priv; loff_t ofs =3D 0; - size_t len =3D this->chipsize; + size_t len =3D mtd->size; =20 if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Set start block address */ @@ -2122,9 +2430,14 @@ static void onenand_unlock_all(struct mtd_info = *mtd) =20 /* Workaround for all block unlock in DDP */ if (ONENAND_IS_DDP(this)) { - /* All blocks on another chip */ - ofs =3D this->chipsize >> 1; - len =3D this->chipsize >> 1; + /* All blocks on another chip + * For Flex-OneNAND with both slc + * mlc regions, we use diesize + */ + ofs =3D FLEXONENAND(this) ? this->diesize[0] : + this->chipsize >> 1; + len =3D FLEXONENAND(this) ? this->diesize[1] : + this->chipsize >> 1; } } =20 @@ -2163,7 +2476,9 @@ static int do_otp_read(struct mtd_info *mtd, = loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); =20 - ret =3D onenand_read_ops_nolock(mtd, from, &ops); + ret =3D ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, &ops) : + onenand_read_ops_nolock(mtd, from, &ops); =20 /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2230,21 +2545,32 @@ static int do_otp_lock(struct mtd_info *mtd, = loff_t from, size_t len, size_t *retlen, u_char *buf) { struct onenand_chip *this =3D mtd->priv; - struct mtd_oob_ops ops =3D { - .mode =3D MTD_OOB_PLACE, - .ooblen =3D len, - .oobbuf =3D buf, - .ooboffs =3D 0, - }; + struct mtd_oob_ops ops; int ret; =20 /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); =20 - ret =3D onenand_write_oob_nolock(mtd, from, &ops); - - *retlen =3D ops.oobretlen; + if (FLEXONENAND(this)) { + /* + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. + */ + ops.len =3D mtd->writesize; + ops.ooblen =3D 0; + ops.datbuf =3D buf; + ops.oobbuf =3D NULL; + ret =3D onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); + *retlen =3D ops.retlen; + } else { + ops.mode =3D MTD_OOB_PLACE; + ops.ooblen =3D len; + ops.oobbuf =3D buf; + ops.ooboffs =3D 0; + ret =3D onenand_write_oob_nolock(mtd, from, &ops); + *retlen =3D ops.oobretlen; + } =20 /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2428,27 +2754,33 @@ static int onenand_lock_user_prot_reg(struct = mtd_info *mtd, loff_t from, size_t len) { struct onenand_chip *this =3D mtd->priv; - u_char *oob_buf =3D this->oob_buf; + u_char *buf =3D FLEXONENAND(this) ? this->page_buf : this->oob_buf; size_t retlen; int ret; =20 - memset(oob_buf, 0xff, mtd->oobsize); + memset(buf, 0xff, FLEXONENAND(this) ? this->writesize + : mtd->oobsize); /* * Note: OTP lock operation * OTP block : 0xXXFC * 1st block : 0xXXF3 (If chip support) * Both : 0xXXF0 (If chip support) */ - oob_buf[ONENAND_OTP_LOCK_OFFSET] =3D 0xFC; + FLEXONENAND(this) ? + buf[FLEXONENAND_OTP_LOCK_OFFSET] =3D 0xFC : + buf[ONENAND_OTP_LOCK_OFFSET] =3D 0xFC; =20 /* * Write lock mark to 8th word of sector0 of page0 of the spare0. * We write 16 bytes spare area instead of 2 bytes. + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. */ + from =3D 0; - len =3D 16; + len =3D FLEXONENAND(this) ? mtd->writesize : 16; =20 - ret =3D onenand_otp_walk(mtd, from, len, &retlen, oob_buf, = do_otp_lock, MTD_OTP_USER); + ret =3D onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, = MTD_OTP_USER); =20 return ret ? : retlen; } @@ -2495,6 +2827,14 @@ static void onenand_check_features(struct = mtd_info *mtd) break; } =20 + if (ONENAND_IS_MLC(this)) + this->options &=3D ~ONENAND_HAS_2PLANE; + + if (FLEXONENAND(this)) { + this->options &=3D ~ONENAND_HAS_CONT_LOCK; + this->options |=3D ONENAND_HAS_UNLOCK_ALL; + } + if (this->options & ONENAND_HAS_CONT_LOCK) printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); if (this->options & ONENAND_HAS_UNLOCK_ALL) @@ -2512,14 +2852,16 @@ static void onenand_check_features(struct = mtd_info *mtd) */ static void onenand_print_device_info(int device, int version) { - int vcc, demuxed, ddp, density; + int vcc, demuxed, ddp, density, flexonenand; =20 vcc =3D device & ONENAND_DEVICE_VCC_MASK; demuxed =3D device & ONENAND_DEVICE_IS_DEMUX; ddp =3D device & ONENAND_DEVICE_IS_DDP; density =3D onenand_get_density(device); - printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", - demuxed ? "" : "Muxed ", + flexonenand =3D device & DEVICE_IS_FLEXONENAND; + printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", + demuxed ? "" : "Muxed ", + flexonenand ? "Flex-" : "", ddp ? "(DDP)" : "", (16 << density), vcc ? "2.65/3.3" : "1.8", @@ -2558,6 +2900,181 @@ static int onenand_check_maf(int manuf) } =20 /** +* 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 =3D mtd->priv; + unsigned die, bdry; + int ret, syscfg, locked; + + /* Disable ECC */ + syscfg =3D this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + = ONENAND_REG_SYS_CFG1); + + for (die =3D 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 =3D this->wait(mtd, FL_READING); + + bdry =3D this->read_word(this->base + ONENAND_DATARAM); + locked =3D bdry >> FLEXONENAND_PI_UNLOCK_SHIFT; + locked =3D (locked =3D=3D 0x3) ? 0 : 1; + this->boundary[die] =3D bdry & FLEXONENAND_PI_MASK; + this->boundary_locked[die] =3D locked; + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + ret =3D this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * flexonenand_get_size - Fill up fields in onenand_chip + * boundary[], diesize[], chipsize, + * boundary_locked[] + * @param mtd - MTD device structure + */ +static void flexonenand_get_size(struct mtd_info *mtd) +{ + struct onenand_chip *this =3D mtd->priv; + int die, ofs, i, eraseshift, density; + int blksperdie, maxbdry; + + density =3D onenand_get_density(this->device_id); + blksperdie =3D ((16 << density) << 20) >> (this->erase_shift); + blksperdie >>=3D ONENAND_IS_DDP(this) ? 1 : 0; + maxbdry =3D blksperdie - 1; + eraseshift =3D this->erase_shift - 1; + + this->chipsize =3D 0; + mtd->numeraseregions =3D this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die =3D ofs =3D 0; + i =3D -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] !=3D maxbdry) { + i++; + mtd->eraseregions[i].offset =3D ofs; + mtd->eraseregions[i].erasesize =3D 1 << eraseshift; + mtd->eraseregions[i].numblocks =3D + this->boundary[die] + 1; + ofs +=3D mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -=3D 1; + mtd->eraseregions[i].numblocks +=3D + this->boundary[die] + 1; + ofs +=3D (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] !=3D maxbdry) { + i++; + mtd->eraseregions[i].offset =3D ofs; + mtd->eraseregions[i].erasesize =3D 1 << eraseshift; + mtd->eraseregions[i].numblocks =3D maxbdry ^ + this->boundary[die]; + ofs +=3D mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -=3D 1; + } + + mtd->erasesize =3D 1 << (this->erase_shift); + if (mtd->numeraseregions =3D=3D 1) + mtd->erasesize >>=3D 1; + + printk(KERN_INFO "Device has %d eraseregions\n", = mtd->numeraseregions); + for (i =3D 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); + + for (die =3D 0, mtd->size =3D 0; die < this->dies; die++) { + this->diesize[die] =3D (blksperdie << this->erase_shift); + this->diesize[die] -=3D (this->boundary[die] + 1) + << (this->erase_shift - 1); + mtd->size +=3D this->diesize[die]; + } + + /* this->chipsize represents maximum possible chip size */ + this->chipsize =3D (16 << density) << 20; +} + +/** + * flexonenand_set_boundary - Writes the SLC boundary + * @param onenand_info - onenand info structure + */ +static int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die, + int boundary, int lock) +{ + struct onenand_chip *this =3D mtd->priv; + int ret, density, blksperdie; + loff_t addr; + + density =3D onenand_get_density(this->device_id); + blksperdie =3D ((16 << density) << 20) >> this->erase_shift; + blksperdie >>=3D ONENAND_IS_DDP(this) ? 1 : 0; + + printk(KERN_INFO "Changing die %d boundary: %d%s\n", die, boundary, + lock ? "(Locked)" : "(Unlocked)"); + if (boundary >=3D blksperdie) { + printk(KERN_ERR "Invalid boundary value.\ + Boundary not changed.\n"); + return -1; + } + + if (this->boundary_locked[die]) { + printk(KERN_ERR "Die boundary is locked.\ + Boundary not changed.\n"); + return -1; + } + + addr =3D die ? this->diesize[0] : 0; + + boundary &=3D FLEXONENAND_PI_MASK; + boundary |=3D 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 =3D 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, die, 0); + ret =3D 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*/ + flexonenand_get_size(mtd); + return ret; +} + +/** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd MTD device structure * @@ -2599,6 +3116,7 @@ static int onenand_probe(struct mtd_info *mtd) maf_id =3D this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id =3D this->read_word(this->base + ONENAND_REG_DEVICE_ID); ver_id =3D this->read_word(this->base + ONENAND_REG_VERSION_ID); + this->technology =3D this->read_word(this->base + = ONENAND_REG_TECHNOLOGY); =20 /* Check OneNAND device */ if (maf_id !=3D bram_maf_id || dev_id !=3D bram_dev_id) @@ -2610,20 +3128,35 @@ static int onenand_probe(struct mtd_info *mtd) this->version_id =3D ver_id; =20 density =3D onenand_get_density(dev_id); - this->chipsize =3D (16 << density) << 20; + if (FLEXONENAND(this)) { + this->dies =3D ONENAND_IS_DDP(this) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions =3D this->dies << 1; + mtd->eraseregions =3D kzalloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1), GFP_KERNEL); + if (!mtd->eraseregions) + return -ENOMEM; + } + this->chipsize =3D FLEXONENAND(this) ? 0 : (16 << density) << 20; /* Set density mask. it is used for DDP */ if (ONENAND_IS_DDP(this)) - this->density_mask =3D (1 << (density + 6)); + this->density_mask =3D (1 << (density + + (FLEXONENAND(this) ? 4 : 6))); else this->density_mask =3D 0; =20 /* OneNAND page size & block size */ /* The data buffer size is equal to page size */ mtd->writesize =3D this->read_word(this->base + = ONENAND_REG_DATA_BUFFER_SIZE); + /* We use the full BufferRAM */ + if (ONENAND_IS_MLC(this)) + mtd->writesize <<=3D 1; + mtd->oobsize =3D mtd->writesize >> 5; /* Pages per a block are always 64 in OneNAND */ mtd->erasesize =3D mtd->writesize << 6; - + /* Flex-OneNAND always has 128 pages per block */ + mtd->erasesize <<=3D FLEXONENAND(this) ? 1 : 0; this->erase_shift =3D ffs(mtd->erasesize) - 1; this->page_shift =3D ffs(mtd->writesize) - 1; this->page_mask =3D (1 << (this->erase_shift - this->page_shift)) - 1; @@ -2632,7 +3165,24 @@ static int onenand_probe(struct mtd_info *mtd) =20 /* REVIST: Multichip handling */ =20 - mtd->size =3D this->chipsize; + if (FLEXONENAND(this)) { + int boundary[] =3D + {FLEXONENAND_DIE0_BOUNDARY, FLEXONENAND_DIE1_BOUNDARY}; + int lock[] =3D + {FLEXONENAND_DIE0_ISLOCKED, FLEXONENAND_DIE1_ISLOCKED}; + unsigned die; + + flexonenand_get_size(mtd); + + /* Change the device boundaries if required */ + for (die =3D 0; die < this->dies; die++) + if ((!this->boundary_locked[die]) && + (boundary[die] >=3D 0) && + (boundary[die] !=3D this->boundary[die])) + flexonenand_set_boundary(mtd, die, + boundary[die], lock[die]); + } else + mtd->size =3D this->chipsize; =20 /* Check OneNAND features */ onenand_check_features(mtd); @@ -2749,6 +3299,10 @@ int onenand_scan(struct mtd_info *mtd, int = maxchips) * Allow subpage writes up to oobsize. */ switch (mtd->oobsize) { + case 128: + this->ecclayout =3D &onenand_oob_128; + mtd->subpage_sft =3D 0; + break; case 64: this->ecclayout =3D &onenand_oob_64; mtd->subpage_sft =3D 2; @@ -2768,6 +3322,10 @@ int onenand_scan(struct mtd_info *mtd, int = maxchips) break; } =20 + /* Don't allow the sub-page write in MLC */ + if (ONENAND_IS_MLC(this)) + mtd->subpage_sft =3D 0; + this->subpagesize =3D mtd->writesize >> mtd->subpage_sft; =20 /* @@ -2843,6 +3401,8 @@ void onenand_release(struct mtd_info *mtd) kfree(this->page_buf); if (this->options & ONENAND_OOBBUF_ALLOC) kfree(this->oob_buf); + if (FLEXONENAND(this)) + kfree(mtd->eraseregions); } =20 EXPORT_SYMBOL_GPL(onenand_scan); diff --git a/drivers/mtd/onenand/onenand_bbt.c = b/drivers/mtd/onenand/onenand_bbt.c index 2f53b51..ef862dd 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -60,6 +60,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t = *buf, struct nand_bbt_descr struct bbm_info *bbm =3D this->bbm; int i, j, numblocks, len, scanlen; int startblock; + unsigned slc; loff_t from; size_t readlen, ooblen; struct mtd_oob_ops ops; @@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t = *buf, struct nand_bbt_descr /* Note that numblocks is 2 * (real numblocks) here; * see i +=3D 2 below as it makses shifting and masking less painful */ - numblocks =3D mtd->size >> (bbm->bbt_erase_shift - 1); + numblocks =3D this->chipsize >> (bbm->bbt_erase_shift - 1); startblock =3D 0; from =3D 0; =20 @@ -106,7 +107,13 @@ static int create_bbt(struct mtd_info *mtd, uint8_t = *buf, struct nand_bbt_descr } } i +=3D 2; - from +=3D (1 << bbm->bbt_erase_shift); + if (FLEXONENAND(this)) { + onenand_get_block(this, from, &slc); + from +=3D (1 << bbm->bbt_erase_shift) >> 1; + if (!slc) + from +=3D (1 << bbm->bbt_erase_shift) >> 1; + } else + from +=3D (1 << bbm->bbt_erase_shift); } =20 return 0; @@ -143,7 +150,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, = loff_t offs, int allowbbt) uint8_t res; =20 /* Get block number * 2 */ - block =3D (int) (offs >> (bbm->bbt_erase_shift - 1)); + block =3D (int) (onenand_get_block(this, offs, NULL) << 1); res =3D (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; =20 DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: = (block %d) 0x%02x\n", @@ -178,7 +185,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct = nand_bbt_descr *bd) struct bbm_info *bbm =3D this->bbm; int len, ret =3D 0; =20 - len =3D mtd->size >> (this->erase_shift + 2); + len =3D this->chipsize >> (this->erase_shift + 2); /* Allocate memory (2bit per block) and clear the memory bad block = table */ bbm->bbt =3D kzalloc(len, GFP_KERNEL); if (!bbm->bbt) { diff --git a/drivers/mtd/onenand/onenand_sim.c = b/drivers/mtd/onenand/onenand_sim.c index d64200b..c66946e 100644 --- a/drivers/mtd/onenand/onenand_sim.c +++ b/drivers/mtd/onenand/onenand_sim.c @@ -6,6 +6,10 @@ * Copyright =C2=A9 2005-2007 Samsung Electronics * Kyungmin Park * + * Vishak G , Rohit Hagargundgi + * Flex-OneNAND simulator support + * Copyright (C) Samsung Electronics, 2008 + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -24,16 +28,38 @@ #ifndef CONFIG_ONENAND_SIM_MANUFACTURER #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec #endif + #ifndef CONFIG_ONENAND_SIM_DEVICE_ID #define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 #endif + +#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1) + #ifndef CONFIG_ONENAND_SIM_VERSION_ID #define CONFIG_ONENAND_SIM_VERSION_ID 0x1e #endif =20 +#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID +#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND +#endif + +/* Initial boundary values for Flex-OneNAND Simulator */ +#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY +#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01 +#endif + +#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY +#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01 +#endif + static int manuf_id =3D CONFIG_ONENAND_SIM_MANUFACTURER; static int device_id =3D CONFIG_ONENAND_SIM_DEVICE_ID; static int version_id =3D CONFIG_ONENAND_SIM_VERSION_ID; +static int technology_id =3D CONFIG_ONENAND_SIM_TECHNOLOGY_ID; +static int boundary[] =3D { + CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY, + CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY, +}; =20 struct onenand_flash { void __iomem *base; @@ -57,12 +83,18 @@ struct onenand_flash { (writew(v, this->base + ONENAND_REG_WP_STATUS)) =20 /* It has all 0xff chars */ -#define MAX_ONENAND_PAGESIZE (2048 + 64) +#define MAX_ONENAND_PAGESIZE (4096 + 128) static unsigned char *ffchars; =20 +#if CONFIG_FLEXONENAND +#define PARTITION_NAME "Flex-OneNAND simulator partition" +#else +#define PARTITION_NAME "OneNAND simulator partition" +#endif + static struct mtd_partition os_partitions[] =3D { { - .name =3D "OneNAND simulator partition", + .name =3D PARTITION_NAME, .offset =3D 0, .size =3D MTDPART_SIZ_FULL, }, @@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip = *this, int cmd) =20 switch (cmd) { case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_UNLOCK_ALL: if (block_lock_scheme) ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); else @@ -228,10 +261,11 @@ static void onenand_data_handle(struct = onenand_chip *this, int cmd, { struct mtd_info *mtd =3D &info->mtd; struct onenand_flash *flash =3D this->priv; - int main_offset, spare_offset; + int main_offset, spare_offset, die =3D 0; void __iomem *src; void __iomem *dest; - unsigned int i; + unsigned int i, slc =3D 0; + static int pi_operation; =20 if (dataram) { main_offset =3D mtd->writesize; @@ -241,10 +275,27 @@ static void onenand_data_handle(struct = onenand_chip *this, int cmd, spare_offset =3D 0; } =20 + if (pi_operation) { + die =3D readw(this->base + ONENAND_REG_START_ADDRESS2); + die >>=3D ONENAND_DDP_SHIFT; + } + switch (cmd) { + case FLEXONENAND_CMD_PI_ACCESS: + pi_operation =3D 1; + break; + + case ONENAND_CMD_RESET: + pi_operation =3D 0; + break; + case ONENAND_CMD_READ: src =3D ONENAND_CORE(flash) + offset; dest =3D ONENAND_MAIN_AREA(this, main_offset); + if (pi_operation) { + writew(boundary[die], this->base + ONENAND_DATARAM); + break; + } memcpy(dest, src, mtd->writesize); /* Fall through */ =20 @@ -257,6 +308,10 @@ static void onenand_data_handle(struct onenand_chip = *this, int cmd, case ONENAND_CMD_PROG: src =3D ONENAND_MAIN_AREA(this, main_offset); dest =3D ONENAND_CORE(flash) + offset; + if (pi_operation) { + boundary[die] =3D readw(this->base + ONENAND_DATARAM); + break; + } /* To handle partial write */ for (i =3D 0; i < (1 << mtd->subpage_sft); i++) { int off =3D i * this->subpagesize; @@ -284,9 +339,16 @@ static void onenand_data_handle(struct onenand_chip = *this, int cmd, break; =20 case ONENAND_CMD_ERASE: + if (pi_operation) + break; + onenand_get_block(this, offset, &slc); + if (slc && (mtd->numeraseregions > 1)) + mtd->erasesize >>=3D 1; memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, (mtd->erasesize >> 5)); + if (slc && (mtd->numeraseregions > 1)) + mtd->erasesize <<=3D 1; break; =20 default: @@ -295,6 +357,29 @@ static void onenand_data_handle(struct onenand_chip = *this, int cmd, } =20 /** + * flexonenand_get_addr - Return address of the block + * @block: Block number on Flex-OneNAND + * + */ +loff_t flexonenand_get_addr(struct onenand_chip *this, int block) +{ + loff_t ofs; + int die =3D 0, boundary; + + ofs =3D 0; + if (ONENAND_IS_DDP(this) && block >=3D this->density_mask) { + block -=3D this->density_mask; + die =3D 1; + ofs =3D this->diesize[0]; + } + boundary =3D this->boundary[die]; + ofs +=3D block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs +=3D (block - boundary - 1) << (this->erase_shift - 1); + return ofs; +} + +/** * onenand_command_handle - Handle command * @this: OneNAND device structure * @cmd: The command to be sent @@ -338,8 +423,12 @@ static void onenand_command_handle(struct = onenand_chip *this, int cmd) break; } =20 - if (block !=3D -1) - offset +=3D block << this->erase_shift; + if (block !=3D -1) { + if (FLEXONENAND(this)) + offset =3D flexonenand_get_addr(this, block); + else + offset +=3D block << this->erase_shift; + } =20 if (page !=3D -1) offset +=3D page << this->page_shift; @@ -390,6 +479,7 @@ static int __init flash_init(struct onenand_flash = *flash) } =20 density =3D device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density &=3D ONENAND_DEVICE_DENSITY_MASK; size =3D ((16 << 20) << density); =20 ONENAND_CORE(flash) =3D vmalloc(size + (size >> 5)); @@ -405,8 +495,9 @@ static int __init flash_init(struct onenand_flash = *flash) writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); writew(version_id, flash->base + ONENAND_REG_VERSION_ID); + writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY); =20 - if (density < 2) + if (density < 2 && (!CONFIG_FLEXONENAND)) buffer_size =3D 0x0400; /* 1KiB page */ else buffer_size =3D 0x0800; /* 2KiB page */ diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 9aa2a91..18e55db 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -17,8 +17,24 @@ #include #include =20 +#define MAX_DIES 2 #define MAX_BUFFERRAM 2 =20 +/** + * FlexOneNAND device boundary setting + * Setting -1 will not change the boundary + */ +#define FLEXONENAND_DIE0_BOUNDARY -1 +#define FLEXONENAND_DIE1_BOUNDARY -1 + +/** + * Setting value 1 locks the boundary + * WARNING : Once locked, the boundary cannot be changed. + * Use with care. + */ +#define FLEXONENAND_DIE0_ISLOCKED 0 +#define FLEXONENAND_DIE1_ISLOCKED 0 + /* Scan and identify a OneNAND device */ extern int onenand_scan(struct mtd_info *mtd, int max_chips); /* Free resources held by the OneNAND device */ @@ -51,6 +67,11 @@ struct onenand_bufferram { /** * struct onenand_chip - OneNAND Private Flash Chip Data * @base: [BOARDSPECIFIC] address to access OneNAND + * @dies: [INTERN][FLEX-ONENAND] number of dies on chip + * @boundary: [INTERN][FLEX-ONENAND] Boundary of the dies + * @boundary_locked: [INTERN][FLEX-ONENAND] TRUE indicates die boundary + * is locked and cannot be changed + * @diesize: [INTERN][FLEX-ONENAND] Size of the dies * @chipsize: [INTERN] the size of one chip for multichip arrays * @device_id: [INTERN] device ID * @density_mask: chip density, used for DDP devices @@ -92,9 +113,14 @@ struct onenand_bufferram { */ struct onenand_chip { void __iomem *base; + unsigned dies; + unsigned boundary[MAX_DIES]; + unsigned int boundary_locked[MAX_DIES]; + unsigned int diesize[MAX_DIES]; unsigned int chipsize; unsigned int device_id; unsigned int version_id; + unsigned int technology; unsigned int density_mask; unsigned int options; =20 @@ -145,6 +171,8 @@ struct onenand_chip { #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index =3D 0) #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index =3D 1) =20 +#define FLEXONENAND(this) \ + (this->device_id & DEVICE_IS_FLEXONENAND) #define ONENAND_GET_SYS_CFG1(this) \ (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) #define ONENAND_SET_SYS_CFG1(v, this) \ @@ -153,6 +181,9 @@ struct onenand_chip { #define ONENAND_IS_DDP(this) \ (this->device_id & ONENAND_DEVICE_IS_DDP) =20 +#define ONENAND_IS_MLC(this) \ + (this->technology & ONENAND_TECHNOLOGY_IS_MLC) + #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM #define ONENAND_IS_2PLANE(this) \ (this->options & ONENAND_HAS_2PLANE) @@ -189,5 +220,7 @@ struct onenand_manufacturers { =20 int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); +unsigned onenand_get_block(struct onenand_chip *this, loff_t addr, + unsigned *isblkslc); =20 #endif /* __LINUX_MTD_ONENAND_H */ diff --git a/include/linux/mtd/onenand_regs.h = b/include/linux/mtd/onenand_regs.h index 0c6bbe2..da48c36 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -67,6 +67,9 @@ /* * 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) @@ -84,6 +87,11 @@ #define ONENAND_VERSION_PROCESS_SHIFT (8) =20 /* + * Technology Register F006h (R) + */ +#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0) + +/* * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) */ #define ONENAND_DDP_SHIFT (15) @@ -93,7 +101,8 @@ /* * Start Address 8 F107h (R/W) */ -#define ONENAND_FPA_MASK (0x3f) +/* Note: It's actually 0x3f in case of SLC */ +#define ONENAND_FPA_MASK (0x7f) #define ONENAND_FPA_SHIFT (2) #define ONENAND_FSA_MASK (0x03) =20 @@ -105,7 +114,8 @@ #define ONENAND_BSA_BOOTRAM (0 << 2) #define ONENAND_BSA_DATARAM0 (2 << 2) #define ONENAND_BSA_DATARAM1 (3 << 2) -#define ONENAND_BSC_MASK (0x03) +/* Note: It's actually 0x03 in case of SLC */ +#define ONENAND_BSC_MASK (0x07) =20 /* * Command Register F220h (R/W) @@ -124,6 +134,9 @@ #define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_OTP_ACCESS (0x65) #define ONENAND_CMD_READID (0x90) +#define FLEXONENAND_CMD_PI_UPDATE (0x05) +#define FLEXONENAND_CMD_PI_ACCESS (0x66) +#define FLEXONENAND_CMD_RECOVER_LSB (0x05) =20 /* NOTE: Those are not *REAL* commands */ #define ONENAND_CMD_BUFFERRAM (0x1978) @@ -192,10 +205,12 @@ #define ONENAND_ECC_1BIT_ALL (0x5555) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) +#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) =20 /* * One-Time Programmable (OTP) */ +#define FLEXONENAND_OTP_LOCK_OFFSET (2048) #define ONENAND_OTP_LOCK_OFFSET (14) =20 #endif /* __ONENAND_REG_H */