xref: /openbmc/linux/drivers/acpi/glue.c (revision ca5b74d2675a44f54aacb919c1cf022463e2f738)
14e10d12aSDavid Shaohua Li /*
24e10d12aSDavid Shaohua Li  * Link physical devices with ACPI devices support
34e10d12aSDavid Shaohua Li  *
44e10d12aSDavid Shaohua Li  * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com>
54e10d12aSDavid Shaohua Li  * Copyright (c) 2005 Intel Corp.
64e10d12aSDavid Shaohua Li  *
74e10d12aSDavid Shaohua Li  * This file is released under the GPLv2.
84e10d12aSDavid Shaohua Li  */
9214f2c90SPaul Gortmaker #include <linux/export.h>
104e10d12aSDavid Shaohua Li #include <linux/init.h>
114e10d12aSDavid Shaohua Li #include <linux/list.h>
124e10d12aSDavid Shaohua Li #include <linux/device.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
144e10d12aSDavid Shaohua Li #include <linux/rwsem.h>
154e10d12aSDavid Shaohua Li #include <linux/acpi.h>
164e10d12aSDavid Shaohua Li 
17a192a958SLen Brown #include "internal.h"
18a192a958SLen Brown 
194e10d12aSDavid Shaohua Li #define ACPI_GLUE_DEBUG	0
204e10d12aSDavid Shaohua Li #if ACPI_GLUE_DEBUG
2123415eb5SJoe Perches #define DBG(fmt, ...)						\
2223415eb5SJoe Perches 	printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__)
234e10d12aSDavid Shaohua Li #else
2423415eb5SJoe Perches #define DBG(fmt, ...)						\
2523415eb5SJoe Perches do {								\
2623415eb5SJoe Perches 	if (0)							\
2723415eb5SJoe Perches 		printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__);	\
2823415eb5SJoe Perches } while (0)
294e10d12aSDavid Shaohua Li #endif
304e10d12aSDavid Shaohua Li static LIST_HEAD(bus_type_list);
314e10d12aSDavid Shaohua Li static DECLARE_RWSEM(bus_type_sem);
324e10d12aSDavid Shaohua Li 
331033f904SLan Tianyu #define PHYSICAL_NODE_STRING "physical_node"
34007ccfcfSRafael J. Wysocki #define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)
351033f904SLan Tianyu 
364e10d12aSDavid Shaohua Li int register_acpi_bus_type(struct acpi_bus_type *type)
374e10d12aSDavid Shaohua Li {
384e10d12aSDavid Shaohua Li 	if (acpi_disabled)
394e10d12aSDavid Shaohua Li 		return -ENODEV;
40e3f02c52SRafael J. Wysocki 	if (type && type->match && type->find_companion) {
414e10d12aSDavid Shaohua Li 		down_write(&bus_type_sem);
424e10d12aSDavid Shaohua Li 		list_add_tail(&type->list, &bus_type_list);
434e10d12aSDavid Shaohua Li 		up_write(&bus_type_sem);
4453540098SRafael J. Wysocki 		printk(KERN_INFO PREFIX "bus type %s registered\n", type->name);
454e10d12aSDavid Shaohua Li 		return 0;
464e10d12aSDavid Shaohua Li 	}
474e10d12aSDavid Shaohua Li 	return -ENODEV;
484e10d12aSDavid Shaohua Li }
4991e4d5a1SJeff Garzik EXPORT_SYMBOL_GPL(register_acpi_bus_type);
504e10d12aSDavid Shaohua Li 
514e10d12aSDavid Shaohua Li int unregister_acpi_bus_type(struct acpi_bus_type *type)
524e10d12aSDavid Shaohua Li {
534e10d12aSDavid Shaohua Li 	if (acpi_disabled)
544e10d12aSDavid Shaohua Li 		return 0;
554e10d12aSDavid Shaohua Li 	if (type) {
564e10d12aSDavid Shaohua Li 		down_write(&bus_type_sem);
574e10d12aSDavid Shaohua Li 		list_del_init(&type->list);
584e10d12aSDavid Shaohua Li 		up_write(&bus_type_sem);
5953540098SRafael J. Wysocki 		printk(KERN_INFO PREFIX "bus type %s unregistered\n",
6053540098SRafael J. Wysocki 		       type->name);
614e10d12aSDavid Shaohua Li 		return 0;
624e10d12aSDavid Shaohua Li 	}
634e10d12aSDavid Shaohua Li 	return -ENODEV;
644e10d12aSDavid Shaohua Li }
6591e4d5a1SJeff Garzik EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
664e10d12aSDavid Shaohua Li 
6753540098SRafael J. Wysocki static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
684e10d12aSDavid Shaohua Li {
694e10d12aSDavid Shaohua Li 	struct acpi_bus_type *tmp, *ret = NULL;
704e10d12aSDavid Shaohua Li 
714e10d12aSDavid Shaohua Li 	down_read(&bus_type_sem);
724e10d12aSDavid Shaohua Li 	list_for_each_entry(tmp, &bus_type_list, list) {
7353540098SRafael J. Wysocki 		if (tmp->match(dev)) {
744e10d12aSDavid Shaohua Li 			ret = tmp;
754e10d12aSDavid Shaohua Li 			break;
764e10d12aSDavid Shaohua Li 		}
774e10d12aSDavid Shaohua Li 	}
784e10d12aSDavid Shaohua Li 	up_read(&bus_type_sem);
794e10d12aSDavid Shaohua Li 	return ret;
804e10d12aSDavid Shaohua Li }
814e10d12aSDavid Shaohua Li 
8211b88ee2SRafael J. Wysocki #define FIND_CHILD_MIN_SCORE	1
8311b88ee2SRafael J. Wysocki #define FIND_CHILD_MAX_SCORE	2
8411b88ee2SRafael J. Wysocki 
85d9fef0c4SRafael J. Wysocki static int find_child_checks(struct acpi_device *adev, bool check_children)
864e10d12aSDavid Shaohua Li {
8711b88ee2SRafael J. Wysocki 	bool sta_present = true;
8860f75b8eSRafael J. Wysocki 	unsigned long long sta;
8960f75b8eSRafael J. Wysocki 	acpi_status status;
904e10d12aSDavid Shaohua Li 
91d9fef0c4SRafael J. Wysocki 	status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
9211b88ee2SRafael J. Wysocki 	if (status == AE_NOT_FOUND)
9311b88ee2SRafael J. Wysocki 		sta_present = false;
9411b88ee2SRafael J. Wysocki 	else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
9511b88ee2SRafael J. Wysocki 		return -ENODEV;
964e10d12aSDavid Shaohua Li 
97d9fef0c4SRafael J. Wysocki 	if (check_children && list_empty(&adev->children))
9811b88ee2SRafael J. Wysocki 		return -ENODEV;
99d9fef0c4SRafael J. Wysocki 
10011b88ee2SRafael J. Wysocki 	return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
10160f75b8eSRafael J. Wysocki }
10260f75b8eSRafael J. Wysocki 
103d9fef0c4SRafael J. Wysocki struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
104d9fef0c4SRafael J. Wysocki 					   u64 address, bool check_children)
10560f75b8eSRafael J. Wysocki {
106d9fef0c4SRafael J. Wysocki 	struct acpi_device *adev, *ret = NULL;
107d9fef0c4SRafael J. Wysocki 	int ret_score = 0;
108d9fef0c4SRafael J. Wysocki 
1095ce79d20SRafael J. Wysocki 	if (!parent)
1105ce79d20SRafael J. Wysocki 		return NULL;
1115ce79d20SRafael J. Wysocki 
112d9fef0c4SRafael J. Wysocki 	list_for_each_entry(adev, &parent->children, node) {
11360f75b8eSRafael J. Wysocki 		unsigned long long addr;
11460f75b8eSRafael J. Wysocki 		acpi_status status;
11511b88ee2SRafael J. Wysocki 		int score;
11660f75b8eSRafael J. Wysocki 
117d9fef0c4SRafael J. Wysocki 		status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR,
118d9fef0c4SRafael J. Wysocki 					       NULL, &addr);
119d9fef0c4SRafael J. Wysocki 		if (ACPI_FAILURE(status) || addr != address)
120d9fef0c4SRafael J. Wysocki 			continue;
12160f75b8eSRafael J. Wysocki 
122d9fef0c4SRafael J. Wysocki 		if (!ret) {
123d9fef0c4SRafael J. Wysocki 			/* This is the first matching object.  Save it. */
124d9fef0c4SRafael J. Wysocki 			ret = adev;
125d9fef0c4SRafael J. Wysocki 			continue;
12660f75b8eSRafael J. Wysocki 		}
12760f75b8eSRafael J. Wysocki 		/*
128d9fef0c4SRafael J. Wysocki 		 * There is more than one matching device object with the same
129d9fef0c4SRafael J. Wysocki 		 * _ADR value.  That really is unexpected, so we are kind of
130d9fef0c4SRafael J. Wysocki 		 * beyond the scope of the spec here.  We have to choose which
131d9fef0c4SRafael J. Wysocki 		 * one to return, though.
13260f75b8eSRafael J. Wysocki 		 *
133d9fef0c4SRafael J. Wysocki 		 * First, check if the previously found object is good enough
134d9fef0c4SRafael J. Wysocki 		 * and return it if so.  Second, do the same for the object that
135d9fef0c4SRafael J. Wysocki 		 * we've just found.
13660f75b8eSRafael J. Wysocki 		 */
137d9fef0c4SRafael J. Wysocki 		if (!ret_score) {
138d9fef0c4SRafael J. Wysocki 			ret_score = find_child_checks(ret, check_children);
139d9fef0c4SRafael J. Wysocki 			if (ret_score == FIND_CHILD_MAX_SCORE)
140d9fef0c4SRafael J. Wysocki 				return ret;
14160f75b8eSRafael J. Wysocki 		}
142d9fef0c4SRafael J. Wysocki 		score = find_child_checks(adev, check_children);
14311b88ee2SRafael J. Wysocki 		if (score == FIND_CHILD_MAX_SCORE) {
144d9fef0c4SRafael J. Wysocki 			return adev;
145d9fef0c4SRafael J. Wysocki 		} else if (score > ret_score) {
146d9fef0c4SRafael J. Wysocki 			ret = adev;
147d9fef0c4SRafael J. Wysocki 			ret_score = score;
14860f75b8eSRafael J. Wysocki 		}
149d9fef0c4SRafael J. Wysocki 	}
150d9fef0c4SRafael J. Wysocki 	return ret;
15160f75b8eSRafael J. Wysocki }
1529c5ad36dSRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_find_child_device);
15360f75b8eSRafael J. Wysocki 
154bdbdbf91SRafael J. Wysocki static void acpi_physnode_link_name(char *buf, unsigned int node_id)
155bdbdbf91SRafael J. Wysocki {
156bdbdbf91SRafael J. Wysocki 	if (node_id > 0)
157bdbdbf91SRafael J. Wysocki 		snprintf(buf, PHYSICAL_NODE_NAME_SIZE,
158bdbdbf91SRafael J. Wysocki 			 PHYSICAL_NODE_STRING "%u", node_id);
159bdbdbf91SRafael J. Wysocki 	else
160bdbdbf91SRafael J. Wysocki 		strcpy(buf, PHYSICAL_NODE_STRING);
161bdbdbf91SRafael J. Wysocki }
162bdbdbf91SRafael J. Wysocki 
16324dee1fcSRafael J. Wysocki int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
1644e10d12aSDavid Shaohua Li {
165f3fd0c8aSRafael J. Wysocki 	struct acpi_device_physical_node *physical_node, *pn;
166007ccfcfSRafael J. Wysocki 	char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
167007ccfcfSRafael J. Wysocki 	struct list_head *physnode_list;
168007ccfcfSRafael J. Wysocki 	unsigned int node_id;
1691033f904SLan Tianyu 	int retval = -EINVAL;
1704e10d12aSDavid Shaohua Li 
171*ca5b74d2SRafael J. Wysocki 	if (has_acpi_companion(dev)) {
17224dee1fcSRafael J. Wysocki 		if (acpi_dev) {
1737b199811SRafael J. Wysocki 			dev_warn(dev, "ACPI companion already set\n");
1744e10d12aSDavid Shaohua Li 			return -EINVAL;
175f3fd0c8aSRafael J. Wysocki 		} else {
1767b199811SRafael J. Wysocki 			acpi_dev = ACPI_COMPANION(dev);
1774e10d12aSDavid Shaohua Li 		}
178f3fd0c8aSRafael J. Wysocki 	}
1797b199811SRafael J. Wysocki 	if (!acpi_dev)
180f3fd0c8aSRafael J. Wysocki 		return -EINVAL;
1811033f904SLan Tianyu 
182a104b4d4SRafael J. Wysocki 	get_device(&acpi_dev->dev);
1834e10d12aSDavid Shaohua Li 	get_device(dev);
184f3fd0c8aSRafael J. Wysocki 	physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
1851033f904SLan Tianyu 	if (!physical_node) {
1861033f904SLan Tianyu 		retval = -ENOMEM;
1871033f904SLan Tianyu 		goto err;
1884e10d12aSDavid Shaohua Li 	}
1891033f904SLan Tianyu 
1901033f904SLan Tianyu 	mutex_lock(&acpi_dev->physical_node_lock);
191f3fd0c8aSRafael J. Wysocki 
192007ccfcfSRafael J. Wysocki 	/*
193007ccfcfSRafael J. Wysocki 	 * Keep the list sorted by node_id so that the IDs of removed nodes can
194007ccfcfSRafael J. Wysocki 	 * be recycled easily.
195007ccfcfSRafael J. Wysocki 	 */
196007ccfcfSRafael J. Wysocki 	physnode_list = &acpi_dev->physical_node_list;
197007ccfcfSRafael J. Wysocki 	node_id = 0;
198007ccfcfSRafael J. Wysocki 	list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
199f3fd0c8aSRafael J. Wysocki 		/* Sanity check. */
200f3fd0c8aSRafael J. Wysocki 		if (pn->dev == dev) {
2013342c753SRafael J. Wysocki 			mutex_unlock(&acpi_dev->physical_node_lock);
2023fe444adSRafael J. Wysocki 
2033342c753SRafael J. Wysocki 			dev_warn(dev, "Already associated with ACPI node\n");
2043342c753SRafael J. Wysocki 			kfree(physical_node);
2057b199811SRafael J. Wysocki 			if (ACPI_COMPANION(dev) != acpi_dev)
2063342c753SRafael J. Wysocki 				goto err;
2073342c753SRafael J. Wysocki 
2083342c753SRafael J. Wysocki 			put_device(dev);
209a104b4d4SRafael J. Wysocki 			put_device(&acpi_dev->dev);
2103342c753SRafael J. Wysocki 			return 0;
211f3fd0c8aSRafael J. Wysocki 		}
212007ccfcfSRafael J. Wysocki 		if (pn->node_id == node_id) {
213007ccfcfSRafael J. Wysocki 			physnode_list = &pn->node;
214007ccfcfSRafael J. Wysocki 			node_id++;
215007ccfcfSRafael J. Wysocki 		}
2161033f904SLan Tianyu 	}
2171033f904SLan Tianyu 
218007ccfcfSRafael J. Wysocki 	physical_node->node_id = node_id;
2191033f904SLan Tianyu 	physical_node->dev = dev;
220007ccfcfSRafael J. Wysocki 	list_add(&physical_node->node, physnode_list);
2211033f904SLan Tianyu 	acpi_dev->physical_node_count++;
222f3fd0c8aSRafael J. Wysocki 
223*ca5b74d2SRafael J. Wysocki 	if (!has_acpi_companion(dev))
2247b199811SRafael J. Wysocki 		ACPI_COMPANION_SET(dev, acpi_dev);
2254e10d12aSDavid Shaohua Li 
226bdbdbf91SRafael J. Wysocki 	acpi_physnode_link_name(physical_node_name, node_id);
2271033f904SLan Tianyu 	retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
2281033f904SLan Tianyu 				   physical_node_name);
229464c1147SRafael J. Wysocki 	if (retval)
230464c1147SRafael J. Wysocki 		dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n",
231464c1147SRafael J. Wysocki 			physical_node_name, retval);
232464c1147SRafael J. Wysocki 
2331033f904SLan Tianyu 	retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
2341071695fSDavid Brownell 				   "firmware_node");
235464c1147SRafael J. Wysocki 	if (retval)
236464c1147SRafael J. Wysocki 		dev_err(dev, "Failed to create link firmware_node (%d)\n",
237464c1147SRafael J. Wysocki 			retval);
2381033f904SLan Tianyu 
23940055206SRafael J. Wysocki 	mutex_unlock(&acpi_dev->physical_node_lock);
24040055206SRafael J. Wysocki 
2417fa69bafSRafael J. Wysocki 	if (acpi_dev->wakeup.flags.valid)
242eb9d0fe4SRafael J. Wysocki 		device_set_wakeup_capable(dev, true);
2431071695fSDavid Brownell 
2444e10d12aSDavid Shaohua Li 	return 0;
2451033f904SLan Tianyu 
2461033f904SLan Tianyu  err:
2477b199811SRafael J. Wysocki 	ACPI_COMPANION_SET(dev, NULL);
2481033f904SLan Tianyu 	put_device(dev);
249a104b4d4SRafael J. Wysocki 	put_device(&acpi_dev->dev);
2501033f904SLan Tianyu 	return retval;
2514e10d12aSDavid Shaohua Li }
252ac212b69SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_bind_one);
2534e10d12aSDavid Shaohua Li 
254ac212b69SRafael J. Wysocki int acpi_unbind_one(struct device *dev)
2554e10d12aSDavid Shaohua Li {
2567b199811SRafael J. Wysocki 	struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
2571033f904SLan Tianyu 	struct acpi_device_physical_node *entry;
2581033f904SLan Tianyu 
2597b199811SRafael J. Wysocki 	if (!acpi_dev)
2604e10d12aSDavid Shaohua Li 		return 0;
2611071695fSDavid Brownell 
2621033f904SLan Tianyu 	mutex_lock(&acpi_dev->physical_node_lock);
2631033f904SLan Tianyu 
2643e332783SRafael J. Wysocki 	list_for_each_entry(entry, &acpi_dev->physical_node_list, node)
2653e332783SRafael J. Wysocki 		if (entry->dev == dev) {
2663e332783SRafael J. Wysocki 			char physnode_name[PHYSICAL_NODE_NAME_SIZE];
2671033f904SLan Tianyu 
2683e332783SRafael J. Wysocki 			list_del(&entry->node);
2691033f904SLan Tianyu 			acpi_dev->physical_node_count--;
2701033f904SLan Tianyu 
2713e332783SRafael J. Wysocki 			acpi_physnode_link_name(physnode_name, entry->node_id);
2723e332783SRafael J. Wysocki 			sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
2731071695fSDavid Brownell 			sysfs_remove_link(&dev->kobj, "firmware_node");
2747b199811SRafael J. Wysocki 			ACPI_COMPANION_SET(dev, NULL);
275a104b4d4SRafael J. Wysocki 			/* Drop references taken by acpi_bind_one(). */
2764e10d12aSDavid Shaohua Li 			put_device(dev);
277a104b4d4SRafael J. Wysocki 			put_device(&acpi_dev->dev);
2781033f904SLan Tianyu 			kfree(entry);
2793e332783SRafael J. Wysocki 			break;
2804e10d12aSDavid Shaohua Li 		}
2813e332783SRafael J. Wysocki 
2821033f904SLan Tianyu 	mutex_unlock(&acpi_dev->physical_node_lock);
2834e10d12aSDavid Shaohua Li 	return 0;
2844e10d12aSDavid Shaohua Li }
285ac212b69SRafael J. Wysocki EXPORT_SYMBOL_GPL(acpi_unbind_one);
2864e10d12aSDavid Shaohua Li 
2874e10d12aSDavid Shaohua Li static int acpi_platform_notify(struct device *dev)
2884e10d12aSDavid Shaohua Li {
28953540098SRafael J. Wysocki 	struct acpi_bus_type *type = acpi_get_bus_type(dev);
2909cb32acfSRafael J. Wysocki 	struct acpi_device *adev;
29111909ca1SRafael J. Wysocki 	int ret;
2924e10d12aSDavid Shaohua Li 
293f3fd0c8aSRafael J. Wysocki 	ret = acpi_bind_one(dev, NULL);
29492414481SRafael J. Wysocki 	if (ret && type) {
295e3f02c52SRafael J. Wysocki 		struct acpi_device *adev;
296e3f02c52SRafael J. Wysocki 
297e3f02c52SRafael J. Wysocki 		adev = type->find_companion(dev);
298e3f02c52SRafael J. Wysocki 		if (!adev) {
29911909ca1SRafael J. Wysocki 			DBG("Unable to get handle for %s\n", dev_name(dev));
300e3f02c52SRafael J. Wysocki 			ret = -ENODEV;
30111909ca1SRafael J. Wysocki 			goto out;
30211909ca1SRafael J. Wysocki 		}
30324dee1fcSRafael J. Wysocki 		ret = acpi_bind_one(dev, adev);
30411909ca1SRafael J. Wysocki 		if (ret)
30511909ca1SRafael J. Wysocki 			goto out;
30611909ca1SRafael J. Wysocki 	}
3079cb32acfSRafael J. Wysocki 	adev = ACPI_COMPANION(dev);
3089cb32acfSRafael J. Wysocki 	if (!adev)
3099cb32acfSRafael J. Wysocki 		goto out;
31011909ca1SRafael J. Wysocki 
31111909ca1SRafael J. Wysocki 	if (type && type->setup)
31211909ca1SRafael J. Wysocki 		type->setup(dev);
3139cb32acfSRafael J. Wysocki 	else if (adev->handler && adev->handler->bind)
3149cb32acfSRafael J. Wysocki 		adev->handler->bind(dev);
3154e10d12aSDavid Shaohua Li 
316f3fd0c8aSRafael J. Wysocki  out:
3174e10d12aSDavid Shaohua Li #if ACPI_GLUE_DEBUG
3184e10d12aSDavid Shaohua Li 	if (!ret) {
3194e10d12aSDavid Shaohua Li 		struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
3204e10d12aSDavid Shaohua Li 
321a412a11dSYinghai Lu 		acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
322db1461adSKay Sievers 		DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
32302438d87SLen Brown 		kfree(buffer.pointer);
3244e10d12aSDavid Shaohua Li 	} else
325db1461adSKay Sievers 		DBG("Device %s -> No ACPI support\n", dev_name(dev));
3264e10d12aSDavid Shaohua Li #endif
3274e10d12aSDavid Shaohua Li 
3284e10d12aSDavid Shaohua Li 	return ret;
3294e10d12aSDavid Shaohua Li }
3304e10d12aSDavid Shaohua Li 
3314e10d12aSDavid Shaohua Li static int acpi_platform_notify_remove(struct device *dev)
3324e10d12aSDavid Shaohua Li {
3339cb32acfSRafael J. Wysocki 	struct acpi_device *adev = ACPI_COMPANION(dev);
33411909ca1SRafael J. Wysocki 	struct acpi_bus_type *type;
33511909ca1SRafael J. Wysocki 
3369cb32acfSRafael J. Wysocki 	if (!adev)
3379cb32acfSRafael J. Wysocki 		return 0;
3389cb32acfSRafael J. Wysocki 
33953540098SRafael J. Wysocki 	type = acpi_get_bus_type(dev);
34011909ca1SRafael J. Wysocki 	if (type && type->cleanup)
34111909ca1SRafael J. Wysocki 		type->cleanup(dev);
3429cb32acfSRafael J. Wysocki 	else if (adev->handler && adev->handler->unbind)
3439cb32acfSRafael J. Wysocki 		adev->handler->unbind(dev);
34411909ca1SRafael J. Wysocki 
3454e10d12aSDavid Shaohua Li 	acpi_unbind_one(dev);
3464e10d12aSDavid Shaohua Li 	return 0;
3474e10d12aSDavid Shaohua Li }
3484e10d12aSDavid Shaohua Li 
3490e46517dSBjorn Helgaas int __init init_acpi_device_notify(void)
3504e10d12aSDavid Shaohua Li {
3514e10d12aSDavid Shaohua Li 	if (platform_notify || platform_notify_remove) {
3524e10d12aSDavid Shaohua Li 		printk(KERN_ERR PREFIX "Can't use platform_notify\n");
3534e10d12aSDavid Shaohua Li 		return 0;
3544e10d12aSDavid Shaohua Li 	}
3554e10d12aSDavid Shaohua Li 	platform_notify = acpi_platform_notify;
3564e10d12aSDavid Shaohua Li 	platform_notify_remove = acpi_platform_notify_remove;
3574e10d12aSDavid Shaohua Li 	return 0;
3584e10d12aSDavid Shaohua Li }
359