📄 ehci-hcd-fusbh200.c
字号:
/* EHCI 0.96 (and later) section 5.1 says how to kick BIOS/SMM/...
* off the controller (maybe it can boot from highspeed USB disks).
*/
static int bios_handoff (struct ehci_hcd *ehci, int where, u32 cap)
{
if (cap & (1 << 16)) {
int msec = 500;
/* request handoff to OS */
cap &= 1 << 24;
pci_write_config_dword (ehci->hcd.pdev, where, cap);
/* and wait a while for it to happen */
do {
wait_ms (10);
msec -= 10;
pci_read_config_dword (ehci->hcd.pdev, where, &cap);
} while ((cap & (1 << 16)) && msec);
if (cap & (1 << 16)) {
ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
where, cap);
return 1;
}
ehci_dbg (ehci, "BIOS handoff succeeded\n");
}
return 0;
}
/* called by khubd or root hub init threads */
static int ehci_start (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 temp;
struct usb_device *udev;
struct usb_bus *bus;
int retval;
u32 hcc_params;
u8 tempbyte=0;
//Add Einsn@VIA
ehci_FEHCI_Init();
//End Add
spin_lock_init (&ehci->lock);
ehci->caps = (struct ehci_caps *) hcd->regs;
ehci->regs = (struct ehci_regs *) (hcd->regs + ehci->caps->length);
dbg_hcs_params (ehci, "ehci_start");
dbg_hcc_params (ehci, "ehci_start");
hcc_params = readl (&ehci->caps->hcc_params);
/* EHCI 0.96 and later may have "extended capabilities" */
temp = HCC_EXT_CAPS (hcc_params);
while (temp) {
u32 cap;
pci_read_config_dword (ehci->hcd.pdev, temp, &cap);
ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
switch (cap & 0xff) {
case 1: /* BIOS/SMM/... handoff */
if (bios_handoff (ehci, temp, cap) != 0)
return -EOPNOTSUPP;
break;
case 0: /* illegal reserved capability */
ehci_warn (ehci, "illegal capability!\n");
cap = 0;
/* FALLTHROUGH */
default: /* unknown */
break;
}
temp = (cap >> 8) & 0xff;
}
/* cache this readonly data; minimize PCI reads */
ehci->hcs_params = readl (&ehci->caps->hcs_params);
/* force HC to halt state */
if ((retval = ehci_halt (ehci)) != 0)
return retval;
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
*/
ehci->periodic_size = DEFAULT_I_TDPS;
if ((retval = ehci_mem_init (ehci, SLAB_KERNEL)) < 0)
return retval;
/* controllers may cache some of the periodic schedule ... */
//Add Einsn@VIA
#if 1 ehci->i_thresh = 8;#else
if (HCC_ISOC_CACHE (hcc_params)) // full frame cache
ehci->i_thresh = 8;
else // N microframes cached
ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
#endif
//End Add
ehci->reclaim = 0;
ehci->next_uframe = -1;
/* controller state: unknown --> reset */
/* EHCI spec section 4.1 */
if ((retval = ehci_reset (ehci)) != 0) {
ehci_mem_cleanup (ehci);
return retval;
}
writel (INTR_MASK, &ehci->regs->intr_enable);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
/*
* dedicate a qh for the async ring head, since we couldn't unlink
* a 'real' qh without stopping the async schedule [4.8]. use it
* as the 'reclamation list head' too.
* its dummy is used in hw_alt_next of many tds, to prevent the qh
* from automatically advancing to the next td after short reads.
*/
ehci->async->qh_next.qh = 0;
ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
ehci->async->hw_qtd_next = EHCI_LIST_END;
ehci->async->qh_state = QH_STATE_LINKED;
ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
/*
* hcc_params controls whether ehci->regs->segment must (!!!)
* be used; it constrains QH/ITD/SITD and QTD locations.
* pci_pool consistent memory always uses segment zero.
* streaming mappings for I/O buffers, like pci_map_single(),
* can return segments above 4GB, if the device allows.
*
* NOTE: the dma mask is visible through dma_supported(), so
* drivers can pass this info along ... like NETIF_F_HIGHDMA,
* Scsi_Host.highmem_io, and so forth. It's readonly to all
* host side drivers though.
*/
//Add Einsn@VIA
#if 0
if (HCC_64BIT_ADDR (hcc_params)) {
writel (0, &ehci->regs->segment);
if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL))
ehci_info (ehci, "enabled 64bit PCI DMA\n");
}
#endif
/* help hc dma work well with cachelines */
pci_set_mwi (ehci->hcd.pdev);
/* clear interrupt enables, set irq latency */
temp = readl (&ehci->regs->command) & 0xff;
if (log2_irq_thresh < 0 || log2_irq_thresh > 6)
log2_irq_thresh = 0;
temp |= 1 << (16 + log2_irq_thresh);
// if hc can park (ehci >= 0.96), default is 3 packets per async QH
if (HCC_PGM_FRAMELISTLEN (hcc_params)) {
/* periodic schedule size can be smaller than default */
temp &= ~(3 << 2);
temp |= (EHCI_TUNE_FLS << 2);
switch (EHCI_TUNE_FLS) {
case 0: ehci->periodic_size = 1024; break;
case 1: ehci->periodic_size = 512; break;
case 2: ehci->periodic_size = 256; break;
default: BUG ();
}
}
temp &= ~(CMD_IAAD | CMD_ASE | CMD_PSE),
// Philips, Intel, and maybe others need CMD_RUN before the
// root hub will detect new devices (why?); NEC doesn't
//Add Einsn@VIA
#if 0
temp |= CMD_RUN;
#endif
writel (temp, &ehci->regs->command);
dbg_cmd (ehci, "init", temp);
/* set async sleep time = 10 us ... ? */
init_timer (&ehci->watchdog);
ehci->watchdog.function = ehci_watchdog;
ehci->watchdog.data = (unsigned long) ehci;
/* wire up the root hub */
bus = hcd_to_bus (hcd);
bus->root_hub = udev = usb_alloc_dev (NULL, bus);
if (!udev) {
done2:
ehci_mem_cleanup (ehci);
return -ENOMEM;
}
/*
* Start, enabling full USB 2.0 functionality ... usb 1.1 devices
* are explicitly handed to companion controller(s), so no TT is
* involved with the root hub.
*/
ehci->hcd.state = USB_STATE_READY;
//Add Einsn@VIA
#if 0
writel (FLAG_CF, &ehci->regs->configured_flag);
#endif
readl (&ehci->regs->command); /* unblock posted write */
/* PCI Serial Bus Release Number is at 0x60 offset */
//Einsn@VIA pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
temp = readw (&ehci->caps->hci_version);
ehci_info (ehci,
"USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
temp >> 8, temp & 0xff, DRIVER_VERSION);
/*
* From here on, khubd concurrently accesses the root
* hub; drivers will be talking to enumerated devices.
*
* Before this point the HC was idle/ready. After, khubd
* and device drivers may start it running.
*/
usb_connect (udev);
udev->speed = USB_SPEED_HIGH;
if (hcd_register_root (hcd) != 0) {
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
ehci_reset (ehci);
bus->root_hub = 0;
retval = -ENODEV;
goto done2;
}
create_debug_files (ehci);
return 0;
}
/* always called by thread; normally rmmod */
static void ehci_stop (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
ehci_dbg (ehci, "stop\n");
/* no more interrupts ... */
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
if (in_interrupt ()) { /* must not happen!! */
ehci_err (ehci, "stopped in_interrupt!\n");
return;
}
del_timer_sync (&ehci->watchdog);
ehci_reset (ehci);
/* let companion controllers work when we aren't */
//Add Einsn@VIA //writel (0, &ehci->regs->configured_flag);
//End Add
remove_debug_files (ehci);
/* root hub is shut down separately (first, when possible) */
spin_lock_irq (&ehci->lock);
ehci_work (ehci, NULL);
spin_unlock_irq (&ehci->lock);
ehci_mem_cleanup (ehci);
#ifdef EHCI_STATS
ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld\n",
ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
ehci_dbg (ehci, "complete %ld unlink %ld\n",
ehci->stats.complete, ehci->stats.unlink);
#endif
dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
}
static int ehci_get_frame (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
return (readl (&ehci->regs->frame_index) >> 3) % ehci->periodic_size;
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* suspend/resume, section 4.3 */
static int ehci_suspend (struct usb_hcd *hcd, u32 state)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports;
int i;
dbg ("%s: suspend to %d", hcd_to_bus (hcd)->bus_name, state);
ports = HCS_N_PORTS (ehci->hcs_params);
// FIXME: This assumes what's probably a D3 level suspend...
// FIXME: usb wakeup events on this bus should resume the machine.
// pci config register PORTWAKECAP controls which ports can do it;
// bios may have initted the register...
/* suspend each port, then stop the hc */
for (i = 0; i < ports; i++) {
int temp = readl (&ehci->regs->port_status [i]);
#if 1
if (((temp & PORT_PE) == 0))
continue;
#else
if ((temp & PORT_PE) == 0
|| (temp & PORT_OWNER) != 0)
continue;
#endif
dbg ("%s: suspend port %d", hcd_to_bus (hcd)->bus_name, i);
//Einsn@VIA temp |= PORT_SUSPEND;
//Einsn@VIA writel (temp, &ehci->regs->port_status [i]);
}
if (hcd->state == USB_STATE_RUNNING)
ehci_ready (ehci);
writel (readl (&ehci->regs->command) & ~CMD_RUN, &ehci->regs->command);
// save pci FLADJ value
/* who tells PCI to reduce power consumption? */
return 0;
}
static int ehci_resume (struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports;
int i;
dbg ("%s: resume", hcd_to_bus (hcd)->bus_name);
ports = HCS_N_PORTS (ehci->hcs_params);
// FIXME: if controller didn't retain state,
// return and let generic code clean it up
// test configured_flag ?
/* resume HC and each port */
// restore pci FLADJ value
// khubd and drivers will set HC running, if needed;
hcd->state = USB_STATE_READY;
// FIXME Philips/Intel/... etc don't really have a "READY"
// state ... turn on CMD_RUN too
for (i = 0; i < ports; i++) {
int temp = readl (&ehci->regs->port_status [i]);
#if 1
if (((temp & PORT_PE) == 0))
continue;
#else
if ((temp & PORT_PE) == 0
|| (temp & PORT_SUSPEND) != 0)
continue;
#endif
//Add Einsn@VIA
if ((temp & PORT_SUSPEND)>0){
dbg ("%s: resume port %d", hcd_to_bus (hcd)->bus_name, i);
temp |= PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
readl (&ehci->regs->command); /* unblock posted writes */
wait_ms (20);
temp &= ~PORT_RESUME;
writel (temp, &ehci->regs->port_status [i]);
}
//End Add
}
readl (&ehci->regs->command); /* unblock posted writes */
return 0;
}
#endif
/*-------------------------------------------------------------------------*/
/*
* ehci_work is called from some interrupts, timers, and so on.
* it calls driver completion functions, after dropping ehci->lock.
*/
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
{
if (ehci->reclaim_ready)
end_unlink_async (ehci, regs);
scan_async (ehci, regs);
if (ehci->next_uframe != -1)
scan_periodic (ehci, regs);
}
/*-------------------------------------------------------------------------*/
static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
{
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
u32 status;
int bh;
spin_lock (&ehci->lock);
status = readl (&ehci->regs->status);
/* e.g. cardbus physical eject */
if (status == ~(u32) 0) {
ehci_dbg (ehci, "device removed\n");
goto dead;
}
status &= INTR_MASK;
if (!status) /* irq sharing? */
goto done;
/* clear (just) interrupts */
writel (status, &ehci->regs->status);
readl (&ehci->regs->command); /* unblock posted write */
bh = 0;
#ifdef EHCI_VERBOSE_DEBUG
/* unrequested/ignored: Port Change Detect, Frame List Rollover */
dbg_status (ehci, "irq", status);
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -