/* * linux/arch/arm/mach-np5/time.c * * Copyright (C) 2008 Neotion * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "time.h" static unsigned long timer_period; static void set_np5_timer(unsigned long delay) { //Set the static delay timer_period = delay; //First stop the timer WRITE_REG(IO_ADDRESS(TMR0_CTRL_REG), TMR_STOP); //Configure in autoreload mode with a prescaler of 256 #ifdef CONFIG_NP5_TIMER_AUTORELOAD WRITE_REG(IO_ADDRESS(TMR0_SETTINGS_REG), TMR_AUTORELOAD | TMR_PRESCALER_256); #else WRITE_REG(IO_ADDRESS(TMR0_SETTINGS_REG), TMR_PRESCALER_256); #endif //Configure period of timer WRITE_REG(IO_ADDRESS(TMR0_PERIOD_REG), delay); //Clear pending interrupt WRITE_REG(IO_ADDRESS(TMR0_IRQ_CLEAR_REG), TMR_IRQ_CLEAR); //Enable timer interrupt WRITE_REG(IO_ADDRESS(TMR0_IRQ_CTRL_REG), TMR_IRQ_ENABLE); //Start the timer WRITE_REG(IO_ADDRESS(TMR0_CTRL_REG), TMR_START); } #ifdef CONFIG_IPIPE static unsigned long timer_lxlost; static unsigned long long __ipipe_mach_tsc; int __ipipe_mach_timerint = TMR0_IRQ; EXPORT_SYMBOL(__ipipe_mach_timerint); int __ipipe_mach_timerstolen = 0; EXPORT_SYMBOL(__ipipe_mach_timerstolen); unsigned __ipipe_mach_ticks_per_jiffy = NP5_PERIOD; EXPORT_SYMBOL(__ipipe_mach_ticks_per_jiffy); /* IPIPE timer lock */ static IPIPE_DEFINE_SPINLOCK(timer_lock); static inline unsigned long np5_getticksoffset(void) { return (timer_period - (READ_REG(IO_ADDRESS(TMR0_TMR_REG)) & 0xFFFF)); } /* Acknoledge the hardware timer interrupt at hardware timer level. */ void __ipipe_mach_acktimer(void) { WRITE_REG(IO_ADDRESS(TMR0_IRQ_CLEAR_REG), TMR_IRQ_CLEAR); } /* High resolution counter, or its emulation using the hardware decrementer or free-running counter */ notrace unsigned long long __ipipe_mach_get_tsc(void) { unsigned long long result; unsigned long flags; local_irq_save_hw_notrace(flags); spin_lock(&timer_lock); result = __ipipe_mach_tsc + np5_getticksoffset(); spin_unlock(&timer_lock); local_irq_restore_hw_notrace(flags); return result; } EXPORT_SYMBOL(__ipipe_mach_get_tsc); /* Fills a structure which will be used in user-space to emulate the tsc.*/ void __ipipe_mach_get_tscinfo(struct __ipipe_tscinfo *info) { info->type = IPIPE_TSC_TYPE_NONE; } /* Program the hardware timer to trig an interrupt in 'delay' hardware timer ticks. */ void __ipipe_mach_set_dec(unsigned long delay) { unsigned long ticks; unsigned long flags; //spin_lock_irqsave(&timer_lock, flags); local_irq_save_hw(flags); ticks = np5_getticksoffset(); __ipipe_mach_tsc += ticks; timer_lxlost += ticks; set_np5_timer(delay); local_irq_restore_hw(flags); //spin_unlock_irqrestore(&timer_lock, flags); } EXPORT_SYMBOL(__ipipe_mach_set_dec); /* Called when Xenomai stops handling the hardware timer. */ void __ipipe_mach_release_timer(void) { __ipipe_mach_set_dec(__ipipe_mach_ticks_per_jiffy); } EXPORT_SYMBOL(__ipipe_mach_release_timer); /* Returns the count of hardware timer ticks remaining before the next timer interrupt. */ unsigned long __ipipe_mach_get_dec(void) { return READ_REG(IO_ADDRESS(TMR0_TMR_REG)) & 0xFFFF; } #endif static irqreturn_t np5_timer_interrupt(int irq, void *dev_id) { write_seqlock(&xtime_lock); #ifndef CONFIG_IPIPE //Clear pending interrupt WRITE_REG(IO_ADDRESS(TMR0_IRQ_CLEAR_REG), TMR_IRQ_CLEAR); #else timer_lxlost = 0; if (!__ipipe_mach_timerstolen) __ipipe_mach_tsc += np5_getticksoffset(); #endif #ifndef CONFIG_NP5_TIMER_AUTORELOAD //Write back delay WRITE_REG(IO_ADDRESS(TMR0_PERIOD_REG), timer_period); //Start the timer WRITE_REG(IO_ADDRESS(TMR0_CTRL_REG), TMR_START); #endif timer_tick(); write_sequnlock(&xtime_lock); return IRQ_HANDLED; } static struct irqaction np5_timer_irq = { .name = "NP5 Timer Tick", .flags = IRQF_DISABLED | IRQF_TIMER, .handler = np5_timer_interrupt, }; void __init np5_timer_init(void) { printk("np5: Timer 0 Init. : Freq %d Prescaler %d Period %d HZ %d\n", NP5_FREQ, NP5_PRESCALER, NP5_PERIOD, HZ); //Set timer period set_np5_timer(NP5_PERIOD); // Configure IRQ Handler setup_irq(TMR0_IRQ, &np5_timer_irq); } unsigned long np5_gettimeoffset(void) { unsigned int value; unsigned long elapsed, usec; value = READ_REG(IO_ADDRESS(TMR0_TMR_REG)); value &= 0xFFFF; //Select only 16bits elapsed = timer_period - value; #ifdef CONFIG_IPIPE elapsed += timer_lxlost; #endif usec = (unsigned long)((elapsed * (1000000/HZ)) / NP5_PERIOD); return usec; }