summaryrefslogtreecommitdiff
path: root/drivers/i3c/master/svc-i3c-master.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i3c/master/svc-i3c-master.c')
-rw-r--r--drivers/i3c/master/svc-i3c-master.c594
1 files changed, 436 insertions, 158 deletions
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 5ee4db68988e..7e1a7cb94b43 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -32,6 +32,7 @@
#define SVC_I3C_MCONFIG_ODBAUD(x) FIELD_PREP(GENMASK(23, 16), (x))
#define SVC_I3C_MCONFIG_ODHPP(x) FIELD_PREP(BIT(24), (x))
#define SVC_I3C_MCONFIG_SKEW(x) FIELD_PREP(GENMASK(27, 25), (x))
+#define SVC_I3C_MCONFIG_SKEW_MASK GENMASK(27, 25)
#define SVC_I3C_MCONFIG_I2CBAUD(x) FIELD_PREP(GENMASK(31, 28), (x))
#define SVC_I3C_MCTRL 0x084
@@ -58,6 +59,7 @@
#define SVC_I3C_MSTATUS 0x088
#define SVC_I3C_MSTATUS_STATE(x) FIELD_GET(GENMASK(2, 0), (x))
#define SVC_I3C_MSTATUS_STATE_DAA(x) (SVC_I3C_MSTATUS_STATE(x) == 5)
+#define SVC_I3C_MSTATUS_STATE_SLVREQ(x) (SVC_I3C_MSTATUS_STATE(x) == 1)
#define SVC_I3C_MSTATUS_STATE_IDLE(x) (SVC_I3C_MSTATUS_STATE(x) == 0)
#define SVC_I3C_MSTATUS_BETWEEN(x) FIELD_GET(BIT(4), (x))
#define SVC_I3C_MSTATUS_NACKED(x) FIELD_GET(BIT(5), (x))
@@ -113,6 +115,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
@@ -127,9 +130,37 @@
/* This parameter depends on the implementation and may be tuned */
#define SVC_I3C_FIFO_SIZE 16
+#define SVC_I3C_PPBAUD_MAX 15
+#define SVC_I3C_QUICK_I2C_CLK 4170000
-#define SVC_I3C_EVENT_IBI BIT(0)
-#define SVC_I3C_EVENT_HOTJOIN BIT(1)
+#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)
+/*
+ * SVC_I3C_QUIRK_FLASE_SLVSTART:
+ * I3C HW may generate an invalid SlvStart event when emitting a STOP.
+ * If it is a true SlvStart, the MSTATUS state is SLVREQ.
+ */
+#define SVC_I3C_QUIRK_FALSE_SLVSTART BIT(1)
+/*
+ * SVC_I3C_QUIRK_DAA_CORRUPT:
+ * When MCONFIG.SKEW=0 and MCONFIG.ODHPP=0, the ENTDAA transaction gets
+ * corrupted and results in a no repeated-start condition at the end of
+ * address assignment.
+ * Workaround:
+ * Set MCONFIG.SKEW to 1 before initiating the DAA process. After the DAA
+ * process is completed, return MCONFIG.SKEW to its previous value.
+ */
+#define SVC_I3C_QUIRK_DAA_CORRUPT BIT(2)
struct svc_i3c_cmd {
u8 addr;
@@ -156,6 +187,10 @@ struct svc_i3c_regs_save {
u32 mdynaddr;
};
+struct svc_i3c_drvdata {
+ u32 quirks;
+};
+
/**
* struct svc_i3c_master - Silvaco I3C Master structure
* @base: I3C master controller
@@ -166,11 +201,10 @@ struct svc_i3c_regs_save {
* @addrs: Array containing the dynamic addresses of each attached device
* @descs: Array of descriptors, one per attached device
* @hj_work: Hot-join work
- * @ibi_work: IBI work
* @irq: Main interrupt
- * @pclk: System clock
+ * @num_clks: I3C clock number
* @fclk: Fast clock (bus)
- * @sclk: Slow clock (other events)
+ * @clks: I3C clock array
* @xferqueue: Transfer queue structure
* @xferqueue.list: List member
* @xferqueue.cur: Current ongoing transfer
@@ -181,7 +215,9 @@ struct svc_i3c_regs_save {
* @ibi.tbq_slot: To be queued IBI slot
* @ibi.lock: IBI lock
* @lock: Transfer lock, protect between IBI work thread and callbacks from master
+ * @drvdata: Driver data
* @enabled_events: Bit masks for enable events (IBI, HotJoin).
+ * @mctrl_config: Configuration value in SVC_I3C_MCTRL for setting speed back.
*/
struct svc_i3c_master {
struct i3c_master_controller base;
@@ -192,11 +228,10 @@ struct svc_i3c_master {
u8 addrs[SVC_I3C_MAX_DEVS];
struct i3c_dev_desc *descs[SVC_I3C_MAX_DEVS];
struct work_struct hj_work;
- struct work_struct ibi_work;
int irq;
- struct clk *pclk;
+ int num_clks;
struct clk *fclk;
- struct clk *sclk;
+ struct clk_bulk_data *clks;
struct {
struct list_head list;
struct svc_i3c_xfer *cur;
@@ -211,7 +246,9 @@ struct svc_i3c_master {
spinlock_t lock;
} ibi;
struct mutex lock;
- int enabled_events;
+ const struct svc_i3c_drvdata *drvdata;
+ u32 enabled_events;
+ u32 mctrl_config;
};
/**
@@ -226,6 +263,18 @@ 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 svc_has_daa_corrupt(struct svc_i3c_master *master)
+{
+ return ((master->drvdata->quirks & SVC_I3C_QUIRK_DAA_CORRUPT) &&
+ !(master->mctrl_config &
+ (SVC_I3C_MCONFIG_SKEW_MASK | SVC_I3C_MCONFIG_ODHPP(1))));
+}
+
static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask)
{
return !!(master->enabled_events & mask);
@@ -374,7 +423,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
slot->len < SVC_I3C_FIFO_SIZE) {
mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl);
- readsl(master->regs + SVC_I3C_MRDATAB, buf, count);
+ readsb(master->regs + SVC_I3C_MRDATAB, buf, count);
slot->len += count;
buf += count;
}
@@ -384,10 +433,11 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
return 0;
}
-static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
+static int svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
bool mandatory_byte)
{
unsigned int ibi_ack_nack;
+ u32 reg;
ibi_ack_nack = SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK;
if (mandatory_byte)
@@ -396,42 +446,90 @@ static void svc_i3c_master_ack_ibi(struct svc_i3c_master *master,
ibi_ack_nack |= SVC_I3C_MCTRL_IBIRESP_ACK_WITHOUT_BYTE;
writel(ibi_ack_nack, master->regs + SVC_I3C_MCTRL);
+
+ return readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
+ SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000);
+
}
-static void svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
+static int svc_i3c_master_nack_ibi(struct svc_i3c_master *master)
{
+ int ret;
+ u32 reg;
+
writel(SVC_I3C_MCTRL_REQUEST_IBI_ACKNACK |
SVC_I3C_MCTRL_IBIRESP_NACK,
master->regs + SVC_I3C_MCTRL);
+
+ ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, reg,
+ SVC_I3C_MSTATUS_MCTRLDONE(reg), 1, 1000);
+ return ret;
+}
+
+static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 mstatus)
+{
+ u32 ibitype;
+ int ret = 0;
+
+ ibitype = SVC_I3C_MSTATUS_IBITYPE(mstatus);
+
+ writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
+ /* Hardware can't auto emit NACK for hot join and master request */
+ switch (ibitype) {
+ case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
+ case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
+ ret = svc_i3c_master_nack_ibi(master);
+ }
+
+ return ret;
}
-static void svc_i3c_master_ibi_work(struct work_struct *work)
+static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
{
- struct svc_i3c_master *master = container_of(work, struct svc_i3c_master, ibi_work);
struct svc_i3c_i2c_dev_data *data;
unsigned int ibitype, ibiaddr;
struct i3c_dev_desc *dev;
u32 status, val;
int ret;
- mutex_lock(&master->lock);
+ /*
+ * According to I3C spec ver 1.1, 09-Jun-2021, section 5.1.2.5:
+ *
+ * The I3C Controller shall hold SCL low while the Bus is in ACK/NACK Phase of I3C/I2C
+ * transfer. But maximum stall time is 100us. The IRQs have to be disabled to prevent
+ * schedule during the whole I3C transaction, otherwise, the I3C bus timeout may happen if
+ * any irq or schedule happen during transaction.
+ */
+ guard(spinlock)(&master->xferqueue.lock);
+
+ /*
+ * IBIWON may be set before SVC_I3C_MCTRL_REQUEST_AUTO_IBI, causing
+ * readl_relaxed_poll_timeout() to return immediately. Consequently,
+ * ibitype will be 0 since it was last updated only after the 8th SCL
+ * cycle, leading to missed client IBI handlers.
+ *
+ * A typical scenario is when IBIWON occurs and bus arbitration is lost
+ * at svc_i3c_master_priv_xfers().
+ *
+ * Clear SVC_I3C_MINT_IBIWON before sending SVC_I3C_MCTRL_REQUEST_AUTO_IBI.
+ */
+ writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
SVC_I3C_MCTRL_IBIRESP_AUTO,
master->regs + SVC_I3C_MCTRL);
/* Wait for IBIWON, should take approximately 100us */
- ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
- SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
+ ret = readl_relaxed_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS, val,
+ SVC_I3C_MSTATUS_IBIWON(val), 0, 100);
if (ret) {
dev_err(master->dev, "Timeout when polling for IBIWON\n");
svc_i3c_master_emit_stop(master);
- goto reenable_ibis;
+ return;
}
- /* Clear the interrupt status */
- writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
-
status = readl(master->regs + SVC_I3C_MSTATUS);
ibitype = SVC_I3C_MSTATUS_IBITYPE(status);
ibiaddr = SVC_I3C_MSTATUS_IBIADDR(status);
@@ -473,17 +571,17 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
svc_i3c_master_emit_stop(master);
- goto reenable_ibis;
+ return;
}
/* Handle the non critical tasks */
switch (ibitype) {
case SVC_I3C_MSTATUS_IBITYPE_IBI:
+ svc_i3c_master_emit_stop(master);
if (dev) {
i3c_master_queue_ibi(dev, master->ibi.tbq_slot);
master->ibi.tbq_slot = NULL;
}
- svc_i3c_master_emit_stop(master);
break;
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
svc_i3c_master_emit_stop(master);
@@ -491,13 +589,11 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
queue_work(master->base.wq, &master->hj_work);
break;
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
+ svc_i3c_master_emit_stop(master);
+ break;
default:
break;
}
-
-reenable_ibis:
- svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
- mutex_unlock(&master->lock);
}
static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
@@ -511,20 +607,76 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
/* Clear the interrupt status */
writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS);
- svc_i3c_master_disable_interrupts(master);
+ /* Ignore the false event */
+ if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART) &&
+ !SVC_I3C_MSTATUS_STATE_SLVREQ(active))
+ return IRQ_HANDLED;
- /* Handle the interrupt in a non atomic context */
- queue_work(master->base.wq, &master->ibi_work);
+ /*
+ * The SDA line remains low until the request is processed.
+ * Receive the request in the interrupt context to respond promptly
+ * and restore the bus to idle state.
+ */
+ svc_i3c_master_ibi_isr(master);
return IRQ_HANDLED;
}
+static int svc_i3c_master_set_speed(struct i3c_master_controller *m,
+ enum i3c_open_drain_speed speed)
+{
+ struct svc_i3c_master *master = to_svc_i3c_master(m);
+ struct i3c_bus *bus = i3c_master_get_bus(&master->base);
+ u32 ppbaud, odbaud, odhpp, mconfig;
+ unsigned long fclk_rate;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(master->dev);
+ if (ret < 0) {
+ dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
+ return ret;
+ }
+
+ switch (speed) {
+ case I3C_OPEN_DRAIN_SLOW_SPEED:
+ fclk_rate = clk_get_rate(master->fclk);
+ if (!fclk_rate) {
+ ret = -EINVAL;
+ goto rpm_out;
+ }
+ /*
+ * Set 50% duty-cycle I2C speed to I3C OPEN-DRAIN mode, so the first
+ * broadcast address is visible to all I2C/I3C devices on the I3C bus.
+ * I3C device working as a I2C device will turn off its 50ns Spike
+ * Filter to change to I3C mode.
+ */
+ mconfig = master->mctrl_config;
+ ppbaud = FIELD_GET(GENMASK(11, 8), mconfig);
+ odhpp = 0;
+ odbaud = DIV_ROUND_UP(fclk_rate, bus->scl_rate.i2c * (2 + 2 * ppbaud)) - 1;
+ mconfig &= ~GENMASK(24, 16);
+ mconfig |= SVC_I3C_MCONFIG_ODBAUD(odbaud) | SVC_I3C_MCONFIG_ODHPP(odhpp);
+ writel(mconfig, master->regs + SVC_I3C_MCONFIG);
+ break;
+ case I3C_OPEN_DRAIN_NORMAL_SPEED:
+ writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
+ break;
+ }
+
+rpm_out:
+ pm_runtime_mark_last_busy(master->dev);
+ pm_runtime_put_autosuspend(master->dev);
+
+ return ret;
+}
+
static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
struct i3c_bus *bus = i3c_master_get_bus(m);
struct i3c_device_info info = {};
unsigned long fclk_rate, fclk_period_ns;
+ unsigned long i2c_period_ns, i2c_scl_rate, i3c_scl_rate;
unsigned int high_period_ns, od_low_period_ns;
u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
int ret;
@@ -545,12 +697,15 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
}
fclk_period_ns = DIV_ROUND_UP(1000000000, fclk_rate);
+ i2c_period_ns = DIV_ROUND_UP(1000000000, bus->scl_rate.i2c);
+ i2c_scl_rate = bus->scl_rate.i2c;
+ i3c_scl_rate = bus->scl_rate.i3c;
/*
* Using I3C Push-Pull mode, target is 12.5MHz/80ns period.
* Simplest configuration is using a 50% duty-cycle of 40ns.
*/
- ppbaud = DIV_ROUND_UP(40, fclk_period_ns) - 1;
+ ppbaud = DIV_ROUND_UP(fclk_rate / 2, i3c_scl_rate) - 1;
pplow = 0;
/*
@@ -560,7 +715,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
*/
odhpp = 1;
high_period_ns = (ppbaud + 1) * fclk_period_ns;
- odbaud = DIV_ROUND_UP(240 - high_period_ns, high_period_ns) - 1;
+ odbaud = DIV_ROUND_UP(fclk_rate, SVC_I3C_QUICK_I2C_CLK * (1 + ppbaud)) - 2;
od_low_period_ns = (odbaud + 1) * high_period_ns;
switch (bus->mode) {
@@ -569,20 +724,27 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
odstop = 0;
break;
case I3C_BUS_MODE_MIXED_FAST:
- case I3C_BUS_MODE_MIXED_LIMITED:
/*
* Using I2C Fm+ mode, target is 1MHz/1000ns, the difference
* between the high and low period does not really matter.
*/
- i2cbaud = DIV_ROUND_UP(1000, od_low_period_ns) - 2;
+ i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
odstop = 1;
break;
+ case I3C_BUS_MODE_MIXED_LIMITED:
case I3C_BUS_MODE_MIXED_SLOW:
- /*
- * Using I2C Fm mode, target is 0.4MHz/2500ns, with the same
- * constraints as the FM+ mode.
- */
- i2cbaud = DIV_ROUND_UP(2500, od_low_period_ns) - 2;
+ /* I3C PP + I3C OP + I2C OP both use i2c clk rate */
+ if (ppbaud > SVC_I3C_PPBAUD_MAX) {
+ ppbaud = SVC_I3C_PPBAUD_MAX;
+ pplow = DIV_ROUND_UP(fclk_rate, i3c_scl_rate) - (2 + 2 * ppbaud);
+ }
+
+ high_period_ns = (ppbaud + 1) * fclk_period_ns;
+ odhpp = 0;
+ odbaud = DIV_ROUND_UP(fclk_rate, i2c_scl_rate * (2 + 2 * ppbaud)) - 1;
+
+ od_low_period_ns = (odbaud + 1) * high_period_ns;
+ i2cbaud = DIV_ROUND_UP(i2c_period_ns, od_low_period_ns) - 2;
odstop = 1;
break;
default:
@@ -601,6 +763,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
SVC_I3C_MCONFIG_I2CBAUD(i2cbaud);
writel(reg, master->regs + SVC_I3C_MCONFIG);
+ master->mctrl_config = reg;
/* Master core's registration */
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
@@ -775,12 +938,30 @@ 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 = 0;
u32 reg;
int ret, i;
+ svc_i3c_master_flush_fifo(master);
+
while (true) {
- /* Enter/proceed with DAA */
+ /* clean SVC_I3C_MINT_IBIWON w1c bits */
+ writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
+
+ /* SVC_I3C_MCTRL_REQUEST_PROC_DAA have two mode, ENTER DAA or PROCESS DAA.
+ *
+ * ENTER DAA:
+ * 1 will issue START, 7E, ENTDAA, and then emits 7E/R to process first target.
+ * 2 Stops just before the new Dynamic Address (DA) is to be emitted.
+ *
+ * PROCESS DAA:
+ * 1 The DA is written using MWDATAB or ADDR bits 6:0.
+ * 2 ProcessDAA is requested again to write the new address, and then starts the
+ * next (START, 7E, ENTDAA) unless marked to STOP; an MSTATUS indicating NACK
+ * means DA was not accepted (e.g. parity error). If PROCESSDAA is NACKed on the
+ * 7E/R, which means no more Slaves need a DA, then a COMPLETE will be signaled
+ * (along with DONE), and a STOP issued automatically.
+ */
writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA |
SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_NACK |
@@ -797,19 +978,39 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_MCTRLDONE(reg),
1, 1000);
if (ret)
- return ret;
+ break;
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.
+ */
+ ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
+ if (ret < 0)
+ break;
+
+ dyn_addr = ret;
+ 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.
* Otherwise, we would just need to flush the RX FIFO.
*/
ret = svc_i3c_master_readb(master, data, 6);
if (ret)
- return ret;
+ break;
for (i = 0; i < 6; i++)
prov_id[dev_nb] |= (u64)(data[i]) << (8 * (5 - i));
@@ -817,7 +1018,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
/* We do not care about the BCR and DCR yet */
ret = svc_i3c_master_readb(master, data, 2);
if (ret)
- return ret;
+ break;
+ } else if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+ ret = svc_i3c_master_handle_ibi_won(master, reg);
+ if (ret)
+ break;
+ continue;
} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
SVC_I3C_MSTATUS_COMPLETE(reg)) {
@@ -825,12 +1031,23 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* All devices received and acked they dynamic
* address, this is the natural end of the DAA
* procedure.
+ *
+ * Hardware will auto emit STOP at this case.
*/
- break;
+ *count = dev_nb;
+ return 0;
+
} else if (SVC_I3C_MSTATUS_NACKED(reg)) {
/* No I3C devices attached */
- if (dev_nb == 0)
+ if (dev_nb == 0) {
+ /*
+ * Hardware can't treat first NACK for ENTAA as normal
+ * COMPLETE. So need manual emit STOP.
+ */
+ ret = 0;
+ *count = 0;
break;
+ }
/*
* A slave device nacked the address, this is
@@ -839,8 +1056,10 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* answer again immediately and shall ack the
* address this time.
*/
- if (prov_id[dev_nb] == nacking_prov_id)
- return -EIO;
+ if (prov_id[dev_nb] == nacking_prov_id) {
+ ret = -EIO;
+ break;
+ }
dev_nb--;
nacking_prov_id = prov_id[dev_nb];
@@ -848,7 +1067,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
continue;
} else {
- return -EIO;
+ break;
}
}
@@ -860,24 +1079,19 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_BETWEEN(reg),
0, 1000);
if (ret)
- return ret;
-
- /* Give the slave device a suitable dynamic address */
- ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
- if (ret < 0)
- return ret;
+ 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++];
}
- *count = dev_nb;
+ /* Need manual issue STOP except for Complete condition */
+ svc_i3c_master_emit_stop(master);
+ svc_i3c_master_flush_fifo(master);
- return 0;
+ return ret;
}
static int svc_i3c_update_ibirules(struct svc_i3c_master *master)
@@ -890,7 +1104,7 @@ static int svc_i3c_update_ibirules(struct svc_i3c_master *master)
/* Create the IBIRULES register for both cases */
i3c_bus_for_each_i3cdev(&master->base.bus, dev) {
- if (I3C_BCR_DEVICE_ROLE(dev->info.bcr) == I3C_BCR_I3C_MASTER)
+ if (!(dev->info.bcr & I3C_BCR_IBI_REQ_CAP))
continue;
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD) {
@@ -949,20 +1163,43 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
}
spin_lock_irqsave(&master->xferqueue.lock, flags);
+
+ if (svc_has_daa_corrupt(master))
+ writel(master->mctrl_config | SVC_I3C_MCONFIG_SKEW(1),
+ master->regs + SVC_I3C_MCONFIG);
+
ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
+
+ if (svc_has_daa_corrupt(master))
+ writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
+
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
- if (ret) {
- svc_i3c_master_emit_stop(master);
- svc_i3c_master_clear_merrwarn(master);
+
+ svc_i3c_master_clear_merrwarn(master);
+ if (ret)
goto rpm_out;
- }
- /* Register all devices who participated to the core */
- for (i = 0; i < dev_nb; i++) {
- ret = i3c_master_add_i3c_dev_locked(m, addrs[i]);
- if (ret)
- goto rpm_out;
- }
+ /*
+ * Register all devices who participated to the core
+ *
+ * If two devices (A and B) are detected in DAA and address 0xa is assigned to
+ * device A and 0xb to device B, a failure in i3c_master_add_i3c_dev_locked()
+ * for device A (addr: 0xa) could prevent device B (addr: 0xb) from being
+ * registered on the bus. The I3C stack might still consider 0xb a free
+ * address. If a subsequent Hotjoin occurs, 0xb might be assigned to Device A,
+ * causing both devices A and B to use the same address 0xb, violating the I3C
+ * specification.
+ *
+ * The return value for i3c_master_add_i3c_dev_locked() should not be checked
+ * because subsequent steps will scan the entire I3C bus, independent of
+ * whether i3c_master_add_i3c_dev_locked() returns success.
+ *
+ * If device A registration fails, there is still a chance to register device
+ * B. i3c_master_add_i3c_dev_locked() can reset DAA if a failure occurs while
+ * retrieving device information.
+ */
+ for (i = 0; i < dev_nb; i++)
+ i3c_master_add_i3c_dev_locked(m, addrs[i]);
/* Configure IBI auto-rules */
ret = svc_i3c_update_ibirules(master);
@@ -1040,49 +1277,99 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
static int svc_i3c_master_xfer(struct svc_i3c_master *master,
bool rnw, unsigned int xfer_type, u8 addr,
u8 *in, const u8 *out, unsigned int xfer_len,
- unsigned int *actual_len, bool continued)
+ unsigned int *actual_len, bool continued, bool repeat_start)
{
+ int retry = repeat_start ? 1 : 2;
u32 reg;
int ret;
/* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
- writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
- xfer_type |
- SVC_I3C_MCTRL_IBIRESP_NACK |
- SVC_I3C_MCTRL_DIR(rnw) |
- SVC_I3C_MCTRL_ADDR(addr) |
- SVC_I3C_MCTRL_RDTERM(*actual_len),
- master->regs + SVC_I3C_MCTRL);
- ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
+ while (retry--) {
+ writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
+ xfer_type |
+ SVC_I3C_MCTRL_IBIRESP_NACK |
+ SVC_I3C_MCTRL_DIR(rnw) |
+ SVC_I3C_MCTRL_ADDR(addr) |
+ 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)
- goto emit_stop;
+ if (ret)
+ goto emit_stop;
- if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
- ret = -ENXIO;
- *actual_len = 0;
- goto emit_stop;
- }
+ /*
+ * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a
+ * Frame with I3C Target Address.
+ *
+ * The I3C Controller normally should start a Frame, the Address may be arbitrated,
+ * and so the Controller shall monitor to see whether an In-Band Interrupt request,
+ * a Controller Role Request (i.e., Secondary Controller requests to become the
+ * Active Controller), or a Hot-Join Request has been made.
+ *
+ * If missed IBIWON check, the wrong data will be return. When IBIWON happen, issue
+ * repeat start. Address arbitrate only happen at START, never happen at REPEAT
+ * start.
+ */
+ if (SVC_I3C_MSTATUS_IBIWON(reg)) {
+ ret = svc_i3c_master_handle_ibi_won(master, reg);
+ if (ret)
+ goto emit_stop;
+ continue;
+ }
- /*
- * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
- * with I3C Target Address.
- *
- * The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
- * the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
- * Role Request (i.e., Secondary Controller requests to become the Active Controller), or
- * a Hot-Join Request has been made.
- *
- * If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
- * and yield the above events handler.
- */
- if (SVC_I3C_MSTATUS_IBIWON(reg)) {
- ret = -ENXIO;
- *actual_len = 0;
- goto emit_stop;
+ if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
+ /*
+ * According to I3C Spec 1.1.1, 11-Jun-2021, section: 5.1.2.2.3.
+ * If the Controller chooses to start an I3C Message with an I3C Dynamic
+ * Address, then special provisions shall be made because that same I3C
+ * Target may be initiating an IBI or a Controller Role Request. So, one of
+ * three things may happen: (skip 1, 2)
+ *
+ * 3. The Addresses match and the RnW bits also match, and so neither
+ * Controller nor Target will ACK since both are expecting the other side to
+ * provide ACK. As a result, each side might think it had "won" arbitration,
+ * but neither side would continue, as each would subsequently see that the
+ * other did not provide ACK.
+ * ...
+ * For either value of RnW: Due to the NACK, the Controller shall defer the
+ * Private Write or Private Read, and should typically transmit the Target
+ * Address again after a Repeated START (i.e., the next one or any one prior
+ * to a STOP in the Frame). Since the Address Header following a Repeated
+ * START is not arbitrated, the Controller will always win (see Section
+ * 5.1.2.2.4).
+ */
+ if (retry && addr != 0x7e) {
+ writel(SVC_I3C_MERRWARN_NACK, master->regs + SVC_I3C_MERRWARN);
+ } else {
+ ret = -ENXIO;
+ *actual_len = 0;
+ goto emit_stop;
+ }
+ } else {
+ break;
+ }
}
if (rnw)
@@ -1115,6 +1402,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;
}
@@ -1176,7 +1464,7 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
cmd->addr, cmd->in, cmd->out,
cmd->len, &cmd->actual_len,
- cmd->continued);
+ cmd->continued, i > 0);
/* cmd->xfer is NULL if I2C or CCC transfer */
if (cmd->xfer)
cmd->xfer->actual_len = cmd->actual_len;
@@ -1311,7 +1599,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
cmd->addr = ccc->dests[0].addr;
cmd->rnw = ccc->rnw;
cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
- cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data,
+ cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data;
cmd->len = xfer_len;
cmd->actual_len = actual_len;
cmd->continued = false;
@@ -1391,7 +1679,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
}
static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
- const struct i2c_msg *xfers,
+ struct i2c_msg *xfers,
int nxfers)
{
struct i3c_master_controller *m = i2c_dev_get_master(dev);
@@ -1495,7 +1783,7 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
return ret;
}
- master->enabled_events |= SVC_I3C_EVENT_IBI;
+ master->enabled_events++;
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
@@ -1507,7 +1795,7 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
struct svc_i3c_master *master = to_svc_i3c_master(m);
int ret;
- master->enabled_events &= ~SVC_I3C_EVENT_IBI;
+ master->enabled_events--;
if (!master->enabled_events)
svc_i3c_master_disable_interrupts(master);
@@ -1580,77 +1868,54 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
.disable_ibi = svc_i3c_master_disable_ibi,
.enable_hotjoin = svc_i3c_master_enable_hotjoin,
.disable_hotjoin = svc_i3c_master_disable_hotjoin,
+ .set_speed = svc_i3c_master_set_speed,
};
-static int svc_i3c_master_prepare_clks(struct svc_i3c_master *master)
-{
- int ret = 0;
-
- ret = clk_prepare_enable(master->pclk);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(master->fclk);
- if (ret) {
- clk_disable_unprepare(master->pclk);
- return ret;
- }
-
- ret = clk_prepare_enable(master->sclk);
- if (ret) {
- clk_disable_unprepare(master->pclk);
- clk_disable_unprepare(master->fclk);
- return ret;
- }
-
- return 0;
-}
-
-static void svc_i3c_master_unprepare_clks(struct svc_i3c_master *master)
-{
- clk_disable_unprepare(master->pclk);
- clk_disable_unprepare(master->fclk);
- clk_disable_unprepare(master->sclk);
-}
-
static int svc_i3c_master_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct svc_i3c_master *master;
- int ret;
+ int ret, i;
master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
+ master->drvdata = of_device_get_match_data(dev);
+ if (!master->drvdata)
+ return -EINVAL;
+
master->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(master->regs))
return PTR_ERR(master->regs);
- master->pclk = devm_clk_get(dev, "pclk");
- if (IS_ERR(master->pclk))
- return PTR_ERR(master->pclk);
+ master->num_clks = devm_clk_bulk_get_all(dev, &master->clks);
+ if (master->num_clks < 0)
+ return dev_err_probe(dev, -EINVAL, "can't get I3C clocks\n");
+
+ for (i = 0; i < master->num_clks; i++) {
+ if (!strcmp(master->clks[i].id, "fast_clk"))
+ break;
+ }
+
+ if (i == master->num_clks)
+ return dev_err_probe(dev, -EINVAL,
+ "can't get I3C peripheral clock\n");
- master->fclk = devm_clk_get(dev, "fast_clk");
+ master->fclk = master->clks[i].clk;
if (IS_ERR(master->fclk))
return PTR_ERR(master->fclk);
- master->sclk = devm_clk_get(dev, "slow_clk");
- if (IS_ERR(master->sclk))
- return PTR_ERR(master->sclk);
-
master->irq = platform_get_irq(pdev, 0);
if (master->irq < 0)
return master->irq;
master->dev = dev;
-
- ret = svc_i3c_master_prepare_clks(master);
+ ret = clk_bulk_prepare_enable(master->num_clks, master->clks);
if (ret)
- return ret;
+ return dev_err_probe(dev, ret, "can't enable I3C clocks\n");
INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
- INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
mutex_init(&master->lock);
ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
@@ -1697,11 +1962,11 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
rpm_disable:
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
- pm_runtime_set_suspended(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
err_disable_clks:
- svc_i3c_master_unprepare_clks(master);
+ clk_bulk_disable_unprepare(master->num_clks, master->clks);
return ret;
}
@@ -1710,6 +1975,7 @@ static void svc_i3c_master_remove(struct platform_device *pdev)
{
struct svc_i3c_master *master = platform_get_drvdata(pdev);
+ cancel_work_sync(&master->hj_work);
i3c_master_unregister(&master->base);
pm_runtime_dont_use_autosuspend(&pdev->dev);
@@ -1738,7 +2004,7 @@ static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
struct svc_i3c_master *master = dev_get_drvdata(dev);
svc_i3c_save_regs(master);
- svc_i3c_master_unprepare_clks(master);
+ clk_bulk_disable_unprepare(master->num_clks, master->clks);
pinctrl_pm_select_sleep_state(dev);
return 0;
@@ -1747,9 +2013,12 @@ static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
{
struct svc_i3c_master *master = dev_get_drvdata(dev);
+ int ret;
pinctrl_pm_select_default_state(dev);
- svc_i3c_master_prepare_clks(master);
+ ret = clk_bulk_prepare_enable(master->num_clks, master->clks);
+ if (ret)
+ return ret;
svc_i3c_restore_regs(master);
@@ -1763,15 +2032,24 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
svc_i3c_runtime_resume, NULL)
};
+static const struct svc_i3c_drvdata npcm845_drvdata = {
+ .quirks = SVC_I3C_QUIRK_FIFO_EMPTY |
+ SVC_I3C_QUIRK_FALSE_SLVSTART |
+ SVC_I3C_QUIRK_DAA_CORRUPT,
+};
+
+static const struct svc_i3c_drvdata svc_default_drvdata = {};
+
static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
- { .compatible = "silvaco,i3c-master-v1"},
+ { .compatible = "nuvoton,npcm845-i3c", .data = &npcm845_drvdata },
+ { .compatible = "silvaco,i3c-master-v1", .data = &svc_default_drvdata },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
static struct platform_driver svc_i3c_master = {
.probe = svc_i3c_master_probe,
- .remove_new = svc_i3c_master_remove,
+ .remove = svc_i3c_master_remove,
.driver = {
.name = "silvaco-i3c-master",
.of_match_table = svc_i3c_master_of_match_tbl,