diff options
Diffstat (limited to 'usr.bin/sdiotool/cam_sdio.c')
-rw-r--r-- | usr.bin/sdiotool/cam_sdio.c | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/usr.bin/sdiotool/cam_sdio.c b/usr.bin/sdiotool/cam_sdio.c new file mode 100644 index 000000000000..0cd8eeb0485a --- /dev/null +++ b/usr.bin/sdiotool/cam_sdio.c @@ -0,0 +1,436 @@ +/*- + * 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. + */ + +#include <sys/cdefs.h> +#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"); +} |