18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ACPI support for platform bus type. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012, Intel Corporation 68c2ecf20Sopenharmony_ci * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 78c2ecf20Sopenharmony_ci * Mathias Nyman <mathias.nyman@linux.intel.com> 88c2ecf20Sopenharmony_ci * Rafael J. Wysocki <rafael.j.wysocki@intel.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/acpi.h> 128c2ecf20Sopenharmony_ci#include <linux/device.h> 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "internal.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct acpi_device_id forbidden_id_list[] = { 238c2ecf20Sopenharmony_ci {"PNP0000", 0}, /* PIC */ 248c2ecf20Sopenharmony_ci {"PNP0100", 0}, /* Timer */ 258c2ecf20Sopenharmony_ci {"PNP0200", 0}, /* AT DMA Controller */ 268c2ecf20Sopenharmony_ci {"ACPI0009", 0}, /* IOxAPIC */ 278c2ecf20Sopenharmony_ci {"ACPI000A", 0}, /* IOAPIC */ 288c2ecf20Sopenharmony_ci {"SMB0001", 0}, /* ACPI SMBUS virtual device */ 298c2ecf20Sopenharmony_ci {"", 0}, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct platform_device *acpi_platform_device_find_by_companion(struct acpi_device *adev) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct device *dev; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev); 378c2ecf20Sopenharmony_ci return dev ? to_platform_device(dev) : NULL; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int acpi_platform_device_remove_notify(struct notifier_block *nb, 418c2ecf20Sopenharmony_ci unsigned long value, void *arg) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci struct acpi_device *adev = arg; 448c2ecf20Sopenharmony_ci struct platform_device *pdev; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci switch (value) { 478c2ecf20Sopenharmony_ci case ACPI_RECONFIG_DEVICE_ADD: 488c2ecf20Sopenharmony_ci /* Nothing to do here */ 498c2ecf20Sopenharmony_ci break; 508c2ecf20Sopenharmony_ci case ACPI_RECONFIG_DEVICE_REMOVE: 518c2ecf20Sopenharmony_ci if (!acpi_device_enumerated(adev)) 528c2ecf20Sopenharmony_ci break; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci pdev = acpi_platform_device_find_by_companion(adev); 558c2ecf20Sopenharmony_ci if (!pdev) 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci platform_device_unregister(pdev); 598c2ecf20Sopenharmony_ci put_device(&pdev->dev); 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return NOTIFY_OK; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic struct notifier_block acpi_platform_notifier = { 678c2ecf20Sopenharmony_ci .notifier_call = acpi_platform_device_remove_notify, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void acpi_platform_fill_resource(struct acpi_device *adev, 718c2ecf20Sopenharmony_ci const struct resource *src, struct resource *dest) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct device *parent; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci *dest = *src; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * If the device has parent we need to take its resources into 798c2ecf20Sopenharmony_ci * account as well because this device might consume part of those. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci parent = acpi_get_first_physical_node(adev->parent); 828c2ecf20Sopenharmony_ci if (parent && dev_is_pci(parent)) 838c2ecf20Sopenharmony_ci dest->parent = pci_find_resource(to_pci_dev(parent), dest); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/** 878c2ecf20Sopenharmony_ci * acpi_create_platform_device - Create platform device for ACPI device node 888c2ecf20Sopenharmony_ci * @adev: ACPI device node to create a platform device for. 898c2ecf20Sopenharmony_ci * @properties: Optional collection of build-in properties. 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * Check if the given @adev can be represented as a platform device and, if 928c2ecf20Sopenharmony_ci * that's the case, create and register a platform device, populate its common 938c2ecf20Sopenharmony_ci * resources and returns a pointer to it. Otherwise, return %NULL. 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Name of the platform device will be the same as @adev's. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistruct platform_device *acpi_create_platform_device(struct acpi_device *adev, 988c2ecf20Sopenharmony_ci struct property_entry *properties) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct platform_device *pdev = NULL; 1018c2ecf20Sopenharmony_ci struct platform_device_info pdevinfo; 1028c2ecf20Sopenharmony_ci struct resource_entry *rentry; 1038c2ecf20Sopenharmony_ci struct list_head resource_list; 1048c2ecf20Sopenharmony_ci struct resource *resources = NULL; 1058c2ecf20Sopenharmony_ci int count; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* If the ACPI node already has a physical device attached, skip it. */ 1088c2ecf20Sopenharmony_ci if (adev->physical_node_count) 1098c2ecf20Sopenharmony_ci return NULL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!acpi_match_device_ids(adev, forbidden_id_list)) 1128c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&resource_list); 1158c2ecf20Sopenharmony_ci count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 1168c2ecf20Sopenharmony_ci if (count < 0) { 1178c2ecf20Sopenharmony_ci return NULL; 1188c2ecf20Sopenharmony_ci } else if (count > 0) { 1198c2ecf20Sopenharmony_ci resources = kcalloc(count, sizeof(struct resource), 1208c2ecf20Sopenharmony_ci GFP_KERNEL); 1218c2ecf20Sopenharmony_ci if (!resources) { 1228c2ecf20Sopenharmony_ci dev_err(&adev->dev, "No memory for resources\n"); 1238c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 1248c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci count = 0; 1278c2ecf20Sopenharmony_ci list_for_each_entry(rentry, &resource_list, node) 1288c2ecf20Sopenharmony_ci acpi_platform_fill_resource(adev, rentry->res, 1298c2ecf20Sopenharmony_ci &resources[count++]); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci acpi_dev_free_resource_list(&resource_list); 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci memset(&pdevinfo, 0, sizeof(pdevinfo)); 1358c2ecf20Sopenharmony_ci /* 1368c2ecf20Sopenharmony_ci * If the ACPI node has a parent and that parent has a physical device 1378c2ecf20Sopenharmony_ci * attached to it, that physical device should be the parent of the 1388c2ecf20Sopenharmony_ci * platform device we are about to create. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci pdevinfo.parent = adev->parent ? 1418c2ecf20Sopenharmony_ci acpi_get_first_physical_node(adev->parent) : NULL; 1428c2ecf20Sopenharmony_ci pdevinfo.name = dev_name(&adev->dev); 1438c2ecf20Sopenharmony_ci pdevinfo.id = -1; 1448c2ecf20Sopenharmony_ci pdevinfo.res = resources; 1458c2ecf20Sopenharmony_ci pdevinfo.num_res = count; 1468c2ecf20Sopenharmony_ci pdevinfo.fwnode = acpi_fwnode_handle(adev); 1478c2ecf20Sopenharmony_ci pdevinfo.properties = properties; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (acpi_dma_supported(adev)) 1508c2ecf20Sopenharmony_ci pdevinfo.dma_mask = DMA_BIT_MASK(32); 1518c2ecf20Sopenharmony_ci else 1528c2ecf20Sopenharmony_ci pdevinfo.dma_mask = 0; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci pdev = platform_device_register_full(&pdevinfo); 1558c2ecf20Sopenharmony_ci if (IS_ERR(pdev)) 1568c2ecf20Sopenharmony_ci dev_err(&adev->dev, "platform device creation failed: %ld\n", 1578c2ecf20Sopenharmony_ci PTR_ERR(pdev)); 1588c2ecf20Sopenharmony_ci else { 1598c2ecf20Sopenharmony_ci set_dev_node(&pdev->dev, acpi_get_node(adev->handle)); 1608c2ecf20Sopenharmony_ci dev_dbg(&adev->dev, "created platform device %s\n", 1618c2ecf20Sopenharmony_ci dev_name(&pdev->dev)); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci kfree(resources); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return pdev; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_create_platform_device); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_civoid __init acpi_platform_init(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci acpi_reconfig_notifier_register(&acpi_platform_notifier); 1738c2ecf20Sopenharmony_ci} 174