Blame view

drivers/pci/pci-acpi.c 10.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  /*
   * File:	pci-acpi.c
a406d9e63   Len Brown   [ACPI] gut acpi_p...
3
   * Purpose:	Provide PCI support in ACPI
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
4
   *
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
5
6
7
   * Copyright (C) 2005 David Shaohua Li <shaohua.li@intel.com>
   * Copyright (C) 2004 Tom Long Nguyen <tom.l.nguyen@intel.com>
   * Copyright (C) 2004 Intel Corp.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
8
9
10
11
12
13
   */
  
  #include <linux/delay.h>
  #include <linux/init.h>
  #include <linux/pci.h>
  #include <linux/module.h>
5fde244d3   Shaohua Li   PCI: disable ASPM...
14
  #include <linux/pci-aspm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <acpi/acpi.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
17
18
  #include <acpi/acpi_bus.h>
  
  #include <linux/pci-acpi.h>
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
19
  #include <linux/pm_runtime.h>
0f64474b8   David Shaohua Li   [ACPI] PCI can no...
20
  #include "pci.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21

b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  static DEFINE_MUTEX(pci_acpi_pm_notify_mtx);
  
  /**
   * pci_acpi_wake_bus - Wake-up notification handler for root buses.
   * @handle: ACPI handle of a device the notification is for.
   * @event: Type of the signaled event.
   * @context: PCI root bus to wake up devices on.
   */
  static void pci_acpi_wake_bus(acpi_handle handle, u32 event, void *context)
  {
  	struct pci_bus *pci_bus = context;
  
  	if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_bus)
  		pci_pme_wakeup_bus(pci_bus);
  }
  
  /**
   * pci_acpi_wake_dev - Wake-up notification handler for PCI devices.
   * @handle: ACPI handle of a device the notification is for.
   * @event: Type of the signaled event.
   * @context: PCI device object to wake up.
   */
  static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
  {
  	struct pci_dev *pci_dev = context;
a424948dd   Rafael J. Wysocki   PCI/ACPI/PM: Avoi...
47
48
49
50
51
  	if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
  		return;
  
  	if (!pci_dev->pm_cap || !pci_dev->pme_support
  	     || pci_check_pme_status(pci_dev)) {
379021d5c   Rafael J. Wysocki   PCI / PM: Extend ...
52
53
  		if (pci_dev->pme_poll)
  			pci_dev->pme_poll = false;
0f953bf6b   Rafael J. Wysocki   PCI/PM: Report wa...
54
  		pci_wakeup_event(pci_dev);
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
55
  		pm_runtime_resume(&pci_dev->dev);
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
56
  	}
a424948dd   Rafael J. Wysocki   PCI/ACPI/PM: Avoi...
57
58
59
  
  	if (pci_dev->subordinate)
  		pci_pme_wakeup_bus(pci_dev->subordinate);
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
  }
  
  /**
   * add_pm_notifier - Register PM notifier for given ACPI device.
   * @dev: ACPI device to add the notifier for.
   * @context: PCI device or bus to check for PME status if an event is signaled.
   *
   * NOTE: @dev need not be a run-wake or wake-up device to be a valid source of
   * PM wake-up events.  For example, wake-up events may be generated for bridges
   * if one of the devices below the bridge is signaling PME, even if the bridge
   * itself doesn't have a wake-up GPE associated with it.
   */
  static acpi_status add_pm_notifier(struct acpi_device *dev,
  				   acpi_notify_handler handler,
  				   void *context)
  {
  	acpi_status status = AE_ALREADY_EXISTS;
  
  	mutex_lock(&pci_acpi_pm_notify_mtx);
  
  	if (dev->wakeup.flags.notifier_present)
  		goto out;
  
  	status = acpi_install_notify_handler(dev->handle,
  					     ACPI_SYSTEM_NOTIFY,
  					     handler, context);
  	if (ACPI_FAILURE(status))
  		goto out;
  
  	dev->wakeup.flags.notifier_present = true;
  
   out:
  	mutex_unlock(&pci_acpi_pm_notify_mtx);
  	return status;
  }
  
  /**
   * remove_pm_notifier - Unregister PM notifier from given ACPI device.
   * @dev: ACPI device to remove the notifier from.
   */
  static acpi_status remove_pm_notifier(struct acpi_device *dev,
  				      acpi_notify_handler handler)
  {
  	acpi_status status = AE_BAD_PARAMETER;
  
  	mutex_lock(&pci_acpi_pm_notify_mtx);
  
  	if (!dev->wakeup.flags.notifier_present)
  		goto out;
  
  	status = acpi_remove_notify_handler(dev->handle,
  					    ACPI_SYSTEM_NOTIFY,
  					    handler);
  	if (ACPI_FAILURE(status))
  		goto out;
  
  	dev->wakeup.flags.notifier_present = false;
  
   out:
  	mutex_unlock(&pci_acpi_pm_notify_mtx);
  	return status;
  }
  
  /**
   * pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus.
   * @dev: ACPI device to add the notifier for.
   * @pci_bus: PCI bus to walk checking for PME status if an event is signaled.
   */
  acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev,
  					 struct pci_bus *pci_bus)
  {
  	return add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus);
  }
  
  /**
   * pci_acpi_remove_bus_pm_notifier - Unregister PCI bus PM notifier.
   * @dev: ACPI device to remove the notifier from.
   */
  acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev)
  {
  	return remove_pm_notifier(dev, pci_acpi_wake_bus);
  }
  
  /**
   * pci_acpi_add_pm_notifier - Register PM notifier for given PCI device.
   * @dev: ACPI device to add the notifier for.
   * @pci_dev: PCI device to check for the PME status if an event is signaled.
   */
  acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
  				     struct pci_dev *pci_dev)
  {
  	return add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev);
  }
  
  /**
   * pci_acpi_remove_pm_notifier - Unregister PCI device PM notifier.
   * @dev: ACPI device to remove the notifier from.
   */
  acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
  {
  	return remove_pm_notifier(dev, pci_acpi_wake_dev);
  }
