Page MenuHomeFreeBSD

D35837.id109911.diff
No OneTemporary

D35837.id109911.diff

diff --git a/sys/dev/iicbus/ds1307.c b/sys/dev/iicbus/ds1307.c
--- a/sys/dev/iicbus/ds1307.c
+++ b/sys/dev/iicbus/ds1307.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2015 Luiz Otavio O Souza <loos@FreeBSD.org>
+ * Copyright (c) 2022 Mathew McBride <matt@traverse.com.au>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -54,23 +55,31 @@
#include "clock_if.h"
#include "iicbus_if.h"
+enum {
+ TYPE_DS1307,
+ TYPE_MAXIM1307,
+ TYPE_MICROCHIP_MCP7491X,
+ TYPE_EPSON_RX8035,
+ TYPE_COUNT
+};
+
struct ds1307_softc {
- device_t sc_dev;
- struct intr_config_hook
- enum_hook;
- uint8_t sc_ctrl;
- bool sc_mcp7941x;
- bool sc_use_ampm;
+ device_t sc_dev;
+ struct intr_config_hook enum_hook;
+ uint32_t chiptype;
+ uint8_t sc_ctrl;
+ bool sc_use_ampm;
};
static void ds1307_start(void *);
#ifdef FDT
static const struct ofw_compat_data ds1307_compat_data[] = {
- {"dallas,ds1307", (uintptr_t)"Dallas DS1307 RTC"},
- {"maxim,ds1307", (uintptr_t)"Maxim DS1307 RTC"},
- {"microchip,mcp7941x", (uintptr_t)"Microchip MCP7941x RTC"},
- { NULL, 0 }
+ {"dallas,ds1307", TYPE_DS1307},
+ {"maxim,ds1307", TYPE_MAXIM1307},
+ {"microchip,mcp7941x", TYPE_MICROCHIP_MCP7491X},
+ {"epson,rx8035", TYPE_EPSON_RX8035},
+ { NULL, 0 }
};
#endif
@@ -96,7 +105,8 @@
sc->sc_ctrl = 0;
error = ds1307_read1(sc->sc_dev, DS1307_CONTROL, &sc->sc_ctrl);
if (error) {
- device_printf(sc->sc_dev, "cannot read from RTC.\n");
+ device_printf(sc->sc_dev, "%s: cannot read from RTC: %d\n",
+ __func__, error);
return (error);
}
@@ -112,7 +122,8 @@
ctrl = sc->sc_ctrl & DS1307_CTRL_MASK;
error = ds1307_write1(sc->sc_dev, DS1307_CONTROL, ctrl);
if (error != 0)
- device_printf(sc->sc_dev, "cannot write to RTC.\n");
+ device_printf(sc->sc_dev, "%s: cannot write to RTC: %d\n",
+ __func__, error);
return (error);
}
@@ -127,7 +138,7 @@
error = ds1307_ctrl_read(sc);
if (error != 0)
return (error);
- if (sc->sc_mcp7941x)
+ if (sc->chiptype == TYPE_MICROCHIP_MCP74)
sqwe_bit = MCP7941X_CTRL_SQWE;
else
sqwe_bit = DS1307_CTRL_SQWE;
@@ -216,11 +227,29 @@
return (ENXIO);
compat = ofw_bus_search_compatible(dev, ds1307_compat_data);
- if (compat->ocd_str != NULL) {
- device_set_desc(dev, (const char *)compat->ocd_data);
- return (BUS_PROBE_DEFAULT);
+ if (compat->ocd_str == NULL)
+ return (ENXIO);
+
+ switch(compat->ocd_data) {
+ case TYPE_DS1307:
+ device_set_desc(dev, "Dallas DS1307");
+ break;
+ case TYPE_MAXIM1307:
+ device_set_desc(dev, "Maxim DS1307");
+ break;
+ case TYPE_MICROCHIP_MCP7491X:
+ device_set_desc(dev, "Microchip MCP7491X");
+ break;
+ case TYPE_EPSON_RX8035:
+ device_set_desc(dev, "Epson RX-8035");
+ break;
+ default:
+ device_set_desc(dev, "Unknown DS1307-like device");
+ break;
}
+ return (BUS_PROBE_DEFAULT);
#endif
+
device_set_desc(dev, "Maxim DS1307 RTC");
return (BUS_PROBE_NOWILDCARD);
}
@@ -228,16 +257,23 @@
static int
ds1307_attach(device_t dev)
{
+#ifdef FDT
+ const struct ofw_compat_data *compat;
+#endif
struct ds1307_softc *sc;
sc = device_get_softc(dev);
sc->sc_dev = dev;
sc->enum_hook.ich_func = ds1307_start;
sc->enum_hook.ich_arg = dev;
-
#ifdef FDT
- if (ofw_bus_is_compatible(dev, "microchip,mcp7941x"))
- sc->sc_mcp7941x = 1;
+ compat = ofw_bus_search_compatible(dev, ds1307_compat_data);
+ sc->chiptype = compat->ocd_data;
+ /* Unify the chiptypes to DS1307 where possible. */
+ if (sc->chiptype == TYPE_MAXIM1307)
+ sc->chiptype = TYPE_DS1307;
+#else
+ sc->chiptype = TYPE_DS1307;
#endif
/*
@@ -258,6 +294,110 @@
return (0);
}
+static bool
+is_epson_time_valid(struct ds1307_softc *sc)
+{
+ device_t dev;
+ int error;
+ uint8_t ctrl2;
+
+ dev = sc->sc_dev;
+
+ /*
+ * The RX-8035 single register read is non-standard
+ * Refer to section 8.9.5 of the RX-8035 application manual:
+ * "I2C bus basic transfer format", under "Standard Read Method".
+ * Basically, register to read goes into the top 4 bits.
+ */
+ error = ds1307_read1(dev, (RX8035_CTRL_2 << 4), &ctrl2);
+ if (error) {
+ device_printf(dev, "%s cannot read Control 2 register: %d\n",
+ __func__, error);
+ return (false);
+ }
+
+ if (ctrl2 & RX8035_CTRL_2_XSTP) {
+ device_printf(dev, "Oscillation stop detected (ctrl2=%#02x)\n",
+ ctrl2);
+ return (false);
+ }
+
+ /*
+ * Power on reset (PON) generally implies oscillation stop,
+ * but catch it as well to be sure.
+ */
+ if (ctrl2 & RX8035_CTRL_2_PON) {
+ device_printf(dev, "Power-on reset detected (ctrl2=%#02x)\n",
+ ctrl2);
+ return (false);
+ }
+
+ return (true);
+}
+
+static int
+mark_epson_time_valid(struct ds1307_softc *sc)
+{
+ device_t dev;
+ int error;
+ uint8_t ctrl2;
+ uint8_t control_mask;
+
+ dev = sc->sc_dev;
+
+ error = ds1307_read1(dev, (RX8035_CTRL_2 << 4), &ctrl2);
+ if (error) {
+ device_printf(dev, "%s cannot read Control 2 register: %d\n",
+ __func__, error);
+ return (false);
+ }
+
+ control_mask = (RX8035_CTRL_2_PON | RX8035_CTRL_2_XSTP | RX8035_CTRL_2_VDET);
+ ctrl2 = ctrl2 & ~(control_mask);
+
+ error = ds1307_write1(dev, (RX8035_CTRL_2 << 4), ctrl2);
+ if (error) {
+ device_printf(dev, "%s cannot write to Control 2 register: %d\n",
+ __func__, error);
+ return (false);
+ }
+ return (true);
+}
+
+static bool is_dev_time_valid(struct ds1307_softc *sc)
+{
+ device_t dev;
+ int error;
+ uint8_t osc_en;
+ uint8_t secs;
+
+ /* Epson RTCs have different control/status registers. */
+ if (sc->chiptype == TYPE_EPSON_RX8035)
+ return (is_epson_time_valid(sc));
+
+ dev = sc->sc_dev;
+ /* Check if the oscillator is disabled. */
+ error = ds1307_read1(dev, DS1307_SECS, &secs);
+ if (error) {
+ device_printf(dev, "%s: cannot read from RTC: %d\n",
+ __func__, error);
+ return (false);
+ }
+
+ switch (sc->chiptype) {
+ case TYPE_MICROCHIP_MCP7491X:
+ osc_en = 0x80;
+ break;
+ default:
+ osc_en = 0x00;
+ break;
+ }
+ if (((secs & DS1307_SECS_CH) ^ osc_en) != 0)
+ return (false);
+
+ return (true);
+}
+
static void
ds1307_start(void *xdev)
{
@@ -266,33 +406,28 @@
struct sysctl_ctx_list *ctx;
struct sysctl_oid *tree_node;
struct sysctl_oid_list *tree;
- uint8_t secs;
- uint8_t osc_en;
dev = (device_t)xdev;
sc = device_get_softc(dev);
- ctx = device_get_sysctl_ctx(dev);
- tree_node = device_get_sysctl_tree(dev);
- tree = SYSCTL_CHILDREN(tree_node);
config_intrhook_disestablish(&sc->enum_hook);
- /* 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 (sc->sc_mcp7941x)
- osc_en = 0x80;
- else
- osc_en = 0x00;
-
- if (((secs & DS1307_SECS_CH) ^ osc_en) != 0) {
- device_printf(sc->sc_dev,
+ if (!is_dev_time_valid(sc))
+ device_printf(dev,
"WARNING: RTC clock stopped, check the battery.\n");
- }
- /* Configuration parameters. */
+ /*
+ * Configuration parameters:
+ * square wave output cannot be changed or inhibited on the RX-8035,
+ * so don't present the sysctls there.
+ */
+ if (sc->chiptype == TYPE_EPSON_RX8035)
+ goto skip_sysctl;
+
+ ctx = device_get_sysctl_ctx(dev);
+ tree_node = device_get_sysctl_tree(dev);
+ tree = SYSCTL_CHILDREN(tree_node);
+
SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqwe",
CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
ds1307_sqwe_sysctl, "IU", "DS1307 square-wave enable");
@@ -303,6 +438,7 @@
SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_out",
CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
ds1307_sqw_out_sysctl, "IU", "DS1307 square-wave output state");
+skip_sysctl:
/*
* Register as a clock with 1 second resolution. Schedule the
@@ -316,30 +452,36 @@
static int
ds1307_gettime(device_t dev, struct timespec *ts)
{
- int error;
struct bcd_clocktime bct;
struct ds1307_softc *sc;
- uint8_t data[7], hourmask, st_mask;
+ int error;
+ uint8_t data[7], hourmask, ampm_mode;
sc = device_get_softc(dev);
error = iicdev_readfrom(sc->sc_dev, DS1307_SECS, data, sizeof(data),
IIC_INTRWAIT);
if (error != 0) {
- device_printf(dev, "cannot read from RTC.\n");
+ device_printf(dev, "%s: cannot read from RTC: %d\n",
+ __func__, error);
return (error);
}
- /* If the clock halted, we don't have good data. */
- if (sc->sc_mcp7941x)
- st_mask = 0x80;
- else
- st_mask = 0x00;
-
- if (((data[DS1307_SECS] & DS1307_SECS_CH) ^ st_mask) != 0)
+ if (!is_dev_time_valid(sc)) {
+ device_printf(dev, "Device time not valid.\n");
return (EINVAL);
+ }
- /* If chip is in AM/PM mode remember that. */
- if (data[DS1307_HOUR] & DS1307_HOUR_USE_AMPM) {
+ /*
+ * If the chip is in AM/PM mode remember that.
+ * The EPSON uses a 1 to signify 24 hour mode, while the DS uses a 0,
+ * in slighly different positions.
+ */
+ if (sc->chiptype == TYPE_EPSON_RX8035)
+ ampm_mode = !(data[DS1307_HOUR] & RX8035_HOUR_USE_24);
+ else
+ ampm_mode = data[DS1307_HOUR] & DS1307_HOUR_USE_AMPM;
+
+ if (ampm_mode) {
sc->sc_use_ampm = true;
hourmask = DS1307_HOUR_MASK_12HR;
} else
@@ -377,12 +519,19 @@
clock_ts_to_bcd(ts, &bct, sc->sc_use_ampm);
clock_dbgprint_bcd(sc->sc_dev, CLOCK_DBG_WRITE, &bct);
- /* If the chip is in AM/PM mode, adjust hour and set flags as needed. */
+ /*
+ * If the chip is in AM/PM mode, adjust hour and set flags as needed.
+ * The AM/PM bit polarity and position is different on the EPSON.
+ */
if (sc->sc_use_ampm) {
- pmflags = DS1307_HOUR_USE_AMPM;
+ pmflags = (sc->chiptype != TYPE_EPSON_RX8035) ?
+ DS1307_HOUR_USE_AMPM : 0;
if (bct.ispm)
pmflags |= DS1307_HOUR_IS_PM;
- } else
+
+ } else if (sc->chiptype == TYPE_EPSON_RX8035)
+ pmflags = RX8035_HOUR_USE_24;
+ else
pmflags = 0;
data[DS1307_SECS] = bct.sec;
@@ -392,18 +541,23 @@
data[DS1307_WEEKDAY] = bct.dow;
data[DS1307_MONTH] = bct.mon;
data[DS1307_YEAR] = bct.year & 0xff;
- if (sc->sc_mcp7941x) {
+ if (sc->chiptype == TYPE_MICROCHIP_MCP7491X) {
data[DS1307_SECS] |= MCP7941X_SECS_ST;
data[DS1307_WEEKDAY] |= MCP7941X_WEEKDAY_VBATEN;
year = bcd2bin(bct.year >> 8) * 100 + bcd2bin(bct.year & 0xff);
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
data[DS1307_MONTH] |= MCP7941X_MONTH_LPYR;
}
+
/* Write the time back to RTC. */
error = iicdev_writeto(sc->sc_dev, DS1307_SECS, data, sizeof(data),
IIC_INTRWAIT);
if (error != 0)
- device_printf(dev, "cannot write to RTC.\n");
+ device_printf(dev, "%s: cannot write to RTC: %d\n",
+ __func__, error);
+
+ if (sc->chiptype == TYPE_EPSON_RX8035)
+ error = mark_epson_time_valid(sc);
return (error);
}
diff --git a/sys/dev/iicbus/ds1307reg.h b/sys/dev/iicbus/ds1307reg.h
--- a/sys/dev/iicbus/ds1307reg.h
+++ b/sys/dev/iicbus/ds1307reg.h
@@ -44,6 +44,7 @@
#define DS1307_HOUR_MASK_24HR 0x3f
#define DS1307_HOUR_IS_PM 0x20
#define DS1307_HOUR_USE_AMPM 0x40
+#define RX8035_HOUR_USE_24 0x80
#define DS1307_WEEKDAY 0x03
#define MCP7941X_WEEKDAY_VBATEN 0x08
#define DS1307_WEEKDAY_MASK 0x07
@@ -63,4 +64,9 @@
#define DS1307_CTRL_RS_MASK (DS1307_CTRL_RS1 | DS1307_CTRL_RS0)
#define DS1307_CTRL_MASK 0x93
+#define RX8035_CTRL_2 0x0F
+#define RX8035_CTRL_2_PON (1 << 4)
+#define RX8035_CTRL_2_XSTP (1 << 5)
+#define RX8035_CTRL_2_VDET (1 << 6)
+
#endif /* _DS1307REG_H_ */

File Metadata

Mime Type
text/plain
Expires
Wed, Sep 25, 3:28 AM (20 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
12726172
Default Alt Text
D35837.id109911.diff (11 KB)

Event Timeline