diff options
| author | Charles Keepax <ckeepax@opensource.cirrus.com> | 2025-10-20 16:55:10 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2025-10-27 15:31:27 +0000 |
| commit | e92e25f777483b7cc3e170214cc84337d7a415cf (patch) | |
| tree | b02f821f36bd0b4692477b793c3595a9f5c7b4d1 | |
| parent | 0723affa1bee50c3bd7ca00e00dee07fcef224b8 (diff) | |
ASoC: SDCA: Add UMP timeout handling for FDL
Several of the UMP transactions in the FDL process should timeout if the
device does not respond within a certain time, add handling into the UMP
helpers and the FDL code to handle this.
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.dev>
Link: https://patch.msgid.link/20251020155512.353774-18-ckeepax@opensource.cirrus.com
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | include/sound/sdca_fdl.h | 7 | ||||
| -rw-r--r-- | include/sound/sdca_ump.h | 5 | ||||
| -rw-r--r-- | sound/soc/sdca/sdca_fdl.c | 50 | ||||
| -rw-r--r-- | sound/soc/sdca/sdca_ump.c | 15 |
4 files changed, 76 insertions, 1 deletions
diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h index 4ea000d6acef..f4ba809cb203 100644 --- a/include/sound/sdca_fdl.h +++ b/include/sound/sdca_fdl.h @@ -11,6 +11,7 @@ #define __SDCA_FDL_H__ #include <linux/completion.h> +#include <linux/workqueue.h> struct device; struct regmap; @@ -23,13 +24,19 @@ struct sdca_interrupt_info; * struct fdl_state - FDL state structure to keep data between interrupts * @begin: Completion indicating the start of an FDL download cycle. * @done: Completion indicating the end of an FDL download cycle. + * @timeout: Delayed work used for timing out UMP transactions. + * @lock: Mutex to protect between the timeout work and IRQ handlers. + * @interrupt: Pointer to the interrupt struct to which this FDL is attached. * @set: Pointer to the FDL set currently being downloaded. * @file_index: Index of the current file being processed. */ struct fdl_state { struct completion begin; struct completion done; + struct delayed_work timeout; + struct mutex lock; + struct sdca_interrupt *interrupt; struct sdca_fdl_set *set; int file_index; }; diff --git a/include/sound/sdca_ump.h b/include/sound/sdca_ump.h index b2363199d19a..f54f9d48c64c 100644 --- a/include/sound/sdca_ump.h +++ b/include/sound/sdca_ump.h @@ -15,6 +15,7 @@ struct sdca_control; struct sdca_entity; struct sdca_function_data; struct snd_soc_component; +struct delayed_work; int sdca_ump_get_owner_host(struct device *dev, struct regmap *function_regmap, @@ -42,4 +43,8 @@ int sdca_ump_write_message(struct device *dev, unsigned int length_sel, void *msg, int msg_len); +void sdca_ump_cancel_timeout(struct delayed_work *work); +void sdca_ump_schedule_timeout(struct delayed_work *work, + unsigned int timeout_us); + #endif // __SDCA_UMP_H__ diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 39298314f69c..cb79dc3131b8 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -116,7 +116,7 @@ int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, done_timeout); if (!time) { dev_err(dev, "timed out waiting for FDL to complete\n"); - return -ETIMEDOUT; + goto error; } } @@ -125,6 +125,25 @@ int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, } dev_err(dev, "too many FDL requests\n"); + +error: + for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) { + struct sdca_interrupt *interrupt = &info->irqs[j]; + struct fdl_state *fdl_state; + + if (interrupt->function != function || + !interrupt->entity || !interrupt->control || + interrupt->entity->type != SDCA_ENTITY_TYPE_XU || + interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER) + continue; + + disable_irq(interrupt->irq); + + fdl_state = interrupt->priv; + + sdca_ump_cancel_timeout(&fdl_state->timeout); + } + return -ETIMEDOUT; } EXPORT_SYMBOL_NS_GPL(sdca_fdl_sync, "SND_SOC_SDCA"); @@ -302,6 +321,21 @@ static void fdl_end(struct sdca_interrupt *interrupt) dev_dbg(interrupt->dev, "completed FDL process\n"); } +static void sdca_fdl_timeout_work(struct work_struct *work) +{ + struct fdl_state *fdl_state = container_of(work, struct fdl_state, + timeout.work); + struct sdca_interrupt *interrupt = fdl_state->interrupt; + struct device *dev = interrupt->dev; + + dev_err(dev, "FDL transaction timed out\n"); + + guard(mutex)(&fdl_state->lock); + + fdl_end(interrupt); + sdca_reset_function(dev, interrupt->function, interrupt->function_regmap); +} + static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int status) { struct fdl_state *fdl_state = interrupt->priv; @@ -364,15 +398,20 @@ int sdca_fdl_process(struct sdca_interrupt *interrupt) { struct device *dev = interrupt->dev; struct sdca_entity_xu *xu = &interrupt->entity->xu; + struct fdl_state *fdl_state = interrupt->priv; unsigned int reg, status; int response, ret; + guard(mutex)(&fdl_state->lock); + ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap, interrupt->function, interrupt->entity, interrupt->control); if (ret) goto reset_function; + sdca_ump_cancel_timeout(&fdl_state->timeout); + reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, SDCA_CTL_XU_FDL_STATUS, 0); ret = regmap_read(interrupt->function_regmap, reg, &status); @@ -415,7 +454,13 @@ int sdca_fdl_process(struct sdca_interrupt *interrupt) case SDCA_XU_RESET_FUNCTION: goto reset_function; } + case SDCA_CTL_XU_FDLH_COMPLETE: + if (status & SDCA_CTL_XU_FDLD_REQ_ABORT || + status == SDCA_CTL_XU_FDLD_COMPLETE) + return 0; + fallthrough; default: + sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay); return 0; } @@ -441,8 +486,11 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) if (!fdl_state) return -ENOMEM; + INIT_DELAYED_WORK(&fdl_state->timeout, sdca_fdl_timeout_work); init_completion(&fdl_state->begin); init_completion(&fdl_state->done); + mutex_init(&fdl_state->lock); + fdl_state->interrupt = interrupt; interrupt->priv = fdl_state; diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c index 5dcad2f7ea05..8aba3ff16872 100644 --- a/sound/soc/sdca/sdca_ump.c +++ b/sound/soc/sdca/sdca_ump.c @@ -245,3 +245,18 @@ int sdca_ump_write_message(struct device *dev, return 0; } EXPORT_SYMBOL_NS_GPL(sdca_ump_write_message, "SND_SOC_SDCA"); + +void sdca_ump_cancel_timeout(struct delayed_work *work) +{ + cancel_delayed_work_sync(work); +} +EXPORT_SYMBOL_NS_GPL(sdca_ump_cancel_timeout, "SND_SOC_SDCA"); + +void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_us) +{ + if (!timeout_us) + return; + + queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us)); +} +EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA"); |