Files
mongoose/src/drivers/sdio.c
T
Sergio R. Caprile 1b06403a19 Add support for SDIO
Add support for SDIO in CYW driver
Add Portenta H7 example
2025-05-26 10:04:44 -03:00

170 lines
6.6 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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