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 /* Flags for acpi_create_platform_device */ 26 #define ACPI_PLATFORM_CLK BIT(0) 27 28 /* 29 * The following ACPI IDs are known to be suitable for representing as 30 * platform devices. 31 */ 32 static const struct acpi_device_id acpi_platform_device_ids[] = { 33 34 { "PNP0D40" }, 35 36 /* Haswell LPSS devices */ 37 { "INT33C0", ACPI_PLATFORM_CLK }, 38 { "INT33C1", ACPI_PLATFORM_CLK }, 39 { "INT33C2", ACPI_PLATFORM_CLK }, 40 { "INT33C3", ACPI_PLATFORM_CLK }, 41 { "INT33C4", ACPI_PLATFORM_CLK }, 42 { "INT33C5", ACPI_PLATFORM_CLK }, 43 { "INT33C6", ACPI_PLATFORM_CLK }, 44 { "INT33C7", ACPI_PLATFORM_CLK }, 45 46 { } 47 }; 48 49 static int acpi_create_platform_clks(struct acpi_device *adev) 50 { 51 static struct platform_device *pdev; 52 53 /* Create Lynxpoint LPSS clocks */ 54 if (!pdev && !strncmp(acpi_device_hid(adev), "INT33C", 6)) { 55 pdev = platform_device_register_simple("clk-lpt", -1, NULL, 0); 56 if (IS_ERR(pdev)) 57 return PTR_ERR(pdev); 58 } 59 60 return 0; 61 } 62 63 /** 64 * acpi_create_platform_device - Create platform device for ACPI device node 65 * @adev: ACPI device node to create a platform device for. 66 * @id: ACPI device ID used to match @adev. 67 * 68 * Check if the given @adev can be represented as a platform device and, if 69 * that's the case, create and register a platform device, populate its common 70 * resources and returns a pointer to it. Otherwise, return %NULL. 71 * 72 * Name of the platform device will be the same as @adev's. 73 */ 74 static int acpi_create_platform_device(struct acpi_device *adev, 75 const struct acpi_device_id *id) 76 { 77 unsigned long flags = id->driver_data; 78 struct platform_device *pdev = NULL; 79 struct acpi_device *acpi_parent; 80 struct platform_device_info pdevinfo; 81 struct resource_list_entry *rentry; 82 struct list_head resource_list; 83 struct resource *resources; 84 int count; 85 86 if (flags & ACPI_PLATFORM_CLK) { 87 int ret = acpi_create_platform_clks(adev); 88 if (ret) { 89 dev_err(&adev->dev, "failed to create clocks\n"); 90 return ret; 91 } 92 } 93 94 /* If the ACPI node already has a physical device attached, skip it. */ 95 if (adev->physical_node_count) 96 return 0; 97 98 INIT_LIST_HEAD(&resource_list); 99 count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); 100 if (count <= 0) 101 return 0; 102 103 resources = kmalloc(count * sizeof(struct resource), GFP_KERNEL); 104 if (!resources) { 105 dev_err(&adev->dev, "No memory for resources\n"); 106 acpi_dev_free_resource_list(&resource_list); 107 return -ENOMEM; 108 } 109 count = 0; 110 list_for_each_entry(rentry, &resource_list, node) 111 resources[count++] = rentry->res; 112 113 acpi_dev_free_resource_list(&resource_list); 114 115 memset(&pdevinfo, 0, sizeof(pdevinfo)); 116 /* 117 * If the ACPI node has a parent and that parent has a physical device 118 * attached to it, that physical device should be the parent of the 119 * platform device we are about to create. 120 */ 121 pdevinfo.parent = NULL; 122 acpi_parent = adev->parent; 123 if (acpi_parent) { 124 struct acpi_device_physical_node *entry; 125 struct list_head *list; 126 127 mutex_lock(&acpi_parent->physical_node_lock); 128 list = &acpi_parent->physical_node_list; 129 if (!list_empty(list)) { 130 entry = list_first_entry(list, 131 struct acpi_device_physical_node, 132 node); 133 pdevinfo.parent = entry->dev; 134 } 135 mutex_unlock(&acpi_parent->physical_node_lock); 136 } 137 pdevinfo.name = dev_name(&adev->dev); 138 pdevinfo.id = -1; 139 pdevinfo.res = resources; 140 pdevinfo.num_res = count; 141 pdevinfo.acpi_node.handle = adev->handle; 142 pdev = platform_device_register_full(&pdevinfo); 143 if (IS_ERR(pdev)) { 144 dev_err(&adev->dev, "platform device creation failed: %ld\n", 145 PTR_ERR(pdev)); 146 pdev = NULL; 147 } else { 148 dev_dbg(&adev->dev, "created platform device %s\n", 149 dev_name(&pdev->dev)); 150 } 151 152 kfree(resources); 153 return 1; 154 } 155 156 static struct acpi_scan_handler platform_handler = { 157 .ids = acpi_platform_device_ids, 158 .attach = acpi_create_platform_device, 159 }; 160 161 void __init acpi_platform_init(void) 162 { 163 acpi_scan_add_handler(&platform_handler); 164 } 165