Skip to contentSkip to conversation

spi: driver for Cirrus EP93xx SPI controller

PATCH Superseded Mika Westerberg 4 participants 14 messages May 2, 2010 – May 11, 2010

Subsystems (4)

v6 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v6 cover spi: driver for Cirrus EP93xx SPI controller

Hello,

This series implements SPI master driver for Cirrus Logic EP93xx SPI controllers.

This is sixth iteration of the driver.

Changes to the previous version:
- addressed most review comments by H. Hartley Sweeten
- incorporated GPIO chipselect patch by H. Hartley Sweeten

I tested this on TS-7260 board with at25 and mmc_spi drivers. Now that I have Sim.One board, which is EP9307 based, I also tested with that (I hacked the board a bit to get EGPIO9 as a chip select).

Note that patch 2/2 depends on patch that is already in Russell's patch tracking system:

	http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=5998/1

Thanks,
MW

Mika Westerberg (2):
spi: implemented driver for Cirrus EP93xx SPI controller
ep93xx: SPI driver platform support code

 Documentation/spi/ep93xx_spi                    |   95 +++
 arch/arm/mach-ep93xx/clock.c                    |   13 +
 arch/arm/mach-ep93xx/core.c                     |   53 ++
 arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h |    1 +
 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h  |   27 +
 arch/arm/mach-ep93xx/include/mach/platform.h    |    4 +
 drivers/spi/Kconfig                             |   10 +
 drivers/spi/Makefile                            |    1 +
 drivers/spi/ep93xx_spi.c                        |  986 +++++++++++++++++++++++
 9 files changed, 1190 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/spi/ep93xx_spi
 create mode 100644 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
 create mode 100644 drivers/spi/ep93xx_spi.c
#1 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

This patch adds an SPI master driver for the Cirrus EP93xx SPI controller found in EP93xx chips.

---
 Documentation/spi/ep93xx_spi                   |   95 +++
 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |   27 +
 drivers/spi/Kconfig                            |   10 +
 drivers/spi/Makefile                           |    1 +
 drivers/spi/ep93xx_spi.c                       |  986 ++++++++++++++++++++++++
 5 files changed, 1119 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/spi/ep93xx_spi
 create mode 100644 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
 create mode 100644 drivers/spi/ep93xx_spi.c
Documentation/spi/ep93xx_spi new file +95 -0
new file mode 100644
index 0000000..6325f5b
@@ -0,0 +1,95 @@
+Cirrus EP93xx SPI controller driver HOWTO
+=========================================
+
+ep93xx_spi driver brings SPI master support for EP93xx SPI controller.  Chip
+selects are implemented with GPIO lines.
+
+NOTE: If possible, don't use SFRMOUT (SFRM1) signal as a chip select. It will
+not work correctly (it cannot be controlled by software). Use GPIO lines
+instead.
+
+Sample configuration
+====================
+
+Typically driver configuration is done in platform board files (the files under
+arch/arm/mach-ep93xx/*.c). In this example we configure MMC over SPI through
+this driver on TS-7260 board. You can adapt the code to suit your needs.
+
+This example uses EGPIO9 as SD/MMC card chip select (this is wired in DIO1
+header on the board).
+
+You need to select CONFIG_MMC_SPI to use mmc_spi driver.
+
+arch/arm/mach-ep93xx/ts72xx.c:
+
+...
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <mach/ep93xx_spi.h>
+
+/* this is our GPIO line used for chip select */
+#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9
+
+static int ts72xx_mmc_spi_setup(struct spi_device *spi)
+{
+	int err;
+
+	err = gpio_request(MMC_CHIP_SELECT_GPIO, spi->modalias);
+	if (err)
+		return err;
+
+	gpio_direction_output(MMC_CHIP_SELECT_GPIO, 1);
+
+	return 0;
+}
+
+static void ts72xx_mmc_spi_cleanup(struct spi_device *spi)
+{
+	gpio_set_value(MMC_CHIP_SELECT_GPIO, 1);
+	gpio_direction_input(MMC_CHIP_SELECT_GPIO);
+	gpio_free(MMC_CHIP_SELECT_GPIO);
+}
+
+static void ts72xx_mmc_spi_cs_control(struct spi_device *spi, int value)
+{
+	gpio_set_value(MMC_CHIP_SELECT_GPIO, value);
+}
+
+static struct ep93xx_spi_chip_ops ts72xx_mmc_spi_ops = {
+	.setup		= ts72xx_mmc_spi_setup,
+	.cleanup	= ts72xx_mmc_spi_cleanup,
+	.cs_control	= ts72xx_mmc_spi_cs_control,
+};
+
+static struct spi_board_info ts72xx_spi_devices[] __initdata = {
+	{
+		.modalias		= "mmc_spi",
+		.controller_data	= &ts72xx_mmc_spi_ops,
+		/*
+		 * We use 10 MHz even though the maximum is 7.4 MHz. The driver
+		 * will limit it automatically to max. frequency.
+		 */
+		.max_speed_hz		= 10 * 1000 * 1000,
+		.bus_num		= 0,
+		.chip_select		= 0,
+		.mode			= SPI_MODE_0,
+	},
+};
+
+static struct ep93xx_spi_info ts72xx_spi_info = {
+	.num_chipselect	= ARRAY_SIZE(ts72xx_spi_devices),
+};
+
+static void __init ts72xx_init_machine(void)
+{
+	...
+	ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices,
+			    ARRAY_SIZE(ts72xx_spi_devices));
+}
+
+Thanks to
+=========
+Martin Guy, H. Hartley Sweeten and others who helped me during development of
+the driver. Simplemachines.it donated me a Sim.One board which I used testing
+the driver on EP9307.
arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h new file +27 -0
new file mode 100644
index 0000000..0a37961
@@ -0,0 +1,27 @@
+#ifndef __ASM_MACH_EP93XX_SPI_H
+#define __ASM_MACH_EP93XX_SPI_H
+
+struct spi_device;
+
+/**
+ * struct ep93xx_spi_info - EP93xx specific SPI descriptor
+ * @num_chipselect: number of chip selects on this board, must be
+ *                  at least one
+ */
+struct ep93xx_spi_info {
+	int	num_chipselect;
+};
+
+/**
+ * struct ep93xx_spi_chip_ops - operation callbacks for SPI slave device
+ * @setup: setup the chip select mechanism
+ * @cleanup: cleanup the chip select mechanism
+ * @cs_control: control the device chip select
+ */
+struct ep93xx_spi_chip_ops {
+	int	(*setup)(struct spi_device *spi);
+	void	(*cleanup)(struct spi_device *spi);
+	void	(*cs_control)(struct spi_device *spi, int value);
+};
+
+#endif /* __ASM_MACH_EP93XX_SPI_H */
drivers/spi/Kconfig +10 -0
index a191fa2..2b2f4c3 100644
@@ -117,6 +117,16 @@ config SPI_DAVINCI
 	help
 	  SPI master controller for DaVinci and DA8xx SPI modules.
 
+config SPI_EP93XX
+	tristate "Cirrus Logic EP93xx SPI controller"
+	depends on ARCH_EP93XX
+	help
+	  This enables using the Cirrus EP93xx SPI controller in master
+	  mode.
+
+	  To compile this driver as a module, choose M here. The module will be
+	  called ep93xx_spi.
+
 config SPI_GPIO
 	tristate "GPIO-based bitbanging SPI Master"
 	depends on GENERIC_GPIO
drivers/spi/Makefile +1 -0
index d7d0f89..377f845 100644
@@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_DAVINCI)		+= davinci_spi.o
 obj-$(CONFIG_SPI_DESIGNWARE)		+= dw_spi.o
 obj-$(CONFIG_SPI_DW_PCI)		+= dw_spi_pci.o
 obj-$(CONFIG_SPI_DW_MMIO)		+= dw_spi_mmio.o
+obj-$(CONFIG_SPI_EP93XX)		+= ep93xx_spi.o
 obj-$(CONFIG_SPI_GPIO)			+= spi_gpio.o
 obj-$(CONFIG_SPI_IMX)			+= spi_imx.o
 obj-$(CONFIG_SPI_LM70_LLP)		+= spi_lm70llp.o
drivers/spi/ep93xx_spi.c new file +986 -0

986 lines not on this page · raw message

Signature · 1 line
1.5.6.5
#2 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

2010/5/2 Mika Westerberg <[email protected]>:

Quoted 2 lines

This patch adds an SPI master driver for the Cirrus EP93xx SPI controller found in EP93xx chips.

(...)

Quoted 18 lines
(quoted diff) +18 -0
+#define SSPCR0 ? ? ? ? ? ? ? ? 0x0000
+#define SSPCR0_MODE_SHIFT ? ? ?6
+#define SSPCR0_SCR_SHIFT ? ? ? 8
+
+#define SSPCR1 ? ? ? ? ? ? ? ? 0x0004
+#define SSPCR1_RIE ? ? ? ? ? ? BIT(0)
+#define SSPCR1_TIE ? ? ? ? ? ? BIT(1)
+#define SSPCR1_RORIE ? ? ? ? ? BIT(2)
+#define SSPCR1_LBM ? ? ? ? ? ? BIT(3)
+#define SSPCR1_SSE ? ? ? ? ? ? BIT(4)
+#define SSPCR1_MS ? ? ? ? ? ? ?BIT(5)
+#define SSPCR1_SOD ? ? ? ? ? ? BIT(6)
+
+#define SSPDR ? ? ? ? ? ? ? ? ?0x0008
+
+#define SSPSR ? ? ? ? ? ? ? ? ?0x000c
+#define SSPSR_TFE ? ? ? ? ? ? ?BIT(0)
+#define SSPSR_TNF ? ? ? ? ? ? ?BIT(1)

Review

Quoted 10 lines
(quoted diff) +10 -0
+#define SSPSR_RNE ? ? ? ? ? ? ?BIT(2)
+#define SSPSR_RFF ? ? ? ? ? ? ?BIT(3)
+#define SSPSR_BSY ? ? ? ? ? ? ?BIT(4)
+#define SSPCPSR ? ? ? ? ? ? ? ? ? ? ? ?0x0010
+
+#define SSPIIR ? ? ? ? ? ? ? ? 0x0014
+#define SSPIIR_RIS ? ? ? ? ? ? BIT(0)
+#define SSPIIR_TIS ? ? ? ? ? ? BIT(1)
+#define SSPIIR_RORIS ? ? ? ? ? BIT(2)
+#define SSPICR ? ? ? ? ? ? ? ? SSPIIR

When I look at this it's quite obvious that this is the same hardware or very close (maybe a redux version) of the thing supported by drivers/spi/amba-pl022.c.

What is the difference really? I suspect this is a PL022 PrimeCell straight off. Even the CPSR algorithm is the same, just written differently.

Can you make a hexdump of the PrimeCell ID registers at offset base+0xffe0..0xffff and post the contents?

Yours,
Linus Walleij

#3 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

