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.eric...@gmail.com>
Cc: Nicolas Pitre <nicolas.pi...@linaro.org>
Cc: Russell King <rmk+ker...@arm.linux.org.uk>
Signed-off-by: Linus Walleij <linus.wall...@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.eric...@gmail.com>
+ * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniew...@tefnet.pl>
+ * Copyright (C) 2011 Linus Walleij <linus.wall...@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


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

Reply via email to