/* * Copyright (C) 2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*! *@defgroup USB ARC OTG USB Driver */ /*! * @file usb_common.c * * @brief platform related part of usb driver. * @ingroup USB */ /*! *Include files */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MXC_NUMBER_USB_TRANSCEIVER 6 struct fsl_xcvr_ops *g_xc_ops[MXC_NUMBER_USB_TRANSCEIVER] = { NULL }; enum fsl_usb2_modes get_usb_mode(struct fsl_usb2_platform_data *pdata) { enum fsl_usb2_modes mode; mode = FSL_USB_UNKNOWN; if (!strcmp("DR", pdata->name)) { if ((UOG_USBMODE & 0x3) == 0x2) mode = FSL_USB_DR_DEVICE; else if ((UOG_USBMODE & 0x3) == 0x3) mode = FSL_USB_DR_HOST; } else if (!strcmp("Host 1", pdata->name)) mode = FSL_USB_MPH_HOST1; else if (!strcmp("Host 2", pdata->name)) mode = FSL_USB_MPH_HOST2; if (mode == FSL_USB_UNKNOWN) printk(KERN_ERR "unknow usb mode,name is %s\n", pdata->name); return mode; } static struct clk *usb_clk; static struct clk *usb_ahb_clk; extern int gpio_usbotg_hs_active(void); extern int gpio_usbotg_hs_inactive(void); /* * make sure USB_CLK is running at 60 MHz +/- 1000 Hz */ static int fsl_check_usbclk(void) { unsigned long freq; usb_ahb_clk = clk_get(NULL, "usb_ahb_clk"); if (clk_enable(usb_ahb_clk)) { printk(KERN_ERR "clk_enable(usb_ahb_clk) failed\n"); return -EINVAL; } clk_put(usb_ahb_clk); usb_clk = clk_get(NULL, "usb_clk"); freq = clk_get_rate(usb_clk); clk_put(usb_clk); if ((freq < 59999000) || (freq > 60001000)) { printk(KERN_ERR "USB_CLK=%lu, should be 60MHz\n", freq); return -1; } return 0; } void fsl_usb_xcvr_register(struct fsl_xcvr_ops *xcvr_ops) { int i; pr_debug("%s\n", __func__); for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) { if (g_xc_ops[i] == NULL) { g_xc_ops[i] = xcvr_ops; return; } } pr_debug("Failed %s\n", __func__); } EXPORT_SYMBOL(fsl_usb_xcvr_register); void fsl_platform_set_test_mode (struct fsl_usb2_platform_data *pdata, enum usb_test_mode mode) { if (pdata->xcvr_ops && pdata->xcvr_ops->set_test_mode) pdata->xcvr_ops->set_test_mode((u32 *)(pdata->regs + ULPIVW_OFF), mode); } EXPORT_SYMBOL(fsl_platform_set_test_mode); void fsl_usb_xcvr_unregister(struct fsl_xcvr_ops *xcvr_ops) { int i; pr_debug("%s\n", __func__); for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) { if (g_xc_ops[i] == xcvr_ops) { g_xc_ops[i] = NULL; return; } } pr_debug("Failed %s\n", __func__); } EXPORT_SYMBOL(fsl_usb_xcvr_unregister); static struct fsl_xcvr_ops *fsl_usb_get_xcvr(char *name) { int i; pr_debug("%s\n", __func__); if (name == NULL) { printk(KERN_ERR "get_xcvr(): No tranceiver name\n"); return NULL; } for (i = 0; i < MXC_NUMBER_USB_TRANSCEIVER; i++) { if (strcmp(g_xc_ops[i]->name, name) == 0) { return g_xc_ops[i]; } } pr_debug("Failed %s\n", __func__); return NULL; } /* The dmamask must be set for EHCI to work */ static u64 ehci_dmamask = ~(u32) 0; /*! * Register an instance of a USB host platform device. * * @param res: resource pointer * @param n_res: number of resources * @param config: config pointer * * @return newly-registered platform_device * * The USB controller supports 3 host interfaces, and the * kernel can be configured to support some number of them. * Each supported host interface is registered as an instance * of the "fsl-ehci" device. Call this function multiple times * to register each host interface. */ static int instance_id = 0; __init struct platform_device *host_pdev_register(struct resource *res, int n_res, struct fsl_usb2_platform_data *config) { struct platform_device *pdev; int rc; pr_debug("register host res=0x%p, size=%d\n", res, n_res); pdev = platform_device_register_simple("fsl-ehci", instance_id, res, n_res); if (IS_ERR(pdev)) { pr_debug("can't register %s Host, %ld\n", config->name, PTR_ERR(pdev)); return NULL; } pdev->dev.coherent_dma_mask = 0xffffffff; pdev->dev.dma_mask = &ehci_dmamask; /* * platform_device_add_data() makes a copy of * the platform_data passed in. That makes it * impossible to share the same config struct for * all OTG devices (host,gadget,otg). So, just * set the platorm_data pointer ourselves. */ rc = platform_device_add_data(pdev, config, sizeof(struct fsl_usb2_platform_data)); if (rc) { platform_device_unregister(pdev); return NULL; } printk(KERN_INFO "usb: %s host (%s) registered\n", config->name, config->transceiver); pr_debug("pdev=0x%p dev=0x%p resources=0x%p pdata=0x%p\n", pdev, &pdev->dev, pdev->resource, pdev->dev.platform_data); instance_id++; return pdev; } static void usbh1_set_serial_xcvr(void) { pr_debug("%s: \n", __func__); USBCTRL &= ~(UCTRL_H1SIC_MASK | UCTRL_BPE); /* disable bypass mode */ USBCTRL |= UCTRL_H1SIC_SU6 | /* single-ended / unidir. */ UCTRL_H1WIE | UCTRL_H1DT | /* disable H1 TLL */ UCTRL_H1PM; /* power mask */ } static void usbh1_set_ulpi_xcvr(void) { pr_debug("%s: \n", __func__); /* Stop then Reset */ UH1_USBCMD &= ~UCMD_RUN_STOP; while (UH1_USBCMD & UCMD_RUN_STOP) ; UH1_USBCMD |= UCMD_RESET; while (UH1_USBCMD & UCMD_RESET) ; /* Select the clock from external PHY */ USB_CTRL_1 |= USB_CTRL_UH1_EXT_CLK_EN; /* select ULPI PHY PTS=2 */ UH1_PORTSC1 = (UH1_PORTSC1 & ~PORTSC_PTS_MASK) | PORTSC_PTS_ULPI; USBCTRL &= ~UCTRL_H1WIE; /* HOST1 wakeup intr disable */ USBCTRL &= ~UCTRL_H1UIE; /* Host1 ULPI interrupt disable */ USBCTRL |= UCTRL_H1PM; /* HOST1 power mask */ USB_PHY_CTR_FUNC |= USB_UH1_OC_DIS; /* OC is not used */ /* Interrupt Threshold Control:Immediate (no threshold) */ UH1_USBCMD &= UCMD_ITC_NO_THRESHOLD; UH1_USBCMD |= UCMD_RESET; /* reset the controller */ /* allow controller to reset, and leave time for * the ULPI transceiver to reset too. */ msleep(100); /* Turn off the usbpll for ulpi tranceivers */ clk_disable(usb_clk); } static void usbh1_set_utmi_xcvr(void) { u32 tmp; /* Stop then Reset */ UH1_USBCMD &= ~UCMD_RUN_STOP; while (UH1_USBCMD & UCMD_RUN_STOP) ; UH1_USBCMD |= UCMD_RESET; while ((UH1_USBCMD) & (UCMD_RESET)) ; /* MX53 EVK is not using OC */ USB_PHY_CTR_FUNC |= USB_UH1_OC_DIS; USBCTRL &= ~UCTRL_H1PM; /* OTG Power Mask */ USBCTRL &= ~UCTRL_H1WIE; /* OTG Wakeup Intr Disable */ /* Over current disable */ USB_PHY_CTR_FUNC |= (0x1 << 5); /* set UTMI xcvr */ tmp = UH1_PORTSC1 & ~PORTSC_PTS_MASK; tmp |= PORTSC_PTS_UTMI; UH1_PORTSC1 = tmp; /* Set the PHY clock to 19.2MHz */ USBH1_PHY_CTRL1 &= ~USB_UTMI_PHYCTRL2_PLLDIV_MASK; USBH1_PHY_CTRL1 |= 0x01; /* Workaround an IC issue for ehci driver: * when turn off root hub port power, EHCI set * PORTSC reserved bits to be 0, but PTW with 0 * means 8 bits tranceiver width, here change * it back to be 16 bits and do PHY diable and * then enable. */ UH1_PORTSC1 |= PORTSC_PTW; /* need to reset the controller here so that the ID pin * is correctly detected. */ /* Stop then Reset */ UH1_USBCMD &= ~UCMD_RUN_STOP; while (UH1_USBCMD & UCMD_RUN_STOP) ; UH1_USBCMD |= UCMD_RESET; while ((UH1_USBCMD) & (UCMD_RESET)) ; /* allow controller to reset, and leave time for * the ULPI transceiver to reset too. */ msleep(100); /* Turn off the usbpll for UTMI tranceivers */ clk_disable(usb_clk); } static void usbh2_set_ulpi_xcvr(void) { u32 tmp; pr_debug("%s\n", __func__); if (cpu_is_mx51()) { USBCTRL_HOST2 &= ~(UCTRL_H2SIC_MASK | UCTRL_BPE); USBCTRL_HOST2 |= UCTRL_H2WIE | /* wakeup intr enable */ UCTRL_H2UIE | /* ULPI intr enable */ UCTRL_H2DT | /* disable H2 TLL */ UCTRL_H2PM; /* power mask */ } else { USBCTRL &= ~(UCTRL_H2SIC_MASK | UCTRL_BPE); USBCTRL |= UCTRL_H2WIE | /* wakeup intr enable */ UCTRL_H2UIE | /* ULPI intr enable */ UCTRL_H2DT | /* disable H2 TLL */ UCTRL_H2PM; /* power mask */ } /* must set ULPI phy before turning off clock */ tmp = UH2_PORTSC1 & ~PORTSC_PTS_MASK; tmp |= PORTSC_PTS_ULPI; UH2_PORTSC1 = tmp; UH2_USBCMD |= UCMD_RESET; /* reset the controller */ /* allow controller to reset, and leave time for * the ULPI transceiver to reset too. */ msleep(100); /* Turn off the usbpll for ulpi tranceivers */ clk_disable(usb_clk); } static void usbh2_set_serial_xcvr(void) { pr_debug("%s: \n", __func__); /* Stop then Reset */ UH2_USBCMD &= ~UCMD_RUN_STOP; while (UH2_USBCMD & UCMD_RUN_STOP) ; UH2_USBCMD |= UCMD_RESET; while (UH2_USBCMD & UCMD_RESET) ; USBCTRL &= ~(UCTRL_H2SIC_MASK); /* Disable bypass mode */ USBCTRL &= ~(UCTRL_H2PM); /* Power Mask */ USBCTRL &= ~UCTRL_H2OCPOL; /* OverCurrent Polarity is Low Active */ USBCTRL &= ~UCTRL_H2WIE; /* Wakeup intr disable */ USBCTRL |= UCTRL_IP_PUE_DOWN | /* ipp_pue_pulldwn_dpdm */ UCTRL_USBTE | /* USBT is enabled */ UCTRL_H2DT; /* Disable H2 TLL */ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0) { /* Disable Host2 bus Lock for i.MX35 1.0 */ USBCTRL |= UCTRL_H2LOCKD; /* USBOTG_PWR low active */ USBCTRL &= ~UCTRL_PP; /* OverCurrent Polarity is Low Active */ USBCTRL &= ~UCTRL_OCPOL; } else if (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1) { /* i.MX35 2.0 OTG and Host2 have seperate OC/PWR polarity */ USBCTRL &= ~UCTRL_H2PP; USBCTRL &= ~UCTRL_H2OCPOL; } else if (cpu_is_mx25()) { /* * USBH2_PWR and USBH2_OC are active high. * Must force xcvr clock to "internal" so that * we can write to PTS field after it's been * cleared by ehci_turn_off_all_ports(). */ USBCTRL |= UCTRL_H2PP | UCTRL_H2OCPOL | UCTRL_XCSH2; /* Disable Host2 bus Lock */ USBCTRL |= UCTRL_H2LOCKD; } USBCTRL &= ~(UCTRL_PP); UH2_PORTSC1 = (UH2_PORTSC1 & (~PORTSC_PTS_MASK)) | PORTSC_PTS_SERIAL; if (UH2_HCSPARAMS & HCSPARAMS_PPC) UH2_PORTSC1 |= PORTSC_PORT_POWER; /* Reset controller before set host mode */ UH2_USBCMD |= UCMD_RESET; while (UH2_USBCMD & UCMD_RESET) ; msleep(100); } /*! * Register remote wakeup by this usb controller * * @param pdev: platform_device for this usb controller * * @return 0 or negative error code in case not supportted. */ static int usb_register_remote_wakeup(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; struct resource *res; int irq; pr_debug("%s: pdev=0x%p \n", __func__, pdev); if (!cpu_is_mx51() && !cpu_is_mx25()) return -ECANCELED; /* The Host2 USB controller On mx25 platform * is no path available from internal USB FS * PHY to FS PHY wake up interrupt, So to * remove the function of USB Remote Wakeup on Host2 */ if (cpu_is_mx25() && (!strcmp("Host 2", pdata->name))) return -ECANCELED; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", dev_name(&pdev->dev)); return -ENODEV; } irq = res->start; pdev->dev.power.can_wakeup = 1; return 0; } int fsl_usb_host_init(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; struct fsl_xcvr_ops *xops; pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata); xops = fsl_usb_get_xcvr(pdata->transceiver); if (!xops) { printk(KERN_ERR "%s transceiver ops missing\n", pdata->name); return -EINVAL; } pdata->xcvr_ops = xops; pdata->xcvr_type = xops->xcvr_type; pdata->pdev = pdev; if (fsl_check_usbclk() != 0) return -EINVAL; pr_debug("%s: grab pins\n", __func__); if (pdata->gpio_usb_active && pdata->gpio_usb_active()) return -EINVAL; if (clk_enable(usb_clk)) { printk(KERN_ERR "clk_enable(usb_clk) failed\n"); return -EINVAL; } if (cpu_is_mx51()) { struct clk *usboh3_clk = clk_get(NULL, "usboh3_clk"); clk_enable(usboh3_clk); clk_put(usboh3_clk); } /* enable board power supply for xcvr */ if (pdata->xcvr_pwr) { if (pdata->xcvr_pwr->regu1) regulator_enable(pdata->xcvr_pwr->regu1); if (pdata->xcvr_pwr->regu2) regulator_enable(pdata->xcvr_pwr->regu2); } if (xops->init) xops->init(xops); if (usb_register_remote_wakeup(pdev)) pr_debug("%s port is not a wakeup source.\n", pdata->name); if (xops->xcvr_type == PORTSC_PTS_SERIAL) { if (cpu_is_mx35()) { usbh2_set_serial_xcvr(); /* Close the internal 60Mhz */ USBCTRL &= ~UCTRL_XCSH2; } else if (cpu_is_mx25()) usbh2_set_serial_xcvr(); else usbh1_set_serial_xcvr(); } else if (xops->xcvr_type == PORTSC_PTS_ULPI) { if (!strcmp("Host 1", pdata->name)) usbh1_set_ulpi_xcvr(); if (!strcmp("Host 2", pdata->name)) usbh2_set_ulpi_xcvr(); } else if (xops->xcvr_type == PORTSC_PTS_UTMI) { usbh1_set_utmi_xcvr(); } pr_debug("%s: %s success\n", __func__, pdata->name); return 0; } EXPORT_SYMBOL(fsl_usb_host_init); void fsl_usb_host_uninit(struct fsl_usb2_platform_data *pdata) { pr_debug("%s\n", __func__); if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) pdata->xcvr_ops->uninit(pdata->xcvr_ops); pdata->regs = NULL; if (pdata->gpio_usb_inactive) pdata->gpio_usb_inactive(); if (pdata->xcvr_type == PORTSC_PTS_SERIAL) { /* Workaround an IC issue for ehci driver. * when turn off root hub port power, EHCI set * PORTSC reserved bits to be 0, but PTS with 0 * means UTMI interface, so here force the Host2 * port use the internal 60Mhz. */ if (cpu_is_mx35()) USBCTRL |= UCTRL_XCSH2; clk_disable(usb_clk); } /* disable board power supply for xcvr */ if (pdata->xcvr_pwr) { if (pdata->xcvr_pwr->regu1) regulator_disable(pdata->xcvr_pwr->regu1); if (pdata->xcvr_pwr->regu2) regulator_disable(pdata->xcvr_pwr->regu2); } if (cpu_is_mx51()) { usb_clk = clk_get(NULL, "usboh3_clk"); clk_disable(usb_clk); clk_put(usb_clk); } clk_disable(usb_ahb_clk); } EXPORT_SYMBOL(fsl_usb_host_uninit); static void otg_set_serial_xcvr(void) { pr_debug("%s\n", __func__); } void otg_set_serial_host(void) { pr_debug("%s\n", __func__); /* set USBCTRL for host operation * disable: bypass mode, * set: single-ended/unidir/6 wire, OTG wakeup intr enable, * power mask */ USBCTRL &= ~UCTRL_OSIC_MASK; #if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3) USBCTRL &= ~UCTRL_BPE; #endif #if defined(CONFIG_MXC_USB_SB3) USBCTRL |= UCTRL_OSIC_SB3 | UCTRL_OWIE | UCTRL_OPM; #elif defined(CONFIG_MXC_USB_SU6) USBCTRL |= UCTRL_OSIC_SU6 | UCTRL_OWIE | UCTRL_OPM; #elif defined(CONFIG_MXC_USB_DB4) USBCTRL |= UCTRL_OSIC_DB4 | UCTRL_OWIE | UCTRL_OPM; #else USBCTRL |= UCTRL_OSIC_DU6 | UCTRL_OWIE | UCTRL_OPM; #endif USB_OTG_MIRROR = OTGM_VBUSVAL | OTGM_ASESVLD; /* 0xa */ } EXPORT_SYMBOL(otg_set_serial_host); void otg_set_serial_peripheral(void) { /* set USBCTRL for device operation * disable: bypass mode * set: differential/unidir/6 wire, OTG wakeup intr enable, * power mask */ USBCTRL &= ~UCTRL_OSIC_MASK; #if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3) USBCTRL &= ~UCTRL_BPE; #endif #if defined(CONFIG_MXC_USB_SB3) USBCTRL |= UCTRL_OSIC_SB3 | UCTRL_OWIE | UCTRL_OPM; #elif defined(CONFIG_MXC_USB_SU6) USBCTRL |= UCTRL_OSIC_SU6 | UCTRL_OWIE | UCTRL_OPM; #elif defined(CONFIG_MXC_USB_DB4) USBCTRL |= UCTRL_OSIC_DB4 | UCTRL_OWIE | UCTRL_OPM; #else USBCTRL |= UCTRL_OSIC_DU6 | UCTRL_OWIE | UCTRL_OPM; #endif USB_OTG_MIRROR = OTGM_VBUSVAL | OTGM_BSESVLD | OTGM_IDIDG; /* oxd */ } EXPORT_SYMBOL(otg_set_serial_peripheral); static void otg_set_ulpi_xcvr(void) { u32 tmp; pr_debug("%s\n", __func__); USBCTRL &= ~UCTRL_OSIC_MASK; #if defined(CONFIG_ARCH_MX27) || defined(CONFIG_ARCH_MX3) USBCTRL &= ~UCTRL_BPE; #endif USBCTRL |= UCTRL_OUIE | /* ULPI intr enable */ UCTRL_OWIE | /* OTG wakeup intr enable */ UCTRL_OPM; /* power mask */ /* must set ULPI phy before turning off clock */ tmp = UOG_PORTSC1 & ~PORTSC_PTS_MASK; tmp |= PORTSC_PTS_ULPI; UOG_PORTSC1 = tmp; /* need to reset the controller here so that the ID pin * is correctly detected. */ UOG_USBCMD |= UCMD_RESET; /* allow controller to reset, and leave time for * the ULPI transceiver to reset too. */ msleep(100); /* Turn off the usbpll for ulpi tranceivers */ clk_disable(usb_clk); } int fsl_usb_xcvr_suspend(struct fsl_xcvr_ops *xcvr_ops) { if (!machine_is_mx31_3ds()) return -ECANCELED; if (xcvr_ops->xcvr_type == PORTSC_PTS_ULPI) { if (fsl_check_usbclk() != 0) return -EINVAL; if (gpio_usbotg_hs_active()) return -EINVAL; clk_enable(usb_clk); otg_set_ulpi_xcvr(); if (xcvr_ops->suspend) /* suspend transceiver */ xcvr_ops->suspend(xcvr_ops); gpio_usbotg_hs_inactive(); clk_disable(usb_clk); } return 0; } EXPORT_SYMBOL(fsl_usb_xcvr_suspend); static void otg_set_utmi_xcvr(void) { u32 tmp; /* Stop then Reset */ UOG_USBCMD &= ~UCMD_RUN_STOP; while (UOG_USBCMD & UCMD_RUN_STOP) ; UOG_USBCMD |= UCMD_RESET; while ((UOG_USBCMD) & (UCMD_RESET)) ; if (cpu_is_mx53()) USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_OC_DIS; if (cpu_is_mx51()) { if (machine_is_mx51_3ds()) { /* OTG Polarity of Overcurrent is Low active */ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_OC_POL; /* Enable OTG Overcurrent Event */ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_OC_DIS; } else { /* BBG is not using OC */ USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_OC_DIS; } } else if (cpu_is_mx25()) { USBCTRL |= UCTRL_OCPOL; USBCTRL &= ~UCTRL_PP; } else { /* USBOTG_PWR low active */ USBCTRL &= ~UCTRL_PP; /* OverCurrent Polarity is Low Active */ USBCTRL &= ~UCTRL_OCPOL; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 0) /* OTG Lock Disable */ USBCTRL |= UCTRL_OLOCKD; } if (!cpu_is_mx53()) USBCTRL &= ~UCTRL_OPM; /* OTG Power Mask */ USBCTRL &= ~UCTRL_OWIE; /* OTG Wakeup Intr Disable */ /* set UTMI xcvr */ tmp = UOG_PORTSC1 & ~PORTSC_PTS_MASK; tmp |= PORTSC_PTS_UTMI; UOG_PORTSC1 = tmp; if (cpu_is_mx51()) { /* Set the PHY clock to 19.2MHz */ USB_PHY_CTR_FUNC2 &= ~USB_UTMI_PHYCTRL2_PLLDIV_MASK; USB_PHY_CTR_FUNC2 |= 0x01; } else if (machine_is_mx37_3ds()) { /* Reference voltage for HS disconnect envelope detector */ /* adjust the Squelch level */ USB_PHY_CTR_FUNC2 &= ~(USB_UTMI_PHYCTRL2_HSDEVSEL_MASK << USB_UTMI_PHYCTRL2_HSDEVSEL_SHIFT); } /* Workaround an IC issue for ehci driver: * when turn off root hub port power, EHCI set * PORTSC reserved bits to be 0, but PTW with 0 * means 8 bits tranceiver width, here change * it back to be 16 bits and do PHY diable and * then enable. */ UOG_PORTSC1 |= PORTSC_PTW; if (cpu_is_mx35() || cpu_is_mx25()) { /* Enable UTMI interface in PHY control Reg */ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_UTMI_ENABLE; USB_PHY_CTR_FUNC |= USB_UTMI_PHYCTRL_UTMI_ENABLE; } /* need to reset the controller here so that the ID pin * is correctly detected. */ /* Stop then Reset */ UOG_USBCMD &= ~UCMD_RUN_STOP; while (UOG_USBCMD & UCMD_RUN_STOP) ; UOG_USBCMD |= UCMD_RESET; while ((UOG_USBCMD) & (UCMD_RESET)) ; /* allow controller to reset, and leave time for * the ULPI transceiver to reset too. */ msleep(100); if (cpu_is_mx37()) { /* fix USB PHY Power Gating leakage issue for i.MX37 */ USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_CHGRDETON; USB_PHY_CTR_FUNC &= ~USB_UTMI_PHYCTRL_CHGRDETEN; } /* Turn off the usbpll for UTMI tranceivers */ clk_disable(usb_clk); } static int otg_used = 0; int usbotg_init(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; struct fsl_xcvr_ops *xops; pr_debug("%s: pdev=0x%p pdata=0x%p\n", __func__, pdev, pdata); xops = fsl_usb_get_xcvr(pdata->transceiver); if (!xops) { printk(KERN_ERR "DR transceiver ops missing\n"); return -EINVAL; } pdata->xcvr_ops = xops; pdata->xcvr_type = xops->xcvr_type; pdata->pdev = pdev; if (!otg_used) { if (fsl_check_usbclk() != 0) return -EINVAL; pr_debug("%s: grab pins\n", __func__); if (pdata->gpio_usb_active && pdata->gpio_usb_active()) return -EINVAL; if (clk_enable(usb_clk)) { printk(KERN_ERR "clk_enable(usb_clk) failed\n"); return -EINVAL; } if (xops->init) xops->init(xops); UOG_PORTSC1 = UOG_PORTSC1 & ~PORTSC_PHCD; if (xops->xcvr_type == PORTSC_PTS_SERIAL) { if (pdata->operating_mode == FSL_USB2_DR_HOST) { otg_set_serial_host(); /* need reset */ UOG_USBCMD |= UCMD_RESET; msleep(100); } else if (pdata->operating_mode == FSL_USB2_DR_DEVICE) otg_set_serial_peripheral(); otg_set_serial_xcvr(); } else if (xops->xcvr_type == PORTSC_PTS_ULPI) { otg_set_ulpi_xcvr(); } else if (xops->xcvr_type == PORTSC_PTS_UTMI) { otg_set_utmi_xcvr(); } } if (usb_register_remote_wakeup(pdev)) pr_debug("DR is not a wakeup source.\n"); otg_used++; pr_debug("%s: success\n", __func__); return 0; } EXPORT_SYMBOL(usbotg_init); void usbotg_uninit(struct fsl_usb2_platform_data *pdata) { pr_debug("%s\n", __func__); otg_used--; if (!otg_used) { if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) pdata->xcvr_ops->uninit(pdata->xcvr_ops); pdata->regs = NULL; if (machine_is_mx31_3ds()) { if (pdata->xcvr_ops && pdata->xcvr_ops->suspend) pdata->xcvr_ops->suspend(pdata->xcvr_ops); clk_disable(usb_clk); } msleep(1); UOG_PORTSC1 = UOG_PORTSC1 | PORTSC_PHCD; if (pdata->gpio_usb_inactive) pdata->gpio_usb_inactive(); if (pdata->xcvr_type == PORTSC_PTS_SERIAL) clk_disable(usb_clk); clk_disable(usb_ahb_clk); } } EXPORT_SYMBOL(usbotg_uninit); int usb_host_wakeup_irq(struct device *wkup_dev) { int wakeup_req = 0; struct fsl_usb2_platform_data *pdata = wkup_dev->platform_data; if (!strcmp("Host 1", pdata->name)) { wakeup_req = USBCTRL & UCTRL_H1WIR; } else if (!strcmp("DR", pdata->name)) { wakeup_req = USBCTRL & UCTRL_OWIR; /* If DR is in device mode, let udc handle it */ if (wakeup_req && ((UOG_USBMODE & 0x3) == 0x2)) wakeup_req = 0; } return wakeup_req; } EXPORT_SYMBOL(usb_host_wakeup_irq); void usb_host_set_wakeup(struct device *wkup_dev, bool para) { struct fsl_usb2_platform_data *pdata = wkup_dev->platform_data; if (pdata->wake_up_enable) pdata->wake_up_enable(pdata, para); } EXPORT_SYMBOL(usb_host_set_wakeup);