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