xref: /openbmc/linux/drivers/usb/host/ohci-spear.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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