📄 ohci-hcd.c
字号:
/* this timeout is arbitrary. we make it long, so systems * depending on usb keyboards may be usable even if the * BIOS/SMM code seems pretty broken. */ temp = 500; /* arbitrary: five seconds */ ohci_write_intrenable (ohci, OHCI_INTR_OC); ohci_write_cmdstatus (ohci, OHCI_OCR); while (ohci_read_control (ohci) & OHCI_CTRL_IR) { msleep (10); if (--temp == 0) { ohci_err (ohci, "USB HC TakeOver failed!\n"); return -1; } } }#endif /* Disable HC interrupts */ ohci_write_intrdisable (ohci, OHCI_INTR_MIE); ohci_dbg (ohci, "reset, control = 0x%x\n", ohci_read_control (ohci)); /* Reset USB (needed by some controllers); RemoteWakeupConnected * saved if boot firmware (BIOS/SMM/...) told us it's connected * (for OHCI integrated on mainboard, it normally is) */ ohci->hc_control = ohci_read_control (ohci); ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ if (ohci->hc_control) ohci->hcd.can_wakeup = 1; ohci_write_control (ohci, ohci->hc_control); if (power_switching) { unsigned ports = roothub_a (ohci) & RH_A_NDP; /* power down each port */ for (temp = 0; temp < ports; temp++) ohci_write_roothub_portstatus(ohci, temp, RH_PS_LSDA); } // flush those pci writes (void) ohci_read_control (ohci); msleep (50); /* HC Reset requires max 10 us delay */ ohci_write_cmdstatus (ohci, OHCI_HCR); temp = 30; /* ... allow extra time */ while ((ohci_read_cmdstatus (ohci) & OHCI_HCR) != 0) { if (--temp == 0) { ohci_err (ohci, "USB HC reset timed out!\n"); return -1; } udelay (1); } /* now we're in the SUSPEND state ... must go OPERATIONAL * within 2msec else HC enters RESUME * * ... but some hardware won't init fmInterval "by the book" * (SiS, OPTi ...), so reset again instead. SiS doesn't need * this if we write fmInterval after we're OPERATIONAL. */ ohci_write_control (ohci, ohci->hc_control); // flush those pci writes (void) ohci_read_control (ohci); return 0;}/*-------------------------------------------------------------------------*//* Start an OHCI controller, set the BUS operational * enable interrupts * connect the virtual root hub */static int hc_start (struct ohci_hcd *ohci){ u32 mask, tmp; struct usb_device *udev; struct usb_bus *bus; disable (ohci); /* Tell the controller where the control and bulk lists are * The lists are empty now. */ ohci_write_controlhead (ohci, 0); ohci_write_bulkhead (ohci, 0); /* a reset clears this */ writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); periodic_reinit (ohci); /* some OHCI implementations are finicky about how they init. * bogus values here mean not even enumeration could work. */ if ((ohci_readl (&ohci->regs->fminterval) & 0x3fff0000) == 0 || !ohci_readl (&ohci->regs->periodicstart)) { ohci_err (ohci, "init err\n"); return -EOVERFLOW; } /* start controller operations */ ohci->hc_control &= OHCI_CTRL_RWC; ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; ohci_write_control(ohci, ohci->hc_control); ohci->hcd.state = USB_STATE_RUNNING; /* wake on ConnectStatusChange, matching external hubs */ ohci_write_roothub_status (ohci, RH_HS_DRWE); /* Choose the interrupts we care about now, others later on demand */ mask = OHCI_INTR_INIT; ohci_write_intrstatus (ohci, mask); ohci_write_intrenable (ohci, mask); /* handle root hub init quirks ... */ tmp = roothub_a (ohci); tmp &= ~(RH_A_PSM | RH_A_OCPM); if (ohci->flags & OHCI_QUIRK_SUPERIO) { /* NSC 87560 and maybe others */ tmp |= RH_A_NOCP; tmp &= ~(RH_A_POTPGT | RH_A_NPS); } else if (power_switching) { /* act like most external hubs: use per-port power * switching and overcurrent reporting. */ tmp &= ~(RH_A_NPS | RH_A_NOCP); tmp |= RH_A_PSM | RH_A_OCPM; } else { /* hub power always on; required for AMD-756 and some * Mac platforms. ganged overcurrent reporting, if any. */ tmp |= RH_A_NPS; } ohci_write_roothub_a (ohci, tmp); ohci_write_roothub_status (ohci, RH_HS_LPSC); ohci_write_roothub_b (ohci, power_switching ? RH_B_PPCM : 0); // flush those pci writes (void) ohci_read_control (ohci); // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((roothub_a (ohci) >> 23) & 0x1fe); bus = hcd_to_bus (&ohci->hcd); if (bus->root_hub) { ohci->hcd.state = USB_STATE_RUNNING; return 0; } /* connect the virtual root hub */ udev = usb_alloc_dev (NULL, bus, 0); ohci->hcd.state = USB_STATE_RUNNING; if (!udev) { disable (ohci); ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci_write_control (ohci, ohci->hc_control); return -ENOMEM; } udev->speed = USB_SPEED_FULL; if (hcd_register_root (udev, &ohci->hcd) != 0) { usb_put_dev (udev); disable (ohci); ohci->hc_control &= ~OHCI_CTRL_HCFS; ohci_write_control (ohci, ohci->hc_control); return -ENODEV; } return 0;}/*-------------------------------------------------------------------------*//* an interrupt happens */static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs){ struct ohci_hcd *ohci = hcd_to_ohci (hcd); struct ohci_regs *regs = ohci->regs; int ints; /* we can eliminate a (slow) ohci_readl() if _only_ WDH caused this irq */ if ((ohci->hcca->done_head != 0) && ! (le32_to_cpup (&ohci->hcca->done_head) & 0x01)) { ints = OHCI_INTR_WDH; /* cardbus/... hardware gone before remove() */ } else if ((ints = ohci_read_intrstatus (ohci)) == ~(u32)0) { disable (ohci); ohci_dbg (ohci, "device removed!\n"); return IRQ_HANDLED; /* interrupt for some other device? */ } else if ((ints &= ohci_read_intrenable (ohci)) == 0) { return IRQ_NONE; } if (ints & OHCI_INTR_UE) { disable (ohci); ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); // e.g. due to PCI Master/Target Abort ohci_dump (ohci, 1); hc_reset (ohci); } if (ints & OHCI_INTR_RD) { ohci_vdbg (ohci, "resume detect\n"); schedule_work(&ohci->rh_resume); } if (ints & OHCI_INTR_WDH) { if (HCD_IS_RUNNING(hcd->state)) ohci_write_intrdisable (ohci, OHCI_INTR_WDH); spin_lock (&ohci->lock); dl_done_list (ohci, ptregs); spin_unlock (&ohci->lock); if (HCD_IS_RUNNING(hcd->state)) ohci_write_intrenable (ohci, OHCI_INTR_WDH); } /* could track INTR_SO to reduce available PCI/... bandwidth */ /* handle any pending URB/ED unlinks, leaving INTR_SF enabled * when there's still unlinking to be done (next frame). */ spin_lock (&ohci->lock); if (ohci->ed_rm_list) finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca), ptregs); if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list && HCD_IS_RUNNING(ohci->hcd.state)) ohci_write_intrdisable (ohci, OHCI_INTR_SF); spin_unlock (&ohci->lock); if (HCD_IS_RUNNING(ohci->hcd.state)) { ohci_write_intrstatus (ohci, ints); ohci_write_intrenable (ohci, OHCI_INTR_MIE); // flush those pci writes (void) ohci_read_control (ohci); } return IRQ_HANDLED;}/*-------------------------------------------------------------------------*/static void ohci_stop (struct usb_hcd *hcd){ struct ohci_hcd *ohci = hcd_to_ohci (hcd); ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n", hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), ohci->hcd.state); ohci_dump (ohci, 1); flush_scheduled_work(); if (HCD_IS_RUNNING(ohci->hcd.state)) hc_reset (ohci); else ohci_write_intrdisable (ohci, OHCI_INTR_MIE); remove_debug_files (ohci); ohci_mem_cleanup (ohci); if (ohci->hcca) { dma_free_coherent (ohci->hcd.self.controller, sizeof *ohci->hcca, ohci->hcca, ohci->hcca_dma); ohci->hcca = NULL; ohci->hcca_dma = 0; }}/*-------------------------------------------------------------------------*//* must not be called from interrupt context */#ifdef CONFIG_PMstatic void mark_children_gone (struct usb_device *dev){ unsigned i; for (i = 0; i < dev->maxchild; i++) { if (dev->children [i] == 0) continue; dev->children [i]->state = USB_STATE_NOTATTACHED; mark_children_gone (dev->children [i]); }}static int hc_restart (struct ohci_hcd *ohci){ int temp; int i; struct urb_priv *priv; /* mark any devices gone, so they do nothing till khubd disconnects. * recycle any "live" eds/tds (and urbs) right away. * later, khubd disconnect processing will recycle the other state, * (either as disconnect/reconnect, or maybe someday as a reset). */ spin_lock_irq(&ohci->lock); disable (ohci); mark_children_gone (ohci->hcd.self.root_hub); if (!list_empty (&ohci->pending)) ohci_dbg(ohci, "abort schedule...\n"); list_for_each_entry (priv, &ohci->pending, pending) { struct urb *urb = priv->td[0]->urb; struct ed *ed = priv->ed; switch (ed->state) { case ED_OPER: ed->state = ED_UNLINK; ed->hwINFO |= ED_DEQUEUE; ed_deschedule (ohci, ed); ed->ed_next = ohci->ed_rm_list; ed->ed_prev = NULL; ohci->ed_rm_list = ed; /* FALLTHROUGH */ case ED_UNLINK: break; default: ohci_dbg(ohci, "bogus ed %p state %d\n", ed, ed->state); } spin_lock (&urb->lock); urb->status = -ESHUTDOWN; spin_unlock (&urb->lock); } finish_unlinks (ohci, 0, NULL); spin_unlock_irq(&ohci->lock); /* paranoia, in case that didn't work: */ /* empty the interrupt branches */ for (i = 0; i < NUM_INTS; i++) ohci->load [i] = 0; for (i = 0; i < NUM_INTS; i++) ohci->hcca->int_table [i] = 0; /* no EDs to remove */ ohci->ed_rm_list = NULL; /* empty control and bulk lists */ ohci->ed_controltail = NULL; ohci->ed_bulktail = NULL; if ((temp = hc_reset (ohci)) < 0 || (temp = hc_start (ohci)) < 0) { ohci_err (ohci, "can't restart, %d\n", temp); return temp; } else { /* here we "know" root ports should always stay powered, * and that if we try to turn them back on the root hub * will respond to CSC processing. */ i = roothub_a (ohci) & RH_A_NDP; while (i--) ohci_write_roothub_portstatus (ohci, temp, RH_PS_PSS); ohci->hcd.self.root_hub->dev.power.power_state = 0; ohci->hcd.state = USB_STATE_RUNNING; ohci_dbg (ohci, "restart complete\n"); ohci_dump (ohci, 1); } return 0;}#endif/*-------------------------------------------------------------------------*/#define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESCMODULE_AUTHOR (DRIVER_AUTHOR);MODULE_DESCRIPTION (DRIVER_INFO);MODULE_LICENSE ("GPL");#ifdef CONFIG_PCI#include "ohci-pci.c"#endif#ifdef CONFIG_SA1111//#include "ohci-sa1111.c" //it's default in FS_PXA255 config#endif#ifdef CONFIG_ARCH_OMAP#include "ohci-omap.c"#endif#ifdef CONFIG_ARCH_LH7A404#include "ohci-lh7a404.c"#endif#ifdef CONFIG_ARCH_S3C2410#include "ohci-s3c2410.c"#endif#ifdef CONFIG_USB_OHCI_SL811#include "ohci-sl811.c"#endif#ifdef CONFIG_USB_OHCI_ISP1362#include "ohci-isp1362.c"#endif#if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ || defined(CONFIG_ARCH_OMAP) \ || defined(CONFIG_ARCH_LH7A404) \ || defined(CONFIG_ARCH_S3C2410) \ || defined(CONFIG_USB_OHCI_SL811) \ || defined(CONFIG_USB_OHCI_ISP1362) \ )#error "missing bus glue for ohci-hcd"#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -