]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 |