2008-04-24 09:22:39 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2008 by Michael Sevakis
|
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
* 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.
|
2008-04-24 09:22:39 +00:00
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include "config.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "rtc.h"
|
|
|
|
#include "mc13783.h"
|
|
|
|
|
|
|
|
/* NOTE: Defined the base to be original firmware compatible if needed -
|
|
|
|
* ie. the day and year as it would interpret a DAY register value of zero. */
|
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* None of this code concerns itself with (year mod 100) = 0 leap year
|
|
|
|
* exceptions because all (year mod 4) = 0 years in the relevant range are
|
|
|
|
* leap years. A base year of 1901 to an end date of 28 Feb 2100 are ok. */
|
|
|
|
|
2008-04-24 09:22:39 +00:00
|
|
|
#ifdef TOSHIBA_GIGABEAT_S
|
|
|
|
/* Gigabeat S seems to be 1 day behind the ususual - this will
|
|
|
|
* make the RTC match file dates created by retailos. */
|
2010-12-24 17:06:35 +00:00
|
|
|
#define RTC_BASE_WDAY 1 /* Monday */
|
|
|
|
#define RTC_BASE_YDAY 364 /* 31 Dec */
|
2008-04-24 09:22:39 +00:00
|
|
|
#define RTC_BASE_YEAR 1979
|
|
|
|
#elif 1
|
2010-12-24 17:06:35 +00:00
|
|
|
#define RTC_BASE_WDAY 2 /* Tuesday */
|
|
|
|
#define RTC_BASE_YDAY 0 /* 01 Jan */
|
2008-04-24 09:22:39 +00:00
|
|
|
#define RTC_BASE_YEAR 1980
|
|
|
|
#else
|
2010-12-24 17:06:35 +00:00
|
|
|
#define RTC_BASE_WDAY 4 /* Thursday */
|
|
|
|
#define RTC_BASE_YDAY 0 /* 01 Jan */
|
2008-04-24 09:22:39 +00:00
|
|
|
#define RTC_BASE_YEAR 1970
|
|
|
|
#endif
|
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Reference year for leap calculation - year that is on or before the base
|
|
|
|
* year and immediately follows a leap year */
|
|
|
|
#define RTC_REF_YEAR_OFFS ((RTC_BASE_YEAR - 1) & 3)
|
|
|
|
#define RTC_REF_YEAR (RTC_BASE_YEAR - RTC_REF_YEAR_OFFS)
|
|
|
|
|
2008-04-24 09:22:39 +00:00
|
|
|
enum rtc_registers_indexes
|
|
|
|
{
|
|
|
|
RTC_REG_TIME = 0,
|
|
|
|
RTC_REG_DAY,
|
|
|
|
RTC_REG_TIME2,
|
2010-12-24 17:06:35 +00:00
|
|
|
RTC_NUM_REGS_RD = 3,
|
|
|
|
RTC_NUM_REGS_WR = 2,
|
2008-04-24 09:22:39 +00:00
|
|
|
};
|
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
static const unsigned char rtc_registers[RTC_NUM_REGS_RD] =
|
2008-04-24 09:22:39 +00:00
|
|
|
{
|
|
|
|
[RTC_REG_TIME] = MC13783_RTC_TIME,
|
|
|
|
[RTC_REG_DAY] = MC13783_RTC_DAY,
|
|
|
|
[RTC_REG_TIME2] = MC13783_RTC_TIME,
|
|
|
|
};
|
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* was it an alarm that triggered power on ? */
|
|
|
|
static bool alarm_start = false;
|
2017-01-27 02:08:55 +00:00
|
|
|
static unsigned long rtc_is_dirty = 1; /* force a read right away */
|
2010-12-24 17:06:35 +00:00
|
|
|
|
|
|
|
static const unsigned short month_table[13] =
|
2008-04-24 09:22:39 +00:00
|
|
|
{
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Since 1 Jan, how many days have passed this year? (non-leap)
|
|
|
|
+31 28 31 30 31 30 31 31 30 31 30 31 */
|
|
|
|
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
|
2008-04-24 09:22:39 +00:00
|
|
|
};
|
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
static bool read_time_and_day(uint32_t regs[RTC_NUM_REGS_RD])
|
2008-04-24 09:22:39 +00:00
|
|
|
{
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Read time, day, time - 2nd read of time should be the same or
|
|
|
|
* greater */
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (mc13783_read_regs(rtc_registers, regs,
|
|
|
|
RTC_NUM_REGS_RD) < RTC_NUM_REGS_RD)
|
|
|
|
{
|
|
|
|
/* Couldn't read registers */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If TOD counter turned over - reread */
|
|
|
|
while (regs[RTC_REG_TIME2] < regs[RTC_REG_TIME]);
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
return true;
|
2008-04-24 09:22:39 +00:00
|
|
|
}
|
|
|
|
|
2017-01-27 02:08:55 +00:00
|
|
|
void MC13783_EVENT_CB_1HZ(void)
|
|
|
|
{
|
|
|
|
rtc_is_dirty = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rtc_mc13783_dirty(void)
|
|
|
|
{
|
|
|
|
return bitclr32(&rtc_is_dirty, 1);
|
|
|
|
}
|
|
|
|
|
2008-04-24 09:22:39 +00:00
|
|
|
/** Public APIs **/
|
|
|
|
void rtc_init(void)
|
|
|
|
{
|
2008-05-21 08:42:11 +00:00
|
|
|
/* only needs to be polled on startup */
|
|
|
|
if (mc13783_read(MC13783_INTERRUPT_STATUS1) & MC13783_TODAI)
|
|
|
|
{
|
|
|
|
alarm_start = true;
|
|
|
|
mc13783_write(MC13783_INTERRUPT_STATUS1, MC13783_TODAI);
|
|
|
|
}
|
2017-01-27 02:08:55 +00:00
|
|
|
|
|
|
|
mc13783_enable_event(MC13783_INT_ID_1HZ, true);
|
2008-04-24 09:22:39 +00:00
|
|
|
}
|
|
|
|
|
2009-09-26 14:58:32 +00:00
|
|
|
int rtc_read_datetime(struct tm *tm)
|
2008-04-24 09:22:39 +00:00
|
|
|
{
|
2010-12-24 17:06:35 +00:00
|
|
|
uint32_t regs[RTC_NUM_REGS_RD];
|
|
|
|
int year, month, yday, x, n;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
if (!read_time_and_day(regs))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* TOD: 0 to 86399 */
|
|
|
|
x = regs[RTC_REG_TIME];
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Hour */
|
|
|
|
n = x / 3600;
|
|
|
|
tm->tm_hour = n;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Minute */
|
|
|
|
x -= n*3600;
|
|
|
|
n = x / 60;
|
|
|
|
tm->tm_min = n;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Second */
|
|
|
|
x -= n*60;
|
|
|
|
tm->tm_sec = x;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
|
|
|
/* DAY: 0 to 32767 */
|
2010-12-24 17:06:35 +00:00
|
|
|
x = regs[RTC_REG_DAY];
|
2008-04-24 09:22:39 +00:00
|
|
|
|
|
|
|
/* Weekday */
|
2010-12-24 17:06:35 +00:00
|
|
|
tm->tm_wday = (x + RTC_BASE_WDAY) % 7;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Year */
|
|
|
|
x += RTC_REF_YEAR_OFFS*365 + RTC_BASE_YDAY;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Lag year increment by subtracting leaps since the reference year
|
|
|
|
* on 31 Dec of each leap year, essentially removing them from the
|
|
|
|
* calculation */
|
|
|
|
n = (x + 1) / 1461;
|
|
|
|
year = (x - n) / 365;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Year day */
|
|
|
|
yday = x - n - year*365;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* If (x + 1) mod 1461 == 0, then it is yday 365 of a leap year */
|
|
|
|
if (n * 1461 - 1 == x)
|
|
|
|
yday++;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
tm->tm_yday = x = yday;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
if (((year + RTC_REF_YEAR) & 3) == 0 && x >= month_table[2])
|
2008-04-24 09:22:39 +00:00
|
|
|
{
|
2010-12-24 17:06:35 +00:00
|
|
|
if (x > month_table[2])
|
|
|
|
yday--; /* 1 Mar or after; lag date by one day */
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
x--; /* 29 Feb or after, lag month by one day */
|
2008-04-24 09:22:39 +00:00
|
|
|
}
|
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Get the current month */
|
|
|
|
month = x >> 5; /* yday / 32, close enough */
|
|
|
|
|
|
|
|
if (month_table[month + 1] <= x)
|
|
|
|
month++; /* Round to next */
|
|
|
|
|
|
|
|
tm->tm_mday = yday - month_table[month] + 1; /* 1 to 31 */
|
2009-09-26 14:58:32 +00:00
|
|
|
tm->tm_mon = month; /* 0 to 11 */
|
2010-12-24 17:06:35 +00:00
|
|
|
|
|
|
|
/* {BY to (BY+89 or 90)} - 1900 */
|
|
|
|
tm->tm_year = year + RTC_REF_YEAR - 1900;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
|
|
|
return 7;
|
|
|
|
}
|
|
|
|
|
2009-09-26 14:58:32 +00:00
|
|
|
int rtc_write_datetime(const struct tm *tm)
|
2008-04-24 09:22:39 +00:00
|
|
|
{
|
2010-12-24 17:06:35 +00:00
|
|
|
uint32_t regs[RTC_NUM_REGS_WR];
|
|
|
|
int year, month, x;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Convert time of day into seconds since midnight */
|
|
|
|
x = tm->tm_sec + tm->tm_min*60 + tm->tm_hour*3600;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Keep in range (it throws off the PMIC counters otherwise) */
|
|
|
|
regs[RTC_REG_TIME] = MAX(0, MIN(86399, x));
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
year = tm->tm_year + 1900;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Get the number of days elapsed from 1 Jan of reference year to 1 Jan of
|
|
|
|
* this year */
|
|
|
|
x = year - RTC_REF_YEAR;
|
|
|
|
x = x*365 + (x >> 2);
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Add the number of days passed this year since 1 Jan and offset by the
|
|
|
|
* base yearday and the reference offset in days from the base */
|
2009-09-26 14:58:32 +00:00
|
|
|
month = tm->tm_mon;
|
2010-12-24 17:06:35 +00:00
|
|
|
x += month_table[month] + tm->tm_mday - RTC_REF_YEAR_OFFS*365
|
|
|
|
- RTC_BASE_YDAY;
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
if ((year & 3) != 0 || month < 2) {
|
|
|
|
/* Sub one day because tm_mday starts at 1, otherwise the offset is
|
|
|
|
* required because of 29 Feb */
|
|
|
|
x--;
|
2008-04-24 09:22:39 +00:00
|
|
|
}
|
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
/* Keep in range */
|
|
|
|
regs[RTC_REG_DAY] = MAX(0, MIN(32767, x));
|
2008-04-24 09:22:39 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
if (mc13783_write_regs(rtc_registers, regs, RTC_NUM_REGS_WR)
|
|
|
|
== RTC_NUM_REGS_WR)
|
2008-04-24 09:22:39 +00:00
|
|
|
{
|
2009-09-26 14:58:32 +00:00
|
|
|
return 7;
|
2008-04-24 09:22:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2008-05-05 12:53:03 +00:00
|
|
|
|
|
|
|
bool rtc_check_alarm_flag(void)
|
|
|
|
{
|
|
|
|
/* We don't need to do anything special if it has already fired */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-05-22 00:28:26 +00:00
|
|
|
void rtc_enable_alarm(bool enable)
|
2008-05-05 12:53:03 +00:00
|
|
|
{
|
|
|
|
if (enable)
|
2008-05-16 06:21:11 +00:00
|
|
|
mc13783_clear(MC13783_INTERRUPT_MASK1, MC13783_TODAM);
|
2008-05-05 12:53:03 +00:00
|
|
|
else
|
2008-05-16 06:21:11 +00:00
|
|
|
mc13783_set(MC13783_INTERRUPT_MASK1, MC13783_TODAM);
|
2008-05-05 12:53:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool rtc_check_alarm_started(bool release_alarm)
|
|
|
|
{
|
|
|
|
bool rc = alarm_start;
|
2008-05-21 08:42:11 +00:00
|
|
|
|
|
|
|
if (release_alarm)
|
|
|
|
alarm_start = false;
|
2008-05-05 12:53:03 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void rtc_set_alarm(int h, int m)
|
|
|
|
{
|
2010-12-24 17:06:35 +00:00
|
|
|
uint32_t regs[RTC_NUM_REGS_RD];
|
|
|
|
uint32_t tod;
|
2008-05-05 12:53:03 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
if (!read_time_and_day(regs))
|
|
|
|
return;
|
2008-05-05 12:53:03 +00:00
|
|
|
|
2010-12-24 17:06:35 +00:00
|
|
|
tod = h*3600 + m*60;
|
|
|
|
|
|
|
|
if (tod < regs[RTC_REG_TIME])
|
|
|
|
regs[RTC_REG_DAY]++;
|
|
|
|
|
|
|
|
mc13783_write(MC13783_RTC_DAY_ALARM, regs[RTC_REG_DAY]);
|
|
|
|
mc13783_write(MC13783_RTC_ALARM, tod);
|
2008-05-05 12:53:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void rtc_get_alarm(int *h, int *m)
|
|
|
|
{
|
2010-12-24 17:06:35 +00:00
|
|
|
uint32_t tod = mc13783_read(MC13783_RTC_ALARM);
|
2008-05-05 12:53:03 +00:00
|
|
|
*h = tod / 3600;
|
|
|
|
*m = tod % 3600 / 60;
|
|
|
|
}
|