Blame view

drivers/acpi/glue.c 5.92 KB
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * Link physical devices with ACPI devices support
   *
   * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com>
   * Copyright (c) 2005 Intel Corp.
   *
   * This file is released under the GPLv2.
   */
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
12
  #include <linux/slab.h>
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
13
14
  #include <linux/rwsem.h>
  #include <linux/acpi.h>
a192a9580   Len Brown   ACPI: Move defini...
15
  #include "internal.h"
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
16
17
18
19
  #define ACPI_GLUE_DEBUG	0
  #if ACPI_GLUE_DEBUG
  #define DBG(x...) printk(PREFIX x)
  #else
4ebf83c8c   Dave Jones   ACPI: fix empty m...
20
  #define DBG(x...) do { } while(0)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
21
22
23
24
25
26
27
28
29
30
31
32
  #endif
  static LIST_HEAD(bus_type_list);
  static DECLARE_RWSEM(bus_type_sem);
  
  int register_acpi_bus_type(struct acpi_bus_type *type)
  {
  	if (acpi_disabled)
  		return -ENODEV;
  	if (type && type->bus && type->find_device) {
  		down_write(&bus_type_sem);
  		list_add_tail(&type->list, &bus_type_list);
  		up_write(&bus_type_sem);
4be44fcd3   Len Brown   [ACPI] Lindent al...
33
34
35
  		printk(KERN_INFO PREFIX "bus type %s registered
  ",
  		       type->bus->name);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
36
37
38
39
  		return 0;
  	}
  	return -ENODEV;
  }
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
40
41
42
43
44
45
46
47
  int unregister_acpi_bus_type(struct acpi_bus_type *type)
  {
  	if (acpi_disabled)
  		return 0;
  	if (type) {
  		down_write(&bus_type_sem);
  		list_del_init(&type->list);
  		up_write(&bus_type_sem);
4be44fcd3   Len Brown   [ACPI] Lindent al...
48
49
50
  		printk(KERN_INFO PREFIX "ACPI bus type %s unregistered
  ",
  		       type->bus->name);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
51
52
53
54
  		return 0;
  	}
  	return -ENODEV;
  }
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
  static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
  {
  	struct acpi_bus_type *tmp, *ret = NULL;
  
  	down_read(&bus_type_sem);
  	list_for_each_entry(tmp, &bus_type_list, list) {
  		if (tmp->bus == type) {
  			ret = tmp;
  			break;
  		}
  	}
  	up_read(&bus_type_sem);
  	return ret;
  }
  
  static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
  {
  	struct acpi_bus_type *tmp;
  	int ret = -ENODEV;
  
  	down_read(&bus_type_sem);
  	list_for_each_entry(tmp, &bus_type_list, list) {
  		if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
  			ret = 0;
  			break;
  		}
  	}
  	up_read(&bus_type_sem);
  	return ret;
  }
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
85
86
87
  /* Get device's handler per its address under its parent */
  struct acpi_find_child {
  	acpi_handle handle;
439913fff   Lin Ming   ACPI: replace acp...
88
  	u64 address;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
89
90
91
92
93
94
95
  };
  
  static acpi_status
  do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv)
  {
  	acpi_status status;
  	struct acpi_device_info *info;
50dd09697   Jan Engelhardt   ACPI: Remove unne...
96
  	struct acpi_find_child *find = context;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
97

15b8dd53f   Bob Moore   ACPICA: Major upd...
98
  	status = acpi_get_object_info(handle, &info);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
99
  	if (ACPI_SUCCESS(status)) {
108029ff8   Zhao Yakui   ACPI: Add the che...
100
101
  		if ((info->address == find->address)
  			&& (info->valid & ACPI_VALID_ADR))
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
102
  			find->handle = handle;
15b8dd53f   Bob Moore   ACPICA: Major upd...
103
  		kfree(info);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
104
105
106
  	}
  	return AE_OK;
  }
439913fff   Lin Ming   ACPI: replace acp...
107
  acpi_handle acpi_get_child(acpi_handle parent, u64 address)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
108
109
110
111
112
113
  {
  	struct acpi_find_child find = { NULL, address };
  
  	if (!parent)
  		return NULL;
  	acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
2263576cf   Lin Ming   ACPICA: Add post-...
114
  			    1, do_acpi_find_child, NULL, &find, NULL);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
