diff options
author | Ingo Molnar <mingo@elte.hu> | 2009-01-18 14:15:05 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-01-18 14:15:05 -0500 |
commit | 4092762aebfe55c1f8e31440b80a053c2dbe519b (patch) | |
tree | 8fb9fd14131194174c12daf5d8195afd3b62bc3e /arch/mips/alchemy/common/time.c | |
parent | 745b1626dd71ce9661a05ea4db57859ed5c773d2 (diff) | |
parent | 1de9e8e70f5acc441550ca75433563d91b269bbe (diff) |
Merge branch 'tracing/ftrace'; commit 'v2.6.29-rc2' into tracing/core
Diffstat (limited to 'arch/mips/alchemy/common/time.c')
-rw-r--r-- | arch/mips/alchemy/common/time.c | 311 |
1 files changed, 105 insertions, 206 deletions
diff --git a/arch/mips/alchemy/common/time.c b/arch/mips/alchemy/common/time.c index 563d9390a872..32880146cbc1 100644 --- a/arch/mips/alchemy/common/time.c +++ b/arch/mips/alchemy/common/time.c | |||
@@ -1,5 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2008 Manuel Lauss <mano@roarinelk.homelinux.net> | ||
2 | * | 3 | * |
4 | * Previous incarnations were: | ||
3 | * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> | 5 | * Copyright (C) 2001, 2006, 2008 MontaVista Software, <source@mvista.com> |
4 | * Copied and modified Carsten Langgaard's time.c | 6 | * Copied and modified Carsten Langgaard's time.c |
5 | * | 7 | * |
@@ -23,244 +25,141 @@ | |||
23 | * | 25 | * |
24 | * ######################################################################## | 26 | * ######################################################################## |
25 | * | 27 | * |
26 | * Setting up the clock on the MIPS boards. | 28 | * Clocksource/event using the 32.768kHz-clocked Counter1 ('RTC' in the |
27 | * | 29 | * databooks). Firmware/Board init code must enable the counters in the |
28 | * We provide the clock interrupt processing and the timer offset compute | 30 | * counter control register, otherwise the CP0 counter clocksource/event |
29 | * functions. If CONFIG_PM is selected, we also ensure the 32KHz timer is | 31 | * will be installed instead (and use of 'wait' instruction is prohibited). |
30 | * available. -- Dan | ||
31 | */ | 32 | */ |
32 | 33 | ||
33 | #include <linux/types.h> | 34 | #include <linux/clockchips.h> |
34 | #include <linux/init.h> | 35 | #include <linux/clocksource.h> |
36 | #include <linux/interrupt.h> | ||
35 | #include <linux/spinlock.h> | 37 | #include <linux/spinlock.h> |
36 | 38 | ||
37 | #include <asm/mipsregs.h> | ||
38 | #include <asm/time.h> | 39 | #include <asm/time.h> |
39 | #include <asm/mach-au1x00/au1000.h> | 40 | #include <asm/mach-au1x00/au1000.h> |
40 | 41 | ||
41 | static int no_au1xxx_32khz; | 42 | /* 32kHz clock enabled and detected */ |
42 | extern int allow_au1k_wait; /* default off for CP0 Counter */ | 43 | #define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S) |
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 | 44 | ||
52 | static DEFINE_SPINLOCK(time_lock); | 45 | extern int allow_au1k_wait; /* default off for CP0 Counter */ |
53 | |||
54 | unsigned long wtimer; | ||
55 | 46 | ||
56 | #ifdef CONFIG_PM | 47 | static cycle_t au1x_counter1_read(void) |
57 | static irqreturn_t counter0_irq(int irq, void *dev_id) | ||
58 | { | 48 | { |
59 | unsigned long pc0; | 49 | return au_readl(SYS_RTCREAD); |
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 | } | 50 | } |
105 | 51 | ||
106 | struct irqaction counter0_action = { | 52 | static struct clocksource au1x_counter1_clocksource = { |
107 | .handler = counter0_irq, | 53 | .name = "alchemy-counter1", |
108 | .flags = IRQF_DISABLED, | 54 | .read = au1x_counter1_read, |
109 | .name = "alchemy-toy", | 55 | .mask = CLOCKSOURCE_MASK(32), |
110 | .dev_id = NULL, | 56 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
57 | .rating = 100, | ||
111 | }; | 58 | }; |
112 | 59 | ||
113 | /* When we wakeup from sleep, we have to "catch up" on all of the | 60 | static int au1x_rtcmatch2_set_next_event(unsigned long delta, |
114 | * timer ticks we have missed. | 61 | struct clock_event_device *cd) |
115 | */ | ||
116 | void wakeup_counter0_adjust(void) | ||
117 | { | 62 | { |
118 | unsigned long pc0; | 63 | delta += au_readl(SYS_RTCREAD); |
119 | int time_elapsed; | 64 | /* wait for register access */ |
120 | 65 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M21) | |
121 | pc0 = au_readl(SYS_TOYREAD); | 66 | ; |
122 | if (pc0 < last_match20) | 67 | au_writel(delta, SYS_RTCMATCH2); |
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(); | 68 | au_sync(); |
136 | 69 | ||
70 | return 0; | ||
137 | } | 71 | } |
138 | 72 | ||
139 | /* This is just for debugging to set the timer for a sleep delay. */ | 73 | static void au1x_rtcmatch2_set_mode(enum clock_event_mode mode, |
140 | void wakeup_counter0_set(int ticks) | 74 | struct clock_event_device *cd) |
141 | { | 75 | { |
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 | } | 76 | } |
149 | #endif | ||
150 | 77 | ||
151 | /* | 78 | static irqreturn_t au1x_rtcmatch2_irq(int irq, void *dev_id) |
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 | { | 79 | { |
171 | unsigned long cpu_speed; | 80 | struct clock_event_device *cd = dev_id; |
172 | unsigned long flags; | 81 | cd->event_handler(cd); |
173 | unsigned long counter; | 82 | return IRQ_HANDLED; |
174 | 83 | } | |
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 | 84 | ||
190 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); | 85 | static struct clock_event_device au1x_rtcmatch2_clockdev = { |
191 | au_writel(0, SYS_TOYWRITE); | 86 | .name = "rtcmatch2", |
192 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S); | 87 | .features = CLOCK_EVT_FEAT_ONESHOT, |
193 | } else | 88 | .rating = 100, |
194 | no_au1xxx_32khz = 1; | 89 | .irq = AU1000_RTC_MATCH2_INT, |
90 | .set_next_event = au1x_rtcmatch2_set_next_event, | ||
91 | .set_mode = au1x_rtcmatch2_set_mode, | ||
92 | .cpumask = CPU_MASK_ALL, | ||
93 | }; | ||
195 | 94 | ||
196 | /* | 95 | static struct irqaction au1x_rtcmatch2_irqaction = { |
197 | * On early Au1000, sys_cpupll was write-only. Since these | 96 | .handler = au1x_rtcmatch2_irq, |
198 | * silicon versions of Au1000 are not sold by AMD, we don't bend | 97 | .flags = IRQF_DISABLED | IRQF_TIMER, |
199 | * over backwards trying to determine the frequency. | 98 | .name = "timer", |
200 | */ | 99 | .dev_id = &au1x_rtcmatch2_clockdev, |
201 | if (cur_cpu_spec[0]->cpu_pll_wo) | 100 | }; |
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 | 101 | ||
218 | void __init plat_time_init(void) | 102 | void __init plat_time_init(void) |
219 | { | 103 | { |
220 | unsigned int est_freq = calc_clock(); | 104 | struct clock_event_device *cd = &au1x_rtcmatch2_clockdev; |
221 | 105 | unsigned long t; | |
222 | est_freq += 5000; /* round */ | 106 | |
223 | est_freq -= est_freq%10000; | 107 | /* Check if firmware (YAMON, ...) has enabled 32kHz and clock |
224 | printk(KERN_INFO "CPU frequency %u.%02u MHz\n", | 108 | * has been detected. If so install the rtcmatch2 clocksource, |
225 | est_freq / 1000000, ((est_freq % 1000000) * 100) / 1000000); | 109 | * otherwise don't bother. Note that both bits being set is by |
226 | set_au1x00_speed(est_freq); | 110 | * no means a definite guarantee that the counters actually work |
227 | set_au1x00_lcd_clock(); /* program the LCD clock */ | 111 | * (the 32S bit seems to be stuck set to 1 once a single clock- |
112 | * edge is detected, hence the timeouts). | ||
113 | */ | ||
114 | if (CNTR_OK != (au_readl(SYS_COUNTER_CNTRL) & CNTR_OK)) | ||
115 | goto cntr_err; | ||
228 | 116 | ||
229 | #ifdef CONFIG_PM | ||
230 | /* | 117 | /* |
231 | * setup counter 0, since it keeps ticking after a | 118 | * setup counter 1 (RTC) to tick at full speed |
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 | */ | 119 | */ |
242 | if (no_au1xxx_32khz) | 120 | t = 0xffffff; |
243 | printk(KERN_WARNING "WARNING: no 32KHz clock found.\n"); | 121 | while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S) && t--) |
244 | else { | 122 | asm volatile ("nop"); |
245 | while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S); | 123 | if (!t) |
246 | au_writel(0, SYS_TOYWRITE); | 124 | goto cntr_err; |
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 | 125 | ||
254 | /* Setup match20 to interrupt once every HZ */ | 126 | au_writel(0, SYS_RTCTRIM); /* 32.768 kHz */ |
255 | last_pc0 = last_match20 = au_readl(SYS_TOYREAD); | 127 | au_sync(); |
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 | 128 | ||
261 | /* We can use the real 'wait' instruction. */ | 129 | t = 0xffffff; |
262 | allow_au1k_wait = 1; | 130 | while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S) && t--) |
263 | } | 131 | asm volatile ("nop"); |
132 | if (!t) | ||
133 | goto cntr_err; | ||
134 | au_writel(0, SYS_RTCWRITE); | ||
135 | au_sync(); | ||
264 | 136 | ||
265 | #endif | 137 | t = 0xffffff; |
138 | while ((au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S) && t--) | ||
139 | asm volatile ("nop"); | ||
140 | if (!t) | ||
141 | goto cntr_err; | ||
142 | |||
143 | /* register counter1 clocksource and event device */ | ||
144 | clocksource_set_clock(&au1x_counter1_clocksource, 32768); | ||
145 | clocksource_register(&au1x_counter1_clocksource); | ||
146 | |||
147 | cd->shift = 32; | ||
148 | cd->mult = div_sc(32768, NSEC_PER_SEC, cd->shift); | ||
149 | cd->max_delta_ns = clockevent_delta2ns(0xffffffff, cd); | ||
150 | cd->min_delta_ns = clockevent_delta2ns(8, cd); /* ~0.25ms */ | ||
151 | clockevents_register_device(cd); | ||
152 | setup_irq(AU1000_RTC_MATCH2_INT, &au1x_rtcmatch2_irqaction); | ||
153 | |||
154 | printk(KERN_INFO "Alchemy clocksource installed\n"); | ||
155 | |||
156 | /* can now use 'wait' */ | ||
157 | allow_au1k_wait = 1; | ||
158 | return; | ||
159 | |||
160 | cntr_err: | ||
161 | /* counters unusable, use C0 counter */ | ||
162 | r4k_clockevent_init(); | ||
163 | init_r4k_clocksource(); | ||
164 | allow_au1k_wait = 0; | ||
266 | } | 165 | } |