@@ -113,6 +113,7 @@
#define SVC_I3C_MWDATAHE 0x0BC
#define SVC_I3C_MRDATAB 0x0C0
#define SVC_I3C_MRDATAH 0x0C8
+#define SVC_I3C_MWDATAB1 0x0CC
#define SVC_I3C_MWMSG_SDR 0x0D0
#define SVC_I3C_MRMSG_SDR 0x0D4
#define SVC_I3C_MWMSG_DDR 0x0D8
@@ -133,6 +134,16 @@
#define SVC_I3C_EVENT_IBI GENMASK(7, 0)
#define SVC_I3C_EVENT_HOTJOIN BIT(31)
+/*
+ * SVC_I3C_QUIRK_FIFO_EMPTY:
+ * 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.
+ * Workaround:
+ * Fill the FIFO in advance to prevent FIFO from becoming empty.
+ */
+#define SVC_I3C_QUIRK_FIFO_EMPTY BIT(0)
+
struct svc_i3c_cmd {
u8 addr;
bool rnw;
@@ -236,6 +247,11 @@ struct svc_i3c_i2c_dev_data {
struct i3c_generic_ibi_pool *ibi_pool;
};
+static inline bool svc_has_quirk(struct svc_i3c_master *master, u32 quirk)
+{
+ return (master->drvdata->quirks & quirk);
+}
+
static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask)
{
return !!(master->enabled_events & mask);
@@ -894,7 +910,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
u8 *addrs, unsigned int *count)
{
u64 prov_id[SVC_I3C_MAX_DEVS] = {}, nacking_prov_id = 0;
- unsigned int dev_nb = 0, last_addr = 0;
+ unsigned int dev_nb = 0, last_addr = 0, dyn_addr;
u32 reg;
int ret, i;
@@ -937,6 +953,25 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
if (SVC_I3C_MSTATUS_RXPEND(reg)) {
u8 data[6];
+ /*
+ * One slave sends its ID to request for address assignment,
+ * prefilling the dynamic address can reduce SCL clock stalls
+ * and also fix the SVC_I3C_QUIRK_FIFO_EMPTY quirk.
+ *
+ * Ideally, prefilling before the processDAA command is better.
+ * However, it requires an additional check to write the dyn_addr
+ * at the right time because the driver needs to write the processDAA
+ * command twice for one assignment.
+ * Prefilling here is safe and efficient because the FIFO starts
+ * filling within a few hundred nanoseconds, which is significantly
+ * faster compared to the 64 SCL clock cycles.
+ */
+ dyn_addr = i3c_master_get_free_addr(&master->base, last_addr + 1);
+ if (dyn_addr < 0)
+ return -ENOSPC;
+
+ writel(dyn_addr, master->regs + SVC_I3C_MWDATAB);
+
/*
* We only care about the 48-bit provisioned ID yet to
* be sure a device does not nack an address twice.
@@ -1015,21 +1050,16 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
if (ret)
break;
- /* Give the slave device a suitable dynamic address */
- ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
- if (ret < 0)
- break;
-
- addrs[dev_nb] = ret;
+ addrs[dev_nb] = dyn_addr;
dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n",
dev_nb, addrs[dev_nb]);
-
- writel(addrs[dev_nb], master->regs + SVC_I3C_MWDATAB);
last_addr = addrs[dev_nb++];
}
/* Need manual issue STOP except for Complete condition */
svc_i3c_master_emit_stop(master);
+ svc_i3c_master_flush_fifo(master);
+
return ret;
}
@@ -1226,6 +1256,24 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
SVC_I3C_MCTRL_RDTERM(*actual_len),
master->regs + SVC_I3C_MCTRL);
+ /*
+ * The entire transaction can consist of multiple write transfers.
+ * Prefilling before EmitStartAddr causes the data to be emitted
+ * immediately, becoming part of the previous transfer.
+ * The only way to work around this hardware issue is to let the
+ * FIFO start filling as soon as possible after EmitStartAddr.
+ */
+ if (svc_has_quirk(master, SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) {
+ u32 end = xfer_len > SVC_I3C_FIFO_SIZE ? 0 : SVC_I3C_MWDATAB_END;
+ u32 len = min_t(u32, xfer_len, SVC_I3C_FIFO_SIZE);
+
+ writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1);
+ /* Mark END bit if this is the last byte */
+ writel(out[len - 1] | end, master->regs + SVC_I3C_MWDATAB);
+ xfer_len -= len;
+ out += len;
+ }
+
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
if (ret)
@@ -1314,6 +1362,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;
}
@@ -1968,7 +2017,9 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
svc_i3c_runtime_resume, NULL)
};
-const struct svc_i3c_drvdata npcm845_drvdata = {};
+const struct svc_i3c_drvdata npcm845_drvdata = {
+ .quirks = SVC_I3C_QUIRK_FIFO_EMPTY,
+};
const struct svc_i3c_drvdata svc_default_drvdata = {};