glue.c 8.5 KB
Newer Older
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.
 */
9
#include <linux/export.h>
10 11 12
#include <linux/init.h>
#include <linux/list.h>
#include <linux/device.h>
13
#include <linux/slab.h>
14 15 16
#include <linux/rwsem.h>
#include <linux/acpi.h>

17 18
#include "internal.h"

19 20
#define ACPI_GLUE_DEBUG	0
#if ACPI_GLUE_DEBUG
21 22
#define DBG(fmt, ...)						\
	printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__)
23
#else
24 25 26 27 28
#define DBG(fmt, ...)						\
do {								\
	if (0)							\
		printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__);	\
} while (0)
29 30 31 32
#endif
static LIST_HEAD(bus_type_list);
static DECLARE_RWSEM(bus_type_sem);

33
#define PHYSICAL_NODE_STRING "physical_node"
34
#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)
35

36 37 38 39
int register_acpi_bus_type(struct acpi_bus_type *type)
{
	if (acpi_disabled)
		return -ENODEV;
40
	if (type && type->match && type->find_companion) {
41 42 43
		down_write(&bus_type_sem);
		list_add_tail(&type->list, &bus_type_list);
		up_write(&bus_type_sem);
44
		printk(KERN_INFO PREFIX "bus type %s registered\n", type->name);
45 46 47 48
		return 0;
	}
	return -ENODEV;
}
49
EXPORT_SYMBOL_GPL(register_acpi_bus_type);
50 51 52 53 54 55 56 57 58

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);
59 60
		printk(KERN_INFO PREFIX "bus type %s unregistered\n",
		       type->name);
61 62 63 64
		return 0;
	}
	return -ENODEV;
}
65
EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
66

67
static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
68 69 70 71 72
{
	struct acpi_bus_type *tmp, *ret = NULL;

	down_read(&bus_type_sem);
	list_for_each_entry(tmp, &bus_type_list, list) {
73
		if (tmp->match(dev)) {
74 75 76 77 78 79 80 81
			ret = tmp;
			break;
		}
	}
	up_read(&bus_type_sem);
	return ret;
}

82 83 84
#define FIND_CHILD_MIN_SCORE	1
#define FIND_CHILD_MAX_SCORE	2

85
static int find_child_checks(struct acpi_device *adev, bool check_children)
86
{
87
	bool sta_present = true;
88 89 90
	unsigned long long sta;
	acpi_status status;

91
	status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
92 93 94 95
	if (status == AE_NOT_FOUND)
		sta_present = false;
	else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
		return -ENODEV;
96

97 98
	if (check_children && list_empty(&adev->children))
		return -ENODEV;
99

100
	return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
101 102
}

103 104
struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
					   u64 address, bool check_children)
105
{
106 107 108
	struct acpi_device *adev, *ret = NULL;
	int ret_score = 0;

109 110 111
	if (!parent)
		return NULL;

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
	list_for_each_entry(adev, &parent->children, node) {
		unsigned long long addr;
		acpi_status status;
		int score;

		status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR,
					       NULL, &addr);
		if (ACPI_FAILURE(status) || addr != address)
			continue;

		if (!ret) {
			/* This is the first matching object.  Save it. */
			ret = adev;
			continue;
		}
		/*
		 * There is more than one matching device object with the same
		 * _ADR value.  That really is unexpected, so we are kind of
		 * beyond the scope of the spec here.  We have to choose which
		 * one to return, though.
		 *
		 * First, check if the previously found object is good enough
		 * and return it if so.  Second, do the same for the object that
		 * we've just found.
		 */
		if (!ret_score) {
			ret_score = find_child_checks(ret, check_children);
			if (ret_score == FIND_CHILD_MAX_SCORE)
				return ret;
		}
		score = find_child_checks(adev, check_children);
		if (score == FIND_CHILD_MAX_SCORE) {
			return adev;
		} else if (score > ret_score) {
			ret = adev;
			ret_score = score;
		}
149
	}
150
	return ret;
151
}
152
EXPORT_SYMBOL_GPL(acpi_find_child_device);
153

154
acpi_handle acpi_get_child(acpi_handle handle, u64 addr)
155
{
156 157 158 159 160
	struct acpi_device *adev;

	if (!handle || acpi_bus_get_device(handle, &adev))
		return NULL;

161
	adev = acpi_find_child_device(adev, addr, false);
162
	return adev ? adev->handle : NULL;
163
}
164
EXPORT_SYMBOL_GPL(acpi_get_child);
165

166 167 168 169 170 171 172 173 174
static void acpi_physnode_link_name(char *buf, unsigned int node_id)
{
	if (node_id > 0)
		snprintf(buf, PHYSICAL_NODE_NAME_SIZE,
			 PHYSICAL_NODE_STRING "%u", node_id);
	else
		strcpy(buf, PHYSICAL_NODE_STRING);
}

