blob: a714b22713cf04cc8cb7bd3a83f6819535109a6a [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
David 'Digit' Turnera7fb77d2010-05-10 23:50:54 -070012#include "qemu-common.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080013#include "qemu-timer.h"
14#include "cpu.h"
15#include "arm_pic.h"
16#include "goldfish_device.h"
Ot ten Thijea7f114b2010-09-06 15:03:52 +010017#include "hw/hw.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080018
19enum {
20 TIMER_TIME_LOW = 0x00, // get low bits of current time and update TIMER_TIME_HIGH
21 TIMER_TIME_HIGH = 0x04, // get high bits of time at last TIMER_TIME_LOW read
22 TIMER_ALARM_LOW = 0x08, // set low bits of alarm and activate it
23 TIMER_ALARM_HIGH = 0x0c, // set high bits of next alarm
24 TIMER_CLEAR_INTERRUPT = 0x10,
25 TIMER_CLEAR_ALARM = 0x14
26};
27
28struct timer_state {
29 struct goldfish_device dev;
Ot ten Thijea7f114b2010-09-06 15:03:52 +010030 uint32_t alarm_low_ns;
31 int32_t alarm_high_ns;
32 int64_t now_ns;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080033 int armed;
34 QEMUTimer *timer;
35};
36
Ot ten Thijea7f114b2010-09-06 15:03:52 +010037/* Converts nanoseconds into ticks */
38static int64_t ns2tks(int64_t ns) {
39 return muldiv64(ns, get_ticks_per_sec(), 1000000000);
40}
41
42/* Converts ticks into nanoseconds */
43static int64_t tks2ns(int64_t tks) {
44 return muldiv64(tks, 1000000000, get_ticks_per_sec());
45}
46
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080047#define GOLDFISH_TIMER_SAVE_VERSION 1
48
49static void goldfish_timer_save(QEMUFile* f, void* opaque)
50{
51 struct timer_state* s = opaque;
52
Ot ten Thijea7f114b2010-09-06 15:03:52 +010053 qemu_put_be64(f, s->now_ns); /* in case the kernel is in the middle of a timer read */
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080054 qemu_put_byte(f, s->armed);
55 if (s->armed) {
Ot ten Thijea7f114b2010-09-06 15:03:52 +010056 int64_t now_tks = qemu_get_clock(vm_clock);
57 int64_t alarm_tks = ns2tks(s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32);
58 qemu_put_be64(f, alarm_tks - now_tks);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080059 }
60}
61
62static int goldfish_timer_load(QEMUFile* f, void* opaque, int version_id)
63{
64 struct timer_state* s = opaque;
65
66 if (version_id != GOLDFISH_TIMER_SAVE_VERSION)
67 return -1;
68
Ot ten Thijeafb0f8e2010-09-22 15:47:33 +010069 s->now_ns = qemu_get_be64(f);
Ot ten Thijea7f114b2010-09-06 15:03:52 +010070 s->armed = qemu_get_byte(f);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080071 if (s->armed) {
Ot ten Thijea7f114b2010-09-06 15:03:52 +010072 int64_t now_tks = qemu_get_clock(vm_clock);
73 int64_t diff_tks = qemu_get_be64(f);
74 int64_t alarm_tks = now_tks + diff_tks;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080075
Ot ten Thijea7f114b2010-09-06 15:03:52 +010076 if (alarm_tks <= now_tks) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080077 goldfish_device_set_irq(&s->dev, 0, 1);
78 s->armed = 0;
79 } else {
Ot ten Thijea7f114b2010-09-06 15:03:52 +010080 qemu_mod_timer(s->timer, alarm_tks);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080081 }
82 }
83 return 0;
84}
85
86static uint32_t goldfish_timer_read(void *opaque, target_phys_addr_t offset)
87{
88 struct timer_state *s = (struct timer_state *)opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080089 switch(offset) {
90 case TIMER_TIME_LOW:
Ot ten Thijea7f114b2010-09-06 15:03:52 +010091 s->now_ns = tks2ns(qemu_get_clock(vm_clock));
92 return s->now_ns;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080093 case TIMER_TIME_HIGH:
Ot ten Thijea7f114b2010-09-06 15:03:52 +010094 return s->now_ns >> 32;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080095 default:
96 cpu_abort (cpu_single_env, "goldfish_timer_read: Bad offset %x\n", offset);
97 return 0;
98 }
99}
100
Ot ten Thijea7f114b2010-09-06 15:03:52 +0100101static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value_ns)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800102{
103 struct timer_state *s = (struct timer_state *)opaque;
Ot ten Thijea7f114b2010-09-06 15:03:52 +0100104 int64_t alarm_tks, now_tks;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800105 switch(offset) {
106 case TIMER_ALARM_LOW:
Ot ten Thijea7f114b2010-09-06 15:03:52 +0100107 s->alarm_low_ns = value_ns;
108 alarm_tks = ns2tks(s->alarm_low_ns | (int64_t)s->alarm_high_ns << 32);
109 now_tks = qemu_get_clock(vm_clock);
110 if (alarm_tks <= now_tks) {
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800111 goldfish_device_set_irq(&s->dev, 0, 1);
112 } else {
Ot ten Thijea7f114b2010-09-06 15:03:52 +0100113 qemu_mod_timer(s->timer, alarm_tks);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800114 s->armed = 1;
115 }
116 break;
117 case TIMER_ALARM_HIGH:
Ot ten Thijea7f114b2010-09-06 15:03:52 +0100118 s->alarm_high_ns = value_ns;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800119 break;
120 case TIMER_CLEAR_ALARM:
121 qemu_del_timer(s->timer);
122 s->armed = 0;
123 /* fall through */
124 case TIMER_CLEAR_INTERRUPT:
125 goldfish_device_set_irq(&s->dev, 0, 0);
126 break;
127 default:
128 cpu_abort (cpu_single_env, "goldfish_timer_write: Bad offset %x\n", offset);
129 }
130}
131
132static void goldfish_timer_tick(void *opaque)
133{
134 struct timer_state *s = (struct timer_state *)opaque;
135
136 s->armed = 0;
137 goldfish_device_set_irq(&s->dev, 0, 1);
138}
139
140struct rtc_state {
141 struct goldfish_device dev;
142 uint32_t alarm_low;
143 int32_t alarm_high;
144 int64_t now;
145};
146
147/* we save the RTC for the case where the kernel is in the middle of a rtc_read
148 * (i.e. it has read the low 32-bit of s->now, but not the high 32-bits yet */
149#define GOLDFISH_RTC_SAVE_VERSION 1
150
151static void goldfish_rtc_save(QEMUFile* f, void* opaque)
152{
153 struct rtc_state* s = opaque;
154
155 qemu_put_be64(f, s->now);
156}
157
158static int goldfish_rtc_load(QEMUFile* f, void* opaque, int version_id)
159{
160 struct rtc_state* s = opaque;
161
162 if (version_id != GOLDFISH_RTC_SAVE_VERSION)
163 return -1;
164
165 /* this is an old value that is not correct. but that's ok anyway */
166 s->now = qemu_get_be64(f);
167 return 0;
168}
169
170static uint32_t goldfish_rtc_read(void *opaque, target_phys_addr_t offset)
171{
172 struct rtc_state *s = (struct rtc_state *)opaque;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800173 switch(offset) {
174 case 0x0:
175 s->now = (int64_t)time(NULL) * 1000000000;
176 return s->now;
177 case 0x4:
178 return s->now >> 32;
179 default:
180 cpu_abort (cpu_single_env, "goldfish_rtc_read: Bad offset %x\n", offset);
181 return 0;
182 }
183}
184
185static void goldfish_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value)
186{
187 struct rtc_state *s = (struct rtc_state *)opaque;
188 int64_t alarm;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800189 switch(offset) {
190 case 0x8:
191 s->alarm_low = value;
192 alarm = s->alarm_low | (int64_t)s->alarm_high << 32;
193 //printf("next alarm at %lld, tps %lld\n", alarm, ticks_per_sec);
194 //qemu_mod_timer(s->timer, alarm);
195 break;
196 case 0xc:
197 s->alarm_high = value;
198 //printf("alarm_high %d\n", s->alarm_high);
199 break;
200 case 0x10:
201 goldfish_device_set_irq(&s->dev, 0, 0);
202 break;
203 default:
204 cpu_abort (cpu_single_env, "goldfish_rtc_write: Bad offset %x\n", offset);
205 }
206}
207
208static struct timer_state timer_state = {
209 .dev = {
210 .name = "goldfish_timer",
211 .id = -1,
212 .size = 0x1000,
213 .irq_count = 1,
214 }
215};
216
217static struct timer_state rtc_state = {
218 .dev = {
219 .name = "goldfish_rtc",
220 .id = -1,
221 .size = 0x1000,
222 .irq_count = 1,
223 }
224};
225
226static CPUReadMemoryFunc *goldfish_timer_readfn[] = {
227 goldfish_timer_read,
228 goldfish_timer_read,
229 goldfish_timer_read
230};
231
232static CPUWriteMemoryFunc *goldfish_timer_writefn[] = {
233 goldfish_timer_write,
234 goldfish_timer_write,
235 goldfish_timer_write
236};
237
238static CPUReadMemoryFunc *goldfish_rtc_readfn[] = {
239 goldfish_rtc_read,
240 goldfish_rtc_read,
241 goldfish_rtc_read
242};
243
244static CPUWriteMemoryFunc *goldfish_rtc_writefn[] = {
245 goldfish_rtc_write,
246 goldfish_rtc_write,
247 goldfish_rtc_write
248};
249
250void goldfish_timer_and_rtc_init(uint32_t timerbase, int timerirq)
251{
252 timer_state.dev.base = timerbase;
253 timer_state.dev.irq = timerirq;
254 timer_state.timer = qemu_new_timer(vm_clock, goldfish_timer_tick, &timer_state);
255 goldfish_device_add(&timer_state.dev, goldfish_timer_readfn, goldfish_timer_writefn, &timer_state);
256 register_savevm( "goldfish_timer", 0, GOLDFISH_TIMER_SAVE_VERSION,
257 goldfish_timer_save, goldfish_timer_load, &timer_state);
258
259 goldfish_device_add(&rtc_state.dev, goldfish_rtc_readfn, goldfish_rtc_writefn, &rtc_state);
260 register_savevm( "goldfish_rtc", 0, GOLDFISH_RTC_SAVE_VERSION,
261 goldfish_rtc_save, goldfish_rtc_load, &rtc_state);
262}
263