Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/8xx/m8xx_setup.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 1995 Linus Torvalds
4
* Adapted from 'alpha' version by Gary Thomas
5
* Modified by Cort Dougan ([email protected])
6
* Modified for MBX using prep/chrp/pmac functions by Dan ([email protected])
7
* Further modified for generic 8xx by Dan.
8
*/
9
10
/*
11
* bootup setup stuff..
12
*/
13
14
#include <linux/kernel.h>
15
#include <linux/interrupt.h>
16
#include <linux/init.h>
17
#include <linux/time.h>
18
#include <linux/rtc.h>
19
#include <linux/fsl_devices.h>
20
#include <linux/of.h>
21
#include <linux/of_irq.h>
22
23
#include <asm/io.h>
24
#include <asm/8xx_immap.h>
25
#include <mm/mmu_decl.h>
26
27
#include "pic.h"
28
29
#include "mpc8xx.h"
30
31
/* A place holder for time base interrupts, if they are ever enabled. */
32
static irqreturn_t timebase_interrupt(int irq, void *dev)
33
{
34
printk ("timebase_interrupt()\n");
35
36
return IRQ_HANDLED;
37
}
38
39
static int __init get_freq(char *name, unsigned long *val)
40
{
41
struct device_node *cpu;
42
const unsigned int *fp;
43
int found = 0;
44
45
/* The cpu node should have timebase and clock frequency properties */
46
cpu = of_get_cpu_node(0, NULL);
47
48
if (cpu) {
49
fp = of_get_property(cpu, name, NULL);
50
if (fp) {
51
found = 1;
52
*val = *fp;
53
}
54
55
of_node_put(cpu);
56
}
57
58
return found;
59
}
60
61
/* The decrementer counts at the system (internal) clock frequency divided by
62
* sixteen, or external oscillator divided by four. We force the processor
63
* to use system clock divided by sixteen.
64
*/
65
void __init mpc8xx_calibrate_decr(void)
66
{
67
struct device_node *cpu;
68
int irq, virq;
69
70
/* Unlock the SCCR. */
71
out_be32(&mpc8xx_immr->im_clkrstk.cark_sccrk, ~KAPWR_KEY);
72
out_be32(&mpc8xx_immr->im_clkrstk.cark_sccrk, KAPWR_KEY);
73
74
/* Force all 8xx processors to use divide by 16 processor clock. */
75
setbits32(&mpc8xx_immr->im_clkrst.car_sccr, 0x02000000);
76
77
/* Processor frequency is MHz.
78
*/
79
ppc_proc_freq = 50000000;
80
if (!get_freq("clock-frequency", &ppc_proc_freq))
81
printk(KERN_ERR "WARNING: Estimating processor frequency "
82
"(not found)\n");
83
84
ppc_tb_freq = ppc_proc_freq / 16;
85
printk("Decrementer Frequency = 0x%lx\n", ppc_tb_freq);
86
87
/* Perform some more timer/timebase initialization. This used
88
* to be done elsewhere, but other changes caused it to get
89
* called more than once....that is a bad thing.
90
*
91
* First, unlock all of the registers we are going to modify.
92
* To protect them from corruption during power down, registers
93
* that are maintained by keep alive power are "locked". To
94
* modify these registers we have to write the key value to
95
* the key location associated with the register.
96
* Some boards power up with these unlocked, while others
97
* are locked. Writing anything (including the unlock code?)
98
* to the unlocked registers will lock them again. So, here
99
* we guarantee the registers are locked, then we unlock them
100
* for our use.
101
*/
102
out_be32(&mpc8xx_immr->im_sitk.sitk_tbscrk, ~KAPWR_KEY);
103
out_be32(&mpc8xx_immr->im_sitk.sitk_rtcsck, ~KAPWR_KEY);
104
out_be32(&mpc8xx_immr->im_sitk.sitk_tbk, ~KAPWR_KEY);
105
out_be32(&mpc8xx_immr->im_sitk.sitk_tbscrk, KAPWR_KEY);
106
out_be32(&mpc8xx_immr->im_sitk.sitk_rtcsck, KAPWR_KEY);
107
out_be32(&mpc8xx_immr->im_sitk.sitk_tbk, KAPWR_KEY);
108
109
/* Disable the RTC one second and alarm interrupts. */
110
clrbits16(&mpc8xx_immr->im_sit.sit_rtcsc, (RTCSC_SIE | RTCSC_ALE));
111
112
/* Enable the RTC */
113
setbits16(&mpc8xx_immr->im_sit.sit_rtcsc, (RTCSC_RTF | RTCSC_RTE));
114
115
/* Enabling the decrementer also enables the timebase interrupts
116
* (or from the other point of view, to get decrementer interrupts
117
* we have to enable the timebase). The decrementer interrupt
118
* is wired into the vector table, nothing to do here for that.
119
*/
120
cpu = of_get_cpu_node(0, NULL);
121
virq= irq_of_parse_and_map(cpu, 0);
122
of_node_put(cpu);
123
irq = virq_to_hw(virq);
124
125
out_be16(&mpc8xx_immr->im_sit.sit_tbscr,
126
((1 << (7 - (irq / 2))) << 8) | (TBSCR_TBF | TBSCR_TBE));
127
128
if (request_irq(virq, timebase_interrupt, IRQF_NO_THREAD, "tbint",
129
NULL))
130
panic("Could not allocate timer IRQ!");
131
}
132
133
/* The RTC on the MPC8xx is an internal register.
134
* We want to protect this during power down, so we need to unlock,
135
* modify, and re-lock.
136
*/
137
138
int mpc8xx_set_rtc_time(struct rtc_time *tm)
139
{
140
time64_t time;
141
142
time = rtc_tm_to_time64(tm);
143
144
out_be32(&mpc8xx_immr->im_sitk.sitk_rtck, KAPWR_KEY);
145
out_be32(&mpc8xx_immr->im_sit.sit_rtc, (u32)time);
146
out_be32(&mpc8xx_immr->im_sitk.sitk_rtck, ~KAPWR_KEY);
147
148
return 0;
149
}
150
151
void mpc8xx_get_rtc_time(struct rtc_time *tm)
152
{
153
unsigned long data;
154
155
/* Get time from the RTC. */
156
data = in_be32(&mpc8xx_immr->im_sit.sit_rtc);
157
rtc_time64_to_tm(data, tm);
158
return;
159
}
160
161
void __noreturn mpc8xx_restart(char *cmd)
162
{
163
local_irq_disable();
164
165
setbits32(&mpc8xx_immr->im_clkrst.car_plprcr, 0x00000080);
166
/* Clear the ME bit in MSR to cause checkstop on machine check
167
*/
168
mtmsr(mfmsr() & ~0x1000);
169
170
in_8(&mpc8xx_immr->im_clkrst.res[0]);
171
panic("Restart failed\n");
172
}
173
174