diff options
Diffstat (limited to 'arch/ppc64/kernel/pmac_time.c')
-rw-r--r-- | arch/ppc64/kernel/pmac_time.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/pmac_time.c b/arch/ppc64/kernel/pmac_time.c new file mode 100644 index 000000000000..f24827581dd7 --- /dev/null +++ b/arch/ppc64/kernel/pmac_time.c | |||
@@ -0,0 +1,201 @@ | |||
1 | /* | ||
2 | * Support for periodic interrupts (100 per second) and for getting | ||
3 | * the current time from the RTC on Power Macintoshes. | ||
4 | * | ||
5 | * We use the decrementer register for our periodic interrupts. | ||
6 | * | ||
7 | * Paul Mackerras August 1996. | ||
8 | * Copyright (C) 1996 Paul Mackerras. | ||
9 | * Copyright (C) 2003-2005 Benjamin Herrenschmidt. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/config.h> | ||
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/mm.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/time.h> | ||
21 | #include <linux/adb.h> | ||
22 | #include <linux/pmu.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | |||
25 | #include <asm/sections.h> | ||
26 | #include <asm/prom.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/pgtable.h> | ||
30 | #include <asm/machdep.h> | ||
31 | #include <asm/time.h> | ||
32 | #include <asm/nvram.h> | ||
33 | #include <asm/smu.h> | ||
34 | |||
35 | #undef DEBUG | ||
36 | |||
37 | #ifdef DEBUG | ||
38 | #define DBG(x...) printk(x) | ||
39 | #else | ||
40 | #define DBG(x...) | ||
41 | #endif | ||
42 | |||
43 | extern void setup_default_decr(void); | ||
44 | |||
45 | extern unsigned long ppc_tb_freq; | ||
46 | extern unsigned long ppc_proc_freq; | ||
47 | |||
48 | /* Apparently the RTC stores seconds since 1 Jan 1904 */ | ||
49 | #define RTC_OFFSET 2082844800 | ||
50 | |||
51 | /* | ||
52 | * Calibrate the decrementer frequency with the VIA timer 1. | ||
53 | */ | ||
54 | #define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */ | ||
55 | |||
56 | extern struct timezone sys_tz; | ||
57 | extern void to_tm(int tim, struct rtc_time * tm); | ||
58 | |||
59 | void __pmac pmac_get_rtc_time(struct rtc_time *tm) | ||
60 | { | ||
61 | switch(sys_ctrler) { | ||
62 | #ifdef CONFIG_ADB_PMU | ||
63 | case SYS_CTRLER_PMU: { | ||
64 | /* TODO: Move that to a function in the PMU driver */ | ||
65 | struct adb_request req; | ||
66 | unsigned int now; | ||
67 | |||
68 | if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0) | ||
69 | return; | ||
70 | pmu_wait_complete(&req); | ||
71 | if (req.reply_len != 4) | ||
72 | printk(KERN_ERR "pmac_get_rtc_time: PMU returned a %d" | ||
73 | " bytes reply\n", req.reply_len); | ||
74 | now = (req.reply[0] << 24) + (req.reply[1] << 16) | ||
75 | + (req.reply[2] << 8) + req.reply[3]; | ||
76 | DBG("get: %u -> %u\n", (int)now, (int)(now - RTC_OFFSET)); | ||
77 | now -= RTC_OFFSET; | ||
78 | |||
79 | to_tm(now, tm); | ||
80 | tm->tm_year -= 1900; | ||
81 | tm->tm_mon -= 1; | ||
82 | |||
83 | DBG("-> tm_mday: %d, tm_mon: %d, tm_year: %d, %d:%02d:%02d\n", | ||
84 | tm->tm_mday, tm->tm_mon, tm->tm_year, | ||
85 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
86 | break; | ||
87 | } | ||
88 | #endif /* CONFIG_ADB_PMU */ | ||
89 | |||
90 | #ifdef CONFIG_PMAC_SMU | ||
91 | case SYS_CTRLER_SMU: | ||
92 | smu_get_rtc_time(tm); | ||
93 | break; | ||
94 | #endif /* CONFIG_PMAC_SMU */ | ||
95 | default: | ||
96 | ; | ||
97 | } | ||
98 | } | ||
99 | |||
100 | int __pmac pmac_set_rtc_time(struct rtc_time *tm) | ||
101 | { | ||
102 | switch(sys_ctrler) { | ||
103 | #ifdef CONFIG_ADB_PMU | ||
104 | case SYS_CTRLER_PMU: { | ||
105 | /* TODO: Move that to a function in the PMU driver */ | ||
106 | struct adb_request req; | ||
107 | unsigned int nowtime; | ||
108 | |||
109 | DBG("set: tm_mday: %d, tm_mon: %d, tm_year: %d," | ||
110 | " %d:%02d:%02d\n", | ||
111 | tm->tm_mday, tm->tm_mon, tm->tm_year, | ||
112 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
113 | |||
114 | nowtime = mktime(tm->tm_year + 1900, tm->tm_mon + 1, | ||
115 | tm->tm_mday, tm->tm_hour, tm->tm_min, | ||
116 | tm->tm_sec); | ||
117 | |||
118 | DBG("-> %u -> %u\n", (int)nowtime, | ||
119 | (int)(nowtime + RTC_OFFSET)); | ||
120 | nowtime += RTC_OFFSET; | ||
121 | |||
122 | if (pmu_request(&req, NULL, 5, PMU_SET_RTC, | ||
123 | nowtime >> 24, nowtime >> 16, | ||
124 | nowtime >> 8, nowtime) < 0) | ||
125 | return -ENXIO; | ||
126 | pmu_wait_complete(&req); | ||
127 | if (req.reply_len != 0) | ||
128 | printk(KERN_ERR "pmac_set_rtc_time: PMU returned a %d" | ||
129 | " bytes reply\n", req.reply_len); | ||
130 | return 0; | ||
131 | } | ||
132 | #endif /* CONFIG_ADB_PMU */ | ||
133 | |||
134 | #ifdef CONFIG_PMAC_SMU | ||
135 | case SYS_CTRLER_SMU: | ||
136 | return smu_set_rtc_time(tm); | ||
137 | #endif /* CONFIG_PMAC_SMU */ | ||
138 | default: | ||
139 | return -ENODEV; | ||
140 | } | ||
141 | } | ||
142 | |||
143 | void __init pmac_get_boot_time(struct rtc_time *tm) | ||
144 | { | ||
145 | pmac_get_rtc_time(tm); | ||
146 | |||
147 | #ifdef disabled__CONFIG_NVRAM | ||
148 | s32 delta = 0; | ||
149 | int dst; | ||
150 | |||
151 | delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16; | ||
152 | delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8; | ||
153 | delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb); | ||
154 | if (delta & 0x00800000UL) | ||
155 | delta |= 0xFF000000UL; | ||
156 | dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0); | ||
157 | printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60, | ||
158 | dst ? "on" : "off"); | ||
159 | #endif | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * Query the OF and get the decr frequency. | ||
164 | * This was taken from the pmac time_init() when merging the prep/pmac | ||
165 | * time functions. | ||
166 | */ | ||
167 | void __init pmac_calibrate_decr(void) | ||
168 | { | ||
169 | struct device_node *cpu; | ||
170 | unsigned int freq, *fp; | ||
171 | struct div_result divres; | ||
172 | |||
173 | /* | ||
174 | * The cpu node should have a timebase-frequency property | ||
175 | * to tell us the rate at which the decrementer counts. | ||
176 | */ | ||
177 | cpu = find_type_devices("cpu"); | ||
178 | if (cpu == 0) | ||
179 | panic("can't find cpu node in time_init"); | ||
180 | fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL); | ||
181 | if (fp == 0) | ||
182 | panic("can't get cpu timebase frequency"); | ||
183 | freq = *fp; | ||
184 | printk("time_init: decrementer frequency = %u.%.6u MHz\n", | ||
185 | freq/1000000, freq%1000000); | ||
186 | tb_ticks_per_jiffy = freq / HZ; | ||
187 | tb_ticks_per_sec = tb_ticks_per_jiffy * HZ; | ||
188 | tb_ticks_per_usec = freq / 1000000; | ||
189 | tb_to_us = mulhwu_scale_factor(freq, 1000000); | ||
190 | div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres ); | ||
191 | tb_to_xs = divres.result_low; | ||
192 | ppc_tb_freq = freq; | ||
193 | |||
194 | fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL); | ||
195 | if (fp == 0) | ||
196 | panic("can't get cpu processor frequency"); | ||
197 | ppc_proc_freq = *fp; | ||
198 | |||
199 | setup_default_decr(); | ||
200 | } | ||
201 | |||