18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Link physical devices with ACPI devices support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2005 Intel Corp. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/acpi_iort.h> 108c2ecf20Sopenharmony_ci#include <linux/export.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/list.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/rwsem.h> 168c2ecf20Sopenharmony_ci#include <linux/acpi.h> 178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "internal.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define ACPI_GLUE_DEBUG 0 238c2ecf20Sopenharmony_ci#if ACPI_GLUE_DEBUG 248c2ecf20Sopenharmony_ci#define DBG(fmt, ...) \ 258c2ecf20Sopenharmony_ci printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__) 268c2ecf20Sopenharmony_ci#else 278c2ecf20Sopenharmony_ci#define DBG(fmt, ...) \ 288c2ecf20Sopenharmony_cido { \ 298c2ecf20Sopenharmony_ci if (0) \ 308c2ecf20Sopenharmony_ci printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__); \ 318c2ecf20Sopenharmony_ci} while (0) 328c2ecf20Sopenharmony_ci#endif 338c2ecf20Sopenharmony_cistatic LIST_HEAD(bus_type_list); 348c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(bus_type_sem); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define PHYSICAL_NODE_STRING "physical_node" 378c2ecf20Sopenharmony_ci#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciint register_acpi_bus_type(struct acpi_bus_type *type) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci if (acpi_disabled) 428c2ecf20Sopenharmony_ci return -ENODEV; 438c2ecf20Sopenharmony_ci if (type && type->match && type->find_companion) { 448c2ecf20Sopenharmony_ci down_write(&bus_type_sem); 458c2ecf20Sopenharmony_ci list_add_tail(&type->list, &bus_type_list); 468c2ecf20Sopenharmony_ci up_write(&bus_type_sem); 478c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "bus type %s registered\n", type->name); 488c2ecf20Sopenharmony_ci return 0; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci return -ENODEV; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_acpi_bus_type); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciint unregister_acpi_bus_type(struct acpi_bus_type *type) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci if (acpi_disabled) 578c2ecf20Sopenharmony_ci return 0; 588c2ecf20Sopenharmony_ci if (type) { 598c2ecf20Sopenharmony_ci down_write(&bus_type_sem); 608c2ecf20Sopenharmony_ci list_del_init(&type->list); 618c2ecf20Sopenharmony_ci up_write(&bus_type_sem); 628c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "bus type %s unregistered\n", 638c2ecf20Sopenharmony_ci type->name); 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci return -ENODEV; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_acpi_bus_type); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct acpi_bus_type *acpi_get_bus_type(struct device *dev) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct acpi_bus_type *tmp, *ret = NULL; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci down_read(&bus_type_sem); 758c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &bus_type_list, list) { 768c2ecf20Sopenharmony_ci if (tmp->match(dev)) { 778c2ecf20Sopenharmony_ci ret = tmp; 788c2ecf20Sopenharmony_ci break; 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci up_read(&bus_type_sem); 828c2ecf20Sopenharmony_ci return ret; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define FIND_CHILD_MIN_SCORE 1 868c2ecf20Sopenharmony_ci#define FIND_CHILD_MAX_SCORE 2 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int find_child_checks(struct acpi_device *adev, bool check_children) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci bool sta_present = true; 918c2ecf20Sopenharmony_ci unsigned long long sta; 928c2ecf20Sopenharmony_ci acpi_status status; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta); 958c2ecf20Sopenharmony_ci if (status == AE_NOT_FOUND) 968c2ecf20Sopenharmony_ci sta_present = false; 978c2ecf20Sopenharmony_ci else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED)) 988c2ecf20Sopenharmony_ci return -ENODEV; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (check_children && list_empty(&adev->children)) 1018c2ecf20Sopenharmony_ci return -ENODEV; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * If the device has a _HID returning a valid ACPI/PNP device ID, it is 1058c2ecf20Sopenharmony_ci * better to make it look less attractive here, so that the other device 1068c2ecf20Sopenharmony_ci * with the same _ADR value (that may not have a valid device ID) can be 1078c2ecf20Sopenharmony_ci * matched going forward. [This means a second spec violation in a row, 1088c2ecf20Sopenharmony_ci * so whatever we do here is best effort anyway.] 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ci return sta_present && !adev->pnp.type.platform_id ? 1118c2ecf20Sopenharmony_ci FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistruct acpi_device *acpi_find_child_device(struct acpi_device *parent, 1158c2ecf20Sopenharmony_ci u64 address, bool check_children) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct acpi_device *adev, *ret = NULL; 1188c2ecf20Sopenharmony_ci int ret_score = 0; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (!parent) 1218c2ecf20Sopenharmony_ci return NULL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci list_for_each_entry(adev, &parent->children, node) { 1248c2ecf20Sopenharmony_ci unsigned long long addr; 1258c2ecf20Sopenharmony_ci acpi_status status; 1268c2ecf20Sopenharmony_ci int score; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(adev->handle, METHOD_NAME__ADR, 1298c2ecf20Sopenharmony_ci NULL, &addr); 1308c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || addr != address) 1318c2ecf20Sopenharmony_ci continue; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (!ret) { 1348c2ecf20Sopenharmony_ci /* This is the first matching object. Save it. */ 1358c2ecf20Sopenharmony_ci ret = adev; 1368c2ecf20Sopenharmony_ci continue; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci /* 1398c2ecf20Sopenharmony_ci * There is more than one matching device object with the same 1408c2ecf20Sopenharmony_ci * _ADR value. That really is unexpected, so we are kind of 1418c2ecf20Sopenharmony_ci * beyond the scope of the spec here. We have to choose which 1428c2ecf20Sopenharmony_ci * one to return, though. 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * First, check if the previously found object is good enough 1458c2ecf20Sopenharmony_ci * and return it if so. Second, do the same for the object that 1468c2ecf20Sopenharmony_ci * we've just found. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci if (!ret_score) { 1498c2ecf20Sopenharmony_ci ret_score = find_child_checks(ret, check_children); 1508c2ecf20Sopenharmony_ci if (ret_score == FIND_CHILD_MAX_SCORE) 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci score = find_child_checks(adev, check_children); 1548c2ecf20Sopenharmony_ci if (score == FIND_CHILD_MAX_SCORE) { 1558c2ecf20Sopenharmony_ci return adev; 1568c2ecf20Sopenharmony_ci } else if (score > ret_score) { 1578c2ecf20Sopenharmony_ci ret = adev; 1588c2ecf20Sopenharmony_ci ret_score = score; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_find_child_device); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void acpi_physnode_link_name(char *buf, unsigned int node_id) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci if (node_id > 0) 1688c2ecf20Sopenharmony_ci snprintf(buf, PHYSICAL_NODE_NAME_SIZE, 1698c2ecf20Sopenharmony_ci PHYSICAL_NODE_STRING "%u", node_id); 1708c2ecf20Sopenharmony_ci else 1718c2ecf20Sopenharmony_ci strcpy(buf, PHYSICAL_NODE_STRING); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ciint acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct acpi_device_physical_node *physical_node, *pn; 1778c2ecf20Sopenharmony_ci char physical_node_name[PHYSICAL_NODE_NAME_SIZE]; 1788c2ecf20Sopenharmony_ci struct list_head *physnode_list; 1798c2ecf20Sopenharmony_ci unsigned int node_id; 1808c2ecf20Sopenharmony_ci int retval = -EINVAL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (has_acpi_companion(dev)) { 1838c2ecf20Sopenharmony_ci if (acpi_dev) { 1848c2ecf20Sopenharmony_ci dev_warn(dev, "ACPI companion already set\n"); 1858c2ecf20Sopenharmony_ci return -EINVAL; 1868c2ecf20Sopenharmony_ci } else { 1878c2ecf20Sopenharmony_ci acpi_dev = ACPI_COMPANION(dev); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci if (!acpi_dev) 1918c2ecf20Sopenharmony_ci return -EINVAL; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci get_device(&acpi_dev->dev); 1948c2ecf20Sopenharmony_ci get_device(dev); 1958c2ecf20Sopenharmony_ci physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!physical_node) { 1978c2ecf20Sopenharmony_ci retval = -ENOMEM; 1988c2ecf20Sopenharmony_ci goto err; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci mutex_lock(&acpi_dev->physical_node_lock); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* 2048c2ecf20Sopenharmony_ci * Keep the list sorted by node_id so that the IDs of removed nodes can 2058c2ecf20Sopenharmony_ci * be recycled easily. 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ci physnode_list = &acpi_dev->physical_node_list; 2088c2ecf20Sopenharmony_ci node_id = 0; 2098c2ecf20Sopenharmony_ci list_for_each_entry(pn, &acpi_dev->physical_node_list, node) { 2108c2ecf20Sopenharmony_ci /* Sanity check. */ 2118c2ecf20Sopenharmony_ci if (pn->dev == dev) { 2128c2ecf20Sopenharmony_ci mutex_unlock(&acpi_dev->physical_node_lock); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci dev_warn(dev, "Already associated with ACPI node\n"); 2158c2ecf20Sopenharmony_ci kfree(physical_node); 2168c2ecf20Sopenharmony_ci if (ACPI_COMPANION(dev) != acpi_dev) 2178c2ecf20Sopenharmony_ci goto err; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci put_device(dev); 2208c2ecf20Sopenharmony_ci put_device(&acpi_dev->dev); 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci if (pn->node_id == node_id) { 2248c2ecf20Sopenharmony_ci physnode_list = &pn->node; 2258c2ecf20Sopenharmony_ci node_id++; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci physical_node->node_id = node_id; 2308c2ecf20Sopenharmony_ci physical_node->dev = dev; 2318c2ecf20Sopenharmony_ci list_add(&physical_node->node, physnode_list); 2328c2ecf20Sopenharmony_ci acpi_dev->physical_node_count++; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (!has_acpi_companion(dev)) 2358c2ecf20Sopenharmony_ci ACPI_COMPANION_SET(dev, acpi_dev); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci acpi_physnode_link_name(physical_node_name, node_id); 2388c2ecf20Sopenharmony_ci retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, 2398c2ecf20Sopenharmony_ci physical_node_name); 2408c2ecf20Sopenharmony_ci if (retval) 2418c2ecf20Sopenharmony_ci dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n", 2428c2ecf20Sopenharmony_ci physical_node_name, retval); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, 2458c2ecf20Sopenharmony_ci "firmware_node"); 2468c2ecf20Sopenharmony_ci if (retval) 2478c2ecf20Sopenharmony_ci dev_err(dev, "Failed to create link firmware_node (%d)\n", 2488c2ecf20Sopenharmony_ci retval); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci mutex_unlock(&acpi_dev->physical_node_lock); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (acpi_dev->wakeup.flags.valid) 2538c2ecf20Sopenharmony_ci device_set_wakeup_capable(dev, true); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci err: 2588c2ecf20Sopenharmony_ci ACPI_COMPANION_SET(dev, NULL); 2598c2ecf20Sopenharmony_ci put_device(dev); 2608c2ecf20Sopenharmony_ci put_device(&acpi_dev->dev); 2618c2ecf20Sopenharmony_ci return retval; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_bind_one); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciint acpi_unbind_one(struct device *dev) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct acpi_device *acpi_dev = ACPI_COMPANION(dev); 2688c2ecf20Sopenharmony_ci struct acpi_device_physical_node *entry; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (!acpi_dev) 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci mutex_lock(&acpi_dev->physical_node_lock); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci list_for_each_entry(entry, &acpi_dev->physical_node_list, node) 2768c2ecf20Sopenharmony_ci if (entry->dev == dev) { 2778c2ecf20Sopenharmony_ci char physnode_name[PHYSICAL_NODE_NAME_SIZE]; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci list_del(&entry->node); 2808c2ecf20Sopenharmony_ci acpi_dev->physical_node_count--; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci acpi_physnode_link_name(physnode_name, entry->node_id); 2838c2ecf20Sopenharmony_ci sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name); 2848c2ecf20Sopenharmony_ci sysfs_remove_link(&dev->kobj, "firmware_node"); 2858c2ecf20Sopenharmony_ci ACPI_COMPANION_SET(dev, NULL); 2868c2ecf20Sopenharmony_ci /* Drop references taken by acpi_bind_one(). */ 2878c2ecf20Sopenharmony_ci put_device(dev); 2888c2ecf20Sopenharmony_ci put_device(&acpi_dev->dev); 2898c2ecf20Sopenharmony_ci kfree(entry); 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci mutex_unlock(&acpi_dev->physical_node_lock); 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_unbind_one); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int acpi_device_notify(struct device *dev) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct acpi_bus_type *type = acpi_get_bus_type(dev); 3018c2ecf20Sopenharmony_ci struct acpi_device *adev; 3028c2ecf20Sopenharmony_ci int ret; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci ret = acpi_bind_one(dev, NULL); 3058c2ecf20Sopenharmony_ci if (ret && type) { 3068c2ecf20Sopenharmony_ci struct acpi_device *adev; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci adev = type->find_companion(dev); 3098c2ecf20Sopenharmony_ci if (!adev) { 3108c2ecf20Sopenharmony_ci DBG("Unable to get handle for %s\n", dev_name(dev)); 3118c2ecf20Sopenharmony_ci ret = -ENODEV; 3128c2ecf20Sopenharmony_ci goto out; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci ret = acpi_bind_one(dev, adev); 3158c2ecf20Sopenharmony_ci if (ret) 3168c2ecf20Sopenharmony_ci goto out; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci adev = ACPI_COMPANION(dev); 3198c2ecf20Sopenharmony_ci if (!adev) 3208c2ecf20Sopenharmony_ci goto out; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (dev_is_platform(dev)) 3238c2ecf20Sopenharmony_ci acpi_configure_pmsi_domain(dev); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (type && type->setup) 3268c2ecf20Sopenharmony_ci type->setup(dev); 3278c2ecf20Sopenharmony_ci else if (adev->handler && adev->handler->bind) 3288c2ecf20Sopenharmony_ci adev->handler->bind(dev); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci out: 3318c2ecf20Sopenharmony_ci#if ACPI_GLUE_DEBUG 3328c2ecf20Sopenharmony_ci if (!ret) { 3338c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer); 3368c2ecf20Sopenharmony_ci DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer); 3378c2ecf20Sopenharmony_ci kfree(buffer.pointer); 3388c2ecf20Sopenharmony_ci } else 3398c2ecf20Sopenharmony_ci DBG("Device %s -> No ACPI support\n", dev_name(dev)); 3408c2ecf20Sopenharmony_ci#endif 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int acpi_device_notify_remove(struct device *dev) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct acpi_device *adev = ACPI_COMPANION(dev); 3488c2ecf20Sopenharmony_ci struct acpi_bus_type *type; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!adev) 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci type = acpi_get_bus_type(dev); 3548c2ecf20Sopenharmony_ci if (type && type->cleanup) 3558c2ecf20Sopenharmony_ci type->cleanup(dev); 3568c2ecf20Sopenharmony_ci else if (adev->handler && adev->handler->unbind) 3578c2ecf20Sopenharmony_ci adev->handler->unbind(dev); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci acpi_unbind_one(dev); 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ciint acpi_platform_notify(struct device *dev, enum kobject_action action) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci switch (action) { 3668c2ecf20Sopenharmony_ci case KOBJ_ADD: 3678c2ecf20Sopenharmony_ci acpi_device_notify(dev); 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci case KOBJ_REMOVE: 3708c2ecf20Sopenharmony_ci acpi_device_notify_remove(dev); 3718c2ecf20Sopenharmony_ci break; 3728c2ecf20Sopenharmony_ci default: 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 377