1/* 2 * arch/arm/mach-pxa/time.c 3 * 4 * Author: Nicolas Pitre 5 * Created: Jun 15, 2001 6 * Copyright: MontaVista Software Inc. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13#include <linux/kernel.h> 14#include <linux/init.h> 15#include <linux/delay.h> 16#include <linux/interrupt.h> 17#include <linux/time.h> 18#include <linux/signal.h> 19#include <linux/errno.h> 20#include <linux/sched.h> 21#include <linux/clocksource.h> 22 23#include <asm/system.h> 24#include <asm/hardware.h> 25#include <asm/io.h> 26#include <asm/leds.h> 27#include <asm/irq.h> 28#include <asm/mach/irq.h> 29#include <asm/mach/time.h> 30#include <asm/arch/pxa-regs.h> 31 32 33static inline unsigned long pxa_get_rtc_time(void) 34{ 35 return RCNR; 36} 37 38static int pxa_set_rtc(void) 39{ 40 unsigned long current_time = xtime.tv_sec; 41 42 if (RTSR & RTSR_ALE) { 43 /* make sure not to forward the clock over an alarm */ 44 unsigned long alarm = RTAR; 45 if (current_time >= alarm && alarm >= RCNR) 46 return -ERESTARTSYS; 47 } 48 RCNR = current_time; 49 return 0; 50} 51 52#ifdef CONFIG_NO_IDLE_HZ 53static unsigned long initial_match; 54static int match_posponed; 55#endif 56 57static irqreturn_t 58pxa_timer_interrupt(int irq, void *dev_id) 59{ 60 int next_match; 61 62 write_seqlock(&xtime_lock); 63 64#ifdef CONFIG_NO_IDLE_HZ 65 if (match_posponed) { 66 match_posponed = 0; 67 OSMR0 = initial_match; 68 } 69#endif 70 71 do { 72 timer_tick(); 73 OSSR = OSSR_M0; /* Clear match on timer 0 */ 74 next_match = (OSMR0 += LATCH); 75 } while( (signed long)(next_match - OSCR) <= 8 ); 76 77 write_sequnlock(&xtime_lock); 78 79 return IRQ_HANDLED; 80} 81 82static struct irqaction pxa_timer_irq = { 83 .name = "PXA Timer Tick", 84 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 85 .handler = pxa_timer_interrupt, 86}; 87 88static cycle_t pxa_get_cycles(void) 89{ 90 return OSCR; 91} 92 93static struct clocksource clocksource_pxa = { 94 .name = "pxa_timer", 95 .rating = 200, 96 .read = pxa_get_cycles, 97 .mask = CLOCKSOURCE_MASK(32), 98 .shift = 20, 99 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 100}; 101 102static void __init pxa_timer_init(void) 103{ 104 struct timespec tv; 105 unsigned long flags; 106 107 set_rtc = pxa_set_rtc; 108 109 tv.tv_nsec = 0; 110 tv.tv_sec = pxa_get_rtc_time(); 111 do_settimeofday(&tv); 112 113 OIER = 0; /* disable any timer interrupts */ 114 OSSR = 0xf; /* clear status on all timers */ 115 setup_irq(IRQ_OST0, &pxa_timer_irq); 116 local_irq_save(flags); 117 OIER = OIER_E0; /* enable match on timer 0 to cause interrupts */ 118 OSMR0 = OSCR + LATCH; /* set initial match */ 119 local_irq_restore(flags); 120 121 /* 122 * OSCR runs continuously on PXA and is not written to, 123 * so we can use it as clock source directly. 124 */ 125 clocksource_pxa.mult = 126 clocksource_hz2mult(CLOCK_TICK_RATE, clocksource_pxa.shift); 127 clocksource_register(&clocksource_pxa); 128} 129 130#ifdef CONFIG_NO_IDLE_HZ 131static int pxa_dyn_tick_enable_disable(void) 132{ 133 /* nothing to do */ 134 return 0; 135} 136 137static void pxa_dyn_tick_reprogram(unsigned long ticks) 138{ 139 if (ticks > 1) { 140 initial_match = OSMR0; 141 OSMR0 = initial_match + ticks * LATCH; 142 match_posponed = 1; 143 } 144} 145 146static irqreturn_t 147pxa_dyn_tick_handler(int irq, void *dev_id) 148{ 149 if (match_posponed) { 150 match_posponed = 0; 151 OSMR0 = initial_match; 152 if ( (signed long)(initial_match - OSCR) <= 8 ) 153 return pxa_timer_interrupt(irq, dev_id); 154 } 155 return IRQ_NONE; 156} 157 158static struct dyn_tick_timer pxa_dyn_tick = { 159 .enable = pxa_dyn_tick_enable_disable, 160 .disable = pxa_dyn_tick_enable_disable, 161 .reprogram = pxa_dyn_tick_reprogram, 162 .handler = pxa_dyn_tick_handler, 163}; 164#endif 165 166#ifdef CONFIG_PM 167static unsigned long osmr[4], oier; 168 169static void pxa_timer_suspend(void) 170{ 171 osmr[0] = OSMR0; 172 osmr[1] = OSMR1; 173 osmr[2] = OSMR2; 174 osmr[3] = OSMR3; 175 oier = OIER; 176} 177 178static void pxa_timer_resume(void) 179{ 180 OSMR0 = osmr[0]; 181 OSMR1 = osmr[1]; 182 OSMR2 = osmr[2]; 183 OSMR3 = osmr[3]; 184 OIER = oier; 185 186 /* 187 * OSMR0 is the system timer: make sure OSCR is sufficiently behind 188 */ 189 OSCR = OSMR0 - LATCH; 190} 191#else 192#define pxa_timer_suspend NULL 193#define pxa_timer_resume NULL 194#endif 195 196struct sys_timer pxa_timer = { 197 .init = pxa_timer_init, 198 .suspend = pxa_timer_suspend, 199 .resume = pxa_timer_resume, 200#ifdef CONFIG_NO_IDLE_HZ 201 .dyn_tick = &pxa_dyn_tick, 202#endif 203}; 204