source: rtems/c/src/lib/libbsp/arm/beagle/pwm/pwm.c @ 5e3096db

5
Last change on this file since 5e3096db was 5e3096db, checked in by Punit Vara <punitvara@…>, on 07/04/16 at 18:05:43

Beaglebone: Update PWM driver imported from BBBIO

This patch adapts the previously added Beaglebone PWM code from BBBIO to RTEMS.
This work was done in the context of the Google Summer of Code 2016, and further
patches will follow to improve the code quality and documentation.

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup arm_beagle
5 *
6 * @brief Support for PWM for the BeagleBone Black.
7 */
8
9/**
10 * Copyright (c) 2016 Punit Vara <punitvara at gmail.com>
11 *
12 * The license and distribution terms for this file may be
13 * found in the file LICENSE in this distribution or at
14 * http://www.rtems.org/license/LICENSE.
15 */
16
17/** This file is based on
18  * https://github.com/VegetableAvenger/BBBIOlib/blob/master/BBBio_lib/BBBiolib_PWMSS.c
19  */
20
21#include <libcpu/am335x.h>
22#include <stdio.h>
23#include <bsp/gpio.h>
24#include <bsp/bbb-gpio.h>
25#include <bsp.h>
26#include <bsp/bbb-pwm.h>
27
28/* Currently these definitions are for BeagleBone Black board only
29 * Later on Beagle-xM board support can be added in this code.
30 * After support gets added if condition should be removed
31 */
32#if IS_AM335X
33
34/*
35 * @brief This function select PWM module to be enabled
36 *
37 * @param pwm_id It is the instance number of EPWM of pwm sub system.
38 *
39 * @return Base Address of respective pwm instant.
40*/
41static uint32_t select_pwmss(uint32_t pwm_id)
42{
43uint32_t baseAddr=0;
44   if (pwm_id == BBB_PWMSS0)
45   {
46   baseAddr = AM335X_EPWM_0_REGS;
47   return baseAddr;
48   }
49   else if (pwm_id == BBB_PWMSS1)
50   {
51   baseAddr = AM335X_EPWM_1_REGS;
52   return baseAddr;
53   }
54   else if (pwm_id == BBB_PWMSS2)
55   {
56   baseAddr = AM335X_EPWM_2_REGS;
57   return baseAddr;
58   }
59   else
60   {
61   printf("Invalid PWM Id\n");
62   return 0;   
63   }
64}
65
66bool beagle_epwm_pinmux_setup(uint32_t pin_no, uint32_t pwm_id)
67{
68  switch(pwm_id)  {
69   case BBB_PWMSS2:
70       switch(pin_no) {
71           case BBB_P8_13_2B:
72               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(9)) = BBB_MUXMODE(BBB_MUX4);
73               break;
74           case BBB_P8_19_2A:
75               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(8)) = BBB_MUXMODE(BBB_MUX4);
76               break;
77           case BBB_P8_45_2A:
78               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(0)) = BBB_MUXMODE(BBB_MUX3);
79               break;
80           case BBB_P8_46_2B:
81               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(1)) = BBB_MUXMODE(BBB_MUX3);
82               break;
83           default :
84               printf("Invalid pin for module 2\n");
85               return false;
86       }
87       break;
88   case BBB_PWMSS1:
89       switch(pin_no) {
90           case BBB_P8_34_1B:
91               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(11)) = BBB_MUXMODE(BBB_MUX2);
92               break;
93           case BBB_P8_36_1A:
94               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(10)) = BBB_MUXMODE(BBB_MUX2);
95               break;
96           case BBB_P9_14_1A:
97               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(2)) = BBB_MUXMODE(BBB_MUX6);
98               break;
99           case BBB_P9_16_1B:
100               REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(3)) = BBB_MUXMODE(BBB_MUX6);
101               break;
102           default :
103               printf("Invalid pin for module 1\n");
104               return false;
105       }   
106       break;
107   case BBB_PWMSS0:
108       switch(pin_no) {
109           case BBB_P9_21_0B:
110               REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D0) = BBB_MUXMODE(BBB_MUX3);
111               break;
112           case BBB_P9_22_0A:
113               REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_SCLK) = BBB_MUXMODE(BBB_MUX3);
114               break;
115           case BBB_P9_29_0B:
116               REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_FSX) = BBB_MUXMODE(BBB_MUX1);
117               break;
118           case BBB_P9_31_0A:
119               REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_ACLKX) = BBB_MUXMODE(BBB_MUX1);
120               break;
121           default:
122               printf("Invalid pin for module 0\n");
123               return false;
124       }
125       break;
126   
127   default:
128       printf("Invalid PWM sub system\n");
129       return false;
130}
131}
132
133/**
134 * @brief   This function Enables TBCLK(Time Base Clock) for specific
135 *          EPWM instance of pwmsubsystem.
136 *
137 * @param   instance  It is the instance number of EPWM of pwmsubsystem.
138 *
139 * @return  true if successful
140 **/
141static bool pwmss_tbclk_enable(unsigned int instance)
142{
143uint32_t enable_bit;
144bool is_valid = true;
145 
146  if (instance == BBB_PWMSS0)
147  {
148       enable_bit = AM335X_PWMSS_CTRL_PWMSS0_TBCLKEN;
149  }
150  else if (instance == BBB_PWMSS1)
151  {
152       enable_bit = AM335X_PWMSS_CTRL_PWMSS1_TBCLKEN;
153  }
154  else if (instance == BBB_PWMSS2)
155  {
156       enable_bit = AM335X_PWMSS_CTRL_PWMSS2_TBCLKEN;
157  }
158  else
159  {
160       is_valid = false;
161  }
162
163  if (is_valid)
164  {
165       REG(AM335X_PADCONF_BASE + AM335X_PWMSS_CTRL) |= enable_bit;
166  }
167
168  return is_valid;
169 }
170
171/**
172 * @brief   This functions enables clock for EHRPWM module in PWMSS subsystem.
173 *
174 * @param   pwm_id  It is the instance number of EPWM of pwm sub system.
175 *
176 * @return  None.
177 *
178 **/
179static void epwm_clock_enable(uint32_t pwm_id)
180
181   if((pwm_id <3) && (pwm_id >=0)) {
182       uint32_t baseAddr;
183       baseAddr = select_pwmss(pwm_id);
184               REG(baseAddr - AM335X_EPWM_REGS + AM335X_PWMSS_CLKCONFIG) |= AM335X_PWMSS_CLK_EN_ACK;
185        } else {
186       printf("Invalid pwm_id\n");
187   }
188}
189
190/**
191 * @brief   This function configures the L3 and L4_PER system clocks.
192 *          It also configures the system clocks for the specified ePWMSS
193 *          instance.
194 *
195 * @param   pwmss_id    The instance number of ePWMSS whose system clocks
196 *                         have to be configured.
197 *
198 * 'pwmss_id' can take one of the following values:
199 * (0 <= pwmss_id <= 2)
200 *
201 * @return  None.
202 *
203 */
204static void module_clk_config(uint32_t pwmss_id)
205{
206        if(pwmss_id == 0)
207        {
208                REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS0_CLKCTRL) |=
209                        AM335X_CM_PER_EPWMSS0_CLKCTRL_MODULEMODE_ENABLE;
210   
211    while(AM335X_CM_PER_EPWMSS0_CLKCTRL_MODULEMODE_ENABLE !=
212              (REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS0_CLKCTRL) &
213               AM335X_CM_PER_EPWMSS0_CLKCTRL_MODULEMODE));
214
215    while((AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST_FUNC <<
216               AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST_SHIFT) !=
217              (REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS0_CLKCTRL) &
218               AM335X_CM_PER_EPWMSS0_CLKCTRL_IDLEST));
219        }
220        else if(pwmss_id == 1)
221        {
222                REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS1_CLKCTRL) |=
223                        AM335X_CM_PER_EPWMSS1_CLKCTRL_MODULEMODE_ENABLE;
224   while(AM335X_CM_PER_EPWMSS1_CLKCTRL_MODULEMODE_ENABLE !=
225              (REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS1_CLKCTRL) &
226               AM335X_CM_PER_EPWMSS1_CLKCTRL_MODULEMODE));
227
228        while((AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST_FUNC <<
229               AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST_SHIFT) !=
230               (REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS1_CLKCTRL) &
231               AM335X_CM_PER_EPWMSS1_CLKCTRL_IDLEST));
232        }
233        else if(pwmss_id == 2)
234        {
235                REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS2_CLKCTRL) |=
236                        AM335X_CM_PER_EPWMSS2_CLKCTRL_MODULEMODE_ENABLE;
237   while(AM335X_CM_PER_EPWMSS2_CLKCTRL_MODULEMODE_ENABLE !=
238              (REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS2_CLKCTRL) &
239               AM335X_CM_PER_EPWMSS2_CLKCTRL_MODULEMODE));
240
241        while((AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST_FUNC <<
242               AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST_SHIFT) !=
243               (REG(AM335X_CM_PER_ADDR + AM335X_CM_PER_EPWMSS2_CLKCTRL) &
244                AM335X_CM_PER_EPWMSS2_CLKCTRL_IDLEST));
245        }
246        else
247        {
248       printf("Please enter valid pwm Id \n");
249        }
250}
251
252bool beagle_pwm_init(uint32_t pwmss_id)
253{
254  bool status = true;
255  if((pwmss_id <3) && (pwmss_id >=0))
256  {
257  module_clk_config(pwmss_id);
258  epwm_clock_enable(pwmss_id);
259  pwmss_tbclk_enable(pwmss_id);
260  return status;
261  }
262  else {
263   status =false;
264  return status;
265  }
266}
267
268int beagle_pwmss_setting(uint32_t pwm_id, float pwm_freq, float dutyA, float dutyB)
269
270  uint32_t baseAddr;
271  int status = 1;
272   
273  if(pwm_freq <= 0.5) {
274   status =0;
275   return status;
276  }
277  if(dutyA < 0.0f || dutyA > 100.0f || dutyB < 0.0f || dutyB > 100.0f) {
278   status = 0;
279   return status;
280  }
281  dutyA /= 100.0f;
282  dutyB /= 100.0f;
283
284  /*Compute necessary TBPRD*/
285  float Cyclens = 0.0f;
286  float Divisor =0;
287  int i,j;
288  const float CLKDIV_div[] = {1.0,2.0,4.0,8.0,16.0,32.0,64.0,128.0};
289  const float HSPCLKDIV_div[] = {1.0, 2.0, 4.0, 6.0, 8.0, 10.0,12.0, 14.0};
290  int NearCLKDIV =7;
291  int NearHSPCLKDIV =7;
292  int NearTBPRD =0;
293
294  /** 10^9 /Hz compute time per cycle (ns) */
295  Cyclens = 1000000000.0f / pwm_freq;
296
297  /** am335x provide (128* 14) divider and per TBPRD means 10ns when divider
298    * and max TBPRD is 65535 so max cycle is 128 * 8 * 14 * 65535 * 10ns */
299  Divisor = (Cyclens / 655350.0f);
300   
301  if(Divisor > (128 * 14)) {
302   printf("Can't generate %f HZ",pwm_freq);
303   return 0;
304  }
305  else {
306   for (i=0;i<8;i++) {
307       for(j=0 ; j<8; j++) {
308           if((CLKDIV_div[i] * HSPCLKDIV_div[j]) < (CLKDIV_div[NearCLKDIV]
309                       * HSPCLKDIV_div[NearHSPCLKDIV]) && (CLKDIV_div[i] * HSPCLKDIV_div[j] > Divisor)) {
310               NearCLKDIV = i;
311               NearHSPCLKDIV = j;
312           }
313       }
314   }
315  baseAddr = select_pwmss(pwm_id);
316  REG16(baseAddr + AM335X_EPWM_TBCTL) &= ~(AM335X_TBCTL_CLKDIV_MASK | AM335X_TBCTL_HSPCLKDIV_MASK);
317           
318  REG16(baseAddr + AM335X_EPWM_TBCTL) = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
319  (~AM335X_EPWM_TBCTL_CLKDIV)) | ((NearCLKDIV
320  << AM335X_EPWM_TBCTL_CLKDIV_SHIFT) & AM335X_EPWM_TBCTL_CLKDIV);
321
322  REG16(baseAddr + AM335X_EPWM_TBCTL) = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
323  (~AM335X_EPWM_TBCTL_HSPCLKDIV)) | ((NearHSPCLKDIV <<
324  AM335X_EPWM_TBCTL_HSPCLKDIV_SHIFT) & AM335X_EPWM_TBCTL_HSPCLKDIV);
325
326  NearTBPRD = (Cyclens / (10.0 * CLKDIV_div[NearCLKDIV] * HSPCLKDIV_div[NearHSPCLKDIV]));
327       
328  REG16(baseAddr + AM335X_EPWM_TBCTL) = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
329  (~AM335X_EPWM_PRD_LOAD_SHADOW_MASK)) | (((bool)AM335X_EPWM_SHADOW_WRITE_DISABLE <<
330  AM335X_EPWM_TBCTL_PRDLD_SHIFT) & AM335X_EPWM_PRD_LOAD_SHADOW_MASK);
331
332  REG16(baseAddr + AM335X_EPWM_TBCTL) = (REG16(baseAddr + AM335X_EPWM_TBCTL) &
333  (~AM335X_EPWM_COUNTER_MODE_MASK)) | (((unsigned int)AM335X_EPWM_COUNT_UP <<
334  AM335X_TBCTL_CTRMODE_SHIFT) &  AM335X_EPWM_COUNTER_MODE_MASK);
335
336  /*setting clock divider and freeze time base*/
337  REG16(baseAddr + AM335X_EPWM_CMPB) = (unsigned short)((float)(NearTBPRD) * dutyB);
338  REG16(baseAddr + AM335X_EPWM_CMPA) = (unsigned short)((float)(NearTBPRD) * dutyA);
339  REG16(baseAddr + AM335X_EPWM_TBPRD) = (unsigned short)NearTBPRD;
340  REG16(baseAddr + AM335X_EPWM_TBCNT) = 0;
341  }
342  return status;
343}
344
345bool beagle_ehrpwm_enable(uint32_t pwmid)
346{
347  bool status = true;
348  uint32_t baseAddr;
349  if((pwmid<3) && (pwmid >=0)) {
350  baseAddr = select_pwmss(pwmid);
351  REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XAHIGH | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT);
352  REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLB_ZRO_XBHIGH | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT);
353  REG16(baseAddr + AM335X_EPWM_TBCNT) = 0;
354  REG16(baseAddr + AM335X_EPWM_TBCTL) |=  AM335X_TBCTL_FREERUN  | AM335X_TBCTL_CTRMODE_UP;
355  return status;
356  }
357  else {
358   status =false;
359   return status;
360  }
361}
362
363bool beagle_ehrpwm_disable(uint32_t pwmid)
364{
365  bool status = true;
366  uint32_t baseAddr;
367  if((pwmid<3) && (pwmid >=0)) {
368  baseAddr = select_pwmss(pwmid);
369  REG16(baseAddr + AM335X_EPWM_TBCTL) = AM335X_EPWM_TBCTL_CTRMODE_STOPFREEZE;
370  REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XALOW | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT);
371  REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLA_ZRO_XBLOW | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT);
372  REG16(baseAddr + AM335X_EPWM_TBCNT)  = 0;
373  return status;
374  }
375  else {
376   status = false;
377   return status;
378  }
379}
380
381#endif
382
383/* For support of BeagleboardxM */
384#if IS_DM3730
385
386/* Currently this section is just to satisfy
387 * GPIO API and to make the build successful.
388 * Later on support can be added here.
389 */
390uint32_t select_pwmss(uint32_t pwm_id)
391{
392return -1;
393}
394bool pwmss_tbclk_enable(unsigned int instance)
395{
396return false;
397}
398bool beagle_pwm_init(uint32_t pwmss_id)
399{
400return false;
401}
402bool beagle_ehrpwm_disable(uint32_t pwmid)
403{
404return false;
405}
406bool beagle_ehrpwm_enable(uint32_t pwmid)
407{
408return false;
409}
410int beagle_pwmss_setting(uint32_t pwm_id, float pwm_freq, float dutyA, float dutyB)
411{
412return -1;
413}
414bool beagle_epwm_pinmux_setup(uint32_t pin_no, uint32_t pwm_id)
415{
416return false;
417}
418
419#endif
Note: See TracBrowser for help on using the repository browser.