/*****************************************************************************\

Module Name:    MtxTimer.c

Description:    Timer functions provided by the library.

References:     None.

    Copyright (c) 2000, Matrox Graphics Inc.
    All Rights Reserved.

\*****************************************************************************/

// ----------------------------------------------------------------------------
//                          H E A D E R   F I L E S
// ----------------------------------------------------------------------------

#include "precomp.h"

// ----------------------------------------------------------------------------
//                   C O N S T A N T S   A N D   T Y P E S
// ----------------------------------------------------------------------------
#define IN
#define OUT

// Timers
#define TIMER_2_CTRL        0x43
#define TIMER_2_COUNT       0x42
#define SYS_CTRL_PORT_B     0x61

// System Control Port Fields
#define SYS_SPKEN           0x02
#define SYS_CNT2EN          0x01
#define CNT2_FINISH         0x20

// Timers Control Fields
#define BINARY_COUNT                0
#define BCD_COUNT                   1

                                        //              Terminal Count --------+
                                        //                                     v
// Timers modes of operations
#define SIGNAL_ON_TERMINAL_COUNT    0   // Interrupt on terminal count: _______|~~~~~~~ 
#define HW_RETRIG_ONE_SHOT          1   // H/W retriggerable One-Shot:  _______|~~~~~~~ 
#define RATE_GENERATOR              2   // Rate Generator:              |_|~~~~|_|~~~~| 
#define SQUARE_WAVE                 3   // Square wave:                 ___|~~~|___|~~~ 
#define SW_TRIGGER_STROBE           4   // S/W retriggerable Strobe:    ~~~~~~~|_|~~~~~
#define HW_TRIGGER_STROBE           5   // H/W retriggerable Strobe:    ~~~~~~~|_|~~~~~

// Timers commands
#define COUNTER_LATCH_COMMAND       0
#define RW_LSB                      1   // R/W LSB of counter
#define RW_MSB                      2   // R/W MSB of counter
#define RW_LSB_THEN_MSB             3   // R/W LSB then MSB of counter

#define COUNTER_0                   0
#define COUNTER_1                   1
#define COUNTER_2                   2
#define READ_BACK_COMMAND           3

#define DELAY_55ms                  0xFFFF

typedef union
{
    BYTE   fullreg;
    struct
    {
        BYTE   CountDownSelect : 1;     // TimerCTRL<0>
        BYTE   CounterMode     : 3;     // TimerCTRL<3:1>
        BYTE   RW_Select       : 2;     // TimerCTRL<5:4>
        BYTE   CounterSelect   : 2;     // TimerCTRL<7:6>
    }f;
} TIMER_CTRL;

#define outp(port, value) outb(value, port)
#define inp inb

// ----------------------------------------------------------------------------
//                      G L O B A L   V A R I A B L E S
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
//              L O C A L   F U N C T I O N   P R O T O T Y P E S
// ----------------------------------------------------------------------------

DWORD Timer2Delay(WORD);

// ----------------------------------------------------------------------------
//                     I N L I N E S   A N D   M A C R O S
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
//     I N T E R F A C E   F U N C T I O N   I M P L E M E N T A T I O N 
// ----------------------------------------------------------------------------

/****************************************************************************\

Function      : tmDelay

Description   : 
        @func   Wait for dwDelayus microseconds.
        @end

I/O Desc.     : 
    Input     : 
        @parm   TIMERDEVICE* | hTimer | Handle to the Timer structure 
                                        containing informations required to 
                                        use it (not used in DOS).
        
        @parm   DWORD   | dwDelayus    | Delay in uSec
        @end

Return Val    : 
        @rdesc  None.
        @end


Function used : 
        @xref   <f Timer2Delay>
         @end

Creation Date : January 10, 2001

\*****************************************************************************/
DWORD tmDelay(IN DWORD dwDelayus)
{
    ClientMicroDelay(dwDelayus);
    return TRUE;
}


// ----------------------------------------------------------------------------
//          L O C A L   F U N C T I O N   I M P L E M E N T A T I O N 
// ----------------------------------------------------------------------------

/*****************************************************************************\

Function      : Timer2Delay

Description   :  
        @func   Uses the Timer 2 to perform a delay.
         @end

I/O Desc.     : 
        @parm   WORD | byCountValue | Value to program in Timer 2 counter
         @end

Local Var     : DWORD | dwTimeOut   | WatchDog in case an interrupt happens
                                      and uses the timer.
                                   
Return Val    : None
         @end

Comments      : 
    @comm        tmDelay brings some overhead, and empirical experiments
                 gives the following formula: 
                        Counter Value = (Desired Time - 32 us) / 839 ns
                 Some experiment results (with the Bios function): 
                                       Loading value        Time elapse
                                           1                   32 us
                                          50                   74 us
                                         240                  232 us
                                         500                  450 us
                                        1200                 1.04 ms
                                           0                   55 ms
    @end

Creation Date : January 10, 2001

\*****************************************************************************/
#if defined(__i386__)
DWORD Timer2Delay(WORD byCountValue)
{
    DWORD   dwTimeOut;

    // This write stops the counter
    ClientIoWriteByte(TIMER_2_COUNT, (BYTE) byCountValue);              
    
    // This write restarts the counter with new value
    ClientIoWriteByte(TIMER_2_COUNT, (BYTE) (byCountValue >> 8));       
    
    // We don't want to lock. So we use a time-out.
    // We know the longuest delay for the PIT is 55 ms. 
    // (1 193 181 Hz / 2^16)^-1
    // The following intructions in the loop in REAL MODE take
    // 11 cycles to execute.  So a 2GHz machine, should take no
    // more than 10 000 000 iterations for the Timer 2 to time-out.
    dwTimeOut = 0;
    while (dwTimeOut < 10000000)
    {
        if ( ClientIoReadByte(SYS_CTRL_PORT_B) & CNT2_FINISH )
        {
            break;
        }
        dwTimeOut++;
    }

    return TRUE;
}
#endif
