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> --- ChangeLog v1->v2: - Rebase to v3.5-rc1 - Move SA1100 SSP platform data to <linux/platform_data> - Store bits per word (bpw) for the device in the state holder. - Rewrite to use the new transfer queue. - Delete reference to the FIFO data register from SA-1100.h since SA1100 now uses dmaengine and the device tells the engine what register to use. - Use devm_* family for iomap, irq request etc. Kristoffer, can you test this on the Jornada? I suspect you're the only one who can actually take the SSP driver for a ride on real hardware. --- 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 | 83 +----- 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 | 470 +++++++++++++++++++++++++++ include/linux/platform_data/sa1100-ssp.h | 15 + 11 files changed, 574 insertions(+), 415 deletions(-) delete mode 100644 arch/arm/include/asm/hardware/ssp.h delete mode 100644 arch/arm/mach-sa1100/ssp.c create mode 100644 drivers/spi/spi-sa1100.c create mode 100644 include/linux/platform_data/sa1100-ssp.h 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 60b97ec..b7f348e 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 3f2d1b6..b6310b2 100644 --- a/arch/arm/mach-sa1100/include/mach/SA-1100.h +++ b/arch/arm/mach-sa1100/include/mach/SA-1100.h @@ -727,86 +727,8 @@ #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 /* * Operating System (OS) timer control registers @@ -1594,7 +1516,6 @@ #define DMA_SIZE (6 * 0x20) #define DMA_PHYS 0xb0000000 - /* * Liquid Crystal Display (LCD) control registers * diff --git a/arch/arm/mach-sa1100/jornada720.c b/arch/arm/mach-sa1100/jornada720.c index e3084f4..8abd1b6 100644 --- a/arch/arm/mach-sa1100/jornada720.c +++ b/arch/arm/mach-sa1100/jornada720.c @@ -21,6 +21,8 @@ #include <linux/ioport.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <linux/spi/spi.h> +#include <linux/platform_data/sa1100-ssp.h> #include <video/s1d13xxxfb.h> #include <asm/hardware/sa1111.h> @@ -212,11 +214,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, @@ -227,7 +224,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, @@ -235,6 +260,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; @@ -250,7 +284,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 e22fca9..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 <mach/hardware.h> -#include <mach/irqs.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 cd2fe35..9c9ae71 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -316,6 +316,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 9d75d21..4109fcf 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -50,6 +50,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_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o diff --git a/drivers/spi/spi-sa1100.c b/drivers/spi/spi-sa1100.c new file mode 100644 index 0000000..5626288 --- /dev/null +++ b/drivers/spi/spi-sa1100.c @@ -0,0 +1,470 @@ +/* + * 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 <linux/platform_data/sa1100-ssp.h> + +#include <asm/irq.h> +#include <mach/hardware.h> +#include <mach/jornada720.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; + unsigned int en_gpio; + unsigned int rdy_gpio; + unsigned int cr0; + unsigned int cr1; + int bpw; +}; + +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 ssp_state *ssp, struct spi_transfer *t) +{ + int bpw = ssp->bpw; + 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_setup(struct spi_device *spi) +{ + struct ssp_state *ssp = spi_master_get_devdata(spi->master); + + ssp->bpw = spi->bits_per_word; + return 0; +} + +static int sa1100_prepare_transfer_hardware(struct spi_master *master) +{ + struct ssp_state *ssp = spi_master_get_devdata(master); + + sa1100_ssp_enable(ssp); + return 0; +} + +static int sa1100_unprepare_transfer_hardware(struct spi_master *master) +{ + struct ssp_state *ssp = spi_master_get_devdata(master); + + sa1100_ssp_disable(ssp); + return 0; +} + +static int sa1100_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct ssp_state *ssp = spi_master_get_devdata(master); + struct spi_transfer *t = NULL; + + /* Iterate over the transfers in this message */ + list_for_each_entry(t, &msg->transfers, transfer_list) { + if (t->len) + msg->actual_length += sa1100_readwrite(ssp, t); + } + msg->status = 0; + 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->setup = sa1100_setup; + master->prepare_transfer_hardware = sa1100_prepare_transfer_hardware; + master->unprepare_transfer_hardware = sa1100_unprepare_transfer_hardware; + master->transfer_one_message = sa1100_transfer_one_message; + + /* 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; + + if (devm_request_mem_region(&pdev->dev, + ssp->memres->start, + resource_size(ssp->memres), + DRV_NAME) == NULL) + goto out; + + ssp->base = devm_ioremap(&pdev->dev, + ssp->memres->start, + resource_size(ssp->memres)); + if (ssp->base == NULL) + goto out; + + /* This also lowers any GPIO signal */ + sa1100_ssp_disable(ssp); + if (!(PPAR & PPAR_SPR) && (Ser4MCCR0 & MCCR0_MCE)) + goto out; + + writel(SSSR_ROR, ssp->base + SSSR); + + ssp->irq = platform_get_irq(pdev, 0); + if (ssp->irq >= 0) { + ret = devm_request_irq(&pdev->dev, + 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, comes from the jornada */ + 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: + spi_master_put(master); + platform_set_drvdata(pdev, NULL); + return ret; +} + +static int sa1100_ssp_remove(struct platform_device *pdev) +{ + struct ssp_state *ssp = platform_get_drvdata(pdev); + + if (ssp) { + sa1100_ssp_disable(ssp); + 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); diff --git a/include/linux/platform_data/sa1100-ssp.h b/include/linux/platform_data/sa1100-ssp.h new file mode 100644 index 0000000..3d3df47 --- /dev/null +++ b/include/linux/platform_data/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; +}; -- 1.7.7.6 ------------------------------------------------------------------------------ Live Security Virtual Conference Exclusive live event will cover all the ways today's security and threat landscape has changed and how IT managers can respond. Discussions will include endpoint security, mobile security and the latest in malware threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/ _______________________________________________ spi-devel-general mailing list spi-devel-general@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/spi-devel-general