Fixes long standing chip select issue for non-default CS-polarity setups. The issue only shows up if 2 devices are on the bus and at least one of them is configured for inverse chip_select polarity.
Essentially the polarity bits in the SPI controller registers are getting reset to 0 (=default polarity) whenever a different SPI device is activated. This only negatively impacts SPI devices on the bus with non-default polarity. This patch makes use of spi_master->setup to set up the flags for all possible variants of the chip-select polarity flags during device setup. It also moves repeated calculations from bcm2835_spi_start_transfer into setup. Signed-off-by: Martin Sperl <[email protected]> --- drivers/spi/spi-bcm2835.c | 52 ++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 4c33214..95ee83f 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -73,6 +73,8 @@ #define BCM2835_SPI_TIMEOUT_MS 30000 #define BCM2835_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS) +#define BCM2835_SPI_NUM_CS 3 + #define DRV_NAME "spi-bcm2835" struct bcm2835_spi { @@ -83,6 +85,8 @@ struct bcm2835_spi { const u8 *tx_buf; u8 *rx_buf; int len; + u32 cs_device_flags_idle; + u32 cs_device_flags[BCM2835_SPI_NUM_CS]; }; static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg) @@ -203,19 +207,8 @@ static int bcm2835_spi_start_transfer(struct spi_device *spi, } else cdiv = 0; /* 0 is the slowest we can go */ - if (spi->mode & SPI_CPOL) - cs |= BCM2835_SPI_CS_CPOL; - if (spi->mode & SPI_CPHA) - cs |= BCM2835_SPI_CS_CPHA; - - if (!(spi->mode & SPI_NO_CS)) { - if (spi->mode & SPI_CS_HIGH) { - cs |= BCM2835_SPI_CS_CSPOL; - cs |= BCM2835_SPI_CS_CSPOL0 << spi->chip_select; - } - - cs |= spi->chip_select; - } + /* take cs from the precalculated version */ + cs |= bs->cs_device_flags[spi->chip_select]; INIT_COMPLETION(bs->done); bs->tx_buf = tfr->tx_buf; @@ -291,13 +284,41 @@ static int bcm2835_spi_transfer_one(struct spi_master *master, out: /* Clear FIFOs, and disable the HW block */ bcm2835_wr(bs, BCM2835_SPI_CS, - BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + bs->cs_device_flags_idle + | 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) { + struct bcm2835_spi *bs = spi_master_get_devdata(spi->master); + u8 cs = spi->chip_select; + u32 mode = spi->mode; + int i; + + if (!(mode & SPI_NO_CS)) { + if (mode & SPI_CS_HIGH) { + for (i=0 ; i<=BCM2835_SPI_NUM_CS ; i++) { + bs->cs_device_flags[i] |= + (BCM2835_SPI_CS_CSPOL0 << cs); + } + bs->cs_device_flags_idle |= + (BCM2835_SPI_CS_CSPOL0 << cs); + bs->cs_device_flags[cs] |= BCM2835_SPI_CS_CSPOL; + } + bs->cs_device_flags[cs] |= spi->chip_select; + } + + if (mode & SPI_CPOL) + bs->cs_device_flags[cs] |= BCM2835_SPI_CS_CPOL; + if (mode & SPI_CPHA) + bs->cs_device_flags[cs] |= BCM2835_SPI_CS_CPHA; + + return 0; +} + static int bcm2835_spi_probe(struct platform_device *pdev) { struct spi_master *master; @@ -316,7 +337,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev) master->mode_bits = BCM2835_SPI_MODE_BITS; master->bits_per_word_mask = SPI_BPW_MASK(8); master->bus_num = -1; - master->num_chipselect = 3; + master->num_chipselect = BCM2835_SPI_NUM_CS; + master->setup = bcm2835_spi_setup; master->transfer_one_message = bcm2835_spi_transfer_one; master->dev.of_node = pdev->dev.of_node; -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
