root / hw / omap_gptimer.c @ 5c3234c6
History | View | Annotate | Download (12.7 kB)
1 | c58d37cf | cmchao | /*
|
---|---|---|---|
2 | c58d37cf | cmchao | * TI OMAP2 general purpose timers emulation.
|
3 | c58d37cf | cmchao | *
|
4 | c58d37cf | cmchao | * Copyright (C) 2007-2008 Nokia Corporation
|
5 | c58d37cf | cmchao | * Written by Andrzej Zaborowski <andrew@openedhand.com>
|
6 | c58d37cf | cmchao | *
|
7 | c58d37cf | cmchao | * This program is free software; you can redistribute it and/or
|
8 | c58d37cf | cmchao | * modify it under the terms of the GNU General Public License as
|
9 | c58d37cf | cmchao | * published by the Free Software Foundation; either version 2 or
|
10 | c58d37cf | cmchao | * (at your option) any later version of the License.
|
11 | c58d37cf | cmchao | *
|
12 | c58d37cf | cmchao | * This program is distributed in the hope that it will be useful,
|
13 | c58d37cf | cmchao | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | c58d37cf | cmchao | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 | c58d37cf | cmchao | * GNU General Public License for more details.
|
16 | c58d37cf | cmchao | *
|
17 | c58d37cf | cmchao | * You should have received a copy of the GNU General Public License along
|
18 | c58d37cf | cmchao | * with this program; if not, see <http://www.gnu.org/licenses/>.
|
19 | c58d37cf | cmchao | */
|
20 | c58d37cf | cmchao | #include "hw.h" |
21 | c58d37cf | cmchao | #include "qemu-timer.h" |
22 | c58d37cf | cmchao | #include "omap.h" |
23 | c58d37cf | cmchao | |
24 | c58d37cf | cmchao | /* GP timers */
|
25 | c58d37cf | cmchao | struct omap_gp_timer_s {
|
26 | c58d37cf | cmchao | qemu_irq irq; |
27 | c58d37cf | cmchao | qemu_irq wkup; |
28 | c58d37cf | cmchao | qemu_irq in; |
29 | c58d37cf | cmchao | qemu_irq out; |
30 | c58d37cf | cmchao | omap_clk clk; |
31 | c58d37cf | cmchao | QEMUTimer *timer; |
32 | c58d37cf | cmchao | QEMUTimer *match; |
33 | c58d37cf | cmchao | struct omap_target_agent_s *ta;
|
34 | c58d37cf | cmchao | |
35 | c58d37cf | cmchao | int in_val;
|
36 | c58d37cf | cmchao | int out_val;
|
37 | c58d37cf | cmchao | int64_t time; |
38 | c58d37cf | cmchao | int64_t rate; |
39 | c58d37cf | cmchao | int64_t ticks_per_sec; |
40 | c58d37cf | cmchao | |
41 | c58d37cf | cmchao | int16_t config; |
42 | c58d37cf | cmchao | int status;
|
43 | c58d37cf | cmchao | int it_ena;
|
44 | c58d37cf | cmchao | int wu_ena;
|
45 | c58d37cf | cmchao | int enable;
|
46 | c58d37cf | cmchao | int inout;
|
47 | c58d37cf | cmchao | int capt2;
|
48 | c58d37cf | cmchao | int pt;
|
49 | c58d37cf | cmchao | enum {
|
50 | c58d37cf | cmchao | gpt_trigger_none, gpt_trigger_overflow, gpt_trigger_both |
51 | c58d37cf | cmchao | } trigger; |
52 | c58d37cf | cmchao | enum {
|
53 | c58d37cf | cmchao | gpt_capture_none, gpt_capture_rising, |
54 | c58d37cf | cmchao | gpt_capture_falling, gpt_capture_both |
55 | c58d37cf | cmchao | } capture; |
56 | c58d37cf | cmchao | int scpwm;
|
57 | c58d37cf | cmchao | int ce;
|
58 | c58d37cf | cmchao | int pre;
|
59 | c58d37cf | cmchao | int ptv;
|
60 | c58d37cf | cmchao | int ar;
|
61 | c58d37cf | cmchao | int st;
|
62 | c58d37cf | cmchao | int posted;
|
63 | c58d37cf | cmchao | uint32_t val; |
64 | c58d37cf | cmchao | uint32_t load_val; |
65 | c58d37cf | cmchao | uint32_t capture_val[2];
|
66 | c58d37cf | cmchao | uint32_t match_val; |
67 | c58d37cf | cmchao | int capt_num;
|
68 | c58d37cf | cmchao | |
69 | c58d37cf | cmchao | uint16_t writeh; /* LSB */
|
70 | c58d37cf | cmchao | uint16_t readh; /* MSB */
|
71 | c58d37cf | cmchao | }; |
72 | c58d37cf | cmchao | |
73 | c58d37cf | cmchao | #define GPT_TCAR_IT (1 << 2) |
74 | c58d37cf | cmchao | #define GPT_OVF_IT (1 << 1) |
75 | c58d37cf | cmchao | #define GPT_MAT_IT (1 << 0) |
76 | c58d37cf | cmchao | |
77 | c58d37cf | cmchao | static inline void omap_gp_timer_intr(struct omap_gp_timer_s *timer, int it) |
78 | c58d37cf | cmchao | { |
79 | c58d37cf | cmchao | if (timer->it_ena & it) {
|
80 | c58d37cf | cmchao | if (!timer->status)
|
81 | c58d37cf | cmchao | qemu_irq_raise(timer->irq); |
82 | c58d37cf | cmchao | |
83 | c58d37cf | cmchao | timer->status |= it; |
84 | c58d37cf | cmchao | /* Or are the status bits set even when masked?
|
85 | c58d37cf | cmchao | * i.e. is masking applied before or after the status register? */
|
86 | c58d37cf | cmchao | } |
87 | c58d37cf | cmchao | |
88 | c58d37cf | cmchao | if (timer->wu_ena & it)
|
89 | c58d37cf | cmchao | qemu_irq_pulse(timer->wkup); |
90 | c58d37cf | cmchao | } |
91 | c58d37cf | cmchao | |
92 | c58d37cf | cmchao | static inline void omap_gp_timer_out(struct omap_gp_timer_s *timer, int level) |
93 | c58d37cf | cmchao | { |
94 | c58d37cf | cmchao | if (!timer->inout && timer->out_val != level) {
|
95 | c58d37cf | cmchao | timer->out_val = level; |
96 | c58d37cf | cmchao | qemu_set_irq(timer->out, level); |
97 | c58d37cf | cmchao | } |
98 | c58d37cf | cmchao | } |
99 | c58d37cf | cmchao | |
100 | c58d37cf | cmchao | static inline uint32_t omap_gp_timer_read(struct omap_gp_timer_s *timer) |
101 | c58d37cf | cmchao | { |
102 | c58d37cf | cmchao | uint64_t distance; |
103 | c58d37cf | cmchao | |
104 | c58d37cf | cmchao | if (timer->st && timer->rate) {
|
105 | 74475455 | Paolo Bonzini | distance = qemu_get_clock_ns(vm_clock) - timer->time; |
106 | c58d37cf | cmchao | distance = muldiv64(distance, timer->rate, timer->ticks_per_sec); |
107 | c58d37cf | cmchao | |
108 | c58d37cf | cmchao | if (distance >= 0xffffffff - timer->val) |
109 | c58d37cf | cmchao | return 0xffffffff; |
110 | c58d37cf | cmchao | else
|
111 | c58d37cf | cmchao | return timer->val + distance;
|
112 | c58d37cf | cmchao | } else
|
113 | c58d37cf | cmchao | return timer->val;
|
114 | c58d37cf | cmchao | } |
115 | c58d37cf | cmchao | |
116 | c58d37cf | cmchao | static inline void omap_gp_timer_sync(struct omap_gp_timer_s *timer) |
117 | c58d37cf | cmchao | { |
118 | c58d37cf | cmchao | if (timer->st) {
|
119 | c58d37cf | cmchao | timer->val = omap_gp_timer_read(timer); |
120 | 74475455 | Paolo Bonzini | timer->time = qemu_get_clock_ns(vm_clock); |
121 | c58d37cf | cmchao | } |
122 | c58d37cf | cmchao | } |
123 | c58d37cf | cmchao | |
124 | c58d37cf | cmchao | static inline void omap_gp_timer_update(struct omap_gp_timer_s *timer) |
125 | c58d37cf | cmchao | { |
126 | c58d37cf | cmchao | int64_t expires, matches; |
127 | c58d37cf | cmchao | |
128 | c58d37cf | cmchao | if (timer->st && timer->rate) {
|
129 | c58d37cf | cmchao | expires = muldiv64(0x100000000ll - timer->val,
|
130 | c58d37cf | cmchao | timer->ticks_per_sec, timer->rate); |
131 | c58d37cf | cmchao | qemu_mod_timer(timer->timer, timer->time + expires); |
132 | c58d37cf | cmchao | |
133 | c58d37cf | cmchao | if (timer->ce && timer->match_val >= timer->val) {
|
134 | c58d37cf | cmchao | matches = muldiv64(timer->match_val - timer->val, |
135 | c58d37cf | cmchao | timer->ticks_per_sec, timer->rate); |
136 | c58d37cf | cmchao | qemu_mod_timer(timer->match, timer->time + matches); |
137 | c58d37cf | cmchao | } else
|
138 | c58d37cf | cmchao | qemu_del_timer(timer->match); |
139 | c58d37cf | cmchao | } else {
|
140 | c58d37cf | cmchao | qemu_del_timer(timer->timer); |
141 | c58d37cf | cmchao | qemu_del_timer(timer->match); |
142 | c58d37cf | cmchao | omap_gp_timer_out(timer, timer->scpwm); |
143 | c58d37cf | cmchao | } |
144 | c58d37cf | cmchao | } |
145 | c58d37cf | cmchao | |
146 | c58d37cf | cmchao | static inline void omap_gp_timer_trigger(struct omap_gp_timer_s *timer) |
147 | c58d37cf | cmchao | { |
148 | c58d37cf | cmchao | if (timer->pt)
|
149 | c58d37cf | cmchao | /* TODO in overflow-and-match mode if the first event to
|
150 | c58d37cf | cmchao | * occur is the match, don't toggle. */
|
151 | c58d37cf | cmchao | omap_gp_timer_out(timer, !timer->out_val); |
152 | c58d37cf | cmchao | else
|
153 | c58d37cf | cmchao | /* TODO inverted pulse on timer->out_val == 1? */
|
154 | c58d37cf | cmchao | qemu_irq_pulse(timer->out); |
155 | c58d37cf | cmchao | } |
156 | c58d37cf | cmchao | |
157 | c58d37cf | cmchao | static void omap_gp_timer_tick(void *opaque) |
158 | c58d37cf | cmchao | { |
159 | c58d37cf | cmchao | struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; |
160 | c58d37cf | cmchao | |
161 | c58d37cf | cmchao | if (!timer->ar) {
|
162 | c58d37cf | cmchao | timer->st = 0;
|
163 | c58d37cf | cmchao | timer->val = 0;
|
164 | c58d37cf | cmchao | } else {
|
165 | c58d37cf | cmchao | timer->val = timer->load_val; |
166 | 74475455 | Paolo Bonzini | timer->time = qemu_get_clock_ns(vm_clock); |
167 | c58d37cf | cmchao | } |
168 | c58d37cf | cmchao | |
169 | c58d37cf | cmchao | if (timer->trigger == gpt_trigger_overflow ||
|
170 | c58d37cf | cmchao | timer->trigger == gpt_trigger_both) |
171 | c58d37cf | cmchao | omap_gp_timer_trigger(timer); |
172 | c58d37cf | cmchao | |
173 | c58d37cf | cmchao | omap_gp_timer_intr(timer, GPT_OVF_IT); |
174 | c58d37cf | cmchao | omap_gp_timer_update(timer); |
175 | c58d37cf | cmchao | } |
176 | c58d37cf | cmchao | |
177 | c58d37cf | cmchao | static void omap_gp_timer_match(void *opaque) |
178 | c58d37cf | cmchao | { |
179 | c58d37cf | cmchao | struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; |
180 | c58d37cf | cmchao | |
181 | c58d37cf | cmchao | if (timer->trigger == gpt_trigger_both)
|
182 | c58d37cf | cmchao | omap_gp_timer_trigger(timer); |
183 | c58d37cf | cmchao | |
184 | c58d37cf | cmchao | omap_gp_timer_intr(timer, GPT_MAT_IT); |
185 | c58d37cf | cmchao | } |
186 | c58d37cf | cmchao | |
187 | c58d37cf | cmchao | static void omap_gp_timer_input(void *opaque, int line, int on) |
188 | c58d37cf | cmchao | { |
189 | c58d37cf | cmchao | struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; |
190 | c58d37cf | cmchao | int trigger;
|
191 | c58d37cf | cmchao | |
192 | c58d37cf | cmchao | switch (s->capture) {
|
193 | c58d37cf | cmchao | default:
|
194 | c58d37cf | cmchao | case gpt_capture_none:
|
195 | c58d37cf | cmchao | trigger = 0;
|
196 | c58d37cf | cmchao | break;
|
197 | c58d37cf | cmchao | case gpt_capture_rising:
|
198 | c58d37cf | cmchao | trigger = !s->in_val && on; |
199 | c58d37cf | cmchao | break;
|
200 | c58d37cf | cmchao | case gpt_capture_falling:
|
201 | c58d37cf | cmchao | trigger = s->in_val && !on; |
202 | c58d37cf | cmchao | break;
|
203 | c58d37cf | cmchao | case gpt_capture_both:
|
204 | c58d37cf | cmchao | trigger = (s->in_val == !on); |
205 | c58d37cf | cmchao | break;
|
206 | c58d37cf | cmchao | } |
207 | c58d37cf | cmchao | s->in_val = on; |
208 | c58d37cf | cmchao | |
209 | c58d37cf | cmchao | if (s->inout && trigger && s->capt_num < 2) { |
210 | c58d37cf | cmchao | s->capture_val[s->capt_num] = omap_gp_timer_read(s); |
211 | c58d37cf | cmchao | |
212 | c58d37cf | cmchao | if (s->capt2 == s->capt_num ++)
|
213 | c58d37cf | cmchao | omap_gp_timer_intr(s, GPT_TCAR_IT); |
214 | c58d37cf | cmchao | } |
215 | c58d37cf | cmchao | } |
216 | c58d37cf | cmchao | |
217 | c58d37cf | cmchao | static void omap_gp_timer_clk_update(void *opaque, int line, int on) |
218 | c58d37cf | cmchao | { |
219 | c58d37cf | cmchao | struct omap_gp_timer_s *timer = (struct omap_gp_timer_s *) opaque; |
220 | c58d37cf | cmchao | |
221 | c58d37cf | cmchao | omap_gp_timer_sync(timer); |
222 | c58d37cf | cmchao | timer->rate = on ? omap_clk_getrate(timer->clk) : 0;
|
223 | c58d37cf | cmchao | omap_gp_timer_update(timer); |
224 | c58d37cf | cmchao | } |
225 | c58d37cf | cmchao | |
226 | c58d37cf | cmchao | static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) |
227 | c58d37cf | cmchao | { |
228 | c58d37cf | cmchao | omap_clk_adduser(timer->clk, |
229 | c58d37cf | cmchao | qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]); |
230 | c58d37cf | cmchao | timer->rate = omap_clk_getrate(timer->clk); |
231 | c58d37cf | cmchao | } |
232 | c58d37cf | cmchao | |
233 | c58d37cf | cmchao | void omap_gp_timer_reset(struct omap_gp_timer_s *s) |
234 | c58d37cf | cmchao | { |
235 | c58d37cf | cmchao | s->config = 0x000;
|
236 | c58d37cf | cmchao | s->status = 0;
|
237 | c58d37cf | cmchao | s->it_ena = 0;
|
238 | c58d37cf | cmchao | s->wu_ena = 0;
|
239 | c58d37cf | cmchao | s->inout = 0;
|
240 | c58d37cf | cmchao | s->capt2 = 0;
|
241 | c58d37cf | cmchao | s->capt_num = 0;
|
242 | c58d37cf | cmchao | s->pt = 0;
|
243 | c58d37cf | cmchao | s->trigger = gpt_trigger_none; |
244 | c58d37cf | cmchao | s->capture = gpt_capture_none; |
245 | c58d37cf | cmchao | s->scpwm = 0;
|
246 | c58d37cf | cmchao | s->ce = 0;
|
247 | c58d37cf | cmchao | s->pre = 0;
|
248 | c58d37cf | cmchao | s->ptv = 0;
|
249 | c58d37cf | cmchao | s->ar = 0;
|
250 | c58d37cf | cmchao | s->st = 0;
|
251 | c58d37cf | cmchao | s->posted = 1;
|
252 | c58d37cf | cmchao | s->val = 0x00000000;
|
253 | c58d37cf | cmchao | s->load_val = 0x00000000;
|
254 | c58d37cf | cmchao | s->capture_val[0] = 0x00000000; |
255 | c58d37cf | cmchao | s->capture_val[1] = 0x00000000; |
256 | c58d37cf | cmchao | s->match_val = 0x00000000;
|
257 | c58d37cf | cmchao | omap_gp_timer_update(s); |
258 | c58d37cf | cmchao | } |
259 | c58d37cf | cmchao | |
260 | c58d37cf | cmchao | static uint32_t omap_gp_timer_readw(void *opaque, target_phys_addr_t addr) |
261 | c58d37cf | cmchao | { |
262 | c58d37cf | cmchao | struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; |
263 | c58d37cf | cmchao | |
264 | c58d37cf | cmchao | switch (addr) {
|
265 | c58d37cf | cmchao | case 0x00: /* TIDR */ |
266 | c58d37cf | cmchao | return 0x21; |
267 | c58d37cf | cmchao | |
268 | c58d37cf | cmchao | case 0x10: /* TIOCP_CFG */ |
269 | c58d37cf | cmchao | return s->config;
|
270 | c58d37cf | cmchao | |
271 | c58d37cf | cmchao | case 0x14: /* TISTAT */ |
272 | c58d37cf | cmchao | /* ??? When's this bit reset? */
|
273 | c58d37cf | cmchao | return 1; /* RESETDONE */ |
274 | c58d37cf | cmchao | |
275 | c58d37cf | cmchao | case 0x18: /* TISR */ |
276 | c58d37cf | cmchao | return s->status;
|
277 | c58d37cf | cmchao | |
278 | c58d37cf | cmchao | case 0x1c: /* TIER */ |
279 | c58d37cf | cmchao | return s->it_ena;
|
280 | c58d37cf | cmchao | |
281 | c58d37cf | cmchao | case 0x20: /* TWER */ |
282 | c58d37cf | cmchao | return s->wu_ena;
|
283 | c58d37cf | cmchao | |
284 | c58d37cf | cmchao | case 0x24: /* TCLR */ |
285 | c58d37cf | cmchao | return (s->inout << 14) | |
286 | c58d37cf | cmchao | (s->capt2 << 13) |
|
287 | c58d37cf | cmchao | (s->pt << 12) |
|
288 | c58d37cf | cmchao | (s->trigger << 10) |
|
289 | c58d37cf | cmchao | (s->capture << 8) |
|
290 | c58d37cf | cmchao | (s->scpwm << 7) |
|
291 | c58d37cf | cmchao | (s->ce << 6) |
|
292 | c58d37cf | cmchao | (s->pre << 5) |
|
293 | c58d37cf | cmchao | (s->ptv << 2) |
|
294 | c58d37cf | cmchao | (s->ar << 1) |
|
295 | c58d37cf | cmchao | (s->st << 0);
|
296 | c58d37cf | cmchao | |
297 | c58d37cf | cmchao | case 0x28: /* TCRR */ |
298 | c58d37cf | cmchao | return omap_gp_timer_read(s);
|
299 | c58d37cf | cmchao | |
300 | c58d37cf | cmchao | case 0x2c: /* TLDR */ |
301 | c58d37cf | cmchao | return s->load_val;
|
302 | c58d37cf | cmchao | |
303 | c58d37cf | cmchao | case 0x30: /* TTGR */ |
304 | c58d37cf | cmchao | return 0xffffffff; |
305 | c58d37cf | cmchao | |
306 | c58d37cf | cmchao | case 0x34: /* TWPS */ |
307 | c58d37cf | cmchao | return 0x00000000; /* No posted writes pending. */ |
308 | c58d37cf | cmchao | |
309 | c58d37cf | cmchao | case 0x38: /* TMAR */ |
310 | c58d37cf | cmchao | return s->match_val;
|
311 | c58d37cf | cmchao | |
312 | c58d37cf | cmchao | case 0x3c: /* TCAR1 */ |
313 | c58d37cf | cmchao | return s->capture_val[0]; |
314 | c58d37cf | cmchao | |
315 | c58d37cf | cmchao | case 0x40: /* TSICR */ |
316 | c58d37cf | cmchao | return s->posted << 2; |
317 | c58d37cf | cmchao | |
318 | c58d37cf | cmchao | case 0x44: /* TCAR2 */ |
319 | c58d37cf | cmchao | return s->capture_val[1]; |
320 | c58d37cf | cmchao | } |
321 | c58d37cf | cmchao | |
322 | c58d37cf | cmchao | OMAP_BAD_REG(addr); |
323 | c58d37cf | cmchao | return 0; |
324 | c58d37cf | cmchao | } |
325 | c58d37cf | cmchao | |
326 | c58d37cf | cmchao | static uint32_t omap_gp_timer_readh(void *opaque, target_phys_addr_t addr) |
327 | c58d37cf | cmchao | { |
328 | c58d37cf | cmchao | struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; |
329 | c58d37cf | cmchao | uint32_t ret; |
330 | c58d37cf | cmchao | |
331 | c58d37cf | cmchao | if (addr & 2) |
332 | c58d37cf | cmchao | return s->readh;
|
333 | c58d37cf | cmchao | else {
|
334 | c58d37cf | cmchao | ret = omap_gp_timer_readw(opaque, addr); |
335 | c58d37cf | cmchao | s->readh = ret >> 16;
|
336 | c58d37cf | cmchao | return ret & 0xffff; |
337 | c58d37cf | cmchao | } |
338 | c58d37cf | cmchao | } |
339 | c58d37cf | cmchao | |
340 | c58d37cf | cmchao | static CPUReadMemoryFunc * const omap_gp_timer_readfn[] = { |
341 | c58d37cf | cmchao | omap_badwidth_read32, |
342 | c58d37cf | cmchao | omap_gp_timer_readh, |
343 | c58d37cf | cmchao | omap_gp_timer_readw, |
344 | c58d37cf | cmchao | }; |
345 | c58d37cf | cmchao | |
346 | c58d37cf | cmchao | static void omap_gp_timer_write(void *opaque, target_phys_addr_t addr, |
347 | c58d37cf | cmchao | uint32_t value) |
348 | c58d37cf | cmchao | { |
349 | c58d37cf | cmchao | struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; |
350 | c58d37cf | cmchao | |
351 | c58d37cf | cmchao | switch (addr) {
|
352 | c58d37cf | cmchao | case 0x00: /* TIDR */ |
353 | c58d37cf | cmchao | case 0x14: /* TISTAT */ |
354 | c58d37cf | cmchao | case 0x34: /* TWPS */ |
355 | c58d37cf | cmchao | case 0x3c: /* TCAR1 */ |
356 | c58d37cf | cmchao | case 0x44: /* TCAR2 */ |
357 | c58d37cf | cmchao | OMAP_RO_REG(addr); |
358 | c58d37cf | cmchao | break;
|
359 | c58d37cf | cmchao | |
360 | c58d37cf | cmchao | case 0x10: /* TIOCP_CFG */ |
361 | c58d37cf | cmchao | s->config = value & 0x33d;
|
362 | c58d37cf | cmchao | if (((value >> 3) & 3) == 3) /* IDLEMODE */ |
363 | c58d37cf | cmchao | fprintf(stderr, "%s: illegal IDLEMODE value in TIOCP_CFG\n",
|
364 | c58d37cf | cmchao | __FUNCTION__); |
365 | c58d37cf | cmchao | if (value & 2) /* SOFTRESET */ |
366 | c58d37cf | cmchao | omap_gp_timer_reset(s); |
367 | c58d37cf | cmchao | break;
|
368 | c58d37cf | cmchao | |
369 | c58d37cf | cmchao | case 0x18: /* TISR */ |
370 | c58d37cf | cmchao | if (value & GPT_TCAR_IT)
|
371 | c58d37cf | cmchao | s->capt_num = 0;
|
372 | c58d37cf | cmchao | if (s->status && !(s->status &= ~value))
|
373 | c58d37cf | cmchao | qemu_irq_lower(s->irq); |
374 | c58d37cf | cmchao | break;
|
375 | c58d37cf | cmchao | |
376 | c58d37cf | cmchao | case 0x1c: /* TIER */ |
377 | c58d37cf | cmchao | s->it_ena = value & 7;
|
378 | c58d37cf | cmchao | break;
|
379 | c58d37cf | cmchao | |
380 | c58d37cf | cmchao | case 0x20: /* TWER */ |
381 | c58d37cf | cmchao | s->wu_ena = value & 7;
|
382 | c58d37cf | cmchao | break;
|
383 | c58d37cf | cmchao | |
384 | c58d37cf | cmchao | case 0x24: /* TCLR */ |
385 | c58d37cf | cmchao | omap_gp_timer_sync(s); |
386 | c58d37cf | cmchao | s->inout = (value >> 14) & 1; |
387 | c58d37cf | cmchao | s->capt2 = (value >> 13) & 1; |
388 | c58d37cf | cmchao | s->pt = (value >> 12) & 1; |
389 | c58d37cf | cmchao | s->trigger = (value >> 10) & 3; |
390 | c58d37cf | cmchao | if (s->capture == gpt_capture_none &&
|
391 | c58d37cf | cmchao | ((value >> 8) & 3) != gpt_capture_none) |
392 | c58d37cf | cmchao | s->capt_num = 0;
|
393 | c58d37cf | cmchao | s->capture = (value >> 8) & 3; |
394 | c58d37cf | cmchao | s->scpwm = (value >> 7) & 1; |
395 | c58d37cf | cmchao | s->ce = (value >> 6) & 1; |
396 | c58d37cf | cmchao | s->pre = (value >> 5) & 1; |
397 | c58d37cf | cmchao | s->ptv = (value >> 2) & 7; |
398 | c58d37cf | cmchao | s->ar = (value >> 1) & 1; |
399 | c58d37cf | cmchao | s->st = (value >> 0) & 1; |
400 | c58d37cf | cmchao | if (s->inout && s->trigger != gpt_trigger_none)
|
401 | c58d37cf | cmchao | fprintf(stderr, "%s: GP timer pin must be an output "
|
402 | c58d37cf | cmchao | "for this trigger mode\n", __FUNCTION__);
|
403 | c58d37cf | cmchao | if (!s->inout && s->capture != gpt_capture_none)
|
404 | c58d37cf | cmchao | fprintf(stderr, "%s: GP timer pin must be an input "
|
405 | c58d37cf | cmchao | "for this capture mode\n", __FUNCTION__);
|
406 | c58d37cf | cmchao | if (s->trigger == gpt_trigger_none)
|
407 | c58d37cf | cmchao | omap_gp_timer_out(s, s->scpwm); |
408 | c58d37cf | cmchao | /* TODO: make sure this doesn't overflow 32-bits */
|
409 | c58d37cf | cmchao | s->ticks_per_sec = get_ticks_per_sec() << (s->pre ? s->ptv + 1 : 0); |
410 | c58d37cf | cmchao | omap_gp_timer_update(s); |
411 | c58d37cf | cmchao | break;
|
412 | c58d37cf | cmchao | |
413 | c58d37cf | cmchao | case 0x28: /* TCRR */ |
414 | 74475455 | Paolo Bonzini | s->time = qemu_get_clock_ns(vm_clock); |
415 | c58d37cf | cmchao | s->val = value; |
416 | c58d37cf | cmchao | omap_gp_timer_update(s); |
417 | c58d37cf | cmchao | break;
|
418 | c58d37cf | cmchao | |
419 | c58d37cf | cmchao | case 0x2c: /* TLDR */ |
420 | c58d37cf | cmchao | s->load_val = value; |
421 | c58d37cf | cmchao | break;
|
422 | c58d37cf | cmchao | |
423 | c58d37cf | cmchao | case 0x30: /* TTGR */ |
424 | 74475455 | Paolo Bonzini | s->time = qemu_get_clock_ns(vm_clock); |
425 | c58d37cf | cmchao | s->val = s->load_val; |
426 | c58d37cf | cmchao | omap_gp_timer_update(s); |
427 | c58d37cf | cmchao | break;
|
428 | c58d37cf | cmchao | |
429 | c58d37cf | cmchao | case 0x38: /* TMAR */ |
430 | c58d37cf | cmchao | omap_gp_timer_sync(s); |
431 | c58d37cf | cmchao | s->match_val = value; |
432 | c58d37cf | cmchao | omap_gp_timer_update(s); |
433 | c58d37cf | cmchao | break;
|
434 | c58d37cf | cmchao | |
435 | c58d37cf | cmchao | case 0x40: /* TSICR */ |
436 | c58d37cf | cmchao | s->posted = (value >> 2) & 1; |
437 | c58d37cf | cmchao | if (value & 2) /* How much exactly are we supposed to reset? */ |
438 | c58d37cf | cmchao | omap_gp_timer_reset(s); |
439 | c58d37cf | cmchao | break;
|
440 | c58d37cf | cmchao | |
441 | c58d37cf | cmchao | default:
|
442 | c58d37cf | cmchao | OMAP_BAD_REG(addr); |
443 | c58d37cf | cmchao | } |
444 | c58d37cf | cmchao | } |
445 | c58d37cf | cmchao | |
446 | c58d37cf | cmchao | static void omap_gp_timer_writeh(void *opaque, target_phys_addr_t addr, |
447 | c58d37cf | cmchao | uint32_t value) |
448 | c58d37cf | cmchao | { |
449 | c58d37cf | cmchao | struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) opaque; |
450 | c58d37cf | cmchao | |
451 | c58d37cf | cmchao | if (addr & 2) |
452 | c58d37cf | cmchao | return omap_gp_timer_write(opaque, addr, (value << 16) | s->writeh); |
453 | c58d37cf | cmchao | else
|
454 | c58d37cf | cmchao | s->writeh = (uint16_t) value; |
455 | c58d37cf | cmchao | } |
456 | c58d37cf | cmchao | |
457 | c58d37cf | cmchao | static CPUWriteMemoryFunc * const omap_gp_timer_writefn[] = { |
458 | c58d37cf | cmchao | omap_badwidth_write32, |
459 | c58d37cf | cmchao | omap_gp_timer_writeh, |
460 | c58d37cf | cmchao | omap_gp_timer_write, |
461 | c58d37cf | cmchao | }; |
462 | c58d37cf | cmchao | |
463 | c58d37cf | cmchao | struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, |
464 | c58d37cf | cmchao | qemu_irq irq, omap_clk fclk, omap_clk iclk) |
465 | c58d37cf | cmchao | { |
466 | c58d37cf | cmchao | int iomemtype;
|
467 | c58d37cf | cmchao | struct omap_gp_timer_s *s = (struct omap_gp_timer_s *) |
468 | c58d37cf | cmchao | qemu_mallocz(sizeof(struct omap_gp_timer_s)); |
469 | c58d37cf | cmchao | |
470 | c58d37cf | cmchao | s->ta = ta; |
471 | c58d37cf | cmchao | s->irq = irq; |
472 | c58d37cf | cmchao | s->clk = fclk; |
473 | 74475455 | Paolo Bonzini | s->timer = qemu_new_timer_ns(vm_clock, omap_gp_timer_tick, s); |
474 | 74475455 | Paolo Bonzini | s->match = qemu_new_timer_ns(vm_clock, omap_gp_timer_match, s); |
475 | c58d37cf | cmchao | s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0]; |
476 | c58d37cf | cmchao | omap_gp_timer_reset(s); |
477 | c58d37cf | cmchao | omap_gp_timer_clk_setup(s); |
478 | c58d37cf | cmchao | |
479 | c58d37cf | cmchao | iomemtype = l4_register_io_memory(omap_gp_timer_readfn, |
480 | c58d37cf | cmchao | omap_gp_timer_writefn, s); |
481 | c58d37cf | cmchao | omap_l4_attach(ta, 0, iomemtype);
|
482 | c58d37cf | cmchao | |
483 | c58d37cf | cmchao | return s;
|
484 | c58d37cf | cmchao | } |