Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:48

0001 /**
0002  * @file
0003  *
0004  * @ingroup arm_beagle
0005  *
0006  * @brief Support for PWM for the BeagleBone Black.
0007  */
0008 
0009 /*
0010  * SPDX-License-Identifier: BSD-2-Clause
0011  *
0012  * Copyright (c) 2016 Punit Vara <punitvara@gmail.com>
0013  *
0014  * Redistribution and use in source and binary forms, with or without
0015  * modification, are permitted provided that the following conditions
0016  * are met:
0017  * 1. Redistributions of source code must retain the above copyright
0018  *    notice, this list of conditions and the following disclaimer.
0019  * 2. Redistributions in binary form must reproduce the above copyright
0020  *    notice, this list of conditions and the following disclaimer in the
0021  *    documentation and/or other materials provided with the distribution.
0022  *
0023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0033  * POSSIBILITY OF SUCH DAMAGE.
0034  */
0035 
0036 /** This file is based on 
0037   * https://github.com/VegetableAvenger/BBBIOlib/blob/master/BBBio_lib/BBBiolib_PWMSS.c
0038   */
0039 
0040 #include <libcpu/am335x.h>
0041 #include <stdio.h>
0042 #include <bsp/gpio.h>
0043 #include <bsp/bbb-gpio.h>
0044 #include <bsp.h>
0045 #include <bsp/pwmss.h>
0046 #include <bsp/bbb-pwm.h>
0047 #include <bsp/beagleboneblack.h>
0048 
0049 /* Currently these definitions are for BeagleBone Black board only
0050  * Later on Beagle-xM board support can be added in this code.
0051  * After support gets added if condition should be removed
0052  */
0053 #if IS_AM335X
0054 
0055 /*
0056  * @brief This function selects EPWM module to be enabled
0057  * 
0058  * @param pwm_id It is the instance number of EPWM of pwm sub system.
0059  * 
0060  * @return Base Address of respective pwm instant.
0061  */
0062 static uint32_t select_pwm(BBB_PWMSS pwm_id)
0063 {
0064   uint32_t baseAddr=0;
0065   
0066   if (pwm_id == BBB_PWMSS0) {
0067     baseAddr = AM335X_EPWM_0_REGS;
0068   } else if (pwm_id == BBB_PWMSS1) {
0069     baseAddr = AM335X_EPWM_1_REGS;
0070   } else if (pwm_id == BBB_PWMSS2) {
0071     baseAddr = AM335X_EPWM_2_REGS;
0072   } else {
0073     baseAddr = 0;
0074   }
0075   return baseAddr;  
0076 }
0077 
0078 /*
0079  * @brief This function selects PWM Sub system to be enabled
0080  *  
0081  * @param pwmss_id  The instance number of ePWMSS whose system clocks
0082  *                  have to be configured.
0083  * 
0084  * @return Base Address of respective pwmss instant.
0085 */
0086 static uint32_t select_pwmss(BBB_PWMSS pwmss_id)
0087 {
0088   uint32_t baseAddr=0;
0089   
0090   if (pwmss_id == BBB_PWMSS0) {
0091     baseAddr = AM335X_PWMSS0_MMAP_ADDR;
0092   } else if (pwmss_id == BBB_PWMSS1) {
0093     baseAddr = AM335X_PWMSS1_MMAP_ADDR;
0094   } else if (pwmss_id == BBB_PWMSS2) {
0095     baseAddr = AM335X_PWMSS2_MMAP_ADDR;
0096   } else {
0097     baseAddr = 0;
0098   }
0099   return baseAddr;
0100 }
0101 
0102 bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no, BBB_PWMSS pwm_id)
0103 {
0104   bool is_valid = true;
0105   
0106   if(pwm_id == BBB_PWMSS0) {
0107     if (pin_no == BBB_P9_21_0B) {
0108       REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D0) = BBB_MUXMODE(BBB_P9_21_MUX_PWM);
0109     } else if (pin_no == BBB_P9_22_0A) {
0110       REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_SCLK) = BBB_MUXMODE(BBB_P9_22_MUX_PWM);
0111     } else if (pin_no == BBB_P9_29_0B) {
0112       REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_FSX) = BBB_MUXMODE(BBB_P9_29_MUX_PWM);
0113     } else if (pin_no == BBB_P9_31_0A) {
0114       REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_ACLKX) = BBB_MUXMODE(BBB_P9_31_MUX_PWM);
0115     } else { 
0116       is_valid = false;
0117     }
0118     
0119     } else if (pwm_id == BBB_PWMSS1) {
0120         if (pin_no == BBB_P8_34_1B) {
0121           REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(11)) = BBB_MUXMODE(BBB_P8_34_MUX_PWM);
0122     } else if (pin_no == BBB_P8_36_1A) {
0123       REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(10)) = BBB_MUXMODE(BBB_P8_36_MUX_PWM);
0124     } else if (pin_no == BBB_P9_14_1A) {
0125       REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A2) = BBB_MUXMODE(BBB_P9_14_MUX_PWM);
0126     } else if (pin_no == BBB_P9_16_1B) {
0127       REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A3) = BBB_MUXMODE(BBB_P9_16_MUX_PWM);
0128     } else { 
0129       is_valid = false;
0130         }
0131    } else if (pwm_id == BBB_PWMSS2) {
0132     if (pin_no == BBB_P8_13_2B) {
0133       REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(9)) = BBB_MUXMODE(BBB_P8_13_MUX_PWM);
0134     } else if (pin_no == BBB_P8_19_2A) {
0135       REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(8)) = BBB_MUXMODE(BBB_P8_19_MUX_PWM);
0136     } else if (pin_no == BBB_P8_45_2A) {
0137       REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(0)) = BBB_MUXMODE(BBB_P8_45_MUX_PWM);
0138     } else if (pin_no == BBB_P8_46_2B) {
0139       REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(1)) = BBB_MUXMODE(BBB_P8_46_MUX_PWM);
0140     } else {
0141       is_valid = false;
0142         }
0143   } else {
0144     is_valid = false;
0145   }
0146   return is_valid;
0147 }
0148 
0149 /**
0150  * @brief   This function Enables TBCLK(Time Base Clock) for specific
0151  *          EPWM instance of pwmsubsystem.
0152  *
0153  * @param   instance  It is the instance number of EPWM of pwmsubsystem.
0154  *
0155  * @return  true if successful
0156  *          false if unsuccessful
0157  **/
0158 static bool pwmss_tbclk_enable(BBB_PWMSS instance)
0159 {
0160   uint32_t enable_bit;
0161   bool is_valid = true;
0162   
0163   if (instance == BBB_PWMSS0)  {
0164     enable_bit = AM335X_PWMSS_CTRL_PWMSS0_TBCLKEN;
0165   }  else if (instance == BBB_PWMSS1)  {
0166        enable_bit = AM335X_PWMSS_CTRL_PWMSS1_TBCLKEN;
0167   }  else if (instance == BBB_PWMSS2)  {
0168        enable_bit = AM335X_PWMSS_CTRL_PWMSS2_TBCLKEN;
0169   }  else  {
0170        is_valid = false;
0171   }
0172 
0173   if (is_valid)
0174   {
0175     REG(AM335X_PADCONF_BASE + AM335X_PWMSS_CTRL) |= enable_bit;
0176   }
0177 
0178   return is_valid;
0179  }
0180 
0181 /**
0182  * @brief   This functions enables clock for EHRPWM module in PWMSS subsystem.
0183  *
0184  * @param   pwm_id  It is the instance number of EPWM of pwm sub system.
0185  *
0186  * @return  true if successful
0187  *          false if unsuccessful 
0188  *
0189  **/
0190 static bool pwm_clock_enable(BBB_PWMSS pwm_id)
0191 {
0192   const bool id_is_valid = pwm_id < BBB_PWMSS_COUNT;    
0193   bool status = true;
0194   
0195   if (id_is_valid) {
0196     const uint32_t baseAddr = select_pwmss(pwm_id);
0197     REG(baseAddr + AM335X_PWMSS_CLKCONFIG) |= AM335X_PWMSS_CLK_EN_ACK;
0198   }  else  {
0199        status = false;
0200   }
0201   return status;
0202 }
0203 
0204 bool beagle_pwm_init(BBB_PWMSS pwmss_id)
0205 {
0206   const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT;
0207   bool status = true;
0208   
0209   if(id_is_valid) {
0210     pwmss_module_clk_config(pwmss_id);
0211     pwm_clock_enable(pwmss_id); 
0212     pwmss_tbclk_enable(pwmss_id);
0213   } else {
0214       status =false;
0215   }
0216   return status;
0217 }
0218 
0219 int beagle_pwm_configure(BBB_PWMSS pwm_id, float pwm_freq, float duty_a, float duty_b)
0220 {
0221   uint32_t baseAddr;
0222   int status = 1;
0223   float cycle = 0.0f,divisor = 0;
0224   unsigned int i,j;
0225   const float CLKDIV_div[] = {1.0,2.0,4.0,8.0,16.0,32.0,64.0,128.0};
0226   const float HSPCLKDIV_div[] = {1.0, 2.0, 4.0, 6.0, 8.0, 10.0,12.0, 14.0};
0227   int NearCLKDIV =7,NearHSPCLKDIV =7,NearTBPRD =0;
0228  
0229   if (pwm_freq <= BBB_PWM_FREQ_THRESHOLD) {
0230     status =0;
0231   }
0232   
0233   if (duty_a < 0.0f || duty_a > 100.0f || duty_b < 0.0f || duty_b > 100.0f) {
0234     status = 0;
0235   }
0236   duty_a /= 100.0f;
0237   duty_b /= 100.0f;
0238   
0239   /** 10^9 /Hz compute time per cycle (ns) */
0240   cycle = 1000000000.0f / pwm_freq;
0241 
0242   /** am335x provide (128* 14) divider and per TBPRD means 10ns when divider 
0243     * and max TBPRD is 65535 so max cycle is 128 * 8 * 14 * 65535 * 10ns */
0244   divisor = (cycle / 655350.0f);
0245   if (divisor > (128 * 14)) {
0246     return 0;
0247   }
0248   else {
0249     for (i=0;i<8;i++) {
0250       for(j=0 ; j<8; j++) {
0251         if((CLKDIV_div[i] * HSPCLKDIV_div[j]) < (CLKDIV_div[NearCLKDIV]
0252                             * HSPCLKDIV_div[NearHSPCLKDIV]) && (CLKDIV_div[i] * HSPCLKDIV_div[j] > divisor)) {
0253           NearCLKDIV = i;
0254           NearHSPCLKDIV = j;
0255          }
0256       }
0257     }
0258   
0259   baseAddr = select_pwm(pwm_id);
0260   
0261   REG16(baseAddr + AM335X_EPWM_TBCTL) &= ~(AM335X_TBCTL_CLKDIV_MASK | AM335X_TBCTL_HSPCLKDIV_MASK);
0262   const uint16_t clkdiv_clear = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
0263   (~AM335X_EPWM_TBCTL_CLKDIV));
0264   const uint16_t clkdiv_write = ((NearCLKDIV
0265   << AM335X_EPWM_TBCTL_CLKDIV_SHIFT) & AM335X_EPWM_TBCTL_CLKDIV);
0266   REG16(baseAddr + AM335X_EPWM_TBCTL) = clkdiv_clear | clkdiv_write;
0267   const uint16_t hspclkdiv_clear =  (REG16(baseAddr + AM335X_EPWM_TBCTL) &
0268   (~AM335X_EPWM_TBCTL_HSPCLKDIV));
0269   const uint16_t hspclkdiv_write = ((NearHSPCLKDIV <<
0270   AM335X_EPWM_TBCTL_HSPCLKDIV_SHIFT) & AM335X_EPWM_TBCTL_HSPCLKDIV);
0271   REG16(baseAddr + AM335X_EPWM_TBCTL) = hspclkdiv_clear | hspclkdiv_write;
0272   NearTBPRD = (cycle / (10.0 * CLKDIV_div[NearCLKDIV] * HSPCLKDIV_div[NearHSPCLKDIV]));
0273   const uint16_t shadow_mask =  (REG16(baseAddr + AM335X_EPWM_TBCTL) &
0274   (~AM335X_EPWM_PRD_LOAD_SHADOW_MASK));
0275   const uint16_t shadow_disable =  (((bool)AM335X_EPWM_SHADOW_WRITE_DISABLE <<
0276   AM335X_EPWM_TBCTL_PRDLD_SHIFT) & AM335X_EPWM_PRD_LOAD_SHADOW_MASK);
0277   REG16(baseAddr + AM335X_EPWM_TBCTL) = shadow_mask | shadow_disable;
0278   const uint16_t counter_mask = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
0279   (~AM335X_EPWM_COUNTER_MODE_MASK));
0280   const uint16_t counter_shift = (((unsigned int)AM335X_EPWM_COUNT_UP <<
0281   AM335X_TBCTL_CTRMODE_SHIFT) &  AM335X_EPWM_COUNTER_MODE_MASK);
0282   REG16(baseAddr + AM335X_EPWM_TBCTL) = counter_mask | counter_shift;
0283   /*setting clock divider and freeze time base*/
0284   REG16(baseAddr + AM335X_EPWM_CMPB) = (unsigned short)((float)(NearTBPRD) * duty_b);
0285   REG16(baseAddr + AM335X_EPWM_CMPA) = (unsigned short)((float)(NearTBPRD) * duty_a);
0286   REG16(baseAddr + AM335X_EPWM_TBPRD) = (unsigned short)NearTBPRD;
0287   REG16(baseAddr + AM335X_EPWM_TBCNT) = 0;
0288   }
0289   return status;
0290 }
0291 
0292 bool beagle_pwm_enable(BBB_PWMSS pwmid)
0293 {
0294   const bool id_is_valid = pwmid < BBB_PWMSS_COUNT;
0295   bool status = true;
0296   
0297   if (id_is_valid)  {
0298     const uint32_t baseAddr = select_pwm(pwmid);
0299   /* Initially set EPWMxA o/p high , when increasing counter = CMPA toggle o/p of EPWMxA */
0300     REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XAHIGH | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT);
0301   /* Initially set EPWMxB o/p high , when increasing counter = CMPA toggle o/p of EPWMxB */  
0302     REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLB_ZRO_XBHIGH | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT);
0303     REG16(baseAddr + AM335X_EPWM_TBCNT) = 0;
0304   /* Set counter mode : Up-count mode */
0305     REG16(baseAddr + AM335X_EPWM_TBCTL) |=  AM335X_TBCTL_FREERUN  | AM335X_TBCTL_CTRMODE_UP;
0306   }  else  {
0307        status =false;
0308   }
0309   return status;    
0310 }
0311 
0312 bool beagle_pwm_disable(BBB_PWMSS pwmid)
0313 {
0314   const bool id_is_valid = pwmid < BBB_PWMSS_COUNT;
0315   bool status = true;
0316   
0317   if (id_is_valid) {
0318     const uint32_t baseAddr = select_pwm(pwmid);
0319     REG16(baseAddr + AM335X_EPWM_TBCTL) = AM335X_EPWM_TBCTL_CTRMODE_STOPFREEZE;
0320     REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XALOW | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT);
0321     REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLA_ZRO_XBLOW | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT);
0322     REG16(baseAddr + AM335X_EPWM_TBCNT)  = 0;
0323   }  else  {
0324     status = false;
0325   }
0326   return status;
0327 }
0328 
0329 /**
0330  * @brief   This functions determines whether time base clock is enabled for EPWMSS
0331  *
0332  * @param   pwmss_id  The instance number of ePWMSS whose time base clock need to
0333  *                    be checked
0334  *                    
0335  * @return  returns 4 for PWMSS_ID = 2
0336  *          returns 2 for PWMSS_ID = 1
0337  *          returns 1 for PWMSS_ID = 0
0338  **/ 
0339 static int pwmss_tb_clock_check(unsigned int pwmss_id)
0340 {
0341   unsigned int reg_value;
0342 
0343   /*control module check*/
0344   reg_value = REG(AM335X_CONTROL_MODULE + AM335X_PWMSS_CTRL);
0345   return (reg_value & (1 << pwmss_id));
0346 }
0347 
0348 /**
0349  * @brief   This functions determines whether clock for EPWMSS is enabled or not.
0350  *
0351  * @param   It is the Memory address of the PWMSS instance used.
0352  *
0353  * @return  
0354  *
0355  **/
0356 static unsigned int pwmss_clock_en_status(unsigned int pwmid)
0357 {
0358   unsigned int status;
0359   const uint32_t baseAddr = select_pwmss(pwmid);
0360   
0361   status = REG(baseAddr + AM335X_PWMSS_CLKSTATUS);
0362   status = status >> 8 & 0x1;
0363   return status;
0364 }
0365 
0366 bool beagle_pwmss_is_running(unsigned int pwmss_id)
0367 {
0368   const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT;
0369   bool status=true;
0370   
0371   if (id_is_valid) {
0372     status = pwmss_clock_en_status(pwmss_id);
0373     if(status){
0374     status = pwmss_tb_clock_check(pwmss_id);
0375     } else {
0376         status = false;
0377     }  
0378   } else {
0379   status = false;
0380   }
0381   return status;
0382 }
0383 
0384 #endif
0385 
0386 /* For support of BeagleboardxM */
0387 #if IS_DM3730
0388 
0389 /* Currently this section is just to satisfy
0390  * GPIO API and to make the build successful.
0391  * Later on support can be added here.
0392  */
0393 bool beagle_pwm_init(BBB_PWMSS pwmss_id)
0394 {
0395   return false;
0396 }
0397 bool beagle_pwm_disable(BBB_PWMSS pwmid)
0398 {
0399   return false;
0400 }
0401 bool beagle_pwm_enable(BBB_PWMSS pwmid)
0402 {
0403   return false;
0404 }
0405 int beagle_pwm_configure(BBB_PWMSS pwm_id, float pwm_freq, float duty_a, float duty_b)
0406 {
0407   return -1;
0408 }
0409 bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no, BBB_PWMSS pwm_id)
0410 {
0411   return false;
0412 }
0413 bool beagle_pwmss_is_running(unsigned int pwmss_id)
0414 {
0415 return false;
0416 }
0417 #endif