162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 462306a36Sopenharmony_ci * Adapted from 'alpha' version by Gary Thomas 562306a36Sopenharmony_ci * Modified by Cort Dougan (cort@cs.nmt.edu) 662306a36Sopenharmony_ci * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) 762306a36Sopenharmony_ci * Further modified for generic 8xx by Dan. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* 1162306a36Sopenharmony_ci * bootup setup stuff.. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/time.h> 1862306a36Sopenharmony_ci#include <linux/rtc.h> 1962306a36Sopenharmony_ci#include <linux/fsl_devices.h> 2062306a36Sopenharmony_ci#include <linux/of.h> 2162306a36Sopenharmony_ci#include <linux/of_irq.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/io.h> 2462306a36Sopenharmony_ci#include <asm/8xx_immap.h> 2562306a36Sopenharmony_ci#include <mm/mmu_decl.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "pic.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "mpc8xx.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* A place holder for time base interrupts, if they are ever enabled. */ 3262306a36Sopenharmony_cistatic irqreturn_t timebase_interrupt(int irq, void *dev) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci printk ("timebase_interrupt()\n"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci return IRQ_HANDLED; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int __init get_freq(char *name, unsigned long *val) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct device_node *cpu; 4262306a36Sopenharmony_ci const unsigned int *fp; 4362306a36Sopenharmony_ci int found = 0; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* The cpu node should have timebase and clock frequency properties */ 4662306a36Sopenharmony_ci cpu = of_get_cpu_node(0, NULL); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (cpu) { 4962306a36Sopenharmony_ci fp = of_get_property(cpu, name, NULL); 5062306a36Sopenharmony_ci if (fp) { 5162306a36Sopenharmony_ci found = 1; 5262306a36Sopenharmony_ci *val = *fp; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci of_node_put(cpu); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return found; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* The decrementer counts at the system (internal) clock frequency divided by 6262306a36Sopenharmony_ci * sixteen, or external oscillator divided by four. We force the processor 6362306a36Sopenharmony_ci * to use system clock divided by sixteen. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_civoid __init mpc8xx_calibrate_decr(void) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct device_node *cpu; 6862306a36Sopenharmony_ci int irq, virq; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* Unlock the SCCR. */ 7162306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_clkrstk.cark_sccrk, ~KAPWR_KEY); 7262306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_clkrstk.cark_sccrk, KAPWR_KEY); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci /* Force all 8xx processors to use divide by 16 processor clock. */ 7562306a36Sopenharmony_ci setbits32(&mpc8xx_immr->im_clkrst.car_sccr, 0x02000000); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* Processor frequency is MHz. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci ppc_proc_freq = 50000000; 8062306a36Sopenharmony_ci if (!get_freq("clock-frequency", &ppc_proc_freq)) 8162306a36Sopenharmony_ci printk(KERN_ERR "WARNING: Estimating processor frequency " 8262306a36Sopenharmony_ci "(not found)\n"); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci ppc_tb_freq = ppc_proc_freq / 16; 8562306a36Sopenharmony_ci printk("Decrementer Frequency = 0x%lx\n", ppc_tb_freq); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Perform some more timer/timebase initialization. This used 8862306a36Sopenharmony_ci * to be done elsewhere, but other changes caused it to get 8962306a36Sopenharmony_ci * called more than once....that is a bad thing. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * First, unlock all of the registers we are going to modify. 9262306a36Sopenharmony_ci * To protect them from corruption during power down, registers 9362306a36Sopenharmony_ci * that are maintained by keep alive power are "locked". To 9462306a36Sopenharmony_ci * modify these registers we have to write the key value to 9562306a36Sopenharmony_ci * the key location associated with the register. 9662306a36Sopenharmony_ci * Some boards power up with these unlocked, while others 9762306a36Sopenharmony_ci * are locked. Writing anything (including the unlock code?) 9862306a36Sopenharmony_ci * to the unlocked registers will lock them again. So, here 9962306a36Sopenharmony_ci * we guarantee the registers are locked, then we unlock them 10062306a36Sopenharmony_ci * for our use. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_tbscrk, ~KAPWR_KEY); 10362306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_rtcsck, ~KAPWR_KEY); 10462306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_tbk, ~KAPWR_KEY); 10562306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_tbscrk, KAPWR_KEY); 10662306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_rtcsck, KAPWR_KEY); 10762306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_tbk, KAPWR_KEY); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Disable the RTC one second and alarm interrupts. */ 11062306a36Sopenharmony_ci clrbits16(&mpc8xx_immr->im_sit.sit_rtcsc, (RTCSC_SIE | RTCSC_ALE)); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* Enable the RTC */ 11362306a36Sopenharmony_ci setbits16(&mpc8xx_immr->im_sit.sit_rtcsc, (RTCSC_RTF | RTCSC_RTE)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Enabling the decrementer also enables the timebase interrupts 11662306a36Sopenharmony_ci * (or from the other point of view, to get decrementer interrupts 11762306a36Sopenharmony_ci * we have to enable the timebase). The decrementer interrupt 11862306a36Sopenharmony_ci * is wired into the vector table, nothing to do here for that. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci cpu = of_get_cpu_node(0, NULL); 12162306a36Sopenharmony_ci virq= irq_of_parse_and_map(cpu, 0); 12262306a36Sopenharmony_ci of_node_put(cpu); 12362306a36Sopenharmony_ci irq = virq_to_hw(virq); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci out_be16(&mpc8xx_immr->im_sit.sit_tbscr, 12662306a36Sopenharmony_ci ((1 << (7 - (irq / 2))) << 8) | (TBSCR_TBF | TBSCR_TBE)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (request_irq(virq, timebase_interrupt, IRQF_NO_THREAD, "tbint", 12962306a36Sopenharmony_ci NULL)) 13062306a36Sopenharmony_ci panic("Could not allocate timer IRQ!"); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* The RTC on the MPC8xx is an internal register. 13462306a36Sopenharmony_ci * We want to protect this during power down, so we need to unlock, 13562306a36Sopenharmony_ci * modify, and re-lock. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ciint mpc8xx_set_rtc_time(struct rtc_time *tm) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci time64_t time; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci time = rtc_tm_to_time64(tm); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_rtck, KAPWR_KEY); 14562306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sit.sit_rtc, (u32)time); 14662306a36Sopenharmony_ci out_be32(&mpc8xx_immr->im_sitk.sitk_rtck, ~KAPWR_KEY); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid mpc8xx_get_rtc_time(struct rtc_time *tm) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci unsigned long data; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Get time from the RTC. */ 15662306a36Sopenharmony_ci data = in_be32(&mpc8xx_immr->im_sit.sit_rtc); 15762306a36Sopenharmony_ci rtc_time64_to_tm(data, tm); 15862306a36Sopenharmony_ci return; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_civoid __noreturn mpc8xx_restart(char *cmd) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci local_irq_disable(); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci setbits32(&mpc8xx_immr->im_clkrst.car_plprcr, 0x00000080); 16662306a36Sopenharmony_ci /* Clear the ME bit in MSR to cause checkstop on machine check 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_ci mtmsr(mfmsr() & ~0x1000); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci in_8(&mpc8xx_immr->im_clkrst.res[0]); 17162306a36Sopenharmony_ci panic("Restart failed\n"); 17262306a36Sopenharmony_ci} 173