From patchwork Wed Feb 28 11:55:01 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Radu Pirea X-Patchwork-Id: 879077 X-Patchwork-Delegate: cyrille.pitchen@atmel.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=microchip.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="BUtc7wNw"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3zrvB96Bmlz9s1x for ; Wed, 28 Feb 2018 22:54:41 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:Message-ID:Date:Subject:To :From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=/0BMoLOCLK9KuRF4demfNHmpfa9vFnC5eqZai1oG1/A=; b=BUtc7wNw1LuM/z C5CTdj756Llu80B+fshY1/ExjcynjxtnaWxBMicmEd9lZeFVMMxaiQ6XS+aCwAZ6rCnArL0Ix4rMO sAViB5TALD5H8KEAs01/Dx9m3c5fNXjl4TUcYlLvZ1M8jg6z5gZvmXBU2siOenioI66Hr5y6WfQaf HKkwuVvGGYmmGxJdAJd4bqKwQIL9KnzWTcIpvL+RmPLC1doYMGYvm2P9ZzMrfMtDHZKSJnmLZ88+1 qLt3ZsoBkKfSCIK94SUcfsPi/P6w9J9JgX4ScoxHbeAT8NL5SAFOAv9eV83qAJyzOllaTw+/04syL sfhiNIqXwyKkmwqMk76g==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.89 #1 (Red Hat Linux)) id 1er0Jh-0002ej-LT; Wed, 28 Feb 2018 11:54:37 +0000 Received: from esa1.microchip.iphmx.com ([68.232.147.91]) by bombadil.infradead.org with esmtps (Exim 4.89 #1 (Red Hat Linux)) id 1er0Jd-0002cZ-VK for linux-mtd@lists.infradead.org; Wed, 28 Feb 2018 11:54:35 +0000 X-IronPort-AV: E=Sophos;i="5.47,405,1515481200"; d="scan'208";a="12463654" Received: from smtpout.microchip.com (HELO email.microchip.com) ([198.175.253.82]) by esa1.microchip.iphmx.com with ESMTP/TLS/DHE-RSA-AES256-SHA; 28 Feb 2018 04:54:22 -0700 Received: from rob-ult-m19893.microchip.com (10.10.76.4) by chn-sv-exch04.mchp-main.com (10.10.76.105) with Microsoft SMTP Server id 14.3.352.0; Wed, 28 Feb 2018 04:54:21 -0700 From: Radu Pirea To: , , , , , , , , Subject: [PATCH] spi-nor: Add support for Atmel Dataflash memories Date: Wed, 28 Feb 2018 13:55:01 +0200 Message-ID: <1519818901-7116-1-git-send-email-radu.pirea@microchip.com> X-Mailer: git-send-email 2.7.4 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180228_035434_038956_79510E0A X-CRM114-Status: GOOD ( 18.21 ) X-Spam-Score: -2.6 (--) X-Spam-Report: SpamAssassin version 3.4.1 on bombadil.infradead.org summary: Content analysis details: (-2.6 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [68.232.147.91 listed in list.dnswl.org] -0.0 SPF_PASS SPF: sender matches SPF record -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: Linux MTD discussion mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Radu Pirea Sender: "linux-mtd" Errors-To: linux-mtd-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org This patch add support in spi-nor for allmost all dataflash memories supported by old mtd_dataflash driver. Signed-off-by: Radu Pirea --- drivers/mtd/spi-nor/spi-nor.c | 150 +++++++++++++++++++++++++++++++++++++++++- include/linux/mtd/spi-nor.h | 10 +++ 2 files changed, 158 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index d445a4d..4cb3cf7 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -89,6 +89,7 @@ struct flash_info { #define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */ #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */ #define USE_CLSR BIT(14) /* use CLSR command */ +#define SPI_DATAFLASH BIT(15) /* Atmel Dataflash memory */ int (*quad_enable)(struct spi_nor *nor); }; @@ -306,6 +307,20 @@ static int s3an_sr_ready(struct spi_nor *nor) return !!(val & XSR_RDY); } +static int dataflash_sr_ready(struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_DFRDSR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error %d reading DFSR\n", ret); + return ret; + } + + return !!(val & DFSR_RDY); +} + static inline int spi_nor_sr_ready(struct spi_nor *nor) { int sr = read_sr(nor); @@ -354,6 +369,8 @@ static int spi_nor_ready(struct spi_nor *nor) if (nor->flags & SNOR_F_READY_XSR_RDY) sr = s3an_sr_ready(nor); + else if (nor->flags & SPI_DATAFLASH) + sr = dataflash_sr_ready(nor); else sr = spi_nor_sr_ready(nor); if (sr < 0) @@ -457,6 +474,44 @@ static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr) return page | offset; } +static loff_t spi_nor_dataflash_addr_convert(struct spi_nor *nor, + unsigned int addr) +{ + unsigned int offset; + unsigned int page; + unsigned int page_offset; + + page = addr / nor->page_size; + offset = addr % nor->page_size; + page_offset = fls(nor->page_size); + if (is_power_of_2(nor->page_size)) + page_offset--; + + return (page << page_offset) | offset; +} + +static int spi_nor_dataflash_erase_sector(struct spi_nor *nor, u32 addr) +{ + u32 block_size = 8 * nor->page_size; + u32 blocks = nor->mtd.erasesize / block_size; + u32 addr_local; + u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; + int i, j; + + for (j = 0; j < blocks; j++) { + addr_local = spi_nor_dataflash_addr_convert(nor, addr); + for (i = nor->addr_width - 1; i >= 0; i--) { + buf[i] = addr_local & 0xff; + addr_local >>= 8; + } + nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width); + addr += block_size; + spi_nor_wait_till_ready(nor); + } + + return 0; +} + /* * Initiate the erasure of a single sector */ @@ -542,7 +597,11 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) while (len) { write_enable(nor); - ret = spi_nor_erase_sector(nor, addr); + if (nor->flags & SPI_DATAFLASH) + ret = spi_nor_dataflash_erase_sector(nor, addr); + else + ret = spi_nor_erase_sector(nor, addr); + if (ret) goto erase_err; @@ -914,6 +973,20 @@ static int macronix_quad_enable(struct spi_nor *nor); .page_size = 256, \ .flags = (_flags), +#define INFOP(_jedec_id, _ext_id, _sector_size, _n_sectors, _page_size, _flags) \ + .id = { \ + ((_jedec_id) >> 16) & 0xff, \ + ((_jedec_id) >> 8) & 0xff, \ + (_jedec_id) & 0xff, \ + ((_ext_id) >> 8) & 0xff, \ + (_ext_id) & 0xff, \ + }, \ + .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))), \ + .sector_size = (_sector_size), \ + .n_sectors = (_n_sectors), \ + .page_size = _page_size, \ + .flags = (_flags), + #define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ .id = { \ ((_jedec_id) >> 16) & 0xff, \ @@ -975,7 +1048,14 @@ static const struct flash_info spi_nor_ids[] = { { "at26df161a", INFO(0x1f4601, 0, 64 * 1024, 32, SECT_4K) }, { "at26df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) }, - { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, + { "at45db021d", INFOP(0x1f2300, 0, 512 * 264, 2, 264, SPI_DATAFLASH | NO_CHIP_ERASE) }, + { "at45db041d", INFOP(0x1f2400, 0, 256 * 264, 8, 264, SPI_DATAFLASH | NO_CHIP_ERASE) }, + { "at45db081d", INFOP(0x1f2500, 0, 256 * 264, 16, 264, SPI_DATAFLASH | NO_CHIP_ERASE) }, + { "at45db161d", INFOP(0x1f2600, 0, 256 * 528, 16, 528, SPI_DATAFLASH | NO_CHIP_ERASE) }, + { "at45db321d", INFOP(0x1f2700, 0, 128 * 528, 64, 528, SPI_DATAFLASH | NO_CHIP_ERASE) }, + { "at45db321d", INFOP(0x1f2701, 0, 128 * 528, 64, 528, SPI_DATAFLASH | NO_CHIP_ERASE) }, + { "at45db642d", INFOP(0x1f2800, 0, 256 * 1056, 32, 1056, SPI_DATAFLASH | NO_CHIP_ERASE) }, + { "at45db641e", INFOP(0x1f2800, 0x0100, 1024 * 264, 32, 264, SPI_DATAFLASH | NO_CHIP_ERASE) }, /* EON -- en25xxx */ { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, @@ -1278,6 +1358,9 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) addr = spi_nor_s3an_addr_convert(nor, addr); + if (nor->flags & SPI_DATAFLASH) + addr = spi_nor_dataflash_addr_convert(nor, addr); + ret = nor->read(nor, addr, len, buf); if (ret == 0) { /* We shouldn't see 0-length reads */ @@ -1379,6 +1462,21 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len, return ret; } +static int dataflash_memtobuf(struct spi_nor *nor, u32 addr) +{ + u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; + int i; + + addr = spi_nor_dataflash_addr_convert(nor, addr); + + for (i = nor->addr_width - 1; i >= 0; i--) { + buf[i] = addr & 0xff; + addr >>= 8; + } + + return nor->write_reg(nor, SPINOR_OP_DFMTB, buf, nor->addr_width); +} + /* * Write an address range to the nor chip. Data must be written in * FLASH_PAGESIZE chunks. The address range may be any size provided @@ -1420,9 +1518,17 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, page_remain = min_t(size_t, nor->page_size - page_offset, len - i); + if (nor->flags & SPI_DATAFLASH) { + dataflash_memtobuf(nor, addr); + spi_nor_wait_till_ready(nor); + } + if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT) addr = spi_nor_s3an_addr_convert(nor, addr); + if (nor->flags & SPI_DATAFLASH) + addr = spi_nor_dataflash_addr_convert(nor, addr); + write_enable(nor); ret = nor->write(nor, addr, page_remain, buf + i); if (ret < 0) @@ -1742,6 +1848,37 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor) return 0; } +static int dataflash_nor_scan(const struct flash_info *info, + struct spi_nor *nor) +{ + int ret; + u8 val; + + ret = nor->read_reg(nor, SPINOR_OP_DFRDSR, &val, 1); + if (ret < 0) { + dev_err(nor->dev, "error reading status register\n"); + return ret; + } + + nor->erase_opcode = SPINOR_OP_DFBE_8k; + nor->program_opcode = SPINOR_OP_DFBTOM; + nor->read_opcode = SPINOR_OP_DFRD; + nor->flags |= SNOR_F_NO_OP_CHIP_ERASE; + + if (val & DFSR_PAGESIZE) { + /* Flash in Power of 2 mode */ + nor->mtd.erasesize = nor->mtd.erasesize / nor->page_size * + (1 << (fls(nor->page_size) - 1)); + nor->mtd.size = nor->mtd.erasesize * info->n_sectors; + nor->page_size = 1 << (fls(nor->page_size) - 1); + nor->mtd.writebufsize = nor->page_size; + } + + nor->read_dummy = 32; + + return 0; +} + struct spi_nor_read_command { u8 num_mode_clocks; u8 num_wait_states; @@ -2818,6 +2955,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, if (info->flags & SPI_S3AN) nor->flags |= SNOR_F_READY_XSR_RDY; + if (info->flags & SPI_DATAFLASH) + nor->flags |= SPI_DATAFLASH; + /* Parse the Serial Flash Discoverable Parameters table. */ ret = spi_nor_init_params(nor, info, ¶ms); if (ret) @@ -2922,6 +3062,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, return ret; } + if (info->flags & SPI_DATAFLASH) { + ret = dataflash_nor_scan(info, nor); + if (ret) + return ret; + } + /* Send all the required SPI flash commands to initialize device */ nor->info = info; ret = spi_nor_init(nor); diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index de36969..843edf9 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -91,6 +91,16 @@ #define SPINOR_OP_WRDI 0x04 /* Write disable */ #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ +/* Used for Atmel Dataflashes only. */ +#define SPINOR_OP_DFRDSR 0xd7 +#define SPINOR_OP_DFBE_8k 0x50 +#define SPINOR_OP_DFMTB 0x53 +#define SPINOR_OP_DFBTOM 0x82 +#define SPINOR_OP_DFRD 0xe8 + +#define DFSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */ +#define DFSR_RDY BIT(7) /* Ready */ + /* Used for S3AN flashes only */ #define SPINOR_OP_XSE 0x50 /* Sector erase */ #define SPINOR_OP_XPP 0x82 /* Page program */