Remove the PIO config from the clkout_on() function.
[dtplug] / drivers / timers.c
1 /****************************************************************************
2  *  drivers/timers.c
3  *
4  * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
5  *
6  * This program 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, see <http://www.gnu.org/licenses/>.
18  *
19  *************************************************************************** */
23 /***************************************************************************** */
24 /*                Timers                                                       */
25 /***************************************************************************** */
27 /* Timers driver for the integrated timers of the LPC176x.
28  * The LPC176x Has two 16bits timers and 2 32bits timers.
29  * Refer to LPC176x documentation (UM10360.pdf) for more information.
30  */
32 #include <stdint.h>
33 #include "core/lpc_regs_17xx.h"
34 #include "core/lpc_core_cm3.h"
35 #include "core/system.h"
36 #include "core/pio.h"
37 #include "lib/string.h"
38 #include "drivers/timers.h"
41 /* These are local to our file */
42 struct timer_device
43 {
44         struct lpc_timer* regs;
45         uint32_t power_bit;
46         uint8_t pclk_bit;
47         uint8_t irq;
48         void (*callback)(uint8_t); /* Possible RX callback */
49 };
50 static struct timer_device timer_devices[NUM_TIMERS] = {
51         { LPC_TMR32B0, LPC_TIMER0_POWER_ON, LPC_TIMER0_PCLK, TIMER0_IRQ }, 
52         { LPC_TMR32B1, LPC_TIMER1_POWER_ON, LPC_TIMER1_PCLK, TIMER1_IRQ },
53         { LPC_TMR32B2, LPC_TIMER2_POWER_ON, LPC_TIMER2_PCLK, TIMER2_IRQ },
54         { LPC_TMR32B3, LPC_TIMER3_POWER_ON, LPC_TIMER3_PCLK, TIMER3_IRQ },
55         { LPC_TMR_PWM, LPC_PWM1_POWER_ON, LPC_PWM1_PCLK, PWM1_IRQ },
56 };
60 /* Handlers */
61 void TIMER_Handler(struct timer_device* timer)
62 {
63         struct lpc_timer* regs = timer->regs;
64         uint32_t intr_flags = regs->int_reg; /* Backup the flags */
66         /* Clear the interrupt */
67         regs->int_reg = intr_flags;
68         /* And call the user routine if one has been registered */
69         if (timer->callback != NULL) {
70                 timer->callback(intr_flags);
71         }
72 }
73 void TIMER_0_Handler(void)
74 {
75         TIMER_Handler(&timer_devices[0]);
76 }
77 void TIMER_1_Handler(void)
78 {
79         TIMER_Handler(&timer_devices[1]);
80 }
81 void TIMER_2_Handler(void)
82 {
83         TIMER_Handler(&timer_devices[2]);
84 }
85 void TIMER_3_Handler(void)
86 {
87         TIMER_Handler(&timer_devices[3]);
88 }
92 /* Start the timer :
93  * Remove the reset flag if present and set timer enable flag.
94  * Timer must be turned on and configured (no checks done here).
95  */
96 void timer_start(uint32_t timer_num)
97 {
98         if (timer_num >= NUM_TIMERS)
99                 return;
100         /* Remove reset flag and set timer enable flag */
101         timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE;
103 void timer_continue(uint32_t timer_num) __attribute__ ((alias ("timer_start")));
104 /* Pause the timer counter, does not reset */
105 void timer_pause(uint32_t timer_num)
107         if (timer_num >= NUM_TIMERS)
108                 return;
109         /* Remove timer enable flag */
110         timer_devices[timer_num].regs->timer_ctrl = 0;
112 /* Stops and resets the timer counter */
113 void timer_stop(uint32_t timer_num)
115         if (timer_num >= NUM_TIMERS)
116                 return;
117         /* Remove timer enable flag */
118         timer_devices[timer_num].regs->timer_ctrl |= LPC_TIMER_COUNTER_RESET;
119         timer_devices[timer_num].regs->timer_ctrl = 0;
121 /* Resets the timer and lets it count again imediately */
122 void timer_restart(uint32_t timer_num)
124         if (timer_num >= NUM_TIMERS)
125                 return;
126         /* Set and remove timer reset flag */
127         timer_devices[timer_num].regs->timer_ctrl |= LPC_TIMER_COUNTER_RESET;
128         timer_devices[timer_num].regs->timer_ctrl &= ~LPC_TIMER_COUNTER_RESET;
131 uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel)
133         if (timer_num >= NUM_TIMERS)
134                 return 0;
135         /* FIXME */
136         return 0;
138 uint32_t timer_get_counter_val(uint32_t timer_num)
140         if (timer_num >= NUM_TIMERS)
141                 return 0;
142         return timer_devices[timer_num].regs->timer_counter;
145 /* Change the match value of a single timer channel */
146 void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val)
148         if (timer_num >= NUM_TIMERS)
149                 return;
150         if (channel > 2)
151                 return;
153         timer_devices[timer_num].regs->match_reg[channel] = val;
157 /***************************************************************************** */
158 /*   Timer Setup */
159 /* Returns 0 on success
160  * Takes a timer number and a timer config structure as arguments.
161  * Refer to timer config structure for details.
162  */
163 int timer_setup(uint32_t timer_num, struct timer_config* conf)
165         struct timer_device* timer = NULL;
166         int i = 0;
167         if (timer_num >= NUM_TIMERS)
168                 return -ENODEV;
169         timer = &(timer_devices[timer_num]);
171         /* Configure the reset on capture functionality */
172         if (conf->reset_on_capture != 0x00) {
173                 timer->regs->count_ctrl = LPC_COUNTER_CLEAR_ON_EVENT_EN;
174                 timer->regs->count_ctrl |= ((conf->reset_on_capture & 0x07) << LPC_COUNTER_CLEAR_ON_EVENT_SHIFT);
175         }
177         switch (conf->mode) {
178                 case LPC_TIMER_MODE_TIMER:
179                         timer->regs->capture_ctrl = 0; /* Timer mode ! */
180                         timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER;
181                         break;
182                 case LPC_TIMER_MODE_COUNTER:
183                         if ((conf->config[0] & 0x03) == 0x00) {
184                                 return -EINVAL;
185                         }
186                         /* Must set capture chanel N config to 0b000 in capture control register,
187                          * (see remarks in user manual UM10441 page 268 section 14.7.11)
188                          * Use the LPC_COUNTER_INC_INPUT(x) set by the user to do so automatically
189                          */
190                         timer->regs->capture_ctrl &= ~LPC_TIMER_CAPTURE_ERASE(((conf->config[0] >> LPC_COUNTER_INC_INPUT_SHIFT) & 0x03) * 3);
191                         /* Configure the counter */
192                         timer->regs->count_ctrl |= (conf->config[0] & 0x0F);
193                         break;
194                 case LPC_TIMER_MODE_CAPTURE:
195                         timer->regs->capture_ctrl = 0;
196                         for (i = 0; i < NUM_CHANS; i++) {
197                                 timer->regs->capture_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_CAPTURE_SHIFT(i));
198                         }
199                         break;
200                 case LPC_TIMER_MODE_MATCH:
201                         timer->regs->match_ctrl = 0;
202                         timer->regs->external_match = 0;
203                         for (i = 0; i < NUM_CHANS; i++) {
204                                 timer->regs->match_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_MATCH_SHIFT(i));
205                                 timer->regs->match_reg[i] = conf->match[i];
206                                 timer->regs->external_match |= ((conf->ext_match_config[i] & 0x03) << (LPC_TIMER_EXT_MATCH0_SHIFT + i*2));
207                         }
208                         break;
209                 case LPC_TIMER_MODE_PWM:
210                         if (timer_num != LPC_TIMER_PWM) {
211                                 return -EINVAL;
212                         }
213                         if (conf->match[ conf->config[1] ] != 0) {
214                                 return -EINVAL; /* Force use of Channel 0 for PWM cycle length */
215                         }
216                         /* Activate selected PWM channels */
217                         /* FIXME check config for PWM (see EXTRA_PWM_NUM_CHANS in drivers/timer.h) */
218                         timer->regs->pwm_ctrl = (conf->config[0] & 0x07);
219                         /* Use Channel 3 for PWM cycle length as recommended in the manual */
220                         timer->regs->match_ctrl &= ~(LPC_TIMER_MATCH_ERASE(conf->config[1]));
221                         timer->regs->match_ctrl |= (LPC_TIMER_RESET_ON_MATCH << LPC_TIMER_MATCH_SHIFT(conf->config[1]));
222                         for (i = 0; i < NUM_CHANS; i++) {
223                                 timer->regs->match_reg[i] = conf->match[i];
224                         }
225                         break;
226                 case LPC_TIMER_MODE_PWD:
227                         break;
228         }
229         return 0; /* Config OK */
233 /* Power up a timer.
234  * Note that clkrate should be a divider of the main clock frequency chosed
235  *   for your application as it will be used to divide the main clock to get
236  *   the prescaler value.
237  * Set clkrate to 0 to disable the prescaler.
238  */
239 void timer_on(uint32_t timer_num, uint32_t clkrate, void (*callback)(uint8_t))
241         struct timer_device* timer = NULL;
242         uint32_t prescale; /* The clock divider for the counter */
244         if (timer_num >= NUM_TIMERS)
245                 return;
246         timer = &(timer_devices[timer_num]);
248         NVIC_DisableIRQ( timer->irq );
249         /* Power up the timer */
250         subsystem_power(timer->power_bit, 1);
251         /* Set the timer pclk divider */
252         set_subsystem_clk_divider(timer->pclk_bit, LPC_PCLK_CCLK);
254         /* Reset counter on next PCLK positive edge, and disable counter */
255         timer->regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
257         /* Store the callback, OK even if none given */
258         timer->callback = callback;
260         /* Set the prescaler value */
261         if (clkrate == 0) {
262                 prescale = 0;
263         } else {
264                 prescale = (get_main_clock() / clkrate) - 1;
265         }
266         timer->regs->prescale = prescale;
268         NVIC_EnableIRQ(timer_devices[timer_num].irq);
271 /* Removes the main clock from the selected timer block */
272 void timer_off(uint32_t timer_num)
274         if (timer_num >= NUM_TIMERS)
275                 return;
276         NVIC_DisableIRQ( timer_devices[timer_num].irq );
277         subsystem_power(timer_devices[timer_num].power_bit, 0);