/* 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 #include #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 */