diff --git a/bsps/aarch64/raspberrypi/include/bsp/raspberrypi-pwm.h b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi-pwm.h new file mode 100644 index 0000000000..fa804a8538 --- /dev/null +++ b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi-pwm.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64Raspberrypi4 + * + * @brief Raspberry Pi specific PWM definitions. + */ + +/* + * Copyright (C) 2025 Shaunak Datar + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LIBBSP_AARCH64_RASPBERRYPI_4_PWM_H +#define LIBBSP_AARCH64_RASPBERRYPI_4_PWM_H + +#include "bsp/raspberrypi.h" +#include "bsp/rpi-gpio.h" +#include "bsp/utility.h" + +typedef enum { raspberrypi_pwm0, raspberrypi_pwm1 } raspberrypi_pwm_channel; + +typedef enum { + raspberrypi_pwm_master0, + raspberrypi_pwm_master1 +} raspberrypi_pwm_master; + +/** + * @name PWM_CONTROL register bits + * @{ + */ +#define C_MSEN2 BSP_BIT32( 15 ) /**< Channel 2 Mark-Space enable */ +#define C_USEF2 BSP_BIT32( 13 ) /**< Channel 2 use FIFO */ +#define C_POLA2 BSP_BIT32( 12 ) /**< Channel 2 invert polarity */ +#define C_SBIT2 BSP_BIT32( 11 ) /**< Channel 2 silence bit high */ +#define C_RPTL2 BSP_BIT32( 10 ) /**< Channel 2 repeat on underrun */ +#define C_MODE2 BSP_BIT32( 9 ) /**< Channel 2 serializer mode */ +#define C_PWEN2 BSP_BIT32( 8 ) /**< Channel 2 enable output */ +#define C_MSEN1 BSP_BIT32( 7 ) /**< Channel 1 Mark-Space enable */ +#define C_CLRF BSP_BIT32( 6 ) /**< Clear FIFO */ +#define C_USEF1 BSP_BIT32( 5 ) /**< Channel 1 use FIFO */ +#define C_POLA1 BSP_BIT32( 4 ) /**< Channel 1 invert polarity */ +#define C_SBIT1 BSP_BIT32( 3 ) /**< Channel 1 silence bit high */ +#define C_RPTL1 BSP_BIT32( 2 ) /**< Channel 1 repeat underrun */ +#define C_MODE1 BSP_BIT32( 1 ) /**< Channel 1 serializer mode */ +#define C_PWEN1 BSP_BIT32( 0 ) /**< Channel 1 enable output */ +/** @} */ + +/** + * @brief Set PWM clock divider. + * @param divisor 1 – 4095; PWMCLK = 19.2 MHz / @p divisor. + * @retval RTEMS_SUCCESSFUL OK + * @retval RTEMS_INVALID_NUMBER 0 or >4095 + */ +rtems_status_code rpi_pwm_set_clock( uint32_t divisor ); + +/** + * @brief Update duty-cycle register. + * @param master Selects the hardware instance to be used + * (raspberrypi_pwm_master0 = PWM0, raspberrypi_pwm_master1 = PWM1) + * @param channel Selects the channel for @p master (raspberrypi_pwm0 = PWMx_0, + * raspberrypi_pwm1 = PWMx_1) + * @param data Initial duty count, 1 – current range value.(0 rejected) + */ +rtems_status_code rpi_pwm_set_data( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel, + uint32_t data +); + +/** + * @brief Main PWM initialization function. This functions sets up the PWM + * master, channel, duty cycle and GPIO pin. + * @param master Selects the hardware instance to be used + * (raspberrypi_pwm_master0 = PWM0, raspberrypi_pwm_master1 = PWM1) + * @param channel Selects the channel for @p master (raspberrypi_pwm0 = PWMx_0, + * raspberrypi_pwm1 = PWMx_1) + * @param range Period register value (> 0) + * @param data Initial duty count, 1 – @p range (0 rejected) + */ +rtems_status_code rpi_pwm_init( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel, + uint32_t range, + uint32_t data +); + +#endif /* LIBBSP_AARCH64_RASPBERRYPI_4_PWM_H */ \ No newline at end of file diff --git a/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h index cc3eea7129..63eaf5c6f1 100644 --- a/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h +++ b/bsps/aarch64/raspberrypi/include/bsp/raspberrypi.h @@ -11,6 +11,7 @@ /* * Copyright (c) 2022 Mohd Noor Aman * Copyright (c) 2024 Ning Yang + * Copyright (c) 2025 Shaunak Datar * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -284,6 +285,42 @@ /** @} */ +/** + * @name PWM Clock Manager Register and Offsets + * + * @{ + */ + +#define BCM2711_CM_PWM_BASE ( RPI_PERIPHERAL_BASE + 0x00101000 ) +#define BCM2711_CM_PWM_CTL 0xA0 +#define BCM2711_CM_PWM_DIV 0xA4 +#define CM_PWM_PASSWD ( 0x5A << 24 ) +#define CM_PWM_CTL_SRC_OSC BSP_BIT32( 0 ) +#define CM_PWM_CTL_BUSY BSP_BIT32( 7 ) +#define CM_PWM_CTL_ENAB BSP_BIT32( 4 ) +#define CM_PWM_DIV_MASK 0xFFF + +/** @} */ + +/** + * @name PWM Registers and offsets + * + * @{ + */ + +#define BCM2711_PWM0_BASE ( RPI_PERIPHERAL_BASE + 0x0020C000 ) +#define BCM2711_PWM1_BASE ( RPI_PERIPHERAL_BASE + 0x0020C800 ) +#define BCM2711_PWM_CONTROL 0x00 +#define BCM2711_PWM_STATUS 0x04 +#define BCM2711_PWM_DMAC 0x08 +#define BCM2711_PWM_RNG1 0x10 +#define BCM2711_PWM_DAT1 0x14 +#define BCM2711_PWM_FIFO 0x18 +#define BCM2711_PWM_RNG2 0x20 +#define BCM2711_PWM_DAT2 0x24 + +/** @} */ + /** * @name Mailbox Registers * diff --git a/bsps/aarch64/raspberrypi/pwm/raspberrypi-pwm.c b/bsps/aarch64/raspberrypi/pwm/raspberrypi-pwm.c new file mode 100644 index 0000000000..37efb0afbb --- /dev/null +++ b/bsps/aarch64/raspberrypi/pwm/raspberrypi-pwm.c @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/** + * @file + * + * @ingroup RTEMSBSPsAArch64Raspberrypi4 + * + * @brief PWM Support + */ + +/* + * Copyright (C) 2025 Shaunak Datar + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "bsp/raspberrypi-pwm.h" + +// Clock manager macros +#define BCM2711_CM_PWM( x ) BCM2835_REG( BCM2711_CM_PWM_BASE + ( x ) ) +#define CM_PWM_CTL_ENABLE_OSC \ + ( CM_PWM_PASSWD | CM_PWM_CTL_ENAB | CM_PWM_CTL_SRC_OSC ) +#define CM_PWM_CTL_DISABLE ( CM_PWM_PASSWD | CM_PWM_CTL_SRC_OSC ) + +static inline bool rpi_pwm_validate( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel +) +{ + return ( ( master == raspberrypi_pwm_master0 ) || + ( master == raspberrypi_pwm_master1 ) ) && + ( ( channel == raspberrypi_pwm0 ) || ( channel == raspberrypi_pwm1 ) ); +} + +rtems_status_code rpi_pwm_set_clock( uint32_t divisor ) +{ + if ( !( divisor > 0 && divisor < 4096 ) ) { + return RTEMS_INVALID_NUMBER; + } + + /* Stop Clock */ + BCM2711_CM_PWM( BCM2711_CM_PWM_CTL ) = CM_PWM_CTL_DISABLE; + while ( BCM2711_CM_PWM( BCM2711_CM_PWM_CTL ) & CM_PWM_CTL_BUSY ); + + /* Set divisor */ + divisor &= CM_PWM_DIV_MASK; + BCM2711_CM_PWM( BCM2711_CM_PWM_DIV ) = CM_PWM_PASSWD | ( divisor << 12 ); + + /* Select src = osc(1) and enable */ + BCM2711_CM_PWM( BCM2711_CM_PWM_CTL ) = CM_PWM_CTL_ENABLE_OSC; + while ( !( BCM2711_CM_PWM( BCM2711_CM_PWM_CTL ) & CM_PWM_CTL_BUSY ) ); + + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code rpi_pwm_set_control( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel +) +{ + uint32_t pwm_base = ( master == raspberrypi_pwm_master0 ) ? + BCM2711_PWM0_BASE : + BCM2711_PWM1_BASE; + uint32_t control_reg = pwm_base + BCM2711_PWM_CONTROL; + uint32_t control = BCM2835_REG( control_reg ); + + if ( channel == raspberrypi_pwm0 ) { + control &= ~( C_MODE1 | C_POLA1 | C_SBIT1 | C_RPTL1 | C_USEF1 ); + control |= ( C_PWEN1 | C_CLRF | C_MSEN1 ); + } else { + control &= ~( C_MODE2 | C_POLA2 | C_SBIT2 | C_RPTL2 | C_USEF2 ); + control |= ( C_PWEN2 | C_CLRF | C_MSEN2 ); + } + BCM2835_REG( control_reg ) = control; + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code rpi_pwm_set_range( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel, + uint32_t range +) +{ + uint32_t pwm_base = ( master == raspberrypi_pwm_master0 ) ? + BCM2711_PWM0_BASE : + BCM2711_PWM1_BASE; + uint32_t range_offset = ( channel == raspberrypi_pwm0 ) ? BCM2711_PWM_RNG1 : + BCM2711_PWM_RNG2; + + BCM2835_REG( pwm_base + range_offset ) = range; + return RTEMS_SUCCESSFUL; +} + +rtems_status_code rpi_pwm_set_data( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel, + uint32_t data +) +{ + if ( !( rpi_pwm_validate( master, channel ) ) || data == 0 ) { + return RTEMS_INVALID_NUMBER; + } + + uint32_t pwm_base = ( master == raspberrypi_pwm_master0 ) ? + BCM2711_PWM0_BASE : + BCM2711_PWM1_BASE; + uint32_t range_offset = ( channel == raspberrypi_pwm0 ) ? BCM2711_PWM_RNG1 : + BCM2711_PWM_RNG2; + + if ( data > BCM2835_REG( pwm_base + range_offset ) ) { + return RTEMS_INVALID_NUMBER; + } + + uint32_t data_offset = ( channel == raspberrypi_pwm0 ) ? BCM2711_PWM_DAT1 : + BCM2711_PWM_DAT2; + + BCM2835_REG( pwm_base + data_offset ) = data; + return RTEMS_SUCCESSFUL; +} + +static rtems_status_code rpi_pwm_set_gpio( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel +) +{ + rtems_status_code sc; + + if ( master == raspberrypi_pwm_master0 ) { + if ( channel == raspberrypi_pwm0 ) { + sc = raspberrypi_gpio_set_function( 18, GPIO_AF5 ); + } else { + sc = raspberrypi_gpio_set_function( 19, GPIO_AF5 ); + } + } else { + if ( channel == raspberrypi_pwm0 ) { + sc = raspberrypi_gpio_set_function( 40, GPIO_AF0 ); + } else { + sc = raspberrypi_gpio_set_function( 41, GPIO_AF0 ); + } + } + + return sc; +} + +rtems_status_code rpi_pwm_init( + raspberrypi_pwm_master master, + raspberrypi_pwm_channel channel, + uint32_t range, + uint32_t data +) +{ + rtems_status_code sc; + + if ( !( rpi_pwm_validate( master, channel ) ) || range == 0 ) { + return RTEMS_INVALID_NUMBER; + } + + sc = rpi_pwm_set_gpio( master, channel ); + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + sc = rpi_pwm_set_range( master, channel, range ); + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + sc = rpi_pwm_set_data( master, channel, data ); + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + sc = rpi_pwm_set_control( master, channel ); + if ( sc != RTEMS_SUCCESSFUL ) { + return sc; + } + + return RTEMS_SUCCESSFUL; +} \ No newline at end of file diff --git a/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml b/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml index 1614a7d55e..011ee11ba0 100644 --- a/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml +++ b/spec/build/bsps/aarch64/raspberrypi/bspraspberrypi4.yml @@ -65,6 +65,8 @@ links: uid: objspi - role: build-dependency uid: objwatchdog +- role: build-dependency + uid: objpwm source: - bsps/aarch64/raspberrypi/fdt/bsp_fdt.c - bsps/aarch64/raspberrypi/fdt/rpi4b_dtb.c diff --git a/spec/build/bsps/aarch64/raspberrypi/objpwm.yml b/spec/build/bsps/aarch64/raspberrypi/objpwm.yml new file mode 100644 index 0000000000..761f6e86c2 --- /dev/null +++ b/spec/build/bsps/aarch64/raspberrypi/objpwm.yml @@ -0,0 +1,17 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +build-type: objects +cflags: [] +copyrights: +- Copyright (C) 2025 Shaunak Datar +cppflags: [] +cxxflags: [] +enabled-by: true +includes: [] +install: +- destination: ${BSP_INCLUDEDIR}/bsp + source: + - bsps/aarch64/raspberrypi/include/bsp/raspberrypi-pwm.h +links: [] +source: +- bsps/aarch64/raspberrypi/pwm/raspberrypi-pwm.c +type: build