summaryrefslogblamecommitdiffstats
path: root/bsps/shared/dev/clock/arm-generic-timer.c
blob: 11df5cef254e2cdcacb344fe59a402b1f882be8c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12


                                                                 








                                                          
                                        





                                
  

                                                                                   














                                                                            
                                              

                                        








                                            


                                             































                                                                        
                 
 


                                                         





                                                                        
                                                              










                                         

                                 








                                                                

                                                    



                                                          

                                     
                                                          

 




                                             









                                                              

                                         
                     
 

                                
                               
               

                              





                                                                         



                          

                            







                                                    

                                               

                                             
                                             
/*
 * Copyright (c) 2017 embedded brains GmbH.  All rights reserved.
 *
 * 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 <bsp.h>
#include <bsp/fatal.h>
#include <bsp/irq.h>
#include <bsp/irq-generic.h>
#include <dev/clock/arm-generic-timer.h>

#include <rtems/counter.h>
#include <rtems/sysinit.h>
#include <rtems/timecounter.h>
#include <rtems/score/smpimpl.h>

/*
 * Clock driver using the ARMv7-AR/AArch64 Generic Timer.  The BSP must provide the
 * following function:
 *
 * void arm_generic_timer_get_config(uint32_t *frequency, uint32_t *irq);
 *
 * The BSP may optionally define ARM_GENERIC_TIMER_USE_VIRTUAL in <bsp.h> to
 * use the virtual timer instead of the physical timer.
 */

typedef struct {
  struct timecounter tc;
  uint32_t interval;
  rtems_vector_number irq;
} arm_gt_clock_context;

static arm_gt_clock_context arm_gt_clock_instance;

/* This is defined in dev/clock/clockimpl.h */
void Clock_isr(rtems_irq_hdl_param arg);

static void arm_gt_clock_at_tick(void)
{
  uint64_t cval;
  uint32_t interval;

  interval = arm_gt_clock_instance.interval;
  cval = arm_gt_clock_get_compare_value();
  cval += interval;
  arm_gt_clock_set_compare_value(cval);
#ifdef ARM_GENERIC_TIMER_UNMASK_AT_TICK
  arm_gt_clock_set_control(0x1);
#endif /* ARM_GENERIC_TIMER_UNMASK_AT_TICK */
}

static void arm_gt_clock_handler_install(void)
{
  rtems_status_code sc;

  sc = rtems_interrupt_handler_install(
    arm_gt_clock_instance.irq,
    "Clock",
    RTEMS_INTERRUPT_UNIQUE,
    (rtems_interrupt_handler) Clock_isr,
    NULL
  );
  if (sc != RTEMS_SUCCESSFUL) {
    bsp_fatal(BSP_ARM_FATAL_GENERIC_TIMER_CLOCK_IRQ_INSTALL);
  }
}

static uint32_t arm_gt_clock_get_timecount(struct timecounter *tc)
{
  return (uint32_t) arm_gt_clock_get_count();
}

static void arm_gt_clock_gt_init(uint64_t cval)
{
  arm_gt_clock_set_compare_value(cval);
  arm_gt_clock_set_control(0x1);
}

#if defined(RTEMS_SMP) && !defined(CLOCK_DRIVER_USE_ONLY_BOOT_PROCESSOR)
static void arm_gt_clock_secondary_action(void *arg)
{
  uint64_t *cval;

  cval = arg;
  arm_gt_clock_gt_init(*cval);
  bsp_interrupt_vector_enable(arm_gt_clock_instance.irq);
}
#endif

static void arm_gt_clock_secondary_initialization(uint64_t cval)
{
#if defined(RTEMS_SMP) && !defined(CLOCK_DRIVER_USE_ONLY_BOOT_PROCESSOR)
  _SMP_Broadcast_action(arm_gt_clock_secondary_action, &cval);
#endif
}

static void arm_gt_clock_initialize(void)
{
  uint64_t frequency;
  uint64_t us_per_tick;
  uint32_t interval;
  uint64_t cval;
  struct timecounter *tc;

  tc = &arm_gt_clock_instance.tc;
  frequency = tc->tc_frequency;
  us_per_tick = rtems_configuration_get_microseconds_per_tick();
  interval = (uint32_t) ((frequency * us_per_tick) / 1000000);
  cval = arm_gt_clock_get_count();
  cval += interval;
  arm_gt_clock_instance.interval = interval;

  arm_gt_clock_gt_init(cval);
  arm_gt_clock_secondary_initialization(cval);

  tc->tc_get_timecount = arm_gt_clock_get_timecount;
  tc->tc_counter_mask = 0xffffffff;
  tc->tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
  rtems_timecounter_install(tc);
}

uint32_t _CPU_Counter_frequency(void)
{
  return (uint32_t) arm_gt_clock_instance.tc.tc_frequency;
}

CPU_Counter_ticks _CPU_Counter_read(void)
{
  return (uint32_t) arm_gt_clock_get_count();
}

static void arm_gt_system_init(void)
{
#ifdef ARM_GENERIC_TIMER_SYSTEM_BASE
  volatile uint32_t *cntcr;

  cntcr = (volatile uint32_t *) ARM_GENERIC_TIMER_SYSTEM_BASE;
  *cntcr = ARM_GENERIC_TIMER_SYSTEM_CNTCR;
#endif
}

static void arm_gt_clock_early_init(void)
{
  uint32_t frequency;

  arm_gt_system_init();
  arm_gt_clock_set_control(0x3);
  arm_generic_timer_get_config(
    &frequency,
    &arm_gt_clock_instance.irq
  );

  /*
   * Used by _CPU_Counter_frequency() before arm_gt_clock_initialize() is
   * called.
   */
  arm_gt_clock_instance.tc.tc_frequency = frequency;
}

RTEMS_SYSINIT_ITEM(
  arm_gt_clock_early_init,
  RTEMS_SYSINIT_CPU_COUNTER,
  RTEMS_SYSINIT_ORDER_FIRST
);

#define Clock_driver_support_at_tick() \
  arm_gt_clock_at_tick()

#define Clock_driver_support_initialize_hardware() \
  arm_gt_clock_initialize()

#define Clock_driver_support_install_isr(isr) \
  arm_gt_clock_handler_install()

/* Include shared source clock driver code */
#include "../../shared/dev/clock/clockimpl.h"