linux/drivers/usb/cdns3/host.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Cadence USBSS and USBSSP DRD Driver - host side
   4 *
   5 * Copyright (C) 2018-2019 Cadence Design Systems.
   6 * Copyright (C) 2017-2018 NXP
   7 *
   8 * Authors: Peter Chen <peter.chen@nxp.com>
   9 *          Pawel Laszczak <pawell@cadence.com>
  10 */
  11
  12#include <linux/platform_device.h>
  13#include "core.h"
  14#include "drd.h"
  15#include "host-export.h"
  16#include <linux/usb/hcd.h>
  17#include "../host/xhci.h"
  18#include "../host/xhci-plat.h"
  19
  20#define XECP_PORT_CAP_REG       0x8000
  21#define XECP_AUX_CTRL_REG1      0x8120
  22
  23#define CFG_RXDET_P3_EN         BIT(15)
  24#define LPM_2_STB_SWITCH_EN     BIT(25)
  25
  26static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd);
  27
  28static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
  29        .quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI,
  30        .suspend_quirk = xhci_cdns3_suspend_quirk,
  31};
  32
  33static int __cdns_host_init(struct cdns *cdns)
  34{
  35        struct platform_device *xhci;
  36        int ret;
  37        struct usb_hcd *hcd;
  38
  39        cdns_drd_host_on(cdns);
  40
  41        xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
  42        if (!xhci) {
  43                dev_err(cdns->dev, "couldn't allocate xHCI device\n");
  44                return -ENOMEM;
  45        }
  46
  47        xhci->dev.parent = cdns->dev;
  48        cdns->host_dev = xhci;
  49
  50        ret = platform_device_add_resources(xhci, cdns->xhci_res,
  51                                            CDNS_XHCI_RESOURCES_NUM);
  52        if (ret) {
  53                dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
  54                goto err1;
  55        }
  56
  57        cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci,
  58                        sizeof(struct xhci_plat_priv), GFP_KERNEL);
  59        if (!cdns->xhci_plat_data) {
  60                ret = -ENOMEM;
  61                goto err1;
  62        }
  63
  64        if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))
  65                cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
  66
  67        ret = platform_device_add_data(xhci, cdns->xhci_plat_data,
  68                        sizeof(struct xhci_plat_priv));
  69        if (ret)
  70                goto free_memory;
  71
  72        ret = platform_device_add(xhci);
  73        if (ret) {
  74                dev_err(cdns->dev, "failed to register xHCI device\n");
  75                goto free_memory;
  76        }
  77
  78        /* Glue needs to access xHCI region register for Power management */
  79        hcd = platform_get_drvdata(xhci);
  80        if (hcd)
  81                cdns->xhci_regs = hcd->regs;
  82
  83        return 0;
  84
  85free_memory:
  86        kfree(cdns->xhci_plat_data);
  87err1:
  88        platform_device_put(xhci);
  89        return ret;
  90}
  91
  92static int xhci_cdns3_suspend_quirk(struct usb_hcd *hcd)
  93{
  94        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
  95        u32 value;
  96
  97        if (pm_runtime_status_suspended(hcd->self.controller))
  98                return 0;
  99
 100        /* set usbcmd.EU3S */
 101        value = readl(&xhci->op_regs->command);
 102        value |= CMD_PM_INDEX;
 103        writel(value, &xhci->op_regs->command);
 104
 105        if (hcd->regs) {
 106                value = readl(hcd->regs + XECP_AUX_CTRL_REG1);
 107                value |= CFG_RXDET_P3_EN;
 108                writel(value, hcd->regs + XECP_AUX_CTRL_REG1);
 109
 110                value = readl(hcd->regs + XECP_PORT_CAP_REG);
 111                value |= LPM_2_STB_SWITCH_EN;
 112                writel(value, hcd->regs + XECP_PORT_CAP_REG);
 113        }
 114
 115        return 0;
 116}
 117
 118static void cdns_host_exit(struct cdns *cdns)
 119{
 120        kfree(cdns->xhci_plat_data);
 121        platform_device_unregister(cdns->host_dev);
 122        cdns->host_dev = NULL;
 123        cdns_drd_host_off(cdns);
 124}
 125
 126int cdns_host_init(struct cdns *cdns)
 127{
 128        struct cdns_role_driver *rdrv;
 129
 130        rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
 131        if (!rdrv)
 132                return -ENOMEM;
 133
 134        rdrv->start     = __cdns_host_init;
 135        rdrv->stop      = cdns_host_exit;
 136        rdrv->state     = CDNS_ROLE_STATE_INACTIVE;
 137        rdrv->name      = "host";
 138
 139        cdns->roles[USB_ROLE_HOST] = rdrv;
 140
 141        return 0;
 142}
 143