summaryrefslogtreecommitdiffstats
path: root/sys/dev/usb/controller/usb_controller.c
diff options
context:
space:
mode:
authorhselasky <hselasky@FreeBSD.org>2011-12-14 00:28:54 +0000
committerhselasky <hselasky@FreeBSD.org>2011-12-14 00:28:54 +0000
commit7076389cec83009f9046b6c6e2695fe340a63802 (patch)
tree56520f5d0635d8db6259b372fff280b4e6288da5 /sys/dev/usb/controller/usb_controller.c
parent145914e3e7153161b6dd67fdcd58c7a8201845bc (diff)
downloadFreeBSD-src-7076389cec83009f9046b6c6e2695fe340a63802.zip
FreeBSD-src-7076389cec83009f9046b6c6e2695fe340a63802.tar.gz
Implement better support for USB controller suspend and resume.
This patch should remove the need for kldunload of USB controller drivers at suspend and kldload of USB controller drivers at resume. This patch also fixes some build issues in avr32dci.c MFC after: 2 weeks
Diffstat (limited to 'sys/dev/usb/controller/usb_controller.c')
-rw-r--r--sys/dev/usb/controller/usb_controller.c247
1 files changed, 231 insertions, 16 deletions
diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c
index 7ff0bc0..f31ab5b 100644
--- a/sys/dev/usb/controller/usb_controller.c
+++ b/sys/dev/usb/controller/usb_controller.c
@@ -61,12 +61,16 @@
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/usb_pf.h>
+#include "usb_if.h"
/* function prototypes */
static device_probe_t usb_probe;
static device_attach_t usb_attach;
static device_detach_t usb_detach;
+static device_suspend_t usb_suspend;
+static device_resume_t usb_resume;
+static device_shutdown_t usb_shutdown;
static void usb_attach_sub(device_t, struct usb_bus *);
@@ -91,9 +95,9 @@ static device_method_t usb_methods[] = {
DEVMETHOD(device_probe, usb_probe),
DEVMETHOD(device_attach, usb_attach),
DEVMETHOD(device_detach, usb_detach),
- DEVMETHOD(device_suspend, bus_generic_suspend),
- DEVMETHOD(device_resume, bus_generic_resume),
- DEVMETHOD(device_shutdown, bus_generic_shutdown),
+ DEVMETHOD(device_suspend, usb_suspend),
+ DEVMETHOD(device_resume, usb_resume),
+ DEVMETHOD(device_shutdown, usb_shutdown),
{0, 0}
};
@@ -182,12 +186,12 @@ usb_detach(device_t dev)
usb_root_mount_rel(bus);
USB_BUS_LOCK(bus);
- if (usb_proc_msignal(&bus->explore_proc,
- &bus->detach_msg[0], &bus->detach_msg[1])) {
- /* ignore */
- }
- /* Wait for detach to complete */
+ /* Queue detach job */
+ usb_proc_msignal(&bus->explore_proc,
+ &bus->detach_msg[0], &bus->detach_msg[1]);
+
+ /* Wait for detach to complete */
usb_proc_mwait(&bus->explore_proc,
&bus->detach_msg[0], &bus->detach_msg[1]);
@@ -213,6 +217,75 @@ usb_detach(device_t dev)
}
/*------------------------------------------------------------------------*
+ * usb_suspend
+ *------------------------------------------------------------------------*/
+static int
+usb_suspend(device_t dev)
+{
+ struct usb_bus *bus = device_get_softc(dev);
+
+ DPRINTF("\n");
+
+ if (bus == NULL) {
+ /* was never setup properly */
+ return (0);
+ }
+
+ USB_BUS_LOCK(bus);
+ usb_proc_msignal(&bus->explore_proc,
+ &bus->suspend_msg[0], &bus->suspend_msg[1]);
+ USB_BUS_UNLOCK(bus);
+
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_resume
+ *------------------------------------------------------------------------*/
+static int
+usb_resume(device_t dev)
+{
+ struct usb_bus *bus = device_get_softc(dev);
+
+ DPRINTF("\n");
+
+ if (bus == NULL) {
+ /* was never setup properly */
+ return (0);
+ }
+
+ USB_BUS_LOCK(bus);
+ usb_proc_msignal(&bus->explore_proc,
+ &bus->resume_msg[0], &bus->resume_msg[1]);
+ USB_BUS_UNLOCK(bus);
+
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_shutdown
+ *------------------------------------------------------------------------*/
+static int
+usb_shutdown(device_t dev)
+{
+ struct usb_bus *bus = device_get_softc(dev);
+
+ DPRINTF("\n");
+
+ if (bus == NULL) {
+ /* was never setup properly */
+ return (0);
+ }
+
+ USB_BUS_LOCK(bus);
+ usb_proc_msignal(&bus->explore_proc,
+ &bus->shutdown_msg[0], &bus->shutdown_msg[1]);
+ USB_BUS_UNLOCK(bus);
+
+ return (0);
+}
+
+/*------------------------------------------------------------------------*
* usb_bus_explore
*
* This function is used to explore the device tree from the root.
@@ -226,6 +299,9 @@ usb_bus_explore(struct usb_proc_msg *pm)
bus = ((struct usb_bus_msg *)pm)->bus;
udev = bus->devices[USB_ROOT_HUB_ADDR];
+ if (bus->no_explore != 0)
+ return;
+
if (udev && udev->hub) {
if (bus->do_probe) {
@@ -296,6 +372,133 @@ usb_bus_detach(struct usb_proc_msg *pm)
bus->bdev = NULL;
}
+/*------------------------------------------------------------------------*
+ * usb_bus_suspend
+ *
+ * This function is used to suspend the USB contoller.
+ *------------------------------------------------------------------------*/
+static void
+usb_bus_suspend(struct usb_proc_msg *pm)
+{
+ struct usb_bus *bus;
+ struct usb_device *udev;
+ usb_error_t err;
+
+ bus = ((struct usb_bus_msg *)pm)->bus;
+ udev = bus->devices[USB_ROOT_HUB_ADDR];
+
+ if (udev == NULL || bus->bdev == NULL)
+ return;
+
+ bus_generic_shutdown(bus->bdev);
+
+ usbd_enum_lock(udev);
+
+ err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX);
+ if (err)
+ device_printf(bus->bdev, "Could not unconfigure root HUB\n");
+
+ USB_BUS_LOCK(bus);
+ bus->hw_power_state = 0;
+ bus->no_explore = 1;
+ USB_BUS_UNLOCK(bus);
+
+ if (bus->methods->set_hw_power != NULL)
+ (bus->methods->set_hw_power) (bus);
+
+ if (bus->methods->set_hw_power_sleep != NULL)
+ (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SUSPEND);
+
+ usbd_enum_unlock(udev);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_bus_resume
+ *
+ * This function is used to resume the USB contoller.
+ *------------------------------------------------------------------------*/
+static void
+usb_bus_resume(struct usb_proc_msg *pm)
+{
+ struct usb_bus *bus;
+ struct usb_device *udev;
+ usb_error_t err;
+
+ bus = ((struct usb_bus_msg *)pm)->bus;
+ udev = bus->devices[USB_ROOT_HUB_ADDR];
+
+ if (udev == NULL || bus->bdev == NULL)
+ return;
+
+ usbd_enum_lock(udev);
+#if 0
+ DEVMETHOD(usb_take_controller, NULL); /* dummy */
+#endif
+ USB_TAKE_CONTROLLER(device_get_parent(bus->bdev));
+
+ USB_BUS_LOCK(bus);
+ bus->hw_power_state =
+ USB_HW_POWER_CONTROL |
+ USB_HW_POWER_BULK |
+ USB_HW_POWER_INTERRUPT |
+ USB_HW_POWER_ISOC |
+ USB_HW_POWER_NON_ROOT_HUB;
+ bus->no_explore = 0;
+ USB_BUS_UNLOCK(bus);
+
+ if (bus->methods->set_hw_power_sleep != NULL)
+ (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_RESUME);
+
+ if (bus->methods->set_hw_power != NULL)
+ (bus->methods->set_hw_power) (bus);
+
+ err = usbd_set_config_index(udev, 0);
+ if (err)
+ device_printf(bus->bdev, "Could not configure root HUB\n");
+
+ usbd_enum_unlock(udev);
+}
+
+/*------------------------------------------------------------------------*
+ * usb_bus_shutdown
+ *
+ * This function is used to shutdown the USB contoller.
+ *------------------------------------------------------------------------*/
+static void
+usb_bus_shutdown(struct usb_proc_msg *pm)
+{
+ struct usb_bus *bus;
+ struct usb_device *udev;
+ usb_error_t err;
+
+ bus = ((struct usb_bus_msg *)pm)->bus;
+ udev = bus->devices[USB_ROOT_HUB_ADDR];
+
+ if (udev == NULL || bus->bdev == NULL)
+ return;
+
+ bus_generic_shutdown(bus->bdev);
+
+ usbd_enum_lock(udev);
+
+ err = usbd_set_config_index(udev, USB_UNCONFIG_INDEX);
+ if (err)
+ device_printf(bus->bdev, "Could not unconfigure root HUB\n");
+
+ USB_BUS_LOCK(bus);
+ bus->hw_power_state = 0;
+ bus->no_explore = 1;
+ USB_BUS_UNLOCK(bus);
+
+ if (bus->methods->set_hw_power != NULL)
+ (bus->methods->set_hw_power) (bus);
+
+ if (bus->methods->set_hw_power_sleep != NULL)
+ (bus->methods->set_hw_power_sleep) (bus, USB_HW_POWER_SHUTDOWN);
+
+ usbd_enum_unlock(udev);
+}
+
static void
usb_power_wdog(void *arg)
{
@@ -374,8 +577,6 @@ usb_bus_attach(struct usb_proc_msg *pm)
return;
}
- USB_BUS_UNLOCK(bus);
-
/* default power_mask value */
bus->hw_power_state =
USB_HW_POWER_CONTROL |
@@ -384,13 +585,15 @@ usb_bus_attach(struct usb_proc_msg *pm)
USB_HW_POWER_ISOC |
USB_HW_POWER_NON_ROOT_HUB;
+ USB_BUS_UNLOCK(bus);
+
/* make sure power is set at least once */
if (bus->methods->set_hw_power != NULL) {
(bus->methods->set_hw_power) (bus);
}
- /* Allocate the Root USB device */
+ /* allocate the Root USB device */
child = usb_alloc_device(bus->bdev, bus, NULL, 0, 0, 1,
speed, USB_MODE_HOST);
@@ -456,6 +659,21 @@ usb_attach_sub(device_t dev, struct usb_bus *bus)
bus->attach_msg[1].hdr.pm_callback = &usb_bus_attach;
bus->attach_msg[1].bus = bus;
+ bus->suspend_msg[0].hdr.pm_callback = &usb_bus_suspend;
+ bus->suspend_msg[0].bus = bus;
+ bus->suspend_msg[1].hdr.pm_callback = &usb_bus_suspend;
+ bus->suspend_msg[1].bus = bus;
+
+ bus->resume_msg[0].hdr.pm_callback = &usb_bus_resume;
+ bus->resume_msg[0].bus = bus;
+ bus->resume_msg[1].hdr.pm_callback = &usb_bus_resume;
+ bus->resume_msg[1].bus = bus;
+
+ bus->shutdown_msg[0].hdr.pm_callback = &usb_bus_shutdown;
+ bus->shutdown_msg[0].bus = bus;
+ bus->shutdown_msg[1].hdr.pm_callback = &usb_bus_shutdown;
+ bus->shutdown_msg[1].bus = bus;
+
/* Create USB explore and callback processes */
if (usb_proc_create(&bus->giant_callback_proc,
@@ -477,10 +695,8 @@ usb_attach_sub(device_t dev, struct usb_bus *bus)
} else {
/* Get final attach going */
USB_BUS_LOCK(bus);
- if (usb_proc_msignal(&bus->explore_proc,
- &bus->attach_msg[0], &bus->attach_msg[1])) {
- /* ignore */
- }
+ usb_proc_msignal(&bus->explore_proc,
+ &bus->attach_msg[0], &bus->attach_msg[1]);
USB_BUS_UNLOCK(bus);
/* Do initial explore */
@@ -602,4 +818,3 @@ usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb)
mtx_destroy(&bus->bus_mtx);
}
-
OpenPOWER on IntegriCloud