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@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/pwmss.h> |
---|
27 | #include <bsp/bbb-pwm.h> |
---|
28 | #include <bsp/beagleboneblack.h> |
---|
29 | |
---|
30 | /* Currently these definitions are for BeagleBone Black board only |
---|
31 | * Later on Beagle-xM board support can be added in this code. |
---|
32 | * After support gets added if condition should be removed |
---|
33 | */ |
---|
34 | #if IS_AM335X |
---|
35 | |
---|
36 | /* |
---|
37 | * @brief This function selects EPWM module to be enabled |
---|
38 | * |
---|
39 | * @param pwm_id It is the instance number of EPWM of pwm sub system. |
---|
40 | * |
---|
41 | * @return Base Address of respective pwm instant. |
---|
42 | */ |
---|
43 | static uint32_t select_pwm(BBB_PWMSS pwm_id) |
---|
44 | { |
---|
45 | uint32_t baseAddr=0; |
---|
46 | |
---|
47 | if (pwm_id == BBB_PWMSS0) { |
---|
48 | baseAddr = AM335X_EPWM_0_REGS; |
---|
49 | } else if (pwm_id == BBB_PWMSS1) { |
---|
50 | baseAddr = AM335X_EPWM_1_REGS; |
---|
51 | } else if (pwm_id == BBB_PWMSS2) { |
---|
52 | baseAddr = AM335X_EPWM_2_REGS; |
---|
53 | } else { |
---|
54 | baseAddr = 0; |
---|
55 | } |
---|
56 | return baseAddr; |
---|
57 | } |
---|
58 | |
---|
59 | /* |
---|
60 | * @brief This function selects PWM Sub system to be enabled |
---|
61 | * |
---|
62 | * @param pwmss_id The instance number of ePWMSS whose system clocks |
---|
63 | * have to be configured. |
---|
64 | * |
---|
65 | * @return Base Address of respective pwmss instant. |
---|
66 | */ |
---|
67 | static uint32_t select_pwmss(BBB_PWMSS pwmss_id) |
---|
68 | { |
---|
69 | uint32_t baseAddr=0; |
---|
70 | |
---|
71 | if (pwmss_id == BBB_PWMSS0) { |
---|
72 | baseAddr = AM335X_PWMSS0_MMAP_ADDR; |
---|
73 | } else if (pwmss_id == BBB_PWMSS1) { |
---|
74 | baseAddr = AM335X_PWMSS1_MMAP_ADDR; |
---|
75 | } else if (pwmss_id == BBB_PWMSS2) { |
---|
76 | baseAddr = AM335X_PWMSS2_MMAP_ADDR; |
---|
77 | } else { |
---|
78 | baseAddr = 0; |
---|
79 | } |
---|
80 | return baseAddr; |
---|
81 | } |
---|
82 | |
---|
83 | bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no, BBB_PWMSS pwm_id) |
---|
84 | { |
---|
85 | bool is_valid = true; |
---|
86 | |
---|
87 | if(pwm_id == BBB_PWMSS0) { |
---|
88 | if (pin_no == BBB_P9_21_0B) { |
---|
89 | REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_D0) = BBB_MUXMODE(BBB_P9_21_MUX_PWM); |
---|
90 | } else if (pin_no == BBB_P9_22_0A) { |
---|
91 | REG(AM335X_PADCONF_BASE + AM335X_CONF_SPI0_SCLK) = BBB_MUXMODE(BBB_P9_22_MUX_PWM); |
---|
92 | } else if (pin_no == BBB_P9_29_0B) { |
---|
93 | REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_FSX) = BBB_MUXMODE(BBB_P9_29_MUX_PWM); |
---|
94 | } else if (pin_no == BBB_P9_31_0A) { |
---|
95 | REG(AM335X_PADCONF_BASE + AM335X_CONF_MCASP0_ACLKX) = BBB_MUXMODE(BBB_P9_31_MUX_PWM); |
---|
96 | } else { |
---|
97 | is_valid = false; |
---|
98 | } |
---|
99 | |
---|
100 | } else if (pwm_id == BBB_PWMSS1) { |
---|
101 | if (pin_no == BBB_P8_34_1B) { |
---|
102 | REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(11)) = BBB_MUXMODE(BBB_P8_34_MUX_PWM); |
---|
103 | } else if (pin_no == BBB_P8_36_1A) { |
---|
104 | REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(10)) = BBB_MUXMODE(BBB_P8_36_MUX_PWM); |
---|
105 | } else if (pin_no == BBB_P9_14_1A) { |
---|
106 | REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A2) = BBB_MUXMODE(BBB_P9_14_MUX_PWM); |
---|
107 | } else if (pin_no == BBB_P9_16_1B) { |
---|
108 | REG(AM335X_PADCONF_BASE + AM335X_CONF_GPMC_A3) = BBB_MUXMODE(BBB_P9_16_MUX_PWM); |
---|
109 | } else { |
---|
110 | is_valid = false; |
---|
111 | } |
---|
112 | } else if (pwm_id == BBB_PWMSS2) { |
---|
113 | if (pin_no == BBB_P8_13_2B) { |
---|
114 | REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(9)) = BBB_MUXMODE(BBB_P8_13_MUX_PWM); |
---|
115 | } else if (pin_no == BBB_P8_19_2A) { |
---|
116 | REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_GPMC_AD(8)) = BBB_MUXMODE(BBB_P8_19_MUX_PWM); |
---|
117 | } else if (pin_no == BBB_P8_45_2A) { |
---|
118 | REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(0)) = BBB_MUXMODE(BBB_P8_45_MUX_PWM); |
---|
119 | } else if (pin_no == BBB_P8_46_2B) { |
---|
120 | REG(AM335X_PADCONF_BASE + BBB_CONTROL_CONF_LCD_DATA(1)) = BBB_MUXMODE(BBB_P8_46_MUX_PWM); |
---|
121 | } else { |
---|
122 | is_valid = false; |
---|
123 | } |
---|
124 | } else { |
---|
125 | is_valid = false; |
---|
126 | } |
---|
127 | return is_valid; |
---|
128 | } |
---|
129 | |
---|
130 | /** |
---|
131 | * @brief This function Enables TBCLK(Time Base Clock) for specific |
---|
132 | * EPWM instance of pwmsubsystem. |
---|
133 | * |
---|
134 | * @param instance It is the instance number of EPWM of pwmsubsystem. |
---|
135 | * |
---|
136 | * @return true if successful |
---|
137 | * false if unsuccessful |
---|
138 | **/ |
---|
139 | static bool pwmss_tbclk_enable(BBB_PWMSS instance) |
---|
140 | { |
---|
141 | uint32_t enable_bit; |
---|
142 | bool is_valid = true; |
---|
143 | |
---|
144 | if (instance == BBB_PWMSS0) { |
---|
145 | enable_bit = AM335X_PWMSS_CTRL_PWMSS0_TBCLKEN; |
---|
146 | } else if (instance == BBB_PWMSS1) { |
---|
147 | enable_bit = AM335X_PWMSS_CTRL_PWMSS1_TBCLKEN; |
---|
148 | } else if (instance == BBB_PWMSS2) { |
---|
149 | enable_bit = AM335X_PWMSS_CTRL_PWMSS2_TBCLKEN; |
---|
150 | } else { |
---|
151 | is_valid = false; |
---|
152 | } |
---|
153 | |
---|
154 | if (is_valid) |
---|
155 | { |
---|
156 | REG(AM335X_PADCONF_BASE + AM335X_PWMSS_CTRL) |= enable_bit; |
---|
157 | } |
---|
158 | |
---|
159 | return is_valid; |
---|
160 | } |
---|
161 | |
---|
162 | /** |
---|
163 | * @brief This functions enables clock for EHRPWM module in PWMSS subsystem. |
---|
164 | * |
---|
165 | * @param pwm_id It is the instance number of EPWM of pwm sub system. |
---|
166 | * |
---|
167 | * @return true if successful |
---|
168 | * false if unsuccessful |
---|
169 | * |
---|
170 | **/ |
---|
171 | static bool pwm_clock_enable(BBB_PWMSS pwm_id) |
---|
172 | { |
---|
173 | const bool id_is_valid = pwm_id < BBB_PWMSS_COUNT; |
---|
174 | bool status = true; |
---|
175 | |
---|
176 | if (id_is_valid) { |
---|
177 | const uint32_t baseAddr = select_pwmss(pwm_id); |
---|
178 | REG(baseAddr + AM335X_PWMSS_CLKCONFIG) |= AM335X_PWMSS_CLK_EN_ACK; |
---|
179 | } else { |
---|
180 | status = false; |
---|
181 | } |
---|
182 | return status; |
---|
183 | } |
---|
184 | |
---|
185 | bool beagle_pwm_init(BBB_PWMSS pwmss_id) |
---|
186 | { |
---|
187 | const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT; |
---|
188 | bool status = true; |
---|
189 | |
---|
190 | if(id_is_valid) { |
---|
191 | pwmss_module_clk_config(pwmss_id); |
---|
192 | pwm_clock_enable(pwmss_id); |
---|
193 | pwmss_tbclk_enable(pwmss_id); |
---|
194 | } else { |
---|
195 | status =false; |
---|
196 | } |
---|
197 | return status; |
---|
198 | } |
---|
199 | |
---|
200 | int beagle_pwm_configure(BBB_PWMSS pwm_id, float pwm_freq, float duty_a, float duty_b) |
---|
201 | { |
---|
202 | uint32_t baseAddr; |
---|
203 | int status = 1; |
---|
204 | float cycle = 0.0f,divisor = 0; |
---|
205 | unsigned int i,j; |
---|
206 | const float CLKDIV_div[] = {1.0,2.0,4.0,8.0,16.0,32.0,64.0,128.0}; |
---|
207 | const float HSPCLKDIV_div[] = {1.0, 2.0, 4.0, 6.0, 8.0, 10.0,12.0, 14.0}; |
---|
208 | int NearCLKDIV =7,NearHSPCLKDIV =7,NearTBPRD =0; |
---|
209 | |
---|
210 | if (pwm_freq <= BBB_PWM_FREQ_THRESHOLD) { |
---|
211 | status =0; |
---|
212 | } |
---|
213 | |
---|
214 | if (duty_a < 0.0f || duty_a > 100.0f || duty_b < 0.0f || duty_b > 100.0f) { |
---|
215 | status = 0; |
---|
216 | } |
---|
217 | duty_a /= 100.0f; |
---|
218 | duty_b /= 100.0f; |
---|
219 | |
---|
220 | /** 10^9 /Hz compute time per cycle (ns) */ |
---|
221 | cycle = 1000000000.0f / pwm_freq; |
---|
222 | |
---|
223 | /** am335x provide (128* 14) divider and per TBPRD means 10ns when divider |
---|
224 | * and max TBPRD is 65535 so max cycle is 128 * 8 * 14 * 65535 * 10ns */ |
---|
225 | divisor = (cycle / 655350.0f); |
---|
226 | if (divisor > (128 * 14)) { |
---|
227 | return 0; |
---|
228 | } |
---|
229 | else { |
---|
230 | for (i=0;i<8;i++) { |
---|
231 | for(j=0 ; j<8; j++) { |
---|
232 | if((CLKDIV_div[i] * HSPCLKDIV_div[j]) < (CLKDIV_div[NearCLKDIV] |
---|
233 | * HSPCLKDIV_div[NearHSPCLKDIV]) && (CLKDIV_div[i] * HSPCLKDIV_div[j] > divisor)) { |
---|
234 | NearCLKDIV = i; |
---|
235 | NearHSPCLKDIV = j; |
---|
236 | } |
---|
237 | } |
---|
238 | } |
---|
239 | |
---|
240 | baseAddr = select_pwm(pwm_id); |
---|
241 | |
---|
242 | REG16(baseAddr + AM335X_EPWM_TBCTL) &= ~(AM335X_TBCTL_CLKDIV_MASK | AM335X_TBCTL_HSPCLKDIV_MASK); |
---|
243 | const uint16_t clkdiv_clear = (REG16(baseAddr + AM335X_EPWM_TBCTL) & |
---|
244 | (~AM335X_EPWM_TBCTL_CLKDIV)); |
---|
245 | const uint16_t clkdiv_write = ((NearCLKDIV |
---|
246 | << AM335X_EPWM_TBCTL_CLKDIV_SHIFT) & AM335X_EPWM_TBCTL_CLKDIV); |
---|
247 | REG16(baseAddr + AM335X_EPWM_TBCTL) = clkdiv_clear | clkdiv_write; |
---|
248 | const uint16_t hspclkdiv_clear = (REG16(baseAddr + AM335X_EPWM_TBCTL) & |
---|
249 | (~AM335X_EPWM_TBCTL_HSPCLKDIV)); |
---|
250 | const uint16_t hspclkdiv_write = ((NearHSPCLKDIV << |
---|
251 | AM335X_EPWM_TBCTL_HSPCLKDIV_SHIFT) & AM335X_EPWM_TBCTL_HSPCLKDIV); |
---|
252 | REG16(baseAddr + AM335X_EPWM_TBCTL) = hspclkdiv_clear | hspclkdiv_write; |
---|
253 | NearTBPRD = (cycle / (10.0 * CLKDIV_div[NearCLKDIV] * HSPCLKDIV_div[NearHSPCLKDIV])); |
---|
254 | const uint16_t shadow_mask = (REG16(baseAddr + AM335X_EPWM_TBCTL) & |
---|
255 | (~AM335X_EPWM_PRD_LOAD_SHADOW_MASK)); |
---|
256 | const uint16_t shadow_disable = (((bool)AM335X_EPWM_SHADOW_WRITE_DISABLE << |
---|
257 | AM335X_EPWM_TBCTL_PRDLD_SHIFT) & AM335X_EPWM_PRD_LOAD_SHADOW_MASK); |
---|
258 | REG16(baseAddr + AM335X_EPWM_TBCTL) = shadow_mask | shadow_disable; |
---|
259 | const uint16_t counter_mask = (REG16(baseAddr + AM335X_EPWM_TBCTL) & |
---|
260 | (~AM335X_EPWM_COUNTER_MODE_MASK)); |
---|
261 | const uint16_t counter_shift = (((unsigned int)AM335X_EPWM_COUNT_UP << |
---|
262 | AM335X_TBCTL_CTRMODE_SHIFT) & AM335X_EPWM_COUNTER_MODE_MASK); |
---|
263 | REG16(baseAddr + AM335X_EPWM_TBCTL) = counter_mask | counter_shift; |
---|
264 | /*setting clock divider and freeze time base*/ |
---|
265 | REG16(baseAddr + AM335X_EPWM_CMPB) = (unsigned short)((float)(NearTBPRD) * duty_b); |
---|
266 | REG16(baseAddr + AM335X_EPWM_CMPA) = (unsigned short)((float)(NearTBPRD) * duty_a); |
---|
267 | REG16(baseAddr + AM335X_EPWM_TBPRD) = (unsigned short)NearTBPRD; |
---|
268 | REG16(baseAddr + AM335X_EPWM_TBCNT) = 0; |
---|
269 | } |
---|
270 | return status; |
---|
271 | } |
---|
272 | |
---|
273 | bool beagle_pwm_enable(BBB_PWMSS pwmid) |
---|
274 | { |
---|
275 | const bool id_is_valid = pwmid < BBB_PWMSS_COUNT; |
---|
276 | bool status = true; |
---|
277 | |
---|
278 | if (id_is_valid) { |
---|
279 | const uint32_t baseAddr = select_pwm(pwmid); |
---|
280 | /* Initially set EPWMxA o/p high , when increasing counter = CMPA toggle o/p of EPWMxA */ |
---|
281 | REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XAHIGH | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT); |
---|
282 | /* Initially set EPWMxB o/p high , when increasing counter = CMPA toggle o/p of EPWMxB */ |
---|
283 | REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLB_ZRO_XBHIGH | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT); |
---|
284 | REG16(baseAddr + AM335X_EPWM_TBCNT) = 0; |
---|
285 | /* Set counter mode : Up-count mode */ |
---|
286 | REG16(baseAddr + AM335X_EPWM_TBCTL) |= AM335X_TBCTL_FREERUN | AM335X_TBCTL_CTRMODE_UP; |
---|
287 | } else { |
---|
288 | status =false; |
---|
289 | } |
---|
290 | return status; |
---|
291 | } |
---|
292 | |
---|
293 | bool beagle_pwm_disable(BBB_PWMSS pwmid) |
---|
294 | { |
---|
295 | const bool id_is_valid = pwmid < BBB_PWMSS_COUNT; |
---|
296 | bool status = true; |
---|
297 | |
---|
298 | if (id_is_valid) { |
---|
299 | const uint32_t baseAddr = select_pwm(pwmid); |
---|
300 | REG16(baseAddr + AM335X_EPWM_TBCTL) = AM335X_EPWM_TBCTL_CTRMODE_STOPFREEZE; |
---|
301 | REG16(baseAddr + AM335X_EPWM_AQCTLA) = AM335X_EPWM_AQCTLA_ZRO_XALOW | (AM335X_EPWM_AQCTLA_CAU_EPWMXATOGGLE << AM335X_EPWM_AQCTLA_CAU_SHIFT); |
---|
302 | REG16(baseAddr + AM335X_EPWM_AQCTLB) = AM335X_EPWM_AQCTLA_ZRO_XBLOW | (AM335X_EPWM_AQCTLB_CBU_EPWMXBTOGGLE << AM335X_EPWM_AQCTLB_CBU_SHIFT); |
---|
303 | REG16(baseAddr + AM335X_EPWM_TBCNT) = 0; |
---|
304 | } else { |
---|
305 | status = false; |
---|
306 | } |
---|
307 | return status; |
---|
308 | } |
---|
309 | |
---|
310 | /** |
---|
311 | * @brief This functions determines whether time base clock is enabled for EPWMSS |
---|
312 | * |
---|
313 | * @param pwmss_id The instance number of ePWMSS whose time base clock need to |
---|
314 | * be checked |
---|
315 | * |
---|
316 | * @return returns 4 for PWMSS_ID = 2 |
---|
317 | * returns 2 for PWMSS_ID = 1 |
---|
318 | * returns 1 for PWMSS_ID = 0 |
---|
319 | **/ |
---|
320 | static int pwmss_tb_clock_check(unsigned int pwmss_id) |
---|
321 | { |
---|
322 | unsigned int reg_value; |
---|
323 | |
---|
324 | /*control module check*/ |
---|
325 | reg_value = REG(AM335X_CONTROL_MODULE + AM335X_PWMSS_CTRL); |
---|
326 | return (reg_value & (1 << pwmss_id)); |
---|
327 | } |
---|
328 | |
---|
329 | /** |
---|
330 | * @brief This functions determines whether clock for EPWMSS is enabled or not. |
---|
331 | * |
---|
332 | * @param It is the Memory address of the PWMSS instance used. |
---|
333 | * |
---|
334 | * @return |
---|
335 | * |
---|
336 | **/ |
---|
337 | static unsigned int pwmss_clock_en_status(unsigned int pwmid) |
---|
338 | { |
---|
339 | unsigned int status; |
---|
340 | const uint32_t baseAddr = select_pwmss(pwmid); |
---|
341 | |
---|
342 | status = REG(baseAddr + AM335X_PWMSS_CLKSTATUS); |
---|
343 | status = status >> 8 & 0x1; |
---|
344 | return status; |
---|
345 | } |
---|
346 | |
---|
347 | bool beagle_pwmss_is_running(unsigned int pwmss_id) |
---|
348 | { |
---|
349 | const bool id_is_valid = pwmss_id < BBB_PWMSS_COUNT; |
---|
350 | bool status=true; |
---|
351 | |
---|
352 | if (id_is_valid) { |
---|
353 | status = pwmss_clock_en_status(pwmss_id); |
---|
354 | if(status){ |
---|
355 | status = pwmss_tb_clock_check(pwmss_id); |
---|
356 | } else { |
---|
357 | status = false; |
---|
358 | } |
---|
359 | } else { |
---|
360 | status = false; |
---|
361 | } |
---|
362 | return status; |
---|
363 | } |
---|
364 | |
---|
365 | #endif |
---|
366 | |
---|
367 | /* For support of BeagleboardxM */ |
---|
368 | #if IS_DM3730 |
---|
369 | |
---|
370 | /* Currently this section is just to satisfy |
---|
371 | * GPIO API and to make the build successful. |
---|
372 | * Later on support can be added here. |
---|
373 | */ |
---|
374 | bool beagle_pwm_init(BBB_PWMSS pwmss_id) |
---|
375 | { |
---|
376 | return false; |
---|
377 | } |
---|
378 | bool beagle_pwm_disable(BBB_PWMSS pwmid) |
---|
379 | { |
---|
380 | return false; |
---|
381 | } |
---|
382 | bool beagle_pwm_enable(BBB_PWMSS pwmid) |
---|
383 | { |
---|
384 | return false; |
---|
385 | } |
---|
386 | int beagle_pwm_configure(BBB_PWMSS pwm_id, float pwm_freq, float duty_a, float duty_b) |
---|
387 | { |
---|
388 | return -1; |
---|
389 | } |
---|
390 | bool beagle_pwm_pinmux_setup(bbb_pwm_pin_t pin_no, BBB_PWMSS pwm_id) |
---|
391 | { |
---|
392 | return false; |
---|
393 | } |
---|
394 | bool beagle_pwmss_is_running(unsigned int pwmss_id) |
---|
395 | { |
---|
396 | return false; |
---|
397 | } |
---|
398 | #endif |
---|