1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * ST OHCI driver |
4 | * |
5 | * Copyright (C) 2014 STMicroelectronics – All Rights Reserved |
6 | * |
7 | * Author: Peter Griffin <peter.griffin@linaro.org> |
8 | * |
9 | * Derived from ohci-platform.c |
10 | */ |
11 | |
12 | #include <linux/clk.h> |
13 | #include <linux/dma-mapping.h> |
14 | #include <linux/hrtimer.h> |
15 | #include <linux/io.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/err.h> |
19 | #include <linux/phy/phy.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/reset.h> |
22 | #include <linux/usb/ohci_pdriver.h> |
23 | #include <linux/usb.h> |
24 | #include <linux/usb/hcd.h> |
25 | |
26 | #include "ohci.h" |
27 | |
28 | #define USB_MAX_CLKS 3 |
29 | |
30 | struct st_ohci_platform_priv { |
31 | struct clk *clks[USB_MAX_CLKS]; |
32 | struct clk *clk48; |
33 | struct reset_control *rst; |
34 | struct reset_control *pwr; |
35 | struct phy *phy; |
36 | }; |
37 | |
38 | #define DRIVER_DESC "OHCI STMicroelectronics driver" |
39 | |
40 | #define hcd_to_ohci_priv(h) \ |
41 | ((struct st_ohci_platform_priv *)hcd_to_ohci(h)->priv) |
42 | |
43 | static int st_ohci_platform_power_on(struct platform_device *dev) |
44 | { |
45 | struct usb_hcd *hcd = platform_get_drvdata(pdev: dev); |
46 | struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); |
47 | int clk, ret; |
48 | |
49 | ret = reset_control_deassert(rstc: priv->pwr); |
50 | if (ret) |
51 | return ret; |
52 | |
53 | ret = reset_control_deassert(rstc: priv->rst); |
54 | if (ret) |
55 | goto err_assert_power; |
56 | |
57 | /* some SoCs don't have a dedicated 48Mhz clock, but those that do |
58 | need the rate to be explicitly set */ |
59 | if (priv->clk48) { |
60 | ret = clk_set_rate(clk: priv->clk48, rate: 48000000); |
61 | if (ret) |
62 | goto err_assert_reset; |
63 | } |
64 | |
65 | for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) { |
66 | ret = clk_prepare_enable(clk: priv->clks[clk]); |
67 | if (ret) |
68 | goto err_disable_clks; |
69 | } |
70 | |
71 | ret = phy_init(phy: priv->phy); |
72 | if (ret) |
73 | goto err_disable_clks; |
74 | |
75 | ret = phy_power_on(phy: priv->phy); |
76 | if (ret) |
77 | goto err_exit_phy; |
78 | |
79 | return 0; |
80 | |
81 | err_exit_phy: |
82 | phy_exit(phy: priv->phy); |
83 | err_disable_clks: |
84 | while (--clk >= 0) |
85 | clk_disable_unprepare(clk: priv->clks[clk]); |
86 | err_assert_reset: |
87 | reset_control_assert(rstc: priv->rst); |
88 | err_assert_power: |
89 | reset_control_assert(rstc: priv->pwr); |
90 | |
91 | return ret; |
92 | } |
93 | |
94 | static void st_ohci_platform_power_off(struct platform_device *dev) |
95 | { |
96 | struct usb_hcd *hcd = platform_get_drvdata(pdev: dev); |
97 | struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); |
98 | |
99 | int clk; |
100 | |
101 | reset_control_assert(rstc: priv->pwr); |
102 | |
103 | reset_control_assert(rstc: priv->rst); |
104 | |
105 | phy_power_off(phy: priv->phy); |
106 | |
107 | phy_exit(phy: priv->phy); |
108 | |
109 | for (clk = USB_MAX_CLKS - 1; clk >= 0; clk--) |
110 | if (priv->clks[clk]) |
111 | clk_disable_unprepare(clk: priv->clks[clk]); |
112 | } |
113 | |
114 | static struct hc_driver __read_mostly ohci_platform_hc_driver; |
115 | |
116 | static const struct ohci_driver_overrides platform_overrides __initconst = { |
117 | .product_desc = "ST OHCI controller" , |
118 | .extra_priv_size = sizeof(struct st_ohci_platform_priv), |
119 | }; |
120 | |
121 | static struct usb_ohci_pdata ohci_platform_defaults = { |
122 | .power_on = st_ohci_platform_power_on, |
123 | .power_suspend = st_ohci_platform_power_off, |
124 | .power_off = st_ohci_platform_power_off, |
125 | }; |
126 | |
127 | static int st_ohci_platform_probe(struct platform_device *dev) |
128 | { |
129 | struct usb_hcd *hcd; |
130 | struct resource *res_mem; |
131 | struct usb_ohci_pdata *pdata = &ohci_platform_defaults; |
132 | struct st_ohci_platform_priv *priv; |
133 | int err, irq, clk = 0; |
134 | |
135 | if (usb_disabled()) |
136 | return -ENODEV; |
137 | |
138 | irq = platform_get_irq(dev, 0); |
139 | if (irq < 0) |
140 | return irq; |
141 | |
142 | hcd = usb_create_hcd(driver: &ohci_platform_hc_driver, dev: &dev->dev, |
143 | bus_name: dev_name(dev: &dev->dev)); |
144 | if (!hcd) |
145 | return -ENOMEM; |
146 | |
147 | platform_set_drvdata(pdev: dev, data: hcd); |
148 | dev->dev.platform_data = pdata; |
149 | priv = hcd_to_ohci_priv(hcd); |
150 | |
151 | priv->phy = devm_phy_get(dev: &dev->dev, string: "usb" ); |
152 | if (IS_ERR(ptr: priv->phy)) { |
153 | err = PTR_ERR(ptr: priv->phy); |
154 | goto err_put_hcd; |
155 | } |
156 | |
157 | for (clk = 0; clk < USB_MAX_CLKS; clk++) { |
158 | priv->clks[clk] = of_clk_get(np: dev->dev.of_node, index: clk); |
159 | if (IS_ERR(ptr: priv->clks[clk])) { |
160 | err = PTR_ERR(ptr: priv->clks[clk]); |
161 | if (err == -EPROBE_DEFER) |
162 | goto err_put_clks; |
163 | priv->clks[clk] = NULL; |
164 | break; |
165 | } |
166 | } |
167 | |
168 | /* some SoCs don't have a dedicated 48Mhz clock, but those that |
169 | do need the rate to be explicitly set */ |
170 | priv->clk48 = devm_clk_get(dev: &dev->dev, id: "clk48" ); |
171 | if (IS_ERR(ptr: priv->clk48)) { |
172 | dev_info(&dev->dev, "48MHz clk not found\n" ); |
173 | priv->clk48 = NULL; |
174 | } |
175 | |
176 | priv->pwr = |
177 | devm_reset_control_get_optional_shared(dev: &dev->dev, id: "power" ); |
178 | if (IS_ERR(ptr: priv->pwr)) { |
179 | err = PTR_ERR(ptr: priv->pwr); |
180 | goto err_put_clks; |
181 | } |
182 | |
183 | priv->rst = |
184 | devm_reset_control_get_optional_shared(dev: &dev->dev, id: "softreset" ); |
185 | if (IS_ERR(ptr: priv->rst)) { |
186 | err = PTR_ERR(ptr: priv->rst); |
187 | goto err_put_clks; |
188 | } |
189 | |
190 | if (pdata->power_on) { |
191 | err = pdata->power_on(dev); |
192 | if (err < 0) |
193 | goto err_power; |
194 | } |
195 | |
196 | hcd->regs = devm_platform_get_and_ioremap_resource(pdev: dev, index: 0, res: &res_mem); |
197 | if (IS_ERR(ptr: hcd->regs)) { |
198 | err = PTR_ERR(ptr: hcd->regs); |
199 | goto err_power; |
200 | } |
201 | hcd->rsrc_start = res_mem->start; |
202 | hcd->rsrc_len = resource_size(res: res_mem); |
203 | |
204 | err = usb_add_hcd(hcd, irqnum: irq, IRQF_SHARED); |
205 | if (err) |
206 | goto err_power; |
207 | |
208 | device_wakeup_enable(dev: hcd->self.controller); |
209 | |
210 | platform_set_drvdata(pdev: dev, data: hcd); |
211 | |
212 | return err; |
213 | |
214 | err_power: |
215 | if (pdata->power_off) |
216 | pdata->power_off(dev); |
217 | |
218 | err_put_clks: |
219 | while (--clk >= 0) |
220 | clk_put(clk: priv->clks[clk]); |
221 | err_put_hcd: |
222 | if (pdata == &ohci_platform_defaults) |
223 | dev->dev.platform_data = NULL; |
224 | |
225 | usb_put_hcd(hcd); |
226 | |
227 | return err; |
228 | } |
229 | |
230 | static void st_ohci_platform_remove(struct platform_device *dev) |
231 | { |
232 | struct usb_hcd *hcd = platform_get_drvdata(pdev: dev); |
233 | struct usb_ohci_pdata *pdata = dev_get_platdata(dev: &dev->dev); |
234 | struct st_ohci_platform_priv *priv = hcd_to_ohci_priv(hcd); |
235 | int clk; |
236 | |
237 | usb_remove_hcd(hcd); |
238 | |
239 | if (pdata->power_off) |
240 | pdata->power_off(dev); |
241 | |
242 | |
243 | for (clk = 0; clk < USB_MAX_CLKS && priv->clks[clk]; clk++) |
244 | clk_put(clk: priv->clks[clk]); |
245 | |
246 | usb_put_hcd(hcd); |
247 | |
248 | if (pdata == &ohci_platform_defaults) |
249 | dev->dev.platform_data = NULL; |
250 | } |
251 | |
252 | #ifdef CONFIG_PM_SLEEP |
253 | |
254 | static int st_ohci_suspend(struct device *dev) |
255 | { |
256 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
257 | struct usb_ohci_pdata *pdata = dev->platform_data; |
258 | struct platform_device *pdev = to_platform_device(dev); |
259 | bool do_wakeup = device_may_wakeup(dev); |
260 | int ret; |
261 | |
262 | ret = ohci_suspend(hcd, do_wakeup); |
263 | if (ret) |
264 | return ret; |
265 | |
266 | if (pdata->power_suspend) |
267 | pdata->power_suspend(pdev); |
268 | |
269 | return ret; |
270 | } |
271 | |
272 | static int st_ohci_resume(struct device *dev) |
273 | { |
274 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
275 | struct usb_ohci_pdata *pdata = dev_get_platdata(dev); |
276 | struct platform_device *pdev = to_platform_device(dev); |
277 | int err; |
278 | |
279 | if (pdata->power_on) { |
280 | err = pdata->power_on(pdev); |
281 | if (err < 0) |
282 | return err; |
283 | } |
284 | |
285 | ohci_resume(hcd, hibernated: false); |
286 | return 0; |
287 | } |
288 | |
289 | static SIMPLE_DEV_PM_OPS(st_ohci_pm_ops, st_ohci_suspend, st_ohci_resume); |
290 | |
291 | #endif /* CONFIG_PM_SLEEP */ |
292 | |
293 | static const struct of_device_id st_ohci_platform_ids[] = { |
294 | { .compatible = "st,st-ohci-300x" , }, |
295 | { /* sentinel */ } |
296 | }; |
297 | MODULE_DEVICE_TABLE(of, st_ohci_platform_ids); |
298 | |
299 | static struct platform_driver ohci_platform_driver = { |
300 | .probe = st_ohci_platform_probe, |
301 | .remove_new = st_ohci_platform_remove, |
302 | .shutdown = usb_hcd_platform_shutdown, |
303 | .driver = { |
304 | .name = "st-ohci" , |
305 | #ifdef CONFIG_PM_SLEEP |
306 | .pm = &st_ohci_pm_ops, |
307 | #endif |
308 | .of_match_table = st_ohci_platform_ids, |
309 | } |
310 | }; |
311 | |
312 | static int __init ohci_platform_init(void) |
313 | { |
314 | if (usb_disabled()) |
315 | return -ENODEV; |
316 | |
317 | ohci_init_driver(drv: &ohci_platform_hc_driver, over: &platform_overrides); |
318 | return platform_driver_register(&ohci_platform_driver); |
319 | } |
320 | module_init(ohci_platform_init); |
321 | |
322 | static void __exit ohci_platform_cleanup(void) |
323 | { |
324 | platform_driver_unregister(&ohci_platform_driver); |
325 | } |
326 | module_exit(ohci_platform_cleanup); |
327 | |
328 | MODULE_DESCRIPTION(DRIVER_DESC); |
329 | MODULE_AUTHOR("Peter Griffin <peter.griffin@linaro.org>" ); |
330 | MODULE_LICENSE("GPL" ); |
331 | |