linux/drivers/usb/host/ehci-mxc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
   4 * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
   5 */
   6
   7#include <linux/kernel.h>
   8#include <linux/module.h>
   9#include <linux/io.h>
  10#include <linux/platform_device.h>
  11#include <linux/clk.h>
  12#include <linux/delay.h>
  13#include <linux/usb/otg.h>
  14#include <linux/usb/ulpi.h>
  15#include <linux/slab.h>
  16#include <linux/usb.h>
  17#include <linux/usb/hcd.h>
  18#include <linux/platform_data/usb-ehci-mxc.h>
  19#include "ehci.h"
  20
  21#define DRIVER_DESC "Freescale On-Chip EHCI Host driver"
  22
  23static const char hcd_name[] = "ehci-mxc";
  24
  25#define ULPI_VIEWPORT_OFFSET    0x170
  26
  27struct ehci_mxc_priv {
  28        struct clk *usbclk, *ahbclk, *phyclk;
  29};
  30
  31static struct hc_driver __read_mostly ehci_mxc_hc_driver;
  32
  33static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = {
  34        .extra_priv_size =      sizeof(struct ehci_mxc_priv),
  35};
  36
  37static int ehci_mxc_drv_probe(struct platform_device *pdev)
  38{
  39        struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
  40        struct usb_hcd *hcd;
  41        struct resource *res;
  42        int irq, ret;
  43        struct ehci_mxc_priv *priv;
  44        struct device *dev = &pdev->dev;
  45        struct ehci_hcd *ehci;
  46
  47        if (!pdata) {
  48                dev_err(dev, "No platform data given, bailing out.\n");
  49                return -EINVAL;
  50        }
  51
  52        irq = platform_get_irq(pdev, 0);
  53
  54        hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev));
  55        if (!hcd)
  56                return -ENOMEM;
  57
  58        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  59        hcd->regs = devm_ioremap_resource(&pdev->dev, res);
  60        if (IS_ERR(hcd->regs)) {
  61                ret = PTR_ERR(hcd->regs);
  62                goto err_alloc;
  63        }
  64        hcd->rsrc_start = res->start;
  65        hcd->rsrc_len = resource_size(res);
  66
  67        hcd->has_tt = 1;
  68        ehci = hcd_to_ehci(hcd);
  69        priv = (struct ehci_mxc_priv *) ehci->priv;
  70
  71        /* enable clocks */
  72        priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
  73        if (IS_ERR(priv->usbclk)) {
  74                ret = PTR_ERR(priv->usbclk);
  75                goto err_alloc;
  76        }
  77        clk_prepare_enable(priv->usbclk);
  78
  79        priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
  80        if (IS_ERR(priv->ahbclk)) {
  81                ret = PTR_ERR(priv->ahbclk);
  82                goto err_clk_ahb;
  83        }
  84        clk_prepare_enable(priv->ahbclk);
  85
  86        /* "dr" device has its own clock on i.MX51 */
  87        priv->phyclk = devm_clk_get(&pdev->dev, "phy");
  88        if (IS_ERR(priv->phyclk))
  89                priv->phyclk = NULL;
  90        if (priv->phyclk)
  91                clk_prepare_enable(priv->phyclk);
  92
  93
  94        /* call platform specific init function */
  95        if (pdata->init) {
  96                ret = pdata->init(pdev);
  97                if (ret) {
  98                        dev_err(dev, "platform init failed\n");
  99                        goto err_init;
 100                }
 101                /* platforms need some time to settle changed IO settings */
 102                mdelay(10);
 103        }
 104
 105        /* EHCI registers start at offset 0x100 */
 106        ehci->caps = hcd->regs + 0x100;
 107        ehci->regs = hcd->regs + 0x100 +
 108                HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
 109
 110        /* set up the PORTSCx register */
 111        ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]);
 112
 113        /* is this really needed? */
 114        msleep(10);
 115
 116        /* Initialize the transceiver */
 117        if (pdata->otg) {
 118                pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET;
 119                ret = usb_phy_init(pdata->otg);
 120                if (ret) {
 121                        dev_err(dev, "unable to init transceiver, probably missing\n");
 122                        ret = -ENODEV;
 123                        goto err_add;
 124                }
 125                ret = otg_set_vbus(pdata->otg->otg, 1);
 126                if (ret) {
 127                        dev_err(dev, "unable to enable vbus on transceiver\n");
 128                        goto err_add;
 129                }
 130        }
 131
 132        platform_set_drvdata(pdev, hcd);
 133
 134        ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
 135        if (ret)
 136                goto err_add;
 137
 138        device_wakeup_enable(hcd->self.controller);
 139        return 0;
 140
 141err_add:
 142        if (pdata && pdata->exit)
 143                pdata->exit(pdev);
 144err_init:
 145        if (priv->phyclk)
 146                clk_disable_unprepare(priv->phyclk);
 147
 148        clk_disable_unprepare(priv->ahbclk);
 149err_clk_ahb:
 150        clk_disable_unprepare(priv->usbclk);
 151err_alloc:
 152        usb_put_hcd(hcd);
 153        return ret;
 154}
 155
 156static int ehci_mxc_drv_remove(struct platform_device *pdev)
 157{
 158        struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
 159        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 160        struct ehci_hcd *ehci = hcd_to_ehci(hcd);
 161        struct ehci_mxc_priv *priv = (struct ehci_mxc_priv *) ehci->priv;
 162
 163        usb_remove_hcd(hcd);
 164
 165        if (pdata && pdata->exit)
 166                pdata->exit(pdev);
 167
 168        if (pdata && pdata->otg)
 169                usb_phy_shutdown(pdata->otg);
 170
 171        clk_disable_unprepare(priv->usbclk);
 172        clk_disable_unprepare(priv->ahbclk);
 173
 174        if (priv->phyclk)
 175                clk_disable_unprepare(priv->phyclk);
 176
 177        usb_put_hcd(hcd);
 178        return 0;
 179}
 180
 181MODULE_ALIAS("platform:mxc-ehci");
 182
 183static struct platform_driver ehci_mxc_driver = {
 184        .probe = ehci_mxc_drv_probe,
 185        .remove = ehci_mxc_drv_remove,
 186        .shutdown = usb_hcd_platform_shutdown,
 187        .driver = {
 188                   .name = "mxc-ehci",
 189        },
 190};
 191
 192static int __init ehci_mxc_init(void)
 193{
 194        if (usb_disabled())
 195                return -ENODEV;
 196
 197        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 198
 199        ehci_init_driver(&ehci_mxc_hc_driver, &ehci_mxc_overrides);
 200        return platform_driver_register(&ehci_mxc_driver);
 201}
 202module_init(ehci_mxc_init);
 203
 204static void __exit ehci_mxc_cleanup(void)
 205{
 206        platform_driver_unregister(&ehci_mxc_driver);
 207}
 208module_exit(ehci_mxc_cleanup);
 209
 210MODULE_DESCRIPTION(DRIVER_DESC);
 211MODULE_AUTHOR("Sascha Hauer");
 212MODULE_LICENSE("GPL");
 213