diff options
Diffstat (limited to 'drivers/mfd/omap-usb-tll.c')
| -rw-r--r-- | drivers/mfd/omap-usb-tll.c | 352 |
1 files changed, 183 insertions, 169 deletions
diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index eb869153206..532eacab6b4 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -1,8 +1,9 @@ /** * omap-usb-tll.c - The USB TLL driver for OMAP EHCI & OHCI * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2012-2013 Texas Instruments Incorporated - http://www.ti.com * Author: Keshava Munegowda <keshava_mgowda@ti.com> + * Author: Roger Quadros <rogerq@ti.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 of @@ -27,6 +28,7 @@ #include <linux/err.h> #include <linux/pm_runtime.h> #include <linux/platform_data/usb-omap.h> +#include <linux/of.h> #define USBTLL_DRIVER_NAME "usbhs_tll" @@ -54,10 +56,13 @@ #define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) #define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24 +#define OMAP_TLL_CHANNEL_CONF_DRVVBUS (1 << 16) +#define OMAP_TLL_CHANNEL_CONF_CHRGVBUS (1 << 15) #define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) #define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) #define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) #define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) +#define OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI (2 << 1) #define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1) #define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) @@ -92,42 +97,46 @@ #define OMAP_USBTLL_REV1 0x00000015 /* OMAP3 */ #define OMAP_USBTLL_REV2 0x00000018 /* OMAP 3630 */ #define OMAP_USBTLL_REV3 0x00000004 /* OMAP4 */ +#define OMAP_USBTLL_REV4 0x00000006 /* OMAP5 */ #define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) +/* only PHY and UNUSED modes don't need TLL */ +#define omap_usb_mode_needs_tll(x) ((x) != OMAP_USBHS_PORT_MODE_UNUSED &&\ + (x) != OMAP_EHCI_PORT_MODE_PHY) + struct usbtll_omap { - struct clk *usbtll_p1_fck; - struct clk *usbtll_p2_fck; - struct usbtll_omap_platform_data platdata; - /* secure the register updates */ - spinlock_t lock; + int nch; /* num. of channels */ + struct clk **ch_clk; + void __iomem *base; }; /*-------------------------------------------------------------------------*/ -const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; -struct platform_device *tll_pdev; +static const char usbtll_driver_name[] = USBTLL_DRIVER_NAME; +static struct device *tll_dev; +static DEFINE_SPINLOCK(tll_lock); /* serialize access to tll_dev */ /*-------------------------------------------------------------------------*/ static inline void usbtll_write(void __iomem *base, u32 reg, u32 val) { - __raw_writel(val, base + reg); + writel_relaxed(val, base + reg); } static inline u32 usbtll_read(void __iomem *base, u32 reg) { - return __raw_readl(base + reg); + return readl_relaxed(base + reg); } static inline void usbtll_writeb(void __iomem *base, u8 reg, u8 val) { - __raw_writeb(val, base + reg); + writeb_relaxed(val, base + reg); } static inline u8 usbtll_readb(void __iomem *base, u8 reg) { - return __raw_readb(base + reg); + return readb_relaxed(base + reg); } /*-------------------------------------------------------------------------*/ @@ -203,84 +212,140 @@ static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode) static int usbtll_omap_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct usbtll_omap_platform_data *pdata = dev->platform_data; - void __iomem *base; struct resource *res; struct usbtll_omap *tll; - unsigned reg; - unsigned long flags; int ret = 0; - int i, ver, count; + int i, ver; dev_dbg(dev, "starting TI HSUSB TLL Controller\n"); - tll = kzalloc(sizeof(struct usbtll_omap), GFP_KERNEL); + tll = devm_kzalloc(dev, sizeof(struct usbtll_omap), GFP_KERNEL); if (!tll) { dev_err(dev, "Memory allocation failed\n"); - ret = -ENOMEM; - goto end; - } - - spin_lock_init(&tll->lock); - - for (i = 0; i < OMAP3_HS_USB_PORTS; i++) - tll->platdata.port_mode[i] = pdata->port_mode[i]; - - tll->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); - if (IS_ERR(tll->usbtll_p1_fck)) { - ret = PTR_ERR(tll->usbtll_p1_fck); - dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); - goto err_tll; - } - - tll->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); - if (IS_ERR(tll->usbtll_p2_fck)) { - ret = PTR_ERR(tll->usbtll_p2_fck); - dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); - goto err_usbtll_p1_fck; + return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "usb tll get resource failed\n"); - ret = -ENODEV; - goto err_usbtll_p2_fck; - } - - base = ioremap(res->start, resource_size(res)); - if (!base) { - dev_err(dev, "TLL ioremap failed\n"); - ret = -ENOMEM; - goto err_usbtll_p2_fck; - } + tll->base = devm_ioremap_resource(dev, res); + if (IS_ERR(tll->base)) + return PTR_ERR(tll->base); platform_set_drvdata(pdev, tll); pm_runtime_enable(dev); pm_runtime_get_sync(dev); - spin_lock_irqsave(&tll->lock, flags); - - ver = usbtll_read(base, OMAP_USBTLL_REVISION); + ver = usbtll_read(tll->base, OMAP_USBTLL_REVISION); switch (ver) { case OMAP_USBTLL_REV1: - case OMAP_USBTLL_REV2: - count = OMAP_TLL_CHANNEL_COUNT; + case OMAP_USBTLL_REV4: + tll->nch = OMAP_TLL_CHANNEL_COUNT; break; + case OMAP_USBTLL_REV2: case OMAP_USBTLL_REV3: - count = OMAP_REV2_TLL_CHANNEL_COUNT; + tll->nch = OMAP_REV2_TLL_CHANNEL_COUNT; break; default: - dev_err(dev, "TLL version failed\n"); - ret = -ENODEV; - goto err_ioremap; + tll->nch = OMAP_TLL_CHANNEL_COUNT; + dev_dbg(dev, + "USB TLL Rev : 0x%x not recognized, assuming %d channels\n", + ver, tll->nch); + break; + } + + tll->ch_clk = devm_kzalloc(dev, sizeof(struct clk *) * tll->nch, + GFP_KERNEL); + if (!tll->ch_clk) { + ret = -ENOMEM; + dev_err(dev, "Couldn't allocate memory for channel clocks\n"); + goto err_clk_alloc; } - if (is_ehci_tll_mode(pdata->port_mode[0]) || - is_ehci_tll_mode(pdata->port_mode[1]) || - is_ehci_tll_mode(pdata->port_mode[2]) || - is_ohci_port(pdata->port_mode[0]) || - is_ohci_port(pdata->port_mode[1]) || - is_ohci_port(pdata->port_mode[2])) { + for (i = 0; i < tll->nch; i++) { + char clkname[] = "usb_tll_hs_usb_chx_clk"; + + snprintf(clkname, sizeof(clkname), + "usb_tll_hs_usb_ch%d_clk", i); + tll->ch_clk[i] = clk_get(dev, clkname); + + if (IS_ERR(tll->ch_clk[i])) + dev_dbg(dev, "can't get clock : %s\n", clkname); + } + + pm_runtime_put_sync(dev); + /* only after this can omap_tll_enable/disable work */ + spin_lock(&tll_lock); + tll_dev = dev; + spin_unlock(&tll_lock); + + return 0; + +err_clk_alloc: + pm_runtime_put_sync(dev); + pm_runtime_disable(dev); + + return ret; +} + +/** + * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usbtll_omap_probe(). + */ +static int usbtll_omap_remove(struct platform_device *pdev) +{ + struct usbtll_omap *tll = platform_get_drvdata(pdev); + int i; + + spin_lock(&tll_lock); + tll_dev = NULL; + spin_unlock(&tll_lock); + + for (i = 0; i < tll->nch; i++) + if (!IS_ERR(tll->ch_clk[i])) + clk_put(tll->ch_clk[i]); + + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id usbtll_omap_dt_ids[] = { + { .compatible = "ti,usbhs-tll" }, + { } +}; + +MODULE_DEVICE_TABLE(of, usbtll_omap_dt_ids); + +static struct platform_driver usbtll_omap_driver = { + .driver = { + .name = (char *)usbtll_driver_name, + .owner = THIS_MODULE, + .of_match_table = usbtll_omap_dt_ids, + }, + .probe = usbtll_omap_probe, + .remove = usbtll_omap_remove, +}; + +int omap_tll_init(struct usbhs_omap_platform_data *pdata) +{ + int i; + bool needs_tll; + unsigned reg; + struct usbtll_omap *tll; + + if (!tll_dev) + return -ENODEV; + + pm_runtime_get_sync(tll_dev); + + spin_lock(&tll_lock); + tll = dev_get_drvdata(tll_dev); + needs_tll = false; + for (i = 0; i < tll->nch; i++) + needs_tll |= omap_usb_mode_needs_tll(pdata->port_mode[i]); + + if (needs_tll) { + void __iomem *base = tll->base; /* Program Common TLL register */ reg = usbtll_read(base, OMAP_TLL_SHARED_CONF); @@ -292,7 +357,7 @@ static int usbtll_omap_probe(struct platform_device *pdev) usbtll_write(base, OMAP_TLL_SHARED_CONF, reg); /* Enable channels now */ - for (i = 0; i < count; i++) { + for (i = 0; i < tll->nch; i++) { reg = usbtll_read(base, OMAP_TLL_CHANNEL_CONF(i)); if (is_ohci_port(pdata->port_mode[i])) { @@ -308,6 +373,15 @@ static int usbtll_omap_probe(struct platform_device *pdev) reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + } else if (pdata->port_mode[i] == + OMAP_EHCI_PORT_MODE_HSIC) { + /* + * HSIC Mode requires UTMI port configurations + */ + reg |= OMAP_TLL_CHANNEL_CONF_DRVVBUS + | OMAP_TLL_CHANNEL_CONF_CHRGVBUS + | OMAP_TLL_CHANNEL_CONF_MODE_TRANSPARENT_UTMI + | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF; } else { continue; } @@ -320,134 +394,74 @@ static int usbtll_omap_probe(struct platform_device *pdev) } } -err_ioremap: - spin_unlock_irqrestore(&tll->lock, flags); - iounmap(base); - pm_runtime_put_sync(dev); - tll_pdev = pdev; - if (!ret) - goto end; - pm_runtime_disable(dev); - -err_usbtll_p2_fck: - clk_put(tll->usbtll_p2_fck); - -err_usbtll_p1_fck: - clk_put(tll->usbtll_p1_fck); + spin_unlock(&tll_lock); + pm_runtime_put_sync(tll_dev); -err_tll: - kfree(tll); - -end: - return ret; -} - -/** - * usbtll_omap_remove - shutdown processing for UHH & TLL HCDs - * @pdev: USB Host Controller being removed - * - * Reverses the effect of usbtll_omap_probe(). - */ -static int usbtll_omap_remove(struct platform_device *pdev) -{ - struct usbtll_omap *tll = platform_get_drvdata(pdev); - - clk_put(tll->usbtll_p2_fck); - clk_put(tll->usbtll_p1_fck); - pm_runtime_disable(&pdev->dev); - kfree(tll); return 0; } +EXPORT_SYMBOL_GPL(omap_tll_init); -static int usbtll_runtime_resume(struct device *dev) +int omap_tll_enable(struct usbhs_omap_platform_data *pdata) { - struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbtll_omap_platform_data *pdata = &tll->platdata; - unsigned long flags; + int i; + struct usbtll_omap *tll; - dev_dbg(dev, "usbtll_runtime_resume\n"); + if (!tll_dev) + return -ENODEV; - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } + pm_runtime_get_sync(tll_dev); - spin_lock_irqsave(&tll->lock, flags); + spin_lock(&tll_lock); + tll = dev_get_drvdata(tll_dev); - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_enable(tll->usbtll_p1_fck); + for (i = 0; i < tll->nch; i++) { + if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { + int r; - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_enable(tll->usbtll_p2_fck); + if (IS_ERR(tll->ch_clk[i])) + continue; - spin_unlock_irqrestore(&tll->lock, flags); + r = clk_prepare_enable(tll->ch_clk[i]); + if (r) { + dev_err(tll_dev, + "Error enabling ch %d clock: %d\n", i, r); + } + } + } + + spin_unlock(&tll_lock); return 0; } +EXPORT_SYMBOL_GPL(omap_tll_enable); -static int usbtll_runtime_suspend(struct device *dev) +int omap_tll_disable(struct usbhs_omap_platform_data *pdata) { - struct usbtll_omap *tll = dev_get_drvdata(dev); - struct usbtll_omap_platform_data *pdata = &tll->platdata; - unsigned long flags; - - dev_dbg(dev, "usbtll_runtime_suspend\n"); - - if (!pdata) { - dev_dbg(dev, "missing platform_data\n"); - return -ENODEV; - } + int i; + struct usbtll_omap *tll; - spin_lock_irqsave(&tll->lock, flags); + if (!tll_dev) + return -ENODEV; - if (is_ehci_tll_mode(pdata->port_mode[0])) - clk_disable(tll->usbtll_p1_fck); + spin_lock(&tll_lock); + tll = dev_get_drvdata(tll_dev); - if (is_ehci_tll_mode(pdata->port_mode[1])) - clk_disable(tll->usbtll_p2_fck); + for (i = 0; i < tll->nch; i++) { + if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { + if (!IS_ERR(tll->ch_clk[i])) + clk_disable_unprepare(tll->ch_clk[i]); + } + } - spin_unlock_irqrestore(&tll->lock, flags); + spin_unlock(&tll_lock); + pm_runtime_put_sync(tll_dev); return 0; } - -static const struct dev_pm_ops usbtllomap_dev_pm_ops = { - SET_RUNTIME_PM_OPS(usbtll_runtime_suspend, - usbtll_runtime_resume, - NULL) -}; - -static struct platform_driver usbtll_omap_driver = { - .driver = { - .name = (char *)usbtll_driver_name, - .owner = THIS_MODULE, - .pm = &usbtllomap_dev_pm_ops, - }, - .probe = usbtll_omap_probe, - .remove = usbtll_omap_remove, -}; - -int omap_tll_enable(void) -{ - if (!tll_pdev) { - pr_err("missing omap usbhs tll platform_data\n"); - return -ENODEV; - } - return pm_runtime_get_sync(&tll_pdev->dev); -} -EXPORT_SYMBOL_GPL(omap_tll_enable); - -int omap_tll_disable(void) -{ - if (!tll_pdev) { - pr_err("missing omap usbhs tll platform_data\n"); - return -ENODEV; - } - return pm_runtime_put_sync(&tll_pdev->dev); -} EXPORT_SYMBOL_GPL(omap_tll_disable); MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); +MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); MODULE_ALIAS("platform:" USBHS_DRIVER_NAME); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("usb tll driver for TI OMAP EHCI and OHCI controllers"); |