0f64474b8   David Shaohua Li   [ACPI] PCI can no...
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
  /*
   * _SxD returns the D-state with the highest power
   * (lowest D-state number) supported in the S-state "x".
   *
   * If the devices does not have a _PRW
   * (Power Resources for Wake) supporting system wakeup from "x"
   * then the OS is free to choose a lower power (higher number
   * D-state) than the return value from _SxD.
   *
   * But if _PRW is enabled at S-state "x", the OS
   * must not choose a power lower than _SxD --
   * unless the device has an _SxW method specifying
   * the lowest power (highest D-state number) the device
   * may enter while still able to wake the system.
   *
   * ie. depending on global OS policy:
   *
   * if (_PRW at S-state x)
   *	choose from highest power _SxD to lowest power _SxW
   * else // no _PRW at S-state x
   * 	choose highest power _SxD or any lower power
0f64474b8   David Shaohua Li   [ACPI] PCI can no...
183
   */
8d2bdf494   Rafael J. Wysocki   PCI ACPI: Drop th...
184
  static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
0f64474b8   David Shaohua Li   [ACPI] PCI can no...
185
  {
ab826ca4c   Shaohua Li   ACPI: Use ACPI me...
186
  	int acpi_state;
0f64474b8   David Shaohua Li   [ACPI] PCI can no...
187

06166780e   David Brownell   ACPI PM: acpi_pm_...
188
  	acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL);
ab826ca4c   Shaohua Li   ACPI: Use ACPI me...
189
190
191
192
193
194
195
196
197
198
199
200
  	if (acpi_state < 0)
  		return PCI_POWER_ERROR;
  
  	switch (acpi_state) {
  	case ACPI_STATE_D0:
  		return PCI_D0;
  	case ACPI_STATE_D1:
  		return PCI_D1;
  	case ACPI_STATE_D2:
  		return PCI_D2;
  	case ACPI_STATE_D3:
  		return PCI_D3hot;
28c2103da   Lin Ming   ACPI: Add D3 cold...
201
202
  	case ACPI_STATE_D3_COLD:
  		return PCI_D3cold;
ab826ca4c   Shaohua Li   ACPI: Use ACPI me...
203
204
  	}
  	return PCI_POWER_ERROR;
0f64474b8   David Shaohua Li   [ACPI] PCI can no...
205
  }
961d9120f   Rafael J. Wysocki   PCI: Introduce pl...
206
207
208
209
210
211
212
  
  static bool acpi_pci_power_manageable(struct pci_dev *dev)
  {
  	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
  
  	return handle ? acpi_bus_power_manageable(handle) : false;
  }
0f64474b8   David Shaohua Li   [ACPI] PCI can no...
213

