⛏️ index : haiku.git

#include <vm/vm.h>

#include "soc_omap3.h"

enum {
	INTCPS_REVISION = 0,
	INTCPS_SYSCONFIG = 4,
	INTCPS_SYSSTATUS,
	INTCPS_SIR_IRQ = 16,
	INTCPS_SIR_FIQ = 17,
	INTCPS_CONTROL = 18,
	INTCPS_PROTECTION = 19,
	INTCPS_IDLE = 20,
	INTCPS_IRQ_PRIORITY = 24,
	INTCPS_FIQ_PRIORITY = 25,
	INTCPS_THRESHOLD = 26,
	INTCPS_ITRn = 32,
	INTCPS_MIRn = 33,
	INTCPS_MIR_CLEARn = 34,
	INTCPS_MIR_SETn = 35,
	INTCPS_ISR_SETn = 36,
	INTCPS_ISR_CLEARn = 37,
	INTCPS_PENDING_IRQn = 38,
	INTCPS_PENDING_FIQn = 39,
	INTCPS_ILRm = 40,
};


void
OMAP3InterruptController::EnableInterrupt(int32 irq)
{
	uint32 bit = irq % 32, bank = irq / 32;
	fRegBase[INTCPS_MIR_CLEARn + (8 * bank)] = 1 << bit;
}


void
OMAP3InterruptController::DisableInterrupt(int32 irq)
{
	uint32 bit = irq % 32, bank = irq / 32;
	fRegBase[INTCPS_MIR_SETn + (8 * bank)] = 1 << bit;
}


void
OMAP3InterruptController::HandleInterrupt()
{
	bool handledIRQ = false;
	int irqnr = 0;

	do {
		for (uint32 i=0; i < fNumPending; i++) {
			irqnr = fRegBase[INTCPS_PENDING_IRQn + (8 * i)];
			if (irqnr)
				break;
		}

		if (!irqnr)
			break;

		irqnr = fRegBase[INTCPS_SIR_IRQ];
		irqnr &= 0x7f; /* ACTIVEIRQ */

		if (irqnr) {
			io_interrupt_handler(irqnr, true);
			handledIRQ = true;
		}
	} while(irqnr);

	// If IRQ got cleared before we could handle it, simply
	// ack it.
	if (!handledIRQ)
		fRegBase[INTCPS_CONTROL] = 1;
}


void
OMAP3InterruptController::SoftReset()
{
	uint32 tmp = fRegBase[INTCPS_REVISION] & 0xff;

	dprintf("OMAP: INTC found at 0x%p (rev %" B_PRIu32 ".%" B_PRIu32 ")\n",
		fRegBase, tmp >> 4, tmp & 0xf);

	tmp = fRegBase[INTCPS_SYSCONFIG];
	tmp |= 1 << 1;  /* soft reset */
	fRegBase[INTCPS_SYSCONFIG] = tmp;

        while (!(fRegBase[INTCPS_SYSSTATUS] & 0x1))
                /* Wait for reset to complete */;

        /* Enable autoidle */
        fRegBase[INTCPS_SYSCONFIG] = 1;
}


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

	SoftReset();

	// Enable protection (MPU registers only available in privileged mode)
	fRegBase[INTCPS_PROTECTION] |= 1;
}


enum {
	TIDR = 0,
	TIOCP_CFG = 4,
	TISTAT,
	TISR,
	TIER,
	TWER,
	TCLR,
	TCRR,
	TLDR,
	TTGR,
	TWPS,
	TMAR,
	TCAR1,
	TSICR,
	TCAR2,
	TPIR,
	TNIR,
	TCVR,
	TOCR,
	TOWR,
};

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


int32
OMAP3Timer::HandleInterrupt()
{
	uint32 ints = fRegBase[TISR] & 7;

	if (ints & 1) { // Match?
		dprintf("OMAP3Timer: match!\n");
		timer_interrupt();
	} else if (ints & 2) { // Overflow?
		dprintf("OMAP3Timer: overflow!\n");
		fSystemTime += UINT_MAX +1;
	} else if (ints & 4) { // Capture?
		dprintf("OMAP3Timer: capture!\n");
	}

	// clear interrupt
	fRegBase[TISR] = ints;

	return B_HANDLED_INTERRUPT;
}


void
OMAP3Timer::SetTimeout(bigtime_t timeout)
{
	fRegBase[TMAR] = fRegBase[TCRR] + timeout / 1000ULL;
	fRegBase[TIER] |= 1; // Enable match interrupt
}


bigtime_t
OMAP3Timer::Time()
{
	return fSystemTime + fRegBase[TCRR];
}


void
OMAP3Timer::Clear()
{
	fRegBase[TIER] &= ~1; // Disable match interrupt
}


OMAP3Timer::OMAP3Timer(uint32_t reg_base, uint32_t interrupt)
	: fSystemTime(0)
{
	fRegArea = vm_map_physical_memory(B_SYSTEM_TEAM, "timer-omap3", (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 OMAP3Timer registers!");

	fInterrupt = interrupt;
	if (fInterrupt < 0)
		panic("Cannot get OMAP3Timer interrupt!");

	uint32 rev = fRegBase[TIDR];
	dprintf("OMAP: Found timer @ 0x%p, IRQ %d (rev %" B_PRIu32 ".%" B_PRIu32 ")\n",
		fRegBase, fInterrupt, (rev >> 4) & 0xf, rev & 0xf);

	// Let the timer run (so we can use it as clocksource)
	fRegBase[TCLR] |= 1;
	fRegBase[TIER] = 2; // Enable overflow interrupt

	install_io_interrupt_handler(fInterrupt, &OMAP3Timer::_InterruptWrapper, this, 0);
}