Message ID | 20250220061107.1718239-4-yschu@nuvoton.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | Add support for Nuvoton npcm845 i3c controller | expand |
On Thu, Feb 20, 2025 at 02:11:06PM +0800, Stanley Chu wrote: > From: Stanley Chu <yschu@nuvoton.com> > > I3C HW stalls the write transfer if the transmit FIFO becomes empty, > when new data is written to FIFO, I3C HW resumes the transfer but the > first transmitted data bit may have the wrong value. > Fill the FIFO in advance to prevent FIFO from becoming empty. > > Signed-off-by: Stanley Chu <yschu@nuvoton.com> > --- You missed do_daa part. > drivers/i3c/master/svc-i3c-master.c | 22 ++++++++++++++++++---- > 1 file changed, 18 insertions(+), 4 deletions(-) > > diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c > index c58440061d5a..2140da3f5187 100644 > --- a/drivers/i3c/master/svc-i3c-master.c > +++ b/drivers/i3c/master/svc-i3c-master.c > @@ -1196,8 +1196,8 @@ static int svc_i3c_master_read(struct svc_i3c_master *master, > return offset; > } > > -static int svc_i3c_master_write(struct svc_i3c_master *master, > - const u8 *out, unsigned int len) > +static int svc_i3c_master_write(struct svc_i3c_master *master, const u8 *out, > + unsigned int len, bool last) > { > int offset = 0, ret; > u32 mdctrl; > @@ -1214,7 +1214,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master, > * The last byte to be sent over the bus must either have the > * "end" bit set or be written in MWDATABE. > */ > - if (likely(offset < (len - 1))) > + if (likely(offset < (len - 1)) || !last) > writel(out[offset++], master->regs + SVC_I3C_MWDATAB); > else > writel(out[offset++], master->regs + SVC_I3C_MWDATABE); > @@ -1245,6 +1245,19 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, > SVC_I3C_MCTRL_RDTERM(*actual_len), > master->regs + SVC_I3C_MCTRL); > > + if ((master->quirks & SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) { > + unsigned int len = xfer_len; len = max_t(u32, xfer_len, SVC_I3C_FIFO_SIZE); > + > + if (xfer_len > SVC_I3C_FIFO_SIZE) > + len = SVC_I3C_FIFO_SIZE; > + ret = svc_i3c_master_write(master, out, len, > + xfer_len <= SVC_I3C_FIFO_SIZE); > + if (ret < 0) > + goto emit_stop; > + xfer_len -= len; > + out += len; > + } > + > ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg, > SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000); > if (ret) > @@ -1306,7 +1319,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, > if (rnw) > ret = svc_i3c_master_read(master, in, xfer_len); > else > - ret = svc_i3c_master_write(master, out, xfer_len); > + ret = svc_i3c_master_write(master, out, xfer_len, true); > if (ret < 0) > goto emit_stop; > > @@ -1333,6 +1346,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, > emit_stop: > svc_i3c_master_emit_stop(master); > svc_i3c_master_clear_merrwarn(master); > + svc_i3c_master_flush_fifo(master); > > return ret; > } > -- > 2.34.1 >
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c index c58440061d5a..2140da3f5187 100644 --- a/drivers/i3c/master/svc-i3c-master.c +++ b/drivers/i3c/master/svc-i3c-master.c @@ -1196,8 +1196,8 @@ static int svc_i3c_master_read(struct svc_i3c_master *master, return offset; } -static int svc_i3c_master_write(struct svc_i3c_master *master, - const u8 *out, unsigned int len) +static int svc_i3c_master_write(struct svc_i3c_master *master, const u8 *out, + unsigned int len, bool last) { int offset = 0, ret; u32 mdctrl; @@ -1214,7 +1214,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master, * The last byte to be sent over the bus must either have the * "end" bit set or be written in MWDATABE. */ - if (likely(offset < (len - 1))) + if (likely(offset < (len - 1)) || !last) writel(out[offset++], master->regs + SVC_I3C_MWDATAB); else writel(out[offset++], master->regs + SVC_I3C_MWDATABE); @@ -1245,6 +1245,19 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, SVC_I3C_MCTRL_RDTERM(*actual_len), master->regs + SVC_I3C_MCTRL); + if ((master->quirks & SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) { + unsigned int len = xfer_len; + + if (xfer_len > SVC_I3C_FIFO_SIZE) + len = SVC_I3C_FIFO_SIZE; + ret = svc_i3c_master_write(master, out, len, + xfer_len <= SVC_I3C_FIFO_SIZE); + if (ret < 0) + goto emit_stop; + xfer_len -= len; + out += len; + } + ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg, SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000); if (ret) @@ -1306,7 +1319,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, if (rnw) ret = svc_i3c_master_read(master, in, xfer_len); else - ret = svc_i3c_master_write(master, out, xfer_len); + ret = svc_i3c_master_write(master, out, xfer_len, true); if (ret < 0) goto emit_stop; @@ -1333,6 +1346,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, emit_stop: svc_i3c_master_emit_stop(master); svc_i3c_master_clear_merrwarn(master); + svc_i3c_master_flush_fifo(master); return ret; }