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
00066
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
00101
00102
00103 tc_write_reg(timer->port, IDR, TC_SR_CPAS_MASK);
00104 }
00105
00106
00107
00108
00109
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
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
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
00142 if (!timer->is_running)
00143 return 0;
00144
00145
00146
00147
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
00179
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
00191 else if (timer_get_time(timer) > time_stamp) {
00192 timer->action_pending = true;
00193
00194
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
00273
00274
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
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
00297 if (timer->first_task == NULL) {
00298 timer_stop(timer);
00299 cpu_irq_restore(iflags);
00300 } else {
00301 cpu_irq_restore(iflags);
00302
00303
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
00336 assert(task->next == NULL);
00337
00338 task->timer = timer;
00339
00340 task->time_stamp = timer_get_time(timer) + delay;
00341
00342
00343 if (timer->first_task == NULL) {
00344 timer->first_task = task;
00345 task->next = NULL;
00346 goto out;
00347 }
00348
00349
00350
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
00359
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
00373 timer_set_alarm(timer, task->time_stamp);
00374 }