#include #include #include #include #define SDIO_CMD53_TIMEOUT_MS 100 #define SDIO_IDLE_DELAY_MS 50 struct __attribute__((packed)) sdio_cmd52 { uint32_t write_data : 8; uint32_t reserved_8 : 1; uint32_t register_address : 17; uint32_t reserved_26 : 1; uint32_t raw_flag : 1; uint32_t function_number : 3; uint32_t rw_flag : 1; }; struct __attribute__((packed)) sdio_cmd53 { uint32_t byte_block_count : 9; uint32_t register_address : 17; uint32_t op_code : 1; uint32_t block_mode : 1; uint32_t function_number : 3; uint32_t rw_flag : 1; }; struct __attribute__((packed)) sdio_resp_R5 { uint32_t data : 8; struct { uint32_t out_of_range : 1; uint32_t function_number : 1; uint32_t rfu : 1; uint32_t error : 1; uint32_t io_current_state : 2; uint32_t illegal_command : 1; uint32_t com_crc_error : 1; } flags; uint32_t reserved_16 : 16; }; union sdio_cmd5x { uint32_t value; struct sdio_cmd52 cmd52; struct sdio_cmd53 cmd53; }; int sdio_sendcmdpoll(FAR struct sdio_dev_s *dev, uint32_t cmd, uint32_t arg) { int ret; /* Send the command */ ret = SDIO_SENDCMD(dev, cmd, arg); if (ret == OK) { /* Then poll-wait until the response is available */ ret = SDIO_WAITRESPONSE(dev, cmd); if (ret != OK) { wlerr("ERROR: Wait for response to cmd: %08x failed: %d\n", cmd, ret); } } return ret; } int sdio_io_rw_direct(FAR struct sdio_dev_s *dev, bool write, uint8_t function, uint32_t address, uint8_t inb, uint8_t *outb) { union sdio_cmd5x arg; struct sdio_resp_R5 resp; int ret; /* Setup CMD52 argument */ arg.value = 0; if (write) { arg.cmd52.write_data = inb; } else { arg.cmd52.write_data = 0; } arg.cmd52.register_address = address & 0x1ffff; arg.cmd52.raw_flag = (write && outb); arg.cmd52.function_number = function & 7; arg.cmd52.rw_flag = write; /* Send CMD52 command */ sdio_sendcmdpoll(dev, SDIO_ACMD52, arg.value); ret = SDIO_RECVR5(dev, SDIO_ACMD52, (uint32_t *)&resp); if (ret != OK) { wlerr("ERROR: SDIO_RECVR5 failed %d\n", ret); return ret; } /* Check for errors */ if (resp.flags.error) { return -EIO; } if (resp.flags.function_number || resp.flags.out_of_range) { return -EINVAL; } /* Write output byte */ if (outb) { *outb = resp.data & 0xff; } return OK; } int sdio_io_rw_extended(FAR struct sdio_dev_s *dev, bool write, uint8_t function, uint32_t address, bool inc_addr, uint8_t *buf, unsigned int blocklen, unsigned int nblocks) { union sdio_cmd5x arg; struct sdio_resp_R5 resp; int ret; sdio_eventset_t wkupevent; /* Setup CMD53 argument */ arg.value = 0; arg.cmd53.register_address = address & 0x1ffff; arg.cmd53.op_code = inc_addr; arg.cmd53.function_number = function & 7; arg.cmd53.rw_flag = write; if (nblocks == 0 && blocklen < 512) { /* Use byte mode */ // wlinfo("byte mode\n"); arg.cmd53.block_mode = 0; arg.cmd53.byte_block_count = blocklen; nblocks = 1; } else { /* Use block mode */ arg.cmd53.block_mode = 1; arg.cmd53.byte_block_count = nblocks; } /* Send CMD53 command */ SDIO_BLOCKSETUP(dev, blocklen, nblocks); SDIO_WAITENABLE(dev, SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR); if (write) { // wlinfo("prep write %d %d\n", blocklen, nblocks); sdio_sendcmdpoll(dev, SDIO_ACMD53, (uint32_t)arg.value); ret = SDIO_RECVR5(dev, SDIO_ACMD53, (uint32_t *)&resp); SDIO_DMASENDSETUP(dev, buf, blocklen * nblocks); wkupevent = SDIO_EVENTWAIT(dev, SDIO_CMD53_TIMEOUT_MS); } else { // wlinfo("prep read %d\n", blocklen * nblocks); SDIO_DMARECVSETUP(dev, buf, blocklen * nblocks); SDIO_SENDCMD(dev, SDIO_ACMD53, (uint32_t)arg.value); wkupevent = SDIO_EVENTWAIT(dev, SDIO_CMD53_TIMEOUT_MS); ret = SDIO_RECVR5(dev, SDIO_ACMD53, (uint32_t *)&resp); } if (ret != OK) { wlerr("ERROR: SDIO_RECVR5 failed %d\n", ret); return ret; } /* Check for errors */ if (wkupevent & SDIOWAIT_TIMEOUT) { wlerr("timeout\n"); return -ETIMEDOUT; } if (resp.flags.error || (wkupevent & SDIOWAIT_ERROR)) { wlerr("error 1\n"); return -EIO; } if (resp.flags.function_number || resp.flags.out_of_range) { wlerr("error 2\n"); return -EINVAL; } return OK; } int sdio_set_wide_bus(struct sdio_dev_s *dev) { int ret; uint8_t value; /* Read Bus Interface Control register */ ret = sdio_io_rw_direct(dev, false, 0, SDIO_CCCR_BUS_IF, 0, &value); if (ret != OK) { return ret; } /* Set 4 bits bus width setting */ value &= ~SDIO_CCCR_BUS_IF_WIDTH_MASK; value |= SDIO_CCCR_BUS_IF_4_BITS; ret = sdio_io_rw_direct(dev, true, 0, SDIO_CCCR_BUS_IF, value, NULL); if (ret != OK) { return ret; } SDIO_WIDEBUS(dev, true); return OK; } int sdio_probe(FAR struct sdio_dev_s *dev) { int ret; uint32_t data = 0; /* Set device state from reset to idle */ sdio_sendcmdpoll(dev, MMCSD_CMD0, 0); up_mdelay(SDIO_IDLE_DELAY_MS); /* Device is SDIO card compatible so we can send CMD5 instead of ACMD41 */ sdio_sendcmdpoll(dev, SDIO_CMD5, 0); /* Receive R4 response */ ret = SDIO_RECVR4(dev, SDIO_CMD5, &data); if (ret != OK) { return ret; } /* Device is in Card Identification Mode, request device RCA */ sdio_sendcmdpoll(dev, SD_CMD3, 0); ret = SDIO_RECVR6(dev, SD_CMD3, &data); if (ret != OK) { wlerr("ERROR: RCA request failed: %d\n", ret); return ret; } wlinfo("rca is %x\n", data >> 16); /* Send CMD7 with the argument == RCA in order to select the card * and put it in Transfer State */ sdio_sendcmdpoll(dev, MMCSD_CMD7S, data & 0xffff0000); ret = SDIO_RECVR1(dev, MMCSD_CMD7S, &data); if (ret != OK) { wlerr("ERROR: card selection failed: %d\n", ret); return ret; } /* Configure 4 bits bus width */ ret = sdio_set_wide_bus(dev); if (ret != OK) { return ret; } return OK; } int sdio_set_blocksize(FAR struct sdio_dev_s *dev, uint8_t function, uint16_t blocksize) { int ret; ret = sdio_io_rw_direct(dev, true, 0, (function << SDIO_FBR_SHIFT) + SDIO_CCCR_FN0_BLKSIZE_0, blocksize & 0xff, NULL); if (ret != OK) { return ret; } ret = sdio_io_rw_direct(dev, true, 0, (function << SDIO_FBR_SHIFT) + SDIO_CCCR_FN0_BLKSIZE_1, (blocksize >> 8), NULL); if (ret != OK) { return ret; } return OK; } int sdio_enable_function(FAR struct sdio_dev_s *dev, uint8_t function) { int ret; uint8_t value; /* Read current I/O Enable register */ ret = sdio_io_rw_direct(dev, false, 0, SDIO_CCCR_IOEN, 0, &value); if (ret != OK) { return ret; } ret = sdio_io_rw_direct(dev, true, 0, SDIO_CCCR_IOEN, value | (1 << function), NULL); if (ret != OK) { return ret; } /* Wait 10ms for function to be enabled */ int loops = 10; while (loops-- > 0) { up_mdelay(1); ret = sdio_io_rw_direct(dev, false, 0, SDIO_CCCR_IOEN, 0, &value); if (ret != OK) { return ret; } if (value & (1 << function)) { /* Function enabled */ wlinfo("Function %d enabled\n", function); return OK; } } return -ETIMEDOUT; } int sdio_enable_interrupt(FAR struct sdio_dev_s *dev, uint8_t function) { int ret; uint8_t value; /* Read current Int Enable register */ ret = sdio_io_rw_direct(dev, false, 0, SDIO_CCCR_INTEN, 0, &value); if (ret != OK) { return ret; } return sdio_io_rw_direct(dev, true, 0, SDIO_CCCR_INTEN, value | (1 << function), NULL); }