- ohci-add-auto-stop-support.patch removed from -mm tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The patch titled

     OHCI: add auto-stop support

has been removed from the -mm tree.  Its filename is

     ohci-add-auto-stop-support.patch

This patch was dropped because it was merged into mainline or a subsystem tree

------------------------------------------------------
Subject: OHCI: add auto-stop support
From: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>

This patch (as790) adds "auto-stop" support to ohci-hcd: the driver will
automatically stop the host controller when no devices have been connected for
at least one second.  This feature is useful when the USB core autosuspend
facility isn't available, such as when CONFIG_USB_SUSPEND hasn't been set.

Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
Cc: Greg KH <greg@xxxxxxxxx>
Cc: "Rafael J. Wysocki" <rjw@xxxxxxx>
Signed-off-by: Andrew Morton <akpm@xxxxxxxx>
---

 drivers/usb/host/ohci-hcd.c |   18 +-
 drivers/usb/host/ohci-hub.c |  229 +++++++++++++++++++++++-----------
 drivers/usb/host/ohci.h     |    1 
 3 files changed, 164 insertions(+), 84 deletions(-)

diff -puN drivers/usb/host/ohci-hcd.c~ohci-add-auto-stop-support drivers/usb/host/ohci-hcd.c
--- a/drivers/usb/host/ohci-hcd.c~ohci-add-auto-stop-support
+++ a/drivers/usb/host/ohci-hcd.c
@@ -715,17 +715,8 @@ static irqreturn_t ohci_irq (struct usb_
 		return IRQ_NOTMINE;
 	}
 
-	/* NOTE:  vendors didn't always make the same implementation
-	 * choices for RHSC.  Sometimes it triggers on an edge (like
-	 * setting and maybe clearing a port status change bit); and
-	 * it's level-triggered on other silicon, active until khubd
-	 * clears all active port status change bits.  Poll by timer
-	 * til it's fully debounced and the difference won't matter.
-	 */
 	if (ints & OHCI_INTR_RHSC) {
 		ohci_vdbg (ohci, "rhsc\n");
-		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrdisable);
-		hcd->poll_rh = 1;
 		ohci->next_statechange = jiffies + STATECHANGE_DELAY;
 		ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
 		usb_hcd_poll_rh_status(hcd);
@@ -743,13 +734,18 @@ static irqreturn_t ohci_irq (struct usb_
 	if (ints & OHCI_INTR_RD) {
 		ohci_vdbg (ohci, "resume detect\n");
 		ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus);
-		if (hcd->state != HC_STATE_QUIESCING)
+		hcd->poll_rh = 1;
+		if (ohci->autostop) {
+			spin_lock (&ohci->lock);
+			ohci_rh_resume (ohci);
+			spin_unlock (&ohci->lock);
+		} else
 			usb_hcd_resume_root_hub(hcd);
 	}
 
 	if (ints & OHCI_INTR_WDH) {
 		if (HC_IS_RUNNING(hcd->state))
-			ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);	
+			ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
 		spin_lock (&ohci->lock);
 		dl_done_list (ohci, ptregs);
 		spin_unlock (&ohci->lock);
