]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
14cf11af PM |
2 | /* |
3 | * Support for periodic interrupts (100 per second) and for getting | |
4 | * the current time from the RTC on Power Macintoshes. | |
5 | * | |
6 | * We use the decrementer register for our periodic interrupts. | |
7 | * | |
8 | * Paul Mackerras August 1996. | |
9 | * Copyright (C) 1996 Paul Mackerras. | |
f2783c15 PM |
10 | * Copyright (C) 2003-2005 Benjamin Herrenschmidt. |
11 | * | |
14cf11af | 12 | */ |
14cf11af PM |
13 | #include <linux/errno.h> |
14 | #include <linux/sched.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/param.h> | |
17 | #include <linux/string.h> | |
18 | #include <linux/mm.h> | |
19 | #include <linux/init.h> | |
20 | #include <linux/time.h> | |
21 | #include <linux/adb.h> | |
22 | #include <linux/cuda.h> | |
23 | #include <linux/pmu.h> | |
f2783c15 | 24 | #include <linux/interrupt.h> |
14cf11af | 25 | #include <linux/hardirq.h> |
f2783c15 | 26 | #include <linux/rtc.h> |
14cf11af PM |
27 | |
28 | #include <asm/sections.h> | |
29 | #include <asm/prom.h> | |
14cf11af PM |
30 | #include <asm/io.h> |
31 | #include <asm/pgtable.h> | |
32 | #include <asm/machdep.h> | |
33 | #include <asm/time.h> | |
34 | #include <asm/nvram.h> | |
35499c01 | 35 | #include <asm/smu.h> |
14cf11af | 36 | |
f2783c15 PM |
37 | #undef DEBUG |
38 | ||
39 | #ifdef DEBUG | |
40 | #define DBG(x...) printk(x) | |
41 | #else | |
42 | #define DBG(x...) | |
43 | #endif | |
44 | ||
14cf11af PM |
45 | /* Apparently the RTC stores seconds since 1 Jan 1904 */ |
46 | #define RTC_OFFSET 2082844800 | |
47 | ||
48 | /* | |
49 | * Calibrate the decrementer frequency with the VIA timer 1. | |
50 | */ | |
51 | #define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ | |
52 | ||
53 | /* VIA registers */ | |
54 | #define RS 0x200 /* skip between registers */ | |
55 | #define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ | |
56 | #define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ | |
57 | #define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ | |
58 | #define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ | |
59 | #define ACR (11*RS) /* Auxiliary control register */ | |
60 | #define IFR (13*RS) /* Interrupt flag register */ | |
61 | ||
62 | /* Bits in ACR */ | |
63 | #define T1MODE 0xc0 /* Timer 1 mode */ | |
64 | #define T1MODE_CONT 0x40 /* continuous interrupts */ | |
65 | ||
66 | /* Bits in IFR and IER */ | |
67 | #define T1_INT 0x40 /* Timer 1 interrupt */ | |
68 | ||
f2783c15 | 69 | long __init pmac_time_init(void) |
14cf11af | 70 | { |
14cf11af | 71 | s32 delta = 0; |
35499c01 | 72 | #ifdef CONFIG_NVRAM |
14cf11af PM |
73 | int dst; |
74 | ||
75 | delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16; | |
76 | delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8; | |
77 | delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb); | |
78 | if (delta & 0x00800000UL) | |
79 | delta |= 0xFF000000UL; | |
80 | dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0); | |
81 | printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60, | |
82 | dst ? "on" : "off"); | |
14cf11af | 83 | #endif |
35499c01 | 84 | return delta; |
14cf11af PM |
85 | } |
86 | ||
62157620 | 87 | #if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) |
35499c01 PM |
88 | static void to_rtc_time(unsigned long now, struct rtc_time *tm) |
89 | { | |
90 | to_tm(now, tm); | |
91 | tm->tm_year -= 1900; | |
92 | tm->tm_mon -= 1; | |
93 | } | |
62157620 | 94 | #endif |
35499c01 | 95 | |
fedcf4c7 TB |
96 | #if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) || \ |
97 | defined(CONFIG_PMAC_SMU) | |
35499c01 PM |
98 | static unsigned long from_rtc_time(struct rtc_time *tm) |
99 | { | |
100 | return mktime(tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, | |
101 | tm->tm_hour, tm->tm_min, tm->tm_sec); | |
102 | } | |
fedcf4c7 | 103 | #endif |
35499c01 PM |
104 | |
105 | #ifdef CONFIG_ADB_CUDA | |
106 | static unsigned long cuda_get_time(void) | |
14cf11af | 107 | { |
14cf11af | 108 | struct adb_request req; |
0c37ec2a | 109 | unsigned int now; |
14cf11af | 110 | |
35499c01 PM |
111 | if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0) |
112 | return 0; | |
113 | while (!req.complete) | |
114 | cuda_poll(); | |
115 | if (req.reply_len != 7) | |
116 | printk(KERN_ERR "cuda_get_time: got %d byte reply\n", | |
117 | req.reply_len); | |
118 | now = (req.reply[3] << 24) + (req.reply[4] << 16) | |
119 | + (req.reply[5] << 8) + req.reply[6]; | |
0c37ec2a | 120 | return ((unsigned long)now) - RTC_OFFSET; |
35499c01 PM |
121 | } |
122 | ||
123 | #define cuda_get_rtc_time(tm) to_rtc_time(cuda_get_time(), (tm)) | |
124 | ||
125 | static int cuda_set_rtc_time(struct rtc_time *tm) | |
126 | { | |
127 | unsigned int nowtime; | |
128 | struct adb_request req; | |
129 | ||
130 | nowtime = from_rtc_time(tm) + RTC_OFFSET; | |
131 | if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME, | |
132 | nowtime >> 24, nowtime >> 16, nowtime >> 8, | |
133 | nowtime) < 0) | |
134 | return -ENXIO; | |
135 | while (!req.complete) | |
136 | cuda_poll(); | |
137 | if ((req.reply_len != 3) && (req.reply_len != 7)) | |
138 | printk(KERN_ERR "cuda_set_rtc_time: got %d byte reply\n", | |
139 | req.reply_len); | |
14cf11af PM |
140 | return 0; |
141 | } | |
142 | ||
35499c01 PM |
143 | #else |
144 | #define cuda_get_time() 0 | |
145 | #define cuda_get_rtc_time(tm) | |
146 | #define cuda_set_rtc_time(tm) 0 | |
147 | #endif | |
148 | ||
149 | #ifdef CONFIG_ADB_PMU | |
150 | static unsigned long pmu_get_time(void) | |
14cf11af | 151 | { |
35499c01 | 152 | struct adb_request req; |
0c37ec2a | 153 | unsigned int now; |
143a1dec | 154 | |
35499c01 PM |
155 | if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) |
156 | return 0; | |
157 | pmu_wait_complete(&req); | |
158 | if (req.reply_len != 4) | |
159 | printk(KERN_ERR "pmu_get_time: got %d byte reply from PMU\n", | |
160 | req.reply_len); | |
161 | now = (req.reply[0] << 24) + (req.reply[1] << 16) | |
162 | + (req.reply[2] << 8) + req.reply[3]; | |
0c37ec2a | 163 | return ((unsigned long)now) - RTC_OFFSET; |
143a1dec PM |
164 | } |
165 | ||
35499c01 PM |
166 | #define pmu_get_rtc_time(tm) to_rtc_time(pmu_get_time(), (tm)) |
167 | ||
168 | static int pmu_set_rtc_time(struct rtc_time *tm) | |
143a1dec | 169 | { |
35499c01 | 170 | unsigned int nowtime; |
14cf11af | 171 | struct adb_request req; |
35499c01 PM |
172 | |
173 | nowtime = from_rtc_time(tm) + RTC_OFFSET; | |
174 | if (pmu_request(&req, NULL, 5, PMU_SET_RTC, nowtime >> 24, | |
175 | nowtime >> 16, nowtime >> 8, nowtime) < 0) | |
176 | return -ENXIO; | |
177 | pmu_wait_complete(&req); | |
178 | if (req.reply_len != 0) | |
179 | printk(KERN_ERR "pmu_set_rtc_time: %d byte reply from PMU\n", | |
180 | req.reply_len); | |
181 | return 0; | |
182 | } | |
183 | ||
184 | #else | |
185 | #define pmu_get_time() 0 | |
186 | #define pmu_get_rtc_time(tm) | |
187 | #define pmu_set_rtc_time(tm) 0 | |
14cf11af PM |
188 | #endif |
189 | ||
35499c01 PM |
190 | #ifdef CONFIG_PMAC_SMU |
191 | static unsigned long smu_get_time(void) | |
192 | { | |
193 | struct rtc_time tm; | |
194 | ||
195 | if (smu_get_rtc_time(&tm, 1)) | |
196 | return 0; | |
197 | return from_rtc_time(&tm); | |
198 | } | |
199 | ||
200 | #else | |
201 | #define smu_get_time() 0 | |
202 | #define smu_get_rtc_time(tm, spin) | |
203 | #define smu_set_rtc_time(tm, spin) 0 | |
204 | #endif | |
14cf11af | 205 | |
ba76cd57 PM |
206 | /* Can't be __init, it's called when suspending and resuming */ |
207 | unsigned long pmac_get_boot_time(void) | |
35499c01 PM |
208 | { |
209 | /* Get the time from the RTC, used only at boot time */ | |
14cf11af | 210 | switch (sys_ctrler) { |
14cf11af | 211 | case SYS_CTRLER_CUDA: |
35499c01 | 212 | return cuda_get_time(); |
14cf11af | 213 | case SYS_CTRLER_PMU: |
35499c01 PM |
214 | return pmu_get_time(); |
215 | case SYS_CTRLER_SMU: | |
216 | return smu_get_time(); | |
14cf11af PM |
217 | default: |
218 | return 0; | |
219 | } | |
220 | } | |
221 | ||
35499c01 PM |
222 | void pmac_get_rtc_time(struct rtc_time *tm) |
223 | { | |
224 | /* Get the time from the RTC, used only at boot time */ | |
225 | switch (sys_ctrler) { | |
226 | case SYS_CTRLER_CUDA: | |
227 | cuda_get_rtc_time(tm); | |
228 | break; | |
229 | case SYS_CTRLER_PMU: | |
230 | pmu_get_rtc_time(tm); | |
231 | break; | |
232 | case SYS_CTRLER_SMU: | |
233 | smu_get_rtc_time(tm, 1); | |
234 | break; | |
235 | default: | |
236 | ; | |
237 | } | |
238 | } | |
239 | ||
240 | int pmac_set_rtc_time(struct rtc_time *tm) | |
241 | { | |
242 | switch (sys_ctrler) { | |
243 | case SYS_CTRLER_CUDA: | |
244 | return cuda_set_rtc_time(tm); | |
245 | case SYS_CTRLER_PMU: | |
246 | return pmu_set_rtc_time(tm); | |
247 | case SYS_CTRLER_SMU: | |
248 | return smu_set_rtc_time(tm, 1); | |
249 | default: | |
250 | return -ENODEV; | |
251 | } | |
252 | } | |
253 | ||
254 | #ifdef CONFIG_PPC32 | |
14cf11af PM |
255 | /* |
256 | * Calibrate the decrementer register using VIA timer 1. | |
257 | * This is used both on powermacs and CHRP machines. | |
258 | */ | |
35499c01 | 259 | int __init via_calibrate_decr(void) |
14cf11af PM |
260 | { |
261 | struct device_node *vias; | |
262 | volatile unsigned char __iomem *via; | |
263 | int count = VIA_TIMER_FREQ_6 / 100; | |
264 | unsigned int dstart, dend; | |
cc5d0189 | 265 | struct resource rsrc; |
14cf11af | 266 | |
cc5d0189 | 267 | vias = of_find_node_by_name(NULL, "via-cuda"); |
afcb0654 | 268 | if (vias == NULL) |
cc5d0189 | 269 | vias = of_find_node_by_name(NULL, "via-pmu"); |
afcb0654 | 270 | if (vias == NULL) |
cc5d0189 | 271 | vias = of_find_node_by_name(NULL, "via"); |
afcb0654 NP |
272 | if (vias == NULL || of_address_to_resource(vias, 0, &rsrc)) { |
273 | of_node_put(vias); | |
14cf11af | 274 | return 0; |
afcb0654 NP |
275 | } |
276 | of_node_put(vias); | |
28f65c11 | 277 | via = ioremap(rsrc.start, resource_size(&rsrc)); |
cc5d0189 BH |
278 | if (via == NULL) { |
279 | printk(KERN_ERR "Failed to map VIA for timer calibration !\n"); | |
280 | return 0; | |
281 | } | |
14cf11af PM |
282 | |
283 | /* set timer 1 for continuous interrupts */ | |
284 | out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT); | |
285 | /* set the counter to a small value */ | |
286 | out_8(&via[T1CH], 2); | |
287 | /* set the latch to `count' */ | |
288 | out_8(&via[T1LL], count); | |
289 | out_8(&via[T1LH], count >> 8); | |
290 | /* wait until it hits 0 */ | |
291 | while ((in_8(&via[IFR]) & T1_INT) == 0) | |
292 | ; | |
293 | dstart = get_dec(); | |
294 | /* clear the interrupt & wait until it hits 0 again */ | |
295 | in_8(&via[T1CL]); | |
296 | while ((in_8(&via[IFR]) & T1_INT) == 0) | |
297 | ; | |
298 | dend = get_dec(); | |
299 | ||
5d14a18d | 300 | ppc_tb_freq = (dstart - dend) * 100 / 6; |
14cf11af PM |
301 | |
302 | iounmap(via); | |
afcb0654 | 303 | |
14cf11af PM |
304 | return 1; |
305 | } | |
35499c01 | 306 | #endif |
14cf11af | 307 | |
14cf11af PM |
308 | /* |
309 | * Query the OF and get the decr frequency. | |
14cf11af | 310 | */ |
35499c01 | 311 | void __init pmac_calibrate_decr(void) |
14cf11af | 312 | { |
35499c01 PM |
313 | generic_calibrate_decr(); |
314 | ||
315 | #ifdef CONFIG_PPC32 | |
14cf11af PM |
316 | /* We assume MacRISC2 machines have correct device-tree |
317 | * calibration. That's better since the VIA itself seems | |
318 | * to be slightly off. --BenH | |
319 | */ | |
71a157e8 GL |
320 | if (!of_machine_is_compatible("MacRISC2") && |
321 | !of_machine_is_compatible("MacRISC3") && | |
322 | !of_machine_is_compatible("MacRISC4")) | |
14cf11af PM |
323 | if (via_calibrate_decr()) |
324 | return; | |
325 | ||
326 | /* Special case: QuickSilver G4s seem to have a badly calibrated | |
327 | * timebase-frequency in OF, VIA is much better on these. We should | |
328 | * probably implement calibration based on the KL timer on these | |
329 | * machines anyway... -BenH | |
330 | */ | |
71a157e8 | 331 | if (of_machine_is_compatible("PowerMac3,5")) |
14cf11af PM |
332 | if (via_calibrate_decr()) |
333 | return; | |
35499c01 | 334 | #endif |
14cf11af | 335 | } |