From patchwork Wed Jan 8 02:18:04 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Insop Song X-Patchwork-Id: 3451131 Return-Path: X-Original-To: patchwork-linux-spi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 009C19F1C4 for ; Wed, 8 Jan 2014 02:20:07 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id D216F20145 for ; Wed, 8 Jan 2014 02:20:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9C3FF20144 for ; Wed, 8 Jan 2014 02:20:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753750AbaAHCUC (ORCPT ); Tue, 7 Jan 2014 21:20:02 -0500 Received: from [207.46.163.155] ([207.46.163.155]:15021 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-FAIL-FAIL-OK-OK) by vger.kernel.org with ESMTP id S1754197AbaAHCT6 convert rfc822-to-8bit (ORCPT ); Tue, 7 Jan 2014 21:19:58 -0500 Received: from BY2PR07MB011.namprd07.prod.outlook.com (10.255.241.37) by BY2PR07MB010.namprd07.prod.outlook.com (10.255.241.36) with Microsoft SMTP Server (TLS) id 15.0.842.7; Wed, 8 Jan 2014 02:18:05 +0000 Received: from BY2PR07MB011.namprd07.prod.outlook.com ([169.254.9.102]) by BY2PR07MB011.namprd07.prod.outlook.com ([169.254.9.102]) with mapi id 15.00.0842.003; Wed, 8 Jan 2014 02:18:05 +0000 From: Insop Song To: "linux-spi@vger.kernel.org" CC: "broonie@kernel.org" Subject: [PATCH] spi: spi-nor: mtd Check flag status register for Micron nor spi n25q512a Thread-Topic: [PATCH] spi: spi-nor: mtd Check flag status register for Micron nor spi n25q512a Thread-Index: AQHPDBfcF2x0md2bFUOu+EDG/WnPqw== Date: Wed, 8 Jan 2014 02:18:04 +0000 Message-ID: Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-originating-ip: [108.60.97.2] x-forefront-prvs: 00851CA28B x-forefront-antispam-report: SFV:NSPM; SFS:(10009001)(199002)(189002)(81816001)(81686001)(77096001)(81542001)(76786001)(76576001)(76796001)(74876001)(74706001)(81342001)(2656002)(15975445006)(87266001)(76176001)(87936001)(92566001)(74366001)(65816001)(83322001)(19580395003)(19580405001)(46102001)(79102001)(80022001)(56776001)(49866001)(54316002)(56816005)(90146001)(4396001)(53806001)(54356001)(51856001)(66066001)(59766001)(63696002)(69226001)(85306002)(77982001)(33646001)(74316001)(74662001)(31966008)(74502001)(80976001)(85852003)(47976001)(50986001)(83072002)(47736001)(47446002)(138113003)(24736002); DIR:OUT; SFP:1101; SCL:1; SRVR:BY2PR07MB010; H:BY2PR07MB011.namprd07.prod.outlook.com; CLIP:108.60.97.2; FPR:; RD:InfoNoRecords; A:1; MX:1; LANG:en; MIME-Version: 1.0 X-OriginatorOrg: gainspeed.com Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP (re-posting with new subject and add a maintainer in CC, thank you,) In order to use Micron NOR SPI Flash n25q512a, two changes are required as follows: - jedec code should be fixed - flag status should be read for writing. Flag status register should be checked for Micron NOR SPI n25q512a - Programing Micron n25q512a requires to check flag status register - According to datasheet " The flag status register must be read any time a PROGRAM, ERASE, SUSPEND/RESUME command is issued, or after a RESET command while device is busy. The cycle is not complete until bit 7 of the flag status register output 1. " - Ref: https://www.micron.com/~/media/Documents/Products/Data%20Sheet/NOR%20Flash/Serial%20NOR/N25Q/n25q_512mb_1ce_3v_65nm.pdf Signed-off-by: Insop Song --- drivers/mtd/devices/m25p80.c | 91 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 7eda71d..e53e522 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -38,6 +38,7 @@ /* Flash opcodes. */ #define OPCODE_WREN 0x06 /* Write enable */ #define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_RDFSR 0x70 /* Read flag status register */ #define OPCODE_WRSR 0x01 /* Write status register 1 byte */ #define OPCODE_NORM_READ 0x03 /* Read data bytes (low frequency) */ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ @@ -70,6 +71,8 @@ /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ +/* Flag Status Register bits. */ +#define FSR_RDY 0x80 /* FSR ready */ /* meaning of other SR_* bits may differ between vendors */ #define SR_BP0 4 /* Block protect 0 */ #define SR_BP1 8 /* Block protect 1 */ @@ -95,6 +98,7 @@ struct m25p { u8 program_opcode; u8 *command; bool fast_read; + bool flag_status; }; static inline struct m25p *mtd_to_m25p(struct mtd_info *mtd) @@ -131,6 +135,28 @@ static int read_sr(struct m25p *flash) } /* + * Read the flag status register, returning its value in the location + * Return the status register value. + * Returns negative if error occurred. + */ +static int read_fsr(struct m25p *flash) +{ + ssize_t retval; + u8 code = OPCODE_RDFSR; + u8 val; + + retval = spi_write_then_read(flash->spi, &code, 1, &val, 1); + + if (retval < 0) { + printk(&flash->spi->dev, "error %d reading FSR\n", + (int) retval); + return retval; + } + + return val; +} + +/* * Write status register 1 byte * Returns negative if error occurred. */ @@ -220,6 +246,30 @@ static int wait_till_ready(struct m25p *flash) } /* + * Service routine to read flag status register until ready, or timeout occurs. + * Returns non-zero if error. + */ +static int wait_till_fsr_ready(struct m25p *flash) +{ + unsigned long deadline; + int sr; + + deadline = jiffies + MAX_READY_WAIT_JIFFIES; + + do { + if ((sr = read_fsr(flash)) < 0) + break; + else if ((sr & FSR_RDY)) + return 0; + + cond_resched(); + + } while (!time_after_eq(jiffies, deadline)); + + return 1; +} + +/* * Erase the whole flash memory * * Returns 0 if successful, non-zero otherwise. @@ -273,6 +323,14 @@ static int erase_sector(struct m25p *flash, u32 offset) if (wait_till_ready(flash)) return 1; + /* Flag status, Wait until finished previous write command. */ + if (flash->flag_status == true) { + if (wait_till_fsr_ready(flash)) { + mutex_unlock(&flash->lock); + return 1; + } + } + /* Send write enable, then erase commands. */ write_enable(flash); @@ -427,6 +485,14 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, mutex_lock(&flash->lock); + /* Flag status, Wait until finished previous write command. */ + if (flash->flag_status == true) { + if (wait_till_fsr_ready(flash)) { + mutex_unlock(&flash->lock); + return 1; + } + } + /* Wait until finished previous write command. */ if (wait_till_ready(flash)) { mutex_unlock(&flash->lock); @@ -457,6 +523,14 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, t[1].len = page_size; spi_sync(flash->spi, &m); + /* Flag status, Wait until finished previous write command. */ + if (flash->flag_status == true) { + if (wait_till_fsr_ready(flash)) { + mutex_unlock(&flash->lock); + return 1; + } + } + *retlen = m.actual_length - m25p_cmdsz(flash); /* write everything in flash->page_size chunks */ @@ -477,6 +551,14 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, spi_sync(flash->spi, &m); + /* Flag status, Wait until finished previous write command. */ + if (flash->flag_status == true) { + if (wait_till_fsr_ready(flash)) { + mutex_unlock(&flash->lock); + return 1; + } + } + *retlen += m.actual_length - m25p_cmdsz(flash); } } @@ -782,7 +864,7 @@ static const struct spi_device_id m25p_ids[] = { { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, - { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + { "n25q512a", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K) }, /* PMC */ { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, @@ -996,6 +1078,13 @@ static int m25p_probe(struct spi_device *spi) spi_set_drvdata(spi, flash); /* + * Micron n25q512a requires to check flag status register + */ + flash->flag_status = false; + if (strcmp(id->name, "n25q512a") == 0) + flash->flag_status = true;; + + /* * Atmel, SST and Intel/Numonyx serial flash tend to power * up with the software protection bits set */