Skip to content

Commit 18f78b5

Browse files
committed
spi: axi-spi-engine: improvements round 2
Merge series from David Lechner <dlechner@baylibre.com>: We are working towards adding support for the offload feature [1] of the AXI SPI Engine IP core. Before we can do that, we want to make some general fixes and improvements to the driver. In order to avoid a giant series with 35+ patches, we are splitting this up into a few smaller series. This is a continuation of the work started in [2] which has been applied to spi/for-6.8 [3]. This series must be applied on top of that series to apply cleanly. Once this series is applied, we will follow up with the 3rd series that implements the offload support. The offload support will also involve the IIO subsystem (a new IIO driver will depend on the new SPI offload feature), so I'm mentioning this now in case we want to do anything ahead of time to prepare for that (e.g. putting all of these changes on a separate branch). [1]: https://wiki.analog.com/resources/fpga/peripherals/spi_engine/offload [2]: https://lore.kernel.org/linux-spi/20231117-axi-spi-engine-series-1-v1-0-cc59db999b87@baylibre.com/ [3]: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git/log/?h=for-6.8
2 parents 5cb4751 + 07d33c2 commit 18f78b5

1 file changed

Lines changed: 77 additions & 39 deletions

File tree

drivers/spi/spi-axi-spi-engine.c

Lines changed: 77 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/module.h>
1414
#include <linux/platform_device.h>
1515
#include <linux/spi/spi.h>
16+
#include <linux/timer.h>
1617

1718
#define SPI_ENGINE_VERSION_MAJOR(x) ((x >> 16) & 0xff)
1819
#define SPI_ENGINE_VERSION_MINOR(x) ((x >> 8) & 0xff)
@@ -114,6 +115,8 @@ struct spi_engine {
114115

115116
void __iomem *base;
116117
struct ida sync_ida;
118+
struct timer_list watchdog_timer;
119+
struct spi_controller *controller;
117120

118121
unsigned int int_enable;
119122
};
@@ -140,21 +143,6 @@ static unsigned int spi_engine_get_config(struct spi_device *spi)
140143
return config;
141144
}
142145

143-
static unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine,
144-
struct spi_device *spi, struct spi_transfer *xfer)
145-
{
146-
unsigned int clk_div;
147-
148-
clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk),
149-
xfer->speed_hz * 2);
150-
if (clk_div > 255)
151-
clk_div = 255;
152-
else if (clk_div > 0)
153-
clk_div -= 1;
154-
155-
return clk_div;
156-
}
157-
158146
static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
159147
struct spi_transfer *xfer)
160148
{
@@ -183,22 +171,16 @@ static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
183171
}
184172

185173
static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry,
186-
struct spi_engine *spi_engine, unsigned int clk_div,
187-
struct spi_transfer *xfer)
174+
int delay_ns, u32 sclk_hz)
188175
{
189-
unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk);
190176
unsigned int t;
191-
int delay;
192-
193-
delay = spi_delay_to_ns(&xfer->delay, xfer);
194-
if (delay < 0)
195-
return;
196-
delay /= 1000;
197177

198-
if (delay == 0)
178+
/* negative delay indicates error, e.g. from spi_delay_to_ns() */
179+
if (delay_ns <= 0)
199180
return;
200181

201-
t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2);
182+
/* rounding down since executing the instruction adds a couple of ticks delay */
183+
t = DIV_ROUND_DOWN_ULL((u64)delay_ns * sclk_hz, NSEC_PER_SEC);
202184
while (t) {
203185
unsigned int n = min(t, 256U);
204186

@@ -215,19 +197,41 @@ static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry,
215197
if (assert)
216198
mask ^= BIT(spi_get_chipselect(spi, 0));
217199

218-
spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask));
200+
spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(0, mask));
219201
}
220202

221-
static int spi_engine_compile_message(struct spi_engine *spi_engine,
222-
struct spi_message *msg, bool dry, struct spi_engine_program *p)
203+
/*
204+
* Performs precompile steps on the message.
205+
*
206+
* The SPI core does most of the message/transfer validation and filling in
207+
* fields for us via __spi_validate(). This fixes up anything remaining not
208+
* done there.
209+
*
210+
* NB: This is separate from spi_engine_compile_message() because the latter
211+
* is called twice and would otherwise result in double-evaluation.
212+
*/
213+
static void spi_engine_precompile_message(struct spi_message *msg)
214+
{
215+
unsigned int clk_div, max_hz = msg->spi->controller->max_speed_hz;
216+
struct spi_transfer *xfer;
217+
218+
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
219+
clk_div = DIV_ROUND_UP(max_hz, xfer->speed_hz);
220+
xfer->effective_speed_hz = max_hz / min(clk_div, 256U);
221+
}
222+
}
223+
224+
static void spi_engine_compile_message(struct spi_message *msg, bool dry,
225+
struct spi_engine_program *p)
223226
{
224227
struct spi_device *spi = msg->spi;
228+
struct spi_controller *host = spi->controller;
225229
struct spi_transfer *xfer;
226230
int clk_div, new_clk_div;
227231
bool keep_cs = false;
228232
u8 bits_per_word = 0;
229233

230-
clk_div = -1;
234+
clk_div = 1;
231235

232236
spi_engine_program_add_cmd(p, dry,
233237
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG,
@@ -237,12 +241,13 @@ static int spi_engine_compile_message(struct spi_engine *spi_engine,
237241
spi_engine_gen_cs(p, dry, spi, !xfer->cs_off);
238242

239243
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
240-
new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer);
244+
new_clk_div = host->max_speed_hz / xfer->effective_speed_hz;
241245
if (new_clk_div != clk_div) {
242246
clk_div = new_clk_div;
247+
/* actual divider used is register value + 1 */
243248
spi_engine_program_add_cmd(p, dry,
244249
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV,
245-
clk_div));
250+
clk_div - 1));
246251
}
247252

