Commit ba3cfbd6 authored by Russell King's avatar Russell King

[ARM] Fix Acorn RTC year handling.

Acorn decided to use an esoteric method for handling the year.
Update RTC implementation to follow this method.
parent b9ed68e3
...@@ -34,9 +34,13 @@ extern int (*set_rtc)(void); ...@@ -34,9 +34,13 @@ extern int (*set_rtc)(void);
static struct i2c_client *rtc_client; static struct i2c_client *rtc_client;
static const unsigned char days_in_mon[] = static const unsigned char days_in_mon[] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static unsigned int rtc_epoch = 1900;
#define CMOS_CHECKSUM (63) #define CMOS_CHECKSUM (63)
/*
* Acorn machines store the year in the static RAM at
* location 128.
*/
#define CMOS_YEAR (64 + 128) #define CMOS_YEAR (64 + 128)
static inline int rtc_command(int cmd, void *data) static inline int rtc_command(int cmd, void *data)
...@@ -49,6 +53,38 @@ static inline int rtc_command(int cmd, void *data) ...@@ -49,6 +53,38 @@ static inline int rtc_command(int cmd, void *data)
return ret; return ret;
} }
/*
* Update the century + year bytes in the CMOS RAM, ensuring
* that the check byte is correctly adjusted for the change.
*/
static int rtc_update_year(unsigned int new_year)
{
unsigned char yr[2], chk;
struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr };
struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk };
int ret;
ret = rtc_command(MEM_READ, &cmos_check);
if (ret)
goto out;
ret = rtc_command(MEM_READ, &cmos_year);
if (ret)
goto out;
chk -= yr[1] + yr[0];
yr[1] = new_year / 100;
yr[0] = new_year % 100;
chk += yr[1] + yr[0];
ret = rtc_command(MEM_WRITE, &cmos_year);
if (ret == 0)
ret = rtc_command(MEM_WRITE, &cmos_check);
out:
return ret;
}
/* /*
* Read the current RTC time and date, and update xtime. * Read the current RTC time and date, and update xtime.
*/ */
...@@ -56,45 +92,51 @@ static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year) ...@@ -56,45 +92,51 @@ static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year)
{ {
unsigned char ctrl, yr[2]; unsigned char ctrl, yr[2];
struct mem rtcmem = { CMOS_YEAR, sizeof(yr), yr }; struct mem rtcmem = { CMOS_YEAR, sizeof(yr), yr };
int real_year, year_offset;
/* /*
* Ensure that the RTC is running. * Ensure that the RTC is running.
*/ */
rtc_command(RTC_GETCTRL, &ctrl); rtc_command(RTC_GETCTRL, &ctrl);
if (ctrl & 0xc0) { if (ctrl & 0xc0) {
unsigned char new_ctrl; unsigned char new_ctrl = ctrl & ~0xc0;
new_ctrl = ctrl & ~0xc0;
printk("RTC: resetting control %02X -> %02X\n", printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n",
ctrl, new_ctrl); ctrl, new_ctrl);
rtc_command(RTC_SETCTRL, &new_ctrl); rtc_command(RTC_SETCTRL, &new_ctrl);
} }
/* if (rtc_command(RTC_GETDATETIME, rtctm) ||
* Acorn machines store the year in rtc_command(MEM_READ, &rtcmem))
* the static RAM at location 192.
*/
if (rtc_command(MEM_READ, &rtcmem))
return; return;
if (rtc_command(RTC_GETDATETIME, rtctm)) real_year = yr[0];
return;
*year = yr[1] * 100 + yr[0]; /*
* The RTC year holds the LSB two bits of the current
* year, which should reflect the LSB two bits of the
* CMOS copy of the year. Any difference indicates
* that we have to correct the CMOS version.
*/
year_offset = rtctm->year_off - (real_year & 3);
if (year_offset < 0)
/*
* RTC year wrapped. Adjust it appropriately.
*/
year_offset += 4;
*year = real_year + year_offset + yr[1] * 100;
} }
static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year) static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year)
{ {
unsigned char yr[2], leap, chk; unsigned char leap;
struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr };
struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk };
int ret; int ret;
leap = (!(year % 4) && (year % 100)) || !(year % 400); leap = (!(year % 4) && (year % 100)) || !(year % 400);
if (rtctm->mon > 12 || rtctm->mday == 0) if (rtctm->mon > 12 || rtctm->mon == 0 || rtctm->mday == 0)
return -EINVAL; return -EINVAL;
if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap))) if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap)))
...@@ -103,21 +145,16 @@ static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year) ...@@ -103,21 +145,16 @@ static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year)
if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60) if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60)
return -EINVAL; return -EINVAL;
ret = rtc_command(RTC_SETDATETIME, rtctm); /*
if (ret == 0) { * The RTC's own 2-bit year must reflect the least
rtc_command(MEM_READ, &cmos_check); * significant two bits of the CMOS year.
rtc_command(MEM_READ, &cmos_year); */
rtctm->year_off = (year % 100) & 3;
chk -= yr[1] + yr[0];
yr[1] = year / 100;
yr[0] = year % 100;
chk += yr[1] + yr[0]; ret = rtc_command(RTC_SETDATETIME, rtctm);
if (ret == 0)
ret = rtc_update_year(year);
rtc_command(MEM_WRITE, &cmos_year);
rtc_command(MEM_WRITE, &cmos_check);
}
return ret; return ret;
} }
...@@ -189,13 +226,12 @@ static int rtc_ioctl(struct inode *inode, struct file *file, ...@@ -189,13 +226,12 @@ static int rtc_ioctl(struct inode *inode, struct file *file,
rtc_raw.hours = rtctm.tm_hour; rtc_raw.hours = rtctm.tm_hour;
rtc_raw.mday = rtctm.tm_mday; rtc_raw.mday = rtctm.tm_mday;
rtc_raw.mon = rtctm.tm_mon + 1; rtc_raw.mon = rtctm.tm_mon + 1;
rtc_raw.year_off = 2;
year = rtctm.tm_year + 1900; year = rtctm.tm_year + 1900;
return set_rtc_time(&rtc_raw, year); return set_rtc_time(&rtc_raw, year);
break; break;
case RTC_EPOCH_READ: case RTC_EPOCH_READ:
return put_user(rtc_epoch, (unsigned long *)arg); return put_user(1900, (unsigned long *)arg);
} }
return -EINVAL; return -EINVAL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment