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
BCD_INT(_In_ UCHAR Bcd)39 BCD_INT(
40 _In_ UCHAR Bcd)
41 {
42 return ((Bcd & 0xF0) >> 4) * 10 + (Bcd & 0x0F);
43 }
44
45 static UCHAR
46 NTAPI
HalpReadNvram(_In_ UCHAR Register)47 HalpReadNvram(
48 _In_ UCHAR Register)
49 {
50 return READ_REGISTER_UCHAR((PUCHAR)(MappedNvram + Register));
51 }
52
_Requires_lock_held_(HalpSystemHardwareLock)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
_Requires_lock_held_(HalpSystemHardwareLock)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
_Requires_lock_held_(HalpSystemHardwareLock)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
_Requires_lock_held_(HalpSystemHardwareLock)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
HalpReadCmos(_In_ UCHAR Reg)123 HalpReadCmos(
124 _In_ UCHAR Reg)
125 {
126 /* Not supported by hardware */
127 return 0;
128 }
129
130 VOID
131 NTAPI
HalpWriteCmos(_In_ UCHAR Reg,_In_ UCHAR Value)132 HalpWriteCmos(
133 _In_ UCHAR Reg,
134 _In_ UCHAR Value)
135 {
136 /* Not supported by hardware */
137 NOTHING;
138 }
139
140 ULONG
141 NTAPI
HalpGetCmosData(_In_ ULONG BusNumber,_In_ ULONG SlotNumber,_Out_writes_bytes_ (Length)PVOID Buffer,_In_ ULONG Length)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
HalpSetCmosData(_In_ ULONG BusNumber,_In_ ULONG SlotNumber,_In_reads_bytes_ (Length)PVOID Buffer,_In_ ULONG Length)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
HalpInitializeCmos(VOID)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
HalGetEnvironmentVariable(_In_ PCH Name,_In_ USHORT ValueLength,_Out_writes_z_ (ValueLength)PCH Value)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
HalSetEnvironmentVariable(_In_ PCH Name,_In_ PCH Value)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
HalQueryRealTimeClock(_Out_ PTIME_FIELDS Time)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
HalSetRealTimeClock(_In_ PTIME_FIELDS Time)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