root/src/system/kernel/arch/arm/arch_timer_generic.cpp
/*
 * Copyright 2022 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */


#include "arch_timer_generic.h"


#define TIMER_IRQ 30


static uint32_t
get_counter_freq(void)
{
    uint32_t freq;
    asm volatile ("MRC p15, 0, %0, c14, c0, 0": "=r" (freq));
    return freq;
}


static uint64_t
get_counter(void)
{
    uint32_t counter_low;
    uint32_t counter_high;

    asm volatile ("ISB\n"
        "MRRC p15, 0, %0, %1, c14"
        : "=r" (counter_low), "=r" (counter_high));
    return ((uint64_t)counter_high << 32) | counter_low;
}


void
ARMGenericTimer::SetTimeout(bigtime_t timeout)
{
        uint32_t timeout_ticks = timeout * fTimerFrequencyMHz;
        asm volatile ("ISB\n"
                "MCR p15, 0, %0, c14, c2, 0"
                : : "r"(timeout_ticks));

        uint32_t timer_ctl = 1;
        asm volatile ("MCR p15, 0, %0, c14, c2, 1"
                : : "r"(timer_ctl));
}


void
ARMGenericTimer::Clear()
{
        uint32_t timer_ctl = 2;
        asm volatile ("MCR p15, 0, %0, c14, c2, 1"
                : : "r"(timer_ctl));
}


bigtime_t
ARMGenericTimer::Time()
{
        return get_counter() / fTimerFrequencyMHz;
}


int32
ARMGenericTimer::_InterruptWrapper(void *data)
{
        return ((ARMGenericTimer*)data)->HandleInterrupt();
}


int32
ARMGenericTimer::HandleInterrupt()
{
        return timer_interrupt();
}


ARMGenericTimer::ARMGenericTimer()
: HardwareTimer()
{
        Clear();

        install_io_interrupt_handler(TIMER_IRQ,
                &ARMGenericTimer::_InterruptWrapper, this, 0);

        fTimerFrequency = get_counter_freq();
        fTimerFrequencyMHz = fTimerFrequency / 1000000;
}


bool
ARMGenericTimer::IsAvailable()
{
        uint32_t pfr1;
        asm volatile("MRC p15, 0, %0, c0, c1, 1": "=r" (pfr1));
        return (pfr1 & 0x000F0000) == 0x00010000;
}