mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 03:10:12 +00:00
spi: Fixes for v4.0
A collection of driver specific fixes to which the usual comments about them being important if you see them mostly apply (except for the comment fix). The pl022 one is particularly nasty for anyone affected by it. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJU+tr0AAoJECTWi3JdVIfQ4HIH/2p3AR8VJ1NzmqKslFUaC7SQ CT6iIiV+1gT+Q/2CLtTwY04gVJrmbO85pl4aotefxuCsb8YFGPCEo3f0lYU/3XwK ZQuC/7LFpWCqQCtSxoat9XQBHoFkWMrFDdsesQJLg9F46bCx/vVUuMaPrTXwSPLG DA6isoNZgEBJeKAxKhOdwT/nJUrVJhNwEX8fa/vuISnde4ckVuX+34O60V0N0/S2 7hEw3LQFZW0IPsnkmEygd5ATonK/+s7BXLwoAZWJGpZeWB1YsBUiHV7fLunj6gVy DMbKI3Fp1Yy/q0h6J+DzzbLvQxj0WTAX8EUz8PCh2QYRvUNiKeJdbKLbLUjGZgA= =Lpqm -----END PGP SIGNATURE----- Merge tag 'spi-v4.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi Pull spi fixes from Mark Brown: "A collection of driver specific fixes to which the usual comments about them being important if you see them mostly apply (except for the comment fix). The pl022 one is particularly nasty for anyone affected by it" * tag 'spi-v4.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: spi: pl022: Fix race in giveback() leading to driver lock-up spi: dw-mid: avoid potential NULL dereference spi: img-spfi: Verify max spfi transfer length spi: fix a typo in comment. spi: atmel: Fix interrupt setup for PDC transfers spi: dw: revisit FIFO size detection again spi: dw-pci: correct number of chip selects drivers: spi: ti-qspi: wait for busy bit clear before data write/read
This commit is contained in:
commit
d08edd8f09
8 changed files with 47 additions and 12 deletions
|
@ -764,17 +764,17 @@ static void atmel_spi_pdc_next_xfer(struct spi_master *master,
|
||||||
(unsigned long long)xfer->rx_dma);
|
(unsigned long long)xfer->rx_dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* REVISIT: We're waiting for ENDRX before we start the next
|
/* REVISIT: We're waiting for RXBUFF before we start the next
|
||||||
* transfer because we need to handle some difficult timing
|
* transfer because we need to handle some difficult timing
|
||||||
* issues otherwise. If we wait for ENDTX in one transfer and
|
* issues otherwise. If we wait for TXBUFE in one transfer and
|
||||||
* then starts waiting for ENDRX in the next, it's difficult
|
* then starts waiting for RXBUFF in the next, it's difficult
|
||||||
* to tell the difference between the ENDRX interrupt we're
|
* to tell the difference between the RXBUFF interrupt we're
|
||||||
* actually waiting for and the ENDRX interrupt of the
|
* actually waiting for and the RXBUFF interrupt of the
|
||||||
* previous transfer.
|
* previous transfer.
|
||||||
*
|
*
|
||||||
* It should be doable, though. Just not now...
|
* It should be doable, though. Just not now...
|
||||||
*/
|
*/
|
||||||
spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES));
|
spi_writel(as, IER, SPI_BIT(RXBUFF) | SPI_BIT(OVRES));
|
||||||
spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN));
|
spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,9 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_tx(struct dw_spi *dws)
|
||||||
1,
|
1,
|
||||||
DMA_MEM_TO_DEV,
|
DMA_MEM_TO_DEV,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!txdesc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
txdesc->callback = dw_spi_dma_tx_done;
|
txdesc->callback = dw_spi_dma_tx_done;
|
||||||
txdesc->callback_param = dws;
|
txdesc->callback_param = dws;
|
||||||
|
|
||||||
|
@ -184,6 +187,9 @@ static struct dma_async_tx_descriptor *dw_spi_dma_prepare_rx(struct dw_spi *dws)
|
||||||
1,
|
1,
|
||||||
DMA_DEV_TO_MEM,
|
DMA_DEV_TO_MEM,
|
||||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!rxdesc)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
rxdesc->callback = dw_spi_dma_rx_done;
|
rxdesc->callback = dw_spi_dma_rx_done;
|
||||||
rxdesc->callback_param = dws;
|
rxdesc->callback_param = dws;
|
||||||
|
|
||||||
|
|
|
@ -36,13 +36,13 @@ struct spi_pci_desc {
|
||||||
|
|
||||||
static struct spi_pci_desc spi_pci_mid_desc_1 = {
|
static struct spi_pci_desc spi_pci_mid_desc_1 = {
|
||||||
.setup = dw_spi_mid_init,
|
.setup = dw_spi_mid_init,
|
||||||
.num_cs = 32,
|
.num_cs = 5,
|
||||||
.bus_num = 0,
|
.bus_num = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct spi_pci_desc spi_pci_mid_desc_2 = {
|
static struct spi_pci_desc spi_pci_mid_desc_2 = {
|
||||||
.setup = dw_spi_mid_init,
|
.setup = dw_spi_mid_init,
|
||||||
.num_cs = 4,
|
.num_cs = 2,
|
||||||
.bus_num = 1,
|
.bus_num = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -621,14 +621,14 @@ static void spi_hw_init(struct device *dev, struct dw_spi *dws)
|
||||||
if (!dws->fifo_len) {
|
if (!dws->fifo_len) {
|
||||||
u32 fifo;
|
u32 fifo;
|
||||||
|
|
||||||
for (fifo = 2; fifo <= 256; fifo++) {
|
for (fifo = 1; fifo < 256; fifo++) {
|
||||||
dw_writew(dws, DW_SPI_TXFLTR, fifo);
|
dw_writew(dws, DW_SPI_TXFLTR, fifo);
|
||||||
if (fifo != dw_readw(dws, DW_SPI_TXFLTR))
|
if (fifo != dw_readw(dws, DW_SPI_TXFLTR))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dw_writew(dws, DW_SPI_TXFLTR, 0);
|
dw_writew(dws, DW_SPI_TXFLTR, 0);
|
||||||
|
|
||||||
dws->fifo_len = (fifo == 2) ? 0 : fifo - 1;
|
dws->fifo_len = (fifo == 1) ? 0 : fifo;
|
||||||
dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
|
dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -459,6 +459,13 @@ static int img_spfi_transfer_one(struct spi_master *master,
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (xfer->len > SPFI_TRANSACTION_TSIZE_MASK) {
|
||||||
|
dev_err(spfi->dev,
|
||||||
|
"Transfer length (%d) is greater than the max supported (%d)",
|
||||||
|
xfer->len, SPFI_TRANSACTION_TSIZE_MASK);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Stop all DMA and reset the controller if the previous transaction
|
* Stop all DMA and reset the controller if the previous transaction
|
||||||
* timed-out and never completed it's DMA.
|
* timed-out and never completed it's DMA.
|
||||||
|
|
|
@ -534,12 +534,12 @@ static void giveback(struct pl022 *pl022)
|
||||||
pl022->cur_msg = NULL;
|
pl022->cur_msg = NULL;
|
||||||
pl022->cur_transfer = NULL;
|
pl022->cur_transfer = NULL;
|
||||||
pl022->cur_chip = NULL;
|
pl022->cur_chip = NULL;
|
||||||
spi_finalize_current_message(pl022->master);
|
|
||||||
|
|
||||||
/* disable the SPI/SSP operation */
|
/* disable the SPI/SSP operation */
|
||||||
writew((readw(SSP_CR1(pl022->virtbase)) &
|
writew((readw(SSP_CR1(pl022->virtbase)) &
|
||||||
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
|
(~SSP_CR1_MASK_SSE)), SSP_CR1(pl022->virtbase));
|
||||||
|
|
||||||
|
spi_finalize_current_message(pl022->master);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -101,6 +101,7 @@ struct ti_qspi {
|
||||||
#define QSPI_FLEN(n) ((n - 1) << 0)
|
#define QSPI_FLEN(n) ((n - 1) << 0)
|
||||||
|
|
||||||
/* STATUS REGISTER */
|
/* STATUS REGISTER */
|
||||||
|
#define BUSY 0x01
|
||||||
#define WC 0x02
|
#define WC 0x02
|
||||||
|
|
||||||
/* INTERRUPT REGISTER */
|
/* INTERRUPT REGISTER */
|
||||||
|
@ -199,6 +200,21 @@ static void ti_qspi_restore_ctx(struct ti_qspi *qspi)
|
||||||
ti_qspi_write(qspi, ctx_reg->clkctrl, QSPI_SPI_CLOCK_CNTRL_REG);
|
ti_qspi_write(qspi, ctx_reg->clkctrl, QSPI_SPI_CLOCK_CNTRL_REG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u32 qspi_is_busy(struct ti_qspi *qspi)
|
||||||
|
{
|
||||||
|
u32 stat;
|
||||||
|
unsigned long timeout = jiffies + QSPI_COMPLETION_TIMEOUT;
|
||||||
|
|
||||||
|
stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG);
|
||||||
|
while ((stat & BUSY) && time_after(timeout, jiffies)) {
|
||||||
|
cpu_relax();
|
||||||
|
stat = ti_qspi_read(qspi, QSPI_SPI_STATUS_REG);
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN(stat & BUSY, "qspi busy\n");
|
||||||
|
return stat & BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
int wlen, count;
|
int wlen, count;
|
||||||
|
@ -211,6 +227,9 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
||||||
wlen = t->bits_per_word >> 3; /* in bytes */
|
wlen = t->bits_per_word >> 3; /* in bytes */
|
||||||
|
|
||||||
while (count) {
|
while (count) {
|
||||||
|
if (qspi_is_busy(qspi))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
switch (wlen) {
|
switch (wlen) {
|
||||||
case 1:
|
case 1:
|
||||||
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n",
|
dev_dbg(qspi->dev, "tx cmd %08x dc %08x data %02x\n",
|
||||||
|
@ -266,6 +285,9 @@ static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t)
|
||||||
|
|
||||||
while (count) {
|
while (count) {
|
||||||
dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc);
|
dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc);
|
||||||
|
if (qspi_is_busy(qspi))
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
|
ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG);
|
||||||
if (!wait_for_completion_timeout(&qspi->transfer_complete,
|
if (!wait_for_completion_timeout(&qspi->transfer_complete,
|
||||||
QSPI_COMPLETION_TIMEOUT)) {
|
QSPI_COMPLETION_TIMEOUT)) {
|
||||||
|
|
|
@ -649,7 +649,7 @@ struct spi_transfer {
|
||||||
* sequence completes. On some systems, many such sequences can execute as
|
* sequence completes. On some systems, many such sequences can execute as
|
||||||
* as single programmed DMA transfer. On all systems, these messages are
|
* as single programmed DMA transfer. On all systems, these messages are
|
||||||
* queued, and might complete after transactions to other devices. Messages
|
* queued, and might complete after transactions to other devices. Messages
|
||||||
* sent to a given spi_device are alway executed in FIFO order.
|
* sent to a given spi_device are always executed in FIFO order.
|
||||||
*
|
*
|
||||||
* The code that submits an spi_message (and its spi_transfers)
|
* The code that submits an spi_message (and its spi_transfers)
|
||||||
* to the lower layers is responsible for managing its memory.
|
* to the lower layers is responsible for managing its memory.
|
||||||
|
|
Loading…
Reference in a new issue