linux/drivers/acpi/acpi_platform.c
<<
>>
Prefs
   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
  23ACPI_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 */
  32static 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
  49static 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 */
  74static 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
 156static struct acpi_scan_handler platform_handler = {
 157        .ids = acpi_platform_device_ids,
 158        .attach = acpi_create_platform_device,
 159};
 160
 161void __init acpi_platform_init(void)
 162{
 163        acpi_scan_add_handler(&platform_handler);
 164}
 165