src: update stm32 source

Bring in the latest changes from Klipper.  The STM32F4 variants are
still unsupported at this time.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Eric Callahan 2022-04-11 19:59:56 -04:00
parent 73d7ddd5aa
commit 26b12a658b
No known key found for this signature in database
GPG Key ID: 7027245FBBDDF59A
10 changed files with 397 additions and 339 deletions

View File

@ -12,7 +12,7 @@ config CANSERIAL
bool
default y
config CANBUS_BAUD
config CONFIG_CANBUS_FREQUENCY
int "CAN bus speed"
default 500000

View File

@ -17,18 +17,27 @@ choice
config MACH_STM32F405
bool "STM32F405"
select MACH_STM32F4
select MACH_STM32F4x5
config MACH_STM32F407
bool "STM32F407"
select MACH_STM32F4
select MACH_STM32F4x5
config MACH_STM32F429
bool "STM32F429"
select MACH_STM32F4
select MACH_STM32F4x5
config MACH_STM32F446
bool "STM32F446"
select MACH_STM32F4
config MACH_STM32F042
bool "STM32F042"
select MACH_STM32F0
config MACH_STM32F070
bool "STM32F070"
select MACH_STM32F0x2
config MACH_STM32F072
bool "STM32F072"
select MACH_STM32F0
select MACH_STM32F0x2
endchoice
config MACH_STM32F0
@ -43,11 +52,12 @@ config MACH_STM32F4
config MCU
string
default "stm32f042x6" if MACH_STM32F042
default "stm32f070xb" if MACH_STM32F070
default "stm32f072xb" if MACH_STM32F072
default "stm32f103xe" if MACH_STM32F103
default "stm32f207xx" if MACH_STM32F207
default "stm32f405xx" if MACH_STM32F405
default "stm32f407xx" if MACH_STM32F407
default "stm32f429xx" if MACH_STM32F429
default "stm32f446xx" if MACH_STM32F446
config CLOCK_FREQ
@ -56,13 +66,13 @@ config CLOCK_FREQ
default 64000000 if MACH_STM32F103 && STM32_CLOCK_REF_INTERNAL
default 72000000 if MACH_STM32F103
default 120000000 if MACH_STM32F207
default 168000000 if MACH_STM32F405 || MACH_STM32F407
default 168000000 if MACH_STM32F4x5
default 180000000 if MACH_STM32F446
config FLASH_SIZE
hex
default 0x8000 if MACH_STM32F042
default 0x20000 if MACH_STM32F070
default 0x20000 if MACH_STM32F072
default 0x10000 if MACH_STM32F103 # Flash size of stm32f103x8 (64KiB)
default 0x40000 if MACH_STM32F2
default 0x80000 if MACH_STM32F4
@ -74,7 +84,7 @@ config RAM_START
config RAM_SIZE
hex
default 0x1800 if MACH_STM32F042
default 0x4000 if MACH_STM32F070
default 0x4000 if MACH_STM32F072
default 0x5000 if MACH_STM32F103 # Ram size of stm32f103x8 (20KiB)
default 0x20000 if MACH_STM32F207
default 0x20000 if MACH_STM32F4
@ -97,6 +107,16 @@ config ARMCM_RAM_VECTORTABLE
default y if MACH_STM32F0 && FLASH_START != 0x8000000
default n
config STM32F103GD_DISABLE_SWD
bool "Disable SWD at startup (for GigaDevice stm32f103 clones)"
depends on MACH_STM32F103
default n
help
The GigaDevice clone of the STM32F103 may not be able to
reliably disable SWD at run-time. This can prevent the PA13
and PA14 pins from being available. Selecting this option
disables SWD at startup and thus makes these pins available.
choice
prompt "Clock Reference"
config STM32_CLOCK_REF_8M
@ -105,11 +125,14 @@ choice
bool "12 MHz crystal"
config STM32_CLOCK_REF_16M
bool "16 MHz crystal"
config STM32_CLOCK_REF_25M
bool "25 MHz crystal"
config STM32_CLOCK_REF_INTERNAL
bool "Internal clock"
endchoice
config CLOCK_REF_FREQ
int
default 25000000 if STM32_CLOCK_REF_25M
default 16000000 if STM32_CLOCK_REF_16M
default 12000000 if STM32_CLOCK_REF_12M
default 1 if STM32_CLOCK_REF_INTERNAL
@ -117,16 +140,20 @@ config CLOCK_REF_FREQ
choice
prompt "CAN pins"
config CAN_PINS_PA11_PA12
config STM32_CANBUS_PA11_PA12
bool "Pins PA11(rx) and PA12(tx)"
config CAN_PINS_PB8_PB9
config STM32_CANBUS_PA11_PA12_REMAP
bool "Pins on PA9(rx) and PA10(tx)" if MACH_STM32F042
config STM32_CANBUS_PB8_PB9
bool "Pins PB8(rx) and PB9(tx)"
config CAN_PINS_PI8_PH13
bool "Pins PI8(rx) and PH13(tx)" if MACH_STM32F4
config CAN_PINS_PB5_PB6
config STM32_CANBUS_PI9_PH13
bool "Pins PI9(rx) and PH13(tx)" if MACH_STM32F4
config STM32_CANBUS_PB5_PB6
bool "Pins PB5(rx) and PB6(tx)" if MACH_STM32F4
config CAN_PINS_PB12_PB13
config STM32_CANBUS_PB12_PB13
bool "Pins PB12(rx) and PB13(tx)" if MACH_STM32F4
config STM32_CANBUS_PD0_PD1
bool "Pins PD0(rx) and PD1(tx)"
endchoice
config STM32F0_TRIM
@ -138,14 +165,6 @@ config STM32F0_TRIM
Default is 16 (use factory default). Each increment increases
the clock rate by ~240KHz.
config STM32F042_PIN_SWAP
bool "Use PA9/PA10 for USB or CAN" if MACH_STM32F042
depends on MACH_STM32F042
default y if MACH_STM32F042
default n
help
Remaps logical pins PA11/PA12 to physical PA9/PA10 on low pincount F042 devices.
choice
depends on MACH_STM32F103
prompt "Flash Page Size"

