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