]>
Commit | Line | Data |
---|---|---|
6b92487d | 1 | /* |
1b719e66 | 2 | * (C) Copyright 2009, 2011 Freescale Semiconductor, Inc. |
4ef01010 | 3 | * |
6b92487d MT |
4 | * (C) Copyright 2008, Excito Elektronik i Sk=E5ne AB |
5 | * | |
6 | * Author: Tor Krill tor@excito.com | |
7 | * | |
1a459660 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
6b92487d MT |
9 | */ |
10 | ||
11 | #include <common.h> | |
12 | #include <pci.h> | |
13 | #include <usb.h> | |
6b92487d | 14 | #include <asm/io.h> |
4ef01010 | 15 | #include <usb/ehci-fsl.h> |
1b719e66 | 16 | #include <hwconfig.h> |
c26c80a1 | 17 | #include <fsl_usb.h> |
a1c04e27 | 18 | #include <fdt_support.h> |
6b92487d | 19 | |
2731b9a8 | 20 | #include "ehci.h" |
6b92487d | 21 | |
a1c04e27 NB |
22 | #ifndef CONFIG_USB_MAX_CONTROLLER_COUNT |
23 | #define CONFIG_USB_MAX_CONTROLLER_COUNT 1 | |
24 | #endif | |
25 | ||
896720ce NB |
26 | static void set_txfifothresh(struct usb_ehci *, u32); |
27 | ||
047cea36 SL |
28 | /* Check USB PHY clock valid */ |
29 | static int usb_phy_clk_valid(struct usb_ehci *ehci) | |
30 | { | |
31 | if (!((in_be32(&ehci->control) & PHY_CLK_VALID) || | |
32 | in_be32(&ehci->prictrl))) { | |
33 | printf("USB PHY clock invalid!\n"); | |
34 | return 0; | |
35 | } else { | |
36 | return 1; | |
37 | } | |
38 | } | |
39 | ||
6b92487d MT |
40 | /* |
41 | * Create the appropriate control structures to manage | |
42 | * a new EHCI host controller. | |
43 | * | |
44 | * Excerpts from linux ehci fsl driver. | |
45 | */ | |
127efc4f TK |
46 | int ehci_hcd_init(int index, enum usb_init_type init, |
47 | struct ehci_hccr **hccr, struct ehci_hcor **hcor) | |
6b92487d | 48 | { |
77354e9d | 49 | struct usb_ehci *ehci = NULL; |
1b719e66 RM |
50 | const char *phy_type = NULL; |
51 | size_t len; | |
0ecb15c8 | 52 | char current_usb_controller[5]; |
dd22f7cf KG |
53 | #ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY |
54 | char usb_phy[5]; | |
1b719e66 RM |
55 | |
56 | usb_phy[0] = '\0'; | |
dd22f7cf | 57 | #endif |
11856919 NB |
58 | if (has_erratum_a007075()) { |
59 | /* | |
60 | * A 5ms delay is needed after applying soft-reset to the | |
61 | * controller to let external ULPI phy come out of reset. | |
62 | * This delay needs to be added before re-initializing | |
63 | * the controller after soft-resetting completes | |
64 | */ | |
65 | mdelay(5); | |
66 | } | |
0ecb15c8 NB |
67 | memset(current_usb_controller, '\0', 5); |
68 | snprintf(current_usb_controller, 4, "usb%d", index+1); | |
6b92487d | 69 | |
77354e9d | 70 | switch (index) { |
71 | case 0: | |
72 | ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB1_ADDR; | |
73 | break; | |
74 | case 1: | |
75 | ehci = (struct usb_ehci *)CONFIG_SYS_FSL_USB2_ADDR; | |
76 | break; | |
77 | default: | |
78 | printf("ERROR: wrong controller index!!\n"); | |
19b17d12 | 79 | return -EINVAL; |
77354e9d | 80 | }; |
81 | ||
676ae068 LS |
82 | *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); |
83 | *hcor = (struct ehci_hcor *)((uint32_t) *hccr + | |
84 | HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); | |
6b92487d | 85 | |
6b92487d | 86 | /* Set to Host mode */ |
08066152 | 87 | setbits_le32(&ehci->usbmode, CM_HOST); |
6b92487d | 88 | |
08066152 VM |
89 | out_be32(&ehci->snoop1, SNOOP_SIZE_2GB); |
90 | out_be32(&ehci->snoop2, 0x80000000 | SNOOP_SIZE_2GB); | |
6b92487d MT |
91 | |
92 | /* Init phy */ | |
0ecb15c8 NB |
93 | if (hwconfig_sub(current_usb_controller, "phy_type")) |
94 | phy_type = hwconfig_subarg(current_usb_controller, | |
95 | "phy_type", &len); | |
4ef01010 | 96 | else |
1b719e66 RM |
97 | phy_type = getenv("usb_phy_type"); |
98 | ||
99 | if (!phy_type) { | |
100 | #ifdef CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY | |
101 | /* if none specified assume internal UTMI */ | |
102 | strcpy(usb_phy, "utmi"); | |
103 | phy_type = usb_phy; | |
104 | #else | |
105 | printf("WARNING: USB phy type not defined !!\n"); | |
106 | return -1; | |
107 | #endif | |
108 | } | |
109 | ||
91d7746d | 110 | if (!strncmp(phy_type, "utmi", 4)) { |
1b719e66 | 111 | #if defined(CONFIG_SYS_FSL_USB_INTERNAL_UTMI_PHY) |
15231f6d NB |
112 | clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK, |
113 | PHY_CLK_SEL_UTMI); | |
114 | clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK, | |
115 | UTMI_PHY_EN); | |
1b719e66 RM |
116 | udelay(1000); /* delay required for PHY Clk to appear */ |
117 | #endif | |
676ae068 | 118 | out_le32(&(*hcor)->or_portsc[0], PORT_PTS_UTMI); |
15231f6d NB |
119 | clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK, |
120 | USB_EN); | |
1b719e66 | 121 | } else { |
15231f6d NB |
122 | clrsetbits_be32(&ehci->control, CONTROL_REGISTER_W1C_MASK, |
123 | PHY_CLK_SEL_ULPI); | |
124 | clrsetbits_be32(&ehci->control, UTMI_PHY_EN | | |
125 | CONTROL_REGISTER_W1C_MASK, USB_EN); | |
1b719e66 | 126 | udelay(1000); /* delay required for PHY Clk to appear */ |
047cea36 SL |
127 | if (!usb_phy_clk_valid(ehci)) |
128 | return -EINVAL; | |
676ae068 | 129 | out_le32(&(*hcor)->or_portsc[0], PORT_PTS_ULPI); |
1b719e66 | 130 | } |
6b92487d | 131 | |
08066152 VM |
132 | out_be32(&ehci->prictrl, 0x0000000c); |
133 | out_be32(&ehci->age_cnt_limit, 0x00000040); | |
134 | out_be32(&ehci->sictrl, 0x00000001); | |
6b92487d | 135 | |
08066152 | 136 | in_le32(&ehci->usbmode); |
6b92487d | 137 | |
f3dff695 | 138 | if (has_erratum_a007798()) |
896720ce NB |
139 | set_txfifothresh(ehci, TXFIFOTHRESH); |
140 | ||
0dc78ff8 NB |
141 | if (has_erratum_a004477()) { |
142 | /* | |
143 | * When reset is issued while any ULPI transaction is ongoing | |
144 | * then it may result to corruption of ULPI Function Control | |
145 | * Register which eventually causes phy clock to enter low | |
146 | * power mode which stops the clock. Thus delay is required | |
147 | * before reset to let ongoing ULPI transaction complete. | |
148 | */ | |
149 | udelay(1); | |
150 | } | |
6b92487d MT |
151 | return 0; |
152 | } | |
153 | ||
154 | /* | |
155 | * Destroy the appropriate control structures corresponding | |
156 | * the the EHCI host controller. | |
157 | */ | |
676ae068 | 158 | int ehci_hcd_stop(int index) |
6b92487d MT |
159 | { |
160 | return 0; | |
161 | } | |
896720ce NB |
162 | |
163 | /* | |
164 | * Setting the value of TXFIFO_THRESH field in TXFILLTUNING register | |
165 | * to counter DDR latencies in writing data into Tx buffer. | |
166 | * This prevents Tx buffer from getting underrun | |
167 | */ | |
168 | static void set_txfifothresh(struct usb_ehci *ehci, u32 txfifo_thresh) | |
169 | { | |
170 | u32 cmd; | |
171 | cmd = ehci_readl(&ehci->txfilltuning); | |
172 | cmd &= ~TXFIFO_THRESH_MASK; | |
173 | cmd |= TXFIFO_THRESH(txfifo_thresh); | |
174 | ehci_writel(&ehci->txfilltuning, cmd); | |
175 | } | |
a1c04e27 NB |
176 | |
177 | #if defined(CONFIG_HAS_FSL_DR_USB) || defined(CONFIG_HAS_FSL_MPH_USB) | |
178 | static int fdt_fixup_usb_mode_phy_type(void *blob, const char *mode, | |
179 | const char *phy_type, int start_offset) | |
180 | { | |
181 | const char *compat_dr = "fsl-usb2-dr"; | |
182 | const char *compat_mph = "fsl-usb2-mph"; | |
183 | const char *prop_mode = "dr_mode"; | |
184 | const char *prop_type = "phy_type"; | |
185 | const char *node_type = NULL; | |
186 | int node_offset; | |
187 | int err; | |
188 | ||
189 | node_offset = fdt_node_offset_by_compatible(blob, | |
190 | start_offset, compat_mph); | |
191 | if (node_offset < 0) { | |
192 | node_offset = fdt_node_offset_by_compatible(blob, | |
193 | start_offset, | |
194 | compat_dr); | |
195 | if (node_offset < 0) { | |
196 | printf("WARNING: could not find compatible node: %s", | |
197 | fdt_strerror(node_offset)); | |
198 | return -1; | |
199 | } | |
200 | node_type = compat_dr; | |
201 | } else { | |
202 | node_type = compat_mph; | |
203 | } | |
204 | ||
205 | if (mode) { | |
206 | err = fdt_setprop(blob, node_offset, prop_mode, mode, | |
207 | strlen(mode) + 1); | |
208 | if (err < 0) | |
209 | printf("WARNING: could not set %s for %s: %s.\n", | |
210 | prop_mode, node_type, fdt_strerror(err)); | |
211 | } | |
212 | ||
213 | if (phy_type) { | |
214 | err = fdt_setprop(blob, node_offset, prop_type, phy_type, | |
215 | strlen(phy_type) + 1); | |
216 | if (err < 0) | |
217 | printf("WARNING: could not set %s for %s: %s.\n", | |
218 | prop_type, node_type, fdt_strerror(err)); | |
219 | } | |
220 | ||
221 | return node_offset; | |
222 | } | |
223 | ||
ecfc19f3 NB |
224 | static const char *fdt_usb_get_node_type(void *blob, int start_offset, |
225 | int *node_offset) | |
226 | { | |
227 | const char *compat_dr = "fsl-usb2-dr"; | |
228 | const char *compat_mph = "fsl-usb2-mph"; | |
229 | const char *node_type = NULL; | |
230 | ||
231 | *node_offset = fdt_node_offset_by_compatible(blob, start_offset, | |
232 | compat_mph); | |
233 | if (*node_offset < 0) { | |
234 | *node_offset = fdt_node_offset_by_compatible(blob, | |
235 | start_offset, | |
236 | compat_dr); | |
237 | if (*node_offset < 0) { | |
238 | printf("ERROR: could not find compatible node: %s\n", | |
239 | fdt_strerror(*node_offset)); | |
240 | } else { | |
241 | node_type = compat_dr; | |
242 | } | |
243 | } else { | |
244 | node_type = compat_mph; | |
245 | } | |
246 | ||
247 | return node_type; | |
248 | } | |
249 | ||
250 | static int fdt_fixup_usb_erratum(void *blob, const char *prop_erratum, | |
251 | int start_offset) | |
252 | { | |
253 | int node_offset, err; | |
254 | const char *node_type = NULL; | |
255 | ||
256 | node_type = fdt_usb_get_node_type(blob, start_offset, &node_offset); | |
257 | if (!node_type) | |
258 | return -1; | |
259 | ||
260 | err = fdt_setprop(blob, node_offset, prop_erratum, NULL, 0); | |
261 | if (err < 0) { | |
262 | printf("ERROR: could not set %s for %s: %s.\n", | |
263 | prop_erratum, node_type, fdt_strerror(err)); | |
264 | } | |
265 | ||
266 | return node_offset; | |
267 | } | |
268 | ||
a1c04e27 NB |
269 | void fdt_fixup_dr_usb(void *blob, bd_t *bd) |
270 | { | |
271 | static const char * const modes[] = { "host", "peripheral", "otg" }; | |
0c771060 | 272 | static const char * const phys[] = { "ulpi", "utmi", "utmi_dual" }; |
ecfc19f3 NB |
273 | int usb_erratum_a006261_off = -1; |
274 | int usb_erratum_a007075_off = -1; | |
da5ce448 | 275 | int usb_erratum_a007792_off = -1; |
b4e78faa | 276 | int usb_erratum_a005697_off = -1; |
a1c04e27 NB |
277 | int usb_mode_off = -1; |
278 | int usb_phy_off = -1; | |
279 | char str[5]; | |
280 | int i, j; | |
281 | ||
282 | for (i = 1; i <= CONFIG_USB_MAX_CONTROLLER_COUNT; i++) { | |
283 | const char *dr_mode_type = NULL; | |
284 | const char *dr_phy_type = NULL; | |
285 | int mode_idx = -1, phy_idx = -1; | |
286 | ||
287 | snprintf(str, 5, "%s%d", "usb", i); | |
288 | if (hwconfig(str)) { | |
289 | for (j = 0; j < ARRAY_SIZE(modes); j++) { | |
290 | if (hwconfig_subarg_cmp(str, "dr_mode", | |
291 | modes[j])) { | |
292 | mode_idx = j; | |
293 | break; | |
294 | } | |
295 | } | |
296 | ||
297 | for (j = 0; j < ARRAY_SIZE(phys); j++) { | |
298 | if (hwconfig_subarg_cmp(str, "phy_type", | |
299 | phys[j])) { | |
300 | phy_idx = j; | |
301 | break; | |
302 | } | |
303 | } | |
304 | ||
305 | if (mode_idx < 0 && phy_idx < 0) { | |
306 | printf("WARNING: invalid phy or mode\n"); | |
307 | return; | |
308 | } | |
309 | ||
310 | if (mode_idx > -1) | |
311 | dr_mode_type = modes[mode_idx]; | |
312 | ||
313 | if (phy_idx > -1) | |
314 | dr_phy_type = phys[phy_idx]; | |
315 | } | |
316 | ||
0c771060 NB |
317 | if (has_dual_phy()) |
318 | dr_phy_type = phys[2]; | |
319 | ||
a1c04e27 NB |
320 | usb_mode_off = fdt_fixup_usb_mode_phy_type(blob, |
321 | dr_mode_type, NULL, | |
322 | usb_mode_off); | |
323 | ||
324 | if (usb_mode_off < 0) | |
325 | return; | |
326 | ||
327 | usb_phy_off = fdt_fixup_usb_mode_phy_type(blob, | |
328 | NULL, dr_phy_type, | |
329 | usb_phy_off); | |
330 | ||
331 | if (usb_phy_off < 0) | |
332 | return; | |
ecfc19f3 NB |
333 | |
334 | if (has_erratum_a006261()) { | |
335 | usb_erratum_a006261_off = fdt_fixup_usb_erratum | |
336 | (blob, | |
337 | "fsl,usb-erratum-a006261", | |
338 | usb_erratum_a006261_off); | |
339 | if (usb_erratum_a006261_off < 0) | |
340 | return; | |
341 | } | |
0c771060 | 342 | |
ecfc19f3 NB |
343 | if (has_erratum_a007075()) { |
344 | usb_erratum_a007075_off = fdt_fixup_usb_erratum | |
345 | (blob, | |
346 | "fsl,usb-erratum-a007075", | |
347 | usb_erratum_a007075_off); | |
348 | if (usb_erratum_a007075_off < 0) | |
349 | return; | |
350 | } | |
0c771060 | 351 | |
da5ce448 NB |
352 | if (has_erratum_a007792()) { |
353 | usb_erratum_a007792_off = fdt_fixup_usb_erratum | |
354 | (blob, | |
355 | "fsl,usb-erratum-a007792", | |
356 | usb_erratum_a007792_off); | |
357 | if (usb_erratum_a007792_off < 0) | |
358 | return; | |
359 | } | |
b4e78faa NB |
360 | if (has_erratum_a005697()) { |
361 | usb_erratum_a005697_off = fdt_fixup_usb_erratum | |
362 | (blob, | |
363 | "fsl,usb-erratum-a005697", | |
364 | usb_erratum_a005697_off); | |
365 | if (usb_erratum_a005697_off < 0) | |
366 | return; | |
367 | } | |
a1c04e27 NB |
368 | } |
369 | } | |
370 | #endif |