From patchwork Mon Jul 29 15:23:39 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jagannadha Sutradharudu Teki X-Patchwork-Id: 262840 X-Patchwork-Delegate: jagannadh.teki@gmail.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 2E52B2C00FB for ; Tue, 30 Jul 2013 01:40:20 +1000 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 3957A4A026; Mon, 29 Jul 2013 17:40:06 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 0ywBc0hDMMoU; Mon, 29 Jul 2013 17:40:06 +0200 (CEST) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 2F1574A03F; Mon, 29 Jul 2013 17:39:41 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 604B24A025 for ; Mon, 29 Jul 2013 17:39:34 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zcSDmxymvZKs for ; Mon, 29 Jul 2013 17:39:28 +0200 (CEST) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from am1outboundpool.messaging.microsoft.com (am1ehsobe003.messaging.microsoft.com [213.199.154.206]) by theia.denx.de (Postfix) with ESMTPS id 1EBCA4A023 for ; Mon, 29 Jul 2013 17:39:17 +0200 (CEST) Received: from mail56-am1-R.bigfish.com (10.3.201.245) by AM1EHSOBE014.bigfish.com (10.3.207.136) with Microsoft SMTP Server id 14.1.225.22; Mon, 29 Jul 2013 15:24:07 +0000 Received: from mail56-am1 (localhost [127.0.0.1]) by mail56-am1-R.bigfish.com (Postfix) with ESMTP id 4DA834A0093; Mon, 29 Jul 2013 15:24:07 +0000 (UTC) X-Forefront-Antispam-Report: CIP:149.199.60.83; KIP:(null); UIP:(null); IPV:NLI; H:xsj-gw1; RD:unknown-60-83.xilinx.com; EFVD:NLI X-SpamScore: -2 X-BigFish: VPS-2(zzzz1f42h208ch1ee6h1de0h1fdah2073h1202h1e76h1d1ah1d2ah1fc6hzz1de098h17326ah1de096h5eeeK8275bh1de097hz2fh95h668h839hd24hf0ah119dh1288h12a5h12a9h12bdh12e5h137ah139eh13b6h1441h14ddh1504h1537h162dh1631h1758h1898h18e1h1946h19b5h1b0ah1d0ch1d2eh1d3fh1dfeh1dffh1e1dh1e23h906i1155h192ch) Received-SPF: pass (mail56-am1: domain of xilinx.com designates 149.199.60.83 as permitted sender) client-ip=149.199.60.83; envelope-from=jagannadha.sutradharudu-teki@xilinx.com; helo=xsj-gw1 ; helo=xsj-gw1 ; Received: from mail56-am1 (localhost.localdomain [127.0.0.1]) by mail56-am1 (MessageSwitch) id 1375111446224717_14051; Mon, 29 Jul 2013 15:24:06 +0000 (UTC) Received: from AM1EHSMHS015.bigfish.com (unknown [10.3.201.242]) by mail56-am1.bigfish.com (Postfix) with ESMTP id 2D973360047; Mon, 29 Jul 2013 15:24:06 +0000 (UTC) Received: from xsj-gw1 (149.199.60.83) by AM1EHSMHS015.bigfish.com (10.3.207.153) with Microsoft SMTP Server id 14.16.227.3; Mon, 29 Jul 2013 15:24:05 +0000 Received: from unknown-38-66.xilinx.com ([149.199.38.66] helo=xsj-smtp1.xilinx.com) by xsj-gw1 with esmtp (Exim 4.63) (envelope-from ) id 1V3pIq-0008Gb-Qc; Mon, 29 Jul 2013 08:24:04 -0700 From: Jagannadha Sutradharudu Teki To: Date: Mon, 29 Jul 2013 20:53:39 +0530 X-Mailer: git-send-email 1.8.3 In-Reply-To: <1375111423-31385-1-git-send-email-jaganna@xilinx.com> References: <1375111423-31385-1-git-send-email-jaganna@xilinx.com> X-RCIS-Action: ALLOW MIME-Version: 1.0 Message-ID: X-OriginatorOrg: xilinx.com X-FOPE-CONNECTOR: Id%0$Dn%*$RO%0$TLS%0$FQDN%$TlsDn% Cc: Willis Max , Jagannadha Sutradharudu Teki , "Todd Legler \(tlegler\)" , Tom Rini Subject: [U-Boot] [PATCH 1/5] sf: Add support for accessing dual parallel memories X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de This patch added support for accessing dual memories in parallel connections with single chipselect line from controller. this connection mode is implemented in xilinx zynq qspi controller. For more info, see doc/README.spi-flash-conn-modes Below are the changes for dual parallel to work: - mtd layer -> addr/2, page_size*2, sector_size*2 - driver -> enable SEP_BUS[BIT:29],TWO_MEM[BIT:30] on LQSPI_CFG reg. Signed-off-by: Jagannadha Sutradharudu Teki --- doc/README.spi-flash-conn-modes | 68 +++++++++++++++++++++++++++++++++++++++++ drivers/mtd/spi/spi_flash.c | 52 ++++++++++++++++++++++--------- include/spi.h | 2 ++ include/spi_flash.h | 7 +++++ 4 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 doc/README.spi-flash-conn-modes diff --git a/doc/README.spi-flash-conn-modes b/doc/README.spi-flash-conn-modes new file mode 100644 index 0000000..f0c94b7 --- /dev/null +++ b/doc/README.spi-flash-conn-modes @@ -0,0 +1,68 @@ +# +# (C) Copyright 2013 Xilinx, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ + +SPI/QSPI flash connection modes: +================================ + +This describes how SPI/QSPI flash memories are connected to a given +controller in a single chipselect line. + +Current spi_flash framework supports, single flash memory connected +to a given controller with single chipselect line, but there are some +hw logics(ex: xilinx zynq qspi) that describes two/dual memories are +connected with a single chipselect line from a controller. + +"is_dual" from include/spi.h decribes these types of connection modes. + +Possible connections: +-------------------- +SPI_FLASH_CONN_UNKNOWN: +SPI_FLASH_CONN_SINGLE: + - single spi flash memory connected with single chip select line. + + +-------------+ CS +---------------+ + | |---------------------->| | + | Controller | I0[3:0] | Flash memory | + | SPI/QSPI |<=====================>| (SPI/QSPI) | + | | CLK | | + | |---------------------->| | + +-------------+ +---------------+ + +SPI_FLASH_CONN_DUALPARALLEL: + - dual spi/qspi flash memories are connected with a single chipselect + line and these two memories are operating parallel with separate buses. + - xilinx zynq qspi controller has implemented this feature. + see trm for more info + http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf + + +-------------+ CS +---------------+ + | |---------------------->| | + | | I0[3:0] | Upper Flash | + | |<=====================>| memory | + | | CLK | (SPI/QSPI) | + | |---------------------->| | + | Controller | CS +---------------+ + | SPI/QSPI |---------------------->| | + | | I0[3:0] | Lower Flash | + | |<=====================>| memory | + | | CLK | (SPI/QSPI) | + | |---------------------->| | + +-------------+ +---------------+ + + - two memory flash devices should has same hw part attributes (like size, + vendor..etc) + - Configurations: + Need to enable SEP_BUS[BIT:29],TWO_MEM[BIT:30] on LQSPI_CFG register. + - Operation: + Even bits, i.e. bit 0, 2, 4 ., of a data word is located in the lower memory + and odd bits, i.e. bit 1, 3, 5, ., of a data word is located in the upper memory. + +Note: Technically there is only one CS line from the controller, but +zynq qspi controller has an internal hw logic to enable additional CS +when controller is configured for dual memories. + +-- +Jagannadha Sutradharudu Teki +29-07-2013. diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c index 6a6fe37..3ed90bd 100644 --- a/drivers/mtd/spi/spi_flash.c +++ b/drivers/mtd/spi/spi_flash.c @@ -156,9 +156,10 @@ int spi_flash_write_common(struct spi_flash *flash, const u8 *cmd, int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) { - u32 erase_size; + u32 erase_size, erase_addr; u8 cmd[4]; int ret = -1; + int is_dual = flash->spi->is_dual; erase_size = flash->sector_size; if (offset % erase_size || len % erase_size) { @@ -172,10 +173,13 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) cmd[0] = CMD_ERASE_64K; while (len) { + erase_addr = offset; + if (is_dual == SPI_FLASH_CONN_DUALPARALLEL) + erase_addr /= 2; #ifdef CONFIG_SPI_FLASH_BAR u8 bank_sel; - bank_sel = offset / SPI_FLASH_16MB_BOUN; + bank_sel = erase_addr / SPI_FLASH_16MB_BOUN; ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); if (ret) { @@ -183,10 +187,10 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) return ret; } #endif - spi_flash_addr(offset, cmd); + spi_flash_addr(erase_addr, cmd); debug("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1], - cmd[2], cmd[3], offset); + cmd[2], cmd[3], erase_addr); ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0); if (ret < 0) { @@ -204,19 +208,23 @@ int spi_flash_cmd_erase(struct spi_flash *flash, u32 offset, size_t len) int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, size_t len, const void *buf) { - unsigned long byte_addr, page_size; + unsigned long byte_addr, page_size, write_addr; size_t chunk_len, actual; u8 cmd[4]; int ret = -1; + int is_dual = flash->spi->is_dual; page_size = flash->page_size; cmd[0] = CMD_PAGE_PROGRAM; for (actual = 0; actual < len; actual += chunk_len) { + write_addr = offset; + if (is_dual == SPI_FLASH_CONN_DUALPARALLEL) + write_addr /= 2; #ifdef CONFIG_SPI_FLASH_BAR u8 bank_sel; - bank_sel = offset / SPI_FLASH_16MB_BOUN; + bank_sel = write_addr / SPI_FLASH_16MB_BOUN; ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); if (ret) { @@ -230,7 +238,7 @@ int spi_flash_cmd_write_multi(struct spi_flash *flash, u32 offset, if (flash->spi->max_write_size) chunk_len = min(chunk_len, flash->spi->max_write_size); - spi_flash_addr(offset, cmd); + spi_flash_addr(write_addr, cmd); debug("PP: 0x%p => cmd = { 0x%02x 0x%02x%02x%02x } chunk_len = %zu\n", buf + actual, cmd[0], cmd[1], cmd[2], cmd[3], chunk_len); @@ -275,8 +283,9 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, size_t len, void *data) { u8 cmd[5], bank_sel = 0; - u32 remain_len, read_len; + u32 remain_len, read_len, read_addr, bank_boun; int ret = -1; + int is_dual = flash->spi->is_dual; /* Handle memory-mapped SPI */ if (flash->memory_map) { @@ -284,12 +293,19 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, return 0; } + bank_boun = SPI_FLASH_16MB_BOUN; + if (is_dual == SPI_FLASH_CONN_DUALPARALLEL) + bank_boun = SPI_FLASH_16MB_BOUN << 1; + cmd[0] = CMD_READ_ARRAY_FAST; cmd[4] = 0x00; while (len) { + read_addr = offset; + if (is_dual == SPI_FLASH_CONN_DUALPARALLEL) + read_addr /= 2; #ifdef CONFIG_SPI_FLASH_BAR - bank_sel = offset / SPI_FLASH_16MB_BOUN; + bank_sel = read_addr / SPI_FLASH_16MB_BOUN; ret = spi_flash_cmd_bankaddr_write(flash, bank_sel); if (ret) { @@ -297,13 +313,13 @@ int spi_flash_cmd_read_fast(struct spi_flash *flash, u32 offset, return ret; } #endif - remain_len = (SPI_FLASH_16MB_BOUN * (bank_sel + 1) - offset); + remain_len = (bank_boun * (bank_sel + 1) - read_addr); if (len < remain_len) read_len = len; else read_len = remain_len; - spi_flash_addr(offset, cmd); + spi_flash_addr(read_addr, cmd); ret = spi_flash_read_common(flash, cmd, sizeof(cmd), data, read_len); @@ -380,7 +396,10 @@ int spi_flash_bank_config(struct spi_flash *flash, u8 idcode0) /* read the bank reg - on which bank the flash is in currently */ cmd = flash->bank_read_cmd; - if (flash->size > SPI_FLASH_16MB_BOUN) { + if (((flash->spi->is_dual == SPI_FLASH_CONN_SINGLE) && + (flash->size > SPI_FLASH_16MB_BOUN)) || + ((flash->spi->is_dual == SPI_FLASH_CONN_DUALPARALLEL) && + ((flash->size / 2) > SPI_FLASH_16MB_BOUN))) { if (spi_flash_read_common(flash, &cmd, 1, &curr_bank, 1)) { debug("SF: fail to read bank addr register\n"); return -1; @@ -561,9 +580,12 @@ struct spi_flash *spi_flash_probe(unsigned int bus, unsigned int cs, printf(", mapped at %p", flash->memory_map); puts("\n"); #ifndef CONFIG_SPI_FLASH_BAR - if (flash->size > SPI_FLASH_16MB_BOUN) { - puts("SF: Warning - Only lower 16MiB accessible,"); - puts(" Full access #define CONFIG_SPI_FLASH_BAR\n"); + if (((flash->spi->is_dual == SPI_FLASH_CONN_SINGLE) && + (flash->size > SPI_FLASH_16MB_BOUN)) || + ((flash->spi->is_dual == SPI_FLASH_CONN_DUALPARALLEL) && + ((flash->size / 2) > SPI_FLASH_16MB_BOUN))) { + puts("SF: Warning - Only lower 16MiB accessible for given"); + puts("device, Full access #define CONFIG_SPI_FLASH_BAR\n"); } #endif diff --git a/include/spi.h b/include/spi.h index c0dab57..bb242bd 100644 --- a/include/spi.h +++ b/include/spi.h @@ -40,11 +40,13 @@ * cs: ID of the chip select connected to the slave. * max_write_size: If non-zero, the maximum number of bytes which can * be written at once, excluding command bytes. + * is_dual: Indicates whether dual memories are used. */ struct spi_slave { unsigned int bus; unsigned int cs; unsigned int max_write_size; + int is_dual; }; /*----------------------------------------------------------------------- diff --git a/include/spi_flash.h b/include/spi_flash.h index bfc59aa..fb132ab 100644 --- a/include/spi_flash.h +++ b/include/spi_flash.h @@ -17,6 +17,13 @@ #include #include +/* SPI connection modes */ +enum spi_conn_modes { + SPI_FLASH_CONN_UNKNOWN = -1, + SPI_FLASH_CONN_SINGLE, + SPI_FLASH_CONN_DUALPARALLEL, +}; + struct spi_flash { struct spi_slave *spi;