spi: driver for Cirrus EP93xx SPI controller
v5 cover spi: driver for Cirrus EP93xx SPI controller
Hello,
This series implements SPI master driver for Cirrus Logic EP93xx SPI controllers.
This is fifth iteration of the driver.
Changes to the previous version:
- addressed review comments
- added priming the TX FIFO when transfer is started
- in case of ROR interrupt we clear it
- added documentation in Documentation/spi/ep93xx_spi which provides sample code how the driver can be hooked into platform data
I tested this on TS-7260 board with at25 and mmc_spi drivers. Now that I have Sim.One board, which is EP9307 based, I also tested with that (I hacked the board a bit to get EGPIO9 as a chip select).
Note that patch 2/2 depends on patch that is already in Russell's patch tracking system:
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=5998/1
Thanks,
MW
Mika Westerberg (2):
spi: implemented driver for Cirrus EP93xx SPI controller
ep93xx: SPI driver platform support code
Documentation/spi/ep93xx_spi | 102 +++ arch/arm/mach-ep93xx/clock.c | 13 + arch/arm/mach-ep93xx/core.c | 50 ++ arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1 + arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h | 32 + arch/arm/mach-ep93xx/include/mach/platform.h | 4 + drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/ep93xx_spi.c | 972 +++++++++++++++++++++++ 9 files changed, 1186 insertions(+), 0 deletions(-) create mode 100644 Documentation/spi/ep93xx_spi create mode 100644 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h create mode 100644 drivers/spi/ep93xx_spi.c
PATCH v5 1/2 spi: implemented driver for Cirrus EP93xx SPI controller
This patch adds an SPI master driver for the Cirrus EP93xx SPI controller found in EP93xx chips (EP9301, EP9302, EP9307, EP9312 and EP9315).
--- Documentation/spi/ep93xx_spi | 102 +++ arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h | 32 + drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/ep93xx_spi.c | 972 ++++++++++++++++++++++++ 5 files changed, 1118 insertions(+), 0 deletions(-) create mode 100644 Documentation/spi/ep93xx_spi create mode 100644 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h create mode 100644 drivers/spi/ep93xx_spi.c
Documentation/spi/ep93xx_spi new file +102 -0
@@ -0,0 +1,102 @@ +Cirrus EP93xx SPI controller driver HOWTO +========================================= + +ep93xx_spi driver brings SPI master support for EP93xx SPI controller. Driver +supports EP9301, EP9302, EP9307, EP9312 and EP9315 chips. Chip selects are +implemented with GPIO lines. + +NOTE: If possible, don't use SFRMOUT (SFRM1) signal as a chip select. It will +not work correctly (it cannot be controlled by software). Use GPIO lines +instead. + +Sample configuration +==================== + +Typically driver configuration is done in platform board files (the files under +arch/arm/mach-ep93xx/*.c). In this example we configure MMC over SPI through +this driver on TS-7260 board. You can adapt the code to suit your needs. + +This example uses EGPIO9 as SD/MMC card chip select (this is wired in DIO1 +header on the board). + +You need to select CONFIG_MMC_SPI to use mmc_spi driver. + +arch/arm/mach-ep93xx/ts72xx.c: + +... +#include <linux/gpio.h> +#include <linux/spi/spi.h> + +#include <mach/ep93xx_spi.h> + +/* + * First declare our SD/MMC card as spi_board_info. We only have one device on + * this bus (this is also what mmc_spi expects). + */ +static struct spi_board_info ts72xx_spi_devices[] __initconst = { + { + .modalias = "mmc_spi", + /* + * We use 10 MHz even though the maximum is 7.4 MHz. The driver + * will limit it automatically to max. frequency. + */ + .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, + }, +}; + +/* this is our GPIO line used for chip select */ +#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9 + +static void ts72xx_spi_cs_control(struct spi_device *spi, int value) +{ + gpio_set_value(MMC_CHIP_SELECT_GPIO, value); +} + +static struct ep93xx_spi_info ts72xx_spi_info = { + .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices), + .cs_control = ts72xx_spi_cs_control, +}; + +static void __init ts72xx_init_spi(void) +{ + unsigned gpio = MMC_CHIP_SELECT_GPIO; + int err; + + /* + * Allocate chip select GPIO line here and configure it as output. + */ + err = gpio_request(gpio, "ep93xx-spi"); + if (err) { + pr_err("failed to allocate GPIO%d for SPI device\n", gpio); + return; + } + + err = gpio_direction_output(gpio, 1); + if (err) { + pr_err("failed to configure GPIO%d for SPI device\n", gpio); + gpio_free(gpio); + return; + } + + /* + * Now we can register the device. After this, the driver should be + * ready. + */ + ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices, + ARRAY_SIZE(ts72xx_spi_devices)); +} + +static void __init ts72xx_init_machine(void) +{ + ... + ts72xx_init_spi(); +} + +Thanks to +========= +Martin Guy, H. Hartley Sweeten and others who helped me during development of +the driver. Simplemachines.it donated me a Sim.One board which I used testing +the driver on EP9307.
arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h new file +32 -0
@@ -0,0 +1,32 @@ +#ifndef __ASM_MACH_EP93XX_SPI_H +#define __ASM_MACH_EP93XX_SPI_H + +struct spi_device ; + +/** +* struct ep93xx_spi_info - EP93xx specific SPI descriptor +* @num_chipselect: number of chip selects on this board, must be +* at least one +* @cs_control: chip select control function. Can be %NULL if not needed. +* +* This structure is passed from board support files to EP93xx SPI controller +* driver. It provides callback hook to control chip select lines that are +* allocated in board support files during the board initialization. +*/ +struct ep93xx_spi_info { +int num_chipselect; +/* +* cs_control() - control board chipselect GPIO lines +* @spi: SPI device whose chipselect is controlled +* @value: value to set the chip select line to +* +* This function is used to control board specific chip select lines. +* @value is either %0 or %1. +* +* This function is called from thread context and can sleep if +* necessary. +*/ +void (*cs_control)(struct spi_device *spi,int value); +}; + +#endif /* __ASM_MACH_EP93XX_SPI_H */
drivers/spi/Kconfig +11 -0
@@ -117,6 +117,17 @@ config SPI_DAVINCI help SPI master controller for DaVinci and DA8xx SPI modules. +config SPI_EP93XX + tristate "Cirrus Logic EP93xx SPI controller" + depends on ARCH_EP93XX + help + This enables using the Cirrus EP93xx SPI controller in master + mode. This driver supports EP9301, EP9302, EP9307, EP9312 and EP9315 + chips. + + To compile this driver as a module, choose M here. The module will be + called ep93xx_spi. + config SPI_GPIO tristate "GPIO-based bitbanging SPI Master" depends on GENERIC_GPIO
drivers/spi/Makefile +1 -0
@@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o +obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o obj-$(CONFIG_SPI_GPIO) += spi_gpio.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
drivers/spi/ep93xx_spi.c new file +972 -0
972 lines not on this page · raw message
Signature · 1 line
1.5.6.5
PATCH v5 1/2 spi: implemented driver for Cirrus EP93xx SPI controller
Quoted 22 lines · Mika Westerberg
This patch adds an SPI master driver for the Cirrus EP93xx SPI controller found in EP93xx chips (EP9301, EP9302, EP9307, EP9312 and EP9315).
Signed-off-by Mika Westerberg <[email protected]>--- Documentation/spi/ep93xx_spi | 102 +++ arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h | 32 + drivers/spi/Kconfig | 11 + drivers/spi/Makefile | 1 + drivers/spi/ep93xx_spi.c | 972 ++++++++++++++++++++++++ 5 files changed, 1118 insertions(+), 0 deletions(-) create mode 100644 Documentation/spi/ep93xx_spi create mode 100644 arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h create mode 100644 drivers/spi/ep93xx_spi.cDocumentation/spi/ep93xx_spi new file +0
-0
Documentation/spi/ep93xx_spi line 6
Quoted 6 lines
Documentation/spi/ep93xx_spi new file +6
-0@@ -0,0 +1,102 @@ +Cirrus EP93xx SPI controller driver HOWTO +========================================= + +ep93xx_spi driver brings SPI master support for EP93xx SPI controller. Driver +supports EP9301, EP9302, EP9307, EP9312 and EP9315 chips. Chip selects are +implemented with GPIO lines.
The "Driver supports EP9301..." is really not needed. This driver will work with any of the EP93xx variants.
Quoted 20 lines
Documentation/spi/ep93xx_spi new file +20
-0+ +NOTE: If possible, don't use SFRMOUT (SFRM1) signal as a chip select. It will +not work correctly (it cannot be controlled by software). Use GPIO lines +instead. + +Sample configuration +==================== + +Typically driver configuration is done in platform board files (the files under +arch/arm/mach-ep93xx/*.c). In this example we configure MMC over SPI through +this driver on TS-7260 board. You can adapt the code to suit your needs. + +This example uses EGPIO9 as SD/MMC card chip select (this is wired in DIO1 +header on the board). + +You need to select CONFIG_MMC_SPI to use mmc_spi driver. + +arch/arm/mach-ep93xx/ts72xx.c: + +...
Documentation/spi/ep93xx_spi line 36
Quoted 10 lines
Documentation/spi/ep93xx_spi new file +10
-0+#include <linux/gpio.h> +#include <linux/spi/spi.h> + +#include <mach/ep93xx_spi.h> + +/* + * First declare our SD/MMC card as spi_board_info. We only have one device on + * this bus (this is also what mmc_spi expects). + */ +static struct spi_board_info ts72xx_spi_devices[] __initconst = {
Not sure if it matters but I think this should be tagged '__initdata'. Seems like both tags and no tag are used, not sure which is more correct.
Quoted 56 lines
Documentation/spi/ep93xx_spi new file +56
-0+ { + .modalias = "mmc_spi", + /* + * We use 10 MHz even though the maximum is 7.4 MHz. The driver + * will limit it automatically to max. frequency. + */ + .max_speed_hz = 10 * 1000 * 1000, + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, + }, +}; + +/* this is our GPIO line used for chip select */ +#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9 + +static void ts72xx_spi_cs_control(struct spi_device *spi, int value) +{ + gpio_set_value(MMC_CHIP_SELECT_GPIO, value); +} + +static struct ep93xx_spi_info ts72xx_spi_info = { + .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices), + .cs_control = ts72xx_spi_cs_control, +}; + +static void __init ts72xx_init_spi(void) +{ + unsigned gpio = MMC_CHIP_SELECT_GPIO; + int err; + + /* + * Allocate chip select GPIO line here and configure it as output. + */ + err = gpio_request(gpio, "ep93xx-spi"); + if (err) { + pr_err("failed to allocate GPIO%d for SPI device\n", gpio); + return; + } + + err = gpio_direction_output(gpio, 1); + if (err) { + pr_err("failed to configure GPIO%d for SPI device\n", gpio); + gpio_free(gpio); + return; + } + + /* + * Now we can register the device. After this, the driver should be + * ready. + */ + ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices, + ARRAY_SIZE(ts72xx_spi_devices)); +} + +static void __init ts72xx_init_machine(void)
Documentation/spi/ep93xx_spi line 102
Quoted 10 lines
Documentation/spi/ep93xx_spi new file +10
-0+{ + ... + ts72xx_init_spi(); +} + +Thanks to +========= +Martin Guy, H. Hartley Sweeten and others who helped me during development of +the driver. Simplemachines.it donated me a Sim.One board which I used testing +the driver on EP9307.
Good starting point. In a bit I will post a patch to change the chip select mechanism to allow using non built-in gpios. If it looks good this document will need a bit of editing.
Quoted 43 lines
arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h new file +32
-0@@ -0,0 +1,32 @@ +#ifndef __ASM_MACH_EP93XX_SPI_H +#define __ASM_MACH_EP93XX_SPI_H + +struct spi_device ; + +/** +* struct ep93xx_spi_info - EP93xx specific SPI descriptor +* @num_chipselect: number of chip selects on this board, must be +* at least one +* @cs_control: chip select control function. Can be %NULL if not needed. +* +* This structure is passed from board support files to EP93xx SPI controller +* driver. It provides callback hook to control chip select lines that are +* allocated in board support files during the board initialization. +*/ +struct ep93xx_spi_info { +int num_chipselect; +/* +* cs_control() - control board chipselect GPIO lines +* @spi: SPI device whose chipselect is controlled +* @value: value to set the chip select line to +* +* This function is used to control board specific chip select lines. +* @value is either %0 or %1. +* +* This function is called from thread context and can sleep if +* necessary. +*/ +void (*cs_control)(struct spi_device *spi,int value); +}; + +#endif /* __ASM_MACH_EP93XX_SPI_H */ drivers/spi/Kconfig +0
-0
drivers/spi/Kconfig config SPI_DAVINCI · line 126
Quoted 10 lines
drivers/spi/Kconfig +7
-0@@ -117,6 +117,17 @@ config SPI_DAVINCI help SPI master controller for DaVinci and DA8xx SPI modules. +config SPI_EP93XX + tristate "Cirrus Logic EP93xx SPI controller" + depends on ARCH_EP93XX + help + This enables using the Cirrus EP93xx SPI controller in master + mode. This driver supports EP9301, EP9302, EP9307, EP9312 and EP9315 + chips.
Again, the "This driver supports..." is really not needed.
Quoted 39 lines
drivers/spi/Kconfig +4
-0+ + To compile this driver as a module, choose M here. The module will be + called ep93xx_spi. + config SPI_GPIO tristate "GPIO-based bitbanging SPI Master" depends on GENERIC_GPIOdrivers/spi/Makefile +1
-0@@ -21,6 +21,7 @@ obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o +obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o obj-$(CONFIG_SPI_GPIO) += spi_gpio.o obj-$(CONFIG_SPI_IMX) += spi_imx.o obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.odrivers/spi/ep93xx_spi.c new file +14
-0@@ -0,0 +1,972 @@ +/* +* Driver for Cirrus Logic EP93xx SPI controller. +* +* Copyright (c) 2010 Mika Westerberg +* +* Explicit FIFO handling code was inspired by amba-pl022 driver. +* +* For more information about the SPI controller see documentation on Cirrus +* Logic web site: +* http://www.cirrus.com/en/pubs/manual/EP93xx_Users_Guide_UM1.pdf +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation.
drivers/spi/ep93xx_spi.c line 24
Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10
-0+*/ + +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/bitops.h> +#include <linux/module.h> +#include <linux/moduleparam.h>
I don't think <linux/module.h> and <linux/moduleparam.h> are needed.
Quoted 479 lines
drivers/spi/ep93xx_spi.c new file +479
-0+#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/spi/spi.h> + +#include <mach/ep93xx_spi.h> + +#define SSPCR0 0x0000 +#define SSPCR0_MODE_SHIFT 6 +#define SSPCR0_SCR_SHIFT 8 + +#define SSPCR1 0x0004 +#define SSPCR1_RIE BIT(0) +#define SSPCR1_TIE BIT(1) +#define SSPCR1_RORIE BIT(2) +#define SSPCR1_LBM BIT(3) +#define SSPCR1_SSE BIT(4) +#define SSPCR1_MS BIT(5) +#define SSPCR1_SOD BIT(6) + +#define SSPDR 0x0008 + +#define SSPSR 0x000c +#define SSPSR_TFE BIT(0) +#define SSPSR_TNF BIT(1) +#define SSPSR_RNE BIT(2) +#define SSPSR_RFF BIT(3) +#define SSPSR_BSY BIT(4) +#define SSPCPSR 0x0010 + +#define SSPIIR 0x0014 +#define SSPIIR_RIS BIT(0) +#define SSPIIR_TIS BIT(1) +#define SSPIIR_RORIS BIT(2) +#define SSPICR SSPIIR + +/* timeout in milliseconds */ +#define SPI_TIMEOUT 5 +/* maximum depth of RX/TX FIFO */ +#define SPI_FIFO_SIZE 8 + +/** +* struct ep93xx_spi - EP93xx SPI controller structure +* @lock: spinlock that protects concurrent accesses to fields @running, +* @current_msg and @msg_queue +* @pdev: pointer to platform device +* @clk: clock for the controller +* @regs_base: pointer to ioremap()'d registers +* @irq: IRQ number used by the driver +* @min_rate: minimum clock rate (in Hz) supported by the controller +* @max_rate: maximum clock rate (in Hz) supported by the controller +* @running: is the queue running +* @wq: workqueue used by the driver +* @msg_work: work that is queued for the driver +* @wait: wait here until given transfer is completed +* @msg_queue: queue for the messages +* @current_msg: message that is currently processed (or %NULL if none) +* @tx: current byte in transfer to transmit +* @rx: current byte in transfer to receive +* @fifo_level: how full is FIFO (%0..%SPI_FIFO_SIZE - %1). Receiving one +* frame decreases this level and sending one frame increases it. +* @cs_control: chip select control function +* +* This structure holds EP93xx SPI controller specific information. When +* @running is %true, driver accepts transfer requests from protocol drivers. +* @current_msg is used to hold pointer to the message that is currently +* processed. If @current_msg is %NULL, it means that no processing is going +* on. +* +* Most of the fields are only written once and they can be accessed without +* taking the @lock. Fields that are accessed concurrently are: @current_msg, +* @running, and @msg_queue. +*/ +struct ep93xx_spi { +spinlock_t lock; +const struct platform_device *pdev; +struct clk *clk; +void __iomem *regs_base; +int irq; +unsigned long min_rate; +unsigned long max_rate; +bool running; +struct workqueue_struct *wq; +struct work_struct msg_work; +struct completion wait; +struct list_head msg_queue; +struct spi_message *current_msg; +size_t tx; +size_t rx; +size_t fifo_level; +void (*cs_control)(struct spi_device *,int ); +}; + +/** +* struct ep93xx_spi_chip - SPI device hardware settings +* @spi: back pointer to the SPI device +* @rate: max rate in hz this chip supports +* @div_cpsr: cpsr (pre-scaler) divider +* @div_scr: scr divider +* @dss: bits per word (4 - 16 bits) +* +* This structure is used to store hardware register specific settings for each +* SPI device. Settings are written to hardware by function +* ep93xx_spi_chip_setup(). +*/ +struct ep93xx_spi_chip { +const struct spi_device *spi; +unsigned long rate; +u8 div_cpsr; +u8 div_scr; +u8 dss; +}; + +/* converts bits per word to CR0.DSS value */ +#define bits_per_word_to_dss (bpw) ((bpw) - 1) + +static inline void +ep93xx_spi_write_u8 (const struct ep93xx_spi *espi,u16 reg,u8 value) +{ +__raw_writeb (value, espi->regs_base + reg); +} + +static inline u8 +ep93xx_spi_read_u8 (const struct ep93xx_spi *spi,u16 reg) +{ +return __raw_readb (spi->regs_base + reg); +} + +static inline void +ep93xx_spi_write_u16 (const struct ep93xx_spi *espi,u16 reg,u16 value) +{ +__raw_writew (value, espi->regs_base + reg); +} + +static inline u16 +ep93xx_spi_read_u16 (const struct ep93xx_spi *spi,u16 reg) +{ +return __raw_readw (spi->regs_base + reg); +} + +/** +* ep93xx_spi_enable() - enables the SPI controller and clock +* @espi: ep93xx SPI controller struct +* +* This function enables the SPI controller and its clock. Returns %0 in case +* of success and negative error in case if failure. +*/ +static int ep93xx_spi_enable (const struct ep93xx_spi *espi) +{ +u8 regval; +int err; + + err =clk_enable (espi->clk); +if (err) +return err; + + regval =ep93xx_spi_read_u8 (espi,SSPCR1 ); + regval |=SSPCR1_SSE ; +ep93xx_spi_write_u8 (espi,SSPCR1 , regval); + +return 0 ; +} + +/** +* ep93xx_spi_disable() - disables the SPI controller and clock +* @espi: ep93xx SPI controller struct +* +* Function disables SPI controller and its clock. +*/ +static void ep93xx_spi_disable (const struct ep93xx_spi *espi) +{ +u8 regval; + + regval =ep93xx_spi_read_u8 (espi,SSPCR1 ); + regval &= ~SSPCR1_SSE ; +ep93xx_spi_write_u8 (espi,SSPCR1 , regval); + +clk_disable (espi->clk); +} + +/** +* ep93xx_spi_enable_interrupts() - enables all SPI interrupts +* @espi: ep93xx SPI controller struct +* +* Enables all SPI interrupts: receive overrun (ROR), transmit, and receive. +*/ +static inline void +ep93xx_spi_enable_interrupts (const struct ep93xx_spi *espi) +{ +u8 regval; + + regval =ep93xx_spi_read_u8 (espi,SSPCR1 ); + regval |= (SSPCR1_RORIE |SSPCR1_TIE |SSPCR1_RIE ); +ep93xx_spi_write_u8 (espi,SSPCR1 , regval); +} + +/** +* ep93xx_spi_disable_interrupts() - disables all SPI interrupts +* @espi: ep93xx SPI controller struct +* +* Disables all SPI interrupts. +*/ +static inline void +ep93xx_spi_disable_interrupts (const struct ep93xx_spi *espi) +{ +u8 regval; + + regval =ep93xx_spi_read_u8 (espi,SSPCR1 ); + regval &= ~(SSPCR1_RORIE |SSPCR1_TIE |SSPCR1_RIE ); +ep93xx_spi_write_u8 (espi,SSPCR1 , regval); +} + +/** +* ep93xx_spi_calc_divisors() - calculates SPI clock divisors +* @espi: ep93xx SPI controller struct +* @chip: divisors are calculated for this chip +* @rate: desired SPI output clock rate +* +* Function calculates cpsr (clock pre-scaler) and scr divisors based on +* given @rate and places them to @chip->div_cpsr and @chip->div_scr. If, +* for some reason, divisors cannot be calculated nothing is stored and +* %-EINVAL is returned. +*/ +static int ep93xx_spi_calc_divisors (const struct ep93xx_spi *espi, +struct ep93xx_spi_chip *chip, +unsigned long rate) +{ +unsigned long spi_clk_rate =clk_get_rate (espi->clk); +int cpsr, scr; + +/* +* Make sure that max value is between values supported by the +* controller. Note that minimum value is already checked in +* ep93xx_spi_transfer(). +*/ + rate =clamp (rate, espi->min_rate, espi->max_rate); + +/* +* Calculate divisors so that we can get speed according the +* following formula: +* rate = spi_clock_rate / (cpsr * (1 + scr)) +* +* cpsr must be even number and starts from 2, scr can be any number +* between 0 and 255. +*/ +for (cpsr =2 ; cpsr <=254 ; cpsr +=2 ) { +for (scr =0 ; scr <=255 ; scr++) { +if ((spi_clk_rate / (cpsr * (scr +1 ))) <= rate) { + chip->div_scr = (u8 )scr; + chip->div_cpsr = (u8 )cpsr; +return 0 ; + } + } + } + +return -EINVAL ; +} + +/** +* ep93xx_spi_cs_control() - controls chipselect for given device +* @espi: ep93xx SPI controller struct +* @spi: SPI device to select/deselect +* @control: select (%true) / deselect (%false) +* +* Function controls chipselect line for given SPI device. +* +* Note that this function is called from a thread context and can sleep. +*/ +static inline void ep93xx_spi_cs_control (const struct ep93xx_spi *espi, +struct spi_device *spi, +bool control) +{ +int value = (spi->mode &SPI_CS_HIGH ) ? control : !control; + +if (espi->cs_control) + espi->cs_control (spi, value); +} + +/** +* ep93xx_spi_setup() - setup an SPI device +* @spi: SPI device to setup +* +* This function sets up SPI device mode, speed etc. Can be called multiple +* times for a single device. Returns %0 in case of success, negative error in +* case of failure. When this function returns success, the device is +* deselected. +*/ +static int ep93xx_spi_setup (struct spi_device *spi) +{ +struct ep93xx_spi *espi =spi_master_get_devdata (spi->master); +struct ep93xx_spi_chip *chip; + +if (spi->bits_per_word <4 || spi->bits_per_word >16 ) { +dev_err (&espi->pdev->dev,"invalid bits per word %d\n" , + spi->bits_per_word); +return -EINVAL ; + } + + chip =spi_get_ctldata (spi); +if (!chip) { + chip =kzalloc (sizeof (*chip),GFP_KERNEL ); +if (!chip) +return -ENOMEM ; + +spi_set_ctldata (spi, chip); + chip->spi = spi; + } + +if (spi->max_speed_hz != chip->rate) { +int err; + + err =ep93xx_spi_calc_divisors (espi, chip, spi->max_speed_hz); +if (err !=0 ) { +spi_set_ctldata (spi,NULL ); +kfree (chip); +return err; + } + chip->rate = spi->max_speed_hz; + } + + chip->dss =bits_per_word_to_dss (spi->bits_per_word); + +ep93xx_spi_cs_control (espi, spi, false); +return 0 ; +} + +/** +* ep93xx_spi_transfer() - queue message to be transferred +* @spi: target SPI device +* @msg: message to be transferred +* +* This function is called by SPI device drivers when they are going to transfer +* a new message. It simply puts the message in the queue and schedules +* workqueue to perform the actual transfer later on. +* +* Returns %0 on success and negative error in case of failure. +*/ +static int ep93xx_spi_transfer (struct spi_device *spi,struct spi_message *msg) +{ +struct ep93xx_spi *espi =spi_master_get_devdata (spi->master); +struct spi_transfer *t; +unsigned long flags; + +if (!msg || !msg->complete) +return -EINVAL ; + +/* first validate each transfer */ +list_for_each_entry (t, &msg->transfers, transfer_list) { +if (t->bits_per_word) { +if (t->bits_per_word <4 || t->bits_per_word >16 ) +return -EINVAL ; + } +if (t->speed_hz && t->speed_hz < espi->min_rate) +return -EINVAL ; + } + +/* +* Now that we own the message, let's initialize it so that it is +* suitable for us. We use @msg->status to signal whether there was +* error in transfer and @msg->state is used to hold pointer to the +* current transfer (or %NULL if no active current transfer). +*/ + msg->state =NULL ; + msg->status =0 ; + msg->actual_length =0 ; + +spin_lock_irqsave (&espi->lock, flags); +if (!espi->running) { +spin_unlock_irqrestore (&espi->lock, flags); +return -ESHUTDOWN ; + } +list_add_tail (&msg->queue, &espi->msg_queue); +queue_work (espi->wq, &espi->msg_work); +spin_unlock_irqrestore (&espi->lock, flags); + +return 0 ; +} + +/** +* ep93xx_spi_cleanup() - cleans up master controller specific state +* @spi: SPI device to cleanup +* +* This function releases master controller specific state for given @spi +* device. +*/ +static void ep93xx_spi_cleanup (struct spi_device *spi) +{ +struct ep93xx_spi_chip *chip; + + chip =spi_get_ctldata (spi); +if (chip) { +spi_set_ctldata (spi,NULL ); +kfree (chip); + } +} + +/** +* ep93xx_spi_chip_setup() - configures hardware according to given @chip +* @espi: ep93xx SPI controller struct +* @chip: chip specific settings +* +* This function sets up the actual hardware registers with settings given in +* @chip. Note that no validation is done so make sure that callers validate +* settings before calling this. +*/ +static void ep93xx_spi_chip_setup (const struct ep93xx_spi *espi, +const struct ep93xx_spi_chip *chip) +{ +u16 cr0; + + cr0 = chip->div_scr <<SSPCR0_SCR_SHIFT ; + cr0 |= (chip->spi->mode & (SPI_CPHA |SPI_CPOL )) <<SSPCR0_MODE_SHIFT ; + cr0 |= chip->dss; + +dev_dbg (&espi->pdev->dev,"setup: mode %d, cpsr %d, scr %d, dss %d\n" , + chip->spi->mode, chip->div_cpsr, chip->div_scr, chip->dss); +dev_dbg (&espi->pdev->dev,"setup: cr0 %#x" , cr0); + +ep93xx_spi_write_u8 (espi,SSPCPSR , chip->div_cpsr); +ep93xx_spi_write_u16 (espi,SSPCR0 , cr0); +} + +/** +* bits_per_word() - returns bits per word for current message +*/ +static inline int bits_per_word (const struct ep93xx_spi *espi) +{ +struct spi_message *msg = espi->current_msg; +struct spi_transfer *t = msg->state; + +return t->bits_per_word ? t->bits_per_word : msg->spi->bits_per_word; +} + +static void ep93xx_do_write (struct ep93xx_spi *espi,struct spi_transfer *t) +{ +if (bits_per_word (espi) >8 ) { +u16 tx_val =0 ; + +if (t->tx_buf) + tx_val = ((u16 *)t->tx_buf)[espi->tx]; +ep93xx_spi_write_u16 (espi,SSPDR , tx_val); + espi->tx +=sizeof (tx_val); + }else { +u8 tx_val =0 ; + +if (t->tx_buf) + tx_val = ((u8 *)t->tx_buf)[espi->tx]; +ep93xx_spi_write_u8 (espi,SSPDR , tx_val); + espi->tx +=sizeof (tx_val); + } +} + +static void ep93xx_do_read (struct ep93xx_spi *espi,struct spi_transfer *t) +{ +if (bits_per_word (espi) >8 ) { +u16 rx_val; + + rx_val =ep93xx_spi_read_u16 (espi,SSPDR ); +if (t->rx_buf) + ((u16 *)t->rx_buf)[espi->rx] = rx_val; + espi->rx +=sizeof (rx_val); + }else { +u8 rx_val; + + rx_val =ep93xx_spi_read_u8 (espi,SSPDR ); +if (t->rx_buf) + ((u8 *)t->rx_buf)[espi->rx] = rx_val; + espi->rx +=sizeof (rx_val); + } +} + +/** +* ep93xx_spi_read_write() - perform next RX/TX transfer +* @espi: ep93xx SPI controller struct +* +* This function transfers next bytes (or half-words) to/from RX/TX FIFOs. If +* called several times, the whole transfer will be completed. Returns %0 when +* current transfer was not yet completed otherwise length of the transfer
drivers/spi/ep93xx_spi.c line 513
Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10
-0+* (>%0). When this function is finished, RX FIFO should be empty and TX FIFO +* should be full. +*/ +static int ep93xx_spi_read_write (struct ep93xx_spi *espi) +{ +struct spi_message *msg = espi->current_msg; +struct spi_transfer *t = msg->state; + +/* read as long as RX FIFO has frames in it */ +while ((ep93xx_spi_read_u8 (espi,SSPSR ) &SSPSR_RNE )) {
It might be overkill but you might want to add:
&& espi->rx < t->len
to the end of this while statement. It 'should' never happen, but if you do get extra data it will cause problems...
Quoted 7 lines
drivers/spi/ep93xx_spi.c new file +7
-0+ep93xx_do_read (espi, t); + espi->fifo_level--; + } + +/* write as long as TX FIFO has room */ +while (espi->fifo_level <SPI_FIFO_SIZE && espi->tx < t->len) { +ep93xx_do_write (espi, t);
drivers/spi/ep93xx_spi.c line 530
Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10
-0+ espi->fifo_level++; + } + +if (espi->rx == t->len) { + msg->actual_length += t->len; +return t->len; + } + +return 0 ; +}
Since the caller of this function doesn't use the 't->len' value, change this function to return 0 on success and -EINPROGRESS when the transfer is still happening.
Quoted 47 lines
drivers/spi/ep93xx_spi.c new file +47
-0+ +/** +* ep93xx_spi_process_transfer() - processes one SPI transfer +* @espi: ep93xx SPI controller struct +* @msg: current message +* @t: transfer to process +* +* This function processes one SPI transfer given in @t. Function waits until +* transfer is complete (may sleep) and updates @msg->status based on whether +* transfer was succesfully processed or not. +*/ +static void ep93xx_spi_process_transfer (struct ep93xx_spi *espi, +struct spi_message *msg, +struct spi_transfer *t) +{ +struct ep93xx_spi_chip *chip =spi_get_ctldata (msg->spi); + + msg->state = t; + +/* +* Handle any transfer specific settings if needed. We use +* temporary chip settings here and restore original later when +* the transfer is finished. +*/ +if (t->speed_hz || t->bits_per_word) { +struct ep93xx_spi_chip tmp_chip = *chip; + +if (t->speed_hz) { +int err; + + err =ep93xx_spi_calc_divisors (espi, &tmp_chip, + t->speed_hz); +if (err) { +dev_err (&espi->pdev->dev, +"failed to adjust speed\n" ); + msg->status = err; +return ; + } + } + +if (t->bits_per_word) + tmp_chip.dss =bits_per_word_to_dss (t->bits_per_word); + +/* +* Set up temporary new hw settings for this transfer. +*/ +ep93xx_spi_chip_setup (espi, &tmp_chip);
drivers/spi/ep93xx_spi.c line 587
Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10
-0+ } + + espi->rx =0 ; + espi->tx =0 ; + +/* +* Now everything is set up for the current transfer. We prime the TX +* FIFO, enable interrupts, and wait for the transfer to complete. +*/ +ep93xx_spi_read_write (espi);
Again, probably overkill, you should check the return value to make sure the transfer is still in progress before enabling interrupts and waiting.
Quoted 284 lines
drivers/spi/ep93xx_spi.c new file +284
-0+ep93xx_spi_enable_interrupts (espi); +wait_for_completion (&espi->wait); + +/* +* In case of error during transmit, we bail out from processing +* the message. +*/ +if (msg->status) +return ; + +/* +* After this transfer is finished, perform any possible +* post-transfer actions requested by the protocol driver. +*/ +if (t->delay_usecs) { +set_current_state (TASK_UNINTERRUPTIBLE ); +schedule_timeout (usecs_to_jiffies (t->delay_usecs)); + } +if (t->cs_change) { +if (!list_is_last (&t->transfer_list, &msg->transfers)) { +/* +* In case protocol driver is asking us to drop the +* chipselect briefly, we let the scheduler to handle +* any "delay" here. +*/ +ep93xx_spi_cs_control (espi, msg->spi, false); +cond_resched (); +ep93xx_spi_cs_control (espi, msg->spi, true); + } + } + +if (t->speed_hz || t->bits_per_word) +ep93xx_spi_chip_setup (espi, chip); +} + +/* +* ep93xx_spi_process_message() - process one SPI message +* @espi: ep93xx SPI controller struct +* @msg: message to process +* +* This function processes a single SPI message. We go through all transfers in +* the message and pass them to ep93xx_spi_process_transfer(). Chipselect is +* asserted during the whole message (unless per transfer cs_change is set). +* +* @msg->status contains %0 in case of success or negative error code in case of +* failure. +*/ +static void ep93xx_spi_process_message (struct ep93xx_spi *espi, +struct spi_message *msg) +{ +unsigned long timeout; +struct spi_transfer *t; +int err; + +/* +* Enable the SPI controller and its clock. +*/ + err =ep93xx_spi_enable (espi); +if (err) { +dev_err (&espi->pdev->dev,"failed to enable SPI controller\n" ); + msg->status = err; +return ; + } + +/* +* Just to be sure: flush any data from RX FIFO. +*/ + timeout = jiffies +msecs_to_jiffies (SPI_TIMEOUT ); +while (ep93xx_spi_read_u16 (espi,SSPSR ) &SSPSR_RNE ) { +if (time_after (jiffies, timeout)) { +dev_warn (&espi->pdev->dev, +"timeout while flushing RX FIFO\n" ); + msg->status = -ETIMEDOUT ; +return ; + } +ep93xx_spi_read_u16 (espi,SSPDR ); + } + +/* +* We explicitly handle FIFO level. This way we don't have to check TX +* FIFO status using %SSPSR_TNF bit which may cause RX FIFO overruns. +*/ + espi->fifo_level =0 ; + +/* +* Update SPI controller registers according to spi device and assert +* the chipselect. +*/ +ep93xx_spi_chip_setup (espi,spi_get_ctldata (msg->spi)); +ep93xx_spi_cs_control (espi, msg->spi, true); + +list_for_each_entry (t, &msg->transfers, transfer_list) { +ep93xx_spi_process_transfer (espi, msg, t); +if (msg->status) +break ; + } + +/* +* Now the whole message is transferred (or failed for some reason). We +* deselect the device and disable the SPI controller. +*/ +ep93xx_spi_cs_control (espi, msg->spi, false); +ep93xx_spi_disable (espi); +} + +#define work_to_espi (work) (container_of((work), struct ep93xx_spi, msg_work)) + +/** +* ep93xx_spi_work() - EP93xx SPI workqueue worker function +* @work: work struct +* +* Workqueue worker function. This function is called when there are new +* SPI messages to be processed. Message is taken out from the queue and then +* passed to ep93xx_spi_process_message(). +* +* After message is transferred, protocol driver is notified by calling +* @msg->complete(). In case of error, @msg->status is set to negative error +* number, otherwise it contains zero (and @msg->actual_length is updated). +*/ +static void ep93xx_spi_work (struct work_struct *work) +{ +struct ep93xx_spi *espi =work_to_espi (work); +struct spi_message *msg; + +spin_lock_irq (&espi->lock); +if (!espi->running || espi->current_msg || +list_empty (&espi->msg_queue)) { +spin_unlock_irq (&espi->lock); +return ; + } + msg = list_first_entry(&espi->msg_queue, struct spi_message, queue); +list_del_init (&msg->queue); + espi->current_msg = msg; +spin_unlock_irq (&espi->lock); + +ep93xx_spi_process_message (espi, msg); + +/* +* Update the current message and re-schedule ourselves if there are +* more messages in the queue. +*/ +spin_lock_irq (&espi->lock); + espi->current_msg =NULL ; +if (espi->running && !list_empty (&espi->msg_queue)) +queue_work (espi->wq, &espi->msg_work); +spin_unlock_irq (&espi->lock); + +/* notify the protocol driver that we are done with this message */ + msg->complete (msg->context); +} + +/** +* ep93xx_spi_interrupt() - SPI interrupt handler +* @irq: IRQ number (not used) +* @dev_id: pointer to EP93xx controller struct +* +* This function handles TX/RX/ROR interrupts that come from the SPI controller. +* Returns %IRQ_HANDLED when interrupt was handled and %IRQ_NONE in case the +* @irq was not handled. +*/ +static irqreturn_t ep93xx_spi_interrupt (int irq,void *dev_id) +{ +struct ep93xx_spi *espi = dev_id; +u8 irq_status =ep93xx_spi_read_u8 (espi,SSPIIR ); + +/* +* If we got ROR (receive overrun) interrupt we know that something is +* wrong. Just abort the message. +*/ +if (unlikely (irq_status &SSPIIR_RORIS )) { +/* clear the overrun interrupt */ +ep93xx_spi_write_u8 (espi,SSPICR ,0 ); +dev_warn (&espi->pdev->dev, +"receive overrun, aborting the message\n" ); + espi->current_msg->status = -EIO ; + }else { +/* +* Interrupt is either RX (RIS) or TX (TIS). For both cases we +* simply execute next data transfer. +*/ +if (!ep93xx_spi_read_write (espi)) { +/* +* In normal case, there still is some processing left +* for current transfer. Let's wait for the next +* interrupt then. +*/ +return IRQ_HANDLED ; + } + } + +/* +* Current transfer is finished, either with error or with success. In +* any case we disable interrupts and notify the worker to handle +* any post-processing of the message. +*/ +ep93xx_spi_disable_interrupts (espi); +complete (&espi->wait); +return IRQ_HANDLED ; +} + +static int __init ep93xx_spi_probe (struct platform_device *pdev) +{ +struct spi_master *master; +struct ep93xx_spi_info *info; +struct ep93xx_spi *espi; +struct resource *res; +int error; + + info = pdev->dev.platform_data; + + master =spi_alloc_master (&pdev->dev,sizeof (*espi)); +if (!master) { +dev_err (&pdev->dev,"failed to allocate spi master\n" ); +return -ENOMEM ; + } + + master->setup = ep93xx_spi_setup; + master->transfer = ep93xx_spi_transfer; + master->cleanup = ep93xx_spi_cleanup; + master->bus_num = pdev->id; + master->num_chipselect = info->num_chipselect; + master->mode_bits =SPI_CPOL |SPI_CPHA |SPI_CS_HIGH ; + +platform_set_drvdata (pdev, master); + + espi =spi_master_get_devdata (master); + espi->cs_control = info->cs_control; + + espi->clk =clk_get (&pdev->dev,NULL ); +if (IS_ERR (espi->clk)) { +dev_err (&pdev->dev,"unable to get spi clock\n" ); + error =PTR_ERR (espi->clk); + goto fail_release_master; + } + +spin_lock_init (&espi->lock); +init_completion (&espi->wait); + +/* +* Calculate maximum and minimum supported clock rates +* for the controller. +*/ + espi->max_rate =clk_get_rate (espi->clk) /2 ; + espi->min_rate =clk_get_rate (espi->clk) / (254 *256 ); + espi->pdev = pdev; + + espi->irq =platform_get_irq (pdev,0 ); +if (espi->irq <0 ) { + error = -EBUSY ; +dev_err (&pdev->dev,"failed to get irq resources\n" ); + goto fail_put_clock; + } + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!res) { +dev_err (&pdev->dev,"unable to get iomem resource\n" ); + error = -ENODEV ; + goto fail_put_clock; + } + + res =request_mem_region (res->start,resource_size (res), pdev->name); +if (!res) { +dev_err (&pdev->dev,"unable to request iomem resources\n" ); + error = -EBUSY ; + goto fail_put_clock; + } + + espi->regs_base =ioremap (res->start,resource_size (res)); +if (!espi->regs_base) { +dev_err (&pdev->dev,"failed to map resources\n" ); + error = -ENODEV ; + goto fail_free_mem; + } + + error =request_irq (espi->irq, ep93xx_spi_interrupt,0 , +"ep93xx-spi" , espi); +if (error) { +dev_err (&pdev->dev,"failed to request irq\n" ); + goto fail_unmap_regs; + } + + espi->wq =create_singlethread_workqueue ("ep93xx_spid" ); +if (!espi->wq) { +dev_err (&pdev->dev,"unable to create workqueue\n" );
drivers/spi/ep93xx_spi.c line 881
Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10
-0+ goto fail_free_irq; + } +INIT_WORK (&espi->msg_work, ep93xx_spi_work); +INIT_LIST_HEAD (&espi->msg_queue); + espi->running = true; + +/* make sure that the hardware is disabled */ +ep93xx_spi_write_u8 (espi,SSPCR1 ,0 ); + + error =spi_register_master (master);
Nitpick, you have a stray TAB after the = instead of a space.
Quoted 81 lines
drivers/spi/ep93xx_spi.c new file +81
-0+if (error) { +dev_err (&pdev->dev,"failed to register SPI master\n" ); + goto fail_free_queue; + } + +dev_info (&pdev->dev,"EP93xx SPI Controller at 0x%08lx irq %d\n" , + (unsigned long )res->start, espi->irq); + +return 0 ; + +fail_free_queue: +destroy_workqueue (espi->wq); +fail_free_irq: +free_irq (espi->irq, espi); +fail_unmap_regs: +iounmap (espi->regs_base); +fail_free_mem: +release_mem_region (res->start,resource_size (res)); +fail_put_clock: +clk_put (espi->clk); +fail_release_master: +spi_master_put (master); +platform_set_drvdata (pdev,NULL ); + +return error; +} + +static int __exit ep93xx_spi_remove(struct platform_device *pdev) +{ +struct spi_master *master =platform_get_drvdata (pdev); +struct ep93xx_spi *espi =spi_master_get_devdata (master); +struct resource *res; + +spin_lock_irq (&espi->lock); + espi->running = false; +spin_unlock_irq (&espi->lock); + +destroy_workqueue (espi->wq); + +/* +* Complete remaining messages with %-ESHUTDOWN status. +*/ +spin_lock_irq (&espi->lock); +while (!list_empty (&espi->msg_queue)) { +struct spi_message *msg; + + msg =list_first_entry (&espi->msg_queue, + struct spi_message, queue); +list_del_init (&msg->queue); + msg->status = -ESHUTDOWN ; +spin_unlock_irq (&espi->lock); + msg->complete (msg->context); +spin_lock_irq (&espi->lock); + } +spin_unlock_irq (&espi->lock); + +free_irq (espi->irq, espi); +iounmap (espi->regs_base); + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +release_mem_region (res->start,resource_size (res)); +clk_put (espi->clk); +platform_set_drvdata (pdev,NULL ); + +spi_unregister_master (master); +return 0 ; +} + +static struct platform_driver ep93xx_spi_driver = { + .driver = { + .name ="ep93xx-spi" , + .owner =THIS_MODULE , + }, + .remove =__exit_p (ep93xx_spi_remove), +}; + +static int __init ep93xx_spi_init (void ) +{ +return platform_driver_probe (&ep93xx_spi_driver, ep93xx_spi_probe); +} +module_init (ep93xx_spi_init); +
drivers/spi/ep93xx_spi.c line 972
Quoted 10 lines
drivers/spi/ep93xx_spi.c new file +10
-0+static void __exit ep93xx_spi_exit(void) +{ +platform_driver_unregister (&ep93xx_spi_driver); +} +module_exit (ep93xx_spi_exit); + +MODULE_DESCRIPTION ("EP93xx SPI Controller driver" ); +MODULE_AUTHOR ("Mika Westerberg <[email protected]>" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS ("platform:ep93xx-spi" );
Regards,
Hartley
PATCH v5 2/2 ep93xx: SPI driver platform support code
In reply to Mika WesterbergThis patch adds platform side support code for the EP93xx SPI driver. This includes clock, resources and muxing. There is a new function: ep93xx_register_spi() which can be used by board support code to register new SPI devices for the board.
Patch depends on following ARM patch:
5998/1 ep93xx: added chip revision reading function
--- arch/arm/mach-ep93xx/clock.c | 13 ++++++ arch/arm/mach-ep93xx/core.c | 50 +++++++++++++++++++++++ arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1 + arch/arm/mach-ep93xx/include/mach/platform.h | 4 ++ 4 files changed, 68 insertions(+), 0 deletions(-)
arch/arm/mach-ep93xx/clock.c +13 -0
@@ -96,6 +96,10 @@ static struct clk clk_keypad = { .enable_mask = EP93XX_SYSCON_KEYTCHCLKDIV_KEN, .set_rate = set_keytchclk_rate, }; +static struct clk clk_spi = { + .parent = &clk_xtali, + .rate =EP93XX_EXT_CLK_RATE , +};static struct clk clk_pwm = { .parent = &clk_xtali, .rate =EP93XX_EXT_CLK_RATE ,
@@ -186,6 +190,7 @@ static struct clk_lookup clocks[] = {INIT_CK ("ep93xx-ohci" ,NULL , &clk_usb_host),INIT_CK ("ep93xx-keypad" ,NULL , &clk_keypad),INIT_CK ("ep93xx-fb" ,NULL , &clk_video), +INIT_CK ("ep93xx-spi.0" ,NULL , &clk_spi),INIT_CK (NULL ,"pwm_clk" , &clk_pwm),INIT_CK (NULL ,"m2p0" , &clk_m2p0), INIT_CK(NULL, "m2p1", &clk_m2p1),
@@ -473,6 +478,14 @@ static int __init ep93xx_clock_init(void)/* Initialize the pll2 derived clocks */ clk_usb_host.rate = clk_pll2.rate / (((value >>28 ) &0xf ) +1 ); +/* +* EP93xx SSP clock rate was doubled in version E2. For more information +* see: +* http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf +*/ +if (ep93xx_chip_revision () <EP93XX_CHIP_REV_E2 ) + clk_spi.rate /=2 ; +pr_info ("PLL1 running at %ld MHz, PLL2@%ld MHz\n" , clk_pll1.rate /1000000 , clk_pll2.rate /1000000 ); pr_info("FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n",
arch/arm/mach-ep93xx/core.c +50 -0
68 lines not on this page · raw message
arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +1 -0
@@ -106,6 +106,7 @@#define EP93XX_AAC_BASE EP93XX_APB_IOMEM(0x00080000) +#define EP93XX_SPI_PHYS_BASE EP93XX_APB_PHYS(0x000a0000)#define EP93XX_SPI_BASE EP93XX_APB_IOMEM(0x000a0000)#define EP93XX_IRDA_BASE EP93XX_APB_IOMEM(0x000b0000)
arch/arm/mach-ep93xx/include/mach/platform.h +4 -0
@@ -4,11 +4,13 @@#ifndef __ASSEMBLY__ +struct spi_board_info ;struct i2c_gpio_platform_data ;struct i2c_board_info ;struct platform_device ;struct ep93xxfb_mach_info ;struct ep93xx_keypad_platform_data ; +struct ep93xx_spi_info ;struct ep93xx_eth_data {
@@ -34,6 +36,8 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits) }void ep93xx_register_eth (struct ep93xx_eth_data *data,int copy_addr); +void ep93xx_register_spi (struct ep93xx_spi_info *info, +struct spi_board_info *devices,int num);void ep93xx_register_i2c (struct i2c_gpio_platform_data *data,struct i2c_board_info *devices,int num);void ep93xx_register_fb (struct ep93xxfb_mach_info *data);
Signature · 1 line
1.5.6.5
PATCH v5 2/2 ep93xx: SPI driver platform support code
Quoted 44 lines · Mika Westerberg
This patch adds platform side support code for the EP93xx SPI driver. This includes clock, resources and muxing. There is a new function:
ep93xx_register_spi()which can be used by board support code to register new SPI devices for the board.Patch depends on following ARM patch:
5998/1 ep93xx: added chip revision reading functionSigned-off-by Mika Westerberg <[email protected]>--- arch/arm/mach-ep93xx/clock.c | 13 ++++++ arch/arm/mach-ep93xx/core.c | 50 +++++++++++++++++++++++ arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1 + arch/arm/mach-ep93xx/include/mach/platform.h | 4 ++ 4 files changed, 68 insertions(+), 0 deletions(-)arch/arm/mach-ep93xx/clock.c +6
-0@@ -96,6 +96,10 @@ static struct clk clk_keypad = { .enable_mask = EP93XX_SYSCON_KEYTCHCLKDIV_KEN, .set_rate = set_keytchclk_rate, }; +static struct clk clk_spi = { + .parent = &clk_xtali, + .rate =EP93XX_EXT_CLK_RATE , +};static struct clk clk_pwm = { .parent = &clk_xtali, .rate =EP93XX_EXT_CLK_RATE ,@@ -186,6 +190,7 @@ static struct clk_lookup clocks[] = {INIT_CK ("ep93xx-ohci" ,NULL , &clk_usb_host),INIT_CK ("ep93xx-keypad" ,NULL , &clk_keypad),INIT_CK ("ep93xx-fb" ,NULL , &clk_video), +INIT_CK ("ep93xx-spi.0" ,NULL , &clk_spi),INIT_CK (NULL ,"pwm_clk" , &clk_pwm),INIT_CK (NULL ,"m2p0" , &clk_m2p0), INIT_CK(NULL, "m2p1", &clk_m2p1),@@ -473,6 +478,14 @@ static int __init ep93xx_clock_init(void)/* Initialize the pll2 derived clocks */ clk_usb_host.rate = clk_pll2.rate / (((value >>28 ) &0xf ) +1 ); +/*
arch/arm/mach-ep93xx/clock.c static int __init ep93xx_clock_init(void) · line 491
Quoted 10 lines
arch/arm/mach-ep93xx/clock.c +7
-0+* EP93xx SSP clock rate was doubled in version E2. For more information +* see: +* http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf +*/ +if (ep93xx_chip_revision () <EP93XX_CHIP_REV_E2 ) + clk_spi.rate /=2 ; +pr_info ("PLL1 running at %ld MHz, PLL2 at %ld MHz\n" , clk_pll1.rate /1000000 , clk_pll2.rate /1000000 ); pr_info("FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n",
This all looks good.
arch/arm/mach-ep93xx/core.c line 32
Quoted 9 lines
arch/arm/mach-ep93xx/core.c +1
-0@@ -29,12 +29,14 @@#include <linux/termios.h> #include <linux/amba/bus.h> #include <linux/amba/serial.h> +#include <linux/spi/spi.h>
Nitpick... Please put this after the i2c includes below.
Quoted 33 lines
arch/arm/mach-ep93xx/core.c +20
-0#include <linux/i2c.h> #include <linux/i2c-gpio.h> #include <mach/hardware.h> #include <mach/fb.h> #include <mach/ep93xx_keypad.h> +#include <mach/ep93xx_spi.h> #include <asm/mach/map.h> #include <asm/mach/time.h> @@ -363,6 +365,54 @@ void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr)platform_device_register (&ep93xx_eth_device); } +static struct ep93xx_spi_info ep93xx_spi_master_data; + +static struct resource ep93xx_spi_resources[] = { + { + .start =EP93XX_SPI_PHYS_BASE , + .end =EP93XX_SPI_PHYS_BASE +0x18 -1 , + .flags =IORESOURCE_MEM , + }, + { + .start =IRQ_EP93XX_SSP , + .end =IRQ_EP93XX_SSP , + .flags =IORESOURCE_IRQ , + }, +}; + +static struct platform_device ep93xx_spi_device = { + .name ="ep93xx-spi" , + .id =0 , + .dev = {
arch/arm/mach-ep93xx/core.c void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr) · line 396
Quoted 10 lines
arch/arm/mach-ep93xx/core.c +10
-0+ .platform_data = &ep93xx_spi_master_data, + }, + .num_resources = ARRAY_SIZE(ep93xx_spi_resources), + .resource = ep93xx_spi_resources, +}; + +/** +* ep93xx_register_spi() - registers spi platform device +* @info: ep93xx board specific spi master info (__initdata) +* @devices: SPI devices to register
Please add (__initdata) at the end of the line above.
Quoted 9 lines
arch/arm/mach-ep93xx/core.c +9
-0+* @num: number of SPI devices to register +* +* This function registers platform device for the EP93xx SPI controller and +* also makes sure that SPI pins are muxed so that I2S is not using those pins. +* Caller should allocate necessary GPIO lines before before calling this. +*/ +void __init ep93xx_register_spi (struct ep93xx_spi_info *info, +struct spi_board_info *devices,int num) +{
arch/arm/mach-ep93xx/core.c void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr) · line 415
Quoted 10 lines
arch/arm/mach-ep93xx/core.c +10
-0+/* +* When SPI is used, we need to make sure that I2S is muxed off from +* SPI pins. +*/ +ep93xx_devcfg_clear_bits (EP93XX_SYSCON_DEVCFG_I2SONSSP ); + + ep93xx_spi_master_data = *info; +spi_register_board_info (devices, num); +platform_device_register (&ep93xx_spi_device); +}
Nitpick... Please move the spi block above after the i2c block below it.
arch/arm/mach-ep93xx/core.c void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr) · line 418
Quoted 2 lines
arch/arm/mach-ep93xx/core.c +0
-0/************************************************************************* * EP93xx i2c peripheral handling
Other than the comments above, this all looks good.
arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h line 112
Quoted 12 lines
arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h +1
-0@@ -106,6 +106,7 @@#define EP93XX_AAC_BASE EP93XX_APB_IOMEM(0x00080000) +#define EP93XX_SPI_PHYS_BASE EP93XX_APB_PHYS(0x000a0000)#define EP93XX_SPI_BASE EP93XX_APB_IOMEM(0x000a0000)#define EP93XX_IRDA_BASE EP93XX_APB_IOMEM(0x000b0000)
This is good.
Quoted 19 lines
arch/arm/mach-ep93xx/include/mach/platform.h +2
-0@@ -4,11 +4,13 @@#ifndef __ASSEMBLY__ +struct spi_board_info ;struct i2c_gpio_platform_data ;struct i2c_board_info ;struct platform_device ;struct ep93xxfb_mach_info ;struct ep93xx_keypad_platform_data ; +struct ep93xx_spi_info ;struct ep93xx_eth_data {
arch/arm/mach-ep93xx/include/mach/platform.h static inline void ep93xx_devcfg_clear_bits(unsigned int bits) · line 43
Quoted 8 lines
arch/arm/mach-ep93xx/include/mach/platform.h +2
-0@@ -34,6 +36,8 @@ static inline void ep93xx_devcfg_clear_bits(unsigned int bits) }void ep93xx_register_eth (struct ep93xx_eth_data *data,int copy_addr); +void ep93xx_register_spi (struct ep93xx_spi_info *info, +struct spi_board_info *devices,int num);void ep93xx_register_i2c (struct i2c_gpio_platform_data *data,struct i2c_board_info *devices,int num);void ep93xx_register_fb (struct ep93xxfb_mach_info *data);
Nitpick... Again, please move the spi stuff to after the i2c stuff.
I would like them moved since an i2c gpio expander "could" be used to provide the chip selects for the spi controller. Putting them in this order provides a hint to the registration order for the devices to get the bindings correct. I will provide the necessary patch to you shortly.
Again, this is all just nitpick stuff.
Overall this is all good.
Regards,
Hartley
v5 cover spi: driver for Cirrus EP93xx SPI controller
In reply to Mika WesterbergQuoted 15 lines · Mika Westerberg
Hello,
This series implements SPI master driver for Cirrus Logic EP93xx SPI controllers.
This is fifth iteration of the driver.
Changes to the previous version:
- addressed review comments
- added priming the TX FIFO when transfer is started
- in case of ROR interrupt we clear it
- added documentation inDocumentation/spi/ep93xx_spiwhich provides sample code how the driver can be hooked into platform data
Quoted 8 lines
I tested this on TS-7260 board with at25 and
mmc_spidrivers. Now that I have Sim.One board, which is EP9307 based, I also tested with that (I hacked the board a bit to get EGPIO9 as a chip select).Note that patch 2/2 depends on patch that is already in Russell's patch tracking system:
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=5998/1
Mika,
Following is a patch to allow using built-in gpios, external gpio expanders (spi/i2c/etc.) or really any other mechanism for the chip select.
Using your example in Documentation/spi/ep93xx_spi as a starting point, the modified setup code would look like this:
+...
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <mach/ep93xx_spi.h>
+
+/* this is our GPIO line used for chip select */
+#define MMC_CHIP_SELECT_GPIO EP93XX_GPIO_LINE_EGPIO9
+
+static int ts72xx_mmc_spi_setup(struct spi_device *spi)
+{
+ int err;
+
+ err = gpio_request(MMC_CHIP_SELECT_GPIO, spi->modalias);
+ if (err)
+ return err;
+
+ gpio_direction_output(MMC_CHIP_SELECT_GPIO, 1);
+
+ return 0;
+}
+
+static void ts72xx_mmc_spi_cleanup(struct spi_device *spi) +{
+ gpio_set_value(MMC_CHIP_SELECT_GPIO, 1);
+ gpio_direction_input(MMC_CHIP_SELECT_GPIO);
+ gpio_free(MMC_CHIP_SELECT_GPIO);
+}
+
+static void ts72xx_mmc_spi_cs_control(struct spi_device *spi, int value) +{
+ gpio_set_value(MMC_CHIP_SELECT_GPIO, value);
+}
+
+static struct ep93xx_spi_chip_ops ts72xx_mmc_spi_ops = {
+ .setup = ts72xx_mmc_spi_setup,
+ .cleanup = ts72xx_mmc_spi_cleanup,
+ .cs_control = ts72xx_mmc_spi_cs_control,
+};
+
+static struct spi_board_info ts72xx_spi_devices[] __initconst = {
+ {
+ .modalias = "mmc_spi",
+ .controller_data = &ts72xx_mmc_spi_ops,
+ /*
+ * We use 10 MHz even though the maximum is 7.4 MHz. The driver
+ * will limit it automatically to max. frequency.
+ */
+ .max_speed_hz = 10 * 1000 * 1000,
+ .bus_num = 0,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ },
+};
+
+static struct ep93xx_spi_info ts72xx_spi_info = {
+ .num_chipselect = ARRAY_SIZE(ts72xx_spi_devices),
+};
+
+static void __init ts72xx_init_machine(void)
+{
+ ...
+ ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices,
+ ARRAY_SIZE(ts72xx_spi_devices));
+}
Every spi device in struct spi_board_info would have it's own private struct ep93xx_spi_chip_ops which is passed in the controller_data field.
Note, this isn't a git produced patch. I currently don't have my development tree under git control...
---
Expand the chip select options for the ep93xx_spi driver to allow using spi/i2c/etc. gpio expanders to provide the chip selects.
This introduces a per-chip structure to provide setup/cleanup callbacks for the chip select mechanism and a cs_control callback that is used to actually select/deselect the device.
---
arm/mach-ep93xx/include/mach/ep93xx_spi.h renamed from arm/mach-ep93xx/include/mach/ep93xx_spi.h.orig +11 -16
@@ -7,25 +7,20 @@ struct spi_device;* struct ep93xx_spi_info - EP93xx specific SPI descriptor * @num_chipselect: number of chip selects on this board, must be * at least one -* @cs_control: chip select control function. Can be %NULL if not needed. -* -* This structure is passed from board support files to EP93xx SPI controller -* driver. It provides callback hook to control chip select lines that are -* allocated in board support files during the board initialization. */ struct ep93xx_spi_info {int num_chipselect; -/* -* cs_control() - control board chipselect GPIO lines -* @spi: SPI device whose chipselect is controlled -* @value: value to set the chip select line to -* -* This function is used to control board specific chip select lines. -* @value is either %0 or %1. -* -* This function is called from thread context and can sleep if -* necessary. -*/ +}; + +/** +* struct ep93xx_spi_chip_ops - operation callbacks for SPI slave device +* @setup: setup the chip select mechanism +* @cleanup: cleanup the chip select mechanism +* @cs_control: control the device chip select +*/ +struct ep93xx_spi_chip_ops { +int (*setup)(struct spi_device *spi); +void (*cleanup)(struct spi_device *spi);void (*cs_control)(struct spi_device *spi,int value); };
spi/ep93xx_spi.c renamed from spi/ep93xx_spi.c.orig +29 -17
145 lines not on this page · raw message
v5 cover spi: driver for Cirrus EP93xx SPI controller
Quoted 25 lines · H Hartley Sweeten
Quoted 15 lines · Mika Westerberg
Hello,
This series implements SPI master driver for Cirrus Logic EP93xx SPI controllers.
This is fifth iteration of the driver.
Changes to the previous version:
- addressed review comments
- added priming the TX FIFO when transfer is started
- in case of ROR interrupt we clear it
- added documentation inDocumentation/spi/ep93xx_spiwhich provides sample code how the driver can be hooked into platform dataQuoted 8 lines
I tested this on TS-7260 board with at25 and
mmc_spidrivers. Now that I have Sim.One board, which is EP9307 based, I also tested with that (I hacked the board a bit to get EGPIO9 as a chip select).Note that patch 2/2 depends on patch that is already in Russell's patch tracking system:
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=5998/1
Quoted 4 lines
Mika,
Following is a patch to allow using built-in gpios, external gpio expanders (spi/i2c/etc.) or really any other mechanism for the chip select.
Ok, thanks. I'll wait a bit if there are more comments and then incorporate your patch along with your review comments and post v6. I'm really hoping that v6 would be last revision.
Regards,
MW