summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig3
-rw-r--r--drivers/spi/spi-dw-dma.c14
-rw-r--r--drivers/spi/spi-lantiq-ssc.c12
-rw-r--r--drivers/spi/spi-rockchip.c2
-rw-r--r--drivers/spi/spi-stm32.c27
-rw-r--r--drivers/spi/spi.c21
-rw-r--r--drivers/spi/spidev.c21
7 files changed, 85 insertions, 15 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8f1f8fca79e3..8eb053803429 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -999,4 +999,7 @@ config SPI_SLAVE_SYSTEM_CONTROL
endif # SPI_SLAVE
+config SPI_DYNAMIC
+ def_bool ACPI || OF_DYNAMIC || SPI_SLAVE
+
endif # SPI
diff --git a/drivers/spi/spi-dw-dma.c b/drivers/spi/spi-dw-dma.c
index 5986c520b196..bb390ff67d1d 100644
--- a/drivers/spi/spi-dw-dma.c
+++ b/drivers/spi/spi-dw-dma.c
@@ -372,8 +372,20 @@ static int dw_spi_dma_setup(struct dw_spi *dws, struct spi_transfer *xfer)
{
u16 imr = 0, dma_ctrl = 0;
+ /*
+ * Having a Rx DMA channel serviced with higher priority than a Tx DMA
+ * channel might not be enough to provide a well balanced DMA-based
+ * SPI transfer interface. There might still be moments when the Tx DMA
+ * channel is occasionally handled faster than the Rx DMA channel.
+ * That in its turn will eventually cause the SPI Rx FIFO overflow if
+ * SPI bus speed is high enough to fill the SPI Rx FIFO in before it's
+ * cleared by the Rx DMA channel. In order to fix the problem the Tx
+ * DMA activity is intentionally slowed down by limiting the SPI Tx
+ * FIFO depth with a value twice bigger than the Tx burst length
+ * calculated earlier by the dw_spi_dma_maxburst_init() method.
+ */
dw_writel(dws, DW_SPI_DMARDLR, dws->rxburst - 1);
- dw_writel(dws, DW_SPI_DMATDLR, dws->fifo_len - dws->txburst);
+ dw_writel(dws, DW_SPI_DMATDLR, dws->txburst);
if (xfer->tx_buf)
dma_ctrl |= SPI_DMA_TDMAE;
diff --git a/drivers/spi/spi-lantiq-ssc.c b/drivers/spi/spi-lantiq-ssc.c
index 1fd7ee53d451..049a64451c75 100644
--- a/drivers/spi/spi-lantiq-ssc.c
+++ b/drivers/spi/spi-lantiq-ssc.c
@@ -184,6 +184,7 @@ struct lantiq_ssc_spi {
unsigned int tx_fifo_size;
unsigned int rx_fifo_size;
unsigned int base_cs;
+ unsigned int fdx_tx_level;
};
static u32 lantiq_ssc_readl(const struct lantiq_ssc_spi *spi, u32 reg)
@@ -481,6 +482,7 @@ static void tx_fifo_write(struct lantiq_ssc_spi *spi)
u32 data;
unsigned int tx_free = tx_fifo_free(spi);
+ spi->fdx_tx_level = 0;
while (spi->tx_todo && tx_free) {
switch (spi->bits_per_word) {
case 2 ... 8:
@@ -509,6 +511,7 @@ static void tx_fifo_write(struct lantiq_ssc_spi *spi)
lantiq_ssc_writel(spi, data, LTQ_SPI_TB);
tx_free--;
+ spi->fdx_tx_level++;
}
}
@@ -520,6 +523,13 @@ static void rx_fifo_read_full_duplex(struct lantiq_ssc_spi *spi)
u32 data;
unsigned int rx_fill = rx_fifo_level(spi);
+ /*
+ * Wait until all expected data to be shifted in.
+ * Otherwise, rx overrun may occur.
+ */
+ while (rx_fill != spi->fdx_tx_level)
+ rx_fill = rx_fifo_level(spi);
+
while (rx_fill) {
data = lantiq_ssc_readl(spi, LTQ_SPI_RB);
@@ -899,7 +909,7 @@ static int lantiq_ssc_probe(struct platform_device *pdev)
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 8) |
SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
- spi->wq = alloc_ordered_workqueue(dev_name(dev), 0);
+ spi->wq = alloc_ordered_workqueue(dev_name(dev), WQ_MEM_RECLAIM);
if (!spi->wq) {
err = -ENOMEM;
goto err_clk_put;
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 9b8a5e1233c0..4776aa815c3f 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -288,7 +288,7 @@ static void rockchip_spi_pio_writer(struct rockchip_spi *rs)
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)
{
u32 words = readl_relaxed(rs->regs + ROCKCHIP_SPI_RXFLR);
- u32 rx_left = rs->rx_left - words;
+ u32 rx_left = (rs->rx_left > words) ? rs->rx_left - words : 0;
/* the hardware doesn't allow us to change fifo threshold
* level while spi is enabled, so instead make sure to leave
diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c
index 4c643dfc7fbb..9672cda2f803 100644
--- a/drivers/spi/spi-stm32.c
+++ b/drivers/spi/spi-stm32.c
@@ -13,6 +13,7 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
@@ -1996,6 +1997,8 @@ static int stm32_spi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
+ pinctrl_pm_select_sleep_state(&pdev->dev);
+
return 0;
}
@@ -2007,13 +2010,18 @@ static int stm32_spi_runtime_suspend(struct device *dev)
clk_disable_unprepare(spi->clk);
- return 0;
+ return pinctrl_pm_select_sleep_state(dev);
}
static int stm32_spi_runtime_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct stm32_spi *spi = spi_master_get_devdata(master);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
return clk_prepare_enable(spi->clk);
}
@@ -2043,10 +2051,23 @@ static int stm32_spi_resume(struct device *dev)
return ret;
ret = spi_master_resume(master);
- if (ret)
+ if (ret) {
clk_disable_unprepare(spi->clk);
+ return ret;
+ }
- return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret) {
+ dev_err(dev, "Unable to power device:%d\n", ret);
+ return ret;
+ }
+
+ spi->cfg->config(spi);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
}
#endif
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 8158e281f354..5c5a95792c0d 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -475,6 +475,12 @@ static LIST_HEAD(spi_controller_list);
*/
static DEFINE_MUTEX(board_lock);
+/*
+ * Prevents addition of devices with same chip select and
+ * addition of devices below an unregistering controller.
+ */
+static DEFINE_MUTEX(spi_add_lock);
+
/**
* spi_alloc_device - Allocate a new SPI device
* @ctlr: Controller to which device is connected
@@ -554,7 +560,6 @@ static int spi_dev_check(struct device *dev, void *data)
*/
int spi_add_device(struct spi_device *spi)
{
- static DEFINE_MUTEX(spi_add_lock);
struct spi_controller *ctlr = spi->controller;
struct device *dev = ctlr->dev.parent;
int status;
@@ -582,6 +587,13 @@ int spi_add_device(struct spi_device *spi)
goto done;
}
+ /* Controller may unregister concurrently */
+ if (IS_ENABLED(CONFIG_SPI_DYNAMIC) &&
+ !device_is_registered(&ctlr->dev)) {
+ status = -ENODEV;
+ goto done;
+ }
+
/* Descriptors take precedence */
if (ctlr->cs_gpiods)
spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
@@ -2764,6 +2776,10 @@ void spi_unregister_controller(struct spi_controller *ctlr)
struct spi_controller *found;
int id = ctlr->bus_num;
+ /* Prevent addition of new devices, unregister existing ones */
+ if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
+ mutex_lock(&spi_add_lock);
+
device_for_each_child(&ctlr->dev, NULL, __unregister);
/* First make sure that this controller was ever added */
@@ -2784,6 +2800,9 @@ void spi_unregister_controller(struct spi_controller *ctlr)
if (found == ctlr)
idr_remove(&spi_master_idr, id);
mutex_unlock(&board_lock);
+
+ if (IS_ENABLED(CONFIG_SPI_DYNAMIC))
+ mutex_unlock(&spi_add_lock);
}
EXPORT_SYMBOL_GPL(spi_unregister_controller);
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 59e07675ef86..455e99c4958e 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -224,6 +224,11 @@ static int spidev_message(struct spidev_data *spidev,
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
n;
n--, k_tmp++, u_tmp++) {
+ /* Ensure that also following allocations from rx_buf/tx_buf will meet
+ * DMA alignment requirements.
+ */
+ unsigned int len_aligned = ALIGN(u_tmp->len, ARCH_KMALLOC_MINALIGN);
+
k_tmp->len = u_tmp->len;
total += k_tmp->len;
@@ -239,17 +244,17 @@ static int spidev_message(struct spidev_data *spidev,
if (u_tmp->rx_buf) {
/* this transfer needs space in RX bounce buffer */
- rx_total += k_tmp->len;
+ rx_total += len_aligned;
if (rx_total > bufsiz) {
status = -EMSGSIZE;
goto done;
}
k_tmp->rx_buf = rx_buf;
- rx_buf += k_tmp->len;
+ rx_buf += len_aligned;
}
if (u_tmp->tx_buf) {
/* this transfer needs space in TX bounce buffer */
- tx_total += k_tmp->len;
+ tx_total += len_aligned;
if (tx_total > bufsiz) {
status = -EMSGSIZE;
goto done;
@@ -259,7 +264,7 @@ static int spidev_message(struct spidev_data *spidev,
(uintptr_t) u_tmp->tx_buf,
u_tmp->len))
goto done;
- tx_buf += k_tmp->len;
+ tx_buf += len_aligned;
}
k_tmp->cs_change = !!u_tmp->cs_change;
@@ -293,16 +298,16 @@ static int spidev_message(struct spidev_data *spidev,
goto done;
/* copy any rx data out of bounce buffer */
- rx_buf = spidev->rx_buffer;
- for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
+ for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
+ n;
+ n--, k_tmp++, u_tmp++) {
if (u_tmp->rx_buf) {
if (copy_to_user((u8 __user *)
- (uintptr_t) u_tmp->rx_buf, rx_buf,
+ (uintptr_t) u_tmp->rx_buf, k_tmp->rx_buf,
u_tmp->len)) {
status = -EFAULT;
goto done;
}
- rx_buf += u_tmp->len;
}
}
status = total;