From 7632906fc290b652416ab59eb5fb49356c064ed6 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 19 Apr 2018 06:35:52 +0200 Subject: bsps: Move clock drivers to bsps This patch is a part of the BSP source reorganization. Update #3285. --- bsps/i386/pc386/clock/ckinit.c | 262 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 bsps/i386/pc386/clock/ckinit.c (limited to 'bsps/i386/pc386/clock/ckinit.c') diff --git a/bsps/i386/pc386/clock/ckinit.c b/bsps/i386/pc386/clock/ckinit.c new file mode 100644 index 0000000000..fce267bda7 --- /dev/null +++ b/bsps/i386/pc386/clock/ckinit.c @@ -0,0 +1,262 @@ +/** + * @file + * + * Clock Tick Device Driver + * + * History: + * + Original driver was go32 clock by Joel Sherrill + * + go32 clock driver hardware code was inserted into new + * boilerplate when the pc386 BSP by: + * Pedro Miguel Da Cruz Neto Romano + * Jose Rufino + * + Reworked by Joel Sherrill to use clock driver template. + * This removes all boilerplate and leave original hardware + * code I developed for the go32 BSP. + */ + +/* + * COPYRIGHT (c) 1989-2012. + * 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 +#include +#include +#include +#include +#include + +#define CLOCK_VECTOR 0 + +volatile uint32_t pc386_microseconds_per_isr; +volatile uint32_t pc386_isrs_per_tick; +uint32_t pc386_clock_click_count; + +/* forward declaration */ +void Clock_isr(void *param); +static void clockOff(void); +static void Clock_isr_handler(void *param); + +/* + * Roughly the number of cycles per second. Note that these + * will be wildly inaccurate if the chip speed changes due to power saving + * or thermal modes. + * + * NOTE: These are only used when the TSC method is used. + */ +static uint64_t pc586_tsc_frequency; + +static struct timecounter pc386_tc; + +/* this driver may need to count ISRs per tick */ +#define CLOCK_DRIVER_ISRS_PER_TICK 1 +#define CLOCK_DRIVER_ISRS_PER_TICK_VALUE pc386_isrs_per_tick + +extern volatile uint32_t Clock_driver_ticks; + +#define READ_8254( _lsb, _msb ) \ + do { outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH); \ + inport_byte(TIMER_CNTR0, _lsb); \ + inport_byte(TIMER_CNTR0, _msb); \ + } while (0) + + +#ifdef RTEMS_SMP +#define Clock_driver_support_at_tick() \ + _SMP_Send_message_broadcast(SMP_MESSAGE_CLOCK_TICK) +#endif + +static uint32_t pc386_get_timecount_tsc(struct timecounter *tc) +{ + return (uint32_t)rdtsc(); +} + +static uint32_t pc386_get_timecount_i8254(struct timecounter *tc) +{ + uint32_t irqs; + uint8_t lsb, msb; + rtems_interrupt_lock_context lock_context; + + /* + * Fetch all the data in an interrupt critical section. + */ + + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + + READ_8254(lsb, msb); + irqs = Clock_driver_ticks; + + rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); + + return (irqs + 1) * pc386_microseconds_per_isr - ((msb << 8) | lsb); +} + +/* + * Calibrate CPU cycles per tick. Interrupts should be disabled. + */ +static void calibrate_tsc(void) +{ + uint64_t begin_time; + uint8_t then_lsb, then_msb, now_lsb, now_msb; + uint32_t i; + + /* + * We just reset the timer, so we know we're at the beginning of a tick. + */ + + /* + * Count cycles. Watching the timer introduces a several microsecond + * uncertaintity, so let it cook for a while and divide by the number of + * ticks actually executed. + */ + + begin_time = rdtsc(); + + for (i = rtems_clock_get_ticks_per_second() * pc386_isrs_per_tick; + i != 0; --i ) { + /* We know we've just completed a tick when timer goes from low to high */ + then_lsb = then_msb = 0xff; + do { + READ_8254(now_lsb, now_msb); + if ((then_msb < now_msb) || + ((then_msb == now_msb) && (then_lsb < now_lsb))) + break; + then_lsb = now_lsb; + then_msb = now_msb; + } while (1); + } + + pc586_tsc_frequency = rdtsc() - begin_time; + +#if 0 + printk( "CPU clock at %u MHz\n", (uint32_t)(pc586_tsc_frequency / 1000000)); +#endif +} + +static void clockOn(void) +{ + rtems_interrupt_lock_context lock_context; + pc386_isrs_per_tick = 1; + pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick(); + + while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) { + pc386_isrs_per_tick *= 10; + pc386_microseconds_per_isr /= 10; + } + pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr); + + #if 0 + printk( "configured usecs per tick=%d \n", + rtems_configuration_get_microseconds_per_tick() ); + printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr ); + printk( "final ISRs per=%d\n", pc386_isrs_per_tick ); + printk( "final timer counts=%d\n", pc386_clock_click_count ); + #endif + + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN); + outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff); + outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff); + rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context); + + bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER - BSP_IRQ_VECTOR_BASE ); + + /* + * Now calibrate cycles per tick. Do this every time we + * turn the clock on in case the CPU clock speed has changed. + */ + if ( x86_has_tsc() ) + calibrate_tsc(); +} + +static void clockOff(void) +{ + rtems_interrupt_lock_context lock_context; + rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context); + /* reset timer mode to standard (BIOS) 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); +} /* Clock_exit */ + +bool Clock_isr_enabled = false; +static void Clock_isr_handler(void *param) +{ + if ( Clock_isr_enabled ) + Clock_isr( param ); +} + +void Clock_driver_install_handler(void) +{ + rtems_status_code status; + + status = rtems_interrupt_handler_install( + BSP_PERIODIC_TIMER, + "ckinit", + RTEMS_INTERRUPT_UNIQUE, + Clock_isr_handler, + NULL + ); + assert(status == RTEMS_SUCCESSFUL); + clockOn(); +} + +#define Clock_driver_support_set_interrupt_affinity(online_processors) \ + do { \ + /* FIXME: Is there a way to do this on x86? */ \ + (void) online_processors; \ + } while (0) + +void Clock_driver_support_initialize_hardware(void) +{ + bool use_tsc = false; + bool use_8254 = false; + + #if (CLOCK_DRIVER_USE_TSC == 1) + use_tsc = true; + #endif + + #if (CLOCK_DRIVER_USE_8254 == 1) + use_8254 = true; + #endif + + if ( !use_tsc && !use_8254 ) { + if ( x86_has_tsc() ) use_tsc = true; + else use_8254 = true; + } + + if ( use_8254 ) { + /* printk( "Use 8254\n" ); */ + pc386_tc.tc_get_timecount = pc386_get_timecount_i8254; + pc386_tc.tc_counter_mask = 0xffffffff; + pc386_tc.tc_frequency = TIMER_TICK; + } else { + /* printk( "Use TSC\n" ); */ + pc386_tc.tc_get_timecount = pc386_get_timecount_tsc; + pc386_tc.tc_counter_mask = 0xffffffff; + pc386_tc.tc_frequency = pc586_tsc_frequency; + } + + pc386_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; + rtems_timecounter_install(&pc386_tc); + Clock_isr_enabled = true; +} + +#define Clock_driver_support_shutdown_hardware() \ + do { \ + rtems_status_code status; \ + clockOff(); \ + status = rtems_interrupt_handler_remove( \ + BSP_PERIODIC_TIMER, \ + Clock_isr_handler, \ + NULL \ + ); \ + assert(status == RTEMS_SUCCESSFUL); \ + } while (0) + +#include "../../../shared/dev/clock/clockimpl.h" -- cgit v1.2.3