View File

@ -24,19 +24,22 @@ CFLAGS_canboot.elf += -T $(OUT)src/generic/armcm_link.ld
$(OUT)canboot.elf: $(OUT)src/generic/armcm_link.ld
# Add source files
src-y += stm32/gpio.c stm32/flash.c
src-y += stm32/gpio.c stm32/flash.c stm32/clockline.c
src-y += generic/armcm_boot.c generic/armcm_irq.c
src-$(CONFIG_MACH_STM32F0) += ../lib/stm32f0/system_stm32f0xx.c
src-$(CONFIG_MACH_STM32F0) += stm32/stm32f0.c stm32/stm32f0_timer.c
src-$(CONFIG_MACH_STM32F0) += stm32/gpioperiph.c
src-$(CONFIG_MACH_STM32F1) += ../lib/stm32f1/system_stm32f1xx.c
src-$(CONFIG_MACH_STM32F1) += stm32/stm32f1.c generic/armcm_timer.c
src-$(CONFIG_MACH_STM32F2) += ../lib/stm32f2/system_stm32f2xx.c
src-$(CONFIG_MACH_STM32F2) += stm32/stm32f4.c generic/armcm_timer.c
src-$(CONFIG_MACH_STM32F2) += stm32/gpioperiph.c
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
can-src-$(CONFIG_CANSERIAL) += stm32/can.c ../lib/fast-hash/fasthash.c
src-$(CONFIG_CANSERIAL) += $(can-src-y) generic/canbus.c

View File

