aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/glue.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/glue.c')
-rw-r--r--drivers/acpi/glue.c133
1 files changed, 101 insertions, 32 deletions
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index f68095756fb7..408f6b2a5fa8 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -31,6 +31,7 @@ static LIST_HEAD(bus_type_list);
31static DECLARE_RWSEM(bus_type_sem); 31static DECLARE_RWSEM(bus_type_sem);
32 32
33#define PHYSICAL_NODE_STRING "physical_node" 33#define PHYSICAL_NODE_STRING "physical_node"
34#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)
34 35
35int register_acpi_bus_type(struct acpi_bus_type *type) 36int register_acpi_bus_type(struct acpi_bus_type *type)
36{ 37{
@@ -78,41 +79,108 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
78 return ret; 79 return ret;
79} 80}
80 81
81static acpi_status do_acpi_find_child(acpi_handle handle, u32 lvl_not_used, 82static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
82 void *addr_p, void **ret_p) 83 void *not_used, void **ret_p)
83{ 84{
84 unsigned long long addr, sta; 85 struct acpi_device *adev = NULL;
85 acpi_status status;
86 86
87 status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr); 87 acpi_bus_get_device(handle, &adev);
88 if (ACPI_SUCCESS(status) && addr == *((u64 *)addr_p)) { 88 if (adev) {
89 *ret_p = handle; 89 *ret_p = handle;
90 status = acpi_bus_get_status_handle(handle, &sta); 90 return AE_CTRL_TERMINATE;
91 if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_ENABLED))
92 return AE_CTRL_TERMINATE;
93 } 91 }
94 return AE_OK; 92 return AE_OK;
95} 93}
96 94
97acpi_handle acpi_get_child(acpi_handle parent, u64 address) 95static bool acpi_extra_checks_passed(acpi_handle handle, bool is_bridge)
98{ 96{
99 void *ret = NULL; 97 unsigned long long sta;
98 acpi_status status;
99
100 status = acpi_bus_get_status_handle(handle, &sta);
101 if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
102 return false;
103
104 if (is_bridge) {
105 void *test = NULL;
106
107 /* Check if this object has at least one child device. */
108 acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
109 acpi_dev_present, NULL, NULL, &test);
110 return !!test;
111 }
112 return true;
113}
114
115struct find_child_context {
116 u64 addr;
117 bool is_bridge;
118 acpi_handle ret;
119 bool ret_checked;
120};
121
122static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
123 void *data, void **not_used)
124{
125 struct find_child_context *context = data;
126 unsigned long long addr;
127 acpi_status status;
100 128
101 if (!parent) 129 status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
102 return NULL; 130 if (ACPI_FAILURE(status) || addr != context->addr)
131 return AE_OK;
103 132
104 acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, NULL, 133 if (!context->ret) {
105 do_acpi_find_child, &address, &ret); 134 /* This is the first matching object. Save its handle. */
106 return (acpi_handle)ret; 135 context->ret = handle;
136 return AE_OK;
137 }
138 /*
139 * There is more than one matching object with the same _ADR value.
140 * That really is unexpected, so we are kind of beyond the scope of the
141 * spec here. We have to choose which one to return, though.
142 *
143 * First, check if the previously found object is good enough and return
144 * its handle if so. Second, check the same for the object that we've
145 * just found.
146 */
147 if (!context->ret_checked) {
148 if (acpi_extra_checks_passed(context->ret, context->is_bridge))
149 return AE_CTRL_TERMINATE;
150 else
151 context->ret_checked = true;
152 }
153 if (acpi_extra_checks_passed(handle, context->is_bridge)) {
154 context->ret = handle;
155 return AE_CTRL_TERMINATE;
156 }
157 return AE_OK;
107} 158}
108EXPORT_SYMBOL(acpi_get_child); 159
160acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge)
161{
162 if (parent) {
163 struct find_child_context context = {
164 .addr = addr,
165 .is_bridge = is_bridge,
166 };
167
168 acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child,
169 NULL, &context, NULL);
170 return context.ret;
171 }
172 return NULL;
173}
174EXPORT_SYMBOL_GPL(acpi_find_child);
109 175
110int acpi_bind_one(struct device *dev, acpi_handle handle) 176int acpi_bind_one(struct device *dev, acpi_handle handle)
111{ 177{
112 struct acpi_device *acpi_dev; 178 struct acpi_device *acpi_dev;
113 acpi_status status; 179 acpi_status status;
114 struct acpi_device_physical_node *physical_node, *pn; 180 struct acpi_device_physical_node *physical_node, *pn;
115 char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; 181 char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
182 struct list_head *physnode_list;
183 unsigned int node_id;
116 int retval = -EINVAL; 184 int retval = -EINVAL;
117 185
118 if (ACPI_HANDLE(dev)) { 186 if (ACPI_HANDLE(dev)) {
@@ -139,25 +207,27 @@ int acpi_bind_one(struct device *dev, acpi_handle handle)
139 207
140 mutex_lock(&acpi_dev->physical_node_lock); 208 mutex_lock(&acpi_dev->physical_node_lock);
141 209
142 /* Sanity check. */ 210 /*
143 list_for_each_entry(pn, &acpi_dev->physical_node_list, node) 211 * Keep the list sorted by node_id so that the IDs of removed nodes can
212 * be recycled easily.
213 */
214 physnode_list = &acpi_dev->physical_node_list;
215 node_id = 0;
216 list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
217 /* Sanity check. */
144 if (pn->dev == dev) { 218 if (pn->dev == dev) {
145 dev_warn(dev, "Already associated with ACPI node\n"); 219 dev_warn(dev, "Already associated with ACPI node\n");
146 goto err_free; 220 goto err_free;
147 } 221 }
148 222 if (pn->node_id == node_id) {
149 /* allocate physical node id according to physical_node_id_bitmap */ 223 physnode_list = &pn->node;
150 physical_node->node_id = 224 node_id++;
151 find_first_zero_bit(acpi_dev->physical_node_id_bitmap, 225 }
152 ACPI_MAX_PHYSICAL_NODE);
153 if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) {
154 retval = -ENOSPC;
155 goto err_free;
156 } 226 }
157 227
158 set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap); 228 physical_node->node_id = node_id;
159 physical_node->dev = dev; 229 physical_node->dev = dev;
160 list_add_tail(&physical_node->node, &acpi_dev->physical_node_list); 230 list_add(&physical_node->node, physnode_list);
161 acpi_dev->physical_node_count++; 231 acpi_dev->physical_node_count++;
162 232
163 mutex_unlock(&acpi_dev->physical_node_lock); 233 mutex_unlock(&acpi_dev->physical_node_lock);
@@ -208,7 +278,7 @@ int acpi_unbind_one(struct device *dev)
208 278
209 mutex_lock(&acpi_dev->physical_node_lock); 279 mutex_lock(&acpi_dev->physical_node_lock);
210 list_for_each_safe(node, next, &acpi_dev->physical_node_list) { 280 list_for_each_safe(node, next, &acpi_dev->physical_node_list) {
211 char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; 281 char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
212 282
213 entry = list_entry(node, struct acpi_device_physical_node, 283 entry = list_entry(node, struct acpi_device_physical_node,
214 node); 284 node);
@@ -216,7 +286,6 @@ int acpi_unbind_one(struct device *dev)
216 continue; 286 continue;
217 287
218 list_del(node); 288 list_del(node);
219 clear_bit(entry->node_id, acpi_dev->physical_node_id_bitmap);
220 289
221 acpi_dev->physical_node_count--; 290 acpi_dev->physical_node_count--;
222 291