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
Hello build bot (Jenkins),
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/44384
to look at the new patch set (#2).
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/2
Hello build bot (Jenkins),
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/44384
to look at the new patch set (#9).
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 --- M src/mainboard/ti/beaglebone/Kconfig M src/mainboard/ti/beaglebone/Makefile.inc A src/mainboard/ti/beaglebone/board.fmd A src/mainboard/ti/beaglebone/sd_media.c M src/soc/ti/am335x/Makefile.inc D src/soc/ti/am335x/bootblock_media.c M src/soc/ti/am335x/header.c M src/soc/ti/am335x/header.h M src/soc/ti/am335x/memlayout.ld A src/soc/ti/am335x/mmc.c A src/soc/ti/am335x/mmc.h D src/soc/ti/am335x/nand.c 12 files changed, 483 insertions(+), 47 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/84/44384/9
Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Julius Werner,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/44384
to look at the new patch set (#10).
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/10
Arthur Heymans has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/44384 )
Change subject: soc/ti/am335x: Add MMC/SD driver ......................................................................
Patch Set 10: Code-Review+2
(2 comments)
https://review.coreboot.org/c/coreboot/+/44384/10/src/soc/ti/am335x/mmc.h File src/soc/ti/am335x/mmc.h:
https://review.coreboot.org/c/coreboot/+/44384/10/src/soc/ti/am335x/mmc.h@11 PS10, Line 11: 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; : }; You might want to use the __packed directive?
https://review.coreboot.org/c/coreboot/+/44384/10/src/soc/ti/am335x/mmc.c File src/soc/ti/am335x/mmc.c:
https://review.coreboot.org/c/coreboot/+/44384/10/src/soc/ti/am335x/mmc.c@50 PS10, Line 50: while (!(read32(&mmc->sysstatus) & SYSSTATUS_RESETDONE)) : ; It would be nicer with timeouts.
Attention is currently required from: Sam Lewis. Paul Menzel has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/44384 )
Change subject: soc/ti/am335x: Add MMC/SD driver ......................................................................
Patch Set 10:
(1 comment)
Patchset:
PS10: Sam, to get this committed, are you planning on addressing Arthur’s comments, or should it be submitted as is?
Attention is currently required from: Sam Lewis. Hello build bot (Jenkins), Patrick Georgi, Martin Roth, Julius Werner, Arthur Heymans,
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/coreboot/+/44384
to look at the new patch set (#11).
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, 333 insertions(+), 0 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/84/44384/11
Attention is currently required from: Paul Menzel, Arthur Heymans. Sam Lewis has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/44384 )
Change subject: soc/ti/am335x: Add MMC/SD driver ......................................................................
Patch Set 11:
(3 comments)
Patchset:
PS10:
Sam, to get this committed, are you planning on addressing Arthur’s comments, or should it be submit […]
I've addressed Arthur's comments and it should be good to be merged now. Sorry for taking so long!
File src/soc/ti/am335x/mmc.h:
https://review.coreboot.org/c/coreboot/+/44384/comment/a7072a06_46f0f34c PS10, Line 11: 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; : };
You might want to use the __packed directive?
Done
File src/soc/ti/am335x/mmc.c:
https://review.coreboot.org/c/coreboot/+/44384/comment/69b95af2_7322536a PS10, Line 50: while (!(read32(&mmc->sysstatus) & SYSSTATUS_RESETDONE)) : ;
It would be nicer with timeouts.
Have now added a rudimentary timeout for all of the polling register loops.
Attention is currently required from: Sam Lewis, Paul Menzel. Arthur Heymans has posted comments on this change. ( https://review.coreboot.org/c/coreboot/+/44384 )
Change subject: soc/ti/am335x: Add MMC/SD driver ......................................................................
Patch Set 11: Code-Review+2
Arthur Heymans has submitted this change. ( 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 Reviewed-on: https://review.coreboot.org/c/coreboot/+/44384 Tested-by: build bot (Jenkins) no-reply@coreboot.org Reviewed-by: Arthur Heymans arthur@aheymans.xyz --- A src/soc/ti/am335x/mmc.c A src/soc/ti/am335x/mmc.h 2 files changed, 333 insertions(+), 0 deletions(-)
Approvals: build bot (Jenkins): Verified Arthur Heymans: Looks good to me, approved
diff --git a/src/soc/ti/am335x/mmc.c b/src/soc/ti/am335x/mmc.c new file mode 100644 index 0000000..395cf4f --- /dev/null +++ b/src/soc/ti/am335x/mmc.c @@ -0,0 +1,283 @@ +/* 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 <timer.h> +#include "mmc.h" + +#define AM335X_TIMEOUT_MSEC 1000 + +#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_wait_for_reg(const void *addr, uint32_t mask, unsigned long timeout) +{ + struct mono_time current, end; + + timer_monotonic_get(¤t); + end = current; + mono_time_add_msecs(&end, timeout); + + do { + if ((read32(addr) & mask)) + return 0; + + timer_monotonic_get(¤t); + } while (!mono_time_after(¤t, &end)); + + printk(BIOS_DEBUG, "am335x MMC timeout: %ld msec\n", timeout); + return -1; +} + +static int am335x_mmc_init(struct am335x_mmc *mmc) +{ + // Follows the initialisiation from the AM335X technical reference manual + setbits32(&mmc->sysconfig, SYSCONFIG_SOFTRESET); + + if (am335x_wait_for_reg(&mmc->sysstatus, SYSSTATUS_RESETDONE, AM335X_TIMEOUT_MSEC)) + return -1; + + setbits32(&mmc->capa, CAPA_VS30); + setbits32(&mmc->hctl, HCTL_SDVS_VS30 | HCTL_DTW_1BIT); + setbits32(&mmc->hctl, HCTL_SDBP); + + if (am335x_wait_for_reg(&mmc->hctl, HCTL_SDBP, AM335X_TIMEOUT_MSEC)) + return -1; + + // 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); + + if (am335x_wait_for_reg(&mmc->sysctl, SYSCTL_ICS, AM335X_TIMEOUT_MSEC)) + return -1; + + write32(&mmc->ie, IE_ERRORS | IE_TC | IE_CC); + + // Clear interrupts + write32(&mmc->stat, 0xffffffffu); + + setbits32(&mmc->con, CON_INIT); + write32(&mmc->cmd, 0x00); + + if (am335x_wait_for_reg(&mmc->stat, STAT_CC, AM335X_TIMEOUT_MSEC)) + return -1; + + 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 + if (am335x_wait_for_reg(®->stat, 0xffffffff, AM335X_TIMEOUT_MSEC)) + return -1; + + // 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) { + if (am335x_wait_for_reg(®->stat, IE_TC, AM335X_TIMEOUT_MSEC)) + return -1; + + 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) { + if (am335x_wait_for_reg(®->stat, IE_BRR, AM335X_TIMEOUT_MSEC)) + return -1; + + 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 + am335x_wait_for_reg(®->sysctl, SYSCTL_ICS, AM335X_TIMEOUT_MSEC); + + 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..757795d --- /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; +} __packed; + +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