534 lines
13 KiB
C
534 lines
13 KiB
C
/*
|
|
* Copyright (C) 2009-2013 Apple Inc. All rights reserved.
|
|
*
|
|
* This document is the property of Apple Inc.
|
|
* It is considered confidential and proprietary.
|
|
*
|
|
* This document may not be reproduced or transmitted in any form,
|
|
* in whole or in part, without the express written permission of
|
|
* Apple Inc.
|
|
*/
|
|
#include <arch.h>
|
|
#include <arch/arm/arm.h>
|
|
#include <debug.h>
|
|
#include <drivers/aic/aic.h>
|
|
#include <platform.h>
|
|
#include <platform/int.h>
|
|
#include <platform/timer.h>
|
|
#include <platform/soc/hwclocks.h>
|
|
#include <platform/soc/hwisr.h>
|
|
#include <platform/soc/hwregbase.h>
|
|
#include <platform/soc/pmgr.h>
|
|
#include <sys/callout.h>
|
|
|
|
#if WITH_AIC_TIMERS
|
|
static void timer_deadline(void *arg);
|
|
static void (* timer_deadline_func)(void);
|
|
#endif
|
|
|
|
#if WITH_AIC_INTERRUPTS || WITH_AIC_TIMERS
|
|
|
|
static inline uint32_t _aic_read_reg(volatile uint32_t *reg)
|
|
{
|
|
uint32_t val;
|
|
#if PLATFORM_VARIANT_IOP || AIC_VERSION > 0
|
|
val = *reg;
|
|
#else
|
|
/* An exclusive load is required for the CPU ID to be passed along */
|
|
__asm__ volatile( "ldrex %0, [%1]" : "=r" (val) : "r" (reg));
|
|
#endif // PLATFORM_VARIANT_IOP || AIC_VERSION > 0
|
|
return val;
|
|
}
|
|
|
|
static inline void _aic_write_reg(volatile uint32_t *reg, uint32_t val)
|
|
{
|
|
*reg = val;
|
|
}
|
|
|
|
static void _aic_init()
|
|
{
|
|
#if AIC_VERSION > 0
|
|
rAIC_GLB_CFG |= (AIC_GLBCFG_SYNC_ACG | AIC_GLBCFG_EIR_ACG | AIC_GLBCFG_REG_ACG);
|
|
|
|
/* Clear the timeout values before setting them to avoid set bits from the default values */
|
|
rAIC_GLB_CFG &= ~(AIC_GLBCFG_SEWT(AIC_GLBCFG_WT_MASK) | AIC_GLBCFG_AEWT(AIC_GLBCFG_WT_MASK));
|
|
rAIC_GLB_CFG &= ~(AIC_GLBCFG_SIWT(AIC_GLBCFG_WT_MASK) | AIC_GLBCFG_AIWT(AIC_GLBCFG_WT_MASK));
|
|
rAIC_GLB_CFG |= (AIC_GLBCFG_SEWT(AIC_GLBCFG_WT_64MICRO) | AIC_GLBCFG_AEWT(AIC_GLBCFG_WT_64MICRO));
|
|
rAIC_GLB_CFG |= (AIC_GLBCFG_SIWT(AIC_GLBCFG_WT_64MICRO) | AIC_GLBCFG_AIWT(AIC_GLBCFG_WT_64MICRO));
|
|
#else
|
|
rAIC_GLB_CFG |= AIC_GLBCFG_ACG;
|
|
|
|
/* Clear the timeout values before setting them to avoid set bits from the default values */
|
|
rAIC_GLB_CFG &= ~(AIC_GLBCFG_EWT(AIC_GLBCFG_WT_MASK) | AIC_GLBCFG_IWT(AIC_GLBCFG_WT_MASK));
|
|
rAIC_GLB_CFG |= (AIC_GLBCFG_EWT(AIC_GLBCFG_WT_64MICRO) | AIC_GLBCFG_IWT(AIC_GLBCFG_WT_64MICRO));
|
|
#endif // AIC_VERSION > 0
|
|
|
|
// Enable Interrupts
|
|
rAIC_GLB_CFG |= AIC_GLBCFG_IEN;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if WITH_AIC_INTERRUPTS
|
|
|
|
/*
|
|
* Interrupt support
|
|
*/
|
|
|
|
extern void __arm_irq(void);
|
|
|
|
static struct interrupt_entry handlers[kAIC_NUM_INTS];
|
|
|
|
#if AIC_VERSION < 1
|
|
static uint32_t fiq_source = kAIC_INT_SPURIOUS;
|
|
#endif // AIC_VERSION < 1
|
|
|
|
static void _aic_mask_timer_int(bool mask)
|
|
{
|
|
#if AIC_VERSION < 1
|
|
if (mask)
|
|
_aic_write_reg(&rAIC_TMR_ST_SET, AIC_TMRST_TIM);
|
|
else
|
|
_aic_write_reg(&rAIC_TMR_ST_CLR, AIC_TMRST_TIM);
|
|
#endif // AIC_VERSION < 1
|
|
}
|
|
|
|
static void _aic_mask_int_internal(uint32_t vector)
|
|
{
|
|
if (vector == kAIC_VEC_IPI) {
|
|
/* Not maskable */
|
|
} else if (vector == kAIC_VEC_TMR) {
|
|
_aic_mask_timer_int(true);
|
|
} else if (vector == kAIC_VEC_SW_TMR) {
|
|
/* Not maskable */
|
|
} else {
|
|
uint32_t reg = AIC_SRC_TO_EIR(vector);
|
|
rAIC_EIR_MASK_SET(reg) = AIC_SRC_TO_MASK(vector);
|
|
}
|
|
}
|
|
|
|
static void _aic_unmask_int_internal(uint32_t vector)
|
|
{
|
|
if (vector == kAIC_VEC_IPI) {
|
|
/* Not maskable */
|
|
} else if (vector == kAIC_VEC_TMR) {
|
|
_aic_mask_timer_int(false);
|
|
} else {
|
|
/* if AIC client requested interrupt state is not masked, unmask it */
|
|
if (!handlers[vector].int_mask_requested) {
|
|
uint32_t reg = AIC_SRC_TO_EIR(vector);
|
|
rAIC_EIR_MASK_CLR(reg) = AIC_SRC_TO_MASK(vector);
|
|
}
|
|
}
|
|
}
|
|
|
|
int interrupt_init(void)
|
|
{
|
|
_aic_init();
|
|
|
|
#if !PLATFORM_VARIANT_IOP
|
|
/* mask everything */
|
|
interrupt_mask_all();
|
|
#endif
|
|
|
|
/* also enable it */
|
|
exit_critical_section();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void interrupt_mask_all(void)
|
|
{
|
|
uint32_t eir;
|
|
|
|
/* mask everything */
|
|
for (eir = 0; eir < kAIC_NUM_EIRS; eir++) {
|
|
rAIC_EIR_MASK_SET(eir) = 0xffffffff;
|
|
}
|
|
/* XXX timers? */
|
|
}
|
|
|
|
void mask_int(uint32_t vector)
|
|
{
|
|
/* save AIC client requested interrupt mask state */
|
|
handlers[vector].int_mask_requested = true;
|
|
_aic_mask_int_internal(vector);
|
|
}
|
|
|
|
void unmask_int(uint32_t vector)
|
|
{
|
|
/* clear AIC client requested interrupt mask state */
|
|
handlers[vector].int_mask_requested = false;
|
|
_aic_unmask_int_internal(vector);
|
|
}
|
|
|
|
|
|
void set_int_type(uint32_t vector, int type)
|
|
{
|
|
enter_critical_section();
|
|
|
|
#if AIC_VERSION < 1
|
|
uint32_t reg;
|
|
|
|
if (type & INT_TYPE_FIQ) {
|
|
reg = _aic_read_reg(&rAIC_TMR_CFG);
|
|
reg &= ~kAIC_TMRCFG_FSL_MASK;
|
|
if ((reg & (AIC_TMRCFG_IMD|AIC_TMRCFG_EMD|AIC_TMRCFG_SMD)) !=
|
|
(AIC_TMRCFG_IMD|AIC_TMRCFG_EMD|AIC_TMRCFG_SMD))
|
|
panic("Can't support multiple timer FIQ sources\n");
|
|
if (vector == kAIC_VEC_TMR) {
|
|
reg &= ~AIC_TMRCFG_IMD;
|
|
_aic_write_reg(&rAIC_TMR_CFG, reg | AIC_TMRCFG_FSL(kAIC_TMRCFGFSL_PVT));
|
|
fiq_source = kAIC_INT_PVT_TMR;
|
|
arm_enable_fiqs();
|
|
} else if (vector == kAIC_VEC_SW_TMR) {
|
|
reg &= ~AIC_TMRCFG_SMD;
|
|
_aic_write_reg(&rAIC_TMR_CFG, reg | AIC_TMRCFG_FSL(kAIC_TMRCFGFSL_SGT));
|
|
fiq_source = kAIC_INT_SW_TMR;
|
|
arm_enable_fiqs();
|
|
}
|
|
} else {
|
|
if (vector == kAIC_VEC_TMR) {
|
|
_aic_write_reg(&rAIC_TMR_CFG, _aic_read_reg(&rAIC_TMR_CFG) | AIC_TMRCFG_IMD);
|
|
} else if (vector == kAIC_VEC_SW_TMR) {
|
|
_aic_write_reg(&rAIC_TMR_CFG, _aic_read_reg(&rAIC_TMR_CFG) | AIC_TMRCFG_SMD);
|
|
}
|
|
}
|
|
#endif // AIC_VERSION < 1
|
|
|
|
if (type & INT_TYPE_EDGE)
|
|
panic("set_int_type: edge not supported\n");
|
|
|
|
exit_critical_section();
|
|
}
|
|
|
|
int install_int_handler(uint32_t vector, int_handler handler, void *arg)
|
|
{
|
|
ASSERT(vector < kAIC_NUM_INTS);
|
|
|
|
enter_critical_section();
|
|
|
|
handlers[vector].handler = handler;
|
|
handlers[vector].arg = arg;
|
|
|
|
if (vector < kAIC_MAX_EXTID) {
|
|
rAIC_EIR_DEST(vector) = 1 << AIC_CPU_ID;
|
|
}
|
|
|
|
exit_critical_section();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t spurious = 0;
|
|
|
|
#define SPURIOUS_DEBUG 0
|
|
#define PLATFORM_IRQ_SLICES ((PLATFORM_IRQ_COUNT + 31) / 32)
|
|
|
|
void platform_irq(void)
|
|
{
|
|
#if SPURIOUS_DEBUG
|
|
uint32_t unmasked[PLATFORM_IRQ_SLICES];
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < PLATFORM_IRQ_SLICES; i++) {
|
|
unmasked[i] = rAIC_EIR_INT_RO(i) & ~rAIC_EIR_MASK_CLR(i);
|
|
}
|
|
#endif
|
|
uint32_t vector = _aic_read_reg(&rAIC_IACK);
|
|
uint32_t source;
|
|
|
|
if (vector == kAIC_INT_SPURIOUS) {
|
|
/* Do some extra checking to be sure IACK is behaving
|
|
* as expected. We don't really expect spurious
|
|
* interrupts in this environment, and certainly want
|
|
* to see a non-spurious IACK any time a source is
|
|
* active. */
|
|
#if SPURIOUS_DEBUG
|
|
for (i = 0; i < PLATFORM_IRQ_SLICES; i++) {
|
|
if (unmasked[i])
|
|
panic("IACK=0 but sources are pending\n");
|
|
}
|
|
printf("spurious\n");
|
|
#endif
|
|
spurious++;
|
|
return;
|
|
}
|
|
while (vector != kAIC_INT_SPURIOUS) {
|
|
if (AIC_INT_EXT(vector)) {
|
|
source = AIC_INT_EXTID(vector);
|
|
} else if (AIC_INT_IPI(vector)) {
|
|
panic("Unexpected IPI\n");
|
|
#if AIC_VERSION < 1
|
|
} else if (vector == kAIC_INT_PVT_TMR) {
|
|
source = kAIC_VEC_TMR;
|
|
} else if (vector == kAIC_INT_SW_TMR) {
|
|
_aic_write_reg(&rAIC_TMR_ST_CLR, AIC_TMRST_SGT);
|
|
source = kAIC_VEC_SW_TMR;
|
|
#endif // AIC_VERSION < 1
|
|
} else {
|
|
panic("Unrecognized IRQ vector: %x\n", vector);
|
|
}
|
|
if (handlers[source].handler)
|
|
handlers[source].handler(handlers[source].arg);
|
|
_aic_unmask_int_internal(source);
|
|
vector = _aic_read_reg(&rAIC_IACK);
|
|
}
|
|
}
|
|
|
|
#if AIC_VERSION < 1
|
|
|
|
void platform_fiq(void)
|
|
{
|
|
uint32_t source;
|
|
|
|
if (fiq_source == kAIC_INT_SW_TMR) {
|
|
_aic_write_reg(&rAIC_TMR_ST_CLR, AIC_TMRST_SGT);
|
|
source = kAIC_VEC_SW_TMR;
|
|
} else if (fiq_source == kAIC_INT_PVT_TMR) {
|
|
source = kAIC_VEC_TMR;
|
|
} else
|
|
panic("Unexpected FIQ\n");
|
|
if (handlers[source].handler)
|
|
handlers[source].handler(handlers[source].arg);
|
|
_aic_unmask_int_internal(source);
|
|
}
|
|
|
|
#endif // AIC_VERSION < 1
|
|
|
|
#if AIC_VERSION == 2
|
|
|
|
void platform_fiq(void)
|
|
{
|
|
/* clear FIQ and disable decrementer */
|
|
_aic_write_reg(&rAIC_TMR_CFG, ~AIC_TMRCFG_EN);
|
|
_aic_write_reg(&rAIC_TMR_ISR, AIC_TMRISR_PCT);
|
|
|
|
/* if we have a callback, invoke it now */
|
|
if (timer_deadline_func) {
|
|
timer_deadline_func();
|
|
}
|
|
}
|
|
|
|
#endif // AIC_VERSION == 2
|
|
|
|
#if PLATFORM_VARIANT_IOP
|
|
|
|
void interrupt_generate_ipc(uint32_t vector)
|
|
{
|
|
uint32_t reg = AIC_SRC_TO_EIR(vector);
|
|
rAIC_EIR_SW_SET(reg) = AIC_SRC_TO_MASK(vector);
|
|
}
|
|
|
|
void interrupt_clear_ipc(uint32_t vector)
|
|
{
|
|
uint32_t reg = AIC_SRC_TO_EIR(vector);
|
|
rAIC_EIR_SW_CLR(reg) = AIC_SRC_TO_MASK(vector);
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if WITH_AIC_TIMERS
|
|
/*
|
|
* Timer support; currently assumes that the banked register block works
|
|
* (XXX no it doesn't - see top of file)
|
|
*/
|
|
|
|
#define DECR_MAX_COUNT (0xffffffffULL)
|
|
|
|
int timer_init(uint32_t timer)
|
|
{
|
|
/* Allow only banked access to CPU's own timer */
|
|
if (timer != 0) return -1;
|
|
|
|
/* Default to disabled and IRQ for all modes */
|
|
_aic_write_reg(&rAIC_TMR_CFG, AIC_TMRCFG_IMD|AIC_TMRCFG_EMD|AIC_TMRCFG_SMD);
|
|
|
|
/* install our interrupt handler */
|
|
install_int_handler(kAIC_VEC_TMR, (int_handler)&timer_deadline, NULL);
|
|
unmask_int(kAIC_VEC_TMR);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void timer_stop_all(void)
|
|
{
|
|
/* Even though it says "all", only stop the dedicated timer */
|
|
_aic_write_reg(&rAIC_TMR_CFG, _aic_read_reg(&rAIC_TMR_CFG) & ~AIC_TMRCFG_EN);
|
|
}
|
|
|
|
void timer_deadline_enter(uint64_t deadline, void (* func)(void))
|
|
{
|
|
uint64_t ticks;
|
|
uint32_t decr;
|
|
|
|
timer_deadline_func = func;
|
|
if (func) {
|
|
// printf("installing deadline %p\n", func);
|
|
|
|
/* convert absolute deadline to relative time */
|
|
ticks = timer_get_ticks();
|
|
if (deadline < ticks) {
|
|
// printf(" %llu too soon (it is now %llu)\n", deadline, ticks);
|
|
#if AIC_VERSION == 2
|
|
deadline = 1;
|
|
#else // AIC_VERSION == 2
|
|
deadline = 0;
|
|
#endif
|
|
} else {
|
|
deadline -= ticks;
|
|
}
|
|
|
|
/* clamp the deadline to our maximum, which is about 178 seconds */
|
|
decr = (deadline > DECR_MAX_COUNT) ? DECR_MAX_COUNT : deadline;
|
|
// printf(" using decrementer count %u\n", decr);
|
|
|
|
/*
|
|
* Reset the decrementer by programming the max count, clearing
|
|
* any pending interrupt status, then programming the desired count.
|
|
*/
|
|
_aic_write_reg(&rAIC_TMR_CNT, DECR_MAX_COUNT);
|
|
_aic_write_reg(&rAIC_TMR_CFG, _aic_read_reg(&rAIC_TMR_CFG) | AIC_TMRCFG_EN);
|
|
_aic_write_reg(&rAIC_TMR_ISR, AIC_TMRISR_PCT);
|
|
_aic_write_reg(&rAIC_TMR_CNT, decr);
|
|
} else {
|
|
/* no deadline, disable the decrementer */
|
|
_aic_write_reg(&rAIC_TMR_CFG, _aic_read_reg(&rAIC_TMR_CFG) & ~AIC_TMRCFG_EN);
|
|
_aic_write_reg(&rAIC_TMR_ISR, _aic_read_reg(&rAIC_TMR_ISR) | AIC_TMRISR_PCT);
|
|
}
|
|
}
|
|
|
|
static void timer_deadline(void *arg)
|
|
{
|
|
/* acknowledge the interrupt and disable the timer */
|
|
_aic_write_reg(&rAIC_TMR_CFG, _aic_read_reg(&rAIC_TMR_CFG) & ~AIC_TMRCFG_EN);
|
|
_aic_write_reg(&rAIC_TMR_ISR, _aic_read_reg(&rAIC_TMR_ISR) | AIC_TMRISR_PCT);
|
|
|
|
/* if we have a callback, invoke it now */
|
|
if (timer_deadline_func)
|
|
timer_deadline_func();
|
|
}
|
|
|
|
#endif // WITH_AIC_TIMERS
|
|
|
|
/*
|
|
* The rest are defined for all targets that include the AIC driver
|
|
*/
|
|
|
|
uint64_t aic_get_ticks(void)
|
|
{
|
|
uint32_t time_lo, time_hi;
|
|
|
|
/* read the timebase */
|
|
do {
|
|
time_hi = rAIC_TIME_HI;
|
|
time_lo = rAIC_TIME_LO;
|
|
} while (time_hi != rAIC_TIME_HI);
|
|
|
|
return ((uint64_t)time_hi << 32) | time_lo;
|
|
}
|
|
|
|
void aic_spin(uint32_t usecs)
|
|
{
|
|
uint32_t delta_time = usecs * (OSC_FREQ / 1000000);
|
|
uint32_t start_time = rAIC_TIME_LO;
|
|
|
|
while ((rAIC_TIME_LO - start_time) < delta_time);
|
|
}
|
|
|
|
#if WITH_AIC_TIMERS
|
|
|
|
#ifdef OSC_FREQ
|
|
|
|
utime_t timer_ticks_to_usecs(uint64_t ticks)
|
|
{
|
|
#if (OSC_FREQ % 1000000) == 0
|
|
return ticks / (OSC_FREQ / 1000000);
|
|
#else
|
|
#error "The code below has overflow issues. Make sure you really want to use it"
|
|
return (ticks * 1000 * 1000) / OSC_FREQ;
|
|
#endif
|
|
}
|
|
|
|
uint64_t timer_usecs_to_ticks(utime_t usecs)
|
|
{
|
|
#if (OSC_FREQ % 1000000) == 0
|
|
return usecs * (OSC_FREQ / 1000000);
|
|
#else
|
|
#error "The code below has overflow issues. Make sure you really want to use it"
|
|
uint64_t timer_scale = ((uint64_t)(OSC_FREQ) << 20) / (1000 * 1000);
|
|
return (ticks * timer_scale) >> 20;
|
|
#endif
|
|
}
|
|
|
|
uint32_t timer_get_tick_rate(void)
|
|
{
|
|
return OSC_FREQ;
|
|
}
|
|
|
|
#endif // OSC_FREQ
|
|
|
|
uint64_t timer_get_ticks(void)
|
|
{
|
|
return aic_get_ticks();
|
|
}
|
|
|
|
#endif // WITH_AIC_TIMERS
|
|
|
|
uint32_t timer_get_entropy(void)
|
|
{
|
|
return arch_get_entropy(&rAIC_TIME_LO);
|
|
}
|
|
|
|
#ifndef PMGR_WDG_VERSION
|
|
#error "PMGR_WDG_VERSION not defined"
|
|
#endif
|
|
|
|
#if PMGR_WDG_VERSION < 1
|
|
void wdt_chip_reset(void)
|
|
{
|
|
rPMGR_WDOG_CTL = 0; // Turn off any pending events
|
|
rPMGR_WDOG_RST = 1; // Set counter for immediate reset
|
|
rPMGR_WDOG_TMR = 0x80000000; // Set counter to a safe value
|
|
rPMGR_WDOG_CTL = 4; // Enable watchdog reset
|
|
rPMGR_WDOG_TMR = 0; // Reset counter to zero
|
|
}
|
|
|
|
void wdt_enable(void)
|
|
{
|
|
rPMGR_WDOG_CTL = 0; // Turn off any pending events
|
|
rPMGR_WDOG_RST = 120*OSC_FREQ; // Set counter for two minute reset
|
|
rPMGR_WDOG_TMR = 0x80000000; // Set counter to a safe value
|
|
rPMGR_WDOG_CTL = 0x0C; // Enable watchdog system reset
|
|
rPMGR_WDOG_TMR = 0; // Reset counter to zero
|
|
}
|
|
#else
|
|
void wdt_chip_reset(void)
|
|
{
|
|
rPMGR_CHIP_WDOG_CTL = 0;
|
|
rPMGR_CHIP_WDOG_RST_CNT = 1;
|
|
rPMGR_CHIP_WDOG_TMR = 0x80000000;
|
|
rPMGR_CHIP_WDOG_CTL = 4;
|
|
rPMGR_CHIP_WDOG_TMR = 0;
|
|
}
|
|
|
|
void wdt_system_reset(void)
|
|
{
|
|
rPMGR_SYS_WDOG_CTL = 0; // Turn off watchdog
|
|
rPMGR_SYS_WDOG_RST_CNT = 1; // Set counter for immediate reset
|
|
rPMGR_SYS_WDOG_TMR = 0x80000000;// Set up-counter to safe value
|
|
rPMGR_SYS_WDOG_CTL = 4; // Turn on interrupt
|
|
rPMGR_SYS_WDOG_TMR = 0; // Reset counter to zero
|
|
}
|
|
|
|
void wdt_enable(void)
|
|
{
|
|
rPMGR_SYS_WDOG_CTL = 0;
|
|
rPMGR_SYS_WDOG_RST_CNT = 120 * OSC_FREQ;
|
|
rPMGR_SYS_WDOG_TMR = 0x80000000;
|
|
rPMGR_SYS_WDOG_CTL = 4;
|
|
}
|
|
#endif
|