|
| 1 | +#include "sdio.h" |
| 2 | + |
| 3 | +#if MG_ENABLE_TCPIP && \ |
| 4 | + (defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO) |
| 5 | + |
| 6 | +// SDIO 6.9 Table 6-1 CCCR (Common Card Control Registers) |
| 7 | +#define MG_SDIO_CCCR_SDIOREV 0x000 |
| 8 | +#define MG_SDIO_CCCR_SDREV 0x001 |
| 9 | +#define MG_SDIO_CCCR_IOEN 0x002 |
| 10 | +#define MG_SDIO_CCCR_IORDY 0x003 |
| 11 | +#define MG_SDIO_CCCR_INTEN 0x004 |
| 12 | +#define MG_SDIO_CCCR_BIC 0x007 |
| 13 | +#define MG_SDIO_CCCR_CCAP 0x008 |
| 14 | +#define MG_SDIO_CCCR_CCIS 0x009 // 3 registers |
| 15 | +#define MG_SDIO_CCCR_F0BLKSZ 0x010 // 2 registers |
| 16 | +#define MG_SDIO_CCCR_HISPD 0x013 |
| 17 | +// SDIO 6.10 Table 6-3 FBR (Function Basic Registers) |
| 18 | +#define MG_SDIO_FBR_FnBLKSZ(n) (((n) &7) * 0x100 + 0x10) // 2 registers |
| 19 | + |
| 20 | +// SDIO 5.1 IO_RW_DIRECT Command (CMD52) |
| 21 | +#define MG_SDIO_DATA(x) ((x) &0xFF) // bits 0-7 |
| 22 | +#define MG_SDIO_ADDR(x) (((x) &0x1FFFF) << 9) // bits 9-25 |
| 23 | +#define MG_SDIO_FUNC(x) (((x) &3) << 28) // bits 28-30 (30 unused here) |
| 24 | +#define MG_SDIO_WR MG_BIT(31) |
| 25 | + |
| 26 | +// SDIO 5.3 IO_RW_EXTENDED Command (CMD53) |
| 27 | +#define MG_SDIO_LEN(x) ((x) &0x1FF) // bits 0-8 |
| 28 | +#define MG_SDIO_OPINC MG_BIT(26) |
| 29 | +#define MG_SDIO_BLKMODE MG_BIT(27) |
| 30 | + |
| 31 | +// - Drivers set blocksize, drivers request transfers. Requesting a read |
| 32 | +// transfer > blocksize means block transfer will be used. |
| 33 | +// - To simplify the use of DMA transfers and avoid intermediate buffers, |
| 34 | +// drivers must have room to accomodate a whole block transfer, e.g.: blocksize |
| 35 | +// = 64, read 65 => 2 blocks = 128 bytes |
| 36 | +// - Transfers of more than 1 byte assume (uint32_t *) data. 1-byte transfers |
| 37 | +// use (uint8_t *) data |
| 38 | +// - 'len' is the number of _bytes_ to transfer |
| 39 | +bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f, |
| 40 | + uint32_t addr, void *data, uint32_t len) { |
| 41 | + uint32_t arg, val = 0; |
| 42 | + unsigned int blksz = 64; // TODO(): mg_sdio_set_blksz() stores in an array, |
| 43 | + // index on f, skip if 0 |
| 44 | + if (len == 1) { |
| 45 | + arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_FUNC(f) | MG_SDIO_ADDR(addr) | |
| 46 | + (write ? MG_SDIO_DATA(*(uint8_t *) data) : 0); |
| 47 | + bool res = sdio->txn(sdio, 52, arg, &val); // IO_RW_DIRECT |
| 48 | + if (!write) *(uint8_t *) data = (uint8_t) val; |
| 49 | + return res; |
| 50 | + } |
| 51 | + // IO_RW_EXTENDED |
| 52 | + arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_OPINC | MG_SDIO_FUNC(f) | |
| 53 | + MG_SDIO_ADDR(addr); |
| 54 | + if (len > 512 || (blksz != 0 && len > blksz)) { // SDIO 5.3 512 -> len=0 |
| 55 | + unsigned int blkcnt; |
| 56 | + if (blksz == 0) return false; // > 512 requires block size set |
| 57 | + blkcnt = (len + blksz - 1) / blksz; |
| 58 | + if (blkcnt > 511) return false; // we don't support "infinite" blocks |
| 59 | + arg |= MG_SDIO_BLKMODE | MG_SDIO_LEN(blkcnt); // block transfer |
| 60 | + len = blksz * blkcnt; |
| 61 | + } else { |
| 62 | + arg |= MG_SDIO_LEN(len); // multi-byte transfer |
| 63 | + } |
| 64 | + return sdio->xfr(sdio, write, arg, |
| 65 | + (arg & MG_SDIO_BLKMODE) ? (uint16_t) blksz : 0, |
| 66 | + (uint32_t *) data, len, &val); |
| 67 | +} |
| 68 | + |
| 69 | +bool mg_sdio_set_blksz(struct mg_tcpip_sdio *sdio, unsigned int f, |
| 70 | + uint16_t blksz) { |
| 71 | + uint32_t val = blksz & 0xff; |
| 72 | + if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f), &val, 1)) |
| 73 | + return false; |
| 74 | + val = (blksz >> 8) & 0x0f; // SDIO 6.10 Table 6-4, max 2048 |
| 75 | + if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f) + 1, &val, 1)) |
| 76 | + return false; |
| 77 | + // TODO(): store in an array, index on f. Static 8-element array |
| 78 | + MG_VERBOSE(("F%c block size set", (f & 7) + '0')); |
| 79 | + return true; |
| 80 | +} |
| 81 | + |
| 82 | +// Enable Fx |
| 83 | +bool mg_sdio_enable_f(struct mg_tcpip_sdio *sdio, unsigned int f) { |
| 84 | + uint8_t bit = 1U << (f & 7), bits; |
| 85 | + uint32_t val = 0; |
| 86 | + if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1)) |
| 87 | + return false; |
| 88 | + bits = (uint8_t) val | bit; |
| 89 | + unsigned int times = 501; |
| 90 | + while (times--) { |
| 91 | + val = bits; /* IOEf */ |
| 92 | + ; |
| 93 | + if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_IOEN, &val, 1)) |
| 94 | + return false; |
| 95 | + mg_delayms(1); |
| 96 | + val = 0; |
| 97 | + if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1)) |
| 98 | + return false; |
| 99 | + if (val & bit) break; |
| 100 | + } |
| 101 | + if (times == (unsigned int) ~0) return false; |
| 102 | + MG_VERBOSE(("F%c enabled", (f & 7) + '0')); |
| 103 | + return true; |
| 104 | +} |
| 105 | + |
| 106 | +// Wait for Fx to be ready |
| 107 | +bool mg_sdio_waitready_f(struct mg_tcpip_sdio *sdio, unsigned int f) { |
| 108 | + uint8_t bit = 1U << (f & 7); |
| 109 | + unsigned int times = 501; |
| 110 | + while (times--) { |
| 111 | + uint32_t val; |
| 112 | + if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IORDY, &val, 1)) |
| 113 | + return false; |
| 114 | + if (val & bit) break; // IORf |
| 115 | + mg_delayms(1); |
| 116 | + } |
| 117 | + if (times == (unsigned int) ~0) return false; |
| 118 | + MG_VERBOSE(("F%c ready", (f & 7) + '0')); |
| 119 | + return true; |
| 120 | +} |
| 121 | + |
| 122 | +// SDIO 6.14 Bus State Diagram |
| 123 | +bool mg_sdio_init(struct mg_tcpip_sdio *sdio) { |
| 124 | + uint32_t val = 0; |
| 125 | + if (!sdio->txn(sdio, 0, 0, NULL)) return false; // GO_IDLE_STATE |
| 126 | + sdio->txn(sdio, 5, 0, &val); // IO_SEND_OP_COND, no CRC |
| 127 | + MG_VERBOSE(("IO Functions: %u, Memory: %c", 1 + ((val >> 28) & 7), |
| 128 | + (val & MG_BIT(27)) ? 'Y' : 'N')); |
| 129 | + if (!sdio->txn(sdio, 3, 0, &val)) return false; // SEND_RELATIVE_ADDR |
| 130 | + val = ((uint32_t) val) >> 16; // RCA |
| 131 | + if (!sdio->txn(sdio, 7, val << 16, &val)) |
| 132 | + return false; // SELECT/DESELECT_CARD |
| 133 | + mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDIOREV, &val, 1); |
| 134 | + MG_DEBUG(("CCCR: %u.%u, SDIO: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3, |
| 135 | + 1 + ((val >> 6) & 3), (val >> 4) & 3)); |
| 136 | + mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDREV, &val, 1); |
| 137 | + MG_VERBOSE(("SD: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3)); |
| 138 | + mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_BIC, &val, 1); |
| 139 | + MG_SET_BITS(val, 3, |
| 140 | + MG_BIT(7) | MG_BIT(1)); // SDIO 6.9 Tables 6-1 6-2, 4-bit bus |
| 141 | + mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_BIC, &val, 1); |
| 142 | + // All Full-Speed SDIO cards support a 4-bit bus. Skip for Low-Speed SDIO |
| 143 | + // cards, we don't provide separate low-level functions for width and speed |
| 144 | + sdio->cfg(sdio, 0); // set DS; |
| 145 | + if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_HISPD, &val, 1)) |
| 146 | + return false; |
| 147 | + if (val & MG_BIT(0) /* SHS */) { |
| 148 | + val = MG_BIT(1); /* EHS */ |
| 149 | + if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_HISPD, &val, 1)) |
| 150 | + return false; |
| 151 | + sdio->cfg(sdio, 1); // set HS; |
| 152 | + MG_VERBOSE(("Bus set to 4-bit @50MHz")); |
| 153 | + } else { |
| 154 | + MG_VERBOSE(("Bus set to 4-bit @25MHz")); |
| 155 | + } |
| 156 | + return true; |
| 157 | +} |
| 158 | + |
| 159 | +// - 6.11 Card Information Structure (CIS): 0x0001000-0x017FF; for card common |
| 160 | +// and all functions |
| 161 | +// - 16.5 SDIO Card Metaformat |
| 162 | +// - 16.7.2 CISTPL_FUNCE (0x22): Function Extension Tuple, provides standard |
| 163 | +// information about the card (common) and each individual function. One |
| 164 | +// CISTPL_FUNCE in each function’s CIS, immediately following the CISTPL_FUNCID |
| 165 | +// tuple |
| 166 | +// - 16.7.3 CISTPL_FUNCE Tuple for Function 0 (common) |
| 167 | +// - 16.7.4 CISTPL_FUNCE Tuple for Function 1-7 |
| 168 | + |
| 169 | +#endif |
0 commit comments