Message ID | 20240304000355.2614421-1-hamish.martin@alliedtelesis.co.nz (mailing list archive) |
---|---|
State | Handled Elsewhere, archived |
Headers | show |
Series | [v3] i2c: acpi: Unbind mux adapters before delete | expand |
On Mon, Mar 04, 2024 at 01:03:55PM +1300, Hamish Martin wrote: > There is an issue with ACPI overlay table removal specifically related > to I2C multiplexers. > > Consider an ACPI SSDT Overlay that defines a PCA9548 I2C mux on an > existing I2C bus. When this table is loaded we see the creation of a > device for the overall PCA9548 chip and 8 further devices - one > i2c_adapter each for the mux channels. These are all bound to their > ACPI equivalents via an eventual invocation of acpi_bind_one(). > > When we unload the SSDT overlay we run into the problem. The ACPI > devices are deleted as normal via acpi_device_del_work_fn() and the > acpi_device_del_list. > > However, the following warning and stack trace is output as the > deletion does not go smoothly: Applied to for-current, thanks!
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index d6037a328669..67fa8deccef6 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -445,6 +445,11 @@ static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) return i2c_find_device_by_fwnode(acpi_fwnode_handle(adev)); } +static struct i2c_adapter *i2c_acpi_find_adapter_by_adev(struct acpi_device *adev) +{ + return i2c_find_adapter_by_fwnode(acpi_fwnode_handle(adev)); +} + static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, void *arg) { @@ -471,11 +476,17 @@ static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, break; client = i2c_acpi_find_client_by_adev(adev); - if (!client) - break; + if (client) { + i2c_unregister_device(client); + put_device(&client->dev); + } + + adapter = i2c_acpi_find_adapter_by_adev(adev); + if (adapter) { + acpi_device_notify_remove(&adapter->dev); + put_device(&adapter->dev); + } - i2c_unregister_device(client); - put_device(&client->dev); break; }