1 // SPDX-License-Identifier: GPL-2.0
3 * OHCI HCD (Host Controller Driver) for USB.
5 * Copyright (C) 2010 ST Microelectronics.
6 * Deepak Sikri<deepak.sikri@st.com>
8 * Based on various ohci-*.c drivers
11 #include <linux/clk.h>
12 #include <linux/dma-mapping.h>
14 #include <linux/kernel.h>
15 #include <linux/module.h>
17 #include <linux/platform_device.h>
18 #include <linux/signal.h>
19 #include <linux/usb.h>
20 #include <linux/usb/hcd.h>
24 #define DRIVER_DESC "OHCI SPEAr driver"
26 static const char hcd_name
[] = "SPEAr-ohci";
31 #define to_spear_ohci(hcd) (struct spear_ohci *)(hcd_to_ohci(hcd)->priv)
33 static struct hc_driver __read_mostly ohci_spear_hc_driver
;
35 static int spear_ohci_hcd_drv_probe(struct platform_device
*pdev
)
37 const struct hc_driver
*driver
= &ohci_spear_hc_driver
;
38 struct ohci_hcd
*ohci
;
39 struct usb_hcd
*hcd
= NULL
;
41 struct spear_ohci
*sohci_p
;
45 irq
= platform_get_irq(pdev
, 0);
52 * Right now device-tree probed devices don't get dma_mask set.
53 * Since shared usb code relies on it, set it here for now.
54 * Once we have dma capability bindings this can go away.
56 retval
= dma_coerce_mask_and_coherent(&pdev
->dev
, DMA_BIT_MASK(32));
60 usbh_clk
= devm_clk_get(&pdev
->dev
, NULL
);
61 if (IS_ERR(usbh_clk
)) {
62 dev_err(&pdev
->dev
, "Error getting interface clock\n");
63 retval
= PTR_ERR(usbh_clk
);
67 hcd
= usb_create_hcd(driver
, &pdev
->dev
, dev_name(&pdev
->dev
));
73 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
74 hcd
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
75 if (IS_ERR(hcd
->regs
)) {
76 retval
= PTR_ERR(hcd
->regs
);
80 hcd
->rsrc_start
= pdev
->resource
[0].start
;
81 hcd
->rsrc_len
= resource_size(res
);
83 sohci_p
= to_spear_ohci(hcd
);
84 sohci_p
->clk
= usbh_clk
;
86 clk_prepare_enable(sohci_p
->clk
);
88 ohci
= hcd_to_ohci(hcd
);
90 retval
= usb_add_hcd(hcd
, platform_get_irq(pdev
, 0), 0);
92 device_wakeup_enable(hcd
->self
.controller
);
96 clk_disable_unprepare(sohci_p
->clk
);
100 dev_err(&pdev
->dev
, "init fail, %d\n", retval
);
105 static int spear_ohci_hcd_drv_remove(struct platform_device
*pdev
)
107 struct usb_hcd
*hcd
= platform_get_drvdata(pdev
);
108 struct spear_ohci
*sohci_p
= to_spear_ohci(hcd
);
112 clk_disable_unprepare(sohci_p
->clk
);
118 #if defined(CONFIG_PM)
119 static int spear_ohci_hcd_drv_suspend(struct platform_device
*pdev
,
120 pm_message_t message
)
122 struct usb_hcd
*hcd
= platform_get_drvdata(pdev
);
123 struct ohci_hcd
*ohci
= hcd_to_ohci(hcd
);
124 struct spear_ohci
*sohci_p
= to_spear_ohci(hcd
);
125 bool do_wakeup
= device_may_wakeup(&pdev
->dev
);
128 if (time_before(jiffies
, ohci
->next_statechange
))
130 ohci
->next_statechange
= jiffies
;
132 ret
= ohci_suspend(hcd
, do_wakeup
);
136 clk_disable_unprepare(sohci_p
->clk
);
141 static int spear_ohci_hcd_drv_resume(struct platform_device
*dev
)
143 struct usb_hcd
*hcd
= platform_get_drvdata(dev
);
144 struct ohci_hcd
*ohci
= hcd_to_ohci(hcd
);
145 struct spear_ohci
*sohci_p
= to_spear_ohci(hcd
);
147 if (time_before(jiffies
, ohci
->next_statechange
))
149 ohci
->next_statechange
= jiffies
;
151 clk_prepare_enable(sohci_p
->clk
);
152 ohci_resume(hcd
, false);
157 static const struct of_device_id spear_ohci_id_table
[] = {
158 { .compatible
= "st,spear600-ohci", },
161 MODULE_DEVICE_TABLE(of
, spear_ohci_id_table
);
163 /* Driver definition to register with the platform bus */
164 static struct platform_driver spear_ohci_hcd_driver
= {
165 .probe
= spear_ohci_hcd_drv_probe
,
166 .remove
= spear_ohci_hcd_drv_remove
,
168 .suspend
= spear_ohci_hcd_drv_suspend
,
169 .resume
= spear_ohci_hcd_drv_resume
,
172 .name
= "spear-ohci",
173 .of_match_table
= spear_ohci_id_table
,
177 static const struct ohci_driver_overrides spear_overrides __initconst
= {
178 .extra_priv_size
= sizeof(struct spear_ohci
),
180 static int __init
ohci_spear_init(void)
185 pr_info("%s: " DRIVER_DESC
"\n", hcd_name
);
187 ohci_init_driver(&ohci_spear_hc_driver
, &spear_overrides
);
188 return platform_driver_register(&spear_ohci_hcd_driver
);
190 module_init(ohci_spear_init
);
192 static void __exit
ohci_spear_cleanup(void)
194 platform_driver_unregister(&spear_ohci_hcd_driver
);
196 module_exit(ohci_spear_cleanup
);
198 MODULE_DESCRIPTION(DRIVER_DESC
);
199 MODULE_AUTHOR("Deepak Sikri");
200 MODULE_LICENSE("GPL v2");
201 MODULE_ALIAS("platform:spear-ohci");