@ -15,31 +15,36 @@
#include "internal.h" // enable_pclock
#include "ctr.h" // DECL_CONSTANT_STR
#if CONFIG_CAN_PINS_PA11_PA12
#if CONFIG_STM32_CANBUS_PA11_PA12 || CONFIG_STM32_CANBUS_PA11_PA12_REMAP
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PA11,PA12");
#define GPIO_Rx GPIO('A', 11)
#define GPIO_Tx GPIO('A', 12)
#endif
#if CONFIG_CAN_PINS_PB8_PB9
#if CONFIG_STM32_CANBUS_PB8_PB9
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB8,PB9");
#define GPIO_Rx GPIO('B', 8)
#define GPIO_Tx GPIO('B', 9)
#endif
#if CONFIG_CAN_PINS_PI8_PH13
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PI8,PH13");
#define GPIO_Rx GPIO('I', 8)
#if CONFIG_STM32_CANBUS_PI9_PH13
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PI9,PH13");
#define GPIO_Rx GPIO('I', 9)
#define GPIO_Tx GPIO('H', 13)
#endif
#if CONFIG_CAN_PINS_PB5_PB6
#if CONFIG_STM32_CANBUS_PB5_PB6
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB5,PB6");
#define GPIO_Rx GPIO('B', 5)
#define GPIO_Tx GPIO('B', 6)
#endif
#if CONFIG_CAN_PINS_PB12_PB13
#if CONFIG_STM32_CANBUS_PB12_PB13
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PB12,PB13");
#define GPIO_Rx GPIO('B', 12)
#define GPIO_Tx GPIO('B', 13)
#endif
#if CONFIG_STM32_CANBUS_PD0_PD1
DECL_CONSTANT_STR("RESERVE_PINS_CAN", "PD0,PD1");
#define GPIO_Rx GPIO('D', 0)
#define GPIO_Tx GPIO('D', 1)
#endif
#if CONFIG_MACH_STM32F0
#define SOC_CAN CAN
@ -61,14 +66,14 @@
#if CONFIG_MACH_STM32F4
#warning CAN on STM32F4 is untested
#if (CONFIG_CAN_PINS_PA11_PA12 || CONFIG_CAN_PINS_PB8_PB9 \
|| CONFIG_CAN_PINS_PI8_PH13)
#if (CONFIG_STM32_CANBUS_PA11_PA12 || CONFIG_STM32_CANBUS_PB8_PB9 \
|| CONFIG_STM32_CANBUS_PD0_PD1 || CONFIG_STM32_CANBUS_PI9_PH13)
#define SOC_CAN CAN1
#define CAN_RX0_IRQn CAN1_RX0_IRQn
#define CAN_RX1_IRQn CAN1_RX1_IRQn
#define CAN_TX_IRQn CAN1_TX_IRQn
#define CAN_SCE_IRQn CAN1_SCE_IRQn
#elif CONFIG_CAN_PINS_PB5_PB6 || CONFIG_CAN_PINS_PB12_PB13
#elif CONFIG_STM32_CANBUS_PB5_PB6 || CONFIG_STM32_CANBUS_PB12_PB13
#define SOC_CAN CAN2
#define CAN_RX0_IRQn CAN2_RX0_IRQn
#define CAN_RX1_IRQn CAN2_RX1_IRQn
@ -152,62 +157,64 @@ canbus_send(uint32_t id, uint32_t len, uint8_t *data)
return len;
}
#define CAN_FILTER_NUMBER 0
// Setup the receive packet filter
static void
can_set_filter(uint32_t id1, uint32_t id2)
void
canbus_set_filter(uint32_t id)
{
uint32_t filternbrbitpos = 1 << CAN_FILTER_NUMBER;
/* Select the start slave bank */
SOC_CAN->FMR |= CAN_FMR_FINIT;
/* Initialisation mode for the filter */
SOC_CAN->FA1R = 0;
SOC_CAN->sFilterRegister[CAN_FILTER_NUMBER].FR1 = id1 << (5 + 16);
SOC_CAN->sFilterRegister[CAN_FILTER_NUMBER].FR2 = id2 << (5 + 16);
uint32_t mask = CAN_RI0R_STID | CAN_TI0R_IDE | CAN_TI0R_RTR;
SOC_CAN->sFilterRegister[0].FR1 = CANBUS_ID_ADMIN << CAN_RI0R_STID_Pos;
SOC_CAN->sFilterRegister[0].FR2 = mask;
SOC_CAN->sFilterRegister[1].FR1 = (id + 1) << CAN_RI0R_STID_Pos;
SOC_CAN->sFilterRegister[1].FR2 = mask;
SOC_CAN->sFilterRegister[2].FR1 = id << CAN_RI0R_STID_Pos;
SOC_CAN->sFilterRegister[2].FR2 = mask;
/* Identifier list mode for the filter */
SOC_CAN->FM1R = filternbrbitpos;
/* 32-bit scale for the filter */
SOC_CAN->FS1R = filternbrbitpos;
SOC_CAN->FS1R = (1<<0) | (1<<1) | (1<<2);
/* FIFO 0 assigned for the filter */
SOC_CAN->FFA1R = 0;
/* FIFO 1 assigned to 'id' */
SOC_CAN->FFA1R = (1<<2);
/* Filter activation */
SOC_CAN->FA1R = filternbrbitpos;
SOC_CAN->FA1R = (1<<0) | (id ? (1<<1) | (1<<2) : 0);
/* Leave the initialisation mode for the filter */
SOC_CAN->FMR &= ~CAN_FMR_FINIT;
}
void
canbus_set_dataport(uint32_t id)
{
can_set_filter(CANBUS_ID_UUID, id);
}
void
canbus_reboot(void)
{
NVIC_SystemReset();
}
// This function handles CAN global interrupts
void
CAN_IRQHandler(void)
{
if (SOC_CAN->RF0R & CAN_RF0R_FMP0) {
// Rx
SOC_CAN->IER &= ~CAN_IER_FMPIE0;
canbus_notify_rx();
if (SOC_CAN->RF1R & CAN_RF1R_FMP1) {
// Read and ack data packet
CAN_FIFOMailBox_TypeDef *mb = &SOC_CAN->sFIFOMailBox[1];
uint32_t rir_id = (mb->RIR >> CAN_RI0R_STID_Pos) & 0x7FF;
uint32_t dlc = mb->RDTR & CAN_RDT0R_DLC;
uint32_t rdlr = mb->RDLR, rdhr = mb->RDHR;
SOC_CAN->RF1R = CAN_RF1R_RFOM1;
// Process packet
union {
struct { uint32_t rdlr, rdhr; };
uint8_t data[8];
} rdata = { .rdlr = rdlr, .rdhr = rdhr };
canbus_process_data(rir_id, dlc, rdata.data);
}
uint32_t ier = SOC_CAN->IER;
if (ier & CAN_IER_FMPIE0 && SOC_CAN->RF0R & CAN_RF0R_FMP0) {
// Admin Rx
SOC_CAN->IER = ier = ier & ~CAN_IER_FMPIE0;
canbus_notify_rx();
}
if (ier & CAN_IER_TMEIE
&& SOC_CAN->TSR & (CAN_TSR_RQCP0|CAN_TSR_RQCP1|CAN_TSR_RQCP2)) {
// Tx
SOC_CAN->IER &= ~CAN_IER_TMEIE;
SOC_CAN->IER = ier & ~CAN_IER_TMEIE;
canbus_notify_tx();
}
}
@ -260,6 +267,12 @@ compute_btr(uint32_t pclock, uint32_t bitrate)
return make_btr(sjw, time_seg1, time_seg2, brp);
}
void
canbus_reboot(void)
{
NVIC_SystemReset();
}
void
can_init(void)
{
@ -270,7 +283,7 @@ can_init(void)
uint32_t pclock = get_pclock_frequency((uint32_t)SOC_CAN);
uint32_t btr = compute_btr(pclock, CONFIG_CANBUS_BAUD);
uint32_t btr = compute_btr(pclock, CONFIG_CANBUS_FREQUENCY);
/*##-1- Configure the CAN #######################################*/
@ -290,17 +303,15 @@ can_init(void)
;
/*##-2- Configure the CAN Filter #######################################*/
can_set_filter(CANBUS_ID_UUID, CANBUS_ID_SET);
canbus_set_filter(0);
/*##-3- Configure Interrupts #################################*/
SOC_CAN->IER = CAN_IER_FMPIE0; // RX mailbox IRQ
armcm_enable_irq(CAN_IRQHandler, CAN_RX0_IRQn, 0);
if (CAN_RX0_IRQn != CAN_RX1_IRQn)
armcm_enable_irq(CAN_IRQHandler, CAN_RX1_IRQn, 0);
if (CAN_RX0_IRQn != CAN_TX_IRQn)
armcm_enable_irq(CAN_IRQHandler, CAN_TX_IRQn, 0);
SOC_CAN->IER = CAN_IER_FMPIE1;
// Convert unique 96-bit chip id into 48 bit representation
uint64_t hash = fasthash64((uint8_t*)UID_BASE, 12, 0xA16231A7);

32
src/stm32/clockline.c Normal file
View File

@ -0,0 +1,32 @@
// Code to enable clock lines on stm32
//
// Copyright (C) 2021 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "board/irq.h" // irq_save
#include "internal.h" // struct cline
// Enable a peripheral clock
void
enable_pclock(uint32_t periph_base)
{
struct cline cl = lookup_clock_line(periph_base);
irqstatus_t flag = irq_save();
*cl.en |= cl.bit;
*cl.en; // Pause 2 cycles to ensure peripheral is enabled
if (cl.rst) {
// Reset peripheral
*cl.rst = cl.bit;
*cl.rst = 0;
}
irq_restore(flag);
}
// Check if a peripheral clock has been enabled
int
is_enabled_pclock(uint32_t periph_base)
{
struct cline cl = lookup_clock_line(periph_base);
return *cl.en & cl.bit;
}

38
src/stm32/gpioperiph.c Normal file
View File

@ -0,0 +1,38 @@
// Code to setup gpio on stm32 chip (except for stm32f1)
//
// Copyright (C) 2019-2021 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "internal.h" // gpio_peripheral
// Set the mode and extended function of a pin
void
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
{
GPIO_TypeDef *regs = digital_regs[GPIO2PORT(gpio)];
// Enable GPIO clock
gpio_clock_enable(regs);
// Configure GPIO
uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf, od = mode >> 8;
uint32_t pup = pullup ? (pullup > 0 ? 1 : 2) : 0;
uint32_t pos = gpio % 16, af_reg = pos / 8;
uint32_t af_shift = (pos % 8) * 4, af_msk = 0x0f << af_shift;
uint32_t m_shift = pos * 2, m_msk = 0x03 << m_shift;
regs->AFR[af_reg] = (regs->AFR[af_reg] & ~af_msk) | (func << af_shift);
regs->MODER = (regs->MODER & ~m_msk) | (mode_bits << m_shift);
regs->PUPDR = (regs->PUPDR & ~m_msk) | (pup << m_shift);
regs->OTYPER = (regs->OTYPER & ~(1 << pos)) | (od << pos);
// Setup OSPEEDR:
// stm32f0 is ~10Mhz at 50pF
// stm32f2 is ~25Mhz at 40pF
// stm32f4 is ~50Mhz at 40pF
// stm32g0 is ~30Mhz at 50pF
// stm32h7 is ~85Mhz at 50pF
uint32_t ospeed = CONFIG_MACH_STM32F0 ? 0x01 : 0x02;
regs->OSPEEDR = (regs->OSPEEDR & ~m_msk) | (ospeed << m_shift);
}

View File

@ -12,24 +12,34 @@
#include "stm32f2xx.h"
#elif CONFIG_MACH_STM32F4
#include "stm32f4xx.h"
#elif CONFIG_MACH_STM32G0
#include "stm32g0xx.h"
#elif CONFIG_MACH_STM32H7
#include "stm32h7xx.h"
#endif
// gpio.c
extern GPIO_TypeDef * const digital_regs[];
#define GPIO(PORT, NUM) (((PORT)-'A') * 16 + (NUM))
#define GPIO2PORT(PIN) ((PIN) / 16)
#define GPIO2BIT(PIN) (1<<((PIN) % 16))
// gpioperiph.c
#define GPIO_INPUT 0
#define GPIO_OUTPUT 1
#define GPIO_OPEN_DRAIN 0x100
#define GPIO_FUNCTION(fn) (2 | ((fn) << 4))
#define GPIO_ANALOG 3
void enable_pclock(uint32_t periph_base);
int is_enabled_pclock(uint32_t periph_base);
uint32_t get_pclock_frequency(uint32_t periph_base);
void gpio_clock_enable(GPIO_TypeDef *regs);
void gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup);
// clockline.c
void enable_pclock(uint32_t periph_base);
int is_enabled_pclock(uint32_t periph_base);
// stm32??.c
struct cline { volatile uint32_t *en, *rst; uint32_t bit; };
struct cline lookup_clock_line(uint32_t periph_base);
uint32_t get_pclock_frequency(uint32_t periph_base);
void gpio_clock_enable(GPIO_TypeDef *regs);
#endif // internal.h

