forked from thesofproject/sof
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtimer.c
More file actions
146 lines (114 loc) · 2.91 KB
/
timer.c
File metadata and controls
146 lines (114 loc) · 2.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// SPDX-License-Identifier: BSD-3-Clause
//
// Copyright(c) 2016 Intel Corporation. All rights reserved.
//
// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
#include <rtos/interrupt.h>
#include <rtos/timer.h>
#include <sof/lib/memory.h>
#include <xtensa/config/core-isa.h>
#include <xtensa/hal.h>
#include <errno.h>
#include <stdint.h>
void timer_64_handler(void *arg)
{
struct timer *timer = arg;
uint32_t ccompare;
if (timer->id >= ARCH_TIMER_COUNT)
return;
/* get comparator value - will tell us timeout reason */
ccompare = xthal_get_ccompare(timer->id);
/* 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 */
if (timer->handler)
timer->handler(timer->data);
}
/* get next timeout value */
if (timer->hitimeout == timer->hitime) {
/* timeout is in this 32 bit period */
ccompare = timer->lowtimeout;
} else {
/* timeout is in another 32 bit period */
ccompare = 1;
}
xthal_set_ccompare(timer->id, ccompare);
}
int timer64_register(struct timer *timer, void(*handler)(void *arg), void *arg)
{
if (timer->id >= ARCH_TIMER_COUNT)
return -EINVAL;
timer->handler = handler;
timer->data = arg;
timer->hitime = 0;
timer->hitimeout = 0;
return 0;
}
uint64_t arch_timer_get_system(struct timer *timer)
{
uint64_t time = 0;
uint32_t flags;
uint32_t low;
uint32_t high;
uint32_t ccompare;
if (timer->id >= ARCH_TIMER_COUNT)
goto out;
ccompare = xthal_get_ccompare(timer->id);
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);
out:
return time;
}
int64_t arch_timer_set(struct timer *timer, uint64_t ticks)
{
uint32_t time = 1;
uint32_t hitimeout = ticks >> 32;
uint32_t flags;
int64_t ret;
if (timer->id >= ARCH_TIMER_COUNT) {
ret = -EINVAL;
goto out;
}
/* 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) {
/* cant be in the past */
arch_interrupt_global_enable(flags);
ret = -EINVAL;
goto out;
}
/* check whether new timeout requires timer
* rollover. If so, ccompare value should
* be set to 1 in order to increment timer->hitime
* properly in timer_64_handler().
*/
if (timer->hitime < hitimeout)
time = 1;
else
time = ticks;
timer->hitimeout = hitimeout;
timer->lowtimeout = ticks;
xthal_set_ccompare(timer->id, time);
arch_interrupt_global_enable(flags);
ret = ticks;
out:
return ret;
}