* [PATCH 0/3] I3C character device driver using driver_override
@ 2026-09-11 21:09 Meagan Lloyd
2026-09-11 21:09 ` [PATCH 1/3] i3c: master: enable driver_override for I3C Meagan Lloyd
` (3 more replies)
0 siblings, 4 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-11 21:09 UTC (permalink / raw)
To: linux-i3c
Cc: meaganlloyd, alexandre.belloni, vitor.soares, samagazaryan,
gregkh, arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
This is a rework and revival option for Vitor Soares' I3C character
device driver patch series from 2020 [1] that I've been exploring for a
few months. Recently there was a revival posted to the list [2], so I
wanted to share this design option as well.
In [1] and [2], the i3cdev driver automatically attaches and detaches
depending whether another driver has attached/not. In [1], Boris was
suggesting we explore a more straightforward and traditional binding
method aligning with the Linux driver model. At the time, there wasn't
a way to auto-bind while keeping manual binding possible as they shared
the same match() hook. Now with the new driver_override feature, the
auto-binding of i3cdev on boot can be avoided if the i3cdev driver has
an empty match ID table. After boot, where specialized drivers would have
already bound, user-space can explicitly opt-in by setting the
driver_override sysfs file with 'i3cdev' and manually binding via sysfs
(or by simply loading the driver if it's loadable). This can also be
easily automated with udev rules that run whenever the I3C core exposes
a new device.
One downside of the automatic attach/de-attach is that if a different
driver is loaded later, the first driver could have altered something
on the device, breaking any assumptions of the subsequent driver.
My series builds on [1] through:
0. Addressing code review feedback in [1] from Greg, Boris, and Randy.
1. Using actual_len for accurate read response reporting. The kernel
will report actual_len received from the core to user-space via the
uapi i3cdev_xfer struct.
2. Placing limits on the number of transfers and bytes in requests to
prevent unlimited-sized transfers or kernel memory allocation
3. Checking inputs and descriptive return codes as guard-rails
for user-space and to ease use of the i3cdev driver
4. Checking on MWL to ensure that we respect device limits
5. Proper lifetime management of i3cdev_data and underlying device
6. Addressing dangling fops in the event we have an open file descriptor
when a device gets unbound.
7. Fast-path locking to ensure transfers complete before a device is
unbound.
8. Allowing only one file descriptor per I3C device to avoid bugs
around multiple processes interacting with the device and altering
the device underneath the other. For example, without this, one process
could change the device's page or address pointer register underneath
the other process.
9. copy_struct_from_user to ensure struct i3cdev_xfer could be extended
in a compatible way. This is to be forward-looking towards potential
HDR mode expansion and code reuse.
10. Reserving the IOCTL number formally
11. Updating the Documentation to be a syntax correct example program
template.
12. Preserving /dev/bus/i3c/<bus id>-<Provisional ID> naming while
allowing sysfs path to be neatly named i3cdev-<minor>. This avoids
repeated <bus id>-<Provisional ID> in the sysfs paths which can be
confusing/circular-looking.
e.g. /sys/bus/i3c/devices/0-deadbeef001/i3cdev/0-deadbeef001 ->
/sys/bus/i3c/devices/0-deadbeef001/i3cdev/i3cdev-0
13. Updating all naming references related to i3c_priv_xfer to align
with new i3c_xfer struct
14. Updating the MAINTAINERS file for the new pieces of code
Note that i3c-tools [3] or a fork of it will need small updates:
1. Update include/uapi/linux/i3c/i3cdev.h to match updated uapi structs
2. In i3ctransfer.c, use actual_len for reads
I've added MODULE_VERSION("1.0.0") in the i3cdev driver, so i3c-tools
could use that to determine whether to use the old out-of-tree uapi or this one.
[1] https://lore.kernel.org/linux-i3c/cover.1582069402.git.vitor.soares@synopsys.com/
[2] https://lore.kernel.org/linux-i3c/ap_1-gFF7S821xJT@ninjato/T/#m9107c1785a4a16b1b3cb84c3269d17fac35819c8
[3] https://github.com/vitor-soares-snps/i3c-tools
Meagan Lloyd (3):
i3c: master: enable driver_override for I3C
i3c: set i3c_xfer.actual_len in controller drivers
i3c: add i3cdev character device module for user-space access
Documentation/userspace-api/i3c/i3cdev.rst | 152 ++++
.../userspace-api/ioctl/ioctl-number.rst | 1 +
MAINTAINERS | 7 +
drivers/hwmon/lm75.c | 7 +-
drivers/hwmon/tmp108.c | 2 +
drivers/i3c/Kconfig | 12 +
drivers/i3c/Makefile | 1 +
drivers/i3c/i3cdev.c | 730 ++++++++++++++++++
drivers/i3c/master.c | 6 +
drivers/i3c/master/adi-i3c-master.c | 5 +-
drivers/i3c/master/dw-i3c-master.c | 4 +-
drivers/i3c/master/i3c-master-cdns.c | 5 +-
drivers/i3c/master/mipi-i3c-hci/core.c | 5 +-
drivers/i3c/master/renesas-i3c.c | 3 +
drivers/iio/adc/ad4062.c | 10 +-
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c | 3 +
include/uapi/linux/i3c/i3cdev.h | 70 ++
17 files changed, 1016 insertions(+), 7 deletions(-)
create mode 100644 Documentation/userspace-api/i3c/i3cdev.rst
create mode 100644 drivers/i3c/i3cdev.c
create mode 100644 include/uapi/linux/i3c/i3cdev.h
base-commit: cab40cfc9e116acd4d60f95b4b1264cab78f3803
--
2.49.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 1/3] i3c: master: enable driver_override for I3C
2026-09-11 21:09 [PATCH 0/3] I3C character device driver using driver_override Meagan Lloyd
@ 2026-09-11 21:09 ` Meagan Lloyd
2026-09-11 21:36 ` Guenter Roeck
` (2 more replies)
2026-09-11 21:09 ` [PATCH 2/3] i3c: set i3c_xfer.actual_len in controller drivers Meagan Lloyd
` (2 subsequent siblings)
3 siblings, 3 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-11 21:09 UTC (permalink / raw)
To: linux-i3c
Cc: meaganlloyd, alexandre.belloni, vitor.soares, samagazaryan,
gregkh, arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
Opt-in to driver_override sysfs functionality for I3C subsystem.
Update some I3C drivers that are assuming that if their .probe is
invoked that the bus match function already checked the ID table. Go
ahead and verify in .probe that i3c_device_match_id() returns a valid
match before using the i3c_device_id pointer.
Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
Assisted-by: Claude:claude-opus-4.8 copilot
---
drivers/hwmon/lm75.c | 7 ++++++-
drivers/hwmon/tmp108.c | 2 ++
drivers/i3c/master.c | 6 ++++++
drivers/iio/adc/ad4062.c | 10 ++++++++--
drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c | 3 +++
5 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index 104149a03bad..0d3c843d1823 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -888,6 +888,7 @@ MODULE_DEVICE_TABLE(i3c, lm75_i3c_ids);
static int lm75_i3c_probe(struct i3c_device *i3cdev)
{
struct device *dev = i3cdev_to_dev(i3cdev);
+ const struct i3c_device_id *id;
const struct lm75_i3c_device *id_data;
struct regmap *regmap;
@@ -895,7 +896,11 @@ static int lm75_i3c_probe(struct i3c_device *i3cdev)
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- id_data = i3c_device_match_id(i3cdev, lm75_i3c_ids)->data;
+ id = i3c_device_match_id(i3cdev, lm75_i3c_ids);
+ if (!id)
+ return -ENODEV;
+
+ id_data = id->data;
return lm75_generic_probe(dev, id_data->name, id_data->type, 0, regmap);
}
diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c
index 1c4a58855e2d..98076ae8dce8 100644
--- a/drivers/hwmon/tmp108.c
+++ b/drivers/hwmon/tmp108.c
@@ -581,6 +581,8 @@ static int p3t1085_i3c_probe(struct i3c_device *i3cdev)
"Failed to register i3c regmap\n");
id = i3c_device_match_id(i3cdev, p3t1085_i3c_ids);
+ if (!id)
+ return -ENODEV;
return tmp108_common_probe(dev, regmap, "p3t1085_i3c", id->data);
}
diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index afcd7a21a3e6..42bc9a923d81 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -340,10 +340,15 @@ static int i3c_device_match(struct device *dev, const struct device_driver *drv)
struct i3c_device *i3cdev;
const struct i3c_driver *i3cdrv;
u8 static_addr_method = 0;
+ int ret;
if (dev->type != &i3c_device_type)
return 0;
+ ret = device_match_driver_override(dev, drv);
+ if (ret >= 0)
+ return ret;
+
i3cdev = dev_to_i3cdev(dev);
i3cdrv = drv_to_i3cdrv(drv);
@@ -890,6 +895,7 @@ const struct bus_type i3c_bus_type = {
.probe = i3c_device_probe,
.remove = i3c_device_remove,
.shutdown = i3c_device_shutdown,
+ .driver_override = true,
};
EXPORT_SYMBOL_GPL(i3c_bus_type);
diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
index 8e5984055b15..f28b503d16eb 100644
--- a/drivers/iio/adc/ad4062.c
+++ b/drivers/iio/adc/ad4062.c
@@ -1482,14 +1482,20 @@ MODULE_DEVICE_TABLE(i3c, ad4062_id_table);
static int ad4062_probe(struct i3c_device *i3cdev)
{
- const struct i3c_device_id *id = i3c_device_match_id(i3cdev, ad4062_id_table);
- const struct ad4062_chip_info *chip = id->data;
+ const struct i3c_device_id *id;
+ const struct ad4062_chip_info *chip;
struct device *dev = &i3cdev->dev;
struct iio_dev *indio_dev;
struct ad4062_state *st;
bool ref_sel;
int ret;
+ id = i3c_device_match_id(i3cdev, ad4062_id_table);
+ if (!id)
+ return -ENODEV;
+
+ chip = id->data;
+
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
index cb5c5d7e1f3d..153a03e06801 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
@@ -32,6 +32,9 @@ static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev)
struct device *dev = i3cdev_to_dev(i3cdev);
struct regmap *regmap;
+ if (!id)
+ return -ENODEV;
+
regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap));
--
2.49.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 2/3] i3c: set i3c_xfer.actual_len in controller drivers
2026-09-11 21:09 [PATCH 0/3] I3C character device driver using driver_override Meagan Lloyd
2026-09-11 21:09 ` [PATCH 1/3] i3c: master: enable driver_override for I3C Meagan Lloyd
@ 2026-09-11 21:09 ` Meagan Lloyd
2026-09-13 0:26 ` Jonathan Cameron
2026-09-11 21:09 ` [PATCH 3/3] i3c: add i3cdev character device module for user-space access Meagan Lloyd
2026-09-12 13:26 ` [PATCH 0/3] I3C character device driver using driver_override Andy Shevchenko
3 siblings, 1 reply; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-11 21:09 UTC (permalink / raw)
To: linux-i3c
Cc: meaganlloyd, alexandre.belloni, vitor.soares, samagazaryan,
gregkh, arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
Set i3c_xfer.actual_len for reads across the I3C Controller drivers.
actual_len provides a consistent location for device drivers to know the
read response bytes without overriding the len field.
Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
---
drivers/i3c/master/adi-i3c-master.c | 5 ++++-
drivers/i3c/master/dw-i3c-master.c | 4 +++-
drivers/i3c/master/i3c-master-cdns.c | 5 ++++-
drivers/i3c/master/mipi-i3c-hci/core.c | 5 ++++-
drivers/i3c/master/renesas-i3c.c | 3 +++
5 files changed, 18 insertions(+), 4 deletions(-)
diff --git a/drivers/i3c/master/adi-i3c-master.c b/drivers/i3c/master/adi-i3c-master.c
index b35386260350..d033bfd4ecc2 100644
--- a/drivers/i3c/master/adi-i3c-master.c
+++ b/drivers/i3c/master/adi-i3c-master.c
@@ -412,8 +412,11 @@ static int adi_i3c_master_i3c_xfers(struct i3c_dev_desc *dev,
ret = xfer->ret;
- for (i = 0; i < nxfers; i++)
+ for (i = 0; i < nxfers; i++) {
xfers[i].err = adi_i3c_cmd_get_err(&xfer->cmds[i]);
+ if (xfers[i].rnw)
+ xfers[i].actual_len = xfer->cmds[i].rx_len;
+ }
return ret;
}
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 4563d8761ba0..54406e31c870 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -1028,8 +1028,10 @@ static int dw_i3c_master_i3c_xfers(struct i3c_dev_desc *dev,
for (i = 0; i < i3c_nxfers; i++) {
struct dw_i3c_cmd *cmd = &xfer->cmds[i];
- if (i3c_xfers[i].rnw)
+ if (i3c_xfers[i].rnw) {
i3c_xfers[i].len = cmd->rx_len;
+ i3c_xfers[i].actual_len = cmd->rx_len;
+ }
}
ret = xfer->ret;
diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index 2d98c1ce9b12..331062c10b31 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -795,8 +795,11 @@ static int cdns_i3c_master_i3c_xfers(struct i3c_dev_desc *dev,
ret = cdns_xfer->ret;
- for (i = 0; i < nxfers; i++)
+ for (i = 0; i < nxfers; i++) {
xfers[i].err = cdns_i3c_cmd_get_err(&cdns_xfer->cmds[i]);
+ if (xfers[i].rnw)
+ xfers[i].actual_len = cdns_xfer->cmds[i].rx_len;
+ }
cdns_i3c_master_free_xfer(cdns_xfer);
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index dadf049bd4b5..29fa68e29484 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -508,8 +508,11 @@ static int i3c_hci_i3c_xfers(struct i3c_dev_desc *dev,
if (ret)
goto out;
for (i = 0; i < nxfers; i++) {
- if (i3c_xfers[i].rnw)
+ if (i3c_xfers[i].rnw) {
i3c_xfers[i].len = RESP_DATA_LENGTH(xfer[i].response);
+ i3c_xfers[i].actual_len = RESP_DATA_LENGTH(xfer[i].response);
+ }
+
if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) {
ret = -EIO;
goto out;
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
index 28c0927a0179..a3dcb4cd9b0e 100644
--- a/drivers/i3c/master/renesas-i3c.c
+++ b/drivers/i3c/master/renesas-i3c.c
@@ -918,6 +918,9 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_
time_left = renesas_i3c_wait_xfer(i3c, xfer);
if (!time_left)
xfer_failed = true;
+
+ if (i3c_xfers[i].rnw)
+ i3c_xfers[i].actual_len = cmd->rx_count;
}
if (xfer_failed)
--
2.49.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [PATCH 3/3] i3c: add i3cdev character device module for user-space access
2026-09-11 21:09 [PATCH 0/3] I3C character device driver using driver_override Meagan Lloyd
2026-09-11 21:09 ` [PATCH 1/3] i3c: master: enable driver_override for I3C Meagan Lloyd
2026-09-11 21:09 ` [PATCH 2/3] i3c: set i3c_xfer.actual_len in controller drivers Meagan Lloyd
@ 2026-09-11 21:09 ` Meagan Lloyd
2026-09-11 23:29 ` Randy Dunlap
2026-09-12 13:34 ` Andy Shevchenko
2026-09-12 13:26 ` [PATCH 0/3] I3C character device driver using driver_override Andy Shevchenko
3 siblings, 2 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-11 21:09 UTC (permalink / raw)
To: linux-i3c
Cc: meaganlloyd, alexandre.belloni, vitor.soares, samagazaryan,
gregkh, arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
The i3cdev driver is a character device driver that allows user-space
to control and interact with I3C devices.
Currently, it has the ability to perform Single Data Rate (SDR)
transfers - basic reads/writes.
With the addition of sysfs driver_override, there is now a
straightforward and direct way to match the i3cdev driver to any i3c
device without stepping on the toes of more specialized drivers that are
loaded automatically.
This is accomplished by the i3cdev driver not having any entries in the
i3c_device_id table. After boot, simply set the driver_override to
"i3cdev" and bind the device manually via the sysfs bind knob. This can
also be automated with udev rules as well.
The character device interface will be exposed at:
/dev/bus/i3c/<bus id>-<Provisional ID>
Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
Assisted-by: Claude:claude-opus-4.8 copilot
---
Documentation/userspace-api/i3c/i3cdev.rst | 152 ++++
.../userspace-api/ioctl/ioctl-number.rst | 1 +
MAINTAINERS | 7 +
drivers/i3c/Kconfig | 12 +
drivers/i3c/Makefile | 1 +
drivers/i3c/i3cdev.c | 730 ++++++++++++++++++
include/uapi/linux/i3c/i3cdev.h | 70 ++
7 files changed, 973 insertions(+)
create mode 100644 Documentation/userspace-api/i3c/i3cdev.rst
create mode 100644 drivers/i3c/i3cdev.c
create mode 100644 include/uapi/linux/i3c/i3cdev.h
diff --git a/Documentation/userspace-api/i3c/i3cdev.rst b/Documentation/userspace-api/i3c/i3cdev.rst
new file mode 100644
index 000000000000..ad246bf28919
--- /dev/null
+++ b/Documentation/userspace-api/i3c/i3cdev.rst
@@ -0,0 +1,152 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+====================
+I3C Device Interface
+====================
+
+This interface allows access to I3C devices from userspace.
+
+As I3C devices adhere to the I3C protocol, simple transfers can be accomplished
+using a generic driver. Currently, this interface supports private Single Data
+Rate (SDR) read and write transfers.
+
+The i3cdev module will not auto-bind to devices. Userspace needs to explicitly
+bind the device to the driver. This is to avoid interfering with the binding of
+specialized drivers.
+
+Once bound, a character device interface will be created at:
+/dev/bus/i3c/<bus id>-<Provisional ID>.
+
+====================
+Usage
+====================
+
+Any discovered I3C devices by the I3C subsystem will have device folders under /sys/bus/i3c/devices/<bus id>-<Provisional ID>.
+To allow binding of a device with i3cdev driver, set the driver_override:
+
+::
+
+ # echo "i3cdev" > /sys/bus/i3c/devices/<bus id>-<Provisional ID>/driver_override
+
+If the i3cdev driver is not yet loaded, load it and it will cause the driver to bind
+to any devices with the override in place.
+
+If the i3cdev driver is already loaded, go ahead and perform a manual bind:
+
+::
+
+ # echo "<bus id>-<Provisional ID>" > /sys/bus/i3c/drivers/i3cdev/bind
+
+Set driver override
+::
+
+ # echo "i3cdev" > /sys/bus/i3c/devices/0-deadbeef001/driver_override
+ # echo "i3cdev" > /sys/bus/i3c/devices/0-deadbeef002/driver_override
+
+Bind the device to the driver
+::
+
+ # echo "0-deadbeef001" > /sys/bus/i3c/drivers/i3cdev/bind
+ # echo "0-deadbeef002" > /sys/bus/i3c/drivers/i3cdev/bind
+
+Observe the resulting character device files under /dev/bus/i3c/
+::
+
+ # ls -ltr /dev/bus/i3c/
+ crw------- 1 root root 235, 1 Jun 30 17:49 0-deadbeef002
+ crw------- 1 root root 235, 0 Jun 30 17:49 0-deadbeef001
+
+BASIC CHARACTER DEVICE API
+===============================
+The API supports private Single Data Rate (SDR) read and write transfers.
+Those transaction can be achieved by the following:
+
+``read(file, buffer, sizeof(buffer))``
+ The standard read() operation will work as a simple transaction of private
+ SDR read data followed a stop.
+ Return the number of bytes read on success, and a negative error otherwise.
+
+``write(file, buffer, sizeof(buffer))``
+ The standard write() operation will work as a simple transaction of private
+ SDR write data followed a stop.
+ Return the number of bytes written on success, and a negative error otherwise.
+
+``ioctl(file, I3CDEV_XFER, struct i3cdev_xfers *xfers)``
+ It combines read/write transactions without a stop in between.
+ Return 0 on success, and a negative error otherwise.
+
+C EXAMPLE (PSEUDO CODE)
+=======================
+You need to open (and get a file descriptor) to /dev/bus/i3c/<bus id>-<Provisional ID>,
+do your operations (read, write, ioctl), and then close it.
+
+The following header files should be included in an I3C program::
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <i3c/i3cdev.h>
+#include <stdint.h>
+
+These additionally are used by this example::
+
+#include <stdlib.h>
+#include <assert.h>
+
+To work with an I3C device, one must call open and get a file descriptor::
+
+ int fd;
+
+ fd = open("/dev/bus/i3c/0-deadbeef001", O_RDWR);
+ if (fd < 0)
+ exit(EXIT_FAILURE);
+
+Now that the file is open, we can do some operations::
+
+ int ret;
+
+ /* Write function */
+ uint8_t buf[] = {0x00, 0xde, 0xad, 0xbe, 0xef};
+ ret = write(fd, buf, 5);
+ if (ret != 5) {
+ /* ERROR HANDLING: I3C transaction failed */
+ }
+
+ /* Read function */
+ ret = read(fd, buf, 4);
+ if (ret < 0) {
+ /* ERROR HANDLING: I3C transaction failed */
+ } else {
+ /* Iterate over buf[] to get the read data */
+ }
+
+ /* IOCTL function */
+ struct i3cdev_xfer xfers[2] = {0}; /* Must zero out for compatibility */
+ struct i3cdev_xfers xfers_metadata = { .nxfers = 2,
+ .xfers = (uintptr_t) xfers,
+ .xfer_size = sizeof(struct i3cdev_xfer)
+ };
+
+ uint8_t tx_buf[] = {0x00, 0xde, 0xad, 0xbe, 0xef};
+ uint8_t rx_buf[10];
+
+ xfers[0].data = (uintptr_t) tx_buf;
+ xfers[0].len = 5;
+ xfers[0].rnw = 0;
+ xfers[1].data = (uintptr_t) rx_buf;
+ xfers[1].len = 10;
+ xfers[1].rnw = 1;
+
+ ret = ioctl(fd, I3CDEV_XFER, (uintptr_t) &xfers_metadata);
+ if (ret < 0) {
+ /* ERROR HANDLING: I3C transaction failed */
+ } else {
+ /* For reads, optionally verify that the response matches expectations */
+ assert(xfers[1].len == xfers[1].actual_len);
+
+ /* For reads, iterate through response data using xfers[i].actual_len */
+ }
+
+The device can be closed when the open file descriptor is no longer required::
+
+ close(fd);
\ No newline at end of file
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 3f0ef1e27eb0..b40f3b3f63a6 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -81,6 +81,7 @@ Code Seq# Include File Comments
0x03 all linux/hdreg.h
0x04 D2-DC linux/umsdos_fs.h Dead since 2.6.11, but don't reuse these.
0x06 all linux/lp.h
+0x07 00-9E linux/i3c/i3cdev.h i3cdev driver for I3C subystem
0x07 9F-D0 linux/vmw_vmci_defs.h, uapi/linux/vm_sockets.h
0x09 all linux/raid/md_u.h
0x10 00-0F drivers/char/s390/vmcp.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 81a9a02c919d..5c75a3585d23 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12350,6 +12350,13 @@ S: Orphan
F: Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml
F: drivers/i3c/master/dw*
+I3C CHARACTER DEVICE DRIVER
+M: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
+S: Supported
+F: Documentation/userspace-api/i3c/i3cdev.rst
+F: drivers/i3c/i3cdev.c
+F: include/uapi/linux/i3c/i3cdev.h
+
I3C SUBSYSTEM
M: Alexandre Belloni <alexandre.belloni@bootlin.com>
R: Frank Li <Frank.Li@nxp.com>
diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig
index 626c54b386d5..630dc776c464 100644
--- a/drivers/i3c/Kconfig
+++ b/drivers/i3c/Kconfig
@@ -20,6 +20,18 @@ menuconfig I3C
will be called i3c.
if I3C
+
+config I3CDEV
+ tristate "I3C device interface"
+ depends on I3C
+ help
+ Say Y here to include the i3cdev driver. This allows user-space to
+ explicitly bind I3C devices to the driver in order to expose a
+ character device interface for the I3C device under /dev/bus/i3c/.
+
+ This support is also available as a module. If so, the module will
+ be called i3cdev.
+
source "drivers/i3c/master/Kconfig"
endif # I3C
diff --git a/drivers/i3c/Makefile b/drivers/i3c/Makefile
index 11982efbc6d9..606d422841b2 100644
--- a/drivers/i3c/Makefile
+++ b/drivers/i3c/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
i3c-y := device.o master.o
obj-$(CONFIG_I3C) += i3c.o
+obj-$(CONFIG_I3CDEV) += i3cdev.o
obj-$(CONFIG_I3C) += master/
diff --git a/drivers/i3c/i3cdev.c b/drivers/i3c/i3cdev.c
new file mode 100644
index 000000000000..da0e6910a4f7
--- /dev/null
+++ b/drivers/i3c/i3cdev.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026 Microsoft Corporation
+ *
+ * Author: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
+ *
+ * Based on code from:
+ * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates.
+ * Author: Vitor Soares <vitor.soares@synopsys.com>
+ *
+ * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#include <linux/cdev.h>
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/i3c/device.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/i3c/master.h>
+#include <linux/cleanup.h>
+
+#include <uapi/linux/i3c/i3cdev.h>
+
+#define DRV_VERSION "1.0.0"
+#define SYSFS_PREFIX "i3cdev-"
+#define SYSFS_FORMAT SYSFS_PREFIX "%u"
+#define MAX_I3CDEV_DEVS 256
+#define MAX_XFERS 256
+#define MAX_TOTAL_DATA_BYTES 256
+
+struct i3cdev_data {
+ struct i3c_device *i3c;
+ struct device dev;
+ struct cdev cdev;
+ dev_t devt;
+ /* serializes in-flight transfers, set-up, and tear-down */
+ struct mutex lock;
+ bool bound;
+ u16 mwl;
+ atomic_t open_fd;
+};
+
+enum i3c_xfer_rnw {
+ I3C_WRITE = 0,
+ I3C_READ = 1,
+};
+
+static DEFINE_IDA(i3cdev_ida);
+static dev_t base_dev_t;
+
+/* Set-up character device path */
+static char *i3cdev_devnode(const struct device *dev, umode_t *mode)
+{
+ const struct i3cdev_data *i3cdev;
+ const char *name;
+
+ i3cdev = container_of_const(dev, struct i3cdev_data, dev);
+ name = dev_name(i3cdev_to_dev(i3cdev->i3c));
+
+ return kasprintf(GFP_KERNEL, "bus/i3c/%s", name);
+}
+
+static const struct class i3cdev_class = {
+ .name = "i3cdev",
+ .devnode = i3cdev_devnode,
+};
+
+/**
+ * exceeds_mwl() - Check if a write exceeds a specified Max Write Length (MWL)
+ * @i3cdev: Pointer to i3cdev_data
+ * @xfer: Pointer to a given i3c_xfer
+ *
+ * Returns: true if @xfer length exceeds MWL, false otherwise.
+ */
+static bool exceeds_mwl(struct i3cdev_data *i3cdev, struct i3c_xfer *xfer)
+{
+ if (i3cdev->mwl && xfer->len > i3cdev->mwl) {
+ dev_dbg(&i3cdev->dev, "Requested len exceeds MWL\n");
+ return true;
+ }
+
+ return false;
+}
+
+static inline bool is_write(struct i3c_xfer *x) { return x->rnw == I3C_WRITE; }
+
+static ssize_t
+i3cdev_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
+{
+ struct i3cdev_data *i3cdev = file->private_data;
+ struct i3c_device *i3c = i3cdev->i3c;
+ struct i3c_xfer xfer = {
+ .rnw = I3C_READ
+ };
+ size_t len;
+ char *tmp;
+ int ret;
+
+ if (!i3cdev->bound)
+ return -ENXIO;
+
+ /* File system operation of 0 bytes is effectively a valid, no-op */
+ if (!count)
+ return 0;
+
+ /* Clamp transfer length within driver limits */
+ len = min(count, MAX_TOTAL_DATA_BYTES);
+ xfer.len = len;
+
+ tmp = kzalloc(len, GFP_KERNEL);
+ if (!tmp)
+ return -ENOMEM;
+
+ xfer.data.in = tmp;
+
+ dev_dbg(&i3cdev->dev, "Reading %zu bytes\n", len);
+
+ scoped_guard(mutex, &i3cdev->lock) {
+ /* .remove was called so don't mess with the device */
+ if (!i3cdev->bound) {
+ ret = -ENXIO;
+ goto out_free_kbuf;
+ }
+
+ ret = i3c_device_do_xfers(i3c, &xfer, 1, I3C_SDR);
+ if (ret)
+ goto out_free_kbuf;
+ }
+
+ dev_dbg(&i3cdev->dev, "Received %u bytes\n", xfer.actual_len);
+
+ /* Guard against a buggy controller driver */
+ if (xfer.actual_len > len) {
+ ret = -EIO;
+ goto out_free_kbuf;
+ }
+
+ ret = copy_to_user(buf, tmp, xfer.actual_len) ? -EFAULT : xfer.actual_len;
+
+out_free_kbuf:
+ kfree(tmp);
+
+ return ret;
+}
+
+static ssize_t
+i3cdev_write(struct file *file, const char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ struct i3cdev_data *i3cdev = file->private_data;
+ struct i3c_device *i3c = i3cdev->i3c;
+ struct i3c_xfer xfer = {
+ .rnw = I3C_WRITE
+ };
+ size_t len;
+ char *tmp;
+ int ret;
+
+ if (!i3cdev->bound)
+ return -ENXIO;
+
+ /* File system operation of 0 bytes is effectively a valid, no-op */
+ if (!count)
+ return 0;
+
+ if (exceeds_mwl(i3cdev, &xfer))
+ return -ENXIO;
+
+ /* Clamp transfer length within driver limits */
+ len = min(count, MAX_TOTAL_DATA_BYTES);
+ xfer.len = len;
+
+ tmp = memdup_user(buf, len);
+ if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+
+ xfer.data.out = tmp;
+
+ dev_dbg(&i3cdev->dev, "Writing %zu bytes\n", len);
+
+ scoped_guard(mutex, &i3cdev->lock) {
+ /* .remove was called so don't mess with the device */
+ if (!i3cdev->bound) {
+ ret = -ENXIO;
+ goto out_free_kbuf;
+ }
+
+ ret = i3c_device_do_xfers(i3c, &xfer, 1, I3C_SDR);
+ if (ret)
+ goto out_free_kbuf;
+ }
+
+out_free_kbuf:
+ kfree(tmp);
+
+ return !ret ? len : ret;
+}
+
+/* IOCTL Helper Functions */
+
+/**
+ * get_metadata() - Copy i3cdev_xfers (I3CDEV_XFER ioctl input) from user-space
+ * @i3cdev: i3cdev_data object
+ * @uxfers: User-space i3cdev_xfers object
+ * @metadata: Kernel i3cdev_xfers object into which @uxfers will be copied
+ *
+ * Returns: 0 on success, a negative error code otherwise
+ */
+static int get_metadata(struct i3cdev_data *i3cdev,
+ struct i3cdev_xfers __user *uxfers,
+ struct i3cdev_xfers *metadata)
+{
+ if (copy_from_user(metadata, uxfers, sizeof(*metadata)))
+ return -EFAULT;
+
+ if (!metadata->nxfers)
+ return -EINVAL;
+
+ /* Limit and ensure nxfers fits in an int (for i3cdev and the core) */
+ if (metadata->nxfers > MAX_XFERS || metadata->nxfers > INT_MAX) {
+ dev_dbg(&i3cdev->dev,
+ "Number of transfers exceeds driver limit\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * get_user_xfers() - Copy array of i3cdev_xfer objects from user-space
+ * @metadata: Kernel i3cdev_xfers object (I3CDEV_XFER ioctl input)
+ *
+ * Allocates kernel memory and copies the user-space array of i3cdev_xfer
+ * objects into it. On success, the caller must free the memory.
+ *
+ * Returns: a pointer to the kernel's copy of the i3cdev_xfer array on
+ * success, an ERR_PTR otherwise.
+ */
+static struct i3cdev_xfer *get_user_xfers(struct i3cdev_xfers *metadata)
+{
+ struct i3cdev_xfer *k_uxfers, *k_uxfer;
+ u8 *uxfer;
+ int ret;
+
+ k_uxfers = kcalloc(metadata->nxfers, sizeof(*k_uxfers), GFP_KERNEL);
+ if (!k_uxfers)
+ return ERR_PTR(-ENOMEM);
+
+ k_uxfer = k_uxfers;
+ uxfer = u64_to_user_ptr(metadata->xfers);
+ for (int i = 0; i < metadata->nxfers; i++) {
+ ret = copy_struct_from_user(k_uxfer,
+ sizeof(*k_uxfers),
+ uxfer,
+ metadata->xfer_size);
+ if (ret)
+ goto out_free_k_uxfers;
+
+ /* Enforce that padding must be zero */
+ if (memchr_inv(k_uxfer->pad, 0, sizeof(k_uxfer->pad))) {
+ ret = -EINVAL;
+ goto out_free_k_uxfers;
+ }
+
+ uxfer += metadata->xfer_size; /* u8 pointer so use xfer_size */
+ k_uxfer++; /* struct i3cdev_xfer pointer */
+ }
+
+ return k_uxfers;
+
+out_free_k_uxfers:
+ kfree(k_uxfers);
+ return ERR_PTR(ret);
+}
+
+/**
+ * ioctl_i3c_xfer_input_checks() - Checks that an anticipated transfer is valid
+ * by itself or in the context of an array of i3c_xfer objects.
+ * @i3cdev: i3cdev_data object
+ * @xfer: i3c_xfer object
+ * @prior_bytes: Number of bytes in the i3c_xfer array prior to this transfer
+ *
+ * Returns: 0 on success, a negative error code otherwise
+ */
+static int
+ioctl_i3c_xfer_input_checks(struct i3cdev_data *i3cdev,
+ struct i3c_xfer *xfer,
+ unsigned long prior_bytes)
+{
+ /* I3C core will error on a transfer of 0 bytes */
+ if (!xfer->len) {
+ dev_dbg(&i3cdev->dev, "Invalid transfer of zero bytes\n");
+ return -EINVAL;
+ }
+
+ if (xfer->rnw > 1) {
+ dev_dbg(&i3cdev->dev, "Invalid rnw encoding\n");
+ return -EINVAL;
+ }
+
+ if ((prior_bytes + xfer->len) > MAX_TOTAL_DATA_BYTES) {
+ dev_dbg(&i3cdev->dev, "Byte count exceeds driver limit\n");
+ return -EINVAL;
+ }
+
+ if (is_write(xfer) && exceeds_mwl(i3cdev, xfer))
+ return -ENXIO;
+
+ return 0;
+}
+
+/**
+ * i3cdev_prepare_xfers_from_user() - Prepare the i3c_xfer array
+ * @i3cdev: i3cdev_data object
+ * @metadata: Kernel's copy of i3cdev_xfers (ioctl I3CDEV_XFER input)
+ * @k_uxfers: Kernel's copy of the i3cdev_xfer array
+ * @i3c_xfers: i3c_xfer array that will be sent to the I3C core
+ * @nbufs: In/out variable representing the number of successfully allocated
+ * i3c_xfer data buffers. Use this to walk the array when freeing the memory.
+ *
+ * Returns: 0 on success, a negative error code otherwise
+ */
+static int
+i3cdev_prepare_xfers_from_user(struct i3cdev_data *i3cdev,
+ struct i3cdev_xfers *metadata,
+ struct i3cdev_xfer *k_uxfers,
+ struct i3c_xfer *i3c_xfers,
+ int *nbufs)
+{
+ int ret;
+ unsigned long total_bytes = 0;
+ void __user *udata;
+ void *data;
+
+ *nbufs = 0;
+
+ /* Prepare i3c_xfer objs to send via the I3C core */
+ for (int i = 0; i < metadata->nxfers; i++) {
+ /* Copy fields from i3cdev_xfer -> i3c_xfer */
+ i3c_xfers[i].rnw = k_uxfers[i].rnw;
+ i3c_xfers[i].len = k_uxfers[i].len;
+
+ ret = ioctl_i3c_xfer_input_checks(i3cdev, &i3c_xfers[i],
+ total_bytes);
+ if (!ret)
+ total_bytes += i3c_xfers[i].len;
+ else
+ return ret;
+
+ if (is_write(&i3c_xfers[i])) {
+ /* Copy the data to transmit to kernel-space */
+ udata = u64_to_user_ptr(k_uxfers[i].data);
+ data = memdup_user(udata, i3c_xfers[i].len);
+ } else {
+ /* Prepare a buffer for the resulting read data */
+ data = kzalloc(i3c_xfers[i].len, GFP_KERNEL);
+ if (!data)
+ data = ERR_PTR(-ENOMEM);
+ }
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ /* Track allocated data buffers for freeing */
+ (*nbufs)++;
+
+ if (is_write(&i3c_xfers[i]))
+ i3c_xfers[i].data.out = data;
+ else
+ i3c_xfers[i].data.in = data;
+ }
+
+ return 0;
+}
+
+/**
+ * print_i3c_err() - Prints the I3C error encountered during the prior
+ * call to the core's transfer function.
+ * @i3cdev: i3cdev_data object
+ * @metadata: Kernel's copy of i3cdev_xfers (ioctl I3CDEV_XFER input)
+ * @i3c_xfers: i3c_xfer array that was sent to the I3C core
+ *
+ * Returns: void
+ */
+static void print_i3c_err(struct i3cdev_data *i3cdev,
+ struct i3cdev_xfers *metadata,
+ struct i3c_xfer *i3c_xfers)
+{
+ for (int i = 0; i < metadata->nxfers; i++) {
+ /* Valid errors, e.g. M0 (now called C0) start at 1 */
+ if (i3c_xfers[i].err > 0) {
+ dev_warn(&i3cdev->dev,
+ "I3C error encountered: C%u\n",
+ i3c_xfers[i].err - 1);
+ return;
+ }
+ }
+}
+
+/**
+ * i3cdev_copy_results_to_user() - Copy results to user-space
+ * @metadata: Kernel's copy of i3cdev_xfers (ioctl I3CDEV_XFER input)
+ * @k_uxfers: Kernel's copy of i3cdev_xfer array
+ * @i3c_xfers: i3c_xfer array that was sent to the I3C core
+ *
+ * Returns: 0 on success, a negative error code otherwise
+ */
+static int
+i3cdev_copy_results_to_user(struct i3cdev_xfers *metadata,
+ struct i3cdev_xfer *k_uxfers,
+ struct i3c_xfer *i3c_xfers)
+{
+ u8 __user *uxfer, *uactual_len;
+ void __user *udata;
+ __u16 nbytes;
+
+ uxfer = u64_to_user_ptr(metadata->xfers);
+ for (int i = 0; i < metadata->nxfers; i++, uxfer += metadata->xfer_size) {
+ if (is_write(&i3c_xfers[i])) {
+ continue;
+ } else {
+ udata = u64_to_user_ptr(k_uxfers[i].data);
+ nbytes = i3c_xfers[i].actual_len;
+
+ /* Guard against a buggy controller driver */
+ if (nbytes > k_uxfers[i].len)
+ return -EIO;
+
+ /* Copy over the read response data */
+ if (copy_to_user(udata, i3c_xfers[i].data.in, nbytes))
+ return -EFAULT;
+
+ /* Copy over actual_len */
+ uactual_len = uxfer + offsetof(struct i3cdev_xfer, actual_len);
+ if (copy_to_user(uactual_len, &nbytes, sizeof(__u16)))
+ return -EFAULT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * i3cdev_ioctl_do_xfers() - Implementing function of the I3CDEV_XFER IOCTL
+ * @i3cdev: i3cdev_data object
+ * @uxfers: User-space pointer to I3CDEV_XFER IOCTL input (struct i3cdev_xfers)
+ *
+ * Performs the requested SDR transfers and copies the results to user-space.
+ *
+ * Returns: 0 on success, negative error code otherwise.
+ */
+static int
+i3cdev_ioctl_do_xfers(struct i3cdev_data *i3cdev,
+ struct i3cdev_xfers __user *uxfers)
+{
+ struct i3c_device *i3c = i3cdev->i3c;
+ struct i3cdev_xfers metadata;
+ struct i3cdev_xfer *k_uxfers;
+ struct i3c_xfer *i3c_xfers;
+ int nbufs;
+ int ret;
+
+ ret = get_metadata(i3cdev, uxfers, &metadata);
+ if (ret)
+ return ret;
+
+ k_uxfers = get_user_xfers(&metadata);
+ if (IS_ERR(k_uxfers))
+ return PTR_ERR(k_uxfers);
+
+ i3c_xfers = kcalloc(metadata.nxfers, sizeof(*i3c_xfers), GFP_KERNEL);
+ if (!i3c_xfers) {
+ ret = -ENOMEM;
+ goto out_free_k_uxfers;
+ }
+
+ /* Prepare i3c_xfer objs to send via the I3C core */
+ ret = i3cdev_prepare_xfers_from_user(i3cdev, &metadata, k_uxfers,
+ i3c_xfers, &nbufs);
+ if (ret)
+ goto out_free_i3c_xfers;
+
+ scoped_guard(mutex, &i3cdev->lock) {
+ /* .remove was called so don't mess with the device */
+ if (!i3cdev->bound) {
+ ret = -ENXIO;
+ goto out_free_i3c_xfers;
+ }
+ ret = i3c_device_do_xfers(i3c, i3c_xfers,
+ metadata.nxfers, I3C_SDR);
+ if (ret) {
+ print_i3c_err(i3cdev, &metadata, i3c_xfers);
+ goto out_free_i3c_xfers;
+ }
+ }
+
+ ret = i3cdev_copy_results_to_user(&metadata, k_uxfers, i3c_xfers);
+
+out_free_i3c_xfers:
+ for (int i = 0; i < nbufs; i++)
+ kfree(i3c_xfers[i].data.in);
+ kfree(i3c_xfers);
+
+out_free_k_uxfers:
+ kfree(k_uxfers);
+
+ return ret;
+}
+
+static long
+i3cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct i3cdev_data *i3cdev = file->private_data;
+ void __user *udata = (void __user *)arg;
+ int ret;
+
+ if (!i3cdev->bound)
+ return -ENXIO;
+
+ dev_dbg(&i3cdev->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n", cmd, arg);
+
+ switch (cmd) {
+ case I3CDEV_XFER:
+ ret = i3cdev_ioctl_do_xfers(i3cdev, udata);
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ return ret;
+}
+
+static int i3cdev_open(struct inode *inode, struct file *file)
+{
+ struct i3cdev_data *i3cdev;
+
+ i3cdev = container_of_const(inode->i_cdev, struct i3cdev_data, cdev);
+
+ /* Let probe finish cdev_device_add */
+ scoped_guard(mutex, &i3cdev->lock)
+ if (!i3cdev->bound)
+ return -ENXIO;
+
+ /*
+ * Some devices have pointer or page registers where programming
+ * it can change what an address refers to, avoid this by allowing
+ * only one process to interact with the device.
+ */
+ if (atomic_cmpxchg_relaxed(&i3cdev->open_fd, 0, 1))
+ return -EBUSY;
+
+ file->private_data = i3cdev;
+
+ return 0;
+}
+
+static int i3cdev_release(struct inode *inode, struct file *file)
+{
+ struct i3cdev_data *i3cdev = file->private_data;
+
+ atomic_set(&i3cdev->open_fd, 0);
+
+ return 0;
+}
+
+static const struct file_operations i3cdev_fops = {
+ .owner = THIS_MODULE,
+ .read = i3cdev_read,
+ .write = i3cdev_write,
+ .unlocked_ioctl = i3cdev_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .open = i3cdev_open,
+ .release = i3cdev_release,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static void free_i3cdev_data(struct device *d)
+{
+ struct i3cdev_data *i3cdev = container_of(d, struct i3cdev_data, dev);
+ struct device *i3c_device_dev = i3cdev_to_dev(i3cdev->i3c);
+
+ kfree(i3cdev);
+ /* Release the reference to the underlying device */
+ put_device(i3c_device_dev);
+}
+
+static int i3cdev_probe(struct i3c_device *i3c)
+{
+ struct i3cdev_data *i3cdev;
+ struct device *i3c_device_dev = i3cdev_to_dev(i3c);
+ int minor, ret;
+ struct i3c_device_info info;
+
+ i3cdev = kzalloc_obj(*i3cdev);
+ if (!i3cdev)
+ return -ENOMEM;
+
+ minor = ida_alloc_range(&i3cdev_ida, MINOR(base_dev_t),
+ MAX_I3CDEV_DEVS - 1, GFP_KERNEL);
+ if (minor < 0) {
+ dev_err(i3c_device_dev, "Not able to reserve a minor\n");
+ kfree(i3cdev);
+ return minor;
+ }
+
+ i3cdev->devt = MKDEV(MAJOR(base_dev_t), minor);
+
+ i3cdev->i3c = i3c;
+ i3cdev_set_drvdata(i3c, i3cdev);
+ /* Pin the underlying device as long as i3cdev lives */
+ get_device(i3c_device_dev);
+
+ i3c_device_get_info(i3c, &info);
+ i3cdev->mwl = info.max_write_len;
+
+ mutex_init(&i3cdev->lock);
+ atomic_set(&i3cdev->open_fd, 0);
+
+ i3cdev->dev.parent = i3c_device_dev;
+ i3cdev->dev.devt = i3cdev->devt;
+ i3cdev->dev.class = &i3cdev_class;
+ i3cdev->dev.release = free_i3cdev_data;
+ ret = dev_set_name(&i3cdev->dev, SYSFS_FORMAT, MINOR(i3cdev->devt));
+ if (ret)
+ goto error_free_ida;
+
+ device_initialize(&i3cdev->dev);
+
+ cdev_init(&i3cdev->cdev, &i3cdev_fops);
+ i3cdev->cdev.owner = THIS_MODULE;
+
+ scoped_guard(mutex, &i3cdev->lock) {
+ ret = cdev_device_add(&i3cdev->cdev, &i3cdev->dev);
+ if (ret)
+ goto error_cleanup;
+
+ i3cdev->bound = true;
+ }
+
+ return 0;
+
+error_cleanup:
+ put_device(&i3cdev->dev);
+
+error_free_ida:
+ ida_free(&i3cdev_ida, minor);
+
+ return ret;
+}
+
+static void i3cdev_remove(struct i3c_device *i3c)
+{
+ struct i3cdev_data *i3cdev;
+
+ i3cdev = i3cdev_get_drvdata(i3c);
+
+ /* via the lock, allow any work impacting the system to complete */
+ scoped_guard(mutex, &i3cdev->lock) {
+ /* signal to fops that the device is no longer managed */
+ i3cdev->bound = false;
+ cdev_device_del(&i3cdev->cdev, &i3cdev->dev);
+ }
+
+ ida_free(&i3cdev_ida, MINOR(i3cdev->devt));
+ put_device(&i3cdev->dev);
+}
+
+static const struct i3c_device_id i3cdev_ids[] = {
+ { /* Sentinel */ },
+};
+
+static struct i3c_driver i3cdev_driver = {
+ .probe = i3cdev_probe,
+ .remove = i3cdev_remove,
+ .id_table = i3cdev_ids,
+ .driver = {
+ .name = "i3cdev",
+ }
+};
+
+static int __init i3cdev_init(void)
+{
+ int ret;
+
+ ret = alloc_chrdev_region(&base_dev_t, 0,
+ MAX_I3CDEV_DEVS, "i3cdev");
+ if (ret)
+ return ret;
+
+ ret = class_register(&i3cdev_class);
+ if (ret)
+ goto err_unreg_chrdev_region;
+
+ ret = i3c_driver_register(&i3cdev_driver);
+ if (ret)
+ goto err_unregister_class;
+
+ return 0;
+
+err_unregister_class:
+ class_unregister(&i3cdev_class);
+
+err_unreg_chrdev_region:
+ unregister_chrdev_region(base_dev_t, MAX_I3CDEV_DEVS);
+
+ return ret;
+}
+module_init(i3cdev_init);
+
+static void __exit i3cdev_exit(void)
+{
+ i3c_driver_unregister(&i3cdev_driver);
+ class_unregister(&i3cdev_class);
+ unregister_chrdev_region(base_dev_t, MAX_I3CDEV_DEVS);
+ ida_destroy(&i3cdev_ida);
+}
+module_exit(i3cdev_exit);
+
+MODULE_AUTHOR("Meagan Lloyd <meaganlloyd@linux.microsoft.com>");
+MODULE_DESCRIPTION("I3C Character Device Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/include/uapi/linux/i3c/i3cdev.h b/include/uapi/linux/i3c/i3cdev.h
new file mode 100644
index 000000000000..ed72d7455f31
--- /dev/null
+++ b/include/uapi/linux/i3c/i3cdev.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (c) 2026 Microsoft Corporation
+ *
+ * Author: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
+ *
+ * Based on code from:
+ * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates.
+ * Author: Vitor Soares <vitor.soares@synopsys.com>
+ *
+ * Author: Boris Brezillon <boris.brezillon@bootlin.com>
+ */
+
+#ifndef _UAPI_I3C_DEV_H_
+#define _UAPI_I3C_DEV_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/**
+ * Reserved ioctl encoding for i3cdev
+ * Code 0x7, Seq# 0x0-0x9E
+ */
+#define I3CDEV_IOCTL_ID 0x07
+
+/**
+ * struct i3cdev_xfer - I3C transfer (read/write)
+ *
+ * This struct more-or-less mirrors the kernel's 'struct i3c_xfer'.
+ * Currently, only Single Data Rate (SDR) transfers are supported.
+ *
+ * @data: Pointer to userspace buffer. For writes, this will be the
+ * bytes to send to the Target. For reads, this buffer will be
+ * populated with the read response from the Target.
+ * @actual_len: Where the kernel will report the number of processed
+ * bytes. For reads, this reflects the number of response bytes in the
+ * @data buffer. For SDR writes, the user shouldn't use this member
+ * as it's neither supported nor useful.
+ * @len: Length of input @data buffer in bytes.
+ * @rnw: Transfer direction. 1 for a read, 0 for a write
+ * @pad: Used to eliminate implicit, undefined-value padding. Zero
+ * these bytes without referencing this field (for compatibility).
+ */
+struct i3cdev_xfer {
+ __u64 data;
+ __u16 actual_len; /* output */
+ __u16 len;
+ union {
+ __u8 rnw; /* SDR */
+ __u8 cmd; /* Not currently supported (HDR) */
+ };
+ __u8 pad[3];
+};
+
+/**
+ * struct i3cdev_xfers - I3C transfers
+ * @nxfers: Number of i3cdev_xfer objs in @xfers
+ * @xfers: Pointer to an array of i3cdev_xfer objs
+ * @xfer_size: sizeof(struct i3cdev_xfer)
+ */
+struct i3cdev_xfers {
+ __u64 nxfers;
+ __u64 xfers;
+ __u64 xfer_size;
+};
+
+#define I3CDEV_XFER \
+ _IOWR(I3CDEV_IOCTL_ID, 0, struct i3cdev_xfers)
+
+#endif
--
2.49.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] i3c: master: enable driver_override for I3C
2026-09-11 21:09 ` [PATCH 1/3] i3c: master: enable driver_override for I3C Meagan Lloyd
@ 2026-09-11 21:36 ` Guenter Roeck
2026-09-16 18:29 ` Meagan Lloyd
2026-09-12 13:22 ` Andy Shevchenko
2026-09-13 0:24 ` Jonathan Cameron
2 siblings, 1 reply; 20+ messages in thread
From: Guenter Roeck @ 2026-09-11 21:36 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Fri, Sep 11, 2026 at 02:09:33PM -0700, Meagan Lloyd wrote:
> Opt-in to driver_override sysfs functionality for I3C subsystem.
>
> Update some I3C drivers that are assuming that if their .probe is
> invoked that the bus match function already checked the ID table. Go
> ahead and verify in .probe that i3c_device_match_id() returns a valid
> match before using the i3c_device_id pointer.
>
> Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> Assisted-by: Claude:claude-opus-4.8 copilot
> ---
> drivers/hwmon/lm75.c | 7 ++++++-
> drivers/hwmon/tmp108.c | 2 ++
Separate patches for each hwmon driver, please. Each patch should use the
standard prefix used in hwmon drivers.
Thanks,
Guenter
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] i3c: add i3cdev character device module for user-space access
2026-09-11 21:09 ` [PATCH 3/3] i3c: add i3cdev character device module for user-space access Meagan Lloyd
@ 2026-09-11 23:29 ` Randy Dunlap
2026-09-16 18:37 ` Meagan Lloyd
2026-09-12 13:34 ` Andy Shevchenko
1 sibling, 1 reply; 20+ messages in thread
From: Randy Dunlap @ 2026-09-11 23:29 UTC (permalink / raw)
To: Meagan Lloyd, linux-i3c
Cc: alexandre.belloni, vitor.soares, samagazaryan, gregkh, arnd,
boris.brezillon, oleksandr.shulzhenko.viktorovych, tgopinath,
corbet, skhan, linux, Frank.Li, jorge.marques, pgaj, wsa+renesas,
tommaso.merciai.xr, nuno.sa, Michael.Hennerich, jic23, dlechner,
andy, lorenzo, enelsonmoore, rppt, pratyush, giovanni.cabiddu,
gabewhigham, haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng,
ustc.gu, jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
On 9/11/26 2:09 PM, Meagan Lloyd wrote:
> The i3cdev driver is a character device driver that allows user-space
> to control and interact with I3C devices.
>
> Currently, it has the ability to perform Single Data Rate (SDR)
> transfers - basic reads/writes.
>
> With the addition of sysfs driver_override, there is now a
> straightforward and direct way to match the i3cdev driver to any i3c
> device without stepping on the toes of more specialized drivers that are
> loaded automatically.
>
> This is accomplished by the i3cdev driver not having any entries in the
> i3c_device_id table. After boot, simply set the driver_override to
> "i3cdev" and bind the device manually via the sysfs bind knob. This can
> also be automated with udev rules as well.
>
> The character device interface will be exposed at:
> /dev/bus/i3c/<bus id>-<Provisional ID>
>
> Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> Assisted-by: Claude:claude-opus-4.8 copilot
> ---
> Documentation/userspace-api/i3c/i3cdev.rst | 152 ++++
> .../userspace-api/ioctl/ioctl-number.rst | 1 +
> MAINTAINERS | 7 +
> drivers/i3c/Kconfig | 12 +
> drivers/i3c/Makefile | 1 +
> drivers/i3c/i3cdev.c | 730 ++++++++++++++++++
> include/uapi/linux/i3c/i3cdev.h | 70 ++
> 7 files changed, 973 insertions(+)
> create mode 100644 Documentation/userspace-api/i3c/i3cdev.rst
> create mode 100644 drivers/i3c/i3cdev.c
> create mode 100644 include/uapi/linux/i3c/i3cdev.h
>
> diff --git a/Documentation/userspace-api/i3c/i3cdev.rst b/Documentation/userspace-api/i3c/i3cdev.rst
> new file mode 100644
> index 000000000000..ad246bf28919
> --- /dev/null
> +++ b/Documentation/userspace-api/i3c/i3cdev.rst
> @@ -0,0 +1,152 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +====================
> +I3C Device Interface
> +====================
> +
> +This interface allows access to I3C devices from userspace.
> +
> +As I3C devices adhere to the I3C protocol, simple transfers can be accomplished
> +using a generic driver. Currently, this interface supports private Single Data
> +Rate (SDR) read and write transfers.
> +
> +The i3cdev module will not auto-bind to devices. Userspace needs to explicitly
> +bind the device to the driver. This is to avoid interfering with the binding of
> +specialized drivers.
> +
> +Once bound, a character device interface will be created at:
> +/dev/bus/i3c/<bus id>-<Provisional ID>.
> +
> +====================
> +Usage
> +====================
> +
> +Any discovered I3C devices by the I3C subsystem will have device folders under /sys/bus/i3c/devices/<bus id>-<Provisional ID>.
> +To allow binding of a device with i3cdev driver, set the driver_override:
> +
> +::
> +
> + # echo "i3cdev" > /sys/bus/i3c/devices/<bus id>-<Provisional ID>/driver_override
> +
> +If the i3cdev driver is not yet loaded, load it and it will cause the driver to bind
> +to any devices with the override in place.
> +
> +If the i3cdev driver is already loaded, go ahead and perform a manual bind:
> +
> +::
> +
> + # echo "<bus id>-<Provisional ID>" > /sys/bus/i3c/drivers/i3cdev/bind
> +
> +Set driver override
> +::
> +
> + # echo "i3cdev" > /sys/bus/i3c/devices/0-deadbeef001/driver_override
> + # echo "i3cdev" > /sys/bus/i3c/devices/0-deadbeef002/driver_override
> +
> +Bind the device to the driver
> +::
> +
> + # echo "0-deadbeef001" > /sys/bus/i3c/drivers/i3cdev/bind
> + # echo "0-deadbeef002" > /sys/bus/i3c/drivers/i3cdev/bind
> +
> +Observe the resulting character device files under /dev/bus/i3c/
> +::
> +
> + # ls -ltr /dev/bus/i3c/
> + crw------- 1 root root 235, 1 Jun 30 17:49 0-deadbeef002
> + crw------- 1 root root 235, 0 Jun 30 17:49 0-deadbeef001
> +
> +BASIC CHARACTER DEVICE API
> +===============================
> +The API supports private Single Data Rate (SDR) read and write transfers.
> +Those transaction can be achieved by the following:
> +
> +``read(file, buffer, sizeof(buffer))``
> + The standard read() operation will work as a simple transaction of private
> + SDR read data followed a stop.
> + Return the number of bytes read on success, and a negative error otherwise.
> +
> +``write(file, buffer, sizeof(buffer))``
> + The standard write() operation will work as a simple transaction of private
> + SDR write data followed a stop.
> + Return the number of bytes written on success, and a negative error otherwise.
> +
> +``ioctl(file, I3CDEV_XFER, struct i3cdev_xfers *xfers)``
> + It combines read/write transactions without a stop in between.
> + Return 0 on success, and a negative error otherwise.
> +
> +C EXAMPLE (PSEUDO CODE)
> +=======================
> +You need to open (and get a file descriptor) to /dev/bus/i3c/<bus id>-<Provisional ID>,
> +do your operations (read, write, ioctl), and then close it.
> +
> +The following header files should be included in an I3C program::
> +
> +#include <fcntl.h>
> +#include <unistd.h>
> +#include <sys/ioctl.h>
> +#include <i3c/i3cdev.h>
> +#include <stdint.h>
> +
> +These additionally are used by this example::
> +
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +To work with an I3C device, one must call open and get a file descriptor::
> +
> + int fd;
> +
> + fd = open("/dev/bus/i3c/0-deadbeef001", O_RDWR);
> + if (fd < 0)
> + exit(EXIT_FAILURE);
> +
> +Now that the file is open, we can do some operations::
> +
> + int ret;
> +
> + /* Write function */
> + uint8_t buf[] = {0x00, 0xde, 0xad, 0xbe, 0xef};
> + ret = write(fd, buf, 5);
> + if (ret != 5) {
> + /* ERROR HANDLING: I3C transaction failed */
> + }
> +
> + /* Read function */
> + ret = read(fd, buf, 4);
> + if (ret < 0) {
> + /* ERROR HANDLING: I3C transaction failed */
> + } else {
> + /* Iterate over buf[] to get the read data */
> + }
> +
> + /* IOCTL function */
> + struct i3cdev_xfer xfers[2] = {0}; /* Must zero out for compatibility */
> + struct i3cdev_xfers xfers_metadata = { .nxfers = 2,
> + .xfers = (uintptr_t) xfers,
> + .xfer_size = sizeof(struct i3cdev_xfer)
> + };
> +
> + uint8_t tx_buf[] = {0x00, 0xde, 0xad, 0xbe, 0xef};
> + uint8_t rx_buf[10];
> +
> + xfers[0].data = (uintptr_t) tx_buf;
> + xfers[0].len = 5;
> + xfers[0].rnw = 0;
> + xfers[1].data = (uintptr_t) rx_buf;
> + xfers[1].len = 10;
> + xfers[1].rnw = 1;
> +
> + ret = ioctl(fd, I3CDEV_XFER, (uintptr_t) &xfers_metadata);
> + if (ret < 0) {
> + /* ERROR HANDLING: I3C transaction failed */
> + } else {
> + /* For reads, optionally verify that the response matches expectations */
> + assert(xfers[1].len == xfers[1].actual_len);
> +
> + /* For reads, iterate through response data using xfers[i].actual_len */
> + }
> +
> +The device can be closed when the open file descriptor is no longer required::
> +
> + close(fd);
> \ No newline at end of file
Fix the warning above, please.
Also:
Documentation/userspace-api/i3c/i3cdev.rst: WARNING: document isn't included in any toctree [toc.not_included]
add that file to the table of contents (somewhere).
thanks.
--
~Randy
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] i3c: master: enable driver_override for I3C
2026-09-11 21:09 ` [PATCH 1/3] i3c: master: enable driver_override for I3C Meagan Lloyd
2026-09-11 21:36 ` Guenter Roeck
@ 2026-09-12 13:22 ` Andy Shevchenko
2026-09-16 18:39 ` Meagan Lloyd
2026-09-13 0:24 ` Jonathan Cameron
2 siblings, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2026-09-12 13:22 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Fri, Sep 11, 2026 at 02:09:33PM -0700, Meagan Lloyd wrote:
> Opt-in to driver_override sysfs functionality for I3C subsystem.
>
> Update some I3C drivers that are assuming that if their .probe is
> invoked that the bus match function already checked the ID table. Go
> ahead and verify in .probe that i3c_device_match_id() returns a valid
> match before using the i3c_device_id pointer.
>
> Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> Assisted-by: Claude:claude-opus-4.8 copilot
Assisted-by: LLM
...
> drivers/hwmon/lm75.c | 7 ++++++-
> drivers/hwmon/tmp108.c | 2 ++
> drivers/i3c/master.c | 6 ++++++
> drivers/iio/adc/ad4062.c | 10 ++++++++--
> drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c | 3 +++
No, each IIO driver should have its own patch.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/3] I3C character device driver using driver_override
2026-09-11 21:09 [PATCH 0/3] I3C character device driver using driver_override Meagan Lloyd
` (2 preceding siblings ...)
2026-09-11 21:09 ` [PATCH 3/3] i3c: add i3cdev character device module for user-space access Meagan Lloyd
@ 2026-09-12 13:26 ` Andy Shevchenko
2026-09-16 19:28 ` Meagan Lloyd
3 siblings, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2026-09-12 13:26 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Fri, Sep 11, 2026 at 02:09:32PM -0700, Meagan Lloyd wrote:
> This is a rework and revival option for Vitor Soares' I3C character
> device driver patch series from 2020 [1] that I've been exploring for a
> few months. Recently there was a revival posted to the list [2], so I
> wanted to share this design option as well.
>
> In [1] and [2], the i3cdev driver automatically attaches and detaches
> depending whether another driver has attached/not. In [1], Boris was
> suggesting we explore a more straightforward and traditional binding
> method aligning with the Linux driver model. At the time, there wasn't
> a way to auto-bind while keeping manual binding possible as they shared
> the same match() hook. Now with the new driver_override feature,
Where is it new? It's quite an old mechanism in the driver core...
> the auto-binding of i3cdev on boot can be avoided if the i3cdev driver has
> an empty match ID table. After boot, where specialized drivers would have
> already bound, user-space can explicitly opt-in by setting the
> driver_override sysfs file with 'i3cdev' and manually binding via sysfs
> (or by simply loading the driver if it's loadable). This can also be
> easily automated with udev rules that run whenever the I3C core exposes
> a new device.
>
> One downside of the automatic attach/de-attach is that if a different
> driver is loaded later, the first driver could have altered something
> on the device, breaking any assumptions of the subsequent driver.
>
> My series builds on [1] through:
> 0. Addressing code review feedback in [1] from Greg, Boris, and Randy.
> 1. Using actual_len for accurate read response reporting. The kernel
> will report actual_len received from the core to user-space via the
> uapi i3cdev_xfer struct.
> 2. Placing limits on the number of transfers and bytes in requests to
> prevent unlimited-sized transfers or kernel memory allocation
> 3. Checking inputs and descriptive return codes as guard-rails
> for user-space and to ease use of the i3cdev driver
> 4. Checking on MWL to ensure that we respect device limits
> 5. Proper lifetime management of i3cdev_data and underlying device
> 6. Addressing dangling fops in the event we have an open file descriptor
> when a device gets unbound.
> 7. Fast-path locking to ensure transfers complete before a device is
> unbound.
> 8. Allowing only one file descriptor per I3C device to avoid bugs
> around multiple processes interacting with the device and altering
> the device underneath the other. For example, without this, one process
> could change the device's page or address pointer register underneath
> the other process.
> 9. copy_struct_from_user to ensure struct i3cdev_xfer could be extended
> in a compatible way. This is to be forward-looking towards potential
> HDR mode expansion and code reuse.
> 10. Reserving the IOCTL number formally
> 11. Updating the Documentation to be a syntax correct example program
> template.
> 12. Preserving /dev/bus/i3c/<bus id>-<Provisional ID> naming while
> allowing sysfs path to be neatly named i3cdev-<minor>. This avoids
> repeated <bus id>-<Provisional ID> in the sysfs paths which can be
> confusing/circular-looking.
> e.g. /sys/bus/i3c/devices/0-deadbeef001/i3cdev/0-deadbeef001 ->
> /sys/bus/i3c/devices/0-deadbeef001/i3cdev/i3cdev-0
> 13. Updating all naming references related to i3c_priv_xfer to align
> with new i3c_xfer struct
> 14. Updating the MAINTAINERS file for the new pieces of code
>
> Note that i3c-tools [3] or a fork of it will need small updates:
> 1. Update include/uapi/linux/i3c/i3cdev.h to match updated uapi structs
> 2. In i3ctransfer.c, use actual_len for reads
> I've added MODULE_VERSION("1.0.0") in the i3cdev driver, so i3c-tools
> could use that to determine whether to use the old out-of-tree uapi or this one.
Absolutely no. This is legacy macro which has no need since Git era. In Git
the module version is the Git SHA hash of the tip of the used tree. Nobody will
understand what 1.0.0 means and how it maps to the applied patches (if any of
them affects the behaviour of the feature in question).
On top of that, upstream has no clue what and how many possible custom ABIs /
UAPIs exists, and we do not care, to be honest.
> [1] https://lore.kernel.org/linux-i3c/cover.1582069402.git.vitor.soares@synopsys.com/
> [2] https://lore.kernel.org/linux-i3c/ap_1-gFF7S821xJT@ninjato/T/#m9107c1785a4a16b1b3cb84c3269d17fac35819c8
> [3] https://github.com/vitor-soares-snps/i3c-tools
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] i3c: add i3cdev character device module for user-space access
2026-09-11 21:09 ` [PATCH 3/3] i3c: add i3cdev character device module for user-space access Meagan Lloyd
2026-09-11 23:29 ` Randy Dunlap
@ 2026-09-12 13:34 ` Andy Shevchenko
2026-09-16 22:57 ` Meagan Lloyd
1 sibling, 1 reply; 20+ messages in thread
From: Andy Shevchenko @ 2026-09-12 13:34 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Fri, Sep 11, 2026 at 02:09:35PM -0700, Meagan Lloyd wrote:
> The i3cdev driver is a character device driver that allows user-space
> to control and interact with I3C devices.
> Currently, it has the ability to perform Single Data Rate (SDR)
> transfers - basic reads/writes.
>
> With the addition of sysfs driver_override, there is now a
> straightforward and direct way to match the i3cdev driver to any i3c
> device without stepping on the toes of more specialized drivers that are
> loaded automatically.
Is it safe? Why on the earth do we need this? The commit message has not enough
information.
> This is accomplished by the i3cdev driver not having any entries in the
> i3c_device_id table. After boot, simply set the driver_override to
> "i3cdev" and bind the device manually via the sysfs bind knob. This can
> also be automated with udev rules as well.
>
> The character device interface will be exposed at:
> /dev/bus/i3c/<bus id>-<Provisional ID>
...
> + struct i3c_xfer xfer = {
> + .rnw = I3C_WRITE
In such cases always leave a trailing comma. It will reduce possible churn in
the future.
> + };
...
> + return !ret ? len : ret;
My gosh, wouldn't Elvis just work naturally?
return ret ?: len;
...
> + for (int i = 0; i < metadata->nxfers; i++) {
Why is 'i' signed?
> + ret = copy_struct_from_user(k_uxfer,
> + sizeof(*k_uxfers),
> + uxfer,
> + metadata->xfer_size);
> + if (ret)
> + goto out_free_k_uxfers;
> +
> + /* Enforce that padding must be zero */
> + if (memchr_inv(k_uxfer->pad, 0, sizeof(k_uxfer->pad))) {
> + ret = -EINVAL;
> + goto out_free_k_uxfers;
> + }
> +
> + uxfer += metadata->xfer_size; /* u8 pointer so use xfer_size */
> + k_uxfer++; /* struct i3cdev_xfer pointer */
> + }
...
> + if (!ret)
> + total_bytes += i3c_xfers[i].len;
> + else
> + return ret;
Yeah, you really need to reconsider patterns you use in the code. Here 'else'
is redundant. Homework to understand how (#easy).
...
> +/**
> + * print_i3c_err() - Prints the I3C error encountered during the prior
> + * call to the core's transfer function.
> + * @i3cdev: i3cdev_data object
> + * @metadata: Kernel's copy of i3cdev_xfers (ioctl I3CDEV_XFER input)
> + * @i3c_xfers: i3c_xfer array that was sent to the I3C core
> + * Returns: void
Huh?! Where is this coming from?
> + */
...
Please, rely less on AI and more on the common sense and proof-reading.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] i3c: master: enable driver_override for I3C
2026-09-11 21:09 ` [PATCH 1/3] i3c: master: enable driver_override for I3C Meagan Lloyd
2026-09-11 21:36 ` Guenter Roeck
2026-09-12 13:22 ` Andy Shevchenko
@ 2026-09-13 0:24 ` Jonathan Cameron
2026-09-16 18:53 ` Meagan Lloyd
2 siblings, 1 reply; 20+ messages in thread
From: Jonathan Cameron @ 2026-09-13 0:24 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Fri, 11 Sep 2026 14:09:33 -0700
Meagan Lloyd <meaganlloyd@linux.microsoft.com> wrote:
> Opt-in to driver_override sysfs functionality for I3C subsystem.
>
> Update some I3C drivers that are assuming that if their .probe is
> invoked that the bus match function already checked the ID table. Go
> ahead and verify in .probe that i3c_device_match_id() returns a valid
> match before using the i3c_device_id pointer.
>
> Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> Assisted-by: Claude:claude-opus-4.8 copilot
Along with splitting these up as this is the sort of change that
will sometimes need backporting in individual drivers because we are
layering something on top, I'd look at adding some helpers to skip
the id as that isn't much used and get the specific fields people
want.
> diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c
> index 1c4a58855e2d..98076ae8dce8 100644
> --- a/drivers/hwmon/tmp108.c
> +++ b/drivers/hwmon/tmp108.c
> @@ -581,6 +581,8 @@ static int p3t1085_i3c_probe(struct i3c_device *i3cdev)
> "Failed to register i3c regmap\n");
>
> id = i3c_device_match_id(i3cdev, p3t1085_i3c_ids);
> + if (!id)
> + return -ENODEV;
>
> return tmp108_common_probe(dev, regmap, "p3t1085_i3c", id->data);
Similar to below, jumping directly to an i3c_device_match_data() helper
in this series would avoid getting and id that these drivers don't
need access to.
> }
> diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
> index 8e5984055b15..f28b503d16eb 100644
> --- a/drivers/iio/adc/ad4062.c
> +++ b/drivers/iio/adc/ad4062.c
> @@ -1482,14 +1482,20 @@ MODULE_DEVICE_TABLE(i3c, ad4062_id_table);
>
> static int ad4062_probe(struct i3c_device *i3cdev)
> {
> - const struct i3c_device_id *id = i3c_device_match_id(i3cdev, ad4062_id_table);
> - const struct ad4062_chip_info *chip = id->data;
> + const struct i3c_device_id *id;
> + const struct ad4062_chip_info *chip;
> struct device *dev = &i3cdev->dev;
> struct iio_dev *indio_dev;
> struct ad4062_state *st;
> bool ref_sel;
> int ret;
>
> + id = i3c_device_match_id(i3cdev, ad4062_id_table);
> + if (!id)
> + return -ENODEV;
> +
> + chip = id->data;
This might be a good opportunity to introduce
i3c_device_match_data() as in many drivers we don't actually care about
access to the id itself.
> +
> indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
> if (!indio_dev)
> return -ENOMEM;
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
> index cb5c5d7e1f3d..153a03e06801 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
> @@ -32,6 +32,9 @@ static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev)
> struct device *dev = i3cdev_to_dev(i3cdev);
> struct regmap *regmap;
>
Please bring the assignment down to above the check so
id = i3c_device_match_id(i3cdev,st_lsm6dsx_i3c_ids);
Which is what you did in the previous driver...
> + if (!id)
> + return -ENODEV;
> +
> regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config);
> if (IS_ERR(regmap)) {
> dev_err(dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap));
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] i3c: set i3c_xfer.actual_len in controller drivers
2026-09-11 21:09 ` [PATCH 2/3] i3c: set i3c_xfer.actual_len in controller drivers Meagan Lloyd
@ 2026-09-13 0:26 ` Jonathan Cameron
2026-09-16 19:11 ` Meagan Lloyd
0 siblings, 1 reply; 20+ messages in thread
From: Jonathan Cameron @ 2026-09-13 0:26 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Fri, 11 Sep 2026 14:09:34 -0700
Meagan Lloyd <meaganlloyd@linux.microsoft.com> wrote:
> Set i3c_xfer.actual_len for reads across the I3C Controller drivers.
>
> actual_len provides a consistent location for device drivers to know the
> read response bytes without overriding the len field.
Hi Meagan,
Why is that useful in this series? Currently this sounds like
an unrelated change.
Jonathan
>
> Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] i3c: master: enable driver_override for I3C
2026-09-11 21:36 ` Guenter Roeck
@ 2026-09-16 18:29 ` Meagan Lloyd
0 siblings, 0 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-16 18:29 UTC (permalink / raw)
To: Guenter Roeck
Cc: Meagan Lloyd, linux-i3c, alexandre.belloni, vitor.soares,
samagazaryan, gregkh, arnd, boris.brezillon,
oleksandr.shulzhenko.viktorovych, tgopinath, corbet, skhan,
Frank.Li, jorge.marques, pgaj, wsa+renesas, tommaso.merciai.xr,
nuno.sa, Michael.Hennerich, jic23, dlechner, andy, lorenzo,
enelsonmoore, rppt, pratyush, giovanni.cabiddu, gabewhigham,
haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng, ustc.gu,
jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
> Separate patches for each hwmon driver, please. Each patch should use the
> standard prefix used in hwmon drivers.
>
> Thanks,
> Guenter
Will do, thanks for the feedback.
Best regards,
Meagan
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] i3c: add i3cdev character device module for user-space access
2026-09-11 23:29 ` Randy Dunlap
@ 2026-09-16 18:37 ` Meagan Lloyd
0 siblings, 0 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-16 18:37 UTC (permalink / raw)
To: Randy Dunlap
Cc: Meagan Lloyd, linux-i3c, alexandre.belloni, vitor.soares,
samagazaryan, gregkh, arnd, boris.brezillon,
oleksandr.shulzhenko.viktorovych, tgopinath, corbet, skhan,
linux, Frank.Li, jorge.marques, pgaj, wsa+renesas,
tommaso.merciai.xr, nuno.sa, Michael.Hennerich, jic23, dlechner,
andy, lorenzo, enelsonmoore, rppt, pratyush, giovanni.cabiddu,
gabewhigham, haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng,
ustc.gu, jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
> > + close(fd);
> > \ No newline at end of file
>
> Fix the warning above, please.
> Also:
> Documentation/userspace-api/i3c/i3cdev.rst: WARNING: document isn't included in any toctree [toc.not_included]
>
> add that file to the table of contents (somewhere).
>
> thanks.
> --
> ~Randy
Thanks for catching that! I'll add the newline at the end of the file as
well as add this page to the table of contents.
Best regards,
Meagan
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] i3c: master: enable driver_override for I3C
2026-09-12 13:22 ` Andy Shevchenko
@ 2026-09-16 18:39 ` Meagan Lloyd
0 siblings, 0 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-16 18:39 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Meagan Lloyd, linux-i3c, alexandre.belloni, vitor.soares,
samagazaryan, gregkh, arnd, boris.brezillon,
oleksandr.shulzhenko.viktorovych, tgopinath, corbet, skhan,
linux, Frank.Li, jorge.marques, pgaj, wsa+renesas,
tommaso.merciai.xr, nuno.sa, Michael.Hennerich, jic23, dlechner,
andy, lorenzo, enelsonmoore, rppt, pratyush, giovanni.cabiddu,
gabewhigham, haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng,
ustc.gu, jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
> > Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> > Assisted-by: Claude:claude-opus-4.8 copilot
>
> Assisted-by: LLM
Sure, I can update the tag accordingly. Thanks!
>
> ...
>
> > drivers/hwmon/lm75.c | 7 ++++++-
> > drivers/hwmon/tmp108.c | 2 ++
> > drivers/i3c/master.c | 6 ++++++
> > drivers/iio/adc/ad4062.c | 10 ++++++++--
> > drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c | 3 +++
>
> No, each IIO driver should have its own patch.
>
> --
> With Best Regards,
> Andy Shevchenko
>
Got it. Thanks for the feedback.
Sincerely,
Meagan
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 1/3] i3c: master: enable driver_override for I3C
2026-09-13 0:24 ` Jonathan Cameron
@ 2026-09-16 18:53 ` Meagan Lloyd
0 siblings, 0 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-16 18:53 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Meagan Lloyd, linux-i3c, alexandre.belloni, vitor.soares,
samagazaryan, gregkh, arnd, boris.brezillon,
oleksandr.shulzhenko.viktorovych, tgopinath, corbet, skhan,
linux, Frank.Li, jorge.marques, pgaj, wsa+renesas,
tommaso.merciai.xr, nuno.sa, Michael.Hennerich, dlechner, andy,
lorenzo, enelsonmoore, rppt, pratyush, giovanni.cabiddu,
gabewhigham, haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng,
ustc.gu, jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
> > Opt-in to driver_override sysfs functionality for I3C subsystem.
> >
> > Update some I3C drivers that are assuming that if their .probe is
> > invoked that the bus match function already checked the ID table. Go
> > ahead and verify in .probe that i3c_device_match_id() returns a valid
> > match before using the i3c_device_id pointer.
> >
> > Signed-off-by: Meagan Lloyd <meaganlloyd@linux.microsoft.com>
> > Assisted-by: Claude:claude-opus-4.8 copilot
>
> Along with splitting these up as this is the sort of change that
> will sometimes need backporting in individual drivers because we are
> layering something on top, I'd look at adding some helpers to skip
> the id as that isn't much used and get the specific fields people
> want.
Yeah, I'll split these up for each individual driver.
i3c_device_match_data() is a good idea. It would reduce the likelihood
that future drivers make the mistake of not checking the id before
dereferencing it for data. I'll include it in the next revision.
Best regards,
Meagan
>
> > diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c
> > index 1c4a58855e2d..98076ae8dce8 100644
> > --- a/drivers/hwmon/tmp108.c
> > +++ b/drivers/hwmon/tmp108.c
> > @@ -581,6 +581,8 @@ static int p3t1085_i3c_probe(struct i3c_device *i3cdev)
> > "Failed to register i3c regmap\n");
> >
> > id = i3c_device_match_id(i3cdev, p3t1085_i3c_ids);
> > + if (!id)
> > + return -ENODEV;
> >
> > return tmp108_common_probe(dev, regmap, "p3t1085_i3c", id->data);
>
> Similar to below, jumping directly to an i3c_device_match_data() helper
> in this series would avoid getting and id that these drivers don't
> need access to.
>
I acknowledge and agree.
> > }
>
> > diff --git a/drivers/iio/adc/ad4062.c b/drivers/iio/adc/ad4062.c
> > index 8e5984055b15..f28b503d16eb 100644
> > --- a/drivers/iio/adc/ad4062.c
> > +++ b/drivers/iio/adc/ad4062.c
> > @@ -1482,14 +1482,20 @@ MODULE_DEVICE_TABLE(i3c, ad4062_id_table);
> >
> > static int ad4062_probe(struct i3c_device *i3cdev)
> > {
> > - const struct i3c_device_id *id = i3c_device_match_id(i3cdev, ad4062_id_table);
> > - const struct ad4062_chip_info *chip = id->data;
> > + const struct i3c_device_id *id;
> > + const struct ad4062_chip_info *chip;
> > struct device *dev = &i3cdev->dev;
> > struct iio_dev *indio_dev;
> > struct ad4062_state *st;
> > bool ref_sel;
> > int ret;
> >
> > + id = i3c_device_match_id(i3cdev, ad4062_id_table);
> > + if (!id)
> > + return -ENODEV;
> > +
> > + chip = id->data;
>
> This might be a good opportunity to introduce
>
> i3c_device_match_data() as in many drivers we don't actually care about
> access to the id itself.
>
> > +
> > indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
> > if (!indio_dev)
> > return -ENOMEM;
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
> > index cb5c5d7e1f3d..153a03e06801 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
> > @@ -32,6 +32,9 @@ static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev)
> > struct device *dev = i3cdev_to_dev(i3cdev);
> > struct regmap *regmap;
> >
>
> Please bring the assignment down to above the check so
>
> id = i3c_device_match_id(i3cdev,st_lsm6dsx_i3c_ids);
>
> Which is what you did in the previous driver...
You're right. It is more readable that way. Thanks!
> > + if (!id)
> > + return -ENODEV;
> > +
> > regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config);
> > if (IS_ERR(regmap)) {
> > dev_err(dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap));
>
>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 2/3] i3c: set i3c_xfer.actual_len in controller drivers
2026-09-13 0:26 ` Jonathan Cameron
@ 2026-09-16 19:11 ` Meagan Lloyd
0 siblings, 0 replies; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-16 19:11 UTC (permalink / raw)
To: Jonathan Cameron
Cc: Meagan Lloyd, linux-i3c, alexandre.belloni, vitor.soares,
samagazaryan, gregkh, arnd, boris.brezillon,
oleksandr.shulzhenko.viktorovych, tgopinath, corbet, skhan,
linux, Frank.Li, jorge.marques, pgaj, wsa+renesas,
tommaso.merciai.xr, nuno.sa, Michael.Hennerich, dlechner, andy,
lorenzo, enelsonmoore, rppt, pratyush, giovanni.cabiddu,
gabewhigham, haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng,
ustc.gu, jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
On Sun, Sep 13, 2026 at 01:26:12AM +0100, Jonathan Cameron wrote:
> On Fri, 11 Sep 2026 14:09:34 -0700
> Meagan Lloyd <meaganlloyd@linux.microsoft.com> wrote:
>
> > Set i3c_xfer.actual_len for reads across the I3C Controller drivers.
> >
> > actual_len provides a consistent location for device drivers to know the
> > read response bytes without overriding the len field.
>
> Hi Meagan,
>
> Why is that useful in this series? Currently this sounds like
> an unrelated change.
>
> Jonathan
Hi Jonathan,
For read operations, user-space allocates and provides a buffer, as well
as indicates how many bytes it would like to/can receive in that buffer.
actual_len allows us to report to user-space how many bytes were
actually received from the device and are thus valid in that buffer.
Thanks,
Meagan
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/3] I3C character device driver using driver_override
2026-09-12 13:26 ` [PATCH 0/3] I3C character device driver using driver_override Andy Shevchenko
@ 2026-09-16 19:28 ` Meagan Lloyd
2026-09-17 6:23 ` Andy Shevchenko
0 siblings, 1 reply; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-16 19:28 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Meagan Lloyd, linux-i3c, alexandre.belloni, vitor.soares,
samagazaryan, gregkh, arnd, boris.brezillon,
oleksandr.shulzhenko.viktorovych, tgopinath, corbet, skhan,
linux, Frank.Li, jorge.marques, pgaj, wsa+renesas,
tommaso.merciai.xr, nuno.sa, Michael.Hennerich, jic23, dlechner,
andy, lorenzo, enelsonmoore, rppt, pratyush, giovanni.cabiddu,
gabewhigham, haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng,
ustc.gu, jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
On Sat, Sep 12, 2026 at 04:26:28PM +0300, Andy Shevchenko wrote:
> On Fri, Sep 11, 2026 at 02:09:32PM -0700, Meagan Lloyd wrote:
> > This is a rework and revival option for Vitor Soares' I3C character
> > device driver patch series from 2020 [1] that I've been exploring for a
> > few months. Recently there was a revival posted to the list [2], so I
> > wanted to share this design option as well.
> >
> > In [1] and [2], the i3cdev driver automatically attaches and detaches
> > depending whether another driver has attached/not. In [1], Boris was
> > suggesting we explore a more straightforward and traditional binding
> > method aligning with the Linux driver model. At the time, there wasn't
> > a way to auto-bind while keeping manual binding possible as they shared
> > the same match() hook. Now with the new driver_override feature,
>
> Where is it new? It's quite an old mechanism in the driver core...
My understanding is that this support was added in March 2026 here:
https://lore.kernel.org/all/20260303115720.48783-1-dakr@kernel.org/
Some busses had their own version of this, but the above series made
a general, re-usable solution available.
>
> > the auto-binding of i3cdev on boot can be avoided if the i3cdev driver has
> > an empty match ID table. After boot, where specialized drivers would have
> > already bound, user-space can explicitly opt-in by setting the
> > driver_override sysfs file with 'i3cdev' and manually binding via sysfs
> > (or by simply loading the driver if it's loadable). This can also be
> > easily automated with udev rules that run whenever the I3C core exposes
> > a new device.
> >
> > One downside of the automatic attach/de-attach is that if a different
> > driver is loaded later, the first driver could have altered something
> > on the device, breaking any assumptions of the subsequent driver.
> >
> > My series builds on [1] through:
> > 0. Addressing code review feedback in [1] from Greg, Boris, and Randy.
> > 1. Using actual_len for accurate read response reporting. The kernel
> > will report actual_len received from the core to user-space via the
> > uapi i3cdev_xfer struct.
> > 2. Placing limits on the number of transfers and bytes in requests to
> > prevent unlimited-sized transfers or kernel memory allocation
> > 3. Checking inputs and descriptive return codes as guard-rails
> > for user-space and to ease use of the i3cdev driver
> > 4. Checking on MWL to ensure that we respect device limits
> > 5. Proper lifetime management of i3cdev_data and underlying device
> > 6. Addressing dangling fops in the event we have an open file descriptor
> > when a device gets unbound.
> > 7. Fast-path locking to ensure transfers complete before a device is
> > unbound.
> > 8. Allowing only one file descriptor per I3C device to avoid bugs
> > around multiple processes interacting with the device and altering
> > the device underneath the other. For example, without this, one process
> > could change the device's page or address pointer register underneath
> > the other process.
> > 9. copy_struct_from_user to ensure struct i3cdev_xfer could be extended
> > in a compatible way. This is to be forward-looking towards potential
> > HDR mode expansion and code reuse.
> > 10. Reserving the IOCTL number formally
> > 11. Updating the Documentation to be a syntax correct example program
> > template.
> > 12. Preserving /dev/bus/i3c/<bus id>-<Provisional ID> naming while
> > allowing sysfs path to be neatly named i3cdev-<minor>. This avoids
> > repeated <bus id>-<Provisional ID> in the sysfs paths which can be
> > confusing/circular-looking.
> > e.g. /sys/bus/i3c/devices/0-deadbeef001/i3cdev/0-deadbeef001 ->
> > /sys/bus/i3c/devices/0-deadbeef001/i3cdev/i3cdev-0
> > 13. Updating all naming references related to i3c_priv_xfer to align
> > with new i3c_xfer struct
> > 14. Updating the MAINTAINERS file for the new pieces of code
> >
> > Note that i3c-tools [3] or a fork of it will need small updates:
> > 1. Update include/uapi/linux/i3c/i3cdev.h to match updated uapi structs
> > 2. In i3ctransfer.c, use actual_len for reads
>
> > I've added MODULE_VERSION("1.0.0") in the i3cdev driver, so i3c-tools
> > could use that to determine whether to use the old out-of-tree uapi or this one.
>
> Absolutely no. This is legacy macro which has no need since Git era. In Git
> the module version is the Git SHA hash of the tip of the used tree. Nobody will
> understand what 1.0.0 means and how it maps to the applied patches (if any of
> them affects the behaviour of the feature in question).
>
> On top of that, upstream has no clue what and how many possible custom ABIs /
> UAPIs exists, and we do not care, to be honest.
>
> > [1] https://lore.kernel.org/linux-i3c/cover.1582069402.git.vitor.soares@synopsys.com/
> > [2] https://lore.kernel.org/linux-i3c/ap_1-gFF7S821xJT@ninjato/T/#m9107c1785a4a16b1b3cb84c3269d17fac35819c8
> > [3] https://github.com/vitor-soares-snps/i3c-tools
>
> --
> With Best Regards,
> Andy Shevchenko
>
My reasoning was that i3c-tools has been around for years now and it may
be in-use assuming the UAPI from [1]. Adding MODULE_VERSION was a low
effort, compatible, and reliable way to keep using the same i3c-tools
repo (and accommodate a changed UAPI).
In any case, in Sam's thread [2], there is talk of moving i3c-tools into
the kernel's tools directory. That's the better solution, so I can drop
the MODULE_VERSION in future revisions.
Thank you,
Meagan
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] i3c: add i3cdev character device module for user-space access
2026-09-12 13:34 ` Andy Shevchenko
@ 2026-09-16 22:57 ` Meagan Lloyd
2026-09-17 5:49 ` Andy Shevchenko
0 siblings, 1 reply; 20+ messages in thread
From: Meagan Lloyd @ 2026-09-16 22:57 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Meagan Lloyd, linux-i3c, alexandre.belloni, vitor.soares,
samagazaryan, gregkh, arnd, boris.brezillon,
oleksandr.shulzhenko.viktorovych, tgopinath, corbet, skhan,
linux, Frank.Li, jorge.marques, pgaj, wsa+renesas,
tommaso.merciai.xr, nuno.sa, Michael.Hennerich, jic23, dlechner,
andy, lorenzo, enelsonmoore, rppt, pratyush, giovanni.cabiddu,
gabewhigham, haren, pasha.tatashin, jirislaby, adrian.ho.yin.ng,
ustc.gu, jszhang, adrian.hunter, akhilrajeev, tze.yee.ng,
manikanta.guntupalli, shubhrajyoti.datta, jarkko.nikula,
linux-doc, linux-kernel, linux-hwmon, linux, linux-iio
On Sat, Sep 12, 2026 at 04:34:01PM +0300, Andy Shevchenko wrote:
> On Fri, Sep 11, 2026 at 02:09:35PM -0700, Meagan Lloyd wrote:
> > The i3cdev driver is a character device driver that allows user-space
> > to control and interact with I3C devices.
>
> > Currently, it has the ability to perform Single Data Rate (SDR)
> > transfers - basic reads/writes.
> >
> > With the addition of sysfs driver_override, there is now a
> > straightforward and direct way to match the i3cdev driver to any i3c
> > device without stepping on the toes of more specialized drivers that are
> > loaded automatically.
>
> Is it safe? Why on the earth do we need this? The commit message has not enough
> information.
>
I can't see a reason that it'd be unsafe. To give additional confidence,
it's already in-use in many bus_types:
drivers/platform/wmi/core.c: .driver_override = true,
drivers/hv/vmbus_drv.c: .driver_override = true,
drivers/base/platform.c: .driver_override = true,
drivers/cdx/cdx.c: .driver_override = true,
drivers/s390/cio/css.c: .driver_override = true,
drivers/vdpa/vdpa.c: .driver_override = true,
drivers/bus/fsl-mc/fsl-mc-bus.c: .driver_override = true,
drivers/pci/pci-driver.c: .driver_override = true,
drivers/rpmsg/rpmsg_core.c: .driver_override = true,
drivers/amba/bus.c: .driver_override = true,
To answer why we need it:
If we want to write i3cdev as a standard device driver, it can't
actually match anything by default. This is because, some devices on the
system may need specific drivers and i3cdev is generic and should
technically match every device.
Since the driver_override is default NULL and is set via sysfs, this
allows any specific drivers on boot to be loaded up and would allow
explicit control on what device i3cdev gets bound to.
This was my rational. I will refine the commit message with more details.
> > This is accomplished by the i3cdev driver not having any entries in
> > the i3c_device_id table. After boot, simply set the driver_override
> > to "i3cdev" and bind the device manually via the sysfs bind knob.
> > This can also be automated with udev rules as well.
> >
> > The character device interface will be exposed at: /dev/bus/i3c/<bus
> > id>-<Provisional ID>
>
> ...
>
> > + struct i3c_xfer xfer = { + .rnw = I3C_WRITE
>
> In such cases always leave a trailing comma. It will reduce possible
> churn in the future.
>
Good point, I'll fix that.
> > + };
>
> ...
>
> > + return !ret ? len : ret;
>
> My gosh, wouldn't Elvis just work naturally?
>
> return ret ?: len;
>
You're right, that is much nicer!
> ...
>
> > + for (int i = 0; i < metadata->nxfers; i++) {
>
> Why is 'i' signed?
>
Mostly for readability and to make sure the line length on loop headers
is kept below 80 chars. As a precaution, to make sure that 'i' can
represent any metadata->nxfers value without overflow during loops, I
check that metadata->nxfers is less than/equal to INT_MAX in
get_metadata().
> > + ret = copy_struct_from_user(k_uxfer, +
> > sizeof(*k_uxfers), + uxfer, + metadata->xfer_size); + if (ret) +
> > goto out_free_k_uxfers; + + /* Enforce that padding must be
> > zero */ + if (memchr_inv(k_uxfer->pad, 0,
> > sizeof(k_uxfer->pad))) { + ret = -EINVAL; + goto
> > out_free_k_uxfers; + } + + uxfer +=
> > metadata->xfer_size; /* u8 pointer so use xfer_size */ + k_uxfer++;
> > /* struct i3cdev_xfer pointer */ + }
>
> ...
>
> > + if (!ret) + total_bytes +=
> > i3c_xfers[i].len; + else + return ret;
>
> Yeah, you really need to reconsider patterns you use in the code. Here
> 'else' is redundant. Homework to understand how (#easy).
>
Thanks for pointing this out. It would read better if I removed the else
block, opting to bail out early if ret is non-zero.
> ...
>
> > +/** + * print_i3c_err() - Prints the I3C error encountered during
> > the prior + * call to the core's transfer function. + * @i3cdev:
> > i3cdev_data object + * @metadata: Kernel's copy of i3cdev_xfers
> > (ioctl I3CDEV_XFER input) + * @i3c_xfers: i3c_xfer array that was
> > sent to the I3C core
>
> > + * Returns: void
>
> Huh?! Where is this coming from?
>
In i3cdev_ioctl_do_xfers, if i3c_device_do_xfers failed, I wanted to
print out the first I3C controller error encountered. The controller
drivers can set this in the i3c_xfer.err field. Hence this function.
It's to aid debugging and provide useful error information.
I can certainly refine the wording on the print_i3c_err documentation
header to make this more clear.
> > + */
>
> ...
>
> Please, rely less on AI and more on the common sense and
> proof-reading.
>
> -- With Best Regards, Andy Shevchenko
I think I gave you the wrong impression. The new contributions in this
series were written and developed by me. I used AI for quality assurance
and cross-referencing. Since I incorporated some AI-flagged suggestions,
I tried to acknowledge that with the Assisted-by tag.
Thank you,
Meagan
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 3/3] i3c: add i3cdev character device module for user-space access
2026-09-16 22:57 ` Meagan Lloyd
@ 2026-09-17 5:49 ` Andy Shevchenko
0 siblings, 0 replies; 20+ messages in thread
From: Andy Shevchenko @ 2026-09-17 5:49 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Wed, Sep 16, 2026 at 03:57:10PM -0700, Meagan Lloyd wrote:
> On Sat, Sep 12, 2026 at 04:34:01PM +0300, Andy Shevchenko wrote:
> > On Fri, Sep 11, 2026 at 02:09:35PM -0700, Meagan Lloyd wrote:
> > > The i3cdev driver is a character device driver that allows user-space
> > > to control and interact with I3C devices.
> >
> > > Currently, it has the ability to perform Single Data Rate (SDR)
> > > transfers - basic reads/writes.
> > >
> > > With the addition of sysfs driver_override, there is now a
> > > straightforward and direct way to match the i3cdev driver to any i3c
> > > device without stepping on the toes of more specialized drivers that are
> > > loaded automatically.
> >
> > Is it safe? Why on the earth do we need this? The commit message has not enough
> > information.
>
> I can't see a reason that it'd be unsafe. To give additional confidence,
> it's already in-use in many bus_types:
This argument has nothing to do with i³c. Each bus is different on a physical
layer, electrical protocols and programming flow. Each of them has own
constraints.
> To answer why we need it:
> If we want to write i3cdev as a standard device driver, it can't
> actually match anything by default. This is because, some devices on the
> system may need specific drivers and i3cdev is generic and should
> technically match every device.
Yes, but I have seen no reason why we should expose i³c bus to the user
space. With i²c we already know very well that it was (and still is)
a bad idea. Why i³c is better (especially taking into account i²c
compatible mode and more complex programming flow)?
> Since the driver_override is default NULL and is set via sysfs, this
> allows any specific drivers on boot to be loaded up and would allow
> explicit control on what device i3cdev gets bound to.
>
> This was my rational. I will refine the commit message with more details.
Put a real life example why the exposing i³c devices into user space is
absolutely necessary.
> > > This is accomplished by the i3cdev driver not having any entries in
> > > the i3c_device_id table. After boot, simply set the driver_override
> > > to "i3cdev" and bind the device manually via the sysfs bind knob.
> > > This can also be automated with udev rules as well.
> > >
> > > The character device interface will be exposed at: /dev/bus/i3c/<bus
> > > id>-<Provisional ID>
...
> > > + for (int i = 0; i < metadata->nxfers; i++) {
> >
> > Why is 'i' signed?
> Mostly for readability and to make sure the line length on loop headers
> is kept below 80 chars. As a precaution, to make sure that 'i' can
> represent any metadata->nxfers value without overflow during loops, I
> check that metadata->nxfers is less than/equal to INT_MAX in
> get_metadata().
No need to add useless checks.
...
> > > +/** + * print_i3c_err() - Prints the I3C error encountered during
> > > the prior + * call to the core's transfer function. + * @i3cdev:
> > > i3cdev_data object + * @metadata: Kernel's copy of i3cdev_xfers
> > > (ioctl I3CDEV_XFER input) + * @i3c_xfers: i3c_xfer array that was
> > > sent to the I3C core
> >
> > > + * Returns: void
> >
> > Huh?! Where is this coming from?
>
> In i3cdev_ioctl_do_xfers, if i3c_device_do_xfers failed, I wanted to
> print out the first I3C controller error encountered. The controller
> drivers can set this in the i3c_xfer.err field. Hence this function.
>
> It's to aid debugging and provide useful error information.
> I can certainly refine the wording on the print_i3c_err documentation
> header to make this more clear.
My point is about kernel-doc. Why do we need the return section for void?
Where it comes from?
> > > + */
...
> > Please, rely less on AI and more on the common sense and
> > proof-reading.
> I think I gave you the wrong impression. The new contributions in this
> series were written and developed by me. I used AI for quality assurance
> and cross-referencing. Since I incorporated some AI-flagged suggestions,
> I tried to acknowledge that with the Assisted-by tag.
I see, then there is a room to improve the code. But the main question is
why do we even need this whole interface to begin with?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [PATCH 0/3] I3C character device driver using driver_override
2026-09-16 19:28 ` Meagan Lloyd
@ 2026-09-17 6:23 ` Andy Shevchenko
0 siblings, 0 replies; 20+ messages in thread
From: Andy Shevchenko @ 2026-09-17 6:23 UTC (permalink / raw)
To: Meagan Lloyd
Cc: linux-i3c, alexandre.belloni, vitor.soares, samagazaryan, gregkh,
arnd, boris.brezillon, oleksandr.shulzhenko.viktorovych,
tgopinath, corbet, skhan, linux, Frank.Li, jorge.marques, pgaj,
wsa+renesas, tommaso.merciai.xr, nuno.sa, Michael.Hennerich,
jic23, dlechner, andy, lorenzo, enelsonmoore, rppt, pratyush,
giovanni.cabiddu, gabewhigham, haren, pasha.tatashin, jirislaby,
adrian.ho.yin.ng, ustc.gu, jszhang, adrian.hunter, akhilrajeev,
tze.yee.ng, manikanta.guntupalli, shubhrajyoti.datta,
jarkko.nikula, linux-doc, linux-kernel, linux-hwmon, linux,
linux-iio
On Wed, Sep 16, 2026 at 12:28:57PM -0700, Meagan Lloyd wrote:
> On Sat, Sep 12, 2026 at 04:26:28PM +0300, Andy Shevchenko wrote:
> > On Fri, Sep 11, 2026 at 02:09:32PM -0700, Meagan Lloyd wrote:
> > > This is a rework and revival option for Vitor Soares' I3C character
> > > device driver patch series from 2020 [1] that I've been exploring for a
> > > few months. Recently there was a revival posted to the list [2], so I
> > > wanted to share this design option as well.
> > >
> > > In [1] and [2], the i3cdev driver automatically attaches and detaches
> > > depending whether another driver has attached/not. In [1], Boris was
> > > suggesting we explore a more straightforward and traditional binding
> > > method aligning with the Linux driver model. At the time, there wasn't
> > > a way to auto-bind while keeping manual binding possible as they shared
> > > the same match() hook. Now with the new driver_override feature,
> >
> > Where is it new? It's quite an old mechanism in the driver core...
>
> My understanding is that this support was added in March 2026 here:
> https://lore.kernel.org/all/20260303115720.48783-1-dakr@kernel.org/
> Some busses had their own version of this, but the above series made
> a general, re-usable solution available.
Nope, the series fixes the bug and at the same time refactored to provide a
generalised solution. The driver_override as a concept exists for ages.
> > > the auto-binding of i3cdev on boot can be avoided if the i3cdev driver has
> > > an empty match ID table. After boot, where specialized drivers would have
> > > already bound, user-space can explicitly opt-in by setting the
> > > driver_override sysfs file with 'i3cdev' and manually binding via sysfs
> > > (or by simply loading the driver if it's loadable). This can also be
> > > easily automated with udev rules that run whenever the I3C core exposes
> > > a new device.
> > > One downside of the automatic attach/de-attach is that if a different
> > > driver is loaded later, the first driver could have altered something
> > > on the device, breaking any assumptions of the subsequent driver.
...
> > > Note that i3c-tools [3] or a fork of it will need small updates:
> > > 1. Update include/uapi/linux/i3c/i3cdev.h to match updated uapi structs
> > > 2. In i3ctransfer.c, use actual_len for reads
> >
> > > I've added MODULE_VERSION("1.0.0") in the i3cdev driver, so i3c-tools
> > > could use that to determine whether to use the old out-of-tree uapi or this one.
> >
> > Absolutely no. This is legacy macro which has no need since Git era. In Git
> > the module version is the Git SHA hash of the tip of the used tree. Nobody will
> > understand what 1.0.0 means and how it maps to the applied patches (if any of
> > them affects the behaviour of the feature in question).
> >
> > On top of that, upstream has no clue what and how many possible custom ABIs /
> > UAPIs exists, and we do not care, to be honest.
> >
> > > [1] https://lore.kernel.org/linux-i3c/cover.1582069402.git.vitor.soares@synopsys.com/
> > > [2] https://lore.kernel.org/linux-i3c/ap_1-gFF7S821xJT@ninjato/T/#m9107c1785a4a16b1b3cb84c3269d17fac35819c8
> > > [3] https://github.com/vitor-soares-snps/i3c-tools
> My reasoning was that i3c-tools has been around for years now and it may
> be in-use assuming the UAPI from [1]. Adding MODULE_VERSION was a low
> effort, compatible, and
> reliable way
Nope, you are mistaken. As I explained the opaque 1.0.0 means nothing. The Git
SHA *is* the version of the code in question.
> to keep using the same i3c-tools
> repo (and accommodate a changed UAPI).
>
> In any case, in Sam's thread [2], there is talk of moving i3c-tools into
> the kernel's tools directory. That's the better solution, so I can drop
> the MODULE_VERSION in future revisions.
This is really orthogonal. But yes, keeping tools at the kernel source tree
makes sense for a better maintenance.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-09-17 6:24 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-11 21:09 [PATCH 0/3] I3C character device driver using driver_override Meagan Lloyd
2026-09-11 21:09 ` [PATCH 1/3] i3c: master: enable driver_override for I3C Meagan Lloyd
2026-09-11 21:36 ` Guenter Roeck
2026-09-16 18:29 ` Meagan Lloyd
2026-09-12 13:22 ` Andy Shevchenko
2026-09-16 18:39 ` Meagan Lloyd
2026-09-13 0:24 ` Jonathan Cameron
2026-09-16 18:53 ` Meagan Lloyd
2026-09-11 21:09 ` [PATCH 2/3] i3c: set i3c_xfer.actual_len in controller drivers Meagan Lloyd
2026-09-13 0:26 ` Jonathan Cameron
2026-09-16 19:11 ` Meagan Lloyd
2026-09-11 21:09 ` [PATCH 3/3] i3c: add i3cdev character device module for user-space access Meagan Lloyd
2026-09-11 23:29 ` Randy Dunlap
2026-09-16 18:37 ` Meagan Lloyd
2026-09-12 13:34 ` Andy Shevchenko
2026-09-16 22:57 ` Meagan Lloyd
2026-09-17 5:49 ` Andy Shevchenko
2026-09-12 13:26 ` [PATCH 0/3] I3C character device driver using driver_override Andy Shevchenko
2026-09-16 19:28 ` Meagan Lloyd
2026-09-17 6:23 ` Andy Shevchenko
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®