ARM: kernel: introduce SoC abstraction
This introduces InterruptController and HardwareTimer classes to
handle the SoC specific implementations of timers and ints for
the ARM platform.
These could be improved and moved to a more 'generic' level once
we're confident they are 'good enough'.
NOTE: The OMAP timer implementation is fully untested and probably
completely non-functional....
This commit is contained in:
@@ -34,6 +34,12 @@ KernelMergeObject kernel_arch_arm.o :
|
||||
arch_atomic64.cpp
|
||||
arch_atomic32.cpp
|
||||
|
||||
# SoC minimal kernel-required support
|
||||
# (timers, interrupts, rtc?)
|
||||
soc.cpp
|
||||
soc_pxa.cpp
|
||||
soc_omap3.cpp
|
||||
|
||||
# paging
|
||||
arm_physical_page_mapper_large_memory.cpp
|
||||
ARMPagingMethod.cpp
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
#include <vm/VMAddressSpace.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <drivers/bus/FDT.h>
|
||||
#include "soc.h"
|
||||
|
||||
#include "soc_pxa.h"
|
||||
#include "soc_omap3.h"
|
||||
|
||||
#define TRACE_ARCH_INT
|
||||
#ifdef TRACE_ARCH_INT
|
||||
@@ -42,17 +47,6 @@
|
||||
#define USER_VECTOR_ADDR_LOW 0x00000000
|
||||
#define USER_VECTOR_ADDR_HIGH 0xffff0000
|
||||
|
||||
#define PXA_INTERRUPT_PHYS_BASE 0x40D00000
|
||||
#define PXA_INTERRUPT_SIZE 0x00000034
|
||||
|
||||
#define PXA_ICIP 0x00
|
||||
#define PXA_ICMR 0x01
|
||||
#define PXA_ICFP 0x03
|
||||
#define PXA_ICMR2 0x28
|
||||
|
||||
static area_id sPxaInterruptArea;
|
||||
static uint32 *sPxaInterruptBase;
|
||||
|
||||
extern int _vectors_start;
|
||||
extern int _vectors_end;
|
||||
|
||||
@@ -60,6 +54,7 @@ static area_id sVectorPageArea;
|
||||
static void *sVectorPageAddress;
|
||||
static area_id sUserVectorPageArea;
|
||||
static void *sUserVectorPageAddress;
|
||||
static fdt_module_info *sFdtModule;
|
||||
|
||||
// An iframe stack used in the early boot process when we don't have
|
||||
// threads yet.
|
||||
@@ -70,13 +65,9 @@ void
|
||||
arch_int_enable_io_interrupt(int irq)
|
||||
{
|
||||
TRACE(("arch_int_enable_io_interrupt(%d)\n", irq));
|
||||
|
||||
if (irq <= 31) {
|
||||
sPxaInterruptBase[PXA_ICMR] |= 1 << irq;
|
||||
return;
|
||||
}
|
||||
|
||||
sPxaInterruptBase[PXA_ICMR2] |= 1 << (irq - 32);
|
||||
InterruptController *ic = InterruptController::Get();
|
||||
if (ic != NULL)
|
||||
ic->EnableInterrupt(irq);
|
||||
}
|
||||
|
||||
|
||||
@@ -84,13 +75,9 @@ void
|
||||
arch_int_disable_io_interrupt(int irq)
|
||||
{
|
||||
TRACE(("arch_int_disable_io_interrupt(%d)\n", irq));
|
||||
|
||||
if (irq <= 31) {
|
||||
sPxaInterruptBase[PXA_ICMR] &= ~(1 << irq);
|
||||
return;
|
||||
}
|
||||
|
||||
sPxaInterruptBase[PXA_ICMR2] &= ~(1 << (irq - 32));
|
||||
InterruptController *ic = InterruptController::Get();
|
||||
if (ic != NULL)
|
||||
ic->DisableInterrupt(irq);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +116,18 @@ arch_int_init(kernel_args *args)
|
||||
extern "C" void arm_vector_init(void);
|
||||
|
||||
|
||||
static struct fdt_device_info intc_table[] = {
|
||||
{
|
||||
.compatible = "marvell,pxa-intc",
|
||||
.init = PXAInterruptController::Init,
|
||||
}, {
|
||||
.compatible = "ti,omap3-intc",
|
||||
.init = OMAP3InterruptController::Init,
|
||||
}
|
||||
};
|
||||
static int intc_count = sizeof(intc_table) / sizeof(struct fdt_device_info);
|
||||
|
||||
|
||||
status_t
|
||||
arch_int_init_post_vm(kernel_args *args)
|
||||
{
|
||||
@@ -136,7 +135,6 @@ arch_int_init_post_vm(kernel_args *args)
|
||||
sVectorPageArea = create_area("vectorpage", (void **)&sVectorPageAddress,
|
||||
B_ANY_ADDRESS, VECTORPAGE_SIZE, B_FULL_LOCK,
|
||||
B_KERNEL_WRITE_AREA | B_KERNEL_READ_AREA);
|
||||
|
||||
if (sVectorPageArea < 0)
|
||||
panic("vector page could not be created!");
|
||||
|
||||
@@ -166,14 +164,13 @@ arch_int_init_post_vm(kernel_args *args)
|
||||
dprintf("Enabled high vectors\n");
|
||||
}
|
||||
|
||||
sPxaInterruptArea = map_physical_memory("pxa_intc", PXA_INTERRUPT_PHYS_BASE,
|
||||
PXA_INTERRUPT_SIZE, 0, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&sPxaInterruptBase);
|
||||
status_t rc = get_module(B_FDT_MODULE_NAME, (module_info**)&sFdtModule);
|
||||
if (rc != B_OK)
|
||||
panic("Unable to get FDT module: %08lx!\n", rc);
|
||||
|
||||
if (sPxaInterruptArea < 0)
|
||||
return sPxaInterruptArea;
|
||||
|
||||
sPxaInterruptBase[PXA_ICMR] = 0;
|
||||
sPxaInterruptBase[PXA_ICMR2] = 0;
|
||||
rc = sFdtModule->setup_devices(intc_table, intc_count, NULL);
|
||||
if (rc != B_OK)
|
||||
panic("No interrupt controllers found!\n");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@@ -338,10 +335,9 @@ arch_arm_irq(struct iframe *iframe)
|
||||
{
|
||||
IFrameScope scope(iframe);
|
||||
|
||||
for (int i=0; i < 32; i++) {
|
||||
if (sPxaInterruptBase[PXA_ICIP] & (1 << i))
|
||||
int_io_interrupt_handler(i, true);
|
||||
}
|
||||
InterruptController *ic = InterruptController::Get();
|
||||
if (ic != NULL)
|
||||
ic->HandleInterrupt();
|
||||
}
|
||||
|
||||
|
||||
@@ -350,10 +346,5 @@ arch_arm_fiq(struct iframe *iframe)
|
||||
{
|
||||
IFrameScope scope(iframe);
|
||||
|
||||
for (int i=0; i < 32; i++) {
|
||||
if (sPxaInterruptBase[PXA_ICIP] & (1 << i)) {
|
||||
dprintf("arch_arm_fiq: help me, FIQ %d was triggered but no "
|
||||
"FIQ support!\n", i);
|
||||
}
|
||||
}
|
||||
panic("FIQ not implemented yet!");
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
#include <arch/timer.h>
|
||||
#include <arch/cpu.h>
|
||||
|
||||
#include <drivers/bus/FDT.h>
|
||||
#include "soc.h"
|
||||
|
||||
#include "soc_pxa.h"
|
||||
#include "soc_omap3.h"
|
||||
|
||||
//#define TRACE_ARCH_TIMER
|
||||
#ifdef TRACE_ARCH_TIMER
|
||||
@@ -27,99 +32,49 @@
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
static fdt_module_info *sFdtModule;
|
||||
|
||||
#define PXA_TIMERS_PHYS_BASE 0x40A00000
|
||||
#define PXA_TIMERS_SIZE B_PAGE_SIZE
|
||||
#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)
|
||||
|
||||
static area_id sPxaTimersArea = -1;
|
||||
static uint32 *sPxaTimersBase = NULL;
|
||||
static bigtime_t sSystemTime = 0;
|
||||
|
||||
static int32
|
||||
pxa_timer_interrupt(void *data)
|
||||
{
|
||||
if (sPxaTimersBase[PXA_OSSR] & (1 << 4)) {
|
||||
sPxaTimersBase[PXA_OSSR] |= (1 << 4);
|
||||
return timer_interrupt();
|
||||
static struct fdt_device_info intc_table[] = {
|
||||
{
|
||||
.compatible = "marvell,pxa-timers", // XXX not in FDT (also not in upstream!)
|
||||
.init = PXATimer::Init,
|
||||
}, {
|
||||
.compatible = "ti,omap3430-timer",
|
||||
.init = OMAP3Timer::Init,
|
||||
}
|
||||
};
|
||||
static int intc_count = sizeof(intc_table) / sizeof(struct fdt_device_info);
|
||||
|
||||
if (sPxaTimersBase[PXA_OSSR] & (1 << 5)) {
|
||||
sPxaTimersBase[PXA_OSSR] |= (1 << 5);
|
||||
sSystemTime += UINT_MAX + 1ULL;
|
||||
}
|
||||
|
||||
return B_HANDLED_INTERRUPT;
|
||||
}
|
||||
|
||||
void
|
||||
arch_timer_set_hardware_timer(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;
|
||||
}
|
||||
}
|
||||
|
||||
TRACE(("arch_timer_set_hardware_timer(val=%lu, res=%lu)\n", val, res));
|
||||
sPxaTimersBase[PXA_OIER] |= (1 << 4);
|
||||
sPxaTimersBase[PXA_OMCR4] = res;
|
||||
sPxaTimersBase[PXA_OSMR4] = val;
|
||||
sPxaTimersBase[PXA_OSCR4] = 0; // start counting from 0 again
|
||||
HardwareTimer *timer = HardwareTimer::Get();
|
||||
if (timer != NULL)
|
||||
timer->SetTimeout(timeout);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
arch_timer_clear_hardware_timer()
|
||||
{
|
||||
TRACE(("arch_timer_clear_hardware_timer\n"));
|
||||
|
||||
sPxaTimersBase[PXA_OMCR4] = 0; // disable our timer
|
||||
sPxaTimersBase[PXA_OIER] &= ~(1 << 4);
|
||||
HardwareTimer *timer = HardwareTimer::Get();
|
||||
if (timer != NULL)
|
||||
timer->Clear();
|
||||
}
|
||||
|
||||
int
|
||||
arch_init_timer(kernel_args *args)
|
||||
{
|
||||
TRACE(("%s\n", __func__));
|
||||
sPxaTimersArea = map_physical_memory("pxa_timers", PXA_TIMERS_PHYS_BASE,
|
||||
PXA_TIMERS_SIZE, 0, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void**)&sPxaTimersBase);
|
||||
|
||||
if (sPxaTimersArea < 0)
|
||||
return sPxaTimersArea;
|
||||
status_t rc = get_module(B_FDT_MODULE_NAME, (module_info**)&sFdtModule);
|
||||
if (rc != B_OK)
|
||||
panic("Unable to get FDT module: %08lx!\n", rc);
|
||||
|
||||
sPxaTimersBase[PXA_OIER] |= (1 << 5); // enable timekeeping timer
|
||||
sPxaTimersBase[PXA_OMCR5] = PXA_RES_US | (1 << 7);
|
||||
sPxaTimersBase[PXA_OSMR5] = UINT_MAX;
|
||||
sPxaTimersBase[PXA_OSCR5] = 0;
|
||||
|
||||
install_io_interrupt_handler(PXA_TIMERS_INTERRUPT, &pxa_timer_interrupt, NULL, 0);
|
||||
rc = sFdtModule->setup_devices(intc_table, intc_count, NULL);
|
||||
if (rc != B_OK)
|
||||
panic("No interrupt controllers found!\n");
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@@ -127,10 +82,9 @@ arch_init_timer(kernel_args *args)
|
||||
bigtime_t
|
||||
system_time(void)
|
||||
{
|
||||
if (sPxaTimersArea < 0)
|
||||
return 0;
|
||||
HardwareTimer *timer = HardwareTimer::Get();
|
||||
if (timer != NULL)
|
||||
return timer->Time();
|
||||
|
||||
return (sPxaTimersBase != NULL) ?
|
||||
sSystemTime + sPxaTimersBase[PXA_OSCR5] :
|
||||
0ULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#include "soc.h"
|
||||
|
||||
InterruptController *InterruptController::sInstance = NULL;
|
||||
HardwareTimer *HardwareTimer::sInstance = NULL;
|
||||
@@ -0,0 +1,68 @@
|
||||
#ifndef ARCH_ARM_SOC_H
|
||||
#define ARCH_ARM_SOC_H
|
||||
|
||||
class InterruptController;
|
||||
|
||||
#include <drivers/bus/FDT.h>
|
||||
#include <private/kernel/int.h>
|
||||
#include <private/kernel/timer.h>
|
||||
|
||||
// ------------------------------------------------------ InterruptController
|
||||
|
||||
class InterruptController {
|
||||
public:
|
||||
virtual void EnableInterrupt(int irq) = 0;
|
||||
virtual void DisableInterrupt(int irq) = 0;
|
||||
|
||||
virtual void HandleInterrupt() = 0;
|
||||
|
||||
static InterruptController* Get() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
protected:
|
||||
InterruptController(fdt_module_info *fdtModule, fdt_device_node node)
|
||||
: fFDT(fdtModule), fNode(node) {
|
||||
if (sInstance) {
|
||||
panic("Multiple InterruptController objects created; that is currently unsupported!");
|
||||
}
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
// Keep our node around as we might want to grab attributes from it
|
||||
fdt_module_info *fFDT;
|
||||
fdt_device_node fNode;
|
||||
|
||||
static InterruptController *sInstance;
|
||||
};
|
||||
|
||||
|
||||
// ------------------------------------------------------ HardwareTimer
|
||||
|
||||
class HardwareTimer {
|
||||
public:
|
||||
virtual void SetTimeout(bigtime_t timeout) = 0;
|
||||
virtual bigtime_t Time() = 0;
|
||||
virtual void Clear() = 0;
|
||||
|
||||
static HardwareTimer* Get() {
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
protected:
|
||||
HardwareTimer(fdt_module_info *fdtModule, fdt_device_node node)
|
||||
: fFDT(fdtModule), fNode(node) {
|
||||
if (sInstance) {
|
||||
panic("Multiple HardwareTimer objects created; that is currently unsupported!");
|
||||
}
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
// Keep our node around as we might want to grab attributes from it
|
||||
fdt_module_info *fFDT;
|
||||
fdt_device_node fNode;
|
||||
|
||||
static HardwareTimer *sInstance;
|
||||
};
|
||||
|
||||
#endif // ARCH_ARM_SOC_H
|
||||
@@ -0,0 +1,204 @@
|
||||
#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(int irq)
|
||||
{
|
||||
uint32 bit = irq % 32, bank = irq / 32;
|
||||
fRegBase[INTCPS_MIR_CLEARn + (8 * bank)] = 1 << bit;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
OMAP3InterruptController::DisableInterrupt(int 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) {
|
||||
int_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 %ld.%ld)\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(fdt_module_info *fdt, fdt_device_node node)
|
||||
: InterruptController(fdt, node),
|
||||
fNumPending(3)
|
||||
{
|
||||
fRegArea = fFDT->map_reg_range(node, 0, (void**)&fRegBase);
|
||||
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(fdt_module_info *fdtModule, fdt_device_node node)
|
||||
: HardwareTimer(fdtModule, node),
|
||||
fSystemTime(0)
|
||||
{
|
||||
fRegArea = fFDT->map_reg_range(node, 0, (void**)&fRegBase);
|
||||
if (fRegArea < 0)
|
||||
panic("Cannot map OMAP3Timer registers!");
|
||||
|
||||
fInterrupt = fFDT->get_interrupt(node, 0);
|
||||
if (fInterrupt < 0)
|
||||
panic("Cannot get OMAP3Timer interrupt!");
|
||||
|
||||
uint32 rev = fRegBase[TIDR];
|
||||
dprintf("OMAP: Found timer @ 0x%p, IRQ %d (rev %ld.%ld)\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);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef ARCH_ARM_SOC_OMAP3_H
|
||||
#define ARCH_ARM_SOC_OMAP3_H
|
||||
|
||||
class OMAP3InterruptController;
|
||||
|
||||
#include "soc.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
|
||||
class OMAP3InterruptController : public InterruptController {
|
||||
public:
|
||||
void EnableInterrupt(int irq);
|
||||
void DisableInterrupt(int irq);
|
||||
void HandleInterrupt();
|
||||
|
||||
static status_t Init(fdt_module_info *fdt, fdt_device_node node, void *cookie) {
|
||||
InterruptController *ic = new(std::nothrow) OMAP3InterruptController(fdt, node);
|
||||
// XXX implement InitCheck() functionality
|
||||
return ic != NULL ? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
protected:
|
||||
OMAP3InterruptController(fdt_module_info *fdt, fdt_device_node node);
|
||||
|
||||
void SoftReset();
|
||||
|
||||
area_id fRegArea;
|
||||
uint32 *fRegBase;
|
||||
uint32 fNumPending;
|
||||
};
|
||||
|
||||
class OMAP3Timer : public HardwareTimer {
|
||||
public:
|
||||
void SetTimeout(bigtime_t timeout);
|
||||
bigtime_t Time();
|
||||
void Clear();
|
||||
|
||||
static status_t Init(fdt_module_info *fdt, fdt_device_node node, void *cookie) {
|
||||
if (sInstance == NULL) {
|
||||
OMAP3Timer *timer = new(std::nothrow) OMAP3Timer(fdt, node);
|
||||
// XXX implement InitCheck() functionality
|
||||
return timer != NULL ? B_OK : B_NO_MEMORY;
|
||||
} else {
|
||||
// XXX We have multiple timers; just create the first one
|
||||
// and ignore the rest
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
OMAP3Timer(fdt_module_info *fdtModule, fdt_device_node node);
|
||||
|
||||
static int32 _InterruptWrapper(void *data);
|
||||
int32 HandleInterrupt();
|
||||
|
||||
bigtime_t fSystemTime;
|
||||
area_id fRegArea;
|
||||
uint32 *fRegBase;
|
||||
int fInterrupt;
|
||||
};
|
||||
|
||||
#endif /* ARCH_ARM_SOC_OMAP3_H */
|
||||
@@ -0,0 +1,154 @@
|
||||
#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(int irq)
|
||||
{
|
||||
if (irq <= 31) {
|
||||
fRegBase[PXA_ICMR] |= 1 << irq;
|
||||
return;
|
||||
}
|
||||
|
||||
fRegBase[PXA_ICMR2] |= 1 << (irq - 32);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PXAInterruptController::DisableInterrupt(int 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))
|
||||
int_io_interrupt_handler(i, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PXAInterruptController::PXAInterruptController(fdt_module_info *fdt, fdt_device_node node)
|
||||
: InterruptController(fdt, node) {
|
||||
fRegArea = fFDT->map_reg_range(node, 0, (void**)&fRegBase);
|
||||
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=%lu, res=%lu)\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(fdt_module_info *fdt, fdt_device_node node)
|
||||
: HardwareTimer(fdt, node)
|
||||
{
|
||||
fRegArea = fFDT->map_reg_range(node, 0, (void**)&fRegBase);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef ARCH_ARM_SOC_PXA_H
|
||||
#define ARCH_ARM_SOC_PXA_H
|
||||
|
||||
class PXAInterruptController;
|
||||
|
||||
#include "soc.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
|
||||
class PXAInterruptController : public InterruptController {
|
||||
public:
|
||||
void EnableInterrupt(int irq);
|
||||
void DisableInterrupt(int irq);
|
||||
void HandleInterrupt();
|
||||
|
||||
static status_t Init(fdt_module_info *fdt, fdt_device_node node, void *cookie) {
|
||||
InterruptController *ic = new(std::nothrow) PXAInterruptController(fdt, node);
|
||||
// XXX implement InitCheck() functionality
|
||||
return ic != NULL ? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
|
||||
protected:
|
||||
PXAInterruptController(fdt_module_info *fdt, fdt_device_node node);
|
||||
|
||||
area_id fRegArea;
|
||||
uint32 *fRegBase;
|
||||
};
|
||||
|
||||
|
||||
class PXATimer : public HardwareTimer {
|
||||
public:
|
||||
void SetTimeout(bigtime_t timeout);
|
||||
void Clear();
|
||||
bigtime_t Time();
|
||||
|
||||
static status_t Init(fdt_module_info *fdt, fdt_device_node node, void *cookie) {
|
||||
PXATimer *timer = new(std::nothrow) PXATimer(fdt, node);
|
||||
// XXX implement InitCheck() functionality
|
||||
return timer != NULL ? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
|
||||
protected:
|
||||
PXATimer(fdt_module_info *fdt, fdt_device_node node);
|
||||
|
||||
area_id fRegArea;
|
||||
uint32 *fRegBase;
|
||||
bigtime_t fSystemTime;
|
||||
private:
|
||||
static int32 _InterruptWrapper(void *data);
|
||||
int32 HandleInterrupt();
|
||||
};
|
||||
|
||||
|
||||
#endif // ARCH_ARM_SOC_PXA_H
|
||||
Reference in New Issue
Block a user