diff options
Diffstat (limited to 'arch/mips/pnx8550/common/time.c')
-rw-r--r-- | arch/mips/pnx8550/common/time.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/arch/mips/pnx8550/common/time.c b/arch/mips/pnx8550/common/time.c new file mode 100644 index 000000000000..8836c6203df0 --- /dev/null +++ b/arch/mips/pnx8550/common/time.c | |||
@@ -0,0 +1,151 @@ | |||
1 | /* | ||
2 | * Copyright 2001, 2002, 2003 MontaVista Software Inc. | ||
3 | * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net | ||
4 | * Copyright (C) 2007 Ralf Baechle (ralf@linux-mips.org) | ||
5 | * | ||
6 | * Common time service routines for MIPS machines. See | ||
7 | * Documents/MIPS/README.txt. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | */ | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/param.h> | ||
19 | #include <linux/time.h> | ||
20 | #include <linux/timer.h> | ||
21 | #include <linux/smp.h> | ||
22 | #include <linux/kernel_stat.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | |||
26 | #include <asm/bootinfo.h> | ||
27 | #include <asm/cpu.h> | ||
28 | #include <asm/time.h> | ||
29 | #include <asm/hardirq.h> | ||
30 | #include <asm/div64.h> | ||
31 | #include <asm/debug.h> | ||
32 | |||
33 | #include <int.h> | ||
34 | #include <cm.h> | ||
35 | |||
36 | static unsigned long cpj; | ||
37 | |||
38 | static cycle_t hpt_read(struct clocksource *cs) | ||
39 | { | ||
40 | return read_c0_count2(); | ||
41 | } | ||
42 | |||
43 | static struct clocksource pnx_clocksource = { | ||
44 | .name = "pnx8xxx", | ||
45 | .rating = 200, | ||
46 | .read = hpt_read, | ||
47 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, | ||
48 | }; | ||
49 | |||
50 | static irqreturn_t pnx8xxx_timer_interrupt(int irq, void *dev_id) | ||
51 | { | ||
52 | struct clock_event_device *c = dev_id; | ||
53 | |||
54 | /* clear MATCH, signal the event */ | ||
55 | c->event_handler(c); | ||
56 | |||
57 | return IRQ_HANDLED; | ||
58 | } | ||
59 | |||
60 | static struct irqaction pnx8xxx_timer_irq = { | ||
61 | .handler = pnx8xxx_timer_interrupt, | ||
62 | .flags = IRQF_DISABLED | IRQF_PERCPU | IRQF_TIMER, | ||
63 | .name = "pnx8xxx_timer", | ||
64 | }; | ||
65 | |||
66 | static irqreturn_t monotonic_interrupt(int irq, void *dev_id) | ||
67 | { | ||
68 | /* Timer 2 clear interrupt */ | ||
69 | write_c0_compare2(-1); | ||
70 | return IRQ_HANDLED; | ||
71 | } | ||
72 | |||
73 | static struct irqaction monotonic_irqaction = { | ||
74 | .handler = monotonic_interrupt, | ||
75 | .flags = IRQF_DISABLED | IRQF_TIMER, | ||
76 | .name = "Monotonic timer", | ||
77 | }; | ||
78 | |||
79 | static int pnx8xxx_set_next_event(unsigned long delta, | ||
80 | struct clock_event_device *evt) | ||
81 | { | ||
82 | write_c0_compare(delta); | ||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static struct clock_event_device pnx8xxx_clockevent = { | ||
87 | .name = "pnx8xxx_clockevent", | ||
88 | .features = CLOCK_EVT_FEAT_ONESHOT, | ||
89 | .set_next_event = pnx8xxx_set_next_event, | ||
90 | }; | ||
91 | |||
92 | static inline void timer_ack(void) | ||
93 | { | ||
94 | write_c0_compare(cpj); | ||
95 | } | ||
96 | |||
97 | __init void plat_time_init(void) | ||
98 | { | ||
99 | unsigned int configPR; | ||
100 | unsigned int n; | ||
101 | unsigned int m; | ||
102 | unsigned int p; | ||
103 | unsigned int pow2p; | ||
104 | |||
105 | pnx8xxx_clockevent.cpumask = cpu_none_mask; | ||
106 | clockevents_register_device(&pnx8xxx_clockevent); | ||
107 | clocksource_register(&pnx_clocksource); | ||
108 | |||
109 | /* Timer 1 start */ | ||
110 | configPR = read_c0_config7(); | ||
111 | configPR &= ~0x00000008; | ||
112 | write_c0_config7(configPR); | ||
113 | |||
114 | /* Timer 2 start */ | ||
115 | configPR = read_c0_config7(); | ||
116 | configPR &= ~0x00000010; | ||
117 | write_c0_config7(configPR); | ||
118 | |||
119 | /* Timer 3 stop */ | ||
120 | configPR = read_c0_config7(); | ||
121 | configPR |= 0x00000020; | ||
122 | write_c0_config7(configPR); | ||
123 | |||
124 | |||
125 | /* PLL0 sets MIPS clock (PLL1 <=> TM1, PLL6 <=> TM2, PLL5 <=> mem) */ | ||
126 | /* (but only if CLK_MIPS_CTL select value [bits 3:1] is 1: FIXME) */ | ||
127 | |||
128 | n = (PNX8550_CM_PLL0_CTL & PNX8550_CM_PLL_N_MASK) >> 16; | ||
129 | m = (PNX8550_CM_PLL0_CTL & PNX8550_CM_PLL_M_MASK) >> 8; | ||
130 | p = (PNX8550_CM_PLL0_CTL & PNX8550_CM_PLL_P_MASK) >> 2; | ||
131 | pow2p = (1 << p); | ||
132 | |||
133 | db_assert(m != 0 && pow2p != 0); | ||
134 | |||
135 | /* | ||
136 | * Compute the frequency as in the PNX8550 User Manual 1.0, p.186 | ||
137 | * (a.k.a. 8-10). Divide by HZ for a timer offset that results in | ||
138 | * HZ timer interrupts per second. | ||
139 | */ | ||
140 | mips_hpt_frequency = 27UL * ((1000000UL * n)/(m * pow2p)); | ||
141 | cpj = DIV_ROUND_CLOSEST(mips_hpt_frequency, HZ); | ||
142 | write_c0_count(0); | ||
143 | timer_ack(); | ||
144 | |||
145 | /* Setup Timer 2 */ | ||
146 | write_c0_count2(0); | ||
147 | write_c0_compare2(0xffffffff); | ||
148 | |||
149 | setup_irq(PNX8550_INT_TIMER1, &pnx8xxx_timer_irq); | ||
150 | setup_irq(PNX8550_INT_TIMER2, &monotonic_irqaction); | ||
151 | } | ||