175
int acpi_bind_one(struct device *dev, acpi_handle handle)
176
{
177
	struct acpi_device *acpi_dev = NULL;
178
	struct acpi_device_physical_node *physical_node, *pn;
179 180 181
	char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
	struct list_head *physnode_list;
	unsigned int node_id;
182
	int retval = -EINVAL;
183

184
	if (ACPI_COMPANION(dev)) {
185
		if (handle) {
186
			dev_warn(dev, "ACPI companion already set\n");
187 188
			return -EINVAL;
		} else {
189
			acpi_dev = ACPI_COMPANION(dev);
190
		}
191 192
	} else {
		acpi_bus_get_device(handle, &acpi_dev);
193
	}
194
	if (!acpi_dev)
195
		return -EINVAL;
196

197
	get_device(&acpi_dev->dev);
198
	get_device(dev);
199
	physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
200 201 202
	if (!physical_node) {
		retval = -ENOMEM;
		goto err;
203 204
	}

205
	mutex_lock(&acpi_dev->physical_node_lock);
206

207 208 209 210 211 212 213 214
	/*
	 * Keep the list sorted by node_id so that the IDs of removed nodes can
	 * be recycled easily.
	 */
	physnode_list = &acpi_dev->physical_node_list;
	node_id = 0;
	list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
		/* Sanity check. */
215
		if (pn->dev == dev) {
216 217
			mutex_unlock(&acpi_dev->physical_node_lock);

218
			dev_warn(dev, "Already associated with ACPI node\n");
219
			kfree(physical_node);
220
			if (ACPI_COMPANION(dev) != acpi_dev)
221
				goto err;
222

223
			put_device(dev);
224
			put_device(&acpi_dev->dev);
225
			return 0;
226
		}
227 228 229 230
		if (pn->node_id == node_id) {
			physnode_list = &pn->node;
			node_id++;
		}
231 232
	}

233
	physical_node->node_id = node_id;
234
	physical_node->dev = dev;
235
	list_add(&physical_node->node, physnode_list);
236
	acpi_dev->physical_node_count++;
237

238 239
	if (!ACPI_COMPANION(dev))
		ACPI_COMPANION_SET(dev, acpi_dev);
240

241
	acpi_physnode_link_name(physical_node_name, node_id);
242
	retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
243
				   physical_node_name);
244 245 246 247
	if (retval)
		dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n",
			physical_node_name, retval);

248
	retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
249
				   "firmware_node");
250 251 252
	if (retval)
		dev_err(dev, "Failed to create link firmware_node (%d)\n",
			retval);
253

254 255
	mutex_unlock(&acpi_dev->physical_node_lock);

256 257 258
	if (acpi_dev->wakeup.flags.valid)
		device_set_wakeup_capable(dev, true);

259
	return 0;
260 261

 err:
262
	ACPI_COMPANION_SET(dev, NULL);
263
	put_device(dev);
264
	put_device(&acpi_dev->dev);
265
	return retval;
266
}
267
EXPORT_SYMBOL_GPL(acpi_bind_one);
268

269
int acpi_unbind_one(struct device *dev)
270
{
271
	struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
272 273
	struct acpi_device_physical_node *entry;

274
	if (!acpi_dev)
275
		return 0;
276

277
	mutex_lock(&acpi_dev->physical_node_lock);
278 279 280 281 282 283 284 285 286 287 288

	list_for_each_entry(entry, &acpi_dev->physical_node_list, node)
		if (entry->dev == dev) {
			char physnode_name[PHYSICAL_NODE_NAME_SIZE];

			list_del(&entry->node);
			acpi_dev->physical_node_count--;

			acpi_physnode_link_name(physnode_name, entry->node_id);
			sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
			sysfs_remove_link(&dev->kobj, "firmware_node");
289
			ACPI_COMPANION_SET(dev, NULL);
290
			/* Drop references taken by acpi_bind_one(). */
291
			put_device(dev);
292
			put_device(&acpi_dev->dev);
293 294 295 296
			kfree(entry);
			break;
		}

297
	mutex_unlock(&acpi_dev->physical_node_lock);
298 299
	return 0;
}
300
EXPORT_SYMBOL_GPL(acpi_unbind_one);
301 302 303

static int acpi_platform_notify(struct device *dev)
{
304
	struct acpi_bus_type *type = acpi_get_bus_type(dev);
305
	int ret;
306

307
	ret = acpi_bind_one(dev, NULL);
308
	if (ret && type) {
309 310 311 312
		struct acpi_device *adev;

		adev = type->find_companion(dev);
		if (!adev) {
313
			DBG("Unable to get handle for %s\n", dev_name(dev));
314
			ret = -ENODEV;
315 316
			goto out;
		}
317
		ret = acpi_bind_one(dev, adev->handle);
318 319
		if (ret)
			goto out;
320
	}
321 322 323

	if (type && type->setup)
		type->setup(dev);
324

325
 out:
326 327 328 329
#if ACPI_GLUE_DEBUG
	if (!ret) {
		struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };

330
		acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
331
		DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
332
		kfree(buffer.pointer);
333
	} else
334
		DBG("Device %s -> No ACPI support\n", dev_name(dev));
335 336 337 338 339 340 341
#endif

	return ret;
}

static int acpi_platform_notify_remove(struct device *dev)
{
342 343
	struct acpi_bus_type *type;

344
	type = acpi_get_bus_type(dev);
345 346 347
	if (type && type->cleanup)
		type->cleanup(dev);

348 349 350 351
	acpi_unbind_one(dev);
	return 0;
}

352
int __init init_acpi_device_notify(void)
353 354 355 356 357 358 359 360 361
{
	if (platform_notify || platform_notify_remove) {
		printk(KERN_ERR PREFIX "Can't use platform_notify\n");
		return 0;
	}
	platform_notify = acpi_platform_notify;
	platform_notify_remove = acpi_platform_notify_remove;
	return 0;
}