Message ID | 20240718091329.3788619-2-carlos.song@nxp.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | i3c: master: svc: adjust the first broadcast speed | expand |
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 >
> -----Original Message----- > From: Frank Li <frank.li@nxp.com> > Sent: Thursday, July 18, 2024 10:08 PM > To: Carlos Song <carlos.song@nxp.com> > Cc: miquel.raynal@bootlin.com; conor.culhane@silvaco.com; > alexandre.belloni@bootlin.com; linux-i3c@lists.infradead.org; > linux-kernel@vger.kernel.org; dl-linux-imx <linux-imx@nxp.com> > Subject: Re: [PATCH] i3c: master: svc: adjust the first broadcast speed > > 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. > Sorry, I will keep the steps in the future. > > --- > > 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? I agree about it. > > }; > > > > /** > > @@ -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. > Will do it > > + /* 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 > I will try to do it and will send V2 > > > > 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 > >
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)))