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