/* hifive1.c * * Copyright (C) 2021 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 */ /* SiFive HiFive1 HAL for wolfBoot */ /* Supports: * QSPI Flash Erase/Write * PLL Clock reconfigure * UART TX/RX * RTC Timer */ #include #include #include #include "image.h" #ifndef ARCH_RISCV # error "wolfBoot hifive1 HAL: wrong architecture selected. Please compile with ARCH=RISCV." #endif /* CLINT Registers (Core Local Interruptor) for time */ #define CLINT_BASE 0x02000000UL #define CLINT_REG_MTIME (*((volatile uint32_t *)(CLINT_BASE + 0xBFF8))) #define RTC_FREQ 32768UL /* QSPI0 Registers */ #define QSPI0_CTRL 0x10014000UL #define FESPI_REG_SCKDIV (*((volatile uint32_t *)(QSPI0_CTRL + 0x00))) #define FESPI_REG_CSMODE (*((volatile uint32_t *)(QSPI0_CTRL + 0x18))) #define FESPI_REG_FMT (*((volatile uint32_t *)(QSPI0_CTRL + 0x40))) #define FESPI_REG_TXDATA (*((volatile uint32_t *)(QSPI0_CTRL + 0x48))) #define FESPI_REG_RXDATA (*((volatile uint32_t *)(QSPI0_CTRL + 0x4c))) #define FESPI_REG_TXMARK (*((volatile uint32_t *)(QSPI0_CTRL + 0x50))) #define FESPI_REG_RXMARK (*((volatile uint32_t *)(QSPI0_CTRL + 0x54))) #define FESPI_REG_FCTRL (*((volatile uint32_t *)(QSPI0_CTRL + 0x60))) #define FESPI_REG_FFMT (*((volatile uint32_t *)(QSPI0_CTRL + 0x64))) #define FESPI_REG_IP (*((volatile uint32_t *)(QSPI0_CTRL + 0x74))) /* QSPI Fields */ #define FESPI_IP_TXWM 0x1 #define FESPI_RXDATA_FIFO_EMPTY (1UL << 31) #define FESPI_TXDATA_FIFO_FULL (1UL << 31) #define FESPI_FMT_DIR_TX (1UL << 3) #define FESPI_CSMODE_AUTO 0x0UL #define FESPI_CSMODE_HOLD 0x2UL #define FESPI_CSMODE_MASK 0x3UL #define FESPI_FCTRL_MODE_SEL 0x1UL #define FESPI_FFMT_CMD_EN 0x1UL #define FESPI_FFMT_ADDR_LEN(x) (((x) & 0x7) << 1UL) #define FESPI_FFMT_PAD_CNT(x) (((x) & 0xf) << 4UL) #define FESPI_FFMT_CMD_PROTO(x) (((x) & 0x3) << 8UL) #define FESPI_FFMT_ADDR_PROTO(x) (((x) & 0x3) << 10UL) #define FESPI_FFMT_DATA_PROTO(x) (((x) & 0x3) << 12UL) #define FESPI_FFMT_CMD_CODE(x) (((x) & 0xff) << 16UL) #define FESPI_FFMT_PAD_CODE(x) (((x) & 0xff) << 24UL) #define FESPI_SCKDIV_MASK 0xFFFUL #define FESPI_TXMARK_MASK 0x3UL /* FESPI_REG_FMT Fields */ /* SPI I/O direction */ #define FESPI_DIR_RX 0 #define FESPI_DIR_TX 1 /* Frame format */ #define FESPI_PROTO_S 0 /* Single */ #define FESPI_PROTO_D 1 /* Dual */ #define FESPI_PROTO_Q 2 /* Quad */ //#define SPI_QUAD_MODE /* SPI Flash Commands */ #define FESPI_READ_ID 0xAB /* Read Flash Identification */ #define FESPI_READ_MID 0xAF /* Read Flash Identification, multi-io */ #define FESPI_READ_STATUS 0x05 /* Read Status Register */ #define FESPI_WRITE_ENABLE 0x06 /* Write Enable */ #define FESPI_PAGE_PROGRAM 0x02 /* Page Program */ #define FESPI_ROW_PROGRAM 0x62 /* Row Program */ #define FESPI_FAST_READ 0x0B /* Fast Read */ #define FESPI_READ 0x03 /* Normal Read */ #ifdef SPI_QUAD_MODE #define FESPI_ERASE_SECTOR 0x20 /* Sector Erase */ #else #define FESPI_ERASE_SECTOR 0xD7 /* Sector Erase */ #endif /* SPI flash status fields (from FESPI_READ_STATUS command) */ #define FESPI_RX_BSY (1 << 0UL) #define FESPI_RX_WE (1 << 1UL) /* QSPI Flash Sector Size */ #define FESPI_FLASH_SECTOR_SIZE (4 * 1024UL) /* PRCI Registers */ #define PRCI_BASE 0x10008000UL #define PRCI_REG_HFROSCCFG (*((volatile uint32_t *)(PRCI_BASE + 0x00))) #define PRCI_REG_HFXOSCCFG (*((volatile uint32_t *)(PRCI_BASE + 0x04))) #define PRCI_REG_PLLCFG (*((volatile uint32_t *)(PRCI_BASE + 0x08))) #define PRCI_REG_PLLOUTDIV (*((volatile uint32_t *)(PRCI_BASE + 0x0c))) #define PLLCFG_R 0x00000007UL #define PLLCFG_F 0x000003F0UL #define PLLCFG_Q 0x00000C00UL #define PLLCFG_SEL 0x00010000UL #define PLLCFG_REFSEL 0x00020000UL #define PLLCFG_BYPASS 0x00040000UL #define PLLCFG_LOCK 0x80000000UL #define PLLCFG_R_SHIFT(r) ((r << 0) & PLLCFG_R) #define PLLCFG_F_SHIFT(f) ((f << 4) & PLLCFG_F) #define PLLCFG_Q_SHIFT(q) ((q << 10) & PLLCFG_Q) #define PLLOUTDIV_DIV 0x0000003FUL #define PLLOUTDIV_DIV_BY_1 0x00000100UL #define PLLOUTDIV_SHIFT(d) ((d << 0) & PLLOUTDIV_DIV) #define HFROSCCFG_DIV 0x0000001FUL #define HFROSCCFG_TRIM 0x001F0000UL #define HFROSCCFG_EN (1UL << 30UL) #define HFROSCCFG_READY (1UL << 31UL) #define HFROSCCFG_DIV_SHIFT(d) ((d << 0) & HFROSCCFG_TRIM) #define HFROSCCFG_TRIM_SHIFT(t) ((t << 16) & HFROSCCFG_TRIM) #define HFXOSCCFG_EN (1 << 30) /* UART */ #define UART0_BASE 0x10013000UL #define UART_REG_TXDATA (*(volatile uint32_t *)(UART0_BASE + 0x00)) #define UART_REG_RXDATA (*(volatile uint32_t *)(UART0_BASE + 0x04)) #define UART_REG_TXCTRL (*(volatile uint32_t *)(UART0_BASE + 0x08)) #define UART_REG_RXCTRL (*(volatile uint32_t *)(UART0_BASE + 0x0c)) #define UART_REG_IE (*(volatile uint32_t *)(UART0_BASE + 0x10)) #define UART_REG_IP (*(volatile uint32_t *)(UART0_BASE + 0x14)) #define UART_REG_DIV (*(volatile uint32_t *)(UART0_BASE + 0x18)) /* TXDATA Fields */ #define UART_TXEN (1UL << 0) #define UART_TXFULL (1UL << 31) /* RXDATA Fields */ #define UART_RXEN (1UL << 0) #define UART_RXEMPTY (1UL << 31) /* TXCTRL Fields */ #define UART_NSTOP (1UL << 1) #define UART_TXCNT(count) ((0x7UL & count) << 16) /* IP Fields */ #define UART_TXWM (1UL << 0) /* Configuration Defaults */ /* Boot (default) Clock settings */ /* Use External PLL, 320MHz CPU and 50MHz flash */ #define PLLREF_FREQ 16000000 #ifndef CPU_FREQ #define CPU_FREQ 320000000 #endif #define MAX_CPU_FREQ 320000000 #define MAX_FLASH_FREQ 50000000 /* PLL Configuration */ /* R and Q are fixed values for this PLL code */ #define PLL_R (1) /* First Divisor: By 2 (takes 16Mhz PLLREF / 2 = 8MHz) */ #define PLL_F(cpuHz) (((cpuHz / PLLREF_FREQ) * 2U) - 1U) /* Multiplier */ #define PLL_Q (1) /* Second Divisor: By 2 */ /* SPI Serial clock divisor */ #define FESPI_SCKDIV_DEFAULT 0x03U #define FESPI_SCKDIV_VAL(cpuHz, flashHz) (cpuHz / ((2U * flashHz) - 1U)) /* UART baud initialize value */ #ifndef UART_BAUD_INIT #define UART_BAUD_INIT 115200U #endif /* TIME for HiFive1 CLIENT RTC timer */ /* sleep ticks function */ void sleep(uint32_t ticks) { uint32_t start = CLINT_REG_MTIME; while((CLINT_REG_MTIME - start) < ticks) { asm("nop"); } } /* delay milliseconds */ void delay_ms(uint32_t msec) { uint32_t ticks = msec * (RTC_FREQ / 1000); sleep(ticks); } /* UART functions for HiFive1 UART */ void uart_write(char c) { /* wait for space in TX FIFO */ while ((UART_REG_TXDATA & UART_TXFULL) != 0); UART_REG_TXDATA = (uint32_t)c; } char uart_read(void) { uint32_t ch; /* wait for RX to have data */ do { ch = UART_REG_RXDATA; } while ((ch & UART_RXEMPTY) != 0); return (char)(ch & 0xFF); } void uart_init(uint32_t cpu_clock, uint32_t baud_rate) { uint32_t div_val = (cpu_clock / baud_rate) - 1; UART_REG_DIV = div_val; UART_REG_TXCTRL |= UART_TXEN; UART_REG_RXCTRL |= UART_RXEN; } void uart_flush(void) { uint32_t bits_per_symbol, cycles_to_wait; volatile uint32_t x; /* Detect when the TXDATA is empty by setting the transmit watermark count * to one and waiting until an interrupt is pending */ UART_REG_TXCTRL &= ~(UART_TXCNT(0x7)); UART_REG_TXCTRL |= UART_TXCNT(1); while((UART_REG_IP & UART_TXWM) == 0); /* When the TXDATA clears, the UART is still shifting out the last byte. * Calculate the time we must drain to finish transmitting and then wait * that long. */ bits_per_symbol = (UART_REG_TXCTRL & (1 << 1)) ? 9 : 10; cycles_to_wait = bits_per_symbol * (UART_REG_DIV + 1); for(x = 0; x < cycles_to_wait; x++) { asm volatile ("nop"); } } /* QSPI FESPI functions */ void fespi_init(uint32_t cpu_clock, uint32_t flash_freq) { /* Setup desired flash clock divisor */ FESPI_REG_SCKDIV &= ~FESPI_SCKDIV_MASK; FESPI_REG_SCKDIV |= FESPI_SCKDIV_VAL(cpu_clock, flash_freq); } static RAMFUNCTION void fespi_swmode(void) { asm volatile("fence"); asm volatile("fence.i"); if (FESPI_REG_FCTRL & FESPI_FCTRL_MODE_SEL) FESPI_REG_FCTRL &= ~FESPI_FCTRL_MODE_SEL; } static RAMFUNCTION void fespi_hwmode(void) { uint32_t x; if ((FESPI_REG_FCTRL & FESPI_FCTRL_MODE_SEL) == 0) FESPI_REG_FCTRL |= FESPI_FCTRL_MODE_SEL; asm volatile("fence"); asm volatile("fence.i"); /* Wait two milliseconds for the eSPI device * to reboot into hw-mapped mode and link to the * instruction cache */ for(x = 0; x < CPU_FREQ / 500; x++) { asm volatile ("nop"); } } static RAMFUNCTION void fespi_csmode_hold(void) { uint32_t reg = FESPI_REG_CSMODE & ~FESPI_CSMODE_MASK; FESPI_REG_CSMODE = reg | FESPI_CSMODE_HOLD; } static RAMFUNCTION void fespi_csmode_auto(void) { uint32_t reg = FESPI_REG_CSMODE & ~FESPI_CSMODE_MASK; FESPI_REG_CSMODE = reg | FESPI_CSMODE_AUTO; } static RAMFUNCTION void fespi_wait_txwm(void) { while((FESPI_REG_IP & FESPI_IP_TXWM) == 0) ; } static RAMFUNCTION void fespi_sw_tx(uint8_t b) { while((FESPI_REG_TXDATA & FESPI_TXDATA_FIFO_FULL) != 0) ; FESPI_REG_TXDATA = b; } static RAMFUNCTION uint8_t fespi_sw_rx(void) { volatile uint32_t reg; do { reg = FESPI_REG_RXDATA; } while ((reg & FESPI_RXDATA_FIFO_EMPTY) != 0); return (uint8_t)(reg & 0xFF); } static RAMFUNCTION void fespi_sw_setdir(int tx) { if (tx) FESPI_REG_FMT |= FESPI_FMT_DIR_TX; else FESPI_REG_FMT &= ~FESPI_FMT_DIR_TX; } static RAMFUNCTION void fespi_write_address(uint32_t address) { fespi_sw_tx((address & 0xFF0000) >> 16); fespi_sw_tx((address & 0xFF00) >> 8); fespi_sw_tx((address & 0xFF)); fespi_wait_txwm(); } static RAMFUNCTION void fespi_wait_write_disabled(void) { uint8_t rx; fespi_sw_setdir(FESPI_DIR_RX); fespi_csmode_hold(); fespi_sw_tx(FESPI_READ_STATUS); rx = fespi_sw_rx(); while (1) { fespi_sw_tx(0); rx = fespi_sw_rx(); if ((rx & FESPI_RX_WE) == 0) { break; } } fespi_csmode_auto(); fespi_sw_setdir(FESPI_DIR_TX); } static RAMFUNCTION void fespi_write_enable(void) { uint8_t rx; int i; while(1) { fespi_sw_tx(FESPI_WRITE_ENABLE); fespi_wait_txwm(); fespi_sw_setdir(FESPI_DIR_RX); fespi_csmode_hold(); fespi_sw_tx(FESPI_READ_STATUS); rx = fespi_sw_rx(); for (i = 0; i < 3; i++) { fespi_sw_tx(0); rx = fespi_sw_rx(); if ((rx & FESPI_RX_WE) == FESPI_RX_WE) { fespi_csmode_auto(); fespi_sw_setdir(FESPI_DIR_TX); return; } } fespi_csmode_auto(); fespi_sw_setdir(FESPI_DIR_TX); } } static RAMFUNCTION void fespi_wait_flash_busy(void) { uint8_t rx; fespi_sw_setdir(FESPI_DIR_RX); fespi_csmode_hold(); fespi_sw_tx(FESPI_READ_STATUS); while (1) { fespi_sw_tx(0); rx = fespi_sw_rx(); if ((rx & FESPI_RX_BSY) == 0) { break; } } fespi_csmode_auto(); fespi_sw_setdir(FESPI_DIR_TX); } static uint32_t fespi_flash_probe(void); void hifive1_init(uint32_t cpu_clock, uint32_t uart_baud) { uint32_t config_val = 0; /* don't exceed maximum frequency for chip */ if (cpu_clock > MAX_CPU_FREQ) cpu_clock = MAX_CPU_FREQ; /* Flush UART */ uart_flush(); /* Enforce initial default value for QSPI flash clock divisor */ FESPI_REG_SCKDIV = FESPI_SCKDIV_DEFAULT; /* Make sure internal high frequency oscillator is enabled */ PRCI_REG_HFROSCCFG = (HFROSCCFG_EN | HFROSCCFG_DIV_SHIFT(0x4) | HFROSCCFG_TRIM_SHIFT(0x10)); /* Wait for ready */ while((PRCI_REG_HFROSCCFG & HFROSCCFG_READY) == 0); /* If PLL on then switch off before configuring it */ if (PRCI_REG_PLLCFG & PLLCFG_SEL) PRCI_REG_PLLCFG &= ~PLLCFG_SEL; /* Enable external reference */ PRCI_REG_PLLCFG |= PLLCFG_REFSEL; /* Set R */ PRCI_REG_PLLCFG &= ~PLLCFG_R; PRCI_REG_PLLCFG |= PLLCFG_R_SHIFT(PLL_R); /* Calculate and Set F */ config_val = PLL_F(cpu_clock); PRCI_REG_PLLCFG &= ~PLLCFG_F; PRCI_REG_PLLCFG |= PLLCFG_F_SHIFT(config_val); /* Set Q */ PRCI_REG_PLLCFG &= ~PLLCFG_Q; PRCI_REG_PLLCFG |= PLLCFG_Q_SHIFT(PLL_Q); /* Disable final divider */ PRCI_REG_PLLOUTDIV |= PLLOUTDIV_DIV_BY_1; PRCI_REG_PLLOUTDIV &= ~PLLOUTDIV_DIV; PRCI_REG_PLLOUTDIV |= PLLOUTDIV_SHIFT(1); /* Disable bypass */ PRCI_REG_PLLCFG &= ~PLLCFG_BYPASS; /* Wait for PLL to lock */ while ((PRCI_REG_PLLCFG & PLLCFG_LOCK) == 0); /* Enable the PLL */ PRCI_REG_PLLCFG |= PLLCFG_SEL; /* Reconfigure the SPI to maximum frequency */ fespi_init(cpu_clock, MAX_FLASH_FREQ); /* Reconfigure the UART */ uart_init(cpu_clock, uart_baud); } /* public HAL functions */ void hal_init(void) { hifive1_init(CPU_FREQ, UART_BAUD_INIT); } void hal_prepare_boot(void) { } #define FLASH_PAGE_SIZE 256 #define FLASH_BASE 0x20000000UL /* Flash functions must be relocated to RAM for execution */ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) { uint32_t i, j = 0; uint32_t off, page; const uint8_t *src; uint8_t data_copy[FLASH_PAGE_SIZE]; int swmode = 0; if (address >= FLASH_BASE) address -= FLASH_BASE; off = address & 0xFF; page = address >> 8; while (j < (uint32_t)len) { if ((off > 0) || (len < FLASH_PAGE_SIZE)) { uint8_t *orig = (uint8_t *)(FLASH_BASE + (page << 8)); int rel_len; rel_len = FLASH_PAGE_SIZE - off; if (swmode) { fespi_hwmode(); swmode = 0; } if (rel_len > len) rel_len = len; for (i = 0; i < off; i++) data_copy[i] = orig[i]; for (i = off; i < off + rel_len; i++) data_copy[i] = data[j++]; for (i = off + rel_len; i < FLASH_PAGE_SIZE; i++) data_copy[i] = orig[i]; src = data_copy; } else { src = (data + j); j += FLASH_PAGE_SIZE; } if (!swmode) { FESPI_REG_TXMARK = 1; fespi_swmode(); fespi_wait_flash_busy(); swmode++; } fespi_write_enable(); fespi_csmode_hold(); fespi_sw_tx(FESPI_PAGE_PROGRAM); fespi_wait_txwm(); fespi_write_address((page << 8)); for(i = 0; i < FLASH_PAGE_SIZE; i++) { fespi_sw_tx(src[i]); } fespi_csmode_auto(); page++; off = 0; } fespi_hwmode(); return 0; } void RAMFUNCTION hal_flash_unlock(void) { } void RAMFUNCTION hal_flash_lock(void) { } static uint32_t RAMFUNCTION fespi_flash_probe(void) { uint32_t rx; FESPI_REG_TXMARK = 1; fespi_sw_setdir(FESPI_DIR_RX); fespi_swmode(); fespi_wait_txwm(); fespi_wait_flash_busy(); fespi_sw_setdir(FESPI_DIR_RX); fespi_csmode_hold(); fespi_sw_tx(FESPI_READ_ID); fespi_sw_tx(0); fespi_sw_tx(0); fespi_sw_tx(0); rx = fespi_sw_rx(); rx |= fespi_sw_rx() << 8; rx |= fespi_sw_rx() << 16; fespi_csmode_auto(); fespi_sw_setdir(FESPI_DIR_TX); return rx; } int RAMFUNCTION hal_flash_erase(uint32_t address, int len) { uint32_t end; uint32_t p; if (address >= FLASH_BASE) address -= FLASH_BASE; end = address + len - 1; FESPI_REG_TXMARK = 1; fespi_wait_txwm(); fespi_swmode(); fespi_wait_flash_busy(); for (p = address; p <= end; p += FESPI_FLASH_SECTOR_SIZE) { fespi_write_enable(); fespi_csmode_hold(); fespi_sw_tx(FESPI_ERASE_SECTOR); fespi_write_address(p); fespi_wait_txwm(); fespi_csmode_auto(); fespi_wait_flash_busy(); } fespi_hwmode(); return 0; }