On Wed, 2005-01-05 at 10:50, Li Shaohua wrote:
> Hi,
> The series of patches implement binding physical devices with ACPI
> devices. With it, device drivers can utilize methods provided by
> firmware (ACPI). These patches are against 2.6.10, please give your
> comments.
Hi,
This is updated patches according to latest discussion.
Changes from last one:
1. introduce new field 'firmware_data' in 'struct device', since people
complain rename 'platform_data. Greg, could you please check if the
comments I added in 'struct device' are correct?
2. align to Pavel's latest PCI state convention work.
3. Some cleanups and add more comments.
One issue is 'platform_pci_choose_state' doesn't get called, it should
be after Pavel updates the parameter of 'pci_choose_state'

Thanks,
Shaohua

This patch implemented the framework for binding physical devices with ACPI
devices. A physical bus like PCI bus should create a 'acpi_bus_type'.
The method in 'acpi_bus_type':
.find_device:
	For device which has parent such as normal PCI devices.
.find_bridge:
	It's for special devices, such as PCI root bridge and IDE controller. 
such devices generally haven't parent or ->bus. We use the special method 
to get an ACPI handle.

---

 2.5-root/drivers/acpi/Makefile   |    2 
 2.5-root/drivers/acpi/glue.c     |  360 +++++++++++++++++++++++++++++++++++++++
 2.5-root/drivers/acpi/ibm_acpi.c |    4 
 2.5-root/include/acpi/acpi_bus.h |   21 ++
 2.5-root/include/linux/device.h  |    6 
 5 files changed, 388 insertions(+), 5 deletions(-)

