]>
Commit | Line | Data |
---|---|---|
90fbb282 AB |
1 | /* |
2 | * Copyright (C) 2015 Alexey Brodkin <abrodkin@synopsys.com> | |
3 | * | |
4 | * SPDX-License-Identifier: GPL-2.0+ | |
5 | */ | |
6 | ||
7 | #include <common.h> | |
4feefdcf | 8 | #include <clk.h> |
a1cee8e8 | 9 | #include <dm/ofnode.h> |
0d0ba1a7 | 10 | #include <generic-phy.h> |
8824cfc1 | 11 | #include <reset.h> |
643cacb6 | 12 | #include <asm/io.h> |
90fbb282 AB |
13 | #include <dm.h> |
14 | #include "ehci.h" | |
15 | ||
16 | /* | |
17 | * Even though here we don't explicitly use "struct ehci_ctrl" | |
18 | * ehci_register() expects it to be the first thing that resides in | |
19 | * device's private data. | |
20 | */ | |
21 | struct generic_ehci { | |
22 | struct ehci_ctrl ctrl; | |
a1cee8e8 PC |
23 | struct clk *clocks; |
24 | struct reset_ctl *resets; | |
0d0ba1a7 | 25 | struct phy phy; |
a1cee8e8 PC |
26 | int clock_count; |
27 | int reset_count; | |
90fbb282 AB |
28 | }; |
29 | ||
30 | static int ehci_usb_probe(struct udevice *dev) | |
31 | { | |
a1cee8e8 | 32 | struct generic_ehci *priv = dev_get_priv(dev); |
643cacb6 | 33 | struct ehci_hccr *hccr; |
90fbb282 | 34 | struct ehci_hcor *hcor; |
a1cee8e8 PC |
35 | int i, err, ret, clock_nb, reset_nb; |
36 | ||
37 | err = 0; | |
38 | priv->clock_count = 0; | |
39 | clock_nb = ofnode_count_phandle_with_args(dev_ofnode(dev), "clocks", | |
40 | "#clock-cells"); | |
41 | if (clock_nb > 0) { | |
42 | priv->clocks = devm_kcalloc(dev, clock_nb, sizeof(struct clk), | |
43 | GFP_KERNEL); | |
44 | if (!priv->clocks) | |
45 | return -ENOMEM; | |
46 | ||
47 | for (i = 0; i < clock_nb; i++) { | |
48 | err = clk_get_by_index(dev, i, &priv->clocks[i]); | |
49 | ||
50 | if (err < 0) | |
51 | break; | |
52 | err = clk_enable(&priv->clocks[i]); | |
53 | if (err) { | |
9b643e31 | 54 | pr_err("failed to enable clock %d\n", i); |
a1cee8e8 PC |
55 | clk_free(&priv->clocks[i]); |
56 | goto clk_err; | |
57 | } | |
58 | priv->clock_count++; | |
59 | } | |
60 | } else { | |
61 | if (clock_nb != -ENOENT) { | |
9b643e31 | 62 | pr_err("failed to get clock phandle(%d)\n", clock_nb); |
a1cee8e8 PC |
63 | return clock_nb; |
64 | } | |
4feefdcf | 65 | } |
90fbb282 | 66 | |
a1cee8e8 PC |
67 | priv->reset_count = 0; |
68 | reset_nb = ofnode_count_phandle_with_args(dev_ofnode(dev), "resets", | |
69 | "#reset-cells"); | |
70 | if (reset_nb > 0) { | |
71 | priv->resets = devm_kcalloc(dev, reset_nb, | |
72 | sizeof(struct reset_ctl), | |
73 | GFP_KERNEL); | |
74 | if (!priv->resets) | |
75 | return -ENOMEM; | |
76 | ||
77 | for (i = 0; i < reset_nb; i++) { | |
78 | err = reset_get_by_index(dev, i, &priv->resets[i]); | |
79 | if (err < 0) | |
80 | break; | |
8824cfc1 | 81 | |
a1cee8e8 | 82 | if (reset_deassert(&priv->resets[i])) { |
9b643e31 | 83 | pr_err("failed to deassert reset %d\n", i); |
a1cee8e8 PC |
84 | reset_free(&priv->resets[i]); |
85 | goto reset_err; | |
86 | } | |
87 | priv->reset_count++; | |
88 | } | |
89 | } else { | |
90 | if (reset_nb != -ENOENT) { | |
9b643e31 | 91 | pr_err("failed to get reset phandle(%d)\n", reset_nb); |
a1cee8e8 PC |
92 | goto clk_err; |
93 | } | |
8824cfc1 MY |
94 | } |
95 | ||
0d0ba1a7 PC |
96 | err = generic_phy_get_by_index(dev, 0, &priv->phy); |
97 | if (err) { | |
98 | if (err != -ENOENT) { | |
9b643e31 | 99 | pr_err("failed to get usb phy\n"); |
0d0ba1a7 PC |
100 | goto reset_err; |
101 | } | |
4b3928a0 | 102 | } else { |
0d0ba1a7 | 103 | |
4b3928a0 PC |
104 | err = generic_phy_init(&priv->phy); |
105 | if (err) { | |
9b643e31 | 106 | pr_err("failed to init usb phy\n"); |
4b3928a0 PC |
107 | goto reset_err; |
108 | } | |
0d0ba1a7 PC |
109 | } |
110 | ||
6e652e3a | 111 | hccr = map_physmem(dev_read_addr(dev), 0x100, MAP_NOCACHE); |
90fbb282 AB |
112 | hcor = (struct ehci_hcor *)((uintptr_t)hccr + |
113 | HC_LENGTH(ehci_readl(&hccr->cr_capbase))); | |
114 | ||
a1cee8e8 PC |
115 | err = ehci_register(dev, hccr, hcor, NULL, 0, USB_INIT_HOST); |
116 | if (err) | |
0d0ba1a7 | 117 | goto phy_err; |
a1cee8e8 PC |
118 | |
119 | return 0; | |
120 | ||
0d0ba1a7 PC |
121 | phy_err: |
122 | if (generic_phy_valid(&priv->phy)) { | |
123 | ret = generic_phy_exit(&priv->phy); | |
124 | if (ret) | |
9b643e31 | 125 | pr_err("failed to release phy\n"); |
0d0ba1a7 PC |
126 | } |
127 | ||
a1cee8e8 PC |
128 | reset_err: |
129 | ret = reset_release_all(priv->resets, priv->reset_count); | |
130 | if (ret) | |
9b643e31 | 131 | pr_err("failed to assert all resets\n"); |
a1cee8e8 PC |
132 | clk_err: |
133 | ret = clk_release_all(priv->clocks, priv->clock_count); | |
134 | if (ret) | |
9b643e31 | 135 | pr_err("failed to disable all clocks\n"); |
a1cee8e8 PC |
136 | |
137 | return err; | |
138 | } | |
139 | ||
140 | static int ehci_usb_remove(struct udevice *dev) | |
141 | { | |
142 | struct generic_ehci *priv = dev_get_priv(dev); | |
143 | int ret; | |
144 | ||
145 | ret = ehci_deregister(dev); | |
146 | if (ret) | |
147 | return ret; | |
148 | ||
0d0ba1a7 PC |
149 | if (generic_phy_valid(&priv->phy)) { |
150 | ret = generic_phy_exit(&priv->phy); | |
151 | if (ret) | |
152 | return ret; | |
153 | } | |
154 | ||
a1cee8e8 PC |
155 | ret = reset_release_all(priv->resets, priv->reset_count); |
156 | if (ret) | |
157 | return ret; | |
158 | ||
159 | return clk_release_all(priv->clocks, priv->clock_count); | |
90fbb282 AB |
160 | } |
161 | ||
90fbb282 AB |
162 | static const struct udevice_id ehci_usb_ids[] = { |
163 | { .compatible = "generic-ehci" }, | |
164 | { } | |
165 | }; | |
166 | ||
167 | U_BOOT_DRIVER(ehci_generic) = { | |
168 | .name = "ehci_generic", | |
169 | .id = UCLASS_USB, | |
170 | .of_match = ehci_usb_ids, | |
171 | .probe = ehci_usb_probe, | |
a1cee8e8 | 172 | .remove = ehci_usb_remove, |
90fbb282 AB |
173 | .ops = &ehci_usb_ops, |
174 | .priv_auto_alloc_size = sizeof(struct generic_ehci), | |
175 | .flags = DM_FLAG_ALLOC_PRIV_DMA, | |
176 | }; |