bcm63xx: update patches to latest upstream versions
[openwrt.git] / target / linux / brcm63xx / patches-3.3 / 419-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch
index 44e6958..739c2c6 100644 (file)
-From 2982127b8a0127667cb5354e03987cd3baa84b8c Mon Sep 17 00:00:00 2001
+From 4b27423676485d05bcd6fc6f3809164fb8f9d22d Mon Sep 17 00:00:00 2001
 From: Jonas Gorski <jonas.gorski@gmail.com>
 Date: Sat, 12 Nov 2011 12:19:55 +0100
-Subject: [PATCH 54/79] SPI: MIPS: BCM63XX: Add HS SPI driver
+Subject: [PATCH 30/60] SPI: MIPS: BCM63XX: Add HSSPI driver
 
 Add a driver for the High Speed SPI controller found on newer BCM63XX SoCs.
 
 Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 ---
- arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h   |   18 +
- .../include/asm/mach-bcm63xx/bcm63xx_dev_hsspi.h   |    3 +
- arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h  |   47 ++
+ .../include/asm/mach-bcm63xx/bcm63xx_dev_hsspi.h   |    2 +
  drivers/spi/Kconfig                                |    7 +
  drivers/spi/Makefile                               |    1 +
- drivers/spi/spi-bcm63xx-hsspi.c                    |  502 ++++++++++++++++++++
6 files changed, 578 insertions(+)
+ drivers/spi/spi-bcm63xx-hsspi.c                    |  427 ++++++++++++++++++++
4 files changed, 437 insertions(+), 0 deletions(-)
  create mode 100644 drivers/spi/spi-bcm63xx-hsspi.c
 
---- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
-+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
-@@ -116,6 +116,7 @@ enum bcm63xx_regs_set {
-       RSET_UART1,
-       RSET_GPIO,
-       RSET_SPI,
-+      RSET_HSSPI,
-       RSET_UDC0,
-       RSET_OHCI0,
-       RSET_OHCI_PRIV,
-@@ -161,6 +162,7 @@ enum bcm63xx_regs_set {
- #define RSET_ENETDMAS_SIZE(chans)     (16 * (chans))
- #define RSET_ENETSW_SIZE              65536
- #define RSET_UART_SIZE                        24
-+#define RSET_HSSPI_SIZE                       2048
- #define RSET_UDC_SIZE                 256
- #define RSET_OHCI_SIZE                        256
- #define RSET_EHCI_SIZE                        256
-@@ -184,6 +186,7 @@ enum bcm63xx_regs_set {
- #define BCM_6328_UART1_BASE           (0xb0000120)
- #define BCM_6328_GPIO_BASE            (0xb0000080)
- #define BCM_6328_SPI_BASE             (0xdeadbeef)
-+#define BCM_6328_HSSPI_BASE           (0xb0001000)
- #define BCM_6328_UDC0_BASE            (0xdeadbeef)
- #define BCM_6328_USBDMA_BASE          (0xdeadbeef)
- #define BCM_6328_OHCI0_BASE           (0xdeadbeef)
-@@ -229,6 +232,7 @@ enum bcm63xx_regs_set {
- #define BCM_6338_UART1_BASE           (0xdeadbeef)
- #define BCM_6338_GPIO_BASE            (0xfffe0400)
- #define BCM_6338_SPI_BASE             (0xfffe0c00)
-+#define BCM_6338_HSSPI_BASE           (0xdeadbeef)
- #define BCM_6338_UDC0_BASE            (0xdeadbeef)
- #define BCM_6338_USBDMA_BASE          (0xfffe2400)
- #define BCM_6338_OHCI0_BASE           (0xdeadbeef)
-@@ -275,6 +279,7 @@ enum bcm63xx_regs_set {
- #define BCM_6345_UART1_BASE           (0xdeadbeef)
- #define BCM_6345_GPIO_BASE            (0xfffe0400)
- #define BCM_6345_SPI_BASE             (0xdeadbeef)
-+#define BCM_6345_HSSPI_BASE           (0xdeadbeef)
- #define BCM_6345_UDC0_BASE            (0xdeadbeef)
- #define BCM_6345_USBDMA_BASE          (0xfffe2800)
- #define BCM_6345_ENET0_BASE           (0xfffe1800)
-@@ -320,6 +325,7 @@ enum bcm63xx_regs_set {
- #define BCM_6348_UART1_BASE           (0xdeadbeef)
- #define BCM_6348_GPIO_BASE            (0xfffe0400)
- #define BCM_6348_SPI_BASE             (0xfffe0c00)
-+#define BCM_6348_HSSPI_BASE           (0xdeadbeef)
- #define BCM_6348_UDC0_BASE            (0xfffe1000)
- #define BCM_6348_OHCI0_BASE           (0xfffe1b00)
- #define BCM_6348_OHCI_PRIV_BASE               (0xfffe1c00)
-@@ -363,6 +369,7 @@ enum bcm63xx_regs_set {
- #define BCM_6358_UART1_BASE           (0xfffe0120)
- #define BCM_6358_GPIO_BASE            (0xfffe0080)
- #define BCM_6358_SPI_BASE             (0xfffe0800)
-+#define BCM_6358_HSSPI_BASE           (0xdeadbeef)
- #define BCM_6358_UDC0_BASE            (0xfffe0800)
- #define BCM_6358_OHCI0_BASE           (0xfffe1400)
- #define BCM_6358_OHCI_PRIV_BASE               (0xdeadbeef)
-@@ -407,6 +414,7 @@ enum bcm63xx_regs_set {
- #define BCM_6368_UART1_BASE           (0xb0000120)
- #define BCM_6368_GPIO_BASE            (0xb0000080)
- #define BCM_6368_SPI_BASE             (0xb0000800)
-+#define BCM_6368_HSSPI_BASE           (0xdeadbeef)
- #define BCM_6368_UDC0_BASE            (0xdeadbeef)
- #define BCM_6368_OHCI0_BASE           (0xb0001600)
- #define BCM_6368_OHCI_PRIV_BASE               (0xdeadbeef)
-@@ -456,6 +464,7 @@ extern const unsigned long *bcm63xx_regs
-       __GEN_RSET_BASE(__cpu, UART1)                                   \
-       __GEN_RSET_BASE(__cpu, GPIO)                                    \
-       __GEN_RSET_BASE(__cpu, SPI)                                     \
-+      __GEN_RSET_BASE(__cpu, HSSPI)                                   \
-       __GEN_RSET_BASE(__cpu, UDC0)                                    \
-       __GEN_RSET_BASE(__cpu, OHCI0)                                   \
-       __GEN_RSET_BASE(__cpu, OHCI_PRIV)                               \
-@@ -497,6 +506,7 @@ extern const unsigned long *bcm63xx_regs
-       [RSET_UART1]            = BCM_## __cpu ##_UART1_BASE,           \
-       [RSET_GPIO]             = BCM_## __cpu ##_GPIO_BASE,            \
-       [RSET_SPI]              = BCM_## __cpu ##_SPI_BASE,             \
-+      [RSET_HSSPI]            = BCM_## __cpu ##_HSSPI_BASE,           \
-       [RSET_UDC0]             = BCM_## __cpu ##_UDC0_BASE,            \
-       [RSET_OHCI0]            = BCM_## __cpu ##_OHCI0_BASE,           \
-       [RSET_OHCI_PRIV]        = BCM_## __cpu ##_OHCI_PRIV_BASE,       \
-@@ -569,6 +579,7 @@ enum bcm63xx_irq {
-       IRQ_ENET0,
-       IRQ_ENET1,
-       IRQ_ENET_PHY,
-+      IRQ_HSSPI,
-       IRQ_OHCI0,
-       IRQ_EHCI0,
-       IRQ_ENET0_RXDMA,
-@@ -604,6 +615,7 @@ enum bcm63xx_irq {
- #define BCM_6328_ENET0_IRQ            0
- #define BCM_6328_ENET1_IRQ            0
- #define BCM_6328_ENET_PHY_IRQ         (IRQ_INTERNAL_BASE + 12)
-+#define BCM_6328_HSSPI_IRQ            (IRQ_INTERNAL_BASE + 29)
- #define BCM_6328_OHCI0_IRQ            (IRQ_INTERNAL_BASE + 9)
- #define BCM_6328_EHCI0_IRQ            (IRQ_INTERNAL_BASE + 10)
- #define BCM_6328_PCMCIA_IRQ           0
-@@ -642,6 +654,7 @@ enum bcm63xx_irq {
- #define BCM_6338_ENET0_IRQ            (IRQ_INTERNAL_BASE + 8)
- #define BCM_6338_ENET1_IRQ            0
- #define BCM_6338_ENET_PHY_IRQ         (IRQ_INTERNAL_BASE + 9)
-+#define BCM_6338_HSSPI_IRQ            0
- #define BCM_6338_OHCI0_IRQ            0
- #define BCM_6338_EHCI0_IRQ            0
- #define BCM_6338_ENET0_RXDMA_IRQ      (IRQ_INTERNAL_BASE + 15)
-@@ -673,6 +686,7 @@ enum bcm63xx_irq {
- #define BCM_6345_ENET0_IRQ            (IRQ_INTERNAL_BASE + 8)
- #define BCM_6345_ENET1_IRQ            0
- #define BCM_6345_ENET_PHY_IRQ         (IRQ_INTERNAL_BASE + 12)
-+#define BCM_6345_HSSPI_IRQ            0
- #define BCM_6345_OHCI0_IRQ            0
- #define BCM_6345_EHCI0_IRQ            0
- #define BCM_6345_ENET0_RXDMA_IRQ      (IRQ_INTERNAL_BASE + 13 + 1)
-@@ -704,6 +718,7 @@ enum bcm63xx_irq {
- #define BCM_6348_ENET0_IRQ            (IRQ_INTERNAL_BASE + 8)
- #define BCM_6348_ENET1_IRQ            (IRQ_INTERNAL_BASE + 7)
- #define BCM_6348_ENET_PHY_IRQ         (IRQ_INTERNAL_BASE + 9)
-+#define BCM_6348_HSSPI_IRQ            0
- #define BCM_6348_OHCI0_IRQ            (IRQ_INTERNAL_BASE + 12)
- #define BCM_6348_EHCI0_IRQ            0
- #define BCM_6348_ENET0_RXDMA_IRQ      (IRQ_INTERNAL_BASE + 20)
-@@ -735,6 +750,7 @@ enum bcm63xx_irq {
- #define BCM_6358_ENET0_IRQ            (IRQ_INTERNAL_BASE + 8)
- #define BCM_6358_ENET1_IRQ            (IRQ_INTERNAL_BASE + 6)
- #define BCM_6358_ENET_PHY_IRQ         (IRQ_INTERNAL_BASE + 9)
-+#define BCM_6358_HSSPI_IRQ            0
- #define BCM_6358_OHCI0_IRQ            (IRQ_INTERNAL_BASE + 5)
- #define BCM_6358_EHCI0_IRQ            (IRQ_INTERNAL_BASE + 10)
- #define BCM_6358_ENET0_RXDMA_IRQ      (IRQ_INTERNAL_BASE + 15)
-@@ -775,6 +791,7 @@ enum bcm63xx_irq {
- #define BCM_6368_ENET0_IRQ            0
- #define BCM_6368_ENET1_IRQ            0
- #define BCM_6368_ENET_PHY_IRQ         (IRQ_INTERNAL_BASE + 15)
-+#define BCM_6368_HSSPI_IRQ            0
- #define BCM_6368_OHCI0_IRQ            (IRQ_INTERNAL_BASE + 5)
- #define BCM_6368_EHCI0_IRQ            (IRQ_INTERNAL_BASE + 7)
- #define BCM_6368_PCMCIA_IRQ           0
-@@ -815,6 +832,7 @@ extern const int *bcm63xx_irqs;
-       [IRQ_ENET0]             = BCM_## __cpu ##_ENET0_IRQ,            \
-       [IRQ_ENET1]             = BCM_## __cpu ##_ENET1_IRQ,            \
-       [IRQ_ENET_PHY]          = BCM_## __cpu ##_ENET_PHY_IRQ,         \
-+      [IRQ_HSSPI]             = BCM_## __cpu ##_HSSPI_IRQ,            \
-       [IRQ_OHCI0]             = BCM_## __cpu ##_OHCI0_IRQ,            \
-       [IRQ_EHCI0]             = BCM_## __cpu ##_EHCI0_IRQ,            \
-       [IRQ_ENET0_RXDMA]       = BCM_## __cpu ##_ENET0_RXDMA_IRQ,      \
 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_hsspi.h
 +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_hsspi.h
-@@ -23,4 +23,7 @@ struct bcm63xx_hsspi_pdata {
- #define HSSPI_OP_WRITE                (2 << HSSPI_OP_CODE_SHIFT)
- #define HSSPI_OP_READ         (3 << HSSPI_OP_CODE_SHIFT)
+@@ -17,4 +17,6 @@ struct bcm63xx_hsspi_pdata {
  
-+#define HS_SPI_CLOCK_DEF    40000000
-+#define HS_SPI_BUFFER_LEN   512
-+
- #endif /* BCM63XX_DEV_HSSPI_H */
---- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
-+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
-@@ -1283,4 +1283,51 @@
- #define PCIE_DEVICE_OFFSET            0x8000
+ #define HSSPI_PLL_HZ_6328     133333333
  
-+/*************************************************************************
-+ * _REG relative to RSET_HSSPI
-+ *************************************************************************/
-+
-+#define HSSPI_GLOBAL_CTRL_REG                 0x0
-+#define GLOBAL_CTRL_CLK_POLARITY              BIT(17)
-+#define GLOBAL_CTRL_CLK_GATE_SSOFF            BIT(16)
-+
-+#define HSSPI_GLOBAL_EXT_TRIGGER_REG          0x4
-+
-+#define HSSPI_INT_STATUS_REG                  0x8
-+#define HSSPI_INT_STATUS_MASKED_REG           0xc
-+#define HSSPI_INT_MASK_REG                    0x10
-+
-+#define HSSPI_PING0_CMD_DONE                  BIT(0)
-+
-+#define HSSPI_INT_CLEAR_ALL                   0xff001f1f
-+
-+#define HSSPI_PINGPONG_COMMAND_REG(x)         (0x80 + (x) * 0x40)
-+#define PINGPONG_CMD_COMMAND_MASK             0xf
-+#define PINGPONG_COMMAND_NOOP                 0
-+#define PINGPONG_COMMAND_START_NOW            1
-+#define PINGPONG_COMMAND_START_TRIGGER                2
-+#define PINGPONG_COMMAND_HALT                 3
-+#define PINGPONG_COMMAND_FLUSH                        4
-+#define PINGPONG_CMD_PROFILE_SHIFT            8
-+#define PINGPONG_CMD_SS_SHIFT                 12
++#define HSSPI_BUFFER_LEN      512
 +
-+#define HSSPI_PINGPONG_STATUS_REG(x)          (0x84 + (x) * 0x40)
-+
-+#define HSSPI_PROFILE_CLK_CTRL_REG(x)         (0x100 + (x) * 0x20)
-+#define CLK_CTRL_ACCUM_RST_ON_LOOP            BIT(15)
-+
-+#define HSSPI_PROFILE_SIGNAL_CTRL_REG(x)      (0x104 + (x) * 0x20)
-+#define SIGNAL_CTRL_LATCH_RISING              BIT(12)
-+#define SIGNAL_CTRL_LAUNCH_RISING             BIT(13)
-+#define SIGNAL_CTRL_ASYNC_INPUT_PATH          BIT(16)
-+
-+#define HSSPI_PROFILE_MODE_CTRL_REG(x)                (0x108 + (x) * 0x20)
-+#define MODE_CTRL_MULTIDATA_RD_STRT_SHIFT     8
-+#define MODE_CTRL_MULTIDATA_WR_STRT_SHIFT     12
-+#define MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT     16
-+#define MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT     18
-+#define MODE_CTRL_PREPENDBYTE_CNT_SHIFT               24
-+
-+#define HSSPI_FIFO_REG(x)                     (0x200 + (x) * 0x200)
-+
- #endif /* BCM63XX_REGS_H_ */
+ #endif /* BCM63XX_DEV_HSSPI_H */
 --- a/drivers/spi/Kconfig
 +++ b/drivers/spi/Kconfig
 @@ -100,6 +100,13 @@ config SPI_BCM63XX
@@ -254,18 +51,19 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
  obj-$(CONFIG_SPI_BITBANG)             += spi-bitbang.o
 --- /dev/null
 +++ b/drivers/spi/spi-bcm63xx-hsspi.c
-@@ -0,0 +1,502 @@
+@@ -0,0 +1,427 @@
 +/*
 + * Broadcom BCM63XX High Speed SPI Controller driver
 + *
 + * Copyright 2000-2010 Broadcom Corporation
-+ * Copyright 2011 Jonas Gorski <jonas.gorski@gmail.com>
++ * Copyright 2012 Jonas Gorski <jonas.gorski@gmail.com>
 + *
 + * Licensed under the GNU/GPL. See COPYING for details.
 + */
 +
 +#include <linux/kernel.h>
 +#include <linux/init.h>
++#include <linux/io.h>
 +#include <linux/clk.h>
 +#include <linux/module.h>
 +#include <linux/platform_device.h>
@@ -279,49 +77,44 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +#include <bcm63xx_regs.h>
 +#include <bcm63xx_dev_hsspi.h>
 +
++#define HSSPI_OP_CODE_SHIFT   13
++#define HSSPI_OP_SLEEP                (0 << HSSPI_OP_CODE_SHIFT)
++#define HSSPI_OP_READ_WRITE   (1 << HSSPI_OP_CODE_SHIFT)
++#define HSSPI_OP_WRITE                (2 << HSSPI_OP_CODE_SHIFT)
++#define HSSPI_OP_READ         (3 << HSSPI_OP_CODE_SHIFT)
 +
-+#define PFX           KBUILD_MODNAME
++#define HSSPI_MAX_PREPEND_LEN 15
 +
-+struct bcm63xx_hsspi {
-+      spinlock_t              lock;
-+      int                     irq;
-+      u8                      stopping;
++#define HSSPI_MAX_SYNC_CLOCK  30000000
 +
-+      struct list_head        queue;
-+      struct workqueue_struct *workqueue;
-+      struct work_struct      ws;
++struct bcm63xx_hsspi {
 +      struct completion       done;
-+
 +      struct spi_transfer     *curr_trans;
 +
 +      struct platform_device  *pdev;
-+      void __iomem            *regs;
 +      struct clk              *clk;
-+
-+      /* Platform data */
-+      u32                     speed_hz;
-+
-+      /* data iomem */
++      void __iomem            *regs;
 +      u8 __iomem              *fifo;
 +
-+
++      u32                     speed_hz;
++      int                     irq;
 +};
 +
 +static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, int hz,
 +                                int profile)
 +{
-+      int clock;
-+
-+      clock = bs->speed_hz / hz;
-+      if (bs->speed_hz % HS_SPI_CLOCK_DEF)
-+              clock++;
-+
-+      clock = 2048 / clock;
-+      if (2048 % clock)
-+              clock++;
++      u32 reg;
 +
-+      bcm_hsspi_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | clock,
++      reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz));
++      bcm_hsspi_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | reg,
 +                       HSSPI_PROFILE_CLK_CTRL_REG(profile));
++
++      reg = bcm_hsspi_readl(HSSPI_PROFILE_SIGNAL_CTRL_REG(profile));
++      if (hz > HSSPI_MAX_SYNC_CLOCK)
++              reg |= SIGNAL_CTRL_ASYNC_INPUT_PATH;
++      else
++              reg &= ~SIGNAL_CTRL_ASYNC_INPUT_PATH;
++      bcm_hsspi_writel(reg, HSSPI_PROFILE_SIGNAL_CTRL_REG(profile));
 +}
 +
 +static int bcm63xx_hsspi_do_txrx(struct spi_device *spi,
@@ -331,20 +124,19 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +      struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
 +      u8 chip_select = spi->chip_select;
 +      u16 opcode = 0;
-+      int prepend_size = 0;
++      int len, prepend_size = 0;
 +
 +      init_completion(&bs->done);
++
 +      bs->curr_trans = t2 ? t2 : t1;
 +      bcm63xx_hsspi_set_clk(bs, bs->curr_trans->speed_hz, chip_select);
 +
-+      BUG_ON(t2 && !t1->tx_buf && t1->rx_buf && t2->tx_buf && !t2->rx_buf);
-+
 +      if (t2 && !t2->tx_buf)
 +              prepend_size = t1->len;
 +
-+      bcm_hsspi_writel(prepend_size<<MODE_CTRL_PREPENDBYTE_CNT_SHIFT |
-+                       2<<MODE_CTRL_MULTIDATA_WR_STRT_SHIFT |
-+                       2<<MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff,
++      bcm_hsspi_writel(prepend_size << MODE_CTRL_PREPENDBYTE_CNT_SHIFT |
++                       2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT |
++                       2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff,
 +                       HSSPI_PROFILE_MODE_CTRL_REG(chip_select));
 +
 +      if (t1->rx_buf && t1->tx_buf)
@@ -354,22 +146,21 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +      else if (t1->tx_buf)
 +              opcode = HSSPI_OP_WRITE;
 +
-+      BUG_ON(opcode == 0);
-+
 +      if (opcode == HSSPI_OP_READ && t2)
-+              opcode |= t2->len;
++              len = t2->len;
 +      else
-+              opcode |= t1->len;
++              len = t1->len;
 +
 +      if (t1->tx_buf) {
 +              memcpy_toio(bs->fifo + 2, t1->tx_buf, t1->len);
 +              if (t2 && t2->tx_buf) {
 +                      memcpy_toio(bs->fifo + 2 + t1->len,
 +                                  t2->tx_buf, t2->len);
-+                      opcode += t2->len;
++                      len += t2->len;
 +              }
 +      }
 +
++      opcode |= len;
 +      memcpy_toio(bs->fifo, &opcode, sizeof(opcode));
 +
 +      /* enable interrupt */
@@ -381,20 +172,17 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +                       PINGPONG_COMMAND_START_NOW,
 +                       HSSPI_PINGPONG_COMMAND_REG(0));
 +
-+      wait_for_completion(&bs->done);
++      if (wait_for_completion_timeout(&bs->done, HZ) == 0) {
++              dev_err(&bs->pdev->dev, "transfer timed out!\n");
++              return -ETIMEDOUT;
++      }
++
 +      return t1->len + (t2 ? t2->len : 0);
 +}
++
 +static int bcm63xx_hsspi_setup(struct spi_device *spi)
 +{
-+      struct bcm63xx_hsspi *bs;
 +      u32 reg;
-+      bs = spi_master_get_devdata(spi->master);
-+
-+      if (bs->stopping)
-+              return -ESHUTDOWN;
-+
-+      if (!spi->bits_per_word)
-+              spi->bits_per_word = 8;
 +
 +      if (spi->bits_per_word != 8)
 +              return -EINVAL;
@@ -404,133 +192,109 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +
 +      reg = bcm_hsspi_readl(HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select));
 +      reg &= ~(SIGNAL_CTRL_LAUNCH_RISING | SIGNAL_CTRL_LATCH_RISING);
-+
 +      if (spi->mode & SPI_CPHA)
 +              reg |= SIGNAL_CTRL_LAUNCH_RISING;
 +      else
 +              reg |= SIGNAL_CTRL_LATCH_RISING;
-+
 +      bcm_hsspi_writel(reg, HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select));
 +
 +      return 0;
 +}
 +
-+
-+static int bcm63xx_hsspi_transfer(struct spi_device *spi,
-+                                struct spi_message *msg)
++static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
++                                    struct spi_message *msg)
 +{
-+      struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
 +      struct spi_transfer *t, *prev = NULL;
++      struct spi_device *spi = msg->spi;
++      u32 reg;
++      int ret = -EINVAL;
++      int len = 0;
 +
-+      if (unlikely(list_empty(&msg->transfers)))
-+              return -EINVAL;
-+
-+      if (bs->stopping)
-+              return -ESHUTDOWN;
-+
-+      list_for_each_entry(t, &msg->transfers, transfer_list)  {
-+              /* check transfer parameters */
++      /* check if we are able to make these transfers */
++      list_for_each_entry(t, &msg->transfers, transfer_list) {
 +              if (!t->tx_buf && !t->rx_buf)
-+                      return -EINVAL;
++                      goto out;
 +
 +              if (t->speed_hz == 0)
 +                      t->speed_hz = spi->max_speed_hz;
 +
 +              if (t->speed_hz > spi->max_speed_hz)
-+                      return -EINVAL;
++                      goto out;
 +
-+              if (t->len > HS_SPI_BUFFER_LEN)
-+                      return -EINVAL;
++              if (t->len > HSSPI_BUFFER_LEN)
++                      goto out;
 +
-+              /* reject if we have to combine two tx transfers and their
-+               * combined length is bigger than the buffer
++              /*
++               * This controller does not support keeping the chip select
++               * active between transfers.
++               * This logic currently supports combining:
++               *  write then read with no cs_change (e.g. m25p80 RDSR)
++               *  write then write with no cs_change (e.g. m25p80 PP)
 +               */
-+              if (prev && !prev->cs_change && !t->cs_change && prev->tx_buf &&
-+                  t->tx_buf && (prev->len + t->len) > HS_SPI_BUFFER_LEN)
-+                      return -EINVAL;
-+
-+              prev = t;
-+      }
-+
-+
-+      msg->actual_length = 0;
-+
-+#if 0
-+      /* disable interrupts for the SPI controller
-+         using spin_lock_irqsave would disable all interrupts */
-+      bcm_hsspi_writel(0, HSSPI_INT_MASK_REG);
-+#endif
-+      spin_lock(&bs->lock);
-+      list_add_tail(&msg->queue, &bs->queue);
-+      queue_work(bs->workqueue, &bs->ws);
-+      spin_unlock(&bs->lock);
-+
-+#if 0
-+      bcm_hsspi_writel(HSSPI_PING0_CMD_DONE, HSSPI_INT_MASK_REG);
-+#endif
-+      return 0;
-+}
-+
-+static void bcm63xx_hsspi_do_work(struct work_struct *work)
-+{
-+      struct bcm63xx_hsspi *bs = container_of(work, struct bcm63xx_hsspi,
-+                                              ws);
-+      struct spi_message *msg;
-+      struct spi_transfer *prev = NULL;
-+      struct spi_transfer *t;
-+      u32 reg;
-+
-+      int len = 0;
-+
-+      spin_lock(&bs->lock);
-+      msg = list_entry(bs->queue.next, struct spi_message, queue);
-+      list_del(&msg->queue);
-+      spin_unlock(&bs->lock);
++              if (prev && prev->tx_buf && !prev->cs_change && !t->cs_change) {
++                      /*
++                       * reject if we have to combine two tx transfers and
++                       * their combined length is bigger than the buffer
++                       */
++                      if (prev->tx_buf && t->tx_buf &&
++                          (prev->len + t->len) > HSSPI_BUFFER_LEN)
++                              goto out;
++                      /*
++                       * reject if we need write more than 15 bytes in read
++                       * then write.
++                       */
++                      if (prev->tx_buf && t->rx_buf &&
++                          prev->len > HSSPI_MAX_PREPEND_LEN)
++                              goto out;
++              }
 +
-+      if (bs->stopping) {
-+              msg->status = -ESHUTDOWN;
-+              goto out;
 +      }
 +
 +      /* setup clock polarity */
 +      reg = bcm_hsspi_readl(HSSPI_GLOBAL_CTRL_REG);
 +      reg &= ~GLOBAL_CTRL_CLK_POLARITY;
-+
-+      if (msg->spi->mode & SPI_CPOL)
++      if (spi->mode & SPI_CPOL)
 +              reg |= GLOBAL_CTRL_CLK_POLARITY;
-+
 +      bcm_hsspi_writel(reg, HSSPI_GLOBAL_CTRL_REG);
 +
 +      list_for_each_entry(t, &msg->transfers, transfer_list) {
-+              /*
-+               * This controller does not support keeping the chip select
-+               * active between transfers.
-+               * This logic currently supports combining:
-+               *  write then read with no cs_change (e.g. m25p80 RDSR)
-+               *  write then write with no cs_change (e.g. m25p80 PP)
-+               */
 +              if (prev && prev->tx_buf && !prev->cs_change && !t->cs_change) {
 +                      /* combine write with following transfer */
-+                      len += bcm63xx_hsspi_do_txrx(msg->spi, prev, t);
++                      ret = bcm63xx_hsspi_do_txrx(msg->spi, prev, t);
++                      if (ret < 0)
++                              goto out;
++
++                      len += ret;
 +                      prev = NULL;
 +                      continue;
 +              }
 +
 +              /* write the previous pending transfer */
-+              if (prev != NULL)
-+                      len += bcm63xx_hsspi_do_txrx(msg->spi, prev, NULL);
++              if (prev != NULL) {
++                      ret = bcm63xx_hsspi_do_txrx(msg->spi, prev, NULL);
++                      if (ret < 0)
++                              goto out;
++
++                      len += ret;
++              }
 +
 +              prev = t;
 +      }
 +
 +      /* do last pending transfer */
-+      if (prev != NULL)
-+              len += bcm63xx_hsspi_do_txrx(msg->spi, prev, NULL);
++      if (prev != NULL) {
++              ret = bcm63xx_hsspi_do_txrx(msg->spi, prev, NULL);
++              if (ret < 0)
++                      goto out;
++              len += ret;
++      }
 +
-+      msg->status = 0;
 +      msg->actual_length = len;
++      ret = 0;
 +out:
-+      msg->complete(msg->context);
++      msg->status = ret;
++      spi_finalize_current_message(master);
++      return 0;
 +}
 +
 +static irqreturn_t bcm63xx_hsspi_interrupt(int irq, void *dev_id)
@@ -544,55 +308,48 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +      bcm_hsspi_writel(HSSPI_INT_CLEAR_ALL, HSSPI_INT_STATUS_REG);
 +      bcm_hsspi_writel(0, HSSPI_INT_MASK_REG);
 +
-+      spin_lock(&bs->lock);
-+
 +      if (bs->curr_trans && bs->curr_trans->rx_buf)
 +              memcpy_fromio(bs->curr_trans->rx_buf,  bs->fifo,
 +                            bs->curr_trans->len);
-+
 +      complete(&bs->done);
-+      spin_unlock(&bs->lock);
 +
 +      return IRQ_HANDLED;
 +}
 +
-+
-+static void bcm63xx_hsspi_cleanup(struct spi_device *spi)
-+{
-+      /* would free spi_controller memory here if any was allocated */
-+}
-+
 +static int __devinit bcm63xx_hsspi_probe(struct platform_device *pdev)
 +{
 +
 +      struct spi_master *master;
 +      struct bcm63xx_hsspi *bs;
 +      struct resource *res_mem;
++      void __iomem *regs;
 +      struct device *dev = &pdev->dev;
 +      struct bcm63xx_hsspi_pdata *pdata = pdev->dev.platform_data;
 +      struct clk *clk;
 +      int irq;
 +      int ret;
 +
-+      res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      if (!res_mem) {
-+              dev_err(dev, "no iomem\n");
-+              return -ENXIO;
-+      }
-+
 +      irq = platform_get_irq(pdev, 0);
 +      if (irq < 0) {
 +              dev_err(dev, "no irq\n");
 +              return -ENXIO;
 +      }
 +
++      res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      regs = devm_request_and_ioremap(dev, res_mem);
++      if (!regs) {
++              dev_err(dev, "unable to ioremap regs\n");
++              return -ENXIO;
++      }
++
 +      clk = clk_get(dev, "hsspi");
 +
 +      if (IS_ERR(clk)) {
 +              ret = PTR_ERR(clk);
 +              goto out_release;
 +      }
-+      clk_enable(clk);
++
++      clk_prepare_enable(clk);
 +
 +      master = spi_alloc_master(&pdev->dev, sizeof(*bs));
 +      if (!master) {
@@ -601,51 +358,38 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +      }
 +
 +      bs = spi_master_get_devdata(master);
-+      init_completion(&bs->done);
 +      bs->pdev = pdev;
 +      bs->clk = clk;
++      bs->regs = regs;
 +
-+      bs->regs = devm_request_and_ioremap(dev, res_mem);
-+      if (!bs->regs) {
-+              dev_err(dev, "unable to ioremap regs\n");
-+              ret = -ENOMEM;
-+              goto out_put_master;
-+      }
-+
-+      master->bus_num        = pdata->bus_num;
++      master->bus_num = pdata->bus_num;
 +      master->num_chipselect = 8;
-+      master->setup          = bcm63xx_hsspi_setup;
-+      master->transfer       = bcm63xx_hsspi_transfer;
-+      master->cleanup        = bcm63xx_hsspi_cleanup;
-+      master->mode_bits      = SPI_CPOL | SPI_CPHA;
++      master->setup = bcm63xx_hsspi_setup;
++      master->transfer_one_message = bcm63xx_hsspi_transfer_one;
++      master->mode_bits = SPI_CPOL | SPI_CPHA;
 +
 +      bs->speed_hz = pdata->speed_hz;
-+      bs->fifo = (u8 *)(bs->regs + HSSPI_FIFO_REG(0));
++      bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
 +
 +      platform_set_drvdata(pdev, master);
 +
-+      spin_lock_init(&bs->lock);
-+      INIT_LIST_HEAD(&bs->queue);
-+      INIT_WORK(&bs->ws, bcm63xx_hsspi_do_work);
-+      bs->workqueue = create_singlethread_workqueue(pdev->name);
 +      bs->curr_trans = NULL;
 +
 +      /* Initialize the hardware */
 +      bcm_hsspi_writel(0, HSSPI_INT_MASK_REG);
 +
++      /* clean up any pending interrupts */
++      bcm_hsspi_writel(HSSPI_INT_CLEAR_ALL, HSSPI_INT_STATUS_REG);
++
 +      bcm_hsspi_writel(bcm_hsspi_readl(HSSPI_GLOBAL_CTRL_REG) |
 +                       GLOBAL_CTRL_CLK_GATE_SSOFF,
 +                       HSSPI_GLOBAL_CTRL_REG);
 +
-+      ret = request_irq(irq, bcm63xx_hsspi_interrupt, IRQF_SHARED, pdev->name,
-+                        master);
++      ret = devm_request_irq(dev, irq, bcm63xx_hsspi_interrupt, IRQF_SHARED,
++                             pdev->name, master);
 +
 +      if (ret)
-+              goto out_destroy_workqueue;
-+
-+      spin_lock(&bs->lock);
-+      bs->irq = irq;
-+      spin_unlock(&bs->lock);
++              goto out_put_master;
 +
 +      /* register and we are done */
 +      ret = spi_register_master(master);
@@ -655,18 +399,14 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +      return 0;
 +
 +out_free_irq:
-+      free_irq(bs->irq, master);
-+out_destroy_workqueue:
-+      flush_workqueue(bs->workqueue);
-+      destroy_workqueue(bs->workqueue);
-+      iounmap(bs->regs);
++      devm_free_irq(dev, bs->irq, master);
 +out_put_master:
 +      spi_master_put(master);
 +out_disable_clk:
-+      clk_disable(clk);
++      clk_disable_unprepare(clk);
 +      clk_put(clk);
 +out_release:
-+      release_mem_region(res_mem->start, resource_size(res_mem));
++      devm_ioremap_release(dev, regs);
 +
 +      return ret;
 +}
@@ -676,34 +416,14 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +{
 +      struct spi_master *master = platform_get_drvdata(pdev);
 +      struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
-+      struct spi_message *msg;
 +
-+      cancel_work_sync(&bs->ws);
++      spi_unregister_master(master);
 +
 +      /* reset the hardware and block queue progress */
 +      bcm_hsspi_writel(0, HSSPI_INT_MASK_REG);
-+
-+      spin_lock(&bs->lock);
-+      /* HW shutdown */
-+      bs->stopping = 1;
-+      spin_unlock(&bs->lock);
-+
-+
-+      /* Terminate remaining queued transfers */
-+      list_for_each_entry(msg, &bs->queue, queue) {
-+              msg->status = -ESHUTDOWN;
-+              msg->complete(msg->context);
-+      }
-+
-+
-+      free_irq(bs->irq, master);
-+      flush_workqueue(bs->workqueue);
-+      destroy_workqueue(bs->workqueue);
-+
-+      clk_disable(bs->clk);
++      clk_disable_unprepare(bs->clk);
 +      clk_put(bs->clk);
 +
-+      spi_unregister_master(master);
 +      return 0;
 +}
 +
@@ -711,9 +431,10 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +static int bcm63xx_hsspi_suspend(struct platform_device *pdev,
 +                               pm_message_t mesg)
 +{
-+      struct spi_master       *master = platform_get_drvdata(pdev);
-+      struct bcm63xx_hsspi    *bs = spi_master_get_devdata(master);
++      struct spi_master *master = platform_get_drvdata(pdev);
++      struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
 +
++      spi_master_suspend(master);
 +      clk_disable(bs->clk);
 +
 +      return 0;
@@ -721,10 +442,11 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 +
 +static int bcm63xx_hsspi_resume(struct platform_device *pdev)
 +{
-+      struct spi_master       *master = platform_get_drvdata(pdev);
-+      struct bcm63xx_hsspi    *bs = spi_master_get_devdata(master);
++      struct spi_master *master = platform_get_drvdata(pdev);
++      struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
 +
 +      clk_enable(bs->clk);
++      spi_master_resume(master);
 +
 +      return 0;
 +}