Skip to content

Commit 525e6fa

Browse files
Octavian Purdilarafaeljw
authored andcommitted
i2c / ACPI: add support for ACPI reconfigure notifications
This patch adds supports for I2C device enumeration and removal via ACPI reconfiguration notifications that are send as a result of an ACPI table load or unload operation. The code is very similar with the device tree reconfiguration code with only small differences in the way we test and set the enumerated state of the device: * the equivalent of device tree's OF_POPULATED flag is the flags.visited field in the ACPI device and the following wrappers are used to manipulate it: acpi_device_enumerated(), acpi_device_set_enumerated() and acpi_device_clear_enumerated() * the device tree code checks of status of the OF_POPULATED flag to avoid trying to create duplicate Linux devices in two places: once when the controller is probed, and once when the reconfigure event is received; in the ACPI code the check is performed only once when the ACPI namespace is searched because this code path is invoked in both of the two mentioned cases The rest of the enumeration handling is similar with device tree: when the Linux device is unregistered the ACPI device is marked as not enumerated; also, when a device remove notification is received we check that the device is in the enumerated state before continuing with the removal of the Linux device. Signed-off-by: Octavian Purdila <[email protected]> Reviewed-by: Mika Westerberg <[email protected]> Acked-by: Wolfram Sang <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 68bdb67 commit 525e6fa

File tree

1 file changed

+137
-38
lines changed

1 file changed

+137
-38
lines changed

drivers/i2c/i2c-core.c

Lines changed: 137 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,11 @@ struct acpi_i2c_lookup {
107107
acpi_handle device_handle;
108108
};
109109

110-
static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)
110+
static int acpi_i2c_fill_info(struct acpi_resource *ares, void *data)
111111
{
112112
struct acpi_i2c_lookup *lookup = data;
113113
struct i2c_board_info *info = lookup->info;
114114
struct acpi_resource_i2c_serialbus *sb;
115-
acpi_handle adapter_handle;
116115
acpi_status status;
117116

118117
if (info->addr || ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
@@ -122,80 +121,102 @@ static int acpi_i2c_find_address(struct acpi_resource *ares, void *data)
122121
if (sb->type != ACPI_RESOURCE_SERIAL_TYPE_I2C)
123122
return 1;
124123

125-
/*
126-
* Extract the ResourceSource and make sure that the handle matches
127-
* with the I2C adapter handle.
128-
*/
129124
status = acpi_get_handle(lookup->device_handle,
130125
sb->resource_source.string_ptr,
131-
&adapter_handle);
132-
if (ACPI_SUCCESS(status) && adapter_handle == lookup->adapter_handle) {
133-
info->addr = sb->slave_address;
134-
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
135-
info->flags |= I2C_CLIENT_TEN;
136-
}
126+
&lookup->adapter_handle);
127+
if (!ACPI_SUCCESS(status))
128+
return 1;
129+
130+
info->addr = sb->slave_address;
131+
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
132+
info->flags |= I2C_CLIENT_TEN;
137133

138134
return 1;
139135
}
140136

141-
static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
142-
void *data, void **return_value)
137+
static int acpi_i2c_get_info(struct acpi_device *adev,
138+
struct i2c_board_info *info,
139+
acpi_handle *adapter_handle)
143140
{
144-
struct i2c_adapter *adapter = data;
145141
struct list_head resource_list;
146-
struct acpi_i2c_lookup lookup;
147142
struct resource_entry *entry;
148-
struct i2c_board_info info;
149-
struct acpi_device *adev;
143+
struct acpi_i2c_lookup lookup;
150144
int ret;
151145

152-
if (acpi_bus_get_device(handle, &adev))
153-
return AE_OK;
154-
if (acpi_bus_get_status(adev) || !adev->status.present)
155-
return AE_OK;
146+
if (acpi_bus_get_status(adev) || !adev->status.present ||
147+
acpi_device_enumerated(adev))
148+
return -EINVAL;
156149

157-
memset(&info, 0, sizeof(info));
158-
info.fwnode = acpi_fwnode_handle(adev);
150+
memset(info, 0, sizeof(*info));
151+
info->fwnode = acpi_fwnode_handle(adev);
159152

160153
memset(&lookup, 0, sizeof(lookup));
161-
lookup.adapter_handle = ACPI_HANDLE(&adapter->dev);
162-
lookup.device_handle = handle;
163-
lookup.info = &info;
154+
lookup.device_handle = acpi_device_handle(adev);
155+
lookup.info = info;
164156

165-
/*
166-
* Look up for I2cSerialBus resource with ResourceSource that
167-
* matches with this adapter.
168-
*/
157+
/* Look up for I2cSerialBus resource */
169158
INIT_LIST_HEAD(&resource_list);
170159
ret = acpi_dev_get_resources(adev, &resource_list,
171-
acpi_i2c_find_address, &lookup);
160+
acpi_i2c_fill_info, &lookup);
172161
acpi_dev_free_resource_list(&resource_list);
173162

174-
if (ret < 0 || !info.addr)
175-
return AE_OK;
163+
if (ret < 0 || !info->addr)
164+
return -EINVAL;
165+
166+
*adapter_handle = lookup.adapter_handle;
176167

177168
/* Then fill IRQ number if any */
178169
ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
179170
if (ret < 0)
180-
return AE_OK;
171+
return -EINVAL;
181172

182173
resource_list_for_each_entry(entry, &resource_list) {
183174
if (resource_type(entry->res) == IORESOURCE_IRQ) {
184-
info.irq = entry->res->start;
175+
info->irq = entry->res->start;
185176
break;
186177
}
187178
}
188179

189180
acpi_dev_free_resource_list(&resource_list);
190181

