drivers/i3c/master/svc-i3c-master.c | 31 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-)
From: Carlos Song <carlos.song@nxp.com>
According to I3C Specification(Version 1.1) 5.1.2.4 "Use of Clock
Speed to Prevent Legacy I2C Devices From Seeing I3C traffic", when
slow i2c devices(FM/FM+ rate i2c frequency without 50ns filter)
works on i3c bus, i3c SDR should work at FM/FM+ rate.
Adjust timing for difference mode.
Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Signed-off-by: Carlos Song <carlos.song@nxp.com>
Reviewed-by: Frank Li <frank.li@nxp.com>
---
Change for V2:
- Correct I3C clk configuration and simplify the code:
Pure I3C mode and MIXED-FAST I3C mode just use the same i3c clk configuration:
1. i3c push-pull timing is 40ns high and 40ns low at 12.5Mhz
2. i3c open-darin timing is 40ns high and 200ns low at ~4Mhz
3. i2cbaud should be different between Pure I3C mode and MIXED-FAST
I3C mode.
---
drivers/i3c/master/svc-i3c-master.c | 31 ++++++++++++++++++++---------
1 file changed, 22 insertions(+), 9 deletions(-)
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index e80c002991f7..78116530f431 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -127,6 +127,8 @@
/* 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)
@@ -535,6 +537,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *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;
@@ -555,12 +558,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;
/*
@@ -570,7 +576,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) {
@@ -579,20 +585,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:
--
2.34.1
Please add imx@lists.linux.dev next time.
On Thu, Jul 18, 2024 at 05:13:28PM +0800, carlos.song@nxp.com wrote:
> From: Carlos Song <carlos.song@nxp.com>
>
> According to I3C Specification(Version 1.1) 5.1.2.4 "Use of Clock
> Speed to Prevent Legacy I2C Devices From Seeing I3C traffic", when
> slow i2c devices(FM/FM+ rate i2c frequency without 50ns filter)
> works on i3c bus, i3c SDR should work at FM/FM+ rate.
>
> Adjust timing for difference mode.
>
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> Signed-off-by: Carlos Song <carlos.song@nxp.com>
> Reviewed-by: Frank Li <frank.li@nxp.com>
V1 have my signed of, please keep it.
> ---
> Change for V2:
> - Correct I3C clk configuration and simplify the code:
> Pure I3C mode and MIXED-FAST I3C mode just use the same i3c clk configuration:
> 1. i3c push-pull timing is 40ns high and 40ns low at 12.5Mhz
> 2. i3c open-darin timing is 40ns high and 200ns low at ~4Mhz
> 3. i2cbaud should be different between Pure I3C mode and MIXED-FAST
> I3C mode.
V1 get Acked-by: Miquel Raynal <miquel.raynal@bootlin.com>
If you drop it because big change, you should mention at change log, said
not apply Miquel's ACK tag, because below change is quite big.
> ---
> drivers/i3c/master/svc-i3c-master.c | 31 ++++++++++++++++++++---------
> 1 file changed, 22 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index e80c002991f7..78116530f431 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -127,6 +127,8 @@
>
> /* 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)
> @@ -535,6 +537,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *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;
> @@ -555,12 +558,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;
>
> /*
> @@ -570,7 +576,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) {
> @@ -579,20 +585,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:
> --
> 2.34.1
>
From: Carlos Song <carlos.song@nxp.com>
According to the i3c spec 6.2 Timing Specification, the first
broadcast open drain timing should be adjust to High Period
of SCL Clock is 200ns at least. I3C device working as a i2c
device will see the broadcast to close its Spike Filter to
change to i3c mode. After that I3C open drain SCL high level
should be adjust to 32ns~45ns.
Signed-off-by: Carlos Song <carlos.song@nxp.com>
---
drivers/i3c/master/svc-i3c-master.c | 47 +++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 78116530f431..2d68c8d34089 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -142,6 +142,7 @@ struct svc_i3c_cmd {
unsigned int actual_len;
struct i3c_priv_xfer *xfer;
bool continued;
+ bool slow_address;
};
struct svc_i3c_xfer {
@@ -214,6 +215,10 @@ struct svc_i3c_master {
} ibi;
struct mutex lock;
int enabled_events;
+
+ unsigned long fclk_rate;
+ u32 mctrl_config;
+ bool first_broadcast;
};
/**
@@ -531,6 +536,36 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static void svc_i3c_master_set_slow_address_speed(struct svc_i3c_master *master)
+{
+ struct i3c_bus *bus = i3c_master_get_bus(&master->base);
+ u32 ppbaud, odbaud, odhpp, mconfig;
+
+ master->mctrl_config = readl(master->regs + SVC_I3C_MCONFIG);
+ mconfig = master->mctrl_config;
+
+ /*
+ * Set the I3C OPEN-DRAIN mode to the FM speed of 50% duty-cycle(400K/2500ns),
+ * so that the first broadcast is visible to all devices on the i3c bus.
+ * I3C device with 50ns filter will turn off the filter.
+ */
+
+ ppbaud = FIELD_GET(GENMASK(11, 8), mconfig);
+ odhpp = 0;
+ odbaud = DIV_ROUND_UP(master->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);
+}
+
+static void svc_i3c_master_set_default_speed(struct svc_i3c_master *master)
+{
+ /* Whatever, setting the controller to initial configuration */
+ writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
+ master->first_broadcast = false;
+}
+
static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
{
struct svc_i3c_master *master = to_svc_i3c_master(m);
@@ -624,6 +659,8 @@ 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->first_broadcast = true;
+ master->fclk_rate = fclk_rate;
/* Master core's registration */
ret = i3c_master_get_free_addr(m, 0);
if (ret < 0)
@@ -1250,6 +1287,8 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
for (i = 0; i < xfer->ncmds; i++) {
struct svc_i3c_cmd *cmd = &xfer->cmds[i];
+ if (cmd->slow_address)
+ svc_i3c_master_set_slow_address_speed(master);
ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
cmd->addr, cmd->in, cmd->out,
@@ -1259,6 +1298,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
if (cmd->xfer)
cmd->xfer->actual_len = cmd->actual_len;
+ if (cmd->slow_address)
+ svc_i3c_master_set_default_speed(master);
+
if (ret)
break;
}
@@ -1346,6 +1388,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
cmd->actual_len = 0;
cmd->continued = false;
+ if (master->first_broadcast)
+ cmd->slow_address = true;
+ else
+ cmd->slow_address = false;
+
mutex_lock(&master->lock);
svc_i3c_master_enqueue_xfer(master, xfer);
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
--
2.34.1
On Thu, Jul 18, 2024 at 05:13:29PM +0800, carlos.song@nxp.com wrote:
> From: Carlos Song <carlos.song@nxp.com>
>
> According to the i3c spec 6.2 Timing Specification, the first
> broadcast open drain timing should be adjust to High Period
> of SCL Clock is 200ns at least. I3C device working as a i2c
> device will see the broadcast to close its Spike Filter to
> change to i3c mode. After that I3C open drain SCL high level
> should be adjust to 32ns~45ns.
>
> Signed-off-by: Carlos Song <carlos.song@nxp.com>
If you sent two patch together, it should use "git format -n2", need
1/2, 2/2 in subject.
If create patch seperately, send patch one by one to avoid combine into
one email thread.
> ---
> drivers/i3c/master/svc-i3c-master.c | 47 +++++++++++++++++++++++++++++
> 1 file changed, 47 insertions(+)
>
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 78116530f431..2d68c8d34089 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -142,6 +142,7 @@ struct svc_i3c_cmd {
> unsigned int actual_len;
> struct i3c_priv_xfer *xfer;
> bool continued;
> + bool slow_address;
> };
>
> struct svc_i3c_xfer {
> @@ -214,6 +215,10 @@ struct svc_i3c_master {
> } ibi;
> struct mutex lock;
> int enabled_events;
> +
> + unsigned long fclk_rate;
> + u32 mctrl_config;
> + bool first_broadcast;
slow_speed may be better?
> };
>
> /**
> @@ -531,6 +536,36 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> +static void svc_i3c_master_set_slow_address_speed(struct svc_i3c_master *master)
> +{
> + struct i3c_bus *bus = i3c_master_get_bus(&master->base);
> + u32 ppbaud, odbaud, odhpp, mconfig;
> +
> + master->mctrl_config = readl(master->regs + SVC_I3C_MCONFIG);
> + mconfig = master->mctrl_config;
> +
> + /*
> + * Set the I3C OPEN-DRAIN mode to the FM speed of 50% duty-cycle(400K/2500ns),
> + * so that the first broadcast is visible to all devices on the i3c bus.
> + * I3C device with 50ns filter will turn off the filter.
> + */
> +
> + ppbaud = FIELD_GET(GENMASK(11, 8), mconfig);
> + odhpp = 0;
> + odbaud = DIV_ROUND_UP(master->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);
master->slow_speed = true;
> +}
> +
> +static void svc_i3c_master_set_default_speed(struct svc_i3c_master *master)
> +{
if (master->slow_speed)
writel()
master->slow_speed = false
}
to avoid set default speed every time.
> + /* Whatever, setting the controller to initial configuration */
> + writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
> + master->first_broadcast = false;
> +}
> +
> static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
> {
> struct svc_i3c_master *master = to_svc_i3c_master(m);
> @@ -624,6 +659,8 @@ 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->first_broadcast = true;
> + master->fclk_rate = fclk_rate;
> /* Master core's registration */
> ret = i3c_master_get_free_addr(m, 0);
> if (ret < 0)
> @@ -1250,6 +1287,8 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
>
> for (i = 0; i < xfer->ncmds; i++) {
> struct svc_i3c_cmd *cmd = &xfer->cmds[i];
> + if (cmd->slow_address)
> + svc_i3c_master_set_slow_address_speed(master);
if (cmd->slow_address)
svc_i3c_master_set_slow_address_speed(master);
else
svc_i3c_master_set_default_speed(master);
will be easy understand
>
> ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
> cmd->addr, cmd->in, cmd->out,
> @@ -1259,6 +1298,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
> if (cmd->xfer)
> cmd->xfer->actual_len = cmd->actual_len;
>
> + if (cmd->slow_address)
> + svc_i3c_master_set_default_speed(master);
> +
> if (ret)
> break;
> }
> @@ -1346,6 +1388,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
> cmd->actual_len = 0;
> cmd->continued = false;
>
> + if (master->first_broadcast)
> + cmd->slow_address = true;
> + else
> + cmd->slow_address = false;
> +
> mutex_lock(&master->lock);
> svc_i3c_master_enqueue_xfer(master, xfer);
> if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
> --
> 2.34.1
>
© 2016 - 2025 Red Hat, Inc.