mirror of https://github.com/wolfSSL/wolfBoot.git
1934 lines
60 KiB
C
1934 lines
60 KiB
C
/* mpfs250.c
|
|
*
|
|
* Copyright (C) 2025 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfBoot.
|
|
*
|
|
* wolfBoot is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfBoot is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
/* Microchip PolarFire SoC MPFS250T HAL for wolfBoot */
|
|
/* Supports:
|
|
* RISC-V 64-bit architecture
|
|
* External flash operations
|
|
* UART communication
|
|
* System initialization
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "target.h"
|
|
|
|
#include "mpfs250.h"
|
|
#include "image.h"
|
|
#ifndef ARCH_RISCV64
|
|
# error "wolfBoot mpfs250 HAL: wrong architecture selected. Please compile with ARCH=RISCV64."
|
|
#endif
|
|
|
|
#include "printf.h"
|
|
#include "loader.h"
|
|
#include "hal.h"
|
|
#include "disk.h"
|
|
#include "gpt.h"
|
|
#include "fdt.h"
|
|
|
|
#ifdef DISK_TEST
|
|
static int disk_test(int drv);
|
|
#endif
|
|
|
|
void hal_init(void)
|
|
{
|
|
wolfBoot_printf("wolfBoot Version: %s (%s %s)\n",
|
|
LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__);
|
|
}
|
|
|
|
/* Linux kernel command line arguments */
|
|
#ifndef LINUX_BOOTARGS
|
|
#ifndef LINUX_BOOTARGS_ROOT
|
|
#define LINUX_BOOTARGS_ROOT "/dev/mmcblk0p4"
|
|
#endif
|
|
|
|
#define LINUX_BOOTARGS \
|
|
"earlycon root="LINUX_BOOTARGS_ROOT" rootwait uio_pdrv_genirq.of_id=generic-uio"
|
|
#endif
|
|
|
|
int hal_dts_fixup(void* dts_addr)
|
|
{
|
|
int off, ret;
|
|
struct fdt_header *fdt = (struct fdt_header *)dts_addr;
|
|
|
|
/* Verify FDT header */
|
|
ret = fdt_check_header(dts_addr);
|
|
if (ret != 0) {
|
|
wolfBoot_printf("FDT: Invalid header! %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
wolfBoot_printf("FDT: Version %d, Size %d\n",
|
|
fdt_version(fdt), fdt_totalsize(fdt));
|
|
|
|
/* Expand total size to allow adding/modifying properties */
|
|
fdt_set_totalsize(fdt, fdt_totalsize(fdt) + 512);
|
|
|
|
/* Find /chosen node */
|
|
off = fdt_find_node_offset(fdt, -1, "chosen");
|
|
if (off < 0) {
|
|
/* Create /chosen node if it doesn't exist */
|
|
off = fdt_add_subnode(fdt, 0, "chosen");
|
|
}
|
|
|
|
if (off >= 0) {
|
|
/* Set bootargs property */
|
|
fdt_fixup_str(fdt, off, "chosen", "bootargs", LINUX_BOOTARGS);
|
|
}
|
|
|
|
/* TODO: Consider additional FDT fixups:
|
|
* ethernet0: local-mac-address {0x00, 0x04, 0xA3, SERIAL2, SERIAL1, SERIAL0} */
|
|
|
|
return 0;
|
|
}
|
|
void hal_prepare_boot(void)
|
|
{
|
|
/* reset the eMMC/SD card? */
|
|
|
|
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_unlock(void)
|
|
{
|
|
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_lock(void)
|
|
{
|
|
|
|
}
|
|
|
|
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
|
{
|
|
(void)address;
|
|
(void)data;
|
|
(void)len;
|
|
return 0;
|
|
}
|
|
|
|
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
|
{
|
|
(void)address;
|
|
(void)len;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef EXT_FLASH
|
|
/* External flash support */
|
|
void ext_flash_lock(void)
|
|
{
|
|
/* TODO: Lock external flash */
|
|
}
|
|
|
|
void ext_flash_unlock(void)
|
|
{
|
|
/* TODO: Unlock external flash */
|
|
}
|
|
|
|
int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|
{
|
|
/* TODO: Write to external flash */
|
|
(void)address;
|
|
(void)data;
|
|
(void)len;
|
|
return 0;
|
|
}
|
|
|
|
int ext_flash_read(uintptr_t address, uint8_t *data, int len)
|
|
{
|
|
/* TODO: Read from external flash */
|
|
(void)address;
|
|
(void)data;
|
|
(void)len;
|
|
return 0;
|
|
}
|
|
|
|
int ext_flash_erase(uintptr_t address, int len)
|
|
{
|
|
/* TODO: Erase external flash sectors */
|
|
(void)address;
|
|
(void)len;
|
|
return 0;
|
|
}
|
|
#endif /* EXT_FLASH */
|
|
|
|
#if defined(MMU) && !defined(WOLFBOOT_NO_PARTITIONS)
|
|
void* hal_get_dts_address(void)
|
|
{
|
|
return (void*)WOLFBOOT_DTS_BOOT_ADDRESS;
|
|
}
|
|
#endif
|
|
|
|
static uint32_t g_sector_count;
|
|
static uint32_t g_sector_size;
|
|
static uint32_t g_bus_width = 1;
|
|
static uint32_t g_rca = 0; /* SD Card Relative Address */
|
|
|
|
/* MMC Interrupt state - volatile for interrupt handler access */
|
|
static volatile uint32_t g_mmc_irq_status = 0;
|
|
static volatile int g_mmc_irq_pending = 0;
|
|
|
|
/* ==========================================================================
|
|
* PHY Register Access Functions
|
|
* ========================================================================== */
|
|
|
|
/* Write to SD/eMMC PHY register via HRS04 */
|
|
static void mmc_phy_write(uint8_t phy_addr, uint8_t delay_val)
|
|
{
|
|
uint32_t phycfg;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_phy_write: phyaddr: 0x%08x, delay_value: %d\n",
|
|
phy_addr, delay_val);
|
|
#endif
|
|
|
|
/* Wait for ACK to clear */
|
|
while ((EMMC_SD_HRS04 & EMMC_SD_HRS04_UIS_ACK) == 0);
|
|
|
|
/* Set address and delay value */
|
|
phycfg = ((uint32_t)phy_addr & EMMC_SD_HRS04_UIS_ADDR_MASK) |
|
|
((uint32_t)delay_val << EMMC_SD_HRS04_UIS_WDATA_SHIFT);
|
|
EMMC_SD_HRS04 = phycfg;
|
|
|
|
/* Send write request */
|
|
EMMC_SD_HRS04 = phycfg | EMMC_SD_HRS04_UIS_WR;
|
|
/* Wait for ACK */
|
|
while ((EMMC_SD_HRS04 & EMMC_SD_HRS04_UIS_ACK) == 0);
|
|
|
|
/* Clear write request */
|
|
EMMC_SD_HRS04 = phycfg;
|
|
EMMC_SD_HRS04 = 0;
|
|
}
|
|
|
|
/* ============================================================================
|
|
* PLIC - Platform-Level Interrupt Controller Functions
|
|
* ============================================================================ */
|
|
|
|
/* Get the PLIC context for the current hart in S-mode */
|
|
extern unsigned long get_boot_hartid(void);
|
|
static inline uint32_t plic_get_context(void)
|
|
{
|
|
uint32_t hart_id = get_boot_hartid();
|
|
return PLIC_HART_TO_SMODE_CTX(hart_id);
|
|
}
|
|
|
|
/* Set priority for an interrupt source */
|
|
void plic_set_priority(uint32_t irq, uint32_t priority)
|
|
{
|
|
if (irq > 0 && irq < PLIC_NUM_SOURCES && priority <= PLIC_PRIORITY_MAX) {
|
|
PLIC_PRIORITY(irq) = priority;
|
|
}
|
|
}
|
|
|
|
/* Enable an interrupt for the current hart's context */
|
|
void plic_enable_interrupt(uint32_t irq)
|
|
{
|
|
uint32_t ctx = plic_get_context();
|
|
if (irq > 0 && irq < PLIC_NUM_SOURCES) {
|
|
PLIC_ENABLE(ctx, irq) |= PLIC_ENABLE_BIT(irq);
|
|
}
|
|
}
|
|
|
|
/* Disable an interrupt for the current hart's context */
|
|
void plic_disable_interrupt(uint32_t irq)
|
|
{
|
|
uint32_t ctx = plic_get_context();
|
|
if (irq > 0 && irq < PLIC_NUM_SOURCES) {
|
|
PLIC_ENABLE(ctx, irq) &= ~PLIC_ENABLE_BIT(irq);
|
|
}
|
|
}
|
|
|
|
/* Set the priority threshold for the current hart's context */
|
|
void plic_set_threshold(uint32_t threshold)
|
|
{
|
|
uint32_t ctx = plic_get_context();
|
|
if (threshold <= PLIC_PRIORITY_MAX) {
|
|
PLIC_THRESHOLD(ctx) = threshold;
|
|
}
|
|
}
|
|
|
|
/* Claim the highest priority pending interrupt (returns IRQ number, 0 if none) */
|
|
uint32_t plic_claim(void)
|
|
{
|
|
uint32_t ctx = plic_get_context();
|
|
return PLIC_CLAIM(ctx);
|
|
}
|
|
|
|
/* Signal completion of interrupt handling */
|
|
void plic_complete(uint32_t irq)
|
|
{
|
|
uint32_t ctx = plic_get_context();
|
|
PLIC_COMPLETE(ctx) = irq;
|
|
}
|
|
|
|
/* Initialize PLIC for MMC interrupt handling */
|
|
void plic_init_mmc(void)
|
|
{
|
|
/* Set priority for MMC main interrupt */
|
|
plic_set_priority(PLIC_INT_MMC_MAIN, PLIC_PRIORITY_DEFAULT);
|
|
|
|
/* Set threshold to 0 (allow all priorities > 0) */
|
|
plic_set_threshold(0);
|
|
|
|
/* Enable MMC interrupt for this hart */
|
|
plic_enable_interrupt(PLIC_INT_MMC_MAIN);
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("plic_init_mmc: hart %d, context %d, irq %d enabled\n",
|
|
get_boot_hartid(), plic_get_context(), PLIC_INT_MMC_MAIN);
|
|
#endif
|
|
}
|
|
|
|
/* ============================================================================
|
|
* MMC Interrupt Handler
|
|
* ============================================================================ */
|
|
|
|
/* MMC interrupt handler - called from PLIC dispatch */
|
|
void mmc_irq_handler(void)
|
|
{
|
|
uint32_t status = EMMC_SD_SRS12;
|
|
|
|
/* Check for DMA interrupt */
|
|
if (status & EMMC_SD_SRS12_DMAINT) {
|
|
g_mmc_irq_status |= MMC_IRQ_FLAG_DMAINT;
|
|
EMMC_SD_SRS12 = EMMC_SD_SRS12_DMAINT; /* Clear interrupt */
|
|
}
|
|
|
|
/* Check for transfer complete */
|
|
if (status & EMMC_SD_SRS12_TC) {
|
|
g_mmc_irq_status |= MMC_IRQ_FLAG_TC;
|
|
EMMC_SD_SRS12 = EMMC_SD_SRS12_TC; /* Clear interrupt */
|
|
}
|
|
|
|
/* Check for command complete */
|
|
if (status & EMMC_SD_SRS12_CC) {
|
|
g_mmc_irq_status |= MMC_IRQ_FLAG_CC;
|
|
EMMC_SD_SRS12 = EMMC_SD_SRS12_CC; /* Clear interrupt */
|
|
}
|
|
|
|
/* Check for data timeout error */
|
|
if (status & EMMC_SD_SRS12_EDT) {
|
|
g_mmc_irq_status |= MMC_IRQ_FLAG_ERROR;
|
|
EMMC_SD_SRS12 = EMMC_SD_SRS12_EDT; /* Clear interrupt */
|
|
}
|
|
|
|
/* Check for any other errors */
|
|
if (status & EMMC_SD_SRS12_EINT) {
|
|
g_mmc_irq_status |= MMC_IRQ_FLAG_ERROR;
|
|
/* Clear all error status bits */
|
|
EMMC_SD_SRS12 = (status & EMMC_SD_SRS12_ERR_STAT);
|
|
}
|
|
|
|
/* Signal that interrupt was handled */
|
|
g_mmc_irq_pending = 1;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_irq_handler: status=0x%08X, flags=0x%02X\n",
|
|
status, g_mmc_irq_status);
|
|
#endif
|
|
}
|
|
|
|
/* Enable MMC interrupts for SDMA transfer */
|
|
static void mmc_enable_sdma_interrupts(void)
|
|
{
|
|
/* Enable signal interrupts for: DMA, Transfer Complete, Command Complete,
|
|
* Data Timeout Error */
|
|
uint32_t sig_enable = EMMC_SD_SRS14_DMAINT_IE |
|
|
EMMC_SD_SRS14_TC_IE |
|
|
EMMC_SD_SRS14_CC_IE |
|
|
EMMC_SD_SRS14_EDT_IE;
|
|
EMMC_SD_SRS14 |= sig_enable;
|
|
|
|
/* Clear any pending interrupt state */
|
|
g_mmc_irq_status = 0;
|
|
g_mmc_irq_pending = 0;
|
|
}
|
|
|
|
/* Disable MMC signal interrupts (status enables remain for polling) */
|
|
static void mmc_disable_sdma_interrupts(void)
|
|
{
|
|
EMMC_SD_SRS14 &= ~(EMMC_SD_SRS14_DMAINT_IE |
|
|
EMMC_SD_SRS14_TC_IE |
|
|
EMMC_SD_SRS14_CC_IE |
|
|
EMMC_SD_SRS14_EDT_IE);
|
|
}
|
|
|
|
/* Wait for MMC interrupt with timeout */
|
|
static int mmc_wait_irq(uint32_t expected_flags, uint32_t timeout)
|
|
{
|
|
while (timeout-- > 0) {
|
|
if (g_mmc_irq_pending) {
|
|
g_mmc_irq_pending = 0;
|
|
|
|
/* Check for error */
|
|
if (g_mmc_irq_status & MMC_IRQ_FLAG_ERROR) {
|
|
return -1;
|
|
}
|
|
|
|
/* Check for expected flags */
|
|
if (g_mmc_irq_status & expected_flags) {
|
|
return 0;
|
|
}
|
|
}
|
|
/* Brief delay while waiting */
|
|
asm volatile("nop");
|
|
}
|
|
return -1; /* Timeout */
|
|
}
|
|
|
|
static int mmc_set_timeout(uint32_t timeout_us)
|
|
{
|
|
uint32_t reg, i, tcfclk, tcfclk_mhz, tcfclk_khz, timeout_val, dtcv;
|
|
|
|
/* read capabilities to determine timeout clock frequency and unit (MHz or kHz) */
|
|
reg = EMMC_SD_SRS16;
|
|
tcfclk_khz = (reg & EMMC_SD_SRS16_TCF_MASK) >> EMMC_SD_SRS16_TCF_SHIFT;
|
|
/* Default timeout clock frequency should be 50MHz */
|
|
|
|
if (((reg & EMMC_SD_SRS16_TCU) == 0) && (timeout_us < 1000)) {
|
|
/* invalid timeout_us value */
|
|
return -1;
|
|
}
|
|
if (tcfclk_khz == 0) {
|
|
/* reported timeout clock frequency is 0 */
|
|
return -1;
|
|
}
|
|
|
|
if ((reg & EMMC_SD_SRS16_TCU) != 0) {
|
|
tcfclk_khz *= 1000; /* MHz to kHz */
|
|
}
|
|
tcfclk_mhz = tcfclk_khz / 1000;
|
|
if (tcfclk_mhz == 0) {
|
|
tcfclk = tcfclk_khz;
|
|
timeout_val = timeout_us / 1000;
|
|
}
|
|
else {
|
|
tcfclk = tcfclk_mhz;
|
|
timeout_val = timeout_us;
|
|
}
|
|
|
|
/* calculate the data timeout counter value */
|
|
dtcv = 8192; /* 2*13 */
|
|
for (i=0; i<15; i++) {
|
|
if (timeout_val < (dtcv / tcfclk)) {
|
|
break;
|
|
}
|
|
dtcv *= 2;
|
|
}
|
|
dtcv = i;
|
|
|
|
/* set the data timeout counter value */
|
|
reg = EMMC_SD_SRS11;
|
|
reg &= ~EMMC_SD_SRS11_DTCV_MASK;
|
|
reg |= (dtcv << EMMC_SD_SRS11_DTCV_SHIFT) & EMMC_SD_SRS11_DTCV_MASK;
|
|
EMMC_SD_SRS11 = reg;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_set_timeout: timeout_val %d (%d)\n", timeout_val, dtcv);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* voltage values:
|
|
* 0 = off
|
|
* EMMC_SD_SRS10_BVS_1_8V
|
|
* EMMC_SD_SRS10_BVS_3_0V
|
|
* EMMC_SD_SRS10_BVS_3_3V
|
|
*/
|
|
static int mmc_set_power(uint32_t voltage)
|
|
{
|
|
uint32_t reg;
|
|
|
|
/* disable bus power */
|
|
reg = EMMC_SD_SRS10;
|
|
reg &= ~EMMC_SD_SRS10_BP;
|
|
EMMC_SD_SRS10 = reg;
|
|
|
|
if (voltage != 0) {
|
|
/* read voltage capabilities */
|
|
uint32_t cap2 = EMMC_SD_SRS16;
|
|
|
|
/* select voltage (if capable) */
|
|
reg &= ~EMMC_SD_SRS10_BVS_MASK;
|
|
if (voltage == EMMC_SD_SRS10_BVS_1_8V && (cap2 & EMMC_SD_SRS16_VS18)) {
|
|
reg |= EMMC_SD_SRS10_BP | EMMC_SD_SRS10_BVS_1_8V;
|
|
}
|
|
else if (voltage == EMMC_SD_SRS10_BVS_3_0V && (cap2 & EMMC_SD_SRS16_VS30)) {
|
|
reg |= EMMC_SD_SRS10_BP | EMMC_SD_SRS10_BVS_3_0V;
|
|
}
|
|
else if (voltage == EMMC_SD_SRS10_BVS_3_3V && (cap2 & EMMC_SD_SRS16_VS33)) {
|
|
reg |= EMMC_SD_SRS10_BP | EMMC_SD_SRS10_BVS_3_3V;
|
|
}
|
|
else {
|
|
/* voltage not supported */
|
|
return -1;
|
|
}
|
|
/* should be - 0xf06 */
|
|
EMMC_SD_SRS10 = reg;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* returns actual frequency in kHz */
|
|
static uint32_t mmc_set_clock(uint32_t clock_khz)
|
|
{
|
|
static uint32_t last_clock_khz = 0;
|
|
uint32_t reg, base_clk_khz, i, mclk, freq_khz;
|
|
|
|
if (last_clock_khz != 0 && last_clock_khz == clock_khz) {
|
|
/* clock already set */
|
|
return 0;
|
|
}
|
|
|
|
/* disable clock */
|
|
EMMC_SD_SRS11 &= ~EMMC_SD_SRS11_SDCE;
|
|
|
|
/* get base clock */
|
|
reg = EMMC_SD_SRS16;
|
|
base_clk_khz = (reg & EMMC_SD_SRS16_BCSDCLK_MASK) >> EMMC_SD_SRS16_BCSDCLK_SHIFT;
|
|
if (base_clk_khz == 0) {
|
|
/* error getting base clock */
|
|
return -1;
|
|
}
|
|
base_clk_khz *= 1000; /* convert MHz to kHz */
|
|
|
|
/* calculate divider */
|
|
for (i=1; i<2046; i++) {
|
|
if (((base_clk_khz / i) < clock_khz) ||
|
|
(((base_clk_khz / i) == clock_khz) && (base_clk_khz % i) == 0)) {
|
|
break;
|
|
}
|
|
}
|
|
mclk = (i / 2);
|
|
|
|
/* select clock frequency */
|
|
reg = EMMC_SD_SRS11;
|
|
reg &= ~(EMMC_SD_SRS11_SDCFSL_MASK | EMMC_SD_SRS11_SDCFSH_MASK);
|
|
reg |= (((mclk & 0x0FF) << EMMC_SD_SRS11_SDCFSL_SHIFT) & EMMC_SD_SRS11_SDCFSL_MASK); /* lower 8 bits */
|
|
reg |= (((mclk & 0x300) << EMMC_SD_SRS11_SDCFSH_SHIFT) & EMMC_SD_SRS11_SDCFSH_SHIFT); /* upper 2 bits */
|
|
reg |= EMMC_SD_SRS11_ICE; /* clock enable */
|
|
reg &= ~EMMC_SD_SRS11_CGS; /* select clock */
|
|
EMMC_SD_SRS11 = reg;
|
|
freq_khz = base_clk_khz / i;
|
|
|
|
/* wait for clock to stabilize */
|
|
while ((EMMC_SD_SRS11 & EMMC_SD_SRS11_ICS) == 0);
|
|
|
|
/* enable clock */
|
|
EMMC_SD_SRS11 |= EMMC_SD_SRS11_SDCE;
|
|
last_clock_khz = clock_khz;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_set_clock: requested khz: %d, actual khz: %d\n",
|
|
clock_khz, freq_khz);
|
|
#endif
|
|
|
|
return freq_khz;
|
|
}
|
|
|
|
/* eMMC/SD Response Type */
|
|
typedef enum {
|
|
EMMC_SD_RESP_NONE,
|
|
EMMC_SD_RESP_R1,
|
|
EMMC_SD_RESP_R1B,
|
|
EMMC_SD_RESP_R2,
|
|
EMMC_SD_RESP_R3,
|
|
EMMC_SD_RESP_R4,
|
|
EMMC_SD_RESP_R5,
|
|
EMMC_SD_RESP_R5B,
|
|
EMMC_SD_RESP_R6,
|
|
EMMC_SD_RESP_R7,
|
|
EMMC_SD_RESP_R1A
|
|
} EMMC_SD_Resp_t;
|
|
|
|
static uint32_t mmc_get_response_type(uint8_t resp_type)
|
|
{
|
|
uint32_t cmd_reg;
|
|
switch (resp_type) {
|
|
case EMMC_SD_RESP_R2:
|
|
cmd_reg = (EMMC_SD_SRS03_RESP_136 | EMMC_SD_SRS03_CRCCE);
|
|
break;
|
|
case EMMC_SD_RESP_R3:
|
|
case EMMC_SD_RESP_R4:
|
|
cmd_reg = EMMC_SD_SRS03_RESP_48;
|
|
break;
|
|
case EMMC_SD_RESP_R1:
|
|
case EMMC_SD_RESP_R5:
|
|
case EMMC_SD_RESP_R6:
|
|
case EMMC_SD_RESP_R7:
|
|
cmd_reg = (EMMC_SD_SRS03_RESP_48 | EMMC_SD_SRS03_CRCCE | EMMC_SD_SRS03_CICE);
|
|
break;
|
|
case EMMC_SD_RESP_R1B:
|
|
case EMMC_SD_RESP_R5B:
|
|
cmd_reg = (EMMC_SD_SRS03_RESP_48B | EMMC_SD_SRS03_CRCCE | EMMC_SD_SRS03_CICE);
|
|
break;
|
|
case EMMC_SD_RESP_NONE:
|
|
default:
|
|
cmd_reg = EMMC_SD_SRS03_RESP_NONE;
|
|
break;
|
|
}
|
|
return cmd_reg;
|
|
}
|
|
|
|
static int mmc_send_cmd_internal(uint32_t cmd_type,
|
|
uint32_t cmd_index, uint32_t cmd_arg, uint8_t resp_type)
|
|
{
|
|
int status = 0;
|
|
uint32_t cmd_reg;
|
|
uint32_t timeout = 0x000FFFFF;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_send_cmd: cmd_index: %d, cmd_arg: %08X, resp_type: %d\n",
|
|
cmd_index, cmd_arg, resp_type);
|
|
#endif
|
|
|
|
/* wait for command line to be idle */
|
|
while ((EMMC_SD_SRS09 & EMMC_SD_SRS09_CICMD) != 0);
|
|
|
|
/* set command argument and command transfer registers */
|
|
EMMC_SD_SRS02 = cmd_arg;
|
|
cmd_reg =
|
|
((cmd_index << EMMC_SD_SRS03_CIDX_SHIFT) & EMMC_SD_SRS03_CIDX_MASK) |
|
|
((cmd_type << EMMC_SD_SRS03_CT_SHIFT) & EMMC_SD_SRS03_CT_MASK) |
|
|
mmc_get_response_type(resp_type);
|
|
|
|
EMMC_SD_SRS03 = cmd_reg;
|
|
|
|
/* wait for command complete or error */
|
|
while ((EMMC_SD_SRS12 & (EMMC_SD_SRS12_CC | EMMC_SD_SRS12_TC |
|
|
EMMC_SD_SRS12_EINT)) == 0 && --timeout > 0);
|
|
|
|
if (timeout == 0 || (EMMC_SD_SRS12 & EMMC_SD_SRS12_EINT)) {
|
|
wolfBoot_printf("mmc_send_cmd:%s error SRS12: 0x%08X\n",
|
|
(timeout == 0) ? " timeout" : "", EMMC_SD_SRS12);
|
|
status = -1; /* error */
|
|
}
|
|
|
|
EMMC_SD_SRS12 = EMMC_SD_SRS12_CC; /* clear command complete */
|
|
while ((EMMC_SD_SRS09 & EMMC_SD_SRS09_CICMD) != 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
#define DEVICE_BUSY 1
|
|
int mmc_send_cmd(uint32_t cmd_index, uint32_t cmd_arg, uint8_t resp_type)
|
|
{
|
|
/* send command */
|
|
int status = mmc_send_cmd_internal(EMMC_SD_SRS03_CMD_NORMAL, cmd_index,
|
|
cmd_arg, resp_type);
|
|
if (status == 0) {
|
|
/* check for device busy */
|
|
if (resp_type == EMMC_SD_RESP_R1 || resp_type == EMMC_SD_RESP_R1B) {
|
|
uint32_t resp = EMMC_SD_SRS04;
|
|
#define CARD_STATUS_READY_FOR_DATA (1U << 8)
|
|
if ((resp & CARD_STATUS_READY_FOR_DATA) == 0) {
|
|
status = DEVICE_BUSY; /* card is busy */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* clear all status interrupts
|
|
* (except current limit, card interrupt/removal/insert) */
|
|
EMMC_SD_SRS12 = ~(EMMC_SD_SRS12_ECL |
|
|
EMMC_SD_SRS12_CINT |
|
|
EMMC_SD_SRS12_CR |
|
|
EMMC_SD_SRS12_CIN);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* TODO: Add timeout */
|
|
static int mmc_wait_busy(int check_dat0)
|
|
{
|
|
uint32_t status;
|
|
if (check_dat0) {
|
|
/* wait for DATA0 not busy */
|
|
while ((EMMC_SD_SRS09 & EMMC_SD_SRS09_DAT0_LVL) == 0);
|
|
}
|
|
/* wait for CMD13 */
|
|
while ((status = mmc_send_cmd(MMC_CMD13_SEND_STATUS,
|
|
(g_rca << SD_RCA_SHIFT), EMMC_SD_RESP_R1)) == DEVICE_BUSY);
|
|
return status;
|
|
}
|
|
|
|
/* Set power and send initialization commands */
|
|
/* voltage: 0=off or EMMC_SD_SRS10_BVS_[X_X]V */
|
|
int mmc_power_init_seq(uint32_t voltage)
|
|
{
|
|
/* Set power to specified voltage */
|
|
int status = mmc_set_power(voltage);
|
|
if (status == 0) {
|
|
/* send CMD0 (go idle) to reset card */
|
|
status = mmc_send_cmd(MMC_CMD0_GO_IDLE, 0, EMMC_SD_RESP_NONE);
|
|
}
|
|
if (status == 0) {
|
|
/* send the operating conditions command */
|
|
status = mmc_send_cmd(SD_CMD8_SEND_IF_COND, IF_COND_27V_33V,
|
|
EMMC_SD_RESP_R7);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int mmc_card_init(uint32_t acmd41_arg, uint32_t *ocr_reg)
|
|
{
|
|
int status = mmc_send_cmd(SD_CMD55_APP_CMD, 0, EMMC_SD_RESP_R1);
|
|
if (status == 0) {
|
|
status = mmc_send_cmd(SD_ACMD41_SEND_OP_COND, acmd41_arg,
|
|
EMMC_SD_RESP_R3);
|
|
if (status == 0) {
|
|
*ocr_reg = EMMC_SD_SRS04;
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("ocr_reg: 0x%08X\n", *ocr_reg);
|
|
#endif
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* MMC_CMD17_READ_SINGLE, MMC_CMD18_READ_MULTIPLE */
|
|
int mmc_read(uint32_t cmd_index, uint32_t block_addr, uint32_t* dst,
|
|
uint32_t sz)
|
|
{
|
|
int status;
|
|
uint32_t block_count;
|
|
uint32_t reg, cmd_reg;
|
|
|
|
/* get block count (round up) */
|
|
block_count = (sz + (EMMC_SD_BLOCK_SIZE - 1)) / EMMC_SD_BLOCK_SIZE;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_read: cmd_index: %d, block_addr: %08X, dst %p, sz: %d (%d blocks)\n",
|
|
cmd_index, block_addr, dst, sz, block_count);
|
|
#endif
|
|
|
|
/* wait for idle */
|
|
status = mmc_wait_busy(0);
|
|
if (status != 0) {
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_read: wait busy error\n");
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
/* reset data and command lines */
|
|
EMMC_SD_SRS11 |= EMMC_SD_SRS11_RESET_DAT_CMD;
|
|
|
|
/* wait for command and data line busy to clear */
|
|
while ((EMMC_SD_SRS09 & (EMMC_SD_SRS09_CICMD | EMMC_SD_SRS09_CIDAT)) != 0);
|
|
|
|
/* set transfer block count */
|
|
EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) | sz;
|
|
|
|
cmd_reg = ((cmd_index << EMMC_SD_SRS03_CIDX_SHIFT) |
|
|
EMMC_SD_SRS03_DPS | EMMC_SD_SRS03_DTDS |
|
|
EMMC_SD_SRS03_BCE | EMMC_SD_SRS03_RECE | EMMC_SD_SRS03_RID |
|
|
EMMC_SD_SRS03_RESP_48 | EMMC_SD_SRS03_CRCCE | EMMC_SD_SRS03_CICE);
|
|
|
|
if (cmd_index == SD_ACMD51_SEND_SCR) {
|
|
status = mmc_send_cmd(SD_CMD16, sz, EMMC_SD_RESP_R1);
|
|
if (status == 0) {
|
|
status = mmc_send_cmd(SD_CMD55_APP_CMD, (g_rca << SD_RCA_SHIFT),
|
|
EMMC_SD_RESP_R1);
|
|
}
|
|
status = 0; /* ignore error */
|
|
}
|
|
else if (cmd_index == MMC_CMD18_READ_MULTIPLE) {
|
|
cmd_reg |= EMMC_SD_SRS03_MSBS; /* enable multi-block select */
|
|
|
|
if (sz >= (512 * 1024)) { /* use DMA */
|
|
cmd_reg |= EMMC_SD_SRS03_DMAE; /* enable DMA */
|
|
|
|
EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) |
|
|
EMMC_SD_SRS01_DMA_BUFF_512KB | EMMC_SD_BLOCK_SIZE;
|
|
|
|
/* SDMA mode (for 32-bit transfers) */
|
|
EMMC_SD_SRS10 |= EMMC_SD_SRS10_DMA_SDMA;
|
|
EMMC_SD_SRS15 |= EMMC_SD_SRS15_HV4E;
|
|
EMMC_SD_SRS16 &= ~EMMC_SD_SRS16_A64S;
|
|
/* set SDMA destination address */
|
|
EMMC_SD_SRS22 = (uint32_t)(uintptr_t)dst;
|
|
EMMC_SD_SRS23 = (uint32_t)(((uint64_t)(uintptr_t)dst) >> 32);
|
|
|
|
/* Enable SDMA interrupts */
|
|
mmc_enable_sdma_interrupts();
|
|
}
|
|
}
|
|
|
|
EMMC_SD_SRS02 = block_addr; /* cmd argument */
|
|
EMMC_SD_SRS03 = cmd_reg; /* execute command */
|
|
|
|
if (cmd_reg & EMMC_SD_SRS03_DMAE) {
|
|
while (1) { /* DMA mode with interrupt support */
|
|
/* Wait for DMA interrupt, transfer complete, or error */
|
|
status = mmc_wait_irq(MMC_IRQ_FLAG_DMAINT | MMC_IRQ_FLAG_TC,
|
|
0x00FFFFFF);
|
|
if (status != 0) {
|
|
/* Timeout or error */
|
|
wolfBoot_printf("mmc_read: SDMA interrupt timeout/error\n");
|
|
status = -1; /* error */
|
|
break;
|
|
}
|
|
|
|
/* Check for transfer complete */
|
|
if (g_mmc_irq_status & MMC_IRQ_FLAG_TC) {
|
|
g_mmc_irq_status &= ~MMC_IRQ_FLAG_TC;
|
|
break; /* Transfer complete */
|
|
}
|
|
|
|
/* Check for DMA boundary interrupt - need to update address */
|
|
if (g_mmc_irq_status & MMC_IRQ_FLAG_DMAINT) {
|
|
g_mmc_irq_status &= ~MMC_IRQ_FLAG_DMAINT;
|
|
/* Read updated DMA address - engine will have incremented */
|
|
dst = (uint32_t*)(uintptr_t)((((uint64_t)EMMC_SD_SRS23) << 32) |
|
|
EMMC_SD_SRS22);
|
|
/* Set new DMA address for next boundary */
|
|
EMMC_SD_SRS22 = (uint32_t)(uintptr_t)dst;
|
|
EMMC_SD_SRS23 = (uint32_t)(((uint64_t)(uintptr_t)dst) >> 32);
|
|
}
|
|
}
|
|
|
|
/* Disable SDMA interrupts after transfer */
|
|
mmc_disable_sdma_interrupts();
|
|
}
|
|
else {
|
|
while (sz > 0) { /* blocking mode */
|
|
/* wait for buffer read ready (or error) */
|
|
while (((reg = EMMC_SD_SRS12) &
|
|
(EMMC_SD_SRS12_BRR | EMMC_SD_SRS12_EINT)) == 0);
|
|
|
|
/* read in buffer - read 4 bytes at a time */
|
|
if (reg & EMMC_SD_SRS12_BRR) {
|
|
uint32_t i, read_sz = sz;
|
|
if (read_sz > EMMC_SD_BLOCK_SIZE) {
|
|
read_sz = EMMC_SD_BLOCK_SIZE;
|
|
}
|
|
for (i=0; i<read_sz; i+=4) {
|
|
*dst = EMMC_SD_SRS08;
|
|
dst++;
|
|
}
|
|
sz -= read_sz;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for any errors */
|
|
reg = EMMC_SD_SRS12;
|
|
if ((reg & EMMC_SD_SRS12_ERR_STAT) == 0) { /* no errors */
|
|
/* if multi-block read, send CMD12 to stop transfer */
|
|
if (cmd_index == MMC_CMD18_READ_MULTIPLE) {
|
|
(void)mmc_send_cmd_internal(EMMC_SD_SRS03_CMD_ABORT,
|
|
MMC_CMD12_STOP_TRANS, (g_rca << SD_RCA_SHIFT),
|
|
EMMC_SD_RESP_R1); /* use R1B for write */
|
|
}
|
|
|
|
/* wait for idle */
|
|
status = mmc_wait_busy(0);
|
|
}
|
|
else {
|
|
wolfBoot_printf("mmc_read: error SRS12: 0x%08X\n", reg);
|
|
status = -1; /* error */
|
|
}
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_read: status: %d\n", status);
|
|
#endif
|
|
|
|
/* clear all status interrupts
|
|
* (except current limit, card interrupt/removal/insert) */
|
|
EMMC_SD_SRS12 = ~(EMMC_SD_SRS12_ECL |
|
|
EMMC_SD_SRS12_CINT |
|
|
EMMC_SD_SRS12_CR |
|
|
EMMC_SD_SRS12_CIN);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* MMC_CMD24_WRITE_SINGLE, MMC_CMD25_WRITE_MULTIPLE */
|
|
int mmc_write(uint32_t cmd_index, uint32_t block_addr, const uint32_t* src,
|
|
uint32_t sz)
|
|
{
|
|
int status;
|
|
uint32_t block_count;
|
|
uint32_t reg, cmd_reg;
|
|
|
|
/* get block count (round up) */
|
|
block_count = (sz + (EMMC_SD_BLOCK_SIZE - 1)) / EMMC_SD_BLOCK_SIZE;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_write: cmd_index: %d, block_addr: %08X, src %p, sz: %d (%d blocks)\n",
|
|
cmd_index, block_addr, src, sz, block_count);
|
|
#endif
|
|
|
|
/* wait for idle */
|
|
status = mmc_wait_busy(0);
|
|
if (status != 0) {
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_write: wait busy error\n");
|
|
#endif
|
|
return status;
|
|
}
|
|
|
|
/* reset data and command lines */
|
|
EMMC_SD_SRS11 |= EMMC_SD_SRS11_RESET_DAT_CMD;
|
|
|
|
/* wait for command and data line busy to clear */
|
|
while ((EMMC_SD_SRS09 & (EMMC_SD_SRS09_CICMD | EMMC_SD_SRS09_CIDAT)) != 0);
|
|
|
|
/* set transfer block count */
|
|
EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) | sz;
|
|
|
|
/* Build command register for write:
|
|
* - DTDS=0 for write direction (DTDS=1 is read)
|
|
* - DPS=1 data present
|
|
* - BCE=1 block count enable
|
|
* - RECE=1 response error check enable
|
|
* - RID=1 response interrupt disable
|
|
*/
|
|
cmd_reg = ((cmd_index << EMMC_SD_SRS03_CIDX_SHIFT) |
|
|
EMMC_SD_SRS03_DPS | /* Data present, no DTDS = write direction */
|
|
EMMC_SD_SRS03_BCE | EMMC_SD_SRS03_RECE | EMMC_SD_SRS03_RID |
|
|
EMMC_SD_SRS03_RESP_48 | EMMC_SD_SRS03_CRCCE | EMMC_SD_SRS03_CICE);
|
|
|
|
if (cmd_index == MMC_CMD25_WRITE_MULTIPLE) {
|
|
cmd_reg |= EMMC_SD_SRS03_MSBS; /* enable multi-block select */
|
|
|
|
if (sz >= (512 * 1024)) { /* use DMA for large transfers */
|
|
cmd_reg |= EMMC_SD_SRS03_DMAE; /* enable DMA */
|
|
|
|
EMMC_SD_SRS01 = (block_count << EMMC_SD_SRS01_BCCT_SHIFT) |
|
|
EMMC_SD_SRS01_DMA_BUFF_512KB | EMMC_SD_BLOCK_SIZE;
|
|
|
|
/* SDMA mode (for 32-bit transfers) */
|
|
EMMC_SD_SRS10 |= EMMC_SD_SRS10_DMA_SDMA;
|
|
EMMC_SD_SRS15 |= EMMC_SD_SRS15_HV4E;
|
|
EMMC_SD_SRS16 &= ~EMMC_SD_SRS16_A64S;
|
|
/* set SDMA source address */
|
|
EMMC_SD_SRS22 = (uint32_t)(uintptr_t)src;
|
|
EMMC_SD_SRS23 = (uint32_t)(((uint64_t)(uintptr_t)src) >> 32);
|
|
|
|
/* Enable SDMA interrupts */
|
|
mmc_enable_sdma_interrupts();
|
|
}
|
|
}
|
|
|
|
/* wait for cmd/data line not busy */
|
|
while ((EMMC_SD_SRS09 &
|
|
(EMMC_SD_SRS09_CICMD | EMMC_SD_SRS09_CIDAT)) != 0);
|
|
|
|
EMMC_SD_SRS02 = block_addr; /* cmd argument */
|
|
EMMC_SD_SRS03 = cmd_reg; /* execute command */
|
|
|
|
if (cmd_reg & EMMC_SD_SRS03_DMAE) {
|
|
while (1) { /* DMA mode with interrupt support */
|
|
/* Wait for DMA interrupt, transfer complete, or error */
|
|
status = mmc_wait_irq(MMC_IRQ_FLAG_DMAINT | MMC_IRQ_FLAG_TC,
|
|
0x00FFFFFF);
|
|
if (status != 0) {
|
|
/* Timeout or error */
|
|
wolfBoot_printf("mmc_write: SDMA interrupt timeout/error\n");
|
|
status = -1; /* error */
|
|
break;
|
|
}
|
|
|
|
/* Check for transfer complete */
|
|
if (g_mmc_irq_status & MMC_IRQ_FLAG_TC) {
|
|
g_mmc_irq_status &= ~MMC_IRQ_FLAG_TC;
|
|
break; /* Transfer complete */
|
|
}
|
|
|
|
/* Check for DMA boundary interrupt - need to update address */
|
|
if (g_mmc_irq_status & MMC_IRQ_FLAG_DMAINT) {
|
|
g_mmc_irq_status &= ~MMC_IRQ_FLAG_DMAINT;
|
|
/* Read updated DMA address - engine will have incremented */
|
|
src = (const uint32_t*)(uintptr_t)((((uint64_t)EMMC_SD_SRS23) << 32) |
|
|
EMMC_SD_SRS22);
|
|
/* Set new DMA address for next boundary */
|
|
EMMC_SD_SRS22 = (uint32_t)(uintptr_t)src;
|
|
EMMC_SD_SRS23 = (uint32_t)(((uint64_t)(uintptr_t)src) >> 32);
|
|
}
|
|
}
|
|
|
|
/* Disable SDMA interrupts after transfer */
|
|
mmc_disable_sdma_interrupts();
|
|
}
|
|
else {
|
|
while (sz > 0) { /* blocking mode */
|
|
/* wait for buffer write ready (or error) */
|
|
while (((reg = EMMC_SD_SRS12) &
|
|
(EMMC_SD_SRS12_BWR | EMMC_SD_SRS12_EINT)) == 0);
|
|
|
|
/* write buffer - write 4 bytes at a time */
|
|
if (reg & EMMC_SD_SRS12_BWR) {
|
|
uint32_t i, write_sz = sz;
|
|
if (write_sz > EMMC_SD_BLOCK_SIZE) {
|
|
write_sz = EMMC_SD_BLOCK_SIZE;
|
|
}
|
|
for (i=0; i<write_sz; i+=4) {
|
|
EMMC_SD_SRS08 = *src;
|
|
src++;
|
|
}
|
|
sz -= write_sz;
|
|
}
|
|
|
|
/* wait for trasnfer complete (or error) */
|
|
while (((reg = EMMC_SD_SRS12) &
|
|
(EMMC_SD_SRS12_TC | EMMC_SD_SRS12_EINT)) == 0);
|
|
}
|
|
}
|
|
|
|
/* check for any errors */
|
|
reg = EMMC_SD_SRS12;
|
|
if ((reg & EMMC_SD_SRS12_ERR_STAT) == 0) { /* no errors */
|
|
/* if multi-block write, send CMD12 to stop transfer */
|
|
if (cmd_index == MMC_CMD25_WRITE_MULTIPLE) {
|
|
status = mmc_send_cmd_internal(EMMC_SD_SRS03_CMD_ABORT,
|
|
MMC_CMD12_STOP_TRANS, (g_rca << SD_RCA_SHIFT),
|
|
EMMC_SD_RESP_R1B); /* R1B for write with busy */
|
|
if (status != 0) {
|
|
wolfBoot_printf("mmc_write: CMD12 stop transfer error\n");
|
|
}
|
|
}
|
|
|
|
/* wait for card to finish programming (DAT0 goes high when ready) */
|
|
if (status == 0) {
|
|
status = mmc_wait_busy(1);
|
|
}
|
|
}
|
|
else {
|
|
wolfBoot_printf("mmc_write: error SRS12: 0x%08X\n", reg);
|
|
status = -1; /* error */
|
|
}
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_write: status: %d\n", status);
|
|
#endif
|
|
|
|
/* clear all status interrupts
|
|
* (except current limit, card interrupt/removal/insert) */
|
|
EMMC_SD_SRS12 = ~(EMMC_SD_SRS12_ECL |
|
|
EMMC_SD_SRS12_CINT |
|
|
EMMC_SD_SRS12_CR |
|
|
EMMC_SD_SRS12_CIN);
|
|
|
|
return status;
|
|
}
|
|
|
|
int mmc_set_bus_width(uint32_t bus_width)
|
|
{
|
|
int status;
|
|
|
|
if (bus_width == g_bus_width) {
|
|
/* nothing to do */
|
|
return 0;
|
|
}
|
|
|
|
/* set bus width */
|
|
status = mmc_send_cmd(SD_CMD55_APP_CMD, g_rca << SD_RCA_SHIFT,
|
|
EMMC_SD_RESP_R1);
|
|
if (status == 0) {
|
|
uint32_t cmd_arg = (bus_width == 4) ? 2 : 0;
|
|
status = mmc_send_cmd(SD_ACMD6_SET_BUS_WIDTH, cmd_arg, EMMC_SD_RESP_R1);
|
|
if (status == 0) {
|
|
/* change host bus width */
|
|
if (bus_width == 4) {
|
|
EMMC_SD_SRS10 |= EMMC_SD_SRS10_DTW;
|
|
}
|
|
else {
|
|
EMMC_SD_SRS10 &= ~EMMC_SD_SRS10_DTW;
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* helper to get bits from the response registers */
|
|
static uint32_t get_srs_bits(int from, int count)
|
|
{
|
|
volatile uint32_t *resp = ((volatile uint32_t*)(EMMC_SD_BASE + 0x210));
|
|
uint32_t mask, ret;
|
|
int off, shft;
|
|
|
|
from -= 8;
|
|
mask = ((count < 32) ? (1U << (uint32_t)count) : 0) - 1;
|
|
off = from / 32;
|
|
shft = from & 31;
|
|
ret = resp[off] >> shft;
|
|
if ((from + shft) > 32) {
|
|
ret |= resp[off + 1] << ((32 - shft) % 32);
|
|
}
|
|
return ret & mask;
|
|
}
|
|
|
|
/* check or set switch function/group:
|
|
* returns 0 if supported */
|
|
int mmc_send_switch_function(uint32_t mode, uint32_t function_number,
|
|
uint32_t group_number)
|
|
{
|
|
int status;
|
|
uint32_t timeout = 4;
|
|
uint32_t cmd_arg;
|
|
uint32_t func_status[64/sizeof(uint32_t)]; /* fixed 512 bits */
|
|
uint8_t* p_func_status = (uint8_t*)func_status;
|
|
|
|
if (group_number > 6 || function_number > 15) {
|
|
return -1; /* Invalid group or function number */
|
|
}
|
|
|
|
cmd_arg = (function_number << ((group_number - 1) * 4));
|
|
do {
|
|
/* first run check to see if function is supported */
|
|
status = mmc_read(SD_CMD6_SWITCH_FUNC,
|
|
(mode | cmd_arg),
|
|
func_status, sizeof(func_status));
|
|
if (status == 0) {
|
|
/* check if busy */
|
|
/* data structure version 368:375
|
|
* (0=supported only, 1=supported and busy) */
|
|
if (p_func_status[17] == 1) {
|
|
/* busy status: group 1 272:287 */
|
|
if ((p_func_status[29 -
|
|
((group_number-1)*2)] & (1 << function_number))) {
|
|
continue; /* busy */
|
|
}
|
|
}
|
|
|
|
/* supported: group 1 415:400 */
|
|
if ((p_func_status[13 -
|
|
((group_number-1)*2)] & (1 << function_number))) {
|
|
status = 0; /* supported */
|
|
}
|
|
else {
|
|
status = -1; /* not supported */
|
|
}
|
|
break;
|
|
}
|
|
} while (status == 0 && --timeout > 0); /* retry until function not busy */
|
|
return status;
|
|
}
|
|
|
|
int mmc_set_function(uint32_t function_number, uint32_t group_number)
|
|
{
|
|
/* send check first */
|
|
int status = mmc_send_switch_function(SDCARD_SWITCH_FUNC_MODE_CHECK,
|
|
function_number, group_number);
|
|
if (status == 0) {
|
|
/* send switch function */
|
|
status = mmc_send_switch_function(SDCARD_SWITCH_FUNC_MODE_SWITCH,
|
|
function_number, group_number);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
#ifdef ENABLE_MMC_SD_TUNING
|
|
/* ==========================================================================
|
|
* SD Tuning Functions (CMD19-based for SDR50/SDR104)
|
|
* ========================================================================== */
|
|
|
|
#define EMMC_SD_TUNING_BLOCK_SIZE 64 /* SD tuning block size (bytes) */
|
|
#define EMMC_SD_TUNING_MAX_LOOPS 40 /* Max tuning iterations per spec */
|
|
|
|
/* Send CMD19 tuning block and read 64 bytes.
|
|
* Based on HSS read_tune_block() for SD_CMD_19_SEND_TUNING_BLK */
|
|
static int mmc_send_tuning_block(uint32_t *data)
|
|
{
|
|
uint32_t cmd_reg, srs12;
|
|
int i;
|
|
|
|
/* Wait for idle */
|
|
while (EMMC_SD_SRS09 & (EMMC_SD_SRS09_CICMD | EMMC_SD_SRS09_CIDAT));
|
|
|
|
/* Clear all status interrupts */
|
|
EMMC_SD_SRS12 = (EMMC_SD_SRS12_NORM_STAT | EMMC_SD_SRS12_ERR_STAT);
|
|
|
|
/* Block length = 64, block count = 1 */
|
|
EMMC_SD_SRS01 = (1 << EMMC_SD_SRS01_BCCT_SHIFT) | EMMC_SD_TUNING_BLOCK_SIZE;
|
|
|
|
/* CMD19: Data present, read direction, R1 response */
|
|
cmd_reg = (SD_CMD19_SEND_TUNING << EMMC_SD_SRS03_CIDX_SHIFT) |
|
|
EMMC_SD_SRS03_DPS | /* Data Present */
|
|
EMMC_SD_SRS03_DTDS | /* Data Transfer Direction: Read */
|
|
EMMC_SD_SRS03_BCE | /* Block Count Enable */
|
|
EMMC_SD_SRS03_RID | /* Response Interrupt Disable */
|
|
EMMC_SD_SRS03_RECE | /* Response Error Check Enable */
|
|
EMMC_SD_SRS03_RESP_48 |
|
|
EMMC_SD_SRS03_CRCCE |
|
|
EMMC_SD_SRS03_CICE;
|
|
|
|
/* Command argument = 0 for CMD19 */
|
|
EMMC_SD_SRS02 = 0;
|
|
EMMC_SD_SRS03 = cmd_reg;
|
|
|
|
/* Wait for buffer read ready or error */
|
|
do {
|
|
srs12 = EMMC_SD_SRS12;
|
|
} while ((srs12 & (EMMC_SD_SRS12_BRR | EMMC_SD_SRS12_EINT)) == 0);
|
|
|
|
/* Read data if buffer ready */
|
|
if (srs12 & EMMC_SD_SRS12_BRR) {
|
|
for (i = 0; i < (EMMC_SD_TUNING_BLOCK_SIZE / 4); i++) {
|
|
data[i] = EMMC_SD_SRS08;
|
|
}
|
|
}
|
|
|
|
/* Check for errors */
|
|
srs12 = EMMC_SD_SRS12;
|
|
EMMC_SD_SRS12 = (EMMC_SD_SRS12_NORM_STAT | EMMC_SD_SRS12_ERR_STAT);
|
|
|
|
if (srs12 & EMMC_SD_SRS12_ERR_STAT) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Execute SD tuning procedure using CMD19 and Execute Tuning bit.
|
|
* Based on HSS sd_tuning() implementation */
|
|
static int mmc_sd_tuning(void)
|
|
{
|
|
uint32_t reg;
|
|
uint32_t tuning_data[EMMC_SD_TUNING_BLOCK_SIZE / 4];
|
|
int count;
|
|
int status = 0;
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_sd_tuning: starting\n");
|
|
#endif
|
|
|
|
reg = EMMC_SD_SRS15;
|
|
|
|
/* Reset tuning: clear Sampling Clock Select */
|
|
reg &= ~EMMC_SD_SRS15_SCS;
|
|
/* Start tuning: set Execute Tuning */
|
|
reg |= EMMC_SD_SRS15_EXTNG;
|
|
EMMC_SD_SRS15 = reg;
|
|
|
|
/* Tuning loop - send CMD19 up to 40 times */
|
|
for (count = EMMC_SD_TUNING_MAX_LOOPS; count > 0; count--) {
|
|
status = mmc_send_tuning_block(tuning_data);
|
|
if (status != 0) {
|
|
/* Reset data/cmd lines on failure */
|
|
EMMC_SD_SRS11 |= EMMC_SD_SRS11_RESET_DAT_CMD;
|
|
while (EMMC_SD_SRS11 & EMMC_SD_SRS11_RESET_DAT_CMD);
|
|
break;
|
|
}
|
|
|
|
/* Check if Execute Tuning has cleared (hardware completed) */
|
|
reg = EMMC_SD_SRS15;
|
|
if ((reg & EMMC_SD_SRS15_EXTNG) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check result: Sampling Clock Select should be set on success */
|
|
reg = EMMC_SD_SRS15;
|
|
if ((reg & EMMC_SD_SRS15_SCS) == 0) {
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_sd_tuning: FAILED (SCS not set)\n");
|
|
#endif
|
|
/* Clear Execute Tuning if still set */
|
|
if (reg & EMMC_SD_SRS15_EXTNG) {
|
|
EMMC_SD_SRS15 = reg & ~EMMC_SD_SRS15_EXTNG;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_sd_tuning: SUCCESS after %d iterations\n",
|
|
EMMC_SD_TUNING_MAX_LOOPS - count + 1);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
/* PHY training - find optimal delay value by testing reads
|
|
* Based on HSS phy_training_mmc() implementation */
|
|
static int mmc_tune(uint8_t phy_addr, uint32_t clk_khz)
|
|
{
|
|
int status;
|
|
uint8_t delay, max_delay;
|
|
uint8_t pos = 0, length = 0, curr_length = 0;
|
|
uint32_t tmp_block[EMMC_SD_BLOCK_SIZE / sizeof(uint32_t)];
|
|
|
|
/* Calculate max delay based on clock rate (from HSS) */
|
|
if (clk_khz <= 12500) {
|
|
max_delay = 20;
|
|
} else {
|
|
max_delay = (uint8_t)((200000 / clk_khz) * 2);
|
|
}
|
|
if (max_delay > 40) {
|
|
max_delay = 40;
|
|
}
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_tune: phy_addr=0x%02x, clk=%d kHz, max_delay=%d\n",
|
|
phy_addr, clk_khz, max_delay);
|
|
#endif
|
|
|
|
/* Test each delay value to find longest valid range */
|
|
for (delay = 0; delay < max_delay; delay++) {
|
|
mmc_phy_write(phy_addr, delay);
|
|
|
|
/* Try a single block read to test this delay setting */
|
|
status = mmc_read(MMC_CMD17_READ_SINGLE, 0, tmp_block,
|
|
EMMC_SD_BLOCK_SIZE);
|
|
if (status == 0) {
|
|
curr_length++;
|
|
if (curr_length > length) {
|
|
pos = delay - length;
|
|
length++;
|
|
}
|
|
} else {
|
|
/* Reset data/cmd lines on failure */
|
|
EMMC_SD_SRS11 |= EMMC_SD_SRS11_RESET_DAT_CMD;
|
|
while (EMMC_SD_SRS11 & EMMC_SD_SRS11_RESET_DAT_CMD);
|
|
curr_length = 0;
|
|
}
|
|
}
|
|
|
|
/* Set optimal delay (middle of longest valid range) */
|
|
if (length > 0) {
|
|
uint8_t new_delay = pos + (length / 2);
|
|
mmc_phy_write(phy_addr, new_delay);
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_tune: PHY delay=%d (range: pos=%d, len=%d)\n",
|
|
new_delay, pos, length);
|
|
#endif
|
|
|
|
/* For SDR50/SDR104, also run SD tuning (CMD19) if required.
|
|
* Check SRS17 bit 13 (TSDR50) - Tuning for SDR50 required */
|
|
if (EMMC_SD_SRS17 & EMMC_SD_SRS17_TSDR50) {
|
|
status = mmc_sd_tuning();
|
|
if (status != 0) {
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_tune: SD tuning failed\n");
|
|
#endif
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_tune: FAILED - no valid PHY delay found\n");
|
|
#endif
|
|
return -1;
|
|
}
|
|
#endif /* ENABLE_MMC_SD_TUNING */
|
|
|
|
int mmc_init(void)
|
|
{
|
|
int status = 0;
|
|
uint32_t reg, cap;
|
|
uint32_t ctrl_volts, card_volts;
|
|
uint32_t irq_restore;
|
|
int xpc, si8r;
|
|
|
|
/* Reset the MMC controller */
|
|
SYSREG_SOFT_RESET_CR &= ~SYSREG_SOFT_RESET_CR_MMC;
|
|
/* Disable the EMMC/SD IRQ */
|
|
|
|
/* Reset the host controller */
|
|
EMMC_SD_HRS00 |= EMMC_SD_HRS00_SWR;
|
|
/* Bit will clear when reset is done */
|
|
while ((EMMC_SD_HRS00 & EMMC_SD_HRS00_SWR) != 0);
|
|
|
|
/* Set debounce period to ~15ms (at 200MHz) */
|
|
EMMC_SD_HRS01 = ((EMMC_SD_DEBOUNCE_TIME << EMMC_SD_HRS01_DP_SHIFT) &
|
|
EMMC_SD_HRS01_DP_MASK);
|
|
|
|
/* Select SDCard Mode */
|
|
reg = EMMC_SD_HRS06;
|
|
reg &= ~EMMC_SD_HRS06_EMM_MASK;
|
|
reg |= EMMC_SD_HRS06_MODE_SD;
|
|
EMMC_SD_HRS06 = reg;
|
|
|
|
/* Clear error/interrupt status */
|
|
EMMC_SD_SRS12 = (EMMC_SD_SRS12_NORM_STAT | EMMC_SD_SRS12_ERR_STAT);
|
|
|
|
/* Check and enable 64-bit DMA support */
|
|
reg = EMMC_SD_SRS15;
|
|
cap = EMMC_SD_SRS16;
|
|
if (cap & EMMC_SD_SRS16_A64S) {
|
|
reg |= EMMC_SD_SRS15_A64;
|
|
reg |= EMMC_SD_SRS15_HV4E;
|
|
EMMC_SD_SRS15 = reg;
|
|
}
|
|
/* Set all status enables - 0xbff40ff */
|
|
EMMC_SD_SRS13 = (
|
|
EMMC_SD_SRS13_ETUNE_SE | EMMC_SD_SRS13_EADMA_SE | EMMC_SD_SRS13_EAC_SE |
|
|
EMMC_SD_SRS13_ECL_SE | EMMC_SD_SRS13_EDEB_SE |
|
|
EMMC_SD_SRS13_EDCRC_SE | EMMC_SD_SRS13_EDT_SE |
|
|
EMMC_SD_SRS13_ECI_SE | EMMC_SD_SRS13_ECEB_SE | EMMC_SD_SRS13_ECCRC_SE |
|
|
EMMC_SD_SRS13_ECT_SE | EMMC_SD_SRS13_RTUNE_SE |
|
|
EMMC_SD_SRS13_INT_ONC | EMMC_SD_SRS13_INT_ONB | EMMC_SD_SRS13_INT_ONA |
|
|
EMMC_SD_SRS13_CR_SE | EMMC_SD_SRS13_CIN_SE |
|
|
EMMC_SD_SRS13_BRR_SE | EMMC_SD_SRS13_BWR_SE | EMMC_SD_SRS13_DMAINT_SE |
|
|
EMMC_SD_SRS13_BGE_SE | EMMC_SD_SRS13_TC_SE | EMMC_SD_SRS13_CC_SE |
|
|
EMMC_SD_SRS13_ERSP_SE | EMMC_SD_SRS13_CQINT_SE
|
|
);
|
|
/* Clear all signal enables (will be enabled per-transfer for SDMA) */
|
|
EMMC_SD_SRS14 = 0;
|
|
|
|
/* Initialize PLIC for MMC interrupts */
|
|
plic_init_mmc();
|
|
|
|
/* Set initial timeout to 500ms */
|
|
status = mmc_set_timeout(EMMC_SD_DATA_TIMEOUT_US);
|
|
if (status != 0) {
|
|
return status;
|
|
}
|
|
/* Turn off host controller power */
|
|
(void)mmc_set_power(0);
|
|
|
|
/* check if card inserted and stable */
|
|
reg = EMMC_SD_SRS09;
|
|
if ((reg & EMMC_SD_SRS09_CSS) == 0) {
|
|
/* card not inserted or not stable */
|
|
return -1;
|
|
}
|
|
/* NOTE: if using eMMC mode skip this check */
|
|
if ((reg & EMMC_SD_SRS09_CI) == 0) {
|
|
/* card not inserted */
|
|
return -1;
|
|
}
|
|
|
|
/* Start in 1-bit bus mode */
|
|
EMMC_SD_SRS10 &= ~(EMMC_SD_SRS10_EDTW | EMMC_SD_SRS10_DTW);
|
|
|
|
/* Setup 400khz starting clock */
|
|
mmc_set_clock(EMMC_SD_CLK_400KHZ);
|
|
|
|
/* Set power to 3.3v and send init commands */
|
|
ctrl_volts = EMMC_SD_SRS10_BVS_3_3V; /* default to 3.3v */
|
|
status = mmc_power_init_seq(ctrl_volts);
|
|
if (status == 0) {
|
|
uint32_t max_ma_3_3v, max_ma_1_8v;
|
|
/* determine host controller capabilities */
|
|
reg = EMMC_SD_SRS18;
|
|
max_ma_3_3v = ((reg & EMMC_SD_SRS18_MC33_MASK) >> EMMC_SD_SRS18_MC33_SHIFT) * 4;
|
|
max_ma_1_8v = ((reg & EMMC_SD_SRS18_MC18_MASK) >> EMMC_SD_SRS18_MC18_SHIFT) * 4;
|
|
/* does controller support eXtended Power Control (XPC)? */
|
|
xpc = (max_ma_1_8v >= 150) && (max_ma_3_3v >= 150) ? 1 : 0;
|
|
/* does controller support UHS-I (Ultra High Speed Interface) v1.8 signaling? */
|
|
si8r =((EMMC_SD_SRS16 & EMMC_SD_SRS16_VS18) && /* 1.8v supported */
|
|
(EMMC_SD_SRS17 & (EMMC_SD_SRS17_DDR50 | /* DDR50, SDR104 or SDR50 supported */
|
|
EMMC_SD_SRS17_SDR104 |
|
|
EMMC_SD_SRS17_SDR50))) ? 1: 0;
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_init: xpc:%d, si8r:%d, max_ma (3.3v:%d 1.8v:%d)\n",
|
|
xpc, si8r, max_ma_3_3v, max_ma_1_8v);
|
|
#endif
|
|
}
|
|
if (status == 0) {
|
|
reg = 0;
|
|
/* get operating conditions */
|
|
status = mmc_card_init(0, ®);
|
|
if (status == 0) {
|
|
/* pick host and card operating voltages */
|
|
if (reg & SDCARD_REG_OCR_3_3_3_4) { /* 3.3v - 3.4v */
|
|
card_volts = SDCARD_REG_OCR_3_3_3_4;
|
|
}
|
|
else if (reg & SDCARD_REG_OCR_3_2_3_3) { /* 3.2v - 3.3v */
|
|
card_volts = SDCARD_REG_OCR_3_2_3_3;
|
|
}
|
|
else if (reg & SDCARD_REG_OCR_3_1_3_2) { /* 3.1v - 3.2v */
|
|
card_volts = SDCARD_REG_OCR_3_1_3_2;
|
|
}
|
|
else if (reg & SDCARD_REG_OCR_3_0_3_1) { /* 3.0v - 3.1v */
|
|
card_volts = SDCARD_REG_OCR_3_0_3_1;
|
|
ctrl_volts = EMMC_SD_SRS10_BVS_3_0V;
|
|
}
|
|
else if (reg & SDCARD_REG_OCR_2_9_3_0) { /* 2.9v - 3.0v */
|
|
card_volts = SDCARD_REG_OCR_2_9_3_0;
|
|
ctrl_volts = EMMC_SD_SRS10_BVS_3_0V;
|
|
}
|
|
else { /* default to v3.3 */
|
|
card_volts = SDCARD_REG_OCR_3_3_3_4;
|
|
}
|
|
/* if needed change operating volage and re-init */
|
|
if (ctrl_volts != EMMC_SD_SRS10_BVS_3_3V) {
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_init: changing operating voltage to 3.0v\n");
|
|
#endif
|
|
status = mmc_power_init_seq(ctrl_volts);
|
|
}
|
|
}
|
|
}
|
|
if (status == 0) {
|
|
/* configure operating conditions */
|
|
uint32_t cmd_arg = SDCARD_ACMD41_HCS;
|
|
cmd_arg |= card_volts;
|
|
if (si8r) {
|
|
cmd_arg |= SDCARD_REG_OCR_S18RA;
|
|
}
|
|
if (xpc) {
|
|
cmd_arg |= SDCARD_REG_OCR_XPC;
|
|
}
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_init: sending OCR arg: 0x%08X\n", cmd_arg);
|
|
#endif
|
|
|
|
/* retry until OCR ready */
|
|
do {
|
|
status = mmc_card_init(cmd_arg, ®);
|
|
} while (status == 0 && (reg & SDCARD_REG_OCR_READY) == 0);
|
|
}
|
|
if (status == 0) {
|
|
/* Get card identification */
|
|
status = mmc_send_cmd(MMC_CMD2_ALL_SEND_CID, 0, EMMC_SD_RESP_R2);
|
|
}
|
|
if (status == 0) {
|
|
/* Set relative address */
|
|
status = mmc_send_cmd(MMC_CMD3_SET_REL_ADDR, 0, EMMC_SD_RESP_R6);
|
|
}
|
|
if (status == 0) {
|
|
g_rca = ((EMMC_SD_SRS04 >> SD_RCA_SHIFT) & 0xFFFF);
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_init: rca: %d\n", g_rca);
|
|
#endif
|
|
}
|
|
if (status == 0) {
|
|
/* read CSD register from device */
|
|
status = mmc_send_cmd(MMC_CMD9_SEND_CSD, g_rca << SD_RCA_SHIFT,
|
|
EMMC_SD_RESP_R2);
|
|
}
|
|
if (status == 0) {
|
|
/* Get sector size and count */
|
|
uint32_t csd_struct;
|
|
uint32_t bl_len, c_size, c_size_mult;
|
|
bl_len = get_srs_bits(22, 4);
|
|
g_sector_size = (1U << bl_len);
|
|
|
|
csd_struct = get_srs_bits(126, 2);
|
|
switch (csd_struct) {
|
|
case 0:
|
|
c_size = get_srs_bits(62, 12);
|
|
c_size_mult = get_srs_bits(47, 3);
|
|
g_sector_count = (c_size + 1) << (c_size_mult + 2);
|
|
break;
|
|
case 1:
|
|
c_size = get_srs_bits(48, 22);
|
|
g_sector_count = (c_size + 1) << 10;
|
|
break;
|
|
default:
|
|
/* invalid CSD structure */
|
|
status = -1;
|
|
break;
|
|
}
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_init: csd_version: %d, sector: size %d count %d\n",
|
|
csd_struct, g_sector_size, g_sector_count);
|
|
#endif
|
|
}
|
|
if (status == 0) {
|
|
/* select card */
|
|
status = mmc_send_cmd(MMC_CMD7_SELECT_CARD, g_rca << SD_RCA_SHIFT,
|
|
EMMC_SD_RESP_R1B);
|
|
if (status == DEVICE_BUSY) {
|
|
status = mmc_wait_busy(1);
|
|
}
|
|
}
|
|
if (status == 0) {
|
|
/* disable card insert interrupt while changing bus width to avoid false triggers */
|
|
irq_restore = EMMC_SD_SRS13;
|
|
EMMC_SD_SRS13 = (irq_restore & ~EMMC_SD_SRS13_CINT_SE);
|
|
|
|
status = mmc_set_bus_width(4);
|
|
}
|
|
if (status == 0) {
|
|
/* Get SCR registers - 8 bytes */
|
|
uint32_t scr_reg[SCR_REG_DATA_SIZE/sizeof(uint32_t)];
|
|
status = mmc_read(SD_ACMD51_SEND_SCR, 0, scr_reg,
|
|
sizeof(scr_reg));
|
|
}
|
|
if (status == 0) {
|
|
/* set UHS mode to SDR25 and driver strength to Type B */
|
|
uint32_t card_access_mode = SDCARD_SWITCH_ACCESS_MODE_SDR25;
|
|
status = mmc_set_function(card_access_mode, 1);
|
|
if (status == 0) {
|
|
/* set driver strength */
|
|
reg = EMMC_SD_SRS15;
|
|
reg &= ~EMMC_SD_SRS15_DSS_MASK;
|
|
reg |= EMMC_SD_SRS15_DSS_TYPE_B; /* default */
|
|
EMMC_SD_SRS15 = reg;
|
|
|
|
/* enable high speed */
|
|
EMMC_SD_SRS10 |= EMMC_SD_SRS10_HSE;
|
|
|
|
/* set UHS mode */
|
|
reg = EMMC_SD_SRS15;
|
|
reg &= ~EMMC_SD_SRS15_UMS_MASK;
|
|
reg |= EMMC_SD_SRS15_UMS_SDR25;
|
|
EMMC_SD_SRS15 = reg;
|
|
}
|
|
}
|
|
if (status == 0) {
|
|
mmc_set_clock(EMMC_SD_CLK_50MHZ);
|
|
|
|
#ifdef ENABLE_MMC_SD_TUNING
|
|
/* PHY training for SDR25 at 50MHz */
|
|
status = mmc_tune(EMMC_SD_PHY_ADDR_UHSI_SDR25, EMMC_SD_CLK_50MHZ);
|
|
if (status != 0) {
|
|
#ifdef DEBUG_MMC
|
|
wolfBoot_printf("mmc_init: tuning failed, continuing\n");
|
|
#endif
|
|
status = 0; /* Don't fail init on tuning failure */
|
|
}
|
|
#endif
|
|
|
|
EMMC_SD_SRS13 = irq_restore; /* re-enable interrupt */
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* returns number of bytes read on success or negative on error */
|
|
/* start may not be block aligned and count may not be block multiple */
|
|
int disk_read(int drv, uint64_t start, uint32_t count, uint8_t *buf)
|
|
{
|
|
int status = 0;
|
|
uint32_t read_sz, block_addr;
|
|
uint32_t tmp_block[EMMC_SD_BLOCK_SIZE/sizeof(uint32_t)];
|
|
uint32_t start_offset = (start % EMMC_SD_BLOCK_SIZE);
|
|
(void)drv; /* only one drive supported */
|
|
|
|
#if 1 //def DEBUG_MMC
|
|
wolfBoot_printf("disk_read: drv:%d, start:%llu, count:%d, dst:%p\n",
|
|
drv, start, count, buf);
|
|
#endif
|
|
|
|
while (count > 0) {
|
|
block_addr = (start / EMMC_SD_BLOCK_SIZE);
|
|
read_sz = count;
|
|
if (read_sz > EMMC_SD_BLOCK_SIZE) {
|
|
read_sz = EMMC_SD_BLOCK_SIZE;
|
|
}
|
|
if (read_sz < EMMC_SD_BLOCK_SIZE || /* last partial */
|
|
start_offset != 0 || /* start not block aligned */
|
|
((uintptr_t)buf % 4) != 0) /* buf not 4-byte aligned */
|
|
{
|
|
/* block read to temporary buffer */
|
|
status = mmc_read(MMC_CMD17_READ_SINGLE, block_addr,
|
|
tmp_block, EMMC_SD_BLOCK_SIZE);
|
|
if (status == 0) {
|
|
uint8_t* tmp_buf = (uint8_t*)tmp_block;
|
|
memcpy(buf, tmp_buf + start_offset, read_sz);
|
|
start_offset = 0;
|
|
}
|
|
}
|
|
else {
|
|
/* direct full block(s) read */
|
|
uint32_t blocks = (count / EMMC_SD_BLOCK_SIZE);
|
|
read_sz = (blocks * EMMC_SD_BLOCK_SIZE);
|
|
status = mmc_read(blocks > 1 ?
|
|
MMC_CMD18_READ_MULTIPLE :
|
|
MMC_CMD17_READ_SINGLE,
|
|
block_addr, (uint32_t*)buf, read_sz);
|
|
}
|
|
if (status != 0) {
|
|
break;
|
|
}
|
|
|
|
start += read_sz;
|
|
buf += read_sz;
|
|
count -= read_sz;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int disk_write(int drv, uint64_t start, uint32_t count, const uint8_t *buf)
|
|
{
|
|
int status = 0;
|
|
uint32_t write_sz, block_addr;
|
|
uint32_t tmp_block[EMMC_SD_BLOCK_SIZE/sizeof(uint32_t)];
|
|
uint32_t start_offset = (start % EMMC_SD_BLOCK_SIZE);
|
|
(void)drv; /* only one drive supported */
|
|
|
|
#if 1 //def DEBUG_MMC
|
|
wolfBoot_printf("disk_write: drv:%d, start:%llu, count:%d, src:%p\n",
|
|
drv, start, count, buf);
|
|
#endif
|
|
|
|
while (count > 0) {
|
|
block_addr = (start / EMMC_SD_BLOCK_SIZE);
|
|
write_sz = count;
|
|
if (write_sz > EMMC_SD_BLOCK_SIZE) {
|
|
write_sz = EMMC_SD_BLOCK_SIZE;
|
|
}
|
|
if (write_sz < EMMC_SD_BLOCK_SIZE || /* partial block */
|
|
start_offset != 0 || /* start not block aligned */
|
|
((uintptr_t)buf % 4) != 0) /* buf not 4-byte aligned */
|
|
{
|
|
/* read-modify-write for partial block */
|
|
status = mmc_read(MMC_CMD17_READ_SINGLE, block_addr,
|
|
tmp_block, EMMC_SD_BLOCK_SIZE);
|
|
if (status == 0) {
|
|
uint8_t* tmp_buf = (uint8_t*)tmp_block;
|
|
memcpy(tmp_buf + start_offset, buf, write_sz);
|
|
status = mmc_write(MMC_CMD24_WRITE_SINGLE, block_addr,
|
|
tmp_block, EMMC_SD_BLOCK_SIZE);
|
|
start_offset = 0;
|
|
}
|
|
}
|
|
else {
|
|
/* direct full block(s) write */
|
|
uint32_t blocks = (count / EMMC_SD_BLOCK_SIZE);
|
|
write_sz = (blocks * EMMC_SD_BLOCK_SIZE);
|
|
status = mmc_write(blocks > 1 ?
|
|
MMC_CMD25_WRITE_MULTIPLE :
|
|
MMC_CMD24_WRITE_SINGLE,
|
|
block_addr, (const uint32_t*)buf, write_sz);
|
|
}
|
|
if (status != 0) {
|
|
break;
|
|
}
|
|
|
|
start += write_sz;
|
|
buf += write_sz;
|
|
count -= write_sz;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
int disk_init(int drv)
|
|
{
|
|
int r = mmc_init();
|
|
if (r != 0) {
|
|
wolfBoot_printf("Failed to initialize MMC\n");
|
|
}
|
|
(void)drv;
|
|
#ifdef DISK_TEST
|
|
disk_test(drv);
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
void disk_close(int drv)
|
|
{
|
|
(void)drv;
|
|
}
|
|
|
|
#ifdef DISK_TEST
|
|
/* Test block address in update partition */
|
|
#ifndef DISK_TEST_BLOCK_ADDR
|
|
#define DISK_TEST_BLOCK_ADDR 149504
|
|
#endif
|
|
|
|
/* disk_test: Test read/write functionality at update partition
|
|
* Tests sizes: 128, 512, 1024, 512KB (524288), 1MB (1048576) bytes
|
|
* Uses DDR at WOLFBOOT_LOAD_ADDRESS for test buffer
|
|
* Returns 0 on success, negative on failure */
|
|
static int disk_test(int drv)
|
|
{
|
|
int status = 0;
|
|
int test_num = 0;
|
|
uint32_t i;
|
|
static const uint32_t test_sizes[] = {
|
|
128, /* partial block */
|
|
512, /* single block */
|
|
1024, /* two blocks */
|
|
512 * 1024, /* 512KB - DMA threshold */
|
|
1024 * 1024 /* 1MB */
|
|
};
|
|
/* Use DDR memory at WOLFBOOT_LOAD_ADDRESS for test buffer */
|
|
uint32_t* tmp_buf32 = (uint32_t*)WOLFBOOT_LOAD_ADDRESS;
|
|
uint8_t* tmp_buf = (uint8_t*)WOLFBOOT_LOAD_ADDRESS;
|
|
|
|
wolfBoot_printf("disk_test: Starting tests at block %d (buf @ %p)\n",
|
|
DISK_TEST_BLOCK_ADDR, tmp_buf);
|
|
|
|
for (test_num = 0; test_num < (int)(sizeof(test_sizes)/sizeof(test_sizes[0])); test_num++) {
|
|
uint32_t test_sz = test_sizes[test_num];
|
|
uint64_t test_addr = (uint64_t)DISK_TEST_BLOCK_ADDR * EMMC_SD_BLOCK_SIZE;
|
|
uint32_t blocks_needed = (test_sz + EMMC_SD_BLOCK_SIZE - 1) / EMMC_SD_BLOCK_SIZE;
|
|
|
|
wolfBoot_printf(" Test %d: size=%u bytes (%u blocks)... ",
|
|
test_num + 1, test_sz, blocks_needed);
|
|
|
|
/* Fill with test pattern */
|
|
for (i = 0; i < test_sz / sizeof(uint32_t); i++) {
|
|
tmp_buf32[i] = (test_num << 24) | i;
|
|
}
|
|
/* Handle remaining bytes for non-word-aligned sizes */
|
|
for (i = (test_sz / sizeof(uint32_t)) * sizeof(uint32_t); i < test_sz; i++) {
|
|
tmp_buf[i] = (uint8_t)((test_num << 4) | (i & 0x0F));
|
|
}
|
|
|
|
/* Write */
|
|
status = disk_write(drv, test_addr, test_sz, tmp_buf);
|
|
if (status != 0) {
|
|
wolfBoot_printf("FAIL (write error %d)\n", status);
|
|
continue;
|
|
}
|
|
|
|
/* Clear buffer */
|
|
memset(tmp_buf, 0, test_sz);
|
|
|
|
/* Read back */
|
|
status = disk_read(drv, test_addr, test_sz, tmp_buf);
|
|
if (status != 0) {
|
|
wolfBoot_printf("FAIL (read error %d)\n", status);
|
|
continue;
|
|
}
|
|
|
|
/* Verify pattern */
|
|
for (i = 0; i < test_sz / sizeof(uint32_t); i++) {
|
|
uint32_t expected = (test_num << 24) | i;
|
|
if (tmp_buf32[i] != expected) {
|
|
wolfBoot_printf("FAIL (verify @ word %u: got 0x%08X, expected 0x%08X)\n",
|
|
i, tmp_buf32[i], expected);
|
|
status = -1;
|
|
break;
|
|
}
|
|
}
|
|
/* Verify remaining bytes for non-word-aligned sizes */
|
|
if (status == 0) {
|
|
for (i = (test_sz / sizeof(uint32_t)) * sizeof(uint32_t); i < test_sz; i++) {
|
|
uint8_t expected = (uint8_t)((test_num << 4) | (i & 0x0F));
|
|
if (tmp_buf[i] != expected) {
|
|
wolfBoot_printf("FAIL (verify @ byte %u: got 0x%02X, expected 0x%02X)\n",
|
|
i, tmp_buf[i], expected);
|
|
status = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status == 0) {
|
|
wolfBoot_printf("PASS\n");
|
|
}
|
|
}
|
|
|
|
wolfBoot_printf("disk_test: Complete\n");
|
|
return status;
|
|
}
|
|
#endif /* DISK_TEST */
|
|
|
|
|
|
#ifdef DEBUG_UART
|
|
|
|
#ifndef DEBUG_UART_BASE
|
|
#define DEBUG_UART_BASE MSS_UART1_LO_BASE
|
|
#endif
|
|
|
|
/* Configure baud divisors with fractional baud rate support.
|
|
*
|
|
* UART baud rate divisor formula: divisor = PCLK / (baudrate * 16)
|
|
*
|
|
* To support fractional divisors (6-bit, 0-63), we scale up the calculation:
|
|
* divisor_x128 = (PCLK * 8) / baudrate (128x scaled for rounding precision)
|
|
* divisor_x64 = divisor_x128 / 2 (64x scaled for 6-bit fractional)
|
|
* integer_div = divisor_x64 / 64 (integer portion of divisor)
|
|
* frac_div = divisor_x64 % 64 (fractional portion, 0-63)
|
|
*
|
|
* The fractional part is then adjusted using the x128 value for rounding.
|
|
*/
|
|
static void uart_config_clk(uint32_t baudrate)
|
|
{
|
|
const uint64_t pclk = MSS_APB_AHB_CLK;
|
|
|
|
/* Scale up for precision: (PCLK * 128) / (baudrate * 16) */
|
|
uint32_t div_x128 = (uint32_t)((8UL * pclk) / baudrate);
|
|
uint32_t div_x64 = div_x128 / 2u;
|
|
|
|
/* Extract integer and fractional parts */
|
|
uint32_t div_int = div_x64 / 64u;
|
|
uint32_t div_frac = div_x64 - (div_int * 64u);
|
|
|
|
/* Apply rounding correction from x128 calculation */
|
|
div_frac += (div_x128 - (div_int * 128u)) - (div_frac * 2u);
|
|
|
|
if (div_int > (uint32_t)UINT16_MAX)
|
|
return;
|
|
|
|
/* Write 16-bit divisor: set DLAB, write high/low bytes, clear DLAB */
|
|
MMUART_LCR(DEBUG_UART_BASE) |= DLAB_MASK;
|
|
MMUART_DMR(DEBUG_UART_BASE) = (uint8_t)(div_int >> 8);
|
|
MMUART_DLR(DEBUG_UART_BASE) = (uint8_t)div_int;
|
|
MMUART_LCR(DEBUG_UART_BASE) &= ~DLAB_MASK;
|
|
|
|
/* Enable fractional divisor if integer divisor > 1 */
|
|
if (div_int > 1u) {
|
|
MMUART_MM0(DEBUG_UART_BASE) |= EFBR_MASK;
|
|
MMUART_DFR(DEBUG_UART_BASE) = (uint8_t)div_frac;
|
|
}
|
|
else {
|
|
MMUART_MM0(DEBUG_UART_BASE) &= ~EFBR_MASK;
|
|
}
|
|
}
|
|
|
|
void uart_init(void)
|
|
{
|
|
/* Disable special modes: LIN, IrDA, SmartCard */
|
|
MMUART_MM0(DEBUG_UART_BASE) &= ~ELIN_MASK;
|
|
MMUART_MM1(DEBUG_UART_BASE) &= ~EIRD_MASK;
|
|
MMUART_MM2(DEBUG_UART_BASE) &= ~EERR_MASK;
|
|
|
|
/* Disable interrupts */
|
|
MMUART_IER(DEBUG_UART_BASE) = 0u;
|
|
|
|
/* Reset and configure FIFOs, enable RXRDYN/TXRDYN pins */
|
|
MMUART_FCR(DEBUG_UART_BASE) = 0u;
|
|
MMUART_FCR(DEBUG_UART_BASE) |= CLEAR_RX_FIFO_MASK | CLEAR_TX_FIFO_MASK;
|
|
MMUART_FCR(DEBUG_UART_BASE) |= RXRDY_TXRDYN_EN_MASK;
|
|
|
|
/* Disable loopback (local and remote) */
|
|
MMUART_MCR(DEBUG_UART_BASE) &= ~(LOOP_MASK | RLOOP_MASK);
|
|
|
|
/* Set LSB-first for TX/RX */
|
|
MMUART_MM1(DEBUG_UART_BASE) &= ~(E_MSB_TX_MASK | E_MSB_RX_MASK);
|
|
|
|
/* Disable AFM, single wire mode */
|
|
MMUART_MM2(DEBUG_UART_BASE) &= ~(EAFM_MASK | ESWM_MASK);
|
|
|
|
/* Disable TX time guard, RX timeout, fractional baud */
|
|
MMUART_MM0(DEBUG_UART_BASE) &= ~(ETTG_MASK | ERTO_MASK | EFBR_MASK);
|
|
|
|
/* Clear timing registers */
|
|
MMUART_GFR(DEBUG_UART_BASE) = 0u;
|
|
MMUART_TTG(DEBUG_UART_BASE) = 0u;
|
|
MMUART_RTO(DEBUG_UART_BASE) = 0u;
|
|
|
|
/* Configure baud rate (115200) */
|
|
uart_config_clk(115200);
|
|
|
|
/* Set line config: 8N1 */
|
|
MMUART_LCR(DEBUG_UART_BASE) = MSS_UART_DATA_8_BITS |
|
|
MSS_UART_NO_PARITY |
|
|
MSS_UART_ONE_STOP_BIT;
|
|
}
|
|
|
|
void uart_write(const char* buf, unsigned int sz)
|
|
{
|
|
uint32_t pos = 0;
|
|
while (sz-- > 0) {
|
|
char c = buf[pos++];
|
|
if (c == '\n') { /* handle CRLF */
|
|
while ((MMUART_LSR(DEBUG_UART_BASE) & MSS_UART_THRE) == 0);
|
|
MMUART_THR(DEBUG_UART_BASE) = '\r';
|
|
}
|
|
while ((MMUART_LSR(DEBUG_UART_BASE) & MSS_UART_THRE) == 0);
|
|
MMUART_THR(DEBUG_UART_BASE) = c;
|
|
}
|
|
}
|
|
#endif /* DEBUG_UART */
|