blob: 9009d49ca142c8c7fbe10d328893ddc95a612e01 [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//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "biosvar.h" // SET_BDA
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05009#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
Kevin O'Connord21c0892008-11-26 17:02:43 -050012#include "pic.h" // eoi_pic1
Kevin O'Connor9521e262008-07-04 13:04:29 -040013#include "bregs.h" // struct bregs
Kevin O'Connor15157a32008-12-13 11:10:37 -050014#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor4b60c002008-02-25 22:29:55 -050015
Kevin O'Connor5be04902008-05-18 17:12:06 -040016// RTC register flags
17#define RTC_A_UIP 0x80
Kevin O'Connorf3587592009-02-15 13:02:56 -050018
19#define RTC_B_SET 0x80
20#define RTC_B_PIE 0x40
21#define RTC_B_AIE 0x20
22#define RTC_B_UIE 0x10
23#define RTC_B_BIN 0x04
24#define RTC_B_24HR 0x02
25#define RTC_B_DSE 0x01
26
Kevin O'Connor5be04902008-05-18 17:12:06 -040027
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050028// Bits for PORT_PS2_CTRLB
29#define PPCB_T2GATE (1<<0)
30#define PPCB_SPKR (1<<1)
31#define PPCB_T2OUT (1<<5)
32
33// Bits for PORT_PIT_MODE
34#define PM_SEL_TIMER0 (0<<6)
35#define PM_SEL_TIMER1 (1<<6)
36#define PM_SEL_TIMER2 (2<<6)
37#define PM_SEL_READBACK (3<<6)
38#define PM_ACCESS_LATCH (0<<4)
39#define PM_ACCESS_LOBYTE (1<<4)
40#define PM_ACCESS_HIBYTE (2<<4)
41#define PM_ACCESS_WORD (3<<4)
42#define PM_MODE0 (0<<1)
43#define PM_MODE1 (1<<1)
44#define PM_MODE2 (2<<1)
45#define PM_MODE3 (3<<1)
46#define PM_MODE4 (4<<1)
47#define PM_MODE5 (5<<1)
48#define PM_CNT_BINARY (0<<0)
49#define PM_CNT_BCD (1<<0)
50
51
52/****************************************************************
53 * TSC timer
54 ****************************************************************/
55
56#define PIT_TICK_RATE 1193182 // Underlying HZ of PIT
57#define CALIBRATE_COUNT 0x800 // Approx 1.7ms
58
Kevin O'Connor372e0712009-09-09 09:51:31 -040059u32 cpu_khz VAR16VISIBLE;
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050060
61static void
62calibrate_tsc()
63{
64 // Setup "timer2"
65 u8 orig = inb(PORT_PS2_CTRLB);
66 outb((orig & ~PPCB_SPKR) | PPCB_T2GATE, PORT_PS2_CTRLB);
67 /* binary, mode 0, LSB/MSB, Ch 2 */
68 outb(PM_SEL_TIMER2|PM_ACCESS_WORD|PM_MODE0|PM_CNT_BINARY, PORT_PIT_MODE);
69 /* LSB of ticks */
70 outb(CALIBRATE_COUNT & 0xFF, PORT_PIT_COUNTER2);
71 /* MSB of ticks */
72 outb(CALIBRATE_COUNT >> 8, PORT_PIT_COUNTER2);
73
74 u64 start = rdtscll();
75 while ((inb(PORT_PS2_CTRLB) & PPCB_T2OUT) == 0)
76 ;
77 u64 end = rdtscll();
78
79 // Restore PORT_PS2_CTRLB
80 outb(orig, PORT_PS2_CTRLB);
81
82 // Store calibrated cpu khz.
83 u64 diff = end - start;
84 dprintf(6, "tsc calibrate start=%u end=%u diff=%u\n"
85 , (u32)start, (u32)end, (u32)diff);
86 u32 hz = diff * PIT_TICK_RATE / CALIBRATE_COUNT;
Kevin O'Connor15157a32008-12-13 11:10:37 -050087 SET_GLOBAL(cpu_khz, hz / 1000);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050088
89 dprintf(1, "CPU Mhz=%u\n", hz / 1000000);
90}
91
92static void
93tscsleep(u64 diff)
94{
95 u64 start = rdtscll();
96 u64 end = start + diff;
Kevin O'Connorf3587592009-02-15 13:02:56 -050097 while (rdtscll() <= end)
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -050098 cpu_relax();
99}
100
101void
102ndelay(u32 count)
103{
Kevin O'Connor15157a32008-12-13 11:10:37 -0500104 u32 khz = GET_GLOBAL(cpu_khz);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500105 tscsleep(count * khz / 1000000);
106}
107void
108udelay(u32 count)
109{
Kevin O'Connor15157a32008-12-13 11:10:37 -0500110 u32 khz = GET_GLOBAL(cpu_khz);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500111 tscsleep(count * khz / 1000);
112}
113void
114mdelay(u32 count)
115{
Kevin O'Connor15157a32008-12-13 11:10:37 -0500116 u32 khz = GET_GLOBAL(cpu_khz);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500117 tscsleep(count * khz);
118}
119
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500120// Return the TSC value that is 'msecs' time in the future.
121u64
122calc_future_tsc(u32 msecs)
123{
Kevin O'Connor15157a32008-12-13 11:10:37 -0500124 u32 khz = GET_GLOBAL(cpu_khz);
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500125 return rdtscll() + ((u64)khz * msecs);
126}
127
Kevin O'Connor5be04902008-05-18 17:12:06 -0400128
129/****************************************************************
130 * Init
131 ****************************************************************/
132
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500133static int
134rtc_updating()
135{
136 // This function checks to see if the update-in-progress bit
137 // is set in CMOS Status Register A. If not, it returns 0.
138 // If it is set, it tries to wait until there is a transition
139 // to 0, and will return 0 if such a transition occurs. A -1
140 // is returned only after timing out. The maximum period
Kevin O'Connor4f5586c2009-02-16 10:14:10 -0500141 // that this bit should be set is constrained to (1984+244)
142 // useconds, so we wait for 3 msec max.
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500143
Kevin O'Connorf3587592009-02-15 13:02:56 -0500144 if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500145 return 0;
Kevin O'Connor4f5586c2009-02-16 10:14:10 -0500146 u64 end = calc_future_tsc(3);
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500147 do {
Kevin O'Connorf3587592009-02-15 13:02:56 -0500148 if ((inb_cmos(CMOS_STATUS_A) & RTC_A_UIP) == 0)
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500149 return 0;
150 } while (rdtscll() <= end);
151
152 // update-in-progress never transitioned to 0
153 return -1;
154}
155
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500156static void
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400157pit_setup()
158{
159 // timer0: binary count, 16bit count, mode 2
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500160 outb(PM_SEL_TIMER0|PM_ACCESS_WORD|PM_MODE2|PM_CNT_BINARY, PORT_PIT_MODE);
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400161 // maximum count of 0000H = 18.2Hz
162 outb(0x0, PORT_PIT_COUNTER0);
163 outb(0x0, PORT_PIT_COUNTER0);
164}
165
Kevin O'Connorf3587592009-02-15 13:02:56 -0500166static void
167init_rtc()
168{
Kevin O'Connor4f5586c2009-02-16 10:14:10 -0500169 outb_cmos(0x26, CMOS_STATUS_A); // 32,768Khz src, 976.5625us updates
Kevin O'Connorf3587592009-02-15 13:02:56 -0500170 u8 regB = inb_cmos(CMOS_STATUS_B);
171 outb_cmos((regB & RTC_B_DSE) | RTC_B_24HR, CMOS_STATUS_B);
172 inb_cmos(CMOS_STATUS_C);
173 inb_cmos(CMOS_STATUS_D);
174}
175
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400176static u32
177bcd2bin(u8 val)
178{
179 return (val & 0xf) + ((val >> 4) * 10);
180}
181
182void
183timer_setup()
184{
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400185 dprintf(3, "init timer\n");
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500186 calibrate_tsc();
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400187 pit_setup();
188
Kevin O'Connorf3587592009-02-15 13:02:56 -0500189 init_rtc();
Kevin O'Connor4e6c9702008-12-13 10:45:50 -0500190 rtc_updating();
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400191 u32 seconds = bcd2bin(inb_cmos(CMOS_RTC_SECONDS));
192 u32 ticks = (seconds * 18206507) / 1000000;
193 u32 minutes = bcd2bin(inb_cmos(CMOS_RTC_MINUTES));
194 ticks += (minutes * 10923904) / 10000;
195 u32 hours = bcd2bin(inb_cmos(CMOS_RTC_HOURS));
196 ticks += (hours * 65543427) / 1000;
197 SET_BDA(timer_counter, ticks);
198 SET_BDA(timer_rollover, 0);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400199
Kevin O'Connord21c0892008-11-26 17:02:43 -0500200 enable_hwirq(0, entry_08);
201 enable_hwirq(8, entry_70);
Kevin O'Connore6eb3f52008-04-13 17:37:41 -0400202}
203
Kevin O'Connor5be04902008-05-18 17:12:06 -0400204
205/****************************************************************
206 * Standard clock functions
207 ****************************************************************/
208
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500209// get current clock count
210static void
211handle_1a00(struct bregs *regs)
212{
213 u32 ticks = GET_BDA(timer_counter);
214 regs->cx = ticks >> 16;
215 regs->dx = ticks;
216 regs->al = GET_BDA(timer_rollover);
217 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor6c781222008-03-09 12:19:23 -0400218 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500219}
220
221// Set Current Clock Count
222static void
223handle_1a01(struct bregs *regs)
224{
225 u32 ticks = (regs->cx << 16) | regs->dx;
226 SET_BDA(timer_counter, ticks);
227 SET_BDA(timer_rollover, 0); // reset flag
Kevin O'Connor15157a32008-12-13 11:10:37 -0500228 // XXX - should use set_code_success()?
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500229 regs->ah = 0;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400230 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500231}
232
233// Read CMOS Time
234static void
235handle_1a02(struct bregs *regs)
236{
237 if (rtc_updating()) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400238 set_fail(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500239 return;
240 }
241
242 regs->dh = inb_cmos(CMOS_RTC_SECONDS);
243 regs->cl = inb_cmos(CMOS_RTC_MINUTES);
244 regs->ch = inb_cmos(CMOS_RTC_HOURS);
Kevin O'Connorf3587592009-02-15 13:02:56 -0500245 regs->dl = inb_cmos(CMOS_STATUS_B) & RTC_B_DSE;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500246 regs->ah = 0;
247 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400248 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500249}
250
251// Set CMOS Time
252static void
253handle_1a03(struct bregs *regs)
254{
255 // Using a debugger, I notice the following masking/setting
256 // of bits in Status Register B, by setting Reg B to
257 // a few values and getting its value after INT 1A was called.
258 //
259 // try#1 try#2 try#3
260 // before 1111 1101 0111 1101 0000 0000
261 // after 0110 0010 0110 0010 0000 0010
262 //
263 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
264 // My assumption: RegB = ((RegB & 01100000b) | 00000010b)
265 if (rtc_updating()) {
266 init_rtc();
267 // fall through as if an update were not in progress
268 }
269 outb_cmos(regs->dh, CMOS_RTC_SECONDS);
270 outb_cmos(regs->cl, CMOS_RTC_MINUTES);
271 outb_cmos(regs->ch, CMOS_RTC_HOURS);
272 // Set Daylight Savings time enabled bit to requested value
Kevin O'Connorf3587592009-02-15 13:02:56 -0500273 u8 val8 = ((inb_cmos(CMOS_STATUS_B) & (RTC_B_PIE|RTC_B_AIE))
274 | RTC_B_24HR | (regs->dl & RTC_B_DSE));
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500275 outb_cmos(val8, CMOS_STATUS_B);
276 regs->ah = 0;
277 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400278 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500279}
280
281// Read CMOS Date
282static void
283handle_1a04(struct bregs *regs)
284{
285 regs->ah = 0;
286 if (rtc_updating()) {
Kevin O'Connor6c781222008-03-09 12:19:23 -0400287 set_fail(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500288 return;
289 }
290 regs->cl = inb_cmos(CMOS_RTC_YEAR);
291 regs->dh = inb_cmos(CMOS_RTC_MONTH);
292 regs->dl = inb_cmos(CMOS_RTC_DAY_MONTH);
Kevin O'Connorf3587592009-02-15 13:02:56 -0500293 if (CONFIG_COREBOOT) {
294 if (regs->cl > 0x80)
295 regs->ch = 0x19;
296 else
297 regs->ch = 0x20;
298 } else {
299 regs->ch = inb_cmos(CMOS_CENTURY);
300 }
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500301 regs->al = regs->ch;
Kevin O'Connor6c781222008-03-09 12:19:23 -0400302 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500303}
304
305// Set CMOS Date
306static void
307handle_1a05(struct bregs *regs)
308{
309 // Using a debugger, I notice the following masking/setting
310 // of bits in Status Register B, by setting Reg B to
311 // a few values and getting its value after INT 1A was called.
312 //
313 // try#1 try#2 try#3 try#4
314 // before 1111 1101 0111 1101 0000 0010 0000 0000
315 // after 0110 1101 0111 1101 0000 0010 0000 0000
316 //
317 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
318 // My assumption: RegB = (RegB & 01111111b)
319 if (rtc_updating()) {
320 init_rtc();
Kevin O'Connor6c781222008-03-09 12:19:23 -0400321 set_fail(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500322 return;
323 }
324 outb_cmos(regs->cl, CMOS_RTC_YEAR);
325 outb_cmos(regs->dh, CMOS_RTC_MONTH);
326 outb_cmos(regs->dl, CMOS_RTC_DAY_MONTH);
Kevin O'Connorf3587592009-02-15 13:02:56 -0500327 if (!CONFIG_COREBOOT)
328 outb_cmos(regs->ch, CMOS_CENTURY);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400329 // clear halt-clock bit
330 u8 val8 = inb_cmos(CMOS_STATUS_B) & ~RTC_B_SET;
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500331 outb_cmos(val8, CMOS_STATUS_B);
332 regs->ah = 0;
333 regs->al = val8; // AL = val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400334 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500335}
336
337// Set Alarm Time in CMOS
338static void
339handle_1a06(struct bregs *regs)
340{
341 // Using a debugger, I notice the following masking/setting
342 // of bits in Status Register B, by setting Reg B to
343 // a few values and getting its value after INT 1A was called.
344 //
345 // try#1 try#2 try#3
346 // before 1101 1111 0101 1111 0000 0000
347 // after 0110 1111 0111 1111 0010 0000
348 //
349 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
350 // My assumption: RegB = ((RegB & 01111111b) | 00100000b)
351 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
352 regs->ax = 0;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500353 if (val8 & RTC_B_AIE) {
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500354 // Alarm interrupt enabled already
Kevin O'Connor6c781222008-03-09 12:19:23 -0400355 set_fail(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500356 return;
357 }
358 if (rtc_updating()) {
359 init_rtc();
360 // fall through as if an update were not in progress
361 }
362 outb_cmos(regs->dh, CMOS_RTC_SECONDS_ALARM);
363 outb_cmos(regs->cl, CMOS_RTC_MINUTES_ALARM);
364 outb_cmos(regs->ch, CMOS_RTC_HOURS_ALARM);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500365 // enable Status Reg B alarm bit, clear halt clock bit
Kevin O'Connor5be04902008-05-18 17:12:06 -0400366 outb_cmos((val8 & ~RTC_B_SET) | RTC_B_AIE, CMOS_STATUS_B);
Kevin O'Connor6c781222008-03-09 12:19:23 -0400367 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500368}
369
370// Turn off Alarm
371static void
372handle_1a07(struct bregs *regs)
373{
374 // Using a debugger, I notice the following masking/setting
375 // of bits in Status Register B, by setting Reg B to
376 // a few values and getting its value after INT 1A was called.
377 //
378 // try#1 try#2 try#3 try#4
379 // before 1111 1101 0111 1101 0010 0000 0010 0010
380 // after 0100 0101 0101 0101 0000 0000 0000 0010
381 //
382 // Bit4 in try#1 flipped in hardware (forced low) due to bit7=1
383 // My assumption: RegB = (RegB & 01010111b)
384 u8 val8 = inb_cmos(CMOS_STATUS_B); // Get Status Reg B
385 // clear clock-halt bit, disable alarm bit
Kevin O'Connor5be04902008-05-18 17:12:06 -0400386 outb_cmos(val8 & ~(RTC_B_SET|RTC_B_AIE), CMOS_STATUS_B);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500387 regs->ah = 0;
388 regs->al = val8; // val last written to Reg B
Kevin O'Connor6c781222008-03-09 12:19:23 -0400389 set_success(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500390}
391
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500392// Unsupported
393static void
394handle_1aXX(struct bregs *regs)
395{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400396 set_fail(regs);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500397}
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500398
399// INT 1Ah Time-of-day Service Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500400void VISIBLE16
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500401handle_1a(struct bregs *regs)
402{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400403 debug_enter(regs, DEBUG_HDL_1a);
Kevin O'Connor4b60c002008-02-25 22:29:55 -0500404 switch (regs->ah) {
405 case 0x00: handle_1a00(regs); break;
406 case 0x01: handle_1a01(regs); break;
407 case 0x02: handle_1a02(regs); break;
408 case 0x03: handle_1a03(regs); break;
409 case 0x04: handle_1a04(regs); break;
410 case 0x05: handle_1a05(regs); break;
411 case 0x06: handle_1a06(regs); break;
412 case 0x07: handle_1a07(regs); break;
413 case 0xb1: handle_1ab1(regs); break;
414 default: handle_1aXX(regs); break;
415 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500416}
417
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500418// INT 08h System Timer ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500419void VISIBLE16
Kevin O'Connored128492008-03-11 11:14:59 -0400420handle_08()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500421{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400422 debug_isr(DEBUG_ISR_08);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500423
424 floppy_tick();
425
426 u32 counter = GET_BDA(timer_counter);
427 counter++;
428 // compare to one days worth of timer ticks at 18.2 hz
429 if (counter >= 0x001800B0) {
430 // there has been a midnight rollover at this point
431 counter = 0;
432 SET_BDA(timer_rollover, GET_BDA(timer_rollover) + 1);
433 }
434
435 SET_BDA(timer_counter, counter);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500436
437 // chain to user timer tick INT #0x1c
Kevin O'Connora83ff552009-01-01 21:00:59 -0500438 u32 eax=0, flags;
439 call16_simpint(0x1c, &eax, &flags);
Kevin O'Connored128492008-03-11 11:14:59 -0400440
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400441 eoi_pic1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500442}
443
Kevin O'Connor5be04902008-05-18 17:12:06 -0400444
445/****************************************************************
446 * Periodic timer
447 ****************************************************************/
448
449static int
Kevin O'Connor72743f12008-05-24 23:04:09 -0400450set_usertimer(u32 usecs, u16 seg, u16 offset)
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500451{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400452 if (GET_BDA(rtc_wait_flag) & RWS_WAIT_PENDING)
453 return -1;
454
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500455 // Interval not already set.
456 SET_BDA(rtc_wait_flag, RWS_WAIT_PENDING); // Set status byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400457 SET_BDA(user_wait_complete_flag, SEGOFF(seg, offset));
Kevin O'Connor72743f12008-05-24 23:04:09 -0400458 SET_BDA(user_wait_timeout, usecs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500459
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500460 // Turn on the Periodic Interrupt timer
461 u8 bRegister = inb_cmos(CMOS_STATUS_B);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400462 outb_cmos(bRegister | RTC_B_PIE, CMOS_STATUS_B);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500463
Kevin O'Connor5be04902008-05-18 17:12:06 -0400464 return 0;
465}
466
467static void
468clear_usertimer()
469{
470 // Turn off status byte.
471 SET_BDA(rtc_wait_flag, 0);
472 // Clear the Periodic Interrupt.
473 u8 bRegister = inb_cmos(CMOS_STATUS_B);
474 outb_cmos(bRegister & ~RTC_B_PIE, CMOS_STATUS_B);
475}
476
Kevin O'Connor5be04902008-05-18 17:12:06 -0400477#define RET_ECLOCKINUSE 0x83
478
Kevin O'Connord21c0892008-11-26 17:02:43 -0500479// Wait for CX:DX microseconds
Kevin O'Connor5be04902008-05-18 17:12:06 -0400480void
481handle_1586(struct bregs *regs)
482{
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500483 // Use the rtc to wait for the specified time.
484 u8 statusflag = 0;
485 u32 count = (regs->cx << 16) | regs->dx;
486 int ret = set_usertimer(count, GET_SEG(SS), (u32)&statusflag);
487 if (ret) {
Kevin O'Connor5be04902008-05-18 17:12:06 -0400488 set_code_fail(regs, RET_ECLOCKINUSE);
Kevin O'Connorbc2aecd2008-11-28 16:40:06 -0500489 return;
490 }
491 irq_enable();
492 while (!statusflag)
493 cpu_relax();
494 irq_disable();
495 set_success(regs);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400496}
497
498// Set Interval requested.
499static void
500handle_158300(struct bregs *regs)
501{
502 int ret = set_usertimer((regs->cx << 16) | regs->dx, regs->es, regs->bx);
503 if (ret)
504 // Interval already set.
505 set_code_fail(regs, RET_EUNSUPPORTED);
506 else
507 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500508}
509
510// Clear interval requested
511static void
512handle_158301(struct bregs *regs)
513{
Kevin O'Connor5be04902008-05-18 17:12:06 -0400514 clear_usertimer();
515 set_success(regs);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500516}
517
518static void
519handle_1583XX(struct bregs *regs)
520{
Kevin O'Connor6c781222008-03-09 12:19:23 -0400521 set_code_fail(regs, RET_EUNSUPPORTED);
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500522 regs->al--;
Kevin O'Connorbdce35f2008-02-26 21:33:14 -0500523}
524
525void
526handle_1583(struct bregs *regs)
527{
528 switch (regs->al) {
529 case 0x00: handle_158300(regs); break;
530 case 0x01: handle_158301(regs); break;
531 default: handle_1583XX(regs); break;
532 }
533}
534
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500535// int70h: IRQ8 - CMOS RTC
Kevin O'Connor19786762008-03-05 21:09:59 -0500536void VISIBLE16
Kevin O'Connored128492008-03-11 11:14:59 -0400537handle_70()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500538{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400539 debug_isr(DEBUG_ISR_70);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500540
541 // Check which modes are enabled and have occurred.
542 u8 registerB = inb_cmos(CMOS_STATUS_B);
543 u8 registerC = inb_cmos(CMOS_STATUS_C);
544
Kevin O'Connor5be04902008-05-18 17:12:06 -0400545 if (!(registerB & (RTC_B_PIE|RTC_B_AIE)))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500546 goto done;
Kevin O'Connorf3587592009-02-15 13:02:56 -0500547 if (registerC & RTC_B_AIE) {
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500548 // Handle Alarm Interrupt.
Kevin O'Connora83ff552009-01-01 21:00:59 -0500549 u32 eax=0, flags;
550 call16_simpint(0x4a, &eax, &flags);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500551 }
Kevin O'Connorf3587592009-02-15 13:02:56 -0500552 if (!(registerC & RTC_B_PIE))
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500553 goto done;
554
555 // Handle Periodic Interrupt.
556
557 if (!GET_BDA(rtc_wait_flag))
558 goto done;
559
560 // Wait Interval (Int 15, AH=83) active.
561 u32 time = GET_BDA(user_wait_timeout); // Time left in microseconds.
562 if (time < 0x3D1) {
Kevin O'Connor5be04902008-05-18 17:12:06 -0400563 // Done waiting - write to specified flag byte.
Kevin O'Connor9f985422009-09-09 11:34:39 -0400564 struct segoff_s segoff = GET_BDA(user_wait_complete_flag);
565 u16 ptr_seg = segoff.seg;
566 u8 *ptr_far = (u8*)(segoff.offset+0);
567 u8 oldval = GET_FARVAR(ptr_seg, *ptr_far);
568 SET_FARVAR(ptr_seg, *ptr_far, oldval | 0x80);
Kevin O'Connor5be04902008-05-18 17:12:06 -0400569
570 clear_usertimer();
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500571 } else {
572 // Continue waiting.
Kevin O'Connorf3587592009-02-15 13:02:56 -0500573 time -= 977;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500574 SET_BDA(user_wait_timeout, time);
575 }
576
577done:
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400578 eoi_pic2();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500579}