diff options
Diffstat (limited to 'arch/arm/plat-stmp3xxx/usb_common.c')
-rw-r--r-- | arch/arm/plat-stmp3xxx/usb_common.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/arch/arm/plat-stmp3xxx/usb_common.c b/arch/arm/plat-stmp3xxx/usb_common.c new file mode 100644 index 000000000000..5e5687f070c0 --- /dev/null +++ b/arch/arm/plat-stmp3xxx/usb_common.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2009-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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/fsl_devices.h> +#include <linux/usb/otg.h> +#include <linux/usb/fsl_xcvr.h> +#include <mach/arc_otg.h> +#include <mach/platform.h> +#include <mach/regs-power.h> +#include <mach/regs-digctl.h> +#include <mach/regs-clkctrl.h> +#include <mach/regs-usbctrl.h> +#include <mach/regs-usbphy.h> +#include <mach/pinmux.h> + +#define MXC_NUMBER_USB_TRANSCEIVER 6 +struct fsl_xcvr_ops *g_xc_ops[MXC_NUMBER_USB_TRANSCEIVER] = { NULL }; + +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_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; + +static int instance_id; +struct platform_device *host_pdev_register(struct resource *res, int n_res, + struct fsl_usb2_platform_data *config) +{ + struct platform_device *pdev; + int rc; + instance_id = 0; + + 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; + } + + pr_debug(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; +} + +int usb_phy_enable(void) +{ + u32 tmp; + /* Reset USBPHY module */ + stmp3xxx_setl(BM_USBPHY_CTRL_SFTRST, + REGS_USBPHY_BASE + HW_USBPHY_CTRL); + udelay(10); + + /* Remove CLKGATE and SFTRST */ + stmp3xxx_clearl(BM_USBPHY_CTRL_CLKGATE | BM_USBPHY_CTRL_SFTRST, + REGS_USBPHY_BASE + HW_USBPHY_CTRL); + + /* Turn on the USB clocks */ + stmp3xxx_setl(BM_CLKCTRL_PLLCTRL0_EN_USB_CLKS, + REGS_CLKCTRL_BASE + HW_CLKCTRL_PLLCTRL0); + stmp3xxx_clearl(BM_DIGCTL_CTRL_USB_CLKGATE, + REGS_DIGCTL_BASE + HW_DIGCTL_CTRL); + + /* set UTMI xcvr */ + /* 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. + */ + tmp = __raw_readl(REGS_USBCTRL_BASE + HW_USBCTRL_PORTSC1) & ~PORTSC_PTS_MASK; + tmp |= (PORTSC_PTS_UTMI | PORTSC_PTW); + __raw_writel(tmp, REGS_USBCTRL_BASE + HW_USBCTRL_PORTSC1); + + /* Power up the PHY */ + __raw_writel(0, REGS_USBPHY_BASE + HW_USBPHY_PWD); + + /* + * Set precharge bit to cure overshoot problems at the + * start of packets + */ + stmp3xxx_setl(1, REGS_USBPHY_BASE + HW_USBPHY_CTRL); + +#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE) + /* enable disconnect detector */ + /* enable disconnect detector must be after entry high speed mode*/ + /*HW_USBPHY_CTRL_SET(BM_USBPHY_CTRL_ENHOSTDISCONDETECT); + */ +#endif + return 0; +} +EXPORT_SYMBOL(usb_phy_enable); + +static int otg_used; + +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; + + otg_used = 0; + if (!otg_used) { + pr_debug("%s: grab pins\n", __func__); + if (xops->init) + xops->init(xops); + usb_phy_enable(); + } + + 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__); + + if (pdata->xcvr_ops && pdata->xcvr_ops->uninit) + pdata->xcvr_ops->uninit(pdata->xcvr_ops); + + pdata->regs = NULL; + otg_used--; +} +EXPORT_SYMBOL(usbotg_uninit); |