Quoted 29 lines · Linus Walleij

2010/5/2 Mika Westerberg <[email protected]>:

Quoted 2 lines

This patch adds an SPI master driver for the Cirrus EP93xx SPI controller found in EP93xx chips.

(...)

Quoted 24 lines
(quoted diff) +24 -0
+#define SSPCR0 ? ? ? ? ? ? ? ? 0x0000
+#define SSPCR0_MODE_SHIFT ? ? ?6
+#define SSPCR0_SCR_SHIFT ? ? ? 8
+
+#define SSPCR1 ? ? ? ? ? ? ? ? 0x0004
+#define SSPCR1_RIE ? ? ? ? ? ? BIT(0)
+#define SSPCR1_TIE ? ? ? ? ? ? BIT(1)
+#define SSPCR1_RORIE ? ? ? ? ? BIT(2)
+#define SSPCR1_LBM ? ? ? ? ? ? BIT(3)
+#define SSPCR1_SSE ? ? ? ? ? ? BIT(4)
+#define SSPCR1_MS ? ? ? ? ? ? ?BIT(5)
+#define SSPCR1_SOD ? ? ? ? ? ? BIT(6)
+
+#define SSPDR ? ? ? ? ? ? ? ? ?0x0008
+
+#define SSPSR ? ? ? ? ? ? ? ? ?0x000c
+#define SSPSR_TFE ? ? ? ? ? ? ?BIT(0)
+#define SSPSR_TNF ? ? ? ? ? ? ?BIT(1)
+#define SSPSR_RNE ? ? ? ? ? ? ?BIT(2)
+#define SSPSR_RFF ? ? ? ? ? ? ?BIT(3)
+#define SSPSR_BSY ? ? ? ? ? ? ?BIT(4)
+#define SSPCPSR ? ? ? ? ? ? ? ? ? ? ? ?0x0010
+
+#define SSPIIR ? ? ? ? ? ? ? ? 0x0014
Quoted 8 lines

Review

Quoted 4 lines
(quoted diff) +4 -0
+#define SSPIIR_RIS ? ? ? ? ? ? BIT(0)
+#define SSPIIR_TIS ? ? ? ? ? ? BIT(1)
+#define SSPIIR_RORIS ? ? ? ? ? BIT(2)
+#define SSPICR ? ? ? ? ? ? ? ? SSPIIR

When I look at this it's quite obvious that this is the same hardware or very close (maybe a redux version) of the thing supported by drivers/spi/amba-pl022.c.

Yeah. It looks pretty similar. Although there seems to be differences. Documentation of the controller can be found in:

	http://www.cirrus.com/en/pubs/manual/EP93xx_Users_Guide_UM1.pdf

It does not mention anything about AMBA PL022.

Quoted 2 lines

What is the difference really? I suspect this is a PL022 PrimeCell straight off. Even the CPSR algorithm is the same, just written differently.

There are differences, for example PL022 has 4 interrupt registers (SSPIMSC, SSPRIS, SSPMIS and SSPICR) whereas EP93xx has only one. SSPCR1 is different (in EP93xx we have interrupt enable/disable bits there) etc.

Quoted 2 lines

Can you make a hexdump of the PrimeCell ID registers at offset base+0xffe0..0xffff and post the contents?

Here it is, you probably meant base + 0xfe0 .. 0xfff, right?

[    2.410000] 0xc6070fe0: 0x0
[    2.410000] 0xc6070fe4: 0x0
[    2.410000] 0xc6070fe8: 0x0
[    2.420000] 0xc6070fec: 0x0
[    2.420000] 0xc6070ff0: 0x0
[    2.420000] 0xc6070ff4: 0x0
[    2.430000] 0xc6070ff8: 0x0
[    2.430000] 0xc6070ffc: 0x0

Thanks,
MW

#4 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

2010/5/3 Mika Westerberg <[email protected]>:

Quoted 6 lines

Yeah. It looks pretty similar. Although there seems to be differences. Documentation of the controller can be found in:

? ? ? ?http://www.cirrus.com/en/pubs/manual/EP93xx_Users_Guide_UM1.pdf

It does not mention anything about AMBA PL022.

They have surely based the hardware on the PL022. A diff document would surely be useful, can Cirrus answer that kind of questions for you?

Hm, it has to be *really* different to warrant a totally new driver duplicate code is never fun...

Quoted 5 lines
Quoted 2 lines

Can you make a hexdump of the PrimeCell ID registers at offset base+0xffe0..0xffff and post the contents?

Here it is, you probably meant base + 0xfe0 .. 0xfff, right?

Quoted 8 lines

[ ? ?2.410000] 0xc6070fe0: 0x0 [ ? ?2.410000] 0xc6070fe4: 0x0 [ ? ?2.410000] 0xc6070fe8: 0x0 [ ? ?2.420000] 0xc6070fec: 0x0 [ ? ?2.420000] 0xc6070ff0: 0x0 [ ? ?2.420000] 0xc6070ff4: 0x0 [ ? ?2.430000] 0xc6070ff8: 0x0 [ ? ?2.430000] 0xc6070ffc: 0x0

Looks like it's been blanked out so atleast it is not identifying itself as a PL022 anymore.

It could also be that PL022 and the Cirrus controller has some common ancestor...

Yours,
Linus Walleij

#5 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

In reply to Linus Walleij
Quoted 37 lines · Linus Walleij

2010/5/2 Mika Westerberg <[email protected]>:

Quoted 2 lines

This patch adds an SPI master driver for the Cirrus EP93xx SPI controller found in EP93xx chips.

(...)

Quoted 18 lines
(quoted diff) +18 -0
+#define SSPCR0 ? ? ? ? ? ? ? ? 0x0000
+#define SSPCR0_MODE_SHIFT ? ? ?6
+#define SSPCR0_SCR_SHIFT ? ? ? 8
+
+#define SSPCR1 ? ? ? ? ? ? ? ? 0x0004
+#define SSPCR1_RIE ? ? ? ? ? ? BIT(0)
+#define SSPCR1_TIE ? ? ? ? ? ? BIT(1)
+#define SSPCR1_RORIE ? ? ? ? ? BIT(2)
+#define SSPCR1_LBM ? ? ? ? ? ? BIT(3)
+#define SSPCR1_SSE ? ? ? ? ? ? BIT(4)
+#define SSPCR1_MS ? ? ? ? ? ? ?BIT(5)
+#define SSPCR1_SOD ? ? ? ? ? ? BIT(6)
+
+#define SSPDR ? ? ? ? ? ? ? ? ?0x0008
+
+#define SSPSR ? ? ? ? ? ? ? ? ?0x000c
+#define SSPSR_TFE ? ? ? ? ? ? ?BIT(0)
+#define SSPSR_TNF ? ? ? ? ? ? ?BIT(1)

Review

Quoted 10 lines
(quoted diff) +10 -0
+#define SSPSR_RNE ? ? ? ? ? ? ?BIT(2)
+#define SSPSR_RFF ? ? ? ? ? ? ?BIT(3)
+#define SSPSR_BSY ? ? ? ? ? ? ?BIT(4)
+#define SSPCPSR ? ? ? ? ? ? ? ? ? ? ? ?0x0010
+
+#define SSPIIR ? ? ? ? ? ? ? ? 0x0014
+#define SSPIIR_RIS ? ? ? ? ? ? BIT(0)
+#define SSPIIR_TIS ? ? ? ? ? ? BIT(1)
+#define SSPIIR_RORIS ? ? ? ? ? BIT(2)
+#define SSPICR ? ? ? ? ? ? ? ? SSPIIR

When I look at this it's quite obvious that this is the same hardware or very close (maybe a redux version) of the thing supported by drivers/spi/amba-pl022.c.

Quoted 5 lines

What is the difference really? I suspect this is a PL022 PrimeCell straight off. Even the CPSR algorithm is the same, just written differently.

Can you make a hexdump of the PrimeCell ID registers at offset base+0xffe0..0xffff and post the contents?

Linus,

As Mika already pointed out, the ep93xx ssp peripheral does not have a valid PeripheralID or PrimeCellID to identify it as an AMBA PL022 peripheral.

It probably was originally based on the PL022 but I think Cirrus hacked it enough that the generic amba-pl022 driver will not work with it, at least without a number of ugly #ifdef hacks.

Or, I could be wrong. It appears the amba-pl022 driver doesn't actually match the ARM spec for the PL022.

#define SSP_CR0(r) (r + 0x000)

/*
* SSP Control Register 0 - SSP_CR0
*/
#define SSP_CR0_MASK_DSS (0x1FUL << 0)
#define SSP_CR0_MASK_HALFDUP (0x1UL << 5)
#define SSP_CR0_MASK_SPO (0x1UL << 6)
#define SSP_CR0_MASK_SPH (0x1UL << 7)
#define SSP_CR0_MASK_SCR (0xFFUL << 8)
#define SSP_CR0_MASK_CSS (0x1FUL << 16)
#define SSP_CR0_MASK_FRF (0x3UL << 21)

The spec says that the DSS bits are only bits 3:0 (16-bit transfers), the driver has them as bits 4:0 (32-bit transfers).
HALFDUP is not listed in the spec, and FRF is listed as bits 5:4. CSS is not listed in the spec.

The PL022 spec actually matches the EP93xx usage.

#define SSP_CR1(r) (r + 0x004)

/*
* SSP Control Register 0 - SSP_CR1
*/
#define SSP_CR1_MASK_LBM (0x1UL << 0)
#define SSP_CR1_MASK_SSE (0x1UL << 1)
#define SSP_CR1_MASK_MS (0x1UL << 2)
#define SSP_CR1_MASK_SOD (0x1UL << 3)
#define SSP_CR1_MASK_RENDN (0x1UL << 4)
#define SSP_CR1_MASK_TENDN (0x1UL << 5)
#define SSP_CR1_MASK_MWAIT (0x1UL << 6)
#define SSP_CR1_MASK_RXIFLSEL (0x7UL << 7)
#define SSP_CR1_MASK_TXIFLSEL (0x7UL << 10)

Bits 15:4 are listed as reserved in the spec, bits 3:0 match the spec. The EP93xx usage of this register is different.

0  RIE    Receive FIFO interrupt enable
1  TIE    Transmit FIFO interrupt enable
2  RORIE  Receive FIFO overrun interrupt enable
3  LBM    Loop back mode
4  SSE    Synchronous serial port enable
5  MS     Master / Slave mode select
6  SOD    Slave-mode output disable

#define SSP_DR(r) (r + 0x008)

/*
* SSP Data Register - SSP_DR
*/
#define SSP_DR_MASK_DATA 0xFFFFFFFF

The PL022 spec only shows the peripheral as supporting up to 16-bit transfers. This is the limit for the EP93xx also but the amba-pl022 driver actually supports up to 32-bit transfers.

#define SSP_SR(r) (r + 0x00C)

