Browse Source

Initial port of Greaseweazle to AT32F415.

Keir Fraser 4 years ago
parent
commit
cfa91ce5f1

+ 5 - 0
Makefile

@@ -65,6 +65,11 @@ dist:
 	$(PYTHON) ./scripts/mk_update.py cat $(PROJ)-$(VER)/$(PROJ)-$(VER).upd \
 		$(PROJ)-$(VER)/$(PROJ)-$(VER).upd $(PROJ)-$(VER).upd
 	$(MAKE) clean
+	$(MAKE) mcu=at32f415 all
+	cp -a $(PROJ)-$(VER).hex $(PROJ)-$(VER)/$(PROJ)-AT32F415-$(VER).hex
+	$(PYTHON) ./scripts/mk_update.py cat $(PROJ)-$(VER)/$(PROJ)-$(VER).upd \
+		$(PROJ)-$(VER)/$(PROJ)-$(VER).upd $(PROJ)-$(VER).upd
+	$(MAKE) clean
 	$(ZIP) $(PROJ)-$(VER).zip $(PROJ)-$(VER)
 
 windist: pysetup

+ 3 - 0
Rules.mk

@@ -30,6 +30,9 @@ stm32f1=y
 else ifeq ($(mcu),stm32f7)
 FLAGS += -mcpu=cortex-m7 -DSTM32F7=7 -DMCU=7
 stm32f7=y
+else ifeq ($(mcu),at32f415)
+FLAGS += -mcpu=cortex-m4 -DAT32F415=4 -DMCU=4
+at32f415=y
 endif
 
 ifneq ($(debug),y)

+ 1 - 1
bootloader/Bootloader.ld.S

@@ -1,5 +1,5 @@
 
-#if MCU == STM32F1
+#if MCU == STM32F1 || MCU == AT32F415
 
 #define FLASH_BASE 0x08000000
 #define FLASH_LEN  8K

+ 1 - 0
bootloader/mcu/Makefile

@@ -1,2 +1,3 @@
 SUBDIRS-$(stm32f1) += stm32f1
 SUBDIRS-$(stm32f7) += stm32f7
+SUBDIRS-$(at32f415) += at32f415

+ 4 - 0
bootloader/mcu/at32f415/Makefile

@@ -0,0 +1,4 @@
+RPATH = $(ROOT)/src/mcu/at32f415
+
+OBJS += stm32.o
+OBJS += fpec.o

+ 6 - 0
bootloader/usb/Makefile

@@ -3,7 +3,13 @@ RPATH = $(ROOT)/src/usb
 OBJS += config.o
 OBJS += core.o
 OBJS += cdc_acm.o
+
 OBJS-$(stm32f1) += hw_f1.o
+
 OBJS-$(stm32f7) += hw_dwc_otg.o
+OBJS-$(stm32f7) += hw_f7.o
+
+OBJS-$(at32f415) += hw_dwc_otg.o
+OBJS-$(at32f415) += hw_at32f415.o
 
 $(OBJS) $(OBJS-y): CFLAGS += -include $(ROOT)/src/usb/defs.h

+ 3 - 0
inc/decls.h

@@ -24,6 +24,9 @@
 #elif MCU == STM32F7
 #include "mcu/stm32/f7_regs.h"
 #include "mcu/stm32/f7.h"
+#elif MCU == AT32F415
+#include "mcu/at32/f415_regs.h"
+#include "mcu/at32/f415.h"
 #endif
 #include "intrinsics.h"
 

+ 2 - 0
inc/mcu/at32/f415.h

@@ -0,0 +1,2 @@
+
+#include "../stm32/f1.h"

+ 4 - 0
inc/mcu/at32/f415_regs.h

@@ -0,0 +1,4 @@
+
+#include "../stm32/f1_regs.h"
+
+#define TIM_CR1_PMEN (1u<<10)

+ 3 - 0
inc/mcu/stm32/f1.h

@@ -60,6 +60,9 @@ static SER_ID ser_id = (uint32_t *)0x1ffff7e8;
 /* No delay required after enabling a peripheral clock, before accessing it. */
 #define peripheral_clock_delay() ((void)0)
 
