dwc3-generic: Don't fail probe if clk/reset entries are absent
[oweals/u-boot.git] / drivers / usb / dwc3 / dwc3-generic.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Generic DWC3 Glue layer
4  *
5  * Copyright (C) 2016 - 2018 Xilinx, Inc.
6  *
7  * Based on dwc3-omap.c.
8  */
9
10 #include <common.h>
11 #include <asm-generic/io.h>
12 #include <dm.h>
13 #include <dm/device-internal.h>
14 #include <dm/lists.h>
15 #include <dwc3-uboot.h>
16 #include <linux/usb/ch9.h>
17 #include <linux/usb/gadget.h>
18 #include <malloc.h>
19 #include <usb.h>
20 #include "core.h"
21 #include "gadget.h"
22 #include <reset.h>
23 #include <clk.h>
24 #include <usb/xhci.h>
25
26 struct dwc3_generic_plat {
27         fdt_addr_t base;
28         u32 maximum_speed;
29         enum usb_dr_mode dr_mode;
30 };
31
32 struct dwc3_generic_priv {
33         void *base;
34         struct dwc3 dwc3;
35         struct phy *phys;
36         int num_phys;
37 };
38
39 struct dwc3_generic_host_priv {
40         struct xhci_ctrl xhci_ctrl;
41         struct dwc3_generic_priv gen_priv;
42 };
43
44 static int dwc3_generic_probe(struct udevice *dev,
45                               struct dwc3_generic_priv *priv)
46 {
47         int rc;
48         struct dwc3_generic_plat *plat = dev_get_platdata(dev);
49         struct dwc3 *dwc3 = &priv->dwc3;
50
51         dwc3->dev = dev;
52         dwc3->maximum_speed = plat->maximum_speed;
53         dwc3->dr_mode = plat->dr_mode;
54 #if CONFIG_IS_ENABLED(OF_CONTROL)
55         dwc3_of_parse(dwc3);
56 #endif
57
58         rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys);
59         if (rc)
60                 return rc;
61
62         priv->base = map_physmem(plat->base, DWC3_OTG_REGS_END, MAP_NOCACHE);
63         dwc3->regs = priv->base + DWC3_GLOBALS_REGS_START;
64
65
66         rc =  dwc3_init(dwc3);
67         if (rc) {
68                 unmap_physmem(priv->base, MAP_NOCACHE);
69                 return rc;
70         }
71
72         return 0;
73 }
74
75 static int dwc3_generic_remove(struct udevice *dev,
76                                struct dwc3_generic_priv *priv)
77 {
78         struct dwc3 *dwc3 = &priv->dwc3;
79
80         dwc3_remove(dwc3);
81         dwc3_shutdown_phy(dev, priv->phys, priv->num_phys);
82         unmap_physmem(dwc3->regs, MAP_NOCACHE);
83
84         return 0;
85 }
86
87 static int dwc3_generic_ofdata_to_platdata(struct udevice *dev)
88 {
89         struct dwc3_generic_plat *plat = dev_get_platdata(dev);
90         int node = dev_of_offset(dev);
91
92         plat->base = devfdt_get_addr(dev);
93
94         plat->maximum_speed = usb_get_maximum_speed(node);
95         if (plat->maximum_speed == USB_SPEED_UNKNOWN) {
96                 pr_info("No USB maximum speed specified. Using super speed\n");
97                 plat->maximum_speed = USB_SPEED_SUPER;
98         }
99
100         plat->dr_mode = usb_get_dr_mode(node);
101         if (plat->dr_mode == USB_DR_MODE_UNKNOWN) {
102                 pr_err("Invalid usb mode setup\n");
103                 return -ENODEV;
104         }
105
106         return 0;
107 }
108
109 #if CONFIG_IS_ENABLED(DM_USB_GADGET)
110 int dm_usb_gadget_handle_interrupts(struct udevice *dev)
111 {
112         struct dwc3_generic_priv *priv = dev_get_priv(dev);
113         struct dwc3 *dwc3 = &priv->dwc3;
114
115         dwc3_gadget_uboot_handle_interrupt(dwc3);
116
117         return 0;
118 }
119
120 static int dwc3_generic_peripheral_probe(struct udevice *dev)
121 {
122         struct dwc3_generic_priv *priv = dev_get_priv(dev);
123
124         return dwc3_generic_probe(dev, priv);
125 }
126
127 static int dwc3_generic_peripheral_remove(struct udevice *dev)
128 {
129         struct dwc3_generic_priv *priv = dev_get_priv(dev);
130
131         return dwc3_generic_remove(dev, priv);
132 }
133
134 U_BOOT_DRIVER(dwc3_generic_peripheral) = {
135         .name   = "dwc3-generic-peripheral",
136         .id     = UCLASS_USB_GADGET_GENERIC,
137         .ofdata_to_platdata = dwc3_generic_ofdata_to_platdata,
138         .probe = dwc3_generic_peripheral_probe,
139         .remove = dwc3_generic_peripheral_remove,
140         .priv_auto_alloc_size = sizeof(struct dwc3_generic_priv),
141         .platdata_auto_alloc_size = sizeof(struct dwc3_generic_plat),
142 };
143 #endif
144
145 #if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD)
146 static int dwc3_generic_host_probe(struct udevice *dev)
147 {
148         struct xhci_hcor *hcor;
149         struct xhci_hccr *hccr;
150         struct dwc3_generic_host_priv *priv = dev_get_priv(dev);
151         int rc;
152
153         rc = dwc3_generic_probe(dev, &priv->gen_priv);
154         if (rc)
155                 return rc;
156
157         hccr = (struct xhci_hccr *)priv->gen_priv.base;
158         hcor = (struct xhci_hcor *)(priv->gen_priv.base +
159                         HC_LENGTH(xhci_readl(&(hccr)->cr_capbase)));
160
161         return xhci_register(dev, hccr, hcor);
162 }
163
164 static int dwc3_generic_host_remove(struct udevice *dev)
165 {
166         struct dwc3_generic_host_priv *priv = dev_get_priv(dev);
167         int rc;
168
169         rc = xhci_deregister(dev);
170         if (rc)
171                 return rc;
172
173         return dwc3_generic_remove(dev, &priv->gen_priv);
174 }
175
176 U_BOOT_DRIVER(dwc3_generic_host) = {
177         .name   = "dwc3-generic-host",
178         .id     = UCLASS_USB,
179         .ofdata_to_platdata = dwc3_generic_ofdata_to_platdata,
180         .probe = dwc3_generic_host_probe,
181         .remove = dwc3_generic_host_remove,
182         .priv_auto_alloc_size = sizeof(struct dwc3_generic_host_priv),
183         .platdata_auto_alloc_size = sizeof(struct dwc3_generic_plat),
184         .ops = &xhci_usb_ops,
185         .flags = DM_FLAG_ALLOC_PRIV_DMA,
186 };
187 #endif
188
189 struct dwc3_glue_data {
190         struct clk_bulk         clks;
191         struct reset_ctl_bulk   resets;
192         fdt_addr_t regs;
193 };
194
195 struct dwc3_glue_ops {
196         void (*select_dr_mode)(struct udevice *dev, int index,
197                                enum usb_dr_mode mode);
198 };
199
200 void dwc3_ti_select_dr_mode(struct udevice *dev, int index,
201                             enum usb_dr_mode mode)
202 {
203 #define USBOTGSS_UTMI_OTG_STATUS                0x0084
204 #define USBOTGSS_UTMI_OTG_OFFSET                0x0480
205
206 /* UTMI_OTG_STATUS REGISTER */
207 #define USBOTGSS_UTMI_OTG_STATUS_SW_MODE        BIT(31)
208 #define USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT   BIT(9)
209 #define USBOTGSS_UTMI_OTG_STATUS_TXBITSTUFFENABLE BIT(8)
210 #define USBOTGSS_UTMI_OTG_STATUS_IDDIG          BIT(4)
211 #define USBOTGSS_UTMI_OTG_STATUS_SESSEND        BIT(3)
212 #define USBOTGSS_UTMI_OTG_STATUS_SESSVALID      BIT(2)
213 #define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID      BIT(1)
214 enum dwc3_omap_utmi_mode {
215         DWC3_OMAP_UTMI_MODE_UNKNOWN = 0,
216         DWC3_OMAP_UTMI_MODE_HW,
217         DWC3_OMAP_UTMI_MODE_SW,
218 };
219
220         u32 use_id_pin;
221         u32 host_mode;
222         u32 reg;
223         u32 utmi_mode;
224         u32 utmi_status_offset = USBOTGSS_UTMI_OTG_STATUS;
225
226         struct dwc3_glue_data *glue = dev_get_platdata(dev);
227         void *base = map_physmem(glue->regs, 0x10000, MAP_NOCACHE);
228
229         if (device_is_compatible(dev, "ti,am437x-dwc3"))
230                 utmi_status_offset += USBOTGSS_UTMI_OTG_OFFSET;
231
232         utmi_mode = dev_read_u32_default(dev, "utmi-mode",
233                                          DWC3_OMAP_UTMI_MODE_UNKNOWN);
234         if (utmi_mode != DWC3_OMAP_UTMI_MODE_HW) {
235                 debug("%s: OTG is not supported. defaulting to PERIPHERAL\n",
236                       dev->name);
237                 mode = USB_DR_MODE_PERIPHERAL;
238         }
239
240         switch (mode)  {
241         case USB_DR_MODE_PERIPHERAL:
242                 use_id_pin = 0;
243                 host_mode = 0;
244                 break;
245         case USB_DR_MODE_HOST:
246                 use_id_pin = 0;
247                 host_mode = 1;
248                 break;
249         case USB_DR_MODE_OTG:
250         default:
251                 use_id_pin = 1;
252                 host_mode = 0;
253                 break;
254         }
255
256         reg = readl(base + utmi_status_offset);
257
258         reg &= ~(USBOTGSS_UTMI_OTG_STATUS_SW_MODE);
259         if (!use_id_pin)
260                 reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
261
262         writel(reg, base + utmi_status_offset);
263
264         reg &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSEND |
265                 USBOTGSS_UTMI_OTG_STATUS_VBUSVALID |
266                 USBOTGSS_UTMI_OTG_STATUS_IDDIG);
267
268         reg |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID |
269                 USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
270
271         if (!host_mode)
272                 reg |= USBOTGSS_UTMI_OTG_STATUS_IDDIG |
273                         USBOTGSS_UTMI_OTG_STATUS_VBUSVALID;
274
275         writel(reg, base + utmi_status_offset);
276
277         unmap_physmem(base, MAP_NOCACHE);
278 }
279
280 struct dwc3_glue_ops ti_ops = {
281         .select_dr_mode = dwc3_ti_select_dr_mode,
282 };
283
284 static int dwc3_glue_bind(struct udevice *parent)
285 {
286         const void *fdt = gd->fdt_blob;
287         int node;
288         int ret;
289
290         for (node = fdt_first_subnode(fdt, dev_of_offset(parent)); node > 0;
291              node = fdt_next_subnode(fdt, node)) {
292                 const char *name = fdt_get_name(fdt, node, NULL);
293                 enum usb_dr_mode dr_mode;
294                 struct udevice *dev;
295                 const char *driver = NULL;
296
297                 debug("%s: subnode name: %s\n", __func__, name);
298
299                 dr_mode = usb_get_dr_mode(node);
300
301                 switch (dr_mode) {
302                 case USB_DR_MODE_PERIPHERAL:
303                 case USB_DR_MODE_OTG:
304 #if CONFIG_IS_ENABLED(DM_USB_GADGET)
305                         debug("%s: dr_mode: OTG or Peripheral\n", __func__);
306                         driver = "dwc3-generic-peripheral";
307 #endif
308                         break;
309 #if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD)
310                 case USB_DR_MODE_HOST:
311                         debug("%s: dr_mode: HOST\n", __func__);
312                         driver = "dwc3-generic-host";
313                         break;
314 #endif
315                 default:
316                         debug("%s: unsupported dr_mode\n", __func__);
317                         return -ENODEV;
318                 };
319
320                 if (!driver)
321                         continue;
322
323                 ret = device_bind_driver_to_node(parent, driver, name,
324                                                  offset_to_ofnode(node), &dev);
325                 if (ret) {
326                         debug("%s: not able to bind usb device mode\n",
327                               __func__);
328                         return ret;
329                 }
330         }
331
332         return 0;
333 }
334
335 static int dwc3_glue_reset_init(struct udevice *dev,
336                                 struct dwc3_glue_data *glue)
337 {
338         int ret;
339
340         ret = reset_get_bulk(dev, &glue->resets);
341         if (ret == -ENOTSUPP || ret == -ENOENT)
342                 return 0;
343         else if (ret)
344                 return ret;
345
346         ret = reset_deassert_bulk(&glue->resets);
347         if (ret) {
348                 reset_release_bulk(&glue->resets);
349                 return ret;
350         }
351
352         return 0;
353 }
354
355 static int dwc3_glue_clk_init(struct udevice *dev,
356                               struct dwc3_glue_data *glue)
357 {
358         int ret;
359
360         ret = clk_get_bulk(dev, &glue->clks);
361         if (ret == -ENOSYS || ret == -ENOENT)
362                 return 0;
363         if (ret)
364                 return ret;
365
366 #if CONFIG_IS_ENABLED(CLK)
367         ret = clk_enable_bulk(&glue->clks);
368         if (ret) {
369                 clk_release_bulk(&glue->clks);
370                 return ret;
371         }
372 #endif
373
374         return 0;
375 }
376
377 static int dwc3_glue_probe(struct udevice *dev)
378 {
379         struct dwc3_glue_ops *ops = (struct dwc3_glue_ops *)dev_get_driver_data(dev);
380         struct dwc3_glue_data *glue = dev_get_platdata(dev);
381         struct udevice *child = NULL;
382         int index = 0;
383         int ret;
384
385         glue->regs = dev_read_addr(dev);
386
387         ret = dwc3_glue_clk_init(dev, glue);
388         if (ret)
389                 return ret;
390
391         ret = dwc3_glue_reset_init(dev, glue);
392         if (ret)
393                 return ret;
394
395         ret = device_find_first_child(dev, &child);
396         if (ret)
397                 return ret;
398
399         while (child) {
400                 enum usb_dr_mode dr_mode;
401
402                 dr_mode = usb_get_dr_mode(dev_of_offset(child));
403                 device_find_next_child(&child);
404                 if (ops && ops->select_dr_mode)
405                         ops->select_dr_mode(dev, index, dr_mode);
406                 index++;
407         }
408
409         return 0;
410 }
411
412 static int dwc3_glue_remove(struct udevice *dev)
413 {
414         struct dwc3_glue_data *glue = dev_get_platdata(dev);
415
416         reset_release_bulk(&glue->resets);
417
418         clk_release_bulk(&glue->clks);
419
420         return 0;
421 }
422
423 static const struct udevice_id dwc3_glue_ids[] = {
424         { .compatible = "xlnx,zynqmp-dwc3" },
425         { .compatible = "ti,keystone-dwc3"},
426         { .compatible = "ti,dwc3", .data = (ulong)&ti_ops },
427         { .compatible = "ti,am437x-dwc3", .data = (ulong)&ti_ops },
428         { }
429 };
430
431 U_BOOT_DRIVER(dwc3_generic_wrapper) = {
432         .name   = "dwc3-generic-wrapper",
433         .id     = UCLASS_NOP,
434         .of_match = dwc3_glue_ids,
435         .bind = dwc3_glue_bind,
436         .probe = dwc3_glue_probe,
437         .remove = dwc3_glue_remove,
438         .platdata_auto_alloc_size = sizeof(struct dwc3_glue_data),
439
440 };