Re: [PATCH v2 5/5] i2c: mux: pca9541: add support for PCA9641

From: Peter Rosin
Date: Mon Mar 25 2019 - 11:01:32 EST


On 2019-03-22 20:38, Pradeep Srinivasan wrote:
> I have verified the changes on PCA 9541. May I know how you want the test results to be shared ? (newbie here; please bear with me)
>
> root@cumulus:/home/cumulus# dmesg| grep "pca9541" | grep -v "pmbus"
> [ Â Â2.922288] pca9541 1-0070: registered master selector for I2C pca9541
>
> root@cumulus:/home/cumulus# cat /sys/class/i2c-dev/i2c-1/device/1-0070/name
> pca9541

This only verifies that the probe works and that the chip is detected properly.
It says nothing about if it works to communicate with whatever is beyond the
PCA9541, and nothing on how the interaction with the "alien" other master
connected to the PCA9541 is working. I don't know how I want this to be tested,
but if you have a setup with a PCA9541 / PCA9641 I would assume that you
have some kind of need for those chips and that you at least could report
if basic xfers through them are working? I don't need to see actual commands
that you have executed, I'm much more interested in some summary of what
you did and what worked (or not).

E.g. if you have an eeprom beyond the master selector, you could read from
it in a loop while doing something else from the alien master and check if
it all works as expected? Perhaps try to verify timing if there are stalls
and/or timeouts etc. Go wild! But if you don't know how or don't have the
time, I'd be happy with a report on basic functionality (but a little bit
more than probe-ok would be nice though), because the code affecting the
PCA9541 is probably not broken subtly, it either works as it did before or
it doesn't work at all. And any problem with the PCA9641 side of things
will not be a regression and therefore not a big problem...

Cheers,
Peter