115
116
117
118
119
120
121
  	return find.handle;
  }
  
  EXPORT_SYMBOL(acpi_get_child);
  
  /* Link ACPI devices with physical devices */
  static void acpi_glue_data_handler(acpi_handle handle,
8e4319c42   Bob Moore   ACPICA: Fix sever...
122
  				   void *context)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
  {
  	/* we provide an empty handler */
  }
  
  /* Note: a success call will increase reference count by one */
  struct device *acpi_get_physical_device(acpi_handle handle)
  {
  	acpi_status status;
  	struct device *dev;
  
  	status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev);
  	if (ACPI_SUCCESS(status))
  		return get_device(dev);
  	return NULL;
  }
  
  EXPORT_SYMBOL(acpi_get_physical_device);
  
  static int acpi_bind_one(struct device *dev, acpi_handle handle)
  {
1071695f1   David Brownell   ACPI: crosslink A...
143
  	struct acpi_device *acpi_dev;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
144
  	acpi_status status;
465ae641e   Benjamin Herrenschmidt   ACPI: Change ACPI...
145
  	if (dev->archdata.acpi_handle) {
fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
146
147
  		dev_warn(dev, "Drivers changed 'acpi_handle'
  ");
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
148
149
150
151
152
153
154
155
  		return -EINVAL;
  	}
  	get_device(dev);
  	status = acpi_attach_data(handle, acpi_glue_data_handler, dev);
  	if (ACPI_FAILURE(status)) {
  		put_device(dev);
  		return -EINVAL;
  	}
465ae641e   Benjamin Herrenschmidt   ACPI: Change ACPI...
156
  	dev->archdata.acpi_handle = handle;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
157

1071695f1   David Brownell   ACPI: crosslink A...
158
159
160
161
162
163
164
165
  	status = acpi_bus_get_device(handle, &acpi_dev);
  	if (!ACPI_FAILURE(status)) {
  		int ret;
  
  		ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
  				"firmware_node");
  		ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
  				"physical_node");
76acae04c   Rafael J. Wysocki   ACPI: Make /proc/...
166
  		if (acpi_dev->wakeup.flags.valid) {
eb9d0fe40   Rafael J. Wysocki   PCI ACPI: Rework ...
167
  			device_set_wakeup_capable(dev, true);
76acae04c   Rafael J. Wysocki   ACPI: Make /proc/...
168
169
170
  			device_set_wakeup_enable(dev,
  						acpi_dev->wakeup.state.enabled);
  		}
1071695f1   David Brownell   ACPI: crosslink A...
171
  	}
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
172
173
174
175
176
  	return 0;
  }
  
  static int acpi_unbind_one(struct device *dev)
  {
465ae641e   Benjamin Herrenschmidt   ACPI: Change ACPI...
177
  	if (!dev->archdata.acpi_handle)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
178
  		return 0;
465ae641e   Benjamin Herrenschmidt   ACPI: Change ACPI...
179
  	if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
1071695f1   David Brownell   ACPI: crosslink A...
180
  		struct acpi_device *acpi_dev;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
181
182
  		/* acpi_get_physical_device increase refcnt by one */
  		put_device(dev);
1071695f1   David Brownell   ACPI: crosslink A...
183
184
185
186
187
188
  
  		if (!acpi_bus_get_device(dev->archdata.acpi_handle,
  					&acpi_dev)) {
  			sysfs_remove_link(&dev->kobj, "firmware_node");
  			sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node");
  		}
465ae641e   Benjamin Herrenschmidt   ACPI: Change ACPI...
189
190
191
  		acpi_detach_data(dev->archdata.acpi_handle,
  				 acpi_glue_data_handler);
  		dev->archdata.acpi_handle = NULL;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
192
193
194
  		/* acpi_bind_one increase refcnt by one */
  		put_device(dev);
  	} else {
fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
195
196
  		dev_err(dev, "Oops, 'acpi_handle' corrupt
  ");
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  	}
  	return 0;
  }
  
  static int acpi_platform_notify(struct device *dev)
  {
  	struct acpi_bus_type *type;
  	acpi_handle handle;
  	int ret = -EINVAL;
  
  	if (!dev->bus || !dev->parent) {
  		/* bridge devices genernally haven't bus or parent */
  		ret = acpi_find_bridge_device(dev, &handle);
  		goto end;
  	}
  	type = acpi_get_bus_type(dev->bus);
  	if (!type) {
db1461ad4   Kay Sievers   ACPI: struct devi...
214
215
  		DBG("No ACPI bus support for %s
  ", dev_name(dev));
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
216
217
218
219
  		ret = -EINVAL;
  		goto end;
  	}
  	if ((ret = type->find_device(dev, &handle)) != 0)
db1461ad4   Kay Sievers   ACPI: struct devi...
220
221
  		DBG("Can't get handler for %s
  ", dev_name(dev));
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
222
223
224
225
226
227
228
        end:
  	if (!ret)
  		acpi_bind_one(dev, handle);
  
  #if ACPI_GLUE_DEBUG
  	if (!ret) {
  		struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
465ae641e   Benjamin Herrenschmidt   ACPI: Change ACPI...
229
230
  		acpi_get_name(dev->archdata.acpi_handle,
  			      ACPI_FULL_PATHNAME, &buffer);
db1461ad4   Kay Sievers   ACPI: struct devi...
231
232
  		DBG("Device %s -> %s
  ", dev_name(dev), (char *)buffer.pointer);
02438d877   Len Brown   ACPI: delete acpi...
233
  		kfree(buffer.pointer);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
234
  	} else
db1461ad4   Kay Sievers   ACPI: struct devi...
235
236
  		DBG("Device %s -> No ACPI support
  ", dev_name(dev));
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
237
238
239
240
241
242
243
244
245
246
  #endif
  
  	return ret;
  }
  
  static int acpi_platform_notify_remove(struct device *dev)
  {
  	acpi_unbind_one(dev);
  	return 0;
  }
0e46517d9   Bjorn Helgaas   ACPI: call init_a...
247
  int __init init_acpi_device_notify(void)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
248
  {
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
249
250
251
252
253
254
255
256
257
  	if (platform_notify || platform_notify_remove) {
  		printk(KERN_ERR PREFIX "Can't use platform_notify
  ");
  		return 0;
  	}
  	platform_notify = acpi_platform_notify;
  	platform_notify_remove = acpi_platform_notify_remove;
  	return 0;
  }