mirror of https://github.com/wolfSSL/wolfBoot.git
542 lines
16 KiB
C
542 lines
16 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 "riscv.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 "gpt.h"
|
|
#include "fdt.h"
|
|
|
|
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
|
|
#include "sdhci.h"
|
|
#endif
|
|
|
|
void hal_init(void)
|
|
{
|
|
#if defined(DEBUG_UART) && defined(__WOLFBOOT)
|
|
#ifdef WOLFBOOT_REPRODUCIBLE_BUILD
|
|
wolfBoot_printf("wolfBoot Version: %s\n", LIBWOLFBOOT_VERSION_STRING);
|
|
#else
|
|
wolfBoot_printf("wolfBoot Version: %s (%s %s)\n",
|
|
LIBWOLFBOOT_VERSION_STRING,__DATE__, __TIME__);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/* ============================================================================
|
|
* System Controller Mailbox Functions
|
|
*
|
|
* The MPFS system controller provides various system services via a mailbox
|
|
* interface. Commands are sent by writing the opcode to the control register
|
|
* and responses are read from the mailbox RAM.
|
|
* ============================================================================ */
|
|
|
|
/**
|
|
* mpfs_scb_mailbox_busy - Check if the system controller mailbox is busy
|
|
*
|
|
* Returns: non-zero if busy, 0 if ready
|
|
*/
|
|
static int mpfs_scb_mailbox_busy(void)
|
|
{
|
|
return (SCBCTRL_REG(SERVICES_SR_OFFSET) & SERVICES_SR_BUSY_MASK);
|
|
}
|
|
|
|
/**
|
|
* mpfs_read_serial_number - Read the device serial number via system services
|
|
* @serial: Buffer to store the 16-byte device serial number
|
|
*
|
|
* This function sends a serial number request (opcode 0x00) to the system
|
|
* controller and reads the 16-byte response from the mailbox RAM.
|
|
*
|
|
* Returns: 0 on success, negative error code on failure
|
|
*/
|
|
static int mpfs_read_serial_number(uint8_t *serial)
|
|
{
|
|
uint32_t cmd, status;
|
|
int i, timeout;
|
|
|
|
if (serial == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* Check if mailbox is busy */
|
|
if (mpfs_scb_mailbox_busy()) {
|
|
wolfBoot_printf("SCB mailbox busy\n");
|
|
return -2;
|
|
}
|
|
|
|
/* Send serial number request command (opcode 0x00)
|
|
* Command format: [31:16] = opcode, [0] = request bit */
|
|
cmd = (SYS_SERV_CMD_SERIAL_NUMBER << SERVICES_CR_COMMAND_SHIFT) |
|
|
SERVICES_CR_REQ_MASK;
|
|
SCBCTRL_REG(SERVICES_CR_OFFSET) = cmd;
|
|
|
|
/* Wait for request bit to clear (command accepted) */
|
|
timeout = 10000;
|
|
while ((SCBCTRL_REG(SERVICES_CR_OFFSET) & SERVICES_CR_REQ_MASK) && timeout > 0) {
|
|
timeout--;
|
|
}
|
|
if (timeout == 0) {
|
|
wolfBoot_printf("SCB mailbox request timeout\n");
|
|
return -3;
|
|
}
|
|
|
|
/* Wait for busy bit to clear (command completed) */
|
|
timeout = 10000;
|
|
while (mpfs_scb_mailbox_busy() && timeout > 0) {
|
|
timeout--;
|
|
}
|
|
if (timeout == 0) {
|
|
wolfBoot_printf("SCB mailbox busy timeout\n");
|
|
return -4;
|
|
}
|
|
|
|
/* Check status (upper 16 bits of status register) */
|
|
status = (SCBCTRL_REG(SERVICES_SR_OFFSET) >> SERVICES_SR_STATUS_SHIFT) & 0xFFFF;
|
|
if (status != 0) {
|
|
wolfBoot_printf("SCB mailbox error: 0x%x\n", status);
|
|
return -5;
|
|
}
|
|
|
|
/* Read serial number from mailbox RAM (16 bytes) */
|
|
for (i = 0; i < DEVICE_SERIAL_NUMBER_SIZE; i++) {
|
|
serial[i] = SCBMBOX_BYTE(i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* 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
|
|
|
|
/* Microchip OUI (Organizationally Unique Identifier) for MAC address */
|
|
#define MICROCHIP_OUI_0 0x00
|
|
#define MICROCHIP_OUI_1 0x04
|
|
#define MICROCHIP_OUI_2 0xA3
|
|
|
|
int hal_dts_fixup(void* dts_addr)
|
|
{
|
|
int off, ret;
|
|
struct fdt_header *fdt = (struct fdt_header *)dts_addr;
|
|
uint8_t device_serial_number[DEVICE_SERIAL_NUMBER_SIZE];
|
|
uint8_t mac_addr[6];
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* Read device serial number from system controller */
|
|
ret = mpfs_read_serial_number(device_serial_number);
|
|
if (ret != 0) {
|
|
wolfBoot_printf("FDT: Failed to read serial number (%d)\n", ret);
|
|
/* Continue without setting MAC addresses */
|
|
return 0;
|
|
}
|
|
|
|
wolfBoot_printf("FDT: Device serial: %02x%02x%02x%02x-%02x%02x%02x%02x-"
|
|
"%02x%02x%02x%02x-%02x%02x%02x%02x\n",
|
|
device_serial_number[15], device_serial_number[14],
|
|
device_serial_number[13], device_serial_number[12],
|
|
device_serial_number[11], device_serial_number[10],
|
|
device_serial_number[9], device_serial_number[8],
|
|
device_serial_number[7], device_serial_number[6],
|
|
device_serial_number[5], device_serial_number[4],
|
|
device_serial_number[3], device_serial_number[2],
|
|
device_serial_number[1], device_serial_number[0]);
|
|
|
|
/* Build MAC address: Microchip OUI + lower 3 bytes of serial number
|
|
* Format: {0x00, 0x04, 0xA3, serial[2], serial[1], serial[0]} */
|
|
mac_addr[0] = MICROCHIP_OUI_0;
|
|
mac_addr[1] = MICROCHIP_OUI_1;
|
|
mac_addr[2] = MICROCHIP_OUI_2;
|
|
mac_addr[3] = device_serial_number[2];
|
|
mac_addr[4] = device_serial_number[1];
|
|
mac_addr[5] = device_serial_number[0];
|
|
|
|
wolfBoot_printf("FDT: MAC0 = %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
mac_addr[0], mac_addr[1], mac_addr[2],
|
|
mac_addr[3], mac_addr[4], mac_addr[5]);
|
|
|
|
/* Set local-mac-address for ethernet@20110000 (mac0) */
|
|
off = fdt_find_node_offset(fdt, -1, "ethernet@20110000");
|
|
if (off >= 0) {
|
|
ret = fdt_setprop(fdt, off, "local-mac-address", mac_addr, 6);
|
|
if (ret != 0) {
|
|
wolfBoot_printf("FDT: Failed to set mac0 address (%d)\n", ret);
|
|
}
|
|
}
|
|
else {
|
|
wolfBoot_printf("FDT: ethernet@20110000 not found\n");
|
|
}
|
|
|
|
/* Set local-mac-address for ethernet@20112000 (mac1)
|
|
* Use MAC address + 1 for the second interface */
|
|
mac_addr[5] = device_serial_number[0] + 1;
|
|
|
|
wolfBoot_printf("FDT: MAC1 = %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
mac_addr[0], mac_addr[1], mac_addr[2],
|
|
mac_addr[3], mac_addr[4], mac_addr[5]);
|
|
|
|
off = fdt_find_node_offset(fdt, -1, "ethernet@20112000");
|
|
if (off >= 0) {
|
|
ret = fdt_setprop(fdt, off, "local-mac-address", mac_addr, 6);
|
|
if (ret != 0) {
|
|
wolfBoot_printf("FDT: Failed to set mac1 address (%d)\n", ret);
|
|
}
|
|
}
|
|
else {
|
|
wolfBoot_printf("FDT: ethernet@20112000 not found\n");
|
|
}
|
|
|
|
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
|
|
|
|
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
|
|
/* ============================================================================
|
|
* SDHCI Platform HAL Implementation
|
|
* ============================================================================ */
|
|
|
|
/* Register access functions for generic SDHCI driver */
|
|
uint32_t sdhci_reg_read(uint32_t offset)
|
|
{
|
|
return *((volatile uint32_t*)(EMMC_SD_BASE + offset));
|
|
}
|
|
|
|
void sdhci_reg_write(uint32_t offset, uint32_t val)
|
|
{
|
|
*((volatile uint32_t*)(EMMC_SD_BASE + offset)) = val;
|
|
}
|
|
#endif /* DISK_SDCARD || DISK_EMMC */
|
|
|
|
/* ============================================================================
|
|
* PLIC - Platform-Level Interrupt Controller (MPFS250-specific)
|
|
*
|
|
* Generic PLIC functions are in src/boot_riscv.c
|
|
* Platform must provide:
|
|
* - plic_get_context(): Map current hart to PLIC context
|
|
* - plic_dispatch_irq(): Dispatch IRQ to appropriate handler
|
|
* ============================================================================ */
|
|
|
|
/* Get the PLIC context for the current hart in S-mode */
|
|
extern unsigned long get_boot_hartid(void);
|
|
uint32_t plic_get_context(void)
|
|
{
|
|
uint32_t hart_id = get_boot_hartid();
|
|
/* Get S-mode context for a given hart (1-4 for U54 cores) */
|
|
return hart_id * 2;
|
|
}
|
|
|
|
/* Forward declaration of SDHCI IRQ handler */
|
|
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
|
|
extern void sdhci_irq_handler(void);
|
|
#endif
|
|
|
|
/* Dispatch IRQ to appropriate platform handler */
|
|
void plic_dispatch_irq(uint32_t irq)
|
|
{
|
|
switch (irq) {
|
|
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
|
|
case PLIC_INT_MMC_MAIN:
|
|
sdhci_irq_handler();
|
|
break;
|
|
#endif
|
|
default:
|
|
/* Unknown interrupt - ignore */
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if defined(DISK_SDCARD) || defined(DISK_EMMC)
|
|
/* ============================================================================
|
|
* SDHCI Platform HAL Functions
|
|
* ============================================================================ */
|
|
|
|
/* Platform initialization - called from sdhci_init() */
|
|
void sdhci_platform_init(void)
|
|
{
|
|
/* Release MMC controller from reset */
|
|
SYSREG_SOFT_RESET_CR &= ~SYSREG_SOFT_RESET_CR_MMC;
|
|
}
|
|
|
|
/* Platform interrupt setup - called from sdhci_init() */
|
|
void sdhci_platform_irq_init(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_SDHCI
|
|
wolfBoot_printf("sdhci_platform_irq_init: hart %d, context %d, irq %d enabled\n",
|
|
get_boot_hartid(), plic_get_context(), PLIC_INT_MMC_MAIN);
|
|
#endif
|
|
}
|
|
|
|
/* Platform bus mode selection - called from sdhci_init() */
|
|
void sdhci_platform_set_bus_mode(int is_emmc)
|
|
{
|
|
(void)is_emmc;
|
|
/* Nothing additional needed for MPFS - mode is set in generic driver */
|
|
}
|
|
#endif /* DISK_SDCARD || DISK_EMMC */
|
|
|
|
/* ============================================================================
|
|
* DEBUG UART Functions
|
|
* ============================================================================ */
|
|
|
|
#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 */
|