From patchwork Wed Jun 1 04:50:28 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Ruehl X-Patchwork-Id: 9146065 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 9FBD560757 for ; Wed, 1 Jun 2016 05:04:20 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9801420499 for ; Wed, 1 Jun 2016 05:04:20 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8C73225819; Wed, 1 Jun 2016 05:04:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6967220499 for ; Wed, 1 Jun 2016 05:04:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750842AbcFAFER (ORCPT ); Wed, 1 Jun 2016 01:04:17 -0400 Received: from sh1.xit.com.hk ([111.91.236.50]:56611 "EHLO sh1.xit.com.hk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750697AbcFAFER (ORCPT ); Wed, 1 Jun 2016 01:04:17 -0400 X-Greylist: delayed 550 seconds by postgrey-1.27 at vger.kernel.org; Wed, 01 Jun 2016 01:04:16 EDT Received: from mail.gtsys.com.hk (ruehlchr-3-pt.tunnel.tserv25.sin1.ipv6.he.net [IPv6:2001:470:35:5f1::2]) by sh1.xit.com.hk (Postfix) with ESMTPSA id 350FD30066A5; Wed, 1 Jun 2016 12:55:02 +0800 (HKT) Received: from localhost (localhost [127.0.0.1]) by mail.gtsys.com.hk (Postfix) with ESMTP id 6293A200011C; Wed, 1 Jun 2016 12:50:58 +0800 (HKT) X-Virus-Scanned: Debian amavisd-new at gtsys.com.hk Received: from mail.gtsys.com.hk ([127.0.0.1]) by localhost (mail.gtsys.com.hk [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id AIFCXQ8WSteT; Wed, 1 Jun 2016 12:50:58 +0800 (HKT) Received: from s01.gtsys.com.hk (unknown [10.128.4.2]) by mail.gtsys.com.hk (Postfix) with ESMTP id 45675200011A; Wed, 1 Jun 2016 12:50:58 +0800 (HKT) Received: from armhfbuild.gtsys.com.hk (unknown [10.128.4.5]) by s01.gtsys.com.hk (Postfix) with ESMTP id 253FCC00BD8; Wed, 1 Jun 2016 12:50:58 +0800 (HKT) Received: by armhfbuild.gtsys.com.hk (Postfix, from userid 1000) id 417D244AC13; Wed, 1 Jun 2016 12:50:58 +0800 (HKT) From: Chris Ruehl To: linux-spi@vger.kernel.org, broonie@kernel.org Cc: festevam@gmail.com, anton.bondarenko.sama@gmail.com, kernel@pengutronix.de, Chris Ruehl Subject: [PATCH] spi-imx: imx6q add single burst transfer support Date: Wed, 1 Jun 2016 12:50:28 +0800 Message-Id: <1464756628-25463-1-git-send-email-chris.ruehl@gtsys.com.hk> X-Mailer: git-send-email 2.1.4 Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The patch add support for single burst transfer where chipselect will hold active until transfer completes with a limit to 2^7 words transferred. The single-burst-mode need set the burstlength in ECSPI_CONREG.BURST_LENGTH and clear the ecspi channel related ss_ctl flag in ECSPI_CONFIGREG.SS_CTL. The single-burst-mode is disabled by default. The activation from spidev is implemented by set bit0 of the xfer.speed_hz, which don't break anything in the mx51_ecspi_clkdiv() function. xfer[0].speed_hz = 2000000 | 0x1; /* enable single burst mode with 1hz */ There is a bug in the SDMA firmware (referenced as TKT238285 or ERR008517) which breaks the transmission with a odd numbers of bytes. Its turns out, that its safe to use with slaves in half duplex mode where always an even number of bytes is required. function added: spi_imx_buf_tx_sb() - singleburst transmit spi_imx_buf_rx_sb() - singleburst receive stucts modified: spi_imx_config - add len ; add single_burst_mode:1 spi_imx_data - add rxcount; add bits_per_word Signed-off-by: Chris Ruehl --- drivers/spi/spi-imx.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 231 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 50769078..15708eb 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -60,6 +60,8 @@ struct spi_imx_config { unsigned int speed_hz; unsigned int bpw; unsigned int mode; + unsigned int len; + unsigned int single_burst_mode:1; u8 cs; }; @@ -99,11 +101,13 @@ struct spi_imx_data { unsigned int bytes_per_word; unsigned int count; + unsigned int rxcount; void (*tx)(struct spi_imx_data *); void (*rx)(struct spi_imx_data *); void *rx_buf; const void *tx_buf; unsigned int txfifo; /* number of words pushed in tx FIFO */ + u8 bits_per_word; /* DMA */ bool usedma; @@ -168,6 +172,188 @@ MXC_SPI_BUF_TX(u16) MXC_SPI_BUF_RX(u32) MXC_SPI_BUF_TX(u32) +static void spi_imx_buf_rx_sb(struct spi_imx_data *spi_imx) +{ + unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA); + + dev_dbg(spi_imx->dev, "%s: rxcount: %u val:0x%08x\n", + __func__, spi_imx->rxcount, val); + + if (spi_imx->rxcount >= 4) { + if (spi_imx->rx_buf) { + if (spi_imx->bits_per_word == 32) { + spi_imx_buf_rx_u32(spi_imx); + } else if (spi_imx->bits_per_word == 16) { + *(u16 *)spi_imx->rx_buf = (val>>16); + spi_imx->rx_buf += sizeof(u16); + *(u16 *)spi_imx->rx_buf = val; + spi_imx->rx_buf += sizeof(u16); + } else { + *(u8 *)spi_imx->rx_buf = (val>>24); + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = (val>>16); + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = (val>>8); + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = val; + spi_imx->rx_buf += sizeof(u8); + } + } + spi_imx->rxcount -= 4; + } else if (spi_imx->rxcount == 3) { + if (spi_imx->rx_buf) { + if (spi_imx->bits_per_word == 32) { + *(u8 *)spi_imx->rx_buf = val>>24; + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = val>>16; + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = val>>8; + spi_imx->rx_buf += sizeof(u8); + } else if (spi_imx->bits_per_word == 16) { + *(u16 *)spi_imx->rx_buf = (val>>16); + spi_imx->rx_buf += sizeof(u16); + *(u8 *)spi_imx->rx_buf = val>>8; + spi_imx->rx_buf += sizeof(u8); + } else { + *(u8 *)spi_imx->rx_buf = (val>>16); + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = (val>>8); + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = val; + spi_imx->rx_buf += sizeof(u8); + } + } + spi_imx->rxcount -= 3; + } else if (spi_imx->rxcount == 2) { + if (spi_imx->rx_buf) { + if (spi_imx->bits_per_word == 32) { + *(u8 *)spi_imx->rx_buf = val>>24; + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = val>>16; + spi_imx->rx_buf += sizeof(u8); + } else if (spi_imx->bits_per_word == 16) { + spi_imx_buf_rx_u16(spi_imx); + } else { + *(u8 *)spi_imx->rx_buf = (val>>8); + spi_imx->rx_buf += sizeof(u8); + *(u8 *)spi_imx->rx_buf = val; + spi_imx->rx_buf += sizeof(u8); + } + } + spi_imx->rxcount -= 2; + } else if (spi_imx->rxcount == 1) { + if (spi_imx->rx_buf) { + if (spi_imx->bits_per_word == 32) + *(u8 *)spi_imx->rx_buf = val>>24; + else if (spi_imx->bits_per_word == 16) + *(u8 *)spi_imx->rx_buf = val>>8; + else + spi_imx_buf_rx_u8(spi_imx); + } + spi_imx->rxcount -= 1; + } +} + +static void spi_imx_buf_tx_sb(struct spi_imx_data *spi_imx) +{ + unsigned int val = 0; + + dev_dbg(spi_imx->dev, "%s: txcount: %u ptr:0x%08x\n", + __func__, spi_imx->count, *(u32 *)spi_imx->tx_buf); + + if (spi_imx->count >= 4) { + if (spi_imx->bits_per_word == 32) { + spi_imx_buf_tx_u32(spi_imx); + } else { + if (spi_imx->tx_buf) { + if (spi_imx->bits_per_word == 16) { + val = *(u16 *)spi_imx->tx_buf<<16; + spi_imx->tx_buf += sizeof(u16); + val |= *(u16 *)spi_imx->tx_buf; + spi_imx->tx_buf += sizeof(u16); + } else { + val = *(u8 *)spi_imx->tx_buf<<24; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf<<16; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf<<8; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf; + spi_imx->tx_buf += sizeof(u8); + } + writel(val, spi_imx->base + MXC_CSPITXDATA); + } + spi_imx->count -= 4; + } + } else if (spi_imx->count == 3) { + if (spi_imx->tx_buf) { + if (spi_imx->bits_per_word == 32) { + val = *(u8 *)spi_imx->tx_buf<<24; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf<<16; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf<<8; + spi_imx->tx_buf += sizeof(u8); + + } else if (spi_imx->bits_per_word == 16) { + val = *(u8 *)spi_imx->tx_buf<<24; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf<<16; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf; + spi_imx->tx_buf += sizeof(u8); + + } else { + val = *(u8 *)spi_imx->tx_buf<<16; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf<<8; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf; + spi_imx->tx_buf += sizeof(u8); + } + writel(val, spi_imx->base + MXC_CSPITXDATA); + } + spi_imx->count -= 3; + } else if (spi_imx->count == 2) { + if (spi_imx->bits_per_word == 16) { + spi_imx_buf_tx_u16(spi_imx); + } else { + if (spi_imx->tx_buf) { + if (spi_imx->bits_per_word == 32) { + val = *(u8 *)spi_imx->tx_buf<<24; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf<<16; + spi_imx->tx_buf += sizeof(u8); + } else { + val = *(u8 *)spi_imx->tx_buf<<8; + spi_imx->tx_buf += sizeof(u8); + val |= *(u8 *)spi_imx->tx_buf; + spi_imx->tx_buf += sizeof(u8); + } + writel(val, spi_imx->base + MXC_CSPITXDATA); + } + spi_imx->count -= 2; + } + } else if (spi_imx->count == 1) { + if (spi_imx->bits_per_word == 8) { + spi_imx_buf_tx_u8(spi_imx); + } else { + if (spi_imx->tx_buf) { + if (spi_imx->bits_per_word == 32) { + val = *(u8 *)spi_imx->tx_buf<<24; + spi_imx->tx_buf += sizeof(u8); + } else if (spi_imx->bits_per_word == 16) { + val = *(u8 *)spi_imx->tx_buf<<8; + spi_imx->tx_buf += sizeof(u8); + } + writel(val, spi_imx->base + MXC_CSPITXDATA); + } + spi_imx->count -= 1; + } + } +} + + /* First entry is reserved, second entry is valid only if SDHC_SPIEN is set * (which is currently not the case in this driver) */ @@ -357,9 +543,49 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, /* set chip select to use */ ctrl |= MX51_ECSPI_CTRL_CS(config->cs); - ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET; + /* set single else multible burst parameters */ + if (config->single_burst_mode > 0) { + reg = 0; + spi_imx->rx = spi_imx_buf_rx_sb; + spi_imx->tx = spi_imx_buf_tx_sb; + spi_imx->rxcount = config->len; + spi_imx->bits_per_word = config->bpw; + + /* + * calculate the Burst Length, + * refer to 21.7.3 Control Register (ECSPIx_CONREG) + * for details. + */ + switch (config->len%4) { + case 1: + ctrl |= 7 << MX51_ECSPI_CTRL_BL_OFFSET; + reg = (config->len-1) / 4; + break; + case 2: + ctrl |= 15 << MX51_ECSPI_CTRL_BL_OFFSET; + reg = (config->len-2) / 4; + break; + case 3: + ctrl |= 23 << MX51_ECSPI_CTRL_BL_OFFSET; + reg = (config->len-3) / 4; + break; + case 0: + ctrl |= 31 << MX51_ECSPI_CTRL_BL_OFFSET; + reg = (config->len-4) / 4; + break; + } + + if (reg > 0) + ctrl |= reg << (MX51_ECSPI_CTRL_BL_OFFSET + 5); - cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs); + cfg &= ~(MX51_ECSPI_CONFIG_SBBCTRL(config->cs)); + + dev_dbg(spi_imx->dev, "Single Burst reg:0x%08x cfg:0x%08x ctrl:0x%08x\n", + reg, cfg, ctrl); + } else { + ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET; + cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs); + } if (config->mode & SPI_CPHA) cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs); @@ -861,6 +1087,9 @@ static int spi_imx_setupxfer(struct spi_device *spi, config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; config.mode = spi->mode; config.cs = spi->chip_select; + config.len = t->len; + /* using bit0 of hz to enable the single burst mode */ + config.single_burst_mode = (config.speed_hz&0x1) > 0 ? true : false; if (!config.speed_hz) config.speed_hz = spi->max_speed_hz;