root / hw / timer / cadence_ttc.c @ f487b677
History | View | Annotate | Download (12.2 kB)
1 | f3a6cc07 | Peter A. G. Crosthwaite | /*
|
---|---|---|---|
2 | f3a6cc07 | Peter A. G. Crosthwaite | * Xilinx Zynq cadence TTC model
|
3 | f3a6cc07 | Peter A. G. Crosthwaite | *
|
4 | f3a6cc07 | Peter A. G. Crosthwaite | * Copyright (c) 2011 Xilinx Inc.
|
5 | f3a6cc07 | Peter A. G. Crosthwaite | * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com)
|
6 | f3a6cc07 | Peter A. G. Crosthwaite | * Copyright (c) 2012 PetaLogix Pty Ltd.
|
7 | f3a6cc07 | Peter A. G. Crosthwaite | * Written By Haibing Ma
|
8 | f3a6cc07 | Peter A. G. Crosthwaite | * M. Habib
|
9 | f3a6cc07 | Peter A. G. Crosthwaite | *
|
10 | f3a6cc07 | Peter A. G. Crosthwaite | * This program is free software; you can redistribute it and/or
|
11 | f3a6cc07 | Peter A. G. Crosthwaite | * modify it under the terms of the GNU General Public License
|
12 | f3a6cc07 | Peter A. G. Crosthwaite | * as published by the Free Software Foundation; either version
|
13 | f3a6cc07 | Peter A. G. Crosthwaite | * 2 of the License, or (at your option) any later version.
|
14 | f3a6cc07 | Peter A. G. Crosthwaite | *
|
15 | f3a6cc07 | Peter A. G. Crosthwaite | * You should have received a copy of the GNU General Public License along
|
16 | f3a6cc07 | Peter A. G. Crosthwaite | * with this program; if not, see <http://www.gnu.org/licenses/>.
|
17 | f3a6cc07 | Peter A. G. Crosthwaite | */
|
18 | f3a6cc07 | Peter A. G. Crosthwaite | |
19 | 83c9f4ca | Paolo Bonzini | #include "hw/sysbus.h" |
20 | 1de7afc9 | Paolo Bonzini | #include "qemu/timer.h" |
21 | f3a6cc07 | Peter A. G. Crosthwaite | |
22 | f3a6cc07 | Peter A. G. Crosthwaite | #ifdef CADENCE_TTC_ERR_DEBUG
|
23 | f3a6cc07 | Peter A. G. Crosthwaite | #define DB_PRINT(...) do { \ |
24 | f3a6cc07 | Peter A. G. Crosthwaite | fprintf(stderr, ": %s: ", __func__); \
|
25 | f3a6cc07 | Peter A. G. Crosthwaite | fprintf(stderr, ## __VA_ARGS__); \ |
26 | f3a6cc07 | Peter A. G. Crosthwaite | } while (0); |
27 | f3a6cc07 | Peter A. G. Crosthwaite | #else
|
28 | f3a6cc07 | Peter A. G. Crosthwaite | #define DB_PRINT(...)
|
29 | f3a6cc07 | Peter A. G. Crosthwaite | #endif
|
30 | f3a6cc07 | Peter A. G. Crosthwaite | |
31 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_INTR_IV 0x00000001 |
32 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_INTR_M1 0x00000002 |
33 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_INTR_M2 0x00000004 |
34 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_INTR_M3 0x00000008 |
35 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_INTR_OV 0x00000010 |
36 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_INTR_EV 0x00000020 |
37 | f3a6cc07 | Peter A. G. Crosthwaite | |
38 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_CTRL_DIS 0x00000001 |
39 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_CTRL_INT 0x00000002 |
40 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_CTRL_DEC 0x00000004 |
41 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_CTRL_MATCH 0x00000008 |
42 | f3a6cc07 | Peter A. G. Crosthwaite | #define COUNTER_CTRL_RST 0x00000010 |
43 | f3a6cc07 | Peter A. G. Crosthwaite | |
44 | f3a6cc07 | Peter A. G. Crosthwaite | #define CLOCK_CTRL_PS_EN 0x00000001 |
45 | f3a6cc07 | Peter A. G. Crosthwaite | #define CLOCK_CTRL_PS_V 0x0000001e |
46 | f3a6cc07 | Peter A. G. Crosthwaite | |
47 | f3a6cc07 | Peter A. G. Crosthwaite | typedef struct { |
48 | f3a6cc07 | Peter A. G. Crosthwaite | QEMUTimer *timer; |
49 | f3a6cc07 | Peter A. G. Crosthwaite | int freq;
|
50 | f3a6cc07 | Peter A. G. Crosthwaite | |
51 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t reg_clock; |
52 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t reg_count; |
53 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t reg_value; |
54 | f3a6cc07 | Peter A. G. Crosthwaite | uint16_t reg_interval; |
55 | f3a6cc07 | Peter A. G. Crosthwaite | uint16_t reg_match[3];
|
56 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t reg_intr; |
57 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t reg_intr_en; |
58 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t reg_event_ctrl; |
59 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t reg_event; |
60 | f3a6cc07 | Peter A. G. Crosthwaite | |
61 | f3a6cc07 | Peter A. G. Crosthwaite | uint64_t cpu_time; |
62 | f3a6cc07 | Peter A. G. Crosthwaite | unsigned int cpu_time_valid; |
63 | f3a6cc07 | Peter A. G. Crosthwaite | |
64 | f3a6cc07 | Peter A. G. Crosthwaite | qemu_irq irq; |
65 | f3a6cc07 | Peter A. G. Crosthwaite | } CadenceTimerState; |
66 | f3a6cc07 | Peter A. G. Crosthwaite | |
67 | f3a6cc07 | Peter A. G. Crosthwaite | typedef struct { |
68 | f3a6cc07 | Peter A. G. Crosthwaite | SysBusDevice busdev; |
69 | f3a6cc07 | Peter A. G. Crosthwaite | MemoryRegion iomem; |
70 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTimerState timer[3];
|
71 | f3a6cc07 | Peter A. G. Crosthwaite | } CadenceTTCState; |
72 | f3a6cc07 | Peter A. G. Crosthwaite | |
73 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_timer_update(CadenceTimerState *s) |
74 | f3a6cc07 | Peter A. G. Crosthwaite | { |
75 | f3a6cc07 | Peter A. G. Crosthwaite | qemu_set_irq(s->irq, !!(s->reg_intr & s->reg_intr_en)); |
76 | f3a6cc07 | Peter A. G. Crosthwaite | } |
77 | f3a6cc07 | Peter A. G. Crosthwaite | |
78 | f3a6cc07 | Peter A. G. Crosthwaite | static CadenceTimerState *cadence_timer_from_addr(void *opaque, |
79 | a8170e5e | Avi Kivity | hwaddr offset) |
80 | f3a6cc07 | Peter A. G. Crosthwaite | { |
81 | f3a6cc07 | Peter A. G. Crosthwaite | unsigned int index; |
82 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTTCState *s = (CadenceTTCState *)opaque; |
83 | f3a6cc07 | Peter A. G. Crosthwaite | |
84 | f3a6cc07 | Peter A. G. Crosthwaite | index = (offset >> 2) % 3; |
85 | f3a6cc07 | Peter A. G. Crosthwaite | |
86 | f3a6cc07 | Peter A. G. Crosthwaite | return &s->timer[index];
|
87 | f3a6cc07 | Peter A. G. Crosthwaite | } |
88 | f3a6cc07 | Peter A. G. Crosthwaite | |
89 | f3a6cc07 | Peter A. G. Crosthwaite | static uint64_t cadence_timer_get_ns(CadenceTimerState *s, uint64_t timer_steps)
|
90 | f3a6cc07 | Peter A. G. Crosthwaite | { |
91 | f3a6cc07 | Peter A. G. Crosthwaite | /* timer_steps has max value of 0x100000000. double check it
|
92 | f3a6cc07 | Peter A. G. Crosthwaite | * (or overflow can happen below) */
|
93 | f3a6cc07 | Peter A. G. Crosthwaite | assert(timer_steps <= 1ULL << 32); |
94 | f3a6cc07 | Peter A. G. Crosthwaite | |
95 | f3a6cc07 | Peter A. G. Crosthwaite | uint64_t r = timer_steps * 1000000000ULL;
|
96 | f3a6cc07 | Peter A. G. Crosthwaite | if (s->reg_clock & CLOCK_CTRL_PS_EN) {
|
97 | f3a6cc07 | Peter A. G. Crosthwaite | r >>= 16 - (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); |
98 | f3a6cc07 | Peter A. G. Crosthwaite | } else {
|
99 | f3a6cc07 | Peter A. G. Crosthwaite | r >>= 16;
|
100 | f3a6cc07 | Peter A. G. Crosthwaite | } |
101 | f3a6cc07 | Peter A. G. Crosthwaite | r /= (uint64_t)s->freq; |
102 | f3a6cc07 | Peter A. G. Crosthwaite | return r;
|
103 | f3a6cc07 | Peter A. G. Crosthwaite | } |
104 | f3a6cc07 | Peter A. G. Crosthwaite | |
105 | f3a6cc07 | Peter A. G. Crosthwaite | static uint64_t cadence_timer_get_steps(CadenceTimerState *s, uint64_t ns)
|
106 | f3a6cc07 | Peter A. G. Crosthwaite | { |
107 | f3a6cc07 | Peter A. G. Crosthwaite | uint64_t to_divide = 1000000000ULL;
|
108 | f3a6cc07 | Peter A. G. Crosthwaite | |
109 | f3a6cc07 | Peter A. G. Crosthwaite | uint64_t r = ns; |
110 | f3a6cc07 | Peter A. G. Crosthwaite | /* for very large intervals (> 8s) do some division first to stop
|
111 | f3a6cc07 | Peter A. G. Crosthwaite | * overflow (costs some prescision) */
|
112 | f3a6cc07 | Peter A. G. Crosthwaite | while (r >= 8ULL << 30 && to_divide > 1) { |
113 | f3a6cc07 | Peter A. G. Crosthwaite | r /= 1000;
|
114 | f3a6cc07 | Peter A. G. Crosthwaite | to_divide /= 1000;
|
115 | f3a6cc07 | Peter A. G. Crosthwaite | } |
116 | f3a6cc07 | Peter A. G. Crosthwaite | r <<= 16;
|
117 | f3a6cc07 | Peter A. G. Crosthwaite | /* keep early-dividing as needed */
|
118 | f3a6cc07 | Peter A. G. Crosthwaite | while (r >= 8ULL << 30 && to_divide > 1) { |
119 | f3a6cc07 | Peter A. G. Crosthwaite | r /= 1000;
|
120 | f3a6cc07 | Peter A. G. Crosthwaite | to_divide /= 1000;
|
121 | f3a6cc07 | Peter A. G. Crosthwaite | } |
122 | f3a6cc07 | Peter A. G. Crosthwaite | r *= (uint64_t)s->freq; |
123 | f3a6cc07 | Peter A. G. Crosthwaite | if (s->reg_clock & CLOCK_CTRL_PS_EN) {
|
124 | f3a6cc07 | Peter A. G. Crosthwaite | r /= 1 << (((s->reg_clock & CLOCK_CTRL_PS_V) >> 1) + 1); |
125 | f3a6cc07 | Peter A. G. Crosthwaite | } |
126 | f3a6cc07 | Peter A. G. Crosthwaite | |
127 | f3a6cc07 | Peter A. G. Crosthwaite | r /= to_divide; |
128 | f3a6cc07 | Peter A. G. Crosthwaite | return r;
|
129 | f3a6cc07 | Peter A. G. Crosthwaite | } |
130 | f3a6cc07 | Peter A. G. Crosthwaite | |
131 | f3a6cc07 | Peter A. G. Crosthwaite | /* determine if x is in between a and b, exclusive of a, inclusive of b */
|
132 | f3a6cc07 | Peter A. G. Crosthwaite | |
133 | f3a6cc07 | Peter A. G. Crosthwaite | static inline int64_t is_between(int64_t x, int64_t a, int64_t b) |
134 | f3a6cc07 | Peter A. G. Crosthwaite | { |
135 | f3a6cc07 | Peter A. G. Crosthwaite | if (a < b) {
|
136 | f3a6cc07 | Peter A. G. Crosthwaite | return x > a && x <= b;
|
137 | f3a6cc07 | Peter A. G. Crosthwaite | } |
138 | f3a6cc07 | Peter A. G. Crosthwaite | return x < a && x >= b;
|
139 | f3a6cc07 | Peter A. G. Crosthwaite | } |
140 | f3a6cc07 | Peter A. G. Crosthwaite | |
141 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_timer_run(CadenceTimerState *s) |
142 | f3a6cc07 | Peter A. G. Crosthwaite | { |
143 | f3a6cc07 | Peter A. G. Crosthwaite | int i;
|
144 | f3a6cc07 | Peter A. G. Crosthwaite | int64_t event_interval, next_value; |
145 | f3a6cc07 | Peter A. G. Crosthwaite | |
146 | f3a6cc07 | Peter A. G. Crosthwaite | assert(s->cpu_time_valid); /* cadence_timer_sync must be called first */
|
147 | f3a6cc07 | Peter A. G. Crosthwaite | |
148 | f3a6cc07 | Peter A. G. Crosthwaite | if (s->reg_count & COUNTER_CTRL_DIS) {
|
149 | f3a6cc07 | Peter A. G. Crosthwaite | s->cpu_time_valid = 0;
|
150 | f3a6cc07 | Peter A. G. Crosthwaite | return;
|
151 | f3a6cc07 | Peter A. G. Crosthwaite | } |
152 | f3a6cc07 | Peter A. G. Crosthwaite | |
153 | f3a6cc07 | Peter A. G. Crosthwaite | { /* figure out what's going to happen next (rollover or match) */
|
154 | f3a6cc07 | Peter A. G. Crosthwaite | int64_t interval = (uint64_t)((s->reg_count & COUNTER_CTRL_INT) ? |
155 | f3a6cc07 | Peter A. G. Crosthwaite | (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; |
156 | f3a6cc07 | Peter A. G. Crosthwaite | next_value = (s->reg_count & COUNTER_CTRL_DEC) ? -1ULL : interval;
|
157 | f3a6cc07 | Peter A. G. Crosthwaite | for (i = 0; i < 3; ++i) { |
158 | f3a6cc07 | Peter A. G. Crosthwaite | int64_t cand = (uint64_t)s->reg_match[i] << 16;
|
159 | f3a6cc07 | Peter A. G. Crosthwaite | if (is_between(cand, (uint64_t)s->reg_value, next_value)) {
|
160 | f3a6cc07 | Peter A. G. Crosthwaite | next_value = cand; |
161 | f3a6cc07 | Peter A. G. Crosthwaite | } |
162 | f3a6cc07 | Peter A. G. Crosthwaite | } |
163 | f3a6cc07 | Peter A. G. Crosthwaite | } |
164 | f3a6cc07 | Peter A. G. Crosthwaite | DB_PRINT("next timer event value: %09llx\n",
|
165 | f3a6cc07 | Peter A. G. Crosthwaite | (unsigned long long)next_value); |
166 | f3a6cc07 | Peter A. G. Crosthwaite | |
167 | f3a6cc07 | Peter A. G. Crosthwaite | event_interval = next_value - (int64_t)s->reg_value; |
168 | f3a6cc07 | Peter A. G. Crosthwaite | event_interval = (event_interval < 0) ? -event_interval : event_interval;
|
169 | f3a6cc07 | Peter A. G. Crosthwaite | |
170 | f3a6cc07 | Peter A. G. Crosthwaite | qemu_mod_timer(s->timer, s->cpu_time + |
171 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_get_ns(s, event_interval)); |
172 | f3a6cc07 | Peter A. G. Crosthwaite | } |
173 | f3a6cc07 | Peter A. G. Crosthwaite | |
174 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_timer_sync(CadenceTimerState *s) |
175 | f3a6cc07 | Peter A. G. Crosthwaite | { |
176 | f3a6cc07 | Peter A. G. Crosthwaite | int i;
|
177 | f3a6cc07 | Peter A. G. Crosthwaite | int64_t r, x; |
178 | f3a6cc07 | Peter A. G. Crosthwaite | int64_t interval = ((s->reg_count & COUNTER_CTRL_INT) ? |
179 | f3a6cc07 | Peter A. G. Crosthwaite | (int64_t)s->reg_interval + 1 : 0x10000ULL) << 16; |
180 | f3a6cc07 | Peter A. G. Crosthwaite | uint64_t old_time = s->cpu_time; |
181 | f3a6cc07 | Peter A. G. Crosthwaite | |
182 | f3a6cc07 | Peter A. G. Crosthwaite | s->cpu_time = qemu_get_clock_ns(vm_clock); |
183 | f3a6cc07 | Peter A. G. Crosthwaite | DB_PRINT("cpu time: %lld ns\n", (long long)old_time); |
184 | f3a6cc07 | Peter A. G. Crosthwaite | |
185 | f3a6cc07 | Peter A. G. Crosthwaite | if (!s->cpu_time_valid || old_time == s->cpu_time) {
|
186 | f3a6cc07 | Peter A. G. Crosthwaite | s->cpu_time_valid = 1;
|
187 | f3a6cc07 | Peter A. G. Crosthwaite | return;
|
188 | f3a6cc07 | Peter A. G. Crosthwaite | } |
189 | f3a6cc07 | Peter A. G. Crosthwaite | |
190 | f3a6cc07 | Peter A. G. Crosthwaite | r = (int64_t)cadence_timer_get_steps(s, s->cpu_time - old_time); |
191 | f3a6cc07 | Peter A. G. Crosthwaite | x = (int64_t)s->reg_value + ((s->reg_count & COUNTER_CTRL_DEC) ? -r : r); |
192 | f3a6cc07 | Peter A. G. Crosthwaite | |
193 | f3a6cc07 | Peter A. G. Crosthwaite | for (i = 0; i < 3; ++i) { |
194 | f3a6cc07 | Peter A. G. Crosthwaite | int64_t m = (int64_t)s->reg_match[i] << 16;
|
195 | f3a6cc07 | Peter A. G. Crosthwaite | if (m > interval) {
|
196 | f3a6cc07 | Peter A. G. Crosthwaite | continue;
|
197 | f3a6cc07 | Peter A. G. Crosthwaite | } |
198 | f3a6cc07 | Peter A. G. Crosthwaite | /* check to see if match event has occurred. check m +/- interval
|
199 | f3a6cc07 | Peter A. G. Crosthwaite | * to account for match events in wrap around cases */
|
200 | f3a6cc07 | Peter A. G. Crosthwaite | if (is_between(m, s->reg_value, x) ||
|
201 | f3a6cc07 | Peter A. G. Crosthwaite | is_between(m + interval, s->reg_value, x) || |
202 | f3a6cc07 | Peter A. G. Crosthwaite | is_between(m - interval, s->reg_value, x)) { |
203 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_intr |= (2 << i);
|
204 | f3a6cc07 | Peter A. G. Crosthwaite | } |
205 | f3a6cc07 | Peter A. G. Crosthwaite | } |
206 | f3a6cc07 | Peter A. G. Crosthwaite | while (x < 0) { |
207 | f3a6cc07 | Peter A. G. Crosthwaite | x += interval; |
208 | f3a6cc07 | Peter A. G. Crosthwaite | } |
209 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_value = (uint32_t)(x % interval); |
210 | f3a6cc07 | Peter A. G. Crosthwaite | |
211 | f3a6cc07 | Peter A. G. Crosthwaite | if (s->reg_value != x) {
|
212 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? |
213 | f3a6cc07 | Peter A. G. Crosthwaite | COUNTER_INTR_IV : COUNTER_INTR_OV; |
214 | f3a6cc07 | Peter A. G. Crosthwaite | } |
215 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_update(s); |
216 | f3a6cc07 | Peter A. G. Crosthwaite | } |
217 | f3a6cc07 | Peter A. G. Crosthwaite | |
218 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_timer_tick(void *opaque) |
219 | f3a6cc07 | Peter A. G. Crosthwaite | { |
220 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTimerState *s = opaque; |
221 | f3a6cc07 | Peter A. G. Crosthwaite | |
222 | f3a6cc07 | Peter A. G. Crosthwaite | DB_PRINT("\n");
|
223 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_sync(s); |
224 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_run(s); |
225 | f3a6cc07 | Peter A. G. Crosthwaite | } |
226 | f3a6cc07 | Peter A. G. Crosthwaite | |
227 | a8170e5e | Avi Kivity | static uint32_t cadence_ttc_read_imp(void *opaque, hwaddr offset) |
228 | f3a6cc07 | Peter A. G. Crosthwaite | { |
229 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); |
230 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t value; |
231 | f3a6cc07 | Peter A. G. Crosthwaite | |
232 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_sync(s); |
233 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_run(s); |
234 | f3a6cc07 | Peter A. G. Crosthwaite | |
235 | f3a6cc07 | Peter A. G. Crosthwaite | switch (offset) {
|
236 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x00: /* clock control */ |
237 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x04: |
238 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x08: |
239 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_clock;
|
240 | f3a6cc07 | Peter A. G. Crosthwaite | |
241 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x0c: /* counter control */ |
242 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x10: |
243 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x14: |
244 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_count;
|
245 | f3a6cc07 | Peter A. G. Crosthwaite | |
246 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x18: /* counter value */ |
247 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x1c: |
248 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x20: |
249 | f3a6cc07 | Peter A. G. Crosthwaite | return (uint16_t)(s->reg_value >> 16); |
250 | f3a6cc07 | Peter A. G. Crosthwaite | |
251 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x24: /* reg_interval counter */ |
252 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x28: |
253 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x2c: |
254 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_interval;
|
255 | f3a6cc07 | Peter A. G. Crosthwaite | |
256 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x30: /* match 1 counter */ |
257 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x34: |
258 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x38: |
259 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_match[0]; |
260 | f3a6cc07 | Peter A. G. Crosthwaite | |
261 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x3c: /* match 2 counter */ |
262 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x40: |
263 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x44: |
264 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_match[1]; |
265 | f3a6cc07 | Peter A. G. Crosthwaite | |
266 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x48: /* match 3 counter */ |
267 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x4c: |
268 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x50: |
269 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_match[2]; |
270 | f3a6cc07 | Peter A. G. Crosthwaite | |
271 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x54: /* interrupt register */ |
272 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x58: |
273 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x5c: |
274 | f3a6cc07 | Peter A. G. Crosthwaite | /* cleared after read */
|
275 | f3a6cc07 | Peter A. G. Crosthwaite | value = s->reg_intr; |
276 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_intr = 0;
|
277 | 884285bf | Soren Brinkmann | cadence_timer_update(s); |
278 | f3a6cc07 | Peter A. G. Crosthwaite | return value;
|
279 | f3a6cc07 | Peter A. G. Crosthwaite | |
280 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x60: /* interrupt enable */ |
281 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x64: |
282 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x68: |
283 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_intr_en;
|
284 | f3a6cc07 | Peter A. G. Crosthwaite | |
285 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x6c: |
286 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x70: |
287 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x74: |
288 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_event_ctrl;
|
289 | f3a6cc07 | Peter A. G. Crosthwaite | |
290 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x78: |
291 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x7c: |
292 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x80: |
293 | f3a6cc07 | Peter A. G. Crosthwaite | return s->reg_event;
|
294 | f3a6cc07 | Peter A. G. Crosthwaite | |
295 | f3a6cc07 | Peter A. G. Crosthwaite | default:
|
296 | f3a6cc07 | Peter A. G. Crosthwaite | return 0; |
297 | f3a6cc07 | Peter A. G. Crosthwaite | } |
298 | f3a6cc07 | Peter A. G. Crosthwaite | } |
299 | f3a6cc07 | Peter A. G. Crosthwaite | |
300 | a8170e5e | Avi Kivity | static uint64_t cadence_ttc_read(void *opaque, hwaddr offset, |
301 | f3a6cc07 | Peter A. G. Crosthwaite | unsigned size)
|
302 | f3a6cc07 | Peter A. G. Crosthwaite | { |
303 | f3a6cc07 | Peter A. G. Crosthwaite | uint32_t ret = cadence_ttc_read_imp(opaque, offset); |
304 | f3a6cc07 | Peter A. G. Crosthwaite | |
305 | c6954413 | Peter Crosthwaite | DB_PRINT("addr: %08x data: %08x\n", (unsigned)offset, (unsigned)ret); |
306 | f3a6cc07 | Peter A. G. Crosthwaite | return ret;
|
307 | f3a6cc07 | Peter A. G. Crosthwaite | } |
308 | f3a6cc07 | Peter A. G. Crosthwaite | |
309 | a8170e5e | Avi Kivity | static void cadence_ttc_write(void *opaque, hwaddr offset, |
310 | f3a6cc07 | Peter A. G. Crosthwaite | uint64_t value, unsigned size)
|
311 | f3a6cc07 | Peter A. G. Crosthwaite | { |
312 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTimerState *s = cadence_timer_from_addr(opaque, offset); |
313 | f3a6cc07 | Peter A. G. Crosthwaite | |
314 | c6954413 | Peter Crosthwaite | DB_PRINT("addr: %08x data %08x\n", (unsigned)offset, (unsigned)value); |
315 | f3a6cc07 | Peter A. G. Crosthwaite | |
316 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_sync(s); |
317 | f3a6cc07 | Peter A. G. Crosthwaite | |
318 | f3a6cc07 | Peter A. G. Crosthwaite | switch (offset) {
|
319 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x00: /* clock control */ |
320 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x04: |
321 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x08: |
322 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_clock = value & 0x3F;
|
323 | f3a6cc07 | Peter A. G. Crosthwaite | break;
|
324 | f3a6cc07 | Peter A. G. Crosthwaite | |
325 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x0c: /* counter control */ |
326 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x10: |
327 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x14: |
328 | f3a6cc07 | Peter A. G. Crosthwaite | if (value & COUNTER_CTRL_RST) {
|
329 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_value = 0;
|
330 | f3a6cc07 | Peter A. G. Crosthwaite | } |
331 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_count = value & 0x3f & ~COUNTER_CTRL_RST;
|
332 | f3a6cc07 | Peter A. G. Crosthwaite | break;
|
333 | f3a6cc07 | Peter A. G. Crosthwaite | |
334 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x24: /* interval register */ |
335 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x28: |
336 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x2c: |
337 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_interval = value & 0xffff;
|
338 | f3a6cc07 | Peter A. G. Crosthwaite | break;
|
339 | f3a6cc07 | Peter A. G. Crosthwaite | |
340 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x30: /* match register */ |
341 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x34: |
342 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x38: |
343 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_match[0] = value & 0xffff; |
344 | f3a6cc07 | Peter A. G. Crosthwaite | |
345 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x3c: /* match register */ |
346 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x40: |
347 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x44: |
348 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_match[1] = value & 0xffff; |
349 | f3a6cc07 | Peter A. G. Crosthwaite | |
350 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x48: /* match register */ |
351 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x4c: |
352 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x50: |
353 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_match[2] = value & 0xffff; |
354 | f3a6cc07 | Peter A. G. Crosthwaite | break;
|
355 | f3a6cc07 | Peter A. G. Crosthwaite | |
356 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x54: /* interrupt register */ |
357 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x58: |
358 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x5c: |
359 | f3a6cc07 | Peter A. G. Crosthwaite | break;
|
360 | f3a6cc07 | Peter A. G. Crosthwaite | |
361 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x60: /* interrupt enable */ |
362 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x64: |
363 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x68: |
364 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_intr_en = value & 0x3f;
|
365 | f3a6cc07 | Peter A. G. Crosthwaite | break;
|
366 | f3a6cc07 | Peter A. G. Crosthwaite | |
367 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x6c: /* event control */ |
368 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x70: |
369 | f3a6cc07 | Peter A. G. Crosthwaite | case 0x74: |
370 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_event_ctrl = value & 0x07;
|
371 | f3a6cc07 | Peter A. G. Crosthwaite | break;
|
372 | f3a6cc07 | Peter A. G. Crosthwaite | |
373 | f3a6cc07 | Peter A. G. Crosthwaite | default:
|
374 | f3a6cc07 | Peter A. G. Crosthwaite | return;
|
375 | f3a6cc07 | Peter A. G. Crosthwaite | } |
376 | f3a6cc07 | Peter A. G. Crosthwaite | |
377 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_run(s); |
378 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_update(s); |
379 | f3a6cc07 | Peter A. G. Crosthwaite | } |
380 | f3a6cc07 | Peter A. G. Crosthwaite | |
381 | f3a6cc07 | Peter A. G. Crosthwaite | static const MemoryRegionOps cadence_ttc_ops = { |
382 | f3a6cc07 | Peter A. G. Crosthwaite | .read = cadence_ttc_read, |
383 | f3a6cc07 | Peter A. G. Crosthwaite | .write = cadence_ttc_write, |
384 | f3a6cc07 | Peter A. G. Crosthwaite | .endianness = DEVICE_NATIVE_ENDIAN, |
385 | f3a6cc07 | Peter A. G. Crosthwaite | }; |
386 | f3a6cc07 | Peter A. G. Crosthwaite | |
387 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_timer_reset(CadenceTimerState *s) |
388 | f3a6cc07 | Peter A. G. Crosthwaite | { |
389 | f3a6cc07 | Peter A. G. Crosthwaite | s->reg_count = 0x21;
|
390 | f3a6cc07 | Peter A. G. Crosthwaite | } |
391 | f3a6cc07 | Peter A. G. Crosthwaite | |
392 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_timer_init(uint32_t freq, CadenceTimerState *s) |
393 | f3a6cc07 | Peter A. G. Crosthwaite | { |
394 | f3a6cc07 | Peter A. G. Crosthwaite | memset(s, 0, sizeof(CadenceTimerState)); |
395 | f3a6cc07 | Peter A. G. Crosthwaite | s->freq = freq; |
396 | f3a6cc07 | Peter A. G. Crosthwaite | |
397 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_reset(s); |
398 | f3a6cc07 | Peter A. G. Crosthwaite | |
399 | f3a6cc07 | Peter A. G. Crosthwaite | s->timer = qemu_new_timer_ns(vm_clock, cadence_timer_tick, s); |
400 | f3a6cc07 | Peter A. G. Crosthwaite | } |
401 | f3a6cc07 | Peter A. G. Crosthwaite | |
402 | f3a6cc07 | Peter A. G. Crosthwaite | static int cadence_ttc_init(SysBusDevice *dev) |
403 | f3a6cc07 | Peter A. G. Crosthwaite | { |
404 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTTCState *s = FROM_SYSBUS(CadenceTTCState, dev); |
405 | f3a6cc07 | Peter A. G. Crosthwaite | int i;
|
406 | f3a6cc07 | Peter A. G. Crosthwaite | |
407 | f3a6cc07 | Peter A. G. Crosthwaite | for (i = 0; i < 3; ++i) { |
408 | 69efc026 | Peter A. G. Crosthwaite | cadence_timer_init(133000000, &s->timer[i]);
|
409 | f3a6cc07 | Peter A. G. Crosthwaite | sysbus_init_irq(dev, &s->timer[i].irq); |
410 | f3a6cc07 | Peter A. G. Crosthwaite | } |
411 | f3a6cc07 | Peter A. G. Crosthwaite | |
412 | f3a6cc07 | Peter A. G. Crosthwaite | memory_region_init_io(&s->iomem, &cadence_ttc_ops, s, "timer", 0x1000); |
413 | f3a6cc07 | Peter A. G. Crosthwaite | sysbus_init_mmio(dev, &s->iomem); |
414 | f3a6cc07 | Peter A. G. Crosthwaite | |
415 | f3a6cc07 | Peter A. G. Crosthwaite | return 0; |
416 | f3a6cc07 | Peter A. G. Crosthwaite | } |
417 | f3a6cc07 | Peter A. G. Crosthwaite | |
418 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_timer_pre_save(void *opaque) |
419 | f3a6cc07 | Peter A. G. Crosthwaite | { |
420 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_sync((CadenceTimerState *)opaque); |
421 | f3a6cc07 | Peter A. G. Crosthwaite | } |
422 | f3a6cc07 | Peter A. G. Crosthwaite | |
423 | f3a6cc07 | Peter A. G. Crosthwaite | static int cadence_timer_post_load(void *opaque, int version_id) |
424 | f3a6cc07 | Peter A. G. Crosthwaite | { |
425 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTimerState *s = opaque; |
426 | f3a6cc07 | Peter A. G. Crosthwaite | |
427 | f3a6cc07 | Peter A. G. Crosthwaite | s->cpu_time_valid = 0;
|
428 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_sync(s); |
429 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_run(s); |
430 | f3a6cc07 | Peter A. G. Crosthwaite | cadence_timer_update(s); |
431 | f3a6cc07 | Peter A. G. Crosthwaite | return 0; |
432 | f3a6cc07 | Peter A. G. Crosthwaite | } |
433 | f3a6cc07 | Peter A. G. Crosthwaite | |
434 | f3a6cc07 | Peter A. G. Crosthwaite | static const VMStateDescription vmstate_cadence_timer = { |
435 | f3a6cc07 | Peter A. G. Crosthwaite | .name = "cadence_timer",
|
436 | f3a6cc07 | Peter A. G. Crosthwaite | .version_id = 1,
|
437 | f3a6cc07 | Peter A. G. Crosthwaite | .minimum_version_id = 1,
|
438 | f3a6cc07 | Peter A. G. Crosthwaite | .minimum_version_id_old = 1,
|
439 | f3a6cc07 | Peter A. G. Crosthwaite | .pre_save = cadence_timer_pre_save, |
440 | f3a6cc07 | Peter A. G. Crosthwaite | .post_load = cadence_timer_post_load, |
441 | f3a6cc07 | Peter A. G. Crosthwaite | .fields = (VMStateField[]) { |
442 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT32(reg_clock, CadenceTimerState), |
443 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT32(reg_count, CadenceTimerState), |
444 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT32(reg_value, CadenceTimerState), |
445 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT16(reg_interval, CadenceTimerState), |
446 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT16_ARRAY(reg_match, CadenceTimerState, 3),
|
447 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT32(reg_intr, CadenceTimerState), |
448 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT32(reg_intr_en, CadenceTimerState), |
449 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT32(reg_event_ctrl, CadenceTimerState), |
450 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_UINT32(reg_event, CadenceTimerState), |
451 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_END_OF_LIST() |
452 | f3a6cc07 | Peter A. G. Crosthwaite | } |
453 | f3a6cc07 | Peter A. G. Crosthwaite | }; |
454 | f3a6cc07 | Peter A. G. Crosthwaite | |
455 | f3a6cc07 | Peter A. G. Crosthwaite | static const VMStateDescription vmstate_cadence_ttc = { |
456 | f3a6cc07 | Peter A. G. Crosthwaite | .name = "cadence_TTC",
|
457 | f3a6cc07 | Peter A. G. Crosthwaite | .version_id = 1,
|
458 | f3a6cc07 | Peter A. G. Crosthwaite | .minimum_version_id = 1,
|
459 | f3a6cc07 | Peter A. G. Crosthwaite | .minimum_version_id_old = 1,
|
460 | f3a6cc07 | Peter A. G. Crosthwaite | .fields = (VMStateField[]) { |
461 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_STRUCT_ARRAY(timer, CadenceTTCState, 3, 0, |
462 | f3a6cc07 | Peter A. G. Crosthwaite | vmstate_cadence_timer, |
463 | f3a6cc07 | Peter A. G. Crosthwaite | CadenceTimerState), |
464 | f3a6cc07 | Peter A. G. Crosthwaite | VMSTATE_END_OF_LIST() |
465 | f3a6cc07 | Peter A. G. Crosthwaite | } |
466 | f3a6cc07 | Peter A. G. Crosthwaite | }; |
467 | f3a6cc07 | Peter A. G. Crosthwaite | |
468 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_ttc_class_init(ObjectClass *klass, void *data) |
469 | f3a6cc07 | Peter A. G. Crosthwaite | { |
470 | f3a6cc07 | Peter A. G. Crosthwaite | DeviceClass *dc = DEVICE_CLASS(klass); |
471 | f3a6cc07 | Peter A. G. Crosthwaite | SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); |
472 | f3a6cc07 | Peter A. G. Crosthwaite | |
473 | f3a6cc07 | Peter A. G. Crosthwaite | sdc->init = cadence_ttc_init; |
474 | f3a6cc07 | Peter A. G. Crosthwaite | dc->vmsd = &vmstate_cadence_ttc; |
475 | f3a6cc07 | Peter A. G. Crosthwaite | } |
476 | f3a6cc07 | Peter A. G. Crosthwaite | |
477 | 8c43a6f0 | Andreas Färber | static const TypeInfo cadence_ttc_info = { |
478 | f3a6cc07 | Peter A. G. Crosthwaite | .name = "cadence_ttc",
|
479 | f3a6cc07 | Peter A. G. Crosthwaite | .parent = TYPE_SYS_BUS_DEVICE, |
480 | f3a6cc07 | Peter A. G. Crosthwaite | .instance_size = sizeof(CadenceTTCState),
|
481 | f3a6cc07 | Peter A. G. Crosthwaite | .class_init = cadence_ttc_class_init, |
482 | f3a6cc07 | Peter A. G. Crosthwaite | }; |
483 | f3a6cc07 | Peter A. G. Crosthwaite | |
484 | f3a6cc07 | Peter A. G. Crosthwaite | static void cadence_ttc_register_types(void) |
485 | f3a6cc07 | Peter A. G. Crosthwaite | { |
486 | f3a6cc07 | Peter A. G. Crosthwaite | type_register_static(&cadence_ttc_info); |
487 | f3a6cc07 | Peter A. G. Crosthwaite | } |
488 | f3a6cc07 | Peter A. G. Crosthwaite | |
489 | f3a6cc07 | Peter A. G. Crosthwaite | type_init(cadence_ttc_register_types) |