blob: 6895876b004e58104da2c4fa1fb0cd8a15959c67 [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to handle system clocks.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
6// This file may be distributed under the terms of the GNU GPLv3 license.
7
8#include "biosvar.h" // struct bregs
9#include "util.h" // debug_enter
10#include "disk.h" // floppy_tick
Kevin O'Connor4b60c002008-02-25 22:29:55 -050011#include "cmos.h" // inb_cmos
12
13static void
14init_rtc()
15{
16 outb_cmos(0x26, CMOS_STATUS_A);
17 outb_cmos(0x02, CMOS_STATUS_B);
18 inb_cmos(CMOS_STATUS_C);
19 inb_cmos(CMOS_STATUS_D);
20}
21
22static u8
23rtc_updating()
24{
25 // This function checks to see if the update-in-progress bit
26 // is set in CMOS Status Register A. If not, it returns 0.
27 // If it is set, it tries to wait until there is a transition
28 // to 0, and will return 0 if such a transition occurs. A 1
29 // is returned only after timing out. The maximum period
30 // that this bit should be set is constrained to 244useconds.
31 // The count I use below guarantees coverage or more than
32 // this time, with any reasonable IPS setting.
33
34 u16 count = 25000;
35 while (--count != 0) {
36 if ( (inb_cmos(CMOS_STATUS_A) & 0x80) == 0 )
37 return 0;
38 }
39 return 1; // update-in-progress never transitioned to 0
40}
41
42// get current clock count
43static void
44handle_1a00(struct bregs *regs)
45{
46 u32 ticks = GET_BDA(timer_counter);
47 regs->cx = ticks >> 16;
48 regs->dx = ticks;
49 regs->al = GET_BDA(timer_rollover);
50 SET_BDA(timer_rollover, 0); // reset flag
51 set_cf(regs, 0);
52}
53
54// Set Current Clock Count
55static void
56handle_1a01(struct bregs *regs)
57{
58 u32 ticks = (regs->cx << 16) | regs->dx;
59 SET_BDA(timer_counter, ticks);
60 SET_BDA(timer_rollover, 0); // reset flag
61 regs->ah = 0;
62 set_cf(regs, 0);
63}
64
65// Read CMOS Time
66static void
67handle_1a02(struct bregs *regs)
68{
69 if (rtc_updating()) {
70 set_cf(regs, 1);
71 return;
72 }
73
74 regs->dh = inb_cmos(CMOS_RTC_SECONDS);
75 regs->cl = inb_cmos(CMOS_RTC_MINUTES);
76 regs->ch = inb_cmos(CMOS_RTC_HOURS);
77 regs->dl = inb_cmos(CMOS_STATUS_B) & 0x01;
78 regs->ah = 0;
79 regs->al = regs->ch;
80 set_cf(regs, 0);
81}
82
83// Set CMOS Time
84static void
85handle_1a03(struct bregs *regs)
86{
87 // Using a debugger, I notice the following masking/setting
88 // of bits in Status Register B, by setting Reg B to
89 // a few values and getting its value after INT 1A was called.
90 //
91 // try#1 try#2 try#3
92 // before 1111 1101 0111 1101 0000 0000
93 // after 0110 0010 0110 0010 0000 0010
94 //
95 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
96 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
97 if (rtc_updating()) {
98 init_rtc();
99 // fall through as if an update were not in progress
100 }
101 outb_cmos(regs->dh, CMOS_RTC_SECONDS);
102 outb_cmos(regs->cl, CMOS_RTC_MINUTES);
103 outb_cmos(regs->ch, CMOS_RTC_HOURS);
104 // Set Daylight Savings time enabled bit to requested value
105 u8 val8 = (inb_cmos(CMOS_STATUS_B) & 0x60) | 0x02 | (regs->dl & 0x01);
106 outb_cmos(val8, CMOS_STATUS_B);
107 regs->ah = 0;
108 regs->al = val8; // val last written to Reg B
109 set_cf(regs, 0);
110}
111
112// Read CMOS Date
113static void
114handle_1a04(struct bregs *regs)
115{
116 regs->ah = 0;
117 if (rtc_updating()) {
118 set_cf(regs, 1);
119 return;
120 }
121 regs->cl = inb_cmos(CMOS_RTC_YEAR);
122 regs->dh = inb_cmos(CMOS_RTC_MONTH);
123 regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
124 regs->ch = inb_cmos(CMOS_CENTURY);
125 regs->al = regs->ch;
126 set_cf(regs, 0);
127}
128
129// Set CMOS Date
130static void
131handle_1a05(struct bregs *regs)
132{
133 // Using a debugger, I notice the following masking/setting
134 // of bits in Status Register B, by setting Reg B to
135 // a few values and getting its value after INT 1A was called.
136 //
137 // try#1 try#2 try#3 try#4
138 // before 1111 1101 0111 1101 0000 0010 0000 0000
139 // after 0110 1101 0111 1101 0000 0010 0000 0000
140 //
141 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
142 // My assumption: RegB = (RegB & 01111111b)
143 if (rtc_updating()) {
144 init_rtc();
145 set_cf(regs, 1);
146 return;
147 }
148 outb_cmos(regs->cl, CMOS_RTC_YEAR);
149 outb_cmos(regs->dh, CMOS_RTC_MONTH);
150 outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
151 outb_cmos(regs->ch, CMOS_CENTURY);
152 u8 val8 = inb_cmos(CMOS_STATUS_B) & 0x7f; // clear halt-clock bit
153 outb_cmos(val8, CMOS_STATUS_B);
154 regs->ah = 0;
155 regs->al = val8; // AL = val last written to Reg B
156 set_cf(regs, 0);
157}
158
159// Set Alarm Time in CMOS
160static void
161handle_1a06(struct bregs *regs)
162{
163 // Using a debugger, I notice the following masking/setting
164 // of bits in Status Register B, by setting Reg B to
165 // a few values and getting its value after INT 1A was called.
166 //
167 // try#1 try#2 try#3
168 // before 1101 1111 0101 1111 0000 0000
169 // after 0110 1111 0111 1111 0010 0000
170 //
171 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
172 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
173 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
174 regs->ax = 0;
175 if (val8 & 0x20) {
176 // Alarm interrupt enabled already
177 set_cf(regs, 1);
178 return;
179 }
180 if (rtc_updating()) {
181 init_rtc();
182 // fall through as if an update were not in progress
183 }
184 outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
185 outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
186 outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
187 outb(inb(PORT_PIC2_DATA) & ~PIC2_IRQ8, PORT_PIC2_DATA); // enable IRQ 8
188 // enable Status Reg B alarm bit, clear halt clock bit
189 outb_cmos((val8 & 0x7f) | 0x20, CMOS_STATUS_B);
190 set_cf(regs, 0);
191}
192
193// Turn off Alarm
194static void
195handle_1a07(struct bregs *regs)
196{
197 // Using a debugger, I notice the following masking/setting
198 // of bits in Status Register B, by setting Reg B to
199 // a few values and getting its value after INT 1A was called.
200 //
201 // try#1 try#2 try#3 try#4
202 // before 1111 1101 0111 1101 0010 0000 0010 0010
203 // after 0100 0101 0101 0101 0000 0000 0000 0010
204 //
205 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
206 // My assumption: RegB = (RegB & 01010111b)
207 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
208 // clear clock-halt bit, disable alarm bit
209 outb_cmos(val8 & 0x57, CMOS_STATUS_B); // disable alarm bit
210 regs->ah = 0;
211 regs->al = val8; // val last written to Reg B
212 set_cf(regs, 0);
213}
214
215static void
216handle_1ab1(struct bregs *regs)
217{
218 // XXX - pcibios stuff
219 set_cf(regs, 1);
220}
221
222// Unsupported
223static void
224handle_1aXX(struct bregs *regs)
225{
226 set_cf(regs, 1);
227}
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500228
229// INT 1Ah Time-of-day Service Entry Point
230void VISIBLE
231handle_1a(struct bregs *regs)
232{
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500233 //debug_enter(regs);
234 switch (regs->ah) {
235 case 0x00: handle_1a00(regs); break;
236 case 0x01: handle_1a01(regs); break;
237 case 0x02: handle_1a02(regs); break;
238 case 0x03: handle_1a03(regs); break;
239 case 0x04: handle_1a04(regs); break;
240 case 0x05: handle_1a05(regs); break;
241 case 0x06: handle_1a06(regs); break;
242 case 0x07: handle_1a07(regs); break;
243 case 0xb1: handle_1ab1(regs); break;
244 default: handle_1aXX(regs); break;
245 }
246 debug_exit(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500247}
248
249// User Timer Tick
250void VISIBLE
251handle_1c(struct bregs *regs)
252{
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500253 //debug_enter(regs);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500254}
255
256// INT 08h System Timer ISR Entry Point
257void VISIBLE
258handle_08(struct bregs *regs)
259{
260// debug_enter(regs);
261
262 floppy_tick();
263
264 u32 counter = GET_BDA(timer_counter);
265 counter++;
266 // compare to one days worth of timer ticks at 18.2 hz
267 if (counter >= 0x001800B0) {
268 // there has been a midnight rollover at this point
269 counter = 0;
270 SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
271 }
272
273 SET_BDA(timer_counter, counter);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500274
275 // chain to user timer tick INT #0x1c
276 struct bregs br;
277 memset(&br, 0, sizeof(br));
278 call16_int(0x1c, &br);
279
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500280 eoi_master_pic();
281}
282
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500283// Set Interval requested.
284static void
285handle_158300(struct bregs *regs)
286{
287 if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING) {
288 // Interval already set.
289 DEBUGF("int15: Func 83h, failed, already waiting.\n" );
290 handle_ret(regs, RET_EUNSUPPORTED);
291 }
292 // Interval not already set.
293 SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
294 u32 v = (regs->es << 16) | regs->bx;
295 SET_BDA(ptr_user_wait_complete_flag, v);
296 v = (regs->dx << 16) | regs->cx;
297 SET_BDA(user_wait_timeout, v);
298
299 // Unmask IRQ8 so INT70 will get through.
300 u8 irqDisable = inb(PORT_PIC2_DATA);
301 outb(irqDisable & ~PIC2_IRQ8, PORT_PIC2_DATA);
302 // Turn on the Periodic Interrupt timer
303 u8 bRegister = inb_cmos(CMOS_STATUS_B);
304 outb_cmos(CMOS_STATUS_B, bRegister | CSB_EN_ALARM_IRQ);
305
306 set_cf(regs, 0); // XXX - no set ah?
307}
308
309// Clear interval requested
310static void
311handle_158301(struct bregs *regs)
312{
313 SET_BDA(rtc_wait_flag, 0); // Clear status byte
314 // Turn off the Periodic Interrupt timer
315 u8 bRegister = inb_cmos(CMOS_STATUS_B);
316 outb_cmos(CMOS_STATUS_B, bRegister & ~CSB_EN_ALARM_IRQ);
317 set_cf(regs, 0); // XXX - no set ah?
318}
319
320static void
321handle_1583XX(struct bregs *regs)
322{
323 regs->al--;
324 handle_ret(regs, RET_EUNSUPPORTED);
325}
326
327void
328handle_1583(struct bregs *regs)
329{
330 switch (regs->al) {
331 case 0x00: handle_158300(regs); break;
332 case 0x01: handle_158301(regs); break;
333 default: handle_1583XX(regs); break;
334 }
335}
336
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500337// int70h: IRQ8 - CMOS RTC
338void VISIBLE
339handle_70(struct bregs *regs)
340{
341 debug_enter(regs);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500342
343 // Check which modes are enabled and have occurred.
344 u8 registerB = inb_cmos(CMOS_STATUS_B);
345 u8 registerC = inb_cmos(CMOS_STATUS_C);
346
347 if (!(registerB & 0x60))
348 goto done;
349 if (registerC & 0x20) {
350 // Handle Alarm Interrupt.
351 struct bregs br;
352 memset(&br, 0, sizeof(br));
353 call16_int(0x4a, &br);
354 }
355 if (!(registerC & 0x40))
356 goto done;
357
358 // Handle Periodic Interrupt.
359
360 if (!GET_BDA(rtc_wait_flag))
361 goto done;
362
363 // Wait Interval (Int 15, AH=83) active.
364 u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds.
365 if (time < 0x3D1) {
366 // Done waiting.
367 u32 segoff = GET_BDA(ptr_user_wait_complete_flag);
368 u16 segment = segoff >> 16;
369 u16 offset = segoff & 0xffff;
370 // Turn off status byte.
371 SET_BDA(rtc_wait_flag, 0);
372 // Clear the Periodic Interrupt.
373 outb_cmos(registerB & 0x37, CMOS_STATUS_B);
374 // Write to specified flag byte.
375 u8 oldval = GET_FARVAR(segment, *(u8*)(offset+0));
376 SET_FARVAR(segment, *(u8*)(offset+0), oldval | 0x80);
377 } else {
378 // Continue waiting.
379 time -= 0x3D1;
380 SET_BDA(user_wait_timeout, time);
381 }
382
383done:
384 eoi_both_pics();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500385}