- Main Page
- Related Pages
- Data Structures
- Files

- File List
- Globals

# drivers/timer/timer_tc.c

Go to the documentation of this file.

    00001
    00045 #include <debug.h>
    00046
    00047 #include <timer_tc.h>
    00048 #include <softirq.h>
    00049 #include <chip/clk.h>
    00050 #include <chip/irq-map.h>
    00051 #include <app/softirq.h>
    00052
    00053 #include "tc_regs.h"
    00054
    00056 #define COUNTER_LOW_MASK                ((1UL << 16)-1)
    00057
    00058 #define COUNTER_HIGH_OFFSET             16
    00059
    00060 static void timer_do_pending(void *timer);
    00061
    00062 counter_t timer_convert_us(struct timer *timer, uint32_t us)
    00063 {
    00064         /*
    00065          * Avoid 64-bit division. This might need tweaking when running
    00066          * from a slow clock.
    00067          */
    00068         if (us > 1000000) {
    00069                 us /= 1000;
    00070                 return (us * (timer_get_frequency(timer) / 1000));
    00071         } else {
    00072                 return (us * (timer_get_frequency(timer) / 1000)) / 1000;
    00073         }
    00074 }
    00075
    00091 static void timer_irq_handler(void *data)
    00092 {
    00093         struct timer *timer = (struct timer *)data;
    00094         unsigned long status = tc_read_reg(timer->port, SR);
    00095
    00096         if (status & TC_SR_COVFS_MASK) {
    00097                 timer->counter_high++;
    00098         }
    00099         if (status & TC_SR_CPAS_MASK) {
    00100                 /* Disable compare interrupt, if necessary this will
    00101                  * be renabled when the pending actions are done
    00102                  */
    00103                 tc_write_reg(timer->port, IDR, TC_SR_CPAS_MASK);
    00104         }
    00105
    00106         /*
    00107          * Whenever we get an alarm or overflow, we need to set up the next
    00108          * alarm; this is handled by setting the pending action flag and
    00109          * calling the do_pending function.
    00110          */
    00111         timer->action_pending = true;
    00112
    00113         softirq_raise(SOFTIRQ_ID_TIMER);
    00114 }
    00115
    00116 DEFINE_IRQ_HANDLER(timer_tick, timer_irq_handler, 0);
    00117
    00118 static void timer_start(struct timer *timer)
    00119 {
    00120         /* Reset and start timer */
    00121         tc_write_reg(timer->port, IER, TC_SR_COVFS_MASK);
    00122         tc_write_reg(timer->port, CCR, TC_CCR_CLKEN_MASK | TC_CCR_SWTRG_MASK);
    00123         timer->is_running = true;
    00124 }
    00125
    00126 static void timer_stop(struct timer *timer)
    00127 {
    00128         tc_write_reg(timer->port, CCR, TC_CCR_CLKDIS_MASK);
    00129         tc_write_reg(timer->port, IDR, TC_SR_COVFS_MASK | TC_SR_CPAS_MASK);
    00130         timer->is_running = false;
    00131
    00132         /* Reset counter */
    00133         timer->counter_high = 0;
    00134 }
    00135
    00136 static counter_t timer_get_time(struct timer *timer)
    00137 {
    00138         uint16_t counter_low;
    00139         counter_high_t counter_high_first, counter_high_second;
    00140
    00141         /* A stopped timer will always restart from 0 */
    00142         if (!timer->is_running)
    00143                 return 0;
    00144
    00145         /* Read high and low counter; ensure consistence by re-reading if
    00146          * high value changes during operation. Since we can not stop the
    00147          * hardware timer, disabling interrupt here would not help us
    00148          */
    00149         do {
    00150                 counter_high_first = timer->counter_high;
    00151                 counter_low = tc_read_reg(timer->port, CV);
    00152                 barrier();
    00153                 counter_high_second = timer->counter_high;
    00154         } while (counter_high_second != counter_high_first);
    00155
    00156         return (counter_t)counter_high_second << COUNTER_HIGH_OFFSET |
    00157                         counter_low;
    00158 }
    00159
    00174 static void timer_set_alarm(struct timer *timer, counter_t time_stamp)
    00175 {
    00176         uint16_t counter_low;
    00177
    00178         /* Check if alarm time is before next overflow, if it is not
    00179          * we will not need to handle it yet
    00180          */
    00181         if ((time_stamp >> COUNTER_HIGH_OFFSET) <
    00182                         (counter_t)(timer->counter_high + 1)) {
    00183                 counter_low = time_stamp & COUNTER_LOW_MASK;
    00184                 tc_write_reg(timer->port, RA, counter_low);
    00185                 tc_write_reg(timer->port, IER, TC_SR_CPAS_MASK);
    00186         }
    00187
    00188         if (!timer->is_running)
    00189                 timer_start(timer);
    00190         /* Make sure we did not miss the deadline while setting alarm */
    00191         else if (timer_get_time(timer) > time_stamp) {
    00192                 timer->action_pending = true;
    00193                 /* TODO: Utilize soft IRQ or workqueue here in order to avoid
    00194                  * nested timer_do_pending() calls.
    00195                  */
    00196                 timer_do_pending(timer);
    00197         }
    00198 }
    00199
    00216 void timer_init(struct timer *timer, void *port, uint32_t resolution)
    00217 {
    00218         unsigned long clock_rate = get_pba_clock_rate();
    00219         uint8_t tcclks = 0;
    00220
    00221         build_assert(sizeof(counter_t) ==
    00222                         (sizeof(uint16_t) + sizeof(counter_high_t)));
    00223         assert(timer);
    00224         assert(port);
    00225
    00226         timer->port = port;
    00227         timer->counter_high = 0;
    00228         timer->action_pending = false;
    00229         timer->first_task = NULL;
    00230         timer->is_running = false;
    00231
    00232         setup_irq_handler(TC0_IRQ, timer_tick, 0, timer);
    00233         softirq_set_handler(SOFTIRQ_ID_TIMER, timer_do_pending, timer);
    00234
    00235         if (clock_rate >= resolution * 128) {
    00236                 tcclks = 4;
    00237                 timer->frequency = clock_rate / 128;
    00238         } else if (clock_rate >= resolution * 32) {
    00239                 tcclks = 3;
    00240                 timer->frequency = clock_rate / 32;
    00241         } else if (clock_rate >= resolution * 8) {
    00242                 tcclks = 2;
    00243                 timer->frequency = clock_rate / 8;
    00244         } else {
    00245                 tcclks = 1;
    00246                 timer->frequency = clock_rate / 2;
    00247         }
    00248
    00249         tc_write_reg(timer->port, CMR, TC_CMR_WAVE_MASK | tcclks);
    00250 }
    00251
    00261 static void timer_do_pending(void *context)
    00262 {
    00263         struct timer *timer = context;
    00264         counter_t current_time;
    00265         struct timer_task *task;
    00266         unsigned long iflags;
    00267
    00268         assert(timer);
    00269
    00270         while (timer->action_pending)
    00271         {
    00272                 /* Save time by only reading out this once, if we get another
    00273                  * timeout while executing tasks, the pending action flag will
    00274                  * be raised again.
    00275                  */
    00276                 current_time = timer_get_time(timer);
    00277                 while (timer->first_task)
    00278                 {
    00279                         if (timer->first_task->time_stamp <= current_time) {
    00280                                 iflags = cpu_irq_save();
    00281                                 task = timer->first_task;
    00282                                 timer->first_task = timer->first_task->next;
    00283                                 cpu_irq_restore(iflags);
    00284
    00285                                 task->next = NULL;
    00286                                 /* Run task handler with interrupts enabled */
    00287                                 task->callback(timer_task_get_data(task));
    00288                         } else {
    00289                                 break;
    00290                         }
    00291                 }
    00292
    00293                 iflags = cpu_irq_save();
    00294                 timer->action_pending = false;
    00295
    00296                 /* Stop timer if queue is empty */
    00297                 if (timer->first_task == NULL) {
    00298                         timer_stop(timer);
    00299                         cpu_irq_restore(iflags);
    00300                 } else {
    00301                         cpu_irq_restore(iflags);
    00302                         /* Set next alarm, this will set pending action flag if
    00303                          * timestamp is exceeded.
    00304                          */
    00305                         timer_set_alarm(timer, timer->first_task->time_stamp);
    00306                 }
    00307         }
    00308 }
    00309
    00323 void timer_add_task_ticks(
    00324                 struct timer *timer,
    00325                 struct timer_task *task,
    00326                 counter_t delay)
    00327 {
    00328         struct timer_task *temp_task, *prev_task;
    00329
    00330         assert(timer);
    00331         assert(task);
    00332         assert(task->callback);
    00333         assert(delay < TIMER_MAX_DELAY);
    00334
    00335         /* A initialized task should have cleared next pointer */
    00336         assert(task->next == NULL);
    00337
    00338         task->timer = timer;
    00339         /* Calculate the absolute time_stamp of the task */
    00340         task->time_stamp = timer_get_time(timer) + delay;
    00341
    00342         /* Special handling if list is empty */
    00343         if (timer->first_task == NULL) {
    00344                 timer->first_task = task;
    00345                 task->next = NULL;
    00346                 goto out;
    00347         }
    00348
    00349         /* Same for lists with only one entry
    00350          * (this should be optimized when the list is replaced)
    00351          */
    00352         if (task->time_stamp < timer->first_task->time_stamp) {
    00353                 task->next = timer->first_task;
    00354                 timer->first_task = task;
    00355                 goto out;
    00356         }
    00357
    00358         /* Search through list and insert task */
    00359         /* TODO: Optimize this */
    00360         prev_task = timer->first_task;
    00361         temp_task = prev_task->next;
    00362         while (temp_task != NULL) {
    00363                 if (task->time_stamp < temp_task->time_stamp)
    00364                         break;
    00365                 prev_task = temp_task;
    00366                 temp_task = temp_task->next;
    00367         }
    00368         task->next = temp_task;
    00369         prev_task->next = task;
    00370
    00371 out:
    00372         /* Set alarm -- will also handle already expired tasks */
    00373         timer_set_alarm(timer, task->time_stamp);
    00374 }