mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-05 23:53:46 +07:00
170 lines
6.6 KiB
C
170 lines
6.6 KiB
C
#include "sdio.h"
|
||
|
||
#if MG_ENABLE_TCPIP && \
|
||
(defined(MG_ENABLE_DRIVER_CYW_SDIO) && MG_ENABLE_DRIVER_CYW_SDIO)
|
||
|
||
// SDIO 6.9 Table 6-1 CCCR (Common Card Control Registers)
|
||
#define MG_SDIO_CCCR_SDIOREV 0x000
|
||
#define MG_SDIO_CCCR_SDREV 0x001
|
||
#define MG_SDIO_CCCR_IOEN 0x002
|
||
#define MG_SDIO_CCCR_IORDY 0x003
|
||
#define MG_SDIO_CCCR_INTEN 0x004
|
||
#define MG_SDIO_CCCR_BIC 0x007
|
||
#define MG_SDIO_CCCR_CCAP 0x008
|
||
#define MG_SDIO_CCCR_CCIS 0x009 // 3 registers
|
||
#define MG_SDIO_CCCR_F0BLKSZ 0x010 // 2 registers
|
||
#define MG_SDIO_CCCR_HISPD 0x013
|
||
// SDIO 6.10 Table 6-3 FBR (Function Basic Registers)
|
||
#define MG_SDIO_FBR_FnBLKSZ(n) (((n) &7) * 0x100 + 0x10) // 2 registers
|
||
|
||
// SDIO 5.1 IO_RW_DIRECT Command (CMD52)
|
||
#define MG_SDIO_DATA(x) ((x) &0xFF) // bits 0-7
|
||
#define MG_SDIO_ADDR(x) (((x) &0x1FFFF) << 9) // bits 9-25
|
||
#define MG_SDIO_FUNC(x) (((x) &3) << 28) // bits 28-30 (30 unused here)
|
||
#define MG_SDIO_WR MG_BIT(31)
|
||
|
||
// SDIO 5.3 IO_RW_EXTENDED Command (CMD53)
|
||
#define MG_SDIO_LEN(x) ((x) &0x1FF) // bits 0-8
|
||
#define MG_SDIO_OPINC MG_BIT(26)
|
||
#define MG_SDIO_BLKMODE MG_BIT(27)
|
||
|
||
// - Drivers set blocksize, drivers request transfers. Requesting a read
|
||
// transfer > blocksize means block transfer will be used.
|
||
// - To simplify the use of DMA transfers and avoid intermediate buffers,
|
||
// drivers must have room to accomodate a whole block transfer, e.g.: blocksize
|
||
// = 64, read 65 => 2 blocks = 128 bytes
|
||
// - Transfers of more than 1 byte assume (uint32_t *) data. 1-byte transfers
|
||
// use (uint8_t *) data
|
||
// - 'len' is the number of _bytes_ to transfer
|
||
bool mg_sdio_transfer(struct mg_tcpip_sdio *sdio, bool write, unsigned int f,
|
||
uint32_t addr, void *data, uint32_t len) {
|
||
uint32_t arg, val = 0;
|
||
unsigned int blksz = 64; // TODO(): mg_sdio_set_blksz() stores in an array,
|
||
// index on f, skip if 0
|
||
if (len == 1) {
|
||
arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_FUNC(f) | MG_SDIO_ADDR(addr) |
|
||
(write ? MG_SDIO_DATA(*(uint8_t *) data) : 0);
|
||
bool res = sdio->txn(sdio, 52, arg, &val); // IO_RW_DIRECT
|
||
if (!write) *(uint8_t *) data = (uint8_t) val;
|
||
return res;
|
||
}
|
||
// IO_RW_EXTENDED
|
||
arg = (write ? MG_SDIO_WR : 0) | MG_SDIO_OPINC | MG_SDIO_FUNC(f) |
|
||
MG_SDIO_ADDR(addr);
|
||
if (len > 512 || (blksz != 0 && len > blksz)) { // SDIO 5.3 512 -> len=0
|
||
unsigned int blkcnt;
|
||
if (blksz == 0) return false; // > 512 requires block size set
|
||
blkcnt = (len + blksz - 1) / blksz;
|
||
if (blkcnt > 511) return false; // we don't support "infinite" blocks
|
||
arg |= MG_SDIO_BLKMODE | MG_SDIO_LEN(blkcnt); // block transfer
|
||
len = blksz * blkcnt;
|
||
} else {
|
||
arg |= MG_SDIO_LEN(len); // multi-byte transfer
|
||
}
|
||
return sdio->xfr(sdio, write, arg,
|
||
(arg & MG_SDIO_BLKMODE) ? (uint16_t) blksz : 0,
|
||
(uint32_t *) data, len, &val);
|
||
}
|
||
|
||
bool mg_sdio_set_blksz(struct mg_tcpip_sdio *sdio, unsigned int f,
|
||
uint16_t blksz) {
|
||
uint32_t val = blksz & 0xff;
|
||
if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f), &val, 1))
|
||
return false;
|
||
val = (blksz >> 8) & 0x0f; // SDIO 6.10 Table 6-4, max 2048
|
||
if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_FBR_FnBLKSZ(f) + 1, &val, 1))
|
||
return false;
|
||
// TODO(): store in an array, index on f. Static 8-element array
|
||
MG_VERBOSE(("F%c block size set", (f & 7) + '0'));
|
||
return true;
|
||
}
|
||
|
||
// Enable Fx
|
||
bool mg_sdio_enable_f(struct mg_tcpip_sdio *sdio, unsigned int f) {
|
||
uint8_t bit = 1U << (f & 7), bits;
|
||
uint32_t val = 0;
|
||
if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1))
|
||
return false;
|
||
bits = (uint8_t) val | bit;
|
||
unsigned int times = 501;
|
||
while (times--) {
|
||
val = bits; /* IOEf */
|
||
;
|
||
if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_IOEN, &val, 1))
|
||
return false;
|
||
mg_delayms(1);
|
||
val = 0;
|
||
if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IOEN, &val, 1))
|
||
return false;
|
||
if (val & bit) break;
|
||
}
|
||
if (times == (unsigned int) ~0) return false;
|
||
MG_VERBOSE(("F%c enabled", (f & 7) + '0'));
|
||
return true;
|
||
}
|
||
|
||
// Wait for Fx to be ready
|
||
bool mg_sdio_waitready_f(struct mg_tcpip_sdio *sdio, unsigned int f) {
|
||
uint8_t bit = 1U << (f & 7);
|
||
unsigned int times = 501;
|
||
while (times--) {
|
||
uint32_t val;
|
||
if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_IORDY, &val, 1))
|
||
return false;
|
||
if (val & bit) break; // IORf
|
||
mg_delayms(1);
|
||
}
|
||
if (times == (unsigned int) ~0) return false;
|
||
MG_VERBOSE(("F%c ready", (f & 7) + '0'));
|
||
return true;
|
||
}
|
||
|
||
// SDIO 6.14 Bus State Diagram
|
||
bool mg_sdio_init(struct mg_tcpip_sdio *sdio) {
|
||
uint32_t val = 0;
|
||
if (!sdio->txn(sdio, 0, 0, NULL)) return false; // GO_IDLE_STATE
|
||
sdio->txn(sdio, 5, 0, &val); // IO_SEND_OP_COND, no CRC
|
||
MG_VERBOSE(("IO Functions: %u, Memory: %c", 1 + ((val >> 28) & 7),
|
||
(val & MG_BIT(27)) ? 'Y' : 'N'));
|
||
if (!sdio->txn(sdio, 3, 0, &val)) return false; // SEND_RELATIVE_ADDR
|
||
val = ((uint32_t) val) >> 16; // RCA
|
||
if (!sdio->txn(sdio, 7, val << 16, &val))
|
||
return false; // SELECT/DESELECT_CARD
|
||
mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDIOREV, &val, 1);
|
||
MG_DEBUG(("CCCR: %u.%u, SDIO: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3,
|
||
1 + ((val >> 6) & 3), (val >> 4) & 3));
|
||
mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_SDREV, &val, 1);
|
||
MG_VERBOSE(("SD: %u.%u", 1 + ((val >> 2) & 3), (val >> 0) & 3));
|
||
mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_BIC, &val, 1);
|
||
MG_SET_BITS(val, 3,
|
||
MG_BIT(7) | MG_BIT(1)); // SDIO 6.9 Tables 6-1 6-2, 4-bit bus
|
||
mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_BIC, &val, 1);
|
||
// All Full-Speed SDIO cards support a 4-bit bus. Skip for Low-Speed SDIO
|
||
// cards, we don't provide separate low-level functions for width and speed
|
||
sdio->cfg(sdio, 0); // set DS;
|
||
if (!mg_sdio_transfer(sdio, false, 0, MG_SDIO_CCCR_HISPD, &val, 1))
|
||
return false;
|
||
if (val & MG_BIT(0) /* SHS */) {
|
||
val = MG_BIT(1); /* EHS */
|
||
if (!mg_sdio_transfer(sdio, true, 0, MG_SDIO_CCCR_HISPD, &val, 1))
|
||
return false;
|
||
sdio->cfg(sdio, 1); // set HS;
|
||
MG_VERBOSE(("Bus set to 4-bit @50MHz"));
|
||
} else {
|
||
MG_VERBOSE(("Bus set to 4-bit @25MHz"));
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// - 6.11 Card Information Structure (CIS): 0x0001000-0x017FF; for card common
|
||
// and all functions
|
||
// - 16.5 SDIO Card Metaformat
|
||
// - 16.7.2 CISTPL_FUNCE (0x22): Function Extension Tuple, provides standard
|
||
// information about the card (common) and each individual function. One
|
||
// CISTPL_FUNCE in each function’s CIS, immediately following the CISTPL_FUNCID
|
||
// tuple
|
||
// - 16.7.3 CISTPL_FUNCE Tuple for Function 0 (common)
|
||
// - 16.7.4 CISTPL_FUNCE Tuple for Function 1-7
|
||
|
||
#endif
|