1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2023 Oxide Computer Company 14 */ 15 16 #include "payload_common.h" 17 #include "payload_utils.h" 18 #include "test_defs.h" 19 20 #define IOP_RTC_ADDR 0x70 21 #define IOP_RTC_DATA 0x71 22 23 /* Convenience definitions for RTC offsets */ 24 #define RTC_SEC 0x00 25 #define RTC_MIN 0x02 26 #define RTC_HOUR 0x04 27 #define RTC_DAY 0x07 28 #define RTC_MONTH 0x08 29 #define RTC_YEAR 0x09 30 #define RTC_CENTURY 0x32 31 32 #define RTC_REGA 0x0a 33 #define RTC_REGB 0x0b 34 #define RTC_REGC 0x0c 35 #define RTC_REGD 0x0d 36 37 #define REGA_DIVIDER_32K 0x20 38 #define REGA_DIVIDER_DIS 0x70 39 #define REGA_PERIOD_512HZ 0x07 40 #define REGA_PERIOD_128HZ 0x09 41 42 #define REGB_HALT 0x80 43 #define REGB_DATA_BIN 0x04 44 #define REGB_24HR 0x02 45 #define REGB_DST 0x01 46 47 #define REGC_IRQ 0x80 48 #define REGC_PERIODIC 0x40 49 #define REGC_ALARM 0x20 50 #define REGC_UPDATE 0x10 51 52 #define PPM_THRESHOLD 500 53 #define ABS(x) ((x) < 0 ? -(x) : (x)) 54 55 static uint8_t rtc_last_off = 0xff; 56 57 static uint8_t 58 rtc_read(uint8_t off) 59 { 60 if (off != rtc_last_off) { 61 outb(IOP_RTC_ADDR, off); 62 rtc_last_off = off; 63 } 64 65 return (inb(IOP_RTC_DATA)); 66 } 67 68 static void 69 rtc_write(uint8_t off, uint8_t data) 70 { 71 if (off != rtc_last_off) { 72 outb(IOP_RTC_ADDR, off); 73 rtc_last_off = off; 74 } 75 76 return (outb(IOP_RTC_DATA, data)); 77 } 78 79 static uint8_t 80 wait_for_flag(uint8_t mask) 81 { 82 uint8_t regc; 83 84 do { 85 regc = rtc_read(RTC_REGC); 86 } while ((regc & mask) == 0); 87 88 return (regc); 89 } 90 91 void 92 start(void) 93 { 94 /* 95 * Initialize RTC to known state: 96 * - rega: divider and periodic timer disabled 97 * - regb: updates halted, intr disabled, 24hr time, binary fmt, no DST 98 * - regc: cleared (by read) 99 */ 100 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 101 rtc_write(RTC_REGB, REGB_HALT | REGB_DATA_BIN | REGB_24HR); 102 (void) rtc_read(RTC_REGC); 103 104 /* Start at 1970 epoch */ 105 rtc_write(RTC_DAY, 1); 106 rtc_write(RTC_MONTH, 1); 107 rtc_write(RTC_YEAR, 70); 108 rtc_write(RTC_CENTURY, 19); 109 rtc_write(RTC_HOUR, 0); 110 rtc_write(RTC_MIN, 0); 111 rtc_write(RTC_SEC, 0); 112 113 uint64_t start, end; 114 /* 115 * After allowing the divider to run, and enabling time updates, we 116 * expect a 500ms delay until the first update to the date/time data. 117 * Measure this with the TSC, even though we do not have a calibration 118 * for its frequency. 119 */ 120 rtc_write(RTC_REGA, REGA_DIVIDER_32K); 121 start = rdtsc(); 122 rtc_write(RTC_REGB, REGB_DATA_BIN | REGB_24HR); 123 124 if (rtc_read(RTC_REGC) != 0) { 125 TEST_ABORT("unexpected flags set in regC"); 126 } 127 128 (void) wait_for_flag(REGC_UPDATE); 129 end = rdtsc(); 130 131 const uint64_t tsc_500ms = end - start; 132 start = end; 133 134 /* Expect the clock to read 00:00:01 after the first update */ 135 if (rtc_read(RTC_SEC) != 1) { 136 TEST_ABORT("did not find 01 in seconds field"); 137 } 138 139 /* Wait for another update to pass by */ 140 (void) wait_for_flag(REGC_UPDATE); 141 end = rdtsc(); 142 143 const uint64_t tsc_1s = end - start; 144 145 /* Expect the clock to read 00:00:02 after the second update */ 146 if (rtc_read(RTC_SEC) != 2) { 147 TEST_ABORT("did not find 02 in seconds field"); 148 } 149 150 /* 151 * Determine ratio between the intervals which should be 500ms and 152 * 1000ms long, as measured by the TSC. 153 */ 154 int64_t ppm_delta = (int64_t)(tsc_500ms * 2 * 1000000) / tsc_1s; 155 ppm_delta = ABS(ppm_delta - 1000000); 156 157 if (ppm_delta > PPM_THRESHOLD) { 158 TEST_ABORT("clock update timing outside threshold"); 159 } 160 161 /* Put RTC in 12-hr, BCD-formatted mode */ 162 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 163 rtc_write(RTC_REGB, REGB_HALT); 164 165 /* Set time to 11:59:59, prepping for roll-over into noon */ 166 rtc_write(RTC_HOUR, 0x11); 167 rtc_write(RTC_MIN, 0x59); 168 rtc_write(RTC_SEC, 0x59); 169 170 /* Release the clock to run again */ 171 rtc_write(RTC_REGA, REGA_DIVIDER_32K); 172 rtc_write(RTC_REGB, 0); 173 174 /* Wait for it to tick over */ 175 (void) wait_for_flag(REGC_UPDATE); 176 177 if (rtc_read(RTC_SEC) != 0) { 178 TEST_ABORT("invalid RTC_SEC value"); 179 } 180 if (rtc_read(RTC_MIN) != 0) { 181 TEST_ABORT("invalid RTC_MIN value"); 182 } 183 /* Hour field should now hold 0x12 (BCD noon) | 0x80 (PM flag) */ 184 if (rtc_read(RTC_HOUR) != 0x92) { 185 TEST_ABORT("invalid RTC_HOUR value"); 186 } 187 188 /* Halt the RTC once again to prep for test of periodic timer */ 189 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 190 rtc_write(RTC_REGB, REGB_HALT); 191 192 /* Clear any pending event flags */ 193 (void) rtc_read(RTC_REGC); 194 195 /* Release divider to run, configuring a 512Hz periodic timer */ 196 rtc_write(RTC_REGA, REGA_DIVIDER_32K | REGA_PERIOD_512HZ); 197 rtc_write(RTC_REGB, 0); 198 199 /* Count periodic firings until the next time update */ 200 uint_t periodic_fire = 0; 201 uint8_t events = 0; 202 do { 203 events = wait_for_flag(REGC_UPDATE | REGC_PERIODIC); 204 205 if ((events & REGC_PERIODIC) != 0) { 206 periodic_fire++; 207 } 208 } while ((events & REGC_UPDATE) == 0); 209 210 /* 211 * In the 500ms between releasing the divider and the first time update, 212 * we expect 256 firings of the 512Hz periodic timer. 213 */ 214 if (periodic_fire != 256) { 215 TEST_ABORT("unexpected periodic firing count at 512Hz"); 216 } 217 218 /* Change the periodic timer to 128Hz */ 219 rtc_write(RTC_REGA, REGA_DIVIDER_32K | REGA_PERIOD_128HZ); 220 221 /* Count periodic firings until the next time update */ 222 periodic_fire = 0; 223 do { 224 events = wait_for_flag(REGC_UPDATE | REGC_PERIODIC); 225 226 if ((events & REGC_PERIODIC) != 0) { 227 periodic_fire++; 228 } 229 } while ((events & REGC_UPDATE) == 0); 230 231 /* 232 * With 1s between time updates, we expect 128 firings for the 233 * reconfigured 128Hz periodic timer. 234 */ 235 if (periodic_fire != 128) { 236 TEST_ABORT("unexpected periodic firing count at 128Hz"); 237 } 238 239 /* 240 * TODO - Add additional tests: 241 * - alarm interrupts 242 */ 243 244 /* Happy for now */ 245 test_result_pass(); 246 } 247