aboutsummaryrefslogtreecommitdiffstats
path: root/src/stm32/stm32l4.c
diff options
context:
space:
mode:
authorMatt Baker <baker.matt.j@gmail.com>2021-04-22 13:44:39 -0700
committerKevinOConnor <kevin@koconnor.net>2022-09-15 11:51:26 -0400
commitd9c917b95099617b32dfaca4ae90c9e2dd641fff (patch)
treea6a7adb3fec7d84a5fa0bbb75432a229b1ee7699 /src/stm32/stm32l4.c
parent57b0eb5d43e4324c2c52282925ddc63fc36df783 (diff)
downloadkutter-d9c917b95099617b32dfaca4ae90c9e2dd641fff.tar.gz
kutter-d9c917b95099617b32dfaca4ae90c9e2dd641fff.tar.xz
kutter-d9c917b95099617b32dfaca4ae90c9e2dd641fff.zip
stm32l4: add stm32l412 support with adc,i2c,spi,usb
Signed-off-by: Matt Baker <baker.matt.j@gmail.com>
Diffstat (limited to 'src/stm32/stm32l4.c')
-rw-r--r--src/stm32/stm32l4.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/src/stm32/stm32l4.c b/src/stm32/stm32l4.c
new file mode 100644
index 00000000..8fb50a23
--- /dev/null
+++ b/src/stm32/stm32l4.c
@@ -0,0 +1,175 @@
+// Code to setup clocks and gpio on stm32l4
+//
+// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
+//
+// 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" // VectorTable
+#include "board/irq.h" // irq_disable
+#include "board/usb_cdc.h" // usb_request_bootloader
+#include "command.h" // DECL_CONSTANT_STR
+#include "internal.h" // enable_pclock
+#include "sched.h" // sched_main
+
+#define FREQ_PERIPH_DIV 1
+#define FREQ_PERIPH (CONFIG_CLOCK_FREQ / FREQ_PERIPH_DIV)
+
+// Map a peripheral address to its enable bits
+struct cline
+lookup_clock_line(uint32_t periph_base)
+{
+ if (periph_base < APB2PERIPH_BASE) {
+ uint32_t pos = (periph_base - APB1PERIPH_BASE) / 0x400;
+ if (pos < 32) {
+ return (struct cline){.en = &RCC->APB1ENR1,
+ .rst = &RCC->APB1RSTR1,
+ .bit = 1 << pos};
+ } else {
+ return (struct cline){.en = &RCC->APB1ENR2,
+ .rst = &RCC->APB1RSTR2,
+ .bit = 1 << (pos - 32)};
+ }
+ } else if (periph_base < AHB1PERIPH_BASE) {
+ uint32_t pos = (periph_base - APB2PERIPH_BASE) / 0x400;
+ return (struct cline){.en = &RCC->APB2ENR,
+ .rst = &RCC->APB2RSTR,
+ .bit = 1 << pos};
+
+ } else if (periph_base < AHB2PERIPH_BASE) {
+ uint32_t pos = (periph_base - AHB1PERIPH_BASE) / 0x400;
+ return (struct cline){.en = &RCC->AHB1ENR,
+ .rst = &RCC->AHB1RSTR,
+ .bit = 1 << pos};
+
+ } else if (periph_base == ADC1_BASE) {
+ return (struct cline){.en = &RCC->AHB2ENR,
+ .rst = &RCC->AHB2RSTR,
+ .bit = RCC_AHB2ENR_ADCEN};
+ }
+ return (struct cline){.en = &RCC->AHB2ENR,
+ .rst = &RCC->AHB2RSTR,
+ .bit = 0};
+}
+
+// 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 - GPIOA_BASE) / 0x400;
+ RCC->AHB2ENR |= 1 << rcc_pos;
+ RCC->AHB2ENR;
+}
+
+#define USB_BOOT_FLAG_ADDR (CONFIG_RAM_START + CONFIG_RAM_SIZE - 4096)
+#define USB_BOOT_FLAG 0x55534220424f4f54 // "USB BOOT"
+
+// Handle USB reboot requests
+void
+usb_request_bootloader(void)
+{
+ irq_disable();
+ // System DFU Bootloader
+ *(uint64_t*)USB_BOOT_FLAG_ADDR = USB_BOOT_FLAG;
+ NVIC_SystemReset();
+}
+
+void
+bootloader_request(void)
+{
+ usb_request_bootloader();
+}
+
+#if !CONFIG_STM32_CLOCK_REF_INTERNAL
+DECL_CONSTANT_STR("RESERVE_PINS_crystal", "PC14,PC15");
+#endif
+
+static void
+enable_clock_stm32l4(void)
+{
+ uint32_t pll_base = 4000000, pll_freq = CONFIG_CLOCK_FREQ * 2, pllcfgr;
+ if (!CONFIG_STM32_CLOCK_REF_INTERNAL) {
+ // Configure 80Mhz PLL from external crystal (HSE)
+ uint32_t div = CONFIG_CLOCK_REF_FREQ / pll_base - 1;
+ RCC->CR |= RCC_CR_HSEON;
+ while (!(RCC->CR & RCC_CR_HSERDY))
+ ;
+ pllcfgr = RCC_PLLCFGR_PLLSRC_HSE | (div << RCC_PLLCFGR_PLLM_Pos);
+ } else {
+ // Configure 80Mhz PLL from internal 16Mhz oscillator (HSI)
+ uint32_t div = 16000000 / pll_base - 1;
+ pllcfgr = RCC_PLLCFGR_PLLSRC_HSI | (div << RCC_PLLCFGR_PLLM_Pos);
+ RCC->CR |= RCC_CR_HSION;
+ while (!(RCC->CR & RCC_CR_HSIRDY))
+ ;
+ }
+ RCC->PLLCFGR = (pllcfgr | ((pll_freq/pll_base) << RCC_PLLCFGR_PLLN_Pos)
+ | (0 << RCC_PLLCFGR_PLLR_Pos));
+ RCC->CR |= RCC_CR_PLLON;
+
+ // Enable 48Mhz USB clock using clock recovery
+ if (CONFIG_USBSERIAL) {
+ RCC->CRRCR |= RCC_CRRCR_HSI48ON;
+ while (!(RCC->CRRCR & RCC_CRRCR_HSI48RDY))
+ ;
+ enable_pclock(CRS_BASE);
+ CRS->CR |= CRS_CR_AUTOTRIMEN | CRS_CR_CEN;
+ enable_pclock(PWR_BASE);
+ PWR->CR2 |= PWR_CR2_USV;
+ }
+}
+
+// Main clock setup called at chip startup
+static void
+clock_setup(void)
+{
+ enable_clock_stm32l4();
+
+ // Set flash latency
+ uint32_t latency = ((CONFIG_CLOCK_FREQ>64000000) ? FLASH_ACR_LATENCY_4WS :
+ ((CONFIG_CLOCK_FREQ>48000000) ? FLASH_ACR_LATENCY_3WS :
+ ((CONFIG_CLOCK_FREQ>32000000) ? FLASH_ACR_LATENCY_2WS :
+ ((CONFIG_CLOCK_FREQ>16000000) ? FLASH_ACR_LATENCY_1WS :
+ FLASH_ACR_LATENCY_0WS))));
+ FLASH->ACR = (latency | FLASH_ACR_ICEN | FLASH_ACR_DCEN
+ | FLASH_ACR_PRFTEN);
+
+ // Wait for PLL lock
+ while (!(RCC->CR & RCC_CR_PLLRDY))
+ ;
+
+ RCC->PLLCFGR |= RCC_PLLCFGR_PLLREN;
+
+ // Switch system clock to PLL
+ RCC->CFGR = RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV1 | RCC_CFGR_PPRE2_DIV1
+ | RCC_CFGR_SW_PLL;
+ while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL)
+ ;
+}
+
+// Main entry point - called from armcm_boot.c:ResetHandler()
+void
+armcm_main(void)
+{
+ if (CONFIG_USBSERIAL && *(uint64_t*)USB_BOOT_FLAG_ADDR == USB_BOOT_FLAG) {
+ *(uint64_t*)USB_BOOT_FLAG_ADDR = 0;
+ uint32_t *sysbase = (uint32_t*)0x1fff0000;
+ asm volatile("mov sp, %0\n bx %1"
+ : : "r"(sysbase[0]), "r"(sysbase[1]));
+ }
+
+ // Run SystemInit() and then restore VTOR
+ SystemInit();
+ SCB->VTOR = (uint32_t)VectorTable;
+
+ clock_setup();
+
+ sched_main();
+}