[PATCH] RFC: spi/sa1100: rewrite the SA1100 SPI driver
Linus Walleij
linus.walleij at linaro.org
Tue Jan 17 18:00:36 EST 2012
This heavily rewrites the SA1100 SPI driver and moves it to the
SPI subsystem. I seriously doubt it will work (though you're
encouraged to give it a spin). It is meant as a starting
point for others who are able to pick up on it. I discussed
this with Kristoffer some time back.
The Jornada 720 seems to be the only in-kernel user of the SSP,
so the MCU driver (now called jornada720_ssp.c) is now an
SPI device on the SPI bus, and the jornada720_ssp is just
"some SPI device". Anything generic (like GPIO toggling to
sync to the other side) is now in the SPI driver. The
spinlock across transfers found in jornada720_ssp is probably
not going to play well with the SPI subsystem so this has
been replaced by a mutex.
Cc: Kristoffer Ericson <kristoffer.ericson at gmail.com>
Cc: Nicolas Pitre <nicolas.pitre at linaro.org>
Cc: Russell King <rmk+kernel at arm.linux.org.uk>
Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
---
arch/arm/include/asm/hardware/ssp.h | 28 --
arch/arm/mach-sa1100/Kconfig | 10 +-
arch/arm/mach-sa1100/Makefile | 1 -
arch/arm/mach-sa1100/include/mach/SA-1100.h | 88 +-----
arch/arm/mach-sa1100/include/mach/sa1100-ssp.h | 15 +
arch/arm/mach-sa1100/jornada720.c | 52 +++-
arch/arm/mach-sa1100/jornada720_ssp.c | 79 ++---
arch/arm/mach-sa1100/ssp.c | 243 -------------
drivers/spi/Kconfig | 7 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-sa1100.c | 461 ++++++++++++++++++++++++
11 files changed, 569 insertions(+), 416 deletions(-)
delete mode 100644 arch/arm/include/asm/hardware/ssp.h
create mode 100644 arch/arm/mach-sa1100/include/mach/sa1100-ssp.h
delete mode 100644 arch/arm/mach-sa1100/ssp.c
create mode 100644 drivers/spi/spi-sa1100.c
diff --git a/arch/arm/include/asm/hardware/ssp.h b/arch/arm/include/asm/hardware/ssp.h
deleted file mode 100644
index 3b42e18..0000000
--- a/arch/arm/include/asm/hardware/ssp.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * ssp.h
- *
- * Copyright (C) 2003 Russell King, All Rights Reserved.
- *
- * 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.
- */
-#ifndef SSP_H
-#define SSP_H
-
-struct ssp_state {
- unsigned int cr0;
- unsigned int cr1;
-};
-
-int ssp_write_word(u16 data);
-int ssp_read_word(u16 *data);
-int ssp_flush(void);
-void ssp_enable(void);
-void ssp_disable(void);
-void ssp_save_state(struct ssp_state *ssp);
-void ssp_restore_state(struct ssp_state *ssp);
-int ssp_init(void);
-void ssp_exit(void);
-
-#endif
diff --git a/arch/arm/mach-sa1100/Kconfig b/arch/arm/mach-sa1100/Kconfig
index 42625e4..cb1c115 100644
--- a/arch/arm/mach-sa1100/Kconfig
+++ b/arch/arm/mach-sa1100/Kconfig
@@ -95,7 +95,8 @@ config SA1100_JORNADA720
config SA1100_JORNADA720_SSP
bool "HP Jornada 720 Extended SSP driver"
- select SA1100_SSP
+ select SPI
+ select SPI_SA1100
depends on SA1100_JORNADA720
help
Say Y here if you have a HP Jornada 7xx handheld computer and you
@@ -157,13 +158,6 @@ config SA1100_SIMPAD
like CL4 in additional it has a PCMCIA-Slot. For more information
visit <http://www.usa.siemens.com/> or <http://www.siemens.ch/>.
-config SA1100_SSP
- tristate "Generic PIO SSP"
- help
- Say Y here to enable support for the generic PIO SSP driver.
- This isn't for audio support, but for attached sensors and
- other devices, eg for BadgePAD 4 sensor support.
-
endmenu
endif
diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile
index ed7408d..7b47672 100644
--- a/arch/arm/mach-sa1100/Makefile
+++ b/arch/arm/mach-sa1100/Makefile
@@ -51,5 +51,4 @@ obj-$(CONFIG_LEDS) += $(led-y)
# Miscellaneous functions
obj-$(CONFIG_PM) += pm.o sleep.o
-obj-$(CONFIG_SA1100_SSP) += ssp.o
diff --git a/arch/arm/mach-sa1100/include/mach/SA-1100.h b/arch/arm/mach-sa1100/include/mach/SA-1100.h
index bae8296..ed68746 100644
--- a/arch/arm/mach-sa1100/include/mach/SA-1100.h
+++ b/arch/arm/mach-sa1100/include/mach/SA-1100.h
@@ -727,86 +727,10 @@
#define MCCR1_F10MHz (MCCR1_CFS*1) /* Freq. (fmc) = ~ 10 MHz */
/* (9.585 MHz) */
-
-/*
- * Synchronous Serial Port (SSP) control registers
- *
- * Registers
- * Ser4SSCR0 Serial port 4 Synchronous Serial Port (SSP) Control
- * Register 0 (read/write).
- * Ser4SSCR1 Serial port 4 Synchronous Serial Port (SSP) Control
- * Register 1 (read/write).
- * [Bits SPO and SP are only implemented in versions 2.0
- * (rev. = 8) and higher of the StrongARM SA-1100.]
- * Ser4SSDR Serial port 4 Synchronous Serial Port (SSP) Data
- * Register (read/write).
- * Ser4SSSR Serial port 4 Synchronous Serial Port (SSP) Status
- * Register (read/write).
- *
- * Clocks
- * fxtl, Txtl Frequency, period of the system crystal (3.6864 MHz
- * or 3.5795 MHz).
- * fss, Tss Frequency, period of the SSP communication.
- */
-
-#define Ser4SSCR0 __REG(0x80070060) /* Ser. port 4 SSP Control Reg. 0 */
-#define Ser4SSCR1 __REG(0x80070064) /* Ser. port 4 SSP Control Reg. 1 */
-#define Ser4SSDR __REG(0x8007006C) /* Ser. port 4 SSP Data Reg. */
-#define Ser4SSSR __REG(0x80070074) /* Ser. port 4 SSP Status Reg. */
-
-#define SSCR0_DSS Fld (4, 0) /* Data Size - 1 Select [3..15] */
-#define SSCR0_DataSize(Size) /* Data Size Select [4..16] */ \
- (((Size) - 1) << FShft (SSCR0_DSS))
-#define SSCR0_FRF Fld (2, 4) /* FRame Format */
-#define SSCR0_Motorola /* Motorola Serial Peripheral */ \
- /* Interface (SPI) format */ \
- (0 << FShft (SSCR0_FRF))
-#define SSCR0_TI /* Texas Instruments Synchronous */ \
- /* Serial format */ \
- (1 << FShft (SSCR0_FRF))
-#define SSCR0_National /* National Microwire format */ \
- (2 << FShft (SSCR0_FRF))
-#define SSCR0_SSE 0x00000080 /* SSP Enable */
-#define SSCR0_SCR Fld (8, 8) /* Serial Clock Rate divisor/2 - 1 */
- /* fss = fxtl/(2*(SCR + 1)) */
- /* Tss = 2*(SCR + 1)*Txtl */
-#define SSCR0_SerClkDiv(Div) /* Serial Clock Divisor [2..512] */ \
- (((Div) - 2)/2 << FShft (SSCR0_SCR))
- /* fss = fxtl/(2*Floor (Div/2)) */
- /* Tss = 2*Floor (Div/2)*Txtl */
-#define SSCR0_CeilSerClkDiv(Div) /* Ceil. of SerClkDiv [2..512] */ \
- (((Div) - 1)/2 << FShft (SSCR0_SCR))
- /* fss = fxtl/(2*Ceil (Div/2)) */
- /* Tss = 2*Ceil (Div/2)*Txtl */
-
-#define SSCR1_RIE 0x00000001 /* Receive FIFO 1/2-full or more */
- /* Interrupt Enable */
-#define SSCR1_TIE 0x00000002 /* Transmit FIFO 1/2-full or less */
- /* Interrupt Enable */
-#define SSCR1_LBM 0x00000004 /* Look-Back Mode */
-#define SSCR1_SPO 0x00000008 /* Sample clock (SCLK) POlarity */
-#define SSCR1_SClkIactL (SSCR1_SPO*0) /* Sample Clock Inactive Low */
-#define SSCR1_SClkIactH (SSCR1_SPO*1) /* Sample Clock Inactive High */
-#define SSCR1_SP 0x00000010 /* Sample clock (SCLK) Phase */
-#define SSCR1_SClk1P (SSCR1_SP*0) /* Sample Clock active 1 Period */
- /* after frame (SFRM, 1st edge) */
-#define SSCR1_SClk1_2P (SSCR1_SP*1) /* Sample Clock active 1/2 Period */
- /* after frame (SFRM, 1st edge) */
-#define SSCR1_ECS 0x00000020 /* External Clock Select */
-#define SSCR1_IntClk (SSCR1_ECS*0) /* Internal Clock */
-#define SSCR1_ExtClk (SSCR1_ECS*1) /* External Clock (GPIO [19]) */
-
-#define SSDR_DATA Fld (16, 0) /* receive/transmit DATA FIFOs */
-
-#define SSSR_TNF 0x00000002 /* Transmit FIFO Not Full (read) */
-#define SSSR_RNE 0x00000004 /* Receive FIFO Not Empty (read) */
-#define SSSR_BSY 0x00000008 /* SSP BuSY (read) */
-#define SSSR_TFS 0x00000010 /* Transmit FIFO 1/2-full or less */
- /* Service request (read) */
-#define SSSR_RFS 0x00000020 /* Receive FIFO 1/2-full or more */
- /* Service request (read) */
-#define SSSR_ROR 0x00000040 /* Receive FIFO Over-Run */
-
+/* The driver will contain the offsets for this peripheral */
+#define Ser4SSBase 0x80070000
+/* Ser. port 4 SSP Data Reg. needed by DMA defines */
+#define Ser4SSDR_DMA __REG(Ser4SSBase + 0x6C)
/*
* Operating System (OS) timer control registers
@@ -1789,10 +1713,10 @@
DDAR_Ser4MCP1Rc + DDAR_DevAdd (__PREG(Ser4MCDR1)))
#define DDAR_Ser4SSPWr /* Ser. port 4 SSP Write (16 bits) */ \
(DDAR_DevWr + DDAR_Brst4 + DDAR_16BitDev + \
- DDAR_Ser4SSPTr + DDAR_DevAdd (__PREG(Ser4SSDR)))
+ DDAR_Ser4SSPTr + DDAR_DevAdd (__PREG(Ser4SSDR_DMA)))
#define DDAR_Ser4SSPRd /* Ser. port 4 SSP Read (16 bits) */ \
(DDAR_DevRd + DDAR_Brst4 + DDAR_16BitDev + \
- DDAR_Ser4SSPRc + DDAR_DevAdd (__PREG(Ser4SSDR)))
+ DDAR_Ser4SSPRc + DDAR_DevAdd (__PREG(Ser4SSDR_DMA)))
#define DCSR_RUN 0x00000001 /* DMA running */
#define DCSR_IE 0x00000002 /* DMA Interrupt Enable */
diff --git a/arch/arm/mach-sa1100/include/mach/sa1100-ssp.h b/arch/arm/mach-sa1100/include/mach/sa1100-ssp.h
new file mode 100644
index 0000000..3d3df47
--- /dev/null
+++ b/arch/arm/mach-sa1100/include/mach/sa1100-ssp.h
@@ -0,0 +1,15 @@
+/*
+ * Header file for the SA1100 SPI driver
+ */
+
+/**
+ * struct sa1100_ssp_plat_data - platform data for SA1100 SSP driver
+ * @en_gpio: GPIO pin to enable SSP device on the other end
+ * @rdy_gpio: GPIO pin to listen for ready flag for SSP device on other end -
+ * when this is given this GPIO pin will be polled for retrieveing
+ * words instead of using IRQ
+ */
+struct sa1100_ssp_plat_data {
+ unsigned int en_gpio;
+ unsigned int rdy_gpio;
+};
diff --git a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c
index 77198fe..b675be1 100644
--- a/arch/arm/mach-sa1100/jornada720.c
+++ b/arch/arm/mach-sa1100/jornada720.c
@@ -21,9 +21,11 @@
#include <linux/ioport.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/spi/spi.h>
#include <video/s1d13xxxfb.h>
#include <mach/hardware.h>
+#include <mach/sa1100-ssp.h>
#include <asm/hardware/sa1111.h>
#include <asm/irq.h>
#include <asm/page.h>
@@ -227,11 +229,6 @@ static struct platform_device sa1111_device = {
.resource = sa1111_resources,
};
-static struct platform_device jornada_ssp_device = {
- .name = "jornada_ssp",
- .id = -1,
-};
-
static struct platform_device jornada_kbd_device = {
.name = "jornada720_kbd",
.id = -1,
@@ -242,7 +239,35 @@ static struct platform_device jornada_ts_device = {
.id = -1,
};
-static struct platform_device *devices[] __initdata = {
+static struct sa1100_ssp_plat_data jornada_ssp_plat = {
+ .en_gpio = GPIO_GPIO25,
+ .rdy_gpio = GPIO_GPIO10,
+};
+
+static struct resource jornada_ssp_resources[] = {
+ [0] = {
+ .start = Ser4SSBase,
+ .end = Ser4SSBase + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_Ser4SSP,
+ .end = IRQ_Ser4SSP,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device jornada_ssp_device = {
+ .name = "sa1100-ssp",
+ .id = 0,
+ .dev = {
+ .platform_data = &jornada_ssp_plat,
+ },
+ .num_resources = ARRAY_SIZE(jornada_ssp_resources),
+ .resource = jornada_ssp_resources,
+};
+
+static struct platform_device *jornada_devices[] __initdata = {
&sa1111_device,
&jornada_ssp_device,
&s1d13xxxfb_device,
@@ -250,6 +275,15 @@ static struct platform_device *devices[] __initdata = {
&jornada_ts_device,
};
+/* Todo, rename this device, it's driver and all symbols "jornada mcu" */
+static struct spi_board_info jornada_spi_devices[] = {
+ {
+ .modalias = "jornada_ssp",
+ .bus_num = 0,
+ .chip_select = 0,
+ }
+};
+
static int __init jornada720_init(void)
{
int ret = -ENODEV;
@@ -265,7 +299,11 @@ static int __init jornada720_init(void)
GPSR = GPIO_GPIO20; /* restart gpio20 */
udelay(20); /* give it some time to restart */
- ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ /* Register SPI board data, then the platform devices */
+ spi_register_board_info(jornada_spi_devices,
+ ARRAY_SIZE(jornada_spi_devices));
+ ret = platform_add_devices(jornada_devices,
+ ARRAY_SIZE(jornada_devices));
}
return ret;
diff --git a/arch/arm/mach-sa1100/jornada720_ssp.c b/arch/arm/mach-sa1100/jornada720_ssp.c
index b412fc0..499c14c 100644
--- a/arch/arm/mach-sa1100/jornada720_ssp.c
+++ b/arch/arm/mach-sa1100/jornada720_ssp.c
@@ -16,15 +16,14 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/sched.h>
+#include <linux/spi/spi.h>
#include <mach/hardware.h>
#include <mach/jornada720.h>
-#include <asm/hardware/ssp.h>
-static DEFINE_SPINLOCK(jornada_ssp_lock);
-static unsigned long jornada_ssp_flags;
+static DEFINE_MUTEX(jornada_ssp_lock);
+static struct spi_device *spi;
/**
* jornada_ssp_reverse - reverses input byte
@@ -57,23 +56,25 @@ EXPORT_SYMBOL(jornada_ssp_reverse);
*/
int jornada_ssp_byte(u8 byte)
{
- int timeout = 400000;
- u16 ret;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ u16 txword;
+ u16 rxword;
+ int ret;
- while ((GPLR & GPIO_GPIO10)) {
- if (!--timeout) {
- printk(KERN_WARNING "SSP: timeout while waiting for transmit\n");
- return -ETIMEDOUT;
- }
- cpu_relax();
- }
+ txword = jornada_ssp_reverse(byte) << 8;
- ret = jornada_ssp_reverse(byte) << 8;
+ xfer.tx_buf = &txword;
+ xfer.rx_buf = &rxword;
+ xfer.len = sizeof(u16);
- ssp_write_word(ret);
- ssp_read_word(&ret);
+ spi_message_init(&msg);
+ spi_message_add_tail(&xfer, &msg);
+ ret = spi_sync(spi, &msg);
+ if (ret < 0)
+ return ret;
- return jornada_ssp_reverse(ret);
+ return jornada_ssp_reverse(rxword);
};
EXPORT_SYMBOL(jornada_ssp_byte);
@@ -110,8 +111,7 @@ EXPORT_SYMBOL(jornada_ssp_inout);
*/
void jornada_ssp_start(void)
{
- spin_lock_irqsave(&jornada_ssp_lock, jornada_ssp_flags);
- GPCR = GPIO_GPIO25;
+ mutex_lock(&jornada_ssp_lock);
udelay(50);
return;
};
@@ -123,35 +123,22 @@ EXPORT_SYMBOL(jornada_ssp_start);
*/
void jornada_ssp_end(void)
{
- GPSR = GPIO_GPIO25;
- spin_unlock_irqrestore(&jornada_ssp_lock, jornada_ssp_flags);
+ mutex_unlock(&jornada_ssp_lock);
return;
};
EXPORT_SYMBOL(jornada_ssp_end);
-static int __devinit jornada_ssp_probe(struct platform_device *dev)
+static int __devinit jornada_ssp_probe(struct spi_device *spi)
{
int ret;
- GPSR = GPIO_GPIO25;
-
- ret = ssp_init();
-
- /* worked fine, lets not bother with anything else */
- if (!ret) {
- printk(KERN_INFO "SSP: device initialized with irq\n");
+ spi->bits_per_word = 16;
+ ret = spi_setup(spi);
+ if (ret < 0)
return ret;
- }
-
- printk(KERN_WARNING "SSP: initialization failed, trying non-irq solution \n");
/* init of Serial 4 port */
Ser4MCCR0 = 0;
- Ser4SSCR0 = 0x0387;
- Ser4SSCR1 = 0x18;
-
- /* clear out any left over data */
- ssp_flush();
/* enable MCU */
jornada_ssp_start();
@@ -167,36 +154,34 @@ static int __devinit jornada_ssp_probe(struct platform_device *dev)
/* failed, lets just kill everything */
if (ret == -ETIMEDOUT) {
- printk(KERN_WARNING "SSP: attempts failed, bailing\n");
- ssp_exit();
+ printk(KERN_WARNING "Jornada SSP: attempts failed, bailing\n");
return -ENODEV;
}
/* all fine */
- printk(KERN_INFO "SSP: device initialized\n");
+ printk(KERN_INFO "Jornada SSP: device initialized\n");
return 0;
};
-static int jornada_ssp_remove(struct platform_device *dev)
+static int jornada_ssp_remove(struct spi_device *spi)
{
- /* Note that this doesn't actually remove the driver, since theres nothing to remove
- * It just makes sure everything is turned off */
- GPSR = GPIO_GPIO25;
- ssp_exit();
return 0;
};
-struct platform_driver jornadassp_driver = {
+struct spi_driver jornadassp_driver = {
.probe = jornada_ssp_probe,
.remove = jornada_ssp_remove,
.driver = {
.name = "jornada_ssp",
+ .owner = THIS_MODULE,
+ .bus = &spi_bus_type,
},
};
static int __init jornada_ssp_init(void)
{
- return platform_driver_register(&jornadassp_driver);
+ return spi_register_driver(&jornadassp_driver);
}
module_init(jornada_ssp_init);
+MODULE_ALIAS("spi:jornada_ssp");
diff --git a/arch/arm/mach-sa1100/ssp.c b/arch/arm/mach-sa1100/ssp.c
deleted file mode 100644
index b20ff93..0000000
--- a/arch/arm/mach-sa1100/ssp.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/ssp.c
- *
- * Copyright (C) 2003 Russell King.
- *
- * 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.
- *
- * Generic SSP driver. This provides the generic core for simple
- * IO-based SSP applications.
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/io.h>
-
-#include <asm/irq.h>
-#include <mach/hardware.h>
-#include <asm/hardware/ssp.h>
-
-#define TIMEOUT 100000
-
-static irqreturn_t ssp_interrupt(int irq, void *dev_id)
-{
- unsigned int status = Ser4SSSR;
-
- if (status & SSSR_ROR)
- printk(KERN_WARNING "SSP: receiver overrun\n");
-
- Ser4SSSR = SSSR_ROR;
-
- return status ? IRQ_HANDLED : IRQ_NONE;
-}
-
-/**
- * ssp_write_word - write a word to the SSP port
- * @data: 16-bit, MSB justified data to write.
- *
- * Wait for a free entry in the SSP transmit FIFO, and write a data
- * word to the SSP port. Wait for the SSP port to start sending
- * the data.
- *
- * The caller is expected to perform the necessary locking.
- *
- * Returns:
- * %-ETIMEDOUT timeout occurred
- * 0 success
- */
-int ssp_write_word(u16 data)
-{
- int timeout = TIMEOUT;
-
- while (!(Ser4SSSR & SSSR_TNF)) {
- if (!--timeout)
- return -ETIMEDOUT;
- cpu_relax();
- }
-
- Ser4SSDR = data;
-
- timeout = TIMEOUT;
- while (!(Ser4SSSR & SSSR_BSY)) {
- if (!--timeout)
- return -ETIMEDOUT;
- cpu_relax();
- }
-
- return 0;
-}
-
-/**
- * ssp_read_word - read a word from the SSP port
- *
- * Wait for a data word in the SSP receive FIFO, and return the
- * received data. Data is LSB justified.
- *
- * Note: Currently, if data is not expected to be received, this
- * function will wait for ever.
- *
- * The caller is expected to perform the necessary locking.
- *
- * Returns:
- * %-ETIMEDOUT timeout occurred
- * 16-bit data success
- */
-int ssp_read_word(u16 *data)
-{
- int timeout = TIMEOUT;
-
- while (!(Ser4SSSR & SSSR_RNE)) {
- if (!--timeout)
- return -ETIMEDOUT;
- cpu_relax();
- }
-
- *data = (u16)Ser4SSDR;
-
- return 0;
-}
-
-/**
- * ssp_flush - flush the transmit and receive FIFOs
- *
- * Wait for the SSP to idle, and ensure that the receive FIFO
- * is empty.
- *
- * The caller is expected to perform the necessary locking.
- *
- * Returns:
- * %-ETIMEDOUT timeout occurred
- * 0 success
- */
-int ssp_flush(void)
-{
- int timeout = TIMEOUT * 2;
-
- do {
- while (Ser4SSSR & SSSR_RNE) {
- if (!--timeout)
- return -ETIMEDOUT;
- (void) Ser4SSDR;
- }
- if (!--timeout)
- return -ETIMEDOUT;
- } while (Ser4SSSR & SSSR_BSY);
-
- return 0;
-}
-
-/**
- * ssp_enable - enable the SSP port
- *
- * Turn on the SSP port.
- */
-void ssp_enable(void)
-{
- Ser4SSCR0 |= SSCR0_SSE;
-}
-
-/**
- * ssp_disable - shut down the SSP port
- *
- * Turn off the SSP port, optionally powering it down.
- */
-void ssp_disable(void)
-{
- Ser4SSCR0 &= ~SSCR0_SSE;
-}
-
-/**
- * ssp_save_state - save the SSP configuration
- * @ssp: pointer to structure to save SSP configuration
- *
- * Save the configured SSP state for suspend.
- */
-void ssp_save_state(struct ssp_state *ssp)
-{
- ssp->cr0 = Ser4SSCR0;
- ssp->cr1 = Ser4SSCR1;
-
- Ser4SSCR0 &= ~SSCR0_SSE;
-}
-
-/**
- * ssp_restore_state - restore a previously saved SSP configuration
- * @ssp: pointer to configuration saved by ssp_save_state
- *
- * Restore the SSP configuration saved previously by ssp_save_state.
- */
-void ssp_restore_state(struct ssp_state *ssp)
-{
- Ser4SSSR = SSSR_ROR;
-
- Ser4SSCR0 = ssp->cr0 & ~SSCR0_SSE;
- Ser4SSCR1 = ssp->cr1;
- Ser4SSCR0 = ssp->cr0;
-}
-
-/**
- * ssp_init - setup the SSP port
- *
- * initialise and claim resources for the SSP port.
- *
- * Returns:
- * %-ENODEV if the SSP port is unavailable
- * %-EBUSY if the resources are already in use
- * %0 on success
- */
-int ssp_init(void)
-{
- int ret;
-
- if (!(PPAR & PPAR_SPR) && (Ser4MCCR0 & MCCR0_MCE))
- return -ENODEV;
-
- if (!request_mem_region(__PREG(Ser4SSCR0), 0x18, "SSP")) {
- return -EBUSY;
- }
-
- Ser4SSSR = SSSR_ROR;
-
- ret = request_irq(IRQ_Ser4SSP, ssp_interrupt, 0, "SSP", NULL);
- if (ret)
- goto out_region;
-
- return 0;
-
- out_region:
- release_mem_region(__PREG(Ser4SSCR0), 0x18);
- return ret;
-}
-
-/**
- * ssp_exit - undo the effects of ssp_init
- *
- * release and free resources for the SSP port.
- */
-void ssp_exit(void)
-{
- Ser4SSCR0 &= ~SSCR0_SSE;
-
- free_irq(IRQ_Ser4SSP, NULL);
- release_mem_region(__PREG(Ser4SSCR0), 0x18);
-}
-
-MODULE_AUTHOR("Russell King");
-MODULE_DESCRIPTION("SA11x0 SSP PIO driver");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(ssp_write_word);
-EXPORT_SYMBOL(ssp_read_word);
-EXPORT_SYMBOL(ssp_flush);
-EXPORT_SYMBOL(ssp_enable);
-EXPORT_SYMBOL(ssp_disable);
-EXPORT_SYMBOL(ssp_save_state);
-EXPORT_SYMBOL(ssp_restore_state);
-EXPORT_SYMBOL(ssp_init);
-EXPORT_SYMBOL(ssp_exit);
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8ba4510..c5e36a2 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -305,6 +305,13 @@ config SPI_S3C64XX
help
SPI driver for Samsung S3C64XX and newer SoCs.
+config SPI_SA1100
+ tristate "SA1100 PIO SSP"
+ help
+ Say Y here to enable support for the SA1100 SSP/SPI driver.
+ This isn't for audio support, but for attached sensors and
+ other devices, eg for BadgePAD 4 sensor support.
+
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
depends on SUPERH && HAVE_CLK
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 61c3261..87ae268 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
spi-s3c24xx-hw-y := spi-s3c24xx.o
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
obj-$(CONFIG_SPI_S3C64XX) += spi-s3c64xx.o
+obj-$(CONFIG_SPI_SA1100) += spi-sa1100.o
obj-$(CONFIG_SPI_SH) += spi-sh.o
obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o
obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o
diff --git a/drivers/spi/spi-sa1100.c b/drivers/spi/spi-sa1100.c
new file mode 100644
index 0000000..94e5cc3
--- /dev/null
+++ b/drivers/spi/spi-sa1100.c
@@ -0,0 +1,461 @@
+/*
+ * Generic SA1100 and Jornada 720 derivate SSP/SPI driver
+ *
+ * Copyright (C) 2003 Russell King.
+ * Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson at gmail.com>
+ * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski at tefnet.pl>
+ * Copyright (C) 2011 Linus Walleij <linus.walleij at linaro.org>
+ *
+ * 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.
+ *
+ * Generic SSP driver. This provides the generic core for simple
+ * IO-based SSP applications. It also encompasses the SSP driver for the
+ * HP Jornada 710/720/72 which is currently the only user of it.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include <mach/jornada720.h>
+#include <mach/sa1100-ssp.h>
+
+/*
+ * TODO: tidy up this documentation (copied verbatim)
+ *
+ * Synchronous Serial Port (SSP) control registers
+ *
+ * Registers
+ * SSCR0 Serial port 4 Synchronous Serial Port (SSP) Control
+ * Register 0 (read/write).
+ * SSCR1 Serial port 4 Synchronous Serial Port (SSP) Control
+ * Register 1 (read/write).
+ * [Bits SPO and SP are only implemented in versions 2.0
+ * (rev. = 8) and higher of the StrongARM SA-1100.]
+ * SSDR Serial port 4 Synchronous Serial Port (SSP) Data
+ * Register (read/write).
+ * SSSR Serial port 4 Synchronous Serial Port (SSP) Status
+ * Register (read/write).
+ *
+ * Clocks
+ * fxtl, Txtl Frequency, period of the system crystal (3.6864 MHz
+ * or 3.5795 MHz).
+ * fss, Tss Frequency, period of the SSP communication.
+ */
+
+#define SSCR0 0x60 /* Ser. port 4 SSP Control Reg. 0 */
+#define SSCR1 0x64 /* Ser. port 4 SSP Control Reg. 1 */
+#define SSDR 0x6C /* Ser. port 4 SSP Data Reg. */
+#define SSSR 0x74 /* Ser. port 4 SSP Status Reg. */
+
+#define SSCR0_DSS Fld (4, 0) /* Data Size - 1 Select [3..15] */
+#define SSCR0_DataSize(Size) /* Data Size Select [4..16] */ \
+ (((Size) - 1) << FShft (SSCR0_DSS))
+#define SSCR0_FRF Fld (2, 4) /* FRame Format */
+#define SSCR0_Motorola /* Motorola Serial Peripheral */ \
+ /* Interface (SPI) format */ \
+ (0 << FShft (SSCR0_FRF))
+#define SSCR0_TI /* Texas Instruments Synchronous */ \
+ /* Serial format */ \
+ (1 << FShft (SSCR0_FRF))
+#define SSCR0_National /* National Microwire format */ \
+ (2 << FShft (SSCR0_FRF))
+#define SSCR0_SSE 0x00000080 /* SSP Enable */
+#define SSCR0_SCR Fld (8, 8) /* Serial Clock Rate divisor/2 - 1 */
+ /* fss = fxtl/(2*(SCR + 1)) */
+ /* Tss = 2*(SCR + 1)*Txtl */
+#define SSCR0_SerClkDiv(Div) /* Serial Clock Divisor [2..512] */ \
+ (((Div) - 2)/2 << FShft (SSCR0_SCR))
+ /* fss = fxtl/(2*Floor (Div/2)) */
+ /* Tss = 2*Floor (Div/2)*Txtl */
+#define SSCR0_CeilSerClkDiv(Div) /* Ceil. of SerClkDiv [2..512] */ \
+ (((Div) - 1)/2 << FShft (SSCR0_SCR))
+ /* fss = fxtl/(2*Ceil (Div/2)) */
+ /* Tss = 2*Ceil (Div/2)*Txtl */
+
+#define SSCR1_RIE 0x00000001 /* Receive FIFO 1/2-full or more */
+ /* Interrupt Enable */
+#define SSCR1_TIE 0x00000002 /* Transmit FIFO 1/2-full or less */
+ /* Interrupt Enable */
+#define SSCR1_LBM 0x00000004 /* Look-Back Mode */
+#define SSCR1_SPO 0x00000008 /* Sample clock (SCLK) POlarity */
+#define SSCR1_SClkIactL (SSCR1_SPO*0) /* Sample Clock Inactive Low */
+#define SSCR1_SClkIactH (SSCR1_SPO*1) /* Sample Clock Inactive High */
+#define SSCR1_SP 0x00000010 /* Sample clock (SCLK) Phase */
+#define SSCR1_SClk1P (SSCR1_SP*0) /* Sample Clock active 1 Period */
+ /* after frame (SFRM, 1st edge) */
+#define SSCR1_SClk1_2P (SSCR1_SP*1) /* Sample Clock active 1/2 Period */
+ /* after frame (SFRM, 1st edge) */
+#define SSCR1_ECS 0x00000020 /* External Clock Select */
+#define SSCR1_IntClk (SSCR1_ECS*0) /* Internal Clock */
+#define SSCR1_ExtClk (SSCR1_ECS*1) /* External Clock (GPIO [19]) */
+
+#define SSDR_DATA Fld (16, 0) /* receive/transmit DATA FIFOs */
+
+#define SSSR_TNF 0x00000002 /* Transmit FIFO Not Full (read) */
+#define SSSR_RNE 0x00000004 /* Receive FIFO Not Empty (read) */
+#define SSSR_BSY 0x00000008 /* SSP BuSY (read) */
+#define SSSR_TFS 0x00000010 /* Transmit FIFO 1/2-full or less */
+ /* Service request (read) */
+#define SSSR_RFS 0x00000020 /* Receive FIFO 1/2-full or more */
+ /* Service request (read) */
+#define SSSR_ROR 0x00000040 /* Receive FIFO Over-Run */
+
+#define DRV_NAME "sa1100-ssp"
+#define TIMEOUT 100000
+
+struct ssp_state {
+ struct spi_master *master;
+ struct device *dev;
+ struct resource *memres;
+ void __iomem *base;
+ int irq;
+ bool j720;
+ bool swizzle;
+ unsigned int en_gpio;
+ unsigned int rdy_gpio;
+ unsigned int cr0;
+ unsigned int cr1;
+};
+
+static inline bool sa1100_gpio_is_valid(unsigned int gpio)
+{
+ return gpio >= 0;
+}
+
+static irqreturn_t sa1100_ssp_interrupt(int irq, void *dev_id)
+{
+ struct ssp_state *ssp = dev_id;
+ u32 status = readl(ssp->base + SSSR);
+
+ if (status & SSSR_ROR)
+ dev_warn(ssp->dev, "receiver overrun\n");
+
+ writel(SSSR_ROR, ssp->base + SSSR);
+
+ return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/**
+ * sa1100_ssp_write_word - write a word to the SSP port
+ * @data: 16-bit, MSB justified data to write.
+ *
+ * Wait for a free entry in the SSP transmit FIFO, and write a data
+ * word to the SSP port. Wait for the SSP port to start sending
+ * the data.
+ *
+ * The caller is expected to perform the necessary locking.
+ *
+ * Returns:
+ * %-ETIMEDOUT timeout occurred
+ * 0 success
+ */
+static int sa1100_ssp_write_word(struct ssp_state *ssp, u16 data)
+{
+ int timeout = 400000; /* GPIO timeout */
+
+ /* If there is a GPIO ready pin to wait on */
+ if (sa1100_gpio_is_valid(ssp->rdy_gpio)) {
+ while ((GPLR & ssp->rdy_gpio)) {
+ if (!--timeout) {
+ dev_err(ssp->dev, "GPIO ready timeout\n");
+ return -ETIMEDOUT;
+ }
+ cpu_relax();
+ }
+ }
+
+ timeout = TIMEOUT;
+ while (!(readl(ssp->base + SSSR) & SSSR_TNF)) {
+ if (!--timeout)
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ writel(data, ssp->base + SSDR);
+
+ timeout = TIMEOUT;
+ while (!(readl(ssp->base + SSSR) & SSSR_BSY)) {
+ if (!--timeout)
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ return 0;
+}
+
+/**
+ * sa1100_ssp_read_word - read a word from the SSP port
+ *
+ * Wait for a data word in the SSP receive FIFO, and return the
+ * received data. Data is LSB justified.
+ *
+ * Note: Currently, if data is not expected to be received, this
+ * function will wait for ever.
+ *
+ * The caller is expected to perform the necessary locking.
+ *
+ * Returns:
+ * %-ETIMEDOUT timeout occurred
+ * 16-bit data success
+ */
+static int sa1100_ssp_read_word(struct ssp_state *ssp, u16 *data)
+{
+ int timeout = TIMEOUT;
+
+ while (!(readl(ssp->base + SSSR) & SSSR_RNE)) {
+ if (!--timeout)
+ return -ETIMEDOUT;
+ cpu_relax();
+ }
+
+ *data = (u16) readl(ssp->base + SSDR);
+
+ return 0;
+}
+
+/**
+ * sa1100_ssp_flush - flush the transmit and receive FIFOs
+ *
+ * Wait for the SSP to idle, and ensure that the receive FIFO
+ * is empty.
+ *
+ * The caller is expected to perform the necessary locking.
+ *
+ * Returns:
+ * %-ETIMEDOUT timeout occurred
+ * 0 success
+ */
+static int sa1100_ssp_flush(struct ssp_state *ssp)
+{
+ int timeout = TIMEOUT * 2;
+
+ do {
+ while (readl(ssp->base + SSSR) & SSSR_RNE) {
+ if (!--timeout)
+ return -ETIMEDOUT;
+ (void) readl(ssp->base + SSDR);
+ }
+ if (!--timeout)
+ return -ETIMEDOUT;
+ } while (readl(ssp->base + SSSR) & SSSR_BSY);
+
+ return 0;
+}
+
+/**
+ * sa1100_ssp_enable - enable the SSP port
+ *
+ * Turn on the SSP port.
+ */
+static void sa1100_ssp_enable(struct ssp_state *ssp)
+{
+ /*
+ * Enable SSP device on other end with GPIO (GPIO active low)
+ */
+ if (sa1100_gpio_is_valid(ssp->en_gpio))
+ GPCR = ssp->en_gpio;
+ writel((readl(ssp->base + SSCR0) | SSCR0_SSE), ssp->base + SSCR0);
+}
+
+/**
+ * sa1100_ssp_disable - shut down the SSP port
+ *
+ * Turn off the SSP port, optionally powering it down.
+ */
+static void sa1100_ssp_disable(struct ssp_state *ssp)
+{
+ /* Disable SSP device on other end (GPIO active low) */
+ if (sa1100_gpio_is_valid(ssp->en_gpio))
+ GPSR = ssp->en_gpio;
+ writel((readl(ssp->base + SSCR0) & ~SSCR0_SSE), ssp->base + SSCR0);
+}
+
+static int sa1100_readwrite(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct ssp_state *ssp = spi_master_get_devdata(spi->master);
+ int bpw = spi->bits_per_word;
+ unsigned count;
+ const u16 *tx = t->tx_buf;
+ u16 *rx = t->rx_buf;
+ int ret;
+
+ /*
+ * This SSP can do 16 bit transfers only
+ */
+ if (t->bits_per_word)
+ bpw = t->bits_per_word;
+ if (bpw != 16)
+ return -EIO;
+
+ count = t->len;
+
+ do {
+ ret = sa1100_ssp_write_word(ssp, *tx);
+ if (ret < 0)
+ goto out;
+ ret = sa1100_ssp_read_word(ssp, rx);
+ if (ret < 0)
+ goto out;
+ tx++;
+ rx++;
+ count -= 2;
+ } while (count);
+out:
+ return t->len - count;
+}
+
+static int sa1100_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct ssp_state *ssp = spi_master_get_devdata(spi->master);
+ struct spi_transfer *t = NULL;
+
+ if (!msg || !msg->complete)
+ return -EINVAL;
+
+ sa1100_ssp_enable(ssp);
+
+ /* Transfer message */
+ msg->actual_length = 0;
+ msg->status = -EINPROGRESS;
+
+ /* Iterate over the transfers in this message */
+ list_for_each_entry(t, &msg->transfers, transfer_list) {
+ if (t->len)
+ msg->actual_length += sa1100_readwrite(spi, t);
+ }
+ msg->status = 0;
+
+ /* disable mcu and unlock */
+ sa1100_ssp_disable(ssp);
+ return 0;
+};
+
+static int __devinit sa1100_ssp_probe(struct platform_device *pdev)
+{
+ struct sa1100_ssp_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct spi_master *master;
+ struct ssp_state *ssp;
+ int ret = -ENODEV;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*ssp));
+ if (!master)
+ return -ENOMEM;
+
+ /* setup the master state */
+ master->bus_num = pdev->id;
+ master->num_chipselect = 1;
+ master->mode_bits = SPI_CS_HIGH;
+ master->transfer = sa1100_transfer;
+
+ /* setup the driver state */
+ ssp = spi_master_get_devdata(master);
+ ssp->master = master;
+ ssp->dev = &pdev->dev;
+ ssp->en_gpio = pdata->en_gpio;
+ ssp->rdy_gpio = pdata->rdy_gpio;
+ platform_set_drvdata(pdev, ssp);
+
+ ssp->memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!ssp->memres)
+ goto out_no_memresource;
+
+ if (request_mem_region(ssp->memres->start,
+ resource_size(ssp->memres),
+ DRV_NAME) == NULL)
+ goto out_no_region;
+
+ ssp->base = ioremap(ssp->memres->start,
+ resource_size(ssp->memres));
+ if (ssp->base == NULL)
+ goto out_no_ioremap;
+
+ /* This also lowers any GPIO signal */
+ sa1100_ssp_disable(ssp);
+ if (!(PPAR & PPAR_SPR) && (Ser4MCCR0 & MCCR0_MCE))
+ goto out_no_dev;
+
+ writel(SSSR_ROR, ssp->base + SSSR);
+
+ ssp->irq = platform_get_irq(pdev, 0);
+ if (ssp->irq >= 0) {
+ ret = request_irq(ssp->irq, sa1100_ssp_interrupt,
+ 0, DRV_NAME, ssp);
+ if (!ret) {
+ /* With IRQ initialization we are done now */
+ dev_info(&pdev->dev, "irq initialized\n");
+ return ret;
+ }
+ }
+
+ /* Fall back to polling mode */
+ dev_warn(&pdev->dev,
+ "initialization failed, fallback to non-irq polling mode\n");
+
+ /* TODO: explain this magic */
+ writel(0x0387, ssp->base + SSCR0);
+ writel(0x18, ssp->base + SSCR1);
+
+ /* clear out any left over data */
+ sa1100_ssp_flush(ssp);
+
+ /* all fine */
+ dev_info(&pdev->dev, "device initialized\n");
+ return 0;
+
+out_no_dev:
+ iounmap(ssp->base);
+out_no_ioremap:
+ release_mem_region(ssp->memres->start, resource_size(ssp->memres));
+out_no_region:
+out_no_memresource:
+ devm_kfree(&pdev->dev, ssp);
+ return ret;
+}
+
+static int sa1100_ssp_remove(struct platform_device *pdev)
+{
+ struct ssp_state *ssp = platform_get_drvdata(pdev);
+
+ if (ssp) {
+ if (ssp->irq >= 0)
+ free_irq(ssp->irq, NULL);
+ sa1100_ssp_disable(ssp);
+ iounmap(ssp->base);
+ release_mem_region(ssp->memres->start,
+ resource_size(ssp->memres));
+ spi_master_put(ssp->master);
+ }
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+struct platform_driver sa1100_ssp_driver = {
+ .probe = sa1100_ssp_probe,
+ .remove = sa1100_ssp_remove,
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+static int __init sa1100_ssp_module_init(void)
+{
+ return platform_driver_register(&sa1100_ssp_driver);
+}
+subsys_initcall(sa1100_ssp_module_init);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("SA11x0 SSP PIO driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
--
1.7.7.5
More information about the linux-arm-kernel
mailing list