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

i3c: master: svc: Fix npcm845 FIFO_EMPTY quirk

In a private write transfer, the driver pre-fills the FIFO to work around
the FIFO_EMPTY quirk. However, if an IBIWON event occurs, the hardware
emits a NACK and the driver initiates a retry. During the retry, driver
attempts to pre-fill the FIFO again if there is remaining data, but since
the FIFO is already full, this leads to data loss.

Check available space in FIFO to prevent overflow.

Fixes: 4008a74e0f9b ("i3c: master: svc: Fix npcm845 FIFO empty issue")
Signed-off-by: Stanley Chu <yschu@nuvoton.com>
Link: https://lore.kernel.org/r/20250730003719.1825593-1-yschu@nuvoton.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Stanley Chu and committed by
Alexandre Belloni
bc4a09d8 d028219a

+13 -7
+13 -7
drivers/i3c/master/svc-i3c-master.c
··· 104 104 #define SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL GENMASK(5, 4) 105 105 #define SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY 0 106 106 #define SVC_I3C_MDATACTRL_RXCOUNT(x) FIELD_GET(GENMASK(28, 24), (x)) 107 + #define SVC_I3C_MDATACTRL_TXCOUNT(x) FIELD_GET(GENMASK(20, 16), (x)) 107 108 #define SVC_I3C_MDATACTRL_TXFULL BIT(30) 108 109 #define SVC_I3C_MDATACTRL_RXEMPTY BIT(31) 109 110 ··· 1305 1304 * FIFO start filling as soon as possible after EmitStartAddr. 1306 1305 */ 1307 1306 if (svc_has_quirk(master, SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) { 1308 - u32 end = xfer_len > SVC_I3C_FIFO_SIZE ? 0 : SVC_I3C_MWDATAB_END; 1309 - u32 len = min_t(u32, xfer_len, SVC_I3C_FIFO_SIZE); 1307 + u32 space, end, len; 1310 1308 1311 - writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1); 1312 - /* Mark END bit if this is the last byte */ 1313 - writel(out[len - 1] | end, master->regs + SVC_I3C_MWDATAB); 1314 - xfer_len -= len; 1315 - out += len; 1309 + reg = readl(master->regs + SVC_I3C_MDATACTRL); 1310 + space = SVC_I3C_FIFO_SIZE - SVC_I3C_MDATACTRL_TXCOUNT(reg); 1311 + if (space) { 1312 + end = xfer_len > space ? 0 : SVC_I3C_MWDATAB_END; 1313 + len = min_t(u32, xfer_len, space); 1314 + writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1); 1315 + /* Mark END bit if this is the last byte */ 1316 + writel(out[len - 1] | end, master->regs + SVC_I3C_MWDATAB); 1317 + xfer_len -= len; 1318 + out += len; 1319 + } 1316 1320 } 1317 1321 1318 1322 ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,