xref: /reactos/hal/halx86/pc98/cmos.c (revision 5c7ce447)
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