summaryrefslogtreecommitdiffstats
path: root/arch/ppc64/kernel/pmac_time.c
blob: f24827581dd7c24be919f126e5fe12e15850a39a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
 * Support for periodic interrupts (100 per second) and for getting
 * the current time from the RTC on Power Macintoshes.
 *
 * We use the decrementer register for our periodic interrupts.
 *
 * Paul Mackerras	August 1996.
 * Copyright (C) 1996 Paul Mackerras.
 * Copyright (C) 2003-2005 Benjamin Herrenschmidt.
 *
 */
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/time.h>
#include <linux/adb.h>
#include <linux/pmu.h>
#include <linux/interrupt.h>

#include <asm/sections.h>
#include <asm/prom.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/machdep.h>
#include <asm/time.h>
#include <asm/nvram.h>
#include <asm/smu.h>

#undef DEBUG

#ifdef DEBUG
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif

extern void setup_default_decr(void);

extern unsigned long ppc_tb_freq;
extern unsigned long ppc_proc_freq;

/* Apparently the RTC stores seconds since 1 Jan 1904 */
#define RTC_OFFSET	2082844800

/*
 * Calibrate the decrementer frequency with the VIA timer 1.
 */
#define VIA_TIMER_FREQ_6	4700000	/* time 1 frequency * 6 */

extern struct timezone sys_tz;
extern void to_tm(int tim, struct rtc_time * tm);

void __pmac pmac_get_rtc_time(struct rtc_time *tm)
{
	switch(sys_ctrler) {
#ifdef CONFIG_ADB_PMU
	case SYS_CTRLER_PMU: {
		/* TODO: Move that to a function in the PMU driver */
		struct adb_request req;
		unsigned int now;

		if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
			return;
		pmu_wait_complete(&req);
		if (req.reply_len != 4)
			printk(KERN_ERR "pmac_get_rtc_time: PMU returned a %d"
			       " bytes reply\n", req.reply_len);
		now = (req.reply[0] << 24) + (req.reply[1] << 16)
			+ (req.reply[2] << 8) + req.reply[3];
		DBG("get: %u -> %u\n", (int)now, (int)(now - RTC_OFFSET));
		now -= RTC_OFFSET;

		to_tm(now, tm);
		tm->tm_year -= 1900;
		tm->tm_mon -= 1;
	
		DBG("-> tm_mday: %d, tm_mon: %d, tm_year: %d, %d:%02d:%02d\n",
		    tm->tm_mday, tm->tm_mon, tm->tm_year,
		    tm->tm_hour, tm->tm_min, tm->tm_sec);
		break;
	}
#endif /* CONFIG_ADB_PMU */

#ifdef CONFIG_PMAC_SMU
	case SYS_CTRLER_SMU:
		smu_get_rtc_time(tm);
		break;
#endif /* CONFIG_PMAC_SMU */
	default:
		;
	}
}

int __pmac pmac_set_rtc_time(struct rtc_time *tm)
{
	switch(sys_ctrler) {
#ifdef CONFIG_ADB_PMU
	case SYS_CTRLER_PMU: {
		/* TODO: Move that to a function in the PMU driver */
		struct adb_request req;
		unsigned int nowtime;

		DBG("set: tm_mday: %d, tm_mon: %d, tm_year: %d,"
		    " %d:%02d:%02d\n",
		    tm->tm_mday, tm->tm_mon, tm->tm_year,
		    tm->tm_hour, tm->tm_min, tm->tm_sec);

		nowtime = mktime(tm->tm_year + 1900, tm->tm_mon + 1,
				 tm->tm_mday, tm->tm_hour, tm->tm_min,
				 tm->tm_sec);

		DBG("-> %u -> %u\n", (int)nowtime,
		    (int)(nowtime + RTC_OFFSET));
		nowtime += RTC_OFFSET;

		if (pmu_request(&req, NULL, 5, PMU_SET_RTC,
				nowtime >> 24, nowtime >> 16,
				nowtime >> 8, nowtime) < 0)
			return -ENXIO;
		pmu_wait_complete(&req);
		if (req.reply_len != 0)
			printk(KERN_ERR "pmac_set_rtc_time: PMU returned a %d"
			       " bytes reply\n", req.reply_len);
		return 0;
	}
#endif /* CONFIG_ADB_PMU */

#ifdef CONFIG_PMAC_SMU
	case SYS_CTRLER_SMU:
		return smu_set_rtc_time(tm);
#endif /* CONFIG_PMAC_SMU */
	default:
		return -ENODEV;
	}
}

void __init pmac_get_boot_time(struct rtc_time *tm)
{
	pmac_get_rtc_time(tm);

#ifdef disabled__CONFIG_NVRAM
	s32 delta = 0;
	int dst;
	
	delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
	delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
	delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
	if (delta & 0x00800000UL)
		delta |= 0xFF000000UL;
	dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
	printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
		dst ? "on" : "off");
#endif
}

/*
 * Query the OF and get the decr frequency.
 * This was taken from the pmac time_init() when merging the prep/pmac
 * time functions.
 */
void __init pmac_calibrate_decr(void)
{
	struct device_node *cpu;
	unsigned int freq, *fp;
	struct div_result divres;

	/*
	 * The cpu node should have a timebase-frequency property
	 * to tell us the rate at which the decrementer counts.
	 */
	cpu = find_type_devices("cpu");
	if (cpu == 0)
		panic("can't find cpu node in time_init");
	fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL);
	if (fp == 0)
		panic("can't get cpu timebase frequency");
	freq = *fp;
	printk("time_init: decrementer frequency = %u.%.6u MHz\n",
	       freq/1000000, freq%1000000);
	tb_ticks_per_jiffy = freq / HZ;
	tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
	tb_ticks_per_usec = freq / 1000000;
	tb_to_us = mulhwu_scale_factor(freq, 1000000);
	div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres );
	tb_to_xs = divres.result_low;
	ppc_tb_freq = freq;

	fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL);
	if (fp == 0)
		panic("can't get cpu processor frequency");
	ppc_proc_freq = *fp;

	setup_default_decr();
}