Commit | Line | Data |
---|---|---|
5fd54ace | 1 | // SPDX-License-Identifier: GPL-2.0 |
62e11d1b HM |
2 | /* |
3 | * Broadcom specific Advanced Microcontroller Bus | |
4 | * Broadcom USB-core driver (BCMA bus glue) | |
5 | * | |
10bc04b7 HM |
6 | * Copyright 2011-2015 Hauke Mehrtens <hauke@hauke-m.de> |
7 | * Copyright 2015 Felix Fietkau <nbd@openwrt.org> | |
62e11d1b HM |
8 | * |
9 | * Based on ssb-ohci driver | |
10 | * Copyright 2007 Michael Buesch <m@bues.ch> | |
11 | * | |
12 | * Derived from the OHCI-PCI driver | |
13 | * Copyright 1999 Roman Weissgaerber | |
14 | * Copyright 2000-2002 David Brownell | |
15 | * Copyright 1999 Linus Torvalds | |
16 | * Copyright 1999 Gregory P. Smith | |
17 | * | |
18 | * Derived from the USBcore related parts of Broadcom-SB | |
19 | * Copyright 2005-2011 Broadcom Corporation | |
62e11d1b HM |
20 | */ |
21 | #include <linux/bcma/bcma.h> | |
22 | #include <linux/delay.h> | |
9faae5a3 | 23 | #include <linux/gpio/consumer.h> |
62e11d1b HM |
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> | |
3cc7e7b7 | 29 | #include <linux/of_platform.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 | ||
d6b76c4d RM |
37 | /* See BCMA_CLKCTLST_EXTRESREQ and BCMA_CLKCTLST_EXTRESST */ |
38 | #define USB_BCMA_CLKCTLST_USB_CLK_REQ 0x00000100 | |
39 | ||
62e11d1b | 40 | struct bcma_hcd_device { |
adbff3a4 | 41 | struct bcma_device *core; |
62e11d1b HM |
42 | struct platform_device *ehci_dev; |
43 | struct platform_device *ohci_dev; | |
9faae5a3 | 44 | struct gpio_desc *gpio_desc; |
62e11d1b HM |
45 | }; |
46 | ||
47 | /* Wait for bitmask in a register to get set or cleared. | |
48 | * timeout is in units of ten-microseconds. | |
49 | */ | |
50 | static int bcma_wait_bits(struct bcma_device *dev, u16 reg, u32 bitmask, | |
51 | int timeout) | |
52 | { | |
53 | int i; | |
54 | u32 val; | |
55 | ||
56 | for (i = 0; i < timeout; i++) { | |
57 | val = bcma_read32(dev, reg); | |
58 | if ((val & bitmask) == bitmask) | |
59 | return 0; | |
60 | udelay(10); | |
61 | } | |
62 | ||
63 | return -ETIMEDOUT; | |
64 | } | |
65 | ||
41ac7b3a | 66 | static void bcma_hcd_4716wa(struct bcma_device *dev) |
62e11d1b HM |
67 | { |
68 | #ifdef CONFIG_BCMA_DRIVER_MIPS | |
69 | /* Work around for 4716 failures. */ | |
70 | if (dev->bus->chipinfo.id == 0x4716) { | |
71 | u32 tmp; | |
72 | ||
73 | tmp = bcma_cpu_clock(&dev->bus->drv_mips); | |
74 | if (tmp >= 480000000) | |
75 | tmp = 0x1846b; /* set CDR to 0x11(fast) */ | |
76 | else if (tmp == 453000000) | |
77 | tmp = 0x1046b; /* set CDR to 0x10(slow) */ | |
78 | else | |
79 | tmp = 0; | |
80 | ||
81 | /* Change Shim mdio control reg to fix host not acking at | |
82 | * high frequencies | |
83 | */ | |
84 | if (tmp) { | |
85 | bcma_write32(dev, 0x524, 0x1); /* write sel to enable */ | |
86 | udelay(500); | |
87 | ||
88 | bcma_write32(dev, 0x524, tmp); | |
89 | udelay(500); | |
90 | bcma_write32(dev, 0x524, 0x4ab); | |
91 | udelay(500); | |
92 | bcma_read32(dev, 0x528); | |
93 | bcma_write32(dev, 0x528, 0x80000000); | |
94 | } | |
95 | } | |
96 | #endif /* CONFIG_BCMA_DRIVER_MIPS */ | |
97 | } | |
98 | ||
99 | /* based on arch/mips/brcm-boards/bcm947xx/pcibios.c */ | |
10bc04b7 | 100 | static void bcma_hcd_init_chip_mips(struct bcma_device *dev) |
62e11d1b HM |
101 | { |
102 | u32 tmp; | |
103 | ||
104 | /* | |
105 | * USB 2.0 special considerations: | |
106 | * | |
107 | * 1. Since the core supports both OHCI and EHCI functions, it must | |
108 | * only be reset once. | |
109 | * | |
110 | * 2. In addition to the standard SI reset sequence, the Host Control | |
111 | * Register must be programmed to bring the USB core and various | |
112 | * phy components out of reset. | |
113 | */ | |
114 | if (!bcma_core_is_enabled(dev)) { | |
115 | bcma_core_enable(dev, 0); | |
116 | mdelay(10); | |
117 | if (dev->id.rev >= 5) { | |
118 | /* Enable Misc PLL */ | |
119 | tmp = bcma_read32(dev, 0x1e0); | |
120 | tmp |= 0x100; | |
121 | bcma_write32(dev, 0x1e0, tmp); | |
122 | if (bcma_wait_bits(dev, 0x1e0, 1 << 24, 100)) | |
123 | printk(KERN_EMERG "Failed to enable misc PPL!\n"); | |
124 | ||
125 | /* Take out of resets */ | |
126 | bcma_write32(dev, 0x200, 0x4ff); | |
127 | udelay(25); | |
128 | bcma_write32(dev, 0x200, 0x6ff); | |
129 | udelay(25); | |
130 | ||
131 | /* Make sure digital and AFE are locked in USB PHY */ | |
132 | bcma_write32(dev, 0x524, 0x6b); | |
133 | udelay(50); | |
134 | tmp = bcma_read32(dev, 0x524); | |
135 | udelay(50); | |
136 | bcma_write32(dev, 0x524, 0xab); | |
137 | udelay(50); | |
138 | tmp = bcma_read32(dev, 0x524); | |
139 | udelay(50); | |
140 | bcma_write32(dev, 0x524, 0x2b); | |
141 | udelay(50); | |
142 | tmp = bcma_read32(dev, 0x524); | |
143 | udelay(50); | |
144 | bcma_write32(dev, 0x524, 0x10ab); | |
145 | udelay(50); | |
146 | tmp = bcma_read32(dev, 0x524); | |
147 | ||
148 | if (bcma_wait_bits(dev, 0x528, 0xc000, 10000)) { | |
149 | tmp = bcma_read32(dev, 0x528); | |
150 | printk(KERN_EMERG | |
151 | "USB20H mdio_rddata 0x%08x\n", tmp); | |
152 | } | |
153 | bcma_write32(dev, 0x528, 0x80000000); | |
154 | tmp = bcma_read32(dev, 0x314); | |
155 | udelay(265); | |
156 | bcma_write32(dev, 0x200, 0x7ff); | |
157 | udelay(10); | |
158 | ||
159 | /* Take USB and HSIC out of non-driving modes */ | |
160 | bcma_write32(dev, 0x510, 0); | |
161 | } else { | |
162 | bcma_write32(dev, 0x200, 0x7ff); | |
163 | ||
164 | udelay(1); | |
165 | } | |
166 | ||
167 | bcma_hcd_4716wa(dev); | |
168 | } | |
169 | } | |
170 | ||
d6b76c4d RM |
171 | /** |
172 | * bcma_hcd_usb20_old_arm_init - Initialize old USB 2.0 controller on ARM | |
173 | * | |
174 | * Old USB 2.0 core is identified as BCMA_CORE_USB20_HOST and was introduced | |
175 | * long before Northstar devices. It seems some cheaper chipsets like BCM53573 | |
176 | * still use it. | |
177 | * Initialization of this old core differs between MIPS and ARM. | |
178 | */ | |
179 | static int bcma_hcd_usb20_old_arm_init(struct bcma_hcd_device *usb_dev) | |
180 | { | |
181 | struct bcma_device *core = usb_dev->core; | |
182 | struct device *dev = &core->dev; | |
183 | struct bcma_device *pmu_core; | |
184 | ||
185 | usleep_range(10000, 20000); | |
186 | if (core->id.rev < 5) | |
187 | return 0; | |
188 | ||
189 | pmu_core = bcma_find_core(core->bus, BCMA_CORE_PMU); | |
190 | if (!pmu_core) { | |
191 | dev_err(dev, "Could not find PMU core\n"); | |
192 | return -ENOENT; | |
193 | } | |
194 | ||
195 | /* Take USB core out of reset */ | |
196 | bcma_awrite32(core, BCMA_IOCTL, BCMA_IOCTL_CLK | BCMA_IOCTL_FGC); | |
197 | usleep_range(100, 200); | |
198 | bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET); | |
199 | usleep_range(100, 200); | |
200 | bcma_awrite32(core, BCMA_RESET_CTL, 0); | |
201 | usleep_range(100, 200); | |
202 | bcma_awrite32(core, BCMA_IOCTL, BCMA_IOCTL_CLK); | |
203 | usleep_range(100, 200); | |
204 | ||
205 | /* Enable Misc PLL */ | |
206 | bcma_write32(core, BCMA_CLKCTLST, BCMA_CLKCTLST_FORCEHT | | |
207 | BCMA_CLKCTLST_HQCLKREQ | | |
208 | USB_BCMA_CLKCTLST_USB_CLK_REQ); | |
209 | usleep_range(100, 200); | |
210 | ||
211 | bcma_write32(core, 0x510, 0xc7f85000); | |
212 | bcma_write32(core, 0x510, 0xc7f85003); | |
213 | usleep_range(300, 600); | |
214 | ||
215 | /* Program USB PHY PLL parameters */ | |
216 | bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_ADDR, 0x6); | |
217 | bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_DATA, 0x005360c1); | |
218 | usleep_range(100, 200); | |
219 | bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_ADDR, 0x7); | |
220 | bcma_write32(pmu_core, BCMA_CC_PMU_PLLCTL_DATA, 0x0); | |
221 | usleep_range(100, 200); | |
222 | bcma_set32(pmu_core, BCMA_CC_PMU_CTL, BCMA_CC_PMU_CTL_PLL_UPD); | |
223 | usleep_range(100, 200); | |
224 | ||
225 | bcma_write32(core, 0x510, 0x7f8d007); | |
226 | udelay(1000); | |
227 | ||
228 | /* Take controller out of reset */ | |
229 | bcma_write32(core, 0x200, 0x4ff); | |
230 | usleep_range(25, 50); | |
231 | bcma_write32(core, 0x200, 0x6ff); | |
232 | usleep_range(25, 50); | |
233 | bcma_write32(core, 0x200, 0x7ff); | |
234 | usleep_range(25, 50); | |
235 | ||
236 | of_platform_default_populate(dev->of_node, NULL, dev); | |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
e8624859 | 241 | static void bcma_hcd_usb20_ns_init_hc(struct bcma_device *dev) |
10bc04b7 HM |
242 | { |
243 | u32 val; | |
244 | ||
10bc04b7 HM |
245 | /* Set packet buffer OUT threshold */ |
246 | val = bcma_read32(dev, 0x94); | |
247 | val &= 0xffff; | |
248 | val |= 0x80 << 16; | |
249 | bcma_write32(dev, 0x94, val); | |
250 | ||
251 | /* Enable break memory transfer */ | |
252 | val = bcma_read32(dev, 0x9c); | |
253 | val |= 1; | |
254 | bcma_write32(dev, 0x9c, val); | |
e8624859 RM |
255 | |
256 | /* | |
257 | * Broadcom initializes PHY and then waits to ensure HC is ready to be | |
258 | * configured. In our case the order is reversed. We just initialized | |
259 | * controller and we let HCD initialize PHY, so let's wait (sleep) now. | |
260 | */ | |
261 | usleep_range(1000, 2000); | |
10bc04b7 HM |
262 | } |
263 | ||
e8624859 RM |
264 | /** |
265 | * bcma_hcd_usb20_ns_init - Initialize Northstar USB 2.0 controller | |
266 | */ | |
267 | static int bcma_hcd_usb20_ns_init(struct bcma_hcd_device *bcma_hcd) | |
10bc04b7 | 268 | { |
e8624859 RM |
269 | struct bcma_device *core = bcma_hcd->core; |
270 | struct bcma_chipinfo *ci = &core->bus->chipinfo; | |
271 | struct device *dev = &core->dev; | |
10bc04b7 | 272 | |
e8624859 | 273 | bcma_core_enable(core, 0); |
10bc04b7 | 274 | |
e8624859 RM |
275 | if (ci->id == BCMA_CHIP_ID_BCM4707 || |
276 | ci->id == BCMA_CHIP_ID_BCM53018) | |
277 | bcma_hcd_usb20_ns_init_hc(core); | |
278 | ||
279 | of_platform_default_populate(dev->of_node, NULL, dev); | |
280 | ||
281 | return 0; | |
10bc04b7 HM |
282 | } |
283 | ||
eb4861c3 HM |
284 | static void bcma_hci_platform_power_gpio(struct bcma_device *dev, bool val) |
285 | { | |
9faae5a3 | 286 | struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev); |
eb4861c3 | 287 | |
9faae5a3 | 288 | if (IS_ERR_OR_NULL(usb_dev->gpio_desc)) |
eb4861c3 HM |
289 | return; |
290 | ||
9faae5a3 | 291 | gpiod_set_value(usb_dev->gpio_desc, val); |
eb4861c3 HM |
292 | } |
293 | ||
62e11d1b HM |
294 | static const struct usb_ehci_pdata ehci_pdata = { |
295 | }; | |
296 | ||
297 | static const struct usb_ohci_pdata ohci_pdata = { | |
298 | }; | |
299 | ||
352d9e2e RM |
300 | static struct platform_device *bcma_hcd_create_pdev(struct bcma_device *dev, |
301 | const char *name, u32 addr, | |
302 | const void *data, | |
303 | size_t size) | |
62e11d1b HM |
304 | { |
305 | struct platform_device *hci_dev; | |
306 | struct resource hci_res[2]; | |
ab2de579 | 307 | int ret; |
62e11d1b HM |
308 | |
309 | memset(hci_res, 0, sizeof(hci_res)); | |
310 | ||
311 | hci_res[0].start = addr; | |
312 | hci_res[0].end = hci_res[0].start + 0x1000 - 1; | |
313 | hci_res[0].flags = IORESOURCE_MEM; | |
314 | ||
315 | hci_res[1].start = dev->irq; | |
316 | hci_res[1].flags = IORESOURCE_IRQ; | |
317 | ||
352d9e2e | 318 | hci_dev = platform_device_alloc(name, 0); |
62e11d1b | 319 | if (!hci_dev) |
ab2de579 | 320 | return ERR_PTR(-ENOMEM); |
62e11d1b HM |
321 | |
322 | hci_dev->dev.parent = &dev->dev; | |
323 | hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; | |
324 | ||
325 | ret = platform_device_add_resources(hci_dev, hci_res, | |
326 | ARRAY_SIZE(hci_res)); | |
327 | if (ret) | |
328 | goto err_alloc; | |
352d9e2e RM |
329 | if (data) |
330 | ret = platform_device_add_data(hci_dev, data, size); | |
62e11d1b HM |
331 | if (ret) |
332 | goto err_alloc; | |
333 | ret = platform_device_add(hci_dev); | |
334 | if (ret) | |
335 | goto err_alloc; | |
336 | ||
337 | return hci_dev; | |
338 | ||
339 | err_alloc: | |
340 | platform_device_put(hci_dev); | |
341 | return ERR_PTR(ret); | |
342 | } | |
343 | ||
adbff3a4 | 344 | static int bcma_hcd_usb20_init(struct bcma_hcd_device *usb_dev) |
62e11d1b | 345 | { |
adbff3a4 RM |
346 | struct bcma_device *dev = usb_dev->core; |
347 | struct bcma_chipinfo *chipinfo = &dev->bus->chipinfo; | |
62e11d1b | 348 | u32 ohci_addr; |
adbff3a4 | 349 | int err; |
62e11d1b | 350 | |
d288059e | 351 | if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32))) |
62e11d1b HM |
352 | return -EOPNOTSUPP; |
353 | ||
e8624859 | 354 | bcma_hcd_init_chip_mips(dev); |
62e11d1b HM |
355 | |
356 | /* In AI chips EHCI is addrspace 0, OHCI is 1 */ | |
23a2f39c | 357 | ohci_addr = dev->addr_s[0]; |
98e13e05 HM |
358 | if ((chipinfo->id == BCMA_CHIP_ID_BCM5357 || |
359 | chipinfo->id == BCMA_CHIP_ID_BCM4749) | |
62e11d1b HM |
360 | && chipinfo->rev == 0) |
361 | ohci_addr = 0x18009000; | |
362 | ||
352d9e2e RM |
363 | usb_dev->ohci_dev = bcma_hcd_create_pdev(dev, "ohci-platform", |
364 | ohci_addr, &ohci_pdata, | |
365 | sizeof(ohci_pdata)); | |
c27da2b2 HM |
366 | if (IS_ERR(usb_dev->ohci_dev)) |
367 | return PTR_ERR(usb_dev->ohci_dev); | |
62e11d1b | 368 | |
352d9e2e RM |
369 | usb_dev->ehci_dev = bcma_hcd_create_pdev(dev, "ehci-platform", |
370 | dev->addr, &ehci_pdata, | |
371 | sizeof(ehci_pdata)); | |
62e11d1b HM |
372 | if (IS_ERR(usb_dev->ehci_dev)) { |
373 | err = PTR_ERR(usb_dev->ehci_dev); | |
374 | goto err_unregister_ohci_dev; | |
375 | } | |
376 | ||
62e11d1b HM |
377 | return 0; |
378 | ||
379 | err_unregister_ohci_dev: | |
380 | platform_device_unregister(usb_dev->ohci_dev); | |
62e11d1b HM |
381 | return err; |
382 | } | |
383 | ||
3cc7e7b7 RM |
384 | static int bcma_hcd_usb30_init(struct bcma_hcd_device *bcma_hcd) |
385 | { | |
386 | struct bcma_device *core = bcma_hcd->core; | |
387 | struct device *dev = &core->dev; | |
388 | ||
389 | bcma_core_enable(core, 0); | |
390 | ||
391 | of_platform_default_populate(dev->of_node, NULL, dev); | |
392 | ||
393 | return 0; | |
394 | } | |
395 | ||
adbff3a4 RM |
396 | static int bcma_hcd_probe(struct bcma_device *core) |
397 | { | |
398 | int err; | |
399 | struct bcma_hcd_device *usb_dev; | |
400 | ||
401 | /* TODO: Probably need checks here; is the core connected? */ | |
402 | ||
403 | usb_dev = devm_kzalloc(&core->dev, sizeof(struct bcma_hcd_device), | |
404 | GFP_KERNEL); | |
405 | if (!usb_dev) | |
406 | return -ENOMEM; | |
407 | usb_dev->core = core; | |
408 | ||
409 | if (core->dev.of_node) | |
1507372b RM |
410 | usb_dev->gpio_desc = devm_gpiod_get(&core->dev, "vcc", |
411 | GPIOD_OUT_HIGH); | |
adbff3a4 RM |
412 | |
413 | switch (core->id.id) { | |
414 | case BCMA_CORE_USB20_HOST: | |
d6b76c4d RM |
415 | if (IS_ENABLED(CONFIG_ARM)) |
416 | err = bcma_hcd_usb20_old_arm_init(usb_dev); | |
417 | else if (IS_ENABLED(CONFIG_MIPS)) | |
418 | err = bcma_hcd_usb20_init(usb_dev); | |
419 | else | |
420 | err = -ENOTSUPP; | |
421 | break; | |
adbff3a4 | 422 | case BCMA_CORE_NS_USB20: |
e8624859 | 423 | err = bcma_hcd_usb20_ns_init(usb_dev); |
adbff3a4 | 424 | break; |
3cc7e7b7 RM |
425 | case BCMA_CORE_NS_USB30: |
426 | err = bcma_hcd_usb30_init(usb_dev); | |
3cc7e7b7 | 427 | break; |
adbff3a4 RM |
428 | default: |
429 | return -ENODEV; | |
430 | } | |
d6b76c4d RM |
431 | if (err) |
432 | return err; | |
adbff3a4 RM |
433 | |
434 | bcma_set_drvdata(core, usb_dev); | |
435 | return 0; | |
436 | } | |
437 | ||
fb4e98ab | 438 | static void bcma_hcd_remove(struct bcma_device *dev) |
62e11d1b HM |
439 | { |
440 | struct bcma_hcd_device *usb_dev = bcma_get_drvdata(dev); | |
441 | struct platform_device *ohci_dev = usb_dev->ohci_dev; | |
442 | struct platform_device *ehci_dev = usb_dev->ehci_dev; | |
443 | ||
444 | if (ohci_dev) | |
445 | platform_device_unregister(ohci_dev); | |
446 | if (ehci_dev) | |
447 | platform_device_unregister(ehci_dev); | |
448 | ||
449 | bcma_core_disable(dev, 0); | |
450 | } | |
451 | ||
452 | static void bcma_hcd_shutdown(struct bcma_device *dev) | |
453 | { | |
eb4861c3 | 454 | bcma_hci_platform_power_gpio(dev, false); |
62e11d1b HM |
455 | bcma_core_disable(dev, 0); |
456 | } | |
457 | ||
458 | #ifdef CONFIG_PM | |
459 | ||
1f6155f5 | 460 | static int bcma_hcd_suspend(struct bcma_device *dev) |
62e11d1b | 461 | { |
eb4861c3 | 462 | bcma_hci_platform_power_gpio(dev, false); |
62e11d1b HM |
463 | bcma_core_disable(dev, 0); |
464 | ||
465 | return 0; | |
466 | } | |
467 | ||
468 | static int bcma_hcd_resume(struct bcma_device *dev) | |
469 | { | |
eb4861c3 | 470 | bcma_hci_platform_power_gpio(dev, true); |
62e11d1b HM |
471 | bcma_core_enable(dev, 0); |
472 | ||
473 | return 0; | |
474 | } | |
475 | ||
476 | #else /* !CONFIG_PM */ | |
477 | #define bcma_hcd_suspend NULL | |
478 | #define bcma_hcd_resume NULL | |
479 | #endif /* CONFIG_PM */ | |
480 | ||
2f82686e | 481 | static const struct bcma_device_id bcma_hcd_table[] = { |
62e11d1b | 482 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_USB20_HOST, BCMA_ANY_REV, BCMA_ANY_CLASS), |
10bc04b7 | 483 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_USB20, BCMA_ANY_REV, BCMA_ANY_CLASS), |
3cc7e7b7 | 484 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_USB30, BCMA_ANY_REV, BCMA_ANY_CLASS), |
f7219b52 | 485 | {}, |
62e11d1b HM |
486 | }; |
487 | MODULE_DEVICE_TABLE(bcma, bcma_hcd_table); | |
488 | ||
489 | static struct bcma_driver bcma_hcd_driver = { | |
490 | .name = KBUILD_MODNAME, | |
491 | .id_table = bcma_hcd_table, | |
492 | .probe = bcma_hcd_probe, | |
7690417d | 493 | .remove = bcma_hcd_remove, |
62e11d1b HM |
494 | .shutdown = bcma_hcd_shutdown, |
495 | .suspend = bcma_hcd_suspend, | |
496 | .resume = bcma_hcd_resume, | |
497 | }; | |
498 | ||
499 | static int __init bcma_hcd_init(void) | |
500 | { | |
501 | return bcma_driver_register(&bcma_hcd_driver); | |
502 | } | |
503 | module_init(bcma_hcd_init); | |
504 | ||
505 | static void __exit bcma_hcd_exit(void) | |
506 | { | |
507 | bcma_driver_unregister(&bcma_hcd_driver); | |
508 | } | |
509 | module_exit(bcma_hcd_exit); |