diff -puN /dev/null drivers/acpi/glue.c
--- /dev/null	2004-02-24 05:02:56.000000000 +0800
+++ 2.5-root/drivers/acpi/glue.c	2005-01-17 12:52:16.825046520 +0800
@@ -0,0 +1,360 @@
+/*
+ * Link physical devices with ACPI devices support
+ */
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/rwsem.h>
+#include <linux/acpi.h>
+
+#define ACPI_GLUE_DEBUG	0
+#if ACPI_GLUE_DEBUG
+#define DBG(x...) printk(PREFIX x)
+#else
+#define DBG(x...)
+#endif
+static LIST_HEAD(bus_type_list);
+static DECLARE_RWSEM(bus_type_sem);
+
+int register_acpi_bus_type(struct acpi_bus_type *type)
+{
+	if (acpi_disabled)
+		return -ENODEV;
+	if (type && type->bus && type->find_device) {
+		down_write(&bus_type_sem);
+		list_add_tail(&type->list, &bus_type_list);
+		up_write(&bus_type_sem);
+		DBG("ACPI bus type %s registered\n", type->bus->name);
+		return 0;
+	}
+	return -ENODEV;
+}
+EXPORT_SYMBOL(register_acpi_bus_type);
+
+int unregister_acpi_bus_type(struct acpi_bus_type *type)
+{
+	if (acpi_disabled)
+		return 0;
+	if (type) {
+		down_write(&bus_type_sem);
+		list_del_init(&type->list);
+		up_write(&bus_type_sem);
+		DBG("ACPI bus type %s unregistered\n", type->bus->name);
+		return 0;
+	}
+	return -ENODEV;
+}
+EXPORT_SYMBOL(unregister_acpi_bus_type);
+
+static struct acpi_bus_type *
+acpi_get_bus_type(struct bus_type *type)
+{
+	struct acpi_bus_type *tmp, *ret = NULL;
+
+	down_read(&bus_type_sem);
+	list_for_each_entry(tmp, &bus_type_list, list) {
+		if (tmp->bus == type) {
+			ret = tmp;
+			break;
+		}
+	}
+	up_read(&bus_type_sem);
+	return ret;
+}
+
+static int
+acpi_find_bridge_device(struct device *dev, acpi_handle *handle)
+{
+	struct acpi_bus_type *tmp;
+	int	ret = -ENODEV;
+
+	down_read(&bus_type_sem);
+	list_for_each_entry(tmp, &bus_type_list, list) {
+		if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
+			ret = 0;
+			break;
+		}
+	}
+	up_read(&bus_type_sem);
+	return ret;
+}
+
+/* Get PCI root bridge's handle from its segment and bus number */
+struct acpi_find_pci_root {
+	unsigned int seg;
+	unsigned int bus;
+	acpi_handle handle;
+};
+
+static acpi_status
+do_root_bridge_busnr_callback (struct acpi_resource *resource, void *data)
+{
+	int *busnr = (int *)data;
+	struct acpi_resource_address64 address;
+
+	if (resource->id != ACPI_RSTYPE_ADDRESS16 &&
+	    resource->id != ACPI_RSTYPE_ADDRESS32 &&
+	    resource->id != ACPI_RSTYPE_ADDRESS64)
+		return AE_OK;
+
+	acpi_resource_to_address64(resource, &address);
+	if ((address.address_length > 0) &&
+	   (address.resource_type == ACPI_BUS_NUMBER_RANGE))
+		*busnr = address.min_address_range;
+
+	return AE_OK;
+}
+
+static int
+get_root_bridge_busnr(acpi_handle handle)
+{
+	acpi_status status;
+	int bus, bbn;
+	struct acpi_buffer	buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+	acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+
+	status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL,
+		(unsigned long *)&bbn);
+	if (status == AE_NOT_FOUND) {
+		/* Assume bus = 0 */
+		printk(KERN_INFO PREFIX
+			"Assume root bridge [%s] bus is 0\n",
+			(char *)buffer.pointer);
+		status = AE_OK;
+		bbn = 0;
+	}
+	if (ACPI_FAILURE(status)) {
+		bbn = -ENODEV;
+		goto exit;
+	}
+	if (bbn > 0)
+		goto exit;
+
+	/* _BBN in some systems return 0 for all root bridges */
+	bus = -1;
+	status = acpi_walk_resources(handle, METHOD_NAME__CRS,
+		do_root_bridge_busnr_callback, &bus);
+	/* If _CRS failed, we just use _BBN */
+	if (ACPI_FAILURE(status) || (bus == -1))
+		goto exit;
+	/* We select _CRS */
+	if (bbn != bus) {
+		printk(KERN_INFO PREFIX
+			"_BBN and _CRS returns different value for %s. Select _CRS\n",
+			(char*)buffer.pointer);
+		bbn = bus;
+	}
+exit:
+	acpi_os_free(buffer.pointer);
+	return bbn;
+}
+
+static acpi_status
+find_pci_rootbridge(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	struct acpi_find_pci_root *find = (struct acpi_find_pci_root *)context;
+	unsigned long seg, bus;
+	acpi_status status;
+	int tmp;
+	struct acpi_buffer	buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+	acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+
+	status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL,
+		&seg);
+	if (status == AE_NOT_FOUND) {
+		/* Assume seg = 0 */
+		printk(KERN_INFO PREFIX
+			"Assume root bridge [%s] segment is 0\n",
+			(char *)buffer.pointer);
+		status = AE_OK;
+		seg = 0;
+	}
+	if (ACPI_FAILURE(status)) {
+		status = AE_CTRL_DEPTH;
+		goto exit;
+	}
+
+	tmp = get_root_bridge_busnr(handle);
+	if (tmp < 0) {
+		printk(KERN_ERR PREFIX
+			"Find root bridge failed for %s\n",
+			(char*)buffer.pointer);
+		status = AE_CTRL_DEPTH;
+		goto exit;
+	}
+	bus = tmp;
+
+	if (seg == find->seg && bus == find->bus)
+		find->handle = handle;
+	status = AE_OK;
+exit:
+	acpi_os_free(buffer.pointer);
+	return status;
+}
+
+acpi_handle
+acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
+{
+	struct acpi_find_pci_root find = {seg, bus, NULL};
+
+	acpi_get_devices(PCI_ROOT_HID_STRING,
+		find_pci_rootbridge,
+		&find,
+		NULL);
+	return find.handle;
+}
+
+/* Get device's handler per its address under its parent */
+struct acpi_find_child {
+	acpi_handle     handle;
+	acpi_integer    address;
+};
+
+static acpi_status
+do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status     status;
+	struct acpi_device_info *info;
+	struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+	struct acpi_find_child *find = (struct acpi_find_child*)context;
+
+	status = acpi_get_object_info(handle, &buffer);
+	if (ACPI_SUCCESS(status)) {
+		info = buffer.pointer;
+		if (info->address == find->address)
+			find->handle = handle;
+		acpi_os_free(buffer.pointer);
+	}
+	return AE_OK;
+}
+
+acpi_handle
+acpi_get_child(acpi_handle parent, acpi_integer address)
+{
+	struct acpi_find_child find = {NULL, address};
+
+	if (!parent)
+		return NULL;
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
+		1, do_acpi_find_child,
+		&find, NULL);
+	return find.handle;
+}
+EXPORT_SYMBOL(acpi_get_child);
+
+/* Link ACPI devices with physical devices */
+static void acpi_glue_data_handler(acpi_handle handle,
+	u32 function, void *context)
+{
+	/* we provide an empty handler */
+}
+
+/* Note: a success call will increase reference count by one */
+struct device *acpi_get_physical_device(acpi_handle handle)
+{
+	acpi_status status;
+	struct device *dev;
+
+	status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev);
+	if (ACPI_SUCCESS(status))
+		return get_device(dev);
+	return NULL;
+}
+EXPORT_SYMBOL(acpi_get_physical_device);
+
+static int acpi_bind_one(struct device *dev, acpi_handle handle)
+{
+	acpi_status status;
+
+	if (dev->firmware_data) {
+		printk(KERN_WARNING PREFIX
+		   "Drivers changed 'firmware_data' for %s\n", dev->bus_id);
+		return -EINVAL;
+	}
+	get_device(dev);
+	status = acpi_attach_data(handle, acpi_glue_data_handler, dev);
+	if (ACPI_FAILURE(status)) {
+		put_device(dev);
+		return -EINVAL;
+	}
+	dev->firmware_data = handle;
+
+	return 0;
+}
+
+static int acpi_unbind_one(struct device *dev)
+{
+	if (!dev->firmware_data)
+		return 0;
+	if (dev == acpi_get_physical_device(dev->firmware_data)) {
+		/* acpi_get_physical_device increase refcnt by one */
+		put_device(dev);
+		acpi_detach_data(dev->firmware_data, acpi_glue_data_handler);
+		dev->firmware_data = NULL;
+		/* acpi_bind_one increase refcnt by one */
+		put_device(dev);
+	} else {
+		printk(KERN_ERR PREFIX
+			"Oops, 'firmware_data' corrupt for %s\n", dev->bus_id);
+	}
+	return 0;
+}
+
+static int acpi_platform_notify (struct device *dev)
+{
+	struct acpi_bus_type	*type;
+	acpi_handle		handle;
+	int			ret = -EINVAL;
+
+	if (!dev->bus || !dev->parent) {
+		/* bridge devices genernally haven't bus or parent */
+		ret = acpi_find_bridge_device(dev, &handle);
+		goto end;
+	}
+	type = acpi_get_bus_type(dev->bus);
+	if (!type) {
+		printk(KERN_INFO PREFIX "No ACPI bus support for %s\n", dev->bus_id);
+		ret = -EINVAL;
+		goto end;
+	}
+	if ((ret = type->find_device(dev, &handle)) != 0)
+		printk(KERN_INFO PREFIX "Can't get handler for %s\n", dev->bus_id);
+end:
+	if (!ret)
+		acpi_bind_one(dev, handle);
+
+#if ACPI_GLUE_DEBUG
+	if (!ret) {
+		struct acpi_buffer      buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+		acpi_get_name(dev->firmware_data, ACPI_FULL_PATHNAME, &buffer);
+		DBG("Device %s -> %s\n", dev->bus_id, (char *)buffer.pointer);
+		acpi_os_free(buffer.pointer);
+	} else
+		DBG("Device %s -> No ACPI support\n", dev->bus_id);
+#endif
+
+	return ret;
+}
+
+static int acpi_platform_notify_remove(struct device *dev)
+{
+	acpi_unbind_one(dev);
+	return 0;
+}
+
+static int __init init_acpi_device_notify(void)
+{
+	if (acpi_disabled)
+		return 0;
+	if (platform_notify || platform_notify_remove) {
+		printk(KERN_ERR PREFIX "Can't use platform_notify\n");
+		return 0;
+	}
+	platform_notify = acpi_platform_notify;
+	platform_notify_remove = acpi_platform_notify_remove;
+	return 0;
+}
+arch_initcall(init_acpi_device_notify);
diff -puN drivers/acpi/ibm_acpi.c~bind-acpi-devcore drivers/acpi/ibm_acpi.c
--- 2.5/drivers/acpi/ibm_acpi.c~bind-acpi-devcore	2005-01-17 12:52:16.815048040 +0800
+++ 2.5-root/drivers/acpi/ibm_acpi.c	2005-01-17 12:52:16.826046368 +0800
@@ -1025,7 +1025,7 @@ static int setup_notify(struct ibm_struc
 	return 0;
 }
 
