|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2018 [email protected] ! 4: // ! 5: ! 6: #include "rp5c15.h" ! 7: #include <sys/time.h> ! 8: ! 9: // アラームは未実装 ! 10: ! 11: // XXX RTC の読み書きはロックしたほうがいいような気がする ! 12: ! 13: RP5C15Device::RP5C15Device() ! 14: { ! 15: logname = "rtc"; ! 16: devname = "RTC"; ! 17: devaddr = 0xe8a000; ! 18: devlen = 0x2000; ! 19: monitor.Init(30, 17); ! 20: } ! 21: ! 22: RP5C15Device::~RP5C15Device() ! 23: { ! 24: } ! 25: ! 26: // RTC を初期化 ! 27: bool ! 28: RP5C15Device::InitRTC() ! 29: { ! 30: // ホストの時刻をローカル時刻で取得 ! 31: LoadHostRTC(true); ! 32: ! 33: // それをレジスタに反映。 ! 34: UpdateReg(UPDATE_ALL); ! 35: ! 36: return true; ! 37: } ! 38: ! 39: uint64 ! 40: RP5C15Device::Read8(uint32 addr) ! 41: { ! 42: uint8 data; ! 43: int n; ! 44: ! 45: // 偶数アドレスへのアクセスは先にエラーにする ! 46: if (addr % 2 == 0) ! 47: return (uint64)-1; ! 48: ! 49: // Bank0 を 0..15、Bank1 を 16..31 に並べる ! 50: n = ((addr - devaddr) / 2) + bank * 0x10; ! 51: ! 52: // XXX 14, 15 は write only らしいが何が読めるか ! 53: ! 54: switch (n) { ! 55: case 0x1d: // MODE (Bank0 と同じ) ! 56: case 0x1e: // TEST (Bank0 と同じ) ! 57: case 0x1f: // RESET (Bank0 と同じ) ! 58: n -= 0x10; ! 59: FALLTHROUGH; ! 60: ! 61: case 0x00 ... 0x1c: ! 62: data = reg.r[n]; ! 63: break; ! 64: ! 65: default: ! 66: putlog(2, "RTC $%06x 未実装領域読み込み", addr); ! 67: return 0xf0; ! 68: } ! 69: ! 70: // 下位4ビット(RP5C15) の中の未実装ビットは %0 だが ! 71: // 上位4ビットは %1 になる。 ! 72: data |= 0xf0; ! 73: putlog(2, "RTC Bank%d $%x -> $%02x", bank, (n & 0x0f), data); ! 74: ! 75: return data; ! 76: } ! 77: ! 78: uint64 ! 79: RP5C15Device::Read16(uint32 addr) ! 80: { ! 81: return 0xff00 | Read8(addr + 1); ! 82: } ! 83: ! 84: uint64 ! 85: RP5C15Device::Write8(uint32 addr, uint32 data) ! 86: { ! 87: int n; ! 88: ! 89: // 偶数アドレスへのアクセスは先にエラーにする ! 90: if (addr % 2 == 0) ! 91: return (uint64)-1; ! 92: ! 93: // Bank0 を 0..15、Bank1 を 16..31 に並べる ! 94: n = ((addr - devaddr) / 2) + bank * 0x10; ! 95: ! 96: putlog(2, "RTC Bank%d $%x <- $%x", bank, (n & 0x0f), (data & 0x0f)); ! 97: ! 98: switch (n) { ! 99: case RP5C15::SEC1: ! 100: rtc.sec = reg.sec10 * 10 + (data & reg.mask[n]); ! 101: UpdateReg(UPDATE_SEC); ! 102: break; ! 103: case RP5C15::SEC10: ! 104: rtc.sec = (data & reg.mask[n]) * 10 + reg.sec1; ! 105: UpdateReg(UPDATE_SEC); ! 106: break; ! 107: ! 108: case RP5C15::MIN1: ! 109: rtc.min = reg.min10 * 10 + (data & reg.mask[n]); ! 110: UpdateReg(UPDATE_MIN); ! 111: break; ! 112: case RP5C15::MIN10: ! 113: rtc.min = (data & reg.mask[n]) * 10 + reg.min1; ! 114: UpdateReg(UPDATE_MIN); ! 115: break; ! 116: ! 117: case RP5C15::HOUR1: // hour1 ! 118: if (reg.sel24) { ! 119: // 24時間計 ! 120: rtc.hour = reg.hour10 * 10 + (data & reg.mask[n]); ! 121: } else { ! 122: // 12時間計 ! 123: rtc.hour = ((reg.hour10 & 0x02) ? 12 : 0) ! 124: + (reg.hour10 & 0x01) * 10 + (data & reg.mask[n]); ! 125: } ! 126: UpdateReg(UPDATE_HOUR); ! 127: break; ! 128: case RP5C15::HOUR10: // hour10 ! 129: if (reg.sel24) { ! 130: // 24時間計 ! 131: rtc.hour = (data & reg.mask[n]) * 10 + reg.hour1; ! 132: } else { ! 133: // 12時間計 ! 134: rtc.hour = ((data & 0x02) ? 12 : 0) ! 135: + (data & 0x01) * 10 + reg.hour1; ! 136: } ! 137: UpdateReg(UPDATE_HOUR); ! 138: break; ! 139: ! 140: case RP5C15::WDAY: ! 141: // 曜日カウンタは日付などとは一切連動していない ! 142: rtc.wday = data & reg.mask[n]; ! 143: UpdateReg(UPDATE_DAY); ! 144: break; ! 145: ! 146: case RP5C15::DAY1: ! 147: rtc.mday = reg.day10 * 10 + (data & reg.mask[n]); ! 148: UpdateReg(UPDATE_DAY); ! 149: break; ! 150: case RP5C15::DAY10: ! 151: rtc.mday = (data & reg.mask[n]) * 10 + reg.day1; ! 152: UpdateReg(UPDATE_DAY); ! 153: break; ! 154: ! 155: case RP5C15::MON1: ! 156: rtc.mon = reg.mon10 * 10 + (data & reg.mask[n]); ! 157: UpdateReg(UPDATE_MON); ! 158: break; ! 159: case RP5C15::MON10: ! 160: rtc.mon = (data & reg.mask[n]) * 10 + reg.mon1; ! 161: UpdateReg(UPDATE_MON); ! 162: break; ! 163: ! 164: case RP5C15::YEAR1: ! 165: rtc.year = 1980 + (reg.year10 * 10) + (data & reg.mask[n]); ! 166: UpdateReg(UPDATE_YEAR); ! 167: break; ! 168: case RP5C15::YEAR10: ! 169: rtc.year = 1980 + (data & reg.mask[n]) * 10 + reg.year1; ! 170: UpdateReg(UPDATE_YEAR); ! 171: break; ! 172: ! 173: case RP5C15::CLKSEL: ! 174: case 0x12 ... 0x1c: ! 175: // マスクして書き込む ! 176: reg.r[n] = data & reg.mask[n]; ! 177: break; ! 178: ! 179: case RP5C15::ADJUST: ! 180: { ! 181: // %0 なら何もしない? ! 182: if ((data & 1) == 0) { ! 183: break; ! 184: } ! 185: // %1 ならアジャスト。 ! 186: ! 187: // 現在の秒が 0..29秒なら 0秒に切り下げ。 ! 188: // 30..59秒なら +1分0秒に繰り上げ。 ! 189: // いずれにしても 1秒と10秒の位は 0 にする。 ! 190: uint32 updates = UPDATE_SEC; ! 191: if (rtc.sec < 30) { ! 192: // 1分切り下げ ! 193: rtc.sec = 0; ! 194: } else { ! 195: // 1分繰り上げ ! 196: rtc.sec = 0; ! 197: updates = CarryMin(updates); ! 198: } ! 199: UpdateReg(updates); ! 200: break; ! 201: } ! 202: ! 203: case RP5C15::MODE: ! 204: case RP5C15::MODE_BANK1: ! 205: reg.mode = data & reg.mask[RP5C15::MODE]; ! 206: // bank を更新 ! 207: bank = (data & RP5C15::MODE_BANK); ! 208: break; ! 209: ! 210: case RP5C15::TEST: ! 211: case RP5C15::TEST_BANK1: ! 212: data &= reg.mask[RP5C15::TEST]; ! 213: if (data == 0) { ! 214: // 通常 0 を書き込むこと、となっているので 0 は許可 ! 215: } else { ! 216: putlog(0, "TEST <- $%1x 未実装書き込み", data); ! 217: } ! 218: reg.test = data; ! 219: break; ! 220: ! 221: case RP5C15::RESET: ! 222: case RP5C15::RESET_BANK1: ! 223: reg.reset = data & reg.mask[RP5C15::RESET]; ! 224: if ((reg.reset & RP5C15::RESET_TIMER)) { ! 225: // %1 を書き込むと、秒未満の内部カウンタをリセット ! 226: // 繰り上がり分の保持も消えるけどいいよな? ! 227: rtc.cnt = 0; ! 228: } ! 229: break; ! 230: ! 231: default: ! 232: break; ! 233: } ! 234: return 0; ! 235: } ! 236: ! 237: uint64 ! 238: RP5C15Device::Write16(uint32 addr, uint32 data) ! 239: { ! 240: return Write8(addr + 1, data); ! 241: } ! 242: ! 243: uint64 ! 244: RP5C15Device::Peek8(uint32 addr) ! 245: { ! 246: int n; ! 247: ! 248: // 偶数アドレスへのアクセスを先に弾く ! 249: if (addr % 2 == 0) { ! 250: return (uint64)-1; ! 251: } ! 252: ! 253: n = ((addr - devaddr) / 2) + bank * 0x10; ! 254: ! 255: switch (n) { ! 256: case RP5C15::MODE_BANK1: ! 257: case RP5C15::TEST_BANK1: ! 258: case RP5C15::RESET_BANK1: ! 259: // Bank0 と同じものが見える ! 260: n -= 0x10; ! 261: FALLTHROUGH; ! 262: ! 263: case 0x00 ... 0x1c: ! 264: return reg.r[n] | 0xf0; ! 265: ! 266: default: ! 267: return 0xf0; ! 268: } ! 269: } ! 270: ! 271: bool ! 272: RP5C15Device::MonitorUpdate() ! 273: { ! 274: int x; ! 275: int y; ! 276: ! 277: // ! 278: // #0 #1 ! 279: // 1:4 2 ! 280: // ! 281: // 0 1 2 3 4 5 6 7 8 9 A B C D E F ! 282: // Bank0 0 1 2 3 4 5 6 7 8 9 a b c d e f ! 283: // Bank1 ! 284: ! 285: monitor.Clear(); ! 286: ! 287: monitor.Print(0, 0, " #0 #1"); ! 288: y = 1; ! 289: for (int i = 0; i < 13; i++, y++) { ! 290: monitor.Print(0, y, "%X:%x %x", i, reg.r[i], reg.r[i + 16]); ! 291: } ! 292: for (int i = 13; i < 16; i++, y++) { ! 293: monitor.Print(0, y, "%X:%x", i, reg.r[i]); ! 294: } ! 295: ! 296: x = 8; ! 297: y = 0; ! 298: ! 299: int year = reg.year10 * 10 + reg.year1 + 1980; ! 300: int mon = reg.mon10 * 10 + reg.mon1; ! 301: int day = reg.day10 * 10 + reg.day1; ! 302: monitor.Print(x, y, "%04d/%02d/%02d(%s)", ! 303: year, mon, day, wdays[reg.wday]); ! 304: y++; ! 305: ! 306: int hour; ! 307: if (reg.sel24) { ! 308: hour = reg.hour10 * 10 + reg.hour1; ! 309: } else { ! 310: hour = (reg.hour10 & 1) * 10 + reg.hour1; ! 311: monitor.Print(x, y, "%s", (reg.hour10 & 2) == 0 ? "AM" : "PM"); ! 312: } ! 313: int min = reg.min10 * 10 + reg.min1; ! 314: int sec = reg.sec10 * 10 + reg.sec1; ! 315: monitor.Print(x + 3, y, "%02d:%02d:%02d", hour, min, sec); ! 316: y++; ! 317: ! 318: monitor.Print(x, y++, "Leap %d", reg.leap); ! 319: y++; ! 320: ! 321: monitor.Print(x, y++, "CLKOUT %c", clkout ? 'H' : 'L'); ! 322: monitor.Print(x, y++, "ALARM %c", alarmout ? 'H' : 'L'); ! 323: y++; ! 324: ! 325: monitor.Print(x, y++, "cnt %2d", rtc.cnt); ! 326: ! 327: return true; ! 328: } ! 329: ! 330: // 32Hz クロック入力 ! 331: // ! 332: // 32Hz ごとに呼ばれて、これが RTC の時計を進める処理をするところ。 ! 333: // RTC を実時間に同期して進めるモードなら実時間を計るスケジューラから、 ! 334: // RTC を仮想時間に同期して進めるモードなら VM スケジューラから呼ばれる。 ! 335: // ! 336: // CLKOUT 128Hz 以上のモードには対応していない。 ! 337: void ! 338: RP5C15Device::ClockIn() ! 339: { ! 340: uint32 updates; ! 341: ! 342: // 32Hz カウンタを進める ! 343: updates = CountUp(); ! 344: ! 345: // レジスタの値を更新する ! 346: UpdateReg(updates); ! 347: ! 348: // CLKOUT 端子の切り替え周期は CLKSEL による ! 349: switch (reg.clksel) { ! 350: case 0: // ハイインピーダンス ! 351: case 1: // 16384Hz ! 352: case 2: // 1024Hz ! 353: case 3: // 128Hz ! 354: // 128Hz 点滅とか点灯みたいなもんだろ(適当) ! 355: clkout = true; ! 356: break; ! 357: ! 358: case 4: // 16Hz ! 359: clkout = !clkout; ! 360: // アラーム端子 16Hz パルス出力は %0 ならオン ! 361: if ((reg.reset & RP5C15::RESET_16Hz) == 0) { ! 362: alarmout = !alarmout; ! 363: } ! 364: break; ! 365: ! 366: case 5: // 1Hz ! 367: // CLKOUT 端子は秒の繰り上がりに同期している。 ! 368: // ALARM 端子の 1Hz パルスもそれに同期するとは書いてないけど、 ! 369: // 面倒なのでとりあえず同じところで処理してしまう。 ! 370: // TIMER_EN (秒への繰り上がり) が無効でも反転自体は起きる気が ! 371: // するので、ここでは UPDATE_SEC ではなく cnt で判定する。 ! 372: if ((rtc.cnt & 31) == 0) { ! 373: // CLKOUT は 1秒の繰り上がりが起きる時に 'H' になる ! 374: clkout = true; ! 375: ! 376: // ALARM 反転 ! 377: if ((reg.reset & RP5C15::RESET_1Hz) == 0) { ! 378: alarmout = !alarmout; ! 379: } ! 380: } else if ((rtc.cnt & 31) == 16) { ! 381: // CLKOUT、その反対側 ! 382: clkout = false; ! 383: ! 384: // ALARM 反転 ! 385: if ((reg.reset & RP5C15::RESET_1Hz) == 0) { ! 386: alarmout = !alarmout; ! 387: } ! 388: } ! 389: break; ! 390: ! 391: case 6: // 1/60Hz ! 392: if ((updates & UPDATE_MIN)) { ! 393: // 00秒になったので CLKOUT を 'H' に ! 394: clkout = true; ! 395: } else if ((updates & UPDATE_SEC) && rtc.sec == 30) { ! 396: // 30秒になったので CLKOUT を 'L' に ! 397: clkout = false; ! 398: } ! 399: break; ! 400: ! 401: case 7: // 'L' 固定 ! 402: clkout = false; ! 403: break; ! 404: } ! 405: } ! 406: ! 407: // updates で指定された桁に該当するレジスタを更新する。 ! 408: // 繰り上がりかどうかは処理せず現在の値を RP5C15 レジスタにセットするだけ。 ! 409: void ! 410: RP5C15Device::UpdateReg(uint32 updates) ! 411: { ! 412: if ((updates & UPDATE_SEC)) { ! 413: reg.sec1 = rtc.sec % 10; ! 414: reg.sec10 = rtc.sec / 10; ! 415: } ! 416: if ((updates & UPDATE_MIN)) { ! 417: reg.min1 = rtc.min % 10; ! 418: reg.min10 = rtc.min / 10; ! 419: } ! 420: if ((updates & UPDATE_HOUR)) { ! 421: if (reg.sel24) { ! 422: // 24時間計モードでは普通の BCD ! 423: reg.hour1 = rtc.hour % 10; ! 424: reg.hour10 = rtc.hour / 10; ! 425: } else { ! 426: // 12時間計モードなら、時のbit1 が AM/PM を表す ! 427: int h12 = rtc.hour % 12; ! 428: reg.hour1 = h12 % 10; ! 429: reg.hour10 = h12 / 10; ! 430: reg.hour10 |= (rtc.hour >= 12) ? 0x02 : 0x00; ! 431: } ! 432: } ! 433: ! 434: if ((updates & UPDATE_DAY)) { ! 435: reg.wday = rtc.wday; ! 436: reg.day1 = rtc.mday % 10; ! 437: reg.day10 = rtc.mday / 10; ! 438: } ! 439: if ((updates & UPDATE_MON)) { ! 440: reg.mon1 = rtc.mon % 10; ! 441: reg.mon10 = rtc.mon / 10; ! 442: } ! 443: if ((updates & UPDATE_YEAR)) { ! 444: // X680x0 では 0 を 1980年としている。 ! 445: int y = rtc.year - 1980; ! 446: reg.year1 = y % 10; ! 447: reg.year10 = y / 10; ! 448: } ! 449: } ! 450: ! 451: /*static*/ const uint8 ! 452: RP5C15::mask[] = { ! 453: 0x0f, 0x07, 0x0f, 0x07, // 1秒 10秒 1分 10分 ! 454: 0x0f, 0x03, 0x07, 0x0f, // 1時 10時 曜日 1日 ! 455: 0x01, 0x0f, 0x01, 0x0f, // 10日 1月 10月 1年 ! 456: 0x0f, 0x0d, 0x0f, 0x0f, // 10年 MODE TEST RESET ! 457: ! 458: 0x07, 0x01, 0x0f, 0x07, // CLKO Adjust AL1分 AL10分 ! 459: 0x0f, 0x03, 0x07, 0x0f, // AL1時 AL10時 AL曜日 AL1日 ! 460: 0x03, 0x00, 0x01, 0x03, // AL10日 --- 12/24 閏年 ! 461: 0x00 // --- ! 462: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.