Skip to content

Commit 30c4130

Browse files
ananglfabiobaltieri
authored andcommitted
drivers: mspi_dw: Add option to offload handling of FIFOs
Provide a Kconfig option that allows offloading handling of the SSI FIFOs, which the driver normally executes in the interrupt handler, to the system workqueue. Signed-off-by: Andrzej Głąbek <[email protected]>
1 parent 9a6dba9 commit 30c4130

File tree

2 files changed

+91
-18
lines changed

2 files changed

+91
-18
lines changed

drivers/mspi/Kconfig.dw

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ config MSPI_DW
1212

1313
if MSPI_DW
1414

15+
config MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE
16+
bool "Handle FIFO in system workqueue"
17+
help
18+
When the driver does not use DMA for transferring data to/from the
19+
SSI FIFOs, handling of those may take a significant amount of time.
20+
It may be destructive for some applications if that time is spent
21+
in the interrupt context. This option allows offloading the job to
22+
the system workqueue.
23+
1524
config MSPI_DW_TXD_DIV
1625
int "Designware SSI TX Drive edge divisor"
1726
default 4

drivers/mspi/mspi_dw.c

Lines changed: 82 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ struct mspi_dw_data {
7171
/* For locking of controller configuration. */
7272
struct k_sem cfg_lock;
7373
struct mspi_xfer xfer;
74+
75+
#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE)
76+
struct k_work fifo_work;
77+
const struct device *dev;
78+
uint32_t imr;
79+
#endif
7480
};
7581

7682
struct mspi_dw_config {
@@ -111,7 +117,7 @@ DEFINE_MM_REG_RD_WR(rxftlr, 0x1c)
111117
DEFINE_MM_REG_RD(txflr, 0x20)
112118
DEFINE_MM_REG_RD(rxflr, 0x24)
113119
DEFINE_MM_REG_RD(sr, 0x28)
114-
DEFINE_MM_REG_WR(imr, 0x2c)
120+
DEFINE_MM_REG_RD_WR(imr, 0x2c)
115121
DEFINE_MM_REG_RD(isr, 0x30)
116122
DEFINE_MM_REG_RD(risr, 0x34)
117123
DEFINE_MM_REG_RD_WR(dr, 0x60)
@@ -218,6 +224,8 @@ static bool tx_dummy_bytes(const struct device *dev)
218224
write_dr(dev, dummy_val);
219225
} while (--dummy_bytes);
220226

227+
dev_data->dummy_bytes = 0;
228+
221229
/* Set the TX start level to 0, so that the transmission will be
222230
* started now if it hasn't been yet. The threshold value is also
223231
* set to 0 here, but it doesn't really matter, as the interrupt
@@ -280,7 +288,18 @@ static bool read_rx_fifo(const struct device *dev,
280288
return false;
281289
}
282290

283-
static void mspi_dw_isr(const struct device *dev)
291+
static inline void set_imr(const struct device *dev, uint32_t imr)
292+
{
293+
#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE)
294+
struct mspi_dw_data *dev_data = dev->data;
295+
296+
dev_data->imr = imr;
297+
#else
298+
write_imr(dev, imr);
299+
#endif
300+
}
301+
302+
static void handle_fifos(const struct device *dev)
284303
{
285304
struct mspi_dw_data *dev_data = dev->data;
286305
const struct mspi_xfer_packet *packet =
@@ -302,42 +321,82 @@ static void mspi_dw_isr(const struct device *dev)
302321
finished = true;
303322
}
304323
} else {
305-
uint32_t int_status = read_isr(dev);
324+
for (;;) {
325+
/* Use RISR, not ISR, because when this function is
326+
* executed through the system workqueue, all interrupts
327+
* are masked (IMR is 0).
328+
*/
329+
uint32_t int_status = read_risr(dev);
306330

307-
do {
308-
if (int_status & ISR_RXFIS_BIT) {
331+
if (int_status & RISR_RXFIR_BIT) {
309332
if (read_rx_fifo(dev, packet)) {
310333
finished = true;
311334
break;
312335
}
313336

314-
if (read_risr(dev) & RISR_RXOIR_BIT) {
337+
if (int_status & RISR_RXOIR_BIT) {
315338
finished = true;
316339
break;
317340
}
318341

319-
int_status = read_isr(dev);
342+
/* Refresh interrupt status, as during reading
343+
* from the RX FIFO, the TX FIFO status might
344+
* have changed.
345+
*/
346+
int_status = read_risr(dev);
320347
}
321348

322-
if (int_status & ISR_TXEIS_BIT) {
323-
if (tx_dummy_bytes(dev)) {
324-
/* All the required dummy bytes were
325-
* written to the FIFO; disable the TXE
326-
* interrupt, as it's no longer needed.
327-
*/
328-
write_imr(dev, IMR_RXFIM_BIT);
329-
}
349+
if (dev_data->dummy_bytes == 0 ||
350+
!(int_status & RISR_TXEIR_BIT)) {
351+
break;
352+
}
330353

331-
int_status = read_isr(dev);
354+
if (tx_dummy_bytes(dev)) {
355+
/* All the required dummy bytes were written
356+
* to the FIFO; disable the TXE interrupt,
357+
* as it's no longer needed.
358+
*/
359+
set_imr(dev, IMR_RXFIM_BIT);
332360
}
333-
} while (int_status != 0);
361+
}
334362
}
335363

336364
if (finished) {
337-
write_imr(dev, 0);
365+
set_imr(dev, 0);
338366

339367
k_sem_give(&dev_data->finished);
340368
}
369+
}
370+
371+
#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE)
372+
static void fifo_work_handler(struct k_work *work)
373+
{
374+
struct mspi_dw_data *dev_data =
375+
CONTAINER_OF(work, struct mspi_dw_data, fifo_work);
376+
const struct device *dev = dev_data->dev;
377+
378+
handle_fifos(dev);
379+
380+
write_imr(dev, dev_data->imr);
381+
}
382+
#endif
383+
384+
static void mspi_dw_isr(const struct device *dev)
385+
{
386+
#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE)
387+
struct mspi_dw_data *dev_data = dev->data;
388+
int rc;
389+
390+
dev_data->imr = read_imr(dev);
391+
write_imr(dev, 0);
392+
393+
rc = k_work_submit(&dev_data->fifo_work);
394+
if (rc < 0) {
395+
LOG_ERR("k_work_submit failed: %d\n", rc);
396+
}
397+
#else
398+
handle_fifos(dev);
399+
#endif
341400

342401
vendor_specific_irq_clear(dev);
343402
}
@@ -1426,6 +1485,11 @@ static int dev_init(const struct device *dev)
14261485
k_sem_init(&dev_data->cfg_lock, 1, 1);
14271486
k_sem_init(&dev_data->ctx_lock, 1, 1);
14281487

1488+
#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE)
1489+
dev_data->dev = dev;
1490+
k_work_init(&dev_data->fifo_work, fifo_work_handler);
1491+
#endif
1492+
14291493
for (ce_gpio = dev_config->ce_gpios;
14301494
ce_gpio < &dev_config->ce_gpios[dev_config->ce_gpios_len];
14311495
ce_gpio++) {

0 commit comments

Comments
 (0)