18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 48c2ecf20Sopenharmony_ci * Adapted from 'alpha' version by Gary Thomas 58c2ecf20Sopenharmony_ci * Modified by Cort Dougan (cort@cs.nmt.edu) 68c2ecf20Sopenharmony_ci * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) 78c2ecf20Sopenharmony_ci * Further modified for generic 8xx by Dan. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * bootup setup stuff.. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/time.h> 188c2ecf20Sopenharmony_ci#include <linux/rtc.h> 198c2ecf20Sopenharmony_ci#include <linux/fsl_devices.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <asm/io.h> 228c2ecf20Sopenharmony_ci#include <asm/8xx_immap.h> 238c2ecf20Sopenharmony_ci#include <asm/prom.h> 248c2ecf20Sopenharmony_ci#include <asm/fs_pd.h> 258c2ecf20Sopenharmony_ci#include <mm/mmu_decl.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "pic.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#include "mpc8xx.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciextern int cpm_pic_init(void); 328c2ecf20Sopenharmony_ciextern int cpm_get_irq(void); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* A place holder for time base interrupts, if they are ever enabled. */ 358c2ecf20Sopenharmony_cistatic irqreturn_t timebase_interrupt(int irq, void *dev) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci printk ("timebase_interrupt()\n"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return IRQ_HANDLED; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* per-board overridable init_internal_rtc() function. */ 438c2ecf20Sopenharmony_civoid __init __attribute__ ((weak)) 448c2ecf20Sopenharmony_ciinit_internal_rtc(void) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci sit8xx_t __iomem *sys_tmr = immr_map(im_sit); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Disable the RTC one second and alarm interrupts. */ 498c2ecf20Sopenharmony_ci clrbits16(&sys_tmr->sit_rtcsc, (RTCSC_SIE | RTCSC_ALE)); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Enable the RTC */ 528c2ecf20Sopenharmony_ci setbits16(&sys_tmr->sit_rtcsc, (RTCSC_RTF | RTCSC_RTE)); 538c2ecf20Sopenharmony_ci immr_unmap(sys_tmr); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int __init get_freq(char *name, unsigned long *val) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct device_node *cpu; 598c2ecf20Sopenharmony_ci const unsigned int *fp; 608c2ecf20Sopenharmony_ci int found = 0; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* The cpu node should have timebase and clock frequency properties */ 638c2ecf20Sopenharmony_ci cpu = of_get_cpu_node(0, NULL); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (cpu) { 668c2ecf20Sopenharmony_ci fp = of_get_property(cpu, name, NULL); 678c2ecf20Sopenharmony_ci if (fp) { 688c2ecf20Sopenharmony_ci found = 1; 698c2ecf20Sopenharmony_ci *val = *fp; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci of_node_put(cpu); 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return found; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* The decrementer counts at the system (internal) clock frequency divided by 798c2ecf20Sopenharmony_ci * sixteen, or external oscillator divided by four. We force the processor 808c2ecf20Sopenharmony_ci * to use system clock divided by sixteen. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_civoid __init mpc8xx_calibrate_decr(void) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct device_node *cpu; 858c2ecf20Sopenharmony_ci cark8xx_t __iomem *clk_r1; 868c2ecf20Sopenharmony_ci car8xx_t __iomem *clk_r2; 878c2ecf20Sopenharmony_ci sitk8xx_t __iomem *sys_tmr1; 888c2ecf20Sopenharmony_ci sit8xx_t __iomem *sys_tmr2; 898c2ecf20Sopenharmony_ci int irq, virq; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci clk_r1 = immr_map(im_clkrstk); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Unlock the SCCR. */ 948c2ecf20Sopenharmony_ci out_be32(&clk_r1->cark_sccrk, ~KAPWR_KEY); 958c2ecf20Sopenharmony_ci out_be32(&clk_r1->cark_sccrk, KAPWR_KEY); 968c2ecf20Sopenharmony_ci immr_unmap(clk_r1); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* Force all 8xx processors to use divide by 16 processor clock. */ 998c2ecf20Sopenharmony_ci clk_r2 = immr_map(im_clkrst); 1008c2ecf20Sopenharmony_ci setbits32(&clk_r2->car_sccr, 0x02000000); 1018c2ecf20Sopenharmony_ci immr_unmap(clk_r2); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Processor frequency is MHz. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci ppc_proc_freq = 50000000; 1068c2ecf20Sopenharmony_ci if (!get_freq("clock-frequency", &ppc_proc_freq)) 1078c2ecf20Sopenharmony_ci printk(KERN_ERR "WARNING: Estimating processor frequency " 1088c2ecf20Sopenharmony_ci "(not found)\n"); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ppc_tb_freq = ppc_proc_freq / 16; 1118c2ecf20Sopenharmony_ci printk("Decrementer Frequency = 0x%lx\n", ppc_tb_freq); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Perform some more timer/timebase initialization. This used 1148c2ecf20Sopenharmony_ci * to be done elsewhere, but other changes caused it to get 1158c2ecf20Sopenharmony_ci * called more than once....that is a bad thing. 1168c2ecf20Sopenharmony_ci * 1178c2ecf20Sopenharmony_ci * First, unlock all of the registers we are going to modify. 1188c2ecf20Sopenharmony_ci * To protect them from corruption during power down, registers 1198c2ecf20Sopenharmony_ci * that are maintained by keep alive power are "locked". To 1208c2ecf20Sopenharmony_ci * modify these registers we have to write the key value to 1218c2ecf20Sopenharmony_ci * the key location associated with the register. 1228c2ecf20Sopenharmony_ci * Some boards power up with these unlocked, while others 1238c2ecf20Sopenharmony_ci * are locked. Writing anything (including the unlock code?) 1248c2ecf20Sopenharmony_ci * to the unlocked registers will lock them again. So, here 1258c2ecf20Sopenharmony_ci * we guarantee the registers are locked, then we unlock them 1268c2ecf20Sopenharmony_ci * for our use. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci sys_tmr1 = immr_map(im_sitk); 1298c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_tbscrk, ~KAPWR_KEY); 1308c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_rtcsck, ~KAPWR_KEY); 1318c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_tbk, ~KAPWR_KEY); 1328c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_tbscrk, KAPWR_KEY); 1338c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_rtcsck, KAPWR_KEY); 1348c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_tbk, KAPWR_KEY); 1358c2ecf20Sopenharmony_ci immr_unmap(sys_tmr1); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci init_internal_rtc(); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci /* Enabling the decrementer also enables the timebase interrupts 1408c2ecf20Sopenharmony_ci * (or from the other point of view, to get decrementer interrupts 1418c2ecf20Sopenharmony_ci * we have to enable the timebase). The decrementer interrupt 1428c2ecf20Sopenharmony_ci * is wired into the vector table, nothing to do here for that. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci cpu = of_get_cpu_node(0, NULL); 1458c2ecf20Sopenharmony_ci virq= irq_of_parse_and_map(cpu, 0); 1468c2ecf20Sopenharmony_ci of_node_put(cpu); 1478c2ecf20Sopenharmony_ci irq = virq_to_hw(virq); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci sys_tmr2 = immr_map(im_sit); 1508c2ecf20Sopenharmony_ci out_be16(&sys_tmr2->sit_tbscr, ((1 << (7 - (irq/2))) << 8) | 1518c2ecf20Sopenharmony_ci (TBSCR_TBF | TBSCR_TBE)); 1528c2ecf20Sopenharmony_ci immr_unmap(sys_tmr2); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (request_irq(virq, timebase_interrupt, IRQF_NO_THREAD, "tbint", 1558c2ecf20Sopenharmony_ci NULL)) 1568c2ecf20Sopenharmony_ci panic("Could not allocate timer IRQ!"); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci/* The RTC on the MPC8xx is an internal register. 1608c2ecf20Sopenharmony_ci * We want to protect this during power down, so we need to unlock, 1618c2ecf20Sopenharmony_ci * modify, and re-lock. 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciint mpc8xx_set_rtc_time(struct rtc_time *tm) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci sitk8xx_t __iomem *sys_tmr1; 1678c2ecf20Sopenharmony_ci sit8xx_t __iomem *sys_tmr2; 1688c2ecf20Sopenharmony_ci time64_t time; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci sys_tmr1 = immr_map(im_sitk); 1718c2ecf20Sopenharmony_ci sys_tmr2 = immr_map(im_sit); 1728c2ecf20Sopenharmony_ci time = rtc_tm_to_time64(tm); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_rtck, KAPWR_KEY); 1758c2ecf20Sopenharmony_ci out_be32(&sys_tmr2->sit_rtc, (u32)time); 1768c2ecf20Sopenharmony_ci out_be32(&sys_tmr1->sitk_rtck, ~KAPWR_KEY); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci immr_unmap(sys_tmr2); 1798c2ecf20Sopenharmony_ci immr_unmap(sys_tmr1); 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid mpc8xx_get_rtc_time(struct rtc_time *tm) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci unsigned long data; 1868c2ecf20Sopenharmony_ci sit8xx_t __iomem *sys_tmr = immr_map(im_sit); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Get time from the RTC. */ 1898c2ecf20Sopenharmony_ci data = in_be32(&sys_tmr->sit_rtc); 1908c2ecf20Sopenharmony_ci rtc_time64_to_tm(data, tm); 1918c2ecf20Sopenharmony_ci immr_unmap(sys_tmr); 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid __noreturn mpc8xx_restart(char *cmd) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci car8xx_t __iomem *clk_r = immr_map(im_clkrst); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci local_irq_disable(); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci setbits32(&clk_r->car_plprcr, 0x00000080); 2038c2ecf20Sopenharmony_ci /* Clear the ME bit in MSR to cause checkstop on machine check 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ci mtmsr(mfmsr() & ~0x1000); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci in_8(&clk_r->res[0]); 2088c2ecf20Sopenharmony_ci panic("Restart failed\n"); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void cpm_cascade(struct irq_desc *desc) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci generic_handle_irq(cpm_get_irq()); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* Initialize the internal interrupt controllers. The number of 2178c2ecf20Sopenharmony_ci * interrupts supported can vary with the processor type, and the 2188c2ecf20Sopenharmony_ci * 82xx family can have up to 64. 2198c2ecf20Sopenharmony_ci * External interrupts can be either edge or level triggered, and 2208c2ecf20Sopenharmony_ci * need to be initialized by the appropriate driver. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_civoid __init mpc8xx_pics_init(void) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci int irq; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (mpc8xx_pic_init()) { 2278c2ecf20Sopenharmony_ci printk(KERN_ERR "Failed interrupt 8xx controller initialization\n"); 2288c2ecf20Sopenharmony_ci return; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci irq = cpm_pic_init(); 2328c2ecf20Sopenharmony_ci if (irq) 2338c2ecf20Sopenharmony_ci irq_set_chained_handler(irq, cpm_cascade); 2348c2ecf20Sopenharmony_ci} 235