]>
Commit | Line | Data |
---|---|---|
a94bb7a4 SM |
1 | /* |
2 | * Copyright (c) 2015 Sanchayan Maity <sanchayan.maity@toradex.com> | |
3 | * Copyright (C) 2015 Toradex AG | |
4 | * | |
5 | * Based on ehci-mx6 driver | |
6 | * | |
7 | * SPDX-License-Identifier: GPL-2.0+ | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
0885cdb9 | 11 | #include <dm.h> |
a94bb7a4 SM |
12 | #include <usb.h> |
13 | #include <errno.h> | |
14 | #include <linux/compiler.h> | |
15 | #include <asm/io.h> | |
0885cdb9 | 16 | #include <asm-generic/gpio.h> |
a94bb7a4 SM |
17 | #include <asm/arch/clock.h> |
18 | #include <asm/arch/imx-regs.h> | |
19 | #include <asm/arch/crm_regs.h> | |
552a848e SB |
20 | #include <asm/mach-imx/iomux-v3.h> |
21 | #include <asm/mach-imx/regs-usbphy.h> | |
e162c6b1 | 22 | #include <usb/ehci-ci.h> |
0885cdb9 SM |
23 | #include <libfdt.h> |
24 | #include <fdtdec.h> | |
a94bb7a4 SM |
25 | |
26 | #include "ehci.h" | |
27 | ||
28 | #define USB_NC_REG_OFFSET 0x00000800 | |
29 | ||
30 | #define ANADIG_PLL_CTRL_EN_USB_CLKS (1 << 6) | |
31 | ||
32 | #define UCTRL_OVER_CUR_POL (1 << 8) /* OTG Polarity of Overcurrent */ | |
33 | #define UCTRL_OVER_CUR_DIS (1 << 7) /* Disable OTG Overcurrent Detection */ | |
34 | ||
35 | /* USBCMD */ | |
36 | #define UCMD_RUN_STOP (1 << 0) /* controller run/stop */ | |
37 | #define UCMD_RESET (1 << 1) /* controller reset */ | |
38 | ||
0885cdb9 SM |
39 | DECLARE_GLOBAL_DATA_PTR; |
40 | ||
a94bb7a4 SM |
41 | static const unsigned phy_bases[] = { |
42 | USB_PHY0_BASE_ADDR, | |
43 | USB_PHY1_BASE_ADDR, | |
44 | }; | |
45 | ||
46 | static const unsigned nc_reg_bases[] = { | |
47 | USBC0_BASE_ADDR, | |
48 | USBC1_BASE_ADDR, | |
49 | }; | |
50 | ||
51 | static void usb_internal_phy_clock_gate(int index) | |
52 | { | |
53 | void __iomem *phy_reg; | |
54 | ||
55 | phy_reg = (void __iomem *)phy_bases[index]; | |
56 | clrbits_le32(phy_reg + USBPHY_CTRL, USBPHY_CTRL_CLKGATE); | |
57 | } | |
58 | ||
59 | static void usb_power_config(int index) | |
60 | { | |
61 | struct anadig_reg __iomem *anadig = | |
62 | (struct anadig_reg __iomem *)ANADIG_BASE_ADDR; | |
63 | void __iomem *pll_ctrl; | |
64 | ||
65 | switch (index) { | |
66 | case 0: | |
67 | pll_ctrl = &anadig->pll3_ctrl; | |
68 | clrbits_le32(pll_ctrl, ANADIG_PLL3_CTRL_BYPASS); | |
69 | setbits_le32(pll_ctrl, ANADIG_PLL3_CTRL_ENABLE | |
70 | | ANADIG_PLL3_CTRL_POWERDOWN | |
71 | | ANADIG_PLL_CTRL_EN_USB_CLKS); | |
72 | break; | |
73 | case 1: | |
74 | pll_ctrl = &anadig->pll7_ctrl; | |
75 | clrbits_le32(pll_ctrl, ANADIG_PLL7_CTRL_BYPASS); | |
76 | setbits_le32(pll_ctrl, ANADIG_PLL7_CTRL_ENABLE | |
77 | | ANADIG_PLL7_CTRL_POWERDOWN | |
78 | | ANADIG_PLL_CTRL_EN_USB_CLKS); | |
79 | break; | |
80 | default: | |
81 | return; | |
82 | } | |
83 | } | |
84 | ||
85 | static void usb_phy_enable(int index, struct usb_ehci *ehci) | |
86 | { | |
87 | void __iomem *phy_reg; | |
88 | void __iomem *phy_ctrl; | |
89 | void __iomem *usb_cmd; | |
90 | ||
91 | phy_reg = (void __iomem *)phy_bases[index]; | |
92 | phy_ctrl = (void __iomem *)(phy_reg + USBPHY_CTRL); | |
93 | usb_cmd = (void __iomem *)&ehci->usbcmd; | |
94 | ||
95 | /* Stop then Reset */ | |
96 | clrbits_le32(usb_cmd, UCMD_RUN_STOP); | |
97 | while (readl(usb_cmd) & UCMD_RUN_STOP) | |
98 | ; | |
99 | ||
100 | setbits_le32(usb_cmd, UCMD_RESET); | |
101 | while (readl(usb_cmd) & UCMD_RESET) | |
102 | ; | |
103 | ||
104 | /* Reset USBPHY module */ | |
105 | setbits_le32(phy_ctrl, USBPHY_CTRL_SFTRST); | |
106 | udelay(10); | |
107 | ||
108 | /* Remove CLKGATE and SFTRST */ | |
109 | clrbits_le32(phy_ctrl, USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST); | |
110 | udelay(10); | |
111 | ||
112 | /* Power up the PHY */ | |
113 | writel(0, phy_reg + USBPHY_PWD); | |
114 | ||
115 | /* Enable FS/LS device */ | |
116 | setbits_le32(phy_ctrl, USBPHY_CTRL_ENUTMILEVEL2 | | |
117 | USBPHY_CTRL_ENUTMILEVEL3); | |
118 | } | |
119 | ||
120 | static void usb_oc_config(int index) | |
121 | { | |
122 | void __iomem *ctrl; | |
123 | ||
124 | ctrl = (void __iomem *)(nc_reg_bases[index] + USB_NC_REG_OFFSET); | |
125 | ||
126 | setbits_le32(ctrl, UCTRL_OVER_CUR_POL); | |
127 | setbits_le32(ctrl, UCTRL_OVER_CUR_DIS); | |
128 | } | |
129 | ||
08c11cb5 SM |
130 | int __weak board_usb_phy_mode(int port) |
131 | { | |
132 | return 0; | |
133 | } | |
134 | ||
60ed2864 SM |
135 | int __weak board_ehci_hcd_init(int port) |
136 | { | |
137 | return 0; | |
138 | } | |
139 | ||
0885cdb9 SM |
140 | int ehci_vf_common_init(struct usb_ehci *ehci, int index) |
141 | { | |
142 | int ret; | |
143 | ||
144 | /* Do board specific initialisation */ | |
145 | ret = board_ehci_hcd_init(index); | |
146 | if (ret) | |
147 | return ret; | |
148 | ||
149 | usb_power_config(index); | |
150 | usb_oc_config(index); | |
151 | usb_internal_phy_clock_gate(index); | |
152 | usb_phy_enable(index, ehci); | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | #ifndef CONFIG_DM_USB | |
a94bb7a4 SM |
158 | int ehci_hcd_init(int index, enum usb_init_type init, |
159 | struct ehci_hccr **hccr, struct ehci_hcor **hcor) | |
160 | { | |
161 | struct usb_ehci *ehci; | |
08c11cb5 | 162 | enum usb_init_type type; |
0885cdb9 | 163 | int ret; |
a94bb7a4 SM |
164 | |
165 | if (index >= ARRAY_SIZE(nc_reg_bases)) | |
166 | return -EINVAL; | |
167 | ||
a94bb7a4 SM |
168 | ehci = (struct usb_ehci *)nc_reg_bases[index]; |
169 | ||
0885cdb9 SM |
170 | ret = ehci_vf_common_init(index); |
171 | if (ret) | |
172 | return ret; | |
a94bb7a4 SM |
173 | |
174 | *hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); | |
175 | *hcor = (struct ehci_hcor *)((uint32_t)*hccr + | |
176 | HC_LENGTH(ehci_readl(&(*hccr)->cr_capbase))); | |
177 | ||
08c11cb5 SM |
178 | type = board_usb_phy_mode(index); |
179 | if (type != init) | |
180 | return -ENODEV; | |
181 | ||
a94bb7a4 SM |
182 | if (init == USB_INIT_DEVICE) { |
183 | setbits_le32(&ehci->usbmode, CM_DEVICE); | |
184 | writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc); | |
185 | setbits_le32(&ehci->portsc, USB_EN); | |
186 | } else if (init == USB_INIT_HOST) { | |
187 | setbits_le32(&ehci->usbmode, CM_HOST); | |
188 | writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc); | |
189 | setbits_le32(&ehci->portsc, USB_EN); | |
190 | } | |
191 | ||
192 | return 0; | |
193 | } | |
194 | ||
195 | int ehci_hcd_stop(int index) | |
196 | { | |
197 | return 0; | |
198 | } | |
0885cdb9 SM |
199 | #else |
200 | /* Possible port types (dual role mode) */ | |
201 | enum dr_mode { | |
202 | DR_MODE_NONE = 0, | |
203 | DR_MODE_HOST, /* supports host operation */ | |
204 | DR_MODE_DEVICE, /* supports device operation */ | |
205 | DR_MODE_OTG, /* supports both */ | |
206 | }; | |
207 | ||
208 | struct ehci_vf_priv_data { | |
209 | struct ehci_ctrl ctrl; | |
210 | struct usb_ehci *ehci; | |
211 | struct gpio_desc cdet_gpio; | |
212 | enum usb_init_type init_type; | |
213 | enum dr_mode dr_mode; | |
214 | u32 portnr; | |
215 | }; | |
216 | ||
217 | static int vf_usb_ofdata_to_platdata(struct udevice *dev) | |
218 | { | |
219 | struct ehci_vf_priv_data *priv = dev_get_priv(dev); | |
220 | const void *dt_blob = gd->fdt_blob; | |
e160f7d4 | 221 | int node = dev_of_offset(dev); |
0885cdb9 SM |
222 | const char *mode; |
223 | ||
224 | priv->portnr = dev->seq; | |
225 | ||
a821c4af | 226 | priv->ehci = (struct usb_ehci *)devfdt_get_addr(dev); |
0885cdb9 SM |
227 | mode = fdt_getprop(dt_blob, node, "dr_mode", NULL); |
228 | if (mode) { | |
229 | if (0 == strcmp(mode, "host")) { | |
230 | priv->dr_mode = DR_MODE_HOST; | |
231 | priv->init_type = USB_INIT_HOST; | |
232 | } else if (0 == strcmp(mode, "peripheral")) { | |
233 | priv->dr_mode = DR_MODE_DEVICE; | |
234 | priv->init_type = USB_INIT_DEVICE; | |
235 | } else if (0 == strcmp(mode, "otg")) { | |
236 | priv->dr_mode = DR_MODE_OTG; | |
237 | /* | |
238 | * We set init_type to device by default when OTG | |
239 | * mode is requested. If a valid gpio is provided | |
240 | * we will switch the init_type based on the state | |
241 | * of the gpio pin. | |
242 | */ | |
243 | priv->init_type = USB_INIT_DEVICE; | |
244 | } else { | |
245 | debug("%s: Cannot decode dr_mode '%s'\n", | |
246 | __func__, mode); | |
247 | return -EINVAL; | |
248 | } | |
249 | } else { | |
250 | priv->dr_mode = DR_MODE_HOST; | |
251 | priv->init_type = USB_INIT_HOST; | |
252 | } | |
253 | ||
254 | if (priv->dr_mode == DR_MODE_OTG) { | |
150c5afe SG |
255 | gpio_request_by_name_nodev(offset_to_ofnode(node), |
256 | "fsl,cdet-gpio", 0, &priv->cdet_gpio, | |
257 | GPIOD_IS_IN); | |
0885cdb9 SM |
258 | if (dm_gpio_is_valid(&priv->cdet_gpio)) { |
259 | if (dm_gpio_get_value(&priv->cdet_gpio)) | |
260 | priv->init_type = USB_INIT_DEVICE; | |
261 | else | |
262 | priv->init_type = USB_INIT_HOST; | |
263 | } | |
264 | } | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
269 | static int vf_init_after_reset(struct ehci_ctrl *dev) | |
270 | { | |
271 | struct ehci_vf_priv_data *priv = dev->priv; | |
272 | enum usb_init_type type = priv->init_type; | |
273 | struct usb_ehci *ehci = priv->ehci; | |
274 | int ret; | |
275 | ||
276 | ret = ehci_vf_common_init(priv->ehci, priv->portnr); | |
277 | if (ret) | |
278 | return ret; | |
279 | ||
280 | if (type == USB_INIT_DEVICE) | |
281 | return 0; | |
282 | ||
283 | setbits_le32(&ehci->usbmode, CM_HOST); | |
284 | writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc); | |
285 | setbits_le32(&ehci->portsc, USB_EN); | |
286 | ||
287 | mdelay(10); | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | static const struct ehci_ops vf_ehci_ops = { | |
293 | .init_after_reset = vf_init_after_reset | |
294 | }; | |
295 | ||
296 | static int vf_usb_bind(struct udevice *dev) | |
297 | { | |
298 | static int num_controllers; | |
299 | ||
300 | /* | |
301 | * Without this hack, if we return ENODEV for USB Controller 0, on | |
302 | * probe for the next controller, USB Controller 1 will be given a | |
303 | * sequence number of 0. This conflicts with our requirement of | |
304 | * sequence numbers while initialising the peripherals. | |
305 | */ | |
306 | dev->req_seq = num_controllers; | |
307 | num_controllers++; | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | static int ehci_usb_probe(struct udevice *dev) | |
313 | { | |
314 | struct usb_platdata *plat = dev_get_platdata(dev); | |
315 | struct ehci_vf_priv_data *priv = dev_get_priv(dev); | |
316 | struct usb_ehci *ehci = priv->ehci; | |
317 | struct ehci_hccr *hccr; | |
318 | struct ehci_hcor *hcor; | |
319 | int ret; | |
320 | ||
321 | ret = ehci_vf_common_init(ehci, priv->portnr); | |
322 | if (ret) | |
323 | return ret; | |
324 | ||
325 | if (priv->init_type != plat->init_type) | |
326 | return -ENODEV; | |
327 | ||
328 | if (priv->init_type == USB_INIT_HOST) { | |
329 | setbits_le32(&ehci->usbmode, CM_HOST); | |
330 | writel((PORT_PTS_UTMI | PORT_PTS_PTW), &ehci->portsc); | |
331 | setbits_le32(&ehci->portsc, USB_EN); | |
332 | } | |
333 | ||
334 | mdelay(10); | |
335 | ||
336 | hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); | |
337 | hcor = (struct ehci_hcor *)((uint32_t)hccr + | |
338 | HC_LENGTH(ehci_readl(&hccr->cr_capbase))); | |
339 | ||
340 | return ehci_register(dev, hccr, hcor, &vf_ehci_ops, 0, priv->init_type); | |
341 | } | |
342 | ||
0885cdb9 SM |
343 | static const struct udevice_id vf_usb_ids[] = { |
344 | { .compatible = "fsl,vf610-usb" }, | |
345 | { } | |
346 | }; | |
347 | ||
348 | U_BOOT_DRIVER(usb_ehci) = { | |
349 | .name = "ehci_vf", | |
350 | .id = UCLASS_USB, | |
351 | .of_match = vf_usb_ids, | |
352 | .bind = vf_usb_bind, | |
353 | .probe = ehci_usb_probe, | |
99e2df47 | 354 | .remove = ehci_deregister, |
0885cdb9 SM |
355 | .ops = &ehci_usb_ops, |
356 | .ofdata_to_platdata = vf_usb_ofdata_to_platdata, | |
357 | .platdata_auto_alloc_size = sizeof(struct usb_platdata), | |
358 | .priv_auto_alloc_size = sizeof(struct ehci_vf_priv_data), | |
359 | .flags = DM_FLAG_ALLOC_PRIV_DMA, | |
360 | }; | |
361 | #endif |