|
|
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.2 ! root 23: #include "mpu.h"
1.1 root 24: #include "textscreen.h"
25:
26: //
27: // GFRTC
28: //
29:
30: // コンストラクタ
31: GFRTCDevice::GFRTCDevice()
32: : inherited()
33: {
34: }
35:
36: // デストラクタ
37: GFRTCDevice::~GFRTCDevice()
38: {
39: }
40:
41: // 初期化
42: bool
43: GFRTCDevice::Init()
44: {
45: if (inherited::Init() == false) {
46: return false;
47: }
48:
49: return true;
50: }
51:
52: // リセット
53: void
54: GFRTCDevice::ResetHard(bool poweron)
55: {
56: time_nsec = 0;
57: }
58:
59: busdata
1.1.1.2 ! root 60: GFRTCDevice::ReadPort(uint32 offset)
1.1 root 61: {
62: busdata data;
63:
64: switch (offset) {
65: case GFRTC::TIME_LOW:
66: time_nsec = (uint64)MkTime() * 1000'000'000U;
67: data = (uint32)time_nsec;
68: if (__predict_false(loglevel >= 1)) {
69: if (loglevel >= 3) {
70: putlogn("TIME_LOW -> $%08x", data.Data());
71: } else {
72: putlogn("TIME -> $%08x'%08x",
73: (uint32)(time_nsec >> 32), data.Data());
74: }
1.1.1.2 ! root 75:
! 76: if (loglevel >= 2) {
! 77: struct tm tm, tr;
! 78: time_t t = (time_t)(time_nsec / 1_sec);
! 79: gmtime_r(&t, &tm);
! 80: putlogn("TIME / 1e9: %4u/%02u/%02u %02u:%02u:%02u",
! 81: tm.tm_year + 1900,
! 82: tm.tm_mon + 1,
! 83: tm.tm_mday,
! 84: tm.tm_hour,
! 85: tm.tm_min,
! 86: tm.tm_sec);
! 87:
! 88: struct timeval tv;
! 89: gettimeofday(&tv, NULL);
! 90: gmtime_r(&tv.tv_sec, &tr);
! 91: putlogn("gettimeofday: %4u/%02u/%02u %02u:%02u:%02u",
! 92: tr.tm_year + 1900,
! 93: tr.tm_mon + 1,
! 94: tr.tm_mday,
! 95: tr.tm_hour,
! 96: tr.tm_min,
! 97: tr.tm_sec);
! 98: }
1.1 root 99: }
100: break;
101:
102: case GFRTC::TIME_HIGH:
103: data = (uint32)(time_nsec >> 32);
104: putlog(3, "TIME_HIGH -> $%08x", data.Data());
105: break;
106:
107: case GFRTC::INTR_STATUS:
108: putlog(2, "Read INTR_STATUS (No function)");
109: data = 0;
110: break;
111:
112: case GFRTC::ALARM_STATUS:
113: putlog(2, "Read ALARM_STATUS (No function)");
114: data = 0;
115: break;
116:
117: default:
1.1.1.2 ! root 118: putlog(1, "Read unknown $%08x", mpu->GetPaddr());
1.1 root 119: data.SetBusErr();
120: break;
121: }
1.1.1.2 ! root 122:
! 123: data |= BusData::Size4;
1.1 root 124: return data;
125: }
126:
127: busdata
1.1.1.2 ! root 128: GFRTCDevice::WritePort(uint32 offset, uint32 data)
1.1 root 129: {
130: busdata r;
131:
132: switch (offset) {
133: case GFRTC::TIME_LOW:
134: {
135: time_nsec |= data;
136:
137: if (__predict_false(loglevel >= 1)) {
138: if (loglevel >= 2) {
139: putlogn("TIME_LOW <- $%08x", (uint32)time_nsec);
140: } else {
141: putlogn("TIME <- $%08x'%08x",
142: (uint32)(time_nsec >> 32), (uint32)time_nsec);
143: }
144: }
145:
146: time_t t = time_nsec / 1000'000'000U;
147: struct tm tm;
148: gmtime_r(&t, &tm);
149: SetYear(tm.tm_year + 1900);
150: SetMon(tm.tm_mon + 1);
151: SetMday(tm.tm_mday);
152: SetHour(tm.tm_hour);
153: SetMin(tm.tm_min);
154: SetSec(tm.tm_sec);
155: break;
156: }
157:
158: case GFRTC::TIME_HIGH:
159: putlog(2, "TIME_HIGH <- $%08x", data);
160: time_nsec = (uint64)data << 32;
161: break;
162:
163: case GFRTC::ALARM_LOW:
164: putlog(2, "ALARM_LOW <- $%08x (No function)", data);
165: break;
166:
167: case GFRTC::ALARM_HIGH:
168: putlog(2, "ALARM_HIGH <- $%08x (No function)", data);
169: break;
170:
171: case GFRTC::INTR_STATUS:
172: putlog(2, "INTR_STATUS <- $%08x (No function)", data);
173: break;
174:
175: case GFRTC::ALARM_CLEAR:
176: putlog(2, "ALARM_CLEAR <- $%08x (No function)", data);
177: break;
178:
179: case GFRTC::INTR_CLEAR:
180: putlog(2, "INTR_CLEAR <- $%08x (No function)", data);
181: break;
182:
183: default:
1.1.1.2 ! root 184: putlog(1, "Write unknown $%08x <- $%08x", mpu->GetPaddr(), data);
1.1 root 185: r.SetBusErr();
186: break;
187: }
1.1.1.2 ! root 188:
! 189: r |= BusData::Size4;
1.1 root 190: return r;
191: }
192:
193: busdata
1.1.1.2 ! root 194: GFRTCDevice::PeekPort(uint32 offset)
1.1 root 195: {
196: switch (offset) {
1.1.1.2 ! root 197: case GFRTC::TIME_LOW:
1.1 root 198: {
199: uint64 now = MkTime() * 1000'000'000U;
1.1.1.2 ! root 200: return now & 0xffffffffU;
1.1 root 201: }
1.1.1.2 ! root 202: case GFRTC::TIME_HIGH:
! 203: {
! 204: uint64 now = MkTime() * 1000'000'000U;
! 205: return now >> 32;
! 206: }
! 207: case GFRTC::INTR_STATUS:
! 208: case GFRTC::ALARM_STATUS:
1.1 root 209: return 0;
210: default:
1.1.1.2 ! root 211: return BusData::BusErr;
1.1 root 212: }
213: }
214:
215: // モニタの下請け。(GFTimer から呼ばれる)
216: int
217: GFRTCDevice::MonitorUpdateRTC(TextScreen& screen, int y) const
218: {
219: screen.Puts(0, y++, "<RTC>");
220: screen.Print(0, y++, "%04u/%02u/%02u(%s) %02u:%02u:%02u",
221: year, mon, day, wdays[GetWday()], hour, min, sec);
222: screen.Puts(0, y++, "TimeZone: UTC");
223:
224: return y;
225: }
226:
227: void
228: GFRTCDevice::Tick1Hz()
229: {
230: CountUpSec();
231: }
232:
233: // 内部時刻から time_t を作成する。
234: time_t
235: GFRTCDevice::MkTime() const
236: {
237: struct tm tm;
238:
239: tm.tm_year = year - 1900;
240: tm.tm_mon = mon - 1;
241: tm.tm_mday = day;
242: tm.tm_hour = hour;
243: tm.tm_min = min;
244: tm.tm_sec = sec;
245:
246: return timegm(&tm);
247: }
248:
249: uint
250: GFRTCDevice::GetWday() const
251: {
252: // 曜日は保持していないので一旦日付を作ってから求める。
253: struct tm tm;
254: time_t t = MkTime();
255:
256: gmtime_r(&t, &tm);
257: return tm.tm_wday;
258: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.