+/* No secondary RAM region */
+#define section_ext_ram
+
 /*
  * Local variables:
  * mode: C

+ 12 - 3
scripts/mk_update.py

@@ -30,9 +30,18 @@ import re, struct, sys
 
 from greaseweazle import version
 
+name_to_hw_model = { 'stm32f1': 1,
+                     'stm32f7': 7,
+                     'at32f415': 4 }
+
+hw_model_to_name = { 1: 'STM32F103',
+                     7: 'STM32F730',
+                     4: 'AT32F415' }
+
 def mk_cat_entry(dat, hw_model, sig):
     max_kb = { 1: { b'BL':  8, b'GW': 56 },
-               7: { b'BL': 16, b'GW': 48 } }
+               7: { b'BL': 16, b'GW': 48 },
+               4: { b'BL':  8, b'GW': 56 } }
     dlen = len(dat)
     assert (dlen & 3) == 0, "input is not longword padded"
     assert dlen <= max_kb[hw_model][sig]*1024, "input is too long"
@@ -46,7 +55,7 @@ def mk_cat_entry(dat, hw_model, sig):
 
 def new_upd(argv):
     dat = b'GWUP'
-    hw_model = int(re.match("\w+f(\d)$", argv[2]).group(1))
+    hw_model = name_to_hw_model[argv[2]]
     with open(argv[1], "rb") as gw_f:
         dat += mk_cat_entry(gw_f.read(), hw_model, b'GW')
     with open(argv[0], "rb") as bl_f:
@@ -77,7 +86,7 @@ def _verify_upd(d):
         crc16 = crcmod.predefined.Crc('crc-ccitt-false')
         crc16.update(d[4:upd_len+4])
         assert crc16.crcValue == 0
-        print('F%u %s v%u.%u' % (hw_model,
+        print('%s %s v%u.%u' % (hw_model_to_name[hw_model],
                                  {b'BL': 'Boot', b'GW': 'Main'}[upd_type],
                                  major, minor))
         d = d[upd_len+4:]

+ 2 - 0
scripts/stm32.ld.S

@@ -8,6 +8,8 @@ MEMORY
 #elif MCU == STM32F7
   RAM (rwx)       : ORIGIN = 0x20000000, LENGTH = 64K
   EXT_RAM (rwx)   : ORIGIN = 0x20010000, LENGTH = 192K
+#elif MCU == AT32F415
+  RAM (rwx)       : ORIGIN = 0x20000000, LENGTH = 32K
 #endif
 }
 

+ 1 - 1
src/Greaseweazle.ld.S

@@ -1,5 +1,5 @@
 
-#if MCU == STM32F1
+#if MCU == STM32F1 || MCU == AT32F415
 
 #define FLASH_BASE 0x08002000
 #define FLASH_LEN  56K

+ 2 - 0
src/board.c

@@ -24,6 +24,8 @@ static void gpio_pull_up_pins(GPIO gpio, uint16_t mask)
 #include "mcu/stm32f1/board.c"
 #elif MCU == STM32F7
 #include "mcu/stm32f7/board.c"
+#elif MCU == AT32F415
+#include "mcu/at32f415/board.c"
 #endif
 
 void board_init(void)

+ 5 - 3
src/console.c

@@ -17,13 +17,15 @@
 #define PCLK SYSCLK
 #elif MCU == STM32F7
 #define PCLK (APB2_MHZ * 1000000)
+#elif MCU == AT32F415
+#define PCLK SYSCLK
 #endif
 
 #define USART1_IRQ 37
 
 static void ser_putc(uint8_t c)
 {
-#if MCU == STM32F1
+#if MCU == STM32F1 || MCU == AT32F415
     while (!(usart1->sr & USART_SR_TXE))
         cpu_relax();
     usart1->dr = c;
@@ -83,7 +85,7 @@ void console_init(void)
     peripheral_clock_delay();
 
     /* Enable TX pin (PA9) for USART output, RX pin (PA10) as input. */
-#if MCU == STM32F1
+#if MCU == STM32F1 || MCU == AT32F415
     gpio_configure_pin(gpioa, 9, AFO_pushpull(_10MHz));
     gpio_configure_pin(gpioa, 10, GPI_pull_up);
 #elif MCU == STM32F7