-static int device_add(struct acpi_device *device)
+static int ibmacpi_device_add(struct acpi_device *device)
 {
 	return 0;
 }
@@ -1043,7 +1043,7 @@ static int register_driver(struct ibm_st
 	memset(ibm->driver, 0, sizeof(struct acpi_driver));
 	sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name);
 	ibm->driver->ids = ibm->hid;
-	ibm->driver->ops.add = &device_add;
+	ibm->driver->ops.add = &ibmacpi_device_add;
 
 	ret = acpi_bus_register_driver(ibm->driver);
 	if (ret < 0) {
diff -puN drivers/acpi/Makefile~bind-acpi-devcore drivers/acpi/Makefile
--- 2.5/drivers/acpi/Makefile~bind-acpi-devcore	2005-01-17 12:52:16.817047736 +0800
+++ 2.5-root/drivers/acpi/Makefile	2005-01-17 12:52:16.826046368 +0800
@@ -36,7 +36,7 @@ processor-objs	+= processor_perflib.o			
 endif
 
 obj-$(CONFIG_ACPI_BUS)		+= sleep/
-obj-$(CONFIG_ACPI_BUS)		+= bus.o
+obj-$(CONFIG_ACPI_BUS)		+= bus.o glue.o
 obj-$(CONFIG_ACPI_AC) 		+= ac.o
 obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
 obj-$(CONFIG_ACPI_BUTTON)	+= button.o
diff -puN include/acpi/acpi_bus.h~bind-acpi-devcore include/acpi/acpi_bus.h
--- 2.5/include/acpi/acpi_bus.h~bind-acpi-devcore	2005-01-17 12:52:16.818047584 +0800
+++ 2.5-root/include/acpi/acpi_bus.h	2005-01-17 12:52:16.826046368 +0800
@@ -337,6 +337,27 @@ int acpi_match_ids (struct acpi_device	*
 int acpi_create_dir(struct acpi_device *);
 void acpi_remove_dir(struct acpi_device *);
 
+
+/*
+ * Bind physical devices with ACPI devices
+ */
+#include <linux/device.h>
+struct acpi_bus_type {
+	struct list_head	list;
+	struct bus_type		*bus;
+	/* For general devices under the bus*/
+	int (*find_device)(struct device *, acpi_handle*);
+	/* For bridges, such as PCI root bridge, IDE controller */
+	int (*find_bridge)(struct device *, acpi_handle *);
+};
+int register_acpi_bus_type(struct acpi_bus_type *);
+int unregister_acpi_bus_type(struct acpi_bus_type *);
+struct device *acpi_get_physical_device(acpi_handle);
+/* helper */
+acpi_handle acpi_get_child(acpi_handle, acpi_integer);
+acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
+#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->firmware_data))
+
 #endif /*CONFIG_ACPI_BUS*/
 
 #endif /*__ACPI_BUS_H__*/
diff -puN include/linux/device.h~bind-acpi-devcore include/linux/device.h
--- 2.5/include/linux/device.h~bind-acpi-devcore	2005-01-17 12:52:16.820047280 +0800
+++ 2.5-root/include/linux/device.h	2005-01-17 12:52:16.827046216 +0800
@@ -268,8 +268,10 @@ struct device {
 	struct device_driver *driver;	/* which driver has allocated this
 					   device */
 	void		*driver_data;	/* data private to the driver */
-	void		*platform_data;	/* Platform specific data (e.g. ACPI,
-					   BIOS data relevant to device) */
+	void		*platform_data;	/* Platform specific data, device
+					   core doesn't touch it */
+	void		*firmware_data; /* Firmware specific data (e.g. ACPI,
+					   BIOS data),reserved for device core*/
 	struct dev_pm_info	power;
 
 	u32		detach_state;	/* State to enter when device is
_
An implementation for binding ACPI devices with PCI devices.

---

 2.5-root/drivers/pci/pci-acpi.c |   50 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 50 insertions(+)

diff -puN drivers/pci/pci-acpi.c~bind-acpi-pci drivers/pci/pci-acpi.c
--- 2.5/drivers/pci/pci-acpi.c~bind-acpi-pci	2005-01-17 12:52:50.100987808 +0800
+++ 2.5-root/drivers/pci/pci-acpi.c	2005-01-17 12:53:52.365522160 +0800
@@ -207,3 +207,53 @@ acpi_status pci_osc_control_set(u32 flag
 	return status;
 }
 EXPORT_SYMBOL(pci_osc_control_set);
+
+/* ACPI bus type */
+static int pci_acpi_find_device(struct device *dev, acpi_handle *handle)
+{
+	struct pci_dev * pci_dev;
+	acpi_integer	addr;
+
+	pci_dev = to_pci_dev(dev);
+	/* Please ref to ACPI spec for the syntax of _ADR */
+	addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn);
+	*handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr);
+	if (!*handle)
+		return -ENODEV;
+	return 0;
+}
+
+static int pci_acpi_find_root_bridge(struct device *dev, acpi_handle *handle)
+{
+	int num;
+	unsigned int seg, bus;
+
+	/*
+	 * The string should be the same as root bridge's name
+	 * Please look at 'pci_scan_bus_parented'
+	 */
+	num = sscanf(dev->bus_id, "pci%04x:%02x", &seg, &bus);
+	if (num != 2)
+		return -ENODEV;
+	*handle = acpi_get_pci_rootbridge_handle(seg, bus);
+	if (!*handle)
+		return -ENODEV;
+	return 0;
+}
+
+static struct acpi_bus_type pci_acpi_bus = {
+	.bus = &pci_bus_type,
+	.find_device = pci_acpi_find_device,
+	.find_bridge = pci_acpi_find_root_bridge,
+};
+
+static int __init pci_acpi_init(void)
+{
+	int ret;
+
+	ret = register_acpi_bus_type(&pci_acpi_bus);
+	if (ret)
+		return 0;
+	return 0;
+}
+arch_initcall(pci_acpi_init);
_
An ACPI callback for getting suspend state

---

 2.5-root/drivers/pci/pci-acpi.c |   22 ++++++++++++++++++++++
 2.5-root/drivers/pci/pci.c      |    1 +
 2.5-root/drivers/pci/pci.h      |    4 ++++
 3 files changed, 27 insertions(+)

diff -puN drivers/pci/pci-acpi.c~acpi-pci-get-suspend-state-callback drivers/pci/pci-acpi.c
--- 2.5/drivers/pci/pci-acpi.c~acpi-pci-get-suspend-state-callback	2005-01-17 12:54:05.355547376 +0800
+++ 2.5-root/drivers/pci/pci-acpi.c	2005-01-17 13:09:46.266507168 +0800
@@ -16,6 +16,7 @@
 #include <acpi/acpi_bus.h>
 
 #include <linux/pci-acpi.h>
+#include "pci.h"
 
 static u32 ctrlset_buf[3] = {0, 0, 0};
 static u32 global_ctrlsets = 0;
@@ -208,6 +209,25 @@ acpi_status pci_osc_control_set(u32 flag
 }
 EXPORT_SYMBOL(pci_osc_control_set);
 
+static int acpi_pci_choose_state(struct pci_dev *pdev,
+	pm_message_t state)
+{
+	char dstate_str[] = "_S0D";
+	acpi_status status;
+	unsigned long val;
+	struct device *dev = &pdev->dev;
+
+	/* state is PM_SUSPEND_* */
+	if ((state >= PM_SUSPEND_MAX) || !DEVICE_ACPI_HANDLE(dev))
+		return -EINVAL;
+	dstate_str[2] += (int __force)state;
+	status = acpi_evaluate_integer(DEVICE_ACPI_HANDLE(dev), dstate_str,
+		NULL, &val);
+	if (ACPI_SUCCESS(status))
+		return val;
+	return -EINVAL;;
+}
+
 /* ACPI bus type */
 static int pci_acpi_find_device(struct device *dev, acpi_handle *handle)
 {
@@ -254,6 +274,8 @@ static int __init pci_acpi_init(void)
 	ret = register_acpi_bus_type(&pci_acpi_bus);
 	if (ret)
 		return 0;
+	if (!platform_pci_choose_state)
+		platform_pci_choose_state = acpi_pci_choose_state;
 	return 0;
 }
 arch_initcall(pci_acpi_init);
diff -puN drivers/pci/pci.c~acpi-pci-get-suspend-state-callback drivers/pci/pci.c
--- 2.5/drivers/pci/pci.c~acpi-pci-get-suspend-state-callback	2005-01-17 12:54:05.357547072 +0800
+++ 2.5-root/drivers/pci/pci.c	2005-01-17 13:08:50.835933896 +0800
@@ -317,6 +317,7 @@ pci_set_power_state(struct pci_dev *dev,
  * Returns PCI power state suitable for given device and given system
  * message.
  */
+int (*platform_pci_choose_state)(struct pci_dev *, pm_message_t) = 0;
 
 pci_power_t pci_choose_state(struct pci_dev *dev, u32 state)
 {
diff -puN drivers/pci/pci.h~acpi-pci-get-suspend-state-callback drivers/pci/pci.h
--- 2.5/drivers/pci/pci.h~acpi-pci-get-suspend-state-callback	2005-01-17 12:54:05.358546920 +0800
+++ 2.5-root/drivers/pci/pci.h	2005-01-17 13:08:50.835933896 +0800
@@ -11,6 +11,10 @@ extern int pci_bus_alloc_resource(struct
 				  void (*alignf)(void *, struct resource *,
 					  	 unsigned long, unsigned long),
 				  void *alignf_data);
+/* return >= 0 if platform can get correct suspend state */
+extern int (*platform_pci_choose_state)(struct pci_dev *pdev,
+	pm_message_t state);
+
 /* PCI /proc functions */
 #ifdef CONFIG_PROC_FS
 extern int pci_proc_attach_device(struct pci_dev *dev);
_

---

 2.5-root/drivers/acpi/bus.c     |    7 +++++++
 2.5-root/drivers/pci/pci-acpi.c |   15 +++++++++++++++
 2.5-root/drivers/pci/pci.c      |    9 +++++++++
 2.5-root/drivers/pci/pci.h      |    3 +++
 4 files changed, 34 insertions(+)

diff -puN drivers/acpi/bus.c~acpi-pci-set-power-state-callback drivers/acpi/bus.c
--- 2.5/drivers/acpi/bus.c~acpi-pci-set-power-state-callback	2005-01-17 13:09:51.308740632 +0800
+++ 2.5-root/drivers/acpi/bus.c	2005-01-17 13:09:51.316739416 +0800
@@ -212,6 +212,13 @@ acpi_bus_set_power (
 		ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Device is not power manageable\n"));
 		return_VALUE(-ENODEV);
 	}
+	/*
+	 * Get device's current power state if it's unknown
+	 * This means device power state isn't initialized
+	 * or previous setting failed
+	 */
+	if (device->power.state == ACPI_STATE_UNKNOWN)
+		acpi_bus_get_power(device->handle, &device->power.state);
 	if (state == device->power.state) {
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", state));
 		return_VALUE(0);
diff -puN drivers/pci/pci-acpi.c~acpi-pci-set-power-state-callback drivers/pci/pci-acpi.c
--- 2.5/drivers/pci/pci-acpi.c~acpi-pci-set-power-state-callback	2005-01-17 13:09:51.309740480 +0800
+++ 2.5-root/drivers/pci/pci-acpi.c	2005-01-17 13:09:51.316739416 +0800
@@ -228,6 +228,19 @@ static int acpi_pci_choose_state(struct 
 	return -EINVAL;;
 }
 
+static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
+{
+	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	int acpi_state;
+
+	if (!handle)
+		return -ENODEV;
+	acpi_state = (int __force)state;
+	if (state == PCI_D3cold)
+		acpi_state = 3;
+	return acpi_bus_set_power(handle, acpi_state);
+}
+
 /* ACPI bus type */
 static int pci_acpi_find_device(struct device *dev, acpi_handle *handle)
 {
@@ -276,6 +289,8 @@ static int __init pci_acpi_init(void)
 		return 0;
 	if (!platform_pci_choose_state)
 		platform_pci_choose_state = acpi_pci_choose_state;
+	if (!platform_pci_set_power_state)
+		platform_pci_set_power_state = acpi_pci_set_power_state;
 	return 0;
 }
 arch_initcall(pci_acpi_init);
diff -puN drivers/pci/pci.c~acpi-pci-set-power-state-callback drivers/pci/pci.c
--- 2.5/drivers/pci/pci.c~acpi-pci-set-power-state-callback	2005-01-17 13:09:51.311740176 +0800
+++ 2.5-root/drivers/pci/pci.c	2005-01-17 13:09:51.317739264 +0800
@@ -240,6 +240,7 @@ pci_find_parent_resource(const struct pc
  * -EIO if device does not support PCI PM.
  * 0 if we can successfully change the power state.
  */
+int (*platform_pci_set_power_state)(struct pci_dev*, pci_power_t) = NULL;
 
 int
 pci_set_power_state(struct pci_dev *dev, pci_power_t state)
@@ -304,6 +305,14 @@ pci_set_power_state(struct pci_dev *dev,
 		msleep(10);
 	else if (state == PCI_D2 || dev->current_state == PCI_D2)
 		udelay(200);
+
+	/*
+	 * Give firmware a chance to be called, such as ACPI _PRx, _PSx
+	 * Firmware method after natice method ?
+	 */
+	if (platform_pci_set_power_state)
+		platform_pci_set_power_state(dev, state);
+
 	dev->current_state = state;
 
 	return 0;
diff -puN drivers/pci/pci.h~acpi-pci-set-power-state-callback drivers/pci/pci.h
--- 2.5/drivers/pci/pci.h~acpi-pci-set-power-state-callback	2005-01-17 13:09:51.312740024 +0800
+++ 2.5-root/drivers/pci/pci.h	2005-01-17 13:11:19.458339856 +0800
@@ -14,6 +14,9 @@ extern int pci_bus_alloc_resource(struct
 /* return >= 0 if platform can get correct suspend state */
 extern int (*platform_pci_choose_state)(struct pci_dev *pdev,
 	pm_message_t state);
+/* return  0 if success, < 0 failed */
+extern int (*platform_pci_set_power_state)(struct pci_dev *pdev,
+	pci_power_t state);
 
 /* PCI /proc functions */
 #ifdef CONFIG_PROC_FS
_

Reply via email to