diff options
Diffstat (limited to 'drivers/acpi/glue.c')
-rw-r--r-- | drivers/acpi/glue.c | 135 |
1 files changed, 86 insertions, 49 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 243ee85e4d2e..d1a2d74033e9 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c | |||
@@ -25,6 +25,8 @@ | |||
25 | static LIST_HEAD(bus_type_list); | 25 | static LIST_HEAD(bus_type_list); |
26 | static DECLARE_RWSEM(bus_type_sem); | 26 | static DECLARE_RWSEM(bus_type_sem); |
27 | 27 | ||
28 | #define PHYSICAL_NODE_STRING "physical_node" | ||
29 | |||
28 | int register_acpi_bus_type(struct acpi_bus_type *type) | 30 | int register_acpi_bus_type(struct acpi_bus_type *type) |
29 | { | 31 | { |
30 | if (acpi_disabled) | 32 | if (acpi_disabled) |
@@ -124,84 +126,119 @@ acpi_handle acpi_get_child(acpi_handle parent, u64 address) | |||
124 | 126 | ||
125 | EXPORT_SYMBOL(acpi_get_child); | 127 | EXPORT_SYMBOL(acpi_get_child); |
126 | 128 | ||
127 | /* Link ACPI devices with physical devices */ | ||
128 | static void acpi_glue_data_handler(acpi_handle handle, | ||
129 | void *context) | ||
130 | { | ||
131 | /* we provide an empty handler */ | ||
132 | } | ||
133 | |||
134 | /* Note: a success call will increase reference count by one */ | ||
135 | struct device *acpi_get_physical_device(acpi_handle handle) | ||
136 | { | ||
137 | acpi_status status; | ||
138 | struct device *dev; | ||
139 | |||
140 | status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev); | ||
141 | if (ACPI_SUCCESS(status)) | ||
142 | return get_device(dev); | ||
143 | return NULL; | ||
144 | } | ||
145 | |||
146 | EXPORT_SYMBOL(acpi_get_physical_device); | ||
147 | |||
148 | static int acpi_bind_one(struct device *dev, acpi_handle handle) | 129 | static int acpi_bind_one(struct device *dev, acpi_handle handle) |
149 | { | 130 | { |
150 | struct acpi_device *acpi_dev; | 131 | struct acpi_device *acpi_dev; |
151 | acpi_status status; | 132 | acpi_status status; |
133 | struct acpi_device_physical_node *physical_node; | ||
134 | char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; | ||
135 | int retval = -EINVAL; | ||
152 | 136 | ||
153 | if (dev->archdata.acpi_handle) { | 137 | if (dev->archdata.acpi_handle) { |
154 | dev_warn(dev, "Drivers changed 'acpi_handle'\n"); | 138 | dev_warn(dev, "Drivers changed 'acpi_handle'\n"); |
155 | return -EINVAL; | 139 | return -EINVAL; |
156 | } | 140 | } |
141 | |||
157 | get_device(dev); | 142 | get_device(dev); |
158 | status = acpi_attach_data(handle, acpi_glue_data_handler, dev); | 143 | status = acpi_bus_get_device(handle, &acpi_dev); |
159 | if (ACPI_FAILURE(status)) { | 144 | if (ACPI_FAILURE(status)) |
160 | put_device(dev); | 145 | goto err; |
161 | return -EINVAL; | 146 | |
147 | physical_node = kzalloc(sizeof(struct acpi_device_physical_node), | ||
148 | GFP_KERNEL); | ||
149 | if (!physical_node) { | ||
150 | retval = -ENOMEM; | ||
151 | goto err; | ||
162 | } | 152 | } |
163 | dev->archdata.acpi_handle = handle; | ||
164 | 153 | ||
165 | status = acpi_bus_get_device(handle, &acpi_dev); | 154 | mutex_lock(&acpi_dev->physical_node_lock); |
166 | if (!ACPI_FAILURE(status)) { | 155 | /* allocate physical node id according to physical_node_id_bitmap */ |
167 | int ret; | 156 | physical_node->node_id = |
168 | 157 | find_first_zero_bit(acpi_dev->physical_node_id_bitmap, | |
169 | ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, | 158 | ACPI_MAX_PHYSICAL_NODE); |
170 | "firmware_node"); | 159 | if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) { |
171 | ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, | 160 | retval = -ENOSPC; |
172 | "physical_node"); | 161 | mutex_unlock(&acpi_dev->physical_node_lock); |
173 | if (acpi_dev->wakeup.flags.valid) | 162 | goto err; |
174 | device_set_wakeup_capable(dev, true); | ||
175 | } | 163 | } |
176 | 164 | ||
165 | set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap); | ||
166 | physical_node->dev = dev; | ||
167 | list_add_tail(&physical_node->node, &acpi_dev->physical_node_list); | ||
168 | acpi_dev->physical_node_count++; | ||
169 | mutex_unlock(&acpi_dev->physical_node_lock); | ||
170 | |||
171 | dev->archdata.acpi_handle = handle; | ||
172 | |||
173 | if (!physical_node->node_id) | ||
174 | strcpy(physical_node_name, PHYSICAL_NODE_STRING); | ||
175 | else | ||
176 | sprintf(physical_node_name, | ||
177 | "physical_node%d", physical_node->node_id); | ||
178 | retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, | ||
179 | physical_node_name); | ||
180 | retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, | ||
181 | "firmware_node"); | ||
182 | |||
183 | if (acpi_dev->wakeup.flags.valid) | ||
184 | device_set_wakeup_capable(dev, true); | ||
185 | |||
177 | return 0; | 186 | return 0; |
187 | |||
188 | err: | ||
189 | put_device(dev); | ||
190 | return retval; | ||
178 | } | 191 | } |
179 | 192 | ||
180 | static int acpi_unbind_one(struct device *dev) | 193 | static int acpi_unbind_one(struct device *dev) |
181 | { | 194 | { |
195 | struct acpi_device_physical_node *entry; | ||
196 | struct acpi_device *acpi_dev; | ||
197 | acpi_status status; | ||
198 | struct list_head *node, *next; | ||
199 | |||
182 | if (!dev->archdata.acpi_handle) | 200 | if (!dev->archdata.acpi_handle) |
183 | return 0; | 201 | return 0; |
184 | if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) { | ||
185 | struct acpi_device *acpi_dev; | ||
186 | 202 | ||
187 | /* acpi_get_physical_device increase refcnt by one */ | 203 | status = acpi_bus_get_device(dev->archdata.acpi_handle, |
188 | put_device(dev); | 204 | &acpi_dev); |
205 | if (ACPI_FAILURE(status)) | ||
206 | goto err; | ||
189 | 207 | ||
190 | if (!acpi_bus_get_device(dev->archdata.acpi_handle, | 208 | mutex_lock(&acpi_dev->physical_node_lock); |
191 | &acpi_dev)) { | 209 | list_for_each_safe(node, next, &acpi_dev->physical_node_list) { |
192 | sysfs_remove_link(&dev->kobj, "firmware_node"); | 210 | char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; |
193 | sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node"); | 211 | |
194 | } | 212 | entry = list_entry(node, struct acpi_device_physical_node, |
213 | node); | ||
214 | if (entry->dev != dev) | ||
215 | continue; | ||
216 | |||
217 | list_del(node); | ||
218 | clear_bit(entry->node_id, acpi_dev->physical_node_id_bitmap); | ||
195 | 219 | ||
196 | acpi_detach_data(dev->archdata.acpi_handle, | 220 | acpi_dev->physical_node_count--; |
197 | acpi_glue_data_handler); | 221 | |
222 | if (!entry->node_id) | ||
223 | strcpy(physical_node_name, PHYSICAL_NODE_STRING); | ||
224 | else | ||
225 | sprintf(physical_node_name, | ||
226 | "physical_node%d", entry->node_id); | ||
227 | |||
228 | sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name); | ||
229 | sysfs_remove_link(&dev->kobj, "firmware_node"); | ||
198 | dev->archdata.acpi_handle = NULL; | 230 | dev->archdata.acpi_handle = NULL; |
199 | /* acpi_bind_one increase refcnt by one */ | 231 | /* acpi_bind_one increase refcnt by one */ |
200 | put_device(dev); | 232 | put_device(dev); |
201 | } else { | 233 | kfree(entry); |
202 | dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); | ||
203 | } | 234 | } |
235 | mutex_unlock(&acpi_dev->physical_node_lock); | ||
236 | |||
204 | return 0; | 237 | return 0; |
238 | |||
239 | err: | ||
240 | dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); | ||
241 | return -EINVAL; | ||
205 | } | 242 | } |
206 | 243 | ||
207 | static int acpi_platform_notify(struct device *dev) | 244 | static int acpi_platform_notify(struct device *dev) |