Commit | Line | Data |
---|---|---|
55716d26 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4e10d12a DSL |
2 | /* |
3 | * Link physical devices with ACPI devices support | |
4 | * | |
5 | * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com> | |
6 | * Copyright (c) 2005 Intel Corp. | |
4e10d12a | 7 | */ |
d4f54a18 | 8 | |
e2935abb HG |
9 | #define pr_fmt(fmt) "ACPI: " fmt |
10 | ||
d4f54a18 | 11 | #include <linux/acpi_iort.h> |
214f2c90 | 12 | #include <linux/export.h> |
4e10d12a DSL |
13 | #include <linux/init.h> |
14 | #include <linux/list.h> | |
15 | #include <linux/device.h> | |
5a0e3ad6 | 16 | #include <linux/slab.h> |
4e10d12a DSL |
17 | #include <linux/rwsem.h> |
18 | #include <linux/acpi.h> | |
d0562674 | 19 | #include <linux/dma-mapping.h> |
47954481 RW |
20 | #include <linux/pci.h> |
21 | #include <linux/pci-acpi.h> | |
d4f54a18 | 22 | #include <linux/platform_device.h> |
4e10d12a | 23 | |
a192a958 LB |
24 | #include "internal.h" |
25 | ||
4e10d12a DSL |
26 | static LIST_HEAD(bus_type_list); |
27 | static DECLARE_RWSEM(bus_type_sem); | |
28 | ||
1033f904 | 29 | #define PHYSICAL_NODE_STRING "physical_node" |
007ccfcf | 30 | #define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10) |
1033f904 | 31 | |
4e10d12a DSL |
32 | int register_acpi_bus_type(struct acpi_bus_type *type) |
33 | { | |
34 | if (acpi_disabled) | |
35 | return -ENODEV; | |
e3f02c52 | 36 | if (type && type->match && type->find_companion) { |
4e10d12a DSL |
37 | down_write(&bus_type_sem); |
38 | list_add_tail(&type->list, &bus_type_list); | |
39 | up_write(&bus_type_sem); | |
e2935abb | 40 | pr_info("bus type %s registered\n", type->name); |
4e10d12a DSL |
41 | return 0; |
42 | } | |
43 | return -ENODEV; | |
44 | } | |
91e4d5a1 | 45 | EXPORT_SYMBOL_GPL(register_acpi_bus_type); |
4e10d12a | 46 | |
4e10d12a DSL |
47 | int unregister_acpi_bus_type(struct acpi_bus_type *type) |
48 | { | |
49 | if (acpi_disabled) | |
50 | return 0; | |
51 | if (type) { | |
52 | down_write(&bus_type_sem); | |
53 | list_del_init(&type->list); | |
54 | up_write(&bus_type_sem); | |
e2935abb | 55 | pr_info("bus type %s unregistered\n", type->name); |
4e10d12a DSL |
56 | return 0; |
57 | } | |
58 | return -ENODEV; | |
59 | } | |
91e4d5a1 | 60 | EXPORT_SYMBOL_GPL(unregister_acpi_bus_type); |
4e10d12a | 61 | |
53540098 | 62 | static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) |
4e10d12a DSL |
63 | { |
64 | struct acpi_bus_type *tmp, *ret = NULL; | |
65 | ||
66 | down_read(&bus_type_sem); | |
67 | list_for_each_entry(tmp, &bus_type_list, list) { | |
53540098 | 68 | if (tmp->match(dev)) { |
4e10d12a DSL |
69 | ret = tmp; |
70 | break; | |
71 | } | |
72 | } | |
73 | up_read(&bus_type_sem); | |
74 | return ret; | |
75 | } | |
76 | ||
11b88ee2 | 77 | #define FIND_CHILD_MIN_SCORE 1 |
f64e4275 HG |
78 | #define FIND_CHILD_MID_SCORE 2 |
79 | #define FIND_CHILD_MAX_SCORE 3 | |
11b88ee2 | 80 | |
f5122be8 RW |
81 | static int match_any(struct acpi_device *adev, void *not_used) |
82 | { | |
83 | return 1; | |
84 | } | |
85 | ||
86 | static bool acpi_dev_has_children(struct acpi_device *adev) | |
87 | { | |
88 | return acpi_dev_for_each_child(adev, match_any, NULL) > 0; | |
89 | } | |
90 | ||
d9fef0c4 | 91 | static int find_child_checks(struct acpi_device *adev, bool check_children) |
60f75b8e RW |
92 | { |
93 | unsigned long long sta; | |
94 | acpi_status status; | |
95 | ||
f5122be8 | 96 | if (check_children && !acpi_dev_has_children(adev)) |
b7fbf4ce RW |
97 | return -ENODEV; |
98 | ||
d9fef0c4 | 99 | status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); |
f64e4275 HG |
100 | if (status == AE_NOT_FOUND) { |
101 | /* | |
102 | * Special case: backlight device objects without _STA are | |
103 | * preferred to other objects with the same _ADR value, because | |
104 | * it is more likely that they are actually useful. | |
105 | */ | |
106 | if (adev->pnp.type.backlight) | |
107 | return FIND_CHILD_MID_SCORE; | |
108 | ||
b7fbf4ce | 109 | return FIND_CHILD_MIN_SCORE; |
f64e4275 | 110 | } |
60f75b8e | 111 | |
b7fbf4ce | 112 | if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) |
d9fef0c4 | 113 | return -ENODEV; |
60f75b8e | 114 | |
c2a6bbaf | 115 | /* |
fdad4e7a RW |
116 | * If the device has a _HID returning a valid ACPI/PNP device ID, it is |
117 | * better to make it look less attractive here, so that the other device | |
118 | * with the same _ADR value (that may not have a valid device ID) can be | |
119 | * matched going forward. [This means a second spec violation in a row, | |
120 | * so whatever we do here is best effort anyway.] | |
c2a6bbaf | 121 | */ |
b7fbf4ce RW |
122 | if (adev->pnp.type.platform_id) |
123 | return FIND_CHILD_MIN_SCORE; | |
124 | ||
125 | return FIND_CHILD_MAX_SCORE; | |
60f75b8e RW |
126 | } |
127 | ||
d21b5700 RW |
128 | struct find_child_walk_data { |
129 | struct acpi_device *adev; | |
130 | u64 address; | |
131 | int score; | |
2f6fe93f | 132 | bool check_sta; |
d21b5700 RW |
133 | bool check_children; |
134 | }; | |
135 | ||
136 | static int check_one_child(struct acpi_device *adev, void *data) | |
60f75b8e | 137 | { |
d21b5700 RW |
138 | struct find_child_walk_data *wd = data; |
139 | int score; | |
d9fef0c4 | 140 | |
d21b5700 RW |
141 | if (!adev->pnp.type.bus_address || acpi_device_adr(adev) != wd->address) |
142 | return 0; | |
5ce79d20 | 143 | |
d21b5700 | 144 | if (!wd->adev) { |
2f6fe93f RW |
145 | /* |
146 | * This is the first matching object, so save it. If it is not | |
147 | * necessary to look for any other matching objects, stop the | |
148 | * search. | |
149 | */ | |
d21b5700 | 150 | wd->adev = adev; |
2f6fe93f | 151 | return !(wd->check_sta || wd->check_children); |
d21b5700 | 152 | } |
d9fef0c4 | 153 | |
d21b5700 RW |
154 | /* |
155 | * There is more than one matching device object with the same _ADR | |
156 | * value. That really is unexpected, so we are kind of beyond the scope | |
157 | * of the spec here. We have to choose which one to return, though. | |
158 | * | |
159 | * First, get the score for the previously found object and terminate | |
160 | * the walk if it is maximum. | |
161 | */ | |
162 | if (!wd->score) { | |
163 | score = find_child_checks(wd->adev, wd->check_children); | |
164 | if (score == FIND_CHILD_MAX_SCORE) | |
165 | return 1; | |
166 | ||
167 | wd->score = score; | |
168 | } | |
169 | /* | |
170 | * Second, if the object that has just been found has a better score, | |
171 | * replace the previously found one with it and terminate the walk if | |
172 | * the new score is maximum. | |
173 | */ | |
174 | score = find_child_checks(adev, wd->check_children); | |
175 | if (score > wd->score) { | |
176 | wd->adev = adev; | |
177 | if (score == FIND_CHILD_MAX_SCORE) | |
178 | return 1; | |
d9fef0c4 | 179 | |
d21b5700 | 180 | wd->score = score; |
4e10d12a | 181 | } |
d21b5700 RW |
182 | |
183 | /* Continue, because there may be better matches. */ | |
184 | return 0; | |
185 | } | |
186 | ||
2f6fe93f RW |
187 | static struct acpi_device *acpi_find_child(struct acpi_device *parent, |
188 | u64 address, bool check_children, | |
189 | bool check_sta) | |
d21b5700 RW |
190 | { |
191 | struct find_child_walk_data wd = { | |
192 | .address = address, | |
193 | .check_children = check_children, | |
2f6fe93f | 194 | .check_sta = check_sta, |
d21b5700 RW |
195 | .adev = NULL, |
196 | .score = 0, | |
197 | }; | |
198 | ||
199 | if (parent) | |
200 | acpi_dev_for_each_child(parent, check_one_child, &wd); | |
201 | ||
202 | return wd.adev; | |
4e10d12a | 203 | } |
2f6fe93f RW |
204 | |
205 | struct acpi_device *acpi_find_child_device(struct acpi_device *parent, | |
206 | u64 address, bool check_children) | |
207 | { | |
208 | return acpi_find_child(parent, address, check_children, true); | |
209 | } | |
9c5ad36d | 210 | EXPORT_SYMBOL_GPL(acpi_find_child_device); |
4e10d12a | 211 | |
2f6fe93f RW |
212 | struct acpi_device *acpi_find_child_by_adr(struct acpi_device *adev, |
213 | acpi_bus_address adr) | |
214 | { | |
215 | return acpi_find_child(adev, adr, false, false); | |
216 | } | |
217 | EXPORT_SYMBOL_GPL(acpi_find_child_by_adr); | |
218 | ||
bdbdbf91 RW |
219 | static void acpi_physnode_link_name(char *buf, unsigned int node_id) |
220 | { | |
221 | if (node_id > 0) | |
222 | snprintf(buf, PHYSICAL_NODE_NAME_SIZE, | |
223 | PHYSICAL_NODE_STRING "%u", node_id); | |
224 | else | |
225 | strcpy(buf, PHYSICAL_NODE_STRING); | |
226 | } | |
227 | ||
24dee1fc | 228 | int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) |
4e10d12a | 229 | { |
f3fd0c8a | 230 | struct acpi_device_physical_node *physical_node, *pn; |
007ccfcf RW |
231 | char physical_node_name[PHYSICAL_NODE_NAME_SIZE]; |
232 | struct list_head *physnode_list; | |
233 | unsigned int node_id; | |
1033f904 | 234 | int retval = -EINVAL; |
4e10d12a | 235 | |
ca5b74d2 | 236 | if (has_acpi_companion(dev)) { |
24dee1fc | 237 | if (acpi_dev) { |
7b199811 | 238 | dev_warn(dev, "ACPI companion already set\n"); |
f3fd0c8a RW |
239 | return -EINVAL; |
240 | } else { | |
7b199811 | 241 | acpi_dev = ACPI_COMPANION(dev); |
f3fd0c8a | 242 | } |
4e10d12a | 243 | } |
7b199811 | 244 | if (!acpi_dev) |
f3fd0c8a | 245 | return -EINVAL; |
1033f904 | 246 | |
4cbaba4e | 247 | acpi_dev_get(acpi_dev); |
4e10d12a | 248 | get_device(dev); |
f3fd0c8a | 249 | physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL); |
1033f904 LT |
250 | if (!physical_node) { |
251 | retval = -ENOMEM; | |
252 | goto err; | |
4e10d12a | 253 | } |
4e10d12a | 254 | |
1033f904 | 255 | mutex_lock(&acpi_dev->physical_node_lock); |
f3fd0c8a | 256 | |
007ccfcf RW |
257 | /* |
258 | * Keep the list sorted by node_id so that the IDs of removed nodes can | |
259 | * be recycled easily. | |
260 | */ | |
261 | physnode_list = &acpi_dev->physical_node_list; | |
262 | node_id = 0; | |
263 | list_for_each_entry(pn, &acpi_dev->physical_node_list, node) { | |
264 | /* Sanity check. */ | |
f3fd0c8a | 265 | if (pn->dev == dev) { |
3342c753 RW |
266 | mutex_unlock(&acpi_dev->physical_node_lock); |
267 | ||
f3fd0c8a | 268 | dev_warn(dev, "Already associated with ACPI node\n"); |
3342c753 | 269 | kfree(physical_node); |
7b199811 | 270 | if (ACPI_COMPANION(dev) != acpi_dev) |
3342c753 | 271 | goto err; |
3fe444ad | 272 | |
3342c753 | 273 | put_device(dev); |
4cbaba4e | 274 | acpi_dev_put(acpi_dev); |
3342c753 | 275 | return 0; |
f3fd0c8a | 276 | } |
007ccfcf RW |
277 | if (pn->node_id == node_id) { |
278 | physnode_list = &pn->node; | |
279 | node_id++; | |
280 | } | |
1071695f DB |
281 | } |
282 | ||
007ccfcf | 283 | physical_node->node_id = node_id; |
1033f904 | 284 | physical_node->dev = dev; |
007ccfcf | 285 | list_add(&physical_node->node, physnode_list); |
1033f904 | 286 | acpi_dev->physical_node_count++; |
f3fd0c8a | 287 | |
ca5b74d2 | 288 | if (!has_acpi_companion(dev)) |
7b199811 | 289 | ACPI_COMPANION_SET(dev, acpi_dev); |
1033f904 | 290 | |
bdbdbf91 | 291 | acpi_physnode_link_name(physical_node_name, node_id); |
1033f904 | 292 | retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, |
f501b6ec | 293 | physical_node_name); |
464c1147 RW |
294 | if (retval) |
295 | dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n", | |
296 | physical_node_name, retval); | |
297 | ||
1033f904 | 298 | retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, |
f501b6ec | 299 | "firmware_node"); |
464c1147 RW |
300 | if (retval) |
301 | dev_err(dev, "Failed to create link firmware_node (%d)\n", | |
302 | retval); | |
1033f904 | 303 | |
40055206 RW |
304 | mutex_unlock(&acpi_dev->physical_node_lock); |
305 | ||
1033f904 LT |
306 | if (acpi_dev->wakeup.flags.valid) |
307 | device_set_wakeup_capable(dev, true); | |
308 | ||
4e10d12a | 309 | return 0; |
1033f904 LT |
310 | |
311 | err: | |
7b199811 | 312 | ACPI_COMPANION_SET(dev, NULL); |
1033f904 | 313 | put_device(dev); |
4cbaba4e | 314 | acpi_dev_put(acpi_dev); |
1033f904 | 315 | return retval; |
4e10d12a | 316 | } |
ac212b69 | 317 | EXPORT_SYMBOL_GPL(acpi_bind_one); |
4e10d12a | 318 | |
ac212b69 | 319 | int acpi_unbind_one(struct device *dev) |
4e10d12a | 320 | { |
7b199811 | 321 | struct acpi_device *acpi_dev = ACPI_COMPANION(dev); |
1033f904 | 322 | struct acpi_device_physical_node *entry; |
1033f904 | 323 | |
7b199811 | 324 | if (!acpi_dev) |
4e10d12a | 325 | return 0; |
1071695f | 326 | |
1033f904 | 327 | mutex_lock(&acpi_dev->physical_node_lock); |
3e332783 RW |
328 | |
329 | list_for_each_entry(entry, &acpi_dev->physical_node_list, node) | |
330 | if (entry->dev == dev) { | |
331 | char physnode_name[PHYSICAL_NODE_NAME_SIZE]; | |
332 | ||
333 | list_del(&entry->node); | |
334 | acpi_dev->physical_node_count--; | |
335 | ||
336 | acpi_physnode_link_name(physnode_name, entry->node_id); | |
337 | sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name); | |
338 | sysfs_remove_link(&dev->kobj, "firmware_node"); | |
7b199811 | 339 | ACPI_COMPANION_SET(dev, NULL); |
a104b4d4 | 340 | /* Drop references taken by acpi_bind_one(). */ |
3e332783 | 341 | put_device(dev); |
4cbaba4e | 342 | acpi_dev_put(acpi_dev); |
3e332783 RW |
343 | kfree(entry); |
344 | break; | |
345 | } | |
346 | ||
1033f904 | 347 | mutex_unlock(&acpi_dev->physical_node_lock); |
4e10d12a DSL |
348 | return 0; |
349 | } | |
ac212b69 | 350 | EXPORT_SYMBOL_GPL(acpi_unbind_one); |
4e10d12a | 351 | |
d0b8e398 | 352 | void acpi_device_notify(struct device *dev) |
4e10d12a | 353 | { |
9cb32acf | 354 | struct acpi_device *adev; |
11909ca1 | 355 | int ret; |
4e10d12a | 356 | |
f3fd0c8a | 357 | ret = acpi_bind_one(dev, NULL); |
42878a9f | 358 | if (ret) { |
2ef52366 RW |
359 | struct acpi_bus_type *type = acpi_get_bus_type(dev); |
360 | ||
42878a9f RW |
361 | if (!type) |
362 | goto err; | |
e3f02c52 RW |
363 | |
364 | adev = type->find_companion(dev); | |
365 | if (!adev) { | |
42878a9f | 366 | dev_dbg(dev, "ACPI companion not found\n"); |
42878a9f | 367 | goto err; |
11909ca1 | 368 | } |
24dee1fc | 369 | ret = acpi_bind_one(dev, adev); |
11909ca1 | 370 | if (ret) |
42878a9f | 371 | goto err; |
11909ca1 | 372 | |
2ef52366 RW |
373 | if (type->setup) { |
374 | type->setup(dev); | |
375 | goto done; | |
376 | } | |
47954481 | 377 | } else { |
2ef52366 | 378 | adev = ACPI_COMPANION(dev); |
d4f54a18 | 379 | |
2ef52366 RW |
380 | if (dev_is_pci(dev)) { |
381 | pci_acpi_setup(dev, adev); | |
382 | goto done; | |
383 | } else if (dev_is_platform(dev)) { | |
384 | acpi_configure_pmsi_domain(dev); | |
385 | } | |
47954481 | 386 | } |
d4f54a18 | 387 | |
2ef52366 | 388 | if (adev->handler && adev->handler->bind) |
9cb32acf | 389 | adev->handler->bind(dev); |
4e10d12a | 390 | |
2ef52366 | 391 | done: |
42878a9f RW |
392 | acpi_handle_debug(ACPI_HANDLE(dev), "Bound to device %s\n", |
393 | dev_name(dev)); | |
4e10d12a | 394 | |
7d625e5b | 395 | return; |
42878a9f RW |
396 | |
397 | err: | |
398 | dev_dbg(dev, "No ACPI support\n"); | |
4e10d12a DSL |
399 | } |
400 | ||
d0b8e398 | 401 | void acpi_device_notify_remove(struct device *dev) |
4e10d12a | 402 | { |
9cb32acf | 403 | struct acpi_device *adev = ACPI_COMPANION(dev); |
11909ca1 | 404 | |
9cb32acf | 405 | if (!adev) |
7d625e5b | 406 | return; |
9cb32acf | 407 | |
c4d19838 | 408 | if (dev_is_pci(dev)) |
47954481 | 409 | pci_acpi_cleanup(dev, adev); |
9cb32acf RW |
410 | else if (adev->handler && adev->handler->unbind) |
411 | adev->handler->unbind(dev); | |
11909ca1 | 412 | |
4e10d12a | 413 | acpi_unbind_one(dev); |
4e10d12a | 414 | } |