Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Charles Keepax and committed by
Mark Brown
e92e25f7 0723affa

+76 -1
+7
include/sound/sdca_fdl.h
··· 11 11 #define __SDCA_FDL_H__ 12 12 13 13 #include <linux/completion.h> 14 + #include <linux/workqueue.h> 14 15 15 16 struct device; 16 17 struct regmap; ··· 24 23 * struct fdl_state - FDL state structure to keep data between interrupts 25 24 * @begin: Completion indicating the start of an FDL download cycle. 26 25 * @done: Completion indicating the end of an FDL download cycle. 26 + * @timeout: Delayed work used for timing out UMP transactions. 27 + * @lock: Mutex to protect between the timeout work and IRQ handlers. 28 + * @interrupt: Pointer to the interrupt struct to which this FDL is attached. 27 29 * @set: Pointer to the FDL set currently being downloaded. 28 30 * @file_index: Index of the current file being processed. 29 31 */ 30 32 struct fdl_state { 31 33 struct completion begin; 32 34 struct completion done; 35 + struct delayed_work timeout; 36 + struct mutex lock; 33 37 38 + struct sdca_interrupt *interrupt; 34 39 struct sdca_fdl_set *set; 35 40 int file_index; 36 41 };
+5
include/sound/sdca_ump.h
··· 15 15 struct sdca_entity; 16 16 struct sdca_function_data; 17 17 struct snd_soc_component; 18 + struct delayed_work; 18 19 19 20 int sdca_ump_get_owner_host(struct device *dev, 20 21 struct regmap *function_regmap, ··· 42 41 unsigned int offset_sel, unsigned int msg_offset, 43 42 unsigned int length_sel, 44 43 void *msg, int msg_len); 44 + 45 + void sdca_ump_cancel_timeout(struct delayed_work *work); 46 + void sdca_ump_schedule_timeout(struct delayed_work *work, 47 + unsigned int timeout_us); 45 48 46 49 #endif // __SDCA_UMP_H__
+49 -1
sound/soc/sdca/sdca_fdl.c
··· 116 116 done_timeout); 117 117 if (!time) { 118 118 dev_err(dev, "timed out waiting for FDL to complete\n"); 119 - return -ETIMEDOUT; 119 + goto error; 120 120 } 121 121 } 122 122 ··· 125 125 } 126 126 127 127 dev_err(dev, "too many FDL requests\n"); 128 + 129 + error: 130 + for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) { 131 + struct sdca_interrupt *interrupt = &info->irqs[j]; 132 + struct fdl_state *fdl_state; 133 + 134 + if (interrupt->function != function || 135 + !interrupt->entity || !interrupt->control || 136 + interrupt->entity->type != SDCA_ENTITY_TYPE_XU || 137 + interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER) 138 + continue; 139 + 140 + disable_irq(interrupt->irq); 141 + 142 + fdl_state = interrupt->priv; 143 + 144 + sdca_ump_cancel_timeout(&fdl_state->timeout); 145 + } 146 + 128 147 return -ETIMEDOUT; 129 148 } 130 149 EXPORT_SYMBOL_NS_GPL(sdca_fdl_sync, "SND_SOC_SDCA"); ··· 321 302 dev_dbg(interrupt->dev, "completed FDL process\n"); 322 303 } 323 304 305 + static void sdca_fdl_timeout_work(struct work_struct *work) 306 + { 307 + struct fdl_state *fdl_state = container_of(work, struct fdl_state, 308 + timeout.work); 309 + struct sdca_interrupt *interrupt = fdl_state->interrupt; 310 + struct device *dev = interrupt->dev; 311 + 312 + dev_err(dev, "FDL transaction timed out\n"); 313 + 314 + guard(mutex)(&fdl_state->lock); 315 + 316 + fdl_end(interrupt); 317 + sdca_reset_function(dev, interrupt->function, interrupt->function_regmap); 318 + } 319 + 324 320 static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int status) 325 321 { 326 322 struct fdl_state *fdl_state = interrupt->priv; ··· 398 364 { 399 365 struct device *dev = interrupt->dev; 400 366 struct sdca_entity_xu *xu = &interrupt->entity->xu; 367 + struct fdl_state *fdl_state = interrupt->priv; 401 368 unsigned int reg, status; 402 369 int response, ret; 370 + 371 + guard(mutex)(&fdl_state->lock); 403 372 404 373 ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap, 405 374 interrupt->function, interrupt->entity, 406 375 interrupt->control); 407 376 if (ret) 408 377 goto reset_function; 378 + 379 + sdca_ump_cancel_timeout(&fdl_state->timeout); 409 380 410 381 reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, 411 382 SDCA_CTL_XU_FDL_STATUS, 0); ··· 454 415 case SDCA_XU_RESET_FUNCTION: 455 416 goto reset_function; 456 417 } 418 + case SDCA_CTL_XU_FDLH_COMPLETE: 419 + if (status & SDCA_CTL_XU_FDLD_REQ_ABORT || 420 + status == SDCA_CTL_XU_FDLD_COMPLETE) 421 + return 0; 422 + fallthrough; 457 423 default: 424 + sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay); 458 425 return 0; 459 426 } 460 427 ··· 486 441 if (!fdl_state) 487 442 return -ENOMEM; 488 443 444 + INIT_DELAYED_WORK(&fdl_state->timeout, sdca_fdl_timeout_work); 489 445 init_completion(&fdl_state->begin); 490 446 init_completion(&fdl_state->done); 447 + mutex_init(&fdl_state->lock); 448 + fdl_state->interrupt = interrupt; 491 449 492 450 interrupt->priv = fdl_state; 493 451
+15
sound/soc/sdca/sdca_ump.c
··· 245 245 return 0; 246 246 } 247 247 EXPORT_SYMBOL_NS_GPL(sdca_ump_write_message, "SND_SOC_SDCA"); 248 + 249 + void sdca_ump_cancel_timeout(struct delayed_work *work) 250 + { 251 + cancel_delayed_work_sync(work); 252 + } 253 + EXPORT_SYMBOL_NS_GPL(sdca_ump_cancel_timeout, "SND_SOC_SDCA"); 254 + 255 + void sdca_ump_schedule_timeout(struct delayed_work *work, unsigned int timeout_us) 256 + { 257 + if (!timeout_us) 258 + return; 259 + 260 + queue_delayed_work(system_wq, work, usecs_to_jiffies(timeout_us)); 261 + } 262 + EXPORT_SYMBOL_NS_GPL(sdca_ump_schedule_timeout, "SND_SOC_SDCA");