> I need to do the same on PCA 9641. If the above is sufficient, I will grab a switch with PCA 9641 and check if the driver works .
>
>
> Thanks
> Pradeep
>
> On Thu, Mar 7, 2019 at 1:16 PM Peter Rosin <peda@xxxxxxxxxx <mailto:peda@xxxxxxxxxx>> wrote:
>
> Hi!
>
> I should have read Kens code more carefully, before signing off on it...
>
> Review comments inline...
>
> On 2019-03-07 00:15, Peter Rosin wrote:
> > Heavily based on code from Ken Chen <chen.kenyy@xxxxxxxxxxxx <mailto:chen.kenyy@xxxxxxxxxxxx>>.
> >
> > Signed-off-by: Peter Rosin <peda@xxxxxxxxxx <mailto:peda@xxxxxxxxxx>>
> > ---
> > drivers/i2c/muxes/Kconfig     Â| Â6 +-
> >Â drivers/i2c/muxes/i2c-mux-pca9541.c | 137 ++++++++++++++++++++++++++++++++++--
> >Â 2 files changed, 136 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
> > index 52a4a922e7e6..8532841de5db 100644
> > --- a/drivers/i2c/muxes/Kconfig
> > +++ b/drivers/i2c/muxes/Kconfig
> > @@ -55,10 +55,10 @@ config I2C_MUX_LTC4306
> >Â Â Â Â Âwill be called i2c-mux-ltc4306.
>
> >Â config I2C_MUX_PCA9541
> > -Â Â Âtristate "NXP PCA9541 I2C Master Selector"
> > +Â Â Âtristate "NXP PCA9541/PCA9641 I2C Master Selectors"
> >Â Â Â Âhelp
> > -Â Â Â ÂIf you say yes here you get support for the NXP PCA9541
> > -Â Â Â ÂI2C Master Selector.
> > +Â Â Â ÂIf you say yes here you get support for the NXP PCA9541/PCA9641
> > +Â Â Â ÂI2C Master Selectors.
>
> >    ÂThis driver can also be built as a module. If so, the module
> >Â Â Â Â Âwill be called i2c-mux-pca9541.
> > diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c
> > index 5eb36e3223d5..5d4e0c92e978 100644
> > --- a/drivers/i2c/muxes/i2c-mux-pca9541.c
> > +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c
> > @@ -1,5 +1,5 @@
> >Â /*
> > - * I2C multiplexer driver for PCA9541 bus master selector
> > + * I2C multiplexer driver for PCA9541/PCA9641 bus master selectors
> >Â Â*
> >Â Â* Copyright (c) 2010 Ericsson AB.
> >Â Â*
> > @@ -28,8 +28,8 @@
> >Â #include <linux/slab.h>
>
> >Â /*
> > - * The PCA9541 is a bus master selector. It supports two I2C masters connected
> > - * to a single slave bus.
> > + * The PCA9541 and PCA9641 are bus master selector. They support two I2C masters
> > + * connected to a single slave bus.
> >Â Â*
> >Â Â* Before each bus transaction, a master has to acquire bus ownership. After the
> >Â Â* transaction is complete, bus ownership has to be released. This fits well
> > @@ -63,6 +63,33 @@
> >Â #define PCA9541_BUSONÂ Â Â Â (PCA9541_CTL_BUSON | PCA9541_CTL_NBUSON)
> >Â #define PCA9541_MYBUSÂ Â Â Â (PCA9541_CTL_MYBUS | PCA9541_CTL_NMYBUS)
>
> > +#define PCA9641_IDÂ Â Â Â Â Â Â Â Â Â0x00
> > +#define PCA9641_ID_MAGICÂ Â Â Â Â Â Â0x38
> > +
> > +#define PCA9641_CONTROLÂ Â Â Â Â Â Â Â Â Â Â 0x01
> > +#define PCA9641_STATUSÂ Â Â Â Â Â Â Â Â Â Â Â0x02
> > +#define PCA9641_TIMEÂ Â Â Â Â Â Â Â Â0x03
> > +
> > +#define PCA9641_CTL_LOCK_REQÂ Â Â Â ÂBIT(0)
> > +#define PCA9641_CTL_LOCK_GRANTÂ Â Â Â Â Â Â ÂBIT(1)
> > +#define PCA9641_CTL_BUS_CONNECTÂ Â Â Â Â Â Â BIT(2)
> > +#define PCA9641_CTL_BUS_INITÂ Â Â Â ÂBIT(3)
> > +#define PCA9641_CTL_SMBUS_SWRSTÂ Â Â Â Â Â Â BIT(4)
> > +#define PCA9641_CTL_IDLE_TIMER_DISÂ ÂBIT(5)
> > +#define PCA9641_CTL_SMBUS_DISÂ Â Â Â Â Â Â Â BIT(6)
> > +#define PCA9641_CTL_PRIORITYÂ Â Â Â ÂBIT(7)
> > +
> > +#define PCA9641_STS_OTHER_LOCKÂ Â Â Â Â Â Â ÂBIT(0)
> > +#define PCA9641_STS_BUS_INIT_FAILÂ Â BIT(1)
> > +#define PCA9641_STS_BUS_HUNGÂ Â Â Â ÂBIT(2)
> > +#define PCA9641_STS_MBOX_EMPTYÂ Â Â Â Â Â Â ÂBIT(3)
> > +#define PCA9641_STS_MBOX_FULLÂ Â Â Â Â Â Â Â BIT(4)
> > +#define PCA9641_STS_TEST_INTÂ Â Â Â ÂBIT(5)
> > +#define PCA9641_STS_SCL_IOÂ Â Â Â Â ÂBIT(6)
> > +#define PCA9641_STS_SDA_IOÂ Â Â Â Â ÂBIT(7)
> > +
> > +#define PCA9641_RES_TIMEÂ Â Â Â Â Â Â0x03
>
> This appears to be the same thing as PCA9641_TIME above. The
> register is called PCA9641_RT in my data sheet.
>
> > +
> >Â /* arbitration timeouts, in jiffies */
> >Â #define ARB_TIMEOUTÂ (HZ / 8)Â Â Â Â /* 125 ms until forcing bus ownership */
> >Â #define ARB2_TIMEOUT (HZ / 4)Â Â Â Â /* 250 ms until acquisition failure */
> > @@ -73,6 +100,7 @@
>
> >Â enum chip_name {
> >Â Â Â Âpca9541,
> > +Â Â Âpca9641,
> >Â };
>
> >Â struct chip_desc {
> > @@ -102,6 +130,21 @@ static bool pca9541_busoff(int ctl)
> >Â Â Â Âreturn (ctl & PCA9541_BUSON) == PCA9541_BUSON;
> >Â }
>
> > +static bool pca9641_lock_grant(int ctl)
> > +{
> > +Â Â Âreturn !!(ctl & PCA9641_CTL_LOCK_GRANT);
> > +}
> > +
> > +static bool pca9641_other_lock(int sts)
> > +{
> > +Â Â Âreturn !!(sts & PCA9641_STS_OTHER_LOCK);
> > +}
> > +
> > +static bool pca9641_busoff(int ctl, int sts)
> > +{
> > +Â Â Âreturn !pca9641_lock_grant(ctl) && !pca9641_other_lock(sts);
> > +}
> > +
> >Â /*
> >Â Â* Write to chip register. Don't use i2c_transfer()/i2c_smbus_xfer()
> >Â Â* as they will try to lock the adapter a second time.
> > @@ -256,6 +299,86 @@ static int pca9541_arbitrate(struct i2c_client *client)
> >Â Â Â Âreturn 0;
> >Â }
>
> > +/* Release bus. */
> > +static void pca9641_release_bus(struct i2c_client *client)
> > +{
> > +Â Â Âpca9541_reg_write(client, PCA9641_CONTROL, 0);
>
> Should this release bus function really "clobber" the control bits
> PCA9641_CTL_IDLE_TIMER_DIS, PCA9641_CTL_SMBUS_DIS, PCA9641_CTL_PRIORITY?
> Yes yes, the driver never sets these bits so they are likely zero. But
> the driver doesn't reset the chip either, so some bootstrap code might
> have configured those bits...
>
> Also related to bus release, since the driver does not touch the
> reserve time register, and then clears the above bits, the only way
> to release the bus is if everything continues to work and the above
> pca9641_release_bus is in fact happening. But if the kernel crashes
> while hogging the bus, and fails to come up, then the other master
> has no way of stealing the ownership. I really feel that the driver
> should make use of the timers so that the arbiter releases the bus
> automatically on catastrophic failure. But maybe I plain and simple
> just misunderstand the datasheet?
>
> > +}
> > +
> > +/*
> > + * Channel arbitration
> > + *
> > + * Return values:
> > + *Â <0: error
> > + *Â 0 : bus not acquired
> > + *Â 1 : bus acquired
> > + */
> > +static int pca9641_arbitrate(struct i2c_client *client)
> > +{
> > +Â Â Âstruct i2c_mux_core *muxc = i2c_get_clientdata(client);
> > +Â Â Âstruct pca9541 *data = i2c_mux_priv(muxc);
> > +Â Â Âint reg_ctl, reg_sts;
> > +
> > +Â Â Âreg_ctl = pca9541_reg_read(client, PCA9641_CONTROL);
> > +Â Â Âif (reg_ctl < 0)
> > +Â Â Â Â Â Â Âreturn reg_ctl;
> > +Â Â Âreg_sts = pca9541_reg_read(client, PCA9641_STATUS);
> > +
> > +Â Â Âif (pca9641_busoff(reg_ctl, reg_sts)) {
> > +Â Â Â Â Â Â Â/*
> > +Â Â Â Â Â Â Â * Bus is off. Request ownership or turn it on unless
> > +Â Â Â Â Â Â Â * other master requested ownership.
> > +Â Â Â Â Â Â Â */
> > +Â Â Â Â Â Â Âreg_ctl |= PCA9641_CTL_LOCK_REQ;
> > +Â Â Â Â Â Â Âpca9541_reg_write(client, PCA9641_CONTROL, reg_ctl);
> > +Â Â Â Â Â Â Âreg_ctl = pca9541_reg_read(client, PCA9641_CONTROL);
> > +
> > +Â Â Â Â Â Â Âif (pca9641_lock_grant(reg_ctl)) {
> > +Â Â Â Â Â Â Â Â Â Â Â/*
> > +Â Â Â Â Â Â Â Â Â Â Â * Other master did not request ownership,
> > +Â Â Â Â Â Â Â Â Â Â Â * or arbitration timeout expired. Take the bus.
> > +Â Â Â Â Â Â Â Â Â Â Â */
> > +Â Â Â Â Â Â Â Â Â Â Âreg_ctl |= PCA9641_CTL_BUS_CONNECT |
> > +Â Â Â Â Â Â Â Â Â Â Â Â Â Â ÂPCA9641_CTL_LOCK_REQ;
> > +Â Â Â Â Â Â Â Â Â Â Âpca9541_reg_write(client, PCA9641_CONTROL, reg_ctl);
> > +Â Â Â Â Â Â Â Â Â Â Âdata->select_timeout = SELECT_DELAY_SHORT;
> > +
> > +Â Â Â Â Â Â Â Â Â Â Âreturn 1;
> > +Â Â Â Â Â Â Â}
> > +
> > +Â Â Â Â Â Â Â/*
> > +Â Â Â Â Â Â Â * Other master requested ownership.
> > +Â Â Â Â Â Â Â * Set extra long timeout to give it time to acquire it.
> > +Â Â Â Â Â Â Â */
> > +Â Â Â Â Â Â Âdata->select_timeout = SELECT_DELAY_LONG * 2;
> > +
> > +Â Â Â Â Â Â Âreturn 0;
> > +Â Â Â}
> > +
> > +Â Â Âif (pca9641_lock_grant(reg_ctl)) {
> > +Â Â Â Â Â Â Â/*
> > +Â Â Â Â Â Â Â * Bus is on, and we own it. We are done with acquisition.
> > +Â Â Â Â Â Â Â */
> > +Â Â Â Â Â Â Âreg_ctl |= PCA9641_CTL_BUS_CONNECT | PCA9641_CTL_LOCK_REQ;
> > +Â Â Â Â Â Â Âpca9541_reg_write(client, PCA9641_CONTROL, reg_ctl);
> > +
> > +Â Â Â Â Â Â Âreturn 1;
> > +Â Â Â}
> > +
> > +Â Â Âif (pca9641_other_lock(reg_sts)) {
> > +Â Â Â Â Â Â Â/*
> > +Â Â Â Â Â Â Â * Other master owns the bus.
> > +Â Â Â Â Â Â Â * If arbitration timeout has expired, force ownership.
> > +Â Â Â Â Â Â Â * Otherwise request it.
>
> This comment is stale. Reading the data sheet, I find no way to force
> ownership with the PCA9641 (as indicated above in the release_bus
> review comment). But I have only browsed the data sheet so I could
> easily be mistaken...
>
> [time passes]
>
> Ahhh, wait, it could reset the chip to get a new chance to get ownership.
> But that will reset all registers for the other master as well, since I
> read it as if the reset is chip-global and not master-local with minimal
> effects on the other master. So, a big hammer indeed.
>
> Cheers,
> Peter
>
> > +Â Â Â Â Â Â Â */
> > +Â Â Â Â Â Â Âdata->select_timeout = SELECT_DELAY_LONG;
> > +Â Â Â Â Â Â Âreg_ctl |= PCA9641_CTL_LOCK_REQ;
> > +Â Â Â Â Â Â Âpca9541_reg_write(client, PCA9641_CONTROL, reg_ctl);
> > +Â Â Â}
> > +
> > +Â Â Âreturn 0;
> > +}
> > +
> >Â static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan)
> >Â {
> >Â Â Â Âstruct pca9541 *data = i2c_mux_priv(muxc);
> > @@ -295,10 +418,15 @@ static const struct chip_desc chips[] = {
> >Â Â Â Â Â Â Â Â.arbitrate = pca9541_arbitrate,
> >Â Â Â Â Â Â Â Â.release_bus = pca9541_release_bus,
> >Â Â Â Â},
> > +Â Â Â[pca9641] = {
> > +Â Â Â Â Â Â Â.arbitrate = pca9641_arbitrate,
> > +Â Â Â Â Â Â Â.release_bus = pca9641_release_bus,
> > +Â Â Â},
> >Â };
>
> >Â static const struct i2c_device_id pca9541_id[] = {
> >Â Â Â Â{ "pca9541", pca9541 },
> > +Â Â Â{ "pca9641", pca9641 },
> >Â Â Â Â{}
> >Â };
>
> > @@ -307,6 +435,7 @@ MODULE_DEVICE_TABLE(i2c, pca9541_id);
> >Â #ifdef CONFIG_OF
> >Â static const struct of_device_id pca9541_of_match[] = {
> >Â Â Â Â{ .compatible = "nxp,pca9541", .data = &chips[pca9541] },
> > +Â Â Â{ .compatible = "nxp,pca9641", .data = &chips[pca9641] },
> >Â Â Â Â{}
> >Â };
> >Â MODULE_DEVICE_TABLE(of, pca9541_of_match);
> > @@ -392,5 +521,5 @@ static struct i2c_driver pca9541_driver = {
> >Â module_i2c_driver(pca9541_driver);
>
> >Â MODULE_AUTHOR("Guenter Roeck <linux@xxxxxxxxxxxx <mailto:linux@xxxxxxxxxxxx>>");
> > -MODULE_DESCRIPTION("PCA9541 I2C master selector driver");
> > +MODULE_DESCRIPTION("PCA9541/PCA9641 I2C master selector driver");
> >Â MODULE_LICENSE("GPL v2");
> >
>