1 // SPDX-License-Identifier: GPL-2.0+
3 * SAMSUNG EXYNOS USB HOST OHCI Controller
5 * Copyright (C) 2011 Samsung Electronics Co.Ltd
6 * Author: Jingoo Han <jg1.han@samsung.com>
10 #include <linux/dma-mapping.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/phy/phy.h>
17 #include <linux/usb.h>
18 #include <linux/usb/hcd.h>
22 #define DRIVER_DESC "OHCI EXYNOS driver"
24 static const char hcd_name
[] = "ohci-exynos";
25 static struct hc_driver __read_mostly exynos_ohci_hc_driver
;
27 #define to_exynos_ohci(hcd) (struct exynos_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
31 struct exynos_ohci_hcd
{
33 struct phy
*phy
[PHY_NUMBER
];
36 static int exynos_ohci_get_phy(struct device
*dev
,
37 struct exynos_ohci_hcd
*exynos_ohci
)
39 struct device_node
*child
;
44 /* Get PHYs for the controller */
45 for_each_available_child_of_node(dev
->of_node
, child
) {
46 ret
= of_property_read_u32(child
, "reg", &phy_number
);
48 dev_err(dev
, "Failed to parse device tree\n");
53 if (phy_number
>= PHY_NUMBER
) {
54 dev_err(dev
, "Invalid number of PHYs\n");
59 phy
= devm_of_phy_get(dev
, child
, NULL
);
60 exynos_ohci
->phy
[phy_number
] = phy
;
63 if (ret
== -EPROBE_DEFER
) {
66 } else if (ret
!= -ENOSYS
&& ret
!= -ENODEV
) {
68 "Error retrieving usb2 phy: %d\n", ret
);
78 static int exynos_ohci_phy_enable(struct device
*dev
)
80 struct usb_hcd
*hcd
= dev_get_drvdata(dev
);
81 struct exynos_ohci_hcd
*exynos_ohci
= to_exynos_ohci(hcd
);
85 for (i
= 0; ret
== 0 && i
< PHY_NUMBER
; i
++)
86 if (!IS_ERR(exynos_ohci
->phy
[i
]))
87 ret
= phy_power_on(exynos_ohci
->phy
[i
]);
89 for (i
--; i
>= 0; i
--)
90 if (!IS_ERR(exynos_ohci
->phy
[i
]))
91 phy_power_off(exynos_ohci
->phy
[i
]);
96 static void exynos_ohci_phy_disable(struct device
*dev
)
98 struct usb_hcd
*hcd
= dev_get_drvdata(dev
);
99 struct exynos_ohci_hcd
*exynos_ohci
= to_exynos_ohci(hcd
);
102 for (i
= 0; i
< PHY_NUMBER
; i
++)
103 if (!IS_ERR(exynos_ohci
->phy
[i
]))
104 phy_power_off(exynos_ohci
->phy
[i
]);
107 static int exynos_ohci_probe(struct platform_device
*pdev
)
109 struct exynos_ohci_hcd
*exynos_ohci
;
111 struct resource
*res
;
116 * Right now device-tree probed devices don't get dma_mask set.
117 * Since shared usb code relies on it, set it here for now.
118 * Once we move to full device tree support this will vanish off.
120 err
= dma_coerce_mask_and_coherent(&pdev
->dev
, DMA_BIT_MASK(32));
124 hcd
= usb_create_hcd(&exynos_ohci_hc_driver
,
125 &pdev
->dev
, dev_name(&pdev
->dev
));
127 dev_err(&pdev
->dev
, "Unable to create HCD\n");
131 exynos_ohci
= to_exynos_ohci(hcd
);
133 if (of_device_is_compatible(pdev
->dev
.of_node
,
134 "samsung,exynos5440-ohci"))
137 err
= exynos_ohci_get_phy(&pdev
->dev
, exynos_ohci
);
142 exynos_ohci
->clk
= devm_clk_get(&pdev
->dev
, "usbhost");
144 if (IS_ERR(exynos_ohci
->clk
)) {
145 dev_err(&pdev
->dev
, "Failed to get usbhost clock\n");
146 err
= PTR_ERR(exynos_ohci
->clk
);
150 err
= clk_prepare_enable(exynos_ohci
->clk
);
154 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
155 hcd
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
156 if (IS_ERR(hcd
->regs
)) {
157 err
= PTR_ERR(hcd
->regs
);
160 hcd
->rsrc_start
= res
->start
;
161 hcd
->rsrc_len
= resource_size(res
);
163 irq
= platform_get_irq(pdev
, 0);
165 dev_err(&pdev
->dev
, "Failed to get IRQ\n");
170 platform_set_drvdata(pdev
, hcd
);
172 err
= exynos_ohci_phy_enable(&pdev
->dev
);
174 dev_err(&pdev
->dev
, "Failed to enable USB phy\n");
178 err
= usb_add_hcd(hcd
, irq
, IRQF_SHARED
);
180 dev_err(&pdev
->dev
, "Failed to add USB HCD\n");
183 device_wakeup_enable(hcd
->self
.controller
);
187 exynos_ohci_phy_disable(&pdev
->dev
);
189 clk_disable_unprepare(exynos_ohci
->clk
);
195 static int exynos_ohci_remove(struct platform_device
*pdev
)
197 struct usb_hcd
*hcd
= platform_get_drvdata(pdev
);
198 struct exynos_ohci_hcd
*exynos_ohci
= to_exynos_ohci(hcd
);
202 exynos_ohci_phy_disable(&pdev
->dev
);
204 clk_disable_unprepare(exynos_ohci
->clk
);
211 static void exynos_ohci_shutdown(struct platform_device
*pdev
)
213 struct usb_hcd
*hcd
= platform_get_drvdata(pdev
);
215 if (hcd
->driver
->shutdown
)
216 hcd
->driver
->shutdown(hcd
);
220 static int exynos_ohci_suspend(struct device
*dev
)
222 struct usb_hcd
*hcd
= dev_get_drvdata(dev
);
223 struct exynos_ohci_hcd
*exynos_ohci
= to_exynos_ohci(hcd
);
224 bool do_wakeup
= device_may_wakeup(dev
);
225 int rc
= ohci_suspend(hcd
, do_wakeup
);
230 exynos_ohci_phy_disable(dev
);
232 clk_disable_unprepare(exynos_ohci
->clk
);
237 static int exynos_ohci_resume(struct device
*dev
)
239 struct usb_hcd
*hcd
= dev_get_drvdata(dev
);
240 struct exynos_ohci_hcd
*exynos_ohci
= to_exynos_ohci(hcd
);
243 clk_prepare_enable(exynos_ohci
->clk
);
245 ret
= exynos_ohci_phy_enable(dev
);
247 dev_err(dev
, "Failed to enable USB phy\n");
248 clk_disable_unprepare(exynos_ohci
->clk
);
252 ohci_resume(hcd
, false);
257 #define exynos_ohci_suspend NULL
258 #define exynos_ohci_resume NULL
261 static const struct ohci_driver_overrides exynos_overrides __initconst
= {
262 .extra_priv_size
= sizeof(struct exynos_ohci_hcd
),
265 static const struct dev_pm_ops exynos_ohci_pm_ops
= {
266 .suspend
= exynos_ohci_suspend
,
267 .resume
= exynos_ohci_resume
,
271 static const struct of_device_id exynos_ohci_match
[] = {
272 { .compatible
= "samsung,exynos4210-ohci" },
273 { .compatible
= "samsung,exynos5440-ohci" },
276 MODULE_DEVICE_TABLE(of
, exynos_ohci_match
);
279 static struct platform_driver exynos_ohci_driver
= {
280 .probe
= exynos_ohci_probe
,
281 .remove
= exynos_ohci_remove
,
282 .shutdown
= exynos_ohci_shutdown
,
284 .name
= "exynos-ohci",
285 .pm
= &exynos_ohci_pm_ops
,
286 .of_match_table
= of_match_ptr(exynos_ohci_match
),
289 static int __init
ohci_exynos_init(void)
294 pr_info("%s: " DRIVER_DESC
"\n", hcd_name
);
295 ohci_init_driver(&exynos_ohci_hc_driver
, &exynos_overrides
);
296 return platform_driver_register(&exynos_ohci_driver
);
298 module_init(ohci_exynos_init
);
300 static void __exit
ohci_exynos_cleanup(void)
302 platform_driver_unregister(&exynos_ohci_driver
);
304 module_exit(ohci_exynos_cleanup
);
306 MODULE_ALIAS("platform:exynos-ohci");
307 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
308 MODULE_LICENSE("GPL v2");