18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Support for periodic interrupts (100 per second) and for getting
48c2ecf20Sopenharmony_ci * the current time from the RTC on Power Macintoshes.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * We use the decrementer register for our periodic interrupts.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Paul Mackerras	August 1996.
98c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras.
108c2ecf20Sopenharmony_ci * Copyright (C) 2003-2005 Benjamin Herrenschmidt.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci#include <linux/errno.h>
148c2ecf20Sopenharmony_ci#include <linux/sched.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/param.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include <linux/mm.h>
198c2ecf20Sopenharmony_ci#include <linux/init.h>
208c2ecf20Sopenharmony_ci#include <linux/time.h>
218c2ecf20Sopenharmony_ci#include <linux/adb.h>
228c2ecf20Sopenharmony_ci#include <linux/cuda.h>
238c2ecf20Sopenharmony_ci#include <linux/pmu.h>
248c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
258c2ecf20Sopenharmony_ci#include <linux/hardirq.h>
268c2ecf20Sopenharmony_ci#include <linux/rtc.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <asm/sections.h>
298c2ecf20Sopenharmony_ci#include <asm/prom.h>
308c2ecf20Sopenharmony_ci#include <asm/io.h>
318c2ecf20Sopenharmony_ci#include <asm/machdep.h>
328c2ecf20Sopenharmony_ci#include <asm/time.h>
338c2ecf20Sopenharmony_ci#include <asm/nvram.h>
348c2ecf20Sopenharmony_ci#include <asm/smu.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include "pmac.h"
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#undef DEBUG
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#ifdef DEBUG
418c2ecf20Sopenharmony_ci#define DBG(x...) printk(x)
428c2ecf20Sopenharmony_ci#else
438c2ecf20Sopenharmony_ci#define DBG(x...)
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/*
478c2ecf20Sopenharmony_ci * Calibrate the decrementer frequency with the VIA timer 1.
488c2ecf20Sopenharmony_ci */
498c2ecf20Sopenharmony_ci#define VIA_TIMER_FREQ_6	4700000	/* time 1 frequency * 6 */
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* VIA registers */
528c2ecf20Sopenharmony_ci#define RS		0x200		/* skip between registers */
538c2ecf20Sopenharmony_ci#define T1CL		(4*RS)		/* Timer 1 ctr/latch (low 8 bits) */
548c2ecf20Sopenharmony_ci#define T1CH		(5*RS)		/* Timer 1 counter (high 8 bits) */
558c2ecf20Sopenharmony_ci#define T1LL		(6*RS)		/* Timer 1 latch (low 8 bits) */
568c2ecf20Sopenharmony_ci#define T1LH		(7*RS)		/* Timer 1 latch (high 8 bits) */
578c2ecf20Sopenharmony_ci#define ACR		(11*RS)		/* Auxiliary control register */
588c2ecf20Sopenharmony_ci#define IFR		(13*RS)		/* Interrupt flag register */
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* Bits in ACR */
618c2ecf20Sopenharmony_ci#define T1MODE		0xc0		/* Timer 1 mode */
628c2ecf20Sopenharmony_ci#define T1MODE_CONT	0x40		/*  continuous interrupts */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* Bits in IFR and IER */
658c2ecf20Sopenharmony_ci#define T1_INT		0x40		/* Timer 1 interrupt */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cilong __init pmac_time_init(void)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	s32 delta = 0;
708c2ecf20Sopenharmony_ci#if defined(CONFIG_NVRAM) && defined(CONFIG_PPC32)
718c2ecf20Sopenharmony_ci	int dst;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
748c2ecf20Sopenharmony_ci	delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
758c2ecf20Sopenharmony_ci	delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
768c2ecf20Sopenharmony_ci	if (delta & 0x00800000UL)
778c2ecf20Sopenharmony_ci		delta |= 0xFF000000UL;
788c2ecf20Sopenharmony_ci	dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
798c2ecf20Sopenharmony_ci	printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
808c2ecf20Sopenharmony_ci		dst ? "on" : "off");
818c2ecf20Sopenharmony_ci#endif
828c2ecf20Sopenharmony_ci	return delta;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_SMU
868c2ecf20Sopenharmony_cistatic time64_t smu_get_time(void)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct rtc_time tm;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (smu_get_rtc_time(&tm, 1))
918c2ecf20Sopenharmony_ci		return 0;
928c2ecf20Sopenharmony_ci	return rtc_tm_to_time64(&tm);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci#endif
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* Can't be __init, it's called when suspending and resuming */
978c2ecf20Sopenharmony_citime64_t pmac_get_boot_time(void)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	/* Get the time from the RTC, used only at boot time */
1008c2ecf20Sopenharmony_ci	switch (sys_ctrler) {
1018c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_CUDA
1028c2ecf20Sopenharmony_ci	case SYS_CTRLER_CUDA:
1038c2ecf20Sopenharmony_ci		return cuda_get_time();
1048c2ecf20Sopenharmony_ci#endif
1058c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_PMU
1068c2ecf20Sopenharmony_ci	case SYS_CTRLER_PMU:
1078c2ecf20Sopenharmony_ci		return pmu_get_time();
1088c2ecf20Sopenharmony_ci#endif
1098c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_SMU
1108c2ecf20Sopenharmony_ci	case SYS_CTRLER_SMU:
1118c2ecf20Sopenharmony_ci		return smu_get_time();
1128c2ecf20Sopenharmony_ci#endif
1138c2ecf20Sopenharmony_ci	default:
1148c2ecf20Sopenharmony_ci		return 0;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_civoid pmac_get_rtc_time(struct rtc_time *tm)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	/* Get the time from the RTC, used only at boot time */
1218c2ecf20Sopenharmony_ci	switch (sys_ctrler) {
1228c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_CUDA
1238c2ecf20Sopenharmony_ci	case SYS_CTRLER_CUDA:
1248c2ecf20Sopenharmony_ci		rtc_time64_to_tm(cuda_get_time(), tm);
1258c2ecf20Sopenharmony_ci		break;
1268c2ecf20Sopenharmony_ci#endif
1278c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_PMU
1288c2ecf20Sopenharmony_ci	case SYS_CTRLER_PMU:
1298c2ecf20Sopenharmony_ci		rtc_time64_to_tm(pmu_get_time(), tm);
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci#endif
1328c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_SMU
1338c2ecf20Sopenharmony_ci	case SYS_CTRLER_SMU:
1348c2ecf20Sopenharmony_ci		smu_get_rtc_time(tm, 1);
1358c2ecf20Sopenharmony_ci		break;
1368c2ecf20Sopenharmony_ci#endif
1378c2ecf20Sopenharmony_ci	default:
1388c2ecf20Sopenharmony_ci		;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciint pmac_set_rtc_time(struct rtc_time *tm)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	switch (sys_ctrler) {
1458c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_CUDA
1468c2ecf20Sopenharmony_ci	case SYS_CTRLER_CUDA:
1478c2ecf20Sopenharmony_ci		return cuda_set_rtc_time(tm);
1488c2ecf20Sopenharmony_ci#endif
1498c2ecf20Sopenharmony_ci#ifdef CONFIG_ADB_PMU
1508c2ecf20Sopenharmony_ci	case SYS_CTRLER_PMU:
1518c2ecf20Sopenharmony_ci		return pmu_set_rtc_time(tm);
1528c2ecf20Sopenharmony_ci#endif
1538c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_SMU
1548c2ecf20Sopenharmony_ci	case SYS_CTRLER_SMU:
1558c2ecf20Sopenharmony_ci		return smu_set_rtc_time(tm, 1);
1568c2ecf20Sopenharmony_ci#endif
1578c2ecf20Sopenharmony_ci	default:
1588c2ecf20Sopenharmony_ci		return -ENODEV;
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
1638c2ecf20Sopenharmony_ci/*
1648c2ecf20Sopenharmony_ci * Calibrate the decrementer register using VIA timer 1.
1658c2ecf20Sopenharmony_ci * This is used both on powermacs and CHRP machines.
1668c2ecf20Sopenharmony_ci */
1678c2ecf20Sopenharmony_cistatic int __init via_calibrate_decr(void)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct device_node *vias;
1708c2ecf20Sopenharmony_ci	volatile unsigned char __iomem *via;
1718c2ecf20Sopenharmony_ci	int count = VIA_TIMER_FREQ_6 / 100;
1728c2ecf20Sopenharmony_ci	unsigned int dstart, dend;
1738c2ecf20Sopenharmony_ci	struct resource rsrc;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	vias = of_find_node_by_name(NULL, "via-cuda");
1768c2ecf20Sopenharmony_ci	if (vias == NULL)
1778c2ecf20Sopenharmony_ci		vias = of_find_node_by_name(NULL, "via-pmu");
1788c2ecf20Sopenharmony_ci	if (vias == NULL)
1798c2ecf20Sopenharmony_ci		vias = of_find_node_by_name(NULL, "via");
1808c2ecf20Sopenharmony_ci	if (vias == NULL || of_address_to_resource(vias, 0, &rsrc)) {
1818c2ecf20Sopenharmony_ci	        of_node_put(vias);
1828c2ecf20Sopenharmony_ci		return 0;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci	of_node_put(vias);
1858c2ecf20Sopenharmony_ci	via = ioremap(rsrc.start, resource_size(&rsrc));
1868c2ecf20Sopenharmony_ci	if (via == NULL) {
1878c2ecf20Sopenharmony_ci		printk(KERN_ERR "Failed to map VIA for timer calibration !\n");
1888c2ecf20Sopenharmony_ci		return 0;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	/* set timer 1 for continuous interrupts */
1928c2ecf20Sopenharmony_ci	out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
1938c2ecf20Sopenharmony_ci	/* set the counter to a small value */
1948c2ecf20Sopenharmony_ci	out_8(&via[T1CH], 2);
1958c2ecf20Sopenharmony_ci	/* set the latch to `count' */
1968c2ecf20Sopenharmony_ci	out_8(&via[T1LL], count);
1978c2ecf20Sopenharmony_ci	out_8(&via[T1LH], count >> 8);
1988c2ecf20Sopenharmony_ci	/* wait until it hits 0 */
1998c2ecf20Sopenharmony_ci	while ((in_8(&via[IFR]) & T1_INT) == 0)
2008c2ecf20Sopenharmony_ci		;
2018c2ecf20Sopenharmony_ci	dstart = get_dec();
2028c2ecf20Sopenharmony_ci	/* clear the interrupt & wait until it hits 0 again */
2038c2ecf20Sopenharmony_ci	in_8(&via[T1CL]);
2048c2ecf20Sopenharmony_ci	while ((in_8(&via[IFR]) & T1_INT) == 0)
2058c2ecf20Sopenharmony_ci		;
2068c2ecf20Sopenharmony_ci	dend = get_dec();
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	ppc_tb_freq = (dstart - dend) * 100 / 6;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	iounmap(via);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	return 1;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci#endif
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/*
2178c2ecf20Sopenharmony_ci * Query the OF and get the decr frequency.
2188c2ecf20Sopenharmony_ci */
2198c2ecf20Sopenharmony_civoid __init pmac_calibrate_decr(void)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	generic_calibrate_decr();
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC32
2248c2ecf20Sopenharmony_ci	/* We assume MacRISC2 machines have correct device-tree
2258c2ecf20Sopenharmony_ci	 * calibration. That's better since the VIA itself seems
2268c2ecf20Sopenharmony_ci	 * to be slightly off. --BenH
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	if (!of_machine_is_compatible("MacRISC2") &&
2298c2ecf20Sopenharmony_ci	    !of_machine_is_compatible("MacRISC3") &&
2308c2ecf20Sopenharmony_ci	    !of_machine_is_compatible("MacRISC4"))
2318c2ecf20Sopenharmony_ci		if (via_calibrate_decr())
2328c2ecf20Sopenharmony_ci			return;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* Special case: QuickSilver G4s seem to have a badly calibrated
2358c2ecf20Sopenharmony_ci	 * timebase-frequency in OF, VIA is much better on these. We should
2368c2ecf20Sopenharmony_ci	 * probably implement calibration based on the KL timer on these
2378c2ecf20Sopenharmony_ci	 * machines anyway... -BenH
2388c2ecf20Sopenharmony_ci	 */
2398c2ecf20Sopenharmony_ci	if (of_machine_is_compatible("PowerMac3,5"))
2408c2ecf20Sopenharmony_ci		if (via_calibrate_decr())
2418c2ecf20Sopenharmony_ci			return;
2428c2ecf20Sopenharmony_ci#endif
2438c2ecf20Sopenharmony_ci}
244