summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig18
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/atmel_spi.c131
-rw-r--r--drivers/spi/au1550_spi.c26
-rw-r--r--drivers/spi/mpc52xx_psc_spi.c5
-rw-r--r--drivers/spi/pxa2xx_spi.c5
-rw-r--r--drivers/spi/spi_gpio.c360
-rw-r--r--drivers/spi/spi_imx.c25
-rw-r--r--drivers/spi/spi_s3c24xx.c40
-rw-r--r--drivers/spi/spi_s3c24xx_gpio.c3
-rw-r--r--drivers/spi/spidev.c4
11 files changed, 537 insertions, 81 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b9d0efb6803f..4a6fe01831a8 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -78,7 +78,7 @@ config SPI_AU1550
will be called au1550_spi.
config SPI_BITBANG
- tristate "Bitbanging SPI master"
+ tristate "Utilities for Bitbanging SPI masters"
help
With a few GPIO pins, your system can bitbang the SPI protocol.
Select this to get SPI support through I/O pins (GPIO, parallel
@@ -100,6 +100,22 @@ config SPI_BUTTERFLY
inexpensive battery powered microcontroller evaluation board.
This same cable can be used to flash new firmware.
+config SPI_GPIO
+ tristate "GPIO-based bitbanging SPI Master"
+ depends on GENERIC_GPIO
+ select SPI_BITBANG
+ help
+ This simple GPIO bitbanging SPI master uses the arch-neutral GPIO
+ interface to manage MOSI, MISO, SCK, and chipselect signals. SPI
+ slaves connected to a bus using this driver are configured as usual,
+ except that the spi_board_info.controller_data holds the GPIO number
+ for the chipselect used by this controller driver.
+
+ Note that this driver often won't achieve even 1 Mbit/sec speeds,
+ making it unusually slow for SPI. If your platform can inline
+ GPIO operations, you should be able to leverage that for better
+ speed with a custom version of this driver; see the source code.
+
config SPI_IMX
tristate "Freescale iMX SPI controller"
depends on ARCH_IMX && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ccf18de34e1e..5e9f521b8844 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_BFIN) += spi_bfin5xx.o
obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o
obj-$(CONFIG_SPI_AU1550) += au1550_spi.o
obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
+obj-$(CONFIG_SPI_GPIO) += spi_gpio.o
obj-$(CONFIG_SPI_IMX) += spi_imx.o
obj-$(CONFIG_SPI_LM70_LLP) += spi_lm70llp.o
obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index 8abae4ad0fa5..5e39bac9c51b 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -30,13 +30,6 @@
* The core SPI transfer engine just talks to a register bank to set up
* DMA transfers; transfer queue progress is driven by IRQs. The clock
* framework provides the base clock, subdivided for each spi_device.
- *
- * Newer controllers, marked with "new_1" flag, have:
- * - CR.LASTXFER
- * - SPI_MR.DIV32 may become FDIV or must-be-zero (here: always zero)
- * - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs)
- * - SPI_CSRx.CSAAT
- * - SPI_CSRx.SBCR allows faster clocking
*/
struct atmel_spi {
spinlock_t lock;
@@ -45,7 +38,6 @@ struct atmel_spi {
int irq;
struct clk *clk;
struct platform_device *pdev;
- unsigned new_1:1;
struct spi_device *stay;
u8 stopping;
@@ -59,10 +51,33 @@ struct atmel_spi {
dma_addr_t buffer_dma;
};
+/* Controller-specific per-slave state */
+struct atmel_spi_device {
+ unsigned int npcs_pin;
+ u32 csr;
+};
+
#define BUFFER_SIZE PAGE_SIZE
#define INVALID_DMA_ADDRESS 0xffffffff
/*
+ * Version 2 of the SPI controller has
+ * - CR.LASTXFER
+ * - SPI_MR.DIV32 may become FDIV or must-be-zero (here: always zero)
+ * - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs)
+ * - SPI_CSRx.CSAAT
+ * - SPI_CSRx.SBCR allows faster clocking
+ *
+ * We can determine the controller version by reading the VERSION
+ * register, but I haven't checked that it exists on all chips, and
+ * this is cheaper anyway.
+ */
+static bool atmel_spi_is_v2(void)
+{
+ return !cpu_is_at91rm9200();
+}
+
+/*
* Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby
* they assume that spi slave device state will not change on deselect, so
* that automagic deselection is OK. ("NPCSx rises if no data is to be
@@ -80,39 +95,58 @@ struct atmel_spi {
* Master on Chip Select 0.") No workaround exists for that ... so for
* nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH,
* and (c) will trigger that first erratum in some cases.
+ *
+ * TODO: Test if the atmel_spi_is_v2() branch below works on
+ * AT91RM9200 if we use some other register than CSR0. However, don't
+ * do this unconditionally since AP7000 has an errata where the BITS
+ * field in CSR0 overrides all other CSRs.
*/
static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
{
- unsigned gpio = (unsigned) spi->controller_data;
+ struct atmel_spi_device *asd = spi->controller_state;
unsigned active = spi->mode & SPI_CS_HIGH;
u32 mr;
- int i;
- u32 csr;
- u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
-
- /* Make sure clock polarity is correct */
- for (i = 0; i < spi->master->num_chipselect; i++) {
- csr = spi_readl(as, CSR0 + 4 * i);
- if ((csr ^ cpol) & SPI_BIT(CPOL))
- spi_writel(as, CSR0 + 4 * i, csr ^ SPI_BIT(CPOL));
- }
- mr = spi_readl(as, MR);
- mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
+ if (atmel_spi_is_v2()) {
+ /*
+ * Always use CSR0. This ensures that the clock
+ * switches to the correct idle polarity before we
+ * toggle the CS.
+ */
+ spi_writel(as, CSR0, asd->csr);
+ spi_writel(as, MR, SPI_BF(PCS, 0x0e) | SPI_BIT(MODFDIS)
+ | SPI_BIT(MSTR));
+ mr = spi_readl(as, MR);
+ gpio_set_value(asd->npcs_pin, active);
+ } else {
+ u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
+ int i;
+ u32 csr;
+
+ /* Make sure clock polarity is correct */
+ for (i = 0; i < spi->master->num_chipselect; i++) {
+ csr = spi_readl(as, CSR0 + 4 * i);
+ if ((csr ^ cpol) & SPI_BIT(CPOL))
+ spi_writel(as, CSR0 + 4 * i,
+ csr ^ SPI_BIT(CPOL));
+ }
+
+ mr = spi_readl(as, MR);
+ mr = SPI_BFINS(PCS, ~(1 << spi->chip_select), mr);
+ if (spi->chip_select != 0)
+ gpio_set_value(asd->npcs_pin, active);
+ spi_writel(as, MR, mr);
+ }
dev_dbg(&spi->dev, "activate %u%s, mr %08x\n",
- gpio, active ? " (high)" : "",
+ asd->npcs_pin, active ? " (high)" : "",
mr);
-
- if (!(cpu_is_at91rm9200() && spi->chip_select == 0))
- gpio_set_value(gpio, active);
- spi_writel(as, MR, mr);
}
static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
{
- unsigned gpio = (unsigned) spi->controller_data;
+ struct atmel_spi_device *asd = spi->controller_state;
unsigned active = spi->mode & SPI_CS_HIGH;
u32 mr;
@@ -126,11 +160,11 @@ static void cs_deactivate(struct atmel_spi *as, struct spi_device *spi)
}
dev_dbg(&spi->dev, "DEactivate %u%s, mr %08x\n",
- gpio, active ? " (low)" : "",
+ asd->npcs_pin, active ? " (low)" : "",
mr);
- if (!(cpu_is_at91rm9200() && spi->chip_select == 0))
- gpio_set_value(gpio, !active);
+ if (atmel_spi_is_v2() || spi->chip_select != 0)
+ gpio_set_value(asd->npcs_pin, !active);
}
static inline int atmel_spi_xfer_is_last(struct spi_message *msg,
@@ -502,6 +536,7 @@ atmel_spi_interrupt(int irq, void *dev_id)
static int atmel_spi_setup(struct spi_device *spi)
{
struct atmel_spi *as;
+ struct atmel_spi_device *asd;
u32 scbr, csr;
unsigned int bits = spi->bits_per_word;
unsigned long bus_hz;
@@ -536,19 +571,16 @@ static int atmel_spi_setup(struct spi_device *spi)
}
/* see notes above re chipselect */
- if (cpu_is_at91rm9200()
+ if (!atmel_spi_is_v2()
&& spi->chip_select == 0
&& (spi->mode & SPI_CS_HIGH)) {
dev_dbg(&spi->dev, "setup: can't be active-high\n");
return -EINVAL;
}
- /*
- * Pre-new_1 chips start out at half the peripheral
- * bus speed.
- */
+ /* v1 chips start out at half the peripheral bus speed. */
bus_hz = clk_get_rate(as->clk);
- if (!as->new_1)
+ if (!atmel_spi_is_v2())
bus_hz /= 2;
if (spi->max_speed_hz) {
@@ -589,11 +621,20 @@ static int atmel_spi_setup(struct spi_device *spi)
/* chipselect must have been muxed as GPIO (e.g. in board setup) */
npcs_pin = (unsigned int)spi->controller_data;
- if (!spi->controller_state) {
+ asd = spi->controller_state;
+ if (!asd) {
+ asd = kzalloc(sizeof(struct atmel_spi_device), GFP_KERNEL);
+ if (!asd)
+ return -ENOMEM;
+
ret = gpio_request(npcs_pin, spi->dev.bus_id);
- if (ret)
+ if (ret) {
+ kfree(asd);
return ret;
- spi->controller_state = (void *)npcs_pin;
+ }
+
+ asd->npcs_pin = npcs_pin;
+ spi->controller_state = asd;
gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH));
} else {
unsigned long flags;
@@ -605,11 +646,14 @@ static int atmel_spi_setup(struct spi_device *spi)
spin_unlock_irqrestore(&as->lock, flags);
}
+ asd->csr = csr;
+
dev_dbg(&spi->dev,
"setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n",
bus_hz / scbr, bits, spi->mode, spi->chip_select, csr);
- spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
+ if (!atmel_spi_is_v2())
+ spi_writel(as, CSR0 + 4 * spi->chip_select, csr);
return 0;
}
@@ -684,10 +728,11 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg)
static void atmel_spi_cleanup(struct spi_device *spi)
{
struct atmel_spi *as = spi_master_get_devdata(spi->master);
+ struct atmel_spi_device *asd = spi->controller_state;
unsigned gpio = (unsigned) spi->controller_data;
unsigned long flags;
- if (!spi->controller_state)
+ if (!asd)
return;
spin_lock_irqsave(&as->lock, flags);
@@ -697,7 +742,9 @@ static void atmel_spi_cleanup(struct spi_device *spi)
}
spin_unlock_irqrestore(&as->lock, flags);
+ spi->controller_state = NULL;
gpio_free(gpio);
+ kfree(asd);
}
/*-------------------------------------------------------------------------*/
@@ -755,8 +802,6 @@ static int __init atmel_spi_probe(struct platform_device *pdev)
goto out_free_buffer;
as->irq = irq;
as->clk = clk;
- if (!cpu_is_at91rm9200())
- as->new_1 = 1;
ret = request_irq(irq, atmel_spi_interrupt, 0,
pdev->dev.bus_id, master);
diff --git a/drivers/spi/au1550_spi.c b/drivers/spi/au1550_spi.c
index 87b73e0169c5..b02f25c702fd 100644
--- a/drivers/spi/au1550_spi.c
+++ b/drivers/spi/au1550_spi.c
@@ -369,10 +369,23 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t)
dma_rx_addr = t->rx_dma;
/*
- * check if buffers are already dma mapped, map them otherwise
+ * check if buffers are already dma mapped, map them otherwise:
+ * - first map the TX buffer, so cache data gets written to memory
+ * - then map the RX buffer, so that cache entries (with
+ * soon-to-be-stale data) get removed
* use rx buffer in place of tx if tx buffer was not provided
* use temp rx buffer (preallocated or realloc to fit) for rx dma
*/
+ if (t->tx_buf) {
+ if (t->tx_dma == 0) { /* if DMA_ADDR_INVALID, map it */
+ dma_tx_addr = dma_map_single(hw->dev,
+ (void *)t->tx_buf,
+ t->len, DMA_TO_DEVICE);
+ if (dma_mapping_error(hw->dev, dma_tx_addr))
+ dev_err(hw->dev, "tx dma map error\n");
+ }
+ }
+
if (t->rx_buf) {
if (t->rx_dma == 0) { /* if DMA_ADDR_INVALID, map it */
dma_rx_addr = dma_map_single(hw->dev,
@@ -396,15 +409,8 @@ static int au1550_spi_dma_txrxb(struct spi_device *spi, struct spi_transfer *t)
dma_sync_single_for_device(hw->dev, dma_rx_addr,
t->len, DMA_FROM_DEVICE);
}
- if (t->tx_buf) {
- if (t->tx_dma == 0) { /* if DMA_ADDR_INVALID, map it */
- dma_tx_addr = dma_map_single(hw->dev,
- (void *)t->tx_buf,
- t->len, DMA_TO_DEVICE);
- if (dma_mapping_error(hw->dev, dma_tx_addr))
- dev_err(hw->dev, "tx dma map error\n");
- }
- } else {
+
+ if (!t->tx_buf) {
dma_sync_single_for_device(hw->dev, dma_rx_addr,
t->len, DMA_BIDIRECTIONAL);
hw->tx = hw->rx;
diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c
index 0debe11b67b4..3b97803e1d11 100644
--- a/drivers/spi/mpc52xx_psc_spi.c
+++ b/drivers/spi/mpc52xx_psc_spi.c
@@ -142,6 +142,7 @@ static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi,
unsigned rfalarm;
unsigned send_at_once = MPC52xx_PSC_BUFSIZE;
unsigned recv_at_once;
+ int last_block = 0;
if (!t->tx_buf && !t->rx_buf && t->len)
return -EINVAL;
@@ -151,15 +152,17 @@ static int mpc52xx_psc_spi_transfer_rxtx(struct spi_device *spi,
while (rb < t->len) {
if (t->len - rb > MPC52xx_PSC_BUFSIZE) {
rfalarm = MPC52xx_PSC_RFALARM;
+ last_block = 0;
} else {
send_at_once = t->len - sb;
rfalarm = MPC52xx_PSC_BUFSIZE - (t->len - rb);
+ last_block = 1;
}
dev_dbg(&spi->dev, "send %d bytes...\n", send_at_once);
for (; send_at_once; sb++, send_at_once--) {
/* set EOF flag before the last word is sent */
- if (send_at_once == 1)
+ if (send_at_once == 1 && last_block)
out_8(&psc->ircr2, 0x01);
if (tx_buf)
diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c
index cf12f2d84be2..d0fc4ca2f656 100644
--- a/drivers/spi/pxa2xx_spi.c
+++ b/drivers/spi/pxa2xx_spi.c
@@ -32,8 +32,8 @@
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/delay.h>
-#include <asm/dma.h>
+#include <mach/dma.h>
#include <mach/hardware.h>
#include <mach/pxa-regs.h>
#include <mach/regs-ssp.h>
@@ -1561,11 +1561,12 @@ out_error_master_alloc:
static int pxa2xx_spi_remove(struct platform_device *pdev)
{
struct driver_data *drv_data = platform_get_drvdata(pdev);
- struct ssp_device *ssp = drv_data->ssp;
+ struct ssp_device *ssp;
int status = 0;
if (!drv_data)
return 0;
+ ssp = drv_data->ssp;
/* Remove the queue */
status = destroy_queue(drv_data);
diff --git a/drivers/spi/spi_gpio.c b/drivers/spi/spi_gpio.c
new file mode 100644
index 000000000000..49698cabc30d
--- /dev/null
+++ b/drivers/spi/spi_gpio.c
@@ -0,0 +1,360 @@
+/*
+ * spi_gpio.c - SPI master driver using generic bitbanged GPIO
+ *
+ * Copyright (C) 2006,2008 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/spi/spi_gpio.h>
+
+
+/*
+ * This bitbanging SPI master driver should help make systems usable
+ * when a native hardware SPI engine is not available, perhaps because
+ * its driver isn't yet working or because the I/O pins it requires
+ * are used for other purposes.
+ *
+ * platform_device->driver_data ... points to spi_gpio
+ *
+ * spi->controller_state ... reserved for bitbang framework code
+ * spi->controller_data ... holds chipselect GPIO
+ *
+ * spi->master->dev.driver_data ... points to spi_gpio->bitbang
+ */
+
+struct spi_gpio {
+ struct spi_bitbang bitbang;
+ struct spi_gpio_platform_data pdata;
+ struct platform_device *pdev;
+};
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Because the overhead of going through four GPIO procedure calls
+ * per transferred bit can make performance a problem, this code
+ * is set up so that you can use it in either of two ways:
+ *
+ * - The slow generic way: set up platform_data to hold the GPIO
+ * numbers used for MISO/MOSI/SCK, and issue procedure calls for
+ * each of them. This driver can handle several such busses.
+ *
+ * - The quicker inlined way: only helps with platform GPIO code
+ * that inlines operations for constant GPIOs. This can give
+ * you tight (fast!) inner loops, but each such bus needs a
+ * new driver. You'll define a new C file, with Makefile and
+ * Kconfig support; the C code can be a total of six lines:
+ *
+ * #define DRIVER_NAME "myboard_spi2"
+ * #define SPI_MISO_GPIO 119
+ * #define SPI_MOSI_GPIO 120
+ * #define SPI_SCK_GPIO 121
+ * #define SPI_N_CHIPSEL 4
+ * #include "spi_gpio.c"
+ */
+
+#ifndef DRIVER_NAME
+#define DRIVER_NAME "spi_gpio"
+
+#define GENERIC_BITBANG /* vs tight inlines */
+
+/* all functions referencing these symbols must define pdata */
+#define SPI_MISO_GPIO ((pdata)->miso)
+#define SPI_MOSI_GPIO ((pdata)->mosi)
+#define SPI_SCK_GPIO ((pdata)->sck)
+
+#define SPI_N_CHIPSEL ((pdata)->num_chipselect)
+
+#endif
+
+/*----------------------------------------------------------------------*/
+
+static inline const struct spi_gpio_platform_data * __pure
+spi_to_pdata(const struct spi_device *spi)
+{
+ const struct spi_bitbang *bang;
+ const struct spi_gpio *spi_gpio;
+
+ bang = spi_master_get_devdata(spi->master);
+ spi_gpio = container_of(bang, struct spi_gpio, bitbang);
+ return &spi_gpio->pdata;
+}
+
+/* this is #defined to avoid unused-variable warnings when inlining */
+#define pdata spi_to_pdata(spi)
+
+static inline void setsck(const struct spi_device *spi, int is_on)
+{
+ gpio_set_value(SPI_SCK_GPIO, is_on);
+}
+
+static inline void setmosi(const struct spi_device *spi, int is_on)
+{
+ gpio_set_value(SPI_MOSI_GPIO, is_on);
+}
+
+static inline int getmiso(const struct spi_device *spi)
+{
+ return gpio_get_value(SPI_MISO_GPIO);
+}
+
+#undef pdata
+
+/*
+ * NOTE: this clocks "as fast as we can". It "should" be a function of the
+ * requested device clock. Software overhead means we usually have trouble
+ * reaching even one Mbit/sec (except when we can inline bitops), so for now
+ * we'll just assume we never need additional per-bit slowdowns.
+ */
+#define spidelay(nsecs) do {} while (0)
+
+#define EXPAND_BITBANG_TXRX
+#include <linux/spi/spi_bitbang.h>
+
+/*
+ * These functions can leverage inline expansion of GPIO calls to shrink
+ * costs for a txrx bit, often by factors of around ten (by instruction
+ * count). That is particularly visible for larger word sizes, but helps
+ * even with default 8-bit words.
+ *
+ * REVISIT overheads calling these functions for each word also have
+ * significant performance costs. Having txrx_bufs() calls that inline
+ * the txrx_word() logic would help performance, e.g. on larger blocks
+ * used with flash storage or MMC/SD. There should also be ways to make
+ * GCC be less stupid about reloading registers inside the I/O loops,
+ * even without inlined GPIO calls; __attribute__((hot)) on GCC 4.3?
+ */
+
+static u32 spi_gpio_txrx_word_mode0(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, bits);
+}
+
+static u32 spi_gpio_txrx_word_mode1(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits);
+}
+
+static u32 spi_gpio_txrx_word_mode2(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits);
+}
+
+static u32 spi_gpio_txrx_word_mode3(struct spi_device *spi,
+ unsigned nsecs, u32 word, u8 bits)
+{
+ return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits);
+}
+
+/*----------------------------------------------------------------------*/
+
+static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
+{
+ unsigned long cs = (unsigned long) spi->controller_data;
+
+ /* set initial clock polarity */
+ if (is_active)
+ setsck(spi, spi->mode & SPI_CPOL);
+
+ /* SPI is normally active-low */
+ gpio_set_value(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
+}
+
+static int spi_gpio_setup(struct spi_device *spi)
+{
+ unsigned long cs = (unsigned long) spi->controller_data;
+ int status = 0;
+
+ if (spi->bits_per_word > 32)
+ return -EINVAL;
+
+ if (!spi->controller_state) {
+ status = gpio_request(cs, spi->dev.bus_id);
+ if (status)
+ return status;
+ status = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH);
+ }
+ if (!status)
+ status = spi_bitbang_setup(spi);
+ if (status) {
+ if (!spi->controller_state)
+ gpio_free(cs);
+ }
+ return status;
+}
+
+static void spi_gpio_cleanup(struct spi_device *spi)
+{
+ unsigned long cs = (unsigned long) spi->controller_data;
+
+ gpio_free(cs);
+ spi_bitbang_cleanup(spi);
+}
+
+static int __init spi_gpio_alloc(unsigned pin, const char *label, bool is_in)
+{
+ int value;
+
+ value = gpio_request(pin, label);
+ if (value == 0) {
+ if (is_in)
+ value = gpio_direction_input(pin);
+ else
+ value = gpio_direction_output(pin, 0);
+ }
+ return value;
+}
+
+static int __init
+spi_gpio_request(struct spi_gpio_platform_data *pdata, const char *label)
+{
+ int value;
+
+ /* NOTE: SPI_*_GPIO symbols may reference "pdata" */
+
+ value = spi_gpio_alloc(SPI_MOSI_GPIO, label, false);
+ if (value)
+ goto done;
+
+ value = spi_gpio_alloc(SPI_MISO_GPIO, label, true);
+ if (value)
+ goto free_mosi;
+
+ value = spi_gpio_alloc(SPI_SCK_GPIO, label, false);
+ if (value)
+ goto free_miso;
+
+ goto done;
+
+free_miso:
+ gpio_free(SPI_MISO_GPIO);
+free_mosi:
+ gpio_free(SPI_MOSI_GPIO);
+done:
+ return value;
+}
+
+static int __init spi_gpio_probe(struct platform_device *pdev)
+{
+ int status;
+ struct spi_master *master;
+ struct spi_gpio *spi_gpio;
+ struct spi_gpio_platform_data *pdata;
+
+ pdata = pdev->dev.platform_data;
+#ifdef GENERIC_BITBANG
+ if (!pdata || !pdata->num_chipselect)
+ return -ENODEV;
+#endif
+
+ status = spi_gpio_request(pdata, dev_name(&pdev->dev));
+ if (status < 0)
+ return status;
+
+ master = spi_alloc_master(&pdev->dev, sizeof *spi_gpio);
+ if (!master) {
+ status = -ENOMEM;
+ goto gpio_free;
+ }
+ spi_gpio = spi_master_get_devdata(master);
+ platform_set_drvdata(pdev, spi_gpio);
+
+ spi_gpio->pdev = pdev;
+ if (pdata)
+ spi_gpio->pdata = *pdata;
+
+ master->bus_num = pdev->id;
+ master->num_chipselect = SPI_N_CHIPSEL;
+ master->setup = spi_gpio_setup;
+ master->cleanup = spi_gpio_cleanup;
+
+ spi_gpio->bitbang.master = spi_master_get(master);
+ spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_0] = spi_gpio_txrx_word_mode0;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_1] = spi_gpio_txrx_word_mode1;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_2] = spi_gpio_txrx_word_mode2;
+ spi_gpio->bitbang.txrx_word[SPI_MODE_3] = spi_gpio_txrx_word_mode3;
+ spi_gpio->bitbang.setup_transfer = spi_bitbang_setup_transfer;
+ spi_gpio->bitbang.flags = SPI_CS_HIGH;
+
+ status = spi_bitbang_start(&spi_gpio->bitbang);
+ if (status < 0) {
+ spi_master_put(spi_gpio->bitbang.master);
+gpio_free:
+ gpio_free(SPI_MISO_GPIO);
+ gpio_free(SPI_MOSI_GPIO);
+ gpio_free(SPI_SCK_GPIO);
+ spi_master_put(master);
+ }
+
+ return status;
+}
+
+static int __exit spi_gpio_remove(struct platform_device *pdev)
+{
+ struct spi_gpio *spi_gpio;
+ struct spi_gpio_platform_data *pdata;
+ int status;
+
+ spi_gpio = platform_get_drvdata(pdev);
+ pdata = pdev->dev.platform_data;
+
+ /* stop() unregisters child devices too */
+ status = spi_bitbang_stop(&spi_gpio->bitbang);
+ spi_master_put(spi_gpio->bitbang.master);
+
+ platform_set_drvdata(pdev, NULL);
+
+ gpio_free(SPI_MISO_GPIO);
+ gpio_free(SPI_MOSI_GPIO);
+ gpio_free(SPI_SCK_GPIO);
+
+ return status;
+}
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
+static struct platform_driver spi_gpio_driver = {
+ .driver.name = DRIVER_NAME,
+ .driver.owner = THIS_MODULE,
+ .remove = __exit_p(spi_gpio_remove),
+};
+
+static int __init spi_gpio_init(void)
+{
+ return platform_driver_probe(&spi_gpio_driver, spi_gpio_probe);
+}
+module_init(spi_gpio_init);
+
+static void __exit spi_gpio_exit(void)
+{
+ platform_driver_unregister(&spi_gpio_driver);
+}
+module_exit(spi_gpio_exit);
+
+
+MODULE_DESCRIPTION("SPI master driver using generic bitbanged GPIO ");
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c
index 0b4db0ce78d6..269a55ec52ef 100644
--- a/drivers/spi/spi_imx.c
+++ b/drivers/spi/spi_imx.c
@@ -1456,7 +1456,7 @@ static int __init spi_imx_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct spi_imx_master *platform_info;
struct spi_master *master;
- struct driver_data *drv_data = NULL;
+ struct driver_data *drv_data;
struct resource *res;
int irq, status = 0;
@@ -1467,14 +1467,6 @@ static int __init spi_imx_probe(struct platform_device *pdev)
goto err_no_pdata;
}
- drv_data->clk = clk_get(&pdev->dev, "perclk2");
- if (IS_ERR(drv_data->clk)) {
- dev_err(&pdev->dev, "probe - cannot get get\n");
- status = PTR_ERR(drv_data->clk);
- goto err_no_clk;
- }
- clk_enable(drv_data->clk);
-
/* Allocate master with space for drv_data */
master = spi_alloc_master(dev, sizeof(struct driver_data));
if (!master) {
@@ -1495,6 +1487,14 @@ static int __init spi_imx_probe(struct platform_device *pdev)
drv_data->dummy_dma_buf = SPI_DUMMY_u32;
+ drv_data->clk = clk_get(&pdev->dev, "perclk2");
+ if (IS_ERR(drv_data->clk)) {
+ dev_err(&pdev->dev, "probe - cannot get clock\n");
+ status = PTR_ERR(drv_data->clk);
+ goto err_no_clk;
+ }
+ clk_enable(drv_data->clk);
+
/* Find and map resources */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
@@ -1630,12 +1630,13 @@ err_no_iomap:
kfree(drv_data->ioarea);
err_no_iores:
- spi_master_put(master);
-
-err_no_pdata:
clk_disable(drv_data->clk);
clk_put(drv_data->clk);
+
err_no_clk:
+ spi_master_put(master);
+
+err_no_pdata:
err_no_mem:
return status;
}
diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c
index c252cbac00f1..b3ebc1d0f85f 100644
--- a/drivers/spi/spi_s3c24xx.c
+++ b/drivers/spi/spi_s3c24xx.c
@@ -19,6 +19,7 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
@@ -27,8 +28,7 @@
#include <asm/dma.h>
#include <mach/hardware.h>
-#include <mach/regs-gpio.h>
-#include <asm/plat-s3c24xx/regs-spi.h>
+#include <plat/regs-spi.h>
#include <mach/spi.h>
struct s3c24xx_spi {
@@ -66,7 +66,7 @@ static inline struct s3c24xx_spi *to_hw(struct spi_device *sdev)
static void s3c24xx_spi_gpiocs(struct s3c2410_spi_info *spi, int cs, int pol)
{
- s3c2410_gpio_setpin(spi->pin_cs, pol);
+ gpio_set_value(spi->pin_cs, pol);
}
static void s3c24xx_spi_chipsel(struct spi_device *spi, int value)
@@ -248,8 +248,13 @@ static void s3c24xx_spi_initialsetup(struct s3c24xx_spi *hw)
writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
- if (hw->pdata && hw->pdata->gpio_setup)
- hw->pdata->gpio_setup(hw->pdata, 1);
+ if (hw->pdata) {
+ if (hw->set_cs == s3c24xx_spi_gpiocs)
+ gpio_direction_output(hw->pdata->pin_cs, 1);
+
+ if (hw->pdata->gpio_setup)
+ hw->pdata->gpio_setup(hw->pdata, 1);
+ }
}
static int __init s3c24xx_spi_probe(struct platform_device *pdev)
@@ -343,18 +348,27 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
goto err_no_clk;
}
- s3c24xx_spi_initialsetup(hw);
-
/* setup any gpio we can */
if (!pdata->set_cs) {
- hw->set_cs = s3c24xx_spi_gpiocs;
+ if (pdata->pin_cs < 0) {
+ dev_err(&pdev->dev, "No chipselect pin\n");
+ goto err_register;
+ }
- s3c2410_gpio_setpin(pdata->pin_cs, 1);
- s3c2410_gpio_cfgpin(pdata->pin_cs, S3C2410_GPIO_OUTPUT);
+ err = gpio_request(pdata->pin_cs, dev_name(&pdev->dev));
+ if (err) {
+ dev_err(&pdev->dev, "Failed to get gpio for cs\n");
+ goto err_register;
+ }
+
+ hw->set_cs = s3c24xx_spi_gpiocs;
+ gpio_direction_output(pdata->pin_cs, 1);
} else
hw->set_cs = pdata->set_cs;
+ s3c24xx_spi_initialsetup(hw);
+
/* register our spi controller */
err = spi_bitbang_start(&hw->bitbang);
@@ -366,6 +380,9 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev)
return 0;
err_register:
+ if (hw->set_cs == s3c24xx_spi_gpiocs)
+ gpio_free(pdata->pin_cs);
+
clk_disable(hw->clk);
clk_put(hw->clk);
@@ -401,6 +418,9 @@ static int __exit s3c24xx_spi_remove(struct platform_device *dev)
free_irq(hw->irq, hw);
iounmap(hw->regs);
+ if (hw->set_cs == s3c24xx_spi_gpiocs)
+ gpio_free(hw->pdata->pin_cs);
+
release_resource(hw->ioarea);
kfree(hw->ioarea);
diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c
index cc1f647f579b..f2447a5476bb 100644
--- a/drivers/spi/spi_s3c24xx_gpio.c
+++ b/drivers/spi/spi_s3c24xx_gpio.c
@@ -34,7 +34,7 @@ struct s3c2410_spigpio {
static inline struct s3c2410_spigpio *spidev_to_sg(struct spi_device *spi)
{
- return spi->controller_data;
+ return spi_master_get_devdata(spi->master);
}
static inline void setsck(struct spi_device *dev, int on)
@@ -118,6 +118,7 @@ static int s3c2410_spigpio_probe(struct platform_device *dev)
/* setup spi bitbang adaptor */
sp->bitbang.master = spi_master_get(master);
sp->bitbang.master->bus_num = info->bus_num;
+ sp->bitbang.master->num_chipselect = info->num_chipselect;
sp->bitbang.chipselect = s3c2410_spigpio_chipselect;
sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0;
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 89a43755a453..5d869c4d3eb2 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -597,7 +597,9 @@ static int spidev_probe(struct spi_device *spi)
}
mutex_unlock(&device_list_lock);
- if (status != 0)
+ if (status == 0)
+ spi_set_drvdata(spi, spidev);
+ else
kfree(spidev);
return status;