aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/host/Kconfig13
-rw-r--r--drivers/usb/host/ohci-hcd.c21
-rw-r--r--drivers/usb/host/ohci-ssb.c247
3 files changed, 280 insertions, 1 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 565d6ef4c4cf..c978d622fa8a 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -154,6 +154,19 @@ config USB_OHCI_HCD_PCI
154 Enables support for PCI-bus plug-in USB controller cards. 154 Enables support for PCI-bus plug-in USB controller cards.
155 If unsure, say Y. 155 If unsure, say Y.
156 156
157config USB_OHCI_HCD_SSB
158 bool "OHCI support for Broadcom SSB OHCI core"
159 depends on USB_OHCI_HCD && SSB && EXPERIMENTAL
160 default n
161 ---help---
162 Support for the Sonics Silicon Backplane (SSB) attached
163 Broadcom USB OHCI core.
164
165 This device is present in some embedded devices with
166 Broadcom based SSB bus.
167
168 If unsure, say N.
169
157config USB_OHCI_BIG_ENDIAN_DESC 170config USB_OHCI_BIG_ENDIAN_DESC
158 bool 171 bool
159 depends on USB_OHCI_HCD 172 depends on USB_OHCI_HCD
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index f7c6ced2bc03..240c7f507541 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1033,11 +1033,17 @@ MODULE_LICENSE ("GPL");
1033#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver 1033#define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver
1034#endif 1034#endif
1035 1035
1036#ifdef CONFIG_USB_OHCI_HCD_SSB
1037#include "ohci-ssb.c"
1038#define SSB_OHCI_DRIVER ssb_ohci_driver
1039#endif
1040
1036#if !defined(PCI_DRIVER) && \ 1041#if !defined(PCI_DRIVER) && \
1037 !defined(PLATFORM_DRIVER) && \ 1042 !defined(PLATFORM_DRIVER) && \
1038 !defined(OF_PLATFORM_DRIVER) && \ 1043 !defined(OF_PLATFORM_DRIVER) && \
1039 !defined(SA1111_DRIVER) && \ 1044 !defined(SA1111_DRIVER) && \
1040 !defined(PS3_SYSTEM_BUS_DRIVER) 1045 !defined(PS3_SYSTEM_BUS_DRIVER) && \
1046 !defined(SSB_OHCI_DRIVER)
1041#error "missing bus glue for ohci-hcd" 1047#error "missing bus glue for ohci-hcd"
1042#endif 1048#endif
1043 1049
@@ -1082,10 +1088,20 @@ static int __init ohci_hcd_mod_init(void)
1082 goto error_pci; 1088 goto error_pci;
1083#endif 1089#endif
1084 1090
1091#ifdef SSB_OHCI_DRIVER
1092 retval = ssb_driver_register(&SSB_OHCI_DRIVER);
1093 if (retval)
1094 goto error_ssb;
1095#endif
1096
1085 return retval; 1097 return retval;
1086 1098
1087 /* Error path */ 1099 /* Error path */
1100#ifdef SSB_OHCI_DRIVER
1101 error_ssb:
1102#endif
1088#ifdef PCI_DRIVER 1103#ifdef PCI_DRIVER
1104 pci_unregister_driver(&PCI_DRIVER);
1089 error_pci: 1105 error_pci:
1090#endif 1106#endif
1091#ifdef SA1111_DRIVER 1107#ifdef SA1111_DRIVER
@@ -1110,6 +1126,9 @@ module_init(ohci_hcd_mod_init);
1110 1126
1111static void __exit ohci_hcd_mod_exit(void) 1127static void __exit ohci_hcd_mod_exit(void)
1112{ 1128{
1129#ifdef SSB_OHCI_DRIVER
1130 ssb_driver_unregister(&SSB_OHCI_DRIVER);
1131#endif
1113#ifdef PCI_DRIVER 1132#ifdef PCI_DRIVER
1114 pci_unregister_driver(&PCI_DRIVER); 1133 pci_unregister_driver(&PCI_DRIVER);
1115#endif 1134#endif
diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c
new file mode 100644
index 000000000000..bc3e785d8c00
--- /dev/null
+++ b/drivers/usb/host/ohci-ssb.c
@@ -0,0 +1,247 @@
1/*
2 * Sonics Silicon Backplane
3 * Broadcom USB-core OHCI driver
4 *
5 * Copyright 2007 Michael Buesch <mb@bu3sch.de>
6 *
7 * Derived from the OHCI-PCI driver
8 * Copyright 1999 Roman Weissgaerber
9 * Copyright 2000-2002 David Brownell
10 * Copyright 1999 Linus Torvalds
11 * Copyright 1999 Gregory P. Smith
12 *
13 * Derived from the USBcore related parts of Broadcom-SB
14 * Copyright 2005 Broadcom Corporation
15 *
16 * Licensed under the GNU/GPL. See COPYING for details.
17 */
18#include <linux/ssb/ssb.h>
19
20
21#define SSB_OHCI_TMSLOW_HOSTMODE (1 << 29)
22
23struct ssb_ohci_device {
24 struct ohci_hcd ohci; /* _must_ be at the beginning. */
25
26 u32 enable_flags;
27};
28
29static inline
30struct ssb_ohci_device *hcd_to_ssb_ohci(struct usb_hcd *hcd)
31{
32 return (struct ssb_ohci_device *)(hcd->hcd_priv);
33}
34
35
36static int ssb_ohci_reset(struct usb_hcd *hcd)
37{
38 struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
39 struct ohci_hcd *ohci = &ohcidev->ohci;
40 int err;
41
42 ohci_hcd_init(ohci);
43 err = ohci_init(ohci);
44
45 return err;
46}
47
48static int ssb_ohci_start(struct usb_hcd *hcd)
49{
50 struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
51 struct ohci_hcd *ohci = &ohcidev->ohci;
52 int err;
53
54 err = ohci_run(ohci);
55 if (err < 0) {
56 ohci_err(ohci, "can't start\n");
57 ohci_stop(hcd);
58 }
59
60 return err;
61}
62
63#ifdef CONFIG_PM
64static int ssb_ohci_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
65{
66 struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
67 struct ohci_hcd *ohci = &ohcidev->ohci;
68 unsigned long flags;
69
70 spin_lock_irqsave(&ohci->lock, flags);
71
72 ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
73 ohci_readl(ohci, &ohci->regs->intrdisable); /* commit write */
74
75 /* make sure snapshot being resumed re-enumerates everything */
76 if (message.event == PM_EVENT_PRETHAW)
77 ohci_usb_reset(ohci);
78
79 clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
80
81 spin_unlock_irqrestore(&ohci->lock, flags);
82 return 0;
83}
84
85static int ssb_ohci_hcd_resume(struct usb_hcd *hcd)
86{
87 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
88 usb_hcd_resume_root_hub(hcd);
89 return 0;
90}
91#endif /* CONFIG_PM */
92
93static const struct hc_driver ssb_ohci_hc_driver = {
94 .description = "ssb-usb-ohci",
95 .product_desc = "SSB OHCI Controller",
96 .hcd_priv_size = sizeof(struct ssb_ohci_device),
97
98 .irq = ohci_irq,
99 .flags = HCD_MEMORY | HCD_USB11,
100
101 .reset = ssb_ohci_reset,
102 .start = ssb_ohci_start,
103 .stop = ohci_stop,
104 .shutdown = ohci_shutdown,
105
106#ifdef CONFIG_PM
107 .suspend = ssb_ohci_hcd_suspend,
108 .resume = ssb_ohci_hcd_resume,
109#endif
110
111 .urb_enqueue = ohci_urb_enqueue,
112 .urb_dequeue = ohci_urb_dequeue,
113 .endpoint_disable = ohci_endpoint_disable,
114
115 .get_frame_number = ohci_get_frame,
116
117 .hub_status_data = ohci_hub_status_data,
118 .hub_control = ohci_hub_control,
119 .hub_irq_enable = ohci_rhsc_enable,
120 .bus_suspend = ohci_bus_suspend,
121 .bus_resume = ohci_bus_resume,
122
123 .start_port_reset = ohci_start_port_reset,
124};
125
126static void ssb_ohci_detach(struct ssb_device *dev)
127{
128 struct usb_hcd *hcd = ssb_get_drvdata(dev);
129
130 usb_remove_hcd(hcd);
131 iounmap(hcd->regs);
132 usb_put_hcd(hcd);
133 ssb_device_disable(dev, 0);
134}
135
136static int ssb_ohci_attach(struct ssb_device *dev)
137{
138 struct ssb_ohci_device *ohcidev;
139 struct usb_hcd *hcd;
140 int err = -ENOMEM;
141 u32 tmp, flags = 0;
142
143 if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
144 flags |= SSB_OHCI_TMSLOW_HOSTMODE;
145
146 ssb_device_enable(dev, flags);
147
148 hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
149 dev->dev->bus_id);
150 if (!hcd)
151 goto err_dev_disable;
152 ohcidev = hcd_to_ssb_ohci(hcd);
153 ohcidev->enable_flags = flags;
154
155 tmp = ssb_read32(dev, SSB_ADMATCH0);
156 hcd->rsrc_start = ssb_admatch_base(tmp);
157 hcd->rsrc_len = ssb_admatch_size(tmp);
158 hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
159 if (!hcd->regs)
160 goto err_put_hcd;
161 err = usb_add_hcd(hcd, dev->irq, IRQF_SHARED);
162 if (err)
163 goto err_iounmap;
164
165 ssb_set_drvdata(dev, hcd);
166
167 return err;
168
169err_iounmap:
170 iounmap(hcd->regs);
171err_put_hcd:
172 usb_put_hcd(hcd);
173err_dev_disable:
174 ssb_device_disable(dev, flags);
175 return err;
176}
177
178static int ssb_ohci_probe(struct ssb_device *dev,
179 const struct ssb_device_id *id)
180{
181 int err;
182 u16 chipid_top;
183
184 /* USBcores are only connected on embedded devices. */
185 chipid_top = (dev->bus->chip_id & 0xFF00);
186 if (chipid_top != 0x4700 && chipid_top != 0x5300)
187 return -ENODEV;
188
189 /* TODO: Probably need checks here; is the core connected? */
190
191 if (usb_disabled())
192 return -ENODEV;
193
194 /* We currently always attach SSB_DEV_USB11_HOSTDEV
195 * as HOST OHCI. If we want to attach it as Client device,
196 * we must branch here and call into the (yet to
197 * be written) Client mode driver. Same for remove(). */
198
199 err = ssb_ohci_attach(dev);
200
201 return err;
202}
203
204static void ssb_ohci_remove(struct ssb_device *dev)
205{
206 ssb_ohci_detach(dev);
207}
208
209#ifdef CONFIG_PM
210
211static int ssb_ohci_suspend(struct ssb_device *dev, pm_message_t state)
212{
213 ssb_device_disable(dev, 0);
214
215 return 0;
216}
217
218static int ssb_ohci_resume(struct ssb_device *dev)
219{
220 struct usb_hcd *hcd = ssb_get_drvdata(dev);
221 struct ssb_ohci_device *ohcidev = hcd_to_ssb_ohci(hcd);
222
223 ssb_device_enable(dev, ohcidev->enable_flags);
224
225 return 0;
226}
227
228#else /* !CONFIG_PM */
229#define ssb_ohci_suspend NULL
230#define ssb_ohci_resume NULL
231#endif /* CONFIG_PM */
232
233static const struct ssb_device_id ssb_ohci_table[] = {
234 SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
235 SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
236 SSB_DEVTABLE_END
237};
238MODULE_DEVICE_TABLE(ssb, ssb_ohci_table);
239
240static struct ssb_driver ssb_ohci_driver = {
241 .name = KBUILD_MODNAME,
242 .id_table = ssb_ohci_table,
243 .probe = ssb_ohci_probe,
244 .remove = ssb_ohci_remove,
245 .suspend = ssb_ohci_suspend,
246 .resume = ssb_ohci_resume,
247};