|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2024 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // Goldfish (Qemu) RTC+Timer (の RTC のほう)
9: //
10:
11: // RTC デバイスのレジスタは次の通り。(QEMU 仕様)
12: //
13: // +$00.L TIME_LOW RW: ホスト時刻 [nsec] の下位 32 ビット
14: // +$04.L TIME_HIGH RW: ホスト時刻 [nsec] の上位 32 ビット
15: //
16: // TIME_LOW の動作は Timer デバイスと同じだが、こちらはホストの RTC 時刻
17: // (実世界時間) を秒の粒度で返す。つまり time() * 1e9 だと仕様書にはある。
18: // 書き込みも可能 (当然 VM の RTC を書き換える)。
19: // アラームと割り込み機能は一切ないが、レジスタマップは Timer デバイスと
20: // 同じものを使うらしい。
21:
22: #include "goldfish_rtc.h"
1.1.1.4 ! root 23: #include "event.h"
1.1.1.2 root 24: #include "mpu.h"
1.1 root 25: #include "textscreen.h"
26:
27: //
28: // GFRTC
29: //
30:
31: // コンストラクタ
32: GFRTCDevice::GFRTCDevice()
33: : inherited()
34: {
35: }
36:
37: // デストラクタ
38: GFRTCDevice::~GFRTCDevice()
39: {
40: }
41:
42: // リセット
43: void
44: GFRTCDevice::ResetHard(bool poweron)
45: {
46: time_nsec = 0;
47: }
48:
49: busdata
1.1.1.2 root 50: GFRTCDevice::ReadPort(uint32 offset)
1.1 root 51: {
52: busdata data;
53:
54: switch (offset) {
55: case GFRTC::TIME_LOW:
56: time_nsec = (uint64)MkTime() * 1000'000'000U;
57: data = (uint32)time_nsec;
58: if (__predict_false(loglevel >= 1)) {
59: if (loglevel >= 3) {
60: putlogn("TIME_LOW -> $%08x", data.Data());
61: } else {
62: putlogn("TIME -> $%08x'%08x",
63: (uint32)(time_nsec >> 32), data.Data());
64: }
1.1.1.2 root 65:
66: if (loglevel >= 2) {
67: struct tm tm, tr;
68: time_t t = (time_t)(time_nsec / 1_sec);
69: gmtime_r(&t, &tm);
70: putlogn("TIME / 1e9: %4u/%02u/%02u %02u:%02u:%02u",
71: tm.tm_year + 1900,
72: tm.tm_mon + 1,
73: tm.tm_mday,
74: tm.tm_hour,
75: tm.tm_min,
76: tm.tm_sec);
77:
78: struct timeval tv;
79: gettimeofday(&tv, NULL);
80: gmtime_r(&tv.tv_sec, &tr);
81: putlogn("gettimeofday: %4u/%02u/%02u %02u:%02u:%02u",
82: tr.tm_year + 1900,
83: tr.tm_mon + 1,
84: tr.tm_mday,
85: tr.tm_hour,
86: tr.tm_min,
87: tr.tm_sec);
88: }
1.1 root 89: }
90: break;
91:
92: case GFRTC::TIME_HIGH:
93: data = (uint32)(time_nsec >> 32);
94: putlog(3, "TIME_HIGH -> $%08x", data.Data());
95: break;
96:
97: case GFRTC::INTR_STATUS:
98: putlog(2, "Read INTR_STATUS (No function)");
99: data = 0;
100: break;
101:
102: case GFRTC::ALARM_STATUS:
103: putlog(2, "Read ALARM_STATUS (No function)");
104: data = 0;
105: break;
106:
107: default:
1.1.1.2 root 108: putlog(1, "Read unknown $%08x", mpu->GetPaddr());
1.1 root 109: data.SetBusErr();
110: break;
111: }
1.1.1.2 root 112:
113: data |= BusData::Size4;
1.1 root 114: return data;
115: }
116:
117: busdata
1.1.1.2 root 118: GFRTCDevice::WritePort(uint32 offset, uint32 data)
1.1 root 119: {
120: busdata r;
121:
122: switch (offset) {
123: case GFRTC::TIME_LOW:
124: {
125: time_nsec |= data;
126:
127: if (__predict_false(loglevel >= 1)) {
128: if (loglevel >= 2) {
129: putlogn("TIME_LOW <- $%08x", (uint32)time_nsec);
130: } else {
131: putlogn("TIME <- $%08x'%08x",
132: (uint32)(time_nsec >> 32), (uint32)time_nsec);
133: }
134: }
135:
136: time_t t = time_nsec / 1000'000'000U;
137: struct tm tm;
138: gmtime_r(&t, &tm);
139: SetYear(tm.tm_year + 1900);
140: SetMon(tm.tm_mon + 1);
141: SetMday(tm.tm_mday);
142: SetHour(tm.tm_hour);
143: SetMin(tm.tm_min);
144: SetSec(tm.tm_sec);
145: break;
146: }
147:
148: case GFRTC::TIME_HIGH:
149: putlog(2, "TIME_HIGH <- $%08x", data);
150: time_nsec = (uint64)data << 32;
151: break;
152:
153: case GFRTC::ALARM_LOW:
154: putlog(2, "ALARM_LOW <- $%08x (No function)", data);
155: break;
156:
157: case GFRTC::ALARM_HIGH:
158: putlog(2, "ALARM_HIGH <- $%08x (No function)", data);
159: break;
160:
161: case GFRTC::INTR_STATUS:
162: putlog(2, "INTR_STATUS <- $%08x (No function)", data);
163: break;
164:
165: case GFRTC::ALARM_CLEAR:
166: putlog(2, "ALARM_CLEAR <- $%08x (No function)", data);
167: break;
168:
169: case GFRTC::INTR_CLEAR:
170: putlog(2, "INTR_CLEAR <- $%08x (No function)", data);
171: break;
172:
173: default:
1.1.1.2 root 174: putlog(1, "Write unknown $%08x <- $%08x", mpu->GetPaddr(), data);
1.1 root 175: r.SetBusErr();
176: break;
177: }
1.1.1.2 root 178:
179: r |= BusData::Size4;
1.1 root 180: return r;
181: }
182:
183: busdata
1.1.1.2 root 184: GFRTCDevice::PeekPort(uint32 offset)
1.1 root 185: {
186: switch (offset) {
1.1.1.2 root 187: case GFRTC::TIME_LOW:
1.1 root 188: {
189: uint64 now = MkTime() * 1000'000'000U;
1.1.1.2 root 190: return now & 0xffffffffU;
1.1 root 191: }
1.1.1.2 root 192: case GFRTC::TIME_HIGH:
193: {
194: uint64 now = MkTime() * 1000'000'000U;
195: return now >> 32;
196: }
197: case GFRTC::INTR_STATUS:
198: case GFRTC::ALARM_STATUS:
1.1 root 199: return 0;
200: default:
1.1.1.2 root 201: return BusData::BusErr;
1.1 root 202: }
203: }
204:
205: // モニタの下請け。(GFTimer から呼ばれる)
206: int
207: GFRTCDevice::MonitorUpdateRTC(TextScreen& screen, int y) const
208: {
209: screen.Puts(0, y++, "<RTC>");
210: screen.Print(0, y++, "%04u/%02u/%02u(%s) %02u:%02u:%02u",
211: year, mon, day, wdays[GetWday()], hour, min, sec);
212: screen.Puts(0, y++, "TimeZone: UTC");
213:
214: return y;
215: }
216:
217: void
218: GFRTCDevice::Tick1Hz()
219: {
220: CountUpSec();
221: }
222:
223: // 内部時刻から time_t を作成する。
224: time_t
225: GFRTCDevice::MkTime() const
226: {
227: struct tm tm;
228:
229: tm.tm_year = year - 1900;
230: tm.tm_mon = mon - 1;
231: tm.tm_mday = day;
232: tm.tm_hour = hour;
233: tm.tm_min = min;
234: tm.tm_sec = sec;
235:
236: return timegm(&tm);
237: }
238:
239: uint
240: GFRTCDevice::GetWday() const
241: {
242: // 曜日は保持していないので一旦日付を作ってから求める。
243: struct tm tm;
244: time_t t = MkTime();
245:
246: gmtime_r(&t, &tm);
247: return tm.tm_wday;
248: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.