xref: /openbmc/linux/drivers/usb/host/ehci-platform.c (revision 840ef8b7cc584a23c4f9d05352f4dbaf8e56e5ab)
1 /*
2  * Generic platform ehci driver
3  *
4  * Copyright 2007 Steven Brown <sbrown@cortland.com>
5  * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
6  *
7  * Derived from the ohci-ssb driver
8  * Copyright 2007 Michael Buesch <m@bues.ch>
9  *
10  * Derived from the EHCI-PCI driver
11  * Copyright (c) 2000-2004 by David Brownell
12  *
13  * Derived from the ohci-pci driver
14  * Copyright 1999 Roman Weissgaerber
15  * Copyright 2000-2002 David Brownell
16  * Copyright 1999 Linus Torvalds
17  * Copyright 1999 Gregory P. Smith
18  *
19  * Licensed under the GNU/GPL. See COPYING for details.
20  */
21 #include <linux/err.h>
22 #include <linux/kernel.h>
23 #include <linux/hrtimer.h>
24 #include <linux/io.h>
25 #include <linux/module.h>
26 #include <linux/platform_device.h>
27 #include <linux/usb.h>
28 #include <linux/usb/hcd.h>
29 #include <linux/usb/ehci_pdriver.h>
30 
31 #include "ehci.h"
32 
33 #define DRIVER_DESC "EHCI generic platform driver"
34 
35 static const char hcd_name[] = "ehci-platform";
36 
37 static int ehci_platform_reset(struct usb_hcd *hcd)
38 {
39 	struct platform_device *pdev = to_platform_device(hcd->self.controller);
40 	struct usb_ehci_pdata *pdata = pdev->dev.platform_data;
41 	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
42 	int retval;
43 
44 	hcd->has_tt = pdata->has_tt;
45 	ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
46 	ehci->big_endian_desc = pdata->big_endian_desc;
47 	ehci->big_endian_mmio = pdata->big_endian_mmio;
48 
49 	ehci->caps = hcd->regs + pdata->caps_offset;
50 	retval = ehci_setup(hcd);
51 	if (retval)
52 		return retval;
53 
54 	if (pdata->no_io_watchdog)
55 		ehci->need_io_watchdog = 0;
56 	return 0;
57 }
58 
59 static struct hc_driver __read_mostly ehci_platform_hc_driver;
60 
61 static const struct ehci_driver_overrides platform_overrides __initdata = {
62 	.reset =	ehci_platform_reset,
63 };
64 
65 static int ehci_platform_probe(struct platform_device *dev)
66 {
67 	struct usb_hcd *hcd;
68 	struct resource *res_mem;
69 	struct usb_ehci_pdata *pdata = dev->dev.platform_data;
70 	int irq;
71 	int err = -ENOMEM;
72 
73 	if (!pdata) {
74 		WARN_ON(1);
75 		return -ENODEV;
76 	}
77 
78 	if (usb_disabled())
79 		return -ENODEV;
80 
81 	irq = platform_get_irq(dev, 0);
82 	if (irq < 0) {
83 		dev_err(&dev->dev, "no irq provided");
84 		return irq;
85 	}
86 	res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
87 	if (!res_mem) {
88 		dev_err(&dev->dev, "no memory resource provided");
89 		return -ENXIO;
90 	}
91 
92 	if (pdata->power_on) {
93 		err = pdata->power_on(dev);
94 		if (err < 0)
95 			return err;
96 	}
97 
98 	hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
99 			     dev_name(&dev->dev));
100 	if (!hcd) {
101 		err = -ENOMEM;
102 		goto err_power;
103 	}
104 
105 	hcd->rsrc_start = res_mem->start;
106 	hcd->rsrc_len = resource_size(res_mem);
107 
108 	hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
109 	if (IS_ERR(hcd->regs)) {
110 		err = PTR_ERR(hcd->regs);
111 		goto err_put_hcd;
112 	}
113 	err = usb_add_hcd(hcd, irq, IRQF_SHARED);
114 	if (err)
115 		goto err_put_hcd;
116 
117 	platform_set_drvdata(dev, hcd);
118 
119 	return err;
120 
121 err_put_hcd:
122 	usb_put_hcd(hcd);
123 err_power:
124 	if (pdata->power_off)
125 		pdata->power_off(dev);
126 
127 	return err;
128 }
129 
130 static int ehci_platform_remove(struct platform_device *dev)
131 {
132 	struct usb_hcd *hcd = platform_get_drvdata(dev);
133 	struct usb_ehci_pdata *pdata = dev->dev.platform_data;
134 
135 	usb_remove_hcd(hcd);
136 	usb_put_hcd(hcd);
137 	platform_set_drvdata(dev, NULL);
138 
139 	if (pdata->power_off)
140 		pdata->power_off(dev);
141 
142 	return 0;
143 }
144 
145 #ifdef CONFIG_PM
146 
147 static int ehci_platform_suspend(struct device *dev)
148 {
149 	struct usb_hcd *hcd = dev_get_drvdata(dev);
150 	struct usb_ehci_pdata *pdata = dev->platform_data;
151 	struct platform_device *pdev =
152 		container_of(dev, struct platform_device, dev);
153 	bool do_wakeup = device_may_wakeup(dev);
154 	int ret;
155 
156 	ret = ehci_suspend(hcd, do_wakeup);
157 
158 	if (pdata->power_suspend)
159 		pdata->power_suspend(pdev);
160 
161 	return ret;
162 }
163 
164 static int ehci_platform_resume(struct device *dev)
165 {
166 	struct usb_hcd *hcd = dev_get_drvdata(dev);
167 	struct usb_ehci_pdata *pdata = dev->platform_data;
168 	struct platform_device *pdev =
169 		container_of(dev, struct platform_device, dev);
170 
171 	if (pdata->power_on) {
172 		int err = pdata->power_on(pdev);
173 		if (err < 0)
174 			return err;
175 	}
176 
177 	ehci_resume(hcd, false);
178 	return 0;
179 }
180 
181 #else /* !CONFIG_PM */
182 #define ehci_platform_suspend	NULL
183 #define ehci_platform_resume	NULL
184 #endif /* CONFIG_PM */
185 
186 static const struct platform_device_id ehci_platform_table[] = {
187 	{ "ehci-platform", 0 },
188 	{ }
189 };
190 MODULE_DEVICE_TABLE(platform, ehci_platform_table);
191 
192 static const struct dev_pm_ops ehci_platform_pm_ops = {
193 	.suspend	= ehci_platform_suspend,
194 	.resume		= ehci_platform_resume,
195 };
196 
197 static struct platform_driver ehci_platform_driver = {
198 	.id_table	= ehci_platform_table,
199 	.probe		= ehci_platform_probe,
200 	.remove		= ehci_platform_remove,
201 	.shutdown	= usb_hcd_platform_shutdown,
202 	.driver		= {
203 		.owner	= THIS_MODULE,
204 		.name	= "ehci-platform",
205 		.pm	= &ehci_platform_pm_ops,
206 	}
207 };
208 
209 static int __init ehci_platform_init(void)
210 {
211 	if (usb_disabled())
212 		return -ENODEV;
213 
214 	pr_info("%s: " DRIVER_DESC "\n", hcd_name);
215 
216 	ehci_init_driver(&ehci_platform_hc_driver, &platform_overrides);
217 	return platform_driver_register(&ehci_platform_driver);
218 }
219 module_init(ehci_platform_init);
220 
221 static void __exit ehci_platform_cleanup(void)
222 {
223 	platform_driver_unregister(&ehci_platform_driver);
224 }
225 module_exit(ehci_platform_cleanup);
226 
227 MODULE_DESCRIPTION(DRIVER_DESC);
228 MODULE_AUTHOR("Hauke Mehrtens");
229 MODULE_AUTHOR("Alan Stern");
230 MODULE_LICENSE("GPL");
231