/* * Copyright (c) 2013, 2016 embedded brains GmbH. All rights reserved. * * embedded brains GmbH * Dornierstr. 4 * 82178 Puchheim * Germany * * * 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 #include #include #include #include #include #include #include #define A9MPCORE_GT ((volatile a9mpcore_gt *) BSP_ARM_A9MPCORE_GT_BASE) static struct timecounter a9mpcore_tc; /* This is defined in clockdrv_shell.h */ void Clock_isr(rtems_irq_hdl_param arg); __attribute__ ((weak)) uint32_t a9mpcore_clock_periphclk(void) { /* default to the BSP option. */ return BSP_ARM_A9MPCORE_PERIPHCLK; } static void a9mpcore_clock_at_tick(void) { volatile a9mpcore_gt *gt = A9MPCORE_GT; gt->irqst = A9MPCORE_GT_IRQST_EFLG; } static void a9mpcore_clock_handler_install(void) { rtems_status_code sc; sc = rtems_interrupt_handler_install( A9MPCORE_IRQ_GT, "Clock", RTEMS_INTERRUPT_UNIQUE, (rtems_interrupt_handler) Clock_isr, NULL ); if (sc != RTEMS_SUCCESSFUL) { bsp_fatal(BSP_ARM_A9MPCORE_FATAL_CLOCK_IRQ_INSTALL); } } static uint64_t a9mpcore_clock_get_counter(volatile a9mpcore_gt *gt) { uint32_t cl; uint32_t cu1; uint32_t cu2; do { cu1 = gt->cntrupper; cl = gt->cntrlower; cu2 = gt->cntrupper; } while (cu1 != cu2); return ((uint64_t) cu2 << 32) | cl; } static uint32_t a9mpcore_clock_get_timecount(struct timecounter *tc) { volatile a9mpcore_gt *gt = A9MPCORE_GT; return gt->cntrlower; } static void a9mpcore_clock_gt_init( volatile a9mpcore_gt *gt, uint64_t cmpval, uint32_t interval ) { gt->cmpvallower = (uint32_t) cmpval; gt->cmpvalupper = (uint32_t) (cmpval >> 32); gt->autoinc = interval; gt->ctrl = A9MPCORE_GT_CTRL_AUTOINC_EN | A9MPCORE_GT_CTRL_IRQ_EN | A9MPCORE_GT_CTRL_COMP_EN | A9MPCORE_GT_CTRL_TMR_EN; } #if defined(RTEMS_SMP) && !defined(CLOCK_DRIVER_USE_ONLY_BOOT_PROCESSOR) typedef struct { uint64_t cmpval; uint32_t interval; } a9mpcore_clock_init_data; static void a9mpcore_clock_secondary_action(void *arg) { volatile a9mpcore_gt *gt = A9MPCORE_GT; a9mpcore_clock_init_data *init_data = arg; a9mpcore_clock_gt_init(gt, init_data->cmpval, init_data->interval); bsp_interrupt_vector_enable(A9MPCORE_IRQ_GT); } #endif static void a9mpcore_clock_secondary_initialization( volatile a9mpcore_gt *gt, uint64_t cmpval, uint32_t interval ) { #if defined(RTEMS_SMP) && !defined(CLOCK_DRIVER_USE_ONLY_BOOT_PROCESSOR) a9mpcore_clock_init_data init_data = { .cmpval = cmpval, .interval = interval }; _SMP_Before_multitasking_action_broadcast( a9mpcore_clock_secondary_action, &init_data ); if (cmpval - a9mpcore_clock_get_counter(gt) >= interval) { bsp_fatal(BSP_ARM_A9MPCORE_FATAL_CLOCK_SMP_INIT); } #endif } static void a9mpcore_clock_initialize(void) { volatile a9mpcore_gt *gt = A9MPCORE_GT; uint64_t periphclk = a9mpcore_clock_periphclk(); uint64_t us_per_tick = rtems_configuration_get_microseconds_per_tick(); uint32_t interval = (uint32_t) ((periphclk * us_per_tick) / 1000000); uint64_t cmpval; gt->ctrl &= A9MPCORE_GT_CTRL_TMR_EN; gt->irqst = A9MPCORE_GT_IRQST_EFLG; cmpval = a9mpcore_clock_get_counter(gt); cmpval += interval; a9mpcore_clock_gt_init(gt, cmpval, interval); a9mpcore_clock_secondary_initialization(gt, cmpval, interval); a9mpcore_tc.tc_get_timecount = a9mpcore_clock_get_timecount; a9mpcore_tc.tc_counter_mask = 0xffffffff; a9mpcore_tc.tc_frequency = periphclk; a9mpcore_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER; rtems_timecounter_install(&a9mpcore_tc); } CPU_Counter_ticks _CPU_Counter_read(void) { volatile a9mpcore_gt *gt = A9MPCORE_GT; return gt->cntrlower; } static void a9mpcore_clock_cleanup_isr(void *arg) { volatile a9mpcore_gt *gt = A9MPCORE_GT; (void) arg; gt->ctrl &= A9MPCORE_GT_CTRL_TMR_EN; gt->irqst = A9MPCORE_GT_IRQST_EFLG; } static void a9mpcore_clock_cleanup(void) { rtems_status_code sc; /* * The relevant registers / bits of the global timer are banked and chances * are on an SPM system, that we are executing on the wrong CPU to reset * them. Thus we will have the actual cleanup done with the next clock tick. * The ISR will execute on the right CPU for the cleanup. */ sc = rtems_interrupt_handler_install( A9MPCORE_IRQ_GT, "Clock", RTEMS_INTERRUPT_REPLACE, a9mpcore_clock_cleanup_isr, NULL ); if (sc != RTEMS_SUCCESSFUL) { bsp_fatal(BSP_ARM_A9MPCORE_FATAL_CLOCK_IRQ_REMOVE); } } #define Clock_driver_support_at_tick() \ a9mpcore_clock_at_tick() #define Clock_driver_support_initialize_hardware() \ a9mpcore_clock_initialize() #define Clock_driver_support_install_isr(isr) \ a9mpcore_clock_handler_install() #define Clock_driver_support_shutdown_hardware() \ a9mpcore_clock_cleanup() /* Include shared source clock driver code */ #include "../../shared/clockdrv_shell.h"