rtc: ds1307: fix century bit support

Add an option to properly support the century bit of ds1337 and compatibles
and ds1340.
Because the driver had a bug until now, it is not possible to switch users
to the fixed code directly as RTCs in the field will wrongly have the
century bit set.

Acked-by: Arnaud Ebalard <arno@natisbad.org>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
This commit is contained in:
Alexandre Belloni 2016-07-13 02:26:08 +02:00
parent 59e5e70c95
commit 50d6c0ea81
2 changed files with 57 additions and 5 deletions

View File

@ -234,6 +234,20 @@ config RTC_DRV_DS1307_HWMON
Say Y here if you want to expose temperature sensor data on
rtc-ds1307 (only DS3231)
config RTC_DRV_DS1307_CENTURY
bool "Century bit support for rtc-ds1307"
depends on RTC_DRV_DS1307
default n
help
The DS1307 driver suffered from a bug where it was enabling the
century bit inconditionnally but never used it when reading the time.
It made the driver unable to support dates beyond 2099.
Setting this option will add proper support for the century bit but if
the time was previously set using a kernel predating this option,
reading the date will return a date in the next century.
To solve that, you could boot a kernel without this option set, set
the RTC date and then boot a kernel with this option set.
config RTC_DRV_DS1374
tristate "Dallas/Maxim DS1374"
help

View File

@ -382,10 +382,25 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
t->tm_mon = bcd2bin(tmp) - 1;
/* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;
#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
switch (ds1307->type) {
case ds_1337:
case ds_1339:
case ds_3231:
if (ds1307->regs[DS1307_REG_MONTH] & DS1337_BIT_CENTURY)
t->tm_year += 100;
break;
case ds_1340:
if (ds1307->regs[DS1307_REG_HOUR] & DS1340_BIT_CENTURY)
t->tm_year += 100;
break;
default:
break;
}
#endif
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read", t->tm_sec, t->tm_min,
@ -409,6 +424,27 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
t->tm_hour, t->tm_mday,
t->tm_mon, t->tm_year, t->tm_wday);
#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
if (t->tm_year < 100)
return -EINVAL;
switch (ds1307->type) {
case ds_1337:
case ds_1339:
case ds_3231:
case ds_1340:
if (t->tm_year > 299)
return -EINVAL;
default:
if (t->tm_year > 199)
return -EINVAL;
break;
}
#else
if (t->tm_year < 100 || t->tm_year > 199)
return -EINVAL;
#endif
buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
buf[DS1307_REG_MIN] = bin2bcd(t->tm_min);
buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
@ -424,11 +460,13 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
case ds_1337:
case ds_1339:
case ds_3231:
buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
if (t->tm_year > 199)
buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
break;
case ds_1340:
buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
| DS1340_BIT_CENTURY;
buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN;
if (t->tm_year > 199)
buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY;
break;
case mcp794xx:
/*