PERFORCE change 155086 for review
Hans Petter Selasky
hselasky at FreeBSD.org
Sun Dec 21 07:36:26 PST 2008
http://perforce.freebsd.org/chv.cgi?CH=155086
Change 155086 by hselasky at hselasky_laptop001 on 2008/12/21 15:35:46
Implement and add support for USB powersave.
Affected files ...
.. //depot/projects/usb/src/sys/dev/usb2/controller/at91dci.c#16 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/ehci2.c#20 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/musb2_otg.c#17 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/ohci2.c#17 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/uhci2.c#15 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/uhci2.h#6 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/usb2_bus.h#6 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/usb2_controller.c#16 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/usb2_controller.h#6 edit
.. //depot/projects/usb/src/sys/dev/usb2/controller/uss820dci.c#14 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_device.c#38 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_device.h#10 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_generic.c#35 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_handle_request.c#11 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_hub.c#22 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_hub.h#6 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_request.c#26 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_request.h#7 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_transfer.c#32 edit
.. //depot/projects/usb/src/sys/dev/usb2/core/usb2_transfer.h#10 edit
.. //depot/projects/usb/src/sys/dev/usb2/include/usb2_standard.h#10 edit
Differences ...
==== //depot/projects/usb/src/sys/dev/usb2/controller/at91dci.c#16 (text+ko) ====
@@ -266,45 +266,28 @@
}
static void
-at91dci_wakeup_peer(struct at91dci_softc *sc)
+at91dci_wakeup_peer(struct usb2_xfer *xfer)
{
- uint32_t temp;
+ struct at91dci_softc *sc = xfer->usb2_sc;
+ uint8_t use_polling;
if (!(sc->sc_flags.status_suspend)) {
return;
}
- temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE);
+ use_polling = mtx_owned(xfer->xfer_mtx) ? 1 : 0;
- if (!(temp & AT91_UDP_GSTATE_ESR)) {
- return;
- }
- AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp);
+ AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, AT91_UDP_GSTATE_ESR);
- return;
-}
-
-static void
-at91dci_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on)
-{
- struct at91dci_softc *sc;
- uint32_t temp;
-
- DPRINTFN(5, "is_on=%u\n", is_on);
-
- USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
-
- sc = AT9100_DCI_BUS2SC(udev->bus);
-
- temp = AT91_UDP_READ_4(sc, AT91_UDP_GSTATE);
-
- if (is_on) {
- temp |= AT91_UDP_GSTATE_ESR;
+ /* wait 8 milliseconds */
+ if (use_polling) {
+ /* polling */
+ DELAY(8000);
} else {
- temp &= ~AT91_UDP_GSTATE_ESR;
+ /* Wait for reset to complete. */
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 8);
}
- AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, temp);
-
+ AT91_UDP_WRITE_4(sc, AT91_UDP_GSTATE, 0);
return;
}
@@ -2166,7 +2149,7 @@
switch (value) {
case UHF_PORT_SUSPEND:
- at91dci_wakeup_peer(sc);
+ at91dci_wakeup_peer(xfer);
break;
case UHF_PORT_ENABLE:
@@ -2543,5 +2526,4 @@
.set_stall = &at91dci_set_stall,
.clear_stall = &at91dci_clear_stall,
.vbus_interrupt = &at91dci_vbus_interrupt,
- .rem_wakeup_set = &at91dci_rem_wakeup_set,
};
==== //depot/projects/usb/src/sys/dev/usb2/controller/ehci2.c#20 (text+ko) ====
@@ -1038,6 +1038,11 @@
{
DPRINTFN(11, "%p to %p\n", sqh, last);
+ if (sqh->prev != NULL) {
+ /* should not happen */
+ DPRINTFN(0, "QH already linked!\n");
+ return (last);
+ }
/* (sc->sc_bus.mtx) must be locked */
sqh->next = last->next;
@@ -1125,14 +1130,6 @@
sqh->next->prev = sqh->prev;
usb2_pc_cpu_flush(sqh->next->page_cache);
}
- /*
- * set the Terminate-bit in the e_next of the QH, in case
- * the transferred packet was short so that the QH still
- * points at the last used TD
- */
-
- sqh->qh_qtd.qtd_next = htole32(EHCI_LINK_TERMINATE);
-
last = ((last == sqh) ? sqh->prev : last);
sqh->prev = 0;
@@ -1955,7 +1952,9 @@
usb2_pc_cpu_flush(qh->page_cache);
- EHCI_APPEND_QH(qh, *qh_last);
+ if (xfer->udev->pwr_save.suspended == 0) {
+ EHCI_APPEND_QH(qh, *qh_last);
+ }
return;
}
@@ -3233,7 +3232,32 @@
EOWRITE4(sc, port, v & ~EHCI_PS_PE);
break;
case UHF_PORT_SUSPEND:
- EOWRITE4(sc, port, v & ~EHCI_PS_SUSP);
+ if ((v & EHCI_PS_SUSP) && (!(v & EHCI_PS_FPR))) {
+
+ /*
+ * waking up a High Speed device is rather
+ * complicated if
+ */
+ EOWRITE4(sc, port, v | EHCI_PS_FPR);
+
+ /* wait 20ms for resume sequence to complete */
+ if (use_polling) {
+ /* polling */
+ DELAY(20 * 1000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 20);
+ }
+ }
+ EOWRITE4(sc, port, v & ~(EHCI_PS_SUSP |
+ EHCI_PS_FPR | (3 << 10) /* High Speed */ ));
+
+ /* settle time */
+ if (use_polling) {
+ /* polling */
+ DELAY(4 * 1000);
+ } else {
+ usb2_pause_mtx(&sc->sc_bus.bus_mtx, 4);
+ }
break;
case UHF_PORT_POWER:
EOWRITE4(sc, port, v & ~EHCI_PS_PP);
@@ -3280,7 +3304,8 @@
(EHCI_HCS_PPC(v) ? UHD_PWR_INDIVIDUAL : UHD_PWR_NO_SWITCH) |
(EHCI_HCS_P_INDICATOR(EREAD4(sc, EHCI_HCSPARAMS)) ?
UHD_PORT_IND : 0));
- sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200; /* XXX can't find out? */
+ /* XXX can't find out? */
+ sc->sc_hub_desc.hubd.bPwrOn2PwrGood = 200;
for (l = 0; l < sc->sc_noport; l++) {
/* XXX can't find out? */
sc->sc_hub_desc.hubd.DeviceRemovable[l / 8] &= ~(1 << (l % 8));
@@ -3317,7 +3342,7 @@
i |= UPS_CURRENT_CONNECT_STATUS;
if (v & EHCI_PS_PE)
i |= UPS_PORT_ENABLED;
- if (v & EHCI_PS_SUSP)
+ if ((v & EHCI_PS_SUSP) && !(v & EHCI_PS_FPR))
i |= UPS_SUSPEND;
if (v & EHCI_PS_OCA)
i |= UPS_OVERCURRENT_INDICATOR;
@@ -3333,6 +3358,8 @@
i |= UPS_C_PORT_ENABLED;
if (v & EHCI_PS_OCC)
i |= UPS_C_OVERCURRENT_INDICATOR;
+ if (v & EHCI_PS_FPR)
+ i |= UPS_C_SUSPEND;
if (sc->sc_isreset)
i |= UPS_C_PORT_RESET;
USETW(sc->sc_hub_desc.ps.wPortChange, i);
@@ -3856,6 +3883,108 @@
return;
}
+static void
+ehci_device_resume(struct usb2_device *udev)
+{
+ struct ehci_softc *sc = EHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->udev == udev) {
+
+ methods = xfer->pipe->methods;
+
+ if ((methods == &ehci_device_bulk_methods) ||
+ (methods == &ehci_device_ctrl_methods)) {
+ EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_async_p_last);
+ }
+ if (methods == &ehci_device_intr_methods) {
+ EHCI_APPEND_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ehci_device_suspend(struct usb2_device *udev)
+{
+ struct ehci_softc *sc = EHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->udev == udev) {
+
+ methods = xfer->pipe->methods;
+
+ if ((methods == &ehci_device_bulk_methods) ||
+ (methods == &ehci_device_ctrl_methods)) {
+ EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_async_p_last);
+ }
+ if (methods == &ehci_device_intr_methods) {
+ EHCI_REMOVE_QH(xfer->qh_start[xfer->flags_int.curr_dma_set],
+ sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ehci_set_hw_power(struct usb2_bus *bus)
+{
+ struct ehci_softc *sc = EHCI_BUS2SC(bus);
+ uint32_t temp;
+ uint32_t flags;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(bus);
+
+ flags = bus->hw_power_state;
+
+ temp = EOREAD4(sc, EHCI_USBCMD);
+
+ temp &= ~(EHCI_CMD_ASE | EHCI_CMD_PSE);
+
+ if (flags & (USB_HW_POWER_CONTROL |
+ USB_HW_POWER_BULK)) {
+ DPRINTF("Async is active\n");
+ temp |= EHCI_CMD_ASE;
+ }
+ if (flags & (USB_HW_POWER_INTERRUPT |
+ USB_HW_POWER_ISOC)) {
+ DPRINTF("Periodic is active\n");
+ temp |= EHCI_CMD_PSE;
+ }
+ EOWRITE4(sc, EHCI_USBCMD, temp);
+
+ USB_BUS_UNLOCK(bus);
+
+ return;
+}
+
struct usb2_bus_methods ehci_bus_methods =
{
.pipe_init = ehci_pipe_init,
@@ -3863,4 +3992,7 @@
.xfer_unsetup = ehci_xfer_unsetup,
.do_poll = ehci_do_poll,
.get_dma_delay = ehci_get_dma_delay,
+ .device_resume = ehci_device_resume,
+ .device_suspend = ehci_device_suspend,
+ .set_hw_power = ehci_set_hw_power,
};
==== //depot/projects/usb/src/sys/dev/usb2/controller/musb2_otg.c#17 (text+ko) ====
@@ -243,13 +243,6 @@
}
static void
-musbotg_rem_wakeup_set(struct usb2_device *udev, uint8_t is_on)
-{
- DPRINTFN(4, "is_on=%u\n", is_on);
- return;
-}
-
-static void
musbotg_set_address(struct musbotg_softc *sc, uint8_t addr)
{
DPRINTFN(4, "addr=%d\n", addr);
@@ -2941,5 +2934,4 @@
.set_stall = &musbotg_set_stall,
.clear_stall = &musbotg_clear_stall,
.vbus_interrupt = &musbotg_vbus_interrupt,
- .rem_wakeup_set = &musbotg_rem_wakeup_set,
};
==== //depot/projects/usb/src/sys/dev/usb2/controller/ohci2.c#17 (text+ko) ====
@@ -693,18 +693,22 @@
return;
}
-#define OHCI_APPEND_QH(sed,td_self,last) (last) = _ohci_append_qh(sed,td_self,last)
+#define OHCI_APPEND_QH(sed,last) (last) = _ohci_append_qh(sed,last)
static ohci_ed_t *
-_ohci_append_qh(ohci_ed_t *sed, uint32_t td_self, ohci_ed_t *last)
+_ohci_append_qh(ohci_ed_t *sed, ohci_ed_t *last)
{
DPRINTFN(11, "%p to %p\n", sed, last);
+ if (sed->prev != NULL) {
+ /* should not happen */
+ DPRINTFN(0, "ED already linked!\n");
+ return (last);
+ }
/* (sc->sc_bus.bus_mtx) must be locked */
sed->next = last->next;
sed->ed_next = last->ed_next;
sed->ed_tailp = 0;
- sed->ed_headp = td_self;
sed->prev = last;
@@ -742,13 +746,6 @@
sed->next->prev = sed->prev;
usb2_pc_cpu_flush(sed->next->page_cache);
}
- /*
- * terminate transfer in case the transferred packet was
- * short so that the ED still points at the last used TD
- */
- sed->ed_flags |= htole32(OHCI_ED_SKIP);
- sed->ed_headp = sed->ed_tailp;
-
last = ((last == sed) ? sed->prev : last);
sed->prev = 0;
@@ -1593,17 +1590,23 @@
td = xfer->td_transfer_first;
- OHCI_APPEND_QH(ed, td->td_self, *ed_last);
+ ed->ed_headp = td->td_self;
+
+ if (xfer->udev->pwr_save.suspended == 0) {
+ OHCI_APPEND_QH(ed, *ed_last);
- if (methods == &ohci_device_bulk_methods) {
- ohci_softc_t *sc = xfer->usb2_sc;
+ if (methods == &ohci_device_bulk_methods) {
+ ohci_softc_t *sc = xfer->usb2_sc;
- OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
- }
- if (methods == &ohci_device_ctrl_methods) {
- ohci_softc_t *sc = xfer->usb2_sc;
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
+ }
+ if (methods == &ohci_device_ctrl_methods) {
+ ohci_softc_t *sc = xfer->usb2_sc;
- OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+ }
+ } else {
+ usb2_pc_cpu_flush(ed->page_cache);
}
return;
}
@@ -2048,7 +2051,11 @@
td = xfer->td_transfer_first;
- OHCI_APPEND_QH(ed, td->itd_self, sc->sc_isoc_p_last);
+ ed->ed_headp = td->itd_self;
+
+ /* isochronous transfers are not affected by suspend / resume */
+
+ OHCI_APPEND_QH(ed, sc->sc_isoc_p_last);
return;
}
@@ -2792,6 +2799,115 @@
return;
}
+static void
+ohci_device_resume(struct usb2_device *udev)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ ohci_ed_t *ed;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (methods == &ohci_device_bulk_methods) {
+ OHCI_APPEND_QH(ed, sc->sc_bulk_p_last);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_BLF);
+ }
+ if (methods == &ohci_device_ctrl_methods) {
+ OHCI_APPEND_QH(ed, sc->sc_ctrl_p_last);
+ OWRITE4(sc, OHCI_COMMAND_STATUS, OHCI_CLF);
+ }
+ if (methods == &ohci_device_intr_methods) {
+ OHCI_APPEND_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ohci_device_suspend(struct usb2_device *udev)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ ohci_ed_t *ed;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ ed = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (methods == &ohci_device_bulk_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_bulk_p_last);
+ }
+ if (methods == &ohci_device_ctrl_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_ctrl_p_last);
+ }
+ if (methods == &ohci_device_intr_methods) {
+ OHCI_REMOVE_QH(ed, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+ohci_set_hw_power(struct usb2_bus *bus)
+{
+ struct ohci_softc *sc = OHCI_BUS2SC(bus);
+ uint32_t temp;
+ uint32_t flags;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(bus);
+
+ flags = bus->hw_power_state;
+
+ temp = OREAD4(sc, OHCI_CONTROL);
+ temp &= ~(OHCI_PLE | OHCI_IE | OHCI_CLE | OHCI_BLE);
+
+ if (flags & USB_HW_POWER_CONTROL)
+ temp |= OHCI_CLE;
+
+ if (flags & USB_HW_POWER_BULK)
+ temp |= OHCI_BLE;
+
+ if (flags & USB_HW_POWER_INTERRUPT)
+ temp |= OHCI_PLE;
+
+ if (flags & USB_HW_POWER_ISOC)
+ temp |= OHCI_IE;
+
+ OWRITE4(sc, OHCI_CONTROL, temp);
+
+ USB_BUS_UNLOCK(bus);
+
+ return;
+}
+
struct usb2_bus_methods ohci_bus_methods =
{
.pipe_init = ohci_pipe_init,
@@ -2799,4 +2915,7 @@
.xfer_unsetup = ohci_xfer_unsetup,
.do_poll = ohci_do_poll,
.get_dma_delay = ohci_get_dma_delay,
+ .device_resume = ohci_device_resume,
+ .device_suspend = ohci_device_suspend,
+ .set_hw_power = ohci_set_hw_power,
};
==== //depot/projects/usb/src/sys/dev/usb2/controller/uhci2.c#15 (text+ko) ====
@@ -933,17 +933,19 @@
return (std);
}
-#define UHCI_APPEND_QH(sqh,td,last) (last) = _uhci_append_qh(sqh,td,last)
+#define UHCI_APPEND_QH(sqh,last) (last) = _uhci_append_qh(sqh,last)
static uhci_qh_t *
-_uhci_append_qh(uhci_qh_t *sqh, uhci_td_t *td, uhci_qh_t *last)
+_uhci_append_qh(uhci_qh_t *sqh, uhci_qh_t *last)
{
DPRINTFN(11, "%p to %p\n", sqh, last);
+ if (sqh->h_prev != NULL) {
+ /* should not happen */
+ DPRINTFN(0, "QH already linked!\n");
+ return (last);
+ }
/* (sc->sc_bus.mtx) must be locked */
- sqh->e_next = td;
- sqh->qh_e_next = td->td_self;
-
sqh->h_next = last->h_next;
sqh->qh_h_next = last->qh_h_next;
@@ -1007,13 +1009,6 @@
sqh->h_next->h_prev = sqh->h_prev;
usb2_pc_cpu_flush(sqh->h_next->page_cache);
}
- /*
- * set the Terminate-bit in the e_next of the QH, in case
- * the transferred packet was short so that the QH still
- * points at the last used TD
- */
- sqh->qh_e_next = htole32(UHCI_PTR_T);
-
last = ((last == sqh) ? sqh->h_prev : last);
sqh->h_prev = 0;
@@ -1481,10 +1476,12 @@
}
if (status & UHCI_STS_HCH) {
/* no acknowledge needed */
- printf("%s: host controller halted\n",
+ DPRINTF("%s: host controller halted\n",
__FUNCTION__);
#if USB_DEBUG
- uhci_dump_all(sc);
+ if (uhcidebug > 0) {
+ uhci_dump_all(sc);
+ }
#endif
}
}
@@ -1865,11 +1862,6 @@
qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
if (qh) {
usb2_pc_cpu_invalidate(qh->page_cache);
-
- qh->e_next = 0;
- qh->qh_e_next = htole32(UHCI_PTR_T);
-
- usb2_pc_cpu_flush(qh->page_cache);
}
if (xfer->flags_int.bandwidth_reclaimed) {
xfer->flags_int.bandwidth_reclaimed = 0;
@@ -1940,9 +1932,16 @@
/* setup QH */
qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
- UHCI_APPEND_QH(qh, td, sc->sc_bulk_p_last);
- uhci_add_loop(sc);
- xfer->flags_int.bandwidth_reclaimed = 1;
+ qh->e_next = td;
+ qh->qh_e_next = td->td_self;
+
+ if (xfer->udev->pwr_save.suspended == 0) {
+ UHCI_APPEND_QH(qh, sc->sc_bulk_p_last);
+ uhci_add_loop(sc);
+ xfer->flags_int.bandwidth_reclaimed = 1;
+ } else {
+ usb2_pc_cpu_flush(qh->page_cache);
+ }
/* put transfer on interrupt queue */
uhci_transfer_intr_enqueue(xfer);
@@ -1994,14 +1993,21 @@
/* setup QH */
qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+ qh->e_next = td;
+ qh->qh_e_next = td->td_self;
+
/*
* NOTE: some devices choke on bandwidth- reclamation for control
* transfers
*/
- if (xfer->udev->speed == USB_SPEED_LOW) {
- UHCI_APPEND_QH(qh, td, sc->sc_ls_ctl_p_last);
+ if (xfer->udev->pwr_save.suspended == 0) {
+ if (xfer->udev->speed == USB_SPEED_LOW) {
+ UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last);
+ } else {
+ UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last);
+ }
} else {
- UHCI_APPEND_QH(qh, td, sc->sc_fs_ctl_p_last);
+ usb2_pc_cpu_flush(qh->page_cache);
}
/* put transfer on interrupt queue */
uhci_transfer_intr_enqueue(xfer);
@@ -2085,8 +2091,17 @@
/* setup QH */
qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
- /* enter QHs into the controller data structures */
- UHCI_APPEND_QH(qh, td, sc->sc_intr_p_last[xfer->qh_pos]);
+ qh->e_next = td;
+ qh->qh_e_next = td->td_self;
+
+ if (xfer->udev->pwr_save.suspended == 0) {
+
+ /* enter QHs into the controller data structures */
+ UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
+
+ } else {
+ usb2_pc_cpu_flush(qh->page_cache);
+ }
/* put transfer on interrupt queue */
uhci_transfer_intr_enqueue(xfer);
@@ -2707,7 +2722,8 @@
break;
case UHF_PORT_SUSPEND:
x = URWMASK(UREAD2(sc, port));
- UWRITE2(sc, port, x & ~UHCI_PORTSC_SUSP);
+ /* clear suspend and resume detect */
+ UWRITE2(sc, port, x & ~(UHCI_PORTSC_SUSP | UHCI_PORTSC_RD));
break;
case UHF_PORT_RESET:
x = URWMASK(UREAD2(sc, port));
@@ -2729,11 +2745,13 @@
sc->sc_isreset = 0;
std->err = USB_ERR_NORMAL_COMPLETION;
goto done;
+ case UHF_C_PORT_SUSPEND:
+ /* nop */
+ break;
case UHF_PORT_CONNECTION:
case UHF_PORT_OVER_CURRENT:
case UHF_PORT_POWER:
case UHF_PORT_LOW_SPEED:
- case UHF_C_PORT_SUSPEND:
default:
std->err = USB_ERR_IOERROR;
goto done;
@@ -2788,10 +2806,13 @@
status |= UPS_OVERCURRENT_INDICATOR;
if (x & UHCI_PORTSC_OCIC)
change |= UPS_C_OVERCURRENT_INDICATOR;
- if (x & UHCI_PORTSC_SUSP)
- status |= UPS_SUSPEND;
if (x & UHCI_PORTSC_LSDA)
status |= UPS_LOW_SPEED;
+ if ((x & UHCI_PORTSC_PE) && (x & UHCI_PORTSC_RD))
+ change |= UPS_C_SUSPEND;
+ else if (x & UHCI_PORTSC_SUSP)
+ status |= UPS_SUSPEND;
+
status |= UPS_PORT_POWER;
if (sc->sc_isreset)
change |= UPS_C_PORT_RESET;
@@ -2945,13 +2966,15 @@
sc->sc_hub_idata[0] = 0;
- if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) {
+ if (UREAD2(sc, UHCI_PORTSC1) & (UHCI_PORTSC_CSC |
+ UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) {
sc->sc_hub_idata[0] |= 1 << 1;
}
- if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC | UHCI_PORTSC_OCIC)) {
+ if (UREAD2(sc, UHCI_PORTSC2) & (UHCI_PORTSC_CSC |
+ UHCI_PORTSC_OCIC | UHCI_PORTSC_RD)) {
sc->sc_hub_idata[0] |= 1 << 2;
}
- if ((sc->sc_hub_idata[0] == 0) || !(UREAD2(sc, UHCI_CMD) & UHCI_CMD_RS)) {
+ if (sc->sc_hub_idata[0] == 0) {
/*
* no change or controller not running, try again in a while
*/
@@ -3246,6 +3269,124 @@
return;
}
+static void
+uhci_device_resume(struct usb2_device *udev)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ uhci_qh_t *qh;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (methods == &uhci_device_bulk_methods) {
+ UHCI_APPEND_QH(qh, sc->sc_bulk_p_last);
+ uhci_add_loop(sc);
+ xfer->flags_int.bandwidth_reclaimed = 1;
+ }
+ if (methods == &uhci_device_ctrl_methods) {
+ if (xfer->udev->speed == USB_SPEED_LOW) {
+ UHCI_APPEND_QH(qh, sc->sc_ls_ctl_p_last);
+ } else {
+ UHCI_APPEND_QH(qh, sc->sc_fs_ctl_p_last);
+ }
+ }
+ if (methods == &uhci_device_intr_methods) {
+ UHCI_APPEND_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+uhci_device_suspend(struct usb2_device *udev)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(udev->bus);
+ struct usb2_xfer *xfer;
+ struct usb2_pipe_methods *methods;
+ uhci_qh_t *qh;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(udev->bus);
+
+ TAILQ_FOREACH(xfer, &sc->sc_bus.intr_q.head, wait_entry) {
+
+ if (xfer->udev == udev) {
+
+ methods = xfer->pipe->methods;
+ qh = xfer->qh_start[xfer->flags_int.curr_dma_set];
+
+ if (xfer->flags_int.bandwidth_reclaimed) {
+ xfer->flags_int.bandwidth_reclaimed = 0;
+ uhci_rem_loop(sc);
+ }
+ if (methods == &uhci_device_bulk_methods) {
+ UHCI_REMOVE_QH(qh, sc->sc_bulk_p_last);
+ }
+ if (methods == &uhci_device_ctrl_methods) {
+ if (xfer->udev->speed == USB_SPEED_LOW) {
+ UHCI_REMOVE_QH(qh, sc->sc_ls_ctl_p_last);
+ } else {
+ UHCI_REMOVE_QH(qh, sc->sc_fs_ctl_p_last);
+ }
+ }
+ if (methods == &uhci_device_intr_methods) {
+ UHCI_REMOVE_QH(qh, sc->sc_intr_p_last[xfer->qh_pos]);
+ }
+ }
+ }
+
+ USB_BUS_UNLOCK(udev->bus);
+
+ return;
+}
+
+static void
+uhci_set_hw_power(struct usb2_bus *bus)
+{
+ struct uhci_softc *sc = UHCI_BUS2SC(bus);
+ uint32_t flags;
+
+ DPRINTF("\n");
+
+ USB_BUS_LOCK(bus);
+
+ flags = bus->hw_power_state;
+
+ if (flags & (USB_HW_POWER_CONTROL |
+ USB_HW_POWER_BULK |
+ USB_HW_POWER_INTERRUPT |
+ USB_HW_POWER_ISOC)) {
+ DPRINTF("Some USB transfer is "
+ "active on %u.\n",
+ device_get_unit(sc->sc_bus.bdev));
+ UHCICMD(sc, (UHCI_CMD_MAXP | UHCI_CMD_RS));
+ } else {
+ DPRINTF("Power save on %u.\n",
+ device_get_unit(sc->sc_bus.bdev));
+ UHCICMD(sc, UHCI_CMD_MAXP);
+ }
+
+ USB_BUS_UNLOCK(bus);
+
+ return;
+}
+
+
struct usb2_bus_methods uhci_bus_methods =
{
.pipe_init = uhci_pipe_init,
@@ -3253,4 +3394,7 @@
.xfer_unsetup = uhci_xfer_unsetup,
.do_poll = uhci_do_poll,
.get_dma_delay = uhci_get_dma_delay,
+ .device_resume = uhci_device_resume,
+ .device_suspend = uhci_device_suspend,
+ .set_hw_power = uhci_set_hw_power,
};
==== //depot/projects/usb/src/sys/dev/usb2/controller/uhci2.h#6 (text+ko) ====
@@ -300,7 +300,7 @@
uint8_t sc_addr; /* device address */
uint8_t sc_conf; /* device configuration */
- uint8_t sc_isreset;
+ uint8_t sc_isreset; /* bits set if a root hub is reset */
uint8_t sc_saved_sof;
uint8_t sc_hub_idata[1];
==== //depot/projects/usb/src/sys/dev/usb2/controller/usb2_bus.h#6 (text+ko) ====
@@ -54,10 +54,13 @@
struct usb2_process explore_proc;
struct usb2_bus_msg explore_msg[2];
struct usb2_bus_msg detach_msg[2];
- struct mtx bus_mtx; /* This mutex protects the USB
- * hardware */
+ /*
+ * This mutex protects the USB hardware:
+ */
+ struct mtx bus_mtx;
struct usb2_perm perm;
struct usb2_xfer_queue intr_q;
+ struct usb2_callout power_wdog; /* power management */
device_t bdev; /* filled by HC driver */
@@ -67,6 +70,7 @@
struct usb2_bus_methods *methods; /* filled by HC driver */
struct usb2_device *devices[USB_MAX_DEVICES];
+ uint32_t hw_power_state; /* see USB_HW_POWER_XXX */
uint32_t uframe_usage[USB_HS_MICRO_FRAMES_MAX];
uint32_t transfer_count[4];
uint16_t isoc_time_last; /* in milliseconds */
==== //depot/projects/usb/src/sys/dev/usb2/controller/usb2_controller.c#16 (text+ko) ====
@@ -142,6 +142,9 @@
/* was never setup properly */
return (0);
}
+ /* Stop power watchdog */
+ usb2_callout_drain(&bus->power_wdog);
+
/* Let the USB explore process detach all devices. */
USB_BUS_LOCK(bus);
@@ -192,6 +195,11 @@
mtx_lock(&Giant);
/*
+ * First update the USB power state!
+ */
+ usb2_bus_powerd(bus);
+
+ /*
* Explore the Root USB HUB. This call can sleep,
* exiting Giant, which is actually Giant.
*/
@@ -242,6 +250,23 @@
return;
}
+static void
+usb2_power_wdog(void *arg)
+{
+ struct usb2_bus *bus = arg;
+
+ USB_BUS_LOCK_ASSERT(bus, MA_OWNED);
+
+ usb2_callout_reset(&bus->power_wdog,
+ 4 * hz, usb2_power_wdog, arg);
+
+ USB_BUS_UNLOCK(bus);
+
+ usb2_bus_power_update(bus);
+
+ return;
+}
+
/*------------------------------------------------------------------------*
* usb2_attach_sub
*
@@ -325,6 +350,10 @@
}
/* set softc - we are ready */
device_set_softc(dev, bus);
+ /* start watchdog */
+ USB_BUS_LOCK(bus);
+ /* this function will unlock the BUS lock ! */
+ usb2_power_wdog(bus);
return;
}
@@ -435,6 +464,9 @@
mtx_init(&bus->bus_mtx, "USB bus lock",
NULL, MTX_DEF | MTX_RECURSE);
+ usb2_callout_init_mtx(&bus->power_wdog,
+ &bus->bus_mtx, CALLOUT_RETURNUNLOCKED);
+
TAILQ_INIT(&bus->intr_q.head);
usb2_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags,
==== //depot/projects/usb/src/sys/dev/usb2/controller/usb2_controller.h#6 (text+ko) ====
>>> TRUNCATED FOR MAIL (1000 lines) <<<
More information about the p4-projects
mailing list