Index: sbin/camcontrol/camcontrol.c =================================================================== --- sbin/camcontrol/camcontrol.c +++ sbin/camcontrol/camcontrol.c @@ -7791,6 +7791,8 @@ mmc_d.len = mmc_data_len; mmc_d.data = mmc_data; mmc_d.flags = MMC_DATA_READ; + mmc_d.block_size = 0; + mmc_d.block_count = 0; } else flags |= CAM_DIR_NONE; cam_fill_mmcio(&ccb->mmcio, Index: sys/arm/allwinner/aw_mmc.c =================================================================== --- sys/arm/allwinner/aw_mmc.c +++ sys/arm/allwinner/aw_mmc.c @@ -57,6 +57,8 @@ #include "opt_mmccam.h" +#define DEBUG 1 + #ifdef MMCCAM #include #include @@ -1104,8 +1106,20 @@ cmdreg |= AW_MMC_CMDR_DIR_WRITE; blksz = min(cmd->data->len, MMC_SECTOR_SIZE); +#ifdef MMCCAM + if (cmd->data->block_count > 0) { + AW_MMC_WRITE_4(sc, AW_MMC_BKSR, cmd->data->block_size); + /* This is a byte count register, not block + * count?! */ + AW_MMC_WRITE_4(sc, AW_MMC_BYCR, cmd->data->len); + } else { + AW_MMC_WRITE_4(sc, AW_MMC_BKSR, blksz); + AW_MMC_WRITE_4(sc, AW_MMC_BYCR, cmd->data->len); + } +#else AW_MMC_WRITE_4(sc, AW_MMC_BKSR, blksz); AW_MMC_WRITE_4(sc, AW_MMC_BYCR, cmd->data->len); +#endif } else { imask |= AW_MMC_INT_CMD_DONE; } Index: sys/cam/mmc/mmc_da.c =================================================================== --- sys/cam/mmc/mmc_da.c +++ sys/cam/mmc/mmc_da.c @@ -843,6 +843,7 @@ struct mmc_data d; KASSERT(buf_len == 512, ("Buffer for ext csd must be 512 bytes")); + memset(&d, 0, sizeof(d)); d.data = rawextcsd; d.len = buf_len; d.flags = MMC_DATA_READ; @@ -989,6 +990,7 @@ uint32_t arg; memset(res, 0, 64); + memset(&mmc_d, 0, sizeof(mmc_d)); mmc_d.len = 64; mmc_d.data = res; mmc_d.flags = MMC_DATA_READ; @@ -1777,6 +1779,7 @@ mmcio->cmd.data->data = bp->bio_data; mmcio->cmd.data->len = 512 * count; mmcio->cmd.data->flags = (bp->bio_cmd == BIO_READ ? MMC_DATA_READ : MMC_DATA_WRITE); + mmcio->cmd.data->block_count = mmcio->cmd.data->block_size = 0; /* Direct h/w to issue CMD12 upon completion */ if (count > 1) { mmcio->cmd.data->flags |= MMC_DATA_MULTI; Index: sys/dev/mmc/mmcreg.h =================================================================== --- sys/dev/mmc/mmcreg.h +++ sys/dev/mmc/mmcreg.h @@ -190,6 +190,8 @@ struct mmc_data { size_t len; /* size of the data */ + size_t block_size; /* block size for CMD53 */ + size_t block_count; /* block count for CMD53 */ size_t xfer_len; void *data; /* data buffer */ uint32_t flags; @@ -546,7 +548,11 @@ #define SD_IO_RW_LEN(x) (((x) & 0xFF) << 0) #define SD_IOE_RW_LEN(x) (((x) & 0x1FF) << 0) +#define SD_IOE_RW_ADR(x) (((x) & 0x1FFFF) << 9) +#define SD_IOE_RW_INCR (1u << 26) #define SD_IOE_RW_BLK (1u << 27) +#define SD_IOE_RW_FUNC(x) (((x) & 0x7) << 28) +#define SD_IOE_RW_WR (1u << 31) /* Card Common Control Registers (CCCR) */ #define SD_IO_CCCR_START 0x00000 @@ -561,11 +567,15 @@ #define CCCR_BUS_WIDTH_4 (1 << 1) #define CCCR_BUS_WIDTH_1 (1 << 0) #define SD_IO_CCCR_CARDCAP 0x08 -#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */ - +#define CCCR_CC_SMB (1 << 1) +#define SD_IO_CCCR_CISPTR 0x09 /* 0x09 - 0x0B */ +#define SD_IO_CCCR_FN0_BLKSZ 0x10 /* 0x10 - 0x11 */ /* Function Basic Registers (FBR) */ #define SD_IO_FBR_START 0x00100 #define SD_IO_FBR_SIZE 0x00700 +#define SD_IO_FBR_START_F(n) (SD_IO_FBR_START + (n-1) * 0x00100) +#define SD_IO_FBR_CIS_OFFSET 0x9 +#define SD_IO_FBR_IOBLKSZ 0x10 /* Card Information Structure (CIS) */ #define SD_IO_CIS_START 0x01000 Index: sys/dev/sdhci/sdhci.c =================================================================== --- sys/dev/sdhci/sdhci.c +++ sys/dev/sdhci/sdhci.c @@ -1623,9 +1623,10 @@ return; mode = SDHCI_TRNS_BLK_CNT_EN; - if (data->len > 512) { + if (data->len > 512 || data->block_count > 1) { mode |= SDHCI_TRNS_MULTI; - if (__predict_true( + slot_printf(slot, "Enabling SDHCI_TRNS_MULTI\n"); + if (data->block_count == 0 && __predict_true( #ifdef MMCCAM slot->ccb->mmcio.stop.opcode == MMC_STOP_TRANSMISSION && #else @@ -1888,11 +1889,26 @@ } /* Current data offset for both PIO and DMA. */ slot->offset = 0; + +#ifdef MMCCAM + if (data->block_count > 0) { + blksz = SDHCI_MAKE_BLKSZ(slot->sdma_boundary, data->block_size); + blkcnt = data->block_count; + if (__predict_false(sdhci_debug > 0)) + slot_printf(slot, "SDIO Custom block params: blksz: 0x%08x | Blk cnt: 0x%08x\n", + blksz, blkcnt); + } else { + /* Set block size and request border interrupts on the SDMA boundary. */ + blksz = SDHCI_MAKE_BLKSZ(slot->sdma_boundary, ulmin(data->len, 512)); + blkcnt = howmany(data->len, 512); + } +#else /* Set block size and request border interrupts on the SDMA boundary. */ blksz = SDHCI_MAKE_BLKSZ(slot->sdma_boundary, ulmin(data->len, 512)); + blkcnt = howmany(data->len, 512); +#endif WR2(slot, SDHCI_BLOCK_SIZE, blksz); /* Set block count. */ - blkcnt = howmany(data->len, 512); WR2(slot, SDHCI_BLOCK_COUNT, blkcnt); if (__predict_false(sdhci_debug > 1)) slot_printf(slot, "Blk size: 0x%08x | Blk cnt: 0x%08x\n", @@ -2771,10 +2787,12 @@ } */ if (__predict_false(sdhci_debug > 1)) { - slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x\n", - mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags, - mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0, - mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0); + slot_printf(slot, "CMD%u arg %#x flags %#x dlen %u dflags %#x blksz=%zu blkcnt=%zu\n", + mmcio->cmd.opcode, mmcio->cmd.arg, mmcio->cmd.flags, + mmcio->cmd.data != NULL ? (unsigned int) mmcio->cmd.data->len : 0, + mmcio->cmd.data != NULL ? mmcio->cmd.data->flags: 0, + mmcio->cmd.data != NULL ? mmcio->cmd.data->block_size:0, + mmcio->cmd.data != NULL ? mmcio->cmd.data->block_count:0); } if (mmcio->cmd.data != NULL) { if (mmcio->cmd.data->len == 0 || mmcio->cmd.data->flags == 0) Index: usr.bin/sdiotool/cam_sdio.h =================================================================== --- usr.bin/sdiotool/cam_sdio.h +++ usr.bin/sdiotool/cam_sdio.h @@ -71,12 +71,14 @@ 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 func_number, + uint32_t addr, + uint8_t is_increment, + uint8_t is_write, + caddr_t data, + size_t blk_size, + 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); @@ -95,3 +97,4 @@ 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); +int sdio_func_set_block_size(struct cam_device *dev, uint8_t func_number, uint16_t block_size); Index: usr.bin/sdiotool/cam_sdio.c =================================================================== --- usr.bin/sdiotool/cam_sdio.c +++ usr.bin/sdiotool/cam_sdio.c @@ -86,17 +86,26 @@ * CMD53 -- IO_RW_EXTENDED * Use to read or write memory blocks * - * is_increment=1: FIFO mode - * blk_count > 0: block mode + * is_increment: 0 for FIFO mode, 1 for normal. + * blk_size: block size to use for transfer. + * blk_count: how many blocks of size blk_size to operate on. 0 means + * use byte mode. + * + * This function does not check if the supplied block size is actually + * supported by the given SDIO function. + * Also this function does not attempt to change an effective block + * size for the given SDIO function. + * The total transfer size will be blk_size * blk_count. */ 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 func_number, + uint32_t addr, + uint8_t is_increment, + uint8_t is_write, + caddr_t data, + size_t blk_size, + uint16_t blk_count) { union ccb *ccb; uint32_t flags; uint32_t arg; @@ -105,8 +114,14 @@ struct mmc_data mmcd; int retval = 0; - if (blk_count != 0) { - warnx("%s: block mode is not supported yet", __func__); + warnx("sdio_rw_extended(fn=%d, addr=0x%x, incr=%d, write=%d, datap=%p, blk_size=%zu, blk_count=%d)", func_number, addr, is_increment, is_write, data, blk_size, blk_count); + if (blk_count > 512) { + warnx("%s: cannot operate on more than 512 blocks", __func__); + return (-1); + } + + if (blk_size > 1 && blk_count > 511) { + warnx("%s: cannot operate on more than 511 blocks in block mode", __func__); return (-1); } @@ -119,25 +134,42 @@ 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); + arg = SD_IOE_RW_FUNC(func_number) | SD_IOE_RW_ADR(addr); + + /* Block mode if needed */ + if (blk_count > 0) { + arg |= SD_IOE_RW_BLK; + /* + * In block mode, putting 0 here means "infinite block count", + * we don't support this. + */ + arg |= SD_IOE_RW_LEN(blk_count); + } else { + /* + * In byte mode, 0 means "512 bytes". + */ + arg |= blk_size == 512 ? 0 : SD_IOE_RW_LEN(blk_size); + } if (is_increment) - arg |= SD_IO_RW_INCR; + arg |= SD_IOE_RW_INCR; mmcd.data = data; - mmcd.len = datalen; + mmcd.len = blk_count > 0 ? blk_size * blk_count : blk_size; + mmcd.block_size = blk_size; + mmcd.block_count = blk_count; mmcd.xfer_len = 0; /* not used by MMCCAM */ mmcd.mrq = NULL; /* not used by MMCCAM */ if (is_write) { - arg |= SD_IO_RW_WR; + arg |= SD_IOE_RW_WR; cam_flags = CAM_DIR_OUT; mmcd.flags = MMC_DATA_WRITE; } else { cam_flags = CAM_DIR_IN; mmcd.flags = MMC_DATA_READ; } + printf("arg=%#x\n", arg); cam_fill_mmcio(&ccb->mmcio, /*retries*/ 0, /*cbfcnp*/ NULL, @@ -166,6 +198,35 @@ return (retval); } +int +sdio_func_set_block_size(struct cam_device *dev, uint8_t func_number, uint16_t block_size) { + uint32_t addr; + int ret; + + uint8_t card_cap = sdio_read_1(dev, 0, SD_IO_CCCR_START + SD_IO_CCCR_CARDCAP, &ret); + if (!(card_cap & CCCR_CC_SMB)) { + warnx("%s: block mode is not supported, card cap = %x", __func__, card_cap); + return (-1); + } + + if (block_size > 2048) { + warnx("%s: block size cannot exceed 2048", __func__); + return (-1); + } + + if (func_number == 0) { + addr = SD_IO_CCCR_START + SD_IO_CCCR_FN0_BLKSZ; + } else { + addr = SD_IO_FBR_START_F(func_number) + SD_IO_FBR_IOBLKSZ; + } + + if (sdio_write_1(dev, 0, addr, (block_size & 0xFF)) < 0 || + sdio_write_1(dev, 0, addr + 1, (block_size >> 8)) < 0) { + warnx("%s: cannot write new block size", __func__); + return (-1); + } + return (0); +} int sdio_read_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, uint8_t *is_enab) { @@ -216,18 +277,18 @@ 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); + return sdio_rw_direct(dev, func_number, addr, 1, &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 + /* is_increment */ 1, + /* is_write */ 0, + /* data */ (caddr_t) &val, + /* blk_size */ sizeof(val), + /* blk_count */ 0 ); return val; } @@ -236,11 +297,11 @@ 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 + /* is_increment */ 1, + /* is_write */ 1, + /* data */ (caddr_t) &val, + /* blk_size */ sizeof(val), + /* blk_count */ 0 ); } @@ -248,11 +309,11 @@ 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 + /* is_increment */ 1, + /* is_write */ 0, + /* data */ (caddr_t) &val, + /* blk_size */ sizeof(val), + /* blk_count */ 0 ); return val; } @@ -261,11 +322,11 @@ 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 + /* is_increment */ 1, + /* is_write */ 1, + /* data */ (caddr_t) &val, + /* blk_size */ sizeof(val), + /* blk_count */ 0 ); } Index: usr.bin/sdiotool/sdiotool.c =================================================================== --- usr.bin/sdiotool/sdiotool.c +++ usr.bin/sdiotool/sdiotool.c @@ -486,6 +486,98 @@ } } +static void +cmd53_experiments(struct cam_device *cam_dev) { + int ret; + uint8_t b0, b1, b2, b3; + uint32_t intval; + uint8_t val[32]; + + /* First set block size = 1 for F0 */ + printf("Setting block size for F0 to 0...\n"); + ret = sdio_func_set_block_size(cam_dev, 0, 0); + if (ret < 0) { + warnx("Cannot set block size = 1 for F0"); + return; + } + printf("F0 Block size set to 0\n"); + + /* Try to read the same data with CMD52 and CMD53 */ + b0 = sdio_read_1(cam_dev, 0, 0, &ret); + b1 = sdio_read_1(cam_dev, 0, 1, &ret); + b2 = sdio_read_1(cam_dev, 0, 2, &ret); + b3 = sdio_read_1(cam_dev, 0, 3, &ret); + printf("Read values (LE): %02x%02x%02x%02x\n", b3, b2, b1, b0); + + intval = sdio_read_4(cam_dev, 0, 0, &ret); + printf("read_4: ret=%d, value=%04x\n", ret, intval); + + /* Aggressive! Try 32 bytes from CCCR */ + ret = sdio_rw_extended(cam_dev, 0, 0, + /* is_increment */ 1, + /* is_write */ 0, + /* data */ (caddr_t) &val, + /* blk_size */ sizeof(val), + /* blk_count */ 0 + ); + if (ret < 0) { + warnx("Cannot read in byte mode"); + return; + } + printf("Read 32 bytes at once in BYTE mode:\n"); + hexdump(val, sizeof(val), NULL, 0); + + /* Super-aggressive ! Try reading 1 block of size 32 */ + ret = sdio_func_set_block_size(cam_dev, 0, 32); + if (ret < 0) { + warnx("Cannot set non-1 block size for F0"); + return; + } + bzero(&val, sizeof(val)); + ret = sdio_rw_extended(cam_dev, 0, 0, + /* is_increment */ 1, + /* is_write */ 0, + /* data */ (caddr_t) &val, + /* blk_size */ 32, + /* blk_count */ 1 + ); + if (ret < 0) { + warnx("Cannot read in block mode"); + return; + } + + printf("Read 1 block x 32b in BLOCK mode:\n"); + hexdump(val, sizeof(val), NULL, 0); + + /* Mega-aggressive! Read 2 blocks of size 16 */ + ret = sdio_func_set_block_size(cam_dev, 0, 16); + if (ret < 0) { + warnx("Cannot set non-1 block size for F0"); + return; + } + bzero(&val, sizeof(val)); + ret = sdio_rw_extended(cam_dev, 0, 0, + /* is_increment */ 1, + /* is_write */ 0, + /* data */ (caddr_t) &val, + /* blk_size */ 16, + /* blk_count */ 2 + ); + if (ret < 0) { + warnx("Cannot read in block mode"); + return; + } + + printf("Read 2 blocks x 16b in BLOCK mode:\n"); + hexdump(val, sizeof(val), NULL, 0); + + ret = sdio_func_set_block_size(cam_dev, 0, 0); + if (ret < 0) { + warnx("Cannot set block size to 0 for F0"); + return; + } +} + int main(int argc, char **argv) { char device[] = "pass"; @@ -514,10 +606,13 @@ } argc -= optind; argv += optind; - if ((cam_dev = cam_open_spec_device(device, unit, O_RDWR, NULL)) == NULL) errx(1, "Cannot open device"); + cmd53_experiments(cam_dev); + cam_close_spec_device(cam_dev); + return 0; + get_sdio_card_info(cam_dev, &ci); /* For now, everything non-broadcom is out of the question */