1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
4 * Author: Chao Xie <chao.xie@marvell.com>
5 * Neil Zhang <zhangwm@marvell.com>
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>
17 #include <linux/usb/hcd.h>
22 #define U2x_CAPREGS_OFFSET 0x100
24 #define CAPLENGTH_MASK (0xff)
26 #define hcd_to_ehci_hcd_mv(h) ((struct ehci_hcd_mv *)hcd_to_ehci(h)->priv)
29 /* Which mode does this ehci running OTG/Host ? */
33 void __iomem
*cap_regs
;
34 void __iomem
*op_regs
;
41 int (*set_vbus
)(unsigned int vbus
);
44 static void ehci_clock_enable(struct ehci_hcd_mv
*ehci_mv
)
46 clk_prepare_enable(ehci_mv
->clk
);
49 static void ehci_clock_disable(struct ehci_hcd_mv
*ehci_mv
)
51 clk_disable_unprepare(ehci_mv
->clk
);
54 static int mv_ehci_enable(struct ehci_hcd_mv
*ehci_mv
)
56 ehci_clock_enable(ehci_mv
);
57 return phy_init(ehci_mv
->phy
);
60 static void mv_ehci_disable(struct ehci_hcd_mv
*ehci_mv
)
62 phy_exit(ehci_mv
->phy
);
63 ehci_clock_disable(ehci_mv
);
66 static int mv_ehci_reset(struct usb_hcd
*hcd
)
68 struct device
*dev
= hcd
->self
.controller
;
69 struct ehci_hcd_mv
*ehci_mv
= hcd_to_ehci_hcd_mv(hcd
);
72 if (ehci_mv
== NULL
) {
73 dev_err(dev
, "Can not find private ehci data\n");
79 retval
= ehci_setup(hcd
);
81 dev_err(dev
, "ehci_setup failed %d\n", retval
);
86 static struct hc_driver __read_mostly ehci_platform_hc_driver
;
88 static const struct ehci_driver_overrides platform_overrides __initconst
= {
89 .reset
= mv_ehci_reset
,
90 .extra_priv_size
= sizeof(struct ehci_hcd_mv
),
93 static int mv_ehci_probe(struct platform_device
*pdev
)
95 struct mv_usb_platform_data
*pdata
= dev_get_platdata(&pdev
->dev
);
97 struct ehci_hcd
*ehci
;
98 struct ehci_hcd_mv
*ehci_mv
;
100 int retval
= -ENODEV
;
106 hcd
= usb_create_hcd(&ehci_platform_hc_driver
, &pdev
->dev
, "mv ehci");
110 platform_set_drvdata(pdev
, hcd
);
111 ehci_mv
= hcd_to_ehci_hcd_mv(hcd
);
113 ehci_mv
->mode
= MV_USB_MODE_HOST
;
115 ehci_mv
->mode
= pdata
->mode
;
116 ehci_mv
->set_vbus
= pdata
->set_vbus
;
119 ehci_mv
->phy
= devm_phy_get(&pdev
->dev
, "usb");
120 if (IS_ERR(ehci_mv
->phy
)) {
121 retval
= PTR_ERR(ehci_mv
->phy
);
122 if (retval
!= -EPROBE_DEFER
)
123 dev_err(&pdev
->dev
, "Failed to get phy.\n");
127 ehci_mv
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
128 if (IS_ERR(ehci_mv
->clk
)) {
129 dev_err(&pdev
->dev
, "error getting clock\n");
130 retval
= PTR_ERR(ehci_mv
->clk
);
136 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
137 ehci_mv
->base
= devm_ioremap_resource(&pdev
->dev
, r
);
138 if (IS_ERR(ehci_mv
->base
)) {
139 retval
= PTR_ERR(ehci_mv
->base
);
143 retval
= mv_ehci_enable(ehci_mv
);
145 dev_err(&pdev
->dev
, "init phy error %d\n", retval
);
150 (void __iomem
*) ((unsigned long) ehci_mv
->base
+ U2x_CAPREGS_OFFSET
);
151 offset
= readl(ehci_mv
->cap_regs
) & CAPLENGTH_MASK
;
153 (void __iomem
*) ((unsigned long) ehci_mv
->cap_regs
+ offset
);
155 hcd
->rsrc_start
= r
->start
;
156 hcd
->rsrc_len
= resource_size(r
);
157 hcd
->regs
= ehci_mv
->op_regs
;
159 hcd
->irq
= platform_get_irq(pdev
, 0);
161 dev_err(&pdev
->dev
, "Cannot get irq.");
163 goto err_disable_clk
;
166 ehci
= hcd_to_ehci(hcd
);
167 ehci
->caps
= (struct ehci_caps
*) ehci_mv
->cap_regs
;
169 if (ehci_mv
->mode
== MV_USB_MODE_OTG
) {
170 ehci_mv
->otg
= devm_usb_get_phy(&pdev
->dev
, USB_PHY_TYPE_USB2
);
171 if (IS_ERR(ehci_mv
->otg
)) {
172 retval
= PTR_ERR(ehci_mv
->otg
);
174 if (retval
== -ENXIO
)
175 dev_info(&pdev
->dev
, "MV_USB_MODE_OTG "
176 "must have CONFIG_USB_PHY enabled\n");
179 "unable to find transceiver\n");
180 goto err_disable_clk
;
183 retval
= otg_set_host(ehci_mv
->otg
->otg
, &hcd
->self
);
186 "unable to register with transceiver\n");
188 goto err_disable_clk
;
190 /* otg will enable clock before use as host */
191 mv_ehci_disable(ehci_mv
);
193 if (ehci_mv
->set_vbus
)
194 ehci_mv
->set_vbus(1);
196 retval
= usb_add_hcd(hcd
, hcd
->irq
, IRQF_SHARED
);
199 "failed to add hcd with err %d\n", retval
);
202 device_wakeup_enable(hcd
->self
.controller
);
206 "successful find EHCI device with regs 0x%p irq %d"
207 " working in %s mode\n", hcd
->regs
, hcd
->irq
,
208 ehci_mv
->mode
== MV_USB_MODE_OTG
? "OTG" : "Host");
213 if (ehci_mv
->set_vbus
)
214 ehci_mv
->set_vbus(0);
216 mv_ehci_disable(ehci_mv
);
223 static int mv_ehci_remove(struct platform_device
*pdev
)
225 struct usb_hcd
*hcd
= platform_get_drvdata(pdev
);
226 struct ehci_hcd_mv
*ehci_mv
= hcd_to_ehci_hcd_mv(hcd
);
228 if (hcd
->rh_registered
)
231 if (!IS_ERR_OR_NULL(ehci_mv
->otg
))
232 otg_set_host(ehci_mv
->otg
->otg
, NULL
);
234 if (ehci_mv
->mode
== MV_USB_MODE_HOST
) {
235 if (ehci_mv
->set_vbus
)
236 ehci_mv
->set_vbus(0);
238 mv_ehci_disable(ehci_mv
);
246 MODULE_ALIAS("mv-ehci");
248 static const struct platform_device_id ehci_id_table
[] = {
249 {"pxa-u2oehci", PXA_U2OEHCI
},
250 {"pxa-sph", PXA_SPH
},
251 {"mmp3-hsic", MMP3_HSIC
},
252 {"mmp3-fsic", MMP3_FSIC
},
256 static void mv_ehci_shutdown(struct platform_device
*pdev
)
258 struct usb_hcd
*hcd
= platform_get_drvdata(pdev
);
260 if (!hcd
->rh_registered
)
263 if (hcd
->driver
->shutdown
)
264 hcd
->driver
->shutdown(hcd
);
267 static const struct of_device_id ehci_mv_dt_ids
[] = {
268 { .compatible
= "marvell,pxau2o-ehci", },
272 static struct platform_driver ehci_mv_driver
= {
273 .probe
= mv_ehci_probe
,
274 .remove
= mv_ehci_remove
,
275 .shutdown
= mv_ehci_shutdown
,
278 .bus
= &platform_bus_type
,
279 .of_match_table
= ehci_mv_dt_ids
,
281 .id_table
= ehci_id_table
,
284 static int __init
ehci_platform_init(void)
289 ehci_init_driver(&ehci_platform_hc_driver
, &platform_overrides
);
290 return platform_driver_register(&ehci_mv_driver
);
292 module_init(ehci_platform_init
);
294 static void __exit
ehci_platform_cleanup(void)
296 platform_driver_unregister(&ehci_mv_driver
);
298 module_exit(ehci_platform_cleanup
);
300 MODULE_DESCRIPTION("Marvell EHCI driver");
301 MODULE_AUTHOR("Chao Xie <chao.xie@marvell.com>");
302 MODULE_AUTHOR("Neil Zhang <zhangwm@marvell.com>");
303 MODULE_ALIAS("mv-ehci");
304 MODULE_LICENSE("GPL");
305 MODULE_DEVICE_TABLE(of
, ehci_mv_dt_ids
);