mirror of https://github.com/wolfSSL/wolfBoot.git
450 lines
13 KiB
C
450 lines
13 KiB
C
/* spi_drv_renesas_rx.c
|
|
*
|
|
* Driver for the SPI back-end of the SPI_FLASH module.
|
|
*
|
|
* Example implementation for Renesas RX65N.
|
|
*
|
|
* Copyright (C) 2024 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
|
|
*/
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "spi_drv.h"
|
|
|
|
#ifdef WOLFBOOT_ARCH_RENESAS_RX
|
|
|
|
#include "printf.h"
|
|
#include "hal/renesas-rx.h"
|
|
|
|
#if defined(SPI_FLASH) || defined(QSPI_FLASH)
|
|
|
|
#ifdef SPI_FLASH
|
|
static int rx_spi_init_done = 0;
|
|
#endif
|
|
#ifdef QSPI_FLASH
|
|
static int rx_qspi_init_done = 0;
|
|
static uint16_t rx_qspi_cmd_def;
|
|
#endif
|
|
|
|
/* RSPI1: P27/RSPCKB-A, P26/MOSIB-A, P30/MISOB-A, P31/SSLB0-A */
|
|
/* QSPI: PD2/QIO2-B, PD3/QIO3-B, PD4/QSSL-B, PD5/QSPCLK-B, PD6/QIO0-B, PD7/QIO1-B */
|
|
void spi_init(int polarity, int phase)
|
|
{
|
|
#ifdef SPI_FLASH
|
|
/* Release RSPI1 module stop (clear bit) */
|
|
PROTECT_OFF();
|
|
/* SYS_MSTPCRB: bit 17=RSPI0, 16=RSPI1, SYS_MSTPCRC: bit 22=RSPI2 */
|
|
#if FLASH_RSPI_PORT == 0
|
|
SYS_MSTPCRB &= ~(1 << 17);
|
|
#elif FLASH_RSPI_PORT == 1
|
|
SYS_MSTPCRB &= ~(1 << 16);
|
|
#elif FLASH_RSPI_PORT == 2
|
|
SYS_MSTPCRC &= ~(1 << 22);
|
|
#endif
|
|
PROTECT_ON();
|
|
|
|
/* Configure P26-27 and P30-31 for alt mode */
|
|
PORT_PMR(0x2) |= ((1 << 6) | (1 << 7));
|
|
PORT_PMR(0x3) |= (1 << 0);
|
|
PORT_PDR(0x3) &= ~(1 << 0); /* input */
|
|
#ifdef FLASH_SPI_USE_HW_CS
|
|
PORT_PMR(0x3) |= (1 << 1);
|
|
#else
|
|
PORT_PDR(0x3) |= (1 << 1); /* output */
|
|
#endif
|
|
|
|
/* Disable MPC Write Protect for PFS */
|
|
MPC_PWPR &= ~MPC_PWPR_B0WI;
|
|
MPC_PWPR |= MPC_PWPR_PFSWE;
|
|
|
|
/* Pin Function Select */
|
|
MPC_PFS(0x76) = 0xD; /* P26/MOSIB-A */
|
|
MPC_PFS(0x77) = 0xD; /* P27/RSPCKB-A */
|
|
MPC_PFS(0x78) = 0xD; /* P30/MISOB-A */
|
|
#ifdef FLASH_SPI_USE_HW_CS
|
|
MPC_PFS(0x79) = 0xD; /* P31/SSLB0-A */
|
|
#endif
|
|
|
|
/* Enable MPC Write Protect for PFS */
|
|
MPC_PWPR &= ~(MPC_PWPR_PFSWE | MPC_PWPR_B0WI);
|
|
MPC_PWPR |= MPC_PWPR_PFSWE;
|
|
|
|
/* Configure RSPI */
|
|
RSPI_SPPCR(FLASH_RSPI_PORT) = (RSPI_SPPCR_MOIFV | RSPI_SPPCR_MOIDE); /* enable idle fixing */
|
|
RSPI_SPSCR(FLASH_RSPI_PORT) = RSPI_SPSCR_SPSLN(0); /* seq len 1 */
|
|
RSPI_SPBR(FLASH_RSPI_PORT) = 5; /* 5Mbps */
|
|
RSPI_SPDCR(FLASH_RSPI_PORT) = (RSPI_SPDCR_SPFC(0) | RSPI_SPDCR_SPBYT); /* frames=1, SPDR=byte */
|
|
RSPI_SPCKD(FLASH_RSPI_PORT) = RSPI_SPCKD_SCKDL(0); /* 1 clock delay (SSL assert and first clock cycle) */
|
|
RSPI_SSLND(FLASH_RSPI_PORT) = RSPI_SSLND_SLNDL(0); /* 1 clock delay (last clock cycle and SSL negation) */
|
|
RSPI_SPND(FLASH_RSPI_PORT) = RSPI_SPND_SPNDL(0); /* Next-Access Delay: 1RSPCK+2PCLK */
|
|
RSPI_SPCR2(FLASH_RSPI_PORT) = 0; /* no parity */
|
|
RSPI_SPCMD(FLASH_RSPI_PORT, 0) = (
|
|
RSPI_SPCMD_BRDV(1) | /* div/1 */
|
|
RSPI_SPCMD_SSLA(0) | /* slave select 0 */
|
|
RSPI_SPCMD_SSLKP | /* keep signal level between transfers */
|
|
RSPI_SPCMD_SPB(7) | /* 8-bit data */
|
|
RSPI_SPCMD_SPNDEN | /* enable Next-Access Delay */
|
|
RSPI_SPCMD_SCKDEN /* enable RSPCK Delay */
|
|
);
|
|
if (polarity)
|
|
RSPI_SPCMD(FLASH_RSPI_PORT, 0) |= RSPI_SPCMD_CPOL;
|
|
if (phase)
|
|
RSPI_SPCMD(FLASH_RSPI_PORT, 0) |= RSPI_SPCMD_CPHA;
|
|
|
|
/* Master SPI operation (4-wire method) */
|
|
RSPI_SPCR(FLASH_RSPI_PORT) = RSPI_SPCR_MSTR;
|
|
|
|
rx_spi_init_done++;
|
|
#endif /* SPI_FLASH */
|
|
|
|
#ifdef QSPI_FLASH
|
|
/* Release QSPI module stop (clear bit) */
|
|
PROTECT_OFF();
|
|
/* SYS_MSTPCRC: bit 23=QSPI */
|
|
SYS_MSTPCRC &= ~(1 << 23);
|
|
PROTECT_ON();
|
|
|
|
/* Configure PD2-PD7 for alt mode */
|
|
PORT_PMR(0xD) |= ((1 << 2) | (1 << 3) | (1 << 4) |
|
|
(1 << 5) | (1 << 6) | (1 << 7));
|
|
|
|
/* Disable MPC Write Protect for PFS */
|
|
MPC_PWPR &= ~MPC_PWPR_B0WI;
|
|
MPC_PWPR |= MPC_PWPR_PFSWE;
|
|
|
|
/* Pin Function Select */
|
|
MPC_PFS(0x6A) = 0x1B; /* PD2/QIO2-B */
|
|
MPC_PFS(0x6B) = 0x1B; /* PD3/QIO3-B */
|
|
MPC_PFS(0x6C) = 0x1B; /* PD4/QSSL-B */
|
|
MPC_PFS(0x6D) = 0x1B; /* PD5/QSPCLK-B */
|
|
MPC_PFS(0x6E) = 0x1B; /* PD6/QIO0-B */
|
|
MPC_PFS(0x6F) = 0x1B; /* PD7/QIO1-B */
|
|
|
|
/* Enable MPC Write Protect for PFS */
|
|
MPC_PWPR &= ~(MPC_PWPR_PFSWE | MPC_PWPR_B0WI);
|
|
MPC_PWPR |= MPC_PWPR_PFSWE;
|
|
|
|
/* Configure QSPI */
|
|
QSPI_SPCR = QSPI_SPCR_MSTR; /* Master mode */
|
|
QSPI_SSLP &= ~QSPI_SSLP_SSLP; /* SS Active low */
|
|
QSPI_SPPCR = (QSPI_SPPCR_MOIFV | QSPI_SPPCR_MOIDE); /* enable idle fixing */
|
|
QSPI_SPBR = 1; /* 30Mhz */
|
|
QSPI_SPCKD = QSPI_SPCKD_SCKDL(0); /* 1 clock delay (SSL assert and first clock cycle) */
|
|
QSPI_SSLND = QSPI_SSLND_SLNDL(0); /* 1 clock delay (last clock cycle and SSL negation) */
|
|
QSPI_SPND = QSPI_SPND_SPNDL(0); /* Next-Access Delay: 1RSPCK+2PCLK */
|
|
QSPI_SPDCR = 0; /* no dummy TX */
|
|
|
|
/* Setup default QSPI commands */
|
|
rx_qspi_cmd_def = (
|
|
QSPI_SPCMD_SPIMOD(0) | /* Single SPI */
|
|
QSPI_SPCMD_SPB(0) | /* use byte */
|
|
QSPI_SPCMD_BRDV(0) | /* div/1 (no div) */
|
|
QSPI_SPCMD_SSLKP | /* keep signal level between transfers */
|
|
QSPI_SPCMD_SPNDEN | /* enable Next-Access Delay */
|
|
QSPI_SPCMD_SLNDEN | /* enable negation Delay */
|
|
QSPI_SPCMD_SCKDEN /* enable RSPCK Delay */
|
|
);
|
|
if (polarity)
|
|
rx_qspi_cmd_def |= QSPI_SPCMD_CPOL;
|
|
if (phase)
|
|
rx_qspi_cmd_def |= QSPI_SPCMD_CPHA;
|
|
QSPI_SPCMD(0) = rx_qspi_cmd_def;
|
|
QSPI_SPCMD(1) = rx_qspi_cmd_def;
|
|
QSPI_SPCMD(2) = rx_qspi_cmd_def;
|
|
QSPI_SPCMD(3) = rx_qspi_cmd_def;
|
|
|
|
rx_qspi_init_done++;
|
|
#endif /* QSPI_FLASH */
|
|
(void)polarity;
|
|
(void)phase;
|
|
}
|
|
|
|
void spi_release(void)
|
|
{
|
|
#ifdef SPI_FLASH
|
|
RSPI_SPCR(FLASH_RSPI_PORT) &= ~RSPI_SPCR_SPE; /* Disable SPI master */
|
|
#endif
|
|
#ifdef QSPI_FLASH
|
|
QSPI_SPCR &= ~QSPI_SPCR_SPE; /* Disable QSPI master */
|
|
#endif
|
|
}
|
|
#endif /* SPI_FLASH || QSPI_FLASH */
|
|
|
|
|
|
#ifdef SPI_FLASH
|
|
void spi_cs_on(uint32_t base, int pin)
|
|
{
|
|
(void)base;
|
|
(void)pin;
|
|
#ifdef FLASH_SPI_USE_HW_CS
|
|
/* Enable SPI Master */
|
|
RSPI_SPCR(FLASH_RSPI_PORT) |= RSPI_SPCR_SPE;
|
|
RSPI_SPCMD(FLASH_RSPI_PORT, 0) |= RSPI_SPCMD_SSLKP;
|
|
#else
|
|
PORT_PODR(0x3) &= ~(1 << 1); /* drive low */
|
|
#endif
|
|
}
|
|
void spi_cs_off(uint32_t base, int pin)
|
|
{
|
|
(void)base;
|
|
(void)pin;
|
|
#ifdef FLASH_SPI_USE_HW_CS
|
|
RSPI_SPCMD(FLASH_RSPI_PORT, 0) &= ~RSPI_SPCMD_SSLKP;
|
|
RSPI_SPCR(FLASH_RSPI_PORT) &= ~RSPI_SPCR_SPE;
|
|
#else
|
|
PORT_PODR(0x3) |= (1 << 1); /* drive high */
|
|
#endif
|
|
}
|
|
|
|
void spi_write(const char byte)
|
|
{
|
|
while ((RSPI_SPSR(FLASH_RSPI_PORT) & RSPI_SPSR_SPTEF) == 0);
|
|
RSPI_SPSR8(FLASH_RSPI_PORT) = byte;
|
|
}
|
|
uint8_t spi_read(void)
|
|
{
|
|
while ((RSPI_SPSR(FLASH_RSPI_PORT) & RSPI_SPSR_SPRF) == 0);
|
|
return RSPI_SPSR8(FLASH_RSPI_PORT);
|
|
}
|
|
|
|
#ifdef WOLFBOOT_TPM
|
|
int spi_xfer(int cs, const uint8_t* tx, uint8_t* rx, uint32_t sz, int flags)
|
|
{
|
|
uint32_t i;
|
|
if (!rx_spi_init_done) {
|
|
wolfBoot_printf("SPI init not yet called\n");
|
|
return -1;
|
|
}
|
|
|
|
spi_cs_on(SPI_CS_TPM_PIO_BASE, cs);
|
|
for (i = 0; i < sz; i++) {
|
|
spi_write((const char)tx[i]);
|
|
rx[i] = spi_read();
|
|
}
|
|
if (!(flags & SPI_XFER_FLAG_CONTINUE)) {
|
|
spi_cs_off(SPI_CS_TPM_PIO_BASE, cs);
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /* WOLFBOOT_TPM */
|
|
|
|
#endif /* SPI_FLASH */
|
|
|
|
|
|
#ifdef QSPI_FLASH
|
|
|
|
static uint32_t fifoLvl = 0;
|
|
static void qspi_cmd(const uint8_t* cmd, uint32_t cmdSz)
|
|
{
|
|
uint8_t tmp;
|
|
while (cmdSz > 0) {
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPTEF) == 0);
|
|
if (cmd != NULL)
|
|
QSPI_SPDR8 = *cmd++;
|
|
else
|
|
QSPI_SPDR8 = 0xFF;
|
|
QSPI_SPSR &= ~QSPI_SPSR_SPTEF;
|
|
cmdSz--;
|
|
fifoLvl++;
|
|
}
|
|
}
|
|
|
|
/* dataSz in bytes */
|
|
static int qspi_data(const uint32_t* txData, uint32_t* rxData, uint32_t dataSz)
|
|
{
|
|
volatile uint32_t tmp;
|
|
uint32_t i;
|
|
uint8_t *pTx, *pRx;
|
|
|
|
/* flush anything in the RX FIFO */
|
|
while (fifoLvl > 0) {
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPRFF) == 0);
|
|
tmp = QSPI_SPDR8;
|
|
QSPI_SPSR &= ~QSPI_SPSR_SPRFF;
|
|
fifoLvl--;
|
|
}
|
|
|
|
/* Do full FIFO (32 bytes) TX/RX - word */
|
|
while (dataSz >= (QSPI_FIFO_SIZE/2)) {
|
|
/* Transfer bytes - fill 16 bytes */
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPTEF) == 0);
|
|
for (i=0; i<(QSPI_FIFO_SIZE/2); i+=4) {
|
|
tmp = 0xFFFFFFFF;
|
|
if (txData) {
|
|
tmp = *txData++;
|
|
#ifndef BIG_ENDIAN_ORDER
|
|
tmp = __builtin_bswap32(tmp);
|
|
#endif
|
|
}
|
|
QSPI_SPDR32 = tmp;
|
|
dataSz -= 4;
|
|
fifoLvl += 4;
|
|
}
|
|
QSPI_SPSR &= ~QSPI_SPSR_SPTEF;
|
|
|
|
/* Recieve bytes - (previous 16 bytes) */
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPRFF) == 0);
|
|
while (fifoLvl > (QSPI_FIFO_SIZE/2)) {
|
|
tmp = QSPI_SPDR32;
|
|
if (rxData) {
|
|
#ifndef BIG_ENDIAN_ORDER
|
|
tmp = __builtin_bswap32(tmp);
|
|
#endif
|
|
*rxData++ = tmp;
|
|
}
|
|
fifoLvl -= 4;
|
|
}
|
|
QSPI_SPSR &= ~QSPI_SPSR_SPRFF;
|
|
}
|
|
|
|
/* Remainder < FIFO TX/RX - byte */
|
|
pTx = (uint8_t*)txData;
|
|
pRx = (uint8_t*)rxData;
|
|
|
|
/* Transmit Data */
|
|
while (dataSz > 0) {
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPTEF) == 0);
|
|
if (pTx)
|
|
QSPI_SPDR8 = *pTx++;
|
|
else
|
|
QSPI_SPDR8 = 0xFF;
|
|
QSPI_SPSR &= ~QSPI_SPSR_SPTEF;
|
|
dataSz--;
|
|
fifoLvl++;
|
|
}
|
|
|
|
/* wait for transfer to finish */
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPSSLF) == 0);
|
|
|
|
/* Recieve bytes */
|
|
while (fifoLvl > 0) {
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPRFF) == 0);
|
|
if (pRx)
|
|
*pRx++ = QSPI_SPDR8;
|
|
else
|
|
tmp = QSPI_SPDR8;
|
|
fifoLvl--;
|
|
QSPI_SPSR &= ~QSPI_SPSR_SPRFF;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* fmode = read (1) / write (0) */
|
|
int qspi_transfer(uint8_t fmode, const uint8_t cmd,
|
|
uint32_t addr, uint32_t addrSz, uint32_t addrMode,
|
|
uint32_t alt, uint32_t altSz, uint32_t altMode,
|
|
uint32_t dummySz,
|
|
uint8_t* data, uint32_t dataSz, uint32_t dataMode)
|
|
{
|
|
int ret;
|
|
uint8_t seq = 0;
|
|
volatile uint32_t reg;
|
|
uint32_t timeout = 10000;
|
|
|
|
if (!rx_qspi_init_done) {
|
|
wolfBoot_printf("QSPI init not yet called\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Clear flags - write 0 to bit to clear */
|
|
QSPI_SPSR &= ~(QSPI_SPSR_SPTEF | QSPI_SPSR_SPRFF | QSPI_SPSR_SPSSLF);
|
|
|
|
/* Reset buffers */
|
|
QSPI_SPBFCR |= (QSPI_SPBFCR_RXRST | QSPI_SPBFCR_TXRST);
|
|
reg = QSPI_SPBFCR; /* SPBFCR requires dummy read after write */
|
|
/* Set FIFO Trigger Level - must be set when SPCR.SPE=0 */
|
|
//QSPI_SPBFCR = QSPI_SPBFCR_RXTRG(5) | QSPI_SPBFCR_TXTRG(3); /* RX Trig=16 bytes, TX Trig=16 bytes */
|
|
QSPI_SPBFCR = QSPI_SPBFCR_RXTRG(0) | QSPI_SPBFCR_TXTRG(6); /* RX Trig=1 byte, TX Trig=0 bytes */
|
|
reg = QSPI_SPBFCR; /* SPBFCR requires dummy read after write */
|
|
|
|
/* Command / Instruction - Write (command always SPI mode) */
|
|
QSPI_SPBMUL(seq) = 1; /* Set Data length */
|
|
QSPI_SPCMD(seq) = (rx_qspi_cmd_def | QSPI_SPCMD_SPIMOD(0));
|
|
seq++;
|
|
|
|
/* Address Write */
|
|
if (addrSz > 0 && addrMode != QSPI_DATA_MODE_NONE) {
|
|
QSPI_SPBMUL(seq) = addrSz;
|
|
QSPI_SPCMD(seq) = (rx_qspi_cmd_def | QSPI_SPCMD_SPIMOD(addrMode-1));
|
|
seq++;
|
|
}
|
|
|
|
/* Alternate bytes */
|
|
if (altSz > 0 && altMode != QSPI_DATA_MODE_NONE) {
|
|
QSPI_SPBMUL(seq) = altSz;
|
|
QSPI_SPCMD(seq) = (rx_qspi_cmd_def | QSPI_SPCMD_SPIMOD(altMode-1));
|
|
seq++;
|
|
}
|
|
|
|
/* Data */
|
|
if (dataSz > 0 && dataMode != QSPI_DATA_MODE_NONE) {
|
|
QSPI_SPBMUL(seq) = dataSz;
|
|
QSPI_SPCMD(seq) = (rx_qspi_cmd_def | QSPI_SPCMD_SPIMOD(dataMode-1));
|
|
if (fmode == QSPI_MODE_READ)
|
|
QSPI_SPCMD(seq) |= QSPI_SPCMD_SPREAD;
|
|
seq++;
|
|
}
|
|
|
|
/* End CS (set high) on last transaction */
|
|
QSPI_SPCMD(seq-1) &= ~QSPI_SPCMD_SSLKP;
|
|
|
|
/* Set number of sequences */
|
|
QSPI_SPSCR = QSPI_SPSCR_SPSC(seq-1);
|
|
|
|
/* Enable the QSPI peripheral */
|
|
QSPI_SPCR |= QSPI_SPCR_SPE;
|
|
|
|
/* Transfer Data for sequences */
|
|
qspi_cmd(&cmd, 1);
|
|
if (addrMode != QSPI_DATA_MODE_NONE) {
|
|
qspi_cmd((const uint8_t*)&addr, addrSz);
|
|
}
|
|
if (altMode != QSPI_DATA_MODE_NONE) {
|
|
qspi_cmd((const uint8_t*)&alt, altSz);
|
|
}
|
|
if (dummySz > 0) {
|
|
qspi_cmd(NULL, dummySz/8);
|
|
}
|
|
if (fmode == QSPI_MODE_READ)
|
|
qspi_data(NULL, (uint32_t*)data, dataSz);
|
|
else
|
|
qspi_data((const uint32_t*)data, NULL, dataSz);
|
|
|
|
/* wait for slave select to de-assert */
|
|
while ((QSPI_SPSR & QSPI_SPSR_SPSSLF) == 0 && --timeout > 0) {
|
|
hal_delay_us(1);
|
|
}
|
|
|
|
/* check for timeout (-1) or success */
|
|
ret = (timeout == 0) ? -1 : 0;
|
|
|
|
/* Clear flags - write 0 to bit to clear */
|
|
QSPI_SPSR &= ~(QSPI_SPSR_SPTEF | QSPI_SPSR_SPRFF | QSPI_SPSR_SPSSLF);
|
|
|
|
/* Disable QSPI */
|
|
QSPI_SPCR &= ~QSPI_SPCR_SPE;
|
|
|
|
return ret;
|
|
}
|
|
#endif /* QSPI_FLASH */
|
|
#endif /* WOLFBOOT_ARCH_RENESAS_RX */
|