From patchwork Tue Nov 10 05:29:56 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Vignesh Raghavendra X-Patchwork-Id: 7588231 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.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id C1A009F1C2 for ; Tue, 10 Nov 2015 05:32:35 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C91E320490 for ; Tue, 10 Nov 2015 05:32:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 98BA220639 for ; Tue, 10 Nov 2015 05:32:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751228AbbKJFcM (ORCPT ); Tue, 10 Nov 2015 00:32:12 -0500 Received: from devils.ext.ti.com ([198.47.26.153]:45984 "EHLO devils.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751379AbbKJFaw (ORCPT ); Tue, 10 Nov 2015 00:30:52 -0500 Received: from dflxv15.itg.ti.com ([128.247.5.124]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id tAA5UH7s002978; Mon, 9 Nov 2015 23:30:17 -0600 Received: from DFLE73.ent.ti.com (dfle73.ent.ti.com [128.247.5.110]) by dflxv15.itg.ti.com (8.14.3/8.13.8) with ESMTP id tAA5UH7w015400; Mon, 9 Nov 2015 23:30:17 -0600 Received: from dlep33.itg.ti.com (157.170.170.75) by DFLE73.ent.ti.com (128.247.5.110) with Microsoft SMTP Server id 14.3.224.2; Mon, 9 Nov 2015 23:30:17 -0600 Received: from uda0132425.apr.dhcp.ti.com (ileax41-snat.itg.ti.com [10.172.224.153]) by dlep33.itg.ti.com (8.14.3/8.13.8) with ESMTP id tAA5U5vC023652; Mon, 9 Nov 2015 23:30:13 -0600 From: Vignesh R To: Mark Brown , Tony Lindgren CC: Rob Herring , Michal Suchanek , Russell King , Vignesh R , , , , , , Subject: [PATCH v3 2/5] spi: spi-ti-qspi: add mmap mode read support Date: Tue, 10 Nov 2015 10:59:56 +0530 Message-ID: <1447133399-25658-3-git-send-email-vigneshr@ti.com> X-Mailer: git-send-email 2.6.3 In-Reply-To: <1447133399-25658-1-git-send-email-vigneshr@ti.com> References: <1447133399-25658-1-git-send-email-vigneshr@ti.com> MIME-Version: 1.0 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Spam-Status: No, score=-7.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable 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 ti-qspi controller provides mmap port to read data from SPI flashes. mmap port is enabled in QSPI_SPI_SWITCH_REG. ctrl module register may also need to be accessed for some SoCs. The QSPI_SPI_SETUP_REGx needs to be populated with flash specific information like read opcode, read mode(quad, dual, normal), address width and dummy bytes. Once, controller is in mmap mode, the whole flash memory is available as a memory region at SoC specific address. This region can be accessed using normal memcpy() (or mem-to-mem dma copy). The ti-qspi controller hardware will internally communicate with SPI flash over SPI bus and get the requested data. Implement spi_mtd_mmap_read() callback to support mmap read over SPI flash devices. With this, the read throughput increases from ~100kB/s to ~2.5 MB/s. Signed-off-by: Vignesh R --- v3: * optimize enable/disable of mmap mode drivers/spi/spi-ti-qspi.c | 99 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 64318fcfacf2..295c11f48440 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -56,6 +56,7 @@ struct ti_qspi { u32 dc; bool ctrl_mod; + bool mmap_enabled; }; #define QSPI_PID (0x0) @@ -65,11 +66,8 @@ struct ti_qspi { #define QSPI_SPI_CMD_REG (0x48) #define QSPI_SPI_STATUS_REG (0x4c) #define QSPI_SPI_DATA_REG (0x50) -#define QSPI_SPI_SETUP0_REG (0x54) +#define QSPI_SPI_SETUP_REG(n) ((0x54 + 4 * n)) #define QSPI_SPI_SWITCH_REG (0x64) -#define QSPI_SPI_SETUP1_REG (0x58) -#define QSPI_SPI_SETUP2_REG (0x5c) -#define QSPI_SPI_SETUP3_REG (0x60) #define QSPI_SPI_DATA_REG_1 (0x68) #define QSPI_SPI_DATA_REG_2 (0x6c) #define QSPI_SPI_DATA_REG_3 (0x70) @@ -109,6 +107,16 @@ struct ti_qspi { #define QSPI_AUTOSUSPEND_TIMEOUT 2000 +#define MEM_CS_EN(n) ((n + 1) << 8) + +#define MM_SWITCH 0x1 + +#define QSPI_SETUP_RD_NORMAL (0x0 << 12) +#define QSPI_SETUP_RD_DUAL (0x1 << 12) +#define QSPI_SETUP_RD_QUAD (0x3 << 12) +#define QSPI_SETUP_ADDR_SHIFT 8 +#define QSPI_SETUP_DUMMY_SHIFT 10 + static inline unsigned long ti_qspi_read(struct ti_qspi *qspi, unsigned long reg) { @@ -366,6 +374,84 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t) return 0; } +static void ti_qspi_enable_memory_map(struct spi_device *spi) +{ + struct ti_qspi *qspi = spi_master_get_devdata(spi->master); + u32 val; + + ti_qspi_write(qspi, MM_SWITCH, QSPI_SPI_SWITCH_REG); + if (qspi->ctrl_mod) { + val = readl(qspi->ctrl_base); + val |= MEM_CS_EN(spi->chip_select); + writel(val, qspi->ctrl_base); + /* dummy readl to ensure bus sync */ + readl(qspi->ctrl_base); + } + qspi->mmap_enabled = true; +} + +static void ti_qspi_disable_memory_map(struct spi_device *spi) +{ + struct ti_qspi *qspi = spi_master_get_devdata(spi->master); + u32 val; + + ti_qspi_write(qspi, 0, QSPI_SPI_SWITCH_REG); + if (qspi->ctrl_mod) { + val = readl(qspi->ctrl_base); + val &= ~MEM_CS_EN(spi->chip_select); + writel(val, qspi->ctrl_base); + } + qspi->mmap_enabled = false; +} + +static void ti_qspi_setup_mmap_read(struct spi_device *spi, + u8 read_opcode, u8 addr_width, + u8 dummy_bytes) +{ + struct ti_qspi *qspi = spi_master_get_devdata(spi->master); + u32 mode = spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD); + u32 memval = read_opcode; + + switch (mode) { + case SPI_RX_QUAD: + memval |= QSPI_SETUP_RD_QUAD; + break; + case SPI_RX_DUAL: + memval |= QSPI_SETUP_RD_DUAL; + break; + default: + memval |= QSPI_SETUP_RD_NORMAL; + break; + } + memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT | + dummy_bytes << QSPI_SETUP_DUMMY_SHIFT); + ti_qspi_write(qspi, memval, + QSPI_SPI_SETUP_REG(spi->chip_select)); +} + +static int ti_qspi_spi_mtd_mmap_read(struct spi_device *spi, + loff_t from, size_t len, + size_t *retlen, u_char *buf, + u8 read_opcode, u8 addr_width, + u8 dummy_bytes) +{ + struct ti_qspi *qspi = spi_master_get_devdata(spi->master); + int ret = 0; + + mutex_lock(&qspi->list_lock); + + if (!qspi->mmap_enabled) + ti_qspi_enable_memory_map(spi); + ti_qspi_setup_mmap_read(spi, read_opcode, addr_width, + dummy_bytes); + memcpy_fromio(buf, qspi->mmap_base + from, len); + *retlen = len; + + mutex_unlock(&qspi->list_lock); + + return ret; +} + static int ti_qspi_start_transfer_one(struct spi_master *master, struct spi_message *m) { @@ -398,6 +484,9 @@ static int ti_qspi_start_transfer_one(struct spi_master *master, mutex_lock(&qspi->list_lock); + if (qspi->mmap_enabled) + ti_qspi_disable_memory_map(spi); + list_for_each_entry(t, &m->transfers, transfer_list) { qspi->cmd |= QSPI_WLEN(t->bits_per_word); @@ -526,7 +615,9 @@ static int ti_qspi_probe(struct platform_device *pdev) ret = PTR_ERR(qspi->mmap_base); goto free_master; } + master->spi_mtd_mmap_read = ti_qspi_spi_mtd_mmap_read; } + qspi->mmap_enabled = false; qspi->fclk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(qspi->fclk)) {