linux/drivers/usb/cdns3/core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Cadence USBSS and USBSSP DRD Driver.
   4 *
   5 * Copyright (C) 2018-2019 Cadence.
   6 * Copyright (C) 2017-2018 NXP
   7 * Copyright (C) 2019 Texas Instruments
   8 *
   9 * Author: Peter Chen <peter.chen@nxp.com>
  10 *         Pawel Laszczak <pawell@cadence.com>
  11 *         Roger Quadros <rogerq@ti.com>
  12 */
  13
  14#include <linux/dma-mapping.h>
  15#include <linux/module.h>
  16#include <linux/kernel.h>
  17#include <linux/platform_device.h>
  18#include <linux/interrupt.h>
  19#include <linux/io.h>
  20#include <linux/pm_runtime.h>
  21
  22#include "core.h"
  23#include "host-export.h"
  24#include "drd.h"
  25
  26static int cdns_idle_init(struct cdns *cdns);
  27
  28static int cdns_role_start(struct cdns *cdns, enum usb_role role)
  29{
  30        int ret;
  31
  32        if (WARN_ON(role > USB_ROLE_DEVICE))
  33                return 0;
  34
  35        mutex_lock(&cdns->mutex);
  36        cdns->role = role;
  37        mutex_unlock(&cdns->mutex);
  38
  39        if (!cdns->roles[role])
  40                return -ENXIO;
  41
  42        if (cdns->roles[role]->state == CDNS_ROLE_STATE_ACTIVE)
  43                return 0;
  44
  45        mutex_lock(&cdns->mutex);
  46        ret = cdns->roles[role]->start(cdns);
  47        if (!ret)
  48                cdns->roles[role]->state = CDNS_ROLE_STATE_ACTIVE;
  49        mutex_unlock(&cdns->mutex);
  50
  51        return ret;
  52}
  53
  54static void cdns_role_stop(struct cdns *cdns)
  55{
  56        enum usb_role role = cdns->role;
  57
  58        if (WARN_ON(role > USB_ROLE_DEVICE))
  59                return;
  60
  61        if (cdns->roles[role]->state == CDNS_ROLE_STATE_INACTIVE)
  62                return;
  63
  64        mutex_lock(&cdns->mutex);
  65        cdns->roles[role]->stop(cdns);
  66        cdns->roles[role]->state = CDNS_ROLE_STATE_INACTIVE;
  67        mutex_unlock(&cdns->mutex);
  68}
  69
  70static void cdns_exit_roles(struct cdns *cdns)
  71{
  72        cdns_role_stop(cdns);
  73        cdns_drd_exit(cdns);
  74}
  75
  76/**
  77 * cdns_core_init_role - initialize role of operation
  78 * @cdns: Pointer to cdns structure
  79 *
  80 * Returns 0 on success otherwise negative errno
  81 */
  82static int cdns_core_init_role(struct cdns *cdns)
  83{
  84        struct device *dev = cdns->dev;
  85        enum usb_dr_mode best_dr_mode;
  86        enum usb_dr_mode dr_mode;
  87        int ret;
  88
  89        dr_mode = usb_get_dr_mode(dev);
  90        cdns->role = USB_ROLE_NONE;
  91
  92        /*
  93         * If driver can't read mode by means of usb_get_dr_mode function then
  94         * chooses mode according with Kernel configuration. This setting
  95         * can be restricted later depending on strap pin configuration.
  96         */
  97        if (dr_mode == USB_DR_MODE_UNKNOWN) {
  98                if (cdns->version == CDNSP_CONTROLLER_V2) {
  99                        if (IS_ENABLED(CONFIG_USB_CDNSP_HOST) &&
 100                            IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
 101                                dr_mode = USB_DR_MODE_OTG;
 102                        else if (IS_ENABLED(CONFIG_USB_CDNSP_HOST))
 103                                dr_mode = USB_DR_MODE_HOST;
 104                        else if (IS_ENABLED(CONFIG_USB_CDNSP_GADGET))
 105                                dr_mode = USB_DR_MODE_PERIPHERAL;
 106                } else {
 107                        if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
 108                            IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
 109                                dr_mode = USB_DR_MODE_OTG;
 110                        else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
 111                                dr_mode = USB_DR_MODE_HOST;
 112                        else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
 113                                dr_mode = USB_DR_MODE_PERIPHERAL;
 114                }
 115        }
 116
 117        /*
 118         * At this point cdns->dr_mode contains strap configuration.
 119         * Driver try update this setting considering kernel configuration
 120         */
 121        best_dr_mode = cdns->dr_mode;
 122
 123        ret = cdns_idle_init(cdns);
 124        if (ret)
 125                return ret;
 126
 127        if (dr_mode == USB_DR_MODE_OTG) {
 128                best_dr_mode = cdns->dr_mode;
 129        } else if (cdns->dr_mode == USB_DR_MODE_OTG) {
 130                best_dr_mode = dr_mode;
 131        } else if (cdns->dr_mode != dr_mode) {
 132                dev_err(dev, "Incorrect DRD configuration\n");
 133                return -EINVAL;
 134        }
 135
 136        dr_mode = best_dr_mode;
 137
 138        if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
 139                if ((cdns->version == CDNSP_CONTROLLER_V2 &&
 140                     IS_ENABLED(CONFIG_USB_CDNSP_HOST)) ||
 141                    (cdns->version < CDNSP_CONTROLLER_V2 &&
 142                     IS_ENABLED(CONFIG_USB_CDNS3_HOST)))
 143                        ret = cdns_host_init(cdns);
 144                else
 145                        ret = -ENXIO;
 146
 147                if (ret) {
 148                        dev_err(dev, "Host initialization failed with %d\n",
 149                                ret);
 150                        goto err;
 151                }
 152        }
 153
 154        if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
 155                if (cdns->gadget_init)
 156                        ret = cdns->gadget_init(cdns);
 157                else
 158                        ret = -ENXIO;
 159
 160                if (ret) {
 161                        dev_err(dev, "Device initialization failed with %d\n",
 162                                ret);
 163                        goto err;
 164                }
 165        }
 166
 167        cdns->dr_mode = dr_mode;
 168
 169        ret = cdns_drd_update_mode(cdns);
 170        if (ret)
 171                goto err;
 172
 173        /* Initialize idle role to start with */
 174        ret = cdns_role_start(cdns, USB_ROLE_NONE);
 175        if (ret)
 176                goto err;
 177
 178        switch (cdns->dr_mode) {
 179        case USB_DR_MODE_OTG:
 180                ret = cdns_hw_role_switch(cdns);
 181                if (ret)
 182                        goto err;
 183                break;
 184        case USB_DR_MODE_PERIPHERAL:
 185                ret = cdns_role_start(cdns, USB_ROLE_DEVICE);
 186                if (ret)
 187                        goto err;
 188                break;
 189        case USB_DR_MODE_HOST:
 190                ret = cdns_role_start(cdns, USB_ROLE_HOST);
 191                if (ret)
 192                        goto err;
 193                break;
 194        default:
 195                ret = -EINVAL;
 196                goto err;
 197        }
 198
 199        return 0;
 200err:
 201        cdns_exit_roles(cdns);
 202        return ret;
 203}
 204
 205/**
 206 * cdns_hw_role_state_machine  - role switch state machine based on hw events.
 207 * @cdns: Pointer to controller structure.
 208 *
 209 * Returns next role to be entered based on hw events.
 210 */
 211static enum usb_role cdns_hw_role_state_machine(struct cdns *cdns)
 212{
 213        enum usb_role role = USB_ROLE_NONE;
 214        int id, vbus;
 215
 216        if (cdns->dr_mode != USB_DR_MODE_OTG) {
 217                if (cdns_is_host(cdns))
 218                        role = USB_ROLE_HOST;
 219                if (cdns_is_device(cdns))
 220                        role = USB_ROLE_DEVICE;
 221
 222                return role;
 223        }
 224
 225        id = cdns_get_id(cdns);
 226        vbus = cdns_get_vbus(cdns);
 227
 228        /*
 229         * Role change state machine
 230         * Inputs: ID, VBUS
 231         * Previous state: cdns->role
 232         * Next state: role
 233         */
 234        role = cdns->role;
 235
 236        switch (role) {
 237        case USB_ROLE_NONE:
 238                /*
 239                 * Driver treats USB_ROLE_NONE synonymous to IDLE state from
 240                 * controller specification.
 241                 */
 242                if (!id)
 243                        role = USB_ROLE_HOST;
 244                else if (vbus)
 245                        role = USB_ROLE_DEVICE;
 246                break;
 247        case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
 248                if (id)
 249                        role = USB_ROLE_NONE;
 250                break;
 251        case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
 252                if (!vbus)
 253                        role = USB_ROLE_NONE;
 254                break;
 255        }
 256
 257        dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
 258
 259        return role;
 260}
 261
 262static int cdns_idle_role_start(struct cdns *cdns)
 263{
 264        return 0;
 265}
 266
 267static void cdns_idle_role_stop(struct cdns *cdns)
 268{
 269        /* Program Lane swap and bring PHY out of RESET */
 270        phy_reset(cdns->usb3_phy);
 271}
 272
 273static int cdns_idle_init(struct cdns *cdns)
 274{
 275        struct cdns_role_driver *rdrv;
 276
 277        rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
 278        if (!rdrv)
 279                return -ENOMEM;
 280
 281        rdrv->start = cdns_idle_role_start;
 282        rdrv->stop = cdns_idle_role_stop;
 283        rdrv->state = CDNS_ROLE_STATE_INACTIVE;
 284        rdrv->suspend = NULL;
 285        rdrv->resume = NULL;
 286        rdrv->name = "idle";
 287
 288        cdns->roles[USB_ROLE_NONE] = rdrv;
 289
 290        return 0;
 291}
 292
 293/**
 294 * cdns_hw_role_switch - switch roles based on HW state
 295 * @cdns: controller
 296 */
 297int cdns_hw_role_switch(struct cdns *cdns)
 298{
 299        enum usb_role real_role, current_role;
 300        int ret = 0;
 301
 302        /* Depends on role switch class */
 303        if (cdns->role_sw)
 304                return 0;
 305
 306        pm_runtime_get_sync(cdns->dev);
 307
 308        current_role = cdns->role;
 309        real_role = cdns_hw_role_state_machine(cdns);
 310
 311        /* Do nothing if nothing changed */
 312        if (current_role == real_role)
 313                goto exit;
 314
 315        cdns_role_stop(cdns);
 316
 317        dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
 318
 319        ret = cdns_role_start(cdns, real_role);
 320        if (ret) {
 321                /* Back to current role */
 322                dev_err(cdns->dev, "set %d has failed, back to %d\n",
 323                        real_role, current_role);
 324                ret = cdns_role_start(cdns, current_role);
 325                if (ret)
 326                        dev_err(cdns->dev, "back to %d failed too\n",
 327                                current_role);
 328        }
 329exit:
 330        pm_runtime_put_sync(cdns->dev);
 331        return ret;
 332}
 333
 334/**
 335 * cdns_role_get - get current role of controller.
 336 *
 337 * @sw: pointer to USB role switch structure
 338 *
 339 * Returns role
 340 */
 341static enum usb_role cdns_role_get(struct usb_role_switch *sw)
 342{
 343        struct cdns *cdns = usb_role_switch_get_drvdata(sw);
 344
 345        return cdns->role;
 346}
 347
 348/**
 349 * cdns_role_set - set current role of controller.
 350 *
 351 * @sw: pointer to USB role switch structure
 352 * @role: the previous role
 353 * Handles below events:
 354 * - Role switch for dual-role devices
 355 * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
 356 */
 357static int cdns_role_set(struct usb_role_switch *sw, enum usb_role role)
 358{
 359        struct cdns *cdns = usb_role_switch_get_drvdata(sw);
 360        int ret = 0;
 361
 362        pm_runtime_get_sync(cdns->dev);
 363
 364        if (cdns->role == role)
 365                goto pm_put;
 366
 367        if (cdns->dr_mode == USB_DR_MODE_HOST) {
 368                switch (role) {
 369                case USB_ROLE_NONE:
 370                case USB_ROLE_HOST:
 371                        break;
 372                default:
 373                        goto pm_put;
 374                }
 375        }
 376
 377        if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) {
 378                switch (role) {
 379                case USB_ROLE_NONE:
 380                case USB_ROLE_DEVICE:
 381                        break;
 382                default:
 383                        goto pm_put;
 384                }
 385        }
 386
 387        cdns_role_stop(cdns);
 388        ret = cdns_role_start(cdns, role);
 389        if (ret)
 390                dev_err(cdns->dev, "set role %d has failed\n", role);
 391
 392pm_put:
 393        pm_runtime_put_sync(cdns->dev);
 394        return ret;
 395}
 396
 397
 398/**
 399 * cdns_wakeup_irq - interrupt handler for wakeup events
 400 * @irq: irq number for cdns3/cdnsp core device
 401 * @data: structure of cdns
 402 *
 403 * Returns IRQ_HANDLED or IRQ_NONE
 404 */
 405static irqreturn_t cdns_wakeup_irq(int irq, void *data)
 406{
 407        struct cdns *cdns = data;
 408
 409        if (cdns->in_lpm) {
 410                disable_irq_nosync(irq);
 411                cdns->wakeup_pending = true;
 412                if ((cdns->role == USB_ROLE_HOST) && cdns->host_dev)
 413                        pm_request_resume(&cdns->host_dev->dev);
 414
 415                return IRQ_HANDLED;
 416        }
 417
 418        return IRQ_NONE;
 419}
 420
 421/**
 422 * cdns_init - probe for cdns3/cdnsp core device
 423 * @cdns: Pointer to cdns structure.
 424 *
 425 * Returns 0 on success otherwise negative errno
 426 */
 427int cdns_init(struct cdns *cdns)
 428{
 429        struct device *dev = cdns->dev;
 430        int ret;
 431
 432        ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
 433        if (ret) {
 434                dev_err(dev, "error setting dma mask: %d\n", ret);
 435                return ret;
 436        }
 437
 438        mutex_init(&cdns->mutex);
 439
 440        if (device_property_read_bool(dev, "usb-role-switch")) {
 441                struct usb_role_switch_desc sw_desc = { };
 442
 443                sw_desc.set = cdns_role_set;
 444                sw_desc.get = cdns_role_get;
 445                sw_desc.allow_userspace_control = true;
 446                sw_desc.driver_data = cdns;
 447                sw_desc.fwnode = dev->fwnode;
 448
 449                cdns->role_sw = usb_role_switch_register(dev, &sw_desc);
 450                if (IS_ERR(cdns->role_sw)) {
 451                        dev_warn(dev, "Unable to register Role Switch\n");
 452                        return PTR_ERR(cdns->role_sw);
 453                }
 454        }
 455
 456        if (cdns->wakeup_irq) {
 457                ret = devm_request_irq(cdns->dev, cdns->wakeup_irq,
 458                                                cdns_wakeup_irq,
 459                                                IRQF_SHARED,
 460                                                dev_name(cdns->dev), cdns);
 461
 462                if (ret) {
 463                        dev_err(cdns->dev, "couldn't register wakeup irq handler\n");
 464                        goto role_switch_unregister;
 465                }
 466        }
 467
 468        ret = cdns_drd_init(cdns);
 469        if (ret)
 470                goto init_failed;
 471
 472        ret = cdns_core_init_role(cdns);
 473        if (ret)
 474                goto init_failed;
 475
 476        spin_lock_init(&cdns->lock);
 477
 478        dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
 479
 480        return 0;
 481init_failed:
 482        cdns_drd_exit(cdns);
 483role_switch_unregister:
 484        if (cdns->role_sw)
 485                usb_role_switch_unregister(cdns->role_sw);
 486
 487        return ret;
 488}
 489EXPORT_SYMBOL_GPL(cdns_init);
 490
 491/**
 492 * cdns_remove - unbind drd driver and clean up
 493 * @cdns: Pointer to cdns structure.
 494 *
 495 * Returns 0 on success otherwise negative errno
 496 */
 497int cdns_remove(struct cdns *cdns)
 498{
 499        cdns_exit_roles(cdns);
 500        usb_role_switch_unregister(cdns->role_sw);
 501
 502        return 0;
 503}
 504EXPORT_SYMBOL_GPL(cdns_remove);
 505
 506#ifdef CONFIG_PM_SLEEP
 507int cdns_suspend(struct cdns *cdns)
 508{
 509        struct device *dev = cdns->dev;
 510        unsigned long flags;
 511
 512        if (pm_runtime_status_suspended(dev))
 513                pm_runtime_resume(dev);
 514
 515        if (cdns->roles[cdns->role]->suspend) {
 516                spin_lock_irqsave(&cdns->lock, flags);
 517                cdns->roles[cdns->role]->suspend(cdns, false);
 518                spin_unlock_irqrestore(&cdns->lock, flags);
 519        }
 520
 521        return 0;
 522}
 523EXPORT_SYMBOL_GPL(cdns_suspend);
 524
 525int cdns_resume(struct cdns *cdns, u8 set_active)
 526{
 527        struct device *dev = cdns->dev;
 528        enum usb_role real_role;
 529        bool role_changed = false;
 530        int ret = 0;
 531
 532        if (cdns_power_is_lost(cdns)) {
 533                if (cdns->role_sw) {
 534                        cdns->role = cdns_role_get(cdns->role_sw);
 535                } else {
 536                        real_role = cdns_hw_role_state_machine(cdns);
 537                        if (real_role != cdns->role) {
 538                                ret = cdns_hw_role_switch(cdns);
 539                                if (ret)
 540                                        return ret;
 541                                role_changed = true;
 542                        }
 543                }
 544
 545                if (!role_changed) {
 546                        if (cdns->role == USB_ROLE_HOST)
 547                                ret = cdns_drd_host_on(cdns);
 548                        else if (cdns->role == USB_ROLE_DEVICE)
 549                                ret = cdns_drd_gadget_on(cdns);
 550
 551                        if (ret)
 552                                return ret;
 553                }
 554        }
 555
 556        if (cdns->roles[cdns->role]->resume)
 557                cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns));
 558
 559        if (set_active) {
 560                pm_runtime_disable(dev);
 561                pm_runtime_set_active(dev);
 562                pm_runtime_enable(dev);
 563        }
 564
 565        return 0;
 566}
 567EXPORT_SYMBOL_GPL(cdns_resume);
 568#endif /* CONFIG_PM_SLEEP */
 569
 570MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
 571MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
 572MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
 573MODULE_DESCRIPTION("Cadence USBSS and USBSSP DRD Driver");
 574MODULE_LICENSE("GPL");
 575