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