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