• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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