From patchwork Wed Dec 2 14:24:20 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: vimal singh X-Patchwork-Id: 40044 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 ozlabs.org (Postfix) with ESMTPS id 4CCB3B7BEB for ; Thu, 3 Dec 2009 01:27:23 +1100 (EST) Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1NFq8O-0001gO-7U; Wed, 02 Dec 2009 14:24:48 +0000 Received: from mail-bw0-f212.google.com ([209.85.218.212]) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1NFq8H-0001f9-DH for linux-mtd@lists.infradead.org; Wed, 02 Dec 2009 14:24:46 +0000 Received: by bwz4 with SMTP id 4so214908bwz.2 for ; Wed, 02 Dec 2009 06:24:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:from:date:message-id :subject:to:content-type; bh=eK5BbG+TvaUKnBcYrnQg3uifvJKR2vKNon9qsvT7dks=; b=cN+LB2/XwhKgivRgIB+bvX8oNgP9CZyf4OXyksJXquMsBU/T/jjf+6uTh6biu1+caZ NWVVAq2Dos14esuD0tFcvlSIdVEjjHbTkCVJQodZq9Yp+FJOsAdEe7dNOiTmF+JOek7O iTlyB+1T1kUPR+DAVRVI6f4Mb71gK1Y6k3m40= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:from:date:message-id:subject:to:content-type; b=esjPdXkGunhbUdR7CNN5C6IhFDAblbt1JozyJIjNNwVJzUPoHKNngksaFUFxbp7YPe 1wACpMXFB4+O+b/koZS7hGmYkeFOf8TcnLRet9clo/D1sKiw++6DQ1G45DMnGa6qT09z 7F83Jd//4IZNw6cTDQWmxGVTX6XN4UxZqbIfk= MIME-Version: 1.0 Received: by 10.204.33.131 with SMTP id h3mr206270bkd.53.1259763880114; Wed, 02 Dec 2009 06:24:40 -0800 (PST) From: Vimal Singh Date: Wed, 2 Dec 2009 19:54:20 +0530 Message-ID: Subject: [RFC][PATCH] Add NAND lock/unlock routines To: Linux MTD X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20091202_092441_773888_651FAFD4 X-CRM114-Status: GOOD ( 24.23 ) X-Spam-Score: 0.0 (/) X-Spam-Report: SpamAssassin version 3.2.5 on bombadil.infradead.org summary: Content analysis details: (0.0 points) pts rule name description ---- ---------------------- -------------------------------------------------- _SUMMARY_ 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: , Sender: linux-mtd-bounces@lists.infradead.org Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org I am not sure how useful it will be, but still here is a patch for review. -vimal From: Vimal Singh Date: Tue, 24 Nov 2009 18:26:43 +0530 Subject: [PATCH] Add NAND lock/unlock routines At least 'Micron' NAND parts have lock/unlock feature. Adding routines for this. Signed-off-by: Vimal Singh --- drivers/mtd/nand/nand_base.c | 217 +++++++++++++++++++++++++++++++++++++++++- include/linux/mtd/nand.h | 6 + 2 files changed, 221 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2957cc7..e447c24 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -757,6 +757,218 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** + * __nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * @invert - when = 0, unlock the range of blocks within the lower and + * upper boundary address + * whne = 1, unlock the range of blocks outside the boundaries + * of the lower and upper boundary address + * + * @return - unlock status + */ +static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, + uint64_t len, int invert) +{ + int ret = 0; + int status, page; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + /* Submit address of first page to unlock */ + page = (int)(ofs >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* Submit address of last page to unlock */ + page = (int)((ofs + len) >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, + ((page | invert) & chip->pagemask)); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + /* There was an error */ + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + } + + return ret; +} + +/** + * nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - unlock status + */ +static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr; + struct nand_chip *chip = mtd->priv; + + /* Start address must align on block boundary */ + if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + ret = -EINVAL; + goto out; + } + + /* Length must align on block boundary */ + if (len & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); + ret = -EINVAL; + goto out; + } + + /* Do not allow past end of device */ + if ((ofs + len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", + __func__); + ret = -EINVAL; + goto out; + } + + /* Align to last block address if size addresses end of the device */ + if ((ofs + len) == mtd->size) + len -= mtd->erasesize; + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd, FL_UNLOCKING); + + /* Shift to get chip number */ + chipnr = (int)(ofs >> chip->chip_shift); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} + +/** + * nand_lock - [REPLACABLE] locks all blockes present in the device + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - lock status + * + * This feature is not support in many NAND parts. 'Micron' NAND parts + * do have this feature, but it allows only to lock all blocks not for + * specified range for block. + * + * Implementing 'lock' feature by making use of 'unlock', for now. + */ +static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr, status, page; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + /* Start address must align on block boundary */ + if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + ret = -EINVAL; + goto out; + } + + /* Length must align on block boundary */ + if (len & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); + ret = -EINVAL; + goto out; + } + + /* Do not allow past end of device */ + if ((ofs + len) > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", + __func__); + ret = -EINVAL; + goto out; + } + + /* Grab the lock and see if the device is available */ + nand_get_device(chip, mtd, FL_LOCKING); + + /* Shift to get first page */ + page = (int)(ofs >> chip->page_shift); + chipnr = (int)(ofs >> chip->chip_shift); + + /* Select the NAND device */ + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + status = MTD_ERASE_FAILED; + ret = -EIO; + goto out; + } + + /* Submit address of first page to unlock */ + page = (int)(ofs >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + /* There was an error */ + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + goto out; + } + + if (len != -1) + ret = __nand_unlock(mtd, ofs, len, 0x1); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + /* Deselect and wake up anyone waiting on the device */ + nand_release_device(mtd); + + return ret; +} + +/** * nand_read_page_raw - [Intern] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure @@ -2257,6 +2469,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, status = chip->waitfunc(mtd, chip); +printk(KERN_INFO"VIMAL: status: 0x%x\n",status); /* * See if operation failed and additional status checks are * available @@ -2880,8 +3093,8 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->sync = nand_sync; - mtd->lock = NULL; - mtd->unlock = NULL; + mtd->lock = nand_lock; + mtd->unlock = nand_unlock; mtd->suspend = nand_suspend; mtd->resume = nand_resume; mtd->block_isbad = nand_block_isbad; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7a232a9..f2d97ea 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -80,6 +80,10 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff +#define NAND_CMD_LOCK 0x2a +#define NAND_CMD_UNLOCK1 0x23 +#define NAND_CMD_UNLOCK2 0x24 + /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 #define NAND_CMD_RNDOUTSTART 0xE0 @@ -214,6 +218,8 @@ typedef enum { FL_SYNCING, FL_CACHEDPRG, FL_PM_SUSPENDED, + FL_LOCKING, + FL_UNLOCKING, } nand_state_t; /* Keep gcc happy */