@@ -102,7 +104,7 @@ void console_init(void)
  * any serial input to cause a crash dump of the stuck context. */
 void console_crash_on_input(void)
 {
-#if MCU == STM32F1
+#if MCU == STM32F1 || MCU == AT32F415
     (void)usart1->dr; /* clear UART_SR_RXNE */
 #elif MCU == STM32F7
     usart1->rqr = USART_RQR_RXFRQ; /* clear ISR_RXNE */

+ 2 - 0
src/floppy.c

@@ -49,6 +49,8 @@ static const struct gw_delay factory_delay_params = {
 #include "mcu/stm32f1/floppy.c"
 #elif MCU == STM32F7
 #include "mcu/stm32f7/floppy.c"
+#elif MCU == AT32F415
+#include "mcu/at32f415/floppy.c"
 #endif
 
 static struct index {

+ 41 - 1
src/fw_update.c

@@ -9,7 +9,7 @@
  * See the file COPYING for more details, or visit <http://unlicense.org>.
  */
 
-#if MCU == STM32F1
+#if MCU == STM32F1 || MCU == AT32F415
 /*  8kB-64kB (56kB total) */
 #define FIRMWARE_START 0x08002000
 #define FIRMWARE_END   0x08010000
@@ -284,6 +284,46 @@ static bool_t enter_bootloader(void)
     return upd_requested || upd_strapped;
 }
 
+#elif MCU == AT32F415
+
+static bool_t check_update_requested(void)
+{
+#if 1
+    return FALSE;
+#else
+    /* Check-and-clear a magic value poked into SRAM1 by the main firmware. */
+    bool_t match = (*(volatile uint32_t *)0x20010000 == 0xdeadbeef);
+    *(volatile uint32_t *)0x20010000 = 0;
+    return match;
+#endif
+}
+
+static bool_t check_update_strapped(void)
+{
+    int i, j;
+
+    /* Check whether the Serial TX/RX lines (PA9/PA10) are strapped. */
+    rcc->apb2enr |= RCC_APB2ENR_IOPAEN;
+    gpio_configure_pin(gpioa, 9, GPO_pushpull(IOSPD_LOW, HIGH));
+    gpio_configure_pin(gpioa, 10, GPI_pull_up);
+    for (i = 0; i < 10; i++) {
+        gpio_write_pin(gpioa, 9, i&1);
+        for (j = 0; j < 10000; j++)
+            cpu_relax();
+        if (gpio_read_pin(gpioa, 10) != (i&1))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+static bool_t enter_bootloader(void)
+{
+    bool_t upd_requested = check_update_requested();
+    upd_strapped = check_update_strapped();
+    return upd_requested || upd_strapped;
+}
+
 #endif
 
 int main(void)

+ 1 - 0
src/mcu/Makefile

@@ -1,2 +1,3 @@
 SUBDIRS-$(stm32f1) += stm32f1
 SUBDIRS-$(stm32f7) += stm32f7
+SUBDIRS-$(at32f415) += at32f415

+ 5 - 0
src/mcu/at32f415/Makefile

@@ -0,0 +1,5 @@
+OBJS += stm32.o
+OBJS += fpec.o
+
+fpec.c:
+	ln -sf ../stm32f1/$@ $@

+ 30 - 0
src/mcu/at32f415/board.c

@@ -0,0 +1,30 @@
+/*
+ * at32f415/board.c
+ * 
+ * Board-specific setup and management.
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+#define gpio_led gpiob
+#define pin_led 13
+
+static void mcu_board_init(void)
+{
+    gpio_pull_up_pins(gpioa, 0xe1fe); /* PA1-8,13-15 */
+    gpio_pull_up_pins(gpiob, 0x0027); /* PB0-2,5 */
+    gpio_pull_up_pins(gpioc, 0xffff); /* PC0-15 */
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 202 - 0
src/mcu/at32f415/floppy.c

@@ -0,0 +1,202 @@
+/*
+ * at32f415/floppy.c
+ * 
+ * Floppy interface control: AT32F415CBT7
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+#define O_FALSE 1
+#define O_TRUE  0
+
+#define GPO_bus GPO_opendrain(_2MHz,O_FALSE)
+#define AFO_bus AFO_opendrain(_2MHz)
+static unsigned int GPI_bus;
+
+/* Input pins */
+#define gpio_index  gpiob
+#define pin_index   6 /* PB6 */
+#define gpio_trk0   gpiob
+#define pin_trk0    7 /* PB7 */
+#define gpio_wrprot gpiob
+#define pin_wrprot  8 /* PB8 */
+
+/* Output pins. */
+#define gpio_densel gpiob
+#define pin_densel  9 /* PB9 */
+#define gpio_sel   gpiob
+#define pin_sel    10 /* PB10 */
+#define gpio_mot   gpiob
+#define pin_mot    11 /* PB11 */
+#define gpio_dir   gpiob
+#define pin_dir    12 /* PB12 */
+#define gpio_step  gpiob
+#define pin_step   13 /* PB13 */
+#define gpio_wgate gpiob
+#define pin_wgate  14 /* PB14 */
+#define gpio_head  gpiob
+#define pin_head   15 /* PB15 */
+
+/* RDATA: Pin B3, Timer 2 Channel 2, DMA1 Channel 7. */
+#define gpio_rdata  gpiob
+#define pin_rdata   3
+#define tim_rdata   (tim2)
+#define dma_rdata   (dma1->ch7)
+
+/* WDATA: Pin B4, Timer 3 Channel 1, DMA1 Channel 3. */
+#define gpio_wdata  gpiob
+#define pin_wdata   4
+#define tim_wdata   (tim3)
+#define dma_wdata   (dma1->ch3)
+
+typedef uint16_t timcnt_t;
+
+#define irq_index 23
+void IRQ_23(void) __attribute__((alias("IRQ_INDEX_changed"))); /* EXTI9_5 */
+
+/* We sometimes cast u_buf to uint32_t[], hence the alignment constraint. */
+#define U_BUF_SZ 8192
+static uint8_t u_buf[U_BUF_SZ] aligned(4);
+
+static void floppy_mcu_init(void)
+{
+    /* Determine whether input pins must be internally pulled down. */
+    configure_pin(index, GPI_pull_down);
+    delay_us(10);
+    GPI_bus = (get_index() == LOW) ? GPI_pull_up : GPI_floating;
+    printk("Floppy Inputs: %sternal Pullup\n",
+           (GPI_bus == GPI_pull_up) ? "In" : "Ex");
+
+    /* Remap timers to RDATA/WDATA pins. */
+    afio->mapr |= (AFIO_MAPR_TIM2_REMAP_PARTIAL_1
+                   | AFIO_MAPR_TIM3_REMAP_PARTIAL);
+
+    /* Set up EXTI mapping for INDEX: PB[15:0] -> EXT[15:0] */
+    afio->exticr1 = afio->exticr2 = afio->exticr3 = afio->exticr4 = 0x1111;
+
+    configure_pin(rdata, GPI_bus);
+
+    /* Configure SELECT/MOTOR lines. */
+    configure_pin(sel, GPO_bus);
+    configure_pin(mot, GPO_bus);
+
+    /* Configure user-modifiable lines. */
+    configure_pin(densel, GPO_bus);
+}
+
+static void rdata_prep(void)
+{
+    /* RDATA Timer setup: 
+     * The counter runs from 0x0000-0xFFFF inclusive at SAMPLE rate.
+     *  
+     * Ch.2 (RDATA) is in Input Capture mode, sampling on every clock and with
+     * no input prescaling or filtering. Samples are captured on the falling 
+     * edge of the input (CCxP=1). DMA is used to copy the sample into a ring
+     * buffer for batch processing in the DMA-completion ISR. */
+    tim_rdata->psc = TIM_PSC-1;
+    tim_rdata->arr = 0xffff;
+    tim_rdata->ccmr1 = TIM_CCMR1_CC2S(TIM_CCS_INPUT_TI1);
+    tim_rdata->dier = TIM_DIER_CC2DE;
+    tim_rdata->cr2 = 0;
+    tim_rdata->egr = TIM_EGR_UG; /* update CNT, PSC, ARR */
+    tim_rdata->sr = 0; /* dummy write */
+
+    /* RDATA DMA setup: From the RDATA Timer's CCRx into a circular buffer. */
+    dma_rdata.par = (uint32_t)(unsigned long)&tim_rdata->ccr2;
+    dma_rdata.cr = (DMA_CR_PL_HIGH |
+                    DMA_CR_MSIZE_16BIT |
+                    DMA_CR_PSIZE_16BIT |
+                    DMA_CR_MINC |
+                    DMA_CR_CIRC |
+                    DMA_CR_DIR_P2M |
+                    DMA_CR_EN);
+
+    tim_rdata->ccer = TIM_CCER_CC2E | TIM_CCER_CC2P;
+}
+
+static void wdata_prep(void)
+{
+    /* WDATA Timer setup:
+     * The counter is incremented at SAMPLE rate. 
+     *  
+     * Ch.1 (WDATA) is in PWM mode 1. It outputs O_TRUE for 400ns and then 
+     * O_FALSE until the counter reloads. By changing the ARR via DMA we alter
+     * the time between (fixed-width) O_TRUE pulses, mimicking floppy drive 
+     * timings. */
+    tim_wdata->psc = TIM_PSC-1;
+    tim_wdata->ccmr1 = (TIM_CCMR1_CC1S(TIM_CCS_OUTPUT) |
+                        TIM_CCMR1_OC1M(TIM_OCM_PWM1));
+    tim_wdata->ccer = TIM_CCER_CC1E | ((O_TRUE==0) ? TIM_CCER_CC1P : 0);
+    tim_wdata->ccr1 = sample_ns(400);
+    tim_wdata->dier = TIM_DIER_UDE;
+    tim_wdata->cr2 = 0;
+}
+
+static void dma_wdata_start(void)
+{
+    dma_wdata.cr = (DMA_CR_PL_HIGH |
+                    DMA_CR_MSIZE_16BIT |
+                    DMA_CR_PSIZE_16BIT |
+                    DMA_CR_MINC |
+                    DMA_CR_CIRC |
+                    DMA_CR_DIR_M2P |
+                    DMA_CR_EN);
+}
+
+static void drive_deselect(void)
+{
+    write_pin(sel, FALSE);
+    unit_nr = -1;
+}
+
+static uint8_t drive_select(uint8_t nr)
+{
+    write_pin(sel, TRUE);
+    unit_nr = 0;
+    delay_us(delay_params.select_delay);
+    return ACK_OKAY;
+}
+
+static uint8_t drive_motor(uint8_t nr, bool_t on)
+{
+    if (unit[0].motor == on)
+        return ACK_OKAY;
+
+    write_pin(mot, on);
+    unit[0].motor = on;
+    if (on)
+        delay_ms(delay_params.motor_delay);
+
+    return ACK_OKAY;
+}
+
+static uint8_t set_user_pin(unsigned int pin, unsigned int level)
+{
+    if (pin != 2)
+        return ACK_BAD_PIN;
+    gpio_write_pin(gpio_densel, pin_densel, level);
+    return ACK_OKAY;
+}
+
+static void reset_user_pins(void)
+{
+    write_pin(densel, FALSE);
+}
+
+/* No Flippy-modded drive support on F1 boards. */
+#define flippy_trk0_sensor_disable() ((void)0)
+#define flippy_trk0_sensor_enable() ((void)0)
+#define flippy_detect() FALSE
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 1 - 0
src/mcu/at32f415/fpec.c

@@ -0,0 +1 @@
+../stm32f1/fpec.c

+ 96 - 0
src/mcu/at32f415/stm32.c

@@ -0,0 +1,96 @@
+/*
+ * f1/stm32.c
+ * 
+ * Core and peripheral registers.
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+static void clock_init(void)
+{
+    /* Flash controller: reads require 2 wait states at 72MHz. */
+    flash->acr = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY(2);
+
+    /* Start up the external oscillator. */
+    rcc->cr |= RCC_CR_HSEON;
+    while (!(rcc->cr & RCC_CR_HSERDY))
+        cpu_relax();
+
+    /* PLLs, scalers, muxes. */
+    rcc->cfgr = (RCC_CFGR_PLLMUL(9) |        /* PLL = 9*8MHz = 72MHz */
+                 RCC_CFGR_PLLSRC_PREDIV1 |
+                 RCC_CFGR_ADCPRE_DIV8 |
+                 RCC_CFGR_PPRE1_DIV2);
+
+    /* Enable and stabilise the PLL. */
+    rcc->cr |= RCC_CR_PLLON;
+    while (!(rcc->cr & RCC_CR_PLLRDY))
+        cpu_relax();
+
+    /* Switch to the externally-driven PLL for system clock. */
+    rcc->cfgr |= RCC_CFGR_SW_PLL;
+    while ((rcc->cfgr & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL)
+        cpu_relax();
+
+    /* Internal oscillator no longer needed. */
+    rcc->cr &= ~RCC_CR_HSION;
+}
+
+static void gpio_init(GPIO gpio)
+{
+    /* Floating Input. Reference Manual states that JTAG pins are in PU/PD
+     * mode at reset, so ensure all PU/PD are disabled. */
+    gpio->crl = gpio->crh = 0x44444444u;
+}
+
+static void peripheral_init(void)
+{
+    /* Enable basic GPIO and AFIO clocks, all timers, and DMA. */
+    rcc->apb1enr = (RCC_APB1ENR_TIM2EN |
+                    RCC_APB1ENR_TIM3EN |
+                    RCC_APB1ENR_TIM4EN);
+    rcc->apb2enr = (RCC_APB2ENR_IOPAEN |
+                    RCC_APB2ENR_IOPBEN |
+                    RCC_APB2ENR_IOPCEN |
+                    RCC_APB2ENR_AFIOEN |
+                    RCC_APB2ENR_TIM1EN);
+    rcc->ahbenr = RCC_AHBENR_DMA1EN;
+
+    /* All pins in a stable state. */
+    gpio_init(gpioa);
+    gpio_init(gpiob);
+    gpio_init(gpioc);
+}
+
+void stm32_init(void)
+{
+    cortex_init();
+    clock_init();
+    peripheral_init();
+    cpu_sync();
+}
+
+void gpio_configure_pin(GPIO gpio, unsigned int pin, unsigned int mode)
+{
+    gpio_write_pin(gpio, pin, mode >> 4);
+    mode &= 0xfu;
+    if (pin >= 8) {
+        pin -= 8;
+        gpio->crh = (gpio->crh & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
+    } else {
+        gpio->crl = (gpio->crl & ~(0xfu<<(pin<<2))) | (mode<<(pin<<2));
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 10 - 2
src/timer.c

@@ -14,15 +14,23 @@ void IRQ_25(void) __attribute__((alias("IRQ_timer")));
 #define TIMER_IRQ 25
 #define tim tim1
 #define tim_bits 16
+#define TIM_CR1_MCUBITS 0
 #elif MCU == STM32F7
 void IRQ_50(void) __attribute__((alias("IRQ_timer")));
 #define TIMER_IRQ 50
 #define tim tim5 /* 32-bit timer */
 #define tim_bits 32
+#define TIM_CR1_MCUBITS 0
+#elif MCU == AT32F415
+void IRQ_50(void) __attribute__((alias("IRQ_timer")));
+#define TIMER_IRQ 50
+#define tim tim5 /* 32-bit timer */
+#define tim_bits 32
+#define TIM_CR1_MCUBITS TIM_CR1_PMEN
 #endif
 
 /* IRQ only on counter overflow, one-time enable. */
-#define TIM_CR1 (TIM_CR1_URS | TIM_CR1_OPM)
+#define TIM_CR1 (TIM_CR1_URS | TIM_CR1_OPM | TIM_CR1_MCUBITS)
 
 /* Empirically-determined offset applied to timer deadlines to counteract the
  * latency incurred by reprogram_timer() and IRQ_timer(). */
@@ -114,7 +122,7 @@ void timer_cancel(struct timer *timer)
 
 void timers_init(void)
 {
-#if MCU == STM32F7
+#if MCU == STM32F7 || MCU == AT32F415
     rcc->apb1enr |= RCC_APB1ENR_TIM5EN;
     peripheral_clock_delay();
 #endif

+ 6 - 0
src/usb/Makefile

@@ -1,7 +1,13 @@
 OBJS += config.o
 OBJS += core.o
 OBJS += cdc_acm.o
+
 OBJS-$(stm32f1) += hw_f1.o
+
 OBJS-$(stm32f7) += hw_dwc_otg.o
+OBJS-$(stm32f7) += hw_f7.o
+
+OBJS-$(at32f415) += hw_dwc_otg.o
+OBJS-$(at32f415) += hw_at32f415.o
 
 $(OBJS) $(OBJS-y): CFLAGS += -include defs.h

+ 43 - 0
src/usb/hw_at32f415.c

@@ -0,0 +1,43 @@
+/*
+ * hw_at32f415.c
+ * 
+ * AT32F415-specific handling for DWC-OTG USB 2.0 controller.
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+#include "hw_dwc_otg.h"
+
+void hw_usb_init(void)
+{
+    conf_iface = IFACE_FS;
+
+    rcc->ahbenr |= RCC_AHBENR_OTGFSEN;
+
+    /* PHY selection must be followed by a core reset. */
+    core_reset();
+    /* Activate FS transceiver. */
+    otg->gccfg = OTG_GCCFG_PWRDWN | OTG_GCCFG_VBUSBSEN;
+
+    dwc_otg_init();
+}
+
+void hw_usb_deinit(void)
+{
+    dwc_otg_deinit();
+
+    rcc->ahbenr &= ~RCC_AHBENR_OTGFSEN;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

+ 5 - 97
src/usb/hw_dwc_otg.c

@@ -11,11 +11,11 @@
 
 #include "hw_dwc_otg.h"
 
-static int conf_iface;
+int conf_iface;
 static bool_t is_hs;
 
 static struct rx_buf {
-    uint32_t data[USB_HS_MPS / 4];
+    uint32_t data[MAX_MPS / 4];
     uint32_t count;
 } rx_buf0[1], rx_bufn[32] section_ext_ram;
 
@@ -37,34 +37,13 @@ bool_t usb_is_highspeed(void)
     return is_hs;
 }
 
-static void core_reset(void)
+void core_reset(void)
 {
     do { delay_us(1); } while (!(otg->grstctl & OTG_GRSTCTL_AHBIDL));
     otg->grstctl |= OTG_GRSTCTL_CSRST;
     do { delay_us(1); } while (otg->grstctl & OTG_GRSTCTL_CSRST);
 }
 
-static void hsphyc_init(void)
-{
-    /* Enable the LDO and wait for it to be ready. */
-    hsphyc->ldo |= HSPHYC_LDO_ENABLE;
-    do { delay_us(1); } while (!(hsphyc->ldo & HSPHYC_LDO_STATUS));
-
-    /* This is correct only for HSE = 16Mhz. */
-    hsphyc->pll1 |= HSPHYC_PLL1_SEL(3);
-
-    /* Magic values from the LL driver. We can probably discard them. */
-    hsphyc->tune |= (HSPHYC_TUNE_HSDRVCHKITRIM(7) |
-                     HSPHYC_TUNE_HSDRVRFRED |
-                     HSPHYC_TUNE_HSDRVDCCUR |
-                     HSPHYC_TUNE_INCURRINT |
-                     HSPHYC_TUNE_INCURREN);
-
-    /* Enable the PLL and wait to stabilise. */
-    hsphyc->pll1 |= HSPHYC_PLL1_EN;
-    delay_ms(2);
-}
-
 static void flush_tx_fifo(int num)
 {
     otg->grstctl = OTG_GRSTCTL_TXFFLSH | OTG_GRSTCTL_TXFNUM(num);
@@ -150,65 +129,10 @@ static void fifos_init(void)
     }
 }
 
-void hw_usb_init(void)
+void dwc_otg_init(void)
 {
     int i;
 
-    /* Determine which PHY we use based on hardware submodel ID. */
-    conf_iface = board_config->hs_usb ? IFACE_HS_EMBEDDED : IFACE_FS;
-
-    /*
-     * HAL_PCD_MspInit
-     */
-
-    switch (conf_port) {
-    case PORT_FS:
-        ASSERT(conf_iface == IFACE_FS);
-        gpio_set_af(gpioa, 11, 10);
-        gpio_set_af(gpioa, 12, 10);
-        gpio_configure_pin(gpioa, 11, AFO_pushpull(IOSPD_HIGH));
-        gpio_configure_pin(gpioa, 12, AFO_pushpull(IOSPD_HIGH));
-        rcc->ahb2enr |= RCC_AHB2ENR_OTGFSEN;
-        break;
-    case PORT_HS:
-        ASSERT((conf_iface == IFACE_FS) || (conf_iface == IFACE_HS_EMBEDDED));
-        if (conf_iface == IFACE_FS) {
-            gpio_set_af(gpiob, 14, 12);
-            gpio_set_af(gpiob, 15, 12);
-            gpio_configure_pin(gpiob, 14, AFO_pushpull(IOSPD_HIGH));
-            gpio_configure_pin(gpiob, 15, AFO_pushpull(IOSPD_HIGH));
-            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
-        } else {
-            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
-            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSULPIEN;
-            rcc->apb2enr |= RCC_APB2ENR_OTGPHYCEN;
-        }
-        break;
-    default:
-        ASSERT(0);
-    }
-
-    peripheral_clock_delay();
-
-    /*
-     * USB_CoreInit
-     */
-
-    if (conf_iface == IFACE_FS) {
-        /* Select Full Speed PHY. */
-        otg->gusbcfg |= OTG_GUSBCFG_PHYSEL;
-        /* PHY selection must be followed by a core reset. */
-        core_reset();
-        /* Activate FS transceiver. */
-        otg->gccfg |= OTG_GCCFG_PWRDWN;
-    } else {
-        /* Disable the FS transceiver, enable the HS transceiver. */
-        otg->gccfg &= ~OTG_GCCFG_PWRDWN;
-        otg->gccfg |= OTG_GCCFG_PHYHSEN;
-        hsphyc_init();
-        core_reset();
-    }
-
     /*
      * USB_SetCurrentMode
      */
@@ -275,27 +199,11 @@ void hw_usb_init(void)
     delay_ms(3);
 }
 
-void hw_usb_deinit(void)
+void dwc_otg_deinit(void)
 {
     /* HAL_PCD_Stop, USB_DevDisconnect */
     otgd->dctl |= OTG_DCTL_SDIS;
     peripheral_clock_delay();
-
-    /* HAL_PCD_MspDeInit */
-    switch (conf_port) {
-    case PORT_FS:
-        gpio_configure_pin(gpioa, 11, GPI_floating);
-        gpio_configure_pin(gpioa, 12, GPI_floating);
-        rcc->ahb2enr &= ~RCC_AHB2ENR_OTGFSEN;
-        break;
-    case PORT_HS:
-        gpio_configure_pin(gpiob, 14, GPI_floating);
-        gpio_configure_pin(gpiob, 15, GPI_floating);
-        rcc->ahb1enr &= ~RCC_AHB1ENR_OTGHSEN;
-        break;
-    default:
-        ASSERT(0);
-    }
 }
 
 int ep_rx_ready(uint8_t epnr)

+ 14 - 0
src/usb/hw_dwc_otg.h

@@ -16,7 +16,13 @@
 #define IFACE_HS_EMBEDDED 1
 #define IFACE_HS_ULPI     2
 
+#if MCU == STM32F7
 #define conf_port PORT_HS
+#define MAX_MPS USB_HS_MPS
+#elif MCU == AT32F415
+#define conf_port PORT_FS
+#define MAX_MPS USB_FS_MPS
+#endif
 #define conf_nr_ep 4
 
 /* USB On-The-Go Full Speed interface */
@@ -204,6 +210,8 @@ struct otg_dfifo { /* 1000.. */
 #define OTG_RXSTS_CHNUM(r)   ((r)&0xf)
 
 #define OTG_GCCFG_PHYHSEN    (1u<<23)
+#define OTG_GCCFG_VBUSBSEN   (1u<<19)
+#define OTG_GCCFG_VBUSASEN   (1u<<18)
 #define OTG_GCCFG_VBDEN      (1u<<21)
 #define OTG_GCCFG_PWRDWN     (1u<<16)
 
@@ -368,6 +376,12 @@ static OTG_DOEP otg_doep = (struct otg_doep *)(OTG_BASE + 0xb00);
 static OTG_PCGCCTL otg_pcgcctl = (struct otg_pcgcctl *)(OTG_BASE + 0xe00);
 static OTG_DFIFO otg_dfifo = (struct otg_dfifo *)(OTG_BASE + 0x1000);
 
+/* DWC OTG private interface to MCU-specific layer. */
+extern int conf_iface;
+void core_reset(void);
+void dwc_otg_init(void);
+void dwc_otg_deinit(void);
+
 /*
  * Local variables:
  * mode: C

+ 124 - 0
src/usb/hw_f7.c

@@ -0,0 +1,124 @@
+/*
+ * hw_f7.c
+ * 
+ * STM32F730-specific handling for DWC-OTG USB 2.0 controller.
+ * 
+ * Written & released by Keir Fraser <keir.xen@gmail.com>
+ * 
+ * This is free and unencumbered software released into the public domain.
+ * See the file COPYING for more details, or visit <http://unlicense.org>.
+ */
+
+#include "hw_dwc_otg.h"
+
+static void hsphyc_init(void)
+{
+    /* Enable the LDO and wait for it to be ready. */
+    hsphyc->ldo |= HSPHYC_LDO_ENABLE;
+    do { delay_us(1); } while (!(hsphyc->ldo & HSPHYC_LDO_STATUS));
+
+    /* This is correct only for HSE = 16Mhz. */
+    hsphyc->pll1 |= HSPHYC_PLL1_SEL(3);
+
+    /* Magic values from the LL driver. We can probably discard them. */
+    hsphyc->tune |= (HSPHYC_TUNE_HSDRVCHKITRIM(7) |
+                     HSPHYC_TUNE_HSDRVRFRED |
+                     HSPHYC_TUNE_HSDRVDCCUR |
+                     HSPHYC_TUNE_INCURRINT |
+                     HSPHYC_TUNE_INCURREN);
+
+    /* Enable the PLL and wait to stabilise. */
+    hsphyc->pll1 |= HSPHYC_PLL1_EN;
+    delay_ms(2);
+}
+
+void hw_usb_init(void)
+{
+    /* Determine which PHY we use based on hardware submodel ID. */
+    conf_iface = board_config->hs_usb ? IFACE_HS_EMBEDDED : IFACE_FS;
+
+    /*
+     * HAL_PCD_MspInit
+     */
+
+    switch (conf_port) {
+    case PORT_FS:
+        ASSERT(conf_iface == IFACE_FS);
+        gpio_set_af(gpioa, 11, 10);
+        gpio_set_af(gpioa, 12, 10);
+        gpio_configure_pin(gpioa, 11, AFO_pushpull(IOSPD_HIGH));
+        gpio_configure_pin(gpioa, 12, AFO_pushpull(IOSPD_HIGH));
+        rcc->ahb2enr |= RCC_AHB2ENR_OTGFSEN;
+        break;
+    case PORT_HS:
+        ASSERT((conf_iface == IFACE_FS) || (conf_iface == IFACE_HS_EMBEDDED));
+        if (conf_iface == IFACE_FS) {
+            gpio_set_af(gpiob, 14, 12);
+            gpio_set_af(gpiob, 15, 12);
+            gpio_configure_pin(gpiob, 14, AFO_pushpull(IOSPD_HIGH));
+            gpio_configure_pin(gpiob, 15, AFO_pushpull(IOSPD_HIGH));
+            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
+        } else {
+            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSEN;
+            rcc->ahb1enr |= RCC_AHB1ENR_OTGHSULPIEN;
+            rcc->apb2enr |= RCC_APB2ENR_OTGPHYCEN;
+        }
+        break;
+    default:
+        ASSERT(0);
+    }
+
+    peripheral_clock_delay();
+
+    /*
+     * USB_CoreInit
+     */
+
+    if (conf_iface == IFACE_FS) {
+        /* Select Full Speed PHY. */
+        otg->gusbcfg |= OTG_GUSBCFG_PHYSEL;
+        /* PHY selection must be followed by a core reset. */
+        core_reset();
+        /* Activate FS transceiver. */
+        otg->gccfg |= OTG_GCCFG_PWRDWN;
+    } else {
+        /* Disable the FS transceiver, enable the HS transceiver. */
+        otg->gccfg &= ~OTG_GCCFG_PWRDWN;
+        otg->gccfg |= OTG_GCCFG_PHYHSEN;
+        hsphyc_init();
+        core_reset();
+    }
+
+    dwc_otg_init();
+}
+
+void hw_usb_deinit(void)
+{
+    dwc_otg_deinit();
+
+    /* HAL_PCD_MspDeInit */
+    switch (conf_port) {
+    case PORT_FS:
+        gpio_configure_pin(gpioa, 11, GPI_floating);
+        gpio_configure_pin(gpioa, 12, GPI_floating);
+        rcc->ahb2enr &= ~RCC_AHB2ENR_OTGFSEN;
+        break;
+    case PORT_HS:
+        gpio_configure_pin(gpiob, 14, GPI_floating);
+        gpio_configure_pin(gpiob, 15, GPI_floating);
+        rcc->ahb1enr &= ~RCC_AHB1ENR_OTGHSEN;
+        break;
+    default:
+        ASSERT(0);
+    }
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "Linux"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */