wolfBoot/hal/spi/spi_drv_stm32.c

534 lines
16 KiB
C

/* spi_drv_stm32.c
*
* Driver for the SPI back-end of the SPI_FLASH module.
*
* Example implementation for stm32F4, using SPI1.
*
* Pinout: see spi_drv_stm32.h
*
* Copyright (C) 2022 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 <stddef.h>
#include "spi_drv.h"
#ifdef WOLFBOOT_STM32_SPIDRV
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM) || defined(QSPI_FLASH) || \
defined(OCTOSPI_FLASH)
void RAMFUNCTION stm_gpio_config(uint32_t base, uint32_t pin, uint32_t mode,
uint32_t af, uint32_t pull, uint32_t speed)
{
uint32_t reg;
uint32_t base_num = 0;
/* Determine GPIO clock bit */
if (base == GPIOA_BASE)
base_num = 0;
else if (base == GPIOB_BASE)
base_num = 1;
#ifdef GPIOC_BASE
else if (base == GPIOC_BASE)
base_num = 2;
#endif
#ifdef GPIOD_BASE
else if (base == GPIOD_BASE)
base_num = 3;
#endif
#ifdef GPIOE_BASE
else if (base == GPIOE_BASE)
base_num = 4;
#endif
#ifdef GPIOF_BASE
else if (base == GPIOF_BASE)
base_num = 5;
#endif
#ifdef GPIOG_BASE
else if (base == GPIOG_BASE)
base_num = 6;
#endif
#ifdef GPIOH_BASE
else if (base == GPIOH_BASE)
base_num = 7;
#endif
#ifdef GPIOI_BASE
else if (base == GPIOI_BASE)
base_num = 8;
#endif
#ifdef GPIOJ_BASE
else if (base == GPIOJ_BASE)
base_num = 9;
#endif
#ifdef GPIOK_BASE
else if (base == GPIOK_BASE)
base_num = 10;
#endif
/* Enable GPIO clock */
RCC_GPIO_CLOCK_ER |= (1 << base_num);
/* Set Mode and Alternate Function */
reg = GPIO_MODE(base) & ~(0x03UL << (pin * 2));
GPIO_MODE(base) = reg | (mode << (pin * 2));
if (mode < 2) {
if (pin < 8)
GPIO_AFL(base) &= ~(0xfUL << (pin * 4));
else
GPIO_AFH(base) &= ~(0xfUL << ((pin - 8) * 4));
}
else if (mode == 2) {
/* alternate mode */
if (pin < 8) {
reg = GPIO_AFL(base) & ~(0xfUL << (pin * 4));
GPIO_AFL(base) = reg | (af << (pin * 4));
}
else {
reg = GPIO_AFH(base) & ~(0xfUL << ((pin - 8) * 4));
GPIO_AFH(base) = reg | (af << ((pin - 8) * 4));
}
}
/* configure for pull 0=float, 1=pull up, 2=pull down */
reg = GPIO_PUPD(base) & ~(0x03UL << (pin * 2));
GPIO_PUPD(base) = reg | (pull << (pin * 2));
/* configure output speed 0=low, 1=med, 2=high, 3=very high */
reg = GPIO_OSPD(base) & ~(0x03UL << (pin * 2));
GPIO_OSPD(base) |= (speed << (pin * 2));
}
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM)
void RAMFUNCTION spi_cs_off(uint32_t base, int pin)
{
GPIO_BSRR(base) |= (1 << pin);
while (!(GPIO_ODR(base) & (1 << pin)))
;
}
void RAMFUNCTION spi_cs_on(uint32_t base, int pin)
{
GPIO_BSRR(base) |= (1 << (pin + 16));
while (GPIO_ODR(base) & (1 << pin))
;
}
#endif /* SPI_FLASH || WOLFBOOT_TPM */
static void RAMFUNCTION stm_pins_setup(void)
{
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM)
#ifdef TARGET_stm32l0
stm_gpio_config(SPI_CLOCK_PIO_BASE, SPI_CLOCK_PIN, GPIO_MODE_AF,
SPI_CLOCK_PIN_AF, 2, 3);
stm_gpio_config(SPI_MOSI_PIO_BASE, SPI_MOSI_PIN, GPIO_MODE_AF,
SPI_MOSI_PIN_AF, 2, 3);
stm_gpio_config(SPI_MISO_PIO_BASE, SPI_MISO_PIN, GPIO_MODE_AF,
SPI_MISO_PIN_AF, 2, 3);
#else
stm_gpio_config(SPI_CLOCK_PIO_BASE, SPI_CLOCK_PIN, GPIO_MODE_AF,
SPI_CLOCK_PIN_AF, 0, 3);
stm_gpio_config(SPI_MOSI_PIO_BASE, SPI_MOSI_PIN, GPIO_MODE_AF,
SPI_MOSI_PIN_AF, 0, 0);
stm_gpio_config(SPI_MISO_PIO_BASE, SPI_MISO_PIN, GPIO_MODE_AF,
SPI_MISO_PIN_AF, 1, 0);
#endif
#endif
#if defined(QSPI_FLASH) || defined(OCTOSPI_FLASH)
stm_gpio_config(QSPI_CS_PIO_BASE, QSPI_CS_FLASH_PIN, GPIO_MODE_AF,
QSPI_CS_FLASH_AF, 1, 3);
stm_gpio_config(QSPI_CLOCK_PIO_BASE, QSPI_CLOCK_PIN, GPIO_MODE_AF,
QSPI_CLOCK_PIN_AF, 0, 3);
stm_gpio_config(QSPI_IO0_PIO_BASE, QSPI_IO0_PIN, GPIO_MODE_AF,
QSPI_IO0_PIN_AF, 0, 3);
stm_gpio_config(QSPI_IO1_PIO_BASE, QSPI_IO1_PIN, GPIO_MODE_AF,
QSPI_IO1_PIN_AF, 0, 3);
stm_gpio_config(QSPI_IO2_PIO_BASE, QSPI_IO2_PIN, GPIO_MODE_AF,
QSPI_IO2_PIN_AF, 0, 3);
stm_gpio_config(QSPI_IO3_PIO_BASE, QSPI_IO3_PIN, GPIO_MODE_AF,
QSPI_IO3_PIN_AF, 0, 3);
#endif
}
static void stm_pins_release(void)
{
#if defined (SPI_FLASH) || defined (WOLFBOOT_TPM)
stm_gpio_config(SPI_CLOCK_PIO_BASE, SPI_CLOCK_PIN, GPIO_MODE_INPUT, 0, 0, 0);
stm_gpio_config(SPI_MOSI_PIO_BASE, SPI_MOSI_PIN, GPIO_MODE_INPUT, 0, 0, 0);
stm_gpio_config(SPI_MISO_PIO_BASE, SPI_MISO_PIN, GPIO_MODE_INPUT, 0, 0, 0);
#endif
#if defined(QSPI_FLASH) || defined(OCTOSPI_FLASH)
stm_gpio_config(QSPI_CS_PIO_BASE, QSPI_CS_FLASH_PIN, GPIO_MODE_INPUT, 0, 0, 0);
stm_gpio_config(QSPI_CLOCK_PIO_BASE, QSPI_CLOCK_PIN, GPIO_MODE_INPUT, 0, 0, 0);
stm_gpio_config(QSPI_IO0_PIO_BASE, QSPI_IO0_PIN, GPIO_MODE_INPUT, 0, 0, 0);
stm_gpio_config(QSPI_IO1_PIO_BASE, QSPI_IO1_PIN, GPIO_MODE_INPUT, 0, 0, 0);
stm_gpio_config(QSPI_IO2_PIO_BASE, QSPI_IO2_PIN, GPIO_MODE_INPUT, 0, 0, 0);
stm_gpio_config(QSPI_IO3_PIO_BASE, QSPI_IO3_PIN, GPIO_MODE_INPUT, 0, 0, 0);
#endif
}
static void RAMFUNCTION spi_reset(void)
{
#if defined(QSPI_FLASH) || defined(OCTOSPI_FLASH)
#ifndef TARGET_stm32u5
AHB3_CLOCK_RST |= RCC_AHB3ENR_QSPIEN;
AHB3_CLOCK_RST &= ~RCC_AHB3ENR_QSPIEN;
#else
AHB2_CLOCK_RST |= RCC_AHB2ENR_QSPIEN;
AHB2_CLOCK_RST &= ~RCC_AHB2ENR_QSPIEN;
#endif
#endif
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM)
APB2_CLOCK_RST |= SPI1_APB2_CLOCK_ER_VAL;
APB2_CLOCK_RST &= ~SPI1_APB2_CLOCK_ER_VAL;
#endif
}
#ifdef OCTOSPI_FLASH
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)
{
uint32_t adsz = 0, absz = 0;
if (addrSz > 0) {
adsz = addrSz-1;
}
if (altSz > 0) {
absz = altSz-1;
}
/* Enable the QSPI peripheral */
OCTOSPI_CR &= ~(OCTOSPI_CR_EN | OCTOSPI_CR_FMODE_MASK);
OCTOSPI_CR |= OCTOSPI_CR_EN | OCTOSPI_CR_FMODE(fmode);
if (dataSz > 0) {
OCTOSPI_DLR = dataSz-1;
}
/* Configure QSPI: CCR register with all communications parameters */
/* mode 1=1SPI, 2=2SPI, 3=4SPI, 4=8SPI */
OCTOSPI_CCR = (
OCTOSPI_CCR_IMODE(1) | /* Instruction Mode - always single SPI */
OCTOSPI_CCR_ADMODE(addrMode) | /* Address Mode */
OCTOSPI_CCR_ADSIZE(adsz) | /* Address Size */
OCTOSPI_CCR_ABMODE(altMode) | /* Alternate byte mode */
OCTOSPI_CCR_ABSIZE(absz ) | /* Alternate byte size */
OCTOSPI_CCR_DMODE(dataMode) /* Data Mode */
);
OCTOSPI_TCR = OCTOSPI_TCR_DCYC(dummySz); /* Dummy Cycles (between instruction and read) */
OCTOSPI_IR = cmd;
/* Set optional alternate bytes */
if (altSz > 0) {
OCTOSPI_ABR = alt;
}
/* Set command address 4 or 3 byte */
OCTOSPI_AR = addr;
/* Fill data 32-bits at a time */
while (dataSz >= 4U) {
if (fmode == 0) {
while ((OCTOSPI_SR & OCTOSPI_SR_FTF) == 0);
OCTOSPI_DR32 = *(uint32_t*)data;
}
else {
while ((OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TCF)) == 0);
*(uint32_t*)data = OCTOSPI_DR32;
}
dataSz -= 4;
data += 4;
}
/* Fill remainder bytes */
while (dataSz > 0U) {
if (fmode == 0) {
while ((OCTOSPI_SR & OCTOSPI_SR_FTF) == 0);
OCTOSPI_DR = *data;
}
else {
while ((OCTOSPI_SR & (OCTOSPI_SR_FTF | OCTOSPI_SR_TCF)) == 0);
*data = OCTOSPI_DR;
}
dataSz--;
data++;
}
/* wait for transfer complete */
while ((OCTOSPI_SR & OCTOSPI_SR_TCF) == 0);
OCTOSPI_FCR |= OCTOSPI_SR_TCF; /* clear transfer complete */
/* Disable QSPI */
OCTOSPI_CR &= ~OCTOSPI_CR_EN;
return 0;
}
#elif defined(QSPI_FLASH)
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)
{
uint32_t adsz = 0, absz = 0;
if (addrSz > 0) {
adsz = addrSz-1;
}
if (altSz > 0) {
absz = altSz-1;
}
/* Enable the QSPI peripheral */
QUADSPI_CR |= QUADSPI_CR_EN;
if (dataSz > 0) {
QUADSPI_DLR = dataSz-1;
}
/* Configure QSPI: CCR register with all communications parameters */
/* mode 1=1SPI, 2=2SPI, 3=4SPI */
QUADSPI_CCR = (
QUADSPI_CCR_FMODE(fmode) | /* Functional Mode */
QUADSPI_CCR_IMODE(1) | /* Instruction Mode - always single SPI */
QUADSPI_CCR_ADMODE(addrMode) | /* Address Mode */
QUADSPI_CCR_ADSIZE(adsz) | /* Address Size */
QUADSPI_CCR_ABMODE(altMode) | /* Alternate byte mode */
QUADSPI_CCR_ABSIZE(absz ) | /* Alternate byte size */
QUADSPI_CCR_DMODE(dataMode) | /* Data Mode */
QUADSPI_CCR_DCYC(dummySz) | /* Dummy Cycles (between instruction and read) */
cmd /* Instruction / Command byte */
);
/* Set optional alternate bytes */
if (altSz > 0) {
QUADSPI_ABR= alt;
}
/* Set command address 4 or 3 byte */
QUADSPI_AR = addr;
/* Fill data 32-bits at a time */
while (dataSz >= 4U) {
if (fmode == 0) {
while ((QUADSPI_SR & QUADSPI_SR_FTF) == 0);
QUADSPI_DR32 = *(uint32_t*)data;
}
else {
while ((QUADSPI_SR & (QUADSPI_SR_FTF | QUADSPI_SR_TCF)) == 0);
*(uint32_t*)data = QUADSPI_DR32;
}
dataSz -= 4;
data += 4;
}
/* Fill remainder bytes */
while (dataSz > 0U) {
if (fmode == 0) {
while ((QUADSPI_SR & QUADSPI_SR_FTF) == 0);
QUADSPI_DR = *data;
}
else {
while ((QUADSPI_SR & (QUADSPI_SR_FTF | QUADSPI_SR_TCF)) == 0);
*data = QUADSPI_DR;
}
dataSz--;
data++;
}
/* wait for transfer complete */
while ((QUADSPI_SR & QUADSPI_SR_TCF) == 0);
QUADSPI_FCR |= QUADSPI_SR_TCF; /* clear transfer complete */
/* Disable QSPI */
QUADSPI_CR &= ~QUADSPI_CR_EN;
return 0;
}
#endif /* QSPI_FLASH */
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM)
uint8_t RAMFUNCTION spi_read(void)
{
volatile uint32_t reg;
do {
reg = SPI1_SR;
} while(!(reg & SPI_SR_RX_NOTEMPTY));
return (uint8_t)SPI1_DR;
}
void RAMFUNCTION spi_write(const char byte)
{
int i;
volatile uint32_t reg;
do {
reg = SPI1_SR;
} while ((reg & SPI_SR_TX_EMPTY) == 0);
SPI1_DR = byte;
do {
reg = SPI1_SR;
} while ((reg & SPI_SR_TX_EMPTY) == 0);
}
#endif /* SPI_FLASH || WOLFBOOT_TPM */
static int initialized = 0;
void RAMFUNCTION spi_init(int polarity, int phase)
{
if (!initialized) {
initialized++;
/* Setup clocks */
#if defined(QSPI_FLASH) || defined(OCTOSPI_FLASH)
#ifdef TARGET_stm32u5
/* Clock configuration for QSPI defaults to SYSCLK
* (RM0456 section 11.8.47)
*/
#else
/* Select QUADSPI clock source */
RCC_D1CCIPR &= ~RCC_D1CCIPR_QSPISEL_MASK;
RCC_D1CCIPR |= RCC_D1CCIPR_QSPISEL(QSPI_CLOCK_SEL);
AHB3_CLOCK_EN |= RCC_AHB3ENR_QSPIEN;
#endif
#endif
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM)
APB2_CLOCK_ER |= SPI1_APB2_CLOCK_ER_VAL;
#endif
/* reset peripheral before setting up GPIO pins */
spi_reset();
/* Configure pings for SPI / QSPI */
stm_pins_setup();
/* Configure chip selects */
#ifdef SPI_FLASH
stm_gpio_config(SPI_CS_PIO_BASE, SPI_CS_FLASH, GPIO_MODE_OUTPUT,
0, 1, 3);
spi_cs_off(SPI_CS_PIO_BASE, SPI_CS_FLASH);
#endif
#ifdef WOLFBOOT_TPM
stm_gpio_config(SPI_CS_TPM_PIO_BASE, SPI_CS_TPM, GPIO_MODE_OUTPUT,
0, 1, 3);
spi_cs_off(SPI_CS_TPM_PIO_BASE, SPI_CS_TPM);
#endif
#ifdef OCTOSPI_FLASH
/* STM32 OCTOSPI Peripheral */
/* Configure OCTOSPI FIFO Threshold (4 bytes) */
OCTOSPI_CR &= ~OCTOSPI_CR_FTHRES_MASK;
OCTOSPI_CR |= OCTOSPI_CR_FTHRES(4);
/* Wait till BUSY flag cleared */
while (OCTOSPI_SR & OCTOSPI_SR_BUSY) {};
/* Configure OCTOSPI Clock Prescaler (64/X), Flash ID 2 (IO4-7)
* Sample Shift=None */
OCTOSPI_DCR2 &= ~OCTOSPI_DCR2_PRESCALER_MASK;
OCTOSPI_DCR2 |= OCTOSPI_DCR2_PRESCALER((QSPI_CLOCK_BASE/QSPI_CLOCK_MHZ));
OCTOSPI_CR &= ~OCTOSPI_CR_FSEL;
#if QSPI_FLASH_BANK == 2
OCTOSPI_CR |= OCTOSPI_CR_FSEL;
#endif
OCTOSPI_TCR &= ~OCTOSPI_TCR_SSHIFT;
/* Configure OCTOSPI Flash Size (16MB), CS High Time (1 clock) and
* Clock Mode (0) */
OCTOSPI_DCR1 &= ~(OCTOSPI_DCR1_DEVSIZE_MASK | OCTOSPI_DCR1_CSHT_MASK |
OCTOSPI_DCR1_CKMODE_3);
OCTOSPI_DCR1 |= (OCTOSPI_DCR1_DEVSIZE(QSPI_FLASH_SIZE) |
OCTOSPI_DCR1_CSHT(0) | OCTOSPI_DCR1_CKMODE_0);
#elif defined(QSPI_FLASH)
/* STM32 QSPI Peripheral */
/* Configure QSPI FIFO Threshold (4 bytes) */
QUADSPI_CR &= ~QUADSPI_CR_FTHRES_MASK;
QUADSPI_CR |= QUADSPI_CR_FTHRES(4);
/* Wait till BUSY flag cleared */
while (QUADSPI_SR & QUADSPI_SR_BUSY) {};
/* Configure QSPI Clock Prescaler (64/X), Flash ID 0, Dual Flash=0,
* Sample Shift=None */
QUADSPI_CR &= ~(QUADSPI_CR_PRESCALER_MASK | QUADSPI_CR_FSEL |
QUADSPI_CR_DFM | QUADSPI_CR_SSHIFT);
QUADSPI_CR |= (QUADSPI_CR_PRESCALER((QSPI_CLOCK_BASE/QSPI_CLOCK_MHZ)));
#if QSPI_FLASH_BANK == 2
QUADSPI_CR |= QUADSPI_CR_FSEL;
#endif
/* Configure QSPI Flash Size (16MB), CS High Time (1 clock) and
* Clock Mode (0) */
QUADSPI_DCR &= ~(QUADSPI_DCR_FSIZE_MASK | QUADSPI_DCR_CSHT_MASK |
QUADSPI_DCR_CKMODE_3);
QUADSPI_DCR |= (QUADSPI_DCR_FSIZE(QSPI_FLASH_SIZE) |
QUADSPI_DCR_CSHT(0) | QUADSPI_DCR_CKMODE_0);
#endif
#if defined(SPI_FLASH) || defined(WOLFBOOT_TPM)
/* Configure SPI1 for master mode */
# ifdef TARGET_stm32l0
SPI1_CR1 = SPI_CR1_MASTER | (polarity << 1) | (phase << 0);
# else
/* baud rate 5 (hclk/6) */
SPI1_CR1 = SPI_CR1_MASTER | (5 << 3) | (polarity << 1) | (phase << 0);
# endif
SPI1_CR2 |= SPI_CR2_SSOE;
SPI1_CR1 |= SPI_CR1_SPI_EN;
#endif /* SPI_FLASH || WOLFBOOOT_TPM */
}
}
void RAMFUNCTION spi_release(void)
{
if (initialized > 0) {
initialized--;
}
if (initialized == 0) {
spi_reset();
#if defined (SPI_FLASH) || defined(WOLFBOOT_TPM)
SPI1_CR2 &= ~SPI_CR2_SSOE;
SPI1_CR1 = 0;
#endif
stm_pins_release();
}
}
#ifdef WOLFBOOT_TPM
int spi_xfer(int cs, const uint8_t* tx, uint8_t* rx, uint32_t sz, int flags)
{
uint32_t i;
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 || WOLFBOOT_TPM || QSPI_FLASH || OCTOSPI_FLASH */
#endif /* WOLFBOOT_STM32_SPIDRV */