1/* 2 * arch/arm/mach-ns9xxx/time.c 3 * 4 * Copyright (C) 2006 by Digi International Inc. 5 * All rights reserved. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 */ 11#include <linux/jiffies.h> 12#include <linux/interrupt.h> 13#include <linux/irq.h> 14#include <asm/arch-ns9xxx/regs-sys.h> 15#include <asm/arch-ns9xxx/clock.h> 16#include <asm/arch-ns9xxx/irqs.h> 17#include <asm/arch/system.h> 18#include "generic.h" 19 20#define TIMERCLOCKSELECT 64 21 22static u32 usecs_per_tick; 23 24static irqreturn_t 25ns9xxx_timer_interrupt(int irq, void *dev_id) 26{ 27 write_seqlock(&xtime_lock); 28 timer_tick(); 29 write_sequnlock(&xtime_lock); 30 31 return IRQ_HANDLED; 32} 33 34static unsigned long ns9xxx_timer_gettimeoffset(void) 35{ 36 /* return the microseconds which have passed since the last interrupt 37 * was _serviced_. That is, if an interrupt is pending or the counter 38 * reloads, return one period more. */ 39 40 u32 counter1 = SYS_TR(0); 41 int pending = SYS_ISR & (1 << IRQ_TIMER0); 42 u32 counter2 = SYS_TR(0); 43 u32 elapsed; 44 45 if (pending || counter2 > counter1) 46 elapsed = 2 * SYS_TRC(0) - counter2; 47 else 48 elapsed = SYS_TRC(0) - counter1; 49 50 return (elapsed * usecs_per_tick) >> 16; 51 52} 53 54static struct irqaction ns9xxx_timer_irq = { 55 .name = "NS9xxx Timer Tick", 56 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 57 .handler = ns9xxx_timer_interrupt, 58}; 59 60static void __init ns9xxx_timer_init(void) 61{ 62 int tc; 63 64 usecs_per_tick = 65 SH_DIV(1000000 * TIMERCLOCKSELECT, ns9xxx_cpuclock(), 16); 66 67 /* disable timer */ 68 if ((tc = SYS_TC(0)) & SYS_TCx_TEN) 69 SYS_TC(0) = tc & ~SYS_TCx_TEN; 70 71 SYS_TRC(0) = SH_DIV(ns9xxx_cpuclock(), (TIMERCLOCKSELECT * HZ), 0); 72 73 REGSET(tc, SYS_TCx, TEN, EN); 74 REGSET(tc, SYS_TCx, TLCS, DIV64); /* This must match TIMERCLOCKSELECT */ 75 REGSET(tc, SYS_TCx, INTS, EN); 76 REGSET(tc, SYS_TCx, UDS, DOWN); 77 REGSET(tc, SYS_TCx, TDBG, STOP); 78 REGSET(tc, SYS_TCx, TSZ, 32); 79 REGSET(tc, SYS_TCx, REN, EN); 80 SYS_TC(0) = tc; 81 82 setup_irq(IRQ_TIMER0, &ns9xxx_timer_irq); 83} 84 85struct sys_timer ns9xxx_timer = { 86 .init = ns9xxx_timer_init, 87 .offset = ns9xxx_timer_gettimeoffset, 88}; 89