summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/ppc403/clock/clock_4xx.c
blob: 4b887d18ce2842dae0aa9cfe5249ce39c5cf3511 (plain) (tree)































































                                                                      
 







                                                                        



                                                
 

                                                 
 


















                                                   
 








                                                  
 



























                                                                                                                          
 





















































                                                                      

                                                              




                                            
 















                                                                
 




                                                                  
 

                          
/*  clock.c
 *
 *  This routine initializes the interval timer on the
 *  PowerPC 405 CPU.  The tick frequency is specified by the bsp.
 *
 *  Author: Andrew Bray <andy@i-cubed.co.uk>
 *
 *  COPYRIGHT (c) 1995 by i-cubed ltd.
 *
 *  To anyone who acknowledges that this file is provided "AS IS"
 *  without any express or implied warranty:
 *      permission to use, copy, modify, and distribute this file
 *      for any purpose is hereby granted without fee, provided that
 *      the above copyright notice and this notice appears in all
 *      copies, and that the name of i-cubed limited not be used in
 *      advertising or publicity pertaining to distribution of the
 *      software without specific, written prior permission.
 *      i-cubed limited makes no representations about the suitability
 *      of this software for any purpose.
 *
 *  Derived from c/src/lib/libcpu/hppa1.1/clock/clock.c:
 *
 *  Modifications for deriving timer clock from cpu system clock by
 *              Thomas Doerfler <td@imd.m.isar.de>
 *  for these modifications:
 *  COPYRIGHT (c) 1997 by IMD, Puchheim, Germany.
 *
 *  COPYRIGHT (c) 1989-2007.
 *  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.com/license/LICENSE.
 *
 *  Modifications for PPC405GP by Dennis Ehlin
 *
 * Further modifications for PPC405GP/EX by Michael Hamel
 *
 *  $Id$
 */

#include <rtems.h>
#include <rtems/clockdrv.h>
#include <rtems/libio.h>
#include <stdlib.h>                     /* for atexit() */
#include <rtems/bspIo.h>
#include <ppc405common.h>
#include <libcpu/cpuIdent.h>
#include <bsp/irq.h>



/* PPC405GP */
#define CPC0_CR1	0xB2
 #define CR1_CETE	0x00800000

/* PPC405EX */
#define	SDR0_C405	0x180
 #define SDR_CETE	0x02000000

volatile uint32_t   Clock_driver_ticks;
static uint32_t   pit_value, tick_time;

void Clock_exit( void );

rtems_isr_entry set_vector(                    /* returns old vector */
  rtems_isr_entry     handler,                  /* isr routine        */
  rtems_vector_number vector,                   /* vector number      */
  int                 type                      /* RTEMS or RAW intr  */
);

/* Defined in bspstart.c */
extern uint32_t bsp_clicks_per_usec;

/*
 * These are set by clock driver during its init
 */

rtems_device_major_number rtems_clock_major = ~0;
rtems_device_minor_number rtems_clock_minor;

/*
 *  ISR Handler
 */

void Clock_isr(void* handle)
{
	Clock_driver_ticks++;
    rtems_clock_tick();
}

int ClockIsOn(const rtems_irq_connect_data* unused)
{
    return ((mfspr(TCR) & PIE) != 0);
}


void ClockOff(const rtems_irq_connect_data* unused)
{
    register uint32_t   r;

	r = mfspr(TCR);
	mtspr(TCR, r & ~(PIE | ARE) );
}

void ClockOn(const rtems_irq_connect_data* unused)
{
    uint32_t   iocr, r;
	ppc_cpu_id_t cpu;
    Clock_driver_ticks = 0;

 	cpu = get_ppc_cpu_type();
	if (cpu==PPC_405GP) {
		iocr = mfdcr(CPC0_CR1);
		if (bsp_timer_internal_clock) iocr &= ~CR1_CETE	;/* timer clocked from system clock */
								 else iocr |=  CR1_CETE; /* select external timer clock */
		mtdcr(CPC0_CR1,iocr);
	} else if (cpu==PPC_405EX) {
		mfsdr(SDR0_C405,iocr);
		if (bsp_timer_internal_clock) iocr &= ~SDR_CETE	;/* timer clocked from system clock */
								 else iocr |=  SDR_CETE; /* select external timer clock */
		mtsdr(SDR0_C405,iocr);
	} else {
		printk("clock.c:unrecognised CPU");
		rtems_fatal_error_occurred(1);
	}

    pit_value = rtems_configuration_get_microseconds_per_tick() * bsp_clicks_per_usec;
	mtspr(PIT,pit_value);

	tick_time = mfspr(TBL) + pit_value;
	r = mfspr(TCR);
	mtspr(TCR, r | PIE | ARE);
}



void Install_clock(void (*clock_isr)(void *))
{

    /*
     * initialize the interval here
     * First tick is set to right amount of time in the future
     * Future ticks will be incremented over last value set
     * in order to provide consistent clicks in the face of
     * interrupt overhead
     */

	rtems_irq_connect_data clockIrqConnData;

	Clock_driver_ticks = 0;
	clockIrqConnData.on   = ClockOn;
	clockIrqConnData.off  = ClockOff;
	clockIrqConnData.isOn = ClockIsOn;
	clockIrqConnData.name = BSP_PIT;
	clockIrqConnData.hdl  = clock_isr;
	if ( ! BSP_install_rtems_irq_handler (&clockIrqConnData)) {
		 printk("Unable to connect Clock Irq handler\n");
		 rtems_fatal_error_occurred(1);
	}
    atexit(Clock_exit);
}

void
ReInstall_clock(void (*new_clock_isr)(void *))
{
	uint32_t   isrlevel = 0;
	rtems_irq_connect_data clockIrqConnData;

	rtems_interrupt_disable(isrlevel);
	clockIrqConnData.name = BSP_PIT;
	if ( ! BSP_get_current_rtems_irq_handler(&clockIrqConnData)) {
		printk("Unable to stop system clock\n");
		rtems_fatal_error_occurred(1);
	}

	BSP_remove_rtems_irq_handler (&clockIrqConnData);
	clockIrqConnData.on   = ClockOn;
	clockIrqConnData.off  = ClockOff;
	clockIrqConnData.isOn = ClockIsOn;
	clockIrqConnData.name = BSP_PIT;
	clockIrqConnData.hdl  = new_clock_isr;
	if (!BSP_install_rtems_irq_handler (&clockIrqConnData)) {
		printk("Unable to connect Clock Irq handler\n");
		rtems_fatal_error_occurred(1);
	}
	rtems_interrupt_enable(isrlevel);
}


/*
 * Called via atexit()
 * Remove the clock interrupt handler by setting handler to NULL
 *
 * This will not work on the 405GP because
 * when bit's are set in TCR they can only be unset by a reset
 */

void Clock_exit(void)
{
    rtems_irq_connect_data clockIrqConnData;

    clockIrqConnData.name = BSP_PIT;
    if (!BSP_get_current_rtems_irq_handler(&clockIrqConnData)) {
      printk("Unable to stop system clock\n");
      rtems_fatal_error_occurred(1);
    }
    BSP_remove_rtems_irq_handler (&clockIrqConnData);
}


rtems_device_driver Clock_initialize(
  rtems_device_major_number major,
  rtems_device_minor_number minor,
  void *pargp
)
{
  Install_clock( Clock_isr );

  /*
   * make major/minor avail to others such as shared memory driver
   */
  rtems_clock_major = major;
  rtems_clock_minor = minor;

  return RTEMS_SUCCESSFUL;
}