altos/stm32f4: Start adding support for STM32F413
authorKeith Packard <keithp@keithp.com>
Tue, 11 Sep 2018 07:07:38 +0000 (00:07 -0700)
committerKeith Packard <keithp@keithp.com>
Wed, 3 Oct 2018 21:51:57 +0000 (14:51 -0700)
Enough to get clocks lit up at least.

Signed-off-by: Keith Packard <keithp@keithp.com>
13 files changed:
src/stm32f4/Makefile-flash.defs [new file with mode: 0644]
src/stm32f4/Makefile-raw.defs [new file with mode: 0644]
src/stm32f4/Makefile-stm32f4.defs [new file with mode: 0644]
src/stm32f4/Makefile.defs [new file with mode: 0644]
src/stm32f4/altos-loader.ld [new file with mode: 0644]
src/stm32f4/altos-raw.ld [new file with mode: 0644]
src/stm32f4/altos.ld [new file with mode: 0644]
src/stm32f4/ao_arch.h [new file with mode: 0644]
src/stm32f4/ao_arch_funcs.h [new file with mode: 0644]
src/stm32f4/ao_interrupt.c [new file with mode: 0644]
src/stm32f4/ao_timer.c [new file with mode: 0644]
src/stm32f4/registers.ld [new file with mode: 0644]
src/stm32f4/stm32f4.h [new file with mode: 0644]

