altos: Add initial stm32l0 support
authorKeith Packard <keithp@keithp.com>
Sat, 20 Jun 2020 22:13:09 +0000 (15:13 -0700)
committerKeith Packard <keithp@keithp.com>
Sat, 8 Aug 2020 03:30:11 +0000 (20:30 -0700)
A low-power cortex M0 chip for MicroPeak v2.0

Signed-off-by: Keith Packard <keithp@keithp.com>
17 files changed:
src/stm32l0/Makefile-flash.defs [new file with mode: 0644]
src/stm32l0/Makefile-stm32l0.defs [new file with mode: 0644]
src/stm32l0/Makefile.defs [new file with mode: 0644]
src/stm32l0/altos.ld [new file with mode: 0644]
src/stm32l0/ao_arch.h [new file with mode: 0644]
src/stm32l0/ao_arch_funcs.h [new file with mode: 0644]
src/stm32l0/ao_dma_stm32l0.c [new file with mode: 0644]
src/stm32l0/ao_exti.h [new file with mode: 0644]
src/stm32l0/ao_exti_stm.c [new file with mode: 0644]
src/stm32l0/ao_interrupt.c [new file with mode: 0644]
src/stm32l0/ao_pwm_stm.c [new file with mode: 0644]
src/stm32l0/ao_serial_stm.c [new file with mode: 0644]
src/stm32l0/ao_spi_stm32l0.c [new file with mode: 0644]
src/stm32l0/ao_timer.c [new file with mode: 0644]
src/stm32l0/ao_timer_stm32l0.h [new file with mode: 0644]
src/stm32l0/stm32l0.h [new file with mode: 0644]
src/stm32l0/stm32l0.ld [new file with mode: 0644]

