1/* MN10300 RTC management 2 * 3 * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. 4 * Written by David Howells (dhowells@redhat.com) 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public Licence 8 * as published by the Free Software Foundation; either version 9 * 2 of the Licence, or (at your option) any later version. 10 */ 11#include <linux/kernel.h> 12#include <linux/module.h> 13#include <linux/init.h> 14#include <linux/mc146818rtc.h> 15#include <linux/bcd.h> 16#include <linux/timex.h> 17#include <asm/rtc-regs.h> 18#include <asm/rtc.h> 19 20DEFINE_SPINLOCK(rtc_lock); 21EXPORT_SYMBOL(rtc_lock); 22 23/* 24 * Read the current RTC time 25 */ 26void read_persistent_clock(struct timespec *ts) 27{ 28 struct rtc_time tm; 29 30 get_rtc_time(&tm); 31 32 ts->tv_nsec = 0; 33 ts->tv_sec = mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, 34 tm.tm_hour, tm.tm_min, tm.tm_sec); 35 36 /* if rtc is way off in the past, set something reasonable */ 37 if (ts->tv_sec < 0) 38 ts->tv_sec = mktime(2009, 1, 1, 12, 0, 0); 39} 40 41/* 42 * In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500 43 * ms after the second nowtime has started, because when nowtime is written 44 * into the registers of the CMOS clock, it will jump to the next second 45 * precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data 46 * sheet for details. 47 * 48 * BUG: This routine does not handle hour overflow properly; it just 49 * sets the minutes. Usually you'll only notice that after reboot! 50 */ 51static int set_rtc_mmss(unsigned long nowtime) 52{ 53 unsigned char save_control, save_freq_select; 54 int retval = 0; 55 int real_seconds, real_minutes, cmos_minutes; 56 57 /* gets recalled with irq locally disabled */ 58 spin_lock(&rtc_lock); 59 save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being 60 * set */ 61 CMOS_WRITE(save_control | RTC_SET, RTC_CONTROL); 62 63 save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset 64 * prescaler */ 65 CMOS_WRITE(save_freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT); 66 67 cmos_minutes = CMOS_READ(RTC_MINUTES); 68 if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) 69 cmos_minutes = bcd2bin(cmos_minutes); 70 71 /* 72 * since we're only adjusting minutes and seconds, 73 * don't interfere with hour overflow. This avoids 74 * messing with unknown time zones but requires your 75 * RTC not to be off by more than 15 minutes 76 */ 77 real_seconds = nowtime % 60; 78 real_minutes = nowtime / 60; 79 if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) 80 /* correct for half hour time zone */ 81 real_minutes += 30; 82 real_minutes %= 60; 83 84 if (abs(real_minutes - cmos_minutes) < 30) { 85 if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { 86 real_seconds = bin2bcd(real_seconds); 87 real_minutes = bin2bcd(real_minutes); 88 } 89 CMOS_WRITE(real_seconds, RTC_SECONDS); 90 CMOS_WRITE(real_minutes, RTC_MINUTES); 91 } else { 92 printk_once(KERN_NOTICE 93 "set_rtc_mmss: can't update from %d to %d\n", 94 cmos_minutes, real_minutes); 95 retval = -1; 96 } 97 98 /* The following flags have to be released exactly in this order, 99 * otherwise the DS12887 (popular MC146818A clone with integrated 100 * battery and quartz) will not reset the oscillator and will not 101 * update precisely 500 ms later. You won't find this mentioned in 102 * the Dallas Semiconductor data sheets, but who believes data 103 * sheets anyway ... -- Markus Kuhn 104 */ 105 CMOS_WRITE(save_control, RTC_CONTROL); 106 CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); 107 spin_unlock(&rtc_lock); 108 109 return retval; 110} 111 112int update_persistent_clock(struct timespec now) 113{ 114 return set_rtc_mmss(now.tv_sec); 115} 116 117/* 118 * calibrate the TSC clock against the RTC 119 */ 120void __init calibrate_clock(void) 121{ 122 unsigned char status; 123 124 /* make sure the RTC is running and is set to operate in 24hr mode */ 125 status = RTSRC; 126 RTCRB |= RTCRB_SET; 127 RTCRB |= RTCRB_TM_24HR; 128 RTCRB &= ~RTCRB_DM_BINARY; 129 RTCRA |= RTCRA_DVR; 130 RTCRA &= ~RTCRA_DVR; 131 RTCRB &= ~RTCRB_SET; 132} 133