From 115582ce889ab7c76aa6243313e8730be73c1bff Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Mon, 16 May 2022 22:24:36 -0400 Subject: [PATCH] stm32: Add stm32g0 support Signed-off-by: Kevin O'Connor --- src/stm32/Kconfig | 6 +- src/stm32/Makefile | 5 ++ src/stm32/flash.c | 29 +++++++++- src/stm32/stm32g0.c | 133 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 5 deletions(-) create mode 100644 src/stm32/stm32g0.c diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index dab645a..30642ab 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -65,7 +65,7 @@ choice select MACH_STM32F0 select MACH_STM32F0x2 config MACH_STM32G0B1 - bool "STM32G0B1" if 0 + bool "STM32G0B1" select MACH_STM32G0 config MACH_STM32H743 bool "STM32H743" if 0 @@ -348,9 +348,9 @@ choice config STM32_APP_START_4000 bool "16KiB offset" if MACH_STM32F2 || MACH_STM32F4 config STM32_APP_START_2000 - bool "8KiB offset" if MACH_STM32F0 || MACH_STM32F1 + bool "8KiB offset" if MACH_STM32F0 || MACH_STM32F1 || MACH_STM32G0 config STM32_APP_START_1000 - bool "4KiB offset" if MACH_STM32F0 || MACH_STM32F1 + bool "4KiB offset" if MACH_STM32F0 || MACH_STM32F1 || MACH_STM32G0 endchoice config APPLICATION_START diff --git a/src/stm32/Makefile b/src/stm32/Makefile index 1c07e7e..5cdf64f 100644 --- a/src/stm32/Makefile +++ b/src/stm32/Makefile @@ -8,6 +8,7 @@ dirs-$(CONFIG_MACH_STM32F0) += lib/stm32f0 dirs-$(CONFIG_MACH_STM32F1) += lib/stm32f1 dirs-$(CONFIG_MACH_STM32F2) += lib/stm32f2 dirs-$(CONFIG_MACH_STM32F4) += lib/stm32f4 +dirs-$(CONFIG_MACH_STM32G0) += lib/stm32g0 MCU := $(shell echo $(CONFIG_MCU)) MCU_UPPER := $(shell echo $(CONFIG_MCU) | tr a-z A-Z | tr X x) @@ -16,6 +17,7 @@ CFLAGS-$(CONFIG_MACH_STM32F0) += -mcpu=cortex-m0 -Ilib/stm32f0/include CFLAGS-$(CONFIG_MACH_STM32F1) += -mcpu=cortex-m3 -Ilib/stm32f1/include CFLAGS-$(CONFIG_MACH_STM32F2) += -mcpu=cortex-m3 -Ilib/stm32f2/include CFLAGS-$(CONFIG_MACH_STM32F4) += -mcpu=cortex-m4 -Ilib/stm32f4/include +CFLAGS-$(CONFIG_MACH_STM32G0) += -mcpu=cortex-m0plus -Ilib/stm32g0/include CFLAGS += $(CFLAGS-y) -D$(MCU_UPPER) -mthumb -Ilib/cmsis-core -Ilib/fast-hash CFLAGS_canboot.elf += --specs=nano.specs --specs=nosys.specs @@ -40,6 +42,9 @@ src-$(CONFIG_MACH_STM32F4) += ../lib/stm32f4/system_stm32f4xx.c src-$(CONFIG_MACH_STM32F4) += stm32/stm32f4.c generic/armcm_timer.c src-$(CONFIG_MACH_STM32F4) += stm32/gpioperiph.c +src-$(CONFIG_MACH_STM32G0) += stm32/stm32f0_timer.c +src-$(CONFIG_MACH_STM32G0) += stm32/stm32g0.c stm32/gpioperiph.c + usb-src-$(CONFIG_HAVE_STM32_USBFS) := stm32/usbfs.c usb-src-$(CONFIG_HAVE_STM32_USBOTG) := stm32/usbotg.c src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c diff --git a/src/stm32/flash.c b/src/stm32/flash.c index a46a652..ea582d5 100644 --- a/src/stm32/flash.c +++ b/src/stm32/flash.c @@ -32,6 +32,8 @@ flash_get_page_size(uint32_t addr) return 2 * 1024; uint16_t *flash_size = (void*)FLASHSIZE_BASE; return *flash_size <= 64 ? 1024 : 2 * 1024; + } else if (CONFIG_MACH_STM32G0) { + return 2 * 1024; } } @@ -46,6 +48,10 @@ check_erased(uint32_t addr, uint32_t count) return 1; } +#if CONFIG_MACH_STM32G0 // stm32g0 chip has slightly different bit name +#define FLASH_SR_BSY (FLASH_SR_BSY1 | FLASH_SR_BSY2) +#endif + // Wait for flash hardware to report ready static void wait_flash(void) @@ -93,10 +99,21 @@ erase_page(uint32_t page_address) sidx = sidx > 0x0f ? 0x0f : sidx; FLASH->CR = (FLASH_CR_PSIZE_1 | FLASH_CR_STRT | FLASH_CR_SER | (sidx << FLASH_CR_SNB_Pos)); -#else +#elif CONFIG_MACH_STM32F0 || CONFIG_MACH_STM32F1 FLASH->CR = FLASH_CR_PER; FLASH->AR = page_address; FLASH->CR = FLASH_CR_PER | FLASH_CR_STRT; +#elif CONFIG_MACH_STM32G0 + uint32_t pidx = (page_address - 0x08000000) / (2 * 1024); + if (pidx >= 64) { + uint16_t *flash_size = (void*)FLASHSIZE_BASE; + if (*flash_size <= 256) + pidx = pidx + 256 - 64; + else + pidx = pidx < 128 ? pidx : pidx + 256 - 128; + } + pidx = pidx > 0x3ff ? 0x3ff : pidx; + FLASH->CR = FLASH_CR_PER | FLASH_CR_STRT | (pidx << FLASH_CR_PNB_Pos); #endif wait_flash(); } @@ -112,13 +129,21 @@ write_block(uint32_t block_address, uint32_t *data) writel(&page[i], data[i]); wait_flash(); } -#else +#elif CONFIG_MACH_STM32F0 || CONFIG_MACH_STM32F1 uint16_t *page = (void*)block_address, *data16 = (void*)data; FLASH->CR = FLASH_CR_PG; for (int i = 0; i < CONFIG_BLOCK_SIZE / 2; i++) { writew(&page[i], data16[i]); wait_flash(); } +#elif CONFIG_MACH_STM32G0 + uint32_t *page = (void*)block_address; + FLASH->CR = FLASH_CR_PG; + for (int i = 0; i < CONFIG_BLOCK_SIZE / 8; i++) { + writel(&page[i*2], data[i*2]); + writel(&page[i*2 + 1], data[i*2 + 1]); + wait_flash(); + } #endif } diff --git a/src/stm32/stm32g0.c b/src/stm32/stm32g0.c new file mode 100644 index 0000000..ef5dec0 --- /dev/null +++ b/src/stm32/stm32g0.c @@ -0,0 +1,133 @@ +// Code to setup clocks on stm32g0 +// +// Copyright (C) 2019-2021 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_REF_FREQ +#include "board/armcm_boot.h" // armcm_main +#include "board/irq.h" // irq_disable +#include "command.h" // DECL_CONSTANT_STR +#include "internal.h" // enable_pclock +#include "sched.h" // sched_main + + +/**************************************************************** + * Clock setup + ****************************************************************/ + +#define FREQ_PERIPH 64000000 +#define FREQ_USB 48000000 + +// Map a peripheral address to its enable bits +struct cline +lookup_clock_line(uint32_t periph_base) +{ + if (periph_base >= IOPORT_BASE) { + uint32_t bit = 1 << ((periph_base - IOPORT_BASE) / 0x400); + return (struct cline){.en=&RCC->IOPENR, .rst=&RCC->IOPRSTR, .bit=bit}; + } else if (periph_base >= AHBPERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - AHBPERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->AHBENR, .rst=&RCC->AHBRSTR, .bit=bit}; + } + if (periph_base == USB_BASE) + return (struct cline){.en=&RCC->APBENR1,.rst=&RCC->APBRSTR1,.bit=1<<13}; + if (periph_base == CRS_BASE) + return (struct cline){.en=&RCC->APBENR1,.rst=&RCC->APBRSTR1,.bit=1<<16}; + if (periph_base == SPI1_BASE) + return (struct cline){.en=&RCC->APBENR2,.rst=&RCC->APBRSTR2,.bit=1<<12}; + if (periph_base == USART1_BASE) + return (struct cline){.en=&RCC->APBENR2,.rst=&RCC->APBRSTR2,.bit=1<<14}; + if (periph_base == ADC1_BASE) + return (struct cline){.en=&RCC->APBENR2,.rst=&RCC->APBRSTR2,.bit=1<<20}; + uint32_t bit = 1 << ((periph_base - APBPERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->APBENR1, .rst=&RCC->APBRSTR1, .bit=bit}; +} + +// Return the frequency of the given peripheral clock +uint32_t +get_pclock_frequency(uint32_t periph_base) +{ + return FREQ_PERIPH; +} + +// Enable a GPIO peripheral clock +void +gpio_clock_enable(GPIO_TypeDef *regs) +{ + uint32_t rcc_pos = ((uint32_t)regs - IOPORT_BASE) / 0x400; + RCC->IOPENR |= 1 << rcc_pos; + RCC->IOPENR; +} + +#if !CONFIG_STM32_CLOCK_REF_INTERNAL +DECL_CONSTANT_STR("RESERVE_PINS_crystal", "PF0,PF1"); +#endif + +// Configure and enable the PLL as clock source +static void +clock_setup(void) +{ + uint32_t pll_base = 4000000, pll_freq = 192000000, pllcfgr; + if (!CONFIG_STM32_CLOCK_REF_INTERNAL) { + // Configure PLL from external crystal (HSE) + uint32_t div = CONFIG_CLOCK_REF_FREQ / pll_base; + RCC->CR |= RCC_CR_HSEON; + pllcfgr = RCC_PLLCFGR_PLLSRC_HSE | ((div - 1) << RCC_PLLCFGR_PLLM_Pos); + } else { + // Configure PLL from internal 16Mhz oscillator (HSI) + uint32_t div = 16000000 / pll_base; + pllcfgr = RCC_PLLCFGR_PLLSRC_HSI | ((div - 1) << RCC_PLLCFGR_PLLM_Pos); + } + pllcfgr |= (pll_freq/pll_base) << RCC_PLLCFGR_PLLN_Pos; + pllcfgr |= (pll_freq/CONFIG_CLOCK_FREQ - 1) << RCC_PLLCFGR_PLLR_Pos; + pllcfgr |= (pll_freq/FREQ_USB - 1) << RCC_PLLCFGR_PLLQ_Pos; + RCC->PLLCFGR = pllcfgr | RCC_PLLCFGR_PLLREN | RCC_PLLCFGR_PLLQEN; + RCC->CR |= RCC_CR_PLLON; + + // Wait for PLL lock + while (!(RCC->CR & RCC_CR_PLLRDY)) + ; + + // Switch system clock to PLL + RCC->CFGR = (2 << RCC_CFGR_SW_Pos); + while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != (2 << RCC_CFGR_SWS_Pos)) + ; + + // Use PLLQCLK for USB (setting USBSEL=2 works in practice) + RCC->CCIPR2 = 2 << RCC_CCIPR2_USBSEL_Pos; +} + + +/**************************************************************** + * Startup + ****************************************************************/ + +// Main entry point - called from armcm_boot.c:ResetHandler() +void +armcm_main(void) +{ + SCB->VTOR = (uint32_t)VectorTable; + + // Reset clock registers (in case bootloader has changed them) + RCC->CR |= RCC_CR_HSION; + while (!(RCC->CR & RCC_CR_HSIRDY)) + ; + RCC->CFGR = 0x00000000; + RCC->CR = RCC_CR_HSION; + while (RCC->CR & RCC_CR_PLLRDY) + ; + RCC->PLLCFGR = 0x00001000; + RCC->IOPENR = 0x00000000; + RCC->AHBENR = 0x00000100; + RCC->APBENR1 = 0x00000000; + RCC->APBENR2 = 0x00000000; + + // Set flash latency + FLASH->ACR = (2<