diff --git a/src/stm32l0/Makefile-flash.defs b/src/stm32l0/Makefile-flash.defs
new file mode 100644 (file)
index 0000000..08a4b17
--- /dev/null
@@ -0,0 +1,58 @@
+include $(TOPDIR)/stm/Makefile-stm.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) $(STM_CFLAGS)
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm -Wl,-Taltos-loader.ld -n
+
+PROGNAME=altos-flash
+PROG=$(HARDWARE)-$(PROGNAME)-$(VERSION).elf
+
+$(PROG): Makefile $(OBJ) altos-loader.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+$(OBJ): $(INC)
+
+all: $(PROG)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(HARDWARE)-$(PROGNAME)-*.elf
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/stm32l0/Makefile-stm32l0.defs b/src/stm32l0/Makefile-stm32l0.defs
new file mode 100644 (file)
index 0000000..1eab283
--- /dev/null
@@ -0,0 +1,16 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+# Disable floating-point support in printf to save space
+
+PICOLIBC_PRINTF_CFLAGS = -DPICOLIBC_INTEGER_PRINTF_SCANF
+
+include $(TOPDIR)/Makefile.defs
+
+vpath % $(TOPDIR)/stm32l0:$(AO_VPATH)
+
+CC=$(ARM_CC)
+
+STML0_CFLAGS=-mlittle-endian -mcpu=cortex-m0 -mthumb \
+       -I$(TOPDIR)/stm32l0 $(AO_CFLAGS) $(PICOLIBC_CFLAGS)
diff --git a/src/stm32l0/Makefile.defs b/src/stm32l0/Makefile.defs
new file mode 100644 (file)
index 0000000..911d8dc
--- /dev/null
@@ -0,0 +1,7 @@
+ifndef TOPDIR
+TOPDIR=..
+endif
+
+include $(TOPDIR)/stm32l0/Makefile-stm32l0.defs
+
+LDFLAGS=$(CFLAGS) -L$(TOPDIR)/stm32l0 -Taltos.ld
diff --git a/src/stm32l0/altos.ld b/src/stm32l0/altos.ld
new file mode 100644 (file)
index 0000000..5c9688e
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+__flash   = 0x08000000;
+__flash_size = 12k;
+__storage = __flash + __flash_size;
+__storage_size = 16k - __flash_size;
+__ram     = 0x20000000;
+__ram_size = 2k;
+__stack_size = 512;
+
+PROVIDE(__storage = __storage);
+PROVIDE(__storage_size = __storage_size);
+
+INCLUDE picolibc.ld
+INCLUDE stm32l0.ld
diff --git a/src/stm32l0/ao_arch.h b/src/stm32l0/ao_arch.h
new file mode 100644 (file)
index 0000000..e51ab31
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stdio.h>
+#include <stm32l0.h>
+
+/*
+ * STM32L0 definitions and code fragments for AltOS
+ */
+
+#ifndef AO_STACK_SIZE
+#define AO_STACK_SIZE  256
+#endif
+
+#define HAS_USB                0
+
+#define AO_PORT_TYPE   uint16_t
+
+/* Various definitions to make GCC look more like SDCC */
+
+#define ao_arch_naked_declare  __attribute__((naked))
+#define ao_arch_naked_define
+#define __interrupt(n)
+#define __at(n)
+
+#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_arch_interrupt(n)   /* nothing */
+
+/*
+ * ao_romconfig.c
+ */
+
+#define AO_ROMCONFIG_SYMBOL __attribute__((section(".romconfig"))) const
+
+#ifndef AO_SYSCLK
+#if AO_HSE
+#define AO_PLLSRC      AO_HSE
+#else
+#define AO_PLLSRC      STM_HSI_FREQ
+#endif
+
+#define AO_PLLVCO      (AO_PLLSRC * AO_PLLMUL)
+#define AO_SYSCLK      (AO_PLLVCO / AO_PLLDIV)
+#endif
+
+#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
+#define AO_FCLK                AO_HCLK
+#define AO_PCLK1       (AO_HCLK / AO_APB1_PRESCALER)
+#define AO_PCLK2       (AO_HCLK / AO_APB2_PRESCALER)
+#define AO_SYSTICK     (AO_HCLK / 8)
+
+#if AO_APB1_PRESCALER == 1
+#define AO_TIM23467_CLK                AO_PCLK1
+#else
+#define AO_TIM23467_CLK                (2 * AO_PCLK1)
+#endif
+
+#if AO_APB2_PRESCALER == 1
+#define AO_TIM91011_CLK                AO_PCLK2
+#else
+#define AO_TIM91011_CLK                (2 * AO_PCLK2)
+#endif
+
+/* The stm32l 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
+
+void ao_lcd_stm_init(void);
+
+void ao_lcd_font_init(void);
+
+void ao_lcd_font_string(char *s);
+
+extern const uint32_t  ao_radio_cal;
+
+void
+ao_adc_init(void);
+
+/* ADC maximum reported value */
+#define AO_ADC_MAX                     4095
+
+#define HAS_BOOT_LOADER                        0
+
+#ifndef AO_LED_TYPE
+#define AO_LED_TYPE uint16_t
+#endif
+
+#endif /* _AO_ARCH_H_ */
+
+
diff --git a/src/stm32l0/ao_arch_funcs.h b/src/stm32l0/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..5b23081
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_ARCH_FUNCS_H_
+#define _AO_ARCH_FUNCS_H_
+
+/* ao_spi_stm.c
+ */
+
+/* PCLK is set to 16MHz (HCLK 32MHz, APB prescaler 2) */
+
+#define AO_SPI_SPEED_8MHz      STM_SPI_CR1_BR_PCLK_2
+#define AO_SPI_SPEED_4MHz      STM_SPI_CR1_BR_PCLK_4
+#define AO_SPI_SPEED_2MHz      STM_SPI_CR1_BR_PCLK_8
+#define AO_SPI_SPEED_1MHz      STM_SPI_CR1_BR_PCLK_16
+#define AO_SPI_SPEED_500kHz    STM_SPI_CR1_BR_PCLK_32
+#define AO_SPI_SPEED_250kHz    STM_SPI_CR1_BR_PCLK_64
+#define AO_SPI_SPEED_125kHz    STM_SPI_CR1_BR_PCLK_128
+#define AO_SPI_SPEED_62500Hz   STM_SPI_CR1_BR_PCLK_256
+
+#define AO_SPI_SPEED_FAST      AO_SPI_SPEED_8MHz
+
+/* Companion bus wants something no faster than 200kHz */
+
+#define AO_SPI_SPEED_200kHz    AO_SPI_SPEED_125kHz
+
+#define AO_SPI_CPOL_BIT                4
+#define AO_SPI_CPHA_BIT                5
+
+#define AO_SPI_CONFIG_1                0x00
+#define AO_SPI_1_CONFIG_PA5_PA6_PA7    AO_SPI_CONFIG_1
+#define AO_SPI_2_CONFIG_PB13_PB14_PB15 AO_SPI_CONFIG_1
+
+#define AO_SPI_CONFIG_2                0x04
+#define AO_SPI_1_CONFIG_PB3_PB4_PB5    AO_SPI_CONFIG_2
+#define AO_SPI_2_CONFIG_PD1_PD3_PD4    AO_SPI_CONFIG_2
+
+#define AO_SPI_CONFIG_3                0x08
+#define AO_SPI_1_CONFIG_PE13_PE14_PE15 AO_SPI_CONFIG_3
+
+#define AO_SPI_CONFIG_NONE     0x0c
+
+#define AO_SPI_INDEX_MASK      0x01
+#define AO_SPI_CONFIG_MASK     0x0c
+
+#define AO_SPI_1_PA5_PA6_PA7   (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PA5_PA6_PA7)
+#define AO_SPI_1_PB3_PB4_PB5   (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PB3_PB4_PB5)
+#define AO_SPI_1_PE13_PE14_PE15        (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PE13_PE14_PE15)
+
+#define AO_SPI_2_PB13_PB14_PB15        (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PB13_PB14_PB15)
+#define AO_SPI_2_PD1_PD3_PD4   (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PD1_PD3_PD4)
+
+#define AO_SPI_INDEX(id)       ((id) & AO_SPI_INDEX_MASK)
+#define AO_SPI_CONFIG(id)      ((id) & AO_SPI_CONFIG_MASK)
+#define AO_SPI_PIN_CONFIG(id)  ((id) & (AO_SPI_INDEX_MASK | AO_SPI_CONFIG_MASK))
+#define AO_SPI_CPOL(id)                ((uint32_t) (((id) >> AO_SPI_CPOL_BIT) & 1))
+#define AO_SPI_CPHA(id)                ((uint32_t) (((id) >> AO_SPI_CPHA_BIT) & 1))
+
+#define AO_SPI_MAKE_MODE(pol,pha)      (((pol) << AO_SPI_CPOL_BIT) | ((pha) << AO_SPI_CPHA_BIT))
+#define AO_SPI_MODE_0          AO_SPI_MAKE_MODE(0,0)
+#define AO_SPI_MODE_1          AO_SPI_MAKE_MODE(0,1)
+#define AO_SPI_MODE_2          AO_SPI_MAKE_MODE(1,0)
+#define AO_SPI_MODE_3          AO_SPI_MAKE_MODE(1,1)
+
+uint8_t
+ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id);
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed);
+
+void
+ao_spi_put(uint8_t spi_index);
+
+void
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_start_bytes(uint8_t spi_index);
+
+void
+ao_spi_stop_bytes(uint8_t spi_index);
+
+static inline void
+ao_spi_send_byte(uint8_t byte, uint8_t spi_index)
+{
+       struct stm_spi  *stm_spi;
+
+       switch (AO_SPI_INDEX(spi_index)) {
+       case 0:
+               stm_spi = &stm_spi1;
+               break;
+       case 1:
+               stm_spi = &stm_spi2;
+               break;
+       }
+
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)))
+               ;
+       stm_spi->dr = byte;
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE)))
+               ;
+       (void) stm_spi->dr;
+}
+
+static inline uint8_t
+ao_spi_recv_byte(uint8_t spi_index)
+{
+       struct stm_spi  *stm_spi;
+
+       switch (AO_SPI_INDEX(spi_index)) {
+       case 0:
+               stm_spi = &stm_spi1;
+               break;
+       case 1:
+               stm_spi = &stm_spi2;
+               break;
+       }
+
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)))
+               ;
+       stm_spi->dr = 0xff;
+       while (!(stm_spi->sr & (1 << STM_SPI_SR_RXNE)))
+               ;
+       return stm_spi->dr;
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index);
+
+extern uint16_t        ao_spi_speed[STM_NUM_SPI];
+
+void
+ao_spi_init(void);
+
+#define ao_spi_set_cs(reg,mask) ((reg)->bsrr = ((uint32_t) (mask)) << 16)
+#define ao_spi_clr_cs(reg,mask) ((reg)->bsrr = (mask))
+
+#define ao_spi_get_mask(reg,mask,bus, speed) do {              \
+               ao_spi_get(bus, speed);                         \
+               ao_spi_set_cs(reg,mask);                        \
+       } while (0)
+
+static inline uint8_t
+ao_spi_try_get_mask(struct stm_gpio *reg, uint16_t mask, uint8_t bus, uint32_t speed, uint8_t task_id)
+{
+       if (!ao_spi_try_get(bus, speed, task_id))
+               return 0;
+       ao_spi_set_cs(reg, mask);
+       return 1;
+}
+
+#define ao_spi_put_mask(reg,mask,bus) do {     \
+               ao_spi_clr_cs(reg,mask);        \
+               ao_spi_put(bus);                \
+       } while (0)
+
+#define ao_spi_get_bit(reg,bit,bus,speed) ao_spi_get_mask(reg,(1<<bit),bus,speed)
+#define ao_spi_put_bit(reg,bit,bus) ao_spi_put_mask(reg,(1<<bit),bus)
+
+#define ao_enable_port(port) do {                                      \
+               if ((port) == &stm_gpioa)                               \
+                       stm_rcc.iopenr |= (1 << STM_RCC_IOPENR_IOPAEN); \
+               else if ((port) == &stm_gpiob)                          \
+                       stm_rcc.iopenr |= (1 << STM_RCC_IOPENR_IOPBEN); \
+               else if ((port) == &stm_gpioc)                          \
+                       stm_rcc.iopenr |= (1 << STM_RCC_IOPENR_IOPCEN); \
+               else if ((port) == &stm_gpiod)                          \
+                       stm_rcc.iopenr |= (1 << STM_RCC_IOPENR_IOPDEN); \
+               else if ((port) == &stm_gpioe)                          \
+                       stm_rcc.iopenr |= (1 << STM_RCC_IOPENR_IOPEEN); \
+               else if ((port) == &stm_gpioh)                          \
+                       stm_rcc.iopenr |= (1 << STM_RCC_IOPENR_IOPHEN); \
+       } while (0)
+
+#define ao_disable_port(port) do {                                     \
+               if ((port) == &stm_gpioa)                               \
+                       stm_rcc.iopenr &= ~(1 << STM_RCC_IOPENR_IOPAEN); \
+               else if ((port) == &stm_gpiob)                          \
+                       stm_rcc.iopenr &= ~(1 << STM_RCC_IOPENR_IOPBEN); \
+               else if ((port) == &stm_gpioc)                          \
+                       stm_rcc.iopenr &= ~(1 << STM_RCC_IOPENR_IOPCEN); \
+               else if ((port) == &stm_gpiod)                          \
+                       stm_rcc.iopenr &= ~(1 << STM_RCC_IOPENR_IOPDEN); \
+               else if ((port) == &stm_gpioe)                          \
+                       stm_rcc.iopenr &= ~(1 << STM_RCC_IOPENR_IOPEEN); \
+               else if ((port) == &stm_gpioh)                          \
+                       stm_rcc.iopenr &= ~(1 << STM_RCC_IOPENR_IOPHEN); \
+       } while (0)
+
+
+#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_gpio_set_bits(port, bits) stm_gpio_set_bits(port, bits)
+
+#define ao_gpio_set_mask(port, bits, mask) stm_gpio_set_mask(port, bits, mask)
+
+#define ao_gpio_clr_bits(port, bits) stm_gpio_clr_bits(port, bits);
+
+#define ao_gpio_get_all(port) stm_gpio_get_all(port)
+
+#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_enable_output_mask(port,bits,mask) do {             \
+               ao_enable_port(port);                           \
+               ao_gpio_set_mask(port, bits, mask);             \
+               ao_set_output_mask(port, mask);                 \
+       } while (0)
+
+#define AO_OUTPUT_PUSH_PULL    STM_OTYPER_PUSH_PULL
+#define AO_OUTPUT_OPEN_DRAIN   STM_OTYPER_OPEN_DRAIN
+
+#define ao_gpio_set_output_mode(port,bit,mode) \
+       stm_otyper_set(port, pin, mode)
+
+#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_gpio_set_mode_mask(port,mask,mode) do {                     \
+               if (mode == AO_EXTI_MODE_PULL_UP)                       \
+                       stm_pupdr_set_mask(port, mask, STM_PUPDR_PULL_UP); \
+               else if (mode == AO_EXTI_MODE_PULL_DOWN)                \
+                       stm_pupdr_set_mask(port, mask, STM_PUPDR_PULL_DOWN); \
+               else                                                    \
+                       stm_pupdr_set_mask(port, mask, STM_PUPDR_NONE); \
+       } while (0)
+
+#define ao_set_input(port, bit) do {                           \
+               stm_moder_set(port, bit, STM_MODER_INPUT);      \
+       } while (0)
+
+#define ao_set_output(port, bit, v) do {                       \
+               ao_gpio_set(port, bit, v);                      \
+               stm_moder_set(port, bit, STM_MODER_OUTPUT);     \
+       } while (0)
+
+#define ao_set_output_mask(port, mask) do {                    \
+               stm_moder_set_mask(port, mask, STM_MODER_OUTPUT);       \
+       } while (0)
+
+#define ao_set_input_mask(port, mask) do {                             \
+               stm_moder_set_mask(port, mask, STM_MODER_INPUT);        \
+       } while (0)
+
+#define ao_enable_input(port,bit,mode) do {                            \
+               ao_enable_port(port);                                   \
+               ao_set_input(port, bit);                                \
+               ao_gpio_set_mode(port, bit, mode);                      \
+       } while (0)
+
+#define ao_enable_input_mask(port,mask,mode) do {      \
+               ao_enable_port(port);                   \
+               ao_gpio_set_mode_mask(port, mask, mode);        \
+               ao_set_input_mask(port, mask);          \
+       } while (0)
+
+#define _ao_enable_cs(port, bit) do {                          \
+               stm_gpio_set((port), bit, 1);                   \
+               stm_moder_set((port), bit, STM_MODER_OUTPUT);   \
+       } while (0)
+
+#define ao_enable_cs(port,bit) do {                            \
+               ao_enable_port(port);                           \
+               _ao_enable_cs(port, bit);                       \
+       } while (0)
+
+#define ao_spi_init_cs(port, mask) do {                                \
+               ao_enable_port(port);                           \
+               if ((mask) & 0x0001) _ao_enable_cs(port, 0);    \
+               if ((mask) & 0x0002) _ao_enable_cs(port, 1);    \
+               if ((mask) & 0x0004) _ao_enable_cs(port, 2);    \
+               if ((mask) & 0x0008) _ao_enable_cs(port, 3);    \
+               if ((mask) & 0x0010) _ao_enable_cs(port, 4);    \
+               if ((mask) & 0x0020) _ao_enable_cs(port, 5);    \
+               if ((mask) & 0x0040) _ao_enable_cs(port, 6);    \
+               if ((mask) & 0x0080) _ao_enable_cs(port, 7);    \
+               if ((mask) & 0x0100) _ao_enable_cs(port, 8);    \
+               if ((mask) & 0x0200) _ao_enable_cs(port, 9);    \
+               if ((mask) & 0x0400) _ao_enable_cs(port, 10);\
+               if ((mask) & 0x0800) _ao_enable_cs(port, 11);\
+               if ((mask) & 0x1000) _ao_enable_cs(port, 12);\
+               if ((mask) & 0x2000) _ao_enable_cs(port, 13);\
+               if ((mask) & 0x4000) _ao_enable_cs(port, 14);\
+               if ((mask) & 0x8000) _ao_enable_cs(port, 15);\
+       } while (0)
+
+/* ao_dma_stm.c
+ */
+
+extern uint8_t ao_dma_done[STM_NUM_DMA];
+
+void
+ao_dma_set_transfer(uint8_t            index,
+                   volatile void       *peripheral,
+                   void                *memory,
+                   uint16_t            count,
+                   uint32_t            ccr);
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int index));
+
+void
+ao_dma_start(uint8_t index);
+
+void
+ao_dma_done_transfer(uint8_t index);
+
+void
+ao_dma_alloc(uint8_t index);
+
+void
+ao_dma_init(void);
+
+/* ao_i2c_stm.c */
+
+void
+ao_i2c_get(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_start(uint8_t i2c_index, uint16_t address);
+
+void
+ao_i2c_put(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+void
+ao_i2c_init(void);
+
+#if USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_SW_FLOW 1
+#else
+#define HAS_SERIAL_SW_FLOW 0
+#endif
+
+#if USE_SERIAL_1_FLOW && !USE_SERIAL_1_SW_FLOW || USE_SERIAL_2_FLOW && !USE_SERIAL_2_SW_FLOW || USE_SERIAL_3_FLOW && !USE_SERIAL_3_SW_FLOW
+#define HAS_SERIAL_HW_FLOW 1
+#else
+#define HAS_SERIAL_HW_FLOW 0
+#endif
+
+/* ao_serial_stm.c */
+struct ao_stm_usart {
+       struct ao_fifo          rx_fifo;
+       struct ao_fifo          tx_fifo;
+       struct stm_usart        *reg;
+       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
+};
+
+void
+ao_debug_out(char c);
+
+#if HAS_SERIAL_1
+extern struct ao_stm_usart     ao_stm_usart1;
+#endif
+
+#if HAS_SERIAL_2
+extern struct ao_stm_usart     ao_stm_usart2;
+#endif
+
+#if HAS_SERIAL_3
+extern struct ao_stm_usart     ao_stm_usart3;
+#endif
+
+#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(void) {
+       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 = &task->stack32[AO_STACK_SIZE >> 2];
+       uint32_t        a = (uint32_t) start;
+       int             i;
+
+       /* Return address (goes into LR) */
+       ARM_PUSH32(sp, a);
+
+       /* Clear register values r0-r7 */
+       i = 8;
+       while (i--)
+               ARM_PUSH32(sp, 0);
+
+       /* APSR */
+       ARM_PUSH32(sp, 0);
+
+       /* PRIMASK with interrupts enabled */
+       ARM_PUSH32(sp, 0);
+
+       task->sp32 = sp;
+}
+
+static inline void ao_arch_save_regs(void) {
+       /* Save general registers */
+       asm("push {r0-r7,lr}\n");
+
+       /* Save APSR */
+       asm("mrs r0,apsr");
+       asm("push {r0}");
+
+       /* Save PRIMASK */
+       asm("mrs r0,primask");
+       asm("push {r0}");
+}
+
+static inline void ao_arch_save_stack(void) {
+       uint32_t        *sp;
+       asm("mov %0,sp" : "=&r" (sp) );
+       ao_cur_task->sp32 = (sp);
+}
+
+static inline void ao_arch_restore_stack(void) {
+       /* Switch stacks */
+       asm("mov sp, %0" : : "r" (ao_cur_task->sp32) );
+
+       /* Restore PRIMASK */
+       asm("pop {r0}");
+       asm("msr primask,r0");
+
+       /* Restore APSR */
+       asm("pop {r0}");
+       asm("msr apsr_nczvq,r0");
+
+       /* Restore general registers */
+       asm("pop {r0-r7,pc}\n");
+}
+
+#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)
+
+void start(void);
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/stm32l0/ao_dma_stm32l0.c b/src/stm32l0/ao_dma_stm32l0.c
new file mode 100644 (file)
index 0000000..962b3ac
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#include "ao.h"
+
+#define NUM_DMA        7
+
+struct ao_dma_config {
+       void            (*isr)(int index);
+};
+
+uint8_t ao_dma_done[NUM_DMA];
+
+static struct ao_dma_config ao_dma_config[NUM_DMA];
+static uint8_t ao_dma_allocated[NUM_DMA];
+static uint8_t ao_dma_mutex[NUM_DMA];
+
+static void
+ao_dma_isr(uint8_t index) {
+       /* Get channel interrupt bits */
+       uint32_t        isr = stm_dma.isr & (STM_DMA_ISR_MASK <<
+                                            STM_DMA_ISR(index));
+
+       /* Ack them */
+       stm_dma.ifcr = isr;
+       if (ao_dma_config[index].isr)
+               (*ao_dma_config[index].isr)(index);
+       else {
+               ao_dma_done[index] = 1;
+               ao_wakeup(&ao_dma_done[index]);
+       }
+}
+
+void stm_dma1_channel1_isr(void) { ao_dma_isr(STM_DMA_INDEX(1)); }
+void stm_dma1_channel2_isr(void) { ao_dma_isr(STM_DMA_INDEX(2)); }
+#ifdef STM_DMA1_3_STOLEN
+#define LEAVE_DMA_ON
+#else
+void stm_dma1_channel3_isr(void) { ao_dma_isr(STM_DMA_INDEX(3)); }
+#endif
+void stm_dma1_channel4_isr(void) { ao_dma_isr(STM_DMA_INDEX(4)); }
+#ifdef STM_DMA1_5_STOLEN
+#define LEAVE_DMA_ON
+#else
+void stm_dma1_channel5_isr(void) { ao_dma_isr(STM_DMA_INDEX(5)); }
+#endif
+void stm_dma1_channel6_isr(void) { ao_dma_isr(STM_DMA_INDEX(6)); }
+void stm_dma1_channel7_isr(void) { ao_dma_isr(STM_DMA_INDEX(7)); }
+
+#ifndef LEAVE_DMA_ON
+static uint8_t ao_dma_active;
+#endif
+
+void
+ao_dma_set_transfer(uint8_t            index,
+                   volatile void       *peripheral,
+                   void                *memory,
+                   uint16_t            count,
+                   uint32_t            ccr)
+{
+       if (ao_dma_allocated[index]) {
+               if (ao_dma_mutex[index])
+                       ao_panic(AO_PANIC_DMA);
+               ao_dma_mutex[index] = 0xff;
+       } else
+               ao_mutex_get(&ao_dma_mutex[index]);
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (ao_dma_active++ == 0)
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+       stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE);
+       stm_dma.channel[index].cndtr = count;
+       stm_dma.channel[index].cpar = peripheral;
+       stm_dma.channel[index].cmar = memory;
+       ao_dma_config[index].isr = NULL;
+}
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int))
+{
+       ao_dma_config[index].isr = isr;
+}
+
+void
+ao_dma_start(uint8_t index)
+{
+       ao_dma_done[index] = 0;
+       stm_dma.channel[index].ccr |= (1 << STM_DMA_CCR_EN);
+}
+
+void
+ao_dma_done_transfer(uint8_t index)
+{
+       stm_dma.channel[index].ccr &= ~(1 << STM_DMA_CCR_EN);
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (--ao_dma_active == 0)
+                       stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+       if (ao_dma_allocated[index])
+               ao_dma_mutex[index] = 0;
+       else
+               ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_alloc(uint8_t index)
+{
+       if (ao_dma_allocated[index])
+               ao_panic(AO_PANIC_DMA);
+       ao_dma_allocated[index] = 1;
+}
+
+#if DEBUG
+void
+ao_dma_dump_cmd(void)
+{
+       int i;
+
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (ao_dma_active++ == 0)
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+       printf ("isr %08x ifcr%08x\n", stm_dma.isr, stm_dma.ifcr);
+       for (i = 0; i < NUM_DMA; i++)
+               printf("%d: done %d allocated %d mutex %2d ccr %04x cndtr %04x cpar %08x cmar %08x isr %08x\n",
+                      i,
+                      ao_dma_done[i],
+                      ao_dma_allocated[i],
+                      ao_dma_mutex[i],
+                      stm_dma.channel[i].ccr,
+                      stm_dma.channel[i].cndtr,
+                      stm_dma.channel[i].cpar,
+                      stm_dma.channel[i].cmar,
+                      ao_dma_config[i].isr);
+#ifndef LEAVE_DMA_ON
+       ao_arch_critical(
+               if (--ao_dma_active == 0)
+                       stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
+               );
+#endif
+}
+
+static const struct ao_cmds ao_dma_cmds[] = {
+       { ao_dma_dump_cmd,      "D\0Dump DMA status" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_dma_init(void)
+{
+       int     index;
+
+#ifdef LEAVE_DMA_ON
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+#endif
+       for (index = 0; index < STM_NUM_DMA; index++) {
+#if STM_DMA1_5_STOLEN
+               if (index == STM_DMA_INDEX(5)) {
+                       ao_dma_allocated[index] = 1;
+                       ao_dma_mutex[index] = 0xff;
+                       continue;
+               }
+#endif
+#if STM_DMA1_3_STOLEN
+               if (index == STM_DMA_INDEX(3)) {
+                       ao_dma_allocated[index] = 1;
+                       ao_dma_mutex[index] = 0xff;
+                       continue;
+               }
+#endif
+               stm_nvic_set_enable(STM_ISR_DMA1_CHANNEL1_POS + index);
+               stm_nvic_set_priority(STM_ISR_DMA1_CHANNEL1_POS + index,
+                                     AO_STM_NVIC_MED_PRIORITY);
+               ao_dma_allocated[index] = 0;
+               ao_dma_mutex[index] = 0;
+       }
+#if DEBUG
+       ao_cmd_register(&ao_dma_cmds[0]);
+#endif
+}
diff --git a/src/stm32l0/ao_exti.h b/src/stm32l0/ao_exti.h
new file mode 100644 (file)
index 0000000..1d19a48
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#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 0
+#define AO_EXTI_MODE_PULL_UP   4
+#define AO_EXTI_MODE_PULL_DOWN 8
+#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));
+
+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));
+
+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/stm32l0/ao_exti_stm.c b/src/stm32l0/ao_exti_stm.c
new file mode 100644 (file)
index 0000000..3a2748a
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#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)) {
+       (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/stm32l0/ao_interrupt.c b/src/stm32l0/ao_interrupt.c
new file mode 100644 (file)
index 0000000..47effe5
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#include <ao.h>
+#include <string.h>
+#include <ao_boot.h>
+
+/* Interrupt functions */
+
+void stm_halt_isr(void)
+{
+       ao_panic(AO_PANIC_CRASH);
+}
+
+void stm_ignore_isr(void)
+{
+}
+
+void _start(void);
+
+#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(rtc)
+isr(flash)
+isr(rcc_crs)
+isr(exti1_0)
+isr(exti3_2)
+isr(exti15_4)
+isr(dma1_channel1)
+isr(dma1_channel3_2)
+isr(dma1_channel7_4)
+isr(adc_comp)
+isr(lptim1)
+isr(usart4_usart5)
+isr(tim2)
+isr(tim3)
+isr(tim4)
+isr(tim6)
+isr(tim7)
+isr(tim21)
+isr(i2c3)
+isr(tim22)
+isr(i2c1)
+isr(i2c2)
+isr(spi1)
+isr(spi2)
+isr(usart1)
+isr(usart2)
+isr(usart3)
+isr(lpuart1_aes)
+
+#define i(addr,name)   [(addr)/4] = stm_ ## name ## _isr
+
+extern char __stack[];
+void _start(void) __attribute__((__noreturn__));
+void main(void) __attribute__((__noreturn__));
+
+__attribute__ ((section(".init")))
+const void *const __interrupt_vector[] = {
+       [0] = &__stack,
+       [1] = _start,
+       i(0x08, nmi),
+       i(0x0c, hardfault),
+       i(0x2c, svc),
+       i(0x38, pendsv),
+       i(0x3c, systick),
+       i(0x40, wwdg),
+       i(0x44, pvd),
+       i(0x48, rtc),
+       i(0x4c, flash),
+       i(0x50, rcc_crs),
+       i(0x54, exti1_0),
+       i(0x58, exti3_2),
+       i(0x5c, exti15_4),
+       i(0x64, dma1_channel1),
+       i(0x68, dma1_channel3_2),
+       i(0x6c, dma1_channel7_4),
+       i(0x70, adc_comp),
+       i(0x74, lptim1),
+       i(0x78, usart4_usart5),
+       i(0x7c, tim2),
+       i(0x80, tim3),
+       i(0x84, tim6),
+       i(0x88, tim7),
+       i(0x90, tim21),
+       i(0x94, i2c3),
+       i(0x98, tim22),
+       i(0x9c, i2c1),
+       i(0xa0, i2c2),
+       i(0xa4, spi1),
+       i(0xa8, spi2),
+       i(0xac, usart1),
+       i(0xb0, usart2),
+       i(0xb4, lpuart1_aes),
+};
+
+extern char __data_source[];
+extern char __data_start[];
+extern char __data_size[];
+extern char __bss_start[];
+extern char __bss_size[];
+
+void _start(void)
+{
+#ifdef AO_BOOT_CHAIN
+       if (ao_boot_check_chain()) {
+#ifdef AO_BOOT_PIN
+               if (ao_boot_check_pin())
+#endif
+               {
+                       ao_boot_chain(AO_BOOT_APPLICATION_BASE);
+               }
+       }
+#endif
+       memcpy(__data_start, __data_source, (uintptr_t) __data_size);
+       memset(__bss_start, '\0', (uintptr_t) __bss_size);
+
+       main();
+}
diff --git a/src/stm32l0/ao_pwm_stm.c b/src/stm32l0/ao_pwm_stm.c
new file mode 100644 (file)
index 0000000..341f888
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright © 2015 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_pwm.h"
+
+static uint8_t pwm_running;
+
+static uint16_t        pwm_value[NUM_PWM];
+
+static void
+ao_pwm_up(void)
+{
+       if (pwm_running++ == 0) {
+               struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+               tim->ccr1 = 0;
+               tim->ccr2 = 0;
+               tim->ccr3 = 0;
+               tim->ccr4 = 0;
+               tim->arr = PWM_MAX - 1; /* turn on the timer */
+               tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                           (0 << STM_TIM234_CR1_ARPE) |
+                           (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                           (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+                           (0 << STM_TIM234_CR1_OPM) |
+                           (0 << STM_TIM234_CR1_URS) |
+                           (0 << STM_TIM234_CR1_UDIS) |
+                           (1 << STM_TIM234_CR1_CEN));
+
+               /* Set the timer running */
+               tim->egr = (1 << STM_TIM234_EGR_UG);
+       }
+}
+
+static void
+ao_pwm_down(void)
+{
+       if (--pwm_running == 0) {
+               struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+               tim->arr = 0;
+               tim->cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                           (0 << STM_TIM234_CR1_ARPE) |
+                           (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                           (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+                           (0 << STM_TIM234_CR1_OPM) |
+                           (0 << STM_TIM234_CR1_URS) |
+                           (0 << STM_TIM234_CR1_UDIS) |
+                           (0 << STM_TIM234_CR1_CEN));
+
+               /* Stop the timer */
+               tim->egr = (1 << STM_TIM234_EGR_UG);
+       }
+}
+
+void
+ao_pwm_set(uint8_t pwm, uint16_t value)
+{
+       struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+       if (value > PWM_MAX)
+               value = PWM_MAX;
+       if (value != 0) {
+               if (pwm_value[pwm] == 0)
+                       ao_pwm_up();
+       }
+       switch (pwm) {
+       case 0:
+               tim->ccr1 = value;
+               break;
+       case 1:
+               tim->ccr2 = value;
+               break;
+       case 2:
+               tim->ccr3 = value;
+               break;
+       case 3:
+               tim->ccr4 = value;
+               break;
+       }
+       if (value == 0) {
+               if (pwm_value[pwm] != 0)
+                       ao_pwm_down();
+       }
+       pwm_value[pwm] = value;
+}
+
+static void
+ao_pwm_cmd(void)
+{
+       uint8_t ch;
+       uint16_t val;
+
+       ch = ao_cmd_decimal();
+       val = ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+
+       printf("Set channel %d to %d\n", ch, val);
+       ao_pwm_set(ch, val);
+}
+
+static const struct ao_cmds ao_pwm_cmds[] = {
+       { ao_pwm_cmd,   "P <ch> <val>\0Set PWM ch to val" },
+       { 0, NULL },
+};
+
+void
+ao_pwm_init(void)
+{
+       struct stm_tim234       *tim = &AO_PWM_TIMER;
+
+       stm_rcc.apb1enr |= (1 << AO_PWM_TIMER_ENABLE);
+
+       tim->cr1 = 0;
+       tim->psc = AO_PWM_TIMER_SCALE - 1;
+       tim->cnt = 0;
+       tim->ccer = ((1 << STM_TIM234_CCER_CC1E) |
+                    (0 << STM_TIM234_CCER_CC1P) |
+                    (1 << STM_TIM234_CCER_CC2E) |
+                    (0 << STM_TIM234_CCER_CC2P) |
+                    (1 << STM_TIM234_CCER_CC3E) |
+                    (0 << STM_TIM234_CCER_CC3P) |
+                    (1 << STM_TIM234_CCER_CC4E) |
+                    (0 << STM_TIM234_CCER_CC4P));
+
+       tim->ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+                     (STM_TIM234_CCMR1_OC2M_PWM_MODE_1 << STM_TIM234_CCMR1_OC2M) |
+                     (0 << STM_TIM234_CCMR1_OC2PE) |
+                     (0 << STM_TIM234_CCMR1_OC2FE) |
+                     (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+                     (0 << STM_TIM234_CCMR1_OC1CE) |
+                     (STM_TIM234_CCMR1_OC1M_PWM_MODE_1 << STM_TIM234_CCMR1_OC1M) |
+                     (0 << STM_TIM234_CCMR1_OC1PE) |
+                     (0 << STM_TIM234_CCMR1_OC1FE) |
+                     (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+
+       tim->ccmr2 = ((0 << STM_TIM234_CCMR2_OC4CE) |
+                     (STM_TIM234_CCMR2_OC4M_PWM_MODE_1 << STM_TIM234_CCMR2_OC4M) |
+                     (0 << STM_TIM234_CCMR2_OC4PE) |
+                     (0 << STM_TIM234_CCMR2_OC4FE) |
+                     (STM_TIM234_CCMR2_CC4S_OUTPUT << STM_TIM234_CCMR2_CC4S) |
+
+                     (0 << STM_TIM234_CCMR2_OC3CE) |
+                     (STM_TIM234_CCMR2_OC3M_PWM_MODE_1 << STM_TIM234_CCMR2_OC3M) |
+                     (0 << STM_TIM234_CCMR2_OC3PE) |
+                     (0 << STM_TIM234_CCMR2_OC3FE) |
+                     (STM_TIM234_CCMR2_CC3S_OUTPUT << STM_TIM234_CCMR2_CC3S));
+       tim->egr = 0;
+
+       tim->sr = 0;
+       tim->dier = 0;
+       tim->smcr = 0;
+       tim->cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+                   (STM_TIM234_CR2_MMS_RESET<< STM_TIM234_CR2_MMS) |
+                   (0 << STM_TIM234_CR2_CCDS));
+
+       stm_afr_set(AO_PWM_0_GPIO, AO_PWM_0_PIN, STM_AFR_AF2);
+       stm_ospeedr_set(AO_PWM_0_GPIO, AO_PWM_0_PIN, STM_OSPEEDR_40MHz);
+#if NUM_PWM > 1
+       stm_afr_set(AO_PWM_1_GPIO, AO_PWM_1_PIN, STM_AFR_AF2);
+       stm_ospeedr_set(AO_PWM_1_GPIO, AO_PWM_1_PIN, STM_OSPEEDR_40MHz);
+#endif
+#if NUM_PWM > 2
+       stm_afr_set(AO_PWM_2_GPIO, AO_PWM_2_PIN, STM_AFR_AF2);
+       stm_ospeedr_set(AO_PWM_2_GPIO, AO_PWM_2_PIN, STM_OSPEEDR_40MHz);
+#endif
+#if NUM_PWM > 3
+       stm_afr_set(AO_PWM_3_GPIO, AO_PWM_3_PIN, STM_AFR_AF2);
+       stm_ospeedr_set(AO_PWM_3_GPIO, AO_PWM_3_PIN, STM_OSPEEDR_40MHz);
+#endif
+       ao_cmd_register(&ao_pwm_cmds[0]);
+}
diff --git a/src/stm32l0/ao_serial_stm.c b/src/stm32l0/ao_serial_stm.c
new file mode 100644 (file)
index 0000000..8471004
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * Copyright © 2020 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.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+void
+ao_debug_out(char c)
+{
+       if (c == '\n')
+               ao_debug_out('\r');
+       while (!(stm_usart1.isr & (1 << STM_USART_ISR_TXE)));
+       stm_usart1.tdr = c;
+}
+
+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->isr & (1 << STM_USART_ISR_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->tdr);
+                       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->isr & (1 << STM_USART_ISR_RXNE)) {
+               usart->reg->icr = (1 << STM_USART_ICR_ORECF);
+               if (!ao_fifo_full(usart->rx_fifo)) {
+                       ao_fifo_insert(usart->rx_fifo, usart->reg->rdr);
+                       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->isr & (1 << STM_USART_ISR_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;
+}
+
+#if 0
+static inline uint8_t
+_ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout)
+{
+       return ao_sleep_for(&usart->rx_fifo, timeout);
+}
+#endif
+
+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 uint32_t ao_usart_speeds[] = {
+       [AO_SERIAL_SPEED_4800] = 4800,
+       [AO_SERIAL_SPEED_9600] = 9600,
+       [AO_SERIAL_SPEED_19200] = 19200,
+       [AO_SERIAL_SPEED_57600] = 57600,
+       [AO_SERIAL_SPEED_115200] = 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_PCLK2 / ao_usart_speeds[speed];
+}
+
+static void
+ao_usart_init(struct ao_stm_usart *usart, int hw_flow)
+{
+       usart->reg->cr1 = ((0 << STM_USART_CR1_M1) |
+                          (0 << STM_USART_CR1_EOBIE) |
+                          (0 << STM_USART_CR1_RTOIE) |
+                          (0 << STM_USART_CR1_DEAT) |
+                          (0 << STM_USART_CR1_DEDT) |
+                          (0 << STM_USART_CR1_OVER8) |
+                          (0 << STM_USART_CR1_CMIE) |
+                          (0 << STM_USART_CR1_MME) |
+                          (0 << STM_USART_CR1_M0) |
+                          (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_UESM) |
+                          (0 << STM_USART_CR1_UE));
+
+       usart->reg->cr2 = ((0 << STM_USART_CR2_ADD) |
+                          (0 << STM_USART_CR2_RTOEN) |
+                          (0 << STM_USART_CR2_ABRMOD) |
+                          (0 << STM_USART_CR2_ABREN) |
+                          (0 << STM_USART_CR2_MSBFIRST) |
+                          (0 << STM_USART_CR2_DATAINV) |
+                          (0 << STM_USART_CR2_TXINV) |
+                          (0 << STM_USART_CR2_RXINV) |
+                          (0 << STM_USART_CR2_SWAP) |
+                          (0 << STM_USART_CR2_LINEN) |
+                          (0 << STM_USART_CR2_STOP) |
+                          (0 << STM_USART_CR2_CLKEN) |
+                          (0 << STM_USART_CR2_CPOL) |
+                          (0 << STM_USART_CR2_CHPA) |
+                          (0 << STM_USART_CR2_LBCL) |
+                          (0 << STM_USART_CR2_LBDIE) |
+                          (0 << STM_USART_CR2_LBDL) |
+                          (0 << STM_USART_CR2_ADDM7));
+
+       uint32_t cr3 = ((0 << STM_USART_CR3_WUFIE) |
+                       (0 << STM_USART_CR3_WUS) |
+                       (0 << STM_USART_CR3_SCARCNT) |
+                       (0 << STM_USART_CR3_DEP) |
+                       (0 << STM_USART_CR3_DEM) |
+                       (0 << STM_USART_CR3_DDRE) |
+                       (0 << STM_USART_CR3_OVRDIS) |
+                       (0 << STM_USART_CR3_ONEBIT) |
+                       (0 << STM_USART_CR3_CTIIE) |
+                       (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)
+               cr3 |= ((1 << STM_USART_CR3_CTSE) |
+                       (1 << STM_USART_CR3_RTSE));
+
+       usart->reg->cr3 = cr3;
+
+       /* Pick a 9600 baud rate */
+       ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+
+       /* Enable the usart */
+       usart->reg->cr1 |= (1 << STM_USART_CR1_UE);
+}
+
+#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);
+}
+
+#if 0
+uint8_t
+_ao_serial1_sleep_for(uint16_t timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
+}
+#endif
+
+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);
+}
+
+#if 0
+uint8_t
+_ao_serial2_sleep_for(uint16_t timeout)
+{
+       return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
+}
+#endif
+
+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 USE_SERIAL_2_FLOW && USE_SERIAL_2_SW_FLOW
+void
+ao_serial2_cts(void)
+{
+       _ao_usart_cts(&ao_stm_usart2);
+}
+#endif
+
+#endif /* HAS_SERIAL_2 */
+
+#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_shutdown(void)
+{
+#if HAS_SERIAL_1
+       stm_rcc.apb2enr &= ~(1 << STM_RCC_APB2ENR_USART1EN);
+#endif
+#if HAS_SERIAL_2
+       stm_rcc.apb1enr &= ~(1 << STM_RCC_APB1ENR_USART2EN);
+#endif
+}
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+#endif
+
+#if HAS_SERIAL_2
+       /*
+        *      TX      RX
+        *      PA2     PA3
+        *      PA9     PA10
+        *      PA14    PA15
+        *      PB6     PB7
+        */
+
+# if SERIAL_2_PA2_PA3
+       ao_enable_port(&stm_gpioa);
+       stm_afr_set(&stm_gpioa, 2, STM_AFR_AF4);
+       stm_afr_set(&stm_gpioa, 3, STM_AFR_AF4);
+# elif SERIAL_2_PA9_PA10
+       ao_enable_port(&stm_gpioa);
+       stm_afr_set(&stm_gpioa, 9, STM_AFR_AF4);
+       stm_afr_set(&stm_gpioa, 10, STM_AFR_AF4);
+# elif SERIAL_2_PA14_PA15
+       ao_enable_port(&stm_gpioa);
+       stm_afr_set(&stm_gpioa, 14, STM_AFR_AF4);
+       stm_afr_set(&stm_gpioa, 15, STM_AFR_AF4);
+# elif SERIAL_2_PB6_PB7
+       ao_enable_port(&stm_gpiob);
+       stm_afr_set(&stm_gpiob, 6, STM_AFR_AF0);
+       stm_afr_set(&stm_gpiob, 7, STM_AFR_AF0);
+# else
+#  error "No SERIAL_2 port configuration specified"
+# 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, 4);
+# if USE_SERIAL_2_STDIN && !DELAY_SERIAL_2_STDIN
+       ao_add_stdio(_ao_serial2_pollchar,
+                    ao_serial2_putchar,
+                    NULL);
+# endif
+#endif
+}
diff --git a/src/stm32l0/ao_spi_stm32l0.c b/src/stm32l0/ao_spi_stm32l0.c
new file mode 100644 (file)
index 0000000..1a04a28
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ * 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.
+ */
+
+#include <ao.h>
+
+struct ao_spi_stm_info {
+       uint8_t miso_dma_index;
+       uint8_t mosi_dma_index;
+       struct stm_spi *stm_spi;
+};
+
+static uint8_t         ao_spi_mutex[STM_NUM_SPI];
+static uint8_t         ao_spi_pin_config[STM_NUM_SPI];
+
+static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
+       {
+               .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
+               .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
+               &stm_spi1
+       },
+       {
+               .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
+               .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
+               &stm_spi2
+       }
+};
+
+static uint8_t spi_dev_null;
+
+#if DEBUG
+static struct {
+       uint8_t task;
+       uint8_t which;
+       AO_TICK_TYPE tick;
+       uint16_t len;
+} spi_tasks[64];
+static uint8_t spi_task_index;
+
+static void
+validate_spi(struct stm_spi *stm_spi, int which, uint16_t len)
+{
+       uint32_t        sr = stm_spi->sr;
+
+       if (stm_spi != &stm_spi2)
+               return;
+       spi_tasks[spi_task_index].task = ao_cur_task ? ao_cur_task->task_id : 0;
+       spi_tasks[spi_task_index].which = which;
+       spi_tasks[spi_task_index].tick = ao_time();
+       spi_tasks[spi_task_index].len = len;
+       spi_task_index = (spi_task_index + 1) & (63);
+       if (sr & (1 << STM_SPI_SR_FRE))
+               ao_panic(0x40 | 1);
+       if (sr & (1 << STM_SPI_SR_BSY))
+               ao_panic(0x40 | 2);
+       if (sr & (1 << STM_SPI_SR_OVR))
+               ao_panic(0x40 | 3);
+       if (sr & (1 << STM_SPI_SR_MODF))
+               ao_panic(0x40 | 4);
+       if (sr & (1 << STM_SPI_SR_UDR))
+               ao_panic(0x40 | 5);
+       if ((sr & (1 << STM_SPI_SR_TXE)) == 0)
+               ao_panic(0x40 | 6);
+       if (sr & (1 << STM_SPI_SR_RXNE))
+               ao_panic(0x40 | 7);
+       if (which != 5 && which != 6 && which != 13)
+               if (ao_cur_task->task_id != ao_spi_mutex[1])
+                       ao_panic(0x40 | 8);
+}
+#else
+#define validate_spi(stm_spi, which, len) do { (void) (which); (void) (len); } while (0)
+#endif
+
+static void
+ao_spi_set_dma_mosi(uint8_t id, const void *data, uint16_t len, uint32_t minc)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
+
+       ao_dma_set_transfer(mosi_dma_index,
+                           &stm_spi->dr,
+                           (void *) data,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (minc << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+}
+
+static void
+ao_spi_set_dma_miso(uint8_t id, void *data, uint16_t len, uint32_t minc)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+       uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
+
+       ao_dma_set_transfer(miso_dma_index,
+                           &stm_spi->dr,
+                           data,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (minc << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+}
+
+static void
+ao_spi_run(uint8_t id, uint8_t which, uint16_t len)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[id].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[id].mosi_dma_index;
+       uint8_t miso_dma_index = ao_spi_stm_info[id].miso_dma_index;
+
+       validate_spi(stm_spi, which, len);
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (1 << STM_SPI_CR2_TXDMAEN) |
+                       (1 << STM_SPI_CR2_RXDMAEN));
+
+       ao_dma_start(miso_dma_index);
+       ao_dma_start(mosi_dma_index);
+
+       ao_arch_critical(
+               while (!ao_dma_done[miso_dma_index])
+                       ao_sleep(&ao_dma_done[miso_dma_index]);
+               );
+
+       while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0);
+       while (stm_spi->sr & (1 << STM_SPI_SR_BSY));
+
+       validate_spi(stm_spi, which+1, len);
+
+       stm_spi->cr2 = 0;
+
+       ao_dma_done_transfer(mosi_dma_index);
+       ao_dma_done_transfer(miso_dma_index);
+}
+
+void
+ao_spi_send(const void *block, uint16_t len, uint8_t spi_index)
+{
+       uint8_t id = AO_SPI_INDEX(spi_index);
+
+       /* Set up the transmit DMA to deliver data */
+       ao_spi_set_dma_mosi(id, block, len, 1);
+
+       /* Set up the receive DMA -- when this is done, we know the SPI unit
+        * is idle. Without this, we'd have to poll waiting for the BSY bit to
+        * be cleared
+        */
+       ao_spi_set_dma_miso(id, &spi_dev_null, len, 0);
+
+       ao_spi_run(id, 1, len);
+}
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
+{
+       uint8_t id = AO_SPI_INDEX(spi_index);
+
+       /* Set up the transmit DMA to deliver data */
+       ao_spi_set_dma_mosi(id, &value, len, 0);
+
+       /* Set up the receive DMA -- when this is done, we know the SPI unit
+        * is idle. Without this, we'd have to poll waiting for the BSY bit to
+        * be cleared
+        */
+       ao_spi_set_dma_miso(id, &spi_dev_null, len, 0);
+
+       ao_spi_run(id, 3, len);
+}
+
+void
+ao_spi_start_bytes(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (0 << STM_SPI_CR2_TXDMAEN) |
+                       (0 << STM_SPI_CR2_RXDMAEN));
+       validate_spi(stm_spi, 5, 0xffff);
+}
+
+void
+ao_spi_stop_bytes(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
+               ;
+       while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
+               ;
+       /* Clear the OVR flag */
+       (void) stm_spi->dr;
+       (void) stm_spi->sr;
+       validate_spi(stm_spi, 6, 0xffff);
+       stm_spi->cr2 = 0;
+}
+
+void
+ao_spi_send_sync(const void *block, uint16_t len, uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       const uint8_t   *b = block;
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (0 << STM_SPI_CR2_TXDMAEN) |
+                       (0 << STM_SPI_CR2_RXDMAEN));
+       validate_spi(stm_spi, 7, len);
+       while (len--) {
+               while (!(stm_spi->sr & (1 << STM_SPI_SR_TXE)));
+               stm_spi->dr = *b++;
+       }
+       while ((stm_spi->sr & (1 << STM_SPI_SR_TXE)) == 0)
+               ;
+       while (stm_spi->sr & (1 << STM_SPI_SR_BSY))
+               ;
+       /* Clear the OVR flag */
+       (void) stm_spi->dr;
+       (void) stm_spi->sr;
+       validate_spi(stm_spi, 8, len);
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       spi_dev_null = 0xff;
+
+       /* Set up transmit DMA to make the SPI hardware actually run */
+       ao_spi_set_dma_mosi(id, &spi_dev_null, len, 0);
+
+       /* Set up the receive DMA to capture data */
+       ao_spi_set_dma_miso(id, block, len, 1);
+
+       ao_spi_run(id, 9, len);
+}
+
+void
+ao_spi_duplex(const void *out, void *in, uint16_t len, uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       /* Set up transmit DMA to send data */
+       ao_spi_set_dma_mosi(id, out, len, 1);
+
+       /* Set up the receive DMA to capture data */
+       ao_spi_set_dma_miso(id, in, len, 1);
+
+       ao_spi_run(id, 11, len);
+}
+
+static void
+ao_spi_disable_pin_config(uint8_t spi_pin_config)
+{
+       /* Disable current config
+        */
+       switch (spi_pin_config) {
+       case AO_SPI_1_PA5_PA6_PA7:
+               stm_gpio_set(&stm_gpioa, 5, 1);
+               stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
+               stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
+               stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
+               break;
+       case AO_SPI_1_PB3_PB4_PB5:
+               stm_gpio_set(&stm_gpiob, 3, 1);
+               stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
+               stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
+               stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
+               break;
+       case AO_SPI_1_PE13_PE14_PE15:
+               stm_gpio_set(&stm_gpioe, 13, 1);
+               stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
+               stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
+               stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
+               break;
+       case AO_SPI_2_PB13_PB14_PB15:
+               stm_gpio_set(&stm_gpiob, 13, 1);
+               stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
+               stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
+               stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
+               break;
+       case AO_SPI_2_PD1_PD3_PD4:
+               stm_gpio_set(&stm_gpiod, 1, 1);
+               stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
+               stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
+               stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
+               break;
+       }
+}
+
+static void
+ao_spi_enable_pin_config(uint8_t spi_pin_config)
+{
+       /* Enable new config
+        */
+       switch (spi_pin_config) {
+       case AO_SPI_1_PA5_PA6_PA7:
+               stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
+               stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
+               stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
+               break;
+       case AO_SPI_1_PB3_PB4_PB5:
+               stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
+               stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
+               stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
+               break;
+       case AO_SPI_1_PE13_PE14_PE15:
+               stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
+               stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
+               stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
+               break;
+       case AO_SPI_2_PB13_PB14_PB15:
+               stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
+               stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
+               stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
+               break;
+       case AO_SPI_2_PD1_PD3_PD4:
+               stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
+               stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
+               stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
+               break;
+       }
+}
+
+static void
+ao_spi_config(uint8_t spi_index, uint32_t speed)
+{
+       uint8_t         spi_pin_config = AO_SPI_PIN_CONFIG(spi_index);
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       if (spi_pin_config != ao_spi_pin_config[id]) {
+
+               /* Disable old config
+                */
+               ao_spi_disable_pin_config(ao_spi_pin_config[id]);
+
+               /* Enable new config
+                */
+               ao_spi_enable_pin_config(spi_pin_config);
+
+               /* Remember current config
+                */
+               ao_spi_pin_config[id] = spi_pin_config;
+       }
+
+       /* Turn the SPI transceiver on and set the mode */
+       stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |           /* Three wire mode */
+                       (0 << STM_SPI_CR1_BIDIOE) |
+                       (0 << STM_SPI_CR1_CRCEN) |              /* CRC disabled */
+                       (0 << STM_SPI_CR1_CRCNEXT) |
+                       (0 << STM_SPI_CR1_DFF) |
+                       (0 << STM_SPI_CR1_RXONLY) |
+                       (1 << STM_SPI_CR1_SSM) |                /* Software SS handling */
+                       (1 << STM_SPI_CR1_SSI) |                /*  ... */
+                       (0 << STM_SPI_CR1_LSBFIRST) |           /* Big endian */
+                       (1 << STM_SPI_CR1_SPE) |                /* Enable SPI unit */
+                       (speed << STM_SPI_CR1_BR) |             /* baud rate to pclk/4 */
+                       (1 << STM_SPI_CR1_MSTR) |
+                       (AO_SPI_CPOL(spi_index) << STM_SPI_CR1_CPOL) |  /* Format */
+                       (AO_SPI_CPHA(spi_index) << STM_SPI_CR1_CPHA));
+       validate_spi(stm_spi, 13, 0);
+}
+
+uint8_t
+ao_spi_try_get(uint8_t spi_index, uint32_t speed, uint8_t task_id)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       if (!ao_mutex_try(&ao_spi_mutex[id], task_id))
+               return 0;
+       ao_spi_config(spi_index, speed);
+       return 1;
+}
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+
+       ao_mutex_get(&ao_spi_mutex[id]);
+       ao_spi_config(spi_index, speed);
+}
+
+void
+ao_spi_put(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       stm_spi->cr1 = 0;
+       ao_mutex_put(&ao_spi_mutex[id]);
+}
+
+static void
+ao_spi_channel_init(uint8_t spi_index)
+{
+       uint8_t         id = AO_SPI_INDEX(spi_index);
+       struct stm_spi  *stm_spi = ao_spi_stm_info[id].stm_spi;
+
+       ao_spi_disable_pin_config(AO_SPI_PIN_CONFIG(spi_index));
+
+       stm_spi->cr1 = 0;
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (0 << STM_SPI_CR2_TXDMAEN) |
+                       (0 << STM_SPI_CR2_RXDMAEN));
+
+       /* Clear any pending data and error flags */
+       (void) stm_spi->dr;
+       (void) stm_spi->sr;
+}
+
+#if DEBUG
+void
+ao_spi_dump_cmd(void)
+{
+       int s;
+
+       for (s = 0; s < 64; s++) {
+               int i = (spi_task_index + s) & 63;
+               if (spi_tasks[i].which) {
+                       int t;
+                       const char *name = "(none)";
+                       for (t = 0; t < ao_num_tasks; t++)
+                               if (ao_tasks[t]->task_id == spi_tasks[i].task) {
+                                       name = ao_tasks[t]->name;
+                                       break;
+                               }
+                       printf("%2d: %5d task %2d which %2d len %5d %s\n",
+                              s,
+                              spi_tasks[i].tick,
+                              spi_tasks[i].task,
+                              spi_tasks[i].which,
+                              spi_tasks[i].len,
+                              name);
+               }
+       }
+       for (s = 0; s < STM_NUM_SPI; s++) {
+               struct stm_spi *spi = ao_spi_stm_info[s].stm_spi;
+
+               printf("%1d: mutex %2d index %3d miso dma %3d mosi dma %3d",
+                      s, ao_spi_mutex[s], ao_spi_index[s],
+                      ao_spi_stm_info[s].miso_dma_index,
+                      ao_spi_stm_info[s].mosi_dma_index);
+               printf(" cr1 %04x cr2 %02x sr %03x\n",
+                      spi->cr1, spi->cr2, spi->sr);
+       }
+
+}
+
+static const struct ao_cmds ao_spi_cmds[] = {
+       { ao_spi_dump_cmd,      "S\0Dump SPI status" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_spi_init(void)
+{
+#if HAS_SPI_1
+# if SPI_1_PA5_PA6_PA7
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+       stm_ospeedr_set(&stm_gpioa, 5, SPI_1_OSPEEDR);
+       stm_ospeedr_set(&stm_gpioa, 6, SPI_1_OSPEEDR);
+       stm_ospeedr_set(&stm_gpioa, 7, SPI_1_OSPEEDR);
+# endif
+# if SPI_1_PB3_PB4_PB5
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+       stm_ospeedr_set(&stm_gpiob, 3, SPI_1_OSPEEDR);
+       stm_ospeedr_set(&stm_gpiob, 4, SPI_1_OSPEEDR);
+       stm_ospeedr_set(&stm_gpiob, 5, SPI_1_OSPEEDR);
+# endif
+# if SPI_1_PE13_PE14_PE15
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
+       stm_ospeedr_set(&stm_gpioe, 13, SPI_1_OSPEEDR);
+       stm_ospeedr_set(&stm_gpioe, 14, SPI_1_OSPEEDR);
+       stm_ospeedr_set(&stm_gpioe, 15, SPI_1_OSPEEDR);
+# endif
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+       ao_spi_pin_config[0] = AO_SPI_CONFIG_NONE;
+       ao_spi_channel_init(0);
+#endif
+
+#if HAS_SPI_2
+# if SPI_2_PB13_PB14_PB15
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+       stm_ospeedr_set(&stm_gpiob, 13, SPI_2_OSPEEDR);
+       stm_ospeedr_set(&stm_gpiob, 14, SPI_2_OSPEEDR);
+       stm_ospeedr_set(&stm_gpiob, 15, SPI_2_OSPEEDR);
+# endif
+# if SPI_2_PD1_PD3_PD4
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+       stm_ospeedr_set(&stm_gpiod, 1, SPI_2_OSPEEDR);
+       stm_ospeedr_set(&stm_gpiod, 3, SPI_2_OSPEEDR);
+       stm_ospeedr_set(&stm_gpiod, 4, SPI_2_OSPEEDR);
+# endif
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
+       ao_spi_pin_config[1] = AO_SPI_CONFIG_NONE;
+       ao_spi_channel_init(1);
+#endif
+#if DEBUG
+       ao_cmd_register(&ao_spi_cmds[0]);
+#endif
+}
diff --git a/src/stm32l0/ao_timer.c b/src/stm32l0/ao_timer.c
new file mode 100644 (file)
index 0000000..3b77afc
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2020 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>
+#if HAS_FAKE_FLIGHT
+#include <ao_fake_flight.h>
+#endif
+
+#ifndef HAS_TICK
+#define HAS_TICK 1
+#endif
+
+#if HAS_TICK
+volatile AO_TICK_TYPE ao_tick_count;
+
+AO_TICK_TYPE
+ao_time(void)
+{
+       return ao_tick_count;
+}
+
+uint64_t
+ao_time_ns(void)
+{
+       AO_TICK_TYPE    before, after;
+       uint32_t        cvr;
+
+       do {
+               before = ao_tick_count;
+               cvr = stm_systick.cvr;
+               after = ao_tick_count;
+       } while (before != after);
+
+       return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
+               (uint64_t) cvr * (1000000000ULL / AO_SYSTICK);
+}
+
+#if AO_DATA_ALL
+volatile uint8_t       ao_data_interval = 1;
+volatile uint8_t       ao_data_count;
+#endif
+
+void stm_systick_isr(void)
+{
+       if (stm_systick.csr & (1 << STM_SYSTICK_CSR_COUNTFLAG)) {
+               ++ao_tick_count;
+#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_ADC
+#if HAS_FAKE_FLIGHT
+                       if (ao_fake_flight_active)
+                               ao_fake_flight_poll();
+                       else
+#endif
+                               ao_adc_poll();
+#endif
+#if (AO_DATA_ALL & ~(AO_DATA_ADC))
+                       ao_wakeup((void *) &ao_data_count);
+#endif
+               }
+#endif
+#ifdef AO_TIMER_HOOK
+               AO_TIMER_HOOK;
+#endif
+       }
+}
+
+#define SYSTICK_RELOAD (AO_SYSTICK / 100 - 1)
+
+void
+ao_timer_init(void)
+{
+       stm_systick.csr = 0;
+       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_HCLK_8 << STM_SYSTICK_CSR_CLKSOURCE));
+}
+
+#endif
diff --git a/src/stm32l0/ao_timer_stm32l0.h b/src/stm32l0/ao_timer_stm32l0.h
new file mode 100644 (file)
index 0000000..d00deff
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ */
+
+#include "ao.h"
+#include <ao_task.h>
+#if HAS_FAKE_FLIGHT
+#include <ao_fake_flight.h>
+#endif
+
+#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;
+}
+
+uint64_t
+ao_time_ns(void)
+{
+       AO_TICK_TYPE    before, after;
+       uint32_t        cvr;
+
+       do {
+               before = ao_tick_count;
+               cvr = stm_systick.cvr;
+               after = ao_tick_count;
+       } while (before != after);
+
+       return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
+               (uint64_t) cvr * (1000000000ULL / AO_SYSTICK);
+}
+
+#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 / 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_HCLK_8 << STM_SYSTICK_CSR_CLKSOURCE));
+       stm_nvic.shpr15_12 |= AO_STM_NVIC_CLOCK_PRIORITY << 24;
+}
+
+#endif
+
+void
+ao_clock_init(void)
+{
+       uint32_t        cfgr;
+       uint32_t        cr;
+       
+       /* Switch to MSI while messing about */
+       stm_rcc.cr |= (1 << STM_RCC_CR_MSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_MSIRDY)))
+               ao_arch_nop();
+
+       stm_rcc.cfgr = (stm_rcc.cfgr & ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW)) |
+               (STM_RCC_CFGR_SW_MSI << STM_RCC_CFGR_SW);
+
+       /* wait for system to switch to MSI */
+       while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+              (STM_RCC_CFGR_SWS_MSI << STM_RCC_CFGR_SWS))
+               ao_arch_nop();
+
+       /* reset SW, HPRE, PPRE1, PPRE2, MCOSEL and MCOPRE */
+       stm_rcc.cfgr &= (uint32_t)0x88FFC00C;
+
+       /* reset HSION, HSEON, CSSON and PLLON bits */
+       stm_rcc.cr &= 0xeefefffe;
+       
+       /* reset PLLSRC, PLLMUL and PLLDIV bits */
+       stm_rcc.cfgr &= 0xff02ffff;
+       
+       /* Disable all interrupts */
+       stm_rcc.cir = 0;
+
+#if AO_HSE
+#if AO_HSE_BYPASS
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
+#else
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_HSEBYP);
+#endif
+       /* Enable HSE clock */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
+               asm("nop");
+
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSE << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSE)
+#define STM_PLLSRC                             AO_HSE
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (1 << STM_RCC_CFGR_PLLSRC)
+#else
+#define STM_HSI                                16000000
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSI)
+#define STM_PLLSRC                             STM_HSI
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (0 << STM_RCC_CFGR_PLLSRC)
+#endif
+
+#if !AO_HSE || HAS_ADC || HAS_ADC_SINGLE
+       /* Enable HSI RC clock 16MHz */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+               asm("nop");
+#endif
+
+       /* Set flash latency to tolerate 32MHz SYSCLK  -> 1 wait state */
+
+       /* Enable 64-bit access and prefetch */
+       stm_flash.acr |= (1 << STM_FLASH_ACR_ACC64);
+       stm_flash.acr |= (1 << STM_FLASH_ACR_PRFEN);
+
+       /* Enable 1 wait state so the CPU can run at 32MHz */
+       stm_flash.acr |= (1 << STM_FLASH_ACR_LATENCY);
+
+       /* Enable power interface clock */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+       /* Set voltage range to 1.8V */
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+
+       /* Configure voltage scaling range */
+       cr = stm_pwr.cr;
+       cr &= ~(STM_PWR_CR_VOS_MASK << STM_PWR_CR_VOS);
+       cr |= (STM_PWR_CR_VOS_1_8 << STM_PWR_CR_VOS);
+       stm_pwr.cr = cr;
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+
+       /* HCLK to 16MHz -> AHB prescaler = /1 */
+       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;
+       while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
+              (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
+               asm ("nop");
+
+       /* APB1 Prescaler = AO_APB1_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE1_MASK << STM_RCC_CFGR_PPRE1);
+       cfgr |= (AO_RCC_CFGR_PPRE1_DIV << STM_RCC_CFGR_PPRE1);
+       stm_rcc.cfgr = cfgr;
+
+       /* APB2 Prescaler = AO_APB2_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE2_MASK << STM_RCC_CFGR_PPRE2);
+       cfgr |= (AO_RCC_CFGR_PPRE2_DIV << STM_RCC_CFGR_PPRE2);
+       stm_rcc.cfgr = cfgr;
+
+       /* Disable the PLL */
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_PLLON);
+       while (stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY))
+               asm("nop");
+       
+       /* PLLVCO to 96MHz (for USB) -> PLLMUL = 6, PLLDIV = 4 */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PLLMUL_MASK << STM_RCC_CFGR_PLLMUL);
+       cfgr &= ~(STM_RCC_CFGR_PLLDIV_MASK << STM_RCC_CFGR_PLLDIV);
+
+       cfgr |= (AO_RCC_CFGR_PLLMUL << STM_RCC_CFGR_PLLMUL);
+       cfgr |= (AO_RCC_CFGR_PLLDIV << STM_RCC_CFGR_PLLDIV);
+
+       /* PLL source */
+       cfgr &= ~(1 << STM_RCC_CFGR_PLLSRC);
+       cfgr |= STM_RCC_CFGR_PLLSRC_TARGET_CLOCK;
+
+       stm_rcc.cfgr = cfgr;
+
+       /* Enable the PLL and wait for it */
+       stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
+               asm("nop");
+
+       /* Switch to the PLL for the system clock */
+
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+       cfgr |= (STM_RCC_CFGR_SW_PLL << STM_RCC_CFGR_SW);
+       stm_rcc.cfgr = cfgr;
+       for (;;) {
+               uint32_t        c, part, mask, val;
+
+               c = stm_rcc.cfgr;
+               mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+               val = (STM_RCC_CFGR_SWS_PLL << STM_RCC_CFGR_SWS);
+               part = c & mask;
+               if (part == val)
+                       break;
+       }
+
+#if 0
+       stm_rcc.apb2rstr = 0xffff;
+       stm_rcc.apb1rstr = 0xffff;
+       stm_rcc.ahbrstr = 0x3f;
+       stm_rcc.ahbenr = (1 << STM_RCC_AHBENR_FLITFEN);
+       stm_rcc.apb2enr = 0;
+       stm_rcc.apb1enr = 0;
+       stm_rcc.ahbrstr = 0;
+       stm_rcc.apb1rstr = 0;
+       stm_rcc.apb2rstr = 0;
+#endif
+
+       /* Clear reset flags */
+       stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+
+
+#if DEBUG_THE_CLOCK
+       /* Output SYSCLK on PA8 for measurments */
+
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+       
+       stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+       stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE);
+       stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
+#endif
+}
diff --git a/src/stm32l0/stm32l0.h b/src/stm32l0/stm32l0.h
new file mode 100644 (file)
index 0000000..6fb8839
--- /dev/null
@@ -0,0 +1,2113 @@
+/*
+ * 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.
+ */
+
+#ifndef _STM32L0_H_
+#define _STM32L0_H_
+
+#include <stdint.h>
+
+typedef volatile uint32_t      vuint32_t;
+typedef volatile void *                vvoid_t;
+
+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;
+       vuint32_t       brr;
+};
+
+#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_spread_mask(uint16_t mask) {
+       uint32_t m = mask;
+
+       /* 0000000000000000mmmmmmmmmmmmmmmm */
+       m = (m & 0xff) | ((m & 0xff00) << 8);
+       /* 00000000mmmmmmmm00000000mmmmmmmm */
+       m = (m & 0x000f000f) | ((m & 0x00f000f0) << 4);
+       /* 0000mmmm0000mmmm0000mmmm0000mmmm */
+       m = (m & 0x03030303) | ((m & 0x0c0c0c0c) << 2);
+       /* 00mm00mm00mm00mm00mm00mm00mm00mm */
+       m = (m & 0x11111111) | ((m & 0x22222222) << 2);
+       /* 0m0m0m0m0m0m0m0m0m0m0m0m0m0m0m0m */
+       return m;
+}
+
+static inline void
+stm_moder_set_mask(struct stm_gpio *gpio, uint16_t mask, uint32_t value) {
+       uint32_t        bits32 = stm_spread_mask(mask);
+       uint32_t        mask32 = 3 * bits32;
+       uint32_t        value32 = (value & 3) * bits32;
+
+       gpio->moder = ((gpio->moder & ~mask32) | value32);
+}
+
+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
+#define STM_OSPEEDR_MEDIUM             1
+#define STM_OSPEEDR_HIGH               2
+#define STM_OSPEEDR_VERY_HIGH          3
+
+static inline void
+stm_ospeedr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
+       gpio->ospeedr = ((gpio->ospeedr &
+                       ~(STM_OSPEEDR_MASK << STM_OSPEEDR_SHIFT(pin))) |
+                      value << STM_OSPEEDR_SHIFT(pin));
+}
+
+static inline void
+stm_ospeedr_set_mask(struct stm_gpio *gpio, uint16_t mask, uint32_t value) {
+       uint32_t        bits32 = stm_spread_mask(mask);
+       uint32_t        mask32 = 3 * bits32;
+       uint32_t        value32 = (value & 3) * bits32;
+
+       gpio->ospeedr = ((gpio->ospeedr & ~mask32) | value32);
+}
+
+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 void
+stm_pupdr_set_mask(struct stm_gpio *gpio, uint16_t mask, uint32_t value) {
+       uint32_t        bits32 = stm_spread_mask(mask);
+       uint32_t        mask32 = 3 * bits32;
+       uint32_t        value32 = (value & 3) * bits32;
+
+       gpio->pupdr = (gpio->pupdr & ~mask32) | value32;
+}
+
+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 = value ? (1 << pin) : (1 << (pin + 16));
+}
+
+static inline void
+stm_gpio_set_mask(struct stm_gpio *gpio, uint16_t bits, uint16_t mask) {
+       /* Use the bit set/reset register to do this atomically */
+       gpio->bsrr = ((uint32_t) (~bits & mask) << 16) | ((uint32_t) (bits & mask));
+}
+
+static inline void
+stm_gpio_set_bits(struct stm_gpio *gpio, uint16_t bits) {
+       gpio->bsrr = bits;
+}
+
+static inline void
+stm_gpio_clr_bits(struct stm_gpio *gpio, uint16_t bits) {
+       gpio->bsrr = ((uint32_t) bits) << 16;
+}
+
+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_gpioh;
+
+#define stm_gpiob  (*((struct stm_gpio *) 0x50000400))
+#define stm_gpioa  (*((struct stm_gpio *) 0x50000000))
+
+struct stm_usart {
+       vuint32_t       cr1;    /* control register 1 */
+       vuint32_t       cr2;    /* control register 2 */
+       vuint32_t       cr3;    /* control register 3 */
+       vuint32_t       brr;    /* baud rate register */
+
+       vuint32_t       gtpr;   /* guard time and prescaler */
+       vuint32_t       rtor;   /* receiver timeout register */
+       vuint32_t       rqr;    /* request register */
+       vuint32_t       isr;    /* interrupt and status register */
+
+       vuint32_t       icr;    /* interrupt flag clear register */
+       vuint32_t       rdr;    /* receive data register */
+       vuint32_t       tdr;    /* transmit data register */
+};
+
+#define STM_USART_CR1_M1       28
+#define STM_USART_CR1_EOBIE    27
+#define STM_USART_CR1_RTOIE    26
+#define STM_USART_CR1_DEAT     21
+#define STM_USART_CR1_DEDT     16
+#define STM_USART_CR1_OVER8    15
+#define STM_USART_CR1_CMIE     14
+#define STM_USART_CR1_MME      13
+#define STM_USART_CR1_M0       12
+#define STM_USART_CR1_WAKE     11
+#define STM_USART_CR1_PCE      10
+#define STM_USART_CR1_PS       9
+#define STM_USART_CR1_PEIE     8
+#define STM_USART_CR1_TXEIE    7
+#define STM_USART_CR1_TCIE     6
+#define STM_USART_CR1_RXNEIE   5
+#define STM_USART_CR1_IDLEIE   4
+#define STM_USART_CR1_TE       3
+#define STM_USART_CR1_RE       2
+#define STM_USART_CR1_UESM     1
+#define STM_USART_CR1_UE       0
+
+#define STM_USART_CR2_ADD      24
+#define STM_USART_CR2_RTOEN    23
+#define STM_USART_CR2_ABRMOD   21
+#define STM_USART_CR2_ABREN    20
+#define STM_USART_CR2_MSBFIRST 19
+#define STM_USART_CR2_DATAINV  18
+#define STM_USART_CR2_TXINV    17
+#define STM_USART_CR2_RXINV    16
+#define STM_USART_CR2_SWAP     15
+#define STM_USART_CR2_LINEN    14
+#define STM_USART_CR2_STOP     12
+#define STM_USART_CR2_CLKEN    11
+#define STM_USART_CR2_CPOL     10
+#define STM_USART_CR2_CHPA     9
+#define STM_USART_CR2_LBCL     8
+#define STM_USART_CR2_LBDIE    6
+#define STM_USART_CR2_LBDL     5
+#define STM_USART_CR2_ADDM7    4
+
+#define STM_USART_CR3_WUFIE    22
+#define STM_USART_CR3_WUS      20
+#define STM_USART_CR3_SCARCNT  17
+#define STM_USART_CR3_DEP      15
+#define STM_USART_CR3_DEM      14
+#define STM_USART_CR3_DDRE     13
+#define STM_USART_CR3_OVRDIS   12
+#define STM_USART_CR3_ONEBIT   11
+#define STM_USART_CR3_CTIIE    10
+#define STM_USART_CR3_CTSE     9
+#define STM_USART_CR3_RTSE     8
+#define STM_USART_CR3_DMAT     7
+#define STM_USART_CR3_DMAR     6
+#define STM_USART_CR3_SCEN     5
+#define STM_USART_CR3_NACK     4
+#define STM_USART_CR3_HDSEL    3
+#define STM_USART_CR3_IRLP     2
+#define STM_USART_CR3_IREN     1
+#define STM_USART_CR3_EIE      0
+
+#define STM_USART_GTPR_GT      8
+#define STM_USART_GTPR_PSC     0
+
+#define STM_USART_RQR_TXFRQ    4
+#define STM_USART_RQR_RXFRQ    3
+#define STM_USART_RQR_MMRQ     2
+#define STM_USART_RQR_SBKRQ    1
+#define STM_USART_RQR_ABRRQ    0
+
+#define STM_USART_ISR_REACK    22
+#define STM_USART_ISR_TEACK    21
+#define STM_USART_ISR_WUF      20
+#define STM_USART_ISR_RWU      19
+#define STM_USART_ISR_SBKF     18
+#define STM_USART_ISR_CMF      17
+#define STM_USART_ISR_BUSY     16
+#define STM_USART_ISR_ABRF     15
+#define STM_USART_ISR_ABRE     14
+#define STM_USART_ISR_EOBF     12
+#define STM_USART_ISR_RTOF     11
+#define STM_USART_ISR_CTS      10
+#define STM_USART_ISR_CTSIF    9
+#define STM_USART_ISR_LBDF     8
+#define STM_USART_ISR_TXE      7
+#define STM_USART_ISR_TC       6
+#define STM_USART_ISR_RXNE     5
+#define STM_USART_ISR_IDLE     4
+#define STM_USART_ISR_ORE      3
+#define STM_USART_ISR_NF       2
+#define STM_USART_ISR_FE       1
+#define STM_USART_ISR_PE       0
+
+#define STM_USART_ICR_WUCF     20
+#define STM_USART_ICR_CMCF     17
+#define STM_USART_ICR_EOBCF    12
+#define STM_USART_ICR_RTOCF    11
+#define STM_USART_ICR_CTSCF    9
+#define STM_USART_ICR_LBDCF    8
+#define STM_USART_ICR_TCCF     6
+#define STM_USART_ICR_IDLECF   4
+#define STM_USART_ICR_ORECF    3
+#define STM_USART_ICR_NCF      2
+#define STM_USART_ICR_FECF     1
+#define STM_USART_ICR_PECF     0
+
+extern struct stm_usart        stm_usart1;
+extern struct stm_usart stm_usart2;
+#define stm_usart1 (*((struct stm_usart *) 0x40013800))
+#define stm_usart2 (*((struct stm_usart *) 0x40004400))
+
+struct stm_tim {
+};
+
+extern struct stm_tim stm_tim9;
+
+struct stm_tim1011 {
+       vuint32_t       cr1;
+       uint32_t        unused_4;
+       vuint32_t       smcr;
+       vuint32_t       dier;
+       vuint32_t       sr;
+       vuint32_t       egr;
+       vuint32_t       ccmr1;
+       uint32_t        unused_1c;
+       vuint32_t       ccer;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+       uint32_t        unused_30;
+       vuint32_t       ccr1;
+       uint32_t        unused_38;
+       uint32_t        unused_3c;
+       uint32_t        unused_40;
+       uint32_t        unused_44;
+       uint32_t        unused_48;
+       uint32_t        unused_4c;
+       vuint32_t       or;
+};
+
+extern struct stm_tim1011 stm_tim10;
+extern struct stm_tim1011 stm_tim11;
+
+#define STM_TIM1011_CR1_CKD    8
+#define  STM_TIM1011_CR1_CKD_1         0
+#define  STM_TIM1011_CR1_CKD_2         1
+#define  STM_TIM1011_CR1_CKD_4         2
+#define  STM_TIM1011_CR1_CKD_MASK      3
+#define STM_TIM1011_CR1_ARPE   7
+#define STM_TIM1011_CR1_URS    2
+#define STM_TIM1011_CR1_UDIS   1
+#define STM_TIM1011_CR1_CEN    0
+
+#define STM_TIM1011_SMCR_ETP   15
+#define STM_TIM1011_SMCR_ECE   14
+#define STM_TIM1011_SMCR_ETPS  12
+#define  STM_TIM1011_SMCR_ETPS_OFF     0
+#define  STM_TIM1011_SMCR_ETPS_2       1
+#define  STM_TIM1011_SMCR_ETPS_4       2
+#define  STM_TIM1011_SMCR_ETPS_8       3
+#define  STM_TIM1011_SMCR_ETPS_MASK    3
+#define STM_TIM1011_SMCR_ETF   8
+#define  STM_TIM1011_SMCR_ETF_NONE             0
+#define  STM_TIM1011_SMCR_ETF_CK_INT_2         1
+#define  STM_TIM1011_SMCR_ETF_CK_INT_4         2
+#define  STM_TIM1011_SMCR_ETF_CK_INT_8         3
+#define  STM_TIM1011_SMCR_ETF_DTS_2_6          4
+#define  STM_TIM1011_SMCR_ETF_DTS_2_8          5
+#define  STM_TIM1011_SMCR_ETF_DTS_4_6          6
+#define  STM_TIM1011_SMCR_ETF_DTS_4_8          7
+#define  STM_TIM1011_SMCR_ETF_DTS_8_6          8
+#define  STM_TIM1011_SMCR_ETF_DTS_8_8          9
+#define  STM_TIM1011_SMCR_ETF_DTS_16_5         10
+#define  STM_TIM1011_SMCR_ETF_DTS_16_6         11
+#define  STM_TIM1011_SMCR_ETF_DTS_16_8         12
+#define  STM_TIM1011_SMCR_ETF_DTS_32_5         13
+#define  STM_TIM1011_SMCR_ETF_DTS_32_6         14
+#define  STM_TIM1011_SMCR_ETF_DTS_32_8         15
+#define  STM_TIM1011_SMCR_ETF_MASK             15
+
+#define STM_TIM1011_DIER_CC1E  1
+#define STM_TIM1011_DIER_UIE   0
+
+#define STM_TIM1011_SR_CC1OF   9
+#define STM_TIM1011_SR_CC1IF   1
+#define STM_TIM1011_SR_UIF     0
+
+#define STM_TIM1011_EGR_CC1G   1
+#define STM_TIM1011_EGR_UG     0
+
+#define STM_TIM1011_CCMR1_OC1CE        7
+#define STM_TIM1011_CCMR1_OC1M 4
+#define  STM_TIM1011_CCMR1_OC1M_FROZEN                 0
+#define  STM_TIM1011_CCMR1_OC1M_SET_1_ACTIVE_ON_MATCH  1
+#define  STM_TIM1011_CCMR1_OC1M_SET_1_INACTIVE_ON_MATCH        2
+#define  STM_TIM1011_CCMR1_OC1M_TOGGLE                 3
+#define  STM_TIM1011_CCMR1_OC1M_FORCE_INACTIVE         4
+#define  STM_TIM1011_CCMR1_OC1M_FORCE_ACTIVE           5
+#define  STM_TIM1011_CCMR1_OC1M_PWM_MODE_1             6
+#define  STM_TIM1011_CCMR1_OC1M_PWM_MODE_2             7
+#define  STM_TIM1011_CCMR1_OC1M_MASK                   7
+#define STM_TIM1011_CCMR1_OC1PE        3
+#define STM_TIM1011_CCMR1_OC1FE        2
+#define STM_TIM1011_CCMR1_CC1S 0
+#define  STM_TIM1011_CCMR1_CC1S_OUTPUT                 0
+#define  STM_TIM1011_CCMR1_CC1S_INPUT_TI1              1
+#define  STM_TIM1011_CCMR1_CC1S_INPUT_TI2              2
+#define  STM_TIM1011_CCMR1_CC1S_INPUT_TRC              3
+#define  STM_TIM1011_CCMR1_CC1S_MASK                   3
+
+#define  STM_TIM1011_CCMR1_IC1F_NONE           0
+#define  STM_TIM1011_CCMR1_IC1F_CK_INT_2       1
+#define  STM_TIM1011_CCMR1_IC1F_CK_INT_4       2
+#define  STM_TIM1011_CCMR1_IC1F_CK_INT_8       3
+#define  STM_TIM1011_CCMR1_IC1F_DTS_2_6                4
+#define  STM_TIM1011_CCMR1_IC1F_DTS_2_8                5
+#define  STM_TIM1011_CCMR1_IC1F_DTS_4_6                6
+#define  STM_TIM1011_CCMR1_IC1F_DTS_4_8                7
+#define  STM_TIM1011_CCMR1_IC1F_DTS_8_6                8
+#define  STM_TIM1011_CCMR1_IC1F_DTS_8_8                9
+#define  STM_TIM1011_CCMR1_IC1F_DTS_16_5       10
+#define  STM_TIM1011_CCMR1_IC1F_DTS_16_6       11
+#define  STM_TIM1011_CCMR1_IC1F_DTS_16_8       12
+#define  STM_TIM1011_CCMR1_IC1F_DTS_32_5       13
+#define  STM_TIM1011_CCMR1_IC1F_DTS_32_6       14
+#define  STM_TIM1011_CCMR1_IC1F_DTS_32_8       15
+#define  STM_TIM1011_CCMR1_IC1F_MASK           15
+#define STM_TIM1011_CCMR1_IC1PSC       2
+#define  STM_TIM1011_CCMR1_IC1PSC_1            0
+#define  STM_TIM1011_CCMR1_IC1PSC_2            1
+#define  STM_TIM1011_CCMR1_IC1PSC_4            2
+#define  STM_TIM1011_CCMR1_IC1PSC_8            3
+#define  STM_TIM1011_CCMR1_IC1PSC_MASK         3
+#define STM_TIM1011_CCMR1_CC1S         0
+
+#define STM_TIM1011_CCER_CC1NP         3
+#define STM_TIM1011_CCER_CC1P          1
+#define STM_TIM1011_CCER_CC1E          0
+
+#define STM_TIM1011_OR_TI1_RMP_RI      3
+#define STM_TIM1011_ETR_RMP            2
+#define STM_TIM1011_TI1_RMP            0
+#define  STM_TIM1011_TI1_RMP_GPIO              0
+#define  STM_TIM1011_TI1_RMP_LSI               1
+#define  STM_TIM1011_TI1_RMP_LSE               2
+#define  STM_TIM1011_TI1_RMP_RTC               3
+#define  STM_TIM1011_TI1_RMP_MASK              3
+
+/* Flash interface */
+
+struct stm_flash {
+       vuint32_t       acr;
+       vuint32_t       pecr;
+       vuint32_t       pdkeyr;
+       vuint32_t       pekeyr;
+
+       vuint32_t       prgkeyr;
+       vuint32_t       optkeyr;
+       vuint32_t       sr;
+       vuint32_t       obr;
+
+       vuint32_t       wrpr;
+};
+
+extern struct stm_flash        stm_flash;
+
+#define STM_FLASH_ACR_RUN_PD   (4)
+#define STM_FLASH_ACR_SLEEP_PD (3)
+#define STM_FLASH_ACR_ACC64    (2)
+#define STM_FLASH_ACR_PRFEN    (1)
+#define STM_FLASH_ACR_LATENCY  (0)
+
+#define STM_FLASH_PECR_OBL_LAUNCH      18
+#define STM_FLASH_PECR_ERRIE           17
+#define STM_FLASH_PECR_EOPIE           16
+#define STM_FLASH_PECR_FPRG            10
+#define STM_FLASH_PECR_ERASE           9
+#define STM_FLASH_PECR_FTDW            8
+#define STM_FLASH_PECR_DATA            4
+#define STM_FLASH_PECR_PROG            3
+#define STM_FLASH_PECR_OPTLOCK         2
+#define STM_FLASH_PECR_PRGLOCK         1
+#define STM_FLASH_PECR_PELOCK          0
+
+#define STM_FLASH_SR_OPTVERR           11
+#define STM_FLASH_SR_SIZERR            10
+#define STM_FLASH_SR_PGAERR            9
+#define STM_FLASH_SR_WRPERR            8
+#define STM_FLASH_SR_READY             3
+#define STM_FLASH_SR_ENDHV             2
+#define STM_FLASH_SR_EOP               1
+#define STM_FLASH_SR_BSY               0
+
+#define STM_FLASH_PEKEYR_PEKEY1        0x89ABCDEF
+#define STM_FLASH_PEKEYR_PEKEY2 0x02030405
+
+#define STM_FLASH_PRGKEYR_PRGKEY1 0x8C9DAEBF
+#define STM_FLASH_PRGKEYR_PRGKEY2 0x13141516
+
+struct stm_rcc {
+       vuint32_t       cr;
+       vuint32_t       icscr;
+       uint32_t        unused08;
+       vuint32_t       cfgr;
+
+       vuint32_t       cier;
+       vuint32_t       cifr;
+       vuint32_t       cicr;
+       vuint32_t       iopstr;
+
+       vuint32_t       ahbrstr;
+       vuint32_t       apb2rstr;
+       vuint32_t       apb1rstr;
+       vuint32_t       iopenr;
+
+       vuint32_t       ahbenr;
+       vuint32_t       apb2enr;
+       vuint32_t       apb1enr;
+       vuint32_t       iopsmen;
+
+       vuint32_t       ahbsmenr;
+       vuint32_t       apb2smenr;
+       vuint32_t       apb1smenr;
+       vuint32_t       ccipr;
+
+       vuint32_t       csr;
+};
+
+extern struct stm_rcc stm_rcc;
+
+/* Nominal high speed internal oscillator frequency is 16MHz */
+#define STM_HSI_FREQ           16000000
+#define STM_MSI_FREQ            2097000
+
+#define STM_RCC_CR_RTCPRE      (29)
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_2   0
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_4   1
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_8   2
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_16  3
+#define  STM_RCC_CR_RTCPRE_HSE_MASK    3
+
+#define STM_RCC_CR_CSSON       (28)
+#define STM_RCC_CR_PLLRDY      (25)
+#define STM_RCC_CR_PLLON       (24)
+#define STM_RCC_CR_HSEBYP      (18)
+#define STM_RCC_CR_HSERDY      (17)
+#define STM_RCC_CR_HSEON       (16)
+#define STM_RCC_CR_MSIRDY      (9)
+#define STM_RCC_CR_MSION       (8)
+#define STM_RCC_CR_HSIRDY      (1)
+#define STM_RCC_CR_HSION       (0)
+
+#define STM_RCC_CFGR_MCOPRE    (28)
+#define  STM_RCC_CFGR_MCOPRE_DIV_1     0
+#define  STM_RCC_CFGR_MCOPRE_DIV_2     1
+#define  STM_RCC_CFGR_MCOPRE_DIV_4     2
+#define  STM_RCC_CFGR_MCOPRE_DIV_8     3
+#define  STM_RCC_CFGR_MCOPRE_DIV_16    4
+#define  STM_RCC_CFGR_MCOPRE_MASK      7
+
+#define STM_RCC_CFGR_MCOSEL    (24)
+#define  STM_RCC_CFGR_MCOSEL_DISABLE   0
+#define  STM_RCC_CFGR_MCOSEL_SYSCLK    1
+#define  STM_RCC_CFGR_MCOSEL_HSI       2
+#define  STM_RCC_CFGR_MCOSEL_MSI       3
+#define  STM_RCC_CFGR_MCOSEL_HSE       4
+#define  STM_RCC_CFGR_MCOSEL_PLL       5
+#define  STM_RCC_CFGR_MCOSEL_LSI       6
+#define  STM_RCC_CFGR_MCOSEL_LSE       7
+#define  STM_RCC_CFGR_MCOSEL_MASK      7
+
+#define STM_RCC_CFGR_PLLDIV    (22)
+#define  STM_RCC_CFGR_PLLDIV_2         1
+#define  STM_RCC_CFGR_PLLDIV_3         2
+#define  STM_RCC_CFGR_PLLDIV_4         3
+#define  STM_RCC_CFGR_PLLDIV_MASK      3
+
+#define STM_RCC_CFGR_PLLMUL    (18)
+#define  STM_RCC_CFGR_PLLMUL_3         0
+#define  STM_RCC_CFGR_PLLMUL_4         1
+#define  STM_RCC_CFGR_PLLMUL_6         2
+#define  STM_RCC_CFGR_PLLMUL_8         3
+#define  STM_RCC_CFGR_PLLMUL_12                4
+#define  STM_RCC_CFGR_PLLMUL_16                5
+#define  STM_RCC_CFGR_PLLMUL_24                6
+#define  STM_RCC_CFGR_PLLMUL_32                7
+#define  STM_RCC_CFGR_PLLMUL_48                8
+#define  STM_RCC_CFGR_PLLMUL_MASK      0xf
+
+#define STM_RCC_CFGR_PLLSRC    (16)
+
+#define STM_RCC_CFGR_PPRE2     (11)
+#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     (8)
+#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       0
+#define  STM_RCC_CFGR_HPRE_DIV_2       8
+#define  STM_RCC_CFGR_HPRE_DIV_4       9
+#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_MSI          0
+#define  STM_RCC_CFGR_SWS_HSI          1
+#define  STM_RCC_CFGR_SWS_HSE          2
+#define  STM_RCC_CFGR_SWS_PLL          3
+#define  STM_RCC_CFGR_SWS_MASK         3
+
+#define STM_RCC_CFGR_SW                (0)
+#define  STM_RCC_CFGR_SW_MSI           0
+#define  STM_RCC_CFGR_SW_HSI           1
+#define  STM_RCC_CFGR_SW_HSE           2
+#define  STM_RCC_CFGR_SW_PLL           3
+#define  STM_RCC_CFGR_SW_MASK          3
+
+#define STM_RCC_IOPENR_IOPAEN          0
+#define STM_RCC_IOPENR_IOPBEN          1
+#define STM_RCC_IOPENR_IOPCEN          2
+#define STM_RCC_IOPENR_IOPDEN          3
+#define STM_RCC_IOPENR_IOPEEN          4
+#define STM_RCC_IOPENR_IOPHEN          7
+
+#define STM_RCC_AHBENR_DMA1EN          0
+#define STM_RCC_AHBENR_MIFEN           8
+#define STM_RCC_AHBENR_CRCEN           12
+#define STM_RCC_AHBENR_CRYPEN          24
+
+#define STM_RCC_APB2ENR_DBGEN          (22)
+#define STM_RCC_APB2ENR_USART1EN       (14)
+#define STM_RCC_APB2ENR_SPI1EN         (12)
+#define STM_RCC_APB2ENR_ADC1EN         (9)
+#define STM_RCC_APB2ENR_FWEN           (7)
+#define STM_RCC_APB2ENR_TIM22EN                (5)
+#define STM_RCC_APB2ENR_TIM21EN                (2)
+#define STM_RCC_APB2ENR_SYSCFGEN       (0)
+
+#define STM_RCC_APB1ENR_LPTIM1EN       31
+#define STM_RCC_APB1ENR_I2C3EN         30
+#define STM_RCC_APB1ENR_PWREN          28
+#define STM_RCC_APB1ENR_I2C2EN         22
+#define STM_RCC_APB1ENR_I2C1EN         21
+#define STM_RCC_APB1ENR_USART5EN       20
+#define STM_RCC_APB1ENR_USART4EN       19
+#define STM_RCC_APB1ENR_LPUART1EN      18
+#define STM_RCC_APB1ENR_USART2EN       17
+#define STM_RCC_APB1ENR_SPI2EN         14
+#define STM_RCC_APB1ENR_WWDGEN         11
+#define STM_RCC_APB1ENR_TIM7EN         5
+#define STM_RCC_APB1ENR_TIM6EN         4
+#define STM_RCC_APB1ENR_TIM3EN         1
+#define STM_RCC_APB1ENR_TIM2EN         0
+
+#define STM_RCC_CSR_LPWRRSTF           (31)
+#define STM_RCC_CSR_WWDGRSTF           (30)
+#define STM_RCC_CSR_IWDGRSTF           (29)
+#define STM_RCC_CSR_SFTRSTF            (28)
+#define STM_RCC_CSR_PORRSTF            (27)
+#define STM_RCC_CSR_PINRSTF            (26)
+#define STM_RCC_CSR_OBLRSTF            (25)
+#define STM_RCC_CSR_RMVF               (24)
+#define STM_RCC_CSR_RTFRST             (23)
+#define STM_RCC_CSR_RTCEN              (22)
+#define STM_RCC_CSR_RTCSEL             (16)
+
+#define  STM_RCC_CSR_RTCSEL_NONE               0
+#define  STM_RCC_CSR_RTCSEL_LSE                        1
+#define  STM_RCC_CSR_RTCSEL_LSI                        2
+#define  STM_RCC_CSR_RTCSEL_HSE                        3
+#define  STM_RCC_CSR_RTCSEL_MASK               3
+
+#define STM_RCC_CSR_LSEBYP             (10)
+#define STM_RCC_CSR_LSERDY             (9)
+#define STM_RCC_CSR_LSEON              (8)
+#define STM_RCC_CSR_LSIRDY             (1)
+#define STM_RCC_CSR_LSION              (0)
+
+struct stm_pwr {
+       vuint32_t       cr;
+       vuint32_t       csr;
+};
+
+extern struct stm_pwr stm_pwr;
+
+#define STM_PWR_CR_LPRUN       (14)
+
+#define STM_PWR_CR_VOS         (11)
+#define  STM_PWR_CR_VOS_1_8            1
+#define  STM_PWR_CR_VOS_1_5            2
+#define  STM_PWR_CR_VOS_1_2            3
+#define  STM_PWR_CR_VOS_MASK           3
+
+#define STM_PWR_CR_FWU         (10)
+#define STM_PWR_CR_ULP         (9)
+#define STM_PWR_CR_DBP         (8)
+
+#define STM_PWR_CR_PLS         (5)
+#define  STM_PWR_CR_PLS_1_9    0
+#define  STM_PWR_CR_PLS_2_1    1
+#define  STM_PWR_CR_PLS_2_3    2
+#define  STM_PWR_CR_PLS_2_5    3
+#define  STM_PWR_CR_PLS_2_7    4
+#define  STM_PWR_CR_PLS_2_9    5
+#define  STM_PWR_CR_PLS_3_1    6
+#define  STM_PWR_CR_PLS_EXT    7
+#define  STM_PWR_CR_PLS_MASK   7
+
+#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_LPSDSR      (0)
+
+#define STM_PWR_CSR_EWUP3      (10)
+#define STM_PWR_CSR_EWUP2      (9)
+#define STM_PWR_CSR_EWUP1      (8)
+#define STM_PWR_CSR_REGLPF     (5)
+#define STM_PWR_CSR_VOSF       (4)
+#define STM_PWR_CSR_VREFINTRDYF        (3)
+#define STM_PWR_CSR_PVDO       (2)
+#define STM_PWR_CSR_SBF                (1)
+#define STM_PWR_CSR_WUF                (0)
+
+struct stm_tim67 {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       uint32_t        _unused_08;
+       vuint32_t       dier;
+
+       vuint32_t       sr;
+       vuint32_t       egr;
+       uint32_t        _unused_18;
+       uint32_t        _unused_1c;
+
+       uint32_t        _unused_20;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+};
+
+extern struct stm_tim67 stm_tim6;
+
+#define STM_TIM67_CR1_ARPE     (7)
+#define STM_TIM67_CR1_OPM      (3)
+#define STM_TIM67_CR1_URS      (2)
+#define STM_TIM67_CR1_UDIS     (1)
+#define STM_TIM67_CR1_CEN      (0)
+
+#define STM_TIM67_CR2_MMS      (4)
+#define  STM_TIM67_CR2_MMS_RESET       0
+#define  STM_TIM67_CR2_MMS_ENABLE      1
+#define  STM_TIM67_CR2_MMS_UPDATE      2
+#define  STM_TIM67_CR2_MMS_MASK                7
+
+#define STM_TIM67_DIER_UDE     (8)
+#define STM_TIM67_DIER_UIE     (0)
+
+#define STM_TIM67_SR_UIF       (0)
+
+#define STM_TIM67_EGR_UG       (0)
+
+struct stm_lcd {
+       vuint32_t       cr;
+       vuint32_t       fcr;
+       vuint32_t       sr;
+       vuint32_t       clr;
+       uint32_t        unused_0x10;
+       vuint32_t       ram[8*2];
+};
+
+extern struct stm_lcd stm_lcd;
+
+#define STM_LCD_CR_MUX_SEG             (7)
+
+#define STM_LCD_CR_BIAS                        (5)
+#define  STM_LCD_CR_BIAS_1_4           0
+#define  STM_LCD_CR_BIAS_1_2           1
+#define  STM_LCD_CR_BIAS_1_3           2
+#define  STM_LCD_CR_BIAS_MASK          3
+
+#define STM_LCD_CR_DUTY                        (2)
+#define  STM_LCD_CR_DUTY_STATIC                0
+#define  STM_LCD_CR_DUTY_1_2           1
+#define  STM_LCD_CR_DUTY_1_3           2
+#define  STM_LCD_CR_DUTY_1_4           3
+#define  STM_LCD_CR_DUTY_1_8           4
+#define  STM_LCD_CR_DUTY_MASK          7
+
+#define STM_LCD_CR_VSEL                        (1)
+#define STM_LCD_CR_LCDEN               (0)
+
+#define STM_LCD_FCR_PS                 (22)
+#define  STM_LCD_FCR_PS_1              0x0
+#define  STM_LCD_FCR_PS_2              0x1
+#define  STM_LCD_FCR_PS_4              0x2
+#define  STM_LCD_FCR_PS_8              0x3
+#define  STM_LCD_FCR_PS_16             0x4
+#define  STM_LCD_FCR_PS_32             0x5
+#define  STM_LCD_FCR_PS_64             0x6
+#define  STM_LCD_FCR_PS_128            0x7
+#define  STM_LCD_FCR_PS_256            0x8
+#define  STM_LCD_FCR_PS_512            0x9
+#define  STM_LCD_FCR_PS_1024           0xa
+#define  STM_LCD_FCR_PS_2048           0xb
+#define  STM_LCD_FCR_PS_4096           0xc
+#define  STM_LCD_FCR_PS_8192           0xd
+#define  STM_LCD_FCR_PS_16384          0xe
+#define  STM_LCD_FCR_PS_32768          0xf
+#define  STM_LCD_FCR_PS_MASK           0xf
+
+#define STM_LCD_FCR_DIV                        (18)
+#define STM_LCD_FCR_DIV_16             0x0
+#define STM_LCD_FCR_DIV_17             0x1
+#define STM_LCD_FCR_DIV_18             0x2
+#define STM_LCD_FCR_DIV_19             0x3
+#define STM_LCD_FCR_DIV_20             0x4
+#define STM_LCD_FCR_DIV_21             0x5
+#define STM_LCD_FCR_DIV_22             0x6
+#define STM_LCD_FCR_DIV_23             0x7
+#define STM_LCD_FCR_DIV_24             0x8
+#define STM_LCD_FCR_DIV_25             0x9
+#define STM_LCD_FCR_DIV_26             0xa
+#define STM_LCD_FCR_DIV_27             0xb
+#define STM_LCD_FCR_DIV_28             0xc
+#define STM_LCD_FCR_DIV_29             0xd
+#define STM_LCD_FCR_DIV_30             0xe
+#define STM_LCD_FCR_DIV_31             0xf
+#define STM_LCD_FCR_DIV_MASK           0xf
+
+#define STM_LCD_FCR_BLINK              (16)
+#define  STM_LCD_FCR_BLINK_DISABLE             0
+#define  STM_LCD_FCR_BLINK_SEG0_COM0           1
+#define  STM_LCD_FCR_BLINK_SEG0_COMALL         2
+#define  STM_LCD_FCR_BLINK_SEGALL_COMALL       3
+#define  STM_LCD_FCR_BLINK_MASK                        3
+
+#define STM_LCD_FCR_BLINKF             (13)
+#define  STM_LCD_FCR_BLINKF_8                  0
+#define  STM_LCD_FCR_BLINKF_16                 1
+#define  STM_LCD_FCR_BLINKF_32                 2
+#define  STM_LCD_FCR_BLINKF_64                 3
+#define  STM_LCD_FCR_BLINKF_128                        4
+#define  STM_LCD_FCR_BLINKF_256                        5
+#define  STM_LCD_FCR_BLINKF_512                        6
+#define  STM_LCD_FCR_BLINKF_1024               7
+#define  STM_LCD_FCR_BLINKF_MASK               7
+
+#define STM_LCD_FCR_CC                 (10)
+#define  STM_LCD_FCR_CC_MASK                   7
+
+#define STM_LCD_FCR_DEAD               (7)
+#define  STM_LCD_FCR_DEAD_MASK                 7
+
+#define STM_LCD_FCR_PON                        (4)
+#define  STM_LCD_FCR_PON_MASK                  7
+
+#define STM_LCD_FCR_UDDIE              (3)
+#define STM_LCD_FCR_SOFIE              (1)
+#define STM_LCD_FCR_HD                 (0)
+
+#define STM_LCD_SR_FCRSF               (5)
+#define STM_LCD_SR_RDY                 (4)
+#define STM_LCD_SR_UDD                 (3)
+#define STM_LCD_SR_UDR                 (2)
+#define STM_LCD_SR_SOF                 (1)
+#define STM_LCD_SR_ENS                 (0)
+
+#define STM_LCD_CLR_UDDC               (3)
+#define STM_LCD_CLR_SOFC               (1)
+
+/* 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_CSR_ENABLE         0
+#define STM_SYSTICK_CSR_TICKINT                1
+#define STM_SYSTICK_CSR_CLKSOURCE      2
+#define  STM_SYSTICK_CSR_CLKSOURCE_HCLK_8              0
+#define  STM_SYSTICK_CSR_CLKSOURCE_HCLK                        1
+#define STM_SYSTICK_CSR_COUNTFLAG      16
+
+/* The NVIC starts at 0xe000e100, so add that to the offsets to find the absolute address */
+
+struct stm_nvic {
+       vuint32_t       iser;           /* 0x000 0xe000e100 Set Enable Register */
+
+       uint8_t         _unused020[0x080 - 0x004];
+
+       vuint32_t       icer;           /* 0x080 0xe000e180 Clear Enable Register */
+
+       uint8_t         _unused0a0[0x100 - 0x084];
+
+       vuint32_t       ispr;           /* 0x100 0xe000e200 Set Pending Register */
+
+       uint8_t         _unused120[0x180 - 0x104];
+
+       vuint32_t       icpr;           /* 0x180 0xe000e280 Clear Pending Register */
+
+       uint8_t         _unused1a0[0x300 - 0x184];
+
+       vuint32_t       ipr[8];         /* 0x300 0xe000e400 Priority Register */
+};
+
+extern struct stm_nvic stm_nvic;
+
+#define IRQ_MASK(irq)  (1 << (irq))
+#define IRQ_BOOL(v,irq)        (((v) >> (irq)) & 1)
+
+static inline void
+stm_nvic_set_enable(int irq) {
+       stm_nvic.iser = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_enable(int irq) {
+       stm_nvic.icer = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_enabled(int irq) {
+       return IRQ_BOOL(stm_nvic.iser, irq);
+}
+
+static inline void
+stm_nvic_set_pending(int irq) {
+       stm_nvic.ispr = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_pending(int irq) {
+       stm_nvic.icpr = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_pending(int irq) {
+       return IRQ_BOOL(stm_nvic.ispr, irq);
+}
+
+#define IRQ_PRIO_REG(irq)      ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq)      (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq)     (0xff << IRQ_PRIO_BIT(irq))
+
+static inline void
+stm_nvic_set_priority(int irq, uint8_t prio) {
+       int             n = IRQ_PRIO_REG(irq);
+       uint32_t        v;
+
+       v = stm_nvic.ipr[n];
+       v &= ~IRQ_PRIO_MASK(irq);
+       v |= (prio) << IRQ_PRIO_BIT(irq);
+       stm_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+stm_nvic_get_priority(int irq) {
+       return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+struct stm_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       shcrs;
+       vuint32_t       cfsr;
+       vuint32_t       hfsr;
+
+       uint32_t        unused_30;
+       vuint32_t       mmfar;
+       vuint32_t       bfar;
+};
+
+extern struct stm_scb stm_scb;
+
+#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
+
+struct stm_mpu {
+       vuint32_t       typer;
+       vuint32_t       cr;
+       vuint32_t       rnr;
+       vuint32_t       rbar;
+
+       vuint32_t       rasr;
+       vuint32_t       rbar_a1;
+       vuint32_t       rasr_a1;
+       vuint32_t       rbar_a2;
+       vuint32_t       rasr_a2;
+       vuint32_t       rbar_a3;
+       vuint32_t       rasr_a3;
+};
+
+extern struct stm_mpu stm_mpu;
+
+#define STM_MPU_TYPER_IREGION  16
+#define  STM_MPU_TYPER_IREGION_MASK    0xff
+#define STM_MPU_TYPER_DREGION  8
+#define  STM_MPU_TYPER_DREGION_MASK    0xff
+#define STM_MPU_TYPER_SEPARATE 0
+
+#define STM_MPU_CR_PRIVDEFENA  2
+#define STM_MPU_CR_HFNMIENA    1
+#define STM_MPU_CR_ENABLE      0
+
+#define STM_MPU_RNR_REGION     0
+#define STM_MPU_RNR_REGION_MASK                0xff
+
+#define STM_MPU_RBAR_ADDR      5
+#define STM_MPU_RBAR_ADDR_MASK         0x7ffffff
+
+#define STM_MPU_RBAR_VALID     4
+#define STM_MPU_RBAR_REGION    0
+#define STM_MPU_RBAR_REGION_MASK       0xf
+
+#define STM_MPU_RASR_XN                28
+#define STM_MPU_RASR_AP                24
+#define  STM_MPU_RASR_AP_NONE_NONE     0
+#define  STM_MPU_RASR_AP_RW_NONE       1
+#define  STM_MPU_RASR_AP_RW_RO         2
+#define  STM_MPU_RASR_AP_RW_RW         3
+#define  STM_MPU_RASR_AP_RO_NONE       5
+#define  STM_MPU_RASR_AP_RO_RO         6
+#define  STM_MPU_RASR_AP_MASK          7
+#define STM_MPU_RASR_TEX       19
+#define  STM_MPU_RASR_TEX_MASK         7
+#define STM_MPU_RASR_S         18
+#define STM_MPU_RASR_C         17
+#define STM_MPU_RASR_B         16
+#define STM_MPU_RASR_SRD       8
+#define  STM_MPU_RASR_SRD_MASK         0xff
+#define STM_MPU_RASR_SIZE      1
+#define  STM_MPU_RASR_SIZE_MASK                0x1f
+#define STM_MPU_RASR_ENABLE    0
+
+#define isr_decl(name) void stm_ ## name ## _isr(void)
+
+isr_decl(halt);
+isr_decl(ignore);
+
+isr_decl(nmi);
+isr_decl(hardfault);
+isr_decl(usagefault);
+isr_decl(svc);
+isr_decl(debugmon);
+isr_decl(pendsv);
+isr_decl(systick);
+isr_decl(wwdg);
+isr_decl(pvd);
+isr_decl(rtc);
+isr_decl(flash);
+isr_decl(rcc_crs);
+isr_decl(exti1_0);
+isr_decl(exti3_2);
+isr_decl(exti15_4);
+isr_decl(dma1_channel1);
+isr_decl(dma1_channel3_2);
+isr_decl(dma1_channel7_4);
+isr_decl(adc_comp);
+isr_decl(lptim1);
+isr_decl(usart4_usart5);
+isr_decl(tim2);
+isr_decl(tim3);
+isr_decl(tim4);
+isr_decl(tim6);
+isr_decl(tim7);
+isr_decl(tim21);
+isr_decl(i2c3);
+isr_decl(tim22);
+isr_decl(i2c1);
+isr_decl(i2c2);
+isr_decl(spi1);
+isr_decl(spi2);
+isr_decl(usart1);
+isr_decl(usart2);
+isr_decl(usart3);
+isr_decl(lpuart1_aes);
+
+#undef isr_decl
+
+#define STM_ISR_WWDG_POS               0
+#define STM_ISR_PVD_POS                        1
+#define STM_ISR_RTC_POS                        2
+#define STM_ISR_FLASH_POS              3
+#define STM_ISR_RCC_CRS_POS            4
+#define STM_ISR_EXTI1_0_POS            5
+#define STM_ISR_EXTI3_2_POS            6
+#define STM_ISR_EXTI15_4_POS           7
+#define STM_ISR_DMA1_CHANNEL1_POS      9
+#define STM_ISR_DMA1_CHANNEL3_2_POS    10
+#define STM_ISR_DMA1_CHANNEL7_4_POS    11
+#define STM_ISR_ADC_COMP_POS           12
+#define STM_ISR_LPTIM1_POS             13
+#define STM_ISR_USART4_USART5_POS      14
+#define STM_ISR_TIM2_POS               15
+#define STM_ISR_TIM3_POS               16
+#define STM_ISR_TIM6_POS               17
+#define STM_ISR_TIM7_POS               18
+#define STM_ISR_TIM21_POS              20
+#define STM_ISR_I2C3_POS               21
+#define STM_ISR_TIM22_POS              22
+#define STM_ISR_I2C1_POS               23
+#define STM_ISR_I2C2_POS               24
+#define STM_ISR_SPI1_POS               25
+#define STM_ISR_SPI2_POS               26
+#define STM_ISR_USART1_POS             27
+#define STM_ISR_USART2_POS             28
+#define STM_ISR_LPUART1_AES_POS                29
+
+struct stm_syscfg {
+       vuint32_t       memrmp;
+       vuint32_t       pmc;
+       vuint32_t       exticr[4];
+};
+
+extern struct stm_syscfg stm_syscfg;
+
+#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_USB_PU          0
+
+#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_PH           5
+
+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;
+
+       stm_syscfg.exticr[reg] = (stm_syscfg.exticr[reg] & ~(0xf << shift)) | val << shift;
+}
+
+struct stm_dma_channel {
+       vuint32_t       ccr;
+       vuint32_t       cndtr;
+       vvoid_t         cpar;
+       vvoid_t         cmar;
+       vuint32_t       reserved;
+};
+
+#define STM_NUM_DMA    7
+
+struct stm_dma {
+       vuint32_t               isr;
+       vuint32_t               ifcr;
+       struct stm_dma_channel  channel[STM_NUM_DMA];
+};
+
+extern struct stm_dma stm_dma;
+
+/* DMA channels go from 1 to 7, instead of 0 to 6 (sigh)
+ */
+
+#define STM_DMA_INDEX(channel)         ((channel) - 1)
+
+#define STM_DMA_ISR(index)             ((index) << 2)
+#define STM_DMA_ISR_MASK                       0xf
+#define STM_DMA_ISR_TEIF                       3
+#define STM_DMA_ISR_HTIF                       2
+#define STM_DMA_ISR_TCIF                       1
+#define STM_DMA_ISR_GIF                                0
+
+#define STM_DMA_IFCR(index)            ((index) << 2)
+#define STM_DMA_IFCR_MASK                      0xf
+#define STM_DMA_IFCR_CTEIF                     3
+#define STM_DMA_IFCR_CHTIF                     2
+#define STM_DMA_IFCR_CTCIF                     1
+#define STM_DMA_IFCR_CGIF                      0
+
+#define STM_DMA_CCR_MEM2MEM            (14)
+
+#define STM_DMA_CCR_PL                 (12)
+#define  STM_DMA_CCR_PL_LOW                    (0)
+#define  STM_DMA_CCR_PL_MEDIUM                 (1)
+#define  STM_DMA_CCR_PL_HIGH                   (2)
+#define  STM_DMA_CCR_PL_VERY_HIGH              (3)
+#define  STM_DMA_CCR_PL_MASK                   (3)
+
+#define STM_DMA_CCR_MSIZE              (10)
+#define  STM_DMA_CCR_MSIZE_8                   (0)
+#define  STM_DMA_CCR_MSIZE_16                  (1)
+#define  STM_DMA_CCR_MSIZE_32                  (2)
+#define  STM_DMA_CCR_MSIZE_MASK                        (3)
+
+#define STM_DMA_CCR_PSIZE              (8)
+#define  STM_DMA_CCR_PSIZE_8                   (0)
+#define  STM_DMA_CCR_PSIZE_16                  (1)
+#define  STM_DMA_CCR_PSIZE_32                  (2)
+#define  STM_DMA_CCR_PSIZE_MASK                        (3)
+
+#define STM_DMA_CCR_MINC               (7)
+#define STM_DMA_CCR_PINC               (6)
+#define STM_DMA_CCR_CIRC               (5)
+#define STM_DMA_CCR_DIR                        (4)
+#define  STM_DMA_CCR_DIR_PER_TO_MEM            0
+#define  STM_DMA_CCR_DIR_MEM_TO_PER            1
+#define STM_DMA_CCR_TEIE               (3)
+#define STM_DMA_CCR_HTIE               (2)
+#define STM_DMA_CCR_TCIE               (1)
+#define STM_DMA_CCR_EN                 (0)
+
+#define STM_DMA_CHANNEL_ADC1           1
+#define STM_DMA_CHANNEL_SPI1_RX                2
+#define STM_DMA_CHANNEL_SPI1_TX                3
+#define STM_DMA_CHANNEL_SPI2_RX                4
+#define STM_DMA_CHANNEL_SPI2_TX                5
+#define STM_DMA_CHANNEL_USART3_TX      2
+#define STM_DMA_CHANNEL_USART3_RX      3
+#define STM_DMA_CHANNEL_USART1_TX      4
+#define STM_DMA_CHANNEL_USART1_RX      5
+#define STM_DMA_CHANNEL_USART2_RX      6
+#define STM_DMA_CHANNEL_USART2_TX      7
+#define STM_DMA_CHANNEL_I2C2_TX                4
+#define STM_DMA_CHANNEL_I2C2_RX                5
+#define STM_DMA_CHANNEL_I2C1_TX                6
+#define STM_DMA_CHANNEL_I2C1_RX                7
+#define STM_DMA_CHANNEL_TIM2_CH3       1
+#define STM_DMA_CHANNEL_TIM2_UP                2
+#define STM_DMA_CHANNEL_TIM2_CH1       5
+#define STM_DMA_CHANNEL_TIM2_CH2       7
+#define STM_DMA_CHANNEL_TIM2_CH4       7
+#define STM_DMA_CHANNEL_TIM3_CH3       2
+#define STM_DMA_CHANNEL_TIM3_CH4       3
+#define STM_DMA_CHANNEL_TIM3_UP                3
+#define STM_DMA_CHANNEL_TIM3_CH1       6
+#define STM_DMA_CHANNEL_TIM3_TRIG      6
+#define STM_DMA_CHANNEL_TIM4_CH1       1
+#define STM_DMA_CHANNEL_TIM4_CH2       4
+#define STM_DMA_CHANNEL_TIM4_CH3       5
+#define STM_DMA_CHANNEL_TIM4_UP                7
+#define STM_DMA_CHANNEL_TIM6_UP_DA     2
+#define STM_DMA_CHANNEL_C_CHANNEL1     2
+#define STM_DMA_CHANNEL_TIM7_UP_DA     3
+#define STM_DMA_CHANNEL_C_CHANNEL2     3
+
+/*
+ * Only spi channel 1 and 2 can use DMA
+ */
+#define STM_NUM_SPI    2
+
+struct stm_spi {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       sr;
+       vuint32_t       dr;
+       vuint32_t       crcpr;
+       vuint32_t       rxcrcr;
+       vuint32_t       txcrcr;
+};
+
+extern struct stm_spi stm_spi1, stm_spi2, stm_spi3;
+
+/* SPI channels go from 1 to 3, instead of 0 to 2 (sigh)
+ */
+
+#define STM_SPI_INDEX(channel)         ((channel) - 1)
+
+#define STM_SPI_CR1_BIDIMODE           15
+#define STM_SPI_CR1_BIDIOE             14
+#define STM_SPI_CR1_CRCEN              13
+#define STM_SPI_CR1_CRCNEXT            12
+#define STM_SPI_CR1_DFF                        11
+#define STM_SPI_CR1_RXONLY             10
+#define STM_SPI_CR1_SSM                        9
+#define STM_SPI_CR1_SSI                        8
+#define STM_SPI_CR1_LSBFIRST           7
+#define STM_SPI_CR1_SPE                        6
+#define STM_SPI_CR1_BR                 3
+#define  STM_SPI_CR1_BR_PCLK_2                 0
+#define  STM_SPI_CR1_BR_PCLK_4                 1
+#define  STM_SPI_CR1_BR_PCLK_8                 2
+#define  STM_SPI_CR1_BR_PCLK_16                        3
+#define  STM_SPI_CR1_BR_PCLK_32                        4
+#define  STM_SPI_CR1_BR_PCLK_64                        5
+#define  STM_SPI_CR1_BR_PCLK_128               6
+#define  STM_SPI_CR1_BR_PCLK_256               7
+#define  STM_SPI_CR1_BR_MASK                   7
+
+#define STM_SPI_CR1_MSTR               2
+#define STM_SPI_CR1_CPOL               1
+#define STM_SPI_CR1_CPHA               0
+
+#define STM_SPI_CR2_TXEIE      7
+#define STM_SPI_CR2_RXNEIE     6
+#define STM_SPI_CR2_ERRIE      5
+#define STM_SPI_CR2_SSOE       2
+#define STM_SPI_CR2_TXDMAEN    1
+#define STM_SPI_CR2_RXDMAEN    0
+
+#define STM_SPI_SR_FRE         8
+#define STM_SPI_SR_BSY         7
+#define STM_SPI_SR_OVR         6
+#define STM_SPI_SR_MODF                5
+#define STM_SPI_SR_CRCERR      4
+#define STM_SPI_SR_UDR         3
+#define STM_SPI_SR_CHSIDE      2
+#define STM_SPI_SR_TXE         1
+#define STM_SPI_SR_RXNE                0
+
+struct stm_adc {
+       vuint32_t       sr;
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smpr1;
+       vuint32_t       smpr2;
+       vuint32_t       smpr3;
+       vuint32_t       jofr1;
+       vuint32_t       jofr2;
+       vuint32_t       jofr3;
+       vuint32_t       jofr4;
+       vuint32_t       htr;
+       vuint32_t       ltr;
+       vuint32_t       sqr1;
+       vuint32_t       sqr2;
+       vuint32_t       sqr3;
+       vuint32_t       sqr4;
+       vuint32_t       sqr5;
+       vuint32_t       jsqr;
+       vuint32_t       jdr1;
+       vuint32_t       jdr2;
+       vuint32_t       jdr3;
+       vuint32_t       jdr4;
+       vuint32_t       dr;
+       uint8_t         reserved[0x300 - 0x5c];
+       vuint32_t       csr;
+       vuint32_t       ccr;
+};
+
+extern struct stm_adc stm_adc;
+
+#define STM_ADC_SQ_TEMP                16
+#define STM_ADC_SQ_V_REF       17
+
+#define STM_ADC_SR_JCNR                9
+#define STM_ADC_SR_RCNR                8
+#define STM_ADC_SR_ADONS       6
+#define STM_ADC_SR_OVR         5
+#define STM_ADC_SR_STRT                4
+#define STM_ADC_SR_JSTRT       3
+#define STM_ADC_SR_JEOC                2
+#define STM_ADC_SR_EOC         1
+#define STM_ADC_SR_AWD         0
+
+#define STM_ADC_CR1_OVRIE      26
+#define STM_ADC_CR1_RES                24
+#define  STM_ADC_CR1_RES_12            0
+#define  STM_ADC_CR1_RES_10            1
+#define  STM_ADC_CR1_RES_8             2
+#define  STM_ADC_CR1_RES_6             3
+#define  STM_ADC_CR1_RES_MASK          3
+#define STM_ADC_CR1_AWDEN       23
+#define STM_ADC_CR1_JAWDEN     22
+#define STM_ADC_CR1_PDI                17
+#define STM_ADC_CR1_PDD                16
+#define STM_ADC_CR1_DISCNUM    13
+#define  STM_ADC_CR1_DISCNUM_1         0
+#define  STM_ADC_CR1_DISCNUM_2         1
+#define  STM_ADC_CR1_DISCNUM_3         2
+#define  STM_ADC_CR1_DISCNUM_4         3
+#define  STM_ADC_CR1_DISCNUM_5         4
+#define  STM_ADC_CR1_DISCNUM_6         5
+#define  STM_ADC_CR1_DISCNUM_7         6
+#define  STM_ADC_CR1_DISCNUM_8         7
+#define  STM_ADC_CR1_DISCNUM_MASK      7
+#define STM_ADC_CR1_JDISCEN    12
+#define STM_ADC_CR1_DISCEN     11
+#define STM_ADC_CR1_JAUTO      10
+#define STM_ADC_CR1_AWDSGL     9
+#define STM_ADC_CR1_SCAN       8
+#define STM_ADC_CR1_JEOCIE     7
+#define STM_ADC_CR1_AWDIE      6
+#define STM_ADC_CR1_EOCIE      5
+#define STM_ADC_CR1_AWDCH      0
+#define  STM_ADC_CR1_AWDCH_MASK                0x1f
+
+#define STM_ADC_CR2_SWSTART    30
+#define STM_ADC_CR2_EXTEN      28
+#define  STM_ADC_CR2_EXTEN_DISABLE     0
+#define  STM_ADC_CR2_EXTEN_RISING      1
+#define  STM_ADC_CR2_EXTEN_FALLING     2
+#define  STM_ADC_CR2_EXTEN_BOTH                3
+#define  STM_ADC_CR2_EXTEN_MASK                3
+#define STM_ADC_CR2_EXTSEL     24
+#define  STM_ADC_CR2_EXTSEL_TIM9_CC2   0
+#define  STM_ADC_CR2_EXTSEL_TIM9_TRGO  1
+#define  STM_ADC_CR2_EXTSEL_TIM2_CC3   2
+#define  STM_ADC_CR2_EXTSEL_TIM2_CC2   3
+#define  STM_ADC_CR2_EXTSEL_TIM3_TRGO  4
+#define  STM_ADC_CR2_EXTSEL_TIM4_CC4   5
+#define  STM_ADC_CR2_EXTSEL_TIM2_TRGO  6
+#define  STM_ADC_CR2_EXTSEL_TIM3_CC1   7
+#define  STM_ADC_CR2_EXTSEL_TIM3_CC3   8
+#define  STM_ADC_CR2_EXTSEL_TIM4_TRGO  9
+#define  STM_ADC_CR2_EXTSEL_TIM6_TRGO  10
+#define  STM_ADC_CR2_EXTSEL_EXTI_11    15
+#define  STM_ADC_CR2_EXTSEL_MASK       15
+#define STM_ADC_CR2_JWSTART    22
+#define STM_ADC_CR2_JEXTEN     20
+#define  STM_ADC_CR2_JEXTEN_DISABLE    0
+#define  STM_ADC_CR2_JEXTEN_RISING     1
+#define  STM_ADC_CR2_JEXTEN_FALLING    2
+#define  STM_ADC_CR2_JEXTEN_BOTH       3
+#define  STM_ADC_CR2_JEXTEN_MASK       3
+#define STM_ADC_CR2_JEXTSEL    16
+#define  STM_ADC_CR2_JEXTSEL_TIM9_CC1  0
+#define  STM_ADC_CR2_JEXTSEL_TIM9_TRGO 1
+#define  STM_ADC_CR2_JEXTSEL_TIM2_TRGO 2
+#define  STM_ADC_CR2_JEXTSEL_TIM2_CC1  3
+#define  STM_ADC_CR2_JEXTSEL_TIM3_CC4  4
+#define  STM_ADC_CR2_JEXTSEL_TIM4_TRGO 5
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC1  6
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC2  7
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC3  8
+#define  STM_ADC_CR2_JEXTSEL_TIM10_CC1 9
+#define  STM_ADC_CR2_JEXTSEL_TIM7_TRGO 10
+#define  STM_ADC_CR2_JEXTSEL_EXTI_15   15
+#define  STM_ADC_CR2_JEXTSEL_MASK      15
+#define STM_ADC_CR2_ALIGN      11
+#define STM_ADC_CR2_EOCS       10
+#define STM_ADC_CR2_DDS                9
+#define STM_ADC_CR2_DMA                8
+#define STM_ADC_CR2_DELS       4
+#define  STM_ADC_CR2_DELS_NONE         0
+#define  STM_ADC_CR2_DELS_UNTIL_READ   1
+#define  STM_ADC_CR2_DELS_7            2
+#define  STM_ADC_CR2_DELS_15           3
+#define  STM_ADC_CR2_DELS_31           4
+#define  STM_ADC_CR2_DELS_63           5
+#define  STM_ADC_CR2_DELS_127          6
+#define  STM_ADC_CR2_DELS_255          7
+#define  STM_ADC_CR2_DELS_MASK         7
+#define STM_ADC_CR2_CONT       1
+#define STM_ADC_CR2_ADON       0
+
+#define STM_ADC_CCR_TSVREFE    23
+#define STM_ADC_CCR_ADCPRE     16
+#define  STM_ADC_CCR_ADCPRE_HSI_1      0
+#define  STM_ADC_CCR_ADCPRE_HSI_2      1
+#define  STM_ADC_CCR_ADCPRE_HSI_4      2
+#define  STM_ADC_CCR_ADCPRE_MASK       3
+
+struct stm_temp_cal {
+       uint16_t        vref;
+       uint16_t        ts_cal_cold;
+       uint16_t        reserved;
+       uint16_t        ts_cal_hot;
+};
+
+extern struct stm_temp_cal     stm_temp_cal;
+
+#define stm_temp_cal_cold      25
+#define stm_temp_cal_hot       110
+
+struct stm_dbg_mcu {
+       uint32_t        idcode;
+};
+
+extern struct stm_dbg_mcu      stm_dbg_mcu;
+
+static inline uint16_t
+stm_dev_id(void) {
+       return stm_dbg_mcu.idcode & 0xfff;
+}
+
+struct stm_flash_size {
+       uint16_t        f_size;
+};
+
+extern struct stm_flash_size   stm_flash_size_reg;
+#define stm_flash_size_reg     (*((struct stm_flash_size *) 0x1ff8007c))
+
+/* Returns flash size in bytes */
+extern uint32_t
+stm_flash_size(void);
+
+struct stm_unique_id {
+       uint32_t        u_id0;
+       uint32_t        u_id1;
+       uint32_t        u_id2;
+};
+
+extern struct stm_unique_id    stm_unique_id;
+#define stm_unique_id  (*((struct stm_unique_id) 0x1ff80050))
+
+struct stm_device_id {
+       uint32_t        device_id;
+};
+
+extern struct stm_device_id    stm_device_id;
+#define stm_device_id  (*((struct stm_device_id) 0x40015800))
+
+#define STM_NUM_I2C    2
+
+#define STM_I2C_INDEX(channel) ((channel) - 1)
+
+struct stm_i2c {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       oar1;
+       vuint32_t       oar2;
+       vuint32_t       dr;
+       vuint32_t       sr1;
+       vuint32_t       sr2;
+       vuint32_t       ccr;
+       vuint32_t       trise;
+};
+
+extern struct stm_i2c stm_i2c1, stm_i2c2;
+
+#define STM_I2C_CR1_SWRST      15
+#define STM_I2C_CR1_ALERT      13
+#define STM_I2C_CR1_PEC                12
+#define STM_I2C_CR1_POS                11
+#define STM_I2C_CR1_ACK                10
+#define STM_I2C_CR1_STOP       9
+#define STM_I2C_CR1_START      8
+#define STM_I2C_CR1_NOSTRETCH  7
+#define STM_I2C_CR1_ENGC       6
+#define STM_I2C_CR1_ENPEC      5
+#define STM_I2C_CR1_ENARP      4
+#define STM_I2C_CR1_SMBTYPE    3
+#define STM_I2C_CR1_SMBUS      1
+#define STM_I2C_CR1_PE         0
+
+#define STM_I2C_CR2_LAST       12
+#define STM_I2C_CR2_DMAEN      11
+#define STM_I2C_CR2_ITBUFEN    10
+#define STM_I2C_CR2_ITEVTEN    9
+#define STM_I2C_CR2_ITERREN    8
+#define STM_I2C_CR2_FREQ       0
+#define  STM_I2C_CR2_FREQ_2_MHZ                2
+#define  STM_I2C_CR2_FREQ_4_MHZ                4
+#define  STM_I2C_CR2_FREQ_8_MHZ                8
+#define  STM_I2C_CR2_FREQ_16_MHZ       16
+#define  STM_I2C_CR2_FREQ_24_MHZ       24
+#define  STM_I2C_CR2_FREQ_32_MHZ       32
+#define  STM_I2C_CR2_FREQ_MASK         0x3f
+
+#define STM_I2C_SR1_SMBALERT   15
+#define STM_I2C_SR1_TIMEOUT    14
+#define STM_I2C_SR1_PECERR     12
+#define STM_I2C_SR1_OVR                11
+#define STM_I2C_SR1_AF         10
+#define STM_I2C_SR1_ARLO       9
+#define STM_I2C_SR1_BERR       8
+#define STM_I2C_SR1_TXE                7
+#define STM_I2C_SR1_RXNE       6
+#define STM_I2C_SR1_STOPF      4
+#define STM_I2C_SR1_ADD10      3
+#define STM_I2C_SR1_BTF                2
+#define STM_I2C_SR1_ADDR       1
+#define STM_I2C_SR1_SB         0
+
+#define STM_I2C_SR2_PEC                8
+#define  STM_I2C_SR2_PEC_MASK  0xff00
+#define STM_I2C_SR2_DUALF      7
+#define STM_I2C_SR2_SMBHOST    6
+#define STM_I2C_SR2_SMBDEFAULT 5
+#define STM_I2C_SR2_GENCALL    4
+#define STM_I2C_SR2_TRA                2
+#define STM_I2C_SR2_BUSY               1
+#define STM_I2C_SR2_MSL                0
+
+#define STM_I2C_CCR_FS         15
+#define STM_I2C_CCR_DUTY       14
+#define STM_I2C_CCR_CCR                0
+#define  STM_I2C_CCR_MASK      0x7ff
+
+struct stm_tim234 {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smcr;
+       vuint32_t       dier;
+
+       vuint32_t       sr;
+       vuint32_t       egr;
+       vuint32_t       ccmr1;
+       vuint32_t       ccmr2;
+
+       vuint32_t       ccer;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+
+       uint32_t        reserved_30;
+       vuint32_t       ccr1;
+       vuint32_t       ccr2;
+       vuint32_t       ccr3;
+
+       vuint32_t       ccr4;
+       uint32_t        reserved_44;
+       vuint32_t       dcr;
+       vuint32_t       dmar;
+
+       uint32_t        reserved_50;
+};
+
+extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
+
+#define STM_TIM234_CR1_CKD     8
+#define  STM_TIM234_CR1_CKD_1          0
+#define  STM_TIM234_CR1_CKD_2          1
+#define  STM_TIM234_CR1_CKD_4          2
+#define  STM_TIM234_CR1_CKD_MASK       3
+#define STM_TIM234_CR1_ARPE    7
+#define STM_TIM234_CR1_CMS     5
+#define  STM_TIM234_CR1_CMS_EDGE       0
+#define  STM_TIM234_CR1_CMS_CENTER_1   1
+#define  STM_TIM234_CR1_CMS_CENTER_2   2
+#define  STM_TIM234_CR1_CMS_CENTER_3   3
+#define  STM_TIM234_CR1_CMS_MASK       3
+#define STM_TIM234_CR1_DIR     4
+#define  STM_TIM234_CR1_DIR_UP         0
+#define  STM_TIM234_CR1_DIR_DOWN       1
+#define STM_TIM234_CR1_OPM     3
+#define STM_TIM234_CR1_URS     2
+#define STM_TIM234_CR1_UDIS    1
+#define STM_TIM234_CR1_CEN     0
+
+#define STM_TIM234_CR2_TI1S    7
+#define STM_TIM234_CR2_MMS     4
+#define  STM_TIM234_CR2_MMS_RESET              0
+#define  STM_TIM234_CR2_MMS_ENABLE             1
+#define  STM_TIM234_CR2_MMS_UPDATE             2
+#define  STM_TIM234_CR2_MMS_COMPARE_PULSE      3
+#define  STM_TIM234_CR2_MMS_COMPARE_OC1REF     4
+#define  STM_TIM234_CR2_MMS_COMPARE_OC2REF     5
+#define  STM_TIM234_CR2_MMS_COMPARE_OC3REF     6
+#define  STM_TIM234_CR2_MMS_COMPARE_OC4REF     7
+#define  STM_TIM234_CR2_MMS_MASK               7
+#define STM_TIM234_CR2_CCDS    3
+
+#define STM_TIM234_SMCR_ETP    15
+#define STM_TIM234_SMCR_ECE    14
+#define STM_TIM234_SMCR_ETPS   12
+#define  STM_TIM234_SMCR_ETPS_OFF              0
+#define  STM_TIM234_SMCR_ETPS_DIV_2            1
+#define  STM_TIM234_SMCR_ETPS_DIV_4            2
+#define  STM_TIM234_SMCR_ETPS_DIV_8            3
+#define  STM_TIM234_SMCR_ETPS_MASK             3
+#define STM_TIM234_SMCR_ETF    8
+#define  STM_TIM234_SMCR_ETF_NONE              0
+#define  STM_TIM234_SMCR_ETF_INT_N_2           1
+#define  STM_TIM234_SMCR_ETF_INT_N_4           2
+#define  STM_TIM234_SMCR_ETF_INT_N_8           3
+#define  STM_TIM234_SMCR_ETF_DTS_2_N_6         4
+#define  STM_TIM234_SMCR_ETF_DTS_2_N_8         5
+#define  STM_TIM234_SMCR_ETF_DTS_4_N_6         6
+#define  STM_TIM234_SMCR_ETF_DTS_4_N_8         7
+#define  STM_TIM234_SMCR_ETF_DTS_8_N_6         8
+#define  STM_TIM234_SMCR_ETF_DTS_8_N_8         9
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_5                10
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_6                11
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_8                12
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_5                13
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_6                14
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_8                15
+#define  STM_TIM234_SMCR_ETF_MASK              15
+#define STM_TIM234_SMCR_MSM    7
+#define STM_TIM234_SMCR_TS     4
+#define  STM_TIM234_SMCR_TS_ITR0               0
+#define  STM_TIM234_SMCR_TS_ITR1               1
+#define  STM_TIM234_SMCR_TS_ITR2               2
+#define  STM_TIM234_SMCR_TS_ITR3               3
+#define  STM_TIM234_SMCR_TS_TI1F_ED            4
+#define  STM_TIM234_SMCR_TS_TI1FP1             5
+#define  STM_TIM234_SMCR_TS_TI2FP2             6
+#define  STM_TIM234_SMCR_TS_ETRF               7
+#define  STM_TIM234_SMCR_TS_MASK               7
+#define STM_TIM234_SMCR_OCCS   3
+#define STM_TIM234_SMCR_SMS    0
+#define  STM_TIM234_SMCR_SMS_DISABLE           0
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_1    1
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_2    2
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_3    3
+#define  STM_TIM234_SMCR_SMS_RESET_MODE                4
+#define  STM_TIM234_SMCR_SMS_GATED_MODE                5
+#define  STM_TIM234_SMCR_SMS_TRIGGER_MODE      6
+#define  STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK    7
+#define  STM_TIM234_SMCR_SMS_MASK              7
+
+#define STM_TIM234_DIER_TDE            14
+#define STM_TIM234_DIER_CC4DE          12
+#define STM_TIM234_DIER_CC3DE          11
+#define STM_TIM234_DIER_CC2DE          10
+#define STM_TIM234_DIER_CC1DE          9
+#define STM_TIM234_DIER_UDE            8
+
+#define STM_TIM234_DIER_TIE            6
+#define STM_TIM234_DIER_CC4IE          4
+#define STM_TIM234_DIER_CC3IE          3
+#define STM_TIM234_DIER_CC2IE          2
+#define STM_TIM234_DIER_CC1IE          1
+#define STM_TIM234_DIER_UIE            0
+
+#define STM_TIM234_SR_CC4OF    12
+#define STM_TIM234_SR_CC3OF    11
+#define STM_TIM234_SR_CC2OF    10
+#define STM_TIM234_SR_CC1OF    9
+#define STM_TIM234_SR_TIF      6
+#define STM_TIM234_SR_CC4IF    4
+#define STM_TIM234_SR_CC3IF    3
+#define STM_TIM234_SR_CC2IF    2
+#define STM_TIM234_SR_CC1IF    1
+#define STM_TIM234_SR_UIF      0
+
+#define STM_TIM234_EGR_TG      6
+#define STM_TIM234_EGR_CC4G    4
+#define STM_TIM234_EGR_CC3G    3
+#define STM_TIM234_EGR_CC2G    2
+#define STM_TIM234_EGR_CC1G    1
+#define STM_TIM234_EGR_UG      0
+
+#define STM_TIM234_CCMR1_OC2CE 15
+#define STM_TIM234_CCMR1_OC2M  12
+#define  STM_TIM234_CCMR1_OC2M_FROZEN                  0
+#define  STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR1_OC2M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR1_OC2M_TOGGLE                  3
+#define  STM_TIM234_CCMR1_OC2M_FORCE_LOW               4
+#define  STM_TIM234_CCMR1_OC2M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR1_OC2M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR1_OC2M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR1_OC2M_MASK                    7
+#define STM_TIM234_CCMR1_OC2PE 11
+#define STM_TIM234_CCMR1_OC2FE 10
+#define STM_TIM234_CCMR1_CC2S  8
+#define  STM_TIM234_CCMR1_CC2S_OUTPUT                  0
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TI2               1
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TI1               2
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TRC               3
+#define  STM_TIM234_CCMR1_CC2S_MASK                    3
+
+#define STM_TIM234_CCMR1_OC1CE 7
+#define STM_TIM234_CCMR1_OC1M  4
+#define  STM_TIM234_CCMR1_OC1M_FROZEN                  0
+#define  STM_TIM234_CCMR1_OC1M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR1_OC1M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR1_OC1M_TOGGLE                  3
+#define  STM_TIM234_CCMR1_OC1M_FORCE_LOW               4
+#define  STM_TIM234_CCMR1_OC1M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR1_OC1M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR1_OC1M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR1_OC1M_MASK                    7
+#define STM_TIM234_CCMR1_OC1PE 3
+#define STM_TIM234_CCMR1_OC1FE 2
+#define STM_TIM234_CCMR1_CC1S  0
+#define  STM_TIM234_CCMR1_CC1S_OUTPUT                  0
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TI1               1
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TI2               2
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TRC               3
+#define  STM_TIM234_CCMR1_CC1S_MASK                    3
+
+#define STM_TIM234_CCMR1_IC2F  12
+#define  STM_TIM234_CCMR1_IC2F_NONE                    0
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_2              1
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_4              2
+#define  STM_TIM234_CCMR1_IC2F_CK_INT_N_8              3
+#define  STM_TIM234_CCMR1_IC2F_DTS_2_N_6               4
+#define  STM_TIM234_CCMR1_IC2F_DTS_2_N_8               5
+#define  STM_TIM234_CCMR1_IC2F_DTS_4_N_6               6
+#define  STM_TIM234_CCMR1_IC2F_DTS_4_N_8               7
+#define  STM_TIM234_CCMR1_IC2F_DTS_8_N_6               8
+#define  STM_TIM234_CCMR1_IC2F_DTS_8_N_8               9
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_5              10
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_6              11
+#define  STM_TIM234_CCMR1_IC2F_DTS_16_N_8              12
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_5              13
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_6              14
+#define  STM_TIM234_CCMR1_IC2F_DTS_32_N_8              15
+#define STM_TIM234_CCMR1_IC2PSC        10
+#define  STM_TIM234_CCMR1_IC2PSC_NONE                  0
+#define  STM_TIM234_CCMR1_IC2PSC_2                     1
+#define  STM_TIM234_CCMR1_IC2PSC_4                     2
+#define  STM_TIM234_CCMR1_IC2PSC_8                     3
+#define STM_TIM234_CCMR1_IC1F  4
+#define  STM_TIM234_CCMR1_IC1F_NONE                    0
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_2              1
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_4              2
+#define  STM_TIM234_CCMR1_IC1F_CK_INT_N_8              3
+#define  STM_TIM234_CCMR1_IC1F_DTS_2_N_6               4
+#define  STM_TIM234_CCMR1_IC1F_DTS_2_N_8               5
+#define  STM_TIM234_CCMR1_IC1F_DTS_4_N_6               6
+#define  STM_TIM234_CCMR1_IC1F_DTS_4_N_8               7
+#define  STM_TIM234_CCMR1_IC1F_DTS_8_N_6               8
+#define  STM_TIM234_CCMR1_IC1F_DTS_8_N_8               9
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_5              10
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_6              11
+#define  STM_TIM234_CCMR1_IC1F_DTS_16_N_8              12
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_5              13
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_6              14
+#define  STM_TIM234_CCMR1_IC1F_DTS_32_N_8              15
+#define STM_TIM234_CCMR1_IC1PSC        2
+#define  STM_TIM234_CCMR1_IC1PSC_NONE                  0
+#define  STM_TIM234_CCMR1_IC1PSC_2                     1
+#define  STM_TIM234_CCMR1_IC1PSC_4                     2
+#define  STM_TIM234_CCMR1_IC1PSC_8                     3
+
+#define STM_TIM234_CCMR2_OC4CE 15
+#define STM_TIM234_CCMR2_OC4M  12
+#define  STM_TIM234_CCMR2_OC4M_FROZEN                  0
+#define  STM_TIM234_CCMR2_OC4M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR2_OC4M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR2_OC4M_TOGGLE                  3
+#define  STM_TIM234_CCMR2_OC4M_FORCE_LOW               4
+#define  STM_TIM234_CCMR2_OC4M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR2_OC4M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR2_OC4M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR2_OC4M_MASK                    7
+#define STM_TIM234_CCMR2_OC4PE 11
+#define STM_TIM234_CCMR2_OC4FE 10
+#define STM_TIM234_CCMR2_CC4S  8
+#define  STM_TIM234_CCMR2_CC4S_OUTPUT                  0
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TI4               1
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TI3               2
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TRC               3
+#define  STM_TIM234_CCMR2_CC4S_MASK                    3
+
+#define STM_TIM234_CCMR2_OC3CE 7
+#define STM_TIM234_CCMR2_OC3M  4
+#define  STM_TIM234_CCMR2_OC3M_FROZEN                  0
+#define  STM_TIM234_CCMR2_OC3M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR2_OC3M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR2_OC3M_TOGGLE                  3
+#define  STM_TIM234_CCMR2_OC3M_FORCE_LOW               4
+#define  STM_TIM234_CCMR2_OC3M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR2_OC3M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR2_OC3M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR2_OC3M_MASK                    7
+#define STM_TIM234_CCMR2_OC3PE 3
+#define STM_TIM234_CCMR2_OC3FE 2
+#define STM_TIM234_CCMR2_CC3S  0
+#define  STM_TIM234_CCMR2_CC3S_OUTPUT                  0
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TI3               1
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TI4               2
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TRC               3
+#define  STM_TIM234_CCMR2_CC3S_MASK                    3
+
+#define STM_TIM234_CCER_CC4NP  15
+#define STM_TIM234_CCER_CC4P   13
+#define  STM_TIM234_CCER_CC4P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC4P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC4E   12
+#define STM_TIM234_CCER_CC3NP  11
+#define STM_TIM234_CCER_CC3P   9
+#define  STM_TIM234_CCER_CC3P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC3P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC3E   8
+#define STM_TIM234_CCER_CC2NP  7
+#define STM_TIM234_CCER_CC2P   5
+#define  STM_TIM234_CCER_CC2P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC2P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC2E   4
+#define STM_TIM234_CCER_CC1NP  3
+#define STM_TIM234_CCER_CC1P   1
+#define  STM_TIM234_CCER_CC1P_ACTIVE_HIGH      0
+#define  STM_TIM234_CCER_CC1P_ACTIVE_LOW       1
+#define STM_TIM234_CCER_CC1E   0
+
+struct stm_usb {
+       vuint32_t       epr[8];
+       uint8_t         reserved_20[0x40 - 0x20];
+       vuint32_t       cntr;
+       vuint32_t       istr;
+       vuint32_t       fnr;
+       vuint32_t       daddr;
+       vuint32_t       btable;
+};
+
+#define STM_USB_EPR_CTR_RX     15
+#define  STM_USB_EPR_CTR_RX_WRITE_INVARIANT            1
+#define STM_USB_EPR_DTOG_RX    14
+#define STM_USB_EPR_DTOG_RX_WRITE_INVARIANT            0
+#define STM_USB_EPR_STAT_RX    12
+#define  STM_USB_EPR_STAT_RX_DISABLED                  0
+#define  STM_USB_EPR_STAT_RX_STALL                     1
+#define  STM_USB_EPR_STAT_RX_NAK                       2
+#define  STM_USB_EPR_STAT_RX_VALID                     3
+#define  STM_USB_EPR_STAT_RX_MASK                      3
+#define  STM_USB_EPR_STAT_RX_WRITE_INVARIANT           0
+#define STM_USB_EPR_SETUP      11
+#define STM_USB_EPR_EP_TYPE    9
+#define  STM_USB_EPR_EP_TYPE_BULK                      0
+#define  STM_USB_EPR_EP_TYPE_CONTROL                   1
+#define  STM_USB_EPR_EP_TYPE_ISO                       2
+#define  STM_USB_EPR_EP_TYPE_INTERRUPT                 3
+#define  STM_USB_EPR_EP_TYPE_MASK                      3
+#define STM_USB_EPR_EP_KIND    8
+#define  STM_USB_EPR_EP_KIND_DBL_BUF                   1       /* Bulk */
+#define  STM_USB_EPR_EP_KIND_STATUS_OUT                        1       /* Control */
+#define STM_USB_EPR_CTR_TX     7
+#define  STM_USB_CTR_TX_WRITE_INVARIANT                        1
+#define STM_USB_EPR_DTOG_TX    6
+#define  STM_USB_EPR_DTOG_TX_WRITE_INVARIANT           0
+#define STM_USB_EPR_STAT_TX    4
+#define  STM_USB_EPR_STAT_TX_DISABLED                  0
+#define  STM_USB_EPR_STAT_TX_STALL                     1
+#define  STM_USB_EPR_STAT_TX_NAK                       2
+#define  STM_USB_EPR_STAT_TX_VALID                     3
+#define  STM_USB_EPR_STAT_TX_WRITE_INVARIANT           0
+#define  STM_USB_EPR_STAT_TX_MASK                      3
+#define STM_USB_EPR_EA         0
+#define  STM_USB_EPR_EA_MASK                           0xf
+
+#define STM_USB_CNTR_CTRM      15
+#define STM_USB_CNTR_PMAOVRM   14
+#define STM_USB_CNTR_ERRM      13
+#define STM_USB_CNTR_WKUPM     12
+#define STM_USB_CNTR_SUSPM     11
+#define STM_USB_CNTR_RESETM    10
+#define STM_USB_CNTR_SOFM      9
+#define STM_USB_CNTR_ESOFM     8
+#define STM_USB_CNTR_RESUME    4
+#define STM_USB_CNTR_FSUSP     3
+#define STM_USB_CNTR_LP_MODE   2
+#define STM_USB_CNTR_PDWN      1
+#define STM_USB_CNTR_FRES      0
+
+#define STM_USB_ISTR_CTR       15
+#define STM_USB_ISTR_PMAOVR    14
+#define STM_USB_ISTR_ERR       13
+#define STM_USB_ISTR_WKUP      12
+#define STM_USB_ISTR_SUSP      11
+#define STM_USB_ISTR_RESET     10
+#define STM_USB_ISTR_SOF       9
+#define STM_USB_ISTR_ESOF      8
+#define STM_USB_ISTR_DIR       4
+#define STM_USB_ISTR_EP_ID     0
+#define  STM_USB_ISTR_EP_ID_MASK               0xf
+
+#define STM_USB_FNR_RXDP       15
+#define STM_USB_FNR_RXDM       14
+#define STM_USB_FNR_LCK                13
+#define STM_USB_FNR_LSOF       11
+#define  STM_USB_FNR_LSOF_MASK                 0x3
+#define STM_USB_FNR_FN         0
+#define  STM_USB_FNR_FN_MASK                   0x7ff
+
+#define STM_USB_DADDR_EF       7
+#define STM_USB_DADDR_ADD      0
+#define  STM_USB_DADDR_ADD_MASK                        0x7f
+
+extern struct stm_usb stm_usb;
+
+union stm_usb_bdt {
+       struct {
+               vuint32_t       addr_tx;
+               vuint32_t       count_tx;
+               vuint32_t       addr_rx;
+               vuint32_t       count_rx;
+       } single;
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_tx[2];
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_rx[2];
+};
+
+#define STM_USB_BDT_COUNT_RX_BL_SIZE   15
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK 10
+#define  STM_USB_BDT_COUNT_RX_NUM_BLOCK_MASK   0x1f
+#define STM_USB_BDT_COUNT_RX_COUNT_RX  0
+#define  STM_USB_BDT_COUNT_RX_COUNT_RX_MASK    0x1ff
+
+#define STM_USB_BDT_SIZE       8
+
+extern uint8_t stm_usb_sram[] __attribute__ ((aligned(4)));
+
+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;
+
+#endif /* _STM32L0_H_ */
diff --git a/src/stm32l0/stm32l0.ld b/src/stm32l0/stm32l0.ld
new file mode 100644 (file)
index 0000000..8f86736
--- /dev/null
@@ -0,0 +1,58 @@
+/* IOPORT */
+stm_gpioh  = 0x50001c00;
+stm_gpioe  = 0x50001000;
+stm_gpiod  = 0x50000c00;
+stm_gpioc  = 0x50000800;
+stm_gpiob  = 0x50000400;
+stm_gpioa  = 0x50000000;
+
+/* AHB */
+stm_aes    = 0x40026000;
+stm_crc    = 0x40023000;
+stm_flash  = 0x40022000;
+stm_rcc    = 0x40021000;
+stm_dma1   = 0x40020000;
+
+/* APB2 */
+stm_dbg    = 0x40015800;
+stm_usart1 = 0x40013800;
+stm_spi1   = 0x40013000;
+stm_adc1   = 0x40012400;
+stm_firewall = 0x41011c00;
+stm_tim22  = 0x41011400;
+stm_tim21  = 0x41010800;
+stm_exti   = 0x41010400;
+stm_syscfg = 0x41010000;
+stm_comp   = 0x41010000;
+
+/* APB1 */
+stm_lptim1 = 0x40007c00;
+stm_i2c3   = 0x40007800;
+stm_pwr    = 0x40007000;
+stm_i2c2   = 0x40005800;
+stm_i2c1   = 0x40005400;
+stm_usart5 = 0x40005000;
+stm_usart4 = 0x40004c00;
+stm_lpuart1 = 0x40004800;
+stm_usart2 = 0x40004400;
+stm_spi2   = 0x40003800;
+stm_iwdg   = 0x40003000;
+stm_wwdg   = 0x40002c00;
+stm_rtc    = 0x40002800;
+stm_timer7 = 0x40001400;
+stm_timer6 = 0x40001000;
+stm_timer3 = 0x40000400;
+stm_timer2 = 0x40000000;
+
+/* ARM */
+stm_systick = 0xe000e010;
+
+stm_nvic   = 0xe000e100;
+
+stm_scb    = 0xe000ed00;
+
+stm_mpu    = 0xe000ed90;
+
+/* ID registers */
+stm_flash_size = 0x1ff8007c;
+stm_device_id = 0x1ff80050;