Initial commit, code based on LPC1224 support and LPC812-MAX CMSIS-DAP interface...
[lpc11u3x] / 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 #include <stdint.h>
28 #include "core/lpc_regs_11u3x.h"
29 #include "core/lpc_core_cm0.h"
30 #include "core/system.h"
31 #include "core/pio.h"
32 #include "lib/string.h"
33 #include "drivers/timers.h"
36 /* These are local to our file */
37 struct timer_device
38 {
39         struct lpc_timer* regs;
40         uint32_t power_bit;
41         uint32_t irq;
42         void (*callback)(uint32_t); /* Possible RX callback */
43 };
44 static struct timer_device timer_devices[NUM_TIMERS] = {
45         { LPC_TMR16B0, LPC_SYS_ABH_CLK_CTRL_CT16B0, TIMER0_IRQ },
46         { LPC_TMR16B1, LPC_SYS_ABH_CLK_CTRL_CT16B1, TIMER1_IRQ },
47         { LPC_TMR32B0, LPC_SYS_ABH_CLK_CTRL_CT32B0, TIMER2_IRQ },
48         { LPC_TMR32B1, LPC_SYS_ABH_CLK_CTRL_CT32B1, TIMER3_IRQ },
49 };
53 /* Handlers */
54 void TIMER_Handler(struct timer_device* timer)
55 {
56         uint32_t intr_flags = timer->regs->int_reg; /* Backup the flags */
58         /* Clear the interrupt */
59         timer->regs->int_reg = intr_flags;
60         /* And call the user routine if one has been registered */
61         if (timer->callback != NULL) {
62                 timer->callback(intr_flags);
63         }
64 }
65 void TIMER_0_Handler(void)
66 {
67         TIMER_Handler(&timer_devices[0]);
68 }
69 void TIMER_1_Handler(void)
70 {
71         TIMER_Handler(&timer_devices[1]);
72 }
73 void TIMER_2_Handler(void)
74 {
75         TIMER_Handler(&timer_devices[2]);
76 }
77 void TIMER_3_Handler(void)
78 {
79         TIMER_Handler(&timer_devices[3]);
80 }
84 /* Start the timer :
85  * Remove the reset flag if present and set timer enable flag.
86  * Timer must be turned on and configured (no checks done here).
87  */
88 void timer_start(uint32_t timer_num)
89 {
90         if (timer_num >= NUM_TIMERS)
91                 return;
92         /* Remove reset flag and set timer enable flag */
93         timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE;
94 }
95 void timer_continue(uint32_t timer_num) __attribute__ ((alias ("timer_start")));
96 /* Pause the timer counter, does not reset */
97 void timer_pause(uint32_t timer_num)
98 {
99         if (timer_num >= NUM_TIMERS)
100                 return;
101         /* Remove timer enable flag */
102         timer_devices[timer_num].regs->timer_ctrl = 0;
104 /* Stops and resets the timer counter */
105 void timer_stop(uint32_t timer_num)
107         if (timer_num >= NUM_TIMERS)
108                 return;
109         /* Remove timer enable flag and request reset */
110         timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
111         /* Remove reset flag */
112         timer_devices[timer_num].regs->timer_ctrl = 0;
114 /* Resets the timer and lets it count again imediately */
115 void timer_restart(uint32_t timer_num)
117         if (timer_num >= NUM_TIMERS)
118                 return;
119         /* Set timer reset flag */
120         timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
121         /* Remove reset flag and start counter */
122         timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE;
125 uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel)
127         if (timer_num >= NUM_TIMERS)
128                 return 0;
129         /* FIXME */
130         return 0;
132 uint32_t timer_get_counter_val(uint32_t timer_num)
134         if (timer_num >= NUM_TIMERS)
135                 return 0;
136         return timer_devices[timer_num].regs->timer_counter;
139 /* Change the match value of a single timer channel */
140 void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val)
142         if (timer_num >= NUM_TIMERS)
143                 return;
144         if (channel > 3)
145                 return;
147         timer_devices[timer_num].regs->match_reg[channel] = val;
151 /***************************************************************************** */
152 /*   Timer Setup */
153 /* Returns 0 on success
154  * Takes a timer number and a timer config structure as arguments.
155  * Refer to timer config structure for details.
156  */
157 int timer_setup(uint32_t timer_num, const struct timer_config* conf)
159         struct timer_device* timer = NULL;
160         int i = 0;
161         if (timer_num >= NUM_TIMERS)
162                 return -ENODEV;
163         timer = &(timer_devices[timer_num]);
165         /* Configure the reset on capture functionality */
166         if (conf->reset_on_capture != 0x00) {
167                 timer->regs->count_ctrl = LPC_COUNTER_CLEAR_ON_EVENT_EN;
168                 timer->regs->count_ctrl |= ((conf->reset_on_capture & 0x07) << LPC_COUNTER_CLEAR_ON_EVENT_SHIFT);
169         }
171         switch (conf->mode) {
172                 case LPC_TIMER_MODE_TIMER:
173                         timer->regs->capture_ctrl = 0; /* Timer mode ! */
174                         timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER;
175                         break;
176                 case LPC_TIMER_MODE_COUNTER:
177                         if ((conf->config[0] & 0x03) == 0x00) {
178                                 return -EINVAL;
179                         }
180                         /* Must set capture chanel N config to 0b000 in capture control register,
181                          * (see remarks in user manual UM10462 page 361 section 16.7.11)
182                          * Use the LPC_COUNTER_INC_INPUT(x) set by the user to do so automatically
183                          */
184                         timer->regs->capture_ctrl &= ~LPC_TIMER_CAPTURE_ERASE(((conf->config[0] >> LPC_COUNTER_INC_INPUT_SHIFT) & 0x03) * 3);
185                         /* Configure the counter */
186                         timer->regs->count_ctrl |= (conf->config[0] & 0x0F);
187                         break;
188                 case LPC_TIMER_MODE_CAPTURE:
189                         timer->regs->capture_ctrl = 0;
190                         for (i = 0; i < NUM_CHANS; i++) {
191                                 timer->regs->capture_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_CAPTURE_SHIFT(i));
192                         }
193                         break;
194                 case LPC_TIMER_MODE_MATCH:
195                         timer->regs->match_ctrl = 0;
196                         timer->regs->external_match = 0;
197                         for (i = 0; i < NUM_CHANS; i++) {
198                                 timer->regs->match_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_MATCH_SHIFT(i));
199                                 timer->regs->match_reg[i] = conf->match[i];
200                                 timer->regs->external_match |= ((conf->ext_match_config[i] & 0x03) << (LPC_TIMER_EXT_MATCH0_SHIFT + i*2));
201                         }
202                         break;
203                 case LPC_TIMER_MODE_PWM:
204                         if (conf->match[ conf->config[1] ] == 0) {
205                                 return -EINVAL; /* Force use of Channel 3 for PWM cycle length */
206                         }
207                         /* Activate selected PWM channels 0 to 2 */
208                         timer->regs->pwm_ctrl = (conf->config[0] & 0x07);
209                         /* Use Channel 3 for PWM cycle length as recommended in the manual */
210                         timer->regs->match_ctrl &= ~(LPC_TIMER_MATCH_ERASE(conf->config[1]));
211                         timer->regs->match_ctrl |= (LPC_TIMER_RESET_ON_MATCH << LPC_TIMER_MATCH_SHIFT(conf->config[1]));
212                         for (i = 0; i < NUM_CHANS; i++) {
213                                 timer->regs->match_reg[i] = conf->match[i];
214                         }
215                         break;
216                 case LPC_TIMER_MODE_PWD:
217                         break;
218         }
219         return 0; /* Config OK */
223 /* Power up a timer.
224  * Note that clkrate should be a divider of the main clock frequency chosed
225  *   for your application as it will be used to divide the main clock to get
226  *   the prescaler value.
227  * Set clkrate to 0 to disable the prescaler.
228  */
229 void timer_on(uint32_t timer_num, uint32_t clkrate, void (*callback)(uint32_t))
231         struct timer_device* timer = NULL;
232         uint32_t prescale; /* The clock divider for the counter */
234         if (timer_num >= NUM_TIMERS)
235                 return;
236         timer = &(timer_devices[timer_num]);
238         NVIC_DisableIRQ( timer->irq );
239         /* Power up the timer */
240         subsystem_power(timer->power_bit, 1);
241         /* Reset counter on next PCLK positive edge, and disable counter */
242         timer->regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
244         /* Store the callback, OK even if none given */
245         timer->callback = callback;
247         /* Set the prescaler value */
248         if (clkrate == 0) {
249                 prescale = 0;
250         } else {
251                 prescale = (get_main_clock() / clkrate) - 1;
252         }
253         timer->regs->prescale = prescale;
255         NVIC_EnableIRQ(timer_devices[timer_num].irq);
258 /* Removes the main clock from the selected timer block */
259 void timer_off(uint32_t timer_num)
261         if (timer_num >= NUM_TIMERS)
262                 return;
263         NVIC_DisableIRQ( timer_devices[timer_num].irq );
264         subsystem_power(timer_devices[timer_num].power_bit, 0);