/*
* SSP Status Register - SSP_SR
*/
#define SSP_SR_MASK_TFE (0x1UL << 0) /* Transmit FIFO empty */
#define SSP_SR_MASK_TNF (0x1UL << 1) /* Transmit FIFO not full */
#define SSP_SR_MASK_RNE (0x1UL << 2) /* Receive FIFO not empty */
#define SSP_SR_MASK_RFF (0x1UL << 3) /* Receive FIFO full */
#define SSP_SR_MASK_BSY (0x1UL << 4) /* Busy Flag */

#define SSP_CPSR(r) (r + 0x010)

/*
* SSP Clock Prescale Register - SSP_CPSR
*/
#define SSP_CPSR_MASK_CPSDVSR (0xFFUL << 0)

These match the spec and the EP93xx usage.

#define SSP_IMSC(r) (r + 0x014)

/*
* SSP Interrupt Mask Set/Clear Register - SSP_IMSC
*/
#define SSP_IMSC_MASK_RORIM (0x1UL << 0) /* Receive Overrun Interrupt mask */

#define SSP_IMSC_MASK_RTIM  (0x1UL << 1) /* Receive timeout Interrupt mask */
#define SSP_IMSC_MASK_RXIM  (0x1UL << 2) /* Receive FIFO Interrupt mask */
#define SSP_IMSC_MASK_TXIM  (0x1UL << 3) /* Transmit FIFO Interrupt mask */

In the EP93xx this register is called SSPIIR / SSPICR. It is the interrupt status register for reads and the interrupt clear register for writes. For reads it is defined as:

0  RIS    SSP Receive FIFO service request
1  TIS    SSP Transmit FIFO service request
2  RORIS  SSP Receive FIFO overrun interrupt status

For writes, any value written clears the RORIS bit.

The other registers listed for the amba-pl022 do not exist in the EP93xx implementation.

The biggest problem I can see trying to use the amba-pl022 with the ep93xx are the missing Peripheral ID and PrimeCell ID registers. The AMBA bus driver will not be able to match the driver to the device.

Beyond that, once the amba-pl022 driver supports DMA transfers the implementation needed for the EP93xx will be completely different.

I think it's best to leave these as separate drivers. Cirrus has stopped development of their ARM processors so we have no hope of ever getting a full pl022 compatible version.

Regards,
Hartley

#6 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

2010/5/3 H Hartley Sweeten <[email protected]>:

Quoted 6 lines · Linus Walleij
Quoted 5 lines

When I look at this it's quite obvious that this is the same hardware or very close (maybe a redux version) of the thing supported by drivers/spi/amba-pl022.c.

Quoted 8 lines
Quoted 5 lines

What is the difference really? I suspect this is a PL022 PrimeCell straight off. Even the CPSR algorithm is the same, just written differently.

Can you make a hexdump of the PrimeCell ID registers at offset base+0xffe0..0xffff and post the contents?

As Mika already pointed out, the ep93xx ssp peripheral does not have a valid PeripheralID or PrimeCellID to identify it as an AMBA PL022 peripheral.

Yep, but how close is it... the raw PL022 and the ST Micro variant only differ in a few small places.

Quoted 3 lines

It probably was originally based on the PL022 but I think Cirrus hacked it enough that the generic amba-pl022 driver will not work with it, at least without a number of ugly #ifdef hacks.

The stuff we've done with vendor data is mostly done so that we alter execution path in runtime rather than throwing in #ifdefs.

Quoted 2 lines

Or, I could be wrong. ?It appears the amba-pl022 driver doesn't actually match the ARM spec for the PL022.

You're right, the control registers are not cleanly separated into the vanilla PL022 vs ST Micro Nomadik extensions. I will post a fixup patch soonish, covering all the stuff you pointed out.

When you look at the code post-patch I think you will see that it is actually even closer, to the vanilla PL022.

Sorry for the mess, I was retrofitting a driver for the ST extended version to the plain PL022 found in U300 and missed some stuff.

By chance, Rabin pointed me to the same problems some day ago...

Quoted 3 lines

The PL022 spec only shows the peripheral as supporting up to 16-bit transfers. This is the limit for the EP93xx also but the amba-pl022 driver actually supports up to 32-bit transfers.

Actually the code execution path takes this into account in the pl022_setup() function:

	if (chip_info->data_size <= 8) {
		dev_dbg(&spi->dev, "1 <= n <=8 bits per word\n");
		chip->n_bytes = 1;
		chip->read = READING_U8;
		chip->write = WRITING_U8;
	} else if (chip_info->data_size <= 16) {
		dev_dbg(&spi->dev, "9 <= n <= 16 bits per word\n");
		chip->n_bytes = 2;
		chip->read = READING_U16;
		chip->write = WRITING_U16;
	} else {
		if (pl022->vendor->max_bpw >= 32) {
			dev_dbg(&spi->dev, "17 <= n <= 32 bits per word\n");
			chip->n_bytes = 4;
			chip->read = READING_U32;
			chip->write = WRITING_U32;
		} else {
			dev_err(&spi->dev,
				"illegal data size for this controller!\n");
			dev_err(&spi->dev,
				"a standard pl022 can only handle "
				"1 <= n <= 16 bit words\n");
			goto err_config_params;
		}
	}

As you see 32bit transfers will only be accepted as configuration if the vendor data indicates that this is possible on that version of the controller.

Quoted 8 lines

/*
?* SSP Interrupt Mask Set/Clear Register - SSP_IMSC
?*/
#define SSP_IMSC_MASK_RORIM (0x1UL << 0) /* Receive Overrun Interrupt mask */ #define SSP_IMSC_MASK_RTIM ?(0x1UL << 1) /* Receive timeout Interrupt mask */ #define SSP_IMSC_MASK_RXIM ?(0x1UL << 2) /* Receive FIFO Interrupt mask */ #define SSP_IMSC_MASK_TXIM ?(0x1UL << 3) /* Transmit FIFO Interrupt mask */

Quoted 7 lines

In the EP93xx this register is called SSPIIR / SSPICR. ?It is the interrupt status register for reads and the interrupt clear register for writes. ?For reads it is defined as:

0 ?RIS ? ?SSP Receive FIFO service request 1 ?TIS ? ?SSP Transmit FIFO service request 2 ?RORIS ?SSP Receive FIFO overrun interrupt status

This seems like it is either a precursor to the PL022 or a seriously stripped down version. I don't know if this alone warrants duplicating the rest of the code, could be.

Quoted 2 lines

The other registers listed for the amba-pl022 do not exist in the EP93xx implementation.

We have a stripped down version called "PL023" as well, it's simply that we don't use the extended features and it just works, so I think it's not much of a problem.

Quoted 3 lines

The biggest problem I can see trying to use the amba-pl022 with the ep93xx are the missing Peripheral ID and PrimeCell ID registers. ?The AMBA bus driver will not be able to match the driver to the device.

That is actually not a problem at all, you can hardcode a PrimeCell ID into the platform config, and the device will be registered on the given ID instead, like this:

struct amba_device ssp0_device = {
        .dev = {
                .coherent_dma_mask = ~0,
                .init_name = "ssp0",
                .platform_data = &ssp0_platform_data,
        },
        .res = {
                .start = U8500_SSP0_BASE,
                .end   = U8500_SSP0_BASE + SZ_4K - 1,
                .flags = IORESOURCE_MEM,
        },
        .irq = {IRQ_SSP0, NO_IRQ },
        /* PL022 Device ID */
        .periphid = 0x00041022,
};

Then you have to register it on the PrimeCell bus with

static struct amba_device *amba_devs[] __initdata = {
        &ssp0_device,
};

for (i = 0; i < ARRAY_SIZE(amba_devs); i++)

         amba_device_register(amba_devs[i], &iomem_resource);
Quoted 2 lines

Beyond that, once the amba-pl022 driver supports DMA transfers the implementation needed for the EP93xx will be completely different.

The idea about the PrimeCell DMA is to use the DMAdevices in the DMA engine only, to find some common ground exactly in order to facilitate usage with diverse platforms.

I know that there are many custom DMA implementations, the idea is that if we can unify these under DMA engine we can abstract away the specifics. I have already done this for two very different DMA
engines: COH901318 and DMA40 and it works. DMA controllers IMO are not so special at all, they all share the same features and shall hopefully all go in behind the DMA engine API.

Quoted 3 lines

I think it's best to leave these as separate drivers. ?Cirrus has stopped development of their ARM processors so we have no hope of ever getting a full pl022 compatible version.

I think it's worth a try to use amba-pl022.c, but hey, it's just me...

Yours,
Linus Walleij

#7 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

In reply to Mika Westerberg
Quoted 5 lines · Mika Westerberg

This patch adds an SPI master driver for the Cirrus EP93xx SPI controller found in EP93xx chips.

A couple nitpick comments below that you can choose to ignore if you wish.

Quoted 102 lines
---
 Documentation/spi/ep93xx_spi                   |   95 +++
 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h |   27 +
 drivers/spi/Kconfig                            |   10 +
 drivers/spi/Makefile                           |    1 +
 drivers/spi/ep93xx_spi.c                       |  986 ++++++++++++++++++++++++
 5 files changed, 1119 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/spi/ep93xx_spi
 create mode 100644 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
 create mode 100644 drivers/spi/ep93xx_spi.c
Documentation/spi/ep93xx_spi new file +85 -0
new file mode 100644
index 0000000..6325f5b
@@ -0,0 +1,95 @@
+Cirrus EP93xx SPI controller driver HOWTO
+=========================================
+
+ep93xx_spi driver brings SPI master support for EP93xx SPI controller.  Chip
+selects are implemented with GPIO lines.
+
+NOTE: If possible, don't use SFRMOUT (SFRM1) signal as a chip select. It will
+not work correctly (it cannot be controlled by software). Use GPIO lines
+instead.
+
+Sample configuration
+====================
+
+Typically driver configuration is done in platform board files (the files under
+arch/arm/mach-ep93xx/*.c). In this example we configure MMC over SPI through
+this driver on TS-7260 board. You can adapt the code to suit your needs.
+
+This example uses EGPIO9 as SD/MMC card chip select (this is wired in DIO1
+header on the board).
+
+You need to select CONFIG_MMC_SPI to use mmc_spi driver.
+
+arch/arm/mach-ep93xx/ts72xx.c:
+
+...
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <mach/ep93xx_spi.h>
+
+/* this is our GPIO line used for chip select */
+#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9
+
+static int ts72xx_mmc_spi_setup(struct spi_device *spi)
+{
+     int err;
+
+     err = gpio_request(MMC_CHIP_SELECT_GPIO, spi->modalias);
+     if (err)
+             return err;
+
+     gpio_direction_output(MMC_CHIP_SELECT_GPIO, 1);
+
+     return 0;
+}
+
+static void ts72xx_mmc_spi_cleanup(struct spi_device *spi)
+{
+     gpio_set_value(MMC_CHIP_SELECT_GPIO, 1);
+     gpio_direction_input(MMC_CHIP_SELECT_GPIO);
+     gpio_free(MMC_CHIP_SELECT_GPIO);
+}
+
+static void ts72xx_mmc_spi_cs_control(struct spi_device *spi, int value)
+{
+     gpio_set_value(MMC_CHIP_SELECT_GPIO, value);
+}
+
+static struct ep93xx_spi_chip_ops ts72xx_mmc_spi_ops = {
+     .setup          = ts72xx_mmc_spi_setup,
+     .cleanup        = ts72xx_mmc_spi_cleanup,
+     .cs_control     = ts72xx_mmc_spi_cs_control,
+};
+
+static struct spi_board_info ts72xx_spi_devices[] __initdata = {
+     {
+             .modalias               = "mmc_spi",
+             .controller_data        = &ts72xx_mmc_spi_ops,
+             /*
+              * We use 10 MHz even though the maximum is 7.4 MHz. The driver
+              * will limit it automatically to max. frequency.
+              */
+             .max_speed_hz           = 10 * 1000 * 1000,
+             .bus_num                = 0,
+             .chip_select            = 0,
+             .mode                   = SPI_MODE_0,
+     },
+};
+
+static struct ep93xx_spi_info ts72xx_spi_info = {
+     .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices),
+};
+
+static void __init ts72xx_init_machine(void)
+{

Documentation/spi/ep93xx_spi line 95

Quoted 10 lines
Documentation/spi/ep93xx_spi new file +10 -0
+     ...
+     ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices,
+                         ARRAY_SIZE(ts72xx_spi_devices));
+}
+
+Thanks to
+=========
+Martin Guy, H. Hartley Sweeten and others who helped me during development of
+the driver. Simplemachines.it donated me a Sim.One board which I used testing
+the driver on EP9307.

