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