dwc3-generic: Handle the PHYs, the clocks and the reset lines
[project/bcm63xx/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 <dm.h>
12 #include <dm/device-internal.h>
13 #include <dm/lists.h>
14 #include <dwc3-uboot.h>
15 #include <linux/usb/ch9.h>
16 #include <linux/usb/gadget.h>
17 #include <malloc.h>
18 #include <usb.h>
19 #include "core.h"
20 #include "gadget.h"
21 #include <reset.h>
22 #include <clk.h>
23
24 #if CONFIG_IS_ENABLED(DM_USB_GADGET)
25 struct dwc3_generic_peripheral {
26 struct dwc3 dwc3;
27 struct phy *phys;
28 int num_phys;
29 fdt_addr_t base;
30 };
31
32 int dm_usb_gadget_handle_interrupts(struct udevice *dev)
33 {
34 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
35 struct dwc3 *dwc3 = &priv->dwc3;
36
37 dwc3_gadget_uboot_handle_interrupt(dwc3);
38
39 return 0;
40 }
41
42 static int dwc3_generic_peripheral_probe(struct udevice *dev)
43 {
44 int rc;
45 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
46 struct dwc3 *dwc3 = &priv->dwc3;
47
48 rc = dwc3_setup_phy(dev, &priv->phys, &priv->num_phys);
49 if (rc)
50 return rc;
51
52 dwc3->regs = map_physmem(priv->base, DWC3_OTG_REGS_END, MAP_NOCACHE);
53 dwc3->regs += DWC3_GLOBALS_REGS_START;
54 dwc3->dev = dev;
55
56 rc = dwc3_init(dwc3);
57 if (rc) {
58 unmap_physmem(dwc3->regs, MAP_NOCACHE);
59 return rc;
60 }
61
62 return 0;
63 }
64
65 static int dwc3_generic_peripheral_remove(struct udevice *dev)
66 {
67 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
68 struct dwc3 *dwc3 = &priv->dwc3;
69
70 dwc3_remove(dwc3);
71 dwc3_shutdown_phy(dev, priv->phys, priv->num_phys);
72 unmap_physmem(dwc3->regs, MAP_NOCACHE);
73
74 return 0;
75 }
76
77 static int dwc3_generic_peripheral_ofdata_to_platdata(struct udevice *dev)
78 {
79 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
80 struct dwc3 *dwc3 = &priv->dwc3;
81 int node = dev_of_offset(dev);
82
83 priv->base = devfdt_get_addr(dev);
84
85 dwc3->maximum_speed = usb_get_maximum_speed(node);
86 if (dwc3->maximum_speed == USB_SPEED_UNKNOWN) {
87 pr_err("Invalid usb maximum speed\n");
88 return -ENODEV;
89 }
90
91 dwc3->dr_mode = usb_get_dr_mode(node);
92 if (dwc3->dr_mode == USB_DR_MODE_UNKNOWN) {
93 pr_err("Invalid usb mode setup\n");
94 return -ENODEV;
95 }
96
97 return 0;
98 }
99
100 U_BOOT_DRIVER(dwc3_generic_peripheral) = {
101 .name = "dwc3-generic-peripheral",
102 .id = UCLASS_USB_GADGET_GENERIC,
103 .ofdata_to_platdata = dwc3_generic_peripheral_ofdata_to_platdata,
104 .probe = dwc3_generic_peripheral_probe,
105 .remove = dwc3_generic_peripheral_remove,
106 .priv_auto_alloc_size = sizeof(struct dwc3_generic_peripheral),
107 };
108 #endif
109
110 struct dwc3_glue_data {
111 struct clk_bulk clks;
112 struct reset_ctl_bulk resets;
113 };
114
115 static int dwc3_glue_bind(struct udevice *parent)
116 {
117 const void *fdt = gd->fdt_blob;
118 int node;
119 int ret;
120
121 for (node = fdt_first_subnode(fdt, dev_of_offset(parent)); node > 0;
122 node = fdt_next_subnode(fdt, node)) {
123 const char *name = fdt_get_name(fdt, node, NULL);
124 enum usb_dr_mode dr_mode;
125 struct udevice *dev;
126 const char *driver = NULL;
127
128 debug("%s: subnode name: %s\n", __func__, name);
129
130 dr_mode = usb_get_dr_mode(node);
131
132 switch (dr_mode) {
133 case USB_DR_MODE_PERIPHERAL:
134 case USB_DR_MODE_OTG:
135 #if CONFIG_IS_ENABLED(DM_USB_GADGET)
136 debug("%s: dr_mode: OTG or Peripheral\n", __func__);
137 driver = "dwc3-generic-peripheral";
138 #endif
139 break;
140 case USB_DR_MODE_HOST:
141 debug("%s: dr_mode: HOST\n", __func__);
142 driver = "xhci-dwc3";
143 break;
144 default:
145 debug("%s: unsupported dr_mode\n", __func__);
146 return -ENODEV;
147 };
148
149 if (!driver)
150 continue;
151
152 ret = device_bind_driver_to_node(parent, driver, name,
153 offset_to_ofnode(node), &dev);
154 if (ret) {
155 debug("%s: not able to bind usb device mode\n",
156 __func__);
157 return ret;
158 }
159 }
160
161 return 0;
162 }
163
164 static int dwc3_glue_reset_init(struct udevice *dev,
165 struct dwc3_glue_data *glue)
166 {
167 int ret;
168
169 ret = reset_get_bulk(dev, &glue->resets);
170 if (ret == -ENOTSUPP)
171 return 0;
172 else if (ret)
173 return ret;
174
175 ret = reset_deassert_bulk(&glue->resets);
176 if (ret) {
177 reset_release_bulk(&glue->resets);
178 return ret;
179 }
180
181 return 0;
182 }
183
184 static int dwc3_glue_clk_init(struct udevice *dev,
185 struct dwc3_glue_data *glue)
186 {
187 int ret;
188
189 ret = clk_get_bulk(dev, &glue->clks);
190 if (ret == -ENOSYS)
191 return 0;
192 if (ret)
193 return ret;
194
195 #if CONFIG_IS_ENABLED(CLK)
196 ret = clk_enable_bulk(&glue->clks);
197 if (ret) {
198 clk_release_bulk(&glue->clks);
199 return ret;
200 }
201 #endif
202
203 return 0;
204 }
205
206 static int dwc3_glue_probe(struct udevice *dev)
207 {
208 struct dwc3_glue_data *glue = dev_get_platdata(dev);
209 int ret;
210
211 ret = dwc3_glue_clk_init(dev, glue);
212 if (ret)
213 return ret;
214
215 ret = dwc3_glue_reset_init(dev, glue);
216 if (ret)
217 return ret;
218
219 return 0;
220 }
221
222 static int dwc3_glue_remove(struct udevice *dev)
223 {
224 struct dwc3_glue_data *glue = dev_get_platdata(dev);
225
226 reset_release_bulk(&glue->resets);
227
228 clk_release_bulk(&glue->clks);
229
230 return dm_scan_fdt_dev(dev);
231 }
232
233 static const struct udevice_id dwc3_glue_ids[] = {
234 { .compatible = "xlnx,zynqmp-dwc3" },
235 { }
236 };
237
238 U_BOOT_DRIVER(dwc3_generic_wrapper) = {
239 .name = "dwc3-generic-wrapper",
240 .id = UCLASS_MISC,
241 .of_match = dwc3_glue_ids,
242 .bind = dwc3_glue_bind,
243 .probe = dwc3_glue_probe,
244 .remove = dwc3_glue_remove,
245 .platdata_auto_alloc_size = sizeof(struct dwc3_glue_data),
246
247 };