diff options
Diffstat (limited to 'target/linux/brcm63xx/patches-3.3/419-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch')
-rw-r--r-- | target/linux/brcm63xx/patches-3.3/419-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch | 554 |
1 files changed, 138 insertions, 416 deletions
diff --git a/target/linux/brcm63xx/patches-3.3/419-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch b/target/linux/brcm63xx/patches-3.3/419-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch index 44e6958738..739c2c60f7 100644 --- a/target/linux/brcm63xx/patches-3.3/419-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch +++ b/target/linux/brcm63xx/patches-3.3/419-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch @@ -1,231 +1,28 @@ -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; +} |