This looks good.

Quoted 23 lines
arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h new file +17 -0
new file mode 100644
index 0000000..0a37961
@@ -0,0 +1,27 @@
+#ifndef __ASM_MACH_EP93XX_SPI_H
+#define __ASM_MACH_EP93XX_SPI_H
+
+struct spi_device;
+
+/**
+ * struct ep93xx_spi_info - EP93xx specific SPI descriptor
+ * @num_chipselect: number of chip selects on this board, must be
+ *                  at least one
+ */
+struct ep93xx_spi_info {
+     int     num_chipselect;
+};
+
+/**
+ * struct ep93xx_spi_chip_ops - operation callbacks for SPI slave device
+ * @setup: setup the chip select mechanism

arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h line 27

Quoted 10 lines
arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h new file +10 -0
+ * @cleanup: cleanup the chip select mechanism
+ * @cs_control: control the device chip select
+ */
+struct ep93xx_spi_chip_ops {
+     int     (*setup)(struct spi_device *spi);
+     void    (*cleanup)(struct spi_device *spi);
+     void    (*cs_control)(struct spi_device *spi, int value);
+};
+
+#endif /* __ASM_MACH_EP93XX_SPI_H */

Again, good.

Quoted 11 lines
drivers/spi/Kconfig +3 -0
index a191fa2..2b2f4c3 100644
@@ -117,6 +117,16 @@ config SPI_DAVINCI
      help
        SPI master controller for DaVinci and DA8xx SPI modules.
 
+config SPI_EP93XX
+     tristate "Cirrus Logic EP93xx SPI controller"
+     depends on ARCH_EP93XX

drivers/spi/Kconfig config SPI_DAVINCI · line 132

Quoted 10 lines
drivers/spi/Kconfig +7 -0
+     help
+       This enables using the Cirrus EP93xx SPI controller in master
+       mode.
+
+       To compile this driver as a module, choose M here. The module will be
+       called ep93xx_spi.
+
 config SPI_GPIO
      tristate "GPIO-based bitbanging SPI Master"
      depends on GENERIC_GPIO

Good.

drivers/spi/Makefile obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o · line 27

Quoted 12 lines
drivers/spi/Makefile +1 -0
index d7d0f89..377f845 100644
@@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_DAVINCI)           += davinci_spi.o
 obj-$(CONFIG_SPI_DESIGNWARE)         += dw_spi.o
 obj-$(CONFIG_SPI_DW_PCI)             += dw_spi_pci.o
 obj-$(CONFIG_SPI_DW_MMIO)            += dw_spi_mmio.o
+obj-$(CONFIG_SPI_EP93XX)             += ep93xx_spi.o
 obj-$(CONFIG_SPI_GPIO)                       += spi_gpio.o
 obj-$(CONFIG_SPI_IMX)                        += spi_imx.o
 obj-$(CONFIG_SPI_LM70_LLP)           += spi_lm70llp.o

Good.

Quoted 168 lines
drivers/spi/ep93xx_spi.c new file +162 -0
new file mode 100644
index 0000000..2fbd489
@@ -0,0 +1,986 @@
+/*
+ * Driver for Cirrus Logic EP93xx SPI controller.
+ *
+ * Copyright (c) 2010 Mika Westerberg
+ *
+ * Explicit FIFO handling code was inspired by amba-pl022 driver.
+ *
+ * Chip select support using other than built-in GPIOs by H. Hartley Sweeten.
+ *
+ * For more information about the SPI controller see documentation on Cirrus
+ * Logic web site:
+ *     http://www.cirrus.com/en/pubs/manual/EP93xx_Users_Guide_UM1.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/spi/spi.h>
+
+#include <mach/ep93xx_spi.h>
+
+#define SSPCR0                       0x0000
+#define SSPCR0_MODE_SHIFT    6
+#define SSPCR0_SCR_SHIFT     8
+
+#define SSPCR1                       0x0004
+#define SSPCR1_RIE           BIT(0)
+#define SSPCR1_TIE           BIT(1)
+#define SSPCR1_RORIE         BIT(2)
+#define SSPCR1_LBM           BIT(3)
+#define SSPCR1_SSE           BIT(4)
+#define SSPCR1_MS            BIT(5)
+#define SSPCR1_SOD           BIT(6)
+
+#define SSPDR                        0x0008
+
+#define SSPSR                        0x000c
+#define SSPSR_TFE            BIT(0)
+#define SSPSR_TNF            BIT(1)
+#define SSPSR_RNE            BIT(2)
+#define SSPSR_RFF            BIT(3)
+#define SSPSR_BSY            BIT(4)
+#define SSPCPSR                      0x0010
+
+#define SSPIIR                       0x0014
+#define SSPIIR_RIS           BIT(0)
+#define SSPIIR_TIS           BIT(1)
+#define SSPIIR_RORIS         BIT(2)
+#define SSPICR                       SSPIIR
+
+/* timeout in milliseconds */
+#define SPI_TIMEOUT          5
+/* maximum depth of RX/TX FIFO */
+#define SPI_FIFO_SIZE                8
+
+/**
+ * struct ep93xx_spi - EP93xx SPI controller structure
+ * @lock: spinlock that protects concurrent accesses to fields @running,
+ *        @current_msg and @msg_queue
+ * @pdev: pointer to platform device
+ * @clk: clock for the controller
+ * @regs_base: pointer to ioremap()'d registers
+ * @irq: IRQ number used by the driver
+ * @min_rate: minimum clock rate (in Hz) supported by the controller
+ * @max_rate: maximum clock rate (in Hz) supported by the controller
+ * @running: is the queue running
+ * @wq: workqueue used by the driver
+ * @msg_work: work that is queued for the driver
+ * @wait: wait here until given transfer is completed
+ * @msg_queue: queue for the messages
+ * @current_msg: message that is currently processed (or %NULL if none)
+ * @tx: current byte in transfer to transmit
+ * @rx: current byte in transfer to receive
+ * @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one
+ *              frame decreases this level and sending one frame increases it.
+ *
+ * This structure holds EP93xx SPI controller specific information. When
+ * @running is %true, driver accepts transfer requests from protocol drivers.
+ * @current_msg is used to hold pointer to the message that is currently
+ * processed. If @current_msg is %NULL, it means that no processing is going
+ * on.
+ *
+ * Most of the fields are only written once and they can be accessed without
+ * taking the @lock. Fields that are accessed concurrently are: @current_msg,
+ * @running, and @msg_queue.
+ */
+struct ep93xx_spi {
+     spinlock_t                      lock;
+     const struct platform_device    *pdev;
+     struct clk                      *clk;
+     void __iomem                    *regs_base;
+     int                             irq;
+     unsigned long                   min_rate;
+     unsigned long                   max_rate;
+     bool                            running;
+     struct workqueue_struct         *wq;
+     struct work_struct              msg_work;
+     struct completion               wait;
+     struct list_head                msg_queue;
+     struct spi_message              *current_msg;
+     size_t                          tx;
+     size_t                          rx;
+     size_t                          fifo_level;
+};
+
+/**
+ * struct ep93xx_spi_chip - SPI device hardware settings
+ * @spi: back pointer to the SPI device
+ * @rate: max rate in hz this chip supports
+ * @div_cpsr: cpsr (pre-scaler) divider
+ * @div_scr: scr divider
+ * @dss: bits per word (4 - 16 bits)
+ * @ops: private chip operations
+ *
+ * This structure is used to store hardware register specific settings for each
+ * SPI device. Settings are written to hardware by function
+ * ep93xx_spi_chip_setup().
+ */
+struct ep93xx_spi_chip {
+     const struct spi_device         *spi;
+     unsigned long                   rate;
+     u8                              div_cpsr;
+     u8                              div_scr;
+     u8                              dss;
+     struct ep93xx_spi_chip_ops      *ops;
+};
+
+/* converts bits per word to CR0.DSS value */
+#define bits_per_word_to_dss(bpw)    ((bpw) - 1)
+
+static inline void
+ep93xx_spi_write_u8(const struct ep93xx_spi *espi, u16 reg, u8 value)
+{
+     __raw_writeb(value, espi->regs_base + reg);
+}
+
+static inline u8
+ep93xx_spi_read_u8(const struct ep93xx_spi *spi, u16 reg)
+{
+     return __raw_readb(spi->regs_base + reg);
+}
+
+static inline void
+ep93xx_spi_write_u16(const struct ep93xx_spi *espi, u16 reg, u16 value)
+{
+     __raw_writew(value, espi->regs_base + reg);
+}
+
+static inline u16
+ep93xx_spi_read_u16(const struct ep93xx_spi *spi, u16 reg)
+{

drivers/spi/ep93xx_spi.c line 172

Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10 -0
+     return __raw_readw(spi->regs_base + reg);
+}
+
+/**
+ * ep93xx_spi_enable() - enables the SPI controller and clock
+ * @espi: ep93xx SPI controller struct
+ *
+ * This function enables the SPI controller and its clock. Returns %0 in case
+ * of success and negative error in case if failure.
+ */

Most of these comments are overkill. The functions are adequately described by the name of the function. Also, the DocBook style comments are typically used only for exported functions.

