141a297bd0
Currently, the ACPI namespace scanning code creates platform device objects for ACPI device nodes whose IDs match the contents of the acpi_platform_device_ids[] table. However, this adds a superfluous special case into acpi_bus_device_attach() and makes it more difficult to follow than it has to be. It also will make it more difficult to implement removal code for those platform device objects in the future. For the above reasons, introduce a struct acpi_scan_handler object for creating platform devices and move the code related to that from acpi_bus_device_attach() to the .attach() callback of that object. Also move the acpi_platform_device_ids[] table to acpi_platform.c. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Yinghai Lu <yinghai@kernel.org> Acked-by: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com> Acked-by: Toshi Kani <toshi.kani@hp.com> Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com> Tested-by: Mika Westerberg <mika.westerberg@linux.intel.com>
165 lines
4.4 KiB
C
165 lines
4.4 KiB
C
/*
|
|
* ACPI support for platform bus type.
|
|
*
|
|
* Copyright (C) 2012, Intel Corporation
|
|
* Authors: Mika Westerberg <mika.westerberg@linux.intel.com>
|
|
* Mathias Nyman <mathias.nyman@linux.intel.com>
|
|
* Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/acpi.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include "internal.h"
|
|
|
|
ACPI_MODULE_NAME("platform");
|
|
|
|
/* Flags for acpi_create_platform_device */
|
|
#define ACPI_PLATFORM_CLK BIT(0)
|
|
|
|
/*
|
|
* The following ACPI IDs are known to be suitable for representing as
|
|
* platform devices.
|
|
*/
|
|
static const struct acpi_device_id acpi_platform_device_ids[] = {
|
|
|
|
{ "PNP0D40" },
|
|
|
|
/* Haswell LPSS devices */
|
|
{ "INT33C0", ACPI_PLATFORM_CLK },
|
|
{ "INT33C1", ACPI_PLATFORM_CLK },
|
|
{ "INT33C2", ACPI_PLATFORM_CLK },
|
|
{ "INT33C3", ACPI_PLATFORM_CLK },
|
|
{ "INT33C4", ACPI_PLATFORM_CLK },
|
|
{ "INT33C5", ACPI_PLATFORM_CLK },
|
|
{ "INT33C6", ACPI_PLATFORM_CLK },
|
|
{ "INT33C7", ACPI_PLATFORM_CLK },
|
|
|
|
{ }
|
|
};
|
|
|
|
static int acpi_create_platform_clks(struct acpi_device *adev)
|
|
{
|
|
static struct platform_device *pdev;
|
|
|
|
/* Create Lynxpoint LPSS clocks */
|
|
if (!pdev && !strncmp(acpi_device_hid(adev), "INT33C", 6)) {
|
|
pdev = platform_device_register_simple("clk-lpt", -1, NULL, 0);
|
|
if (IS_ERR(pdev))
|
|
return PTR_ERR(pdev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* acpi_create_platform_device - Create platform device for ACPI device node
|
|
* @adev: ACPI device node to create a platform device for.
|
|
* @id: ACPI device ID used to match @adev.
|
|
*
|
|
* Check if the given @adev can be represented as a platform device and, if
|
|
* that's the case, create and register a platform device, populate its common
|
|
* resources and returns a pointer to it. Otherwise, return %NULL.
|
|
*
|
|
* Name of the platform device will be the same as @adev's.
|
|
*/
|
|
static int acpi_create_platform_device(struct acpi_device *adev,
|
|
const struct acpi_device_id *id)
|
|
{
|
|
unsigned long flags = id->driver_data;
|
|
struct platform_device *pdev = NULL;
|
|
struct acpi_device *acpi_parent;
|
|
struct platform_device_info pdevinfo;
|
|
struct resource_list_entry *rentry;
|
|
struct list_head resource_list;
|
|
struct resource *resources;
|
|
int count;
|
|
|
|
if (flags & ACPI_PLATFORM_CLK) {
|
|
int ret = acpi_create_platform_clks(adev);
|
|
if (ret) {
|
|
dev_err(&adev->dev, "failed to create clocks\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/* If the ACPI node already has a physical device attached, skip it. */
|
|
if (adev->physical_node_count)
|
|
return 0;
|
|
|
|
INIT_LIST_HEAD(&resource_list);
|
|
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
|
|
if (count <= 0)
|
|
return 0;
|
|
|
|
resources = kmalloc(count * sizeof(struct resource), GFP_KERNEL);
|
|
if (!resources) {
|
|
dev_err(&adev->dev, "No memory for resources\n");
|
|
acpi_dev_free_resource_list(&resource_list);
|
|
return -ENOMEM;
|
|
}
|
|
count = 0;
|
|
list_for_each_entry(rentry, &resource_list, node)
|
|
resources[count++] = rentry->res;
|
|
|
|
acpi_dev_free_resource_list(&resource_list);
|
|
|
|
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
|
/*
|
|
* If the ACPI node has a parent and that parent has a physical device
|
|
* attached to it, that physical device should be the parent of the
|
|
* platform device we are about to create.
|
|
*/
|
|
pdevinfo.parent = NULL;
|
|
acpi_parent = adev->parent;
|
|
if (acpi_parent) {
|
|
struct acpi_device_physical_node *entry;
|
|
struct list_head *list;
|
|
|
|
mutex_lock(&acpi_parent->physical_node_lock);
|
|
list = &acpi_parent->physical_node_list;
|
|
if (!list_empty(list)) {
|
|
entry = list_first_entry(list,
|
|
struct acpi_device_physical_node,
|
|
node);
|
|
pdevinfo.parent = entry->dev;
|
|
}
|
|
mutex_unlock(&acpi_parent->physical_node_lock);
|
|
}
|
|
pdevinfo.name = dev_name(&adev->dev);
|
|
pdevinfo.id = -1;
|
|
pdevinfo.res = resources;
|
|
pdevinfo.num_res = count;
|
|
pdevinfo.acpi_node.handle = adev->handle;
|
|
pdev = platform_device_register_full(&pdevinfo);
|
|
if (IS_ERR(pdev)) {
|
|
dev_err(&adev->dev, "platform device creation failed: %ld\n",
|
|
PTR_ERR(pdev));
|
|
pdev = NULL;
|
|
} else {
|
|
dev_dbg(&adev->dev, "created platform device %s\n",
|
|
dev_name(&pdev->dev));
|
|
}
|
|
|
|
kfree(resources);
|
|
return 1;
|
|
}
|
|
|
|
static struct acpi_scan_handler platform_handler = {
|
|
.ids = acpi_platform_device_ids,
|
|
.attach = acpi_create_platform_device,
|
|
};
|
|
|
|
void __init acpi_platform_init(void)
|
|
{
|
|
acpi_scan_add_handler(&platform_handler);
|
|
}
|