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