1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * ACPI support for platform bus type. 4 * 5 * Copyright (C) 2012, Intel Corporation 6 * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 7 * Mathias Nyman <mathias.nyman@linux.intel.com> 8 * Rafael J. Wysocki <rafael.j.wysocki@intel.com> 9 */ 10 11 #include <linux/acpi.h> 12 #include <linux/device.h> 13 #include <linux/err.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/dma-mapping.h> 17 #include <linux/pci.h> 18 #include <linux/platform_device.h> 19 20 #include "internal.h" 21 22 static const struct acpi_device_id forbidden_id_list[] = { 23 {"ACPI0009", 0}, /* IOxAPIC */ 24 {"ACPI000A", 0}, /* IOAPIC */ 25 {"PNP0000", 0}, /* PIC */ 26 {"PNP0100", 0}, /* Timer */ 27 {"PNP0200", 0}, /* AT DMA Controller */ 28 {"SMB0001", 0}, /* ACPI SMBUS virtual device */ 29 { } 30 }; 31 32 static struct platform_device *acpi_platform_device_find_by_companion(struct acpi_device *adev) 33 { 34 struct device *dev; 35 36 dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev); 37 return dev ? to_platform_device(dev) : NULL; 38 } 39 40 static int acpi_platform_device_remove_notify(struct notifier_block *nb, 41 unsigned long value, void *arg) 42 { 43 struct acpi_device *adev = arg; 44 struct platform_device *pdev; 45 46 switch (value) { 47 case ACPI_RECONFIG_DEVICE_ADD: 48 /* Nothing to do here */ 49 break; 50 case ACPI_RECONFIG_DEVICE_REMOVE: 51 if (!acpi_device_enumerated(adev)) 52 break; 53 54 pdev = acpi_platform_device_find_by_companion(adev); 55 if (!pdev) 56 break; 57 58 platform_device_unregister(pdev); 59 put_device(&pdev->dev); 60 break; 61 } 62 63 return NOTIFY_OK; 64 } 65 66 static struct notifier_block acpi_platform_notifier = { 67 .notifier_call = acpi_platform_device_remove_notify, 68 }; 69 70 static void acpi_platform_fill_resource(struct acpi_device *adev, 71 const struct resource *src, struct resource *dest) 72 { 73 struct device *parent; 74 75 *dest = *src; 76 77 /* 78 * If the device has parent we need to take its resources into 79 * account as well because this device might consume part of those. 80 */ 81 parent = acpi_get_first_physical_node(acpi_dev_parent(adev)); 82 if (parent && dev_is_pci(parent)) 83 dest->parent = pci_find_resource(to_pci_dev(parent), dest); 84 } 85 86 /** 87 * acpi_create_platform_device - Create platform device for ACPI device node 88 * @adev: ACPI device node to create a platform device for. 89 * @properties: Optional collection of build-in properties. 90 * 91 * Check if the given @adev can be represented as a platform device and, if 92 * that's the case, create and register a platform device, populate its common 93 * resources and returns a pointer to it. Otherwise, return %NULL. 94 * 95 * Name of the platform device will be the same as @adev's. 96 */ 97 struct platform_device *acpi_create_platform_device(struct acpi_device *adev, 98 const struct property_entry *properties) 99 { 100 struct acpi_device *parent = acpi_dev_parent(adev); 101 struct platform_device *pdev = NULL; 102 struct platform_device_info pdevinfo; 103 struct resource_entry *rentry; 104 struct list_head resource_list; 105 struct resource *resources = NULL; 106 int count; 107 108 /* If the ACPI node already has a physical device attached, skip it. */ 109 if (adev->physical_node_count) 110 return NULL; 111 112 if (!acpi_match_device_ids(adev, forbidden_id_list)) 113 return ERR_PTR(-EINVAL); 114 115 INIT_LIST_HEAD(&resource_list); 116 count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 117 if (count < 0) 118 return NULL; 119 if (count > 0) { 120 resources = kcalloc(count, sizeof(*resources), GFP_KERNEL); 121 if (!resources) { 122 acpi_dev_free_resource_list(&resource_list); 123 return ERR_PTR(-ENOMEM); 124 } 125 count = 0; 126 list_for_each_entry(rentry, &resource_list, node) 127 acpi_platform_fill_resource(adev, rentry->res, 128 &resources[count++]); 129 130 acpi_dev_free_resource_list(&resource_list); 131 } 132 133 memset(&pdevinfo, 0, sizeof(pdevinfo)); 134 /* 135 * If the ACPI node has a parent and that parent has a physical device 136 * attached to it, that physical device should be the parent of the 137 * platform device we are about to create. 138 */ 139 pdevinfo.parent = parent ? acpi_get_first_physical_node(parent) : NULL; 140 pdevinfo.name = dev_name(&adev->dev); 141 pdevinfo.id = PLATFORM_DEVID_NONE; 142 pdevinfo.res = resources; 143 pdevinfo.num_res = count; 144 pdevinfo.fwnode = acpi_fwnode_handle(adev); 145 pdevinfo.properties = properties; 146 147 if (acpi_dma_supported(adev)) 148 pdevinfo.dma_mask = DMA_BIT_MASK(32); 149 else 150 pdevinfo.dma_mask = 0; 151 152 pdev = platform_device_register_full(&pdevinfo); 153 if (IS_ERR(pdev)) 154 dev_err(&adev->dev, "platform device creation failed: %ld\n", 155 PTR_ERR(pdev)); 156 else { 157 set_dev_node(&pdev->dev, acpi_get_node(adev->handle)); 158 dev_dbg(&adev->dev, "created platform device %s\n", 159 dev_name(&pdev->dev)); 160 } 161 162 kfree(resources); 163 164 return pdev; 165 } 166 EXPORT_SYMBOL_GPL(acpi_create_platform_device); 167 168 void __init acpi_platform_init(void) 169 { 170 acpi_reconfig_notifier_register(&acpi_platform_notifier); 171 } 172