RE: [PATCH] i2c: imx: add I2C_M_REV_DIR_ADDR and I2C_M_NOSTART
"Carlos Song (OSS)" <[email protected]> Wed, 12 Aug 2026 09:20:25 +0000
| Newsgroups | org.kernel.vger.linux-i2c,dev.linux.lists.imx,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <AM0PR04MB680259939B7ED9AF5BA5957BE8DC2@AM0PR04MB6802.eurprd04.prod.outlook.com> |
> -----Original Message----- > From: Vincent Jardin <[email protected]> > Sent: Wednesday, August 12, 2026 1:47 PM > To: Carlos Song (OSS) <[email protected]> > Cc: Oleksij Rempel <[email protected]>; Pengutronix Kernel Team > <[email protected]>; Andi Shyti <[email protected]>; Frank Li > <[email protected]>; Sascha Hauer <[email protected]>; Fabio > Estevam <[email protected]>; [email protected]; > [email protected]; [email protected]; > [email protected] > Subject: Re: [PATCH] i2c: imx: add I2C_M_REV_DIR_ADDR and > I2C_M_NOSTART > > Hi Carlos, > > Thanks a lot for the review, and no problem for the delay. > > > This is a such rare i2c frame design in the Realtek RTL8366SE SMI read > > frame, right? > > It is rare, but I have been inspired for I2C_M_REV_DIR_ADDR by the source of > two client drivers that use it. > > Note that I2C_M_NOSTART is not rare at all. It is what regmap uses for a real > gather write. regmap_i2c_gather_write() opens with > > if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART)) > return -ENOTSUPP; > > FYI, some clients that need REV_DIR_ADDR and NOSTART, > > drivers/input/joystick/as5011.c > drivers/video/fbdev/matrox/matroxfb_maven.c > > both build the identical 2-message pattern, > > i2c_check_functionality(adapter, > I2C_FUNC_NOSTART | > I2C_FUNC_PROTOCOL_MANGLING) > > I do not have those devices, so I did not check it beside code readings. > > Some clients that need NOSTART alone, > > drivers/base/regmap/regmap-i2c.c > drivers/infiniband/hw/hfi1/qsfp.c > drivers/gpu/drm/i915/display/dvo_ivch.c > > About the i2c masters, that use I2C_M_REV_DIR_ADDR in code, > > drivers/i2c/algos/i2c-algo-bit.c > drivers/i2c/algos/i2c-algo-pcf.c > drivers/i2c/busses/i2c-s3c2410.c <- the model I did investigate > drivers/i2c/busses/i2c-tegra-bpmp.c > drivers/media/pci/cobalt/cobalt-i2c.c > > So a taxnonomy can be, > > adapter REV_DIR impl MANGLING adv NOSTART adv > usable > i2c-algo-bit yes yes yes > yes > i2c-algo-pcf yes yes no > no > i2c-s3c2410 yes yes yes > yes > i2c-tegra-bpmp yes yes yes > yes > cobalt-i2c yes (private adapter, not exposed) > i2c-brcmstb no yes yes > no > i2c-pxa no yes yes > no > i2c-tegra no yes yes (cond) > no > i2c-imx (this patch) yes yes yes > yes > > > Is Realtek RTL8366SE SMI driver upstream? Can I found the driver? > > Not yet, and I cannot point you at a tree today. It is Realtek's "Unmanaged > Switch" DSA driver, which is not public yet. > > What I can share is the message construction, which is the part you asked > about and is not Realtek-specific. > > static int as5011_i2c_read(struct i2c_client *client, > uint8_t aregaddr, signed char > *value) > { > uint8_t data[2] = { aregaddr }; > struct i2c_msg msg_set[2] = { > { > .addr = client->addr, > .flags = I2C_M_REV_DIR_ADDR, > .len = 1, > .buf = (uint8_t *)data > }, > { > .addr = client->addr, > .flags = I2C_M_RD | > I2C_M_NOSTART, > .len = 1, > .buf = (uint8_t *)data > } > }; > int error; > > error = i2c_transfer(client->adapter, msg_set, 2); > if (error < 0) > return error; > > *value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0]; > return 0; > } > > And the Realtek accessor, which is the same two messages with a 2-byte > register and 2-byte data instead of 1 and 1: > > u8 ra[2] = { reg & 0xff, (reg >> 8) & 0xff }; > u8 data[2] = { 0xff, 0xff }; > struct i2c_msg msgs[2] = { > { > .addr = client->addr, > .flags = I2C_M_REV_DIR_ADDR, > .len = sizeof(ra), > .buf = ra, > }, { > .addr = client->addr, > .flags = I2C_M_RD | I2C_M_NOSTART, > .len = sizeof(data), > .buf = data, > }, > }; > > ret = i2c_transfer(client->adapter, msgs, 2); > if (ret != 2) > return ret < 0 ? ret : -EIO; > > *val = data[0] | (data[1] << 8); > > msgs[0] is a write message carrying I2C_M_REV_DIR_ADDR, so the address > byte goes out with the read bit set while the master keeps transmitting the > two register-address bytes. msgs[1] is the read half with I2C_M_NOSTART, so > no repeated start is emitted and the controller simply turns the bus around. > Writes are an ordinary unflagged 4-byte write and need nothing from this > patch. > > > ... Have you test this i2c-imx feature in your LS board with RTL8366SE? > > Yes, on an LX2160A board carrying four RTL8366SE-CG. Two are strapped to > the chip's 2-wire "EEPROM SMI" mode and hang off hardware i2c-imx > controllers; the other two are on MDIO, for both the same switch registers are > reachable both ways and should answer the same values. > > First, the failure on an unpatched kernel, which is reproducible with nothing > but i2ctools and is I think the clearest way to see the problem. > These two commands are byte-identical: > > # i2ctransfer -y -f -a 1 w2@0x7c 0x00 0x13 r4@0x7c > 0xff 0xff 0x00 0x00 > # i2ctransfer -y -f -a 1 r6@0x7c # no preceding write at all > 0xff 0xff 0x00 0x00 0x00 0x00 > > Then, with this patch both chips answer correctly. For testing this patch, I did > use the following that I have just pushed to help, for the record: > > https://github.com/vjardin/smi-probe > > # smi-probe -t i2c -b /dev/i2c-1 -a 0x7c id > chip_num 0x6980 CHIP_RTL8367E (inside RTL8366SE-CG) > chip_ver 0x0030 > svlan_tpid 0x88a8 reset default, as expected > > # smi-probe -t i2c -b /dev/i2c-1 -a 0x7c rd 0x1300 > reg 0x1300 = 0x6980 > # smi-probe -t i2c -b /dev/i2c-1 -a 0x7c rd 0x1202 > reg 0x1202 = 0x88a8 > > # smi-probe -t i2c -b /dev/i2c-2 -a 0x5c rd 0x1202 # U19, IIC3 > reg 0x1202 = 0x88a8 > # smi-probe -t mdio -b 0x8b97000 -a 0x1a rd 0x1202 # MDIO > control > reg 0x1202 = 0x88a8 > > Then it binds as a DSA switch and enumerates its four user ports: > > realtek-US-switch-dsa-i2c 2-005c: RTK DSA unit 0 (EEPROM SMI, > addr 0x5c) > > I have many other i2c devices on this board, no regression. > > Thanks again for your review, > Vincent Hi, Vincent Thank you very much for such clear instructions. That helps lot to understand what happened. Follow this, I also spend some time to learn and understand this case. 1. Why need I2C_M_REV_DIR_ADDR but not directly read? The msg with I2C_M_REV_DIR_ADDR is a write msg in fact, it is a write message, master write data and target ack, only w bit is toggled to r bit. Because the device don't follow the standard I2C spec, it need this special frame to write: [1] S + addr + r + [ACK] + data +[ACK] + data... Yes, not wrong, a read bit on bus but it is a write msg for this device. But the standard spec write msg is [2] S + addr + w +[ACK] +data + [ACK] +data So add this I2C_M_REV_DIR_ADDR flag to make this write msg w bit is changed to r bit, msg [2] become msg [1] so the device worked. Only change the r/w bit, other data flow keep write logic. 2. What I2C_M_NOSTART is doing? It help skip the msg repeat start header, next msg start from data. If the first msg setting this flag, it should be unsupported(so you add a loop check to make sure the first msg without I2C_M_NOSTART flag). 3. The msg with I2C_M_NOSTART should keep the same direction with pre msg? No, not need. For example, RTL8366SE frame: (1){S Addr Rd [A] reg[7:0] [A] reg[15:8] [A]} (2){ [data[7:0]] A [data[15:8]] NA} P For (1): {S Addr Wr [A] reg[7:0] [A] reg[15:8] [A] is a write msg and "change the Wr bit to Rd bit" by I2C_M_REV_DIR_ADDR. So i2c bus signal is {S Addr Rd [A] reg[7:0] [A] reg[15:8] [A]} For (2): {Rs Addr Rd [A] [data[7:0]] A [data[15:8]] NA} is a read msg and "skip the {Rs Addr Rd [A]}" by I2C_M_NOSTART. So i2c bus signal is [data[7:0]] A [data[15:8]] NA. This is a write msg then a read msg. So you add the I2C_M_NOSTART flag handle in these 4 path: atomic read/irq read/atomic write/irq write. Do I understand right? About code, Could this part of the code be improved? Of course, that's a matter of personal preference. @@ -1583,6 +1622,27 @@ static int i2c_imx_xfer_common(struct i2c_adapter *adapter, struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); int use_dma = 0; + for (i = 0; i < num; i++) { + /* + * I2C_FUNC_PROTOCOL_MANGLING is advertised for + * I2C_M_REV_DIR_ADDR only; reject the flags it also covers but + * that this driver does not implement, rather than silently + * transferring something the caller did not ask for. + */ + if (msgs[i].flags & (I2C_M_IGNORE_NAK | I2C_M_NO_RD_ACK | + I2C_M_STOP)) + return -EOPNOTSUPP; + + if (!(msgs[i].flags & I2C_M_NOSTART)) + continue; + + /* Nothing to continue from, and a zero-length continuation + * would leave the controller with no byte to clock. + */ + if (i == 0 || msgs[i].len == 0) + return -EOPNOTSUPP; + } + /* Start I2C transfer */ result = i2c_imx_start(i2c_imx, atomic); if (result) { I prefer this: 1. Do a single function for this check. static int i2c_imx_check_msgs() 2. Add macro definition. #define I2C_IMX_UNSUPPORTED_PROTOCOL_MANGLING_FLAGS \ (I2C_M_IGNORE_NAK | I2C_M_NO_RD_ACK | I2C_M_STOP) 3. drop continue and use: /* NOSTART-specific sanity checks. */ if (msgs[i].flags & I2C_M_NOSTART) { /* * NOSTART continues an already open frame. The first * message has nothing to continue from, and a * zero-length continuation leaves the controller with * no byte to clock. */ if (i == 0 || msgs[i].len == 0) return -EOPNOTSUPP; } It is up to you. All in all, I think this patch is nice for me and thank you again for the improvements. Carlos Song