linux/drivers/usb/dwc3/dwc3-omap.c
<<
>>
Prefs
   1/**
   2 * dwc3-omap.c - OMAP Specific Glue layer
   3 *
   4 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
   5 *
   6 * Authors: Felipe Balbi <balbi@ti.com>,
   7 *          Sebastian Andrzej Siewior <bigeasy@linutronix.de>
   8 *
   9 * Redistribution and use in source and binary forms, with or without
  10 * modification, are permitted provided that the following conditions
  11 * are met:
  12 * 1. Redistributions of source code must retain the above copyright
  13 *    notice, this list of conditions, and the following disclaimer,
  14 *    without modification.
  15 * 2. Redistributions in binary form must reproduce the above copyright
  16 *    notice, this list of conditions and the following disclaimer in the
  17 *    documentation and/or other materials provided with the distribution.
  18 * 3. The names of the above-listed copyright holders may not be used
  19 *    to endorse or promote products derived from this software without
  20 *    specific prior written permission.
  21 *
  22 * ALTERNATIVELY, this software may be distributed under the terms of the
  23 * GNU General Public License ("GPL") version 2, as published by the Free
  24 * Software Foundation.
  25 *
  26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  27 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  28 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  30 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  31 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  32 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  33 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  34 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  35 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  36 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  37 */
  38
  39#include <linux/module.h>
  40#include <linux/kernel.h>
  41#include <linux/slab.h>
  42#include <linux/interrupt.h>
  43#include <linux/spinlock.h>
  44#include <linux/platform_device.h>
  45#include <linux/platform_data/dwc3-omap.h>
  46#include <linux/usb/dwc3-omap.h>
  47#include <linux/pm_runtime.h>
  48#include <linux/dma-mapping.h>
  49#include <linux/ioport.h>
  50#include <linux/io.h>
  51#include <linux/of.h>
  52#include <linux/of_platform.h>
  53
  54#include <linux/usb/otg.h>
  55#include <linux/usb/nop-usb-xceiv.h>
  56
  57/*
  58 * All these registers belong to OMAP's Wrapper around the
  59 * DesignWare USB3 Core.
  60 */
  61
  62#define USBOTGSS_REVISION                       0x0000
  63#define USBOTGSS_SYSCONFIG                      0x0010
  64#define USBOTGSS_IRQ_EOI                        0x0020
  65#define USBOTGSS_IRQSTATUS_RAW_0                0x0024
  66#define USBOTGSS_IRQSTATUS_0                    0x0028
  67#define USBOTGSS_IRQENABLE_SET_0                0x002c
  68#define USBOTGSS_IRQENABLE_CLR_0                0x0030
  69#define USBOTGSS_IRQSTATUS_RAW_1                0x0034
  70#define USBOTGSS_IRQSTATUS_1                    0x0038
  71#define USBOTGSS_IRQENABLE_SET_1                0x003c
  72#define USBOTGSS_IRQENABLE_CLR_1                0x0040
  73#define USBOTGSS_UTMI_OTG_CTRL                  0x0080
  74#define USBOTGSS_UTMI_OTG_STATUS                0x0084
  75#define USBOTGSS_MMRAM_OFFSET                   0x0100
  76#define USBOTGSS_FLADJ                          0x0104
  77#define USBOTGSS_DEBUG_CFG                      0x0108
  78#define USBOTGSS_DEBUG_DATA                     0x010c
  79
  80/* SYSCONFIG REGISTER */
  81#define USBOTGSS_SYSCONFIG_DMADISABLE           (1 << 16)
  82
  83/* IRQ_EOI REGISTER */
  84#define USBOTGSS_IRQ_EOI_LINE_NUMBER            (1 << 0)
  85
  86/* IRQS0 BITS */
  87#define USBOTGSS_IRQO_COREIRQ_ST                (1 << 0)
  88
  89/* IRQ1 BITS */
  90#define USBOTGSS_IRQ1_DMADISABLECLR             (1 << 17)
  91#define USBOTGSS_IRQ1_OEVT                      (1 << 16)
  92#define USBOTGSS_IRQ1_DRVVBUS_RISE              (1 << 13)
  93#define USBOTGSS_IRQ1_CHRGVBUS_RISE             (1 << 12)
  94#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE          (1 << 11)
  95#define USBOTGSS_IRQ1_IDPULLUP_RISE             (1 << 8)
  96#define USBOTGSS_IRQ1_DRVVBUS_FALL              (1 << 5)
  97#define USBOTGSS_IRQ1_CHRGVBUS_FALL             (1 << 4)
  98#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL          (1 << 3)
  99#define USBOTGSS_IRQ1_IDPULLUP_FALL             (1 << 0)
 100
 101/* UTMI_OTG_CTRL REGISTER */
 102#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS          (1 << 5)
 103#define USBOTGSS_UTMI_OTG_CTRL_CHRGVBUS         (1 << 4)
 104#define USBOTGSS_UTMI_OTG_CTRL_DISCHRGVBUS      (1 << 3)
 105#define USBOTGSS_UTMI_OTG_CTRL_IDPULLUP         (1 << 0)
 106
 107/* UTMI_OTG_STATUS REGISTER */
 108#define USBOTGSS_UTMI_OTG_STATUS_SW_MODE        (1 << 31)
 109#define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT   (1 << 9)
 110#define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE (1 << 8)
 111#define USBOTGSS_UTMI_OTG_STATUS_IDDIG          (1 << 4)
 112#define USBOTGSS_UTMI_OTG_STATUS_SESSEND        (1 << 3)
 113#define USBOTGSS_UTMI_OTG_STATUS_SESSVALID      (1 << 2)
 114#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID      (1 << 1)
 115
 116struct dwc3_omap {
 117        /* device lock */
 118        spinlock_t              lock;
 119
 120        struct platform_device  *usb2_phy;
 121        struct platform_device  *usb3_phy;
 122        struct device           *dev;
 123
 124        int                     irq;
 125        void __iomem            *base;
 126
 127        void                    *context;
 128        u32                     resource_size;
 129
 130        u32                     dma_status:1;
 131};
 132
 133struct dwc3_omap                *_omap;
 134
 135static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
 136{
 137        return readl(base + offset);
 138}
 139
 140static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
 141{
 142        writel(value, base + offset);
 143}
 144
 145void dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
 146{
 147        u32                     val;
 148        struct dwc3_omap        *omap = _omap;
 149
 150        switch (status) {
 151        case OMAP_DWC3_ID_GROUND:
 152                dev_dbg(omap->dev, "ID GND\n");
 153
 154                val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
 155                val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
 156                                | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
 157                                | USBOTGSS_UTMI_OTG_STATUS_SESSEND);
 158                val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
 159                                | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
 160                dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
 161                break;
 162
 163        case OMAP_DWC3_VBUS_VALID:
 164                dev_dbg(omap->dev, "VBUS Connect\n");
 165
 166                val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
 167                val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
 168                val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
 169                                | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
 170                                | USBOTGSS_UTMI_OTG_STATUS_SESSVALID
 171                                | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
 172                dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
 173                break;
 174
 175        case OMAP_DWC3_ID_FLOAT:
 176        case OMAP_DWC3_VBUS_OFF:
 177                dev_dbg(omap->dev, "VBUS Disconnect\n");
 178
 179                val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
 180                val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
 181                                | USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
 182                                | USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
 183                val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
 184                                | USBOTGSS_UTMI_OTG_STATUS_IDDIG;
 185                dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
 186                break;
 187
 188        default:
 189                dev_dbg(omap->dev, "ID float\n");
 190        }
 191
 192        return;
 193}
 194EXPORT_SYMBOL_GPL(dwc3_omap_mailbox);
 195
 196static int dwc3_omap_register_phys(struct dwc3_omap *omap)
 197{
 198        struct nop_usb_xceiv_platform_data pdata;
 199        struct platform_device  *pdev;
 200        int                     ret;
 201
 202        memset(&pdata, 0x00, sizeof(pdata));
 203
 204        pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
 205        if (!pdev)
 206                return -ENOMEM;
 207
 208        omap->usb2_phy = pdev;
 209        pdata.type = USB_PHY_TYPE_USB2;
 210
 211        ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata));
 212        if (ret)
 213                goto err1;
 214
 215        pdev = platform_device_alloc("nop_usb_xceiv", PLATFORM_DEVID_AUTO);
 216        if (!pdev) {
 217                ret = -ENOMEM;
 218                goto err1;
 219        }
 220
 221        omap->usb3_phy = pdev;
 222        pdata.type = USB_PHY_TYPE_USB3;
 223
 224        ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata));
 225        if (ret)
 226                goto err2;
 227
 228        ret = platform_device_add(omap->usb2_phy);
 229        if (ret)
 230                goto err2;
 231
 232        ret = platform_device_add(omap->usb3_phy);
 233        if (ret)
 234                goto err3;
 235
 236        return 0;
 237
 238err3:
 239        platform_device_del(omap->usb2_phy);
 240
 241err2:
 242        platform_device_put(omap->usb3_phy);
 243
 244err1:
 245        platform_device_put(omap->usb2_phy);
 246
 247        return ret;
 248}
 249
 250static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
 251{
 252        struct dwc3_omap        *omap = _omap;
 253        u32                     reg;
 254
 255        spin_lock(&omap->lock);
 256
 257        reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_1);
 258
 259        if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
 260                dev_dbg(omap->dev, "DMA Disable was Cleared\n");
 261                omap->dma_status = false;
 262        }
 263
 264        if (reg & USBOTGSS_IRQ1_OEVT)
 265                dev_dbg(omap->dev, "OTG Event\n");
 266
 267        if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE)
 268                dev_dbg(omap->dev, "DRVVBUS Rise\n");
 269
 270        if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE)
 271                dev_dbg(omap->dev, "CHRGVBUS Rise\n");
 272
 273        if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE)
 274                dev_dbg(omap->dev, "DISCHRGVBUS Rise\n");
 275
 276        if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE)
 277                dev_dbg(omap->dev, "IDPULLUP Rise\n");
 278
 279        if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL)
 280                dev_dbg(omap->dev, "DRVVBUS Fall\n");
 281
 282        if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL)
 283                dev_dbg(omap->dev, "CHRGVBUS Fall\n");
 284
 285        if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL)
 286                dev_dbg(omap->dev, "DISCHRGVBUS Fall\n");
 287
 288        if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
 289                dev_dbg(omap->dev, "IDPULLUP Fall\n");
 290
 291        dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
 292
 293        reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0);
 294        dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
 295
 296        spin_unlock(&omap->lock);
 297
 298        return IRQ_HANDLED;
 299}
 300
 301static int dwc3_omap_remove_core(struct device *dev, void *c)
 302{
 303        struct platform_device *pdev = to_platform_device(dev);
 304
 305        platform_device_unregister(pdev);
 306
 307        return 0;
 308}
 309
 310static int dwc3_omap_probe(struct platform_device *pdev)
 311{
 312        struct dwc3_omap_data   *pdata = pdev->dev.platform_data;
 313        struct device_node      *node = pdev->dev.of_node;
 314
 315        struct dwc3_omap        *omap;
 316        struct resource         *res;
 317        struct device           *dev = &pdev->dev;
 318
 319        int                     size;
 320        int                     ret = -ENOMEM;
 321        int                     irq;
 322
 323        const u32               *utmi_mode;
 324        u32                     reg;
 325
 326        void __iomem            *base;
 327        void                    *context;
 328
 329        omap = devm_kzalloc(dev, sizeof(*omap), GFP_KERNEL);
 330        if (!omap) {
 331                dev_err(dev, "not enough memory\n");
 332                return -ENOMEM;
 333        }
 334
 335        platform_set_drvdata(pdev, omap);
 336
 337        irq = platform_get_irq(pdev, 1);
 338        if (irq < 0) {
 339                dev_err(dev, "missing IRQ resource\n");
 340                return -EINVAL;
 341        }
 342
 343        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 344        if (!res) {
 345                dev_err(dev, "missing memory base resource\n");
 346                return -EINVAL;
 347        }
 348
 349        base = devm_ioremap_nocache(dev, res->start, resource_size(res));
 350        if (!base) {
 351                dev_err(dev, "ioremap failed\n");
 352                return -ENOMEM;
 353        }
 354
 355        ret = dwc3_omap_register_phys(omap);
 356        if (ret) {
 357                dev_err(dev, "couldn't register PHYs\n");
 358                return ret;
 359        }
 360
 361        context = devm_kzalloc(dev, resource_size(res), GFP_KERNEL);
 362        if (!context) {
 363                dev_err(dev, "couldn't allocate dwc3 context memory\n");
 364                return -ENOMEM;
 365        }
 366
 367        spin_lock_init(&omap->lock);
 368
 369        omap->resource_size = resource_size(res);
 370        omap->context   = context;
 371        omap->dev       = dev;
 372        omap->irq       = irq;
 373        omap->base      = base;
 374
 375        /*
 376         * REVISIT if we ever have two instances of the wrapper, we will be
 377         * in big trouble
 378         */
 379        _omap   = omap;
 380
 381        pm_runtime_enable(dev);
 382        ret = pm_runtime_get_sync(dev);
 383        if (ret < 0) {
 384                dev_err(dev, "get_sync failed with err %d\n", ret);
 385                return ret;
 386        }
 387
 388        reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
 389
 390        utmi_mode = of_get_property(node, "utmi-mode", &size);
 391        if (utmi_mode && size == sizeof(*utmi_mode)) {
 392                reg |= *utmi_mode;
 393        } else {
 394                if (!pdata) {
 395                        dev_dbg(dev, "missing platform data\n");
 396                } else {
 397                        switch (pdata->utmi_mode) {
 398                        case DWC3_OMAP_UTMI_MODE_SW:
 399                                reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
 400                                break;
 401                        case DWC3_OMAP_UTMI_MODE_HW:
 402                                reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
 403                                break;
 404                        default:
 405                                dev_dbg(dev, "UNKNOWN utmi mode %d\n",
 406                                                pdata->utmi_mode);
 407                        }
 408                }
 409        }
 410
 411        dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
 412
 413        /* check the DMA Status */
 414        reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
 415        omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
 416
 417        ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
 418                        "dwc3-omap", omap);
 419        if (ret) {
 420                dev_err(dev, "failed to request IRQ #%d --> %d\n",
 421                                omap->irq, ret);
 422                return ret;
 423        }
 424
 425        /* enable all IRQs */
 426        reg = USBOTGSS_IRQO_COREIRQ_ST;
 427        dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
 428
 429        reg = (USBOTGSS_IRQ1_OEVT |
 430                        USBOTGSS_IRQ1_DRVVBUS_RISE |
 431                        USBOTGSS_IRQ1_CHRGVBUS_RISE |
 432                        USBOTGSS_IRQ1_DISCHRGVBUS_RISE |
 433                        USBOTGSS_IRQ1_IDPULLUP_RISE |
 434                        USBOTGSS_IRQ1_DRVVBUS_FALL |
 435                        USBOTGSS_IRQ1_CHRGVBUS_FALL |
 436                        USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
 437                        USBOTGSS_IRQ1_IDPULLUP_FALL);
 438
 439        dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
 440
 441        if (node) {
 442                ret = of_platform_populate(node, NULL, NULL, dev);
 443                if (ret) {
 444                        dev_err(&pdev->dev,
 445                                "failed to add create dwc3 core\n");
 446                        return ret;
 447                }
 448        }
 449
 450        return 0;
 451}
 452
 453static int dwc3_omap_remove(struct platform_device *pdev)
 454{
 455        struct dwc3_omap        *omap = platform_get_drvdata(pdev);
 456
 457        platform_device_unregister(omap->usb2_phy);
 458        platform_device_unregister(omap->usb3_phy);
 459        pm_runtime_put_sync(&pdev->dev);
 460        pm_runtime_disable(&pdev->dev);
 461        device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
 462
 463        return 0;
 464}
 465
 466static const struct of_device_id of_dwc3_match[] = {
 467        {
 468                "ti,dwc3",
 469        },
 470        { },
 471};
 472MODULE_DEVICE_TABLE(of, of_dwc3_match);
 473
 474static struct platform_driver dwc3_omap_driver = {
 475        .probe          = dwc3_omap_probe,
 476        .remove         = dwc3_omap_remove,
 477        .driver         = {
 478                .name   = "omap-dwc3",
 479                .of_match_table = of_dwc3_match,
 480        },
 481};
 482
 483module_platform_driver(dwc3_omap_driver);
 484
 485MODULE_ALIAS("platform:omap-dwc3");
 486MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
 487MODULE_LICENSE("Dual BSD/GPL");
 488MODULE_DESCRIPTION("DesignWare USB3 OMAP Glue Layer");
 489
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.