b913100d7   David Shaohua Li   [ACPI] pci_set_po...
214
215
216
  static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)
  {
  	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
10b3dcae0   Shaohua Li   ACPI: ignore _PSx...
217
  	acpi_handle tmp;
583c377f1   David Brownell   ACPI: acpi_pci_se...
218
219
220
221
222
223
  	static const u8 state_conv[] = {
  		[PCI_D0] = ACPI_STATE_D0,
  		[PCI_D1] = ACPI_STATE_D1,
  		[PCI_D2] = ACPI_STATE_D2,
  		[PCI_D3hot] = ACPI_STATE_D3,
  		[PCI_D3cold] = ACPI_STATE_D3
b913100d7   David Shaohua Li   [ACPI] pci_set_po...
224
  	};
44e4e66ee   Rafael J. Wysocki   PCI: rework pci_s...
225
  	int error = -EINVAL;
b913100d7   David Shaohua Li   [ACPI] pci_set_po...
226

10b3dcae0   Shaohua Li   ACPI: ignore _PSx...
227
  	/* If the ACPI device has _EJ0, ignore the device */
44e4e66ee   Rafael J. Wysocki   PCI: rework pci_s...
228
229
  	if (!handle || ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp)))
  		return -ENODEV;
583c377f1   David Brownell   ACPI: acpi_pci_se...
230
231
232
233
234
235
236
  
  	switch (state) {
  	case PCI_D0:
  	case PCI_D1:
  	case PCI_D2:
  	case PCI_D3hot:
  	case PCI_D3cold:
44e4e66ee   Rafael J. Wysocki   PCI: rework pci_s...
237
  		error = acpi_bus_set_power(handle, state_conv[state]);
583c377f1   David Brownell   ACPI: acpi_pci_se...
238
  	}
44e4e66ee   Rafael J. Wysocki   PCI: rework pci_s...
239
240
241
242
243
244
245
  
  	if (!error)
  		dev_printk(KERN_INFO, &dev->dev,
  				"power state changed by ACPI to D%d
  ", state);
  
  	return error;
b913100d7   David Shaohua Li   [ACPI] pci_set_po...
246
  }
eb9d0fe40   Rafael J. Wysocki   PCI ACPI: Rework ...
247
248
249
250
251
252
  static bool acpi_pci_can_wakeup(struct pci_dev *dev)
  {
  	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
  
  	return handle ? acpi_bus_can_wakeup(handle) : false;
  }
0baed8da1   Rafael J. Wysocki   PCI / ACPI PM: Pr...
253
254
255
  static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable)
  {
  	while (bus->parent) {
dc1a94ae1   Rafael J. Wysocki   PCI/PM: Propagate...
256
  		if (!acpi_pm_device_sleep_wake(&bus->self->dev, enable))
0baed8da1   Rafael J. Wysocki   PCI / ACPI PM: Pr...
257
258
259
260
261
262
263
264
  			return;
  		bus = bus->parent;
  	}
  
  	/* We have reached the root bus. */
  	if (bus->bridge)
  		acpi_pm_device_sleep_wake(bus->bridge, enable);
  }
eb9d0fe40   Rafael J. Wysocki   PCI ACPI: Rework ...
265
266
  static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
  {
0baed8da1   Rafael J. Wysocki   PCI / ACPI PM: Pr...
267
268
  	if (acpi_pci_can_wakeup(dev))
  		return acpi_pm_device_sleep_wake(&dev->dev, enable);
dc1a94ae1   Rafael J. Wysocki   PCI/PM: Propagate...
269
  	acpi_pci_propagate_wakeup_enable(dev->bus, enable);
0baed8da1   Rafael J. Wysocki   PCI / ACPI PM: Pr...
270
  	return 0;
eb9d0fe40   Rafael J. Wysocki   PCI ACPI: Rework ...
271
  }
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
272
273
274
275
276
277
278
279
280
281
282
283
  /**
   * acpi_dev_run_wake - Enable/disable wake-up for given device.
   * @phys_dev: Device to enable/disable the platform to wake-up the system for.
   * @enable: Whether enable or disable the wake-up functionality.
   *
   * Find the ACPI device object corresponding to @pci_dev and try to
   * enable/disable the GPE associated with it.
   */
  static int acpi_dev_run_wake(struct device *phys_dev, bool enable)
  {
  	struct acpi_device *dev;
  	acpi_handle handle;
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
284
285
286
287
288
289
290
291
292
293
294
295
296
  
  	if (!device_run_wake(phys_dev))
  		return -EINVAL;
  
  	handle = DEVICE_ACPI_HANDLE(phys_dev);
  	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) {
  		dev_dbg(phys_dev, "ACPI handle has no context in %s!
  ",
  			__func__);
  		return -ENODEV;
  	}
  
  	if (enable) {
519072676   Rafael J. Wysocki   ACPI: Remove the ...
297
298
  		acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0);
  		acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
299
  	} else {
519072676   Rafael J. Wysocki   ACPI: Remove the ...
300
301
  		acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number);
  		acpi_disable_wakeup_device_power(dev);
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
302
  	}
