diff --git a/src/arch/xtensa/include/arch/timer.h b/src/arch/xtensa/include/arch/timer.h index 3262ac044..e36df6ba1 100644 --- a/src/arch/xtensa/include/arch/timer.h +++ b/src/arch/xtensa/include/arch/timer.h @@ -39,12 +39,28 @@ struct timer { uint32_t id; uint32_t irq; + void *timer_data; /* used by core */ + uint32_t hitime; /* high end of 64bit timer */ + uint32_t hitimeout; + uint32_t lowtimeout; }; +/* internal API calls */ +int timer64_register(struct timer *timer, void(*handler)(void *arg), void *arg); +void timer_64_handler(void *arg); + static inline int arch_timer_register(struct timer *timer, void(*handler)(void *arg), void *arg) { - return arch_interrupt_register(timer->id, handler, arg); + uint32_t flags; + int ret; + + flags = arch_interrupt_global_disable(); + timer64_register(timer, handler, arg); + ret = arch_interrupt_register(timer->id, timer_64_handler, timer); + arch_interrupt_global_enable(flags); + + return ret; } static inline void arch_timer_unregister(struct timer *timer) @@ -62,12 +78,9 @@ static inline void arch_timer_disable(struct timer *timer) arch_interrupt_disable_mask(1 << timer->irq); } -static inline uint32_t arch_timer_get_system(struct timer *timer) -{ - return xthal_get_ccount(); -} +uint64_t arch_timer_get_system(struct timer *timer); -void arch_timer_set(struct timer *timer, unsigned int ticks); +int arch_timer_set(struct timer *timer, uint64_t ticks); static inline void arch_timer_clear(struct timer *timer) { diff --git a/src/arch/xtensa/timer.c b/src/arch/xtensa/timer.c index 9395ca9c7..82c248126 100644 --- a/src/arch/xtensa/timer.c +++ b/src/arch/xtensa/timer.c @@ -41,19 +41,174 @@ #include #include -void arch_timer_set(struct timer *timer, unsigned int ticks) +struct timer_data { + void (*handler2)(void *arg); + void *arg2; +}; + +static struct timer_data xtimer[3] = {}; + +void timer_64_handler(void *arg) { + struct timer *timer = arg; + struct timer_data *tdata = timer->timer_data; + uint32_t ccompare; + + /* get comparator value - will tell us timeout reason */ switch (timer->id) { case TIMER0: - xthal_set_ccompare(0, ticks); + ccompare = xthal_get_ccompare(0); break; case TIMER1: - xthal_set_ccompare(1, ticks); + ccompare = xthal_get_ccompare(1); break; case TIMER2: - xthal_set_ccompare(2, ticks); + ccompare = xthal_get_ccompare(2); break; default: + return; + } + + /* is this a 32 bit rollover ? */ + if (ccompare == 1) { + /* roll over the timer */ + timer->hitime++; + arch_timer_clear(timer); + } else { + /* no roll over, run the handler */ + tdata->handler2(tdata->arg2); + } + + /* get next timeout value */ + if (timer->hitimeout > 0 && timer->hitimeout == timer->hitime) { + /* timeout is in this 32 bit period */ + ccompare = timer->lowtimeout; + } else { + /* timeout is in another 32 bit period */ + ccompare = 1; + } + + switch (timer->id) { + case TIMER0: + xthal_set_ccompare(0, ccompare); break; + case TIMER1: + xthal_set_ccompare(1, ccompare); + break; + case TIMER2: + xthal_set_ccompare(2, ccompare); + break; + default: + return; } } + +int timer64_register(struct timer *timer, void(*handler)(void *arg), void *arg) +{ + struct timer_data *tdata; + + switch (timer->id) { + case TIMER0: + tdata = &xtimer[0]; + break; + case TIMER1: + tdata = &xtimer[1]; + break; + case TIMER2: + tdata = &xtimer[2]; + break; + default: + return -EINVAL; + } + + tdata->handler2 = handler; + tdata->arg2 = arg; + timer->timer_data = tdata; + timer->hitime = 0; + timer->hitimeout = 0; + return 0; +} + +uint64_t arch_timer_get_system(struct timer *timer) +{ + uint64_t time; + uint32_t flags, low, high, ccompare; + + switch (timer->id) { + case TIMER0: + ccompare = xthal_get_ccompare(0); + break; + case TIMER1: + ccompare = xthal_get_ccompare(1); + break; + case TIMER2: + ccompare = xthal_get_ccompare(2); + break; + default: + return 0; + } + + flags = arch_interrupt_global_disable(); + + /* read low 32 bits */ + low = xthal_get_ccount(); + + /* check and see whether 32bit IRQ is pending for timer */ + if (arch_interrupt_get_status() & (1 << timer->irq) && ccompare == 1) { + /* yes, overflow has occured but handler has not run */ + high = timer->hitime + 1; + } else { + /* no overflow */ + high = timer->hitime; + } + + time = ((uint64_t)high << 32) | low; + + arch_interrupt_global_enable(flags); + + return time; +} + +int arch_timer_set(struct timer *timer, uint64_t ticks) +{ + uint32_t time = 1, hitimeout = ticks >> 32, flags; + + /* value of 1 represents rollover */ + if ((ticks & 0xffffffff) == 0x1) + ticks++; + + flags = arch_interrupt_global_disable(); + + /* same hi 64 bit context as ticks ? */ + if (hitimeout == timer->hitime) { + /* yes, then set the value for next timeout */ + time = ticks; + timer->lowtimeout = 0; + timer->hitimeout = 0; + } else if (hitimeout < timer->hitime) { + /* cant be in the past */ + arch_interrupt_global_enable(flags); + return -EINVAL; + } else { + /* set for checking at next timeout */ + timer->hitimeout = hitimeout; + timer->lowtimeout = ticks; + } + + switch (timer->id) { + case TIMER0: + xthal_set_ccompare(0, time); + break; + case TIMER1: + xthal_set_ccompare(1, time); + break; + case TIMER2: + xthal_set_ccompare(2, time); + break; + default: + return -EINVAL; + } + + arch_interrupt_global_enable(flags); + return 0; +} diff --git a/src/include/reef/timer.h b/src/include/reef/timer.h index d10d5e272..613cb8b52 100644 --- a/src/include/reef/timer.h +++ b/src/include/reef/timer.h @@ -34,11 +34,8 @@ #include #include -static inline int timer_register(struct timer *timer, - void(*handler)(void *arg), void *arg) -{ - return arch_timer_register(timer, handler, arg); -} +int timer_register(struct timer *timer, + void(*handler)(void *arg), void *arg); static inline void timer_unregister(struct timer *timer) { @@ -55,9 +52,9 @@ static inline void timer_disable(struct timer *timer) arch_timer_disable(timer); } -static inline void timer_set(struct timer *timer, unsigned int ticks) +static inline int timer_set(struct timer *timer, uint64_t ticks) { - arch_timer_set(timer, ticks); + return arch_timer_set(timer, ticks); } void timer_set_ms(struct timer *timer, unsigned int ms); @@ -71,7 +68,7 @@ unsigned int timer_get_count(struct timer *timer); unsigned int timer_get_count_delta(struct timer *timer); -static inline uint32_t timer_get_system(struct timer *timer) +static inline uint64_t timer_get_system(struct timer *timer) { return arch_timer_get_system(timer); } diff --git a/src/include/reef/work.h b/src/include/reef/work.h index 6428301ae..199e46cbe 100644 --- a/src/include/reef/work.h +++ b/src/include/reef/work.h @@ -57,9 +57,9 @@ struct work_queue_timesource { struct timer timer; int clk; int notifier; - void (*timer_set)(struct timer *, uint32_t ticks); + int (*timer_set)(struct timer *, uint64_t ticks); void (*timer_clear)(struct timer *); - uint32_t (*timer_get)(struct timer *); + uint64_t (*timer_get)(struct timer *); }; /* initialise our work */ @@ -69,11 +69,11 @@ struct work_queue_timesource { (w)->flags = xflags; /* schedule/cancel work on work queue */ -void work_schedule(struct work_queue *queue, struct work *w, uint32_t timeout); +void work_schedule(struct work_queue *queue, struct work *w, uint64_t timeout); void work_cancel(struct work_queue *queue, struct work *work); /* schedule/cancel work on default system work queue */ -void work_schedule_default(struct work *work, uint32_t timeout); +void work_schedule_default(struct work *work, uint64_t timeout); void work_cancel_default(struct work *work); /* create new work queue */ diff --git a/src/lib/work.c b/src/lib/work.c index 3172710f8..cac3deb48 100644 --- a/src/lib/work.c +++ b/src/lib/work.c @@ -60,22 +60,26 @@ struct work_queue { struct list_item work; /* list of work */ - uint32_t timeout; /* timeout for next queue run */ + uint64_t timeout; /* timeout for next queue run */ uint32_t window_size; /* window size for pending work */ spinlock_t lock; struct notifier notifier; /* notify CPU freq changes */ struct work_queue_timesource *ts; /* time source for work queue */ uint32_t ticks_per_usec; /* ticks per msec */ - uint32_t run_ticks; /* ticks when last run */ + uint64_t run_ticks; /* ticks when last run */ }; /* generic system work queue */ static struct work_queue *queue_; -static inline void work_set_timer(struct work_queue *queue, uint32_t ticks) +static inline int work_set_timer(struct work_queue *queue, uint64_t ticks) { - queue->ts->timer_set(&queue->ts->timer, ticks); + int ret; + + ret = queue->ts->timer_set(&queue->ts->timer, ticks); timer_enable(&queue->ts->timer); + + return ret; } static inline void work_clear_timer(struct work_queue *queue) @@ -84,7 +88,7 @@ static inline void work_clear_timer(struct work_queue *queue) timer_disable(&queue->ts->timer); } -static inline uint32_t work_get_timer(struct work_queue *queue) +static inline uint64_t work_get_timer(struct work_queue *queue) { return queue->ts->timer_get(&queue->ts->timer); } @@ -317,7 +321,7 @@ static void work_notify(int message, void *data, void *event_data) spin_unlock_irq(&queue->lock, flags); } -void work_schedule(struct work_queue *queue, struct work *w, uint32_t timeout) +void work_schedule(struct work_queue *queue, struct work *w, uint64_t timeout) { struct work *work; struct list_item *wlist; @@ -362,7 +366,7 @@ void work_cancel(struct work_queue *queue, struct work *w) spin_unlock_irq(&queue->lock, flags); } -void work_schedule_default(struct work *w, uint32_t timeout) +void work_schedule_default(struct work *w, uint64_t timeout) { struct work *work; struct list_item *wlist; diff --git a/src/platform/baytrail/include/platform/timer.h b/src/platform/baytrail/include/platform/timer.h index a7d2fc81f..8f0aea451 100644 --- a/src/platform/baytrail/include/platform/timer.h +++ b/src/platform/baytrail/include/platform/timer.h @@ -50,9 +50,10 @@ struct comp_dev; struct sof_ipc_stream_posn; extern struct timer *platform_timer; -void platform_timer_set(struct timer *timer, uint32_t ticks); + +int platform_timer_set(struct timer *timer, uint64_t ticks); void platform_timer_clear(struct timer *timer); -uint32_t platform_timer_get(struct timer *timer); +uint64_t platform_timer_get(struct timer *timer); void platform_timer_start(struct timer *timer); void platform_timer_stop(struct timer *timer); diff --git a/src/platform/baytrail/platform.c b/src/platform/baytrail/platform.c index f97971a99..ce8a68940 100644 --- a/src/platform/baytrail/platform.c +++ b/src/platform/baytrail/platform.c @@ -172,15 +172,15 @@ int platform_init(struct reef *reef) platform_ipc_pmc_init(); /* init work queues and clocks */ + trace_point(TRACE_BOOT_SYS_WORK); + init_system_workq(&platform_generic_queue); + trace_point(TRACE_BOOT_PLATFORM_TIMER); platform_timer_start(platform_timer); trace_point(TRACE_BOOT_PLATFORM_CLOCK); init_platform_clocks(); - trace_point(TRACE_BOOT_SYS_WORK); - init_system_workq(&platform_generic_queue); - /* Set CPU to default frequency for booting */ trace_point(TRACE_BOOT_SYS_CPU_FREQ); clock_set_freq(CLK_CPU, CLK_MAX_CPU_HZ); diff --git a/src/platform/baytrail/timer.c b/src/platform/baytrail/timer.c index 3d08f6f85..17c1f0b78 100644 --- a/src/platform/baytrail/timer.c +++ b/src/platform/baytrail/timer.c @@ -32,15 +32,58 @@ #include #include +#include #include #include #include +struct timer_data { + void (*handler2)(void *arg); + void *arg2; +}; + +static struct timer_data xtimer[1] = {}; + +void platform_timer_64_handler(void *arg) +{ + struct timer *timer = arg; + struct timer_data *tdata = timer->timer_data; + uint32_t timeout; + + /* get timeout value - will tell us timeout reason */ + timeout = shim_read(SHIM_EXT_TIMER_CNTLL); + + /* we dont use the timer clear bit as we only need to clear the ISR */ + shim_write(SHIM_PISR, SHIM_PISR_EXT_TIMER); + + /* is this a 32 bit rollover ? */ + if (timeout == 1) { + /* roll over the timer */ + timer->hitime++; + } else { + /* no roll over, run the handler */ + tdata->handler2(tdata->arg2); + } + + /* get next timeout value */ + if (timer->hitimeout > 0 && timer->hitimeout == timer->hitime) { + /* timeout is in this 32 bit period */ + timeout = timer->lowtimeout; + } else { + /* timeout is in another 32 bit period */ + timeout = 1; + } + + /* set new value and run */ + shim_write(SHIM_EXT_TIMER_CNTLH, SHIM_EXT_TIMER_RUN); + shim_write(SHIM_EXT_TIMER_CNTLL, timeout); +} + void platform_timer_start(struct timer *timer) { /* run timer */ shim_write(SHIM_EXT_TIMER_CNTLH, SHIM_EXT_TIMER_RUN); - shim_write(SHIM_EXT_TIMER_CNTLL, 0); + shim_write(SHIM_EXT_TIMER_CNTLL, 1); } /* this seems to stop rebooting with RTD3 ???? */ @@ -51,15 +94,40 @@ void platform_timer_stop(struct timer *timer) shim_write(SHIM_EXT_TIMER_CNTLH, SHIM_EXT_TIMER_CLEAR); } -void platform_timer_set(struct timer *timer, uint32_t ticks) +int platform_timer_set(struct timer *timer, uint64_t ticks) { + uint32_t time = 1, hitimeout = ticks >> 32, flags; + /* a tick value of 0 will not generate an IRQ */ - if (ticks == 0) - ticks = 1; + /* value of 1 represents rollover */ + if ((ticks & 0xffffffff) < 0x2) + ticks += 2; + + flags = arch_interrupt_global_disable(); + + /* same hi 64 bit context as ticks ? */ + if (hitimeout == timer->hitime) { + /* yes, then set the value for next timeout */ + time = ticks; + timer->lowtimeout = 0; + timer->hitimeout = 0; + } else if (hitimeout < timer->hitime) { + /* cant be in the past */ + arch_interrupt_global_enable(flags); + return -EINVAL; + } else { + /* set for checking at next timeout */ + timer->hitimeout = hitimeout; + timer->lowtimeout = ticks; + } /* set new value and run */ shim_write(SHIM_EXT_TIMER_CNTLH, SHIM_EXT_TIMER_RUN); - shim_write(SHIM_EXT_TIMER_CNTLL, ticks); + shim_write(SHIM_EXT_TIMER_CNTLL, time); + + arch_interrupt_global_enable(flags); + + return 0; } void platform_timer_clear(struct timer *timer) @@ -68,9 +136,31 @@ void platform_timer_clear(struct timer *timer) shim_write(SHIM_PISR, SHIM_PISR_EXT_TIMER); } -uint32_t platform_timer_get(struct timer *timer) +uint64_t platform_timer_get(struct timer *timer) { - return shim_read(SHIM_EXT_TIMER_STAT); + uint64_t time; + uint32_t flags, low, high; + + flags = arch_interrupt_global_disable(); + + /* read low 32 bits */ + low = shim_read(SHIM_EXT_TIMER_STAT); + + /* check and see whether 32bit IRQ is pending for timer */ + if (arch_interrupt_get_status() & IRQ_MASK_EXT_TIMER && + shim_read(SHIM_EXT_TIMER_CNTLL) == 1) { + /* yes, overflow has occured but handler has not run */ + high = timer->hitime + 1; + } else { + /* no overflow */ + high = timer->hitime; + } + + time = ((uint64_t)high << 32) | low; + + arch_interrupt_global_enable(flags); + + return time; } /* get timestamp for host stream DMA position */ @@ -107,3 +197,36 @@ void platform_dai_wallclock(struct comp_dev *dai, uint64_t *wallclock) /* only 1 wallclock on BYT */ *wallclock = shim_read(SHIM_EXT_TIMER_STAT); } + +static int platform_timer_register(struct timer *timer, + void(*handler)(void *arg), void *arg) +{ + struct timer_data *tdata = &xtimer[0]; + uint32_t flags; + int ret; + + flags = arch_interrupt_global_disable(); + tdata->handler2 = handler; + tdata->arg2 = arg; + timer->timer_data = tdata; + timer->hitime = 0; + timer->hitimeout = 0; + ret = arch_interrupt_register(timer->id, platform_timer_64_handler, timer); + arch_interrupt_global_enable(flags); + + return ret; +} + +int timer_register(struct timer *timer, void(*handler)(void *arg), void *arg) +{ + switch (timer->id) { + case TIMER0: + case TIMER1: + case TIMER2: + return arch_timer_register(timer, handler, arg); + case TIMER3: + return platform_timer_register(timer, handler, arg); + default: + return -EINVAL; + } +}