linux/drivers/usb/host/ehci-mxc.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
   3 * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms of the GNU General Public License as published by the
   7 * Free Software Foundation; either version 2 of the License, or (at your
   8 * option) any later version.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  12 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  13 * for more details.
  14 *
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program; if not, write to the Free Software Foundation,
  17 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18 */
  19
  20#include <linux/kernel.h>
  21#include <linux/module.h>
  22#include <linux/io.h>
  23#include <linux/platform_device.h>
  24#include <linux/clk.h>
  25#include <linux/delay.h>
  26#include <linux/usb/otg.h>
  27#include <linux/usb/ulpi.h>
  28#include <linux/slab.h>
  29#include <linux/usb.h>
  30#include <linux/usb/hcd.h>
  31#include <linux/platform_data/usb-ehci-mxc.h>
  32#include "ehci.h"
  33
  34#define DRIVER_DESC "Freescale On-Chip EHCI Host driver"
  35
  36static const char hcd_name[] = "ehci-mxc";
  37
  38#define ULPI_VIEWPORT_OFFSET    0x170
  39
  40struct ehci_mxc_priv {
  41        struct clk *usbclk, *ahbclk, *phyclk;
  42};
  43
  44static struct hc_driver __read_mostly ehci_mxc_hc_driver;
  45
  46static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = {
  47        .extra_priv_size =      sizeof(struct ehci_mxc_priv),
  48};
  49
  50static int ehci_mxc_drv_probe(struct platform_device *pdev)
  51{
  52        struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
  53        struct usb_hcd *hcd;
  54        struct resource *res;
  55        int irq, ret;
  56        struct ehci_mxc_priv *priv;
  57        struct device *dev = &pdev->dev;
  58        struct ehci_hcd *ehci;
  59
  60        if (!pdata) {
  61                dev_err(dev, "No platform data given, bailing out.\n");
  62                return -EINVAL;
  63        }
  64
  65        irq = platform_get_irq(pdev, 0);
  66
  67        hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev));
  68        if (!hcd)
  69                return -ENOMEM;
  70
  71        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  72        if (!res) {
  73                dev_err(dev, "Found HC with no register addr. Check setup!\n");
  74                ret = -ENODEV;
  75                goto err_alloc;
  76        }
  77
  78        hcd->rsrc_start = res->start;
  79        hcd->rsrc_len = resource_size(res);
  80
  81        hcd->regs = devm_ioremap_resource(&pdev->dev, res);
  82        if (IS_ERR(hcd->regs)) {
  83                ret = PTR_ERR(hcd->regs);
  84                goto err_alloc;
  85        }
  86
  87        hcd->has_tt = 1;
  88        ehci = hcd_to_ehci(hcd);
  89        priv = (struct ehci_mxc_priv *) ehci->priv;
  90
  91        /* enable clocks */
  92        priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
  93        if (IS_ERR(priv->usbclk)) {
  94                ret = PTR_ERR(priv->usbclk);
  95                goto err_alloc;
  96        }
  97        clk_prepare_enable(priv->usbclk);
  98
  99        priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
 100        if (IS_ERR(priv->ahbclk)) {
 101                ret = PTR_ERR(priv->ahbclk);
 102                goto err_clk_ahb;
 103        }
 104        clk_prepare_enable(priv->ahbclk);
 105
 106        /* "dr" device has its own clock on i.MX51 */
 107        priv->phyclk = devm_clk_get(&pdev->dev, "phy");
 108        if (IS_ERR(priv->phyclk))
 109                priv->phyclk = NULL;
 110        if (priv->phyclk)
 111                clk_prepare_enable(priv->phyclk);
 112
 113
 114        /* call platform specific init function */
 115        if (pdata->init) {
 116                ret = pdata->init(pdev);
 117                if (ret) {
 118                        dev_err(dev, "platform init failed\n");
 119                        goto err_init;
 120                }
 121                /* platforms need some time to settle changed IO settings */
 122                mdelay(10);
 123        }
 124
 125        /* EHCI registers start at offset 0x100 */
 126        ehci->caps = hcd->regs + 0x100;
 127        ehci->regs = hcd->regs + 0x100 +
 128                HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
 129
 130        /* set up the PORTSCx register */
 131        ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
 132
 133        /* is this really needed? */
 134        msleep(10);
 135
 136        /* Initialize the transceiver */
 137        if (pdata->otg) {
 138                pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
 139                ret = usb_phy_init(pdata->otg);
 140                if (ret) {
 141                        dev_err(dev, "unable to init transceiver, probably missing\n");
 142                        ret = -ENODEV;
 143                        goto err_add;
 144                }
 145                ret = otg_set_vbus(pdata->otg->otg, 1);
 146                if (ret) {
 147                        dev_err(dev, "unable to enable vbus on transceiver\n");
 148                        goto err_add;
 149                }
 150        }
 151
 152        platform_set_drvdata(pdev, hcd);
 153
 154        ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
 155        if (ret)
 156                goto err_add;
 157
 158        return 0;
 159
 160err_add:
 161        if (pdata && pdata->exit)
 162                pdata->exit(pdev);
 163err_init:
 164        if (priv->phyclk)
 165                clk_disable_unprepare(priv->phyclk);
 166
 167        clk_disable_unprepare(priv->ahbclk);
 168err_clk_ahb:
 169        clk_disable_unprepare(priv->usbclk);
 170err_alloc:
 171        usb_put_hcd(hcd);
 172        return ret;
 173}
 174
 175static int ehci_mxc_drv_remove(struct platform_device *pdev)
 176{
 177        struct mxc_usbh_platform_data *pdata = pdev->dev.platform_data;
 178        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 179        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 180        struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv;
 181
 182        usb_remove_hcd(hcd);
 183
 184        if (pdata && pdata->exit)
 185                pdata->exit(pdev);
 186
 187        if (pdata && pdata->otg)
 188                usb_phy_shutdown(pdata->otg);
 189
 190        clk_disable_unprepare(priv->usbclk);
 191        clk_disable_unprepare(priv->ahbclk);
 192
 193        if (priv->phyclk)
 194                clk_disable_unprepare(priv->phyclk);
 195
 196        usb_put_hcd(hcd);
 197        platform_set_drvdata(pdev, NULL);
 198        return 0;
 199}
 200
 201static void ehci_mxc_drv_shutdown(struct platform_device *pdev)
 202{
 203        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 204
 205        if (hcd->driver->shutdown)
 206                hcd->driver->shutdown(hcd);
 207}
 208
 209MODULE_ALIAS("platform:mxc-ehci");
 210
 211static struct platform_driver ehci_mxc_driver = {
 212        .probe = ehci_mxc_drv_probe,
 213        .remove = ehci_mxc_drv_remove,
 214        .shutdown = ehci_mxc_drv_shutdown,
 215        .driver = {
 216                   .name = "mxc-ehci",
 217        },
 218};
 219
 220static int __init ehci_mxc_init(void)
 221{
 222        if (usb_disabled())
 223                return -ENODEV;
 224
 225        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 226
 227        ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides);
 228        return platform_driver_register(&ehci_mxc_driver);
 229}
 230module_init(ehci_mxc_init);
 231
 232static void __exit ehci_mxc_cleanup(void)
 233{
 234        platform_driver_unregister(&ehci_mxc_driver);
 235}
 236module_exit(ehci_mxc_cleanup);
 237
 238MODULE_DESCRIPTION(DRIVER_DESC);
 239MODULE_AUTHOR("Sascha Hauer");
 240MODULE_LICENSE("GPL");
 241