linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI
@ 2012-01-31 14:10 Florian Fainelli
  2012-01-31 14:10 ` [PATCH 1/9 v3] MIPS: BCM63XX: add IRQ_SPI and CPU specific SPI IRQ values Florian Fainelli
                   ` (9 more replies)
  0 siblings, 10 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

This patch set depend on the serie "MIPS: BCM63XX: misc cleanup" and
adds support for the SPI controller found in BCM63xx SoCs.

Grant, it probably makes sense to get this merged via the MIPS tree
since it mostly depends on it.

Florian Fainelli (9):
  MIPS: BCM63XX: add IRQ_SPI and CPU specific SPI IRQ values
  MIPS: BCM63XX: define BCM6358 SPI base address
  MIPS: BCM63XX: add BCM6368 SPI clock mask
  MIPS: BCM63XX: define SPI register sizes.
  MIPS: BCM63XX: remove SPI2 register
  MIPS: BCM63XX: define internal registers offsets of the SPI
    controller
  MIPS: BCM63XX: add stub to register the SPI platform driver
  MIPS: BCM63XX: make board setup code register the spi platform device
  spi: add Broadcom BCM63xx SPI controller driver

 arch/mips/bcm63xx/Makefile                         |    3 +-
 arch/mips/bcm63xx/boards/board_bcm963xx.c          |    3 +
 arch/mips/bcm63xx/clk.c                            |    6 +-
 arch/mips/bcm63xx/dev-spi.c                        |  119 +++++
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h   |   23 +-
 .../include/asm/mach-bcm63xx/bcm63xx_dev_spi.h     |   89 ++++
 arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h  |  119 +++++
 drivers/spi/Kconfig                                |    6 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-bcm63xx.c                          |  486 ++++++++++++++++++++
 10 files changed, 842 insertions(+), 13 deletions(-)
 create mode 100644 arch/mips/bcm63xx/dev-spi.c
 create mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
 create mode 100644 drivers/spi/spi-bcm63xx.c

-- 
1.7.5.4

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 1/9 v3] MIPS: BCM63XX: add IRQ_SPI and CPU specific SPI IRQ values
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 2/9 v3] MIPS: BCM63XX: define BCM6358 SPI base address Florian Fainelli
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
No changes in v1 and v2

 arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
index 5b8d15b..9975727 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
@@ -478,6 +478,7 @@ static inline unsigned long bcm63xx_regset_address(enum bcm63xx_regs_set set)
  */
 enum bcm63xx_irq {
 	IRQ_TIMER = 0,
+	IRQ_SPI,
 	IRQ_UART0,
 	IRQ_UART1,
 	IRQ_DSL,
@@ -509,6 +510,7 @@ enum bcm63xx_irq {
  * 6338 irqs
  */
 #define BCM_6338_TIMER_IRQ		(IRQ_INTERNAL_BASE + 0)
+#define BCM_6338_SPI_IRQ		(IRQ_INTERNAL_BASE + 1)
 #define BCM_6338_UART0_IRQ		(IRQ_INTERNAL_BASE + 2)
 #define BCM_6338_UART1_IRQ		0
 #define BCM_6338_DSL_IRQ		(IRQ_INTERNAL_BASE + 5)
@@ -539,6 +541,7 @@ enum bcm63xx_irq {
  * 6345 irqs
  */
 #define BCM_6345_TIMER_IRQ		(IRQ_INTERNAL_BASE + 0)
+#define BCM_6345_SPI_IRQ		0
 #define BCM_6345_UART0_IRQ		(IRQ_INTERNAL_BASE + 2)
 #define BCM_6345_UART1_IRQ		0
 #define BCM_6345_DSL_IRQ		(IRQ_INTERNAL_BASE + 3)
@@ -569,6 +572,7 @@ enum bcm63xx_irq {
  * 6348 irqs
  */
 #define BCM_6348_TIMER_IRQ		(IRQ_INTERNAL_BASE + 0)
+#define BCM_6348_SPI_IRQ		(IRQ_INTERNAL_BASE + 1)
 #define BCM_6348_UART0_IRQ		(IRQ_INTERNAL_BASE + 2)
 #define BCM_6348_UART1_IRQ		0
 #define BCM_6348_DSL_IRQ		(IRQ_INTERNAL_BASE + 4)
@@ -599,6 +603,7 @@ enum bcm63xx_irq {
  * 6358 irqs
  */
 #define BCM_6358_TIMER_IRQ		(IRQ_INTERNAL_BASE + 0)
+#define BCM_6358_SPI_IRQ		(IRQ_INTERNAL_BASE + 1)
 #define BCM_6358_UART0_IRQ		(IRQ_INTERNAL_BASE + 2)
 #define BCM_6358_UART1_IRQ		(IRQ_INTERNAL_BASE + 3)
 #define BCM_6358_DSL_IRQ		(IRQ_INTERNAL_BASE + 29)
@@ -638,6 +643,7 @@ enum bcm63xx_irq {
 #define BCM_6368_HIGH_IRQ_BASE		(IRQ_INTERNAL_BASE + 32)
 
 #define BCM_6368_TIMER_IRQ		(IRQ_INTERNAL_BASE + 0)
+#define BCM_6368_SPI_IRQ		(IRQ_INTERNAL_BASE + 1)
 #define BCM_6368_UART0_IRQ		(IRQ_INTERNAL_BASE + 2)
 #define BCM_6368_UART1_IRQ		(IRQ_INTERNAL_BASE + 3)
 #define BCM_6368_DSL_IRQ		(IRQ_INTERNAL_BASE + 4)
@@ -677,6 +683,7 @@ extern const int *bcm63xx_irqs;
 
 #define __GEN_CPU_IRQ_TABLE(__cpu)					\
 	[IRQ_TIMER]		= BCM_## __cpu ##_TIMER_IRQ,		\
+	[IRQ_SPI]		= BCM_## __cpu ##_SPI_IRQ,		\
 	[IRQ_UART0]		= BCM_## __cpu ##_UART0_IRQ,		\
 	[IRQ_UART1]		= BCM_## __cpu ##_UART1_IRQ,		\
 	[IRQ_DSL]		= BCM_## __cpu ##_DSL_IRQ,		\
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 2/9 v3] MIPS: BCM63XX: define BCM6358 SPI base address
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
  2012-01-31 14:10 ` [PATCH 1/9 v3] MIPS: BCM63XX: add IRQ_SPI and CPU specific SPI IRQ values Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 3/9 v3] MIPS: BCM63XX: add BCM6368 SPI clock mask Florian Fainelli
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
No changes in v1 and v2

 arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
index 9975727..016dc9e 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
@@ -289,7 +289,7 @@ enum bcm63xx_regs_set {
 #define BCM_6358_UART0_BASE		(0xfffe0100)
 #define BCM_6358_UART1_BASE		(0xfffe0120)
 #define BCM_6358_GPIO_BASE		(0xfffe0080)
-#define BCM_6358_SPI_BASE		(0xdeadbeef)
+#define BCM_6358_SPI_BASE		(0xfffe0800)
 #define BCM_6358_SPI2_BASE		(0xfffe0800)
 #define BCM_6358_UDC0_BASE		(0xfffe0800)
 #define BCM_6358_OHCI0_BASE		(0xfffe1400)
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 3/9 v3] MIPS: BCM63XX: add BCM6368 SPI clock mask
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
  2012-01-31 14:10 ` [PATCH 1/9 v3] MIPS: BCM63XX: add IRQ_SPI and CPU specific SPI IRQ values Florian Fainelli
  2012-01-31 14:10 ` [PATCH 2/9 v3] MIPS: BCM63XX: define BCM6358 SPI base address Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 4/9 v3] MIPS: BCM63XX: define SPI register sizes Florian Fainelli
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
No changes in v1 and v2

 arch/mips/bcm63xx/clk.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/mips/bcm63xx/clk.c b/arch/mips/bcm63xx/clk.c
index 8d2ea22..be49b9a 100644
--- a/arch/mips/bcm63xx/clk.c
+++ b/arch/mips/bcm63xx/clk.c
@@ -181,9 +181,11 @@ static void spi_set(struct clk *clk, int enable)
 		mask = CKCTL_6338_SPI_EN;
 	else if (BCMCPU_IS_6348())
 		mask = CKCTL_6348_SPI_EN;
-	else
-		/* BCMCPU_IS_6358 */
+	else if (BCMCPU_IS_6358())
 		mask = CKCTL_6358_SPI_EN;
+	else
+		/* BCMCPU_IS_6368 */
+		mask = CKCTL_6368_SPI_EN;
 	bcm_hwclock_set(mask, enable);
 }
 
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 4/9 v3] MIPS: BCM63XX: define SPI register sizes.
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
                   ` (2 preceding siblings ...)
  2012-01-31 14:10 ` [PATCH 3/9 v3] MIPS: BCM63XX: add BCM6368 SPI clock mask Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 5/9 v3] MIPS: BCM63XX: remove SPI2 register Florian Fainelli
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

There are two distinct sizes for the SPI register depending on the SoC
generation (6338 & 6348 vs 6358 & 6368).

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
Changes since v2:
- added different sizes accounting for the different SPI controller FIFO sizes
No changes in v1

 arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
index 016dc9e..4c1e147 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
@@ -135,6 +135,10 @@ enum bcm63xx_regs_set {
 #define RSET_DSL_LMEM_SIZE		(64 * 1024 * 4)
 #define RSET_DSL_SIZE			4096
 #define RSET_WDT_SIZE			12
+#define BCM_6338_RSET_SPI_SIZE		64
+#define BCM_6348_RSET_SPI_SIZE		64
+#define BCM_6358_RSET_SPI_SIZE		1804
+#define BCM_6368_RSET_SPI_SIZE		1804
 #define RSET_ENET_SIZE			2048
 #define RSET_ENETDMA_SIZE		2048
 #define RSET_ENETSW_SIZE		65536
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 5/9 v3] MIPS: BCM63XX: remove SPI2 register
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
                   ` (3 preceding siblings ...)
  2012-01-31 14:10 ` [PATCH 4/9 v3] MIPS: BCM63XX: define SPI register sizes Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 6/9 v3] MIPS: BCM63XX: define internal registers offsets of the SPI controller Florian Fainelli
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

This register was introduced with the support of the BCM6368 CPU in the idea
that its internal layout was different from the other CPUs SPI controller.
The controller is actually the same as the one present on BCM6358 so we can
remove this register and use the usual SPI register instead.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
No changes in v1 and v2

 arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h |   10 +---------
 1 files changed, 1 insertions(+), 9 deletions(-)

diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
index 4c1e147..82a8175 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h
@@ -102,7 +102,6 @@ enum bcm63xx_regs_set {
 	RSET_UART1,
 	RSET_GPIO,
 	RSET_SPI,
-	RSET_SPI2,
 	RSET_UDC0,
 	RSET_OHCI0,
 	RSET_OHCI_PRIV,
@@ -166,7 +165,6 @@ 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_SPI2_BASE		(0xdeadbeef)
 #define BCM_6338_UDC0_BASE		(0xdeadbeef)
 #define BCM_6338_USBDMA_BASE		(0xfffe2400)
 #define BCM_6338_OHCI0_BASE		(0xdeadbeef)
@@ -210,7 +208,6 @@ 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_SPI2_BASE		(0xdeadbeef)
 #define BCM_6345_UDC0_BASE		(0xdeadbeef)
 #define BCM_6345_USBDMA_BASE		(0xfffe2800)
 #define BCM_6345_ENET0_BASE		(0xfffe1800)
@@ -253,7 +250,6 @@ 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_SPI2_BASE		(0xdeadbeef)
 #define BCM_6348_UDC0_BASE		(0xfffe1000)
 #define BCM_6348_OHCI0_BASE		(0xfffe1b00)
 #define BCM_6348_OHCI_PRIV_BASE		(0xfffe1c00)
@@ -294,7 +290,6 @@ 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_SPI2_BASE		(0xfffe0800)
 #define BCM_6358_UDC0_BASE		(0xfffe0800)
 #define BCM_6358_OHCI0_BASE		(0xfffe1400)
 #define BCM_6358_OHCI_PRIV_BASE		(0xdeadbeef)
@@ -335,8 +330,7 @@ enum bcm63xx_regs_set {
 #define BCM_6368_UART0_BASE		(0xb0000100)
 #define BCM_6368_UART1_BASE		(0xb0000120)
 #define BCM_6368_GPIO_BASE		(0xb0000080)
-#define BCM_6368_SPI_BASE		(0xdeadbeef)
-#define BCM_6368_SPI2_BASE		(0xb0000800)
+#define BCM_6368_SPI_BASE		(0xb0000800)
 #define BCM_6368_UDC0_BASE		(0xdeadbeef)
 #define BCM_6368_OHCI0_BASE		(0xb0001600)
 #define BCM_6368_OHCI_PRIV_BASE		(0xdeadbeef)
@@ -383,7 +377,6 @@ extern const unsigned long *bcm63xx_regs_base;
 	__GEN_RSET_BASE(__cpu, UART1)					\
 	__GEN_RSET_BASE(__cpu, GPIO)					\
 	__GEN_RSET_BASE(__cpu, SPI)					\
-	__GEN_RSET_BASE(__cpu, SPI2)					\
 	__GEN_RSET_BASE(__cpu, UDC0)					\
 	__GEN_RSET_BASE(__cpu, OHCI0)					\
 	__GEN_RSET_BASE(__cpu, OHCI_PRIV)				\
@@ -422,7 +415,6 @@ extern const unsigned long *bcm63xx_regs_base;
 	[RSET_UART1]		= BCM_## __cpu ##_UART1_BASE,		\
 	[RSET_GPIO]		= BCM_## __cpu ##_GPIO_BASE,		\
 	[RSET_SPI]		= BCM_## __cpu ##_SPI_BASE,		\
-	[RSET_SPI2]		= BCM_## __cpu ##_SPI2_BASE,		\
 	[RSET_UDC0]		= BCM_## __cpu ##_UDC0_BASE,		\
 	[RSET_OHCI0]		= BCM_## __cpu ##_OHCI0_BASE,		\
 	[RSET_OHCI_PRIV]	= BCM_## __cpu ##_OHCI_PRIV_BASE,	\
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 6/9 v3] MIPS: BCM63XX: define internal registers offsets of the SPI controller
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
                   ` (4 preceding siblings ...)
  2012-01-31 14:10 ` [PATCH 5/9 v3] MIPS: BCM63XX: remove SPI2 register Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 7/9 v3] MIPS: BCM63XX: add stub to register the SPI platform driver Florian Fainelli
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

BCM6338, BCM6348, BCM6358 and BCM6368 basically use the same SPI controller
though the internal registers are shuffled, which still allows a common
driver to drive that IP block.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
Changes since v2:
- removed invalid SPI_CLK_25MHZ definition

No changes in v1

 arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h |  119 +++++++++++++++++++++
 1 files changed, 119 insertions(+), 0 deletions(-)

diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
index 6ddd081..c21aa34 100644
--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h
@@ -973,4 +973,123 @@
 #define M2M_SRCID_REG(x)		((x) * 0x40 + 0x14)
 #define M2M_DSTID_REG(x)		((x) * 0x40 + 0x18)
 
+/*************************************************************************
+ * _REG relative to RSET_SPI
+ *************************************************************************/
+
+/* BCM 6338 SPI core */
+#define SPI_6338_CMD			0x00	/* 16-bits register */
+#define SPI_6338_INT_STATUS		0x02
+#define SPI_6338_INT_MASK_ST		0x03
+#define SPI_6338_INT_MASK		0x04
+#define SPI_6338_ST			0x05
+#define SPI_6338_CLK_CFG		0x06
+#define SPI_6338_FILL_BYTE		0x07
+#define SPI_6338_MSG_TAIL		0x09
+#define SPI_6338_RX_TAIL		0x0b
+#define SPI_6338_MSG_CTL		0x40
+#define SPI_6338_MSG_DATA		0x41
+#define SPI_6338_MSG_DATA_SIZE		0x3f
+#define SPI_6338_RX_DATA		0x80
+#define SPI_6338_RX_DATA_SIZE		0x3f
+
+/* BCM 6348 SPI core */
+#define SPI_6348_CMD			0x00	/* 16-bits register */
+#define SPI_6348_INT_STATUS		0x02
+#define SPI_6348_INT_MASK_ST		0x03
+#define SPI_6348_INT_MASK		0x04
+#define SPI_6348_ST			0x05
+#define SPI_6348_CLK_CFG		0x06
+#define SPI_6348_FILL_BYTE		0x07
+#define SPI_6348_MSG_TAIL		0x09
+#define SPI_6348_RX_TAIL		0x0b
+#define SPI_6348_MSG_CTL		0x40
+#define SPI_6348_MSG_DATA		0x41
+#define SPI_6348_MSG_DATA_SIZE		0x3f
+#define SPI_6348_RX_DATA		0x80
+#define SPI_6348_RX_DATA_SIZE		0x3f
+
+/* BCM 6358 SPI core */
+#define SPI_6358_MSG_CTL		0x00	/* 16-bits register */
+#define SPI_6358_MSG_DATA		0x02
+#define SPI_6358_MSG_DATA_SIZE		0x21e
+#define SPI_6358_RX_DATA		0x400
+#define SPI_6358_RX_DATA_SIZE		0x220
+#define SPI_6358_CMD			0x700	/* 16-bits register */
+#define SPI_6358_INT_STATUS		0x702
+#define SPI_6358_INT_MASK_ST		0x703
+#define SPI_6358_INT_MASK		0x704
+#define SPI_6358_ST			0x705
+#define SPI_6358_CLK_CFG		0x706
+#define SPI_6358_FILL_BYTE		0x707
+#define SPI_6358_MSG_TAIL		0x709
+#define SPI_6358_RX_TAIL		0x70B
+
+/* BCM 6358 SPI core */
+#define SPI_6368_MSG_CTL		0x00	/* 16-bits register */
+#define SPI_6368_MSG_DATA		0x02
+#define SPI_6368_MSG_DATA_SIZE		0x21e
+#define SPI_6368_RX_DATA		0x400
+#define SPI_6368_RX_DATA_SIZE		0x220
+#define SPI_6368_CMD			0x700	/* 16-bits register */
+#define SPI_6368_INT_STATUS		0x702
+#define SPI_6368_INT_MASK_ST		0x703
+#define SPI_6368_INT_MASK		0x704
+#define SPI_6368_ST			0x705
+#define SPI_6368_CLK_CFG		0x706
+#define SPI_6368_FILL_BYTE		0x707
+#define SPI_6368_MSG_TAIL		0x709
+#define SPI_6368_RX_TAIL		0x70B
+
+/* Shared SPI definitions */
+
+/* Message configuration */
+#define SPI_FD_RW			0x00
+#define SPI_HD_W			0x01
+#define SPI_HD_R			0x02
+#define SPI_BYTE_CNT_SHIFT		0
+#define SPI_MSG_TYPE_SHIFT		14
+
+/* Command */
+#define SPI_CMD_NOOP			0x00
+#define SPI_CMD_SOFT_RESET		0x01
+#define SPI_CMD_HARD_RESET		0x02
+#define SPI_CMD_START_IMMEDIATE		0x03
+#define SPI_CMD_COMMAND_SHIFT		0
+#define SPI_CMD_COMMAND_MASK		0x000f
+#define SPI_CMD_DEVICE_ID_SHIFT		4
+#define SPI_CMD_PREPEND_BYTE_CNT_SHIFT	8
+#define SPI_CMD_ONE_BYTE_SHIFT		11
+#define SPI_CMD_ONE_WIRE_SHIFT		12
+#define SPI_DEV_ID_0			0
+#define SPI_DEV_ID_1			1
+#define SPI_DEV_ID_2			2
+#define SPI_DEV_ID_3			3
+
+/* Interrupt mask */
+#define SPI_INTR_CMD_DONE		0x01
+#define SPI_INTR_RX_OVERFLOW		0x02
+#define SPI_INTR_TX_UNDERFLOW		0x04
+#define SPI_INTR_TX_OVERFLOW		0x08
+#define SPI_INTR_RX_UNDERFLOW		0x10
+#define SPI_INTR_CLEAR_ALL		0x1f
+
+/* Status */
+#define SPI_RX_EMPTY			0x02
+#define SPI_CMD_BUSY			0x04
+#define SPI_SERIAL_BUSY			0x08
+
+/* Clock configuration */
+#define SPI_CLK_20MHZ			0x00
+#define SPI_CLK_0_391MHZ		0x01
+#define SPI_CLK_0_781MHZ		0x02 /* default */
+#define SPI_CLK_1_563MHZ		0x03
+#define SPI_CLK_3_125MHZ		0x04
+#define SPI_CLK_6_250MHZ		0x05
+#define SPI_CLK_12_50MHZ		0x06
+#define SPI_CLK_MASK			0x07
+#define SPI_SSOFFTIME_MASK		0x38
+#define SPI_SSOFFTIME_SHIFT		3
+#define SPI_BYTE_SWAP			0x80
+
 #endif /* BCM63XX_REGS_H_ */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 7/9 v3] MIPS: BCM63XX: add stub to register the SPI platform driver
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
                   ` (5 preceding siblings ...)
  2012-01-31 14:10 ` [PATCH 6/9 v3] MIPS: BCM63XX: define internal registers offsets of the SPI controller Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 8/9 v3] MIPS: BCM63XX: make board setup code register the spi platform device Florian Fainelli
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

This patch adds the necessary stub to register the SPI platform driver.
Since the registers are shuffled between the 4 BCM63xx CPUs supported by
this SPI driver we also need to generate the internal register layout and
export this layout for the driver to use it properly.

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
Changes since v2:
- added different resource size due to different fifo sizes

No changes in v1

 arch/mips/bcm63xx/Makefile                         |    3 +-
 arch/mips/bcm63xx/dev-spi.c                        |  119 ++++++++++++++++++++
 .../include/asm/mach-bcm63xx/bcm63xx_dev_spi.h     |   89 +++++++++++++++
 3 files changed, 210 insertions(+), 1 deletions(-)
 create mode 100644 arch/mips/bcm63xx/dev-spi.c
 create mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h

diff --git a/arch/mips/bcm63xx/Makefile b/arch/mips/bcm63xx/Makefile
index 6dfdc69..4049cd5 100644
--- a/arch/mips/bcm63xx/Makefile
+++ b/arch/mips/bcm63xx/Makefile
@@ -1,5 +1,6 @@
 obj-y		+= clk.o cpu.o cs.o gpio.o irq.o prom.o setup.o timer.o \
-		   dev-dsp.o dev-enet.o dev-pcmcia.o dev-uart.o dev-wdt.o
+		   dev-dsp.o dev-enet.o dev-pcmcia.o dev-spi.o dev-uart.o \
+		   dev-wdt.o
 obj-$(CONFIG_EARLY_PRINTK)	+= early_printk.o
 
 obj-y		+= boards/
diff --git a/arch/mips/bcm63xx/dev-spi.c b/arch/mips/bcm63xx/dev-spi.c
new file mode 100644
index 0000000..67fa45b
--- /dev/null
+++ b/arch/mips/bcm63xx/dev-spi.c
@@ -0,0 +1,119 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org>
+ * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_dev_spi.h>
+#include <bcm63xx_regs.h>
+
+#ifdef BCMCPU_RUNTIME_DETECT
+/*
+ * register offsets
+ */
+static const unsigned long bcm6338_regs_spi[] = {
+	__GEN_SPI_REGS_TABLE(6338)
+};
+
+static const unsigned long bcm6348_regs_spi[] = {
+	__GEN_SPI_REGS_TABLE(6348)
+};
+
+static const unsigned long bcm6358_regs_spi[] = {
+	__GEN_SPI_REGS_TABLE(6358)
+};
+
+static const unsigned long bcm6368_regs_spi[] = {
+	__GEN_SPI_REGS_TABLE(6368)
+};
+
+const unsigned long *bcm63xx_regs_spi;
+EXPORT_SYMBOL(bcm63xx_regs_spi);
+
+static __init void bcm63xx_spi_regs_init(void)
+{
+	if (BCMCPU_IS_6338())
+		bcm63xx_regs_spi = bcm6338_regs_spi;
+	if (BCMCPU_IS_6348())
+		bcm63xx_regs_spi = bcm6348_regs_spi;
+	if (BCMCPU_IS_6358())
+		bcm63xx_regs_spi = bcm6358_regs_spi;
+	if (BCMCPU_IS_6368())
+		bcm63xx_regs_spi = bcm6368_regs_spi;
+}
+#else
+static __init void bcm63xx_spi_regs_init(void) { }
+#endif
+
+static struct resource spi_resources[] = {
+	{
+		.start		= -1, /* filled at runtime */
+		.end		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_MEM,
+	},
+	{
+		.start		= -1, /* filled at runtime */
+		.flags		= IORESOURCE_IRQ,
+	},
+};
+
+static struct bcm63xx_spi_pdata spi_pdata = {
+	.bus_num		= 0,
+	.num_chipselect		= 8,
+};
+
+static struct platform_device bcm63xx_spi_device = {
+	.name		= "bcm63xx-spi",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(spi_resources),
+	.resource	= spi_resources,
+	.dev		= {
+		.platform_data = &spi_pdata,
+	},
+};
+
+int __init bcm63xx_spi_register(void)
+{
+	struct clk *periph_clk;
+
+	if (BCMCPU_IS_6345())
+		return -ENODEV;
+
+	periph_clk = clk_get(NULL, "periph");
+	if (IS_ERR(periph_clk)) {
+		pr_err("unable to get periph clock\n");
+		return -ENODEV;
+	}
+
+	/* Set bus frequency */
+	spi_pdata.speed_hz = clk_get_rate(periph_clk);
+
+	spi_resources[0].start = bcm63xx_regset_address(RSET_SPI);
+	spi_resources[0].end = spi_resources[0].start;
+	spi_resources[1].start = bcm63xx_get_irq_number(IRQ_SPI);
+
+	if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) {
+		spi_resources[0].end += BCM_6338_RSET_SPI_SIZE - 1;
+		spi_pdata.fifo_size = SPI_6338_MSG_DATA_SIZE;
+	}
+
+	if (BCMCPU_IS_6358() || BCMCPU_IS_6368()) {
+		spi_resources[0].end += BCM_6358_RSET_SPI_SIZE - 1;
+		spi_pdata.fifo_size = SPI_6358_MSG_DATA_SIZE;
+	}
+
+	bcm63xx_spi_regs_init();
+
+	return platform_device_register(&bcm63xx_spi_device);
+}
diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
new file mode 100644
index 0000000..7d98dbe
--- /dev/null
+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
@@ -0,0 +1,89 @@
+#ifndef BCM63XX_DEV_SPI_H
+#define BCM63XX_DEV_SPI_H
+
+#include <linux/types.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+int __init bcm63xx_spi_register(void);
+
+struct bcm63xx_spi_pdata {
+	unsigned int	fifo_size;
+	int		bus_num;
+	int		num_chipselect;
+	u32		speed_hz;
+};
+
+enum bcm63xx_regs_spi {
+	SPI_CMD,
+	SPI_INT_STATUS,
+	SPI_INT_MASK_ST,
+	SPI_INT_MASK,
+	SPI_ST,
+	SPI_CLK_CFG,
+	SPI_FILL_BYTE,
+	SPI_MSG_TAIL,
+	SPI_RX_TAIL,
+	SPI_MSG_CTL,
+	SPI_MSG_DATA,
+	SPI_RX_DATA,
+};
+
+#define __GEN_SPI_RSET_BASE(__cpu, __rset)				\
+	case SPI_## __rset:						\
+		return SPI_## __cpu ##_## __rset;
+
+#define __GEN_SPI_RSET(__cpu)						\
+	switch (reg) {							\
+	__GEN_SPI_RSET_BASE(__cpu, CMD)					\
+	__GEN_SPI_RSET_BASE(__cpu, INT_STATUS)				\
+	__GEN_SPI_RSET_BASE(__cpu, INT_MASK_ST)				\
+	__GEN_SPI_RSET_BASE(__cpu, INT_MASK)				\
+	__GEN_SPI_RSET_BASE(__cpu, ST)					\
+	__GEN_SPI_RSET_BASE(__cpu, CLK_CFG)				\
+	__GEN_SPI_RSET_BASE(__cpu, FILL_BYTE)				\
+	__GEN_SPI_RSET_BASE(__cpu, MSG_TAIL)				\
+	__GEN_SPI_RSET_BASE(__cpu, RX_TAIL)				\
+	__GEN_SPI_RSET_BASE(__cpu, MSG_CTL)				\
+	__GEN_SPI_RSET_BASE(__cpu, MSG_DATA)				\
+	__GEN_SPI_RSET_BASE(__cpu, RX_DATA)				\
+	}
+
+#define __GEN_SPI_REGS_TABLE(__cpu)					\
+	[SPI_CMD]		= SPI_## __cpu ##_CMD,			\
+	[SPI_INT_STATUS]	= SPI_## __cpu ##_INT_STATUS,		\
+	[SPI_INT_MASK_ST]	= SPI_## __cpu ##_INT_MASK_ST,		\
+	[SPI_INT_MASK]		= SPI_## __cpu ##_INT_MASK,		\
+	[SPI_ST]		= SPI_## __cpu ##_ST,			\
+	[SPI_CLK_CFG]		= SPI_## __cpu ##_CLK_CFG,		\
+	[SPI_FILL_BYTE]		= SPI_## __cpu ##_FILL_BYTE,		\
+	[SPI_MSG_TAIL]		= SPI_## __cpu ##_MSG_TAIL,		\
+	[SPI_RX_TAIL]		= SPI_## __cpu ##_RX_TAIL,		\
+	[SPI_MSG_CTL]		= SPI_## __cpu ##_MSG_CTL,		\
+	[SPI_MSG_DATA]		= SPI_## __cpu ##_MSG_DATA,		\
+	[SPI_RX_DATA]		= SPI_## __cpu ##_RX_DATA,
+
+static inline unsigned long bcm63xx_spireg(enum bcm63xx_regs_spi reg)
+{
+#ifdef BCMCPU_RUNTIME_DETECT
+	extern const unsigned long *bcm63xx_regs_spi;
+
+	return bcm63xx_regs_spi[reg];
+#else
+#ifdef CONFIG_BCM63XX_CPU_6338
+	__GEN_SPI_RSET(6338)
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6348
+	__GEN_SPI_RSET(6348)
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6358
+	__GEN_SPI_RSET(6358)
+#endif
+#ifdef CONFIG_BCM63XX_CPU_6368
+	__GEN_SPI_RSET(6368)
+#endif
+#endif
+	return 0;
+}
+
+#endif /* BCM63XX_DEV_SPI_H */
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 8/9 v3] MIPS: BCM63XX: make board setup code register the spi platform device
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
                   ` (6 preceding siblings ...)
  2012-01-31 14:10 ` [PATCH 7/9 v3] MIPS: BCM63XX: add stub to register the SPI platform driver Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
  2012-01-31 14:10 ` [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver Florian Fainelli
       [not found] ` <1328019048-5892-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
  9 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf; +Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli

Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
No changes since v1 and v2

 arch/mips/bcm63xx/boards/board_bcm963xx.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/arch/mips/bcm63xx/boards/board_bcm963xx.c b/arch/mips/bcm63xx/boards/board_bcm963xx.c
index 2f1773f..ea65c0f 100644
--- a/arch/mips/bcm63xx/boards/board_bcm963xx.c
+++ b/arch/mips/bcm63xx/boards/board_bcm963xx.c
@@ -25,6 +25,7 @@
 #include <bcm63xx_dev_enet.h>
 #include <bcm63xx_dev_dsp.h>
 #include <bcm63xx_dev_pcmcia.h>
+#include <bcm63xx_dev_spi.h>
 #include <board_bcm963xx.h>
 
 #define PFX	"board_bcm963xx: "
@@ -890,6 +891,8 @@ int __init board_register_devices(void)
 	}
 #endif
 
+	bcm63xx_spi_register();
+
 	/* read base address of boot chip select (0) */
 	val = bcm_mpi_readl(MPI_CSBASE_REG(0));
 	val &= MPI_CSBASE_BASE_MASK;
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver
  2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
                   ` (7 preceding siblings ...)
  2012-01-31 14:10 ` [PATCH 8/9 v3] MIPS: BCM63XX: make board setup code register the spi platform device Florian Fainelli
@ 2012-01-31 14:10 ` Florian Fainelli
       [not found]   ` <1328019048-5892-10-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
  2012-01-31 20:19   ` [PATCH 9/9 v3] " Grant Likely
       [not found] ` <1328019048-5892-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
  9 siblings, 2 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 14:10 UTC (permalink / raw)
  To: ralf
  Cc: linux-mips, grant.likely, spi-devel-general, Florian Fainelli,
	Tanguy Bouzeloc

This patch adds support for the SPI controller found on the Broadcom BCM63xx
SoCs.

Signed-off-by: Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
Signed-off-by: Florian Fainelli <florian@openwrt.org>
---
Changes since v2:
- reworked bcm63xx_spi_setup_transfer to choose closest spi transfer
  frequency
- removed invalid 25Mhz frequency
- fixed some minor checkpatch issues

Changes since v1:
- switched to the devm_* API which frees resources automatically
- switched to dev_pm_ops
- use module_platform_driver
- remove MODULE_VERSION()
- fixed return value when clock is not present using PTR_ERR()
- fixed probe() error path to disable clock in case of failure

 drivers/spi/Kconfig       |    6 +
 drivers/spi/Makefile      |    1 +
 drivers/spi/spi-bcm63xx.c |  486 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 493 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/spi-bcm63xx.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 3f9a47e..16818ac 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -94,6 +94,12 @@ config SPI_AU1550
 	  If you say yes to this option, support will be included for the
 	  PSC SPI controller found on Au1550, Au1200 and Au1300 series.
 
+config SPI_BCM63XX
+	tristate "Broadcom BCM63xx SPI controller"
+	depends on BCM63XX
+	help
+          Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
+
 config SPI_BITBANG
 	tristate "Utilities for Bitbanging SPI masters"
 	help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..be38f73 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o
 obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o
 obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o
 obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o
+obj-$(CONFIG_SPI_BCM63XX)		+= spi-bcm63xx.o
 obj-$(CONFIG_SPI_BFIN)			+= spi-bfin5xx.o
 obj-$(CONFIG_SPI_BFIN_SPORT)		+= spi-bfin-sport.o
 obj-$(CONFIG_SPI_BITBANG)		+= spi-bitbang.o
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
new file mode 100644
index 0000000..eba8505
--- /dev/null
+++ b/drivers/spi/spi-bcm63xx.c
@@ -0,0 +1,486 @@
+/*
+ * Broadcom BCM63xx SPI controller support
+ *
+ * Copyright (C) 2009-2011 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
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+
+#include <bcm63xx_dev_spi.h>
+
+#define PFX		KBUILD_MODNAME
+#define DRV_VER		"0.1.2"
+
+struct bcm63xx_spi {
+	spinlock_t		lock;
+	int			stopping;
+	struct completion	done;
+
+	void __iomem		*regs;
+	int			irq;
+
+	/* Platform data */
+	u32			speed_hz;
+	unsigned		fifo_size;
+
+	/* Data buffers */
+	const unsigned char	*tx_ptr;
+	unsigned char		*rx_ptr;
+
+	/* data iomem */
+	u8 __iomem		*tx_io;
+	const u8 __iomem	*rx_io;
+
+	int			remaining_bytes;
+
+	struct clk		*clk;
+	struct platform_device	*pdev;
+};
+
+static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
+				unsigned int offset)
+{
+	return bcm_readw(bs->regs + bcm63xx_spireg(offset));
+}
+
+static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
+				unsigned int offset)
+{
+	return bcm_readw(bs->regs + bcm63xx_spireg(offset));
+}
+
+static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
+				  u8 value, unsigned int offset)
+{
+	bcm_writeb(value, bs->regs + bcm63xx_spireg(offset));
+}
+
+static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
+				  u16 value, unsigned int offset)
+{
+	bcm_writew(value, bs->regs + bcm63xx_spireg(offset));
+}
+
+static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
+	{ 20000000, SPI_CLK_20MHZ },
+	{ 12500000, SPI_CLK_12_50MHZ },
+	{  6250000, SPI_CLK_6_250MHZ },
+	{  3125000, SPI_CLK_3_125MHZ },
+	{  1563000, SPI_CLK_1_563MHZ },
+	{   781000, SPI_CLK_0_781MHZ },
+	{   391000, SPI_CLK_0_391MHZ }
+};
+
+static int bcm63xx_spi_setup_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);
+		return -EINVAL;
+	}
+
+	if (spi->chip_select > spi->master->num_chipselect) {
+		dev_err(&spi->dev, "%s, unsupported slave %d\n",
+			__func__, spi->chip_select);
+		return -EINVAL;
+	}
+
+	/* Find the closest clock configuration */
+	for (i = 0; i < SPI_CLK_MASK; i++) {
+		if (hz <= bcm63xx_spi_freq_table[i][0]) {
+			clk_cfg = bcm63xx_spi_freq_table[i][1];
+			break;
+		}
+	}
+
+	/* No matching configuration found, default to lowest */
+	if (i == SPI_CLK_MASK)
+		clk_cfg = SPI_CLK_0_391MHZ;
+
+	/* clear existing clock configuration bits of the register */
+	reg = bcm_spi_readb(bs, SPI_CLK_CFG);
+	reg &= ~SPI_CLK_MASK;
+	reg |= clk_cfg;
+
+	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: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static int bcm63xx_spi_setup(struct spi_device *spi)
+{
+	struct bcm63xx_spi *bs;
+	int ret;
+
+	bs = spi_master_get_devdata(spi->master);
+
+	if (bs->stopping)
+		return -ESHUTDOWN;
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = 8;
+
+	if (spi->mode & ~MODEBITS) {
+		dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
+			__func__, spi->mode & ~MODEBITS);
+		return -EINVAL;
+	}
+
+	ret = bcm63xx_spi_setup_transfer(spi, NULL);
+	if (ret < 0) {
+		dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
+			spi->mode & ~MODEBITS);
+		return ret;
+	}
+
+	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
+		__func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
+
+	return 0;
+}
+
+/* Fill the TX FIFO with as many bytes as possible */
+static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
+{
+	u8 size;
+
+	/* Fill the Tx FIFO with as many bytes as possible */
+	size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
+		bs->fifo_size;
+	memcpy_toio(bs->tx_io, bs->tx_ptr, size);
+	bs->remaining_bytes -= size;
+}
+
+static 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;
+
+	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);
+
+	/* Fill in the Message control register */
+	msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
+
+	if (t->rx_buf && t->tx_buf)
+		msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT);
+	else if (t->rx_buf)
+		msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT);
+	else if (t->tx_buf)
+		msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT);
+
+	bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
+
+	/* Issue the transfer */
+	cmd = SPI_CMD_START_IMMEDIATE;
+	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);
+
+	return t->len - bs->remaining_bytes;
+}
+
+static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m)
+{
+	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
+	struct spi_transfer *t;
+	int ret = 0;
+
+	if (unlikely(list_empty(&m->transfers)))
+		return -EINVAL;
+
+	if (bs->stopping)
+		return -ESHUTDOWN;
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		ret += bcm63xx_txrx_bufs(spi, t);
+	}
+
+	m->complete(m->context);
+
+	return ret;
+}
+
+/* This driver supports single master mode only. Hence
+ * CMD_DONE is the only interrupt we care about
+ */
+static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
+{
+	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);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static int __init bcm63xx_spi_probe(struct platform_device *pdev)
+{
+	struct resource *r;
+	struct device *dev = &pdev->dev;
+	struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
+	int irq;
+	struct spi_master *master;
+	struct clk *clk;
+	struct bcm63xx_spi *bs;
+	int ret;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(dev, "no iomem\n");
+		ret = -ENXIO;
+		goto out;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "no irq\n");
+		ret = -ENXIO;
+		goto out;
+	}
+
+	clk = clk_get(dev, "spi");
+	if (IS_ERR(clk)) {
+		dev_err(dev, "no clock for device\n");
+		ret = PTR_ERR(clk);
+		goto out;
+	}
+
+	master = spi_alloc_master(dev, sizeof(*bs));
+	if (!master) {
+		dev_err(dev, "out of memory\n");
+		ret = -ENOMEM;
+		goto out_clk;
+	}
+
+	bs = spi_master_get_devdata(master);
+	init_completion(&bs->done);
+
+	platform_set_drvdata(pdev, master);
+	bs->pdev = pdev;
+
+	if (!devm_request_mem_region(&pdev->dev, r->start,
+					resource_size(r), PFX)) {
+		dev_err(dev, "iomem request failed\n");
+		ret = -ENXIO;
+		goto out_err;
+	}
+
+	bs->regs = devm_ioremap_nocache(&pdev->dev, r->start,
+							resource_size(r));
+	if (!bs->regs) {
+		dev_err(dev, "unable to ioremap regs\n");
+		ret = -ENOMEM;
+		goto out_err;
+	}
+
+	bs->irq = irq;
+	bs->clk = clk;
+	bs->fifo_size = pdata->fifo_size;
+
+	ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
+							pdev->name, master);
+	if (ret) {
+		dev_err(dev, "unable to request irq\n");
+		goto out_err;
+	}
+
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->num_chipselect;
+	master->setup = bcm63xx_spi_setup;
+	master->transfer = bcm63xx_transfer;
+	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);
+	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
+
+	/* register and we are done */
+	ret = spi_register_master(master);
+	if (ret) {
+		dev_err(dev, "spi register failed\n");
+		goto out_clk_disable;
+	}
+
+	dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n",
+		 r->start, irq, bs->fifo_size, DRV_VER);
+
+	return 0;
+
+out_clk_disable:
+	clk_disable(clk);
+out_err:
+	platform_set_drvdata(pdev, NULL);
+	spi_master_put(master);
+out_clk:
+	clk_put(clk);
+out:
+	return ret;
+}
+
+static int __exit bcm63xx_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct bcm63xx_spi *bs = spi_master_get_devdata(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;
+}
+
+#ifdef CONFIG_PM
+static int bcm63xx_spi_suspend(struct device *dev)
+{
+	struct spi_master *master =
+			platform_get_drvdata(to_platform_device(dev));
+	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
+
+	clk_disable(bs->clk);
+
+	return 0;
+}
+
+static int bcm63xx_spi_resume(struct device *dev)
+{
+	struct spi_master *master =
+			platform_get_drvdata(to_platform_device(dev));
+	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
+
+	clk_enable(bs->clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
+	.suspend	= bcm63xx_spi_suspend,
+	.resume		= bcm63xx_spi_resume,
+};
+
+#define BCM63XX_SPI_PM_OPS	(&bcm63xx_spi_pm_ops)
+#else
+#define BCM63XX_SPI_PM_OPS	NULL
+#endif
+
+static struct platform_driver bcm63xx_spi_driver = {
+	.driver = {
+		.name	= "bcm63xx-spi",
+		.owner	= THIS_MODULE,
+		.pm	= BCM63XX_SPI_PM_OPS,
+	},
+	.probe		= bcm63xx_spi_probe,
+	.remove		= __exit_p(bcm63xx_spi_remove),
+};
+
+module_platform_driver(bcm63xx_spi_driver);
+
+MODULE_ALIAS("platform:bcm63xx_spi");
+MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
+MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>");
+MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
+MODULE_LICENSE("GPL");
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver
       [not found]   ` <1328019048-5892-10-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
@ 2012-01-31 17:52     ` Shubhrajyoti Datta
  2012-02-01 10:14     ` [PATCH v4] " Florian Fainelli
  1 sibling, 0 replies; 19+ messages in thread
From: Shubhrajyoti Datta @ 2012-01-31 17:52 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Tanguy Bouzeloc, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	ralf-6z/3iImG2C8G8FEW9MqTrA,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

Hi Florian,

On Tue, Jan 31, 2012 at 7:40 PM, Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org> wrote:
> This patch adds support for the SPI controller found on the Broadcom BCM63xx
> SoCs.
>
> Signed-off-by: Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
> ---
> Changes since v2:
> - reworked bcm63xx_spi_setup_transfer to choose closest spi transfer
>  frequency
> - removed invalid 25Mhz frequency
> - fixed some minor checkpatch issues
>
> Changes since v1:
> - switched to the devm_* API which frees resources automatically
> - switched to dev_pm_ops
> - use module_platform_driver
> - remove MODULE_VERSION()
> - fixed return value when clock is not present using PTR_ERR()
> - fixed probe() error path to disable clock in case of failure
>
>  drivers/spi/Kconfig       |    6 +
>  drivers/spi/Makefile      |    1 +
>  drivers/spi/spi-bcm63xx.c |  486 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 493 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/spi/spi-bcm63xx.c
>
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 3f9a47e..16818ac 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -94,6 +94,12 @@ config SPI_AU1550
>          If you say yes to this option, support will be included for the
>          PSC SPI controller found on Au1550, Au1200 and Au1300 series.
>
> +config SPI_BCM63XX
> +       tristate "Broadcom BCM63xx SPI controller"
> +       depends on BCM63XX
> +       help
> +          Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
> +
>  config SPI_BITBANG
>        tristate "Utilities for Bitbanging SPI masters"
>        help
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 61c3261..be38f73 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA)              += spi-altera.o
>  obj-$(CONFIG_SPI_ATMEL)                        += spi-atmel.o
>  obj-$(CONFIG_SPI_ATH79)                        += spi-ath79.o
>  obj-$(CONFIG_SPI_AU1550)               += spi-au1550.o
> +obj-$(CONFIG_SPI_BCM63XX)              += spi-bcm63xx.o
>  obj-$(CONFIG_SPI_BFIN)                 += spi-bfin5xx.o
>  obj-$(CONFIG_SPI_BFIN_SPORT)           += spi-bfin-sport.o
>  obj-$(CONFIG_SPI_BITBANG)              += spi-bitbang.o
> diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
> new file mode 100644
> index 0000000..eba8505
> --- /dev/null
> +++ b/drivers/spi/spi-bcm63xx.c
> @@ -0,0 +1,486 @@
> +/*
> + * Broadcom BCM63xx SPI controller support
> + *
> + * Copyright (C) 2009-2011 Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
> + * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the
> + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/spi/spi.h>
> +#include <linux/completion.h>
> +#include <linux/err.h>
> +
> +#include <bcm63xx_dev_spi.h>
> +
> +#define PFX            KBUILD_MODNAME
> +#define DRV_VER                "0.1.2"
> +
> +struct bcm63xx_spi {
> +       spinlock_t              lock;
> +       int                     stopping;
> +       struct completion       done;
> +
> +       void __iomem            *regs;
> +       int                     irq;
> +
> +       /* Platform data */
> +       u32                     speed_hz;
> +       unsigned                fifo_size;
> +
> +       /* Data buffers */
> +       const unsigned char     *tx_ptr;
> +       unsigned char           *rx_ptr;
> +
> +       /* data iomem */
> +       u8 __iomem              *tx_io;
> +       const u8 __iomem        *rx_io;
> +
> +       int                     remaining_bytes;
> +
> +       struct clk              *clk;
> +       struct platform_device  *pdev;
> +};
> +
> +static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
> +                               unsigned int offset)
> +{
> +       return bcm_readw(bs->regs + bcm63xx_spireg(offset));

are you sure it should be bcm_readw

> +}
> +
> +static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
> +                               unsigned int offset)
> +{
> +       return bcm_readw(bs->regs + bcm63xx_spireg(offset));
> +}
> +

------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers
is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver
  2012-01-31 14:10 ` [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver Florian Fainelli
       [not found]   ` <1328019048-5892-10-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
@ 2012-01-31 20:19   ` Grant Likely
       [not found]     ` <20120131201922.GE22611-e0URQFbLeQY2iJbIjFUEsiwD8/FfD2ys@public.gmane.org>
  1 sibling, 1 reply; 19+ messages in thread
From: Grant Likely @ 2012-01-31 20:19 UTC (permalink / raw)
  To: Florian Fainelli; +Cc: ralf, linux-mips, spi-devel-general, Tanguy Bouzeloc

On Tue, Jan 31, 2012 at 03:10:48PM +0100, Florian Fainelli wrote:
> This patch adds support for the SPI controller found on the Broadcom BCM63xx
> SoCs.
> 
> Signed-off-by: Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
> Signed-off-by: Florian Fainelli <florian@openwrt.org>
> ---
> Changes since v2:
> - reworked bcm63xx_spi_setup_transfer to choose closest spi transfer
>   frequency
> - removed invalid 25Mhz frequency
> - fixed some minor checkpatch issues
> 
> Changes since v1:
> - switched to the devm_* API which frees resources automatically
> - switched to dev_pm_ops
> - use module_platform_driver
> - remove MODULE_VERSION()
> - fixed return value when clock is not present using PTR_ERR()
> - fixed probe() error path to disable clock in case of failure
> 
>  drivers/spi/Kconfig       |    6 +
>  drivers/spi/Makefile      |    1 +
>  drivers/spi/spi-bcm63xx.c |  486 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 493 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/spi/spi-bcm63xx.c
> 

Looks okay.  There are a couple of problems that needs to be fixed
below, but otherwise:

Acked-by: Grant Likely <grant.likely@secretlab.ca>

Merge this through the same tree as patches 1-8

g.

> +static int __init bcm63xx_spi_probe(struct platform_device *pdev)

__devinit

> +static int __exit bcm63xx_spi_remove(struct platform_device *pdev)

__devexit

> +static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
> +	.suspend	= bcm63xx_spi_suspend,
> +	.resume		= bcm63xx_spi_resume,
> +};
> +
> +#define BCM63XX_SPI_PM_OPS	(&bcm63xx_spi_pm_ops)
> +#else
> +#define BCM63XX_SPI_PM_OPS	NULL

A bit ugly.  Do this instead in the else clause and drop the BCM63XX_SPI_PM_OPS:

#define bcm63xx_spi_pm_ops NULL

> +#endif
> +
> +static struct platform_driver bcm63xx_spi_driver = {
> +	.driver = {
> +		.name	= "bcm63xx-spi",
> +		.owner	= THIS_MODULE,
> +		.pm	= BCM63XX_SPI_PM_OPS,
> +	},
> +	.probe		= bcm63xx_spi_probe,
> +	.remove		= __exit_p(bcm63xx_spi_remove),

__devexit_p

> +};
> +
> +module_platform_driver(bcm63xx_spi_driver);
> +
> +MODULE_ALIAS("platform:bcm63xx_spi");
> +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
> +MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>");
> +MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
> +MODULE_LICENSE("GPL");
> -- 
> 1.7.5.4
> 

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver
       [not found]     ` <20120131201922.GE22611-e0URQFbLeQY2iJbIjFUEsiwD8/FfD2ys@public.gmane.org>
@ 2012-01-31 21:20       ` Florian Fainelli
       [not found]         ` <201201312220.41561.florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
  0 siblings, 1 reply; 19+ messages in thread
From: Florian Fainelli @ 2012-01-31 21:20 UTC (permalink / raw)
  To: Grant Likely
  Cc: Tanguy Bouzeloc, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	ralf-6z/3iImG2C8G8FEW9MqTrA,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

Hello Grant,

On Tuesday 31 January 2012 21:19:22 Grant Likely wrote:
> On Tue, Jan 31, 2012 at 03:10:48PM +0100, Florian Fainelli wrote:
> > This patch adds support for the SPI controller found on the Broadcom
> > BCM63xx SoCs.
> > 
> > Signed-off-by: Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
> > Signed-off-by: Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
> > ---
> > Changes since v2:
> > - reworked bcm63xx_spi_setup_transfer to choose closest spi transfer
> > 
> >   frequency
> > 
> > - removed invalid 25Mhz frequency
> > - fixed some minor checkpatch issues
> > 
> > Changes since v1:
> > - switched to the devm_* API which frees resources automatically
> > - switched to dev_pm_ops
> > - use module_platform_driver
> > - remove MODULE_VERSION()
> > - fixed return value when clock is not present using PTR_ERR()
> > - fixed probe() error path to disable clock in case of failure
> > 
> >  drivers/spi/Kconfig       |    6 +
> >  drivers/spi/Makefile      |    1 +
> >  drivers/spi/spi-bcm63xx.c |  486
> >  +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 493
> >  insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/spi/spi-bcm63xx.c
> 
> Looks okay.  There are a couple of problems that needs to be fixed
> below, but otherwise:
> 
> Acked-by: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
> 
> Merge this through the same tree as patches 1-8
> 
> g.
> 
> > +static int __init bcm63xx_spi_probe(struct platform_device *pdev)
> 
> __devinit
> 
> > +static int __exit bcm63xx_spi_remove(struct platform_device *pdev)
> 
> __devexit
> 
> > +static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
> > +	.suspend	= bcm63xx_spi_suspend,
> > +	.resume		= bcm63xx_spi_resume,
> > +};
> > +
> > +#define BCM63XX_SPI_PM_OPS	(&bcm63xx_spi_pm_ops)
> > +#else
> > +#define BCM63XX_SPI_PM_OPS	NULL
> 
> A bit ugly.  Do this instead in the else clause and drop the
> BCM63XX_SPI_PM_OPS:
> 
> #define bcm63xx_spi_pm_ops NULL

This won't work, because driver.pm must be set to a pointer to a struct 
dev_pm_ops, that's why I used this trick to make it build fine in both cases. 
If I follow your advice, with driver.pm = &bcm63xx_spi_pm_ops, it won't build 
for CONFIG_PM=n.

> 
> > +#endif
> > +
> > +static struct platform_driver bcm63xx_spi_driver = {
> > +	.driver = {
> > +		.name	= "bcm63xx-spi",
> > +		.owner	= THIS_MODULE,
> > +		.pm	= BCM63XX_SPI_PM_OPS,
> > +	},
> > +	.probe		= bcm63xx_spi_probe,
> > +	.remove		= __exit_p(bcm63xx_spi_remove),
> 
> __devexit_p
> 
> > +};
> > +
> > +module_platform_driver(bcm63xx_spi_driver);
> > +
> > +MODULE_ALIAS("platform:bcm63xx_spi");
> > +MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
> > +MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
> > +MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
> > +MODULE_LICENSE("GPL");

-- 
Florian

------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers
is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver
       [not found]         ` <201201312220.41561.florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
@ 2012-01-31 21:59           ` Grant Likely
  0 siblings, 0 replies; 19+ messages in thread
From: Grant Likely @ 2012-01-31 21:59 UTC (permalink / raw)
  To: Florian Fainelli
  Cc: Tanguy Bouzeloc, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	ralf-6z/3iImG2C8G8FEW9MqTrA,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Tue, Jan 31, 2012 at 2:20 PM, Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org> wrote:
> On Tuesday 31 January 2012 21:19:22 Grant Likely wrote:
>> > +static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
>> > +   .suspend        = bcm63xx_spi_suspend,
>> > +   .resume         = bcm63xx_spi_resume,
>> > +};
>> > +
>> > +#define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops)
>> > +#else
>> > +#define BCM63XX_SPI_PM_OPS NULL
>>
>> A bit ugly.  Do this instead in the else clause and drop the
>> BCM63XX_SPI_PM_OPS:
>>
>> #define bcm63xx_spi_pm_ops NULL
>
> This won't work, because driver.pm must be set to a pointer to a struct
> dev_pm_ops, that's why I used this trick to make it build fine in both cases.
> If I follow your advice, with driver.pm = &bcm63xx_spi_pm_ops, it won't build
> for CONFIG_PM=n.

Okay, fair enough.

g.

------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers
is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d

^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v4] spi: add Broadcom BCM63xx SPI controller driver
       [not found]   ` <1328019048-5892-10-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
  2012-01-31 17:52     ` Shubhrajyoti Datta
@ 2012-02-01 10:14     ` Florian Fainelli
  2012-02-01 11:22       ` Maxime Bizon
       [not found]       ` <1328091249-10389-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
  1 sibling, 2 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-02-01 10:14 UTC (permalink / raw)
  To: ralf-6z/3iImG2C8G8FEW9MqTrA
  Cc: Tanguy Bouzeloc, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	Florian Fainelli,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

This patch adds support for the SPI controller found on the Broadcom BCM63xx
SoCs.

Signed-off-by: Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
Signed-off-by: Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
Acked-by: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
---
Changes since v3:
- changed bcm_spi_readb to use readb accessor instead of readw
- fixed multiple __dev{init,exit} annotations

Changes since v2:
- reworked bcm63xx_spi_setup_transfer to choose closest spi transfer
  frequency
- removed invalid 25Mhz frequency
- fixed some minor checkpatch issues

Changes since v1:
- switched to the devm_* API which frees resources automatically
- switched to dev_pm_ops
- use module_platform_driver
- remove MODULE_VERSION()
- fixed return value when clock is not present using PTR_ERR()
- fixed probe() error path to disable clock in case of failure

 drivers/spi/Kconfig       |    6 +
 drivers/spi/Makefile      |    1 +
 drivers/spi/spi-bcm63xx.c |  486 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 493 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/spi-bcm63xx.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 3f9a47e..16818ac 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -94,6 +94,12 @@ config SPI_AU1550
 	  If you say yes to this option, support will be included for the
 	  PSC SPI controller found on Au1550, Au1200 and Au1300 series.
 
+config SPI_BCM63XX
+	tristate "Broadcom BCM63xx SPI controller"
+	depends on BCM63XX
+	help
+          Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
+
 config SPI_BITBANG
 	tristate "Utilities for Bitbanging SPI masters"
 	help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..be38f73 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o
 obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o
 obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o
 obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o
+obj-$(CONFIG_SPI_BCM63XX)		+= spi-bcm63xx.o
 obj-$(CONFIG_SPI_BFIN)			+= spi-bfin5xx.o
 obj-$(CONFIG_SPI_BFIN_SPORT)		+= spi-bfin-sport.o
 obj-$(CONFIG_SPI_BITBANG)		+= spi-bitbang.o
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
new file mode 100644
index 0000000..f01b264
--- /dev/null
+++ b/drivers/spi/spi-bcm63xx.c
@@ -0,0 +1,486 @@
+/*
+ * Broadcom BCM63xx SPI controller support
+ *
+ * Copyright (C) 2009-2011 Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
+ * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+
+#include <bcm63xx_dev_spi.h>
+
+#define PFX		KBUILD_MODNAME
+#define DRV_VER		"0.1.2"
+
+struct bcm63xx_spi {
+	spinlock_t		lock;
+	int			stopping;
+	struct completion	done;
+
+	void __iomem		*regs;
+	int			irq;
+
+	/* Platform data */
+	u32			speed_hz;
+	unsigned		fifo_size;
+
+	/* Data buffers */
+	const unsigned char	*tx_ptr;
+	unsigned char		*rx_ptr;
+
+	/* data iomem */
+	u8 __iomem		*tx_io;
+	const u8 __iomem	*rx_io;
+
+	int			remaining_bytes;
+
+	struct clk		*clk;
+	struct platform_device	*pdev;
+};
+
+static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
+				unsigned int offset)
+{
+	return bcm_readb(bs->regs + bcm63xx_spireg(offset));
+}
+
+static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
+				unsigned int offset)
+{
+	return bcm_readw(bs->regs + bcm63xx_spireg(offset));
+}
+
+static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
+				  u8 value, unsigned int offset)
+{
+	bcm_writeb(value, bs->regs + bcm63xx_spireg(offset));
+}
+
+static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
+				  u16 value, unsigned int offset)
+{
+	bcm_writew(value, bs->regs + bcm63xx_spireg(offset));
+}
+
+static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
+	{ 20000000, SPI_CLK_20MHZ },
+	{ 12500000, SPI_CLK_12_50MHZ },
+	{  6250000, SPI_CLK_6_250MHZ },
+	{  3125000, SPI_CLK_3_125MHZ },
+	{  1563000, SPI_CLK_1_563MHZ },
+	{   781000, SPI_CLK_0_781MHZ },
+	{   391000, SPI_CLK_0_391MHZ }
+};
+
+static int bcm63xx_spi_setup_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);
+		return -EINVAL;
+	}
+
+	if (spi->chip_select > spi->master->num_chipselect) {
+		dev_err(&spi->dev, "%s, unsupported slave %d\n",
+			__func__, spi->chip_select);
+		return -EINVAL;
+	}
+
+	/* Find the closest clock configuration */
+	for (i = 0; i < SPI_CLK_MASK; i++) {
+		if (hz <= bcm63xx_spi_freq_table[i][0]) {
+			clk_cfg = bcm63xx_spi_freq_table[i][1];
+			break;
+		}
+	}
+
+	/* No matching configuration found, default to lowest */
+	if (i == SPI_CLK_MASK)
+		clk_cfg = SPI_CLK_0_391MHZ;
+
+	/* clear existing clock configuration bits of the register */
+	reg = bcm_spi_readb(bs, SPI_CLK_CFG);
+	reg &= ~SPI_CLK_MASK;
+	reg |= clk_cfg;
+
+	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: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static int bcm63xx_spi_setup(struct spi_device *spi)
+{
+	struct bcm63xx_spi *bs;
+	int ret;
+
+	bs = spi_master_get_devdata(spi->master);
+
+	if (bs->stopping)
+		return -ESHUTDOWN;
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = 8;
+
+	if (spi->mode & ~MODEBITS) {
+		dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
+			__func__, spi->mode & ~MODEBITS);
+		return -EINVAL;
+	}
+
+	ret = bcm63xx_spi_setup_transfer(spi, NULL);
+	if (ret < 0) {
+		dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
+			spi->mode & ~MODEBITS);
+		return ret;
+	}
+
+	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
+		__func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
+
+	return 0;
+}
+
+/* Fill the TX FIFO with as many bytes as possible */
+static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
+{
+	u8 size;
+
+	/* Fill the Tx FIFO with as many bytes as possible */
+	size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
+		bs->fifo_size;
+	memcpy_toio(bs->tx_io, bs->tx_ptr, size);
+	bs->remaining_bytes -= size;
+}
+
+static 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;
+
+	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);
+
+	/* Fill in the Message control register */
+	msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
+
+	if (t->rx_buf && t->tx_buf)
+		msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT);
+	else if (t->rx_buf)
+		msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT);
+	else if (t->tx_buf)
+		msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT);
+
+	bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
+
+	/* Issue the transfer */
+	cmd = SPI_CMD_START_IMMEDIATE;
+	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);
+
+	return t->len - bs->remaining_bytes;
+}
+
+static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m)
+{
+	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
+	struct spi_transfer *t;
+	int ret = 0;
+
+	if (unlikely(list_empty(&m->transfers)))
+		return -EINVAL;
+
+	if (bs->stopping)
+		return -ESHUTDOWN;
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		ret += bcm63xx_txrx_bufs(spi, t);
+	}
+
+	m->complete(m->context);
+
+	return ret;
+}
+
+/* This driver supports single master mode only. Hence
+ * CMD_DONE is the only interrupt we care about
+ */
+static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
+{
+	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);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
+{
+	struct resource *r;
+	struct device *dev = &pdev->dev;
+	struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
+	int irq;
+	struct spi_master *master;
+	struct clk *clk;
+	struct bcm63xx_spi *bs;
+	int ret;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(dev, "no iomem\n");
+		ret = -ENXIO;
+		goto out;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "no irq\n");
+		ret = -ENXIO;
+		goto out;
+	}
+
+	clk = clk_get(dev, "spi");
+	if (IS_ERR(clk)) {
+		dev_err(dev, "no clock for device\n");
+		ret = PTR_ERR(clk);
+		goto out;
+	}
+
+	master = spi_alloc_master(dev, sizeof(*bs));
+	if (!master) {
+		dev_err(dev, "out of memory\n");
+		ret = -ENOMEM;
+		goto out_clk;
+	}
+
+	bs = spi_master_get_devdata(master);
+	init_completion(&bs->done);
+
+	platform_set_drvdata(pdev, master);
+	bs->pdev = pdev;
+
+	if (!devm_request_mem_region(&pdev->dev, r->start,
+					resource_size(r), PFX)) {
+		dev_err(dev, "iomem request failed\n");
+		ret = -ENXIO;
+		goto out_err;
+	}
+
+	bs->regs = devm_ioremap_nocache(&pdev->dev, r->start,
+							resource_size(r));
+	if (!bs->regs) {
+		dev_err(dev, "unable to ioremap regs\n");
+		ret = -ENOMEM;
+		goto out_err;
+	}
+
+	bs->irq = irq;
+	bs->clk = clk;
+	bs->fifo_size = pdata->fifo_size;
+
+	ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
+							pdev->name, master);
+	if (ret) {
+		dev_err(dev, "unable to request irq\n");
+		goto out_err;
+	}
+
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->num_chipselect;
+	master->setup = bcm63xx_spi_setup;
+	master->transfer = bcm63xx_transfer;
+	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);
+	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
+
+	/* register and we are done */
+	ret = spi_register_master(master);
+	if (ret) {
+		dev_err(dev, "spi register failed\n");
+		goto out_clk_disable;
+	}
+
+	dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n",
+		 r->start, irq, bs->fifo_size, DRV_VER);
+
+	return 0;
+
+out_clk_disable:
+	clk_disable(clk);
+out_err:
+	platform_set_drvdata(pdev, NULL);
+	spi_master_put(master);
+out_clk:
+	clk_put(clk);
+out:
+	return ret;
+}
+
+static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
+{
+	struct spi_master *master = platform_get_drvdata(pdev);
+	struct bcm63xx_spi *bs = spi_master_get_devdata(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;
+}
+
+#ifdef CONFIG_PM
+static int bcm63xx_spi_suspend(struct device *dev)
+{
+	struct spi_master *master =
+			platform_get_drvdata(to_platform_device(dev));
+	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
+
+	clk_disable(bs->clk);
+
+	return 0;
+}
+
+static int bcm63xx_spi_resume(struct device *dev)
+{
+	struct spi_master *master =
+			platform_get_drvdata(to_platform_device(dev));
+	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
+
+	clk_enable(bs->clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
+	.suspend	= bcm63xx_spi_suspend,
+	.resume		= bcm63xx_spi_resume,
+};
+
+#define BCM63XX_SPI_PM_OPS	(&bcm63xx_spi_pm_ops)
+#else
+#define BCM63XX_SPI_PM_OPS	NULL
+#endif
+
+static struct platform_driver bcm63xx_spi_driver = {
+	.driver = {
+		.name	= "bcm63xx-spi",
+		.owner	= THIS_MODULE,
+		.pm	= BCM63XX_SPI_PM_OPS,
+	},
+	.probe		= bcm63xx_spi_probe,
+	.remove		= __devexit_p(bcm63xx_spi_remove),
+};
+
+module_platform_driver(bcm63xx_spi_driver);
+
+MODULE_ALIAS("platform:bcm63xx_spi");
+MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
+MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
+MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
+MODULE_LICENSE("GPL");
-- 
1.7.5.4


------------------------------------------------------------------------------
Keep Your Developer Skills Current with LearnDevNow!
The most comprehensive online learning library for Microsoft developers
is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
Metro Style Apps, more. Free future releases when you subscribe now!
http://p.sf.net/sfu/learndevnow-d2d

^ permalink raw reply related	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] spi: add Broadcom BCM63xx SPI controller driver
  2012-02-01 10:14     ` [PATCH v4] " Florian Fainelli
@ 2012-02-01 11:22       ` Maxime Bizon
       [not found]       ` <1328091249-10389-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
  1 sibling, 0 replies; 19+ messages in thread
From: Maxime Bizon @ 2012-02-01 11:22 UTC (permalink / raw)
  To: Florian Fainelli; +Cc: ralf, linux-mips, spi-devel-general, Tanguy Bouzeloc


On Wed, 2012-02-01 at 11:14 +0100, Florian Fainelli wrote:

Hi Florian,

> +struct bcm63xx_spi {
> +	spinlock_t		lock;

this lock is never actually used

it is referenced only once in device removal path

> +	int			stopping;

this can be removed by changing device removal path to first call
spi_unregister_master. that way the spi stack cannot call spi_transfer
anymore and we don't need to abort these calls.


> +static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
> +{

> [...]

> +	bcm_spi_writew(bs, cmd, SPI_CMD);
> +	wait_for_completion(&bs->done);
> +

bcm63xx_txrx_bufs() is called by bcm63xx_transfer(), and according to
Documentation/spi/spi-summary:

    master->transfer(struct spi_device *spi, struct spi_message *message)
	This must not sleep.  Its responsibility is arrange that the
        transfer happens and its complete() callback is issued.  The two
        will normally happen later, after other transfers complete, and
        if the controller is idle it will need to be kickstarted.

So we cannot do a synchronous wait here, this must be pushed to a
workqueue or kthread.


-- 
Maxime

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] spi: add Broadcom BCM63xx SPI controller driver
       [not found]       ` <1328091249-10389-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
@ 2012-03-09 22:04         ` Grant Likely
  2012-03-12  8:25           ` Florian Fainelli
  0 siblings, 1 reply; 19+ messages in thread
From: Grant Likely @ 2012-03-09 22:04 UTC (permalink / raw)
  To: Florian Fainelli, ralf-6z/3iImG2C8G8FEW9MqTrA
  Cc: Tanguy Bouzeloc, linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	Florian Fainelli,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

On Wed,  1 Feb 2012 11:14:09 +0100, Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org> wrote:
> This patch adds support for the SPI controller found on the Broadcom BCM63xx
> SoCs.
> 
> Signed-off-by: Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
> Signed-off-by: Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
> Acked-by: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>

Applied, thanks.

In v3.4, the spi subsystem is gaining core support for spi message queues.
You should look at migrating this driver to use it in the v3.5 timeframe.

g.

> ---
> Changes since v3:
> - changed bcm_spi_readb to use readb accessor instead of readw
> - fixed multiple __dev{init,exit} annotations
> 
> Changes since v2:
> - reworked bcm63xx_spi_setup_transfer to choose closest spi transfer
>   frequency
> - removed invalid 25Mhz frequency
> - fixed some minor checkpatch issues
> 
> Changes since v1:
> - switched to the devm_* API which frees resources automatically
> - switched to dev_pm_ops
> - use module_platform_driver
> - remove MODULE_VERSION()
> - fixed return value when clock is not present using PTR_ERR()
> - fixed probe() error path to disable clock in case of failure
> 
>  drivers/spi/Kconfig       |    6 +
>  drivers/spi/Makefile      |    1 +
>  drivers/spi/spi-bcm63xx.c |  486 +++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 493 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/spi/spi-bcm63xx.c
> 
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 3f9a47e..16818ac 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -94,6 +94,12 @@ config SPI_AU1550
>  	  If you say yes to this option, support will be included for the
>  	  PSC SPI controller found on Au1550, Au1200 and Au1300 series.
>  
> +config SPI_BCM63XX
> +	tristate "Broadcom BCM63xx SPI controller"
> +	depends on BCM63XX
> +	help
> +          Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
> +
>  config SPI_BITBANG
>  	tristate "Utilities for Bitbanging SPI masters"
>  	help
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 61c3261..be38f73 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o
>  obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o
>  obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o
>  obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o
> +obj-$(CONFIG_SPI_BCM63XX)		+= spi-bcm63xx.o
>  obj-$(CONFIG_SPI_BFIN)			+= spi-bfin5xx.o
>  obj-$(CONFIG_SPI_BFIN_SPORT)		+= spi-bfin-sport.o
>  obj-$(CONFIG_SPI_BITBANG)		+= spi-bitbang.o
> diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
> new file mode 100644
> index 0000000..f01b264
> --- /dev/null
> +++ b/drivers/spi/spi-bcm63xx.c
> @@ -0,0 +1,486 @@
> +/*
> + * Broadcom BCM63xx SPI controller support
> + *
> + * Copyright (C) 2009-2011 Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
> + * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the
> + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/spi/spi.h>
> +#include <linux/completion.h>
> +#include <linux/err.h>
> +
> +#include <bcm63xx_dev_spi.h>
> +
> +#define PFX		KBUILD_MODNAME
> +#define DRV_VER		"0.1.2"
> +
> +struct bcm63xx_spi {
> +	spinlock_t		lock;
> +	int			stopping;
> +	struct completion	done;
> +
> +	void __iomem		*regs;
> +	int			irq;
> +
> +	/* Platform data */
> +	u32			speed_hz;
> +	unsigned		fifo_size;
> +
> +	/* Data buffers */
> +	const unsigned char	*tx_ptr;
> +	unsigned char		*rx_ptr;
> +
> +	/* data iomem */
> +	u8 __iomem		*tx_io;
> +	const u8 __iomem	*rx_io;
> +
> +	int			remaining_bytes;
> +
> +	struct clk		*clk;
> +	struct platform_device	*pdev;
> +};
> +
> +static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
> +				unsigned int offset)
> +{
> +	return bcm_readb(bs->regs + bcm63xx_spireg(offset));
> +}
> +
> +static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
> +				unsigned int offset)
> +{
> +	return bcm_readw(bs->regs + bcm63xx_spireg(offset));
> +}
> +
> +static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
> +				  u8 value, unsigned int offset)
> +{
> +	bcm_writeb(value, bs->regs + bcm63xx_spireg(offset));
> +}
> +
> +static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
> +				  u16 value, unsigned int offset)
> +{
> +	bcm_writew(value, bs->regs + bcm63xx_spireg(offset));
> +}
> +
> +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
> +	{ 20000000, SPI_CLK_20MHZ },
> +	{ 12500000, SPI_CLK_12_50MHZ },
> +	{  6250000, SPI_CLK_6_250MHZ },
> +	{  3125000, SPI_CLK_3_125MHZ },
> +	{  1563000, SPI_CLK_1_563MHZ },
> +	{   781000, SPI_CLK_0_781MHZ },
> +	{   391000, SPI_CLK_0_391MHZ }
> +};
> +
> +static int bcm63xx_spi_setup_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);
> +		return -EINVAL;
> +	}
> +
> +	if (spi->chip_select > spi->master->num_chipselect) {
> +		dev_err(&spi->dev, "%s, unsupported slave %d\n",
> +			__func__, spi->chip_select);
> +		return -EINVAL;
> +	}
> +
> +	/* Find the closest clock configuration */
> +	for (i = 0; i < SPI_CLK_MASK; i++) {
> +		if (hz <= bcm63xx_spi_freq_table[i][0]) {
> +			clk_cfg = bcm63xx_spi_freq_table[i][1];
> +			break;
> +		}
> +	}
> +
> +	/* No matching configuration found, default to lowest */
> +	if (i == SPI_CLK_MASK)
> +		clk_cfg = SPI_CLK_0_391MHZ;
> +
> +	/* clear existing clock configuration bits of the register */
> +	reg = bcm_spi_readb(bs, SPI_CLK_CFG);
> +	reg &= ~SPI_CLK_MASK;
> +	reg |= clk_cfg;
> +
> +	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: */
> +#define MODEBITS (SPI_CPOL | SPI_CPHA)
> +
> +static int bcm63xx_spi_setup(struct spi_device *spi)
> +{
> +	struct bcm63xx_spi *bs;
> +	int ret;
> +
> +	bs = spi_master_get_devdata(spi->master);
> +
> +	if (bs->stopping)
> +		return -ESHUTDOWN;
> +
> +	if (!spi->bits_per_word)
> +		spi->bits_per_word = 8;
> +
> +	if (spi->mode & ~MODEBITS) {
> +		dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
> +			__func__, spi->mode & ~MODEBITS);
> +		return -EINVAL;
> +	}
> +
> +	ret = bcm63xx_spi_setup_transfer(spi, NULL);
> +	if (ret < 0) {
> +		dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
> +			spi->mode & ~MODEBITS);
> +		return ret;
> +	}
> +
> +	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
> +		__func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
> +
> +	return 0;
> +}
> +
> +/* Fill the TX FIFO with as many bytes as possible */
> +static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
> +{
> +	u8 size;
> +
> +	/* Fill the Tx FIFO with as many bytes as possible */
> +	size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
> +		bs->fifo_size;
> +	memcpy_toio(bs->tx_io, bs->tx_ptr, size);
> +	bs->remaining_bytes -= size;
> +}
> +
> +static 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;
> +
> +	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);
> +
> +	/* Fill in the Message control register */
> +	msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
> +
> +	if (t->rx_buf && t->tx_buf)
> +		msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT);
> +	else if (t->rx_buf)
> +		msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT);
> +	else if (t->tx_buf)
> +		msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT);
> +
> +	bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
> +
> +	/* Issue the transfer */
> +	cmd = SPI_CMD_START_IMMEDIATE;
> +	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);
> +
> +	return t->len - bs->remaining_bytes;
> +}
> +
> +static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m)
> +{
> +	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
> +	struct spi_transfer *t;
> +	int ret = 0;
> +
> +	if (unlikely(list_empty(&m->transfers)))
> +		return -EINVAL;
> +
> +	if (bs->stopping)
> +		return -ESHUTDOWN;
> +
> +	list_for_each_entry(t, &m->transfers, transfer_list) {
> +		ret += bcm63xx_txrx_bufs(spi, t);
> +	}
> +
> +	m->complete(m->context);
> +
> +	return ret;
> +}
> +
> +/* This driver supports single master mode only. Hence
> + * CMD_DONE is the only interrupt we care about
> + */
> +static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
> +{
> +	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);
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +
> +static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
> +{
> +	struct resource *r;
> +	struct device *dev = &pdev->dev;
> +	struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
> +	int irq;
> +	struct spi_master *master;
> +	struct clk *clk;
> +	struct bcm63xx_spi *bs;
> +	int ret;
> +
> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!r) {
> +		dev_err(dev, "no iomem\n");
> +		ret = -ENXIO;
> +		goto out;
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(dev, "no irq\n");
> +		ret = -ENXIO;
> +		goto out;
> +	}
> +
> +	clk = clk_get(dev, "spi");
> +	if (IS_ERR(clk)) {
> +		dev_err(dev, "no clock for device\n");
> +		ret = PTR_ERR(clk);
> +		goto out;
> +	}
> +
> +	master = spi_alloc_master(dev, sizeof(*bs));
> +	if (!master) {
> +		dev_err(dev, "out of memory\n");
> +		ret = -ENOMEM;
> +		goto out_clk;
> +	}
> +
> +	bs = spi_master_get_devdata(master);
> +	init_completion(&bs->done);
> +
> +	platform_set_drvdata(pdev, master);
> +	bs->pdev = pdev;
> +
> +	if (!devm_request_mem_region(&pdev->dev, r->start,
> +					resource_size(r), PFX)) {
> +		dev_err(dev, "iomem request failed\n");
> +		ret = -ENXIO;
> +		goto out_err;
> +	}
> +
> +	bs->regs = devm_ioremap_nocache(&pdev->dev, r->start,
> +							resource_size(r));
> +	if (!bs->regs) {
> +		dev_err(dev, "unable to ioremap regs\n");
> +		ret = -ENOMEM;
> +		goto out_err;
> +	}
> +
> +	bs->irq = irq;
> +	bs->clk = clk;
> +	bs->fifo_size = pdata->fifo_size;
> +
> +	ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
> +							pdev->name, master);
> +	if (ret) {
> +		dev_err(dev, "unable to request irq\n");
> +		goto out_err;
> +	}
> +
> +	master->bus_num = pdata->bus_num;
> +	master->num_chipselect = pdata->num_chipselect;
> +	master->setup = bcm63xx_spi_setup;
> +	master->transfer = bcm63xx_transfer;
> +	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);
> +	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
> +
> +	/* register and we are done */
> +	ret = spi_register_master(master);
> +	if (ret) {
> +		dev_err(dev, "spi register failed\n");
> +		goto out_clk_disable;
> +	}
> +
> +	dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n",
> +		 r->start, irq, bs->fifo_size, DRV_VER);
> +
> +	return 0;
> +
> +out_clk_disable:
> +	clk_disable(clk);
> +out_err:
> +	platform_set_drvdata(pdev, NULL);
> +	spi_master_put(master);
> +out_clk:
> +	clk_put(clk);
> +out:
> +	return ret;
> +}
> +
> +static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
> +{
> +	struct spi_master *master = platform_get_drvdata(pdev);
> +	struct bcm63xx_spi *bs = spi_master_get_devdata(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;
> +}
> +
> +#ifdef CONFIG_PM
> +static int bcm63xx_spi_suspend(struct device *dev)
> +{
> +	struct spi_master *master =
> +			platform_get_drvdata(to_platform_device(dev));
> +	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
> +
> +	clk_disable(bs->clk);
> +
> +	return 0;
> +}
> +
> +static int bcm63xx_spi_resume(struct device *dev)
> +{
> +	struct spi_master *master =
> +			platform_get_drvdata(to_platform_device(dev));
> +	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
> +
> +	clk_enable(bs->clk);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
> +	.suspend	= bcm63xx_spi_suspend,
> +	.resume		= bcm63xx_spi_resume,
> +};
> +
> +#define BCM63XX_SPI_PM_OPS	(&bcm63xx_spi_pm_ops)
> +#else
> +#define BCM63XX_SPI_PM_OPS	NULL
> +#endif
> +
> +static struct platform_driver bcm63xx_spi_driver = {
> +	.driver = {
> +		.name	= "bcm63xx-spi",
> +		.owner	= THIS_MODULE,
> +		.pm	= BCM63XX_SPI_PM_OPS,
> +	},
> +	.probe		= bcm63xx_spi_probe,
> +	.remove		= __devexit_p(bcm63xx_spi_remove),
> +};
> +
> +module_platform_driver(bcm63xx_spi_driver);
> +
> +MODULE_ALIAS("platform:bcm63xx_spi");
> +MODULE_AUTHOR("Florian Fainelli <florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>");
> +MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc-HH44TBFINEIAvxtiuMwx3w@public.gmane.org>");
> +MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
> +MODULE_LICENSE("GPL");
> -- 
> 1.7.5.4
> 
> 
> ------------------------------------------------------------------------------
> Keep Your Developer Skills Current with LearnDevNow!
> The most comprehensive online learning library for Microsoft developers
> is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
> Metro Style Apps, more. Free future releases when you subscribe now!
> http://p.sf.net/sfu/learndevnow-d2d
> _______________________________________________
> spi-devel-general mailing list
> spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
> https://lists.sourceforge.net/lists/listinfo/spi-devel-general

-- 
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies,Ltd.

------------------------------------------------------------------------------
Virtualization & Cloud Management Using Capacity Planning
Cloud computing makes use of virtualization - but cloud computing 
also focuses on allowing computing to be delivered as a service.
http://www.accelacomm.com/jaw/sfnl/114/51521223/

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v4] spi: add Broadcom BCM63xx SPI controller driver
  2012-03-09 22:04         ` Grant Likely
@ 2012-03-12  8:25           ` Florian Fainelli
  0 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-03-12  8:25 UTC (permalink / raw)
  To: Grant Likely; +Cc: ralf, Tanguy Bouzeloc, linux-mips, spi-devel-general

Hi Grant,

Le 03/09/12 23:04, Grant Likely a écrit :
> On Wed,  1 Feb 2012 11:14:09 +0100, Florian Fainelli<florian@openwrt.org>  wrote:
>> This patch adds support for the SPI controller found on the Broadcom BCM63xx
>> SoCs.
>>
>> Signed-off-by: Tanguy Bouzeloc<tanguy.bouzeloc@efixo.com>
>> Signed-off-by: Florian Fainelli<florian@openwrt.org>
>> Acked-by: Grant Likely<grant.likely@secretlab.ca>
> Applied, thanks.
>
> In v3.4, the spi subsystem is gaining core support for spi message queues.
> You should look at migrating this driver to use it in the v3.5 timeframe.

Thanks! I will follow up with a patch taking advantage of the generic 
spi message queuing.

>
> g.
>
>> ---
>> Changes since v3:
>> - changed bcm_spi_readb to use readb accessor instead of readw
>> - fixed multiple __dev{init,exit} annotations
>>
>> Changes since v2:
>> - reworked bcm63xx_spi_setup_transfer to choose closest spi transfer
>>    frequency
>> - removed invalid 25Mhz frequency
>> - fixed some minor checkpatch issues
>>
>> Changes since v1:
>> - switched to the devm_* API which frees resources automatically
>> - switched to dev_pm_ops
>> - use module_platform_driver
>> - remove MODULE_VERSION()
>> - fixed return value when clock is not present using PTR_ERR()
>> - fixed probe() error path to disable clock in case of failure
>>
>>   drivers/spi/Kconfig       |    6 +
>>   drivers/spi/Makefile      |    1 +
>>   drivers/spi/spi-bcm63xx.c |  486 +++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 493 insertions(+), 0 deletions(-)
>>   create mode 100644 drivers/spi/spi-bcm63xx.c
>>
>> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
>> index 3f9a47e..16818ac 100644
>> --- a/drivers/spi/Kconfig
>> +++ b/drivers/spi/Kconfig
>> @@ -94,6 +94,12 @@ config SPI_AU1550
>>   	  If you say yes to this option, support will be included for the
>>   	  PSC SPI controller found on Au1550, Au1200 and Au1300 series.
>>
>> +config SPI_BCM63XX
>> +	tristate "Broadcom BCM63xx SPI controller"
>> +	depends on BCM63XX
>> +	help
>> +          Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
>> +
>>   config SPI_BITBANG
>>   	tristate "Utilities for Bitbanging SPI masters"
>>   	help
>> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
>> index 61c3261..be38f73 100644
>> --- a/drivers/spi/Makefile
>> +++ b/drivers/spi/Makefile
>> @@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o
>>   obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o
>>   obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o
>>   obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o
>> +obj-$(CONFIG_SPI_BCM63XX)		+= spi-bcm63xx.o
>>   obj-$(CONFIG_SPI_BFIN)			+= spi-bfin5xx.o
>>   obj-$(CONFIG_SPI_BFIN_SPORT)		+= spi-bfin-sport.o
>>   obj-$(CONFIG_SPI_BITBANG)		+= spi-bitbang.o
>> diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
>> new file mode 100644
>> index 0000000..f01b264
>> --- /dev/null
>> +++ b/drivers/spi/spi-bcm63xx.c
>> @@ -0,0 +1,486 @@
>> +/*
>> + * Broadcom BCM63xx SPI controller support
>> + *
>> + * Copyright (C) 2009-2011 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
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the
>> + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
>> + */
>> +
>> +#include<linux/kernel.h>
>> +#include<linux/init.h>
>> +#include<linux/clk.h>
>> +#include<linux/io.h>
>> +#include<linux/module.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/delay.h>
>> +#include<linux/interrupt.h>
>> +#include<linux/spi/spi.h>
>> +#include<linux/completion.h>
>> +#include<linux/err.h>
>> +
>> +#include<bcm63xx_dev_spi.h>
>> +
>> +#define PFX		KBUILD_MODNAME
>> +#define DRV_VER		"0.1.2"
>> +
>> +struct bcm63xx_spi {
>> +	spinlock_t		lock;
>> +	int			stopping;
>> +	struct completion	done;
>> +
>> +	void __iomem		*regs;
>> +	int			irq;
>> +
>> +	/* Platform data */
>> +	u32			speed_hz;
>> +	unsigned		fifo_size;
>> +
>> +	/* Data buffers */
>> +	const unsigned char	*tx_ptr;
>> +	unsigned char		*rx_ptr;
>> +
>> +	/* data iomem */
>> +	u8 __iomem		*tx_io;
>> +	const u8 __iomem	*rx_io;
>> +
>> +	int			remaining_bytes;
>> +
>> +	struct clk		*clk;
>> +	struct platform_device	*pdev;
>> +};
>> +
>> +static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
>> +				unsigned int offset)
>> +{
>> +	return bcm_readb(bs->regs + bcm63xx_spireg(offset));
>> +}
>> +
>> +static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
>> +				unsigned int offset)
>> +{
>> +	return bcm_readw(bs->regs + bcm63xx_spireg(offset));
>> +}
>> +
>> +static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
>> +				  u8 value, unsigned int offset)
>> +{
>> +	bcm_writeb(value, bs->regs + bcm63xx_spireg(offset));
>> +}
>> +
>> +static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
>> +				  u16 value, unsigned int offset)
>> +{
>> +	bcm_writew(value, bs->regs + bcm63xx_spireg(offset));
>> +}
>> +
>> +static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
>> +	{ 20000000, SPI_CLK_20MHZ },
>> +	{ 12500000, SPI_CLK_12_50MHZ },
>> +	{  6250000, SPI_CLK_6_250MHZ },
>> +	{  3125000, SPI_CLK_3_125MHZ },
>> +	{  1563000, SPI_CLK_1_563MHZ },
>> +	{   781000, SPI_CLK_0_781MHZ },
>> +	{   391000, SPI_CLK_0_391MHZ }
>> +};
>> +
>> +static int bcm63xx_spi_setup_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);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (spi->chip_select>  spi->master->num_chipselect) {
>> +		dev_err(&spi->dev, "%s, unsupported slave %d\n",
>> +			__func__, spi->chip_select);
>> +		return -EINVAL;
>> +	}
>> +
>> +	/* Find the closest clock configuration */
>> +	for (i = 0; i<  SPI_CLK_MASK; i++) {
>> +		if (hz<= bcm63xx_spi_freq_table[i][0]) {
>> +			clk_cfg = bcm63xx_spi_freq_table[i][1];
>> +			break;
>> +		}
>> +	}
>> +
>> +	/* No matching configuration found, default to lowest */
>> +	if (i == SPI_CLK_MASK)
>> +		clk_cfg = SPI_CLK_0_391MHZ;
>> +
>> +	/* clear existing clock configuration bits of the register */
>> +	reg = bcm_spi_readb(bs, SPI_CLK_CFG);
>> +	reg&= ~SPI_CLK_MASK;
>> +	reg |= clk_cfg;
>> +
>> +	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: */
>> +#define MODEBITS (SPI_CPOL | SPI_CPHA)
>> +
>> +static int bcm63xx_spi_setup(struct spi_device *spi)
>> +{
>> +	struct bcm63xx_spi *bs;
>> +	int ret;
>> +
>> +	bs = spi_master_get_devdata(spi->master);
>> +
>> +	if (bs->stopping)
>> +		return -ESHUTDOWN;
>> +
>> +	if (!spi->bits_per_word)
>> +		spi->bits_per_word = 8;
>> +
>> +	if (spi->mode&  ~MODEBITS) {
>> +		dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
>> +			__func__, spi->mode&  ~MODEBITS);
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = bcm63xx_spi_setup_transfer(spi, NULL);
>> +	if (ret<  0) {
>> +		dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
>> +			spi->mode&  ~MODEBITS);
>> +		return ret;
>> +	}
>> +
>> +	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
>> +		__func__, spi->mode&  MODEBITS, spi->bits_per_word, 0);
>> +
>> +	return 0;
>> +}
>> +
>> +/* Fill the TX FIFO with as many bytes as possible */
>> +static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
>> +{
>> +	u8 size;
>> +
>> +	/* Fill the Tx FIFO with as many bytes as possible */
>> +	size = bs->remaining_bytes<  bs->fifo_size ? bs->remaining_bytes :
>> +		bs->fifo_size;
>> +	memcpy_toio(bs->tx_io, bs->tx_ptr, size);
>> +	bs->remaining_bytes -= size;
>> +}
>> +
>> +static 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;
>> +
>> +	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);
>> +
>> +	/* Fill in the Message control register */
>> +	msg_ctl = (t->len<<  SPI_BYTE_CNT_SHIFT);
>> +
>> +	if (t->rx_buf&&  t->tx_buf)
>> +		msg_ctl |= (SPI_FD_RW<<  SPI_MSG_TYPE_SHIFT);
>> +	else if (t->rx_buf)
>> +		msg_ctl |= (SPI_HD_R<<  SPI_MSG_TYPE_SHIFT);
>> +	else if (t->tx_buf)
>> +		msg_ctl |= (SPI_HD_W<<  SPI_MSG_TYPE_SHIFT);
>> +
>> +	bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
>> +
>> +	/* Issue the transfer */
>> +	cmd = SPI_CMD_START_IMMEDIATE;
>> +	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);
>> +
>> +	return t->len - bs->remaining_bytes;
>> +}
>> +
>> +static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m)
>> +{
>> +	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
>> +	struct spi_transfer *t;
>> +	int ret = 0;
>> +
>> +	if (unlikely(list_empty(&m->transfers)))
>> +		return -EINVAL;
>> +
>> +	if (bs->stopping)
>> +		return -ESHUTDOWN;
>> +
>> +	list_for_each_entry(t,&m->transfers, transfer_list) {
>> +		ret += bcm63xx_txrx_bufs(spi, t);
>> +	}
>> +
>> +	m->complete(m->context);
>> +
>> +	return ret;
>> +}
>> +
>> +/* This driver supports single master mode only. Hence
>> + * CMD_DONE is the only interrupt we care about
>> + */
>> +static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
>> +{
>> +	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);
>> +		}
>> +	}
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +
>> +static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
>> +{
>> +	struct resource *r;
>> +	struct device *dev =&pdev->dev;
>> +	struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
>> +	int irq;
>> +	struct spi_master *master;
>> +	struct clk *clk;
>> +	struct bcm63xx_spi *bs;
>> +	int ret;
>> +
>> +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!r) {
>> +		dev_err(dev, "no iomem\n");
>> +		ret = -ENXIO;
>> +		goto out;
>> +	}
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	if (irq<  0) {
>> +		dev_err(dev, "no irq\n");
>> +		ret = -ENXIO;
>> +		goto out;
>> +	}
>> +
>> +	clk = clk_get(dev, "spi");
>> +	if (IS_ERR(clk)) {
>> +		dev_err(dev, "no clock for device\n");
>> +		ret = PTR_ERR(clk);
>> +		goto out;
>> +	}
>> +
>> +	master = spi_alloc_master(dev, sizeof(*bs));
>> +	if (!master) {
>> +		dev_err(dev, "out of memory\n");
>> +		ret = -ENOMEM;
>> +		goto out_clk;
>> +	}
>> +
>> +	bs = spi_master_get_devdata(master);
>> +	init_completion(&bs->done);
>> +
>> +	platform_set_drvdata(pdev, master);
>> +	bs->pdev = pdev;
>> +
>> +	if (!devm_request_mem_region(&pdev->dev, r->start,
>> +					resource_size(r), PFX)) {
>> +		dev_err(dev, "iomem request failed\n");
>> +		ret = -ENXIO;
>> +		goto out_err;
>> +	}
>> +
>> +	bs->regs = devm_ioremap_nocache(&pdev->dev, r->start,
>> +							resource_size(r));
>> +	if (!bs->regs) {
>> +		dev_err(dev, "unable to ioremap regs\n");
>> +		ret = -ENOMEM;
>> +		goto out_err;
>> +	}
>> +
>> +	bs->irq = irq;
>> +	bs->clk = clk;
>> +	bs->fifo_size = pdata->fifo_size;
>> +
>> +	ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
>> +							pdev->name, master);
>> +	if (ret) {
>> +		dev_err(dev, "unable to request irq\n");
>> +		goto out_err;
>> +	}
>> +
>> +	master->bus_num = pdata->bus_num;
>> +	master->num_chipselect = pdata->num_chipselect;
>> +	master->setup = bcm63xx_spi_setup;
>> +	master->transfer = bcm63xx_transfer;
>> +	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);
>> +	bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
>> +
>> +	/* register and we are done */
>> +	ret = spi_register_master(master);
>> +	if (ret) {
>> +		dev_err(dev, "spi register failed\n");
>> +		goto out_clk_disable;
>> +	}
>> +
>> +	dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n",
>> +		 r->start, irq, bs->fifo_size, DRV_VER);
>> +
>> +	return 0;
>> +
>> +out_clk_disable:
>> +	clk_disable(clk);
>> +out_err:
>> +	platform_set_drvdata(pdev, NULL);
>> +	spi_master_put(master);
>> +out_clk:
>> +	clk_put(clk);
>> +out:
>> +	return ret;
>> +}
>> +
>> +static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
>> +{
>> +	struct spi_master *master = platform_get_drvdata(pdev);
>> +	struct bcm63xx_spi *bs = spi_master_get_devdata(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;
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static int bcm63xx_spi_suspend(struct device *dev)
>> +{
>> +	struct spi_master *master =
>> +			platform_get_drvdata(to_platform_device(dev));
>> +	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
>> +
>> +	clk_disable(bs->clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int bcm63xx_spi_resume(struct device *dev)
>> +{
>> +	struct spi_master *master =
>> +			platform_get_drvdata(to_platform_device(dev));
>> +	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
>> +
>> +	clk_enable(bs->clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
>> +	.suspend	= bcm63xx_spi_suspend,
>> +	.resume		= bcm63xx_spi_resume,
>> +};
>> +
>> +#define BCM63XX_SPI_PM_OPS	(&bcm63xx_spi_pm_ops)
>> +#else
>> +#define BCM63XX_SPI_PM_OPS	NULL
>> +#endif
>> +
>> +static struct platform_driver bcm63xx_spi_driver = {
>> +	.driver = {
>> +		.name	= "bcm63xx-spi",
>> +		.owner	= THIS_MODULE,
>> +		.pm	= BCM63XX_SPI_PM_OPS,
>> +	},
>> +	.probe		= bcm63xx_spi_probe,
>> +	.remove		= __devexit_p(bcm63xx_spi_remove),
>> +};
>> +
>> +module_platform_driver(bcm63xx_spi_driver);
>> +
>> +MODULE_ALIAS("platform:bcm63xx_spi");
>> +MODULE_AUTHOR("Florian Fainelli<florian@openwrt.org>");
>> +MODULE_AUTHOR("Tanguy Bouzeloc<tanguy.bouzeloc@efixo.com>");
>> +MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
>> +MODULE_LICENSE("GPL");
>> -- 
>> 1.7.5.4
>>
>>
>> ------------------------------------------------------------------------------
>> Keep Your Developer Skills Current with LearnDevNow!
>> The most comprehensive online learning library for Microsoft developers
>> is just $99.99! Visual Studio, SharePoint, SQL - plus HTML5, CSS3, MVC3,
>> Metro Style Apps, more. Free future releases when you subscribe now!
>> http://p.sf.net/sfu/learndevnow-d2d
>> _______________________________________________
>> spi-devel-general mailing list
>> spi-devel-general@lists.sourceforge.net
>> https://lists.sourceforge.net/lists/listinfo/spi-devel-general

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI
       [not found] ` <1328019048-5892-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
@ 2012-05-30 10:04   ` Florian Fainelli
  0 siblings, 0 replies; 19+ messages in thread
From: Florian Fainelli @ 2012-05-30 10:04 UTC (permalink / raw)
  To: ralf-6z/3iImG2C8G8FEW9MqTrA
  Cc: linux-mips-6z/3iImG2C8G8FEW9MqTrA,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

Ralf,

On Tuesday 31 January 2012 15:10:39 Florian Fainelli wrote:
> This patch set depend on the serie "MIPS: BCM63XX: misc cleanup" and
> adds support for the SPI controller found in BCM63xx SoCs.
> 
> Grant, it probably makes sense to get this merged via the MIPS tree
> since it mostly depends on it.

Grant merged the SPI driver a while ago during 3.4, and now we can't build 
this driver because the platform-specific knobs have not been merged.

The only complains where from Maxime about the SPI driver itself, not the 
other changes, can you merge this series without the latest patch?

Thanks!

> 
> Florian Fainelli (9):
>   MIPS: BCM63XX: add IRQ_SPI and CPU specific SPI IRQ values
>   MIPS: BCM63XX: define BCM6358 SPI base address
>   MIPS: BCM63XX: add BCM6368 SPI clock mask
>   MIPS: BCM63XX: define SPI register sizes.
>   MIPS: BCM63XX: remove SPI2 register
>   MIPS: BCM63XX: define internal registers offsets of the SPI
>     controller
>   MIPS: BCM63XX: add stub to register the SPI platform driver
>   MIPS: BCM63XX: make board setup code register the spi platform device
>   spi: add Broadcom BCM63xx SPI controller driver
> 
>  arch/mips/bcm63xx/Makefile                         |    3 +-
>  arch/mips/bcm63xx/boards/board_bcm963xx.c          |    3 +
>  arch/mips/bcm63xx/clk.c                            |    6 +-
>  arch/mips/bcm63xx/dev-spi.c                        |  119 +++++
>  arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h   |   23 +-
>  .../include/asm/mach-bcm63xx/bcm63xx_dev_spi.h     |   89 ++++
>  arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h  |  119 +++++
>  drivers/spi/Kconfig                                |    6 +
>  drivers/spi/Makefile                               |    1 +
>  drivers/spi/spi-bcm63xx.c                          |  486 
++++++++++++++++++++
>  10 files changed, 842 insertions(+), 13 deletions(-)
>  create mode 100644 arch/mips/bcm63xx/dev-spi.c
>  create mode 100644 arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h
>  create mode 100644 drivers/spi/spi-bcm63xx.c
> 
> -- 
> 1.7.5.4
> 

------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and 
threat landscape has changed and how IT managers can respond. Discussions 
will include endpoint security, mobile security and the latest in malware 
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/

^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2012-05-30 10:04 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-01-31 14:10 [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli
2012-01-31 14:10 ` [PATCH 1/9 v3] MIPS: BCM63XX: add IRQ_SPI and CPU specific SPI IRQ values Florian Fainelli
2012-01-31 14:10 ` [PATCH 2/9 v3] MIPS: BCM63XX: define BCM6358 SPI base address Florian Fainelli
2012-01-31 14:10 ` [PATCH 3/9 v3] MIPS: BCM63XX: add BCM6368 SPI clock mask Florian Fainelli
2012-01-31 14:10 ` [PATCH 4/9 v3] MIPS: BCM63XX: define SPI register sizes Florian Fainelli
2012-01-31 14:10 ` [PATCH 5/9 v3] MIPS: BCM63XX: remove SPI2 register Florian Fainelli
2012-01-31 14:10 ` [PATCH 6/9 v3] MIPS: BCM63XX: define internal registers offsets of the SPI controller Florian Fainelli
2012-01-31 14:10 ` [PATCH 7/9 v3] MIPS: BCM63XX: add stub to register the SPI platform driver Florian Fainelli
2012-01-31 14:10 ` [PATCH 8/9 v3] MIPS: BCM63XX: make board setup code register the spi platform device Florian Fainelli
2012-01-31 14:10 ` [PATCH 9/9 v3] spi: add Broadcom BCM63xx SPI controller driver Florian Fainelli
     [not found]   ` <1328019048-5892-10-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2012-01-31 17:52     ` Shubhrajyoti Datta
2012-02-01 10:14     ` [PATCH v4] " Florian Fainelli
2012-02-01 11:22       ` Maxime Bizon
     [not found]       ` <1328091249-10389-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2012-03-09 22:04         ` Grant Likely
2012-03-12  8:25           ` Florian Fainelli
2012-01-31 20:19   ` [PATCH 9/9 v3] " Grant Likely
     [not found]     ` <20120131201922.GE22611-e0URQFbLeQY2iJbIjFUEsiwD8/FfD2ys@public.gmane.org>
2012-01-31 21:20       ` Florian Fainelli
     [not found]         ` <201201312220.41561.florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2012-01-31 21:59           ` Grant Likely
     [not found] ` <1328019048-5892-1-git-send-email-florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2012-05-30 10:04   ` [PATCH 0/9 v3] MIPS: BCM63XX: add support for SPI Florian Fainelli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).