linux/drivers/usb/host/ehci-s5p.c
<<
>>
Prefs
   1/*
   2 * SAMSUNG S5P USB HOST EHCI Controller
   3 *
   4 * Copyright (C) 2011 Samsung Electronics Co.Ltd
   5 * Author: Jingoo Han <jg1.han@samsung.com>
   6 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 *
  13 */
  14
  15#include <linux/clk.h>
  16#include <linux/dma-mapping.h>
  17#include <linux/io.h>
  18#include <linux/kernel.h>
  19#include <linux/module.h>
  20#include <linux/of.h>
  21#include <linux/of_gpio.h>
  22#include <linux/platform_device.h>
  23#include <linux/platform_data/usb-ehci-s5p.h>
  24#include <linux/usb/phy.h>
  25#include <linux/usb/samsung_usb_phy.h>
  26#include <linux/usb.h>
  27#include <linux/usb/hcd.h>
  28#include <linux/usb/otg.h>
  29
  30#include "ehci.h"
  31
  32#define DRIVER_DESC "EHCI s5p driver"
  33
  34#define EHCI_INSNREG00(base)                    (base + 0x90)
  35#define EHCI_INSNREG00_ENA_INCR16               (0x1 << 25)
  36#define EHCI_INSNREG00_ENA_INCR8                (0x1 << 24)
  37#define EHCI_INSNREG00_ENA_INCR4                (0x1 << 23)
  38#define EHCI_INSNREG00_ENA_INCRX_ALIGN          (0x1 << 22)
  39#define EHCI_INSNREG00_ENABLE_DMA_BURST \
  40        (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
  41         EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
  42
  43static const char hcd_name[] = "ehci-s5p";
  44static struct hc_driver __read_mostly s5p_ehci_hc_driver;
  45
  46struct s5p_ehci_hcd {
  47        struct clk *clk;
  48        struct usb_phy *phy;
  49        struct usb_otg *otg;
  50        struct s5p_ehci_platdata *pdata;
  51};
  52
  53static struct s5p_ehci_platdata empty_platdata;
  54
  55#define to_s5p_ehci(hcd)      (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
  56
  57static void s5p_setup_vbus_gpio(struct platform_device *pdev)
  58{
  59        struct device *dev = &pdev->dev;
  60        int err;
  61        int gpio;
  62
  63        if (!dev->of_node)
  64                return;
  65
  66        gpio = of_get_named_gpio(dev->of_node, "samsung,vbus-gpio", 0);
  67        if (!gpio_is_valid(gpio))
  68                return;
  69
  70        err = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH,
  71                                    "ehci_vbus_gpio");
  72        if (err)
  73                dev_err(dev, "can't request ehci vbus gpio %d", gpio);
  74}
  75
  76static int s5p_ehci_probe(struct platform_device *pdev)
  77{
  78        struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
  79        struct s5p_ehci_hcd *s5p_ehci;
  80        struct usb_hcd *hcd;
  81        struct ehci_hcd *ehci;
  82        struct resource *res;
  83        struct usb_phy *phy;
  84        int irq;
  85        int err;
  86
  87        /*
  88         * Right now device-tree probed devices don't get dma_mask set.
  89         * Since shared usb code relies on it, set it here for now.
  90         * Once we move to full device tree support this will vanish off.
  91         */
  92        if (!pdev->dev.dma_mask)
  93                pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
  94        if (!pdev->dev.coherent_dma_mask)
  95                pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
  96
  97        s5p_setup_vbus_gpio(pdev);
  98
  99        hcd = usb_create_hcd(&s5p_ehci_hc_driver,
 100                             &pdev->dev, dev_name(&pdev->dev));
 101        if (!hcd) {
 102                dev_err(&pdev->dev, "Unable to create HCD\n");
 103                return -ENOMEM;
 104        }
 105        s5p_ehci = to_s5p_ehci(hcd);
 106
 107        if (of_device_is_compatible(pdev->dev.of_node,
 108                                        "samsung,exynos5440-ehci")) {
 109                s5p_ehci->pdata = &empty_platdata;
 110                goto skip_phy;
 111        }
 112
 113        phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
 114        if (IS_ERR(phy)) {
 115                /* Fallback to pdata */
 116                if (!pdata) {
 117                        usb_put_hcd(hcd);
 118                        dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
 119                        return -EPROBE_DEFER;
 120                } else {
 121                        s5p_ehci->pdata = pdata;
 122                }
 123        } else {
 124                s5p_ehci->phy = phy;
 125                s5p_ehci->otg = phy->otg;
 126        }
 127
 128skip_phy:
 129
 130        s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
 131
 132        if (IS_ERR(s5p_ehci->clk)) {
 133                dev_err(&pdev->dev, "Failed to get usbhost clock\n");
 134                err = PTR_ERR(s5p_ehci->clk);
 135                goto fail_clk;
 136        }
 137
 138        err = clk_prepare_enable(s5p_ehci->clk);
 139        if (err)
 140                goto fail_clk;
 141
 142        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 143        if (!res) {
 144                dev_err(&pdev->dev, "Failed to get I/O memory\n");
 145                err = -ENXIO;
 146                goto fail_io;
 147        }
 148
 149        hcd->rsrc_start = res->start;
 150        hcd->rsrc_len = resource_size(res);
 151        hcd->regs = devm_ioremap(&pdev->dev, res->start, hcd->rsrc_len);
 152        if (!hcd->regs) {
 153                dev_err(&pdev->dev, "Failed to remap I/O memory\n");
 154                err = -ENOMEM;
 155                goto fail_io;
 156        }
 157
 158        irq = platform_get_irq(pdev, 0);
 159        if (!irq) {
 160                dev_err(&pdev->dev, "Failed to get IRQ\n");
 161                err = -ENODEV;
 162                goto fail_io;
 163        }
 164
 165        if (s5p_ehci->otg)
 166                s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 167
 168        if (s5p_ehci->phy)
 169                usb_phy_init(s5p_ehci->phy);
 170        else if (s5p_ehci->pdata->phy_init)
 171                s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
 172
 173        ehci = hcd_to_ehci(hcd);
 174        ehci->caps = hcd->regs;
 175
 176        /* DMA burst Enable */
 177        writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
 178
 179        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
 180        if (err) {
 181                dev_err(&pdev->dev, "Failed to add USB HCD\n");
 182                goto fail_add_hcd;
 183        }
 184
 185        platform_set_drvdata(pdev, hcd);
 186
 187        return 0;
 188
 189fail_add_hcd:
 190        if (s5p_ehci->phy)
 191                usb_phy_shutdown(s5p_ehci->phy);
 192        else if (s5p_ehci->pdata->phy_exit)
 193                s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
 194fail_io:
 195        clk_disable_unprepare(s5p_ehci->clk);
 196fail_clk:
 197        usb_put_hcd(hcd);
 198        return err;
 199}
 200
 201static int s5p_ehci_remove(struct platform_device *pdev)
 202{
 203        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 204        struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
 205
 206        usb_remove_hcd(hcd);
 207
 208        if (s5p_ehci->otg)
 209                s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 210
 211        if (s5p_ehci->phy)
 212                usb_phy_shutdown(s5p_ehci->phy);
 213        else if (s5p_ehci->pdata->phy_exit)
 214                s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
 215
 216        clk_disable_unprepare(s5p_ehci->clk);
 217
 218        usb_put_hcd(hcd);
 219
 220        return 0;
 221}
 222
 223static void s5p_ehci_shutdown(struct platform_device *pdev)
 224{
 225        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 226
 227        if (hcd->driver->shutdown)
 228                hcd->driver->shutdown(hcd);
 229}
 230
 231#ifdef CONFIG_PM
 232static int s5p_ehci_suspend(struct device *dev)
 233{
 234        struct usb_hcd *hcd = dev_get_drvdata(dev);
 235        struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
 236        struct platform_device *pdev = to_platform_device(dev);
 237
 238        bool do_wakeup = device_may_wakeup(dev);
 239        int rc;
 240
 241        rc = ehci_suspend(hcd, do_wakeup);
 242
 243        if (s5p_ehci->otg)
 244                s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 245
 246        if (s5p_ehci->phy)
 247                usb_phy_shutdown(s5p_ehci->phy);
 248        else if (s5p_ehci->pdata->phy_exit)
 249                s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
 250
 251        clk_disable_unprepare(s5p_ehci->clk);
 252
 253        return rc;
 254}
 255
 256static int s5p_ehci_resume(struct device *dev)
 257{
 258        struct usb_hcd *hcd = dev_get_drvdata(dev);
 259        struct  s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
 260        struct platform_device *pdev = to_platform_device(dev);
 261
 262        clk_prepare_enable(s5p_ehci->clk);
 263
 264        if (s5p_ehci->otg)
 265                s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
 266
 267        if (s5p_ehci->phy)
 268                usb_phy_init(s5p_ehci->phy);
 269        else if (s5p_ehci->pdata->phy_init)
 270                s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
 271
 272        /* DMA burst Enable */
 273        writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
 274
 275        ehci_resume(hcd, false);
 276        return 0;
 277}
 278#else
 279#define s5p_ehci_suspend        NULL
 280#define s5p_ehci_resume         NULL
 281#endif
 282
 283static const struct dev_pm_ops s5p_ehci_pm_ops = {
 284        .suspend        = s5p_ehci_suspend,
 285        .resume         = s5p_ehci_resume,
 286};
 287
 288#ifdef CONFIG_OF
 289static const struct of_device_id exynos_ehci_match[] = {
 290        { .compatible = "samsung,exynos4210-ehci" },
 291        { .compatible = "samsung,exynos5440-ehci" },
 292        {},
 293};
 294MODULE_DEVICE_TABLE(of, exynos_ehci_match);
 295#endif
 296
 297static struct platform_driver s5p_ehci_driver = {
 298        .probe          = s5p_ehci_probe,
 299        .remove         = s5p_ehci_remove,
 300        .shutdown       = s5p_ehci_shutdown,
 301        .driver = {
 302                .name   = "s5p-ehci",
 303                .owner  = THIS_MODULE,
 304                .pm     = &s5p_ehci_pm_ops,
 305                .of_match_table = of_match_ptr(exynos_ehci_match),
 306        }
 307};
 308static const struct ehci_driver_overrides s5p_overrides __initdata = {
 309        .extra_priv_size = sizeof(struct s5p_ehci_hcd),
 310};
 311
 312static int __init ehci_s5p_init(void)
 313{
 314        if (usb_disabled())
 315                return -ENODEV;
 316
 317        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 318        ehci_init_driver(&s5p_ehci_hc_driver, &s5p_overrides);
 319        return platform_driver_register(&s5p_ehci_driver);
 320}
 321module_init(ehci_s5p_init);
 322
 323static void __exit ehci_s5p_cleanup(void)
 324{
 325        platform_driver_unregister(&s5p_ehci_driver);
 326}
 327module_exit(ehci_s5p_cleanup);
 328
 329MODULE_DESCRIPTION(DRIVER_DESC);
 330MODULE_ALIAS("platform:s5p-ehci");
 331MODULE_AUTHOR("Jingoo Han");
 332MODULE_AUTHOR("Joonyoung Shim");
 333MODULE_LICENSE("GPL v2");
 334