Add pwm driver.
[bertos.git] / bertos / cpu / arm / drv / pwm_at91.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30  *
31  * -->
32  *
33  *
34  * \brief PWM hardware-specific implementation
35  *
36  * \version $Id$
37  *
38  * \author Daniele Basile <asterix@develer.com>
39  */
40
41 #include "pwm_at91.h"
42
43 #include <hw/hw_cpu.h>
44
45 #include <cfg/macros.h>
46 #include <cfg/debug.h>
47 #include <io/arm.h>
48
49 #include "appconfig.h"
50
51 /**
52  * Register structure for pwm driver.
53  * This array content all data and register pointer
54  * to manage pwm peripheral device.
55  */
56 static PwmChannel pwm_map[PWM_CNT] =
57 {
58         {//PWM Channel 0
59                 .duty_zero = false,
60                 .pwm_pin = BV(PWM0),
61                 .mode_reg = &PWM_CMR0,
62                 .duty_reg = &PWM_CDTY0,
63                 .period_reg = &PWM_CPRD0,
64                 .update_reg = &PWM_CUPD0,
65         },
66         {//PWM Channel 1
67                 .duty_zero = false,
68                 .pwm_pin = BV(PWM1),
69                 .mode_reg = &PWM_CMR1,
70                 .duty_reg = &PWM_CDTY1,
71                 .period_reg = &PWM_CPRD1,
72                 .update_reg = &PWM_CUPD1,
73         },
74         {//PWM Channel 2
75                 .duty_zero = false,
76                 .pwm_pin = BV(PWM2),
77                 .mode_reg = &PWM_CMR2,
78                 .duty_reg = &PWM_CDTY2,
79                 .period_reg = &PWM_CPRD2,
80                 .update_reg = &PWM_CUPD2,
81         },
82         {//PWM Channel 3
83                 .duty_zero = false,
84                 .pwm_pin = BV(PWM3),
85                 .mode_reg = &PWM_CMR3,
86                 .duty_reg = &PWM_CDTY3,
87                 .period_reg = &PWM_CPRD3,
88                 .update_reg = &PWM_CUPD3,
89         }
90 };
91
92
93 /**
94  * Get preiod from select channel
95  *
96  * \a dev channel
97  */
98 pwm_period_t pwm_hw_getPeriod(PwmDev dev)
99 {
100         return *pwm_map[dev].period_reg;
101 }
102
103 /**
104  * Set pwm waveform frequecy.
105  *
106  * \a freq in Hz
107  */
108 void pwm_hw_setFrequency(PwmDev dev, uint32_t freq)
109 {
110         uint32_t period = 0;
111
112         for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++)
113         {
114                 period = CLOCK_FREQ / (BV(i) * freq);
115 //              TRACEMSG("period[%d], prescale[%d]", period, i);
116                 if ((period < PWM_HW_MAX_PERIOD) && (period != 0))
117                 {
118                         //Clean previous channel prescaler, and set new
119                         *pwm_map[dev].mode_reg &= ~PWM_CPRE_MCK_MASK;
120                         *pwm_map[dev].mode_reg |= i;
121                         //Set pwm period
122                         *pwm_map[dev].period_reg = period;
123                         break;
124                 }
125         }
126
127         PWM_ENA = BV(dev);
128
129 //      TRACEMSG("PWM ch[%d] period[%d]", dev, period);
130 }
131
132 /**
133  * Set pwm duty cycle.
134  *
135  * \a duty value 0 - 2^16
136  */
137 void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty)
138 {
139         ASSERT(duty <= (uint16_t)*pwm_map[dev].period_reg);
140
141
142         /*
143          * WARNING: is forbidden to write 0 to duty cycle value,
144          * and so for duty = 0 we must enable PIO and clear output!
145          */
146         if (!duty)
147         {
148                 PWM_PIO_PER = pwm_map[dev].pwm_pin;
149                 pwm_map[dev].duty_zero = true;
150         }
151         else
152         {
153                 ASSERT(PWM_CCNT0);
154                 PWM_PIO_PDR = pwm_map[dev].pwm_pin;
155                 *pwm_map[dev].update_reg = duty;
156                 pwm_map[dev].duty_zero = false;
157         }
158
159 //      TRACEMSG("PWM ch[%d] duty[%d], period[%ld]", dev, duty, *pwm_map[dev].period_reg);
160 }
161
162
163 /**
164  * Enable select pwm channel
165  */
166 void pwm_hw_enable(PwmDev dev)
167 {
168         if (!pwm_map[dev].duty_zero)
169                 PWM_PIO_PDR = pwm_map[dev].pwm_pin;
170 }
171
172 /**
173  * Disable select pwm channel
174  */
175 void pwm_hw_disable(PwmDev dev)
176 {
177         PWM_PIO_PER = pwm_map[dev].pwm_pin;
178 }
179
180
181 /**
182  * Init pwm.
183  */
184 void pwm_hw_init(void)
185 {
186
187         /*
188          * Init pwm:
189          * WARNING: is forbidden to write 0 to duty cycle value,
190          * and so for duty = 0 we must enable PIO and clear output!
191          * - clear PIO outputs
192          * - enable PIO outputs
193          * - Disable PIO and enable PWM functions
194          * - Power on PWM
195          */
196         PWM_PIO_CODR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
197         PWM_PIO_OER = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
198         PWM_PIO_PDR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
199         PWM_PIO_ABSR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
200         PMC_PCER |= BV(PWMC_ID);
201
202         /* Disable all channels. */
203         PWM_DIS = 0xFFFFFFFF;
204         /* Disable prescalers A and B */
205         PWM_MR = 0;
206
207         /*
208          * Set pwm mode:
209          * - set period alidned to left
210          * - set output waveform to low level
211          * - allow duty cycle modify at next period event
212          */
213         for (int ch = 0; ch < PWM_CNT; ch++)
214                 *pwm_map[ch].mode_reg = 0;
215 }
216