aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/bcma-hcd.c
diff options
context:
space:
mode:
authorHauke Mehrtens <hauke@hauke-m.de>2012-03-15 18:49:57 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-04-18 16:43:30 -0400
commit62e11d1bada33102e8827c8d0cca95c020cf5467 (patch)
tree77a9af6b852fc966d7f173834a82db204ec45931 /drivers/usb/host/bcma-hcd.c
parente167d9fbb881c030f93563fd364c8a0b8c5cd6d3 (diff)
USB: Add driver for the bcma bus
This adds a USB driver using the generic platform device driver for the USB controller found on the Broadcom bcma bus. The bcma 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 bcma bus at the same time. Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/bcma-hcd.c')
-rw-r--r--drivers/usb/host/bcma-hcd.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c
new file mode 100644
index 000000000000..afec047e4f94
--- /dev/null
+++ b/drivers/usb/host/bcma-hcd.c
@@ -0,0 +1,334 @@
1/*
2 * Broadcom specific Advanced Microcontroller Bus
3 * Broadcom USB-core driver (BCMA 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/bcma/bcma.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 BCMA Bus");
30MODULE_LICENSE("GPL");
31
32struct bcma_hcd_device {
33 struct platform_device *ehci_dev;
34 struct platform_device *ohci_dev;
35};
36
37/* Wait for bitmask in a register to get set or cleared.
38 * timeout is in units of ten-microseconds.
39 */
40static int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask,
41 int timeout)
42{
43 int i;
44 u32 val;
45
46 for (i = 0; i < timeout; i++) {
47 val = bcma_read32(dev, reg);
48 if ((val & bitmask) == bitmask)
49 return 0;
50 udelay(10);
51 }
52
53 return -ETIMEDOUT;
54}
55
56static void __devinit bcma_hcd_4716wa(struct bcma_device *dev)
57{
58#ifdef CONFIG_BCMA_DRIVER_MIPS
59 /* Work around for 4716 failures. */
60 if (dev->bus->chipinfo.id == 0x4716) {
61 u32 tmp;
62
63 tmp = bcma_cpu_clock(&dev->bus->drv_mips);
64 if (tmp >= 480000000)
65 tmp = 0x1846b; /* set CDR to 0x11(fast) */
66 else if (tmp == 453000000)
67 tmp = 0x1046b; /* set CDR to 0x10(slow) */
68 else
69 tmp = 0;
70
71 /* Change Shim mdio control reg to fix host not acking at
72 * high frequencies
73 */
74 if (tmp) {
75 bcma_write32(dev, 0x524, 0x1); /* write sel to enable */
76 udelay(500);
77
78 bcma_write32(dev, 0x524, tmp);
79 udelay(500);
80 bcma_write32(dev, 0x524, 0x4ab);
81 udelay(500);
82 bcma_read32(dev, 0x528);
83 bcma_write32(dev, 0x528, 0x80000000);
84 }
85 }
86#endif /* CONFIG_BCMA_DRIVER_MIPS */
87}
88
89/* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */
90static void __devinit bcma_hcd_init_chip(struct bcma_device *dev)
91{
92 u32 tmp;
93
94 /*
95 * USB 2.0 special considerations:
96 *
97 * 1. Since the core supports both OHCI and EHCI functions, it must
98 * only be reset once.
99 *
100 * 2. In addition to the standard SI reset sequence, the Host Control
101 * Register must be programmed to bring the USB core and various
102 * phy components out of reset.
103 */
104 if (!bcma_core_is_enabled(dev)) {
105 bcma_core_enable(dev, 0);
106 mdelay(10);
107 if (dev->id.rev >= 5) {
108 /* Enable Misc PLL */
109 tmp = bcma_read32(dev, 0x1e0);
110 tmp |= 0x100;
111 bcma_write32(dev, 0x1e0, tmp);
112 if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100))
113 printk(KERN_EMERG "Failed to enable misc PPL!\n");
114
115 /* Take out of resets */
116 bcma_write32(dev, 0x200, 0x4ff);
117 udelay(25);
118 bcma_write32(dev, 0x200, 0x6ff);
119 udelay(25);
120
121 /* Make sure digital and AFE are locked in USB PHY */
122 bcma_write32(dev, 0x524, 0x6b);
123 udelay(50);
124 tmp = bcma_read32(dev, 0x524);
125 udelay(50);
126 bcma_write32(dev, 0x524, 0xab);
127 udelay(50);
128 tmp = bcma_read32(dev, 0x524);
129 udelay(50);
130 bcma_write32(dev, 0x524, 0x2b);
131 udelay(50);
132 tmp = bcma_read32(dev, 0x524);
133 udelay(50);
134 bcma_write32(dev, 0x524, 0x10ab);
135 udelay(50);
136 tmp = bcma_read32(dev, 0x524);
137
138 if (bcma_wait_bits(dev, 0x528, 0xc000, 10000)) {
139 tmp = bcma_read32(dev, 0x528);
140 printk(KERN_EMERG
141 "USB20H mdio_rddata 0x%08x\n", tmp);
142 }
143 bcma_write32(dev, 0x528, 0x80000000);
144 tmp = bcma_read32(dev, 0x314);
145 udelay(265);
146 bcma_write32(dev, 0x200, 0x7ff);
147 udelay(10);
148
149 /* Take USB and HSIC out of non-driving modes */
150 bcma_write32(dev, 0x510, 0);
151 } else {
152 bcma_write32(dev, 0x200, 0x7ff);
153
154 udelay(1);
155 }
156
157 bcma_hcd_4716wa(dev);
158 }
159}
160
161static const struct usb_ehci_pdata ehci_pdata = {
162};
163
164static const struct usb_ohci_pdata ohci_pdata = {
165};
166
167static struct platform_device * __devinit
168bcma_hcd_create_pdev(struct bcma_device *dev, bool ohci, u32 addr)
169{
170 struct platform_device *hci_dev;
171 struct resource hci_res[2];
172 int ret = -ENOMEM;
173
174 memset(hci_res, 0, sizeof(hci_res));
175
176 hci_res[0].start = addr;
177 hci_res[0].end = hci_res[0].start + 0x1000 - 1;
178 hci_res[0].flags = IORESOURCE_MEM;
179
180 hci_res[1].start = dev->irq;
181 hci_res[1].flags = IORESOURCE_IRQ;
182
183 hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
184 "ehci-platform" , 0);
185 if (!hci_dev)
186 return NULL;
187
188 hci_dev->dev.parent = &dev->dev;
189 hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
190
191 ret = platform_device_add_resources(hci_dev, hci_res,
192 ARRAY_SIZE(hci_res));
193 if (ret)
194 goto err_alloc;
195 if (ohci)
196 ret = platform_device_add_data(hci_dev, &ohci_pdata,
197 sizeof(ohci_pdata));
198 else
199 ret = platform_device_add_data(hci_dev, &ehci_pdata,
200 sizeof(ehci_pdata));
201 if (ret)
202 goto err_alloc;
203 ret = platform_device_add(hci_dev);
204 if (ret)
205 goto err_alloc;
206
207 return hci_dev;
208
209err_alloc:
210 platform_device_put(hci_dev);
211 return ERR_PTR(ret);
212}
213
214static int __devinit bcma_hcd_probe(struct bcma_device *dev)
215{
216 int err;
217 u16 chipid_top;
218 u32 ohci_addr;
219 struct bcma_hcd_device *usb_dev;
220 struct bcma_chipinfo *chipinfo;
221
222 chipinfo = &dev->bus->chipinfo;
223 /* USBcores are only connected on embedded devices. */
224 chipid_top = (chipinfo->id & 0xFF00);
225 if (chipid_top != 0x4700 && chipid_top != 0x5300)
226 return -ENODEV;
227
228 /* TODO: Probably need checks here; is the core connected? */
229
230 if (dma_set_mask(dev->dma_dev, DMA_BIT_MASK(32)) ||
231 dma_set_coherent_mask(dev->dma_dev, DMA_BIT_MASK(32)))
232 return -EOPNOTSUPP;
233
234 usb_dev = kzalloc(sizeof(struct bcma_hcd_device), GFP_KERNEL);
235 if (!usb_dev)
236 return -ENOMEM;
237
238 bcma_hcd_init_chip(dev);
239
240 /* In AI chips EHCI is addrspace 0, OHCI is 1 */
241 ohci_addr = dev->addr1;
242 if ((chipinfo->id == 0x5357 || chipinfo->id == 0x4749)
243 && chipinfo->rev == 0)
244 ohci_addr = 0x18009000;
245
246 usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, true, ohci_addr);
247 if (IS_ERR(usb_dev->ohci_dev)) {
248 err = PTR_ERR(usb_dev->ohci_dev);
249 goto err_free_usb_dev;
250 }
251
252 usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, false, dev->addr);
253 if (IS_ERR(usb_dev->ehci_dev)) {
254 err = PTR_ERR(usb_dev->ehci_dev);
255 goto err_unregister_ohci_dev;
256 }
257
258 bcma_set_drvdata(dev, usb_dev);
259 return 0;
260
261err_unregister_ohci_dev:
262 platform_device_unregister(usb_dev->ohci_dev);
263err_free_usb_dev:
264 kfree(usb_dev);
265 return err;
266}
267
268static void __devexit bcma_hcd_remove(struct bcma_device *dev)
269{
270 struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev);
271 struct platform_device *ohci_dev = usb_dev->ohci_dev;
272 struct platform_device *ehci_dev = usb_dev->ehci_dev;
273
274 if (ohci_dev)
275 platform_device_unregister(ohci_dev);
276 if (ehci_dev)
277 platform_device_unregister(ehci_dev);
278
279 bcma_core_disable(dev, 0);
280}
281
282static void bcma_hcd_shutdown(struct bcma_device *dev)
283{
284 bcma_core_disable(dev, 0);
285}
286
287#ifdef CONFIG_PM
288
289static int bcma_hcd_suspend(struct bcma_device *dev, pm_message_t state)
290{
291 bcma_core_disable(dev, 0);
292
293 return 0;
294}
295
296static int bcma_hcd_resume(struct bcma_device *dev)
297{
298 bcma_core_enable(dev, 0);
299
300 return 0;
301}
302
303#else /* !CONFIG_PM */
304#define bcma_hcd_suspend NULL
305#define bcma_hcd_resume NULL
306#endif /* CONFIG_PM */
307
308static const struct bcma_device_id bcma_hcd_table[] __devinitconst = {
309 BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS),
310 BCMA_CORETABLE_END
311};
312MODULE_DEVICE_TABLE(bcma, bcma_hcd_table);
313
314static struct bcma_driver bcma_hcd_driver = {
315 .name = KBUILD_MODNAME,
316 .id_table = bcma_hcd_table,
317 .probe = bcma_hcd_probe,
318 .remove = __devexit_p(bcma_hcd_remove),
319 .shutdown = bcma_hcd_shutdown,
320 .suspend = bcma_hcd_suspend,
321 .resume = bcma_hcd_resume,
322};
323
324static int __init bcma_hcd_init(void)
325{
326 return bcma_driver_register(&bcma_hcd_driver);
327}
328module_init(bcma_hcd_init);
329
330static void __exit bcma_hcd_exit(void)
331{
332 bcma_driver_unregister(&bcma_hcd_driver);
333}
334module_exit(bcma_hcd_exit);