|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2018 [email protected] ! 4: // ! 5: ! 6: #include "mk48t02.h" ! 7: #include "configfile.h" ! 8: #include <sys/fcntl.h> ! 9: #include <sys/mman.h> ! 10: ! 11: // ! 12: // MK48T02 ! 13: // ! 14: // SRAM 部分はビッグエンディアンのままメモリに置かれるので、 ! 15: // 必要ならアクセス時に都度変換すること。 ! 16: // ! 17: // 仕様は 0x45000000 から 2KB だが、 ! 18: // 0x44000000 から 0x48000000 の手前まで 2KB 単位でミラーが見える。 ! 19: ! 20: MK48T02Device::MK48T02Device() ! 21: { ! 22: logname = "mk48t02"; ! 23: devname = "MK48T02"; ! 24: devaddr = 0x45000000; ! 25: monitor.Init(25, 2); ! 26: ! 27: fd = -1; ! 28: } ! 29: ! 30: MK48T02Device::~MK48T02Device() ! 31: { ! 32: if (mem) { ! 33: munmap(mem, memlen); ! 34: mem = NULL; ! 35: } ! 36: if (fd >= 0) { ! 37: close(fd); ! 38: fd = -1; ! 39: } ! 40: } ! 41: ! 42: bool ! 43: MK48T02Device::Init() ! 44: { ! 45: // ファイルのロード ! 46: filename = gConfig->GetConfigDir() + "NVRAM.DAT"; ! 47: ! 48: fd = open(filename.c_str(), O_RDWR); ! 49: if (fd == -1) { ! 50: warn("%s: cannot open %s", devname, filename.c_str()); ! 51: return false; ! 52: } ! 53: ! 54: memlen = 2040; ! 55: mem = (uint8 *)mmap(NULL, memlen, PROT_READ | PROT_WRITE, ! 56: MAP_SHARED, fd, 0); ! 57: if (mem == MAP_FAILED) { ! 58: close(fd); ! 59: warn("%s: cannot mmap %s", devname, filename.c_str()); ! 60: return false; ! 61: } ! 62: ! 63: // レジスタ値固定オプション (パフォーマンス測定用) ! 64: force_fixed = gConfig->ReadInt("rtc_force_fixed"); ! 65: if (force_fixed) { ! 66: putmsg("force_fixed"); ! 67: } ! 68: return true; ! 69: } ! 70: ! 71: // RTC を初期化 ! 72: bool ! 73: MK48T02Device::InitRTC() ! 74: { ! 75: // ホストの時刻を UTC で取得 ! 76: LoadHostRTC(false); ! 77: ! 78: return true; ! 79: } ! 80: ! 81: // バイナリ表現を BCD 表現に変換する ! 82: /*static*/ inline uint8 ! 83: MK48T02Device::num2BCD(uint8 num) ! 84: { ! 85: return ((num / 10) << 4) + (num % 10); ! 86: } ! 87: ! 88: // BCD 表現をバイナリ表現に変換する ! 89: /*static*/ inline uint8 ! 90: MK48T02Device::BCD2num(uint8 bcd) ! 91: { ! 92: return (bcd >> 4) * 10 + (bcd & 0x0f); ! 93: } ! 94: ! 95: uint64 ! 96: MK48T02Device::Read8(uint32 addr) ! 97: { ! 98: // アドレスは 2KB 単位でミラーになっている。 ! 99: uint32 an = addr & 0x7ff; ! 100: uint32 data; ! 101: ! 102: if (an < 2040) { ! 103: data = mem[an]; ! 104: putlog(3, "$%08x -> $%02x", addr, data); ! 105: } else { ! 106: // XXX ReadBit が立っていない時は何が読めるのか ! 107: switch (an - 2040) { ! 108: case 0: // コントロールレジスタ ! 109: data = reg.ctrl; ! 110: break; ! 111: case 1: // 秒 ! 112: data = reg.sec; ! 113: if (reg.stop) { ! 114: data |= MK48T02Clock::STOP; ! 115: } ! 116: break; ! 117: case 2: // 分 ! 118: data = reg.min; ! 119: break; ! 120: case 3: // 時 ! 121: data = reg.hour; ! 122: if (reg.kickstart) { ! 123: data |= MK48T02Clock::KICKSTART; ! 124: } ! 125: break; ! 126: case 4: // 曜日 ! 127: data = reg.wday; ! 128: break; ! 129: case 5: // 日 ! 130: data = reg.mday; ! 131: break; ! 132: case 6: // 月 ! 133: data = reg.mon; ! 134: break; ! 135: case 7: // 年 ! 136: data = reg.year; ! 137: break; ! 138: default: ! 139: putlog(0, "未実装バイト読み込み $%08x", addr); ! 140: data = 0xff; ! 141: break; ! 142: } ! 143: ! 144: if (force_fixed) { ! 145: // パフォーマンス測定をする際に、RTC が実時間を返すと ! 146: // ゲストの状態が毎回異なることになるため、 ! 147: // いつ何時も同じ値を返すことで、世界から隔絶された ! 148: // VM を作り、パフォーマンス測定を安定させる。 ! 149: data = 3; ! 150: } ! 151: } ! 152: return data; ! 153: } ! 154: ! 155: uint64 ! 156: MK48T02Device::Read16(uint32 addr) ! 157: { ! 158: // アドレスは 2KB 単位でミラーになっている。 ! 159: uint32 an = addr & 0x7ff; ! 160: uint32 data; ! 161: ! 162: if (an < 2040) { ! 163: data = be16toh(*(uint16 *)&mem[an]); ! 164: putlog(3, "$%08x -> $%04x", addr, data); ! 165: } else { ! 166: putlog(0, "未実装ワード読み込み $%08x", addr); ! 167: data = 0xffff; ! 168: } ! 169: return data; ! 170: } ! 171: ! 172: uint64 ! 173: MK48T02Device::Read32(uint32 addr) ! 174: { ! 175: // アドレスは 2KB 単位でミラーになっている。 ! 176: uint32 an = addr & 0x7ff; ! 177: uint32 data; ! 178: ! 179: if (an < 2040) { ! 180: data = be32toh(*(uint32 *)&mem[an]); ! 181: putlog(3, "$%08x -> $%08x", addr, data); ! 182: } else { ! 183: putlog(0, "未実装ロング読み込み $%08x", addr); ! 184: data = 0xffffffff; ! 185: } ! 186: return data; ! 187: } ! 188: ! 189: uint64 ! 190: MK48T02Device::Write8(uint32 addr, uint32 data) ! 191: { ! 192: // アドレスは 2KB 単位でミラーになっている。 ! 193: uint32 an = addr & 0x7ff; ! 194: ! 195: if (an < 2040) { ! 196: putlog(3, "$%08x <- $%02x", addr, data); ! 197: mem[an] = data; ! 198: } else { ! 199: switch (an - 2040) { ! 200: case 0: // コントロール ! 201: WriteCtrl(data); ! 202: break; ! 203: case 1: // 秒 (b7 は STOP BIT) ! 204: reg.stop = (data & MK48T02Clock::STOP); ! 205: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 206: rtc.sec = BCD2num(data & 0x7f); ! 207: } ! 208: break; ! 209: case 2: // 分 ! 210: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 211: rtc.min = BCD2num(data & 0x7f); ! 212: } ! 213: break; ! 214: case 3: // 時 (b7 は KICK START) ! 215: reg.kickstart = (data & MK48T02Clock::KICKSTART); ! 216: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 217: rtc.hour = BCD2num(data & 0x3f); ! 218: } ! 219: break; ! 220: case 4: // 曜日 (b6 は Frequency Test (未実装)) ! 221: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 222: rtc.wday = data & 0x07; ! 223: } ! 224: break; ! 225: case 5: // 日 ! 226: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 227: rtc.mday = BCD2num(data & 0x3f); ! 228: } ! 229: break; ! 230: case 6: // 月 ! 231: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 232: rtc.mon = BCD2num(data & 0x1f); ! 233: } ! 234: break; ! 235: case 7: // 年 ! 236: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 237: rtc.year = BCD2num(data) + MK48T02Clock::YEAR_EPOCH; ! 238: } ! 239: break; ! 240: default: ! 241: putlog(0, "未実装バイト書き込み $%08x <- %02x", addr, data); ! 242: break; ! 243: } ! 244: } ! 245: return 0; ! 246: } ! 247: ! 248: uint64 ! 249: MK48T02Device::Write16(uint32 addr, uint32 data) ! 250: { ! 251: // アドレスは 2KB 単位でミラーになっている。 ! 252: uint32 an = addr & 0x7ff; ! 253: ! 254: if (an < 2040) { ! 255: putlog(3, "$%08x <- $%04x", addr, data); ! 256: *(uint16 *)&mem[an] = htobe16(data); ! 257: } else { ! 258: putlog(0, "未実装ワード書き込み $%08x <- %04x", addr, data); ! 259: } ! 260: return 0; ! 261: } ! 262: ! 263: uint64 ! 264: MK48T02Device::Write32(uint32 addr, uint32 data) ! 265: { ! 266: // アドレスは 2KB 単位でミラーになっている。 ! 267: uint32 an = addr & 0x7ff; ! 268: ! 269: if (an < 2040) { ! 270: putlog(3, "$%08x <- $%08x", addr, data); ! 271: *(uint32 *)&mem[an] = htobe32(data); ! 272: } else { ! 273: putlog(0, "未実装ロング書き込み $%08x <- %08x", addr, data); ! 274: } ! 275: return 0; ! 276: } ! 277: ! 278: uint64 ! 279: MK48T02Device::Peek8(uint32 addr) ! 280: { ! 281: // アドレスは 2KB 単位でミラーになっている。 ! 282: uint32 an = addr & 0x7ff; ! 283: uint8 data; ! 284: ! 285: if (an < 2040) { ! 286: return mem[an]; ! 287: } else { ! 288: switch (an - 2040) { ! 289: case 0: // コントロールレジスタ ! 290: return reg.ctrl; ! 291: case 1: // 秒 | STOP ! 292: data = reg.sec; ! 293: if (reg.stop) { ! 294: data |= MK48T02Clock::STOP; ! 295: } ! 296: return data; ! 297: case 2: // 分 ! 298: return reg.min; ! 299: case 3: // 時 ! 300: data = reg.hour; ! 301: if (reg.kickstart) { ! 302: data |= MK48T02Clock::KICKSTART; ! 303: } ! 304: return data; ! 305: case 4: // 曜日 ! 306: return reg.wday; ! 307: case 5: // 日 ! 308: return reg.mday; ! 309: case 6: // 月 ! 310: return reg.mon; ! 311: case 7: // 年 ! 312: return reg.year; ! 313: default: ! 314: return 0xff; ! 315: } ! 316: } ! 317: } ! 318: ! 319: bool ! 320: MK48T02Device::MonitorUpdate() ! 321: { ! 322: monitor.Clear(); ! 323: ! 324: monitor.Print(0, 0, "WR:%d RD:%d", ! 325: (reg.ctrl & MK48T02Clock::WRITE) ? 1 : 0, ! 326: (reg.ctrl & MK48T02Clock::READ) ? 1 : 0); ! 327: monitor.Print(0, 1, "ST:%d KS:%d", ! 328: reg.stop, ! 329: reg.kickstart); ! 330: // XXX レジスタの値はラッチされるまで意味がないので ! 331: // どうしたものか。 ! 332: monitor.Print(10, 0, "%4d/%02d/%02d(%s)", ! 333: rtc.year, rtc.mon, rtc.mday, wdays[rtc.wday]); ! 334: monitor.Print(10, 1, "%02d:%02d:%02d", ! 335: rtc.hour, rtc.min, rtc.sec); ! 336: ! 337: return true; ! 338: } ! 339: ! 340: // コントロールレジスタへの書き込み ! 341: void ! 342: MK48T02Device::WriteCtrl(uint32 data) ! 343: { ! 344: reg.ctrl = data; ! 345: ! 346: if ((reg.ctrl & MK48T02Clock::WRITE)) { ! 347: // WRITE ビットを立てると計時を止める ! 348: rtc.enable_sec = false; ! 349: } else { ! 350: // WRITE ビットを下ろすと計時を再開して ! 351: rtc.enable_sec = true; ! 352: // KickStart, FreqTest をゼロにする ! 353: reg.kickstart = false; ! 354: } ! 355: ! 356: if ((reg.ctrl & MK48T02Clock::READ)) { ! 357: // READ ビットを立てたところで計時をとめて(?) ! 358: // 値をレジスタにラッチ(?) ! 359: rtc.enable_sec = false; ! 360: reg.sec = num2BCD(rtc.sec); ! 361: reg.min = num2BCD(rtc.min); ! 362: reg.hour = num2BCD(rtc.hour); ! 363: reg.wday = rtc.wday; ! 364: reg.mday = num2BCD(rtc.mday); ! 365: reg.mon = num2BCD(rtc.mon); ! 366: reg.year = num2BCD(rtc.year - MK48T02Clock::YEAR_EPOCH); ! 367: } else { ! 368: // READ ビットを下ろすと計時を再開 ! 369: rtc.enable_sec = true; ! 370: } ! 371: } ! 372: ! 373: // 32Hz クロック入力 ! 374: // ! 375: // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。 ! 376: // RTC を実時間に同期して進めるモードなら実時間を計るスケジューラから、 ! 377: // RTC を仮想時間に同期して進めるモードなら VM スケジューラから呼ばれる。 ! 378: void ! 379: MK48T02Device::ClockIn() ! 380: { ! 381: // 32Hz カウンタを進める。 ! 382: // 内部表現とデバイスから読み出せる値が同じなので、更新処理は不要。 ! 383: CountUp(); ! 384: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.