]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/sdiotool/sdiotool.c
amd64: use register macros for gdb_cpu_getreg()
[FreeBSD/FreeBSD.git] / usr.bin / sdiotool / sdiotool.c
1 /*-
2  * Copyright (c) 2016-2017 Ilya Bakulin
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * Copyright (c) 2010 Broadcom Corporation
27  *
28  * Permission to use, copy, modify, and/or distribute this software for any
29  * purpose with or without fee is hereby granted, provided that the above
30  * copyright notice and this permission notice appear in all copies.
31  *
32  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
33  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
34  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
35  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
36  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
37  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
38  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
39  *
40  * $FreeBSD$
41  */
42
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 #include <sys/ioctl.h>
47 #include <sys/stdint.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <sys/endian.h>
51 #include <sys/sbuf.h>
52 #include <sys/mman.h>
53
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58 #include <inttypes.h>
59 #include <limits.h>
60 #include <fcntl.h>
61 #include <ctype.h>
62 #include <err.h>
63 #include <libutil.h>
64 #include <unistd.h>
65
66 #include <cam/cam.h>
67 #include <cam/cam_debug.h>
68 #include <cam/cam_ccb.h>
69 #include <cam/mmc/mmc_all.h>
70 #include <camlib.h>
71
72 #include "linux_compat.h"
73 #include "linux_sdio_compat.h"
74 #include "cam_sdio.h"
75 #include "brcmfmac_sdio.h"
76 #include "brcmfmac_bus.h"
77
78 static void probe_bcrm(struct cam_device *dev);
79
80 /*
81  * How Linux driver works
82  *
83  * The probing begins by calling brcmf_ops_sdio_probe() which is defined as probe function in struct sdio_driver. http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L1126
84  *
85  * The driver does black magic by copying func struct for F2 and setting func number to zero there, to create an F0 func structure :)
86  * Driver state changes to BRCMF_SDIOD_DOWN.
87  * ops_sdio_probe() then calls brcmf_sdio_probe() -- at this point it has filled in sdiodev struct with the pointers to all three functions (F0, F1, F2).
88  *
89  * brcmf_sdiod_probe() sets block sizes for F1 and F2. It sets F1 block size to 64 and F2 to 512, not consulting the values stored in SDIO CCCR  / FBR registers!
90  * Then it increases timeout for F2 (what is this?!)
91  * Then it enables F1
92  * Then it attaches "freezer" (without PM this is NOP)
93  * Finally it calls brcmf_sdio_probe() http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4082
94  *
95  * Here high-level workqueues and sg tables are allocated.
96  * It then calls brcmf_sdio_probe_attach()
97  *
98  * Here at the beginning there is a pr_debug() call with brcmf_sdiod_regrl() inside to addr #define SI_ENUM_BASE            0x18000000.
99  * Return value is 0x16044330.
100  * Then turns off PLL:  byte-write BRCMF_INIT_CLKCTL1 (0x28) ->  SBSDIO_FUNC1_CHIPCLKCSR (0x1000E)
101  * Then it reads value back, should be 0xe8.
102  * Then calls brcmf_chip_attach()
103  *
104  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1054
105  * This func enumerates and resets all the cores on the dongle.
106  *  - brcmf_sdio_buscoreprep(): force clock to ALPAvail req only:
107  *    SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ -> SBSDIO_FUNC1_CHIPCLKCSR
108  * Wait up to 15ms to !SBSDIO_ALPAV(clkval) of the value from CLKCSR.
109  * Force ALP:
110  *    SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP (0x21)-> SBSDIO_FUNC1_CHIPCLKCSR
111  * Disaable SDIO pullups:
112  * byte 0 -> SBSDIO_FUNC1_SDIOPULLUP (0x0001000f)
113  *
114  *  Calls brcmf_chip_recognition()
115  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L908
116  * Read 0x18000000. Get 0x16044330: chip 4330 rev 4
117  * AXI chip, call  brcmf_chip_dmp_erom_scan() to get info about all cores.
118  * Then  brcmf_chip_cores_check() to check that CPU and RAM are found,
119  *
120  * Setting cores to passive: not clear which of CR4/CA7/CM3 our chip has.
121  *  Quite a few r/w calls to different parts of the chip to reset cores....
122  * Finally get_raminfo() called to fill in RAM info:
123  * brcmf_chip_get_raminfo: RAM: base=0x0 size=294912 (0x48000) sr=0 (0x0)
124  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L700
125  *
126  * Then brcmf_chip_setup() is called, this prints and fills in chipcommon rev and PMU caps:
127  *   brcmf_chip_setup: ccrev=39, pmurev=12, pmucaps=0x19583c0c
128  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1015
129  *  Bus-specific setup code is NOP for SDIO.
130  *
131  * brcmf_sdio_kso_init() is called.
132  * Here it first reads 0x1 from SBSDIO_FUNC1_SLEEPCSR 0x18000650 and then writes it back... WTF?
133  *
134  * brcmf_sdio_drivestrengthinit() is called
135  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L3630
136  *
137  * Set card control so an SDIO card reset does a WLAN backplane reset
138  * set PMUControl so a backplane reset does PMU state reload
139  * === end of brcmf_sdio_probe_attach ===
140
141  **** Finished reading at http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4152, line 2025 in the dump
142
143  * === How register reading works ===
144  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L357
145  * The address to read from is written to three byte-sized registers of F1:
146  *  - SBSDIO_FUNC1_SBADDRLOW  0x1000A
147  *  - SBSDIO_FUNC1_SBADDRMID  0x1000B
148  *  - SBSDIO_FUNC1_SBADDRHIGH 0x1000C
149  * If this is 32-bit read , a flag is set. The address is ANDed with SBSDIO_SB_OFT_ADDR_MASK which is 0x07FFF.
150  * Then brcmf_sdiod_regrw_helper() is called to read the reply.
151  * http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L306
152  * Based on the address it figures out where to read it from (CCCR / FBR in F0, or somewhere in F1).
153  * Reads are retried three times.
154  * 1-byte IO is done with CMD52, more is read with CMD53 with address increment (not FIFO mode).
155  * http://lxr.free-electrons.com/source/drivers/mmc/core/sdio_io.c#L458
156  * ==================================
157  *
158  *
159  */
160
161 /* BRCM-specific functions */
162 #define SDIOH_API_ACCESS_RETRY_LIMIT    2
163 #define SI_ENUM_BASE            0x18000000
164 #define REPLY_MAGIC             0x16044330
165 #define brcmf_err(fmt, ...) brcmf_dbg(0, fmt, ##__VA_ARGS__)
166 #define brcmf_dbg(level, fmt, ...) printf(fmt, ##__VA_ARGS__)
167
168 struct brcmf_sdio_dev {
169         struct cam_device *cam_dev;
170         u32 sbwad;                      /* Save backplane window address */
171         struct brcmf_bus *bus_if;
172         enum brcmf_sdiod_state state;
173         struct sdio_func *func[8];
174 };
175
176 void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
177 void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
178                               enum brcmf_sdiod_state state);
179 static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, u32 addr,
180                                     u8 regsz, void *data, bool write);
181 static int brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address);
182 static int brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr);
183 u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret);
184
185 static void bailout(int ret);
186
187 static void
188 bailout(int ret) {
189         if (ret == 0)
190                 return;
191         errx(1, "Operation returned error %d", ret);
192 }
193
194 void
195 brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state)
196 {
197         bus->state = state;
198 }
199
200 void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
201                               enum brcmf_sdiod_state state)
202 {
203         if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM ||
204             state == sdiodev->state)
205                 return;
206
207         //brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state);
208         switch (sdiodev->state) {
209         case BRCMF_SDIOD_DATA:
210                 /* any other state means bus interface is down */
211                 brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN);
212                 break;
213         case BRCMF_SDIOD_DOWN:
214                 /* transition from DOWN to DATA means bus interface is up */
215                 if (state == BRCMF_SDIOD_DATA)
216                         brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP);
217                 break;
218         default:
219                 break;
220         }
221         sdiodev->state = state;
222 }
223
224 static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func,
225                                         uint regaddr, u8 byte) {
226         int err_ret;
227
228         /*
229          * Can only directly write to some F0 registers.
230          * Handle CCCR_IENx and CCCR_ABORT command
231          * as a special case.
232          */
233         if ((regaddr == SDIO_CCCR_ABORT) ||
234             (regaddr == SDIO_CCCR_IENx))
235                 sdio_writeb(func, byte, regaddr, &err_ret);
236         else
237                 sdio_f0_writeb(func, byte, regaddr, &err_ret);
238
239         return err_ret;
240 }
241
242 static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, u32 addr, u8 regsz, void *data, bool write)
243 {
244         struct sdio_func *func;
245         int ret = -EINVAL;
246
247         brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
248                   write, fn, addr, regsz);
249
250         /* only allow byte access on F0 */
251         if (WARN_ON(regsz > 1 && !fn))
252                 return -EINVAL;
253         func = sdiodev->func[fn];
254
255         switch (regsz) {
256         case sizeof(u8):
257                 if (write) {
258                         if (fn)
259                                 sdio_writeb(func, *(u8 *)data, addr, &ret);
260                         else
261                                 ret = brcmf_sdiod_f0_writeb(func, addr,
262                                                             *(u8 *)data);
263                 } else {
264                         if (fn)
265                                 *(u8 *)data = sdio_readb(func, addr, &ret);
266                         else
267                                 *(u8 *)data = sdio_f0_readb(func, addr, &ret);
268                 }
269                 break;
270         case sizeof(u16):
271                 if (write)
272                         sdio_writew(func, *(u16 *)data, addr, &ret);
273                 else
274                         *(u16 *)data = sdio_readw(func, addr, &ret);
275                 break;
276         case sizeof(u32):
277                 if (write)
278                         sdio_writel(func, *(u32 *)data, addr, &ret);
279                 else
280                         *(u32 *)data = sdio_readl(func, addr, &ret);
281                 break;
282         default:
283                 brcmf_err("invalid size: %d\n", regsz);
284                 break;
285         }
286
287         if (ret)
288                 brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
289                           write ? "write" : "read", fn, addr, ret);
290
291         return ret;
292 }
293
294 static int
295 brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr)
296 {
297         uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK;
298         int err = 0;
299
300         if (bar0 != sdiodev->sbwad) {
301                 err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0);
302                 if (err)
303                         return err;
304
305                 sdiodev->sbwad = bar0;
306         }
307
308         *addr &= SBSDIO_SB_OFT_ADDR_MASK;
309
310         if (width == 4)
311                 *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
312
313         return 0;
314 }
315
316 static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) {
317         u8 func;
318         s32 retry = 0;
319         int ret;
320
321         if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
322                 return -ENOMEDIUM;
323
324         /*
325          * figure out how to read the register based on address range
326          * 0x00 ~ 0x7FF: function 0 CCCR and FBR
327          * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers
328          * The rest: function 1 silicon backplane core registers
329          */
330         if ((addr & ~REG_F0_REG_MASK) == 0)
331                 func = SDIO_FUNC_0;
332         else
333                 func = SDIO_FUNC_1;
334
335         do {
336                 if (!write)
337                         memset(data, 0, regsz);
338                 /* for retry wait for 1 ms till bus get settled down */
339                 if (retry)
340                         usleep_range(1000, 2000);
341                 ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz,
342                                                data, write);
343         } while (ret != 0 && ret != -ENOMEDIUM &&
344                  retry++ < SDIOH_API_ACCESS_RETRY_LIMIT);
345
346         if (ret == -ENOMEDIUM)
347                 brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
348         else if (ret != 0) {
349                 /*
350                  * SleepCSR register access can fail when
351                  * waking up the device so reduce this noise
352                  * in the logs.
353                  */
354                 if (addr != SBSDIO_FUNC1_SLEEPCSR)
355                         brcmf_err("failed to %s data F%d@0x%05x, err: %d\n",
356                                   write ? "write" : "read", func, addr, ret);
357                 else
358                         brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n",
359                                   write ? "write" : "read", func, addr, ret);
360         }
361         return ret;
362 }
363
364 static int
365 brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address)
366 {
367         int err = 0, i;
368         u8 addr[3];
369
370         if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM)
371                 return -ENOMEDIUM;
372
373         addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK;
374         addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK;
375         addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK;
376
377         for (i = 0; i < 3; i++) {
378                 err = brcmf_sdiod_regrw_helper(sdiodev,
379                                                SBSDIO_FUNC1_SBADDRLOW + i,
380                                                sizeof(u8), &addr[i], true);
381                 if (err) {
382                         brcmf_err("failed at addr: 0x%0x\n",
383                                   SBSDIO_FUNC1_SBADDRLOW + i);
384                         break;
385                 }
386         }
387
388         return err;
389 }
390
391 u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
392 {
393         u32 data = 0;
394         int retval;
395
396         brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
397         retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr);
398         if (retval)
399                 goto done;
400         retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data,
401                                           false);
402         brcmf_dbg(SDIO, "data:0x%08x\n", data);
403
404 done:
405         if (ret)
406                 *ret = retval;
407
408         return data;
409 }
410
411 /********************************************************/
412 __unused
413 static void
414 probe_bcrm(struct cam_device *dev) {
415         uint32_t cis_addr;
416         struct cis_info info;
417
418         sdio_card_set_bus_width(dev, bus_width_4);
419         cis_addr = sdio_get_common_cis_addr(dev);
420         printf("CIS address: %04X\n", cis_addr);
421
422         memset(&info, 0, sizeof(info));
423         sdio_func_read_cis(dev, 0, cis_addr, &info);
424         printf("Vendor 0x%04X product 0x%04X\n", info.man_id, info.prod_id);
425 }
426
427 __unused static uint8_t*
428 mmap_fw() {
429         const char fw_path[] = "/home/kibab/repos/fbsd-bbb/brcm-firmware/brcmfmac4330-sdio.bin";
430         struct stat sb;
431         uint8_t *fw_ptr;
432
433         int fd = open(fw_path, O_RDONLY);
434         if (fd < 0)
435                 errx(1, "Cannot open firmware file");
436         if (fstat(fd, &sb) < 0)
437                 errx(1, "Cannot get file stat");
438         fw_ptr = mmap(NULL, sb.st_size, PROT_READ, 0, fd, 0);
439         if (fw_ptr == MAP_FAILED)
440                 errx(1, "Cannot map the file");
441
442         return fw_ptr;
443 }
444
445 static void
446 usage() {
447         printf("sdiotool -u <pass_dev_unit>\n");
448         exit(0);
449 }
450
451 struct card_info {
452         uint8_t num_funcs;
453         struct cis_info f[8];
454 };
455
456 /*
457  * TODO: We should add SDIO card info about at least number of
458  * available functions to struct cam_device and use it instead
459  * of checking for man_id = 0x00 for detecting number of functions
460  */
461 static void
462 get_sdio_card_info(struct cam_device *dev, struct card_info *ci) {
463         uint32_t cis_addr;
464         uint32_t fbr_addr;
465         int ret;
466
467         cis_addr = sdio_get_common_cis_addr(dev);
468
469         memset(ci, 0, sizeof(struct card_info));
470         sdio_func_read_cis(dev, 0, cis_addr, &ci->f[0]);
471         printf("F0: Vendor 0x%04X product 0x%04X max block size %d bytes\n",
472                ci->f[0].man_id, ci->f[0].prod_id, ci->f[0].max_block_size);
473         for (int i = 1; i <= 7; i++) {
474                 fbr_addr = SD_IO_FBR_START * i + 0x9;
475                 cis_addr =  sdio_read_1(dev, 0, fbr_addr++, &ret);bailout(ret);
476                 cis_addr |= sdio_read_1(dev, 0, fbr_addr++, &ret) << 8;
477                 cis_addr |= sdio_read_1(dev, 0, fbr_addr++, &ret) << 16;
478                 sdio_func_read_cis(dev, i, cis_addr, &ci->f[i]);
479                 printf("F%d: Vendor 0x%04X product 0x%04X max block size %d bytes\n",
480                        i, ci->f[i].man_id, ci->f[i].prod_id, ci->f[i].max_block_size);
481                 if (ci->f[i].man_id == 0) {
482                         printf("F%d doesn't exist\n", i);
483                         break;
484                 }
485                 ci->num_funcs++;
486         }
487 }
488
489 int
490 main(int argc, char **argv) {
491         char device[] = "pass";
492         int unit = 0;
493         int func = 0;
494         __unused uint8_t *fw_ptr;
495         int ch;
496         struct cam_device *cam_dev;
497         int ret;
498         struct card_info ci;
499
500         //fw_ptr = mmap_fw();
501
502         while ((ch = getopt(argc, argv, "fu:")) != -1) {
503                 switch (ch) {
504                 case 'u':
505                         unit = (int) strtol(optarg, NULL, 10);
506                         break;
507                 case 'f':
508                         func = (int) strtol(optarg, NULL, 10);
509                         break;
510                 case '?':
511                 default:
512                         usage();
513                 }
514         }
515         argc -= optind;
516         argv += optind;
517
518         if ((cam_dev = cam_open_spec_device(device, unit, O_RDWR, NULL)) == NULL)
519                 errx(1, "Cannot open device");
520
521         get_sdio_card_info(cam_dev, &ci);
522
523         /* For now, everything non-broadcom is out of the question */
524         if (ci.f[0].man_id != 0x02D0) {
525                 printf("The card is not a Broadcom device\n");
526                 exit(1);
527         }
528         /* Init structures */
529         struct brcmf_sdio_dev brcmf_dev;
530         struct brcmf_bus bus_if;
531         struct sdio_func f0, f1, f2;
532         bus_if.state = BRCMF_BUS_DOWN;
533         brcmf_dev.cam_dev = cam_dev;
534         brcmf_dev.bus_if = &bus_if;
535         brcmf_dev.state = BRCMF_SDIOD_DOWN;
536
537         /* Fill in functions */
538         brcmf_dev.func[0] = &f0;
539         brcmf_dev.func[1] = &f1;
540         brcmf_dev.func[2] = &f2;
541
542         brcmf_dev.func[0]->dev = brcmf_dev.func[1]->dev
543                 = brcmf_dev.func[2]->dev = cam_dev;
544         brcmf_dev.func[0]->num = 0;
545         brcmf_dev.func[1]->num = 1;
546         brcmf_dev.func[2]->num = 2;
547
548         ret = sdio_func_enable(cam_dev, 1, 1);bailout(ret);
549         uint32_t magic = brcmf_sdiod_regrl(&brcmf_dev, 0x18000000, &ret);
550         printf("Magic = %08x\n", magic);
551         if (magic != REPLY_MAGIC) {
552                 errx(1, "Reply magic is incorrect: expected %08x, got %08x",
553                      REPLY_MAGIC, magic);
554         }
555         cam_close_spec_device(cam_dev);
556 }