]> git.ipfire.org Git - ipfire-2.x.git/blob - src/patches/suse-2.6.27.39/patches.suse/bug-504646-acpi-enable-root-bridge-to-wakeup.patch
Imported linux-2.6.27.39 suse/xen patches.
[ipfire-2.x.git] / src / patches / suse-2.6.27.39 / patches.suse / bug-504646-acpi-enable-root-bridge-to-wakeup.patch
1 From: Rafael J. Wysocki <rjw@suse.de>
2 Subject: ACPI / PM: Allow PCI root bridges to wake up the system
3 References: bnc#504646
4
5 Wake-on-LAN with PCI network adapters currently doesn't work if the
6 adapter is not directly supported by ACPI. In particular, it doesn't
7 work with add-on PCI (non-PCIe) adapters.
8
9 This problem has recently been fixed in the mainline kernel by
10 propagating the wake-up enable setting from the device to its
11 upstream bridge and, if necessary, further up to the root bridge.
12 Then, if one of the bridges on the path from the device to the root
13 bridge (or the root bridge itself) is an ACPI wake-up device, a PME
14 from the adapter causes the bridge's GPE to generate a wake-up
15 event. Unfortunately, porting this solution into the SLE11 code base
16 would break KABI, so for systems that are known to have this problem,
17 if PCI root bridge is declared in the ACPI tables as a wake-up
18 device, allow it to wake up the system by default (this usually is
19 functionally equivalent the upstream solution, but it may cause
20 regressions to appear on the systems where this is not the case, so
21 use a DMI-based white list of systems that require it).
22
23 This allows add-on PCI devices to work as wake-up devices on some
24 systems, where PME# asserted by an add-on device causes the root
25 bridge GPE to generate a wake-up event, without using
26 /proc/acpi/wakeup to change the root bridge wake-up setting.
27
28 Signed-off-by: Rafael J. Wysocki <rjw@suse.de>
29 ---
30 drivers/acpi/glue.c | 15 +++++++++++++++
31 drivers/acpi/pci_root.c | 9 +++++++++
32 drivers/acpi/sleep/main.c | 14 ++++++++++++++
33 drivers/acpi/sleep/proc.c | 29 +----------------------------
34 drivers/acpi/sleep/sleep.h | 6 ++++++
35 drivers/acpi/sleep/wakeup.c | 35 +++++++++++++++++++++++++++++++++++
36 include/acpi/acpi_bus.h | 1 +
37 7 files changed, 81 insertions(+), 28 deletions(-)
38
39 Index: linux-2.6.27-SLE11_BRANCH/drivers/acpi/glue.c
40 ===================================================================
41 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/acpi/glue.c
42 +++ linux-2.6.27-SLE11_BRANCH/drivers/acpi/glue.c
43 @@ -12,6 +12,14 @@
44 #include <linux/rwsem.h>
45 #include <linux/acpi.h>
46
47 +#include "sleep/sleep.h"
48 +
49 +/*
50 + * If set, enable the PCI root bridge to wake-up the system from sleep states
51 + * by default.
52 + */
53 +bool acpi_root_bridge_wakeup;
54 +
55 #define ACPI_GLUE_DEBUG 0
56 #if ACPI_GLUE_DEBUG
57 #define DBG(x...) printk(PREFIX x)
58 @@ -207,6 +215,13 @@ static int acpi_bind_one(struct device *
59 "physical_node");
60 if (acpi_dev->wakeup.flags.valid) {
61 device_set_wakeup_capable(dev, true);
62 + if (acpi_root_bridge_wakeup
63 + && acpi_dev_is_root_bridge(acpi_dev)) {
64 + spin_lock(&acpi_device_lock);
65 + acpi_dev->wakeup.state.enabled = true;
66 + propagate_enable_wakeup(acpi_dev);
67 + spin_unlock(&acpi_device_lock);
68 + }
69 device_set_wakeup_enable(dev,
70 acpi_dev->wakeup.state.enabled);
71 }
72 Index: linux-2.6.27-SLE11_BRANCH/drivers/acpi/pci_root.c
73 ===================================================================
74 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/acpi/pci_root.c
75 +++ linux-2.6.27-SLE11_BRANCH/drivers/acpi/pci_root.c
76 @@ -135,6 +135,15 @@ acpi_handle acpi_get_pci_rootbridge_hand
77
78 EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
79
80 +/**
81 + * acpi_dev_is_root_bridge - determine if an ACPI device is a PCI root bridge
82 + * @device: ACPI device to check.
83 + */
84 +bool acpi_dev_is_root_bridge(struct acpi_device *device)
85 +{
86 + return !acpi_match_device_ids(device, root_device_ids);
87 +}
88 +
89 static acpi_status
90 get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data)
91 {
92 Index: linux-2.6.27-SLE11_BRANCH/include/acpi/acpi_bus.h
93 ===================================================================
94 --- linux-2.6.27-SLE11_BRANCH.orig/include/acpi/acpi_bus.h
95 +++ linux-2.6.27-SLE11_BRANCH/include/acpi/acpi_bus.h
96 @@ -380,6 +380,7 @@ struct device *acpi_get_physical_pci_dev
97
98 /* helper */
99 acpi_handle acpi_get_child(acpi_handle, acpi_integer);
100 +bool acpi_dev_is_root_bridge(struct acpi_device *);
101 acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
102 #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle))
103
104 Index: linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/wakeup.c
105 ===================================================================
106 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/acpi/sleep/wakeup.c
107 +++ linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/wakeup.c
108 @@ -142,6 +142,41 @@ void acpi_disable_wakeup_device(u8 sleep
109 spin_unlock(&acpi_device_lock);
110 }
111
112 +void physical_device_enable_wakeup(struct acpi_device *adev)
113 +{
114 + struct device *dev = acpi_get_physical_device(adev->handle);
115 +
116 + if (dev && device_can_wakeup(dev))
117 + device_set_wakeup_enable(dev, adev->wakeup.state.enabled);
118 +}
119 +
120 +void propagate_enable_wakeup(struct acpi_device *wakeup_dev)
121 +{
122 + struct list_head *node, *next;
123 +
124 + list_for_each_safe(node, next, &acpi_wakeup_device_list) {
125 + struct acpi_device *dev =
126 + container_of(node, struct acpi_device, wakeup_list);
127 +
128 + if (!dev->wakeup.flags.valid)
129 + continue;
130 +
131 + if (dev == wakeup_dev)
132 + continue;
133 +
134 + if (dev->wakeup.gpe_number != wakeup_dev->wakeup.gpe_number
135 + || dev->wakeup.gpe_device != wakeup_dev->wakeup.gpe_device)
136 + continue;
137 +
138 + printk(KERN_WARNING "ACPI: '%s' and '%s' share a GPE, "
139 + "unable to disable/enable one seperately\n",
140 + dev->pnp.bus_id, wakeup_dev->pnp.bus_id);
141 +
142 + dev->wakeup.state.enabled = wakeup_dev->wakeup.state.enabled;
143 + physical_device_enable_wakeup(dev);
144 + }
145 +}
146 +
147 static int __init acpi_wakeup_device_init(void)
148 {
149 struct list_head *node, *next;
150 Index: linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/proc.c
151 ===================================================================
152 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/acpi/sleep/proc.c
153 +++ linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/proc.c
154 @@ -377,14 +377,6 @@ acpi_system_wakeup_device_seq_show(struc
155 return 0;
156 }
157
158 -static void physical_device_enable_wakeup(struct acpi_device *adev)
159 -{
160 - struct device *dev = acpi_get_physical_device(adev->handle);
161 -
162 - if (dev && device_can_wakeup(dev))
163 - device_set_wakeup_enable(dev, adev->wakeup.state.enabled);
164 -}
165 -
166 static ssize_t
167 acpi_system_write_wakeup_device(struct file *file,
168 const char __user * buffer,
169 @@ -420,26 +412,7 @@ acpi_system_write_wakeup_device(struct f
170 }
171 if (found_dev) {
172 physical_device_enable_wakeup(found_dev);
173 - list_for_each_safe(node, next, &acpi_wakeup_device_list) {
174 - struct acpi_device *dev = container_of(node,
175 - struct
176 - acpi_device,
177 - wakeup_list);
178 -
179 - if ((dev != found_dev) &&
180 - (dev->wakeup.gpe_number ==
181 - found_dev->wakeup.gpe_number)
182 - && (dev->wakeup.gpe_device ==
183 - found_dev->wakeup.gpe_device)) {
184 - printk(KERN_WARNING
185 - "ACPI: '%s' and '%s' have the same GPE, "
186 - "can't disable/enable one seperately\n",
187 - dev->pnp.bus_id, found_dev->pnp.bus_id);
188 - dev->wakeup.state.enabled =
189 - found_dev->wakeup.state.enabled;
190 - physical_device_enable_wakeup(dev);
191 - }
192 - }
193 + propagate_enable_wakeup(found_dev);
194 }
195 spin_unlock(&acpi_device_lock);
196 return count;
197 Index: linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/sleep.h
198 ===================================================================
199 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/acpi/sleep/sleep.h
200 +++ linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/sleep.h
201 @@ -5,3 +5,9 @@ extern int acpi_suspend (u32 state);
202 extern void acpi_enable_wakeup_device_prep(u8 sleep_state);
203 extern void acpi_enable_wakeup_device(u8 sleep_state);
204 extern void acpi_disable_wakeup_device(u8 sleep_state);
205 +extern void physical_device_enable_wakeup(struct acpi_device *adev);
206 +extern void propagate_enable_wakeup(struct acpi_device *root_dev);
207 +
208 +extern spinlock_t acpi_device_lock;
209 +
210 +extern bool acpi_root_bridge_wakeup;
211 Index: linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/main.c
212 ===================================================================
213 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/acpi/sleep/main.c
214 +++ linux-2.6.27-SLE11_BRANCH/drivers/acpi/sleep/main.c
215 @@ -320,6 +320,12 @@ static int __init init_set_sci_en_on_res
216 return 0;
217 }
218
219 +static int __init init_allow_root_bridge_wakeup(const struct dmi_system_id *d)
220 +{
221 + acpi_root_bridge_wakeup = true;
222 + return 0;
223 +}
224 +
225 static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
226 {
227 .callback = init_old_suspend_ordering,
228 @@ -361,6 +367,14 @@ static struct dmi_system_id __initdata a
229 DMI_MATCH(DMI_PRODUCT_NAME, "HP xw4600 Workstation"),
230 },
231 },
232 + {
233 + .callback = init_allow_root_bridge_wakeup,
234 + .ident = "IBM 4852526",
235 + .matches = {
236 + DMI_MATCH(DMI_SYS_VENDOR, "IBM CORPORATION"),
237 + DMI_MATCH(DMI_PRODUCT_NAME, "4852526"),
238 + },
239 + },
240 {},
241 };
242 #endif /* CONFIG_SUSPEND */