* [PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller
@ 2010-04-28 17:51 Mika Westerberg
[not found] ` <cover.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
0 siblings, 1 reply; 7+ messages in thread
From: Mika Westerberg @ 2010-04-28 17:51 UTC (permalink / raw)
To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Cc: martinwguy-Re5JQEeQqe8AvxtiuMwx3w, ryan-7Wk5F4Od5/oYd5yxfr4S2w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
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
------------------------------------------------------------------------------
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v5 1/2] spi: implemented driver for Cirrus EP93xx SPI controller
[not found] ` <cover.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
@ 2010-04-28 17:51 ` Mika Westerberg
[not found] ` <c24e9af6062e579499a1cb5782cdfe44e91cb9a1.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
2010-04-28 17:51 ` [PATCH v5 2/2] ep93xx: SPI driver platform support code Mika Westerberg
2010-04-28 22:30 ` [PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller H Hartley Sweeten
2 siblings, 1 reply; 7+ messages in thread
From: Mika Westerberg @ 2010-04-28 17:51 UTC (permalink / raw)
To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Cc: martinwguy-Re5JQEeQqe8AvxtiuMwx3w, ryan-7Wk5F4Od5/oYd5yxfr4S2w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
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 <mika.westerberg-X3B1VOXEql0@public.gmane.org>
---
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
diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi
new file mode 100644
index 0000000..bbb6fc2
--- /dev/null
+++ b/Documentation/spi/ep93xx_spi
@@ -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.
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
new file mode 100644
index 0000000..98935d8
--- /dev/null
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
@@ -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 */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index a191fa2..5852340 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -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
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d7d0f89..377f845 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -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
diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c
new file mode 100644
index 0000000..20d4f1d
--- /dev/null
+++ b/drivers/spi/ep93xx_spi.c
@@ -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.
+ */
+
+#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>
+#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
+ * (>%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)) {
+ 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);
+ espi->fifo_level++;
+ }
+
+ if (espi->rx == t->len) {
+ msg->actual_length += t->len;
+ return t->len;
+ }
+
+ return 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);
+ }
+
+ 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);
+ 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");
+ 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);
+ 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);
+
+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 <mika.westerberg-X3B1VOXEql0@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-spi");
--
1.5.6.5
------------------------------------------------------------------------------
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v5 2/2] ep93xx: SPI driver platform support code
[not found] ` <cover.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
2010-04-28 17:51 ` [PATCH v5 1/2] spi: implemented " Mika Westerberg
@ 2010-04-28 17:51 ` Mika Westerberg
2010-04-28 18:31 ` H Hartley Sweeten
2010-04-28 22:30 ` [PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller H Hartley Sweeten
2 siblings, 1 reply; 7+ messages in thread
From: Mika Westerberg @ 2010-04-28 17:51 UTC (permalink / raw)
To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Cc: martinwguy-Re5JQEeQqe8AvxtiuMwx3w, ryan-7Wk5F4Od5/oYd5yxfr4S2w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
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 function
Signed-off-by: Mika Westerberg <mika.westerberg-X3B1VOXEql0@public.gmane.org>
---
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(-)
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index 5f80092..e29bdef 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -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 at %ld MHz\n",
clk_pll1.rate / 1000000, clk_pll2.rate / 1000000);
pr_info("FCLK %ld MHz, HCLK %ld MHz, PCLK %ld MHz\n",
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 90fb591..d57b61e 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -29,12 +29,14 @@
#include <linux/termios.h>
#include <linux/amba/bus.h>
#include <linux/amba/serial.h>
+#include <linux/spi/spi.h>
#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 = {
+ .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
+ * @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)
+{
+ /*
+ * 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);
+}
/*************************************************************************
* EP93xx i2c peripheral handling
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
index 93e2ecc..b1e096f 100644
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
@@ -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)
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index c6dc14d..52eebfd 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -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);
--
1.5.6.5
------------------------------------------------------------------------------
^ permalink raw reply related [flat|nested] 7+ messages in thread
* RE: [PATCH v5 2/2] ep93xx: SPI driver platform support code
2010-04-28 17:51 ` [PATCH v5 2/2] ep93xx: SPI driver platform support code Mika Westerberg
@ 2010-04-28 18:31 ` H Hartley Sweeten
0 siblings, 0 replies; 7+ messages in thread
From: H Hartley Sweeten @ 2010-04-28 18:31 UTC (permalink / raw)
To: Mika Westerberg, spi-devel-general
Cc: grant.likely, martinwguy, ryan, linux-arm-kernel
On Wednesday, April 28, 2010 10:51 AM, Mika Westerberg wrote:
>
> 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 function
>
> Signed-off-by: Mika Westerberg <mika.westerberg@iki.fi>
> ---
> 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(-)
>
> diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
> index 5f80092..e29bdef 100644
> --- a/arch/arm/mach-ep93xx/clock.c
> +++ b/arch/arm/mach-ep93xx/clock.c
> @@ -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 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.
> diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
> index 90fb591..d57b61e 100644
> --- a/arch/arm/mach-ep93xx/core.c
> +++ b/arch/arm/mach-ep93xx/core.c
> @@ -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.
> #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 = {
> + .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.
> + * @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)
> +{
> + /*
> + * 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.
> /*************************************************************************
> * EP93xx i2c peripheral handling
Other than the comments above, this all looks good.
> diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
> index 93e2ecc..b1e096f 100644
> --- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
> +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
> @@ -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.
> diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
> index c6dc14d..52eebfd 100644
> --- a/arch/arm/mach-ep93xx/include/mach/platform.h
> +++ b/arch/arm/mach-ep93xx/include/mach/platform.h
> @@ -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);
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
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v5 1/2] spi: implemented driver for Cirrus EP93xx SPI controller
[not found] ` <c24e9af6062e579499a1cb5782cdfe44e91cb9a1.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
@ 2010-04-28 20:45 ` H Hartley Sweeten
0 siblings, 0 replies; 7+ messages in thread
From: H Hartley Sweeten @ 2010-04-28 20:45 UTC (permalink / raw)
To: Mika Westerberg, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Cc: martinwguy-Re5JQEeQqe8AvxtiuMwx3w, ryan-7Wk5F4Od5/oYd5yxfr4S2w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
On Wednesday, April 28, 2010 10:51 AM, Mika Westerberg wrote:
>
> 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 <mika.westerberg-X3B1VOXEql0@public.gmane.org>
> ---
> 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
>
> diff --git a/Documentation/spi/ep93xx_spi b/Documentation/spi/ep93xx_spi
> new file mode 100644
> index 0000000..bbb6fc2
> --- /dev/null
> +++ b/Documentation/spi/ep93xx_spi
> @@ -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.
> +
> +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 = {
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.
> + {
> + .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.
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.
> diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
> new file mode 100644
> index 0000000..98935d8
> --- /dev/null
> +++ b/arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h
> @@ -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 */
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index a191fa2..5852340 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -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.
> +
> + 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
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index d7d0f89..377f845 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -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
> diff --git a/drivers/spi/ep93xx_spi.c b/drivers/spi/ep93xx_spi.c
> new file mode 100644
> index 0000000..20d4f1d
> --- /dev/null
> +++ b/drivers/spi/ep93xx_spi.c
> @@ -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.
> + */
> +
> +#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.
> +#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
> + * (>%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...
> + 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);
> + 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.
> +
> +/**
> + * 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);
> + }
> +
> + 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.
> + 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");
> + 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.
> + 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);
> +
> +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 <mika.westerberg-X3B1VOXEql0@public.gmane.org>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:ep93xx-spi");
Regards,
Hartley
------------------------------------------------------------------------------
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller
[not found] ` <cover.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
2010-04-28 17:51 ` [PATCH v5 1/2] spi: implemented " Mika Westerberg
2010-04-28 17:51 ` [PATCH v5 2/2] ep93xx: SPI driver platform support code Mika Westerberg
@ 2010-04-28 22:30 ` H Hartley Sweeten
[not found] ` <0D753D10438DA54287A00B0270842697636DB622A3-gaq956PjLg32KbjnnMDalRurcAul1UnsRrxOEX5GOmysTnJN9+BGXg@public.gmane.org>
2 siblings, 1 reply; 7+ messages in thread
From: H Hartley Sweeten @ 2010-04-28 22:30 UTC (permalink / raw)
To: Mika Westerberg, spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Cc: martinwguy-Re5JQEeQqe8AvxtiuMwx3w, ryan-7Wk5F4Od5/oYd5yxfr4S2w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
On Wednesday, April 28, 2010 10:51 AM, Mika Westerberg wrote:
>
> 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
>
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.
Signed-off-by: H Hartley Sweeten <hsweeten-3FF4nKcrg1dE2c76skzGb0EOCMrvLtNR@public.gmane.org>
---
--- arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h.orig 2010-04-28 14:36:14.000000000 -0700
+++ arch/arm/mach-ep93xx/include/mach/ep93xx_spi.h 2010-04-28 15:27:39.000000000 -0700
@@ -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);
};
--- drivers/spi/ep93xx_spi.c.orig 2010-04-28 13:06:07.000000000 -0700
+++ drivers/spi/ep93xx_spi.c 2010-04-28 15:24:47.000000000 -0700
@@ -84,7 +84,6 @@
* @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.
@@ -113,7 +112,6 @@ struct ep93xx_spi {
size_t tx;
size_t rx;
size_t fifo_level;
- void (*cs_control)(struct spi_device *, int);
};
/**
@@ -123,6 +121,7 @@ struct ep93xx_spi {
* @div_cpsr: cpsr (pre-scaler) divider
* @div_scr: scr divider
* @dss: bits per word (4 - 16 bits)
+ * @ops: private chip operations
*
* This structure is used to store hardware register specific settings for each
* SPI device. Settings are written to hardware by function
@@ -134,6 +133,7 @@ struct ep93xx_spi_chip {
u8 div_cpsr;
u8 div_scr;
u8 dss;
+ struct ep93xx_spi_chip_ops *ops;
};
/* converts bits per word to CR0.DSS value */
@@ -283,7 +283,6 @@ static int ep93xx_spi_calc_divisors(cons
/**
* 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)
*
@@ -291,14 +290,13 @@ static int ep93xx_spi_calc_divisors(cons
*
* 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)
+static void ep93xx_spi_cs_control(struct spi_device *spi, bool control)
{
+ struct ep93xx_spi_chip *chip = spi_get_ctldata(spi);
int value = (spi->mode & SPI_CS_HIGH) ? control : !control;
- if (espi->cs_control)
- espi->cs_control(spi, value);
+ if (chip->ops && chip->ops->cs_control)
+ chip->ops->cs_control(spi, value);
}
/**
@@ -323,12 +321,25 @@ static int ep93xx_spi_setup(struct spi_d
chip = spi_get_ctldata(spi);
if (!chip) {
+ dev_dbg(&espi->pdev->dev, "initial setup for %s\n",
+ spi->modalias);
+
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
- spi_set_ctldata(spi, chip);
chip->spi = spi;
+ chip->ops = spi->controller_data;
+
+ if (chip->ops && chip->ops->setup) {
+ ret = chip->ops->setup(spi);
+ if (ret) {
+ kfree(chip);
+ return ret;
+ }
+ }
+
+ spi_set_ctldata(spi, chip);
}
if (spi->max_speed_hz != chip->rate) {
@@ -345,7 +356,7 @@ static int ep93xx_spi_setup(struct spi_d
chip->dss = bits_per_word_to_dss(spi->bits_per_word);
- ep93xx_spi_cs_control(espi, spi, false);
+ ep93xx_spi_cs_control(spi, false);
return 0;
}
@@ -414,6 +425,8 @@ static void ep93xx_spi_cleanup(struct sp
chip = spi_get_ctldata(spi);
if (chip) {
+ if (chip->ops && chip->ops->cleanup)
+ chip->ops->cleanup(spi);
spi_set_ctldata(spi, NULL);
kfree(chip);
}
@@ -610,9 +623,9 @@ static void ep93xx_spi_process_transfer(
* chipselect briefly, we let the scheduler to handle
* any "delay" here.
*/
- ep93xx_spi_cs_control(espi, msg->spi, false);
+ ep93xx_spi_cs_control(msg->spi, false);
cond_resched();
- ep93xx_spi_cs_control(espi, msg->spi, true);
+ ep93xx_spi_cs_control(msg->spi, true);
}
}
@@ -674,7 +687,7 @@ static void ep93xx_spi_process_message(s
* the chipselect.
*/
ep93xx_spi_chip_setup(espi, spi_get_ctldata(msg->spi));
- ep93xx_spi_cs_control(espi, msg->spi, true);
+ ep93xx_spi_cs_control(msg->spi, true);
list_for_each_entry(t, &msg->transfers, transfer_list) {
ep93xx_spi_process_transfer(espi, msg, t);
@@ -686,7 +699,7 @@ static void ep93xx_spi_process_message(s
* 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_cs_control(msg->spi, false);
ep93xx_spi_disable(espi);
}
@@ -811,7 +824,6 @@ static int __init ep93xx_spi_probe(struc
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)) {
@@ -860,7 +872,7 @@ static int __init ep93xx_spi_probe(struc
}
error = request_irq(espi->irq, ep93xx_spi_interrupt, 0,
- "ep93xx-spi", espi);
+ "ep93xx-spi", espi);
if (error) {
dev_err(&pdev->dev, "failed to request irq\n");
goto fail_unmap_regs;
@@ -878,7 +890,7 @@ static int __init ep93xx_spi_probe(struc
/* make sure that the hardware is disabled */
ep93xx_spi_write_u8(espi, SSPCR1, 0);
- error = spi_register_master(master);
+ error = spi_register_master(master);
if (error) {
dev_err(&pdev->dev, "failed to register SPI master\n");
goto fail_free_queue;
------------------------------------------------------------------------------
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller
[not found] ` <0D753D10438DA54287A00B0270842697636DB622A3-gaq956PjLg32KbjnnMDalRurcAul1UnsRrxOEX5GOmysTnJN9+BGXg@public.gmane.org>
@ 2010-04-29 10:49 ` Mika Westerberg
0 siblings, 0 replies; 7+ messages in thread
From: Mika Westerberg @ 2010-04-29 10:49 UTC (permalink / raw)
To: H Hartley Sweeten
Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
martinwguy-Re5JQEeQqe8AvxtiuMwx3w, ryan-7Wk5F4Od5/oYd5yxfr4S2w,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
On Wed, Apr 28, 2010 at 05:30:25PM -0500, H Hartley Sweeten wrote:
> On Wednesday, April 28, 2010 10:51 AM, Mika Westerberg wrote:
> >
> > 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
> >
>
> 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
------------------------------------------------------------------------------
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2010-04-29 10:49 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-04-28 17:51 [PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller Mika Westerberg
[not found] ` <cover.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
2010-04-28 17:51 ` [PATCH v5 1/2] spi: implemented " Mika Westerberg
[not found] ` <c24e9af6062e579499a1cb5782cdfe44e91cb9a1.1272476431.git.mika.westerberg-X3B1VOXEql0@public.gmane.org>
2010-04-28 20:45 ` H Hartley Sweeten
2010-04-28 17:51 ` [PATCH v5 2/2] ep93xx: SPI driver platform support code Mika Westerberg
2010-04-28 18:31 ` H Hartley Sweeten
2010-04-28 22:30 ` [PATCH v5 0/2] spi: driver for Cirrus EP93xx SPI controller H Hartley Sweeten
[not found] ` <0D753D10438DA54287A00B0270842697636DB622A3-gaq956PjLg32KbjnnMDalRurcAul1UnsRrxOEX5GOmysTnJN9+BGXg@public.gmane.org>
2010-04-29 10:49 ` Mika Westerberg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).