mirror of https://github.com/wolfSSL/wolfBoot.git
597 lines
17 KiB
C
597 lines
17 KiB
C
/* 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 <stdint.h>
|
|
#include <string.h>
|
|
#include <target.h>
|
|
#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;
|
|
}
|