182+
strlcpy(info->type, dev_name(&adev->dev), sizeof(info->type));
183+
184+
return 0;
185+
}
186+
187+
static void acpi_i2c_register_device(struct i2c_adapter *adapter,
188+
struct acpi_device *adev,
189+
struct i2c_board_info *info)
190+
{
191191
adev->power.flags.ignore_parent = true;
192-
strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
193-
if (!i2c_new_device(adapter, &info)) {
192+
acpi_device_set_enumerated(adev);
193+
194+
if (!i2c_new_device(adapter, info)) {
194195
adev->power.flags.ignore_parent = false;
195196
dev_err(&adapter->dev,
196197
"failed to add I2C device %s from ACPI\n",
197198
dev_name(&adev->dev));
198199
}
200+
}
201+
202+
static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
203+
void *data, void **return_value)
204+
{
205+
struct i2c_adapter *adapter = data;
206+
struct acpi_device *adev;
207+
acpi_handle adapter_handle;
208+
struct i2c_board_info info;
209+
210+
if (acpi_bus_get_device(handle, &adev))
211+
return AE_OK;
212+
213+
if (acpi_i2c_get_info(adev, &info, &adapter_handle))
214+
return AE_OK;
215+
216+
if (adapter_handle != ACPI_HANDLE(&adapter->dev))
217+
return AE_OK;
218+
219+
acpi_i2c_register_device(adapter, adev, &info);
199220

200221
return AE_OK;
201222
}
@@ -225,8 +246,80 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap)
225246
dev_warn(&adap->dev, "failed to enumerate I2C slaves\n");
226247
}
227248

249+
static int acpi_i2c_match_adapter(struct device *dev, void *data)
250+
{
251+
struct i2c_adapter *adapter = i2c_verify_adapter(dev);
252+
253+
if (!adapter)
254+
return 0;
255+
256+
return ACPI_HANDLE(dev) == (acpi_handle)data;
257+
}
258+
259+
static int acpi_i2c_match_device(struct device *dev, void *data)
260+
{
261+
return ACPI_COMPANION(dev) == data;
262+
}
263+
264+
static struct i2c_adapter *acpi_i2c_find_adapter_by_handle(acpi_handle handle)
265+
{
266+
struct device *dev;
267+
268+
dev = bus_find_device(&i2c_bus_type, NULL, handle,
269+
acpi_i2c_match_adapter);
270+
return dev ? i2c_verify_adapter(dev) : NULL;
271+
}
272+
273+
static struct i2c_client *acpi_i2c_find_client_by_adev(struct acpi_device *adev)
274+
{
275+
struct device *dev;
276+
277+
dev = bus_find_device(&i2c_bus_type, NULL, adev, acpi_i2c_match_device);
278+
return dev ? i2c_verify_client(dev) : NULL;
279+
}
280+
281+
static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value,
282+
void *arg)
283+
{
284+
struct acpi_device *adev = arg;
285+
struct i2c_board_info info;
286+
acpi_handle adapter_handle;
287+
struct i2c_adapter *adapter;
288+
struct i2c_client *client;
289+
290+
switch (value) {
291+
case ACPI_RECONFIG_DEVICE_ADD:
292+
if (acpi_i2c_get_info(adev, &info, &adapter_handle))
293+
break;
294+
295+
adapter = acpi_i2c_find_adapter_by_handle(adapter_handle);
296+
if (!adapter)
297+
break;
298+
299+
acpi_i2c_register_device(adapter, adev, &info);
300+
break;
301+
case ACPI_RECONFIG_DEVICE_REMOVE:
302+
if (!acpi_device_enumerated(adev))
303+
break;
304+
305+
client = acpi_i2c_find_client_by_adev(adev);
306+
if (!client)
307+
break;
308+
309+
i2c_unregister_device(client);
310+
put_device(&client->dev);
311+
break;
312+
}
313+
314+
return NOTIFY_OK;
315+
}
316+
317+
static struct notifier_block i2c_acpi_notifier = {
318+
.notifier_call = acpi_i2c_notify,
319+
};
228320
#else /* CONFIG_ACPI */
229321
static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { }
322+
extern struct notifier_block i2c_acpi_notifier;
230323
#endif /* CONFIG_ACPI */
231324

232325
#ifdef CONFIG_ACPI_I2C_OPREGION
@@ -1089,6 +1182,8 @@ void i2c_unregister_device(struct i2c_client *client)
10891182
{
10901183
if (client->dev.of_node)
10911184
of_node_clear_flag(client->dev.of_node, OF_POPULATED);
1185+
if (ACPI_COMPANION(&client->dev))
1186+
acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
10921187
device_unregister(&client->dev);
10931188
}
10941189
EXPORT_SYMBOL_GPL(i2c_unregister_device);
@@ -2117,6 +2212,8 @@ static int __init i2c_init(void)
21172212

21182213
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
21192214
WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier));
2215+
if (IS_ENABLED(CONFIG_ACPI))
2216+
WARN_ON(acpi_reconfig_notifier_register(&i2c_acpi_notifier));
21202217

21212218
return 0;
21222219

@@ -2132,6 +2229,8 @@ static int __init i2c_init(void)
21322229

21332230
static void __exit i2c_exit(void)
21342231
{
2232+
if (IS_ENABLED(CONFIG_ACPI))
2233+
WARN_ON(acpi_reconfig_notifier_unregister(&i2c_acpi_notifier));
21352234
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
21362235
WARN_ON(of_reconfig_notifier_unregister(&i2c_of_notifier));
21372236
i2c_del_driver(&dummy_driver);

0 commit comments

Comments
 (0)