aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2009-05-08 03:12:17 -0400
committerPaul Mundt <lethal@linux-sh.org>2009-05-08 03:12:17 -0400
commitadd47067a8ca324e9d26c4373350dc3cce7f1e7f (patch)
tree2ee7062a8e9b42d0452b5ca2e848483366f1f598 /arch/sh/kernel
parentc2ecb4c4a7da16288062a057b693b7b1e16aaf88 (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_642
-rw-r--r--arch/sh/kernel/cpu/sh5/setup-sh5.c118
-rw-r--r--arch/sh/kernel/time_64.c264
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
3obj-y := debugtraps.o idle.o io.o io_generic.o irq.o machvec.o process_64.o \ 3obj-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
7obj-y += cpu/ timers/ 7obj-y += cpu/ timers/
8obj-$(CONFIG_VSYSCALL) += vsyscall/ 8obj-$(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
18static struct plat_sci_port sci_platform_data[] = { 19static 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
74static 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
82static 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
95static 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
105static 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
113static 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
126static 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
136static struct sh_timer_config tmu2_platform_data = {
137 .name = "TMU2",
138 .channel_offset = 0x1c,
139 .timer_bit = 2,
140 .clk = "module_clk",
141};
142
143static 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
156static 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
166static struct platform_device *sh5_early_devices[] __initdata = {
167 &tmu0_device,
168 &tmu1_device,
169 &tmu2_device,
170};
171
67static struct platform_device *sh5_devices[] __initdata = { 172static 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
72static int __init sh5_devices_setup(void) 177static 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
191void __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
65static 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 */
71static unsigned long ctc_last_interrupt;
72static 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 */
77static 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
99static 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
140u32 arch_gettimeoffset(void)
141{
142 return usecs_since_tick() * 1000;
143}
144
145/* Dummy RTC ops */
146static 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
152static int null_rtc_set_time(const time_t secs)
153{
154 return 0;
155}
156
157void (*rtc_sh_get_time)(struct timespec *) = null_rtc_get_time;
158int (*rtc_sh_set_time)(const time_t) = null_rtc_set_time;
159
160/* last time the RTC clock got updated */
161static 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 */
167static 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 */
214static 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
228static struct irqaction irq0 = {
229 .handler = timer_interrupt,
230 .flags = IRQF_DISABLED,
231 .name = "timer",
232};
233
234void __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}