1 /* 2 * PROJECT: NEC PC-98 series HAL 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: RTC and NVRAM access routines 5 * COPYRIGHT: Copyright 2020 Dmitry Borisov (di.sean@protonmail.com) 6 */ 7 8 /* INCLUDES ******************************************************************/ 9 10 #include <hal.h> 11 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS *******************************************************************/ 16 17 /* 18 * The PC-98 hardware maps data from the NVRAM directly into the text video 19 * memory address space. Every fourth byte is a "writable data". 20 * 21 * |0x2FE2|0x2FE3|0x2FE4|0x2FE5|0x2FE6|0x2FE7| .... |0x2FFD|0x2FFE| 22 * | D | | | | D | | .... | | D | 23 * 24 * Most of these bits of the NVRAM are already used. There are some reserved 25 * bits in the 0x3FE6 and 0x3FFE that we can use. 26 */ 27 #define NVRAM_START 0x3FE2 28 #define NVRAM_SIZE 0x1C 29 #define NVRAM_UNUSED_REG 0x14 30 #define NVRAM_UNUSED_BIT 0x80 31 32 static ULONG_PTR MappedNvram; 33 34 /* PRIVATE FUNCTIONS *********************************************************/ 35 36 /* Avoid double calls */ 37 #undef BCD_INT 38 static UCHAR 39 BCD_INT( 40 _In_ UCHAR Bcd) 41 { 42 return ((Bcd & 0xF0) >> 4) * 10 + (Bcd & 0x0F); 43 } 44 45 static UCHAR 46 NTAPI 47 HalpReadNvram( 48 _In_ UCHAR Register) 49 { 50 return READ_REGISTER_UCHAR((PUCHAR)(MappedNvram + Register)); 51 } 52 53 _Requires_lock_held_(HalpSystemHardwareLock) 54 static VOID 55 NTAPI 56 HalpWriteNvram( 57 _In_ UCHAR Register, 58 _In_ UCHAR Value) 59 { 60 __outbyte(GDC1_IO_o_MODE_FLIPFLOP1, GDC1_NVRAM_UNPROTECT); 61 WRITE_REGISTER_UCHAR((PUCHAR)(MappedNvram + Register), Value); 62 __outbyte(GDC1_IO_o_MODE_FLIPFLOP1, GDC1_NVRAM_PROTECT); 63 } 64 65 _Requires_lock_held_(HalpSystemHardwareLock) 66 static UCHAR 67 NTAPI 68 HalpRtcReadByte(VOID) 69 { 70 UCHAR i; 71 UCHAR Byte = 0; 72 73 /* Read byte from single wire bus */ 74 for (i = 0; i < 8; i++) 75 { 76 Byte |= (__inbyte(PPI_IO_i_PORT_B) & 1) << i; 77 78 __outbyte(RTC_IO_o_DATA, RTC_CLOCK | RTC_CMD_SERIAL_TRANSFER_MODE); 79 KeStallExecutionProcessor(1); 80 81 __outbyte(RTC_IO_o_DATA, RTC_CMD_SERIAL_TRANSFER_MODE); 82 KeStallExecutionProcessor(1); 83 } 84 85 return Byte; 86 } 87 88 _Requires_lock_held_(HalpSystemHardwareLock) 89 static VOID 90 NTAPI 91 HalpRtcWriteBit( 92 _In_ UCHAR Bit) 93 { 94 Bit = (Bit & 1) << 5; 95 96 __outbyte(RTC_IO_o_DATA, Bit | RTC_CMD_SERIAL_TRANSFER_MODE); 97 KeStallExecutionProcessor(1); 98 99 __outbyte(RTC_IO_o_DATA, Bit | RTC_CLOCK | RTC_CMD_SERIAL_TRANSFER_MODE); 100 KeStallExecutionProcessor(1); 101 } 102 103 _Requires_lock_held_(HalpSystemHardwareLock) 104 static VOID 105 NTAPI 106 HalpRtcWriteCommand( 107 _In_ UCHAR Command) 108 { 109 UCHAR i; 110 111 for (i = 0; i < 4; i++) 112 HalpRtcWriteBit(Command >> i); 113 114 __outbyte(RTC_IO_o_DATA, RTC_STROBE | RTC_CMD_SERIAL_TRANSFER_MODE); 115 KeStallExecutionProcessor(1); 116 117 __outbyte(RTC_IO_o_DATA, RTC_CMD_SERIAL_TRANSFER_MODE); 118 KeStallExecutionProcessor(1); 119 } 120 121 UCHAR 122 NTAPI 123 HalpReadCmos( 124 _In_ UCHAR Reg) 125 { 126 /* Not supported by hardware */ 127 return 0; 128 } 129 130 VOID 131 NTAPI 132 HalpWriteCmos( 133 _In_ UCHAR Reg, 134 _In_ UCHAR Value) 135 { 136 /* Not supported by hardware */ 137 NOTHING; 138 } 139 140 ULONG 141 NTAPI 142 HalpGetCmosData( 143 _In_ ULONG BusNumber, 144 _In_ ULONG SlotNumber, 145 _Out_writes_bytes_(Length) PVOID Buffer, 146 _In_ ULONG Length) 147 { 148 /* Not supported by hardware */ 149 return 0; 150 } 151 152 ULONG 153 NTAPI 154 HalpSetCmosData( 155 _In_ ULONG BusNumber, 156 _In_ ULONG SlotNumber, 157 _In_reads_bytes_(Length) PVOID Buffer, 158 _In_ ULONG Length) 159 { 160 /* Not supported by hardware */ 161 return 0; 162 } 163 164 CODE_SEG("INIT") 165 VOID 166 NTAPI 167 HalpInitializeCmos(VOID) 168 { 169 PHYSICAL_ADDRESS PhysicalAddress; 170 171 /* TODO: Detect TVRAM address */ 172 if (TRUE) 173 PhysicalAddress.QuadPart = VRAM_NORMAL_TEXT + NVRAM_START; 174 else 175 PhysicalAddress.QuadPart = VRAM_HI_RESO_TEXT + NVRAM_START; 176 MappedNvram = (ULONG_PTR)HalpMapPhysicalMemory64(PhysicalAddress, BYTES_TO_PAGES(NVRAM_SIZE)); 177 } 178 179 /* PUBLIC FUNCTIONS **********************************************************/ 180 181 ARC_STATUS 182 NTAPI 183 HalGetEnvironmentVariable( 184 _In_ PCH Name, 185 _In_ USHORT ValueLength, 186 _Out_writes_z_(ValueLength) PCH Value) 187 { 188 UCHAR Val; 189 190 /* Only variable supported on x86 */ 191 if (_stricmp(Name, "LastKnownGood")) 192 return ENOENT; 193 194 if (!MappedNvram) 195 return ENOENT; 196 197 HalpAcquireCmosSpinLock(); 198 199 Val = HalpReadNvram(NVRAM_UNUSED_REG) & NVRAM_UNUSED_BIT; 200 201 HalpReleaseCmosSpinLock(); 202 203 /* Check the flag */ 204 if (Val) 205 strncpy(Value, "FALSE", ValueLength); 206 else 207 strncpy(Value, "TRUE", ValueLength); 208 209 return ESUCCESS; 210 } 211 212 ARC_STATUS 213 NTAPI 214 HalSetEnvironmentVariable( 215 _In_ PCH Name, 216 _In_ PCH Value) 217 { 218 UCHAR Val; 219 220 /* Only variable supported on x86 */ 221 if (_stricmp(Name, "LastKnownGood")) 222 return ENOMEM; 223 224 if (!MappedNvram) 225 return ENOMEM; 226 227 /* Check if this is true or false */ 228 if (!_stricmp(Value, "TRUE")) 229 { 230 HalpAcquireCmosSpinLock(); 231 232 Val = HalpReadNvram(NVRAM_UNUSED_REG) | NVRAM_UNUSED_BIT; 233 } 234 else if (!_stricmp(Value, "FALSE")) 235 { 236 HalpAcquireCmosSpinLock(); 237 238 Val = HalpReadNvram(NVRAM_UNUSED_REG) & ~NVRAM_UNUSED_BIT; 239 } 240 else 241 { 242 /* Fail */ 243 return ENOMEM; 244 } 245 246 HalpWriteNvram(NVRAM_UNUSED_REG, Val); 247 248 HalpReleaseCmosSpinLock(); 249 250 return ESUCCESS; 251 } 252 253 BOOLEAN 254 NTAPI 255 HalQueryRealTimeClock( 256 _Out_ PTIME_FIELDS Time) 257 { 258 UCHAR Temp; 259 260 HalpAcquireCmosSpinLock(); 261 262 HalpRtcWriteCommand(RTC_CMD_TIME_READ); 263 HalpRtcWriteCommand(RTC_CMD_REGISTER_SHIFT); 264 KeStallExecutionProcessor(19); 265 266 /* Set the time data */ 267 Time->Second = BCD_INT(HalpRtcReadByte()); 268 Time->Minute = BCD_INT(HalpRtcReadByte()); 269 Time->Hour = BCD_INT(HalpRtcReadByte()); 270 Time->Day = BCD_INT(HalpRtcReadByte()); 271 Temp = HalpRtcReadByte(); 272 Time->Weekday = Temp & 0x0F; 273 Time->Month = Temp >> 4; 274 Time->Year = BCD_INT(HalpRtcReadByte()); 275 Time->Milliseconds = 0; 276 277 Time->Year += (Time->Year >= 80) ? 1900 : 2000; 278 279 HalpRtcWriteCommand(RTC_CMD_REGISTER_HOLD); 280 281 HalpReleaseCmosSpinLock(); 282 283 return TRUE; 284 } 285 286 BOOLEAN 287 NTAPI 288 HalSetRealTimeClock( 289 _In_ PTIME_FIELDS Time) 290 { 291 UCHAR i, j; 292 UCHAR SysTime[6]; 293 294 HalpAcquireCmosSpinLock(); 295 296 HalpRtcWriteCommand(RTC_CMD_REGISTER_SHIFT); 297 298 SysTime[0] = INT_BCD(Time->Second); 299 SysTime[1] = INT_BCD(Time->Minute); 300 SysTime[2] = INT_BCD(Time->Hour); 301 SysTime[3] = INT_BCD(Time->Day); 302 SysTime[4] = (Time->Month << 4) | (Time->Weekday & 0x0F); 303 SysTime[5] = INT_BCD(Time->Year % 100); 304 305 /* Write time fields to RTC */ 306 for (i = 0; i < 6; i++) 307 { 308 for (j = 0; j < 8; j++) 309 HalpRtcWriteBit(SysTime[i] >> j); 310 } 311 312 HalpRtcWriteCommand(RTC_CMD_TIME_SET_COUNTER_HOLD); 313 HalpRtcWriteCommand(RTC_CMD_REGISTER_HOLD); 314 315 HalpReleaseCmosSpinLock(); 316 317 return TRUE; 318 } 319