Bugfixes and enhancements...

Don't enable the oscillator when it is found to be stopped at init time,
just let the first setting of valid time start it.  But still report a dead
battery if it's stopped at init time.

Don't force the chip into 24hr mode, just cope with whatever mode it is
already in.

Align the RTC clock to top of second when setting it.
This commit is contained in:
ian 2017-07-30 18:46:38 +00:00
parent 91d0a1e5ff
commit 6f87a631b7
2 changed files with 74 additions and 70 deletions

View File

@ -56,20 +56,20 @@ __FBSDID("$FreeBSD$");
struct ds1307_softc {
device_t sc_dev;
int sc_year0;
struct intr_config_hook enum_hook;
uint16_t sc_addr; /* DS1307 slave address. */
struct intr_config_hook
enum_hook;
uint8_t sc_ctrl;
int sc_mcp7941x;
bool sc_mcp7941x;
bool sc_use_ampm;
};
static void ds1307_start(void *);
#ifdef FDT
static const struct ofw_compat_data ds1307_compat_data[] = {
{"dallas,ds1307", (uintptr_t)"Maxim DS1307 RTC"},
{"maxim,ds1307", (uintptr_t)"Maxim DS1307 RTC"},
{"microchip,mcp7941x", (uintptr_t)"Microchip MCP7941x RTC"},
{"dallas,ds1307", (uintptr_t)"Dallas DS1307 RTC"},
{"maxim,ds1307", (uintptr_t)"Maxim DS1307 RTC"},
{"microchip,mcp7941x", (uintptr_t)"Microchip MCP7941x RTC"},
{ NULL, 0 }
};
#endif
@ -117,48 +117,6 @@ ds1307_ctrl_write(struct ds1307_softc *sc)
return (error);
}
static int
ds1307_osc_enable(struct ds1307_softc *sc)
{
int error;
uint8_t secs;
error = ds1307_read1(sc->sc_dev, DS1307_SECS, &secs);
if (error) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
}
/* Check if the oscillator is disabled. */
if ((secs & DS1307_SECS_CH) == 0)
return (0);
device_printf(sc->sc_dev, "clock was halted, check the battery.\n");
secs &= DS1307_SECS_MASK;
error = ds1307_write1(sc->sc_dev, DS1307_SECS, secs);
if (error != 0)
device_printf(sc->sc_dev, "cannot write to RTC.\n");
return (error);
}
static int
ds1307_set_24hrs_mode(struct ds1307_softc *sc)
{
int error;
uint8_t hour;
error = ds1307_read1(sc->sc_dev, DS1307_HOUR, &hour);
if (error) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
}
hour &= DS1307_HOUR_MASK;
error = ds1307_write1(sc->sc_dev, DS1307_HOUR, hour);
if (error != 0)
device_printf(sc->sc_dev, "cannot write to RTC.\n");
return (error);
}
static int
ds1307_sqwe_sysctl(SYSCTL_HANDLER_ARGS)
{
@ -268,7 +226,7 @@ ds1307_probe(device_t dev)
#else
device_set_desc(dev, "Maxim DS1307 RTC");
return (BUS_PROBE_DEFAULT);
return (BUS_PROBE_NOWILDCARD);
#endif
}
@ -279,8 +237,6 @@ ds1307_attach(device_t dev)
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->sc_addr = iicbus_get_addr(dev);
sc->sc_year0 = 1900;
sc->enum_hook.ich_func = ds1307_start;
sc->enum_hook.ich_arg = dev;
@ -307,6 +263,7 @@ ds1307_start(void *xdev)
struct sysctl_ctx_list *ctx;
struct sysctl_oid *tree_node;
struct sysctl_oid_list *tree;
uint8_t secs;
dev = (device_t)xdev;
sc = device_get_softc(dev);
@ -315,12 +272,16 @@ ds1307_start(void *xdev)
tree = SYSCTL_CHILDREN(tree_node);
config_intrhook_disestablish(&sc->enum_hook);
/* Set the 24 hours mode. */
if (ds1307_set_24hrs_mode(sc) != 0)
return;
/* Enable the oscillator if halted. */
if (ds1307_osc_enable(sc) != 0)
/* Check if the oscillator is disabled. */
if (ds1307_read1(sc->sc_dev, DS1307_SECS, &secs) != 0) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return;
}
if ((secs & DS1307_SECS_CH) != 0) {
device_printf(sc->sc_dev,
"WARNING: RTC clock stopped, check the battery.\n");
}
/* Configuration parameters. */
SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqwe",
@ -334,8 +295,8 @@ ds1307_start(void *xdev)
CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
ds1307_sqw_out_sysctl, "IU", "DS1307 square-wave output state");
/* 1 second resolution. */
clock_register(dev, 1000000);
/* Register as a clock with 1 second resolution. */
clock_register_flags(dev, 1000000, CLOCKF_SETTIME_NO_TS);
}
static int
@ -344,7 +305,7 @@ ds1307_gettime(device_t dev, struct timespec *ts)
int error;
struct clocktime ct;
struct ds1307_softc *sc;
uint8_t data[7];
uint8_t data[7], hourmask;
sc = device_get_softc(dev);
error = iicdev_readfrom(sc->sc_dev, DS1307_SECS, data, sizeof(data),
@ -353,14 +314,28 @@ ds1307_gettime(device_t dev, struct timespec *ts)
device_printf(dev, "cannot read from RTC.\n");
return (error);
}
/* If chip is in AM/PM mode remember that. */
if (data[DS1307_HOUR] & DS1307_HOUR_USE_AMPM) {
sc->sc_use_ampm = true;
hourmask = DS1307_HOUR_MASK_12HR;
} else
hourmask = DS1307_HOUR_MASK_24HR;
ct.nsec = 0;
ct.sec = FROMBCD(data[DS1307_SECS] & DS1307_SECS_MASK);
ct.min = FROMBCD(data[DS1307_MINS] & DS1307_MINS_MASK);
ct.hour = FROMBCD(data[DS1307_HOUR] & DS1307_HOUR_MASK);
ct.day = FROMBCD(data[DS1307_DATE] & DS1307_DATE_MASK);
ct.dow = data[DS1307_WEEKDAY] & DS1307_WEEKDAY_MASK;
ct.mon = FROMBCD(data[DS1307_MONTH] & DS1307_MONTH_MASK);
ct.year = FROMBCD(data[DS1307_YEAR] & DS1307_YEAR_MASK);
ct.sec = FROMBCD(data[DS1307_SECS] & DS1307_SECS_MASK);
ct.min = FROMBCD(data[DS1307_MINS] & DS1307_MINS_MASK);
ct.hour = FROMBCD(data[DS1307_HOUR] & hourmask);
ct.day = FROMBCD(data[DS1307_DATE] & DS1307_DATE_MASK);
ct.mon = FROMBCD(data[DS1307_MONTH] & DS1307_MONTH_MASK);
ct.year = FROMBCD(data[DS1307_YEAR] & DS1307_YEAR_MASK);
if (sc->sc_use_ampm) {
if (ct.hour == 12)
ct.hour = 0;
if (data[DS1307_HOUR] & DS1307_HOUR_IS_PM)
ct.hour += 12;
}
return (clock_ct_to_ts(&ct, ts));
}
@ -368,17 +343,43 @@ ds1307_gettime(device_t dev, struct timespec *ts)
static int
ds1307_settime(device_t dev, struct timespec *ts)
{
int error;
struct clocktime ct;
struct ds1307_softc *sc;
long waitns;
int error;
uint8_t data[7];
uint8_t pmflags;
sc = device_get_softc(dev);
/* Sleep until 1ms into the second, to align RTC's second to ours. */
getnanotime(ts);
waitns = 1000000 - ts->tv_nsec;
if (waitns < 0)
waitns += 1000000000;
pause_sbt("set1307", nstosbt(waitns), 0, C_PREL(31));
/* Grab a fresh post-sleep idea of the time. */
getnanotime(ts);
ts->tv_sec -= utc_offset();
ts->tv_nsec = 0;
clock_ts_to_ct(ts, &ct);
/* If the chip is in AM/PM mode, adjust hour and set flags as needed. */
if (sc->sc_use_ampm) {
pmflags = DS1307_HOUR_USE_AMPM;
if (ct.hour >= 12) {
ct.hour -= 12;
pmflags |= DS1307_HOUR_IS_PM;
}
if (ct.hour == 0)
ct.hour = 12;
} else
pmflags = 0;
data[DS1307_SECS] = TOBCD(ct.sec);
data[DS1307_MINS] = TOBCD(ct.min);
data[DS1307_HOUR] = TOBCD(ct.hour);
data[DS1307_HOUR] = TOBCD(ct.hour) | pmflags;
data[DS1307_DATE] = TOBCD(ct.day);
data[DS1307_WEEKDAY] = ct.dow;
data[DS1307_MONTH] = TOBCD(ct.mon);

View File

@ -39,7 +39,10 @@
#define DS1307_MINS 0x01
#define DS1307_MINS_MASK 0x7f
#define DS1307_HOUR 0x02
#define DS1307_HOUR_MASK 0x3f
#define DS1307_HOUR_MASK_12HR 0x1f
#define DS1307_HOUR_MASK_24HR 0x3f
#define DS1307_HOUR_IS_PM 0x20
#define DS1307_HOUR_USE_AMPM 0x40
#define DS1307_WEEKDAY 0x03
#define DS1307_WEEKDAY_MASK 0x07
#define DS1307_DATE 0x04