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 | ||
65 | void coldfire_timer_init(irqreturn_t (*handler)(int, void *, struct pt_regs *)) | |
66 | { | |
0b7ac8e4 | 67 | __raw_writew(MCFTIMER_TMR_DISABLE, TA(MCFTIMER_TMR)); |
deb77c85 | 68 | __raw_writetrr(((MCF_BUSCLK / 16) / HZ), TA(MCFTIMER_TRR)); |
0b7ac8e4 GU |
69 | __raw_writew(MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK16 | |
70 | MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE, TA(MCFTIMER_TMR)); | |
1da177e4 | 71 | |
f6f23887 | 72 | request_irq(mcf_timervector, handler, IRQF_DISABLED, "timer", NULL); |
1da177e4 LT |
73 | mcf_settimericr(1, mcf_timerlevel); |
74 | ||
75 | #ifdef CONFIG_HIGHPROFILE | |
76 | coldfire_profile_init(); | |
77 | #endif | |
78 | } | |
79 | ||
80 | /***************************************************************************/ | |
81 | ||
82 | unsigned long coldfire_timer_offset(void) | |
83 | { | |
84 | unsigned long trr, tcn, offset; | |
85 | ||
0b7ac8e4 | 86 | tcn = __raw_readw(TA(MCFTIMER_TCN)); |
deb77c85 | 87 | trr = __raw_readtrr(TA(MCFTIMER_TRR)); |
1da177e4 LT |
88 | offset = (tcn * (1000000 / HZ)) / trr; |
89 | ||
90 | /* Check if we just wrapped the counters and maybe missed a tick */ | |
91 | if ((offset < (1000000 / HZ / 2)) && mcf_timerirqpending(1)) | |
92 | offset += 1000000 / HZ; | |
93 | return offset; | |
94 | } | |
95 | ||
96 | /***************************************************************************/ | |
97 | #ifdef CONFIG_HIGHPROFILE | |
98 | /***************************************************************************/ | |
99 | ||
0b7ac8e4 GU |
100 | /* |
101 | * By default use timer2 as the profiler clock timer. | |
102 | */ | |
103 | #define PA(a) (MCF_MBAR + MCFTIMER_BASE2 + (a)) | |
104 | ||
1da177e4 LT |
105 | /* |
106 | * Choose a reasonably fast profile timer. Make it an odd value to | |
107 | * try and get good coverage of kernal operations. | |
108 | */ | |
109 | #define PROFILEHZ 1013 | |
110 | ||
1da177e4 LT |
111 | /* |
112 | * Use the other timer to provide high accuracy profiling info. | |
113 | */ | |
1da177e4 LT |
114 | void coldfire_profile_tick(int irq, void *dummy, struct pt_regs *regs) |
115 | { | |
116 | /* Reset ColdFire timer2 */ | |
0b7ac8e4 | 117 | __raw_writeb(MCFTIMER_TER_CAP | MCFTIMER_TER_REF, PA(MCFTIMER_TER)); |
1da177e4 LT |
118 | if (current->pid) |
119 | profile_tick(CPU_PROFILING, regs); | |
120 | } | |
121 | ||
122 | /***************************************************************************/ | |
123 | ||
124 | void coldfire_profile_init(void) | |
125 | { | |
126 | printk(KERN_INFO "PROFILE: lodging TIMER2 @ %dHz as profile timer\n", PROFILEHZ); | |
127 | ||
128 | /* Set up TIMER 2 as high speed profile clock */ | |
0b7ac8e4 | 129 | __raw_writew(MCFTIMER_TMR_DISABLE, PA(MCFTIMER_TMR)); |
1da177e4 | 130 | |
deb77c85 | 131 | __raw_writetrr(((MCF_CLK / 16) / PROFILEHZ), PA(MCFTIMER_TRR)); |
0b7ac8e4 GU |
132 | __raw_writew(MCFTIMER_TMR_ENORI | MCFTIMER_TMR_CLK16 | |
133 | MCFTIMER_TMR_RESTART | MCFTIMER_TMR_ENABLE, PA(MCFTIMER_TMR)); | |
1da177e4 LT |
134 | |
135 | request_irq(mcf_profilevector, coldfire_profile_tick, | |
f6f23887 | 136 | (IRQF_DISABLED | IRQ_FLG_FAST), "profile timer", NULL); |
1da177e4 LT |
137 | mcf_settimericr(2, 7); |
138 | } | |
139 | ||
140 | /***************************************************************************/ | |
141 | #endif /* CONFIG_HIGHPROFILE */ | |
142 | /***************************************************************************/ |