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 /*
21 * The XECP_PORT_CAP_REG and XECP_AUX_CTRL_REG1 exist only
22 * in Cadence USB3 dual-role controller, so it can't be used
23 * with Cadence CDNSP dual-role controller.
24 */
25 #define XECP_PORT_CAP_REG 0x8000
26 #define XECP_AUX_CTRL_REG1 0x8120
27
28 #define CFG_RXDET_P3_EN BIT(15)
29 #define LPM_2_STB_SWITCH_EN BIT(25)
30
xhci_cdns3_plat_start(struct usb_hcd * hcd)31 static void xhci_cdns3_plat_start(struct usb_hcd *hcd)
32 {
33 struct xhci_hcd *xhci = hcd_to_xhci(hcd);
34 u32 value;
35
36 /* set usbcmd.EU3S */
37 value = readl(&xhci->op_regs->command);
38 value |= CMD_PM_INDEX;
39 writel(value, &xhci->op_regs->command);
40
41 if (hcd->regs) {
42 value = readl(hcd->regs + XECP_AUX_CTRL_REG1);
43 value |= CFG_RXDET_P3_EN;
44 writel(value, hcd->regs + XECP_AUX_CTRL_REG1);
45
46 value = readl(hcd->regs + XECP_PORT_CAP_REG);
47 value |= LPM_2_STB_SWITCH_EN;
48 writel(value, hcd->regs + XECP_PORT_CAP_REG);
49 }
50 }
51
xhci_cdns3_resume_quirk(struct usb_hcd * hcd)52 static int xhci_cdns3_resume_quirk(struct usb_hcd *hcd)
53 {
54 xhci_cdns3_plat_start(hcd);
55 return 0;
56 }
57
58 static const struct xhci_plat_priv xhci_plat_cdns3_xhci = {
59 .quirks = XHCI_SKIP_PHY_INIT | XHCI_AVOID_BEI,
60 .plat_start = xhci_cdns3_plat_start,
61 .resume_quirk = xhci_cdns3_resume_quirk,
62 };
63
64 static const struct xhci_plat_priv xhci_plat_cdnsp_xhci;
65
__cdns_host_init(struct cdns * cdns)66 static int __cdns_host_init(struct cdns *cdns)
67 {
68 struct platform_device *xhci;
69 int ret;
70 struct usb_hcd *hcd;
71
72 cdns_drd_host_on(cdns);
73
74 xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
75 if (!xhci) {
76 dev_err(cdns->dev, "couldn't allocate xHCI device\n");
77 return -ENOMEM;
78 }
79
80 xhci->dev.parent = cdns->dev;
81 cdns->host_dev = xhci;
82
83 ret = platform_device_add_resources(xhci, cdns->xhci_res,
84 CDNS_XHCI_RESOURCES_NUM);
85 if (ret) {
86 dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
87 goto err1;
88 }
89
90 if (cdns->version < CDNSP_CONTROLLER_V2)
91 cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci,
92 sizeof(struct xhci_plat_priv), GFP_KERNEL);
93 else
94 cdns->xhci_plat_data = kmemdup(&xhci_plat_cdnsp_xhci,
95 sizeof(struct xhci_plat_priv), GFP_KERNEL);
96
97 if (!cdns->xhci_plat_data) {
98 ret = -ENOMEM;
99 goto err1;
100 }
101
102 if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))
103 cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
104
105 ret = platform_device_add_data(xhci, cdns->xhci_plat_data,
106 sizeof(struct xhci_plat_priv));
107 if (ret)
108 goto free_memory;
109
110 ret = platform_device_add(xhci);
111 if (ret) {
112 dev_err(cdns->dev, "failed to register xHCI device\n");
113 goto free_memory;
114 }
115
116 /* Glue needs to access xHCI region register for Power management */
117 hcd = platform_get_drvdata(xhci);
118 if (hcd)
119 cdns->xhci_regs = hcd->regs;
120
121 return 0;
122
123 free_memory:
124 kfree(cdns->xhci_plat_data);
125 err1:
126 platform_device_put(xhci);
127 return ret;
128 }
129
cdns_host_exit(struct cdns * cdns)130 static void cdns_host_exit(struct cdns *cdns)
131 {
132 kfree(cdns->xhci_plat_data);
133 platform_device_unregister(cdns->host_dev);
134 cdns->host_dev = NULL;
135 cdns_drd_host_off(cdns);
136 }
137
cdns_host_init(struct cdns * cdns)138 int cdns_host_init(struct cdns *cdns)
139 {
140 struct cdns_role_driver *rdrv;
141
142 rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
143 if (!rdrv)
144 return -ENOMEM;
145
146 rdrv->start = __cdns_host_init;
147 rdrv->stop = cdns_host_exit;
148 rdrv->state = CDNS_ROLE_STATE_INACTIVE;
149 rdrv->name = "host";
150
151 cdns->roles[USB_ROLE_HOST] = rdrv;
152
153 return 0;
154 }
155