Added new tool for doing experiments with SDIO card.
Due to its experimental nature, it's not yet connected to the build. Submitted by: Ilya Babulin
This commit is contained in:
parent
0e5f9a6dc4
commit
075b75a28b
9
usr.bin/sdiotool/Makefile
Normal file
9
usr.bin/sdiotool/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= sdiotool
|
||||
SRCS= sdiotool.c
|
||||
|
||||
LIBADD= cam util
|
||||
MAN=
|
||||
|
||||
.include <bsd.prog.mk>
|
649
usr.bin/sdiotool/sdiotool.c
Normal file
649
usr.bin/sdiotool/sdiotool.c
Normal file
@ -0,0 +1,649 @@
|
||||
/*-
|
||||
* Copyright (c) 2016-2017 Ilya Bakulin
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/endian.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <libutil.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cam/cam.h>
|
||||
#include <cam/cam_debug.h>
|
||||
#include <cam/cam_ccb.h>
|
||||
#include <cam/mmc/mmc_all.h>
|
||||
#include <camlib.h>
|
||||
|
||||
struct cis_info {
|
||||
uint16_t man_id;
|
||||
uint16_t prod_id;
|
||||
uint16_t max_block_size;
|
||||
};
|
||||
|
||||
static int sdio_rw_direct(struct cam_device *dev,
|
||||
uint8_t func_number,
|
||||
uint32_t addr,
|
||||
uint8_t is_write,
|
||||
uint8_t *data,
|
||||
uint8_t *resp);
|
||||
static uint8_t sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr);
|
||||
static void sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val);
|
||||
static int sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab);
|
||||
static int sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab);
|
||||
static int sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable);
|
||||
static int sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab);
|
||||
static int sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable);
|
||||
static void sdio_card_reset(struct cam_device *dev);
|
||||
static uint32_t sdio_get_common_cis_addr(struct cam_device *dev);
|
||||
static void probe_bcrm(struct cam_device *dev);
|
||||
|
||||
/* Use CMD52 to read or write a single byte */
|
||||
int
|
||||
sdio_rw_direct(struct cam_device *dev,
|
||||
uint8_t func_number,
|
||||
uint32_t addr,
|
||||
uint8_t is_write,
|
||||
uint8_t *data, uint8_t *resp) {
|
||||
union ccb *ccb;
|
||||
uint32_t flags;
|
||||
uint32_t arg;
|
||||
int retval = 0;
|
||||
|
||||
ccb = cam_getccb(dev);
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating CCB", __func__);
|
||||
return (1);
|
||||
}
|
||||
bzero(&(&ccb->ccb_h)[1],
|
||||
sizeof(union ccb) - sizeof(struct ccb_hdr));
|
||||
|
||||
flags = MMC_RSP_R5 | MMC_CMD_AC;
|
||||
arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
|
||||
if (is_write)
|
||||
arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
|
||||
|
||||
cam_fill_mmcio(&ccb->mmcio,
|
||||
/*retries*/ 0,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*flags*/ CAM_DIR_NONE,
|
||||
/*mmc_opcode*/ SD_IO_RW_DIRECT,
|
||||
/*mmc_arg*/ arg,
|
||||
/*mmc_flags*/ flags,
|
||||
/*mmc_data*/ 0,
|
||||
/*timeout*/ 5000);
|
||||
|
||||
if (((retval = cam_send_ccb(dev, ccb)) < 0)
|
||||
|| ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
|
||||
const char warnstr[] = "error sending command";
|
||||
|
||||
if (retval < 0)
|
||||
warn(warnstr);
|
||||
else
|
||||
warnx(warnstr);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*resp = ccb->mmcio.cmd.resp[0] & 0xFF;
|
||||
cam_freeccb(ccb);
|
||||
return (retval);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* CMD53 -- IO_RW_EXTENDED
|
||||
* Use to read or write memory blocks
|
||||
*
|
||||
* is_increment=1: FIFO mode
|
||||
* blk_count > 0: block mode
|
||||
*/
|
||||
int
|
||||
sdio_rw_extended(struct cam_device *dev,
|
||||
uint8_t func_number,
|
||||
uint32_t addr,
|
||||
uint8_t is_write,
|
||||
uint8_t *data, size_t datalen,
|
||||
uint8_t is_increment,
|
||||
uint16_t blk_count) {
|
||||
union ccb *ccb;
|
||||
uint32_t flags;
|
||||
uint32_t arg;
|
||||
int retval = 0;
|
||||
|
||||
if (blk_count != 0) {
|
||||
warnx("%s: block mode is not supported yet", __func__);
|
||||
return (1);
|
||||
}
|
||||
|
||||
ccb = cam_getccb(dev);
|
||||
if (ccb == NULL) {
|
||||
warnx("%s: error allocating CCB", __func__);
|
||||
return (1);
|
||||
}
|
||||
bzero(&(&ccb->ccb_h)[1],
|
||||
sizeof(union ccb) - sizeof(struct ccb_hdr));
|
||||
|
||||
flags = MMC_RSP_R5 | MMC_CMD_AC;
|
||||
arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
|
||||
if (is_write)
|
||||
arg |= SD_IO_RW_WR;
|
||||
|
||||
cam_fill_mmcio(&ccb->mmcio,
|
||||
/*retries*/ 0,
|
||||
/*cbfcnp*/ NULL,
|
||||
/*flags*/ CAM_DIR_NONE,
|
||||
/*mmc_opcode*/ SD_IO_RW_DIRECT,
|
||||
/*mmc_arg*/ arg,
|
||||
/*mmc_flags*/ flags,
|
||||
/*mmc_data*/ 0,
|
||||
/*timeout*/ 5000);
|
||||
|
||||
if (((retval = cam_send_ccb(dev, ccb)) < 0)
|
||||
|| ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
|
||||
const char warnstr[] = "error sending command";
|
||||
|
||||
if (retval < 0)
|
||||
warn(warnstr);
|
||||
else
|
||||
warnx(warnstr);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*resp = ccb->mmcio.cmd.resp[0] & 0xFF;
|
||||
cam_freeccb(ccb);
|
||||
return (retval);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
sdio_read_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, uint8_t *is_enab) {
|
||||
uint8_t resp;
|
||||
int ret;
|
||||
|
||||
ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*is_enab = (resp & (1 << func_number)) > 0 ? 1 : 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_set_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, int enable) {
|
||||
uint8_t resp;
|
||||
int ret;
|
||||
uint8_t is_enabled;
|
||||
|
||||
ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
is_enabled = resp & (1 << func_number);
|
||||
if ((is_enabled !=0 && enable == 1) || (is_enabled == 0 && enable == 0))
|
||||
return 0;
|
||||
|
||||
if (enable)
|
||||
resp |= 1 << func_number;
|
||||
else
|
||||
resp &= ~ (1 << func_number);
|
||||
|
||||
ret = sdio_rw_direct(dev, 0, addr, 1, &resp, &resp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr) {
|
||||
uint8_t val;
|
||||
sdio_rw_direct(dev, func_number, addr, 0, NULL, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
__unused static void
|
||||
sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val) {
|
||||
uint8_t _val;
|
||||
sdio_rw_direct(dev, func_number, addr, 0, &val, &_val);
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
|
||||
return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_READY, func_number, is_enab);
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
|
||||
return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, is_enab);
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable) {
|
||||
return sdio_set_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, enable);
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
|
||||
return sdio_read_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, is_enab);
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable) {
|
||||
return sdio_set_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, enable);
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_card_set_bus_width(struct cam_device *dev, enum mmc_bus_width bw) {
|
||||
int ret;
|
||||
uint8_t ctl_val;
|
||||
ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 0, NULL, &ctl_val);
|
||||
if (ret < 0) {
|
||||
warn("Error getting CCCR_BUS_WIDTH value");
|
||||
return ret;
|
||||
}
|
||||
ctl_val &= ~0x3;
|
||||
switch (bw) {
|
||||
case bus_width_1:
|
||||
/* Already set to 1-bit */
|
||||
break;
|
||||
case bus_width_4:
|
||||
ctl_val |= CCCR_BUS_WIDTH_4;
|
||||
break;
|
||||
case bus_width_8:
|
||||
warn("Cannot do 8-bit on SDIO yet");
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 1, &ctl_val, &ctl_val);
|
||||
if (ret < 0) {
|
||||
warn("Error setting CCCR_BUS_WIDTH value");
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
sdio_func_read_cis(struct cam_device *dev, uint8_t func_number,
|
||||
uint32_t cis_addr, struct cis_info *info) {
|
||||
uint8_t tuple_id, tuple_len, tuple_count;
|
||||
uint32_t addr;
|
||||
|
||||
char *cis1_info[4];
|
||||
int start, i, ch, count;
|
||||
char cis1_info_buf[256];
|
||||
|
||||
tuple_count = 0; /* Use to prevent infinite loop in case of parse errors */
|
||||
memset(cis1_info_buf, 0, 256);
|
||||
do {
|
||||
addr = cis_addr;
|
||||
tuple_id = sdio_read_1(dev, 0, addr++);
|
||||
if (tuple_id == SD_IO_CISTPL_END)
|
||||
break;
|
||||
if (tuple_id == 0) {
|
||||
cis_addr++;
|
||||
continue;
|
||||
}
|
||||
tuple_len = sdio_read_1(dev, 0, addr++);
|
||||
if (tuple_len == 0 && tuple_id != 0x00) {
|
||||
warn("Parse error: 0-length tuple %02X\n", tuple_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (tuple_id) {
|
||||
case SD_IO_CISTPL_VERS_1:
|
||||
addr += 2;
|
||||
for (count = 0, start = 0, i = 0;
|
||||
(count < 4) && ((i + 4) < 256); i++) {
|
||||
ch = sdio_read_1(dev, 0, addr + i);
|
||||
printf("count=%d, start=%d, i=%d, Got %c (0x%02x)\n", count, start, i, ch, ch);
|
||||
if (ch == 0xff)
|
||||
break;
|
||||
cis1_info_buf[i] = ch;
|
||||
if (ch == 0) {
|
||||
cis1_info[count] =
|
||||
cis1_info_buf + start;
|
||||
start = i + 1;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
printf("Card info:");
|
||||
for (i=0; i<4; i++)
|
||||
if (cis1_info[i])
|
||||
printf(" %s", cis1_info[i]);
|
||||
printf("\n");
|
||||
break;
|
||||
case SD_IO_CISTPL_MANFID:
|
||||
info->man_id = sdio_read_1(dev, 0, addr++);
|
||||
info->man_id |= sdio_read_1(dev, 0, addr++) << 8;
|
||||
|
||||
info->prod_id = sdio_read_1(dev, 0, addr++);
|
||||
info->prod_id |= sdio_read_1(dev, 0, addr++) << 8;
|
||||
break;
|
||||
case SD_IO_CISTPL_FUNCID:
|
||||
/* not sure if we need to parse it? */
|
||||
break;
|
||||
case SD_IO_CISTPL_FUNCE:
|
||||
if (tuple_len < 4) {
|
||||
printf("FUNCE is too short: %d\n", tuple_len);
|
||||
break;
|
||||
}
|
||||
if (func_number == 0) {
|
||||
/* skip extended_data */
|
||||
addr++;
|
||||
info->max_block_size = sdio_read_1(dev, 0, addr++);
|
||||
info->max_block_size |= sdio_read_1(dev, 0, addr++) << 8;
|
||||
} else {
|
||||
info->max_block_size = sdio_read_1(dev, 0, addr + 0xC);
|
||||
info->max_block_size |= sdio_read_1(dev, 0, addr + 0xD) << 8;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Skipping tuple ID %02X len %02X\n", tuple_id, tuple_len);
|
||||
}
|
||||
cis_addr += tuple_len + 2;
|
||||
tuple_count++;
|
||||
} while (tuple_count < 20);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
sdio_get_common_cis_addr(struct cam_device *dev) {
|
||||
uint32_t addr;
|
||||
|
||||
addr = sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR);
|
||||
addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 1) << 8;
|
||||
addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 2) << 16;
|
||||
|
||||
if (addr < SD_IO_CIS_START || addr > SD_IO_CIS_START + SD_IO_CIS_SIZE) {
|
||||
warn("Bad CIS address: %04X\n", addr);
|
||||
addr = 0;
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
static void sdio_card_reset(struct cam_device *dev) {
|
||||
int ret;
|
||||
uint8_t ctl_val;
|
||||
ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 0, NULL, &ctl_val);
|
||||
if (ret < 0)
|
||||
errx(1, "Error getting CCCR_CTL value");
|
||||
ctl_val |= CCCR_CTL_RES;
|
||||
ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 1, &ctl_val, &ctl_val);
|
||||
if (ret < 0)
|
||||
errx(1, "Error setting CCCR_CTL value");
|
||||
}
|
||||
|
||||
/*
|
||||
* How Linux driver works
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* The driver does black magic by copying func struct for F2 and setting func number to zero there, to create an F0 func structure :)
|
||||
* Driver state changes to BRCMF_SDIOD_DOWN.
|
||||
* 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).
|
||||
*
|
||||
* 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!
|
||||
* Then it increases timeout for F2 (what is this?!)
|
||||
* Then it enables F1
|
||||
* Then it attaches "freezer" (without PM this is NOP)
|
||||
* Finally it calls brcmf_sdio_probe() http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4082
|
||||
*
|
||||
* Here high-level workqueues and sg tables are allocated.
|
||||
* It then calls brcmf_sdio_probe_attach()
|
||||
*
|
||||
* Here at the beginning there is a pr_debug() call with brcmf_sdiod_regrl() inside to addr #define SI_ENUM_BASE 0x18000000.
|
||||
* Return value is 0x16044330.
|
||||
* Then turns off PLL: byte-write BRCMF_INIT_CLKCTL1 (0x28) -> SBSDIO_FUNC1_CHIPCLKCSR (0x1000E)
|
||||
* Then it reads value back, should be 0xe8.
|
||||
* Then calls brcmf_chip_attach()
|
||||
*
|
||||
* http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1054
|
||||
* This func enumerates and resets all the cores on the dongle.
|
||||
* - brcmf_sdio_buscoreprep(): force clock to ALPAvail req only:
|
||||
* SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ -> SBSDIO_FUNC1_CHIPCLKCSR
|
||||
* Wait up to 15ms to !SBSDIO_ALPAV(clkval) of the value from CLKCSR.
|
||||
* Force ALP:
|
||||
* SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP (0x21)-> SBSDIO_FUNC1_CHIPCLKCSR
|
||||
* Disaable SDIO pullups:
|
||||
* byte 0 -> SBSDIO_FUNC1_SDIOPULLUP (0x0001000f)
|
||||
*
|
||||
* Calls brcmf_chip_recognition()
|
||||
* http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L908
|
||||
* Read 0x18000000. Get 0x16044330: chip 4330 rev 4
|
||||
* AXI chip, call brcmf_chip_dmp_erom_scan() to get info about all cores.
|
||||
* Then brcmf_chip_cores_check() to check that CPU and RAM are found,
|
||||
*
|
||||
* Setting cores to passive: not clear which of CR4/CA7/CM3 our chip has.
|
||||
* Quite a few r/w calls to different parts of the chip to reset cores....
|
||||
* Finally get_raminfo() called to fill in RAM info:
|
||||
* brcmf_chip_get_raminfo: RAM: base=0x0 size=294912 (0x48000) sr=0 (0x0)
|
||||
* http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L700
|
||||
*
|
||||
* Then brcmf_chip_setup() is called, this prints and fills in chipcommon rev and PMU caps:
|
||||
* brcmf_chip_setup: ccrev=39, pmurev=12, pmucaps=0x19583c0c
|
||||
* http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c#L1015
|
||||
* Bus-specific setup code is NOP for SDIO.
|
||||
*
|
||||
* brcmf_sdio_kso_init() is called.
|
||||
* Here it first reads 0x1 from SBSDIO_FUNC1_SLEEPCSR 0x18000650 and then writes it back... WTF?
|
||||
*
|
||||
* brcmf_sdio_drivestrengthinit() is called
|
||||
* http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L3630
|
||||
*
|
||||
* Set card control so an SDIO card reset does a WLAN backplane reset
|
||||
* set PMUControl so a backplane reset does PMU state reload
|
||||
* === end of brcmf_sdio_probe_attach ===
|
||||
|
||||
**** Finished reading at http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c#L4152, line 2025 in the dump
|
||||
|
||||
* === How register reading works ===
|
||||
* http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L357
|
||||
* The address to read from is written to three byte-sized registers of F1:
|
||||
* - SBSDIO_FUNC1_SBADDRLOW 0x1000A
|
||||
* - SBSDIO_FUNC1_SBADDRMID 0x1000B
|
||||
* - SBSDIO_FUNC1_SBADDRHIGH 0x1000C
|
||||
* If this is 32-bit read , a flag is set. The address is ANDed with SBSDIO_SB_OFT_ADDR_MASK which is 0x07FFF.
|
||||
* Then brcmf_sdiod_regrw_helper() is called to read the reply.
|
||||
* http://lxr.free-electrons.com/source/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c#L306
|
||||
* Based on the address it figures out where to read it from (CCCR / FBR in F0, or somewhere in F1).
|
||||
* Reads are retried three times.
|
||||
* 1-byte IO is done with CMD52, more is read with CMD53 with address increment (not FIFO mode).
|
||||
* http://lxr.free-electrons.com/source/drivers/mmc/core/sdio_io.c#L458
|
||||
* ==================================
|
||||
*
|
||||
*
|
||||
*/
|
||||
__unused
|
||||
static void
|
||||
probe_bcrm(struct cam_device *dev) {
|
||||
uint32_t cis_addr;
|
||||
struct cis_info info;
|
||||
|
||||
sdio_card_set_bus_width(dev, bus_width_4);
|
||||
cis_addr = sdio_get_common_cis_addr(dev);
|
||||
printf("CIS address: %04X\n", cis_addr);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
sdio_func_read_cis(dev, 0, cis_addr, &info);
|
||||
printf("Vendor 0x%04X product 0x%04X\n", info.man_id, info.prod_id);
|
||||
}
|
||||
__unused
|
||||
static uint8_t *
|
||||
mmap_fw() {
|
||||
const char fw_path[] = "/home/kibab/repos/fbsd-bbb/brcm-firmware/brcmfmac4330-sdio.bin";
|
||||
struct stat sb;
|
||||
uint8_t *fw_ptr;
|
||||
|
||||
int fd = open(fw_path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
errx(1, "Cannot open firmware file");
|
||||
if (fstat(fd, &sb) < 0)
|
||||
errx(1, "Cannot get file stat");
|
||||
fw_ptr = mmap(NULL, sb.st_size, PROT_READ, 0, fd, 0);
|
||||
if (fw_ptr == MAP_FAILED)
|
||||
errx(1, "Cannot map the file");
|
||||
|
||||
return fw_ptr;
|
||||
}
|
||||
|
||||
static void
|
||||
usage() {
|
||||
printf("sdiotool -u <pass_dev_unit>\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void
|
||||
get_sdio_card_info(struct cam_device *dev) {
|
||||
uint32_t cis_addr;
|
||||
uint32_t fbr_addr;
|
||||
struct cis_info info;
|
||||
|
||||
cis_addr = sdio_get_common_cis_addr(dev);
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
sdio_func_read_cis(dev, 0, cis_addr, &info);
|
||||
printf("F0: Vendor 0x%04X product 0x%04X max block size %d bytes\n",
|
||||
info.man_id, info.prod_id, info.max_block_size);
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
fbr_addr = SD_IO_FBR_START * i + 0x9;
|
||||
cis_addr = sdio_read_1(dev, 0, fbr_addr++);
|
||||
cis_addr |= sdio_read_1(dev, 0, fbr_addr++) << 8;
|
||||
cis_addr |= sdio_read_1(dev, 0, fbr_addr++) << 16;
|
||||
memset(&info, 0, sizeof(info));
|
||||
sdio_func_read_cis(dev, i, cis_addr, &info);
|
||||
printf("F%d: Vendor 0x%04X product 0x%04X max block size %d bytes\n",
|
||||
i, info.man_id, info.prod_id, info.max_block_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Test interrupt delivery when select() */
|
||||
__unused static int
|
||||
sdio_signal_intr(struct cam_device *dev) {
|
||||
uint8_t resp;
|
||||
int ret;
|
||||
|
||||
ret = sdio_rw_direct(dev, 0, 0x666, 0, NULL, &resp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
do_intr_test(__unused struct cam_device *dev) {
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv) {
|
||||
char device[] = "pass";
|
||||
int unit = 0;
|
||||
int func = 0;
|
||||
uint8_t resp;
|
||||
uint8_t is_enab;
|
||||
__unused uint8_t *fw_ptr;
|
||||
int ch;
|
||||
struct cam_device *cam_dev;
|
||||
int is_intr_test = 0;
|
||||
|
||||
//fw_ptr = mmap_fw();
|
||||
|
||||
while ((ch = getopt(argc, argv, "Iu:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'u':
|
||||
unit = (int) strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'f':
|
||||
func = (int) strtol(optarg, NULL, 10);
|
||||
case 'I':
|
||||
is_intr_test = 1;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if ((cam_dev = cam_open_spec_device(device, unit, O_RDWR, NULL)) == NULL)
|
||||
errx(1, "Cannot open device");
|
||||
|
||||
get_sdio_card_info(cam_dev);
|
||||
if (is_intr_test > 0)
|
||||
do_intr_test(cam_dev);
|
||||
exit(0);
|
||||
sdio_card_reset(cam_dev);
|
||||
|
||||
/* Read Addr 7 of func 0 */
|
||||
int ret = sdio_rw_direct(cam_dev, 0, 7, 0, NULL, &resp);
|
||||
if (ret < 0)
|
||||
errx(1, "Error sending CAM command");
|
||||
printf("Result: %02x\n", resp);
|
||||
|
||||
/* Check if func 1 is enabled */
|
||||
ret = sdio_is_func_enabled(cam_dev, 1, &is_enab);
|
||||
if (ret < 0)
|
||||
errx(1, "Cannot check if func is enabled");
|
||||
printf("F1 enabled: %d\n", is_enab);
|
||||
ret = sdio_func_enable(cam_dev, 1, 1 - is_enab);
|
||||
if (ret < 0)
|
||||
errx(1, "Cannot enable/disable func");
|
||||
printf("F1 en/dis result: %d\n", ret);
|
||||
|
||||
/* Check if func 1 is ready */
|
||||
ret = sdio_is_func_ready(cam_dev, 1, &is_enab);
|
||||
if (ret < 0)
|
||||
errx(1, "Cannot check if func is ready");
|
||||
printf("F1 ready: %d\n", is_enab);
|
||||
|
||||
/* Check if interrupts are enabled */
|
||||
ret = sdio_is_func_intr_enabled(cam_dev, 1, &is_enab);
|
||||
if (ret < 0)
|
||||
errx(1, "Cannot check if func intr is enabled");
|
||||
printf("F1 intr enabled: %d\n", is_enab);
|
||||
ret = sdio_func_intr_enable(cam_dev, 1, 1 - is_enab);
|
||||
if (ret < 0)
|
||||
errx(1, "Cannot enable/disable func intr");
|
||||
printf("F1 intr en/dis result: %d\n", ret);
|
||||
|
||||
cam_close_spec_device(cam_dev);
|
||||
}
|
Loading…
Reference in New Issue
Block a user