aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLan Tianyu <tianyu.lan@intel.com>2012-09-05 01:44:36 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-09-10 16:04:01 -0400
commitf7ac7787ad361e31a7972e2854ed8dc2eedfac3b (patch)
tree370b620b012a42f0890c829793dcc6f705c39cad
parent693d8eb853f62a1341cf59df151b12c053f34e8a (diff)
usb/acpi: Use ACPI methods to power off ports.
Upcoming Intel systems will have an ACPI method to control whether a USB port can be completely powered off. The implication of powering off a USB port is that the device and host sees a physical disconnect, and subsequent port connections and remote wakeups will be lost. Add a new function, usb_acpi_power_manageable(), that can be used to find whether the usb port has ACPI power resources that can be used to power on and off the port on these machines. Also add a new function called usb_acpi_set_power_state() that controls the port power via these ACPI methods. When the USB core calls into the xHCI hub driver to power off a port, check whether the port can be completely powered off via this new ACPI mechanism. If so, call into these new ACPI methods. Also use the ACPI methods when the USB core asks to power on a port. Signed-off-by: Lan Tianyu <tianyu.lan@intel.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/usb-acpi.c62
-rw-r--r--drivers/usb/host/xhci-hub.c12
-rw-r--r--include/linux/usb.h10
3 files changed, 84 insertions, 0 deletions
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 404d86afb243..0ef7d42d8abe 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -19,6 +19,68 @@
19 19
20#include "usb.h" 20#include "usb.h"
21 21
22/**
23 * usb_acpi_power_manageable - check whether usb port has
24 * acpi power resource.
25 * @hdev: USB device belonging to the usb hub
26 * @index: port index based zero
27 *
28 * Return true if the port has acpi power resource and false if no.
29 */
30bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
31{
32 acpi_handle port_handle;
33 int port1 = index + 1;
34
35 port_handle = usb_get_hub_port_acpi_handle(hdev,
36 port1);
37 if (port_handle)
38 return acpi_bus_power_manageable(port_handle);
39 else
40 return false;
41}
42EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
43
44/**
45 * usb_acpi_set_power_state - control usb port's power via acpi power
46 * resource
47 * @hdev: USB device belonging to the usb hub
48 * @index: port index based zero
49 * @enable: power state expected to be set
50 *
51 * Notice to use usb_acpi_power_manageable() to check whether the usb port
52 * has acpi power resource before invoking this function.
53 *
54 * Returns 0 on success, else negative errno.
55 */
56int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
57{
58 acpi_handle port_handle;
59 unsigned char state;
60 int port1 = index + 1;
61 int error = -EINVAL;
62
63 port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev,
64 port1);
65 if (!port_handle)
66 return error;
67
68 if (enable)
69 state = ACPI_STATE_D0;
70 else
71 state = ACPI_STATE_D3_COLD;
72
73 error = acpi_bus_set_power(port_handle, state);
74 if (!error)
75 dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n",
76 port1, enable);
77 else
78 dev_dbg(&hdev->dev, "The power of hub port failed to be set\n");
79
80 return error;
81}
82EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
83
22static int usb_acpi_check_port_connect_type(struct usb_device *hdev, 84static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
23 acpi_handle handle, int port1) 85 acpi_handle handle, int port1)
24{ 86{
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 03032b3e6ed2..630e9e6e06b5 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -766,6 +766,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
766 766
767 temp = xhci_readl(xhci, port_array[wIndex]); 767 temp = xhci_readl(xhci, port_array[wIndex]);
768 xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp); 768 xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
769
770 temp = usb_acpi_power_manageable(hcd->self.root_hub,
771 wIndex);
772 if (temp)
773 usb_acpi_set_power_state(hcd->self.root_hub,
774 wIndex, true);
769 break; 775 break;
770 case USB_PORT_FEAT_RESET: 776 case USB_PORT_FEAT_RESET:
771 temp = (temp | PORT_RESET); 777 temp = (temp | PORT_RESET);
@@ -868,6 +874,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
868 case USB_PORT_FEAT_POWER: 874 case USB_PORT_FEAT_POWER:
869 xhci_writel(xhci, temp & ~PORT_POWER, 875 xhci_writel(xhci, temp & ~PORT_POWER,
870 port_array[wIndex]); 876 port_array[wIndex]);
877
878 temp = usb_acpi_power_manageable(hcd->self.root_hub,
879 wIndex);
880 if (temp)
881 usb_acpi_set_power_state(hcd->self.root_hub,
882 wIndex, false);
871 break; 883 break;
872 default: 884 default:
873 goto error; 885 goto error;
diff --git a/include/linux/usb.h b/include/linux/usb.h
index e0084a1fa37f..07915a32fb9d 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -602,6 +602,16 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,
602extern int usb_reset_device(struct usb_device *dev); 602extern int usb_reset_device(struct usb_device *dev);
603extern void usb_queue_reset_device(struct usb_interface *dev); 603extern void usb_queue_reset_device(struct usb_interface *dev);
604 604
605#ifdef CONFIG_ACPI
606extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
607 bool enable);
608extern bool usb_acpi_power_manageable(struct usb_device *hdev, int index);
609#else
610static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index,
611 bool enable) { return 0; }
612static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
613 { return true; }
614#endif
605 615
606/* USB autosuspend and autoresume */ 616/* USB autosuspend and autoresume */
607#ifdef CONFIG_USB_SUSPEND 617#ifdef CONFIG_USB_SUSPEND