123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- #include <nuttx/wireless/ieee80211/mmc_sdio.h>
- #include <debug.h>
- #include <errno.h>
- #include <nuttx/arch.h>
- #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);
- }
|