blob: 6f5e5af47d03d552bb1717c4904ccb6b2f3514fe [file] [log] [blame]
Vignesh Raghavendrab1a49282019-10-01 17:26:33 +05301// SPDX-License-Identifier: GPL-2.0
2/*
3 * Cadence USBSS DRD Driver.
4 *
5 * Copyright (C) 2018-2019 Cadence.
6 * Copyright (C) 2017-2018 NXP
7 * Copyright (C) 2019 Texas Instruments
8 *
9 * Author: Peter Chen <peter.chen@nxp.com>
10 * Pawel Laszczak <pawell@cadence.com>
11 * Roger Quadros <rogerq@ti.com>
12 */
13
14#include <common.h>
15#include <dm.h>
16#include <dm/device-internal.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070017#include <dm/devres.h>
Vignesh Raghavendrab1a49282019-10-01 17:26:33 +053018#include <dm/lists.h>
19#include <linux/kernel.h>
20#include <linux/io.h>
21#include <usb.h>
Vignesh Raghavendra833e4d12019-11-18 19:16:33 +053022#include <usb/xhci.h>
Vignesh Raghavendrab1a49282019-10-01 17:26:33 +053023
24#include "core.h"
25#include "host-export.h"
26#include "gadget-export.h"
27#include "drd.h"
28
29static int cdns3_idle_init(struct cdns3 *cdns);
30
31struct cdns3_host_priv {
32 struct xhci_ctrl xhci_ctrl;
33 struct cdns3 cdns;
34};
35
36struct cdns3_gadget_priv {
37 struct cdns3 cdns;
38};
39
40static inline
41struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
42{
43 WARN_ON(!cdns->roles[cdns->role]);
44 return cdns->roles[cdns->role];
45}
46
47static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
48{
49 int ret;
50
51 if (WARN_ON(role > USB_ROLE_DEVICE))
52 return 0;
53
54 mutex_lock(&cdns->mutex);
55 cdns->role = role;
56 mutex_unlock(&cdns->mutex);
57
58 if (!cdns->roles[role])
59 return -ENXIO;
60
61 if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
62 return 0;
63
64 mutex_lock(&cdns->mutex);
65 ret = cdns->roles[role]->start(cdns);
66 if (!ret)
67 cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
68 mutex_unlock(&cdns->mutex);
69
70 return ret;
71}
72
73static void cdns3_role_stop(struct cdns3 *cdns)
74{
75 enum usb_role role = cdns->role;
76
77 if (WARN_ON(role > USB_ROLE_DEVICE))
78 return;
79
80 if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
81 return;
82
83 mutex_lock(&cdns->mutex);
84 cdns->roles[role]->stop(cdns);
85 cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
86 mutex_unlock(&cdns->mutex);
87}
88
89static void cdns3_exit_roles(struct cdns3 *cdns)
90{
91 cdns3_role_stop(cdns);
92 cdns3_drd_exit(cdns);
93}
94
95static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
96
97/**
98 * cdns3_core_init_role - initialize role of operation
99 * @cdns: Pointer to cdns3 structure
100 *
101 * Returns 0 on success otherwise negative errno
102 */
103static int cdns3_core_init_role(struct cdns3 *cdns)
104{
105 struct udevice *dev = cdns->dev;
106 enum usb_dr_mode best_dr_mode;
107 enum usb_dr_mode dr_mode;
108 int ret = 0;
109
110 dr_mode = usb_get_dr_mode(dev_of_offset(dev));
111 cdns->role = USB_ROLE_NONE;
112
113 /*
114 * If driver can't read mode by means of usb_get_dr_mode function then
115 * chooses mode according with Kernel configuration. This setting
116 * can be restricted later depending on strap pin configuration.
117 */
118 if (dr_mode == USB_DR_MODE_UNKNOWN) {
119 if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
120 IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
121 dr_mode = USB_DR_MODE_OTG;
122 else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
123 dr_mode = USB_DR_MODE_HOST;
124 else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
125 dr_mode = USB_DR_MODE_PERIPHERAL;
126 }
127
128 /*
129 * At this point cdns->dr_mode contains strap configuration.
130 * Driver try update this setting considering kernel configuration
131 */
132 best_dr_mode = cdns->dr_mode;
133
134 ret = cdns3_idle_init(cdns);
135 if (ret)
136 return ret;
137
138 if (dr_mode == USB_DR_MODE_OTG) {
139 best_dr_mode = cdns->dr_mode;
140 } else if (cdns->dr_mode == USB_DR_MODE_OTG) {
141 best_dr_mode = dr_mode;
142 } else if (cdns->dr_mode != dr_mode) {
143 dev_err(dev, "Incorrect DRD configuration\n");
144 return -EINVAL;
145 }
146
147 dr_mode = best_dr_mode;
148
149#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || !defined(CONFIG_SPL_BUILD)
150 if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
151 ret = cdns3_host_init(cdns);
152 if (ret) {
153 dev_err(dev, "Host initialization failed with %d\n",
154 ret);
155 goto err;
156 }
157 }
158#endif
159
160#if CONFIG_IS_ENABLED(DM_USB_GADGET)
161 if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
162 ret = cdns3_gadget_init(cdns);
163 if (ret) {
164 dev_err(dev, "Device initialization failed with %d\n",
165 ret);
166 goto err;
167 }
168 }
169#endif
170
171 cdns->dr_mode = dr_mode;
172
173 ret = cdns3_drd_update_mode(cdns);
174 if (ret)
175 goto err;
176
177 if (cdns->dr_mode != USB_DR_MODE_OTG) {
178 ret = cdns3_hw_role_switch(cdns);
179 if (ret)
180 goto err;
181 }
182
183 return ret;
184err:
185 cdns3_exit_roles(cdns);
186 return ret;
187}
188
189/**
190 * cdsn3_hw_role_state_machine - role switch state machine based on hw events
191 * @cdns: Pointer to controller structure.
192 *
193 * Returns next role to be entered based on hw events.
194 */
195static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
196{
197 enum usb_role role;
198 int id, vbus;
199
200 if (cdns->dr_mode != USB_DR_MODE_OTG)
201 goto not_otg;
202
203 id = cdns3_get_id(cdns);
204 vbus = cdns3_get_vbus(cdns);
205
206 /*
207 * Role change state machine
208 * Inputs: ID, VBUS
209 * Previous state: cdns->role
210 * Next state: role
211 */
212 role = cdns->role;
213
214 switch (role) {
215 case USB_ROLE_NONE:
216 /*
217 * Driver treats USB_ROLE_NONE synonymous to IDLE state from
218 * controller specification.
219 */
220 if (!id)
221 role = USB_ROLE_HOST;
222 else if (vbus)
223 role = USB_ROLE_DEVICE;
224 break;
225 case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
226 if (id)
227 role = USB_ROLE_NONE;
228 break;
229 case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
230 if (!vbus)
231 role = USB_ROLE_NONE;
232 break;
233 }
234
235 dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
236
237 return role;
238
239not_otg:
240 if (cdns3_is_host(cdns))
241 role = USB_ROLE_HOST;
242 if (cdns3_is_device(cdns))
243 role = USB_ROLE_DEVICE;
244
245 return role;
246}
247
248static int cdns3_idle_role_start(struct cdns3 *cdns)
249{
250 return 0;
251}
252
253static void cdns3_idle_role_stop(struct cdns3 *cdns)
254{
255 /* Program Lane swap and bring PHY out of RESET */
256 generic_phy_reset(&cdns->usb3_phy);
257}
258
259static int cdns3_idle_init(struct cdns3 *cdns)
260{
261 struct cdns3_role_driver *rdrv;
262
263 rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
264 if (!rdrv)
265 return -ENOMEM;
266
267 rdrv->start = cdns3_idle_role_start;
268 rdrv->stop = cdns3_idle_role_stop;
269 rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
270 rdrv->suspend = NULL;
271 rdrv->resume = NULL;
272 rdrv->name = "idle";
273
274 cdns->roles[USB_ROLE_NONE] = rdrv;
275
276 return 0;
277}
278
279/**
280 * cdns3_hw_role_switch - switch roles based on HW state
281 * @cdns3: controller
282 */
283int cdns3_hw_role_switch(struct cdns3 *cdns)
284{
285 enum usb_role real_role, current_role;
286 int ret = 0;
287
288 /* Do nothing if role based on syfs. */
289 if (cdns->role_override)
290 return 0;
291
292 current_role = cdns->role;
293 real_role = cdsn3_hw_role_state_machine(cdns);
294
295 /* Do nothing if nothing changed */
296 if (current_role == real_role)
297 goto exit;
298
299 cdns3_role_stop(cdns);
300
301 dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
302
303 ret = cdns3_role_start(cdns, real_role);
304 if (ret) {
305 /* Back to current role */
306 dev_err(cdns->dev, "set %d has failed, back to %d\n",
307 real_role, current_role);
308 ret = cdns3_role_start(cdns, current_role);
309 if (ret)
310 dev_err(cdns->dev, "back to %d failed too\n",
311 current_role);
312 }
313exit:
314 return ret;
315}
316
317static int cdns3_probe(struct cdns3 *cdns)
318{
319 struct udevice *dev = cdns->dev;
320 int ret;
321
322 cdns->xhci_regs = dev_remap_addr_name(dev, "xhci");
323 if (!cdns->xhci_regs)
324 return -EINVAL;
325
326 cdns->dev_regs = dev_remap_addr_name(dev, "dev");
327 if (!cdns->dev_regs)
328 return -EINVAL;
329
330 mutex_init(&cdns->mutex);
331
332 ret = generic_phy_get_by_name(dev, "cdns3,usb2-phy", &cdns->usb2_phy);
333 if (ret)
334 dev_warn(dev, "Unable to get USB2 phy (ret %d)\n", ret);
335
336 ret = generic_phy_init(&cdns->usb2_phy);
337 if (ret)
338 return ret;
339
340 ret = generic_phy_get_by_name(dev, "cdns3,usb3-phy", &cdns->usb3_phy);
341 if (ret)
342 dev_warn(dev, "Unable to get USB3 phy (ret %d)\n", ret);
343
344 ret = generic_phy_init(&cdns->usb3_phy);
345 if (ret)
346 return ret;
347
348 ret = generic_phy_power_on(&cdns->usb2_phy);
349 if (ret)
350 return ret;
351
352 ret = generic_phy_power_on(&cdns->usb3_phy);
353 if (ret)
354 return ret;
355
356 ret = cdns3_drd_init(cdns);
357 if (ret)
358 return ret;
359
360 ret = cdns3_core_init_role(cdns);
361 if (ret)
362 return ret;
363
364 dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
365
366 return 0;
367}
368
369static int cdns3_remove(struct cdns3 *cdns)
370{
371 cdns3_exit_roles(cdns);
372 generic_phy_power_off(&cdns->usb2_phy);
373 generic_phy_power_off(&cdns->usb3_phy);
374 generic_phy_exit(&cdns->usb2_phy);
375 generic_phy_exit(&cdns->usb3_phy);
376 return 0;
377}
378
379static const struct udevice_id cdns3_ids[] = {
380 { .compatible = "cdns,usb3" },
381 { },
382};
383
384int cdns3_bind(struct udevice *parent)
385{
386 int from = dev_of_offset(parent);
387 const void *fdt = gd->fdt_blob;
388 enum usb_dr_mode dr_mode;
389 struct udevice *dev;
390 const char *driver;
391 const char *name;
392 int node;
393 int ret;
394
395 node = fdt_node_offset_by_compatible(fdt, from, "cdns,usb3");
396 if (node < 0) {
397 ret = -ENODEV;
398 goto fail;
399 }
400
401 name = fdt_get_name(fdt, node, NULL);
402 dr_mode = usb_get_dr_mode(node);
403
404 switch (dr_mode) {
405#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \
406 (!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST))
407 case USB_DR_MODE_HOST:
408 debug("%s: dr_mode: HOST\n", __func__);
409 driver = "cdns-usb3-host";
410 break;
411#endif
412#if CONFIG_IS_ENABLED(DM_USB_GADGET)
413 case USB_DR_MODE_PERIPHERAL:
414 debug("%s: dr_mode: PERIPHERAL\n", __func__);
415 driver = "cdns-usb3-peripheral";
416 break;
417#endif
418 default:
419 printf("%s: unsupported dr_mode\n", __func__);
420 ret = -ENODEV;
421 goto fail;
422 };
423
424 ret = device_bind_driver_to_node(parent, driver, name,
425 offset_to_ofnode(node), &dev);
426 if (ret) {
427 printf("%s: not able to bind usb device mode\n",
428 __func__);
429 goto fail;
430 }
431
432 return 0;
433
434fail:
435 /* do not return an error: failing to bind would hang the board */
436 return 0;
437}
438
439#if CONFIG_IS_ENABLED(DM_USB_GADGET)
440static int cdns3_gadget_probe(struct udevice *dev)
441{
442 struct cdns3_gadget_priv *priv = dev_get_priv(dev);
443 struct cdns3 *cdns = &priv->cdns;
444
445 cdns->dev = dev;
446
447 return cdns3_probe(cdns);
448}
449
450static int cdns3_gadget_remove(struct udevice *dev)
451{
452 struct cdns3_gadget_priv *priv = dev_get_priv(dev);
453 struct cdns3 *cdns = &priv->cdns;
454
455 return cdns3_remove(cdns);
456}
457
458U_BOOT_DRIVER(cdns_usb3_peripheral) = {
459 .name = "cdns-usb3-peripheral",
460 .id = UCLASS_USB_GADGET_GENERIC,
461 .of_match = cdns3_ids,
462 .probe = cdns3_gadget_probe,
463 .remove = cdns3_gadget_remove,
464 .priv_auto_alloc_size = sizeof(struct cdns3_gadget_priv),
465 .flags = DM_FLAG_ALLOC_PRIV_DMA,
466};
467#endif
468
469#if defined(CONFIG_SPL_USB_HOST_SUPPORT) || \
470 (!defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_HOST))
471static int cdns3_host_probe(struct udevice *dev)
472{
473 struct cdns3_host_priv *priv = dev_get_priv(dev);
474 struct cdns3 *cdns = &priv->cdns;
475
476 cdns->dev = dev;
477
478 return cdns3_probe(cdns);
479}
480
481static int cdns3_host_remove(struct udevice *dev)
482{
483 struct cdns3_host_priv *priv = dev_get_priv(dev);
484 struct cdns3 *cdns = &priv->cdns;
485
486 return cdns3_remove(cdns);
487}
488
489U_BOOT_DRIVER(cdns_usb3_host) = {
490 .name = "cdns-usb3-host",
491 .id = UCLASS_USB,
492 .of_match = cdns3_ids,
493 .probe = cdns3_host_probe,
494 .remove = cdns3_host_remove,
495 .priv_auto_alloc_size = sizeof(struct cdns3_host_priv),
496 .ops = &xhci_usb_ops,
497 .flags = DM_FLAG_ALLOC_PRIV_DMA,
498};
499#endif