summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-bitbang.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-11-05 13:15:12 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2015-11-05 13:15:12 -0800
commit75f5db39ff14ed95056f2cca3ad98c3cae97170c (patch)
treef265bdbda7dc9fab1bc07bca2449f9fc79c99f25 /drivers/spi/spi-bitbang.c
parent52787e91bf5375e68e90f381bd157bd92e1f4a77 (diff)
parentc70efb85153162295dc80099e95dab93d486a3ed (diff)
Merge tag 'spi-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown: "Quite a lot of activity in SPI this cycle, almost all of it in drivers with a few minor improvements and tweaks in the core. - Updates to pxa2xx to support Intel Broxton and multiple chip selects. - Support for big endian in the bcm63xx driver. - Multiple slave support for the mt8173 - New driver for the auxiliary SPI controller in bcm2835 SoCs. - Support for Layerscale SoCs in the Freescale DSPI driver" * tag 'spi-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (87 commits) spi: pxa2xx: Rework self-initiated platform data creation for non-ACPI spi: pxa2xx: Add support for Intel Broxton spi: pxa2xx: Detect number of enabled Intel LPSS SPI chip select signals spi: pxa2xx: Add output control for multiple Intel LPSS chip selects spi: pxa2xx: Use LPSS prefix for defines that are Intel LPSS specific spi: Add DSPI support for layerscape family spi: ti-qspi: improve ->remove() callback spi/spi-xilinx: Fix race condition on last word read spi: Drop owner assignment from spi_drivers spi: Add THIS_MODULE to spi_driver in SPI core spi: Setup the master controller driver before setting the chipselect spi: dw: replace magic constant by DW_SPI_DR spi: mediatek: mt8173 spi multiple devices support spi: mediatek: handle controller_data in mtk_spi_setup spi: mediatek: remove mtk_spi_config spi: mediatek: Update document devicetree bindings to support multiple devices spi: fix kernel-doc warnings about missing return desc in spi.c spi: fix kernel-doc warnings about missing return desc in spi.h spi: pxa2xx: Align a few defines spi: pxa2xx: Save other reg_cs_ctrl bits when configuring chip select ...
Diffstat (limited to 'drivers/spi/spi-bitbang.c')
-rw-r--r--drivers/spi/spi-bitbang.c152
1 files changed, 43 insertions, 109 deletions
diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c
index 840a4984d365..3aa9e6e3dac8 100644
--- a/drivers/spi/spi-bitbang.c
+++ b/drivers/spi/spi-bitbang.c
@@ -24,6 +24,8 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
+#define SPI_BITBANG_CS_DELAY 100
+
/*----------------------------------------------------------------------*/
@@ -180,7 +182,6 @@ int spi_bitbang_setup(struct spi_device *spi)
{
struct spi_bitbang_cs *cs = spi->controller_state;
struct spi_bitbang *bitbang;
- unsigned long flags;
bitbang = spi_master_get_devdata(spi->master);
@@ -210,12 +211,12 @@ int spi_bitbang_setup(struct spi_device *spi)
*/
/* deselect chip (low or high) */
- spin_lock_irqsave(&bitbang->lock, flags);
+ mutex_lock(&bitbang->lock);
if (!bitbang->busy) {
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(cs->nsecs);
}
- spin_unlock_irqrestore(&bitbang->lock, flags);
+ mutex_unlock(&bitbang->lock);
return 0;
}
@@ -255,122 +256,39 @@ static int spi_bitbang_bufs(struct spi_device *spi, struct spi_transfer *t)
static int spi_bitbang_prepare_hardware(struct spi_master *spi)
{
struct spi_bitbang *bitbang;
- unsigned long flags;
bitbang = spi_master_get_devdata(spi);
- spin_lock_irqsave(&bitbang->lock, flags);
+ mutex_lock(&bitbang->lock);
bitbang->busy = 1;
- spin_unlock_irqrestore(&bitbang->lock, flags);
+ mutex_unlock(&bitbang->lock);
return 0;
}
static int spi_bitbang_transfer_one(struct spi_master *master,
- struct spi_message *m)
+ struct spi_device *spi,
+ struct spi_transfer *transfer)
{
- struct spi_bitbang *bitbang;
- unsigned nsecs;
- struct spi_transfer *t = NULL;
- unsigned cs_change;
- int status;
- int do_setup = -1;
- struct spi_device *spi = m->spi;
-
- bitbang = spi_master_get_devdata(master);
-
- /* FIXME this is made-up ... the correct value is known to
- * word-at-a-time bitbang code, and presumably chipselect()
- * should enforce these requirements too?
- */
- nsecs = 100;
-
- cs_change = 1;
- status = 0;
-
- list_for_each_entry(t, &m->transfers, transfer_list) {
-
- /* override speed or wordsize? */
- if (t->speed_hz || t->bits_per_word)
- do_setup = 1;
-
- /* init (-1) or override (1) transfer params */
- if (do_setup != 0) {
- if (bitbang->setup_transfer) {
- status = bitbang->setup_transfer(spi, t);
- if (status < 0)
- break;
- }
- if (do_setup == -1)
- do_setup = 0;
- }
-
- /* set up default clock polarity, and activate chip;
- * this implicitly updates clock and spi modes as
- * previously recorded for this device via setup().
- * (and also deselects any other chip that might be
- * selected ...)
- */
- if (cs_change) {
- bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
- ndelay(nsecs);
- }
- cs_change = t->cs_change;
- if (!t->tx_buf && !t->rx_buf && t->len) {
- status = -EINVAL;
- break;
- }
-
- /* transfer data. the lower level code handles any
- * new dma mappings it needs. our caller always gave
- * us dma-safe buffers.
- */
- if (t->len) {
- /* REVISIT dma API still needs a designated
- * DMA_ADDR_INVALID; ~0 might be better.
- */
- if (!m->is_dma_mapped)
- t->rx_dma = t->tx_dma = 0;
- status = bitbang->txrx_bufs(spi, t);
- }
- if (status > 0)
- m->actual_length += status;
- if (status != t->len) {
- /* always report some kind of error */
- if (status >= 0)
- status = -EREMOTEIO;
- break;
- }
- status = 0;
+ struct spi_bitbang *bitbang = spi_master_get_devdata(master);
+ int status = 0;
- /* protocol tweaks before next transfer */
- if (t->delay_usecs)
- udelay(t->delay_usecs);
-
- if (cs_change &&
- !list_is_last(&t->transfer_list, &m->transfers)) {
- /* sometimes a short mid-message deselect of the chip
- * may be needed to terminate a mode or command
- */
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
+ if (bitbang->setup_transfer) {
+ status = bitbang->setup_transfer(spi, transfer);
+ if (status < 0)
+ goto out;
}
- m->status = status;
+ if (transfer->len)
+ status = bitbang->txrx_bufs(spi, transfer);
- /* normally deactivate chipselect ... unless no error and
- * cs_change has hinted that the next message will probably
- * be for this chip too.
- */
- if (!(status == 0 && cs_change)) {
- ndelay(nsecs);
- bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
+ if (status == transfer->len)
+ status = 0;
+ else if (status >= 0)
+ status = -EREMOTEIO;
- spi_finalize_current_message(master);
+out:
+ spi_finalize_current_transfer(master);
return status;
}
@@ -378,17 +296,32 @@ static int spi_bitbang_transfer_one(struct spi_master *master,
static int spi_bitbang_unprepare_hardware(struct spi_master *spi)
{
struct spi_bitbang *bitbang;
- unsigned long flags;
bitbang = spi_master_get_devdata(spi);
- spin_lock_irqsave(&bitbang->lock, flags);
+ mutex_lock(&bitbang->lock);
bitbang->busy = 0;
- spin_unlock_irqrestore(&bitbang->lock, flags);
+ mutex_unlock(&bitbang->lock);
return 0;
}
+static void spi_bitbang_set_cs(struct spi_device *spi, bool enable)
+{
+ struct spi_bitbang *bitbang = spi_master_get_devdata(spi->master);
+
+ /* SPI core provides CS high / low, but bitbang driver
+ * expects CS active
+ * spi device driver takes care of handling SPI_CS_HIGH
+ */
+ enable = (!!(spi->mode & SPI_CS_HIGH) == enable);
+
+ ndelay(SPI_BITBANG_CS_DELAY);
+ bitbang->chipselect(spi, enable ? BITBANG_CS_ACTIVE :
+ BITBANG_CS_INACTIVE);
+ ndelay(SPI_BITBANG_CS_DELAY);
+}
+
/*----------------------------------------------------------------------*/
/**
@@ -427,7 +360,7 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
if (!master || !bitbang->chipselect)
return -EINVAL;
- spin_lock_init(&bitbang->lock);
+ mutex_init(&bitbang->lock);
if (!master->mode_bits)
master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
@@ -437,7 +370,8 @@ int spi_bitbang_start(struct spi_bitbang *bitbang)
master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
- master->transfer_one_message = spi_bitbang_transfer_one;
+ master->transfer_one = spi_bitbang_transfer_one;
+ master->set_cs = spi_bitbang_set_cs;
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;