Commit | Line | Data |
---|---|---|
62e11d1b HM |
1 | /* |
2 | * Broadcom specific Advanced Microcontroller Bus | |
3 | * Broadcom USB-core driver (BCMA bus glue) | |
4 | * | |
10bc04b7 HM |
5 | * Copyright 2011-2015 Hauke Mehrtens <hauke@hauke-m.de> |
6 | * Copyright 2015 Felix Fietkau <nbd@openwrt.org> | |
62e11d1b HM |
7 | * |
8 | * Based on ssb-ohci driver | |
9 | * Copyright 2007 Michael Buesch <m@bues.ch> | |
10 | * | |
11 | * Derived from the OHCI-PCI driver | |
12 | * Copyright 1999 Roman Weissgaerber | |
13 | * Copyright 2000-2002 David Brownell | |
14 | * Copyright 1999 Linus Torvalds | |
15 | * Copyright 1999 Gregory P. Smith | |
16 | * | |
17 | * Derived from the USBcore related parts of Broadcom-SB | |
18 | * Copyright 2005-2011 Broadcom Corporation | |
19 | * | |
20 | * Licensed under the GNU/GPL. See COPYING for details. | |
21 | */ | |
22 | #include <linux/bcma/bcma.h> | |
23 | #include <linux/delay.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/module.h> | |
6ba0d809 | 26 | #include <linux/slab.h> |
eb4861c3 HM |
27 | #include <linux/of.h> |
28 | #include <linux/of_gpio.h> | |
62e11d1b HM |
29 | #include <linux/usb/ehci_pdriver.h> |
30 | #include <linux/usb/ohci_pdriver.h> | |
31 | ||
32 | MODULE_AUTHOR("Hauke Mehrtens"); | |
33 | MODULE_DESCRIPTION("Common USB driver for BCMA Bus"); | |
34 | MODULE_LICENSE("GPL"); | |
35 | ||
36 | struct bcma_hcd_device { | |
37 | struct platform_device *ehci_dev; | |
38 | struct platform_device *ohci_dev; | |
39 | }; | |
40 | ||
41 | /* Wait for bitmask in a register to get set or cleared. | |
42 | * timeout is in units of ten-microseconds. | |
43 | */ | |
44 | static int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask, | |
45 | int timeout) | |
46 | { | |
47 | int i; | |
48 | u32 val; | |
49 | ||
50 | for (i = 0; i < timeout; i++) { | |
51 | val = bcma_read32(dev, reg); | |
52 | if ((val & bitmask) == bitmask) | |
53 | return 0; | |
54 | udelay(10); | |
55 | } | |
56 | ||
57 | return -ETIMEDOUT; | |
58 | } | |
59 | ||
41ac7b3a | 60 | static void bcma_hcd_4716wa(struct bcma_device *dev) |
62e11d1b HM |
61 | { |
62 | #ifdef CONFIG_BCMA_DRIVER_MIPS | |
63 | /* Work around for 4716 failures. */ | |
64 | if (dev->bus->chipinfo.id == 0x4716) { | |
65 | u32 tmp; | |
66 | ||
67 | tmp = bcma_cpu_clock(&dev->bus->drv_mips); | |
68 | if (tmp >= 480000000) | |
69 | tmp = 0x1846b; /* set CDR to 0x11(fast) */ | |
70 | else if (tmp == 453000000) | |
71 | tmp = 0x1046b; /* set CDR to 0x10(slow) */ | |
72 | else | |
73 | tmp = 0; | |
74 | ||
75 | /* Change Shim mdio control reg to fix host not acking at | |
76 | * high frequencies | |
77 | */ | |
78 | if (tmp) { | |
79 | bcma_write32(dev, 0x524, 0x1); /* write sel to enable */ | |
80 | udelay(500); | |
81 | ||
82 | bcma_write32(dev, 0x524, tmp); | |
83 | udelay(500); | |
84 | bcma_write32(dev, 0x524, 0x4ab); | |
85 | udelay(500); | |
86 | bcma_read32(dev, 0x528); | |
87 | bcma_write32(dev, 0x528, 0x80000000); | |
88 | } | |
89 | } | |
90 | #endif /* CONFIG_BCMA_DRIVER_MIPS */ | |
91 | } | |
92 | ||
93 | /* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */ | |
10bc04b7 | 94 | static void bcma_hcd_init_chip_mips(struct bcma_device *dev) |
62e11d1b HM |
95 | { |
96 | u32 tmp; | |
97 | ||
98 | /* | |
99 | * USB 2.0 special considerations: | |
100 | * | |
101 | * 1. Since the core supports both OHCI and EHCI functions, it must | |
102 | * only be reset once. | |
103 | * | |
104 | * 2. In addition to the standard SI reset sequence, the Host Control | |
105 | * Register must be programmed to bring the USB core and various | |
106 | * phy components out of reset. | |
107 | */ | |
108 | if (!bcma_core_is_enabled(dev)) { | |
109 | bcma_core_enable(dev, 0); | |
110 | mdelay(10); | |
111 | if (dev->id.rev >= 5) { | |
112 | /* Enable Misc PLL */ | |
113 | tmp = bcma_read32(dev, 0x1e0); | |
114 | tmp |= 0x100; | |
115 | bcma_write32(dev, 0x1e0, tmp); | |
116 | if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100)) | |
117 | printk(KERN_EMERG "Failed to enable misc PPL!\n"); | |
118 | ||
119 | /* Take out of resets */ | |
120 | bcma_write32(dev, 0x200, 0x4ff); | |
121 | udelay(25); | |
122 | bcma_write32(dev, 0x200, 0x6ff); | |
123 | udelay(25); | |
124 | ||
125 | /* Make sure digital and AFE are locked in USB PHY */ | |
126 | bcma_write32(dev, 0x524, 0x6b); | |
127 | udelay(50); | |
128 | tmp = bcma_read32(dev, 0x524); | |
129 | udelay(50); | |
130 | bcma_write32(dev, 0x524, 0xab); | |
131 | udelay(50); | |
132 | tmp = bcma_read32(dev, 0x524); | |
133 | udelay(50); | |
134 | bcma_write32(dev, 0x524, 0x2b); | |
135 | udelay(50); | |
136 | tmp = bcma_read32(dev, 0x524); | |
137 | udelay(50); | |
138 | bcma_write32(dev, 0x524, 0x10ab); | |
139 | udelay(50); | |
140 | tmp = bcma_read32(dev, 0x524); | |
141 | ||
142 | if (bcma_wait_bits(dev, 0x528, 0xc000, 10000)) { | |
143 | tmp = bcma_read32(dev, 0x528); | |
144 | printk(KERN_EMERG | |
145 | "USB20H mdio_rddata 0x%08x\n", tmp); | |
146 | } | |
147 | bcma_write32(dev, 0x528, 0x80000000); | |
148 | tmp = bcma_read32(dev, 0x314); | |
149 | udelay(265); | |
150 | bcma_write32(dev, 0x200, 0x7ff); | |
151 | udelay(10); | |
152 | ||
153 | /* Take USB and HSIC out of non-driving modes */ | |
154 | bcma_write32(dev, 0x510, 0); | |
155 | } else { | |
156 | bcma_write32(dev, 0x200, 0x7ff); | |
157 | ||
158 | udelay(1); | |
159 | } | |
160 | ||
161 | bcma_hcd_4716wa(dev); | |
162 | } | |
163 | } | |
164 | ||
10bc04b7 HM |
165 | static void bcma_hcd_init_chip_arm_phy(struct bcma_device *dev) |
166 | { | |
167 | struct bcma_device *arm_core; | |
168 | void __iomem *dmu; | |
169 | ||
170 | arm_core = bcma_find_core(dev->bus, BCMA_CORE_ARMCA9); | |
171 | if (!arm_core) { | |
172 | dev_err(&dev->dev, "can not find ARM Cortex A9 ihost core\n"); | |
173 | return; | |
174 | } | |
175 | ||
176 | dmu = ioremap_nocache(arm_core->addr_s[0], 0x1000); | |
177 | if (!dmu) { | |
178 | dev_err(&dev->dev, "can not map ARM Cortex A9 ihost core\n"); | |
179 | return; | |
180 | } | |
181 | ||
182 | /* Unlock DMU PLL settings */ | |
183 | iowrite32(0x0000ea68, dmu + 0x180); | |
184 | ||
185 | /* Write USB 2.0 PLL control setting */ | |
186 | iowrite32(0x00dd10c3, dmu + 0x164); | |
187 | ||
188 | /* Lock DMU PLL settings */ | |
189 | iowrite32(0x00000000, dmu + 0x180); | |
190 | ||
191 | iounmap(dmu); | |
192 | } | |
193 | ||
194 | static void bcma_hcd_init_chip_arm_hc(struct bcma_device *dev) | |
195 | { | |
196 | u32 val; | |
197 | ||
198 | /* | |
199 | * Delay after PHY initialized to ensure HC is ready to be configured | |
200 | */ | |
201 | usleep_range(1000, 2000); | |
202 | ||
203 | /* Set packet buffer OUT threshold */ | |
204 | val = bcma_read32(dev, 0x94); | |
205 | val &= 0xffff; | |
206 | val |= 0x80 << 16; | |
207 | bcma_write32(dev, 0x94, val); | |
208 | ||
209 | /* Enable break memory transfer */ | |
210 | val = bcma_read32(dev, 0x9c); | |
211 | val |= 1; | |
212 | bcma_write32(dev, 0x9c, val); | |
213 | } | |
214 | ||
215 | static void bcma_hcd_init_chip_arm(struct bcma_device *dev) | |
216 | { | |
217 | bcma_core_enable(dev, 0); | |
218 | ||
219 | if (dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM4707 || | |
220 | dev->bus->chipinfo.id == BCMA_CHIP_ID_BCM53018) { | |
221 | if (dev->bus->chipinfo.pkg == BCMA_PKG_ID_BCM4707 || | |
222 | dev->bus->chipinfo.pkg == BCMA_PKG_ID_BCM4708) | |
223 | bcma_hcd_init_chip_arm_phy(dev); | |
224 | ||
225 | bcma_hcd_init_chip_arm_hc(dev); | |
226 | } | |
227 | } | |
228 | ||
eb4861c3 HM |
229 | static void bcma_hci_platform_power_gpio(struct bcma_device *dev, bool val) |
230 | { | |
231 | int gpio; | |
232 | ||
233 | gpio = of_get_named_gpio(dev->dev.of_node, "vcc-gpio", 0); | |
234 | if (!gpio_is_valid(gpio)) | |
235 | return; | |
236 | ||
237 | if (val) { | |
238 | gpio_request(gpio, "bcma-hcd-gpio"); | |
239 | gpio_set_value(gpio, 1); | |
240 | } else { | |
241 | gpio_set_value(gpio, 0); | |
242 | gpio_free(gpio); | |
243 | } | |
244 | } | |
245 | ||
62e11d1b HM |
246 | static const struct usb_ehci_pdata ehci_pdata = { |
247 | }; | |
248 | ||
249 | static const struct usb_ohci_pdata ohci_pdata = { | |
250 | }; | |
251 | ||
41ac7b3a | 252 | static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, bool ohci, u32 addr) |
62e11d1b HM |
253 | { |
254 | struct platform_device *hci_dev; | |
255 | struct resource hci_res[2]; | |
ab2de579 | 256 | int ret; |
62e11d1b HM |
257 | |
258 | memset(hci_res, 0, sizeof(hci_res)); | |
259 | ||
260 | hci_res[0].start = addr; | |
261 | hci_res[0].end = hci_res[0].start + 0x1000 - 1; | |
262 | hci_res[0].flags = IORESOURCE_MEM; | |
263 | ||
264 | hci_res[1].start = dev->irq; | |
265 | hci_res[1].flags = IORESOURCE_IRQ; | |
266 | ||
267 | hci_dev = platform_device_alloc(ohci ? "ohci-platform" : | |
268 | "ehci-platform" , 0); | |
269 | if (!hci_dev) | |
ab2de579 | 270 | return ERR_PTR(-ENOMEM); |
62e11d1b HM |
271 | |
272 | hci_dev->dev.parent = &dev->dev; | |
273 | hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; | |
274 | ||
275 | ret = platform_device_add_resources(hci_dev, hci_res, | |
276 | ARRAY_SIZE(hci_res)); | |
277 | if (ret) | |
278 | goto err_alloc; | |
279 | if (ohci) | |
280 | ret = platform_device_add_data(hci_dev, &ohci_pdata, | |
281 | sizeof(ohci_pdata)); | |
282 | else | |
283 | ret = platform_device_add_data(hci_dev, &ehci_pdata, | |
284 | sizeof(ehci_pdata)); | |
285 | if (ret) | |
286 | goto err_alloc; | |
287 | ret = platform_device_add(hci_dev); | |
288 | if (ret) | |
289 | goto err_alloc; | |
290 | ||
291 | return hci_dev; | |
292 | ||
293 | err_alloc: | |
294 | platform_device_put(hci_dev); | |
295 | return ERR_PTR(ret); | |
296 | } | |
297 | ||
41ac7b3a | 298 | static int bcma_hcd_probe(struct bcma_device *dev) |
62e11d1b HM |
299 | { |
300 | int err; | |
62e11d1b HM |
301 | u32 ohci_addr; |
302 | struct bcma_hcd_device *usb_dev; | |
303 | struct bcma_chipinfo *chipinfo; | |
304 | ||
305 | chipinfo = &dev->bus->chipinfo; | |
62e11d1b HM |
306 | |
307 | /* TODO: Probably need checks here; is the core connected? */ | |
308 | ||
d288059e | 309 | if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32))) |
62e11d1b HM |
310 | return -EOPNOTSUPP; |
311 | ||
c27da2b2 HM |
312 | usb_dev = devm_kzalloc(&dev->dev, sizeof(struct bcma_hcd_device), |
313 | GFP_KERNEL); | |
62e11d1b HM |
314 | if (!usb_dev) |
315 | return -ENOMEM; | |
316 | ||
eb4861c3 HM |
317 | bcma_hci_platform_power_gpio(dev, true); |
318 | ||
10bc04b7 HM |
319 | switch (dev->id.id) { |
320 | case BCMA_CORE_NS_USB20: | |
321 | bcma_hcd_init_chip_arm(dev); | |
322 | break; | |
323 | case BCMA_CORE_USB20_HOST: | |
324 | bcma_hcd_init_chip_mips(dev); | |
325 | break; | |
326 | default: | |
327 | return -ENODEV; | |
328 | } | |
62e11d1b HM |
329 | |
330 | /* In AI chips EHCI is addrspace 0, OHCI is 1 */ | |
23a2f39c | 331 | ohci_addr = dev->addr_s[0]; |
98e13e05 HM |
332 | if ((chipinfo->id == BCMA_CHIP_ID_BCM5357 || |
333 | chipinfo->id == BCMA_CHIP_ID_BCM4749) | |
62e11d1b HM |
334 | && chipinfo->rev == 0) |
335 | ohci_addr = 0x18009000; | |
336 | ||
337 | usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, true, ohci_addr); | |
c27da2b2 HM |
338 | if (IS_ERR(usb_dev->ohci_dev)) |
339 | return PTR_ERR(usb_dev->ohci_dev); | |
62e11d1b HM |
340 | |
341 | usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, false, dev->addr); | |
342 | if (IS_ERR(usb_dev->ehci_dev)) { | |
343 | err = PTR_ERR(usb_dev->ehci_dev); | |
344 | goto err_unregister_ohci_dev; | |
345 | } | |
346 | ||
347 | bcma_set_drvdata(dev, usb_dev); | |
348 | return 0; | |
349 | ||
350 | err_unregister_ohci_dev: | |
351 | platform_device_unregister(usb_dev->ohci_dev); | |
62e11d1b HM |
352 | return err; |
353 | } | |
354 | ||
fb4e98ab | 355 | static void bcma_hcd_remove(struct bcma_device *dev) |
62e11d1b HM |
356 | { |
357 | struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev); | |
358 | struct platform_device *ohci_dev = usb_dev->ohci_dev; | |
359 | struct platform_device *ehci_dev = usb_dev->ehci_dev; | |
360 | ||
361 | if (ohci_dev) | |
362 | platform_device_unregister(ohci_dev); | |
363 | if (ehci_dev) | |
364 | platform_device_unregister(ehci_dev); | |
365 | ||
366 | bcma_core_disable(dev, 0); | |
367 | } | |
368 | ||
369 | static void bcma_hcd_shutdown(struct bcma_device *dev) | |
370 | { | |
eb4861c3 | 371 | bcma_hci_platform_power_gpio(dev, false); |
62e11d1b HM |
372 | bcma_core_disable(dev, 0); |
373 | } | |
374 | ||
375 | #ifdef CONFIG_PM | |
376 | ||
1f6155f5 | 377 | static int bcma_hcd_suspend(struct bcma_device *dev) |
62e11d1b | 378 | { |
eb4861c3 | 379 | bcma_hci_platform_power_gpio(dev, false); |
62e11d1b HM |
380 | bcma_core_disable(dev, 0); |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | static int bcma_hcd_resume(struct bcma_device *dev) | |
386 | { | |
eb4861c3 | 387 | bcma_hci_platform_power_gpio(dev, true); |
62e11d1b HM |
388 | bcma_core_enable(dev, 0); |
389 | ||
390 | return 0; | |
391 | } | |
392 | ||
393 | #else /* !CONFIG_PM */ | |
394 | #define bcma_hcd_suspend NULL | |
395 | #define bcma_hcd_resume NULL | |
396 | #endif /* CONFIG_PM */ | |
397 | ||
2f82686e | 398 | static const struct bcma_device_id bcma_hcd_table[] = { |
62e11d1b | 399 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS), |
10bc04b7 | 400 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_USB20, BCMA_ANY_REV, BCMA_ANY_CLASS), |
f7219b52 | 401 | {}, |
62e11d1b HM |
402 | }; |
403 | MODULE_DEVICE_TABLE(bcma, bcma_hcd_table); | |
404 | ||
405 | static struct bcma_driver bcma_hcd_driver = { | |
406 | .name = KBUILD_MODNAME, | |
407 | .id_table = bcma_hcd_table, | |
408 | .probe = bcma_hcd_probe, | |
7690417d | 409 | .remove = bcma_hcd_remove, |
62e11d1b HM |
410 | .shutdown = bcma_hcd_shutdown, |
411 | .suspend = bcma_hcd_suspend, | |
412 | .resume = bcma_hcd_resume, | |
413 | }; | |
414 | ||
415 | static int __init bcma_hcd_init(void) | |
416 | { | |
417 | return bcma_driver_register(&bcma_hcd_driver); | |
418 | } | |
419 | module_init(bcma_hcd_init); | |
420 | ||
421 | static void __exit bcma_hcd_exit(void) | |
422 | { | |
423 | bcma_driver_unregister(&bcma_hcd_driver); | |
424 | } | |
425 | module_exit(bcma_hcd_exit); |