Back to home page

LXR

 
 

    


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

0001 /**
0002  *  @brief Clock Tick Device Driver
0003  *
0004  *  This routine utilizes the Decrementer Register common to the PPC family.
0005  *
0006  *  The tick frequency is directly programmed to the configured number of
0007  *  microseconds per tick.
0008  */
0009 
0010 /*
0011  *  COPYRIGHT (c) 1989-2014.
0012  *  On-Line Applications Research Corporation (OAR).
0013  *
0014  *  The license and distribution terms for this file may in
0015  *  the file LICENSE in this distribution or at
0016  *  http://www.rtems.org/license/LICENSE.
0017  *
0018  *  Modified to support the MPC750.
0019  *  Modifications Copyright (c) 1999 Eric Valette valette@crf.canon.fr
0020  */
0021 
0022 #include <rtems.h>
0023 #include <rtems/libio.h>
0024 #include <rtems/clockdrv.h>
0025 #include <stdlib.h>                     /* for atexit() */
0026 #include <assert.h>
0027 #include <libcpu/c_clock.h>
0028 #include <libcpu/cpuIdent.h>
0029 #include <libcpu/spr.h>
0030 #include <rtems/bspIo.h>                /* for printk() */
0031 #include <libcpu/powerpc-utility.h>
0032 #include <rtems/timecounter.h>
0033 #include <rtems/score/percpu.h>
0034 #include <rtems/score/thread.h>
0035 
0036 #include <bspopts.h>   /* for CLOCK_DRIVER_USE_FAST_IDLE */
0037 
0038 SPR_RW(BOOKE_TCR)
0039 SPR_RW(BOOKE_TSR)
0040 SPR_RW(BOOKE_DECAR)
0041 
0042 extern int BSP_connect_clock_handler (void);
0043 
0044 /*
0045  *  Clock ticks since initialization
0046  */
0047 volatile uint32_t   Clock_driver_ticks;
0048 
0049 /*
0050  *  This is the value programmed into the count down timer.
0051  */
0052 static uint32_t   Clock_Decrementer_value;
0053 
0054 static struct timecounter Clock_TC;
0055 
0056 static uint32_t Clock_Get_timecount(struct timecounter *tc)
0057 {
0058   return ppc_time_base();
0059 }
0060 
0061 void clockOff(void* unused)
0062 {
0063   rtems_interrupt_level l;
0064 
0065   if ( ppc_cpu_is_bookE() ) {
0066     rtems_interrupt_disable(l);
0067     _write_BOOKE_TCR(_read_BOOKE_TCR() & ~BOOKE_TCR_DIE);
0068     rtems_interrupt_enable(l);
0069   } else {
0070     /*
0071      * Nothing to do as we cannot disable all interrupts and
0072      * the decrementer interrupt enable is MSR_EE
0073      */
0074   }
0075 }
0076 
0077 void clockOn(void* unused)
0078 {
0079   rtems_interrupt_level l;
0080 
0081   PPC_Set_decrementer( Clock_Decrementer_value );
0082 
0083   if ( ppc_cpu_is_bookE() ) {
0084     _write_BOOKE_DECAR( Clock_Decrementer_value );
0085 
0086     rtems_interrupt_disable(l);
0087 
0088     /* clear pending/stale irq */
0089     _write_BOOKE_TSR( BOOKE_TSR_DIS );
0090     /* enable */
0091     _write_BOOKE_TCR( _read_BOOKE_TCR() | BOOKE_TCR_DIE );
0092 
0093     rtems_interrupt_enable(l);
0094 
0095   }
0096 }
0097 
0098 static void clockHandler(void)
0099 {
0100   #if (CLOCK_DRIVER_USE_FAST_IDLE == 1)
0101     rtems_interrupt_level  level;
0102     uint32_t               tb;
0103     Per_CPU_Control       *cpu_self;
0104 
0105     rtems_interrupt_disable(level);
0106 
0107     tb = ppc_time_base();
0108     rtems_timecounter_tick();
0109     cpu_self = _Per_CPU_Get();
0110 
0111     while (
0112       cpu_self->thread_dispatch_disable_level == cpu_self->isr_nest_level
0113         && cpu_self->heir == cpu_self->executing
0114         && cpu_self->executing->is_idle
0115     ) {
0116       tb += Clock_Decrementer_value;
0117       ppc_set_time_base( tb );
0118       rtems_timecounter_tick();
0119     }
0120 
0121     rtems_interrupt_enable(level);
0122   #else
0123     rtems_timecounter_tick();
0124   #endif
0125 }
0126 
0127 static void (*clock_handler)(void);
0128 
0129 /*
0130  *  Clock_isr
0131  *
0132  *  This is the clock tick interrupt handler.
0133  *
0134  *  Input parameters:
0135  *    vector - vector number
0136  *
0137  *  Output parameters:  NONE
0138  *
0139  *  Return values:      NONE
0140  *
0141  */
0142 void clockIsr(void *unused)
0143 {
0144   int decr;
0145 
0146   /*
0147    *  The driver has seen another tick.
0148    */
0149   do {
0150     register uint32_t flags;
0151 
0152     rtems_interrupt_disable(flags);
0153       __asm__ volatile (
0154     "mfdec %0; add %0, %0, %1; mtdec %0"
0155     : "=&r"(decr)
0156     : "r"(Clock_Decrementer_value)
0157       );
0158     rtems_interrupt_enable(flags);
0159 
0160     Clock_driver_ticks += 1;
0161     /*
0162      *  Real Time Clock counter/timer is set to automatically reload.
0163      */
0164     clock_handler();
0165   } while ( decr < 0 );
0166 }
0167 
0168 /*
0169  *  Clock_isr_bookE
0170  *
0171  *  This is the clock tick interrupt handler
0172  *  for bookE CPUs. For efficiency reasons we
0173  *  provide a separate handler rather than
0174  *  checking the CPU type each time.
0175  */
0176 void clockIsrBookE(void *unused)
0177 {
0178   /* Note: TSR bit has already been cleared in the exception handler */
0179 
0180   /*
0181    *  The driver has seen another tick.
0182    */
0183 
0184   Clock_driver_ticks += 1;
0185 
0186   /*
0187    *  Real Time Clock counter/timer is set to automatically reload.
0188    */
0189   clock_handler();
0190 }
0191 
0192 int clockIsOn(void* unused)
0193 {
0194   uint32_t   msr_value;
0195 
0196   _CPU_MSR_GET( msr_value );
0197 
0198   if ( ppc_cpu_is_bookE() && ! (_read_BOOKE_TCR() & BOOKE_TCR_DIE) )
0199     msr_value = 0;
0200 
0201   if (msr_value & MSR_EE) return 1;
0202 
0203   return 0;
0204 }
0205 
0206 void _Clock_Initialize( void )
0207 {
0208   rtems_interrupt_level l,tcr;
0209 
0210   Clock_Decrementer_value = (BSP_bus_frequency/BSP_time_base_divisor)*
0211             rtems_configuration_get_milliseconds_per_tick();
0212 
0213   /* set the decrementer now, prior to installing the handler
0214    * so no interrupts will happen in a while.
0215    */
0216   PPC_Set_decrementer( (unsigned)-1 );
0217 
0218   /* On a bookE CPU the decrementer works differently. It doesn't
0219    * count past zero but we can enable auto-reload :-)
0220    */
0221   if ( ppc_cpu_is_bookE() ) {
0222 
0223     rtems_interrupt_disable(l);
0224 
0225       tcr  = _read_BOOKE_TCR();
0226       tcr |= BOOKE_TCR_ARE;
0227       tcr &= ~BOOKE_TCR_DIE;
0228       _write_BOOKE_TCR(tcr);
0229 
0230     rtems_interrupt_enable(l);
0231   }
0232 
0233   Clock_TC.tc_get_timecount = Clock_Get_timecount;
0234   Clock_TC.tc_counter_mask = 0xffffffff;
0235   Clock_TC.tc_frequency = (UINT64_C(1000) * BSP_bus_frequency) / BSP_time_base_divisor;
0236   Clock_TC.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
0237   rtems_timecounter_install(&Clock_TC);
0238 
0239   /*
0240    * If a decrementer exception was pending, it is cleared by
0241    * executing the default (nop) handler at this point;
0242    * The next exception will then be taken by our clock handler.
0243    * Clock handler installation initializes the decrementer to
0244    * the correct value.
0245    */
0246   clock_handler = clockHandler;
0247   if (!BSP_connect_clock_handler ()) {
0248     printk("Unable to initialize system clock\n");
0249     rtems_fatal_error_occurred(1);
0250   }
0251 }