From 689aa55e010d00113a1ea2f5683ea990c2323ba3 Mon Sep 17 00:00:00 2001 From: Kyle Wilson Date: Tue, 21 Jan 2025 11:35:40 -0600 Subject: [PATCH] Initial STM32H5 USB Commit Progress in USB driver Nothing is working yet. But just getting this in here so it is saved. Minor changes with USB power enabled Serial over USB is working now. Removed stm32_usbdev.c, added stm32_usbfs.c. Still needs review before pull-request. Rewrote VDDUSB valid check to use register definitions. Wait for USB33RDY before moving on. Also style updates. Renamed stm32_usbdev files to stm32_usbfs. Renamed STM32H5 configuration options from STM32H5_USB to STM32H5_USBFS. Properly named REGDEBUG defines. Added STM32H5_USB_REGDEBUG to Kconfig and updated stm32_usbfs.c to use it. TRACE functionality still needs work. Updated REGDEBUG for 32-bit register accesses. More style updates. Added usbnsh defconfig to nucleo-h563zi Update to allow 16 buffers (max for 8 endpoints). Renamed lpinterrupt and lptransfer. Changed board.h to use SYNCSRC_USB only if USB is enabled. Buffer size is still set to max packet size of 64 bytes, because we are using a full-speed peripheral. It is unclear to me whether having a buffer larger than the max packet size has any benefit. Double buffering is a way to increase throughput, though that still needs to be implemented. Adapted USBFS Buffer table for 32-bit accesses. Style updates Build stm32_hsi48.c based on CONFIG_STM32H5_HAVE_HSI48. Change LPMODE CNTR bit to SUSPRDY. --- arch/arm/src/stm32h5/Kconfig | 30 + arch/arm/src/stm32h5/Make.defs | 8 + arch/arm/src/stm32h5/hardware/stm32_usbfs.h | 228 + .../src/stm32h5/hardware/stm32h56xxx_pinmap.h | 6 + arch/arm/src/stm32h5/stm32.h | 1 + arch/arm/src/stm32h5/stm32_hsi48.c | 2 +- arch/arm/src/stm32h5/stm32_usbfs.c | 3951 +++++++++++++++++ arch/arm/src/stm32h5/stm32_usbfs.h | 92 + arch/arm/src/stm32h5/stm32h5xx_rcc.c | 12 +- .../nucleo-h563zi/configs/usbnsh/defconfig | 55 + .../arm/stm32h5/nucleo-h563zi/include/board.h | 5 + 11 files changed, 4387 insertions(+), 3 deletions(-) create mode 100644 arch/arm/src/stm32h5/hardware/stm32_usbfs.h create mode 100644 arch/arm/src/stm32h5/stm32_usbfs.c create mode 100644 arch/arm/src/stm32h5/stm32_usbfs.h create mode 100644 boards/arm/stm32h5/nucleo-h563zi/configs/usbnsh/defconfig diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig index cfebc9cb501..79514bca10c 100644 --- a/arch/arm/src/stm32h5/Kconfig +++ b/arch/arm/src/stm32h5/Kconfig @@ -53,6 +53,8 @@ config STM32H5_STM32H56XXX select STM32H5_HAVE_SPI4 select STM32H5_HAVE_SPI5 select STM32H5_HAVE_SPI6 + select STM32H5_HAVE_USBFS + select STM32H5_HAVE_HSI48 config STM32H5_STM32H563XX # STM32H552 and STM32H562 devices documented in RM0439 @@ -220,6 +222,10 @@ config STM32H5_HAVE_FDCAN2 bool default n +config STM32H5_HAVE_HSI48 + bool + default n + config STM32H5_HAVE_LPUART1 bool default n @@ -280,6 +286,10 @@ config STM32H5_HAVE_UART12 bool default n +config STM32H5_HAVE_USBFS + bool + default n + # These "hidden" settings are the OR of individual peripheral selections # indicating that the general capability is required. @@ -555,6 +565,12 @@ config STM32H5_TIM17 endmenu # STM32H5 Timer Selection +config STM32H5_USBFS + bool "USB Device" + default n + depends on STM32H5_HAVE_USBFS + select USBDEV + endmenu config STM32H5_FLASH_PREFETCH @@ -3770,6 +3786,20 @@ endmenu #STM32 TIMx Outputs Configuration endmenu # Timer Configuration +comment "USB Device Configuration" + +menu "USB Full Speed Debug Configuration" + depends on STM32H5_USBFS + +config STM32H5_USBFS_REGDEBUG + bool "Register-Level Debug" + default n + depends on STM32H5_USBFS && DEBUG_USB_INFO + ---help--- + Enable very low-level register access debug. + +endmenu + config STM32H5_SERIALDRIVER bool diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs index d45240a0068..1d5586a9934 100644 --- a/arch/arm/src/stm32h5/Make.defs +++ b/arch/arm/src/stm32h5/Make.defs @@ -68,6 +68,14 @@ ifeq ($(CONFIG_STM32H5_TIM),y) CHIP_CSRCS += stm32_tim.c endif +ifeq ($(CONFIG_STM32H5_HAVE_HSI48),y) +CHIP_CSRCS += stm32_hsi48.c +endif + +ifeq ($(CONFIG_STM32H5_USBFS),y) +CHIP_CSRCS += stm32_usbfs.c +endif + # Required chip type specific files ifeq ($(CONFIG_STM32H5_STM32H5XXXX),y) diff --git a/arch/arm/src/stm32h5/hardware/stm32_usbfs.h b/arch/arm/src/stm32h5/hardware/stm32_usbfs.h new file mode 100644 index 00000000000..24bd3a13bcb --- /dev/null +++ b/arch/arm/src/stm32h5/hardware/stm32_usbfs.h @@ -0,0 +1,228 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/hardware/stm32_usbfs.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_USBFS_H +#define __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_USBFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#ifdef CONFIG_STM32H5_HAVE_USBFS + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *********************************************************/ + +/* Endpoint Registers */ + +#define STM32_USB_EPR_OFFSET(n) ((n) << 2) /* USB endpoint n register (32-bits) */ + +#define STM32_USB_EP0R_OFFSET 0x0000 /* USB endpoint 0 register (32-bits) */ +#define STM32_USB_EP1R_OFFSET 0x0004 /* USB endpoint 1 register (32-bits) */ +#define STM32_USB_EP2R_OFFSET 0x0008 /* USB endpoint 2 register (32-bits) */ +#define STM32_USB_EP3R_OFFSET 0x000c /* USB endpoint 3 register (32-bits) */ +#define STM32_USB_EP4R_OFFSET 0x0010 /* USB endpoint 4 register (32-bits) */ +#define STM32_USB_EP5R_OFFSET 0x0014 /* USB endpoint 5 register (32-bits) */ +#define STM32_USB_EP6R_OFFSET 0x0018 /* USB endpoint 6 register (32-bits) */ +#define STM32_USB_EP7R_OFFSET 0x001c /* USB endpoint 7 register (32-bits) */ + +/* Common Registers */ + +#define STM32_USB_CNTR_OFFSET 0x0040 /* USB control register (32-bits) */ +#define STM32_USB_ISTR_OFFSET 0x0044 /* USB interrupt status register (32-bits) */ +#define STM32_USB_FNR_OFFSET 0x0048 /* USB frame number register (32-bits) */ +#define STM32_USB_DADDR_OFFSET 0x004c /* USB device address (32-bits) */ +#define STM32_USB_LPMCSR_OFFSET 0x0054 /* LPM control and status register (32-bits) */ +#define STM32_USB_BCDR_OFFSET 0x0058 /* Battery charging detector (32-bits) */ + +/* Buffer Descriptor Table (Relatative to BTABLE address) */ + +#define STM32_USB_TX_WOFFSET (0) /* Transmission buffer n (32-bits) */ +#define STM32_USB_RX_WOFFSET (4) /* Reception buffer address n (32-bits) */ + +#define STM32_USB_BTABLE_RADDR(ep,o) ((((uint32_t)STM32_USB_FS_RAM_BASE + ((ep) << 3)) + (o))) + +/* Register Addresses *******************************************************/ + +/* Endpoint Registers */ + +#define STM32_USB_EPR(n) (STM32_USB_FS_BASE + STM32_USB_EPR_OFFSET(n)) +#define STM32_USB_EP0R (STM32_USB_FS_BASE + STM32_USB_EP0R_OFFSET) +#define STM32_USB_EP1R (STM32_USB_FS_BASE + STM32_USB_EP1R_OFFSET) +#define STM32_USB_EP2R (STM32_USB_FS_BASE + STM32_USB_EP2R_OFFSET) +#define STM32_USB_EP3R (STM32_USB_FS_BASE + STM32_USB_EP3R_OFFSET) +#define STM32_USB_EP4R (STM32_USB_FS_BASE + STM32_USB_EP4R_OFFSET) +#define STM32_USB_EP5R (STM32_USB_FS_BASE + STM32_USB_EP5R_OFFSET) +#define STM32_USB_EP6R (STM32_USB_FS_BASE + STM32_USB_EP6R_OFFSET) +#define STM32_USB_EP7R (STM32_USB_FS_BASE + STM32_USB_EP7R_OFFSET) + +/* Common Registers */ + +#define STM32_USB_CNTR (STM32_USB_FS_BASE + STM32_USB_CNTR_OFFSET) +#define STM32_USB_ISTR (STM32_USB_FS_BASE + STM32_USB_ISTR_OFFSET) +#define STM32_USB_FNR (STM32_USB_FS_BASE + STM32_USB_FNR_OFFSET) +#define STM32_USB_DADDR (STM32_USB_FS_BASE + STM32_USB_DADDR_OFFSET) +#define STM32_USB_LPMCSR (STM32_USB_FS_BASE + STM32_USB_LPMCSR_OFFSET) +#define STM32_USB_BCDR (STM32_USB_FS_BASE + STM32_USB_BCDR_OFFSET) + +/* Buffer Descriptor Table (Relative to BTABLE address) */ + +#define STM32_USB_BTABLE_ADDR(ep,o) (STM32_USB_BTABLE_RADDR(ep,o)) +#define STM32_USB_TX(ep) STM32_USB_BTABLE_RADDR(ep,STM32_USB_TX_WOFFSET) +#define STM32_USB_RX(ep) STM32_USB_BTABLE_RADDR(ep,STM32_USB_RX_WOFFSET) + +/* Register Bitfield Definitions ********************************************/ + +/* USB endpoint register */ + +#define USB_EPR_EA_SHIFT (0) /* Bits 3:0 [3:0]: Endpoint Address */ +#define USB_EPR_EA_MASK (0X0f << USB_EPR_EA_SHIFT) +#define USB_EPR_STATTX_SHIFT (4) /* Bits 5-4: Status bits, for transmission transfers */ +#define USB_EPR_STATTX_MASK (3 << USB_EPR_STATTX_SHIFT) +# define USB_EPR_STATTX_DIS (0 << USB_EPR_STATTX_SHIFT) /* EndPoint TX DISabled */ +# define USB_EPR_STATTX_STALL (1 << USB_EPR_STATTX_SHIFT) /* EndPoint TX STALLed */ +# define USB_EPR_STATTX_NAK (2 << USB_EPR_STATTX_SHIFT) /* EndPoint TX NAKed */ +# define USB_EPR_STATTX_VALID (3 << USB_EPR_STATTX_SHIFT) /* EndPoint TX VALID */ +# define USB_EPR_STATTX_DTOG1 (1 << USB_EPR_STATTX_SHIFT) /* EndPoint TX Data Toggle bit1 */ +# define USB_EPR_STATTX_DTOG2 (2 << USB_EPR_STATTX_SHIFT) /* EndPoint TX Data Toggle bit2 */ + +#define USB_EPR_DTOG_TX (1 << 6) /* Bit 6: Data Toggle, for transmission transfers */ +#define USB_EPR_CTR_TX (1 << 7) /* Bit 7: Correct Transfer for transmission */ +#define USB_EPR_EP_KIND (1 << 8) /* Bit 8: Endpoint Kind */ +#define USB_EPR_EPTYPE_SHIFT (9) /* Bits 10-9: Endpoint type */ +#define USB_EPR_EPTYPE_MASK (3 << USB_EPR_EPTYPE_SHIFT) +# define USB_EPR_EPTYPE_BULK (0 << USB_EPR_EPTYPE_SHIFT) /* EndPoint BULK */ +# define USB_EPR_EPTYPE_CONTROL (1 << USB_EPR_EPTYPE_SHIFT) /* EndPoint CONTROL */ +# define USB_EPR_EPTYPE_ISOC (2 << USB_EPR_EPTYPE_SHIFT) /* EndPoint ISOCHRONOUS */ +# define USB_EPR_EPTYPE_INTERRUPT (3 << USB_EPR_EPTYPE_SHIFT) /* EndPoint INTERRUPT */ + +#define USB_EPR_SETUP (1 << 11) /* Bit 11: Setup transaction completed */ +#define USB_EPR_STATRX_SHIFT (12) /* Bits 13-12: Status bits, for reception transfers */ +#define USB_EPR_STATRX_MASK (3 << USB_EPR_STATRX_SHIFT) +# define USB_EPR_STATRX_DIS (0 << USB_EPR_STATRX_SHIFT) /* EndPoint RX DISabled */ +# define USB_EPR_STATRX_STALL (1 << USB_EPR_STATRX_SHIFT) /* EndPoint RX STALLed */ +# define USB_EPR_STATRX_NAK (2 << USB_EPR_STATRX_SHIFT) /* EndPoint RX NAKed */ +# define USB_EPR_STATRX_VALID (3 << USB_EPR_STATRX_SHIFT) /* EndPoint RX VALID */ +# define USB_EPR_STATRX_DTOG1 (1 << USB_EPR_STATRX_SHIFT) /* EndPoint RX Data TOGgle bit1 */ +# define USB_EPR_STATRX_DTOG2 (2 << USB_EPR_STATRX_SHIFT) /* EndPoint RX Data TOGgle bit1 */ + +#define USB_EPR_DTOG_RX (1 << 14) /* Bit 14: Data Toggle, for reception transfers */ +#define USB_EPR_CTR_RX (1 << 15) /* Bit 15: Correct Transfer for reception */ + +/* USB control register */ + +#define USB_CNTR_FRES (1 << 0) /* Bit 0: Force USB Reset */ +#define USB_CNTR_PDWN (1 << 1) /* Bit 1: Power down */ +#define USB_CNTR_SUSPRDY (1 << 2) /* Bit 2: Suspend Ready */ +#define USB_CNTR_FSUSP (1 << 3) /* Bit 3: Force suspend */ +#define USB_CNTR_RESUME (1 << 4) /* Bit 4: Resume request */ +#define USB_CNTR_L1RESUME (1 << 5) /* Bit 5: LPM L1 Resume request */ +#define USB_CNTR_L1REQ (1 << 7) /* Bit 7: LPM L1 state request interrupt mask */ +#define USB_CNTR_ESOFM (1 << 8) /* Bit 8: Expected Start Of Frame Interrupt Mask */ +#define USB_CNTR_SOFM (1 << 9) /* Bit 9: Start Of Frame Interrupt Mask */ +#define USB_CNTR_RESETM (1 << 10) /* Bit 10: USB Reset Interrupt Mask */ +#define USB_CNTR_SUSPM (1 << 11) /* Bit 11: Suspend mode Interrupt Mask */ +#define USB_CNTR_WKUPM (1 << 12) /* Bit 12: Wakeup Interrupt Mask */ +#define USB_CNTR_ERRM (1 << 13) /* Bit 13: Error Interrupt Mask */ +#define USB_CNTR_PMAOVRN (1 << 14) /* Bit 14: Packet Memory Area Over / Underrun Interrupt Mask */ +#define USB_CNTR_CTRM (1 << 15) /* Bit 15: Correct Transfer Interrupt Mask */ + +#define USB_CNTR_ALLINTS (USB_CNTR_L1REQ|USB_CNTR_ESOFM|USB_CNTR_SOFM|USB_CNTR_RESETM|\ + USB_CNTR_SUSPM|USB_CNTR_WKUPM|USB_CNTR_ERRM|USB_CNTR_PMAOVRN|\ + USB_CNTR_CTRM) + +/* USB interrupt status register */ + +#define USB_ISTR_EPID_SHIFT (0) /* Bits 3-0: Endpoint Identifier */ +#define USB_ISTR_EPID_MASK (0x0f << USB_ISTR_EPID_SHIFT) +#define USB_ISTR_DIR (1 << 4) /* Bit 4: Direction of transaction */ +#define USB_ISTR_L1REQ (1 << 7) /* Bit 7: LPM L1 state request */ +#define USB_ISTR_ESOF (1 << 8) /* Bit 8: Expected Start Of Frame */ +#define USB_ISTR_SOF (1 << 9) /* Bit 9: Start Of Frame */ +#define USB_ISTR_RESET (1 << 10) /* Bit 10: USB RESET request */ +#define USB_ISTR_SUSP (1 << 11) /* Bit 11: Suspend mode request */ +#define USB_ISTR_WKUP (1 << 12) /* Bit 12: Wake up */ +#define USB_ISTR_ERR (1 << 13) /* Bit 13: Error */ +#define USB_ISTR_PMAOVRN (1 << 14) /* Bit 14: Packet Memory Area Over / Underrun */ +#define USB_ISTR_CTR (1 << 15) /* Bit 15: Correct Transfer */ + +#define USB_ISTR_ALLINTS (USB_ISTR_L1REQ|USB_ISTR_ESOF|USB_ISTR_SOF|USB_ISTR_RESET|\ + USB_ISTR_SUSP|USB_ISTR_WKUP|USB_ISTR_ERR|USB_ISTR_PMAOVRN|\ + USB_ISTR_CTR) + +/* USB frame number register */ + +#define USB_FNR_FN_SHIFT (0) /* Bits 10-0: Frame Number */ +#define USB_FNR_FN_MASK (0x07ff << USB_FNR_FN_SHIFT) +#define USB_FNR_LSOF_SHIFT (11) /* Bits 12-11: Lost SOF */ +#define USB_FNR_LSOF_MASK (3 << USB_FNR_LSOF_SHIFT) +#define USB_FNR_LCK (1 << 13) /* Bit 13: Locked */ +#define USB_FNR_RXDM (1 << 14) /* Bit 14: Receive Data - Line Status */ +#define USB_FNR_RXDP (1 << 15) /* Bit 15: Receive Data + Line Status */ + +/* USB device address */ + +#define USB_DADDR_ADD_SHIFT (0) /* Bits 6-0: Device Address */ +#define USB_DADDR_ADD_MASK (0x7f << USB_DADDR_ADD_SHIFT) +#define USB_DADDR_EF (1 << 7) /* Bit 7: Enable Function */ + +/* LPM control and status register (32-bits) */ + +#define USB_LPMCSR_LPMEN (1 << 0) /* Bit 0: LPM support enable */ +#define USB_LPMCSR_LPMACK (1 << 1) /* Bit 1: LPM Token acknowledge enable */ +#define USB_LPMCSR_REMWAKE (1 << 3) /* Bit 3: bRemoteWake value */ +#define USB_LPMCSR_BESL_SHIFT (4) /* Bits 4-7: BESL value */ +#define USB_LPMCSR_BESL_MASK (15 << USB_LPMCSR_BESL_SHIFT) + +/* Battery charging detector (32-bits) */ + +#define USB_BCDR_BCDEN (1 << 0) /* Bit 0: Battery charging detector (BCD) enable */ +#define USB_BCDR_DCDEN (1 << 1) /* Bit 1: Data contact detection (DCD) mode enable */ +#define USB_BCDR_PDEN (1 << 2) /* Bit 2: Primary detection (PD) mode enable */ +#define USB_BCDR_SDEN (1 << 3) /* Bit 3: Secondary detection (SD) mode enable */ +#define USB_BCDR_DCDET (1 << 4) /* Bit 4: Data contact detection (DCD) status */ +#define USB_BCDR_PDET (1 << 5) /* Bit 5: Primary detection (PD) status */ +#define USB_BCDR_SDET (1 << 6) /* Bit 6: Secondary detection (SD) status */ +#define USB_BCDR_PS2DET (1 << 7) /* Bit 7: DM pull-up detection status */ +#define USB_BCDR_DPPU (1 << 15) /* Bit 15: DP pull-up control */ + +/* Reception buffer address */ + +#define USB_ADDR_RX_SHIFT (2) /* Bits 15:2 ADDRn_RX[15:2]: Reception Buffer Address */ +#define USB_ADDR_RX_MASK (0x3fff << USB_ADDR_RX_SHIFT) + +/* Reception byte count */ + +#define USB_COUNT_RX_BL_SIZE (1 << 31) /* Bit 15: BLock SIZE. */ +#define USB_COUNT_RX_NUM_BLOCK_SHIFT (26) /* Bits 14-10: Number of blocks */ +#define USB_COUNT_RX_NUM_BLOCK_MASK (0x1f << USB_COUNT_RX_NUM_BLOCK_SHIFT) +#define USB_COUNT_RX_SHIFT (16) /* Bits 9-0: Reception Byte Count */ +#define USB_COUNT_RX_MASK (0x3ff << USB_COUNT_RX_SHIFT) + +#endif /* CONFIG_STM32H5_HAVE_USBFS */ +#endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_USBFS_H */ diff --git a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h index e455ff208ca..d7c82a533a6 100644 --- a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h +++ b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h @@ -386,6 +386,12 @@ #define GPIO_LPUART1_CTS_1 (GPIO_ALT|GPIO_AF3|GPIO_PORTA|GPIO_PIN11) #define GPIO_LPUART1_RTS_DE_1 (GPIO_ALT|GPIO_AF3|GPIO_PORTA|GPIO_PIN12) +/* USB */ + +#define GPIO_USB_DM (GPIO_ALT|GPIO_AF10|GPIO_PUSHPULL|GPIO_FLOAT|GPIO_PORTA|GPIO_PIN11) +#define GPIO_USB_DP (GPIO_ALT|GPIO_AF10|GPIO_PUSHPULL|GPIO_FLOAT|GPIO_PORTA|GPIO_PIN12) +#define GPIO_USB_SOF (GPIO_ALT|GPIO_AF10|GPIO_PUSHPULL|GPIO_FLOAT|GPIO_PORTA|GPIO_PIN8) + /* ADC1 */ #define GPIO_ADC1_IN0_0 (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN0) diff --git a/arch/arm/src/stm32h5/stm32.h b/arch/arm/src/stm32h5/stm32.h index 2a42f101763..172499e7257 100644 --- a/arch/arm/src/stm32h5/stm32.h +++ b/arch/arm/src/stm32h5/stm32.h @@ -46,5 +46,6 @@ #include "stm32_lowputc.h" #include "stm32_i2c.h" #include "stm32_adc.h" +#include "stm32_usbfs.h" #endif /* __ARCH_ARM_SRC_STM32H5_STM32_H */ diff --git a/arch/arm/src/stm32h5/stm32_hsi48.c b/arch/arm/src/stm32h5/stm32_hsi48.c index 261de4ca7a2..7aa4a08fda5 100644 --- a/arch/arm/src/stm32h5/stm32_hsi48.c +++ b/arch/arm/src/stm32h5/stm32_hsi48.c @@ -87,7 +87,7 @@ void stm32h5_enable_hsi48(enum syncsrc_e syncsrc) /* Wait for the HSI48 clock to stabilize */ - while ((getreg32(STM32_RCC_CRRCR) & RCC_CRRCR_HSI48RDY) == 0); + while ((getreg32(STM32_RCC_CR) & RCC_CR_HSI48RDY) == 0); /* Return if no synchronization */ diff --git a/arch/arm/src/stm32h5/stm32_usbfs.c b/arch/arm/src/stm32h5/stm32_usbfs.c new file mode 100644 index 00000000000..f7fd37e428a --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_usbfs.c @@ -0,0 +1,3951 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_usbfs.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "stm32.h" +#include "stm32_gpio.h" +#include "stm32_usbfs.h" + +#if defined(CONFIG_STM32H5_USBFS) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#ifndef CONFIG_USBDEV_EP0_MAXSIZE +# define CONFIG_USBDEV_EP0_MAXSIZE 64 +#endif + +#ifndef CONFIG_USBDEV_SETUP_MAXDATASIZE +# define CONFIG_USBDEV_SETUP_MAXDATASIZE CONFIG_USBDEV_EP0_MAXSIZE +#endif + +/* Extremely detailed register debug that you would normally never want + * enabled. + */ + +#ifndef CONFIG_DEBUG_USB_INFO +# undef CONFIG_STM32H5_USBFS_REGDEBUG +#endif + +/* Initial interrupt mask: Reset + Suspend + Correct Transfer */ + +#define STM32_CNTR_SETUP (USB_CNTR_RESETM|USB_CNTR_SUSPM|USB_CNTR_CTRM) + +/* Endpoint identifiers. The STM32 supports up to 16 mono-directional or 8 + * bidirectional endpoints. We'll define STM32_NENDPOINTS to be 8, + * because that is how many endpoint register sets there are. With a 2048 + * byte USB RAM, there is room for a 64-byte buffer table + 16 64-byte + * buffers. + */ + +#define EP0 (0) +#define EP1 (1) +#define EP2 (2) +#define EP3 (3) +#define EP4 (4) +#define EP5 (5) +#define EP6 (6) +#define EP7 (7) + +#define STM32_ENDP_BIT(ep) (1 << (ep)) +#define STM32_ENDP_ALLSET 0xff + +/* Packet sizes. We use a 64 byte max packet size for all endpoint types + * Max packet size for USB 2.0 Full-Speed is 64 bytes. With 64-byte buffer + * sizes, there is space in the USB RAM for 16 64-byte buffers. + */ + +#define STM32_MAXPACKET_SHIFT (6) +#define STM32_MAXPACKET_SIZE (1 << (STM32_MAXPACKET_SHIFT)) +#define STM32_MAXPACKET_MASK (STM32_MAXPACKET_SIZE-1) + +#define STM32_EP0MAXPACKET STM32_MAXPACKET_SIZE + +/* Buffer descriptor table. The buffer table is positioned at the + * beginning of the 2048-byte USB memory. We will use the first + * STM32_NENDPOINTS*2 words for the buffer table. That is exactly 64 bytes, + * leaving 1,984 bytes for endpoint buffers. + */ + +#define STM32_BTABLE_ADDRESS (0x00) /* Start at the beginning of USB/CAN RAM */ +#define STM32_DESC_SIZE (8) /* Each descriptor is 2*4=8 bytes in size */ +#define STM32_BTABLE_SIZE (STM32_NENDPOINTS*STM32_DESC_SIZE) + +/* Buffer layout. Assume that all buffers are 64-bytes (maxpacketsize). + * With 64-byte buffers and a 2048 byte USB RAM, we can support 8 + * bi-directional endpoints. + */ + +#define STM32_BUFFER_START STM32_BTABLE_SIZE +#define STM32_EP0_RXADDR STM32_BUFFER_START +#define STM32_EP0_TXADDR (STM32_EP0_RXADDR+STM32_EP0MAXPACKET) + +#define STM32_BUFFER_EP0 0x03 +#define STM32_NBUFFERS 16 +#define STM32_BUFFER_BIT(bn) (1 << (bn)) +#define STM32_BUFFER_ALLSET 0xffff +#define STM32_BUFNO2BUF(bn) (STM32_BUFFER_START+((bn)<head == NULL) +#define stm32_rqpeek(ep) ((ep)->head) + +/* USB trace ****************************************************************/ + +/* Trace error codes */ + +#define STM32_TRACEERR_ALLOCFAIL 0x0001 +#define STM32_TRACEERR_BADCLEARFEATURE 0x0002 +#define STM32_TRACEERR_BADDEVGETSTATUS 0x0003 +#define STM32_TRACEERR_BADEPGETSTATUS 0x0004 +#define STM32_TRACEERR_BADEPNO 0x0005 +#define STM32_TRACEERR_BADEPTYPE 0x0006 +#define STM32_TRACEERR_BADGETCONFIG 0x0007 +#define STM32_TRACEERR_BADGETSETDESC 0x0008 +#define STM32_TRACEERR_BADGETSTATUS 0x0009 +#define STM32_TRACEERR_BADSETADDRESS 0x000a +#define STM32_TRACEERR_BADSETCONFIG 0x000b +#define STM32_TRACEERR_BADSETFEATURE 0x000c +#define STM32_TRACEERR_BINDFAILED 0x000d +#define STM32_TRACEERR_DISPATCHSTALL 0x000e +#define STM32_TRACEERR_DRIVER 0x000f +#define STM32_TRACEERR_DRIVERREGISTERED 0x0010 +#define STM32_TRACEERR_EP0BADCTR 0x0011 +#define STM32_TRACEERR_EP0SETUPSTALLED 0x0012 +#define STM32_TRACEERR_EPBUFFER 0x0013 +#define STM32_TRACEERR_EPDISABLED 0x0014 +#define STM32_TRACEERR_EPOUTNULLPACKET 0x0015 +#define STM32_TRACEERR_EPRESERVE 0x0016 +#define STM32_TRACEERR_INVALIDCTRLREQ 0x0017 +#define STM32_TRACEERR_INVALIDPARMS 0x0018 +#define STM32_TRACEERR_IRQREGISTRATION 0x0019 +#define STM32_TRACEERR_NOTCONFIGURED 0x001a +#define STM32_TRACEERR_REQABORTED 0x001b + +/* Trace interrupt codes */ + +#define STM32_TRACEINTID_CLEARFEATURE 0x0001 +#define STM32_TRACEINTID_DEVGETSTATUS 0x0002 +#define STM32_TRACEINTID_DISPATCH 0x0003 +#define STM32_TRACEINTID_EP0IN 0x0004 +#define STM32_TRACEINTID_EP0INDONE 0x0005 +#define STM32_TRACEINTID_EP0OUTDONE 0x0006 +#define STM32_TRACEINTID_EP0SETUPDONE 0x0007 +#define STM32_TRACEINTID_EP0SETUPSETADDRESS 0x0008 +#define STM32_TRACEINTID_EPGETSTATUS 0x0009 +#define STM32_TRACEINTID_EPINDONE 0x000a +#define STM32_TRACEINTID_EPINQEMPTY 0x000b +#define STM32_TRACEINTID_EPOUTDONE 0x000c +#define STM32_TRACEINTID_EPOUTPENDING 0x000d +#define STM32_TRACEINTID_EPOUTQEMPTY 0x000e +#define STM32_TRACEINTID_ESOF 0x000f +#define STM32_TRACEINTID_GETCONFIG 0x0010 +#define STM32_TRACEINTID_GETSETDESC 0x0011 +#define STM32_TRACEINTID_GETSETIF 0x0012 +#define STM32_TRACEINTID_GETSTATUS 0x0013 +#define STM32_TRACEINTID_IFGETSTATUS 0x0015 +#define STM32_TRACEINTID_CTR 0x0016 +#define STM32_TRACEINTID_INTERRUPT 0x0017 +#define STM32_TRACEINTID_NOSTDREQ 0x0018 +#define STM32_TRACEINTID_RESET 0x0019 +#define STM32_TRACEINTID_SETCONFIG 0x001a +#define STM32_TRACEINTID_SETFEATURE 0x001b +#define STM32_TRACEINTID_SUSP 0x001c +#define STM32_TRACEINTID_SYNCHFRAME 0x001d +#define STM32_TRACEINTID_WKUP 0x001e +#define STM32_TRACEINTID_EP0SETUPOUT 0x001f +#define STM32_TRACEINTID_EP0SETUPOUTDATA 0x0020 + +/* Byte ordering in host-based values */ + +#ifdef CONFIG_ENDIAN_BIG +# define LSB 1 +# define MSB 0 +#else +# define LSB 0 +# define MSB 1 +#endif + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* The various states of a control pipe */ + +enum stm32_ep0state_e +{ + EP0STATE_IDLE = 0, /* No request in progress */ + EP0STATE_SETUP_OUT, /* Set up received with data for device OUT in progress */ + EP0STATE_SETUP_READY, /* Set up was received prior and is in ctrl, + * now the data has arrived */ + EP0STATE_WRREQUEST, /* Write request in progress */ + EP0STATE_RDREQUEST, /* Read request in progress */ + EP0STATE_STALLED /* We are stalled */ +}; + +/* Resume states */ + +enum stm32_rsmstate_e +{ + RSMSTATE_IDLE = 0, /* Device is either fully suspended or running */ + RSMSTATE_STARTED, /* Resume sequence has been started */ + RSMSTATE_WAITING /* Waiting (on ESOFs) for end of sequence */ +}; + +union wb_u +{ + uint16_t w; + uint8_t b[2]; +}; + +/* A container for a request so that the request make be retained in a list */ + +struct stm32_req_s +{ + struct usbdev_req_s req; /* Standard USB request */ + struct stm32_req_s *flink; /* Supports a singly linked list */ +}; + +/* This is the internal representation of an endpoint */ + +struct stm32_ep_s +{ + /* Common endpoint fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_ep_s + * to struct stm32_ep_s. + */ + + struct usbdev_ep_s ep; /* Standard endpoint structure */ + + /* STR71X-specific fields */ + + struct stm32_usbdev_s *dev; /* Reference to private driver data */ + struct stm32_req_s *head; /* Request list for this endpoint */ + struct stm32_req_s *tail; + uint8_t bufno; /* Allocated buffer number */ + uint8_t stalled:1; /* true: Endpoint is stalled */ + uint8_t halted:1; /* true: Endpoint feature halted */ + uint8_t txbusy:1; /* true: TX endpoint FIFO full */ + uint8_t txnullpkt:1; /* Null packet needed at end of transfer */ +}; + +struct stm32_usbdev_s +{ + /* Common device fields. This must be the first thing defined in the + * structure so that it is possible to simply cast from struct usbdev_s + * to structstm32_usbdev_s. + */ + + struct usbdev_s usbdev; + + /* The bound device class driver */ + + struct usbdevclass_driver_s *driver; + + /* STM32-specific fields */ + + uint8_t ep0state; /* State of EP0 (see enum stm32_ep0state_e) */ + uint8_t rsmstate; /* Resume state (see enum stm32_rsmstate_e) */ + uint8_t nesofs; /* ESOF counter (for resume support) */ + uint8_t rxpending:1; /* 1: OUT data in PMA, but no read requests */ + uint8_t selfpowered:1; /* 1: Device is self powered */ + uint8_t epavail; /* Bitset of available endpoints */ + uint16_t bufavail; /* Bitset of available buffers */ + uint16_t rxstatus; /* Saved during interrupt processing */ + uint16_t txstatus; /* " " " " " " " " */ + uint16_t imask; /* Current interrupt mask */ + + /* E0 SETUP data buffering. + * + * ctrl + * The 8-byte SETUP request is received on the EP0 OUT endpoint and is + * saved. + * + * ep0data + * For OUT SETUP requests, the SETUP data phase must also complete + * before the SETUP command can be processed. The ep0 packet receipt + * logic stm32_ep0_rdrequest will save the accompanying EP0 OUT data + * in ep0data[] before the SETUP command is re-processed. + * + * ep0datlen + * Length of OUT DATA received in ep0data[] + */ + + struct usb_ctrlreq_s ctrl; /* Last EP0 request */ + + uint8_t ep0data[CONFIG_USBDEV_SETUP_MAXDATASIZE]; + uint16_t ep0datlen; + + /* The endpoint list */ + + struct stm32_ep_s eplist[STM32_NENDPOINTS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Register operations ******************************************************/ + +#ifdef CONFIG_STM32H5_USBFS_REGDEBUG +static uint32_t stm32_getreg(uint32_t addr); +static void stm32_putreg(uint16_t val, uint32_t addr); +static void stm32_checksetup(void); +static void stm32_dumpep(int epno); +#else +# define stm32_getreg(addr) getreg32(addr) +# define stm32_putreg(val,addr) putreg32(val,addr) +# define stm32_checksetup() +# define stm32_dumpep(epno) +#endif + +/* Low-Level Helpers ********************************************************/ + +static inline void + stm32_seteptxcount(uint8_t epno, uint16_t count); +static inline void + stm32_seteptxaddr(uint8_t epno, uint16_t addr); +static inline uint16_t + stm32_geteptxaddr(uint8_t epno); +static void stm32_seteprxcount(uint8_t epno, uint16_t count); +static inline uint16_t + stm32_geteprxcount(uint8_t epno); +static inline void + stm32_seteprxaddr(uint8_t epno, uint16_t addr); +static inline uint16_t + stm32_geteprxaddr(uint8_t epno); +static inline void + stm32_setepaddress(uint8_t epno, uint16_t addr); +static inline void + stm32_seteptype(uint8_t epno, uint16_t type); +static inline void + stm32_seteptxaddr(uint8_t epno, uint16_t addr); +static inline void + stm32_clrstatusout(uint8_t epno); +static void stm32_clrrxdtog(uint8_t epno); +static void stm32_clrtxdtog(uint8_t epno); +static void stm32_clrepctrrx(uint8_t epno); +static void stm32_clrepctrtx(uint8_t epno); +static void stm32_seteptxstatus(uint8_t epno, uint16_t state); +static void stm32_seteprxstatus(uint8_t epno, uint16_t state); +static inline uint16_t + stm32_geteptxstatus(uint8_t epno); +static inline uint16_t + stm32_geteprxstatus(uint8_t epno); +static bool stm32_eptxstalled(uint8_t epno); +static bool stm32_eprxstalled(uint8_t epno); +static void stm32_setimask(struct stm32_usbdev_s *priv, + uint16_t setbits, + uint16_t clrbits); + +/* Suspend/Resume Helpers ***************************************************/ + +static void stm32_suspend(struct stm32_usbdev_s *priv); +static void stm32_initresume(struct stm32_usbdev_s *priv); +static void stm32_esofpoll(struct stm32_usbdev_s *priv) ; + +/* Request Helpers **********************************************************/ + +static void stm32_copytopma(const uint8_t *buffer, uint16_t pma, + uint16_t nbytes); +static inline void stm32_copyfrompma(uint8_t *buffer, + uint16_t pma, uint16_t nbytes); +static struct stm32_req_s * + stm32_rqdequeue(struct stm32_ep_s *privep); +static void stm32_rqenqueue(struct stm32_ep_s *privep, + struct stm32_req_s *req); +static inline void + stm32_abortrequest(struct stm32_ep_s *privep, + struct stm32_req_s *privreq, int16_t result); +static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result); +static void stm32_epwrite(struct stm32_usbdev_s *buf, + struct stm32_ep_s *privep, + const uint8_t *data, uint32_t nbytes); +static int stm32_wrrequest(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep); +inline static int + stm32_wrrequest_ep0(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep); +static inline int + stm32_ep0_rdrequest(struct stm32_usbdev_s *priv); +static int stm32_rdrequest(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep); +static void stm32_cancelrequests(struct stm32_ep_s *privep); + +/* Interrupt level processing ***********************************************/ + +static void stm32_dispatchrequest(struct stm32_usbdev_s *priv); +static void stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno); +static void stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value); +static void stm32_ep0setup(struct stm32_usbdev_s *priv); +static void stm32_ep0out(struct stm32_usbdev_s *priv); +static void stm32_ep0in(struct stm32_usbdev_s *priv); +static inline void + stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr); +static void stm32_transfer(struct stm32_usbdev_s *priv); +static int stm32_interrupt(int irq, void *context, void *arg); + +/* Endpoint helpers *********************************************************/ + +static inline struct stm32_ep_s * + stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset); +static inline void + stm32_epunreserve(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep); +static inline bool + stm32_epreserved(struct stm32_usbdev_s *priv, int epno); +static int stm32_epallocpma(struct stm32_usbdev_s *priv); +static inline void + stm32_epfreepma(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep); + +/* Endpoint operations ******************************************************/ + +static int stm32_epconfigure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, bool last); +static int stm32_epdisable(struct usbdev_ep_s *ep); +static struct usbdev_req_s * + stm32_epallocreq(struct usbdev_ep_s *ep); +static void stm32_epfreereq(struct usbdev_ep_s *ep, + struct usbdev_req_s *); +static int stm32_epsubmit(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int stm32_epcancel(struct usbdev_ep_s *ep, + struct usbdev_req_s *req); +static int stm32_epstall(struct usbdev_ep_s *ep, bool resume); + +/* USB device controller operations *****************************************/ + +static struct usbdev_ep_s * + stm32_allocep(struct usbdev_s *dev, uint8_t epno, bool in, + uint8_t eptype); +static void stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep); +static int stm32_getframe(struct usbdev_s *dev); +static int stm32_wakeup(struct usbdev_s *dev); +static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered); +static int stm32_pullup(struct usbdev_s *dev, bool enable); + +/* Initialization/Reset *****************************************************/ + +static void stm32_reset(struct stm32_usbdev_s *priv); +static void stm32_hwreset(struct stm32_usbdev_s *priv); +static void stm32_hwsetup(struct stm32_usbdev_s *priv); +static void stm32_hwshutdown(struct stm32_usbdev_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Since there is only a single USB interface, all status information can be + * be simply retained in a single global instance. + */ + +static struct stm32_usbdev_s g_usbdev; + +static const struct usbdev_epops_s g_epops = +{ + .configure = stm32_epconfigure, + .disable = stm32_epdisable, + .allocreq = stm32_epallocreq, + .freereq = stm32_epfreereq, + .submit = stm32_epsubmit, + .cancel = stm32_epcancel, + .stall = stm32_epstall, +}; + +static const struct usbdev_ops_s g_devops = +{ + .allocep = stm32_allocep, + .freeep = stm32_freeep, + .getframe = stm32_getframe, + .wakeup = stm32_wakeup, + .selfpowered = stm32_selfpowered, + .pullup = stm32_pullup, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_intdecode[] = +{ + TRACE_STR(STM32_TRACEINTID_CLEARFEATURE), + TRACE_STR(STM32_TRACEINTID_DEVGETSTATUS), + TRACE_STR(STM32_TRACEINTID_DISPATCH), + TRACE_STR(STM32_TRACEINTID_EP0IN), + TRACE_STR(STM32_TRACEINTID_EP0INDONE), + TRACE_STR(STM32_TRACEINTID_EP0OUTDONE), + TRACE_STR(STM32_TRACEINTID_EP0SETUPDONE), + TRACE_STR(STM32_TRACEINTID_EP0SETUPSETADDRESS), + TRACE_STR(STM32_TRACEINTID_EPGETSTATUS), + TRACE_STR(STM32_TRACEINTID_EPINDONE), + TRACE_STR(STM32_TRACEINTID_EPINQEMPTY), + TRACE_STR(STM32_TRACEINTID_EPOUTDONE), + TRACE_STR(STM32_TRACEINTID_EPOUTPENDING), + TRACE_STR(STM32_TRACEINTID_EPOUTQEMPTY), + TRACE_STR(STM32_TRACEINTID_ESOF), + TRACE_STR(STM32_TRACEINTID_GETCONFIG), + TRACE_STR(STM32_TRACEINTID_GETSETDESC), + TRACE_STR(STM32_TRACEINTID_GETSETIF), + TRACE_STR(STM32_TRACEINTID_GETSTATUS), + TRACE_STR(STM32_TRACEINTID_IFGETSTATUS), + TRACE_STR(STM32_TRACEINTID_CTR), + TRACE_STR(STM32_TRACEINTID_INTERRUPT), + TRACE_STR(STM32_TRACEINTID_NOSTDREQ), + TRACE_STR(STM32_TRACEINTID_RESET), + TRACE_STR(STM32_TRACEINTID_SETCONFIG), + TRACE_STR(STM32_TRACEINTID_SETFEATURE), + TRACE_STR(STM32_TRACEINTID_SUSP), + TRACE_STR(STM32_TRACEINTID_SYNCHFRAME), + TRACE_STR(STM32_TRACEINTID_WKUP), + TRACE_STR(STM32_TRACEINTID_EP0SETUPOUT), + TRACE_STR(STM32_TRACEINTID_EP0SETUPOUTDATA), + TRACE_STR_END +}; +#endif + +#ifdef CONFIG_USBDEV_TRACE_STRINGS +const struct trace_msg_t g_usb_trace_strings_deverror[] = +{ + TRACE_STR(STM32_TRACEERR_ALLOCFAIL), + TRACE_STR(STM32_TRACEERR_BADCLEARFEATURE), + TRACE_STR(STM32_TRACEERR_BADDEVGETSTATUS), + TRACE_STR(STM32_TRACEERR_BADEPGETSTATUS), + TRACE_STR(STM32_TRACEERR_BADEPNO), + TRACE_STR(STM32_TRACEERR_BADEPTYPE), + TRACE_STR(STM32_TRACEERR_BADGETCONFIG), + TRACE_STR(STM32_TRACEERR_BADGETSETDESC), + TRACE_STR(STM32_TRACEERR_BADGETSTATUS), + TRACE_STR(STM32_TRACEERR_BADSETADDRESS), + TRACE_STR(STM32_TRACEERR_BADSETCONFIG), + TRACE_STR(STM32_TRACEERR_BADSETFEATURE), + TRACE_STR(STM32_TRACEERR_BINDFAILED), + TRACE_STR(STM32_TRACEERR_DISPATCHSTALL), + TRACE_STR(STM32_TRACEERR_DRIVER), + TRACE_STR(STM32_TRACEERR_DRIVERREGISTERED), + TRACE_STR(STM32_TRACEERR_EP0BADCTR), + TRACE_STR(STM32_TRACEERR_EP0SETUPSTALLED), + TRACE_STR(STM32_TRACEERR_EPBUFFER), + TRACE_STR(STM32_TRACEERR_EPDISABLED), + TRACE_STR(STM32_TRACEERR_EPOUTNULLPACKET), + TRACE_STR(STM32_TRACEERR_EPRESERVE), + TRACE_STR(STM32_TRACEERR_INVALIDCTRLREQ), + TRACE_STR(STM32_TRACEERR_INVALIDPARMS), + TRACE_STR(STM32_TRACEERR_IRQREGISTRATION), + TRACE_STR(STM32_TRACEERR_NOTCONFIGURED), + TRACE_STR(STM32_TRACEERR_REQABORTED), + TRACE_STR_END +}; +#endif + +/**************************************************************************** + * Private Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Register Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_getreg + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_USBFS_REGDEBUG +static uint32_t stm32_getreg(uint32_t addr) +{ + static uint32_t prevaddr = 0; + static uint32_t preval = 0; + static uint32_t count = 0; + + /* Read the value from the register */ + + uint32_t val = getreg32(addr); + + /* Is this the same value that we read from the same register last time? + * Are we polling the register? If so, suppress some of the output. + */ + + if (addr == prevaddr && val == preval) + { + if (count == 0xffffffff || ++count > 3) + { + if (count == 4) + { + uinfo("...\n"); + } + + return val; + } + } + + /* No this is a new address or value */ + + else + { + /* Did we print "..." for the previous value? */ + + if (count > 3) + { + /* Yes.. then show how many times the value repeated */ + + uinfo("[repeats %d more times]\n", count - 3); + } + + /* Save the new address, value, and count */ + + prevaddr = addr; + preval = val; + count = 1; + } + + /* Show the register value read */ + + uinfo("%08x->%04x\n", addr, val); + return val; +} +#endif + +/**************************************************************************** + * Name: stm32_putreg + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_USBFS_REGDEBUG +static void stm32_putreg(uint32_t val, uint32_t addr) +{ + /* Show the register value being written */ + + uinfo("%08x<-%04x\n", addr, val); + + /* Write the value */ + + putreg32(val, addr); +} +#endif + +/**************************************************************************** + * Name: stm32_dumpep + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_USBFS_REGDEBUG +static void stm32_dumpep(int epno) +{ + uint32_t addr; + + /* Common registers */ + + uinfo("CNTR: %04x\n", getreg32(STM32_USB_CNTR)); + uinfo("ISTR: %04x\n", getreg32(STM32_USB_ISTR)); + uinfo("FNR: %04x\n", getreg32(STM32_USB_FNR)); + uinfo("DADDR: %04x\n", getreg32(STM32_USB_DADDR)); + uinfo("BTABLE: %04x\n", getreg32(STM32_USB_BTABLE)); + + /* Endpoint register */ + + addr = STM32_USB_EPR(epno); + uinfo("EPR%d: [%08x] %04x\n", epno, addr, getreg32(addr)); + + /* Endpoint descriptor */ + + addr = STM32_USB_BTABLE_ADDR(epno, 0); + uinfo("DESC: %08x\n", addr); + + /* Endpoint buffer descriptor */ + + addr = STM32_USB_TX(epno); + uinfo(" TX BUF: [%08x] %04x\n", addr, getreg32(addr)); + + addr = STM32_USB_RX(epno); + uinfo(" RX BUF: [%08x] %04x\n", addr, getreg32(addr)); +} +#endif + +/**************************************************************************** + * Name: stm32_checksetup + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_USBFS_REGDEBUG +static void stm32_checksetup(void) +{ + uint32_t cfgr = getreg32(STM32_RCC_CFGR); + uint32_t apb1rstr = getreg32(STM32_RCC_APB1RSTR1); + uint32_t apb1enr = getreg32(STM32_RCC_APB1ENR1); + + uinfo("CFGR: %08x APB1RSTR: %08x APB1ENR: %08x\n", + cfgr, apb1rstr, apb1enr); + + if ((apb1rstr & RCC_APB1RSTR1_USBRST) != 0 || + (apb1enr & RCC_APB1ENR1_USBEN) == 0) + { + uerr("ERROR: USB is NOT setup correctly\n"); + } +} +#endif + +/**************************************************************************** + * Low-Level Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_seteptxcount + ****************************************************************************/ + +static inline void stm32_seteptxcount(uint8_t epno, uint16_t count) +{ + uint32_t txbd; + volatile uint32_t *epaddr = (uint32_t *)STM32_USB_TX(epno); + txbd = (*epaddr & 0x0000ffff) | (count << 16); + *epaddr = txbd; +} + +/**************************************************************************** + * Name: stm32_seteptxaddr + ****************************************************************************/ + +static inline void stm32_seteptxaddr(uint8_t epno, uint16_t addr) +{ + uint32_t txbd; + volatile uint32_t *txaddr = (uint32_t *)STM32_USB_TX(epno); + txbd = (*txaddr & 0xffff0000) | addr; + *txaddr = txbd; +} + +/**************************************************************************** + * Name: stm32_geteptxaddr + ****************************************************************************/ + +static inline uint16_t stm32_geteptxaddr(uint8_t epno) +{ + uint32_t eptxaddr; + volatile uint32_t *txaddr = (uint32_t *)STM32_USB_TX(epno); + eptxaddr = ((uint32_t)*txaddr) & 0x0000ffff; + return (uint16_t) eptxaddr; +} + +/**************************************************************************** + * Name: stm32_seteprxcount + ****************************************************************************/ + +static void stm32_seteprxcount(uint8_t epno, uint16_t count) +{ + uint32_t rxbd; + volatile uint32_t *epaddr = (uint32_t *)STM32_USB_RX(epno); + uint32_t rxcount = 0; + uint32_t nblocks; + + /* The upper bits of the RX COUNT value contain the size of allocated + * RX buffer. This is based on a block size of 2 or 32: + * + * USB_COUNT_RX_BL_SIZE not set: + * nblocks is in units of 2 bytes. + * 00000 - not allowed + * 00001 - 2 bytes + * .... + * 11111 - 62 bytes + * + * USB_COUNT_RX_BL_SIZE set: + * 00000 - 32 bytes + * 00001 - 64 bytes + * ... + * 01111 - 512 bytes + * 11111 - 1024 bytes + */ + + if (count > 62) + { + /* Blocks of 32 (with 0 meaning one block of 32) */ + + nblocks = (count >> 5) - 1 ; + DEBUGASSERT(nblocks <= 0x0f); + rxcount = (uint32_t)((nblocks << + USB_COUNT_RX_NUM_BLOCK_SHIFT) | + USB_COUNT_RX_BL_SIZE); + } + else if (count > 0) + { + /* Blocks of 2 (with 1 meaning one block of 2) */ + + nblocks = (count + 1) >> 1; + DEBUGASSERT(nblocks > 0 && nblocks < 0x1f); + rxcount = (uint32_t)(nblocks << USB_COUNT_RX_NUM_BLOCK_SHIFT); + } + rxbd = (*epaddr & 0x0000ffff) | rxcount; + *epaddr = rxbd; +} + +/**************************************************************************** + * Name: stm32_geteprxcount + ****************************************************************************/ + +static inline uint16_t stm32_geteprxcount(uint8_t epno) +{ + volatile uint32_t *epaddr = (uint32_t *)STM32_USB_RX(epno); + return (((*epaddr) & USB_COUNT_RX_MASK) >> 16); +} + +/**************************************************************************** + * Name: stm32_seteprxaddr + ****************************************************************************/ + +static inline void stm32_seteprxaddr(uint8_t epno, uint16_t addr) +{ + uint32_t rxbd; + volatile uint32_t *rxaddr = (uint32_t *)STM32_USB_RX(epno); + rxbd = (*rxaddr & 0xffff0000) | addr; + *rxaddr = rxbd; +} + +/**************************************************************************** + * Name: stm32_seteprxaddr + ****************************************************************************/ + +static inline uint16_t stm32_geteprxaddr(uint8_t epno) +{ + uint32_t eprxaddr; + volatile uint32_t *rxaddr = (uint32_t *)STM32_USB_RX(epno); + eprxaddr = ((uint32_t)*rxaddr) & 0x0000ffff; + return (uint16_t) eprxaddr; +} + +/**************************************************************************** + * Name: stm32_setepaddress + ****************************************************************************/ + +static inline void stm32_setepaddress(uint8_t epno, uint16_t addr) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + regval = stm32_getreg(epaddr); + regval &= EPR_NOTOG_MASK; + regval &= ~USB_EPR_EA_MASK; + regval |= (addr << USB_EPR_EA_SHIFT); + stm32_putreg(regval, epaddr); +} + +/**************************************************************************** + * Name: stm32_seteptype + ****************************************************************************/ + +static inline void stm32_seteptype(uint8_t epno, uint16_t type) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + regval = stm32_getreg(epaddr); + regval &= EPR_NOTOG_MASK; + regval &= ~USB_EPR_EPTYPE_MASK; + regval |= type; + stm32_putreg(regval, epaddr); +} + +/**************************************************************************** + * Name: stm32_clrstatusout + ****************************************************************************/ + +static inline void stm32_clrstatusout(uint8_t epno) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + /* For a BULK endpoint the EP_KIND bit is used to enabled double buffering; + * for a CONTROL endpoint, it is set to indicate that a status OUT + * transaction is expected. The bit is not used with out endpoint types. + */ + + regval = stm32_getreg(epaddr); + regval &= EPR_NOTOG_MASK; + regval &= ~USB_EPR_EP_KIND; + stm32_putreg(regval, epaddr); +} + +/**************************************************************************** + * Name: stm32_clrrxdtog + ****************************************************************************/ + +static void stm32_clrrxdtog(uint8_t epno) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + regval = stm32_getreg(epaddr); + if ((regval & USB_EPR_DTOG_RX) != 0) + { + regval &= EPR_NOTOG_MASK; + regval |= USB_EPR_DTOG_RX; + stm32_putreg(regval, epaddr); + } +} + +/**************************************************************************** + * Name: stm32_clrtxdtog + ****************************************************************************/ + +static void stm32_clrtxdtog(uint8_t epno) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + regval = stm32_getreg(epaddr); + if ((regval & USB_EPR_DTOG_TX) != 0) + { + regval &= EPR_NOTOG_MASK; + regval |= USB_EPR_DTOG_TX; + stm32_putreg(regval, epaddr); + } +} + +/**************************************************************************** + * Name: stm32_clrepctrrx + ****************************************************************************/ + +static void stm32_clrepctrrx(uint8_t epno) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + regval = stm32_getreg(epaddr); + regval &= EPR_NOTOG_MASK; + regval &= ~USB_EPR_CTR_RX; + stm32_putreg(regval, epaddr); +} + +/**************************************************************************** + * Name: stm32_clrepctrtx + ****************************************************************************/ + +static void stm32_clrepctrtx(uint8_t epno) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + regval = stm32_getreg(epaddr); + regval &= EPR_NOTOG_MASK; + regval &= ~USB_EPR_CTR_TX; + stm32_putreg(regval, epaddr); +} + +/**************************************************************************** + * Name: stm32_geteptxstatus + ****************************************************************************/ + +static inline uint16_t stm32_geteptxstatus(uint8_t epno) +{ + return (uint16_t)(stm32_getreg(STM32_USB_EPR(epno)) & + USB_EPR_STATTX_MASK); +} + +/**************************************************************************** + * Name: stm32_geteprxstatus + ****************************************************************************/ + +static inline uint16_t stm32_geteprxstatus(uint8_t epno) +{ + return ((uint16_t) stm32_getreg(STM32_USB_EPR(epno)) & \ + USB_EPR_STATRX_MASK); +} + +/**************************************************************************** + * Name: stm32_seteptxstatus + ****************************************************************************/ + +static void stm32_seteptxstatus(uint8_t epno, uint16_t state) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + /* The bits in the STAT_TX field can be toggled by software to set their + * value. When set to 0, the value remains unchanged; when set to one, + * value toggles. + */ + + regval = stm32_getreg(epaddr); + + /* The exclusive OR will set STAT_TX bits to 1 if there value is different + * from the bits requested in 'state' + */ + + regval ^= state; + regval &= EPR_TXDTOG_MASK; + stm32_putreg(regval, epaddr); +} + +/**************************************************************************** + * Name: stm32_seteprxstatus + ****************************************************************************/ + +static void stm32_seteprxstatus(uint8_t epno, uint16_t state) +{ + uint32_t epaddr = STM32_USB_EPR(epno); + uint32_t regval; + + /* The bits in the STAT_RX field can be toggled by software to set their + * value. When set to 0, the value remains unchanged; when set to one, + * value toggles. + */ + + regval = stm32_getreg(epaddr); + + /* The exclusive OR will set STAT_RX bits to 1 if there value is different + * from the bits requested in 'state' + */ + + regval ^= state; + regval &= EPR_RXDTOG_MASK; + stm32_putreg(regval, epaddr); +} + +/**************************************************************************** + * Name: stm32_eptxstalled + ****************************************************************************/ + +static inline bool stm32_eptxstalled(uint8_t epno) +{ + return (stm32_geteptxstatus(epno) == USB_EPR_STATTX_STALL); +} + +/**************************************************************************** + * Name: stm32_eprxstalled + ****************************************************************************/ + +static inline bool stm32_eprxstalled(uint8_t epno) +{ + return (stm32_geteprxstatus(epno) == USB_EPR_STATRX_STALL); +} + +/**************************************************************************** + * Request Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_copytopma + ****************************************************************************/ + +static void stm32_copytopma(const uint8_t *buffer, + uint16_t pma, uint16_t nbytes) +{ + uint32_t *dest; + uint32_t b1; + uint32_t b2; + uint32_t b3; + uint32_t b4; + int nwords = (nbytes + 3) >> 2; + int i; + + /* Copy loop. Source=user buffer, Dest=packet memory */ + + dest = (uint32_t *)(STM32_USB_FS_RAM_BASE + (uint32_t)pma); + for (i = nwords; i != 0; i--) + { + /* Read two bytes and pack into on 16-bit word */ + + b1 = (uint32_t)(*buffer++); + b2 = (uint32_t)(*buffer++); + b3 = (uint32_t)(*buffer++); + b4 = (uint32_t)(*buffer++); + *dest++ = (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + } +} + +/**************************************************************************** + * Name: stm32_copyfrompma + ****************************************************************************/ + +static inline void +stm32_copyfrompma(uint8_t *buffer, uint16_t pma, uint16_t nbytes) +{ + uint32_t *src; + int nwords = (nbytes + 3) >> 2; + int i; + + /* Copy loop. Source=packet memory, Dest=user buffer */ + + src = (uint32_t *)(STM32_USB_FS_RAM_BASE + (uint32_t)pma); + for (i = nwords; i != 0; i--) + { + /* Copy 16-bits from packet memory to user buffer. */ + + *(uint32_t *)buffer = *src++; + + /* Source address increments by 1*sizeof(uint16_t) = 2; Dest address + * increments by 2*sizeof(uint8_t) = 2. + */ + + buffer += 4; + } +} + +/**************************************************************************** + * Name: stm32_rqdequeue + ****************************************************************************/ + +static struct stm32_req_s *stm32_rqdequeue(struct stm32_ep_s *privep) +{ + struct stm32_req_s *ret = privep->head; + + if (ret) + { + privep->head = ret->flink; + if (!privep->head) + { + privep->tail = NULL; + } + + ret->flink = NULL; + } + + return ret; +} + +/**************************************************************************** + * Name: stm32_rqenqueue + ****************************************************************************/ + +static void stm32_rqenqueue(struct stm32_ep_s *privep, + struct stm32_req_s *req) +{ + req->flink = NULL; + if (!privep->head) + { + privep->head = req; + privep->tail = req; + } + else + { + privep->tail->flink = req; + privep->tail = req; + } +} + +/**************************************************************************** + * Name: stm32_abortrequest + ****************************************************************************/ + +static inline void +stm32_abortrequest(struct stm32_ep_s *privep, + struct stm32_req_s *privreq, int16_t result) +{ + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_REQABORTED), + (uint16_t)USB_EPNO(privep->ep.eplog)); + + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->req.callback(&privep->ep, &privreq->req); +} + +/**************************************************************************** + * Name: stm32_reqcomplete + ****************************************************************************/ + +static void stm32_reqcomplete(struct stm32_ep_s *privep, int16_t result) +{ + struct stm32_req_s *privreq; + irqstate_t flags; + + /* Remove the completed request at the head of the endpoint request list */ + + flags = enter_critical_section(); + privreq = stm32_rqdequeue(privep); + leave_critical_section(flags); + + if (privreq) + { + /* If endpoint 0, temporarily reflect the state of protocol stalled + * in the callback. + */ + + bool stalled = privep->stalled; + if (USB_EPNO(privep->ep.eplog) == EP0) + { + privep->stalled = (privep->dev->ep0state == EP0STATE_STALLED); + } + + /* Save the result in the request structure */ + + privreq->req.result = result; + + /* Callback to the request completion handler */ + + privreq->flink = NULL; + privreq->req.callback(&privep->ep, &privreq->req); + + /* Restore the stalled indication */ + + privep->stalled = stalled; + } +} + +/**************************************************************************** + * Name: stm32_epwrite + ****************************************************************************/ + +static void stm32_epwrite(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep, + const uint8_t *buf, uint32_t nbytes) +{ + uint8_t epno = USB_EPNO(privep->ep.eplog); + usbtrace(TRACE_WRITE(epno), nbytes); + + /* Check for a zero-length packet */ + + if (nbytes > 0) + { + /* Copy the data from the user buffer into packet memory for this + * endpoint + */ + + stm32_copytopma(buf, stm32_geteptxaddr(epno), nbytes); + } + + /* Send the packet (might be a null packet nbytes == 0) */ + + stm32_seteptxcount(epno, nbytes); + priv->txstatus = USB_EPR_STATTX_VALID; + + /* Indicate that there is data in the TX packet memory. This will be + * cleared when the next data out interrupt is received. + */ + + privep->txbusy = true; +} + +/**************************************************************************** + * Name: stm32_wrrequest_ep0 + * + * Description: + * Handle the ep0 state on writes. + * + ****************************************************************************/ + +inline static int stm32_wrrequest_ep0(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep) +{ + int ret; + ret = stm32_wrrequest(priv, privep); + priv->ep0state = ((ret == OK) ? EP0STATE_WRREQUEST : EP0STATE_IDLE); + return ret; +} + +/**************************************************************************** + * Name: stm32_wrrequest + ****************************************************************************/ + +static int stm32_wrrequest(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep) +{ + struct stm32_req_s *privreq; + uint8_t *buf; + uint8_t epno; + int nbytes; + int bytesleft; + + /* We get here when an IN endpoint interrupt occurs. So now we know that + * there is no TX transfer in progress. + */ + + privep->txbusy = false; + + /* Check the request from the head of the endpoint request queue */ + + privreq = stm32_rqpeek(privep); + if (!privreq) + { + /* There is no TX transfer in progress and no new pending TX + * requests to send. + */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINQEMPTY), 0); + return -ENOENT; + } + + epno = USB_EPNO(privep->ep.eplog); + uinfo("epno=%d req=%p: len=%zu xfrd=%zu nullpkt=%d\n", + epno, privreq, privreq->req.len, + privreq->req.xfrd, privep->txnullpkt); + UNUSED(epno); + + /* Get the number of bytes left to be sent in the packet */ + + bytesleft = privreq->req.len - privreq->req.xfrd; + nbytes = bytesleft; + +#warning "REVISIT: If the EP supports double buffering, then we can do better" + + /* Either (1) we are committed to sending the null packet + * (because txnullpkt == 1 && nbytes == 0), or (2) we have not yet send + * the last packet (nbytes > 0). + * In either case, it is appropriate to clearn txnullpkt now. + */ + + privep->txnullpkt = 0; + + /* If we are not sending a NULL packet, then clip the size to maxpacket + * and check if we need to send a following NULL packet. + */ + + if (nbytes > 0) + { + /* Either send the maxpacketsize or all of the remaining data in + * the request. + */ + + if (nbytes >= privep->ep.maxpacket) + { + nbytes = privep->ep.maxpacket; + + /* Handle the case where this packet is exactly the + * maxpacketsize. Do we need to send a zero-length packet + * in this case? + */ + + if (bytesleft == privep->ep.maxpacket && + (privreq->req.flags & USBDEV_REQFLAGS_NULLPKT) != 0) + { + privep->txnullpkt = 1; + } + } + } + + /* Send the packet (might be a null packet nbytes == 0) */ + + buf = privreq->req.buf + privreq->req.xfrd; + stm32_epwrite(priv, privep, buf, nbytes); + + /* Update for the next data IN interrupt */ + + privreq->req.xfrd += nbytes; + bytesleft = privreq->req.len - privreq->req.xfrd; + + /* If all of the bytes were sent (including any final null packet) + * then we are finished with the request buffer). + */ + + if (bytesleft == 0 && !privep->txnullpkt) + { + /* Return the write request to the class driver */ + + usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), + privreq->req.xfrd); + privep->txnullpkt = 0; + stm32_reqcomplete(privep, OK); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32_ep0_rdrequest + * + * Description: + * This function is called from the stm32_ep0out handler when the ep0state + * is EP0STATE_SETUP_OUT and upon new incoming data is available in the + * endpoint 0's buffer. + * This function will simply copy the OUT data into ep0data. + * + ****************************************************************************/ + +static inline int stm32_ep0_rdrequest(struct stm32_usbdev_s *priv) +{ + uint32_t src; + int pmalen; + int readlen; + + /* Get the number of bytes to read from packet memory */ + + pmalen = stm32_geteprxcount(EP0); + + uinfo("EP0: pmalen=%d\n", pmalen); + usbtrace(TRACE_READ(EP0), pmalen); + + /* Read the data into our special buffer for SETUP data */ + + readlen = MIN(CONFIG_USBDEV_SETUP_MAXDATASIZE, pmalen); + src = stm32_geteprxaddr(EP0); + + /* Receive the next packet */ + + stm32_copyfrompma(&priv->ep0data[0], src, readlen); + + /* Now we can process the setup command */ + + priv->ep0state = EP0STATE_SETUP_READY; + priv->ep0datlen = readlen; + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUTDATA), + readlen); + + stm32_ep0setup(priv); + priv->ep0datlen = 0; /* mark the date consumed */ + + return OK; +} + +/**************************************************************************** + * Name: stm32_rdrequest + ****************************************************************************/ + +static int stm32_rdrequest(struct stm32_usbdev_s *priv, + struct stm32_ep_s *privep) +{ + struct stm32_req_s *privreq; + uint32_t src; + uint8_t *dest; + uint8_t epno; + int pmalen; + int readlen; + + /* Check the request from the head of the endpoint request queue */ + + epno = USB_EPNO(privep->ep.eplog); + privreq = stm32_rqpeek(privep); + if (!privreq) + { + /* Incoming data available in PMA, but no packet to receive the data. + * Mark that the RX data is pending and hope that a packet is returned + * soon. + */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTQEMPTY), epno); + return -ENOENT; + } + + uinfo("EP%d: len=%zu xfrd=%zu\n", + epno, privreq->req.len, privreq->req.xfrd); + + /* Ignore any attempt to receive a zero length packet */ + + if (privreq->req.len == 0) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPOUTNULLPACKET), 0); + stm32_reqcomplete(privep, OK); + return OK; + } + + usbtrace(TRACE_READ(USB_EPNO(privep->ep.eplog)), privreq->req.xfrd); + + /* Get the source and destination transfer addresses */ + + dest = privreq->req.buf + privreq->req.xfrd; + src = stm32_geteprxaddr(epno); + + /* Get the number of bytes to read from packet memory */ + + pmalen = stm32_geteprxcount(epno); + readlen = MIN(privreq->req.len, pmalen); + + /* Receive the next packet */ + + stm32_copyfrompma(dest, src, readlen); + + /* If the receive buffer is full or this is a partial packet, + * then we are finished with the request buffer). + */ + + privreq->req.xfrd += readlen; + if (pmalen < privep->ep.maxpacket || privreq->req.xfrd >= privreq->req.len) + { + /* Return the read request to the class driver. */ + + usbtrace(TRACE_COMPLETE(epno), privreq->req.xfrd); + stm32_reqcomplete(privep, OK); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32_cancelrequests + ****************************************************************************/ + +static void stm32_cancelrequests(struct stm32_ep_s *privep) +{ + while (!stm32_rqempty(privep)) + { + usbtrace(TRACE_COMPLETE(USB_EPNO(privep->ep.eplog)), + (stm32_rqpeek(privep))->req.xfrd); + stm32_reqcomplete(privep, -ESHUTDOWN); + } +} + +/**************************************************************************** + * Interrupt Level Processing + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_dispatchrequest + ****************************************************************************/ + +static void stm32_dispatchrequest(struct stm32_usbdev_s *priv) +{ + int ret; + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_DISPATCH), 0); + if (priv && priv->driver) + { + /* Forward to the control request to the class driver implementation */ + + ret = CLASS_SETUP(priv->driver, &priv->usbdev, &priv->ctrl, + priv->ep0data, priv->ep0datlen); + if (ret < 0) + { + /* Stall on failure */ + + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DISPATCHSTALL), 0); + priv->ep0state = EP0STATE_STALLED; + } + } +} + +/**************************************************************************** + * Name: stm32_epdone + ****************************************************************************/ + +static void stm32_epdone(struct stm32_usbdev_s *priv, uint8_t epno) +{ + struct stm32_ep_s *privep; + uint16_t epr; + + /* Decode and service non control endpoints interrupt */ + + epr = stm32_getreg(STM32_USB_EPR(epno)); + privep = &priv->eplist[epno]; + + /* OUT: host-to-device + * CTR_RX is set by the hardware when an OUT/SETUP transaction + * successfully completed on this endpoint. + */ + + if ((epr & USB_EPR_CTR_RX) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTDONE), epr); + + /* Handle read requests. First check if a read request is available to + * accept the host data. + */ + + if (!stm32_rqempty(privep)) + { + /* Read host data into the current read request */ + + stm32_rdrequest(priv, privep); + + /* "After the received data is processed, the application software + * should set the STAT_RX bits to '11' (Valid) in the USB_EPnR, + * enabling further transactions. " + */ + + priv->rxstatus = USB_EPR_STATRX_VALID; + } + + /* NAK further OUT packets if there there no more read requests */ + + if (stm32_rqempty(privep)) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPOUTPENDING), + (uint16_t)epno); + + /* Mark the RX processing as pending and NAK any OUT actions + * on this endpoint. "While the STAT_RX bits are equal to '10' + * (NAK), any OUT request addressed to that endpoint is NAKed, + * indicating a flow control condition: the USB host will retry + * the transaction until it succeeds." + */ + + priv->rxstatus = USB_EPR_STATRX_NAK; + priv->rxpending = true; + } + + /* Clear the interrupt status and set the new RX status */ + + stm32_clrepctrrx(epno); + stm32_seteprxstatus(epno, priv->rxstatus); + } + + /* IN: device-to-host + * CTR_TX is set when an IN transaction successfully completes on + * an endpoint + */ + + else if ((epr & USB_EPR_CTR_TX) != 0) + { + /* Clear interrupt status */ + + stm32_clrepctrtx(epno); + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPINDONE), epr); + + /* Handle write requests */ + + priv->txstatus = USB_EPR_STATTX_NAK; + if (epno == EP0) + { + stm32_wrrequest_ep0(priv, privep); + } + else + { + stm32_wrrequest(priv, privep); + } + + /* Set the new TX status */ + + stm32_seteptxstatus(epno, priv->txstatus); + } +} + +/**************************************************************************** + * Name: stm32_setdevaddr + ****************************************************************************/ + +static void stm32_setdevaddr(struct stm32_usbdev_s *priv, uint8_t value) +{ + int epno; + + /* Set address in every allocated endpoint */ + + for (epno = 0; epno < STM32_NENDPOINTS; epno++) + { + if (stm32_epreserved(priv, epno)) + { + stm32_setepaddress((uint8_t)epno, (uint8_t)epno); + } + } + + /* Set the device address and enable function */ + + stm32_putreg(value | USB_DADDR_EF, STM32_USB_DADDR); +} + +/**************************************************************************** + * Name: stm32_ep0setup + ****************************************************************************/ + +static void stm32_ep0setup(struct stm32_usbdev_s *priv) +{ + struct stm32_ep_s *ep0 = &priv->eplist[EP0]; + struct stm32_req_s *privreq = stm32_rqpeek(ep0); + struct stm32_ep_s *privep; + union wb_u value; + union wb_u index; + union wb_u len; + union wb_u response; + bool handled = false; + uint8_t epno; + int nbytes = 0; /* Assume zero-length packet */ + + /* Terminate any pending requests (doesn't work if the pending request + * was a zero-length transfer!) + */ + + while (!stm32_rqempty(ep0)) + { + int16_t result = OK; + if (privreq->req.xfrd != privreq->req.len) + { + result = -EPROTO; + } + + usbtrace(TRACE_COMPLETE(ep0->ep.eplog), privreq->req.xfrd); + stm32_reqcomplete(ep0, result); + } + + /* Assume NOT stalled; no TX in progress */ + + ep0->stalled = 0; + ep0->txbusy = 0; + + value.w = 0; + index.w = 0; + len.w = 0; + response.w = 0; + + /* Check to see if called from the DATA phase of a SETUP Transfer */ + + if (priv->ep0state != EP0STATE_SETUP_READY) + { + /* Not the data phase */ + + /* Get a 32-bit PMA address and use that to get the 8-byte setup + * request + */ + + stm32_copyfrompma((uint8_t *)&priv->ctrl, stm32_geteprxaddr(EP0), + USB_SIZEOF_CTRLREQ); + + /* And extract the little-endian 16-bit values to host order */ + + value.w = GETUINT16(priv->ctrl.value); + index.w = GETUINT16(priv->ctrl.index); + len.w = GETUINT16(priv->ctrl.len); + + uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n", + priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w); + + /* Is this an setup with OUT and data of length > 0 */ + + if (USB_REQ_ISOUT(priv->ctrl.type) && len.w > 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPOUT), len.w); + + /* At this point priv->ctrl is the setup packet. */ + + priv->ep0state = EP0STATE_SETUP_OUT; + return; + } + else + { + priv->ep0state = EP0STATE_SETUP_READY; + } + } + + /* Dispatch any non-standard requests */ + + if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_NOSTDREQ), priv->ctrl.type); + + /* Let the class implementation handle all non-standar requests */ + + stm32_dispatchrequest(priv); + return; + } + + /* Handle standard request. Pick off the things of interest to the + * USB device controller driver; pass what is left to the class driver + */ + + switch (priv->ctrl.req) + { + case USB_REQ_GETSTATUS: + { + /* type: device-to-host; recipient = device, interface, endpoint + * value: 0 + * index: zero interface endpoint + * len: 2; data = status + */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSTATUS), + priv->ctrl.type); + if (len.w != 2 || (priv->ctrl.type & + USB_REQ_DIR_IN) == 0 || + index.b[MSB] != 0 || value.w != 0) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), 0); + priv->ep0state = EP0STATE_STALLED; + } + else + { + switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK) + { + case USB_REQ_RECIPIENT_ENDPOINT: + { + epno = USB_EPNO(index.b[LSB]); + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EPGETSTATUS), + epno); + if (epno >= STM32_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPGETSTATUS), + epno); + priv->ep0state = EP0STATE_STALLED; + } + else + { + response.w = 0; /* Not stalled */ + nbytes = 2; /* Response size: 2 bytes */ + + if (USB_ISEPIN(index.b[LSB])) + { + /* IN endpoint */ + + if (stm32_eptxstalled(epno)) + { + /* IN Endpoint stalled */ + + response.b[LSB] = 1; /* Stalled */ + } + } + else + { + /* OUT endpoint */ + + if (stm32_eprxstalled(epno)) + { + /* OUT Endpoint stalled */ + + response.b[LSB] = 1; /* Stalled */ + } + } + } + } + break; + + case USB_REQ_RECIPIENT_DEVICE: + { + if (index.w == 0) + { + usbtrace(TRACE_INTDECODE( + STM32_TRACEINTID_DEVGETSTATUS), 0); + + /* Features: Remote Wakeup=YES; selfpowered=? */ + + response.w = 0; + response.b[LSB] = (priv->selfpowered << + USB_FEATURE_SELFPOWERED) | + (1 << USB_FEATURE_REMOTEWAKEUP); + nbytes = 2; /* Response size: 2 bytes */ + } + else + { + usbtrace(TRACE_DEVERROR( + STM32_TRACEERR_BADDEVGETSTATUS), 0); + priv->ep0state = EP0STATE_STALLED; + } + } + break; + + case USB_REQ_RECIPIENT_INTERFACE: + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_IFGETSTATUS), 0); + response.w = 0; + nbytes = 2; /* Response size: 2 bytes */ + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETSTATUS), 0); + priv->ep0state = EP0STATE_STALLED; + } + break; + } + } + } + break; + + case USB_REQ_CLEARFEATURE: + { + /* type: host-to-device; recipient = device, interface or endpoint + * value: feature selector + * index: zero interface endpoint; + * len: zero, data = none + */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_CLEARFEATURE), + priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + /* Let the class implementation handle all recipients + * (except for the endpoint recipient) + */ + + stm32_dispatchrequest(priv); + handled = true; + } + else + { + /* Endpoint recipient */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = 0; + stm32_epstall(&privep->ep, true); + } + else + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADCLEARFEATURE), 0); + priv->ep0state = EP0STATE_STALLED; + } + } + } + break; + + case USB_REQ_SETFEATURE: + { + /* type: host-to-device; recipient = device, interface, endpoint + * value: feature selector + * index: zero interface endpoint; + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETFEATURE), + priv->ctrl.type); + if (((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE) && + value.w == USB_FEATURE_TESTMODE) + { + /* Special case recipient=device test mode */ + + uinfo("test mode: %d\n", index.w); + } + else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_ENDPOINT) + { + /* The class driver handles all recipients except + * recipient=endpoint + */ + + stm32_dispatchrequest(priv); + handled = true; + } + else + { + /* Handler recipient=endpoint */ + + epno = USB_EPNO(index.b[LSB]); + if (epno < STM32_NENDPOINTS && index.b[MSB] == 0 && + value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0) + { + privep = &priv->eplist[epno]; + privep->halted = 1; + stm32_epstall(&privep->ep, false); + } + else + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETFEATURE), 0); + priv->ep0state = EP0STATE_STALLED; + } + } + } + break; + + case USB_REQ_SETADDRESS: + { + /* type: host-to-device; recipient = device + * value: device address + * index: 0 + * len: 0; data = none + */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPSETADDRESS), + value.w); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) != + USB_REQ_RECIPIENT_DEVICE || + index.w != 0 || len.w != 0 || value.w > 127) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETADDRESS), 0); + priv->ep0state = EP0STATE_STALLED; + } + /* Note that setting of the device address will be deferred. + * A zero-length packet will be sent and the device address + * will be set when the zero- length packet transfer completes. + */ + } + break; + + case USB_REQ_GETDESCRIPTOR: + /* type: device-to-host; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + case USB_REQ_SETDESCRIPTOR: + /* type: host-to-device; recipient = device + * value: descriptor type and index + * index: 0 or language ID; + * len: descriptor len; data = descriptor + */ + + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETDESC), + priv->ctrl.type); + + /* The request seems valid... + * let the class implementation handle it + */ + + stm32_dispatchrequest(priv); + handled = true; + } + break; + + case USB_REQ_GETCONFIGURATION: + /* type: device-to-host; recipient = device + * value: 0; + * index: 0; + * len: 1; data = configuration value + */ + + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETCONFIG), + priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE && + value.w == 0 && index.w == 0 && len.w == 1) + { + /* The request seems valid... + * let the class implementation handle it + */ + + stm32_dispatchrequest(priv); + handled = true; + } + else + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADGETCONFIG), 0); + priv->ep0state = EP0STATE_STALLED; + } + } + break; + + case USB_REQ_SETCONFIGURATION: + /* type: host-to-device; recipient = device + * value: configuration value + * index: 0; + * len: 0; data = none + */ + + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SETCONFIG), + priv->ctrl.type); + if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) == + USB_REQ_RECIPIENT_DEVICE && + index.w == 0 && len.w == 0) + { + /* The request seems valid... + * let the class implementation handle it + */ + + stm32_dispatchrequest(priv); + handled = true; + } + else + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADSETCONFIG), 0); + priv->ep0state = EP0STATE_STALLED; + } + } + break; + + case USB_REQ_GETINTERFACE: + /* type: device-to-host; recipient = interface + * value: 0 + * index: interface; + * len: 1; data = alt interface + */ + + case USB_REQ_SETINTERFACE: + /* type: host-to-device; recipient = interface + * value: alternate setting + * index: interface; + * len: 0; data = none + */ + + { + /* Let the class implementation handle the request */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_GETSETIF), + priv->ctrl.type); + stm32_dispatchrequest(priv); + handled = true; + } + break; + + case USB_REQ_SYNCHFRAME: + /* type: device-to-host; recipient = endpoint + * value: 0 + * index: endpoint; + * len: 2; data = frame number + */ + + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SYNCHFRAME), 0); + } + break; + + default: + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDCTRLREQ), + priv->ctrl.req); + priv->ep0state = EP0STATE_STALLED; + } + break; + } + + /* At this point, the request has been handled and there are three possible + * outcomes: + * + * 1. The setup request was successfully handled above and a response + * packet must be sent (may be a zero length packet). + * 2. The request was successfully handled by the class implementation. + * In case, the EP0 IN response has already been queued and the local + * variable 'handled' will be set to true and + * ep0state != EP0STATE_STALLED; + * 3. An error was detected in either the above logic or by the class + * implementation logic. In either case, priv->state will be set + * EP0STATE_STALLED to indicate this case. + * + * NOTE: + * Non-standard requests are a special case. They are handled by the + * class implementation and this function returned early above, skipping + * this logic altogether. + */ + + if (priv->ep0state != EP0STATE_STALLED && !handled) + { + /* We will response. First, restrict the data length to the length + * requested in the setup packet + */ + + if (nbytes > len.w) + { + nbytes = len.w; + } + + /* Send the response (might be a zero-length packet) */ + + stm32_epwrite(priv, ep0, response.b, nbytes); + priv->ep0state = EP0STATE_IDLE; + } +} + +/**************************************************************************** + * Name: stm32_ep0in + ****************************************************************************/ + +static void stm32_ep0in(struct stm32_usbdev_s *priv) +{ + /* There is no longer anything in the EP0 TX packet memory */ + + priv->eplist[EP0].txbusy = false; + + /* Are we processing the completion of one packet of an outgoing request + * from the class driver? + */ + + if (priv->ep0state == EP0STATE_WRREQUEST) + { + stm32_wrrequest_ep0(priv, &priv->eplist[EP0]); + } + + /* No.. Are we processing the completion of a status response? */ + + else if (priv->ep0state == EP0STATE_IDLE) + { + /* Look at the saved SETUP command. Was it a SET ADDRESS request? + * If so, then now is the time to set the address. + */ + + if (priv->ctrl.req == USB_REQ_SETADDRESS && + (priv->ctrl.type & REQRECIPIENT_MASK) == + (USB_REQ_TYPE_STANDARD | USB_REQ_RECIPIENT_DEVICE)) + { + union wb_u value; + value.w = GETUINT16(priv->ctrl.value); + stm32_setdevaddr(priv, value.b[LSB]); + } + } + else + { + priv->ep0state = EP0STATE_STALLED; + } +} + +/**************************************************************************** + * Name: stm32_ep0out + ****************************************************************************/ + +static void stm32_ep0out(struct stm32_usbdev_s *priv) +{ + int ret; + + struct stm32_ep_s *privep = &priv->eplist[EP0]; + switch (priv->ep0state) + { + case EP0STATE_RDREQUEST: /* Read request in progress */ + case EP0STATE_IDLE: /* No transfer in progress */ + ret = stm32_rdrequest(priv, privep); + priv->ep0state = ((ret == OK) ? EP0STATE_RDREQUEST : EP0STATE_IDLE); + break; + + case EP0STATE_SETUP_OUT: /* SETUP was waiting for data */ + ret = stm32_ep0_rdrequest(priv); /* Off load the data and run the + * last set up command with the OUT + * data + */ + priv->ep0state = EP0STATE_IDLE; /* There is no notion of receiving OUT + * data greater then the length of + * CONFIG_USBDEV_SETUP_MAXDATASIZE + * so we are done + */ + break; + + default: + /* Unexpected state OR host aborted the OUT transfer before it + * completed, STALL the endpoint in either case + */ + + priv->ep0state = EP0STATE_STALLED; + break; + } +} + +/**************************************************************************** + * Name: stm32_ep0done + ****************************************************************************/ + +static inline void stm32_ep0done(struct stm32_usbdev_s *priv, uint16_t istr) +{ + uint16_t epr; + + /* Initialize RX and TX status. We shouldn't have to actually look at the + * status because the hardware is supposed to set the both RX and TX status + * to NAK when an EP0 SETUP occurs (of course, this might not be a setup) + */ + + priv->rxstatus = USB_EPR_STATRX_NAK; + priv->txstatus = USB_EPR_STATTX_NAK; + + /* Set both RX and TX status to NAK */ + + stm32_seteprxstatus(EP0, USB_EPR_STATRX_NAK); + stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK); + + /* Check the direction bit to determine if this the completion of an EP0 + * packet sent to or received from the host PC. + */ + + if ((istr & USB_ISTR_DIR) == 0) + { + /* EP0 IN: device-to-host (DIR=0) */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0IN), istr); + stm32_clrepctrtx(EP0); + stm32_ep0in(priv); + } + else + { + /* EP0 OUT: host-to-device (DIR=1) */ + + epr = stm32_getreg(STM32_USB_EPR(EP0)); + + /* CTR_TX is set when an IN transaction successfully + * completes on an endpoint + */ + + if ((epr & USB_EPR_CTR_TX) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0INDONE), epr); + stm32_clrepctrtx(EP0); + stm32_ep0in(priv); + } + + /* SETUP is set by the hardware when the last completed + * transaction was a control endpoint SETUP + */ + + else if ((epr & USB_EPR_SETUP) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0SETUPDONE), epr); + stm32_clrepctrrx(EP0); + stm32_ep0setup(priv); + } + + /* Set by the hardware when an OUT/SETUP transaction successfully + * completed on this endpoint. + */ + + else if ((epr & USB_EPR_CTR_RX) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_EP0OUTDONE), epr); + stm32_clrepctrrx(EP0); + stm32_ep0out(priv); + } + + /* None of the above */ + + else + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0BADCTR), epr); + return; /* Does this ever happen? */ + } + } + + /* Make sure that the EP0 packet size is still OK (superstitious?) */ + + stm32_seteprxcount(EP0, STM32_EP0MAXPACKET); + + /* Now figure out the new RX/TX status. Here are all possible + * consequences of the above EP0 operations: + * + * rxstatus txstatus ep0state MEANING + * -------- -------- --------- --------------------------------- + * NAK NAK IDLE Nothing happened + * NAK VALID IDLE EP0 response sent from USBDEV driver + * NAK VALID WRREQUEST EP0 response sent from class driver + * NAK --- STALL Some protocol error occurred + * + * First handle the STALL condition: + */ + + if (priv->ep0state == EP0STATE_STALLED) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EP0SETUPSTALLED), + priv->ep0state); + priv->rxstatus = USB_EPR_STATRX_STALL; + priv->txstatus = USB_EPR_STATTX_STALL; + } + + /* Was a transmission started? If so, txstatus will be VALID. The + * only special case to handle is when both are set to NAK. In that + * case, we need to set RX status to VALID in order to accept the next + * SETUP request. + */ + + else if (priv->rxstatus == USB_EPR_STATRX_NAK && + priv->txstatus == USB_EPR_STATTX_NAK) + { + priv->rxstatus = USB_EPR_STATRX_VALID; + } + + /* Now set the new TX and RX status */ + + stm32_seteprxstatus(EP0, priv->rxstatus); + stm32_seteptxstatus(EP0, priv->txstatus); +} + +/**************************************************************************** + * Name: stm32_transfer + ****************************************************************************/ + +static void stm32_transfer(struct stm32_usbdev_s *priv) +{ + uint8_t epno; + uint16_t istr; + + /* Stay in loop while LP interrupts are pending */ + + while (((istr = stm32_getreg(STM32_USB_ISTR)) & USB_ISTR_CTR) != 0) + { + /* Extract highest priority endpoint number */ + + epno = (uint8_t)(istr & USB_ISTR_EPID_MASK); + + /* Handle EP0 completion events */ + + if (epno == 0) + { + stm32_ep0done(priv, istr); + } + + /* Handle other endpoint completion events */ + + else + { + stm32_epdone(priv, epno); + } + } +} + +/**************************************************************************** + * Name: stm32_interrupt + ****************************************************************************/ + +static int stm32_interrupt(int irq, void *context, void *arg) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct stm32_usbdev_s *priv = &g_usbdev; + uint32_t istr = stm32_getreg(STM32_USB_ISTR); + + usbtrace(TRACE_INTENTRY(STM32_TRACEINTID_INTERRUPT), istr); + + /* Handle Reset interrupts. When this event occurs, the peripheral is left + * in the same conditions it is left by the system reset (but with the + * USB controller enabled). + */ + + if ((istr & USB_ISTR_RESET) != 0) + { + /* Reset interrupt received. Clear the RESET interrupt status. */ + + stm32_putreg(~USB_ISTR_RESET, STM32_USB_ISTR); + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_RESET), istr); + + /* Restore our power-up state and exit now because istr is no longer + * valid. + */ + + stm32_reset(priv); + goto exit_interrupt; + } + + /* Handle Wakeup interrupts. + * This interrupt is only enable while the USB is suspended. + */ + + if ((istr & USB_ISTR_WKUP & priv->imask) != 0) + { + /* Wakeup interrupt received. Clear the WKUP interrupt status. The + * cause of the resume is indicated in the FNR register + */ + + stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR); + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_WKUP), + stm32_getreg(STM32_USB_FNR)); + + /* Perform the wakeup action */ + + stm32_initresume(priv); + priv->rsmstate = RSMSTATE_IDLE; + + /* Disable ESOF polling, disable the wakeup interrupt, and + * re-enable the suspend interrupt. Clear any pending SUSP + * interrupts. + */ + + stm32_setimask(priv, USB_CNTR_SUSPM, USB_CNTR_ESOFM | USB_CNTR_WKUPM); + stm32_putreg(~USB_CNTR_SUSPM, STM32_USB_ISTR); + } + + if ((istr & USB_ISTR_SUSP & priv->imask) != 0) + { + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_SUSP), 0); + stm32_suspend(priv); + + /* Clear of the ISTR bit must be done after setting + * of USB_CNTR_FSUSP + */ + + stm32_putreg(~USB_ISTR_SUSP, STM32_USB_ISTR); + } + + if ((istr & USB_ISTR_ESOF & priv->imask) != 0) + { + stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR); + + /* Resume handling timing is made with ESOFs */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_ESOF), 0); + stm32_esofpoll(priv); + } + + if ((istr & USB_ISTR_CTR & priv->imask) != 0) + { + /* Low priority endpoint correct transfer interrupt */ + + usbtrace(TRACE_INTDECODE(STM32_TRACEINTID_CTR), istr); + stm32_transfer(priv); + } + +exit_interrupt: + usbtrace(TRACE_INTEXIT(STM32_TRACEINTID_INTERRUPT), + stm32_getreg(STM32_USB_EP0R)); + return OK; +} + +/**************************************************************************** + * Name: stm32_setimask + ****************************************************************************/ + +static void +stm32_setimask(struct stm32_usbdev_s *priv, + uint16_t setbits, uint16_t clrbits) +{ + uint32_t regval; + + /* Adjust the interrupt mask bits in the shadow copy first */ + + priv->imask &= ~clrbits; + priv->imask |= setbits; + + /* Then make the interrupt mask bits in the CNTR register match the shadow + * register (Hmmm... who is shadowing whom?) + */ + + regval = stm32_getreg(STM32_USB_CNTR); + regval &= ~USB_CNTR_ALLINTS; + regval |= priv->imask; + stm32_putreg(regval, STM32_USB_CNTR); +} + +/**************************************************************************** + * Suspend/Resume Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_suspend + ****************************************************************************/ + +static void stm32_suspend(struct stm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Notify the class driver of the suspend event */ + + if (priv->driver) + { + CLASS_SUSPEND(priv->driver, &priv->usbdev); + } + + /* Disable ESOF polling, disable the SUSP interrupt, and enable the WKUP + * interrupt. Clear any pending WKUP interrupt. + */ + + stm32_setimask(priv, USB_CNTR_WKUPM, USB_CNTR_ESOFM | USB_CNTR_SUSPM); + stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR); + + /* Set the FSUSP bit in the CNTR register. This activates suspend mode + * within the USB peripheral and disables further SUSP interrupts. + */ + + regval = stm32_getreg(STM32_USB_CNTR); + regval |= USB_CNTR_FSUSP; + stm32_putreg(regval, STM32_USB_CNTR); + + /* Let the board-specific logic know that we have entered the suspend + * state + */ + + stm32_usbsuspend((struct usbdev_s *)priv, false); +} + +/**************************************************************************** + * Name: stm32_initresume + ****************************************************************************/ + +static void stm32_initresume(struct stm32_usbdev_s *priv) +{ + /* This function is called when either (1) a WKUP interrupt is received + * from the host PC, or (2) the class device implementation calls the + * wakeup() method. + */ + + /* Reset FSUSP bit and enable normal interrupt handling */ + + stm32_usbsuspend((struct usbdev_s *)priv, true); + + stm32_putreg(STM32_CNTR_SETUP, STM32_USB_CNTR); + + /* Notify the class driver of the resume event */ + + if (priv->driver) + { + CLASS_RESUME(priv->driver, &priv->usbdev); + } +} + +/**************************************************************************** + * Name: stm32_esofpoll + ****************************************************************************/ + +static void stm32_esofpoll(struct stm32_usbdev_s *priv) +{ + uint32_t regval; + + /* Called periodically from ESOF interrupt after RSMSTATE_STARTED */ + + switch (priv->rsmstate) + { + /* One ESOF after internal resume requested */ + + case RSMSTATE_STARTED: + regval = stm32_getreg(STM32_USB_CNTR); + regval |= USB_CNTR_RESUME; + stm32_putreg(regval, STM32_USB_CNTR); + priv->rsmstate = RSMSTATE_WAITING; + priv->nesofs = 10; + break; + + /* Countdown before completing the operation */ + + case RSMSTATE_WAITING: + priv->nesofs--; + if (priv->nesofs == 0) + { + /* Okay.. we are ready to resume normal operation */ + + regval = stm32_getreg(STM32_USB_CNTR); + regval &= (~USB_CNTR_RESUME); + stm32_putreg(regval, STM32_USB_CNTR); + priv->rsmstate = RSMSTATE_IDLE; + + /* Disable ESOF polling, disable the SUSP interrupt, and enable + * the WKUP interrupt. Clear any pending WKUP interrupt. + */ + + stm32_setimask(priv, + USB_CNTR_WKUPM, USB_CNTR_ESOFM | USB_CNTR_SUSPM); + stm32_putreg(~USB_ISTR_WKUP, STM32_USB_ISTR); + } + break; + + case RSMSTATE_IDLE: + default: + priv->rsmstate = RSMSTATE_IDLE; + break; + } +} + +/**************************************************************************** + * Endpoint Helpers + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_epreserve + ****************************************************************************/ + +static inline struct stm32_ep_s * +stm32_epreserve(struct stm32_usbdev_s *priv, uint8_t epset) +{ + struct stm32_ep_s *privep = NULL; + irqstate_t flags; + int epndx = 0; + + flags = enter_critical_section(); + epset &= priv->epavail; + if (epset) + { + /* Select the lowest bit in the set of matching, available endpoints + * (skipping EP0) + */ + + for (epndx = 1; epndx < STM32_NENDPOINTS; epndx++) + { + uint8_t bit = STM32_ENDP_BIT(epndx); + if ((epset & bit) != 0) + { + /* Mark the endpoint no longer available */ + + priv->epavail &= ~bit; + + /* And return the pointer to the standard endpoint structure */ + + privep = &priv->eplist[epndx]; + break; + } + } + } + + leave_critical_section(flags); + return privep; +} + +/**************************************************************************** + * Name: stm32_epunreserve + ****************************************************************************/ + +static inline void +stm32_epunreserve(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep) +{ + irqstate_t flags = enter_critical_section(); + priv->epavail |= STM32_ENDP_BIT(USB_EPNO(privep->ep.eplog)); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: stm32_epreserved + ****************************************************************************/ + +static inline bool +stm32_epreserved(struct stm32_usbdev_s *priv, int epno) +{ + return ((priv->epavail & STM32_ENDP_BIT(epno)) == 0); +} + +/**************************************************************************** + * Name: stm32_epallocpma + ****************************************************************************/ + +static int stm32_epallocpma(struct stm32_usbdev_s *priv) +{ + irqstate_t flags; + int bufno = ERROR; + int bufndx; + + flags = enter_critical_section(); + for (bufndx = 2; bufndx < STM32_NBUFFERS; bufndx++) + { + /* Check if this buffer is available */ + + uint8_t bit = STM32_BUFFER_BIT(bufndx); + if ((priv->bufavail & bit) != 0) + { + /* Yes.. Mark the buffer no longer available */ + + priv->bufavail &= ~bit; + + /* And return the index of the allocated buffer */ + + bufno = bufndx; + break; + } + } + + leave_critical_section(flags); + return bufno; +} + +/**************************************************************************** + * Name: stm32_epfreepma + ****************************************************************************/ + +static inline void +stm32_epfreepma(struct stm32_usbdev_s *priv, struct stm32_ep_s *privep) +{ + irqstate_t flags = enter_critical_section(); + priv->epavail |= STM32_ENDP_BIT(privep->bufno); + leave_critical_section(flags); +} + +/**************************************************************************** + * Endpoint operations + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_epconfigure + ****************************************************************************/ + +static int stm32_epconfigure(struct usbdev_ep_s *ep, + const struct usb_epdesc_s *desc, + bool last) +{ + struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; + uint16_t pma; + uint16_t setting; + uint16_t maxpacket; + uint8_t epno; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !desc) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + uerr("ERROR: ep=%p desc=%p\n", ep, desc); + return -EINVAL; + } +#endif + + /* Get the unadorned endpoint address */ + + epno = USB_EPNO(desc->addr); + usbtrace(TRACE_EPCONFIGURE, (uint16_t)epno); + DEBUGASSERT(epno == USB_EPNO(ep->eplog)); + + /* Set the requested type */ + + switch (desc->attr & USB_EP_ATTR_XFERTYPE_MASK) + { + case USB_EP_ATTR_XFER_INT: /* Interrupt endpoint */ + setting = USB_EPR_EPTYPE_INTERRUPT; + break; + + case USB_EP_ATTR_XFER_BULK: /* Bulk endpoint */ + setting = USB_EPR_EPTYPE_BULK; + break; + + case USB_EP_ATTR_XFER_ISOC: /* Isochronous endpoint */ +#warning "REVISIT: Need to review isochronous EP setup" + setting = USB_EPR_EPTYPE_ISOC; + break; + + case USB_EP_ATTR_XFER_CONTROL: /* Control endpoint */ + setting = USB_EPR_EPTYPE_CONTROL; + break; + + default: + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPTYPE), + (uint16_t)desc->type); + return -EINVAL; + } + + stm32_seteptype(epno, setting); + + /* Get the address of the PMA buffer allocated for this endpoint */ + +#warning "REVISIT: Should configure BULK EPs using double buffer feature" + pma = STM32_BUFNO2BUF(privep->bufno); + + /* Get the maxpacket size of the endpoint. */ + + maxpacket = GETUINT16(desc->mxpacketsize); + DEBUGASSERT(maxpacket <= STM32_MAXPACKET_SIZE); + ep->maxpacket = maxpacket; + + /* Get the subset matching the requested direction */ + + if (USB_ISEPIN(desc->addr)) + { + /* The full, logical EP number includes direction */ + + ep->eplog = USB_EPIN(epno); + + /* Set up TX; disable RX */ + + stm32_seteptxaddr(epno, pma); + stm32_seteptxstatus(epno, USB_EPR_STATTX_NAK); + stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS); + } + else + { + /* The full, logical EP number includes direction */ + + ep->eplog = USB_EPOUT(epno); + + /* Set up RX; disable TX */ + + stm32_seteprxaddr(epno, pma); + stm32_seteprxcount(epno, maxpacket); + stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID); + stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS); + } + + stm32_dumpep(epno); + return OK; +} + +/**************************************************************************** + * Name: stm32_epdisable + ****************************************************************************/ + +static int stm32_epdisable(struct usbdev_ep_s *ep) +{ + struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; + irqstate_t flags; + uint8_t epno; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + uerr("ERROR: ep=%p\n", ep); + return -EINVAL; + } +#endif + + epno = USB_EPNO(ep->eplog); + usbtrace(TRACE_EPDISABLE, epno); + + /* Cancel any ongoing activity */ + + flags = enter_critical_section(); + stm32_cancelrequests(privep); + + /* Disable TX; disable RX */ + + stm32_seteprxcount(epno, 0); + stm32_seteprxstatus(epno, USB_EPR_STATRX_DIS); + stm32_seteptxstatus(epno, USB_EPR_STATTX_DIS); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: stm32_epallocreq + ****************************************************************************/ + +static struct usbdev_req_s *stm32_epallocreq(struct usbdev_ep_s *ep) +{ + struct stm32_req_s *privreq; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + usbtrace(TRACE_EPALLOCREQ, USB_EPNO(ep->eplog)); + + privreq = kmm_malloc(sizeof(struct stm32_req_s)); + if (!privreq) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_ALLOCFAIL), 0); + return NULL; + } + + memset(privreq, 0, sizeof(struct stm32_req_s)); + return &privreq->req; +} + +/**************************************************************************** + * Name: stm32_epfreereq + ****************************************************************************/ + +static void stm32_epfreereq(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct stm32_req_s *privreq = (struct stm32_req_s *)req; + +#ifdef CONFIG_DEBUG_FEATURES + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + usbtrace(TRACE_EPFREEREQ, USB_EPNO(ep->eplog)); + + kmm_free(privreq); +} + +/**************************************************************************** + * Name: stm32_epsubmit + ****************************************************************************/ + +static int stm32_epsubmit(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct stm32_req_s *privreq = (struct stm32_req_s *)req; + struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; + struct stm32_usbdev_s *priv; + irqstate_t flags; + uint8_t epno; + int ret = OK; + +#ifdef CONFIG_DEBUG_FEATURES + if (!req || !req->callback || !req->buf || !ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + uerr("ERROR: req=%p callback=%p buf=%p ep=%p\n", + req, req->callback, req->buf, ep); + return -EINVAL; + } +#endif + + usbtrace(TRACE_EPSUBMIT, USB_EPNO(ep->eplog)); + priv = privep->dev; + +#ifdef CONFIG_DEBUG_FEATURES + if (!priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_NOTCONFIGURED), + priv->usbdev.speed); + uerr("ERROR: driver=%p\n", priv->driver); + return -ESHUTDOWN; + } +#endif + + /* Handle the request from the class driver */ + + epno = USB_EPNO(ep->eplog); + req->result = -EINPROGRESS; + req->xfrd = 0; + flags = enter_critical_section(); + + /* If we are stalled, then drop all requests on the floor */ + + if (privep->stalled) + { + stm32_abortrequest(privep, privreq, -EBUSY); + uerr("ERROR: stalled\n"); + ret = -EBUSY; + } + + /* Handle IN (device-to-host) requests. NOTE: If the class device is + * using the bi-directional EP0, then we assume that they intend the EP0 + * IN functionality. + */ + + else if (USB_ISEPIN(ep->eplog) || epno == EP0) + { + /* Add the new request to the request queue for the IN endpoint */ + + stm32_rqenqueue(privep, privreq); + usbtrace(TRACE_INREQQUEUED(epno), req->len); + + /* If the IN endpoint FIFO is available, then transfer the data now */ + + if (!privep->txbusy) + { + priv->txstatus = USB_EPR_STATTX_NAK; + if (epno == EP0) + { + ret = stm32_wrrequest_ep0(priv, privep); + } + else + { + ret = stm32_wrrequest(priv, privep); + } + + /* Set the new TX status */ + + stm32_seteptxstatus(epno, priv->txstatus); + } + } + + /* Handle OUT (host-to-device) requests */ + + else + { + /* Add the new request to the request queue for the OUT endpoint */ + + privep->txnullpkt = 0; + stm32_rqenqueue(privep, privreq); + usbtrace(TRACE_OUTREQQUEUED(epno), req->len); + + /* This there a incoming data pending the availability of a request? */ + + if (priv->rxpending) + { + /* Set STAT_RX bits to '11' in the USB_EPnR, enabling further + * transactions. "While the STAT_RX bits are equal to '10' + * (NAK), any OUT request addressed to that endpoint is NAKed, + * indicating a flow control condition: the USB host will retry + * the transaction until it succeeds." + */ + + priv->rxstatus = USB_EPR_STATRX_VALID; + stm32_seteprxstatus(epno, priv->rxstatus); + + /* Data is no longer pending */ + + priv->rxpending = false; + } + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: stm32_epcancel + ****************************************************************************/ + +static int stm32_epcancel(struct usbdev_ep_s *ep, struct usbdev_req_s *req) +{ + struct stm32_ep_s *privep = (struct stm32_ep_s *)ep; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_USB + if (!ep || !req) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + usbtrace(TRACE_EPCANCEL, USB_EPNO(ep->eplog)); + + flags = enter_critical_section(); + stm32_cancelrequests(privep); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: stm32_epstall + ****************************************************************************/ + +static int stm32_epstall(struct usbdev_ep_s *ep, bool resume) +{ + struct stm32_ep_s *privep; + struct stm32_usbdev_s *priv; + uint8_t epno; + uint16_t status; + irqstate_t flags; + +#ifdef CONFIG_DEBUG_USB + if (!ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + privep = (struct stm32_ep_s *)ep; + priv = (struct stm32_usbdev_s *)privep->dev; + epno = USB_EPNO(ep->eplog); + + /* STALL or RESUME the endpoint */ + + flags = enter_critical_section(); + usbtrace(resume ? TRACE_EPRESUME : TRACE_EPSTALL, USB_EPNO(ep->eplog)); + + /* Get status of the endpoint; stall the request if the endpoint is + * disabled + */ + + if (USB_ISEPIN(ep->eplog)) + { + status = stm32_geteptxstatus(epno); + } + else + { + status = stm32_geteprxstatus(epno); + } + + if (status == 0) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPDISABLED), 0); + + if (epno == 0) + { + priv->ep0state = EP0STATE_STALLED; + } + + leave_critical_section(flags); + return -ENODEV; + } + + /* Handle the resume condition */ + + if (resume) + { + /* Resuming a stalled endpoint */ + + usbtrace(TRACE_EPRESUME, epno); + privep->stalled = false; + + if (USB_ISEPIN(ep->eplog)) + { + /* IN endpoint */ + + if (stm32_eptxstalled(epno)) + { + stm32_clrtxdtog(epno); + + /* Restart any queued write requests */ + + priv->txstatus = USB_EPR_STATTX_NAK; + if (epno == EP0) + { + stm32_wrrequest_ep0(priv, privep); + } + else + { + stm32_wrrequest(priv, privep); + } + + /* Set the new TX status */ + + stm32_seteptxstatus(epno, priv->txstatus); + } + } + else + { + /* OUT endpoint */ + + if (stm32_eprxstalled(epno)) + { + if (epno == EP0) + { + /* After clear the STALL, enable the default endpoint + * receiver + */ + + stm32_seteprxcount(epno, ep->maxpacket); + } + else + { + stm32_clrrxdtog(epno); + } + + priv->rxstatus = USB_EPR_STATRX_VALID; + stm32_seteprxstatus(epno, USB_EPR_STATRX_VALID); + } + } + } + + /* Handle the stall condition */ + + else + { + usbtrace(TRACE_EPSTALL, epno); + privep->stalled = true; + + if (USB_ISEPIN(ep->eplog)) + { + /* IN endpoint */ + + priv->txstatus = USB_EPR_STATTX_STALL; + stm32_seteptxstatus(epno, USB_EPR_STATTX_STALL); + } + else + { + /* OUT endpoint */ + + priv->rxstatus = USB_EPR_STATRX_STALL; + stm32_seteprxstatus(epno, USB_EPR_STATRX_STALL); + } + } + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Device Controller Operations + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_allocep + ****************************************************************************/ + +static struct usbdev_ep_s *stm32_allocep(struct usbdev_s *dev, uint8_t epno, + bool in, uint8_t eptype) +{ + struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev; + struct stm32_ep_s *privep = NULL; + uint8_t epset = STM32_ENDP_ALLSET; + int bufno; + + usbtrace(TRACE_DEVALLOCEP, (uint16_t)epno); +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return NULL; + } +#endif + + /* Ignore any direction bits in the logical address */ + + epno = USB_EPNO(epno); + + /* A logical address of 0 means that any endpoint will do */ + + if (epno > 0) + { + /* Otherwise, we will return the endpoint structure only for the + * requested 'logical' endpoint. + * All of the other checks will still be performed. + * + * First, verify that the logical endpoint is in the range supported by + * by the hardware. + */ + + if (epno >= STM32_NENDPOINTS) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BADEPNO), (uint16_t)epno); + return NULL; + } + + /* Convert the logical address to a physical OUT endpoint address and + * remove all of the candidate endpoints from the bitset except for the + * the IN/OUT pair for this logical address. + */ + + epset = STM32_ENDP_BIT(epno); + } + + /* Check if the selected endpoint number is available */ + + privep = stm32_epreserve(priv, epset); + if (!privep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPRESERVE), (uint16_t)epset); + goto errout; + } + + /* Allocate a PMA buffer for this endpoint */ + +#warning "REVISIT: Should configure BULK EPs using double buffer feature" + bufno = stm32_epallocpma(priv); + if (bufno < 0) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_EPBUFFER), 0); + goto errout_with_ep; + } + + privep->bufno = (uint8_t)bufno; + return &privep->ep; + +errout_with_ep: + stm32_epunreserve(priv, privep); +errout: + return NULL; +} + +/**************************************************************************** + * Name: stm32_freeep + ****************************************************************************/ + +static void stm32_freeep(struct usbdev_s *dev, struct usbdev_ep_s *ep) +{ + struct stm32_usbdev_s *priv; + struct stm32_ep_s *privep; + +#ifdef CONFIG_DEBUG_USB + if (!dev || !ep) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return; + } +#endif + priv = (struct stm32_usbdev_s *)dev; + privep = (struct stm32_ep_s *)ep; + usbtrace(TRACE_DEVFREEEP, (uint16_t)USB_EPNO(ep->eplog)); + + if (priv && privep) + { + /* Free the PMA buffer assigned to this endpoint */ + + stm32_epfreepma(priv, privep); + + /* Mark the endpoint as available */ + + stm32_epunreserve(priv, privep); + } +} + +/**************************************************************************** + * Name: stm32_getframe + ****************************************************************************/ + +static int stm32_getframe(struct usbdev_s *dev) +{ + uint16_t fnr; + +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Return the last frame number detected by the hardware */ + + fnr = stm32_getreg(STM32_USB_FNR); + usbtrace(TRACE_DEVGETFRAME, fnr); + return (fnr & USB_FNR_FN_MASK); +} + +/**************************************************************************** + * Name: stm32_wakeup + ****************************************************************************/ + +static int stm32_wakeup(struct usbdev_s *dev) +{ + struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev; + irqstate_t flags; + + usbtrace(TRACE_DEVWAKEUP, 0); +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Start the resume sequence. The actual resume steps will be driven + * by the ESOF interrupt. + */ + + flags = enter_critical_section(); + stm32_initresume(priv); + priv->rsmstate = RSMSTATE_STARTED; + + /* Disable the SUSP interrupt (until we are fully resumed), disable + * the WKUP interrupt (we are already waking up), and enable the + * ESOF interrupt that will drive the resume operations. Clear any + * pending ESOF interrupt. + */ + + stm32_setimask(priv, USB_CNTR_ESOFM, USB_CNTR_WKUPM | USB_CNTR_SUSPM); + stm32_putreg(~USB_ISTR_ESOF, STM32_USB_ISTR); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: stm32_selfpowered + ****************************************************************************/ + +static int stm32_selfpowered(struct usbdev_s *dev, bool selfpowered) +{ + struct stm32_usbdev_s *priv = (struct stm32_usbdev_s *)dev; + + usbtrace(TRACE_DEVSELFPOWERED, (uint16_t)selfpowered); + +#ifdef CONFIG_DEBUG_USB + if (!dev) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -ENODEV; + } +#endif + + priv->selfpowered = selfpowered; + return OK; +} + +/**************************************************************************** + * Name: stm32_usbsuspend + * + * Description: + * Board logic must provide the stm32_usbsuspend logic if the USBDEV driver + * is used. This function is called whenever the USB enters or leaves + * suspend mode. This is an opportunity for the board logic to shutdown + * clocks, power, etc. while the USB is suspended. + * + ****************************************************************************/ + +void stm32_usbsuspend(struct usbdev_s *dev, bool resume) +{ + uint32_t regval; + + uinfo("resume: %d\n", resume); + regval = getreg32(STM32_USB_CNTR); + if (resume) + { + regval &= ~(USB_CNTR_FSUSP); + } + else + { + regval |= USB_CNTR_FSUSP; + } + putreg32(regval, STM32_USB_CNTR); +} + +/**************************************************************************** + * Name: stm32_pullup + ****************************************************************************/ + +static int stm32_pullup(struct usbdev_s *dev, bool enable) +{ + uint32_t regval; + irqstate_t flags; + + usbtrace(TRACE_DEVPULLUP, (uint16_t)enable); + + flags = enter_critical_section(); + regval = stm32_getreg(STM32_USB_BCDR); + if (enable) + { + /* Connect the device by setting the DP pull-up bit in the BCDR + * register. + */ + + regval |= USB_BCDR_DPPU; + } + else + { + /* Disconnect the device by clearing the DP pull-up bit in the BCDR + * register. + */ + + regval &= ~USB_BCDR_DPPU; + } + + stm32_putreg(regval, STM32_USB_BCDR); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Initialization/Reset + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_reset + ****************************************************************************/ + +static void stm32_reset(struct stm32_usbdev_s *priv) +{ + int epno; + + /* Put the USB controller in reset, disable all interrupts */ + + stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); + + /* Tell the class driver that we are disconnected. The class driver + * should then accept any new configurations. + */ + + CLASS_DISCONNECT(priv->driver, &priv->usbdev); + + /* Reset the device state structure */ + + priv->ep0state = EP0STATE_IDLE; + priv->rsmstate = RSMSTATE_IDLE; + priv->rxpending = false; + + /* Reset endpoints */ + + for (epno = 0; epno < STM32_NENDPOINTS; epno++) + { + struct stm32_ep_s *privep = &priv->eplist[epno]; + + /* Cancel any queued requests. Since they are canceled + * with status -ESHUTDOWN, then will not be requeued + * until the configuration is reset. NOTE: This should + * not be necessary... the CLASS_DISCONNECT above should + * result in the class implementation calling stm32_epdisable + * for each of its configured endpoints. + */ + + stm32_cancelrequests(privep); + + /* Reset endpoint status */ + + privep->stalled = false; + privep->halted = false; + privep->txbusy = false; + privep->txnullpkt = false; + } + + /* Re-configure the USB controller in its initial, unconnected state */ + + stm32_hwreset(priv); + priv->usbdev.speed = USB_SPEED_FULL; +} + +/**************************************************************************** + * Name: stm32_hwreset + ****************************************************************************/ + +static void stm32_hwreset(struct stm32_usbdev_s *priv) +{ + /* Put the USB controller into reset, clear all interrupt enables */ + + stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); + + /* Disable interrupts (and perhaps take the USB controller out of reset) */ + + priv->imask = 0; + stm32_putreg(priv->imask, STM32_USB_CNTR); + + /* Initialize EP0 */ + + stm32_seteptype(EP0, USB_EPR_EPTYPE_CONTROL); + stm32_seteptxstatus(EP0, USB_EPR_STATTX_NAK); + stm32_seteprxaddr(EP0, STM32_EP0_RXADDR); + stm32_seteprxcount(EP0, STM32_EP0MAXPACKET); + stm32_seteptxaddr(EP0, STM32_EP0_TXADDR); + stm32_clrstatusout(EP0); + stm32_seteprxstatus(EP0, USB_EPR_STATRX_VALID); + + /* Set the device to respond on default address */ + + stm32_setdevaddr(priv, 0); + + /* Clear any pending interrupts */ + + stm32_putreg(0, STM32_USB_ISTR); + + /* Enable interrupts at the USB controller */ + + stm32_setimask(priv, STM32_CNTR_SETUP, + (USB_CNTR_ALLINTS & ~STM32_CNTR_SETUP)); + stm32_dumpep(EP0); +} + +/**************************************************************************** + * Name: stm32_hwsetup + ****************************************************************************/ + +static void stm32_hwsetup(struct stm32_usbdev_s *priv) +{ + int epno; + +#ifdef CONFIG_STM32_STM32G47XX + /* FIXME + * Because stm32g4xxxx_rcc.c does not handle HSI48 for now, + * enable HSI48 clock for USB block here and wait till HSI48 is ready. + */ + + modifyreg32(STM32_RCC_CRRCR, RCC_CRRCR_HSI48ON, RCC_CRRCR_HSI48ON); + while (!(getreg32(STM32_RCC_CRRCR) & RCC_CRRCR_HSI48RDY)) + { + /* nothing to do here */ + } +#endif + + /* Power the USB controller, put the USB controller into reset, disable + * all USB interrupts + */ + + stm32_putreg(USB_CNTR_FRES | USB_CNTR_PDWN, STM32_USB_CNTR); + + /* Disconnect the device / disable the pull-up. We don't want the + * host to enumerate us until the class driver is registered. + */ + + stm32_pullup(&priv->usbdev, false); + + /* Initialize the device state structure. NOTE: many fields + * have the initial value of zero and, hence, are not explicitly + * initialized here. + */ + + memset(priv, 0, sizeof(struct stm32_usbdev_s)); + priv->usbdev.ops = &g_devops; + priv->usbdev.ep0 = &priv->eplist[EP0].ep; + priv->epavail = STM32_ENDP_ALLSET & ~STM32_ENDP_BIT(EP0); + priv->bufavail = STM32_BUFFER_ALLSET & ~STM32_BUFFER_EP0; + + /* Initialize the endpoint list */ + + for (epno = 0; epno < STM32_NENDPOINTS; epno++) + { + /* Set endpoint operations, reference to driver structure (not + * really necessary because there is only one controller), and + * the (physical) endpoint number which is just the index to the + * endpoint. + */ + + priv->eplist[epno].ep.ops = &g_epops; + priv->eplist[epno].dev = priv; + priv->eplist[epno].ep.eplog = epno; + + /* We will use a fixed maxpacket size for all endpoints (perhaps + * ISOC endpoints could have larger maxpacket???). A smaller + * packet size can be selected when the endpoint is configured. + */ + + priv->eplist[epno].ep.maxpacket = STM32_MAXPACKET_SIZE; + } + + /* Select a smaller endpoint size for EP0 */ + +#if STM32_EP0MAXPACKET < STM32_MAXPACKET_SIZE + priv->eplist[EP0].ep.maxpacket = STM32_EP0MAXPACKET; +#endif + + /* Configure the USB controller. USB uses the following GPIO pins: + * + * PA9 - VBUS + * PA10 - ID + * PA11 - DM + * PA12 - DP + * + * "As soon as the USB is enabled, these pins [DM and DP] are connected to + * the USB internal transceiver automatically." + */ + + /* Power up the USB controller, holding it in reset. There is a delay of + * about 1uS after applying power before the USB will behave predictably. + * A 5MS delay is more than enough. NOTE that we leave the USB controller + * in the reset state; the hardware will not be initialized until the + * class driver has been bound. + */ + + stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); + up_mdelay(5); +} + +/**************************************************************************** + * Name: stm32_hwshutdown + ****************************************************************************/ + +static void stm32_hwshutdown(struct stm32_usbdev_s *priv) +{ + priv->usbdev.speed = USB_SPEED_UNKNOWN; + + /* Disable all interrupts and force the USB controller into reset */ + + stm32_putreg(USB_CNTR_FRES, STM32_USB_CNTR); + + /* Clear any pending interrupts */ + + stm32_putreg(0, STM32_USB_ISTR); + + /* Disconnect the device / disable the pull-up */ + + stm32_pullup(&priv->usbdev, false); + + /* Power down the USB controller */ + + stm32_putreg(USB_CNTR_FRES | USB_CNTR_PDWN, STM32_USB_CNTR); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_usbinitialize + * Description: + * Initialize the USB driver + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void arm_usbinitialize(void) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct stm32_usbdev_s *priv = &g_usbdev; + + usbtrace(TRACE_DEVINIT, 0); + stm32_checksetup(); + + /* Check if VDDUSB is Valid */ + + putreg32(PWR_USBSCR_USB33DEN, STM32_PWR_USBSCR); + while (!(getreg32(STM32_PWR_VMSR) & PWR_VMSR_USB33RDY)) + { + } + + putreg32((PWR_USBSCR_USB33DEN | PWR_USBSCR_USB33SV), STM32_PWR_USBSCR); + + /* Configure USB GPIOs */ + + stm32_configgpio(GPIO_USB_DM); + stm32_configgpio(GPIO_USB_DP); + stm32_configgpio(GPIO_USB_SOF); + + /* Power up the USB controller, but leave it in the reset state */ + + stm32_hwsetup(priv); + + /* Remap the USB interrupt as needed + * (Only supported by the STM32 F3 family) + */ + + /* Attach USB controller interrupt handlers. The hardware will not be + * initialized and interrupts will not be enabled until the class device + * driver is bound. Getting the IRQs here only makes sure that we have + * them when we need them later. + */ + + if (irq_attach(STM32_IRQ_USB_FS, stm32_interrupt, NULL) != 0) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_IRQREGISTRATION), + (uint16_t)STM32_IRQ_USB_FS); + goto errout; + } + + return; + +errout: + arm_usbuninitialize(); +} + +/**************************************************************************** + * Name: arm_usbuninitialize + * Description: + * Initialize the USB driver + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void arm_usbuninitialize(void) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct stm32_usbdev_s *priv = &g_usbdev; + irqstate_t flags; + + flags = enter_critical_section(); + usbtrace(TRACE_DEVUNINIT, 0); + + /* Disable and detach the USB IRQs */ + + up_disable_irq(STM32_IRQ_USB_FS); + irq_detach(STM32_IRQ_USB_FS); + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVERREGISTERED), 0); + usbdev_unregister(priv->driver); + } + + /* Put the hardware in an inactive state */ + + stm32_hwshutdown(priv); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: usbdev_register + * + * Description: + * Register a USB device class driver. The class driver's bind() method + * will be called to bind it to a USB device driver. + * + ****************************************************************************/ + +int usbdev_register(struct usbdevclass_driver_s *driver) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct stm32_usbdev_s *priv = &g_usbdev; + int ret; + + usbtrace(TRACE_DEVREGISTER, 0); + +#ifdef CONFIG_DEBUG_USB + if (!driver || !driver->ops->bind || !driver->ops->unbind || + !driver->ops->disconnect || !driver->ops->setup) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } + + if (priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_DRIVER), 0); + return -EBUSY; + } +#endif + + /* First hook up the driver */ + + priv->driver = driver; + + /* Then bind the class driver */ + + ret = CLASS_BIND(driver, &priv->usbdev); + if (ret) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_BINDFAILED), (uint16_t) - ret); + } + else + { + /* Setup the USB controller -- enabling interrupts at the USB + * controller + */ + + stm32_hwreset(priv); + + /* Enable USB controller interrupts at the NVIC */ + + up_enable_irq(STM32_IRQ_USB_FS); + + /* Enable pull-up to connect the device. The host should enumerate us + * some time after this + */ + + stm32_pullup(&priv->usbdev, true); + priv->usbdev.speed = USB_SPEED_FULL; + } + + return ret; +} + +/**************************************************************************** + * Name: usbdev_unregister + * + * Description: + * Un-register usbdev class driver. If the USB device is connected to a + * USB host, it will first disconnect(). The driver is also requested to + * unbind() and clean up any device state, before this procedure finally + * returns. + * + ****************************************************************************/ + +int usbdev_unregister(struct usbdevclass_driver_s *driver) +{ + /* For now there is only one USB controller, but we will always refer to + * it using a pointer to make any future ports to multiple USB controllers + * easier. + */ + + struct stm32_usbdev_s *priv = &g_usbdev; + irqstate_t flags; + + usbtrace(TRACE_DEVUNREGISTER, 0); + +#ifdef CONFIG_DEBUG_USB + if (driver != priv->driver) + { + usbtrace(TRACE_DEVERROR(STM32_TRACEERR_INVALIDPARMS), 0); + return -EINVAL; + } +#endif + + /* Reset the hardware and cancel all requests. All requests must be + * canceled while the class driver is still bound. + */ + + flags = enter_critical_section(); + stm32_reset(priv); + + /* Unbind the class driver */ + + CLASS_UNBIND(driver, &priv->usbdev); + + /* Disable USB controller interrupts (but keep them attached) */ + + up_disable_irq(STM32_IRQ_USB_FS); + + /* Put the hardware in an inactive state. Then bring the hardware back up + * in the reset state (this is probably not necessary, the stm32_reset() + * call above was probably sufficient). + */ + + stm32_hwshutdown(priv); + stm32_hwsetup(priv); + + /* Unhook the driver */ + + priv->driver = NULL; + leave_critical_section(flags); + return OK; +} + +#endif /* CONFIG_STM32H5_USBFS */ diff --git a/arch/arm/src/stm32h5/stm32_usbfs.h b/arch/arm/src/stm32h5/stm32_usbfs.h new file mode 100644 index 00000000000..0514847f160 --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_usbfs.h @@ -0,0 +1,92 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_usbfs.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H5_STM32_USBFS_H +#define __ARCH_ARM_SRC_STM32H5_STM32_USBFS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#include "chip.h" +#include "hardware/stm32_usbfs.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Number of endpoints */ + +#define STM32_NENDPOINTS (8) + +/**************************************************************************** + * Public Functions Prototypes + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: stm32_usbpullup + * + * Description: + * If USB is supported and the board supports a pullup via GPIO + * (for USB software connect and disconnect), then the board software must + * provide stm32_pullup. + * See include/nuttx/usb/usbdev.h for additional description of this method. + * + ****************************************************************************/ + +int stm32_usbpullup(struct usbdev_s *dev, bool enable); + +/**************************************************************************** + * Name: stm32_usbsuspend + * + * Description: + * Board logic must provide the stm32_usbsuspend logic if the USBDEV driver + * is used. This function is called whenever the USB enters or leaves + * suspend mode. This is an opportunity for the board logic to shutdown + * clocks, power, etc. while the USB is suspended. + * + ****************************************************************************/ + +void stm32_usbsuspend(struct usbdev_s *dev, bool resume); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32H5_STM32_USBFS_H */ diff --git a/arch/arm/src/stm32h5/stm32h5xx_rcc.c b/arch/arm/src/stm32h5/stm32h5xx_rcc.c index b99fba47dec..a72f2b89d31 100644 --- a/arch/arm/src/stm32h5/stm32h5xx_rcc.c +++ b/arch/arm/src/stm32h5/stm32h5xx_rcc.c @@ -31,6 +31,7 @@ #include "stm32_pwr.h" #include "stm32_flash.h" #include "stm32_rcc.h" +#include "stm32_hsi48.h" /**************************************************************************** * Pre-processor Definitions @@ -58,7 +59,7 @@ #if defined(CONFIG_STM32H5_HAVE_HSI48) && defined(STM32H5_USE_CLK48) # if STM32H5_CLKUSB_SEL == RCC_CCIPR4_USBSEL_HSI48KERCK -# define STM32H5_USE_HSI48 +# define STM32H5_USE_HSI48 1 # endif #endif @@ -567,7 +568,7 @@ static inline void rcc_enableapb2(void) regval |= RCC_APB2ENR_SAI2EN; #endif -#ifdef CONFIG_STM32H5_USB +#ifdef CONFIG_STM32H5_USBFS /* USB clock enable */ regval |= RCC_APB2ENR_USBEN; @@ -1180,6 +1181,13 @@ void stm32_stdclockconfig(void) regval |= STM32_RCC_CCIPR3_SPI6SEL; putreg32(regval, STM32_RCC_CCIPR3); #endif + +#if defined(STM32H5_CLKUSB_SEL) + regval = getreg32(STM32_RCC_CCIPR4); + regval &= ~RCC_CCIPR4_USBSEL_MASK; + regval |= STM32H5_CLKUSB_SEL; + putreg32(regval, STM32_RCC_CCIPR4); +#endif } } #endif diff --git a/boards/arm/stm32h5/nucleo-h563zi/configs/usbnsh/defconfig b/boards/arm/stm32h5/nucleo-h563zi/configs/usbnsh/defconfig new file mode 100644 index 00000000000..83891b6e4c8 --- /dev/null +++ b/boards/arm/stm32h5/nucleo-h563zi/configs/usbnsh/defconfig @@ -0,0 +1,55 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_DEV_CONSOLE is not set +# CONFIG_NSH_ARGCAT is not set +# CONFIG_STANDARD_SERIAL is not set +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="nucleo-h563zi" +CONFIG_ARCH_BOARD_NUCLEO_H563ZI=y +CONFIG_ARCH_BUTTONS=y +CONFIG_ARCH_CHIP="stm32h5" +CONFIG_ARCH_CHIP_STM32H563ZI=y +CONFIG_ARCH_CHIP_STM32H5=y +CONFIG_ARCH_INTERRUPTSTACK=4096 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV8M_STACKCHECK=y +CONFIG_BOARDCTL_USBDEVCTRL=y +CONFIG_BOARD_LOOPSPERMSEC=9251 +CONFIG_BUILTIN=y +CONFIG_CDCACM=y +CONFIG_CDCACM_CONSOLE=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_FS_PROCFS=y +CONFIG_FS_PROCFS_REGISTER=y +CONFIG_HAVE_CXX=y +CONFIG_HAVE_CXXINITIALIZE=y +CONFIG_IDLETHREAD_STACKSIZE=2048 +CONFIG_INIT_ENTRYPOINT="nsh_main" +CONFIG_INIT_STACKSIZE=4096 +CONFIG_IRQ_WORK_STACKSIZE=4096 +CONFIG_LINE_MAX=64 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_DISABLE_IFUPDOWN=y +CONFIG_NSH_FILEIOSIZE=512 +CONFIG_NSH_READLINE=y +CONFIG_PREALLOC_TIMERS=4 +CONFIG_RAM_SIZE=655360 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_READLINE_CMD_HISTORY=y +CONFIG_READLINE_TABCOMPLETION=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_STACK_COLORATION=y +CONFIG_STM32H5_USART3=y +CONFIG_STM32H5_USBFS=y +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=0 diff --git a/boards/arm/stm32h5/nucleo-h563zi/include/board.h b/boards/arm/stm32h5/nucleo-h563zi/include/board.h index 1b9079ff86f..0f17b75ce03 100644 --- a/boards/arm/stm32h5/nucleo-h563zi/include/board.h +++ b/boards/arm/stm32h5/nucleo-h563zi/include/board.h @@ -152,7 +152,12 @@ #if defined(CONFIG_STM32H5_USBFS) || defined(CONFIG_STM32H5_RNG) # define STM32H5_USE_CLK48 1 +#endif + +#if defined(CONFIG_STM32H5_USBFS) # define STM32H5_CLKUSB_SEL RCC_CCIPR4_USBSEL_HSI48KERCK +# define STM32H5_HSI48_SYNCSRC SYNCSRC_USB +#else # define STM32H5_HSI48_SYNCSRC SYNCSRC_NONE #endif