1 /* 2 * ACPI support for platform bus type. 3 * 4 * Copyright (C) 2012, Intel Corporation 5 * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> 6 * Mathias Nyman <mathias.nyman@linux.intel.com> 7 * Rafael J. Wysocki <rafael.j.wysocki@intel.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 */ 13 14 #include <linux/acpi.h> 15 #include <linux/device.h> 16 #include <linux/err.h> 17 #include <linux/kernel.h> 18 #include <linux/module.h> 19 #include <linux/platform_device.h> 20 21 #include "internal.h" 22 23 ACPI_MODULE_NAME("platform"); 24 25 /* 26 * The following ACPI IDs are known to be suitable for representing as 27 * platform devices. 28 */ 29 static const struct acpi_device_id acpi_platform_device_ids[] = { 30 31 { "PNP0D40" }, 32 33 { } 34 }; 35 36 /** 37 * acpi_create_platform_device - Create platform device for ACPI device node 38 * @adev: ACPI device node to create a platform device for. 39 * @id: ACPI device ID used to match @adev. 40 * 41 * Check if the given @adev can be represented as a platform device and, if 42 * that's the case, create and register a platform device, populate its common 43 * resources and returns a pointer to it. Otherwise, return %NULL. 44 * 45 * Name of the platform device will be the same as @adev's. 46 */ 47 int acpi_create_platform_device(struct acpi_device *adev, 48 const struct acpi_device_id *id) 49 { 50 struct platform_device *pdev = NULL; 51 struct acpi_device *acpi_parent; 52 struct platform_device_info pdevinfo; 53 struct resource_list_entry *rentry; 54 struct list_head resource_list; 55 struct resource *resources = NULL; 56 int count; 57 58 /* If the ACPI node already has a physical device attached, skip it. */ 59 if (adev->physical_node_count) 60 return 0; 61 62 INIT_LIST_HEAD(&resource_list); 63 count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 64 if (count < 0) { 65 return 0; 66 } else if (count > 0) { 67 resources = kmalloc(count * sizeof(struct resource), 68 GFP_KERNEL); 69 if (!resources) { 70 dev_err(&adev->dev, "No memory for resources\n"); 71 acpi_dev_free_resource_list(&resource_list); 72 return -ENOMEM; 73 } 74 count = 0; 75 list_for_each_entry(rentry, &resource_list, node) 76 resources[count++] = rentry->res; 77 78 acpi_dev_free_resource_list(&resource_list); 79 } 80 81 memset(&pdevinfo, 0, sizeof(pdevinfo)); 82 /* 83 * If the ACPI node has a parent and that parent has a physical device 84 * attached to it, that physical device should be the parent of the 85 * platform device we are about to create. 86 */ 87 pdevinfo.parent = NULL; 88 acpi_parent = adev->parent; 89 if (acpi_parent) { 90 struct acpi_device_physical_node *entry; 91 struct list_head *list; 92 93 mutex_lock(&acpi_parent->physical_node_lock); 94 list = &acpi_parent->physical_node_list; 95 if (!list_empty(list)) { 96 entry = list_first_entry(list, 97 struct acpi_device_physical_node, 98 node); 99 pdevinfo.parent = entry->dev; 100 } 101 mutex_unlock(&acpi_parent->physical_node_lock); 102 } 103 pdevinfo.name = dev_name(&adev->dev); 104 pdevinfo.id = -1; 105 pdevinfo.res = resources; 106 pdevinfo.num_res = count; 107 pdevinfo.acpi_node.handle = adev->handle; 108 pdev = platform_device_register_full(&pdevinfo); 109 if (IS_ERR(pdev)) { 110 dev_err(&adev->dev, "platform device creation failed: %ld\n", 111 PTR_ERR(pdev)); 112 pdev = NULL; 113 } else { 114 dev_dbg(&adev->dev, "created platform device %s\n", 115 dev_name(&pdev->dev)); 116 } 117 118 kfree(resources); 119 return 1; 120 } 121 122 static struct acpi_scan_handler platform_handler = { 123 .ids = acpi_platform_device_ids, 124 .attach = acpi_create_platform_device, 125 }; 126 127 void __init acpi_platform_init(void) 128 { 129 acpi_scan_add_handler(&platform_handler); 130 } 131