/** * ehci_ppc_soc_hcd_init - init echi structure * Context: !in_interrupt() * * Init basic fields like regs for this USB host controller. * */ static int ehci_ppc_soc_hcd_init(struct ehci_hcd *ehci) { u32 temp; spin_lock_init(&ehci->lock); ehci->caps = ehci->hcd.regs; ehci->regs = ehci->hcd.regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "init"); dbg_hcc_params(ehci, "init"); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); ehci_reset(ehci); ehci_port_power(ehci, 0); temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) { ehci_dbg(ehci, "bogus port configuration: " "cc=%d x pcc=%d < ports=%d\n", HCS_N_CC(ehci->hcs_params), HCS_N_PCC(ehci->hcs_params), HCS_N_PORTS(ehci->hcs_params)); } /* force HC to halt state */ return ehci_halt(ehci); }
int get_ehci_connect_status(int usbc_no) { struct usb_hcd *hcd = NULL; struct ehci_hcd *ehci = NULL; struct sw_hci_hcd *sw_ehci = NULL; int port; int connect = 0; if(usbc_no < 0 || usbc_no > 2){ printk("usbc_no : %d invalid\n", usbc_no); return 0; } sw_ehci = g_sw_ehci[usbc_no]; if(!sw_ehci){ return 0; } hcd = sw_ehci->hcd; if(!hcd){ return 0; } ehci = hcd_to_ehci(hcd); if(!ehci){ return 0; } port = HCS_N_PORTS(ehci->hcs_params); while(port--) connect |= readl(&ehci->regs->port_status[port]) & PORT_CONNECT; return connect; }
/* * Dedicate or undedicate a port to the companion controller. * Syntax is "[-]portnum", where a leading '-' sign means * return control of the port to the EHCI controller. */ static ssize_t store_companion(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ehci_hcd *ehci; int portnum, new_owner; ehci = hcd_to_ehci(dev_get_drvdata(dev)); new_owner = PORT_OWNER; /* Owned by companion */ if (sscanf(buf, "%d", &portnum) != 1) return -EINVAL; if (portnum < 0) { portnum = - portnum; new_owner = 0; /* Owned by EHCI */ } if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) return -ENOENT; portnum--; if (new_owner) set_bit(portnum, &ehci->companion_ports); else clear_bit(portnum, &ehci->companion_ports); set_owner(ehci, portnum, new_owner); return count; }
static void ehci_hub_descriptor ( struct ehci_hcd *ehci, struct usb_hub_descriptor *desc ) { int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */ desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ memset (&desc->bitmap [0], 0, temp); memset (&desc->bitmap [temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ if (HCS_PPC (ehci->hcs_params)) temp |= 0x0001; /* per-port power control */ if (HCS_INDICATOR (ehci->hcs_params)) temp |= 0x0080; /* per-port indicators (LEDs) */ desc->wHubCharacteristics = cpu_to_le16 (temp); }
static int ci_imx_ehci_bus_resume(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int port; int ret = orig_bus_resume(hcd); if (ret) return ret; port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *reg = &ehci->regs->port_status[port]; u32 portsc = ehci_readl(ehci, reg); /* * Notify PHY after resume signal has finished, it is * for global suspend case. */ if (hcd->usb_phy && test_bit(port, &ehci->bus_suspended) && (portsc & PORT_CONNECT) && (ehci_port_speed(ehci, portsc) == USB_PORT_STAT_HIGH_SPEED)) /* notify the USB PHY */ usb_phy_notify_resume(hcd->usb_phy, USB_SPEED_HIGH); } return 0; }
static void ehci_hub_descriptor ( struct ehci_hcd *ehci, struct usb_hub_descriptor *desc ) { int ports = HCS_N_PORTS (ehci->hcs_params); u16 temp; desc->bDescriptorType = 0x29; desc->bPwrOn2PwrGood = 10; /* ehci 1.0, 2.3.9 says 20ms max */ desc->bHubContrCurrent = 0; desc->bNbrPorts = ports; temp = 1 + (ports / 8); desc->bDescLength = 7 + 2 * temp; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ memset (&desc->bitmap [0], 0, temp); memset (&desc->bitmap [temp], 0xff, temp); temp = 0x0008; /* per-port overcurrent reporting */ if (HCS_PPC (ehci->hcs_params)) temp |= 0x0001; /* per-port power control */ else temp |= 0x0002; /* no power switching */ #if 0 // re-enable when we support USB_PORT_FEAT_INDICATOR below. if (HCS_INDICATOR (ehci->hcs_params)) temp |= 0x0080; /* per-port indicators (LEDs) */ #endif desc->wHubCharacteristics = (__force __u16)cpu_to_le16 (temp); }
static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv; struct device *dev = hcd->self.controller; int ret = 0; int port = HCS_N_PORTS(ehci->hcs_params); if (priv->reg_vbus) { if (port > 1) { dev_warn(dev, "Not support multi-port regulator control\n"); return 0; } if (enable) ret = regulator_enable(priv->reg_vbus); else ret = regulator_disable(priv->reg_vbus); if (ret) { dev_err(dev, "Failed to %s vbus regulator, ret=%d\n", enable ? "enable" : "disable", ret); return ret; } } return 0; };
/* ehci_brcm_hub_control * Intercept echi-hcd request to complete RESUME and align it to the start * of the next microframe. * If RESUME is complete too late in the microframe, host controller * detects babble on suspended port and resets the port afterwards. * This s/w workaround allows to avoid this problem. * See http://jira.broadcom.com/browse/SWLINUX-1909 for more details */ static int ehci_brcm_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int ports = HCS_N_PORTS(ehci->hcs_params); u32 __iomem *status_reg = &ehci->regs->port_status[ (wIndex & 0xff) - 1]; unsigned long flags; int retval, irq_disabled = 0; /* RESUME is cleared when GetPortStatus() is called 20ms after start of RESUME */ if ((typeReq == GetPortStatus) && (wIndex && wIndex <= ports) && ehci->reset_done[wIndex-1] && time_after_eq(jiffies, ehci->reset_done[wIndex-1]) && (ehci_readl(ehci, status_reg) & PORT_RESUME)) { /* to make sure we are not interrupted until RESUME bit is cleared, disable interrupts on current CPU */ ehci_dbg(ehci, "SOF alignment workaround\n"); irq_disabled = 1; local_irq_save(flags); ehci_brcm_wait_for_sof(ehci, 5); } retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); if (irq_disabled) local_irq_restore(flags); return retval; }
static void ehci_hsic_phy_power(struct ehci_hcd *ehci, int is_low_power) { unsigned port; port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *hostpc_reg; u32 t3; hostpc_reg = (u32 __iomem *)((u8 *) ehci->regs + 0x84 + 4 * port); t3 = ehci_readl(ehci, hostpc_reg); ehci_dbg(ehci, "Port %d phy low-power mode org %08x\n", port, t3); if (is_low_power) ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg); else ehci_writel(ehci, t3 & ~HOSTPC_PHCD, hostpc_reg); t3 = ehci_readl(ehci, hostpc_reg); ehci_dbg(ehci, "Port %d phy low-power mode chg %08x\n", port, t3); } }
// mainly a copy of ehci_run(), can perhaps be reduced: static int ehci_msm_run(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int retval = 0; int port = HCS_N_PORTS(ehci->hcs_params); u32 __iomem *reg_ptr; u32 hcc_params; hcd->uses_new_polling = 1; hcd->poll_rh = 0; /* set hostmode */ reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); ehci_writel(ehci, (USBMODE_VBUS | USBMODE_SDIS), reg_ptr); /* port configuration - phy, port speed, port power, port enable */ while (port--) ehci_writel(ehci, (PORTSC_PTS_ULPI | PORT_POWER | PORT_PE), &ehci->regs->port_status[port]); ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_64BIT_ADDR(hcc_params)) ehci_writel(ehci, 0, &ehci->regs->segment); ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ehci->command |= CMD_RUN; ehci_writel(ehci, ehci->command, &ehci->regs->command); /* * 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. (Except where one is integrated, * and there's no companion controller unless maybe for USB OTG.) * * Turning on the CF flag will transfer ownership of all ports * from the companions to the EHCI controller. If any of the * companions are in the middle of a port reset at the time, it * could cause trouble. Write-locking ehci_cf_port_reset_rwsem * guarantees that no resets are in progress. After we set CF, * a short delay lets the hardware catch up; new resets shouldn't * be started before the port switching actions could complete. */ down_write(&ehci_cf_port_reset_rwsem); hcd->state = HC_STATE_RUNNING; ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ msleep(5); up_write(&ehci_cf_port_reset_rwsem); /*Enable appropriate Interrupts*/ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); return retval; }
/* On some systems, leaving remote wakeup enabled prevents system shutdown. * The firmware seems to think that powering off is a wakeup event! * This routine turns off remote wakeup and everything else, on all ports. */ static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) { int port = HCS_N_PORTS(ehci->hcs_params); while (port--) ehci_writel(ehci, PORT_RWC_BITS, &ehci->regs->port_status[port]); }
static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp, status = 0; int ports, i, retval = 1; unsigned long flags; /* if !USB_SUSPEND, root hub timers won't get shut down ... */ if (!HC_IS_RUNNING(hcd->state)) return 0; /* init status to no-changes */ buf [0] = 0; ports = HCS_N_PORTS (ehci->hcs_params); if (ports > 7) { buf [1] = 0; retval++; } /* no hub change reports (bit 0) for now (power, ...) */ /* port N changes (bit N)? */ spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < ports; i++) { temp = readl (&ehci->regs->port_status [i]); if (temp & PORT_OWNER) { /* don't report this in GetPortStatus */ if (temp & PORT_CSC) { temp &= ~PORT_RWC_BITS; temp |= PORT_CSC; writel (temp, &ehci->regs->port_status [i]); } continue; } if (!(temp & PORT_CONNECT)) ehci->reset_done [i] = 0; if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0 // PORT_STAT_C_SUSPEND? || ((temp & PORT_RESUME) != 0 && time_after (jiffies, ehci->reset_done [i]))) { if (i < 7) buf [0] |= 1 << (i + 1); else buf [1] |= 1 << (i - 7); status = STS_PCD; } } /* FIXME autosuspend idle root hubs */ spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; }
static int ehci_bus_suspend (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); int port; if (time_before (jiffies, ehci->next_statechange)) msleep(5); del_timer_sync(&ehci->watchdog); del_timer_sync(&ehci->iaa_watchdog); port = HCS_N_PORTS (ehci->hcs_params); spin_lock_irq (&ehci->lock); /* stop schedules, clean any completed work */ if (HC_IS_RUNNING(hcd->state)) { ehci_quiesce (ehci); hcd->state = HC_STATE_QUIESCING; } ehci->command = readl (&ehci->regs->command); if (ehci->reclaim) end_unlink_async(ehci, NULL); ehci_work(ehci, NULL); /* suspend any active/unsuspended ports, maybe allow wakeup */ while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; u32 t1 = readl (reg) & ~PORT_RWC_BITS; u32 t2 = t1; if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) t2 |= PORT_SUSPEND; if (device_may_wakeup(&hcd->self.root_hub->dev)) { t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); t2 |= PORT_WKOC_E; } else { t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); } if (t1 != t2) { ehci_vdbg (ehci, "port %d, %08x -> %08x\n", port + 1, t1, t2); writel (t2, reg); } } /* turn off now-idle HC */ ehci_halt (ehci); hcd->state = HC_STATE_SUSPENDED; ehci->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irq (&ehci->lock); return 0; }
static int ehci_hub_suspend (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); struct usb_device *root = hcd_to_bus (&ehci->hcd)->root_hub; int port; int status = 0; if (root->dev.power.power_state != 0) return 0; if (time_before (jiffies, ehci->next_statechange)) return -EAGAIN; del_timer_sync(&ehci->watchdog); del_timer_sync(&ehci->iaa_watchdog); port = HCS_N_PORTS (ehci->hcs_params); spin_lock_irq (&ehci->lock); /* suspend any active/unsuspended ports, maybe allow wakeup */ while (port--) { u32 __iomem *reg = &ehci->regs->port_status [port]; u32 t1 = readl (reg) & ~PORT_RWC_BITS; u32 t2 = t1; if ((t1 & PORT_PE) && !(t1 & PORT_OWNER)) t2 |= PORT_SUSPEND; if (ehci->hcd.remote_wakeup) t2 |= PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E; else t2 &= ~(PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E); if (t1 != t2) { ehci_vdbg (ehci, "port %d, %08x -> %08x\n", port + 1, t1, t2); writel (t2, reg); } } /* stop schedules, then turn off HC and clean any completed work */ if (hcd->state == USB_STATE_RUNNING) ehci_ready (ehci); ehci->command = readl (&ehci->regs->command); writel (ehci->command & ~CMD_RUN, &ehci->regs->command); if (ehci->reclaim) end_unlink_async(ehci, NULL); ehci_work(ehci, NULL); (void) handshake (&ehci->regs->status, STS_HALT, STS_HALT, 2000); root->dev.power.power_state = 3; ehci->next_statechange = jiffies + msecs_to_jiffies(10); spin_unlock_irq (&ehci->lock); return status; }
static void ehci_port_power (struct ehci_hcd *ehci, int is_on) { unsigned port; if (!HCS_PPC (ehci->hcs_params)) return; ehci_dbg (ehci, "...power%s ports...\n", is_on ? "up" : "down"); for (port = HCS_N_PORTS (ehci->hcs_params); port > 0; ) (void) ehci_hub_control(ehci_to_hcd(ehci), is_on ? SetPortFeature : ClearPortFeature, USB_PORT_FEAT_POWER, port--, NULL, 0); msleep(20); }
/* this file is part of ehci-hcd.c */ static int __maybe_unused ehci_lpm_set_da(struct ehci_hcd *ehci, int dev_addr, int port_num) { u32 __iomem portsc; ehci_dbg(ehci, "set dev address %d for port %d\n", dev_addr, port_num); if (port_num > HCS_N_PORTS(ehci->hcs_params)) { ehci_dbg(ehci, "invalid port number %d\n", port_num); return -ENODEV; } portsc = ehci_readl(ehci, &ehci->regs->port_status[port_num-1]); portsc &= ~PORT_DEV_ADDR; portsc |= dev_addr<<25; ehci_writel(ehci, portsc, &ehci->regs->port_status[port_num-1]); return 0; }
static void ehci_hsic_port_power(struct ehci_hcd *ehci, int is_on) { unsigned port; if (!HCS_PPC(ehci->hcs_params)) return; dev_dbg(&pci_dev->dev, "...power%s ports...\n", is_on ? "up" : "down"); for (port = HCS_N_PORTS(ehci->hcs_params); port > 0; ) (void) ehci_hub_control(ehci_to_hcd(ehci), is_on ? SetPortFeature : ClearPortFeature, USB_PORT_FEAT_POWER, port--, NULL, 0); /* Flush those writes */ ehci_readl(ehci, &ehci->regs->command); }
static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv; struct device *dev = hcd->self.controller; struct ci_hdrc *ci = dev_get_drvdata(dev); int ret = 0; int port = HCS_N_PORTS(ehci->hcs_params); if (priv->reg_vbus) { if (port > 1) { dev_warn(dev, "Not support multi-port regulator control\n"); return 0; } if (enable) ret = regulator_enable(priv->reg_vbus); else ret = regulator_disable(priv->reg_vbus); if (ret) { dev_err(dev, "Failed to %s vbus regulator, ret=%d\n", enable ? "enable" : "disable", ret); return ret; } } if (ci->platdata->flags & CI_HDRC_PHY_VBUS_CONTROL && ci->usb_phy && ci->usb_phy->set_vbus) { if (enable) ci->usb_phy->set_vbus(ci->usb_phy, 1); else ci->usb_phy->set_vbus(ci->usb_phy, 0); } if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) { /* * Marvell 28nm HSIC PHY requires forcing the port to HS mode. * As HSIC is always HS, this should be safe for others. */ hw_port_test_set(ci, 5); hw_port_test_set(ci, 0); } return 0; };
static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp, status = 0; int ports, i, retval = 1; unsigned long flags; /* init status to no-changes */ buf [0] = 0; ports = HCS_N_PORTS (ehci->hcs_params); if (ports > 7) { buf [1] = 0; retval++; } /* no hub change reports (bit 0) for now (power, ...) */ /* port N changes (bit N)? */ spin_lock_irqsave (&ehci->lock, flags); for (i = 0; i < ports; i++) { temp = readl (&ehci->regs->port_status [i]); if (temp & PORT_OWNER) { /* don't report this in GetPortStatus */ if (temp & PORT_CSC) { temp &= ~PORT_CSC; writel (temp, &ehci->regs->port_status [i]); } continue; } if (!(temp & PORT_CONNECT)) ehci->reset_done [i] = 0; if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) { if (i < 7) buf [0] |= 1 << (i + 1); else buf [1] |= 1 << (i - 7); status = STS_PCD; } } spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; }
static int ehci_msm_run(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct msmusb_hcd *mhcd = hcd_to_mhcd(hcd); int retval = 0; int port = HCS_N_PORTS(ehci->hcs_params); u32 __iomem *reg_ptr; u32 hcc_params; struct msm_usb_host_platform_data *pdata = mhcd->pdata; hcd->uses_new_polling = 1; hcd->poll_rh = 0; USBH_INFO("%s\n", __func__); /* set hostmode */ reg_ptr = (u32 __iomem *)(((u8 __iomem *)ehci->regs) + USBMODE); ehci_writel(ehci, (USBMODE_VBUS | USBMODE_SDIS), reg_ptr); /* port configuration - phy, port speed, port power, port enable */ while (port--) ehci_writel(ehci, (PTS_VAL(pdata->phy_info) | PORT_POWER | PORT_PE), &ehci->regs->port_status[port]); ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); if (HCC_64BIT_ADDR(hcc_params)) ehci_writel(ehci, 0, &ehci->regs->segment); ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); ehci->command |= CMD_RUN; ehci_writel(ehci, ehci->command, &ehci->regs->command); ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ hcd->state = HC_STATE_RUNNING; /*Enable appropriate Interrupts*/ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); return retval; }
/* Display the ports dedicated to the companion controller */ static ssize_t show_companion(struct device *dev, struct device_attribute *attr, char *buf) { struct ehci_hcd *ehci; int nports, index, n; int count = PAGE_SIZE; char *ptr = buf; ehci = hcd_to_ehci(dev_get_drvdata(dev)); nports = HCS_N_PORTS(ehci->hcs_params); for (index = 0; index < nports; ++index) { if (test_bit(index, &ehci->companion_ports)) { n = scnprintf(ptr, count, "%d\n", index + 1); ptr += n; count -= n; } } return ptr - buf; }
static int ci_ehci_bus_suspend(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int port; u32 tmp; int ret = orig_bus_suspend(hcd); if (ret) return ret; port = HCS_N_PORTS(ehci->hcs_params); while (port--) { u32 __iomem *reg = &ehci->regs->port_status[port]; u32 portsc = ehci_readl(ehci, reg); if (portsc & PORT_CONNECT) { /* * For chipidea, the resume signal will be ended * automatically, so for remote wakeup case, the * usbcmd.rs may not be set before the resume has * ended if other resume paths consumes too much * time (~24ms), in that case, the SOF will not * send out within 3ms after resume ends, then the * high speed device will enter full speed mode. */ tmp = ehci_readl(ehci, &ehci->regs->command); tmp |= CMD_RUN; ehci_writel(ehci, tmp, &ehci->regs->command); /* * It needs a short delay between set RS bit and PHCD. */ usleep_range(150, 200); break; } } return 0; }
static int ehci_omap_bus_suspend(struct usb_hcd *hcd) { struct usb_bus *bus = hcd_to_bus(hcd); int ret; #ifdef CONFIG_LOGIC_OMAP3530_USB3320_HACK struct ehci_hcd_omap *omap = platform_get_drvdata(to_platform_device( hcd->self.controller)); struct ehci_hcd *ehci = omap->ehci; int ports = HCS_N_PORTS (omap->ehci->hcs_params); int i; // Manually suspend the ports HERE. We want to be able to switch to // GPIO lines before it shuts down the controller entirely; // Otherwise, it'll wake up the PHY again. for(i=0;i<ports;++i) { u32 __iomem *status_reg = &omap->ehci->regs->port_status[i]; u32 temp = ehci_readl(omap->ehci, status_reg); if((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) continue; ehci_writel(omap->ehci, temp | PORT_SUSPEND, status_reg); set_bit(i, &ehci->suspended_ports); } msleep(1); usb3320_hack_install(); #endif ret = ehci_bus_suspend(hcd); ehci_omap_dev_suspend(bus->controller); return ret; }
static int ehci_hub_control ( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); int ports = HCS_N_PORTS (ehci->hcs_params); u32 temp, status; unsigned long flags; int retval = 0; /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. * HCS_INDICATOR may say we can change LEDs to off/amber/green. * (track current state ourselves) ... blink for diagnostics, * power, "this is the one", etc. EHCI spec supports this. */ spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; switch (wValue) { case USB_PORT_FEAT_ENABLE: writel (temp & ~PORT_PE, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_ENABLE: writel (temp | PORT_PEC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: case USB_PORT_FEAT_C_SUSPEND: /* ? */ break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp & ~PORT_POWER, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_CONNECTION: writel (temp | PORT_CSC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_OVER_CURRENT: writel (temp | PORT_OCC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ break; default: goto error; } readl (&ehci->regs->command); /* unblock posted write */ break; case GetHubDescriptor: ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) buf); break; case GetHubStatus: /* no hub-wide feature/status flags */ memset (buf, 0, 4); //cpu_to_le32s ((u32 *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error; wIndex--; status = 0; temp = readl (&ehci->regs->port_status [wIndex]); // wPortChange bits if (temp & PORT_CSC) status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; // USB_PORT_FEAT_C_SUSPEND if (temp & PORT_OCC) status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) && time_after (jiffies, ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; /* force reset to complete */ writel (temp & ~PORT_RESET, &ehci->regs->port_status [wIndex]); do { temp = readl ( &ehci->regs->port_status [wIndex]); udelay (10); } while (temp & PORT_RESET); /* see what we found out */ temp = check_reset_complete (ehci, wIndex, temp); } // don't show wPortStatus if it's owned by a companion hc if (!(temp & PORT_OWNER)) { if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; status |= 1 << USB_PORT_FEAT_HIGHSPEED; } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; if (temp & PORT_SUSPEND) status |= 1 << USB_PORT_FEAT_SUSPEND; if (temp & PORT_OC) status |= 1 << USB_PORT_FEAT_OVER_CURRENT; if (temp & PORT_RESET) status |= 1 << USB_PORT_FEAT_RESET; if (temp & PORT_POWER) status |= 1 << USB_PORT_FEAT_POWER; } #ifndef EHCI_VERBOSE_DEBUG if (status & ~0xffff) /* only if wPortChange is interesting */ #endif // dbg_port (ehci, "GetStatus", wIndex + 1, temp); // FIXME: for rtl8652 using... if you don't using rtl8652 EHCI driver, please enable this function. // we "know" this alignment is good, caller used kmalloc()... *((u32 *) buf) = cpu_to_le32 (status); break; case SetHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case SetPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; switch (wValue) { case USB_PORT_FEAT_SUSPEND: writel (temp | PORT_SUSPEND, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_RESET: /* line status bits may report this as low speed */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); temp |= PORT_OWNER; } else { ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; /* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root */ ehci->reset_done [wIndex] = jiffies + ((50 /* msec */ * HZ) / 1000); } writel (temp, &ehci->regs->port_status [wIndex]); break; default: goto error; } readl (&ehci->regs->command); /* unblock posted writes */ break; default: error: /* "stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore (&ehci->lock, flags); return retval; }
static int tegra_ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength ) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); int ports = HCS_N_PORTS(ehci->hcs_params); u32 temp, status; u32 __iomem *status_reg; u32 usbsts_reg; unsigned long flags; int retval = 0; unsigned selector; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); bool hsic = false; if (!tegra->host_resumed) { if (buf) memset (buf, 0, wLength); return retval; } if (tegra->phy->instance == 1) { struct tegra_ulpi_config *config = tegra->phy->config; hsic = (config->inf_type == TEGRA_USB_UHSIC); } status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1]; spin_lock_irqsave(&ehci->lock, flags); /* * In ehci_hub_control() for USB_PORT_FEAT_ENABLE clears the other bits * that are write on clear, by writing back the register read value, so * USB_PORT_FEAT_ENABLE is handled by masking the set on clear bits */ if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_ENABLE) { temp = ehci_readl(ehci, status_reg) & ~PORT_RWC_BITS; ehci_writel(ehci, temp & ~PORT_PE, status_reg); goto done; } else if (typeReq == GetPortStatus) { temp = ehci_readl(ehci, status_reg); if (tegra->port_resuming && !(temp & PORT_SUSPEND) && time_after_eq(jiffies, ehci->reset_done[wIndex-1])) { /* Resume completed, re-enable disconnect detection */ tegra->port_resuming = 0; clear_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); ehci->reset_done[wIndex-1] = 0; tegra_usb_phy_postresume(tegra->phy, false); } } else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { temp = ehci_readl(ehci, status_reg); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { retval = -EPIPE; goto done; } temp &= ~PORT_WKCONN_E; temp |= PORT_WKDISC_E | PORT_WKOC_E; ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); /* Need a 4ms delay before the controller goes to suspend */ mdelay(4); /* * If a transaction is in progress, there may be a delay in * suspending the port. Poll until the port is suspended. */ if (handshake(ehci, status_reg, PORT_SUSPEND, PORT_SUSPEND, 5000)) pr_err("%s: timeout waiting for SUSPEND\n", __func__); set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); goto done; } /* * Tegra host controller will time the resume operation to clear the bit * when the port control state switches to HS or FS Idle. This behavior * is different from EHCI where the host controller driver is required * to set this bit to a zero after the resume duration is timed in the * driver. */ else if (typeReq == ClearPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { temp = ehci_readl(ehci, status_reg); if ((temp & PORT_RESET) || !(temp & PORT_PE)) { retval = -EPIPE; goto done; } if (!(temp & PORT_SUSPEND)) goto done; tegra->port_resuming = 1; /* Disable disconnect detection during port resume */ tegra_usb_phy_preresume(tegra->phy, false); ehci_dbg(ehci, "%s:USBSTS = 0x%x", __func__, ehci_readl(ehci, &ehci->regs->status)); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); ehci_writel(ehci, usbsts_reg, &ehci->regs->status); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); udelay(20); if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) pr_err("%s: timeout set for STS_SRI\n", __func__); usbsts_reg = ehci_readl(ehci, &ehci->regs->status); ehci_writel(ehci, usbsts_reg, &ehci->regs->status); if (handshake(ehci, &ehci->regs->status, STS_SRI, 0, 2000)) pr_err("%s: timeout clear STS_SRI\n", __func__); if (handshake(ehci, &ehci->regs->status, STS_SRI, STS_SRI, 2000)) pr_err("%s: timeout set STS_SRI\n", __func__); udelay(20); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); /* start resume signaling */ ehci_writel(ehci, temp | PORT_RESUME, status_reg); ehci->reset_done[wIndex-1] = jiffies + msecs_to_jiffies(25); /* whoever resumes must GetPortStatus to complete it!! */ goto done; } /* Handle port reset here */ if ((hsic) && (typeReq == SetPortFeature) && ((wValue == USB_PORT_FEAT_RESET) || (wValue == USB_PORT_FEAT_POWER))) { selector = wIndex >> 8; wIndex &= 0xff; if (!wIndex || wIndex > ports) { retval = -EPIPE; goto done; } wIndex--; status = 0; temp = ehci_readl(ehci, status_reg); if (temp & PORT_OWNER) goto done; temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_RESET: { if (temp & PORT_RESUME) { retval = -EPIPE; goto done; } /* line status bits may report this as low speed, * which can be fine if this root hub has a * transaction translator built in. */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); temp |= PORT_OWNER; ehci_writel(ehci, temp, status_reg); } else { ehci_vdbg(ehci, "port %d reset\n", wIndex + 1); temp &= ~PORT_PE; /* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(50); ehci_writel(ehci, temp, status_reg); if (hsic && (wIndex == 0)) tegra_usb_phy_bus_reset(tegra->phy); } break; } case USB_PORT_FEAT_POWER: { if (HCS_PPC(ehci->hcs_params)) ehci_writel(ehci, temp | PORT_POWER, status_reg); if (hsic && (wIndex == 0)) tegra_usb_phy_bus_connect(tegra->phy); break; } } goto done; }
/* called during probe() after chip reset completes */ static int ehci_pci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct pci_dev *p_smbus; u8 rev; u32 temp; int retval; switch (pdev->vendor) { case PCI_VENDOR_ID_TOSHIBA_2: /* celleb's companion chip */ if (pdev->device == 0x01b5) { #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO ehci->big_endian_mmio = 1; #else ehci_warn(ehci, "unsupported big endian Toshiba quirk\n"); #endif } break; } ehci->caps = hcd->regs; ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* ehci_init() causes memory for DMA transfers to be * allocated. Thus, any vendor-specific workarounds based on * limiting the type of memory used for DMA transfers must * happen before ehci_init() is called. */ switch (pdev->vendor) { case PCI_VENDOR_ID_NVIDIA: /* NVidia reports that certain chips don't handle * QH, ITD, or SITD addresses above 2GB. (But TD, * data buffer, and periodic schedule are normal.) */ switch (pdev->device) { case 0x003c: /* MCP04 */ case 0x005b: /* CK804 */ case 0x00d8: /* CK8 */ case 0x00e8: /* CK8S */ if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(31)) < 0) ehci_warn(ehci, "can't enable NVidia " "workaround for >2GB RAM\n"); break; } break; } /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); retval = ehci_halt(ehci); if (retval) return retval; if ((pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == 0x7808) || (pdev->vendor == PCI_VENDOR_ID_ATI && pdev->device == 0x4396)) { /* EHCI controller on AMD SB700/SB800/Hudson-2/3 platforms may * read/write memory space which does not belong to it when * there is NULL pointer with T-bit set to 1 in the frame list * table. To avoid the issue, the frame list link pointer * should always contain a valid pointer to a inactive qh. */ ehci->use_dummy_qh = 1; ehci_info(ehci, "applying AMD SB700/SB800/Hudson-2/3 EHCI " "dummy qh workaround\n"); } /* data structure init */ retval = ehci_init(hcd); if (retval) return retval; switch (pdev->vendor) { case PCI_VENDOR_ID_NEC: ehci->need_io_watchdog = 0; break; 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"); ehci->has_lpm = 0; } if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) { hcd->has_tt = 1; tdi_reset(ehci); } if (pdev->subsystem_vendor == PCI_VENDOR_ID_ASUSTEK) { /* EHCI #1 or #2 on 6 Series/C200 Series chipset */ if (pdev->device == 0x1c26 || pdev->device == 0x1c2d) { ehci_info(ehci, "broken D3 during system sleep on ASUS\n"); hcd->broken_pci_sleep = 1; device_set_wakeup_capable(&pdev->dev, false); } } break; case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { hcd->has_tt = 1; tdi_reset(ehci); } break; case PCI_VENDOR_ID_AMD: /* AMD PLL quirk */ if (usb_amd_find_chipset_info()) ehci->amd_pll_fix = 1; /* AMD8111 EHCI doesn't work, according to AMD errata */ if (pdev->device == 0x7463) { ehci_info(ehci, "ignoring AMD8111 (errata)\n"); retval = -EIO; goto done; } break; case PCI_VENDOR_ID_NVIDIA: switch (pdev->device) { /* Some NForce2 chips have problems with selective suspend; * fixed in newer silicon. */ case 0x0068: if (pdev->revision < 0xa4) ehci->no_selective_suspend = 1; break; /* MCP89 chips on the MacBookAir3,1 give EPROTO when * fetching device descriptors unless LPM is disabled. * There are also intermittent problems enumerating * devices with PPCD enabled. */ case 0x0d9d: ehci_info(ehci, "disable lpm/ppcd for nvidia mcp89"); ehci->has_lpm = 0; ehci->has_ppcd = 0; ehci->command &= ~CMD_PPCEE; break; } break; case PCI_VENDOR_ID_VIA: if (pdev->device == 0x3104 && (pdev->revision & 0xf0) == 0x60) { u8 tmp; /* The VT6212 defaults to a 1 usec EHCI sleep time which * hogs the PCI bus *badly*. Setting bit 5 of 0x4B makes * that sleep time use the conventional 10 usec. */ pci_read_config_byte(pdev, 0x4b, &tmp); if (tmp & 0x20) break; pci_write_config_byte(pdev, 0x4b, tmp | 0x20); } break; case PCI_VENDOR_ID_ATI: /* AMD PLL quirk */ if (usb_amd_find_chipset_info()) ehci->amd_pll_fix = 1; /* SB600 and old version of SB700 have a bug in EHCI controller, * which causes usb devices lose response in some cases. */ if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) { p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); if (!p_smbus) break; rev = p_smbus->revision; if ((pdev->device == 0x4386) || (rev == 0x3a) || (rev == 0x3b)) { u8 tmp; ehci_info(ehci, "applying AMD SB600/SB700 USB " "freeze workaround\n"); pci_read_config_byte(pdev, 0x53, &tmp); pci_write_config_byte(pdev, 0x53, tmp | (1<<3)); } pci_dev_put(p_smbus); } break; case PCI_VENDOR_ID_NETMOS: /* MosChip frame-index-register bug */ ehci_info(ehci, "applying MosChip frame-index workaround\n"); ehci->frame_index_bug = 1; break; } /* optional debug port, normally in the first BAR */ temp = pci_find_capability(pdev, 0x0a); if (temp) { pci_read_config_dword(pdev, temp, &temp); temp >>= 16; if ((temp & (3 << 13)) == (1 << 13)) { temp &= 0x1fff; ehci->debug = ehci_to_hcd(ehci)->regs + temp; temp = ehci_readl(ehci, &ehci->debug->control); ehci_info(ehci, "debug port %d%s\n", HCS_DEBUG_PORT(ehci->hcs_params), (temp & DBGP_ENABLED) ? " IN USE" : ""); if (!(temp & DBGP_ENABLED)) ehci->debug = NULL; } } ehci_reset(ehci); /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) { ehci_dbg(ehci, "bogus port configuration: " "cc=%d x pcc=%d < ports=%d\n", HCS_N_CC(ehci->hcs_params), HCS_N_PCC(ehci->hcs_params), HCS_N_PORTS(ehci->hcs_params)); switch (pdev->vendor) { case 0x17a0: /* GENESYS */ /* GL880S: should be PORTS=2 */ temp |= (ehci->hcs_params & ~0xf); ehci->hcs_params = temp; break; case PCI_VENDOR_ID_NVIDIA: /* NF4: should be PCC=10 */ break; } } /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); if (pdev->vendor == PCI_VENDOR_ID_STMICRO && pdev->device == PCI_DEVICE_ID_STMICRO_USB_HOST) ehci->sbrn = 0x20; /* ConneXT has no sbrn register */ /* Keep this around for a while just in case some EHCI * implementation uses legacy PCI PM support. This test * can be removed on 17 Dec 2009 if the dev_warn() hasn't * been triggered by then. */ if (!device_can_wakeup(&pdev->dev)) { u16 port_wake; pci_read_config_word(pdev, 0x62, &port_wake); if (port_wake & 0x0001) { dev_warn(&pdev->dev, "Enabling legacy PCI PM\n"); device_set_wakeup_capable(&pdev->dev, 1); } } #ifdef CONFIG_USB_SUSPEND /* REVISIT: the controller works fine for wakeup iff the root hub * itself is "globally" suspended, but usbcore currently doesn't * understand such things. * * System suspend currently expects to be able to suspend the entire * device tree, device-at-a-time. If we failed selective suspend * reports, system suspend would fail; so the root hub code must claim * success. That's lying to usbcore, and it matters for runtime * PM scenarios with selective suspend and remote wakeup... */ if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev)) ehci_warn(ehci, "selective suspend/wakeup unavailable\n"); #endif ehci_port_power(ehci, 1); retval = ehci_pci_reinit(ehci, pdev); done: return retval; }
/* called during probe() after chip reset completes */ static int ehci_pci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); u32 temp; int retval; switch (pdev->vendor) { case PCI_VENDOR_ID_TOSHIBA_2: /* celleb's companion chip */ if (pdev->device == 0x01b5) { #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO ehci->big_endian_mmio = 1; #else ehci_warn(ehci, "unsupported big endian Toshiba quirk\n"); #endif } break; } ehci->caps = hcd->regs; ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* ehci_init() causes memory for DMA transfers to be * allocated. Thus, any vendor-specific workarounds based on * limiting the type of memory used for DMA transfers must * happen before ehci_init() is called. */ switch (pdev->vendor) { case PCI_VENDOR_ID_NVIDIA: /* NVidia reports that certain chips don't handle * QH, ITD, or SITD addresses above 2GB. (But TD, * data buffer, and periodic schedule are normal.) */ switch (pdev->device) { case 0x003c: /* MCP04 */ case 0x005b: /* CK804 */ case 0x00d8: /* CK8 */ case 0x00e8: /* CK8S */ if (pci_set_consistent_dma_mask(pdev, DMA_31BIT_MASK) < 0) ehci_warn(ehci, "can't enable NVidia " "workaround for >2GB RAM\n"); break; } break; } /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); retval = ehci_halt(ehci); if (retval) return retval; /* data structure init */ retval = ehci_init(hcd); if (retval) return retval; #ifdef CONFIG_BUFFALO_PLATFORM if (hcd->self.controller->bus == &pci_bus_type) { #endif /* CONFIG_BUFFALO_PLATFORM */ switch (pdev->vendor) { case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { ehci->is_tdi_rh_tt = 1; tdi_reset(ehci); } break; case PCI_VENDOR_ID_AMD: /* AMD8111 EHCI doesn't work, according to AMD errata */ if (pdev->device == 0x7463) { ehci_info(ehci, "ignoring AMD8111 (errata)\n"); retval = -EIO; goto done; } break; case PCI_VENDOR_ID_NVIDIA: switch (pdev->device) { /* Some NForce2 chips have problems with selective suspend; * fixed in newer silicon. */ case 0x0068: pci_read_config_dword(pdev, PCI_REVISION_ID, &temp); if ((temp & 0xff) < 0xa4) ehci->no_selective_suspend = 1; break; } break; } if (ehci_is_TDI(ehci)) ehci_reset(ehci); #ifdef CONFIG_BUFFALO_PLATFORM } #endif /* CONFIG_BUFFALO_PLATFORM */ /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) { ehci_dbg(ehci, "bogus port configuration: " "cc=%d x pcc=%d < ports=%d\n", HCS_N_CC(ehci->hcs_params), HCS_N_PCC(ehci->hcs_params), HCS_N_PORTS(ehci->hcs_params)); #ifdef CONFIG_BUFFALO_PLATFORM if (hcd->self.controller->bus == &pci_bus_type) { #endif /* CONFIG_BUFFALO_PLATFORM */ switch (pdev->vendor) { case 0x17a0: /* GENESYS */ /* GL880S: should be PORTS=2 */ temp |= (ehci->hcs_params & ~0xf); ehci->hcs_params = temp; break; case PCI_VENDOR_ID_NVIDIA: /* NF4: should be PCC=10 */ break; } #ifdef CONFIG_BUFFALO_PLATFORM } #endif /* CONFIG_BUFFALO_PLATFORM */ } #ifdef CONFIG_BUFFALO_PLATFORM if (hcd->self.controller->bus == &pci_bus_type) { #endif /* CONFIG_BUFFALO_PLATFORM */ /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); /* Workaround current PCI init glitch: wakeup bits aren't * being set from PCI PM capability. */ if (!device_can_wakeup(&pdev->dev)) { u16 port_wake; pci_read_config_word(pdev, 0x62, &port_wake); if (port_wake & 0x0001) device_init_wakeup(&pdev->dev, 1); } #ifdef CONFIG_USB_SUSPEND /* REVISIT: the controller works fine for wakeup iff the root hub * itself is "globally" suspended, but usbcore currently doesn't * understand such things. * * System suspend currently expects to be able to suspend the entire * device tree, device-at-a-time. If we failed selective suspend * reports, system suspend would fail; so the root hub code must claim * success. That's lying to usbcore, and it matters for for runtime * PM scenarios with selective suspend and remote wakeup... */ if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev)) ehci_warn(ehci, "selective suspend/wakeup unavailable\n"); #endif retval = ehci_pci_reinit(ehci, pdev); #ifdef CONFIG_BUFFALO_PLATFORM } #endif /* CONFIG_BUFFALO_PLATFORM */ done: return retval; }
/* caller has locked the root hub, and should reset/reinit on error */ static int ehci_hub_resume (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; int i; int intr_enable; if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); /* re-init operational registers in case we lost power */ if (readl (&ehci->regs->intr_enable) == 0) { /* at least some APM implementations will try to deliver * IRQs right away, so delay them until we're ready. */ intr_enable = 1; writel (0, &ehci->regs->segment); writel (ehci->periodic_dma, &ehci->regs->frame_list); writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next); } else intr_enable = 0; ehci_dbg(ehci, "resume root hub%s\n", intr_enable ? " after power loss" : ""); /* restore CMD_RUN, framelist size, and irq threshold */ writel (ehci->command, &ehci->regs->command); /* take ports out of suspend */ i = HCS_N_PORTS (ehci->hcs_params); while (i--) { temp = readl (&ehci->regs->port_status [i]); temp &= ~(PORT_RWC_BITS | PORT_WKOC_E | PORT_WKDISC_E | PORT_WKCONN_E); if (temp & PORT_SUSPEND) { ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); temp |= PORT_RESUME; } writel (temp, &ehci->regs->port_status [i]); } i = HCS_N_PORTS (ehci->hcs_params); mdelay (20); while (i--) { temp = readl (&ehci->regs->port_status [i]); if ((temp & PORT_SUSPEND) == 0) continue; temp &= ~(PORT_RWC_BITS | PORT_RESUME); writel (temp, &ehci->regs->port_status [i]); ehci_vdbg (ehci, "resumed port %d\n", i + 1); } (void) readl (&ehci->regs->command); /* maybe re-activate the schedule(s) */ temp = 0; if (ehci->async->qh_next.qh) temp |= CMD_ASE; if (ehci->periodic_sched) temp |= CMD_PSE; if (temp) { ehci->command |= temp; writel (ehci->command, &ehci->regs->command); } ehci->next_statechange = jiffies + msecs_to_jiffies(5); hcd->state = HC_STATE_RUNNING; /* Now we can safely re-enable irqs */ if (intr_enable) writel (INTR_MASK, &ehci->regs->intr_enable); spin_unlock_irq (&ehci->lock); return 0; }
static int ehci_hub_control ( struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength ) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); int ports = HCS_N_PORTS (ehci->hcs_params); u32 temp, status; unsigned long flags; int retval = 0; /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. * HCS_INDICATOR may say we can change LEDs to off/amber/green. * (track current state ourselves) ... blink for diagnostics, * power, "this is the one", etc. EHCI spec supports this. */ spin_lock_irqsave (&ehci->lock, flags); switch (typeReq) { case ClearHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case ClearPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; switch (wValue) { case USB_PORT_FEAT_ENABLE: writel (temp & ~PORT_PE, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_ENABLE: writel((temp & ~PORT_RWC_BITS) | PORT_PEC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_SUSPEND: if (temp & PORT_RESET) goto error; if (temp & PORT_SUSPEND) { if ((temp & PORT_PE) == 0) goto error; /* resume signaling for 20 msec */ temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); writel (temp | PORT_RESUME, &ehci->regs->port_status [wIndex]); ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (20); } break; case USB_PORT_FEAT_C_SUSPEND: /* we auto-clear this feature */ break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp & ~(PORT_RWC_BITS | PORT_POWER), &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_CONNECTION: writel((temp & ~PORT_RWC_BITS) | PORT_CSC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_OVER_CURRENT: writel((temp & ~PORT_RWC_BITS) | PORT_OCC, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ break; default: goto error; } readl (&ehci->regs->command); /* unblock posted write */ break; case GetHubDescriptor: ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *) buf); break; case GetHubStatus: /* no hub-wide feature/status flags */ memset (buf, 0, 4); //cpu_to_le32s ((u32 *) buf); break; case GetPortStatus: if (!wIndex || wIndex > ports) goto error; wIndex--; status = 0; temp = readl (&ehci->regs->port_status [wIndex]); // wPortChange bits if (temp & PORT_CSC) status |= 1 << USB_PORT_FEAT_C_CONNECTION; if (temp & PORT_PEC) status |= 1 << USB_PORT_FEAT_C_ENABLE; if (temp & PORT_OCC) status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT; /* whoever resumes must GetPortStatus to complete it!! */ if ((temp & PORT_RESUME) && time_after (jiffies, ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_SUSPEND; ehci->reset_done [wIndex] = 0; /* stop resume signaling */ temp = readl (&ehci->regs->port_status [wIndex]); writel (temp & ~(PORT_RWC_BITS | PORT_RESUME), &ehci->regs->port_status [wIndex]); retval = handshake ( &ehci->regs->port_status [wIndex], PORT_RESUME, 0, 2000 /* 2msec */); if (retval != 0) { ehci_err (ehci, "port %d resume error %d\n", wIndex + 1, retval); goto error; } temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); } /* whoever resets must GetPortStatus to complete it!! */ if ((temp & PORT_RESET) && time_after (jiffies, ehci->reset_done [wIndex])) { status |= 1 << USB_PORT_FEAT_C_RESET; ehci->reset_done [wIndex] = 0; /* force reset to complete */ writel (temp & ~(PORT_RWC_BITS | PORT_RESET), &ehci->regs->port_status [wIndex]); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ retval = handshake ( &ehci->regs->port_status [wIndex], PORT_RESET, 0, 750); if (retval != 0) { ehci_err (ehci, "port %d reset error %d\n", wIndex + 1, retval); goto error; } /* see what we found out */ temp = check_reset_complete (ehci, wIndex, readl (&ehci->regs->port_status [wIndex])); } // don't show wPortStatus if it's owned by a companion hc if (!(temp & PORT_OWNER)) { if (temp & PORT_CONNECT) { status |= 1 << USB_PORT_FEAT_CONNECTION; // status may be from integrated TT status |= ehci_port_speed(ehci, temp); } if (temp & PORT_PE) status |= 1 << USB_PORT_FEAT_ENABLE; if (temp & (PORT_SUSPEND|PORT_RESUME)) status |= 1 << USB_PORT_FEAT_SUSPEND; if (temp & PORT_OC) status |= 1 << USB_PORT_FEAT_OVER_CURRENT; if (temp & PORT_RESET) status |= 1 << USB_PORT_FEAT_RESET; if (temp & PORT_POWER) status |= 1 << USB_PORT_FEAT_POWER; } #ifndef EHCI_VERBOSE_DEBUG if (status & ~0xffff) /* only if wPortChange is interesting */ #endif dbg_port (ehci, "GetStatus", wIndex + 1, temp); // we "know" this alignment is good, caller used kmalloc()... *((__le32 *) buf) = cpu_to_le32 (status); break; case SetHubFeature: switch (wValue) { case C_HUB_LOCAL_POWER: case C_HUB_OVER_CURRENT: /* no hub-wide feature/status flags */ break; default: goto error; } break; case SetPortFeature: if (!wIndex || wIndex > ports) goto error; wIndex--; temp = readl (&ehci->regs->port_status [wIndex]); if (temp & PORT_OWNER) break; temp &= ~PORT_RWC_BITS; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) goto error; if (hcd->remote_wakeup) temp |= PORT_WAKE_BITS; writel (temp | PORT_SUSPEND, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_POWER: if (HCS_PPC (ehci->hcs_params)) writel (temp | PORT_POWER, &ehci->regs->port_status [wIndex]); break; case USB_PORT_FEAT_RESET: if (temp & PORT_RESUME) goto error; /* line status bits may report this as low speed, * which can be fine if this root hub has a * transaction translator built in. */ if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT && !ehci_is_TDI(ehci) && PORT_USB11 (temp)) { ehci_dbg (ehci, "port %d low speed --> companion\n", wIndex + 1); temp |= PORT_OWNER; } else { ehci_vdbg (ehci, "port %d reset\n", wIndex + 1); temp |= PORT_RESET; temp &= ~PORT_PE; /* * caller must wait, then call GetPortStatus * usb 2.0 spec says 50 ms resets on root */ ehci->reset_done [wIndex] = jiffies + msecs_to_jiffies (50); } writel (temp, &ehci->regs->port_status [wIndex]); break; default: goto error; } readl (&ehci->regs->command); /* unblock posted writes */ break; default: error: /* "stall" on error */ retval = -EPIPE; } spin_unlock_irqrestore (&ehci->lock, flags); return retval; }
/* called during probe() after chip reset completes */ static int ehci_pci_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct pci_dev *p_smbus; u8 rev; u32 temp; int retval; switch (pdev->vendor) { case PCI_VENDOR_ID_TOSHIBA_2: /* celleb's companion chip */ if (pdev->device == 0x01b5) { #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO ehci->big_endian_mmio = 1; #else ehci_warn(ehci, "unsupported big endian Toshiba quirk\n"); #endif } break; } ehci->caps = hcd->regs; ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); /* ehci_init() causes memory for DMA transfers to be * allocated. Thus, any vendor-specific workarounds based on * limiting the type of memory used for DMA transfers must * happen before ehci_init() is called. */ switch (pdev->vendor) { case PCI_VENDOR_ID_NVIDIA: /* NVidia reports that certain chips don't handle * QH, ITD, or SITD addresses above 2GB. (But TD, * data buffer, and periodic schedule are normal.) */ switch (pdev->device) { case 0x003c: /* MCP04 */ case 0x005b: /* CK804 */ case 0x00d8: /* CK8 */ case 0x00e8: /* CK8S */ if (pci_set_consistent_dma_mask(pdev, DMA_31BIT_MASK) < 0) ehci_warn(ehci, "can't enable NVidia " "workaround for >2GB RAM\n"); break; } break; } /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); retval = ehci_halt(ehci); if (retval) return retval; /* data structure init */ retval = ehci_init(hcd); if (retval) return retval; switch (pdev->vendor) { case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { hcd->has_tt = 1; tdi_reset(ehci); } break; case PCI_VENDOR_ID_AMD: /* AMD8111 EHCI doesn't work, according to AMD errata */ if (pdev->device == 0x7463) { ehci_info(ehci, "ignoring AMD8111 (errata)\n"); retval = -EIO; goto done; } break; case PCI_VENDOR_ID_NVIDIA: switch (pdev->device) { /* Some NForce2 chips have problems with selective suspend; * fixed in newer silicon. */ case 0x0068: if (pdev->revision < 0xa4) ehci->no_selective_suspend = 1; break; } break; case PCI_VENDOR_ID_VIA: if (pdev->device == 0x3104 && (pdev->revision & 0xf0) == 0x60) { u8 tmp; /* The VT6212 defaults to a 1 usec EHCI sleep time which * hogs the PCI bus *badly*. Setting bit 5 of 0x4B makes * that sleep time use the conventional 10 usec. */ pci_read_config_byte(pdev, 0x4b, &tmp); if (tmp & 0x20) break; pci_write_config_byte(pdev, 0x4b, tmp | 0x20); } break; case PCI_VENDOR_ID_ATI: /* SB600 and old version of SB700 have a bug in EHCI controller, * which causes usb devices lose response in some cases. */ if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) { p_smbus = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); if (!p_smbus) break; rev = p_smbus->revision; if ((pdev->device == 0x4386) || (rev == 0x3a) || (rev == 0x3b)) { u8 tmp; ehci_info(ehci, "applying AMD SB600/SB700 USB " "freeze workaround\n"); pci_read_config_byte(pdev, 0x53, &tmp); pci_write_config_byte(pdev, 0x53, tmp | (1<<3)); } pci_dev_put(p_smbus); } break; } ehci_reset(ehci); /* at least the Genesys GL880S needs fixup here */ temp = HCS_N_CC(ehci->hcs_params) * HCS_N_PCC(ehci->hcs_params); temp &= 0x0f; if (temp && HCS_N_PORTS(ehci->hcs_params) > temp) { ehci_dbg(ehci, "bogus port configuration: " "cc=%d x pcc=%d < ports=%d\n", HCS_N_CC(ehci->hcs_params), HCS_N_PCC(ehci->hcs_params), HCS_N_PORTS(ehci->hcs_params)); switch (pdev->vendor) { case 0x17a0: /* GENESYS */ /* GL880S: should be PORTS=2 */ temp |= (ehci->hcs_params & ~0xf); ehci->hcs_params = temp; break; case PCI_VENDOR_ID_NVIDIA: /* NF4: should be PCC=10 */ break; } } /* Serial Bus Release Number is at PCI 0x60 offset */ pci_read_config_byte(pdev, 0x60, &ehci->sbrn); /* Keep this around for a while just in case some EHCI * implementation uses legacy PCI PM support. This test * can be removed on 17 Dec 2009 if the dev_warn() hasn't * been triggered by then. */ if (!device_can_wakeup(&pdev->dev)) { u16 port_wake; pci_read_config_word(pdev, 0x62, &port_wake); if (port_wake & 0x0001) { dev_warn(&pdev->dev, "Enabling legacy PCI PM\n"); device_set_wakeup_capable(&pdev->dev, 1); } } #ifdef CONFIG_USB_SUSPEND /* REVISIT: the controller works fine for wakeup iff the root hub * itself is "globally" suspended, but usbcore currently doesn't * understand such things. * * System suspend currently expects to be able to suspend the entire * device tree, device-at-a-time. If we failed selective suspend * reports, system suspend would fail; so the root hub code must claim * success. That's lying to usbcore, and it matters for for runtime * PM scenarios with selective suspend and remote wakeup... */ if (ehci->no_selective_suspend && device_can_wakeup(&pdev->dev)) ehci_warn(ehci, "selective suspend/wakeup unavailable\n"); #endif ehci_port_power(ehci, 1); retval = ehci_pci_reinit(ehci, pdev); done: return retval; }