mirror of https://github.com/dzavalishin/oskit/
196 lines
4.1 KiB
C
Executable File
196 lines
4.1 KiB
C
Executable File
/*
|
|
* Copyright (c) 1996-2001 University of Utah and the Flux Group.
|
|
* All rights reserved.
|
|
*
|
|
* This file is part of the Flux OSKit. The OSKit is free software, also known
|
|
* as "open source;" you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License (GPL), version 2, as published by the Free
|
|
* Software Foundation (FSF). To explore alternate licensing terms, contact
|
|
* the University of Utah at csl-dist@cs.utah.edu or +1-801-585-3271.
|
|
*
|
|
* The OSKit is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GPL for more details. You should have
|
|
* received a copy of the GPL along with the OSKit; see the file COPYING. If
|
|
* not, write to the FSF, 59 Temple Place #330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
/*
|
|
* Timer interrupt support.
|
|
*/
|
|
|
|
#include <oskit/dev/dev.h>
|
|
#include <oskit/debug.h>
|
|
#include <oskit/arm32/pio.h>
|
|
#include <oskit/arm32/proc_reg.h>
|
|
#include "pit_param.h"
|
|
|
|
#define NTIMERS 10
|
|
|
|
|
|
/* Prototypes for helper functions in another file */
|
|
int osenv_timer_pit_init(int freq, void (*timer_intr)());
|
|
void osenv_timer_pit_shutdown();
|
|
int osenv_timer_pit_read();
|
|
|
|
|
|
struct timer_handler {
|
|
void (*func)(void);
|
|
struct timer_handler *next;
|
|
};
|
|
|
|
static struct timer_handler *timer_head, *timer_free;
|
|
static struct timer_handler timer_data[NTIMERS];
|
|
|
|
/*
|
|
* Generic timer interrupt handler.
|
|
*/
|
|
static void
|
|
timer_intr()
|
|
{
|
|
struct timer_handler *th;
|
|
|
|
for (th = timer_head; th; th = th->next)
|
|
(*th->func)();
|
|
}
|
|
|
|
/*
|
|
* Initialize timers.
|
|
*/
|
|
void
|
|
osenv_timer_init()
|
|
{
|
|
int i;
|
|
#ifndef KNIT
|
|
static int initialized = 0; /* guard against multiple invocations */
|
|
|
|
if (initialized)
|
|
return;
|
|
|
|
initialized = 1;
|
|
#endif
|
|
|
|
for (i = 0; i < NTIMERS; i++)
|
|
timer_data[i].next = &timer_data[i + 1];
|
|
timer_data[NTIMERS - 1].next = 0;
|
|
timer_free = &timer_data[0];
|
|
|
|
/*
|
|
* Install interrupt handler at TIMER_FREQ Hz.
|
|
*/
|
|
oskit_timer_rtc_init(TIMER_FREQ, timer_intr);
|
|
}
|
|
|
|
/*
|
|
* Turn all timers off.
|
|
*/
|
|
void
|
|
osenv_timer_shutdown()
|
|
{
|
|
osenv_log(OSENV_LOG_DEBUG, "Shutting down timer\n");
|
|
oskit_timer_rtc_shutdown();
|
|
}
|
|
|
|
/*
|
|
* Register a timer handler to be called at the specified frequency.
|
|
*/
|
|
void
|
|
osenv_timer_register(void (*func)(void), int freq)
|
|
{
|
|
struct timer_handler *th;
|
|
unsigned int flags;
|
|
|
|
#ifndef KNIT
|
|
/* XXX */
|
|
osenv_timer_init();
|
|
#endif
|
|
|
|
if (timer_free == 0)
|
|
osenv_panic("%s:%d: ran out of entries", __FILE__, __LINE__);
|
|
/*
|
|
* XXX
|
|
*/
|
|
if (freq != TIMER_FREQ)
|
|
osenv_panic("%s:%d: bad freq", __FILE__, __LINE__);
|
|
if (func == 0)
|
|
osenv_panic("%s:%d: null function", __FILE__, __LINE__);
|
|
|
|
flags = osenv_intr_save_disable();
|
|
|
|
th = timer_free;
|
|
timer_free = th->next;
|
|
th->next = timer_head;
|
|
timer_head = th;
|
|
th->func = func;
|
|
|
|
if (flags)
|
|
osenv_intr_enable();
|
|
}
|
|
|
|
|
|
/*
|
|
* Frequency and function address uniquely identify which to remove since
|
|
* equal values are equivelent.
|
|
*/
|
|
void
|
|
osenv_timer_unregister(void (*func)(void), int freq)
|
|
{
|
|
int flags;
|
|
struct timer_handler *th, **prev;
|
|
|
|
if (freq != TIMER_FREQ)
|
|
osenv_panic("%s:%d: bad freq", __FILE__, __LINE__);
|
|
|
|
/* dissable interrupts while mucking with the queue */
|
|
flags = osenv_intr_save_disable();
|
|
|
|
/* move the timer from the run-queue to the unused-queue */
|
|
prev = &timer_head;
|
|
th = timer_head;
|
|
|
|
while (th && (th->func != func)) {
|
|
prev = &th->next;
|
|
th = th->next;
|
|
}
|
|
if (!th) {
|
|
osenv_log(OSENV_LOG_WARNING, "Timer handler not found\n");
|
|
} else {
|
|
/* have the active list skip over it */
|
|
*prev = th->next;
|
|
|
|
/* place in inactive queue */
|
|
th->next = timer_free;
|
|
timer_free = th;
|
|
}
|
|
|
|
if (flags)
|
|
osenv_intr_enable();
|
|
}
|
|
|
|
|
|
/*
|
|
* Spin for a small amount of time, specified in nanoseconds,
|
|
* without blocking or enabling interrupts.
|
|
*/
|
|
void
|
|
osenv_timer_spin(long nanosec)
|
|
{
|
|
int prev_val = osenv_timer_pit_read();
|
|
|
|
while (nanosec > 0) {
|
|
int val = osenv_timer_pit_read();
|
|
if (val > prev_val)
|
|
prev_val += TIMER_VALUE;
|
|
nanosec -= (prev_val - val) * PIT_NS;
|
|
prev_val = val;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return the resolution in nanoseconds of the spin timer.
|
|
*/
|
|
long
|
|
osenv_timer_spin_resolution(void)
|
|
{
|
|
return PIT_NS;
|
|
}
|