Quoted 47 lines
drivers/spi/ep93xx_spi.c new file +47 -0
+static int ep93xx_spi_enable(const struct ep93xx_spi *espi)
+{
+     u8 regval;
+     int err;
+
+     err = clk_enable(espi->clk);
+     if (err)
+             return err;
+
+     regval = ep93xx_spi_read_u8(espi, SSPCR1);
+     regval |= SSPCR1_SSE;
+     ep93xx_spi_write_u8(espi, SSPCR1, regval);
+
+     return 0;
+}
+
+/**
+ * ep93xx_spi_disable() - disables the SPI controller and clock
+ * @espi: ep93xx SPI controller struct
+ *
+ * Function disables SPI controller and its clock.
+ */
+static void ep93xx_spi_disable(const struct ep93xx_spi *espi)
+{
+     u8 regval;
+
+     regval = ep93xx_spi_read_u8(espi, SSPCR1);
+     regval &= ~SSPCR1_SSE;
+     ep93xx_spi_write_u8(espi, SSPCR1, regval);
+
+     clk_disable(espi->clk);
+}
+
+/**
+ * ep93xx_spi_enable_interrupts() - enables all SPI interrupts
+ * @espi: ep93xx SPI controller struct
+ *
+ * Enables all SPI interrupts: receive overrun (ROR), transmit, and receive.
+ */
+static inline void
+ep93xx_spi_enable_interrupts(const struct ep93xx_spi *espi)
+{
+     u8 regval;
+
+     regval = ep93xx_spi_read_u8(espi, SSPCR1);
+     regval |= (SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE);
+     ep93xx_spi_write_u8(espi, SSPCR1, regval);

drivers/spi/ep93xx_spi.c line 229

Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10 -0
+}
+
+/**
+ * ep93xx_spi_disable_interrupts() - disables all SPI interrupts
+ * @espi: ep93xx SPI controller struct
+ *
+ * Disables all SPI interrupts.
+ */
+static inline void
+ep93xx_spi_disable_interrupts(const struct ep93xx_spi *espi)

These two functions really don't need the 'inline', the compiler can figure it out as needed. Also, if you remove the 'inline' you can then coalesce the lines into a <80 line.

Quoted 55 lines
drivers/spi/ep93xx_spi.c new file +55 -0
+{
+     u8 regval;
+
+     regval = ep93xx_spi_read_u8(espi, SSPCR1);
+     regval &= ~(SSPCR1_RORIE | SSPCR1_TIE | SSPCR1_RIE);
+     ep93xx_spi_write_u8(espi, SSPCR1, regval);
+}
+
+/**
+ * ep93xx_spi_calc_divisors() - calculates SPI clock divisors
+ * @espi: ep93xx SPI controller struct
+ * @chip: divisors are calculated for this chip
+ * @rate: desired SPI output clock rate
+ *
+ * Function calculates cpsr (clock pre-scaler) and scr divisors based on
+ * given @rate and places them to @chip->div_cpsr and @chip->div_scr. If,
+ * for some reason, divisors cannot be calculated nothing is stored and
+ * %-EINVAL is returned.
+ */
+static int ep93xx_spi_calc_divisors(const struct ep93xx_spi *espi,
+                                 struct ep93xx_spi_chip *chip,
+                                 unsigned long rate)
+{
+     unsigned long spi_clk_rate = clk_get_rate(espi->clk);
+     int cpsr, scr;
+
+     /*
+      * Make sure that max value is between values supported by the
+      * controller. Note that minimum value is already checked in
+      * ep93xx_spi_transfer().
+      */
+     rate = clamp(rate, espi->min_rate, espi->max_rate);
+
+     /*
+      * Calculate divisors so that we can get speed according the
+      * following formula:
+      *      rate = spi_clock_rate / (cpsr * (1 + scr))
+      *
+      * cpsr must be even number and starts from 2, scr can be any number
+      * between 0 and 255.
+      */
+     for (cpsr = 2; cpsr <= 254; cpsr += 2) {
+             for (scr = 0; scr <= 255; scr++) {
+                     if ((spi_clk_rate / (cpsr * (scr + 1))) <= rate) {
+                             chip->div_scr = (u8)scr;
+                             chip->div_cpsr = (u8)cpsr;
+                             return 0;
+                     }
+             }
+     }
+
+     return -EINVAL;
+}
+
+/**

drivers/spi/ep93xx_spi.c line 294

Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10 -0
+ * ep93xx_spi_cs_control() - controls chipselect for given device
+ * @spi: SPI device to select/deselect
+ * @control: select (%true) / deselect (%false)
+ *
+ * Function controls chipselect line for given SPI device.
+ *
+ * Note that this function is called from a thread context and can sleep.
+ */
+static inline void ep93xx_spi_cs_control(struct spi_device *spi,
+                                      bool control)

Again, the inline should probably be removed and then coalesce the lines.

Quoted 161 lines
drivers/spi/ep93xx_spi.c new file +161 -0
+{
+     struct ep93xx_spi_chip *chip = spi_get_ctldata(spi);
+     int value = (spi->mode & SPI_CS_HIGH) ? control : !control;
+
+     if (chip->ops && chip->ops->cs_control)
+             chip->ops->cs_control(spi, value);
+}
+
+/**
+ * ep93xx_spi_setup() - setup an SPI device
+ * @spi: SPI device to setup
+ *
+ * This function sets up SPI device mode, speed etc. Can be called multiple
+ * times for a single device. Returns %0 in case of success, negative error in
+ * case of failure. When this function returns success, the device is
+ * deselected.
+ */
+static int ep93xx_spi_setup(struct spi_device *spi)
+{
+     struct ep93xx_spi *espi = spi_master_get_devdata(spi->master);
+     struct ep93xx_spi_chip *chip;
+
+     if (spi->bits_per_word < 4 || spi->bits_per_word > 16) {
+             dev_err(&espi->pdev->dev, "invalid bits per word %d\n",
+                     spi->bits_per_word);
+             return -EINVAL;
+     }
+
+     chip = spi_get_ctldata(spi);
+     if (!chip) {
+             dev_dbg(&espi->pdev->dev, "initial setup for %s\n",
+                     spi->modalias);
+
+             chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+             if (!chip)
+                     return -ENOMEM;
+
+             chip->spi = spi;
+             chip->ops = spi->controller_data;
+
+             if (chip->ops && chip->ops->setup) {
+                     int ret = chip->ops->setup(spi);
+                     if (ret) {
+                             kfree(chip);
+                             return ret;
+                     }
+             }
+
+             spi_set_ctldata(spi, chip);
+     }
+
+     if (spi->max_speed_hz != chip->rate) {
+             int err;
+
+             err = ep93xx_spi_calc_divisors(espi, chip, spi->max_speed_hz);
+             if (err != 0) {
+                     spi_set_ctldata(spi, NULL);
+                     kfree(chip);
+                     return err;
+             }
+             chip->rate = spi->max_speed_hz;
+     }
+
+     chip->dss = bits_per_word_to_dss(spi->bits_per_word);
+
+     ep93xx_spi_cs_control(spi, false);
+     return 0;
+}
+
+/**
+ * ep93xx_spi_transfer() - queue message to be transferred
+ * @spi: target SPI device
+ * @msg: message to be transferred
+ *
+ * This function is called by SPI device drivers when they are going to transfer
+ * a new message. It simply puts the message in the queue and schedules
+ * workqueue to perform the actual transfer later on.
+ *
+ * Returns %0 on success and negative error in case of failure.
+ */
+static int ep93xx_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+     struct ep93xx_spi *espi = spi_master_get_devdata(spi->master);
+     struct spi_transfer *t;
+     unsigned long flags;
+
+     if (!msg || !msg->complete)
+             return -EINVAL;
+
+     /* first validate each transfer */
+     list_for_each_entry(t, &msg->transfers, transfer_list) {
+             if (t->bits_per_word) {
+                     if (t->bits_per_word < 4 || t->bits_per_word > 16)
+                             return -EINVAL;
+             }
+             if (t->speed_hz && t->speed_hz < espi->min_rate)
+                             return -EINVAL;
+     }
+
+     /*
+      * Now that we own the message, let's initialize it so that it is
+      * suitable for us. We use @msg->status to signal whether there was
+      * error in transfer and @msg->state is used to hold pointer to the
+      * current transfer (or %NULL if no active current transfer).
+      */
+     msg->state = NULL;
+     msg->status = 0;
+     msg->actual_length = 0;
+
+     spin_lock_irqsave(&espi->lock, flags);
+     if (!espi->running) {
+             spin_unlock_irqrestore(&espi->lock, flags);
+             return -ESHUTDOWN;
+     }
+     list_add_tail(&msg->queue, &espi->msg_queue);
+     queue_work(espi->wq, &espi->msg_work);
+     spin_unlock_irqrestore(&espi->lock, flags);
+
+     return 0;
+}
+
+/**
+ * ep93xx_spi_cleanup() - cleans up master controller specific state
+ * @spi: SPI device to cleanup
+ *
+ * This function releases master controller specific state for given @spi
+ * device.
+ */
+static void ep93xx_spi_cleanup(struct spi_device *spi)
+{
+     struct ep93xx_spi_chip *chip;
+
+     chip = spi_get_ctldata(spi);
+     if (chip) {
+             if (chip->ops && chip->ops->cleanup)
+                     chip->ops->cleanup(spi);
+             spi_set_ctldata(spi, NULL);
+             kfree(chip);
+     }
+}
+
+/**
+ * ep93xx_spi_chip_setup() - configures hardware according to given @chip
+ * @espi: ep93xx SPI controller struct
+ * @chip: chip specific settings
+ *
+ * This function sets up the actual hardware registers with settings given in
+ * @chip. Note that no validation is done so make sure that callers validate
+ * settings before calling this.
+ */
+static void ep93xx_spi_chip_setup(const struct ep93xx_spi *espi,
+                               const struct ep93xx_spi_chip *chip)
+{
+     u16 cr0;
+
+     cr0 = chip->div_scr << SSPCR0_SCR_SHIFT;
+     cr0 |= (chip->spi->mode & (SPI_CPHA|SPI_CPOL)) << SSPCR0_MODE_SHIFT;
+     cr0 |= chip->dss;
+
+     dev_dbg(&espi->pdev->dev, "setup: mode %d, cpsr %d, scr %d, dss %d\n",
+             chip->spi->mode, chip->div_cpsr, chip->div_scr, chip->dss);

drivers/spi/ep93xx_spi.c line 465

Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10 -0
+     dev_dbg(&espi->pdev->dev, "setup: cr0 %#x", cr0);
+
+     ep93xx_spi_write_u8(espi, SSPCPSR, chip->div_cpsr);
+     ep93xx_spi_write_u16(espi, SSPCR0, cr0);
+}
+
+/**
+ * bits_per_word() - returns bits per word for current message
+ */
+static inline int bits_per_word(const struct ep93xx_spi *espi)

This inline probably make sense. You would really have to look at the generated assembly to know for sure.

