From patchwork Wed Aug 26 05:41:04 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amul Saha X-Patchwork-Id: 32107 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from bombadil.infradead.org (bombadil.infradead.org [18.85.46.34]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by bilbo.ozlabs.org (Postfix) with ESMTPS id 6367BB6EDE for ; Wed, 26 Aug 2009 15:43:45 +1000 (EST) Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1MgBFf-0004lk-8N; Wed, 26 Aug 2009 05:40:55 +0000 Received: from mailout5.samsung.com ([203.254.224.35]) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1MgBFW-0004kD-ME for linux-mtd@lists.infradead.org; Wed, 26 Aug 2009 05:40:51 +0000 Received: from epmmp1 (mailout5.samsung.com [203.254.224.35]) by mailout1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTP id <0KOY00HI8YFRI7@mailout1.samsung.com> for linux-mtd@lists.infradead.org; Wed, 26 Aug 2009 14:40:39 +0900 (KST) Received: from amulsaha ([107.108.214.27]) by mmp1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0KOY00A12YFPOK@mmp1.samsung.com> for linux-mtd@lists.infradead.org; Wed, 26 Aug 2009 14:40:39 +0900 (KST) Date: Wed, 26 Aug 2009 11:11:04 +0530 From: Amul Kumar Saha Subject: Re: [PATCH] [OneNAND] OTP support re-implementation 1/1 To: linux-mtd@lists.infradead.org Message-id: <87D6E94B11734CE1A253D9AE203F4192@sisodomain.com> X-MIMEOLE: Produced By Microsoft MimeOLE V6.00.2900.5579 X-Mailer: Microsoft Outlook Express 6.00.2900.5843 X-Priority: 3 X-MSMail-priority: Normal X-RFC2646: Format=Flowed; Response References: X-Spam-Score: -4.0 (----) X-Spam-Report: SpamAssassin version 3.2.5 on bombadil.infradead.org summary: Content analysis details: (-4.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- -4.0 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/, medium trust [203.254.224.35 listed in list.dnswl.org] Cc: dedekind@infradead.org, kyungmin.park@samsung.com, David Woodhouse X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org Hi, Resending the patch, as there was a line-wrap issue in the earlier post. With Regards, Amul Kumar Saha Re-implemented OTP support for OneNAND Added following features to OneNAND 1. Lock only 1st Block in OneNAND 2. Lock BOTH 1st Block and OTP Block in OneNAND Signed-off-by: Amul Kumar Saha --- drivers/mtd/onenand/Kconfig | 19 ++ drivers/mtd/onenand/onenand_base.c | 291 +++++++++++++++++++++++++++++++++---- include/linux/mtd/onenand.h | 2 3 files changed, 281 insertions(+), 31 deletions(-) diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 79fa79e..07ca80c 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -48,6 +48,25 @@ config MTD_ONENAND_OTP OTP block is fully-guaranteed to be a valid block. +if MTD_ONENAND_OTP + +config ONENAND_OTP_AREA + bool "OTP area ONLY" + depends on MTD_ONENAND_OTP + select ON_OTP_AREA + +config ONENAND_OTP_BLOCK0 + bool "Block[0] ONLY" + depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA + select ON_OTP_BLOCK0 + +config ONENAND_OTP_AREA_BLOCK0 + bool "BOTH OTP area AND Block[0]" + depends on MTD_ONENAND_OTP && !ONENAND_OTP_AREA && !ONENAND_OTP_BLOCK0 + select ON_OTP_AREA_BLOCK0 + +endif #MTD_ONENAND_OTP + config MTD_ONENAND_2X_PROGRAM bool "OneNAND 2X program support" help diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 6e82909..3da9d63 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -13,6 +13,10 @@ * Flex-OneNAND support * Copyright (C) Samsung Electronics, 2008 * + * Amul Kumar Saha + * OTP support + * Copyright (C) SAMSUNG Electronics, 2009 + * * 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. @@ -308,6 +312,81 @@ int flexonenand_region(struct mtd_info *mtd, loff_t addr) EXPORT_SYMBOL(flexonenand_region); /** + * onenand_otp_command - Send OTP specific command to OneNAND device + * @param mtd MTD device structure + * @param cmd the command to be sent + * @param addr offset to read from or write to + * @param len number of bytes to read or write + */ +static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + int value, block, page; + + /* Address translation */ + switch (cmd) { + case ONENAND_CMD_OTP_ACCESS: + block = (int) (addr >> this->erase_shift); + page = -1; + break; + + default: + block = (int) (addr >> this->erase_shift); + page = (int) (addr >> this->page_shift); + + if (ONENAND_IS_2PLANE(this)) { + /* Make the even block number */ + block &= ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page >>= 1; + } + page &= this->page_mask; + break; + } + + if (block != -1) { + /* Write 'DFS, FBA' of Flash */ + value = onenand_block_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS1); + } + + if (page != -1) { + /* Now we use page size operation */ + int sectors = 4, count = 4; + int dataram; + + switch (cmd) { + default: + if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) + cmd = ONENAND_CMD_2X_PROG; + dataram = ONENAND_CURRENT_BUFFERRAM(this); + break; + } + + /* Write 'FPA, FSA' of Flash */ + value = onenand_page_address(page, sectors); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS8); + + /* Write 'BSA, BSC' of DataRAM */ + value = onenand_buffer_address(dataram, sectors, count); + this->write_word(value, this->base + ONENAND_REG_START_BUFFER); + } + + /* Interrupt clear */ + this->write_word(ONENAND_INT_CLEAR, this->base + ONENAND_REG_INTERRUPT); + + /* Write command */ + this->write_word(cmd, this->base + ONENAND_REG_COMMAND); + + return 0; +} + +/** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd MTD device structure * @param cmd the command to be sent @@ -1879,7 +1958,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); if (ret) { written -= prevlen; - printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write_ops_nolock: \ + write failed %d\n", ret); break; } @@ -1905,14 +1985,16 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); if (ret) { - printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write_ops_nolock: \ + write failed %d\n", ret); break; } /* Only check verify write turn on */ ret = onenand_verify(mtd, buf, to, thislen); if (ret) { - printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); + printk(KERN_ERR "onenand_write_ops_nolock: \ + verify failed %d\n", ret); break; } @@ -1945,6 +2027,134 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /** + * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP + * @param mtd MTD device structure + * @param to offset to write to + * @param len number of bytes to write + * @param retlen pointer to variable to store the number of written bytes + * @param buf the data to write + * + * OneNAND write out-of-band only for OTP + */ +static int onenand_otp_write_oob_nolock(struct mtd_info *mtd, loff_t to, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int column, ret = 0, oobsize; + int written = 0; + u_char *oobbuf; + size_t len = ops->ooblen; + const u_char *buf = ops->oobbuf; + int block, value, status; + + to += ops->ooboffs; + + /* Initialize retlen, in case of early exit */ + ops->oobretlen = 0; + + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + oobbuf = this->oob_buf; + + /* Loop until all data write */ + while (written < len) { + int thislen = min_t(int, oobsize, len - written); + + cond_resched(); + + block = (int) (to >> this->erase_shift); + /* + * Write 'DFS, FBA' of Flash + * Add: F100h DQ=DFS, FBA + */ + + value = onenand_block_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS1); + + /* + * Select DataRAM for DDP + * Add: F101h DQ=DBS + */ + + value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + + ONENAND_REG_START_ADDRESS2); + ONENAND_SET_NEXT_BUFFERRAM(this); + + /* + * Enter OTP access mode + */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); + + /* We send data to spare ram with oobsize + * to prevent byte access */ + memcpy(oobbuf + column, buf, thislen); + + /* + * Write Data into DataRAM + * Add: 8th Word + * in sector0/spare/page0 + * DQ=XXFCh + */ + this->write_bufferram(mtd, ONENAND_SPARERAM, + oobbuf, 0, mtd->oobsize); + + onenand_otp_command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } + + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "onenand_write_oob_nolock: \ + write failed %d\n", ret); + break; + } + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); + + status = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + status &= 0x60; + + if (status == 0x60) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tLOCKED\n"); + printk(KERN_DEBUG "OTP Block\tLOCKED\n"); + } else if (status == 0x20) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tLOCKED\n"); + printk(KERN_DEBUG "OTP Block\tUN-LOCKED\n"); + } else if (status == 0x40) { + printk(KERN_DEBUG "\nBLOCK\tSTATUS\n"); + printk(KERN_DEBUG "1st Block\tUN-LOCKED\n"); + printk(KERN_DEBUG "OTP Block\tLOCKED\n"); + } else { + printk(KERN_DEBUG "Reboot to check\n"); + } + + written += thislen; + if (written == len) + break; + + to += mtd->writesize; + buf += thislen; + column = 0; + } + + ops->oobretlen = written; + + return ret; +} + +/** * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to @@ -2659,11 +2869,11 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, struct mtd_oob_ops ops; int ret; - /* Enter OTP access mode */ - this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); - this->wait(mtd, FL_OTPING); - if (FLEXONENAND(this)) { + + /* Enter OTP access mode */ + this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); + this->wait(mtd, FL_OTPING); /* * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of * main area of page 49. @@ -2674,19 +2884,19 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, ops.oobbuf = NULL; ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); *retlen = ops.retlen; + + /* Exit OTP access mode */ + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + this->wait(mtd, FL_RESETING); } else { ops.mode = MTD_OOB_PLACE; ops.ooblen = len; ops.oobbuf = buf; ops.ooboffs = 0; - ret = onenand_write_oob_nolock(mtd, from, &ops); + ret = onenand_otp_write_oob_nolock(mtd, from, &ops); *retlen = ops.oobretlen; } - /* Exit OTP access mode */ - this->command(mtd, ONENAND_CMD_RESET, 0, 0); - this->wait(mtd, FL_RESETING); - return ret; } @@ -2717,16 +2927,21 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, if (density < ONENAND_DEVICE_DENSITY_512Mb) otp_pages = 20; else - otp_pages = 10; + otp_pages = 50; if (mode == MTD_OTP_FACTORY) { from += mtd->writesize * otp_pages; - otp_pages = 64 - otp_pages; + otp_pages = ONENAND_PAGES_PER_BLOCK - otp_pages; } /* Check User/Factory boundary */ - if (((mtd->writesize * otp_pages) - (from + len)) < 0) - return 0; + if (mode == MTD_OTP_USER) { + if (((mtd->writesize * otp_pages) - (from + len)) < 0) + return 0; + } else { + if (((mtd->writesize * otp_pages) - len) < 0) + return 0; + } onenand_get_device(mtd, FL_OTPING); while (len > 0 && otp_pages > 0) { @@ -2749,13 +2964,12 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, *retlen += sizeof(struct otp_info); } else { size_t tmp_retlen; - int size = len; ret = action(mtd, from, len, &tmp_retlen, buf); - buf += size; - len -= size; - *retlen += size; + buf += tmp_retlen; + len -= tmp_retlen; + *retlen += tmp_retlen; if (ret) break; @@ -2872,17 +3086,6 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, 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) - */ - if (FLEXONENAND(this)) - buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC; - else - buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; - - /* * 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 @@ -2892,6 +3095,32 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, from = 0; len = FLEXONENAND(this) ? mtd->writesize : 16; + /* + * Note: OTP lock operation + * OTP block : 0xXXFC XX 1111 1100 + * 1st block : 0xXXF3 (If chip support) XX 1111 0011 + * Both : 0xXXF0 (If chip support) XX 1111 0000 + */ + if (FLEXONENAND(this)) { + +#ifdef CONFIG_ONENAND_OTP_AREA + buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC; +#elif CONFIG_ONENAND_OTP_BLOCK0 + buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF3; +#elif CONFIG_ONENAND_OTP_AREA_BLOCK0 + buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xF0; +#endif /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */ + } else { + +#ifdef CONFIG_ONENAND_OTP_AREA + buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; +#elif CONFIG_ONENAND_OTP_BLOCK0 + buf[ONENAND_OTP_LOCK_OFFSET] = 0xF3; +#elif CONFIG_ONENAND_OTP_AREA_BLOCK0 + buf[ONENAND_OTP_LOCK_OFFSET] = 0xF0; +#endif /* ONENAND_OTP_AREA | ONENAND_OTP_BLOCK0 | ONENAND_OTP_AREA_BLOCK0 */ + } + ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); return ret ? : retlen; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 8ed8733..7bd1512 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -152,6 +152,8 @@ struct onenand_chip { /* * Helper macros */ +#define ONENAND_PAGES_PER_BLOCK (1<<6) + #define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index) #define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) #define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1)