diff --git a/Documentation/platforms/arm/stm32h5/boards/nucleo-h563zi/index.rst b/Documentation/platforms/arm/stm32h5/boards/nucleo-h563zi/index.rst index 6fb69cc6aad..b8aa29d5118 100644 --- a/Documentation/platforms/arm/stm32h5/boards/nucleo-h563zi/index.rst +++ b/Documentation/platforms/arm/stm32h5/boards/nucleo-h563zi/index.rst @@ -140,6 +140,14 @@ This configuration configures ADC1_IN3 and ADC1_IN10, which can be accessed at the CN9 A0 and A1 pins respectively. Modify nucleo-h563zi/src/stm32_adc.c to enable more channels. +pwm: +-------- + +This configuration configures TIM1_CH1OUT, which can be +accessed at pin D6 on the CN10 A0 connector. TIM1_CH1 is configured +as a pwm output at /dev/pwm0, and can be tested with the example pwm +application. + adc_watchdog: -------------- diff --git a/arch/arm/src/stm32h5/CMakeLists.txt b/arch/arm/src/stm32h5/CMakeLists.txt index 8eb490b0555..6db0f754e7e 100644 --- a/arch/arm/src/stm32h5/CMakeLists.txt +++ b/arch/arm/src/stm32h5/CMakeLists.txt @@ -104,6 +104,10 @@ if(CONFIG_STM32H5_DTS) list(APPEND SRCS stm32_dts.c) endif() +if(CONFIG_STM32H5_PWM) + list(APPEND SRCS stm32_pwm.c) +endif() + # Required chip type specific files if(CONFIG_STM32H5_STM32H5XXXX) diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig index 2689f381751..a7fe7efd460 100644 --- a/arch/arm/src/stm32h5/Kconfig +++ b/arch/arm/src/stm32h5/Kconfig @@ -323,6 +323,11 @@ config STM32H5_FDCAN bool default n +config STM32H5_PWM + bool + default n + select ARCH_HAVE_PWM_PULSECOUNT + config STM32H5_SPI bool default n diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs index 56fa1664ea1..daa07dd5d69 100644 --- a/arch/arm/src/stm32h5/Make.defs +++ b/arch/arm/src/stm32h5/Make.defs @@ -36,7 +36,7 @@ endif # Required STM32H5 files CHIP_CSRCS += stm32_gpio.c stm32_irq.c stm32_lowputc.c stm32_rcc.c -CHIP_CSRCS += stm32_serial.c stm32_start.c stm32_pwr.c stm32_timerisr.c +CHIP_CSRCS += stm32_start.c stm32_pwr.c stm32_timerisr.c CHIP_CSRCS += stm32_lse.c stm32_lsi.c CHIP_CSRCS += stm32_uid.c @@ -44,6 +44,10 @@ ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y) CHIP_CSRCS += stm32_idle.c endif +ifeq ($(CONFIG_STM32H5_USART),y) +CHIP_CSRCS += stm32_serial.c +endif + ifeq ($(CONFIG_TIMER),y) CHIP_CSRCS += stm32_tim_lowerhalf.c endif @@ -96,6 +100,10 @@ ifeq ($(CONFIG_STM32H5_DTS),y) CHIP_CSRCS += stm32_dts.c endif +ifeq ($(CONFIG_STM32H5_PWM),y) +CHIP_CSRCS += stm32_pwm.c +endif + # Required chip type specific files ifeq ($(CONFIG_STM32H5_STM32H5XXXX),y) diff --git a/arch/arm/src/stm32h5/hardware/stm32_tim.h b/arch/arm/src/stm32h5/hardware/stm32_tim.h index 0c169a1295b..58b92a91748 100644 --- a/arch/arm/src/stm32h5/hardware/stm32_tim.h +++ b/arch/arm/src/stm32h5/hardware/stm32_tim.h @@ -34,6 +34,10 @@ * Pre-porcessor Definitions ****************************************************************************/ +/* The STM32H5 family uses STM32 TIMER IP version 2 */ + +#define HAVE_IP_TIMERS_V2 1 + /* Register Offsets *********************************************************/ /* Basic Timers - TIM6 and TIM7 */ @@ -1384,4 +1388,4 @@ #define GTIM_DTR2_DTAE (1 << 16) /* Deadtime asymmetric enable */ #define GTIM_DTR2_DTPE (1 << 17) /* Deadtime preload enable */ -#endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_TIM_H */ \ No newline at end of file +#endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_TIM_H */ diff --git a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h index 07b8e8ec966..a388e2f69fd 100644 --- a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h +++ b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h @@ -352,6 +352,165 @@ #define GPIO_TRACED3_0 (GPIO_ALT|GPIO_AF0|GPIO_PORTC|GPIO_PIN12) #define GPIO_TRACED3_1 (GPIO_ALT|GPIO_AF0|GPIO_PORTE|GPIO_PIN6) +/* Timers */ + +/* TIM1 (AF1) */ + +#define GPIO_TIM1_CH1OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN8) /* PA8 */ +#define GPIO_TIM1_CH1OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTE|GPIO_PIN9) /* PE9 */ +#define GPIO_TIM1_CH1OUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN11) /* PH11 */ + +#define GPIO_TIM1_CH1NOUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PORTA|GPIO_PIN7) /* PA7 */ +#define GPIO_TIM1_CH1NOUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PORTB|GPIO_PIN13) /* PB13 */ +#define GPIO_TIM1_CH1NOUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PORTE|GPIO_PIN8) /* PE8 */ +#define GPIO_TIM1_CH1NOUT_4 (GPIO_ALT|GPIO_AF1|GPIO_PORTH|GPIO_PIN10) /* PH10 */ + +#define GPIO_TIM1_CH2OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN9) /* PA9 */ +#define GPIO_TIM1_CH2OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTE|GPIO_PIN11) /* PE11 */ +#define GPIO_TIM1_CH2OUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN9) /* PH9 */ + +#define GPIO_TIM1_CH2NOUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PORTB|GPIO_PIN0) /* PB0 */ +#define GPIO_TIM1_CH2NOUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PORTB|GPIO_PIN14) /* PB14 */ +#define GPIO_TIM1_CH2NOUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PORTE|GPIO_PIN10) /* PE10 */ +#define GPIO_TIM1_CH2NOUT_4 (GPIO_ALT|GPIO_AF1|GPIO_PORTH|GPIO_PIN8) /* PH8 */ + +#define GPIO_TIM1_CH3OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN10) /* PA10 */ +#define GPIO_TIM1_CH3OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTE|GPIO_PIN13) /* PE13 */ +#define GPIO_TIM1_CH3OUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN7) /* PH7 */ + +#define GPIO_TIM1_CH3NOUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PORTB|GPIO_PIN1) /* PB1 */ +#define GPIO_TIM1_CH3NOUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PORTB|GPIO_PIN15) /* PB15 */ +#define GPIO_TIM1_CH3NOUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PORTE|GPIO_PIN12) /* PE12 */ +#define GPIO_TIM1_CH3NOUT_4 (GPIO_ALT|GPIO_AF1|GPIO_PORTH|GPIO_PIN6) /* PH6 */ + +#define GPIO_TIM1_CH4OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN11) /* PA11 */ +#define GPIO_TIM1_CH4OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTE|GPIO_PIN14) /* PE14 */ + +#define GPIO_TIM1_CH4NOUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PORTE|GPIO_PIN15) /* PE15 */ +#define GPIO_TIM1_CH4NOUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PORTC|GPIO_PIN5) /* PC5 */ + +/* TIM2 (AF1) */ + +#define GPIO_TIM2_CH1OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN0) /* PA0 */ +#define GPIO_TIM2_CH1OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN5) /* PA5 */ +#define GPIO_TIM2_CH1OUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN15) /* PA15 */ + +#define GPIO_TIM2_CH2OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN1) /* PA1 */ +#define GPIO_TIM2_CH2OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN3) /* PB3 */ + +#define GPIO_TIM2_CH3OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN2) /* PA2 */ +#define GPIO_TIM2_CH3OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN10) /* PB10 */ + +#define GPIO_TIM2_CH4OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN3) /* PA3 */ +#define GPIO_TIM2_CH4OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN11) /* PB11 */ +#define GPIO_TIM2_CH4OUT_3 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN4) /* PC4 */ + +/* TIM3 (AF2) */ + +#define GPIO_TIM3_CH1OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN6) /* PA6 */ +#define GPIO_TIM3_CH1OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN4) /* PB4 */ +#define GPIO_TIM3_CH1OUT_3 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN6) /* PC6 */ + +#define GPIO_TIM3_CH2OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN7) /* PA7 */ +#define GPIO_TIM3_CH2OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN5) /* PB5 */ +#define GPIO_TIM3_CH2OUT_3 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN7) /* PC7 */ + +#define GPIO_TIM3_CH3OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN0) /* PB0 */ +#define GPIO_TIM3_CH3OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN8) /* PC8 */ + +#define GPIO_TIM3_CH4OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN1) /* PB1 */ +#define GPIO_TIM3_CH4OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN9) /* PC9 */ + +/* TIM4 (AF2) */ + +#define GPIO_TIM4_CH1OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN6) /* PB6 */ +#define GPIO_TIM4_CH2OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN7) /* PB7 */ +#define GPIO_TIM4_CH3OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN8) /* PB8 */ +#define GPIO_TIM4_CH4OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN9) /* PB9 */ +#define GPIO_TIM4_CH4OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN2) /* PC2 */ + +/* TIM5 (AF2) */ + +#define GPIO_TIM5_CH1OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN0) /* PA0 */ +#define GPIO_TIM5_CH1OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN10) /* PH10 */ + +#define GPIO_TIM5_CH2OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN1) /* PA1 */ +#define GPIO_TIM5_CH2OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN11) /* PH11 */ + +#define GPIO_TIM5_CH3OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN2) /* PA2 */ +#define GPIO_TIM5_CH3OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN12) /* PH12 */ + +#define GPIO_TIM5_CH4OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN3) /* PA3 */ +#define GPIO_TIM5_CH4OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTI|GPIO_PIN0) /* PI0 */ + +/* TIM8 (AF3) */ + +#define GPIO_TIM8_CH1OUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN6) /* PC6 */ +#define GPIO_TIM8_CH1OUT_2 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTI|GPIO_PIN5) /* PI5 */ +#define GPIO_TIM8_CH1OUT_3 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN6) /* PH6 */ + +#define GPIO_TIM8_CH1NOUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PORTA|GPIO_PIN5) /* PA5 */ +#define GPIO_TIM8_CH1NOUT_2 (GPIO_ALT|GPIO_AF3|GPIO_PORTA|GPIO_PIN7) /* PA7 */ +#define GPIO_TIM8_CH1NOUT_3 (GPIO_ALT|GPIO_AF3|GPIO_PORTH|GPIO_PIN7) /* PH7 */ +#define GPIO_TIM8_CH1NOUT_4 (GPIO_ALT|GPIO_AF3|GPIO_PORTH|GPIO_PIN13) /* PH13 */ + +#define GPIO_TIM8_CH2OUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN7) /* PC7 */ +#define GPIO_TIM8_CH2OUT_2 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTI|GPIO_PIN6) /* PI6 */ +#define GPIO_TIM8_CH2OUT_3 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN8) /* PH8 */ + +#define GPIO_TIM8_CH2NOUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PORTB|GPIO_PIN0) /* PB0 */ +#define GPIO_TIM8_CH2NOUT_2 (GPIO_ALT|GPIO_AF3|GPIO_PORTB|GPIO_PIN14) /* PB14 */ +#define GPIO_TIM8_CH2NOUT_3 (GPIO_ALT|GPIO_AF3|GPIO_PORTH|GPIO_PIN9) /* PH9 */ +#define GPIO_TIM8_CH2NOUT_4 (GPIO_ALT|GPIO_AF3|GPIO_PORTH|GPIO_PIN14) /* PH14 */ + +#define GPIO_TIM8_CH3OUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN8) /* PC8 */ +#define GPIO_TIM8_CH3OUT_2 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN10) /* PH10 */ +#define GPIO_TIM8_CH3OUT_3 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTI|GPIO_PIN7) /* PI7 */ + +#define GPIO_TIM8_CH3NOUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PORTB|GPIO_PIN1) /* PB1 */ +#define GPIO_TIM8_CH3NOUT_2 (GPIO_ALT|GPIO_AF3|GPIO_PORTB|GPIO_PIN15) /* PB15 */ +#define GPIO_TIM8_CH3NOUT_3 (GPIO_ALT|GPIO_AF3|GPIO_PORTH|GPIO_PIN11) /* PH11 */ +#define GPIO_TIM8_CH3NOUT_4 (GPIO_ALT|GPIO_AF3|GPIO_PORTH|GPIO_PIN15) /* PH15 */ + +#define GPIO_TIM8_CH4OUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN9) /* PC9 */ +#define GPIO_TIM8_CH4OUT_2 (GPIO_ALT|GPIO_AF3|GPIO_PUSHPULL|GPIO_PORTI|GPIO_PIN2) /* PI2 */ + +#define GPIO_TIM8_CH4NOUT_1 (GPIO_ALT|GPIO_AF3|GPIO_PORTB|GPIO_PIN2) /* PB2 */ + +/* TIM12 (AF2) */ + +#define GPIO_TIM12_CH1OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN14) /* PB14 */ +#define GPIO_TIM12_CH1OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN6) /* PH6 */ + +#define GPIO_TIM12_CH2OUT_1 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN15) /* PB15 */ +#define GPIO_TIM12_CH2OUT_2 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTH|GPIO_PIN9) /* PH9 */ + +/* TIM15 (AF2/AF4, per pin) */ + +#define GPIO_TIM15_CH1OUT_1 (GPIO_ALT|GPIO_AF4|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN2) /* PA2 */ +#define GPIO_TIM15_CH1OUT_2 (GPIO_ALT|GPIO_AF4|GPIO_PUSHPULL|GPIO_PORTE|GPIO_PIN5) /* PE5 */ +#define GPIO_TIM15_CH1OUT_3 (GPIO_ALT|GPIO_AF2|GPIO_PUSHPULL|GPIO_PORTC|GPIO_PIN12) /* PC12 */ + +#define GPIO_TIM15_CH1NOUT_1 (GPIO_ALT|GPIO_AF4|GPIO_PORTA|GPIO_PIN1) /* PA1 */ +#define GPIO_TIM15_CH1NOUT_2 (GPIO_ALT|GPIO_AF4|GPIO_PORTE|GPIO_PIN4) /* PE4 */ + +#define GPIO_TIM15_CH2OUT_1 (GPIO_ALT|GPIO_AF4|GPIO_PUSHPULL|GPIO_PORTA|GPIO_PIN3) /* PA3 */ +#define GPIO_TIM15_CH2OUT_2 (GPIO_ALT|GPIO_AF4|GPIO_PUSHPULL|GPIO_PORTE|GPIO_PIN6) /* PE6 */ + +/* TIM16 (AF1) */ + +#define GPIO_TIM16_CH1OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN8) /* PB8 */ +#define GPIO_TIM16_CH1OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTF|GPIO_PIN6) /* PF6 */ +#define GPIO_TIM16_CH1NOUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PORTB|GPIO_PIN6) /* PB6 */ +#define GPIO_TIM16_CH1NOUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PORTF|GPIO_PIN8) /* PF8 */ + +/* TIM17 (AF1) */ + +#define GPIO_TIM17_CH1OUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTB|GPIO_PIN9) /* PB9 */ +#define GPIO_TIM17_CH1OUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PUSHPULL|GPIO_PORTF|GPIO_PIN7) /* PF7 */ +#define GPIO_TIM17_CH1NOUT_1 (GPIO_ALT|GPIO_AF1|GPIO_PORTB|GPIO_PIN7) /* PB7 */ +#define GPIO_TIM17_CH1NOUT_2 (GPIO_ALT|GPIO_AF1|GPIO_PORTF|GPIO_PIN9) /* PF9 */ + /* UARTs/USARTs */ /* UART4 */ diff --git a/arch/arm/src/stm32h5/stm32_pwm.c b/arch/arm/src/stm32h5/stm32_pwm.c new file mode 100644 index 00000000000..94122afe80d --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_pwm.c @@ -0,0 +1,4424 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_pwm.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 "arm_internal.h" +#include "chip.h" +#include "stm32_pwm.h" +#include "stm32.h" + +/* This module then only compiles if there is at least one enabled timer + * intended for use with the PWM upper half driver. + * + * It implements support for both: + * 1. STM32 TIMER IP version 1 - F0, F1, F2, F37x, F4, L0, L1 + * 2. STM32 TIMER IP version 2 - F3 (no F37x), F7, H7, L4, L4+, H5 + */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* PWM/Timer Definitions ****************************************************/ + +/* The following definitions are used to identify the various time types. */ + +#define TIMTYPE_BASIC 0 /* Basic timers (no outputs) */ +#define TIMTYPE_GENERAL16 1 /* General 16-bit timers (up, down, up/down)*/ +#define TIMTYPE_COUNTUP16 2 /* General 16-bit count-up timers */ +#define TIMTYPE_COUNTUP16_N 3 /* General 16-bit count-up timers with + * complementary outptus + */ +#define TIMTYPE_GENERAL32 4 /* General 32-bit timers (up, down, up/down)*/ +#define TIMTYPE_ADVANCED 5 /* Advanced timers */ + +#define TIMTYPE_TIM1 TIMTYPE_ADVANCED +#define TIMTYPE_TIM2 TIMTYPE_GENERAL32 +#define TIMTYPE_TIM3 TIMTYPE_GENERAL16 +#define TIMTYPE_TIM4 TIMTYPE_GENERAL16 +#define TIMTYPE_TIM5 TIMTYPE_GENERAL32 +#define TIMTYPE_TIM6 TIMTYPE_BASIC +#define TIMTYPE_TIM7 TIMTYPE_BASIC +#define TIMTYPE_TIM8 TIMTYPE_ADVANCED +/* No TIM9-11 */ +#define TIMTYPE_TIM12 TIMTYPE_COUNTUP16 +#define TIMTYPE_TIM13 TIMTYPE_COUNTUP16 +#define TIMTYPE_TIM14 TIMTYPE_COUNTUP16 +#define TIMTYPE_TIM15 TIMTYPE_COUNTUP16_N /* Treated as ADVTIM */ +#define TIMTYPE_TIM16 TIMTYPE_COUNTUP16_N /* Treated as ADVTIM */ +#define TIMTYPE_TIM17 TIMTYPE_COUNTUP16_N /* Treated as ADVTIM */ + +/* Timer clock source, RCC EN offset, enable bit, + * RCC RST offset, reset bit to use + * + * TODO: simplify this and move somewhere else. + */ + +#define TIMCLK_TIM1 STM32_APB2_TIM1_CLKIN +#define TIMRCCEN_TIM1 STM32_RCC_APB2ENR +#define TIMEN_TIM1 RCC_APB2ENR_TIM1EN +#define TIMRCCRST_TIM1 STM32_RCC_APB2RSTR +#define TIMRST_TIM1 RCC_APB2RSTR_TIM1RST +#define TIMCLK_TIM2 STM32_APB1_TIM2_CLKIN +#define TIMRCCEN_TIM2 STM32_RCC_APB1LENR +#define TIMEN_TIM2 RCC_APB1LENR_TIM2EN +#define TIMRCCRST_TIM2 STM32_RCC_APB1LRSTR +#define TIMRST_TIM2 RCC_APB1LRSTR_TIM2RST +#define TIMCLK_TIM3 STM32_APB1_TIM3_CLKIN +#define TIMRCCEN_TIM3 STM32_RCC_APB1LENR +#define TIMEN_TIM3 RCC_APB1LENR_TIM3EN +#define TIMRCCRST_TIM3 STM32_RCC_APB1LRSTR +#define TIMRST_TIM3 RCC_APB1LRSTR_TIM3RST +#define TIMCLK_TIM4 STM32_APB1_TIM4_CLKIN +#define TIMRCCEN_TIM4 STM32_RCC_APB1LENR +#define TIMEN_TIM4 RCC_APB1LENR_TIM4EN +#define TIMRCCRST_TIM4 STM32_RCC_APB1LRSTR +#define TIMRST_TIM4 RCC_APB1LRSTR_TIM4RST +#define TIMCLK_TIM5 STM32_APB1_TIM5_CLKIN +#define TIMRCCEN_TIM5 STM32_RCC_APB1LENR +#define TIMEN_TIM5 RCC_APB1LENR_TIM5EN +#define TIMRCCRST_TIM5 STM32_RCC_APB1LRSTR +#define TIMRST_TIM5 RCC_APB1LRSTR_TIM5RST +#define TIMCLK_TIM8 STM32_APB2_TIM8_CLKIN +#define TIMRCCEN_TIM8 STM32_RCC_APB2ENR +#define TIMEN_TIM8 RCC_APB2ENR_TIM8EN +#define TIMRCCRST_TIM8 STM32_RCC_APB2RSTR +#define TIMRST_TIM8 RCC_APB2RSTR_TIM8RST +#define TIMCLK_TIM12 STM32_APB1_TIM12_CLKIN +#define TIMRCCEN_TIM12 STM32_RCC_APB1LENR +#define TIMEN_TIM12 RCC_APB1LENR_TIM12EN +#define TIMRCCRST_TIM12 STM32_RCC_APB1LRSTR +#define TIMRST_TIM12 RCC_APB1LRSTR_TIM12RST +#define TIMCLK_TIM13 STM32_APB1_TIM13_CLKIN +#define TIMRCCEN_TIM13 STM32_RCC_APB1LENR +#define TIMEN_TIM13 RCC_APB1LENR_TIM13EN +#define TIMRCCRST_TIM13 STM32_RCC_APB1LRSTR +#define TIMRST_TIM13 RCC_APB1LRSTR_TIM13RST +#define TIMCLK_TIM14 STM32_APB1_TIM14_CLKIN +#define TIMRCCEN_TIM14 STM32_RCC_APB1LENR +#define TIMEN_TIM14 RCC_APB1LENR_TIM14EN +#define TIMRCCRST_TIM14 STM32_RCC_APB1LRSTR +#define TIMRST_TIM14 RCC_APB1LRSTR_TIM14RST +#define TIMCLK_TIM15 STM32_APB2_TIM15_CLKIN +#define TIMRCCEN_TIM15 STM32_RCC_APB2ENR +#define TIMEN_TIM15 RCC_APB2ENR_TIM15EN +#define TIMRCCRST_TIM15 STM32_RCC_APB2RSTR +#define TIMRST_TIM15 RCC_APB2RSTR_TIM15RST +#define TIMCLK_TIM16 STM32_APB2_TIM16_CLKIN +#define TIMRCCEN_TIM16 STM32_RCC_APB2ENR +#define TIMEN_TIM16 RCC_APB2ENR_TIM16EN +#define TIMRCCRST_TIM16 STM32_RCC_APB2RSTR +#define TIMRST_TIM16 RCC_APB2RSTR_TIM16RST +#define TIMCLK_TIM17 STM32_APB2_TIM17_CLKIN +#define TIMRCCEN_TIM17 STM32_RCC_APB2ENR +#define TIMEN_TIM17 RCC_APB2ENR_TIM17EN +#define TIMRCCRST_TIM17 STM32_RCC_APB2RSTR +#define TIMRST_TIM17 RCC_APB2RSTR_TIM17RST + +/* Default GPIO pins state */ + +#define PINCFG_DEFAULT (GPIO_INPUT | GPIO_FLOAT) + +/* Advanced Timer support + * NOTE: TIM15-17 are not ADVTIM but they support most of the + * ADVTIM functionality. The main difference is the number of + * supported capture/compare. + */ + +#if defined(CONFIG_STM32H5_TIM1_PWM) || defined(CONFIG_STM32H5_TIM8_PWM) || \ + defined(CONFIG_STM32H5_TIM15_PWM) || defined(CONFIG_STM32_TIM16_PWM) || \ + defined(CONFIG_STM32H5_TIM17_PWM) +# define HAVE_ADVTIM +#else +# undef HAVE_ADVTIM +#endif + +/* Pulsecount support */ + +#ifdef CONFIG_PWM_PULSECOUNT +# ifndef HAVE_ADVTIM +# error "PWM_PULSECOUNT requires HAVE_ADVTIM" +# endif +# if defined(CONFIG_STM32H5_TIM1_PWM) || defined(CONFIG_STM32H5_TIM8_PWM) +# define HAVE_PWM_INTERRUPT +# endif +#endif + +/* Synchronisation support */ + +#ifdef CONFIG_STM32H5_PWM_TRGO +# define HAVE_TRGO +#endif + +/* Break support */ + +#if defined(CONFIG_STM32H5_TIM1_BREAK1) || defined(CONFIG_STM32H5_TIM1_BREAK2) || \ + defined(CONFIG_STM32H5_TIM8_BREAK1) || defined(CONFIG_STM32H5_TIM8_BREAK2) || \ + defined(CONFIG_STM32H5_TIM15_BREAK1) || defined(CONFIG_STM32H5_TIM16_BREAK1) || \ + defined(CONFIG_STM32H5_TIM17_BREAK1) +# defined HAVE_BREAK +#endif + +/* Debug ********************************************************************/ + +#ifdef CONFIG_DEBUG_PWM_INFO +# define pwm_dumpgpio(p,m) stm32_dumpgpio(p,m) +#else +# define pwm_dumpgpio(p,m) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* PWM output configuration */ + +struct stm32_pwm_out_s +{ + uint8_t in_use:1; /* Output in use */ + uint8_t pol:1; /* Polarity. Default: positive */ + uint8_t idle:1; /* Idle state. Default: inactive */ + uint8_t _res:5; /* Reserved */ + uint32_t pincfg; /* Output pin configuration */ +}; + +/* PWM break configuration */ + +#ifdef HAVE_BREAK +struct stm32_pwm_break_s +{ + uint8_t en1:1; /* Break 1 enable */ + uint8_t pol1:1; /* Break 1 polarity */ + uint8_t _res:6; /* Reserved */ +#ifdef HAVE_IP_TIMERS_V2 + uint8_t en2:1; /* Break 2 enable */ + uint8_t pol2:1; /* Break 2 polarity */ + uint8_t flt2:6; /* Break 2 filter */ +#endif +}; +#endif + +/* PWM channel configuration */ + +struct stm32_pwmchan_s +{ + uint8_t channel:4; /* Timer output channel: {1,..4} */ + uint8_t mode:4; /* PWM channel mode (see stm32_pwm_chanmode_e) */ + struct stm32_pwm_out_s out1; /* PWM output configuration */ +#ifdef HAVE_BREAK + struct stm32_pwm_break_s brk; /* PWM break configuration */ +#endif +#ifdef HAVE_PWM_COMPLEMENTARY + struct stm32_pwm_out_s out2; /* PWM complementary output configuration */ +#endif +}; + +/* This structure represents the state of one PWM timer */ + +struct stm32_pwmtimer_s +{ + const struct pwm_ops_s *ops; /* PWM operations */ +#ifdef CONFIG_STM32H5_PWM_LL_OPS + const struct stm32_pwm_ops_s *llops; /* Low-level PWM ops */ +#endif + struct stm32_pwmchan_s *channels; /* Channels configuration */ + uint8_t timid:5; /* Timer ID {1,...,17} */ + uint8_t chan_num:3; /* Number of configured channels */ + uint8_t timtype:3; /* See the TIMTYPE_* definitions */ + uint8_t mode:3; /* Timer mode (see stm32_pwm_tim_mode_e) */ + uint8_t lock:2; /* TODO: Lock configuration */ + uint8_t t_dts:3; /* Clock division for t_DTS */ + uint8_t _res:5; /* Reserved */ +#ifdef HAVE_PWM_COMPLEMENTARY + uint8_t deadtime; /* Dead-time value */ +#endif +#ifdef HAVE_TRGO + uint8_t trgo; /* TRGO configuration: + * 4 LSB = TRGO, 4 MSB = TRGO2 + */ +#endif +#ifdef CONFIG_PWM_PULSECOUNT + uint8_t irq; /* Timer update IRQ */ + uint8_t prev; /* The previous value of the RCR (pre-loaded) */ + uint8_t curr; /* The current value of the RCR (pre-loaded) */ + uint32_t count; /* Remaining pulse count */ +#else + uint32_t frequency; /* Current frequency setting */ +#endif + uint32_t base; /* The base address of the timer */ + uint32_t pclk; /* The frequency of the peripheral clock + * that drives the timer module. + */ +#ifdef CONFIG_PWM_PULSECOUNT + void *handle; /* Handle used for upper-half callback */ +#endif +}; + +/**************************************************************************** + * Static Function Prototypes + ****************************************************************************/ + +/* Register access */ + +static uint32_t pwm_getreg(struct stm32_pwmtimer_s *priv, int offset); +static void pwm_putreg(struct stm32_pwmtimer_s *priv, int offset, + uint32_t value); +static void pwm_modifyreg(struct stm32_pwmtimer_s *priv, uint32_t offset, + uint32_t clearbits, uint32_t setbits); + +#ifdef CONFIG_DEBUG_PWM_INFO +static void pwm_dumpregs(struct pwm_lowerhalf_s *dev, + const char *msg); +#else +# define pwm_dumpregs(priv,msg) +#endif + +/* Timer management */ + +static int pwm_frequency_update(struct pwm_lowerhalf_s *dev, + uint32_t frequency); +static int pwm_mode_configure(struct pwm_lowerhalf_s *dev, + uint8_t channel, uint32_t mode); +static int pwm_timer_configure(struct stm32_pwmtimer_s *priv); +static int pwm_output_configure(struct stm32_pwmtimer_s *priv, + struct stm32_pwmchan_s *chan); +static int pwm_outputs_enable(struct pwm_lowerhalf_s *dev, + uint16_t outputs, bool state); +static int pwm_soft_update(struct pwm_lowerhalf_s *dev); +static int pwm_soft_break(struct pwm_lowerhalf_s *dev, bool state); +static int pwm_ccr_update(struct pwm_lowerhalf_s *dev, uint8_t index, + uint32_t ccr); +static int pwm_arr_update(struct pwm_lowerhalf_s *dev, uint32_t arr); +static uint32_t pwm_arr_get(struct pwm_lowerhalf_s *dev); +static int pwm_duty_update(struct pwm_lowerhalf_s *dev, uint8_t channel, + ub16_t duty); +static int pwm_timer_enable(struct pwm_lowerhalf_s *dev, bool state); + +#ifdef HAVE_ADVTIM +static int pwm_break_dt_configure(struct stm32_pwmtimer_s *priv); +#endif +#ifdef HAVE_TRGO +static int pwm_sync_configure(struct stm32_pwmtimer_s *priv, + uint8_t trgo); +#endif +#if defined(HAVE_PWM_COMPLEMENTARY) && defined(CONFIG_STM32H5_PWM_LL_OPS) +static int pwm_deadtime_update(struct pwm_lowerhalf_s *dev, uint8_t dt); +#endif +#ifdef CONFIG_STM32H5_PWM_LL_OPS +static uint32_t pwm_ccr_get(struct pwm_lowerhalf_s *dev, uint8_t index); +#endif + +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_pulsecount_configure(struct pwm_lowerhalf_s *dev); +#else +static int pwm_configure(struct pwm_lowerhalf_s *dev); +#endif +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_pulsecount_timer(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info); +#else +static int pwm_timer(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info); +#endif +#ifdef HAVE_PWM_INTERRUPT +static int pwm_interrupt(struct pwm_lowerhalf_s *dev); +# ifdef CONFIG_STM32H5_TIM1_PWM +static int pwm_tim1interrupt(int irq, void *context, void *arg); +# endif +# ifdef CONFIG_STM32H5_TIM8_PWM +static int pwm_tim8interrupt(int irq, void *context, void *arg); +# endif +static uint8_t pwm_pulsecount(uint32_t count); +#endif + +/* PWM driver methods */ + +static int pwm_setup(struct pwm_lowerhalf_s *dev); +static int pwm_shutdown(struct pwm_lowerhalf_s *dev); + +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info, + void *handle); +#else +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info); +#endif + +static int pwm_stop(struct pwm_lowerhalf_s *dev); +static int pwm_ioctl(struct pwm_lowerhalf_s *dev, + int cmd, unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is the list of lower half PWM driver methods used by the upper half + * driver. + */ + +static const struct pwm_ops_s g_pwmops = +{ + .setup = pwm_setup, + .shutdown = pwm_shutdown, + .start = pwm_start, + .stop = pwm_stop, + .ioctl = pwm_ioctl, +}; + +#ifdef CONFIG_STM32H5_PWM_LL_OPS +static const struct stm32_pwm_ops_s g_llpwmops = +{ + .configure = pwm_configure, + .soft_break = pwm_soft_break, + .ccr_update = pwm_ccr_update, + .mode_update = pwm_mode_configure, + .ccr_get = pwm_ccr_get, + .arr_update = pwm_arr_update, + .arr_get = pwm_arr_get, + .outputs_enable = pwm_outputs_enable, + .soft_update = pwm_soft_update, + .freq_update = pwm_frequency_update, + .tim_enable = pwm_timer_enable, +# ifdef CONFIG_DEBUG_PWM_INFO + .dump_regs = pwm_dumpregs, +# endif +# ifdef HAVE_PWM_COMPLEMENTARY + .dt_update = pwm_deadtime_update, +# endif +}; +#endif + +#ifdef CONFIG_STM32H5_TIM1_PWM + +static struct stm32_pwmchan_s g_pwm1channels[] = +{ + /* TIM1 has 4 channels, 4 complementary */ + +#ifdef CONFIG_STM32H5_TIM1_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM1_CH1MODE, +#ifdef HAVE_BREAK + .brk = + { +#ifdef CONFIG_STM32H5_TIM1_BREAK1 + .en1 = 1, + .pol1 = CONFIG_STM32H5_TIM1_BRK1POL, +#endif +#ifdef CONFIG_STM32H5_TIM1_BREAK2 + .en2 = 1, + .pol2 = CONFIG_STM32H5_TIM1_BRK2POL, + .flt2 = CONFIG_STM32H5_TIM1_BRK2FLT, +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH1POL, + .idle = CONFIG_STM32H5_TIM1_CH1IDLE, + .pincfg = PWM_TIM1_CH1CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CH1NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH1NPOL, + .idle = CONFIG_STM32H5_TIM1_CH1NIDLE, + .pincfg = PWM_TIM1_CH1NCFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM1_CH2MODE, +#ifdef CONFIG_STM32H5_TIM1_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH2POL, + .idle = CONFIG_STM32H5_TIM1_CH2IDLE, + .pincfg = PWM_TIM1_CH2CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CH2NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH2NPOL, + .idle = CONFIG_STM32H5_TIM1_CH2NIDLE, + .pincfg = PWM_TIM1_CH2NCFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL3 + { + .channel = 3, + .mode = CONFIG_STM32H5_TIM1_CH3MODE, +#ifdef CONFIG_STM32H5_TIM1_CH3OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH3POL, + .idle = CONFIG_STM32H5_TIM1_CH3IDLE, + .pincfg = PWM_TIM1_CH3CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CH3NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH3NPOL, + .idle = CONFIG_STM32H5_TIM1_CH3NIDLE, + .pincfg = PWM_TIM1_CH3NCFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL4 + { + .channel = 4, + .mode = CONFIG_STM32H5_TIM1_CH4MODE, +#ifdef CONFIG_STM32H5_TIM1_CH4OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH4POL, + .idle = CONFIG_STM32H5_TIM1_CH4IDLE, + .pincfg = PWM_TIM1_CH4CFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL5 + { + .channel = 5, + .mode = CONFIG_STM32H5_TIM1_CH5MODE, +#ifdef CONFIG_STM32H5_TIM1_CH5OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH5POL, + .idle = CONFIG_STM32H5_TIM1_CH5IDLE, + .pincfg = 0, /* Not available externally */ + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL6 + { + .channel = 6, + .mode = CONFIG_STM32H5_TIM1_CH6MODE, +#ifdef CONFIG_STM32H5_TIM1_CH6OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM1_CH6POL, + .idle = CONFIG_STM32H5_TIM1_CH6IDLE, + .pincfg = 0, /* Not available externally */ + } +#endif + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm1dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 1, + .chan_num = PWM_TIM1_NCHANNELS, + .channels = g_pwm1channels, + .timtype = TIMTYPE_TIM1, + .mode = CONFIG_STM32H5_TIM1_MODE, + .lock = CONFIG_STM32H5_TIM1_LOCK, + .t_dts = CONFIG_STM32H5_TIM1_TDTS, +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = CONFIG_STM32H5_TIM1_DEADTIME, +#endif +#if defined(HAVE_TRGO) && defined(STM32_TIM1_TRGO) + .trgo = STM32_TIM1_TRGO, +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM1_UP, +#endif + .base = STM32_TIM1_BASE, + .pclk = TIMCLK_TIM1, +}; +#endif /* CONFIG_STM32H5_TIM1_PWM */ + +#ifdef CONFIG_STM32H5_TIM2_PWM + +static struct stm32_pwmchan_s g_pwm2channels[] = +{ + /* TIM2 has 4 channels */ + +#ifdef CONFIG_STM32H5_TIM2_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM2_CH1MODE, +#ifdef CONFIG_STM32H5_TIM2_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM2_CH1POL, + .idle = CONFIG_STM32H5_TIM2_CH1IDLE, + .pincfg = PWM_TIM2_CH1CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM2_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM2_CH2MODE, +#ifdef CONFIG_STM32H5_TIM2_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM2_CH2POL, + .idle = CONFIG_STM32H5_TIM2_CH2IDLE, + .pincfg = PWM_TIM2_CH2CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM2_CHANNEL3 + { + .channel = 3, + .mode = CONFIG_STM32H5_TIM2_CH3MODE, +#ifdef CONFIG_STM32H5_TIM2_CH3OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM2_CH3POL, + .idle = CONFIG_STM32H5_TIM2_CH3IDLE, + .pincfg = PWM_TIM2_CH3CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM2_CHANNEL4 + { + .channel = 4, + .mode = CONFIG_STM32H5_TIM2_CH4MODE, +#ifdef CONFIG_STM32H5_TIM2_CH4OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM2_CH4POL, + .idle = CONFIG_STM32H5_TIM2_CH4IDLE, + .pincfg = PWM_TIM2_CH4CFG, + } +#endif + /* No complementary outputs */ + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm2dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 2, + .chan_num = PWM_TIM2_NCHANNELS, + .channels = g_pwm2channels, + .timtype = TIMTYPE_TIM2, + .mode = CONFIG_STM32H5_TIM2_MODE, + .lock = 0, /* No lock */ + .t_dts = 0, /* No t_dts */ +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = 0, /* No deadtime */ +#endif +#if defined(HAVE_TRGO) && defined(STM32_TIM2_TRGO) + .trgo = STM32_TIM2_TRGO, +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM2, +#endif + .base = STM32_TIM2_BASE, + .pclk = TIMCLK_TIM2, +}; +#endif /* CONFIG_STM32H5_TIM2_PWM */ + +#ifdef CONFIG_STM32H5_TIM3_PWM + +static struct stm32_pwmchan_s g_pwm3channels[] = +{ + /* TIM3 has 4 channels */ + +#ifdef CONFIG_STM32H5_TIM3_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM3_CH1MODE, +#ifdef CONFIG_STM32H5_TIM3_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM3_CH1POL, + .idle = CONFIG_STM32H5_TIM3_CH1IDLE, + .pincfg = PWM_TIM3_CH1CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM3_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM3_CH2MODE, +#ifdef CONFIG_STM32H5_TIM3_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM3_CH2POL, + .idle = CONFIG_STM32H5_TIM3_CH2IDLE, + .pincfg = PWM_TIM3_CH2CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM3_CHANNEL3 + { + .channel = 3, + .mode = CONFIG_STM32H5_TIM3_CH3MODE, +#ifdef CONFIG_STM32H5_TIM3_CH3OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM3_CH3POL, + .idle = CONFIG_STM32H5_TIM3_CH3IDLE, + .pincfg = PWM_TIM3_CH3CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM3_CHANNEL4 + { + .channel = 4, + .mode = CONFIG_STM32H5_TIM3_CH4MODE, +#ifdef CONFIG_STM32H5_TIM3_CH4OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM3_CH4POL, + .idle = CONFIG_STM32H5_TIM3_CH4IDLE, + .pincfg = PWM_TIM3_CH4CFG, + } +#endif + /* No complementary outputs */ + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm3dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 3, + .chan_num = PWM_TIM3_NCHANNELS, + .channels = g_pwm3channels, + .timtype = TIMTYPE_TIM3, + .mode = CONFIG_STM32H5_TIM3_MODE, + .lock = 0, /* No lock */ + .t_dts = 0, /* No t_dts */ +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = 0, /* No deadtime */ +#endif +#if defined(HAVE_TRGO) && defined(STM32_TIM3_TRGO) + .trgo = STM32_TIM3_TRGO, +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM3, +#endif + .base = STM32_TIM3_BASE, + .pclk = TIMCLK_TIM3, +}; +#endif /* CONFIG_STM32H5_TIM3_PWM */ + +#ifdef CONFIG_STM32H5_TIM4_PWM + +static struct stm32_pwmchan_s g_pwm4channels[] = +{ + /* TIM4 has 4 channels */ + +#ifdef CONFIG_STM32H5_TIM4_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM4_CH1MODE, +#ifdef CONFIG_STM32H5_TIM4_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM4_CH1POL, + .idle = CONFIG_STM32H5_TIM4_CH1IDLE, + .pincfg = PWM_TIM4_CH1CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM4_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM4_CH2MODE, +#ifdef CONFIG_STM32H5_TIM4_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM4_CH2POL, + .idle = CONFIG_STM32H5_TIM4_CH2IDLE, + .pincfg = PWM_TIM4_CH2CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM4_CHANNEL3 + { + .channel = 3, + .mode = CONFIG_STM32H5_TIM4_CH3MODE, +#ifdef CONFIG_STM32H5_TIM4_CH3OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM4_CH3POL, + .idle = CONFIG_STM32H5_TIM4_CH3IDLE, + .pincfg = PWM_TIM4_CH3CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM4_CHANNEL4 + { + .channel = 4, + .mode = CONFIG_STM32H5_TIM4_CH4MODE, +#ifdef CONFIG_STM32H5_TIM4_CH4OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM4_CH4POL, + .idle = CONFIG_STM32H5_TIM4_CH4IDLE, + .pincfg = PWM_TIM4_CH4CFG, + } +#endif + /* No complementary outputs */ + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm4dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 4, + .chan_num = PWM_TIM4_NCHANNELS, + .channels = g_pwm4channels, + .timtype = TIMTYPE_TIM4, + .mode = CONFIG_STM32H5_TIM4_MODE, + .lock = 0, /* No lock */ + .t_dts = 0, /* No t_dts */ +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = 0, /* No deadtime */ +#endif +#if defined(HAVE_TRGO) && defined(STM32_TIM4_TRGO) + .trgo = STM32_TIM4_TRGO, +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM4, +#endif + .base = STM32_TIM4_BASE, + .pclk = TIMCLK_TIM4, +}; +#endif /* CONFIG_STM32H5_TIM4_PWM */ + +#ifdef CONFIG_STM32H5_TIM5_PWM + +static struct stm32_pwmchan_s g_pwm5channels[] = +{ + /* TIM5 has 4 channels */ + +#ifdef CONFIG_STM32H5_TIM5_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM5_CH1MODE, +#ifdef CONFIG_STM32H5_TIM5_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM5_CH1POL, + .idle = CONFIG_STM32H5_TIM5_CH1IDLE, + .pincfg = PWM_TIM5_CH1CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM5_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM5_CH2MODE, +#ifdef CONFIG_STM32H5_TIM5_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM5_CH2POL, + .idle = CONFIG_STM32H5_TIM5_CH2IDLE, + .pincfg = PWM_TIM5_CH2CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM5_CHANNEL3 + { + .channel = 3, + .mode = CONFIG_STM32H5_TIM5_CH3MODE, +#ifdef CONFIG_STM32H5_TIM5_CH3OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM5_CH3POL, + .idle = CONFIG_STM32H5_TIM5_CH3IDLE, + .pincfg = PWM_TIM5_CH3CFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM5_CHANNEL4 + { + .channel = 4, + .mode = CONFIG_STM32H5_TIM5_CH4MODE, +#ifdef CONFIG_STM32H5_TIM5_CH4OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM5_CH4POL, + .idle = CONFIG_STM32H5_TIM5_CH4IDLE, + .pincfg = PWM_TIM5_CH4CFG, + } +#endif + }, +#endif +}; + +static struct stm32_pwmtimer_s g_pwm5dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 5, + .chan_num = PWM_TIM5_NCHANNELS, + .channels = g_pwm5channels, + .timtype = TIMTYPE_TIM5, + .mode = CONFIG_STM32H5_TIM5_MODE, + .lock = 0, /* No lock */ + .t_dts = 0, /* No t_dts */ +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = 0, /* No deadtime */ +#endif +#if defined(HAVE_TRGO) && defined(STM32_TIM5_TRGO) + .trgo = STM32_TIM5_TRGO +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM5, +#endif + .base = STM32_TIM5_BASE, + .pclk = TIMCLK_TIM5, +}; +#endif /* CONFIG_STM32H5_TIM5_PWM */ + +#ifdef CONFIG_STM32H5_TIM8_PWM + +static struct stm32_pwmchan_s g_pwm8channels[] = +{ + /* TIM8 has 4 channels, 4 complementary */ + +#ifdef CONFIG_STM32H5_TIM8_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM8_CH1MODE, +#ifdef HAVE_BREAK + .brk = + { +#ifdef CONFIG_STM32H5_TIM8_BREAK1 + .en1 = 1, + .pol1 = CONFIG_STM32H5_TIM8_BRK1POL, +#endif +#ifdef CONFIG_STM32H5_TIM8_BREAK2 + .en2 = 1, + .pol2 = CONFIG_STM32H5_TIM8_BRK2POL, + .flt2 = CONFIG_STM32H5_TIM8_BRK2FLT, +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH1POL, + .idle = CONFIG_STM32H5_TIM8_CH1IDLE, + .pincfg = PWM_TIM8_CH1CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CH1NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH1NPOL, + .idle = CONFIG_STM32H5_TIM8_CH1NIDLE, + .pincfg = PWM_TIM8_CH1NCFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM8_CH2MODE, +#ifdef CONFIG_STM32H5_TIM8_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH2POL, + .idle = CONFIG_STM32H5_TIM8_CH2IDLE, + .pincfg = PWM_TIM8_CH2CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CH2NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH2NPOL, + .idle = CONFIG_STM32H5_TIM8_CH2NIDLE, + .pincfg = PWM_TIM8_CH2NCFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL3 + { + .channel = 3, + .mode = CONFIG_STM32H5_TIM8_CH3MODE, +#ifdef CONFIG_STM32H5_TIM8_CH3OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH3POL, + .idle = CONFIG_STM32H5_TIM8_CH3IDLE, + .pincfg = PWM_TIM8_CH3CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CH3NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH3NPOL, + .idle = CONFIG_STM32H5_TIM8_CH3NIDLE, + .pincfg = PWM_TIM8_CH3NCFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL4 + { + .channel = 4, + .mode = CONFIG_STM32H5_TIM8_CH4MODE, +#ifdef CONFIG_STM32H5_TIM8_CH4OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH4POL, + .idle = CONFIG_STM32H5_TIM8_CH4IDLE, + .pincfg = PWM_TIM8_CH4CFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL5 + { + .channel = 5, + .mode = CONFIG_STM32H5_TIM8_CH5MODE, +#ifdef CONFIG_STM32H5_TIM8_CH5OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH5POL, + .idle = CONFIG_STM32H5_TIM8_CH5IDLE, + .pincfg = 0, /* Not available externally */ + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL6 + { + .channel = 6, + .mode = CONFIG_STM32H5_TIM8_CH6MODE, +#ifdef CONFIG_STM32H5_TIM8_CH6OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM8_CH6POL, + .idle = CONFIG_STM32H5_TIM8_CH6IDLE, + .pincfg = 0, /* Not available externally */ + } +#endif + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm8dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 8, + .chan_num = PWM_TIM8_NCHANNELS, + .channels = g_pwm8channels, + .timtype = TIMTYPE_TIM8, + .mode = CONFIG_STM32H5_TIM8_MODE, + .lock = CONFIG_STM32H5_TIM8_LOCK, + .t_dts = CONFIG_STM32H5_TIM8_TDTS, +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = CONFIG_STM32H5_TIM8_DEADTIME, +#endif +#if defined(HAVE_TRGO) && defined(STM32_TIM8_TRGO) + .trgo = STM32_TIM8_TRGO, +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM8_UP, +#endif + .base = STM32_TIM8_BASE, + .pclk = TIMCLK_TIM8, +}; +#endif /* CONFIG_STM32H5_TIM8_PWM */ + +#ifdef CONFIG_STM32H5_TIM12_PWM + +static struct stm32_pwmchan_s g_pwm12channels[] = +{ + /* TIM12 has 2 channels */ + +#ifdef CONFIG_STM32H5_TIM12_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM12_CH1MODE, +#ifdef CONFIG_STM32H5_TIM12_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM12_CH1POL, + .idle = CONFIG_STM32H5_TIM12_CH1IDLE, + .pincfg = PWM_TIM12_CH1CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM12_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM12_CH2MODE, +#ifdef CONFIG_STM32H5_TIM12_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM12_CH2POL, + .idle = CONFIG_STM32H5_TIM12_CH2IDLE, + .pincfg = PWM_TIM12_CH2CFG, + } +#endif + /* No complementary outputs */ + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm12dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 12, + .chan_num = PWM_TIM12_NCHANNELS, + .channels = g_pwm12channels, + .timtype = TIMTYPE_TIM12, + .mode = STM32_TIMMODE_COUNTUP, + .lock = 0, /* No lock */ + .t_dts = 0, /* No t_dts */ +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = 0, /* No deadtime */ +#endif +#if defined(HAVE_TRGO) + .trgo = 0, /* TRGO not supported for TIM12 */ +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM12, +#endif + .base = STM32_TIM12_BASE, + .pclk = TIMCLK_TIM12, +}; +#endif /* CONFIG_STM32H5_TIM12_PWM */ + +#ifdef CONFIG_STM32H5_TIM13_PWM + +static struct stm32_pwmchan_s g_pwm13channels[] = +{ + /* TIM13 has 1 channel */ + +#ifdef CONFIG_STM32H5_TIM13_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM13_CH1MODE, +#ifdef CONFIG_STM32H5_TIM13_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM13_CH1POL, + .idle = CONFIG_STM32H5_TIM13_CH1IDLE, + .pincfg = PWM_TIM13_CH1CFG, + } +#endif + /* No complementary outputs */ + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm13dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 13, + .chan_num = PWM_TIM13_NCHANNELS, + .channels = g_pwm13channels, + .timtype = TIMTYPE_TIM13, + .mode = STM32_TIMMODE_COUNTUP, + .lock = 0, /* No lock */ + .t_dts = 0, /* No t_dts */ +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = 0, /* No deadtime */ +#endif +#if defined(HAVE_TRGO) + .trgo = 0, /* TRGO not supported for TIM13 */ +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM13, +#endif + .base = STM32_TIM13_BASE, + .pclk = TIMCLK_TIM13, +}; +#endif /* CONFIG_STM32H5_TIM13_PWM */ + +#ifdef CONFIG_STM32H5_TIM14_PWM + +static struct stm32_pwmchan_s g_pwm14channels[] = +{ + /* TIM14 has 1 channel */ + +#ifdef CONFIG_STM32H5_TIM14_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM14_CH1MODE, +#ifdef CONFIG_STM32H5_TIM14_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM14_CH1POL, + .idle = CONFIG_STM32H5_TIM14_CH1IDLE, + .pincfg = PWM_TIM14_CH1CFG, + } +#endif + /* No complementary outputs */ + } +#endif +}; + +static struct stm32_pwmtimer_s g_pwm14dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 14, + .chan_num = PWM_TIM14_NCHANNELS, + .channels = g_pwm14channels, + .timtype = TIMTYPE_TIM14, + .mode = STM32_TIMMODE_COUNTUP, + .lock = 0, /* No lock */ + .t_dts = 0, /* No t_dts */ +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = 0, /* No deadtime */ +#endif +#if defined(HAVE_TRGO) + .trgo = 0, /* TRGO not supported for TIM14 */ +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM14, +#endif + .base = STM32_TIM14_BASE, + .pclk = TIMCLK_TIM14, +}; +#endif /* CONFIG_STM32H5_TIM14_PWM */ + +#ifdef CONFIG_STM32H5_TIM15_PWM + +static struct stm32_pwmchan_s g_pwm15channels[] = +{ + /* TIM15 has 2 channels, 1 complementary */ + +#ifdef CONFIG_STM32H5_TIM15_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM15_CH1MODE, +#ifdef HAVE_BREAK + .brk = + { +#ifdef CONFIG_STM32H5_TIM15_BREAK1 + .en1 = 1, + .pol1 = CONFIG_STM32H5_TIM15_BRK1POL, +#endif + /* No BREAK2 */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM15_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM15_CH1POL, + .idle = CONFIG_STM32H5_TIM15_CH1IDLE, + .pincfg = PWM_TIM15_CH1CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM15_CH1NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM15_CH1NPOL, + .idle = CONFIG_STM32H5_TIM15_CH1NIDLE, + .pincfg = PWM_TIM15_CH2CFG, + } +#endif + }, +#endif +#ifdef CONFIG_STM32H5_TIM15_CHANNEL2 + { + .channel = 2, + .mode = CONFIG_STM32H5_TIM15_CH2MODE, +#ifdef CONFIG_STM32H5_TIM12_CH2OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM15_CH2POL, + .idle = CONFIG_STM32H5_TIM15_CH2IDLE, + .pincfg = PWM_TIM15_CH2CFG, + } +#endif + /* No complementary outputs */ + }, +#endif +}; + +static struct stm32_pwmtimer_s g_pwm15dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 15, + .chan_num = PWM_TIM15_NCHANNELS, + .channels = g_pwm15channels, + .timtype = TIMTYPE_TIM15, + .mode = STM32_TIMMODE_COUNTUP, + .lock = CONFIG_STM32H5_TIM15_LOCK, + .t_dts = CONFIG_STM32H5_TIM15_TDTS, +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = CONFIG_STM32H5_TIM15_DEADTIME, +#endif +#if defined(HAVE_TRGO) && defined(STM32_TIM15_TRGO) + .trgo = STM32_TIM15_TRGO, +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM15, +#endif + .base = STM32_TIM15_BASE, + .pclk = TIMCLK_TIM15, +}; +#endif /* CONFIG_STM32H5_TIM15_PWM */ + +#ifdef CONFIG_STM32H5_TIM16_PWM + +static struct stm32_pwmchan_s g_pwm16channels[] = +{ + /* TIM16 has 1 channel, 1 complementary */ + +#ifdef CONFIG_STM32H5_TIM16_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM16_CH1MODE, +#ifdef HAVE_BREAK + .brk = + { +#ifdef CONFIG_STM32H5_TIM16_BREAK1 + .en1 = 1, + .pol1 = CONFIG_STM32H5_TIM16_BRK1POL, +#endif + /* No BREAK2 */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM16_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM16_CH1POL, + .idle = CONFIG_STM32H5_TIM16_CH1IDLE, + .pincfg = PWM_TIM16_CH1CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM16_CH1NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM16_CH1NPOL, + .idle = CONFIG_STM32H5_TIM16_CH1NIDLE, + .pincfg = PWM_TIM16_CH2CFG, + } +#endif + }, +#endif +}; + +static struct stm32_pwmtimer_s g_pwm16dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 16, + .chan_num = PWM_TIM16_NCHANNELS, + .channels = g_pwm16channels, + .timtype = TIMTYPE_TIM16, + .mode = STM32_TIMMODE_COUNTUP, + .lock = CONFIG_STM32H5_TIM16_LOCK, + .t_dts = CONFIG_STM32H5_TIM16_TDTS, +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = CONFIG_STM32H5_TIM16_DEADTIME, +#endif +#if defined(HAVE_TRGO) + .trgo = 0, /* TRGO not supported for TIM16 */ +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM16, +#endif + .base = STM32_TIM16_BASE, + .pclk = TIMCLK_TIM16, +}; +#endif /* CONFIG_STM32H5_TIM16_PWM */ + +#ifdef CONFIG_STM32H5_TIM17_PWM + +static struct stm32_pwmchan_s g_pwm17channels[] = +{ + /* TIM17 has 1 channel, 1 complementary */ + +#ifdef CONFIG_STM32H5_TIM17_CHANNEL1 + { + .channel = 1, + .mode = CONFIG_STM32H5_TIM17_CH1MODE, +#ifdef HAVE_BREAK + .brk = + { +#ifdef CONFIG_STM32H5_TIM17_BREAK1 + .en1 = 1, + .pol1 = CONFIG_STM32H5_TIM17_BRK1POL, +#endif + /* No BREAK2 */ + }, +#endif +#ifdef CONFIG_STM32H5_TIM17_CH1OUT + .out1 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM17_CH1POL, + .idle = CONFIG_STM32H5_TIM17_CH1IDLE, + .pincfg = PWM_TIM17_CH1CFG, + }, +#endif +#ifdef CONFIG_STM32H5_TIM17_CH1NOUT + .out2 = + { + .in_use = 1, + .pol = CONFIG_STM32H5_TIM17_CH1NPOL, + .idle = CONFIG_STM32H5_TIM17_CH1NIDLE, + .pincfg = PWM_TIM17_CH2CFG, + } +#endif + }, +#endif +}; + +static struct stm32_pwmtimer_s g_pwm17dev = +{ + .ops = &g_pwmops, +#ifdef CONFIG_STM32H5_PWM_LL_OPS + .llops = &g_llpwmops, +#endif + .timid = 17, + .chan_num = PWM_TIM17_NCHANNELS, + .channels = g_pwm17channels, + .timtype = TIMTYPE_TIM17, + .mode = STM32_TIMMODE_COUNTUP, + .lock = CONFIG_STM32H5_TIM17_LOCK, + .t_dts = CONFIG_STM32H5_TIM17_TDTS, +#ifdef HAVE_PWM_COMPLEMENTARY + .deadtime = CONFIG_STM32H5_TIM17_DEADTIME, +#endif +#if defined(HAVE_TRGO) + .trgo = 0, /* TRGO not supported for TIM17 */ +#endif +#ifdef CONFIG_PWM_PULSECOUNT + .irq = STM32_IRQ_TIM17, +#endif + .base = STM32_TIM17_BASE, + .pclk = TIMCLK_TIM17, +}; +#endif /* CONFIG_STM32H5_TIM17_PWM */ + +/* TODO: support for TIM19,20,21,22 */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pwm_reg_is_32bit + ****************************************************************************/ + +static bool pwm_reg_is_32bit(uint8_t timtype, uint32_t offset) +{ + bool ret = false; + + if (timtype == TIMTYPE_GENERAL32) + { + if (offset == STM32_GTIM_CNT_OFFSET || + offset == STM32_GTIM_ARR_OFFSET || + offset == STM32_GTIM_CCR1_OFFSET || + offset == STM32_GTIM_CCR2_OFFSET || + offset == STM32_GTIM_CCR3_OFFSET || + offset == STM32_GTIM_CCR4_OFFSET) + { + ret = true; + } + } +#ifdef HAVE_IP_TIMERS_V2 + else if (timtype == TIMTYPE_ADVANCED) + { + if (offset == STM32_ATIM_CR2_OFFSET || + offset == STM32_ATIM_CCMR1_OFFSET || + offset == STM32_ATIM_CCMR2_OFFSET || + offset == STM32_ATIM_CCER_OFFSET || + offset == STM32_ATIM_BDTR_OFFSET || + offset == STM32_ATIM_CCMR3_OFFSET || + offset == STM32_ATIM_CCR5_OFFSET) + { + ret = true; + } + } +#endif + + return ret; +} + +/**************************************************************************** + * Name: pwm_getreg + * + * Description: + * Read the value of an PWM timer register + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static uint32_t pwm_getreg(struct stm32_pwmtimer_s *priv, int offset) +{ + uint32_t retval = 0; + + if (pwm_reg_is_32bit(priv->timtype, offset) == true) + { + /* 32-bit register */ + + retval = getreg32(priv->base + offset); + } + else + { + /* 16-bit register */ + + retval = getreg16(priv->base + offset); + } + + /* Return 32-bit value */ + + return retval; +} + +/**************************************************************************** + * Name: pwm_putreg + * + * Description: + * Read the value of an PWM timer register + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void pwm_putreg(struct stm32_pwmtimer_s *priv, int offset, + uint32_t value) +{ + if (pwm_reg_is_32bit(priv->timtype, offset) == true) + { + /* 32-bit register */ + + putreg32(value, priv->base + offset); + } + else + { + /* 16-bit register */ + + putreg16((uint16_t)value, priv->base + offset); + } +} + +/**************************************************************************** + * Name: pwm_modifyreg + * + * Description: + * Modify PWM register (32-bit or 16-bit) + * + * Input Parameters: + * priv - A reference to the PWM block status + * offset - The offset to the register to read + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void pwm_modifyreg(struct stm32_pwmtimer_s *priv, uint32_t offset, + uint32_t clearbits, uint32_t setbits) +{ + if (pwm_reg_is_32bit(priv->timtype, offset) == true) + { + /* 32-bit register */ + + modifyreg32(priv->base + offset, clearbits, setbits); + } + else + { + /* 16-bit register */ + + modifyreg16(priv->base + offset, (uint16_t)clearbits, + (uint16_t)setbits); + } +} + +/**************************************************************************** + * Name: pwm_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_PWM_INFO +static void pwm_dumpregs(struct pwm_lowerhalf_s *dev, const char *msg) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + pwminfo("%s:\n", msg); + if (priv->timid == 16 || priv->timid == 17) + { + pwminfo(" CR1: %04x CR2: %04x DIER: %04x\n", + pwm_getreg(priv, STM32_GTIM_CR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CR2_OFFSET), + pwm_getreg(priv, STM32_GTIM_DIER_OFFSET)); + } + else + { + pwminfo(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + pwm_getreg(priv, STM32_GTIM_CR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CR2_OFFSET), + pwm_getreg(priv, STM32_GTIM_SMCR_OFFSET), + pwm_getreg(priv, STM32_GTIM_DIER_OFFSET)); + } + + if (priv->timid >= 15 && priv->timid <= 17) + { + pwminfo(" SR: %04x EGR: %04x CCMR1: %04x\n", + pwm_getreg(priv, STM32_GTIM_SR_OFFSET), + pwm_getreg(priv, STM32_GTIM_EGR_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCMR1_OFFSET)); + } + else + { + pwminfo(" SR: %04x EGR: %04x CCMR1: %04x CCMR2: %04x\n", + pwm_getreg(priv, STM32_GTIM_SR_OFFSET), + pwm_getreg(priv, STM32_GTIM_EGR_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCMR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCMR2_OFFSET)); + } + + /* REVISIT: CNT and ARR may be 32-bits wide */ + + pwminfo(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + pwm_getreg(priv, STM32_GTIM_CCER_OFFSET), + pwm_getreg(priv, STM32_GTIM_CNT_OFFSET), + pwm_getreg(priv, STM32_GTIM_PSC_OFFSET), + pwm_getreg(priv, STM32_GTIM_ARR_OFFSET)); + + if (priv->timid == 1 || priv->timid == 8 || + (priv->timid >= 15 && priv->timid <= 17)) + { + pwminfo(" RCR: %04x BDTR: %04x\n", + pwm_getreg(priv, STM32_ATIM_RCR_OFFSET), + pwm_getreg(priv, STM32_ATIM_BDTR_OFFSET)); + } + + /* REVISIT: CCR1-CCR4 may be 32-bits wide */ + + if (priv->timid == 16 || priv->timid == 17) + { + pwminfo(" CCR1: %04x\n", + pwm_getreg(priv, STM32_GTIM_CCR1_OFFSET)); + } + else if (priv->timid == 15) + { + pwminfo(" CCR1: %04x CCR2: %04x\n", + pwm_getreg(priv, STM32_GTIM_CCR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCR2_OFFSET)); + } + else + { + pwminfo(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + pwm_getreg(priv, STM32_GTIM_CCR1_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCR2_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCR3_OFFSET), + pwm_getreg(priv, STM32_GTIM_CCR4_OFFSET)); + } + + pwminfo(" DCR: %04x DMAR: %04x\n", + pwm_getreg(priv, STM32_GTIM_DCR_OFFSET), + pwm_getreg(priv, STM32_GTIM_DMAR_OFFSET)); + +#ifdef HAVE_IP_TIMERS_V2 + if (priv->timtype == TIMTYPE_ADVANCED) + { + pwminfo(" CCMR3: %04x CCR5: %04x CCR6: %04x\n", + pwm_getreg(priv, STM32_ATIM_CCMR3_OFFSET), + pwm_getreg(priv, STM32_ATIM_CCR5_OFFSET), + pwm_getreg(priv, STM32_ATIM_CCR6_OFFSET)); + } +#endif +} +#endif + +/**************************************************************************** + * Name: pwm_ccr_update + ****************************************************************************/ + +static int pwm_ccr_update(struct pwm_lowerhalf_s *dev, uint8_t index, + uint32_t ccr) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t offset = 0; + + /* Only ADV timers have CC5 and CC6 */ + +#ifdef HAVE_IP_TIMERS_V2 + if (priv->timtype != TIMTYPE_ADVANCED && (index == 5 || index == 6)) + { + pwmerr("ERROR: No such CCR: %u\n", index); + return -EINVAL; + } +#endif + + switch (index) + { + case STM32_PWM_CHAN1: + { + offset = STM32_GTIM_CCR1_OFFSET; + break; + } + + case STM32_PWM_CHAN2: + { + offset = STM32_GTIM_CCR2_OFFSET; + break; + } + + case STM32_PWM_CHAN3: + { + offset = STM32_GTIM_CCR3_OFFSET; + break; + } + + case STM32_PWM_CHAN4: + { + offset = STM32_GTIM_CCR4_OFFSET; + break; + } + +#ifdef HAVE_IP_TIMERS_V2 + case STM32_PWM_CHAN5: + { + offset = STM32_ATIM_CCR5_OFFSET; + break; + } + + case STM32_PWM_CHAN6: + { + offset = STM32_ATIM_CCR6_OFFSET; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such CCR: %u\n", index); + return -EINVAL; + } + } + + /* Update CCR register */ + + pwm_putreg(priv, offset, ccr); + + return OK; +} + +/**************************************************************************** + * Name: pwm_ccr_get + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_PWM_LL_OPS +static uint32_t pwm_ccr_get(struct pwm_lowerhalf_s *dev, uint8_t index) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t offset = 0; + + switch (index) + { + case STM32_PWM_CHAN1: + { + offset = STM32_GTIM_CCR1_OFFSET; + break; + } + + case STM32_PWM_CHAN2: + { + offset = STM32_GTIM_CCR2_OFFSET; + break; + } + + case STM32_PWM_CHAN3: + { + offset = STM32_GTIM_CCR3_OFFSET; + break; + } + + case STM32_PWM_CHAN4: + { + offset = STM32_GTIM_CCR4_OFFSET; + break; + } + +#ifdef HAVE_IP_TIMERS_V2 + case STM32_PWM_CHAN5: + { + offset = STM32_ATIM_CCR5_OFFSET; + break; + } + + case STM32_PWM_CHAN6: + { + offset = STM32_ATIM_CCR6_OFFSET; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such CCR: %u\n", index); + return -EINVAL; + } + } + + /* Return CCR register */ + + return pwm_getreg(priv, offset); +} +#endif /* CONFIG_STM32H5_PWM_LL_OPS */ + +/**************************************************************************** + * Name: pwm_arr_update + ****************************************************************************/ + +static int pwm_arr_update(struct pwm_lowerhalf_s *dev, uint32_t arr) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + /* Update ARR register */ + + pwm_putreg(priv, STM32_GTIM_ARR_OFFSET, arr); + + return OK; +} + +/**************************************************************************** + * Name: pwm_arr_get + ****************************************************************************/ + +static uint32_t pwm_arr_get(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + return pwm_getreg(priv, STM32_GTIM_ARR_OFFSET); +} + +/**************************************************************************** + * Name: pwm_duty_update + * + * Description: + * Try to change only channel duty + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * channel - Channel to by updated + * duty - New duty + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_duty_update(struct pwm_lowerhalf_s *dev, uint8_t channel, + ub16_t duty) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t reload = 0; + uint32_t ccr = 0; + + /* We don't want compilation warnings if no DEBUGASSERT */ + + UNUSED(priv); + + DEBUGASSERT(priv != NULL); + + pwminfo("TIM%u channel: %u duty: %08" PRIx32 "\n", + priv->timid, channel, duty); + +#ifndef CONFIG_PWM_MULTICHAN + DEBUGASSERT(channel == priv->channels[0].channel); + DEBUGASSERT(duty >= 0 && duty < uitoub16(100)); +#endif + + /* Get the reload values */ + + reload = pwm_arr_get(dev); + + /* Duty cycle: + * + * duty cycle = ccr / reload (fractional value) + */ + + ccr = b16toi(duty * reload + b16HALF); + + pwminfo("ccr: %" PRIu32 "\n", ccr); + + /* Write corresponding CCR register */ + + pwm_ccr_update(dev, channel, ccr); + + return OK; +} + +/**************************************************************************** + * Name: pwm_timer_enable + ****************************************************************************/ + +static int pwm_timer_enable(struct pwm_lowerhalf_s *dev, bool state) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + if (state == true) + { + /* Enable timer counter */ + + pwm_modifyreg(priv, STM32_GTIM_CR1_OFFSET, 0, GTIM_CR1_CEN); + } + else + { + /* Disable timer counter */ + + pwm_modifyreg(priv, STM32_GTIM_CR1_OFFSET, GTIM_CR1_CEN, 0); + } + + return OK; +} + +/**************************************************************************** + * Name: pwm_frequency_update + * + * Description: + * Update a PWM timer frequency + * + ****************************************************************************/ + +static int pwm_frequency_update(struct pwm_lowerhalf_s *dev, + uint32_t frequency) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t reload = 0; + uint32_t timclk = 0; + uint32_t prescaler = 0; + + /* Calculate optimal values for the timer prescaler and for the timer + * reload register. If 'frequency' is the desired frequency, then + * + * reload = timclk / frequency + * timclk = pclk / presc + * + * Or, + * + * reload = pclk / presc / frequency + * + * There are many solutions to this, but the best solution will be the one + * that has the largest reload value and the smallest prescaler value. + * That is the solution that should give us the most accuracy in the timer + * control. Subject to: + * + * 0 <= presc <= 65536 + * 1 <= reload <= 65535 + * + * So presc = pclk / 65535 / frequency would be optimal. + * + * Example: + * + * pclk = 42 MHz + * frequency = 100 Hz + * + * prescaler = 42,000,000 / 65,535 / 100 + * = 6.4 (or 7 -- taking the ceiling always) + * timclk = 42,000,000 / 7 + * = 6,000,000 + * reload = 6,000,000 / 100 + * = 60,000 + */ + + prescaler = (priv->pclk / frequency + 65534) / 65535; + if (prescaler < 1) + { + prescaler = 1; + } + else if (prescaler > 65536) + { + prescaler = 65536; + } + + timclk = priv->pclk / prescaler; + + reload = timclk / frequency; + if (reload < 2) + { + reload = 1; + } + else if (reload > 65535) + { + reload = 65535; + } + else + { + reload--; + } + + pwminfo("TIM%u PCLK: %" PRIu32 " frequency: %" PRIu32 + " TIMCLK: %" PRIu32 + " prescaler: %" PRIu32 " reload: %" PRIu32 "\n", + priv->timid, priv->pclk, frequency, timclk, prescaler, reload); + + /* Set the reload and prescaler values */ + + pwm_arr_update(dev, reload); + pwm_putreg(priv, STM32_GTIM_PSC_OFFSET, (uint16_t)(prescaler - 1)); + + return OK; +} + +/**************************************************************************** + * Name: pwm_timer_configure + * + * Description: + * Initial configuration for PWM timer + * + ****************************************************************************/ + +static int pwm_timer_configure(struct stm32_pwmtimer_s *priv) +{ + uint16_t cr1 = 0; + int ret = OK; + + /* Set up the timer CR1 register: + * + * 1,8 CKD[1:0] ARPE CMS[1:0] DIR OPM URS UDIS CEN + * 2-5 CKD[1:0] ARPE CMS DIR OPM URS UDIS CEN + * 6-7 ARPE OPM URS UDIS CEN + * 9-14 CKD[1:0] ARPE URS UDIS CEN + * 15-17 CKD[1:0] ARPE OPM URS UDIS CEN + */ + + cr1 = pwm_getreg(priv, STM32_GTIM_CR1_OFFSET); + + /* Set the counter mode for the advanced timers (1,8) and most general + * purpose timers (all 2-5, but not 9-17), i.e., all but TIMTYPE_COUNTUP16 + * and TIMTYPE_BASIC + */ + + if (priv->timtype != TIMTYPE_BASIC && priv->timtype != TIMTYPE_COUNTUP16) + { + /* Select the Counter Mode: + * + * GTIM_CR1_EDGE: The counter counts up or down depending on the + * direction bit (DIR). + * GTIM_CR1_CENTER1, GTIM_CR1_CENTER2, GTIM_CR1_CENTER3: The counter + * counts up then down. + * GTIM_CR1_DIR: 0: count up, 1: count down + */ + + cr1 &= ~(GTIM_CR1_DIR | GTIM_CR1_CMS_MASK); + + switch (priv->mode) + { + case STM32_TIMMODE_COUNTUP: + { + cr1 |= GTIM_CR1_EDGE; + break; + } + + case STM32_TIMMODE_COUNTDOWN: + { + cr1 |= GTIM_CR1_EDGE | GTIM_CR1_DIR; + break; + } + + case STM32_TIMMODE_CENTER1: + { + cr1 |= GTIM_CR1_CENTER1; + break; + } + + case STM32_TIMMODE_CENTER2: + { + cr1 |= GTIM_CR1_CENTER2; + break; + } + + case STM32_TIMMODE_CENTER3: + { + cr1 |= GTIM_CR1_CENTER3; + break; + } + + default: + { + pwmerr("ERROR: No such timer mode: %u\n", + (unsigned int)priv->mode); + ret = -EINVAL; + goto errout; + } + } + } + + /* Enable ARR Preload + * TODO: this should be configurable + */ + + cr1 |= GTIM_CR1_ARPE; + + /* Write CR1 */ + + pwm_putreg(priv, STM32_GTIM_CR1_OFFSET, cr1); + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_mode_configure + * + * Description: + * Configure a PWM mode for given channel + * + ****************************************************************************/ + +static int pwm_mode_configure(struct pwm_lowerhalf_s *dev, + uint8_t channel, uint32_t mode) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t chanmode = 0; + uint32_t ocmode = 0; + uint32_t ccmr = 0; + uint32_t offset = 0; + int ret = OK; +#ifdef HAVE_IP_TIMERS_V2 + bool ocmbit = false; +#endif + +#ifdef HAVE_IP_TIMERS_V2 + /* Only advanced timers have channels 5-6 */ + + if (channel > 4 && priv->timtype != TIMTYPE_ADVANCED) + { + pwmerr("ERROR: No such channel: %u\n", channel); + ret = -EINVAL; + goto errout; + } +#endif + + /* Get channel mode + * TODO: configurable preload for CCxR + */ + + switch (mode) + { + case STM32_CHANMODE_FRZN: + { + chanmode = GTIM_CCMR_MODE_FRZN; + break; + } + + case STM32_CHANMODE_CHACT: + { + chanmode = GTIM_CCMR_MODE_CHACT; + break; + } + + case STM32_CHANMODE_CHINACT: + { + chanmode = GTIM_CCMR_MODE_CHINACT; + break; + } + + case STM32_CHANMODE_OCREFTOG: + { + chanmode = GTIM_CCMR_MODE_OCREFTOG; + break; + } + + case STM32_CHANMODE_OCREFLO: + { + chanmode = GTIM_CCMR_MODE_OCREFLO; + break; + } + + case STM32_CHANMODE_OCREFHI: + { + chanmode = GTIM_CCMR_MODE_OCREFHI; + break; + } + + case STM32_CHANMODE_PWM1: + { + chanmode = GTIM_CCMR_MODE_PWM1; + break; + } + + case STM32_CHANMODE_PWM2: + { + chanmode = GTIM_CCMR_MODE_PWM2; + break; + } + +#ifdef HAVE_IP_TIMERS_V2 + case STM32_CHANMODE_COMBINED1: + { + chanmode = ATIM_CCMR_MODE_COMBINED1; + ocmbit = true; + break; + } + + case STM32_CHANMODE_COMBINED2: + { + chanmode = ATIM_CCMR_MODE_COMBINED2; + ocmbit = true; + break; + } + + case STM32_CHANMODE_ASYMMETRIC1: + { + chanmode = ATIM_CCMR_MODE_ASYMMETRIC1; + ocmbit = true; + break; + } + + case STM32_CHANMODE_ASYMMETRIC2: + { + chanmode = ATIM_CCMR_MODE_ASYMMETRIC2; + ocmbit = true; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such mode: %u\n", (unsigned int)mode); + ret = -EINVAL; + goto errout; + } + } + + /* Get CCMR offset */ + + switch (channel) + { + case STM32_PWM_CHAN1: + case STM32_PWM_CHAN2: + { + offset = STM32_GTIM_CCMR1_OFFSET; + break; + } + + case STM32_PWM_CHAN3: + case STM32_PWM_CHAN4: + { + offset = STM32_GTIM_CCMR2_OFFSET; + break; + } + +#ifdef HAVE_IP_TIMERS_V2 + case STM32_PWM_CHAN5: + case STM32_PWM_CHAN6: + { + offset = STM32_ATIM_CCMR3_OFFSET; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such channel: %u\n", channel); + ret = -EINVAL; + goto errout; + } + } + + /* Get current registers */ + + ccmr = pwm_getreg(priv, offset); + + /* PWM mode configuration. + * NOTE: The CCMRx registers are identical if the channels are outputs. + */ + + switch (channel) + { + /* Configure channel 1/3/5 */ + + case STM32_PWM_CHAN1: + case STM32_PWM_CHAN3: +#ifdef HAVE_IP_TIMERS_V2 + case STM32_PWM_CHAN5: +#endif + { + /* Reset current channel 1/3/5 mode configuration */ + + ccmr &= ~(ATIM_CCMR1_CC1S_MASK | ATIM_CCMR1_OC1M_MASK | + ATIM_CCMR1_OC1PE); + + /* Configure CC1/3/5 as output */ + + ocmode |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC1S_SHIFT); + + /* Configure Compare 1/3/5 mode */ + + ocmode |= (chanmode << ATIM_CCMR1_OC1M_SHIFT); + + /* Enable CCR1/3/5 preload */ + + ocmode |= ATIM_CCMR1_OC1PE; + +#ifdef HAVE_IP_TIMERS_V2 + /* Reset current OC bit */ + + ccmr &= ~(ATIM_CCMR1_OC1M); + + /* Set an additional OC1/3/5M bit */ + + if (ocmbit) + { + ocmode |= ATIM_CCMR1_OC1M; + } +#endif + break; + } + + /* Configure channel 2/4/6 */ + + case STM32_PWM_CHAN2: + case STM32_PWM_CHAN4: +#ifdef HAVE_IP_TIMERS_V2 + case STM32_PWM_CHAN6: +#endif + { + /* Reset current channel 2/4/6 mode configuration */ + + ccmr &= ~(ATIM_CCMR1_CC2S_MASK | ATIM_CCMR1_OC2M_MASK | + ATIM_CCMR1_OC2PE); + + /* Configure CC2/4/6 as output */ + + ocmode |= (ATIM_CCMR_CCS_CCOUT << ATIM_CCMR1_CC2S_SHIFT); + + /* Configure Compare 2/4/6 mode */ + + ocmode |= (chanmode << ATIM_CCMR1_OC2M_SHIFT); + + /* Enable CCR2/4/6 preload */ + + ocmode |= ATIM_CCMR1_OC2PE; + +#ifdef HAVE_IP_TIMERS_V2 + /* Reset current OC bit */ + + ccmr &= ~(ATIM_CCMR1_OC2M); + + /* Set an additioneal OC2/4/6M bit */ + + if (ocmbit) + { + ocmode |= ATIM_CCMR1_OC2M; + } +#endif + break; + } + } + + /* Set the selected output compare mode */ + + ccmr |= ocmode; + + /* Write CCMRx registers */ + + pwm_putreg(priv, offset, ccmr); + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_output_configure + * + * Description: + * Configure PWM output for given channel + * + ****************************************************************************/ + +static int pwm_output_configure(struct stm32_pwmtimer_s *priv, + struct stm32_pwmchan_s *chan) +{ + uint32_t cr2 = 0; + uint32_t ccer = 0; + uint8_t channel = 0; + + /* Get channel */ + + channel = chan->channel; + + /* Get current registers state */ + + cr2 = pwm_getreg(priv, STM32_GTIM_CR2_OFFSET); + ccer = pwm_getreg(priv, STM32_GTIM_CCER_OFFSET); + + /* | OISx/OISxN | IDLE | for ADVANCED and COUNTUP16 | CR2 register + * | CCxP/CCxNP | POL | all PWM timers | CCER register + */ + + /* Configure output polarity (all PWM timers) */ + + if (chan->out1.pol == STM32_POL_NEG) + { + ccer |= (GTIM_CCER_CC1P << ((channel - 1) * 4)); + } + else + { + ccer &= ~(GTIM_CCER_CC1P << ((channel - 1) * 4)); + } + +#ifdef HAVE_ADVTIM + if (priv->timtype == TIMTYPE_ADVANCED || + priv->timtype == TIMTYPE_COUNTUP16_N) + { + /* Configure output IDLE State */ + + if (chan->out1.idle == STM32_IDLE_ACTIVE) + { + cr2 |= (ATIM_CR2_OIS1 << ((channel - 1) * 2)); + } + else + { + cr2 &= ~(ATIM_CR2_OIS1 << ((channel - 1) * 2)); + } + +#ifdef HAVE_PWM_COMPLEMENTARY + /* Configure complementary output IDLE state */ + + if (chan->out2.idle == STM32_IDLE_ACTIVE) + { + cr2 |= (ATIM_CR2_OIS1N << ((channel - 1) * 2)); + } + else + { + cr2 &= ~(ATIM_CR2_OIS1N << ((channel - 1) * 2)); + } + + /* Configure complementary output polarity */ + + if (chan->out2.pol == STM32_POL_NEG) + { + ccer |= (ATIM_CCER_CC1NP << ((channel - 1) * 4)); + } + else + { + ccer &= ~(ATIM_CCER_CC1NP << ((channel - 1) * 4)); + } +#endif /* HAVE_PWM_COMPLEMENTARY */ + +#ifdef HAVE_IP_TIMERS_V2 + /* TODO: OIS5 and OIS6 */ + + cr2 &= ~(ATIM_CR2_OIS5 | ATIM_CR2_OIS6); + + /* TODO: CC5P and CC6P */ + + ccer &= ~(ATIM_CCER_CC5P | ATIM_CCER_CC6P); +#endif /* HAVE_IP_TIMERS_V2 */ + } +#ifdef HAVE_GTIM_CCXNP + else +#endif /* HAVE_GTIM_CCXNP */ +#endif /* HAVE_ADVTIM */ +#ifdef HAVE_GTIM_CCXNP + { + /* CCxNP must be cleared if not ADVANCED timer. + * + * REVISIT: not all families have CCxNP bits for GTIM, + * which causes an ugly condition above + */ + + ccer &= ~(GTIM_CCER_CC1NP << ((channel - 1) * 4)); + } +#endif /* HAVE_GTIM_CCXNP */ + + /* Write registers */ + + pwm_modifyreg(priv, STM32_GTIM_CR2_OFFSET, 0, cr2); + pwm_modifyreg(priv, STM32_GTIM_CCER_OFFSET, 0, ccer); + + return OK; +} + +/**************************************************************************** + * Name: pwm_outputs_enable + * + * Description: + * Enable/disable given timer PWM outputs. + * + * NOTE: This is bulk operation - we can enable/disable many outputs + * at one time + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * outputs - outputs to set (look at enum stm32_chan_e in stm32_pwm.h) + * state - Enable/disable operation + * + ****************************************************************************/ + +static int pwm_outputs_enable(struct pwm_lowerhalf_s *dev, + uint16_t outputs, bool state) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t ccer = 0; + uint32_t regval = 0; + + /* Get current register state */ + + ccer = pwm_getreg(priv, STM32_GTIM_CCER_OFFSET); + + /* Get outputs configuration */ + + regval |= ((outputs & STM32_PWM_OUT1) ? GTIM_CCER_CC1E : 0); + regval |= ((outputs & STM32_PWM_OUT1N) ? ATIM_CCER_CC1NE : 0); + regval |= ((outputs & STM32_PWM_OUT2) ? GTIM_CCER_CC2E : 0); + regval |= ((outputs & STM32_PWM_OUT2N) ? ATIM_CCER_CC2NE : 0); + regval |= ((outputs & STM32_PWM_OUT3) ? GTIM_CCER_CC3E : 0); + regval |= ((outputs & STM32_PWM_OUT3N) ? ATIM_CCER_CC3NE : 0); + regval |= ((outputs & STM32_PWM_OUT4) ? GTIM_CCER_CC4E : 0); + + /* NOTE: CC4N doesn't exist, but some docs show configuration bits for it */ + +#ifdef HAVE_IP_TIMERS_V2 + regval |= ((outputs & STM32_PWM_OUT5) ? ATIM_CCER_CC5E : 0); + regval |= ((outputs & STM32_PWM_OUT6) ? ATIM_CCER_CC6E : 0); +#endif + + if (state == true) + { + /* Enable outputs - set bits */ + + ccer |= regval; + } + else + { + /* Disable outputs - reset bits */ + + ccer &= ~regval; + } + + /* Write register */ + + pwm_putreg(priv, STM32_GTIM_CCER_OFFSET, ccer); + + return OK; +} + +#if defined(HAVE_PWM_COMPLEMENTARY) && defined(CONFIG_STM32H5_PWM_LL_OPS) + +/**************************************************************************** + * Name: pwm_deadtime_update + ****************************************************************************/ + +static int pwm_deadtime_update(struct pwm_lowerhalf_s *dev, uint8_t dt) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t bdtr = 0; + int ret = OK; + + /* Check if locked */ + + if (priv->lock > 0) + { + ret = -EACCES; + goto errout; + } + + /* Get current register state */ + + bdtr = pwm_getreg(priv, STM32_ATIM_BDTR_OFFSET); + + /* TODO: check if BDTR not locked */ + + /* Update deadtime */ + + bdtr &= ~(ATIM_BDTR_DTG_MASK); + bdtr |= (dt << ATIM_BDTR_DTG_SHIFT); + + /* Write BDTR register */ + + pwm_putreg(priv, STM32_ATIM_BDTR_OFFSET, bdtr); + +errout: + return ret; +} +#endif + +#ifdef HAVE_TRGO +/**************************************************************************** + * Name: pwm_sync_configure + * + * Description: + * Configure an output synchronisation event for PWM timer (TRGO/TRGO2) + * + ****************************************************************************/ + +static int pwm_sync_configure(struct stm32_pwmtimer_s *priv, + uint8_t trgo) +{ + uint32_t cr2 = 0; + + /* Configure TRGO (4 LSB in trgo) */ + + cr2 |= (((trgo >> 0) & 0x0f) << ATIM_CR2_MMS_SHIFT) & ATIM_CR2_MMS_MASK; + +#ifdef HAVE_IP_TIMERS_V2 + /* Configure TRGO2 (4 MSB in trgo) */ + + cr2 |= (((trgo >> 4) & 0x0f) << ATIM_CR2_MMS2_SHIFT) & ATIM_CR2_MMS2_MASK; +#endif + + /* Write register */ + + pwm_modifyreg(priv, STM32_GTIM_CR2_OFFSET, 0, cr2); + + return OK; +} +#endif + +/**************************************************************************** + * Name: pwm_soft_update + * + * Description: + * Generate an software update event + * + ****************************************************************************/ + +static int pwm_soft_update(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + pwm_putreg(priv, STM32_GTIM_EGR_OFFSET, GTIM_EGR_UG); + + return OK; +} + +/**************************************************************************** + * Name: pwm_soft_break + * + * Description: + * Generate an software break event + * + * Outputs are enabled if state is false. + * Outputs are disabled if state is true. + * + * NOTE: only timers with complementary outputs have BDTR register and + * support software break. + * + ****************************************************************************/ + +static int pwm_soft_break(struct pwm_lowerhalf_s *dev, bool state) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + if (state == true) + { + /* Reset MOE bit */ + + pwm_modifyreg(priv, STM32_ATIM_BDTR_OFFSET, ATIM_BDTR_MOE, 0); + } + else + { + /* Set MOE bit */ + + pwm_modifyreg(priv, STM32_ATIM_BDTR_OFFSET, 0, ATIM_BDTR_MOE); + } + + return OK; +} + +/**************************************************************************** + * Name: pwm_outputs_from_channels + * + * Description: + * Get enabled outputs configuration from the PWM timer state + * + ****************************************************************************/ + +static uint16_t pwm_outputs_from_channels(struct stm32_pwmtimer_s *priv) +{ + uint16_t outputs = 0; + uint8_t channel = 0; + uint8_t i = 0; + + for (i = 0; i < priv->chan_num; i += 1) + { + /* Get channel */ + + channel = priv->channels[i].channel; + + /* Set outputs if channel configured */ + + if (channel != 0) + { + /* Enable output if configured */ + + if (priv->channels[i].out1.in_use == 1) + { + outputs |= (STM32_PWM_OUT1 << ((channel - 1) * 2)); + } + +#ifdef HAVE_PWM_COMPLEMENTARY + /* Enable complementary output if configured */ + + if (priv->channels[i].out2.in_use == 1) + { + outputs |= (STM32_PWM_OUT1N << ((channel - 1) * 2)); + } +#endif + } + } + + return outputs; +} + +#ifdef HAVE_ADVTIM + +/**************************************************************************** + * Name: pwm_break_dt_configure + * + * Description: + * Configure break and deadtime + * + * NOTE: we have to configure all BDTR registers at once due to possible + * lock configuration + * + ****************************************************************************/ + +static int pwm_break_dt_configure(struct stm32_pwmtimer_s *priv) +{ + uint32_t bdtr = 0; + + /* Set the clock division to zero for all (but the basic timers, but there + * should be no basic timers in this context + */ + + pwm_modifyreg(priv, STM32_GTIM_CR1_OFFSET, GTIM_CR1_CKD_MASK, + priv->t_dts << GTIM_CR1_CKD_SHIFT); + +#ifdef HAVE_PWM_COMPLEMENTARY + /* Initialize deadtime */ + + bdtr |= (priv->deadtime << ATIM_BDTR_DTG_SHIFT); +#endif + +#ifdef HAVE_BREAK + /* Configure Break 1 */ + + if (priv->brk.en1 == 1) + { + /* Enable Break 1 */ + + bdtr |= ATIM_BDTR_BKE; + + /* Set Break 1 polarity */ + + bdtr |= (priv->brk.pol1 == STM32_POL_NEG ? ATIM_BDTR_BKP : 0); + } + +#ifdef HAVE_IP_TIMERS_V2 + /* Configure Break 1 */ + + if (priv->brk.en2 == 1) + { + /* Enable Break 2 */ + + bdtr |= ATIM_BDTR_BK2E; + + /* Set Break 2 polarity */ + + bdtr |= (priv->brk.pol2 == STM32_POL_NEG ? ATIM_BDTR_BK2P : 0); + + /* Configure BRK2 filter */ + + bdtr |= (priv->brk.flt2 << ATIM_BDTR_BK2F_SHIFT); + } +#endif /* HAVE_IP_TIMERS_V2 */ +#endif /* HAVE_BREAK */ + + /* Clear the OSSI and OSSR bits in the BDTR register. + * + * REVISIT: this should be configurable + */ + + bdtr &= ~(ATIM_BDTR_OSSI | ATIM_BDTR_OSSR); + + /* Configure lock */ + + bdtr |= priv->lock << ATIM_BDTR_LOCK_SHIFT; + + /* Write BDTR register at once */ + + pwm_putreg(priv, STM32_ATIM_BDTR_OFFSET, bdtr); + + return OK; +} +#endif + +#ifdef CONFIG_PWM_PULSECOUNT + +/**************************************************************************** + * Name: pwm_pulsecount_configure + * + * Description: + * Configure PWM timer in PULSECOUNT mode + * + ****************************************************************************/ + +static int pwm_pulsecount_configure(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint16_t outputs = 0; + uint8_t j = 0; + int ret = OK; + + UNUSED(priv); + + /* NOTE: leave timer counter disabled and all outputs disabled! */ + + /* Disable the timer until we get it configured */ + + pwm_timer_enable(dev, false); + + /* Get configured outputs */ + + outputs = pwm_outputs_from_channels(priv); + + /* REVISIT: Disable outputs */ + + ret = pwm_outputs_enable(dev, outputs, false); + if (ret < 0) + { + goto errout; + } + + /* Initial timer configuration */ + + ret = pwm_timer_configure(priv); + if (ret < 0) + { + goto errout; + } + + /* Configure break and deadtime register */ + + ret = pwm_break_dt_configure(priv); + if (ret < 0) + { + goto errout; + } + + /* Disable software break (enable outputs) */ + + ret = pwm_soft_break(dev, false); + if (ret < 0) + { + goto errout; + } + +#ifdef HAVE_TRGO + /* Configure TRGO/TRGO2 */ + + ret = pwm_sync_configure(priv, priv->trgo); + if (ret < 0) + { + goto errout; + } +#endif + + /* Configure timer channels */ + + for (j = 0; j < priv->chan_num; j++) + { + /* Skip channel if not in use */ + + if (priv->channels[j].channel != 0) + { + /* Update PWM mode */ + + pwm_mode_configure(dev, priv->channels[j].channel, + priv->channels[j].mode); + + /* PWM outputs configuration */ + + pwm_output_configure(priv, &priv->channels[j]); + } + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_pulsecount_timer + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + * TODO: PWM_PULSECOUNT should be configurable for each timer instance + * TODO: PULSECOUNT doesn't work with MULTICHAN at this moment + * + ****************************************************************************/ + +static int pwm_pulsecount_timer(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + ub16_t duty = 0; + uint8_t channel = 0; + uint32_t mode = 0; + uint16_t outputs = 0; + int ret = OK; + + /* If we got here it means that timer instance support pulsecount mode! */ + + DEBUGASSERT(priv != NULL && info != NULL); + + pwminfo("TIM%u channel: %u frequency: %u duty: %08x count: %u\n", + priv->timid, priv->channels[0].channel, info->frequency, + info->duty, info->count); + + DEBUGASSERT(info->frequency > 0); + + /* Channel specific setup */ + + duty = info->duty; + channel = priv->channels[0].channel; + mode = priv->channels[0].mode; + + /* Disable all interrupts and DMA requests, clear all pending status */ + + pwm_putreg(priv, STM32_GTIM_DIER_OFFSET, 0); + pwm_putreg(priv, STM32_GTIM_SR_OFFSET, 0); + + /* Set timer frequency */ + + ret = pwm_frequency_update(dev, info->frequency); + if (ret < 0) + { + goto errout; + } + + /* Update duty cycle */ + + ret = pwm_duty_update(dev, channel, duty); + if (ret < 0) + { + goto errout; + } + + /* If a non-zero repetition count has been selected, then set the + * repetition counter to the count-1 (pwm_pulsecount_start() has already + * assured us that the count value is within range). + */ + + if (info->count > 0) + { + /* Save the remaining count and the number of counts that will have + * elapsed on the first interrupt. + */ + + /* If the first interrupt occurs at the end end of the first + * repetition count, then the count will be the same as the RCR + * value. + */ + + priv->prev = pwm_pulsecount(info->count); + pwm_putreg(priv, STM32_GTIM_RCR_OFFSET, (uint16_t)priv->prev - 1); + + /* Generate an update event to reload the prescaler. This should + * preload the RCR into active repetition counter. + */ + + pwm_soft_update(dev); + + /* Now set the value of the RCR that will be loaded on the next + * update event. + */ + + priv->count = info->count; + priv->curr = pwm_pulsecount(info->count - priv->prev); + pwm_putreg(priv, STM32_GTIM_RCR_OFFSET, (uint16_t)priv->curr - 1); + } + + /* Otherwise, just clear the repetition counter */ + + else + { + /* Set the repetition counter to zero */ + + pwm_putreg(priv, STM32_GTIM_RCR_OFFSET, 0); + + /* Generate an update event to reload the prescaler */ + + pwm_soft_update(dev); + } + + /* Get configured outputs */ + + outputs = pwm_outputs_from_channels(priv); + + /* Enable output */ + + ret = pwm_outputs_enable(dev, outputs, true); + if (ret < 0) + { + goto errout; + } + + /* Setup update interrupt. If info->count is > 0, then we can be + * assured that pwm_pulsecount_start() has already verified: (1) that this + * is an advanced timer, and that (2) the repetition count is within range. + */ + + if (info->count > 0) + { + /* Clear all pending interrupts and enable the update interrupt. */ + + pwm_putreg(priv, STM32_GTIM_SR_OFFSET, 0); + pwm_putreg(priv, STM32_GTIM_DIER_OFFSET, GTIM_DIER_UIE); + + /* Enable the timer */ + + pwm_timer_enable(dev, true); + + /* And enable timer interrupts at the NVIC */ + + up_enable_irq(priv->irq); + } + + pwm_dumpregs(dev, "After starting"); + +errout: + return ret; +} + +#else /* !CONFIG_PWM_PULSECOUNT */ + +/**************************************************************************** + * Name: pwm_configure + * + * Description: + * Configure PWM timer in normal mode (no PULSECOUNT) + * + ****************************************************************************/ + +static int pwm_configure(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint16_t outputs = 0; + uint8_t j = 0; + int ret = OK; + + /* NOTE: leave timer counter disabled and all outputs disabled! */ + + /* Get configured outputs */ + + outputs = pwm_outputs_from_channels(priv); + + /* Disable outputs */ + + ret = pwm_outputs_enable(dev, outputs, false); + if (ret < 0) + { + goto errout; + } + + /* Disable the timer until we get it configured */ + + pwm_timer_enable(dev, false); + + /* Initial timer configuration */ + + ret = pwm_timer_configure(priv); + if (ret < 0) + { + goto errout; + } + + /* Some special setup for advanced timers */ + +#ifdef HAVE_ADVTIM + if (priv->timtype == TIMTYPE_ADVANCED || + priv->timtype == TIMTYPE_COUNTUP16_N) + { + /* Configure break and deadtime register */ + + ret = pwm_break_dt_configure(priv); + if (ret < 0) + { + goto errout; + } + +#ifdef HAVE_TRGO + /* Configure TRGO/TRGO2 */ + + ret = pwm_sync_configure(priv, priv->trgo); + if (ret < 0) + { + goto errout; + } +#endif + } +#endif + + /* Configure timer channels */ + + for (j = 0; j < priv->chan_num; j++) + { + /* Skip channel if not in use */ + + if (priv->channels[j].channel != 0) + { + /* Update PWM mode */ + + ret = pwm_mode_configure(dev, priv->channels[j].channel, + priv->channels[j].mode); + if (ret < 0) + { + goto errout; + } + + /* PWM outputs configuration */ + + ret = pwm_output_configure(priv, &priv->channels[j]); + if (ret < 0) + { + goto errout; + } + } + } + + /* Disable software break at the end of the outputs configuration (enablei + * outputs). + * + * NOTE: Only timers with complementary outputs have BDTR register and + * support software break. + */ + + if (priv->timtype == TIMTYPE_ADVANCED || + priv->timtype == TIMTYPE_COUNTUP16_N) + { + ret = pwm_soft_break(dev, false); + if (ret < 0) + { + goto errout; + } + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_duty_channels_update + * + * Description: + * Update duty cycle for given channels + * + ****************************************************************************/ + +static int pwm_duty_channels_update(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint8_t channel = 0; + ub16_t duty = 0; + int ret = OK; +#ifdef CONFIG_PWM_MULTICHAN + int i = 0; + int j = 0; +#endif + +#ifdef CONFIG_PWM_MULTICHAN + for (i = 0; i < CONFIG_PWM_NCHANNELS; i++) +#endif + { +#ifdef CONFIG_PWM_MULTICHAN + /* Break the loop if all following channels are not configured */ + + if (info->channels[i].channel == -1) + { + break; + } + + duty = info->channels[i].duty; + channel = info->channels[i].channel; + + /* A value of zero means to skip this channel */ + + if (channel != 0) + { + /* Find the channel */ + + for (j = 0; j < priv->chan_num; j++) + { + if (priv->channels[j].channel == channel) + { + break; + } + } + + /* Check range */ + + if (j >= priv->chan_num) + { + pwmerr("ERROR: No such channel: %u\n", channel); + ret = -EINVAL; + goto errout; + } +#else + duty = info->duty; + channel = priv->channels[0].channel; +#endif + + /* Update duty cycle */ + + ret = pwm_duty_update(dev, channel, duty); + if (ret < 0) + { + goto errout; + } +#ifdef CONFIG_PWM_MULTICHAN + } +#endif + } + +errout: + return OK; +} + +/**************************************************************************** + * Name: pwm_timer + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_timer(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint16_t outputs = 0; + int ret = OK; + + DEBUGASSERT(priv != NULL && info != NULL); + +#if defined(CONFIG_PWM_MULTICHAN) + pwminfo("TIM%u frequency: %" PRIu32 "\n", + priv->timid, info->frequency); +#else + pwminfo("TIM%u channel: %u frequency: %" PRIu32 " duty: %08" PRIx32 "\n", + priv->timid, priv->channels[0].channel, + info->frequency, info->duty); +#endif + + DEBUGASSERT(info->frequency > 0); +#ifndef CONFIG_PWM_MULTICHAN + DEBUGASSERT(info->duty >= 0 && info->duty < uitoub16(100)); +#endif + + /* TODO: what if we have pwm running and we want disable some channels ? */ + + /* Set timer frequency */ + + ret = pwm_frequency_update(dev, info->frequency); + if (ret < 0) + { + goto errout; + } + + /* Channel specific configuration */ + + ret = pwm_duty_channels_update(dev, info); + if (ret < 0) + { + goto errout; + } + + /* Set the advanced timer's repetition counter */ + +#ifdef HAVE_ADVTIM + if (priv->timtype == TIMTYPE_ADVANCED || + priv->timtype == TIMTYPE_COUNTUP16_N) + { + /* If a non-zero repetition count has been selected, then set the + * repetition counter to the count-1 (pwm_start() has already + * assured us that the count value is within range). + */ + + /* Set the repetition counter to zero */ + + pwm_putreg(priv, STM32_ATIM_RCR_OFFSET, 0); + + /* Generate an update event to reload the prescaler */ + + pwm_soft_update(dev); + } + else +#endif + { + /* Generate an update event to reload the prescaler (all timers) */ + + pwm_soft_update(dev); + } + + /* Get configured outputs */ + + outputs = pwm_outputs_from_channels(priv); + + /* Enable outputs */ + + ret = pwm_outputs_enable(dev, outputs, true); + if (ret < 0) + { + goto errout; + } + + /* Just enable the timer, leaving all interrupts disabled */ + + pwm_timer_enable(dev, true); + + pwm_dumpregs(dev, "After starting"); + +errout: + return ret; +} +#endif /* CONFIG_PWM_PULSECOUNT */ + +#ifdef HAVE_PWM_INTERRUPT + +/**************************************************************************** + * Name: pwm_interrupt + * + * Description: + * Handle timer interrupts. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_interrupt(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint16_t regval; + + /* Verify that this is an update interrupt. Nothing else is expected. */ + + regval = pwm_getreg(priv, STM32_ATIM_SR_OFFSET); + DEBUGASSERT((regval & ATIM_SR_UIF) != 0); + + /* Clear the UIF interrupt bit */ + + pwm_putreg(priv, STM32_ATIM_SR_OFFSET, (regval & ~ATIM_SR_UIF)); + + /* Calculate the new count by subtracting the number of pulses + * since the last interrupt. + */ + + if (priv->count <= priv->prev) + { + /* We are finished. Turn off the master output to stop the output as + * quickly as possible. + */ + + pwm_soft_break(dev, true); + + /* Disable first interrupts, stop and reset the timer */ + + pwm_stop(dev); + + /* Then perform the callback into the upper half driver */ + + pwm_expired(priv->handle); + + priv->handle = NULL; + priv->count = 0; + priv->prev = 0; + priv->curr = 0; + } + else + { + /* Decrement the count of pulses remaining using the number of + * pulses generated since the last interrupt. + */ + + priv->count -= priv->prev; + + /* Set up the next RCR. Set 'prev' to the value of the RCR that + * was loaded when the update occurred (just before this interrupt) + * and set 'curr' to the current value of the RCR register (which + * will bet loaded on the next update event). + */ + + priv->prev = priv->curr; + priv->curr = pwm_pulsecount(priv->count - priv->prev); + pwm_putreg(priv, STM32_ATIM_RCR_OFFSET, (uint16_t)priv->curr - 1); + } + + /* Now all of the time critical stuff is done so we can do some debug + * output. + */ + + pwminfo("Update interrupt SR: %04x prev: %u curr: %u count: %u\n", + regval, priv->prev, priv->curr, priv->count); + + return OK; +} + +/**************************************************************************** + * Name: pwm_tim1/8interrupt + * + * Description: + * Handle timer 1 and 8 interrupts. + * + * Input Parameters: + * Standard NuttX interrupt inputs + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_TIM1_PWM +static int pwm_tim1interrupt(int irq, void *context, void *arg) +{ + return pwm_interrupt((struct pwm_lowerhalf_s *)&g_pwm1dev); +} +#endif /* CONFIG_STM32H5_TIM1_PWM */ + +#ifdef CONFIG_STM32H5_TIM8_PWM +static int pwm_tim8interrupt(int irq, void *context, void *arg) +{ + return pwm_interrupt((struct pwm_lowerhalf_s *)&g_pwm8dev); +} +#endif /* CONFIG_STM32H5_TIM8_PWM */ + +/**************************************************************************** + * Name: pwm_pulsecount + * + * Description: + * Pick an optimal pulse count to program the RCR. + * + * Input Parameters: + * count - The total count remaining + * + * Returned Value: + * The recommended pulse count + * + ****************************************************************************/ + +static uint8_t pwm_pulsecount(uint32_t count) +{ + /* REVISIT: RCR_REP_MAX for GTIM or ATIM ? */ + + /* The the remaining pulse count is less than or equal to the maximum, the + * just return the count. + */ + + if (count <= ATIM_RCR_REP_MAX) + { + return (uint8_t)count; + } + + /* Otherwise, we have to be careful. We do not want a small number of + * counts at the end because we might have trouble responding fast enough. + * If the remaining count is less than 150% of the maximum, then return + * half of the maximum. In this case the final sequence will be between 64 + * and 128. + */ + + else if (count < (3 * ATIM_RCR_REP_MAX / 2)) + { + return (uint8_t)((ATIM_RCR_REP_MAX + 1) >> 1); + } + + /* Otherwise, return the maximum. The final count will be 64 or more */ + + else + { + return (uint8_t)ATIM_RCR_REP_MAX; + } +} +#endif /* HAVE_PWM_INTERRUPT */ + +/**************************************************************************** + * Name: pwm_set_apb_clock + * + * Description: + * Enable or disable APB clock for the timer peripheral + * + * Input Parameters: + * priv - A reference to the PWM block status + * on - Enable clock if 'on' is 'true' and disable if 'false' + * + ****************************************************************************/ + +static int pwm_set_apb_clock(struct stm32_pwmtimer_s *priv, bool on) +{ + uint32_t en_bit = 0; + uint32_t regaddr = 0; + int ret = OK; + + pwminfo("timer %d clock enable: %d\n", priv->timid, on ? 1 : 0); + + /* Determine which timer to configure */ + + switch (priv->timid) + { +#ifdef CONFIG_STM32H5_TIM1_PWM + case 1: + { + regaddr = TIMRCCEN_TIM1; + en_bit = TIMEN_TIM1; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM2_PWM + case 2: + { + regaddr = TIMRCCEN_TIM2; + en_bit = TIMEN_TIM2; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM3_PWM + case 3: + { + regaddr = TIMRCCEN_TIM3; + en_bit = TIMEN_TIM3; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM4_PWM + case 4: + { + regaddr = TIMRCCEN_TIM4; + en_bit = TIMEN_TIM4; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM5_PWM + case 5: + { + regaddr = TIMRCCEN_TIM5; + en_bit = TIMEN_TIM5; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM8_PWM + case 8: + { + regaddr = TIMRCCEN_TIM8; + en_bit = TIMEN_TIM8; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM12_PWM + case 12: + { + regaddr = TIMRCCEN_TIM12; + en_bit = TIMEN_TIM12; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM13_PWM + case 13: + { + regaddr = TIMRCCEN_TIM13; + en_bit = TIMEN_TIM13; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM14_PWM + case 14: + { + regaddr = TIMRCCEN_TIM14; + en_bit = TIMEN_TIM14; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM15_PWM + case 15: + { + regaddr = TIMRCCEN_TIM15; + en_bit = TIMEN_TIM15; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM16_PWM + case 16: + { + regaddr = TIMRCCEN_TIM16; + en_bit = TIMEN_TIM16; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM17_PWM + case 17: + { + regaddr = TIMRCCEN_TIM17; + en_bit = TIMEN_TIM17; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such timer configured %d\n", priv->timid); + ret = -EINVAL; + goto errout; + } + } + + /* Enable/disable APB 1/2 clock for timer */ + + pwminfo("RCC_APBxENR base: %08" PRIx32 " bits: %04" PRIx32 "\n", + regaddr, en_bit); + + if (on) + { + modifyreg32(regaddr, 0, en_bit); + } + else + { + modifyreg32(regaddr, en_bit, 0); + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_setup + * + * Description: + * This method is called when the driver is opened. The lower half driver + * should configure and initialize the device so that it is ready for use. + * It should not, however, output pulses until the start method is called. + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + * Assumptions: + * APB1 or 2 clocking for the GPIOs has already been configured by the RCC + * logic at power up. + * + ****************************************************************************/ + +static int pwm_setup(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t pincfg = 0; + int ret = OK; + int i = 0; + + pwminfo("TIM%u\n", priv->timid); + + /* Enable APB1/2 clocking for timer. */ + + ret = pwm_set_apb_clock(priv, true); + if (ret < 0) + { + goto errout; + } + + pwm_dumpregs(dev, "Initially"); + + /* Configure the PWM output pins, but do not start the timer yet */ + + for (i = 0; i < priv->chan_num; i++) + { + if (priv->channels[i].out1.in_use == 1) + { + /* Do not configure the pin if pincfg is not specified. + * This prevents overwriting the PA0 configuration if the + * channel is used internally. + */ + + pincfg = priv->channels[i].out1.pincfg; + if (pincfg != 0) + { + pwminfo("pincfg: %08" PRIx32 "\n", pincfg); + + stm32_configgpio(pincfg); + pwm_dumpgpio(pincfg, "PWM setup"); + } + } + +#ifdef HAVE_PWM_COMPLEMENTARY + if (priv->channels[i].out2.in_use == 1) + { + pincfg = priv->channels[i].out2.pincfg; + + /* Do not configure the pin if pincfg is not specified. + * This prevents overwriting the PA0 configuration if the + * channel is used internally. + */ + + if (pincfg != 0) + { + pwminfo("pincfg: %08" PRIx32 "\n", pincfg); + + stm32_configgpio(pincfg); + pwm_dumpgpio(pincfg, "PWM setup"); + } + } +#endif + } + + /* Configure PWM timer with the selected configuration. + * + * NOTE: We configure PWM here during setup, but leave timer with disabled + * counter, disabled outputs, not configured frequency and duty cycle + */ + +#ifdef CONFIG_PWM_PULSECOUNT + ret = pwm_pulsecount_configure(dev); +#else + ret = pwm_configure(dev); +#endif + if (ret < 0) + { + pwmerr("failed to configure PWM %d\n", priv->timid); + ret = ERROR; + goto errout; + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_shutdown + * + * Description: + * This method is called when the driver is closed. The lower half driver + * stop pulsed output, free any resources, disable the timer hardware, and + * put the system into the lowest possible power usage state + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_shutdown(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + uint32_t pincfg = 0; + int i = 0; + int ret = OK; + + pwminfo("TIM%u\n", priv->timid); + + /* Make sure that the output has been stopped */ + + pwm_stop(dev); + + /* Disable APB1/2 clocking for timer. */ + + ret = pwm_set_apb_clock(priv, false); + if (ret < 0) + { + goto errout; + } + + /* Then put the GPIO pins back to the default state */ + + for (i = 0; i < priv->chan_num; i++) + { + pincfg = priv->channels[i].out1.pincfg; + if (pincfg != 0) + { + pwminfo("pincfg: %08" PRIx32 "\n", pincfg); + + pincfg &= (GPIO_PORT_MASK | GPIO_PIN_MASK); + pincfg |= PINCFG_DEFAULT; + + stm32_configgpio(pincfg); + } + +#ifdef HAVE_PWM_COMPLEMENTARY + pincfg = priv->channels[i].out2.pincfg; + if (pincfg != 0) + { + pwminfo("pincfg: %08" PRIx32 "\n", pincfg); + + pincfg &= (GPIO_PORT_MASK | GPIO_PIN_MASK); + pincfg |= PINCFG_DEFAULT; + + stm32_configgpio(pincfg); + } +#endif + } + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_start + * + * Description: + * (Re-)initialize the timer resources and start the pulsed output + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * info - A reference to the characteristics of the pulsed output + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +#ifdef CONFIG_PWM_PULSECOUNT +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info, + void *handle) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + /* Check if a pulsecount has been selected */ + + if (info->count > 0) + { + /* Only the advanced timers (TIM1,8 can support the pulse counting) + * REVISIT: verify if TIMTYPE_COUNTUP16_N works with it + */ + + if (priv->timtype != TIMTYPE_ADVANCED) + { + pwmerr("ERROR: TIM%u cannot support pulse count: %u\n", + priv->timid, info->count); + return -EPERM; + } + } + + /* Save the handle */ + + priv->handle = handle; + + /* Start the time */ + + return pwm_pulsecount_timer(dev, info); +} +#else /* !CONFIG_PWM_PULSECOUNT */ +static int pwm_start(struct pwm_lowerhalf_s *dev, + const struct pwm_info_s *info) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + int ret = OK; + + /* if frequency has not changed we just update duty */ + + if (info->frequency == priv->frequency) + { +#ifdef CONFIG_PWM_MULTICHAN + int i; + + for (i = 0; ret == OK && i < CONFIG_PWM_NCHANNELS; i++) + { + /* Break the loop if all following channels are not configured */ + + if (info->channels[i].channel == -1) + { + break; + } + + /* Set output if channel configured */ + + if (info->channels[i].channel != 0) + { + ret = pwm_duty_update(dev, info->channels[i].channel, + info->channels[i].duty); + } + } +#else + ret = pwm_duty_update(dev, priv->channels[0].channel, info->duty); +#endif /* CONFIG_PWM_MULTICHAN */ + } + else + { + ret = pwm_timer(dev, info); + + /* Save current frequency */ + + if (ret == OK) + { + priv->frequency = info->frequency; + } + } + + return ret; +} +#endif /* CONFIG_PWM_PULSECOUNT */ + +/**************************************************************************** + * Name: pwm_stop + * + * Description: + * Stop the pulsed output and reset the timer resources + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + * Assumptions: + * This function is called to stop the pulsed output at anytime. This + * method is also called from the timer interrupt handler when a repetition + * count expires... automatically stopping the timer. + * + ****************************************************************************/ + +static int pwm_stop(struct pwm_lowerhalf_s *dev) +{ + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + irqstate_t flags = 0; + uint32_t resetbit = 0; + uint32_t regaddr = 0; + uint32_t regval = 0; + int ret = OK; + + pwminfo("TIM%u\n", priv->timid); + + /* Determine which timer to reset */ + + switch (priv->timid) + { +#ifdef CONFIG_STM32H5_TIM1_PWM + case 1: + { + regaddr = TIMRCCRST_TIM1; + resetbit = TIMRST_TIM1; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM2_PWM + case 2: + { + regaddr = TIMRCCRST_TIM2; + resetbit = TIMRST_TIM2; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM3_PWM + case 3: + { + regaddr = TIMRCCRST_TIM3; + resetbit = TIMRST_TIM3; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM4_PWM + case 4: + { + regaddr = TIMRCCRST_TIM4; + resetbit = TIMRST_TIM4; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM5_PWM + case 5: + { + regaddr = TIMRCCRST_TIM5; + resetbit = TIMRST_TIM5; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM8_PWM + case 8: + { + regaddr = TIMRCCRST_TIM8; + resetbit = TIMRST_TIM8; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM12_PWM + case 12: + { + regaddr = TIMRCCRST_TIM12; + resetbit = TIMRST_TIM12; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM13_PWM + case 13: + { + regaddr = TIMRCCRST_TIM13; + resetbit = TIMRST_TIM13; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM14_PWM + case 14: + { + regaddr = TIMRCCRST_TIM14; + resetbit = TIMRST_TIM14; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM15_PWM + case 15: + { + regaddr = TIMRCCRST_TIM15; + resetbit = TIMRST_TIM15; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM16_PWM + case 16: + { + regaddr = TIMRCCRST_TIM16; + resetbit = TIMRST_TIM16; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM17_PWM + case 17: + { + regaddr = TIMRCCRST_TIM17; + resetbit = TIMRST_TIM17; + break; + } +#endif + + default: + { + ret = -EINVAL; + goto errout; + } + } + + /* Disable interrupts momentary to stop any ongoing timer processing and + * to prevent any concurrent access to the reset register. + */ + + flags = enter_critical_section(); + +#ifndef CONFIG_PWM_PULSECOUNT + /* Stopped so frequency is zero */ + + priv->frequency = 0; +#endif + + /* Disable further interrupts and stop the timer */ + + pwm_putreg(priv, STM32_GTIM_DIER_OFFSET, 0); + pwm_putreg(priv, STM32_GTIM_SR_OFFSET, 0); + + /* Reset the timer - stopping the output and putting the timer back + * into a state where pwm_start() can be called. + */ + + regval = getreg32(regaddr); + regval |= resetbit; + putreg32(regval, regaddr); + + regval &= ~resetbit; + putreg32(regval, regaddr); + + /* Clear all channels */ + + pwm_putreg(priv, STM32_GTIM_CCR1_OFFSET, 0); + pwm_putreg(priv, STM32_GTIM_CCR2_OFFSET, 0); + pwm_putreg(priv, STM32_GTIM_CCR3_OFFSET, 0); + pwm_putreg(priv, STM32_GTIM_CCR4_OFFSET, 0); + + leave_critical_section(flags); + + pwminfo("regaddr: %08" PRIx32 " resetbit: %08" PRIx32 "\n", + regaddr, resetbit); + pwm_dumpregs(dev, "After stop"); + +errout: + return ret; +} + +/**************************************************************************** + * Name: pwm_ioctl + * + * Description: + * Lower-half logic may support platform-specific ioctl commands + * + * Input Parameters: + * dev - A reference to the lower half PWM driver state structure + * cmd - The ioctl command + * arg - The argument accompanying the ioctl command + * + * Returned Value: + * Zero on success; a negated errno value on failure + * + ****************************************************************************/ + +static int pwm_ioctl(struct pwm_lowerhalf_s *dev, int cmd, + unsigned long arg) +{ +#ifdef CONFIG_DEBUG_PWM_INFO + struct stm32_pwmtimer_s *priv = (struct stm32_pwmtimer_s *)dev; + + /* There are no platform-specific ioctl commands */ + + pwminfo("TIM%u\n", priv->timid); +#endif + return -ENOTTY; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. The number of valid timer + * IDs varies with the STM32 MCU and MCU family but is somewhere in + * the range of {1,..,17}. + * + * Returned Value: + * On success, a pointer to the STM32 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +struct pwm_lowerhalf_s *stm32_pwminitialize(int timer) +{ + struct stm32_pwmtimer_s *lower = NULL; + + pwminfo("TIM%u\n", timer); + + switch (timer) + { +#ifdef CONFIG_STM32H5_TIM1_PWM + case 1: + { + lower = &g_pwm1dev; + + /* Attach but disable the TIM1 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_tim1interrupt, NULL); + up_disable_irq(lower->irq); +#endif + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM2_PWM + case 2: + { + lower = &g_pwm2dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM3_PWM + case 3: + { + lower = &g_pwm3dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM4_PWM + case 4: + { + lower = &g_pwm4dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM5_PWM + case 5: + { + lower = &g_pwm5dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM8_PWM + case 8: + { + lower = &g_pwm8dev; + + /* Attach but disable the TIM8 update interrupt */ + +#ifdef CONFIG_PWM_PULSECOUNT + irq_attach(lower->irq, pwm_tim8interrupt, NULL); + up_disable_irq(lower->irq); +#endif + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM12_PWM + case 12: + { + lower = &g_pwm12dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM13_PWM + case 13: + { + lower = &g_pwm13dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM14_PWM + case 14: + { + lower = &g_pwm14dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM15_PWM + case 15: + { + lower = &g_pwm15dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM16_PWM + case 16: + { + lower = &g_pwm16dev; + break; + } +#endif + +#ifdef CONFIG_STM32H5_TIM17_PWM + case 17: + { + lower = &g_pwm17dev; + break; + } +#endif + + default: + { + pwmerr("ERROR: No such timer configured %d\n", timer); + lower = NULL; + goto errout; + } + } + +errout: + return (struct pwm_lowerhalf_s *)lower; +} diff --git a/arch/arm/src/stm32h5/stm32_pwm.h b/arch/arm/src/stm32h5/stm32_pwm.h new file mode 100644 index 00000000000..94ccee9e216 --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_pwm.h @@ -0,0 +1,1061 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_pwm.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_PWM_H +#define __ARCH_ARM_SRC_STM32H5_STM32_PWM_H + +/* The STM32 does not have dedicated PWM hardware. Rather, pulsed output + * control is a capability of the STM32 timers. The logic in this file + * implements the lower half of the standard, NuttX PWM interface using the + * STM32 timers. That interface is described in include/nuttx/timers/pwm.h. + */ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include "chip.h" + +#ifdef CONFIG_STM32H5_PWM +# include +# include "hardware/stm32_tim.h" +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Timer devices may be used for different purposes. One special purpose is + * to generate modulated outputs for such things as motor control. + * If CONFIG_STM32H5_TIMn is defined then the CONFIG_STM32H5_TIMn_PWM must + * also be defined to indicate that timer "n" is intended to be used for + * pulsed output signal generation. + */ + +#ifndef CONFIG_STM32H5_TIM1 +# undef CONFIG_STM32H5_TIM1_PWM +#endif +#ifndef CONFIG_STM32H5_TIM2 +# undef CONFIG_STM32H5_TIM2_PWM +#endif +#ifndef CONFIG_STM32H5_TIM3 +# undef CONFIG_STM32H5_TIM3_PWM +#endif +#ifndef CONFIG_STM32H5_TIM4 +# undef CONFIG_STM32H5_TIM4_PWM +#endif +#ifndef CONFIG_STM32H5_TIM5 +# undef CONFIG_STM32H5_TIM5_PWM +#endif +#ifndef CONFIG_STM32H5_TIM8 +# undef CONFIG_STM32H5_TIM8_PWM +#endif +#ifndef CONFIG_STM32H5_TIM12 +# undef CONFIG_STM32H5_TIM12_PWM +#endif +#ifndef CONFIG_STM32H5_TIM13 +# undef CONFIG_STM32H5_TIM13_PWM +#endif +#ifndef CONFIG_STM32H5_TIM14 +# undef CONFIG_STM32H5_TIM14_PWM +#endif +#ifndef CONFIG_STM32H5_TIM15 +# undef CONFIG_STM32H5_TIM15_PWM +#endif +#ifndef CONFIG_STM32H5_TIM16 +# undef CONFIG_STM32H5_TIM16_PWM +#endif +#ifndef CONFIG_STM32H5_TIM17 +# undef CONFIG_STM32H5_TIM17_PWM +#endif + +/* The basic timers (timer 6 and 7) are not capable of generating output + * pulses + */ + +#undef CONFIG_STM32H5_TIM6_PWM +#undef CONFIG_STM32H5_TIM7_PWM + +/* Check if PWM support for any channel is enabled. */ + +#ifdef CONFIG_STM32H5_PWM + +/* PWM driver channels configuration */ + +#ifdef CONFIG_STM32H5_PWM_MULTICHAN + +#ifdef CONFIG_STM32H5_TIM1_CHANNEL1 +# define PWM_TIM1_CHANNEL1 1 +#else +# define PWM_TIM1_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL2 +# define PWM_TIM1_CHANNEL2 1 +#else +# define PWM_TIM1_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL3 +# define PWM_TIM1_CHANNEL3 1 +#else +# define PWM_TIM1_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL4 +# define PWM_TIM1_CHANNEL4 1 +#else +# define PWM_TIM1_CHANNEL4 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL5 +# define PWM_TIM1_CHANNEL5 1 +#else +# define PWM_TIM1_CHANNEL5 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CHANNEL6 +# define PWM_TIM1_CHANNEL6 1 +#else +# define PWM_TIM1_CHANNEL6 0 +#endif +#define PWM_TIM1_NCHANNELS (PWM_TIM1_CHANNEL1 + PWM_TIM1_CHANNEL2 + \ + PWM_TIM1_CHANNEL3 + PWM_TIM1_CHANNEL4 + \ + PWM_TIM1_CHANNEL5 + PWM_TIM1_CHANNEL6) + +#ifdef CONFIG_STM32H5_TIM2_CHANNEL1 +# define PWM_TIM2_CHANNEL1 1 +#else +# define PWM_TIM2_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM2_CHANNEL2 +# define PWM_TIM2_CHANNEL2 1 +#else +# define PWM_TIM2_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32H5_TIM2_CHANNEL3 +# define PWM_TIM2_CHANNEL3 1 +#else +# define PWM_TIM2_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32H5_TIM2_CHANNEL4 +# define PWM_TIM2_CHANNEL4 1 +#else +# define PWM_TIM2_CHANNEL4 0 +#endif +#define PWM_TIM2_NCHANNELS (PWM_TIM2_CHANNEL1 + PWM_TIM2_CHANNEL2 + \ + PWM_TIM2_CHANNEL3 + PWM_TIM2_CHANNEL4) + +#ifdef CONFIG_STM32H5_TIM3_CHANNEL1 +# define PWM_TIM3_CHANNEL1 1 +#else +# define PWM_TIM3_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM3_CHANNEL2 +# define PWM_TIM3_CHANNEL2 1 +#else +# define PWM_TIM3_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32H5_TIM3_CHANNEL3 +# define PWM_TIM3_CHANNEL3 1 +#else +# define PWM_TIM3_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32H5_TIM3_CHANNEL4 +# define PWM_TIM3_CHANNEL4 1 +#else +# define PWM_TIM3_CHANNEL4 0 +#endif +#define PWM_TIM3_NCHANNELS (PWM_TIM3_CHANNEL1 + PWM_TIM3_CHANNEL2 + \ + PWM_TIM3_CHANNEL3 + PWM_TIM3_CHANNEL4) + +#ifdef CONFIG_STM32H5_TIM4_CHANNEL1 +# define PWM_TIM4_CHANNEL1 1 +#else +# define PWM_TIM4_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM4_CHANNEL2 +# define PWM_TIM4_CHANNEL2 1 +#else +# define PWM_TIM4_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32H5_TIM4_CHANNEL3 +# define PWM_TIM4_CHANNEL3 1 +#else +# define PWM_TIM4_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32H5_TIM4_CHANNEL4 +# define PWM_TIM4_CHANNEL4 1 +#else +# define PWM_TIM4_CHANNEL4 0 +#endif +#define PWM_TIM4_NCHANNELS (PWM_TIM4_CHANNEL1 + PWM_TIM4_CHANNEL2 + \ + PWM_TIM4_CHANNEL3 + PWM_TIM4_CHANNEL4) + +#ifdef CONFIG_STM32H5_TIM5_CHANNEL1 +# define PWM_TIM5_CHANNEL1 1 +#else +# define PWM_TIM5_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM5_CHANNEL2 +# define PWM_TIM5_CHANNEL2 1 +#else +# define PWM_TIM5_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32H5_TIM5_CHANNEL3 +# define PWM_TIM5_CHANNEL3 1 +#else +# define PWM_TIM5_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32H5_TIM5_CHANNEL4 +# define PWM_TIM5_CHANNEL4 1 +#else +# define PWM_TIM5_CHANNEL4 0 +#endif +#define PWM_TIM5_NCHANNELS (PWM_TIM5_CHANNEL1 + PWM_TIM5_CHANNEL2 + \ + PWM_TIM5_CHANNEL3 + PWM_TIM5_CHANNEL4) + +#ifdef CONFIG_STM32H5_TIM8_CHANNEL1 +# define PWM_TIM8_CHANNEL1 1 +#else +# define PWM_TIM8_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL2 +# define PWM_TIM8_CHANNEL2 1 +#else +# define PWM_TIM8_CHANNEL2 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL3 +# define PWM_TIM8_CHANNEL3 1 +#else +# define PWM_TIM8_CHANNEL3 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL4 +# define PWM_TIM8_CHANNEL4 1 +#else +# define PWM_TIM8_CHANNEL4 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL5 +# define PWM_TIM8_CHANNEL5 1 +#else +# define PWM_TIM8_CHANNEL5 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CHANNEL6 +# define PWM_TIM8_CHANNEL6 1 +#else +# define PWM_TIM8_CHANNEL6 0 +#endif +#define PWM_TIM8_NCHANNELS (PWM_TIM8_CHANNEL1 + PWM_TIM8_CHANNEL2 + \ + PWM_TIM8_CHANNEL3 + PWM_TIM8_CHANNEL4 + \ + PWM_TIM8_CHANNEL5 + PWM_TIM8_CHANNEL6) + +#ifdef CONFIG_STM32H5_TIM12_CHANNEL1 +# define PWM_TIM12_CHANNEL1 1 +#else +# define PWM_TIM12_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM12_CHANNEL2 +# define PWM_TIM12_CHANNEL2 1 +#else +# define PWM_TIM12_CHANNEL2 0 +#endif +#define PWM_TIM12_NCHANNELS (PWM_TIM12_CHANNEL1 + PWM_TIM12_CHANNEL2) + +#ifdef CONFIG_STM32H5_TIM13_CHANNEL1 +# define PWM_TIM13_CHANNEL1 1 +#else +# define PWM_TIM13_CHANNEL1 0 +#endif +#define PWM_TIM13_NCHANNELS (PWM_TIM13_CHANNEL1) + +#ifdef CONFIG_STM32H5_TIM14_CHANNEL1 +# define PWM_TIM14_CHANNEL1 1 +#else +# define PWM_TIM14_CHANNEL1 0 +#endif +#define PWM_TIM14_NCHANNELS (PWM_TIM14_CHANNEL1) + +#ifdef CONFIG_STM32H5_TIM15_CHANNEL1 +# define PWM_TIM15_CHANNEL1 1 +#else +# define PWM_TIM15_CHANNEL1 0 +#endif +#ifdef CONFIG_STM32H5_TIM15_CHANNEL2 +# define PWM_TIM15_CHANNEL2 1 +#else +# define PWM_TIM15_CHANNEL2 0 +#endif +#define PWM_TIM15_NCHANNELS (PWM_TIM15_CHANNEL1 + PWM_TIM15_CHANNEL2) + +#ifdef CONFIG_STM32H5_TIM16_CHANNEL1 +# define PWM_TIM16_CHANNEL1 1 +#else +# define PWM_TIM16_CHANNEL1 0 +#endif +#define PWM_TIM16_NCHANNELS PWM_TIM16_CHANNEL1 + +#ifdef CONFIG_STM32H5_TIM17_CHANNEL1 +# define PWM_TIM17_CHANNEL1 1 +#else +# define PWM_TIM17_CHANNEL1 0 +#endif +#define PWM_TIM17_NCHANNELS PWM_TIM17_CHANNEL1 + +#else /* !CONFIG_PWM_MULTICHAN */ + +/* For each timer that is enabled for PWM usage, we need the following + * additional configuration settings: + * + * CONFIG_STM32H5_TIMx_CHANNEL - Specifies the timer output channel + * {1,..,4} + * PWM_TIMx_CHn - One of the values defined in chip/stm32*_pinmap.h. + * In the case where there are multiple pin selections, the correct + * setting must be provided in the arch/board/board.h file. + * + * NOTE: + * The STM32 timers are each capable of generating different signals on + * each of the four channels with different duty cycles. That capability + * is not supported by this driver: Only one output channel per timer. + */ + +#ifdef CONFIG_STM32H5_TIM1_PWM +# if !defined(CONFIG_STM32H5_TIM1_CHANNEL) +# error "CONFIG_STM32H5_TIM1_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM1_CHANNEL == 1 +# define CONFIG_STM32H5_TIM1_CHANNEL1 1 +# define CONFIG_STM32H5_TIM1_CH1MODE CONFIG_STM32H5_TIM1_CHMODE +# elif CONFIG_STM32H5_TIM1_CHANNEL == 2 +# define CONFIG_STM32H5_TIM1_CHANNEL2 1 +# define CONFIG_STM32H5_TIM1_CH2MODE CONFIG_STM32H5_TIM1_CHMODE +# elif CONFIG_STM32H5_TIM1_CHANNEL == 3 +# define CONFIG_STM32H5_TIM1_CHANNEL3 1 +# define CONFIG_STM32H5_TIM1_CH3MODE CONFIG_STM32H5_TIM1_CHMODE +# elif CONFIG_STM32H5_TIM1_CHANNEL == 4 +# define CONFIG_STM32H5_TIM1_CHANNEL4 1 +# define CONFIG_STM32H5_TIM1_CH4MODE CONFIG_STM32H5_TIM1_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM1_CHANNEL" +# endif +# define PWM_TIM1_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM2_PWM +# if !defined(CONFIG_STM32H5_TIM2_CHANNEL) +# error "CONFIG_STM32H5_TIM2_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM2_CHANNEL == 1 +# define CONFIG_STM32H5_TIM2_CHANNEL1 1 +# define CONFIG_STM32H5_TIM2_CH1MODE CONFIG_STM32H5_TIM2_CHMODE +# elif CONFIG_STM32H5_TIM2_CHANNEL == 2 +# define CONFIG_STM32H5_TIM2_CHANNEL2 1 +# define CONFIG_STM32H5_TIM2_CH2MODE CONFIG_STM32H5_TIM2_CHMODE +# elif CONFIG_STM32H5_TIM2_CHANNEL == 3 +# define CONFIG_STM32H5_TIM2_CHANNEL3 1 +# define CONFIG_STM32H5_TIM2_CH3MODE CONFIG_STM32H5_TIM2_CHMODE +# elif CONFIG_STM32H5_TIM2_CHANNEL == 4 +# define CONFIG_STM32H5_TIM2_CHANNEL4 1 +# define CONFIG_STM32H5_TIM2_CH4MODE CONFIG_STM32H5_TIM2_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM2_CHANNEL" +# endif +# define PWM_TIM2_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM3_PWM +# if !defined(CONFIG_STM32H5_TIM3_CHANNEL) +# error "CONFIG_STM32H5_TIM3_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM3_CHANNEL == 1 +# define CONFIG_STM32H5_TIM3_CHANNEL1 1 +# define CONFIG_STM32H5_TIM3_CH1MODE CONFIG_STM32H5_TIM3_CHMODE +# elif CONFIG_STM32H5_TIM3_CHANNEL == 2 +# define CONFIG_STM32H5_TIM3_CHANNEL2 1 +# define CONFIG_STM32H5_TIM3_CH2MODE CONFIG_STM32H5_TIM3_CHMODE +# elif CONFIG_STM32H5_TIM3_CHANNEL == 3 +# define CONFIG_STM32H5_TIM3_CHANNEL3 1 +# define CONFIG_STM32H5_TIM3_CH3MODE CONFIG_STM32H5_TIM3_CHMODE +# elif CONFIG_STM32H5_TIM3_CHANNEL == 4 +# define CONFIG_STM32H5_TIM3_CHANNEL4 1 +# define CONFIG_STM32H5_TIM3_CH4MODE CONFIG_STM32H5_TIM3_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM3_CHANNEL" +# endif +# define PWM_TIM3_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM4_PWM +# if !defined(CONFIG_STM32H5_TIM4_CHANNEL) +# error "CONFIG_STM32H5_TIM4_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM4_CHANNEL == 1 +# define CONFIG_STM32H5_TIM4_CHANNEL1 1 +# define CONFIG_STM32H5_TIM4_CH1MODE CONFIG_STM32H5_TIM4_CHMODE +# elif CONFIG_STM32H5_TIM4_CHANNEL == 2 +# define CONFIG_STM32H5_TIM4_CHANNEL2 1 +# define CONFIG_STM32H5_TIM4_CH2MODE CONFIG_STM32H5_TIM4_CHMODE +# elif CONFIG_STM32H5_TIM4_CHANNEL == 3 +# define CONFIG_STM32H5_TIM4_CHANNEL3 1 +# define CONFIG_STM32H5_TIM4_CH3MODE CONFIG_STM32H5_TIM4_CHMODE +# elif CONFIG_STM32H5_TIM4_CHANNEL == 4 +# define CONFIG_STM32H5_TIM4_CHANNEL4 1 +# define CONFIG_STM32H5_TIM4_CH4MODE CONFIG_STM32H5_TIM4_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM4_CHANNEL" +# endif +# define PWM_TIM4_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM5_PWM +# if !defined(CONFIG_STM32H5_TIM5_CHANNEL) +# error "CONFIG_STM32H5_TIM5_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM5_CHANNEL == 1 +# define CONFIG_STM32H5_TIM5_CHANNEL1 1 +# define CONFIG_STM32H5_TIM5_CH1MODE CONFIG_STM32H5_TIM5_CHMODE +# elif CONFIG_STM32H5_TIM5_CHANNEL == 2 +# define CONFIG_STM32H5_TIM5_CHANNEL2 1 +# define CONFIG_STM32H5_TIM5_CH2MODE CONFIG_STM32H5_TIM5_CHMODE +# elif CONFIG_STM32H5_TIM5_CHANNEL == 3 +# define CONFIG_STM32H5_TIM5_CHANNEL3 1 +# define CONFIG_STM32H5_TIM5_CH3MODE CONFIG_STM32H5_TIM5_CHMODE +# elif CONFIG_STM32H5_TIM5_CHANNEL == 4 +# define CONFIG_STM32H5_TIM5_CHANNEL4 1 +# define CONFIG_STM32H5_TIM5_CH4MODE CONFIG_STM32H5_TIM5_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM5_CHANNEL" +# endif +# define PWM_TIM5_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM8_PWM +# if !defined(CONFIG_STM32H5_TIM8_CHANNEL) +# error "CONFIG_STM32H5_TIM8_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM8_CHANNEL == 1 +# define CONFIG_STM32H5_TIM8_CHANNEL1 1 +# define CONFIG_STM32H5_TIM8_CH1MODE CONFIG_STM32H5_TIM8_CHMODE +# elif CONFIG_STM32H5_TIM8_CHANNEL == 2 +# define CONFIG_STM32H5_TIM8_CHANNEL2 1 +# define CONFIG_STM32H5_TIM8_CH2MODE CONFIG_STM32H5_TIM8_CHMODE +# elif CONFIG_STM32H5_TIM8_CHANNEL == 3 +# define CONFIG_STM32H5_TIM8_CHANNEL3 1 +# define CONFIG_STM32H5_TIM8_CH3MODE CONFIG_STM32H5_TIM8_CHMODE +# elif CONFIG_STM32H5_TIM8_CHANNEL == 4 +# define CONFIG_STM32H5_TIM8_CHANNEL4 1 +# define CONFIG_STM32H5_TIM8_CH4MODE CONFIG_STM32H5_TIM8_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM8_CHANNEL" +# endif +# define PWM_TIM8_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM12_PWM +# if !defined(CONFIG_STM32H5_TIM12_CHANNEL) +# error "CONFIG_STM32H5_TIM12_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM12_CHANNEL == 1 +# define CONFIG_STM32H5_TIM12_CHANNEL1 1 +# define CONFIG_STM32H5_TIM12_CH1MODE CONFIG_STM32H5_TIM12_CHMODE +# elif CONFIG_STM32H5_TIM12_CHANNEL == 2 +# define CONFIG_STM32H5_TIM12_CHANNEL2 1 +# define CONFIG_STM32H5_TIM12_CH2MODE CONFIG_STM32H5_TIM12_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM12_CHANNEL" +# endif +# define PWM_TIM12_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM13_PWM +# if !defined(CONFIG_STM32H5_TIM13_CHANNEL) +# error "CONFIG_STM32H5_TIM13_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM13_CHANNEL == 1 +# define CONFIG_STM32H5_TIM13_CHANNEL1 1 +# define CONFIG_STM32H5_TIM13_CH1MODE CONFIG_STM32H5_TIM13_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM13_CHANNEL" +# endif +# define PWM_TIM13_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM14_PWM +# if !defined(CONFIG_STM32H5_TIM14_CHANNEL) +# error "CONFIG_STM32H5_TIM14_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM14_CHANNEL == 1 +# define CONFIG_STM32H5_TIM14_CHANNEL1 1 +# define CONFIG_STM32H5_TIM14_CH1MODE CONFIG_STM32H5_TIM14_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM14_CHANNEL" +# endif +# define PWM_TIM14_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM15_PWM +# if !defined(CONFIG_STM32H5_TIM15_CHANNEL) +# error "CONFIG_STM32H5_TIM15_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM15_CHANNEL == 1 +# define CONFIG_STM32H5_TIM15_CHANNEL1 1 +# define CONFIG_STM32H5_TIM15_CH1MODE CONFIG_STM32H5_TIM15_CHMODE +# elif CONFIG_STM32H5_TIM15_CHANNEL == 2 +# define CONFIG_STM32H5_TIM15_CHANNEL2 1 +# define CONFIG_STM32H5_TIM15_CH2MODE CONFIG_STM32H5_TIM15_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM15_CHANNEL" +# endif +# define PWM_TIM15_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM16_PWM +# if !defined(CONFIG_STM32H5_TIM16_CHANNEL) +# error "CONFIG_STM32H5_TIM16_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM16_CHANNEL == 1 +# define CONFIG_STM32H5_TIM16_CHANNEL1 1 +# define CONFIG_STM32H5_TIM16_CH1MODE CONFIG_STM32H5_TIM16_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM16_CHANNEL" +# endif +# define PWM_TIM16_NCHANNELS 1 +#endif + +#ifdef CONFIG_STM32H5_TIM17_PWM +# if !defined(CONFIG_STM32H5_TIM17_CHANNEL) +# error "CONFIG_STM32H5_TIM17_CHANNEL must be provided" +# elif CONFIG_STM32H5_TIM17_CHANNEL == 1 +# define CONFIG_STM32H5_TIM17_CHANNEL1 1 +# define CONFIG_STM32H5_TIM17_CH1MODE CONFIG_STM32H5_TIM17_CHMODE +# else +# error "Unsupported value of CONFIG_STM32H5_TIM17_CHANNEL" +# endif +# define PWM_TIM17_NCHANNELS 1 +#endif + +#endif /* CONFIG_STM32H5_PWM_MULTICHAN */ + +#ifdef CONFIG_STM32H5_TIM1_CH1OUT +# define PWM_TIM1_CH1CFG GPIO_TIM1_CH1OUT +#else +# define PWM_TIM1_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CH1NOUT +# define PWM_TIM1_CH1NCFG GPIO_TIM1_CH1NOUT +#else +# define PWM_TIM1_CH1NCFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CH2OUT +# define PWM_TIM1_CH2CFG GPIO_TIM1_CH2OUT +#else +# define PWM_TIM1_CH2CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CH2NOUT +# define PWM_TIM1_CH2NCFG GPIO_TIM1_CH2NOUT +#else +# define PWM_TIM1_CH2NCFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CH3OUT +# define PWM_TIM1_CH3CFG GPIO_TIM1_CH3OUT +#else +# define PWM_TIM1_CH3CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CH3NOUT +# define PWM_TIM1_CH3NCFG GPIO_TIM1_CH3NOUT +#else +# define PWM_TIM1_CH3NCFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM1_CH4OUT +# define PWM_TIM1_CH4CFG GPIO_TIM1_CH4OUT +#else +# define PWM_TIM1_CH4CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM2_CH1OUT +# define PWM_TIM2_CH1CFG GPIO_TIM2_CH1OUT +#else +# define PWM_TIM2_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM2_CH2OUT +# define PWM_TIM2_CH2CFG GPIO_TIM2_CH2OUT +#else +# define PWM_TIM2_CH2CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM2_CH3OUT +# define PWM_TIM2_CH3CFG GPIO_TIM2_CH3OUT +#else +# define PWM_TIM2_CH3CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM2_CH4OUT +# define PWM_TIM2_CH4CFG GPIO_TIM2_CH4OUT +#else +# define PWM_TIM2_CH4CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM3_CH1OUT +# define PWM_TIM3_CH1CFG GPIO_TIM3_CH1OUT +#else +# define PWM_TIM3_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM3_CH2OUT +# define PWM_TIM3_CH2CFG GPIO_TIM3_CH2OUT +#else +# define PWM_TIM3_CH2CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM3_CH3OUT +# define PWM_TIM3_CH3CFG GPIO_TIM3_CH3OUT +#else +# define PWM_TIM3_CH3CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM3_CH4OUT +# define PWM_TIM3_CH4CFG GPIO_TIM3_CH4OUT +#else +# define PWM_TIM3_CH4CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM4_CH1OUT +# define PWM_TIM4_CH1CFG GPIO_TIM4_CH1OUT +#else +# define PWM_TIM4_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM4_CH2OUT +# define PWM_TIM4_CH2CFG GPIO_TIM4_CH2OUT +#else +# define PWM_TIM4_CH2CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM4_CH3OUT +# define PWM_TIM4_CH3CFG GPIO_TIM4_CH3OUT +#else +# define PWM_TIM4_CH3CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM4_CH4OUT +# define PWM_TIM4_CH4CFG GPIO_TIM4_CH4OUT +#else +# define PWM_TIM4_CH4CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM5_CH1OUT +# define PWM_TIM5_CH1CFG GPIO_TIM5_CH1OUT +#else +# define PWM_TIM5_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM5_CH2OUT +# define PWM_TIM5_CH2CFG GPIO_TIM5_CH2OUT +#else +# define PWM_TIM5_CH2CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM5_CH3OUT +# define PWM_TIM5_CH3CFG GPIO_TIM5_CH3OUT +#else +# define PWM_TIM5_CH3CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM5_CH4OUT +# define PWM_TIM5_CH4CFG GPIO_TIM5_CH4OUT +#else +# define PWM_TIM5_CH4CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM8_CH1OUT +# define PWM_TIM8_CH1CFG GPIO_TIM8_CH1OUT +#else +# define PWM_TIM8_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CH1NOUT +# define PWM_TIM8_CH1NCFG GPIO_TIM8_CH1NOUT +#else +# define PWM_TIM8_CH1NCFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CH2OUT +# define PWM_TIM8_CH2CFG GPIO_TIM8_CH2OUT +#else +# define PWM_TIM8_CH2CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CH2NOUT +# define PWM_TIM8_CH2NCFG GPIO_TIM8_CH2NOUT +#else +# define PWM_TIM8_CH2NCFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CH3OUT +# define PWM_TIM8_CH3CFG GPIO_TIM8_CH3OUT +#else +# define PWM_TIM8_CH3CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CH3NOUT +# define PWM_TIM8_CH3NCFG GPIO_TIM8_CH3NOUT +#else +# define PWM_TIM8_CH3NCFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM8_CH4OUT +# define PWM_TIM8_CH4CFG GPIO_TIM8_CH4OUT +#else +# define PWM_TIM8_CH4CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM12_CH1OUT +# define PWM_TIM12_CH1CFG GPIO_TIM12_CH1OUT +#else +# define PWM_TIM12_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM12_CH2OUT +# define PWM_TIM12_CH2CFG GPIO_TIM12_CH2OUT +#else +# define PWM_TIM12_CH2CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM13_CH1OUT +# define PWM_TIM13_CH1CFG GPIO_TIM13_CH1OUT +#else +# define PWM_TIM13_CH1CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM14_CH1OUT +# define PWM_TIM14_CH1CFG GPIO_TIM14_CH1OUT +#else +# define PWM_TIM14_CH1CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM15_CH1OUT +# define PWM_TIM15_CH1CFG GPIO_TIM15_CH1OUT +#else +# define PWM_TIM15_CH1CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM15_CH1NOUT +# define PWM_TIM15_CH1NCFG GPIO_TIM15_CH1NOUT +#else +# define PWM_TIM15_CH1NCFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM15_CH2OUT +# define PWM_TIM15_CH2CFG GPIO_TIM15_CH2OUT +#else +# define PWM_TIM15_CH2CFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM16_CH1OUT +# define PWM_TIM16_CH1CFG GPIO_TIM16_CH1OUT +#else +# define PWM_TIM16_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM16_CH1NOUT +# define PWM_TIM16_CH1NCFG GPIO_TIM16_CH1NOUT +#else +# define PWM_TIM16_CH1NCFG 0 +#endif + +#ifdef CONFIG_STM32H5_TIM17_CH1OUT +# define PWM_TIM17_CH1CFG GPIO_TIM17_CH1OUT +#else +# define PWM_TIM17_CH1CFG 0 +#endif +#ifdef CONFIG_STM32H5_TIM17_CH1NOUT +# define PWM_TIM17_CH1NCFG GPIO_TIM17_CH1NOUT +#else +# define PWM_TIM17_CH1NCFG 0 +#endif + +/* Complementary outputs support */ + +#if defined(CONFIG_STM32H5_TIM1_CH1NOUT) || defined(CONFIG_STM32H5_TIM1_CH2NOUT) || \ + defined(CONFIG_STM32H5_TIM1_CH3NOUT) +# define HAVE_TIM1_COMPLEMENTARY +#endif +#if defined(CONFIG_STM32H5_TIM8_CH1NOUT) || defined(CONFIG_STM32H5_TIM8_CH2NOUT) || \ + defined(CONFIG_STM32H5_TIM8_CH3NOUT) +# define HAVE_TIM8_COMPLEMENTARY +#endif +#if defined(CONFIG_STM32H5_TIM15_CH1NOUT) +# define HAVE_TIM15_COMPLEMENTARY +#endif +#if defined(CONFIG_STM32H5_TIM16_CH1NOUT) +# define HAVE_TIM16_COMPLEMENTARY +#endif +#if defined(CONFIG_STM32H5_TIM17_CH1NOUT) +# define HAVE_TIM17_COMPLEMENTARY +#endif +#if defined(HAVE_TIM1_COMPLEMENTARY) || defined(HAVE_TIM8_COMPLEMENTARY) || \ + defined(HAVE_TIM15_COMPLEMENTARY) || defined(HAVE_TIM16_COMPLEMENTARY) || \ + defined(HAVE_TIM17_COMPLEMENTARY) +# define HAVE_PWM_COMPLEMENTARY +#endif + +/* Low-level ops helpers ****************************************************/ + +#ifdef CONFIG_STM32H5_PWM_LL_OPS + +/* NOTE: + * low-level ops accept pwm_lowerhalf_s as first argument, but llops access + * can be found in stm32_pwm_dev_s + */ + +#define PWM_SETUP(dev) \ + (dev)->ops->setup((struct pwm_lowerhalf_s *)dev) +#define PWM_SHUTDOWN(dev) \ + (dev)->ops->shutdown((struct pwm_lowerhalf_s *)dev) +#define PWM_CCR_UPDATE(dev, index, ccr) \ + (dev)->llops->ccr_update((struct pwm_lowerhalf_s *)dev, index, ccr) +#define PWM_MODE_UPDATE(dev, index, mode) \ + (dev)->llops->mode_update((struct pwm_lowerhalf_s *)dev, index, mode) +#define PWM_CCR_GET(dev, index) \ + (dev)->llops->ccr_get((struct pwm_lowerhalf_s *)dev, index) +#define PWM_ARR_UPDATE(dev, arr) \ + (dev)->llops->arr_update((struct pwm_lowerhalf_s *)dev, arr) +#define PWM_ARR_GET(dev) \ + (dev)->llops->arr_get((struct pwm_lowerhalf_s *)dev) +#define PWM_OUTPUTS_ENABLE(dev, out, state) \ + (dev)->llops->outputs_enable((struct pwm_lowerhalf_s *)dev, out, state) +#define PWM_SOFT_UPDATE(dev) \ + (dev)->llops->soft_update((struct pwm_lowerhalf_s *)dev) +#define PWM_CONFIGURE(dev) \ + (dev)->llops->configure((struct pwm_lowerhalf_s *)dev) +#define PWM_SOFT_BREAK(dev, state) \ + (dev)->llops->soft_break((struct pwm_lowerhalf_s *)dev, state) +#define PWM_FREQ_UPDATE(dev, freq) \ + (dev)->llops->freq_update((struct pwm_lowerhalf_s *)dev, freq) +#define PWM_TIM_ENABLE(dev, state) \ + (dev)->llops->tim_enable((struct pwm_lowerhalf_s *)dev, state) +#ifdef CONFIG_DEBUG_PWM_INFO +# define PWM_DUMP_REGS(dev, msg) \ + (dev)->llops->dump_regs((struct pwm_lowerhalf_s *)dev, msg) +#else +# define PWM_DUMP_REGS(dev, msg) +#endif +#define PWM_DT_UPDATE(dev, dt) \ + (dev)->llops->dt_update((struct pwm_lowerhalf_s *)dev, dt) + +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Timer mode */ + +enum stm32_pwm_tim_mode_e +{ + STM32_TIMMODE_COUNTUP = 0, + STM32_TIMMODE_COUNTDOWN = 1, + STM32_TIMMODE_CENTER1 = 2, + STM32_TIMMODE_CENTER2 = 3, + STM32_TIMMODE_CENTER3 = 4, +}; + +/* Timer output polarity */ + +enum stm32_pwm_pol_e +{ + STM32_POL_POS = 0, + STM32_POL_NEG = 1, +}; + +/* Timer output IDLE state */ + +enum stm32_pwm_idle_e +{ + STM32_IDLE_INACTIVE = 0, + STM32_IDLE_ACTIVE = 1 +}; + +/* PWM channel mode */ + +enum stm32_pwm_chanmode_e +{ + STM32_CHANMODE_FRZN = 0, /* CCRx matches has no effects on outputs */ + STM32_CHANMODE_CHACT = 1, /* OCxREF active on match */ + STM32_CHANMODE_CHINACT = 2, /* OCxREF inactive on match */ + STM32_CHANMODE_OCREFTOG = 3, /* OCxREF toggles when TIMy_CNT=TIMyCCRx */ + STM32_CHANMODE_OCREFLO = 4, /* OCxREF is forced low */ + STM32_CHANMODE_OCREFHI = 5, /* OCxREF is forced high */ + STM32_CHANMODE_PWM1 = 6, /* PWM mode 1 */ + STM32_CHANMODE_PWM2 = 7, /* PWM mode 2 */ +#ifdef HAVE_IP_TIMERS_V2 + STM32_CHANMODE_COMBINED1 = 8, /* Combined PWM mode 1 */ + STM32_CHANMODE_COMBINED2 = 9, /* Combined PWM mode 2 */ + STM32_CHANMODE_ASYMMETRIC1 = 10, /* Asymmetric PWM mode 1 */ + STM32_CHANMODE_ASYMMETRIC2 = 11, /* Asymmetric PWM mode 2 */ +#endif +}; + +/* PWM timer channel */ + +enum stm32_pwm_chan_e +{ + STM32_PWM_CHAN1 = 1, + STM32_PWM_CHAN2 = 2, + STM32_PWM_CHAN3 = 3, + STM32_PWM_CHAN4 = 4, +#ifdef HAVE_IP_TIMERS_V2 + STM32_PWM_CHAN5 = 5, + STM32_PWM_CHAN6 = 6, +#endif +}; + +/* PWM timer channel output */ + +enum stm32_pwm_output_e +{ + STM32_PWM_OUT1 = (1 << 0), + STM32_PWM_OUT1N = (1 << 1), + STM32_PWM_OUT2 = (1 << 2), + STM32_PWM_OUT2N = (1 << 3), + STM32_PWM_OUT3 = (1 << 4), + STM32_PWM_OUT3N = (1 << 5), + STM32_PWM_OUT4 = (1 << 6), + + /* 1 << 7 reserved - no complementary output for CH4 */ + +#ifdef HAVE_IP_TIMERS_V2 + /* Only available inside micro */ + + STM32_PWM_OUT5 = (1 << 8), + + /* 1 << 9 reserved - no complementary output for CH5 */ + + STM32_PWM_OUT6 = (1 << 10), + + /* 1 << 11 reserved - no complementary output for CH6 */ +#endif +}; + +#ifdef CONFIG_STM32H5_PWM_LL_OPS + +/* This structure provides the publicly visible representation of the + * "lower-half" PWM driver structure. + */ + +struct stm32_pwm_dev_s +{ + /* The first field of this state structure must be a pointer to the PWM + * callback structure to be consistent with upper-half PWM driver. + */ + + const struct pwm_ops_s *ops; + + /* Publicly visible portion of the "lower-half" PWM driver structure */ + + const struct stm32_pwm_ops_s *llops; + + /* Require cast-compatibility with private "lower-half" PWM structure */ +}; + +/* Low-level operations for PWM */ + +struct pwm_lowerhalf_s; +struct stm32_pwm_ops_s +{ + /* Update CCR register */ + + int (*ccr_update)(struct pwm_lowerhalf_s *dev, + uint8_t index, uint32_t ccr); + + /* Update PWM mode */ + + int (*mode_update)(struct pwm_lowerhalf_s *dev, + uint8_t index, uint32_t mode); + + /* Get CCR register */ + + uint32_t (*ccr_get)(struct pwm_lowerhalf_s *dev, uint8_t index); + + /* Update ARR register */ + + int (*arr_update)(struct pwm_lowerhalf_s *dev, uint32_t arr); + + /* Get ARR register */ + + uint32_t (*arr_get)(struct pwm_lowerhalf_s *dev); + + /* Enable outputs */ + + int (*outputs_enable)(struct pwm_lowerhalf_s *dev, uint16_t outputs, + bool state); + + /* Software update */ + + int (*soft_update)(struct pwm_lowerhalf_s *dev); + + /* PWM configure */ + + int (*configure)(struct pwm_lowerhalf_s *dev); + + /* Software break */ + + int (*soft_break)(struct pwm_lowerhalf_s *dev, bool state); + + /* Update frequency */ + + int (*freq_update)(struct pwm_lowerhalf_s *dev, uint32_t frequency); + + /* Enable timer counter */ + + int (*tim_enable)(struct pwm_lowerhalf_s *dev, bool state); + +#ifdef CONFIG_DEBUG_PWM_INFO + /* Dump timer registers */ + + void (*dump_regs)(struct pwm_lowerhalf_s *dev, const char *msg); +#endif + +#ifdef HAVE_PWM_COMPLEMENTARY + /* Deadtime update */ + + int (*dt_update)(struct pwm_lowerhalf_s *dev, uint8_t dt); +#endif +}; + +#endif /* CONFIG_STM32H5_PWM_LL_OPS */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_pwminitialize + * + * Description: + * Initialize one timer for use with the upper_level PWM driver. + * + * Input Parameters: + * timer - A number identifying the timer use. The number of valid timer + * IDs varies with the STM32 MCU and MCU family but is somewhere in + * the range of {1,..,17}. + * + * Returned Value: + * On success, a pointer to the STM32 lower half PWM driver is returned. + * NULL is returned on any failure. + * + ****************************************************************************/ + +struct pwm_lowerhalf_s *stm32_pwminitialize(int timer); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* CONFIG_STM32H5_PWM */ +#endif /* __ARCH_ARM_SRC_STM32H5_STM32_PWM_H */ diff --git a/boards/arm/stm32h5/nucleo-h563zi/configs/pwm/defconfig b/boards/arm/stm32h5/nucleo-h563zi/configs/pwm/defconfig new file mode 100644 index 00000000000..628a5f8735f --- /dev/null +++ b/boards/arm/stm32h5/nucleo-h563zi/configs/pwm/defconfig @@ -0,0 +1,56 @@ +# +# 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_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=2048 +CONFIG_ARCH_STACKDUMP=y +CONFIG_ARMV8M_STACKCHECK=y +CONFIG_BOARD_LOOPSPERMSEC=9251 +CONFIG_BUILTIN=y +CONFIG_DEBUG_ASSERTIONS=y +CONFIG_DEBUG_FEATURES=y +CONFIG_DEBUG_SYMBOLS=y +CONFIG_EXAMPLES_PWM=y +CONFIG_EXAMPLES_PWM_DURATION=60 +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_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_PWM=y +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_TIM1=y +CONFIG_STM32H5_TIM1_CH1OUT=y +CONFIG_STM32H5_TIM1_PWM=y +CONFIG_STM32H5_USART3=y +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=0 +CONFIG_TIMER=y +CONFIG_USART3_SERIAL_CONSOLE=y diff --git a/boards/arm/stm32h5/nucleo-h563zi/include/board.h b/boards/arm/stm32h5/nucleo-h563zi/include/board.h index f893497e7e6..7ff368632c7 100644 --- a/boards/arm/stm32h5/nucleo-h563zi/include/board.h +++ b/boards/arm/stm32h5/nucleo-h563zi/include/board.h @@ -322,6 +322,10 @@ #define GPIO_ADC1_INP3 (GPIO_ADC1_INP3_0) #define GPIO_ADC1_INP10 (GPIO_ADC1_INP10_0) +/* Timers / PWM */ + +#define GPIO_TIM1_CH1OUT GPIO_TIM1_CH1OUT_2 /* PE9 */ + /* USART3 GPIOs *************************************************************/ /* USART3 (Nucleo Virtual Console): Default board solder bridge configuration diff --git a/boards/arm/stm32h5/nucleo-h563zi/src/Makefile b/boards/arm/stm32h5/nucleo-h563zi/src/Makefile index 48c1c412347..d0c78ae59e6 100644 --- a/boards/arm/stm32h5/nucleo-h563zi/src/Makefile +++ b/boards/arm/stm32h5/nucleo-h563zi/src/Makefile @@ -51,4 +51,8 @@ ifeq ($(CONFIG_STM32H5_FDCAN),y) CSRCS += stm32_can.c endif +ifeq ($(CONFIG_STM32H5_PWM),y) +CSRCS += stm32_pwm.c +endif + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm/stm32h5/nucleo-h563zi/src/nucleo-h563zi.h b/boards/arm/stm32h5/nucleo-h563zi/src/nucleo-h563zi.h index b39e62772c8..d1c7ba9828f 100644 --- a/boards/arm/stm32h5/nucleo-h563zi/src/nucleo-h563zi.h +++ b/boards/arm/stm32h5/nucleo-h563zi/src/nucleo-h563zi.h @@ -145,5 +145,17 @@ int stm32_dts_setup(int devno); int stm32_can_setup(uint8_t port); #endif +/**************************************************************************** + * Name: stm32_pwm_setup + * + * Description: + * Initialize PWM and register the PWM device. + * + ****************************************************************************/ + +#ifdef CONFIG_PWM +int stm32_pwm_setup(void); +#endif + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM_STM32H5_NUCLEO_H563ZI_SRC_NUCLEO_H563ZI_H */ diff --git a/boards/arm/stm32h5/nucleo-h563zi/src/stm32_bringup.c b/boards/arm/stm32h5/nucleo-h563zi/src/stm32_bringup.c index bfe9782d2a3..07a4ac7a1ac 100644 --- a/boards/arm/stm32h5/nucleo-h563zi/src/stm32_bringup.c +++ b/boards/arm/stm32h5/nucleo-h563zi/src/stm32_bringup.c @@ -139,6 +139,16 @@ int stm32_bringup(void) # endif #endif +#ifdef CONFIG_PWM + /* Initialize PWM and register the PWM device. */ + + ret = stm32_pwm_setup(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: stm32_pwm_setup() failed: %d\n", ret); + } +#endif + UNUSED(ret); return OK; } diff --git a/boards/arm/stm32h5/nucleo-h563zi/src/stm32_pwm.c b/boards/arm/stm32h5/nucleo-h563zi/src/stm32_pwm.c new file mode 100644 index 00000000000..1060d15a03e --- /dev/null +++ b/boards/arm/stm32h5/nucleo-h563zi/src/stm32_pwm.c @@ -0,0 +1,110 @@ +/**************************************************************************** + * boards/arm/stm32h5/nucleo-h563zi/src/stm32_pwm.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 "chip.h" +#include "arm_internal.h" +#include "stm32_pwm.h" +#include "nucleo-h563zi.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +#define HAVE_PWM 1 + +#ifndef CONFIG_PWM +# undef HAVE_PWM +#endif + +#ifndef CONFIG_STM32H5_TIM1 +# undef HAVE_PWM +#endif + +#ifndef CONFIG_STM32H5_TIM1_PWM +# undef HAVE_PWM +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_pwm_setup + * + * Description: + * Initialize PWM and register the PWM device. + * + ****************************************************************************/ + +int stm32_pwm_setup(void) +{ +#ifdef HAVE_PWM + static bool initialized = false; + struct pwm_lowerhalf_s *pwm; + int ret; + + /* Have we already initialized? */ + + if (!initialized) + { + /* Get an instance of the PWM interface */ + + pwm = stm32_pwminitialize(1); + if (!pwm) + { + tmrerr("ERROR: Failed to get the STM32 PWM lower half\n"); + return -ENODEV; + } + + /* Register the PWM driver at "/dev/pwm0" */ + + ret = pwm_register("/dev/pwm0", pwm); + if (ret < 0) + { + tmrerr("ERROR: pwm_register failed: %d\n", ret); + return ret; + } + + /* Now we are initialized */ + + initialized = true; + } + + return OK; +#else + return -ENODEV; +#endif +}