15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2c8c38de9SDeepak Sikri /*
3c8c38de9SDeepak Sikri * OHCI HCD (Host Controller Driver) for USB.
4c8c38de9SDeepak Sikri *
5c8c38de9SDeepak Sikri * Copyright (C) 2010 ST Microelectronics.
6c8c38de9SDeepak Sikri * Deepak Sikri<deepak.sikri@st.com>
7c8c38de9SDeepak Sikri *
8c8c38de9SDeepak Sikri * Based on various ohci-*.c drivers
9c8c38de9SDeepak Sikri */
10c8c38de9SDeepak Sikri
11c8c38de9SDeepak Sikri #include <linux/clk.h>
121cc6ac59SManjunath Goudar #include <linux/dma-mapping.h>
131cc6ac59SManjunath Goudar #include <linux/io.h>
141cc6ac59SManjunath Goudar #include <linux/kernel.h>
151cc6ac59SManjunath Goudar #include <linux/module.h>
1656fafb94SStefan Roese #include <linux/of.h>
171cc6ac59SManjunath Goudar #include <linux/platform_device.h>
181cc6ac59SManjunath Goudar #include <linux/signal.h>
191cc6ac59SManjunath Goudar #include <linux/usb.h>
201cc6ac59SManjunath Goudar #include <linux/usb/hcd.h>
21c8c38de9SDeepak Sikri
221cc6ac59SManjunath Goudar #include "ohci.h"
231cc6ac59SManjunath Goudar
241cc6ac59SManjunath Goudar #define DRIVER_DESC "OHCI SPEAr driver"
251cc6ac59SManjunath Goudar
26c8c38de9SDeepak Sikri struct spear_ohci {
27c8c38de9SDeepak Sikri struct clk *clk;
28c8c38de9SDeepak Sikri };
29c8c38de9SDeepak Sikri
301cc6ac59SManjunath Goudar #define to_spear_ohci(hcd) (struct spear_ohci *)(hcd_to_ohci(hcd)->priv)
31c8c38de9SDeepak Sikri
321cc6ac59SManjunath Goudar static struct hc_driver __read_mostly ohci_spear_hc_driver;
33c8c38de9SDeepak Sikri
spear_ohci_hcd_drv_probe(struct platform_device * pdev)34c8c38de9SDeepak Sikri static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
35c8c38de9SDeepak Sikri {
36c8c38de9SDeepak Sikri const struct hc_driver *driver = &ohci_spear_hc_driver;
37c8c38de9SDeepak Sikri struct usb_hcd *hcd = NULL;
38c8c38de9SDeepak Sikri struct clk *usbh_clk;
391cc6ac59SManjunath Goudar struct spear_ohci *sohci_p;
40c8c38de9SDeepak Sikri struct resource *res;
41c8c38de9SDeepak Sikri int retval, irq;
42c8c38de9SDeepak Sikri
43c8c38de9SDeepak Sikri irq = platform_get_irq(pdev, 0);
44c8c38de9SDeepak Sikri if (irq < 0) {
45c8c38de9SDeepak Sikri retval = irq;
4698515e59SViresh Kumar goto fail;
47c8c38de9SDeepak Sikri }
48c8c38de9SDeepak Sikri
4956fafb94SStefan Roese /*
5056fafb94SStefan Roese * Right now device-tree probed devices don't get dma_mask set.
5156fafb94SStefan Roese * Since shared usb code relies on it, set it here for now.
5256fafb94SStefan Roese * Once we have dma capability bindings this can go away.
5356fafb94SStefan Roese */
54e1fd7341SRussell King retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
5522d9d8e8SRussell King if (retval)
5622d9d8e8SRussell King goto fail;
5756fafb94SStefan Roese
5898515e59SViresh Kumar usbh_clk = devm_clk_get(&pdev->dev, NULL);
59c8c38de9SDeepak Sikri if (IS_ERR(usbh_clk)) {
60c8c38de9SDeepak Sikri dev_err(&pdev->dev, "Error getting interface clock\n");
61c8c38de9SDeepak Sikri retval = PTR_ERR(usbh_clk);
6298515e59SViresh Kumar goto fail;
63c8c38de9SDeepak Sikri }
64c8c38de9SDeepak Sikri
65c8c38de9SDeepak Sikri hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
66c8c38de9SDeepak Sikri if (!hcd) {
67c8c38de9SDeepak Sikri retval = -ENOMEM;
6898515e59SViresh Kumar goto fail;
69c8c38de9SDeepak Sikri }
70c8c38de9SDeepak Sikri
71*eeaf04a9SYangtao Li hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
7209796be1SJingoo Han if (IS_ERR(hcd->regs)) {
7309796be1SJingoo Han retval = PTR_ERR(hcd->regs);
7498515e59SViresh Kumar goto err_put_hcd;
75c8c38de9SDeepak Sikri }
76c8c38de9SDeepak Sikri
772dec70f1SRob Herring hcd->rsrc_start = res->start;
780bf80cbfSVarka Bhadram hcd->rsrc_len = resource_size(res);
790bf80cbfSVarka Bhadram
801cc6ac59SManjunath Goudar sohci_p = to_spear_ohci(hcd);
811cc6ac59SManjunath Goudar sohci_p->clk = usbh_clk;
821cc6ac59SManjunath Goudar
831cc6ac59SManjunath Goudar clk_prepare_enable(sohci_p->clk);
841cc6ac59SManjunath Goudar
85e725ace0SSergey Shtylyov retval = usb_add_hcd(hcd, irq, 0);
863c9740a1SPeter Chen if (retval == 0) {
873c9740a1SPeter Chen device_wakeup_enable(hcd->self.controller);
88c8c38de9SDeepak Sikri return retval;
893c9740a1SPeter Chen }
90c8c38de9SDeepak Sikri
911cc6ac59SManjunath Goudar clk_disable_unprepare(sohci_p->clk);
9298515e59SViresh Kumar err_put_hcd:
93c8c38de9SDeepak Sikri usb_put_hcd(hcd);
9498515e59SViresh Kumar fail:
95c8c38de9SDeepak Sikri dev_err(&pdev->dev, "init fail, %d\n", retval);
96c8c38de9SDeepak Sikri
97c8c38de9SDeepak Sikri return retval;
98c8c38de9SDeepak Sikri }
99c8c38de9SDeepak Sikri
spear_ohci_hcd_drv_remove(struct platform_device * pdev)100106f477cSUwe Kleine-König static void spear_ohci_hcd_drv_remove(struct platform_device *pdev)
101c8c38de9SDeepak Sikri {
102c8c38de9SDeepak Sikri struct usb_hcd *hcd = platform_get_drvdata(pdev);
1031cc6ac59SManjunath Goudar struct spear_ohci *sohci_p = to_spear_ohci(hcd);
104c8c38de9SDeepak Sikri
105c8c38de9SDeepak Sikri usb_remove_hcd(hcd);
1061cc6ac59SManjunath Goudar if (sohci_p->clk)
1071cc6ac59SManjunath Goudar clk_disable_unprepare(sohci_p->clk);
108c8c38de9SDeepak Sikri
109c8c38de9SDeepak Sikri usb_put_hcd(hcd);
110c8c38de9SDeepak Sikri }
111c8c38de9SDeepak Sikri
112c8c38de9SDeepak Sikri #if defined(CONFIG_PM)
spear_ohci_hcd_drv_suspend(struct platform_device * pdev,pm_message_t message)113d2e3d2b3SMajunath Goudar static int spear_ohci_hcd_drv_suspend(struct platform_device *pdev,
114c8c38de9SDeepak Sikri pm_message_t message)
115c8c38de9SDeepak Sikri {
116d2e3d2b3SMajunath Goudar struct usb_hcd *hcd = platform_get_drvdata(pdev);
117c8c38de9SDeepak Sikri struct ohci_hcd *ohci = hcd_to_ohci(hcd);
1181cc6ac59SManjunath Goudar struct spear_ohci *sohci_p = to_spear_ohci(hcd);
119d2e3d2b3SMajunath Goudar bool do_wakeup = device_may_wakeup(&pdev->dev);
120d2e3d2b3SMajunath Goudar int ret;
121c8c38de9SDeepak Sikri
122c8c38de9SDeepak Sikri if (time_before(jiffies, ohci->next_statechange))
123c8c38de9SDeepak Sikri msleep(5);
124c8c38de9SDeepak Sikri ohci->next_statechange = jiffies;
125c8c38de9SDeepak Sikri
126d2e3d2b3SMajunath Goudar ret = ohci_suspend(hcd, do_wakeup);
127d2e3d2b3SMajunath Goudar if (ret)
128d2e3d2b3SMajunath Goudar return ret;
129d2e3d2b3SMajunath Goudar
1301cc6ac59SManjunath Goudar clk_disable_unprepare(sohci_p->clk);
1311cc6ac59SManjunath Goudar
132d2e3d2b3SMajunath Goudar return ret;
133c8c38de9SDeepak Sikri }
134c8c38de9SDeepak Sikri
spear_ohci_hcd_drv_resume(struct platform_device * dev)135c8c38de9SDeepak Sikri static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
136c8c38de9SDeepak Sikri {
137c8c38de9SDeepak Sikri struct usb_hcd *hcd = platform_get_drvdata(dev);
138c8c38de9SDeepak Sikri struct ohci_hcd *ohci = hcd_to_ohci(hcd);
1391cc6ac59SManjunath Goudar struct spear_ohci *sohci_p = to_spear_ohci(hcd);
140c8c38de9SDeepak Sikri
141c8c38de9SDeepak Sikri if (time_before(jiffies, ohci->next_statechange))
142c8c38de9SDeepak Sikri msleep(5);
143c8c38de9SDeepak Sikri ohci->next_statechange = jiffies;
144c8c38de9SDeepak Sikri
1451cc6ac59SManjunath Goudar clk_prepare_enable(sohci_p->clk);
146cfa49b4bSFlorian Fainelli ohci_resume(hcd, false);
147c8c38de9SDeepak Sikri return 0;
148c8c38de9SDeepak Sikri }
149c8c38de9SDeepak Sikri #endif
150c8c38de9SDeepak Sikri
15110e15d6dSJingoo Han static const struct of_device_id spear_ohci_id_table[] = {
15256fafb94SStefan Roese { .compatible = "st,spear600-ohci", },
15356fafb94SStefan Roese { },
15456fafb94SStefan Roese };
15504c0b766SLuis de Bethencourt MODULE_DEVICE_TABLE(of, spear_ohci_id_table);
15656fafb94SStefan Roese
157c8c38de9SDeepak Sikri /* Driver definition to register with the platform bus */
158c8c38de9SDeepak Sikri static struct platform_driver spear_ohci_hcd_driver = {
159c8c38de9SDeepak Sikri .probe = spear_ohci_hcd_drv_probe,
160106f477cSUwe Kleine-König .remove_new = spear_ohci_hcd_drv_remove,
161c8c38de9SDeepak Sikri #ifdef CONFIG_PM
162c8c38de9SDeepak Sikri .suspend = spear_ohci_hcd_drv_suspend,
163c8c38de9SDeepak Sikri .resume = spear_ohci_hcd_drv_resume,
164c8c38de9SDeepak Sikri #endif
165c8c38de9SDeepak Sikri .driver = {
166c8c38de9SDeepak Sikri .name = "spear-ohci",
167c0d6f0b4SSachin Kamat .of_match_table = spear_ohci_id_table,
168c8c38de9SDeepak Sikri },
169c8c38de9SDeepak Sikri };
170c8c38de9SDeepak Sikri
1711cc6ac59SManjunath Goudar static const struct ohci_driver_overrides spear_overrides __initconst = {
1721cc6ac59SManjunath Goudar .extra_priv_size = sizeof(struct spear_ohci),
1731cc6ac59SManjunath Goudar };
ohci_spear_init(void)1741cc6ac59SManjunath Goudar static int __init ohci_spear_init(void)
1751cc6ac59SManjunath Goudar {
1761cc6ac59SManjunath Goudar if (usb_disabled())
1771cc6ac59SManjunath Goudar return -ENODEV;
1781cc6ac59SManjunath Goudar
1791cc6ac59SManjunath Goudar ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides);
1801cc6ac59SManjunath Goudar return platform_driver_register(&spear_ohci_hcd_driver);
1811cc6ac59SManjunath Goudar }
1821cc6ac59SManjunath Goudar module_init(ohci_spear_init);
1831cc6ac59SManjunath Goudar
ohci_spear_cleanup(void)1841cc6ac59SManjunath Goudar static void __exit ohci_spear_cleanup(void)
1851cc6ac59SManjunath Goudar {
1861cc6ac59SManjunath Goudar platform_driver_unregister(&spear_ohci_hcd_driver);
1871cc6ac59SManjunath Goudar }
1881cc6ac59SManjunath Goudar module_exit(ohci_spear_cleanup);
1891cc6ac59SManjunath Goudar
1901cc6ac59SManjunath Goudar MODULE_DESCRIPTION(DRIVER_DESC);
1911cc6ac59SManjunath Goudar MODULE_AUTHOR("Deepak Sikri");
1921cc6ac59SManjunath Goudar MODULE_LICENSE("GPL v2");
193c8c38de9SDeepak Sikri MODULE_ALIAS("platform:spear-ohci");
194