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>
27f6d57916SPaul Mackerras 
28f6d57916SPaul Mackerras #include <asm/sections.h>
29f6d57916SPaul Mackerras #include <asm/prom.h>
30f6d57916SPaul Mackerras #include <asm/io.h>
31f6d57916SPaul Mackerras #include <asm/pgtable.h>
32f6d57916SPaul Mackerras #include <asm/machdep.h>
33f6d57916SPaul Mackerras #include <asm/time.h>
34f6d57916SPaul Mackerras #include <asm/nvram.h>
3535499c01SPaul Mackerras #include <asm/smu.h>
36f6d57916SPaul Mackerras 
372fff0f07SMathieu Malaterre #include "pmac.h"
382fff0f07SMathieu Malaterre 
39f2783c15SPaul Mackerras #undef DEBUG
40f2783c15SPaul Mackerras 
41f2783c15SPaul Mackerras #ifdef DEBUG
42f2783c15SPaul Mackerras #define DBG(x...) printk(x)
43f2783c15SPaul Mackerras #else
44f2783c15SPaul Mackerras #define DBG(x...)
45f2783c15SPaul Mackerras #endif
46f2783c15SPaul Mackerras 
4722db552bSArnd Bergmann /*
48f6d57916SPaul Mackerras  * Calibrate the decrementer frequency with the VIA timer 1.
49f6d57916SPaul Mackerras  */
50f6d57916SPaul Mackerras #define VIA_TIMER_FREQ_6	4700000	/* time 1 frequency * 6 */
51f6d57916SPaul Mackerras 
52f6d57916SPaul Mackerras /* VIA registers */
53f6d57916SPaul Mackerras #define RS		0x200		/* skip between registers */
54f6d57916SPaul Mackerras #define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
55f6d57916SPaul Mackerras #define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
56f6d57916SPaul Mackerras #define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
57f6d57916SPaul Mackerras #define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
58f6d57916SPaul Mackerras #define ACR		(11*RS)		/* Auxiliary control register */
59f6d57916SPaul Mackerras #define IFR		(13*RS)		/* Interrupt flag register */
60f6d57916SPaul Mackerras 
61f6d57916SPaul Mackerras /* Bits in ACR */
62f6d57916SPaul Mackerras #define T1MODE		0xc0		/* Timer 1 mode */
63f6d57916SPaul Mackerras #define T1MODE_CONT	0x40		/*  continuous interrupts */
64f6d57916SPaul Mackerras 
65f6d57916SPaul Mackerras /* Bits in IFR and IER */
66f6d57916SPaul Mackerras #define T1_INT		0x40		/* Timer 1 interrupt */
67f6d57916SPaul Mackerras 
68f2783c15SPaul Mackerras long __init pmac_time_init(void)
69f6d57916SPaul Mackerras {
70f6d57916SPaul Mackerras 	s32 delta = 0;
7120e07af7SFinn Thain #if defined(CONFIG_NVRAM) && defined(CONFIG_PPC32)
72f6d57916SPaul Mackerras 	int dst;
73f6d57916SPaul Mackerras 
74f6d57916SPaul Mackerras 	delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
75f6d57916SPaul Mackerras 	delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
76f6d57916SPaul Mackerras 	delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
77f6d57916SPaul Mackerras 	if (delta & 0x00800000UL)
78f6d57916SPaul Mackerras 		delta |= 0xFF000000UL;
79f6d57916SPaul Mackerras 	dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
80f6d57916SPaul Mackerras 	printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
81f6d57916SPaul Mackerras 		dst ? "on" : "off");
82f6d57916SPaul Mackerras #endif
8335499c01SPaul Mackerras 	return delta;
84f6d57916SPaul Mackerras }
85f6d57916SPaul Mackerras 
8635499c01SPaul Mackerras #ifdef CONFIG_PMAC_SMU
875bfd6435SArnd Bergmann static time64_t smu_get_time(void)
8835499c01SPaul Mackerras {
8935499c01SPaul Mackerras 	struct rtc_time tm;
9035499c01SPaul Mackerras 
9135499c01SPaul Mackerras 	if (smu_get_rtc_time(&tm, 1))
9235499c01SPaul Mackerras 		return 0;
935bfd6435SArnd Bergmann 	return rtc_tm_to_time64(&tm);
9435499c01SPaul Mackerras }
9535499c01SPaul Mackerras #endif
9635499c01SPaul Mackerras 
97ba76cd57SPaul Mackerras /* Can't be __init, it's called when suspending and resuming */
985bfd6435SArnd Bergmann time64_t pmac_get_boot_time(void)
9935499c01SPaul Mackerras {
10035499c01SPaul Mackerras 	/* Get the time from the RTC, used only at boot time */
10135499c01SPaul Mackerras 	switch (sys_ctrler) {
1020792a2c8SFinn Thain #ifdef CONFIG_ADB_CUDA
10335499c01SPaul Mackerras 	case SYS_CTRLER_CUDA:
10435499c01SPaul Mackerras 		return cuda_get_time();
1050792a2c8SFinn Thain #endif
1060792a2c8SFinn Thain #ifdef CONFIG_ADB_PMU
10735499c01SPaul Mackerras 	case SYS_CTRLER_PMU:
10835499c01SPaul Mackerras 		return pmu_get_time();
1090792a2c8SFinn Thain #endif
1100792a2c8SFinn Thain #ifdef CONFIG_PMAC_SMU
11135499c01SPaul Mackerras 	case SYS_CTRLER_SMU:
11235499c01SPaul Mackerras 		return smu_get_time();
1130792a2c8SFinn Thain #endif
114f6d57916SPaul Mackerras 	default:
115f6d57916SPaul Mackerras 		return 0;
116f6d57916SPaul Mackerras 	}
117f6d57916SPaul Mackerras }
118f6d57916SPaul Mackerras 
11935499c01SPaul Mackerras void pmac_get_rtc_time(struct rtc_time *tm)
12035499c01SPaul Mackerras {
12135499c01SPaul Mackerras 	/* Get the time from the RTC, used only at boot time */
12235499c01SPaul Mackerras 	switch (sys_ctrler) {
1230792a2c8SFinn Thain #ifdef CONFIG_ADB_CUDA
12435499c01SPaul Mackerras 	case SYS_CTRLER_CUDA:
1250792a2c8SFinn Thain 		rtc_time64_to_tm(cuda_get_time(), tm);
12635499c01SPaul Mackerras 		break;
1270792a2c8SFinn Thain #endif
1280792a2c8SFinn Thain #ifdef CONFIG_ADB_PMU
12935499c01SPaul Mackerras 	case SYS_CTRLER_PMU:
1300792a2c8SFinn Thain 		rtc_time64_to_tm(pmu_get_time(), tm);
13135499c01SPaul Mackerras 		break;
1320792a2c8SFinn Thain #endif
1330792a2c8SFinn Thain #ifdef CONFIG_PMAC_SMU
13435499c01SPaul Mackerras 	case SYS_CTRLER_SMU:
13535499c01SPaul Mackerras 		smu_get_rtc_time(tm, 1);
13635499c01SPaul Mackerras 		break;
1370792a2c8SFinn Thain #endif
13835499c01SPaul Mackerras 	default:
13935499c01SPaul Mackerras 		;
14035499c01SPaul Mackerras 	}
14135499c01SPaul Mackerras }
14235499c01SPaul Mackerras 
14335499c01SPaul Mackerras int pmac_set_rtc_time(struct rtc_time *tm)
14435499c01SPaul Mackerras {
14535499c01SPaul Mackerras 	switch (sys_ctrler) {
1460792a2c8SFinn Thain #ifdef CONFIG_ADB_CUDA
14735499c01SPaul Mackerras 	case SYS_CTRLER_CUDA:
14835499c01SPaul Mackerras 		return cuda_set_rtc_time(tm);
1490792a2c8SFinn Thain #endif
1500792a2c8SFinn Thain #ifdef CONFIG_ADB_PMU
15135499c01SPaul Mackerras 	case SYS_CTRLER_PMU:
15235499c01SPaul Mackerras 		return pmu_set_rtc_time(tm);
1530792a2c8SFinn Thain #endif
1540792a2c8SFinn Thain #ifdef CONFIG_PMAC_SMU
15535499c01SPaul Mackerras 	case SYS_CTRLER_SMU:
15635499c01SPaul Mackerras 		return smu_set_rtc_time(tm, 1);
1570792a2c8SFinn Thain #endif
15835499c01SPaul Mackerras 	default:
15935499c01SPaul Mackerras 		return -ENODEV;
16035499c01SPaul Mackerras 	}
16135499c01SPaul Mackerras }
16235499c01SPaul Mackerras 
16335499c01SPaul Mackerras #ifdef CONFIG_PPC32
164f6d57916SPaul Mackerras /*
165f6d57916SPaul Mackerras  * Calibrate the decrementer register using VIA timer 1.
166f6d57916SPaul Mackerras  * This is used both on powermacs and CHRP machines.
167f6d57916SPaul Mackerras  */
1682fff0f07SMathieu Malaterre static int __init via_calibrate_decr(void)
169f6d57916SPaul Mackerras {
170f6d57916SPaul Mackerras 	struct device_node *vias;
171f6d57916SPaul Mackerras 	volatile unsigned char __iomem *via;
172f6d57916SPaul Mackerras 	int count = VIA_TIMER_FREQ_6 / 100;
173f6d57916SPaul Mackerras 	unsigned int dstart, dend;
174cc5d0189SBenjamin Herrenschmidt 	struct resource rsrc;
175f6d57916SPaul Mackerras 
176cc5d0189SBenjamin Herrenschmidt 	vias = of_find_node_by_name(NULL, "via-cuda");
177afcb0654SNicolas Palix 	if (vias == NULL)
178cc5d0189SBenjamin Herrenschmidt 		vias = of_find_node_by_name(NULL, "via-pmu");
179afcb0654SNicolas Palix 	if (vias == NULL)
180cc5d0189SBenjamin Herrenschmidt 		vias = of_find_node_by_name(NULL, "via");
181afcb0654SNicolas Palix 	if (vias == NULL || of_address_to_resource(vias, 0, &rsrc)) {
182afcb0654SNicolas Palix 	        of_node_put(vias);
183f6d57916SPaul Mackerras 		return 0;
184afcb0654SNicolas Palix 	}
185afcb0654SNicolas Palix 	of_node_put(vias);
18628f65c11SJoe Perches 	via = ioremap(rsrc.start, resource_size(&rsrc));
187cc5d0189SBenjamin Herrenschmidt 	if (via == NULL) {
188cc5d0189SBenjamin Herrenschmidt 		printk(KERN_ERR "Failed to map VIA for timer calibration !\n");
189cc5d0189SBenjamin Herrenschmidt 		return 0;
190cc5d0189SBenjamin Herrenschmidt 	}
191f6d57916SPaul Mackerras 
192f6d57916SPaul Mackerras 	/* set timer 1 for continuous interrupts */
193f6d57916SPaul Mackerras 	out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
194f6d57916SPaul Mackerras 	/* set the counter to a small value */
195f6d57916SPaul Mackerras 	out_8(&via[T1CH], 2);
196f6d57916SPaul Mackerras 	/* set the latch to `count' */
197f6d57916SPaul Mackerras 	out_8(&via[T1LL], count);
198f6d57916SPaul Mackerras 	out_8(&via[T1LH], count >> 8);
199f6d57916SPaul Mackerras 	/* wait until it hits 0 */
200f6d57916SPaul Mackerras 	while ((in_8(&via[IFR]) & T1_INT) == 0)
201f6d57916SPaul Mackerras 		;
202f6d57916SPaul Mackerras 	dstart = get_dec();
203f6d57916SPaul Mackerras 	/* clear the interrupt & wait until it hits 0 again */
204f6d57916SPaul Mackerras 	in_8(&via[T1CL]);
205f6d57916SPaul Mackerras 	while ((in_8(&via[IFR]) & T1_INT) == 0)
206f6d57916SPaul Mackerras 		;
207f6d57916SPaul Mackerras 	dend = get_dec();
208f6d57916SPaul Mackerras 
2095d14a18dSPaul Mackerras 	ppc_tb_freq = (dstart - dend) * 100 / 6;
210f6d57916SPaul Mackerras 
211f6d57916SPaul Mackerras 	iounmap(via);
212f6d57916SPaul Mackerras 
213f6d57916SPaul Mackerras 	return 1;
214f6d57916SPaul Mackerras }
21535499c01SPaul Mackerras #endif
216f6d57916SPaul Mackerras 
217f6d57916SPaul Mackerras /*
218f6d57916SPaul Mackerras  * Query the OF and get the decr frequency.
219f6d57916SPaul Mackerras  */
22035499c01SPaul Mackerras void __init pmac_calibrate_decr(void)
221f6d57916SPaul Mackerras {
22235499c01SPaul Mackerras 	generic_calibrate_decr();
22335499c01SPaul Mackerras 
22435499c01SPaul Mackerras #ifdef CONFIG_PPC32
225f6d57916SPaul Mackerras 	/* We assume MacRISC2 machines have correct device-tree
226f6d57916SPaul Mackerras 	 * calibration. That's better since the VIA itself seems
227f6d57916SPaul Mackerras 	 * to be slightly off. --BenH
228f6d57916SPaul Mackerras 	 */
22971a157e8SGrant Likely 	if (!of_machine_is_compatible("MacRISC2") &&
23071a157e8SGrant Likely 	    !of_machine_is_compatible("MacRISC3") &&
23171a157e8SGrant Likely 	    !of_machine_is_compatible("MacRISC4"))
232f6d57916SPaul Mackerras 		if (via_calibrate_decr())
233f6d57916SPaul Mackerras 			return;
234f6d57916SPaul Mackerras 
235f6d57916SPaul Mackerras 	/* Special case: QuickSilver G4s seem to have a badly calibrated
236f6d57916SPaul Mackerras 	 * timebase-frequency in OF, VIA is much better on these. We should
237f6d57916SPaul Mackerras 	 * probably implement calibration based on the KL timer on these
238f6d57916SPaul Mackerras 	 * machines anyway... -BenH
239f6d57916SPaul Mackerras 	 */
24071a157e8SGrant Likely 	if (of_machine_is_compatible("PowerMac3,5"))
241f6d57916SPaul Mackerras 		if (via_calibrate_decr())
242f6d57916SPaul Mackerras 			return;
24335499c01SPaul Mackerras #endif
244f6d57916SPaul Mackerras }
245