78d090b0b   Rafael J. Wysocki   PCI / PM: Remove ...
303
  	return 0;
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  }
  
  static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
  {
  	while (bus->parent) {
  		struct pci_dev *bridge = bus->self;
  
  		if (bridge->pme_interrupt)
  			return;
  		if (!acpi_dev_run_wake(&bridge->dev, enable))
  			return;
  		bus = bus->parent;
  	}
  
  	/* We have reached the root bus. */
  	if (bus->bridge)
  		acpi_dev_run_wake(bus->bridge, enable);
  }
  
  static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
  {
  	if (dev->pme_interrupt)
  		return 0;
  
  	if (!acpi_dev_run_wake(&dev->dev, enable))
  		return 0;
  
  	acpi_pci_propagate_run_wake(dev->bus, enable);
  	return 0;
  }
961d9120f   Rafael J. Wysocki   PCI: Introduce pl...
334
335
336
337
  static struct pci_platform_pm_ops acpi_pci_platform_pm = {
  	.is_manageable = acpi_pci_power_manageable,
  	.set_state = acpi_pci_set_power_state,
  	.choose_state = acpi_pci_choose_state,
eb9d0fe40   Rafael J. Wysocki   PCI ACPI: Rework ...
338
339
  	.can_wakeup = acpi_pci_can_wakeup,
  	.sleep_wake = acpi_pci_sleep_wake,
b67ea7617   Rafael J. Wysocki   PCI / ACPI / PM: ...
340
  	.run_wake = acpi_pci_run_wake,
961d9120f   Rafael J. Wysocki   PCI: Introduce pl...
341
  };
b913100d7   David Shaohua Li   [ACPI] pci_set_po...
342

84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
343
  /* ACPI bus type */
9c273b958   Muthu Kumar   [PATCH] PCI ACPI:...
344
  static int acpi_pci_find_device(struct device *dev, acpi_handle *handle)
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
345
346
  {
  	struct pci_dev * pci_dev;
439913fff   Lin Ming   ACPI: replace acp...
347
  	u64	addr;
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
348
349
350
351
352
353
354
355
356
  
  	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;
  }
9c273b958   Muthu Kumar   [PATCH] PCI ACPI:...
357
  static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle)
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
358
359
360
361
362
363
364
365
  {
  	int num;
  	unsigned int seg, bus;
  
  	/*
  	 * The string should be the same as root bridge's name
  	 * Please look at 'pci_scan_bus_parented'
  	 */
1a9271331   Kay Sievers   PCI: struct devic...
366
  	num = sscanf(dev_name(dev), "pci%04x:%02x", &seg, &bus);
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
367
368
369
370
371
372
373
  	if (num != 2)
  		return -ENODEV;
  	*handle = acpi_get_pci_rootbridge_handle(seg, bus);
  	if (!*handle)
  		return -ENODEV;
  	return 0;
  }
9c273b958   Muthu Kumar   [PATCH] PCI ACPI:...
374
  static struct acpi_bus_type acpi_pci_bus = {
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
375
  	.bus = &pci_bus_type,
9c273b958   Muthu Kumar   [PATCH] PCI ACPI:...
376
377
  	.find_device = acpi_pci_find_device,
  	.find_bridge = acpi_pci_find_root_bridge,
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
378
  };
9c273b958   Muthu Kumar   [PATCH] PCI ACPI:...
379
  static int __init acpi_pci_init(void)
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
380
381
  {
  	int ret;
993958fec   Bob Moore   ACPICA: Update FA...
382
  	if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_MSI) {
f8993aff8   Shaohua Li   ACPI: Disable MSI...
383
384
385
386
  		printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it
  ");
  		pci_no_msi();
  	}
5fde244d3   Shaohua Li   PCI: disable ASPM...
387

993958fec   Bob Moore   ACPICA: Update FA...
388
  	if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) {
5fde244d3   Shaohua Li   PCI: disable ASPM...
389
390
391
392
  		printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it
  ");
  		pcie_no_aspm();
  	}
9c273b958   Muthu Kumar   [PATCH] PCI ACPI:...
393
  	ret = register_acpi_bus_type(&acpi_pci_bus);
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
394
395
  	if (ret)
  		return 0;
961d9120f   Rafael J. Wysocki   PCI: Introduce pl...
396
  	pci_set_platform_pm(&acpi_pci_platform_pm);
84df749f3   David Shaohua Li   [ACPI] Bind ACPI ...
397
398
  	return 0;
  }
9c273b958   Muthu Kumar   [PATCH] PCI ACPI:...
399
  arch_initcall(acpi_pci_init);