| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * This file is subject to the terms and conditions of the GNU General Public | 
|  | 3 | * License.  See the file "COPYING" in the main directory of this archive | 
|  | 4 | * for more details. | 
|  | 5 | * | 
|  | 6 | * arch/sh64/kernel/time.c | 
|  | 7 | * | 
|  | 8 | * Copyright (C) 2000, 2001  Paolo Alberelli | 
|  | 9 | * Copyright (C) 2003, 2004  Paul Mundt | 
|  | 10 | * Copyright (C) 2003  Richard Curnow | 
|  | 11 | * | 
|  | 12 | *    Original TMU/RTC code taken from sh version. | 
|  | 13 | *    Copyright (C) 1999  Tetsuya Okada & Niibe Yutaka | 
|  | 14 | *      Some code taken from i386 version. | 
|  | 15 | *      Copyright (C) 1991, 1992, 1995  Linus Torvalds | 
|  | 16 | */ | 
|  | 17 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | #include <linux/errno.h> | 
|  | 19 | #include <linux/rwsem.h> | 
|  | 20 | #include <linux/sched.h> | 
|  | 21 | #include <linux/kernel.h> | 
|  | 22 | #include <linux/param.h> | 
|  | 23 | #include <linux/string.h> | 
|  | 24 | #include <linux/mm.h> | 
|  | 25 | #include <linux/interrupt.h> | 
|  | 26 | #include <linux/time.h> | 
|  | 27 | #include <linux/delay.h> | 
|  | 28 | #include <linux/init.h> | 
|  | 29 | #include <linux/profile.h> | 
|  | 30 | #include <linux/smp.h> | 
| Alexey Dobriyan | 4940fb4 | 2006-02-01 03:06:09 -0800 | [diff] [blame] | 31 | #include <linux/module.h> | 
| Matt Mackall | 4f3a36a | 2006-03-28 01:56:10 -0800 | [diff] [blame] | 32 | #include <linux/bcd.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 33 |  | 
|  | 34 | #include <asm/registers.h>	 /* required by inline __asm__ stmt. */ | 
|  | 35 |  | 
|  | 36 | #include <asm/processor.h> | 
|  | 37 | #include <asm/uaccess.h> | 
|  | 38 | #include <asm/io.h> | 
|  | 39 | #include <asm/irq.h> | 
|  | 40 | #include <asm/delay.h> | 
|  | 41 |  | 
|  | 42 | #include <linux/timex.h> | 
|  | 43 | #include <linux/irq.h> | 
|  | 44 | #include <asm/hardware.h> | 
|  | 45 |  | 
|  | 46 | #define TMU_TOCR_INIT	0x00 | 
|  | 47 | #define TMU0_TCR_INIT	0x0020 | 
|  | 48 | #define TMU_TSTR_INIT	1 | 
|  | 49 | #define TMU_TSTR_OFF	0 | 
|  | 50 |  | 
|  | 51 | /* RCR1 Bits */ | 
|  | 52 | #define RCR1_CF		0x80	/* Carry Flag             */ | 
|  | 53 | #define RCR1_CIE	0x10	/* Carry Interrupt Enable */ | 
|  | 54 | #define RCR1_AIE	0x08	/* Alarm Interrupt Enable */ | 
|  | 55 | #define RCR1_AF		0x01	/* Alarm Flag             */ | 
|  | 56 |  | 
|  | 57 | /* RCR2 Bits */ | 
|  | 58 | #define RCR2_PEF	0x80	/* PEriodic interrupt Flag */ | 
|  | 59 | #define RCR2_PESMASK	0x70	/* Periodic interrupt Set  */ | 
|  | 60 | #define RCR2_RTCEN	0x08	/* ENable RTC              */ | 
|  | 61 | #define RCR2_ADJ	0x04	/* ADJustment (30-second)  */ | 
|  | 62 | #define RCR2_RESET	0x02	/* Reset bit               */ | 
|  | 63 | #define RCR2_START	0x01	/* Start bit               */ | 
|  | 64 |  | 
|  | 65 | /* Clock, Power and Reset Controller */ | 
|  | 66 | #define	CPRC_BLOCK_OFF	0x01010000 | 
|  | 67 | #define CPRC_BASE	PHYS_PERIPHERAL_BLOCK + CPRC_BLOCK_OFF | 
|  | 68 |  | 
|  | 69 | #define FRQCR		(cprc_base+0x0) | 
|  | 70 | #define WTCSR		(cprc_base+0x0018) | 
|  | 71 | #define STBCR		(cprc_base+0x0030) | 
|  | 72 |  | 
|  | 73 | /* Time Management Unit */ | 
|  | 74 | #define	TMU_BLOCK_OFF	0x01020000 | 
|  | 75 | #define TMU_BASE	PHYS_PERIPHERAL_BLOCK + TMU_BLOCK_OFF | 
|  | 76 | #define TMU0_BASE	tmu_base + 0x8 + (0xc * 0x0) | 
|  | 77 | #define TMU1_BASE	tmu_base + 0x8 + (0xc * 0x1) | 
|  | 78 | #define TMU2_BASE	tmu_base + 0x8 + (0xc * 0x2) | 
|  | 79 |  | 
|  | 80 | #define TMU_TOCR	tmu_base+0x0	/* Byte access */ | 
|  | 81 | #define TMU_TSTR	tmu_base+0x4	/* Byte access */ | 
|  | 82 |  | 
|  | 83 | #define TMU0_TCOR	TMU0_BASE+0x0	/* Long access */ | 
|  | 84 | #define TMU0_TCNT	TMU0_BASE+0x4	/* Long access */ | 
|  | 85 | #define TMU0_TCR	TMU0_BASE+0x8	/* Word access */ | 
|  | 86 |  | 
|  | 87 | /* Real Time Clock */ | 
|  | 88 | #define	RTC_BLOCK_OFF	0x01040000 | 
|  | 89 | #define RTC_BASE	PHYS_PERIPHERAL_BLOCK + RTC_BLOCK_OFF | 
|  | 90 |  | 
|  | 91 | #define R64CNT  	rtc_base+0x00 | 
|  | 92 | #define RSECCNT 	rtc_base+0x04 | 
|  | 93 | #define RMINCNT 	rtc_base+0x08 | 
|  | 94 | #define RHRCNT  	rtc_base+0x0c | 
|  | 95 | #define RWKCNT  	rtc_base+0x10 | 
|  | 96 | #define RDAYCNT 	rtc_base+0x14 | 
|  | 97 | #define RMONCNT 	rtc_base+0x18 | 
|  | 98 | #define RYRCNT  	rtc_base+0x1c	/* 16bit */ | 
|  | 99 | #define RSECAR  	rtc_base+0x20 | 
|  | 100 | #define RMINAR  	rtc_base+0x24 | 
|  | 101 | #define RHRAR   	rtc_base+0x28 | 
|  | 102 | #define RWKAR   	rtc_base+0x2c | 
|  | 103 | #define RDAYAR  	rtc_base+0x30 | 
|  | 104 | #define RMONAR  	rtc_base+0x34 | 
|  | 105 | #define RCR1    	rtc_base+0x38 | 
|  | 106 | #define RCR2    	rtc_base+0x3c | 
|  | 107 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 | #define TICK_SIZE (tick_nsec / 1000) | 
|  | 109 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 110 | static unsigned long tmu_base, rtc_base; | 
|  | 111 | unsigned long cprc_base; | 
|  | 112 |  | 
|  | 113 | /* Variables to allow interpolation of time of day to resolution better than a | 
|  | 114 | * jiffy. */ | 
|  | 115 |  | 
|  | 116 | /* This is effectively protected by xtime_lock */ | 
|  | 117 | static unsigned long ctc_last_interrupt; | 
|  | 118 | static unsigned long long usecs_per_jiffy = 1000000/HZ; /* Approximation */ | 
|  | 119 |  | 
|  | 120 | #define CTC_JIFFY_SCALE_SHIFT 40 | 
|  | 121 |  | 
|  | 122 | /* 2**CTC_JIFFY_SCALE_SHIFT / ctc_ticks_per_jiffy */ | 
|  | 123 | static unsigned long long scaled_recip_ctc_ticks_per_jiffy; | 
|  | 124 |  | 
|  | 125 | /* Estimate number of microseconds that have elapsed since the last timer tick, | 
| Simon Arlott | 0a35477 | 2007-05-14 08:25:48 +0900 | [diff] [blame] | 126 | by scaling the delta that has occurred in the CTC register. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 127 |  | 
|  | 128 | WARNING WARNING WARNING : This algorithm relies on the CTC decrementing at | 
|  | 129 | the CPU clock rate.  If the CPU sleeps, the CTC stops counting.  Bear this | 
|  | 130 | in mind if enabling SLEEP_WORKS in process.c.  In that case, this algorithm | 
|  | 131 | probably needs to use TMU.TCNT0 instead.  This will work even if the CPU is | 
|  | 132 | sleeping, though will be coarser. | 
|  | 133 |  | 
|  | 134 | FIXME : What if usecs_per_tick is moving around too much, e.g. if an adjtime | 
|  | 135 | is running or if the freq or tick arguments of adjtimex are modified after | 
|  | 136 | we have calibrated the scaling factor?  This will result in either a jump at | 
|  | 137 | the end of a tick period, or a wrap backwards at the start of the next one, | 
|  | 138 | if the application is reading the time of day often enough.  I think we | 
|  | 139 | ought to do better than this.  For this reason, usecs_per_jiffy is left | 
|  | 140 | separated out in the calculation below.  This allows some future hook into | 
|  | 141 | the adjtime-related stuff in kernel/timer.c to remove this hazard. | 
|  | 142 |  | 
|  | 143 | */ | 
|  | 144 |  | 
|  | 145 | static unsigned long usecs_since_tick(void) | 
|  | 146 | { | 
|  | 147 | unsigned long long current_ctc; | 
|  | 148 | long ctc_ticks_since_interrupt; | 
|  | 149 | unsigned long long ull_ctc_ticks_since_interrupt; | 
|  | 150 | unsigned long result; | 
|  | 151 |  | 
|  | 152 | unsigned long long mul1_out; | 
|  | 153 | unsigned long long mul1_out_high; | 
|  | 154 | unsigned long long mul2_out_low, mul2_out_high; | 
|  | 155 |  | 
|  | 156 | /* Read CTC register */ | 
|  | 157 | asm ("getcon cr62, %0" : "=r" (current_ctc)); | 
|  | 158 | /* Note, the CTC counts down on each CPU clock, not up. | 
|  | 159 | Note(2), use long type to get correct wraparound arithmetic when | 
|  | 160 | the counter crosses zero. */ | 
|  | 161 | ctc_ticks_since_interrupt = (long) ctc_last_interrupt - (long) current_ctc; | 
|  | 162 | ull_ctc_ticks_since_interrupt = (unsigned long long) ctc_ticks_since_interrupt; | 
|  | 163 |  | 
|  | 164 | /* Inline assembly to do 32x32x32->64 multiplier */ | 
|  | 165 | asm volatile ("mulu.l %1, %2, %0" : | 
|  | 166 | "=r" (mul1_out) : | 
|  | 167 | "r" (ull_ctc_ticks_since_interrupt), "r" (usecs_per_jiffy)); | 
|  | 168 |  | 
|  | 169 | mul1_out_high = mul1_out >> 32; | 
|  | 170 |  | 
|  | 171 | asm volatile ("mulu.l %1, %2, %0" : | 
|  | 172 | "=r" (mul2_out_low) : | 
|  | 173 | "r" (mul1_out), "r" (scaled_recip_ctc_ticks_per_jiffy)); | 
|  | 174 |  | 
|  | 175 | #if 1 | 
|  | 176 | asm volatile ("mulu.l %1, %2, %0" : | 
|  | 177 | "=r" (mul2_out_high) : | 
|  | 178 | "r" (mul1_out_high), "r" (scaled_recip_ctc_ticks_per_jiffy)); | 
|  | 179 | #endif | 
|  | 180 |  | 
|  | 181 | result = (unsigned long) (((mul2_out_high << 32) + mul2_out_low) >> CTC_JIFFY_SCALE_SHIFT); | 
|  | 182 |  | 
|  | 183 | return result; | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | void do_gettimeofday(struct timeval *tv) | 
|  | 187 | { | 
|  | 188 | unsigned long flags; | 
|  | 189 | unsigned long seq; | 
|  | 190 | unsigned long usec, sec; | 
|  | 191 |  | 
|  | 192 | do { | 
|  | 193 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | 
|  | 194 | usec = usecs_since_tick(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 195 | sec = xtime.tv_sec; | 
|  | 196 | usec += xtime.tv_nsec / 1000; | 
|  | 197 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); | 
|  | 198 |  | 
|  | 199 | while (usec >= 1000000) { | 
|  | 200 | usec -= 1000000; | 
|  | 201 | sec++; | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | tv->tv_sec = sec; | 
|  | 205 | tv->tv_usec = usec; | 
|  | 206 | } | 
|  | 207 |  | 
|  | 208 | int do_settimeofday(struct timespec *tv) | 
|  | 209 | { | 
|  | 210 | time_t wtm_sec, sec = tv->tv_sec; | 
|  | 211 | long wtm_nsec, nsec = tv->tv_nsec; | 
|  | 212 |  | 
|  | 213 | if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | 
|  | 214 | return -EINVAL; | 
|  | 215 |  | 
|  | 216 | write_seqlock_irq(&xtime_lock); | 
|  | 217 | /* | 
|  | 218 | * This is revolting. We need to set "xtime" correctly. However, the | 
|  | 219 | * value in this location is the value at the most recent update of | 
|  | 220 | * wall time.  Discover what correction gettimeofday() would have | 
|  | 221 | * made, and then undo it! | 
|  | 222 | */ | 
| Atsushi Nemoto | 8ef3860 | 2006-09-30 23:28:31 -0700 | [diff] [blame] | 223 | nsec -= 1000 * usecs_since_tick(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 |  | 
|  | 225 | wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | 
|  | 226 | wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | 
|  | 227 |  | 
|  | 228 | set_normalized_timespec(&xtime, sec, nsec); | 
|  | 229 | set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | 
|  | 230 |  | 
| john stultz | b149ee2 | 2005-09-06 15:17:46 -0700 | [diff] [blame] | 231 | ntp_clear(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 232 | write_sequnlock_irq(&xtime_lock); | 
|  | 233 | clock_was_set(); | 
|  | 234 |  | 
|  | 235 | return 0; | 
|  | 236 | } | 
| Al Viro | 943eae0 | 2005-10-29 07:32:07 +0100 | [diff] [blame] | 237 | EXPORT_SYMBOL(do_settimeofday); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 238 |  | 
|  | 239 | static int set_rtc_time(unsigned long nowtime) | 
|  | 240 | { | 
|  | 241 | int retval = 0; | 
|  | 242 | int real_seconds, real_minutes, cmos_minutes; | 
|  | 243 |  | 
|  | 244 | ctrl_outb(RCR2_RESET, RCR2);  /* Reset pre-scaler & stop RTC */ | 
|  | 245 |  | 
|  | 246 | cmos_minutes = ctrl_inb(RMINCNT); | 
|  | 247 | BCD_TO_BIN(cmos_minutes); | 
|  | 248 |  | 
|  | 249 | /* | 
|  | 250 | * since we're only adjusting minutes and seconds, | 
|  | 251 | * don't interfere with hour overflow. This avoids | 
|  | 252 | * messing with unknown time zones but requires your | 
|  | 253 | * RTC not to be off by more than 15 minutes | 
|  | 254 | */ | 
|  | 255 | real_seconds = nowtime % 60; | 
|  | 256 | real_minutes = nowtime / 60; | 
|  | 257 | if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) | 
|  | 258 | real_minutes += 30;	/* correct for half hour time zone */ | 
|  | 259 | real_minutes %= 60; | 
|  | 260 |  | 
|  | 261 | if (abs(real_minutes - cmos_minutes) < 30) { | 
|  | 262 | BIN_TO_BCD(real_seconds); | 
|  | 263 | BIN_TO_BCD(real_minutes); | 
|  | 264 | ctrl_outb(real_seconds, RSECCNT); | 
|  | 265 | ctrl_outb(real_minutes, RMINCNT); | 
|  | 266 | } else { | 
|  | 267 | printk(KERN_WARNING | 
|  | 268 | "set_rtc_time: can't update from %d to %d\n", | 
|  | 269 | cmos_minutes, real_minutes); | 
|  | 270 | retval = -1; | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start RTC */ | 
|  | 274 |  | 
|  | 275 | return retval; | 
|  | 276 | } | 
|  | 277 |  | 
|  | 278 | /* last time the RTC clock got updated */ | 
|  | 279 | static long last_rtc_update = 0; | 
|  | 280 |  | 
|  | 281 | /* | 
|  | 282 | * timer_interrupt() needs to keep up the real-time clock, | 
|  | 283 | * as well as call the "do_timer()" routine every clocktick | 
|  | 284 | */ | 
| Paul Mundt | a226d33 | 2007-05-14 09:10:01 +0900 | [diff] [blame] | 285 | static inline void do_timer_interrupt(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 286 | { | 
|  | 287 | unsigned long long current_ctc; | 
|  | 288 | asm ("getcon cr62, %0" : "=r" (current_ctc)); | 
|  | 289 | ctc_last_interrupt = (unsigned long) current_ctc; | 
|  | 290 |  | 
| Atsushi Nemoto | 3171a03 | 2006-09-29 02:00:32 -0700 | [diff] [blame] | 291 | do_timer(1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 292 | #ifndef CONFIG_SMP | 
| Paul Mundt | a226d33 | 2007-05-14 09:10:01 +0900 | [diff] [blame] | 293 | update_process_times(user_mode(get_irq_regs())); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 294 | #endif | 
| Paul Mundt | a226d33 | 2007-05-14 09:10:01 +0900 | [diff] [blame] | 295 | if (current->pid) | 
|  | 296 | profile_tick(CPU_PROFILING); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 297 |  | 
|  | 298 | #ifdef CONFIG_HEARTBEAT | 
|  | 299 | { | 
|  | 300 | extern void heartbeat(void); | 
|  | 301 |  | 
|  | 302 | heartbeat(); | 
|  | 303 | } | 
|  | 304 | #endif | 
|  | 305 |  | 
|  | 306 | /* | 
|  | 307 | * If we have an externally synchronized Linux clock, then update | 
|  | 308 | * RTC clock accordingly every ~11 minutes. Set_rtc_mmss() has to be | 
|  | 309 | * called as close as possible to 500 ms before the new second starts. | 
|  | 310 | */ | 
| john stultz | b149ee2 | 2005-09-06 15:17:46 -0700 | [diff] [blame] | 311 | if (ntp_synced() && | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 312 | xtime.tv_sec > last_rtc_update + 660 && | 
|  | 313 | (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && | 
|  | 314 | (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { | 
|  | 315 | if (set_rtc_time(xtime.tv_sec) == 0) | 
|  | 316 | last_rtc_update = xtime.tv_sec; | 
|  | 317 | else | 
|  | 318 | last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ | 
|  | 319 | } | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | /* | 
|  | 323 | * This is the same as the above, except we _also_ save the current | 
|  | 324 | * Time Stamp Counter value at the time of the timer interrupt, so that | 
|  | 325 | * we later on can estimate the time of day more exactly. | 
|  | 326 | */ | 
| Paul Mundt | a226d33 | 2007-05-14 09:10:01 +0900 | [diff] [blame] | 327 | static irqreturn_t timer_interrupt(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 328 | { | 
|  | 329 | unsigned long timer_status; | 
|  | 330 |  | 
|  | 331 | /* Clear UNF bit */ | 
|  | 332 | timer_status = ctrl_inw(TMU0_TCR); | 
|  | 333 | timer_status &= ~0x100; | 
|  | 334 | ctrl_outw(timer_status, TMU0_TCR); | 
|  | 335 |  | 
|  | 336 | /* | 
|  | 337 | * Here we are in the timer irq handler. We just have irqs locally | 
|  | 338 | * disabled but we don't know if the timer_bh is running on the other | 
|  | 339 | * CPU. We need to avoid to SMP race with it. NOTE: we don' t need | 
|  | 340 | * the irq version of write_lock because as just said we have irq | 
|  | 341 | * locally disabled. -arca | 
|  | 342 | */ | 
|  | 343 | write_lock(&xtime_lock); | 
| Paul Mundt | a226d33 | 2007-05-14 09:10:01 +0900 | [diff] [blame] | 344 | do_timer_interrupt(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 345 | write_unlock(&xtime_lock); | 
|  | 346 |  | 
|  | 347 | return IRQ_HANDLED; | 
|  | 348 | } | 
|  | 349 |  | 
|  | 350 | static unsigned long get_rtc_time(void) | 
|  | 351 | { | 
|  | 352 | unsigned int sec, min, hr, wk, day, mon, yr, yr100; | 
|  | 353 |  | 
|  | 354 | again: | 
|  | 355 | do { | 
|  | 356 | ctrl_outb(0, RCR1);  /* Clear CF-bit */ | 
|  | 357 | sec = ctrl_inb(RSECCNT); | 
|  | 358 | min = ctrl_inb(RMINCNT); | 
|  | 359 | hr  = ctrl_inb(RHRCNT); | 
|  | 360 | wk  = ctrl_inb(RWKCNT); | 
|  | 361 | day = ctrl_inb(RDAYCNT); | 
|  | 362 | mon = ctrl_inb(RMONCNT); | 
|  | 363 | yr  = ctrl_inw(RYRCNT); | 
|  | 364 | yr100 = (yr >> 8); | 
|  | 365 | yr &= 0xff; | 
|  | 366 | } while ((ctrl_inb(RCR1) & RCR1_CF) != 0); | 
|  | 367 |  | 
|  | 368 | BCD_TO_BIN(yr100); | 
|  | 369 | BCD_TO_BIN(yr); | 
|  | 370 | BCD_TO_BIN(mon); | 
|  | 371 | BCD_TO_BIN(day); | 
|  | 372 | BCD_TO_BIN(hr); | 
|  | 373 | BCD_TO_BIN(min); | 
|  | 374 | BCD_TO_BIN(sec); | 
|  | 375 |  | 
|  | 376 | if (yr > 99 || mon < 1 || mon > 12 || day > 31 || day < 1 || | 
|  | 377 | hr > 23 || min > 59 || sec > 59) { | 
|  | 378 | printk(KERN_ERR | 
|  | 379 | "SH RTC: invalid value, resetting to 1 Jan 2000\n"); | 
|  | 380 | ctrl_outb(RCR2_RESET, RCR2);  /* Reset & Stop */ | 
|  | 381 | ctrl_outb(0, RSECCNT); | 
|  | 382 | ctrl_outb(0, RMINCNT); | 
|  | 383 | ctrl_outb(0, RHRCNT); | 
|  | 384 | ctrl_outb(6, RWKCNT); | 
|  | 385 | ctrl_outb(1, RDAYCNT); | 
|  | 386 | ctrl_outb(1, RMONCNT); | 
|  | 387 | ctrl_outw(0x2000, RYRCNT); | 
|  | 388 | ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start */ | 
|  | 389 | goto again; | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | return mktime(yr100 * 100 + yr, mon, day, hr, min, sec); | 
|  | 393 | } | 
|  | 394 |  | 
|  | 395 | static __init unsigned int get_cpu_hz(void) | 
|  | 396 | { | 
|  | 397 | unsigned int count; | 
|  | 398 | unsigned long __dummy; | 
|  | 399 | unsigned long ctc_val_init, ctc_val; | 
|  | 400 |  | 
|  | 401 | /* | 
|  | 402 | ** Regardless the toolchain, force the compiler to use the | 
|  | 403 | ** arbitrary register r3 as a clock tick counter. | 
| Adrian Bunk | 2a10e0b | 2006-01-08 01:02:15 -0800 | [diff] [blame] | 404 | ** NOTE: r3 must be in accordance with sh64_rtc_interrupt() | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 405 | */ | 
|  | 406 | register unsigned long long  __rtc_irq_flag __asm__ ("r3"); | 
|  | 407 |  | 
|  | 408 | local_irq_enable(); | 
|  | 409 | do {} while (ctrl_inb(R64CNT) != 0); | 
|  | 410 | ctrl_outb(RCR1_CIE, RCR1); /* Enable carry interrupt */ | 
|  | 411 |  | 
|  | 412 | /* | 
|  | 413 | * r3 is arbitrary. CDC does not support "=z". | 
|  | 414 | */ | 
|  | 415 | ctc_val_init = 0xffffffff; | 
|  | 416 | ctc_val = ctc_val_init; | 
|  | 417 |  | 
|  | 418 | asm volatile("gettr	tr0, %1\n\t" | 
|  | 419 | "putcon	%0, " __CTC "\n\t" | 
|  | 420 | "and	%2, r63, %2\n\t" | 
|  | 421 | "pta	$+4, tr0\n\t" | 
|  | 422 | "beq/l	%2, r63, tr0\n\t" | 
|  | 423 | "ptabs	%1, tr0\n\t" | 
|  | 424 | "getcon	" __CTC ", %0\n\t" | 
|  | 425 | : "=r"(ctc_val), "=r" (__dummy), "=r" (__rtc_irq_flag) | 
|  | 426 | : "0" (0)); | 
|  | 427 | local_irq_disable(); | 
|  | 428 | /* | 
|  | 429 | * SH-3: | 
|  | 430 | * CPU clock = 4 stages * loop | 
|  | 431 | * tst    rm,rm      if id ex | 
|  | 432 | * bt/s   1b            if id ex | 
|  | 433 | * add    #1,rd            if id ex | 
|  | 434 | *                            (if) pipe line stole | 
|  | 435 | * tst    rm,rm                  if id ex | 
|  | 436 | * .... | 
|  | 437 | * | 
|  | 438 | * | 
|  | 439 | * SH-4: | 
|  | 440 | * CPU clock = 6 stages * loop | 
|  | 441 | * I don't know why. | 
|  | 442 | * .... | 
|  | 443 | * | 
|  | 444 | * SH-5: | 
|  | 445 | * Use CTC register to count.  This approach returns the right value | 
|  | 446 | * even if the I-cache is disabled (e.g. whilst debugging.) | 
|  | 447 | * | 
|  | 448 | */ | 
|  | 449 |  | 
|  | 450 | count = ctc_val_init - ctc_val; /* CTC counts down */ | 
|  | 451 |  | 
|  | 452 | #if defined (CONFIG_SH_SIMULATOR) | 
|  | 453 | /* | 
|  | 454 | * Let's pretend we are a 5MHz SH-5 to avoid a too | 
|  | 455 | * little timer interval. Also to keep delay | 
|  | 456 | * calibration within a reasonable time. | 
|  | 457 | */ | 
|  | 458 | return 5000000; | 
|  | 459 | #else | 
|  | 460 | /* | 
|  | 461 | * This really is count by the number of clock cycles | 
|  | 462 | * by the ratio between a complete R64CNT | 
|  | 463 | * wrap-around (128) and CUI interrupt being raised (64). | 
|  | 464 | */ | 
|  | 465 | return count*2; | 
|  | 466 | #endif | 
|  | 467 | } | 
|  | 468 |  | 
| Paul Mundt | a226d33 | 2007-05-14 09:10:01 +0900 | [diff] [blame] | 469 | static irqreturn_t sh64_rtc_interrupt(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 470 | { | 
| Paul Mundt | a226d33 | 2007-05-14 09:10:01 +0900 | [diff] [blame] | 471 | struct pt_regs *regs = get_irq_regs(); | 
|  | 472 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 473 | ctrl_outb(0, RCR1);	/* Disable Carry Interrupts */ | 
|  | 474 | regs->regs[3] = 1;	/* Using r3 */ | 
|  | 475 |  | 
|  | 476 | return IRQ_HANDLED; | 
|  | 477 | } | 
|  | 478 |  | 
| Thomas Gleixner | 948d12c | 2007-10-03 15:02:14 +0900 | [diff] [blame] | 479 | static struct irqaction irq0  = { | 
|  | 480 | .handler = timer_interrupt, | 
|  | 481 | .flags = IRQF_DISABLED, | 
|  | 482 | .mask = CPU_MASK_NONE, | 
|  | 483 | .name = "timer", | 
|  | 484 | }; | 
|  | 485 | static struct irqaction irq1  = { | 
|  | 486 | .handler = sh64_rtc_interrupt, | 
|  | 487 | .flags = IRQF_DISABLED, | 
|  | 488 | .mask = CPU_MASK_NONE, | 
|  | 489 | .name = "rtc", | 
|  | 490 | }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 491 |  | 
|  | 492 | void __init time_init(void) | 
|  | 493 | { | 
|  | 494 | unsigned int cpu_clock, master_clock, bus_clock, module_clock; | 
|  | 495 | unsigned long interval; | 
|  | 496 | unsigned long frqcr, ifc, pfc; | 
|  | 497 | static int ifc_table[] = { 2, 4, 6, 8, 10, 12, 16, 24 }; | 
|  | 498 | #define bfc_table ifc_table	/* Same */ | 
|  | 499 | #define pfc_table ifc_table	/* Same */ | 
|  | 500 |  | 
|  | 501 | tmu_base = onchip_remap(TMU_BASE, 1024, "TMU"); | 
|  | 502 | if (!tmu_base) { | 
|  | 503 | panic("Unable to remap TMU\n"); | 
|  | 504 | } | 
|  | 505 |  | 
|  | 506 | rtc_base = onchip_remap(RTC_BASE, 1024, "RTC"); | 
|  | 507 | if (!rtc_base) { | 
|  | 508 | panic("Unable to remap RTC\n"); | 
|  | 509 | } | 
|  | 510 |  | 
|  | 511 | cprc_base = onchip_remap(CPRC_BASE, 1024, "CPRC"); | 
|  | 512 | if (!cprc_base) { | 
|  | 513 | panic("Unable to remap CPRC\n"); | 
|  | 514 | } | 
|  | 515 |  | 
|  | 516 | xtime.tv_sec = get_rtc_time(); | 
|  | 517 | xtime.tv_nsec = 0; | 
|  | 518 |  | 
|  | 519 | setup_irq(TIMER_IRQ, &irq0); | 
|  | 520 | setup_irq(RTC_IRQ, &irq1); | 
|  | 521 |  | 
|  | 522 | /* Check how fast it is.. */ | 
|  | 523 | cpu_clock = get_cpu_hz(); | 
|  | 524 |  | 
|  | 525 | /* Note careful order of operations to maintain reasonable precision and avoid overflow. */ | 
|  | 526 | scaled_recip_ctc_ticks_per_jiffy = ((1ULL << CTC_JIFFY_SCALE_SHIFT) / (unsigned long long)(cpu_clock / HZ)); | 
|  | 527 |  | 
|  | 528 | disable_irq(RTC_IRQ); | 
|  | 529 |  | 
|  | 530 | printk("CPU clock: %d.%02dMHz\n", | 
|  | 531 | (cpu_clock / 1000000), (cpu_clock % 1000000)/10000); | 
|  | 532 | { | 
|  | 533 | unsigned short bfc; | 
|  | 534 | frqcr = ctrl_inl(FRQCR); | 
|  | 535 | ifc  = ifc_table[(frqcr>> 6) & 0x0007]; | 
|  | 536 | bfc  = bfc_table[(frqcr>> 3) & 0x0007]; | 
|  | 537 | pfc  = pfc_table[(frqcr>> 12) & 0x0007]; | 
|  | 538 | master_clock = cpu_clock * ifc; | 
|  | 539 | bus_clock = master_clock/bfc; | 
|  | 540 | } | 
|  | 541 |  | 
|  | 542 | printk("Bus clock: %d.%02dMHz\n", | 
|  | 543 | (bus_clock/1000000), (bus_clock % 1000000)/10000); | 
|  | 544 | module_clock = master_clock/pfc; | 
|  | 545 | printk("Module clock: %d.%02dMHz\n", | 
|  | 546 | (module_clock/1000000), (module_clock % 1000000)/10000); | 
|  | 547 | interval = (module_clock/(HZ*4)); | 
|  | 548 |  | 
|  | 549 | printk("Interval = %ld\n", interval); | 
|  | 550 |  | 
|  | 551 | current_cpu_data.cpu_clock    = cpu_clock; | 
|  | 552 | current_cpu_data.master_clock = master_clock; | 
|  | 553 | current_cpu_data.bus_clock    = bus_clock; | 
|  | 554 | current_cpu_data.module_clock = module_clock; | 
|  | 555 |  | 
|  | 556 | /* Start TMU0 */ | 
|  | 557 | ctrl_outb(TMU_TSTR_OFF, TMU_TSTR); | 
|  | 558 | ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); | 
|  | 559 | ctrl_outw(TMU0_TCR_INIT, TMU0_TCR); | 
|  | 560 | ctrl_outl(interval, TMU0_TCOR); | 
|  | 561 | ctrl_outl(interval, TMU0_TCNT); | 
|  | 562 | ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); | 
|  | 563 | } | 
|  | 564 |  | 
|  | 565 | void enter_deep_standby(void) | 
|  | 566 | { | 
|  | 567 | /* Disable watchdog timer */ | 
|  | 568 | ctrl_outl(0xa5000000, WTCSR); | 
|  | 569 | /* Configure deep standby on sleep */ | 
|  | 570 | ctrl_outl(0x03, STBCR); | 
|  | 571 |  | 
|  | 572 | #ifdef CONFIG_SH_ALPHANUMERIC | 
|  | 573 | { | 
|  | 574 | extern void mach_alphanum(int position, unsigned char value); | 
|  | 575 | extern void mach_alphanum_brightness(int setting); | 
|  | 576 | char halted[] = "Halted. "; | 
|  | 577 | int i; | 
|  | 578 | mach_alphanum_brightness(6); /* dimmest setting above off */ | 
|  | 579 | for (i=0; i<8; i++) { | 
|  | 580 | mach_alphanum(i, halted[i]); | 
|  | 581 | } | 
|  | 582 | asm __volatile__ ("synco"); | 
|  | 583 | } | 
|  | 584 | #endif | 
|  | 585 |  | 
|  | 586 | asm __volatile__ ("sleep"); | 
|  | 587 | asm __volatile__ ("synci"); | 
|  | 588 | asm __volatile__ ("nop"); | 
|  | 589 | asm __volatile__ ("nop"); | 
|  | 590 | asm __volatile__ ("nop"); | 
|  | 591 | asm __volatile__ ("nop"); | 
|  | 592 | panic("Unexpected wakeup!\n"); | 
|  | 593 | } |