backport upstream spi-bcm63xx fixes
authorFlorian Fainelli <florian@openwrt.org>
Wed, 6 Jun 2012 13:31:33 +0000 (13:31 +0000)
committerFlorian Fainelli <florian@openwrt.org>
Wed, 6 Jun 2012 13:31:33 +0000 (13:31 +0000)
* message pump conversion
* not using stopping state
* setting spi driver mode bits

SVN-Revision: 32078

target/linux/brcm63xx/patches-3.3/013-spi-bcm63xx-convert-to-the-pump-message-infrastructu.patch [new file with mode: 0644]
target/linux/brcm63xx/patches-3.3/014-spi-bcm63xx-don-t-use-the-stopping-state.patch [new file with mode: 0644]
target/linux/brcm63xx/patches-3.3/015-spi-bcm63xx-set-master-driver-mode_bits.patch [new file with mode: 0644]

diff --git a/target/linux/brcm63xx/patches-3.3/013-spi-bcm63xx-convert-to-the-pump-message-infrastructu.patch b/target/linux/brcm63xx/patches-3.3/013-spi-bcm63xx-convert-to-the-pump-message-infrastructu.patch
new file mode 100644 (file)
index 0000000..d48225b
--- /dev/null
@@ -0,0 +1,289 @@
+From cde4384e1037c15e5dd04c68d19c75798b6281dd Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <florian@openwrt.org>
+Date: Fri, 20 Apr 2012 15:37:33 +0200
+Subject: [PATCH] spi/bcm63xx: convert to the pump message infrastructure
+
+This patch converts the bcm63xx SPI driver to use the SPI infrastructure
+pump message queue. Since we were previously sleeping in the SPI
+driver's transfer() function (which is not allowed) this is now fixed as well.
+
+To complete that conversion a certain number of changes have been made:
+- the transfer len is split into multiple hardware transfers in case its
+  size is bigger than the hardware FIFO size
+- the FIFO refill is no longer done in the interrupt context, which was a
+  bad idea leading to quick interrupt handler re-entrancy
+
+Tested-by: Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
+Signed-off-by: Florian Fainelli <florian@openwrt.org>
+Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
+---
+ drivers/spi/spi-bcm63xx.c |  149 +++++++++++++++++++++++++++------------------
+ 1 file changed, 89 insertions(+), 60 deletions(-)
+
+--- a/drivers/spi/spi-bcm63xx.c
++++ b/drivers/spi/spi-bcm63xx.c
+@@ -1,7 +1,7 @@
+ /*
+  * Broadcom BCM63xx SPI controller support
+  *
+- * Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org>
++ * Copyright (C) 2009-2012 Florian Fainelli <florian@openwrt.org>
+  * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
+  *
+  * This program is free software; you can redistribute it and/or
+@@ -30,6 +30,8 @@
+ #include <linux/spi/spi.h>
+ #include <linux/completion.h>
+ #include <linux/err.h>
++#include <linux/workqueue.h>
++#include <linux/pm_runtime.h>
+ #include <bcm63xx_dev_spi.h>
+@@ -96,17 +98,12 @@ static const unsigned bcm63xx_spi_freq_t
+       {   391000, SPI_CLK_0_391MHZ }
+ };
+-static int bcm63xx_spi_setup_transfer(struct spi_device *spi,
+-                                    struct spi_transfer *t)
++static int bcm63xx_spi_check_transfer(struct spi_device *spi,
++                                      struct spi_transfer *t)
+ {
+-      struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
+       u8 bits_per_word;
+-      u8 clk_cfg, reg;
+-      u32 hz;
+-      int i;
+       bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
+-      hz = (t) ? t->speed_hz : spi->max_speed_hz;
+       if (bits_per_word != 8) {
+               dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+                       __func__, bits_per_word);
+@@ -119,6 +116,19 @@ static int bcm63xx_spi_setup_transfer(st
+               return -EINVAL;
+       }
++      return 0;
++}
++
++static void bcm63xx_spi_setup_transfer(struct spi_device *spi,
++                                    struct spi_transfer *t)
++{
++      struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
++      u32 hz;
++      u8 clk_cfg, reg;
++      int i;
++
++      hz = (t) ? t->speed_hz : spi->max_speed_hz;
++
+       /* Find the closest clock configuration */
+       for (i = 0; i < SPI_CLK_MASK; i++) {
+               if (hz <= bcm63xx_spi_freq_table[i][0]) {
+@@ -139,8 +149,6 @@ static int bcm63xx_spi_setup_transfer(st
+       bcm_spi_writeb(bs, reg, SPI_CLK_CFG);
+       dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n",
+               clk_cfg, hz);
+-
+-      return 0;
+ }
+ /* the spi->mode bits understood by this driver: */
+@@ -165,7 +173,7 @@ static int bcm63xx_spi_setup(struct spi_
+               return -EINVAL;
+       }
+-      ret = bcm63xx_spi_setup_transfer(spi, NULL);
++      ret = bcm63xx_spi_check_transfer(spi, NULL);
+       if (ret < 0) {
+               dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
+                       spi->mode & ~MODEBITS);
+@@ -190,28 +198,29 @@ static void bcm63xx_spi_fill_tx_fifo(str
+       bs->remaining_bytes -= size;
+ }
+-static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
++static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi,
++                                      struct spi_transfer *t)
+ {
+       struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
+       u16 msg_ctl;
+       u16 cmd;
++      /* Disable the CMD_DONE interrupt */
++      bcm_spi_writeb(bs, 0, SPI_INT_MASK);
++
+       dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
+               t->tx_buf, t->rx_buf, t->len);
+       /* Transmitter is inhibited */
+       bs->tx_ptr = t->tx_buf;
+       bs->rx_ptr = t->rx_buf;
+-      init_completion(&bs->done);
+       if (t->tx_buf) {
+               bs->remaining_bytes = t->len;
+               bcm63xx_spi_fill_tx_fifo(bs);
+       }
+-      /* Enable the command done interrupt which
+-       * we use to determine completion of a command */
+-      bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
++      init_completion(&bs->done);
+       /* Fill in the Message control register */
+       msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
+@@ -230,33 +239,76 @@ static int bcm63xx_txrx_bufs(struct spi_
+       cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
+       cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
+       bcm_spi_writew(bs, cmd, SPI_CMD);
+-      wait_for_completion(&bs->done);
+-      /* Disable the CMD_DONE interrupt */
+-      bcm_spi_writeb(bs, 0, SPI_INT_MASK);
++      /* Enable the CMD_DONE interrupt */
++      bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
+       return t->len - bs->remaining_bytes;
+ }
+-static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m)
++static int bcm63xx_spi_prepare_transfer(struct spi_master *master)
+ {
+-      struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
+-      struct spi_transfer *t;
+-      int ret = 0;
++      struct bcm63xx_spi *bs = spi_master_get_devdata(master);
+-      if (unlikely(list_empty(&m->transfers)))
+-              return -EINVAL;
++      pm_runtime_get_sync(&bs->pdev->dev);
+-      if (bs->stopping)
+-              return -ESHUTDOWN;
++      return 0;
++}
++
++static int bcm63xx_spi_unprepare_transfer(struct spi_master *master)
++{
++      struct bcm63xx_spi *bs = spi_master_get_devdata(master);
++
++      pm_runtime_put(&bs->pdev->dev);
++
++      return 0;
++}
++
++static int bcm63xx_spi_transfer_one(struct spi_master *master,
++                                      struct spi_message *m)
++{
++      struct bcm63xx_spi *bs = spi_master_get_devdata(master);
++      struct spi_transfer *t;
++      struct spi_device *spi = m->spi;
++      int status = 0;
++      unsigned int timeout = 0;
+       list_for_each_entry(t, &m->transfers, transfer_list) {
+-              ret += bcm63xx_txrx_bufs(spi, t);
+-      }
++              unsigned int len = t->len;
++              u8 rx_tail;
+-      m->complete(m->context);
++              status = bcm63xx_spi_check_transfer(spi, t);
++              if (status < 0)
++                      goto exit;
++
++              /* configure adapter for a new transfer */
++              bcm63xx_spi_setup_transfer(spi, t);
++
++              while (len) {
++                      /* send the data */
++                      len -= bcm63xx_txrx_bufs(spi, t);
++
++                      timeout = wait_for_completion_timeout(&bs->done, HZ);
++                      if (!timeout) {
++                              status = -ETIMEDOUT;
++                              goto exit;
++                      }
++
++                      /* read out all data */
++                      rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
++
++                      /* Read out all the data */
++                      if (rx_tail)
++                              memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail);
++              }
++
++              m->actual_length += t->len;
++      }
++exit:
++      m->status = status;
++      spi_finalize_current_message(master);
+-      return ret;
++      return 0;
+ }
+ /* This driver supports single master mode only. Hence
+@@ -267,39 +319,15 @@ static irqreturn_t bcm63xx_spi_interrupt
+       struct spi_master *master = (struct spi_master *)dev_id;
+       struct bcm63xx_spi *bs = spi_master_get_devdata(master);
+       u8 intr;
+-      u16 cmd;
+       /* Read interupts and clear them immediately */
+       intr = bcm_spi_readb(bs, SPI_INT_STATUS);
+       bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
+       bcm_spi_writeb(bs, 0, SPI_INT_MASK);
+-      /* A tansfer completed */
+-      if (intr & SPI_INTR_CMD_DONE) {
+-              u8 rx_tail;
+-
+-              rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
+-
+-              /* Read out all the data */
+-              if (rx_tail)
+-                      memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail);
+-
+-              /* See if there is more data to send */
+-              if (bs->remaining_bytes > 0) {
+-                      bcm63xx_spi_fill_tx_fifo(bs);
+-
+-                      /* Start the transfer */
+-                      bcm_spi_writew(bs, SPI_HD_W << SPI_MSG_TYPE_SHIFT,
+-                                     SPI_MSG_CTL);
+-                      cmd = bcm_spi_readw(bs, SPI_CMD);
+-                      cmd |= SPI_CMD_START_IMMEDIATE;
+-                      cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
+-                      bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
+-                      bcm_spi_writew(bs, cmd, SPI_CMD);
+-              } else {
+-                      complete(&bs->done);
+-              }
+-      }
++      /* A transfer completed */
++      if (intr & SPI_INTR_CMD_DONE)
++              complete(&bs->done);
+       return IRQ_HANDLED;
+ }
+@@ -345,7 +373,6 @@ static int __devinit bcm63xx_spi_probe(s
+       }
+       bs = spi_master_get_devdata(master);
+-      init_completion(&bs->done);
+       platform_set_drvdata(pdev, master);
+       bs->pdev = pdev;
+@@ -379,7 +406,9 @@ static int __devinit bcm63xx_spi_probe(s
+       master->bus_num = pdata->bus_num;
+       master->num_chipselect = pdata->num_chipselect;
+       master->setup = bcm63xx_spi_setup;
+-      master->transfer = bcm63xx_transfer;
++      master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer;
++      master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer;
++      master->transfer_one_message = bcm63xx_spi_transfer_one;
+       bs->speed_hz = pdata->speed_hz;
+       bs->stopping = 0;
+       bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
diff --git a/target/linux/brcm63xx/patches-3.3/014-spi-bcm63xx-don-t-use-the-stopping-state.patch b/target/linux/brcm63xx/patches-3.3/014-spi-bcm63xx-don-t-use-the-stopping-state.patch
new file mode 100644 (file)
index 0000000..98bc909
--- /dev/null
@@ -0,0 +1,68 @@
+From 1e41dc0ee2f3807328db95e4f87ff1333245190f Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <florian@openwrt.org>
+Date: Fri, 20 Apr 2012 15:37:34 +0200
+Subject: [PATCH] spi/bcm63xx: don't use the stopping state
+
+We do not need to use a flag to indicate if the master driver is stopping
+it is sufficient to perform spi master unregistering in the platform
+driver's remove function.
+
+Signed-off-by: Florian Fainelli <florian@openwrt.org>
+Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
+---
+ drivers/spi/spi-bcm63xx.c |   13 ++-----------
+ 1 file changed, 2 insertions(+), 11 deletions(-)
+
+--- a/drivers/spi/spi-bcm63xx.c
++++ b/drivers/spi/spi-bcm63xx.c
+@@ -39,8 +39,6 @@
+ #define DRV_VER               "0.1.2"
+ struct bcm63xx_spi {
+-      spinlock_t              lock;
+-      int                     stopping;
+       struct completion       done;
+       void __iomem            *regs;
+@@ -161,9 +159,6 @@ static int bcm63xx_spi_setup(struct spi_
+       bs = spi_master_get_devdata(spi->master);
+-      if (bs->stopping)
+-              return -ESHUTDOWN;
+-
+       if (!spi->bits_per_word)
+               spi->bits_per_word = 8;
+@@ -410,10 +405,8 @@ static int __devinit bcm63xx_spi_probe(s
+       master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer;
+       master->transfer_one_message = bcm63xx_spi_transfer_one;
+       bs->speed_hz = pdata->speed_hz;
+-      bs->stopping = 0;
+       bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
+       bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA));
+-      spin_lock_init(&bs->lock);
+       /* Initialize hardware */
+       clk_enable(bs->clk);
+@@ -447,18 +440,16 @@ static int __devexit bcm63xx_spi_remove(
+       struct spi_master *master = platform_get_drvdata(pdev);
+       struct bcm63xx_spi *bs = spi_master_get_devdata(master);
++      spi_unregister_master(master);
++
+       /* reset spi block */
+       bcm_spi_writeb(bs, 0, SPI_INT_MASK);
+-      spin_lock(&bs->lock);
+-      bs->stopping = 1;
+       /* HW shutdown */
+       clk_disable(bs->clk);
+       clk_put(bs->clk);
+-      spin_unlock(&bs->lock);
+       platform_set_drvdata(pdev, 0);
+-      spi_unregister_master(master);
+       return 0;
+ }
diff --git a/target/linux/brcm63xx/patches-3.3/015-spi-bcm63xx-set-master-driver-mode_bits.patch b/target/linux/brcm63xx/patches-3.3/015-spi-bcm63xx-set-master-driver-mode_bits.patch
new file mode 100644 (file)
index 0000000..8b934d0
--- /dev/null
@@ -0,0 +1,24 @@
+From 88a3a255a510ed193bf0cc35424761c3c9247586 Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <florian@openwrt.org>
+Date: Fri, 20 Apr 2012 15:37:35 +0200
+Subject: [PATCH] spi/bcm63xx: set master driver mode_bits.
+
+We were not properly advertising the MODE bits supported by this driver, fix
+that.
+
+Signed-off-by: Florian Fainelli <florian@openwrt.org>
+Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
+---
+ drivers/spi/spi-bcm63xx.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/spi/spi-bcm63xx.c
++++ b/drivers/spi/spi-bcm63xx.c
+@@ -404,6 +404,7 @@ static int __devinit bcm63xx_spi_probe(s
+       master->prepare_transfer_hardware = bcm63xx_spi_prepare_transfer;
+       master->unprepare_transfer_hardware = bcm63xx_spi_unprepare_transfer;
+       master->transfer_one_message = bcm63xx_spi_transfer_one;
++      master->mode_bits = MODEBITS;
+       bs->speed_hz = pdata->speed_hz;
+       bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
+       bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA));