/** * omap_ehci_init - initialises the USB host EHCI controller * @isc: omap ehci device context * * This initialisation routine is quite heavily based on the work done by the * OMAP Linux team (for which I thank them very much). The init sequence is * almost identical, diverging only for the FreeBSD specifics. * * LOCKING: * none * * RETURNS: * 0 on success, a negative error code on failure. */ static int omap_ehci_init(struct omap_ehci_softc *isc) { uint32_t reg = 0; int i; device_t uhh_dev; uhh_dev = device_get_parent(isc->sc_dev); device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); /* Set the interrupt threshold control, it controls the maximum rate at * which the host controller issues interrupts. We set it to 1 microframe * at startup - the default is 8 mircoframes (equates to 1ms). */ reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD); reg &= 0xff00ffff; reg |= (1 << 16); omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg); /* Soft reset the PHY using PHY reset command over ULPI */ for (i = 0; i < OMAP_HS_USB_PORTS; i++) { if (omap_usb_port_mode(uhh_dev, i) == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(isc, i); } return(0); }
/** * ehci_hcd_omap_probe - initialize TI-based HCDs * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. */ static int ehci_hcd_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ehci_hcd_omap_platform_data *pdata = dev->platform_data; struct resource *res; struct usb_hcd *hcd; void __iomem *regs; struct ehci_hcd *omap_ehci; int ret = -ENODEV; int irq; if (usb_disabled()) return -ENODEV; if (!dev->parent) { dev_err(dev, "Missing parent device\n"); return -ENODEV; } irq = platform_get_irq_byname(pdev, "ehci-irq"); if (irq < 0) { dev_err(dev, "EHCI irq failed\n"); return -ENODEV; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci"); if (!res) { dev_err(dev, "UHH EHCI get resource failed\n"); return -ENODEV; } regs = ioremap(res->start, resource_size(res)); if (!regs) { dev_err(dev, "UHH EHCI ioremap failed\n"); return -ENOMEM; } hcd = usb_create_hcd(&ehci_omap_hc_driver, dev, dev_name(dev)); if (!hcd) { dev_err(dev, "failed to create hcd with err %d\n", ret); ret = -ENOMEM; goto err_io; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); hcd->regs = regs; ret = omap_usbhs_enable(dev); if (ret) { dev_err(dev, "failed to start usbhs with err %d\n", ret); goto err_enable; } /* * An undocumented "feature" in the OMAP3 EHCI controller, * causes suspended ports to be taken out of suspend when * the USBCMD.Run/Stop bit is cleared (for example when * we do ehci_bus_suspend). * This breaks suspend-resume if the root-hub is allowed * to suspend. Writing 1 to this undocumented register bit * disables this feature and restores normal behavior. */ ehci_write(regs, EHCI_INSNREG04, EHCI_INSNREG04_DISABLE_UNSUSPEND); /* Soft reset the PHY using PHY reset command over ULPI */ if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) omap_ehci_soft_phy_reset(pdev, 0); if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) omap_ehci_soft_phy_reset(pdev, 1); omap_ehci = hcd_to_ehci(hcd); omap_ehci->sbrn = 0x20; /* we know this is the memory we want, no need to ioremap again */ omap_ehci->caps = hcd->regs; omap_ehci->regs = hcd->regs + HC_LENGTH(readl(&omap_ehci->caps->hc_capbase)); dbg_hcs_params(omap_ehci, "reset"); dbg_hcc_params(omap_ehci, "reset"); /* cache this readonly data; minimize chip reads */ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; } /* root ports should always stay powered */ ehci_port_power(omap_ehci, 1); return 0; err_add_hcd: omap_usbhs_disable(dev); err_enable: usb_put_hcd(hcd); err_io: return ret; }
/** * ehci_hcd_omap_probe - initialize TI-based HCDs * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. */ static int ehci_hcd_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct ehci_hcd_omap_platform_data *pdata = dev->platform_data; struct resource *res; struct usb_hcd *hcd; void __iomem *regs; struct ehci_hcd *omap_ehci; int ret = -ENODEV; int irq; int i; char supply[7]; if (usb_disabled()) return -ENODEV; if (!dev->parent) { dev_err(dev, "Missing parent device\n"); return -ENODEV; } irq = platform_get_irq_byname(pdev, "ehci-irq"); if (irq < 0) { dev_err(dev, "EHCI irq failed\n"); return -ENODEV; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci"); if (!res) { dev_err(dev, "UHH EHCI get resource failed\n"); return -ENODEV; } regs = ioremap(res->start, resource_size(res)); if (!regs) { dev_err(dev, "UHH EHCI ioremap failed\n"); return -ENOMEM; } hcd = usb_create_hcd(&ehci_omap_hc_driver, dev, dev_name(dev)); if (!hcd) { dev_err(dev, "failed to create hcd with err %d\n", ret); ret = -ENOMEM; goto err_io; } hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); hcd->regs = regs; /* get ehci regulator and enable */ for (i = 0 ; i < OMAP3_HS_USB_PORTS ; i++) { if (pdata->port_mode[i] != OMAP_EHCI_PORT_MODE_PHY) { pdata->regulator[i] = NULL; continue; } snprintf(supply, sizeof(supply), "hsusb%d", i); pdata->regulator[i] = regulator_get(dev, supply); if (IS_ERR(pdata->regulator[i])) { pdata->regulator[i] = NULL; dev_dbg(dev, "failed to get ehci port%d regulator\n", i); } else { regulator_enable(pdata->regulator[i]); } } if (pdata->phy_reset) { if (gpio_is_valid(pdata->reset_gpio_port[0])) gpio_request_one(pdata->reset_gpio_port[0], GPIOF_OUT_INIT_LOW, "USB1 PHY reset"); if (gpio_is_valid(pdata->reset_gpio_port[1])) gpio_request_one(pdata->reset_gpio_port[1], GPIOF_OUT_INIT_LOW, "USB2 PHY reset"); /* Hold the PHY in RESET for enough time till DIR is high */ udelay(10); } pm_runtime_enable(dev); pm_runtime_get_sync(dev); /* * An undocumented "feature" in the OMAP3 EHCI controller, * causes suspended ports to be taken out of suspend when * the USBCMD.Run/Stop bit is cleared (for example when * we do ehci_bus_suspend). * This breaks suspend-resume if the root-hub is allowed * to suspend. Writing 1 to this undocumented register bit * disables this feature and restores normal behavior. */ ehci_write(regs, EHCI_INSNREG04, EHCI_INSNREG04_DISABLE_UNSUSPEND); /* Soft reset the PHY using PHY reset command over ULPI */ if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) omap_ehci_soft_phy_reset(pdev, 0); if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) omap_ehci_soft_phy_reset(pdev, 1); omap_ehci = hcd_to_ehci(hcd); omap_ehci->sbrn = 0x20; /* we know this is the memory we want, no need to ioremap again */ omap_ehci->caps = hcd->regs; omap_ehci->regs = hcd->regs + HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase)); dbg_hcs_params(omap_ehci, "reset"); dbg_hcc_params(omap_ehci, "reset"); /* cache this readonly data; minimize chip reads */ omap_ehci->hcs_params = readl(&omap_ehci->caps->hcs_params); ehci_reset(omap_ehci); if (pdata->phy_reset) { /* Hold the PHY in RESET for enough time till * PHY is settled and ready */ udelay(10); if (gpio_is_valid(pdata->reset_gpio_port[0])) gpio_set_value(pdata->reset_gpio_port[0], 1); if (gpio_is_valid(pdata->reset_gpio_port[1])) gpio_set_value(pdata->reset_gpio_port[1], 1); } ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) { dev_err(dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; } /* root ports should always stay powered */ ehci_port_power(omap_ehci, 1); return 0; err_add_hcd: disable_put_regulator(pdata); pm_runtime_put_sync(dev); err_io: iounmap(regs); return ret; }
/* * Initialize the OMAP EHCI controller and PHY. * Based on "drivers/usb/host/ehci-omap.c" from Linux 3.1 * See there for additional Copyrights. */ int omap_ehci_hcd_init(struct omap_usbhs_board_data *usbhs_pdata) { int ret; unsigned int i, reg = 0, rev = 0; debug("Initializing OMAP EHCI\n"); ret = board_usb_init(); if (ret < 0) return ret; /* Put the PHY in RESET */ omap_ehci_phy_reset(1, 10); ret = omap_uhh_reset(); if (ret < 0) return ret; ret = omap_ehci_tll_reset(); if (ret) return ret; writel(OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | OMAP_USBTLL_SYSCONFIG_SIDLEMODE | OMAP_USBTLL_SYSCONFIG_CACTIVITY, &usbtll->sysc); /* Put UHH in NoIdle/NoStandby mode */ writel(OMAP_UHH_SYSCONFIG_VAL, &uhh->sysc); /* setup ULPI bypass and burst configurations */ clrsetbits_le32(®, OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN, (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN)); rev = readl(&uhh->rev); if (rev == OMAP_USBHS_REV1) { if (is_ehci_phy_mode(usbhs_pdata->port_mode[0])) clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); else setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); if (is_ehci_phy_mode(usbhs_pdata->port_mode[1])) clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS); else setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); if (is_ehci_phy_mode(usbhs_pdata->port_mode[2])) clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS); else setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS); } else if (rev == OMAP_USBHS_REV2) { clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR), OMAP4_UHH_HOSTCONFIG_APP_START_CLK); /* Clear port mode fields for PHY mode*/ if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0])) setbits_le32(®, OMAP_P1_MODE_HSIC); if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1])) setbits_le32(®, OMAP_P2_MODE_HSIC); if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2])) setbits_le32(®, OMAP_P3_MODE_HSIC); } debug("OMAP UHH_REVISION 0x%x\n", rev); writel(reg, &uhh->hostconfig); for (i = 0; i < OMAP_HS_USB_PORTS; i++) if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i])) omap_usbhs_hsic_init(i); omap_ehci_phy_reset(0, 10); /* * An undocumented "feature" in the OMAP3 EHCI controller, * causes suspended ports to be taken out of suspend when * the USBCMD.Run/Stop bit is cleared (for example when * we do ehci_bus_suspend). * This breaks suspend-resume if the root-hub is allowed * to suspend. Writing 1 to this undocumented register bit * disables this feature and restores normal behavior. */ writel(EHCI_INSNREG04_DISABLE_UNSUSPEND, &ehci->insreg04); for (i = 0; i < OMAP_HS_USB_PORTS; i++) if (is_ehci_phy_mode(usbhs_pdata->port_mode[i])) omap_ehci_soft_phy_reset(i); dcache_disable(); hccr = (struct ehci_hccr *)(OMAP_EHCI_BASE); hcor = (struct ehci_hcor *)(OMAP_EHCI_BASE + 0x10); debug("OMAP EHCI init done\n"); return 0; }
/* omap_start_ehc * - Start the TI USBHOST controller */ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) { unsigned long timeout = jiffies + msecs_to_jiffies(1000); u8 tll_ch_mask = 0; unsigned reg = 0; int ret = 0; dev_dbg(omap->dev, "starting TI EHCI USB Controller\n"); /* Enable Clocks for USBHOST */ omap->usbhost_ick = clk_get(omap->dev, "usbhost_ick"); if (IS_ERR(omap->usbhost_ick)) { ret = PTR_ERR(omap->usbhost_ick); goto err_host_ick; } clk_enable(omap->usbhost_ick); omap->usbhost_hs_fck = clk_get(omap->dev, "hs_fck"); if (IS_ERR(omap->usbhost_hs_fck)) { ret = PTR_ERR(omap->usbhost_hs_fck); goto err_host_120m_fck; } clk_enable(omap->usbhost_hs_fck); omap->usbhost_fs_fck = clk_get(omap->dev, "fs_fck"); if (IS_ERR(omap->usbhost_fs_fck)) { ret = PTR_ERR(omap->usbhost_fs_fck); goto err_host_48m_fck; } clk_enable(omap->usbhost_fs_fck); if (omap->phy_reset) { /* Refer: ISSUE1 */ if (gpio_is_valid(omap->reset_gpio_port[0])) { gpio_request(omap->reset_gpio_port[0], "USB1 PHY reset"); gpio_direction_output(omap->reset_gpio_port[0], 0); } if (gpio_is_valid(omap->reset_gpio_port[1])) { gpio_request(omap->reset_gpio_port[1], "USB2 PHY reset"); gpio_direction_output(omap->reset_gpio_port[1], 0); } /* Hold the PHY in RESET for enough time till DIR is high */ udelay(10); } /* Configure TLL for 60Mhz clk for ULPI */ omap->usbtll_fck = clk_get(omap->dev, "usbtll_fck"); if (IS_ERR(omap->usbtll_fck)) { ret = PTR_ERR(omap->usbtll_fck); goto err_tll_fck; } clk_enable(omap->usbtll_fck); omap->usbtll_ick = clk_get(omap->dev, "usbtll_ick"); if (IS_ERR(omap->usbtll_ick)) { ret = PTR_ERR(omap->usbtll_ick); goto err_tll_ick; } clk_enable(omap->usbtll_ick); omap->omap_ehci_rev = ehci_omap_readl(omap->uhh_base, OMAP_UHH_REVISION); dev_dbg(omap->dev, "OMAP UHH_REVISION 0x%x\n", omap->omap_ehci_rev); /* * Enable per-port clocks as needed (newer controllers only). * - External ULPI clock for PHY mode * - Internal clocks for TLL and HSIC modes (TODO) */ if (is_omap_ehci_rev2(omap)) { switch (omap->port_mode[0]) { case EHCI_HCD_OMAP_MODE_PHY: omap->xclk60mhsp1_ck = clk_get(omap->dev, "xclk60mhsp1_ck"); if (IS_ERR(omap->xclk60mhsp1_ck)) { ret = PTR_ERR(omap->xclk60mhsp1_ck); dev_err(omap->dev, "Unable to get Port1 ULPI clock\n"); } omap->utmi_p1_fck = clk_get(omap->dev, "utmi_p1_gfclk"); if (IS_ERR(omap->utmi_p1_fck)) { ret = PTR_ERR(omap->utmi_p1_fck); dev_err(omap->dev, "Unable to get utmi_p1_fck\n"); } ret = clk_set_parent(omap->utmi_p1_fck, omap->xclk60mhsp1_ck); if (ret != 0) { dev_err(omap->dev, "Unable to set P1 f-clock\n"); } break; case EHCI_HCD_OMAP_MODE_TLL: /* TODO */ default: break; } switch (omap->port_mode[1]) { case EHCI_HCD_OMAP_MODE_PHY: omap->xclk60mhsp2_ck = clk_get(omap->dev, "xclk60mhsp2_ck"); if (IS_ERR(omap->xclk60mhsp2_ck)) { ret = PTR_ERR(omap->xclk60mhsp2_ck); dev_err(omap->dev, "Unable to get Port2 ULPI clock\n"); } omap->utmi_p2_fck = clk_get(omap->dev, "utmi_p2_gfclk"); if (IS_ERR(omap->utmi_p2_fck)) { ret = PTR_ERR(omap->utmi_p2_fck); dev_err(omap->dev, "Unable to get utmi_p2_fck\n"); } ret = clk_set_parent(omap->utmi_p2_fck, omap->xclk60mhsp2_ck); if (ret != 0) { dev_err(omap->dev, "Unable to set P2 f-clock\n"); } break; case EHCI_HCD_OMAP_MODE_TLL: /* TODO */ default: break; } } /* perform TLL soft reset, and wait until reset is complete */ ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, OMAP_USBTLL_SYSCONFIG_SOFTRESET); /* Wait for TLL reset to complete */ while (!(ehci_omap_readl(omap->tll_base, OMAP_USBTLL_SYSSTATUS) & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { cpu_relax(); if (time_after(jiffies, timeout)) { dev_dbg(omap->dev, "operation timed out\n"); ret = -EINVAL; goto err_sys_status; } } dev_dbg(omap->dev, "TLL RESET DONE\n"); /* (1<<3) = no idle mode only for initial debugging */ ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | OMAP_USBTLL_SYSCONFIG_SIDLEMODE | OMAP_USBTLL_SYSCONFIG_CACTIVITY); /* Put UHH in NoIdle/NoStandby mode */ reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG); if (is_omap_ehci_rev1(omap)) { reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP | OMAP_UHH_SYSCONFIG_SIDLEMODE | OMAP_UHH_SYSCONFIG_CACTIVITY | OMAP_UHH_SYSCONFIG_MIDLEMODE); reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; } else if (is_omap_ehci_rev2(omap)) { reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; } ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG); /* setup ULPI bypass and burst configurations */ reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; if (is_omap_ehci_rev1(omap)) { if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; /* Bypass the TLL module for PHY mode operation */ if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) { dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n"); if (is_ehci_phy_mode(omap->port_mode[0]) || is_ehci_phy_mode(omap->port_mode[1]) || is_ehci_phy_mode(omap->port_mode[2])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; else reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; } else { dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); if (is_ehci_phy_mode(omap->port_mode[0])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; else if (is_ehci_tll_mode(omap->port_mode[0])) reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; if (is_ehci_phy_mode(omap->port_mode[1])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; else if (is_ehci_tll_mode(omap->port_mode[1])) reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; if (is_ehci_phy_mode(omap->port_mode[2])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; else if (is_ehci_tll_mode(omap->port_mode[2])) reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; } } else if (is_omap_ehci_rev2(omap)) { /* Clear port mode fields for PHY mode*/ reg &= ~OMAP4_P1_MODE_CLEAR; reg &= ~OMAP4_P2_MODE_CLEAR; if (is_ehci_tll_mode(omap->port_mode[0])) reg |= OMAP4_P1_MODE_TLL; else if (is_ehci_hsic_mode(omap->port_mode[0])) reg |= OMAP4_P1_MODE_HSIC; if (is_ehci_tll_mode(omap->port_mode[1])) reg |= OMAP4_P2_MODE_TLL; else if (is_ehci_hsic_mode(omap->port_mode[1])) reg |= OMAP4_P2_MODE_HSIC; } ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg); /* * An undocumented "feature" in the OMAP3 EHCI controller, * causes suspended ports to be taken out of suspend when * the USBCMD.Run/Stop bit is cleared (for example when * we do ehci_bus_suspend). * This breaks suspend-resume if the root-hub is allowed * to suspend. Writing 1 to this undocumented register bit * disables this feature and restores normal behavior. */ ehci_omap_writel(omap->ehci_base, EHCI_INSNREG04, EHCI_INSNREG04_DISABLE_UNSUSPEND); if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= OMAP_TLL_CHANNEL_1_EN_MASK; if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= OMAP_TLL_CHANNEL_2_EN_MASK; if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK; /* Enable UTMI mode for required TLL channels */ omap_usb_utmi_init(omap, tll_ch_mask, OMAP_TLL_CHANNEL_COUNT); } if (omap->phy_reset) { /* Refer ISSUE1: * Hold the PHY in RESET for enough time till * PHY is settled and ready */ udelay(10); if (gpio_is_valid(omap->reset_gpio_port[0])) gpio_set_value(omap->reset_gpio_port[0], 1); if (gpio_is_valid(omap->reset_gpio_port[1])) gpio_set_value(omap->reset_gpio_port[1], 1); } /* Soft reset the PHY using PHY reset command over ULPI */ if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(omap, 0); if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(omap, 1); return 0; err_sys_status: clk_disable(omap->utmi_p2_fck); clk_put(omap->utmi_p2_fck); clk_disable(omap->xclk60mhsp2_ck); clk_put(omap->xclk60mhsp2_ck); clk_disable(omap->utmi_p1_fck); clk_put(omap->utmi_p1_fck); clk_disable(omap->xclk60mhsp1_ck); clk_put(omap->xclk60mhsp1_ck); clk_disable(omap->usbtll_ick); clk_put(omap->usbtll_ick); err_tll_ick: clk_disable(omap->usbtll_fck); clk_put(omap->usbtll_fck); err_tll_fck: clk_disable(omap->usbhost_fs_fck); clk_put(omap->usbhost_fs_fck); if (omap->phy_reset) { if (gpio_is_valid(omap->reset_gpio_port[0])) gpio_free(omap->reset_gpio_port[0]); if (gpio_is_valid(omap->reset_gpio_port[1])) gpio_free(omap->reset_gpio_port[1]); } err_host_48m_fck: clk_disable(omap->usbhost_hs_fck); clk_put(omap->usbhost_hs_fck); err_host_120m_fck: clk_disable(omap->usbhost_ick); clk_put(omap->usbhost_ick); err_host_ick: return ret; }
/** * omap_ehci_init - initialises the USB host EHCI controller * @isc: omap ehci device context * * This initialisation routine is quite heavily based on the work done by the * OMAP Linux team (for which I thank them very much). The init sequence is * almost identical, diverging only for the FreeBSD specifics. * * LOCKING: * none * * RETURNS: * 0 on success, a negative error code on failure. */ static int omap_ehci_init(struct omap_ehci_softc *isc) { unsigned long timeout; int ret = 0; uint8_t tll_ch_mask = 0; uint32_t reg = 0; int reset_performed = 0; int i; device_printf(isc->sc_dev, "Starting TI EHCI USB Controller\n"); /* Enable Clocks for high speed USBHOST */ ti_prcm_clk_enable(USBHSHOST_CLK); /* Hold the PHY in reset while configuring */ for (int i = 0; i < 3; i++) { if (isc->phy_reset[i]) { /* Configure the GPIO to drive low (hold in reset) */ if ((isc->reset_gpio_pin[i] != -1) && (isc->sc_gpio_dev != NULL)) { GPIO_PIN_SETFLAGS(isc->sc_gpio_dev, isc->reset_gpio_pin[i], GPIO_PIN_OUTPUT); GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[i], GPIO_PIN_LOW); reset_performed = 1; } } } /* Hold the PHY in RESET for enough time till DIR is high */ if (reset_performed) DELAY(10); /* Read the UHH revision */ isc->ehci_rev = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_REVISION); device_printf(isc->sc_dev, "UHH revision 0x%08x\n", isc->ehci_rev); /* Initilise the low level interface module(s) */ if (isc->ehci_rev == OMAP_EHCI_REV1) { /* Enable the USB TLL */ ti_prcm_clk_enable(USBTLL_CLK); /* Perform TLL soft reset, and wait until reset is complete */ omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_SOFTRESET); /* Set the timeout to 100ms*/ timeout = (hz < 10) ? 1 : ((100 * hz) / 1000); /* Wait for TLL reset to complete */ while ((omap_tll_read_4(isc, OMAP_USBTLL_SYSSTATUS) & TLL_SYSSTATUS_RESETDONE) == 0x00) { /* Sleep for a tick */ pause("USBRESET", 1); if (timeout-- == 0) { device_printf(isc->sc_dev, "TLL reset operation timed out\n"); ret = EINVAL; goto err_sys_status; } } device_printf(isc->sc_dev, "TLL RESET DONE\n"); /* CLOCKACTIVITY = 1 : OCP-derived internal clocks ON during idle * SIDLEMODE = 2 : Smart-idle mode. Sidleack asserted after Idlereq * assertion when no more activity on the USB. * ENAWAKEUP = 1 : Wakeup generation enabled */ omap_tll_write_4(isc, OMAP_USBTLL_SYSCONFIG, TLL_SYSCONFIG_ENAWAKEUP | TLL_SYSCONFIG_AUTOIDLE | TLL_SYSCONFIG_SIDLE_SMART_IDLE | TLL_SYSCONFIG_CACTIVITY); } else if (isc->ehci_rev == OMAP_EHCI_REV2) { /* For OMAP44xx devices you have to enable the per-port clocks: * PHY_MODE - External ULPI clock * TTL_MODE - Internal UTMI clock * HSIC_MODE - Internal 480Mhz and 60Mhz clocks */ if (isc->ehci_rev == OMAP_EHCI_REV2) { if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) { ti_prcm_clk_set_source(USBP1_PHY_CLK, EXT_CLK); ti_prcm_clk_enable(USBP1_PHY_CLK); } else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) ti_prcm_clk_enable(USBP1_UTMI_CLK); else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) ti_prcm_clk_enable(USBP1_HSIC_CLK); if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) { ti_prcm_clk_set_source(USBP2_PHY_CLK, EXT_CLK); ti_prcm_clk_enable(USBP2_PHY_CLK); } else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) ti_prcm_clk_enable(USBP2_UTMI_CLK); else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) ti_prcm_clk_enable(USBP2_HSIC_CLK); } } /* Put UHH in SmartIdle/SmartStandby mode */ reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_SYSCONFIG); if (isc->ehci_rev == OMAP_EHCI_REV1) { reg &= ~(UHH_SYSCONFIG_SIDLEMODE_MASK | UHH_SYSCONFIG_MIDLEMODE_MASK); reg |= (UHH_SYSCONFIG_ENAWAKEUP | UHH_SYSCONFIG_AUTOIDLE | UHH_SYSCONFIG_CLOCKACTIVITY | UHH_SYSCONFIG_SIDLEMODE_SMARTIDLE | UHH_SYSCONFIG_MIDLEMODE_SMARTSTANDBY); } else if (isc->ehci_rev == OMAP_EHCI_REV2) { reg &= ~UHH_SYSCONFIG_IDLEMODE_MASK; reg |= UHH_SYSCONFIG_IDLEMODE_NOIDLE; reg &= ~UHH_SYSCONFIG_STANDBYMODE_MASK; reg |= UHH_SYSCONFIG_STANDBYMODE_NOSTDBY; } omap_uhh_write_4(isc, OMAP_USBHOST_UHH_SYSCONFIG, reg); device_printf(isc->sc_dev, "OMAP_UHH_SYSCONFIG: 0x%08x\n", reg); reg = omap_uhh_read_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG); /* Setup ULPI bypass and burst configurations */ reg |= (UHH_HOSTCONFIG_ENA_INCR4 | UHH_HOSTCONFIG_ENA_INCR8 | UHH_HOSTCONFIG_ENA_INCR16); reg &= ~UHH_HOSTCONFIG_ENA_INCR_ALIGN; if (isc->ehci_rev == OMAP_EHCI_REV1) { if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P1_CONNECT_STATUS; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P2_CONNECT_STATUS; if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) reg &= ~UHH_HOSTCONFIG_P3_CONNECT_STATUS; /* Bypass the TLL module for PHY mode operation */ if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) reg &= ~UHH_HOSTCONFIG_P1_ULPI_BYPASS; else reg |= UHH_HOSTCONFIG_P1_ULPI_BYPASS; } else if (isc->ehci_rev == OMAP_EHCI_REV2) { reg |= UHH_HOSTCONFIG_APP_START_CLK; /* Clear port mode fields for PHY mode*/ reg &= ~UHH_HOSTCONFIG_P1_MODE_MASK; reg &= ~UHH_HOSTCONFIG_P2_MODE_MASK; if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) reg |= UHH_HOSTCONFIG_P1_MODE_UTMI_PHY; else if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_HSIC) reg |= UHH_HOSTCONFIG_P1_MODE_HSIC; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) reg |= UHH_HOSTCONFIG_P2_MODE_UTMI_PHY; else if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_HSIC) reg |= UHH_HOSTCONFIG_P2_MODE_HSIC; } omap_uhh_write_4(isc, OMAP_USBHOST_UHH_HOSTCONFIG, reg); device_printf(isc->sc_dev, "UHH setup done, uhh_hostconfig=0x%08x\n", reg); /* I found the code and comments in the Linux EHCI driver - thanks guys :) * * "An undocumented "feature" in the OMAP3 EHCI controller, causes suspended * ports to be taken out of suspend when the USBCMD.Run/Stop bit is cleared * (for example when we do ehci_bus_suspend). This breaks suspend-resume if * the root-hub is allowed to suspend. Writing 1 to this undocumented * register bit disables this feature and restores normal behavior." */ #if 0 omap_ehci_write_4(isc, OMAP_USBHOST_INSNREG04, OMAP_USBHOST_INSNREG04_DISABLE_UNSUSPEND); #endif /* If any of the ports are configured in TLL mode, enable them */ if ((isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) || (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) || (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL)) { if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= 0x1; if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= 0x2; if (isc->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) tll_ch_mask |= 0x4; /* Enable UTMI mode for required TLL channels */ omap_ehci_utmi_init(isc, tll_ch_mask); } /* Release the PHY reset signal now we have configured everything */ if (reset_performed) { /* Delay for 10ms */ DELAY(10000); for (i = 0; i < 3; i++) { /* Release reset */ if (isc->phy_reset[i] && (isc->reset_gpio_pin[i] != -1) && (isc->sc_gpio_dev != NULL)) { GPIO_PIN_SET(isc->sc_gpio_dev, isc->reset_gpio_pin[i], GPIO_PIN_HIGH); } } } /* Set the interrupt threshold control, it controls the maximum rate at * which the host controller issues interrupts. We set it to 1 microframe * at startup - the default is 8 mircoframes (equates to 1ms). */ reg = omap_ehci_read_4(isc, OMAP_USBHOST_USBCMD); reg &= 0xff00ffff; reg |= (1 << 16); omap_ehci_write_4(isc, OMAP_USBHOST_USBCMD, reg); /* Soft reset the PHY using PHY reset command over ULPI */ if (isc->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(isc, 0); if (isc->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) omap_ehci_soft_phy_reset(isc, 1); return(0); err_sys_status: /* Disable the TLL clocks */ ti_prcm_clk_disable(USBTLL_CLK); /* Disable Clocks for USBHOST */ ti_prcm_clk_disable(USBHSHOST_CLK); return(ret); }
/** * ehci_hcd_omap_probe - initialize TI-based HCDs * * Allocates basic resources for this USB host controller, and * then invokes the start() method for the HCD associated with it * through the hotplug entry's driver_data. */ static int ehci_hcd_omap_probe(struct platform_device *pdev) { struct uhhtll_apis *uhhtllp = pdev->dev.platform_data; struct ehci_hcd_omap *omap; struct usbhs_omap_platform_data *pdata; struct usbhs_omap_resource *omapresp; struct usb_hcd *hcd; int ret = -ENODEV; if (!uhhtllp) { dev_dbg(&pdev->dev, "missing platform_data\n"); goto err_end; } if (usb_disabled()) goto err_end; omap = kzalloc(sizeof(*omap), GFP_KERNEL); if (!omap) { ret = -ENOMEM; goto err_end; } pdata = &omap->platdata; if (uhhtllp->get_platform_data(pdata) != 0) { ret = -EINVAL; goto err_mem; } omapresp = &omap->res; if (uhhtllp->get_resource(OMAP_EHCI, omapresp) != 0) { ret = -EINVAL; goto err_mem; } if (!omapresp->regs) { dev_dbg(&pdev->dev, "failed to EHCI regs\n"); goto err_mem; } hcd = usb_create_hcd(&ehci_omap_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_dbg(&pdev->dev, "failed to create hcd with err %d\n", ret); ret = -ENOMEM; goto err_mem; } platform_set_drvdata(pdev, omap); omap->dev = &pdev->dev; omap->ehci = hcd_to_ehci(hcd); omap->ehci->sbrn = 0x20; hcd->rsrc_start = omapresp->start; hcd->rsrc_len = omapresp->len; hcd->regs = omapresp->regs; /* we know this is the memory we want, no need to ioremap again */ omap->ehci->caps = hcd->regs; ret = uhhtllp->store(OMAP_EHCI, OMAP_USB_HCD, hcd); if (ret) { dev_dbg(&pdev->dev, "failed to store hcd\n"); goto err_regs; } ret = uhhtllp->enable(OMAP_EHCI); if (ret) { dev_dbg(&pdev->dev, "failed to start ehci\n"); goto err_regs; } if (cpu_is_omap34xx()) { /* * An undocumented "feature" in the OMAP3 EHCI controller, * causes suspended ports to be taken out of suspend when * the USBCMD.Run/Stop bit is cleared (for example when * we do ehci_bus_suspend). * This breaks suspend-resume if the root-hub is allowed * to suspend. Writing 1 to this undocumented register bit * disables this feature and restores normal behavior. */ ehci_omap_writel(omap->res.regs, EHCI_INSNREG04, EHCI_INSNREG04_DISABLE_UNSUSPEND); /* Soft reset the PHY using PHY reset command over ULPI */ if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_PHY) omap_ehci_soft_phy_reset(omap, 0); if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_PHY) omap_ehci_soft_phy_reset(omap, 1); } if (cpu_is_omap44xx()) { /* * Undocumented HW Errata for OMAP4 TLL * the IDGND ULPI bits generate an interrupt when the * controller is enabled from OFF mode. This causes the * port status to be reported as disabled. Mask these * interrupts since TLL does not require them. */ if (pdata->port_mode[0] == OMAP_EHCI_PORT_MODE_TLL) { omap_writeb(0x10, USB_INT_EN_RISE_CLR_0); omap_writeb(0x10, USB_INT_EN_FALL_CLR_0); omap_writeb(0x01, OTG_CTRL_SET_0); } if (pdata->port_mode[1] == OMAP_EHCI_PORT_MODE_TLL) { omap_writeb(0x10, USB_INT_EN_RISE_CLR_1); omap_writeb(0x10, USB_INT_EN_FALL_CLR_1); omap_writeb(0x01, OTG_CTRL_SET_1); } } omap->ehci->regs = hcd->regs + HC_LENGTH(readl(&omap->ehci->caps->hc_capbase)); dbg_hcs_params(omap->ehci, "reset"); dbg_hcc_params(omap->ehci, "reset"); /* cache this readonly data; minimize chip reads */ omap->ehci->hcs_params = readl(&omap->ehci->caps->hcs_params); ret = usb_add_hcd(hcd, omapresp->irq, IRQF_DISABLED | IRQF_SHARED); if (ret) { dev_dbg(&pdev->dev, "failed to add hcd with err %d\n", ret); goto err_add_hcd; } /* root ports should always stay powered */ ehci_port_power(omap->ehci, 1); return 0; err_add_hcd: uhhtllp->disable(OMAP_EHCI); err_regs: usb_put_hcd(hcd); err_mem: kfree(omap); err_end: return ret; }