From 812928613e8c6199e872979503ad83dc0b452c2f Mon Sep 17 00:00:00 2001 From: Kyle Nieman Date: Mon, 3 Aug 2026 16:19:16 -0500 Subject: [PATCH] i3c: Add RSTDAA CCC to SYSFS BMC is unable to use I3C mode for the I3C hubs. Need a workaround to put the DIMMs in I3C mode back to I2C mode. (In I3C mode due BIOS setting of host) Add SYSFS property that ONLY performs the RSTDAA CCC without any of the software cleanup. DIMMs shouldn't be set up as I3C devices for the BMC, so this should be fine. Tested: Verified in Falcon. Signed-off-by: Kyle Nieman --- drivers/i3c/master.c | 62 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 2b763aabbb411a..053c821d1d19ea 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -835,6 +835,67 @@ static ssize_t rescan_store(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_WO(rescan); +/* + * The I3C hubs can't have CCCs sent to them prior to having their internal + * power supply configured. Otherwise, the bus will hang. The devices behind + * the hubs are interacted with by another master, and put into I3C mode. This + * rstdaa is meant to put the devices behind the hubs back into I2C + * mode. This WILL NOT perform any of the software cleanup. Existing I3C + * devices aren't unregistered. (IE still exposed via SYSFS) + */ +static ssize_t rstdaa_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i3c_master_controller *master = dev_to_i3cmaster(dev); + struct i3c_bus *bus = i3c_master_get_bus(master); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + if (!val) + return count; + + i3c_bus_maintenance_lock(bus); + + /* + * Disable SIR/MR/HJ before RSTDAA. A device that just dropped its + * dynamic address is otherwise free to Hot-Join immediately; if the + * controller ACKs it, that silently kicks off a full DAA, which this + * workaround must not trigger since it intentionally leaves software + * state alone and does no rediscovery of its own. + */ + ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR, + I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR | + I3C_CCC_EVENT_HJ); + if (ret && ret != I3C_ERROR_M2) { + dev_dbg(&master->dev, + "Failed to run broadcast DISEC for rstdaa workaround, ret=%d\n", + ret); + goto unlock; + } + + ret = i3c_master_rstdaa_locked(master, I3C_BROADCAST_ADDR); + if (ret && ret != I3C_ERROR_M2) { + dev_dbg(&master->dev, + "Failed to run RSTDAA for rstdaa workaround, ret=%d\n", + ret); + goto unlock; + } + + i3c_bus_maintenance_unlock(bus); + + return count; + +unlock: + i3c_bus_maintenance_unlock(bus); + return ret; +} +static DEVICE_ATTR_WO(rstdaa); + static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_mode.attr, &dev_attr_current_master.attr, @@ -849,6 +910,7 @@ static struct attribute *i3c_masterdev_attrs[] = { &dev_attr_bus_context.attr, &dev_attr_bus_reset.attr, &dev_attr_rescan.attr, + &dev_attr_rstdaa.attr, NULL, }; ATTRIBUTE_GROUPS(i3c_masterdev);