Blame view

drivers/acpi/glue.c 5.87 KB
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
1
2
3
4
5
6
7
8
  /*
   * 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.
   */
214f2c90b   Paul Gortmaker   acpi: add export....
9
  #include <linux/export.h>
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
10
11
12
  #include <linux/init.h>
  #include <linux/list.h>
  #include <linux/device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
13
  #include <linux/slab.h>
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
14
15
  #include <linux/rwsem.h>
  #include <linux/acpi.h>
a192a9580   Len Brown   ACPI: Move defini...
16
  #include "internal.h"
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
17
18
19
20
  #define ACPI_GLUE_DEBUG	0
  #if ACPI_GLUE_DEBUG
  #define DBG(x...) printk(PREFIX x)
  #else
4ebf83c8c   Dave Jones   ACPI: fix empty m...
21
  #define DBG(x...) do { } while(0)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
22
23
24
25
26
27
28
29
30
31
32
33
  #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...
34
35
36
  		printk(KERN_INFO PREFIX "bus type %s registered
  ",
  		       type->bus->name);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
37
38
39
40
  		return 0;
  	}
  	return -ENODEV;
  }
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
41
42
43
44
45
46
47
48
  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...
49
50
51
  		printk(KERN_INFO PREFIX "ACPI bus type %s unregistered
  ",
  		       type->bus->name);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
52
53
54
55
  		return 0;
  	}
  	return -ENODEV;
  }
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
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
85
  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...
86
87
88
  /* Get device's handler per its address under its parent */
  struct acpi_find_child {
  	acpi_handle handle;
439913fff   Lin Ming   ACPI: replace acp...
89
  	u64 address;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
90
91
92
93
94
95
96
  };
  
  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...
97
  	struct acpi_find_child *find = context;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
98

15b8dd53f   Bob Moore   ACPICA: Major upd...
99
  	status = acpi_get_object_info(handle, &info);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
100
  	if (ACPI_SUCCESS(status)) {
108029ff8   Zhao Yakui   ACPI: Add the che...
101
102
  		if ((info->address == find->address)
  			&& (info->valid & ACPI_VALID_ADR))
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
103
  			find->handle = handle;
15b8dd53f   Bob Moore   ACPICA: Major upd...
104
  		kfree(info);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
105
106
107
  	}
  	return AE_OK;
  }
439913fff   Lin Ming   ACPI: replace acp...
108
  acpi_handle acpi_get_child(acpi_handle parent, u64 address)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
109
110
111
112
113
114
  {
  	struct acpi_find_child find = { NULL, address };
  
  	if (!parent)
  		return NULL;
  	acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
2263576cf   Lin Ming   ACPICA: Add post-...
115
  			    1, do_acpi_find_child, NULL, &find, NULL);
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
116
117
118
119
120
121
122
  	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...
123
  				   void *context)
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
  {
  	/* 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...
144
  	struct acpi_device *acpi_dev;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
145
  	acpi_status status;
465ae641e   Benjamin Herrenschmidt   ACPI: Change ACPI...
146
  	if (dev->archdata.acpi_handle) {
fc3a8828b   Greg Kroah-Hartman   driver core: fix ...
147
148
  		dev_warn(dev, "Drivers changed 'acpi_handle'
  ");
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
149
150
151
152
153
154
155
156
  		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...
157
  	dev->archdata.acpi_handle = handle;
4e10d12a3   David Shaohua Li   [ACPI] Bind PCI d...
158

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