Quoted 511 lines
drivers/spi/ep93xx_spi.c new file +511 -0
+{
+     struct spi_message *msg = espi->current_msg;
+     struct spi_transfer *t = msg->state;
+
+     return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word;
+}
+
+static void ep93xx_do_write(struct ep93xx_spi *espi, struct spi_transfer *t)
+{
+     if (bits_per_word(espi) > 8) {
+             u16 tx_val = 0;
+
+             if (t->tx_buf)
+                     tx_val = ((u16 *)t->tx_buf)[espi->tx];
+             ep93xx_spi_write_u16(espi, SSPDR, tx_val);
+             espi->tx += sizeof(tx_val);
+     } else {
+             u8 tx_val = 0;
+
+             if (t->tx_buf)
+                     tx_val = ((u8 *)t->tx_buf)[espi->tx];
+             ep93xx_spi_write_u8(espi, SSPDR, tx_val);
+             espi->tx += sizeof(tx_val);
+     }
+}
+
+static void ep93xx_do_read(struct ep93xx_spi *espi, struct spi_transfer *t)
+{
+     if (bits_per_word(espi) > 8) {
+             u16 rx_val;
+
+             rx_val = ep93xx_spi_read_u16(espi, SSPDR);
+             if (t->rx_buf)
+                     ((u16 *)t->rx_buf)[espi->rx] = rx_val;
+             espi->rx += sizeof(rx_val);
+     } else {
+             u8 rx_val;
+
+             rx_val = ep93xx_spi_read_u8(espi, SSPDR);
+             if (t->rx_buf)
+                     ((u8 *)t->rx_buf)[espi->rx] = rx_val;
+             espi->rx += sizeof(rx_val);
+     }
+}
+
+/**
+ * ep93xx_spi_read_write() - perform next RX/TX transfer
+ * @espi: ep93xx SPI controller struct
+ *
+ * This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If
+ * called several times, the whole transfer will be completed. Returns
+ * %-EINPROGRESS when current transfer was not yet completed otherwise %0.
+ *
+ * When this function is finished, RX FIFO should be empty and TX FIFO should be
+ * full.
+ */
+static int ep93xx_spi_read_write(struct ep93xx_spi *espi)
+{
+     struct spi_message *msg = espi->current_msg;
+     struct spi_transfer *t = msg->state;
+
+     /* read as long as RX FIFO has frames in it */
+     while ((ep93xx_spi_read_u8(espi, SSPSR) & SSPSR_RNE)) {
+             ep93xx_do_read(espi, t);
+             espi->fifo_level--;
+     }
+
+     /* write as long as TX FIFO has room */
+     while (espi->fifo_level < SPI_FIFO_SIZE && espi->tx < t->len) {
+             ep93xx_do_write(espi, t);
+             espi->fifo_level++;
+     }
+
+     if (espi->rx == t->len) {
+             msg->actual_length += t->len;
+             return 0;
+     }
+
+     return -EINPROGRESS;
+}
+
+/**
+ * ep93xx_spi_process_transfer() - processes one SPI transfer
+ * @espi: ep93xx SPI controller struct
+ * @msg: current message
+ * @t: transfer to process
+ *
+ * This function processes one SPI transfer given in @t. Function waits until
+ * transfer is complete (may sleep) and updates @msg->status based on whether
+ * transfer was succesfully processed or not.
+ */
+static void ep93xx_spi_process_transfer(struct ep93xx_spi *espi,
+                                     struct spi_message *msg,
+                                     struct spi_transfer *t)
+{
+     struct ep93xx_spi_chip *chip = spi_get_ctldata(msg->spi);
+
+     msg->state = t;
+
+     /*
+      * Handle any transfer specific settings if needed. We use
+      * temporary chip settings here and restore original later when
+      * the transfer is finished.
+      */
+     if (t->speed_hz || t->bits_per_word) {
+             struct ep93xx_spi_chip tmp_chip = *chip;
+
+             if (t->speed_hz) {
+                     int err;
+
+                     err = ep93xx_spi_calc_divisors(espi, &tmp_chip,
+                                                    t->speed_hz);
+                     if (err) {
+                             dev_err(&espi->pdev->dev,
+                                     "failed to adjust speed\n");
+                             msg->status = err;
+                             return;
+                     }
+             }
+
+             if (t->bits_per_word)
+                     tmp_chip.dss = bits_per_word_to_dss(t->bits_per_word);
+
+             /*
+              * Set up temporary new hw settings for this transfer.
+              */
+             ep93xx_spi_chip_setup(espi, &tmp_chip);
+     }
+
+     espi->rx = 0;
+     espi->tx = 0;
+
+     /*
+      * Now everything is set up for the current transfer. We prime the TX
+      * FIFO, enable interrupts, and wait for the transfer to complete.
+      */
+     if (ep93xx_spi_read_write(espi)) {
+             ep93xx_spi_enable_interrupts(espi);
+             wait_for_completion(&espi->wait);
+     }
+
+     /*
+      * In case of error during transmit, we bail out from processing
+      * the message.
+      */
+     if (msg->status)
+             return;
+
+     /*
+      * After this transfer is finished, perform any possible
+      * post-transfer actions requested by the protocol driver.
+      */
+     if (t->delay_usecs) {
+             set_current_state(TASK_UNINTERRUPTIBLE);
+             schedule_timeout(usecs_to_jiffies(t->delay_usecs));
+     }
+     if (t->cs_change) {
+             if (!list_is_last(&t->transfer_list, &msg->transfers)) {
+                     /*
+                      * In case protocol driver is asking us to drop the
+                      * chipselect briefly, we let the scheduler to handle
+                      * any "delay" here.
+                      */
+                     ep93xx_spi_cs_control(msg->spi, false);
+                     cond_resched();
+                     ep93xx_spi_cs_control(msg->spi, true);
+             }
+     }
+
+     if (t->speed_hz || t->bits_per_word)
+             ep93xx_spi_chip_setup(espi, chip);
+}
+
+/*
+ * ep93xx_spi_process_message() - process one SPI message
+ * @espi: ep93xx SPI controller struct
+ * @msg: message to process
+ *
+ * This function processes a single SPI message. We go through all transfers in
+ * the message and pass them to ep93xx_spi_process_transfer(). Chipselect is
+ * asserted during the whole message (unless per transfer cs_change is set).
+ *
+ * @msg->status contains %0 in case of success or negative error code in case of
+ * failure.
+ */
+static void ep93xx_spi_process_message(struct ep93xx_spi *espi,
+                                    struct spi_message *msg)
+{
+     unsigned long timeout;
+     struct spi_transfer *t;
+     int err;
+
+     /*
+      * Enable the SPI controller and its clock.
+      */
+     err = ep93xx_spi_enable(espi);
+     if (err) {
+             dev_err(&espi->pdev->dev, "failed to enable SPI controller\n");
+             msg->status = err;
+             return;
+     }
+
+     /*
+      * Just to be sure: flush any data from RX FIFO.
+      */
+     timeout = jiffies + msecs_to_jiffies(SPI_TIMEOUT);
+     while (ep93xx_spi_read_u16(espi, SSPSR) & SSPSR_RNE) {
+             if (time_after(jiffies, timeout)) {
+                     dev_warn(&espi->pdev->dev,
+                              "timeout while flushing RX FIFO\n");
+                     msg->status = -ETIMEDOUT;
+                     return;
+             }
+             ep93xx_spi_read_u16(espi, SSPDR);
+     }
+
+     /*
+      * We explicitly handle FIFO level. This way we don't have to check TX
+      * FIFO status using %SSPSR_TNF bit which may cause RX FIFO overruns.
+      */
+     espi->fifo_level = 0;
+
+     /*
+      * Update SPI controller registers according to spi device and assert
+      * the chipselect.
+      */
+     ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi));
+     ep93xx_spi_cs_control(msg->spi, true);
+
+     list_for_each_entry(t, &msg->transfers, transfer_list) {
+             ep93xx_spi_process_transfer(espi, msg, t);
+             if (msg->status)
+                     break;
+     }
+
+     /*
+      * Now the whole message is transferred (or failed for some reason). We
+      * deselect the device and disable the SPI controller.
+      */
+     ep93xx_spi_cs_control(msg->spi, false);
+     ep93xx_spi_disable(espi);
+}
+
+#define work_to_espi(work) (container_of((work), struct ep93xx_spi, msg_work))
+
+/**
+ * ep93xx_spi_work() - EP93xx SPI workqueue worker function
+ * @work: work struct
+ *
+ * Workqueue worker function. This function is called when there are new
+ * SPI messages to be processed. Message is taken out from the queue and then
+ * passed to ep93xx_spi_process_message().
+ *
+ * After message is transferred, protocol driver is notified by calling
+ * @msg->complete(). In case of error, @msg->status is set to negative error
+ * number, otherwise it contains zero (and @msg->actual_length is updated).
+ */
+static void ep93xx_spi_work(struct work_struct *work)
+{
+     struct ep93xx_spi *espi = work_to_espi(work);
+     struct spi_message *msg;
+
+     spin_lock_irq(&espi->lock);
+     if (!espi->running || espi->current_msg ||
+             list_empty(&espi->msg_queue)) {
+             spin_unlock_irq(&espi->lock);
+             return;
+     }
+     msg = list_first_entry(&espi->msg_queue, struct spi_message, queue);
+     list_del_init(&msg->queue);
+     espi->current_msg = msg;
+     spin_unlock_irq(&espi->lock);
+
+     ep93xx_spi_process_message(espi, msg);
+
+     /*
+      * Update the current message and re-schedule ourselves if there are
+      * more messages in the queue.
+      */
+     spin_lock_irq(&espi->lock);
+     espi->current_msg = NULL;
+     if (espi->running && !list_empty(&espi->msg_queue))
+             queue_work(espi->wq, &espi->msg_work);
+     spin_unlock_irq(&espi->lock);
+
+     /* notify the protocol driver that we are done with this message */
+     msg->complete(msg->context);
+}
+
+/**
+ * ep93xx_spi_interrupt() - SPI interrupt handler
+ * @irq: IRQ number (not used)
+ * @dev_id: pointer to EP93xx controller struct
+ *
+ * This function handles TX/RX/ROR interrupts which come from the SPI
+ * controller.
+ */
+static irqreturn_t ep93xx_spi_interrupt(int irq, void *dev_id)
+{
+     struct ep93xx_spi *espi = dev_id;
+     u8 irq_status = ep93xx_spi_read_u8(espi, SSPIIR);
+
+     /*
+      * If we got ROR (receive overrun) interrupt we know that something is
+      * wrong. Just abort the message.
+      */
+     if (unlikely(irq_status & SSPIIR_RORIS)) {
+             /* clear the overrun interrupt */
+             ep93xx_spi_write_u8(espi, SSPICR, 0);
+             dev_warn(&espi->pdev->dev,
+                      "receive overrun, aborting the message\n");
+             espi->current_msg->status = -EIO;
+     } else {
+             /*
+              * Interrupt is either RX (RIS) or TX (TIS). For both cases we
+              * simply execute next data transfer.
+              */
+             if (ep93xx_spi_read_write(espi)) {
+                     /*
+                      * In normal case, there still is some processing left
+                      * for current transfer. Let's wait for the next
+                      * interrupt then.
+                      */
+                     return IRQ_HANDLED;
+             }
+     }
+
+     /*
+      * Current transfer is finished, either with error or with success. In
+      * any case we disable interrupts and notify the worker to handle
+      * any post-processing of the message.
+      */
+     ep93xx_spi_disable_interrupts(espi);
+     complete(&espi->wait);
+     return IRQ_HANDLED;
+}
+
+static int __init ep93xx_spi_probe(struct platform_device *pdev)
+{
+     struct spi_master *master;
+     struct ep93xx_spi_info *info;
+     struct ep93xx_spi *espi;
+     struct resource *res;
+     int error;
+
+     info = pdev->dev.platform_data;
+
+     master = spi_alloc_master(&pdev->dev, sizeof(*espi));
+     if (!master) {
+             dev_err(&pdev->dev, "failed to allocate spi master\n");
+             return -ENOMEM;
+     }
+
+     master->setup = ep93xx_spi_setup;
+     master->transfer = ep93xx_spi_transfer;
+     master->cleanup = ep93xx_spi_cleanup;
+     master->bus_num = pdev->id;
+     master->num_chipselect = info->num_chipselect;
+     master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+
+     platform_set_drvdata(pdev, master);
+
+     espi = spi_master_get_devdata(master);
+
+     espi->clk = clk_get(&pdev->dev, NULL);
+     if (IS_ERR(espi->clk)) {
+             dev_err(&pdev->dev, "unable to get spi clock\n");
+             error = PTR_ERR(espi->clk);
+             goto fail_release_master;
+     }
+
+     spin_lock_init(&espi->lock);
+     init_completion(&espi->wait);
+
+     /*
+      * Calculate maximum and minimum supported clock rates
+      * for the controller.
+      */
+     espi->max_rate = clk_get_rate(espi->clk) / 2;
+     espi->min_rate = clk_get_rate(espi->clk) / (254 * 256);
+     espi->pdev = pdev;
+
+     espi->irq = platform_get_irq(pdev, 0);
+     if (espi->irq < 0) {
+             error = -EBUSY;
+             dev_err(&pdev->dev, "failed to get irq resources\n");
+             goto fail_put_clock;
+     }
+
+     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     if (!res) {
+             dev_err(&pdev->dev, "unable to get iomem resource\n");
+             error = -ENODEV;
+             goto fail_put_clock;
+     }
+
+     res = request_mem_region(res->start, resource_size(res), pdev->name);
+     if (!res) {
+             dev_err(&pdev->dev, "unable to request iomem resources\n");
+             error = -EBUSY;
+             goto fail_put_clock;
+     }
+
+     espi->regs_base = ioremap(res->start, resource_size(res));
+     if (!espi->regs_base) {
+             dev_err(&pdev->dev, "failed to map resources\n");
+             error = -ENODEV;
+             goto fail_free_mem;
+     }
+
+     error = request_irq(espi->irq, ep93xx_spi_interrupt, 0,
+                         "ep93xx-spi", espi);
+     if (error) {
+             dev_err(&pdev->dev, "failed to request irq\n");
+             goto fail_unmap_regs;
+     }
+
+     espi->wq = create_singlethread_workqueue("ep93xx_spid");
+     if (!espi->wq) {
+             dev_err(&pdev->dev, "unable to create workqueue\n");
+             goto fail_free_irq;
+     }
+     INIT_WORK(&espi->msg_work, ep93xx_spi_work);
+     INIT_LIST_HEAD(&espi->msg_queue);
+     espi->running = true;
+
+     /* make sure that the hardware is disabled */
+     ep93xx_spi_write_u8(espi, SSPCR1, 0);
+
+     error = spi_register_master(master);
+     if (error) {
+             dev_err(&pdev->dev, "failed to register SPI master\n");
+             goto fail_free_queue;
+     }
+
+     dev_info(&pdev->dev, "EP93xx SPI Controller at 0x%08lx irq %d\n",
+              (unsigned long)res->start, espi->irq);
+
+     return 0;
+
+fail_free_queue:
+     destroy_workqueue(espi->wq);
+fail_free_irq:
+     free_irq(espi->irq, espi);
+fail_unmap_regs:
+     iounmap(espi->regs_base);
+fail_free_mem:
+     release_mem_region(res->start, resource_size(res));
+fail_put_clock:
+     clk_put(espi->clk);
+fail_release_master:
+     spi_master_put(master);
+     platform_set_drvdata(pdev, NULL);
+
+     return error;
+}
+
+static int __exit ep93xx_spi_remove(struct platform_device *pdev)
+{
+     struct spi_master *master = platform_get_drvdata(pdev);
+     struct ep93xx_spi *espi = spi_master_get_devdata(master);
+     struct resource *res;
+
+     spin_lock_irq(&espi->lock);
+     espi->running = false;
+     spin_unlock_irq(&espi->lock);
+
+     destroy_workqueue(espi->wq);
+
+     /*
+      * Complete remaining messages with %-ESHUTDOWN status.
+      */
+     spin_lock_irq(&espi->lock);
+     while (!list_empty(&espi->msg_queue)) {
+             struct spi_message *msg;
+
+             msg = list_first_entry(&espi->msg_queue,
+                                    struct spi_message, queue);
+             list_del_init(&msg->queue);
+             msg->status = -ESHUTDOWN;
+             spin_unlock_irq(&espi->lock);
+             msg->complete(msg->context);
+             spin_lock_irq(&espi->lock);
+     }
+     spin_unlock_irq(&espi->lock);
+
+     free_irq(espi->irq, espi);
+     iounmap(espi->regs_base);
+     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+     release_mem_region(res->start, resource_size(res));
+     clk_put(espi->clk);
+     platform_set_drvdata(pdev, NULL);
+
+     spi_unregister_master(master);
+     return 0;
+}
+
+static struct platform_driver ep93xx_spi_driver = {
+     .driver         = {
+             .name   = "ep93xx-spi",
+             .owner  = THIS_MODULE,
+     },
+     .remove         = __exit_p(ep93xx_spi_remove),
+};
+
+static int __init ep93xx_spi_init(void)
+{
+     return platform_driver_probe(&ep93xx_spi_driver, ep93xx_spi_probe);
+}
+module_init(ep93xx_spi_init);
+

