static int uhci_pci_init(struct usb_hcd *hcd) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); uhci->io_addr = (unsigned long) hcd->rsrc_start; uhci->rh_numports = uhci_count_ports(hcd); /* Intel controllers report the OverCurrent bit active on. * VIA controllers report it active off, so we'll adjust the * bit value. (It's not standardized in the UHCI spec.) */ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA) uhci->oc_low = 1; /* HP's server management chip requires a longer port reset delay. */ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) uhci->wait_for_hp = 1; /* Intel controllers use non-PME wakeup signalling */ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_INTEL) device_set_wakeup_capable(uhci_dev(uhci), true); /* Set up pointers to PCI-specific functions */ uhci->reset_hc = uhci_pci_reset_hc; uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; uhci->configure_hc = uhci_pci_configure_hc; uhci->resume_detect_interrupts_are_broken = uhci_pci_resume_detect_interrupts_are_broken; uhci->global_suspend_mode_is_broken = uhci_pci_global_suspend_mode_is_broken; /* Kick BIOS off this hardware and reset if the controller * isn't already safely quiescent. */ check_and_reset_hc(uhci); return 0; }
static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); int rc = 0; dev_dbg(uhci_dev(uhci), "%s\n", __func__); spin_lock_irq(&uhci->lock); if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) goto done_okay; /* Already suspended or dead */ /* All PCI host controllers are required to disable IRQ generation * at the source, so we must turn off PIRQ. */ pci_write_config_word(pdev, USBLEGSUP, 0); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); /* Enable platform-specific non-PME# wakeup */ if (do_wakeup) { if (pdev->vendor == PCI_VENDOR_ID_INTEL) pci_write_config_byte(pdev, USBRES_INTEL, USBPORT1EN | USBPORT2EN); } done_okay: clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irq(&uhci->lock); synchronize_irq(hcd->irq); /* Check for race with a wakeup request */ if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { uhci_pci_resume(hcd, false); rc = -EBUSY; } return rc; }
static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); int rc = 0; dev_dbg(uhci_dev(uhci), "%s\n", __func__); spin_lock_irq(&uhci->lock); if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) goto done_okay; /* Already suspended or dead */ if (uhci->rh_state > UHCI_RH_SUSPENDED) { dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n"); rc = -EBUSY; goto done; }; /* All PCI host controllers are required to disable IRQ generation * at the source, so we must turn off PIRQ. */ pci_write_config_word(pdev, USBLEGSUP, 0); clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); /* Enable platform-specific non-PME# wakeup */ if (do_wakeup) { if (pdev->vendor == PCI_VENDOR_ID_INTEL) pci_write_config_byte(pdev, USBRES_INTEL, USBPORT1EN | USBPORT2EN); } done_okay: clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); done: spin_unlock_irq(&uhci->lock); return rc; }
static void uhci_check_ports(struct uhci_hcd *uhci) { unsigned int port; unsigned long port_addr; int status; for (port = 0; port < uhci->rh_numports; ++port) { port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; status = inw(port_addr); if (unlikely(status & USBPORTSC_PR)) { if (time_after_eq(jiffies, uhci->ports_timeout)) { CLR_RH_PORTSTAT(USBPORTSC_PR); udelay(10); /* HP's server management chip requires * a longer delay. */ if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) wait_for_HP(port_addr); /* If the port was enabled before, turning * reset on caused a port enable change. * Turning reset off causes a port connect * status change. Clear these changes. */ CLR_RH_PORTSTAT(USBPORTSC_CSC | USBPORTSC_PEC); SET_RH_PORTSTAT(USBPORTSC_PE); } } if (unlikely(status & USBPORTSC_RD)) { if (!test_bit(port, &uhci->resuming_ports)) { /* Port received a wakeup request */ set_bit(port, &uhci->resuming_ports); uhci->ports_timeout = jiffies + msecs_to_jiffies(20); /* Make sure we see the port again * after the resuming period is over. */ mod_timer(&uhci_to_hcd(uhci)->rh_timer, uhci->ports_timeout); } else if (time_after_eq(jiffies, uhci->ports_timeout)) { uhci_finish_suspend(uhci, port, port_addr); } } } }
/* * Make sure the controller is completely inactive, unable to * generate interrupts or do DMA. */ static void uhci_generic_reset_hc(struct uhci_hcd *uhci) { /* Reset the HC - this will force us to get a * new notification of any already connected * ports due to the virtual disconnect that it * implies. */ uhci_writew(uhci, USBCMD_HCRESET, USBCMD); mb(); udelay(5); if (uhci_readw(uhci, USBCMD) & USBCMD_HCRESET) dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); /* Just to be safe, disable interrupt requests and * make sure the controller is stopped. */ uhci_writew(uhci, 0, USBINTR); uhci_writew(uhci, 0, USBCMD); }
static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); dev_dbg(uhci_dev(uhci), "%s\n", __func__); /* Since we aren't in D3 any more, it's safe to set this flag * even if the controller was dead. */ set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_lock_irq(&uhci->lock); /* Make sure resume from hibernation re-enumerates everything */ if (hibernated) { uhci->reset_hc(uhci); finish_reset(uhci); } /* The firmware may have changed the controller settings during * a system wakeup. Check it and reconfigure to avoid problems. */ else { check_and_reset_hc(uhci); } configure_hc(uhci); /* Tell the core if the controller had to be reset */ if (uhci->rh_state == UHCI_RH_RESET) usb_root_hub_lost_power(hcd->self.root_hub); spin_unlock_irq(&uhci->lock); /* If interrupts don't work and remote wakeup is enabled then * the suspended root hub needs to be polled. */ if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) set_bit(HCD_FLAG_POLL_RH, &hcd->flags); /* Does the root hub have a port wakeup pending? */ usb_hcd_poll_rh_status(hcd); return 0; }
/* * Store the basic register settings needed by the controller. */ static void configure_hc(struct uhci_hcd *uhci) { /* Set the frame length to the default: 1 ms exactly */ outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF); /* Store the frame list base address */ outl(uhci->fl->dma_handle, uhci->io_addr + USBFLBASEADD); /* Set the current frame number */ outw(uhci->frame_number, uhci->io_addr + USBFRNUM); /* Mark controller as running before we enable interrupts */ uhci_to_hcd(uhci)->state = HC_STATE_RUNNING; mb(); /* Enable PIRQ */ pci_write_config_word(to_pci_dev(uhci_dev(uhci)), USBLEGSUP, USBLEGSUP_DEFAULT); }
static void uhci_generic_reset_hc(struct uhci_hcd *uhci) { /* */ uhci_writew(uhci, USBCMD_HCRESET, USBCMD); mb(); udelay(5); if (uhci_readw(uhci, USBCMD) & USBCMD_HCRESET) dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); /* */ uhci_writew(uhci, 0, USBINTR); uhci_writew(uhci, 0, USBCMD); }
static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) { int port; /* If we have to ignore overcurrent events then almost by definition * we can't depend on resume-detect interrupts. */ if (ignore_oc) return 1; switch (to_pci_dev(uhci_dev(uhci))->vendor) { default: break; case PCI_VENDOR_ID_GENESYS: /* Genesys Logic's GL880S controllers don't generate * resume-detect interrupts. */ return 1; case PCI_VENDOR_ID_INTEL: /* Some of Intel's USB controllers have a bug that causes * resume-detect interrupts if any port has an over-current * condition. To make matters worse, some motherboards * hardwire unused USB ports' over-current inputs active! * To prevent problems, we will not enable resume-detect * interrupts if any ports are OC. */ for (port = 0; port < uhci->rh_numports; ++port) { if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & USBPORTSC_OC) return 1; } break; } return 0; }
/* * Initialize a controller that was newly discovered or has just been * resumed. In either case we can't be sure of its previous state. * * Returns: 1 if the controller was reset, 0 otherwise. */ static int uhci_pci_check_and_reset_hc(struct uhci_hcd *uhci) { return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); }
/* * Make sure the controller is completely inactive, unable to * generate interrupts or do DMA. */ static void uhci_pci_reset_hc(struct uhci_hcd *uhci) { uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); }
/* * Initialize a controller that was newly discovered or has lost power * or otherwise been reset while it was suspended. In none of these cases * can we be sure of its previous state. */ static void check_and_reset_hc(struct uhci_hcd *uhci) { if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr)) finish_reset(uhci); }
return 1; } break; } return 0; } static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) __releases(uhci->lock) __acquires(uhci->lock) { int auto_stop; int int_enable; auto_stop = (new_state == UHCI_RH_AUTO_STOPPED); dev_dbg(uhci_dev(uhci), "%s%s\n", __FUNCTION__, (auto_stop ? " (auto-stop)" : "")); /* If we get a suspend request when we're already auto-stopped * then there's nothing to do. */ if (uhci->rh_state == UHCI_RH_AUTO_STOPPED) { uhci->rh_state = new_state; return; } /* Enable resume-detect interrupts if they work. * Then enter Global Suspend mode, still configured. */ int_enable = (resume_detect_interrupts_are_broken(uhci) ? 0 : USBINTR_RESUME);
/* size of returned buffer is part of USB spec */ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); int status, lstatus, retval = 0, len = 0; unsigned int port = wIndex - 1; unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; u16 wPortChange, wPortStatus; unsigned long flags; if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags) || uhci->dead) return -ETIMEDOUT; spin_lock_irqsave(&uhci->lock, flags); switch (typeReq) { case GetHubStatus: *(__le32 *)buf = cpu_to_le32(0); OK(4); /* hub power */ case GetPortStatus: if (port >= uhci->rh_numports) goto err; uhci_check_ports(uhci); status = inw(port_addr); /* Intel controllers report the OverCurrent bit active on. * VIA controllers report it active off, so we'll adjust the * bit value. (It's not standardized in the UHCI spec.) */ if (to_pci_dev(hcd->self.controller)->vendor == PCI_VENDOR_ID_VIA) status ^= USBPORTSC_OC; /* UHCI doesn't support C_RESET (always false) */ wPortChange = lstatus = 0; if (status & USBPORTSC_CSC) wPortChange |= USB_PORT_STAT_C_CONNECTION; if (status & USBPORTSC_PEC) wPortChange |= USB_PORT_STAT_C_ENABLE; if ((status & USBPORTSC_OCC) && !ignore_oc) wPortChange |= USB_PORT_STAT_C_OVERCURRENT; if (test_bit(port, &uhci->port_c_suspend)) { wPortChange |= USB_PORT_STAT_C_SUSPEND; lstatus |= 1; } if (test_bit(port, &uhci->resuming_ports)) lstatus |= 4; /* UHCI has no power switching (always on) */ wPortStatus = USB_PORT_STAT_POWER; if (status & USBPORTSC_CCS) wPortStatus |= USB_PORT_STAT_CONNECTION; if (status & USBPORTSC_PE) { wPortStatus |= USB_PORT_STAT_ENABLE; if (status & SUSPEND_BITS) wPortStatus |= USB_PORT_STAT_SUSPEND; } if (status & USBPORTSC_OC) wPortStatus |= USB_PORT_STAT_OVERCURRENT; if (status & USBPORTSC_PR) wPortStatus |= USB_PORT_STAT_RESET; if (status & USBPORTSC_LSDA) wPortStatus |= USB_PORT_STAT_LOW_SPEED; if (wPortChange) dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n", wIndex, status, lstatus); *(__le16 *)buf = cpu_to_le16(wPortStatus); *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange); OK(4); case SetHubFeature: /* We don't implement these */ case ClearHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: OK(0); default: goto err; } break; case SetPortFeature: if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_SUSPEND: SET_RH_PORTSTAT(USBPORTSC_SUSP); OK(0); case USB_PORT_FEAT_RESET: SET_RH_PORTSTAT(USBPORTSC_PR); /* Reset terminates Resume signalling */ uhci_finish_suspend(uhci, port, port_addr); /* USB v2.0 7.1.7.5 */ uhci->ports_timeout = jiffies + msecs_to_jiffies(50); OK(0); case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ OK(0); default: goto err; } break; case ClearPortFeature: if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PE); /* Disable terminates Resume signalling */ uhci_finish_suspend(uhci, port, port_addr); OK(0); case USB_PORT_FEAT_C_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: if (!(inw(port_addr) & USBPORTSC_SUSP)) { /* Make certain the port isn't suspended */ uhci_finish_suspend(uhci, port, port_addr); } else if (!test_and_set_bit(port, &uhci->resuming_ports)) { SET_RH_PORTSTAT(USBPORTSC_RD); /* The controller won't allow RD to be set * if the port is disabled. When this happens * just skip the Resume signalling. */ if (!(inw(port_addr) & USBPORTSC_RD)) uhci_finish_suspend(uhci, port, port_addr); else /* USB v2.0 7.1.7.7 */ uhci->ports_timeout = jiffies + msecs_to_jiffies(20); } OK(0); case USB_PORT_FEAT_C_SUSPEND: clear_bit(port, &uhci->port_c_suspend); OK(0); case USB_PORT_FEAT_POWER: /* UHCI has no power switching */ goto err; case USB_PORT_FEAT_C_CONNECTION: CLR_RH_PORTSTAT(USBPORTSC_CSC); OK(0); case USB_PORT_FEAT_C_OVER_CURRENT: CLR_RH_PORTSTAT(USBPORTSC_OCC); OK(0); case USB_PORT_FEAT_C_RESET: /* this driver won't report these */ OK(0); default: goto err; } break; case GetHubDescriptor: len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); memcpy(buf, root_hub_hub_des, len); if (len > 2) buf[2] = uhci->rh_numports; OK(len); default: err: retval = -EPIPE; } spin_unlock_irqrestore(&uhci->lock, flags); return retval; }
/* * Last rites for a defunct/nonfunctional controller * or one we don't want to use any more. */ static void hc_died(struct uhci_hcd *uhci) { uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); finish_reset(uhci); uhci->hc_inaccessible = 1; }
static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct uhci_hcd *uhci = hcd_to_uhci(hcd); int status, lstatus, retval = 0, len = 0; unsigned int port = wIndex - 1; unsigned long port_addr = USBPORTSC1 + 2 * port; u16 wPortChange, wPortStatus; unsigned long flags; if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) return -ETIMEDOUT; spin_lock_irqsave(&uhci->lock, flags); switch (typeReq) { case GetHubStatus: *(__le32 *)buf = cpu_to_le32(0); OK(4); case GetPortStatus: if (port >= uhci->rh_numports) goto err; uhci_check_ports(uhci); status = uhci_readw(uhci, port_addr); if (uhci->oc_low) status ^= USBPORTSC_OC; wPortChange = lstatus = 0; if (status & USBPORTSC_CSC) wPortChange |= USB_PORT_STAT_C_CONNECTION; if (status & USBPORTSC_PEC) wPortChange |= USB_PORT_STAT_C_ENABLE; if ((status & USBPORTSC_OCC) && !ignore_oc) wPortChange |= USB_PORT_STAT_C_OVERCURRENT; if (test_bit(port, &uhci->port_c_suspend)) { wPortChange |= USB_PORT_STAT_C_SUSPEND; lstatus |= 1; } if (test_bit(port, &uhci->resuming_ports)) lstatus |= 4; wPortStatus = USB_PORT_STAT_POWER; if (status & USBPORTSC_CCS) wPortStatus |= USB_PORT_STAT_CONNECTION; if (status & USBPORTSC_PE) { wPortStatus |= USB_PORT_STAT_ENABLE; if (status & SUSPEND_BITS) wPortStatus |= USB_PORT_STAT_SUSPEND; } if (status & USBPORTSC_OC) wPortStatus |= USB_PORT_STAT_OVERCURRENT; if (status & USBPORTSC_PR) wPortStatus |= USB_PORT_STAT_RESET; if (status & USBPORTSC_LSDA) wPortStatus |= USB_PORT_STAT_LOW_SPEED; if (wPortChange) dev_dbg(uhci_dev(uhci), "port %d portsc %04x,%02x\n", wIndex, status, lstatus); *(__le16 *)buf = cpu_to_le16(wPortStatus); *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange); OK(4); case SetHubFeature: case ClearHubFeature: switch (wValue) { case C_HUB_OVER_CURRENT: case C_HUB_LOCAL_POWER: OK(0); default: goto err; } break; case SetPortFeature: if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_SUSPEND: SET_RH_PORTSTAT(USBPORTSC_SUSP); OK(0); case USB_PORT_FEAT_RESET: SET_RH_PORTSTAT(USBPORTSC_PR); uhci_finish_suspend(uhci, port, port_addr); uhci->ports_timeout = jiffies + msecs_to_jiffies(50); OK(0); case USB_PORT_FEAT_POWER: OK(0); default: goto err; } break; case ClearPortFeature: if (port >= uhci->rh_numports) goto err; switch (wValue) { case USB_PORT_FEAT_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PE); uhci_finish_suspend(uhci, port, port_addr); OK(0); case USB_PORT_FEAT_C_ENABLE: CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) { uhci_finish_suspend(uhci, port, port_addr); } else if (!test_and_set_bit(port, &uhci->resuming_ports)) { SET_RH_PORTSTAT(USBPORTSC_RD); if (!(uhci_readw(uhci, port_addr) & USBPORTSC_RD)) uhci_finish_suspend(uhci, port, port_addr); else uhci->ports_timeout = jiffies + msecs_to_jiffies(20); } OK(0); case USB_PORT_FEAT_C_SUSPEND: clear_bit(port, &uhci->port_c_suspend); OK(0); case USB_PORT_FEAT_POWER: goto err; case USB_PORT_FEAT_C_CONNECTION: CLR_RH_PORTSTAT(USBPORTSC_CSC); OK(0); case USB_PORT_FEAT_C_OVER_CURRENT: CLR_RH_PORTSTAT(USBPORTSC_OCC); OK(0); case USB_PORT_FEAT_C_RESET: OK(0); default: goto err; } break; case GetHubDescriptor: len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength); memcpy(buf, root_hub_hub_des, len); if (len > 2) buf[2] = uhci->rh_numports; OK(len); default: err: retval = -EPIPE; } spin_unlock_irqrestore(&uhci->lock, flags); return retval; }