root/src/system/kernel/arch/arm/soc_pxa.cpp
#include <vm/vm.h>

#include "soc_pxa.h"

/* PXA Interrupt Controller Registers */
#define PXA_ICIP        0x00
#define PXA_ICMR        0x01
#define PXA_ICFP        0x03
#define PXA_ICMR2       0x28

void
PXAInterruptController::EnableInterrupt(int32 irq)
{
        if (irq <= 31) {
                fRegBase[PXA_ICMR] |= 1 << irq;
                return;
        }

        fRegBase[PXA_ICMR2] |= 1 << (irq - 32);
}


void
PXAInterruptController::DisableInterrupt(int32 irq)
{
        if (irq <= 31) {
                fRegBase[PXA_ICMR] &= ~(1 << irq);
                return;
        }

        fRegBase[PXA_ICMR2] &= ~(1 << (irq - 32));
}


void
PXAInterruptController::HandleInterrupt()
{
        for (int i=0; i < 32; i++) {
                if (fRegBase[PXA_ICIP] & (1 << i))
                        io_interrupt_handler(i, true);
        }
}


PXAInterruptController::PXAInterruptController(uint32_t reg_base)
{
        fRegArea = vm_map_physical_memory(B_SYSTEM_TEAM, "intc-pxa", (void**)&fRegBase,
                B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
                reg_base, false);
        if (fRegArea < 0)
                panic("PXAInterruptController: cannot map registers!");

        fRegBase[PXA_ICMR] = 0;
        fRegBase[PXA_ICMR2] = 0;
}


#define PXA_TIMERS_INTERRUPT    7 /* OST_4_11 */

#define PXA_OSSR        0x05
#define PXA_OIER        0x07
#define PXA_OSCR4       0x10
#define PXA_OSCR5       0x11
#define PXA_OSMR4       0x20
#define PXA_OSMR5       0x21
#define PXA_OMCR4       0x30
#define PXA_OMCR5       0x31

#define PXA_RES_S       (3 << 0)
#define PXA_RES_MS      (1 << 1)
#define PXA_RES_US      (1 << 2)

#define US2S(bt)        ((bt) / 1000000ULL)
#define US2MS(bt)       ((bt) / 1000ULL)

void
PXATimer::SetTimeout(bigtime_t timeout)
{
        uint32 val = timeout & UINT_MAX;
        uint32 res = PXA_RES_US;

        if (timeout & ~UINT_MAX) {
                // Does not fit, so scale resolution down to milliseconds
                if (US2MS(timeout) & ~UINT_MAX) {
                        // Still does not fit, scale down to seconds as last ditch attempt
                        val = US2S(timeout) & UINT_MAX;
                        res = PXA_RES_S;
                } else {
                        // Fits in millisecond resolution
                        val = US2MS(timeout) & UINT_MAX;
                        res = PXA_RES_MS;
                }
        }

        dprintf("arch_timer_set_hardware_timer(val=%" B_PRIu32 ", res=%" B_PRIu32 ")\n", val, res);
        fRegBase[PXA_OIER] |= (1 << 4);
        fRegBase[PXA_OMCR4] = res;
        fRegBase[PXA_OSMR4] = val;
        fRegBase[PXA_OSCR4] = 0; // start counting from 0 again
}

void
PXATimer::Clear()
{
        fRegBase[PXA_OMCR4] = 0; // disable our timer
        fRegBase[PXA_OIER] &= ~(1 << 4);
}


bigtime_t
PXATimer::Time()
{
        if (fRegArea < 0)
                return 0;

        return (fRegBase != NULL) ?
                fSystemTime + fRegBase[PXA_OSCR5] :
                0ULL;
}


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


int32
PXATimer::HandleInterrupt()
{
        if (fRegBase[PXA_OSSR] & (1 << 4)) {
                fRegBase[PXA_OSSR] |= (1 << 4);
                return timer_interrupt();
        }

        if (fRegBase[PXA_OSSR]  & (1 << 5)) {
                fRegBase[PXA_OSSR] |= (1 << 5);
                fSystemTime += UINT_MAX + 1ULL;
        }

        return B_HANDLED_INTERRUPT;
}


PXATimer::PXATimer(uint32_t reg_base)
{
        fRegArea = vm_map_physical_memory(B_SYSTEM_TEAM, "pxa-timer", (void**)&fRegBase,
                B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
                reg_base, false);
        if (fRegArea < 0)
                panic("Cannot map PXATimer registers!");

        fRegBase[PXA_OIER] |= (1 << 5); // enable timekeeping timer
        fRegBase[PXA_OMCR5] = PXA_RES_US | (1 << 7);
        fRegBase[PXA_OSMR5] = UINT_MAX;
        fRegBase[PXA_OSCR5] = 0;

        install_io_interrupt_handler(PXA_TIMERS_INTERRUPT, &PXATimer::_InterruptWrapper, NULL, 0);
}