drivers/spi/ep93xx_spi.c line 986

Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10 -0
+static void __exit ep93xx_spi_exit(void)
+{
+     platform_driver_unregister(&ep93xx_spi_driver);
+}
+module_exit(ep93xx_spi_exit);
+
+MODULE_DESCRIPTION("EP93xx SPI Controller driver");
+MODULE_AUTHOR("Mika Westerberg <[email protected]>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-spi");

Overall this looks good.

For the modified chip select support, you have my:

For the ep93xx driver in general, you have my:

This driver should probably go through the spi tree. You will need Grant Likely to Ack this and pick it up.

Regards,
Hartley

#8 lore raw

PATCH v6 1/2 spi: implemented driver for Cirrus EP93xx SPI controller

On Mon, May 03, 2010 at 01:45:01PM -0500, H Hartley Sweeten wrote:
(...)

Quoted 5 lines

Overall this looks good.

For the modified chip select support, you have my:

Quoted 8 lines

For the ep93xx driver in general, you have my:

This driver should probably go through the spi tree. You will need Grant Likely to Ack this and pick it up.

Thanks Hartley!

I will incorporate your latest comments and submit v7.

Grant: do you have any comments on this? Is it ok for you to cycle this through your spi tree?

Thanks,
MW

#9 lore raw

PATCH v6 2/2 ep93xx: SPI driver platform support code

In reply to Mika Westerberg

This patch adds platform side support code for the EP93xx SPI driver. This includes clock, resources and muxing. There is a new function: ep93xx_register_spi() which can be used by board support code to register new SPI devices for the board.

---
Patch depends on following ARM patch:
5998/1 ep93xx: added chip revision reading function

 arch/arm/mach-ep93xx/clock.c                    |   13 ++++++
 arch/arm/mach-ep93xx/core.c                     |   53 +++++++++++++++++++++++
 arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h |    1 +
 arch/arm/mach-ep93xx/include/mach/platform.h    |    4 ++
 4 files changed, 71 insertions(+), 0 deletions(-)
arch/arm/mach-ep93xx/clock.c +13 -0
index 5f80092..e29bdef 100644
@@ -96,6 +96,10 @@ static struct clk clk_keypad = {
 	.enable_mask	= EP93XX_SYSCON_KEYTCHCLKDIV_KEN,
 	.set_rate	= set_keytchclk_rate,
 };
