aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-hcd.c
diff options
context:
space:
mode:
authorStuart_Hayes@Dell.com <Stuart_Hayes@Dell.com>2007-05-03 11:58:49 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:29:45 -0400
commit196705c9bbc03540429b0f7cf9ee35c2f928a534 (patch)
tree37ddc23737bced60a8defc52f643f362e2744908 /drivers/usb/host/ehci-hcd.c
parentec22559e0b7a05283a3413bda5d177e42c950e23 (diff)
USB: EHCI cpufreq fix
EHCI controllers that don't cache enough microframes can get MMF errors when CPU frequency changes occur between the start and completion of split interrupt transactions, due to delays in reading main memory (caused by CPU cache snoop delays). This patch adds a cpufreq notifier to the EHCI driver that will inactivate split interrupt transactions during frequency transitions. It was tested on Intel ICH7 and Serverworks/Broadcom HT1000 EHCI controllers. Signed-off-by: Stuart Hayes <stuart_hayes@dell.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/ehci-hcd.c')
-rw-r--r--drivers/usb/host/ehci-hcd.c67
1 files changed, 67 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 099aff64f536..566badb05b34 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -273,6 +273,58 @@ static void ehci_work(struct ehci_hcd *ehci);
273 273
274/*-------------------------------------------------------------------------*/ 274/*-------------------------------------------------------------------------*/
275 275
276#ifdef CONFIG_CPU_FREQ
277
278#include <linux/cpufreq.h>
279
280static void ehci_cpufreq_pause (struct ehci_hcd *ehci)
281{
282 unsigned long flags;
283
284 spin_lock_irqsave(&ehci->lock, flags);
285 if (!ehci->cpufreq_changing++)
286 qh_inactivate_split_intr_qhs(ehci);
287 spin_unlock_irqrestore(&ehci->lock, flags);
288}
289
290static void ehci_cpufreq_unpause (struct ehci_hcd *ehci)
291{
292 unsigned long flags;
293
294 spin_lock_irqsave(&ehci->lock, flags);
295 if (!--ehci->cpufreq_changing)
296 qh_reactivate_split_intr_qhs(ehci);
297 spin_unlock_irqrestore(&ehci->lock, flags);
298}
299
300/*
301 * ehci_cpufreq_notifier is needed to avoid MMF errors that occur when
302 * EHCI controllers that don't cache many uframes get delayed trying to
303 * read main memory during CPU frequency transitions. This can cause
304 * split interrupt transactions to not be completed in the required uframe.
305 * This has been observed on the Broadcom/ServerWorks HT1000 controller.
306 */
307static int ehci_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
308 void *data)
309{
310 struct ehci_hcd *ehci = container_of(nb, struct ehci_hcd,
311 cpufreq_transition);
312
313 switch (val) {
314 case CPUFREQ_PRECHANGE:
315 ehci_cpufreq_pause(ehci);
316 break;
317 case CPUFREQ_POSTCHANGE:
318 ehci_cpufreq_unpause(ehci);
319 break;
320 }
321 return 0;
322}
323
324#endif
325
326/*-------------------------------------------------------------------------*/
327
276static void ehci_watchdog (unsigned long param) 328static void ehci_watchdog (unsigned long param)
277{ 329{
278 struct ehci_hcd *ehci = (struct ehci_hcd *) param; 330 struct ehci_hcd *ehci = (struct ehci_hcd *) param;
@@ -404,6 +456,10 @@ static void ehci_stop (struct usb_hcd *hcd)
404 ehci_writel(ehci, 0, &ehci->regs->intr_enable); 456 ehci_writel(ehci, 0, &ehci->regs->intr_enable);
405 spin_unlock_irq(&ehci->lock); 457 spin_unlock_irq(&ehci->lock);
406 458
459#ifdef CONFIG_CPU_FREQ
460 cpufreq_unregister_notifier(&ehci->cpufreq_transition,
461 CPUFREQ_TRANSITION_NOTIFIER);
462#endif
407 /* let companion controllers work when we aren't */ 463 /* let companion controllers work when we aren't */
408 ehci_writel(ehci, 0, &ehci->regs->configured_flag); 464 ehci_writel(ehci, 0, &ehci->regs->configured_flag);
409 465
@@ -509,6 +565,17 @@ static int ehci_init(struct usb_hcd *hcd)
509 } 565 }
510 ehci->command = temp; 566 ehci->command = temp;
511 567
568#ifdef CONFIG_CPU_FREQ
569 INIT_LIST_HEAD(&ehci->split_intr_qhs);
570 /*
571 * If the EHCI controller caches enough uframes, this probably
572 * isn't needed unless there are so many low/full speed devices
573 * that the controller's can't cache it all.
574 */
575 ehci->cpufreq_transition.notifier_call = ehci_cpufreq_notifier;
576 cpufreq_register_notifier(&ehci->cpufreq_transition,
577 CPUFREQ_TRANSITION_NOTIFIER);
578#endif
512 return 0; 579 return 0;
513} 580}
514 581