From 75b996503a8a1c89d472476fec8b504b0cf74021 Mon Sep 17 00:00:00 2001 From: kibab Date: Mon, 18 Sep 2017 20:01:01 +0000 Subject: [PATCH] Make basic Broadcom I/O space reading functions work It's now possible to use Broadcom functions to read the I/O registers of SDIO card. The functions were copied from the BSD-licensed Broadcom Linux driver as-is. To make it possible, a small Linux compatibility layer was introduced. Currently the card responds with the correct version number ("magic") when reading the corresponding address. Approved by: imp (mentor) Differential Revision: https://reviews.freebsd.org/D12111 --- usr.bin/sdiotool/Makefile | 2 +- usr.bin/sdiotool/brcmfmac_bus.h | 26 + usr.bin/sdiotool/brcmfmac_sdio.h | 160 ++++++ usr.bin/sdiotool/cam_sdio.c | 440 ++++++++++++++++ usr.bin/sdiotool/cam_sdio.h | 95 ++++ usr.bin/sdiotool/linux_compat.h | 58 ++ usr.bin/sdiotool/linux_sdio_compat.c | 104 ++++ usr.bin/sdiotool/linux_sdio_compat.h | 63 +++ usr.bin/sdiotool/sdiotool.c | 759 ++++++++++++--------------- 9 files changed, 1280 insertions(+), 427 deletions(-) create mode 100644 usr.bin/sdiotool/brcmfmac_bus.h create mode 100644 usr.bin/sdiotool/brcmfmac_sdio.h create mode 100644 usr.bin/sdiotool/cam_sdio.c create mode 100644 usr.bin/sdiotool/cam_sdio.h create mode 100644 usr.bin/sdiotool/linux_compat.h create mode 100644 usr.bin/sdiotool/linux_sdio_compat.c create mode 100644 usr.bin/sdiotool/linux_sdio_compat.h diff --git a/usr.bin/sdiotool/Makefile b/usr.bin/sdiotool/Makefile index 76d091f42956..f7d1681335ed 100644 --- a/usr.bin/sdiotool/Makefile +++ b/usr.bin/sdiotool/Makefile @@ -1,7 +1,7 @@ # $FreeBSD$ PROG= sdiotool -SRCS= sdiotool.c +SRCS= sdiotool.c cam_sdio.c linux_sdio_compat.c LIBADD= cam util MAN= diff --git a/usr.bin/sdiotool/brcmfmac_bus.h b/usr.bin/sdiotool/brcmfmac_bus.h new file mode 100644 index 000000000000..2c7cb2953598 --- /dev/null +++ b/usr.bin/sdiotool/brcmfmac_bus.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ +/* The level of bus communication with the dongle */ +enum brcmf_bus_state { + BRCMF_BUS_DOWN, /* Not ready for frame transfers */ + BRCMF_BUS_UP /* Ready for frame transfers */ +}; + +struct brcmf_bus { + enum brcmf_bus_state state; +}; diff --git a/usr.bin/sdiotool/brcmfmac_sdio.h b/usr.bin/sdiotool/brcmfmac_sdio.h new file mode 100644 index 000000000000..c4d0b3756def --- /dev/null +++ b/usr.bin/sdiotool/brcmfmac_sdio.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ +#define SDIO_FUNC_0 0 +#define SDIO_FUNC_1 1 +#define SDIO_FUNC_2 2 + +#define SDIOD_FBR_SIZE 0x100 + +/* io_en */ +#define SDIO_FUNC_ENABLE_1 0x02 +#define SDIO_FUNC_ENABLE_2 0x04 + +/* io_rdys */ +#define SDIO_FUNC_READY_1 0x02 +#define SDIO_FUNC_READY_2 0x04 + +/* intr_status */ +#define INTR_STATUS_FUNC1 0x2 +#define INTR_STATUS_FUNC2 0x4 + +/* Maximum number of I/O funcs */ +#define SDIOD_MAX_IOFUNCS 7 + +/* mask of register map */ +#define REG_F0_REG_MASK 0x7FF +#define REG_F1_MISC_MASK 0x1FFFF + +/* as of sdiod rev 0, supports 3 functions */ +#define SBSDIO_NUM_FUNCTION 3 + +/* function 0 vendor specific CCCR registers */ +#define SDIO_CCCR_BRCM_CARDCAP 0xf0 +#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 +#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 +#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 +#define SDIO_CCCR_BRCM_CARDCTRL 0xf1 +#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02 +#define SDIO_CCCR_BRCM_SEPINT 0xf2 + +#define SDIO_SEPINT_MASK 0x01 +#define SDIO_SEPINT_OE 0x02 +#define SDIO_SEPINT_ACT_HI 0x04 + +/* function 1 miscellaneous registers */ + +/* sprom command and status */ +#define SBSDIO_SPROM_CS 0x10000 +/* sprom info register */ +#define SBSDIO_SPROM_INFO 0x10001 +/* sprom indirect access data byte 0 */ +#define SBSDIO_SPROM_DATA_LOW 0x10002 +/* sprom indirect access data byte 1 */ +#define SBSDIO_SPROM_DATA_HIGH 0x10003 +/* sprom indirect access addr byte 0 */ +#define SBSDIO_SPROM_ADDR_LOW 0x10004 +/* gpio select */ +#define SBSDIO_GPIO_SELECT 0x10005 +/* gpio output */ +#define SBSDIO_GPIO_OUT 0x10006 +/* gpio enable */ +#define SBSDIO_GPIO_EN 0x10007 +/* rev < 7, watermark for sdio device */ +#define SBSDIO_WATERMARK 0x10008 +/* control busy signal generation */ +#define SBSDIO_DEVICE_CTL 0x10009 + +/* SB Address Window Low (b15) */ +#define SBSDIO_FUNC1_SBADDRLOW 0x1000A +/* SB Address Window Mid (b23:b16) */ +#define SBSDIO_FUNC1_SBADDRMID 0x1000B +/* SB Address Window High (b31:b24) */ +#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C +/* Frame Control (frame term/abort) */ +#define SBSDIO_FUNC1_FRAMECTRL 0x1000D +/* ChipClockCSR (ALP/HT ctl/status) */ +#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E +/* SdioPullUp (on cmd, d0-d2) */ +#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F +/* Write Frame Byte Count Low */ +#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 +/* Write Frame Byte Count High */ +#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A +/* Read Frame Byte Count Low */ +#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B +/* Read Frame Byte Count High */ +#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C +/* MesBusyCtl (rev 11) */ +#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D +/* Sdio Core Rev 12 */ +#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1 +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1 +#define SBSDIO_FUNC1_SLEEPCSR 0x1001F +#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1 + +#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ +#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */ + +/* function 1 OCP space */ + +/* sb offset addr is <= 15 bits, 32k */ +#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF +#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 +/* with b15, maps to 32-bit SB access */ +#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 + +/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ + +#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ +#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ +#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ +/* Address bits from SBADDR regs */ +#define SBSDIO_SBWINDOW_MASK 0xffff8000 + +#define SDIOH_READ 0 /* Read request */ +#define SDIOH_WRITE 1 /* Write request */ + +#define SDIOH_DATA_FIX 0 /* Fixed addressing */ +#define SDIOH_DATA_INC 1 /* Incremental addressing */ + +/* internal return code */ +#define SUCCESS 0 +#define ERROR 1 + +/* Packet alignment for most efficient SDIO (can change based on platform) */ +#define BRCMF_SDALIGN (1 << 6) + +/** + * enum brcmf_sdiod_state - the state of the bus. + * + * @BRCMF_SDIOD_DOWN: Device can be accessed, no DPC. + * @BRCMF_SDIOD_DATA: Ready for data transfers, DPC enabled. + * @BRCMF_SDIOD_NOMEDIUM: No medium access to dongle possible. + */ +enum brcmf_sdiod_state { + BRCMF_SDIOD_DOWN, + BRCMF_SDIOD_DATA, + BRCMF_SDIOD_NOMEDIUM +}; diff --git a/usr.bin/sdiotool/cam_sdio.c b/usr.bin/sdiotool/cam_sdio.c new file mode 100644 index 000000000000..13576bbf188a --- /dev/null +++ b/usr.bin/sdiotool/cam_sdio.c @@ -0,0 +1,440 @@ +/*- + * Copyright (c) 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 +__FBSDID("$FreeBSD$"); + +#include "cam_sdio.h" + +/* 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); +} + +/* + * 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, + caddr_t data, size_t datalen, + uint8_t is_increment, + uint16_t blk_count) { + union ccb *ccb; + uint32_t flags; + uint32_t arg; + uint32_t cam_flags; + uint8_t resp; + struct mmc_data mmcd; + 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_ADTC; + arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr) | + SD_IOE_RW_LEN(datalen); + + if (is_increment) + arg |= SD_IO_RW_INCR; + + mmcd.data = data; + mmcd.len = datalen; + mmcd.xfer_len = 0; /* not used by MMCCAM */ + mmcd.mrq = NULL; /* not used by MMCCAM */ + + if (is_write) { + arg |= SD_IO_RW_WR; + cam_flags = CAM_DIR_OUT; + mmcd.flags = MMC_DATA_WRITE; + } else { + cam_flags = CAM_DIR_IN; + mmcd.flags = MMC_DATA_READ; + } + cam_fill_mmcio(&ccb->mmcio, + /*retries*/ 0, + /*cbfcnp*/ NULL, + /*flags*/ cam_flags, + /*mmc_opcode*/ SD_IO_RW_EXTENDED, + /*mmc_arg*/ arg, + /*mmc_flags*/ flags, + /*mmc_data*/ &mmcd, + /*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; + if (resp != 0) + warn("Response from CMD53 is not 0?!"); + cam_freeccb(ccb); + return (retval); +} + + +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); +} + +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; +} + +/* Conventional I/O functions */ +uint8_t +sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) { + uint8_t val; + *ret = sdio_rw_direct(dev, func_number, addr, 0, NULL, &val); + return val; +} + +int +sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val) { + uint8_t _val; + return sdio_rw_direct(dev, func_number, addr, 0, &val, &_val); +} + +uint16_t +sdio_read_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) { + uint16_t val; + *ret = sdio_rw_extended(dev, func_number, addr, + /* is_write */ 0, + /* data */ (caddr_t) &val, + /* datalen */ sizeof(val), + /* is_increment */ 1, + /* blk_count */ 0 + ); + return val; +} + + +int +sdio_write_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint16_t val) { + return sdio_rw_extended(dev, func_number, addr, + /* is_write */ 1, + /* data */ (caddr_t) &val, + /* datalen */ sizeof(val), + /* is_increment */ 1, + /* blk_count */ 0 + ); +} + +uint32_t +sdio_read_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) { + uint32_t val; + *ret = sdio_rw_extended(dev, func_number, addr, + /* is_write */ 0, + /* data */ (caddr_t) &val, + /* datalen */ sizeof(val), + /* is_increment */ 1, + /* blk_count */ 0 + ); + return val; +} + + +int +sdio_write_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint32_t val) { + return sdio_rw_extended(dev, func_number, addr, + /* is_write */ 1, + /* data */ (caddr_t) &val, + /* datalen */ sizeof(val), + /* is_increment */ 1, + /* blk_count */ 0 + ); +} + +/* Higher-level wrappers for certain management operations */ +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); +} + +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); +} + +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); +} + +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); +} + +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); +} + +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; +} + +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, ret; + 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++, &ret); + if (tuple_id == SD_IO_CISTPL_END) + break; + if (tuple_id == 0) { + cis_addr++; + continue; + } + tuple_len = sdio_read_1(dev, 0, addr++, &ret); + 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, &ret); + 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++, &ret); + info->man_id |= sdio_read_1(dev, 0, addr++, &ret) << 8; + + info->prod_id = sdio_read_1(dev, 0, addr++, &ret); + info->prod_id |= sdio_read_1(dev, 0, addr++, &ret) << 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++, &ret); + info->max_block_size |= sdio_read_1(dev, 0, addr++, &ret) << 8; + } else { + info->max_block_size = sdio_read_1(dev, 0, addr + 0xC, &ret); + info->max_block_size |= sdio_read_1(dev, 0, addr + 0xD, &ret) << 8; + } + break; + default: + warnx("Skipping tuple ID %02X len %02X\n", tuple_id, tuple_len); + } + cis_addr += tuple_len + 2; + tuple_count++; + } while (tuple_count < 20); + + return 0; +} + +uint32_t +sdio_get_common_cis_addr(struct cam_device *dev) { + uint32_t addr; + int ret; + + addr = sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR, &ret); + addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 1, &ret) << 8; + addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 2, &ret) << 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; +} + +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"); +} diff --git a/usr.bin/sdiotool/cam_sdio.h b/usr.bin/sdiotool/cam_sdio.h new file mode 100644 index 000000000000..6f57fada45a0 --- /dev/null +++ b/usr.bin/sdiotool/cam_sdio.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct cis_info { + uint16_t man_id; + uint16_t prod_id; + uint16_t max_block_size; +}; + +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); +int +sdio_rw_extended(struct cam_device *dev, + uint8_t func_number, + uint32_t addr, + uint8_t is_write, + caddr_t data, size_t datalen, + uint8_t is_increment, + uint16_t blk_count); +uint8_t sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret); +int sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val); +uint16_t sdio_read_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret); +int sdio_write_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint16_t val); +uint32_t sdio_read_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret); +int sdio_write_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint32_t val); +int sdio_read_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, uint8_t *is_enab); +int sdio_set_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, int enable); +int sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab); +int sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab); +int sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable); +int sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab); +int sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable); +void sdio_card_reset(struct cam_device *dev); +uint32_t sdio_get_common_cis_addr(struct cam_device *dev); +int sdio_func_read_cis(struct cam_device *dev, uint8_t func_number, + uint32_t cis_addr, struct cis_info *info); +int sdio_card_set_bus_width(struct cam_device *dev, enum mmc_bus_width bw); diff --git a/usr.bin/sdiotool/linux_compat.h b/usr.bin/sdiotool/linux_compat.h new file mode 100644 index 000000000000..7f49fbde0a09 --- /dev/null +++ b/usr.bin/sdiotool/linux_compat.h @@ -0,0 +1,58 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#ifndef _LINUX_COMPAT_H_ +#define _LINUX_COMPAT_H_ + +/* Linux compatibility shims */ +#define uint unsigned int +#define u32 uint32_t +#define u8 uint8_t +#define u16 uint16_t +#define s32 int32_t +#define bool int8_t +#define true 1 +#define false 0 + +#define usleep_range(a, b) usleep(a) +#define ENOMEDIUM -1 +#define EINVAL -2 + +#define WARN_ON(cond) ({ \ + bool __ret = (cond); \ + if (__ret) { \ + printf("WARNING %s failed at %s:%d\n", \ + #cond, __FILE__, __LINE__); \ + } \ + (__ret); \ +}) + +#endif diff --git a/usr.bin/sdiotool/linux_sdio_compat.c b/usr.bin/sdiotool/linux_sdio_compat.c new file mode 100644 index 000000000000..85a0a8347162 --- /dev/null +++ b/usr.bin/sdiotool/linux_sdio_compat.c @@ -0,0 +1,104 @@ +/*- + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "linux_compat.h" +#include "linux_sdio_compat.h" +#include "cam_sdio.h" + +u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret) { + return sdio_read_1(func->dev, func->num, addr, err_ret); +} + +unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr, int *err_ret) { + return sdio_readb(func, addr, err_ret); +} + +u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret) { + return sdio_read_2(func->dev, func->num, addr, err_ret); +} + +u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret) { + return sdio_read_4(func->dev, func->num, addr, err_ret); +} + +void sdio_writeb(struct sdio_func *func, u8 b, + unsigned int addr, int *err_ret) { + *err_ret = sdio_write_1(func->dev, func->num, addr, b); +} + +/* Only writes to the vendor specific CCCR registers + * (0xF0 - 0xFF) are permiited. */ +void sdio_f0_writeb(struct sdio_func *func, unsigned char b, + unsigned int addr, int *err_ret) +{ + if (addr < 0xF0 || addr > 0xFF) { + if (err_ret) + *err_ret = -EINVAL; + return; + } + sdio_writeb(func, b, addr, err_ret); +} + +void sdio_writew(struct sdio_func *func, u16 b, + unsigned int addr, int *err_ret) { + *err_ret = sdio_write_2(func->dev, func->num, addr, b); +} + +void sdio_writel(struct sdio_func *func, u32 b, + unsigned int addr, int *err_ret) { + *err_ret = sdio_write_4(func->dev, func->num, addr, b); +} diff --git a/usr.bin/sdiotool/linux_sdio_compat.h b/usr.bin/sdiotool/linux_sdio_compat.h new file mode 100644 index 000000000000..671da405d374 --- /dev/null +++ b/usr.bin/sdiotool/linux_sdio_compat.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 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 +__FBSDID("$FreeBSD$"); + +#ifndef _LINUX_SDIO_COMPAT_H_ +#define _LINUX_SDIO_COMPAT_H_ + +#include +#include "linux_compat.h" + +/* Linux SDIO stack functions and definitions */ +#define SDIO_CCCR_ABORT SD_IO_CCCR_CTL +#define SDIO_CCCR_IENx SD_IO_CCCR_INT_ENABLE + +struct sdio_func { + struct cam_device *dev; + uint8_t num; +}; + +u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); +unsigned char sdio_f0_readb(struct sdio_func *func, + unsigned int addr, int *err_ret); +u16 sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret); +u32 sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret); + +void sdio_writeb(struct sdio_func *func, u8 b, + unsigned int addr, int *err_ret); +void sdio_f0_writeb(struct sdio_func *func, unsigned char b, + unsigned int addr, int *err_ret); +void sdio_writew(struct sdio_func *func, u16 b, + unsigned int addr, int *err_ret); +void sdio_writel(struct sdio_func *func, u32 b, + unsigned int addr, int *err_ret); + + +#endif diff --git a/usr.bin/sdiotool/sdiotool.c b/usr.bin/sdiotool/sdiotool.c index f665535bcc35..4d7b6ca91094 100644 --- a/usr.bin/sdiotool/sdiotool.c +++ b/usr.bin/sdiotool/sdiotool.c @@ -23,6 +23,20 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * * $FreeBSD$ */ @@ -55,365 +69,14 @@ __FBSDID("$FreeBSD$"); #include #include -struct cis_info { - uint16_t man_id; - uint16_t prod_id; - uint16_t max_block_size; -}; +#include "linux_compat.h" +#include "linux_sdio_compat.h" +#include "cam_sdio.h" +#include "brcmfmac_sdio.h" +#include "brcmfmac_bus.h" -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 * @@ -494,6 +157,258 @@ static void sdio_card_reset(struct cam_device *dev) { * * */ + +/* BRCM-specific functions */ +#define SDIOH_API_ACCESS_RETRY_LIMIT 2 +#define SI_ENUM_BASE 0x18000000 +#define REPLY_MAGIC 0x16044330 +#define brcmf_err(fmt, ...) brcmf_dbg(0, fmt, ##__VA_ARGS__) +#define brcmf_dbg(level, fmt, ...) printf(fmt, ##__VA_ARGS__) + +struct brcmf_sdio_dev { + struct cam_device *cam_dev; + u32 sbwad; /* Save backplane window address */ + struct brcmf_bus *bus_if; + enum brcmf_sdiod_state state; + struct sdio_func *func[8]; +}; + +void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); +void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, + enum brcmf_sdiod_state state); +static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, u32 addr, + u8 regsz, void *data, bool write); +static int brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address); +static int brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr); +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); + +static void bailout(int ret); + +static void +bailout(int ret) { + if (ret == 0) + return; + errx(1, "Operation returned error %d", ret); +} + +void +brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state) +{ + bus->state = state; +} + +void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev, + enum brcmf_sdiod_state state) +{ + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM || + state == sdiodev->state) + return; + + //brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state); + switch (sdiodev->state) { + case BRCMF_SDIOD_DATA: + /* any other state means bus interface is down */ + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); + break; + case BRCMF_SDIOD_DOWN: + /* transition from DOWN to DATA means bus interface is up */ + if (state == BRCMF_SDIOD_DATA) + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP); + break; + default: + break; + } + sdiodev->state = state; +} + +static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func, + uint regaddr, u8 byte) { + int err_ret; + + /* + * Can only directly write to some F0 registers. + * Handle CCCR_IENx and CCCR_ABORT command + * as a special case. + */ + if ((regaddr == SDIO_CCCR_ABORT) || + (regaddr == SDIO_CCCR_IENx)) + sdio_writeb(func, byte, regaddr, &err_ret); + else + sdio_f0_writeb(func, byte, regaddr, &err_ret); + + return err_ret; +} + +static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, u32 addr, u8 regsz, void *data, bool write) +{ + struct sdio_func *func; + int ret = -EINVAL; + + brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", + write, fn, addr, regsz); + + /* only allow byte access on F0 */ + if (WARN_ON(regsz > 1 && !fn)) + return -EINVAL; + func = sdiodev->func[fn]; + + switch (regsz) { + case sizeof(u8): + if (write) { + if (fn) + sdio_writeb(func, *(u8 *)data, addr, &ret); + else + ret = brcmf_sdiod_f0_writeb(func, addr, + *(u8 *)data); + } else { + if (fn) + *(u8 *)data = sdio_readb(func, addr, &ret); + else + *(u8 *)data = sdio_f0_readb(func, addr, &ret); + } + break; + case sizeof(u16): + if (write) + sdio_writew(func, *(u16 *)data, addr, &ret); + else + *(u16 *)data = sdio_readw(func, addr, &ret); + break; + case sizeof(u32): + if (write) + sdio_writel(func, *(u32 *)data, addr, &ret); + else + *(u32 *)data = sdio_readl(func, addr, &ret); + break; + default: + brcmf_err("invalid size: %d\n", regsz); + break; + } + + if (ret) + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", fn, addr, ret); + + return ret; +} + +static int +brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) +{ + uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; + int err = 0; + + if (bar0 != sdiodev->sbwad) { + err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0); + if (err) + return err; + + sdiodev->sbwad = bar0; + } + + *addr &= SBSDIO_SB_OFT_ADDR_MASK; + + if (width == 4) + *addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; + + return 0; +} + +static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) { + u8 func; + s32 retry = 0; + int ret; + + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) + return -ENOMEDIUM; + + /* + * figure out how to read the register based on address range + * 0x00 ~ 0x7FF: function 0 CCCR and FBR + * 0x10000 ~ 0x1FFFF: function 1 miscellaneous registers + * The rest: function 1 silicon backplane core registers + */ + if ((addr & ~REG_F0_REG_MASK) == 0) + func = SDIO_FUNC_0; + else + func = SDIO_FUNC_1; + + do { + if (!write) + memset(data, 0, regsz); + /* for retry wait for 1 ms till bus get settled down */ + if (retry) + usleep_range(1000, 2000); + ret = brcmf_sdiod_request_data(sdiodev, func, addr, regsz, + data, write); + } while (ret != 0 && ret != -ENOMEDIUM && + retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); + + if (ret == -ENOMEDIUM) + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); + else if (ret != 0) { + /* + * SleepCSR register access can fail when + * waking up the device so reduce this noise + * in the logs. + */ + if (addr != SBSDIO_FUNC1_SLEEPCSR) + brcmf_err("failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + else + brcmf_dbg(SDIO, "failed to %s data F%d@0x%05x, err: %d\n", + write ? "write" : "read", func, addr, ret); + } + return ret; +} + +static int +brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) +{ + int err = 0, i; + u8 addr[3]; + + if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM) + return -ENOMEDIUM; + + addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; + addr[1] = (address >> 16) & SBSDIO_SBADDRMID_MASK; + addr[2] = (address >> 24) & SBSDIO_SBADDRHIGH_MASK; + + for (i = 0; i < 3; i++) { + err = brcmf_sdiod_regrw_helper(sdiodev, + SBSDIO_FUNC1_SBADDRLOW + i, + sizeof(u8), &addr[i], true); + if (err) { + brcmf_err("failed at addr: 0x%0x\n", + SBSDIO_FUNC1_SBADDRLOW + i); + break; + } + } + + return err; +} + +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) +{ + u32 data = 0; + int retval; + + brcmf_dbg(SDIO, "addr:0x%08x\n", addr); + retval = brcmf_sdiod_addrprep(sdiodev, sizeof(data), &addr); + if (retval) + goto done; + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, sizeof(data), &data, + false); + brcmf_dbg(SDIO, "data:0x%08x\n", data); + +done: + if (ret) + *ret = retval; + + return data; +} + +/********************************************************/ __unused static void probe_bcrm(struct cam_device *dev) { @@ -508,8 +423,8 @@ probe_bcrm(struct cam_device *dev) { 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 * + +__unused static uint8_t* mmap_fw() { const char fw_path[] = "/home/kibab/repos/fbsd-bbb/brcm-firmware/brcmfmac4330-sdio.bin"; struct stat sb; @@ -533,69 +448,65 @@ usage() { exit(0); } +struct card_info { + uint8_t num_funcs; + struct cis_info f[8]; +}; + +/* + * TODO: We should add SDIO card info about at least number of + * available functions to struct cam_device and use it instead + * of checking for man_id = 0x00 for detecting number of functions + */ static void -get_sdio_card_info(struct cam_device *dev) { +get_sdio_card_info(struct cam_device *dev, struct card_info *ci) { uint32_t cis_addr; uint32_t fbr_addr; - struct cis_info info; + int ret; cis_addr = sdio_get_common_cis_addr(dev); - memset(&info, 0, sizeof(info)); - sdio_func_read_cis(dev, 0, cis_addr, &info); + memset(ci, 0, sizeof(struct card_info)); + sdio_func_read_cis(dev, 0, cis_addr, &ci->f[0]); 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++) { + ci->f[0].man_id, ci->f[0].prod_id, ci->f[0].max_block_size); + for (int i = 1; i <= 7; 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); + cis_addr = sdio_read_1(dev, 0, fbr_addr++, &ret);bailout(ret); + cis_addr |= sdio_read_1(dev, 0, fbr_addr++, &ret) << 8; + cis_addr |= sdio_read_1(dev, 0, fbr_addr++, &ret) << 16; + sdio_func_read_cis(dev, i, cis_addr, &ci->f[i]); 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); + i, ci->f[i].man_id, ci->f[i].prod_id, ci->f[i].max_block_size); + if (ci->f[i].man_id == 0) { + printf("F%d doesn't exist\n", i); + break; + } + ci->num_funcs++; } } -/* 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; + int ret; + struct card_info ci; //fw_ptr = mmap_fw(); - while ((ch = getopt(argc, argv, "Iu:")) != -1) { + while ((ch = getopt(argc, argv, "fu:")) != -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; + break; case '?': default: usage(); @@ -607,43 +518,39 @@ main(int argc, char **argv) { 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); + get_sdio_card_info(cam_dev, &ci); - /* 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); + /* For now, everything non-broadcom is out of the question */ + if (ci.f[0].man_id != 0x02D0) { + printf("The card is not a Broadcom device\n"); + exit(1); + } + /* Init structures */ + struct brcmf_sdio_dev brcmf_dev; + struct brcmf_bus bus_if; + struct sdio_func f0, f1, f2; + bus_if.state = BRCMF_BUS_DOWN; + brcmf_dev.cam_dev = cam_dev; + brcmf_dev.bus_if = &bus_if; + brcmf_dev.state = BRCMF_SDIOD_DOWN; - /* 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); + /* Fill in functions */ + brcmf_dev.func[0] = &f0; + brcmf_dev.func[1] = &f1; + brcmf_dev.func[2] = &f2; - /* 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); + brcmf_dev.func[0]->dev = brcmf_dev.func[1]->dev + = brcmf_dev.func[2]->dev = cam_dev; + brcmf_dev.func[0]->num = 0; + brcmf_dev.func[1]->num = 1; + brcmf_dev.func[2]->num = 2; + ret = sdio_func_enable(cam_dev, 1, 1);bailout(ret); + uint32_t magic = brcmf_sdiod_regrl(&brcmf_dev, 0x18000000, &ret); + printf("Magic = %08x\n", magic); + if (magic != REPLY_MAGIC) { + errx(1, "Reply magic is incorrect: expected %08x, got %08x", + REPLY_MAGIC, magic); + } cam_close_spec_device(cam_dev); }