summaryrefslogtreecommitdiff
path: root/src/stm32f4
diff options
context:
space:
mode:
Diffstat (limited to 'src/stm32f4')
-rw-r--r--src/stm32f4/Makefile-flash.defs68
-rw-r--r--src/stm32f4/Makefile-raw.defs13
-rw-r--r--src/stm32f4/Makefile-stm32f4.defs52
-rw-r--r--src/stm32f4/Makefile.defs13
-rw-r--r--src/stm32f4/altos-loader.ld97
-rw-r--r--src/stm32f4/altos-raw.ld74
-rw-r--r--src/stm32f4/altos.ld105
-rw-r--r--src/stm32f4/ao_arch.h141
-rw-r--r--src/stm32f4/ao_arch_funcs.h340
-rw-r--r--src/stm32f4/ao_exti.h50
-rw-r--r--src/stm32f4/ao_exti_stm32f4.c162
-rw-r--r--src/stm32f4/ao_interrupt.c290
-rw-r--r--src/stm32f4/ao_serial_stm32f4.c544
-rw-r--r--src/stm32f4/ao_timer.c284
-rw-r--r--src/stm32f4/ao_usart_stm32f4.c350
-rw-r--r--src/stm32f4/ao_usb_gen.c712
-rw-r--r--src/stm32f4/ao_usb_gen.h110
-rw-r--r--src/stm32f4/ao_usb_stm32f4.c1144
-rw-r--r--src/stm32f4/registers.ld75
-rw-r--r--src/stm32f4/stm32f4.h1522
20 files changed, 6146 insertions, 0 deletions
diff --git a/src/stm32f4/Makefile-flash.defs b/src/stm32f4/Makefile-flash.defs
new file mode 100644
index 00000000..1a2aa75c
--- /dev/null
+++ b/src/stm32f4/Makefile-flash.defs
@@ -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
index 00000000..03d92e42
--- /dev/null
+++ b/src/stm32f4/Makefile-raw.defs
@@ -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
index 00000000..c39633c7
--- /dev/null
+++ b/src/stm32f4/Makefile-stm32f4.defs
@@ -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 \
+ -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
index 00000000..be185a59
--- /dev/null
+++ b/src/stm32f4/Makefile.defs
@@ -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
index 00000000..5d6e1f4b
--- /dev/null
+++ b/src/stm32f4/altos-loader.ld
@@ -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
index 00000000..82d33c09
--- /dev/null
+++ b/src/stm32f4/altos-raw.ld
@@ -0,0 +1,74 @@
+/*
+ * 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 */
+ *(.rodata*) /* Constants */
+ } > rom
+
+ .exidx : {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ } > 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
index 00000000..2db0e387
--- /dev/null
+++ b/src/stm32f4/altos.ld
@@ -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
index 00000000..73dc3e93
--- /dev/null
+++ b/src/stm32f4/ao_arch.h
@@ -0,0 +1,141 @@
+/*
+ * 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 2048
+#endif
+
+#ifndef HAS_TASK_QUEUE
+#define HAS_TASK_QUEUE 1
+#endif
+
+#define AO_STACK_ALIGNMENT __attribute__ ((aligned(8)))
+
+#define AO_PORT_TYPE uint16_t
+
+#define ao_arch_reboot() \
+ (stm_scb.aircr = ((STM_SCB_AIRCR_VECTKEY_KEY << STM_SCB_AIRCR_VECTKEY) | \
+ (1 << STM_SCB_AIRCR_SYSRESETREQ)))
+
+#define ao_arch_nop() asm("nop")
+
+#define AO_ROMCONFIG_SYMBOL __attribute__((section(".romconfig"))) const
+
+#define ao_arch_task_members\
+ uint32_t *sp; /* saved stack pointer */
+
+#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)
+
+/* The stm32f413 implements only 4 bits of the priority fields? */
+
+#if AO_NONMASK_INTERRUPT
+#define AO_STM_NVIC_NONMASK_PRIORITY 0x00
+
+/* Set the basepri register to this value to mask all
+ * non-maskable priorities
+ */
+#define AO_STM_NVIC_BASEPRI_MASK 0x10
+#endif
+
+#define AO_STM_NVIC_HIGH_PRIORITY 0x40
+#define AO_STM_NVIC_MED_PRIORITY 0x80
+#define AO_STM_NVIC_LOW_PRIORITY 0xC0
+#define AO_STM_NVIC_CLOCK_PRIORITY 0xf0
+
+#define AO_GPIO_MODE_PULL_NONE 0
+#define AO_GPIO_MODE_PULL_UP 4
+#define AO_GPIO_MODE_PULL_DOWN 8
+
+/* usart stuff */
+
+#define AO_SERIAL_SPEED_4800 4800
+#define AO_SERIAL_SPEED_9600 9600
+#define AO_SERIAL_SPEED_19200 19200
+#define AO_SERIAL_SPEED_57600 57600
+#define AO_SERIAL_SPEED_115200 115200
+
+#endif /* _AO_ARCH_H_ */
diff --git a/src/stm32f4/ao_arch_funcs.h b/src/stm32f4/ao_arch_funcs.h
new file mode 100644
index 00000000..b1ffb5b6
--- /dev/null
+++ b/src/stm32f4/ao_arch_funcs.h
@@ -0,0 +1,340 @@
+/*
+ * 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_
+
+/* task functions */
+
+#define ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+typedef uint32_t ao_arch_irq_t;
+
+static inline void
+ao_arch_block_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+ asm("cpsid i");
+#endif
+}
+
+static inline void
+ao_arch_release_interrupts(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (0x0));
+#else
+ asm("cpsie i");
+#endif
+}
+
+static inline uint32_t
+ao_arch_irqsave(void) {
+ uint32_t val;
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("mrs %0,basepri" : "=r" (val));
+#else
+ asm("mrs %0,primask" : "=r" (val));
+#endif
+ ao_arch_block_interrupts();
+ return val;
+}
+
+static inline void
+ao_arch_irqrestore(uint32_t basepri) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm("msr basepri,%0" : : "r" (basepri));
+#else
+ asm("msr primask,%0" : : "r" (basepri));
+#endif
+}
+
+static inline void
+ao_arch_memory_barrier() {
+ asm volatile("" ::: "memory");
+}
+
+static inline void
+ao_arch_irq_check(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ uint32_t basepri;
+ asm("mrs %0,basepri" : "=r" (basepri));
+ if (basepri == 0)
+ ao_panic(AO_PANIC_IRQ);
+#else
+ uint32_t primask;
+ asm("mrs %0,primask" : "=r" (primask));
+ if ((primask & 1) == 0)
+ ao_panic(AO_PANIC_IRQ);
+#endif
+}
+
+#if HAS_TASK
+static inline void
+ao_arch_init_stack(struct ao_task *task, void *start)
+{
+ uint32_t *sp = (uint32_t *) ((void*) task->stack + AO_STACK_SIZE);
+ uint32_t a = (uint32_t) start;
+ int i;
+
+ /* Return address (goes into LR) */
+ ARM_PUSH32(sp, a);
+
+ /* Clear register values r0-r12 */
+ i = 13;
+ while (i--)
+ ARM_PUSH32(sp, 0);
+
+ /* APSR */
+ ARM_PUSH32(sp, 0);
+
+ /* Clear register values s0-s31 */
+ i = 32;
+ while (i--)
+ ARM_PUSH32(sp, 0);
+
+ /* FPSCR */
+ ARM_PUSH32(sp, 0);
+
+ /* BASEPRI with interrupts enabled */
+ ARM_PUSH32(sp, 0);
+
+ task->sp = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+ /* Save general registers */
+ asm("push {r0-r12,lr}");
+
+ /* Save APSR */
+ asm("mrs r0,apsr");
+ asm("push {r0}");
+
+ /* Save FPU registers */
+ asm("vpush {s0-s15}");
+ asm("vpush {s16-s31}");
+
+ /* Save FPSCR */
+ asm("vmrs r0,fpscr");
+ asm("push {r0}");
+
+#ifdef AO_NONMASK_INTERRUPTS
+ /* Save BASEPRI */
+ asm("mrs r0,basepri");
+#else
+ /* Save PRIMASK */
+ asm("mrs r0,primask");
+#endif
+ asm("push {r0}");
+}
+
+static inline void ao_arch_save_stack(void) {
+ uint32_t *sp;
+ asm("mov %0,sp" : "=&r" (sp) );
+ ao_cur_task->sp = (sp);
+}
+
+static inline void ao_arch_restore_stack(void) {
+ /* Switch stacks */
+ asm("mov sp, %0" : : "r" (ao_cur_task->sp) );
+
+#ifdef AO_NONMASK_INTERRUPTS
+ /* Restore BASEPRI */
+ asm("pop {r0}");
+ asm("msr basepri,r0");
+#else
+ /* Restore PRIMASK */
+ asm("pop {r0}");
+ asm("msr primask,r0");
+#endif
+
+ /* Restore FPSCR */
+ asm("pop {r0}");
+ asm("vmsr fpscr,r0");
+
+ /* Restore FPU registers */
+ asm("vpop {s16-s31}");
+ asm("vpop {s0-s15}");
+
+ /* Restore APSR */
+ asm("pop {r0}");
+ asm("msr apsr_nczvq,r0");
+
+ /* Restore general registers */
+ asm("pop {r0-r12,lr}\n");
+
+ /* Return to calling function */
+ asm("bx lr");
+}
+
+#ifndef HAS_SAMPLE_PROFILE
+#define HAS_SAMPLE_PROFILE 0
+#endif
+
+#if DEBUG
+#define HAS_ARCH_VALIDATE_CUR_STACK 1
+
+static inline void
+ao_validate_cur_stack(void)
+{
+ uint8_t *psp;
+
+ asm("mrs %0,psp" : "=&r" (psp));
+ if (ao_cur_task &&
+ psp <= ao_cur_task->stack &&
+ psp >= ao_cur_task->stack - 256)
+ ao_panic(AO_PANIC_STACK);
+}
+#endif
+
+#if !HAS_SAMPLE_PROFILE
+#define HAS_ARCH_START_SCHEDULER 1
+
+static inline void ao_arch_start_scheduler(void) {
+ uint32_t sp;
+ uint32_t control;
+
+ asm("mrs %0,msp" : "=&r" (sp));
+ asm("msr psp,%0" : : "r" (sp));
+ asm("mrs %0,control" : "=r" (control));
+ control |= (1 << 1);
+ asm("msr control,%0" : : "r" (control));
+ asm("isb");
+}
+#endif
+
+#define ao_arch_isr_stack()
+
+#endif
+
+static inline void
+ao_arch_wait_interrupt(void) {
+#ifdef AO_NONMASK_INTERRUPTS
+ asm(
+ "dsb\n" /* Serialize data */
+ "isb\n" /* Serialize instructions */
+ "cpsid i\n" /* Block all interrupts */
+ "msr basepri,%0\n" /* Allow all interrupts through basepri */
+ "wfi\n" /* Wait for an interrupt */
+ "cpsie i\n" /* Allow all interrupts */
+ "msr basepri,%1\n" /* Block interrupts through basepri */
+ : : "r" (0), "r" (AO_STM_NVIC_BASEPRI_MASK));
+#else
+ asm("\twfi\n");
+ ao_arch_release_interrupts();
+ ao_arch_block_interrupts();
+#endif
+}
+
+#define ao_arch_critical(b) do { \
+ uint32_t __mask = ao_arch_irqsave(); \
+ do { b } while (0); \
+ ao_arch_irqrestore(__mask); \
+ } while (0)
+
+/* 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)
+
+/* usart */
+
+void
+ao_usart_init(void);
+
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/stm32f4/ao_exti.h b/src/stm32f4/ao_exti.h
new file mode 100644
index 00000000..0216f352
--- /dev/null
+++ b/src/stm32f4/ao_exti.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_EXTI_H_
+#define _AO_EXTI_H_
+
+#define AO_EXTI_MODE_RISING 1
+#define AO_EXTI_MODE_FALLING 2
+#define AO_EXTI_MODE_PULL_NONE AO_GPIO_MODE_PULL_NONE
+#define AO_EXTI_MODE_PULL_UP AO_GPIO_MODE_PULL_UP
+#define AO_EXTI_MODE_PULL_DOWN AO_GPIO_MODE_PULL_DOWN
+#define AO_EXTI_PRIORITY_LOW 16
+#define AO_EXTI_PRIORITY_MED 0
+#define AO_EXTI_PRIORITY_HIGH 32
+#define AO_EXTI_PIN_NOCONFIGURE 64
+
+void
+ao_exti_setup(struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)());
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)());
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_H_ */
diff --git a/src/stm32f4/ao_exti_stm32f4.c b/src/stm32f4/ao_exti_stm32f4.c
new file mode 100644
index 00000000..1e288f9c
--- /dev/null
+++ b/src/stm32f4/ao_exti_stm32f4.c
@@ -0,0 +1,162 @@
+/*
+ * 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_exti.h>
+
+static void (*ao_exti_callback[16])(void);
+
+uint32_t ao_last_exti;
+
+static void ao_exti_one_isr(uint8_t pin) {
+ uint32_t pending = (ao_last_exti = stm_exti.pr) & (1 << pin);
+
+ stm_exti.pr = pending;
+ if (pending && ao_exti_callback[pin])
+ (*ao_exti_callback[pin])();
+}
+
+static void ao_exti_range_isr(uint8_t first, uint8_t last, uint16_t mask) {
+ uint16_t pending = (ao_last_exti = stm_exti.pr) & mask;
+ uint8_t pin;
+ static uint16_t last_mask;
+ static uint8_t last_pin;
+
+ if (pending == last_mask) {
+ stm_exti.pr = last_mask;
+ (*ao_exti_callback[last_pin])();
+ return;
+ }
+ stm_exti.pr = pending;
+ for (pin = first; pin <= last; pin++)
+ if ((pending & ((uint32_t) 1 << pin)) && ao_exti_callback[pin]) {
+ last_mask = (1 << pin);
+ last_pin = pin;
+ (*ao_exti_callback[pin])();
+ }
+}
+
+void stm_exti0_isr(void) { ao_exti_one_isr(0); }
+void stm_exti1_isr(void) { ao_exti_one_isr(1); }
+void stm_exti2_isr(void) { ao_exti_one_isr(2); }
+void stm_exti3_isr(void) { ao_exti_one_isr(3); }
+void stm_exti4_isr(void) { ao_exti_one_isr(4); }
+void stm_exti9_5_isr(void) { ao_exti_range_isr(5, 9, 0x3e0); }
+void stm_exti15_10_isr(void) { ao_exti_range_isr(10, 15, 0xfc00); }
+
+void
+ao_exti_setup (struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)(void)) {
+ uint32_t mask = 1 << pin;
+ uint32_t pupdr;
+ uint8_t irq;
+ uint8_t prio;
+
+ ao_exti_callback[pin] = callback;
+
+ /* configure gpio to interrupt routing */
+ stm_exticr_set(gpio, pin);
+
+ if (!(mode & AO_EXTI_PIN_NOCONFIGURE)) {
+ /* configure pin as input, setting selected pull-up/down mode */
+ stm_moder_set(gpio, pin, STM_MODER_INPUT);
+ switch (mode & (AO_EXTI_MODE_PULL_UP|AO_EXTI_MODE_PULL_DOWN)) {
+ case 0:
+ default:
+ pupdr = STM_PUPDR_NONE;
+ break;
+ case AO_EXTI_MODE_PULL_UP:
+ pupdr = STM_PUPDR_PULL_UP;
+ break;
+ case AO_EXTI_MODE_PULL_DOWN:
+ pupdr = STM_PUPDR_PULL_DOWN;
+ break;
+ }
+ stm_pupdr_set(gpio, pin, pupdr);
+ }
+
+ /* Set interrupt mask and rising/falling mode */
+ stm_exti.imr &= ~mask;
+ if (mode & AO_EXTI_MODE_RISING)
+ stm_exti.rtsr |= mask;
+ else
+ stm_exti.rtsr &= ~mask;
+ if (mode & AO_EXTI_MODE_FALLING)
+ stm_exti.ftsr |= mask;
+ else
+ stm_exti.ftsr &= ~mask;
+
+ if (pin <= 4)
+ irq = STM_ISR_EXTI0_POS + pin;
+ else if (pin <= 9)
+ irq = STM_ISR_EXTI9_5_POS;
+ else
+ irq = STM_ISR_EXTI15_10_POS;
+
+ /* Set priority */
+ prio = AO_STM_NVIC_MED_PRIORITY;
+ if (mode & AO_EXTI_PRIORITY_LOW)
+ prio = AO_STM_NVIC_LOW_PRIORITY;
+ else if (mode & AO_EXTI_PRIORITY_HIGH)
+ prio = AO_STM_NVIC_HIGH_PRIORITY;
+
+ stm_nvic_set_priority(irq, prio);
+ stm_nvic_set_enable(irq);
+}
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode) {
+ (void) gpio;
+
+ uint32_t mask = 1 << pin;
+
+ if (mode & AO_EXTI_MODE_RISING)
+ stm_exti.rtsr |= mask;
+ else
+ stm_exti.rtsr &= ~mask;
+ if (mode & AO_EXTI_MODE_FALLING)
+ stm_exti.ftsr |= mask;
+ else
+ stm_exti.ftsr &= ~mask;
+}
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)()) {
+ (void) gpio;
+ ao_exti_callback[pin] = callback;
+}
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin) {
+ uint32_t mask = (1 << pin);
+ (void) gpio;
+ stm_exti.pr = mask;
+ stm_exti.imr |= mask;
+}
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin) {
+ uint32_t mask = (1 << pin);
+ (void) gpio;
+ stm_exti.imr &= ~mask;
+ stm_exti.pr = mask;
+}
+
+void
+ao_exti_init(void)
+{
+}
diff --git a/src/stm32f4/ao_interrupt.c b/src/stm32f4/ao_interrupt.c
new file mode 100644
index 00000000..24f56abc
--- /dev/null
+++ b/src/stm32f4/ao_interrupt.c
@@ -0,0 +1,290 @@
+/*
+ * 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(exti15_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, exti15_10),
+ i(0xe4, rtc_alarm),
+ i(0xe8, otg_fs_wkup),
+ i(0xec, tim8_brk_tim12),
+ i(0xf0, tim8_up_tim13),
+ i(0xf4, tim8_trg_com_tim14),
+ i(0xf8, tim8_cc),
+ i(0xfc, dma1_stream7),
+ i(0x100, fsmc),
+ i(0x104, sdio),
+ i(0x108, tim5),
+ i(0x10c, spi3),
+ i(0x110, uart4),
+ i(0x114, uart5),
+ i(0x118,tim6_glb_it),
+ i(0x11c, tim7),
+ i(0x120, dma2_stream0),
+ i(0x124, dma2_stream1),
+ i(0x128, dma2_stream2),
+ i(0x12c, dma2_stream3),
+ i(0x130, dma2_stream4),
+ i(0x134, dfsdm1_flt0),
+ i(0x138, dfsdm1_flt1),
+ i(0x13c, can2_tx),
+ i(0x140, can2_rx0),
+ i(0x144, can2_rx1),
+ i(0x148, can2_sce),
+ i(0x14c, otg_fs),
+ i(0x150, dma2_stream5),
+ i(0x154, dma2_stream6),
+ i(0x158, dma2_stream7),
+ i(0x15c, usart6),
+ i(0x160, i2c3_ev),
+ i(0x164, i2c3_er),
+ i(0x168, can3_tx),
+ i(0x16c, can3_rx0),
+ i(0x170, can3_rx1),
+ i(0x174, can3_sce),
+ i(0x17c, crypto),
+ i(0x180, rng),
+ i(0x184, fpu),
+ i(0x188, uart7),
+ i(0x18c, uart8),
+ i(0x190, spi4),
+ i(0x194, spi5),
+ i(0x19c, sai1),
+ i(0x1a0, uart9),
+ i(0x1a4, uart10),
+ i(0x1b0, quad_spi),
+ i(0x1bc, i2cfmp1_ev),
+ i(0x1c0, i2cfmp1_er),
+ i(0x1c4, exti23),
+ i(0x1c8, dfsdm2_flt0),
+ i(0x1cc, dfsdm2_flt1),
+ i(0x1d0, dfsdm2_flt2),
+ i(0x1d4, dfsdm2_flt3),
+};
diff --git a/src/stm32f4/ao_serial_stm32f4.c b/src/stm32f4/ao_serial_stm32f4.c
new file mode 100644
index 00000000..bcecc791
--- /dev/null
+++ b/src/stm32f4/ao_serial_stm32f4.c
@@ -0,0 +1,544 @@
+/*
+ * 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_exti.h>
+
+static int
+_ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+ if (!ao_fifo_empty(usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+ if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts) == 1) {
+ ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+ return 0;
+ }
+#endif
+ if (usart->reg->sr & (1 << STM_USART_SR_TXE))
+ {
+ usart->tx_running = 1;
+ usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
+ ao_fifo_remove(usart->tx_fifo, usart->reg->dr);
+ ao_wakeup(&usart->tx_fifo);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+_ao_usart_cts(struct ao_stm_usart *usart)
+{
+ if (_ao_usart_tx_start(usart))
+ ao_exti_disable(usart->gpio_cts, usart->pin_cts);
+}
+#endif
+
+static void
+_ao_usart_rx(struct ao_stm_usart *usart, int is_stdin)
+{
+ if (usart->reg->sr & (1 << STM_USART_SR_RXNE)) {
+ if (!ao_fifo_full(usart->rx_fifo)) {
+ ao_fifo_insert(usart->rx_fifo, usart->reg->dr);
+ ao_wakeup(&usart->rx_fifo);
+ if (is_stdin)
+ ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+ /* If the fifo is nearly full, turn off RTS and wait
+ * for it to drain a bunch
+ */
+ if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, 1);
+ usart->rts = 0;
+ }
+#endif
+ } else {
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_RXNEIE);
+ }
+ }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int is_stdin)
+{
+ _ao_usart_rx(usart, is_stdin);
+
+ if (!_ao_usart_tx_start(usart))
+ usart->reg->cr1 &= ~(1<< STM_USART_CR1_TXEIE);
+
+ if (usart->reg->sr & (1 << STM_USART_SR_TC)) {
+ usart->tx_running = 0;
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_TCIE);
+ if (usart->draining) {
+ usart->draining = 0;
+ ao_wakeup(&usart->tx_fifo);
+ }
+ }
+}
+
+static int
+_ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+ int c;
+
+ if (ao_fifo_empty(usart->rx_fifo))
+ c = AO_READ_AGAIN;
+ else {
+ uint8_t u;
+ ao_fifo_remove(usart->rx_fifo,u);
+ if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
+ if (ao_fifo_barely(usart->rx_fifo))
+ usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
+ }
+#if HAS_SERIAL_SW_FLOW
+ /* If we've cleared RTS, check if there's space now and turn it back on */
+ if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, 0);
+ usart->rts = 1;
+ }
+#endif
+ c = u;
+ }
+ return c;
+}
+
+static char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+ ao_sleep(&usart->rx_fifo);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout)
+{
+ return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+
+static void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+ ao_arch_block_interrupts();
+ while (ao_fifo_full(usart->tx_fifo))
+ ao_sleep(&usart->tx_fifo);
+ ao_fifo_insert(usart->tx_fifo, c);
+ _ao_usart_tx_start(usart);
+ ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+ ao_arch_block_interrupts();
+ while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
+ usart->draining = 1;
+ ao_sleep(&usart->tx_fifo);
+ }
+ ao_arch_release_interrupts();
+}
+
+static const struct {
+ uint32_t brr;
+} ao_usart_speeds[] = {
+ [AO_SERIAL_SPEED_4800] = {
+ AO_PCLK1 / 4800
+ },
+ [AO_SERIAL_SPEED_9600] = {
+ AO_PCLK1 / 9600
+ },
+ [AO_SERIAL_SPEED_19200] = {
+ AO_PCLK1 / 19200
+ },
+ [AO_SERIAL_SPEED_57600] = {
+ AO_PCLK1 / 57600
+ },
+ [AO_SERIAL_SPEED_115200] = {
+ AO_PCLK1 / 115200
+ },
+};
+
+static void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
+{
+ if (speed > AO_SERIAL_SPEED_115200)
+ return;
+ usart->reg->brr = ao_usart_speeds[speed].brr;
+}
+
+static void
+ao_usart_init(struct ao_stm_usart *usart, int hw_flow)
+{
+ usart->reg->cr1 = ((0 << STM_USART_CR1_OVER8) |
+ (1 << STM_USART_CR1_UE) |
+ (0 << STM_USART_CR1_M) |
+ (0 << STM_USART_CR1_WAKE) |
+ (0 << STM_USART_CR1_PCE) |
+ (0 << STM_USART_CR1_PS) |
+ (0 << STM_USART_CR1_PEIE) |
+ (0 << STM_USART_CR1_TXEIE) |
+ (0 << STM_USART_CR1_TCIE) |
+ (1 << STM_USART_CR1_RXNEIE) |
+ (0 << STM_USART_CR1_IDLEIE) |
+ (1 << STM_USART_CR1_TE) |
+ (1 << STM_USART_CR1_RE) |
+ (0 << STM_USART_CR1_RWU) |
+ (0 << STM_USART_CR1_SBK));
+
+ usart->reg->cr2 = ((0 << STM_USART_CR2_LINEN) |
+ (STM_USART_CR2_STOP_1 << STM_USART_CR2_STOP) |
+ (0 << STM_USART_CR2_CLKEN) |
+ (0 << STM_USART_CR2_CPOL) |
+ (0 << STM_USART_CR2_CPHA) |
+ (0 << STM_USART_CR2_LBCL) |
+ (0 << STM_USART_CR2_LBDIE) |
+ (0 << STM_USART_CR2_LBDL) |
+ (0 << STM_USART_CR2_ADD));
+
+ usart->reg->cr3 = ((0 << STM_USART_CR3_ONEBITE) |
+ (0 << STM_USART_CR3_CTSIE) |
+ (0 << STM_USART_CR3_CTSE) |
+ (0 << STM_USART_CR3_RTSE) |
+ (0 << STM_USART_CR3_DMAT) |
+ (0 << STM_USART_CR3_DMAR) |
+ (0 << STM_USART_CR3_SCEN) |
+ (0 << STM_USART_CR3_NACK) |
+ (0 << STM_USART_CR3_HDSEL) |
+ (0 << STM_USART_CR3_IRLP) |
+ (0 << STM_USART_CR3_IREN) |
+ (0 << STM_USART_CR3_EIE));
+
+ if (hw_flow)
+ usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+ (1 << STM_USART_CR3_RTSE));
+
+ /* Pick a 9600 baud rate */
+ ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+}
+
+#if HAS_SERIAL_HW_FLOW
+static void
+ao_usart_set_flow(struct ao_stm_usart *usart)
+{
+}
+#endif
+
+#if HAS_SERIAL_1
+
+struct ao_stm_usart ao_stm_usart1;
+
+void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart1, c);
+}
+
+int
+_ao_serial1_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart1);
+}
+
+uint8_t
+_ao_serial1_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
+}
+
+void
+ao_serial1_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart1);
+ ao_usart_set_speed(&ao_stm_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+#if HAS_SERIAL_2
+
+struct ao_stm_usart ao_stm_usart2;
+
+void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial2_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart2, c);
+}
+
+int
+_ao_serial2_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart2);
+}
+
+uint8_t
+_ao_serial2_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
+}
+
+void
+ao_serial2_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart2);
+}
+
+void
+ao_serial2_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart2);
+ ao_usart_set_speed(&ao_stm_usart2, speed);
+}
+
+#if HAS_SERIAL_SW_FLOW
+void
+ao_serial2_cts(void)
+{
+ _ao_usart_cts(&ao_stm_usart2);
+}
+#endif
+
+#endif /* HAS_SERIAL_2 */
+
+#if HAS_SERIAL_3
+
+struct ao_stm_usart ao_stm_usart3;
+
+void stm_usart3_isr(void) { ao_usart_isr(&ao_stm_usart3, USE_SERIAL_3_STDIN); }
+
+char
+ao_serial3_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart3);
+}
+
+void
+ao_serial3_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart3, c);
+}
+
+int
+_ao_serial3_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart3);
+}
+
+uint8_t
+_ao_serial3_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart3, timeout);
+}
+
+void
+ao_serial3_set_speed(uint8_t speed)
+{
+ ao_usart_drain(&ao_stm_usart3);
+ ao_usart_set_speed(&ao_stm_usart3, speed);
+}
+
+void
+ao_serial3_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart3);
+}
+#endif /* HAS_SERIAL_3 */
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
+ void (*isr)(void),
+ struct stm_gpio *port_rts,
+ int pin_rts,
+ struct stm_gpio *port_cts,
+ int pin_cts)
+{
+ /* Pull RTS low to note that there's space in the FIFO
+ */
+ ao_enable_output(port_rts, pin_rts, 0);
+ usart->gpio_rts = port_rts;
+ usart->pin_rts = pin_rts;
+ usart->rts = 1;
+
+ ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
+ usart->gpio_cts = port_cts;
+ usart->pin_cts = pin_cts;
+}
+#endif
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+ /*
+ * TX RX
+ * PA9 PA10
+ * PB6 PB7 *
+ */
+
+#if SERIAL_1_PA9_PA10
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+ stm_afr_set(&stm_gpioa, 9, STM_AFR_AF7);
+ stm_afr_set(&stm_gpioa, 10, STM_AFR_AF7);
+#else
+#if SERIAL_1_PB6_PB7
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+ stm_afr_set(&stm_gpiob, 6, STM_AFR_AF7);
+ stm_afr_set(&stm_gpiob, 7, STM_AFR_AF7);
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+#endif
+ /* Enable USART */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+
+ ao_stm_usart1.reg = &stm_usart1;
+ ao_usart_init(&ao_stm_usart1, 0);
+
+ stm_nvic_set_enable(STM_ISR_USART1_POS);
+ stm_nvic_set_priority(STM_ISR_USART1_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
+ ao_add_stdio(_ao_serial1_pollchar,
+ ao_serial1_putchar,
+ NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_2
+ /*
+ * TX RX
+ * PA2 PA3
+ * PD5 PD6
+ */
+
+#if SERIAL_2_PA2_PA3
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+ stm_afr_set(&stm_gpioa, 2, STM_AFR_AF7);
+ stm_afr_set(&stm_gpioa, 3, STM_AFR_AF7);
+# if USE_SERIAL_2_FLOW
+# if USE_SERIAL_2_SW_FLOW
+ ao_serial_set_sw_rts_cts(&ao_stm_usart2,
+ ao_serial2_cts,
+ SERIAL_2_PORT_RTS,
+ SERIAL_2_PIN_RTS,
+ SERIAL_2_PORT_CTS,
+ SERIAL_2_PIN_CTS);
+# else
+ stm_afr_set(&stm_gpioa, 0, STM_AFR_AF7);
+ stm_afr_set(&stm_gpioa, 1, STM_AFR_AF7);
+# endif
+# endif
+#else
+#if SERIAL_2_PD5_PD6
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+ stm_afr_set(&stm_gpiod, 5, STM_AFR_AF7);
+ stm_afr_set(&stm_gpiod, 6, STM_AFR_AF7);
+#if USE_SERIAL_2_FLOW
+#error "Don't know how to set flowcontrol for serial 2 on PD"
+#endif
+#else
+#error "No SERIAL_2 port configuration specified"
+#endif
+#endif
+ /* Enable USART */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
+
+ ao_stm_usart2.reg = &stm_usart2;
+ ao_usart_init(&ao_stm_usart2, USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW);
+
+ stm_nvic_set_enable(STM_ISR_USART2_POS);
+ stm_nvic_set_priority(STM_ISR_USART2_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
+ ao_add_stdio(_ao_serial2_pollchar,
+ ao_serial2_putchar,
+ NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_3
+ /*
+ * TX RX
+ * PB10 PB11
+ * PC10 PC11
+ * PD8 PD9
+ */
+#if SERIAL_3_PB10_PB11
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+ stm_afr_set(&stm_gpiob, 10, STM_AFR_AF7);
+ stm_afr_set(&stm_gpiob, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PC10_PC11
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN);
+
+ stm_afr_set(&stm_gpioc, 10, STM_AFR_AF7);
+ stm_afr_set(&stm_gpioc, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PD8_PD9
+ stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+ stm_afr_set(&stm_gpiod, 8, STM_AFR_AF7);
+ stm_afr_set(&stm_gpiod, 9, STM_AFR_AF7);
+#else
+#error "No SERIAL_3 port configuration specified"
+#endif
+#endif
+#endif
+ /* Enable USART */
+ stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART3EN);
+
+ ao_stm_usart3.reg = &stm_usart3;
+ ao_usart_init(&ao_stm_usart3, 0);
+
+ stm_nvic_set_enable(STM_ISR_USART3_POS);
+ stm_nvic_set_priority(STM_ISR_USART3_POS, AO_STM_NVIC_MED_PRIORITY);
+#if USE_SERIAL_3_STDIN && !DELAY_SERIAL_3_STDIN
+ ao_add_stdio(_ao_serial3_pollchar,
+ ao_serial3_putchar,
+ NULL);
+#endif
+#endif
+}
diff --git a/src/stm32f4/ao_timer.c b/src/stm32f4/ao_timer.c
new file mode 100644
index 00000000..d6ef9bc3
--- /dev/null
+++ b/src/stm32f4/ao_timer.c
@@ -0,0 +1,284 @@
+/*
+ * 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");
+
+#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;
+
+ /* Clock configuration register DCKCFGR2; mostly make sure USB
+ * gets clocked from PLL_Q
+ */
+ stm_rcc.dckcfgr2 = ((STM_RCC_DCKCFGR2_LPTIMER1SEL_APB << STM_RCC_DCKCFGR2_LPTIMER1SEL) |
+ (STM_RCC_DCKCFGR2_SDIOSEL_CK_48MHZ << STM_RCC_DCKCFGR2_SDIOSEL) |
+ (STM_RCC_DCKCFGR2_CK48MSEL_PLL_Q << STM_RCC_DCKCFGR2_CK48MSEL) |
+ (STM_RCC_DCKCFGR2_I2CFMP1SEL_APB << STM_RCC_DCKCFGR2_I2CFMP1SEL));
+
+ /* 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 == 2
+#define AO_RCC_PLLCFGR_PLLP STM_RCC_PLLCFGR_PLLP_DIV_2
+#endif
+#if AO_PLL1_P == 4
+#define AO_RCC_PLLCFGR_PLLP STM_RCC_PLLCFGR_PLLP_DIV_4
+#endif
+#if AO_PLL1_P == 6
+#define AO_RCC_PLLCFGR_PLLP STM_RCC_PLLCFGR_PLLP_DIV_6
+#endif
+#if AO_PLL1_P == 8
+#define AO_RCC_PLLCFGR_PLLP STM_RCC_PLLCFGR_PLLP_DIV_8
+#endif
+ pllcfgr |= (AO_RCC_PLLCFGR_PLLP << STM_RCC_PLLCFGR_PLLP);
+ pllcfgr |= (AO_PLL1_Q << STM_RCC_PLLCFGR_PLLQ);
+ pllcfgr |= (AO_PLL1_R << STM_RCC_PLLCFGR_PLLR);
+ /* PLL source */
+ pllcfgr &= ~(1 << STM_RCC_PLLCFGR_PLLSRC);
+#if AO_HSI
+ pllcfgr |= (STM_RCC_PLLCFGR_PLLSRC_HSI << STM_RCC_PLLCFGR_PLLSRC);
+#endif
+#if AO_HSE
+ pllcfgr |= (STM_RCC_PLLCFGR_PLLSRC_HSE << STM_RCC_PLLCFGR_PLLSRC);
+#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 AO_HSE
+ /* Disable HSI clock */
+ stm_rcc.cr &= ~(1 << STM_RCC_CR_HSION);
+#endif
+
+ /* Clear reset flags */
+ stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+
+#if DEBUG_THE_CLOCK
+ /* Output PLL clock on PA8 and SYCLK on PC9 for measurments */
+
+ stm_rcc.ahb1enr |= ((1 << STM_RCC_AHB1ENR_IOPAEN) |
+ (1 << STM_RCC_AHB1ENR_IOPCEN));
+
+ 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_HIGH);
+
+ stm_afr_set(&stm_gpioc, 9, STM_AFR_AF0);
+ stm_moder_set(&stm_gpioc, 9, STM_MODER_ALTERNATE);
+ stm_ospeedr_set(&stm_gpioc, 9, STM_OSPEEDR_HIGH);
+
+ cfgr = stm_rcc.cfgr;
+ cfgr &= 0x001fffff;
+ cfgr |= ((0 << STM_RCC_CFGR_MCO2) |
+ (6 << STM_RCC_CFGR_MCO2PRE) |
+ (6 << STM_RCC_CFGR_MCO1PRE) |
+ (2 << STM_RCC_CFGR_MCO1));
+ stm_rcc.cfgr = cfgr;
+#endif
+}
diff --git a/src/stm32f4/ao_usart_stm32f4.c b/src/stm32f4/ao_usart_stm32f4.c
new file mode 100644
index 00000000..28331b1e
--- /dev/null
+++ b/src/stm32f4/ao_usart_stm32f4.c
@@ -0,0 +1,350 @@
+/*
+ * 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_exti.h>
+
+/* ao_serial_stm.c */
+struct ao_stm_usart {
+ struct ao_fifo rx_fifo;
+ struct ao_fifo tx_fifo;
+ struct stm_usart *reg;
+ uint32_t clk;
+ uint8_t tx_running;
+ uint8_t draining;
+#if HAS_SERIAL_SW_FLOW
+ /* RTS - 0 if we have FIFO space, 1 if not
+ * CTS - 0 if we can send, 0 if not
+ */
+ struct stm_gpio *gpio_rts;
+ struct stm_gpio *gpio_cts;
+ uint8_t pin_rts;
+ uint8_t pin_cts;
+ uint8_t rts;
+#endif
+};
+
+static int
+_ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+ if (!ao_fifo_empty(usart->tx_fifo)) {
+#if HAS_SERIAL_SW_FLOW
+ if (usart->gpio_cts && ao_gpio_get(usart->gpio_cts, usart->pin_cts) == 1) {
+ ao_exti_enable(usart->gpio_cts, usart->pin_cts);
+ return 0;
+ }
+#endif
+ if (usart->reg->sr & (1 << STM_USART_SR_TXE))
+ {
+ usart->tx_running = 1;
+ usart->reg->cr1 |= (1 << STM_USART_CR1_TXEIE) | (1 << STM_USART_CR1_TCIE);
+ ao_fifo_remove(usart->tx_fifo, usart->reg->dr);
+ ao_wakeup(&usart->tx_fifo);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#if HAS_SERIAL_SW_FLOW
+static void
+_ao_usart_cts(struct ao_stm_usart *usart)
+{
+ if (_ao_usart_tx_start(usart))
+ ao_exti_disable(usart->gpio_cts, usart->pin_cts);
+}
+#endif
+
+static void
+_ao_usart_rx(struct ao_stm_usart *usart, int is_stdin)
+{
+ if (usart->reg->sr & (1 << STM_USART_SR_RXNE)) {
+ if (!ao_fifo_full(usart->rx_fifo)) {
+ ao_fifo_insert(usart->rx_fifo, usart->reg->dr);
+ ao_wakeup(&usart->rx_fifo);
+ if (is_stdin)
+ ao_wakeup(&ao_stdin_ready);
+#if HAS_SERIAL_SW_FLOW
+ /* If the fifo is nearly full, turn off RTS and wait
+ * for it to drain a bunch
+ */
+ if (usart->gpio_rts && ao_fifo_mostly(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, 1);
+ usart->rts = 0;
+ }
+#endif
+ } else {
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_RXNEIE);
+ }
+ }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int is_stdin)
+{
+ _ao_usart_rx(usart, is_stdin);
+
+ if (!_ao_usart_tx_start(usart))
+ usart->reg->cr1 &= ~(1<< STM_USART_CR1_TXEIE);
+
+ if (usart->reg->sr & (1 << STM_USART_SR_TC)) {
+ usart->tx_running = 0;
+ usart->reg->cr1 &= ~(1 << STM_USART_CR1_TCIE);
+ if (usart->draining) {
+ usart->draining = 0;
+ ao_wakeup(&usart->tx_fifo);
+ }
+ }
+}
+
+static int
+_ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+ int c;
+
+ if (ao_fifo_empty(usart->rx_fifo))
+ c = AO_READ_AGAIN;
+ else {
+ uint8_t u;
+ ao_fifo_remove(usart->rx_fifo,u);
+ if ((usart->reg->cr1 & (1 << STM_USART_CR1_RXNEIE)) == 0) {
+ if (ao_fifo_barely(usart->rx_fifo))
+ usart->reg->cr1 |= (1 << STM_USART_CR1_RXNEIE);
+ }
+#if HAS_SERIAL_SW_FLOW
+ /* If we've cleared RTS, check if there's space now and turn it back on */
+ if (usart->gpio_rts && usart->rts == 0 && ao_fifo_barely(usart->rx_fifo)) {
+ ao_gpio_set(usart->gpio_rts, usart->pin_rts, 0);
+ usart->rts = 1;
+ }
+#endif
+ c = u;
+ }
+ return c;
+}
+
+static char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+ int c;
+ ao_arch_block_interrupts();
+ while ((c = _ao_usart_pollchar(usart)) == AO_READ_AGAIN)
+ ao_sleep(&usart->rx_fifo);
+ ao_arch_release_interrupts();
+ return (char) c;
+}
+
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout)
+{
+ return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+
+static void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+ ao_arch_block_interrupts();
+ while (ao_fifo_full(usart->tx_fifo))
+ ao_sleep(&usart->tx_fifo);
+ ao_fifo_insert(usart->tx_fifo, c);
+ _ao_usart_tx_start(usart);
+ ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+ ao_arch_block_interrupts();
+ while (!ao_fifo_empty(usart->tx_fifo) || usart->tx_running) {
+ usart->draining = 1;
+ ao_sleep(&usart->tx_fifo);
+ }
+ ao_arch_release_interrupts();
+}
+
+static void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint32_t speed)
+{
+ if (speed > 115200)
+ return;
+ usart->reg->brr = usart->clk / speed;
+}
+
+static void
+_ao_usart_init(struct ao_stm_usart *usart, int hw_flow)
+{
+ usart->reg->cr1 = ((0 << STM_USART_CR1_OVER8) |
+ (1 << STM_USART_CR1_UE) |
+ (0 << STM_USART_CR1_M) |
+ (0 << STM_USART_CR1_WAKE) |
+ (0 << STM_USART_CR1_PCE) |
+ (0 << STM_USART_CR1_PS) |
+ (0 << STM_USART_CR1_PEIE) |
+ (0 << STM_USART_CR1_TXEIE) |
+ (0 << STM_USART_CR1_TCIE) |
+ (1 << STM_USART_CR1_RXNEIE) |
+ (0 << STM_USART_CR1_IDLEIE) |
+ (1 << STM_USART_CR1_TE) |
+ (1 << STM_USART_CR1_RE) |
+ (0 << STM_USART_CR1_RWU) |
+ (0 << STM_USART_CR1_SBK));
+
+ usart->reg->cr2 = ((0 << STM_USART_CR2_LINEN) |
+ (STM_USART_CR2_STOP_1 << STM_USART_CR2_STOP) |
+ (0 << STM_USART_CR2_CLKEN) |
+ (0 << STM_USART_CR2_CPOL) |
+ (0 << STM_USART_CR2_CPHA) |
+ (0 << STM_USART_CR2_LBCL) |
+ (0 << STM_USART_CR2_LBDIE) |
+ (0 << STM_USART_CR2_LBDL) |
+ (0 << STM_USART_CR2_ADD));
+
+ usart->reg->cr3 = ((0 << STM_USART_CR3_ONEBIT) |
+ (0 << STM_USART_CR3_CTSIE) |
+ (0 << STM_USART_CR3_CTSE) |
+ (0 << STM_USART_CR3_RTSE) |
+ (0 << STM_USART_CR3_DMAT) |
+ (0 << STM_USART_CR3_DMAR) |
+ (0 << STM_USART_CR3_SCEN) |
+ (0 << STM_USART_CR3_NACK) |
+ (0 << STM_USART_CR3_HDSEL) |
+ (0 << STM_USART_CR3_IRLP) |
+ (0 << STM_USART_CR3_IREN) |
+ (0 << STM_USART_CR3_EIE));
+
+ if (hw_flow)
+ usart->reg->cr3 |= ((1 << STM_USART_CR3_CTSE) |
+ (1 << STM_USART_CR3_RTSE));
+
+ /* Pick a 9600 baud rate */
+ ao_usart_set_speed(usart, 9600);
+}
+
+#if HAS_SERIAL_HW_FLOW
+static void
+ao_usart_set_flow(struct ao_stm_usart *usart)
+{
+}
+#endif
+
+#if HAS_SERIAL_6
+
+struct ao_stm_usart ao_stm_usart6;
+
+void stm_usart6_isr(void) { ao_usart_isr(&ao_stm_usart6, USE_SERIAL_6_STDIN); }
+
+char
+ao_serial6_getchar(void)
+{
+ return ao_usart_getchar(&ao_stm_usart6);
+}
+
+void
+ao_serial6_putchar(char c)
+{
+ ao_usart_putchar(&ao_stm_usart6, c);
+}
+
+int
+_ao_serial6_pollchar(void)
+{
+ return _ao_usart_pollchar(&ao_stm_usart6);
+}
+
+uint8_t
+_ao_serial6_sleep_for(uint16_t timeout)
+{
+ return _ao_usart_sleep_for(&ao_stm_usart6, timeout);
+}
+
+void
+ao_serial6_set_speed(uint32_t speed)
+{
+ ao_usart_drain(&ao_stm_usart6);
+ ao_usart_set_speed(&ao_stm_usart6, speed);
+}
+
+void
+ao_serial6_drain(void)
+{
+ ao_usart_drain(&ao_stm_usart6);
+}
+#endif /* HAS_SERIAL_6 */
+
+#if HAS_SERIAL_SW_FLOW
+static void
+ao_serial_set_sw_rts_cts(struct ao_stm_usart *usart,
+ void (*isr)(void),
+ struct stm_gpio *port_rts,
+ int pin_rts,
+ struct stm_gpio *port_cts,
+ int pin_cts)
+{
+ /* Pull RTS low to note that there's space in the FIFO */
+ ao_enable_output(port_rts, pin_rts, 0);
+ usart->gpio_rts = port_rts;
+ usart->pin_rts = pin_rts;
+ usart->rts = 1;
+
+ ao_exti_setup(port_cts, pin_cts, AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_MED, isr);
+ usart->gpio_cts = port_cts;
+ usart->pin_cts = pin_cts;
+}
+#endif
+
+void
+ao_usart_init(void)
+{
+#if HAS_SERIAL_6
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART6EN);
+
+ ao_stm_usart6.reg = &stm_usart6;
+ ao_stm_usart6.clk = AO_P2CLK;
+
+ ao_enable_port(SERIAL_6_RX_PORT);
+ ao_enable_port(SERIAL_6_TX_PORT);
+
+ stm_afr_set(SERIAL_6_RX_PORT, SERIAL_6_RX_PIN, STM_AFR_AF8);
+ stm_afr_set(SERIAL_6_TX_PORT, SERIAL_6_TX_PIN, STM_AFR_AF8);
+
+ stm_nvic_set_enable(STM_ISR_USART6_POS);
+ stm_nvic_set_priority(STM_ISR_USART6_POS, AO_STM_NVIC_MED_PRIORITY);
+
+ _ao_usart_init(&ao_stm_usart6, USE_SERIAL_6_FLOW && !USE_SERIAL_6_SW_FLOW);
+
+# if USE_SERIAL_6_FLOW
+# if USE_SERIAL_6_SW_FLOW
+ ao_serial_set_sw_rts_cts(&ao_stm_usart6,
+ ao_serial6_cts,
+ SERIAL_6_PORT_RTS,
+ SERIAL_6_PIN_RTS,
+ SERIAL_6_PORT_CTS,
+ SERIAL_6_PIN_CTS);
+# else
+ stm_afr_set(SERIAL_6_PORT_RTS, SERIAL_6_PIN_RTS, STM_AFR_AF8);
+ stm_afr_set(SERIAL_6_PORT_CTS, SERIAL_6_PIN_CTS, STM_AFR_AF8);
+# endif
+#endif
+
+#if USE_SERIAL_6_STDIN && !DELAY_SERIAL_6_STDIN
+ ao_add_stdio(_ao_serial6_pollchar,
+ ao_serial6_putchar,
+ NULL);
+#endif
+#endif
+}
diff --git a/src/stm32f4/ao_usb_gen.c b/src/stm32f4/ao_usb_gen.c
new file mode 100644
index 00000000..760afad9
--- /dev/null
+++ b/src/stm32f4/ao_usb_gen.c
@@ -0,0 +1,712 @@
+/*
+ * 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_usb_gen.h"
+
+static uint8_t ao_usb_ep0_state;
+
+/* Pending EP0 IN data */
+static const uint8_t *ao_usb_ep0_in_data; /* Remaining data */
+static uint8_t ao_usb_ep0_in_len; /* Remaining amount */
+
+/* Temp buffer for smaller EP0 in data */
+static uint8_t ao_usb_ep0_in_buf[2];
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t ao_usb_ep0_out_len;
+
+/* System ram shadow of USB buffer; writing individual bytes is
+ * too much of a pain (sigh) */
+static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE];
+static uint8_t ao_usb_tx_count;
+
+static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE];
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR 0
+#define AO_USB_INT_EPR 1
+#define AO_USB_OUT_EPR 2
+#define AO_USB_IN_EPR 3
+
+/* Marks when we don't need to send an IN packet.
+ * This happens only when the last IN packet is not full,
+ * otherwise the host will expect to keep seeing packets.
+ * Send a zero-length packet as required
+ */
+static uint8_t ao_usb_in_flushed;
+
+/* Marks when we have delivered an IN packet to the hardware
+ * and it has not been received yet. ao_sleep on this address
+ * to wait for it to be delivered.
+ */
+static uint8_t ao_usb_in_pending;
+
+/* Marks when an OUT packet has been received by the hardware
+ * but not pulled to the shadow buffer.
+ */
+static uint8_t ao_usb_out_avail;
+uint8_t ao_usb_running;
+static uint8_t ao_usb_configuration;
+
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+static void
+ao_usb_set_address(uint8_t address)
+{
+ ao_usb_dev_set_address(address);
+ ao_usb_address_pending = 0;
+}
+
+#define TX_DBG 0
+#define RX_DBG 0
+
+#if TX_DBG
+#define _tx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _tx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _tx_dbg0(msg)
+#define _tx_dbg1(msg,value)
+#endif
+
+#if RX_DBG
+#define _rx_dbg0(msg) _dbg(__LINE__,msg,0)
+#define _rx_dbg1(msg,value) _dbg(__LINE__,msg,value)
+#else
+#define _rx_dbg0(msg)
+#define _rx_dbg1(msg,value)
+#endif
+
+#if TX_DBG || RX_DBG
+static void _dbg(int line, char *msg, uint32_t value);
+#endif
+
+/*
+ * Set just endpoint 0, for use during startup
+ */
+
+static void
+ao_usb_set_ep0(void)
+{
+ ao_usb_dev_ep0_init();
+
+ ao_usb_set_address(0);
+
+ ao_usb_running = 0;
+
+ /* Reset our internal state
+ */
+
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+ ao_usb_ep0_in_data = NULL;
+ ao_usb_ep0_in_len = 0;
+
+ ao_usb_ep0_out_data = 0;
+ ao_usb_ep0_out_len = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+#if 0
+ /* Set up the INT end point */
+ ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
+ ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+ ao_usb_init_ep(AO_USB_INT_EPR,
+ AO_USB_INT_EP);
+
+ /* Set up the OUT end point */
+ ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+ (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+ ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+ ao_usb_init_ep(AO_USB_OUT_EPR,
+ AO_USB_OUT_EP,
+ STM_USB_EPR_EP_TYPE_BULK,
+ STM_USB_EPR_STAT_RX_VALID,
+ STM_USB_EPR_STAT_TX_DISABLED);
+
+ /* Set up the IN end point */
+ ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr;
+ ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
+ ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+ ao_usb_sram_addr += AO_USB_IN_SIZE;
+
+ ao_usb_init_ep(AO_USB_IN_EPR,
+ AO_USB_IN_EP,
+ STM_USB_EPR_EP_TYPE_BULK,
+ STM_USB_EPR_STAT_RX_DISABLED,
+ STM_USB_EPR_STAT_TX_NAK);
+#endif
+
+ ao_usb_in_flushed = 0;
+ ao_usb_in_pending = 0;
+ ao_wakeup(&ao_usb_in_pending);
+
+ ao_usb_out_avail = 0;
+ ao_usb_configuration = 0;
+
+ ao_usb_running = 1;
+ ao_wakeup(&ao_usb_running);
+}
+
+static uint16_t control_count;
+static uint16_t in_count;
+static uint16_t out_count;
+#if USB_DEBUG
+static uint16_t int_count;
+static uint16_t reset_count;
+#endif
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+ uint8_t this_len;
+
+ /* Check to see if the endpoint is still busy */
+ if (ao_usb_dev_ep0_in_busy()) {
+ return;
+ }
+
+ this_len = ao_usb_ep0_in_len;
+ if (this_len > AO_USB_CONTROL_SIZE)
+ this_len = AO_USB_CONTROL_SIZE;
+
+ if (this_len < AO_USB_CONTROL_SIZE)
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+ ao_usb_ep0_in_len -= this_len;
+
+ ao_usb_dev_ep0_in(ao_usb_ep0_in_data, this_len);
+ ao_usb_ep0_in_data += this_len;
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+ uint16_t len;
+
+ len = ao_usb_dev_ep0_out(ao_usb_ep0_out_data, ao_usb_ep0_out_len);
+ ao_usb_ep0_out_len -= len;
+ ao_usb_ep0_out_data += len;
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+ ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+ ao_usb_ep0_in_len = 0;
+}
+
+static void
+ao_usb_ep0_in_queue_byte(uint8_t a)
+{
+ if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_buf))
+ ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_in_set(const uint8_t *data, uint8_t len)
+{
+ ao_usb_ep0_in_data = data;
+ ao_usb_ep0_in_len = len;
+}
+
+static void
+ao_usb_ep0_out_set(uint8_t *data, uint8_t len)
+{
+ ao_usb_ep0_out_data = data;
+ ao_usb_ep0_out_len = len;
+}
+
+static void
+ao_usb_ep0_in_start(uint16_t max)
+{
+ /* Don't send more than asked for */
+ if (ao_usb_ep0_in_len > max)
+ ao_usb_ep0_in_len = max;
+
+ ao_usb_dev_ep0_in(ao_usb_ep0_in_data, ao_usb_ep0_in_len);
+}
+
+struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value, uint16_t length)
+{
+ const uint8_t *descriptor;
+ uint8_t type = value >> 8;
+ uint8_t index = value;
+
+ descriptor = ao_usb_descriptors;
+ while (descriptor[0] != 0) {
+ if (descriptor[1] == type && index-- == 0) {
+ uint8_t len;
+ if (type == AO_USB_DESC_CONFIGURATION)
+ len = descriptor[2];
+ else
+ len = descriptor[0];
+ if (len > length)
+ len = length;
+ ao_usb_ep0_in_set(descriptor, len);
+ break;
+ }
+ descriptor += descriptor[0];
+ }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+ uint16_t setup_len;
+
+ /* Pull the setup packet out of the fifo */
+ setup_len = ao_usb_dev_ep0_out(&ao_usb_setup, 8);
+ if (setup_len != 8) {
+ return;
+ }
+
+ if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0)
+ ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+ else
+ ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+
+ ao_usb_ep0_in_reset();
+
+ switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+ case AO_USB_TYPE_STANDARD:
+ switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+ case AO_USB_RECIP_DEVICE:
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_SET_ADDRESS:
+ ao_usb_address = ao_usb_setup.value;
+ ao_usb_address_pending = 1;
+ break;
+ case AO_USB_REQ_GET_DESCRIPTOR:
+ ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length);
+ break;
+ case AO_USB_REQ_GET_CONFIGURATION:
+ ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+ break;
+ case AO_USB_REQ_SET_CONFIGURATION:
+ ao_usb_configuration = ao_usb_setup.value;
+ ao_usb_set_configuration();
+ break;
+ }
+ break;
+ case AO_USB_RECIP_INTERFACE:
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_GET_INTERFACE:
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ case AO_USB_REQ_SET_INTERFACE:
+ break;
+ }
+ break;
+ case AO_USB_RECIP_ENDPOINT:
+ switch(ao_usb_setup.request) {
+ case AO_USB_REQ_GET_STATUS:
+ ao_usb_ep0_in_queue_byte(0);
+ ao_usb_ep0_in_queue_byte(0);
+ break;
+ }
+ break;
+ }
+ break;
+ case AO_USB_TYPE_CLASS:
+ switch (ao_usb_setup.request) {
+ case AO_USB_SET_LINE_CODING:
+ ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+ break;
+ case AO_USB_GET_LINE_CODING:
+ ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7);
+ break;
+ case AO_USB_SET_CONTROL_LINE_STATE:
+ break;
+ }
+ break;
+ }
+
+ /* If we're not waiting to receive data from the host,
+ * queue an IN response
+ */
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+ ao_usb_ep0_in_start(ao_usb_setup.length);
+}
+
+static void
+ao_usb_ep0_handle(uint8_t receive)
+{
+ if (receive & AO_USB_EP0_GOT_RESET) {
+ ao_usb_set_ep0();
+ return;
+ }
+ if (receive & AO_USB_EP0_GOT_SETUP) {
+ ao_usb_ep0_setup();
+ }
+ if (receive & AO_USB_EP0_GOT_RX_DATA) {
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) {
+ ao_usb_ep0_fill();
+ if (ao_usb_ep0_out_len == 0) {
+ ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+ ao_usb_ep0_in_start(0);
+ }
+ }
+ }
+ if (receive & AO_USB_EP0_GOT_TX_ACK) {
+#if HAS_FLIGHT && AO_USB_FORCE_IDLE
+ ao_flight_force_idle = 1;
+#endif
+ /* Wait until the IN packet is received from addr 0
+ * before assigning our local address
+ */
+ if (ao_usb_address_pending)
+ ao_usb_set_address(ao_usb_address);
+ if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+ ao_usb_ep0_flush();
+ }
+}
+
+void
+ao_usb_ep0_interrupt(uint8_t mask)
+{
+ if (mask) {
+ ++control_count;
+ ao_usb_ep0_handle(mask);
+ }
+}
+
+void
+ao_usb_in_interrupt(uint32_t mask)
+{
+ if (mask & (1 << AO_USB_IN_EPR)) {
+ ++in_count;
+ _tx_dbg1("TX ISR", epr);
+ ao_usb_in_pending = 0;
+ ao_wakeup(&ao_usb_in_pending);
+ }
+}
+
+void
+ao_usb_out_interrupt(uint32_t mask)
+{
+ if (mask & (1 << AO_USB_OUT_EPR)) {
+ ++out_count;
+ _rx_dbg1("RX ISR", epr);
+ ao_usb_out_avail = 1;
+ _rx_dbg0("out avail set");
+ ao_wakeup(AO_USB_OUT_SLEEP_ADDR);
+ _rx_dbg0("stdin awoken");
+ }
+}
+
+void
+ao_usb_int_interrupt(uint32_t mask)
+{
+ (void) mask;
+}
+
+void
+stm_usb_fs_wkup(void)
+{
+ /* USB wakeup, just clear the bit for now */
+// stm_usb.istr &= ~(1 << STM_USB_ISTR_WKUP);
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+_ao_usb_in_send(void)
+{
+ _tx_dbg0("in_send start");
+
+ while (ao_usb_in_pending)
+ ao_sleep(&ao_usb_in_pending);
+
+ ao_usb_in_pending = 1;
+ if (ao_usb_tx_count != AO_USB_IN_SIZE)
+ ao_usb_in_flushed = 1;
+
+ ao_usb_dev_ep_in(AO_USB_IN_EPR, ao_usb_tx_buffer, ao_usb_tx_count);
+ ao_usb_tx_count = 0;
+
+ _tx_dbg0("in_send end");
+}
+
+/* Wait for a free IN buffer. Interrupts are blocked */
+static void
+_ao_usb_in_wait(void)
+{
+ for (;;) {
+ /* Check if the current buffer is writable */
+ if (ao_usb_tx_count < AO_USB_IN_SIZE)
+ break;
+
+ _tx_dbg0("in_wait top");
+ /* Wait for an IN buffer to be ready */
+ while (ao_usb_in_pending)
+ ao_sleep(&ao_usb_in_pending);
+ _tx_dbg0("in_wait bottom");
+ }
+}
+
+void
+ao_usb_flush(void)
+{
+ if (!ao_usb_running)
+ return;
+
+ /* Anytime we've sent a character since
+ * the last time we flushed, we'll need
+ * to send a packet -- the only other time
+ * we would send a packet is when that
+ * packet was full, in which case we now
+ * want to send an empty packet
+ */
+ ao_arch_block_interrupts();
+ while (!ao_usb_in_flushed) {
+ _tx_dbg0("flush top");
+ _ao_usb_in_send();
+ _tx_dbg0("flush end");
+ }
+ ao_arch_release_interrupts();
+}
+
+void
+ao_usb_putchar(char c)
+{
+ if (!ao_usb_running)
+ return;
+
+ ao_arch_block_interrupts();
+ _ao_usb_in_wait();
+
+ ao_usb_in_flushed = 0;
+ ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c;
+
+ /* Send the packet when full */
+ if (ao_usb_tx_count == AO_USB_IN_SIZE) {
+ _tx_dbg0("putchar full");
+ _ao_usb_in_send();
+ _tx_dbg0("putchar flushed");
+ }
+ ao_arch_release_interrupts();
+}
+
+static void
+_ao_usb_out_recv(void)
+{
+ _rx_dbg0("out_recv top");
+ ao_usb_out_avail = 0;
+
+ ao_usb_rx_count = ao_usb_dev_ep_out(AO_USB_OUT_EPR, ao_usb_rx_buffer, sizeof (ao_usb_rx_buffer));
+
+ _rx_dbg1("out_recv count", ao_usb_rx_count);
+
+ ao_usb_rx_pos = 0;
+}
+
+int
+_ao_usb_pollchar(void)
+{
+ uint8_t c;
+
+ if (!ao_usb_running)
+ return AO_READ_AGAIN;
+
+ for (;;) {
+ if (ao_usb_rx_pos != ao_usb_rx_count)
+ break;
+
+ _rx_dbg0("poll check");
+ /* Check to see if a packet has arrived */
+ if (!ao_usb_out_avail) {
+ _rx_dbg0("poll none");
+ return AO_READ_AGAIN;
+ }
+ _ao_usb_out_recv();
+ }
+
+ /* Pull a character out of the fifo */
+ c = ao_usb_rx_buffer[ao_usb_rx_pos++];
+ return c;
+}
+
+char
+ao_usb_getchar(void)
+{
+ int c;
+
+ ao_arch_block_interrupts();
+ while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+ ao_sleep(AO_USB_OUT_SLEEP_ADDR);
+ ao_arch_release_interrupts();
+ return c;
+}
+
+#ifndef HAS_USB_DISABLE
+#define HAS_USB_DISABLE 1
+#endif
+
+#if HAS_USB_DISABLE
+void
+ao_usb_disable(void)
+{
+ ao_usb_dev_disable();
+}
+#endif
+
+void
+ao_usb_enable(void)
+{
+ ao_usb_dev_enable();
+
+ ao_usb_configuration = 0;
+}
+
+#if USB_ECHO
+struct ao_task ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+ char c;
+
+ for (;;) {
+ c = ao_usb_getchar();
+ ao_usb_putchar(c);
+ ao_usb_flush();
+ }
+}
+#endif
+
+#if USB_DEBUG
+static void
+ao_usb_irq(void)
+{
+ printf ("control: %d out: %d in: %d int: %d reset: %d\n",
+ control_count, out_count, in_count, int_count, reset_count);
+}
+
+const struct ao_cmds ao_usb_cmds[] = {
+ { ao_usb_irq, "I\0Show USB interrupt counts" },
+ { 0, NULL }
+};
+#endif
+
+void
+ao_usb_init(void)
+{
+ ao_usb_enable();
+
+ ao_usb_ep0_state = AO_USB_EP0_IDLE;
+#if USB_ECHO
+ ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+#if USB_DEBUG
+ ao_cmd_register(&ao_usb_cmds[0]);
+#endif
+#if !USB_ECHO
+#if USE_USB_STDIO
+ ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+#endif
+}
+
+#if TX_DBG || RX_DBG
+
+struct ao_usb_dbg {
+ int line;
+ char *msg;
+ uint32_t value;
+ uint32_t prival;
+#if TX_DBG
+ uint16_t in_count;
+ uint32_t in_epr;
+ uint32_t in_pending;
+ uint32_t tx_count;
+ uint32_t in_flushed;
+#endif
+#if RX_DBG
+ uint8_t rx_count;
+ uint8_t rx_pos;
+ uint8_t out_avail;
+ uint32_t out_epr;
+#endif
+};
+
+#define NUM_USB_DBG 16
+
+static struct ao_usb_dbg dbg[NUM_USB_DBG];
+static int dbg_i;
+
+static void _dbg(int line, char *msg, uint32_t value)
+{
+ uint32_t prival;
+ dbg[dbg_i].line = line;
+ dbg[dbg_i].msg = msg;
+ dbg[dbg_i].value = value;
+#if AO_NONMASK_INTERRUPT
+ asm("mrs %0,basepri" : "=&r" (prival));
+#else
+ asm("mrs %0,primask" : "=&r" (prival));
+#endif
+ dbg[dbg_i].prival = prival;
+#if TX_DBG
+ dbg[dbg_i].in_count = in_count;
+ dbg[dbg_i].in_epr = stm_usb.epr[AO_USB_IN_EPR];
+ dbg[dbg_i].in_pending = ao_usb_in_pending;
+ dbg[dbg_i].tx_count = ao_usb_tx_count;
+ dbg[dbg_i].in_flushed = ao_usb_in_flushed;
+#endif
+#if RX_DBG
+ dbg[dbg_i].rx_count = ao_usb_rx_count;
+ dbg[dbg_i].rx_pos = ao_usb_rx_pos;
+ dbg[dbg_i].out_avail = ao_usb_out_avail;
+ dbg[dbg_i].out_epr = stm_usb.epr[AO_USB_OUT_EPR];
+#endif
+ if (++dbg_i == NUM_USB_DBG)
+ dbg_i = 0;
+}
+#endif
diff --git a/src/stm32f4/ao_usb_gen.h b/src/stm32f4/ao_usb_gen.h
new file mode 100644
index 00000000..b059ddb7
--- /dev/null
+++ b/src/stm32f4/ao_usb_gen.h
@@ -0,0 +1,110 @@
+/*
+ * 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_USB_GEN_H_
+#define _AO_USB_GEN_H_
+
+#include "ao.h"
+#include "ao_usb.h"
+#include "ao_product.h"
+#include <stdint.h>
+
+#define USB_ECHO 0
+
+#ifndef USE_USB_STDIO
+#define USE_USB_STDIO 1
+#endif
+
+#if USE_USB_STDIO
+#define AO_USB_OUT_SLEEP_ADDR (&ao_stdin_ready)
+#else
+#define AO_USB_OUT_SLEEP_ADDR (&ao_usb_out_avail)
+#endif
+
+struct ao_usb_setup {
+ uint8_t dir_type_recip;
+ uint8_t request;
+ uint16_t value;
+ uint16_t index;
+ uint16_t length;
+} ao_usb_setup;
+
+#define AO_USB_EP0_GOT_RESET 1
+#define AO_USB_EP0_GOT_SETUP 2
+#define AO_USB_EP0_GOT_RX_DATA 4
+#define AO_USB_EP0_GOT_TX_ACK 8
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR 0
+#define AO_USB_INT_EPR 1
+#define AO_USB_OUT_EPR 2
+#define AO_USB_IN_EPR 3
+
+/* Device interfaces required */
+
+/* Queue IN bytes to EP0 */
+void
+ao_usb_dev_ep0_init(void);
+
+void
+ao_usb_dev_ep0_in(const void *data, uint16_t len);
+
+bool
+ao_usb_dev_ep0_in_busy(void);
+
+/* Receive OUT bytes from EP0 */
+uint16_t
+ao_usb_dev_ep0_out(void *data, uint16_t len);
+
+/* Set device address */
+void
+ao_usb_dev_set_address(uint8_t address);
+
+void
+ao_usb_dev_enable(void);
+
+void
+ao_usb_dev_disable(void);
+
+void
+ao_usb_dev_init(void);
+
+/* Queue IN bytes to EPn */
+void
+ao_usb_dev_ep_in(uint8_t ep, const void *data, uint16_t len);
+
+bool
+ao_usb_dev_ep_in_busy(uint8_t ep);
+
+/* Receive OUT bytes from EPn */
+uint16_t
+ao_usb_dev_ep_out(uint8_t ep, void *data, uint16_t len);
+
+
+/* General interfaces provided */
+
+void
+ao_usb_ep0_interrupt(uint8_t mask);
+
+void
+ao_usb_in_interrupt(uint32_t mask);
+
+void
+ao_usb_out_interrupt(uint32_t mask);
+
+#endif /* _AO_USB_GEN_H_ */
+
diff --git a/src/stm32f4/ao_usb_stm32f4.c b/src/stm32f4/ao_usb_stm32f4.c
new file mode 100644
index 00000000..e65c3656
--- /dev/null
+++ b/src/stm32f4/ao_usb_stm32f4.c
@@ -0,0 +1,1144 @@
+/*
+ * 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_usb_gen.h"
+
+static uint32_t grxstsp;
+
+static inline uint8_t
+grxstsp_enum(void)
+{
+ return (grxstsp >> STM_USB_GRXSTSP_EPNUM) & STM_USB_GRXSTSP_EPNUM_MASK;
+}
+
+static inline uint8_t
+grxstsp_pktsts(void)
+{
+ return (grxstsp >> STM_USB_GRXSTSP_PKTSTS) & STM_USB_GRXSTSP_PKTSTS_MASK;
+}
+
+static inline uint16_t
+grxstsp_bcnt(void)
+{
+ return (grxstsp >> STM_USB_GRXSTSP_BCNT) & STM_USB_GRXSTSP_BCNT_MASK;
+}
+
+static void
+ao_usb_dev_ep_out_start(uint8_t ep)
+{
+ stm_usb.doep[ep].doeptsiz = ((1 << STM_USB_DOEPTSIZ_PKTCNT) |
+ (3 << STM_USB_DOEPTSIZ_STUPCNT) |
+ (24 << STM_USB_DOEPTSIZ_XFRSIZ));
+
+// stm_usb.doep[ep].doepctl |= (1 << STM_USB_DOEPCTL_EPENA);
+}
+
+static void
+ao_usb_mask_in_bits(vuint32_t *addr, uint32_t shift, uint32_t mask, uint32_t bits)
+{
+ uint32_t value;
+
+ value = *addr;
+ value &= ~(mask << shift);
+ value |= (bits << shift);
+ *addr = value;
+}
+
+static void
+ao_usb_activate_ep0(void)
+{
+ stm_usb.diep[0].diepctl = ((0 << STM_USB_DIEPCTL_TXFNUM) |
+ (0 << STM_USB_DIEPCTL_STALL) |
+ (STM_USB_DIEPCTL_EPTYP_CONTROL << STM_USB_DIEPCTL_EPTYP) |
+ (1 << STM_USB_DIEPCTL_USBAEP) |
+ (STM_USB_DIEPCTL_MPSIZ0_64 << STM_USB_DIEPCTL_MPSIZ));
+ stm_usb.doep[0].doepctl = ((0 << STM_USB_DOEPCTL_SNPM) |
+ (STM_USB_DOEPCTL_EPTYP_CONTROL << STM_USB_DOEPCTL_EPTYP) |
+ (1 << STM_USB_DOEPCTL_USBAEP) |
+ (STM_USB_DOEPCTL_MPSIZ0_64 << STM_USB_DOEPCTL_MPSIZ));
+}
+
+#if 0
+static void
+ao_usb_activate_in(int epnum)
+{
+ stm_usb.daintmsk |= (1 << (epnum + STM_USB_DAINTMSK_IEPM));
+ stm_usb.diep[epnum].diepctl = ((epnum << STM_USB_DIEPCTL_TXFNUM) |
+ (0 << STM_USB_DIEPCTL_STALL) |
+ (STM_USB_DIEPCTL_EPTYP_BULK << STM_USB_DIEPCTL_EPTYP) |
+ (1 << STM_USB_DIEPCTL_USBAEP) |
+ (64 << STM_USB_DIEPCTL_MPSIZ));
+}
+
+static void
+ao_usb_activate_out(int epnum)
+{
+ stm_usb.daintmsk |= (1 << (epnum + STM_USB_DAINTMSK_OEPM));
+ stm_usb.doep[epnum].doepctl = ((0 << STM_USB_DOEPCTL_SNPM) |
+ (STM_USB_DOEPCTL_EPTYP_BULK << STM_USB_DOEPCTL_EPTYP) |
+ (1 << STM_USB_DOEPCTL_USBAEP) |
+ (64 << STM_USB_DOEPCTL_MPSIZ));
+}
+#endif
+
+static void
+ao_usb_enum_done(void)
+{
+ /* Set turn-around delay. 6 is for high hclk (> 32MHz) */
+ ao_usb_mask_in_bits(&stm_usb.gusbcfg, STM_USB_GUSBCFG_TRDT, STM_USB_GUSBCFG_TRDT_MASK, 6);
+
+ ao_usb_activate_ep0();
+}
+
+static void
+ao_usb_flush_tx_fifo(uint32_t fifo)
+{
+ stm_usb.grstctl = ((1 << STM_USB_GRSTCTL_TXFFLSH) |
+ (fifo << STM_USB_GRSTCTL_TXFNUM));
+ while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_TXFFLSH)) != 0)
+ ao_arch_nop();
+}
+
+static void
+ao_usb_flush_rx_fifo(void)
+{
+ stm_usb.grstctl = (1 << STM_USB_GRSTCTL_RXFFLSH);
+ while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_RXFFLSH)) != 0)
+ ao_arch_nop();
+}
+
+/* reset and enable EP0 */
+void
+ao_usb_dev_ep0_init(void)
+{
+ uint32_t diepctl;
+
+ /* Flush TX fifo */
+ ao_usb_flush_tx_fifo(STM_USB_GRSTCTL_TXFNUM_ALL);
+
+ /* Clear interrupts */
+ for (int i = 0; i < 6; i++) {
+ stm_usb.diep[i].diepint = 0xfffffffful;
+ stm_usb.doep[i].doepint = 0xfffffffful;
+ }
+ stm_usb.daint = 0xfffffffful;
+
+ /* Enable EP0 in/out interrupts */
+ /* 2. Unmask interrupt bits */
+ stm_usb.daintmsk |= ((1 << (STM_USB_DAINTMSK_IEPM + 0)) |
+ (1 << (STM_USB_DAINTMSK_OEPM + 0)));
+
+ stm_usb.doepmsk |= ((1 << STM_USB_DOEPMSK_STUPM) |
+ (1 << STM_USB_DOEPMSK_EPDM) |
+ (1 << STM_USB_DOEPMSK_XFRCM));
+ stm_usb.diepmsk |= ((1 << STM_USB_DIEPMSK_TOM) |
+ (1 << STM_USB_DIEPMSK_XFRCM) |
+ (1 << STM_USB_DIEPMSK_EPDM));
+
+ /* 1. Set NAK bit for all OUT endpoints */
+ stm_usb.doep[0].doepctl |= (1 << STM_USB_DOEPCTL_CNAK);
+ for (int i = 1; i < 6; i++)
+ stm_usb.doep[i].doepctl |= (1 << STM_USB_DOEPCTL_SNAK);
+
+ /* 3. Setup FIFO ram allocation */
+
+ /* XXX make principled decisions here */
+ stm_usb.grxfsiz = 0x80;
+
+ stm_usb.dieptxf0 = ((0x40 << STM_USB_DIEPTXF0_TX0FD) | /* size = 256 bytes */
+ (0x80 << STM_USB_DIEPTXF0_TX0FSA)); /* start address = 0x80 */
+
+ /* 4. Program OUT endpoint 0 to receive a SETUP packet */
+
+ uint32_t doeptsiz;
+
+ doeptsiz = ((1 << STM_USB_DOEPTSIZ_PKTCNT) |
+ (0x40 << STM_USB_DOEPTSIZ_XFRSIZ) |
+ (1 << STM_USB_DOEPTSIZ_STUPCNT));
+
+ stm_usb.doep[0].doeptsiz = doeptsiz;
+
+ /* Program MPSIZ field to set maximum packet size */
+
+ diepctl = ((0 << STM_USB_DIEPCTL_EPENA ) |
+ (0 << STM_USB_DIEPCTL_EPDIS ) |
+ (0 << STM_USB_DIEPCTL_SNAK ) |
+ (0 << STM_USB_DIEPCTL_CNAK ) |
+ (0 << STM_USB_DIEPCTL_TXFNUM) |
+ (0 << STM_USB_DIEPCTL_STALL ) |
+ (STM_USB_DIEPCTL_EPTYP_CONTROL << STM_USB_DIEPCTL_EPTYP ) |
+ (0 << STM_USB_DIEPCTL_NAKSTS ) |
+ (0 << STM_USB_DIEPCTL_EONUM ) |
+ (1 << STM_USB_DIEPCTL_USBAEP ) |
+ (STM_USB_DIEPCTL_MPSIZ0_64 << STM_USB_DIEPCTL_MPSIZ));
+
+ stm_usb.diep[0].diepctl = diepctl;
+
+ uint32_t doepctl;
+
+ doepctl = ((0 << STM_USB_DOEPCTL_EPENA ) |
+ (0 << STM_USB_DOEPCTL_EPDIS ) |
+ (0 << STM_USB_DOEPCTL_SNAK ) |
+ (0 << STM_USB_DOEPCTL_CNAK ) |
+ (0 << STM_USB_DOEPCTL_STALL ) |
+ (0 << STM_USB_DOEPCTL_SNPM ) |
+ (STM_USB_DOEPCTL_EPTYP_CONTROL << STM_USB_DOEPCTL_EPTYP ) |
+ (0 << STM_USB_DOEPCTL_NAKSTS ) |
+ (1 << STM_USB_DOEPCTL_USBAEP ) |
+ (STM_USB_DOEPCTL_MPSIZ0_64 << STM_USB_DOEPCTL_MPSIZ));
+
+ stm_usb.doep[0].doepctl = doepctl;
+
+ /* Clear interrupts */
+ stm_usb.diep[0].diepint = 0xffffffff;
+ stm_usb.doep[0].doepint = 0xffffffff;
+
+ ao_usb_dev_ep_out_start(0);
+}
+
+void
+ao_usb_dev_ep0_in(const void *data, uint16_t len)
+{
+ return ao_usb_dev_ep_in(0, data, len);
+}
+
+bool
+ao_usb_dev_ep0_in_busy(void)
+{
+ return false;
+}
+
+uint16_t
+ao_usb_dev_ep0_out(void *data, uint16_t len)
+{
+ return ao_usb_dev_ep_out(0, data, len);
+}
+
+/* Queue IN bytes to EPn */
+void
+ao_usb_dev_ep_in(uint8_t ep, const void *data, uint16_t len)
+{
+ int l = len;
+
+ while (l > 0) {
+ stm_usb.dfifo[ep].fifo = *((__packed uint32_t *) data);
+ l -= 4;
+ data += 4;
+ }
+
+ /* Set the IN data size */
+ stm_usb.diep[ep].dieptsiz = ((1 << STM_USB_DIEPTSIZ_PKTCNT) |
+ (len << STM_USB_DIEPTSIZ_XFRSIZ));
+
+ /* Enable the TX empty interrupt */
+ stm_usb.diepempmsk |= (1 << ep);
+
+ /* Enable the endpoint to queue the packet for transmission */
+ stm_usb.diep[ep].diepctl |= (1 << STM_USB_DIEPCTL_EPENA);
+}
+
+bool
+ao_usb_dev_ep_in_busy(uint8_t ep)
+{
+ (void) ep;
+ return false;
+}
+
+/* Receive OUT bytes from EPn */
+uint16_t
+ao_usb_dev_ep_out(uint8_t ep, void *data, uint16_t len)
+{
+ uint16_t received;
+ int l = len;
+ uint32_t t;
+
+ if (grxstsp_enum() != ep)
+ return 0;
+
+ received = grxstsp_bcnt();
+ if (received > len)
+ received = len;
+
+ while (l >= 4) {
+ *((__packed uint32_t *) data) = stm_usb.dfifo[0].fifo;
+ l -= 4;
+ data += 4;
+ }
+
+ if (l != 0) {
+ t = stm_usb.dfifo[0].fifo;
+ memcpy(data, &t, l);
+ }
+
+ ao_usb_dev_ep_out_start(ep);
+ return received;
+}
+
+void
+ao_usb_dev_set_address(uint8_t address)
+{
+ uint32_t dcfg;
+
+ dcfg = stm_usb.dcfg;
+
+ dcfg &= ~(STM_USB_DCFG_DAD_MASK << STM_USB_DCFG_DAD);
+ dcfg |= address & STM_USB_DCFG_DAD_MASK;
+ stm_usb.dcfg = dcfg;
+}
+
+static void
+ao_usb_core_reset(void)
+{
+ /* Wait for AHB master IDLE state. */
+ while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_AHBIDL)) == 0)
+ ao_arch_nop();
+
+
+ /* Core soft reset */
+ stm_usb.grstctl |= (1 << STM_USB_GRSTCTL_CSRST);
+
+ /* Wait for reset to complete */
+
+ while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_CSRST)) != 0)
+ ao_arch_nop();
+}
+
+static void
+ao_usb_core_init(void)
+{
+ /* Enable embedded PHY */
+ stm_usb.gusbcfg |= (1 << STM_USB_GUSBCFG_PHYSEL);
+
+ /* Core reset */
+ ao_usb_core_reset();
+
+ /* Deactivate power down */
+ stm_usb.gccfg = (1 << STM_USB_GCCFG_PWRDWN);
+}
+
+static void
+ao_usb_delay(uint32_t ms)
+{
+ AO_TICK_TYPE now = ao_time();
+ AO_TICK_TYPE then = now + AO_MS_TO_TICKS(ms);
+
+ while ((int16_t) (then - ao_time()) > 0)
+ ao_arch_nop();
+}
+
+static void
+ao_usb_set_device_mode(void)
+{
+ uint32_t gusbcfg;
+
+ gusbcfg = stm_usb.gusbcfg;
+ gusbcfg &= ~((1 << STM_USB_GUSBCFG_FHMOD) |
+ (1 << STM_USB_GUSBCFG_FDMOD));
+ gusbcfg |= (1 << STM_USB_GUSBCFG_FDMOD);
+ stm_usb.gusbcfg = gusbcfg;
+ ao_usb_delay(50);
+}
+
+static void
+ao_usb_device_init(void)
+{
+ /* deactivate vbus sensing */
+ stm_usb.gccfg &= ~(1 << STM_USB_GCCFG_VBDEN);
+
+ /* Force device mode */
+ stm_usb.gotgctl |= ((1 << STM_USB_GOTGCTL_BVALOEN) |
+ (1 << STM_USB_GOTGCTL_BVALOVAL));
+
+ /* Restart the phy clock */
+ stm_usb.pcgcctl = 0;
+
+ /* Device mode configuration */
+ stm_usb.dcfg |= (STM_USB_DCFG_PFIVL_80 << STM_USB_DCFG_PFIVL);
+
+ /* Set full speed phy */
+ stm_usb.dcfg |= (STM_USB_DCFG_DSPD_FULL_SPEED << STM_USB_DCFG_DSPD);
+
+ /* Flush the fifos */
+ ao_usb_flush_tx_fifo(STM_USB_GRSTCTL_TXFNUM_ALL);
+ ao_usb_flush_rx_fifo();
+
+ /* Clear all pending device interrupts */
+ stm_usb.diepmsk = 0;
+ stm_usb.doepmsk = 0;
+ stm_usb.daint = 0xffffffffUL;
+ stm_usb.daintmsk = 0;
+
+ /* Reset all endpoints */
+ for (int i = 0; i < 6; i++) {
+
+ /* Reset IN endpoint */
+ if (stm_usb.diep[i].diepctl & (1 << STM_USB_DIEPCTL_EPENA))
+ stm_usb.diep[i].diepctl = ((1 << STM_USB_DIEPCTL_EPDIS) |
+ (1 << STM_USB_DIEPCTL_SNAK));
+ else
+ stm_usb.diep[i].diepctl = 0;
+ stm_usb.diep[i].dieptsiz = 0;
+ stm_usb.diep[i].diepint = 0xfffffffful;
+
+ /* Reset OUT endpoint */
+ if (stm_usb.doep[i].doepctl & (1 << STM_USB_DOEPCTL_EPENA))
+ stm_usb.doep[i].doepctl = ((1 << STM_USB_DOEPCTL_EPDIS) |
+ (1 << STM_USB_DOEPCTL_SNAK));
+ else
+ stm_usb.doep[i].doepctl = 0;
+
+ stm_usb.doep[i].doeptsiz = 0;
+ stm_usb.doep[i].doepint = 0xfffffffful;
+ }
+
+ /* Disable all interrupts */
+ stm_usb.gintmsk = 0;
+
+ /* Clear pending interrupts */
+ stm_usb.gintsts = 0xfffffffful;
+
+ /* Enable core interrupts */
+ stm_usb.gintmsk = ((1 << STM_USB_GINTMSK_WUIM ) |
+ (0 << STM_USB_GINTMSK_SRQIM ) |
+ (0 << STM_USB_GINTMSK_DISCINT ) |
+ (0 << STM_USB_GINTMSK_CIDSCHGM ) |
+ (0 << STM_USB_GINTMSK_LPMINTM ) |
+ (0 << STM_USB_GINTMSK_PTXFEM ) |
+ (0 << STM_USB_GINTMSK_HCIM) |
+ (0 << STM_USB_GINTMSK_PRTIM ) |
+ (0 << STM_USB_GINTMSK_RSTDETM ) |
+ (1 << STM_USB_GINTMSK_IISOOXFRM ) |
+ (1 << STM_USB_GINTMSK_IISOIXFRM ) |
+ (1 << STM_USB_GINTMSK_OEPINT) |
+ (1 << STM_USB_GINTMSK_IEPINT) |
+ (0 << STM_USB_GINTMSK_EOPFM ) |
+ (0 << STM_USB_GINTMSK_ISOODRPM ) |
+ (1 << STM_USB_GINTMSK_ENUMDNEM) |
+ (1 << STM_USB_GINTMSK_USBRST) |
+ (1 << STM_USB_GINTMSK_USBSUSPM ) |
+ (0 << STM_USB_GINTMSK_ESUSPM ) |
+ (0 << STM_USB_GINTMSK_GONAKEFFM ) |
+ (0 << STM_USB_GINTMSK_GINAKEFFM ) |
+ (0 << STM_USB_GINTMSK_NPTXFEM ) |
+ (0 << STM_USB_GINTMSK_RXFLVLM) |
+ (0 << STM_USB_GINTMSK_SOFM ) |
+ (0 << STM_USB_GINTMSK_OTGINT ) |
+ (0 << STM_USB_GINTMSK_MMISM));
+}
+
+static void
+ao_usb_device_connect(void)
+{
+ /* Enable pull-up/pull-down */
+ stm_usb.dctl &= ~(1 << STM_USB_DCTL_SDIS);
+ ao_usb_delay(20);
+}
+
+#if 0
+static void
+ao_usb_device_disconnect(void)
+{
+ /* Disable pull-up/pull-down */
+ stm_usb.dctl |= (1 << STM_USB_DCTL_SDIS);
+ ao_usb_delay(20);
+}
+#endif
+
+void
+ao_usb_dev_enable(void)
+{
+ ao_arch_block_interrupts();
+
+ /* Configure GPIOs */
+ ao_enable_port(&stm_gpioa);
+#if 0
+ stm_afr_set(&stm_gpioa, 8, STM_AFR_AF10); /* USB_FS_SOF */
+ stm_afr_set(&stm_gpioa, 9, STM_AFR_AF10); /* USB_FS_VBUS */
+ stm_afr_set(&stm_gpioa, 10, STM_AFR_AF10); /* USB_FS_ID */
+#endif
+ stm_afr_set(&stm_gpioa, 11, STM_AFR_AF10);
+ stm_ospeedr_set(&stm_gpioa, 11, STM_OSPEEDR_HIGH);
+ stm_pupdr_set(&stm_gpioa, 11, STM_PUPDR_NONE);
+ stm_afr_set(&stm_gpioa, 12, STM_AFR_AF10);
+ stm_ospeedr_set(&stm_gpioa, 12, STM_OSPEEDR_HIGH);
+ stm_pupdr_set(&stm_gpioa, 12, STM_PUPDR_NONE);
+
+ /* Power on USB */
+ stm_rcc.ahb2enr |= (1 << STM_RCC_AHB2ENR_OTGFSEN);
+
+ /* Route interrupts */
+ stm_nvic_set_priority(STM_ISR_OTG_FS_POS, AO_STM_NVIC_LOW_PRIORITY);
+ stm_nvic_set_enable(STM_ISR_OTG_FS_POS);
+
+ /* Core init */
+ ao_usb_core_init();
+
+ /* Set device mode */
+ ao_usb_set_device_mode();
+
+ /* Reset FIFO allocations */
+ for (int i = 1; i < 16; i++)
+ stm_usb.dieptxf[i-1] = 0x0;
+
+ ao_usb_device_init();
+
+ /* Connect */
+ ao_usb_device_connect();
+}
+
+void
+ao_usb_dev_disable(void)
+{
+ stm_usb.gusbcfg = ((1 << STM_USB_GUSBCFG_FDMOD) |
+ (0 << STM_USB_GUSBCFG_FHMOD) |
+ (6 << STM_USB_GUSBCFG_TRDT) |
+ (0 << STM_USB_GUSBCFG_HNPCAP) |
+ (0 << STM_USB_GUSBCFG_SRPCAP) |
+ (1 << STM_USB_GUSBCFG_PHYSEL) |
+ (0 << STM_USB_GUSBCFG_TOCAL));
+
+ stm_usb.gahbcfg = ((0 << STM_USB_GAHBCFG_PTXFELVL) |
+ (1 << STM_USB_GAHBCFG_TXFELVL) |
+ (0 << STM_USB_GAHBCFG_GINTMSK));
+
+ stm_usb.dctl = ((0 << STM_USB_DCTL_POPRGDNE) |
+ (1 << STM_USB_DCTL_SDIS));
+
+ stm_rcc.ahb2enr &= ~(1 << STM_RCC_AHB2ENR_OTGFSEN);
+}
+
+void
+stm_otg_fs_isr(void)
+{
+ uint32_t gintsts = stm_usb.gintsts;
+ uint8_t ep0_receive = 0;
+ uint32_t out_interrupt = 0;
+ uint32_t in_interrupt = 0;
+
+ /* Clear all received interrupts */
+ stm_usb.gintsts = gintsts;
+
+ if (gintsts & (1 << STM_USB_GINTSTS_USBRST)) {
+ ep0_receive |= AO_USB_EP0_GOT_RESET;
+ }
+
+ if (gintsts & (1 << STM_USB_GINTSTS_ENUMDNE)) {
+ ao_usb_enum_done();
+ }
+
+ if (gintsts & ((1 << STM_USB_GINTSTS_OEPINT) |
+ (1 << STM_USB_GINTSTS_IEPINT)))
+ {
+ uint32_t daint = stm_usb.daint;
+ uint32_t oepint = (daint >> STM_USB_DAINT_OEPINT) & STM_USB_DAINT_OEPINT_MASK;
+ uint32_t iepint = (daint >> STM_USB_DAINT_IEPINT) & STM_USB_DAINT_IEPINT_MASK;
+
+ for (int ep = 0; ep < 6; ep++) {
+ if (gintsts & (1 << STM_USB_GINTSTS_OEPINT)) {
+ if (oepint & (1 << ep)) {
+ uint32_t doepint = stm_usb.doep[ep].doepint;
+
+ stm_usb.doep[ep].doepint = doepint;
+ if (doepint & (1 << STM_USB_DOEPINT_XFRC)) {
+ if (ep == 0)
+ ep0_receive |= AO_USB_EP0_GOT_SETUP;
+ else
+ out_interrupt |= (1 << ep);
+ }
+ grxstsp = stm_usb.grxstsp;
+ }
+ }
+
+ if (gintsts & (1 << STM_USB_GINTSTS_IEPINT)) {
+ if (iepint & (1 << ep)) {
+ uint32_t diepint = stm_usb.diep[ep].diepint;
+
+ stm_usb.diep[ep].diepint = diepint;
+ if (diepint & (1 << STM_USB_DIEPINT_XFRC)) {
+ if (ep == 0)
+ ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+ else
+ in_interrupt |= (1 << ep);
+ }
+ }
+ }
+ }
+ } else {
+ grxstsp = 0;
+ }
+
+ if (ep0_receive)
+ ao_usb_ep0_interrupt(ep0_receive);
+
+ if (out_interrupt)
+ ao_usb_out_interrupt(out_interrupt);
+
+ if (in_interrupt)
+ ao_usb_in_interrupt(in_interrupt);
+}
+
+/*
+
+ running before plugging in at first packet
+ gotgctl = 0x04cd0000, 0x04c10000, 0x04cd0000 *************
+
+ CURMOD = 0
+ OTGVER = 0
+ BSVLD = 1 BSVLD = 0
+ ASVLD = 1 ASVLD = 0
+ DBCT = 0
+ CIDSTS = 1
+
+ gotgint = 0x00100000, 0x00100000, 0x00100000
+
+ IDCHNG = 1
+
+ gahbcfg = 0x1, 0x1, 0x00000001
+
+ TXFELVL = 0 trigger half empty
+ GINTMSK = 1 interrupts enabled
+
+ gusbcfg = 0x40001840, 0x40001440 0x40001840 *************
+
+ FDMOD = 1 force device mode
+ FHMOD = 0
+ TRDT = 6 5 6
+ HNPCAP = 0
+ SRPCAP = 0
+ PHYSEL = 1
+ TOCAL = 0
+
+ grstctl = 0x80000040, 0x80000000 0x80000400 ***********
+
+ AHBIDL = 1
+ TXFNUM = 1 TXFNUM = 0 TXFNUM = 0x20 (flush all)
+ TXFFLSH = 0
+ RXFFLSH = 0
+ FCRST = 0
+ PSRST = 0
+ CSRST = 0
+
+ gintsts = 0x0480b43a, 0x04008022 0x04888438 ***********
+
+ WKUPINT = 0 0
+ SRQINT = 0 0
+ DISCINT = 0 0
+ CIDSCHG = 0 0
+ LPMINT = 0 0
+ PTXFE = 1 PTXFE = 1 PTXFE = 1
+ HCINT = 0
+ HPRTINT = 0
+ RSTDET = 1 RSTDET = 0 RSTDET = 1
+ IPXFER = 0
+ IISOIXFR = 0
+ OEPINT = 0 OEPINT = 1
+ IEPINT = 0
+ EOPF = 1 EOPF = 1 EOPF = 1
+ ISOODRP = 0
+ ENUMDNE = 1
+ USBRST = 1
+ USBSUSP = 0
+ ESUSP = 1 ESUSP = 1
+ GONAKEFF = 0
+ GINAKEFF = 0
+ NPTXFE = 1 NPTXFE = 1 NPTXFE = 1
+ RXFLVL = 1 RXFLVL = 1
+ SOF = 1 SOF = 1
+ OTGINT = 0
+ MMIS = 1 MMIS = 1 MMIS = 0
+ CMOD = 0
+
+ gintmsk = 0xc03c3814, 0xc03c3814,
+
+ WUIM = 1
+ SRQIM = 1
+ DISCINT = 0
+ CIDSCHGM = 0
+ LPMINTM = 0
+ PTXFEM = 0
+ HCIM = 0
+ PRTIM = 0
+ RSTDETM = 0
+ IISOOXFRM = 1
+ IISOIXFRM = 1
+ OEPINT = 1
+ IEPINT = 1
+ EOPFM = 0
+ ISOODRPM = 0
+ ENUMDNEM = 1
+ USBRST = 1
+ USBSUSPM = 1
+ ESUSPM =0
+ GONAKEFFM = 0
+ GINAKEFFM = 0
+ NPTXFEM = 0
+ RXFLVLM = 1
+ SOFM = 0
+ OTGINT = 1
+ MMISM = 0
+
+ grxstsr = 0xac0080, 0x0 0x14c0080 ***************
+
+ STSPHST = 0 STSPHST = 0
+ FRMNUM = 5 FRMNUM = 10
+ PKTSTS = 6 -- SETUP data packet PKTSTS = 6 -- SETUP data packet
+ DPID = 0 DPID = 0
+ BCNT = 8 BCNT = 8
+ EPNUM = 0 EPNUM = 0
+
+ grxstsp = 0xac0080, 0x0 0x14c0080
+
+ (same)
+
+ grxfsiz = 0x80, 0x80 0x80
+
+ RXFD = 128 512 bytes
+
+ dieptxf0 = 0x00400080, 0x00400080 0x00400080
+
+ TX0FD = 64 256 bytes
+ TX0FSA = 0x80
+
+ gccfg = 0x21fff0, 0x21fff0 0x21fff0
+
+ VBDEN = 1
+ SDEN = 0
+ PDEN = 0
+ DCDEN = 0
+ BCDEN = 0
+ PWRDN = 1
+ PS2DET = 0
+ SDET = 0
+ PDET = 0
+ DCDET = 0
+
+ cid = 0x2000, 0x2000 0x2000
+
+ PRODUCT_ID = 0x2000
+
+ glpmcfg = 0x0, 0x0 0x0
+
+ ENBESL = 0
+ LPMRCNTTST = 0
+ SNDLPM = 0
+ LPMRCNT = 0
+ LPMCHIDX = 0
+ L1RSMOK = 0
+ SLPSTS = 0
+ LPMRSP = 0
+ L1DSEN = 0
+ BESLTHRS = 0
+ L1SSEN = 0
+ REMWAKE = 0
+ BESL = 0
+ LPMACK = 0
+ LPMEN = 0
+
+ dieptxf = {0x8000c0, 0x0, 0x0, 0x0, 0x0}, {0x8000c0, 0x0, 0x0, 0x0, 0x0}, {0x8000c0, 0x0, 0x0, 0x0, 0x0},
+
+ INEXPTXFD 0 = 0x80 512 bytes
+ INEXPTXSA 0 = 0xc0
+
+ dcfg = 0x82000b3, 0x8200003, 0x8200003
+
+ ERRATIM = 0
+ PFIVL = 0
+ DAD = 0xb DAD = 0x0 DAD = 0
+ NZLSOHSK = 0
+ DSPD = 3 Full speed USB 1.1
+
+ dctl = 0x0, 0x0 0x0
+
+ DSBESLRJCT = 0
+ POPRGDNE = 0
+ CGONAK = 0
+ SGONAK = 0
+ CGINAK = 0
+ SGINAK = 0
+ TCTL = 0
+ GONSTS = 0
+ GINSTS = 0
+ SDIS = 0
+ RWUSIG = 0
+
+ dsts = 0x0043ff06, 0x00000006 0x00400c06
+
+ DEVLNSTS = 1 (D+ low, D- high)
+ FNSOF = 0x3ff FNSOF = 0xc
+ EERR = 0
+ ENUMSPD = 3 Full speed ENUMSPD = 3
+ SUSPSTS = 0 SUSPSTS = 0
+
+ diepmsk = 0xb, 0x0 0xb
+
+ NAKM = 0
+ TXFURM = 0
+ INEPNEM = 0
+ INEPNMM = 0
+ ITTXFEMSK = 0
+ TOM = 1
+ EPDM = 1
+ XFRCM = 1
+
+ doepmsk = 0x2b, 0x0 0x2b
+
+ NYETMSK = 0
+ NAKMSK = 0
+ BERRM =0
+ OUTPKTERRM = 0
+ STSPHSRXM = 1
+ OTEPDM = 0
+ STUPM = 1
+ EPDM = 1
+ XFRCM = 1
+
+ daint = 0x0, 0x0 0x10000
+
+ daintmsk = 0x30003, 0x0 0x10001
+
+ OEPM = 0x3 endpoints 0 and 1 OEPM = 0x1 endpoint 0
+ IEPM = 0x3 endpoints 0 and 1 IEPM = 0x1 endpoint 0
+
+ dvbusdis = 0x17d7, 0x17d7 0x17d7
+
+ VBUSDT = 0x17d7 reset value
+
+ dvbuspulse = 0x5b8, 0x5b8 0x5b8
+
+ DVBUSP = 0x5b8 reset value
+
+ diepempmsk = 0x0, 0x0 0x0
+
+ INEPTXFEM = 0 no endpoints
+
+ diep = {{
+ diepctl = 0x28000,
+
+ EPENA = 0
+ EPDIS = 0
+ SNAK = 0
+ CNAK = 0
+ TXFNUM = 0
+ STALL = 0
+ EPTYP = 0
+ NAKSTS = 1
+ USBAEP = 1
+ MPSIZ = 0 64 bytes
+
+ diepint = 0x20c0,
+
+ NAK = 1
+ PKTDRPSTS = 0
+ TXFIFOUDRN = 0
+ TXFE = 1
+ INEPNE = 1
+ ITTXFE = 0
+ TOC = 0
+ EPDISD = 0
+ XFRC = 0
+
+ dieptsiz = 0x0,
+
+ PKTCNT = 0
+ XFRSIZ = 0
+
+ dtxfsts = 0x40,
+
+ INEPTFSAV = 0x40 256 bytes available
+
+ }, {
+ diepctl = 0x00490040,
+
+ EPENA = 0
+ EPDIS = 0
+ SODDFRM = 0
+ SD0PID = 0
+ SNAK = 0
+ CNAK = 0
+ TXFNUM = 1
+ STALL = 0
+ EPTYP = 2 bulk
+ NAKSTS = 0
+ EONUM = 1
+ USBAEP = 0
+ MPSIZ = 64 256 bytes
+
+ diepint = 0x2090,
+
+ NAK = 1
+ PKTDRPSTS = 0
+ TXFIFOUDRN = 0
+ TXFE = 1
+ INEPNE = 0
+ INPENM = 0
+ ITTXFE = 1
+ TOC = 0
+ EPDISD = 0
+ XFRC = 0
+
+ dieptsiz = 0x0,
+
+ MCNT = 0
+ PKTCNT = 0
+ XFRSIZ = 0
+
+ dtxfsts = 0x80,
+
+ INEPTFSAV = 0x80 512 bytes available
+
+ }, {
+ diepctl = 0x0,
+ pad_04 = 0x0,
+ diepint = 0x80,
+ pad_0c = 0x0,
+ dieptsiz = 0x0,
+ pad_14 = 0x43425355,
+ dtxfsts = 0x40,
+ pad_1c = 0x400000
+ }, {
+ diepctl = 0x0,
+ pad_04 = 0x0,
+ diepint = 0x80,
+ pad_0c = 0x0,
+ dieptsiz = 0x0,
+ pad_14 = 0x43425355,
+ dtxfsts = 0x40,
+ pad_1c = 0x400000
+ }, {
+ diepctl = 0x0,
+ pad_04 = 0x0,
+ diepint = 0x80,
+ pad_0c = 0x0,
+ dieptsiz = 0x0,
+ pad_14 = 0x43425355,
+ dtxfsts = 0x40,
+ pad_1c = 0x400000
+ }, {
+ diepctl = 0x0,
+ pad_04 = 0x0,
+ diepint = 0x80,
+ pad_0c = 0x0,
+ dieptsiz = 0x0,
+ pad_14 = 0x43425355,
+ dtxfsts = 0x40,
+ pad_1c = 0x400000
+ }},
+
+ doep = {{
+ doepctl = 0x80028000, 0x00008000, 0x28000
+
+ EPENA = 1 EPENA = 0 EPENA = 0
+ EPDIS = 0
+ SNAK =0
+ CNAK = 0
+ STALL = 0
+ SNPM = 0
+ EPTYP = 0
+ NAKSTS = 1 NAKSTS = 0 NAKSTS = 1
+ USPAEP = 1 USPAEP = 1
+ MPSIZ = 0 64 bytes MPSIZ = 0
+
+ doepint = 0x8010, 0x0 0x8008
+
+ NYET = 0
+ NAK = 0
+ BERR = 0
+ OUTPKTERR = 0
+ STSPHSRX = 0
+ OTEPDIS = 1
+ STUP = 0 STUP = 1
+ EPDISD = 0
+ XFRC = 0
+
+ doeptsiz = 0x38, 0x0 0x20080008
+
+ STPCNT = 0 1 packet STPCNT = 1
+ PKTCNT = 0 PKTCNT = 1
+ XFRSIZ = 0x38 56 bytes (64 - 8) XFRSIZ = 8
+
+ }, {
+ doepctl = 0x800b0040,
+
+ EPENA = 1
+ EPDIS = 0
+ SD1PID = 0
+ SD0PID = 0
+ SNAK = 0
+ CNAK = 0
+ STALL = 0
+ SNPM =0
+ EPTYP = 2 Bulk
+ NAKSTS = 1
+ EONUM = 1
+ USBAEP = 0
+ MPSIZ = 0x40 64 bytes
+
+ doepint = 0x0,
+ doeptsiz = 0x21,
+
+ RXDPID = 0
+ PKTCNT = 0
+ XFRSIZ = 0x21 33 bytes ?
+
+ }, {
+ doepctl = 0x0,
+ pad_04 = 0x0,
+ doepint = 0x0,
+ pad_0c = 0x0,
+ doeptsiz = 0x0,
+ pad_14 = 0x43425355,
+ pad_18 = 0x40,
+ pad_1c = 0x400000
+ }, {
+ doepctl = 0x0,
+ pad_04 = 0x0,
+ doepint = 0x0,
+ pad_0c = 0x0,
+ doeptsiz = 0x0,
+ pad_14 = 0x43425355,
+ pad_18 = 0x40,
+ pad_1c = 0x400000
+ }, {
+ doepctl = 0x0,
+ pad_04 = 0x0,
+ doepint = 0x0,
+ pad_0c = 0x0,
+ doeptsiz = 0x0,
+ pad_14 = 0x43425355,
+ pad_18 = 0x40,
+ pad_1c = 0x400000
+ }, {
+ doepctl = 0x0,
+ pad_04 = 0x0,
+ doepint = 0x0,
+ pad_0c = 0x0,
+ doeptsiz = 0x0,
+ pad_14 = 0x43425355,
+ pad_18 = 0x40,
+ pad_1c = 0x400000
+ }},
+
+ pcgcctl = 0x0, 0x0, 0x0
+
+ SUSP = 0
+ PHYSLEEP = 0
+ ENL1GTG = 0
+ PHYSUSP = 0
+ GATEHCLK = 0
+ STPPCLK = 0
+
+ dfifo = {{
+ fifo = 0x1000680,
+
+
+ Clock configuration:
+
+$5 = {
+ cr = 0x0f077d83,
+
+ PLLI2SRDY = 1
+ PLLI2SON = 1
+ PLLRDY = 1
+ PLLON = 1
+ CSSON = 0
+ HSEBYP = 1
+ HSERDY = 1
+ HSEON = 1
+ HSICAL = 0x7d
+ HSITRIM = 0x10
+ HSIRDY = 1
+ HSION = 1
+
+ pllcfgr = 0x27403208,
+
+ PLLR = 2
+ PLLQ = 7
+ PLLSRC = 1 HSE
+ PLLP = 0 2
+ PLLN = 0xc8 200
+ PLLM = 8
+
+ clk_pllin = 8000000 / 8 = 1000000
+ vco = 1000000 * 200 = 200000000
+ clk_pll1p = 200000000 / 2 = 100000000 (100MHz)
+ clk_pll1q = 200000000 / 7 = ???
+ clk_pll1r = 200000000 / 2 = 100000000 (100MHz)
+
+ cfgr = 0x0000100a,
+ cir = 0x00000000,
+ ahb1rstr = 0x0,
+ ahb2rstr = 0x0,
+ ahb3rstr = 0x0,
+ pad_1c = 0x0,
+ apb1rstr = 0x0,
+ apb2rstr = 0x0,
+ pad_28 = 0x0,
+ pad_2c = 0x0,
+ ahb1enr = 0x40107f,
+ ahb2enr = 0x80,
+ ahbdnr = 0x3,
+ pad_3c = 0x0,
+ apb1enr = 0x11000410,
+ apb2enr = 0xc800,
+ pad_48 = 0x0,
+ pad_4c = 0x0,
+ ahb1lpenr = 0x6390ff,
+ ahb2lpenr = 0xd0,
+ ahb3lpenr = 0x3,
+ pad_5c = 0x0,
+ apb1lpenr = 0xfffecfff,
+ apb2lpenr = 0x357f9f3,
+ pad_68 = 0x0,
+ pad_6c = 0x0,
+ bdcr = 0x8200,
+ csr = 0x1e000003,
+ pad_78 = 0x0,
+ pad_7c = 0x0,
+ sscgr = 0x0,
+ plli2scfgr = 0x44003008,
+
+ PLLI2SR = 4
+ PLLI2SQ = 4
+ PLLI2SSRC = 0 HSE (due to PLLSRC)
+ PLLI2SN = 0xc0 192
+ PLLI2SM = 8
+
+ clk_plli2sin = 8000000 / 8 = 1000000
+ vcoi2s = 1000000 * 192 = 192000000
+ ck_pl2q = 192000000 / 4 = 48000000
+ ck_pl2r = 192000000 / 4 = 48000000
+
+ pad_88 = 0x0,
+ dckcfgr = 0x0,
+
+
+ ckgatenr = 0x0,
+
+ All clock gates enabled
+
+ dckcfgr2 = 0x08000000
+
+ LPTIMER1SEL = 0 APB
+ CKSDIOSEL = 0 CK_48MHz
+ CK48MSEL = 1 PLLI2S_Q
+ I2CFMP1SEL = 0 APB
+}
+
+
+
+*/
diff --git a/src/stm32f4/registers.ld b/src/stm32f4/registers.ld
new file mode 100644
index 00000000..bc924106
--- /dev/null
+++ b/src/stm32f4/registers.ld
@@ -0,0 +1,75 @@
+
+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_tim1 = 0x40010000;
+stm_tim8 = 0x40010400;
+stm_usart1 = 0x40011000;
+stm_usart6 = 0x40011400;
+stm_uart9 = 0x40011800;
+stm_uart10 = 0x40011c00;
+stm_adc = 0x40012000;
+stm_sdio = 0x40012c00;
+stm_spi1 = 0x40013000;
+stm_spi4 = 0x40013400;
+stm_syscfg = 0x40013800;
+stm_exti = 0x40013c00;
+stm_tim9 = 0x40014000;
+stm_tim10 = 0x40014400;
+stm_tim11 = 0x40014800;
+stm_spi5 = 0x40015000;
+stm_sai1 = 0x40015800;
+stm_dfsdm1 = 0x40016000;
+stm_dfsdm2 = 0x40016400;
+
+/* AHB1 */
+
+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;
+
+/* AHB2 */
+
+stm_usb = 0x50000000;
+stm_rng = 0x50060800;
+
+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
index 00000000..dcdce667
--- /dev/null
+++ b/src/stm32f4/stm32f4.h
@@ -0,0 +1,1522 @@
+/*
+ * 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_DIV_2 0
+#define STM_RCC_PLLCFGR_PLLP_DIV_4 1
+#define STM_RCC_PLLCFGR_PLLP_DIV_6 2
+#define STM_RCC_PLLCFGR_PLLP_DIV_8 3
+#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_AHB2ENR_OTGFSEN 7
+#define STM_RCC_AHB2ENR_RNGEN 6
+#define STM_RCC_AHB2ENR_CRYPEN 4
+
+#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_APB2ENR_DFSDM2EN 25
+#define STM_RCC_APB2ENR_DFSDM1EN 24
+#define STM_RCC_APB2ENR_SAI1EN 22
+#define STM_RCC_APB2ENR_SPI5EN 20
+#define STM_RCC_APB2ENR_TIM11EN 18
+#define STM_RCC_APB2ENR_TIM10EN 17
+#define STM_RCC_APB2ENR_TIM9EN 16
+#define STM_RCC_APB2ENR_EXITEN 15
+#define STM_RCC_APB2ENR_SYSCFGEN 14
+#define STM_RCC_APB2ENR_SPI4EN 13
+#define STM_RCC_APB2ENR_SPI1EN 12
+#define STM_RCC_APB2ENR_SDIOEN 11
+#define STM_RCC_APB2ENR_ADC1EN 8
+#define STM_RCC_APB2ENR_UART10EN 7
+#define STM_RCC_APB2ENR_UART9EN 5
+#define STM_RCC_APB2ENR_USART6EN 5
+#define STM_RCC_APB2ENR_USART1EN 4
+#define STM_RCC_APB2ENR_TIM8EN 1
+#define STM_RCC_APB2ENR_TIM1EN 0
+
+#define STM_RCC_CSR_RMVF 24
+
+#define STM_RCC_DCKCFGR_CKDFSDMSEL 31
+#define STM_RCC_DCKCFGR_I2S2SRC 27
+#define STM_RCC_DCKCFGR_I2S1SRC 25
+#define STM_RCC_DCKCFGR_TIMPRE 24
+#define STM_RCC_DCKCFGR_SAII1BSRC 22
+#define STM_RCC_DCKCFGR_SAII1ASRC 20
+#define STM_RCC_DCKCFGR_CKDFSDM1ASEL 15
+#define STM_RCC_DCKCFGR_CKDFSDM2ASEL 14
+#define STM_RCC_DCKCFGR_PLLDIVR 8
+#define STM_RCC_DCKCFGR_PLLI2SDIVR 0
+
+#define STM_RCC_DCKCFGR2_LPTIMER1SEL 30
+#define STM_RCC_DCKCFGR2_LPTIMER1SEL_APB 0
+#define STM_RCC_DCKCFGR2_LPTIMER1SEL_HSI 1
+#define STM_RCC_DCKCFGR2_LPTIMER1SEL_LSI 2
+#define STM_RCC_DCKCFGR2_LPTIMER1SEL_LSE 3
+#define STM_RCC_DCKCFGR2_SDIOSEL 28
+#define STM_RCC_DCKCFGR2_SDIOSEL_CK_48MHZ 0
+#define STM_RCC_DCKCFGR2_SDIOSEL_SYSTEM_CLOCK 1
+#define STM_RCC_DCKCFGR2_CK48MSEL 27
+#define STM_RCC_DCKCFGR2_CK48MSEL_PLL_Q 1
+#define STM_RCC_DCKCFGR2_CK48MSEL_PLLI2S_Q 1
+#define STM_RCC_DCKCFGR2_I2CFMP1SEL 22
+#define STM_RCC_DCKCFGR2_I2CFMP1SEL_APB 0
+#define STM_RCC_DCKCFGR2_I2CFMP1SEL_SYSTEM_CLOCK 1
+#define STM_RCC_DCKCFGR2_I2CFMP1SEL_HSI 2
+#define STM_RCC_DCKCFGR2_I2CFMP1SEL_APB_ALSO 3
+
+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);
+}
+
+#define isr(name) void stm_ ## name ## _isr(void)
+
+isr(nmi);
+isr(hardfault);
+isr(memmanage);
+isr(busfault);
+isr(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(exti15_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);
+
+#undef isr
+
+#define STM_ISR_WWDG_POS 0
+#define STM_ISR_PVD_POS 1
+#define STM_ISR_TAMPER_STAMP_POS 2
+#define STM_ISR_RTC_WKUP_POS 3
+#define STM_ISR_FLASH_POS 4
+#define STM_ISR_RCC_POS 5
+#define STM_ISR_EXTI0_POS 6
+#define STM_ISR_EXTI1_POS 7
+#define STM_ISR_EXTI2_POS 8
+#define STM_ISR_EXTI3_POS 9
+#define STM_ISR_EXTI4_POS 10
+#define STM_ISR_DMA1_STREAM0_POS 11
+#define STM_ISR_DMA1_STREAM1_POS 12
+#define STM_ISR_DMA1_STREAM2_POS 13
+#define STM_ISR_DMA1_STREAM3_POS 14
+#define STM_ISR_DMA1_STREAM4_POS 15
+#define STM_ISR_DMA1_STREAM5_POS 16
+#define STM_ISR_DMA1_STREAM6_POS 17
+#define STM_ISR_ADC_POS 18
+#define STM_ISR_CAN1_TX_POS 19
+#define STM_ISR_CAN1_RX0_POS 20
+#define STM_ISR_CAN1_RX1_POS 21
+#define STM_ISR_CAN1_SCE_POS 22
+#define STM_ISR_EXTI9_5_POS 23
+#define STM_ISR_TIM1_BRK_TIM9_POS 24
+#define STM_ISR_TIM1_UP_TIM10_POS 25
+#define STM_ISR_TIM_TRG_COM_TIM11_POS 26
+#define STM_ISR_TIM1_CC_POS 27
+#define STM_ISR_TIM2_POS 28
+#define STM_ISR_TIM3_POS 29
+#define STM_ISR_TIM4_POS 30
+#define STM_ISR_I2C1_EVT_POS 31
+#define STM_ISR_I2C1_ERR_POS 32
+#define STM_ISR_I2C2_EVT_POS 33
+#define STM_ISR_I2C2_ERR_POS 34
+#define STM_ISR_SPI1_POS 35
+#define STM_ISR_SPI2_POS 36
+#define STM_ISR_USART1_POS 37
+#define STM_ISR_USART2_POS 38
+#define STM_ISR_USART3_POS 39
+#define STM_ISR_EXTI15_10_POS 40
+#define STM_ISR_EXTI17_RTC_ALARM_POS 41
+#define STM_ISR_EXTI18_OTG_FS_WKUP_POS 42
+#define STM_ISR_TIM2_BRK_TIM12_POS 43
+#define STM_ISR_TIM8_UP_TIM13_POS 44
+#define STM_ISR_TIM8_TRG_COM_TIM14_POS 45
+#define STM_ISR_TIM8_CC_POS 46
+#define STM_ISR_DMA1_STREAM7_POS 47
+#define STM_ISR_FSMC_POS 48
+#define STM_ISR_SDIO_POS 49
+#define STM_ISR_TIM5_POS 50
+#define STM_ISR_SPI3_POS 41
+#define STM_ISR_UART4_POS 52
+#define STM_ISR_UART5_POS 53
+#define STM_ISR_TIM6_GLB_IT_DAC1_DAC2_POS 54
+#define STM_ISR_TIM7_POS 55
+#define STM_ISR_DMA2_STREAM0_POS 56
+#define STM_ISR_DMA2_STREAM1_POS 57
+#define STM_ISR_DMA2_STREAM2_POS 58
+#define STM_ISR_DMA2_STREAM3_POS 59
+#define STM_ISR_DMA2_STREAM4_POS 60
+#define STM_ISR_DFSDM1_FLT0_POS 61
+#define STM_ISR_DFSDM1_FLT1_POS 62
+#define STM_ISR_CAN2_TX_POS 63
+#define STM_ISR_CAN2_RX0_POS 64
+#define STM_ISR_CAN2_RX1_POS 65
+#define STM_ISR_CAN2_SCE_POS 66
+#define STM_ISR_OTG_FS_POS 67
+#define STM_ISR_DMA2_STREAM5_POS 68
+#define STM_ISR_DMA2_STREAM6_POS 69
+#define STM_ISR_DMA2_STREAM7_POS 70
+#define STM_ISR_USART6_POS 71
+#define STM_ISR_UART7_POS 82
+#define STM_ISR_UART9_POS 88
+#define STM_ISR_UART10_POS 89
+
+#define STM_ISR_EXTI15_10_POS 40
+
+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)
+
+#define STM_SCB_AIRCR_VECTKEY 16
+#define STM_SCB_AIRCR_VECTKEY_KEY 0x05fa
+#define STM_SCB_AIRCR_PRIGROUP 8
+#define STM_SCB_AIRCR_SYSRESETREQ 2
+#define STM_SCB_AIRCR_VECTCLRACTIVE 1
+#define STM_SCB_AIRCR_VECTRESET 0
+
+/* 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
+
+#define STM_SYSCFG_EXTICR_PA 0
+#define STM_SYSCFG_EXTICR_PB 1
+#define STM_SYSCFG_EXTICR_PC 2
+#define STM_SYSCFG_EXTICR_PD 3
+#define STM_SYSCFG_EXTICR_PE 4
+#define STM_SYSCFG_EXTICR_PF 5
+#define STM_SYSCFG_EXTICR_PG 6
+#define STM_SYSCFG_EXTICR_PH 7
+
+struct stm_syscfg {
+ vuint32_t memrmp;
+ vuint32_t pmc;
+ vuint32_t exticr[4];
+};
+
+extern struct stm_syscfg stm_syscfg;
+
+#define stm_syscfg (*((struct stm_syscfg *) 0x40013800))
+
+#define STM_SYSCFG_MEMRMP_MEM_MODE 0
+#define STM_SYSCFG_MEMRMP_MEM_MODE_MAIN_FLASH 0
+#define STM_SYSCFG_MEMRMP_MEM_MODE_SYSTEM_FLASH 1
+#define STM_SYSCFG_MEMRMP_MEM_MODE_SRAM 3
+#define STM_SYSCFG_MEMRMP_MEM_MODE_MASK 3
+
+#define STM_SYSCFG_PMC_ADC1DC2 0
+
+static inline void
+stm_exticr_set(struct stm_gpio *gpio, int pin) {
+ uint8_t reg = pin >> 2;
+ uint8_t shift = (pin & 3) << 2;
+ uint8_t val = 0;
+
+ /* Enable SYSCFG */
+ stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGEN);
+
+ if (gpio == &stm_gpioa)
+ val = STM_SYSCFG_EXTICR_PA;
+ else if (gpio == &stm_gpiob)
+ val = STM_SYSCFG_EXTICR_PB;
+ else if (gpio == &stm_gpioc)
+ val = STM_SYSCFG_EXTICR_PC;
+ else if (gpio == &stm_gpiod)
+ val = STM_SYSCFG_EXTICR_PD;
+ else if (gpio == &stm_gpioe)
+ val = STM_SYSCFG_EXTICR_PE;
+ else if (gpio == &stm_gpiof)
+ val = STM_SYSCFG_EXTICR_PF;
+ else if (gpio == &stm_gpiog)
+ val = STM_SYSCFG_EXTICR_PG;
+ else if (gpio == &stm_gpioh)
+ val = STM_SYSCFG_EXTICR_PH;
+
+ stm_syscfg.exticr[reg] = (stm_syscfg.exticr[reg] & ~(0xf << shift)) | val << shift;
+}
+
+struct stm_exti {
+ vuint32_t imr;
+ vuint32_t emr;
+ vuint32_t rtsr;
+ vuint32_t ftsr;
+
+ vuint32_t swier;
+ vuint32_t pr;
+};
+
+extern struct stm_exti stm_exti;
+
+#define stm_exti (*((struct stm_exti *) 0x40013c00))
+
+struct stm_usart {
+ vuint32_t sr; /* status register */
+ vuint32_t dr; /* data register */
+ vuint32_t brr; /* baud rate register */
+ vuint32_t cr1; /* control register 1 */
+
+ vuint32_t cr2; /* control register 2 */
+ vuint32_t cr3; /* control register 3 */
+ vuint32_t gtpr; /* guard time and prescaler */
+};
+
+extern struct stm_usart stm_usart6;
+
+#define stm_usart6 (*((struct stm_usart *) 0x40011400))
+
+#define STM_USART_SR_CTS (9) /* CTS flag */
+#define STM_USART_SR_LBD (8) /* LIN break detection flag */
+#define STM_USART_SR_TXE (7) /* Transmit data register empty */
+#define STM_USART_SR_TC (6) /* Transmission complete */
+#define STM_USART_SR_RXNE (5) /* Read data register not empty */
+#define STM_USART_SR_IDLE (4) /* IDLE line detected */
+#define STM_USART_SR_ORE (3) /* Overrun error */
+#define STM_USART_SR_NF (2) /* Noise detected flag */
+#define STM_USART_SR_FE (1) /* Framing error */
+#define STM_USART_SR_PE (0) /* Parity error */
+
+#define STM_USART_CR1_OVER8 (15) /* Oversampling mode */
+#define STM_USART_CR1_UE (13) /* USART enable */
+#define STM_USART_CR1_M (12) /* Word length */
+#define STM_USART_CR1_WAKE (11) /* Wakeup method */
+#define STM_USART_CR1_PCE (10) /* Parity control enable */
+#define STM_USART_CR1_PS (9) /* Parity selection */
+#define STM_USART_CR1_PEIE (8) /* PE interrupt enable */
+#define STM_USART_CR1_TXEIE (7) /* TXE interrupt enable */
+#define STM_USART_CR1_TCIE (6) /* Transmission complete interrupt enable */
+#define STM_USART_CR1_RXNEIE (5) /* RXNE interrupt enable */
+#define STM_USART_CR1_IDLEIE (4) /* IDLE interrupt enable */
+#define STM_USART_CR1_TE (3) /* Transmitter enable */
+#define STM_USART_CR1_RE (2) /* Receiver enable */
+#define STM_USART_CR1_RWU (1) /* Receiver wakeup */
+#define STM_USART_CR1_SBK (0) /* Send break */
+
+#define STM_USART_CR2_LINEN (14) /* LIN mode enable */
+#define STM_USART_CR2_STOP (12) /* STOP bits */
+#define STM_USART_CR2_STOP_MASK 3
+#define STM_USART_CR2_STOP_1 0
+#define STM_USART_CR2_STOP_0_5 1
+#define STM_USART_CR2_STOP_2 2
+#define STM_USART_CR2_STOP_1_5 3
+
+#define STM_USART_CR2_CLKEN (11) /* Clock enable */
+#define STM_USART_CR2_CPOL (10) /* Clock polarity */
+#define STM_USART_CR2_CPHA (9) /* Clock phase */
+#define STM_USART_CR2_LBCL (8) /* Last bit clock pulse */
+#define STM_USART_CR2_LBDIE (6) /* LIN break detection interrupt enable */
+#define STM_USART_CR2_LBDL (5) /* lin break detection length */
+#define STM_USART_CR2_ADD (0)
+#define STM_USART_CR2_ADD_MASK 0xf
+
+#define STM_USART_CR3_ONEBIT (11) /* One sample bit method enable */
+#define STM_USART_CR3_CTSIE (10) /* CTS interrupt enable */
+#define STM_USART_CR3_CTSE (9) /* CTS enable */
+#define STM_USART_CR3_RTSE (8) /* RTS enable */
+#define STM_USART_CR3_DMAT (7) /* DMA enable transmitter */
+#define STM_USART_CR3_DMAR (6) /* DMA enable receiver */
+#define STM_USART_CR3_SCEN (5) /* Smartcard mode enable */
+#define STM_USART_CR3_NACK (4) /* Smartcard NACK enable */
+#define STM_USART_CR3_HDSEL (3) /* Half-duplex selection */
+#define STM_USART_CR3_IRLP (2) /* IrDA low-power */
+#define STM_USART_CR3_IREN (1) /* IrDA mode enable */
+#define STM_USART_CR3_EIE (0) /* Error interrupt enable */
+
+/* USB */
+struct stm_usb {
+ vuint32_t gotgctl;
+ vuint32_t gotgint;
+ vuint32_t gahbcfg;
+ vuint32_t gusbcfg;
+
+ vuint32_t grstctl;
+ vuint32_t gintsts;
+ vuint32_t gintmsk;
+ vuint32_t grxstsr;
+
+ vuint32_t grxstsp;
+ vuint32_t grxfsiz;
+ vuint32_t dieptxf0;
+ vuint32_t hnptxsts;
+
+ vuint32_t pad_30;
+ vuint32_t pad_34;
+ vuint32_t gccfg;
+ vuint32_t cid;
+
+ vuint32_t pad_40;
+ vuint32_t pad_44;
+ vuint32_t pad_48;
+ vuint32_t ghwcfg3; /* not in docs? */
+
+ vuint32_t pad_50;
+ vuint32_t glpmcfg;
+ vuint32_t pad_58;
+ vuint32_t gdfifocfg; /* not in docs? */
+
+ uint8_t pad_60[0x100 - 0x60];
+
+ vuint32_t hptxfsiz; /* 0x100 */
+ vuint32_t dieptxf[0xf]; /* 0x104 5 in docs? */
+
+ uint8_t pad_140[0x400 - 0x140];
+
+ vuint32_t hcfg;
+ vuint32_t hfir;
+ vuint32_t hfnum;
+ vuint32_t pad_40c;
+
+ vuint32_t hptxsts;
+ vuint32_t haint;
+ vuint32_t haintmsk;
+ vuint32_t pad_41c;
+
+ uint8_t pad_420[0x440-0x420];
+
+ vuint32_t hprt;
+
+ uint8_t pad_444[0x500 - 0x444];
+
+ vuint32_t hcchar0;
+ vuint32_t pad_504;
+ vuint32_t hcint0;
+ vuint32_t hcintmsk0;
+
+ vuint32_t hctsiz0;
+ vuint32_t pad_514;
+ vuint32_t pad_518;
+ vuint32_t pad_51c;
+
+ struct {
+ vuint32_t hcchar;
+ vuint32_t pad_4;
+ vuint32_t hcint;
+ vuint32_t hcintmsk;
+
+ vuint32_t hctsiz;
+ vuint32_t pad_14;
+ vuint32_t pad_18;
+ vuint32_t pad_1c;
+ } h[11];
+
+ uint8_t pad_680[0x800 - 0x680];
+
+ vuint32_t dcfg;
+ vuint32_t dctl;
+ vuint32_t dsts;
+ vuint32_t pad_80c;
+
+ vuint32_t diepmsk;
+ vuint32_t doepmsk;
+ vuint32_t daint;
+ vuint32_t daintmsk;
+
+ vuint32_t pad_820;
+ vuint32_t pad_824;
+ vuint32_t dvbusdis;
+ vuint32_t dvbuspulse;
+
+ vuint32_t pad_830;
+ vuint32_t diepempmsk;
+
+ uint8_t pad_838[0x900 - 0x838];
+
+ struct {
+ vuint32_t diepctl;
+ vuint32_t pad_04;
+ vuint32_t diepint;
+ vuint32_t pad_0c;
+
+ vuint32_t dieptsiz;
+ vuint32_t pad_14;
+ vuint32_t dtxfsts;
+ vuint32_t pad_1c;
+ } diep[6];
+
+ uint8_t pad_9c0[0xb00 - 0x9c0];
+
+ struct {
+ vuint32_t doepctl;
+ vuint32_t pad_04;
+ vuint32_t doepint;
+ vuint32_t pad_0c;
+
+ vuint32_t doeptsiz;
+ vuint32_t pad_14;
+ vuint32_t pad_18;
+ vuint32_t pad_1c;
+ } doep[6];
+
+ uint8_t pad_bc0[0xe00 - 0xbc0];
+
+ vuint32_t pcgcctl;
+
+ uint8_t pad_e04[0x1000 - 0xe04];
+
+ struct {
+ vuint32_t fifo;
+ uint8_t pad_004[0x1000 - 0x004];
+ } dfifo[6];
+};
+
+extern struct stm_usb stm_usb;
+
+#define stm_usb (*((struct stm_usb *) 0x50000000))
+
+#define STM_USB_GOTGCTL_CURMOD 21
+#define STM_USB_GOTGCTL_OTGVER 20
+#define STM_USB_GOTGCTL_BSVLD 19
+#define STM_USB_GOTGCTL_ASVLD 18
+#define STM_USB_GOTGCTL_DBCT 17
+#define STM_USB_GOTGCTL_CIDSTS 16
+#define STM_USB_GOTGCTL_EHEN 12
+#define STM_USB_GOTGCTL_DHNPEN 11
+#define STM_USB_GOTGCTL_HSHNPEN 10
+#define STM_USB_GOTGCTL_HNPRQ 9
+#define STM_USB_GOTGCTL_HNGSCS 8
+#define STM_USB_GOTGCTL_BVALOVAL 7
+#define STM_USB_GOTGCTL_BVALOEN 6
+#define STM_USB_GOTGCTL_AVALOVAL 5
+#define STM_USB_GOTGCTL_AVALOEN 4
+#define STM_USB_GOTGCTL_VBVALOVAL 3
+#define STM_USB_GOTGCTL_VBVALOEN 2
+#define STM_USB_GOTGCTL_SRQ 1
+#define STM_USB_GOTGCTL_SRQSCS 0
+
+#define STM_USB_GOTGINT_IDCHNG 20
+#define STM_USB_GOTGINT_DBCDNE 19
+#define STM_USB_GOTGINT_ADTOCHG 18
+#define STM_USB_GOTGINT_HNGDET 17
+#define STM_USB_GOTGINT_HNSSCHG 9
+#define STM_USB_GOTGINT_SRSSCHG 8
+#define STM_USB_GOTGINT_SEDET 2
+
+#define STM_USB_GAHBCFG_PTXFELVL 8
+#define STM_USB_GAHBCFG_TXFELVL 7
+#define STM_USB_GAHBCFG_GINTMSK 0
+
+#define STM_USB_GUSBCFG_FDMOD 30
+#define STM_USB_GUSBCFG_FHMOD 29
+#define STM_USB_GUSBCFG_TRDT 10
+#define STM_USB_GUSBCFG_TRDT_MASK 0xf
+#define STM_USB_GUSBCFG_HNPCAP 9
+#define STM_USB_GUSBCFG_SRPCAP 8
+#define STM_USB_GUSBCFG_PHYSEL 6
+#define STM_USB_GUSBCFG_TOCAL 0
+#define STM_USB_GUSBCFG_TOCAL_MASK 0x7
+
+#define STM_USB_GRSTCTL_AHBIDL 31
+#define STM_USB_GRSTCTL_TXFNUM 6
+#define STM_USB_GRSTCTL_TXFNUM_ALL 0x10
+#define STM_USB_GRSTCTL_TXFNUM_MASK 0x1f
+#define STM_USB_GRSTCTL_TXFFLSH 5
+#define STM_USB_GRSTCTL_RXFFLSH 4
+#define STM_USB_GRSTCTL_FCRST 2
+#define STM_USB_GRSTCTL_PSRST 1
+#define STM_USB_GRSTCTL_CSRST 0
+
+#define STM_USB_GINTSTS_WKUPINT 31
+#define STM_USB_GINTSTS_SRQINT 30
+#define STM_USB_GINTSTS_DISCINT 29
+#define STM_USB_GINTSTS_CIDSCHG 28
+#define STM_USB_GINTSTS_LPMINT 27
+#define STM_USB_GINTSTS_PTXFE 26
+#define STM_USB_GINTSTS_HCINT 25
+#define STM_USB_GINTSTS_HPRTINT 24
+#define STM_USB_GINTSTS_RSTDET 23
+#define STM_USB_GINTSTS_IPXFER 21
+#define STM_USB_GINTSTS_IISOIXFR 20
+#define STM_USB_GINTSTS_OEPINT 19
+#define STM_USB_GINTSTS_IEPINT 18
+#define STM_USB_GINTSTS_EOPF 15
+#define STM_USB_GINTSTS_ISOODRP 14
+#define STM_USB_GINTSTS_ENUMDNE 13
+#define STM_USB_GINTSTS_USBRST 12
+#define STM_USB_GINTSTS_USBSUSP 11
+#define STM_USB_GINTSTS_ESUSP 10
+#define STM_USB_GINTSTS_GONAKEFF 7
+#define STM_USB_GINTSTS_GINAKEFF 6
+#define STM_USB_GINTSTS_NPTXFE 5
+#define STM_USB_GINTSTS_RXFLVL 4
+#define STM_USB_GINTSTS_SOF 3
+#define STM_USB_GINTSTS_OTGINT 2
+#define STM_USB_GINTSTS_MMIS 1
+#define STM_USB_GINTSTS_CMOD 0
+
+#define STM_USB_GINTMSK_WUIM 31
+#define STM_USB_GINTMSK_SRQIM 30
+#define STM_USB_GINTMSK_DISCINT 29
+#define STM_USB_GINTMSK_CIDSCHGM 28
+#define STM_USB_GINTMSK_LPMINTM 27
+#define STM_USB_GINTMSK_PTXFEM 26
+#define STM_USB_GINTMSK_HCIM 25
+#define STM_USB_GINTMSK_PRTIM 24
+#define STM_USB_GINTMSK_RSTDETM 23
+#define STM_USB_GINTMSK_IPXFERM 21 /* host mode */
+#define STM_USB_GINTMSK_IISOOXFRM 21 /* device mode */
+#define STM_USB_GINTMSK_IISOIXFRM 20
+#define STM_USB_GINTMSK_OEPINT 19
+#define STM_USB_GINTMSK_IEPINT 18
+#define STM_USB_GINTMSK_EOPFM 15
+#define STM_USB_GINTMSK_ISOODRPM 14
+#define STM_USB_GINTMSK_ENUMDNEM 13
+#define STM_USB_GINTMSK_USBRST 12
+#define STM_USB_GINTMSK_USBSUSPM 11
+#define STM_USB_GINTMSK_ESUSPM 10
+#define STM_USB_GINTMSK_GONAKEFFM 7
+#define STM_USB_GINTMSK_GINAKEFFM 6
+#define STM_USB_GINTMSK_NPTXFEM 5
+#define STM_USB_GINTMSK_RXFLVLM 4
+#define STM_USB_GINTMSK_SOFM 3
+#define STM_USB_GINTMSK_OTGINT 2
+#define STM_USB_GINTMSK_MMISM 1
+
+#define STM_USB_GRXSTSP_STSPHST 27
+#define STM_USB_GRXSTSP_FRMNUM 21
+#define STM_USB_GRXSTSP_FRMNUM_MASK 0xf
+#define STM_USB_GRXSTSP_PKTSTS 17
+#define STM_USB_GRXSTSP_PKTSTS_NAK 1
+#define STM_USB_GRXSTSP_PKTSTS_OUT_DATA 2
+#define STM_USB_GRXSTSP_PKTSTS_OUT_COMPLETE 3
+#define STM_USB_GRXSTSP_PKTSTS_SETUP_COMPLETE 4
+#define STM_USB_GRXSTSP_PKTSTS_SETUP_DATA 5
+#define STM_USB_GRXSTSP_PKTSTS_MASK 0xf
+#define STM_USB_GRXSTSP_DPID 15
+#define STM_USB_GRXSTSP_DPID_MASK 3
+#define STM_USB_GRXSTSP_BCNT 4
+#define STM_USB_GRXSTSP_BCNT 4
+#define STM_USB_GRXSTSP_BCNT_MASK 0x3ff
+#define STM_USB_GRXSTSP_EPNUM 0
+#define STM_USB_GRXSTSP_EPNUM_MASK 0xf
+
+#define STM_USB_GRXFSIZ_RXFD 0
+#define STM_USB_GRXFSIZ_RXFD_MASK 0xffff
+
+#define STM_USB_GCCFG_VBDEN 21
+#define STM_USB_GCCFG_SDEN 20
+#define STM_USB_GCCFG_PDEN 19
+#define STM_USB_GCCFG_DCDEN 18
+#define STM_USB_GCCFG_BCDEN 17
+#define STM_USB_GCCFG_PWRDWN 16
+#define STM_USB_GCCFG_PS2DET 3
+#define STM_USB_GCCFG_SDET 2
+#define STM_USB_GCCFG_PDET 1
+#define STM_USB_GCCFG_DCDET 0
+
+#define STM_USB_DIEPTXF0_TX0FD 16
+#define STM_USB_DIEPTXF0_TX0FSA 0
+
+#define STM_USB_DCFG_ERRATIM 15
+#define STM_USB_DCFG_PFIVL 11
+#define STM_USB_DCFG_PFIVL_80 0
+#define STM_USB_DCFG_PFIVL_85 1
+#define STM_USB_DCFG_PFIVL_90 2
+#define STM_USB_DCFG_PFIVL_95 3
+#define STM_USB_DCFG_PFIVL_MASK 3
+#define STM_USB_DCFG_DAD 4
+#define STM_USB_DCFG_DAD_MASK 0x7f
+#define STM_USB_DCFG_NZLSOHSK 2
+#define STM_USB_DCFG_DSPD 0
+#define STM_USB_DCFG_DSPD_FULL_SPEED 3
+#define STM_USB_DCFG_DSPD_MASK 3
+#define STM_USB_DCFG_
+#define STM_USB_DCFG_
+#define STM_USB_DCFG_
+#define STM_USB_DCFG_
+#define STM_USB_DCFG_
+#define STM_USB_DCFG_
+#define STM_USB_DCFG_
+
+#define STM_USB_DCTL_DSBESLRJCT 18
+#define STM_USB_DCTL_POPRGDNE 11
+#define STM_USB_DCTL_CGONAK 10
+#define STM_USB_DCTL_SGONAK 9
+#define STM_USB_DCTL_CGINAK 8
+#define STM_USB_DCTL_SGINAK 7
+#define STM_USB_DCTL_TCTL 4
+#define STM_USB_DCTL_GONSTS 3
+#define STM_USB_DCTL_GINSTS 2
+#define STM_USB_DCTL_SDIS 1
+#define STM_USB_DCTL_RWUSIG 0
+
+#define STM_USB_DSTS_DEVLNSTS 22
+#define STM_USB_DSTS_DEVLNSTS_MASK 0x3
+#define STM_USB_DSTS_FNSOF 8
+#define STM_USB_DSTS_FNSOF_MASK 0x3fff
+#define STM_USB_DSTS_EERR 3
+#define STM_USB_DSTS_ENUMSPD 1
+#define STM_USB_DSTS_ENUMSPD_MASK 3
+#define STM_USB_DSTS_SUSPSTS 0
+
+#define STM_USB_DIEPMSK_NAKM 13
+#define STM_USB_DIEPMSK_TXFURM 8
+#define STM_USB_DIEPMSK_INEPNEM 6
+#define STM_USB_DIEPMSK_INEPNMM 5
+#define STM_USB_DIEPMSK_ITTXFEMSK 4
+#define STM_USB_DIEPMSK_TOM 3
+#define STM_USB_DIEPMSK_EPDM 1
+#define STM_USB_DIEPMSK_XFRCM 0
+
+#define STM_USB_DOEPMSK_NYETMSK 14
+#define STM_USB_DOEPMSK_NAKMSK 13
+#define STM_USB_DOEPMSK_BERRM 12
+#define STM_USB_DOEPMSK_OUTPKTERRM 8
+#define STM_USB_DOEPMSK_STSPHSRXM 5
+#define STM_USB_DOEPMSK_OTEPDM 4
+#define STM_USB_DOEPMSK_STUPM 3
+#define STM_USB_DOEPMSK_EPDM 1
+#define STM_USB_DOEPMSK_XFRCM 0
+
+#define STM_USB_DAINT_OEPINT 16
+#define STM_USB_DAINT_OEPINT_MASK 0xffff
+#define STM_USB_DAINT_IEPINT 16
+#define STM_USB_DAINT_IEPINT_MASK 0xffff
+
+#define STM_USB_DAINTMSK_OEPM 16
+#define STM_USB_DAINTMSK_OEPM_MASK 0xffff
+#define STM_USB_DAINTMSK_IEPM 0
+#define STM_USB_DAINTMSK_IEPM_MASK 0xffff
+
+#define STM_USB_DIEPCTL_EPENA 31
+#define STM_USB_DIEPCTL_EPDIS 30
+#define STM_USB_DIEPCTL_SNAK 27
+#define STM_USB_DIEPCTL_CNAK 26
+#define STM_USB_DIEPCTL_TXFNUM 22
+#define STM_USB_DIEPCTL_TXFNUM_MASK 0xf
+#define STM_USB_DIEPCTL_STALL 21
+#define STM_USB_DIEPCTL_EPTYP 18
+#define STM_USB_DIEPCTL_EPTYP_CONTROL 0
+#define STM_USB_DIEPCTL_EPTYP_ISOCHRONOUS 1
+#define STM_USB_DIEPCTL_EPTYP_BULK 2
+#define STM_USB_DIEPCTL_EPTYP_INTERRUPT 3
+#define STM_USB_DIEPCTL_EPTYP_MASK 3
+#define STM_USB_DIEPCTL_NAKSTS 17
+#define STM_USB_DIEPCTL_EONUM 16
+#define STM_USB_DIEPCTL_USBAEP 15
+#define STM_USB_DIEPCTL_MPSIZ 0
+#define STM_USB_DIEPCTL_MPSIZ0_64 0
+#define STM_USB_DIEPCTL_MPSIZ0_32 1
+#define STM_USB_DIEPCTL_MPSIZ0_16 2
+#define STM_USB_DIEPCTL_MPSIZ0_8 3
+#define STM_USB_DIEPCTL_MPSIZ0_MASK 3
+#define STM_USB_DIEPCTL_MPSIZ_MASK 0x7f
+
+#define STM_USB_DIEPINT_NAK 13
+#define STM_USB_DIEPINT_PKTDRPSTS 11
+#define STM_USB_DIEPINT_TXFIFOUDRN 8
+#define STM_USB_DIEPINT_TXFE 7
+#define STM_USB_DIEPINT_INEPNE 6
+#define STM_USB_DIEPINT_INEPNM 5
+#define STM_USB_DIEPINT_ITTXFE 4
+#define STM_USB_DIEPINT_TOC 3
+#define STM_USB_DIEPINT_EPDISD 1
+#define STM_USB_DIEPINT_XFRC 0
+
+#define STM_USB_DIEPTSIZ_MCNT 29
+#define STM_USB_DIEPTSIZ_MCNT_MASK 3
+#define STM_USB_DIEPTSIZ_PKTCNT 19
+#define STM_USB_DIEPTSIZ_PKTCNT0_MASK 3
+#define STM_USB_DIEPTSIZ_PKTCNT_MASK 0x3ff
+#define STM_USB_DIEPTSIZ_XFRSIZ 0
+#define STM_USB_DIEPTSIZ_XFRSIZ0_MASK 0x7f
+#define STM_USB_DIEPTSIZ_XFRSIZ_MASK 0x7ffff
+
+#define STM_USB_DOEPCTL_EPENA 31
+#define STM_USB_DOEPCTL_EPDIS 30
+#define STM_USB_DOEPCTL_SNAK 27
+#define STM_USB_DOEPCTL_CNAK 26
+#define STM_USB_DOEPCTL_STALL 21
+#define STM_USB_DOEPCTL_SNPM 20
+#define STM_USB_DOEPCTL_EPTYP 18
+#define STM_USB_DOEPCTL_EPTYP_CONTROL 0
+#define STM_USB_DOEPCTL_EPTYP_ISOCHRONOUS 1
+#define STM_USB_DOEPCTL_EPTYP_BULK 2
+#define STM_USB_DOEPCTL_EPTYP_INTERRUPT 3
+#define STM_USB_DOEPCTL_EPTYP_MASK 3
+#define STM_USB_DOEPCTL_NAKSTS 17
+#define STM_USB_DOEPCTL_USBAEP 15
+#define STM_USB_DOEPCTL_MPSIZ 0
+#define STM_USB_DOEPCTL_MPSIZ0_64 0
+#define STM_USB_DOEPCTL_MPSIZ0_32 1
+#define STM_USB_DOEPCTL_MPSIZ0_16 2
+#define STM_USB_DOEPCTL_MPSIZ0_8 3
+#define STM_USB_DOEPCTL_MPSIZ0_MASK 3
+
+#define STM_USB_DOEPINT_NAK 13
+#define STM_USB_DOEPINT_BERR 12
+#define STM_USB_DOEPINT_OUTPKTERR 8
+#define STM_USB_DOEPINT_STSPHSRX 5
+#define STM_USB_DOEPINT_OTEPDIS 4
+#define STM_USB_DOEPINT_STUP 3
+#define STM_USB_DOEPINT_EPDISD 1
+#define STM_USB_DOEPINT_XFRC 0
+
+#define STM_USB_DOEPTSIZ_STUPCNT 29
+#define STM_USB_DOEPTSIZ_STUPCNT_MASK 3
+#define STM_USB_DOEPTSIZ_PKTCNT 19
+#define STM_USB_DOEPTSIZ_XFRSIZ 0
+#define STM_USB_DOEPTSIZ_XFRSIZ_MASK 0x7f
+
+/* 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_ */