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