View File

@ -1,6 +1,6 @@
// Code to setup clocks and gpio on stm32f0
// Code to setup clocks on stm32f0
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2019-2021 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@ -13,38 +13,19 @@
#define FREQ_PERIPH 48000000
// Enable a peripheral clock
void
enable_pclock(uint32_t periph_base)
// Map a peripheral address to its enable bits
struct cline
lookup_clock_line(uint32_t periph_base)
{
if (periph_base < SYSCFG_BASE) {
uint32_t pos = (periph_base - APBPERIPH_BASE) / 0x400;
RCC->APB1ENR |= 1 << pos;
RCC->APB1ENR;
} else if (periph_base < AHBPERIPH_BASE) {
uint32_t pos = (periph_base - SYSCFG_BASE) / 0x400;
RCC->APB2ENR |= 1 << pos;
RCC->APB2ENR;
if (periph_base >= AHB2PERIPH_BASE) {
uint32_t bit = 1 << ((periph_base - AHB2PERIPH_BASE) / 0x400 + 17);
return (struct cline){.en=&RCC->AHBENR, .rst=&RCC->AHBRSTR, .bit=bit};
} else if (periph_base >= SYSCFG_BASE) {
uint32_t bit = 1 << ((periph_base - SYSCFG_BASE) / 0x400);
return (struct cline){.en=&RCC->APB2ENR, .rst=&RCC->APB2RSTR, .bit=bit};
} else {
uint32_t pos = (periph_base - AHB2PERIPH_BASE) / 0x400;
RCC->AHBENR |= 1 << (pos + 17);
RCC->AHBENR;
}
}
// Check if a peripheral clock has been enabled
int
is_enabled_pclock(uint32_t periph_base)
{
if (periph_base < SYSCFG_BASE) {
uint32_t pos = (periph_base - APBPERIPH_BASE) / 0x400;
return RCC->APB1ENR & (1 << pos);
} else if (periph_base < AHBPERIPH_BASE) {
uint32_t pos = (periph_base - SYSCFG_BASE) / 0x400;
return RCC->APB2ENR & (1 << pos);
} else {
uint32_t pos = (periph_base - AHB2PERIPH_BASE) / 0x400;
return RCC->AHBENR & (1 << (pos + 17));
uint32_t bit = 1 << ((periph_base - APBPERIPH_BASE) / 0x400);
return (struct cline){.en=&RCC->APB1ENR, .rst=&RCC->APB1RSTR, .bit=bit};
}
}
@ -64,41 +45,6 @@ gpio_clock_enable(GPIO_TypeDef *regs)
RCC->AHBENR;
}
// Set the mode and extended function of a pin
void
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
{
GPIO_TypeDef *regs = digital_regs[GPIO2PORT(gpio)];
// Enable GPIO clock
gpio_clock_enable(regs);
// Configure GPIO
uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf, od = mode >> 8;
uint32_t pup = pullup ? (pullup > 0 ? 1 : 2) : 0;
uint32_t pos = gpio % 16, af_reg = pos / 8;
uint32_t af_shift = (pos % 8) * 4, af_msk = 0x0f << af_shift;
uint32_t m_shift = pos * 2, m_msk = 0x03 << m_shift;
regs->AFR[af_reg] = (regs->AFR[af_reg] & ~af_msk) | (func << af_shift);
regs->MODER = (regs->MODER & ~m_msk) | (mode_bits << m_shift);
regs->PUPDR = (regs->PUPDR & ~m_msk) | (pup << m_shift);
regs->OTYPER = (regs->OTYPER & ~(1 << pos)) | (od << pos);
regs->OSPEEDR = (regs->OSPEEDR & ~m_msk) | (0x02 << m_shift);
}
#define USB_BOOT_FLAG_ADDR (CONFIG_RAM_START + CONFIG_RAM_SIZE - 1024)
#define USB_BOOT_FLAG 0x55534220424f4f54 // "USB BOOT"
// Handle USB reboot requests
void
usb_request_bootloader(void)
{
irq_disable();
*(uint64_t*)USB_BOOT_FLAG_ADDR = USB_BOOT_FLAG;
NVIC_SystemReset();
}
// Configure and enable the PLL as clock source
static void
pll_setup(void)
@ -160,7 +106,7 @@ armcm_main(void)
// Support pin remapping USB/CAN pins on low pinout stm32f042
#ifdef SYSCFG_CFGR1_PA11_PA12_RMP
if (CONFIG_STM32F042_PIN_SWAP) {
if (CONFIG_STM32_CANBUS_PA11_PA12_REMAP) {
enable_pclock(SYSCFG_BASE);
SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP;
}

View File

@ -1,6 +1,6 @@
// Code to setup clocks and gpio on stm32f1
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2019-2021 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@ -13,38 +13,19 @@
#define FREQ_PERIPH (CONFIG_CLOCK_FREQ / 2)
// Enable a peripheral clock
void
enable_pclock(uint32_t periph_base)
// 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;
RCC->APB1ENR |= (1<<pos);
RCC->APB1ENR;
} else if (periph_base < AHBPERIPH_BASE) {
uint32_t pos = (periph_base - APB2PERIPH_BASE) / 0x400;
RCC->APB2ENR |= (1<<pos);
RCC->APB2ENR;
if (periph_base >= AHBPERIPH_BASE) {
uint32_t bit = 1 << ((periph_base - AHBPERIPH_BASE) / 0x400);
return (struct cline){.en=&RCC->AHBENR, .bit=bit};
} else if (periph_base >= APB2PERIPH_BASE) {
uint32_t bit = 1 << ((periph_base - APB2PERIPH_BASE) / 0x400);
return (struct cline){.en=&RCC->APB2ENR, .rst=&RCC->APB2RSTR, .bit=bit};
} else {
uint32_t pos = (periph_base - AHBPERIPH_BASE) / 0x400;
RCC->AHBENR |= (1<<pos);
RCC->AHBENR;
}
}
// Check if a peripheral clock has been enabled
int
is_enabled_pclock(uint32_t periph_base)
{
if (periph_base < APB2PERIPH_BASE) {
uint32_t pos = (periph_base - APB1PERIPH_BASE) / 0x400;
return RCC->APB1ENR & (1<<pos);
} else if (periph_base < AHBPERIPH_BASE) {
uint32_t pos = (periph_base - APB2PERIPH_BASE) / 0x400;
return RCC->APB2ENR & (1<<pos);
} else {
uint32_t pos = (periph_base - AHBPERIPH_BASE) / 0x400;
return RCC->AHBENR & (1<<pos);
uint32_t bit = 1 << ((periph_base - APB1PERIPH_BASE) / 0x400);
return (struct cline){.en=&RCC->APB1ENR, .rst=&RCC->APB1RSTR, .bit=bit};
}
}
@ -64,74 +45,6 @@ gpio_clock_enable(GPIO_TypeDef *regs)
RCC->APB2ENR;
}
static void stm32f1_alternative_remap(uint32_t mapr_mask, uint32_t mapr_value)
{
// The MAPR register is a mix of write only and r/w bits
// We have to save the written values in a global variable
static uint32_t mapr = 0;
mapr &= ~mapr_mask;
mapr |= mapr_value;
AFIO->MAPR = mapr;
}
// Set the mode and extended function of a pin
void
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
{
GPIO_TypeDef *regs = digital_regs[GPIO2PORT(gpio)];
// Enable GPIO clock
gpio_clock_enable(regs);
// Configure GPIO
uint32_t pos = gpio % 16, shift = (pos % 8) * 4, msk = 0xf << shift, cfg;
if (mode == GPIO_INPUT) {
cfg = pullup ? 0x8 : 0x4;
} else if (mode == GPIO_OUTPUT) {
cfg = 0x1;
} else if (mode == (GPIO_OUTPUT | GPIO_OPEN_DRAIN)) {
cfg = 0x5;
} else if (mode == GPIO_ANALOG) {
cfg = 0x0;
} else {
if (mode & GPIO_OPEN_DRAIN)
// Alternate function with open-drain mode
cfg = 0xd;
else if (pullup > 0)
// Alternate function input pins use GPIO_INPUT mode on the stm32f1
cfg = 0x8;
else
cfg = 0x9;
}
if (pos & 0x8)
regs->CRH = (regs->CRH & ~msk) | (cfg << shift);
else
regs->CRL = (regs->CRL & ~msk) | (cfg << shift);
if (pullup > 0)
regs->BSRR = 1 << pos;
else if (pullup < 0)
regs->BSRR = 1 << (pos + 16);
if (gpio == GPIO('A', 13) || gpio == GPIO('A', 14))
// Disable SWD to free PA13, PA14
stm32f1_alternative_remap(AFIO_MAPR_SWJ_CFG_Msk,
AFIO_MAPR_SWJ_CFG_DISABLE);
// STM32F1 remaps functions to pins in a very different
// way from other STM32s.
// Code below is emulating a few mappings to work like an STM32F4
uint32_t func = (mode >> 4) & 0xf;
if(( gpio == GPIO('B', 8) || gpio == GPIO('B', 9)) &&
func == 9) { // CAN
stm32f1_alternative_remap(AFIO_MAPR_CAN_REMAP_Msk,
AFIO_MAPR_CAN_REMAP_REMAP2);
}
// Add more as needed
}
// Main clock setup called at chip startup
static void
clock_setup(void)
@ -140,9 +53,14 @@ clock_setup(void)
uint32_t cfgr;
if (!CONFIG_STM32_CLOCK_REF_INTERNAL) {
// Configure 72Mhz PLL from external crystal (HSE)
uint32_t div = CONFIG_CLOCK_FREQ / CONFIG_CLOCK_REF_FREQ;
RCC->CR |= RCC_CR_HSEON;
cfgr = (1 << RCC_CFGR_PLLSRC_Pos) | ((div - 2) << RCC_CFGR_PLLMULL_Pos);
uint32_t div = CONFIG_CLOCK_FREQ / (CONFIG_CLOCK_REF_FREQ / 2);
cfgr = 1 << RCC_CFGR_PLLSRC_Pos;
if ((div & 1) && div <= 16)
cfgr |= RCC_CFGR_PLLXTPRE_HSE_DIV2;
else
div /= 2;
cfgr |= (div - 2) << RCC_CFGR_PLLMULL_Pos;
} else {
// Configure 72Mhz PLL from internal 8Mhz oscillator (HSI)
uint32_t div2 = (CONFIG_CLOCK_FREQ / 8000000) * 2;
@ -166,6 +84,131 @@ clock_setup(void)
;
}
/****************************************************************
* GPIO setup
****************************************************************/
static void
stm32f1_alternative_remap(uint32_t mapr_mask, uint32_t mapr_value)
{
// The MAPR register is a mix of write only and r/w bits
// We have to save the written values in a global variable
static uint32_t mapr = 0;
mapr &= ~mapr_mask;
mapr |= mapr_value;
AFIO->MAPR = mapr;
}
#define STM_OSPEED 0x1 // ~10Mhz at 50pF
// Set the mode and extended function of a pin
void
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
{
GPIO_TypeDef *regs = digital_regs[GPIO2PORT(gpio)];
// Enable GPIO clock
gpio_clock_enable(regs);
// Configure GPIO
uint32_t pos = gpio % 16, shift = (pos % 8) * 4, msk = 0xf << shift, cfg;
if (mode == GPIO_INPUT) {
cfg = pullup ? 0x8 : 0x4;
} else if (mode == GPIO_OUTPUT) {
cfg = STM_OSPEED;
} else if (mode == (GPIO_OUTPUT | GPIO_OPEN_DRAIN)) {
cfg = 0x4 | STM_OSPEED;
} else if (mode == GPIO_ANALOG) {
cfg = 0x0;
} else {
if (mode & GPIO_OPEN_DRAIN)
// Alternate function with open-drain mode
cfg = 0xc | STM_OSPEED;
else if (pullup > 0)
// Alternate function input pins use GPIO_INPUT mode on the stm32f1
cfg = 0x8;
else
cfg = 0x8 | STM_OSPEED;
}
if (pos & 0x8)
regs->CRH = (regs->CRH & ~msk) | (cfg << shift);
else
regs->CRL = (regs->CRL & ~msk) | (cfg << shift);
if (pullup > 0)
regs->BSRR = 1 << pos;
else if (pullup < 0)
regs->BSRR = 1 << (pos + 16);
if (gpio == GPIO('A', 13) || gpio == GPIO('A', 14))
// Disable SWD to free PA13, PA14
stm32f1_alternative_remap(AFIO_MAPR_SWJ_CFG_Msk,
AFIO_MAPR_SWJ_CFG_DISABLE);
// STM32F1 remaps functions to pins in a very different
// way from other STM32s.
// Code below is emulating a few mappings to work like an STM32F4
uint32_t func = (mode >> 4) & 0xf;
if (func == 1) {
// TIM2
if (gpio == GPIO('A', 15) || gpio == GPIO('B', 3))
stm32f1_alternative_remap(AFIO_MAPR_TIM2_REMAP_Msk,
AFIO_MAPR_TIM2_REMAP_PARTIALREMAP1);
else if (gpio == GPIO('B', 10) || gpio == GPIO('B', 11))
stm32f1_alternative_remap(AFIO_MAPR_TIM2_REMAP_Msk,
AFIO_MAPR_TIM2_REMAP_PARTIALREMAP2);
} else if (func == 2) {
// TIM3 and TIM4
if (gpio == GPIO('B', 4) || gpio == GPIO('B', 5))
stm32f1_alternative_remap(AFIO_MAPR_TIM3_REMAP_Msk,
AFIO_MAPR_TIM3_REMAP_PARTIALREMAP);
else if (gpio == GPIO('C', 6) || gpio == GPIO('C', 7)
|| gpio == GPIO('C', 8) || gpio == GPIO('C', 9))
stm32f1_alternative_remap(AFIO_MAPR_TIM3_REMAP_Msk,
AFIO_MAPR_TIM3_REMAP_FULLREMAP);
else if (gpio == GPIO('D', 12) || gpio == GPIO('D', 13)
|| gpio == GPIO('D', 14) || gpio == GPIO('D', 15))
stm32f1_alternative_remap(AFIO_MAPR_TIM4_REMAP_Msk,
AFIO_MAPR_TIM4_REMAP);
} else if (func == 4) {
// I2C
if (gpio == GPIO('B', 8) || gpio == GPIO('B', 9))
stm32f1_alternative_remap(AFIO_MAPR_I2C1_REMAP_Msk,
AFIO_MAPR_I2C1_REMAP);
} else if (func == 5) {
// SPI
if (gpio == GPIO('B', 3) || gpio == GPIO('B', 4)
|| gpio == GPIO('B', 5))
stm32f1_alternative_remap(AFIO_MAPR_SPI1_REMAP_Msk,
AFIO_MAPR_SPI1_REMAP);
} else if (func == 7) {
// USART
if (gpio == GPIO('B', 6) || gpio == GPIO('B', 7))
stm32f1_alternative_remap(AFIO_MAPR_USART1_REMAP_Msk,
AFIO_MAPR_USART1_REMAP);
else if (gpio == GPIO('D', 5) || gpio == GPIO('D', 6))
stm32f1_alternative_remap(AFIO_MAPR_USART2_REMAP_Msk,
AFIO_MAPR_USART2_REMAP);
else if (gpio == GPIO('D', 8) || gpio == GPIO('D', 9))
stm32f1_alternative_remap(AFIO_MAPR_USART3_REMAP_Msk,
AFIO_MAPR_USART3_REMAP_FULLREMAP);
} else if (func == 9) {
// CAN
if (gpio == GPIO('B', 8) || gpio == GPIO('B', 9))
stm32f1_alternative_remap(AFIO_MAPR_CAN_REMAP_Msk,
AFIO_MAPR_CAN_REMAP_REMAP2);
if (gpio == GPIO('D', 0) || gpio == GPIO('D', 1))
stm32f1_alternative_remap(AFIO_MAPR_CAN_REMAP_Msk,
AFIO_MAPR_CAN_REMAP_REMAP3);
}
}
/****************************************************************
* Startup
****************************************************************/
// Main entry point - called from armcm_boot.c:ResetHandler()
void
armcm_main(void)
@ -174,7 +217,7 @@ armcm_main(void)
SystemInit();
SCB->VTOR = (uint32_t)VectorTable;
// Reset peripheral clocks
// Reset peripheral clocks (for some bootloaders that don't)
RCC->AHBENR = 0x14;
RCC->APB1ENR = 0;
RCC->APB2ENR = 0;
@ -184,8 +227,13 @@ armcm_main(void)
// Disable JTAG to free PA15, PB3, PB4
enable_pclock(AFIO_BASE);
stm32f1_alternative_remap(AFIO_MAPR_SWJ_CFG_Msk,
AFIO_MAPR_SWJ_CFG_JTAGDISABLE);
if (CONFIG_STM32F103GD_DISABLE_SWD)
// GigaDevice clone can't enable PA13/PA14 at runtime - enable here
stm32f1_alternative_remap(AFIO_MAPR_SWJ_CFG_Msk,
AFIO_MAPR_SWJ_CFG_DISABLE);
else
stm32f1_alternative_remap(AFIO_MAPR_SWJ_CFG_Msk,
AFIO_MAPR_SWJ_CFG_JTAGDISABLE);
canboot_main();
}

