1/* 2 * arch/blackfin/kernel/time.c 3 * 4 * This file contains the Blackfin-specific time handling details. 5 * Most of the stuff is located in the machine specific files. 6 * 7 * Copyright 2004-2008 Analog Devices Inc. 8 * Licensed under the GPL-2 or later. 9 */ 10 11#include <linux/module.h> 12#include <linux/profile.h> 13#include <linux/interrupt.h> 14#include <linux/time.h> 15#include <linux/irq.h> 16#include <linux/delay.h> 17#include <linux/sched.h> 18 19#include <asm/blackfin.h> 20#include <asm/time.h> 21#include <asm/gptimers.h> 22 23/* This is an NTP setting */ 24#define TICK_SIZE (tick_nsec / 1000) 25 26static struct irqaction bfin_timer_irq = { 27 .name = "Blackfin Timer Tick", 28 .flags = IRQF_DISABLED 29}; 30 31#if defined(CONFIG_IPIPE) 32void __init setup_system_timer0(void) 33{ 34 /* Power down the core timer, just to play safe. */ 35 bfin_write_TCNTL(0); 36 37 disable_gptimers(TIMER0bit); 38 set_gptimer_status(0, TIMER_STATUS_TRUN0); 39 while (get_gptimer_status(0) & TIMER_STATUS_TRUN0) 40 udelay(10); 41 42 set_gptimer_config(0, 0x59); /* IRQ enable, periodic, PWM_OUT, SCLKed, OUT PAD disabled */ 43 set_gptimer_period(TIMER0_id, get_sclk() / HZ); 44 set_gptimer_pwidth(TIMER0_id, 1); 45 SSYNC(); 46 enable_gptimers(TIMER0bit); 47} 48#else 49void __init setup_core_timer(void) 50{ 51 u32 tcount; 52 53 /* power up the timer, but don't enable it just yet */ 54 bfin_write_TCNTL(TMPWR); 55 CSYNC(); 56 57 /* the TSCALE prescaler counter */ 58 bfin_write_TSCALE(TIME_SCALE - 1); 59 60 tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1); 61 bfin_write_TPERIOD(tcount); 62 bfin_write_TCOUNT(tcount); 63 64 /* now enable the timer */ 65 CSYNC(); 66 67 bfin_write_TCNTL(TAUTORLD | TMREN | TMPWR); 68} 69#endif 70 71static void __init 72time_sched_init(irqreturn_t(*timer_routine) (int, void *)) 73{ 74#if defined(CONFIG_IPIPE) 75 setup_system_timer0(); 76 bfin_timer_irq.handler = timer_routine; 77 setup_irq(IRQ_TIMER0, &bfin_timer_irq); 78#else 79 setup_core_timer(); 80 bfin_timer_irq.handler = timer_routine; 81 setup_irq(IRQ_CORETMR, &bfin_timer_irq); 82#endif 83} 84 85#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET 86/* 87 * Should return useconds since last timer tick 88 */ 89u32 arch_gettimeoffset(void) 90{ 91 unsigned long offset; 92 unsigned long clocks_per_jiffy; 93 94#if defined(CONFIG_IPIPE) 95 clocks_per_jiffy = bfin_read_TIMER0_PERIOD(); 96 offset = bfin_read_TIMER0_COUNTER() / \ 97 (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); 98 99 if ((get_gptimer_status(0) & TIMER_STATUS_TIMIL0) && offset < (100000 / HZ / 2)) 100 offset += (USEC_PER_SEC / HZ); 101#else 102 clocks_per_jiffy = bfin_read_TPERIOD(); 103 offset = (clocks_per_jiffy - bfin_read_TCOUNT()) / \ 104 (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); 105 106 /* Check if we just wrapped the counters and maybe missed a tick */ 107 if ((bfin_read_ILAT() & (1 << IRQ_CORETMR)) 108 && (offset < (100000 / HZ / 2))) 109 offset += (USEC_PER_SEC / HZ); 110#endif 111 return offset; 112} 113#endif 114 115/* 116 * timer_interrupt() needs to keep up the real-time clock, 117 * as well as call the "xtime_update()" routine every clocktick 118 */ 119#ifdef CONFIG_CORE_TIMER_IRQ_L1 120__attribute__((l1_text)) 121#endif 122irqreturn_t timer_interrupt(int irq, void *dummy) 123{ 124 xtime_update(1); 125 126#ifdef CONFIG_IPIPE 127 update_root_process_times(get_irq_regs()); 128#else 129 update_process_times(user_mode(get_irq_regs())); 130#endif 131 profile_tick(CPU_PROFILING); 132 133 return IRQ_HANDLED; 134} 135 136void read_persistent_clock(struct timespec *ts) 137{ 138 time_t secs_since_1970 = (365 * 37 + 9) * 24 * 60 * 60; /* 1 Jan 2007 */ 139 ts->tv_sec = secs_since_1970; 140 ts->tv_nsec = 0; 141} 142 143void __init time_init(void) 144{ 145#ifdef CONFIG_RTC_DRV_BFIN 146 /* [#2663] hack to filter junk RTC values that would cause 147 * userspace to have to deal with time values greater than 148 * 2^31 seconds (which uClibc cannot cope with yet) 149 */ 150 if ((bfin_read_RTC_STAT() & 0xC0000000) == 0xC0000000) { 151 printk(KERN_NOTICE "bfin-rtc: invalid date; resetting\n"); 152 bfin_write_RTC_STAT(0); 153 } 154#endif 155 156 time_sched_init(timer_interrupt); 157} 158