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