diff --git a/src/stm32f4/Makefile-flash.defs b/src/stm32f4/Makefile-flash.defs
new file mode 100644 (file)
index 0000000..1a2aa75
--- /dev/null
@@ -0,0 +1,68 @@
+include $(TOPDIR)/stm32f4/Makefile-stm32f4.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_flash_pins.h \
+       ao_flash_stm_pins.h \
+       ao_flash_task.h \
+       ao_pins.h \
+       ao_product.h \
+       Makefile
+
+#
+# Common AltOS sources
+#
+SRC = \
+       ao_interrupt.c \
+       ao_romconfig.c \
+       ao_boot_chain.c \
+       ao_boot_pin.c \
+       ao_product.c \
+       ao_notask.c \
+       ao_timer.c \
+       ao_usb_stm.c \
+       ao_flash_stm.c \
+       ao_flash_task.c \
+       ao_flash_loader_stm.c
+
+OBJ=$(SRC:.c=.o)
+
+PRODUCT=AltosFlash
+PRODUCT_DEF=-DALTOS_FLASH
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STM32F4_CFLAGS) -g -Os
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f4 -Wl,-Taltos-loader.ld
+
+PROGNAME=altos-flash
+PROG=$(HARDWARE)-$(PROGNAME)-$(VERSION).elf
+BIN=$(HARDWARE)-$(PROGNAME)-$(VERSION).bin
+
+MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
+FLASH_ADDR=0x08000000
+
+all: $(PROG) $(BIN)
+
+$(PROG): Makefile $(OBJ) altos-loader.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(BIN): $(PROG)
+       $(MAKEBIN) --output=$@ --base=$(FLASH_ADDR) $(PROG)
+       
+ao_product.h: ao-make-product.5c $(TOPDIR)/Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+$(OBJ): $(INC)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(HARDWARE)-$(PROGNAME)-*.elf $(HARDWARE)-$(PROGNAME)-*.bin
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/stm32f4/Makefile-raw.defs b/src/stm32f4/Makefile-raw.defs
new file mode 100644 (file)
index 0000000..03d92e4
--- /dev/null
@@ -0,0 +1,13 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stm32f4/Makefile-stm32f4.defs
+
+LOADER=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
+FLASH_ADDR=0x08000000
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f4 -Wl,-Taltos-raw.ld -n
+
+.DEFAULT_GOAL=all
diff --git a/src/stm32f4/Makefile-stm32f4.defs b/src/stm32f4/Makefile-stm32f4.defs
new file mode 100644 (file)
index 0000000..4346b7d
--- /dev/null
@@ -0,0 +1,52 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/Makedefs
+
+vpath % $(TOPDIR)/stm32f4:$(TOPDIR)/product:$(TOPDIR)/drivers:$(TOPDIR)/kernel:$(TOPDIR)/util:$(TOPDIR)/kalman:$(TOPDIR)/aes:$(TOPDIR):$(TOPDIR)/math
+vpath make-altitude $(TOPDIR)/util
+vpath make-kalman $(TOPDIR)/util
+vpath kalman.5c $(TOPDIR)/kalman
+vpath kalman_filter.5c $(TOPDIR)/kalman
+vpath load_csv.5c $(TOPDIR)/kalman
+vpath matrix.5c $(TOPDIR)/kalman
+vpath ao-make-product.5c $(TOPDIR)/util
+
+.SUFFIXES: .elf .ihx
+
+.elf.ihx:
+       $(ELFTOHEX) --output=$@ $*.elf
+
+ifndef VERSION
+include $(TOPDIR)/Version
+endif
+
+ELFTOHEX=$(TOPDIR)/../ao-tools/ao-elftohex/ao-elftohex
+CC=$(ARM_CC)
+
+WARN_FLAGS=-Wall -Wextra -Werror -Wcast-align
+
+AO_CFLAGS=-I. -I$(TOPDIR)/stm32f4 -I$(TOPDIR)/kernel -I$(TOPDIR)/drivers \
+       -DNEWLIB_INTEGER_PRINTF_SCANF -Os -g \
+       -I$(TOPDIR)/product -I$(TOPDIR) -I$(TOPDIR)/math \
+       -isystem $(NEWLIB_NANO)/arm-none-eabi/include
+
+STM32F4_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m4 -mthumb \
+       -mfloat-abi=hard -mfpu=fpv4-sp-d16 \
+       -ffreestanding -nostdlib $(AO_CFLAGS) $(WARN_FLAGS)
+
+NICKLE=nickle
+
+LIBS=-L$(NEWLIB_NANO)/arm-none-eabi/lib/thumb/v7e-m/fpv4-sp/hard -lc -lm -lgcc
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+.c.o:
+       $(call quiet,CC) -c $(CFLAGS) -o $@ $<
diff --git a/src/stm32f4/Makefile.defs b/src/stm32f4/Makefile.defs
new file mode 100644 (file)
index 0000000..be185a5
--- /dev/null
@@ -0,0 +1,13 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stm32f4/Makefile-stm32f4.defs
+
+LOADER=flash-loader/$(PROGNAME)-altos-flash-$(VERSION).elf
+MAKEBIN=$(TOPDIR)/../ao-tools/ao-makebin/ao-makebin
+FLASH_ADDR=0x08000000
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32f4 -Wl,-Taltos.ld -n
+
+.DEFAULT_GOAL=all
diff --git a/src/stm32f4/altos-loader.ld b/src/stm32f4/altos-loader.ld
new file mode 100644 (file)
index 0000000..5d6e1f4
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+       rom : ORIGIN = 0x08000000, LENGTH = 4K
+       ram : ORIGIN = 0x20000000, LENGTH = 256K
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+       /*
+        * Rom contents
+        */
+
+       .interrupt : {
+               __text_start__ = .;
+               *(.interrupt)   /* Interrupt vectors */
+       } > rom
+
+       .text ORIGIN(rom) + 0x100 : {
+               ao_romconfig.o(.romconfig*)
+               ao_product.o(.romconfig*)
+
+               *(.text*)       /* Executable code */
+               *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+               *(.rodata*)     /* Constants */
+       } > rom
+       __text_end__ = .;
+
+       /* Boot data which must live at the start of ram so that
+        * the application and bootloader share the same addresses.
+        * This must be all uninitialized data
+        */
+       .boot ORIGIN(ram) + SIZEOF(.interrupt) (NOLOAD) : {
+               __boot_start__ = .;
+               *(.boot)
+               __boot_end__ = .;
+       } >ram
+
+       /* Functions placed in RAM (required for flashing)
+        *
+        * Align to 8 bytes as that's what the ARM likes text
+        * segment alignments to be, and if we don't, then
+        * we end up with a mismatch between the location in
+        * ROM and the desired location in RAM. I don't
+        * entirely understand this, but at least this appears
+        * to work...
+        */
+
+       .textram BLOCK(8): {
+               _start__ = .;
+               __text_ram_start__ = .;
+               *(.ramtext)
+               __text_ram_end = .;
+       } >ram AT>rom
+
+       /* Data -- relocated to RAM, but written to ROM.
+        * also aligned to 8 bytes in case textram is empty
+        */
+       .data BLOCK(8): {
+               *(.data)        /* initialized data */
+               _end__ = .;
+       } >ram AT>rom
+
+
+       .bss : {
+               __bss_start__ = .;
+               *(.bss)
+               *(COMMON)
+               __bss_end__ = .;
+       } >ram
+
+       PROVIDE(__stack__ = ORIGIN(ram) + LENGTH(ram));
+       PROVIDE(end = .);
+}
+
+ENTRY(start);
+
+
diff --git a/src/stm32f4/altos-raw.ld b/src/stm32f4/altos-raw.ld
new file mode 100644 (file)
index 0000000..21da5af
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+       rom (rx) :   ORIGIN = 0x08000000, LENGTH = 1M
+       ram (!w) :   ORIGIN = 0x20000000, LENGTH = 256k - 256
+       stack (!w) : ORIGIN = 0x20000000 + 256k - 256, LENGTH = 256
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+       /*
+        * Rom contents
+        */
+
+       .interrupt : {
+               __text_start__ = .;
+               *(.interrupt)   /* Interrupt vectors */
+       } > rom
+
+       .text : {
+               *(.text*)       /* Executable code */
+               *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+               *(.rodata*)     /* Constants */
+       } > rom
+       __text_end__ = .;
+
+       /* Data -- relocated to RAM, but written to ROM
+        */
+       .data : {
+               _start__ = .;
+               *(.data)        /* initialized data */
+               . = ALIGN(4);
+               _end__ = .;
+       } >ram AT>rom
+
+       .bss : {
+               __bss_start__ = .;
+               *(.bss)
+               *(COMMON)
+               . = ALIGN(4);
+               __bss_end__ = .;
+       } >ram
+
+       PROVIDE(end = .);
+
+       PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+}
+
+ENTRY(start);
+
+
diff --git a/src/stm32f4/altos.ld b/src/stm32f4/altos.ld
new file mode 100644 (file)
index 0000000..2db0e38
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+MEMORY {
+       rom (rx) :   ORIGIN = 0x08001000, LENGTH = 1M - 4k
+       ram (!w) :   ORIGIN = 0x20000000, LENGTH = 256k - 256
+       stack (!w) : ORIGIN = 0x20000000 + 256k - 256, LENGTH = 256
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+       /*
+        * Rom contents
+        */
+
+       .interrupt ORIGIN(ram) : AT (ORIGIN(rom)) {
+               __interrupt_start__ = .;
+               __interrupt_rom__ = ORIGIN(rom);
+               *(.interrupt)   /* Interrupt vectors */
+               __interrupt_end__ = .;
+       } > ram
+
+       .text ORIGIN(rom) + 0x100 : {
+               __text_start__ = .;
+
+               /* Ick. What I want is to specify the
+                * addresses of some global constants so
+                * that I can find them across versions
+                * of the application. I can't figure out
+                * how to make gnu ld do that, so instead
+                * we just load the two files that include
+                * these defines in the right order here and
+                * expect things to 'just work'. Don't change
+                * the contents of those files, ok?
+                */
+               ao_romconfig.o(.romconfig*)
+               ao_product.o(.romconfig*)
+
+               *(.text*)       /* Executable code */
+       } > rom
+
+       .ARM.exidx : {
+               *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+       } > rom
+
+       .rodata : {
+               *(.rodata*)     /* Constants */
+       } > rom
+
+       __text_end__ = .;
+
+       /* Boot data which must live at the start of ram so that
+        * the application and bootloader share the same addresses.
+        * This must be all uninitialized data
+        */
+       .boot (NOLOAD) : {
+               __boot_start__ = .;
+               *(.boot)
+               . = ALIGN(4);
+               __boot_end__ = .;
+       } >ram
+
+       /* Data -- relocated to RAM, but written to ROM
+        */
+       .data : {
+               _start__ = .;
+               *(.data)        /* initialized data */
+               . = ALIGN(4);
+               _end__ = .;
+       } >ram AT>rom
+
+       .bss : {
+               __bss_start__ = .;
+               *(.bss)
+               *(COMMON)
+               . = ALIGN(4);
+               __bss_end__ = .;
+       } >ram
+
+       PROVIDE(end = .);
+
+       PROVIDE(__stack__ = ORIGIN(stack) + LENGTH(stack));
+}
+
+ENTRY(start);
+
+
diff --git a/src/stm32f4/ao_arch.h b/src/stm32f4/ao_arch.h
new file mode 100644 (file)
index 0000000..805d756
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ */
+
+#ifndef _AO_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stdio.h>
+#include <stm32f4.h>
+
+#ifndef AO_STACK_SIZE
+#define AO_STACK_SIZE  512
+#endif
+
+#define AO_PORT_TYPE   uint16_t
+
+#define ao_arch_nop()          asm("nop")
+
+#define ao_arch_task_members\
+       uint32_t *sp;                   /* saved stack pointer */
+
+#define ao_arch_block_interrupts()     asm("cpsid i")
+#define ao_arch_release_interrupts()   asm("cpsie i")
+
+#define ao_arch_naked_declare  __attribute__((naked))
+#define ao_arch_naked_define
+
+/*
+ * ao_timer.c
+ *
+ * We'll generally use the HSE clock through the PLL
+ */
+
+#define AO_STM_NVIC_CLOCK_PRIORITY     0xf0    /* low priority for clock */
+
+#if AO_HSE
+#define AO_PLLSRC      AO_HSE
+#endif
+
+#if AO_HSI
+#define AO_PLLSRC      STM_HSI_FREQ
+#endif
+
+#if AO_PLL_M
+#define AO_PLLIN       (AO_PLLSRC / AO_PLL_M)
+#endif
+
+#if AO_PLL1_N
+
+#define AO_PLL1_VCO    (AO_PLLIN * AO_PLL1_N)
+#define AO_PLL1_CLK_P  (AO_PLL1_VCO / AO_PLL1_P)
+#define AO_SYSCLK      (AO_PLL1_CLK_P)
+
+# if AO_PLL1_Q
+#define AO_PLL1_CLK_Q  (AO_PLL1_VCO / AO_PLL1_Q)
+# endif
+
+#else
+
+#define AO_SYSCLK      AO_PLLSRC
+
+#endif
+
+#if AO_PLL2_N
+
+#define AO_PLL2_VCO    (AO_PLLIN * AO_PLL2_N)
+
+# if AO_PLL2_Q
+#define AO_PLL2_CLK_Q  (AL_PLL2_VCO / AO_PLL2_Q)
+# endif
+
+# if AO_PLL2_R
+#define AO_PLL2_CLK_R  (AL_PLL2_VCO / AO_PLL2_R)
+# endif
+
+#endif
+
+#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
+#define AO_P1CLK       (AO_HCLK / AO_APB1_PRESCALER)
+#if AO_ABP1_PRESCALER == 1
+#define AO_P1_TIMER_CLK        AO_P1CLK
+#else
+#define AO_P1_TIMER_CLK        (AO_P1CLK * 2)
+#endif
+#define AO_P2CLK       (AO_HCLK / AO_APB2_PRESCALER)
+#if AO_ABP2_PRESCALER == 1
+#define AO_P2_TIMER_CLK        AO_P2CLK
+#else
+#define AO_P2_TIMER_CLK        (AO_P2CLK * 2)
+#endif
+#define AO_SYSTICK     (AO_HCLK)
+#define AO_PANIC_DELAY_SCALE  (AO_SYSCLK / 12000000)
+
+#endif /* _AO_ARCH_H_ */
diff --git a/src/stm32f4/ao_arch_funcs.h b/src/stm32f4/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..252fe77
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ */
+
+#ifndef _AO_ARCH_FUNCS_H_
+#define _AO_ARCH_FUNCS_H_
+
+/* GPIO functions */
+
+#define ao_power_register(gpio)
+#define ao_power_unregister(gpio)
+
+static inline void ao_enable_port(struct stm_gpio *port)
+{
+       if ((port) == &stm_gpioa) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPAEN);
+               ao_power_register(&ao_power_gpioa);
+       } else if ((port) == &stm_gpiob) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPBEN);
+               ao_power_register(&ao_power_gpiob);
+       } else if ((port) == &stm_gpioc) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPCEN);
+               ao_power_register(&ao_power_gpioc);
+       } else if ((port) == &stm_gpiod) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPDEN);
+               ao_power_register(&ao_power_gpiod);
+       } else if ((port) == &stm_gpioe) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPEEN);
+               ao_power_register(&ao_power_gpioe);
+       } else if ((port) == &stm_gpiof) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPFEN);
+               ao_power_register(&ao_power_gpiof);
+       } else if ((port) == &stm_gpiog) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPGEN);
+               ao_power_register(&ao_power_gpiog);
+       } else if ((port) == &stm_gpioh) {
+               stm_rcc.ahb1enr |= (1 << STM_RCC_AHB1ENR_IOPHEN);
+               ao_power_register(&ao_power_gpioh);
+       }
+}
+
+static inline void ao_disable_port(struct stm_gpio *port)
+{
+       if ((port) == &stm_gpioa) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPAEN);
+               ao_power_unregister(&ao_power_gpioa);
+       } else if ((port) == &stm_gpiob) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPBEN);
+               ao_power_unregister(&ao_power_gpiob);
+       } else if ((port) == &stm_gpioc) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPCEN);
+               ao_power_unregister(&ao_power_gpioc);
+       } else if ((port) == &stm_gpiod) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPDEN);
+               ao_power_unregister(&ao_power_gpiod);
+       } else if ((port) == &stm_gpioe) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPEEN);
+               ao_power_unregister(&ao_power_gpioe);
+       } else if ((port) == &stm_gpiof) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPFEN);
+               ao_power_unregister(&ao_power_gpiof);
+       } else if ((port) == &stm_gpiog) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPGEN);
+               ao_power_unregister(&ao_power_gpiog);
+       } else if ((port) == &stm_gpioh) {
+               stm_rcc.ahb1enr &= ~(1 << STM_RCC_AHB1ENR_IOPHEN);
+               ao_power_unregister(&ao_power_gpioh);
+       }
+}
+
+#define ao_gpio_set(port, bit, v) stm_gpio_set(port, bit, v)
+
+#define ao_gpio_get(port, bit) stm_gpio_get(port, bit)
+
+#define ao_enable_output(port,bit,v) do {                      \
+               ao_enable_port(port);                           \
+               ao_gpio_set(port, bit, v);                      \
+               stm_moder_set(port, bit, STM_MODER_OUTPUT);\
+       } while (0)
+
+#define ao_gpio_set_mode(port,bit,mode) do {                           \
+               if (mode == AO_EXTI_MODE_PULL_UP)                       \
+                       stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP);    \
+               else if (mode == AO_EXTI_MODE_PULL_DOWN)                \
+                       stm_pupdr_set(port, bit, STM_PUPDR_PULL_DOWN);  \
+               else                                                    \
+                       stm_pupdr_set(port, bit, STM_PUPDR_NONE);       \
+       } while (0)
+
+#define ao_enable_input(port,bit,mode) do {                            \
+               ao_enable_port(port);                                   \
+               stm_moder_set(port, bit, STM_MODER_INPUT);              \
+               ao_gpio_set_mode(port, bit, mode);                      \
+       } while (0)
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/stm32f4/ao_interrupt.c b/src/stm32f4/ao_interrupt.c
new file mode 100644 (file)
index 0000000..a00df54
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include "stm32f4.h"
+#include <string.h>
+#include <ao_boot.h>
+
+extern void main(void);
+extern char __stack__;
+extern char __text_start__, __text_end__;
+extern char _start__, _end__;
+extern char __bss_start__, __bss_end__;
+
+/* Interrupt functions */
+
+void stm_halt_isr(void)
+{
+       ao_panic(AO_PANIC_CRASH);
+}
+
+void stm_ignore_isr(void)
+{
+}
+
+const void *stm_interrupt_vector[];
+
+void start(void)
+{
+#ifdef AO_BOOT_CHAIN
+       if (ao_boot_check_chain()) {
+#ifdef AO_BOOT_PIN
+               ao_boot_check_pin();
+#endif
+       }
+#endif
+       /* Enable FPU */
+       stm_scb.cpacr |= ((STM_SCB_CPACR_FULL << STM_SCB_CPACR_FP0) |
+                         (STM_SCB_CPACR_FULL << STM_SCB_CPACR_FP1));
+       ao_arch_nop();
+       /* Set interrupt vector table offset */
+       stm_scb.vtor = (uint32_t) &stm_interrupt_vector;
+       memcpy(&_start__, &__text_end__, &_end__ - &_start__);
+       memset(&__bss_start__, '\0', &__bss_end__ - &__bss_start__);
+       main();
+}
+
+#define STRINGIFY(x) #x
+
+#define isr(name) \
+       void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_ignore_isr))
+
+#define isr_halt(name) \
+       void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_halt_isr))
+
+isr(nmi)
+isr_halt(hardfault)
+isr_halt(memmanage)
+isr_halt(busfault)
+isr_halt(usagefault)
+isr(svc)
+isr(debugmon)
+isr(pendsv)
+isr(systick)
+isr(wwdg)
+isr(pvd)
+isr(tamper_stamp)
+isr(rtc_wkup)
+isr(flash)
+isr(rcc)
+isr(exti0)
+isr(exti1)
+isr(exti2)
+isr(exti3)
+isr(exti4)
+isr(dma1_stream0)
+isr(dma1_stream1)
+isr(dma1_stream2)
+isr(dma1_stream3)
+isr(dma1_stream4)
+isr(dma1_stream5)
+isr(dma1_stream6)
+isr(adc)
+isr(can1_tx)
+isr(can1_rx0)
+isr(can1_rx1)
+isr(can1_sce)
+isr(exti9_5)
+isr(tim1_brk_tim9)
+isr(tim1_up_tim10)
+isr(tim_trg_com_tim11)
+isr(tim1_cc)
+isr(tim2)
+isr(tim3)
+isr(tim4)
+isr(i2c1_evt)
+isr(i2c1_err)
+isr(i2c2_evt)
+isr(i2c2_err)
+isr(spi1)
+isr(spi2)
+isr(usart1)
+isr(usart2)
+isr(usart3)
+isr(exti5_10)
+isr(rtc_alarm)
+isr(otg_fs_wkup)
+isr(tim8_brk_tim12)
+isr(tim8_up_tim13)
+isr(tim8_trg_com_tim14)
+isr(tim8_cc)
+isr(dma1_stream7)
+isr(fsmc)
+isr(sdio)
+isr(tim5)
+isr(spi3)
+isr(uart4)
+isr(uart5)
+isr(tim6_glb_it)
+isr(tim7)
+isr(dma2_stream0)
+isr(dma2_stream1)
+isr(dma2_stream2)
+isr(dma2_stream3)
+isr(dma2_stream4)
+isr(dfsdm1_flt0)
+isr(dfsdm1_flt1)
+isr(can2_tx)
+isr(can2_rx0)
+isr(can2_rx1)
+isr(can2_sce)
+isr(otg_fs)
+isr(dma2_stream5)
+isr(dma2_stream6)
+isr(dma2_stream7)
+isr(usart6)
+isr(i2c3_ev)
+isr(i2c3_er)
+isr(can3_tx)
+isr(can3_rx0)
+isr(can3_rx1)
+isr(can3_sce)
+isr(crypto)
+isr(rng)
+isr(fpu)
+isr(uart7)
+isr(uart8)
+isr(spi4)
+isr(spi5)
+isr(sai1)
+isr(uart9)
+isr(uart10)
+isr(quad_spi)
+isr(i2cfmp1_ev)
+isr(i2cfmp1_er)
+isr(exti23)
+isr(dfsdm2_flt0)
+isr(dfsdm2_flt1)
+isr(dfsdm2_flt2)
+isr(dfsdm2_flt3)
+
+#define i(addr,name)   [(addr)/4] = stm_ ## name ## _isr
+
+__attribute__ ((section(".interrupt")))
+const void *stm_interrupt_vector[] = {
+       [0] = &__stack__,
+       [1] = start,
+       i(0x08, nmi),
+       i(0x0c, hardfault),
+       i(0x10, memmanage),
+       i(0x14, busfault),
+       i(0x18, usagefault),
+       i(0x2c, svc),
+       i(0x30, debugmon),
+       i(0x38, pendsv),
+       i(0x3c, systick),
+       i(0x40, wwdg),
+       i(0x44, pvd),
+       i(0x48, tamper_stamp),
+       i(0x4c, rtc_wkup),
+       i(0x50, flash),
+       i(0x54, rcc),
+       i(0x58, exti0),
+       i(0x5c, exti1),
+       i(0x60, exti2),
+       i(0x64, exti3),
+       i(0x68, exti4),
+       i(0x6c, dma1_stream0),
+       i(0x70, dma1_stream1),
+       i(0x74, dma1_stream2),
+       i(0x78, dma1_stream3),
+       i(0x7c, dma1_stream4),
+       i(0x80, dma1_stream5),
+       i(0x84, dma1_stream6),
+       i(0x88, adc),
+       i(0x8c, can1_tx),
+       i(0x90, can1_rx0),
+       i(0x94, can1_rx1),
+       i(0x98, can1_sce),
+       i(0x9c, exti9_5),
+       i(0xa0, tim1_brk_tim9),
+       i(0xa4, tim1_up_tim10),
+       i(0xa8, tim_trg_com_tim11),
+       i(0xac, tim1_cc),
+       i(0xb0, tim2),
+       i(0xb4, tim3),
+       i(0xb8, tim4),
+       i(0xbc, i2c1_evt),
+       i(0xc0, i2c1_err),
+       i(0xc4, i2c2_evt),
+       i(0xc8, i2c2_err),
+       i(0xcc, spi1),
+       i(0xd0, spi2),
+       i(0xd4, usart1),
+       i(0xd8, usart2),
+       i(0xdc, usart3),
+       i(0xe0, exti5_10),
+       i(0xe4, rtc_alarm),
+       i(0xe8, otg_fs_wkup),
+       i(0xec, tim8_brk_tim12),
+       i(0xf0, tim8_up_tim13),
+};
diff --git a/src/stm32f4/ao_timer.c b/src/stm32f4/ao_timer.c
new file mode 100644 (file)
index 0000000..e96559f
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_task.h>
+
+#ifndef HAS_TICK
+#define HAS_TICK 1
+#endif
+
+#if HAS_TICK || defined(AO_TIMER_HOOK)
+
+#if HAS_TICK
+volatile AO_TICK_TYPE ao_tick_count;
+
+AO_TICK_TYPE
+ao_time(void)
+{
+       return ao_tick_count;
+}
+#endif
+
+#if AO_DATA_ALL
+volatile uint8_t       ao_data_interval = 1;
+volatile uint8_t       ao_data_count;
+#endif
+
+void stm_systick_isr(void)
+{
+       ao_validate_cur_stack();
+       if (stm_systick.csr & (1 << STM_SYSTICK_CSR_COUNTFLAG)) {
+#if HAS_TICK
+               ++ao_tick_count;
+#endif
+#if HAS_TASK_QUEUE
+               if (ao_task_alarm_tick && (int16_t) (ao_tick_count - ao_task_alarm_tick) >= 0)
+                       ao_task_check_alarm((uint16_t) ao_tick_count);
+#endif
+#if AO_DATA_ALL
+               if (++ao_data_count == ao_data_interval) {
+                       ao_data_count = 0;
+#if HAS_FAKE_FLIGHT
+                       if (ao_fake_flight_active)
+                               ao_fake_flight_poll();
+                       else
+#endif
+                               ao_adc_poll();
+#if (AO_DATA_ALL & ~(AO_DATA_ADC))
+                       ao_wakeup((void *) &ao_data_count);
+#endif
+               }
+#endif
+#ifdef AO_TIMER_HOOK
+               AO_TIMER_HOOK;
+#endif
+       }
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval)
+{
+       ao_arch_critical(
+               ao_data_interval = interval;
+               ao_data_count = 0;
+               );
+}
+#endif
+
+#define SYSTICK_RELOAD ((AO_SYSTICK / 8) / 100 - 1)
+
+void
+ao_timer_init(void)
+{
+       stm_systick.rvr = SYSTICK_RELOAD;
+       stm_systick.cvr = 0;
+       stm_systick.csr = ((1 << STM_SYSTICK_CSR_ENABLE) |
+                          (1 << STM_SYSTICK_CSR_TICKINT) |
+                          (STM_SYSTICK_CSR_CLKSOURCE_AHB_8 << STM_SYSTICK_CSR_CLKSOURCE));
+       stm_scb.shpr3 |= AO_STM_NVIC_CLOCK_PRIORITY << 24;
+}
+
+#endif
+
+void
+ao_clock_init(void)
+{
+       uint32_t        cfgr;
+       uint32_t        pllcfgr;
+
+       /* Switch to HSI while messing about */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+               ao_arch_nop();
+
+       stm_rcc.cfgr = (stm_rcc.cfgr & ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) |
+               (STM_RCC_CFGR_SW_HSI << STM_RCC_CFGR_SW);
+
+       /* wait for system to switch to HSI */
+       while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+              (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS))
+               ao_arch_nop();
+
+       /* reset everything but the HSI selection and status */
+       stm_rcc.cfgr &= (uint32_t)0x0000000f;
+
+       /* reset everything but HSI */
+       stm_rcc.cr &= 0x0000ffff;
+
+       /* Disable and clear all interrupts */
+       stm_rcc.cir = 0xffff0000;
+
+#if AO_HSE
+#if AO_HSE_BYPASS
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
+#else
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_HSEBYP);
+#endif
+       /* Enable HSE clock */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
+               asm("nop");
+
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSE << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSE)
+#define STM_PLLSRC                             AO_HSE
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (1 << STM_RCC_CFGR_PLLSRC)
+#else
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSI)
+#define STM_PLLSRC                             STM_HSI
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (0 << STM_RCC_CFGR_PLLSRC)
+#endif
+
+#if !AO_HSE || HAS_ADC || HAS_ADC_SINGLE
+       /* Enable HSI RC clock 16MHz */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+               asm("nop");
+#endif
+
+       /* Set flash latency to tolerate SYSCLK */
+
+#define FLASH_LATENCY  ((AO_SYSCLK - 1) / 25000000)
+
+       /* Enable icache, dcache and prefetch. Set latency */
+       stm_flash.acr = ((1 << STM_FLASH_ACR_DCEN) |
+                        (1 << STM_FLASH_ACR_ICEN) |
+                        (1 << STM_FLASH_ACR_PRFTEN) |
+                        (FLASH_LATENCY << STM_FLASH_ACR_LATENCY));
+
+       /* Enable power interface clock */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+#if AO_SYSCLK <= 64000000
+#define VOS_SCALE_MODE STM_PWR_CR_VOS_SCALE_MODE_1
+#elif AO_SYSCLK <= 84000000
+#define VOS_SCALE_MODE STM_PWR_CR_VOS_SCALE_MODE_2
+#else
+#define VOS_SCALE_MODE STM_PWR_CR_VOS_SCALE_MODE_1
+#endif
+
+       /* Set voltage scale mode */
+       stm_pwr.cr = ((stm_pwr.cr & ~(STM_PWR_CR_VOS_SCALE_MODE_MASK)) |
+                     (VOS_SCALE_MODE << STM_PWR_CR_VOS));
+
+       /* HCLK */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
+       cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
+       stm_rcc.cfgr = cfgr;
+
+       /* APB1 Prescaler = AO_APB1_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE1_MASK << STM_RCC_CFGR_PPRE1);
+       cfgr |= (AO_RCC_CFGR_PPRE1_DIV << STM_RCC_CFGR_PPRE1);
+       stm_rcc.cfgr = cfgr;
+
+       /* APB2 Prescaler = AO_APB2_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE2_MASK << STM_RCC_CFGR_PPRE2);
+       cfgr |= (AO_RCC_CFGR_PPRE2_DIV << STM_RCC_CFGR_PPRE2);
+       stm_rcc.cfgr = cfgr;
+
+       /* Disable the PLL */
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_PLLON);
+       while (stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY))
+               asm("nop");
+
+       /* PLL1VCO */
+       pllcfgr = stm_rcc.pllcfgr;
+       pllcfgr &= ~(STM_RCC_PLLCFGR_PLLM_MASK << STM_RCC_PLLCFGR_PLLM);
+       pllcfgr &= ~(STM_RCC_PLLCFGR_PLLN_MASK << STM_RCC_PLLCFGR_PLLN);
+       pllcfgr &= ~(STM_RCC_PLLCFGR_PLLP_MASK << STM_RCC_PLLCFGR_PLLP);
+       pllcfgr &= ~(STM_RCC_PLLCFGR_PLLQ_MASK << STM_RCC_PLLCFGR_PLLQ);
+       pllcfgr &= ~(STM_RCC_PLLCFGR_PLLR_MASK << STM_RCC_PLLCFGR_PLLR);
+
+       pllcfgr |= (AO_PLL_M << STM_RCC_PLLCFGR_PLLM);
+       pllcfgr |= (AO_PLL1_N << STM_RCC_PLLCFGR_PLLN);
+#if AO_PLL1_P
+       pllcfgr |= (AO_PLL1_P << STM_RCC_PLLCFGR_PLLP);
+#endif
+#if AO_PLL1_Q
+       pllcfgr |= (AO_PLL1_Q << STM_RCC_PLLCFGR_PLLQ);
+#endif
+       /* PLL source */
+       pllcfgr &= ~(1 << STM_RCC_PLLCFGR_PLLSRC);
+#if AO_HSI
+       pllcfgr |= STM_RCC_PLLCFGR_PLLSRC_HSI;
+#endif
+#if AO_HSE
+       pllcfgr |= STM_RCC_PLLCFGR_PLLSRC_HSE;
+#endif
+       stm_rcc.pllcfgr = pllcfgr;
+
+       /* Enable the PLL and wait for it */
+       stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
+               asm("nop");
+
+       /* Switch to the PLL for the system clock */
+
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+       cfgr |= (STM_RCC_CFGR_SW_PLL << STM_RCC_CFGR_SW);
+       stm_rcc.cfgr = cfgr;
+       for (;;) {
+               uint32_t        c, part, mask, val;
+
+               c = stm_rcc.cfgr;
+               mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+               val = (STM_RCC_CFGR_SWS_PLL << STM_RCC_CFGR_SWS);
+               part = c & mask;
+               if (part == val)
+                       break;
+       }
+
+#if 0
+       stm_rcc.apb2rstr = 0xffff;
+       stm_rcc.apb1rstr = 0xffff;
+       stm_rcc.ahbrstr = 0x3f;
+       stm_rcc.ahbenr = (1 << STM_RCC_AHBENR_FLITFEN);
+       stm_rcc.apb2enr = 0;
+       stm_rcc.apb1enr = 0;
+       stm_rcc.ahbrstr = 0;
+       stm_rcc.apb1rstr = 0;
+       stm_rcc.apb2rstr = 0;
+#endif
+
+       /* Clear reset flags */
+       stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+
+#if DEBUG_THE_CLOCK
+       /* Output SYSCLK on PA8 for measurments */
+
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+       
+       stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+       stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE);
+       stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
+#endif
+}
diff --git a/src/stm32f4/registers.ld b/src/stm32f4/registers.ld
new file mode 100644 (file)
index 0000000..b36b209
--- /dev/null
@@ -0,0 +1,47 @@
+stm_tim2   = 0x40000000;
+stm_tim3   = 0x40000400;
+stm_tim4   = 0x40000800;
+stm_tim5   = 0x40000c00;
+stm_tim6   = 0x40001000;
+stm_tim7   = 0x40001400;
+stm_tim12  = 0x40001800;
+stm_tim13  = 0x40001c00;
+stm_tim14  = 0x40002000;
+stm_lptim1 = 0x40002400;
+stm_rtc    = 0x40002800;
+stm_wwdg   = 0x40002c00;
+stm_iwdg   = 0x40003000;
+stm_can1   = 0x40006400;
+stm_can2   = 0x40006800;
+stm_can3   = 0x40006c00;
+stm_pwr    = 0x40007000;
+stm_dac1   = 0x40007400;
+stm_uart7  = 0x40007800;
+stm_uart8  = 0x40007c00;
+
+stm_gpioa  = 0x40020000;
+stm_gpiob  = 0x40020400;
+stm_gpioc  = 0x40020800;
+stm_gpiod  = 0x40020c00;
+stm_gpioe  = 0x40021000;
+stm_gpiof  = 0x40021400;
+stm_gpiog  = 0x40021800;
+stm_gpioh  = 0x40021c00;
+
+stm_crc    = 0x40023000;
+stm_rcc    = 0x40023800;
+stm_flash  = 0x40023c00;
+stm_dma1   = 0x40026000;
+stm_dma2   = 0x40026400;
+
+stm_systick = 0xe000e010;
+
+stm_ictr   = 0xe000e004;
+stm_nvic   = 0xe000e100;
+
+stm_scb    = 0xe000ed00;
+
+stm_mpu    = 0xe000ed90;
+
+stm_flash_size = 0x1fff7a22;
+stm_device_id = 0x1fff7a10;
diff --git a/src/stm32f4/stm32f4.h b/src/stm32f4/stm32f4.h
new file mode 100644 (file)
index 0000000..a351f08
--- /dev/null
@@ -0,0 +1,694 @@
+/*
+ * Copyright © 2018 Keith Packard <keithp@keithp.com>
+ *
+ * This program 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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.
+ */
+
+#ifndef _STM32F4_H_
+#define _STM32F4_H_
+
+#include <stdint.h>
+
+typedef volatile uint32_t      vuint32_t;
+typedef volatile void *                vvoid_t;
+typedef volatile uint16_t      vuint16_t;
+typedef volatile uint8_t       vuint8_t;
+
+struct stm_pwr {
+       vuint32_t       cr;
+       vuint32_t       csr;
+};
+
+extern struct stm_pwr stm_pwr;
+
+#define stm_pwr        (*((struct stm_pwr *) 0x40007000))
+
+#define STM_PWR_CR_FISSR       21
+#define STM_PWR_CR_FMSSR       20
+#define STM_PWR_CR_VOS         14
+#define  STM_PWR_CR_VOS_SCALE_MODE_3   1
+#define  STM_PWR_CR_VOS_SCALE_MODE_2   2
+#define  STM_PWR_CR_VOS_SCALE_MODE_1   3
+#define  STM_PWR_CR_VOS_SCALE_MODE_MASK        3
+#define STM_PWR_CR_ADCDC1      13
+#define STM_PWR_CR_MRLVDS      11
+#define STM_PWR_CR_LPLVDS      10
+#define STM_PWR_CR_FPDS                9
+#define STM_PWR_CR_DBP         8
+#define STM_PWR_CR_PLS         5
+#define STM_PWR_CR_PVDE                4
+#define STM_PWR_CR_CSBF                3
+#define STM_PWR_CR_CWUF                2
+#define STM_PWR_CR_PDDS                1
+#define STM_PWR_CR_LPDS                0
+
+struct stm_rcc {
+       vuint32_t       cr;
+       vuint32_t       pllcfgr;
+       vuint32_t       cfgr;
+       vuint32_t       cir;
+
+       vuint32_t       ahb1rstr;
+       vuint32_t       ahb2rstr;
+       vuint32_t       ahb3rstr;
+       uint32_t        pad_1c;
+
+       vuint32_t       apb1rstr;
+       vuint32_t       apb2rstr;
+       vuint32_t       pad_28;
+       vuint32_t       pad_2c;
+
+       vuint32_t       ahb1enr;
+       vuint32_t       ahb2enr;
+       vuint32_t       ahbdnr;
+       vuint32_t       pad_3c;
+
+       vuint32_t       apb1enr;
+       vuint32_t       apb2enr;
+       vuint32_t       pad_48;
+       vuint32_t       pad_4c;
+
+       vuint32_t       ahb1lpenr;
+       vuint32_t       ahb2lpenr;
+       vuint32_t       ahb3lpenr;
+       vuint32_t       pad_5c;
+
+       vuint32_t       apb1lpenr;
+       vuint32_t       apb2lpenr;
+       vuint32_t       pad_68;
+       vuint32_t       pad_6c;
+
+       vuint32_t       bdcr;
+       vuint32_t       csr;
+       vuint32_t       pad_78;
+       vuint32_t       pad_7c;
+
+       vuint32_t       sscgr;
+       vuint32_t       plli2scfgr;
+       vuint32_t       pad_88;
+       vuint32_t       dckcfgr;
+
+       vuint32_t       ckgatenr;
+       vuint32_t       dckcfgr2;
+};
+
+extern struct stm_rcc stm_rcc;
+
+#define stm_rcc        (*((struct stm_rcc *) 0x40023800))
+
+/* Internal HSI is 16MHz */
+#define STM_HSI_FREQ           16000000
+
+#define STM_RCC_CR_PLLI2SRDY   (27)
+#define STM_RCC_CR_PLLI2SON    (26)
+#define STM_RCC_CR_PLLRDY      (25)
+#define STM_RCC_CR_PLLON       (24)
+#define STM_RCC_CR_CSSON       (19)
+#define STM_RCC_CR_HSEBYP      (18)
+#define STM_RCC_CR_HSERDY      (17)
+#define STM_RCC_CR_HSEON       (16)
+#define STM_RCC_CR_HSICAL      (8)
+#define STM_RCC_CR_HSITRIM     (3)
+#define STM_RCC_CR_HSIRDY      (1)
+#define STM_RCC_CR_HSION       (0)
+
+#define STM_RCC_PLLCFGR_PLLM   0
+#define  STM_RCC_PLLCFGR_PLLM_MASK     0x3f
+#define STM_RCC_PLLCFGR_PLLN   6
+#define  STM_RCC_PLLCFGR_PLLN_MASK     0x1ff
+#define STM_RCC_PLLCFGR_PLLP   16
+#define  STM_RCC_PLLCFGR_PLLP_MASK     0x3
+#define STM_RCC_PLLCFGR_PLLSRC 22
+#define  STM_RCC_PLLCFGR_PLLSRC_HSI    0
+#define  STM_RCC_PLLCFGR_PLLSRC_HSE    1
+#define STM_RCC_PLLCFGR_PLLQ   24
+#define  STM_RCC_PLLCFGR_PLLQ_MASK     0xf
+#define STM_RCC_PLLCFGR_PLLR   28
+#define  STM_RCC_PLLCFGR_PLLR_MASK     0x7
+
+#define STM_RCC_CFGR_MCO2      (30)
+#define STM_RCC_CFGR_MCO2PRE   (27)
+#define STM_RCC_CFGR_MCO1PRE   (24)
+#define STM_RCC_CFGR_MCO1      (21)
+#define STM_RCC_CFGR_RTCPRE    (16)
+
+#define STM_RCC_CFGR_PPRE2     (13)
+#define  STM_RCC_CFGR_PPRE2_DIV_1      0
+#define  STM_RCC_CFGR_PPRE2_DIV_2      4
+#define  STM_RCC_CFGR_PPRE2_DIV_4      5
+#define  STM_RCC_CFGR_PPRE2_DIV_8      6
+#define  STM_RCC_CFGR_PPRE2_DIV_16     7
+#define  STM_RCC_CFGR_PPRE2_MASK       7
+
+#define STM_RCC_CFGR_PPRE1     (10)
+#define  STM_RCC_CFGR_PPRE1_DIV_1      0
+#define  STM_RCC_CFGR_PPRE1_DIV_2      4
+#define  STM_RCC_CFGR_PPRE1_DIV_4      5
+#define  STM_RCC_CFGR_PPRE1_DIV_8      6
+#define  STM_RCC_CFGR_PPRE1_DIV_16     7
+#define  STM_RCC_CFGR_PPRE1_MASK       7
+
+#define STM_RCC_CFGR_HPRE      (4)
+#define  STM_RCC_CFGR_HPRE_DIV_1       0x0
+#define  STM_RCC_CFGR_HPRE_DIV_2       0x8
+#define  STM_RCC_CFGR_HPRE_DIV_4       0x9
+#define  STM_RCC_CFGR_HPRE_DIV_8       0xa
+#define  STM_RCC_CFGR_HPRE_DIV_16      0xb
+#define  STM_RCC_CFGR_HPRE_DIV_64      0xc
+#define  STM_RCC_CFGR_HPRE_DIV_128     0xd
+#define  STM_RCC_CFGR_HPRE_DIV_256     0xe
+#define  STM_RCC_CFGR_HPRE_DIV_512     0xf
+#define  STM_RCC_CFGR_HPRE_MASK                0xf
+
+#define STM_RCC_CFGR_SWS       (2)
+#define  STM_RCC_CFGR_SWS_HSI          0
+#define  STM_RCC_CFGR_SWS_HSE          1
+#define  STM_RCC_CFGR_SWS_PLL          2
+#define  STM_RCC_CFGR_SWS_MASK         3
+
+#define STM_RCC_CFGR_SW                (0)
+#define  STM_RCC_CFGR_SW_HSI           0
+#define  STM_RCC_CFGR_SW_HSE           1
+#define  STM_RCC_CFGR_SW_PLL           2
+#define  STM_RCC_CFGR_SW_MASK          3
+
+#define STM_RCC_AHB1ENR_IOPAEN 0
+#define STM_RCC_AHB1ENR_IOPBEN 1
+#define STM_RCC_AHB1ENR_IOPCEN 2
+#define STM_RCC_AHB1ENR_IOPDEN 3
+#define STM_RCC_AHB1ENR_IOPEEN 4
+#define STM_RCC_AHB1ENR_IOPFEN 5
+#define STM_RCC_AHB1ENR_IOPGEN 6
+#define STM_RCC_AHB1ENR_IOPHEN 7
+
+#define STM_RCC_APB1ENR_UART8EN                31
+#define STM_RCC_APB1ENR_UART7EN                30
+#define STM_RCC_APB1ENR_DACEN          29
+#define STM_RCC_APB1ENR_PWREN          28
+#define STM_RCC_APB1ENR_CAN3EN         27
+#define STM_RCC_APB1ENR_CAN2EN         26
+#define STM_RCC_APB1ENR_CAN1EN         25
+#define STM_RCC_APB1ENR_I2CFMP1EN      24
+#define STM_RCC_APB1ENR_I2C3EN         23
+#define STM_RCC_APB1ENR_I2C2EN         22
+#define STM_RCC_APB1ENR_I2C1EN         21
+#define STM_RCC_APB1ENR_UART5EN                20
+#define STM_RCC_APB1ENR_UART4EN                19
+#define STM_RCC_APB1ENR_USART3EN       18
+#define STM_RCC_APB1ENR_USART2EN       17
+#define STM_RCC_APB1ENR_SPI3EN         15
+#define STM_RCC_APB1ENR_SPI2EN         14
+#define STM_RCC_APB1ENR_WWDGEN         11
+#define STM_RCC_APB1ENR_RTCAPBEN       10
+#define STM_RCC_APB1ENR_LPTIMER1EN     9
+#define STM_RCC_APB1ENR_TIM14EN                8
+#define STM_RCC_APB1ENR_TIM13EN                7
+#define STM_RCC_APB1ENR_TIM12EN                6
+#define STM_RCC_APB1ENR_TIM7EN         5
+#define STM_RCC_APB1ENR_TIM6EN         4
+#define STM_RCC_APB1ENR_TIM5EN         3
+#define STM_RCC_APB1ENR_TIM4EN         2
+#define STM_RCC_APB1ENR_TIM3EN         1
+#define STM_RCC_APB1ENR_TIM2EN         0
+
+#define STM_RCC_CSR_RMVF               24
+
+struct stm_ictr {
+       vuint32_t       ictr;
+};
+
+extern struct stm_ictr stm_ictr;
+
+#define stm_ictr (*((struct stm_ictr *) 0xe000e004))
+
+#define STM_ICTR_ICTR_INTLINESNUM      0
+#define  STM_ICTR_ICTR_INTLINESNUM_MASK                0xf
+
+struct stm_nvic {
+       vuint32_t       iser[8];        /* 0x000 0xe000e100 Set Enable Register */
+
+       uint8_t         _unused020[0x080 - 0x020];
+
+       vuint32_t       icer[8];        /* 0x080 0xe000e180 Clear Enable Register */
+
+       uint8_t         _unused0a0[0x100 - 0x0a0];
+
+       vuint32_t       ispr[8];        /* 0x100 0xe000e200 Set Pending Register */
+
+       uint8_t         _unused120[0x180 - 0x120];
+
+       vuint32_t       icpr[8];        /* 0x180 0xe000e280 Clear Pending Register */
+
+       uint8_t         _unused1a0[0x200 - 0x1a0];
+
+       vuint32_t       iabr[8];        /* 0x200 0xe000e300 Active Bit Register */
+
+       uint8_t         _unused220[0x300 - 0x220];
+
+       vuint32_t       ipr[60];        /* 0x300 0xe000e400 Priority Register */
+};
+
+extern struct stm_nvic stm_nvic;
+
+#define stm_nvic (*((struct stm_nvic *) 0xe000e100))
+
+#define IRQ_REG(irq)   ((irq) >> 5)
+#define IRQ_BIT(irq)   ((irq) & 0x1f)
+#define IRQ_MASK(irq)  (1 << IRQ_BIT(irq))
+#define IRQ_BOOL(v,irq)        (((v) >> IRQ_BIT(irq)) & 1)
+
+static inline void
+stm_nvic_set_enable(int irq) {
+       stm_nvic.iser[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_enable(int irq) {
+       stm_nvic.icer[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_enabled(int irq) {
+       return IRQ_BOOL(stm_nvic.iser[IRQ_REG(irq)], irq);
+}
+       
+static inline void
+stm_nvic_set_pending(int irq) {
+       stm_nvic.ispr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_pending(int irq) {
+       stm_nvic.icpr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_pending(int irq) {
+       return IRQ_BOOL(stm_nvic.ispr[IRQ_REG(irq)], irq);
+}
+
+static inline int
+stm_nvic_active(int irq) {
+       return IRQ_BOOL(stm_nvic.iabr[IRQ_REG(irq)], irq);
+}
+
+#define IRQ_PRIO_REG(irq)      ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq)      (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq)     (0xff << IRQ_PRIO_BIT(irq))
+
+static inline void
+stm_nvic_set_priority(int irq, uint8_t prio) {
+       int             n = IRQ_PRIO_REG(irq);
+       uint32_t        v;
+
+       v = stm_nvic.ipr[n];
+       v &= ~IRQ_PRIO_MASK(irq);
+       v |= (prio) << IRQ_PRIO_BIT(irq);
+       stm_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+stm_nvic_get_priority(int irq) {
+       return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+struct stm_flash {
+       vuint32_t       acr;
+       vuint32_t       keyr;
+       vuint32_t       optkeyr;
+       vuint32_t       sr;
+
+       vuint32_t       cr;
+       vuint32_t       optcr;
+       vuint32_t       wrpr;
+};
+
+extern struct stm_flash stm_flash;
+
+#define stm_flash (*((struct stm_flash *) 0x40023c00))
+
+#define STM_FLASH_ACR_DCRST    12
+#define STM_FLASH_ACR_ICRST    11
+#define STM_FLASH_ACR_DCEN     10
+#define STM_FLASH_ACR_ICEN     9
+#define STM_FLASH_ACR_PRFTEN   8
+#define STM_FLASH_ACR_LATENCY  0
+
+struct stm_flash_size {
+       vuint16_t       f_size;
+};
+
+extern struct stm_flash_size stm_flash_size;
+
+#define stm_flash_size (*((struct stm_flash_size *) 0x1fff7a22))
+
+struct stm_gpio {
+       vuint32_t       moder;
+       vuint32_t       otyper;
+       vuint32_t       ospeedr;
+       vuint32_t       pupdr;
+
+       vuint32_t       idr;
+       vuint32_t       odr;
+       vuint32_t       bsrr;
+       vuint32_t       lckr;
+
+       vuint32_t       afrl;
+       vuint32_t       afrh;
+};
+
+#define STM_MODER_SHIFT(pin)           ((pin) << 1)
+#define STM_MODER_MASK                 3
+#define STM_MODER_INPUT                        0
+#define STM_MODER_OUTPUT               1
+#define STM_MODER_ALTERNATE            2
+#define STM_MODER_ANALOG               3
+
+static inline void
+stm_moder_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+       gpio->moder = ((gpio->moder &
+                       ~(STM_MODER_MASK << STM_MODER_SHIFT(pin))) |
+                      value << STM_MODER_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_moder_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->moder >> STM_MODER_SHIFT(pin)) & STM_MODER_MASK;
+}
+
+#define STM_OTYPER_SHIFT(pin)          (pin)
+#define STM_OTYPER_MASK                        1
+#define STM_OTYPER_PUSH_PULL           0
+#define STM_OTYPER_OPEN_DRAIN          1
+
+static inline void
+stm_otyper_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+       gpio->otyper = ((gpio->otyper &
+                        ~(STM_OTYPER_MASK << STM_OTYPER_SHIFT(pin))) |
+                       value << STM_OTYPER_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_otyper_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->otyper >> STM_OTYPER_SHIFT(pin)) & STM_OTYPER_MASK;
+}
+
+#define STM_OSPEEDR_SHIFT(pin)         ((pin) << 1)
+#define STM_OSPEEDR_MASK               3
+#define STM_OSPEEDR_LOW                        0       /* 2-8MHz */
+#define STM_OSPEEDR_MEDIUM             1       /* 12.5-50MHz */
+#define STM_OSPEEDR_FAST               2       /* 25-100MHz */
+#define STM_OSPEEDR_HIGH               3       /* 50-100MHz */
+
+static inline void
+stm_ospeedr_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+       gpio->ospeedr = ((gpio->ospeedr &
+                       ~(STM_OSPEEDR_MASK << STM_OSPEEDR_SHIFT(pin))) |
+                      value << STM_OSPEEDR_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_ospeedr_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->ospeedr >> STM_OSPEEDR_SHIFT(pin)) & STM_OSPEEDR_MASK;
+}
+
+#define STM_PUPDR_SHIFT(pin)           ((pin) << 1)
+#define STM_PUPDR_MASK                 3
+#define STM_PUPDR_NONE                 0
+#define STM_PUPDR_PULL_UP              1
+#define STM_PUPDR_PULL_DOWN            2
+#define STM_PUPDR_RESERVED             3
+
+static inline void
+stm_pupdr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
+       gpio->pupdr = ((gpio->pupdr &
+                       ~(STM_PUPDR_MASK << STM_PUPDR_SHIFT(pin))) |
+                      value << STM_PUPDR_SHIFT(pin));
+}
+
+static inline uint32_t
+stm_pupdr_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->pupdr >> STM_PUPDR_SHIFT(pin)) & STM_PUPDR_MASK;
+}
+
+#define STM_AFR_SHIFT(pin)             ((pin) << 2)
+#define STM_AFR_MASK                   0xf
+#define STM_AFR_NONE                   0
+#define STM_AFR_AF0                    0x0
+#define STM_AFR_AF1                    0x1
+#define STM_AFR_AF2                    0x2
+#define STM_AFR_AF3                    0x3
+#define STM_AFR_AF4                    0x4
+#define STM_AFR_AF5                    0x5
+#define STM_AFR_AF6                    0x6
+#define STM_AFR_AF7                    0x7
+#define STM_AFR_AF8                    0x8
+#define STM_AFR_AF9                    0x9
+#define STM_AFR_AF10                   0xa
+#define STM_AFR_AF11                   0xb
+#define STM_AFR_AF12                   0xc
+#define STM_AFR_AF13                   0xd
+#define STM_AFR_AF14                   0xe
+#define STM_AFR_AF15                   0xf
+
+static inline void
+stm_afr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
+       /*
+        * Set alternate pin mode too
+        */
+       stm_moder_set(gpio, pin, STM_MODER_ALTERNATE);
+       if (pin < 8)
+               gpio->afrl = ((gpio->afrl &
+                              ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) |
+                             value << STM_AFR_SHIFT(pin));
+       else {
+               pin -= 8;
+               gpio->afrh = ((gpio->afrh &
+                              ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) |
+                             value << STM_AFR_SHIFT(pin));
+       }
+}
+
+static inline uint32_t
+stm_afr_get(struct stm_gpio *gpio, int pin) {
+       if (pin < 8)
+               return (gpio->afrl >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK;
+       else {
+               pin -= 8;
+               return (gpio->afrh >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK;
+       }
+}
+
+static inline void
+stm_gpio_set(struct stm_gpio *gpio, int pin, uint8_t value) {
+       /* Use the bit set/reset register to do this atomically */
+       gpio->bsrr = ((uint32_t) (value ^ 1) << (pin + 16)) | ((uint32_t) value << pin);
+}
+
+static inline uint8_t
+stm_gpio_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->idr >> pin) & 1;
+}
+
+static inline uint16_t
+stm_gpio_get_all(struct stm_gpio *gpio) {
+       return gpio->idr;
+}
+
+/*
+ * We can't define these in registers.ld or our fancy
+ * ao_enable_gpio macro will expand into a huge pile of code
+ * as the compiler won't do correct constant folding and
+ * dead-code elimination
+ */
+
+extern struct stm_gpio stm_gpioa;
+extern struct stm_gpio stm_gpiob;
+extern struct stm_gpio stm_gpioc;
+extern struct stm_gpio stm_gpiod;
+extern struct stm_gpio stm_gpioe;
+extern struct stm_gpio stm_gpiof;
+extern struct stm_gpio stm_gpiog;
+extern struct stm_gpio stm_gpioh;
+
+#define stm_gpioa  (*((struct stm_gpio *) 0x40020000))
+#define stm_gpiob  (*((struct stm_gpio *) 0x40020400))
+#define stm_gpioc  (*((struct stm_gpio *) 0x40020800))
+#define stm_gpiod  (*((struct stm_gpio *) 0x40020c00))
+#define stm_gpioe  (*((struct stm_gpio *) 0x40021000))
+#define stm_gpiof  (*((struct stm_gpio *) 0x40021400))
+#define stm_gpiog  (*((struct stm_gpio *) 0x40021800))
+#define stm_gpioh  (*((struct stm_gpio *) 0x40021c00))
+
+struct stm_scb {
+       vuint32_t       cpuid;
+       vuint32_t       icsr;
+       vuint32_t       vtor;
+       vuint32_t       aircr;
+
+       vuint32_t       scr;
+       vuint32_t       ccr;
+       vuint32_t       shpr1;
+       vuint32_t       shpr2;
+
+       vuint32_t       shpr3;
+       vuint32_t       shcsr;
+       vuint32_t       cfsr;
+       vuint32_t       hfsr;
+
+       vuint32_t       dfsr;
+       vuint32_t       mmcar;
+       vuint32_t       bcar;
+       vuint32_t       afsr;
+
+       vuint32_t       id_pfr0;
+       vuint32_t       id_pfr1;
+       vuint32_t       id_dfr0;
+       vuint32_t       id_afr0;
+
+       vuint32_t       id_mmfr0;
+       vuint32_t       id_mmfr1;
+       vuint32_t       id_mmfr2;
+       vuint32_t       id_mmfr3;
+
+       vuint32_t       id_isar0;
+       vuint32_t       id_isar1;
+       vuint32_t       id_isar2;
+       vuint32_t       id_isar3;
+
+       vuint32_t       id_isar4;
+       vuint32_t       pad_d74;
+       vuint32_t       pad_d78;
+       vuint32_t       pad_d7c;
+
+       vuint32_t       pad_d80;
+       vuint32_t       pad_d84;
+       vuint32_t       cpacr;
+       vuint32_t       pad_d8c;
+
+       vuint8_t        pad_d90[0xf00 - 0xd90];
+
+       vuint32_t       stir;
+};
+
+extern struct stm_scb stm_scb;
+
+#define stm_scb (*((struct stm_scb *) 0xe000ed00))
+
+#define STM_SCB_CPACR_CP(n)    ((n) <<1)
+#define  STM_SCB_CPACR_DENIED          0
+#define  STM_SCB_CPACR_PRIVILEGED      1
+#define  STM_SCB_CPACR_RESERVED                2
+#define  STM_SCB_CPACR_FULL            3
+#define STM_SCB_CPACR_FP0      STM_SCB_CPACR_CP(10)
+#define STM_SCB_CPACR_FP1      STM_SCB_CPACR_CP(11)
+
+/* The SYSTICK starts at 0xe000e010 */
+
+struct stm_systick {
+       vuint32_t       csr;
+       vuint32_t       rvr;
+       vuint32_t       cvr;
+       vuint32_t       calib;
+};
+
+extern struct stm_systick stm_systick;
+
+#define stm_systick    (*((struct stm_systick *) 0xe000e010))
+
+#define STM_SYSTICK_CSR_ENABLE         0
+#define STM_SYSTICK_CSR_TICKINT                1
+#define STM_SYSTICK_CSR_CLKSOURCE      2
+#define  STM_SYSTICK_CSR_CLKSOURCE_AHB_8               0
+#define  STM_SYSTICK_CSR_CLKSOURCE_AHB                 1
+#define STM_SYSTICK_CSR_COUNTFLAG      16
+
+/* Errata 2.1.5
+
+   Delay after an RCC peripheral clock enabling
+
+   Description
+
+   A delay between an RCC peripheral clock enable and the effective
+   peripheral enabling should be taken into account in order to manage
+   the peripheral read/write to registers.
+
+   This delay depends on the peripheral’s mapping:
+
+   • If the peripheral is mapped on AHB: the delay should be equal to
+     2 AHB cycles.
+
+   • If the peripheral is mapped on APB: the delay should be equal to
+     1 + (AHB/APB prescaler) cycles.
+
+   Workarounds
+
+   1. Use the DSB instruction to stall the Cortex-M4 CPU pipeline
+      until the instruction is completed.
+
+   2. Insert “n” NOPs between the RCC enable bit write and the
+      peripheral register writes
+*/
+
+static inline void
+stm32f4_set_rcc(uint32_t *rcc, uint32_t value)
+{
+       *rcc = value;
+       asm("dsb");
+}
+
+/* Errata 2.1.8
+
+   In some specific cases, DMA2 data corruption occurs when managing
+   AHB and APB2 peripherals in a concurrent way
+
+   Description
+
+   When the DMA2 is managing concurrent requests of AHB and APB2
+   peripherals, the transfer on the AHB could be performed several
+   times.
+
+   Impacted peripheral are:
+
+   • Quad-SPI: indirect mode read and write transfers
+
+   • FSMC: read and write operation with external device having FIFO
+
+   • GPIO: DMA2 transfers to GPIO registers (in memory-to-peripheral
+     transfer mode).The transfers from GPIOs register are not
+     impacted.
+
+
+   The data corruption is due to multiple DMA2 accesses over AHB
+   peripheral port impacting peripherals embedding a FIFO.
+
+   For transfer to the internal SRAM through the DMA2 AHB peripheral
+   port the accesses could be performed several times but without data
+   corruptions in cases of concurrent requests.
+
+   Workaround
+
+   • The DMA2 AHB memory port must be used when reading/writing
+     from/to Quad-SPI and FSMC instead of DMA2 AHB default peripheral
+     port.
+
+   • The DMA2 AHB memory port must be used when writing to GPIOs
+     instead of DMA2 AHB default peripheral port.
+
+   Refer to application note AN4031 section “Take benefits of DMA2
+   controller and system architecture flexibility” for more details
+   about DMA controller feature.
+
+*/
+
+
+
+#endif /* _STM32F4_H_ */