blob: b0e0f8ea98a9c9b7623096ac5ec5452a52caaa67 [file] [log] [blame]
Andrew Scullb4b6d4a2019-01-02 15:54:55 +00001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
4 * Author: Chao Xie <chao.xie@marvell.com>
5 * Neil Zhang <zhangwm@marvell.com>
6 */
7
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/platform_device.h>
11#include <linux/clk.h>
12#include <linux/err.h>
13#include <linux/usb/otg.h>
14#include <linux/platform_data/mv_usb.h>
David Brazdil0f672f62019-12-10 10:32:29 +000015#include <linux/io.h>
16
17#include <linux/usb/hcd.h>
18
19#include "ehci.h"
20
21/* registers */
22#define U2x_CAPREGS_OFFSET 0x100
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000023
24#define CAPLENGTH_MASK (0xff)
25
David Brazdil0f672f62019-12-10 10:32:29 +000026#define hcd_to_ehci_hcd_mv(h) ((struct ehci_hcd_mv *)hcd_to_ehci(h)->priv)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000027
David Brazdil0f672f62019-12-10 10:32:29 +000028struct ehci_hcd_mv {
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000029 /* Which mode does this ehci running OTG/Host ? */
30 int mode;
31
David Brazdil0f672f62019-12-10 10:32:29 +000032 void __iomem *base;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000033 void __iomem *cap_regs;
34 void __iomem *op_regs;
35
36 struct usb_phy *otg;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000037 struct clk *clk;
David Brazdil0f672f62019-12-10 10:32:29 +000038
39 struct phy *phy;
40
41 int (*set_vbus)(unsigned int vbus);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000042};
43
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000044static int mv_ehci_enable(struct ehci_hcd_mv *ehci_mv)
45{
Olivier Deprez0e641232021-09-23 10:07:05 +020046 int retval;
47
48 retval = clk_prepare_enable(ehci_mv->clk);
49 if (retval)
50 return retval;
51
52 retval = phy_init(ehci_mv->phy);
53 if (retval)
54 clk_disable_unprepare(ehci_mv->clk);
55
56 return retval;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000057}
58
59static void mv_ehci_disable(struct ehci_hcd_mv *ehci_mv)
60{
David Brazdil0f672f62019-12-10 10:32:29 +000061 phy_exit(ehci_mv->phy);
Olivier Deprez0e641232021-09-23 10:07:05 +020062 clk_disable_unprepare(ehci_mv->clk);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000063}
64
65static int mv_ehci_reset(struct usb_hcd *hcd)
66{
67 struct device *dev = hcd->self.controller;
David Brazdil0f672f62019-12-10 10:32:29 +000068 struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000069 int retval;
70
71 if (ehci_mv == NULL) {
72 dev_err(dev, "Can not find private ehci data\n");
73 return -ENODEV;
74 }
75
76 hcd->has_tt = 1;
77
78 retval = ehci_setup(hcd);
79 if (retval)
80 dev_err(dev, "ehci_setup failed %d\n", retval);
81
82 return retval;
83}
84
David Brazdil0f672f62019-12-10 10:32:29 +000085static struct hc_driver __read_mostly ehci_platform_hc_driver;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000086
David Brazdil0f672f62019-12-10 10:32:29 +000087static const struct ehci_driver_overrides platform_overrides __initconst = {
88 .reset = mv_ehci_reset,
89 .extra_priv_size = sizeof(struct ehci_hcd_mv),
Andrew Scullb4b6d4a2019-01-02 15:54:55 +000090};
91
92static int mv_ehci_probe(struct platform_device *pdev)
93{
94 struct mv_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
95 struct usb_hcd *hcd;
96 struct ehci_hcd *ehci;
97 struct ehci_hcd_mv *ehci_mv;
98 struct resource *r;
99 int retval = -ENODEV;
100 u32 offset;
101
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000102 if (usb_disabled())
103 return -ENODEV;
104
David Brazdil0f672f62019-12-10 10:32:29 +0000105 hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, "mv ehci");
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000106 if (!hcd)
107 return -ENOMEM;
108
David Brazdil0f672f62019-12-10 10:32:29 +0000109 platform_set_drvdata(pdev, hcd);
110 ehci_mv = hcd_to_ehci_hcd_mv(hcd);
111
112 ehci_mv->mode = MV_USB_MODE_HOST;
113 if (pdata) {
114 ehci_mv->mode = pdata->mode;
115 ehci_mv->set_vbus = pdata->set_vbus;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000116 }
117
David Brazdil0f672f62019-12-10 10:32:29 +0000118 ehci_mv->phy = devm_phy_get(&pdev->dev, "usb");
119 if (IS_ERR(ehci_mv->phy)) {
120 retval = PTR_ERR(ehci_mv->phy);
121 if (retval != -EPROBE_DEFER)
122 dev_err(&pdev->dev, "Failed to get phy.\n");
123 goto err_put_hcd;
124 }
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000125
126 ehci_mv->clk = devm_clk_get(&pdev->dev, NULL);
127 if (IS_ERR(ehci_mv->clk)) {
128 dev_err(&pdev->dev, "error getting clock\n");
129 retval = PTR_ERR(ehci_mv->clk);
130 goto err_put_hcd;
131 }
132
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000133
David Brazdil0f672f62019-12-10 10:32:29 +0000134
135 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
136 ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
137 if (IS_ERR(ehci_mv->base)) {
138 retval = PTR_ERR(ehci_mv->base);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000139 goto err_put_hcd;
140 }
141
142 retval = mv_ehci_enable(ehci_mv);
143 if (retval) {
144 dev_err(&pdev->dev, "init phy error %d\n", retval);
145 goto err_put_hcd;
146 }
147
David Brazdil0f672f62019-12-10 10:32:29 +0000148 ehci_mv->cap_regs =
149 (void __iomem *) ((unsigned long) ehci_mv->base + U2x_CAPREGS_OFFSET);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000150 offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
151 ehci_mv->op_regs =
152 (void __iomem *) ((unsigned long) ehci_mv->cap_regs + offset);
153
154 hcd->rsrc_start = r->start;
155 hcd->rsrc_len = resource_size(r);
156 hcd->regs = ehci_mv->op_regs;
157
Olivier Deprez0e641232021-09-23 10:07:05 +0200158 retval = platform_get_irq(pdev, 0);
159 if (retval < 0)
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000160 goto err_disable_clk;
Olivier Deprez0e641232021-09-23 10:07:05 +0200161 hcd->irq = retval;
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000162
163 ehci = hcd_to_ehci(hcd);
164 ehci->caps = (struct ehci_caps *) ehci_mv->cap_regs;
165
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000166 if (ehci_mv->mode == MV_USB_MODE_OTG) {
167 ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
168 if (IS_ERR(ehci_mv->otg)) {
169 retval = PTR_ERR(ehci_mv->otg);
170
171 if (retval == -ENXIO)
172 dev_info(&pdev->dev, "MV_USB_MODE_OTG "
173 "must have CONFIG_USB_PHY enabled\n");
174 else
175 dev_err(&pdev->dev,
176 "unable to find transceiver\n");
177 goto err_disable_clk;
178 }
179
180 retval = otg_set_host(ehci_mv->otg->otg, &hcd->self);
181 if (retval < 0) {
182 dev_err(&pdev->dev,
183 "unable to register with transceiver\n");
184 retval = -ENODEV;
185 goto err_disable_clk;
186 }
187 /* otg will enable clock before use as host */
188 mv_ehci_disable(ehci_mv);
189 } else {
David Brazdil0f672f62019-12-10 10:32:29 +0000190 if (ehci_mv->set_vbus)
191 ehci_mv->set_vbus(1);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000192
193 retval = usb_add_hcd(hcd, hcd->irq, IRQF_SHARED);
194 if (retval) {
195 dev_err(&pdev->dev,
196 "failed to add hcd with err %d\n", retval);
197 goto err_set_vbus;
198 }
199 device_wakeup_enable(hcd->self.controller);
200 }
201
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000202 dev_info(&pdev->dev,
203 "successful find EHCI device with regs 0x%p irq %d"
204 " working in %s mode\n", hcd->regs, hcd->irq,
205 ehci_mv->mode == MV_USB_MODE_OTG ? "OTG" : "Host");
206
207 return 0;
208
209err_set_vbus:
David Brazdil0f672f62019-12-10 10:32:29 +0000210 if (ehci_mv->set_vbus)
211 ehci_mv->set_vbus(0);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000212err_disable_clk:
213 mv_ehci_disable(ehci_mv);
214err_put_hcd:
215 usb_put_hcd(hcd);
216
217 return retval;
218}
219
220static int mv_ehci_remove(struct platform_device *pdev)
221{
David Brazdil0f672f62019-12-10 10:32:29 +0000222 struct usb_hcd *hcd = platform_get_drvdata(pdev);
223 struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000224
225 if (hcd->rh_registered)
226 usb_remove_hcd(hcd);
227
228 if (!IS_ERR_OR_NULL(ehci_mv->otg))
229 otg_set_host(ehci_mv->otg->otg, NULL);
230
231 if (ehci_mv->mode == MV_USB_MODE_HOST) {
David Brazdil0f672f62019-12-10 10:32:29 +0000232 if (ehci_mv->set_vbus)
233 ehci_mv->set_vbus(0);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000234
235 mv_ehci_disable(ehci_mv);
236 }
237
238 usb_put_hcd(hcd);
239
240 return 0;
241}
242
243MODULE_ALIAS("mv-ehci");
244
245static const struct platform_device_id ehci_id_table[] = {
246 {"pxa-u2oehci", PXA_U2OEHCI},
247 {"pxa-sph", PXA_SPH},
248 {"mmp3-hsic", MMP3_HSIC},
249 {"mmp3-fsic", MMP3_FSIC},
250 {},
251};
252
253static void mv_ehci_shutdown(struct platform_device *pdev)
254{
David Brazdil0f672f62019-12-10 10:32:29 +0000255 struct usb_hcd *hcd = platform_get_drvdata(pdev);
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000256
257 if (!hcd->rh_registered)
258 return;
259
260 if (hcd->driver->shutdown)
261 hcd->driver->shutdown(hcd);
262}
263
David Brazdil0f672f62019-12-10 10:32:29 +0000264static const struct of_device_id ehci_mv_dt_ids[] = {
265 { .compatible = "marvell,pxau2o-ehci", },
266 {},
267};
268
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000269static struct platform_driver ehci_mv_driver = {
270 .probe = mv_ehci_probe,
271 .remove = mv_ehci_remove,
272 .shutdown = mv_ehci_shutdown,
273 .driver = {
David Brazdil0f672f62019-12-10 10:32:29 +0000274 .name = "mv-ehci",
275 .bus = &platform_bus_type,
276 .of_match_table = ehci_mv_dt_ids,
277 },
Andrew Scullb4b6d4a2019-01-02 15:54:55 +0000278 .id_table = ehci_id_table,
279};
David Brazdil0f672f62019-12-10 10:32:29 +0000280
281static int __init ehci_platform_init(void)
282{
283 if (usb_disabled())
284 return -ENODEV;
285
286 ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
287 return platform_driver_register(&ehci_mv_driver);
288}
289module_init(ehci_platform_init);
290
291static void __exit ehci_platform_cleanup(void)
292{
293 platform_driver_unregister(&ehci_mv_driver);
294}
295module_exit(ehci_platform_cleanup);
296
297MODULE_DESCRIPTION("Marvell EHCI driver");
298MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
299MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>");
300MODULE_ALIAS("mv-ehci");
301MODULE_LICENSE("GPL");
302MODULE_DEVICE_TABLE(of, ehci_mv_dt_ids);