DS3231 on BeagleBone Black with FreeBSD 13-CURRENT exactly 20 h off backwards
Dr. Rolf Jansen
freebsd-rj at obsigna.com
Thu Jul 16 15:36:06 UTC 2020
> Am 15.07.2020 um 23:32 schrieb Ian Lepore <ian at freebsd.org>:
>
> On Wed, 2020-07-15 at 12:15 -0300, Dr. Rolf Jansen wrote:
>>> Am 15.07.2020 um 09:52 schrieb Dr. Rolf Jansen <
>>> freebsd-rj at obsigna.com <mailto:freebsd-rj at obsigna.com>>:
>>>
>>> I added a DS3231 module to the i2c2 bus of the BBB running 13-
>>> CURRENT. Everything work fine, except that when I set a time in
>>> the range of 20:00 to 24:00 UTC, then on starting up the RTC
>>> reports a date/time of exactly 20 hours off backwards. While, when
>>> I set a time in the range from 0:00 to 19:59 UTC, it would be
>>> correctly stored by the RTC.
>>>
>>> Looking at the Maxim DS3231 datasheet (
>>> https://datasheets.maximintegrated.com/en/ds/DS3231.pdf#page=11 <https://datasheets.maximintegrated.com/en/ds/DS3231.pdf#page=11> <
>>> https://datasheets.maximintegrated.com/en/ds/DS3231.pdf#page=11 <https://datasheets.maximintegrated.com/en/ds/DS3231.pdf#page=11>>),
>>> it might be that something gets mixed-up when setting bits 5 and 6
>>> of the hours register. In the history of ds3231.c, I saw that 24
>>> hour mode is not more forced anymore. Perhaps an unresolved
>>> ambiguity was introduced by this change.
>>>
>>> BTW: the following looks strange:
>>>
>>>
> https://github.com/freebsd/freebsd/blob/b2d136be8c26e5efaf82b7bb25432207a682e250/sys/dev/iicbus/ds3231.c#L526 <https://github.com/freebsd/freebsd/blob/b2d136be8c26e5efaf82b7bb25432207a682e250/sys/dev/iicbus/ds3231.c#L526>
>>> <
>>> https://github.com/freebsd/freebsd/blob/b2d136be8c26e5efaf82b7bb25432207a682e250/sys/dev/iicbus/ds3231.c#L526 <https://github.com/freebsd/freebsd/blob/b2d136be8c26e5efaf82b7bb25432207a682e250/sys/dev/iicbus/ds3231.c#L526>
>>>>
>>>
>>> This would add 256 instead of 100 when rolling over the century,
>>> really? On real world systems this will let to a Year-2100 problem,
>>> better we solve this quickly, since the time is running, and 80
>>> years compares to nothing on the geologic time scale :-D
>>
>> For the time being I resolved the issue for me, by completely
>> dropping AM/PM support - I don’t need it, and I anyway always need to
>> remember that AM means (Am Morgen :-).
>>
>> DS3231_HOUR_MASK_24HR is definitely wrong, since this prevents the
>> setting of times above 20 h. Reading said data sheet, I am almost
>> sure, that DS3231_HOUR_MASK_24HR must be the same as
>> DS3231_HOUR_MASK_12HR = 0x3f.
>>
>
> The driver originally forced the chip into 24-hour mode. I'm the one
> who added support for 12 or 24 hour mode, so this is probably my fault.
> It seems nicer to me to try to deal with whatever mode the chip is
> already in (in case you're dual-booting into some other OS that wants
> it to be a certain way). I'm pretty sure I've got one of those chips
> around here somewhere, I'll find some time this weekend to get the
> driver fixed.
>
> -- Ian
I appreciate your efforts for AM/PM support, only I am not sufficiently familiar with the very details of this format, for example I always got wrong the special meanings of 0:00 AM vs. 0:00 PM and 12:00 AM vs. 12:00 PM. So, I am the wrong person to bugfix AM/PM issues. I applied the temporary dirty fix of dropping out AM/PM only for getting the driver quickly working on my side, and I could continue with my current project. Once the driver is fixed upstream, I will use that one, of course.
That said, I wrote a sysctl function for directly getting/setting the time in the RTC with unix time values. Perhaps, something like this would facilitate your debugging efforts, and here it comes:
static int
ds3231_unixtime_sysctl(SYSCTL_HANDLER_ARGS)
{
int c, error;
struct timespec ts = {};
struct bcd_clocktime bct;
struct ds3231_softc *sc = (struct ds3231_softc *)arg1;
uint8_t data[7];
if (req->newptr == NULL) { // get the unixtime
/* If the clock halted, we don't have good data. */
if ((error = ds3231_status_read(sc)) != 0) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
}
if (sc->sc_status & DS3231_STATUS_OSF)
return (EINVAL);
error = iicdev_readfrom(sc->sc_dev, DS3231_SECS, data, sizeof(data),
IIC_INTRWAIT);
if (error != 0) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
}
bct.nsec = 0;
bct.sec = data[DS3231_SECS] & DS3231_SECS_MASK;
bct.min = data[DS3231_MINS] & DS3231_MINS_MASK;
bct.hour = data[DS3231_HOUR] & DS3231_HOUR_MASK_12HR;
bct.day = data[DS3231_DATE] & DS3231_DATE_MASK;
bct.mon = data[DS3231_MONTH] & DS3231_MONTH_MASK;
bct.year = data[DS3231_YEAR] & DS3231_YEAR_MASK;
bct.ispm = data[DS3231_HOUR] & DS3231_HOUR_IS_PM;
/*
* If the century flag has toggled since we last saw it, there has been
* a century rollover. If this is the first time we're seeing it,
* remember the state so we can preserve its polarity on writes.
*/
c = (data[DS3231_MONTH] & DS3231_C_MASK) ? 1 : 0;
if (sc->sc_last_c == -1)
sc->sc_last_c = c;
else if (c != sc->sc_last_c) {
sc->sc_year0 += 100;
sc->sc_last_c = c;
}
bct.year |= sc->sc_year0;
error = clock_bcd_to_ts(&bct, &ts, false);
if (error == 0) {
error = sysctl_handle_long(oidp, &ts.tv_sec, 0, req);
}
}
else if ((error = sysctl_handle_long(oidp, &ts.tv_sec, 0, req) == 0)) { // set the unixtime
/*
* We request a timespec with no resolution-adjustment. That also
* disables utc adjustment, so apply that ourselves.
*/
ts.tv_sec -= utc_offset();
clock_ts_to_bcd(&ts, &bct, false);
data[DS3231_SECS] = bct.sec;
data[DS3231_MINS] = bct.min;
data[DS3231_HOUR] = bct.hour | 0b01000000;
data[DS3231_DATE] = bct.day;
data[DS3231_WEEKDAY] = bct.dow + 1;
data[DS3231_MONTH] = bct.mon;
data[DS3231_YEAR] = bct.year & 0xff;
if (sc->sc_last_c)
data[DS3231_MONTH] |= DS3231_C_MASK;
/* Write the time back to RTC. */
error = iicdev_writeto(sc->sc_dev, DS3231_SECS, data, sizeof(data),
IIC_INTRWAIT);
if (error != 0) {
device_printf(sc->sc_dev, "cannot write to RTC.\n");
return (error);
}
/*
* Unlike most hardware, the osc-was-stopped bit does not clear itself
* after setting the time, it has to be manually written to zero.
*/
if (sc->sc_status & DS3231_STATUS_OSF) {
if ((error = ds3231_status_read(sc)) != 0) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
}
sc->sc_status &= ~DS3231_STATUS_OSF;
if ((error = ds3231_status_write(sc, 0, 0)) != 0) {
device_printf(sc->sc_dev, "cannot write to RTC.\n");
return (error);
}
}
}
return (error);
}
...
/* Date/Time. */
SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "unixtime",
CTLFLAG_RW | CTLTYPE_ULONG | CTLFLAG_MPSAFE, sc, 0,
ds3231_unixtime_sysctl, "LU", "get/set the Date/Time formatted as a UNIX time stamp");
/* Temperature. */
...
Then, I wrote a shell script for testing, whether setting/getting the time works as expected (checkds3231.sh):
#!/bin/sh
DATE=`date "+%Y-%m-%d"`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 00:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 01:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 02:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 03:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 04:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 05:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 06:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 07:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 08:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 09:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 10:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 11:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 12:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 13:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 14:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 15:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 16:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 17:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 18:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 19:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 20:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 21:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 22:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date -jf "%Y-%m-%d %H:%M:%S" "+%s" $DATE" 23:26:14"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
sysctl dev.ds3231.0.unixtime=`date "+%s"` > /dev/null 2>&1
date -jf "%s" "+%Y-%m-%d %H:%M:%S" `sysctl -n dev.ds3231.0.unixtime`
The sample output here looks good:
2020-07-16 00:26:14
2020-07-16 01:26:14
2020-07-16 02:26:14
2020-07-16 03:26:14
2020-07-16 04:26:14
2020-07-16 05:26:14
2020-07-16 06:26:14
2020-07-16 07:26:14
2020-07-16 08:26:14
2020-07-16 09:26:14
2020-07-16 10:26:14
2020-07-16 11:26:14
2020-07-16 12:26:14
2020-07-16 13:26:14
2020-07-16 14:26:14
2020-07-16 15:26:14
2020-07-16 16:26:14
2020-07-16 17:26:14
2020-07-16 18:26:14
2020-07-16 19:26:14
2020-07-16 20:26:14
2020-07-16 21:26:14
2020-07-16 22:26:14
2020-07-16 23:26:14
2020-07-16 12:32:12
Best regards
Rolf
More information about the freebsd-arm
mailing list