From ddf75be47ca748f8b12d28ac64d624354fddf189 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 3 Aug 2020 13:09:01 +0200 Subject: [PATCH 1/8] spi: Prevent adding devices below an unregistering controller CONFIG_OF_DYNAMIC and CONFIG_ACPI allow adding SPI devices at runtime using a DeviceTree overlay or DSDT patch. CONFIG_SPI_SLAVE allows the same via sysfs. But there are no precautions to prevent adding a device below a controller that's being removed. Such a device is unusable and may not even be able to unbind cleanly as it becomes inaccessible once the controller has been torn down. E.g. it is then impossible to quiesce the device's interrupt. of_spi_notify() and acpi_spi_notify() do hold a ref on the controller, but otherwise run lockless against spi_unregister_controller(). Fix by holding the spi_add_lock in spi_unregister_controller() and bailing out of spi_add_device() if the controller has been unregistered concurrently. Fixes: ce79d54ae447 ("spi/of: Add OF notifier handler") Signed-off-by: Lukas Wunner Cc: stable@vger.kernel.org # v3.19+ Cc: Geert Uytterhoeven Cc: Octavian Purdila Cc: Pantelis Antoniou Link: https://lore.kernel.org/r/a8c3205088a969dc8410eec1eba9aface60f36af.1596451035.git.lukas@wunner.de Signed-off-by: Mark Brown --- drivers/spi/Kconfig | 3 +++ drivers/spi/spi.c | 21 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c3008e423f59..c6ea760ea5f0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1017,4 +1017,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.c b/drivers/spi/spi.c index 0b260484b4f5..92b8fb416dca 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]; @@ -2797,6 +2809,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 */ @@ -2817,6 +2833,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); From ae1ba50f1e706dfd7ce402ac52c1f1f10becad68 Mon Sep 17 00:00:00 2001 From: Tobias Schramm Date: Tue, 4 Aug 2020 21:51:36 +0200 Subject: [PATCH 2/8] spi: stm32: clear only asserted irq flags on interrupt Previously the stm32h7 interrupt thread cleared all non-masked interrupts. If an interrupt was to occur during the handling of another interrupt its flag would be unset, resulting in a lost interrupt. This patches fixes the issue by clearing only the currently set interrupt flags. Signed-off-by: Tobias Schramm Link: https://lore.kernel.org/r/20200804195136.1485392-1-t.schramm@manjaro.org Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 4c643dfc7fbb..4a21feae0103 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -966,7 +966,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) stm32h7_spi_read_rxfifo(spi, false); - writel_relaxed(mask, spi->base + STM32H7_SPI_IFCR); + writel_relaxed(sr & mask, spi->base + STM32H7_SPI_IFCR); spin_unlock_irqrestore(&spi->lock, flags); From 135dd873d3c76d812ae64c668adef3f2c59ed27f Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 10 Aug 2020 09:12:34 +0200 Subject: [PATCH 3/8] spi: stm32h7: fix race condition at end of transfer The caller of stm32_spi_transfer_one(), spi_transfer_one_message(), is waiting for us to call spi_finalize_current_transfer() and will eventually schedule a new transfer, if available. We should guarantee that the spi controller is really available before calling spi_finalize_current_transfer(). Move the call to spi_finalize_current_transfer() _after_ the call to stm32_spi_disable(). Signed-off-by: Antonio Borneo Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1597043558-29668-2-git-send-email-alain.volmat@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 4a21feae0103..814a3ec3b8ad 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -971,8 +971,8 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) spin_unlock_irqrestore(&spi->lock, flags); if (end) { - spi_finalize_current_transfer(master); stm32h7_spi_disable(spi); + spi_finalize_current_transfer(master); } return IRQ_HANDLED; From 3373e9004acc0603242622b4378c64bc01d21b5f Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Mon, 10 Aug 2020 09:12:35 +0200 Subject: [PATCH 4/8] spi: stm32: fix fifo threshold level in case of short transfer When transfer is shorter than half of the fifo, set the data packet size up to transfer size instead of up to half of the fifo. Check also that threshold is set at least to 1 data frame. Signed-off-by: Amelie Delaunay Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1597043558-29668-3-git-send-email-alain.volmat@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 814a3ec3b8ad..e5450233f3f8 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -467,20 +467,27 @@ static int stm32_spi_prepare_mbr(struct stm32_spi *spi, u32 speed_hz, /** * stm32h7_spi_prepare_fthlv - Determine FIFO threshold level * @spi: pointer to the spi controller data structure + * @xfer_len: length of the message to be transferred */ -static u32 stm32h7_spi_prepare_fthlv(struct stm32_spi *spi) +static u32 stm32h7_spi_prepare_fthlv(struct stm32_spi *spi, u32 xfer_len) { - u32 fthlv, half_fifo; + u32 fthlv, half_fifo, packet; /* data packet should not exceed 1/2 of fifo space */ half_fifo = (spi->fifo_size / 2); - if (spi->cur_bpw <= 8) - fthlv = half_fifo; - else if (spi->cur_bpw <= 16) - fthlv = half_fifo / 2; + /* data_packet should not exceed transfer length */ + if (half_fifo > xfer_len) + packet = xfer_len; else - fthlv = half_fifo / 4; + packet = half_fifo; + + if (spi->cur_bpw <= 8) + fthlv = packet; + else if (spi->cur_bpw <= 16) + fthlv = packet / 2; + else + fthlv = packet / 4; /* align packet size with data registers access */ if (spi->cur_bpw > 8) @@ -488,6 +495,9 @@ static u32 stm32h7_spi_prepare_fthlv(struct stm32_spi *spi) else fthlv -= (fthlv % 4); /* multiple of 4 */ + if (!fthlv) + fthlv = 1; + return fthlv; } @@ -1393,7 +1403,7 @@ static void stm32h7_spi_set_bpw(struct stm32_spi *spi) cfg1_setb |= (bpw << STM32H7_SPI_CFG1_DSIZE_SHIFT) & STM32H7_SPI_CFG1_DSIZE; - spi->cur_fthlv = stm32h7_spi_prepare_fthlv(spi); + spi->cur_fthlv = stm32h7_spi_prepare_fthlv(spi, spi->cur_xferlen); fthlv = spi->cur_fthlv - 1; cfg1_clrb |= STM32H7_SPI_CFG1_FTHLV; @@ -1588,6 +1598,8 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, spin_lock_irqsave(&spi->lock, flags); + spi->cur_xferlen = transfer->len; + if (spi->cur_bpw != transfer->bits_per_word) { spi->cur_bpw = transfer->bits_per_word; spi->cfg->set_bpw(spi); @@ -1635,8 +1647,6 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, goto out; } - spi->cur_xferlen = transfer->len; - dev_dbg(spi->dev, "transfer communication mode set to %d\n", spi->cur_comm); dev_dbg(spi->dev, From 9cc61973bf9385b19ff5dda4a2a7e265fcba85e4 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Mon, 10 Aug 2020 09:12:36 +0200 Subject: [PATCH 5/8] spi: stm32: fix stm32_spi_prepare_mbr in case of odd clk_rate Fix spi->clk_rate when it is odd to the nearest lowest even value because minimum SPI divider is 2. Signed-off-by: Amelie Delaunay Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1597043558-29668-4-git-send-email-alain.volmat@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index e5450233f3f8..571dea72bf7e 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -441,7 +441,8 @@ static int stm32_spi_prepare_mbr(struct stm32_spi *spi, u32 speed_hz, { u32 div, mbrdiv; - div = DIV_ROUND_UP(spi->clk_rate, speed_hz); + /* Ensure spi->clk_rate is even */ + div = DIV_ROUND_UP(spi->clk_rate & ~0x1, speed_hz); /* * SPI framework set xfer->speed_hz to master->max_speed_hz if From db96bf976a4fc65439be0b4524c0d41427d98814 Mon Sep 17 00:00:00 2001 From: Amelie Delaunay Date: Mon, 10 Aug 2020 09:12:37 +0200 Subject: [PATCH 6/8] spi: stm32: fixes suspend/resume management This patch adds pinctrl power management, and reconfigure spi controller in case of resume. Fixes: 038ac869c9d2 ("spi: stm32: add runtime PM support") Signed-off-by: Amelie Delaunay Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1597043558-29668-5-git-send-email-alain.volmat@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 571dea72bf7e..9b90a22543fd 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -2007,6 +2008,8 @@ static int stm32_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); + pinctrl_pm_select_sleep_state(&pdev->dev); + return 0; } @@ -2018,13 +2021,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); } @@ -2054,10 +2062,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 From 60ccb3515fc61a0124c70aa37317f75b67560024 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Mon, 10 Aug 2020 09:12:38 +0200 Subject: [PATCH 7/8] spi: stm32: always perform registers configuration prior to transfer SPI registers content may have been lost upon suspend/resume sequence. So, always compute and apply the necessary configuration in stm32_spi_transfer_one_setup routine. Signed-off-by: Alain Volmat Link: https://lore.kernel.org/r/1597043558-29668-6-git-send-email-alain.volmat@st.com Signed-off-by: Mark Brown --- drivers/spi/spi-stm32.c | 42 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 9b90a22543fd..d4b33b358a31 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -1597,41 +1597,33 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, unsigned long flags; unsigned int comm_type; int nb_words, ret = 0; + int mbr; spin_lock_irqsave(&spi->lock, flags); spi->cur_xferlen = transfer->len; - if (spi->cur_bpw != transfer->bits_per_word) { - spi->cur_bpw = transfer->bits_per_word; - spi->cfg->set_bpw(spi); + spi->cur_bpw = transfer->bits_per_word; + spi->cfg->set_bpw(spi); + + /* Update spi->cur_speed with real clock speed */ + mbr = stm32_spi_prepare_mbr(spi, transfer->speed_hz, + spi->cfg->baud_rate_div_min, + spi->cfg->baud_rate_div_max); + if (mbr < 0) { + ret = mbr; + goto out; } - if (spi->cur_speed != transfer->speed_hz) { - int mbr; - - /* Update spi->cur_speed with real clock speed */ - mbr = stm32_spi_prepare_mbr(spi, transfer->speed_hz, - spi->cfg->baud_rate_div_min, - spi->cfg->baud_rate_div_max); - if (mbr < 0) { - ret = mbr; - goto out; - } - - transfer->speed_hz = spi->cur_speed; - stm32_spi_set_mbr(spi, mbr); - } + transfer->speed_hz = spi->cur_speed; + stm32_spi_set_mbr(spi, mbr); comm_type = stm32_spi_communication_type(spi_dev, transfer); - if (spi->cur_comm != comm_type) { - ret = spi->cfg->set_mode(spi, comm_type); + ret = spi->cfg->set_mode(spi, comm_type); + if (ret < 0) + goto out; - if (ret < 0) - goto out; - - spi->cur_comm = comm_type; - } + spi->cur_comm = comm_type; if (spi->cfg->set_data_idleness) spi->cfg->set_data_idleness(spi, transfer->len); From 0454357faaf8eea07af4f01a9e7afde175377b41 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 7 Aug 2020 14:10:57 +0200 Subject: [PATCH 8/8] dt-bindings: lpspi: Add missing boolean type for fsl,spi-only-use-cs1-sel When running "make dt_binding_check" (even if restricted to an unrelated binding document using DT_SCHEMA_FILES=...): Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml: ignoring, error in schema: properties: fsl,spi-only-use-cs1-sel warning: no schema found in file: Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml Fix this by adding a proper type definition for the vendor-specific fsl,spi-only-use-cs1-sel property. Fixes: 7ac9bbf6ab3085c2 ("dt-bindings: lpspi: New property in document DT bindings for LPSPI") Suggested-by: Rob Herring Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200807121057.14204-1-geert+renesas@glider.be Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml index 22882e769e26..312d8fee9dbb 100644 --- a/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml +++ b/Documentation/devicetree/bindings/spi/spi-fsl-lpspi.yaml @@ -39,6 +39,7 @@ properties: spi common code does not support use of CS signals discontinuously. i.MX8DXL-EVK board only uses CS1 without using CS0. Therefore, add this property to re-config the chipselect value in the LPSPI driver. + type: boolean required: - compatible