linux/drivers/usb/host/ehci-mv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
   4 * Author: Chao Xie <chao.xie@marvell.com>
   5 *        Neil Zhang <zhangwm@marvell.com>
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/platform_device.h>
  11#include <linux/clk.h>
  12#include <linux/err.h>
  13#include <linux/usb/otg.h>
  14#include <linux/platform_data/mv_usb.h>
  15#include <linux/io.h>
  16
  17#include <linux/usb/hcd.h>
  18
  19#include "ehci.h"
  20
  21/* registers */
  22#define U2x_CAPREGS_OFFSET       0x100
  23
  24#define CAPLENGTH_MASK         (0xff)
  25
  26#define hcd_to_ehci_hcd_mv(h) ((struct ehci_hcd_mv *)hcd_to_ehci(h)->priv)
  27
  28struct ehci_hcd_mv {
  29        /* Which mode does this ehci running OTG/Host ? */
  30        int mode;
  31
  32        void __iomem *base;
  33        void __iomem *cap_regs;
  34        void __iomem *op_regs;
  35
  36        struct usb_phy *otg;
  37        struct clk *clk;
  38
  39        struct phy *phy;
  40
  41        int (*set_vbus)(unsigned int vbus);
  42};
  43
  44static void ehci_clock_enable(struct ehci_hcd_mv *ehci_mv)
  45{
  46        clk_prepare_enable(ehci_mv->clk);
  47}
  48
  49static void ehci_clock_disable(struct ehci_hcd_mv *ehci_mv)
  50{
  51        clk_disable_unprepare(ehci_mv->clk);
  52}
  53
  54static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
  55{
  56        ehci_clock_enable(ehci_mv);
  57        return phy_init(ehci_mv->phy);
  58}
  59
  60static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
  61{
  62        phy_exit(ehci_mv->phy);
  63        ehci_clock_disable(ehci_mv);
  64}
  65
  66static int mv_ehci_reset(struct usb_hcd *hcd)
  67{
  68        struct device *dev = hcd->self.controller;
  69        struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
  70        int retval;
  71
  72        if (ehci_mv == NULL) {
  73                dev_err(dev, "Can not find private ehci data\n");
  74                return -ENODEV;
  75        }
  76
  77        hcd->has_tt = 1;
  78
  79        retval = ehci_setup(hcd);
  80        if (retval)
  81                dev_err(dev, "ehci_setup failed %d\n", retval);
  82
  83        return retval;
  84}
  85
  86static struct hc_driver __read_mostly ehci_platform_hc_driver;
  87
  88static const struct ehci_driver_overrides platform_overrides __initconst = {
  89        .reset =                mv_ehci_reset,
  90        .extra_priv_size =      sizeof(struct ehci_hcd_mv),
  91};
  92
  93static int mv_ehci_probe(struct platform_device *pdev)
  94{
  95        struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
  96        struct usb_hcd *hcd;
  97        struct ehci_hcd *ehci;
  98        struct ehci_hcd_mv *ehci_mv;
  99        struct resource *r;
 100        int retval = -ENODEV;
 101        u32 offset;
 102
 103        if (usb_disabled())
 104                return -ENODEV;
 105
 106        hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, "mv ehci");
 107        if (!hcd)
 108                return -ENOMEM;
 109
 110        platform_set_drvdata(pdev, hcd);
 111        ehci_mv = hcd_to_ehci_hcd_mv(hcd);
 112
 113        ehci_mv->mode = MV_USB_MODE_HOST;
 114        if (pdata) {
 115                ehci_mv->mode = pdata->mode;
 116                ehci_mv->set_vbus = pdata->set_vbus;
 117        }
 118
 119        ehci_mv->phy = devm_phy_get(&pdev->dev, "usb");
 120        if (IS_ERR(ehci_mv->phy)) {
 121                retval = PTR_ERR(ehci_mv->phy);
 122                if (retval != -EPROBE_DEFER)
 123                        dev_err(&pdev->dev, "Failed to get phy.\n");
 124                goto err_put_hcd;
 125        }
 126
 127        ehci_mv->clk = devm_clk_get(&pdev->dev, NULL);
 128        if (IS_ERR(ehci_mv->clk)) {
 129                dev_err(&pdev->dev, "error getting clock\n");
 130                retval = PTR_ERR(ehci_mv->clk);
 131                goto err_put_hcd;
 132        }
 133
 134
 135
 136        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 137        ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
 138        if (IS_ERR(ehci_mv->base)) {
 139                retval = PTR_ERR(ehci_mv->base);
 140                goto err_put_hcd;
 141        }
 142
 143        retval = mv_ehci_enable(ehci_mv);
 144        if (retval) {
 145                dev_err(&pdev->dev, "init phy error %d\n", retval);
 146                goto err_put_hcd;
 147        }
 148
 149        ehci_mv->cap_regs =
 150                (void __iomem *) ((unsigned long) ehci_mv->base + U2x_CAPREGS_OFFSET);
 151        offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
 152        ehci_mv->op_regs =
 153                (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
 154
 155        hcd->rsrc_start = r->start;
 156        hcd->rsrc_len = resource_size(r);
 157        hcd->regs = ehci_mv->op_regs;
 158
 159        hcd->irq = platform_get_irq(pdev, 0);
 160        if (!hcd->irq) {
 161                dev_err(&pdev->dev, "Cannot get irq.");
 162                retval = -ENODEV;
 163                goto err_disable_clk;
 164        }
 165
 166        ehci = hcd_to_ehci(hcd);
 167        ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
 168
 169        if (ehci_mv->mode == MV_USB_MODE_OTG) {
 170                ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
 171                if (IS_ERR(ehci_mv->otg)) {
 172                        retval = PTR_ERR(ehci_mv->otg);
 173
 174                        if (retval == -ENXIO)
 175                                dev_info(&pdev->dev, "MV_USB_MODE_OTG "
 176                                                "must have CONFIG_USB_PHY enabled\n");
 177                        else
 178                                dev_err(&pdev->dev,
 179                                                "unable to find transceiver\n");
 180                        goto err_disable_clk;
 181                }
 182
 183                retval = otg_set_host(ehci_mv->otg->otg, &hcd->self);
 184                if (retval < 0) {
 185                        dev_err(&pdev->dev,
 186                                "unable to register with transceiver\n");
 187                        retval = -ENODEV;
 188                        goto err_disable_clk;
 189                }
 190                /* otg will enable clock before use as host */
 191                mv_ehci_disable(ehci_mv);
 192        } else {
 193                if (ehci_mv->set_vbus)
 194                        ehci_mv->set_vbus(1);
 195
 196                retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
 197                if (retval) {
 198                        dev_err(&pdev->dev,
 199                                "failed to add hcd with err %d\n", retval);
 200                        goto err_set_vbus;
 201                }
 202                device_wakeup_enable(hcd->self.controller);
 203        }
 204
 205        dev_info(&pdev->dev,
 206                 "successful find EHCI device with regs 0x%p irq %d"
 207                 " working in %s mode\n", hcd->regs, hcd->irq,
 208                 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host");
 209
 210        return 0;
 211
 212err_set_vbus:
 213        if (ehci_mv->set_vbus)
 214                ehci_mv->set_vbus(0);
 215err_disable_clk:
 216        mv_ehci_disable(ehci_mv);
 217err_put_hcd:
 218        usb_put_hcd(hcd);
 219
 220        return retval;
 221}
 222
 223static int mv_ehci_remove(struct platform_device *pdev)
 224{
 225        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 226        struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
 227
 228        if (hcd->rh_registered)
 229                usb_remove_hcd(hcd);
 230
 231        if (!IS_ERR_OR_NULL(ehci_mv->otg))
 232                otg_set_host(ehci_mv->otg->otg, NULL);
 233
 234        if (ehci_mv->mode == MV_USB_MODE_HOST) {
 235                if (ehci_mv->set_vbus)
 236                        ehci_mv->set_vbus(0);
 237
 238                mv_ehci_disable(ehci_mv);
 239        }
 240
 241        usb_put_hcd(hcd);
 242
 243        return 0;
 244}
 245
 246MODULE_ALIAS("mv-ehci");
 247
 248static const struct platform_device_id ehci_id_table[] = {
 249        {"pxa-u2oehci", PXA_U2OEHCI},
 250        {"pxa-sph", PXA_SPH},
 251        {"mmp3-hsic", MMP3_HSIC},
 252        {"mmp3-fsic", MMP3_FSIC},
 253        {},
 254};
 255
 256static void mv_ehci_shutdown(struct platform_device *pdev)
 257{
 258        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 259
 260        if (!hcd->rh_registered)
 261                return;
 262
 263        if (hcd->driver->shutdown)
 264                hcd->driver->shutdown(hcd);
 265}
 266
 267static const struct of_device_id ehci_mv_dt_ids[] = {
 268        { .compatible = "marvell,pxau2o-ehci", },
 269        {},
 270};
 271
 272static struct platform_driver ehci_mv_driver = {
 273        .probe = mv_ehci_probe,
 274        .remove = mv_ehci_remove,
 275        .shutdown = mv_ehci_shutdown,
 276        .driver = {
 277                .name = "mv-ehci",
 278                .bus = &platform_bus_type,
 279                .of_match_table = ehci_mv_dt_ids,
 280        },
 281        .id_table = ehci_id_table,
 282};
 283
 284static int __init ehci_platform_init(void)
 285{
 286        if (usb_disabled())
 287                return -ENODEV;
 288
 289        ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
 290        return platform_driver_register(&ehci_mv_driver);
 291}
 292module_init(ehci_platform_init);
 293
 294static void __exit ehci_platform_cleanup(void)
 295{
 296        platform_driver_unregister(&ehci_mv_driver);
 297}
 298module_exit(ehci_platform_cleanup);
 299
 300MODULE_DESCRIPTION("Marvell EHCI driver");
 301MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
 302MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>");
 303MODULE_ALIAS("mv-ehci");
 304MODULE_LICENSE("GPL");
 305MODULE_DEVICE_TABLE(of, ehci_mv_dt_ids);
 306