Back to home page

LXR

 
 

    


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

0001 /**
0002  *  @file
0003  *
0004  *  This routine initializes the interval timer on the
0005  *  PowerPC 403 CPU.  The tick frequency is specified by the BSP.
0006  */
0007 
0008 /*
0009  *  Original PPC403 Code from:
0010  *  Author: Andrew Bray <andy@i-cubed.co.uk>
0011  *  COPYRIGHT (c) 1995 by i-cubed ltd.
0012  *
0013  *  Modifications for PPC405GP by Dennis Ehlin
0014  *
0015  *  To anyone who acknowledges that this file is provided "AS IS"
0016  *  without any express or implied warranty:
0017  *      permission to use, copy, modify, and distribute this file
0018  *      for any purpose is hereby granted without fee, provided that
0019  *      the above copyright notice and this notice appears in all
0020  *      copies, and that the name of i-cubed limited not be used in
0021  *      advertising or publicity pertaining to distribution of the
0022  *      software without specific, written prior permission.
0023  *      i-cubed limited makes no representations about the suitability
0024  *      of this software for any purpose.
0025  *
0026  *  Modifications for deriving timer clock from cpu system clock by
0027  *              Thomas Doerfler <td@imd.m.isar.de>
0028  *  for these modifications:
0029  *  Copyright (c) 1997 IMD Ingenieurbuero fuer Microcomputertechnik
0030  *
0031  *  COPYRIGHT (c) 1989-2012.
0032  *  On-Line Applications Research Corporation (OAR).
0033  *
0034  *  The license and distribution terms for this file may be
0035  *  found in the file LICENSE in this distribution or at
0036  *  http://www.rtems.org/license/LICENSE.
0037  */
0038 
0039 #include <rtems.h>
0040 #include <rtems/clockdrv.h>
0041 #include <rtems/libio.h>
0042 #include <stdlib.h>                     /* for atexit() */
0043 #include <rtems/bspIo.h>
0044 #include <rtems/powerpc/powerpc.h>
0045 
0046 /*
0047  * check, which exception handling code is present
0048  */
0049 
0050 #include <bsp.h>
0051 
0052 #include <bsp/vectors.h>
0053 #include <bsp/irq.h>
0054 
0055 extern uint32_t   bsp_clicks_per_usec;
0056 
0057 volatile uint32_t Clock_driver_ticks;
0058 static uint32_t   pit_value, tick_time;
0059 static bool       auto_restart;
0060 
0061 static void Clock_exit( void );
0062 
0063 static inline uint32_t get_itimer(void)
0064 {
0065   register uint32_t rc;
0066 
0067 #ifndef ppc405 /* this is a ppc403 */
0068   __asm__ volatile ("mfspr %0, 0x3dd" : "=r" ((rc))); /* TBLO */
0069 #else /* ppc405 */
0070   __asm__ volatile ("mfspr %0, 0x10c" : "=r" ((rc))); /* 405GP TBL */
0071 #endif /* ppc405 */
0072 
0073   return rc;
0074 }
0075 
0076 /*
0077  *  ISR Handler
0078  */
0079 static void Clock_isr(void* handle)
0080 {
0081   uint32_t clicks_til_next_interrupt;
0082 #if defined(BSP_PPC403_CLOCK_ISR_IRQ_LEVEL)
0083   uint32_t l_orig = _ISR_Get_level();
0084 #endif
0085 
0086   if (!auto_restart) {
0087     uint32_t itimer_value;
0088     /*
0089      * setup for next interrupt; making sure the new value is reasonably
0090      * in the future.... in case we lost out on an interrupt somehow
0091      */
0092     itimer_value = get_itimer();
0093     tick_time += pit_value;
0094 
0095     /*
0096      * how far away is next interrupt *really*
0097      * It may be a long time; this subtraction works even if
0098      * Clock_clicks_interrupt < Clock_clicks_low_order via
0099      * the miracle of unsigned math.
0100      */
0101     clicks_til_next_interrupt = tick_time - itimer_value;
0102 
0103     /*
0104      * If it is too soon then bump it up.
0105      * This should only happen if CPU_HPPA_CLICKS_PER_TICK is too small.
0106      * But setting it low is useful for debug, so...
0107      */
0108     if (clicks_til_next_interrupt < 400) {
0109       tick_time = itimer_value + 1000;
0110       clicks_til_next_interrupt = 1000;
0111       /* XXX: count these! this should be rare */
0112     }
0113 
0114     /*
0115      * If it is too late, that means we missed the interrupt somehow.
0116      * Rather than wait 35-50s for a wrap, we just fudge it here.
0117      */
0118     if (clicks_til_next_interrupt > pit_value) {
0119       tick_time = itimer_value + 1000;
0120       clicks_til_next_interrupt = 1000;
0121       /* XXX: count these! this should never happen :-) */
0122     }
0123 
0124     __asm__ volatile ("mtspr 0x3db, %0" :: "r"
0125                       (clicks_til_next_interrupt));                  /* PIT */
0126   }
0127 
0128   __asm__ volatile ("mtspr 0x3d8, %0" :: "r" (0x08000000));          /* TSR */
0129 
0130   Clock_driver_ticks++;
0131 
0132   /* Give BSP a chance to say if they want to re-enable interrupts */
0133 #if defined(BSP_PPC403_CLOCK_ISR_IRQ_LEVEL)
0134   _ISR_Set_level(BSP_PPC403_CLOCK_ISR_IRQ_LEVEL);
0135 #endif
0136   rtems_clock_tick();
0137 
0138 #if defined(BSP_PPC403_CLOCK_ISR_IRQ_LEVEL)
0139   _ISR_Set_level(l_orig)
0140 #endif
0141 }
0142 
0143 static int ClockIsOn(const rtems_irq_connect_data* unused)
0144 {
0145   register uint32_t tcr;
0146 
0147   __asm__ volatile ("mfspr %0, 0x3da" : "=r" ((tcr)));               /* TCR */
0148 
0149   return (tcr & 0x04000000) != 0;
0150 }
0151 
0152 static void ClockOff(const rtems_irq_connect_data* unused)
0153 {
0154   register uint32_t tcr;
0155 
0156   __asm__ volatile ("mfspr %0, 0x3da" : "=r" ((tcr)));               /* TCR */
0157   tcr &= ~ 0x04400000;
0158   __asm__ volatile ("mtspr 0x3da, %0" : "=r" ((tcr)) : "0" ((tcr))); /* TCR */
0159 }
0160 
0161 static void ClockOn(const rtems_irq_connect_data* unused)
0162 {
0163   uint32_t          iocr;
0164   register uint32_t tcr;
0165 #ifndef ppc405
0166   uint32_t          pvr;
0167 #endif /* ppc403 */
0168 
0169   Clock_driver_ticks = 0;
0170 
0171 #ifndef ppc405 /* this is a ppc403 */
0172   __asm__ volatile ("mfdcr %0, 0xa0" : "=r" (iocr));              /* IOCR */
0173   iocr &= ~4;                         /* timer clocked from system clock */
0174   __asm__ volatile ("mtdcr 0xa0, %0" : "=r" (iocr) : "0" (iocr)); /* IOCR */
0175 
0176   __asm__ volatile ("mfspr %0, 0x11f" : "=r" ((pvr))); /* PVR */
0177   if (((pvr & 0xffff0000) >> 16) != 0x0020)
0178     return;                             /* Not a ppc403 */
0179 
0180   if ((pvr & 0xff00) == 0x0000)         /* 403GA */
0181 #if 0 /* FIXME: in which processor versions will "autoload" work properly? */
0182     auto_restart = (pvr & 0x00f0) > 0x0000 ? true : false;
0183 #else
0184     /* no known chip version supports auto restart of timer... */
0185     auto_restart = false;
0186 #endif
0187   else if ((pvr & 0xff00) == 0x0100)    /* 403GB */
0188     auto_restart = true;
0189 
0190 #else /* ppc405 */
0191   __asm__ volatile (
0192     ".machine \"push\"\n"
0193     ".machine \"any\"\n"
0194     "mfdcr %0, 0x0b2\n"
0195     ".machine \"pop\"" :
0196     "=r" (iocr)
0197   ); /*405GP CPC0_CR1 */
0198   iocr &=~0x800000;               /* timer clocked from system clock CETE*/
0199   __asm__ volatile (
0200     ".machine \"push\"\n"
0201     ".machine \"any\"\n"
0202     "mtdcr 0x0b2, %0\n"
0203     ".machine \"pop\"" :
0204     "=r" (iocr) :
0205     "0" (iocr)
0206   ); /* 405GP CPC0_CR1 */
0207 
0208   /*
0209    * Enable auto restart
0210    */
0211   auto_restart = true;
0212 #endif /* ppc405 */
0213 
0214   pit_value = rtems_configuration_get_microseconds_per_tick() *
0215                 bsp_clicks_per_usec;
0216 
0217   /*
0218    * Set PIT value
0219    */
0220   __asm__ volatile ("mtspr 0x3db, %0" : : "r" (pit_value)); /* PIT */
0221 
0222   /*
0223    * Set timer to autoreload, bit TCR->ARE = 1  0x0400000
0224    * Enable PIT interrupt,    bit TCR->PIE = 1  0x4000000
0225    */
0226   tick_time = get_itimer() + pit_value;
0227 
0228   __asm__ volatile ("mfspr %0, 0x3da" : "=r" ((tcr)));               /* TCR */
0229   tcr = (tcr & ~0x04400000) | (auto_restart ? 0x04400000 : 0x04000000);
0230 #if 1
0231   __asm__ volatile ("mtspr 0x3da, %0" : "=r" ((tcr)) : "0" ((tcr))); /* TCR */
0232 #endif
0233 }
0234 
0235 static void Install_clock(void (*clock_isr)(void *))
0236 {
0237   rtems_irq_connect_data clockIrqConnData;
0238 
0239   Clock_driver_ticks = 0;
0240 
0241   /*
0242    * initialize the interval here
0243    * First tick is set to right amount of time in the future
0244    * Future ticks will be incremented over last value set
0245    * in order to provide consistent clicks in the face of
0246    * interrupt overhead
0247    */
0248   clockIrqConnData.on   = ClockOn;
0249   clockIrqConnData.off  = ClockOff;
0250   clockIrqConnData.isOn = ClockIsOn;
0251   clockIrqConnData.name = BSP_PIT;
0252   clockIrqConnData.hdl  = clock_isr;
0253   if (!BSP_install_rtems_irq_handler (&clockIrqConnData)) {
0254     printk("Unable to connect Clock Irq handler\n");
0255     rtems_fatal_error_occurred(1);
0256   }
0257 
0258   atexit(Clock_exit);
0259 }
0260 
0261 /*
0262  * Called via atexit()
0263  * Remove the clock interrupt handler by setting handler to NULL
0264  *
0265  * This will not work on the 405GP because
0266  * when bit's are set in TCR they can only be unset by a reset
0267  */
0268 void Clock_exit(void)
0269 {
0270   rtems_irq_connect_data clockIrqConnData;
0271 
0272   clockIrqConnData.name = BSP_PIT;
0273   if (!BSP_get_current_rtems_irq_handler(&clockIrqConnData)) {
0274     printk("Unable to stop system clock\n");
0275     rtems_fatal_error_occurred(1);
0276   }
0277 
0278   BSP_remove_rtems_irq_handler (&clockIrqConnData);
0279 }
0280 
0281 void _Clock_Initialize( void )
0282 {
0283   Install_clock( Clock_isr );
0284 }