aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ssb-hcd.c
diff options
context:
space:
mode:
authorHauke Mehrtens <hauke@hauke-m.de>2012-03-15 18:49:58 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-18 16:43:30 -0400
commit7043c2ccf7483f170df4abcd4c95919050ac0760 (patch)
tree2d0c419f0e49cc475a3a63633a4cd061ac1cddbf /drivers/usb/host/ssb-hcd.c
parent62e11d1bada33102e8827c8d0cca95c020cf5467 (diff)
USB: Add driver for the ssb bus
This adds a USB driver using the generic platform device driver for the USB controller found on the Broadcom ssb bus. The ssb bus just exposes one device which serves the OHCI and the EHCI controller at the same time. This driver probes for this USB controller and creates and registers two new platform devices which will be probed by the new generic platform device driver. This makes it possible to use the EHCI and the OCHI controller on the ssb bus at the same time. The old ssb OHCI USB driver will be removed in the next step as this driver also provide an OHCI driver and an EHCI for the cores supporting it. Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/ssb-hcd.c')
-rw-r--r--drivers/usb/host/ssb-hcd.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c
new file mode 100644
index 000000000000..c2e73433b306
--- /dev/null
+++ b/drivers/usb/host/ssb-hcd.c
@@ -0,0 +1,279 @@
1/*
2 * Sonics Silicon Backplane
3 * Broadcom USB-core driver (SSB bus glue)
4 *
5 * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de>
6 *
7 * Based on ssb-ohci driver
8 * Copyright 2007 Michael Buesch <m@bues.ch>
9 *
10 * Derived from the OHCI-PCI driver
11 * Copyright 1999 Roman Weissgaerber
12 * Copyright 2000-2002 David Brownell
13 * Copyright 1999 Linus Torvalds
14 * Copyright 1999 Gregory P. Smith
15 *
16 * Derived from the USBcore related parts of Broadcom-SB
17 * Copyright 2005-2011 Broadcom Corporation
18 *
19 * Licensed under the GNU/GPL. See COPYING for details.
20 */
21#include <linux/ssb/ssb.h>
22#include <linux/delay.h>
23#include <linux/platform_device.h>
24#include <linux/module.h>
25#include <linux/usb/ehci_pdriver.h>
26#include <linux/usb/ohci_pdriver.h>
27
28MODULE_AUTHOR("Hauke Mehrtens");
29MODULE_DESCRIPTION("Common USB driver for SSB Bus");
30MODULE_LICENSE("GPL");
31
32#define SSB_HCD_TMSLOW_HOSTMODE (1 << 29)
33
34struct ssb_hcd_device {
35 struct platform_device *ehci_dev;
36 struct platform_device *ohci_dev;
37
38 u32 enable_flags;
39};
40
41static void __devinit ssb_hcd_5354wa(struct ssb_device *dev)
42{
43#ifdef CONFIG_SSB_DRIVER_MIPS
44 /* Work around for 5354 failures */
45 if (dev->id.revision == 2 && dev->bus->chip_id == 0x5354) {
46 /* Change syn01 reg */
47 ssb_write32(dev, 0x894, 0x00fe00fe);
48
49 /* Change syn03 reg */
50 ssb_write32(dev, 0x89c, ssb_read32(dev, 0x89c) | 0x1);
51 }
52#endif
53}
54
55static void __devinit ssb_hcd_usb20wa(struct ssb_device *dev)
56{
57 if (dev->id.coreid == SSB_DEV_USB20_HOST) {
58 /*
59 * USB 2.0 special considerations:
60 *
61 * In addition to the standard SSB reset sequence, the Host
62 * Control Register must be programmed to bring the USB core
63 * and various phy components out of reset.
64 */
65 ssb_write32(dev, 0x200, 0x7ff);
66
67 /* Change Flush control reg */
68 ssb_write32(dev, 0x400, ssb_read32(dev, 0x400) & ~8);
69 ssb_read32(dev, 0x400);
70
71 /* Change Shim control reg */
72 ssb_write32(dev, 0x304, ssb_read32(dev, 0x304) & ~0x100);
73 ssb_read32(dev, 0x304);
74
75 udelay(1);
76
77 ssb_hcd_5354wa(dev);
78 }
79}
80
81/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
82static u32 __devinit ssb_hcd_init_chip(struct ssb_device *dev)
83{
84 u32 flags = 0;
85
86 if (dev->id.coreid == SSB_DEV_USB11_HOSTDEV)
87 /* Put the device into host-mode. */
88 flags |= SSB_HCD_TMSLOW_HOSTMODE;
89
90 ssb_device_enable(dev, flags);
91
92 ssb_hcd_usb20wa(dev);
93
94 return flags;
95}
96
97static const struct usb_ehci_pdata ehci_pdata = {
98};
99
100static const struct usb_ohci_pdata ohci_pdata = {
101};
102
103static struct platform_device * __devinit
104ssb_hcd_create_pdev(struct ssb_device *dev, bool ohci, u32 addr, u32 len)
105{
106 struct platform_device *hci_dev;
107 struct resource hci_res[2];
108 int ret = -ENOMEM;
109
110 memset(hci_res, 0, sizeof(hci_res));
111
112 hci_res[0].start = addr;
113 hci_res[0].end = hci_res[0].start + len - 1;
114 hci_res[0].flags = IORESOURCE_MEM;
115
116 hci_res[1].start = dev->irq;
117 hci_res[1].flags = IORESOURCE_IRQ;
118
119 hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
120 "ehci-platform" , 0);
121 if (!hci_dev)
122 return NULL;
123
124 hci_dev->dev.parent = dev->dev;
125 hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
126
127 ret = platform_device_add_resources(hci_dev, hci_res,
128 ARRAY_SIZE(hci_res));
129 if (ret)
130 goto err_alloc;
131 if (ohci)
132 ret = platform_device_add_data(hci_dev, &ohci_pdata,
133 sizeof(ohci_pdata));
134 else
135 ret = platform_device_add_data(hci_dev, &ehci_pdata,
136 sizeof(ehci_pdata));
137 if (ret)
138 goto err_alloc;
139 ret = platform_device_add(hci_dev);
140 if (ret)
141 goto err_alloc;
142
143 return hci_dev;
144
145err_alloc:
146 platform_device_put(hci_dev);
147 return ERR_PTR(ret);
148}
149
150static int __devinit ssb_hcd_probe(struct ssb_device *dev,
151 const struct ssb_device_id *id)
152{
153 int err, tmp;
154 int start, len;
155 u16 chipid_top;
156 u16 coreid = dev->id.coreid;
157 struct ssb_hcd_device *usb_dev;
158
159 /* USBcores are only connected on embedded devices. */
160 chipid_top = (dev->bus->chip_id & 0xFF00);
161 if (chipid_top != 0x4700 && chipid_top != 0x5300)
162 return -ENODEV;
163
164 /* TODO: Probably need checks here; is the core connected? */
165
166 if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
167 dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
168 return -EOPNOTSUPP;
169
170 usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL);
171 if (!usb_dev)
172 return -ENOMEM;
173
174 /* We currently always attach SSB_DEV_USB11_HOSTDEV
175 * as HOST OHCI. If we want to attach it as Client device,
176 * we must branch here and call into the (yet to
177 * be written) Client mode driver. Same for remove(). */
178 usb_dev->enable_flags = ssb_hcd_init_chip(dev);
179
180 tmp = ssb_read32(dev, SSB_ADMATCH0);
181
182 start = ssb_admatch_base(tmp);
183 len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp);
184 usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len);
185 if (IS_ERR(usb_dev->ohci_dev)) {
186 err = PTR_ERR(usb_dev->ohci_dev);
187 goto err_free_usb_dev;
188 }
189
190 if (coreid == SSB_DEV_USB20_HOST) {
191 start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */
192 usb_dev->ehci_dev = ssb_hcd_create_pdev(dev, false, start, len);
193 if (IS_ERR(usb_dev->ehci_dev)) {
194 err = PTR_ERR(usb_dev->ehci_dev);
195 goto err_unregister_ohci_dev;
196 }
197 }
198
199 ssb_set_drvdata(dev, usb_dev);
200 return 0;
201
202err_unregister_ohci_dev:
203 platform_device_unregister(usb_dev->ohci_dev);
204err_free_usb_dev:
205 kfree(usb_dev);
206 return err;
207}
208
209static void __devexit ssb_hcd_remove(struct ssb_device *dev)
210{
211 struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
212 struct platform_device *ohci_dev = usb_dev->ohci_dev;
213 struct platform_device *ehci_dev = usb_dev->ehci_dev;
214
215 if (ohci_dev)
216 platform_device_unregister(ohci_dev);
217 if (ehci_dev)
218 platform_device_unregister(ehci_dev);
219
220 ssb_device_disable(dev, 0);
221}
222
223static void __devexit ssb_hcd_shutdown(struct ssb_device *dev)
224{
225 ssb_device_disable(dev, 0);
226}
227
228#ifdef CONFIG_PM
229
230static int ssb_hcd_suspend(struct ssb_device *dev, pm_message_t state)
231{
232 ssb_device_disable(dev, 0);
233
234 return 0;
235}
236
237static int ssb_hcd_resume(struct ssb_device *dev)
238{
239 struct ssb_hcd_device *usb_dev = ssb_get_drvdata(dev);
240
241 ssb_device_enable(dev, usb_dev->enable_flags);
242
243 return 0;
244}
245
246#else /* !CONFIG_PM */
247#define ssb_hcd_suspend NULL
248#define ssb_hcd_resume NULL
249#endif /* CONFIG_PM */
250
251static const struct ssb_device_id ssb_hcd_table[] __devinitconst = {
252 SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOSTDEV, SSB_ANY_REV),
253 SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB11_HOST, SSB_ANY_REV),
254 SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_USB20_HOST, SSB_ANY_REV),
255 SSB_DEVTABLE_END
256};
257MODULE_DEVICE_TABLE(ssb, ssb_hcd_table);
258
259static struct ssb_driver ssb_hcd_driver = {
260 .name = KBUILD_MODNAME,
261 .id_table = ssb_hcd_table,
262 .probe = ssb_hcd_probe,
263 .remove = __devexit_p(ssb_hcd_remove),
264 .shutdown = ssb_hcd_shutdown,
265 .suspend = ssb_hcd_suspend,
266 .resume = ssb_hcd_resume,
267};
268
269static int __init ssb_hcd_init(void)
270{
271 return ssb_driver_register(&ssb_hcd_driver);
272}
273module_init(ssb_hcd_init);
274
275static void __exit ssb_hcd_exit(void)
276{
277 ssb_driver_unregister(&ssb_hcd_driver);
278}
279module_exit(ssb_hcd_exit);