/* * Copyright (c) 2005 Jakub Jermar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup kernel_ia64 * @{ */ /** @file */ /** Interval Timer driver. */ #include <arch/drivers/it.h> #include <arch/interrupt.h> #include <arch/register.h> #include <arch/asm.h> #include <barrier.h> #include <time/clock.h> #include <ddi/irq.h> #include <arch.h> #define IT_SERVICE_CLOCKS 64 #define FREQ_NUMERATOR_SHIFT 32 #define FREQ_NUMERATOR_MASK 0xffffffff00000000ULL #define FREQ_DENOMINATOR_SHIFT 0 #define FREQ_DENOMINATOR_MASK 0xffffffffULL uint64_t it_delta; static irq_t it_irq; static irq_ownership_t it_claim(irq_t *); static void it_interrupt(irq_t *); /** Initialize Interval Timer. */ void it_init(void) { if (config.cpu_active == 1) { irq_initialize(&it_irq); it_irq.inr = INTERRUPT_TIMER; it_irq.claim = it_claim; it_irq.handler = it_interrupt; irq_register(&it_irq); uint64_t base_freq; base_freq = ((bootinfo->freq_scale) & FREQ_NUMERATOR_MASK) >> FREQ_NUMERATOR_SHIFT; base_freq *= bootinfo->sys_freq; base_freq /= ((bootinfo->freq_scale) & FREQ_DENOMINATOR_MASK) >> FREQ_DENOMINATOR_SHIFT; it_delta = base_freq / HZ; } /* Initialize Interval Timer external interrupt vector */ cr_itv_t itv; itv.value = itv_read(); itv.vector = INTERRUPT_TIMER; itv.m = 0; itv_write(itv.value); /* Set Interval Timer Counter to zero */ itc_write(0); /* Generate first Interval Timer interrupt in IT_DELTA ticks */ itm_write(IT_DELTA); /* Propagate changes */ srlz_d(); } /** Always claim ownership for this IRQ. * * Other devices are responsible to avoid using INR 0. * * @return Always IRQ_ACCEPT. * */ irq_ownership_t it_claim(irq_t *irq) { return IRQ_ACCEPT; } /** Process Interval Timer interrupt. */ void it_interrupt(irq_t *irq) { eoi_write(EOI); int64_t itm = itm_read(); while (true) { int64_t itc = itc_read(); itc += IT_SERVICE_CLOCKS; itm += IT_DELTA; if (itm - itc < 0) CPU_LOCAL->missed_clock_ticks++; else break; } itm_write(itm); srlz_d(); /* Propagate changes */ /* * We are holding a lock which prevents preemption. * Release the lock, call clock() and reacquire the lock again. */ irq_spinlock_unlock(&irq->lock, false); clock(); irq_spinlock_lock(&irq->lock, false); } /** @} */