title  "ACPI Real Time Clock Functions"
;++
;
; Copyright (c) 1989  Microsoft Corporation
;
; Module Name:
;
;    pmclock.asm
;
; Abstract:
;
;    This module implements the code for ACPI-related RTC
;    functions.
;
; Author:
;
;    Jake Oshins (jakeo) March 28, 1997
;
; Environment:
;
;    Kernel mode only.
;
; Revision History:
;
;    Split from pmclock.asm due to PIIX4 bugs.
;
;--

.386p
        .xlist
include hal386.inc
include callconv.inc                    ; calling convention macros
include mac386.inc
include i386\ix8259.inc
include i386\ixcmos.inc
include xxacpi.h

        .list

        extrn   _HalpFixedAcpiDescTable:DWORD
        EXTRNP  _DbgBreakPoint,0,IMPORT
        EXTRNP  _HalpAcquireCmosSpinLock  ,0
        EXTRNP  _HalpReleaseCmosSpinLock  ,0
        extrn   _HalpRtcRegA:BYTE
        extrn   _HalpRtcRegB:BYTE
        extrn   _HalpCmosCenturyOffset:DWORD

INIT    SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;++
;
;   VOID
;   HalpInitializeCmos(
;       VOID
;       )
;
;   This routine reads CMOS and initializes globals required for
;   CMOS access, such as the location of the century byte.
;
;--

cPublicProc _HalpInitializeCmos,0
cPublicFpo 0,0        

        ;
        ; If the century byte is filled in, use it.
        ;
        
        movzx   eax, byte ptr [RTC_CENTURY]
        or      al, al
        jnz     short @f
        
        ;
        ; Assume default
        ;
        mov     eax, RTC_OFFSET_CENTURY

@@:
        mov     _HalpCmosCenturyOffset, eax
        
        stdRET  _HalpInitializeCmos

stdENDP _HalpInitializeCmos

INIT   ends

_TEXT$03   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;   NTSTATUS
;   HalpSetWakeAlarm (
;       IN ULONGLONG    WakeSystemTime,
;       IN PTIME_FIELDS WakeTimeFields OPTIONAL
;       )
;   /*++
;
;   Routine Description:
;
;       This routine sets the real-time clock's alarm to go
;       off at a specified time in the future and programs
;       the ACPI chipset so that this wakes the computer.
;
;   Arguments:
;
;       WakeSystemTime - amount of time that passes before we wake
;       WakeTimeFields - time to wake broken down into TIME_FIELDS
;
;   Return Value:
;
;       status
;
;   --*/
WakeSystemTime  equ [esp + 4]
WakeTimeFields  equ [esp + 12]

cPublicProc _HalpSetWakeAlarm, 3
cPublicFpo 3, 0

if DBG
hswawait0:
        mov     ecx, 100
hswawait:
        push    ecx
else
hswawait:
endif
        stdCall   _HalpAcquireCmosSpinLock
        mov     ecx, 100
        align   4
hswa00: mov     al, 0Ah                 ; Specify register A
        CMOS_READ                       ; (al) = CMOS register A
        test    al, CMOS_STATUS_BUSY    ; Is time update in progress?
        jz      short hswa10            ; if z, no, go write CMOS time
        loop    short hswa00            ; otherwise, try again.

;
; CMOS is still busy. Try again ...
;

        stdCall _HalpReleaseCmosSpinLock
if DBG
        pop     ecx
        loop    short hswawait
        stdCall _DbgBreakPoint
        jmp     short hswawait0
else
        jmp     short hswawait
endif
        align   4
if DBG
hswa10:
        pop     ecx
else
hswa10:
endif
        mov     edx, WakeTimeFields     ; (edx)-> TIME_FIELDS structure

        mov     al, [edx].TfSecond      ; Read second in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_SECOND_ALARM
        CMOS_WRITE

        mov     al, [edx].TfMinute      ; Read minute in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_MINUTE_ALARM
        CMOS_WRITE

        mov     al, [edx].TfHour        ; Read Hour in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, RTC_OFFSET_HOUR_ALARM
        CMOS_WRITE

        ; test to see if RTC_DAY_ALRM is supported
        mov     cl, byte ptr [RTC_DAY_ALRM]
        or      cl, cl
        jz      hswa20

        mov     al, [edx].TfDay         ; Read day in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, cl
        CMOS_WRITE

        ; test to see if RTC_MON_ALRM is supported
        mov     cl, byte ptr [RTC_MON_ALRM]
        or      cl, cl
        jz      hswa20

        mov     al, [edx].TfMonth       ; Read month in TIME_FIELDS
        BIN_TO_BCD
        mov     ah, al
        mov     al, cl
        CMOS_WRITE

;
; Don't clobber the Daylight Savings Time bit in register B, because we
; stash the LastKnownGood "environment variable" there.
;
hswa20:
        mov     ax, 0bh
        CMOS_READ
        and     al, 1
        mov     ah, al
        or      ah, REGISTER_B_ENABLE_ALARM_INTERRUPT or REGISTER_B_24HOUR_MODE
        mov     al, 0bh
        CMOS_WRITE                      ; Initialize it
        mov     al,0CH                  ; Register C
        CMOS_READ                       ; Read to initialize
        mov     al,0DH                  ; Register D
        CMOS_READ                       ; Read to initialize


        stdCall   _HalpReleaseCmosSpinLock

        xor     eax, eax                ; return STATUS_SUCCESS
        stdRET  _HalpSetWakeAlarm
stdENDP _HalpSetWakeAlarm

_TEXT$03   ends

PAGELK   SEGMENT DWORD PUBLIC 'CODE'
        ASSUME  DS:FLAT, ES:FLAT, SS:NOTHING, FS:NOTHING, GS:NOTHING

;++
;
; VOID
; HalpSetClockBeforeSleep (
;    VOID
;    )
;
; Routine Description:
;
;    This routine sets the RTC such that it will not generate
;    periodic interrupts while the machine is sleeping, as this
;    could be interpretted as an RTC wakeup event.
;
; Arguments:
;
; Return Value:
;
;    None
;
;--
cPublicProc _HalpSetClockBeforeSleep, 0
cPublicFpo 0, 0

        stdCall   _HalpAcquireCmosSpinLock

        mov     al, 0ah
        CMOS_READ
        mov     _HalpRtcRegA, al        ; save RTC Register A

        or      al, al
        jnz     @f                      ; TEMP - debug stop
    int 3                               ; looking for reg-a corruption
@@:

        mov     al, 0bh
        CMOS_READ
        mov     _HalpRtcRegB, al        ; save RTC Register B
        and     al, not REGISTER_B_ENABLE_PERIODIC_INTERRUPT
        or      al, REGISTER_B_24HOUR_MODE
        mov     ah, al
        mov     al, 0bh
        CMOS_WRITE                      ; Initialize it
        mov     al,0CH                  ; Register C
        CMOS_READ                       ; Read to initialize
        mov     al,0DH                  ; Register D
        CMOS_READ                       ; Read to initialize


        stdCall   _HalpReleaseCmosSpinLock

        stdRET  _HalpSetClockBeforeSleep
stdENDP _HalpSetClockBeforeSleep


;++
;
; VOID
; HalpSetClockAfterSleep (
;    VOID
;    )
;
; Routine Description:
;
;    This routine sets the RTC back to the way it was
;    before a call to HalpSetClockBeforeSleep.
;
; Arguments:
;
; Return Value:
;
;    None
;
;--
cPublicProc _HalpSetClockAfterSleep, 0
cPublicFpo 0, 0

        stdCall   _HalpAcquireCmosSpinLock

        mov     ah, _HalpRtcRegA        ; restore RTC Register A
        mov     al, 0ah
        CMOS_WRITE

        mov     ah, _HalpRtcRegB        ; restore RTC Register B
        and     ah, not REGISTER_B_ENABLE_ALARM_INTERRUPT
        or      ah, REGISTER_B_24HOUR_MODE
        mov     al, 0bh
        CMOS_WRITE                      ; Initialize it
        mov     al,0CH                  ; Register C
        CMOS_READ                       ; Read to initialize
        mov     al,0DH                  ; Register D
        CMOS_READ                       ; Read to initialize

        stdCall   _HalpReleaseCmosSpinLock

        stdRET  _HalpSetClockAfterSleep
stdENDP _HalpSetClockAfterSleep

PAGELK   ends

        end