/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2008 by Maurus Cuelenaere * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /* * Jz OnChip Real Time Clock interface for Linux * */ #include "config.h" #include "jz4740.h" #include "rtc.h" #include "timefuncs.h" #include "logf.h" static const unsigned int yearday[5] = {0, 366, 366+365, 366+365*2, 366+365*3}; static const unsigned int sweekday = 6; static const unsigned int sum_monthday[13] = { 0, 31, 31+28, 31+28+31, 31+28+31+30, 31+28+31+30+31, 31+28+31+30+31+30, 31+28+31+30+31+30+31, 31+28+31+30+31+30+31+31, 31+28+31+30+31+30+31+31+30, 31+28+31+30+31+30+31+31+30+31, 31+28+31+30+31+30+31+31+30+31+30, 365 }; static unsigned int jz_mktime(int year, int mon, int day, int hour, int min, int sec) { unsigned int seccounter; if (year < 2000) year = 2000; year -= 2000; seccounter = (year/4)*(365*3+366); seccounter += yearday[year%4]; if (year%4) seccounter += sum_monthday[mon-1]; else if (mon >= 3) seccounter += sum_monthday[mon-1]+1; else seccounter += sum_monthday[mon-1]; seccounter += day-1; seccounter *= 24; seccounter += hour; seccounter *= 60; seccounter += min; seccounter *= 60; seccounter += sec; return seccounter; } static void jz_gettime(unsigned int rtc, int *year, int *mon, int *day, int *hour, int *min, int *sec, int *weekday) { unsigned int tday, tsec, i, tmp; tday = rtc/(24*3600); *weekday = ((tday % 7) + sweekday) % 7; *year = (tday/(366+365*3)) * 4; tday = tday%(366+365*3); for (i=0;i<5;i++) { if (tday=2)) tmp += 1; if (tday=2)) tmp += 1; *day = tday - tmp + 1; break; } } tsec = rtc % (24 * 3600); *hour = tsec / 3600; *min = (tsec / 60) % 60; *sec = tsec - *hour*3600 - *min*60; *year += 2000; } int rtc_read_datetime(struct tm *tm) { unsigned int sec,mon,mday,wday,year,hour,min; /* * Only the values that we read from the RTC are set. We leave * tm_wday, tm_yday and tm_isdst untouched. Even though the * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated * by the RTC when initially set to a non-zero value. */ jz_gettime(REG_RTC_RSR, &year, &mon, &mday, &hour, &min, &sec, &wday); year -= 2000; tm->tm_sec = sec; tm->tm_min = min; tm->tm_hour = hour; tm->tm_mday = mday; tm->tm_wday = wday; /* Don't use centry, but start from year 1970 */ tm->tm_mon = mon; if (year <= 69) year += 100; tm->tm_year = year; return 1; } int rtc_write_datetime(const struct tm *tm) { unsigned int year, lval; year = tm->tm_year; /* Don't use centry, but start from year 1970 */ if (year > 69) year -= 100; year += 2000; lval = jz_mktime(year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); __cpm_start_rtc(); udelay(100); REG_RTC_RSR = lval; __cpm_stop_rtc(); return 0; } #if 0 void get_rtc_alm_time(struct rtc_time *alm_tm) { unsigned int sec,mon,mday,wday,year,hour,min; unsigned int lval; unsigned long flags; /* * Only the values that we read from the RTC are set. That * means only tm_hour, tm_min, and tm_sec. */ lval = REG_RTC_RSAR; jz_gettime(lval, &year, &mon, &mday, &hour, &min, &sec, &wday); alm_tm->tm_sec = sec; alm_tm->tm_min = min; alm_tm->tm_hour = hour; } int rtc_ioctl(unsigned int cmd,struct rtc_time *val,unsigned int epo) { struct rtc_time wtime; switch (cmd) { case RTC_ALM_READ: /* Read the present alarm time */ /* * This returns a struct rtc_time. Reading >= 0xc0 * means "don't care" or "match all". Only the tm_hour, * tm_min, and tm_sec values are filled in. */ get_rtc_alm_time(val); break; case RTC_ALM_SET: /* Store a time into the alarm */ { unsigned char ahrs, amin, asec; unsigned int sec,mon,mday,wday,year,hour,min; unsigned int lval; unsigned long flags; struct rtc_time alm_tm; alm_tm = *val; ahrs = alm_tm.tm_hour; amin = alm_tm.tm_min; asec = alm_tm.tm_sec; if (ahrs >= 24) return -1; if (amin >= 60) return -1; if (asec >= 60) return -1; flags = spin_lock_irqsave(); lval = REG_RTC_RSR; jz_gettime(lval, &year, &mon, &mday, &hour, &min, &sec, &wday); hour = ahrs; min = amin; sec = asec; lval = jz_mktime(year, mon, mday, hour, min, sec); REG_RTC_RSAR = lval; spin_unlock_irqrestore(flags); break; } case RTC_RD_TIME: /* Read the time/date from RTC */ get_rtc_time(val); break; case RTC_SET_TIME: /* Set the RTC */ { struct rtc_time rtc_tm; unsigned int mon, day, hrs, min, sec, leap_yr, date; unsigned int yrs; unsigned int lval; unsigned long flags; rtc_tm = *val; yrs = rtc_tm.tm_year;// + 1900; mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ day = rtc_tm.tm_wday; date = rtc_tm.tm_mday; hrs = rtc_tm.tm_hour; min = rtc_tm.tm_min; sec = rtc_tm.tm_sec; if (yrs < 1970) return -EINVAL; leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); if ((mon > 12) || (date == 0)) return -EINVAL; if (date > (days_in_mo[mon] + ((mon == 2) && leap_yr))) return -EINVAL; if ((hrs >= 24) || (min >= 60) || (sec >= 60)) return -EINVAL; if ((yrs -= epoch) > 255) /* They are unsigned */ return -EINVAL; flags = spin_lock_irqsave(); /* These limits and adjustments are independant of * whether the chip is in binary mode or not. */ if (yrs > 169) { spin_unlock_irqrestore(flags); return -EINVAL; } yrs += epoch; lval = jz_mktime(yrs, mon, date, hrs, min, sec); REG_RTC_RSR = lval; /* FIXME: maybe we need to write alarm register here. */ spin_unlock_irqrestore(flags); return 0; } break; case RTC_EPOCH_READ: /* Read the epoch. */ epo = epoch; return 0; case RTC_EPOCH_SET: /* Set the epoch. */ /* * There were no RTC clocks before 1900. */ if (epo < 1900) return -EINVAL; epoch = epo; return 0; default: return -EINVAL; } return -EINVAL; } #endif void rtc_init(void) { __cpm_start_rtc(); REG_RTC_RCR = RTC_RCR_RTCE; udelay(70); while( !(REG_RTC_RCR & RTC_RCR_WRDY) ); REG_RTC_RGR = (0x7fff | RTC_RGR_LOCK); udelay(70); __cpm_stop_rtc(); }