diff -puN drivers/usb/host/ohci-hub.c~ohci-add-auto-stop-support drivers/usb/host/ohci-hub.c
--- a/drivers/usb/host/ohci-hub.c~ohci-add-auto-stop-support
+++ a/drivers/usb/host/ohci-hub.c
@@ -41,31 +41,20 @@ static void ohci_rhsc_enable (struct usb
 {
 	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
 
-	hcd->poll_rh = 0;
 	ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
 }
 
-#ifdef	CONFIG_PM
-
 #define OHCI_SCHED_ENABLES \
 	(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
 
 static void dl_done_list (struct ohci_hcd *, struct pt_regs *);
 static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *);
-static int ohci_restart (struct ohci_hcd *ohci);
 
-static int ohci_bus_suspend (struct usb_hcd *hcd)
+static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
+__releases(ohci->lock)
+__acquires(ohci->lock)
 {
-	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
 	int			status = 0;
-	unsigned long		flags;
-
-	spin_lock_irqsave (&ohci->lock, flags);
-
-	if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
-		spin_unlock_irqrestore (&ohci->lock, flags);
-		return -ESHUTDOWN;
-	}
 
 	ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
 	switch (ohci->hc_control & OHCI_CTRL_HCFS) {
@@ -81,15 +70,16 @@ static int ohci_bus_suspend (struct usb_
 		ohci_dbg (ohci, "needs reinit!\n");
 		goto done;
 	case OHCI_USB_SUSPEND:
-		ohci_dbg (ohci, "already suspended\n");
-		goto done;
+		if (!ohci->autostop) {
+			ohci_dbg (ohci, "already suspended\n");
+			goto done;
+		}
 	}
-	ohci_dbg (ohci, "suspend root hub\n");
+	ohci_dbg (ohci, "%s root hub\n",
+			autostop ? "auto-stop" : "suspend");
 
 	/* First stop any processing */
-	if (ohci->hc_control & OHCI_SCHED_ENABLES) {
-		int		limit;
-
+	if (!autostop && (ohci->hc_control & OHCI_SCHED_ENABLES)) {
 		ohci->hc_control &= ~OHCI_SCHED_ENABLES;
 		ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
 		ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
@@ -99,24 +89,17 @@ static int ohci_bus_suspend (struct usb_
 		 * then the last WDH could take 6+ msec
 		 */
 		ohci_dbg (ohci, "stopping schedules ...\n");
-		limit = 2000;
-		while (limit > 0) {
-			udelay (250);
-			limit =- 250;
-			if (ohci_readl (ohci, &ohci->regs->intrstatus)
-					& OHCI_INTR_SF)
-				break;
-		}
-		dl_done_list (ohci, NULL);
-		mdelay (7);
+		ohci->autostop = 0;
+		spin_unlock_irq (&ohci->lock);
+		msleep (8);
+		spin_lock_irq (&ohci->lock);
 	}
 	dl_done_list (ohci, NULL);
 	finish_unlinks (ohci, ohci_frame_no(ohci), NULL);
-	ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
-			&ohci->regs->intrstatus);
 
 	/* maybe resume can wake root hub */
-	if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev))
+	if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev) ||
+			autostop)
 		ohci->hc_control |= OHCI_CTRL_RWE;
 	else {
 		ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
@@ -132,13 +115,12 @@ static int ohci_bus_suspend (struct usb_
 	(void) ohci_readl (ohci, &ohci->regs->control);
 
 	/* no resumes until devices finish suspending */
-	ohci->next_statechange = jiffies + msecs_to_jiffies (5);
-
-	/* no timer polling */
-	hcd->poll_rh = 0;
+	if (!autostop) {
+		ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+		ohci->autostop = 0;
+	}
 
 done:
-	spin_unlock_irqrestore (&ohci->lock, flags);
 	return status;
 }
 
@@ -151,24 +133,16 @@ static inline struct ed *find_head (stru
 }
 
 /* caller has locked the root hub */
-static int ohci_bus_resume (struct usb_hcd *hcd)
+static int ohci_rh_resume (struct ohci_hcd *ohci)
+__releases(ohci->lock)
+__acquires(ohci->lock)
 {
-	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	struct usb_hcd		*hcd = ohci_to_hcd (ohci);
 	u32			temp, enables;
 	int			status = -EINPROGRESS;
-	unsigned long		flags;
-
-	if (time_before (jiffies, ohci->next_statechange))
-		msleep(5);
-
-	spin_lock_irqsave (&ohci->lock, flags);
-
-	if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
-		spin_unlock_irqrestore (&ohci->lock, flags);
-		return -ESHUTDOWN;
-	}
-
+	int			autostopped = ohci->autostop;
 
+	ohci->autostop = 0;
 	ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
 
 	if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
@@ -188,7 +162,8 @@ static int ohci_bus_resume (struct usb_h
 		ohci->hc_control |= OHCI_USB_RESUME;
 		ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
 		(void) ohci_readl (ohci, &ohci->regs->control);
-		ohci_dbg (ohci, "resume root hub\n");
+		ohci_dbg (ohci, "%s root hub\n",
+				autostopped ? "auto-start" : "resume");
 		break;
 	case OHCI_USB_RESUME:
 		/* HCFS changes sometime after INTR_RD */
@@ -203,16 +178,26 @@ static int ohci_bus_resume (struct usb_h
 		ohci_dbg (ohci, "lost power\n");
 		status = -EBUSY;
 	}
-	spin_unlock_irqrestore (&ohci->lock, flags);
+#ifdef	CONFIG_PM
 	if (status == -EBUSY) {
-		(void) ohci_init (ohci);
-		return ohci_restart (ohci);
+		if (!autostopped) {
+			static int ohci_restart (struct ohci_hcd *ohci);
+
+			spin_unlock_irq (&ohci->lock);
+			(void) ohci_init (ohci);
+			status = ohci_restart (ohci);
+			spin_lock_irq (&ohci->lock);
+		}
+		return status;
 	}
+#endif
 	if (status != -EINPROGRESS)
 		return status;
+	if (autostopped)
+		goto skip_resume;
+	spin_unlock_irq (&ohci->lock);
 
 	temp = ohci->num_ports;
-	enables = 0;
 	while (temp--) {
 		u32 stat = ohci_readl (ohci,
 				       &ohci->regs->roothub.portstatus [temp]);
@@ -245,17 +230,21 @@ static int ohci_bus_resume (struct usb_h
 	/* Sometimes PCI D3 suspend trashes frame timings ... */
 	periodic_reinit (ohci);
 
+	/* the following code is executed with ohci->lock held and
+	 * irqs disabled if and only if autostopped is true
+	 */
+
+skip_resume:
 	/* interrupts might have been disabled */
 	ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
 	if (ohci->ed_rm_list)
 		ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
-	ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
-			&ohci->regs->intrstatus);
 
 	/* Then re-enable operations */
 	ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
 	(void) ohci_readl (ohci, &ohci->regs->control);
-	msleep (3);
+	if (!autostopped)
+		msleep (3);
 
 	temp = ohci->hc_control;
 	temp &= OHCI_CTRL_RWC;
@@ -265,7 +254,11 @@ static int ohci_bus_resume (struct usb_h
 	(void) ohci_readl (ohci, &ohci->regs->control);
 
 	/* TRSMRCY */
-	msleep (10);
+	if (!autostopped) {
+		msleep (10);
+		spin_lock_irq (&ohci->lock);
+	}
+	/* now ohci->lock is always held and irqs are always disabled */
 
 	/* keep it alive for more than ~5x suspend + resume costs */
 	ohci->next_statechange = jiffies + STATECHANGE_DELAY;
@@ -302,6 +295,45 @@ static int ohci_bus_resume (struct usb_h
 	return 0;
 }
 
+#ifdef	CONFIG_PM
+
+static int ohci_bus_suspend (struct usb_hcd *hcd)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	int			rc;
+
+	spin_lock_irq (&ohci->lock);
+
+	if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+		rc = -ESHUTDOWN;
+	else
+		rc = ohci_rh_suspend (ohci, 0);
+	spin_unlock_irq (&ohci->lock);
+	return rc;
+}
+
+static int ohci_bus_resume (struct usb_hcd *hcd)
+{
+	struct ohci_hcd		*ohci = hcd_to_ohci (hcd);
+	int			rc;
+
+	if (time_before (jiffies, ohci->next_statechange))
+		msleep(5);
+
+	spin_lock_irq (&ohci->lock);
+
+	if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+		rc = -ESHUTDOWN;
+	else
+		rc = ohci_rh_resume (ohci);
+	spin_unlock_irq (&ohci->lock);
+
+	/* poll until we know a device is connected or we autostop */
+	if (rc == 0)
+		usb_hcd_poll_rh_status(hcd);
+	return rc;
+}
+
 #endif	/* CONFIG_PM */
 
 /*-------------------------------------------------------------------------*/
@@ -313,17 +345,11 @@ ohci_hub_status_data (struct usb_hcd *hc
 {
 	struct ohci_hcd	*ohci = hcd_to_ohci (hcd);
 	int		i, changed = 0, length = 1;
+	int		any_connected = 0, rhsc_enabled = 1;
 	unsigned long	flags;
 
 	spin_lock_irqsave (&ohci->lock, flags);
 
-	/* handle autosuspended root:  finish resuming before
-	 * letting khubd or root hub timer see state changes.
-	 */
-	if (unlikely((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
-		     || !HC_IS_RUNNING(hcd->state)))
-		goto done;
-
 	/* undocumented erratum seen on at least rev D */
 	if ((ohci->flags & OHCI_QUIRK_AMD756)
 			&& (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) {
@@ -347,6 +373,9 @@ ohci_hub_status_data (struct usb_hcd *hc
 	for (i = 0; i < ohci->num_ports; i++) {
 		u32	status = roothub_portstatus (ohci, i);
 
+		/* can't autostop if ports are connected */
+		any_connected |= (status & RH_PS_CCS);
+
 		if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
 				| RH_PS_OCIC | RH_PS_PRSC)) {
 			changed = 1;
@@ -354,15 +383,69 @@ ohci_hub_status_data (struct usb_hcd *hc
 			    buf [0] |= 1 << (i + 1);
 			else
 			    buf [1] |= 1 << (i - 7);
-			continue;
 		}
 	}
 
-	/* after root hub changes, stop polling after debouncing
-	 * for a while and maybe kicking in autosuspend
+	/* NOTE:  vendors didn't always make the same implementation
+	 * choices for RHSC.  Sometimes it triggers on an edge (like
+	 * setting and maybe clearing a port status change bit); and
+	 * it's level-triggered on other silicon, active until khubd
+	 * clears all active port status change bits.  If it's still
+	 * set (level-triggered) we must disable it and rely on
+	 * polling until khubd re-enables it.
 	 */
-	if (changed)
-		ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+	if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) {
+		ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
+		(void) ohci_readl (ohci, &ohci->regs->intrdisable);
+		rhsc_enabled = 0;
+	}
+	hcd->poll_rh = 1;
+
+	/* carry out appropriate state changes */
+	switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+
+	case OHCI_USB_OPER:
+		/* keep on polling until we know a device is connected
+		 * and RHSC is enabled */
+		if (!ohci->autostop) {
+			if (any_connected) {
+				if (rhsc_enabled)
+					hcd->poll_rh = 0;
+			} else {
+				ohci->autostop = 1;
+				ohci->next_statechange = jiffies + HZ;
+			}
+
+		/* if no devices have been attached for one second, autostop */
+		} else {
+			if (changed || any_connected) {
+				ohci->autostop = 0;
+				ohci->next_statechange = jiffies +
+						STATECHANGE_DELAY;
+			} else if (time_after_eq (jiffies,
+						ohci->next_statechange)
+					&& !ohci->ed_rm_list
+					&& !(ohci->hc_control &
+						OHCI_SCHED_ENABLES)) {
+				ohci_rh_suspend (ohci, 1);
+			}
+		}
+		break;
+
+	/* if there is a port change, autostart or ask to be resumed */
+	case OHCI_USB_SUSPEND:
+	case OHCI_USB_RESUME:
+		if (changed) {
+			if (ohci->autostop)
+				ohci_rh_resume (ohci);
+			else
+				usb_hcd_resume_root_hub (hcd);
+		} else {
+			/* everything is idle, no need for polling */
+			hcd->poll_rh = 0;
+		}
+		break;
+	}
 
 done:
 	spin_unlock_irqrestore (&ohci->lock, flags);
diff -puN drivers/usb/host/ohci.h~ohci-add-auto-stop-support drivers/usb/host/ohci.h
--- a/drivers/usb/host/ohci.h~ohci-add-auto-stop-support
+++ a/drivers/usb/host/ohci.h
@@ -388,6 +388,7 @@ struct ohci_hcd {
 	u32 			hc_control;	/* copy of hc control reg */
 	unsigned long		next_statechange;	/* suspend/resume */
 	u32			fminterval;		/* saved register */
+	unsigned		autostop:1;	/* rh auto stopping/stopped */
 
 	unsigned long		flags;		/* for HC bugs */
 #define	OHCI_QUIRK_AMD756	0x01			/* erratum #4 */
_

Patches currently in -mm which might be from stern@xxxxxxxxxxxxxxxxxxx are

origin.patch
block-layer-early-detection-of-medium-not-present.patch
scsi-core-and-sd-early-detection-of-medium-not-present.patch
sd-early-detection-of-medium-not-present.patch
scsi-early-detection-of-medium-not-present-updated.patch
add-section-on-function-return-values-to-codingstyle.patch
add-srcu-based-notifier-chains.patch
srcu-report-out-of-memory-errors.patch
srcu-report-out-of-memory-errors-fixlet.patch
cpufreq-make-the-transition_notifier-chain-use-srcu.patch

-
To unsubscribe from this list: send the line "unsubscribe mm-commits" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Kernel Newbies FAQ]     [Kernel Archive]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [Bugtraq]     [Photo]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]

  Powered by Linux