+static struct clk clk_spi = {
+	.parent		= &clk_xtali,
+	.rate		= EP93XX_EXT_CLK_RATE,
+};
 static struct clk clk_pwm = {
 	.parent		= &clk_xtali,
 	.rate		= EP93XX_EXT_CLK_RATE,
@@ -186,6 +190,7 @@ static struct clk_lookup clocks[] = {
 	INIT_CK("ep93xx-ohci",		NULL,		&clk_usb_host),
 	INIT_CK("ep93xx-keypad",	NULL,		&clk_keypad),
 	INIT_CK("ep93xx-fb",		NULL,		&clk_video),
+	INIT_CK("ep93xx-spi.0",		NULL,		&clk_spi),
 	INIT_CK(NULL,			"pwm_clk",	&clk_pwm),
 	INIT_CK(NULL,			"m2p0",		&clk_m2p0),
 	INIT_CK(NULL,			"m2p1",		&clk_m2p1),
@@ -473,6 +478,14 @@ static int __init ep93xx_clock_init(void)
 	/* Initialize the pll2 derived clocks */
 	clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1);
 
+	/*
+	 * EP93xx SSP clock rate was doubled in version E2. For more information
+	 * see:
+	 *     http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf
+	 */
+	if (ep93xx_chip_revision() < EP93XX_CHIP_REV_E2)
+		clk_spi.rate /= 2;
+
 	pr_info("PLL1 running at %ld MHz, PLL2@%ld MHz\n",
 		clk_pll1.rate / 1000000, clk_pll2.rate / 1000000);
 	pr_info("FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n",
arch/arm/mach-ep93xx/core.c +53 -0

69 lines not on this page · raw message

arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +1 -0
index 93e2ecc..b1e096f 100644
@@ -106,6 +106,7 @@
 
 #define EP93XX_AAC_BASE			EP93XX_APB_IOMEM(0x00080000)
 
+#define EP93XX_SPI_PHYS_BASE		EP93XX_APB_PHYS(0x000a0000)
 #define EP93XX_SPI_BASE			EP93XX_APB_IOMEM(0x000a0000)
 
 #define EP93XX_IRDA_BASE		EP93XX_APB_IOMEM(0x000b0000)
arch/arm/mach-ep93xx/include/mach/platform.h +4 -0
index c6dc14d..f11f1d7 100644
@@ -6,9 +6,11 @@
 
 struct i2c_gpio_platform_data;
 struct i2c_board_info;
+struct spi_board_info;
 struct platform_device;
 struct ep93xxfb_mach_info;
 struct ep93xx_keypad_platform_data;
+struct ep93xx_spi_info;
 
 struct ep93xx_eth_data
 {
@@ -36,6 +38,8 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits)
 void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
 void ep93xx_register_i2c(struct i2c_gpio_platform_data *data,
 			 struct i2c_board_info *devices, int num);
+void ep93xx_register_spi(struct ep93xx_spi_info *info,
+			 struct spi_board_info *devices, int num);
 void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
 void ep93xx_register_pwm(int pwm0, int pwm1);
 int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
Signature · 1 line
1.5.6.5
#10 lore raw

PATCH v6 2/2 ep93xx: SPI driver platform support code

Quoted 10 lines · Mika Westerberg

This patch adds platform side support code for the EP93xx SPI driver. This includes clock, resources and muxing. There is a new function: ep93xx_register_spi() which can be used by board support code to register new SPI devices for the board.

---
Patch depends on following ARM patch:
5998/1 ep93xx: added chip revision reading function

There might be merge issues with the core.c and platform.h files due to this. Hopefully not...

That patch is already present in linux-next. If Russell think there might be an issue you could rebase this patch on the linux-next tree.

Quoted 35 lines
 arch/arm/mach-ep93xx/clock.c                    |   13 ++++++
 arch/arm/mach-ep93xx/core.c                     |   53 +++++++++++++++++++++++
 arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h |    1 +
 arch/arm/mach-ep93xx/include/mach/platform.h    |    4 ++
 4 files changed, 71 insertions(+), 0 deletions(-)
arch/arm/mach-ep93xx/clock.c +6 -0
index 5f80092..e29bdef 100644
@@ -96,6 +96,10 @@ static struct clk clk_keypad = {
 	.enable_mask	= EP93XX_SYSCON_KEYTCHCLKDIV_KEN,
 	.set_rate	= set_keytchclk_rate,
 };
+static struct clk clk_spi = {
+	.parent		= &clk_xtali,
+	.rate		= EP93XX_EXT_CLK_RATE,
+};
 static struct clk clk_pwm = {
 	.parent		= &clk_xtali,
 	.rate		= EP93XX_EXT_CLK_RATE,
@@ -186,6 +190,7 @@ static struct clk_lookup clocks[] = {
 	INIT_CK("ep93xx-ohci",		NULL,		&clk_usb_host),
 	INIT_CK("ep93xx-keypad",	NULL,		&clk_keypad),
 	INIT_CK("ep93xx-fb",		NULL,		&clk_video),
+	INIT_CK("ep93xx-spi.0",		NULL,		&clk_spi),
 	INIT_CK(NULL,			"pwm_clk",	&clk_pwm),
 	INIT_CK(NULL,			"m2p0",		&clk_m2p0),
 	INIT_CK(NULL,			"m2p1",		&clk_m2p1),
@@ -473,6 +478,14 @@ static int __init ep93xx_clock_init(void)
 	/* Initialize the pll2 derived clocks */
 	clk_usb_host.rate = clk_pll2.rate / (((value >> 28) & 0xf) + 1);
 
+	/*

arch/arm/mach-ep93xx/clock.c static int __init ep93xx_clock_init(void) · line 491

Quoted 10 lines
arch/arm/mach-ep93xx/clock.c +7 -0
+	 * EP93xx SSP clock rate was doubled in version E2. For more information
+	 * see:
+	 *     http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf
+	 */
+	if (ep93xx_chip_revision() < EP93XX_CHIP_REV_E2)
+		clk_spi.rate /= 2;
+
 	pr_info("PLL1 running at %ld MHz, PLL2 at %ld MHz\n",
 		clk_pll1.rate / 1000000, clk_pll2.rate / 1000000);
 	pr_info("FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n",

This is good.

Quoted 48 lines
arch/arm/mach-ep93xx/core.c +29 -0
index 90fb591..f3b46b9 100644
@@ -31,10 +31,12 @@
 #include <linux/amba/serial.h>
 #include <linux/i2c.h>
 #include <linux/i2c-gpio.h>
+#include <linux/spi/spi.h>
 
 #include <mach/hardware.h>
 #include <mach/fb.h>
 #include <mach/ep93xx_keypad.h>
+#include <mach/ep93xx_spi.h>
 
 #include <asm/mach/map.h>
 #include <asm/mach/time.h>
@@ -398,6 +400,57 @@ void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data,
 	platform_device_register(&ep93xx_i2c_device);
 }
 
+/*************************************************************************
+ * EP93xx SPI peripheral handling
+ *************************************************************************/
+static struct ep93xx_spi_info ep93xx_spi_master_data;
+
+static struct resource ep93xx_spi_resources[] = {
+	{
+		.start	= EP93XX_SPI_PHYS_BASE,
+		.end	= EP93XX_SPI_PHYS_BASE + 0x18 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	{
+		.start	= IRQ_EP93XX_SSP,
+		.end	= IRQ_EP93XX_SSP,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ep93xx_spi_device = {
+	.name		= "ep93xx-spi",
+	.id		= 0,
+	.dev		= {
+		.platform_data = &ep93xx_spi_master_data,
+	},
+	.num_resources	= ARRAY_SIZE(ep93xx_spi_resources),
+	.resource	= ep93xx_spi_resources,
+};

arch/arm/mach-ep93xx/core.c void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data, · line 439

Quoted 10 lines
arch/arm/mach-ep93xx/core.c +10 -0
+
+/**
+ * ep93xx_register_spi() - registers spi platform device
+ * @info: ep93xx board specific spi master info (__initdata)
+ * @devices: SPI devices to register (__initdata)
+ * @num: number of SPI devices to register
+ *
+ * This function registers platform device for the EP93xx SPI controller and
+ * also makes sure that SPI pins are muxed so that I2S is not using those pins.
+ * Caller should allocate necessary GPIO lines before before calling this.

That last line is no longer true. The GPIO allocation will now occur during the individual spi device probes. Just remove the line.

Quoted 7 lines
arch/arm/mach-ep93xx/core.c +7 -0
+ */
+void __init ep93xx_register_spi(struct ep93xx_spi_info *info,
+				struct spi_board_info *devices, int num)
+{
+	/*
+	 * When SPI is used, we need to make sure that I2S is muxed off from
+	 * SPI pins.

arch/arm/mach-ep93xx/core.c void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data, · line 456

Quoted 10 lines
arch/arm/mach-ep93xx/core.c +7 -0
+	 */
+	ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_I2SONSSP);
+
+	ep93xx_spi_master_data = *info;
+	spi_register_board_info(devices, num);
+	platform_device_register(&ep93xx_spi_device);
+}
 
 /*************************************************************************
  * EP93xx LEDs

Other than the comment above, this all looks good.

arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h line 112

Quoted 12 lines
arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +1 -0
index 93e2ecc..b1e096f 100644
@@ -106,6 +106,7 @@
 
 #define EP93XX_AAC_BASE			EP93XX_APB_IOMEM(0x00080000)
 
+#define EP93XX_SPI_PHYS_BASE		EP93XX_APB_PHYS(0x000a0000)
 #define EP93XX_SPI_BASE			EP93XX_APB_IOMEM(0x000a0000)
 
 #define EP93XX_IRDA_BASE		EP93XX_APB_IOMEM(0x000b0000)

This is good.

Quoted 17 lines
arch/arm/mach-ep93xx/include/mach/platform.h +2 -0
index c6dc14d..f11f1d7 100644
@@ -6,9 +6,11 @@
 
 struct i2c_gpio_platform_data;
 struct i2c_board_info;
+struct spi_board_info;
 struct platform_device;
 struct ep93xxfb_mach_info;
 struct ep93xx_keypad_platform_data;
+struct ep93xx_spi_info;
 
 struct ep93xx_eth_data
 {

arch/arm/mach-ep93xx/include/mach/platform.h static inline void ep93xx_devcfg_clear_bits(unsigned int bits) · line 45

Quoted 8 lines
arch/arm/mach-ep93xx/include/mach/platform.h +2 -0
@@ -36,6 +38,8 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits)
 void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
 void ep93xx_register_i2c(struct i2c_gpio_platform_data *data,
 			 struct i2c_board_info *devices, int num);
+void ep93xx_register_spi(struct ep93xx_spi_info *info,
+			 struct spi_board_info *devices, int num);
 void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
 void ep93xx_register_pwm(int pwm0, int pwm1);
 int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);

This is good.

Please change the one comment above. Then you have my:

Thanks,
Hartley

#11 lore raw

PATCH v6 2/2 ep93xx: SPI driver platform support code

Quoted 2 lines · H Hartley Sweeten

That patch is already present in linux-next. If Russell think there might be an issue you could rebase this patch on the linux-next tree.

If this patch depends on 5998/1, then I'll take it via the ep93xx branch (which has 5998/1 on.) It's only touching files in arch/arm so there shouldn't be any problem with that.

#12 lore raw

PATCH v6 2/2 ep93xx: SPI driver platform support code

Quoted 7 lines · Russell King
Quoted 2 lines · H Hartley Sweeten

That patch is already present in linux-next. If Russell think there might be an issue you could rebase this patch on the linux-next tree.

If this patch depends on 5998/1, then I'll take it via the ep93xx branch (which has 5998/1 on.) It's only touching files in arch/arm so there shouldn't be any problem with that.

Mika,

Any word from the spi group on PATCH v6 1/2?

Russell wants this patch (v6 2/2) to go through his tree, please add it to his patch system. It will not be a problem if this patch goes in before the actual driver since nothing "breaks". If the driver goes in first it will not work due to the missing clock resource, even if a user were to hack in the platform init code.

Regards,
Hartley

#13 lore raw

PATCH v6 2/2 ep93xx: SPI driver platform support code

Quoted 5 lines · H Hartley Sweeten
Quoted 4 lines · Russell King
Quoted 2 lines · H Hartley Sweeten

That patch is already present in linux-next. If Russell think there might be an issue you could rebase this patch on the linux-next tree.

Quoted 8 lines
Quoted 3 lines

If this patch depends on 5998/1, then I'll take it via the ep93xx branch (which has 5998/1 on.) It's only touching files in arch/arm so there shouldn't be any problem with that.

Mika,

Any word from the spi group on PATCH v6 1/2?

Nothing yet.

Quoted 5 lines

Russell wants this patch (v6 2/2) to go through his tree, please add it to his patch system. It will not be a problem if this patch goes in before the actual driver since nothing "breaks". If the driver goes in first it will not work due to the missing clock resource, even if a user were to hack in the platform init code.

OK, will do that later on today.

Regards,
MW