1/* 2 * arch/arm/mach-ks8695/time.c 3 * 4 * Copyright (C) 2006 Ben Dooks <ben@simtec.co.uk> 5 * Copyright (C) 2006 Simtec Electronics 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 22#include <linux/init.h> 23#include <linux/interrupt.h> 24#include <linux/irq.h> 25#include <linux/kernel.h> 26#include <linux/sched.h> 27 28#include <asm/io.h> 29#include <asm/mach/time.h> 30 31#include <asm/arch/regs-timer.h> 32#include <asm/arch/regs-irq.h> 33 34#include "generic.h" 35 36/* 37 * Returns number of ms since last clock interrupt. Note that interrupts 38 * will have been disabled by do_gettimeoffset() 39 */ 40static unsigned long ks8695_gettimeoffset (void) 41{ 42 unsigned long elapsed, tick2, intpending; 43 44 /* 45 * Get the current number of ticks. Note that there is a race 46 * condition between us reading the timer and checking for an 47 * interrupt. We solve this by ensuring that the counter has not 48 * reloaded between our two reads. 49 */ 50 elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD); 51 do { 52 tick2 = elapsed; 53 intpending = __raw_readl(KS8695_IRQ_VA + KS8695_INTST) & (1 << KS8695_IRQ_TIMER1); 54 elapsed = __raw_readl(KS8695_TMR_VA + KS8695_T1TC) + __raw_readl(KS8695_TMR_VA + KS8695_T1PD); 55 } while (elapsed > tick2); 56 57 /* Convert to number of ticks expired (not remaining) */ 58 elapsed = (CLOCK_TICK_RATE / HZ) - elapsed; 59 60 /* Is interrupt pending? If so, then timer has been reloaded already. */ 61 if (intpending) 62 elapsed += (CLOCK_TICK_RATE / HZ); 63 64 /* Convert ticks to usecs */ 65 return (unsigned long)(elapsed * (tick_nsec / 1000)) / LATCH; 66} 67 68/* 69 * IRQ handler for the timer. 70 */ 71static irqreturn_t ks8695_timer_interrupt(int irq, void *dev_id) 72{ 73 write_seqlock(&xtime_lock); 74 timer_tick(); 75 write_sequnlock(&xtime_lock); 76 77 return IRQ_HANDLED; 78} 79 80static struct irqaction ks8695_timer_irq = { 81 .name = "ks8695_tick", 82 .flags = IRQF_DISABLED | IRQF_TIMER, 83 .handler = ks8695_timer_interrupt, 84}; 85 86static void ks8695_timer_setup(void) 87{ 88 unsigned long tmout = CLOCK_TICK_RATE / HZ; 89 unsigned long tmcon; 90 91 /* disable timer1 */ 92 tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON); 93 __raw_writel(tmcon & ~TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); 94 95 __raw_writel(tmout / 2, KS8695_TMR_VA + KS8695_T1TC); 96 __raw_writel(tmout / 2, KS8695_TMR_VA + KS8695_T1PD); 97 98 /* re-enable timer1 */ 99 __raw_writel(tmcon | TMCON_T1EN, KS8695_TMR_VA + KS8695_TMCON); 100} 101 102static void __init ks8695_timer_init (void) 103{ 104 ks8695_timer_setup(); 105 106 /* Enable timer interrupts */ 107 setup_irq(KS8695_IRQ_TIMER1, &ks8695_timer_irq); 108} 109 110struct sys_timer ks8695_timer = { 111 .init = ks8695_timer_init, 112 .offset = ks8695_gettimeoffset, 113 .resume = ks8695_timer_setup, 114}; 115