1// SPDX-License-Identifier: GPL-2.0+
2/* Copyright (C) 2019 Stephan Gerhold */
3
4#include <common.h>
5#include <dm.h>
6#include <generic-phy.h>
7#include <dm/device_compat.h>
8#include "musb_uboot.h"
9
10static struct musb_hdrc_config ux500_musb_hdrc_config = {
11	.multipoint	= true,
12	.dyn_fifo	= true,
13	.num_eps	= 16,
14	.ram_bits	= 16,
15};
16
17struct ux500_glue {
18	struct musb_host_data mdata;
19	struct device dev;
20	struct phy phy;
21	bool enabled;
22};
23#define to_ux500_glue(d)	container_of(d, struct ux500_glue, dev)
24
25static int ux500_musb_enable(struct musb *musb)
26{
27	struct ux500_glue *glue = to_ux500_glue(musb->controller);
28	int ret;
29
30	if (glue->enabled)
31		return 0;
32
33	ret = generic_phy_power_on(&glue->phy);
34	if (ret) {
35		printf("%s: failed to power on USB PHY\n", __func__);
36		return ret;
37	}
38
39	glue->enabled = true;
40	return 0;
41}
42
43static void ux500_musb_disable(struct musb *musb)
44{
45	struct ux500_glue *glue = to_ux500_glue(musb->controller);
46	int ret;
47
48	if (!glue->enabled)
49		return;
50
51	ret = generic_phy_power_off(&glue->phy);
52	if (ret) {
53		printf("%s: failed to power off USB PHY\n", __func__);
54		return;
55	}
56
57	glue->enabled = false;
58}
59
60static int ux500_musb_init(struct musb *musb)
61{
62	struct ux500_glue *glue = to_ux500_glue(musb->controller);
63	int ret;
64
65	ret = generic_phy_init(&glue->phy);
66	if (ret) {
67		printf("%s: failed to init USB PHY\n", __func__);
68		return ret;
69	}
70
71	return 0;
72}
73
74static int ux500_musb_exit(struct musb *musb)
75{
76	struct ux500_glue *glue = to_ux500_glue(musb->controller);
77	int ret;
78
79	ret = generic_phy_exit(&glue->phy);
80	if (ret) {
81		printf("%s: failed to exit USB PHY\n", __func__);
82		return ret;
83	}
84
85	return 0;
86}
87
88static const struct musb_platform_ops ux500_musb_ops = {
89	.init		= ux500_musb_init,
90	.exit		= ux500_musb_exit,
91	.enable		= ux500_musb_enable,
92	.disable	= ux500_musb_disable,
93};
94
95int dm_usb_gadget_handle_interrupts(struct udevice *dev)
96{
97	struct ux500_glue *glue = dev_get_priv(dev);
98
99	glue->mdata.host->isr(0, glue->mdata.host);
100	return 0;
101}
102
103static int ux500_musb_probe(struct udevice *dev)
104{
105#ifdef CONFIG_USB_MUSB_HOST
106	struct usb_bus_priv *priv = dev_get_uclass_priv(dev);
107#endif
108	struct ux500_glue *glue = dev_get_priv(dev);
109	struct musb_host_data *host = &glue->mdata;
110	struct musb_hdrc_platform_data pdata;
111	void *base = dev_read_addr_ptr(dev);
112	int ret;
113
114	if (!base)
115		return -EINVAL;
116
117	ret = generic_phy_get_by_name(dev, "usb", &glue->phy);
118	if (ret) {
119		dev_err(dev, "failed to get USB PHY: %d\n", ret);
120		return ret;
121	}
122
123	memset(&pdata, 0, sizeof(pdata));
124	pdata.platform_ops = &ux500_musb_ops;
125	pdata.config = &ux500_musb_hdrc_config;
126
127#ifdef CONFIG_USB_MUSB_HOST
128	priv->desc_before_addr = true;
129	pdata.mode = MUSB_HOST;
130
131	host->host = musb_init_controller(&pdata, &glue->dev, base);
132	if (!host->host)
133		return -EIO;
134
135	return musb_lowlevel_init(host);
136#else
137	pdata.mode = MUSB_PERIPHERAL;
138	host->host = musb_init_controller(&pdata, &glue->dev, base);
139	if (!host->host)
140		return -EIO;
141
142	return usb_add_gadget_udc(&glue->dev, &host->host->g);
143#endif
144}
145
146static int ux500_musb_remove(struct udevice *dev)
147{
148	struct ux500_glue *glue = dev_get_priv(dev);
149	struct musb_host_data *host = &glue->mdata;
150
151	usb_del_gadget_udc(&host->host->g);
152	musb_stop(host->host);
153	free(host->host);
154	host->host = NULL;
155
156	return 0;
157}
158
159static const struct udevice_id ux500_musb_ids[] = {
160	{ .compatible = "stericsson,db8500-musb" },
161	{ }
162};
163
164U_BOOT_DRIVER(ux500_musb) = {
165	.name		= "ux500-musb",
166#ifdef CONFIG_USB_MUSB_HOST
167	.id		= UCLASS_USB,
168#else
169	.id		= UCLASS_USB_GADGET_GENERIC,
170#endif
171	.of_match	= ux500_musb_ids,
172	.probe		= ux500_musb_probe,
173	.remove		= ux500_musb_remove,
174#ifdef CONFIG_USB_MUSB_HOST
175	.ops		= &musb_usb_ops,
176#endif
177	.plat_auto	= sizeof(struct usb_plat),
178	.priv_auto	= sizeof(struct ux500_glue),
179};
180