From f00a73037c18b802d4e2167ee01050457dd90e95 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Wed, 27 May 2026 19:55:57 +0200 Subject: [PATCH 01/27] ACPICA: Add LVR to acrestyp.h ANBZ: #44797 commit f0ee0b0927f78adfd9ba4f57b42a07645b1526a3 upstream. Add a new field called lvr to struct acpi_resource_i2c_serialbus. Link: https://github.com/acpica/acpica/commit/e62e74baf7e0 Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2354060.iZASKD2KPV@rafael.j.wysocki Signed-off-by: Shuai Xue --- include/acpi/acrestyp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/acpi/acrestyp.h b/include/acpi/acrestyp.h index 842f932e2c2b..38a19b1d19ac 100644 --- a/include/acpi/acrestyp.h +++ b/include/acpi/acrestyp.h @@ -423,6 +423,7 @@ struct acpi_resource_i2c_serialbus { ACPI_RESOURCE_SERIAL_COMMON u8 access_mode; u16 slave_address; u32 connection_speed; + u8 lvr; }; /* Values for access_mode field above */ -- Gitee From 709906d0ac120f7ac06cbcbb9f018ae8df055006 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Wed, 27 May 2026 19:56:38 +0200 Subject: [PATCH 02/27] ACPICA: Fetch LVR I2C resource descriptor ANBZ: #44797 commit 468adc6b1ff83431644cf002a1850010d7ff32dd upstream. Add LVR I2C resource entry to acpi_rs_convert_i2c_serial_bus[]. Link: https://github.com/acpica/acpica/commit/c40411823510 Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/23121545.EfDdHjke4D@rafael.j.wysocki Signed-off-by: Shuai Xue --- drivers/acpi/acpica/rsserial.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index 279bfa27da94..3e4a1fe81ef6 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -315,7 +315,7 @@ struct acpi_rsconvert_info acpi_rs_convert_csi2_serial_bus[14] = { * ******************************************************************************/ -struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = { +struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[18] = { {ACPI_RSC_INITGET, ACPI_RESOURCE_TYPE_SERIAL_BUS, ACPI_RS_SIZE(struct acpi_resource_i2c_serialbus), ACPI_RSC_TABLE_SIZE(acpi_rs_convert_i2c_serial_bus)}, @@ -391,6 +391,10 @@ struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[17] = { AML_OFFSET(i2c_serial_bus.type_specific_flags), 0}, + {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr), + AML_OFFSET(i2c_serial_bus.type_specific_flags) + 1, + 0}, + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed), AML_OFFSET(i2c_serial_bus.connection_speed), 1}, -- Gitee From 67c6f5d5d354b6e67f5bcdbbf64de92ab63dd365 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Wed, 27 May 2026 19:57:13 +0200 Subject: [PATCH 03/27] ACPICA: Change LVR to 8 bit value ANBZ: #44797 commit d364d76f3d0ccba6c8b0e3b0df348b4e86a0a72b upstream. In the LVR I2C resource entry to acpi_rs_convert_i2c_serial_bus[]. Link: https://github.com/acpica/acpica/commit/7650d4a889ea Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3952474.kQq0lBPeGt@rafael.j.wysocki Signed-off-by: Shuai Xue --- drivers/acpi/acpica/rsserial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index 3e4a1fe81ef6..1119c64795a7 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -391,7 +391,7 @@ struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[18] = { AML_OFFSET(i2c_serial_bus.type_specific_flags), 0}, - {ACPI_RSC_1BITFLAG, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr), + {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr), AML_OFFSET(i2c_serial_bus.type_specific_flags) + 1, 0}, -- Gitee From 8027ed69daf937b831cf78efa2ed95a55f91b2c8 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Wed, 27 May 2026 19:57:52 +0200 Subject: [PATCH 04/27] ACPICA: Mention the LVR bits ANBZ: #44797 commit 53a3a7723c9eac56c47003291b52f106734eb438 upstream. Add a comment mentioning the LVR byte position in the type_specific_flag. Link: https://github.com/acpica/acpica/commit/014fa9f2dbcc Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/9627007.CDJkKcVGEf@rafael.j.wysocki Signed-off-by: Shuai Xue --- drivers/acpi/acpica/rsserial.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index 1119c64795a7..7d7ee3af7272 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -391,6 +391,7 @@ struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[18] = { AML_OFFSET(i2c_serial_bus.type_specific_flags), 0}, + /* Read LVR from Type Specific Flags, bits[15:8] */ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr), AML_OFFSET(i2c_serial_bus.type_specific_flags) + 1, 0}, -- Gitee From d23b8aed093a3e2d56337a203bb7c6ae88d7a9ee Mon Sep 17 00:00:00 2001 From: Akhil R Date: Wed, 27 May 2026 19:58:29 +0200 Subject: [PATCH 05/27] ACPICA: fix I2C LVR item count in the conversion table ANBZ: #44797 commit 2543fbb21642f740288e3c292cab03cd611f35a4 upstream. For ACPI_RSC_MOVE8, the 'Value' field in struct acpi_rsconvert_info is the item count count and not a bit position like for the bitflags. Set 'Value' as '1' to fix this. Conversion still works coincidentally with '0' because item_count is not reset between table entries, and the previous count value was taking effect. Link: https://github.com/acpica/acpica/commit/70082dc8fc84 Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/6164740.MhkbZ0Pkbq@rafael.j.wysocki Signed-off-by: Shuai Xue --- drivers/acpi/acpica/rsserial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index 7d7ee3af7272..5ab41e9b9039 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -394,7 +394,7 @@ struct acpi_rsconvert_info acpi_rs_convert_i2c_serial_bus[18] = { /* Read LVR from Type Specific Flags, bits[15:8] */ {ACPI_RSC_MOVE8, ACPI_RS_OFFSET(data.i2c_serial_bus.lvr), AML_OFFSET(i2c_serial_bus.type_specific_flags) + 1, - 0}, + 1}, {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.i2c_serial_bus.connection_speed), AML_OFFSET(i2c_serial_bus.connection_speed), -- Gitee From ab685f51058c19b613053b12804fee6a1e3f40c8 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 30 Jun 2026 10:29:04 -0700 Subject: [PATCH 06/27] i3c: dw: avoid shift-out-of-bounds when DAA assigns no devices ANBZ: #44797 commit 038cf48b3170af26a70bf2dee4f8c3ac910f5176 upstream. On an empty bus ENTDAA assigns nothing, so cmd->rx_len (the count of addresses left unassigned) equals master->maxdevs. The GENMASK() index master->maxdevs - cmd->rx_len - 1 then becomes -1, which trips up UBSAN. This happens every time on boot on a Gigabyte/AMD server: UBSAN: shift-out-of-bounds in drivers/i3c/master/dw-i3c-master.c:905:12 shift exponent 64 is too large for 64-bit type 'long unsigned int' CPU: 7 UID: 0 PID: 963 Comm: (udev-worker) Not tainted 7.0.11-200.fc44.x86_64 #1 PREEMPT(lazy) Hardware name: Giga Computing E163-Z34-AAH1-000/MZ33-DC1-000, BIOS R32_F45 04/01/2026 Call Trace: dump_stack_lvl+0x5d/0x80 ubsan_epilogue+0x5/0x2b __ubsan_handle_shift_out_of_bounds.cold+0xd7/0x1ab dw_i3c_master_daa.cold+0x1b/0x96 [dw_i3c_master] i3c_master_do_daa_ext.part.0+0x3e/0xf0 [i3c] Skip the mask when no new device was assigned. Fixes: 1dd728f5d4d4 ("i3c: master: Add driver for Synopsys DesignWare IP") Signed-off-by: Jakub Kicinski Reviewed-by: Frank Li Link: https://patch.msgid.link/20260630172904.2662160-1-kuba@kernel.org Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master/dw-i3c-master.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 259e4f527665..ec8c5ec38958 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -902,7 +902,15 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m) if (!wait_for_completion_timeout(&xfer->comp, XFER_TIMEOUT)) dw_i3c_master_dequeue_xfer(master, xfer); - newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0); + /* + * cmd->rx_len holds the number of addresses ENTDAA left unassigned. + * On an empty bus rx_len == maxdevs, so avoid GENMASK(-1, 0). + */ + if (cmd->rx_len >= master->maxdevs) + newdevs = 0; + else + newdevs = GENMASK(master->maxdevs - cmd->rx_len - 1, 0); + newdevs &= ~olddevs; for (pos = 0; pos < master->maxdevs; pos++) { -- Gitee From 319fbdaf934507add9368ef13f6e257bac0dbe41 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 23 Jul 2026 10:57:47 +0300 Subject: [PATCH 07/27] i3c: master: Fix info leak and UAF in device unregister path ANBZ: #44797 commit d2c743efd2d1ee64e94324664808f623dd865872 upstream. i3c_master_unregister_i3c_devs() clears i3cdev->dev->desc before calling device_unregister(). During device_unregister(), device_del() emits a KOBJ_REMOVE uevent and unbinds the driver while the device descriptor is still expected to be valid. As a result, i3c_device_uevent() and a racing modalias_show() can observe a NULL desc and fall back to an uninitialized stack struct i3c_device_info, leaking kernel stack contents in the generated modalias. Driver .remove() callbacks may also encounter an unexpected NULL desc during unbind. Keep desc valid until device_unregister() has completed. Since device_unregister() drops the device reference and may free the device, take an extra reference with get_device() before unregistering. Clear desc afterwards and release the extra reference with put_device(). This preserves the release-time invariant that desc must be NULL while avoiding both the information leak and a potential use-after-free from writing desc after the device has been released. Reported-by: sashiko-bot@kernel.org Link: https://lore.kernel.org/linux-i3c/20260702190003.8BF741F000E9@smtp.kernel.org/ Fixes: 3a379bbcea0a ("i3c: Add core I3C infrastructure") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260723075747.34049-1-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 9e6be49bebb2..1822e74c44f1 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2732,11 +2732,12 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master) if (!i3cdev->dev) continue; - i3cdev->dev->desc = NULL; - if (device_is_registered(&i3cdev->dev->dev)) + if (device_is_registered(&i3cdev->dev->dev)) { + get_device(&i3cdev->dev->dev); device_unregister(&i3cdev->dev->dev); - else - put_device(&i3cdev->dev->dev); + } + i3cdev->dev->desc = NULL; + put_device(&i3cdev->dev->dev); i3cdev->dev = NULL; } } -- Gitee From 5612f45b47789d047f8bc6a7f1a9fe12ea6f1370 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:43 +0000 Subject: [PATCH 08/27] dt-bindings: i3c: Add mipi-i3c-static-method to support SETAASA ANBZ: #44797 commit 81e7c27b0d5cb3029fc01374c3a96019d3a9e673 upstream. Add the 'mipi-i3c-static-method' property mentioned in the MIPI I3C Discovery and Configuration Specification [1] to specify which discovery method an I3C device supports during bus initialization. The property is a bitmap, where a bit value of 1 indicates support for that method, and 0 indicates lack of support. Bit 0: SETDASA CCC (Direct) Bit 1: SETAASA CCC (Broadcast) Bit 2: Other CCC (vendor / standards extension) All other bits are reserved. It is specifically needed when an I3C device requires SETAASA for the address assignment. SETDASA will be supported by default if this property is absent, which means for now the property just serves as a flag to enable SETAASA, but keep the property as a bitmap to align with the specifications. [1] https://www.mipi.org/mipi-disco-for-i3c-download Reviewed-by: Frank Li Reviewed-by: Rob Herring (Arm) Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-2-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- .../devicetree/bindings/i3c/i3c.yaml | 36 ++++++++++++++++--- include/dt-bindings/i3c/i3c.h | 4 +++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/i3c/i3c.yaml b/Documentation/devicetree/bindings/i3c/i3c.yaml index e25fa72fd785..5603f2e7807d 100644 --- a/Documentation/devicetree/bindings/i3c/i3c.yaml +++ b/Documentation/devicetree/bindings/i3c/i3c.yaml @@ -31,10 +31,12 @@ properties: described in the device tree, which in turn means we have to describe I3C devices. - Another use case for describing an I3C device in the device tree is when - this I3C device has a static I2C address and we want to assign it a - specific I3C dynamic address before the DAA takes place (so that other - devices on the bus can't take this dynamic address). + Other use-cases for describing an I3C device in the device tree are: + - When the I3C device has a static I2C address and we want to assign + it a specific I3C dynamic address before the DAA takes place (so + that other devices on the bus can't take this dynamic address). + - When the I3C device requires SETAASA for its discovery and uses a + pre-defined static address. "#size-cells": const: 0 @@ -145,7 +147,31 @@ patternProperties: Dynamic address to be assigned to this device. In case static address is present (first cell of the reg property != 0), this address is assigned through SETDASA. If static address is not present, this address is assigned - through SETNEWDA after assigning a temporary address via ENTDAA. + through SETNEWDA after assigning a temporary address via ENTDAA. If + SETAASA is used, this property is not used, and the static address itself + becomes the dynamic address. + + mipi-i3c-static-method: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0x1 + maximum: 0x7 + default: 1 + description: | + Bitmap describing which methods of Dynamic Address Assignment from a + static address are supported by this I3C Target. For each defined bit + position, a set bit indicates support for that method and a cleared + bit indicates lack of support. + + Bit 0: SETDASA CCC (Direct) + Bit 1: SETAASA CCC (Broadcast) + Bit 2: Other CCC (vendor / standards extension) + All other bits are reserved. + + This property follows the MIPI I3C specification. The primary use + of this property is to indicate support for SETAASA, i.e Bit 1, but + will allow other values mentioned in the specification so that it + mirrors the specification. SETDASA will remain as the default method + even if this property is not present. required: - reg diff --git a/include/dt-bindings/i3c/i3c.h b/include/dt-bindings/i3c/i3c.h index 373439218bba..78b8c634aad8 100644 --- a/include/dt-bindings/i3c/i3c.h +++ b/include/dt-bindings/i3c/i3c.h @@ -13,4 +13,8 @@ #define I2C_NO_FILTER_HIGH_FREQUENCY (1 << 5) #define I2C_NO_FILTER_LOW_FREQUENCY (2 << 5) +#define I3C_ADDR_METHOD_SETDASA (1 << 0) +#define I3C_ADDR_METHOD_SETAASA (1 << 1) +#define I3C_ADDR_METHOD_VENDOR (1 << 2) + #endif -- Gitee From f07b099b8449d077daf13c3de5a4ca2c9795c158 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:44 +0000 Subject: [PATCH 09/27] i3c: master: Use unified device property interface ANBZ: #44797 commit ee170021bee17124c13f42813607bca553a6b48a upstream. Replace all OF-specific functions with unified device property functions as a prerequisite to support both ACPI and device tree. Reviewed-by: Frank Li Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-3-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 77 +++++++++++++++++++++----------------- include/linux/i3c/master.h | 5 ++- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 1822e74c44f1..4dd7425b9406 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -11,10 +11,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -497,7 +499,7 @@ static void i3c_bus_cleanup(struct i3c_bus *i3cbus) mutex_unlock(&i3c_core_lock); } -static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np) +static int i3c_bus_init(struct i3c_bus *i3cbus, struct fwnode_handle *fwnode) { int ret, start, end, id = -1; @@ -507,8 +509,8 @@ static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np) i3c_bus_init_addrslots(i3cbus); i3cbus->mode = I3C_BUS_MODE_PURE; - if (np) - id = of_alias_get_id(np, "i3c"); + if (fwnode && is_of_node(fwnode)) + id = of_alias_get_id(to_of_node(fwnode), "i3c"); mutex_lock(&i3c_core_lock); if (id >= 0) { @@ -784,7 +786,7 @@ static void i3c_masterdev_release(struct device *dev) WARN_ON(!list_empty(&bus->devs.i2c) || !list_empty(&bus->devs.i3c)); i3c_bus_cleanup(bus); - of_node_put(dev->of_node); + fwnode_handle_put(dev->fwnode); } static const struct device_type i3c_masterdev_type = { @@ -968,7 +970,7 @@ static void i3c_device_release(struct device *dev) WARN_ON(i3cdev->desc); - of_node_put(i3cdev->dev.of_node); + fwnode_handle_put(dev->fwnode); kfree(i3cdev); } @@ -1756,7 +1758,7 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) desc->info.pid); if (desc->boardinfo) - desc->dev->dev.of_node = desc->boardinfo->of_node; + device_set_node(&desc->dev->dev, desc->boardinfo->fwnode); ret = device_register(&desc->dev->dev); if (ret) { @@ -2375,8 +2377,8 @@ EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); #define OF_I3C_REG1_IS_I2C_DEV BIT(31) static int -of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, - struct device_node *node, u32 *reg) +i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, + struct fwnode_handle *fwnode, u32 *reg) { struct i2c_dev_boardinfo *boardinfo; struct device *dev = &master->dev; @@ -2386,9 +2388,13 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, if (!boardinfo) return -ENOMEM; - ret = of_i2c_get_board_info(dev, node, &boardinfo->base); - if (ret) - return ret; + if (is_of_node(fwnode)) { + ret = of_i2c_get_board_info(dev, to_of_node(fwnode), &boardinfo->base); + if (ret) + return ret; + } else { + return -EINVAL; + } /* * The I3C Specification does not clearly say I2C devices with 10-bit @@ -2404,14 +2410,14 @@ of_i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, boardinfo->lvr = reg[2]; list_add_tail(&boardinfo->node, &master->boardinfo.i2c); - of_node_get(node); + fwnode_handle_get(fwnode); return 0; } static int -of_i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, - struct device_node *node, u32 *reg) +i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, + struct fwnode_handle *fwnode, u32 *reg) { struct i3c_dev_boardinfo *boardinfo; struct device *dev = &master->dev; @@ -2434,7 +2440,7 @@ of_i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, boardinfo->static_addr = reg[0]; - if (!of_property_read_u32(node, "assigned-address", &init_dyn_addr)) { + if (!fwnode_property_read_u32(fwnode, "assigned-address", &init_dyn_addr)) { if (init_dyn_addr > I3C_MAX_ADDR) return -EINVAL; @@ -2451,14 +2457,14 @@ of_i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, return -EINVAL; boardinfo->init_dyn_addr = init_dyn_addr; - boardinfo->of_node = of_node_get(node); + boardinfo->fwnode = fwnode_handle_get(fwnode); list_add_tail(&boardinfo->node, &master->boardinfo.i3c); return 0; } -static int of_i3c_master_add_dev(struct i3c_master_controller *master, - struct device_node *node) +static int i3c_master_add_dev(struct i3c_master_controller *master, + struct fwnode_handle *fwnode) { u32 reg[3]; int ret; @@ -2466,7 +2472,7 @@ static int of_i3c_master_add_dev(struct i3c_master_controller *master, if (!master) return -EINVAL; - ret = of_property_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg)); + ret = fwnode_property_read_u32_array(fwnode, "reg", reg, ARRAY_SIZE(reg)); if (ret) return ret; @@ -2475,25 +2481,25 @@ static int of_i3c_master_add_dev(struct i3c_master_controller *master, * dealing with an I2C device. */ if (!reg[1]) - ret = of_i3c_master_add_i2c_boardinfo(master, node, reg); + ret = i3c_master_add_i2c_boardinfo(master, fwnode, reg); else - ret = of_i3c_master_add_i3c_boardinfo(master, node, reg); + ret = i3c_master_add_i3c_boardinfo(master, fwnode, reg); return ret; } -static int of_populate_i3c_bus(struct i3c_master_controller *master) +static int fwnode_populate_i3c_bus(struct i3c_master_controller *master) { struct device *dev = &master->dev; - struct device_node *i3cbus_np = dev->of_node; + struct fwnode_handle *fwnode = dev_fwnode(dev); int ret; u32 val; - if (!i3cbus_np) + if (!fwnode) return 0; - for_each_available_child_of_node_scoped(i3cbus_np, node) { - ret = of_i3c_master_add_dev(master, node); + fwnode_for_each_available_child_node_scoped(fwnode, child) { + ret = i3c_master_add_dev(master, child); if (ret) return ret; } @@ -2503,10 +2509,10 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master) * on the bus are not supporting typical rates, or if the bus topology * prevents it from using max possible rate. */ - if (!of_property_read_u32(i3cbus_np, "i2c-scl-hz", &val)) + if (!device_property_read_u32(dev, "i2c-scl-hz", &val)) master->bus.scl_rate.i2c = val; - if (!of_property_read_u32(i3cbus_np, "i3c-scl-hz", &val)) + if (!device_property_read_u32(dev, "i3c-scl-hz", &val)) master->bus.scl_rate.i3c = val; return 0; @@ -2561,7 +2567,7 @@ static u8 i3c_master_i2c_get_lvr(struct i2c_client *client) u8 lvr = I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE; u32 reg[3]; - if (!of_property_read_u32_array(client->dev.of_node, "reg", reg, ARRAY_SIZE(reg))) + if (!fwnode_property_read_u32_array(client->dev.fwnode, "reg", reg, ARRAY_SIZE(reg))) lvr = reg[2]; return lvr; @@ -2680,7 +2686,8 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master) struct i2c_adapter *adap = i3c_master_to_i2c_adapter(master); struct i2c_dev_desc *i2cdev; struct i2c_dev_boardinfo *i2cboardinfo; - int ret, id; + struct fwnode_handle *fwnode = dev_fwnode(&master->dev); + int ret, id = -1; adap->dev.parent = master->dev.parent; adap->owner = master->dev.parent->driver->owner; @@ -2689,7 +2696,9 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master) adap->timeout = HZ; adap->retries = 3; - id = of_alias_get_id(master->dev.of_node, "i2c"); + if (fwnode && is_of_node(fwnode)) + id = of_alias_get_id(to_of_node(fwnode), "i2c"); + if (id >= 0) { adap->nr = id; ret = i2c_add_numbered_adapter(adap); @@ -2997,7 +3006,7 @@ int i3c_master_register(struct i3c_master_controller *master, return ret; master->dev.parent = parent; - master->dev.of_node = of_node_get(parent->of_node); + device_set_node(&master->dev, fwnode_handle_get(dev_fwnode(parent))); master->dev.bus = &i3c_bus_type; master->dev.type = &i3c_masterdev_type; master->dev.release = i3c_masterdev_release; @@ -3016,13 +3025,13 @@ int i3c_master_register(struct i3c_master_controller *master, master->dev.coherent_dma_mask = parent->coherent_dma_mask; master->dev.dma_parms = parent->dma_parms; - ret = i3c_bus_init(i3cbus, master->dev.of_node); + ret = i3c_bus_init(i3cbus, dev_fwnode(&master->dev)); if (ret) goto err_put_dev; dev_set_name(&master->dev, "i3c-%d", i3cbus->id); - ret = of_populate_i3c_bus(master); + ret = fwnode_populate_i3c_bus(master); if (ret) goto err_put_dev; diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 592b646f6134..6b03a3ce574c 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -177,7 +177,8 @@ struct i3c_device_ibi_info { * @pid: I3C Provisioned ID exposed by the device. This is a unique identifier * that may be used to attach boardinfo to i3c_dev_desc when the device * does not have a static address - * @of_node: optional DT node in case the device has been described in the DT + * @fwnode: Firmware node (DT or ACPI) in case the device has been + * described in firmware * * This structure is used to attach board-level information to an I3C device. * Not all I3C devices connected on the bus will have a boardinfo. It's only @@ -189,7 +190,7 @@ struct i3c_dev_boardinfo { u8 init_dyn_addr; u8 static_addr; u64 pid; - struct device_node *of_node; + struct fwnode_handle *fwnode; }; /** -- Gitee From 1273ee404f22440e9fd0427cf5e959e290053629 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:45 +0000 Subject: [PATCH 10/27] i3c: master: Support ACPI enumeration of child devices ANBZ: #44797 commit b46a4b3c5d1e312e74e5364a04c283a5c88e0916 upstream. Although the existing subsystem allows host controllers to register through the ACPI table, it was not possible to describe I3C or I2C devices when using ACPI. This is because the driver relied on the reg property to retrieve the PID, static address, etc., whereas ACPI uses _ADR or serial resources to describe such devices. Read _ADR and LVR from ACPI resources and extract the data as per the ACPI specification for an I3C bus. Also read mipi-i3c-static-address as per the MIPI DISCO specifications [1] to get the static address to be used. Enable describing I3C or I2C devices in the ACPI table. This is required if the device uses a static address or if it needs device-specific properties. [1] https://www.mipi.org/mipi-disco-for-i3c-download Reviewed-by: Frank Li Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-4-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 151 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 143 insertions(+), 8 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 4dd7425b9406..b0731db1cd19 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -5,6 +5,7 @@ * Author: Boris Brezillon */ +#include #include #include #include @@ -2376,6 +2377,55 @@ EXPORT_SYMBOL_GPL(i3c_master_add_i3c_dev_locked); #define OF_I3C_REG1_IS_I2C_DEV BIT(31) +#ifdef CONFIG_ACPI +static int i3c_acpi_get_i2c_resource(struct acpi_resource *ares, void *data) +{ + struct i2c_dev_boardinfo *boardinfo = data; + struct acpi_resource_i2c_serialbus *sb; + + if (boardinfo->base.addr || !i2c_acpi_get_i2c_resource(ares, &sb)) + return 1; + + boardinfo->base.addr = sb->slave_address; + if (sb->access_mode == ACPI_I2C_10BIT_MODE) + boardinfo->base.flags |= I2C_CLIENT_TEN; + + boardinfo->lvr = sb->lvr; + + return 1; +} + +static int i3c_acpi_add_i2c_boardinfo(struct i2c_dev_boardinfo *boardinfo, + struct fwnode_handle *fwnode) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + LIST_HEAD(resources); + int ret; + + boardinfo->base.fwnode = acpi_fwnode_handle(adev); + acpi_set_modalias(adev, dev_name(&adev->dev), boardinfo->base.type, + sizeof(boardinfo->base.type)); + + ret = acpi_dev_get_resources(adev, &resources, + i3c_acpi_get_i2c_resource, boardinfo); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resources); + + if (!boardinfo->base.addr) + return -ENODEV; + + return 0; +} +#else +static inline int i3c_acpi_add_i2c_boardinfo(struct i2c_dev_boardinfo *boardinfo, + struct fwnode_handle *fwnode) +{ + return -ENODEV; +} +#endif + static int i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, struct fwnode_handle *fwnode, u32 *reg) @@ -2392,6 +2442,15 @@ i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, ret = of_i2c_get_board_info(dev, to_of_node(fwnode), &boardinfo->base); if (ret) return ret; + + /* LVR is encoded in reg[2] for Device Tree. */ + boardinfo->lvr = reg[2]; + } else if (is_acpi_device_node(fwnode)) { + ret = i3c_acpi_add_i2c_boardinfo(boardinfo, fwnode); + if (ret) { + devm_kfree(dev, boardinfo); + return ret; + } } else { return -EINVAL; } @@ -2406,9 +2465,6 @@ i3c_master_add_i2c_boardinfo(struct i3c_master_controller *master, return -EOPNOTSUPP; } - /* LVR is encoded in reg[2]. */ - boardinfo->lvr = reg[2]; - list_add_tail(&boardinfo->node, &master->boardinfo.i2c); fwnode_handle_get(fwnode); @@ -2463,8 +2519,8 @@ i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, return 0; } -static int i3c_master_add_dev(struct i3c_master_controller *master, - struct fwnode_handle *fwnode) +static int i3c_master_add_of_dev(struct i3c_master_controller *master, + struct fwnode_handle *fwnode) { u32 reg[3]; int ret; @@ -2488,6 +2544,74 @@ static int i3c_master_add_dev(struct i3c_master_controller *master, return ret; } +#ifdef CONFIG_ACPI +static int i3c_master_add_acpi_dev(struct i3c_master_controller *master, + struct fwnode_handle *fwnode) +{ + struct acpi_device *adev = to_acpi_device_node(fwnode); + acpi_bus_address adr; + u32 reg[3] = { 0 }; + int ret; + + /* + * If the ACPI table entry has _ADR method, it's an I3C device. + * Otherwise it may be an I2C device described by an I2cSerialBus + * resource. If no I2cSerialBus resource is found, ignore the entry. + */ + if (!acpi_has_method(adev->handle, "_ADR")) { + ret = i3c_master_add_i2c_boardinfo(master, fwnode, reg); + if (ret == -ENODEV) + return 0; + + return ret; + } + + adr = acpi_device_adr(adev); + + /* For I3C devices, _ADR will have the 48 bit PID of the device */ + reg[1] = upper_32_bits(adr); + reg[2] = lower_32_bits(adr); + + fwnode_property_read_u32(fwnode, "mipi-i3c-static-address", ®[0]); + + return i3c_master_add_i3c_boardinfo(master, fwnode, reg); +} + +static u8 i3c_acpi_i2c_get_lvr(struct i2c_client *client) +{ + struct acpi_device *adev = to_acpi_device_node(client->dev.fwnode); + struct i2c_dev_boardinfo boardinfo = {}; + LIST_HEAD(resources); + int ret; + u8 lvr; + + lvr = I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE; + + ret = acpi_dev_get_resources(adev, &resources, + i3c_acpi_get_i2c_resource, &boardinfo); + if (ret < 0) + return lvr; + + if (boardinfo.base.addr) + lvr = boardinfo.lvr; + + acpi_dev_free_resource_list(&resources); + + return lvr; +} +#else +static inline int i3c_master_add_acpi_dev(struct i3c_master_controller *master, + struct fwnode_handle *fwnode) +{ + return -ENODEV; +} + +static inline u8 i3c_acpi_i2c_get_lvr(struct i2c_client *client) +{ + return I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE; +} +#endif + static int fwnode_populate_i3c_bus(struct i3c_master_controller *master) { struct device *dev = &master->dev; @@ -2499,7 +2623,13 @@ static int fwnode_populate_i3c_bus(struct i3c_master_controller *master) return 0; fwnode_for_each_available_child_node_scoped(fwnode, child) { - ret = i3c_master_add_dev(master, child); + if (is_of_node(child)) + ret = i3c_master_add_of_dev(master, child); + else if (is_acpi_device_node(child)) + ret = i3c_master_add_acpi_dev(master, child); + else + continue; + if (ret) return ret; } @@ -2567,8 +2697,13 @@ static u8 i3c_master_i2c_get_lvr(struct i2c_client *client) u8 lvr = I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE; u32 reg[3]; - if (!fwnode_property_read_u32_array(client->dev.fwnode, "reg", reg, ARRAY_SIZE(reg))) - lvr = reg[2]; + if (is_of_node(client->dev.fwnode)) { + if (!fwnode_property_read_u32_array(client->dev.fwnode, "reg", + reg, ARRAY_SIZE(reg))) + lvr = reg[2]; + } else if (is_acpi_device_node(client->dev.fwnode)) { + lvr = i3c_acpi_i2c_get_lvr(client); + } return lvr; } -- Gitee From eba56b4f86e931de697510f679e3a5d3b315bc5a Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:46 +0000 Subject: [PATCH 11/27] i3c: master: Add support for devices using SETAASA ANBZ: #44797 commit bbaf8733b84846897d2d3b997ce650dd2d2539a4 upstream. Add support for devices using SETAASA, such as SPD5118 and SPD5108 attached to DDR5 memory modules that do not support ENTDAA. Follow the guidelines proposed by the MIPI Discovery and Configuration Specification [1] for discovering such devices. SETAASA (Set All Addresses to Static Address) differs from standard I3C address assignment that uses ENTDAA or SETDASA to assign dynamic addresses. Devices using SETAASA assign their pre-defined static addresses as their dynamic addresses during DAA, and it is not mandatory for these devices to implement standard CCC commands like GETPID, GETDCR, or GETBCR. For such devices, it is generally recommended to issue SETHID (specified by JEDEC JESD300) as a prerequisite for SETAASA to stop HID bit flipping. [1] https://www.mipi.org/mipi-disco-for-i3c-download Signed-off-by: Akhil R Link: https://www.mipi.org/mipi-disco-for-i3c-download Link: https://patch.msgid.link/20260728065955.809445-5-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 114 ++++++++++++++++++++++++++++++++++++- include/linux/i3c/ccc.h | 1 + include/linux/i3c/master.h | 17 +++++- 3 files changed, 128 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index b0731db1cd19..a8d881777f5b 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -5,6 +5,7 @@ * Author: Boris Brezillon */ +#include #include #include #include @@ -1022,6 +1023,51 @@ static int i3c_master_rstdaa_locked(struct i3c_master_controller *master, return ret; } +/** + * i3c_master_setaasa_locked() - start a SETAASA procedure (Set All Addresses to Static Address) + * @master: I3C master object + * + * Send a SETAASA CCC command to set all attached I3C devices' dynamic addresses to + * their static address. + * + * This function must be called with the bus lock held in write mode. + * + * First, the SETHID CCC command is sent, followed by the SETAASA CCC. + * + * Return: 0 in case of success, a positive I3C error code if the error is + * one of the official Mx error codes, and a negative error code otherwise. + */ +static int i3c_master_setaasa_locked(struct i3c_master_controller *master) +{ + struct i3c_ccc_cmd_dest dest; + struct i3c_ccc_cmd cmd; + int ret; + + /* + * Send SETHID CCC command. Though it is a standard CCC command specified + * in JESD300-5, we are not defining a separate macro to be explicit that + * the value falls under the vendor specific range. + */ + i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 0); + i3c_ccc_cmd_init(&cmd, false, I3C_CCC_VENDOR(0, true), &dest, 1); + ret = i3c_master_send_ccc_cmd_locked(master, &cmd); + i3c_ccc_cmd_dest_cleanup(&dest); + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + if (ret) + return ret; + + /* Send SETAASA CCC command */ + i3c_ccc_cmd_dest_init(&dest, I3C_BROADCAST_ADDR, 0); + i3c_ccc_cmd_init(&cmd, false, I3C_CCC_SETAASA, &dest, 1); + ret = i3c_master_send_ccc_cmd_locked(master, &cmd); + i3c_ccc_cmd_dest_cleanup(&dest); + if (ret && cmd.err == I3C_ERROR_M2) + ret = 0; + + return ret; +} + /** * i3c_master_entdaa_locked() - start a DAA (Dynamic Address Assignment) * procedure @@ -1697,8 +1743,10 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, int ret; i3cdev = i3c_master_alloc_i3c_dev(master, &info); - if (IS_ERR(i3cdev)) - return -ENOMEM; + if (IS_ERR(i3cdev)) { + ret = -ENOMEM; + goto err_reserve_addr; + } i3cdev->boardinfo = boardinfo; @@ -1706,6 +1754,22 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, if (ret) goto err_free_dev; + /* + * For devices using SETAASA instead of ENTDAA, the address is statically + * assigned. Update the dynamic address to the provided static address. + * Reattach the I3C device after updating the dynamic address with the same + * static address. It is not mandatory for such devices to implement CCC + * commands like GETPID, GETDCR etc. Hence, we can return after reattaching. + */ + if (i3cdev->boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) { + i3cdev->info.dyn_addr = i3cdev->boardinfo->static_addr; + ret = i3c_master_reattach_i3c_dev(i3cdev, 0); + if (ret) + goto err_detach_dev; + + return 0; + } + ret = i3c_master_setdasa_locked(master, i3cdev->info.static_addr, i3cdev->boardinfo->init_dyn_addr); if (ret) @@ -1728,6 +1792,16 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, i3c_master_detach_i3c_dev(i3cdev); err_free_dev: i3c_master_free_i3c_dev(i3cdev); +err_reserve_addr: + /* + * A target using SETAASA may still get the static address on the + * SETAASA broadcast even if attach fails here. Keep the address + * reserved so that it is not assigned to another device during DAA. + */ + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) + i3c_bus_set_addr_slot_status(&master->bus, + boardinfo->static_addr, + I3C_ADDR_SLOT_RSVD); return ret; } @@ -2153,6 +2227,19 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) i3c_master_early_i3c_dev_add(master, i3cboardinfo); } + /* + * SETAASA is a broadcast CCC. Issue it after SETDASA so that devices + * configured for SETDASA (or supporting both methods) are assigned + * first, matching MIPI DISCO guidance to prefer SETDASA when both are + * available. Targets that already have a dynamic address ignore the + * later SETAASA broadcast. + */ + if (master->addr_method & I3C_ADDR_METHOD_SETAASA) { + ret = i3c_master_setaasa_locked(master); + if (ret) + goto err_rstdaa; + } + ret = i3c_master_do_daa(master); if (ret) goto err_rstdaa; @@ -2478,7 +2565,7 @@ i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, struct i3c_dev_boardinfo *boardinfo; struct device *dev = &master->dev; enum i3c_addr_slot_status addrstatus; - u32 init_dyn_addr = 0; + u32 init_dyn_addr = 0, static_addr_method = 0; boardinfo = devm_kzalloc(dev, sizeof(*boardinfo), GFP_KERNEL); if (!boardinfo) @@ -2496,7 +2583,19 @@ i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, boardinfo->static_addr = reg[0]; + if (!fwnode_property_read_u32(fwnode, "mipi-i3c-static-method", &static_addr_method)) + boardinfo->static_addr_method = static_addr_method & + (I3C_ADDR_METHOD_SETDASA | I3C_ADDR_METHOD_SETAASA); + if (!fwnode_property_read_u32(fwnode, "assigned-address", &init_dyn_addr)) { + /* + * When a device advertises both SETDASA and SETAASA, an explicit + * dynamic address selects SETDASA (MIPI DISCO prefers it); drop + * SETAASA so it is not used for this device. + */ + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETDASA) + boardinfo->static_addr_method &= ~I3C_ADDR_METHOD_SETAASA; + if (init_dyn_addr > I3C_MAX_ADDR) return -EINVAL; @@ -2506,6 +2605,14 @@ i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, return -EINVAL; } + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) { + /* For SETAASA, static address is taken as the dynamic address. */ + init_dyn_addr = boardinfo->static_addr; + } + + /* Update the address methods required for device discovery */ + master->addr_method |= boardinfo->static_addr_method; + boardinfo->pid = ((u64)reg[1] << 32) | reg[2]; if ((boardinfo->pid & GENMASK_ULL(63, 48)) || @@ -3147,6 +3254,7 @@ int i3c_master_register(struct i3c_master_controller *master, master->dev.release = i3c_masterdev_release; master->ops = ops; master->secondary = secondary; + master->addr_method = I3C_ADDR_METHOD_SETDASA; INIT_LIST_HEAD(&master->boardinfo.i2c); INIT_LIST_HEAD(&master->boardinfo.i3c); diff --git a/include/linux/i3c/ccc.h b/include/linux/i3c/ccc.h index ad59a4ae60d1..a145d766ab6f 100644 --- a/include/linux/i3c/ccc.h +++ b/include/linux/i3c/ccc.h @@ -32,6 +32,7 @@ #define I3C_CCC_DEFSLVS I3C_CCC_ID(0x8, true) #define I3C_CCC_ENTTM I3C_CCC_ID(0xb, true) #define I3C_CCC_ENTHDR(x) I3C_CCC_ID(0x20 + (x), true) +#define I3C_CCC_SETAASA I3C_CCC_ID(0x29, true) /* Unicast-only commands */ #define I3C_CCC_SETDASA I3C_CCC_ID(0x7, false) diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 6b03a3ce574c..268c8f216ef4 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -174,6 +174,14 @@ struct i3c_device_ibi_info { * assigned a dynamic address by the master. Will be used during * bus initialization to assign it a specific dynamic address * before starting DAA (Dynamic Address Assignment) + * @static_addr_method: Bitmap describing which methods of Dynamic Address + * Assignment from a Static Address are supported by this I3C Target. + * A value of 1 in a bit position indicates that the I3C target + * supports that method, and a value of 0 indicates that the I3C + * target does not support that method. + * Bit 0: SETDASA + * Bit 1: SETAASA + * All other bits are reserved. * @pid: I3C Provisioned ID exposed by the device. This is a unique identifier * that may be used to attach boardinfo to i3c_dev_desc when the device * does not have a static address @@ -189,6 +197,7 @@ struct i3c_dev_boardinfo { struct list_head node; u8 init_dyn_addr; u8 static_addr; + u8 static_addr_method; u64 pid; struct fwnode_handle *fwnode; }; @@ -516,7 +525,12 @@ struct i3c_master_controller_ops { * @boardinfo.i2c: list of I2C boardinfo objects * @boardinfo: board-level information attached to devices connected on the bus * @bus: I3C bus exposed by this master - * @wq: workqueue which can be used by master + * @addr_method: Bitmap describing which methods of Address Assignment required + * to be run for discovering all the devices on the bus. + * Bit 0: SETDASA + * Bit 1: SETAASA + * All other bits are reserved. + * @wq: freezable workqueue which can be used by master * drivers if they need to postpone operations that need to take place * in a thread context. Typical examples are Hot Join processing which * requires taking the bus lock in maintenance, which in turn, can only @@ -543,6 +557,7 @@ struct i3c_master_controller { struct list_head i2c; } boardinfo; struct i3c_bus bus; + u8 addr_method; struct workqueue_struct *wq; unsigned int dev_nack_retry_count; }; -- Gitee From b5f6cf6fe13159177b667df1d4a3d066bcb4d88e Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:47 +0000 Subject: [PATCH 12/27] i3c: master: Add support for devices without PID ANBZ: #44797 commit a1dd42fb82fa71bf4cb6462b4803b55d844c1286 upstream. Devices using SETAASA for address assignment are not required to have a 48-bit PID according to the I3C specification. Allow such devices to register and use the static address where PID was required. Reviewed-by: Frank Li Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-6-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 52 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index a8d881777f5b..991f222ade86 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1829,8 +1829,17 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) desc->dev->dev.type = &i3c_device_type; desc->dev->dev.bus = &i3c_bus_type; desc->dev->dev.release = i3c_device_release; - dev_set_name(&desc->dev->dev, "%d-%llx", master->bus.id, - desc->info.pid); + + /* + * For devices without PID (e.g., SETAASA devices), use + * static address for naming instead. + */ + if (desc->info.pid) + dev_set_name(&desc->dev->dev, "%d-%llx", master->bus.id, + desc->info.pid); + else + dev_set_name(&desc->dev->dev, "%d-%02x", master->bus.id, + desc->info.static_addr); if (desc->boardinfo) device_set_node(&desc->dev->dev, desc->boardinfo->fwnode); @@ -2282,8 +2291,18 @@ static void i3c_master_attach_boardinfo(struct i3c_dev_desc *i3cdev) struct i3c_dev_boardinfo *i3cboardinfo; list_for_each_entry(i3cboardinfo, &master->boardinfo.i3c, node) { - if (i3cdev->info.pid != i3cboardinfo->pid) - continue; + /* + * For devices without PID (e.g., SETAASA devices), match by + * static address. For devices with PID, match by PID. + */ + if (i3cboardinfo->pid) { + if (i3cdev->info.pid != i3cboardinfo->pid) + continue; + } else { + if (!i3cboardinfo->static_addr || + i3cdev->info.static_addr != i3cboardinfo->static_addr) + continue; + } i3cdev->boardinfo = i3cboardinfo; i3cdev->info.static_addr = i3cboardinfo->static_addr; @@ -2297,8 +2316,12 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev) struct i3c_master_controller *master = i3c_dev_get_master(refdev); struct i3c_dev_desc *i3cdev; + if (!refdev->info.pid) + return NULL; + i3c_bus_for_each_i3cdev(&master->bus, i3cdev) { - if (i3cdev != refdev && i3cdev->info.pid == refdev->info.pid) + if (i3cdev != refdev && i3cdev->info.pid && + i3cdev->info.pid == refdev->info.pid) return i3cdev; } @@ -2615,9 +2638,16 @@ i3c_master_add_i3c_boardinfo(struct i3c_master_controller *master, boardinfo->pid = ((u64)reg[1] << 32) | reg[2]; - if ((boardinfo->pid & GENMASK_ULL(63, 48)) || - I3C_PID_RND_LOWER_32BITS(boardinfo->pid)) - return -EINVAL; + /* For SETAASA devices, validate the static address instead of PID */ + if (boardinfo->static_addr_method & I3C_ADDR_METHOD_SETAASA) { + if (!boardinfo->static_addr) + return -EINVAL; + } else { + if (!I3C_PID_MANUF_ID(boardinfo->pid) || + (boardinfo->pid & GENMASK_ULL(63, 48)) || + I3C_PID_RND_LOWER_32BITS(boardinfo->pid)) + return -EINVAL; + } boardinfo->init_dyn_addr = init_dyn_addr; boardinfo->fwnode = fwnode_handle_get(fwnode); @@ -2640,10 +2670,10 @@ static int i3c_master_add_of_dev(struct i3c_master_controller *master, return ret; /* - * The manufacturer ID can't be 0. If reg[1] == 0 that means we're - * dealing with an I2C device. + * I3C device should have either the manufacturer ID specified or the + * address discovery method specified. Else treat it as an I2C device. */ - if (!reg[1]) + if (!reg[1] && !fwnode_property_present(fwnode, "mipi-i3c-static-method")) ret = i3c_master_add_i2c_boardinfo(master, fwnode, reg); else ret = i3c_master_add_i3c_boardinfo(master, fwnode, reg); -- Gitee From a48efea8361af6b438263cbbe558f2ff675d0f17 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:48 +0000 Subject: [PATCH 13/27] i3c: master: match I3C device through DT and ACPI ANBZ: #44797 commit 3456baa2110c1fa31e7609ace50117346b99c3d4 upstream. SETAASA-based devices cannot always be identified by PID or DCR; the standard I3C id_table matching may not be applicable. Allow such devices to match through Device Tree or ACPI. Reviewed-by: Frank Li Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-7-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 991f222ade86..ce2a76ce83cc 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -343,15 +344,32 @@ 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; if (dev->type != &i3c_device_type) return 0; i3cdev = dev_to_i3cdev(dev); i3cdrv = drv_to_i3cdrv(drv); - if (i3c_device_match_id(i3cdev, i3cdrv->id_table)) + + if (i3cdev->desc && i3cdev->desc->boardinfo) + static_addr_method = i3cdev->desc->boardinfo->static_addr_method; + + /* + * SETAASA-based devices need not always have a matching ID since + * it is not mandatory for such devices to implement deviceinfo + * CCC commands. Allow them to register through DT or ACPI. + */ + if (i3cdrv->id_table && i3c_device_match_id(i3cdev, i3cdrv->id_table)) return 1; + if (static_addr_method & I3C_ADDR_METHOD_SETAASA) { + if (of_driver_match_device(dev, drv)) + return 1; + if (acpi_driver_match_device(dev, drv)) + return 1; + } + return 0; } -- Gitee From 7ae3a3d8543d5b4a3922c52221116f118b98ed56 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:49 +0000 Subject: [PATCH 14/27] i3c: dw-i3c-master: Add SETAASA as supported CCC ANBZ: #44797 commit fc6963aad560aebeb926e677aa26746c42c6223b upstream. Add SETAASA and SETHID to the supported list of CCC commands for DesignWare I3C host controller. SETAASA is a broadcast command that assigns predefined static addresses to all I3C devices on the bus. SETHID is to stop HID bit flipping by the SPD Hub to which the SPD devices are connected. It is a prerequisite command to be sent before SETAASA as recommended by JESD300-5 and JESD403 sideband bus specifications. Reviewed-by: Frank Li Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-8-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master/dw-i3c-master.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index ec8c5ec38958..27970ade1cfd 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -309,6 +309,8 @@ static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m, case I3C_CCC_GETSTATUS: case I3C_CCC_GETMXDS: case I3C_CCC_GETHDRCAP: + case I3C_CCC_SETAASA: + case I3C_CCC_VENDOR(0, true): /* SETHID */ return true; default: return false; -- Gitee From 37485f6db3fc7294ceeb5c719a30de3fc74fc0b0 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:50 +0000 Subject: [PATCH 15/27] i3c: dw-i3c-master: Add ACPI core clock frequency quirk ANBZ: #44797 commit 0fd9549975b5b8186a78b654e02518113cc2ac93 upstream. Some ACPI-enumerated devices like Tegra410 do not expose the controller core clock through the clk framework. Unlike device tree, ACPI on Arm does not model clock providers. The hardware is expected to have its clocks enabled by firmware before the OS takes over. Make the core clock optional and allow selected ACPI devices to provide the core clock rate through the "clock-frequency" _DSD property when the core clock is absent. Resolve device quirks before acquiring the core clock so platforms without the ACPI skip-clock quirk still fail probe immediately when the clock is missing, before any MMIO access. Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-9-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master/dw-i3c-master.c | 45 ++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 27970ade1cfd..792f8d3261bf 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -242,6 +242,7 @@ /* List of quirks */ #define AMD_I3C_OD_PP_TIMING BIT(1) #define DW_I3C_DISABLE_RUNTIME_PM_QUIRK BIT(2) +#define DW_I3C_ACPI_SKIP_CLK_RST BIT(3) struct dw_i3c_cmd { u32 cmd_lo; @@ -561,13 +562,28 @@ static void dw_i3c_master_set_intr_regs(struct dw_i3c_master *master) writel(IBI_REQ_REJECT_ALL, master->regs + IBI_MR_REQ_REJECT); } +static unsigned long dw_i3c_master_get_core_rate(struct dw_i3c_master *master) +{ + unsigned int core_rate_prop; + + if (master->core_clk) + return clk_get_rate(master->core_clk); + + if (device_property_read_u32(master->dev, "clock-frequency", &core_rate_prop)) { + dev_err(master->dev, "missing clock-frequency property\n"); + return 0; + } + + return core_rate_prop; +} + static int dw_i3c_clk_cfg(struct dw_i3c_master *master) { unsigned long core_rate, core_period; u32 scl_timing; u8 hcnt, lcnt; - core_rate = clk_get_rate(master->core_clk); + core_rate = dw_i3c_master_get_core_rate(master); if (!core_rate) return -EINVAL; @@ -620,7 +636,7 @@ static int dw_i2c_clk_cfg(struct dw_i3c_master *master) u16 hcnt, lcnt; u32 scl_timing; - core_rate = clk_get_rate(master->core_clk); + core_rate = dw_i3c_master_get_core_rate(master); if (!core_rate) return -EINVAL; @@ -1603,14 +1619,28 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->dev = &pdev->dev; + if (has_acpi_companion(&pdev->dev)) { + quirks = (unsigned long)device_get_match_data(&pdev->dev); + } else if (pdev->dev.of_node) { + drvdata = device_get_match_data(&pdev->dev); + if (drvdata) + quirks = drvdata->flags; + } + master->quirks = quirks; + master->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(master->regs)) return PTR_ERR(master->regs); - master->core_clk = devm_clk_get_enabled(&pdev->dev, NULL); + master->core_clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); if (IS_ERR(master->core_clk)) return PTR_ERR(master->core_clk); + if (!master->core_clk && !(master->quirks & DW_I3C_ACPI_SKIP_CLK_RST)) { + dev_err(&pdev->dev, "missing core clock\n"); + return -EINVAL; + } + master->pclk = devm_clk_get_optional_enabled(&pdev->dev, "pclk"); if (IS_ERR(master->pclk)) return PTR_ERR(master->pclk); @@ -1652,15 +1682,6 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->maxdevs = ret >> 16; master->free_pos = GENMASK(master->maxdevs - 1, 0); - if (has_acpi_companion(&pdev->dev)) { - quirks = (unsigned long)device_get_match_data(&pdev->dev); - } else if (pdev->dev.of_node) { - drvdata = device_get_match_data(&pdev->dev); - if (drvdata) - quirks = drvdata->flags; - } - master->quirks = quirks; - /* Keep controller enabled by preventing runtime suspend */ if (master->quirks & DW_I3C_DISABLE_RUNTIME_PM_QUIRK) pm_runtime_get_noresume(&pdev->dev); -- Gitee From c7a045966fa75a6d21d65d82e61204ffc3b63db3 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:51 +0000 Subject: [PATCH 16/27] i3c: dw-i3c-master: Add ACPI ID for Tegra410 ANBZ: #44797 commit 97d7cfb62a215ed5c64c6e7772545be4afab167f upstream. Update variable names to generic names and add Tegra410 ACPI ID to support the I3C controller in Tegra410, which is a DesignWare I3C host controller. Reviewed-by: Frank Li Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-10-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master/dw-i3c-master.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 792f8d3261bf..aec9212fb167 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1876,11 +1876,12 @@ static const struct of_device_id dw_i3c_master_of_match[] = { }; MODULE_DEVICE_TABLE(of, dw_i3c_master_of_match); -static const struct acpi_device_id amd_i3c_device_match[] = { +static const struct acpi_device_id dw_i3c_master_acpi_match[] = { { "AMDI0015", AMD_I3C_OD_PP_TIMING }, + { "NVDA2018", DW_I3C_ACPI_SKIP_CLK_RST }, { } }; -MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match); +MODULE_DEVICE_TABLE(acpi, dw_i3c_master_acpi_match); static struct platform_driver dw_i3c_driver = { .probe = dw_i3c_probe, @@ -1889,7 +1890,7 @@ static struct platform_driver dw_i3c_driver = { .driver = { .name = "dw-i3c-master", .of_match_table = dw_i3c_master_of_match, - .acpi_match_table = amd_i3c_device_match, + .acpi_match_table = dw_i3c_master_acpi_match, .pm = &dw_i3c_pm_ops, }, }; -- Gitee From 92bae96e516fbbf09a86b197a6e718855fa79d58 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:52 +0000 Subject: [PATCH 17/27] hwmon: spd5118: Remove 16-bit addressing ANBZ: #44797 commit 7bf5a11dde2c957bf6751f6ba28fef66e848ac6d upstream. The intent of introducing 16-bit addressing was to support I3C, but it turns out that I3C does not require reading the Legacy Mode register, nor any specific encoding for page translation. The testing of 16-bit code was limited and there are no known users for this feature. Remove the sections that support 16-bit addressing and prepare the driver to support I3C appropriately. Suggested-by: Guenter Roeck Acked-by: Guenter Roeck Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-11-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/hwmon/spd5118.c | 79 +++-------------------------------------- 1 file changed, 5 insertions(+), 74 deletions(-) diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index 5da44571b6a0..8fdde35e68cd 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -66,9 +66,6 @@ static const unsigned short normal_i2c[] = { #define SPD5118_EEPROM_BASE 0x80 #define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES) -#define PAGE_ADDR0(page) (((page) & BIT(0)) << 6) -#define PAGE_ADDR1_4(page) (((page) & GENMASK(4, 1)) >> 1) - /* Temperature unit in millicelsius */ #define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4) /* Representable temperature range in millicelsius */ @@ -78,7 +75,6 @@ static const unsigned short normal_i2c[] = { struct spd5118_data { struct regmap *regmap; struct mutex nvmem_lock; - bool is_16bit; }; /* hwmon */ @@ -348,12 +344,7 @@ static ssize_t spd5118_nvmem_read_page(struct spd5118_data *data, char *buf, if (offset + count > SPD5118_PAGE_SIZE) count = SPD5118_PAGE_SIZE - offset; - if (data->is_16bit) { - addr = SPD5118_EEPROM_BASE | PAGE_ADDR0(page) | - (PAGE_ADDR1_4(page) << 8); - } else { - addr = page * 0x100 + SPD5118_EEPROM_BASE; - } + addr = page * 0x100 + SPD5118_EEPROM_BASE; err = regmap_bulk_read(regmap, addr + offset, buf, count); if (err) return err; @@ -473,15 +464,6 @@ static const struct regmap_config spd5118_regmap8_config = { .num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg), }; -static const struct regmap_config spd5118_regmap16_config = { - .reg_bits = 16, - .val_bits = 8, - .max_register = 0x7ff, - .writeable_reg = spd5118_writeable_reg, - .volatile_reg = spd5118_volatile_reg, - .cache_type = REGCACHE_MAPLE, -}; - static int spd5118_suspend(struct device *dev) { struct spd5118_data *data = dev_get_drvdata(dev); @@ -519,8 +501,7 @@ static int spd5118_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume); -static int spd5118_common_probe(struct device *dev, struct regmap *regmap, - bool is_16bit) +static int spd5118_common_probe(struct device *dev, struct regmap *regmap) { unsigned int capability, revision, vendor, bank; struct spd5118_data *data; @@ -537,8 +518,6 @@ static int spd5118_common_probe(struct device *dev, struct regmap *regmap, if (!(capability & SPD5118_CAP_TS_SUPPORT)) return -ENODEV; - data->is_16bit = is_16bit; - err = regmap_read(regmap, SPD5118_REG_REVISION, &revision); if (err) return err; @@ -680,69 +659,21 @@ static int spd5118_i2c_init(struct i2c_client *client) return 0; } -/* - * 16-bit addressing note: - * - * If I2C_FUNC_I2C is not supported by an I2C adapter driver, regmap uses - * SMBus operations as alternative. To simulate a read operation with a 16-bit - * address, it writes the address using i2c_smbus_write_byte_data(), followed - * by one or more calls to i2c_smbus_read_byte() to read the data. - * Per spd5118 standard, a read operation after writing the address must start - * with (Repeat Start). However, a SMBus read byte operation starts with - * (Start). This resets the register address in the spd5118 chip. As result, - * i2c_smbus_read_byte() always returns data from register address 0x00. - * - * A working alternative to access chips with 16-bit register addresses in the - * absence of I2C_FUNC_I2C support is not known. - * - * For this reason, 16-bit addressing can only be supported with I2C if the - * adapter supports I2C_FUNC_I2C. - * - * For I2C, the addressing mode selected by the BIOS must not be changed. - * Experiments show that at least some PC BIOS versions will not change the - * addressing mode on a soft reboot and end up in setup, claiming that some - * configuration change happened. This will happen again after a power cycle, - * which does reset the addressing mode. To prevent this from happening, - * detect if 16-bit addressing is enabled and always use the currently - * configured addressing mode. - */ - static int spd5118_i2c_probe(struct i2c_client *client) { - const struct regmap_config *config; struct device *dev = &client->dev; struct regmap *regmap; - int err, mode; - bool is_16bit; + int err; err = spd5118_i2c_init(client); if (err) return err; - mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE); - if (mode < 0) - return mode; - - is_16bit = mode & SPD5118_LEGACY_MODE_ADDR; - if (is_16bit) { - /* - * See 16-bit addressing note above explaining why it is - * necessary to check for I2C_FUNC_I2C support here. - */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(dev, "Adapter does not support 16-bit register addresses\n"); - return -ENODEV; - } - config = &spd5118_regmap16_config; - } else { - config = &spd5118_regmap8_config; - } - - regmap = devm_regmap_init_i2c(client, config); + regmap = devm_regmap_init_i2c(client, &spd5118_regmap8_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n"); - return spd5118_common_probe(dev, regmap, is_16bit); + return spd5118_common_probe(dev, regmap); } static const struct i2c_device_id spd5118_i2c_id[] = { -- Gitee From 0b8401bc2f6ce48a8d2a1254facbe40fc368c0a4 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Tue, 28 Jul 2026 06:59:53 +0000 Subject: [PATCH 18/27] hwmon: spd5118: Add I3C support ANBZ: #44797 commit 9cd3db0cda4741befa6de8af94eb371b9ec6d057 upstream. Add a regmap config and a probe function to support I3C-based communication with SPD5118 devices. On an I3C bus, SPD5118 devices are enumerated via SETAASA and always require an ACPI or device tree entry. Device matching is hence through the OF match tables only and does not need an I3C class match table. The device identity is verified in the type registers before proceeding to the common probe function. Acked-by: Guenter Roeck Signed-off-by: Akhil R Link: https://patch.msgid.link/20260728065955.809445-12-akhilrajeev@nvidia.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/hwmon/Kconfig | 9 ++++--- drivers/hwmon/spd5118.c | 56 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 328867242cb3..1b1c59365dc4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2308,12 +2308,15 @@ config SENSORS_INA3221 config SENSORS_SPD5118 tristate "SPD5118 Compliant Temperature Sensors" - depends on I2C + depends on I3C_OR_I2C select REGMAP_I2C + select REGMAP_I3C if I3C help If you say yes here you get support for SPD5118 (JEDEC JESD300) - compliant temperature sensors. Such sensors are found on DDR5 memory - modules. + compliant temperature sensors using I2C or I3C bus interface. + Such sensors are found on DDR5 memory modules. + + This driver supports both I2C and I3C interfaces. This driver can also be built as a module. If so, the module will be called spd5118. diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index 8fdde35e68cd..2cdeed743ecb 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -464,6 +465,27 @@ static const struct regmap_config spd5118_regmap8_config = { .num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg), }; +/* + * SPD5118 2-byte register address format (JESD300-5, Tables 7 & 20): + * Byte 1 (on wire first): MemReg | BlkAddr[0] | Address[5:0] + * Byte 2 (on wire second): 0000 | BlkAddr[4:1] + * + * The address byte (with MemReg and lower address bits) must be sent first, + * followed by the upper block address byte. With regmap 16-bit register + * format, this maps to little-endian: the low byte of the 16-bit value is + * transmitted first. No range config is needed since I3C does not use MR11 + * page switching. + */ +static const struct regmap_config spd5118_regmap_i3c_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x7ff, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .writeable_reg = spd5118_writeable_reg, + .volatile_reg = spd5118_volatile_reg, + .cache_type = REGCACHE_MAPLE, +}; + static int spd5118_suspend(struct device *dev) { struct spd5118_data *data = dev_get_drvdata(dev); @@ -701,7 +723,39 @@ static struct i2c_driver spd5118_i2c_driver = { .address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL, }; -module_i2c_driver(spd5118_i2c_driver); +/* I3C */ + +static int spd5118_i3c_probe(struct i3c_device *i3cdev) +{ + struct device *dev = i3cdev_to_dev(i3cdev); + struct regmap *regmap; + u8 regval[2]; + int err; + + regmap = devm_regmap_init_i3c(i3cdev, &spd5118_regmap_i3c_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n"); + + err = regmap_bulk_read(regmap, SPD5118_REG_TYPE, regval, 2); + if (err) + return dev_err_probe(dev, err, "failed to read device type\n"); + + if (regval[0] != 0x51 || regval[1] != 0x18) + return -ENODEV; + + return spd5118_common_probe(dev, regmap); +} + +static struct i3c_driver spd5118_i3c_driver = { + .driver = { + .name = "spd5118_i3c", + .of_match_table = spd5118_of_ids, + .pm = pm_sleep_ptr(&spd5118_pm_ops), + }, + .probe = spd5118_i3c_probe, +}; + +module_i3c_i2c_driver(spd5118_i3c_driver, &spd5118_i2c_driver); MODULE_AUTHOR("RenĂ© Rebe "); MODULE_AUTHOR("Guenter Roeck "); -- Gitee From 13b963b1f9af7a4058eb4ac8a25d1875f78edf77 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 7 Aug 2026 17:56:25 +0300 Subject: [PATCH 19/27] i3c: master: Fix recursive locking during device registration ANBZ: #44797 commit 456f832e5fc26fbfd3b8200fd4553eee520cc377 upstream. i3c_master_register_new_i3c_devs() registers newly discovered devices while holding i3c_bus_normaluse_lock(), a down_read(). device_register() can immediately probe the device, and probe callbacks typically invoke I3C helpers that take i3c_bus_normaluse_lock() again, leading to a recursive acquisition of the same rwsem. rwsems do not support recursive read locking and can deadlock when a writer is waiting. See the "Recursive read locks" section of Documentation/locking/lockdep-design.rst. Fix this by separating device creation from device registration. Populate desc->dev under the maintenance lock, collect the devices that still need registration into a local list, then release the lock before calling device_register(). Finally retake the lock and clean up any devices that failed to register. Use the maintenance lock rather than the normal-use lock while adding device objects. A write-side maintenance lock prevents readers from observing a partially initialized desc->dev during initial device population, or desc->dev disappearing if registration fails. The local list requires a list node, so add a list node member to struct i3c_device. Fixes: 3a379bbcea0a ("i3c: Add core I3C infrastructure") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260807145638.168865-2-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni [adapted: drop the master->shutting_down check, the flag is not present in this tree; unwrap the normal-use lock around the direct i3c_master_register_new_i3c_devs() call sites in i3c_master_do_daa_ext() and i3c_master_register() instead of i3c_master_reg_work_fn(), which does not exist in this tree] Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 37 +++++++++++++++++++++++++++---------- include/linux/i3c/master.h | 3 +++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index ce2a76ce83cc..396eaf52c6e5 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -1827,12 +1827,16 @@ static int i3c_master_early_i3c_dev_add(struct i3c_master_controller *master, static void i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) { + struct i3c_device *i3cdev, *tmp; struct i3c_dev_desc *desc; + LIST_HEAD(i3c_unreg_devs); int ret; if (!master->init_done) return; + i3c_bus_maintenance_lock(&master->bus); + i3c_bus_for_each_i3cdev(&master->bus, desc) { if (desc->dev || !desc->info.dyn_addr || desc == master->this) continue; @@ -1862,13 +1866,30 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master) if (desc->boardinfo) device_set_node(&desc->dev->dev, desc->boardinfo->fwnode); - ret = device_register(&desc->dev->dev); - if (ret) { - dev_err(&master->dev, - "Failed to add I3C device (err = %d)\n", ret); - put_device(&desc->dev->dev); - } + list_add_tail(&desc->dev->node, &i3c_unreg_devs); + } + + i3c_bus_maintenance_unlock(&master->bus); + + list_for_each_entry_safe(i3cdev, tmp, &i3c_unreg_devs, node) { + ret = device_register(&i3cdev->dev); + if (ret) + dev_err(&master->dev, "Failed to add I3C device (err = %d)\n", ret); + else + list_del_init(&i3cdev->node); } + + i3c_bus_maintenance_lock(&master->bus); + + list_for_each_entry_safe(i3cdev, tmp, &i3c_unreg_devs, node) { + list_del(&i3cdev->node); + desc = i3cdev->desc; + i3cdev->desc = NULL; + put_device(&i3cdev->dev); + desc->dev = NULL; + } + + i3c_bus_maintenance_unlock(&master->bus); } /** @@ -1911,9 +1932,7 @@ int i3c_master_do_daa_ext(struct i3c_master_controller *master, bool rstdaa) if (ret) goto out; - i3c_bus_normaluse_lock(&master->bus); i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); out: i3c_master_rpm_put(master); @@ -3386,9 +3405,7 @@ int i3c_master_register(struct i3c_master_controller *master, * register I3C devices discovered during the initial DAA. */ master->init_done = true; - i3c_bus_normaluse_lock(&master->bus); i3c_master_register_new_i3c_devs(master); - i3c_bus_normaluse_unlock(&master->bus); if (master->ops->set_dev_nack_retry) device_create_file(&master->dev, &dev_attr_dev_nack_retry_count); diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h index 268c8f216ef4..fa654ed68ede 100644 --- a/include/linux/i3c/master.h +++ b/include/linux/i3c/master.h @@ -238,6 +238,8 @@ struct i3c_dev_desc { * every time the I3C device is rediscovered with a different dynamic * address assigned * @bus: I3C bus this device is attached to + * @node: unregistered device list node, only for use by + * i3c_master_register_new_i3c_devs(), it is not protected by a lock * * I3C device object exposed to I3C device drivers. The takes care of linking * this object to the relevant &struct_i3c_dev_desc one. @@ -248,6 +250,7 @@ struct i3c_device { struct device dev; struct i3c_dev_desc *desc; struct i3c_bus *bus; + struct list_head node; }; /* -- Gitee From 02f83f6236d1ea17fb6320b32b5816c64e1dbf18 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 7 Aug 2026 17:56:26 +0300 Subject: [PATCH 20/27] i3c: Fix unlocked dereference of dev->desc in i3c_device_get_supported_xfer_mode() ANBZ: #44797 commit 8bed7f4fa710914b7f05fd59998316bfb4d43385 upstream. i3c_device_get_supported_xfer_mode() uses dev->desc to obtain the master controller. However, dev->desc must not be dereferenced unless bus->lock is held, and this function does not take that lock. The function only needs access to the master controller associated with the device's bus. Use dev->bus instead, which is always valid for the lifetime of the device and does not require dereferencing dev->desc. Fixes: 256a21743d91 ("i3c: Add HDR API support") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260807145638.168865-3-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/device.c | 2 +- drivers/i3c/internals.h | 5 +++++ drivers/i3c/master.c | 6 ------ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/i3c/device.c b/drivers/i3c/device.c index 101eaa77de68..a3778282e84c 100644 --- a/drivers/i3c/device.c +++ b/drivers/i3c/device.c @@ -309,7 +309,7 @@ EXPORT_SYMBOL_GPL(i3c_device_match_id); */ u32 i3c_device_get_supported_xfer_mode(struct i3c_device *dev) { - return i3c_dev_get_master(dev->desc)->this->info.hdr_cap | BIT(I3C_SDR); + return i3c_bus_to_i3c_master(dev->bus)->this->info.hdr_cap | BIT(I3C_SDR); } EXPORT_SYMBOL_GPL(i3c_device_get_supported_xfer_mode); diff --git a/drivers/i3c/internals.h b/drivers/i3c/internals.h index 0f1f3f766623..86a36b951e0d 100644 --- a/drivers/i3c/internals.h +++ b/drivers/i3c/internals.h @@ -72,4 +72,9 @@ static inline void i3c_readl_fifo(const void __iomem *addr, void *buf, } } +static inline struct i3c_master_controller *i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) +{ + return container_of(i3cbus, struct i3c_master_controller, bus); +} + #endif /* I3C_INTERNAL_H */ diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 396eaf52c6e5..ec65873579e6 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -100,12 +100,6 @@ void i3c_bus_normaluse_unlock(struct i3c_bus *bus) up_read(&bus->lock); } -static struct i3c_master_controller * -i3c_bus_to_i3c_master(struct i3c_bus *i3cbus) -{ - return container_of(i3cbus, struct i3c_master_controller, bus); -} - static struct i3c_master_controller *dev_to_i3cmaster(struct device *dev) { return container_of(dev, struct i3c_master_controller, dev); -- Gitee From 8989e6549a12d11ffc7a6829656656bb6feb5ff8 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 7 Aug 2026 17:56:27 +0300 Subject: [PATCH 21/27] i3c: master: Do not treat master device as a duplicate target ANBZ: #44797 commit 4dc1b3eeba7991905a5b5b8129ebea51be7d87b7 upstream. i3c_master_search_i3c_dev_duplicate() searches the bus for another I3C device with the same PID as the reference device. The search can match master->this, causing the controller itself to be returned as a duplicate. Since the controller is not a target device, it cannot be a duplicate of one. Exclude master->this from matching so that the function only returns real duplicate target devices. Fixes: 3a379bbcea0a ("i3c: Add core I3C infrastructure") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Acked-by: Mukesh Savaliya Link: https://patch.msgid.link/20260807145638.168865-4-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index ec65873579e6..596923dceed6 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2352,7 +2352,8 @@ i3c_master_search_i3c_dev_duplicate(struct i3c_dev_desc *refdev) i3c_bus_for_each_i3cdev(&master->bus, i3cdev) { if (i3cdev != refdev && i3cdev->info.pid && - i3cdev->info.pid == refdev->info.pid) + i3cdev->info.pid == refdev->info.pid && + i3cdev != master->this) return i3cdev; } -- Gitee From 7de14e31a4e6704e8e65289a642ed11b5aebf046 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 7 Aug 2026 17:56:28 +0300 Subject: [PATCH 22/27] i3c: master: Fix use-after-free of master->this ANBZ: #44797 commit feb0ed76601f3c2f91f08688c5a7d8b9d382f720 upstream. sysfs attribute callbacks for the master controller device dereference master->this. However, master->this is freed in i3c_master_detach_free_devs() before the master device itself is released. As a result, sysfs accesses can dereference a freed master->this pointer, leading to a use-after-free. Keep master->this alive until i3c_masterdev_release(), which is called after the master device and its sysfs state are being torn down. Do not free master->this as part of the normal device detach path. On the error path in i3c_master_set_info(), reset master->this and bus.cur_master to NULL before freeing the allocated device. Fixes: 3a379bbcea0a ("i3c: Add core I3C infrastructure") Cc: stable@vger.kernel.org Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260807145638.168865-5-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 596923dceed6..c3869d23416e 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -789,6 +789,11 @@ static struct attribute *i3c_masterdev_attrs[] = { }; ATTRIBUTE_GROUPS(i3c_masterdev); +static void i3c_master_free_i3c_dev(struct i3c_dev_desc *dev) +{ + kfree(dev); +} + static void i3c_masterdev_release(struct device *dev) { struct i3c_master_controller *master = dev_to_i3cmaster(dev); @@ -801,6 +806,8 @@ static void i3c_masterdev_release(struct device *dev) i3c_bus_cleanup(bus); fwnode_handle_put(dev->fwnode); + + i3c_master_free_i3c_dev(master->this); } static const struct device_type i3c_masterdev_type = { @@ -988,11 +995,6 @@ static void i3c_device_release(struct device *dev) kfree(i3cdev); } -static void i3c_master_free_i3c_dev(struct i3c_dev_desc *dev) -{ - kfree(dev); -} - static struct i3c_dev_desc * i3c_master_alloc_i3c_dev(struct i3c_master_controller *master, const struct i3c_device_info *info) @@ -2076,6 +2078,8 @@ int i3c_master_set_info(struct i3c_master_controller *master, return 0; err_free_dev: + master->bus.cur_master = NULL; + master->this = NULL; i3c_master_free_i3c_dev(i3cdev); return ret; @@ -2096,7 +2100,8 @@ static void i3c_master_detach_free_devs(struct i3c_master_controller *master) i3cdev->boardinfo->init_dyn_addr, I3C_ADDR_SLOT_FREE); - i3c_master_free_i3c_dev(i3cdev); + if (i3cdev != master->this) + i3c_master_free_i3c_dev(i3cdev); } list_for_each_entry_safe(i2cdev, i2ctmp, &master->bus.devs.i2c, -- Gitee From c9a9a095eb4826bd7f9a561767a9776fe83d4d3a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 7 Aug 2026 17:56:30 +0300 Subject: [PATCH 23/27] i3c: master: Fix potential UAF in i3c_device_uevent() ANBZ: #44797 commit e5e8dd2e959f470524c16ca444d001c90d6bb3ad upstream. i3c_device_uevent() dereferences i3cdev->desc without holding the bus normal-use lock. Since the descriptor pointer can be replaced concurrently, including when a uevent is generated from sysfs, this can result in dereferencing a stale descriptor and lead to a use-after-free. Use i3c_device_get_info() instead, which protects access to the descriptor with the normal-use lock. Commit 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock") replaced the accessor with a direct descriptor dereference because i3c_device_get_info() would recursively acquire bus->lock during device registration. This change depends on "i3c: master: Fix recursive locking during device registration", which moves device registration out from under bus->lock and removes the possibility of that deadlock. Without that change, restoring the i3c_device_get_info() call would reintroduce the deadlock. Fixes: 6cf7b65f7029 ("i3c: Use i3cdev->desc->info instead of calling i3c_device_get_info() to avoid deadlock") Cc: stable@vger.kernel.org # requires "i3c: master: Fix recursive locking during device registration" Signed-off-by: Adrian Hunter Acked-by: Mukesh Savaliya Reviewed-by: Frank Li Link: https://patch.msgid.link/20260807145638.168865-7-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index c3869d23416e..4b3ea2d09db6 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -314,8 +314,7 @@ static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *e struct i3c_device_info devinfo; u16 manuf, part, ext; - if (i3cdev->desc) - devinfo = i3cdev->desc->info; + i3c_device_get_info(i3cdev, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); -- Gitee From b869b581360c9ee984cc2e126d1da59c56ef0206 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 7 Aug 2026 17:56:31 +0300 Subject: [PATCH 24/27] i3c: master: Fix potential UAF in i3c_device_match() ANBZ: #44797 commit f44d3b15326c498b7a285ee56d4fd8fbc64a8c15 upstream. i3c_device_match() dereferences i3cdev->desc without holding the bus normal-use lock. Since the descriptor pointer can be replaced concurrently, the dereference can race with descriptor replacement and result in a use-after-free. Protect access to i3cdev->desc with the normal-use lock. While the lock is held, the descriptor is guaranteed to remain valid, so the NULL check is also unnecessary and can be removed. This change depends on "i3c: master: Fix recursive locking during device registration". Prior to that change, taking the normal-use lock in i3c_device_match() could recurse on bus->lock during device registration. Fixes: 3456baa2110c ("i3c: master: match I3C device through DT and ACPI") Signed-off-by: Adrian Hunter Reviewed-by: Frank Li Link: https://patch.msgid.link/20260807145638.168865-8-adrian.hunter@intel.com Signed-off-by: Alexandre Belloni Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 4b3ea2d09db6..30315a6dc0f2 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -345,8 +345,10 @@ static int i3c_device_match(struct device *dev, const struct device_driver *drv) i3cdev = dev_to_i3cdev(dev); i3cdrv = drv_to_i3cdrv(drv); - if (i3cdev->desc && i3cdev->desc->boardinfo) + i3c_bus_normaluse_lock(i3cdev->bus); + if (i3cdev->desc->boardinfo) static_addr_method = i3cdev->desc->boardinfo->static_addr_method; + i3c_bus_normaluse_unlock(i3cdev->bus); /* * SETAASA-based devices need not always have a matching ID since -- Gitee From 94b83135bab74c006311e6c6518bfc98d42ca229 Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Thu, 27 Aug 2026 20:46:03 +0800 Subject: [PATCH 25/27] i3c: master: Emit firmware modalias for SETAASA devices ANBZ: #44797 cherry-picked from https://lore.kernel.org/r/20260811103811.3380073-1-nirmoyd@nvidia.com SETAASA devices are allowed to omit their PID and device information CCCs. The I3C core can match these devices through firmware descriptions, but the uevent and sysfs modalias paths expose only an identity-based I3C alias. For a PID-less SETAASA device, that alias contains no useful identity and cannot trigger loading of a driver that matches through Device Tree or ACPI. Such a driver can bind when already loaded, but it is not automatically loaded for the device. For PID-less SETAASA devices, emit the Device Tree or ACPI modalias from both paths when available. Preserve the existing I3C identity alias for devices that report a PID and as a fallback when no firmware modalias can be generated. Fixes: 3456baa2110c ("i3c: master: match I3C device through DT and ACPI") Signed-off-by: Nirmoy Das [adapted: take the bus normal-use lock around the descriptor access in i3c_device_uevent(), mirroring modalias_show(); safe because i3c: master: Fix recursive locking during device registration moved device_register() out from under the normal-use lock] Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 46 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 30315a6dc0f2..8bae0dd740e9 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -280,10 +280,29 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *da, char *buf) { struct i3c_device *i3c = dev_to_i3cdev(dev); - struct i3c_device_info devinfo; + struct i3c_device_info devinfo = {}; + u8 static_addr_method = 0; u16 manuf, part, ext; + int ret; + + i3c_bus_normaluse_lock(i3c->bus); + if (i3c->desc) { + devinfo = i3c->desc->info; + if (i3c->desc->boardinfo) + static_addr_method = i3c->desc->boardinfo->static_addr_method; + } + i3c_bus_normaluse_unlock(i3c->bus); + + if ((static_addr_method & I3C_ADDR_METHOD_SETAASA) && !devinfo.pid) { + ret = of_device_modalias(dev, buf, PAGE_SIZE); + if (ret != -ENODEV) + return ret; + + ret = acpi_device_modalias(dev, buf, PAGE_SIZE - 1); + if (ret != -ENODEV) + return ret; + } - i3c_device_get_info(i3c, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); @@ -311,10 +330,29 @@ ATTRIBUTE_GROUPS(i3c_device); static int i3c_device_uevent(const struct device *dev, struct kobj_uevent_env *env) { const struct i3c_device *i3cdev = dev_to_i3cdev(dev); - struct i3c_device_info devinfo; + struct i3c_device_info devinfo = {}; + u8 static_addr_method = 0; u16 manuf, part, ext; + int ret; + + i3c_bus_normaluse_lock(i3cdev->bus); + if (i3cdev->desc) { + devinfo = i3cdev->desc->info; + if (i3cdev->desc->boardinfo) + static_addr_method = i3cdev->desc->boardinfo->static_addr_method; + } + i3c_bus_normaluse_unlock(i3cdev->bus); + + if ((static_addr_method & I3C_ADDR_METHOD_SETAASA) && !devinfo.pid) { + ret = of_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; + + ret = acpi_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; + } - i3c_device_get_info(i3cdev, &devinfo); manuf = I3C_PID_MANUF_ID(devinfo.pid); part = I3C_PID_PART_ID(devinfo.pid); ext = I3C_PID_EXTRA_INFO(devinfo.pid); -- Gitee From c74cab32435eda7c7267f2317481adb0b2633289 Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Thu, 27 Aug 2026 20:46:45 +0800 Subject: [PATCH 26/27] i3c: master: Propagate SETAASA early device-add failures ANBZ: #44797 cherry-picked from https://lore.kernel.org/r/20260811103811.3380073-2-nirmoyd@nvidia.com i3c_master_bus_init() ignores errors from i3c_master_early_i3c_dev_add(). This is recoverable for SETDASA devices because they can be discovered later through ENTDAA. SETAASA devices do not participate in ENTDAA. If their early attach or reattach fails, bus initialization still sends the SETAASA broadcast and continues without registering the device. The target may consequently have an active address without a corresponding I3C device. Abort bus initialization when early device registration fails for a SETAASA device. Preserve the existing fallback behavior for other static address methods. Fixes: bbaf8733b848 ("i3c: master: Add support for devices using SETAASA") Signed-off-by: Nirmoy Das Signed-off-by: Shuai Xue --- drivers/i3c/master.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 8bae0dd740e9..66515161e8c4 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -2307,8 +2307,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master) * addressable. */ - if (i3cboardinfo->static_addr) - i3c_master_early_i3c_dev_add(master, i3cboardinfo); + if (i3cboardinfo->static_addr) { + ret = i3c_master_early_i3c_dev_add(master, i3cboardinfo); + if (ret && (i3cboardinfo->static_addr_method & + I3C_ADDR_METHOD_SETAASA)) + goto err_rstdaa; + } } /* -- Gitee From 56c1e900a6008e99c6c6a656de091a034cba54fb Mon Sep 17 00:00:00 2001 From: Shuai Xue Date: Thu, 27 Aug 2026 20:55:21 +0800 Subject: [PATCH 27/27] anolis: configs: enable I3C DW master and SPD5118 for arm64 ANBZ: #44797 Enable CONFIG_I3C, CONFIG_DW_I3C_MASTER and CONFIG_SENSORS_SPD5118 as modules on arm64 (L2-OPTIONAL) to support DDR5 DIMM temperature monitoring through the DesignWare I3C master on Vera (Tegra410) platforms. Other architectures keep I3C and SPD5118 disabled. CONFIG_SENSORS_SPD5118_DETECT is enabled by default when CONFIG_SENSORS_SPD5118 is set, so SPD5118 devices on both I2C and I3C buses are automatically detected. Classify the symbols newly exposed by enabling I3C on arm64 so that check_Kconfig passes: CONFIG_SENSORS_SPD5118_DETECT=y, CONFIG_REGMAP_I3C=m, and CONFIG_ADI_I3C_MASTER, CONFIG_CDNS_I3C_MASTER, CONFIG_SVC_I3C_MASTER, CONFIG_MIPI_I3C_HCI kept disabled. Signed-off-by: Shuai Xue --- anolis/configs/L2-OPTIONAL/arm64/CONFIG_ADI_I3C_MASTER | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_CDNS_I3C_MASTER | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_DW_I3C_MASTER | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C_OR_I2C | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_MIPI_I3C_HCI | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_REGMAP_I3C | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118 | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118_DETECT | 1 + anolis/configs/L2-OPTIONAL/arm64/CONFIG_SVC_I3C_MASTER | 1 + anolis/configs/L2-OPTIONAL/{default => loongarch}/CONFIG_I3C | 0 .../configs/L2-OPTIONAL/{default => loongarch}/CONFIG_I3C_OR_I2C | 0 .../L2-OPTIONAL/{default => loongarch}/CONFIG_SENSORS_SPD5118 | 0 anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C | 1 + anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C_OR_I2C | 1 + anolis/configs/L2-OPTIONAL/riscv/CONFIG_SENSORS_SPD5118 | 1 + anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C | 1 + anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C_OR_I2C | 1 + anolis/configs/L2-OPTIONAL/x86/CONFIG_SENSORS_SPD5118 | 1 + 19 files changed, 16 insertions(+) create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_ADI_I3C_MASTER create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_CDNS_I3C_MASTER create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_DW_I3C_MASTER create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C_OR_I2C create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_MIPI_I3C_HCI create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_REGMAP_I3C create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118 create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118_DETECT create mode 100644 anolis/configs/L2-OPTIONAL/arm64/CONFIG_SVC_I3C_MASTER rename anolis/configs/L2-OPTIONAL/{default => loongarch}/CONFIG_I3C (100%) rename anolis/configs/L2-OPTIONAL/{default => loongarch}/CONFIG_I3C_OR_I2C (100%) rename anolis/configs/L2-OPTIONAL/{default => loongarch}/CONFIG_SENSORS_SPD5118 (100%) create mode 100644 anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C create mode 100644 anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C_OR_I2C create mode 100644 anolis/configs/L2-OPTIONAL/riscv/CONFIG_SENSORS_SPD5118 create mode 100644 anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C create mode 100644 anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C_OR_I2C create mode 100644 anolis/configs/L2-OPTIONAL/x86/CONFIG_SENSORS_SPD5118 diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_ADI_I3C_MASTER b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_ADI_I3C_MASTER new file mode 100644 index 000000000000..9a3f4f3df1e8 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_ADI_I3C_MASTER @@ -0,0 +1 @@ +# CONFIG_ADI_I3C_MASTER is not set diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_CDNS_I3C_MASTER b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_CDNS_I3C_MASTER new file mode 100644 index 000000000000..16a7cd172070 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_CDNS_I3C_MASTER @@ -0,0 +1 @@ +# CONFIG_CDNS_I3C_MASTER is not set diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_DW_I3C_MASTER b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_DW_I3C_MASTER new file mode 100644 index 000000000000..332d1efb98bc --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_DW_I3C_MASTER @@ -0,0 +1 @@ +CONFIG_DW_I3C_MASTER=m diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C new file mode 100644 index 000000000000..77de789c5935 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C @@ -0,0 +1 @@ +CONFIG_I3C=m diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C_OR_I2C b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C_OR_I2C new file mode 100644 index 000000000000..4c82afc54daf --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_I3C_OR_I2C @@ -0,0 +1 @@ +CONFIG_I3C_OR_I2C=m diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_MIPI_I3C_HCI b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_MIPI_I3C_HCI new file mode 100644 index 000000000000..37cbc0f6434c --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_MIPI_I3C_HCI @@ -0,0 +1 @@ +# CONFIG_MIPI_I3C_HCI is not set diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_REGMAP_I3C b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_REGMAP_I3C new file mode 100644 index 000000000000..dbfcc9539eea --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_REGMAP_I3C @@ -0,0 +1 @@ +CONFIG_REGMAP_I3C=m diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118 b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118 new file mode 100644 index 000000000000..737a74cf3d75 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118 @@ -0,0 +1 @@ +CONFIG_SENSORS_SPD5118=m diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118_DETECT b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118_DETECT new file mode 100644 index 000000000000..7c3828862733 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SENSORS_SPD5118_DETECT @@ -0,0 +1 @@ +CONFIG_SENSORS_SPD5118_DETECT=y diff --git a/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SVC_I3C_MASTER b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SVC_I3C_MASTER new file mode 100644 index 000000000000..511ba79579d0 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/arm64/CONFIG_SVC_I3C_MASTER @@ -0,0 +1 @@ +# CONFIG_SVC_I3C_MASTER is not set diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_I3C b/anolis/configs/L2-OPTIONAL/loongarch/CONFIG_I3C similarity index 100% rename from anolis/configs/L2-OPTIONAL/default/CONFIG_I3C rename to anolis/configs/L2-OPTIONAL/loongarch/CONFIG_I3C diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_I3C_OR_I2C b/anolis/configs/L2-OPTIONAL/loongarch/CONFIG_I3C_OR_I2C similarity index 100% rename from anolis/configs/L2-OPTIONAL/default/CONFIG_I3C_OR_I2C rename to anolis/configs/L2-OPTIONAL/loongarch/CONFIG_I3C_OR_I2C diff --git a/anolis/configs/L2-OPTIONAL/default/CONFIG_SENSORS_SPD5118 b/anolis/configs/L2-OPTIONAL/loongarch/CONFIG_SENSORS_SPD5118 similarity index 100% rename from anolis/configs/L2-OPTIONAL/default/CONFIG_SENSORS_SPD5118 rename to anolis/configs/L2-OPTIONAL/loongarch/CONFIG_SENSORS_SPD5118 diff --git a/anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C b/anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C new file mode 100644 index 000000000000..387a138c15d8 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C @@ -0,0 +1 @@ +# CONFIG_I3C is not set diff --git a/anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C_OR_I2C b/anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C_OR_I2C new file mode 100644 index 000000000000..d8d21ac540c8 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/riscv/CONFIG_I3C_OR_I2C @@ -0,0 +1 @@ +CONFIG_I3C_OR_I2C=y diff --git a/anolis/configs/L2-OPTIONAL/riscv/CONFIG_SENSORS_SPD5118 b/anolis/configs/L2-OPTIONAL/riscv/CONFIG_SENSORS_SPD5118 new file mode 100644 index 000000000000..8f4d5041e118 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/riscv/CONFIG_SENSORS_SPD5118 @@ -0,0 +1 @@ +# CONFIG_SENSORS_SPD5118 is not set diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C b/anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C new file mode 100644 index 000000000000..387a138c15d8 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C @@ -0,0 +1 @@ +# CONFIG_I3C is not set diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C_OR_I2C b/anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C_OR_I2C new file mode 100644 index 000000000000..d8d21ac540c8 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/x86/CONFIG_I3C_OR_I2C @@ -0,0 +1 @@ +CONFIG_I3C_OR_I2C=y diff --git a/anolis/configs/L2-OPTIONAL/x86/CONFIG_SENSORS_SPD5118 b/anolis/configs/L2-OPTIONAL/x86/CONFIG_SENSORS_SPD5118 new file mode 100644 index 000000000000..8f4d5041e118 --- /dev/null +++ b/anolis/configs/L2-OPTIONAL/x86/CONFIG_SENSORS_SPD5118 @@ -0,0 +1 @@ +# CONFIG_SENSORS_SPD5118 is not set -- Gitee