248253
if (bits_per_word != xfer->bits_per_word) {
@@ -253,7 +258,8 @@ static int spi_engine_compile_message(struct spi_engine *spi_engine,
253258
}
254259

255260
spi_engine_gen_xfer(p, dry, xfer);
256-
spi_engine_gen_sleep(p, dry, spi_engine, clk_div, xfer);
261+
spi_engine_gen_sleep(p, dry, spi_delay_to_ns(&xfer->delay, xfer),
262+
xfer->effective_speed_hz);
257263

258264
if (xfer->cs_change) {
259265
if (list_is_last(&xfer->transfer_list, &msg->transfers)) {
@@ -262,6 +268,10 @@ static int spi_engine_compile_message(struct spi_engine *spi_engine,
262268
if (!xfer->cs_off)
263269
spi_engine_gen_cs(p, dry, spi, false);
264270

271+
spi_engine_gen_sleep(p, dry, spi_delay_to_ns(
272+
&xfer->cs_change_delay, xfer),
273+
xfer->effective_speed_hz);
274+
265275
if (!list_next_entry(xfer, transfer_list)->cs_off)
266276
spi_engine_gen_cs(p, dry, spi, true);
267277
}
@@ -274,7 +284,13 @@ static int spi_engine_compile_message(struct spi_engine *spi_engine,
274284
if (!keep_cs)
275285
spi_engine_gen_cs(p, dry, spi, false);
276286

277-
return 0;
287+
/*
288+
* Restore clockdiv to default so that future gen_sleep commands don't
289+
* have to be aware of the current register state.
290+
*/
291+
if (clk_div != 1)
292+
spi_engine_program_add_cmd(p, dry,
293+
SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV, 0));
278294
}
279295

280296
static void spi_engine_xfer_next(struct spi_message *msg,
@@ -475,9 +491,11 @@ static irqreturn_t spi_engine_irq(int irq, void *devid)
475491
struct spi_engine_message_state *st = msg->state;
476492

477493
if (completed_id == st->sync_id) {
478-
msg->status = 0;
479-
msg->actual_length = msg->frame_length;
480-
spi_finalize_current_message(host);
494+
if (timer_delete_sync(&spi_engine->watchdog_timer)) {
495+
msg->status = 0;
496+
msg->actual_length = msg->frame_length;
497+
spi_finalize_current_message(host);
498+
}
481499
disable_int |= SPI_ENGINE_INT_SYNC;
482500
}
483501
}
@@ -506,8 +524,10 @@ static int spi_engine_prepare_message(struct spi_controller *host,
506524
if (!st)
507525
return -ENOMEM;
508526

527+
spi_engine_precompile_message(msg);
528+
509529
p_dry.length = 0;
510-
spi_engine_compile_message(spi_engine, msg, true, &p_dry);
530+
spi_engine_compile_message(msg, true, &p_dry);
511531

512532
size = sizeof(*p->instructions) * (p_dry.length + 1);
513533
p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
@@ -525,7 +545,7 @@ static int spi_engine_prepare_message(struct spi_controller *host,
525545

526546
st->sync_id = ret;
527547

528-
spi_engine_compile_message(spi_engine, msg, false, p);
548+
spi_engine_compile_message(msg, false, p);
529549

530550
spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id));
531551

@@ -558,6 +578,8 @@ static int spi_engine_transfer_one_message(struct spi_controller *host,
558578
unsigned int int_enable = 0;
559579
unsigned long flags;
560580

581+
mod_timer(&spi_engine->watchdog_timer, jiffies + msecs_to_jiffies(5000));
582+
561583
spin_lock_irqsave(&spi_engine->lock, flags);
562584

563585
if (spi_engine_write_cmd_fifo(spi_engine, msg))
@@ -581,6 +603,20 @@ static int spi_engine_transfer_one_message(struct spi_controller *host,
581603
return 0;
582604
}
583605

606+
static void spi_engine_timeout(struct timer_list *timer)
607+
{
608+
struct spi_engine *spi_engine = from_timer(spi_engine, timer, watchdog_timer);
609+
struct spi_controller *host = spi_engine->controller;
610+
611+
if (WARN_ON(!host->cur_msg))
612+
return;
613+
614+
dev_err(&host->dev,
615+
"Timeout occurred while waiting for transfer to complete. Hardware is probably broken.\n");
616+
host->cur_msg->status = -ETIMEDOUT;
617+
spi_finalize_current_message(host);
618+
}
619+
584620
static void spi_engine_release_hw(void *p)
585621
{
586622
struct spi_engine *spi_engine = p;
@@ -610,6 +646,8 @@ static int spi_engine_probe(struct platform_device *pdev)
610646

611647
spin_lock_init(&spi_engine->lock);
612648
ida_init(&spi_engine->sync_ida);
649+
timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout, TIMER_IRQSAFE);
650+
spi_engine->controller = host;
613651

614652
spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
615653
if (IS_ERR(spi_engine->clk))

0 commit comments

Comments
 (0)