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