Files
rtems/bsps/arm/beagle/pwm/pwm.c
Joel Sherrill be6e30dff3 bsps/arm: Move SPDX line to top of file
The RTEMS Software Engineering Guide specifies that the SPDX license
annotation shouldbe the first line of the file and not part of the
copyright/license comment block.
2025-08-11 19:06:32 +00:00

418 lines
13 KiB
C

/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup arm_beagle
*
* @brief Support for PWM for the BeagleBone Black.
*/
/*
* Copyright (c) 2016 Punit Vara <punitvara@gmail.com>
*
* 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.
*/
/** This file is based on
* https://github.com/VegetableAvenger/BBBIOlib/blob/master/BBBio_lib/BBBiolib_PWMSS.c
*/
#include <libcpu/am335x.h>
#include <stdio.h>
#include <bsp/gpio.h>
#include <bsp/bbb-gpio.h>
#include <bsp.h>
#include <bsp/pwmss.h>
#include <bsp/bbb-pwm.h>
#include <bsp/beagleboneblack.h>
/* Currently these definitions are for BeagleBone Black board only
* Later on Beagle-xM board support can be added in this code.
* After support gets added if condition should be removed
*/
#if IS_AM335X
/*
* @brief This function selects EPWM module to be enabled
*
* @param pwm_id It is the instance number of EPWM of pwm sub system.
*
* @return Base Address of respective pwm instant.
*/
static uint32_t select_pwm(BBB_PWMSS pwm_id)
{
uint32_t baseAddr=0;
if (pwm_id == BBB_PWMSS0) {
baseAddr = AM335X_EPWM_0_REGS;
} else if (pwm_id == BBB_PWMSS1) {
baseAddr = AM335X_EPWM_1_REGS;
} else if (pwm_id == BBB_PWMSS2) {
baseAddr = AM335X_EPWM_2_REGS;
} else {
baseAddr = 0;
}
return baseAddr;
}
/*
* @brief This function selects PWM Sub system to be enabled
*
* @param pwmss_id The instance number of ePWMSS whose system clocks
* have to be configured.
*
* @return Base Address of respective pwmss instant.
*/
static uint32_t select_pwmss(BBB_PWMSS pwmss_id)
{
uint32_t baseAddr=0;
if (pwmss_id == BBB_PWMSS0) {
baseAddr = AM335X_PWMSS0_MMAP_ADDR;
} else if (pwmss_id == BBB_PWMSS1) {
baseAddr = AM335X_PWMSS1_MMAP_ADDR;
} else if (pwmss_id == BBB_PWMSS2) {
baseAddr = AM335X_PWMSS2_MMAP_ADDR;
} else {
baseAddr = 0;
}
return baseAddr;
}
bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no, BBB_PWMSS pwm_id)
{
bool is_valid = true;
if(pwm_id == BBB_PWMSS0) {
if (pin_no == BBB_P9_21_0B) {
REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D0) = BBB_MUXMODE(BBB_P9_21_MUX_PWM);
} else if (pin_no == BBB_P9_22_0A) {
REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_SCLK) = BBB_MUXMODE(BBB_P9_22_MUX_PWM);
} else if (pin_no == BBB_P9_29_0B) {
REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_FSX) = BBB_MUXMODE(BBB_P9_29_MUX_PWM);
} else if (pin_no == BBB_P9_31_0A) {
REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_ACLKX) = BBB_MUXMODE(BBB_P9_31_MUX_PWM);
} else {
is_valid = false;
}
} else if (pwm_id == BBB_PWMSS1) {
if (pin_no == BBB_P8_34_1B) {
REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(11)) = BBB_MUXMODE(BBB_P8_34_MUX_PWM);
} else if (pin_no == BBB_P8_36_1A) {
REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(10)) = BBB_MUXMODE(BBB_P8_36_MUX_PWM);
} else if (pin_no == BBB_P9_14_1A) {
REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A2) = BBB_MUXMODE(BBB_P9_14_MUX_PWM);
} else if (pin_no == BBB_P9_16_1B) {
REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A3) = BBB_MUXMODE(BBB_P9_16_MUX_PWM);
} else {
is_valid = false;
}
} else if (pwm_id == BBB_PWMSS2) {
if (pin_no == BBB_P8_13_2B) {
REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(9)) = BBB_MUXMODE(BBB_P8_13_MUX_PWM);
} else if (pin_no == BBB_P8_19_2A) {
REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(8)) = BBB_MUXMODE(BBB_P8_19_MUX_PWM);
} else if (pin_no == BBB_P8_45_2A) {
REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(0)) = BBB_MUXMODE(BBB_P8_45_MUX_PWM);
} else if (pin_no == BBB_P8_46_2B) {
REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(1)) = BBB_MUXMODE(BBB_P8_46_MUX_PWM);
} else {
is_valid = false;
}
} else {
is_valid = false;
}
return is_valid;
}
/**
* @brief This function Enables TBCLK(Time Base Clock) for specific
* EPWM instance of pwmsubsystem.
*
* @param instance It is the instance number of EPWM of pwmsubsystem.
*
* @return true if successful
* false if unsuccessful
**/
static bool pwmss_tbclk_enable(BBB_PWMSS instance)
{
uint32_t enable_bit;
bool is_valid = true;
if (instance == BBB_PWMSS0) {
enable_bit = AM335X_PWMSS_CTRL_PWMSS0_TBCLKEN;
} else if (instance == BBB_PWMSS1) {
enable_bit = AM335X_PWMSS_CTRL_PWMSS1_TBCLKEN;
} else if (instance == BBB_PWMSS2) {
enable_bit = AM335X_PWMSS_CTRL_PWMSS2_TBCLKEN;
} else {
is_valid = false;
}
if (is_valid)
{
REG(AM335X_PADCONF_BASE + AM335X_PWMSS_CTRL) |= enable_bit;
}
return is_valid;
}
/**
* @brief This functions enables clock for EHRPWM module in PWMSS subsystem.
*
* @param pwm_id It is the instance number of EPWM of pwm sub system.
*
* @return true if successful
* false if unsuccessful
*
**/
static bool pwm_clock_enable(BBB_PWMSS pwm_id)
{
const bool id_is_valid = pwm_id < BBB_PWMSS_COUNT;
bool status = true;
if (id_is_valid) {
const uint32_t baseAddr = select_pwmss(pwm_id);
REG(baseAddr + AM335X_PWMSS_CLKCONFIG) |= AM335X_PWMSS_CLK_EN_ACK;
} else {
status = false;
}
return status;
}
bool beagle_pwm_init(BBB_PWMSS pwmss_id)
{
const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT;
bool status = true;
if(id_is_valid) {
pwmss_module_clk_config(pwmss_id);
pwm_clock_enable(pwmss_id);
pwmss_tbclk_enable(pwmss_id);
} else {
status =false;
}
return status;
}
int beagle_pwm_configure(BBB_PWMSS pwm_id, float pwm_freq, float duty_a, float duty_b)
{
uint32_t baseAddr;
int status = 1;
float cycle = 0.0f,divisor = 0;
unsigned int i,j;
const float CLKDIV_div[] = {1.0,2.0,4.0,8.0,16.0,32.0,64.0,128.0};
const float HSPCLKDIV_div[] = {1.0, 2.0, 4.0, 6.0, 8.0, 10.0,12.0, 14.0};
int NearCLKDIV =7,NearHSPCLKDIV =7,NearTBPRD =0;
if (pwm_freq <= BBB_PWM_FREQ_THRESHOLD) {
status =0;
}
if (duty_a < 0.0f || duty_a > 100.0f || duty_b < 0.0f || duty_b > 100.0f) {
status = 0;
}
duty_a /= 100.0f;
duty_b /= 100.0f;
/** 10^9 /Hz compute time per cycle (ns) */
cycle = 1000000000.0f / pwm_freq;
/** am335x provide (128* 14) divider and per TBPRD means 10ns when divider
* and max TBPRD is 65535 so max cycle is 128 * 8 * 14 * 65535 * 10ns */
divisor = (cycle / 655350.0f);
if (divisor > (128 * 14)) {
return 0;
}
else {
for (i=0;i<8;i++) {
for(j=0 ; j<8; j++) {
if((CLKDIV_div[i] * HSPCLKDIV_div[j]) < (CLKDIV_div[NearCLKDIV]
* HSPCLKDIV_div[NearHSPCLKDIV]) && (CLKDIV_div[i] * HSPCLKDIV_div[j] > divisor)) {
NearCLKDIV = i;
NearHSPCLKDIV = j;
}
}
}
baseAddr = select_pwm(pwm_id);
REG16(baseAddr + AM335X_EPWM_TBCTL) &= ~(AM335X_TBCTL_CLKDIV_MASK | AM335X_TBCTL_HSPCLKDIV_MASK);
const uint16_t clkdiv_clear = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
(~AM335X_EPWM_TBCTL_CLKDIV));
const uint16_t clkdiv_write = ((NearCLKDIV
<< AM335X_EPWM_TBCTL_CLKDIV_SHIFT) & AM335X_EPWM_TBCTL_CLKDIV);
REG16(baseAddr + AM335X_EPWM_TBCTL) = clkdiv_clear | clkdiv_write;
const uint16_t hspclkdiv_clear = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
(~AM335X_EPWM_TBCTL_HSPCLKDIV));
const uint16_t hspclkdiv_write = ((NearHSPCLKDIV <<
AM335X_EPWM_TBCTL_HSPCLKDIV_SHIFT) & AM335X_EPWM_TBCTL_HSPCLKDIV);
REG16(baseAddr + AM335X_EPWM_TBCTL) = hspclkdiv_clear | hspclkdiv_write;
NearTBPRD = (cycle / (10.0 * CLKDIV_div[NearCLKDIV] * HSPCLKDIV_div[NearHSPCLKDIV]));
const uint16_t shadow_mask = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
(~AM335X_EPWM_PRD_LOAD_SHADOW_MASK));
const uint16_t shadow_disable = (((bool)AM335X_EPWM_SHADOW_WRITE_DISABLE <<
AM335X_EPWM_TBCTL_PRDLD_SHIFT) & AM335X_EPWM_PRD_LOAD_SHADOW_MASK);
REG16(baseAddr + AM335X_EPWM_TBCTL) = shadow_mask | shadow_disable;
const uint16_t counter_mask = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
(~AM335X_EPWM_COUNTER_MODE_MASK));
const uint16_t counter_shift = (((unsigned int)AM335X_EPWM_COUNT_UP <<
AM335X_TBCTL_CTRMODE_SHIFT) & AM335X_EPWM_COUNTER_MODE_MASK);
REG16(baseAddr + AM335X_EPWM_TBCTL) = counter_mask | counter_shift;
/*setting clock divider and freeze time base*/
REG16(baseAddr + AM335X_EPWM_CMPB) = (unsigned short)((float)(NearTBPRD) * duty_b);
REG16(baseAddr + AM335X_EPWM_CMPA) = (unsigned short)((float)(NearTBPRD) * duty_a);
REG16(baseAddr + AM335X_EPWM_TBPRD) = (unsigned short)NearTBPRD;
REG16(baseAddr + AM335X_EPWM_TBCNT) = 0;
}
return status;
}
bool beagle_pwm_enable(BBB_PWMSS pwmid)
{
const bool id_is_valid = pwmid < BBB_PWMSS_COUNT;
bool status = true;
if (id_is_valid) {
const uint32_t baseAddr = select_pwm(pwmid);
/* Initially set EPWMxA o/p high , when increasing counter = CMPA toggle o/p of EPWMxA */
REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XAHIGH | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT);
/* Initially set EPWMxB o/p high , when increasing counter = CMPA toggle o/p of EPWMxB */
REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLB_ZRO_XBHIGH | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT);
REG16(baseAddr + AM335X_EPWM_TBCNT) = 0;
/* Set counter mode : Up-count mode */
REG16(baseAddr + AM335X_EPWM_TBCTL) |= AM335X_TBCTL_FREERUN | AM335X_TBCTL_CTRMODE_UP;
} else {
status =false;
}
return status;
}
bool beagle_pwm_disable(BBB_PWMSS pwmid)
{
const bool id_is_valid = pwmid < BBB_PWMSS_COUNT;
bool status = true;
if (id_is_valid) {
const uint32_t baseAddr = select_pwm(pwmid);
REG16(baseAddr + AM335X_EPWM_TBCTL) = AM335X_EPWM_TBCTL_CTRMODE_STOPFREEZE;
REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XALOW | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT);
REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLA_ZRO_XBLOW | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT);
REG16(baseAddr + AM335X_EPWM_TBCNT) = 0;
} else {
status = false;
}
return status;
}
/**
* @brief This functions determines whether time base clock is enabled for EPWMSS
*
* @param pwmss_id The instance number of ePWMSS whose time base clock need to
* be checked
*
* @return returns 4 for PWMSS_ID = 2
* returns 2 for PWMSS_ID = 1
* returns 1 for PWMSS_ID = 0
**/
static int pwmss_tb_clock_check(unsigned int pwmss_id)
{
unsigned int reg_value;
/*control module check*/
reg_value = REG(AM335X_CONTROL_MODULE + AM335X_PWMSS_CTRL);
return (reg_value & (1 << pwmss_id));
}
/**
* @brief This functions determines whether clock for EPWMSS is enabled or not.
*
* @param It is the Memory address of the PWMSS instance used.
*
* @return
*
**/
static unsigned int pwmss_clock_en_status(unsigned int pwmid)
{
unsigned int status;
const uint32_t baseAddr = select_pwmss(pwmid);
status = REG(baseAddr + AM335X_PWMSS_CLKSTATUS);
status = status >> 8 & 0x1;
return status;
}
bool beagle_pwmss_is_running(unsigned int pwmss_id)
{
const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT;
bool status=true;
if (id_is_valid) {
status = pwmss_clock_en_status(pwmss_id);
if(status){
status = pwmss_tb_clock_check(pwmss_id);
} else {
status = false;
}
} else {
status = false;
}
return status;
}
#endif
/* For support of BeagleboardxM */
#if IS_DM3730
/* Currently this section is just to satisfy
* GPIO API and to make the build successful.
* Later on support can be added here.
*/
bool beagle_pwm_init(BBB_PWMSS pwmss_id)
{
return false;
}
bool beagle_pwm_disable(BBB_PWMSS pwmid)
{
return false;
}
bool beagle_pwm_enable(BBB_PWMSS pwmid)
{
return false;
}
int beagle_pwm_configure(BBB_PWMSS pwm_id, float pwm_freq, float duty_a, float duty_b)
{
return -1;
}
bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no, BBB_PWMSS pwm_id)
{
return false;
}
bool beagle_pwmss_is_running(unsigned int pwmss_id)
{
return false;
}
#endif