From 81977f2bff2e80a0129145aff46e2d312cd38a5c Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Fri, 30 Aug 2024 15:34:07 +0300 Subject: [PATCH] drivers: dma: intel_adsp_hda: fix intel_adsp_hda_unused() check The ringbuffer availability check is subject to race with regards to update of BF (Buffer Full) and BNE (Buffer Not Empty) bits in DGCS register, and status of RP (Read Position) and WP (Write Position). Following sequence is observed without this patch when calling dma_get_status() on multiple Intel ADSP platforms: iter 154 pending 1536 RP 768 WP 768, BNE 1, BF 1 -> dma_reload for 384 iter 155 pending 1536 RP 1152 WP 1152, BNE 1, BF 1 -> dma_reload for 384 iter 156 pending 0 RP 0 WP 0, BNE 1, BF 0 Value of pending is not expected to go from 1536 to zero if only 384 bytes have been consumed via dma_reload() since last call to dma_get_status(). Change the logic to read DGCS register later, after the WP and RP have been already read, and only check the BNE bit if Read and Write Positions are equal. Link: https://github.com/thesofproject/sof/issues/9418 Signed-off-by: Kai Vehmanen Co-developed-by: Peter Ujfalusi Signed-off-by: Peter Ujfalusi --- .../common/include/intel_adsp_hda.h | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/soc/intel/intel_adsp/common/include/intel_adsp_hda.h b/soc/intel/intel_adsp/common/include/intel_adsp_hda.h index 1616d1cb67b2..277cf4d34301 100644 --- a/soc/intel/intel_adsp/common/include/intel_adsp_hda.h +++ b/soc/intel/intel_adsp/common/include/intel_adsp_hda.h @@ -273,24 +273,24 @@ static inline bool intel_adsp_hda_is_enabled(uint32_t base, uint32_t regblock_si */ static inline uint32_t intel_adsp_hda_unused(uint32_t base, uint32_t regblock_size, uint32_t sid) { - uint32_t dgcs = *DGCS(base, regblock_size, sid); uint32_t dgbs = *DGBS(base, regblock_size, sid); - - /* Check if buffer is empty */ - if ((dgcs & DGCS_BNE) == 0) { - return dgbs; - } - - /* Check if the buffer is full */ - if (dgcs & DGCS_BF) { - return 0; - } - int32_t rp = *DGBRP(base, regblock_size, sid); int32_t wp = *DGBWP(base, regblock_size, sid); int32_t size = rp - wp; - if (size <= 0) { + if (size == 0) { + uint32_t dgcs = *DGCS(base, regblock_size, sid); + + /* Check if buffer is empty */ + if ((dgcs & DGCS_BNE) == 0) { + return dgbs; + } + + /* + * Buffer is not empty and pointers equal, it can + * only be full, no need to check the DGCS_BF flag + */ + } else if (size < 0) { size += dgbs; }