]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /***************************************************************************/ |
2 | ||
3 | /* | |
4 | * timers.c -- generic ColdFire hardware timer support. | |
5 | * | |
deb77c85 | 6 | * Copyright (C) 1999-2006, Greg Ungerer (gerg@snapgear.com) |
1da177e4 LT |
7 | */ |
8 | ||
9 | /***************************************************************************/ | |
10 | ||
1da177e4 LT |
11 | #include <linux/kernel.h> |
12 | #include <linux/sched.h> | |
13 | #include <linux/param.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/init.h> | |
0b7ac8e4 | 16 | #include <asm/io.h> |
1da177e4 LT |
17 | #include <asm/irq.h> |
18 | #include <asm/traps.h> | |
19 | #include <asm/machdep.h> | |
20 | #include <asm/coldfire.h> | |
21 | #include <asm/mcftimer.h> | |
22 | #include <asm/mcfsim.h> | |
23 | ||
24 | /***************************************************************************/ | |
25 | ||
0b7ac8e4 GU |
26 | /* |
27 | * By default use timer1 as the system clock timer. | |
28 | */ | |
29 | #define TA(a) (MCF_MBAR + MCFTIMER_BASE1 + (a)) | |
30 | ||
1da177e4 LT |
31 | /* |
32 | * Default the timer and vector to use for ColdFire. Some ColdFire | |
33 | * CPU's and some boards may want different. Their sub-architecture | |
34 | * startup code (in config.c) can change these if they want. | |
35 | */ | |
36 | unsigned int mcf_timervector = 29; | |
37 | unsigned int mcf_profilevector = 31; | |
38 | unsigned int mcf_timerlevel = 5; | |
39 | ||
1da177e4 LT |
40 | /* |
41 | * These provide the underlying interrupt vector support. | |
42 | * Unfortunately it is a little different on each ColdFire. | |
43 | */ | |
44 | extern void mcf_settimericr(int timer, int level); | |
45 | extern int mcf_timerirqpending(int timer); | |
46 | ||
deb77c85 GU |
47 | #if defined(CONFIG_M532x) |
48 | #define __raw_readtrr __raw_readl | |
49 | #define __raw_writetrr __raw_writel | |
50 | #else | |
51 | #define __raw_readtrr __raw_readw | |
52 | #define __raw_writetrr __raw_writew | |
53 | #endif | |
54 | ||
1da177e4 LT |
55 | /***************************************************************************/ |
56 | ||
57 | void coldfire_tick(void) | |
58 | { | |
59 | /* Reset the ColdFire timer */ | |
0b7ac8e4 | 60 | __raw_writeb(MCFTIMER_TER_CAP | MCFTIMER_TER_REF, TA(MCFTIMER_TER)); |
1da177e4 LT |
61 | } |
62 | ||
63 | /***************************************************************************/ | |
64 | ||
4342f4ac GU |
65 | static int ticks_per_intr; |
66 | ||
c051b011 | 67 | void coldfire_timer_init(irq_handler_t handler) |
1da177e4 | 68 | { |
0b7ac8e4 | 69 | __raw_writew(MCFTIMER_TMR_DISABLE, TA(MCFTIMER_TMR)); |
4342f4ac GU |
70 | ticks_per_intr = (MCF_BUSCLK / 16) / HZ; |
71 | __raw_writetrr(ticks_per_intr - 1, TA(MCFTIMER_TRR)); | |
0b7ac8e4 GU |
72 | __raw_writew(MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK16 | |
73 | MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE, TA(MCFTIMER_TMR)); | |
1da177e4 | 74 | |
f6f23887 | 75 | request_irq(mcf_timervector, handler, IRQF_DISABLED, "timer", NULL); |
1da177e4 LT |
76 | mcf_settimericr(1, mcf_timerlevel); |
77 | ||
78 | #ifdef CONFIG_HIGHPROFILE | |
79 | coldfire_profile_init(); | |
80 | #endif | |
81 | } | |
82 | ||
83 | /***************************************************************************/ | |
84 | ||
85 | unsigned long coldfire_timer_offset(void) | |
86 | { | |
4342f4ac | 87 | unsigned long tcn, offset; |
1da177e4 | 88 | |
0b7ac8e4 | 89 | tcn = __raw_readw(TA(MCFTIMER_TCN)); |
4342f4ac | 90 | offset = ((tcn + 1) * (1000000 / HZ)) / ticks_per_intr; |
1da177e4 LT |
91 | |
92 | /* Check if we just wrapped the counters and maybe missed a tick */ | |
93 | if ((offset < (1000000 / HZ / 2)) && mcf_timerirqpending(1)) | |
94 | offset += 1000000 / HZ; | |
95 | return offset; | |
96 | } | |
97 | ||
98 | /***************************************************************************/ | |
99 | #ifdef CONFIG_HIGHPROFILE | |
100 | /***************************************************************************/ | |
101 | ||
0b7ac8e4 GU |
102 | /* |
103 | * By default use timer2 as the profiler clock timer. | |
104 | */ | |
105 | #define PA(a) (MCF_MBAR + MCFTIMER_BASE2 + (a)) | |
106 | ||
1da177e4 LT |
107 | /* |
108 | * Choose a reasonably fast profile timer. Make it an odd value to | |
d08df601 | 109 | * try and get good coverage of kernel operations. |
1da177e4 LT |
110 | */ |
111 | #define PROFILEHZ 1013 | |
112 | ||
1da177e4 LT |
113 | /* |
114 | * Use the other timer to provide high accuracy profiling info. | |
115 | */ | |
c051b011 | 116 | irqreturn_t coldfire_profile_tick(int irq, void *dummy) |
1da177e4 LT |
117 | { |
118 | /* Reset ColdFire timer2 */ | |
0b7ac8e4 | 119 | __raw_writeb(MCFTIMER_TER_CAP | MCFTIMER_TER_REF, PA(MCFTIMER_TER)); |
1da177e4 LT |
120 | if (current->pid) |
121 | profile_tick(CPU_PROFILING, regs); | |
c051b011 | 122 | return IRQ_HANDLED; |
1da177e4 LT |
123 | } |
124 | ||
125 | /***************************************************************************/ | |
126 | ||
127 | void coldfire_profile_init(void) | |
128 | { | |
129 | printk(KERN_INFO "PROFILE: lodging TIMER2 @ %dHz as profile timer\n", PROFILEHZ); | |
130 | ||
131 | /* Set up TIMER 2 as high speed profile clock */ | |
0b7ac8e4 | 132 | __raw_writew(MCFTIMER_TMR_DISABLE, PA(MCFTIMER_TMR)); |
1da177e4 | 133 | |
deb77c85 | 134 | __raw_writetrr(((MCF_CLK / 16) / PROFILEHZ), PA(MCFTIMER_TRR)); |
0b7ac8e4 GU |
135 | __raw_writew(MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK16 | |
136 | MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE, PA(MCFTIMER_TMR)); | |
1da177e4 LT |
137 | |
138 | request_irq(mcf_profilevector, coldfire_profile_tick, | |
f6f23887 | 139 | (IRQF_DISABLED | IRQ_FLG_FAST), "profile timer", NULL); |
1da177e4 LT |
140 | mcf_settimericr(2, 7); |
141 | } | |
142 | ||
143 | /***************************************************************************/ | |
144 | #endif /* CONFIG_HIGHPROFILE */ | |
145 | /***************************************************************************/ |