From 2e0d75f8dd9e31b3fb175f780494dd7dd988ceae Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:27 -0600 Subject: spi: axi-spi-engine: return void from spi_engine_compile_message() In the AXI SPI Engine driver, the spi_engine_compile_message() function does not return any error and none of the callers check the return value. So we can change the return type to void and drop the return 0. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-1-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index cbca783830ea..982b37ac3063 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -218,7 +218,7 @@ static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry, spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask)); } -static int spi_engine_compile_message(struct spi_engine *spi_engine, +static void spi_engine_compile_message(struct spi_engine *spi_engine, struct spi_message *msg, bool dry, struct spi_engine_program *p) { struct spi_device *spi = msg->spi; @@ -273,8 +273,6 @@ static int spi_engine_compile_message(struct spi_engine *spi_engine, if (!keep_cs) spi_engine_gen_cs(p, dry, spi, false); - - return 0; } static void spi_engine_xfer_next(struct spi_message *msg, -- cgit From 9d023ecc31859c7f7c8ca27b5fec52b2dbb8086f Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:28 -0600 Subject: spi: axi-spi-engine: populate xfer->effective_speed_hz This adds a new spi_engine_precompile_message() function to the ADI AXI SPI Engine driver to populate the xfer->effective_speed_hz field since the SPI core doesn't/can't do this for us. This driver is already using spi_delay_to_ns() which depends on effective_speed_hz to get an accurate value in some cases. Having an effective_speed_hz value can also be used in future changes to simplify other code. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-2-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 982b37ac3063..ee7b904ae5cf 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -218,6 +218,27 @@ static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry, spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask)); } +/* + * Performs precompile steps on the message. + * + * The SPI core does most of the message/transfer validation and filling in + * fields for us via __spi_validate(). This fixes up anything remaining not + * done there. + * + * NB: This is separate from spi_engine_compile_message() because the latter + * is called twice and would otherwise result in double-evaluation. + */ +static void spi_engine_precompile_message(struct spi_message *msg) +{ + unsigned int clk_div, max_hz = msg->spi->controller->max_speed_hz; + struct spi_transfer *xfer; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + clk_div = DIV_ROUND_UP(max_hz, xfer->speed_hz); + xfer->effective_speed_hz = max_hz / min(clk_div, 256U); + } +} + static void spi_engine_compile_message(struct spi_engine *spi_engine, struct spi_message *msg, bool dry, struct spi_engine_program *p) { @@ -504,6 +525,8 @@ static int spi_engine_prepare_message(struct spi_controller *host, if (!st) return -ENOMEM; + spi_engine_precompile_message(msg); + p_dry.length = 0; spi_engine_compile_message(spi_engine, msg, true, &p_dry); -- cgit From 1fc8dc5721bbc7a21cb4cc60c35eb8031942542b Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:29 -0600 Subject: spi: axi-spi-engine: remove spi_engine_get_clk_div() Now that host->max_speed_hz and xfer->effective_speed_hz are properly set, we can use them instead of having to do more complex calculations to get the clock divider for each transfer. This removes the spi_engine_get_clk_div() function and replaces it with just dividing the two clock rates. Since the hardware register value is the divider minus one, we need to subtract one. Subtracting one was previously done in the spi_engine_get_clk_div() function. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-3-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index ee7b904ae5cf..fa2264d630c3 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -140,21 +140,6 @@ static unsigned int spi_engine_get_config(struct spi_device *spi) return config; } -static unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine, - struct spi_device *spi, struct spi_transfer *xfer) -{ - unsigned int clk_div; - - clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk), - xfer->speed_hz * 2); - if (clk_div > 255) - clk_div = 255; - else if (clk_div > 0) - clk_div -= 1; - - return clk_div; -} - static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry, struct spi_transfer *xfer) { @@ -243,6 +228,7 @@ static void spi_engine_compile_message(struct spi_engine *spi_engine, struct spi_message *msg, bool dry, struct spi_engine_program *p) { struct spi_device *spi = msg->spi; + struct spi_controller *host = spi->controller; struct spi_transfer *xfer; int clk_div, new_clk_div; bool keep_cs = false; @@ -258,12 +244,13 @@ static void spi_engine_compile_message(struct spi_engine *spi_engine, spi_engine_gen_cs(p, dry, spi, !xfer->cs_off); list_for_each_entry(xfer, &msg->transfers, transfer_list) { - new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer); + new_clk_div = host->max_speed_hz / xfer->effective_speed_hz; if (new_clk_div != clk_div) { clk_div = new_clk_div; + /* actual divider used is register value + 1 */ spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV, - clk_div)); + clk_div - 1)); } if (bits_per_word != xfer->bits_per_word) { @@ -274,7 +261,7 @@ static void spi_engine_compile_message(struct spi_engine *spi_engine, } spi_engine_gen_xfer(p, dry, xfer); - spi_engine_gen_sleep(p, dry, spi_engine, clk_div, xfer); + spi_engine_gen_sleep(p, dry, spi_engine, clk_div - 1, xfer); if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, &msg->transfers)) { -- cgit From be9070bcf67057b7b03c5acc1980d3897448ad20 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:30 -0600 Subject: spi: axi-spi-engine: fix sleep ticks calculation This fixes the sleep ticks calculation when generating sleep instructions in the AXI SPI Engine driver. The previous calculation was ignoring delays less than one microsecond and missed a microsecond to second conversion factor. This fixes the first issue by not rounding to microseconds. Now that xfer->effective_speed_hz is guaranteed to be set correctly, we can use that to simplify the calculation. This new calculation replaces the old incorrect math. Also add unit suffix to the delay variable for clarity while we are touching this. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-4-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index fa2264d630c3..b3e72308fcc5 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -168,22 +168,17 @@ static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry, } static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry, - struct spi_engine *spi_engine, unsigned int clk_div, struct spi_transfer *xfer) { - unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk); unsigned int t; - int delay; + int delay_ns; - delay = spi_delay_to_ns(&xfer->delay, xfer); - if (delay < 0) + delay_ns = spi_delay_to_ns(&xfer->delay, xfer); + if (delay_ns <= 0) return; - delay /= 1000; - if (delay == 0) - return; - - t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2); + /* rounding down since executing the instruction adds a couple of ticks delay */ + t = DIV_ROUND_DOWN_ULL((u64)delay_ns * xfer->effective_speed_hz, NSEC_PER_SEC); while (t) { unsigned int n = min(t, 256U); @@ -224,8 +219,8 @@ static void spi_engine_precompile_message(struct spi_message *msg) } } -static void spi_engine_compile_message(struct spi_engine *spi_engine, - struct spi_message *msg, bool dry, struct spi_engine_program *p) +static void spi_engine_compile_message(struct spi_message *msg, bool dry, + struct spi_engine_program *p) { struct spi_device *spi = msg->spi; struct spi_controller *host = spi->controller; @@ -261,7 +256,7 @@ static void spi_engine_compile_message(struct spi_engine *spi_engine, } spi_engine_gen_xfer(p, dry, xfer); - spi_engine_gen_sleep(p, dry, spi_engine, clk_div - 1, xfer); + spi_engine_gen_sleep(p, dry, xfer); if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, &msg->transfers)) { @@ -515,7 +510,7 @@ static int spi_engine_prepare_message(struct spi_controller *host, spi_engine_precompile_message(msg); p_dry.length = 0; - spi_engine_compile_message(spi_engine, msg, true, &p_dry); + spi_engine_compile_message(msg, true, &p_dry); size = sizeof(*p->instructions) * (p_dry.length + 1); p = kzalloc(sizeof(*p) + size, GFP_KERNEL); @@ -533,7 +528,7 @@ static int spi_engine_prepare_message(struct spi_controller *host, st->sync_id = ret; - spi_engine_compile_message(spi_engine, msg, false, p); + spi_engine_compile_message(msg, false, p); spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id)); -- cgit From e006c181dd9ab006d7b0982d35ef7951fbffe825 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:31 -0600 Subject: spi: axi-spi-engine: remove xfer arg from spi_engine_gen_sleep() This replaces the xfer parameter of spi_engine_gen_sleep() in the AXI SPI Engine driver with parameters for the delay in nanoseconds and the SPI SCLK rate. This will allow this function to be used by callers in the future that do not have a spi_transfer struct. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-5-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index b3e72308fcc5..84ec37732d8b 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -168,17 +168,16 @@ static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry, } static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry, - struct spi_transfer *xfer) + int delay_ns, u32 sclk_hz) { unsigned int t; - int delay_ns; - delay_ns = spi_delay_to_ns(&xfer->delay, xfer); + /* negative delay indicates error, e.g. from spi_delay_to_ns() */ if (delay_ns <= 0) return; /* rounding down since executing the instruction adds a couple of ticks delay */ - t = DIV_ROUND_DOWN_ULL((u64)delay_ns * xfer->effective_speed_hz, NSEC_PER_SEC); + t = DIV_ROUND_DOWN_ULL((u64)delay_ns * sclk_hz, NSEC_PER_SEC); while (t) { unsigned int n = min(t, 256U); @@ -256,7 +255,8 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, } spi_engine_gen_xfer(p, dry, xfer); - spi_engine_gen_sleep(p, dry, xfer); + spi_engine_gen_sleep(p, dry, spi_delay_to_ns(&xfer->delay, xfer), + xfer->effective_speed_hz); if (xfer->cs_change) { if (list_is_last(&xfer->transfer_list, &msg->transfers)) { -- cgit From 125a8390995df1a350e9e16e6da11d010e1e7f76 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:32 -0600 Subject: spi: axi-spi-engine: implement xfer->cs_change_delay This adds handling of xfer->cs_change_delay to the AXI SPI Engine driver. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-6-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 84ec37732d8b..3437829ef8b1 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -265,6 +265,10 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, if (!xfer->cs_off) spi_engine_gen_cs(p, dry, spi, false); + spi_engine_gen_sleep(p, dry, spi_delay_to_ns( + &xfer->cs_change_delay, xfer), + xfer->effective_speed_hz); + if (!list_next_entry(xfer, transfer_list)->cs_off) spi_engine_gen_cs(p, dry, spi, true); } -- cgit From 3106edac599f59e1298b034a19a43e7da002fccc Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:33 -0600 Subject: spi: axi-spi-engine: restore clkdiv at end of message This modifies the ADI AXI SPI Engine driver to restore the clkdiv configuration register at the end of a SPI message. Having the clkdiv in a known state is needed to be able to add a new command in the future that only performs a delay without any SPI transfers. Furthermore having that state be the smallest possible divider will allow these delays to have the highest possible precision. Changing the initial value of clk_div from -1 to 1 is now possible because we know the function will always be called with a known clkdiv config register state. Making this change will also have the effect of not emitting a clkdiv configuration register instruction in cases where the maximum sclk rate is used. Having one less instruction to process reduces delays on the bus which will be beneficial when we implement offload support to enable reading data from devices at very high rates. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-7-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 3437829ef8b1..3798f96da586 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -228,7 +228,7 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, bool keep_cs = false; u8 bits_per_word = 0; - clk_div = -1; + clk_div = 1; spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, @@ -280,6 +280,14 @@ static void spi_engine_compile_message(struct spi_message *msg, bool dry, if (!keep_cs) spi_engine_gen_cs(p, dry, spi, false); + + /* + * Restore clockdiv to default so that future gen_sleep commands don't + * have to be aware of the current register state. + */ + if (clk_div != 1) + spi_engine_program_add_cmd(p, dry, + SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV, 0)); } static void spi_engine_xfer_next(struct spi_message *msg, -- cgit From 0db60d821e485a1c9b8080dbec1ba9871efb6a65 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:34 -0600 Subject: spi: axi-spi-engine: remove delay from CS assertion Now that the AXI SPI Engine driver has support for the various CS delays requested through struct spi_message, we don't need to add a separate delay to the CS assertion instruction. Otherwise, we end up with longer than requested delays. Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-8-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 3798f96da586..78221715ba81 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -194,7 +194,7 @@ static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry, if (assert) mask ^= BIT(spi_get_chipselect(spi, 0)); - spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask)); + spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(0, mask)); } /* -- cgit From 07d33c2810bb5fe67747d11f76980ed68602e287 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 4 Dec 2023 11:33:35 -0600 Subject: spi: axi-spi-engine: add watchdog timer If there is an issue with the AXI SPI Engine hardware a scheduled transfer might never be completed and spi_sync() will block forever. This due to the uninterruptible wait for completion waiting for the spi_finalize_current_message() that never comes. Add a watchdog timer that will abort a transfer 5 seconds after it has been started. This will potentially leave the hardware in a broken state but it allows software to recover and allow to better diagnose the underlying issue. Co-developed-by: Lars-Peter Clausen Signed-off-by: Lars-Peter Clausen Signed-off-by: David Lechner Acked-by: Michael Hennerich Acked-by: Nuno Sa Link: https://lore.kernel.org/r/20231204-axi-spi-engine-series-2-v1-9-063672323fce@baylibre.com Signed-off-by: Mark Brown --- drivers/spi/spi-axi-spi-engine.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c index 78221715ba81..58280dd1c901 100644 --- a/drivers/spi/spi-axi-spi-engine.c +++ b/drivers/spi/spi-axi-spi-engine.c @@ -13,6 +13,7 @@ #include #include #include +#include #define SPI_ENGINE_VERSION_MAJOR(x) ((x >> 16) & 0xff) #define SPI_ENGINE_VERSION_MINOR(x) ((x >> 8) & 0xff) @@ -114,6 +115,8 @@ struct spi_engine { void __iomem *base; struct ida sync_ida; + struct timer_list watchdog_timer; + struct spi_controller *controller; unsigned int int_enable; }; @@ -488,9 +491,11 @@ static irqreturn_t spi_engine_irq(int irq, void *devid) struct spi_engine_message_state *st = msg->state; if (completed_id == st->sync_id) { - msg->status = 0; - msg->actual_length = msg->frame_length; - spi_finalize_current_message(host); + if (timer_delete_sync(&spi_engine->watchdog_timer)) { + msg->status = 0; + msg->actual_length = msg->frame_length; + spi_finalize_current_message(host); + } disable_int |= SPI_ENGINE_INT_SYNC; } } @@ -573,6 +578,8 @@ static int spi_engine_transfer_one_message(struct spi_controller *host, unsigned int int_enable = 0; unsigned long flags; + mod_timer(&spi_engine->watchdog_timer, jiffies + msecs_to_jiffies(5000)); + spin_lock_irqsave(&spi_engine->lock, flags); if (spi_engine_write_cmd_fifo(spi_engine, msg)) @@ -596,6 +603,20 @@ static int spi_engine_transfer_one_message(struct spi_controller *host, return 0; } +static void spi_engine_timeout(struct timer_list *timer) +{ + struct spi_engine *spi_engine = from_timer(spi_engine, timer, watchdog_timer); + struct spi_controller *host = spi_engine->controller; + + if (WARN_ON(!host->cur_msg)) + return; + + dev_err(&host->dev, + "Timeout occurred while waiting for transfer to complete. Hardware is probably broken.\n"); + host->cur_msg->status = -ETIMEDOUT; + spi_finalize_current_message(host); +} + static void spi_engine_release_hw(void *p) { struct spi_engine *spi_engine = p; @@ -625,6 +646,8 @@ static int spi_engine_probe(struct platform_device *pdev) spin_lock_init(&spi_engine->lock); ida_init(&spi_engine->sync_ida); + timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout, TIMER_IRQSAFE); + spi_engine->controller = host; spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); if (IS_ERR(spi_engine->clk)) -- cgit