Re: Webcam stops other USB devices (ehci_hcd: HC died; cleaning up)

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

 



On Wed, 21 Dec 2011, Anton Alekseev wrote:

> Alan, I've applied your patch to 3.1.5 and it seems that some troubles fixed.
> HCI doesn't die now even with too small audio latencies (died without
> this patch).
> Will continue testing.

Michael and Anton:

Below is a revised version of the original patch (it's actually a
combination of several patches that I intend to submit, assuming they
work okay).  The patch is based on the 3.3 kernel, but with a little
hand-editing it ought to work with earlier or later versions too.  I'd
appreciate any testing you can do.

Geoff, I removed the changes we added to accomodate the PS3.  I think
the driver will work okay without them, but you may see the warning
message added in ehci_poll_PSS() more often than you want.  If
necessary we can modify that routine to avoid logging the warning on
the PS3, or we could make it a one-time message.

Alan Stern



Index: x/drivers/usb/host/ehci-hcd.c
===================================================================
--- x.orig/drivers/usb/host/ehci-hcd.c
+++ x/drivers/usb/host/ehci-hcd.c
@@ -30,8 +30,7 @@
 #include <linux/vmalloc.h>
 #include <linux/errno.h>
 #include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/ktime.h>
+#include <linux/hrtimer.h>
 #include <linux/list.h>
 #include <linux/interrupt.h>
 #include <linux/usb.h>
