summaryrefslogtreecommitdiffstats
path: root/bsps/i386
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-20 12:08:42 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2018-04-20 13:52:19 +0200
commite0dd8a5ad830798bc8082b03b8c42c32fb9660e0 (patch)
treed147bfc4d670fcdfbd2e2d2e75eb209f92e07df1 /bsps/i386
parentbsps: Move startup files to bsps (diff)
downloadrtems-e0dd8a5ad830798bc8082b03b8c42c32fb9660e0.tar.bz2
bsps: Move benchmark timer to bsps
This patch is a part of the BSP source reorganization. Update #3285.
Diffstat (limited to 'bsps/i386')
-rw-r--r--bsps/i386/pc386/btimer/btimer.c552
-rw-r--r--bsps/i386/pc386/btimer/timerisr.S56
2 files changed, 608 insertions, 0 deletions
diff --git a/bsps/i386/pc386/btimer/btimer.c b/bsps/i386/pc386/btimer/btimer.c
new file mode 100644
index 0000000000..16d3d66176
--- /dev/null
+++ b/bsps/i386/pc386/btimer/btimer.c
@@ -0,0 +1,552 @@
+/*
+ * This file contains the PC386 timer package.
+ *
+ * Rosimildo daSilva -ConnectTel, Inc - Fixed infinite loop in the Calibration
+ * routine. I've seen this problems with faster machines ( pentiums ). Sometimes
+ * RTEMS just hangs at startup.
+ *
+ * Joel 9 May 2010: This is now seen sometimes on qemu.
+ *
+ * Modifications by:
+ * (C) Copyright 1997 -
+ * NavIST Group - Real-Time Distributed Systems and Industrial Automation
+ * http://pandora.ist.utl.pt
+ * Instituto Superior Tecnico * Lisboa * PORTUGAL
+ *
+ * This file is provided "AS IS" without warranty of any kind, either
+ * expressed or implied.
+ *
+ * Based upon code by
+ * COPYRIGHT (c) 1989-1999.
+ * On-Line Applications Research Corporation (OAR).
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <stdlib.h>
+#include <bsp.h>
+#include <rtems/btimer.h>
+#include <bsp/irq-generic.h>
+#include <libcpu/cpuModel.h>
+
+/*
+ * Constants
+ */
+#define AVG_OVERHEAD 0 /* 0.1 microseconds to start/stop timer. */
+#define LEAST_VALID 1 /* Don't trust a value lower than this. */
+#define SLOW_DOWN_IO 0x80 /* io which does nothing */
+
+#define TWO_MS (uint32_t)(2000) /* TWO_MS = 2000us (sic!) */
+
+#define MSK_NULL_COUNT 0x40 /* bit counter available for reading */
+
+#define CMD_READ_BACK_STATUS 0xE2 /* command read back status */
+
+RTEMS_INTERRUPT_LOCK_DEFINE( /* visible global variable */ ,
+ rtems_i386_i8254_access_lock, "rtems_i386_i8254_access_lock" );
+
+/*
+ * Global Variables
+ */
+volatile uint32_t Ttimer_val;
+bool benchmark_timer_find_average_overhead = true;
+volatile unsigned int fastLoop1ms, slowLoop1ms;
+
+void (*benchmark_timer_initialize_function)(void) = 0;
+benchmark_timer_t (*benchmark_timer_read_function)(void) = 0;
+void (*Timer_exit_function)(void) = 0;
+
+/* timer (int 08h) Interrupt Service Routine (defined in 'timerisr.s') */
+extern void timerisr(void);
+
+void Timer_exit(void);
+
+/*
+ * Pentium optimized timer handling.
+ */
+
+/*
+ * Timer cleanup routine at RTEMS exit.
+ *
+ * NOTE: This routine is not really necessary, since there will be
+ * a reset at exit.
+ */
+static void tsc_timer_exit(void)
+{
+}
+
+static void tsc_timer_initialize(void)
+{
+ static bool First = true;
+
+ if (First) {
+ First = false;
+
+ atexit(Timer_exit); /* Try not to hose the system at exit. */
+ }
+ Ttimer_val = rdtsc(); /* read starting time */
+}
+
+/*
+ * Read TSC timer value.
+ */
+static uint32_t tsc_read_timer(void)
+{
+ register uint32_t total;
+
+ total = (uint32_t)(rdtsc() - Ttimer_val);
+
+ if (benchmark_timer_find_average_overhead)
+ return total;
+
+ if (total < LEAST_VALID)
+ return 0; /* below timer resolution */
+
+ return (total - AVG_OVERHEAD);
+}
+
+/*
+ * Non-Pentium timer handling.
+ */
+#define US_PER_ISR 250 /* Number of micro-seconds per timer interruption */
+
+/*
+ * Timer cleanup routine at RTEMS exit. NOTE: This routine is
+ * not really necessary, since there will be a reset at exit.
+ */
+static void timerOff(const rtems_raw_irq_connect_data* used)
+{
+ rtems_interrupt_lock_context lock_context;
+ /*
+ * disable interrrupt at i8259 level
+ */
+ bsp_interrupt_vector_disable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
+
+ rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
+
+ /* 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);
+
+ rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
+}
+
+static void timerOn(const rtems_raw_irq_connect_data* used)
+{
+ rtems_interrupt_lock_context lock_context;
+
+ rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
+
+ /* 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);
+
+ rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
+
+ /*
+ * enable interrrupt at i8259 level
+ */
+ bsp_interrupt_vector_enable(used->idtIndex - BSP_IRQ_VECTOR_BASE);
+}
+
+static rtems_raw_irq_connect_data timer_raw_irq_data = {
+ BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
+ timerisr,
+ timerOn,
+ timerOff,
+ NULL
+};
+
+/*
+ * Timer cleanup routine at RTEMS exit.
+ *
+ * NOTE: This routine is not really necessary, since there will be
+ * a reset at exit.
+ */
+static void i386_timer_exit(void)
+{
+ i386_delete_idt_entry (&timer_raw_irq_data);
+}
+
+extern void rtems_irq_prologue_0(void);
+static void i386_timer_initialize(void)
+{
+ static bool First = true;
+
+ if (First) {
+ rtems_raw_irq_connect_data raw_irq_data = {
+ BSP_PERIODIC_TIMER + BSP_IRQ_VECTOR_BASE,
+ rtems_irq_prologue_0,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ First = false;
+ i386_delete_idt_entry (&raw_irq_data);
+
+ atexit(Timer_exit); /* Try not to hose the system at exit. */
+ if (!i386_set_idt_entry (&timer_raw_irq_data)) {
+ printk("raw handler connection 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;
+}
+
+/*
+ * Read hardware timer value.
+ */
+static uint32_t i386_read_timer(void)
+{
+ register uint32_t total, clicks;
+ register uint8_t lsb, msb;
+ rtems_interrupt_lock_context lock_context;
+
+ rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
+ outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);
+ inport_byte(TIMER_CNTR0, lsb);
+ inport_byte(TIMER_CNTR0, msb);
+ rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
+
+ clicks = (msb << 8) | lsb;
+ total = (Ttimer_val * US_PER_ISR) + (US_PER_ISR - TICK_TO_US(clicks));
+
+ if (benchmark_timer_find_average_overhead)
+ return total;
+
+ if (total < LEAST_VALID)
+ return 0; /* below timer resolution */
+
+ return (total - AVG_OVERHEAD);
+}
+
+/*
+ * General timer functions using either TSC-based implementation
+ * or interrupt-based implementation
+ */
+
+void benchmark_timer_initialize(void)
+{
+ static bool First = true;
+
+ if (First) {
+ if (x86_has_tsc()) {
+#if defined(DEBUG)
+ printk("TSC: timer initialization\n");
+#endif /* DEBUG */
+ benchmark_timer_initialize_function = &tsc_timer_initialize;
+ benchmark_timer_read_function = &tsc_read_timer;
+ Timer_exit_function = &tsc_timer_exit;
+ } else {
+#if defined(DEBUG)
+ printk("ISR: timer initialization\n");
+#endif /* DEBUG */
+ benchmark_timer_initialize_function = &i386_timer_initialize;
+ benchmark_timer_read_function = &i386_read_timer;
+ Timer_exit_function = &i386_timer_exit;
+ }
+ First = false;
+ }
+ (*benchmark_timer_initialize_function)();
+}
+
+uint32_t benchmark_timer_read(void)
+{
+ return (*benchmark_timer_read_function)();
+}
+
+void Timer_exit(void)
+{
+ if ( Timer_exit_function )
+ return (*Timer_exit_function)();
+}
+
+/*
+ * Set internal benchmark_timer_find_average_overhead flag value.
+ */
+void benchmark_timer_disable_subtracting_average_overhead(bool find_flag)
+{
+ benchmark_timer_find_average_overhead = find_flag;
+}
+
+static unsigned short lastLoadedValue;
+
+/*
+ * Loads timer 0 with value passed as arguemnt.
+ *
+ * Returns: Nothing. Loaded value must be a number of clock bits...
+ */
+static void loadTimerValue( unsigned short loadedValue )
+{
+ rtems_interrupt_lock_context lock_context;
+ rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
+ lastLoadedValue = loadedValue;
+ outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_SQWAVE);
+ outport_byte(TIMER_CNTR0, loadedValue & 0xff);
+ outport_byte(TIMER_CNTR0, (loadedValue >> 8) & 0xff);
+ rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
+}
+
+/*
+ * Reads the current value of the timer, and converts the
+ * number of ticks to micro-seconds.
+ *
+ * Returns: number of clock bits elapsed since last load.
+ */
+static unsigned int readTimer0(void)
+{
+ unsigned short lsb, msb;
+ unsigned char status;
+ unsigned int count;
+ rtems_interrupt_lock_context lock_context;
+ rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
+
+ outport_byte(
+ TIMER_MODE,
+ (TIMER_RD_BACK | (RB_COUNT_0 & ~(RB_NOT_STATUS | RB_NOT_COUNT)))
+ );
+ inport_byte(TIMER_CNTR0, status);
+ inport_byte(TIMER_CNTR0, lsb);
+ inport_byte(TIMER_CNTR0, msb);
+
+ rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
+
+ count = ( msb << 8 ) | lsb ;
+ if (status & RB_OUTPUT )
+ count += lastLoadedValue;
+
+ return (2*lastLoadedValue - count);
+}
+
+static void Timer0Reset(void)
+{
+ loadTimerValue(0xffff);
+ readTimer0();
+}
+
+static void fastLoop (unsigned int loopCount)
+{
+ unsigned int i;
+ for( i=0; i < loopCount; i++ )outport_byte( SLOW_DOWN_IO, 0 );
+}
+
+static void slowLoop (unsigned int loopCount)
+{
+ unsigned int j;
+ for (j=0; j <100 ; j++) {
+ fastLoop (loopCount);
+ }
+}
+
+/*
+ * #define DEBUG_CALIBRATE
+ */
+void
+Calibrate_loop_1ms(void)
+{
+ unsigned int offset, offsetTmp, emptyCall, emptyCallTmp, res, i, j;
+ unsigned int targetClockBits, currentClockBits;
+ unsigned int slowLoopGranularity, fastLoopGranularity;
+ rtems_interrupt_level level;
+ int retries = 0;
+
+ /*
+ * This code is designed to run before interrupt management
+ * is enabled and running it on multiple CPUs and or after
+ * secondary CPUs are bring up seems really broken.
+ * Disabling of local interrupts is enough.
+ */
+ rtems_interrupt_local_disable(level);
+
+retry:
+ if ( ++retries >= 5 ) {
+ printk( "Calibrate_loop_1ms: too many attempts. giving up!!\n" );
+ while (1);
+ }
+#ifdef DEBUG_CALIBRATE
+ printk("Calibrate_loop_1ms is starting, please wait (but not too long.)\n");
+#endif
+ targetClockBits = US_TO_TICK(1000);
+ /*
+ * Fill up the cache to get a correct offset
+ */
+ Timer0Reset();
+ readTimer0();
+ /*
+ * Compute the minimal offset to apply due to read counter register.
+ */
+ offset = 0xffffffff;
+ for (i=0; i <1000; i++) {
+ Timer0Reset();
+ offsetTmp = readTimer0();
+ offset += offsetTmp;
+ }
+ offset = offset / 1000; /* compute average */
+ /*
+ * calibrate empty call
+ */
+ fastLoop (0);
+ emptyCall = 0;
+ j = 0;
+ for (i=0; i <10; i++) {
+ Timer0Reset();
+ fastLoop (0);
+ res = readTimer0();
+ /* res may be inferior to offset on fast
+ * machine because we took an average for offset
+ */
+ if (res > offset) {
+ ++j;
+ emptyCallTmp = res - offset;
+ emptyCall += emptyCallTmp;
+ }
+ }
+ if (j == 0) emptyCall = 0;
+ else emptyCall = emptyCall / j; /* compute average */
+ /*
+ * calibrate fast loop
+ */
+ Timer0Reset();
+ fastLoop (10000);
+ res = readTimer0() - offset;
+ if (res < emptyCall) {
+ printk(
+ "Problem #1 in offset computation in Calibrate_loop_1ms "
+ " in file libbsp/i386/pc386/timer/timer.c\n"
+ );
+ goto retry;
+ }
+ fastLoopGranularity = (res - emptyCall) / 10000;
+ /*
+ * calibrate slow loop
+ */
+ Timer0Reset();
+ slowLoop(10);
+ res = readTimer0();
+ if (res < offset + emptyCall) {
+ printk(
+ "Problem #2 in offset computation in Calibrate_loop_1ms "
+ " in file libbsp/i386/pc386/timer/timer.c\n"
+ );
+ goto retry;
+ }
+ slowLoopGranularity = (res - offset - emptyCall)/ 10;
+
+ if (slowLoopGranularity == 0) {
+ printk(
+ "Problem #3 in offset computation in Calibrate_loop_1ms "
+ " in file libbsp/i386/pc386/timer/timer.c\n"
+ );
+ goto retry;
+ }
+
+ targetClockBits += offset;
+#ifdef DEBUG_CALIBRATE
+ printk("offset = %u, emptyCall = %u, targetClockBits = %u\n",
+ offset, emptyCall, targetClockBits);
+ printk("slowLoopGranularity = %u fastLoopGranularity = %u\n",
+ slowLoopGranularity, fastLoopGranularity);
+#endif
+ slowLoop1ms = (targetClockBits - emptyCall) / slowLoopGranularity;
+ if (slowLoop1ms != 0) {
+ fastLoop1ms = targetClockBits % slowLoopGranularity;
+ if (fastLoop1ms > emptyCall) fastLoop1ms -= emptyCall;
+ }
+ else
+ fastLoop1ms = targetClockBits - emptyCall / fastLoopGranularity;
+
+ if (slowLoop1ms != 0) {
+ /*
+ * calibrate slow loop
+ */
+
+ while(1)
+ {
+ int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */
+ Timer0Reset();
+ slowLoop(slowLoop1ms);
+ currentClockBits = readTimer0();
+ if (currentClockBits > targetClockBits) {
+ if ((currentClockBits - targetClockBits) < slowLoopGranularity) {
+ /* decrement loop counter anyway to be sure slowLoop(slowLoop1ms) < targetClockBits */
+ --slowLoop1ms;
+ break;
+ }
+ else {
+ --slowLoop1ms;
+ if (slowLoop1ms == 0) break;
+ if (previousSign == 0) previousSign = 2;
+ if (previousSign == 1) break;
+ }
+ }
+ else {
+ if ((targetClockBits - currentClockBits) < slowLoopGranularity) {
+ break;
+ }
+ else {
+ ++slowLoop1ms;
+ if (previousSign == 0) previousSign = 1;
+ if (previousSign == 2) break;
+ }
+ }
+ }
+ }
+ /*
+ * calibrate fast loop
+ */
+
+ if (fastLoopGranularity != 0 ) {
+ while(1) {
+ int previousSign = 0; /* 0 = unset, 1 = incrementing, 2 = decrementing */
+ Timer0Reset();
+ if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
+ fastLoop(fastLoop1ms);
+ currentClockBits = readTimer0();
+ if (currentClockBits > targetClockBits) {
+ if ((currentClockBits - targetClockBits) < fastLoopGranularity)
+ break;
+ else {
+ --fastLoop1ms;
+ if (previousSign == 0) previousSign = 2;
+ if (previousSign == 1) break;
+ }
+ }
+ else {
+ if ((targetClockBits - currentClockBits) < fastLoopGranularity)
+ break;
+ else {
+ ++fastLoop1ms;
+ if (previousSign == 0) previousSign = 1;
+ if (previousSign == 2) break;
+ }
+ }
+ }
+ }
+#ifdef DEBUG_CALIBRATE
+ printk("slowLoop1ms = %u, fastLoop1ms = %u\n", slowLoop1ms, fastLoop1ms);
+#endif
+ rtems_interrupt_local_enable(level);
+
+}
+
+/*
+ * loop which waits at least timeToWait ms
+ */
+void Wait_X_ms( unsigned int timeToWait)
+{
+ unsigned int j;
+
+ for (j=0; j<timeToWait ; j++) {
+ if (slowLoop1ms != 0) slowLoop(slowLoop1ms);
+ fastLoop(fastLoop1ms);
+ }
+}
diff --git a/bsps/i386/pc386/btimer/timerisr.S b/bsps/i386/pc386/btimer/timerisr.S
new file mode 100644
index 0000000000..da2a07845a
--- /dev/null
+++ b/bsps/i386/pc386/btimer/timerisr.S
@@ -0,0 +1,56 @@
+/*-------------------------------------------------------------------------+
+| timerisr.s v1.1 - PC386 BSP - 1997/08/07
++--------------------------------------------------------------------------+
+| This file contains the PC386 timer interrupt handler.
++--------------------------------------------------------------------------+
+| (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:
+| timerisr.s,v 1.5 1995/12/19 20:07:45 joel Exp - go32 BSP
+| With the following copyright notice:
+| **************************************************************************
+| * COPYRIGHT (c) 1989-1999.
+| * On-Line Applications Research Corporation (OAR).
+| *
+| * The license and distribution terms for this file may be
+| * found in the file LICENSE in this distribution or at
+| * http://www.rtems.org/license/LICENSE.
+| **************************************************************************
++--------------------------------------------------------------------------*/
+
+#include <rtems/asm.h>
+
+BEGIN_CODE
+
+ EXTERN(Ttimer_val)
+
+/*-------------------------------------------------------------------------+
+| Function: rtems_isr timerisr(rtems_vector_number);
+| Description: ISR for the timer. The timer is set up to generate an
+| interrupt at maximum intervals.
+| Global Variables: None.
+| Arguments: standard - see RTEMS documentation.
+| Returns: standard return value - see RTEMS documentation.
++--------------------------------------------------------------------------*/
+ PUBLIC(timerisr)
+SYM (timerisr):
+ incl Ttimer_val # another tick
+ pushl eax
+ movb $0x20, al
+ outb al, $0x20 # signal generic End Of Interrupt (EOI) to PIC
+ popl eax
+ iret
+
+END_CODE
+
+END