diff options
author | Paul Mundt <lethal@linux-sh.org> | 2009-05-08 03:12:17 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-05-08 03:12:17 -0400 |
commit | add47067a8ca324e9d26c4373350dc3cce7f1e7f (patch) | |
tree | 2ee7062a8e9b42d0452b5ca2e848483366f1f598 /arch/sh/kernel | |
parent | c2ecb4c4a7da16288062a057b693b7b1e16aaf88 (diff) |
sh: Finish the sh64 migration off of ARCH_USES_GETTIMEOFFSET.
This adds sh_tmu support to the SH-5 subtypes, which subsequently allows
us to kill off time_64.c and use the now generic time_32.c. As a bonus,
SH-5 now supports highres timers and tickless for the first time.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/Makefile_64 | 2 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh5/setup-sh5.c | 118 | ||||
-rw-r--r-- | arch/sh/kernel/time_64.c | 264 |
3 files changed, 119 insertions, 265 deletions
diff --git a/arch/sh/kernel/Makefile_64 b/arch/sh/kernel/Makefile_64 index d256c7746957..4667d4cac953 100644 --- a/arch/sh/kernel/Makefile_64 +++ b/arch/sh/kernel/Makefile_64 | |||
@@ -2,7 +2,7 @@ extra-y := head_64.o init_task.o vmlinux.lds | |||
2 | 2 | ||
3 | obj-y := debugtraps.o idle.o io.o io_generic.o irq.o machvec.o process_64.o \ | 3 | obj-y := debugtraps.o idle.o io.o io_generic.o irq.o machvec.o process_64.o \ |
4 | ptrace_64.o setup.o signal_64.o sys_sh.o sys_sh64.o \ | 4 | ptrace_64.o setup.o signal_64.o sys_sh.o sys_sh64.o \ |
5 | syscalls_64.o time_64.o topology.o traps.o traps_64.o | 5 | syscalls_64.o time_32.o topology.o traps.o traps_64.o |
6 | 6 | ||
7 | obj-y += cpu/ timers/ | 7 | obj-y += cpu/ timers/ |
8 | obj-$(CONFIG_VSYSCALL) += vsyscall/ | 8 | obj-$(CONFIG_VSYSCALL) += vsyscall/ |
diff --git a/arch/sh/kernel/cpu/sh5/setup-sh5.c b/arch/sh/kernel/cpu/sh5/setup-sh5.c index 9a362c8f3fe9..678d69bdebba 100644 --- a/arch/sh/kernel/cpu/sh5/setup-sh5.c +++ b/arch/sh/kernel/cpu/sh5/setup-sh5.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/serial_sci.h> | 13 | #include <linux/serial_sci.h> |
14 | #include <linux/io.h> | 14 | #include <linux/io.h> |
15 | #include <linux/mm.h> | 15 | #include <linux/mm.h> |
16 | #include <linux/sh_timer.h> | ||
16 | #include <asm/addrspace.h> | 17 | #include <asm/addrspace.h> |
17 | 18 | ||
18 | static struct plat_sci_port sci_platform_data[] = { | 19 | static struct plat_sci_port sci_platform_data[] = { |
@@ -64,6 +65,110 @@ static struct platform_device rtc_device = { | |||
64 | .resource = rtc_resources, | 65 | .resource = rtc_resources, |
65 | }; | 66 | }; |
66 | 67 | ||
68 | #define TMU_BLOCK_OFF 0x01020000 | ||
69 | #define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF | ||
70 | #define TMU0_BASE (TMU_BASE + 0x8 + (0xc * 0x0)) | ||
71 | #define TMU1_BASE (TMU_BASE + 0x8 + (0xc * 0x1)) | ||
72 | #define TMU2_BASE (TMU_BASE + 0x8 + (0xc * 0x2)) | ||
73 | |||
74 | static struct sh_timer_config tmu0_platform_data = { | ||
75 | .name = "TMU0", | ||
76 | .channel_offset = 0x04, | ||
77 | .timer_bit = 0, | ||
78 | .clk = "module_clk", | ||
79 | .clockevent_rating = 200, | ||
80 | }; | ||
81 | |||
82 | static struct resource tmu0_resources[] = { | ||
83 | [0] = { | ||
84 | .name = "TMU0", | ||
85 | .start = TMU0_BASE, | ||
86 | .end = TMU0_BASE + 0xc - 1, | ||
87 | .flags = IORESOURCE_MEM, | ||
88 | }, | ||
89 | [1] = { | ||
90 | .start = IRQ_TUNI0, | ||
91 | .flags = IORESOURCE_IRQ, | ||
92 | }, | ||
93 | }; | ||
94 | |||
95 | static struct platform_device tmu0_device = { | ||
96 | .name = "sh_tmu", | ||
97 | .id = 0, | ||
98 | .dev = { | ||
99 | .platform_data = &tmu0_platform_data, | ||
100 | }, | ||
101 | .resource = tmu0_resources, | ||
102 | .num_resources = ARRAY_SIZE(tmu0_resources), | ||
103 | }; | ||
104 | |||
105 | static struct sh_timer_config tmu1_platform_data = { | ||
106 | .name = "TMU1", | ||
107 | .channel_offset = 0x10, | ||
108 | .timer_bit = 1, | ||
109 | .clk = "module_clk", | ||
110 | .clocksource_rating = 200, | ||
111 | }; | ||
112 | |||
113 | static struct resource tmu1_resources[] = { | ||
114 | [0] = { | ||
115 | .name = "TMU1", | ||
116 | .start = TMU1_BASE, | ||
117 | .end = TMU1_BASE + 0xc - 1, | ||
118 | .flags = IORESOURCE_MEM, | ||
119 | }, | ||
120 | [1] = { | ||
121 | .start = IRQ_TUNI1, | ||
122 | .flags = IORESOURCE_IRQ, | ||
123 | }, | ||
124 | }; | ||
125 | |||
126 | static struct platform_device tmu1_device = { | ||
127 | .name = "sh_tmu", | ||
128 | .id = 1, | ||
129 | .dev = { | ||
130 | .platform_data = &tmu1_platform_data, | ||
131 | }, | ||
132 | .resource = tmu1_resources, | ||
133 | .num_resources = ARRAY_SIZE(tmu1_resources), | ||
134 | }; | ||
135 | |||
136 | static struct sh_timer_config tmu2_platform_data = { | ||
137 | .name = "TMU2", | ||
138 | .channel_offset = 0x1c, | ||
139 | .timer_bit = 2, | ||
140 | .clk = "module_clk", | ||
141 | }; | ||
142 | |||
143 | static struct resource tmu2_resources[] = { | ||
144 | [0] = { | ||
145 | .name = "TMU2", | ||
146 | .start = TMU2_BASE, | ||
147 | .end = TMU2_BASE + 0xc - 1, | ||
148 | .flags = IORESOURCE_MEM, | ||
149 | }, | ||
150 | [1] = { | ||
151 | .start = IRQ_TUNI2, | ||
152 | .flags = IORESOURCE_IRQ, | ||
153 | }, | ||
154 | }; | ||
155 | |||
156 | static struct platform_device tmu2_device = { | ||
157 | .name = "sh_tmu", | ||
158 | .id = 2, | ||
159 | .dev = { | ||
160 | .platform_data = &tmu2_platform_data, | ||
161 | }, | ||
162 | .resource = tmu2_resources, | ||
163 | .num_resources = ARRAY_SIZE(tmu2_resources), | ||
164 | }; | ||
165 | |||
166 | static struct platform_device *sh5_early_devices[] __initdata = { | ||
167 | &tmu0_device, | ||
168 | &tmu1_device, | ||
169 | &tmu2_device, | ||
170 | }; | ||
171 | |||
67 | static struct platform_device *sh5_devices[] __initdata = { | 172 | static struct platform_device *sh5_devices[] __initdata = { |
68 | &sci_device, | 173 | &sci_device, |
69 | &rtc_device, | 174 | &rtc_device, |
@@ -71,7 +176,20 @@ static struct platform_device *sh5_devices[] __initdata = { | |||
71 | 176 | ||
72 | static int __init sh5_devices_setup(void) | 177 | static int __init sh5_devices_setup(void) |
73 | { | 178 | { |
179 | int ret; | ||
180 | |||
181 | ret = platform_add_devices(sh5_early_devices, | ||
182 | ARRAY_SIZE(sh5_early_devices)); | ||
183 | if (unlikely(ret != 0)) | ||
184 | return ret; | ||
185 | |||
74 | return platform_add_devices(sh5_devices, | 186 | return platform_add_devices(sh5_devices, |
75 | ARRAY_SIZE(sh5_devices)); | 187 | ARRAY_SIZE(sh5_devices)); |
76 | } | 188 | } |
77 | __initcall(sh5_devices_setup); | 189 | __initcall(sh5_devices_setup); |
190 | |||
191 | void __init plat_early_device_setup(void) | ||
192 | { | ||
193 | early_platform_add_devices(sh5_early_devices, | ||
194 | ARRAY_SIZE(sh5_early_devices)); | ||
195 | } | ||
diff --git a/arch/sh/kernel/time_64.c b/arch/sh/kernel/time_64.c deleted file mode 100644 index b4fe770e2a3f..000000000000 --- a/arch/sh/kernel/time_64.c +++ /dev/null | |||
@@ -1,264 +0,0 @@ | |||
1 | /* | ||
2 | * arch/sh/kernel/time_64.c | ||
3 | * | ||
4 | * Copyright (C) 2000, 2001 Paolo Alberelli | ||
5 | * Copyright (C) 2003 - 2007 Paul Mundt | ||
6 | * Copyright (C) 2003 Richard Curnow | ||
7 | * | ||
8 | * Original TMU/RTC code taken from sh version. | ||
9 | * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka | ||
10 | * Some code taken from i386 version. | ||
11 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | ||
12 | * | ||
13 | * This file is subject to the terms and conditions of the GNU General Public | ||
14 | * License. See the file "COPYING" in the main directory of this archive | ||
15 | * for more details. | ||
16 | */ | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/rwsem.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/param.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/mm.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/time.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/profile.h> | ||
29 | #include <linux/smp.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/bcd.h> | ||
32 | #include <linux/timex.h> | ||
33 | #include <linux/irq.h> | ||
34 | #include <linux/io.h> | ||
35 | #include <linux/platform_device.h> | ||
36 | #include <cpu/registers.h> /* required by inline __asm__ stmt. */ | ||
37 | #include <cpu/irq.h> | ||
38 | #include <asm/addrspace.h> | ||
39 | #include <asm/processor.h> | ||
40 | #include <asm/uaccess.h> | ||
41 | #include <asm/delay.h> | ||
42 | #include <asm/clock.h> | ||
43 | |||
44 | #define TMU_TOCR_INIT 0x00 | ||
45 | #define TMU0_TCR_INIT 0x0020 | ||
46 | #define TMU_TSTR_INIT 1 | ||
47 | #define TMU_TSTR_OFF 0 | ||
48 | |||
49 | /* Time Management Unit */ | ||
50 | #define TMU_BLOCK_OFF 0x01020000 | ||
51 | #define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF | ||
52 | #define TMU0_BASE tmu_base + 0x8 + (0xc * 0x0) | ||
53 | #define TMU1_BASE tmu_base + 0x8 + (0xc * 0x1) | ||
54 | #define TMU2_BASE tmu_base + 0x8 + (0xc * 0x2) | ||
55 | |||
56 | #define TMU_TOCR tmu_base+0x0 /* Byte access */ | ||
57 | #define TMU_TSTR tmu_base+0x4 /* Byte access */ | ||
58 | |||
59 | #define TMU0_TCOR TMU0_BASE+0x0 /* Long access */ | ||
60 | #define TMU0_TCNT TMU0_BASE+0x4 /* Long access */ | ||
61 | #define TMU0_TCR TMU0_BASE+0x8 /* Word access */ | ||
62 | |||
63 | #define TICK_SIZE (tick_nsec / 1000) | ||
64 | |||
65 | static unsigned long tmu_base; | ||
66 | |||
67 | /* Variables to allow interpolation of time of day to resolution better than a | ||
68 | * jiffy. */ | ||
69 | |||
70 | /* This is effectively protected by xtime_lock */ | ||
71 | static unsigned long ctc_last_interrupt; | ||
72 | static unsigned long long usecs_per_jiffy = 1000000/HZ; /* Approximation */ | ||
73 | |||
74 | #define CTC_JIFFY_SCALE_SHIFT 40 | ||
75 | |||
76 | /* 2**CTC_JIFFY_SCALE_SHIFT / ctc_ticks_per_jiffy */ | ||
77 | static unsigned long long scaled_recip_ctc_ticks_per_jiffy; | ||
78 | |||
79 | /* Estimate number of microseconds that have elapsed since the last timer tick, | ||
80 | by scaling the delta that has occurred in the CTC register. | ||
81 | |||
82 | WARNING WARNING WARNING : This algorithm relies on the CTC decrementing at | ||
83 | the CPU clock rate. If the CPU sleeps, the CTC stops counting. Bear this | ||
84 | in mind if enabling SLEEP_WORKS in process.c. In that case, this algorithm | ||
85 | probably needs to use TMU.TCNT0 instead. This will work even if the CPU is | ||
86 | sleeping, though will be coarser. | ||
87 | |||
88 | FIXME : What if usecs_per_tick is moving around too much, e.g. if an adjtime | ||
89 | is running or if the freq or tick arguments of adjtimex are modified after | ||
90 | we have calibrated the scaling factor? This will result in either a jump at | ||
91 | the end of a tick period, or a wrap backwards at the start of the next one, | ||
92 | if the application is reading the time of day often enough. I think we | ||
93 | ought to do better than this. For this reason, usecs_per_jiffy is left | ||
94 | separated out in the calculation below. This allows some future hook into | ||
95 | the adjtime-related stuff in kernel/timer.c to remove this hazard. | ||
96 | |||
97 | */ | ||
98 | |||
99 | static unsigned long usecs_since_tick(void) | ||
100 | { | ||
101 | unsigned long long current_ctc; | ||
102 | long ctc_ticks_since_interrupt; | ||
103 | unsigned long long ull_ctc_ticks_since_interrupt; | ||
104 | unsigned long result; | ||
105 | |||
106 | unsigned long long mul1_out; | ||
107 | unsigned long long mul1_out_high; | ||
108 | unsigned long long mul2_out_low, mul2_out_high; | ||
109 | |||
110 | /* Read CTC register */ | ||
111 | asm ("getcon cr62, %0" : "=r" (current_ctc)); | ||
112 | /* Note, the CTC counts down on each CPU clock, not up. | ||
113 | Note(2), use long type to get correct wraparound arithmetic when | ||
114 | the counter crosses zero. */ | ||
115 | ctc_ticks_since_interrupt = (long) ctc_last_interrupt - (long) current_ctc; | ||
116 | ull_ctc_ticks_since_interrupt = (unsigned long long) ctc_ticks_since_interrupt; | ||
117 | |||
118 | /* Inline assembly to do 32x32x32->64 multiplier */ | ||
119 | asm volatile ("mulu.l %1, %2, %0" : | ||
120 | "=r" (mul1_out) : | ||
121 | "r" (ull_ctc_ticks_since_interrupt), "r" (usecs_per_jiffy)); | ||
122 | |||
123 | mul1_out_high = mul1_out >> 32; | ||
124 | |||
125 | asm volatile ("mulu.l %1, %2, %0" : | ||
126 | "=r" (mul2_out_low) : | ||
127 | "r" (mul1_out), "r" (scaled_recip_ctc_ticks_per_jiffy)); | ||
128 | |||
129 | #if 1 | ||
130 | asm volatile ("mulu.l %1, %2, %0" : | ||
131 | "=r" (mul2_out_high) : | ||
132 | "r" (mul1_out_high), "r" (scaled_recip_ctc_ticks_per_jiffy)); | ||
133 | #endif | ||
134 | |||
135 | result = (unsigned long) (((mul2_out_high << 32) + mul2_out_low) >> CTC_JIFFY_SCALE_SHIFT); | ||
136 | |||
137 | return result; | ||
138 | } | ||
139 | |||
140 | u32 arch_gettimeoffset(void) | ||
141 | { | ||
142 | return usecs_since_tick() * 1000; | ||
143 | } | ||
144 | |||
145 | /* Dummy RTC ops */ | ||
146 | static void null_rtc_get_time(struct timespec *tv) | ||
147 | { | ||
148 | tv->tv_sec = mktime(2000, 1, 1, 0, 0, 0); | ||
149 | tv->tv_nsec = 0; | ||
150 | } | ||
151 | |||
152 | static int null_rtc_set_time(const time_t secs) | ||
153 | { | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time; | ||
158 | int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time; | ||
159 | |||
160 | /* last time the RTC clock got updated */ | ||
161 | static long last_rtc_update; | ||
162 | |||
163 | /* | ||
164 | * timer_interrupt() needs to keep up the real-time clock, | ||
165 | * as well as call the "do_timer()" routine every clocktick | ||
166 | */ | ||
167 | static inline void do_timer_interrupt(void) | ||
168 | { | ||
169 | unsigned long long current_ctc; | ||
170 | |||
171 | if (current->pid) | ||
172 | profile_tick(CPU_PROFILING); | ||
173 | |||
174 | /* | ||
175 | * Here we are in the timer irq handler. We just have irqs locally | ||
176 | * disabled but we don't know if the timer_bh is running on the other | ||
177 | * CPU. We need to avoid to SMP race with it. NOTE: we don' t need | ||
178 | * the irq version of write_lock because as just said we have irq | ||
179 | * locally disabled. -arca | ||
180 | */ | ||
181 | write_seqlock(&xtime_lock); | ||
182 | asm ("getcon cr62, %0" : "=r" (current_ctc)); | ||
183 | ctc_last_interrupt = (unsigned long) current_ctc; | ||
184 | |||
185 | do_timer(1); | ||
186 | |||
187 | /* | ||
188 | * If we have an externally synchronized Linux clock, then update | ||
189 | * RTC clock accordingly every ~11 minutes. Set_rtc_mmss() has to be | ||
190 | * called as close as possible to 500 ms before the new second starts. | ||
191 | */ | ||
192 | if (ntp_synced() && | ||
193 | xtime.tv_sec > last_rtc_update + 660 && | ||
194 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && | ||
195 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { | ||
196 | if (rtc_sh_set_time(xtime.tv_sec) == 0) | ||
197 | last_rtc_update = xtime.tv_sec; | ||
198 | else | ||
199 | /* do it again in 60 s */ | ||
200 | last_rtc_update = xtime.tv_sec - 600; | ||
201 | } | ||
202 | write_sequnlock(&xtime_lock); | ||
203 | |||
204 | #ifndef CONFIG_SMP | ||
205 | update_process_times(user_mode(get_irq_regs())); | ||
206 | #endif | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * This is the same as the above, except we _also_ save the current | ||
211 | * Time Stamp Counter value at the time of the timer interrupt, so that | ||
212 | * we later on can estimate the time of day more exactly. | ||
213 | */ | ||
214 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | ||
215 | { | ||
216 | unsigned long timer_status; | ||
217 | |||
218 | /* Clear UNF bit */ | ||
219 | timer_status = ctrl_inw(TMU0_TCR); | ||
220 | timer_status &= ~0x100; | ||
221 | ctrl_outw(timer_status, TMU0_TCR); | ||
222 | |||
223 | do_timer_interrupt(); | ||
224 | |||
225 | return IRQ_HANDLED; | ||
226 | } | ||
227 | |||
228 | static struct irqaction irq0 = { | ||
229 | .handler = timer_interrupt, | ||
230 | .flags = IRQF_DISABLED, | ||
231 | .name = "timer", | ||
232 | }; | ||
233 | |||
234 | void __init time_init(void) | ||
235 | { | ||
236 | unsigned long interval; | ||
237 | struct clk *clk; | ||
238 | |||
239 | tmu_base = (unsigned long)ioremap_nocache(TMU_BASE, 1024); | ||
240 | if (!tmu_base) { | ||
241 | panic("Unable to remap TMU\n"); | ||
242 | } | ||
243 | |||
244 | clk = clk_get(NULL, "cpu_clk"); | ||
245 | scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) / | ||
246 | (unsigned long long)(clk_get_rate(clk) / HZ)); | ||
247 | |||
248 | rtc_sh_get_time(&xtime); | ||
249 | |||
250 | setup_irq(TIMER_IRQ, &irq0); | ||
251 | |||
252 | clk = clk_get(NULL, "module_clk"); | ||
253 | interval = (clk_get_rate(clk)/(HZ*4)); | ||
254 | |||
255 | printk("Interval = %ld\n", interval); | ||
256 | |||
257 | /* Start TMU0 */ | ||
258 | ctrl_outb(TMU_TSTR_OFF, TMU_TSTR); | ||
259 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); | ||
260 | ctrl_outw(TMU0_TCR_INIT, TMU0_TCR); | ||
261 | ctrl_outl(interval, TMU0_TCOR); | ||
262 | ctrl_outl(interval, TMU0_TCNT); | ||
263 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); | ||
264 | } | ||