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/bits.h> 13 #include <linux/device.h> 14 #include <linux/err.h> 15 #include <linux/kernel.h> 16 #include <linux/module.h> 17 #include <linux/dma-mapping.h> 18 #include <linux/pci.h> 19 #include <linux/platform_device.h> 20 21 #include "internal.h" 22 23 /* Exclude devices that have no _CRS resources provided */ 24 #define ACPI_ALLOW_WO_RESOURCES BIT(0) 25 26 static const struct acpi_device_id forbidden_id_list[] = { 27 {"ACPI0009", 0}, /* IOxAPIC */ 28 {"ACPI000A", 0}, /* IOAPIC */ 29 {"PNP0000", 0}, /* PIC */ 30 {"PNP0100", 0}, /* Timer */ 31 {"PNP0200", 0}, /* AT DMA Controller */ 32 {ACPI_SMBUS_MS_HID, ACPI_ALLOW_WO_RESOURCES}, /* ACPI SMBUS virtual device */ 33 { } 34 }; 35 36 static struct platform_device *acpi_platform_device_find_by_companion(struct acpi_device *adev) 37 { 38 struct device *dev; 39 40 dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev); 41 return dev ? to_platform_device(dev) : NULL; 42 } 43 44 static int acpi_platform_device_remove_notify(struct notifier_block *nb, 45 unsigned long value, void *arg) 46 { 47 struct acpi_device *adev = arg; 48 struct platform_device *pdev; 49 50 switch (value) { 51 case ACPI_RECONFIG_DEVICE_ADD: 52 /* Nothing to do here */ 53 break; 54 case ACPI_RECONFIG_DEVICE_REMOVE: 55 if (!acpi_device_enumerated(adev)) 56 break; 57 58 pdev = acpi_platform_device_find_by_companion(adev); 59 if (!pdev) 60 break; 61 62 platform_device_unregister(pdev); 63 put_device(&pdev->dev); 64 break; 65 } 66 67 return NOTIFY_OK; 68 } 69 70 static struct notifier_block acpi_platform_notifier = { 71 .notifier_call = acpi_platform_device_remove_notify, 72 }; 73 74 static void acpi_platform_fill_resource(struct acpi_device *adev, 75 const struct resource *src, struct resource *dest) 76 { 77 struct device *parent; 78 79 *dest = *src; 80 81 /* 82 * If the device has parent we need to take its resources into 83 * account as well because this device might consume part of those. 84 */ 85 parent = acpi_get_first_physical_node(acpi_dev_parent(adev)); 86 if (parent && dev_is_pci(parent)) 87 dest->parent = pci_find_resource(to_pci_dev(parent), dest); 88 } 89 90 static unsigned int acpi_platform_resource_count(struct acpi_resource *ares, void *data) 91 { 92 bool *has_resources = data; 93 94 *has_resources = true; 95 96 return AE_CTRL_TERMINATE; 97 } 98 99 /** 100 * acpi_create_platform_device - Create platform device for ACPI device node 101 * @adev: ACPI device node to create a platform device for. 102 * @properties: Optional collection of build-in properties. 103 * 104 * Check if the given @adev can be represented as a platform device and, if 105 * that's the case, create and register a platform device, populate its common 106 * resources and returns a pointer to it. Otherwise, return %NULL. 107 * 108 * Name of the platform device will be the same as @adev's. 109 */ 110 struct platform_device *acpi_create_platform_device(struct acpi_device *adev, 111 const struct property_entry *properties) 112 { 113 struct acpi_device *parent = acpi_dev_parent(adev); 114 struct platform_device *pdev = NULL; 115 struct platform_device_info pdevinfo; 116 const struct acpi_device_id *match; 117 struct resource_entry *rentry; 118 struct list_head resource_list; 119 struct resource *resources = NULL; 120 int count; 121 122 /* If the ACPI node already has a physical device attached, skip it. */ 123 if (adev->physical_node_count) 124 return NULL; 125 126 match = acpi_match_acpi_device(forbidden_id_list, adev); 127 if (match) { 128 if (match->driver_data & ACPI_ALLOW_WO_RESOURCES) { 129 bool has_resources = false; 130 131 acpi_walk_resources(adev->handle, METHOD_NAME__CRS, 132 acpi_platform_resource_count, &has_resources); 133 if (has_resources) 134 return ERR_PTR(-EINVAL); 135 } else { 136 return ERR_PTR(-EINVAL); 137 } 138 } 139 140 INIT_LIST_HEAD(&resource_list); 141 count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 142 if (count < 0) 143 return NULL; 144 if (count > 0) { 145 resources = kcalloc(count, sizeof(*resources), GFP_KERNEL); 146 if (!resources) { 147 acpi_dev_free_resource_list(&resource_list); 148 return ERR_PTR(-ENOMEM); 149 } 150 count = 0; 151 list_for_each_entry(rentry, &resource_list, node) 152 acpi_platform_fill_resource(adev, rentry->res, 153 &resources[count++]); 154 155 acpi_dev_free_resource_list(&resource_list); 156 } 157 158 memset(&pdevinfo, 0, sizeof(pdevinfo)); 159 /* 160 * If the ACPI node has a parent and that parent has a physical device 161 * attached to it, that physical device should be the parent of the 162 * platform device we are about to create. 163 */ 164 pdevinfo.parent = parent ? acpi_get_first_physical_node(parent) : NULL; 165 pdevinfo.name = dev_name(&adev->dev); 166 pdevinfo.id = PLATFORM_DEVID_NONE; 167 pdevinfo.res = resources; 168 pdevinfo.num_res = count; 169 pdevinfo.fwnode = acpi_fwnode_handle(adev); 170 pdevinfo.properties = properties; 171 172 if (acpi_dma_supported(adev)) 173 pdevinfo.dma_mask = DMA_BIT_MASK(32); 174 else 175 pdevinfo.dma_mask = 0; 176 177 pdev = platform_device_register_full(&pdevinfo); 178 if (IS_ERR(pdev)) 179 dev_err(&adev->dev, "platform device creation failed: %ld\n", 180 PTR_ERR(pdev)); 181 else { 182 set_dev_node(&pdev->dev, acpi_get_node(adev->handle)); 183 dev_dbg(&adev->dev, "created platform device %s\n", 184 dev_name(&pdev->dev)); 185 } 186 187 kfree(resources); 188 189 return pdev; 190 } 191 EXPORT_SYMBOL_GPL(acpi_create_platform_device); 192 193 void __init acpi_platform_init(void) 194 { 195 acpi_reconfig_notifier_register(&acpi_platform_notifier); 196 } 197