Sam Lewis has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/44384 )
Change subject: soc/ti/am335x: Add MMC/SD driver ......................................................................
soc/ti/am335x: Add MMC/SD driver
Adds a driver for the am335x MMC peripheral. This has only been tested with SD cards and probably needs some modification to use eMMC or MMC cards.
It's also currently a little slow as it only supports reading a block at a time.
Change-Id: I5c2b250782cddca17aa46cc8222b9aebef505fb2 Signed-off-by: Sam Lewis sam.vr.lewis@gmail.com --- A src/soc/ti/am335x/mmc.c A src/soc/ti/am335x/mmc.h 2 files changed, 311 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/84/44384/1
diff --git a/src/soc/ti/am335x/mmc.c b/src/soc/ti/am335x/mmc.c new file mode 100644 index 0000000..993fe8c --- /dev/null +++ b/src/soc/ti/am335x/mmc.c @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <inttypes.h> +#include <string.h> +#include <console/console.h> +#include <commonlib/sd_mmc_ctrlr.h> +#include <device/mmio.h> +#include "mmc.h" + +#define SYSCONFIG_SOFTRESET (0x1 << 1) + +#define SYSSTATUS_RESETDONE (0x01 << 0) + +#define CON_INIT (0x1 << 1) + +#define CMD_INDEX(x) (x << 24) +#define CMD_RSP_TYPE_NO_RESP (0x0 << 16) +#define CMD_RSP_TYPE_136B (0x1 << 16) +#define CMD_RSP_TYPE_48B (0x2 << 16) +#define CMD_RSP_TYPE_48B_BUSY (0x3 << 16) +#define CMD_DP_DATA (0x1 << 21) +#define CMD_DDIR_READ (0x1 << 4) + +#define HCTL_DTW_1BIT (0x0 << 1) +#define HCTL_SDBP (0x1 << 8) +#define HCTL_SDVS_VS30 (0x6 << 9) + +#define SYSCTL_ICE (0x1 << 0) +#define SYSCTL_ICS (0x1 << 1) +#define SYSCTL_CEN (0x1 << 2) +#define SYSCTL_DTO_15 (0xE << 16) + +#define STAT_ERRI (0x01 << 15) +#define STAT_ERROR_MASK (0xff << 15 | 0x3 << 24 | 0x03 << 28) +#define STAT_CC (0x1 << 0) + +#define IE_CC (0x1 << 0) +#define IE_TC (0x1 << 1) +#define IE_BRR (0x1 << 5) +#define IE_ERRORS (0xff << 15 | 0x3 << 24 | 0x03 << 28) + +#define CAPA_VS18 (0x01 << 26) +#define CAPA_VS30 (0x01 << 25) + +static int am335x_mmc_init(struct am335x_mmc *mmc) +{ + // Follows the initialisiation from the AM335X technical reference manual + setbits32(&mmc->sysconfig, SYSCONFIG_SOFTRESET); + + while (!(read32(&mmc->sysstatus) & SYSSTATUS_RESETDONE)) + ; + + setbits32(&mmc->capa, CAPA_VS30); + setbits32(&mmc->hctl, HCTL_SDVS_VS30 | HCTL_DTW_1BIT); + setbits32(&mmc->hctl, HCTL_SDBP); + + while (!(read32(&mmc->hctl) & HCTL_SDBP)) + ; + + // Assumes the default input clock speed of 96MHz to set a minimum SD + // speed of 400 KHz + write32(&mmc->sysctl, read32(&mmc->sysctl) | 240 << 6 | SYSCTL_DTO_15); + + setbits32(&mmc->sysctl, SYSCTL_ICE | SYSCTL_CEN); + + while (!(read32(&mmc->sysctl) & SYSCTL_ICS)) + ; + + write32(&mmc->ie, IE_ERRORS | IE_TC | IE_CC); + + // Clear interrupts + write32(&mmc->stat, 0xffffffffu); + + setbits32(&mmc->con, CON_INIT); + write32(&mmc->cmd, 0x00); + + while (!(read32(&mmc->stat) & STAT_CC)) + ; + + write32(&mmc->stat, 0xffffffffu); + clrbits32(&mmc->con, CON_INIT); + + return 0; +} + +static int am335x_send_cmd(struct sd_mmc_ctrlr *ctrlr, struct mmc_command *cmd, + struct mmc_data *data) +{ + struct am335x_mmc_host *mmc; + struct am335x_mmc *reg; + + mmc = container_of(ctrlr, struct am335x_mmc_host, sd_mmc_ctrlr); + reg = mmc->reg; + + if (read32(®->stat)) { + printk(BIOS_WARNING, "AM335X MMC: Interrupt already raised\n"); + return 1; + } + + uint32_t transfer_type = 0; + + if (data) { + if (data->flags & DATA_FLAG_READ) { + setbits32(&mmc->reg->ie, IE_BRR); + write32(&mmc->reg->blk, data->blocksize); + transfer_type |= CMD_DP_DATA | CMD_DDIR_READ; + } + + if (data->flags & DATA_FLAG_WRITE) { + printk(BIOS_ERR, "AM335X MMC: Writes currently not supported\n"); + return 1; + } + } + + switch (cmd->resp_type) { + case CARD_RSP_R1b: + transfer_type |= CMD_RSP_TYPE_48B_BUSY; + break; + case CARD_RSP_R1: + case CARD_RSP_R3: + transfer_type |= CMD_RSP_TYPE_48B; + break; + case CARD_RSP_R2: + transfer_type |= CMD_RSP_TYPE_136B; + break; + case CARD_RSP_NONE: + transfer_type |= CMD_RSP_TYPE_NO_RESP; + break; + default: + printk(BIOS_ERR, "AM335X MMC: Unknown response type\n"); + return 1; + } + + if (cmd->cmdidx == MMC_CMD_SET_BLOCKLEN) { + // todo: Support bigger blocks for faster transfers + return 0; + } + + write32(®->arg, cmd->cmdarg); + write32(®->cmd, CMD_INDEX(cmd->cmdidx) | transfer_type); + + // Wait for any interrupt + while (!read32(®->stat)) + ; + + // Check to ensure that there was not any errors + if (read32(®->stat) & STAT_ERRI) { + printk(BIOS_WARNING, "AM335X MMC: Error while reading %08x\n", + read32(®->stat)); + + // Clear the errors + write32(®->stat, STAT_ERROR_MASK); + return 1; + } + + if (cmd->resp_type == CARD_RSP_R1b) { + while (!(read32(®->stat) & IE_TC)) + ; + write32(®->stat, IE_TC); + } + + write32(®->stat, IE_CC); + + switch (cmd->resp_type) { + case CARD_RSP_R1: + case CARD_RSP_R1b: + case CARD_RSP_R3: + cmd->response[0] = read32(®->rsp10); + break; + case CARD_RSP_R2: + cmd->response[3] = read32(®->rsp10); + cmd->response[2] = read32(®->rsp32); + cmd->response[1] = read32(®->rsp54); + cmd->response[0] = read32(®->rsp76); + break; + case CARD_RSP_NONE: + break; + } + + if (data != NULL && data->flags & DATA_FLAG_READ) { + while (!(read32(®->stat) & IE_BRR)) + ; + + uint32_t *dest32 = (uint32_t *)data->dest; + + for (int count = 0; count < data->blocksize; count += 4) { + *dest32 = read32(®->data); + dest32++; + } + + write32(®->stat, IE_TC); + write32(®->stat, IE_BRR); + clrbits32(®->ie, IE_BRR); + } + + return 0; +} + +static void set_ios(struct sd_mmc_ctrlr *ctrlr) +{ + struct am335x_mmc_host *mmc; + struct am335x_mmc *reg; + + mmc = container_of(ctrlr, struct am335x_mmc_host, sd_mmc_ctrlr); + reg = mmc->reg; + + if (ctrlr->request_hz != ctrlr->bus_hz) { + uint32_t requested_hz = ctrlr->request_hz; + + requested_hz = MIN(requested_hz, ctrlr->f_min); + requested_hz = MAX(requested_hz, ctrlr->f_max); + + uint32_t divisor = mmc->sd_clock_hz / requested_hz; + uint32_t actual = mmc->sd_clock_hz * divisor; + + if (actual != ctrlr->bus_hz) { + clrbits32(®->sysctl, SYSCTL_CEN); + + uint32_t new_sysctl = read32(®->sysctl); + new_sysctl &= ~(0x3ff << 6); + new_sysctl |= ((divisor & 0x3ff) << 6); + + write32(®->sysctl, new_sysctl); + + // Wait for clock stability + while (!(read32(®->sysctl) & SYSCTL_ICS)) + ; + + setbits32(®->sysctl, SYSCTL_CEN); + + ctrlr->bus_hz = mmc->sd_clock_hz / divisor; + } + } +} + +int am335x_mmc_init_storage(struct am335x_mmc_host *mmc_host) +{ + int err = 0; + + struct sd_mmc_ctrlr *mmc_ctrlr = &mmc_host->sd_mmc_ctrlr; + memset(mmc_ctrlr, 0, sizeof(mmc_ctrlr)); + + + err = am335x_mmc_init(mmc_host->reg); + if (err != 0) { + printk(BIOS_ERR, "ERROR: Initialising AM335X SD failed.\n"); + return err; + } + + mmc_ctrlr->send_cmd = &am335x_send_cmd; + mmc_ctrlr->set_ios = &set_ios; + + mmc_ctrlr->voltages = MMC_VDD_30_31; + mmc_ctrlr->b_max = 1; + mmc_ctrlr->bus_width = 1; + mmc_ctrlr->f_max = 48000000; + mmc_ctrlr->f_min = 400000; + mmc_ctrlr->bus_hz = 400000; + + return 0; +} diff --git a/src/soc/ti/am335x/mmc.h b/src/soc/ti/am335x/mmc.h new file mode 100644 index 0000000..761aa4e --- /dev/null +++ b/src/soc/ti/am335x/mmc.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef AM335X_MMC_H +#define AM335X_MMC_H + +#include <inttypes.h> +#include <commonlib/sd_mmc_ctrlr.h> + +#define MMCHS0_BASE 0x48060000 + +struct am335x_mmc { + uint8_t res1[0x110]; + uint32_t sysconfig; + uint32_t sysstatus; + uint8_t res2[0x14]; + uint32_t con; + uint32_t pwcnt; + uint32_t dll; + uint8_t res3[0xcc]; + uint32_t blk; + uint32_t arg; + uint32_t cmd; + uint32_t rsp10; + uint32_t rsp32; + uint32_t rsp54; + uint32_t rsp76; + uint32_t data; + uint32_t pstate; + uint32_t hctl; + uint32_t sysctl; + uint32_t stat; + uint32_t ie; + uint8_t res4[0x4]; + uint32_t ac12; + uint32_t capa; + uint32_t capa2; + uint8_t res5[0xc]; + uint32_t admaes; + uint32_t admasal; +}; + +struct am335x_mmc_host { + struct sd_mmc_ctrlr sd_mmc_ctrlr; + struct am335x_mmc *reg; + uint32_t sd_clock_hz; +}; + +int am335x_mmc_init_storage(struct am335x_mmc_host *mmc_host); + +#endif