Merge branch 'acpi-enumeration'
* acpi-enumeration: ACPI: remove unnecessary INIT_LIST_HEAD ACPI / platform: include missed header into acpi_platform.c platform / ACPI: Attach/detach ACPI PM during probe/remove/shutdown mmc: sdhci-acpi: add SDHCI ACPI driver ACPI: add SDHCI to ACPI platform devices ACPI / PNP: skip ACPI device nodes associated with physical nodes already i2c / ACPI: add ACPI enumeration support ACPI / platform: Initialize ACPI handles of platform devices in advance ACPI / driver core: Introduce struct acpi_dev_node and related macros ACPI: Allow ACPI handles of devices to be initialized in advance ACPI / resources: Use AE_CTRL_TERMINATE to terminate resources walks ACPI: Centralized processing of ACPI device resources ACPI / platform: Use common ACPI device resource parsing routines ACPI: Move device resources interpretation code from PNP to ACPI core ACPI / platform: use ACPI device name instead of _HID._UID ACPI: Add support for platform bus type ACPI / ia64: Export acpi_[un]register_gsi() ACPI / x86: Export acpi_[un]register_gsi() ACPI: Provide generic functions for matching ACPI device nodes driver core / ACPI: Move ACPI support to core device and driver types
This commit is contained in:
commit
bcacbdbdc8
27 changed files with 1326 additions and 301 deletions
|
@ -7,9 +7,6 @@
|
|||
#define _ASM_IA64_DEVICE_H
|
||||
|
||||
struct dev_archdata {
|
||||
#ifdef CONFIG_ACPI
|
||||
void *acpi_handle;
|
||||
#endif
|
||||
#ifdef CONFIG_INTEL_IOMMU
|
||||
void *iommu; /* hook for IOMMU specific extension */
|
||||
#endif
|
||||
|
|
|
@ -633,6 +633,7 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int triggering, int polarity)
|
|||
ACPI_EDGE_SENSITIVE) ? IOSAPIC_EDGE :
|
||||
IOSAPIC_LEVEL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_register_gsi);
|
||||
|
||||
void acpi_unregister_gsi(u32 gsi)
|
||||
{
|
||||
|
@ -644,6 +645,7 @@ void acpi_unregister_gsi(u32 gsi)
|
|||
|
||||
iosapic_unregister_intr(gsi);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
|
||||
|
||||
static int __init acpi_parse_fadt(struct acpi_table_header *table)
|
||||
{
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
#define _ASM_X86_DEVICE_H
|
||||
|
||||
struct dev_archdata {
|
||||
#ifdef CONFIG_ACPI
|
||||
void *acpi_handle;
|
||||
#endif
|
||||
#ifdef CONFIG_X86_DEV_DMA_OPS
|
||||
struct dma_map_ops *dma_ops;
|
||||
#endif
|
||||
|
|
|
@ -574,6 +574,12 @@ int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
|
|||
|
||||
return irq;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_register_gsi);
|
||||
|
||||
void acpi_unregister_gsi(u32 gsi)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
|
||||
|
||||
void __init acpi_set_irq_model_pic(void)
|
||||
{
|
||||
|
|
|
@ -181,6 +181,12 @@ config ACPI_DOCK
|
|||
This driver supports ACPI-controlled docking stations and removable
|
||||
drive bays such as the IBM Ultrabay and the Dell Module Bay.
|
||||
|
||||
config ACPI_I2C
|
||||
def_tristate I2C
|
||||
depends on I2C
|
||||
help
|
||||
ACPI I2C enumeration support.
|
||||
|
||||
config ACPI_PROCESSOR
|
||||
tristate "Processor"
|
||||
select THERMAL
|
||||
|
|
|
@ -33,10 +33,12 @@ acpi-$(CONFIG_ACPI_SLEEP) += proc.o
|
|||
#
|
||||
acpi-y += bus.o glue.o
|
||||
acpi-y += scan.o
|
||||
acpi-y += resource.o
|
||||
acpi-y += processor_core.o
|
||||
acpi-y += ec.o
|
||||
acpi-$(CONFIG_ACPI_DOCK) += dock.o
|
||||
acpi-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
|
||||
acpi-y += acpi_platform.o
|
||||
acpi-y += power.o
|
||||
acpi-y += event.o
|
||||
acpi-y += sysfs.o
|
||||
|
@ -68,6 +70,7 @@ obj-$(CONFIG_ACPI_HED) += hed.o
|
|||
obj-$(CONFIG_ACPI_EC_DEBUGFS) += ec_sys.o
|
||||
obj-$(CONFIG_ACPI_CUSTOM_METHOD)+= custom_method.o
|
||||
obj-$(CONFIG_ACPI_BGRT) += bgrt.o
|
||||
obj-$(CONFIG_ACPI_I2C) += acpi_i2c.o
|
||||
|
||||
# processor has its own "processor." module_param namespace
|
||||
processor-y := processor_driver.o processor_throttling.o
|
||||
|
|
103
drivers/acpi/acpi_i2c.c
Normal file
103
drivers/acpi/acpi_i2c.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* ACPI I2C enumeration support
|
||||
*
|
||||
* Copyright (C) 2012, Intel Corporation
|
||||
* Author: Mika Westerberg <mika.westerberg@linux.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/export.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
ACPI_MODULE_NAME("i2c");
|
||||
|
||||
static int acpi_i2c_add_resource(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
struct i2c_board_info *info = data;
|
||||
|
||||
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||
struct acpi_resource_i2c_serialbus *sb;
|
||||
|
||||
sb = &ares->data.i2c_serial_bus;
|
||||
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||
info->addr = sb->slave_address;
|
||||
if (sb->access_mode == ACPI_I2C_10BIT_MODE)
|
||||
info->flags |= I2C_CLIENT_TEN;
|
||||
}
|
||||
} else if (info->irq < 0) {
|
||||
struct resource r;
|
||||
|
||||
if (acpi_dev_resource_interrupt(ares, 0, &r))
|
||||
info->irq = r.start;
|
||||
}
|
||||
|
||||
/* Tell the ACPI core to skip this resource */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level,
|
||||
void *data, void **return_value)
|
||||
{
|
||||
struct i2c_adapter *adapter = data;
|
||||
struct list_head resource_list;
|
||||
struct i2c_board_info info;
|
||||
struct acpi_device *adev;
|
||||
int ret;
|
||||
|
||||
if (acpi_bus_get_device(handle, &adev))
|
||||
return AE_OK;
|
||||
if (acpi_bus_get_status(adev) || !adev->status.present)
|
||||
return AE_OK;
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.acpi_node.handle = handle;
|
||||
info.irq = -1;
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list,
|
||||
acpi_i2c_add_resource, &info);
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (ret < 0 || !info.addr)
|
||||
return AE_OK;
|
||||
|
||||
strlcpy(info.type, dev_name(&adev->dev), sizeof(info.type));
|
||||
if (!i2c_new_device(adapter, &info)) {
|
||||
dev_err(&adapter->dev,
|
||||
"failed to add I2C device %s from ACPI\n",
|
||||
dev_name(&adev->dev));
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_i2c_register_devices - enumerate I2C slave devices behind adapter
|
||||
* @adapter: pointer to adapter
|
||||
*
|
||||
* Enumerate all I2C slave devices behind this adapter by walking the ACPI
|
||||
* namespace. When a device is found it will be added to the Linux device
|
||||
* model and bound to the corresponding ACPI handle.
|
||||
*/
|
||||
void acpi_i2c_register_devices(struct i2c_adapter *adapter)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
|
||||
handle = ACPI_HANDLE(&adapter->dev);
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
|
||||
acpi_i2c_add_device, NULL,
|
||||
adapter, NULL);
|
||||
if (ACPI_FAILURE(status))
|
||||
dev_warn(&adapter->dev, "failed to enumerate I2C slaves\n");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_i2c_register_devices);
|
104
drivers/acpi/acpi_platform.c
Normal file
104
drivers/acpi/acpi_platform.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
ACPI_MODULE_NAME("platform");
|
||||
|
||||
/**
|
||||
* acpi_create_platform_device - Create platform device for ACPI device node
|
||||
* @adev: ACPI device node to create a platform device for.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The platform device's name will be taken from the @adev's _HID and _UID.
|
||||
*/
|
||||
struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
|
||||
{
|
||||
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 the ACPI node already has a physical device attached, skip it. */
|
||||
if (adev->physical_node_count)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);
|
||||
if (count <= 0)
|
||||
return NULL;
|
||||
|
||||
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 NULL;
|
||||
}
|
||||
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 pdev;
|
||||
}
|
|
@ -130,46 +130,59 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
|
|||
{
|
||||
struct acpi_device *acpi_dev;
|
||||
acpi_status status;
|
||||
struct acpi_device_physical_node *physical_node;
|
||||
struct acpi_device_physical_node *physical_node, *pn;
|
||||
char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2];
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (dev->archdata.acpi_handle) {
|
||||
dev_warn(dev, "Drivers changed 'acpi_handle'\n");
|
||||
return -EINVAL;
|
||||
if (ACPI_HANDLE(dev)) {
|
||||
if (handle) {
|
||||
dev_warn(dev, "ACPI handle is already set\n");
|
||||
return -EINVAL;
|
||||
} else {
|
||||
handle = ACPI_HANDLE(dev);
|
||||
}
|
||||
}
|
||||
if (!handle)
|
||||
return -EINVAL;
|
||||
|
||||
get_device(dev);
|
||||
status = acpi_bus_get_device(handle, &acpi_dev);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto err;
|
||||
|
||||
physical_node = kzalloc(sizeof(struct acpi_device_physical_node),
|
||||
GFP_KERNEL);
|
||||
physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
|
||||
if (!physical_node) {
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
mutex_lock(&acpi_dev->physical_node_lock);
|
||||
|
||||
/* Sanity check. */
|
||||
list_for_each_entry(pn, &acpi_dev->physical_node_list, node)
|
||||
if (pn->dev == dev) {
|
||||
dev_warn(dev, "Already associated with ACPI node\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
/* allocate physical node id according to physical_node_id_bitmap */
|
||||
physical_node->node_id =
|
||||
find_first_zero_bit(acpi_dev->physical_node_id_bitmap,
|
||||
ACPI_MAX_PHYSICAL_NODE);
|
||||
if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) {
|
||||
retval = -ENOSPC;
|
||||
mutex_unlock(&acpi_dev->physical_node_lock);
|
||||
kfree(physical_node);
|
||||
goto err;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap);
|
||||
physical_node->dev = dev;
|
||||
list_add_tail(&physical_node->node, &acpi_dev->physical_node_list);
|
||||
acpi_dev->physical_node_count++;
|
||||
|
||||
mutex_unlock(&acpi_dev->physical_node_lock);
|
||||
|
||||
dev->archdata.acpi_handle = handle;
|
||||
if (!ACPI_HANDLE(dev))
|
||||
ACPI_HANDLE_SET(dev, acpi_dev->handle);
|
||||
|
||||
if (!physical_node->node_id)
|
||||
strcpy(physical_node_name, PHYSICAL_NODE_STRING);
|
||||
|
@ -187,8 +200,14 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
|
|||
return 0;
|
||||
|
||||
err:
|
||||
ACPI_HANDLE_SET(dev, NULL);
|
||||
put_device(dev);
|
||||
return retval;
|
||||
|
||||
err_free:
|
||||
mutex_unlock(&acpi_dev->physical_node_lock);
|
||||
kfree(physical_node);
|
||||
goto err;
|
||||
}
|
||||
|
||||
static int acpi_unbind_one(struct device *dev)
|
||||
|
@ -198,11 +217,10 @@ static int acpi_unbind_one(struct device *dev)
|
|||
acpi_status status;
|
||||
struct list_head *node, *next;
|
||||
|
||||
if (!dev->archdata.acpi_handle)
|
||||
if (!ACPI_HANDLE(dev))
|
||||
return 0;
|
||||
|
||||
status = acpi_bus_get_device(dev->archdata.acpi_handle,
|
||||
&acpi_dev);
|
||||
status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev);
|
||||
if (ACPI_FAILURE(status))
|
||||
goto err;
|
||||
|
||||
|
@ -228,7 +246,7 @@ static int acpi_unbind_one(struct device *dev)
|
|||
|
||||
sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name);
|
||||
sysfs_remove_link(&dev->kobj, "firmware_node");
|
||||
dev->archdata.acpi_handle = NULL;
|
||||
ACPI_HANDLE_SET(dev, NULL);
|
||||
/* acpi_bind_one increase refcnt by one */
|
||||
put_device(dev);
|
||||
kfree(entry);
|
||||
|
@ -248,6 +266,10 @@ static int acpi_platform_notify(struct device *dev)
|
|||
acpi_handle handle;
|
||||
int ret = -EINVAL;
|
||||
|
||||
ret = acpi_bind_one(dev, NULL);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
if (!dev->bus || !dev->parent) {
|
||||
/* bridge devices genernally haven't bus or parent */
|
||||
ret = acpi_find_bridge_device(dev, &handle);
|
||||
|
@ -261,16 +283,16 @@ static int acpi_platform_notify(struct device *dev)
|
|||
}
|
||||
if ((ret = type->find_device(dev, &handle)) != 0)
|
||||
DBG("Can't get handler for %s\n", dev_name(dev));
|
||||
end:
|
||||
end:
|
||||
if (!ret)
|
||||
acpi_bind_one(dev, handle);
|
||||
|
||||
out:
|
||||
#if ACPI_GLUE_DEBUG
|
||||
if (!ret) {
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
acpi_get_name(dev->archdata.acpi_handle,
|
||||
ACPI_FULL_PATHNAME, &buffer);
|
||||
acpi_get_name(dev->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
|
||||
DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
|
||||
kfree(buffer.pointer);
|
||||
} else
|
||||
|
|
|
@ -93,4 +93,11 @@ static inline int suspend_nvs_save(void) { return 0; }
|
|||
static inline void suspend_nvs_restore(void) {}
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
Platform bus support
|
||||
-------------------------------------------------------------------------- */
|
||||
struct platform_device;
|
||||
|
||||
struct platform_device *acpi_create_platform_device(struct acpi_device *adev);
|
||||
|
||||
#endif /* _ACPI_INTERNAL_H_ */
|
||||
|
|
|
@ -495,11 +495,6 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME: implement x86/x86_64 version */
|
||||
void __attribute__ ((weak)) acpi_unregister_gsi(u32 i)
|
||||
{
|
||||
}
|
||||
|
||||
void acpi_pci_irq_disable(struct pci_dev *dev)
|
||||
{
|
||||
struct acpi_prt_entry *entry;
|
||||
|
|
526
drivers/acpi/resource.c
Normal file
526
drivers/acpi/resource.c
Normal file
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
* drivers/acpi/resource.c - ACPI device resources interpretation.
|
||||
*
|
||||
* Copyright (C) 2012, Intel Corp.
|
||||
* Author: 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
#define valid_IRQ(i) (((i) != 0) && ((i) != 2))
|
||||
#else
|
||||
#define valid_IRQ(i) (true)
|
||||
#endif
|
||||
|
||||
static unsigned long acpi_dev_memresource_flags(u64 len, u8 write_protect,
|
||||
bool window)
|
||||
{
|
||||
unsigned long flags = IORESOURCE_MEM;
|
||||
|
||||
if (len == 0)
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
if (write_protect == ACPI_READ_WRITE_MEMORY)
|
||||
flags |= IORESOURCE_MEM_WRITEABLE;
|
||||
|
||||
if (window)
|
||||
flags |= IORESOURCE_WINDOW;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len,
|
||||
u8 write_protect)
|
||||
{
|
||||
res->start = start;
|
||||
res->end = start + len - 1;
|
||||
res->flags = acpi_dev_memresource_flags(len, write_protect, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dev_resource_memory - Extract ACPI memory resource information.
|
||||
* @ares: Input ACPI resource object.
|
||||
* @res: Output generic resource object.
|
||||
*
|
||||
* Check if the given ACPI resource object represents a memory resource and
|
||||
* if that's the case, use the information in it to populate the generic
|
||||
* resource object pointed to by @res.
|
||||
*/
|
||||
bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res)
|
||||
{
|
||||
struct acpi_resource_memory24 *memory24;
|
||||
struct acpi_resource_memory32 *memory32;
|
||||
struct acpi_resource_fixed_memory32 *fixed_memory32;
|
||||
|
||||
switch (ares->type) {
|
||||
case ACPI_RESOURCE_TYPE_MEMORY24:
|
||||
memory24 = &ares->data.memory24;
|
||||
acpi_dev_get_memresource(res, memory24->minimum,
|
||||
memory24->address_length,
|
||||
memory24->write_protect);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_MEMORY32:
|
||||
memory32 = &ares->data.memory32;
|
||||
acpi_dev_get_memresource(res, memory32->minimum,
|
||||
memory32->address_length,
|
||||
memory32->write_protect);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
|
||||
fixed_memory32 = &ares->data.fixed_memory32;
|
||||
acpi_dev_get_memresource(res, fixed_memory32->address,
|
||||
fixed_memory32->address_length,
|
||||
fixed_memory32->write_protect);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_resource_memory);
|
||||
|
||||
static unsigned int acpi_dev_ioresource_flags(u64 start, u64 end, u8 io_decode,
|
||||
bool window)
|
||||
{
|
||||
int flags = IORESOURCE_IO;
|
||||
|
||||
if (io_decode == ACPI_DECODE_16)
|
||||
flags |= IORESOURCE_IO_16BIT_ADDR;
|
||||
|
||||
if (start > end || end >= 0x10003)
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
if (window)
|
||||
flags |= IORESOURCE_WINDOW;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len,
|
||||
u8 io_decode)
|
||||
{
|
||||
u64 end = start + len - 1;
|
||||
|
||||
res->start = start;
|
||||
res->end = end;
|
||||
res->flags = acpi_dev_ioresource_flags(start, end, io_decode, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dev_resource_io - Extract ACPI I/O resource information.
|
||||
* @ares: Input ACPI resource object.
|
||||
* @res: Output generic resource object.
|
||||
*
|
||||
* Check if the given ACPI resource object represents an I/O resource and
|
||||
* if that's the case, use the information in it to populate the generic
|
||||
* resource object pointed to by @res.
|
||||
*/
|
||||
bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res)
|
||||
{
|
||||
struct acpi_resource_io *io;
|
||||
struct acpi_resource_fixed_io *fixed_io;
|
||||
|
||||
switch (ares->type) {
|
||||
case ACPI_RESOURCE_TYPE_IO:
|
||||
io = &ares->data.io;
|
||||
acpi_dev_get_ioresource(res, io->minimum,
|
||||
io->address_length,
|
||||
io->io_decode);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_FIXED_IO:
|
||||
fixed_io = &ares->data.fixed_io;
|
||||
acpi_dev_get_ioresource(res, fixed_io->address,
|
||||
fixed_io->address_length,
|
||||
ACPI_DECODE_10);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_resource_io);
|
||||
|
||||
/**
|
||||
* acpi_dev_resource_address_space - Extract ACPI address space information.
|
||||
* @ares: Input ACPI resource object.
|
||||
* @res: Output generic resource object.
|
||||
*
|
||||
* Check if the given ACPI resource object represents an address space resource
|
||||
* and if that's the case, use the information in it to populate the generic
|
||||
* resource object pointed to by @res.
|
||||
*/
|
||||
bool acpi_dev_resource_address_space(struct acpi_resource *ares,
|
||||
struct resource *res)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_resource_address64 addr;
|
||||
bool window;
|
||||
u64 len;
|
||||
u8 io_decode;
|
||||
|
||||
switch (ares->type) {
|
||||
case ACPI_RESOURCE_TYPE_ADDRESS16:
|
||||
case ACPI_RESOURCE_TYPE_ADDRESS32:
|
||||
case ACPI_RESOURCE_TYPE_ADDRESS64:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
status = acpi_resource_to_address64(ares, &addr);
|
||||
if (ACPI_FAILURE(status))
|
||||
return true;
|
||||
|
||||
res->start = addr.minimum;
|
||||
res->end = addr.maximum;
|
||||
window = addr.producer_consumer == ACPI_PRODUCER;
|
||||
|
||||
switch(addr.resource_type) {
|
||||
case ACPI_MEMORY_RANGE:
|
||||
len = addr.maximum - addr.minimum + 1;
|
||||
res->flags = acpi_dev_memresource_flags(len,
|
||||
addr.info.mem.write_protect,
|
||||
window);
|
||||
break;
|
||||
case ACPI_IO_RANGE:
|
||||
io_decode = addr.granularity == 0xfff ?
|
||||
ACPI_DECODE_10 : ACPI_DECODE_16;
|
||||
res->flags = acpi_dev_ioresource_flags(addr.minimum,
|
||||
addr.maximum,
|
||||
io_decode, window);
|
||||
break;
|
||||
case ACPI_BUS_NUMBER_RANGE:
|
||||
res->flags = IORESOURCE_BUS;
|
||||
break;
|
||||
default:
|
||||
res->flags = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space);
|
||||
|
||||
/**
|
||||
* acpi_dev_resource_ext_address_space - Extract ACPI address space information.
|
||||
* @ares: Input ACPI resource object.
|
||||
* @res: Output generic resource object.
|
||||
*
|
||||
* Check if the given ACPI resource object represents an extended address space
|
||||
* resource and if that's the case, use the information in it to populate the
|
||||
* generic resource object pointed to by @res.
|
||||
*/
|
||||
bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares,
|
||||
struct resource *res)
|
||||
{
|
||||
struct acpi_resource_extended_address64 *ext_addr;
|
||||
bool window;
|
||||
u64 len;
|
||||
u8 io_decode;
|
||||
|
||||
if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64)
|
||||
return false;
|
||||
|
||||
ext_addr = &ares->data.ext_address64;
|
||||
|
||||
res->start = ext_addr->minimum;
|
||||
res->end = ext_addr->maximum;
|
||||
window = ext_addr->producer_consumer == ACPI_PRODUCER;
|
||||
|
||||
switch(ext_addr->resource_type) {
|
||||
case ACPI_MEMORY_RANGE:
|
||||
len = ext_addr->maximum - ext_addr->minimum + 1;
|
||||
res->flags = acpi_dev_memresource_flags(len,
|
||||
ext_addr->info.mem.write_protect,
|
||||
window);
|
||||
break;
|
||||
case ACPI_IO_RANGE:
|
||||
io_decode = ext_addr->granularity == 0xfff ?
|
||||
ACPI_DECODE_10 : ACPI_DECODE_16;
|
||||
res->flags = acpi_dev_ioresource_flags(ext_addr->minimum,
|
||||
ext_addr->maximum,
|
||||
io_decode, window);
|
||||
break;
|
||||
case ACPI_BUS_NUMBER_RANGE:
|
||||
res->flags = IORESOURCE_BUS;
|
||||
break;
|
||||
default:
|
||||
res->flags = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space);
|
||||
|
||||
/**
|
||||
* acpi_dev_irq_flags - Determine IRQ resource flags.
|
||||
* @triggering: Triggering type as provided by ACPI.
|
||||
* @polarity: Interrupt polarity as provided by ACPI.
|
||||
* @shareable: Whether or not the interrupt is shareable.
|
||||
*/
|
||||
unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (triggering == ACPI_LEVEL_SENSITIVE)
|
||||
flags = polarity == ACPI_ACTIVE_LOW ?
|
||||
IORESOURCE_IRQ_LOWLEVEL : IORESOURCE_IRQ_HIGHLEVEL;
|
||||
else
|
||||
flags = polarity == ACPI_ACTIVE_LOW ?
|
||||
IORESOURCE_IRQ_LOWEDGE : IORESOURCE_IRQ_HIGHEDGE;
|
||||
|
||||
if (shareable == ACPI_SHARED)
|
||||
flags |= IORESOURCE_IRQ_SHAREABLE;
|
||||
|
||||
return flags | IORESOURCE_IRQ;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_irq_flags);
|
||||
|
||||
static void acpi_dev_irqresource_disabled(struct resource *res, u32 gsi)
|
||||
{
|
||||
res->start = gsi;
|
||||
res->end = gsi;
|
||||
res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED;
|
||||
}
|
||||
|
||||
static void acpi_dev_get_irqresource(struct resource *res, u32 gsi,
|
||||
u8 triggering, u8 polarity, u8 shareable)
|
||||
{
|
||||
int irq, p, t;
|
||||
|
||||
if (!valid_IRQ(gsi)) {
|
||||
acpi_dev_irqresource_disabled(res, gsi);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* In IO-APIC mode, use overrided attribute. Two reasons:
|
||||
* 1. BIOS bug in DSDT
|
||||
* 2. BIOS uses IO-APIC mode Interrupt Source Override
|
||||
*/
|
||||
if (!acpi_get_override_irq(gsi, &t, &p)) {
|
||||
u8 trig = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
|
||||
u8 pol = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH;
|
||||
|
||||
if (triggering != trig || polarity != pol) {
|
||||
pr_warning("ACPI: IRQ %d override to %s, %s\n", gsi,
|
||||
t ? "edge" : "level", p ? "low" : "high");
|
||||
triggering = trig;
|
||||
polarity = pol;
|
||||
}
|
||||
}
|
||||
|
||||
res->flags = acpi_dev_irq_flags(triggering, polarity, shareable);
|
||||
irq = acpi_register_gsi(NULL, gsi, triggering, polarity);
|
||||
if (irq >= 0) {
|
||||
res->start = irq;
|
||||
res->end = irq;
|
||||
} else {
|
||||
acpi_dev_irqresource_disabled(res, gsi);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dev_resource_interrupt - Extract ACPI interrupt resource information.
|
||||
* @ares: Input ACPI resource object.
|
||||
* @index: Index into the array of GSIs represented by the resource.
|
||||
* @res: Output generic resource object.
|
||||
*
|
||||
* Check if the given ACPI resource object represents an interrupt resource
|
||||
* and @index does not exceed the resource's interrupt count (true is returned
|
||||
* in that case regardless of the results of the other checks)). If that's the
|
||||
* case, register the GSI corresponding to @index from the array of interrupts
|
||||
* represented by the resource and populate the generic resource object pointed
|
||||
* to by @res accordingly. If the registration of the GSI is not successful,
|
||||
* IORESOURCE_DISABLED will be set it that object's flags.
|
||||
*/
|
||||
bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
|
||||
struct resource *res)
|
||||
{
|
||||
struct acpi_resource_irq *irq;
|
||||
struct acpi_resource_extended_irq *ext_irq;
|
||||
|
||||
switch (ares->type) {
|
||||
case ACPI_RESOURCE_TYPE_IRQ:
|
||||
/*
|
||||
* Per spec, only one interrupt per descriptor is allowed in
|
||||
* _CRS, but some firmware violates this, so parse them all.
|
||||
*/
|
||||
irq = &ares->data.irq;
|
||||
if (index >= irq->interrupt_count) {
|
||||
acpi_dev_irqresource_disabled(res, 0);
|
||||
return false;
|
||||
}
|
||||
acpi_dev_get_irqresource(res, irq->interrupts[index],
|
||||
irq->triggering, irq->polarity,
|
||||
irq->sharable);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||
ext_irq = &ares->data.extended_irq;
|
||||
if (index >= ext_irq->interrupt_count) {
|
||||
acpi_dev_irqresource_disabled(res, 0);
|
||||
return false;
|
||||
}
|
||||
acpi_dev_get_irqresource(res, ext_irq->interrupts[index],
|
||||
ext_irq->triggering, ext_irq->polarity,
|
||||
ext_irq->sharable);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt);
|
||||
|
||||
/**
|
||||
* acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources().
|
||||
* @list: The head of the resource list to free.
|
||||
*/
|
||||
void acpi_dev_free_resource_list(struct list_head *list)
|
||||
{
|
||||
struct resource_list_entry *rentry, *re;
|
||||
|
||||
list_for_each_entry_safe(rentry, re, list, node) {
|
||||
list_del(&rentry->node);
|
||||
kfree(rentry);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list);
|
||||
|
||||
struct res_proc_context {
|
||||
struct list_head *list;
|
||||
int (*preproc)(struct acpi_resource *, void *);
|
||||
void *preproc_data;
|
||||
int count;
|
||||
int error;
|
||||
};
|
||||
|
||||
static acpi_status acpi_dev_new_resource_entry(struct resource *r,
|
||||
struct res_proc_context *c)
|
||||
{
|
||||
struct resource_list_entry *rentry;
|
||||
|
||||
rentry = kmalloc(sizeof(*rentry), GFP_KERNEL);
|
||||
if (!rentry) {
|
||||
c->error = -ENOMEM;
|
||||
return AE_NO_MEMORY;
|
||||
}
|
||||
rentry->res = *r;
|
||||
list_add_tail(&rentry->node, c->list);
|
||||
c->count++;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static acpi_status acpi_dev_process_resource(struct acpi_resource *ares,
|
||||
void *context)
|
||||
{
|
||||
struct res_proc_context *c = context;
|
||||
struct resource r;
|
||||
int i;
|
||||
|
||||
if (c->preproc) {
|
||||
int ret;
|
||||
|
||||
ret = c->preproc(ares, c->preproc_data);
|
||||
if (ret < 0) {
|
||||
c->error = ret;
|
||||
return AE_CTRL_TERMINATE;
|
||||
} else if (ret > 0) {
|
||||
return AE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
memset(&r, 0, sizeof(r));
|
||||
|
||||
if (acpi_dev_resource_memory(ares, &r)
|
||||
|| acpi_dev_resource_io(ares, &r)
|
||||
|| acpi_dev_resource_address_space(ares, &r)
|
||||
|| acpi_dev_resource_ext_address_space(ares, &r))
|
||||
return acpi_dev_new_resource_entry(&r, c);
|
||||
|
||||
for (i = 0; acpi_dev_resource_interrupt(ares, i, &r); i++) {
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_dev_new_resource_entry(&r, c);
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
}
|
||||
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_dev_get_resources - Get current resources of a device.
|
||||
* @adev: ACPI device node to get the resources for.
|
||||
* @list: Head of the resultant list of resources (must be empty).
|
||||
* @preproc: The caller's preprocessing routine.
|
||||
* @preproc_data: Pointer passed to the caller's preprocessing routine.
|
||||
*
|
||||
* Evaluate the _CRS method for the given device node and process its output by
|
||||
* (1) executing the @preproc() rountine provided by the caller, passing the
|
||||
* resource pointer and @preproc_data to it as arguments, for each ACPI resource
|
||||
* returned and (2) converting all of the returned ACPI resources into struct
|
||||
* resource objects if possible. If the return value of @preproc() in step (1)
|
||||
* is different from 0, step (2) is not applied to the given ACPI resource and
|
||||
* if that value is negative, the whole processing is aborted and that value is
|
||||
* returned as the final error code.
|
||||
*
|
||||
* The resultant struct resource objects are put on the list pointed to by
|
||||
* @list, that must be empty initially, as members of struct resource_list_entry
|
||||
* objects. Callers of this routine should use %acpi_dev_free_resource_list() to
|
||||
* free that list.
|
||||
*
|
||||
* The number of resources in the output list is returned on success, an error
|
||||
* code reflecting the error condition is returned otherwise.
|
||||
*/
|
||||
int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list,
|
||||
int (*preproc)(struct acpi_resource *, void *),
|
||||
void *preproc_data)
|
||||
{
|
||||
struct res_proc_context c;
|
||||
acpi_handle not_used;
|
||||
acpi_status status;
|
||||
|
||||
if (!adev || !adev->handle || !list_empty(list))
|
||||
return -EINVAL;
|
||||
|
||||
status = acpi_get_handle(adev->handle, METHOD_NAME__CRS, ¬_used);
|
||||
if (ACPI_FAILURE(status))
|
||||
return 0;
|
||||
|
||||
c.list = list;
|
||||
c.preproc = preproc;
|
||||
c.preproc_data = preproc_data;
|
||||
c.count = 0;
|
||||
c.error = 0;
|
||||
status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
|
||||
acpi_dev_process_resource, &c);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
acpi_dev_free_resource_list(list);
|
||||
return c.error ? c.error : -EIO;
|
||||
}
|
||||
|
||||
return c.count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_get_resources);
|
|
@ -29,6 +29,17 @@ extern struct acpi_device *acpi_root;
|
|||
|
||||
static const char *dummy_hid = "device";
|
||||
|
||||
/*
|
||||
* 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" },
|
||||
|
||||
{ }
|
||||
};
|
||||
|
||||
static LIST_HEAD(acpi_device_list);
|
||||
static LIST_HEAD(acpi_bus_id_list);
|
||||
DEFINE_MUTEX(acpi_device_lock);
|
||||
|
@ -340,8 +351,8 @@ static void acpi_device_remove_files(struct acpi_device *dev)
|
|||
ACPI Bus operations
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
int acpi_match_device_ids(struct acpi_device *device,
|
||||
const struct acpi_device_id *ids)
|
||||
static const struct acpi_device_id *__acpi_match_device(
|
||||
struct acpi_device *device, const struct acpi_device_id *ids)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
struct acpi_hardware_id *hwid;
|
||||
|
@ -351,14 +362,44 @@ int acpi_match_device_ids(struct acpi_device *device,
|
|||
* driver for it.
|
||||
*/
|
||||
if (!device->status.present)
|
||||
return -ENODEV;
|
||||
return NULL;
|
||||
|
||||
for (id = ids; id->id[0]; id++)
|
||||
list_for_each_entry(hwid, &device->pnp.ids, list)
|
||||
if (!strcmp((char *) id->id, hwid->id))
|
||||
return 0;
|
||||
return id;
|
||||
|
||||
return -ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_match_device - Match a struct device against a given list of ACPI IDs
|
||||
* @ids: Array of struct acpi_device_id object to match against.
|
||||
* @dev: The device structure to match.
|
||||
*
|
||||
* Check if @dev has a valid ACPI handle and if there is a struct acpi_device
|
||||
* object for that handle and use that object to match against a given list of
|
||||
* device IDs.
|
||||
*
|
||||
* Return a pointer to the first matching ID on success or %NULL on failure.
|
||||
*/
|
||||
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
|
||||
const struct device *dev)
|
||||
{
|
||||
struct acpi_device *adev;
|
||||
|
||||
if (!ids || !ACPI_HANDLE(dev)
|
||||
|| ACPI_FAILURE(acpi_bus_get_device(ACPI_HANDLE(dev), &adev)))
|
||||
return NULL;
|
||||
|
||||
return __acpi_match_device(adev, ids);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_match_device);
|
||||
|
||||
int acpi_match_device_ids(struct acpi_device *device,
|
||||
const struct acpi_device_id *ids)
|
||||
{
|
||||
return __acpi_match_device(device, ids) ? 0 : -ENOENT;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_match_device_ids);
|
||||
|
||||
|
@ -1490,8 +1531,13 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
|
|||
*/
|
||||
device = NULL;
|
||||
acpi_bus_get_device(handle, &device);
|
||||
if (ops->acpi_op_add && !device)
|
||||
if (ops->acpi_op_add && !device) {
|
||||
acpi_add_single_object(&device, handle, type, sta, ops);
|
||||
/* Is the device a known good platform device? */
|
||||
if (device
|
||||
&& !acpi_match_device_ids(device, acpi_platform_device_ids))
|
||||
acpi_create_platform_device(device);
|
||||
}
|
||||
|
||||
if (!device)
|
||||
return AE_CTRL_DEPTH;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "power/power.h"
|
||||
|
@ -436,6 +437,7 @@ struct platform_device *platform_device_register_full(
|
|||
goto err_alloc;
|
||||
|
||||
pdev->dev.parent = pdevinfo->parent;
|
||||
ACPI_HANDLE_SET(&pdev->dev, pdevinfo->acpi_node.handle);
|
||||
|
||||
if (pdevinfo->dma_mask) {
|
||||
/*
|
||||
|
@ -466,6 +468,7 @@ struct platform_device *platform_device_register_full(
|
|||
ret = platform_device_add(pdev);
|
||||
if (ret) {
|
||||
err:
|
||||
ACPI_HANDLE_SET(&pdev->dev, NULL);
|
||||
kfree(pdev->dev.dma_mask);
|
||||
|
||||
err_alloc:
|
||||
|
@ -481,8 +484,16 @@ static int platform_drv_probe(struct device *_dev)
|
|||
{
|
||||
struct platform_driver *drv = to_platform_driver(_dev->driver);
|
||||
struct platform_device *dev = to_platform_device(_dev);
|
||||
int ret;
|
||||
|
||||
return drv->probe(dev);
|
||||
if (ACPI_HANDLE(_dev))
|
||||
acpi_dev_pm_attach(_dev, true);
|
||||
|
||||
ret = drv->probe(dev);
|
||||
if (ret && ACPI_HANDLE(_dev))
|
||||
acpi_dev_pm_detach(_dev, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int platform_drv_probe_fail(struct device *_dev)
|
||||
|
@ -494,8 +505,13 @@ static int platform_drv_remove(struct device *_dev)
|
|||
{
|
||||
struct platform_driver *drv = to_platform_driver(_dev->driver);
|
||||
struct platform_device *dev = to_platform_device(_dev);
|
||||
int ret;
|
||||
|
||||
return drv->remove(dev);
|
||||
ret = drv->remove(dev);
|
||||
if (ACPI_HANDLE(_dev))
|
||||
acpi_dev_pm_detach(_dev, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void platform_drv_shutdown(struct device *_dev)
|
||||
|
@ -504,6 +520,8 @@ static void platform_drv_shutdown(struct device *_dev)
|
|||
struct platform_device *dev = to_platform_device(_dev);
|
||||
|
||||
drv->shutdown(dev);
|
||||
if (ACPI_HANDLE(_dev))
|
||||
acpi_dev_pm_detach(_dev, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -709,6 +727,10 @@ static int platform_match(struct device *dev, struct device_driver *drv)
|
|||
if (of_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
|
||||
/* Then try ACPI style match */
|
||||
if (acpi_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
|
||||
/* Then try to match against the id table */
|
||||
if (pdrv->id_table)
|
||||
return platform_match_id(pdrv->id_table, pdev) != NULL;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/irqflags.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include "i2c-core.h"
|
||||
|
@ -78,6 +79,10 @@ static int i2c_device_match(struct device *dev, struct device_driver *drv)
|
|||
if (of_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
|
||||
/* Then ACPI style match */
|
||||
if (acpi_driver_match_device(dev, drv))
|
||||
return 1;
|
||||
|
||||
driver = to_i2c_driver(drv);
|
||||
/* match on an id table if there is one */
|
||||
if (driver->id_table)
|
||||
|
@ -539,6 +544,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
|
|||
client->dev.bus = &i2c_bus_type;
|
||||
client->dev.type = &i2c_client_type;
|
||||
client->dev.of_node = info->of_node;
|
||||
ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);
|
||||
|
||||
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
|
||||
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
|
||||
|
|
|
@ -81,6 +81,18 @@ config MMC_RICOH_MMC
|
|||
|
||||
If unsure, say Y.
|
||||
|
||||
config MMC_SDHCI_ACPI
|
||||
tristate "SDHCI support for ACPI enumerated SDHCI controllers"
|
||||
depends on MMC_SDHCI && ACPI
|
||||
help
|
||||
This selects support for ACPI enumerated SDHCI controllers,
|
||||
identified by ACPI Compatibility ID PNP0D40 or specific
|
||||
ACPI Hardware IDs.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_PLTFM
|
||||
tristate "SDHCI platform and OF driver helper"
|
||||
depends on MMC_SDHCI
|
||||
|
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
|
|||
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
|
||||
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
|
||||
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
||||
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
||||
|
|
304
drivers/mmc/host/sdhci-acpi.c
Normal file
304
drivers/mmc/host/sdhci-acpi.c
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* Secure Digital Host Controller Interface ACPI driver.
|
||||
*
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
#include <linux/mmc/sdhci.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
||||
enum {
|
||||
SDHCI_ACPI_SD_CD = BIT(0),
|
||||
SDHCI_ACPI_RUNTIME_PM = BIT(1),
|
||||
};
|
||||
|
||||
struct sdhci_acpi_chip {
|
||||
const struct sdhci_ops *ops;
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
unsigned long caps;
|
||||
unsigned int caps2;
|
||||
mmc_pm_flag_t pm_caps;
|
||||
};
|
||||
|
||||
struct sdhci_acpi_slot {
|
||||
const struct sdhci_acpi_chip *chip;
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
unsigned long caps;
|
||||
unsigned int caps2;
|
||||
mmc_pm_flag_t pm_caps;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
struct sdhci_acpi_host {
|
||||
struct sdhci_host *host;
|
||||
const struct sdhci_acpi_slot *slot;
|
||||
struct platform_device *pdev;
|
||||
bool use_runtime_pm;
|
||||
};
|
||||
|
||||
static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag)
|
||||
{
|
||||
return c->slot && (c->slot->flags & flag);
|
||||
}
|
||||
|
||||
static int sdhci_acpi_enable_dma(struct sdhci_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_acpi_ops_dflt = {
|
||||
.enable_dma = sdhci_acpi_enable_dma,
|
||||
};
|
||||
|
||||
static const struct acpi_device_id sdhci_acpi_ids[] = {
|
||||
{ "PNP0D40" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
|
||||
|
||||
static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
for (id = sdhci_acpi_ids; id->id[0]; id++)
|
||||
if (!strcmp(id->id, hid))
|
||||
return (const struct sdhci_acpi_slot *)id->driver_data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __devinit sdhci_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
acpi_handle handle = ACPI_HANDLE(dev);
|
||||
struct acpi_device *device;
|
||||
struct sdhci_acpi_host *c;
|
||||
struct sdhci_host *host;
|
||||
struct resource *iomem;
|
||||
resource_size_t len;
|
||||
const char *hid;
|
||||
int err;
|
||||
|
||||
if (acpi_bus_get_device(handle, &device))
|
||||
return -ENODEV;
|
||||
|
||||
if (acpi_bus_get_status(device) || !device->status.present)
|
||||
return -ENODEV;
|
||||
|
||||
hid = acpi_device_hid(device);
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!iomem)
|
||||
return -ENOMEM;
|
||||
|
||||
len = resource_size(iomem);
|
||||
if (len < 0x100)
|
||||
dev_err(dev, "Invalid iomem size!\n");
|
||||
|
||||
if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev)))
|
||||
return -ENOMEM;
|
||||
|
||||
host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
c = sdhci_priv(host);
|
||||
c->host = host;
|
||||
c->slot = sdhci_acpi_get_slot(hid);
|
||||
c->pdev = pdev;
|
||||
c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM);
|
||||
|
||||
platform_set_drvdata(pdev, c);
|
||||
|
||||
host->hw_name = "ACPI";
|
||||
host->ops = &sdhci_acpi_ops_dflt;
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
host->ioaddr = devm_ioremap_nocache(dev, iomem->start,
|
||||
resource_size(iomem));
|
||||
if (host->ioaddr == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (!dev->dma_mask) {
|
||||
u64 dma_mask;
|
||||
|
||||
if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) {
|
||||
/* 64-bit DMA is not supported at present */
|
||||
dma_mask = DMA_BIT_MASK(32);
|
||||
} else {
|
||||
dma_mask = DMA_BIT_MASK(32);
|
||||
}
|
||||
|
||||
dev->dma_mask = &dev->coherent_dma_mask;
|
||||
dev->coherent_dma_mask = dma_mask;
|
||||
}
|
||||
|
||||
if (c->slot) {
|
||||
if (c->slot->chip) {
|
||||
host->ops = c->slot->chip->ops;
|
||||
host->quirks |= c->slot->chip->quirks;
|
||||
host->quirks2 |= c->slot->chip->quirks2;
|
||||
host->mmc->caps |= c->slot->chip->caps;
|
||||
host->mmc->caps2 |= c->slot->chip->caps2;
|
||||
host->mmc->pm_caps |= c->slot->chip->pm_caps;
|
||||
}
|
||||
host->quirks |= c->slot->quirks;
|
||||
host->quirks2 |= c->slot->quirks2;
|
||||
host->mmc->caps |= c->slot->caps;
|
||||
host->mmc->caps2 |= c->slot->caps2;
|
||||
host->mmc->pm_caps |= c->slot->pm_caps;
|
||||
}
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
if (err)
|
||||
goto err_free;
|
||||
|
||||
if (c->use_runtime_pm) {
|
||||
pm_suspend_ignore_children(dev, 1);
|
||||
pm_runtime_set_autosuspend_delay(dev, 50);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
sdhci_free_host(c->host);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit sdhci_acpi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
int dead;
|
||||
|
||||
if (c->use_runtime_pm) {
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
}
|
||||
|
||||
dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
|
||||
sdhci_remove_host(c->host, dead);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
sdhci_free_host(c->host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int sdhci_acpi_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
|
||||
|
||||
return sdhci_suspend_host(c->host);
|
||||
}
|
||||
|
||||
static int sdhci_acpi_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
|
||||
|
||||
return sdhci_resume_host(c->host);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define sdhci_acpi_suspend NULL
|
||||
#define sdhci_acpi_resume NULL
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int sdhci_acpi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
|
||||
|
||||
return sdhci_runtime_suspend_host(c->host);
|
||||
}
|
||||
|
||||
static int sdhci_acpi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_acpi_host *c = dev_get_drvdata(dev);
|
||||
|
||||
return sdhci_runtime_resume_host(c->host);
|
||||
}
|
||||
|
||||
static int sdhci_acpi_runtime_idle(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define sdhci_acpi_runtime_suspend NULL
|
||||
#define sdhci_acpi_runtime_resume NULL
|
||||
#define sdhci_acpi_runtime_idle NULL
|
||||
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
||||
.suspend = sdhci_acpi_suspend,
|
||||
.resume = sdhci_acpi_resume,
|
||||
.runtime_suspend = sdhci_acpi_runtime_suspend,
|
||||
.runtime_resume = sdhci_acpi_runtime_resume,
|
||||
.runtime_idle = sdhci_acpi_runtime_idle,
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_acpi_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-acpi",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = sdhci_acpi_ids,
|
||||
.pm = &sdhci_acpi_pm_ops,
|
||||
},
|
||||
.probe = sdhci_acpi_probe,
|
||||
.remove = __devexit_p(sdhci_acpi_remove),
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_acpi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver");
|
||||
MODULE_AUTHOR("Adrian Hunter");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -159,6 +159,8 @@ struct pnp_resource {
|
|||
|
||||
void pnp_free_resource(struct pnp_resource *pnp_res);
|
||||
|
||||
struct pnp_resource *pnp_add_resource(struct pnp_dev *dev,
|
||||
struct resource *res);
|
||||
struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq,
|
||||
int flags);
|
||||
struct pnp_resource *pnp_add_dma_resource(struct pnp_dev *dev, int dma,
|
||||
|
|
|
@ -242,6 +242,10 @@ static int __init pnpacpi_add_device(struct acpi_device *device)
|
|||
char *pnpid;
|
||||
struct acpi_hardware_id *id;
|
||||
|
||||
/* Skip devices that are already bound */
|
||||
if (device->physical_node_count)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If a PnPacpi device is not present , the device
|
||||
* driver should not be loaded.
|
||||
|
|
|
@ -28,37 +28,6 @@
|
|||
#include "../base.h"
|
||||
#include "pnpacpi.h"
|
||||
|
||||
#ifdef CONFIG_IA64
|
||||
#define valid_IRQ(i) (1)
|
||||
#else
|
||||
#define valid_IRQ(i) (((i) != 0) && ((i) != 2))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocated Resources
|
||||
*/
|
||||
static int irq_flags(int triggering, int polarity, int shareable)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if (triggering == ACPI_LEVEL_SENSITIVE) {
|
||||
if (polarity == ACPI_ACTIVE_LOW)
|
||||
flags = IORESOURCE_IRQ_LOWLEVEL;
|
||||
else
|
||||
flags = IORESOURCE_IRQ_HIGHLEVEL;
|
||||
} else {
|
||||
if (polarity == ACPI_ACTIVE_LOW)
|
||||
flags = IORESOURCE_IRQ_LOWEDGE;
|
||||
else
|
||||
flags = IORESOURCE_IRQ_HIGHEDGE;
|
||||
}
|
||||
|
||||
if (shareable == ACPI_SHARED)
|
||||
flags |= IORESOURCE_IRQ_SHAREABLE;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering,
|
||||
int *polarity, int *shareable)
|
||||
{
|
||||
|
@ -94,45 +63,6 @@ static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering,
|
|||
*shareable = ACPI_EXCLUSIVE;
|
||||
}
|
||||
|
||||
static void pnpacpi_parse_allocated_irqresource(struct pnp_dev *dev,
|
||||
u32 gsi, int triggering,
|
||||
int polarity, int shareable)
|
||||
{
|
||||
int irq, flags;
|
||||
int p, t;
|
||||
|
||||
if (!valid_IRQ(gsi)) {
|
||||
pnp_add_irq_resource(dev, gsi, IORESOURCE_DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* in IO-APIC mode, use overrided attribute. Two reasons:
|
||||
* 1. BIOS bug in DSDT
|
||||
* 2. BIOS uses IO-APIC mode Interrupt Source Override
|
||||
*/
|
||||
if (!acpi_get_override_irq(gsi, &t, &p)) {
|
||||
t = t ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE;
|
||||
p = p ? ACPI_ACTIVE_LOW : ACPI_ACTIVE_HIGH;
|
||||
|
||||
if (triggering != t || polarity != p) {
|
||||
dev_warn(&dev->dev, "IRQ %d override to %s, %s\n",
|
||||
gsi, t ? "edge":"level", p ? "low":"high");
|
||||
triggering = t;
|
||||
polarity = p;
|
||||
}
|
||||
}
|
||||
|
||||
flags = irq_flags(triggering, polarity, shareable);
|
||||
irq = acpi_register_gsi(&dev->dev, gsi, triggering, polarity);
|
||||
if (irq >= 0)
|
||||
pcibios_penalize_isa_irq(irq, 1);
|
||||
else
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
|
||||
pnp_add_irq_resource(dev, irq, flags);
|
||||
}
|
||||
|
||||
static int dma_flags(struct pnp_dev *dev, int type, int bus_master,
|
||||
int transfer)
|
||||
{
|
||||
|
@ -177,21 +107,16 @@ static int dma_flags(struct pnp_dev *dev, int type, int bus_master,
|
|||
return flags;
|
||||
}
|
||||
|
||||
static void pnpacpi_parse_allocated_ioresource(struct pnp_dev *dev, u64 start,
|
||||
u64 len, int io_decode,
|
||||
int window)
|
||||
/*
|
||||
* Allocated Resources
|
||||
*/
|
||||
|
||||
static void pnpacpi_add_irqresource(struct pnp_dev *dev, struct resource *r)
|
||||
{
|
||||
int flags = 0;
|
||||
u64 end = start + len - 1;
|
||||
if (!(r->flags & IORESOURCE_DISABLED))
|
||||
pcibios_penalize_isa_irq(r->start, 1);
|
||||
|
||||
if (io_decode == ACPI_DECODE_16)
|
||||
flags |= IORESOURCE_IO_16BIT_ADDR;
|
||||
if (len == 0 || end >= 0x10003)
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
if (window)
|
||||
flags |= IORESOURCE_WINDOW;
|
||||
|
||||
pnp_add_io_resource(dev, start, end, flags);
|
||||
pnp_add_resource(dev, r);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -249,130 +174,49 @@ static void pnpacpi_parse_allocated_vendor(struct pnp_dev *dev,
|
|||
}
|
||||
}
|
||||
|
||||
static void pnpacpi_parse_allocated_memresource(struct pnp_dev *dev,
|
||||
u64 start, u64 len,
|
||||
int write_protect, int window)
|
||||
{
|
||||
int flags = 0;
|
||||
u64 end = start + len - 1;
|
||||
|
||||
if (len == 0)
|
||||
flags |= IORESOURCE_DISABLED;
|
||||
if (write_protect == ACPI_READ_WRITE_MEMORY)
|
||||
flags |= IORESOURCE_MEM_WRITEABLE;
|
||||
if (window)
|
||||
flags |= IORESOURCE_WINDOW;
|
||||
|
||||
pnp_add_mem_resource(dev, start, end, flags);
|
||||
}
|
||||
|
||||
static void pnpacpi_parse_allocated_busresource(struct pnp_dev *dev,
|
||||
u64 start, u64 len)
|
||||
{
|
||||
u64 end = start + len - 1;
|
||||
|
||||
pnp_add_bus_resource(dev, start, end);
|
||||
}
|
||||
|
||||
static void pnpacpi_parse_allocated_address_space(struct pnp_dev *dev,
|
||||
struct acpi_resource *res)
|
||||
{
|
||||
struct acpi_resource_address64 addr, *p = &addr;
|
||||
acpi_status status;
|
||||
int window;
|
||||
u64 len;
|
||||
|
||||
status = acpi_resource_to_address64(res, p);
|
||||
if (!ACPI_SUCCESS(status)) {
|
||||
dev_warn(&dev->dev, "failed to convert resource type %d\n",
|
||||
res->type);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Windows apparently computes length rather than using _LEN */
|
||||
len = p->maximum - p->minimum + 1;
|
||||
window = (p->producer_consumer == ACPI_PRODUCER) ? 1 : 0;
|
||||
|
||||
if (p->resource_type == ACPI_MEMORY_RANGE)
|
||||
pnpacpi_parse_allocated_memresource(dev, p->minimum, len,
|
||||
p->info.mem.write_protect, window);
|
||||
else if (p->resource_type == ACPI_IO_RANGE)
|
||||
pnpacpi_parse_allocated_ioresource(dev, p->minimum, len,
|
||||
p->granularity == 0xfff ? ACPI_DECODE_10 :
|
||||
ACPI_DECODE_16, window);
|
||||
else if (p->resource_type == ACPI_BUS_NUMBER_RANGE)
|
||||
pnpacpi_parse_allocated_busresource(dev, p->minimum, len);
|
||||
}
|
||||
|
||||
static void pnpacpi_parse_allocated_ext_address_space(struct pnp_dev *dev,
|
||||
struct acpi_resource *res)
|
||||
{
|
||||
struct acpi_resource_extended_address64 *p = &res->data.ext_address64;
|
||||
int window;
|
||||
u64 len;
|
||||
|
||||
/* Windows apparently computes length rather than using _LEN */
|
||||
len = p->maximum - p->minimum + 1;
|
||||
window = (p->producer_consumer == ACPI_PRODUCER) ? 1 : 0;
|
||||
|
||||
if (p->resource_type == ACPI_MEMORY_RANGE)
|
||||
pnpacpi_parse_allocated_memresource(dev, p->minimum, len,
|
||||
p->info.mem.write_protect, window);
|
||||
else if (p->resource_type == ACPI_IO_RANGE)
|
||||
pnpacpi_parse_allocated_ioresource(dev, p->minimum, len,
|
||||
p->granularity == 0xfff ? ACPI_DECODE_10 :
|
||||
ACPI_DECODE_16, window);
|
||||
else if (p->resource_type == ACPI_BUS_NUMBER_RANGE)
|
||||
pnpacpi_parse_allocated_busresource(dev, p->minimum, len);
|
||||
}
|
||||
|
||||
static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
||||
void *data)
|
||||
{
|
||||
struct pnp_dev *dev = data;
|
||||
struct acpi_resource_irq *irq;
|
||||
struct acpi_resource_dma *dma;
|
||||
struct acpi_resource_io *io;
|
||||
struct acpi_resource_fixed_io *fixed_io;
|
||||
struct acpi_resource_vendor_typed *vendor_typed;
|
||||
struct acpi_resource_memory24 *memory24;
|
||||
struct acpi_resource_memory32 *memory32;
|
||||
struct acpi_resource_fixed_memory32 *fixed_memory32;
|
||||
struct acpi_resource_extended_irq *extended_irq;
|
||||
struct resource r;
|
||||
int i, flags;
|
||||
|
||||
switch (res->type) {
|
||||
case ACPI_RESOURCE_TYPE_IRQ:
|
||||
/*
|
||||
* Per spec, only one interrupt per descriptor is allowed in
|
||||
* _CRS, but some firmware violates this, so parse them all.
|
||||
*/
|
||||
irq = &res->data.irq;
|
||||
if (irq->interrupt_count == 0)
|
||||
pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED);
|
||||
else {
|
||||
for (i = 0; i < irq->interrupt_count; i++) {
|
||||
pnpacpi_parse_allocated_irqresource(dev,
|
||||
irq->interrupts[i],
|
||||
irq->triggering,
|
||||
irq->polarity,
|
||||
irq->sharable);
|
||||
}
|
||||
if (acpi_dev_resource_memory(res, &r)
|
||||
|| acpi_dev_resource_io(res, &r)
|
||||
|| acpi_dev_resource_address_space(res, &r)
|
||||
|| acpi_dev_resource_ext_address_space(res, &r)) {
|
||||
pnp_add_resource(dev, &r);
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
r.flags = 0;
|
||||
if (acpi_dev_resource_interrupt(res, 0, &r)) {
|
||||
pnpacpi_add_irqresource(dev, &r);
|
||||
for (i = 1; acpi_dev_resource_interrupt(res, i, &r); i++)
|
||||
pnpacpi_add_irqresource(dev, &r);
|
||||
|
||||
if (i > 1) {
|
||||
/*
|
||||
* The IRQ encoder puts a single interrupt in each
|
||||
* descriptor, so if a _CRS descriptor has more than
|
||||
* one interrupt, we won't be able to re-encode it.
|
||||
*/
|
||||
if (pnp_can_write(dev) && irq->interrupt_count > 1) {
|
||||
if (pnp_can_write(dev)) {
|
||||
dev_warn(&dev->dev, "multiple interrupts in "
|
||||
"_CRS descriptor; configuration can't "
|
||||
"be changed\n");
|
||||
dev->capabilities &= ~PNP_WRITE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
return AE_OK;
|
||||
} else if (r.flags & IORESOURCE_DISABLED) {
|
||||
pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED);
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
switch (res->type) {
|
||||
case ACPI_RESOURCE_TYPE_DMA:
|
||||
dma = &res->data.dma;
|
||||
if (dma->channel_count > 0 && dma->channels[0] != (u8) -1)
|
||||
|
@ -383,26 +227,10 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
|||
pnp_add_dma_resource(dev, dma->channels[0], flags);
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_IO:
|
||||
io = &res->data.io;
|
||||
pnpacpi_parse_allocated_ioresource(dev,
|
||||
io->minimum,
|
||||
io->address_length,
|
||||
io->io_decode, 0);
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_START_DEPENDENT:
|
||||
case ACPI_RESOURCE_TYPE_END_DEPENDENT:
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_FIXED_IO:
|
||||
fixed_io = &res->data.fixed_io;
|
||||
pnpacpi_parse_allocated_ioresource(dev,
|
||||
fixed_io->address,
|
||||
fixed_io->address_length,
|
||||
ACPI_DECODE_10, 0);
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_VENDOR:
|
||||
vendor_typed = &res->data.vendor_typed;
|
||||
pnpacpi_parse_allocated_vendor(dev, vendor_typed);
|
||||
|
@ -411,66 +239,6 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,
|
|||
case ACPI_RESOURCE_TYPE_END_TAG:
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_MEMORY24:
|
||||
memory24 = &res->data.memory24;
|
||||
pnpacpi_parse_allocated_memresource(dev,
|
||||
memory24->minimum,
|
||||
memory24->address_length,
|
||||
memory24->write_protect, 0);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_MEMORY32:
|
||||
memory32 = &res->data.memory32;
|
||||
pnpacpi_parse_allocated_memresource(dev,
|
||||
memory32->minimum,
|
||||
memory32->address_length,
|
||||
memory32->write_protect, 0);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
|
||||
fixed_memory32 = &res->data.fixed_memory32;
|
||||
pnpacpi_parse_allocated_memresource(dev,
|
||||
fixed_memory32->address,
|
||||
fixed_memory32->address_length,
|
||||
fixed_memory32->write_protect, 0);
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_ADDRESS16:
|
||||
case ACPI_RESOURCE_TYPE_ADDRESS32:
|
||||
case ACPI_RESOURCE_TYPE_ADDRESS64:
|
||||
pnpacpi_parse_allocated_address_space(dev, res);
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64:
|
||||
pnpacpi_parse_allocated_ext_address_space(dev, res);
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
|
||||
extended_irq = &res->data.extended_irq;
|
||||
|
||||
if (extended_irq->interrupt_count == 0)
|
||||
pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED);
|
||||
else {
|
||||
for (i = 0; i < extended_irq->interrupt_count; i++) {
|
||||
pnpacpi_parse_allocated_irqresource(dev,
|
||||
extended_irq->interrupts[i],
|
||||
extended_irq->triggering,
|
||||
extended_irq->polarity,
|
||||
extended_irq->sharable);
|
||||
}
|
||||
|
||||
/*
|
||||
* The IRQ encoder puts a single interrupt in each
|
||||
* descriptor, so if a _CRS descriptor has more than
|
||||
* one interrupt, we won't be able to re-encode it.
|
||||
*/
|
||||
if (pnp_can_write(dev) &&
|
||||
extended_irq->interrupt_count > 1) {
|
||||
dev_warn(&dev->dev, "multiple interrupts in "
|
||||
"_CRS descriptor; configuration can't "
|
||||
"be changed\n");
|
||||
dev->capabilities &= ~PNP_WRITE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ACPI_RESOURCE_TYPE_GENERIC_REGISTER:
|
||||
break;
|
||||
|
||||
|
@ -531,7 +299,7 @@ static __init void pnpacpi_parse_irq_option(struct pnp_dev *dev,
|
|||
if (p->interrupts[i])
|
||||
__set_bit(p->interrupts[i], map.bits);
|
||||
|
||||
flags = irq_flags(p->triggering, p->polarity, p->sharable);
|
||||
flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->sharable);
|
||||
pnp_register_irq_resource(dev, option_flags, &map, flags);
|
||||
}
|
||||
|
||||
|
@ -555,7 +323,7 @@ static __init void pnpacpi_parse_ext_irq_option(struct pnp_dev *dev,
|
|||
}
|
||||
}
|
||||
|
||||
flags = irq_flags(p->triggering, p->polarity, p->sharable);
|
||||
flags = acpi_dev_irq_flags(p->triggering, p->polarity, p->sharable);
|
||||
pnp_register_irq_resource(dev, option_flags, &map, flags);
|
||||
}
|
||||
|
||||
|
|
|
@ -503,6 +503,22 @@ static struct pnp_resource *pnp_new_resource(struct pnp_dev *dev)
|
|||
return pnp_res;
|
||||
}
|
||||
|
||||
struct pnp_resource *pnp_add_resource(struct pnp_dev *dev,
|
||||
struct resource *res)
|
||||
{
|
||||
struct pnp_resource *pnp_res;
|
||||
|
||||
pnp_res = pnp_new_resource(dev);
|
||||
if (!pnp_res) {
|
||||
dev_err(&dev->dev, "can't add resource %pR\n", res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pnp_res->res = *res;
|
||||
dev_dbg(&dev->dev, "%pR\n", res);
|
||||
return pnp_res;
|
||||
}
|
||||
|
||||
struct pnp_resource *pnp_add_irq_resource(struct pnp_dev *dev, int irq,
|
||||
int flags)
|
||||
{
|
||||
|
|
|
@ -412,7 +412,7 @@ acpi_handle acpi_get_child(acpi_handle, u64);
|
|||
int acpi_is_root_bridge(acpi_handle);
|
||||
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
|
||||
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle);
|
||||
#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle))
|
||||
#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev))
|
||||
|
||||
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state);
|
||||
int acpi_disable_wakeup_device_power(struct acpi_device *dev);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h> /* for struct resource */
|
||||
#include <linux/device.h>
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
|
@ -251,6 +252,26 @@ extern int pnpacpi_disabled;
|
|||
|
||||
#define PXM_INVAL (-1)
|
||||
|
||||
bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res);
|
||||
bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res);
|
||||
bool acpi_dev_resource_address_space(struct acpi_resource *ares,
|
||||
struct resource *res);
|
||||
bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares,
|
||||
struct resource *res);
|
||||
unsigned long acpi_dev_irq_flags(u8 triggering, u8 polarity, u8 shareable);
|
||||
bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index,
|
||||
struct resource *res);
|
||||
|
||||
struct resource_list_entry {
|
||||
struct list_head node;
|
||||
struct resource res;
|
||||
};
|
||||
|
||||
void acpi_dev_free_resource_list(struct list_head *list);
|
||||
int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list,
|
||||
int (*preproc)(struct acpi_resource *, void *),
|
||||
void *preproc_data);
|
||||
|
||||
int acpi_check_resource_conflict(const struct resource *res);
|
||||
|
||||
int acpi_check_region(resource_size_t start, resource_size_t n,
|
||||
|
@ -365,6 +386,17 @@ extern int acpi_nvs_register(__u64 start, __u64 size);
|
|||
extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
|
||||
void *data);
|
||||
|
||||
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
|
||||
const struct device *dev);
|
||||
|
||||
static inline bool acpi_driver_match_device(struct device *dev,
|
||||
const struct device_driver *drv)
|
||||
{
|
||||
return !!acpi_match_device(drv->acpi_match_table, dev);
|
||||
}
|
||||
|
||||
#define ACPI_PTR(_ptr) (_ptr)
|
||||
|
||||
#else /* !CONFIG_ACPI */
|
||||
|
||||
#define acpi_disabled 1
|
||||
|
@ -419,6 +451,22 @@ static inline int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct acpi_device_id;
|
||||
|
||||
static inline const struct acpi_device_id *acpi_match_device(
|
||||
const struct acpi_device_id *ids, const struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool acpi_driver_match_device(struct device *dev,
|
||||
const struct device_driver *drv)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#define ACPI_PTR(_ptr) (NULL)
|
||||
|
||||
#endif /* !CONFIG_ACPI */
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
|
|
@ -190,6 +190,7 @@ extern struct klist *bus_get_device_klist(struct bus_type *bus);
|
|||
* @mod_name: Used for built-in modules.
|
||||
* @suppress_bind_attrs: Disables bind/unbind via sysfs.
|
||||
* @of_match_table: The open firmware table.
|
||||
* @acpi_match_table: The ACPI match table.
|
||||
* @probe: Called to query the existence of a specific device,
|
||||
* whether this driver can work with it, and bind the driver
|
||||
* to a specific device.
|
||||
|
@ -223,6 +224,7 @@ struct device_driver {
|
|||
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
|
||||
|
||||
const struct of_device_id *of_match_table;
|
||||
const struct acpi_device_id *acpi_match_table;
|
||||
|
||||
int (*probe) (struct device *dev);
|
||||
int (*remove) (struct device *dev);
|
||||
|
@ -576,6 +578,12 @@ struct device_dma_parameters {
|
|||
unsigned long segment_boundary_mask;
|
||||
};
|
||||
|
||||
struct acpi_dev_node {
|
||||
#ifdef CONFIG_ACPI
|
||||
void *handle;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* struct device - The basic device structure
|
||||
* @parent: The device's "parent" device, the device to which it is attached.
|
||||
|
@ -616,6 +624,7 @@ struct device_dma_parameters {
|
|||
* @dma_mem: Internal for coherent mem override.
|
||||
* @archdata: For arch-specific additions.
|
||||
* @of_node: Associated device tree node.
|
||||
* @acpi_node: Associated ACPI device node.
|
||||
* @devt: For creating the sysfs "dev".
|
||||
* @id: device instance
|
||||
* @devres_lock: Spinlock to protect the resource of the device.
|
||||
|
@ -680,6 +689,7 @@ struct device {
|
|||
struct dev_archdata archdata;
|
||||
|
||||
struct device_node *of_node; /* associated device tree node */
|
||||
struct acpi_dev_node acpi_node; /* associated ACPI device node */
|
||||
|
||||
dev_t devt; /* dev_t, creates the sysfs "dev" */
|
||||
u32 id; /* device instance */
|
||||
|
@ -700,6 +710,14 @@ static inline struct device *kobj_to_dev(struct kobject *kobj)
|
|||
return container_of(kobj, struct device, kobj);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
#define ACPI_HANDLE(dev) ((dev)->acpi_node.handle)
|
||||
#define ACPI_HANDLE_SET(dev, _handle_) (dev)->acpi_node.handle = (_handle_)
|
||||
#else
|
||||
#define ACPI_HANDLE(dev) (NULL)
|
||||
#define ACPI_HANDLE_SET(dev, _handle_) do { } while (0)
|
||||
#endif
|
||||
|
||||
/* Get the wakeup routines, which depend on struct device */
|
||||
#include <linux/pm_wakeup.h>
|
||||
|
||||
|
|
|
@ -259,6 +259,7 @@ static inline void i2c_set_clientdata(struct i2c_client *dev, void *data)
|
|||
* @platform_data: stored in i2c_client.dev.platform_data
|
||||
* @archdata: copied into i2c_client.dev.archdata
|
||||
* @of_node: pointer to OpenFirmware device node
|
||||
* @acpi_node: ACPI device node
|
||||
* @irq: stored in i2c_client.irq
|
||||
*
|
||||
* I2C doesn't actually support hardware probing, although controllers and
|
||||
|
@ -279,6 +280,7 @@ struct i2c_board_info {
|
|||
void *platform_data;
|
||||
struct dev_archdata *archdata;
|
||||
struct device_node *of_node;
|
||||
struct acpi_dev_node acpi_node;
|
||||
int irq;
|
||||
};
|
||||
|
||||
|
@ -501,4 +503,11 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
|
|||
i2c_del_driver)
|
||||
|
||||
#endif /* I2C */
|
||||
|
||||
#if IS_ENABLED(CONFIG_ACPI_I2C)
|
||||
extern void acpi_i2c_register_devices(struct i2c_adapter *adap);
|
||||
#else
|
||||
static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) {}
|
||||
#endif
|
||||
|
||||
#endif /* _LINUX_I2C_H */
|
||||
|
|
|
@ -55,6 +55,7 @@ extern int platform_add_devices(struct platform_device **, int);
|
|||
|
||||
struct platform_device_info {
|
||||
struct device *parent;
|
||||
struct acpi_dev_node acpi_node;
|
||||
|
||||
const char *name;
|
||||
int id;
|
||||
|
|
Loading…
Reference in a new issue