xref: /aosp_15_r20/external/coreboot/payloads/libpayload/drivers/usb/dwc2_rh.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /*
2  *
3  * Copyright (C) 2014 Rockchip Electronics
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; version 2 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 
15 #include <usb/usb.h>
16 #include "generic_hub.h"
17 #include "dwc2_private.h"
18 #include "dwc2.h"
19 
20 static int
dwc2_rh_port_status_changed(usbdev_t * const dev,const int port)21 dwc2_rh_port_status_changed(usbdev_t *const dev, const int port)
22 {
23 	hprt_t hprt;
24 	int changed;
25 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
26 
27 	hprt.d32 = readl(dwc2->hprt0);
28 	changed = hprt.prtconndet;
29 
30 	/* Clear connect detect flag */
31 	if (changed) {
32 		hprt.d32 &= HPRT_W1C_MASK;
33 		hprt.prtconndet = 1;
34 		writel(hprt.d32, dwc2->hprt0);
35 	}
36 	return changed;
37 }
38 
39 static int
dwc2_rh_port_connected(usbdev_t * const dev,const int port)40 dwc2_rh_port_connected(usbdev_t *const dev, const int port)
41 {
42 	hprt_t hprt;
43 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
44 
45 	hprt.d32 = readl(dwc2->hprt0);
46 	return hprt.prtconnsts;
47 }
48 
49 static int
dwc2_rh_port_in_reset(usbdev_t * const dev,const int port)50 dwc2_rh_port_in_reset(usbdev_t *const dev, const int port)
51 {
52 	hprt_t hprt;
53 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
54 
55 	hprt.d32 = readl(dwc2->hprt0);
56 	return hprt.prtrst;
57 }
58 
59 static int
dwc2_rh_port_enabled(usbdev_t * const dev,const int port)60 dwc2_rh_port_enabled(usbdev_t *const dev, const int port)
61 {
62 	hprt_t hprt;
63 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
64 
65 	hprt.d32 = readl(dwc2->hprt0);
66 	return hprt.prtena;
67 }
68 
69 static usb_speed
dwc2_rh_port_speed(usbdev_t * const dev,const int port)70 dwc2_rh_port_speed(usbdev_t *const dev, const int port)
71 {
72 	hprt_t hprt;
73 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
74 
75 	hprt.d32 = readl(dwc2->hprt0);
76 	if (hprt.prtena) {
77 		switch (hprt.prtspd) {
78 		case PRTSPD_HIGH:
79 			return HIGH_SPEED;
80 		case PRTSPD_FULL:
81 			return FULL_SPEED;
82 		case PRTSPD_LOW:
83 			return LOW_SPEED;
84 		}
85 	}
86 	return -1;
87 }
88 
89 static int
dwc2_rh_reset_port(usbdev_t * const dev,const int port)90 dwc2_rh_reset_port(usbdev_t *const dev, const int port)
91 {
92 	hprt_t hprt;
93 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
94 
95 	hprt.d32 = readl(dwc2->hprt0);
96 	hprt.d32 &= HPRT_W1C_MASK;
97 	hprt.prtrst = 1;
98 	writel(hprt.d32, dwc2->hprt0);
99 
100 	/* Wait a bit while reset is active. */
101 	mdelay(50);
102 
103 	/* Deassert reset. */
104 	hprt.prtrst = 0;
105 	writel(hprt.d32, dwc2->hprt0);
106 
107 	/*
108 	 * If reset and speed enum success the DWC2 core will set enable bit
109 	 * after port reset bit is deasserted
110 	 */
111 	mdelay(1);
112 	hprt.d32 = readl(dwc2->hprt0);
113 	usb_debug("%s reset port ok, hprt = 0x%08x\n", __func__, hprt.d32);
114 
115 	if (!hprt.prtena) {
116 		usb_debug("%s enable port fail! hprt = 0x%08x\n",
117 			  __func__, hprt.d32);
118 		return -1;
119 	}
120 
121 	return 0;
122 }
123 
124 static int
dwc2_rh_enable_port(usbdev_t * const dev,const int port)125 dwc2_rh_enable_port(usbdev_t *const dev, const int port)
126 {
127 	hprt_t hprt;
128 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
129 
130 	/* Power on the port */
131 	hprt.d32 = readl(dwc2->hprt0);
132 	hprt.d32 &= HPRT_W1C_MASK;
133 	hprt.prtpwr = 1;
134 	writel(hprt.d32, dwc2->hprt0);
135 	return 0;
136 }
137 
138 static int
dwc2_rh_disable_port(usbdev_t * const dev,const int port)139 dwc2_rh_disable_port(usbdev_t *const dev, const int port)
140 {
141 	hprt_t hprt;
142 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
143 
144 	hprt.d32 = readl(dwc2->hprt0);
145 	hprt.d32 &= HPRT_W1C_MASK;
146 	/* Disable the port*/
147 	hprt.prtena = 1;
148 	/* Power off the port */
149 	hprt.prtpwr = 0;
150 	writel(hprt.d32, dwc2->hprt0);
151 	return 0;
152 }
153 
154 static const generic_hub_ops_t dwc2_rh_ops = {
155 	.hub_status_changed	= NULL,
156 	.port_status_changed	= dwc2_rh_port_status_changed,
157 	.port_connected		= dwc2_rh_port_connected,
158 	.port_in_reset		= dwc2_rh_port_in_reset,
159 	.port_enabled		= dwc2_rh_port_enabled,
160 	.port_speed		= dwc2_rh_port_speed,
161 	.enable_port		= dwc2_rh_enable_port,
162 	.disable_port		= dwc2_rh_disable_port,
163 	.start_port_reset	= NULL,
164 	.reset_port		= dwc2_rh_reset_port,
165 };
166 
167 void
dwc2_rh_init(usbdev_t * dev)168 dwc2_rh_init(usbdev_t *dev)
169 {
170 	dwc_ctrl_t *const dwc2 = DWC2_INST(dev->controller);
171 
172 	/* we can set them here because a root hub _really_ shouldn't
173 	   appear elsewhere */
174 	dev->address = 0;
175 	dev->hub = -1;
176 	dev->port = -1;
177 
178 	generic_hub_init(dev, 1, &dwc2_rh_ops);
179 	usb_debug("dwc2_rh_init HPRT 0x%08x p = %p\n ",
180 		  readl(dwc2->hprt0), dwc2->hprt0);
181 	usb_debug("DWC2: root hub init done\n");
182 }
183