aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHauke Mehrtens <hauke@hauke-m.de>2012-03-12 20:04:48 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-03-15 15:42:07 -0400
commit7a7a4a592f42d9abf3b6cc40620b3f79fef49246 (patch)
treeb51cd6655476657d11127d166b9036b1e73e84de
parentfa3364b5a2d79b0c94a912b371c92bd3d06bc8fb (diff)
USB: EHCI: Add a generic platform device driver
This adds a generic driver for platform devices. It works like the PCI driver and is based on it. This is for devices which do not have an own bus but their EHCI controller works like a PCI controller. It will be used for the Broadcom bcma and ssb USB EHCI controller. Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/Kconfig10
-rw-r--r--drivers/usb/host/ehci-hcd.c5
-rw-r--r--drivers/usb/host/ehci-platform.c198
-rw-r--r--include/linux/usb/ehci_pdriver.h46
4 files changed, 259 insertions, 0 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 1dd6e13077a4..cbd3df8477ba 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -407,6 +407,16 @@ config USB_OHCI_HCD_PLATFORM
407 407
408 If unsure, say N. 408 If unsure, say N.
409 409
410config USB_EHCI_HCD_PLATFORM
411 bool "Generic EHCI driver for a platform device"
412 depends on USB_EHCI_HCD && EXPERIMENTAL
413 default n
414 ---help---
415 Adds an EHCI host driver for a generic platform device, which
416 provieds a memory space and an irq.
417
418 If unsure, say N.
419
410config USB_OHCI_BIG_ENDIAN_DESC 420config USB_OHCI_BIG_ENDIAN_DESC
411 bool 421 bool
412 depends on USB_OHCI_HCD 422 depends on USB_OHCI_HCD
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 06e2548b549c..c97b2b6378d6 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1376,6 +1376,11 @@ MODULE_LICENSE ("GPL");
1376#define PLATFORM_DRIVER ehci_ls1x_driver 1376#define PLATFORM_DRIVER ehci_ls1x_driver
1377#endif 1377#endif
1378 1378
1379#ifdef CONFIG_USB_EHCI_HCD_PLATFORM
1380#include "ehci-platform.c"
1381#define PLATFORM_DRIVER ehci_platform_driver
1382#endif
1383
1379#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ 1384#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
1380 !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ 1385 !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
1381 !defined(XILINX_OF_PLATFORM_DRIVER) 1386 !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
new file mode 100644
index 000000000000..d238b4e24bb6
--- /dev/null
+++ b/drivers/usb/host/ehci-platform.c
@@ -0,0 +1,198 @@
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/platform_device.h>
22#include <linux/usb/ehci_pdriver.h>
23
24static int ehci_platform_reset(struct usb_hcd *hcd)
25{
26 struct platform_device *pdev = to_platform_device(hcd->self.controller);
27 struct usb_ehci_pdata *pdata = pdev->dev.platform_data;
28 struct ehci_hcd *ehci = hcd_to_ehci(hcd);
29 int retval;
30
31 hcd->has_tt = pdata->has_tt;
32 ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
33 ehci->big_endian_desc = pdata->big_endian_desc;
34 ehci->big_endian_mmio = pdata->big_endian_mmio;
35
36 ehci->caps = hcd->regs + pdata->caps_offset;
37 retval = ehci_setup(hcd);
38 if (retval)
39 return retval;
40
41 if (pdata->port_power_on)
42 ehci_port_power(ehci, 1);
43 if (pdata->port_power_off)
44 ehci_port_power(ehci, 0);
45
46 return 0;
47}
48
49static const struct hc_driver ehci_platform_hc_driver = {
50 .description = hcd_name,
51 .product_desc = "Generic Platform EHCI Controller",
52 .hcd_priv_size = sizeof(struct ehci_hcd),
53
54 .irq = ehci_irq,
55 .flags = HCD_MEMORY | HCD_USB2,
56
57 .reset = ehci_platform_reset,
58 .start = ehci_run,
59 .stop = ehci_stop,
60 .shutdown = ehci_shutdown,
61
62 .urb_enqueue = ehci_urb_enqueue,
63 .urb_dequeue = ehci_urb_dequeue,
64 .endpoint_disable = ehci_endpoint_disable,
65 .endpoint_reset = ehci_endpoint_reset,
66
67 .get_frame_number = ehci_get_frame,
68
69 .hub_status_data = ehci_hub_status_data,
70 .hub_control = ehci_hub_control,
71#if defined(CONFIG_PM)
72 .bus_suspend = ehci_bus_suspend,
73 .bus_resume = ehci_bus_resume,
74#endif
75 .relinquish_port = ehci_relinquish_port,
76 .port_handed_over = ehci_port_handed_over,
77
78 .update_device = ehci_update_device,
79
80 .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
81};
82
83static int __devinit ehci_platform_probe(struct platform_device *dev)
84{
85 struct usb_hcd *hcd;
86 struct resource *res_mem;
87 int irq;
88 int err = -ENOMEM;
89
90 BUG_ON(!dev->dev.platform_data);
91
92 if (usb_disabled())
93 return -ENODEV;
94
95 irq = platform_get_irq(dev, 0);
96 if (irq < 0) {
97 pr_err("no irq provieded");
98 return irq;
99 }
100 res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
101 if (!res_mem) {
102 pr_err("no memory recourse provieded");
103 return -ENXIO;
104 }
105
106 hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
107 dev_name(&dev->dev));
108 if (!hcd)
109 return -ENOMEM;
110
111 hcd->rsrc_start = res_mem->start;
112 hcd->rsrc_len = resource_size(res_mem);
113
114 if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
115 pr_err("controller already in use");
116 err = -EBUSY;
117 goto err_put_hcd;
118 }
119
120 hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
121 if (!hcd->regs)
122 goto err_release_region;
123 err = usb_add_hcd(hcd, irq, IRQF_SHARED);
124 if (err)
125 goto err_iounmap;
126
127 platform_set_drvdata(dev, hcd);
128
129 return err;
130
131err_iounmap:
132 iounmap(hcd->regs);
133err_release_region:
134 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
135err_put_hcd:
136 usb_put_hcd(hcd);
137 return err;
138}
139
140static int __devexit ehci_platform_remove(struct platform_device *dev)
141{
142 struct usb_hcd *hcd = platform_get_drvdata(dev);
143
144 usb_remove_hcd(hcd);
145 iounmap(hcd->regs);
146 release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
147 usb_put_hcd(hcd);
148 platform_set_drvdata(dev, NULL);
149
150 return 0;
151}
152
153#ifdef CONFIG_PM
154
155static int ehci_platform_suspend(struct device *dev)
156{
157 struct usb_hcd *hcd = dev_get_drvdata(dev);
158 bool wakeup = device_may_wakeup(dev);
159
160 ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), wakeup);
161 return 0;
162}
163
164static int ehci_platform_resume(struct device *dev)
165{
166 struct usb_hcd *hcd = dev_get_drvdata(dev);
167
168 ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd));
169 return 0;
170}
171
172#else /* !CONFIG_PM */
173#define ehci_platform_suspend NULL
174#define ehci_platform_resume NULL
175#endif /* CONFIG_PM */
176
177static const struct platform_device_id ehci_platform_table[] = {
178 { "ehci-platform", 0 },
179 { }
180};
181MODULE_DEVICE_TABLE(platform, ehci_platform_table);
182
183static const struct dev_pm_ops ehci_platform_pm_ops = {
184 .suspend = ehci_platform_suspend,
185 .resume = ehci_platform_resume,
186};
187
188static struct platform_driver ehci_platform_driver = {
189 .id_table = ehci_platform_table,
190 .probe = ehci_platform_probe,
191 .remove = __devexit_p(ehci_platform_remove),
192 .shutdown = usb_hcd_platform_shutdown,
193 .driver = {
194 .owner = THIS_MODULE,
195 .name = "ehci-platform",
196 .pm = &ehci_platform_pm_ops,
197 }
198};
diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h
new file mode 100644
index 000000000000..1894f42fe3f7
--- /dev/null
+++ b/include/linux/usb/ehci_pdriver.h
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2012 Hauke Mehrtens <hauke@hauke-m.de>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
19#ifndef __USB_CORE_EHCI_PDRIVER_H
20#define __USB_CORE_EHCI_PDRIVER_H
21
22/**
23 * struct usb_ehci_pdata - platform_data for generic ehci driver
24 *
25 * @caps_offset: offset of the EHCI Capability Registers to the start of
26 * the io memory region provided to the driver.
27 * @has_tt: set to 1 if TT is integrated in root hub.
28 * @port_power_on: set to 1 if the controller needs a power up after
29 * initialization.
30 * @port_power_off: set to 1 if the controller needs to be powered down
31 * after initialization.
32 *
33 * These are general configuration options for the EHCI controller. All of
34 * these options are activating more or less workarounds for some hardware.
35 */
36struct usb_ehci_pdata {
37 int caps_offset;
38 unsigned has_tt:1;
39 unsigned has_synopsys_hc_bug:1;
40 unsigned big_endian_desc:1;
41 unsigned big_endian_mmio:1;
42 unsigned port_power_on:1;
43 unsigned port_power_off:1;
44};
45
46#endif /* __USB_CORE_EHCI_PDRIVER_H */