Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
bcmsdh_sdmmc.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010 Broadcom Corporation
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18 
19 #include <linux/types.h>
20 #include <linux/netdevice.h>
21 #include <linux/mmc/sdio.h>
22 #include <linux/mmc/core.h>
23 #include <linux/mmc/sdio_func.h>
24 #include <linux/mmc/sdio_ids.h>
25 #include <linux/mmc/card.h>
26 #include <linux/suspend.h>
27 #include <linux/errno.h>
28 #include <linux/sched.h> /* request_irq() */
29 #include <linux/module.h>
30 #include <linux/platform_device.h>
31 #include <net/cfg80211.h>
32 
33 #include <defs.h>
34 #include <brcm_hw_ids.h>
35 #include <brcmu_utils.h>
36 #include <brcmu_wifi.h>
37 #include "sdio_host.h"
38 #include "dhd_dbg.h"
39 #include "dhd_bus.h"
40 
41 #define SDIO_VENDOR_ID_BROADCOM 0x02d0
42 
43 #define DMA_ALIGN_MASK 0x03
44 
45 #define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
46 #define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
47 #define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
48 #define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
49 
50 #define SDIO_FUNC1_BLOCKSIZE 64
51 #define SDIO_FUNC2_BLOCKSIZE 512
52 
53 /* devices we support, null terminated */
54 static const struct sdio_device_id brcmf_sdmmc_ids[] = {
59  { /* end: all zeroes */ },
60 };
61 MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
62 
63 #ifdef CONFIG_BRCMFMAC_SDIO_OOB
64 static struct list_head oobirq_lh;
65 struct brcmf_sdio_oobirq {
66  unsigned int irq;
67  unsigned long flags;
68  struct list_head list;
69 };
70 #endif /* CONFIG_BRCMFMAC_SDIO_OOB */
71 
72 static bool
73 brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev)
74 {
75  bool is_err = false;
76 #ifdef CONFIG_PM_SLEEP
77  is_err = atomic_read(&sdiodev->suspend);
78 #endif
79  return is_err;
80 }
81 
82 static void
83 brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq)
84 {
85 #ifdef CONFIG_PM_SLEEP
86  int retry = 0;
87  while (atomic_read(&sdiodev->suspend) && retry++ != 30)
88  wait_event_timeout(*wq, false, HZ/100);
89 #endif
90 }
91 
92 static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev,
93  uint regaddr, u8 *byte)
94 {
95  struct sdio_func *sdfunc = sdiodev->func[0];
96  int err_ret;
97 
98  /*
99  * Can only directly write to some F0 registers.
100  * Handle F2 enable/disable and Abort command
101  * as a special case.
102  */
103  if (regaddr == SDIO_CCCR_IOEx) {
104  sdfunc = sdiodev->func[2];
105  if (sdfunc) {
106  if (*byte & SDIO_FUNC_ENABLE_2) {
107  /* Enable Function 2 */
108  err_ret = sdio_enable_func(sdfunc);
109  if (err_ret)
111  "enable F2 failed:%d\n",
112  err_ret);
113  } else {
114  /* Disable Function 2 */
115  err_ret = sdio_disable_func(sdfunc);
116  if (err_ret)
118  "Disable F2 failed:%d\n",
119  err_ret);
120  }
121  }
122  } else if ((regaddr == SDIO_CCCR_ABORT) ||
123  (regaddr == SDIO_CCCR_IENx)) {
124  sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func),
125  GFP_KERNEL);
126  if (!sdfunc)
127  return -ENOMEM;
128  sdfunc->num = 0;
129  sdio_writeb(sdfunc, *byte, regaddr, &err_ret);
130  kfree(sdfunc);
131  } else if (regaddr < 0xF0) {
132  brcmf_dbg(ERROR, "F0 Wr:0x%02x: write disallowed\n", regaddr);
133  err_ret = -EPERM;
134  } else {
135  sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret);
136  }
137 
138  return err_ret;
139 }
140 
142  uint regaddr, u8 *byte)
143 {
144  int err_ret;
145 
146  brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr);
147 
148  brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait);
149  if (brcmf_pm_resume_error(sdiodev))
150  return -EIO;
151 
152  if (rw && func == 0) {
153  /* handle F0 separately */
154  err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte);
155  } else {
156  if (rw) /* CMD52 Write */
157  sdio_writeb(sdiodev->func[func], *byte, regaddr,
158  &err_ret);
159  else if (func == 0) {
160  *byte = sdio_f0_readb(sdiodev->func[func], regaddr,
161  &err_ret);
162  } else {
163  *byte = sdio_readb(sdiodev->func[func], regaddr,
164  &err_ret);
165  }
166  }
167 
168  if (err_ret)
169  brcmf_dbg(ERROR, "Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n",
170  rw ? "write" : "read", func, regaddr, *byte, err_ret);
171 
172  return err_ret;
173 }
174 
177  uint nbytes)
178 {
179  int err_ret = -EIO;
180 
181  if (func == 0) {
182  brcmf_dbg(ERROR, "Only CMD52 allowed to F0\n");
183  return -EINVAL;
184  }
185 
186  brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
187  rw, func, addr, nbytes);
188 
189  brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait);
190  if (brcmf_pm_resume_error(sdiodev))
191  return -EIO;
192 
193  if (rw) { /* CMD52 Write */
194  if (nbytes == 4)
195  sdio_writel(sdiodev->func[func], *word, addr,
196  &err_ret);
197  else if (nbytes == 2)
198  sdio_writew(sdiodev->func[func], (*word & 0xFFFF),
199  addr, &err_ret);
200  else
201  brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes);
202  } else { /* CMD52 Read */
203  if (nbytes == 4)
204  *word = sdio_readl(sdiodev->func[func], addr, &err_ret);
205  else if (nbytes == 2)
206  *word = sdio_readw(sdiodev->func[func], addr,
207  &err_ret) & 0xFFFF;
208  else
209  brcmf_dbg(ERROR, "Invalid nbytes: %d\n", nbytes);
210  }
211 
212  if (err_ret)
213  brcmf_dbg(ERROR, "Failed to %s word, Err: 0x%08x\n",
214  rw ? "write" : "read", err_ret);
215 
216  return err_ret;
217 }
218 
219 /* precondition: host controller is claimed */
220 static int
221 brcmf_sdioh_request_data(struct brcmf_sdio_dev *sdiodev, uint write, bool fifo,
222  uint func, uint addr, struct sk_buff *pkt, uint pktlen)
223 {
224  int err_ret = 0;
225 
226  if ((write) && (!fifo)) {
227  err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
228  ((u8 *) (pkt->data)), pktlen);
229  } else if (write) {
230  err_ret = sdio_memcpy_toio(sdiodev->func[func], addr,
231  ((u8 *) (pkt->data)), pktlen);
232  } else if (fifo) {
233  err_ret = sdio_readsb(sdiodev->func[func],
234  ((u8 *) (pkt->data)), addr, pktlen);
235  } else {
236  err_ret = sdio_memcpy_fromio(sdiodev->func[func],
237  ((u8 *) (pkt->data)),
238  addr, pktlen);
239  }
240 
241  return err_ret;
242 }
243 
244 /*
245  * This function takes a queue of packets. The packets on the queue
246  * are assumed to be properly aligned by the caller.
247  */
248 int
250  uint write, uint func, uint addr,
251  struct sk_buff_head *pktq)
252 {
253  bool fifo = (fix_inc == SDIOH_DATA_FIX);
254  u32 SGCount = 0;
255  int err_ret = 0;
256 
257  struct sk_buff *pkt;
258 
259  brcmf_dbg(TRACE, "Enter\n");
260 
261  brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait);
262  if (brcmf_pm_resume_error(sdiodev))
263  return -EIO;
264 
265  skb_queue_walk(pktq, pkt) {
266  uint pkt_len = pkt->len;
267  pkt_len += 3;
268  pkt_len &= 0xFFFFFFFC;
269 
270  err_ret = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
271  addr, pkt, pkt_len);
272  if (err_ret) {
273  brcmf_dbg(ERROR, "%s FAILED %p[%d], addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
274  write ? "TX" : "RX", pkt, SGCount, addr,
275  pkt_len, err_ret);
276  } else {
277  brcmf_dbg(TRACE, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n",
278  write ? "TX" : "RX", pkt, SGCount, addr,
279  pkt_len);
280  }
281  if (!fifo)
282  addr += pkt_len;
283 
284  SGCount++;
285  }
286 
287  brcmf_dbg(TRACE, "Exit\n");
288  return err_ret;
289 }
290 
291 /*
292  * This function takes a single DMA-able packet.
293  */
295  uint fix_inc, uint write, uint func, uint addr,
296  struct sk_buff *pkt)
297 {
298  int status;
299  uint pkt_len;
300  bool fifo = (fix_inc == SDIOH_DATA_FIX);
301 
302  brcmf_dbg(TRACE, "Enter\n");
303 
304  if (pkt == NULL)
305  return -EINVAL;
306  pkt_len = pkt->len;
307 
308  brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
309  if (brcmf_pm_resume_error(sdiodev))
310  return -EIO;
311 
312  pkt_len += 3;
313  pkt_len &= (uint)~3;
314 
315  status = brcmf_sdioh_request_data(sdiodev, write, fifo, func,
316  addr, pkt, pkt_len);
317  if (status) {
318  brcmf_dbg(ERROR, "%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n",
319  write ? "TX" : "RX", pkt, addr, pkt_len, status);
320  } else {
321  brcmf_dbg(TRACE, "%s xfr'd %p, addr=0x%05x, len=%d\n",
322  write ? "TX" : "RX", pkt, addr, pkt_len);
323  }
324 
325  return status;
326 }
327 
328 static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr)
329 {
330  /* read 24 bits and return valid 17 bit addr */
331  int i, ret;
333  __le32 scratch_le;
334  u8 *ptr = (u8 *)&scratch_le;
335 
336  for (i = 0; i < 3; i++) {
337  regdata = brcmf_sdio_regrl(sdiodev, regaddr, &ret);
338  if (ret != 0)
339  brcmf_dbg(ERROR, "Can't read!\n");
340 
341  *ptr++ = (u8) regdata;
342  regaddr++;
343  }
344 
345  /* Only the lower 17-bits are valid */
346  scratch = le32_to_cpu(scratch_le);
347  scratch &= 0x0001FFFF;
348  return scratch;
349 }
350 
351 static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev)
352 {
353  int err_ret;
354  u32 fbraddr;
355  u8 func;
356 
357  brcmf_dbg(TRACE, "\n");
358 
359  /* Get the Card's common CIS address */
360  sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev,
361  SDIO_CCCR_CIS);
362  brcmf_dbg(INFO, "Card's Common CIS Ptr = 0x%x\n",
363  sdiodev->func_cis_ptr[0]);
364 
365  /* Get the Card's function CIS (for each function) */
366  for (fbraddr = SDIO_FBR_BASE(1), func = 1;
367  func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
368  sdiodev->func_cis_ptr[func] =
369  brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr);
370  brcmf_dbg(INFO, "Function %d CIS Ptr = 0x%x\n",
371  func, sdiodev->func_cis_ptr[func]);
372  }
373 
374  /* Enable Function 1 */
375  sdio_claim_host(sdiodev->func[1]);
376  err_ret = sdio_enable_func(sdiodev->func[1]);
377  sdio_release_host(sdiodev->func[1]);
378  if (err_ret)
379  brcmf_dbg(ERROR, "Failed to enable F1 Err: 0x%08x\n", err_ret);
380 
381  return false;
382 }
383 
384 /*
385  * Public entry points & extern's
386  */
387 int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev)
388 {
389  int err_ret = 0;
390 
391  brcmf_dbg(TRACE, "\n");
392 
393  sdiodev->num_funcs = 2;
394 
395  sdio_claim_host(sdiodev->func[1]);
396  err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE);
397  sdio_release_host(sdiodev->func[1]);
398  if (err_ret) {
399  brcmf_dbg(ERROR, "Failed to set F1 blocksize\n");
400  goto out;
401  }
402 
403  sdio_claim_host(sdiodev->func[2]);
404  err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE);
405  sdio_release_host(sdiodev->func[2]);
406  if (err_ret) {
407  brcmf_dbg(ERROR, "Failed to set F2 blocksize\n");
408  goto out;
409  }
410 
411  brcmf_sdioh_enablefuncs(sdiodev);
412 
413 out:
414  brcmf_dbg(TRACE, "Done\n");
415  return err_ret;
416 }
417 
418 void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev)
419 {
420  brcmf_dbg(TRACE, "\n");
421 
422  /* Disable Function 2 */
423  sdio_claim_host(sdiodev->func[2]);
424  sdio_disable_func(sdiodev->func[2]);
425  sdio_release_host(sdiodev->func[2]);
426 
427  /* Disable Function 1 */
428  sdio_claim_host(sdiodev->func[1]);
429  sdio_disable_func(sdiodev->func[1]);
430  sdio_release_host(sdiodev->func[1]);
431 
432 }
433 
434 #ifdef CONFIG_BRCMFMAC_SDIO_OOB
435 static int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev)
436 {
437  struct brcmf_sdio_oobirq *oobirq_entry;
438 
439  if (list_empty(&oobirq_lh)) {
440  brcmf_dbg(ERROR, "no valid oob irq resource\n");
441  return -ENXIO;
442  }
443 
444  oobirq_entry = list_first_entry(&oobirq_lh, struct brcmf_sdio_oobirq,
445  list);
446 
447  sdiodev->irq = oobirq_entry->irq;
448  sdiodev->irq_flags = oobirq_entry->flags;
449  list_del(&oobirq_entry->list);
450  kfree(oobirq_entry);
451 
452  return 0;
453 }
454 #else
455 static inline int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev)
456 {
457  return 0;
458 }
459 #endif /* CONFIG_BRCMFMAC_SDIO_OOB */
460 
461 static int brcmf_ops_sdio_probe(struct sdio_func *func,
462  const struct sdio_device_id *id)
463 {
464  int ret = 0;
465  struct brcmf_sdio_dev *sdiodev;
466  struct brcmf_bus *bus_if;
467 
468  brcmf_dbg(TRACE, "Enter\n");
469  brcmf_dbg(TRACE, "func->class=%x\n", func->class);
470  brcmf_dbg(TRACE, "sdio_vendor: 0x%04x\n", func->vendor);
471  brcmf_dbg(TRACE, "sdio_device: 0x%04x\n", func->device);
472  brcmf_dbg(TRACE, "Function#: 0x%04x\n", func->num);
473 
474  if (func->num == 1) {
475  if (dev_get_drvdata(&func->card->dev)) {
476  brcmf_dbg(ERROR, "card private drvdata occupied\n");
477  return -ENXIO;
478  }
479  bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
480  if (!bus_if)
481  return -ENOMEM;
482  sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
483  if (!sdiodev) {
484  kfree(bus_if);
485  return -ENOMEM;
486  }
487  sdiodev->func[0] = func;
488  sdiodev->func[1] = func;
489  sdiodev->bus_if = bus_if;
490  bus_if->bus_priv.sdio = sdiodev;
491  bus_if->type = SDIO_BUS;
492  bus_if->align = BRCMF_SDALIGN;
493  dev_set_drvdata(&func->card->dev, sdiodev);
494 
495  atomic_set(&sdiodev->suspend, false);
500  }
501 
502  if (func->num == 2) {
503  sdiodev = dev_get_drvdata(&func->card->dev);
504  if ((!sdiodev) || (sdiodev->func[1]->card != func->card))
505  return -ENODEV;
506 
507  ret = brcmf_sdio_getintrcfg(sdiodev);
508  if (ret)
509  return ret;
510  sdiodev->func[2] = func;
511 
512  bus_if = sdiodev->bus_if;
513  sdiodev->dev = &func->dev;
514  dev_set_drvdata(&func->dev, bus_if);
515 
516  brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n");
517  ret = brcmf_sdio_probe(sdiodev);
518  }
519 
520  return ret;
521 }
522 
523 static void brcmf_ops_sdio_remove(struct sdio_func *func)
524 {
525  struct brcmf_bus *bus_if;
526  struct brcmf_sdio_dev *sdiodev;
527  brcmf_dbg(TRACE, "Enter\n");
528  brcmf_dbg(INFO, "func->class=%x\n", func->class);
529  brcmf_dbg(INFO, "sdio_vendor: 0x%04x\n", func->vendor);
530  brcmf_dbg(INFO, "sdio_device: 0x%04x\n", func->device);
531  brcmf_dbg(INFO, "Function#: 0x%04x\n", func->num);
532 
533  if (func->num == 2) {
534  bus_if = dev_get_drvdata(&func->dev);
535  sdiodev = bus_if->bus_priv.sdio;
536  brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_remove...\n");
537  brcmf_sdio_remove(sdiodev);
538  dev_set_drvdata(&func->card->dev, NULL);
539  dev_set_drvdata(&func->dev, NULL);
540  kfree(bus_if);
541  kfree(sdiodev);
542  }
543 }
544 
545 #ifdef CONFIG_PM_SLEEP
546 static int brcmf_sdio_suspend(struct device *dev)
547 {
548  mmc_pm_flag_t sdio_flags;
549  struct sdio_func *func = dev_to_sdio_func(dev);
550  struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev);
551  int ret = 0;
552 
553  brcmf_dbg(TRACE, "\n");
554 
555  atomic_set(&sdiodev->suspend, true);
556 
557  sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]);
558  if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
559  brcmf_dbg(ERROR, "Host can't keep power while suspended\n");
560  return -EINVAL;
561  }
562 
563  ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER);
564  if (ret) {
565  brcmf_dbg(ERROR, "Failed to set pm_flags\n");
566  return ret;
567  }
568 
569  brcmf_sdio_wdtmr_enable(sdiodev, false);
570 
571  return ret;
572 }
573 
574 static int brcmf_sdio_resume(struct device *dev)
575 {
576  struct sdio_func *func = dev_to_sdio_func(dev);
577  struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev);
578 
579  brcmf_sdio_wdtmr_enable(sdiodev, true);
580  atomic_set(&sdiodev->suspend, false);
581  return 0;
582 }
583 
584 static const struct dev_pm_ops brcmf_sdio_pm_ops = {
585  .suspend = brcmf_sdio_suspend,
586  .resume = brcmf_sdio_resume,
587 };
588 #endif /* CONFIG_PM_SLEEP */
589 
590 static struct sdio_driver brcmf_sdmmc_driver = {
591  .probe = brcmf_ops_sdio_probe,
592  .remove = brcmf_ops_sdio_remove,
593  .name = "brcmfmac",
594  .id_table = brcmf_sdmmc_ids,
595 #ifdef CONFIG_PM_SLEEP
596  .drv = {
597  .pm = &brcmf_sdio_pm_ops,
598  },
599 #endif /* CONFIG_PM_SLEEP */
600 };
601 
602 #ifdef CONFIG_BRCMFMAC_SDIO_OOB
603 static int brcmf_sdio_pd_probe(struct platform_device *pdev)
604 {
605  struct resource *res;
606  struct brcmf_sdio_oobirq *oobirq_entry;
607  int i, ret;
608 
609  INIT_LIST_HEAD(&oobirq_lh);
610 
611  for (i = 0; ; i++) {
612  res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
613  if (!res)
614  break;
615 
616  oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq),
617  GFP_KERNEL);
618  if (!oobirq_entry)
619  return -ENOMEM;
620  oobirq_entry->irq = res->start;
621  oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK;
622  list_add_tail(&oobirq_entry->list, &oobirq_lh);
623  }
624  if (i == 0)
625  return -ENXIO;
626 
627  ret = sdio_register_driver(&brcmf_sdmmc_driver);
628 
629  if (ret)
630  brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret);
631 
632  return ret;
633 }
634 
635 static struct platform_driver brcmf_sdio_pd = {
636  .probe = brcmf_sdio_pd_probe,
637  .driver = {
638  .name = "brcmf_sdio_pd"
639  }
640 };
641 
642 void brcmf_sdio_exit(void)
643 {
644  brcmf_dbg(TRACE, "Enter\n");
645 
646  sdio_unregister_driver(&brcmf_sdmmc_driver);
647 
648  platform_driver_unregister(&brcmf_sdio_pd);
649 }
650 
651 void brcmf_sdio_init(void)
652 {
653  int ret;
654 
655  brcmf_dbg(TRACE, "Enter\n");
656 
657  ret = platform_driver_register(&brcmf_sdio_pd);
658 
659  if (ret)
660  brcmf_dbg(ERROR, "platform_driver_register failed: %d\n", ret);
661 }
662 #else
663 void brcmf_sdio_exit(void)
664 {
665  brcmf_dbg(TRACE, "Enter\n");
666 
667  sdio_unregister_driver(&brcmf_sdmmc_driver);
668 }
669 
670 void brcmf_sdio_init(void)
671 {
672  int ret;
673 
674  brcmf_dbg(TRACE, "Enter\n");
675 
676  ret = sdio_register_driver(&brcmf_sdmmc_driver);
677 
678  if (ret)
679  brcmf_dbg(ERROR, "sdio_register_driver failed: %d\n", ret);
680 }
681 #endif /* CONFIG_BRCMFMAC_SDIO_OOB */