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 *resources = NULL; 118 int count = 0; 119 120 /* If the ACPI node already has a physical device attached, skip it. */ 121 if (adev->physical_node_count && !adev->pnp.type.backlight) 122 return NULL; 123 124 match = acpi_match_acpi_device(forbidden_id_list, adev); 125 if (match) { 126 if (match->driver_data & ACPI_ALLOW_WO_RESOURCES) { 127 bool has_resources = false; 128 129 acpi_walk_resources(adev->handle, METHOD_NAME__CRS, 130 acpi_platform_resource_count, &has_resources); 131 if (has_resources) 132 return ERR_PTR(-EINVAL); 133 } else { 134 return ERR_PTR(-EINVAL); 135 } 136 } 137 138 if (adev->device_type == ACPI_BUS_TYPE_DEVICE && !adev->pnp.type.backlight) { 139 LIST_HEAD(resource_list); 140 141 count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 142 if (count < 0) 143 return ERR_PTR(-ENODATA); 144 145 if (count > 0) { 146 struct resource_entry *rentry; 147 148 resources = kcalloc(count, sizeof(*resources), GFP_KERNEL); 149 if (!resources) { 150 acpi_dev_free_resource_list(&resource_list); 151 return ERR_PTR(-ENOMEM); 152 } 153 count = 0; 154 list_for_each_entry(rentry, &resource_list, node) 155 acpi_platform_fill_resource(adev, rentry->res, 156 &resources[count++]); 157 158 acpi_dev_free_resource_list(&resource_list); 159 } 160 } 161 162 memset(&pdevinfo, 0, sizeof(pdevinfo)); 163 /* 164 * If the ACPI node has a parent and that parent has a physical device 165 * attached to it, that physical device should be the parent of the 166 * platform device we are about to create. 167 */ 168 pdevinfo.parent = parent ? acpi_get_first_physical_node(parent) : NULL; 169 pdevinfo.name = dev_name(&adev->dev); 170 pdevinfo.id = PLATFORM_DEVID_NONE; 171 pdevinfo.res = resources; 172 pdevinfo.num_res = count; 173 pdevinfo.fwnode = acpi_fwnode_handle(adev); 174 pdevinfo.properties = properties; 175 176 if (acpi_dma_supported(adev)) 177 pdevinfo.dma_mask = DMA_BIT_MASK(32); 178 else 179 pdevinfo.dma_mask = 0; 180 181 pdev = platform_device_register_full(&pdevinfo); 182 if (IS_ERR(pdev)) 183 dev_err(&adev->dev, "platform device creation failed: %ld\n", 184 PTR_ERR(pdev)); 185 else { 186 set_dev_node(&pdev->dev, acpi_get_node(adev->handle)); 187 dev_dbg(&adev->dev, "created platform device %s\n", 188 dev_name(&pdev->dev)); 189 } 190 191 kfree(resources); 192 193 return pdev; 194 } 195 EXPORT_SYMBOL_GPL(acpi_create_platform_device); 196 197 void __init acpi_platform_init(void) 198 { 199 acpi_reconfig_notifier_register(&acpi_platform_notifier); 200 } 201