SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
829b19c verified
Raw History Blame Contribute Delete
6.79 kB
/*
* OMAP3530 HSMMC driver
*/
#include "mmc.h"
#include "gpio.h"
static int mmc_wait_status(mmc_dev_t *dev, uint32_t mask, uint32_t timeout) {
while (timeout--) {
if (reg_read32(dev->base + MMC_STAT) & mask)
return 0;
}
return -1;
}
static void mmc_set_clock(mmc_dev_t *dev, uint32_t freq) {
hw_reg32_t base = dev->base;
/* Stop clock */
reg_clr32(base + MMC_SYSCTL, MMC_SYSCTL_CEN);
/* Calculate divider: MMC_CLK = SYS_CLK / (2 * CLKD) */
uint32_t div = 1;
while ((dev->clock_hz / (2 * div)) > freq) div++;
if (div > 0x3FF) div = 0x3FF;
/* Set divider */
reg32_t sysctl = reg_read32(base + MMC_SYSCTL);
sysctl &= ~(0x3FF << MMC_SYSCTL_CLKD_SHIFT);
sysctl |= (div << MMC_SYSCTL_CLKD_SHIFT);
reg_write32(base + MMC_SYSCTL, sysctl);
/* Enable clock */
reg_set32(base + MMC_SYSCTL, MMC_SYSCTL_ICE);
while (!(reg_read32(base + MMC_SYSCTL) & MMC_SYSCTL_ICS))
;
reg_set32(base + MMC_SYSCTL, MMC_SYSCTL_CEN);
dev->card_clock = dev->clock_hz / (2 * div);
}
int mmc_init(mmc_dev_t *dev, uint32_t base, uint32_t clock_hz) {
dev->base = (hw_reg32_t)base;
dev->clock_hz = clock_hz;
dev->rca = 0;
dev->bus_width = 1;
dev->high_capacity = 0;
/* Soft reset */
reg_set32(dev->base + MMC_SYSCONFIG, 0x02);
delay_cycles(200);
while (!(reg_read32(dev->base + MMC_SYSSTATUS) & 0x01))
;
/* Reset CMD and DAT lines */
reg_set32(dev->base + MMC_SYSCTL, MMC_SYSCTL_SRD | MMC_SYSCTL_SRH);
delay_cycles(200);
while (reg_read32(dev->base + MMC_SYSCTL) & (MMC_SYSCTL_SRD | MMC_SYSCTL_SRH))
;
/* Set voltage to 3.0V */
reg32_t hctl = reg_read32(dev->base + MMC_HCTL);
hctl &= ~(0xF << MMC_HCTL_SDVS_SHIFT);
hctl |= (6 << MMC_HCTL_SDVS_SHIFT); /* 3.0V */
reg_write32(dev->base + MMC_HCTL, hctl);
/* Turn on bus power */
reg_set32(dev->base + MMC_HCTL, MMC_HCTL_SDBP);
delay_cycles(500);
/* Start with identification clock (400kHz) */
mmc_set_clock(dev, 400000);
/* Enable all interrupts */
reg_write32(dev->base + MMC_IE, 0xFFFFFFFF);
reg_write32(dev->base + MMC_ISE, 0xFFFFFFFF);
/* Set block length to 512 */
reg_write32(dev->base + MMC_BLK, (1 << MMC_BLK_NBLK_SHIFT) | MMC_BLOCK_SIZE);
return 0;
}
int mmc_send_cmd(mmc_dev_t *dev, uint32_t cmd, uint32_t arg,
uint32_t rsp_type, uint32_t *rsp) {
hw_reg32_t base = dev->base;
/* Wait for CMD and DAT lines to be free */
uint32_t timeout = 1000000;
while ((reg_read32(base + MMC_PSTATE) & (MMC_PSTATE_CMDI | MMC_PSTATE_DATI)) && timeout--)
;
/* Clear status */
reg_write32(base + MMC_STAT, 0xFFFFFFFF);
/* Set argument */
reg_write32(base + MMC_ARG, arg);
/* Set command */
reg32_t cmd_reg = (cmd << MMC_CMD_INDX_SHIFT) | rsp_type;
reg_write32(base + MMC_CMD, cmd_reg);
/* Wait for command complete */
if (mmc_wait_status(dev, MMC_STAT_CC, 1000000) != 0)
return -1;
/* Read response if requested */
if (rsp && rsp_type != MMC_CMD_RSP_NONE) {
if (rsp_type == MMC_CMD_RSP_136) {
rsp[0] = reg_read32(base + MMC_RSP10);
rsp[1] = reg_read32(base + MMC_RSP32);
rsp[2] = reg_read32(base + MMC_RSP54);
rsp[3] = reg_read32(base + MMC_RSP76);
} else {
rsp[0] = reg_read32(base + MMC_RSP10);
}
}
/* Clear CC status */
reg_write32(base + MMC_STAT, MMC_STAT_CC);
return 0;
}
int mmc_detect_card(mmc_dev_t *dev) {
uint32_t rsp[4];
/* CMD0: GO_IDLE_STATE */
if (mmc_send_cmd(dev, SD_CMD_GO_IDLE_STATE, 0, MMC_CMD_RSP_NONE, (uint32_t *)0) != 0)
return -1;
/* CMD8: SEND_IF_COND (SD v2.0 check) */
int cmd8_result = mmc_send_cmd(dev, SD_CMD_SEND_IF_COND, 0x000001AA,
MMC_CMD_RSP_48, rsp);
if (cmd8_result == 0 && (rsp[0] & 0xFF) == 0xAA) {
dev->high_capacity = 1;
}
/* ACMD41: SD_SEND_OP_COND (repeat until busy bit set) */
uint32_t retry = 1000;
do {
/* CMD55: APP_CMD */
mmc_send_cmd(dev, SD_CMD_APP_CMD, 0, MMC_CMD_RSP_48, rsp);
/* ACMD41: HCS=1 for SDHC support */
mmc_send_cmd(dev, SD_ACMD_SD_SEND_OP_COND,
0x40FF8000, MMC_CMD_RSP_48, rsp);
retry--;
} while (!(rsp[0] & (1 << 31)) && retry > 0);
if (retry == 0)
return -1;
if (rsp[0] & (1 << 30))
dev->high_capacity = 1;
/* CMD2: ALL_SEND_CID */
mmc_send_cmd(dev, SD_CMD_ALL_SEND_CID, 0, MMC_CMD_RSP_136, rsp);
/* CMD3: SEND_RELATIVE_ADDR */
mmc_send_cmd(dev, SD_CMD_SEND_RELATIVE_ADDR, 0, MMC_CMD_RSP_48, rsp);
dev->rca = (uint16_t)(rsp[0] >> 16);
/* Switch to data transfer clock (25MHz) */
mmc_set_clock(dev, 25000000);
/* CMD7: SELECT_CARD */
mmc_send_cmd(dev, SD_CMD_SELECT_CARD, (uint32_t)dev->rca << 16,
MMC_CMD_RSP_48B, rsp);
/* ACMD6: SET_BUS_WIDTH to 4-bit */
mmc_send_cmd(dev, SD_CMD_APP_CMD, (uint32_t)dev->rca << 16,
MMC_CMD_RSP_48, rsp);
mmc_send_cmd(dev, SD_ACMD_SET_BUS_WIDTH, 2, MMC_CMD_RSP_48, rsp);
/* Configure 4-bit bus in HCTL */
reg32_t hctl = reg_read32(dev->base + MMC_HCTL);
hctl &= ~(0x3 << MMC_HCTL_DTW_SHIFT);
hctl |= (1 << MMC_HCTL_DTW_SHIFT); /* 4-bit */
reg_write32(dev->base + MMC_HCTL, hctl);
dev->bus_width = 4;
return 0;
}
int mmc_read_block(mmc_dev_t *dev, uint32_t block, uint8_t *buf) {
hw_reg32_t base = dev->base;
/* Set block count and length */
reg_write32(base + MMC_BLK, (1 << MMC_BLK_NBLK_SHIFT) | MMC_BLOCK_SIZE);
/* For standard capacity cards, address is byte offset */
uint32_t addr = dev->high_capacity ? block : (block * MMC_BLOCK_SIZE);
/* CMD17: READ_SINGLE_BLOCK */
reg_write32(base + MMC_ARG, addr);
reg32_t cmd_reg = (SD_CMD_READ_SINGLE_BLOCK << MMC_CMD_INDX_SHIFT)
| MMC_CMD_RSP_48B | MMC_CMD_DP;
reg_write32(base + MMC_CMD, cmd_reg);
/* Wait for transfer complete */
if (mmc_wait_status(dev, MMC_STAT_TC, 5000000) != 0)
return -1;
/* Read data from FIFO */
uint32_t *buf32 = (uint32_t *)buf;
for (int i = 0; i < MMC_BLOCK_SIZE / 4; i++) {
while (!(reg_read32(base + MMC_PSTATE) & (1 << 4))) /* BREN */
;
buf32[i] = reg_read32(base + MMC_DATA);
}
/* Clear status */
reg_write32(base + MMC_STAT, 0xFFFFFFFF);
return 0;
}
int mmc_read_blocks(mmc_dev_t *dev, uint32_t start, uint8_t *buf, uint32_t count) {
for (uint32_t i = 0; i < count; i++) {
if (mmc_read_block(dev, start + i, buf + i * MMC_BLOCK_SIZE) != 0)
return -1;
}
return 0;
}