1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2015 Alexey Brodkin <abrodkin@synopsys.com>
4 */
5
6#include <common.h>
7#include <clk.h>
8#include <log.h>
9#include <dm/device_compat.h>
10#include <dm/devres.h>
11#include <dm/ofnode.h>
12#include <generic-phy.h>
13#include <reset.h>
14#include <asm/io.h>
15#include <dm.h>
16#include "ehci.h"
17#include <power/regulator.h>
18
19/*
20 * Even though here we don't explicitly use "struct ehci_ctrl"
21 * ehci_register() expects it to be the first thing that resides in
22 * device's private data.
23 */
24struct generic_ehci {
25	struct ehci_ctrl ctrl;
26	struct clk_bulk clocks;
27	struct reset_ctl_bulk resets;
28	struct phy phy;
29	struct udevice *vbus_supply;
30};
31
32static int ehci_enable_vbus_supply(struct udevice *dev)
33{
34	struct generic_ehci *priv = dev_get_priv(dev);
35	int ret;
36
37	ret = device_get_supply_regulator(dev, "vbus-supply",
38					  &priv->vbus_supply);
39	if (ret && ret != -ENOENT)
40		return ret;
41
42	ret = regulator_set_enable_if_allowed(priv->vbus_supply, true);
43	if (ret && ret != -ENOSYS) {
44		dev_err(dev, "Error enabling VBUS supply (ret=%d)\n", ret);
45		return ret;
46	}
47
48	return 0;
49}
50
51static int ehci_disable_vbus_supply(struct generic_ehci *priv)
52{
53	int ret;
54
55	ret = regulator_set_enable_if_allowed(priv->vbus_supply, false);
56	if (ret && ret != -ENOSYS)
57		return ret;
58
59	return 0;
60}
61
62static int ehci_usb_probe(struct udevice *dev)
63{
64	struct generic_ehci *priv = dev_get_priv(dev);
65	struct ehci_hccr *hccr;
66	struct ehci_hcor *hcor;
67	int err, ret;
68
69	err = 0;
70	ret = clk_get_bulk(dev, &priv->clocks);
71	if (ret && ret != -ENOENT) {
72		dev_err(dev, "Failed to get clocks (ret=%d)\n", ret);
73		return ret;
74	}
75
76	err = clk_enable_bulk(&priv->clocks);
77	if (err) {
78		dev_err(dev, "Failed to enable clocks (err=%d)\n", err);
79		goto clk_err;
80	}
81
82	err = reset_get_bulk(dev, &priv->resets);
83	if (err && err != -ENOENT) {
84		dev_err(dev, "Failed to get resets (err=%d)\n", err);
85		goto clk_err;
86	}
87
88	err = reset_deassert_bulk(&priv->resets);
89	if (err) {
90		dev_err(dev, "Failed to get deassert resets (err=%d)\n", err);
91		goto reset_err;
92	}
93
94	err = ehci_enable_vbus_supply(dev);
95	if (err)
96		goto reset_err;
97
98	err = generic_setup_phy(dev, &priv->phy, 0);
99	if (err)
100		goto regulator_err;
101
102	hccr = map_physmem(dev_read_addr(dev), 0x100, MAP_NOCACHE);
103	hcor = (struct ehci_hcor *)((uintptr_t)hccr +
104				    HC_LENGTH(ehci_readl(&hccr->cr_capbase)));
105
106	err = ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST);
107	if (err)
108		goto phy_err;
109
110	return 0;
111
112phy_err:
113	ret = generic_shutdown_phy(&priv->phy);
114	if (ret)
115		dev_err(dev, "failed to shutdown usb phy (ret=%d)\n", ret);
116
117regulator_err:
118	ret = ehci_disable_vbus_supply(priv);
119	if (ret)
120		dev_err(dev, "failed to disable VBUS supply (ret=%d)\n", ret);
121
122reset_err:
123	ret = reset_release_bulk(&priv->resets);
124	if (ret)
125		dev_err(dev, "failed to release resets (ret=%d)\n", ret);
126clk_err:
127	ret = clk_release_bulk(&priv->clocks);
128	if (ret)
129		dev_err(dev, "failed to release clocks (ret=%d)\n", ret);
130
131	return err;
132}
133
134static int ehci_usb_remove(struct udevice *dev)
135{
136	struct generic_ehci *priv = dev_get_priv(dev);
137	int ret;
138
139	ret = ehci_deregister(dev);
140	if (ret)
141		return ret;
142
143	ret = generic_shutdown_phy(&priv->phy);
144	if (ret)
145		return ret;
146
147	ret = ehci_disable_vbus_supply(priv);
148	if (ret)
149		return ret;
150
151	ret = reset_release_bulk(&priv->resets);
152	if (ret)
153		return ret;
154
155	return clk_release_bulk(&priv->clocks);
156}
157
158static const struct udevice_id ehci_usb_ids[] = {
159	{ .compatible = "generic-ehci" },
160	{ }
161};
162
163U_BOOT_DRIVER(ehci_generic) = {
164	.name	= "ehci_generic",
165	.id	= UCLASS_USB,
166	.of_match = ehci_usb_ids,
167	.probe = ehci_usb_probe,
168	.remove = ehci_usb_remove,
169	.ops	= &ehci_usb_ops,
170	.priv_auto	= sizeof(struct generic_ehci),
171	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
172};
173