Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 21 Dec 2008 15:36:25 GMT
From:      Hans Petter Selasky <hselasky@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 155086 for review
Message-ID:  <200812211536.mBLFaPfI051247@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=155086

Change 155086 by hselasky@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) <<<



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200812211536.mBLFaPfI051247>