From patchwork Fri Oct 16 14:17:19 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Sperl X-Patchwork-Id: 7415811 Return-Path: X-Original-To: patchwork-linux-arm@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 744909F1D5 for ; Fri, 16 Oct 2015 14:19:46 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 7F6BD20A5E for ; Fri, 16 Oct 2015 14:19:45 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 7520820A5C for ; Fri, 16 Oct 2015 14:19:44 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Zn5pf-0005om-Tg; Fri, 16 Oct 2015 14:18:07 +0000 Received: from 212-186-180-163.dynamic.surfer.at ([212.186.180.163] helo=cgate.sperl.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Zn5pV-0005Lc-Cr; Fri, 16 Oct 2015 14:18:00 +0000 Received: from raspcm.intern.sperl.org (account martin@sperl.org [10.10.10.41] verified) by sperl.org (CommuniGate Pro SMTP 6.1.2) with ESMTPSA id 6344424; Fri, 16 Oct 2015 14:17:32 +0000 From: kernel@martin.sperl.org To: Mark Brown , Stephen Warren , Lee Jones , Eric Anholt , linux-spi@vger.kernel.org, linux-rpi-kernel@lists.infradead.org, linux-arm-kernel@lists.infradead.org, Georgii Staroselskii Subject: [PATCH] spi: bcm2835aux: fixed bad data on longer transfers Date: Fri, 16 Oct 2015 14:17:19 +0000 Message-Id: <1445005040-7230-1-git-send-email-kernel@martin.sperl.org> X-Mailer: git-send-email 1.7.10.4 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151016_071757_980511_B7205B2A X-CRM114-Status: GOOD ( 13.14 ) X-Spam-Score: -0.9 (/) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Martin Sperl MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_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 From: Martin Sperl There are strange issues with the auxiliary spi device that result in "lost" data in the RX path if the fifo is filled by too much (even though the status register is checked if new data can get filled in). This has been observed primarily for the interrupt case. Polling works fine, probably because the RX fifo is pulled immediately when in the tight polling loop. For that reason we have to limit the pending bytes to less than 15 when filling the fifo in interrupt mode. There also was an issue returning the "wrong" last 1/2 bytes of a transfer when the transfer is not a multiple of 3 bytes. (this impacted polling and interrupt modes) Also fixed an overflow in the estimation of the transfer time used to decide if we run in interrupt or polling mode (found with the spi-bcm2835.c driver originally). Reported-by: Georgii Staroselskii Signed-off-by: Martin Sperl --- drivers/spi/spi-bcm2835aux.c | 43 ++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index 1a1b58c..7de6f84 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -104,6 +104,7 @@ struct bcm2835aux_spi { u8 *rx_buf; int tx_len; int rx_len; + int pending; }; static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg) @@ -120,15 +121,27 @@ static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned reg, static inline void bcm2835aux_rd_fifo(struct bcm2835aux_spi *bs) { u32 data; - int i; int count = min(bs->rx_len, 3); data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO); if (bs->rx_buf) { - for (i = 0; i < count; i++) - *bs->rx_buf++ = (data >> (8 * (2 - i))) & 0xff; + switch (count) { + case 4: + *bs->rx_buf++ = (data >> 24) & 0xff; + /* fallthrough */ + case 3: + *bs->rx_buf++ = (data >> 16) & 0xff; + /* fallthrough */ + case 2: + *bs->rx_buf++ = (data >> 8) & 0xff; + /* fallthrough */ + case 1: + *bs->rx_buf++ = (data >> 0) & 0xff; + /* fallthrough - no default */ + } } bs->rx_len -= count; + bs->pending -= count; } static inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs) @@ -151,6 +164,7 @@ static inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs) /* and decrement length */ bs->tx_len -= count; + bs->pending += count; /* write to the correct TX-register */ if (bs->tx_len) @@ -183,6 +197,7 @@ static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id) /* check if we have data to write */ while (bs->tx_len && + (bs->pending < 12) && (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & BCM2835_AUX_SPI_STAT_TX_FULL))) { bcm2835aux_wr_fifo(bs); @@ -234,6 +249,7 @@ static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master, /* fill in tx fifo with data before enabling interrupts */ while ((bs->tx_len) && + (bs->pending < 12) && (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & BCM2835_AUX_SPI_STAT_TX_FULL))) { bcm2835aux_wr_fifo(bs); @@ -245,8 +261,7 @@ static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master, static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master, struct spi_device *spi, - struct spi_transfer *tfr, - unsigned long xfer_time_us) + struct spi_transfer *tfr) { struct bcm2835aux_spi *bs = spi_master_get_devdata(master); unsigned long timeout; @@ -305,7 +320,8 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master, { struct bcm2835aux_spi *bs = spi_master_get_devdata(master); unsigned long spi_hz, clk_hz, speed; - unsigned long spi_used_hz, xfer_time_us; + unsigned long spi_used_hz; + unsigned long long xfer_time_us; /* calculate the registers to handle * @@ -348,16 +364,19 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master, bs->rx_buf = tfr->rx_buf; bs->tx_len = tfr->len; bs->rx_len = tfr->len; + bs->pending = 0; - /* calculate the estimated time in us the transfer runs */ - xfer_time_us = tfr->len - * 9 /* clocks/byte - SPI-HW waits 1 clock after each byte */ - * 1000000 / spi_used_hz; + /* calculate the estimated time in us the transfer runs + * note that there are are 2 idle clocks after each + * chunk getting transferred - in our case the chunk size + * is 3 bytes, so we approximate this by 9 bits/byte + */ + xfer_time_us = tfr->len * 9 * 1000000; + do_div(xfer_time_us, spi_used_hz); /* run in polling mode for short transfers */ if (xfer_time_us < BCM2835_AUX_SPI_POLLING_LIMIT_US) - return bcm2835aux_spi_transfer_one_poll(master, spi, tfr, - xfer_time_us); + return bcm2835aux_spi_transfer_one_poll(master, spi, tfr); /* run in interrupt mode for all others */ return bcm2835aux_spi_transfer_one_irq(master, spi, tfr);