Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/powermac/time.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Support for periodic interrupts (100 per second) and for getting
4
* the current time from the RTC on Power Macintoshes.
5
*
6
* We use the decrementer register for our periodic interrupts.
7
*
8
* Paul Mackerras August 1996.
9
* Copyright (C) 1996 Paul Mackerras.
10
* Copyright (C) 2003-2005 Benjamin Herrenschmidt.
11
*
12
*/
13
#include <linux/errno.h>
14
#include <linux/sched.h>
15
#include <linux/kernel.h>
16
#include <linux/param.h>
17
#include <linux/string.h>
18
#include <linux/string_choices.h>
19
#include <linux/mm.h>
20
#include <linux/init.h>
21
#include <linux/time.h>
22
#include <linux/adb.h>
23
#include <linux/cuda.h>
24
#include <linux/pmu.h>
25
#include <linux/interrupt.h>
26
#include <linux/hardirq.h>
27
#include <linux/rtc.h>
28
#include <linux/of_address.h>
29
30
#include <asm/early_ioremap.h>
31
#include <asm/sections.h>
32
#include <asm/machdep.h>
33
#include <asm/time.h>
34
#include <asm/nvram.h>
35
#include <asm/smu.h>
36
37
#include "pmac.h"
38
39
#undef DEBUG
40
41
#ifdef DEBUG
42
#define DBG(x...) printk(x)
43
#else
44
#define DBG(x...)
45
#endif
46
47
/*
48
* Calibrate the decrementer frequency with the VIA timer 1.
49
*/
50
#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */
51
52
/* VIA registers */
53
#define RS 0x200 /* skip between registers */
54
#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */
55
#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */
56
#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */
57
#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */
58
#define ACR (11*RS) /* Auxiliary control register */
59
#define IFR (13*RS) /* Interrupt flag register */
60
61
/* Bits in ACR */
62
#define T1MODE 0xc0 /* Timer 1 mode */
63
#define T1MODE_CONT 0x40 /* continuous interrupts */
64
65
/* Bits in IFR and IER */
66
#define T1_INT 0x40 /* Timer 1 interrupt */
67
68
long __init pmac_time_init(void)
69
{
70
s32 delta = 0;
71
#if defined(CONFIG_NVRAM) && defined(CONFIG_PPC32)
72
int dst;
73
74
delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
75
delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
76
delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
77
if (delta & 0x00800000UL)
78
delta |= 0xFF000000UL;
79
dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
80
printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
81
str_on_off(dst));
82
#endif
83
return delta;
84
}
85
86
#ifdef CONFIG_PMAC_SMU
87
static time64_t smu_get_time(void)
88
{
89
struct rtc_time tm;
90
91
if (smu_get_rtc_time(&tm, 1))
92
return 0;
93
return rtc_tm_to_time64(&tm);
94
}
95
#endif
96
97
/* Can't be __init, it's called when suspending and resuming */
98
time64_t pmac_get_boot_time(void)
99
{
100
/* Get the time from the RTC, used only at boot time */
101
switch (sys_ctrler) {
102
#ifdef CONFIG_ADB_CUDA
103
case SYS_CTRLER_CUDA:
104
return cuda_get_time();
105
#endif
106
#ifdef CONFIG_ADB_PMU
107
case SYS_CTRLER_PMU:
108
return pmu_get_time();
109
#endif
110
#ifdef CONFIG_PMAC_SMU
111
case SYS_CTRLER_SMU:
112
return smu_get_time();
113
#endif
114
default:
115
return 0;
116
}
117
}
118
119
void pmac_get_rtc_time(struct rtc_time *tm)
120
{
121
/* Get the time from the RTC, used only at boot time */
122
switch (sys_ctrler) {
123
#ifdef CONFIG_ADB_CUDA
124
case SYS_CTRLER_CUDA:
125
rtc_time64_to_tm(cuda_get_time(), tm);
126
break;
127
#endif
128
#ifdef CONFIG_ADB_PMU
129
case SYS_CTRLER_PMU:
130
rtc_time64_to_tm(pmu_get_time(), tm);
131
break;
132
#endif
133
#ifdef CONFIG_PMAC_SMU
134
case SYS_CTRLER_SMU:
135
smu_get_rtc_time(tm, 1);
136
break;
137
#endif
138
default:
139
;
140
}
141
}
142
143
int pmac_set_rtc_time(struct rtc_time *tm)
144
{
145
switch (sys_ctrler) {
146
#ifdef CONFIG_ADB_CUDA
147
case SYS_CTRLER_CUDA:
148
return cuda_set_rtc_time(tm);
149
#endif
150
#ifdef CONFIG_ADB_PMU
151
case SYS_CTRLER_PMU:
152
return pmu_set_rtc_time(tm);
153
#endif
154
#ifdef CONFIG_PMAC_SMU
155
case SYS_CTRLER_SMU:
156
return smu_set_rtc_time(tm, 1);
157
#endif
158
default:
159
return -ENODEV;
160
}
161
}
162
163
#ifdef CONFIG_PPC32
164
/*
165
* Calibrate the decrementer register using VIA timer 1.
166
* This is used both on powermacs and CHRP machines.
167
*/
168
static int __init via_calibrate_decr(void)
169
{
170
struct device_node *vias;
171
volatile unsigned char __iomem *via;
172
int count = VIA_TIMER_FREQ_6 / 100;
173
unsigned int dstart, dend;
174
struct resource rsrc;
175
176
vias = of_find_node_by_name(NULL, "via-cuda");
177
if (vias == NULL)
178
vias = of_find_node_by_name(NULL, "via-pmu");
179
if (vias == NULL)
180
vias = of_find_node_by_name(NULL, "via");
181
if (vias == NULL || of_address_to_resource(vias, 0, &rsrc)) {
182
of_node_put(vias);
183
return 0;
184
}
185
of_node_put(vias);
186
via = early_ioremap(rsrc.start, resource_size(&rsrc));
187
if (via == NULL) {
188
printk(KERN_ERR "Failed to map VIA for timer calibration !\n");
189
return 0;
190
}
191
192
/* set timer 1 for continuous interrupts */
193
out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);
194
/* set the counter to a small value */
195
out_8(&via[T1CH], 2);
196
/* set the latch to `count' */
197
out_8(&via[T1LL], count);
198
out_8(&via[T1LH], count >> 8);
199
/* wait until it hits 0 */
200
while ((in_8(&via[IFR]) & T1_INT) == 0)
201
;
202
dstart = get_dec();
203
/* clear the interrupt & wait until it hits 0 again */
204
in_8(&via[T1CL]);
205
while ((in_8(&via[IFR]) & T1_INT) == 0)
206
;
207
dend = get_dec();
208
209
ppc_tb_freq = (dstart - dend) * 100 / 6;
210
211
early_iounmap((void *)via, resource_size(&rsrc));
212
213
return 1;
214
}
215
#endif
216
217
/*
218
* Query the OF and get the decr frequency.
219
*/
220
void __init pmac_calibrate_decr(void)
221
{
222
generic_calibrate_decr();
223
224
#ifdef CONFIG_PPC32
225
/* We assume MacRISC2 machines have correct device-tree
226
* calibration. That's better since the VIA itself seems
227
* to be slightly off. --BenH
228
*/
229
if (!of_machine_is_compatible("MacRISC2") &&
230
!of_machine_is_compatible("MacRISC3") &&
231
!of_machine_is_compatible("MacRISC4"))
232
if (via_calibrate_decr())
233
return;
234
235
/* Special case: QuickSilver G4s seem to have a badly calibrated
236
* timebase-frequency in OF, VIA is much better on these. We should
237
* probably implement calibration based on the KL timer on these
238
* machines anyway... -BenH
239
*/
240
if (of_machine_is_compatible("PowerMac3,5"))
241
if (via_calibrate_decr())
242
return;
243
#endif
244
}
245
246