diff options
author | Paul Mundt <lethal@linux-sh.org> | 2008-09-29 07:09:17 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2008-09-29 07:09:17 -0400 |
commit | 4d01cdafbafc0fdeb730838ca38a48e5ca2894cd (patch) | |
tree | 5157d3e9dd1882253ce848fd2d2650aaa17241aa /arch/sh/kernel | |
parent | 50b72e600b62bcdf40971e55f609cf4771346cc1 (diff) |
sh: SH-5 clk fwk support.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/kernel')
-rw-r--r-- | arch/sh/kernel/cpu/sh5/Makefile | 5 | ||||
-rw-r--r-- | arch/sh/kernel/cpu/sh5/clock-sh5.c | 79 | ||||
-rw-r--r-- | arch/sh/kernel/time_64.c | 168 |
3 files changed, 91 insertions, 161 deletions
diff --git a/arch/sh/kernel/cpu/sh5/Makefile b/arch/sh/kernel/cpu/sh5/Makefile index 8646363e9ded..ce4602ea23a8 100644 --- a/arch/sh/kernel/cpu/sh5/Makefile +++ b/arch/sh/kernel/cpu/sh5/Makefile | |||
@@ -5,3 +5,8 @@ obj-y := entry.o probe.o switchto.o | |||
5 | 5 | ||
6 | obj-$(CONFIG_SH_FPU) += fpu.o | 6 | obj-$(CONFIG_SH_FPU) += fpu.o |
7 | obj-$(CONFIG_KALLSYMS) += unwind.o | 7 | obj-$(CONFIG_KALLSYMS) += unwind.o |
8 | |||
9 | # Primary on-chip clocks (common) | ||
10 | clock-$(CONFIG_CPU_SH5) := clock-sh5.o | ||
11 | |||
12 | obj-y += $(clock-y) | ||
diff --git a/arch/sh/kernel/cpu/sh5/clock-sh5.c b/arch/sh/kernel/cpu/sh5/clock-sh5.c new file mode 100644 index 000000000000..52c49248833a --- /dev/null +++ b/arch/sh/kernel/cpu/sh5/clock-sh5.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * arch/sh/kernel/cpu/sh5/clock-sh5.c | ||
3 | * | ||
4 | * SH-5 support for the clock framework | ||
5 | * | ||
6 | * Copyright (C) 2008 Paul Mundt | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General Public | ||
9 | * License. See the file "COPYING" in the main directory of this archive | ||
10 | * for more details. | ||
11 | */ | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <asm/clock.h> | ||
15 | #include <asm/io.h> | ||
16 | |||
17 | static int ifc_table[] = { 2, 4, 6, 8, 10, 12, 16, 24 }; | ||
18 | |||
19 | /* Clock, Power and Reset Controller */ | ||
20 | #define CPRC_BLOCK_OFF 0x01010000 | ||
21 | #define CPRC_BASE (PHYS_PERIPHERAL_BLOCK + CPRC_BLOCK_OFF) | ||
22 | |||
23 | static unsigned long cprc_base; | ||
24 | |||
25 | static void master_clk_init(struct clk *clk) | ||
26 | { | ||
27 | int idx = (ctrl_inl(cprc_base + 0x00) >> 6) & 0x0007; | ||
28 | clk->rate *= ifc_table[idx]; | ||
29 | } | ||
30 | |||
31 | static struct clk_ops sh5_master_clk_ops = { | ||
32 | .init = master_clk_init, | ||
33 | }; | ||
34 | |||
35 | static void module_clk_recalc(struct clk *clk) | ||
36 | { | ||
37 | int idx = (ctrl_inw(cprc_base) >> 12) & 0x0007; | ||
38 | clk->rate = clk->parent->rate / ifc_table[idx]; | ||
39 | } | ||
40 | |||
41 | static struct clk_ops sh5_module_clk_ops = { | ||
42 | .recalc = module_clk_recalc, | ||
43 | }; | ||
44 | |||
45 | static void bus_clk_recalc(struct clk *clk) | ||
46 | { | ||
47 | int idx = (ctrl_inw(cprc_base) >> 3) & 0x0007; | ||
48 | clk->rate = clk->parent->rate / ifc_table[idx]; | ||
49 | } | ||
50 | |||
51 | static struct clk_ops sh5_bus_clk_ops = { | ||
52 | .recalc = bus_clk_recalc, | ||
53 | }; | ||
54 | |||
55 | static void cpu_clk_recalc(struct clk *clk) | ||
56 | { | ||
57 | int idx = (ctrl_inw(cprc_base) & 0x0007); | ||
58 | clk->rate = clk->parent->rate / ifc_table[idx]; | ||
59 | } | ||
60 | |||
61 | static struct clk_ops sh5_cpu_clk_ops = { | ||
62 | .recalc = cpu_clk_recalc, | ||
63 | }; | ||
64 | |||
65 | static struct clk_ops *sh5_clk_ops[] = { | ||
66 | &sh5_master_clk_ops, | ||
67 | &sh5_module_clk_ops, | ||
68 | &sh5_bus_clk_ops, | ||
69 | &sh5_cpu_clk_ops, | ||
70 | }; | ||
71 | |||
72 | void __init arch_init_clk_ops(struct clk_ops **ops, int idx) | ||
73 | { | ||
74 | cprc_base = onchip_remap(CPRC_BASE, 1024, "CPRC"); | ||
75 | BUG_ON(!cprc_base); | ||
76 | |||
77 | if (idx < ARRAY_SIZE(sh5_clk_ops)) | ||
78 | *ops = sh5_clk_ops[idx]; | ||
79 | } | ||
diff --git a/arch/sh/kernel/time_64.c b/arch/sh/kernel/time_64.c index 791edabf7d83..bbb2af1004d9 100644 --- a/arch/sh/kernel/time_64.c +++ b/arch/sh/kernel/time_64.c | |||
@@ -39,6 +39,7 @@ | |||
39 | #include <asm/processor.h> | 39 | #include <asm/processor.h> |
40 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
41 | #include <asm/delay.h> | 41 | #include <asm/delay.h> |
42 | #include <asm/clock.h> | ||
42 | 43 | ||
43 | #define TMU_TOCR_INIT 0x00 | 44 | #define TMU_TOCR_INIT 0x00 |
44 | #define TMU0_TCR_INIT 0x0020 | 45 | #define TMU0_TCR_INIT 0x0020 |
@@ -51,14 +52,6 @@ | |||
51 | #define RTC_RCR1_CIE 0x10 /* Carry Interrupt Enable */ | 52 | #define RTC_RCR1_CIE 0x10 /* Carry Interrupt Enable */ |
52 | #define RTC_RCR1 (rtc_base + 0x38) | 53 | #define RTC_RCR1 (rtc_base + 0x38) |
53 | 54 | ||
54 | /* Clock, Power and Reset Controller */ | ||
55 | #define CPRC_BLOCK_OFF 0x01010000 | ||
56 | #define CPRC_BASE PHYS_PERIPHERAL_BLOCK + CPRC_BLOCK_OFF | ||
57 | |||
58 | #define FRQCR (cprc_base+0x0) | ||
59 | #define WTCSR (cprc_base+0x0018) | ||
60 | #define STBCR (cprc_base+0x0030) | ||
61 | |||
62 | /* Time Management Unit */ | 55 | /* Time Management Unit */ |
63 | #define TMU_BLOCK_OFF 0x01020000 | 56 | #define TMU_BLOCK_OFF 0x01020000 |
64 | #define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF | 57 | #define TMU_BASE PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF |
@@ -293,103 +286,17 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id) | |||
293 | return IRQ_HANDLED; | 286 | return IRQ_HANDLED; |
294 | } | 287 | } |
295 | 288 | ||
296 | |||
297 | static __init unsigned int get_cpu_hz(void) | ||
298 | { | ||
299 | unsigned int count; | ||
300 | unsigned long __dummy; | ||
301 | unsigned long ctc_val_init, ctc_val; | ||
302 | |||
303 | /* | ||
304 | ** Regardless the toolchain, force the compiler to use the | ||
305 | ** arbitrary register r3 as a clock tick counter. | ||
306 | ** NOTE: r3 must be in accordance with sh64_rtc_interrupt() | ||
307 | */ | ||
308 | register unsigned long long __rtc_irq_flag __asm__ ("r3"); | ||
309 | |||
310 | local_irq_enable(); | ||
311 | do {} while (ctrl_inb(rtc_base) != 0); | ||
312 | ctrl_outb(RTC_RCR1_CIE, RTC_RCR1); /* Enable carry interrupt */ | ||
313 | |||
314 | /* | ||
315 | * r3 is arbitrary. CDC does not support "=z". | ||
316 | */ | ||
317 | ctc_val_init = 0xffffffff; | ||
318 | ctc_val = ctc_val_init; | ||
319 | |||
320 | asm volatile("gettr tr0, %1\n\t" | ||
321 | "putcon %0, " __CTC "\n\t" | ||
322 | "and %2, r63, %2\n\t" | ||
323 | "pta $+4, tr0\n\t" | ||
324 | "beq/l %2, r63, tr0\n\t" | ||
325 | "ptabs %1, tr0\n\t" | ||
326 | "getcon " __CTC ", %0\n\t" | ||
327 | : "=r"(ctc_val), "=r" (__dummy), "=r" (__rtc_irq_flag) | ||
328 | : "0" (0)); | ||
329 | local_irq_disable(); | ||
330 | /* | ||
331 | * SH-3: | ||
332 | * CPU clock = 4 stages * loop | ||
333 | * tst rm,rm if id ex | ||
334 | * bt/s 1b if id ex | ||
335 | * add #1,rd if id ex | ||
336 | * (if) pipe line stole | ||
337 | * tst rm,rm if id ex | ||
338 | * .... | ||
339 | * | ||
340 | * | ||
341 | * SH-4: | ||
342 | * CPU clock = 6 stages * loop | ||
343 | * I don't know why. | ||
344 | * .... | ||
345 | * | ||
346 | * SH-5: | ||
347 | * Use CTC register to count. This approach returns the right value | ||
348 | * even if the I-cache is disabled (e.g. whilst debugging.) | ||
349 | * | ||
350 | */ | ||
351 | |||
352 | count = ctc_val_init - ctc_val; /* CTC counts down */ | ||
353 | |||
354 | /* | ||
355 | * This really is count by the number of clock cycles | ||
356 | * by the ratio between a complete R64CNT | ||
357 | * wrap-around (128) and CUI interrupt being raised (64). | ||
358 | */ | ||
359 | return count*2; | ||
360 | } | ||
361 | |||
362 | static irqreturn_t sh64_rtc_interrupt(int irq, void *dev_id) | ||
363 | { | ||
364 | struct pt_regs *regs = get_irq_regs(); | ||
365 | |||
366 | ctrl_outb(0, RTC_RCR1); /* Disable Carry Interrupts */ | ||
367 | regs->regs[3] = 1; /* Using r3 */ | ||
368 | |||
369 | return IRQ_HANDLED; | ||
370 | } | ||
371 | |||
372 | static struct irqaction irq0 = { | 289 | static struct irqaction irq0 = { |
373 | .handler = timer_interrupt, | 290 | .handler = timer_interrupt, |
374 | .flags = IRQF_DISABLED, | 291 | .flags = IRQF_DISABLED, |
375 | .mask = CPU_MASK_NONE, | 292 | .mask = CPU_MASK_NONE, |
376 | .name = "timer", | 293 | .name = "timer", |
377 | }; | 294 | }; |
378 | static struct irqaction irq1 = { | ||
379 | .handler = sh64_rtc_interrupt, | ||
380 | .flags = IRQF_DISABLED, | ||
381 | .mask = CPU_MASK_NONE, | ||
382 | .name = "rtc", | ||
383 | }; | ||
384 | 295 | ||
385 | void __init time_init(void) | 296 | void __init time_init(void) |
386 | { | 297 | { |
387 | unsigned int cpu_clock, master_clock, bus_clock, module_clock; | ||
388 | unsigned long interval; | 298 | unsigned long interval; |
389 | unsigned long frqcr, ifc, pfc; | 299 | struct clk *clk; |
390 | static int ifc_table[] = { 2, 4, 6, 8, 10, 12, 16, 24 }; | ||
391 | #define bfc_table ifc_table /* Same */ | ||
392 | #define pfc_table ifc_table /* Same */ | ||
393 | 300 | ||
394 | tmu_base = onchip_remap(TMU_BASE, 1024, "TMU"); | 301 | tmu_base = onchip_remap(TMU_BASE, 1024, "TMU"); |
395 | if (!tmu_base) { | 302 | if (!tmu_base) { |
@@ -401,50 +308,19 @@ void __init time_init(void) | |||
401 | panic("Unable to remap RTC\n"); | 308 | panic("Unable to remap RTC\n"); |
402 | } | 309 | } |
403 | 310 | ||
404 | cprc_base = onchip_remap(CPRC_BASE, 1024, "CPRC"); | 311 | clk = clk_get(NULL, "cpu_clk"); |
405 | if (!cprc_base) { | 312 | scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) / |
406 | panic("Unable to remap CPRC\n"); | 313 | (unsigned long long)(clk_get_rate(clk) / HZ)); |
407 | } | ||
408 | 314 | ||
409 | rtc_sh_get_time(&xtime); | 315 | rtc_sh_get_time(&xtime); |
410 | 316 | ||
411 | setup_irq(TIMER_IRQ, &irq0); | 317 | setup_irq(TIMER_IRQ, &irq0); |
412 | setup_irq(RTC_IRQ, &irq1); | ||
413 | |||
414 | /* Check how fast it is.. */ | ||
415 | cpu_clock = get_cpu_hz(); | ||
416 | |||
417 | /* Note careful order of operations to maintain reasonable precision and avoid overflow. */ | ||
418 | scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) / (unsigned long long)(cpu_clock / HZ)); | ||
419 | |||
420 | free_irq(RTC_IRQ, NULL); | ||
421 | |||
422 | printk("CPU clock: %d.%02dMHz\n", | ||
423 | (cpu_clock / 1000000), (cpu_clock % 1000000)/10000); | ||
424 | { | ||
425 | unsigned short bfc; | ||
426 | frqcr = ctrl_inl(FRQCR); | ||
427 | ifc = ifc_table[(frqcr>> 6) & 0x0007]; | ||
428 | bfc = bfc_table[(frqcr>> 3) & 0x0007]; | ||
429 | pfc = pfc_table[(frqcr>> 12) & 0x0007]; | ||
430 | master_clock = cpu_clock * ifc; | ||
431 | bus_clock = master_clock/bfc; | ||
432 | } | ||
433 | 318 | ||
434 | printk("Bus clock: %d.%02dMHz\n", | 319 | clk = clk_get(NULL, "module_clk"); |
435 | (bus_clock/1000000), (bus_clock % 1000000)/10000); | 320 | interval = (clk_get_rate(clk)/(HZ*4)); |
436 | module_clock = master_clock/pfc; | ||
437 | printk("Module clock: %d.%02dMHz\n", | ||
438 | (module_clock/1000000), (module_clock % 1000000)/10000); | ||
439 | interval = (module_clock/(HZ*4)); | ||
440 | 321 | ||
441 | printk("Interval = %ld\n", interval); | 322 | printk("Interval = %ld\n", interval); |
442 | 323 | ||
443 | current_cpu_data.cpu_clock = cpu_clock; | ||
444 | current_cpu_data.master_clock = master_clock; | ||
445 | current_cpu_data.bus_clock = bus_clock; | ||
446 | current_cpu_data.module_clock = module_clock; | ||
447 | |||
448 | /* Start TMU0 */ | 324 | /* Start TMU0 */ |
449 | ctrl_outb(TMU_TSTR_OFF, TMU_TSTR); | 325 | ctrl_outb(TMU_TSTR_OFF, TMU_TSTR); |
450 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); | 326 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); |
@@ -454,36 +330,6 @@ void __init time_init(void) | |||
454 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); | 330 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); |
455 | } | 331 | } |
456 | 332 | ||
457 | void enter_deep_standby(void) | ||
458 | { | ||
459 | /* Disable watchdog timer */ | ||
460 | ctrl_outl(0xa5000000, WTCSR); | ||
461 | /* Configure deep standby on sleep */ | ||
462 | ctrl_outl(0x03, STBCR); | ||
463 | |||
464 | #ifdef CONFIG_SH_ALPHANUMERIC | ||
465 | { | ||
466 | extern void mach_alphanum(int position, unsigned char value); | ||
467 | extern void mach_alphanum_brightness(int setting); | ||
468 | char halted[] = "Halted. "; | ||
469 | int i; | ||
470 | mach_alphanum_brightness(6); /* dimmest setting above off */ | ||
471 | for (i=0; i<8; i++) { | ||
472 | mach_alphanum(i, halted[i]); | ||
473 | } | ||
474 | asm __volatile__ ("synco"); | ||
475 | } | ||
476 | #endif | ||
477 | |||
478 | asm __volatile__ ("sleep"); | ||
479 | asm __volatile__ ("synci"); | ||
480 | asm __volatile__ ("nop"); | ||
481 | asm __volatile__ ("nop"); | ||
482 | asm __volatile__ ("nop"); | ||
483 | asm __volatile__ ("nop"); | ||
484 | panic("Unexpected wakeup!\n"); | ||
485 | } | ||
486 | |||
487 | static struct resource rtc_resources[] = { | 333 | static struct resource rtc_resources[] = { |
488 | [0] = { | 334 | [0] = { |
489 | /* RTC base, filled in by rtc_init */ | 335 | /* RTC base, filled in by rtc_init */ |