1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2f6d57916SPaul Mackerras /*
3f6d57916SPaul Mackerras  * Support for periodic interrupts (100 per second) and for getting
4f6d57916SPaul Mackerras  * the current time from the RTC on Power Macintoshes.
5f6d57916SPaul Mackerras  *
6f6d57916SPaul Mackerras  * We use the decrementer register for our periodic interrupts.
7f6d57916SPaul Mackerras  *
8f6d57916SPaul Mackerras  * Paul Mackerras	August 1996.
9f6d57916SPaul Mackerras  * Copyright (C) 1996 Paul Mackerras.
10f2783c15SPaul Mackerras  * Copyright (C) 2003-2005 Benjamin Herrenschmidt.
11f2783c15SPaul Mackerras  *
12f6d57916SPaul Mackerras  */
13f6d57916SPaul Mackerras #include <linux/errno.h>
14f6d57916SPaul Mackerras #include <linux/sched.h>
15f6d57916SPaul Mackerras #include <linux/kernel.h>
16f6d57916SPaul Mackerras #include <linux/param.h>
17f6d57916SPaul Mackerras #include <linux/string.h>
18f6d57916SPaul Mackerras #include <linux/mm.h>
19f6d57916SPaul Mackerras #include <linux/init.h>
20f6d57916SPaul Mackerras #include <linux/time.h>
21f6d57916SPaul Mackerras #include <linux/adb.h>
22f6d57916SPaul Mackerras #include <linux/cuda.h>
23f6d57916SPaul Mackerras #include <linux/pmu.h>
24f2783c15SPaul Mackerras #include <linux/interrupt.h>
25f6d57916SPaul Mackerras #include <linux/hardirq.h>
26f2783c15SPaul Mackerras #include <linux/rtc.h>
27e6f6390aSChristophe Leroy #include <linux/of_address.h>
28f6d57916SPaul Mackerras 
29*86582e61SBenjamin Gray #include <asm/early_ioremap.h>
30f6d57916SPaul Mackerras #include <asm/sections.h>
31f6d57916SPaul Mackerras #include <asm/machdep.h>
32f6d57916SPaul Mackerras #include <asm/time.h>
33f6d57916SPaul Mackerras #include <asm/nvram.h>
3435499c01SPaul Mackerras #include <asm/smu.h>
35f6d57916SPaul Mackerras 
362fff0f07SMathieu Malaterre #include "pmac.h"
372fff0f07SMathieu Malaterre 
38f2783c15SPaul Mackerras #undef DEBUG
39f2783c15SPaul Mackerras 
40f2783c15SPaul Mackerras #ifdef DEBUG
41f2783c15SPaul Mackerras #define DBG(x...) printk(x)
42f2783c15SPaul Mackerras #else
43f2783c15SPaul Mackerras #define DBG(x...)
44f2783c15SPaul Mackerras #endif
45f2783c15SPaul Mackerras 
4622db552bSArnd Bergmann /*
47f6d57916SPaul Mackerras  * Calibrate the decrementer frequency with the VIA timer 1.
48f6d57916SPaul Mackerras  */
49f6d57916SPaul Mackerras #define VIA_TIMER_FREQ_6	4700000	/* time 1 frequency * 6 */
50f6d57916SPaul Mackerras 
51f6d57916SPaul Mackerras /* VIA registers */
52f6d57916SPaul Mackerras #define RS		0x200		/* skip between registers */
53f6d57916SPaul Mackerras #define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
54f6d57916SPaul Mackerras #define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
55f6d57916SPaul Mackerras #define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
56f6d57916SPaul Mackerras #define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
57f6d57916SPaul Mackerras #define ACR		(11*RS)		/* Auxiliary control register */
58f6d57916SPaul Mackerras #define IFR		(13*RS)		/* Interrupt flag register */
59f6d57916SPaul Mackerras 
60f6d57916SPaul Mackerras /* Bits in ACR */
61f6d57916SPaul Mackerras #define T1MODE		0xc0		/* Timer 1 mode */
62f6d57916SPaul Mackerras #define T1MODE_CONT	0x40		/*  continuous interrupts */
63f6d57916SPaul Mackerras 
64f6d57916SPaul Mackerras /* Bits in IFR and IER */
65f6d57916SPaul Mackerras #define T1_INT		0x40		/* Timer 1 interrupt */
66f6d57916SPaul Mackerras 
pmac_time_init(void)67f2783c15SPaul Mackerras long __init pmac_time_init(void)
68f6d57916SPaul Mackerras {
69f6d57916SPaul Mackerras 	s32 delta = 0;
7020e07af7SFinn Thain #if defined(CONFIG_NVRAM) && defined(CONFIG_PPC32)
71f6d57916SPaul Mackerras 	int dst;
72f6d57916SPaul Mackerras 
73f6d57916SPaul Mackerras 	delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
74f6d57916SPaul Mackerras 	delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
75f6d57916SPaul Mackerras 	delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
76f6d57916SPaul Mackerras 	if (delta & 0x00800000UL)
77f6d57916SPaul Mackerras 		delta |= 0xFF000000UL;
78f6d57916SPaul Mackerras 	dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
79f6d57916SPaul Mackerras 	printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
80f6d57916SPaul Mackerras 		dst ? "on" : "off");
81f6d57916SPaul Mackerras #endif
8235499c01SPaul Mackerras 	return delta;
83f6d57916SPaul Mackerras }
84f6d57916SPaul Mackerras 
8535499c01SPaul Mackerras #ifdef CONFIG_PMAC_SMU
smu_get_time(void)865bfd6435SArnd Bergmann static time64_t smu_get_time(void)
8735499c01SPaul Mackerras {
8835499c01SPaul Mackerras 	struct rtc_time tm;
8935499c01SPaul Mackerras 
9035499c01SPaul Mackerras 	if (smu_get_rtc_time(&tm, 1))
9135499c01SPaul Mackerras 		return 0;
925bfd6435SArnd Bergmann 	return rtc_tm_to_time64(&tm);
9335499c01SPaul Mackerras }
9435499c01SPaul Mackerras #endif
9535499c01SPaul Mackerras 
96ba76cd57SPaul Mackerras /* Can't be __init, it's called when suspending and resuming */
pmac_get_boot_time(void)975bfd6435SArnd Bergmann time64_t pmac_get_boot_time(void)
9835499c01SPaul Mackerras {
9935499c01SPaul Mackerras 	/* Get the time from the RTC, used only at boot time */
10035499c01SPaul Mackerras 	switch (sys_ctrler) {
1010792a2c8SFinn Thain #ifdef CONFIG_ADB_CUDA
10235499c01SPaul Mackerras 	case SYS_CTRLER_CUDA:
10335499c01SPaul Mackerras 		return cuda_get_time();
1040792a2c8SFinn Thain #endif
1050792a2c8SFinn Thain #ifdef CONFIG_ADB_PMU
10635499c01SPaul Mackerras 	case SYS_CTRLER_PMU:
10735499c01SPaul Mackerras 		return pmu_get_time();
1080792a2c8SFinn Thain #endif
1090792a2c8SFinn Thain #ifdef CONFIG_PMAC_SMU
11035499c01SPaul Mackerras 	case SYS_CTRLER_SMU:
11135499c01SPaul Mackerras 		return smu_get_time();
1120792a2c8SFinn Thain #endif
113f6d57916SPaul Mackerras 	default:
114f6d57916SPaul Mackerras 		return 0;
115f6d57916SPaul Mackerras 	}
116f6d57916SPaul Mackerras }
117f6d57916SPaul Mackerras 
pmac_get_rtc_time(struct rtc_time * tm)11835499c01SPaul Mackerras void pmac_get_rtc_time(struct rtc_time *tm)
11935499c01SPaul Mackerras {
12035499c01SPaul Mackerras 	/* Get the time from the RTC, used only at boot time */
12135499c01SPaul Mackerras 	switch (sys_ctrler) {
1220792a2c8SFinn Thain #ifdef CONFIG_ADB_CUDA
12335499c01SPaul Mackerras 	case SYS_CTRLER_CUDA:
1240792a2c8SFinn Thain 		rtc_time64_to_tm(cuda_get_time(), tm);
12535499c01SPaul Mackerras 		break;
1260792a2c8SFinn Thain #endif
1270792a2c8SFinn Thain #ifdef CONFIG_ADB_PMU
12835499c01SPaul Mackerras 	case SYS_CTRLER_PMU:
1290792a2c8SFinn Thain 		rtc_time64_to_tm(pmu_get_time(), tm);
13035499c01SPaul Mackerras 		break;
1310792a2c8SFinn Thain #endif
1320792a2c8SFinn Thain #ifdef CONFIG_PMAC_SMU
13335499c01SPaul Mackerras 	case SYS_CTRLER_SMU:
13435499c01SPaul Mackerras 		smu_get_rtc_time(tm, 1);
13535499c01SPaul Mackerras 		break;
1360792a2c8SFinn Thain #endif
13735499c01SPaul Mackerras 	default:
13835499c01SPaul Mackerras 		;
13935499c01SPaul Mackerras 	}
14035499c01SPaul Mackerras }
14135499c01SPaul Mackerras 
pmac_set_rtc_time(struct rtc_time * tm)14235499c01SPaul Mackerras int pmac_set_rtc_time(struct rtc_time *tm)
14335499c01SPaul Mackerras {
14435499c01SPaul Mackerras 	switch (sys_ctrler) {
1450792a2c8SFinn Thain #ifdef CONFIG_ADB_CUDA
14635499c01SPaul Mackerras 	case SYS_CTRLER_CUDA:
14735499c01SPaul Mackerras 		return cuda_set_rtc_time(tm);
1480792a2c8SFinn Thain #endif
1490792a2c8SFinn Thain #ifdef CONFIG_ADB_PMU
15035499c01SPaul Mackerras 	case SYS_CTRLER_PMU:
15135499c01SPaul Mackerras 		return pmu_set_rtc_time(tm);
1520792a2c8SFinn Thain #endif
1530792a2c8SFinn Thain #ifdef CONFIG_PMAC_SMU
15435499c01SPaul Mackerras 	case SYS_CTRLER_SMU:
15535499c01SPaul Mackerras 		return smu_set_rtc_time(tm, 1);
1560792a2c8SFinn Thain #endif
15735499c01SPaul Mackerras 	default:
15835499c01SPaul Mackerras 		return -ENODEV;
15935499c01SPaul Mackerras 	}
16035499c01SPaul Mackerras }
16135499c01SPaul Mackerras 
16235499c01SPaul Mackerras #ifdef CONFIG_PPC32
163f6d57916SPaul Mackerras /*
164f6d57916SPaul Mackerras  * Calibrate the decrementer register using VIA timer 1.
165f6d57916SPaul Mackerras  * This is used both on powermacs and CHRP machines.
166f6d57916SPaul Mackerras  */
via_calibrate_decr(void)1672fff0f07SMathieu Malaterre static int __init via_calibrate_decr(void)
168f6d57916SPaul Mackerras {
169f6d57916SPaul Mackerras 	struct device_node *vias;
170f6d57916SPaul Mackerras 	volatile unsigned char __iomem *via;
171f6d57916SPaul Mackerras 	int count = VIA_TIMER_FREQ_6 / 100;
172f6d57916SPaul Mackerras 	unsigned int dstart, dend;
173cc5d0189SBenjamin Herrenschmidt 	struct resource rsrc;
174f6d57916SPaul Mackerras 
175cc5d0189SBenjamin Herrenschmidt 	vias = of_find_node_by_name(NULL, "via-cuda");
176afcb0654SNicolas Palix 	if (vias == NULL)
177cc5d0189SBenjamin Herrenschmidt 		vias = of_find_node_by_name(NULL, "via-pmu");
178afcb0654SNicolas Palix 	if (vias == NULL)
179cc5d0189SBenjamin Herrenschmidt 		vias = of_find_node_by_name(NULL, "via");
180afcb0654SNicolas Palix 	if (vias == NULL || of_address_to_resource(vias, 0, &rsrc)) {
181afcb0654SNicolas Palix 	        of_node_put(vias);
182f6d57916SPaul Mackerras 		return 0;
183afcb0654SNicolas Palix 	}
184afcb0654SNicolas Palix 	of_node_put(vias);
185*86582e61SBenjamin Gray 	via = early_ioremap(rsrc.start, resource_size(&rsrc));
186cc5d0189SBenjamin Herrenschmidt 	if (via == NULL) {
187cc5d0189SBenjamin Herrenschmidt 		printk(KERN_ERR "Failed to map VIA for timer calibration !\n");
188cc5d0189SBenjamin Herrenschmidt 		return 0;
189cc5d0189SBenjamin Herrenschmidt 	}
190f6d57916SPaul Mackerras 
191f6d57916SPaul Mackerras 	/* set timer 1 for continuous interrupts */
192f6d57916SPaul Mackerras 	out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
193f6d57916SPaul Mackerras 	/* set the counter to a small value */
194f6d57916SPaul Mackerras 	out_8(&via[T1CH], 2);
195f6d57916SPaul Mackerras 	/* set the latch to `count' */
196f6d57916SPaul Mackerras 	out_8(&via[T1LL], count);
197f6d57916SPaul Mackerras 	out_8(&via[T1LH], count >> 8);
198f6d57916SPaul Mackerras 	/* wait until it hits 0 */
199f6d57916SPaul Mackerras 	while ((in_8(&via[IFR]) & T1_INT) == 0)
200f6d57916SPaul Mackerras 		;
201f6d57916SPaul Mackerras 	dstart = get_dec();
202f6d57916SPaul Mackerras 	/* clear the interrupt & wait until it hits 0 again */
203f6d57916SPaul Mackerras 	in_8(&via[T1CL]);
204f6d57916SPaul Mackerras 	while ((in_8(&via[IFR]) & T1_INT) == 0)
205f6d57916SPaul Mackerras 		;
206f6d57916SPaul Mackerras 	dend = get_dec();
207f6d57916SPaul Mackerras 
2085d14a18dSPaul Mackerras 	ppc_tb_freq = (dstart - dend) * 100 / 6;
209f6d57916SPaul Mackerras 
210*86582e61SBenjamin Gray 	early_iounmap((void *)via, resource_size(&rsrc));
211f6d57916SPaul Mackerras 
212f6d57916SPaul Mackerras 	return 1;
213f6d57916SPaul Mackerras }
21435499c01SPaul Mackerras #endif
215f6d57916SPaul Mackerras 
216f6d57916SPaul Mackerras /*
217f6d57916SPaul Mackerras  * Query the OF and get the decr frequency.
218f6d57916SPaul Mackerras  */
pmac_calibrate_decr(void)21935499c01SPaul Mackerras void __init pmac_calibrate_decr(void)
220f6d57916SPaul Mackerras {
22135499c01SPaul Mackerras 	generic_calibrate_decr();
22235499c01SPaul Mackerras 
22335499c01SPaul Mackerras #ifdef CONFIG_PPC32
224f6d57916SPaul Mackerras 	/* We assume MacRISC2 machines have correct device-tree
225f6d57916SPaul Mackerras 	 * calibration. That's better since the VIA itself seems
226f6d57916SPaul Mackerras 	 * to be slightly off. --BenH
227f6d57916SPaul Mackerras 	 */
22871a157e8SGrant Likely 	if (!of_machine_is_compatible("MacRISC2") &&
22971a157e8SGrant Likely 	    !of_machine_is_compatible("MacRISC3") &&
23071a157e8SGrant Likely 	    !of_machine_is_compatible("MacRISC4"))
231f6d57916SPaul Mackerras 		if (via_calibrate_decr())
232f6d57916SPaul Mackerras 			return;
233f6d57916SPaul Mackerras 
234f6d57916SPaul Mackerras 	/* Special case: QuickSilver G4s seem to have a badly calibrated
235f6d57916SPaul Mackerras 	 * timebase-frequency in OF, VIA is much better on these. We should
236f6d57916SPaul Mackerras 	 * probably implement calibration based on the KL timer on these
237f6d57916SPaul Mackerras 	 * machines anyway... -BenH
238f6d57916SPaul Mackerras 	 */
23971a157e8SGrant Likely 	if (of_machine_is_compatible("PowerMac3,5"))
240f6d57916SPaul Mackerras 		if (via_calibrate_decr())
241f6d57916SPaul Mackerras 			return;
24235499c01SPaul Mackerras #endif
243f6d57916SPaul Mackerras }
244