@@ -128,6 +127,7 @@ MODULE_PARM_DESC(hird, "host initiated r
 #include "ehci.h"
 #include "ehci-dbg.c"
 #include "pci-quirks.h"
+#include "ehci-timer.c"
 
 /*-------------------------------------------------------------------------*/
 
@@ -227,75 +227,18 @@ static int ehci_halt (struct ehci_hcd *e
 	if ((temp & STS_HALT) != 0)
 		return 0;
 
+	/*
+	 * This routine gets called during probe before ehci->command
+	 * has been initialized, so we can't rely on its value.
+	 */
+	ehci->command &= ~CMD_RUN;
 	temp = ehci_readl(ehci, &ehci->regs->command);
-	temp &= ~CMD_RUN;
+	temp &= ~(CMD_RUN | CMD_IAAD);
 	ehci_writel(ehci, temp, &ehci->regs->command);
 	return handshake (ehci, &ehci->regs->status,
 			  STS_HALT, STS_HALT, 16 * 125);
 }
 
-#if defined(CONFIG_USB_SUSPEND) && defined(CONFIG_PPC_PS3)
-
-/*
- * The EHCI controller of the Cell Super Companion Chip used in the
- * PS3 will stop the root hub after all root hub ports are suspended.
- * When in this condition handshake will return -ETIMEDOUT.  The
- * STS_HLT bit will not be set, so inspection of the frame index is
- * used here to test for the condition.  If the condition is found
- * return success to allow the USB suspend to complete.
- */
-
-static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
-					 void __iomem *ptr, u32 mask, u32 done,
-					 int usec)
-{
-	unsigned int old_index;
-	int error;
-
-	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
-		return -ETIMEDOUT;
-
-	old_index = ehci_read_frame_index(ehci);
-
-	error = handshake(ehci, ptr, mask, done, usec);
-
-	if (error == -ETIMEDOUT && ehci_read_frame_index(ehci) == old_index)
-		return 0;
-
-	return error;
-}
-
-#else
-
-static int handshake_for_broken_root_hub(struct ehci_hcd *ehci,
-					 void __iomem *ptr, u32 mask, u32 done,
-					 int usec)
-{
-	return -ETIMEDOUT;
-}
-
-#endif
-
-static int handshake_on_error_set_halt(struct ehci_hcd *ehci, void __iomem *ptr,
-				       u32 mask, u32 done, int usec)
-{
-	int error;
-
-	error = handshake(ehci, ptr, mask, done, usec);
-	if (error == -ETIMEDOUT)
-		error = handshake_for_broken_root_hub(ehci, ptr, mask, done,
-						      usec);
-
-	if (error) {
-		ehci_halt(ehci);
-		ehci->rh_state = EHCI_RH_HALTED;
-		ehci_err(ehci, "force halt; handshake %p %08x %08x -> %d\n",
-			ptr, mask, done, error);
-	}
-
-	return error;
-}
-
 /* put TDI/ARC silicon into EHCI mode */
 static void tdi_reset (struct ehci_hcd *ehci)
 {
@@ -348,6 +291,7 @@ static int ehci_reset (struct ehci_hcd *
 	if (ehci->debug)
 		dbgp_external_startup();
 
+	ehci->command = ehci_readl(ehci, &ehci->regs->command);
 	return retval;
 }
 
@@ -362,20 +306,17 @@ static void ehci_quiesce (struct ehci_hc
 #endif
 
 	/* wait for any schedule enables/disables to take effect */
-	temp = ehci_readl(ehci, &ehci->regs->command) << 10;
-	temp &= STS_ASS | STS_PSS;
-	if (handshake_on_error_set_halt(ehci, &ehci->regs->status,
-					STS_ASS | STS_PSS, temp, 16 * 125))
-		return;
+	temp = (ehci->command << 10) & (STS_ASS | STS_PSS);
+	handshake(ehci, &ehci->regs->status,
+			STS_ASS | STS_PSS, temp, 16 * 125);
 
 	/* then disable anything that's still active */
-	temp = ehci_readl(ehci, &ehci->regs->command);
-	temp &= ~(CMD_ASE | CMD_IAAD | CMD_PSE);
-	ehci_writel(ehci, temp, &ehci->regs->command);
+	ehci->command &= ~(CMD_ASE | CMD_PSE);
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
 
 	/* hardware can take 16 microframes to turn off ... */
-	handshake_on_error_set_halt(ehci, &ehci->regs->status,
-				    STS_ASS | STS_PSS, 0, 16 * 125);
+	handshake(ehci, &ehci->regs->status,
+			STS_ASS | STS_PSS, 0, 16 * 125);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -416,9 +357,6 @@ static void ehci_iaa_watchdog(unsigned l
 		 * CMD_IAAD when it sets STS_IAA.)
 		 */
 		cmd = ehci_readl(ehci, &ehci->regs->command);
-		if (cmd & CMD_IAAD)
-			ehci_writel(ehci, cmd & ~CMD_IAAD,
-					&ehci->regs->command);
 
 		/* If IAA is set here it either legitimately triggered
 		 * before we cleared IAAD above (but _way_ late, so we'll
@@ -499,7 +437,10 @@ static void ehci_shutdown(struct usb_hcd
 
 	spin_lock_irq(&ehci->lock);
 	ehci_silence_controller(ehci);
+	ehci->enabled_hrtimer_events = 0;
 	spin_unlock_irq(&ehci->lock);
+
+	hrtimer_cancel(&ehci->hrtimer);
 }
 
 static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -566,6 +507,7 @@ static void ehci_stop (struct usb_hcd *h
 	del_timer_sync(&ehci->iaa_watchdog);
 
 	spin_lock_irq(&ehci->lock);
+	ehci->enabled_hrtimer_events = 0;
 	if (ehci->rh_state == EHCI_RH_RUNNING)
 		ehci_quiesce (ehci);
 
@@ -573,6 +515,7 @@ static void ehci_stop (struct usb_hcd *h
 	ehci_reset (ehci);
 	spin_unlock_irq(&ehci->lock);
 
+	hrtimer_cancel(&ehci->hrtimer);
 	remove_sysfs_files(ehci);
 	remove_debug_files (ehci);
 
@@ -621,6 +564,10 @@ static int ehci_init(struct usb_hcd *hcd
 	ehci->iaa_watchdog.function = ehci_iaa_watchdog;
 	ehci->iaa_watchdog.data = (unsigned long) ehci;
 
+	hrtimer_init(&ehci->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+	ehci->hrtimer.function = ehci_hrtimer_func;
+	ehci->enabled_hrtimer_events = EHCI_HRTIMER_NO_EVENT;
+
 	hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params);
 
 	/*
@@ -909,7 +856,7 @@ static irqreturn_t ehci_irq (struct usb_
 		pcd_status = status;
 
 		/* resume root hub? */
-		if (!(cmd & CMD_RUN))
+		if (ehci->rh_state == EHCI_RH_SUSPENDED)
 			usb_hcd_resume_root_hub(hcd);
 
 		/* get per-port change detect bits */
@@ -952,6 +899,8 @@ static irqreturn_t ehci_irq (struct usb_
 		dbg_status(ehci, "fatal", status);
 		ehci_halt(ehci);
 dead:
+		ehci->enabled_hrtimer_events = 0;
+		hrtimer_try_to_cancel(&ehci->hrtimer);
 		ehci_reset(ehci);
 		ehci_writel(ehci, 0, &ehci->regs->configured_flag);
 		usb_hc_died(hcd);
Index: x/drivers/usb/host/ehci-dbg.c
===================================================================
--- x.orig/drivers/usb/host/ehci-dbg.c
+++ x/drivers/usb/host/ehci-dbg.c
@@ -1032,10 +1032,8 @@ static ssize_t debug_lpm_write(struct fi
 		if (strict_strtoul(buf + 5, 16, &hird))
 			return -EINVAL;
 		printk(KERN_INFO "setting hird %s %lu\n", buf + 6, hird);
-		temp = ehci_readl(ehci, &ehci->regs->command);
-		temp &= ~CMD_HIRD;
-		temp |= hird << 24;
-		ehci_writel(ehci, temp, &ehci->regs->command);
+		ehci->command = (ehci->command & ~CMD_HIRD) | (hird << 24);
+		ehci_writel(ehci, ehci->command, &ehci->regs->command);
 	} else if (strncmp(buf, "disable", 7) == 0) {
 		if (strict_strtoul(buf + 8, 10, &port))
 			return -EINVAL;
Index: x/drivers/usb/host/ehci-hub.c
===================================================================
--- x.orig/drivers/usb/host/ehci-hub.c
+++ x/drivers/usb/host/ehci-hub.c
@@ -238,7 +238,6 @@ static int ehci_bus_suspend (struct usb_
 	/* stop schedules, clean any completed work */
 	if (ehci->rh_state == EHCI_RH_RUNNING)
 		ehci_quiesce (ehci);
-	ehci->command = ehci_readl(ehci, &ehci->regs->command);
 	ehci_work(ehci);
 
 	/* Unlike other USB host controller types, EHCI doesn't have
@@ -324,12 +323,14 @@ static int ehci_bus_suspend (struct usb_
 	ehci_readl(ehci, &ehci->regs->intr_enable);
 
 	ehci->next_statechange = jiffies + msecs_to_jiffies(10);
+	ehci->enabled_hrtimer_events = 0;
 	spin_unlock_irq (&ehci->lock);
 
 	/* ehci_work() may have re-enabled the watchdog timer, which we do not
 	 * want, and so we must delete any pending watchdog timer events.
 	 */
 	del_timer_sync(&ehci->watchdog);
+	hrtimer_cancel(&ehci->hrtimer);
 	return 0;
 }
 
@@ -379,6 +380,7 @@ static int ehci_bus_resume (struct usb_h
 	ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next);
 
 	/* restore CMD_RUN, framelist size, and irq threshold */
+	ehci->command |= CMD_RUN;
 	ehci_writel(ehci, ehci->command, &ehci->regs->command);
 	ehci->rh_state = EHCI_RH_RUNNING;
 
Index: x/drivers/usb/host/ehci-q.c
===================================================================
--- x.orig/drivers/usb/host/ehci-q.c
+++ x/drivers/usb/host/ehci-q.c
@@ -981,14 +981,12 @@ static void qh_link_async (struct ehci_h
 	head = ehci->async;
 	timer_action_done (ehci, TIMER_ASYNC_OFF);
 	if (!head->qh_next.qh) {
-		u32	cmd = ehci_readl(ehci, &ehci->regs->command);
-
-		if (!(cmd & CMD_ASE)) {
+		if (!(ehci->command & CMD_ASE)) {
 			/* in case a clear of CMD_ASE didn't take yet */
 			(void)handshake(ehci, &ehci->regs->status,
 					STS_ASS, 0, 150);
-			cmd |= CMD_ASE;
-			ehci_writel(ehci, cmd, &ehci->regs->command);
+			ehci->command |= CMD_ASE;
+			ehci_writel(ehci, ehci->command, &ehci->regs->command);
 			/* posted write need not be known to HC yet ... */
 		}
 	}
@@ -1204,7 +1202,6 @@ static void end_unlink_async (struct ehc
 
 static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-	int		cmd = ehci_readl(ehci, &ehci->regs->command);
 	struct ehci_qh	*prev;
 
 #ifdef DEBUG
@@ -1222,8 +1219,8 @@ static void start_unlink_async (struct e
 		if (ehci->rh_state != EHCI_RH_HALTED
 				&& !ehci->reclaim) {
 			/* ... and CMD_IAAD clear */
-			ehci_writel(ehci, cmd & ~CMD_ASE,
-				    &ehci->regs->command);
+			ehci->command &= ~CMD_ASE;
+			ehci_writel(ehci, ehci->command, &ehci->regs->command);
 			wmb ();
 			// handshake later, if we need to
 			timer_action_done (ehci, TIMER_ASYNC_OFF);
@@ -1253,8 +1250,7 @@ static void start_unlink_async (struct e
 		return;
 	}
 
-	cmd |= CMD_IAAD;
-	ehci_writel(ehci, cmd, &ehci->regs->command);
+	ehci_writel(ehci, ehci->command | CMD_IAAD, &ehci->regs->command);
 	(void)ehci_readl(ehci, &ehci->regs->command);
 	iaa_watchdog_start(ehci);
 }
Index: x/drivers/usb/host/ehci-sched.c
===================================================================
--- x.orig/drivers/usb/host/ehci-sched.c
+++ x/drivers/usb/host/ehci-sched.c
@@ -481,67 +481,38 @@ static int tt_no_collision (
 
 static int enable_periodic (struct ehci_hcd *ehci)
 {
-	u32	cmd;
-	int	status;
-
 	if (ehci->periodic_sched++)
 		return 0;
 
-	/* did clearing PSE did take effect yet?
-	 * takes effect only at frame boundaries...
-	 */
-	status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
-					     STS_PSS, 0, 9 * 125);
-	if (status) {
-		usb_hc_died(ehci_to_hcd(ehci));
-		return status;
-	}
-
-	cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE;
-	ehci_writel(ehci, cmd, &ehci->regs->command);
-	/* posted write ... PSS happens later */
+	/* If we're waiting to turn off the periodic schedule, stop waiting */
+	if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_DISABLE_PERIODIC))
+		ehci->enabled_hrtimer_events &=
+				~BIT(EHCI_HRTIMER_DISABLE_PERIODIC);
+
+	/* Otherwise, don't start the schedule until PSS is known to be 0 */
+	else
+		ehci_poll_PSS(ehci, NULL);
 
 	/* make sure ehci_work scans these */
 	ehci->next_uframe = ehci_read_frame_index(ehci)
 		% (ehci->periodic_size << 3);
-	if (unlikely(ehci->broken_periodic))
-		ehci->last_periodic_enable = ktime_get_real();
 	return 0;
 }
 
 static int disable_periodic (struct ehci_hcd *ehci)
 {
-	u32	cmd;
-	int	status;
-
 	if (--ehci->periodic_sched)
 		return 0;
 
-	if (unlikely(ehci->broken_periodic)) {
-		/* delay experimentally determined */
-		ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000);
-		ktime_t now = ktime_get_real();
-		s64 delay = ktime_us_delta(safe, now);
-
-		if (unlikely(delay > 0))
-			udelay(delay);
-	}
-
-	/* did setting PSE not take effect yet?
-	 * takes effect only at frame boundaries...
-	 */
-	status = handshake_on_error_set_halt(ehci, &ehci->regs->status,
-					     STS_PSS, STS_PSS, 9 * 125);
-	if (status) {
-		usb_hc_died(ehci_to_hcd(ehci));
-		return status;
-	}
-
-	cmd = ehci_readl(ehci, &ehci->regs->command) & ~CMD_PSE;
-	ehci_writel(ehci, cmd, &ehci->regs->command);
-	/* posted write ... */
-
-	free_cached_lists(ehci);
+	/* If we're waiting to start the periodic schedule, stop waiting */
+	if (ehci->enabled_hrtimer_events & BIT(EHCI_HRTIMER_POLL_PSS))
+		ehci->enabled_hrtimer_events &=
+				~BIT(EHCI_HRTIMER_POLL_PSS);
+
+	/* Otherwise wait for a while before turning the schedule off */
+	else
+		ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC,
+				&ehci->periodic_event_time, true);
 
 	ehci->next_uframe = -1;
 	return 0;
Index: x/drivers/usb/host/ehci.h
===================================================================
--- x.orig/drivers/usb/host/ehci.h
+++ x/drivers/usb/host/ehci.h
@@ -68,7 +68,24 @@ enum ehci_rh_state {
 	EHCI_RH_RUNNING
 };
 
+/*
+ * Timer events ordered by delay length.
+ * Always update ehci_event_delays_ns[] in parallel with this list.
+ */
+enum ehci_hrtimer_event {
+	EHCI_HRTIMER_POLL_PSS,		/* Poll for periodic schedule off */
+	EHCI_HRTIMER_DISABLE_PERIODIC,	/* Wait to disable periodic sched */
+	EHCI_HRTIMER_NO_EVENT		/* Must come last */
+};
+
 struct ehci_hcd {			/* one per controller */
+	enum ehci_hrtimer_event	next_hrtimer_event;
+	unsigned		enabled_hrtimer_events;
+	struct hrtimer		hrtimer;
+
+	ktime_t			periodic_event_time;
+	ktime_t			PSS_giveup_time;
+
 	/* glue to PCI and HCD framework */
 	struct ehci_caps __iomem *caps;
 	struct ehci_regs __iomem *regs;
@@ -141,7 +158,6 @@ struct ehci_hcd {			/* one per controlle
 	unsigned		big_endian_capbase:1;
 	unsigned		has_amcc_usb23:1;
 	unsigned		need_io_watchdog:1;
-	unsigned		broken_periodic:1;
 	unsigned		amd_pll_fix:1;
 	unsigned		fs_i_thresh:1;	/* Intel iso scheduling */
 	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
Index: x/drivers/usb/host/ehci-timer.c
===================================================================
--- /dev/null
+++ x/drivers/usb/host/ehci-timer.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2012 by Alan Stern
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* This file is part of ehci-hcd.c */
+
+/*-------------------------------------------------------------------------*/
+
+/* Set a bit in the USBCMD register */
+static void ehci_set_command_bit(struct ehci_hcd *ehci, u32 bit)
+{
+	ehci->command |= bit;
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+	/* unblock posted write */
+	ehci_readl(ehci, &ehci->regs->command);
+}
+
+/* Clear a bit in the USBCMD register */
+static void ehci_clear_command_bit(struct ehci_hcd *ehci, u32 bit)
+{
+	ehci->command &= ~bit;
+	ehci_writel(ehci, ehci->command, &ehci->regs->command);
+
+	/* unblock posted write */
+	ehci_readl(ehci, &ehci->regs->command);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * EHCI timer support...  Now using hrtimers.
+ *
+ * Lots of different events are triggered from ehci->hrtimer.  Whenever
+ * the timer routine runs, it checks each possible event; events that are
+ * currently enabled and whose expiration time has passed get handled.
+ * The set of enabled events is stored as a collection of bitflags in
+ * ehci->enabled_hrtimer_events, and they are numbered in order of
+ * increasing delay values (ranging between 1 ms and 100 ms).
+ *
+ * Rather than implementing a sorted list or tree of all pending events,
+ * we keep track only of the lowest-numbered pending event, in
+ * ehci->next_hrtimer_event.  Whenever ehci->hrtimer gets restarted, its
+ * expiration time is set to the timeout value for this event.
+ *
+ * As a result, events might not get handled right away; the actual delay
+ * could be anywhere up to twice the requested delay.  This doesn't
+ * matter, because none of the events are especially time-critical.  The
+ * ones that matter most all have a delay of 1 ms, so they will be
+ * handled after 2 ms at most, which is okay.  In addition to this, we
+ * allow for an expiration range of 1/2 ms.
+ */
+
+/*
+ * Delay lengths for the hrtimer event types.
+ * Keep this sorted by delay lengths, and in the same order as
+ * the events types indexed by enum ehci_hrtimer_event in ehci.h.
+ */
+static unsigned ehci_event_delays_ns[] = {
+	1 * NSEC_PER_MSEC,	/* EHCI_HRTIMER_POLL_PSS */
+	10 * NSEC_PER_MSEC,	/* EHCI_DISABLE_PERIODIC */
+};
+
+/* Enable a pending hrtimer event */
+static void ehci_enable_event(struct ehci_hcd *ehci, unsigned event,
+		ktime_t *timeout, bool resched)
+{
+	if (resched)
+		*timeout = ktime_add(ktime_get(),
+				ktime_set(0, ehci_event_delays_ns[event]));
+	ehci->enabled_hrtimer_events |= (1 << event);
+
+	/* Track only the lowest-numbered event */
+	if (event < ehci->next_hrtimer_event) {
+		ehci->next_hrtimer_event = event;
+		hrtimer_start_range_ns(&ehci->hrtimer, *timeout,
+				NSEC_PER_MSEC / 2, HRTIMER_MODE_ABS);
+	}
+}
+
+
+/* Poll the STS_PSS status bit */
+static void ehci_poll_PSS(struct ehci_hcd *ehci, ktime_t *now)
+{
+	u32	status;
+
+	/* Don't enable anything if the controller isn't running (e.g., died) */
+	if (!(ehci->command & CMD_RUN))
+		return;
+
+	/*
+	 * When the periodic schedule stops, restart it.  If it takes
+	 * too long to stop then give up and restart it anyway, otherwise
+	 * continue polling.
+	 */
+	status = ehci_readl(ehci, &ehci->regs->status);
+	if (unlikely(now && (status & STS_PSS))) {
+		if (now->tv64 >= ehci->PSS_giveup_time.tv64) {
+			ehci_warn(ehci, "Waited too long for the periodic schedule to stop, giving up\n");
+			status = 0;
+		}
+	}
+
+	if (!(status & STS_PSS))
+		ehci_set_command_bit(ehci, CMD_PSE);
+
+	else		/* Poll again later */
+		ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS,
+				&ehci->periodic_event_time, true);
+}
+
+/* Disable the periodic schedule */
+static void ehci_turn_off_PSE(struct ehci_hcd *ehci)
+{
+	ehci_clear_command_bit(ehci, CMD_PSE);
+	free_cached_lists(ehci);
+
+	/* Allow up to 20 ms for the schedule to actually stop */
+	ehci->PSS_giveup_time = ktime_add(ktime_get(),
+			ktime_set(0, 20 * NSEC_PER_MSEC));
+}
+
+
+static enum hrtimer_restart ehci_hrtimer_func(struct hrtimer *t)
+{
+	struct ehci_hcd	*ehci = container_of(t, struct ehci_hcd, hrtimer);
+	ktime_t		now = ktime_get();
+	unsigned	events;
+	unsigned long	flags;
+
+	spin_lock_irqsave(&ehci->lock, flags);
+
+	events = ehci->enabled_hrtimer_events;
+	ehci->enabled_hrtimer_events = 0;
+	ehci->next_hrtimer_event = EHCI_HRTIMER_NO_EVENT;
+
+	/*
+	 * Check each pending event.  If its time has expired, handle
+	 * the event; otherwise re-enable it.
+	 */
+	if (events & BIT(EHCI_HRTIMER_POLL_PSS)) {
+		if (now.tv64 >= ehci->periodic_event_time.tv64)
+			ehci_poll_PSS(ehci, &now);
+		else
+			ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS,
+					&ehci->periodic_event_time, false);
+	}
+
+	if (events & BIT(EHCI_HRTIMER_DISABLE_PERIODIC)) {
+		if (now.tv64 >= ehci->periodic_event_time.tv64)
+			ehci_turn_off_PSE(ehci);
+		else
+			ehci_enable_event(ehci, EHCI_HRTIMER_DISABLE_PERIODIC,
+					&ehci->periodic_event_time, false);
+	}
+
+	spin_unlock_irqrestore(&ehci->lock, flags);
+	return HRTIMER_NORESTART;
+}
Index: x/drivers/usb/host/ehci-pci.c
===================================================================
--- x.orig/drivers/usb/host/ehci-pci.c
+++ x/drivers/usb/host/ehci-pci.c
@@ -131,10 +131,6 @@ static int ehci_pci_setup(struct usb_hcd
 	case PCI_VENDOR_ID_INTEL:
 		ehci->need_io_watchdog = 0;
 		ehci->fs_i_thresh = 1;
-		if (pdev->device == 0x27cc) {
-			ehci->broken_periodic = 1;
-			ehci_info(ehci, "using broken periodic workaround\n");
-		}
 		if (pdev->device == 0x0806 || pdev->device == 0x0811
 				|| pdev->device == 0x0829) {
 			ehci_info(ehci, "disable lpm for langwell/penwell\n");

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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux