/*-------------------------------------------------------------------------+ | timer.c v1.1 - PC386 BSP - 1997/08/07 +--------------------------------------------------------------------------+ | This file contains the PC386 timer package. +--------------------------------------------------------------------------+ | NOTE: It is important that the timer start/stop overhead be determined | when porting or modifying this code. +--------------------------------------------------------------------------+ | (C) Copyright 1997 - | - NavIST Group - Real-Time Distributed Systems and Industrial Automation | | http://pandora.ist.utl.pt | | Instituto Superior Tecnico * Lisboa * PORTUGAL +--------------------------------------------------------------------------+ | Disclaimer: | | This file is provided "AS IS" without warranty of any kind, either | expressed or implied. +--------------------------------------------------------------------------+ | This code is base on: | timer.c,v 1.7 1995/12/19 20:07:43 joel Exp - go32 BSP | With the following copyright notice: | ************************************************************************** | * COPYRIGHT (c) 1989-1998. | * On-Line Applications Research Corporation (OAR). | * Copyright assigned to U.S. Government, 1994. | * | * The license and distribution terms for this file may be | * found in found in the file LICENSE in this distribution or at | * http://www.OARcorp.com/rtems/license.html. | ************************************************************************** | | $Id$ +--------------------------------------------------------------------------*/ #include #include #include /*-------------------------------------------------------------------------+ | Constants +--------------------------------------------------------------------------*/ #define AVG_OVERHEAD 0 /* 0.1 microseconds to start/stop timer. */ #define LEAST_VALID 1 /* Don't trust a value lower than this. */ /*-------------------------------------------------------------------------+ | Global Variables +--------------------------------------------------------------------------*/ volatile rtems_unsigned32 Ttimer_val; rtems_boolean Timer_driver_Find_average_overhead = TRUE; /*-------------------------------------------------------------------------+ | External Prototypes +--------------------------------------------------------------------------*/ extern void timerisr(); /* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */ extern int clockIsOn(const rtems_irq_connect_data*); /*-------------------------------------------------------------------------+ | Pentium optimized timer handling. +--------------------------------------------------------------------------*/ #if defined(pentium) /*-------------------------------------------------------------------------+ | Function: rdtsc | Description: Read the value of PENTIUM on-chip cycle counter. | Global Variables: None. | Arguments: None. | Returns: Value of PENTIUM on-chip cycle counter. +--------------------------------------------------------------------------*/ static inline unsigned long long rdtsc(void) { /* Return the value of the on-chip cycle counter. */ unsigned long long result; asm volatile(".byte 0x0F, 0x31" : "=A" (result)); return result; } /* rdtsc */ /*-------------------------------------------------------------------------+ | Function: Timer_exit | Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is | not really necessary, since there will be a reset at exit. | Global Variables: None. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ void Timer_exit(void) { } /* Timer_exit */ /*-------------------------------------------------------------------------+ | Function: Timer_initialize | Description: Timer initialization routine. | Global Variables: Ttimer_val. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ void Timer_initialize(void) { static rtems_boolean First = TRUE; if (First) { First = FALSE; atexit(Timer_exit); /* Try not to hose the system at exit. */ } Ttimer_val = rdtsc(); /* read starting time */ } /* Timer_initialize */ /*-------------------------------------------------------------------------+ | Function: Read_timer | Description: Read hardware timer value. | Global Variables: Ttimer_val, Timer_driver_Find_average_overhead. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ rtems_unsigned32 Read_timer(void) { register rtems_unsigned32 total; total = (rtems_unsigned32)(rdtsc() - Ttimer_val); if (Timer_driver_Find_average_overhead) return total; else if (total < LEAST_VALID) return 0; /* below timer resolution */ else return (total - AVG_OVERHEAD); } /* Read_timer */ #else /* pentium */ /*-------------------------------------------------------------------------+ | Non-Pentium timer handling. +--------------------------------------------------------------------------*/ #define US_PER_ISR 250 /* Number of micro-seconds per timer interruption */ /*-------------------------------------------------------------------------+ | Function: Timer_exit | Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is | not really necessary, since there will be a reset at exit. | Global Variables: None. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ void timerOff(const rtems_raw_irq_connect_data* used) { /* * disable interrrupt at i8259 level */ pc386_irq_disable_at_i8259s(used->idtIndex - PC386_IRQ_VECTOR_BASE); /* reset timer mode to standard (DOS) value */ outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); outport_byte(TIMER_CNTR0, 0); outport_byte(TIMER_CNTR0, 0); } /* Timer_exit */ void timerOn(const rtems_raw_irq_connect_data* used) { /* load timer for US_PER_ISR microsecond period */ outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 0 & 0xff); outport_byte(TIMER_CNTR0, US_TO_TICK(US_PER_ISR) >> 8 & 0xff); /* * disable interrrupt at i8259 level */ pc386_irq_enable_at_i8259s(used->idtIndex - PC386_IRQ_VECTOR_BASE); } static rtems_raw_irq_connect_data timer_raw_irq_data = { PC_386_PERIODIC_TIMER + PC386_IRQ_VECTOR_BASE, timerisr, timerOn, timerOff, clockIsOn }; /*-------------------------------------------------------------------------+ | Function: Timer_exit | Description: Timer cleanup routine at RTEMS exit. NOTE: This routine is | not really necessary, since there will be a reset at exit. | Global Variables: None. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ void Timer_exit(void) { i386_delete_idt_entry (&timer_raw_irq_data); } /* Timer_exit */ /*-------------------------------------------------------------------------+ | Function: Timer_initialize | Description: Timer initialization routine. | Global Variables: Ttimer_val. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ void Timer_initialize(void) { static rtems_boolean First = TRUE; if (First) { First = FALSE; atexit(Timer_exit); /* Try not to hose the system at exit. */ if (!i386_set_idt_entry (&timer_raw_irq_data)) { printk("raw handler connexion failed\n"); rtems_fatal_error_occurred(1); } } /* wait for ISR to be called at least once */ Ttimer_val = 0; while (Ttimer_val == 0) continue; Ttimer_val = 0; } /* Timer_initialize */ /*-------------------------------------------------------------------------+ | Function: Read_timer | Description: Read hardware timer value. | Global Variables: Ttimer_val, Timer_driver_Find_average_overhead. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ rtems_unsigned32 Read_timer(void) { register rtems_unsigned32 total, clicks; register rtems_unsigned8 lsb, msb; outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH); inport_byte(TIMER_CNTR0, lsb); inport_byte(TIMER_CNTR0, msb); clicks = (msb << 8) | lsb; total = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks)); if (Timer_driver_Find_average_overhead) return total; else if (total < LEAST_VALID) return 0; /* below timer resolution */ else return (total - AVG_OVERHEAD); } #endif /* pentium */ /*-------------------------------------------------------------------------+ | Function: Empty_function | Description: Empty function used in time tests. | Global Variables: None. | Arguments: None. | Returns: Nothing. +--------------------------------------------------------------------------*/ rtems_status_code Empty_function(void) { return RTEMS_SUCCESSFUL; } /* Empty function */ /*-------------------------------------------------------------------------+ | Function: Set_find_average_overhead | Description: Set internal Timer_driver_Find_average_overhead flag value. | Global Variables: Timer_driver_Find_average_overhead. | Arguments: find_flag - new value of the flag. | Returns: Nothing. +--------------------------------------------------------------------------*/ void Set_find_average_overhead(rtems_boolean find_flag) { Timer_driver_Find_average_overhead = find_flag; } /* Set_find_average_overhead */