Commit e34ff011 authored by Martin Sperl's avatar Martin Sperl Committed by Mark Brown

spi: bcm2835: move to the transfer_one driver model

This also allows for GPIO-CS to get used removing the limitation of
2/3 SPI devises on the SPI bus.

Fixes: spi-cs-high with native CS with multiple devices on the spi-bus
resetting the chip selects to "normal" polarity after a finished
transfer.

No other functionality/improvements added.

Tested with the following 4 devices on the spi-bus:
* mcp2515 with native CS
* mcp2515 with gpio CS
* fb_st7735r with native CS
    (plus spi-cs-high via transistor inverting polarity)
* enc28j60 with gpio-CS
Tested-by: default avatarMartin Sperl <kernel@martin.sperl.org>
Signed-off-by: default avatarMartin Sperl <kernel@martin.sperl.org>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 1be38e04
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (C) 2012 Chris Boot * Copyright (C) 2012 Chris Boot
* Copyright (C) 2013 Stephen Warren * Copyright (C) 2013 Stephen Warren
* Copyright (C) 2015 Martin Sperl
* *
* This driver is inspired by: * This driver is inspired by:
* spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org> * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
...@@ -29,6 +30,7 @@ ...@@ -29,6 +30,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_irq.h> #include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
...@@ -76,10 +78,10 @@ struct bcm2835_spi { ...@@ -76,10 +78,10 @@ struct bcm2835_spi {
void __iomem *regs; void __iomem *regs;
struct clk *clk; struct clk *clk;
int irq; int irq;
struct completion done;
const u8 *tx_buf; const u8 *tx_buf;
u8 *rx_buf; u8 *rx_buf;
int len; int tx_len;
int rx_len;
}; };
static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
...@@ -96,10 +98,12 @@ static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs) ...@@ -96,10 +98,12 @@ static inline void bcm2835_rd_fifo(struct bcm2835_spi *bs)
{ {
u8 byte; u8 byte;
while (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_RXD) { while ((bs->rx_len) &&
(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_RXD)) {
byte = bcm2835_rd(bs, BCM2835_SPI_FIFO); byte = bcm2835_rd(bs, BCM2835_SPI_FIFO);
if (bs->rx_buf) if (bs->rx_buf)
*bs->rx_buf++ = byte; *bs->rx_buf++ = byte;
bs->rx_len--;
} }
} }
...@@ -107,47 +111,60 @@ static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs) ...@@ -107,47 +111,60 @@ static inline void bcm2835_wr_fifo(struct bcm2835_spi *bs)
{ {
u8 byte; u8 byte;
while ((bs->len) && while ((bs->tx_len) &&
(bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_TXD)) { (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_TXD)) {
byte = bs->tx_buf ? *bs->tx_buf++ : 0; byte = bs->tx_buf ? *bs->tx_buf++ : 0;
bcm2835_wr(bs, BCM2835_SPI_FIFO, byte); bcm2835_wr(bs, BCM2835_SPI_FIFO, byte);
bs->len--; bs->tx_len--;
} }
} }
static void bcm2835_spi_reset_hw(struct spi_master *master)
{
struct bcm2835_spi *bs = spi_master_get_devdata(master);
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
/* Disable SPI interrupts and transfer */
cs &= ~(BCM2835_SPI_CS_INTR |
BCM2835_SPI_CS_INTD |
BCM2835_SPI_CS_TA);
/* and reset RX/TX FIFOS */
cs |= BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX;
/* and reset the SPI_HW */
bcm2835_wr(bs, BCM2835_SPI_CS, cs);
}
static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id) static irqreturn_t bcm2835_spi_interrupt(int irq, void *dev_id)
{ {
struct spi_master *master = dev_id; struct spi_master *master = dev_id;
struct bcm2835_spi *bs = spi_master_get_devdata(master); struct bcm2835_spi *bs = spi_master_get_devdata(master);
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
/* Read as many bytes as possible from FIFO */ /* Read as many bytes as possible from FIFO */
bcm2835_rd_fifo(bs); bcm2835_rd_fifo(bs);
/* Write as many bytes as possible to FIFO */
if (bs->len) { /* there is more data to transmit */ bcm2835_wr_fifo(bs);
bcm2835_wr_fifo(bs);
} else { /* Transfer complete */ /* based on flags decide if we can finish the transfer */
/* Disable SPI interrupts */ if (bcm2835_rd(bs, BCM2835_SPI_CS) & BCM2835_SPI_CS_DONE) {
cs &= ~(BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD); /* Transfer complete - reset SPI HW */
bcm2835_wr(bs, BCM2835_SPI_CS, cs); bcm2835_spi_reset_hw(master);
/* wake up the framework */
/* complete(&master->xfer_completion);
* Wake up bcm2835_spi_transfer_one(), which will call
* bcm2835_spi_finish_transfer(), to drain the RX FIFO.
*/
complete(&bs->done);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int bcm2835_spi_start_transfer(struct spi_device *spi, static int bcm2835_spi_transfer_one(struct spi_master *master,
struct spi_transfer *tfr) struct spi_device *spi,
struct spi_transfer *tfr)
{ {
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); struct bcm2835_spi *bs = spi_master_get_devdata(master);
unsigned long spi_hz, clk_hz, cdiv; unsigned long spi_hz, clk_hz, cdiv;
u32 cs = BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA; u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
/* set clock */
spi_hz = tfr->speed_hz; spi_hz = tfr->speed_hz;
clk_hz = clk_get_rate(bs->clk); clk_hz = clk_get_rate(bs->clk);
...@@ -163,100 +180,118 @@ static int bcm2835_spi_start_transfer(struct spi_device *spi, ...@@ -163,100 +180,118 @@ static int bcm2835_spi_start_transfer(struct spi_device *spi,
} else { } else {
cdiv = 0; /* 0 is the slowest we can go */ cdiv = 0; /* 0 is the slowest we can go */
} }
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
/* handle all the modes */
if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf)) if ((spi->mode & SPI_3WIRE) && (tfr->rx_buf))
cs |= BCM2835_SPI_CS_REN; cs |= BCM2835_SPI_CS_REN;
if (spi->mode & SPI_CPOL) if (spi->mode & SPI_CPOL)
cs |= BCM2835_SPI_CS_CPOL; cs |= BCM2835_SPI_CS_CPOL;
if (spi->mode & SPI_CPHA) if (spi->mode & SPI_CPHA)
cs |= BCM2835_SPI_CS_CPHA; cs |= BCM2835_SPI_CS_CPHA;
if (!(spi->mode & SPI_NO_CS)) { /* for gpio_cs set dummy CS so that no HW-CS get changed
if (spi->mode & SPI_CS_HIGH) { * we can not run this in bcm2835_spi_set_cs, as it does
cs |= BCM2835_SPI_CS_CSPOL; * not get called for cs_gpio cases, so we need to do it here
cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select; */
} if (gpio_is_valid(spi->cs_gpio) || (spi->mode & SPI_NO_CS))
cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01;
cs |= spi->chip_select;
}
reinit_completion(&bs->done); /* set transmit buffers and length */
bs->tx_buf = tfr->tx_buf; bs->tx_buf = tfr->tx_buf;
bs->rx_buf = tfr->rx_buf; bs->rx_buf = tfr->rx_buf;
bs->len = tfr->len; bs->tx_len = tfr->len;
bs->rx_len = tfr->len;
bcm2835_wr(bs, BCM2835_SPI_CLK, cdiv);
/* /*
* Enable the HW block. This will immediately trigger a DONE (TX * Enable the HW block. This will immediately trigger a DONE (TX
* empty) interrupt, upon which we will fill the TX FIFO with the * empty) interrupt, upon which we will fill the TX FIFO with the
* first TX bytes. Pre-filling the TX FIFO here to avoid the * first TX bytes. Pre-filling the TX FIFO here to avoid the
* interrupt doesn't work:-( * interrupt doesn't work:-(
*/ */
cs |= BCM2835_SPI_CS_INTR | BCM2835_SPI_CS_INTD | BCM2835_SPI_CS_TA;
bcm2835_wr(bs, BCM2835_SPI_CS, cs); bcm2835_wr(bs, BCM2835_SPI_CS, cs);
return 0; /* signal that we need to wait for completion */
return 1;
} }
static int bcm2835_spi_finish_transfer(struct spi_device *spi, static void bcm2835_spi_handle_err(struct spi_master *master,
struct spi_transfer *tfr, struct spi_message *msg)
bool cs_change)
{ {
struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); bcm2835_spi_reset_hw(master);
u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
if (tfr->delay_usecs)
udelay(tfr->delay_usecs);
if (cs_change)
/* Clear TA flag */
bcm2835_wr(bs, BCM2835_SPI_CS, cs & ~BCM2835_SPI_CS_TA);
return 0;
} }
static int bcm2835_spi_transfer_one(struct spi_master *master, static void bcm2835_spi_set_cs(struct spi_device *spi, bool gpio_level)
struct spi_message *mesg)
{ {
/*
* we can assume that we are "native" as per spi_set_cs
* calling us ONLY when cs_gpio is not set
* we can also assume that we are CS < 3 as per bcm2835_spi_setup
* we would not get called because of error handling there.
* the level passed is the electrical level not enabled/disabled
* so it has to get translated back to enable/disable
* see spi_set_cs in spi.c for the implementation
*/
struct spi_master *master = spi->master;
struct bcm2835_spi *bs = spi_master_get_devdata(master); struct bcm2835_spi *bs = spi_master_get_devdata(master);
struct spi_transfer *tfr; u32 cs = bcm2835_rd(bs, BCM2835_SPI_CS);
struct spi_device *spi = mesg->spi; bool enable;
int err = 0;
unsigned int timeout;
bool cs_change;
list_for_each_entry(tfr, &mesg->transfers, transfer_list) {
err = bcm2835_spi_start_transfer(spi, tfr);
if (err)
goto out;
timeout = wait_for_completion_timeout(
&bs->done,
msecs_to_jiffies(BCM2835_SPI_TIMEOUT_MS)
);
if (!timeout) {
err = -ETIMEDOUT;
goto out;
}
cs_change = tfr->cs_change || /* calculate the enable flag from the passed gpio_level */
list_is_last(&tfr->transfer_list, &mesg->transfers); enable = (spi->mode & SPI_CS_HIGH) ? gpio_level : !gpio_level;
err = bcm2835_spi_finish_transfer(spi, tfr, cs_change); /* set flags for "reverse" polarity in the registers */
if (err) if (spi->mode & SPI_CS_HIGH) {
goto out; /* set the correct CS-bits */
cs |= BCM2835_SPI_CS_CSPOL;
cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select;
} else {
/* clean the CS-bits */
cs &= ~BCM2835_SPI_CS_CSPOL;
cs &= ~(BCM2835_SPI_CS_CSPOL0 << spi->chip_select);
}
mesg->actual_length += (tfr->len - bs->len); /* select the correct chip_select depending on disabled/enabled */
if (enable) {
/* set cs correctly */
if (spi->mode & SPI_NO_CS) {
/* use the "undefined" chip-select */
cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01;
} else {
/* set the chip select */
cs &= ~(BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01);
cs |= spi->chip_select;
}
} else {
/* disable CSPOL which puts HW-CS into deselected state */
cs &= ~BCM2835_SPI_CS_CSPOL;
/* use the "undefined" chip-select as precaution */
cs |= BCM2835_SPI_CS_CS_10 | BCM2835_SPI_CS_CS_01;
} }
out: /* finally set the calculated flags in SPI_CS */
/* Clear FIFOs, and disable the HW block */ bcm2835_wr(bs, BCM2835_SPI_CS, cs);
bcm2835_wr(bs, BCM2835_SPI_CS, }
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
mesg->status = err;
spi_finalize_current_message(master);
return 0; static int bcm2835_spi_setup(struct spi_device *spi)
{
/*
* sanity checking the native-chipselects
*/
if (spi->mode & SPI_NO_CS)
return 0;
if (gpio_is_valid(spi->cs_gpio))
return 0;
if (spi->chip_select < 3)
return 0;
/* error in the case of native CS requested with CS-id > 2 */
dev_err(&spi->dev,
"setup: only three native chip-selects are supported\n"
);
return -EINVAL;
} }
static int bcm2835_spi_probe(struct platform_device *pdev) static int bcm2835_spi_probe(struct platform_device *pdev)
...@@ -277,13 +312,14 @@ static int bcm2835_spi_probe(struct platform_device *pdev) ...@@ -277,13 +312,14 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
master->mode_bits = BCM2835_SPI_MODE_BITS; master->mode_bits = BCM2835_SPI_MODE_BITS;
master->bits_per_word_mask = SPI_BPW_MASK(8); master->bits_per_word_mask = SPI_BPW_MASK(8);
master->num_chipselect = 3; master->num_chipselect = 3;
master->transfer_one_message = bcm2835_spi_transfer_one; master->setup = bcm2835_spi_setup;
master->set_cs = bcm2835_spi_set_cs;
master->transfer_one = bcm2835_spi_transfer_one;
master->handle_err = bcm2835_spi_handle_err;
master->dev.of_node = pdev->dev.of_node; master->dev.of_node = pdev->dev.of_node;
bs = spi_master_get_devdata(master); bs = spi_master_get_devdata(master);
init_completion(&bs->done);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
bs->regs = devm_ioremap_resource(&pdev->dev, res); bs->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(bs->regs)) { if (IS_ERR(bs->regs)) {
...@@ -314,7 +350,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) ...@@ -314,7 +350,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
goto out_clk_disable; goto out_clk_disable;
} }
/* initialise the hardware */ /* initialise the hardware with the default polarities */
bcm2835_wr(bs, BCM2835_SPI_CS, bcm2835_wr(bs, BCM2835_SPI_CS,
BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment