blob: 7be0187d21c57050c721fdb7c9d91bd777125c14 [file] [log] [blame]
Patrick Delaunay22929e12018-10-26 09:02:52 +02001// SPDX-License-Identifier: GPL-2.0
Michal Simek49d67452018-05-18 13:15:06 +02002/*
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>
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010014#include <dwc3-uboot.h>
Michal Simek49d67452018-05-18 13:15:06 +020015#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"
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010021#include <reset.h>
22#include <clk.h>
Michal Simek49d67452018-05-18 13:15:06 +020023
Jean-Jacques Hiblot687ab542018-11-29 10:52:42 +010024#if CONFIG_IS_ENABLED(DM_USB_GADGET)
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010025struct dwc3_generic_peripheral {
26 struct dwc3 dwc3;
27 struct phy *phys;
28 int num_phys;
29 fdt_addr_t base;
30};
31
Jean-Jacques Hiblotff8d7552018-11-29 10:52:43 +010032int dm_usb_gadget_handle_interrupts(struct udevice *dev)
Michal Simek49d67452018-05-18 13:15:06 +020033{
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010034 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
35 struct dwc3 *dwc3 = &priv->dwc3;
Michal Simek49d67452018-05-18 13:15:06 +020036
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010037 dwc3_gadget_uboot_handle_interrupt(dwc3);
Michal Simek49d67452018-05-18 13:15:06 +020038
39 return 0;
40}
41
42static int dwc3_generic_peripheral_probe(struct udevice *dev)
43{
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010044 int rc;
45 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
46 struct dwc3 *dwc3 = &priv->dwc3;
Michal Simek49d67452018-05-18 13:15:06 +020047
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010048 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;
Michal Simek49d67452018-05-18 13:15:06 +020063}
64
65static int dwc3_generic_peripheral_remove(struct udevice *dev)
66{
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010067 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
68 struct dwc3 *dwc3 = &priv->dwc3;
Michal Simek49d67452018-05-18 13:15:06 +020069
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010070 dwc3_remove(dwc3);
71 dwc3_shutdown_phy(dev, priv->phys, priv->num_phys);
72 unmap_physmem(dwc3->regs, MAP_NOCACHE);
Michal Simek49d67452018-05-18 13:15:06 +020073
74 return 0;
75}
76
77static int dwc3_generic_peripheral_ofdata_to_platdata(struct udevice *dev)
78{
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010079 struct dwc3_generic_peripheral *priv = dev_get_priv(dev);
80 struct dwc3 *dwc3 = &priv->dwc3;
Michal Simek49d67452018-05-18 13:15:06 +020081 int node = dev_of_offset(dev);
82
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010083 priv->base = devfdt_get_addr(dev);
Michal Simek49d67452018-05-18 13:15:06 +020084
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010085 dwc3->maximum_speed = usb_get_maximum_speed(node);
86 if (dwc3->maximum_speed == USB_SPEED_UNKNOWN) {
Michal Simek49d67452018-05-18 13:15:06 +020087 pr_err("Invalid usb maximum speed\n");
88 return -ENODEV;
89 }
90
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +010091 dwc3->dr_mode = usb_get_dr_mode(node);
92 if (dwc3->dr_mode == USB_DR_MODE_UNKNOWN) {
Michal Simek49d67452018-05-18 13:15:06 +020093 pr_err("Invalid usb mode setup\n");
94 return -ENODEV;
95 }
96
97 return 0;
98}
99
Michal Simek49d67452018-05-18 13:15:06 +0200100U_BOOT_DRIVER(dwc3_generic_peripheral) = {
101 .name = "dwc3-generic-peripheral",
Jean-Jacques Hiblot01311622018-11-29 10:52:46 +0100102 .id = UCLASS_USB_GADGET_GENERIC,
Michal Simek49d67452018-05-18 13:15:06 +0200103 .ofdata_to_platdata = dwc3_generic_peripheral_ofdata_to_platdata,
104 .probe = dwc3_generic_peripheral_probe,
105 .remove = dwc3_generic_peripheral_remove,
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100106 .priv_auto_alloc_size = sizeof(struct dwc3_generic_peripheral),
Michal Simek49d67452018-05-18 13:15:06 +0200107};
Jean-Jacques Hiblot687ab542018-11-29 10:52:42 +0100108#endif
Michal Simek49d67452018-05-18 13:15:06 +0200109
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100110struct dwc3_glue_data {
111 struct clk_bulk clks;
112 struct reset_ctl_bulk resets;
113};
114
115static int dwc3_glue_bind(struct udevice *parent)
Michal Simek49d67452018-05-18 13:15:06 +0200116{
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;
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100126 const char *driver = NULL;
Michal Simek49d67452018-05-18 13:15:06 +0200127
128 debug("%s: subnode name: %s\n", __func__, name);
Michal Simek49d67452018-05-18 13:15:06 +0200129
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:
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100135#if CONFIG_IS_ENABLED(DM_USB_GADGET)
Michal Simek49d67452018-05-18 13:15:06 +0200136 debug("%s: dr_mode: OTG or Peripheral\n", __func__);
137 driver = "dwc3-generic-peripheral";
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100138#endif
Michal Simek49d67452018-05-18 13:15:06 +0200139 break;
140 case USB_DR_MODE_HOST:
141 debug("%s: dr_mode: HOST\n", __func__);
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100142 driver = "xhci-dwc3";
Michal Simek49d67452018-05-18 13:15:06 +0200143 break;
144 default:
145 debug("%s: unsupported dr_mode\n", __func__);
146 return -ENODEV;
147 };
148
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100149 if (!driver)
150 continue;
151
Michal Simek49d67452018-05-18 13:15:06 +0200152 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
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100164static 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
184static 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
206static 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
222static 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
233static const struct udevice_id dwc3_glue_ids[] = {
Michal Simek49d67452018-05-18 13:15:06 +0200234 { .compatible = "xlnx,zynqmp-dwc3" },
235 { }
236};
237
238U_BOOT_DRIVER(dwc3_generic_wrapper) = {
239 .name = "dwc3-generic-wrapper",
240 .id = UCLASS_MISC,
Jean-Jacques Hiblot446e3a22018-11-29 10:52:48 +0100241 .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
Michal Simek49d67452018-05-18 13:15:06 +0200247};