Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/acpi/glue.c
15109 views
1
/*
2
* Link physical devices with ACPI devices support
3
*
4
* Copyright (c) 2005 David Shaohua Li <[email protected]>
5
* Copyright (c) 2005 Intel Corp.
6
*
7
* This file is released under the GPLv2.
8
*/
9
#include <linux/init.h>
10
#include <linux/list.h>
11
#include <linux/device.h>
12
#include <linux/slab.h>
13
#include <linux/rwsem.h>
14
#include <linux/acpi.h>
15
16
#include "internal.h"
17
18
#define ACPI_GLUE_DEBUG 0
19
#if ACPI_GLUE_DEBUG
20
#define DBG(x...) printk(PREFIX x)
21
#else
22
#define DBG(x...) do { } while(0)
23
#endif
24
static LIST_HEAD(bus_type_list);
25
static DECLARE_RWSEM(bus_type_sem);
26
27
int register_acpi_bus_type(struct acpi_bus_type *type)
28
{
29
if (acpi_disabled)
30
return -ENODEV;
31
if (type && type->bus && type->find_device) {
32
down_write(&bus_type_sem);
33
list_add_tail(&type->list, &bus_type_list);
34
up_write(&bus_type_sem);
35
printk(KERN_INFO PREFIX "bus type %s registered\n",
36
type->bus->name);
37
return 0;
38
}
39
return -ENODEV;
40
}
41
42
int unregister_acpi_bus_type(struct acpi_bus_type *type)
43
{
44
if (acpi_disabled)
45
return 0;
46
if (type) {
47
down_write(&bus_type_sem);
48
list_del_init(&type->list);
49
up_write(&bus_type_sem);
50
printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n",
51
type->bus->name);
52
return 0;
53
}
54
return -ENODEV;
55
}
56
57
static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
58
{
59
struct acpi_bus_type *tmp, *ret = NULL;
60
61
down_read(&bus_type_sem);
62
list_for_each_entry(tmp, &bus_type_list, list) {
63
if (tmp->bus == type) {
64
ret = tmp;
65
break;
66
}
67
}
68
up_read(&bus_type_sem);
69
return ret;
70
}
71
72
static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
73
{
74
struct acpi_bus_type *tmp;
75
int ret = -ENODEV;
76
77
down_read(&bus_type_sem);
78
list_for_each_entry(tmp, &bus_type_list, list) {
79
if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
80
ret = 0;
81
break;
82
}
83
}
84
up_read(&bus_type_sem);
85
return ret;
86
}
87
88
/* Get device's handler per its address under its parent */
89
struct acpi_find_child {
90
acpi_handle handle;
91
u64 address;
92
};
93
94
static acpi_status
95
do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv)
96
{
97
acpi_status status;
98
struct acpi_device_info *info;
99
struct acpi_find_child *find = context;
100
101
status = acpi_get_object_info(handle, &info);
102
if (ACPI_SUCCESS(status)) {
103
if ((info->address == find->address)
104
&& (info->valid & ACPI_VALID_ADR))
105
find->handle = handle;
106
kfree(info);
107
}
108
return AE_OK;
109
}
110
111
acpi_handle acpi_get_child(acpi_handle parent, u64 address)
112
{
113
struct acpi_find_child find = { NULL, address };
114
115
if (!parent)
116
return NULL;
117
acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
118
1, do_acpi_find_child, NULL, &find, NULL);
119
return find.handle;
120
}
121
122
EXPORT_SYMBOL(acpi_get_child);
123
124
/* Link ACPI devices with physical devices */
125
static void acpi_glue_data_handler(acpi_handle handle,
126
void *context)
127
{
128
/* we provide an empty handler */
129
}
130
131
/* Note: a success call will increase reference count by one */
132
struct device *acpi_get_physical_device(acpi_handle handle)
133
{
134
acpi_status status;
135
struct device *dev;
136
137
status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev);
138
if (ACPI_SUCCESS(status))
139
return get_device(dev);
140
return NULL;
141
}
142
143
EXPORT_SYMBOL(acpi_get_physical_device);
144
145
static int acpi_bind_one(struct device *dev, acpi_handle handle)
146
{
147
struct acpi_device *acpi_dev;
148
acpi_status status;
149
150
if (dev->archdata.acpi_handle) {
151
dev_warn(dev, "Drivers changed 'acpi_handle'\n");
152
return -EINVAL;
153
}
154
get_device(dev);
155
status = acpi_attach_data(handle, acpi_glue_data_handler, dev);
156
if (ACPI_FAILURE(status)) {
157
put_device(dev);
158
return -EINVAL;
159
}
160
dev->archdata.acpi_handle = handle;
161
162
status = acpi_bus_get_device(handle, &acpi_dev);
163
if (!ACPI_FAILURE(status)) {
164
int ret;
165
166
ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
167
"firmware_node");
168
ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
169
"physical_node");
170
if (acpi_dev->wakeup.flags.valid)
171
device_set_wakeup_capable(dev, true);
172
}
173
174
return 0;
175
}
176
177
static int acpi_unbind_one(struct device *dev)
178
{
179
if (!dev->archdata.acpi_handle)
180
return 0;
181
if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
182
struct acpi_device *acpi_dev;
183
184
/* acpi_get_physical_device increase refcnt by one */
185
put_device(dev);
186
187
if (!acpi_bus_get_device(dev->archdata.acpi_handle,
188
&acpi_dev)) {
189
sysfs_remove_link(&dev->kobj, "firmware_node");
190
sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node");
191
}
192
193
acpi_detach_data(dev->archdata.acpi_handle,
194
acpi_glue_data_handler);
195
dev->archdata.acpi_handle = NULL;
196
/* acpi_bind_one increase refcnt by one */
197
put_device(dev);
198
} else {
199
dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
200
}
201
return 0;
202
}
203
204
static int acpi_platform_notify(struct device *dev)
205
{
206
struct acpi_bus_type *type;
207
acpi_handle handle;
208
int ret = -EINVAL;
209
210
if (!dev->bus || !dev->parent) {
211
/* bridge devices genernally haven't bus or parent */
212
ret = acpi_find_bridge_device(dev, &handle);
213
goto end;
214
}
215
type = acpi_get_bus_type(dev->bus);
216
if (!type) {
217
DBG("No ACPI bus support for %s\n", dev_name(dev));
218
ret = -EINVAL;
219
goto end;
220
}
221
if ((ret = type->find_device(dev, &handle)) != 0)
222
DBG("Can't get handler for %s\n", dev_name(dev));
223
end:
224
if (!ret)
225
acpi_bind_one(dev, handle);
226
227
#if ACPI_GLUE_DEBUG
228
if (!ret) {
229
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
230
231
acpi_get_name(dev->archdata.acpi_handle,
232
ACPI_FULL_PATHNAME, &buffer);
233
DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
234
kfree(buffer.pointer);
235
} else
236
DBG("Device %s -> No ACPI support\n", dev_name(dev));
237
#endif
238
239
return ret;
240
}
241
242
static int acpi_platform_notify_remove(struct device *dev)
243
{
244
acpi_unbind_one(dev);
245
return 0;
246
}
247
248
int __init init_acpi_device_notify(void)
249
{
250
if (platform_notify || platform_notify_remove) {
251
printk(KERN_ERR PREFIX "Can't use platform_notify\n");
252
return 0;
253
}
254
platform_notify = acpi_platform_notify;
255
platform_notify_remove = acpi_platform_notify_remove;
256
return 0;
257
}
258
259