#include "../../kernel.h"
+#include "../../clock.h"
+
#define CLOCK_ACK_BIT 0x80 /* PS/2 clock interrupt acknowledge bit */
/* Clock parameters. */
#define SQUARE_WAVE 0x36 /* ccaammmb, a = access, m = mode, b = BCD */
/* 11x11, 11 = LSB then MSB, x11 = sq wave */
#define TIMER_FREQ 1193182 /* clock frequency for timer in PC and AT */
-#define TIMER_COUNT (TIMER_FREQ/system_hz) /* initial value for counter*/
+#define TIMER_COUNT(freq) (TIMER_FREQ/(freq)) /* initial value for counter*/
+
+PRIVATE irq_hook_t pic_timer_hook; /* interrupt handler hook */
/*===========================================================================*
- * arch_init_clock *
+ * init_8235A_timer *
*===========================================================================*/
-PUBLIC int arch_init_clock(void)
+PUBLIC int init_8253A_timer(unsigned freq)
{
/* Initialize channel 0 of the 8253A timer to, e.g., 60 Hz,
* and register the CLOCK task's interrupt handler to be run
* on every clock tick.
*/
outb(TIMER_MODE, SQUARE_WAVE); /* run continuously */
- outb(TIMER0, (TIMER_COUNT & 0xff)); /* timer low byte */
- outb(TIMER0, TIMER_COUNT >> 8); /* timer high byte */
+ outb(TIMER0, (TIMER_COUNT(freq) & 0xff)); /* timer low byte */
+ outb(TIMER0, TIMER_COUNT(freq) >> 8); /* timer high byte */
return OK;
}
/*===========================================================================*
- * clock_stop *
+ * stop_8235A_timer *
*===========================================================================*/
-PUBLIC void clock_stop(void)
+PUBLIC void stop_8253A_timer(void)
{
/* Reset the clock to the BIOS rate. (For rebooting.) */
outb(TIMER_MODE, 0x36);
}
/*===========================================================================*
- * read_clock *
+ * read_8235A_timer *
*===========================================================================*/
-PUBLIC clock_t read_clock(void)
+PUBLIC clock_t read_8253A_timer(void)
{
/* Read the counter of channel 0 of the 8253A timer. This counter
* counts down at a rate of TIMER_FREQ and restarts at
return count;
}
+PUBLIC int arch_init_local_timer(unsigned freq)
+{
+ init_8253A_timer(freq);
+
+ return 0;
+}
+
+PUBLIC void arch_stop_local_timer(void)
+{
+ stop_8253A_timer();
+}
+
+PUBLIC int arch_register_local_timer_handler(irq_handler_t handler)
+{
+ /* Using PIC, Initialize the CLOCK's interrupt hook. */
+ pic_timer_hook.proc_nr_e = NONE;
+
+ put_irq_handler(&pic_timer_hook, CLOCK_IRQ, handler);
+ put_irq_handler(&pic_timer_hook, CLOCK_IRQ,
+ (irq_handler_t)bsp_timer_int_handler);
+
+ return 0;
+}
* Sep 30, 2004 source code documentation updated (Jorrit N. Herder)
* Sep 24, 2004 redesigned alarm timers (Jorrit N. Herder)
*
- * The function do_clocktick() is triggered by the clock's interrupt
- * handler when a watchdog timer has expired or a process must be scheduled.
+ * Clock task is notified by the clock's interrupt handler when a timer
+ * has expired.
*
* In addition to the main clock_task() entry point, which starts the main
* loop, there are several other minor entry points:
* read_clock: read the counter of channel 0 of the 8253A timer
*
* (+) The CLOCK task keeps tracks of watchdog timers for the entire kernel.
- * The watchdog functions of expired timers are executed in do_clocktick().
* It is crucial that watchdog functions not block, or the CLOCK task may
* be blocked. Do not send() a message when the receiver is not expecting it.
* Instead, notify(), which always returns, should be used.
#include <minix/endpoint.h>
#include <minix/portio.h>
+#include "clock.h"
+
/* Function prototype for PRIVATE functions.
*/
FORWARD _PROTOTYPE( void init_clock, (void) );
-FORWARD _PROTOTYPE( int clock_handler, (irq_hook_t *hook) );
-FORWARD _PROTOTYPE( void do_clocktick, (message *m_ptr) );
FORWARD _PROTOTYPE( void load_update, (void));
/* The CLOCK's timers queue. The functions in <timers.h> operate on this.
/* The time is incremented by the interrupt handler on each clock tick.
*/
PRIVATE clock_t realtime = 0; /* real time clock */
-PRIVATE irq_hook_t clock_hook; /* interrupt handler hook */
/*===========================================================================*
* clock_task *
if (is_notify(m.m_type)) {
switch (_ENDPOINT_P(m.m_source)) {
case HARDWARE:
- do_clocktick(&m); /* handle clock tick */
+ tmrs_exptimers(&clock_timers, realtime, NULL);
+ next_timeout = (clock_timers == NULL) ?
+ TMR_NEVER : clock_timers->tmr_exp_time;
break;
default: /* illegal request type */
kprintf("CLOCK: illegal notify %d from %d.\n",
}
}
-/*===========================================================================*
- * do_clocktick *
- *===========================================================================*/
-PRIVATE void do_clocktick(m_ptr)
-message *m_ptr; /* pointer to request message */
-{
- register struct proc *bill_copy = bill_ptr;
-
-/* Despite its name, this routine is not called on every clock tick. It
- * is called on those clock ticks when a lot of work needs to be done.
- */
-
- /* A process used up a full quantum. The interrupt handler stored this
- * process in 'prev_ptr'. First make sure that the process is not on the
- * scheduling queues. Then announce the process ready again. Since it has
- * no more time left, it gets a new quantum and is inserted at the right
- * place in the queues. As a side-effect a new process will be scheduled.
- */
- if (prev_ptr->p_ticks_left <= 0 && priv(prev_ptr)->s_flags & PREEMPTIBLE) {
- if(prev_ptr->p_rts_flags == 0) { /* if it was runnable .. */
- lock;
- {
- dequeue(prev_ptr); /* take it off the queues */
- enqueue(prev_ptr); /* and reinsert it again */
- }
- unlock;
- } else {
- kprintf("CLOCK: %d not runnable; flags: %x\n",
- prev_ptr->p_endpoint, prev_ptr->p_rts_flags);
- }
- }
-
- /* Check if a process-virtual timer expired. Check prev_ptr, but also
- * bill_ptr - one process's user time is another's system time, and the
- * profile timer decreases for both! Do this before the queue operations
- * below, which may alter bill_ptr. Note the use a copy of bill_ptr, because
- * bill_ptr may have been changed above, and this code can't be put higher
- * up because otherwise cause_sig() may dequeue prev_ptr before we do.
- */
- vtimer_check(prev_ptr);
-
- if (prev_ptr != bill_copy)
- vtimer_check(bill_copy);
-
- /* Check if a clock timer expired and run its watchdog function. */
- if (next_timeout <= realtime) {
- tmrs_exptimers(&clock_timers, realtime, NULL);
- next_timeout = (clock_timers == NULL) ?
- TMR_NEVER : clock_timers->tmr_exp_time;
- }
-
- return;
-}
-
/*===========================================================================*
* init_clock *
*===========================================================================*/
PRIVATE void init_clock()
{
- /* First of all init the clock system.
- *
- * Here the (a) clock is set to produce a interrupt at
- * every 1/60 second (ea. 60Hz).
- *
- * Running right away.
- */
- arch_init_clock(); /* architecture-dependent initialization. */
- /* Initialize the CLOCK's interrupt hook. */
- clock_hook.proc_nr_e = CLOCK;
-
- put_irq_handler(&clock_hook, CLOCK_IRQ, clock_handler);
- enable_irq(&clock_hook); /* ready for clock interrupts */
-
- /* Set a watchdog timer to periodically balance the scheduling queues. */
- balance_queues(NULL); /* side-effect sets new timer */
+ /* Set a watchdog timer to periodically balance the scheduling queues.
+ Side-effect sets new timer */
+
+ balance_queues(NULL);
}
-/*===========================================================================*
- * clock_handler *
- *===========================================================================*/
-PRIVATE int clock_handler(hook)
-irq_hook_t *hook;
-{
-/* This executes on each clock tick (i.e., every time the timer chip generates
- * an interrupt). It does a little bit of work so the clock task does not have
- * to be called on every tick. The clock task is called when:
- *
- * (1) the scheduling quantum of the running process has expired, or
- * (2) a timer has expired and the watchdog function should be run.
- *
- * Many global global and static variables are accessed here. The safety of
- * this must be justified. All scheduling and message passing code acquires a
- * lock by temporarily disabling interrupts, so no conflicts with calls from
- * the task level can occur. Furthermore, interrupts are not reentrant, the
- * interrupt handler cannot be bothered by other interrupts.
- *
- * Variables that are updated in the clock's interrupt handler:
- * lost_ticks:
- * Clock ticks counted outside the clock task. This for example
- * is used when the boot monitor processes a real mode interrupt.
- * realtime:
- * The current uptime is incremented with all outstanding ticks.
- * proc_ptr, bill_ptr:
- * These are used for accounting and virtual timers. It does not
- * matter if proc.c is changing them, provided they are always
- * valid pointers, since at worst the previous process would be
- * billed.
+/*
+ * The boot processor timer interrupt handler. In addition to non-boot cpus it
+ * keeps real time and notifies the clock task if need be
*/
- register unsigned ticks;
- register int expired;
-
- if(minix_panicing) return;
-
- /* Get number of ticks and update realtime. */
- ticks = lost_ticks + 1;
- lost_ticks = 0;
- realtime += ticks;
-
- /* Update user and system accounting times. Charge the current process for
- * user time. If the current process is not billable, that is, if a non-user
- * process is running, charge the billable process for system time as well.
- * Thus the unbillable process' user time is the billable user's system time.
- */
-
- proc_ptr->p_user_time += ticks;
- if (priv(proc_ptr)->s_flags & PREEMPTIBLE) {
- proc_ptr->p_ticks_left -= ticks;
- }
- if (! (priv(proc_ptr)->s_flags & BILLABLE)) {
- bill_ptr->p_sys_time += ticks;
- bill_ptr->p_ticks_left -= ticks;
- }
+PUBLIC int bsp_timer_int_handler(void)
+{
+ unsigned ticks;
+
+ if(minix_panicing)
+ return 0;
+
+ /* Get number of ticks and update realtime. */
+ ticks = lost_ticks + 1;
+ lost_ticks = 0;
+ realtime += ticks;
+
+ ap_timer_int_handler();
- /* Decrement virtual timers, if applicable. We decrement both the virtual
- * and the profile timer of the current process, and if the current process
- * is not billable, the timer of the billed process as well.
- * If any of the timers expire, do_clocktick() will send out signals.
- */
- expired = 0;
- if ((proc_ptr->p_misc_flags & MF_VIRT_TIMER) &&
- (proc_ptr->p_virt_left -= ticks) <= 0) expired = 1;
- if ((proc_ptr->p_misc_flags & MF_PROF_TIMER) &&
- (proc_ptr->p_prof_left -= ticks) <= 0) expired = 1;
- if (! (priv(proc_ptr)->s_flags & BILLABLE) &&
- (bill_ptr->p_misc_flags & MF_PROF_TIMER) &&
- (bill_ptr->p_prof_left -= ticks) <= 0) expired = 1;
-
- /* Update load average. */
- load_update();
-
- /* Check if do_clocktick() must be called. Done for alarms and scheduling.
- * Some processes, such as the kernel tasks, cannot be preempted.
- */
- if ((next_timeout <= realtime) || (proc_ptr->p_ticks_left <= 0) || expired) {
- prev_ptr = proc_ptr; /* store running process */
- mini_notify(proc_addr(HARDWARE), CLOCK); /* send notification */
- }
-
- if (do_serial_debug)
- do_ser_debug();
-
- return(1); /* reenable interrupts */
+ /* if a timer expired, notify the clock task */
+ if ((next_timeout <= realtime)) {
+ mini_notify(proc_addr(HARDWARE), CLOCK); /* send notification */
+ }
+
+ if (do_serial_debug)
+ do_ser_debug();
+
+ return(1); /* reenable interrupts */
}
/*===========================================================================*
kloadinfo.last_clock = realtime;
}
+/*
+ * Timer interupt handler. This is the only think executed on non boot
+ * processors. It is called by bsp_timer_int_handler() on the boot processor
+ */
+PUBLIC int ap_timer_int_handler(void)
+{
+
+ /* Update user and system accounting times. Charge the current process
+ * for user time. If the current process is not billable, that is, if a
+ * non-user process is running, charge the billable process for system
+ * time as well. Thus the unbillable process' user time is the billable
+ * user's system time.
+ */
+
+ unsigned ticks = 1;
+ int expired = 0;
+ struct proc * p, * billp;
+
+ /* Update user and system accounting times. Charge the current process
+ * for user time. If the current process is not billable, that is, if a
+ * non-user process is running, charge the billable process for system
+ * time as well. Thus the unbillable process' user time is the billable
+ * user's system time.
+ */
+
+ /* FIXME prepared for get_cpu_local_var() */
+ p = proc_ptr;
+ billp = bill_ptr;
+
+ p->p_user_time += ticks;
+ if (priv(p)->s_flags & PREEMPTIBLE) {
+ p->p_ticks_left -= ticks;
+ }
+ if (! (priv(p)->s_flags & BILLABLE)) {
+ billp->p_sys_time += ticks;
+ billp->p_ticks_left -= ticks;
+ }
+
+ /* Decrement virtual timers, if applicable. We decrement both the
+ * virtual and the profile timer of the current process, and if the
+ * current process is not billable, the timer of the billed process as
+ * well. If any of the timers expire, do_clocktick() will send out
+ * signals.
+ */
+ if ((p->p_misc_flags & MF_VIRT_TIMER) &&
+ (p->p_virt_left -= ticks) <= 0) expired = 1;
+ if ((p->p_misc_flags & MF_PROF_TIMER) &&
+ (p->p_prof_left -= ticks) <= 0) expired = 1;
+ if (! (priv(p)->s_flags & BILLABLE) &&
+ (billp->p_misc_flags & MF_PROF_TIMER) &&
+ (billp->p_prof_left -= ticks) <= 0) expired = 1;
+
+ /*
+ * Check if a process-virtual timer expired. Check current process, but
+ * also bill_ptr - one process's user time is another's system time, and
+ * the profile timer decreases for both!
+ */
+ vtimer_check(p);
+
+ if (p != billp)
+ vtimer_check(billp);
+
+ /* Update load average. */
+ load_update();
+
+ /* check if the process is still runnable after checking the vtimer */
+ if (p->p_rts_flags == 0 && p->p_ticks_left <= 0 &&
+ priv(p)->s_flags & PREEMPTIBLE) {
+ proc_ptr = NULL; /* no process is scheduled for dequeue and
+ enqueue */
+ dequeue(p); /* take it off the queues */
+ enqueue(p); /* and reinsert it again */
+ proc_ptr = p; /* restore some consitent state */
+ }
+
+ return 1;
+}
+
+PUBLIC int boot_cpu_init_timer(unsigned freq)
+{
+ if (arch_init_local_timer(freq))
+ return -1;
+
+ if (arch_register_local_timer_handler(
+ (irq_handler_t) bsp_timer_int_handler))
+ return -1;
+
+ return 0;
+}