diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-05 13:15:12 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-05 13:15:12 -0800 |
commit | 75f5db39ff14ed95056f2cca3ad98c3cae97170c (patch) | |
tree | f265bdbda7dc9fab1bc07bca2449f9fc79c99f25 /drivers/spi/spi-bitbang.c | |
parent | 52787e91bf5375e68e90f381bd157bd92e1f4a77 (diff) | |
parent | c70efb85153162295dc80099e95dab93d486a3ed (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.c | 152 |
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; |