View File

@ -1,6 +1,6 @@
// Code to setup clocks and gpio on stm32f2/stm32f4
// Code to setup clocks on stm32f2/stm32f4
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2019-2021 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
@ -11,45 +11,34 @@
#include "internal.h" // enable_pclock
#include "canboot_main.h" // sched_main
#define FREQ_PERIPH (CONFIG_CLOCK_FREQ / 4)
/****************************************************************
* Clock setup
****************************************************************/
#define FREQ_PERIPH_DIV (CONFIG_MACH_STM32F401 ? 2 : 4)
#define FREQ_PERIPH (CONFIG_CLOCK_FREQ / FREQ_PERIPH_DIV)
#define FREQ_USB 48000000
// Enable a peripheral clock
void
enable_pclock(uint32_t periph_base)
// 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;
RCC->APB1ENR |= (1<<pos);
RCC->APB1ENR;
} else if (periph_base < AHB1PERIPH_BASE) {
uint32_t pos = (periph_base - APB2PERIPH_BASE) / 0x400;
RCC->APB2ENR |= (1<<pos);
RCC->APB2ENR;
} else if (periph_base < AHB2PERIPH_BASE) {
uint32_t pos = (periph_base - AHB1PERIPH_BASE) / 0x400;
RCC->AHB1ENR |= (1<<pos);
RCC->AHB1ENR;
if (periph_base >= AHB1PERIPH_BASE) {
uint32_t bit = 1 << ((periph_base - AHB1PERIPH_BASE) / 0x400);
return (struct cline){.en=&RCC->AHB1ENR, .rst=&RCC->AHB1RSTR, .bit=bit};
} else if (periph_base >= APB2PERIPH_BASE) {
uint32_t bit = 1 << ((periph_base - APB2PERIPH_BASE) / 0x400);
if (bit & 0x700)
// Skip ADC peripheral reset as they share a bit
return (struct cline){.en=&RCC->APB2ENR, .bit=bit};
return (struct cline){.en=&RCC->APB2ENR, .rst=&RCC->APB2RSTR, .bit=bit};
} else {
uint32_t bit = 1 << ((periph_base - APB1PERIPH_BASE) / 0x400);
return (struct cline){.en=&RCC->APB1ENR, .rst=&RCC->APB1RSTR, .bit=bit};
}
}
// Check if a peripheral clock has been enabled
int
is_enabled_pclock(uint32_t periph_base)
{
if (periph_base < APB2PERIPH_BASE) {
uint32_t pos = (periph_base - APB1PERIPH_BASE) / 0x400;
return RCC->APB1ENR & (1<<pos);
} else if (periph_base < AHB1PERIPH_BASE) {
uint32_t pos = (periph_base - APB2PERIPH_BASE) / 0x400;
return RCC->APB2ENR & (1<<pos);
} else if (periph_base < AHB2PERIPH_BASE) {
uint32_t pos = (periph_base - AHB1PERIPH_BASE) / 0x400;
return RCC->AHB1ENR & (1<<pos);
}
return 0;
}
// Return the frequency of the given peripheral clock
uint32_t
get_pclock_frequency(uint32_t periph_base)
@ -66,52 +55,6 @@ gpio_clock_enable(GPIO_TypeDef *regs)
RCC->AHB1ENR;
}
// Set the mode and extended function of a pin
void
gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
{
GPIO_TypeDef *regs = digital_regs[GPIO2PORT(gpio)];
// Enable GPIO clock
gpio_clock_enable(regs);
// Configure GPIO
uint32_t mode_bits = mode & 0xf, func = (mode >> 4) & 0xf, od = mode >> 8;
uint32_t pup = pullup ? (pullup > 0 ? 1 : 2) : 0;
uint32_t pos = gpio % 16, af_reg = pos / 8;
uint32_t af_shift = (pos % 8) * 4, af_msk = 0x0f << af_shift;
uint32_t m_shift = pos * 2, m_msk = 0x03 << m_shift;
regs->AFR[af_reg] = (regs->AFR[af_reg] & ~af_msk) | (func << af_shift);
regs->MODER = (regs->MODER & ~m_msk) | (mode_bits << m_shift);
regs->PUPDR = (regs->PUPDR & ~m_msk) | (pup << m_shift);
regs->OTYPER = (regs->OTYPER & ~(1 << pos)) | (od << pos);
regs->OSPEEDR = (regs->OSPEEDR & ~m_msk) | (0x02 << m_shift);
}
#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();
if (CONFIG_STM32_FLASH_START_4000) {
// HID Bootloader
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
RCC->APB1ENR;
PWR->CR |= PWR_CR_DBP;
// HID Bootloader magic key
RTC->BKP4R = 0x424C;
PWR->CR &= ~PWR_CR_DBP;
} else {
// System DFU Bootloader
*(uint64_t*)USB_BOOT_FLAG_ADDR = USB_BOOT_FLAG;
}
NVIC_SystemReset();
}
// Clock configuration
static void
enable_clock_stm32f20x(void)
@ -138,8 +81,10 @@ enable_clock_stm32f20x(void)
static void
enable_clock_stm32f40x(void)
{
#if CONFIG_MACH_STM32F405 || CONFIG_MACH_STM32F407
uint32_t pll_base = 2000000, pll_freq = CONFIG_CLOCK_FREQ * 2, pllcfgr;
#if CONFIG_MACH_STM32F401 || CONFIG_MACH_STM32F4x5
uint32_t pll_base = (CONFIG_STM32_CLOCK_REF_25M) ? 1000000 : 2000000;
uint32_t pllp = (CONFIG_MACH_STM32F401) ? 4 : 2;
uint32_t pll_freq = CONFIG_CLOCK_FREQ * pllp, pllcfgr;
if (!CONFIG_STM32_CLOCK_REF_INTERNAL) {
// Configure 168Mhz PLL from external crystal (HSE)
uint32_t div = CONFIG_CLOCK_REF_FREQ / pll_base;
@ -151,7 +96,7 @@ enable_clock_stm32f40x(void)
pllcfgr = RCC_PLLCFGR_PLLSRC_HSI | (div << RCC_PLLCFGR_PLLM_Pos);
}
RCC->PLLCFGR = (pllcfgr | ((pll_freq/pll_base) << RCC_PLLCFGR_PLLN_Pos)
| (0 << RCC_PLLCFGR_PLLP_Pos)
| (((pllp >> 1) - 1) << RCC_PLLCFGR_PLLP_Pos)
| ((pll_freq/FREQ_USB) << RCC_PLLCFGR_PLLQ_Pos));
RCC->CR |= RCC_CR_PLLON;
#endif
@ -213,7 +158,7 @@ clock_setup(void)
// Configure and enable PLL
if (CONFIG_MACH_STM32F207)
enable_clock_stm32f20x();
else if (CONFIG_MACH_STM32F405 || CONFIG_MACH_STM32F407)
else if (CONFIG_MACH_STM32F4x5)
enable_clock_stm32f40x();
else
enable_clock_stm32f446();
@ -227,26 +172,32 @@ clock_setup(void)
;
// Switch system clock to PLL
RCC->CFGR = RCC_CFGR_PPRE1_DIV4 | RCC_CFGR_PPRE2_DIV4 | RCC_CFGR_SW_PLL;
if (FREQ_PERIPH_DIV == 2)
RCC->CFGR = RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_SW_PLL;
else
RCC->CFGR = RCC_CFGR_PPRE1_DIV4 | RCC_CFGR_PPRE2_DIV4 | RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL)
;
}
/****************************************************************
* Startup
****************************************************************/
// 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;
// Reset peripheral clocks (for some bootloaders that don't)
RCC->AHB1ENR = 0x38000;
RCC->AHB2ENR = 0;
RCC->APB1ENR = 0;
RCC->APB2ENR = 0;
clock_setup();
sched_main();