bsp/aarch64/raspberrypi4: Add PWM peripheral support

This commit adds support for the PWM peripheral on the aarch64/raspberrypi BSP.
This commit is contained in:
Shaunak Datar
2025-05-23 16:04:06 +05:30
parent 0ab6c7bc58
commit d4755476bc
5 changed files with 364 additions and 0 deletions

View File

@@ -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 */

View File

@@ -11,6 +11,7 @@
/* /*
* Copyright (c) 2022 Mohd Noor Aman * Copyright (c) 2022 Mohd Noor Aman
* Copyright (c) 2024 Ning Yang * Copyright (c) 2024 Ning Yang
* Copyright (c) 2025 Shaunak Datar
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * 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 * @name Mailbox Registers
* *

View File

@@ -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;
}

View File

@@ -65,6 +65,8 @@ links:
uid: objspi uid: objspi
- role: build-dependency - role: build-dependency
uid: objwatchdog uid: objwatchdog
- role: build-dependency
uid: objpwm
source: source:
- bsps/aarch64/raspberrypi/fdt/bsp_fdt.c - bsps/aarch64/raspberrypi/fdt/bsp_fdt.c
- bsps/aarch64/raspberrypi/fdt/rpi4b_dtb.c - bsps/aarch64/raspberrypi/fdt/rpi4b_dtb.c

View File

@@ -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