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 /* Convenience definitions for RTC offsets */ 21 #define RTC_SEC 0x00 22 #define RTC_MIN 0x02 23 #define RTC_HOUR 0x04 24 #define RTC_DAY 0x07 25 #define RTC_MONTH 0x08 26 #define RTC_YEAR 0x09 27 #define RTC_CENTURY 0x32 28 29 #define RTC_REGA 0x0a 30 #define RTC_REGB 0x0b 31 #define RTC_REGC 0x0c 32 #define RTC_REGD 0x0d 33 34 #define REGA_DIVIDER_32K 0x20 35 #define REGA_DIVIDER_DIS 0x70 36 #define REGA_PERIOD_512HZ 0x07 37 #define REGA_PERIOD_128HZ 0x09 38 39 #define REGB_HALT 0x80 40 #define REGB_IE_PERIODIC 0x40 41 #define REGB_IE_ALARM 0x20 42 #define REGB_IE_UPDATE 0x10 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 /* Prepare the subordinate PIC to process interrupts from RTC */ 92 static void 93 atpic_init(void) 94 { 95 /* ICW1: INIT | ICW4 */ 96 outb(IOP_ATPIC_SCMD, 0x11); 97 /* ICW2: vector offset (useless in context) */ 98 outb(IOP_ATPIC_SDATA, 0x20); 99 /* ICW3: cascade info (ignored) */ 100 outb(IOP_ATPIC_SDATA, 0x00); 101 /* ICW3: 8086_MODE | AEOI */ 102 outb(IOP_ATPIC_SDATA, 0x03); 103 /* No masked bits */ 104 outb(IOP_ATPIC_SDATA, 0x00); 105 106 } 107 108 /* Poll the subordinate PIC for an IRQ */ 109 static uint8_t 110 atpit_poll_for_intr(void) 111 { 112 uint8_t val = 0; 113 114 do { 115 /* OCW3: POLL */ 116 outb(IOP_ATPIC_SCMD, 0x0c); 117 118 val = inb(IOP_ATPIC_SDATA); 119 } while ((val & 0x80) == 0); 120 121 return (val); 122 } 123 124 static void 125 test_periodic_polling(void) 126 { 127 /* Halt the RTC to prep for test of periodic timer */ 128 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 129 rtc_write(RTC_REGB, REGB_HALT); 130 131 /* Clear any pending event flags */ 132 (void) rtc_read(RTC_REGC); 133 134 test_msg("testing periodic (polling)"); 135 136 /* Release divider to run, configuring a 512Hz periodic timer */ 137 rtc_write(RTC_REGA, REGA_DIVIDER_32K | REGA_PERIOD_512HZ); 138 rtc_write(RTC_REGB, 0); 139 140 /* Count periodic firings until the next time update */ 141 uint_t periodic_fire = 0; 142 uint8_t events = 0; 143 do { 144 events = wait_for_flag(REGC_UPDATE | REGC_PERIODIC); 145 146 if ((events & REGC_PERIODIC) != 0) { 147 periodic_fire++; 148 } 149 } while ((events & REGC_UPDATE) == 0); 150 151 /* 152 * In the 500ms between releasing the divider and the first time update, 153 * we expect 256 firings of the 512Hz periodic timer. 154 */ 155 if (periodic_fire != 256) { 156 TEST_ABORT("unexpected periodic firing count at 512Hz"); 157 } 158 159 /* Change the periodic timer to 128Hz */ 160 rtc_write(RTC_REGA, REGA_DIVIDER_32K | REGA_PERIOD_128HZ); 161 162 /* Count periodic firings until the next time update */ 163 periodic_fire = 0; 164 do { 165 events = wait_for_flag(REGC_UPDATE | REGC_PERIODIC); 166 167 if ((events & REGC_PERIODIC) != 0) { 168 periodic_fire++; 169 } 170 } while ((events & REGC_UPDATE) == 0); 171 172 /* 173 * With 1s between time updates, we expect 128 firings for the 174 * reconfigured 128Hz periodic timer. 175 */ 176 if (periodic_fire != 128) { 177 TEST_ABORT("unexpected periodic firing count at 128Hz"); 178 } 179 } 180 181 static void 182 test_periodic_interrupts(void) 183 { 184 /* Halt the RTC to prep for test of periodic timer */ 185 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 186 rtc_write(RTC_REGB, REGB_HALT); 187 188 /* Clear any pending event flags */ 189 (void) rtc_read(RTC_REGC); 190 191 test_msg("testing periodic (interrupts)"); 192 193 /* 194 * The RTC IRQ is routed on line 8, which corresponds to pin 0 on the 195 * subordinate PIC. Initialize it now so we can poll for interrupts. 196 */ 197 atpic_init(); 198 199 /* Release divider to run, configuring a 512Hz periodic timer */ 200 rtc_write(RTC_REGA, REGA_DIVIDER_32K | REGA_PERIOD_512HZ); 201 /* Enable interrupts for periodic timer and 1Hz update */ 202 rtc_write(RTC_REGB, REGB_IE_PERIODIC | REGB_IE_UPDATE); 203 204 /* Count periodic firings until the next time update */ 205 uint_t periodic_fire = 0; 206 uint8_t events = 0; 207 do { 208 const uint8_t irq = atpit_poll_for_intr(); 209 if (irq != 0x80) { 210 /* 211 * RTC is pin 0 on the subordinate PIC chip, so we 212 * expect only the interrupt-present bit set 213 */ 214 TEST_ABORT("spurious interrupt on PIC"); 215 } 216 217 events = rtc_read(RTC_REGC); 218 219 /* Since we waited for the interrupt, the flag should be here */ 220 if ((events & REGC_IRQ) == 0) { 221 TEST_ABORT("missing IRQ flag in regc"); 222 } 223 224 if ((events & REGC_PERIODIC) != 0) { 225 periodic_fire++; 226 } 227 } while ((events & REGC_UPDATE) == 0); 228 229 /* 230 * Like the polling periodic test, we expect 256 firings of the 512Hz 231 * timer between the release of the divider and the first update. 232 */ 233 if (periodic_fire != 256) { 234 TEST_ABORT("unexpected periodic firing count at 512Hz"); 235 } 236 237 /* Disable periodic configuration from RTC */ 238 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 239 rtc_write(RTC_REGB, REGB_HALT); 240 } 241 242 void 243 start(void) 244 { 245 /* 246 * Initialize RTC to known state: 247 * - rega: divider and periodic timer disabled 248 * - regb: updates halted, intr disabled, 24hr time, binary fmt, no DST 249 * - regc: cleared (by read) 250 */ 251 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 252 rtc_write(RTC_REGB, REGB_HALT | REGB_DATA_BIN | REGB_24HR); 253 (void) rtc_read(RTC_REGC); 254 255 /* Start at 1970 epoch */ 256 rtc_write(RTC_DAY, 1); 257 rtc_write(RTC_MONTH, 1); 258 rtc_write(RTC_YEAR, 70); 259 rtc_write(RTC_CENTURY, 19); 260 rtc_write(RTC_HOUR, 0); 261 rtc_write(RTC_MIN, 0); 262 rtc_write(RTC_SEC, 0); 263 264 uint64_t start, end; 265 /* 266 * After allowing the divider to run, and enabling time updates, we 267 * expect a 500ms delay until the first update to the date/time data. 268 * Measure this with the TSC, even though we do not have a calibration 269 * for its frequency. 270 */ 271 rtc_write(RTC_REGA, REGA_DIVIDER_32K); 272 start = rdtsc(); 273 rtc_write(RTC_REGB, REGB_DATA_BIN | REGB_24HR); 274 275 if (rtc_read(RTC_REGC) != 0) { 276 TEST_ABORT("unexpected flags set in regC"); 277 } 278 279 test_msg("waiting for first update"); 280 (void) wait_for_flag(REGC_UPDATE); 281 end = rdtsc(); 282 283 const uint64_t tsc_500ms = end - start; 284 start = end; 285 286 /* Expect the clock to read 00:00:01 after the first update */ 287 if (rtc_read(RTC_SEC) != 1) { 288 TEST_ABORT("did not find 01 in seconds field"); 289 } 290 291 /* Wait for another update to pass by */ 292 test_msg("waiting for second update"); 293 (void) wait_for_flag(REGC_UPDATE); 294 end = rdtsc(); 295 296 const uint64_t tsc_1s = end - start; 297 298 /* Expect the clock to read 00:00:02 after the second update */ 299 if (rtc_read(RTC_SEC) != 2) { 300 TEST_ABORT("did not find 02 in seconds field"); 301 } 302 303 /* 304 * Determine ratio between the intervals which should be 500ms and 305 * 1000ms long, as measured by the TSC. 306 */ 307 int64_t ppm_delta = (int64_t)(tsc_500ms * 2 * 1000000) / tsc_1s; 308 ppm_delta = ABS(ppm_delta - 1000000); 309 310 if (ppm_delta > PPM_THRESHOLD) { 311 TEST_ABORT("clock update timing outside threshold"); 312 } 313 314 /* Put RTC in 12-hr, BCD-formatted mode */ 315 rtc_write(RTC_REGA, REGA_DIVIDER_DIS); 316 rtc_write(RTC_REGB, REGB_HALT); 317 318 /* Set time to 11:59:59, prepping for roll-over into noon */ 319 rtc_write(RTC_HOUR, 0x11); 320 rtc_write(RTC_MIN, 0x59); 321 rtc_write(RTC_SEC, 0x59); 322 323 /* Release the clock to run again */ 324 rtc_write(RTC_REGA, REGA_DIVIDER_32K); 325 rtc_write(RTC_REGB, 0); 326 327 /* Wait for it to tick over */ 328 test_msg("waiting for noon tick-over"); 329 (void) wait_for_flag(REGC_UPDATE); 330 331 if (rtc_read(RTC_SEC) != 0) { 332 TEST_ABORT("invalid RTC_SEC value"); 333 } 334 if (rtc_read(RTC_MIN) != 0) { 335 TEST_ABORT("invalid RTC_MIN value"); 336 } 337 /* Hour field should now hold 0x12 (BCD noon) | 0x80 (PM flag) */ 338 if (rtc_read(RTC_HOUR) != 0x92) { 339 TEST_ABORT("invalid RTC_HOUR value"); 340 } 341 342 test_periodic_polling(); 343 344 test_periodic_interrupts(); 345 346 /* 347 * TODO - Add additional tests: 348 * - alarm interrupts 349 */ 350 351 /* Happy for now */ 352 test_result_pass(); 353 } 354