Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:40

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  *  @file
0005  *
0006  *  Clock Tick Device Driver
0007  *
0008  *  History:
0009  *    + Original driver was go32 clock by Joel Sherrill
0010  *    + go32 clock driver hardware code was inserted into new
0011  *      boilerplate when the pc386 BSP by:
0012  *        Pedro Miguel Da Cruz Neto Romano <pmcnr@camoes.rnl.ist.utl.pt>
0013  *        Jose Rufino <ruf@asterix.ist.utl.pt>
0014  *    + Reworked by Joel Sherrill to use clock driver template.
0015  *      This removes all boilerplate and leave original hardware
0016  *      code I developed for the go32 BSP.
0017  */
0018 
0019 /*
0020  *  COPYRIGHT (c) 1989-2012.
0021  *  On-Line Applications Research Corporation (OAR).
0022  *
0023  * Redistribution and use in source and binary forms, with or without
0024  * modification, are permitted provided that the following conditions
0025  * are met:
0026  * 1. Redistributions of source code must retain the above copyright
0027  *    notice, this list of conditions and the following disclaimer.
0028  * 2. Redistributions in binary form must reproduce the above copyright
0029  *    notice, this list of conditions and the following disclaimer in the
0030  *    documentation and/or other materials provided with the distribution.
0031  *
0032  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0033  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0034  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0035  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0036  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0037  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0038  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0039  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0040  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0041  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0042  * POSSIBILITY OF SUCH DAMAGE.
0043  */
0044 
0045 #include <bsp.h>
0046 #include <bsp/irq-generic.h>
0047 #include <bspopts.h>
0048 #include <libcpu/cpuModel.h>
0049 #include <assert.h>
0050 #include <rtems/timecounter.h>
0051 #ifdef RTEMS_SMP
0052 #include <rtems/score/smpimpl.h>
0053 #endif
0054 
0055 #define CLOCK_VECTOR 0
0056 
0057 volatile uint32_t pc386_microseconds_per_isr;
0058 volatile uint32_t pc386_isrs_per_tick;
0059 uint32_t pc386_clock_click_count;
0060 
0061 /* forward declaration */
0062 void Clock_isr(void *param);
0063 static void Clock_isr_handler(void *param);
0064 
0065 /*
0066  * Roughly the number of cycles per second. Note that these
0067  * will be wildly inaccurate if the chip speed changes due to power saving
0068  * or thermal modes.
0069  *
0070  * NOTE: These are only used when the TSC method is used.
0071  */
0072 static uint64_t pc586_tsc_frequency;
0073 
0074 static struct timecounter pc386_tc;
0075 
0076 /* this driver may need to count ISRs per tick */
0077 #define CLOCK_DRIVER_ISRS_PER_TICK       1
0078 #define CLOCK_DRIVER_ISRS_PER_TICK_VALUE pc386_isrs_per_tick
0079 
0080 extern volatile uint32_t Clock_driver_ticks;
0081 
0082 #define READ_8254( _lsb, _msb )                               \
0083   do { outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_LATCH);      \
0084      inport_byte(TIMER_CNTR0, _lsb);                          \
0085      inport_byte(TIMER_CNTR0, _msb);                          \
0086   } while (0)
0087 
0088 
0089 #ifdef RTEMS_SMP
0090 #define Clock_driver_support_at_tick(arg) \
0091   do {                                                              \
0092     Processor_mask targets;                                         \
0093     _Processor_mask_Assign(&targets, _SMP_Get_online_processors()); \
0094     _Processor_mask_Clear(&targets, _SMP_Get_current_processor());  \
0095     _SMP_Multicast_action(&targets, rtems_timecounter_tick, NULL);               \
0096   } while (0)
0097 #endif
0098 
0099 static uint32_t pc386_get_timecount_tsc(struct timecounter *tc)
0100 {
0101   return (uint32_t)rdtsc();
0102 }
0103 
0104 static uint32_t pc386_get_timecount_i8254(struct timecounter *tc)
0105 {
0106   uint32_t                 irqs;
0107   uint8_t                  lsb, msb;
0108   rtems_interrupt_lock_context lock_context;
0109 
0110   /*
0111    * Fetch all the data in an interrupt critical section.
0112    */
0113 
0114   rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
0115 
0116     READ_8254(lsb, msb);
0117     irqs = Clock_driver_ticks;
0118 
0119   rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
0120 
0121   return (irqs + 1) * pc386_microseconds_per_isr - ((msb << 8) | lsb);
0122 }
0123 
0124 /*
0125  * Calibrate CPU cycles per tick. Interrupts should be disabled.
0126  * Will also set the PIT, so call this before registering the 
0127  * periodic timer for rtems tick generation
0128  */
0129 static void calibrate_tsc(void)
0130 {
0131   uint64_t              begin_time;
0132   uint8_t               lsb, msb;
0133   uint32_t              max_timer_value;
0134   uint32_t              last_tick, cur_tick;
0135   int32_t               diff, remaining;
0136 
0137   /* Set the timer to free running mode */
0138   outport_byte(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_INTTC);
0139   /* Reset the 16 timer reload value, first LSB, then MSB */
0140   outport_byte(TIMER_CNTR0, 0);
0141   outport_byte(TIMER_CNTR0, 0);
0142   /* We use the full 16 bit */
0143   max_timer_value = 0xffff;
0144   /* Calibrate for 1s, i.e. TIMER_TICK PIT ticks */
0145   remaining = TIMER_TICK;
0146 
0147   begin_time = rdtsc();
0148   READ_8254(lsb, msb);
0149   last_tick = (msb << 8) | lsb;
0150   while(remaining > 0) {
0151     READ_8254(lsb, msb);
0152     cur_tick = (msb << 8) | lsb;
0153     /* PIT counts down, so subtract cur from last */
0154     diff = last_tick - cur_tick;
0155     last_tick = cur_tick;
0156     if (diff < 0) {
0157         diff += max_timer_value;
0158     }
0159     remaining -= diff;
0160   }
0161 
0162   pc586_tsc_frequency = rdtsc() - begin_time;
0163 
0164 #if 0
0165   printk( "CPU clock at %u Hz\n", (uint32_t)(pc586_tsc_frequency ));
0166 #endif
0167 }
0168 
0169 static void clockOn(void)
0170 {
0171 
0172   /*
0173    * First calibrate the TSC. Do this every time we
0174    * turn the clock on in case the CPU clock speed has changed.
0175    */
0176   if ( x86_has_tsc() ) {
0177     calibrate_tsc();
0178   }
0179 
0180   rtems_interrupt_lock_context lock_context;
0181   pc386_isrs_per_tick        = 1;
0182   pc386_microseconds_per_isr = rtems_configuration_get_microseconds_per_tick();
0183 
0184   while (US_TO_TICK(pc386_microseconds_per_isr) > 65535) {
0185     pc386_isrs_per_tick  *= 10;
0186     pc386_microseconds_per_isr /= 10;
0187   }
0188   pc386_clock_click_count = US_TO_TICK(pc386_microseconds_per_isr);
0189 
0190   #if 0
0191     printk( "configured usecs per tick=%d \n",
0192       rtems_configuration_get_microseconds_per_tick() );
0193     printk( "Microseconds per ISR =%d\n", pc386_microseconds_per_isr );
0194     printk( "final ISRs per=%d\n", pc386_isrs_per_tick );
0195     printk( "final timer counts=%d\n", pc386_clock_click_count );
0196   #endif
0197 
0198   rtems_interrupt_lock_acquire(&rtems_i386_i8254_access_lock, &lock_context);
0199   outport_byte(TIMER_MODE, TIMER_SEL0|TIMER_16BIT|TIMER_RATEGEN);
0200   outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 0 & 0xff);
0201   outport_byte(TIMER_CNTR0, pc386_clock_click_count >> 8 & 0xff);
0202   rtems_interrupt_lock_release(&rtems_i386_i8254_access_lock, &lock_context);
0203 
0204   bsp_interrupt_vector_enable( BSP_PERIODIC_TIMER );
0205 }
0206 
0207 bool Clock_isr_enabled = false;
0208 static void Clock_isr_handler(void *param)
0209 {
0210   if ( Clock_isr_enabled )
0211     Clock_isr( param );
0212 }
0213 
0214 void Clock_driver_install_handler(void)
0215 {
0216   rtems_status_code status;
0217 
0218   status = rtems_interrupt_handler_install(
0219     BSP_PERIODIC_TIMER,
0220     "ckinit",
0221     RTEMS_INTERRUPT_UNIQUE,
0222     Clock_isr_handler,
0223     NULL
0224   );
0225   assert(status == RTEMS_SUCCESSFUL);
0226   clockOn();
0227 }
0228 
0229 #define Clock_driver_support_set_interrupt_affinity(online_processors) \
0230   do { \
0231     /* FIXME: Is there a way to do this on x86? */ \
0232     (void) online_processors; \
0233   } while (0)
0234 
0235 void Clock_driver_support_initialize_hardware(void)
0236 {
0237   bool use_tsc = false;
0238   bool use_8254 = false;
0239 
0240   #if (CLOCK_DRIVER_USE_TSC == 1)
0241     use_tsc = true;
0242   #endif
0243 
0244   #if (CLOCK_DRIVER_USE_8254 == 1)
0245     use_8254 = true;
0246   #endif
0247 
0248   if ( !use_tsc && !use_8254 ) {
0249     if ( x86_has_tsc() ) use_tsc  = true;
0250     else                 use_8254 = true;
0251   }
0252 
0253   if ( use_8254 ) {
0254     /* printk( "Use 8254\n" ); */
0255     pc386_tc.tc_get_timecount = pc386_get_timecount_i8254;
0256     pc386_tc.tc_counter_mask = 0xffffffff;
0257     pc386_tc.tc_frequency = TIMER_TICK;
0258   } else {
0259     /* printk( "Use TSC\n" ); */
0260     pc386_tc.tc_get_timecount = pc386_get_timecount_tsc;
0261     pc386_tc.tc_counter_mask = 0xffffffff;
0262     pc386_tc.tc_frequency = pc586_tsc_frequency;
0263   }
0264 
0265   pc386_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
0266   rtems_timecounter_install(&pc386_tc);
0267   Clock_isr_enabled = true;
0268 }
0269 
0270 #include "../../../shared/dev/clock/clockimpl.h"