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