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 | |
18a6be67 PL |
65 | static const struct xhci_plat_priv xhci_plat_cdnsp_xhci; |
66 | ||
0b490046 | 67 | static int __cdns_host_init(struct cdns *cdns) |
7733f6c3 PL |
68 | { |
69 | struct platform_device *xhci; | |
70 | int ret; | |
b1234e3b | 71 | struct usb_hcd *hcd; |
7733f6c3 | 72 | |
0b490046 | 73 | cdns_drd_host_on(cdns); |
7733f6c3 PL |
74 | |
75 | xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); | |
76 | if (!xhci) { | |
77 | dev_err(cdns->dev, "couldn't allocate xHCI device\n"); | |
78 | return -ENOMEM; | |
79 | } | |
80 | ||
81 | xhci->dev.parent = cdns->dev; | |
82 | cdns->host_dev = xhci; | |
83 | ||
84 | ret = platform_device_add_resources(xhci, cdns->xhci_res, | |
0b490046 | 85 | CDNS_XHCI_RESOURCES_NUM); |
7733f6c3 PL |
86 | if (ret) { |
87 | dev_err(cdns->dev, "couldn't add resources to xHCI device\n"); | |
88 | goto err1; | |
89 | } | |
90 | ||
18a6be67 PL |
91 | if (cdns->version < CDNSP_CONTROLLER_V2) |
92 | cdns->xhci_plat_data = kmemdup(&xhci_plat_cdns3_xhci, | |
93 | sizeof(struct xhci_plat_priv), GFP_KERNEL); | |
94 | else | |
95 | cdns->xhci_plat_data = kmemdup(&xhci_plat_cdnsp_xhci, | |
96 | sizeof(struct xhci_plat_priv), GFP_KERNEL); | |
97 | ||
7cea9657 PC |
98 | if (!cdns->xhci_plat_data) { |
99 | ret = -ENOMEM; | |
100 | goto err1; | |
101 | } | |
102 | ||
448373d9 | 103 | if (cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)) |
7cea9657 PC |
104 | cdns->xhci_plat_data->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; |
105 | ||
106 | ret = platform_device_add_data(xhci, cdns->xhci_plat_data, | |
ed227648 PC |
107 | sizeof(struct xhci_plat_priv)); |
108 | if (ret) | |
7cea9657 | 109 | goto free_memory; |
ed227648 | 110 | |
7733f6c3 PL |
111 | ret = platform_device_add(xhci); |
112 | if (ret) { | |
113 | dev_err(cdns->dev, "failed to register xHCI device\n"); | |
7cea9657 | 114 | goto free_memory; |
7733f6c3 PL |
115 | } |
116 | ||
b1234e3b PC |
117 | /* Glue needs to access xHCI region register for Power management */ |
118 | hcd = platform_get_drvdata(xhci); | |
119 | if (hcd) | |
120 | cdns->xhci_regs = hcd->regs; | |
121 | ||
7733f6c3 | 122 | return 0; |
7cea9657 PC |
123 | |
124 | free_memory: | |
125 | kfree(cdns->xhci_plat_data); | |
7733f6c3 PL |
126 | err1: |
127 | platform_device_put(xhci); | |
128 | return ret; | |
129 | } | |
130 | ||
0b490046 | 131 | static void cdns_host_exit(struct cdns *cdns) |
7733f6c3 | 132 | { |
7cea9657 | 133 | kfree(cdns->xhci_plat_data); |
7733f6c3 PL |
134 | platform_device_unregister(cdns->host_dev); |
135 | cdns->host_dev = NULL; | |
0b490046 | 136 | cdns_drd_host_off(cdns); |
7733f6c3 PL |
137 | } |
138 | ||
0b490046 | 139 | int cdns_host_init(struct cdns *cdns) |
7733f6c3 | 140 | { |
0b490046 | 141 | struct cdns_role_driver *rdrv; |
7733f6c3 PL |
142 | |
143 | rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL); | |
144 | if (!rdrv) | |
145 | return -ENOMEM; | |
146 | ||
0b490046 PL |
147 | rdrv->start = __cdns_host_init; |
148 | rdrv->stop = cdns_host_exit; | |
149 | rdrv->state = CDNS_ROLE_STATE_INACTIVE; | |
7733f6c3 PL |
150 | rdrv->name = "host"; |
151 | ||
152 | cdns->roles[USB_ROLE_HOST] = rdrv; | |
153 | ||
154 | return 0; | |
155 | } |