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 /sound | |
| 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>
Diffstat (limited to 'sound')
| -rw-r--r-- | sound/soc/sdca/sdca_fdl.c | 50 | ||||
| -rw-r--r-- | sound/soc/sdca/sdca_ump.c | 15 |
2 files changed, 64 insertions, 1 deletions
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"); |
