diff options
Diffstat (limited to 'arch/mips/alchemy/common/time.c')
-rw-r--r-- | arch/mips/alchemy/common/time.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/arch/mips/alchemy/common/time.c b/arch/mips/alchemy/common/time.c new file mode 100644 index 00000000000..563d9390a87 --- /dev/null +++ b/arch/mips/alchemy/common/time.c | |||
@@ -0,0 +1,266 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> | ||
4 | * Copied and modified Carsten Langgaard's time.c | ||
5 | * | ||
6 | * Carsten Langgaard, carstenl@mips.com | ||
7 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | ||
8 | * | ||
9 | * ######################################################################## | ||
10 | * | ||
11 | * This program is free software; you can distribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License (Version 2) as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
16 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
18 | * for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License along | ||
21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
22 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
23 | * | ||
24 | * ######################################################################## | ||
25 | * | ||
26 | * Setting up the clock on the MIPS boards. | ||
27 | * | ||
28 | * We provide the clock interrupt processing and the timer offset compute | ||
29 | * functions. If CONFIG_PM is selected, we also ensure the 32KHz timer is | ||
30 | * available. -- Dan | ||
31 | */ | ||
32 | |||
33 | #include <linux/types.h> | ||
34 | #include <linux/init.h> | ||
35 | #include <linux/spinlock.h> | ||
36 | |||
37 | #include <asm/mipsregs.h> | ||
38 | #include <asm/time.h> | ||
39 | #include <asm/mach-au1x00/au1000.h> | ||
40 | |||
41 | static int no_au1xxx_32khz; | ||
42 | extern int allow_au1k_wait; /* default off for CP0 Counter */ | ||
43 | |||
44 | #ifdef CONFIG_PM | ||
45 | #if HZ < 100 || HZ > 1000 | ||
46 | #error "unsupported HZ value! Must be in [100,1000]" | ||
47 | #endif | ||
48 | #define MATCH20_INC (328 * 100 / HZ) /* magic number 328 is for HZ=100... */ | ||
49 | static unsigned long last_pc0, last_match20; | ||
50 | #endif | ||
51 | |||
52 | static DEFINE_SPINLOCK(time_lock); | ||
53 | |||
54 | unsigned long wtimer; | ||
55 | |||
56 | #ifdef CONFIG_PM | ||
57 | static irqreturn_t counter0_irq(int irq, void *dev_id) | ||
58 | { | ||
59 | unsigned long pc0; | ||
60 | int time_elapsed; | ||
61 | static int jiffie_drift; | ||
62 | |||
63 | if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) { | ||
64 | /* should never happen! */ | ||
65 | printk(KERN_WARNING "counter 0 w status error\n"); | ||
66 | return IRQ_NONE; | ||
67 | } | ||
68 | |||
69 | pc0 = au_readl(SYS_TOYREAD); | ||
70 | if (pc0 < last_match20) | ||
71 | /* counter overflowed */ | ||
72 | time_elapsed = (0xffffffff - last_match20) + pc0; | ||
73 | else | ||
74 | time_elapsed = pc0 - last_match20; | ||
75 | |||
76 | while (time_elapsed > 0) { | ||
77 | do_timer(1); | ||
78 | #ifndef CONFIG_SMP | ||
79 | update_process_times(user_mode(get_irq_regs())); | ||
80 | #endif | ||
81 | time_elapsed -= MATCH20_INC; | ||
82 | last_match20 += MATCH20_INC; | ||
83 | jiffie_drift++; | ||
84 | } | ||
85 | |||
86 | last_pc0 = pc0; | ||
87 | au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); | ||
88 | au_sync(); | ||
89 | |||
90 | /* | ||
91 | * Our counter ticks at 10.009765625 ms/tick, we we're running | ||
92 | * almost 10 uS too slow per tick. | ||
93 | */ | ||
94 | |||
95 | if (jiffie_drift >= 999) { | ||
96 | jiffie_drift -= 999; | ||
97 | do_timer(1); /* increment jiffies by one */ | ||
98 | #ifndef CONFIG_SMP | ||
99 | update_process_times(user_mode(get_irq_regs())); | ||
100 | #endif | ||
101 | } | ||
102 | |||
103 | return IRQ_HANDLED; | ||
104 | } | ||
105 | |||
106 | struct irqaction counter0_action = { | ||
107 | .handler = counter0_irq, | ||
108 | .flags = IRQF_DISABLED, | ||
109 | .name = "alchemy-toy", | ||
110 | .dev_id = NULL, | ||
111 | }; | ||
112 | |||
113 | /* When we wakeup from sleep, we have to "catch up" on all of the | ||
114 | * timer ticks we have missed. | ||
115 | */ | ||
116 | void wakeup_counter0_adjust(void) | ||
117 | { | ||
118 | unsigned long pc0; | ||
119 | int time_elapsed; | ||
120 | |||
121 | pc0 = au_readl(SYS_TOYREAD); | ||
122 | if (pc0 < last_match20) | ||
123 | /* counter overflowed */ | ||
124 | time_elapsed = (0xffffffff - last_match20) + pc0; | ||
125 | else | ||
126 | time_elapsed = pc0 - last_match20; | ||
127 | |||
128 | while (time_elapsed > 0) { | ||
129 | time_elapsed -= MATCH20_INC; | ||
130 | last_match20 += MATCH20_INC; | ||
131 | } | ||
132 | |||
133 | last_pc0 = pc0; | ||
134 | au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); | ||
135 | au_sync(); | ||
136 | |||
137 | } | ||
138 | |||
139 | /* This is just for debugging to set the timer for a sleep delay. */ | ||
140 | void wakeup_counter0_set(int ticks) | ||
141 | { | ||
142 | unsigned long pc0; | ||
143 | |||
144 | pc0 = au_readl(SYS_TOYREAD); | ||
145 | last_pc0 = pc0; | ||
146 | au_writel(last_match20 + (MATCH20_INC * ticks), SYS_TOYMATCH2); | ||
147 | au_sync(); | ||
148 | } | ||
149 | #endif | ||
150 | |||
151 | /* | ||
152 | * I haven't found anyone that doesn't use a 12 MHz source clock, | ||
153 | * but just in case..... | ||
154 | */ | ||
155 | #define AU1000_SRC_CLK 12000000 | ||
156 | |||
157 | /* | ||
158 | * We read the real processor speed from the PLL. This is important | ||
159 | * because it is more accurate than computing it from the 32 KHz | ||
160 | * counter, if it exists. If we don't have an accurate processor | ||
161 | * speed, all of the peripherals that derive their clocks based on | ||
162 | * this advertised speed will introduce error and sometimes not work | ||
163 | * properly. This function is futher convoluted to still allow configurations | ||
164 | * to do that in case they have really, really old silicon with a | ||
165 | * write-only PLL register, that we need the 32 KHz when power management | ||
166 | * "wait" is enabled, and we need to detect if the 32 KHz isn't present | ||
167 | * but requested......got it? :-) -- Dan | ||
168 | */ | ||
169 | unsigned long calc_clock(void) | ||
170 | { | ||
171 | unsigned long cpu_speed; | ||
172 | unsigned long flags; | ||
173 | unsigned long counter; | ||
174 | |||
175 | spin_lock_irqsave(&time_lock, flags); | ||
176 | |||
177 | /* Power management cares if we don't have a 32 KHz counter. */ | ||
178 | no_au1xxx_32khz = 0; | ||
179 | counter = au_readl(SYS_COUNTER_CNTRL); | ||
180 | if (counter & SYS_CNTRL_E0) { | ||
181 | int trim_divide = 16; | ||
182 | |||
183 | au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL); | ||
184 | |||
185 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); | ||
186 | /* RTC now ticks at 32.768/16 kHz */ | ||
187 | au_writel(trim_divide - 1, SYS_RTCTRIM); | ||
188 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S); | ||
189 | |||
190 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); | ||
191 | au_writel(0, SYS_TOYWRITE); | ||
192 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); | ||
193 | } else | ||
194 | no_au1xxx_32khz = 1; | ||
195 | |||
196 | /* | ||
197 | * On early Au1000, sys_cpupll was write-only. Since these | ||
198 | * silicon versions of Au1000 are not sold by AMD, we don't bend | ||
199 | * over backwards trying to determine the frequency. | ||
200 | */ | ||
201 | if (cur_cpu_spec[0]->cpu_pll_wo) | ||
202 | #ifdef CONFIG_SOC_AU1000_FREQUENCY | ||
203 | cpu_speed = CONFIG_SOC_AU1000_FREQUENCY; | ||
204 | #else | ||
205 | cpu_speed = 396000000; | ||
206 | #endif | ||
207 | else | ||
208 | cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK; | ||
209 | /* On Alchemy CPU:counter ratio is 1:1 */ | ||
210 | mips_hpt_frequency = cpu_speed; | ||
211 | /* Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) */ | ||
212 | set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL) | ||
213 | & 0x03) + 2) * 16)); | ||
214 | spin_unlock_irqrestore(&time_lock, flags); | ||
215 | return cpu_speed; | ||
216 | } | ||
217 | |||
218 | void __init plat_time_init(void) | ||
219 | { | ||
220 | unsigned int est_freq = calc_clock(); | ||
221 | |||
222 | est_freq += 5000; /* round */ | ||
223 | est_freq -= est_freq%10000; | ||
224 | printk(KERN_INFO "CPU frequency %u.%02u MHz\n", | ||
225 | est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000); | ||
226 | set_au1x00_speed(est_freq); | ||
227 | set_au1x00_lcd_clock(); /* program the LCD clock */ | ||
228 | |||
229 | #ifdef CONFIG_PM | ||
230 | /* | ||
231 | * setup counter 0, since it keeps ticking after a | ||
232 | * 'wait' instruction has been executed. The CP0 timer and | ||
233 | * counter 1 do NOT continue running after 'wait' | ||
234 | * | ||
235 | * It's too early to call request_irq() here, so we handle | ||
236 | * counter 0 interrupt as a special irq and it doesn't show | ||
237 | * up under /proc/interrupts. | ||
238 | * | ||
239 | * Check to ensure we really have a 32 KHz oscillator before | ||
240 | * we do this. | ||
241 | */ | ||
242 | if (no_au1xxx_32khz) | ||
243 | printk(KERN_WARNING "WARNING: no 32KHz clock found.\n"); | ||
244 | else { | ||
245 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S); | ||
246 | au_writel(0, SYS_TOYWRITE); | ||
247 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S); | ||
248 | |||
249 | au_writel(au_readl(SYS_WAKEMSK) | (1 << 8), SYS_WAKEMSK); | ||
250 | au_writel(~0, SYS_WAKESRC); | ||
251 | au_sync(); | ||
252 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); | ||
253 | |||
254 | /* Setup match20 to interrupt once every HZ */ | ||
255 | last_pc0 = last_match20 = au_readl(SYS_TOYREAD); | ||
256 | au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); | ||
257 | au_sync(); | ||
258 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); | ||
259 | setup_irq(AU1000_TOY_MATCH2_INT, &counter0_action); | ||
260 | |||
261 | /* We can use the real 'wait' instruction. */ | ||
262 | allow_au1k_wait = 1; | ||
263 | } | ||
264 | |||
265 | #endif | ||
266 | } | ||