Commit a021cea7 authored by Len Brown's avatar Len Brown Committed by Len Brown

[ACPI] create ACPI-based PNP driver.

With this driver, legacy device drivers (floppy ACPI driver,
COM ACPI driver, and ACPI motherboard driver) which
directly use ACPI can be removed, since now we have unified PNP
interface for legacy ACPI enumerated devices.

Originally by Matthieu Castet
Signed-off-by: default avatarLi Shaohua <shaohua.li@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 2e1363f0
......@@ -6,7 +6,7 @@ menu "Plug and Play support"
config PNP
bool "Plug and Play support"
depends on ISA
depends on ISA || ACPI_BUS
---help---
Plug and Play (PnP) is a standard for peripherals which allows those
peripherals to be configured by software, e.g. assign IRQ's or other
......@@ -35,5 +35,7 @@ source "drivers/pnp/isapnp/Kconfig"
source "drivers/pnp/pnpbios/Kconfig"
source "drivers/pnp/pnpacpi/Kconfig"
endmenu
......@@ -4,5 +4,6 @@
obj-y := core.o card.o driver.o resource.o manager.o support.o interface.o quirks.o system.o
obj-$(CONFIG_PNPACPI) += pnpacpi/
obj-$(CONFIG_PNPBIOS) += pnpbios/
obj-$(CONFIG_ISAPNP) += isapnp/
......@@ -3,7 +3,7 @@
#
config ISAPNP
bool "ISA Plug and Play support"
depends on PNP
depends on PNP && ISA
help
Say Y here if you would like support for ISA Plug and Play devices.
Some information is in <file:Documentation/isapnp.txt>.
......
#
# Plug and Play ACPI configuration
#
config PNPACPI
bool "Plug and Play ACPI support (EXPERIMENTAL)"
depends on PNP && ACPI_BUS && EXPERIMENTAL
default y
---help---
Linux uses the PNPACPI to autodetect built-in
mainboard resources (e.g. parallel port resources).
Some features (e.g. real hotplug) are not currently
implemented.
If you would like the kernel to detect and allocate resources to
your mainboard devices (on some systems they are disabled by the
BIOS) say Y here. Also the PNPACPI can help prevent resource
conflicts between mainboard devices and other bus devices.
#
# Makefile for the kernel PNPACPI driver.
#
obj-y := core.o rsparser.o
/*
* pnpacpi -- PnP ACPI driver
*
* Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr>
* Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* 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/pnp.h>
#include <acpi/acpi_bus.h>
#include "pnpacpi.h"
static int num = 0;
static char excluded_id_list[] =
"PNP0C0A," /* Battery */
"PNP0C0C,PNP0C0E,PNP0C0D," /* Button */
"PNP0C09," /* EC */
"PNP0C0B," /* Fan */
"PNP0A03," /* PCI root */
"PNP0C0F," /* Link device */
"PNP0000," /* PIC */
"PNP0100," /* Timer */
;
static inline int is_exclusive_device(struct acpi_device *dev)
{
return (!acpi_match_ids(dev, excluded_id_list));
}
void *pnpacpi_kmalloc(size_t size, int f)
{
void *p = kmalloc(size, f);
if (p)
memset(p, 0, size);
return p;
}
/*
* Compatible Device IDs
*/
#define TEST_HEX(c) \
if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
return 0
#define TEST_ALPHA(c) \
if (!('@' <= (c) || (c) <= 'Z')) \
return 0
static int ispnpidacpi(char *id)
{
TEST_ALPHA(id[0]);
TEST_ALPHA(id[1]);
TEST_ALPHA(id[2]);
TEST_HEX(id[3]);
TEST_HEX(id[4]);
TEST_HEX(id[5]);
TEST_HEX(id[6]);
if (id[7] != '\0')
return 0;
return 1;
}
static void pnpidacpi_to_pnpid(char *id, char *str)
{
str[0] = id[0];
str[1] = id[1];
str[2] = id[2];
str[3] = tolower(id[3]);
str[4] = tolower(id[4]);
str[5] = tolower(id[5]);
str[6] = tolower(id[6]);
str[7] = '\0';
}
static int pnpacpi_get_resources(struct pnp_dev * dev, struct pnp_resource_table * res)
{
acpi_status status;
status = pnpacpi_parse_allocated_resource((acpi_handle)dev->data,
&dev->res);
return ACPI_FAILURE(status) ? -ENODEV : 0;
}
static int pnpacpi_set_resources(struct pnp_dev * dev, struct pnp_resource_table * res)
{
acpi_handle handle = dev->data;
struct acpi_buffer buffer;
int ret = 0;
acpi_status status;
ret = pnpacpi_build_resource_template(handle, &buffer);
if (ret)
return ret;
ret = pnpacpi_encode_resources(res, &buffer);
if (ret) {
kfree(buffer.pointer);
return ret;
}
status = acpi_set_current_resources(handle, &buffer);
if (ACPI_FAILURE(status))
ret = -EINVAL;
kfree(buffer.pointer);
return ret;
}
static int pnpacpi_disable_resources(struct pnp_dev *dev)
{
acpi_status status;
/* acpi_unregister_gsi(pnp_irq(dev, 0)); */
status = acpi_evaluate_object((acpi_handle)dev->data,
"_DIS", NULL, NULL);
return ACPI_FAILURE(status) ? -ENODEV : 0;
}
struct pnp_protocol pnpacpi_protocol = {
.name = "Plug and Play ACPI",
.get = pnpacpi_get_resources,
.set = pnpacpi_set_resources,
.disable = pnpacpi_disable_resources,
};
static int acpi_pnp_add(struct acpi_device *device)
{
acpi_handle temp = NULL;
acpi_status status;
struct pnp_id *dev_id;
struct pnp_dev *dev;
pnp_dbg("ACPI device : hid %s", acpi_device_hid(device));
dev = pnpacpi_kmalloc(sizeof(struct pnp_dev), GFP_KERNEL);
if (!dev) {
pnp_err("Out of memory");
return -ENOMEM;
}
dev->data = device->handle;
/* .enabled means if the device can decode the resources */
dev->active = device->status.enabled;
status = acpi_get_handle(device->handle, "_SRS", &temp);
if (ACPI_SUCCESS(status))
dev->capabilities |= PNP_CONFIGURABLE;
dev->capabilities |= PNP_READ;
if (device->flags.dynamic_status)
dev->capabilities |= PNP_WRITE;
if (device->flags.removable)
dev->capabilities |= PNP_REMOVABLE;
status = acpi_get_handle(device->handle, "_DIS", &temp);
if (ACPI_SUCCESS(status))
dev->capabilities |= PNP_DISABLE;
dev->protocol = &pnpacpi_protocol;
if (strlen(acpi_device_name(device)))
strncpy(dev->name, acpi_device_name(device), sizeof(dev->name));
else
strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name));
dev->number = num;
/* set the initial values for the PnP device */
dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id), GFP_KERNEL);
if (!dev_id)
goto err;
pnpidacpi_to_pnpid(acpi_device_hid(device), dev_id->id);
pnp_add_id(dev_id, dev);
if(dev->active) {
/* parse allocated resource */
status = pnpacpi_parse_allocated_resource(device->handle, &dev->res);
if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
pnp_err("PnPACPI: METHOD_NAME__CRS failure for %s", dev_id->id);
goto err1;
}
}
if(dev->capabilities & PNP_CONFIGURABLE) {
status = pnpacpi_parse_resource_option_data(device->handle,
dev);
if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
pnp_err("PnPACPI: METHOD_NAME__PRS failure for %s", dev_id->id);
goto err1;
}
}
/* parse compatible ids */
if (device->flags.compatible_ids) {
struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;
int i;
for (i = 0; i < cid_list->count; i++) {
if (!ispnpidacpi(cid_list->id[i].value))
continue;
dev_id = pnpacpi_kmalloc(sizeof(struct pnp_id),
GFP_KERNEL);
if (!dev_id)
continue;
pnpidacpi_to_pnpid(cid_list->id[i].value, dev_id->id);
pnp_add_id(dev_id, dev);
}
}
/* clear out the damaged flags */
if (!dev->active)
pnp_init_resource_table(&dev->res);
pnp_add_device(dev);
num ++;
acpi_driver_data(device) = dev;
return AE_OK;
err1:
kfree(dev_id);
err:
kfree(dev);
return -EINVAL;
}
static int acpi_pnp_remove (struct acpi_device *device, int type)
{
struct pnp_dev *dev = acpi_driver_data(device);
if (!dev)
return AE_ERROR;
pnp_remove_device(dev);
return AE_OK;
}
static int acpi_pnp_match(struct acpi_device *device,
struct acpi_driver *driver)
{
return (!ispnpidacpi(acpi_device_hid(device)) ||
is_exclusive_device(device));
}
/* default acpi PNP device driver, support hotplug */
static struct acpi_driver acpi_pnp_driver = {
.name = "ACPI PNP Driver",
.class = "acpi_pnp",
.ops = {
.add = acpi_pnp_add,
.remove = acpi_pnp_remove,
.match = acpi_pnp_match,
},
};
int __init pnpacpi_init(void)
{
if (acpi_disabled) {
pnp_info("PnP ACPI: ACPI disable");
return 0;
}
pnp_info("PnP ACPI init");
pnp_register_protocol(&pnpacpi_protocol);
if (acpi_bus_register_driver(&acpi_pnp_driver) < 0)
return -ENODEV;
pnp_info("PnP ACPI: found %d devices", num);
return 0;
}
subsys_initcall(pnpacpi_init);
EXPORT_SYMBOL(pnpacpi_protocol);
#ifndef ACPI_PNP_H
#define ACPI_PNP_H
#include <acpi/acpi_bus.h>
#include <linux/acpi.h>
#include <linux/pnp.h>
void *pnpacpi_kmalloc(size_t size, int f);
acpi_status pnpacpi_parse_allocated_resource(acpi_handle, struct pnp_resource_table*);
acpi_status pnpacpi_parse_resource_option_data(acpi_handle, struct pnp_dev*);
int pnpacpi_encode_resources(struct pnp_resource_table *, struct acpi_buffer *);
int pnpacpi_build_resource_template(acpi_handle, struct acpi_buffer*);
#endif
This diff is collapsed.
......@@ -3,7 +3,8 @@
#
config PNPBIOS
bool "Plug and Play BIOS support (EXPERIMENTAL)"
depends on PNP && X86 && EXPERIMENTAL
depends on PNP && ISA && X86 && EXPERIMENTAL
default n
---help---
Linux uses the PNPBIOS as defined in "Plug and Play BIOS
Specification Version 1.0A May 5, 1994" to autodetect built-in
......
......@@ -531,6 +531,15 @@ int __init pnpbios_init(void)
{
int ret;
/* Don't use pnpbios if pnpacpi is used */
#ifdef CONFIG_PNPACPI
if (!acpi_disabled) {
pnpbios_disabled = 1;
printk(KERN_INFO "PnPBIOS: Disabled by pnpacpi\n");
return -ENODEV;
}
#endif
if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table)) {
printk(KERN_INFO "PnPBIOS: Disabled\n");
return -ENODEV;
......@@ -572,6 +581,8 @@ subsys_initcall(pnpbios_init);
static int __init pnpbios_thread_init(void)
{
if (pnpbios_disabled)
return 0;
#ifdef CONFIG_HOTPLUG
init_completion(&unload_sem);
if (kernel_thread(pnp_dock_thread, NULL, CLONE_KERNEL) > 0)
......
......@@ -421,6 +421,7 @@ int pnp_check_irq(struct pnp_dev * dev, int idx)
int pnp_check_dma(struct pnp_dev * dev, int idx)
{
#ifndef CONFIG_IA64
int tmp;
struct pnp_dev *tdev;
unsigned long * dma = &dev->res.dma_resource[idx].start;
......@@ -470,6 +471,10 @@ int pnp_check_dma(struct pnp_dev * dev, int idx)
}
return 1;
#else
/* IA64 hasn't legacy DMA */
return 0;
#endif
}
......
......@@ -200,6 +200,7 @@ struct pnp_dev {
unsigned short regs; /* ISAPnP: supported registers */
int flags; /* used by protocols */
struct proc_dir_entry *procent; /* device entry in /proc/bus/isapnp */
void *data;
};
#define global_to_pnp_dev(n) list_entry(n, struct pnp_dev, global_list)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment