From 97dce282b48a29e7f03cf6b161d9d47bde7bceec Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Wed, 7 Jan 2026 17:29:43 +0000 Subject: [PATCH] echoplayer: add USB support Enable high speed USB for the Echo R1. Includes reasonably complete support for full speed USB on the STM32H743 since that was necessary to debug why it wasn't working at first (which turned out to be a bug in memcpy, not a hardware or driver issue). Change-Id: Ie713195b22ba88c79b9b0d6eb289cb9ccd2763c2 --- bootloader/echoplayer.c | 43 +++++ firmware/SOURCES | 3 + firmware/drivers/usb-designware.c | 1 + firmware/export/config/echor1.h | 16 +- firmware/reggen/stm32h743.regs | 70 ++++++++ firmware/target/arm/stm32/cpu-stm32h743.h | 33 +++- .../target/arm/stm32/echoplayer/gpio-target.h | 2 + .../arm/stm32/echoplayer/system-echoplayer.c | 18 ++- .../arm/stm32/echoplayer/usb-echoplayer.c | 153 ++++++++++++++++++ .../target/arm/stm32/irqhandlers-stm32h743.c | 7 + firmware/target/arm/stm32/stm32h743-config.h | 17 ++ firmware/target/arm/stm32/usb-stm32h7.c | 149 +++++++++++++---- firmware/target/arm/stm32/vectors-stm32h7.S | 18 +-- 13 files changed, 484 insertions(+), 46 deletions(-) create mode 100644 firmware/target/arm/stm32/echoplayer/usb-echoplayer.c create mode 100644 firmware/target/arm/stm32/stm32h743-config.h diff --git a/bootloader/echoplayer.c b/bootloader/echoplayer.c index 982f16ac96..e2491aa0d5 100644 --- a/bootloader/echoplayer.c +++ b/bootloader/echoplayer.c @@ -29,6 +29,9 @@ #include "storage.h" #include "disk.h" #include "file_internal.h" +#include "usb.h" + +static bool is_usb_connected = false; extern void show_logo(void); @@ -55,6 +58,13 @@ static void demo_storage(void) lcd_clear_display(); lcd_putsf(0, y++, "tick %ld", current_tick); + if (is_usb_connected) + { + lcd_puts(0, y++, "storage disabled by USB"); + lcd_update(); + return; + } + struct partinfo pinfo; if (storage_present(IF_MD(0,)) && disk_partinfo(0, &pinfo)) { @@ -75,15 +85,37 @@ static void demo_storage(void) lcd_update(); } +static void demo_usb(void) +{ + static const char *phyname[] = { + [STM32H743_USBOTG_PHY_ULPI_HS] = "ULPI HS", + [STM32H743_USBOTG_PHY_ULPI_FS] = "ULPI FS", + [STM32H743_USBOTG_PHY_INT_FS] = "internal FS", + }; + + int y = 0; + + lcd_clear_display(); + lcd_putsf(0, y++, "tick %ld", current_tick); + lcd_putsf(0, y++, "usb connected %d", (int)is_usb_connected); + + lcd_putsf(0, y++, "instance = USB%d", STM32H743_USBOTG_INSTANCE + 1); + lcd_putsf(0, y++, "phy = %s", phyname[STM32H743_USBOTG_PHY]); + + lcd_update(); +} + static void (*demo_funcs[]) (void) = { demo_rtc, demo_storage, + demo_usb, }; void main(void) { system_init(); kernel_init(); + power_init(); rtc_init(); lcd_init(); @@ -98,6 +130,9 @@ void main(void) filesystem_init(); disk_mount_all(); + usb_init(); + usb_start_monitoring(); + int demo_page = 0; const int num_pages = ARRAYLEN(demo_funcs); @@ -119,6 +154,14 @@ void main(void) demo_page -= 1; break; + case SYS_USB_CONNECTED: + usb_acknowledge(SYS_USB_CONNECTED_ACK); + is_usb_connected = true; + break; + + case SYS_USB_DISCONNECTED: + is_usb_connected = false; + case BUTTON_X: power_off(); break; diff --git a/firmware/SOURCES b/firmware/SOURCES index 5a540ba7ea..294c3f6185 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -2031,8 +2031,10 @@ target/arm/stm32/sdmmc-stm32h7.c target/arm/stm32/spi-stm32h7.c target/arm/stm32/system-stm32h7.c target/arm/stm32/timer-stm32h7.c +#if defined(HAVE_USBSTACK) target/arm/stm32/usb-stm32h7.c #endif +#endif #if defined(ECHO_R1) target/arm/stm32/echoplayer/audiohw-echoplayer.c @@ -2043,6 +2045,7 @@ target/arm/stm32/echoplayer/lcd-echoplayer.c target/arm/stm32/echoplayer/power-echoplayer.c target/arm/stm32/echoplayer/sdmmc-echoplayer.c target/arm/stm32/echoplayer/system-echoplayer.c +target/arm/stm32/echoplayer/usb-echoplayer.c #endif #if (CONFIG_PLATFORM & PLATFORM_ANDROID) diff --git a/firmware/drivers/usb-designware.c b/firmware/drivers/usb-designware.c index 5dc01fc0b2..6647b9d29c 100644 --- a/firmware/drivers/usb-designware.c +++ b/firmware/drivers/usb-designware.c @@ -79,6 +79,7 @@ #elif CONFIG_CPU == STM32H743 # define USB_DW_PHYSADDR(x) x # define NO_UNCACHED_ADDR /* TODO: maybe implement this */ +# define POST_DMA_FLUSH #elif !defined(USB_DW_ARCH_SLAVE) # error "Must define USB_DW_PHYSADDR / USB_DW_UNCACHEDADDR!" #endif diff --git a/firmware/export/config/echor1.h b/firmware/export/config/echor1.h index 8b74a2cade..3316b2b731 100644 --- a/firmware/export/config/echor1.h +++ b/firmware/export/config/echor1.h @@ -1,3 +1,6 @@ +/* Pull in SoC-specific defines */ +#include "stm32h743-config.h" + /* RoLo-related defines */ #define MODEL_NAME "Echo R1" #define MODEL_NUMBER 119 @@ -92,10 +95,17 @@ /* USB support */ #ifndef SIMULATOR #define CONFIG_USBOTG USBOTG_DESIGNWARE -#define USB_DW_TURNAROUND 5 +#define STM32H743_USBOTG_INSTANCE STM32H743_USBOTG_INSTANCE_USB1 +#define STM32H743_USBOTG_PHY STM32H743_USBOTG_PHY_ULPI_HS +#define STM32H743_USBOTG_CLKSEL STM32H743_USBOTG_CLKSEL_PLL1Q #define HAVE_USBSTACK -#define USB_VENDOR_ID 0x1 -#define USB_PRODUCT_ID 0x2 +/* + * Must force device mode because ID pin on PHY is incorrectly + * connected to ground on Rev1 boards. + */ +#define USB_DW_FORCE_DEVICE_MODE +#define USB_VENDOR_ID 0x6666 /* "prototype device" VID in folklore */ +#define USB_PRODUCT_ID 0xEC01 #define USB_DEVBSS_ATTR __attribute__((aligned(32))) #define HAVE_USB_POWER //#define HAVE_USB_CHARGING_ENABLE diff --git a/firmware/reggen/stm32h743.regs b/firmware/reggen/stm32h743.regs index c05828780c..c6c25cdf4a 100644 --- a/firmware/reggen/stm32h743.regs +++ b/firmware/reggen/stm32h743.regs @@ -254,6 +254,19 @@ RCC @ 0x58024400 : block { 0 LSION } + AHB1ENR @ 0xd8 : reg { + 28 USB2OTGHSULPIEN + 27 USB2OTGHSEN + 26 USB1OTGHSULPIEN + 25 USB1OTGHSEN + 17 ETH1RXEN + 16 ETH1TXEN + 15 ETH1MACEN + 05 ADC12EN + 01 DMA2EN + 00 DMA1EN + } + AHB3RSTR @ 0x7c : reg { 16 SDMMC1RST 14 QSPIRST @@ -418,6 +431,8 @@ SYSCFG @ 0x58000400 : block { PWRCFG @ 0x2c : reg { 0 ODEN } + + EXTICR @ 0x08 [4; 0x04] : reg } FMC @ 0x52004000 : block { @@ -1092,3 +1107,58 @@ block SDMMC { SDMMC1 @ 0x52007000 : SDMMC SDMMC2 @ 0x48022400 : SDMMC + +// Clock recovery system +CRS @ 0x40008400 : block { + CR @ 0x00 : reg { + 13 08 TRIM + -- 07 SWSYNC + -- 06 AUTOTRIMEN + -- 05 CEN + -- 03 ESYNCIE + -- 02 ERRIE + -- 01 SYNCWARNIE + -- 00 SYNCOKIE + } + + CFGR @ 0x04 : reg { + -- 31 SYNCPOL : { 0 = RISING_EDGE; 1 = FALLING_EDGE } + 29 28 SYNCSRC : { 0 = USB2_SOF; 1 = LSE; 2 = USB1_SOF } + 26 24 SYNCDIV + 23 16 FELIM + 15 00 RELOAD + } + + ISR @ 0x08 : reg { + 31 16 FECAP + -- 15 FEDIR + -- 10 TRIMOVF + -- 09 SYNCMISS + -- 08 SYNCERR + -- 03 ESYNCF + -- 02 ERRF + -- 01 SYNCWARNF + -- 00 SYNCOKF + } + + ICR @ 0x0c : reg { + 3 ESYNCC + 2 ERRC + 1 SYNCWARNC + 0 SYNCOKC + } +} + +// Extended interrupt and event controller +EXTI @ 0x58000000 : block { + RTSR @ 0x00 [3; 0x20] : reg + FTSR @ 0x04 [3; 0x20] : reg + SWIER @ 0x08 [3; 0x20] : reg + D3PMR @ 0x0c [3; 0x20] : reg + D3PCRL @ 0x10 [3; 0x20] : reg + D3PCRH @ 0x14 [3; 0x20] : reg + + CPUIMR @ 0x80 [3; 0x10] : reg + CPUEMR @ 0x84 [3; 0x10] : reg + CPUPR @ 0x88 [3; 0x10] : reg +} diff --git a/firmware/target/arm/stm32/cpu-stm32h743.h b/firmware/target/arm/stm32/cpu-stm32h743.h index fc10fdb954..c0a789708d 100644 --- a/firmware/target/arm/stm32/cpu-stm32h743.h +++ b/firmware/target/arm/stm32/cpu-stm32h743.h @@ -21,6 +21,8 @@ #ifndef __CPU_STM32H743_H__ #define __CPU_STM32H743_H__ +#include "config.h" + #define CACHE_SIZE (16 * 1024) #define CACHEALIGN_BITS 5 @@ -34,8 +36,35 @@ #define STM32_CSI_FREQ 4000000 #define STM32_HSI48_FREQ 48000000 -#define OTGBASE 0x40080000 -#define USB_NUM_ENDPOINTS 9 +#if defined(HAVE_USBSTACK) +# if !defined(STM32H743_USBOTG_INSTANCE) +# error "STM32H743_USBOTG_INSTANCE is undefined!" +# endif +# if !defined(STM32H743_USBOTG_PHY) +# error "STM32H743_USBOTG_PHY is undefined!" +# endif +# if !defined(STM32H743_USBOTG_CLKSEL) +# error "STM32H743_USBOTG_CLKSEL is undefined!" +# endif +# if (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1) +# define OTGBASE 0x40040000 +# elif (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB2) +# define OTGBASE 0x40080000 +# endif +# if (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_FS) +# define USB_DW_DCFG_SPEED 1 +# elif (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_INT_FS) +# define USB_DW_DCFG_SPEED 3 +# endif +# define USB_DW_TURNAROUND 5 +/* + * The hardware supports up to 9 endpoints, but since FIFO RAM + * is limited, we can't support more than 5 IN EPs with a max + * packet size of 512 bytes (usb-designware allocates the same + * amount of RAM to each IN endpoint). + */ +# define USB_NUM_ENDPOINTS 6 +#endif #define STM32_ITCM_BASE 0x00000000 #define STM32_ITCM_SIZE (64 * 1024) diff --git a/firmware/target/arm/stm32/echoplayer/gpio-target.h b/firmware/target/arm/stm32/echoplayer/gpio-target.h index 122bef0818..2e92f249c8 100644 --- a/firmware/target/arm/stm32/echoplayer/gpio-target.h +++ b/firmware/target/arm/stm32/echoplayer/gpio-target.h @@ -53,4 +53,6 @@ #define GPIO_LCD_RESET GPIO_PD(11) #define GPIO_BACKLIGHT GPIO_PD(13) +#define GPIO_USB_VBUS GPIO_PA(9) + #endif /* __ECHOPLAYER_GPIO_TARGET_H__ */ diff --git a/firmware/target/arm/stm32/echoplayer/system-echoplayer.c b/firmware/target/arm/stm32/echoplayer/system-echoplayer.c index 97dbb9644a..c624569241 100644 --- a/firmware/target/arm/stm32/echoplayer/system-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/system-echoplayer.c @@ -28,9 +28,6 @@ #define F_INPUT_PD GPIOF_INPUT(GPIO_PULL_DOWN) #define F_OUT_LS(x) GPIOF_OUTPUT(x, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_LOW, GPIO_PULL_DISABLED) #define F_SDRAM GPIOF_FUNCTION(12, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED) -#define F_OTG_FS GPIOF_ANALOG() -#define F_ULPI GPIOF_FUNCTION(10, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED) -#define F_MCO1 GPIOF_FUNCTION(0, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED) #define F_I2C1 GPIOF_FUNCTION(4, GPIO_TYPE_OPEN_DRAIN, GPIO_SPEED_LOW, GPIO_PULL_DISABLED) #define F_SDMMC1 GPIOF_FUNCTION(12, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_MEDIUM, GPIO_PULL_UP) #define F_SDMMC1CK GPIOF_FUNCTION(12, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_MEDIUM, GPIO_PULL_DISABLED) @@ -41,6 +38,16 @@ #define F_LPTIM4_OUT GPIOF_FUNCTION(3, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_LOW, GPIO_PULL_DISABLED) #define F_LPTIM1_OUT GPIOF_FUNCTION(1, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED) +#if STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1 +# define F_OTG_FS GPIOF_ANALOG() +# define F_ULPI GPIOF_FUNCTION(10, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED) +# define F_MCO1 GPIOF_FUNCTION(0, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED) +#else +# define F_OTG_FS GPIOF_FUNCTION(10, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED) +# define F_ULPI GPIOF_ANALOG() +# define F_MCO1 GPIOF_ANALOG() +#endif + static const struct gpio_setting gpios[] = { STM_DEFGPIO(GPIO_BUTTON_A, F_INPUT_PU), STM_DEFGPIO(GPIO_BUTTON_B, F_INPUT_PU), @@ -69,6 +76,7 @@ static const struct gpio_setting gpios[] = { STM_DEFGPIO(GPIO_LINEOUT_DETECT, F_INPUT), /* external pullup */ STM_DEFGPIO(GPIO_LCD_RESET, F_OUT_LS(0)), /* active low */ STM_DEFGPIO(GPIO_BACKLIGHT, F_LPTIM1_OUT), + STM_DEFGPIO(GPIO_USB_VBUS, F_INPUT), /* active high */ }; /* TODO - replace hex constants - there are probably mistakes here */ @@ -82,13 +90,13 @@ static const struct pingroup_setting pingroups[] = { STM_DEFPINS(GPIO_F, 0xf83f, F_SDRAM), STM_DEFPINS(GPIO_G, 0x8137, F_SDRAM), /* OTG_FS */ - STM_DEFPINS(GPIO_A, 0x1a00, F_OTG_FS), + STM_DEFPINS(GPIO_A, 0x1800, F_OTG_FS), /* OTG_HS - ULPI */ STM_DEFPINS(GPIO_A, 0x0028, F_ULPI), STM_DEFPINS(GPIO_B, 0x3c23, F_ULPI), STM_DEFPINS(GPIO_C, 0x0001, F_ULPI), STM_DEFPINS(GPIO_H, 0x0010, F_ULPI), - STM_DEFPINS(GPIO_I, 0x0100, F_ULPI), + STM_DEFPINS(GPIO_I, 0x0800, F_ULPI), /* MCO1 for USB PHY */ STM_DEFPINS(GPIO_A, 0x0100, F_MCO1), /* I2C1 */ diff --git a/firmware/target/arm/stm32/echoplayer/usb-echoplayer.c b/firmware/target/arm/stm32/echoplayer/usb-echoplayer.c new file mode 100644 index 0000000000..f16cd8dc7c --- /dev/null +++ b/firmware/target/arm/stm32/echoplayer/usb-echoplayer.c @@ -0,0 +1,153 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "system.h" +#include "usb.h" +#include "usb_core.h" +#include "usb_drv.h" +#include "usb-designware.h" +#include "gpio-stm32h7.h" +#include "nvic-arm.h" +#include "power-echoplayer.h" +#include "regs/stm32h743/rcc.h" +#include "regs/stm32h743/pwr.h" +#include "regs/stm32h743/crs.h" +#include "regs/stm32h743/syscfg.h" +#include "regs/stm32h743/exti.h" + +/* + * Needed because USB core calls usb_enable(false) on boot, + * so we need to keep track if ULPI was enabled or not. + */ +static bool echoplayer_ulpi_enabled = false; + +static void echoplayer_enable_ulpi_phy(void) +{ + if (!echoplayer_ulpi_enabled) + { + /* Enable MCO1 output for PHY reference clock */ + reg_writef(RCC_CFGR, MCO1_V(HSE), MCO1PRE(1)); + + /* Enable power supplies */ + echoplayer_enable_1v8_regulator(true); + gpio_set_level(GPIO_USBPHY_POWER_EN, 0); + + /* Wait for stable clocks/power */ + sleep(1); + + /* Release PHY from reset */ + gpio_set_level(GPIO_USBPHY_RESET, 1); + + echoplayer_ulpi_enabled = true; + } +} + +static void echoplayer_disable_ulpi_phy(void) +{ + if (echoplayer_ulpi_enabled) + { + /* Put PHY into reset */ + gpio_set_level(GPIO_USBPHY_RESET, 0); + + /* Remove power */ + gpio_set_level(GPIO_USBPHY_POWER_EN, 1); + echoplayer_enable_1v8_regulator(false); + + /* Disable PHY reference clock */ + reg_writef(RCC_CFGR, MCO1PRE(0)); + + echoplayer_ulpi_enabled = false; + } +} + +static void echoplayer_usb_enable(void) +{ + if ((STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_HS) || + (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_FS)) + { + echoplayer_enable_ulpi_phy(); + } + + usb_core_init(); +} + +static void echoplayer_usb_disable(void) +{ + usb_core_exit(); + + if ((STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_HS) || + (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_FS)) + { + echoplayer_disable_ulpi_phy(); + } +} + +void usb_init_device(void) +{ + if (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_INT_FS) + { + /* Required if internal PHY is used */ + reg_writef(PWR_CR3, USB33DEN(1)); + } + + /* + * This is the only place that needs a GPIO interrupt so + * just setup EXTI directly. + */ + const int gpio_port = GPION_PORT(GPIO_USB_VBUS); + const int gpio_pin = GPION_PIN(GPIO_USB_VBUS); + const int syscfg_idx = gpio_pin / 4; + const int syscfg_pos = gpio_pin % 4; + + /* Set GPIO port in syscfg's exti config registers */ + reg_writef(RCC_APB4ENR, SYSCFGEN(1)); + reg_var(SYSCFG_EXTICR(syscfg_idx)) = gpio_port << syscfg_pos; + reg_writef(RCC_APB4ENR, SYSCFGEN(0)); + + /* Enable rising & falling edge trigger for pin */ + reg_var(EXTI_RTSR(0)) |= 1u << gpio_pin; + reg_var(EXTI_FTSR(0)) |= 1u << gpio_pin; + reg_var(EXTI_CPUIMR(0)) |= 1u << gpio_pin; + + nvic_enable_irq(NVIC_IRQN_EXTI9_5); +} + +void usb_enable(bool on) +{ + if (on) + echoplayer_usb_enable(); + else + echoplayer_usb_disable(); +} + +int usb_detect(void) +{ + if (gpio_get_level(GPIO_USB_VBUS)) + return USB_INSERTED; + + return USB_EXTRACTED; +} + +void exti9_5_irq_handler(void) +{ + reg_var(EXTI_CPUPR(0)) = 1u << GPION_PIN(GPIO_USB_VBUS); + + usb_status_event(usb_detect()); +} diff --git a/firmware/target/arm/stm32/irqhandlers-stm32h743.c b/firmware/target/arm/stm32/irqhandlers-stm32h743.c index e27cdc1b49..8399150e6e 100644 --- a/firmware/target/arm/stm32/irqhandlers-stm32h743.c +++ b/firmware/target/arm/stm32/irqhandlers-stm32h743.c @@ -36,3 +36,10 @@ void spi3_irq_handler(void) ATTR_IRQ_HANDLER; void spi4_irq_handler(void) ATTR_IRQ_HANDLER; void spi5_irq_handler(void) ATTR_IRQ_HANDLER; void spi6_irq_handler(void) ATTR_IRQ_HANDLER; +void exti0_irq_handler(void) ATTR_IRQ_HANDLER; +void exti1_irq_handler(void) ATTR_IRQ_HANDLER; +void exti2_irq_handler(void) ATTR_IRQ_HANDLER; +void exti3_irq_handler(void) ATTR_IRQ_HANDLER; +void exti4_irq_handler(void) ATTR_IRQ_HANDLER; +void exti9_5_irq_handler(void) ATTR_IRQ_HANDLER; +void exti15_10_irq_handler(void) ATTR_IRQ_HANDLER; diff --git a/firmware/target/arm/stm32/stm32h743-config.h b/firmware/target/arm/stm32/stm32h743-config.h new file mode 100644 index 0000000000..6ba760b28c --- /dev/null +++ b/firmware/target/arm/stm32/stm32h743-config.h @@ -0,0 +1,17 @@ +/* + * To be included by config/TARGET.h + */ + +/* STM32H743_USBOTG_INSTANCE */ +#define STM32H743_USBOTG_INSTANCE_USB1 0 /* OTG_HS1 */ +#define STM32H743_USBOTG_INSTANCE_USB2 1 /* OTG_HS2 */ + +/* STM32H743_USBOTG_PHY */ +#define STM32H743_USBOTG_PHY_ULPI_HS 0 /* External ULPI PHY in HS mode */ +#define STM32H743_USBOTG_PHY_ULPI_FS 1 /* External ULPI PHY in FS mode */ +#define STM32H743_USBOTG_PHY_INT_FS 2 /* Internal PHY in FS mode */ + +/* STM32H743_USBOTG_CLKSEL */ +#define STM32H743_USBOTG_CLKSEL_PLL1Q 1 /* PLL1Q output */ +#define STM32H743_USBOTG_CLKSEL_PLL3Q 2 /* PLL3Q output */ +#define STM32H743_USBOTG_CLKSEL_HSI48_CRS 3 /* HSI48 trimmed by CRS */ diff --git a/firmware/target/arm/stm32/usb-stm32h7.c b/firmware/target/arm/stm32/usb-stm32h7.c index 02291a4bab..94611c3940 100644 --- a/firmware/target/arm/stm32/usb-stm32h7.c +++ b/firmware/target/arm/stm32/usb-stm32h7.c @@ -23,62 +23,157 @@ #include "usb_core.h" #include "usb_drv.h" #include "usb-designware.h" +#include "nvic-arm.h" +#include "regs/stm32h743/pwr.h" +#include "regs/stm32h743/rcc.h" +#include "regs/stm32h743/crs.h" + +#if STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1 +# define IRQN_USB NVIC_IRQN_OTG_HS +#else +# define IRQN_USB NVIC_IRQN_OTG_FS +#endif + +/* Total size of FIFO RAM */ +#define FIFO_RAMSZ 1024 + +/* + * Must be equal to N * (max packet size / 4) for IN endpoints. + * Due to limited FIFO RAM set N=1; N > 1 increases performance. + * NPTX is only for EP0 so its max packet size is 64 bytes. + */ +#define NPTX_FIFOSZ 16 +#define PTX_FIFOSZ 128 + +#define RX_FIFOSZ \ + (FIFO_RAMSZ - NPTX_FIFOSZ - (PTX_FIFOSZ * (USB_NUM_ENDPOINTS - 1))) + +/* + * See RM0433 -- 57.11.3 FIFO RAM allocation + * We require enough for 2x 512-byte packets / 1x 1024-byte packet + */ +_Static_assert(RX_FIFOSZ >= 14 + 2*(512/4 + 1) + 2*USB_NUM_ENDPOINTS, + "RX FIFO size is too small"); const struct usb_dw_config usb_dw_config = { + .nptx_fifosz = NPTX_FIFOSZ, + .ptx_fifosz = PTX_FIFOSZ, + .rx_fifosz = RX_FIFOSZ, + +#if ((STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_HS) || \ + (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_FS)) .phytype = DWC_PHYTYPE_ULPI_SDR, - /* - * Available FIFO memory: 1024 words (TODO: check this) - * Number of endpoints: 9 - * Max packet size: 512 bytes - */ - .rx_fifosz = 224, /* shared RxFIFO */ - .nptx_fifosz = 32, /* only used for EP0 IN */ - .ptx_fifosz = 192, /* room for 4 IN EPs */ +#else + .phytype = PHSEL, +#endif #ifndef USB_DW_ARCH_SLAVE .ahb_burst_len = HBSTLEN_INCR8, - /* Disable Rx FIFO thresholding. It appears to cause problems, - * apparently a known issue -- Synopsys recommends disabling it - * because it can cause issues during certain error conditions. - */ .ahb_threshold = 0, #else .disable_double_buffering = false, #endif }; -void usb_enable(bool on) -{ - if (on) - usb_core_init(); - else - usb_core_exit(); -} +_Static_assert( + (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1) || + (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_INT_FS), + "ULPI PHY not available on OTG_HS2"); -void usb_init_device(void) -{ - /* TODO - this is highly target specific */ -} +_Static_assert( + STM32H743_USBOTG_CLKSEL_PLL1Q == BV_RCC_D2CCIP2R_USBSEL_PLL1Q && + STM32H743_USBOTG_CLKSEL_PLL3Q == BV_RCC_D2CCIP2R_USBSEL_PLL3Q && + STM32H743_USBOTG_CLKSEL_HSI48_CRS == BV_RCC_D2CCIP2R_USBSEL_HSI48, + "Mismatch between CLKSEL enum and USBSEL register"); -int usb_detect(void) -{ - return USB_EXTRACTED; -} +/* + * The internal PHY seems to need some modifications in usb-designware + * to make it work but it's not clear what's wrong. At minimum it seems + * necessary to set PWRDWN & NOVBUSSENS bits on the GCCFG register in + * addition to setting DSPEED in DCFG, but that is not enough to get it + * to enumerate. ULPI in FS mode works however. + */ +_Static_assert(STM32H743_USBOTG_PHY != STM32H743_USBOTG_PHY_INT_FS, + "internal FS PHY not supported at the moment"); void usb_dw_target_enable_clocks(void) { + if (STM32H743_USBOTG_CLKSEL == STM32H743_USBOTG_CLKSEL_HSI48_CRS) + { + /* Enable HSI48 */ + reg_writef(RCC_CR, HSI48ON(1)); + while (reg_readf(RCC_CR, HSI48RDY) == 0); + + /* Enable CRS */ + reg_writef(RCC_APB1HENR, CRSEN(1)); + + /* Select USBx SOF as input to CRS */ + if (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1) + reg_writef(CRS_CFGR, SYNCSRC_V(USB1_SOF)); + else if (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB2) + reg_writef(CRS_CFGR, SYNCSRC_V(USB2_SOF)); + + /* + * Enable CRS, the reset value of its registers contains + * suitable settings for syncing with the USB FS SOF and + * we assume the CRS configuration won't be modified. + */ + reg_writef(CRS_CR, AUTOTRIMEN(1), CEN(1)); + } + + /* Set appropriate input clock */ + reg_writef(RCC_D2CCIP2R, USBSEL(STM32H743_USBOTG_CLKSEL)); + + /* Enable peripheral clock */ + if (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1) + reg_writef(RCC_AHB1ENR, USB1OTGHSEN(1)); + else if (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB2) + reg_writef(RCC_AHB1ENR, USB2OTGHSEN(1)); + + /* Enable ULPI clock for USB1 if high speed mode is enabled */ + if ((STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_HS) || + (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_FS)) + { + reg_writef(RCC_AHB1ENR, USB1OTGHSULPIEN(1)); + } } void usb_dw_target_disable_clocks(void) { + /* Disable ULPI clock */ + if ((STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_HS) || + (STM32H743_USBOTG_PHY == STM32H743_USBOTG_PHY_ULPI_FS)) + { + reg_writef(RCC_AHB1ENR, USB1OTGHSULPIEN(0)); + } + + /* Disable peripheral clock */ + if (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1) + reg_writef(RCC_AHB1ENR, USB1OTGHSEN(0)); + else if (STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB2) + reg_writef(RCC_AHB1ENR, USB2OTGHSEN(0)); + + if (STM32H743_USBOTG_CLKSEL == STM32H743_USBOTG_CLKSEL_HSI48_CRS) + { + /* Disable CRS */ + reg_writef(CRS_CR, CEN(0)); + reg_writef(RCC_APB1HENR, CRSEN(0)); + + /* Disable HSI48 */ + reg_writef(RCC_CR, HSI48ON(0)); + } } void usb_dw_target_enable_irq(void) { + arm_dsb(); + nvic_enable_irq(IRQN_USB); } void usb_dw_target_disable_irq(void) { + nvic_disable_irq(IRQN_USB); + arm_dsb(); } void usb_dw_target_clear_irq(void) diff --git a/firmware/target/arm/stm32/vectors-stm32h7.S b/firmware/target/arm/stm32/vectors-stm32h7.S index 782e4d9162..d2fb3e2f06 100644 --- a/firmware/target/arm/stm32/vectors-stm32h7.S +++ b/firmware/target/arm/stm32/vectors-stm32h7.S @@ -33,11 +33,11 @@ __vectors_platform: .word UIE /* [ 3] */ .word UIE /* [ 4] */ .word UIE /* [ 5] */ - .word UIE /* [ 6] */ - .word UIE /* [ 7] */ - .word UIE /* [ 8] */ - .word UIE /* [ 9] */ - .word UIE /* [ 10] */ + .word exti0_irq_handler /* [ 6] EXTI0 */ + .word exti1_irq_handler /* [ 7] EXTI1 */ + .word exti2_irq_handler /* [ 8] EXTI2 */ + .word exti3_irq_handler /* [ 9] EXTI3 */ + .word exti4_irq_handler /* [ 10] EXTI4 */ .word dma_irq_handler /* [ 11] DMA1 Stream0 */ .word dma_irq_handler /* [ 12] DMA1 Stream1 */ .word dma_irq_handler /* [ 13] DMA1 Stream2 */ @@ -50,7 +50,7 @@ __vectors_platform: .word UIE /* [ 20] */ .word UIE /* [ 21] */ .word UIE /* [ 22] */ - .word UIE /* [ 23] */ + .word exti9_5_irq_handler /* [ 23] EXTI5 to EXTI9 */ .word UIE /* [ 24] */ .word UIE /* [ 25] */ .word UIE /* [ 26] */ @@ -67,7 +67,7 @@ __vectors_platform: .word UIE /* [ 37] */ .word UIE /* [ 38] */ .word UIE /* [ 39] */ - .word UIE /* [ 40] */ + .word exti15_10_irq_handler /* [ 40] EXTI10 to EXTI15 */ .word UIE /* [ 41] */ .word UIE /* [ 42] */ .word UIE /* [ 43] */ @@ -104,7 +104,7 @@ __vectors_platform: .word UIE /* [ 74] */ .word UIE /* [ 75] */ .word UIE /* [ 76] */ - .word otg_hs_irq_handler /* [ 77] OTG HS */ + .word INT_USB_FUNC /* [ 77] OTG HS */ .word UIE /* [ 78] */ .word UIE /* [ 79] */ .word UIE /* [ 80] */ @@ -128,7 +128,7 @@ __vectors_platform: .word UIE /* [ 98] */ .word UIE /* [ 99] */ .word UIE /* [100] */ - .word UIE /* [101] */ + .word INT_USB_FUNC /* [101] OTG FS */ .word dma_irq_handler /* [102] DMAMUX1 overrun */ .word UIE /* [103] */ .word UIE /* [104] */