summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/arm/allwinner/axp209.c52
-rw-r--r--sys/conf/NOTES17
-rw-r--r--sys/conf/files6
-rw-r--r--sys/dev/iicbus/ds1307.c215
-rw-r--r--sys/dev/iicbus/ds1307reg.h5
-rw-r--r--sys/dev/iicbus/ds133x.c363
-rw-r--r--sys/dev/iicbus/ds1374.c137
-rw-r--r--sys/dev/iicbus/ds13rtc.c629
-rw-r--r--sys/dev/iicbus/ds3231.c216
-rw-r--r--sys/dev/iicbus/ds3231reg.h5
-rw-r--r--sys/dev/iicbus/isl12xx.c354
-rw-r--r--sys/dev/iicbus/nxprtc.c837
-rw-r--r--sys/dev/iicbus/pcf8563.c247
-rw-r--r--sys/dev/iicbus/pcf8563reg.h58
-rw-r--r--sys/dev/iicbus/s35390a.c67
-rw-r--r--sys/mips/conf/XLP.hints5
-rw-r--r--sys/mips/conf/XLR2
-rw-r--r--sys/mips/conf/XLR642
-rw-r--r--sys/mips/conf/XLRN322
-rw-r--r--sys/mips/conf/std.XLP2
-rw-r--r--sys/mips/rmi/xlr_i2c.c12
-rw-r--r--sys/modules/i2c/Makefile20
-rw-r--r--sys/modules/i2c/ds1307/Makefile7
-rw-r--r--sys/modules/i2c/ds13rtc/Makefile7
-rw-r--r--sys/modules/i2c/ds3231/Makefile7
-rw-r--r--sys/modules/i2c/isl12xx/Makefile7
-rw-r--r--sys/modules/i2c/nxprtc/Makefile7
-rw-r--r--sys/modules/i2c/s35390a/Makefile7
28 files changed, 2204 insertions, 1091 deletions
diff --git a/sys/arm/allwinner/axp209.c b/sys/arm/allwinner/axp209.c
index 845c316..8957a99 100644
--- a/sys/arm/allwinner/axp209.c
+++ b/sys/arm/allwinner/axp209.c
@@ -47,7 +47,6 @@ __FBSDID("$FreeBSD$");
#include <sys/rman.h>
#include <sys/sysctl.h>
-#include <dev/iicbus/iicbus.h>
#include <dev/iicbus/iiconf.h>
#include <dev/gpio/gpiobusvar.h>
@@ -59,7 +58,6 @@ __FBSDID("$FreeBSD$");
#include <arm/allwinner/axp209reg.h>
-#include "iicbus_if.h"
#include "gpio_if.h"
#include "regdev_if.h"
@@ -602,7 +600,6 @@ enum AXP2XX_TYPE {
struct axp2xx_softc {
device_t dev;
- uint32_t addr;
struct resource * res[1];
void * intrcookie;
struct intr_config_hook intr_hook;
@@ -641,57 +638,15 @@ static struct resource_spec axp_res_spec[] = {
static int
axp2xx_read(device_t dev, uint8_t reg, uint8_t *data, uint8_t size)
{
- struct axp2xx_softc *sc = device_get_softc(dev);
- struct iic_msg msg[2];
- msg[0].slave = sc->addr;
- msg[0].flags = IIC_M_WR;
- msg[0].len = 1;
- msg[0].buf = &reg;
-
- msg[1].slave = sc->addr;
- msg[1].flags = IIC_M_RD;
- msg[1].len = size;
- msg[1].buf = data;
-
- return (iicbus_transfer(dev, msg, 2));
+ return (iicdev_readfrom(dev, reg, data, size, IIC_INTRWAIT));
}
static int
axp2xx_write(device_t dev, uint8_t reg, uint8_t data)
{
- uint8_t buffer[2];
- struct axp2xx_softc *sc = device_get_softc(dev);
- struct iic_msg msg[2];
- int nmsgs = 0;
-
- if (sc->type == AXP209) {
- buffer[0] = reg;
- buffer[1] = data;
-
- msg[0].slave = sc->addr;
- msg[0].flags = IIC_M_WR;
- msg[0].len = 2;
- msg[0].buf = buffer;
-
- nmsgs = 1;
- }
- else if (sc->type == AXP221) {
- msg[0].slave = sc->addr;
- msg[0].flags = IIC_M_WR;
- msg[0].len = 1;
- msg[0].buf = &reg;
-
- msg[1].slave = sc->addr;
- msg[1].flags = IIC_M_WR;
- msg[1].len = 1;
- msg[1].buf = &data;
- nmsgs = 2;
- }
- else
- return (EINVAL);
- return (iicbus_transfer(dev, msg, nmsgs));
+ return (iicdev_writeto(dev, reg, &data, sizeof(data), IIC_INTRWAIT));
}
static int
@@ -1239,7 +1194,6 @@ axp2xx_start(void *pdev)
dev = pdev;
sc = device_get_softc(dev);
- sc->addr = iicbus_get_addr(dev);
sc->dev = dev;
if (bootverbose) {
@@ -1451,4 +1405,4 @@ EARLY_DRIVER_MODULE(ofw_gpiobus, axp2xx_pmu, ofw_gpiobus_driver,
DRIVER_MODULE(gpioc, axp2xx_pmu, gpioc_driver, gpioc_devclass,
0, 0);
MODULE_VERSION(axp2xx, 1);
-MODULE_DEPEND(axp2xx, iicbus, 1, 1, 1);
+MODULE_DEPEND(axp2xx, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 90cd145..e6c5340 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2567,15 +2567,14 @@ device iicoc # OpenCores I2C controller support
# I2C peripheral devices
#
-# ds133x Dallas Semiconductor DS1337, DS1338 and DS1339 RTC
-# ds1374 Dallas Semiconductor DS1374 RTC
-# ds1672 Dallas Semiconductor DS1672 RTC
-# s35390a Seiko Instruments S-35390A RTC
-#
-device ds133x
-device ds1374
-device ds1672
-device s35390a
+device ds1307 # Dallas DS1307 RTC and compatible
+device ds13rtc # All Dallas/Maxim ds13xx chips
+device ds1672 # Dallas DS1672 RTC
+device ds3231 # Dallas DS3231 RTC + temperature
+device icee # AT24Cxxx and compatible EEPROMs
+device lm75 # LM75 compatible temperature sensor
+device nxprtc # NXP RTCs: PCA/PFC212x PCA/PCF85xx
+device s35390a # Seiko Instruments S-35390A RTC
# Parallel-Port Bus
#
diff --git a/sys/conf/files b/sys/conf/files
index 82eee15..58bbc02 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1686,8 +1686,7 @@ dev/ie/if_ie.c optional ie isa nowerror
dev/ie/if_ie_isa.c optional ie isa
dev/iicbus/ad7418.c optional ad7418
dev/iicbus/ds1307.c optional ds1307
-dev/iicbus/ds133x.c optional ds133x
-dev/iicbus/ds1374.c optional ds1374
+dev/iicbus/ds13rtc.c optional ds13rtc | ds133x | ds1374
dev/iicbus/ds1672.c optional ds1672
dev/iicbus/ds3231.c optional ds3231
dev/iicbus/icee.c optional icee
@@ -1702,9 +1701,10 @@ dev/iicbus/iiconf.c optional iicbus
dev/iicbus/iicsmb.c optional iicsmb \
dependency "iicbus_if.h"
dev/iicbus/iicoc.c optional iicoc
+dev/iicbus/isl12xx.c optional isl12xx
dev/iicbus/lm75.c optional lm75
+dev/iicbus/nxprtc.c optional nxprtc | pcf8563
dev/iicbus/ofw_iicbus.c optional fdt iicbus
-dev/iicbus/pcf8563.c optional pcf8563
dev/iicbus/s35390a.c optional s35390a
dev/iir/iir.c optional iir
dev/iir/iir_ctrl.c optional iir
diff --git a/sys/dev/iicbus/ds1307.c b/sys/dev/iicbus/ds1307.c
index 987db0d..83edbe5 100644
--- a/sys/dev/iicbus/ds1307.c
+++ b/sys/dev/iicbus/ds1307.c
@@ -56,43 +56,36 @@ __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
static int
-ds1307_read(device_t dev, uint16_t addr, uint8_t reg, uint8_t *data, size_t len)
+ds1307_read1(device_t dev, uint8_t reg, uint8_t *data)
{
- struct iic_msg msg[2] = {
- { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
- { addr, IIC_M_RD, len, data },
- };
- return (iicbus_transfer(dev, msg, nitems(msg)));
+ return (iicdev_readfrom(dev, reg, data, 1, IIC_INTRWAIT));
}
static int
-ds1307_write(device_t dev, uint16_t addr, uint8_t *data, size_t len)
+ds1307_write1(device_t dev, uint8_t reg, uint8_t data)
{
- struct iic_msg msg[1] = {
- { addr, IIC_M_WR, len, data },
- };
- return (iicbus_transfer(dev, msg, nitems(msg)));
+ return (iicdev_writeto(dev, reg, &data, 1, IIC_INTRWAIT));
}
static int
@@ -101,8 +94,7 @@ ds1307_ctrl_read(struct ds1307_softc *sc)
int error;
sc->sc_ctrl = 0;
- error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_CONTROL,
- &sc->sc_ctrl, sizeof(sc->sc_ctrl));
+ error = ds1307_read1(sc->sc_dev, DS1307_CONTROL, &sc->sc_ctrl);
if (error) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
@@ -115,59 +107,10 @@ static int
ds1307_ctrl_write(struct ds1307_softc *sc)
{
int error;
- uint8_t data[2];
+ uint8_t ctrl;
- data[0] = DS1307_CONTROL;
- data[1] = sc->sc_ctrl & DS1307_CTRL_MASK;
- error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
- if (error != 0)
- device_printf(sc->sc_dev, "cannot write to RTC.\n");
-
- return (error);
-}
-
-static int
-ds1307_osc_enable(struct ds1307_softc *sc)
-{
- int error;
- uint8_t data[2], secs;
-
- secs = 0;
- error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_SECS,
- &secs, sizeof(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");
- data[0] = DS1307_SECS;
- data[1] = secs & DS1307_SECS_MASK;
- error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
- 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 data[2], hour;
-
- hour = 0;
- error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_HOUR,
- &hour, sizeof(hour));
- if (error) {
- device_printf(sc->sc_dev, "cannot read from RTC.\n");
- return (error);
- }
- data[0] = DS1307_HOUR;
- data[1] = hour & DS1307_HOUR_MASK;
- error = ds1307_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
+ 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");
@@ -283,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
}
@@ -294,13 +237,13 @@ 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;
+#ifdef FDT
if (ofw_bus_is_compatible(dev, "microchip,mcp7941x"))
sc->sc_mcp7941x = 1;
+#endif
/*
* We have to wait until interrupts are enabled. Usually I2C read
@@ -312,6 +255,14 @@ ds1307_attach(device_t dev)
return (0);
}
+static int
+ds1307_detach(device_t dev)
+{
+
+ clock_unregister(dev);
+ return (0);
+}
+
static void
ds1307_start(void *xdev)
{
@@ -320,6 +271,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);
@@ -328,12 +280,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",
@@ -347,8 +303,13 @@ 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. Schedule the
+ * clock_settime() method to be called just after top-of-second;
+ * resetting the time resets top-of-second in the hardware.
+ */
+ clock_register_flags(dev, 1000000, CLOCKF_SETTIME_NO_ADJ);
+ clock_schedule(dev, 1);
}
static int
@@ -357,27 +318,41 @@ 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);
- memset(data, 0, sizeof(data));
- error = ds1307_read(sc->sc_dev, sc->sc_addr, DS1307_SECS,
- data, sizeof(data));
+ error = iicdev_readfrom(sc->sc_dev, DS1307_SECS, data, sizeof(data),
+ IIC_INTRWAIT);
if (error != 0) {
device_printf(dev, "cannot read from RTC.\n");
return (error);
}
+
+ /* If the clock halted, we don't have good data. */
+ if (data[DS1307_SECS] & DS1307_SECS_CH)
+ return (EINVAL);
+
+ /* 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.year += sc->sc_year0;
- if (ct.year < POSIX_BASE_YEAR)
- ct.year += 100; /* assume [1970, 2069] */
+ 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));
}
@@ -385,28 +360,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;
- uint8_t data[8];
+ int error;
+ uint8_t data[7];
+ uint8_t pmflags;
sc = device_get_softc(dev);
- /* Accuracy is only one second. */
- if (ts->tv_nsec >= 500000000)
- ts->tv_sec++;
- ts->tv_nsec = 0;
+
+ /*
+ * 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_ct(ts, &ct);
- memset(data, 0, sizeof(data));
- data[0] = DS1307_SECS;
- data[DS1307_SECS + 1] = TOBCD(ct.sec);
- data[DS1307_MINS + 1] = TOBCD(ct.min);
- data[DS1307_HOUR + 1] = TOBCD(ct.hour);
- data[DS1307_DATE + 1] = TOBCD(ct.day);
- data[DS1307_WEEKDAY + 1] = ct.dow;
- data[DS1307_MONTH + 1] = TOBCD(ct.mon);
- data[DS1307_YEAR + 1] = TOBCD(ct.year % 100);
+
+ /* 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) | pmflags;
+ data[DS1307_DATE] = TOBCD(ct.day);
+ data[DS1307_WEEKDAY] = ct.dow;
+ data[DS1307_MONTH] = TOBCD(ct.mon);
+ data[DS1307_YEAR] = TOBCD(ct.year % 100);
/* Write the time back to RTC. */
- error = ds1307_write(dev, sc->sc_addr, data, sizeof(data));
+ error = iicdev_writeto(sc->sc_dev, DS1307_SECS, data, sizeof(data),
+ IIC_INTRWAIT);
if (error != 0)
device_printf(dev, "cannot write to RTC.\n");
@@ -416,6 +406,7 @@ ds1307_settime(device_t dev, struct timespec *ts)
static device_method_t ds1307_methods[] = {
DEVMETHOD(device_probe, ds1307_probe),
DEVMETHOD(device_attach, ds1307_attach),
+ DEVMETHOD(device_detach, ds1307_detach),
DEVMETHOD(clock_gettime, ds1307_gettime),
DEVMETHOD(clock_settime, ds1307_settime),
diff --git a/sys/dev/iicbus/ds1307reg.h b/sys/dev/iicbus/ds1307reg.h
index ddad40b..b02ddb3 100644
--- a/sys/dev/iicbus/ds1307reg.h
+++ b/sys/dev/iicbus/ds1307reg.h
@@ -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
diff --git a/sys/dev/iicbus/ds133x.c b/sys/dev/iicbus/ds133x.c
deleted file mode 100644
index 1f703dc..0000000
--- a/sys/dev/iicbus/ds133x.c
+++ /dev/null
@@ -1,363 +0,0 @@
-/*-
- * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>,
- * Rafal Jaworowski <raj@FreeBSD.org>,
- * Piotr Ziecik <kosmo@semihalf.com>.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-/*
- * Dallas Semiconductor DS133X RTC sitting on the I2C bus.
- */
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-#include <sys/clock.h>
-#include <sys/time.h>
-#include <sys/bus.h>
-#include <sys/resource.h>
-#include <sys/rman.h>
-
-#include <dev/iicbus/iicbus.h>
-#include <dev/iicbus/iiconf.h>
-
-#include "iicbus_if.h"
-#include "clock_if.h"
-
-#define DS133X_DEVNAME "ds133x_rtc"
-
-#define DS133X_ADDR 0xd0 /* slave address */
-#define DS133X_DATE_REG 0x0
-#define DS133X_CTRL_REG 0x0e
-#define DS133X_OSCD_FLAG 0x80
-#define DS133X_OSF_FLAG 0x80
-
-#define DS133X_24H_FLAG 0x40 /* 24 hours mode. */
-#define DS133X_PM_FLAG 0x20 /* AM/PM bit. */
-#define DS133X_CENT_FLAG 0x80 /* Century selector. */
-#define DS133X_CENT_SHIFT 7
-
-#define DS1338_REG_CLOCK_HALT 0x00
-#define DS1338_REG_CONTROL 0x07
-#define DS1338_CLOCK_HALT (1 << 7)
-#define DS1338_OSC_STOP (1 << 5)
-
-#define DS1339_REG_CONTROL 0x0E
-#define DS1339_REG_STATUS 0x0F
-#define DS1339_OSC_STOP (1 << 7)
-#define DS1339_ENABLE_OSC (1 << 7)
-#define DS1339_BBSQI (1 << 5)
-
-#define HALFSEC 500000000 /* 1/2 of second. */
-
-#define MAX_IIC_DATA_SIZE 7
-
-enum {
- DS1337,
- DS1338,
- DS1339,
-};
-
-struct ds133x_softc {
- int sc_type;
- device_t sc_dev;
-};
-
-static int
-ds133x_read(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
-{
- struct iic_msg msg[] = {
- { DS133X_ADDR, IIC_M_WR, 1, &address },
- { DS133X_ADDR, IIC_M_RD, size, data },
- };
-
- return (iicbus_transfer(dev, msg, 2));
-}
-
-static int
-ds133x_write(device_t dev, uint8_t address, uint8_t *data, uint8_t size)
-{
- uint8_t buffer[MAX_IIC_DATA_SIZE + 1];
- struct iic_msg msg[] = {
- { DS133X_ADDR, IIC_M_WR, size + 1, buffer },
- };
-
- if (size > MAX_IIC_DATA_SIZE)
- return (ENOMEM);
-
- buffer[0] = address;
- memcpy(buffer + 1, data, size);
-
- return (iicbus_transfer(dev, msg, 1));
-}
-
-static int
-ds133x_detect(device_t dev, int *sc_type)
-{
- int error;
- uint8_t reg, orig;
-
- /*
- * Check for DS1338. At address 0x0F this chip has RAM, however
- * DS1337 and DS1339 have status register. Bits 6-2 in status
- * register will be always read as 0.
- */
-
- if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
- return (error);
-
- orig = reg;
- reg |= 0x7C;
-
- if ((error = ds133x_write(dev, DS1339_REG_STATUS, &reg, 1)))
- return (error);
-
- if ((error = ds133x_read(dev, DS1339_REG_STATUS, &reg, 1)))
- return (error);
-
- if ((reg & 0x7C) != 0) {
- /* This is DS1338 */
-
- if ((error = ds133x_write(dev, DS1339_REG_STATUS, &orig, 1)))
- return (error);
-
- *sc_type = DS1338;
-
- return (0);
- }
-
- /*
- * Now Check for DS1337. Bit 5 in control register of this chip will be
- * always read as 0. In DS1339 changing of this bit is safe until
- * chip is powered up.
- */
-
- if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
- return (error);
-
- orig = reg;
- reg |= DS1339_BBSQI;
-
- if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &reg, 1)))
- return (error);
-
- if ((error = ds133x_read(dev, DS1339_REG_CONTROL, &reg, 1)))
- return (error);
-
- if ((reg & DS1339_BBSQI) != 0) {
- /* This is DS1339 */
-
- if ((error = ds133x_write(dev, DS1339_REG_CONTROL, &orig, 1)))
- return (error);
-
- *sc_type = DS1339;
- return (0);
- }
-
- /* This is DS1337 */
- *sc_type = DS1337;
-
- return (0);
-}
-
-static int
-ds133x_init(device_t dev, uint8_t cs_reg, uint8_t cs_bit, uint8_t osf_reg,
- uint8_t osf_bit)
-{
- int error;
- uint8_t reg;
-
- if ((error = ds133x_read(dev, cs_reg, &reg, 1)))
- return (error);
-
- if (reg & cs_bit) { /* If clock is stopped - start it */
- reg &= ~cs_bit;
- if ((error = ds133x_write(dev, cs_reg, &reg, 1)))
- return (error);
- }
-
- if ((error = ds133x_read(dev, osf_reg, &reg, 1)))
- return (error);
-
- if (reg & osf_bit) { /* Clear oscillator stop flag */
- device_printf(dev, "RTC oscillator was stopped. Check system"
- " time and RTC battery.\n");
- reg &= ~osf_bit;
- if ((error = ds133x_write(dev, osf_reg, &reg, 1)))
- return (error);
- }
-
- return (0);
-}
-
-
-static void
-ds133x_identify(driver_t *driver, device_t parent)
-{
-
- if (device_find_child(parent, DS133X_DEVNAME, -1) == NULL)
- BUS_ADD_CHILD(parent, 0, DS133X_DEVNAME, -1);
-}
-
-static int
-ds133x_probe(device_t dev)
-{
- struct ds133x_softc *sc;
- int error;
-
- sc = device_get_softc(dev);
-
- if ((error = ds133x_detect(dev, &sc->sc_type)))
- return (error);
-
- switch (sc->sc_type) {
- case DS1337:
- device_set_desc(dev, "Dallas Semiconductor DS1337 RTC");
- break;
- case DS1338:
- device_set_desc(dev, "Dallas Semiconductor DS1338 RTC");
- break;
- case DS1339:
- device_set_desc(dev, "Dallas Semiconductor DS1339 RTC");
- break;
- default:
- break;
- }
-
- return (0);
-}
-
-static int
-ds133x_attach(device_t dev)
-{
- struct ds133x_softc *sc = device_get_softc(dev);
-
- sc->sc_dev = dev;
-
- if (sc->sc_type == DS1338)
- ds133x_init(dev, DS1338_REG_CLOCK_HALT, DS1338_CLOCK_HALT,
- DS1338_REG_CONTROL, DS1338_OSC_STOP);
- else
- ds133x_init(dev, DS1339_REG_CONTROL, DS1339_ENABLE_OSC,
- DS1339_REG_STATUS, DS1339_OSC_STOP);
-
- clock_register(dev, 1000000);
-
- return (0);
-}
-
-static uint8_t
-ds133x_get_hours(uint8_t val)
-{
- uint8_t ret;
-
- if (!(val & DS133X_24H_FLAG))
- ret = FROMBCD(val & 0x3f);
- else if (!(val & DS133X_PM_FLAG))
- ret = FROMBCD(val & 0x1f);
- else
- ret = FROMBCD(val & 0x1f) + 12;
-
- return (ret);
-}
-
-static int
-ds133x_gettime(device_t dev, struct timespec *ts)
-{
- struct ds133x_softc *sc = device_get_softc(dev);
- struct clocktime ct;
- uint8_t date[7];
- int error;
-
- error = ds133x_read(dev, DS133X_DATE_REG, date, 7);
- if (error == 0) {
- ct.nsec = 0;
- ct.sec = FROMBCD(date[0] & 0x7f);
- ct.min = FROMBCD(date[1] & 0x7f);
- ct.hour = ds133x_get_hours(date[2]);
- ct.dow = FROMBCD(date[3] & 0x07) - 1;
- ct.day = FROMBCD(date[4] & 0x3f);
- ct.mon = FROMBCD(date[5] & 0x1f);
-
- if (sc->sc_type == DS1338)
- ct.year = 2000 + FROMBCD(date[6]);
- else
- ct.year = 1900 + FROMBCD(date[6]) +
- ((date[5] & DS133X_CENT_FLAG) >> DS133X_CENT_SHIFT) * 100;
-
- error = clock_ct_to_ts(&ct, ts);
- }
-
- return (error);
-}
-
-static int
-ds133x_settime(device_t dev, struct timespec *ts)
-{
- struct ds133x_softc *sc = device_get_softc(dev);
- struct clocktime ct;
- uint8_t date[7];
-
- clock_ts_to_ct(ts, &ct);
-
- date[0] = TOBCD(ct.nsec >= HALFSEC ? ct.sec + 1 : ct.sec) & 0x7f;
- date[1] = TOBCD(ct.min) & 0x7f;
- date[2] = TOBCD(ct.hour) & 0x3f; /* We use 24-hours mode. */
- date[3] = TOBCD(ct.dow + 1) & 0x07;
- date[4] = TOBCD(ct.day) & 0x3f;
- date[5] = TOBCD(ct.mon) & 0x1f;
- if (sc->sc_type == DS1338)
- date[6] = TOBCD(ct.year - 2000);
- else if (ct.year >= 2000) {
- date[5] |= DS133X_CENT_FLAG;
- date[6] = TOBCD(ct.year - 2000);
- } else
- date[6] = TOBCD(ct.year - 1900);
-
- return (ds133x_write(dev, DS133X_DATE_REG, date, 7));
-}
-
-static device_method_t ds133x_methods[] = {
- DEVMETHOD(device_identify, ds133x_identify),
- DEVMETHOD(device_probe, ds133x_probe),
- DEVMETHOD(device_attach, ds133x_attach),
-
- DEVMETHOD(clock_gettime, ds133x_gettime),
- DEVMETHOD(clock_settime, ds133x_settime),
-
- DEVMETHOD_END
-};
-
-static driver_t ds133x_driver = {
- DS133X_DEVNAME,
- ds133x_methods,
- sizeof(struct ds133x_softc),
-};
-
-static devclass_t ds133x_devclass;
-
-DRIVER_MODULE(ds133x, iicbus, ds133x_driver, ds133x_devclass, 0, 0);
-MODULE_VERSION(ds133x, 1);
-MODULE_DEPEND(ds133x, iicbus, 1, 1, 1);
diff --git a/sys/dev/iicbus/ds1374.c b/sys/dev/iicbus/ds1374.c
deleted file mode 100644
index 917e1b4..0000000
--- a/sys/dev/iicbus/ds1374.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*-
- * Copyright (c) 2003-2012 Broadcom Corporation
- * All Rights Reserved
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY BROADCOM ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-#include <sys/bus.h>
-#include <sys/clock.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/rman.h>
-
-#include <dev/iicbus/iiconf.h>
-#include <dev/iicbus/iicbus.h>
-
-#include "iicbus_if.h"
-#include "clock_if.h"
-
-#define DS1374_RTC_COUNTER 0 /* counter (bytes 0-3) */
-
-struct ds1374_softc {
- uint32_t sc_addr;
- device_t sc_dev;
-};
-
-static int
-ds1374_probe(device_t dev)
-{
- device_set_desc(dev, "DS1374 RTC");
- return (0);
-}
-
-static int
-ds1374_attach(device_t dev)
-{
- struct ds1374_softc *sc = device_get_softc(dev);
-
- if(sc==NULL) {
- printf("ds1374_attach device_get_softc failed\n");
- return (0);
- }
- sc->sc_dev = dev;
- sc->sc_addr = iicbus_get_addr(dev);
-
- clock_register(dev, 1000);
- return (0);
-}
-
-static int
-ds1374_settime(device_t dev, struct timespec *ts)
-{
- /* NB: register pointer precedes actual data */
- uint8_t data[5] = { DS1374_RTC_COUNTER };
- struct ds1374_softc *sc = device_get_softc(dev);
- struct iic_msg msgs[1] = {
- { sc->sc_addr, IIC_M_WR, 5, data },
- };
-
- data[1] = (ts->tv_sec >> 0) & 0xff;
- data[2] = (ts->tv_sec >> 8) & 0xff;
- data[3] = (ts->tv_sec >> 16) & 0xff;
- data[4] = (ts->tv_sec >> 24) & 0xff;
-
- return iicbus_transfer(dev, msgs, 1);
-}
-
-static int
-ds1374_gettime(device_t dev, struct timespec *ts)
-{
- struct ds1374_softc *sc = device_get_softc(dev);
- uint8_t addr[1] = { DS1374_RTC_COUNTER };
- uint8_t secs[4];
- struct iic_msg msgs[2] = {
- { sc->sc_addr, IIC_M_WR, 1, addr },
- { sc->sc_addr, IIC_M_RD, 4, secs },
- };
- int error;
-
- error = iicbus_transfer(dev, msgs, 2);
- if (error == 0) {
- /* counter has seconds since epoch */
- ts->tv_sec = (secs[3] << 24) | (secs[2] << 16)
- | (secs[1] << 8) | (secs[0] << 0);
- ts->tv_nsec = 0;
- }
- return error;
-}
-
-static device_method_t ds1374_methods[] = {
- DEVMETHOD(device_probe, ds1374_probe),
- DEVMETHOD(device_attach, ds1374_attach),
-
- DEVMETHOD(clock_gettime, ds1374_gettime),
- DEVMETHOD(clock_settime, ds1374_settime),
-
- DEVMETHOD_END
-};
-
-static driver_t ds1374_driver = {
- "ds1374_rtc",
- ds1374_methods,
- sizeof(struct ds1374_softc),
-};
-static devclass_t ds1374_devclass;
-
-DRIVER_MODULE(ds1374, iicbus, ds1374_driver, ds1374_devclass, 0, 0);
-MODULE_VERSION(ds1374, 1);
-MODULE_DEPEND(ds1374, iicbus, 1, 1, 1);
diff --git a/sys/dev/iicbus/ds13rtc.c b/sys/dev/iicbus/ds13rtc.c
new file mode 100644
index 0000000..7a125c0
--- /dev/null
+++ b/sys/dev/iicbus/ds13rtc.c
@@ -0,0 +1,629 @@
+/*-
+ * Copyright (c) 2017 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for Dallas/Maxim DS13xx real-time clock/calendar chips:
+ *
+ * - DS1307 = Original/basic rtc + 56 bytes ram; 5v only.
+ * - DS1308 = Updated 1307, available in 1.8v-5v variations.
+ * - DS1337 = Like 1308, integrated xtal, 32khz output on at powerup.
+ * - DS1338 = Like 1308, integrated xtal.
+ * - DS1339 = Like 1337, integrated xtal, integrated trickle charger.
+ * - DS1340 = Like 1338, ST M41T00 compatible.
+ * - DS1341 = Like 1338, can slave-sync osc to external clock signal.
+ * - DS1342 = Like 1341 but requires different xtal.
+ * - DS1371 = 32-bit binary counter, watchdog timer.
+ * - DS1372 = 32-bit binary counter, 64-bit unique id in rom.
+ * - DS1374 = 32-bit binary counter, watchdog timer, trickle charger.
+ * - DS1375 = Like 1308 but only 16 bytes ram.
+ * - DS1388 = Rtc, watchdog timer, 512 bytes eeprom (not sram).
+ *
+ * This driver supports only basic timekeeping functions. It provides no access
+ * to or control over any other functionality provided by the chips.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/module.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+#ifdef FDT
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include "clock_if.h"
+#include "iicbus_if.h"
+
+/*
+ * I2C address 1101 000x
+ */
+#define DS13xx_ADDR 0xd0
+
+/*
+ * Registers, bits within them, and masks for the various chip types.
+ */
+
+#define DS13xx_R_NONE 0xff /* Placeholder */
+
+#define DS130x_R_CONTROL 0x07
+#define DS133x_R_CONTROL 0x0e
+#define DS1340_R_CONTROL 0x07
+#define DS1341_R_CONTROL 0x0e
+#define DS1371_R_CONTROL 0x07
+#define DS1372_R_CONTROL 0x07
+#define DS1374_R_CONTROL 0x07
+#define DS1375_R_CONTROL 0x0e
+#define DS1388_R_CONTROL 0x0c
+
+#define DS13xx_R_SECOND 0x00
+#define DS1388_R_SECOND 0x01
+
+#define DS130x_R_STATUS DS13xx_R_NONE
+#define DS133x_R_STATUS 0x0f
+#define DS1340_R_STATUS 0x09
+#define DS137x_R_STATUS 0x08
+#define DS1388_R_STATUS 0x0b
+
+#define DS13xx_B_STATUS_OSF 0x80 /* OSF is 1<<7 in status and sec regs */
+#define DS13xx_B_HOUR_AMPM 0x40 /* AMPM mode is bit 1<<6 */
+#define DS13xx_B_HOUR_PM 0x20 /* PM hours indicated by 1<<5 */
+#define DS13xx_B_MONTH_CENTURY 0x80 /* 21st century indicated by 1<<7 */
+
+#define DS13xx_M_SECOND 0x7f /* Masks for all BCD time regs... */
+#define DS13xx_M_MINUTE 0x7f
+#define DS13xx_M_12HOUR 0x1f
+#define DS13xx_M_24HOUR 0x3f
+#define DS13xx_M_DAY 0x3f
+#define DS13xx_M_MONTH 0x1f
+#define DS13xx_M_YEAR 0xff
+
+/*
+ * The chip types we support.
+ */
+enum {
+ TYPE_NONE,
+ TYPE_DS1307,
+ TYPE_DS1308,
+ TYPE_DS1337,
+ TYPE_DS1338,
+ TYPE_DS1339,
+ TYPE_DS1340,
+ TYPE_DS1341,
+ TYPE_DS1342,
+ TYPE_DS1371,
+ TYPE_DS1372,
+ TYPE_DS1374,
+ TYPE_DS1375,
+ TYPE_DS1388,
+
+ TYPE_COUNT
+};
+static const char *desc_strings[] = {
+ "",
+ "Dallas/Maxim DS1307 RTC",
+ "Dallas/Maxim DS1308 RTC",
+ "Dallas/Maxim DS1337 RTC",
+ "Dallas/Maxim DS1338 RTC",
+ "Dallas/Maxim DS1339 RTC",
+ "Dallas/Maxim DS1340 RTC",
+ "Dallas/Maxim DS1341 RTC",
+ "Dallas/Maxim DS1342 RTC",
+ "Dallas/Maxim DS1371 RTC",
+ "Dallas/Maxim DS1372 RTC",
+ "Dallas/Maxim DS1374 RTC",
+ "Dallas/Maxim DS1375 RTC",
+ "Dallas/Maxim DS1388 RTC",
+};
+CTASSERT(nitems(desc_strings) == TYPE_COUNT);
+
+/*
+ * The time registers in the order they are laid out in hardware.
+ */
+struct time_regs {
+ uint8_t sec, min, hour, wday, day, month, year;
+};
+
+struct ds13rtc_softc {
+ device_t dev;
+ device_t busdev;
+ u_int flags; /* SC_F_* flags */
+ u_int chiptype; /* Type of DS13xx chip */
+ uint8_t secaddr; /* Address of seconds register */
+ uint8_t osfaddr; /* Address of register with OSF */
+};
+
+#define SC_F_BINARY (1u << 0) /* Time is 32-bit binary counter */
+#define SC_F_AMPM (1u << 1) /* Use PM flag in hours reg */
+#define SC_F_CENTURY (1u << 2) /* Use century bit */
+
+/*
+ * We use the compat_data table to look up hint strings in the non-FDT case, so
+ * define the struct locally when we don't get it from ofw_bus_subr.h.
+ */
+#ifdef FDT
+typedef struct ofw_compat_data ds13_compat_data;
+#else
+typedef struct {
+ const char *ocd_str;
+ uintptr_t ocd_data;
+} ds13_compat_data;
+#endif
+
+static ds13_compat_data compat_data[] = {
+ {"dallas,ds1307", TYPE_DS1307},
+ {"dallas,ds1308", TYPE_DS1308},
+ {"dallas,ds1337", TYPE_DS1337},
+ {"dallas,ds1338", TYPE_DS1338},
+ {"dallas,ds1339", TYPE_DS1339},
+ {"dallas,ds1340", TYPE_DS1340},
+ {"dallas,ds1341", TYPE_DS1341},
+ {"dallas,ds1342", TYPE_DS1342},
+ {"dallas,ds1371", TYPE_DS1371},
+ {"dallas,ds1372", TYPE_DS1372},
+ {"dallas,ds1374", TYPE_DS1374},
+ {"dallas,ds1375", TYPE_DS1375},
+ {"dallas,ds1388", TYPE_DS1388},
+
+ {NULL, TYPE_NONE},
+};
+
+static int
+read_reg(struct ds13rtc_softc *sc, uint8_t reg, uint8_t *val)
+{
+
+ return (iicdev_readfrom(sc->dev, reg, val, sizeof(*val), IIC_WAIT));
+}
+
+static int
+write_reg(struct ds13rtc_softc *sc, uint8_t reg, uint8_t val)
+{
+
+ return (iicdev_writeto(sc->dev, reg, &val, sizeof(val), IIC_WAIT));
+}
+
+static int
+read_timeregs(struct ds13rtc_softc *sc, struct time_regs *tregs)
+{
+ int err;
+
+ if ((err = iicdev_readfrom(sc->dev, sc->secaddr, tregs,
+ sizeof(*tregs), IIC_WAIT)) != 0)
+ return (err);
+
+ return (err);
+}
+
+static int
+write_timeregs(struct ds13rtc_softc *sc, struct time_regs *tregs)
+{
+
+ return (iicdev_writeto(sc->dev, sc->secaddr, tregs,
+ sizeof(*tregs), IIC_WAIT));
+}
+
+static int
+read_timeword(struct ds13rtc_softc *sc, time_t *secs)
+{
+ int err;
+ uint8_t buf[4];
+
+ if ((err = iicdev_readfrom(sc->dev, sc->secaddr, buf, sizeof(buf),
+ IIC_WAIT)) == 0)
+ *secs = le32dec(buf);
+
+ return (err);
+}
+
+static int
+write_timeword(struct ds13rtc_softc *sc, time_t secs)
+{
+ uint8_t buf[4];
+
+ le32enc(buf, (uint32_t)secs);
+ return (iicdev_writeto(sc->dev, sc->secaddr, buf, sizeof(buf),
+ IIC_WAIT));
+}
+
+static void
+ds13rtc_start(void *arg)
+{
+ struct ds13rtc_softc *sc;
+ uint8_t ctlreg, statreg;
+
+ sc = arg;
+
+ /*
+ * Every chip in this family can be usefully initialized by writing 0 to
+ * the control register, except DS1375 which has an external oscillator
+ * controlled by values in the ctlreg that we know nothing about, so
+ * we'd best leave them alone. For all other chips, writing 0 enables
+ * the oscillator, disables signals/outputs in battery-backed mode
+ * (saves power) and disables features like watchdog timers and alarms.
+ */
+ switch (sc->chiptype) {
+ case TYPE_DS1307:
+ case TYPE_DS1308:
+ case TYPE_DS1338:
+ case TYPE_DS1340:
+ case TYPE_DS1371:
+ case TYPE_DS1372:
+ case TYPE_DS1374:
+ ctlreg = DS130x_R_CONTROL;
+ break;
+ case TYPE_DS1337:
+ case TYPE_DS1339:
+ ctlreg = DS133x_R_CONTROL;
+ break;
+ case TYPE_DS1341:
+ case TYPE_DS1342:
+ ctlreg = DS1341_R_CONTROL;
+ break;
+ case TYPE_DS1375:
+ ctlreg = DS13xx_R_NONE;
+ break;
+ case TYPE_DS1388:
+ ctlreg = DS1388_R_CONTROL;
+ break;
+ default:
+ device_printf(sc->dev, "missing init code for this chiptype\n");
+ return;
+ }
+ if (ctlreg != DS13xx_R_NONE)
+ write_reg(sc, ctlreg, 0);
+
+ /*
+ * Common init. Read the OSF/CH status bit and report stopped clocks to
+ * the user. The status bit will be cleared the first time we write
+ * valid time to the chip (and must not be cleared before that).
+ */
+ if (read_reg(sc, sc->osfaddr, &statreg) != 0) {
+ device_printf(sc->dev, "cannot read RTC clock status bit\n");
+ return;
+ }
+ if (statreg & DS13xx_B_STATUS_OSF) {
+ device_printf(sc->dev,
+ "WARNING: RTC battery failed; time is invalid\n");
+ }
+
+ /*
+ * Figure out whether the chip is configured for AM/PM mode. On all
+ * chips that do AM/PM mode, the flag bit is in the hours register,
+ * which is secaddr+2.
+ */
+ if ((sc->chiptype != TYPE_DS1340) && !(sc->flags & SC_F_BINARY)) {
+ if (read_reg(sc, sc->secaddr + 2, &statreg) != 0) {
+ device_printf(sc->dev,
+ "cannot read RTC clock AM/PM bit\n");
+ return;
+ }
+ if (statreg & DS13xx_B_HOUR_AMPM)
+ sc->flags |= SC_F_AMPM;
+ }
+
+ /*
+ * Everything looks good if we make it to here; register as an RTC.
+ * Schedule RTC updates to happen just after top-of-second.
+ */
+ clock_register_flags(sc->dev, 1000000, CLOCKF_SETTIME_NO_ADJ);
+ clock_schedule(sc->dev, 1);
+}
+
+static int
+ds13rtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ struct time_regs tregs;
+ struct ds13rtc_softc *sc;
+ int err;
+ uint8_t statreg, hourmask;
+
+ sc = device_get_softc(dev);
+
+ /* Read the OSF/CH bit; if the clock stopped we can't provide time. */
+ if ((err = read_reg(sc, sc->osfaddr, &statreg)) != 0) {
+ return (err);
+ }
+ if (statreg & DS13xx_B_STATUS_OSF)
+ return (EINVAL); /* hardware is good, time is not. */
+
+ /* If the chip counts time in binary, we just read and return it. */
+ if (sc->flags & SC_F_BINARY) {
+ if ((err = read_timeword(sc, &ts->tv_sec)) != 0)
+ return (err);
+ ts->tv_nsec = 0;
+ }
+
+ /*
+ * Chip counts in BCD, read and decode it...
+ */
+ if ((err = read_timeregs(sc, &tregs)) != 0) {
+ device_printf(dev, "cannot read RTC time\n");
+ return (err);
+ }
+
+ if (sc->flags & SC_F_AMPM)
+ hourmask = DS13xx_M_12HOUR;
+ else
+ hourmask = DS13xx_M_24HOUR;
+
+ ct.sec = FROMBCD(tregs.sec & DS13xx_M_SECOND);
+ ct.min = FROMBCD(tregs.min & DS13xx_M_MINUTE);
+ ct.hour = FROMBCD(tregs.hour & hourmask);
+ ct.day = FROMBCD(tregs.day & DS13xx_M_DAY);
+ ct.mon = FROMBCD(tregs.month & DS13xx_M_MONTH);
+ ct.year = FROMBCD(tregs.year & DS13xx_M_YEAR);
+ ct.nsec = 0;
+
+ if (sc->flags & SC_F_AMPM) {
+ if (ct.hour == 12)
+ ct.hour = 0;
+ if (tregs.hour & DS13xx_B_HOUR_PM)
+ ct.hour += 12;
+ }
+
+ /*
+ * If this chip has a century bit, honor it. Otherwise let
+ * clock_ct_to_ts() infer the century from the 2-digit year.
+ */
+ if (sc->flags & SC_F_CENTURY)
+ ct.year += (tregs.month & DS13xx_B_MONTH_CENTURY) ? 2000 : 1900;
+
+ err = clock_ct_to_ts(&ct, ts);
+
+ return (err);
+}
+
+static int
+ds13rtc_settime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ struct time_regs tregs;
+ struct ds13rtc_softc *sc;
+ int err;
+ uint8_t cflag, statreg, pmflag;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * We request a timespec with no resolution-adjustment. That also
+ * disables utc adjustment, so apply that ourselves.
+ */
+ ts->tv_sec -= utc_offset();
+
+ /* If the chip counts time in binary, store tv_sec and we're done. */
+ if (sc->flags & SC_F_BINARY)
+ return (write_timeword(sc, ts->tv_sec));
+
+ clock_ts_to_ct(ts, &ct);
+
+ /* If the chip is in AMPM mode deal with the PM flag. */
+ pmflag = 0;
+ if (sc->flags & SC_F_AMPM) {
+ if (ct.hour >= 12) {
+ ct.hour -= 12;
+ pmflag = DS13xx_B_HOUR_PM;
+ }
+ if (ct.hour == 0)
+ ct.hour = 12;
+ }
+
+ /* If the chip has a century bit, set it as needed. */
+ cflag = 0;
+ if (sc->flags & SC_F_CENTURY) {
+ if (ct.year >= 2000)
+ cflag |= DS13xx_B_MONTH_CENTURY;
+ }
+
+ tregs.sec = TOBCD(ct.sec);
+ tregs.min = TOBCD(ct.min);
+ tregs.hour = TOBCD(ct.hour) | pmflag;
+ tregs.day = TOBCD(ct.day);
+ tregs.month = TOBCD(ct.mon) | cflag;
+ tregs.year = TOBCD(ct.year % 100);
+ tregs.wday = ct.dow;
+
+ /*
+ * Set the time. Reset the OSF bit if it is on and it is not part of
+ * the time registers (in which case writing time resets it).
+ */
+ if ((err = write_timeregs(sc, &tregs)) != 0)
+ goto errout;
+ if (sc->osfaddr != sc->secaddr) {
+ if ((err = read_reg(sc, sc->osfaddr, &statreg)) != 0)
+ goto errout;
+ if (statreg & DS13xx_B_STATUS_OSF) {
+ statreg &= ~DS13xx_B_STATUS_OSF;
+ err = write_reg(sc, sc->osfaddr, statreg);
+ }
+ }
+
+errout:
+
+ if (err != 0)
+ device_printf(dev, "cannot update RTC time\n");
+
+ return (err);
+}
+
+static int
+ds13rtc_get_chiptype(device_t dev)
+{
+#ifdef FDT
+
+ return (ofw_bus_search_compatible(dev, compat_data)->ocd_data);
+#else
+ ds13_compat_data *cdata;
+ const char *htype;
+
+ /*
+ * We can only attach if provided a chiptype hint string.
+ */
+ if (resource_string_value(device_get_name(dev),
+ device_get_unit(dev), "compatible", &htype) != 0)
+ return (TYPE_NONE);
+
+ /*
+ * Loop through the ofw compat data comparing the hinted chip type to
+ * the compat strings.
+ */
+ for (cdata = compat_data; cdata->ocd_str != NULL; ++cdata) {
+ if (strcmp(htype, cdata->ocd_str) == 0)
+ break;
+ }
+ return (cdata->ocd_data);
+#endif
+}
+
+static int
+ds13rtc_probe(device_t dev)
+{
+ int chiptype, goodrv;
+
+#ifdef FDT
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ goodrv = BUS_PROBE_GENERIC;
+#else
+ goodrv = BUS_PROBE_NOWILDCARD;
+#endif
+
+ chiptype = ds13rtc_get_chiptype(dev);
+ if (chiptype == TYPE_NONE)
+ return (ENXIO);
+
+ device_set_desc(dev, desc_strings[chiptype]);
+ return (goodrv);
+}
+
+static int
+ds13rtc_attach(device_t dev)
+{
+ struct ds13rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->busdev = device_get_parent(dev);
+
+ /*
+ * We need to know what kind of chip we're driving.
+ */
+ if ((sc->chiptype = ds13rtc_get_chiptype(dev)) == TYPE_NONE) {
+ device_printf(dev, "impossible: cannot determine chip type\n");
+ return (ENXIO);
+ }
+
+ /* The seconds register is in the same place on all except DS1388. */
+ if (sc->chiptype == TYPE_DS1388)
+ sc->secaddr = DS1388_R_SECOND;
+ else
+ sc->secaddr = DS13xx_R_SECOND;
+
+ /*
+ * The OSF/CH (osc failed/clock-halted) bit appears in different
+ * registers for different chip types. The DS1375 has no OSF indicator
+ * because it has no internal oscillator; we just point to an always-
+ * zero bit in the status register for that chip.
+ */
+ switch (sc->chiptype) {
+ case TYPE_DS1307:
+ case TYPE_DS1308:
+ case TYPE_DS1338:
+ sc->osfaddr = DS13xx_R_SECOND;
+ break;
+ case TYPE_DS1337:
+ case TYPE_DS1339:
+ case TYPE_DS1341:
+ case TYPE_DS1342:
+ case TYPE_DS1375:
+ sc->osfaddr = DS133x_R_STATUS;
+ sc->flags |= SC_F_CENTURY;
+ break;
+ case TYPE_DS1340:
+ sc->osfaddr = DS1340_R_STATUS;
+ break;
+ case TYPE_DS1371:
+ case TYPE_DS1372:
+ case TYPE_DS1374:
+ sc->osfaddr = DS137x_R_STATUS;
+ sc->flags |= SC_F_BINARY;
+ break;
+ case TYPE_DS1388:
+ sc->osfaddr = DS1388_R_STATUS;
+ break;
+ }
+
+ /*
+ * We have to wait until interrupts are enabled. Sometimes I2C read
+ * and write only works when the interrupts are available.
+ */
+ config_intrhook_oneshot(ds13rtc_start, sc);
+
+ return (0);
+}
+
+static int
+ds13rtc_detach(device_t dev)
+{
+
+ clock_unregister(dev);
+ return (0);
+}
+
+static device_method_t ds13rtc_methods[] = {
+ DEVMETHOD(device_probe, ds13rtc_probe),
+ DEVMETHOD(device_attach, ds13rtc_attach),
+ DEVMETHOD(device_detach, ds13rtc_detach),
+
+ DEVMETHOD(clock_gettime, ds13rtc_gettime),
+ DEVMETHOD(clock_settime, ds13rtc_settime),
+
+ DEVMETHOD_END
+};
+
+static driver_t ds13rtc_driver = {
+ "ds13rtc",
+ ds13rtc_methods,
+ sizeof(struct ds13rtc_softc),
+};
+
+static devclass_t ds13rtc_devclass;
+
+DRIVER_MODULE(ds13rtc, iicbus, ds13rtc_driver, ds13rtc_devclass, NULL, NULL);
+MODULE_VERSION(ds13rtc, 1);
+MODULE_DEPEND(ds13rtc, iicbus, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
diff --git a/sys/dev/iicbus/ds3231.c b/sys/dev/iicbus/ds3231.c
index 690c92b..eb9a5ee 100644
--- a/sys/dev/iicbus/ds3231.c
+++ b/sys/dev/iicbus/ds3231.c
@@ -62,29 +62,23 @@ struct ds3231_softc {
uint16_t sc_addr; /* DS3231 slave address. */
uint8_t sc_ctrl;
uint8_t sc_status;
+ bool sc_use_ampm;
};
static void ds3231_start(void *);
static int
-ds3231_read(device_t dev, uint16_t addr, uint8_t reg, uint8_t *data, size_t len)
+ds3231_read1(device_t dev, uint8_t reg, uint8_t *data)
{
- struct iic_msg msg[2] = {
- { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
- { addr, IIC_M_RD, len, data },
- };
- return (iicbus_transfer(dev, msg, nitems(msg)));
+ return (iicdev_readfrom(dev, reg, data, 1, IIC_INTRWAIT));
}
static int
-ds3231_write(device_t dev, uint16_t addr, uint8_t *data, size_t len)
+ds3231_write1(device_t dev, uint8_t reg, uint8_t data)
{
- struct iic_msg msg[1] = {
- { addr, IIC_M_WR, len, data },
- };
- return (iicbus_transfer(dev, msg, nitems(msg)));
+ return (iicdev_writeto(dev, reg, &data, 1, IIC_INTRWAIT));
}
static int
@@ -92,14 +86,11 @@ ds3231_ctrl_read(struct ds3231_softc *sc)
{
int error;
- sc->sc_ctrl = 0;
- error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_CONTROL,
- &sc->sc_ctrl, sizeof(sc->sc_ctrl));
+ error = ds3231_read1(sc->sc_dev, DS3231_CONTROL, &sc->sc_ctrl);
if (error) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
}
-
return (0);
}
@@ -107,12 +98,11 @@ static int
ds3231_ctrl_write(struct ds3231_softc *sc)
{
int error;
- uint8_t data[2];
+ uint8_t data;
- data[0] = DS3231_CONTROL;
/* Always enable the oscillator. Always disable both alarms. */
- data[1] = sc->sc_ctrl & ~DS3231_CTRL_MASK;
- error = ds3231_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
+ data = sc->sc_ctrl & ~DS3231_CTRL_MASK;
+ error = ds3231_write1(sc->sc_dev, DS3231_CONTROL, data);
if (error != 0)
device_printf(sc->sc_dev, "cannot write to RTC.\n");
@@ -124,9 +114,7 @@ ds3231_status_read(struct ds3231_softc *sc)
{
int error;
- sc->sc_status = 0;
- error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_STATUS,
- &sc->sc_status, sizeof(sc->sc_status));
+ error = ds3231_read1(sc->sc_dev, DS3231_STATUS, &sc->sc_status);
if (error) {
device_printf(sc->sc_dev, "cannot read from RTC.\n");
return (error);
@@ -139,37 +127,14 @@ static int
ds3231_status_write(struct ds3231_softc *sc, int clear_a1, int clear_a2)
{
int error;
- uint8_t data[2];
+ uint8_t data;
- data[0] = DS3231_STATUS;
- data[1] = sc->sc_status;
+ data = sc->sc_status;
if (clear_a1 == 0)
- data[1] |= DS3231_STATUS_A1F;
+ data |= DS3231_STATUS_A1F;
if (clear_a2 == 0)
- data[1] |= DS3231_STATUS_A2F;
- error = ds3231_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
- if (error != 0)
- device_printf(sc->sc_dev, "cannot write to RTC.\n");
-
- return (error);
-}
-
-static int
-ds3231_set_24hrs_mode(struct ds3231_softc *sc)
-{
- int error;
- uint8_t data[2], hour;
-
- hour = 0;
- error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_HOUR,
- &hour, sizeof(hour));
- if (error) {
- device_printf(sc->sc_dev, "cannot read from RTC.\n");
- return (error);
- }
- data[0] = DS3231_HOUR;
- data[1] = hour & ~DS3231_C_MASK;
- error = ds3231_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
+ data |= DS3231_STATUS_A2F;
+ error = ds3231_write1(sc->sc_dev, DS3231_STATUS, data);
if (error != 0)
device_printf(sc->sc_dev, "cannot write to RTC.\n");
@@ -183,8 +148,8 @@ ds3231_temp_read(struct ds3231_softc *sc, int *temp)
uint8_t buf8[2];
uint16_t buf;
- error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_TEMP,
- buf8, sizeof(buf8));
+ error = iicdev_readfrom(sc->sc_dev, DS3231_TEMP, buf8, sizeof(buf8),
+ IIC_INTRWAIT);
if (error != 0)
return (error);
buf = (buf8[0] << 8) | (buf8[1] & 0xff);
@@ -426,6 +391,14 @@ ds3231_attach(device_t dev)
return (0);
}
+static int
+ds3231_detach(device_t dev)
+{
+
+ clock_unregister(dev);
+ return (0);
+}
+
static void
ds3231_start(void *xdev)
{
@@ -446,20 +419,20 @@ ds3231_start(void *xdev)
return;
if (ds3231_status_read(sc) != 0)
return;
- /* Clear the OSF bit and ack any pending alarm interrupt. */
+ /*
+ * Warn if the clock stopped, but don't restart it until the first
+ * clock_settime() call.
+ */
if (sc->sc_status & DS3231_STATUS_OSF) {
device_printf(sc->sc_dev,
- "oscillator has stopped, check the battery.\n");
- sc->sc_status &= ~DS3231_STATUS_OSF;
+ "WARNING: RTC clock stopped, check the battery.\n");
}
+ /* Ack any pending alarm interrupt. */
if (ds3231_status_write(sc, 1, 1) != 0)
return;
/* Always enable the oscillator. */
if (ds3231_ctrl_write(sc) != 0)
return;
- /* Set the 24 hours mode. */
- if (ds3231_set_24hrs_mode(sc) != 0)
- return;
/* Temperature. */
SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temperature",
@@ -485,8 +458,13 @@ ds3231_start(void *xdev)
CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
ds3231_en32khz_sysctl, "IU", "DS3231 enable the 32kHz output");
- /* 1 second resolution. */
- clock_register(dev, 1000000);
+ /*
+ * Register as a clock with 1 second resolution. Schedule the
+ * clock_settime() method to be called just after top-of-second;
+ * resetting the time resets top-of-second in the hardware.
+ */
+ clock_register_flags(dev, 1000000, CLOCKF_SETTIME_NO_ADJ);
+ clock_schedule(dev, 1);
}
static int
@@ -495,24 +473,45 @@ ds3231_gettime(device_t dev, struct timespec *ts)
int c, error;
struct clocktime ct;
struct ds3231_softc *sc;
- uint8_t data[7];
+ uint8_t data[7], hourmask;
sc = device_get_softc(dev);
- memset(data, 0, sizeof(data));
- error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_SECS,
- data, sizeof(data));
+
+ /* If the clock halted, we don't have good data. */
+ if ((error = ds3231_status_read(sc)) != 0) {
+ device_printf(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(dev, "cannot read from RTC.\n");
return (error);
}
+
+ /* If chip is in AM/PM mode remember that. */
+ if (data[DS3231_HOUR] & DS3231_HOUR_USE_AMPM) {
+ sc->sc_use_ampm = true;
+ hourmask = DS3231_HOUR_MASK_12HR;
+ } else
+ hourmask = DS3231_HOUR_MASK_24HR;
+
ct.nsec = 0;
- ct.sec = FROMBCD(data[DS3231_SECS] & DS3231_SECS_MASK);
- ct.min = FROMBCD(data[DS3231_MINS] & DS3231_MINS_MASK);
- ct.hour = FROMBCD(data[DS3231_HOUR] & DS3231_HOUR_MASK);
- ct.day = FROMBCD(data[DS3231_DATE] & DS3231_DATE_MASK);
- ct.dow = data[DS3231_WEEKDAY] & DS3231_WEEKDAY_MASK;
- ct.mon = FROMBCD(data[DS3231_MONTH] & DS3231_MONTH_MASK);
- ct.year = FROMBCD(data[DS3231_YEAR] & DS3231_YEAR_MASK);
+ ct.sec = FROMBCD(data[DS3231_SECS] & DS3231_SECS_MASK);
+ ct.min = FROMBCD(data[DS3231_MINS] & DS3231_MINS_MASK);
+ ct.hour = FROMBCD(data[DS3231_HOUR] & hourmask);
+ ct.day = FROMBCD(data[DS3231_DATE] & DS3231_DATE_MASK);
+ ct.mon = FROMBCD(data[DS3231_MONTH] & DS3231_MONTH_MASK);
+ ct.year = FROMBCD(data[DS3231_YEAR] & DS3231_YEAR_MASK);
+
+ /*
+ * 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;
@@ -524,6 +523,14 @@ ds3231_gettime(device_t dev, struct timespec *ts)
if (ct.year < POSIX_BASE_YEAR)
ct.year += 100; /* assume [1970, 2069] */
+ /* If running in AM/PM mode, deal with it. */
+ if (sc->sc_use_ampm) {
+ if (ct.hour == 12)
+ ct.hour = 0;
+ if (data[DS3231_HOUR] & DS3231_HOUR_IS_PM)
+ ct.hour += 12;
+ }
+
return (clock_ct_to_ts(&ct, ts));
}
@@ -533,29 +540,63 @@ ds3231_settime(device_t dev, struct timespec *ts)
int error;
struct clocktime ct;
struct ds3231_softc *sc;
- uint8_t data[8];
+ uint8_t data[7];
+ uint8_t pmflags;
sc = device_get_softc(dev);
- /* Accuracy is only one second. */
- if (ts->tv_nsec >= 500000000)
- ts->tv_sec++;
- ts->tv_nsec = 0;
+
+ /*
+ * 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_ct(ts, &ct);
- memset(data, 0, sizeof(data));
- data[0] = DS3231_SECS;
- data[DS3231_SECS + 1] = TOBCD(ct.sec);
- data[DS3231_MINS + 1] = TOBCD(ct.min);
- data[DS3231_HOUR + 1] = TOBCD(ct.hour);
- data[DS3231_DATE + 1] = TOBCD(ct.day);
- data[DS3231_WEEKDAY + 1] = ct.dow;
- data[DS3231_MONTH + 1] = TOBCD(ct.mon);
- data[DS3231_YEAR + 1] = TOBCD(ct.year % 100);
+
+ /* If the chip is in AM/PM mode, adjust hour and set flags as needed. */
+ if (sc->sc_use_ampm) {
+ pmflags = DS3231_HOUR_USE_AMPM;
+ if (ct.hour >= 12) {
+ ct.hour -= 12;
+ pmflags |= DS3231_HOUR_IS_PM;
+ }
+ if (ct.hour == 0)
+ ct.hour = 12;
+ } else
+ pmflags = 0;
+
+ data[DS3231_SECS] = TOBCD(ct.sec);
+ data[DS3231_MINS] = TOBCD(ct.min);
+ data[DS3231_HOUR] = TOBCD(ct.hour) | pmflags;
+ data[DS3231_DATE] = TOBCD(ct.day);
+ data[DS3231_WEEKDAY] = ct.dow;
+ data[DS3231_MONTH] = TOBCD(ct.mon);
+ data[DS3231_YEAR] = TOBCD(ct.year % 100);
if (sc->sc_last_c)
data[DS3231_MONTH] |= DS3231_C_MASK;
+
/* Write the time back to RTC. */
- error = ds3231_write(dev, sc->sc_addr, data, sizeof(data));
- if (error != 0)
+ error = iicdev_writeto(dev, DS3231_SECS, data, sizeof(data),
+ IIC_INTRWAIT);
+ if (error != 0) {
device_printf(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(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(dev, "cannot write to RTC.\n");
+ return (error);
+ }
+ }
return (error);
}
@@ -563,6 +604,7 @@ ds3231_settime(device_t dev, struct timespec *ts)
static device_method_t ds3231_methods[] = {
DEVMETHOD(device_probe, ds3231_probe),
DEVMETHOD(device_attach, ds3231_attach),
+ DEVMETHOD(device_detach, ds3231_detach),
DEVMETHOD(clock_gettime, ds3231_gettime),
DEVMETHOD(clock_settime, ds3231_settime),
diff --git a/sys/dev/iicbus/ds3231reg.h b/sys/dev/iicbus/ds3231reg.h
index 41e011c..86f087c 100644
--- a/sys/dev/iicbus/ds3231reg.h
+++ b/sys/dev/iicbus/ds3231reg.h
@@ -38,7 +38,10 @@
#define DS3231_MINS 0x01
#define DS3231_MINS_MASK 0x7f
#define DS3231_HOUR 0x02
-#define DS3231_HOUR_MASK 0x3f
+#define DS3231_HOUR_MASK_12HR 0x3f
+#define DS3231_HOUR_MASK_24HR 0x1f
+#define DS3231_HOUR_IS_PM 0x20
+#define DS3231_HOUR_USE_AMPM 0x40
#define DS3231_WEEKDAY 0x03
#define DS3231_WEEKDAY_MASK 0x07
#define DS3231_DATE 0x04
diff --git a/sys/dev/iicbus/isl12xx.c b/sys/dev/iicbus/isl12xx.c
new file mode 100644
index 0000000..f834491
--- /dev/null
+++ b/sys/dev/iicbus/isl12xx.c
@@ -0,0 +1,354 @@
+/*-
+ * Copyright (c) 2017 Ian Lepore. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for ISL12xx family i2c realtime clocks:
+ * - ISL1209 = 2B sram, tamper/event timestamp
+ * - ISL1218 = 8B sram, DS13xx pin compatible (but not software compatible)
+ * - ISL1219 = 2B sram, tamper/event timestamp
+ * - ISL1220 = 8B sram, separate Fout
+ * - ISL1221 = 2B sram, separate Fout, tamper/event timestamp
+ *
+ * This driver supports only the basic RTC functionality in all these chips.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/module.h>
+#include <sys/sx.h>
+
+#ifdef FDT
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/iicbus/iiconf.h>
+#include <dev/iicbus/iicbus.h>
+
+#include "clock_if.h"
+#include "iicbus_if.h"
+
+/*
+ * All register and bit names as found in the datasheet. When a bit name ends
+ * in 'B' that stands for "bar" and it is an active-low signal; something named
+ * "EVENB" implies 1=event-disable, 0=event-enable.
+ */
+
+#define ISL12XX_SC_REG 0x00 /* RTC Seconds */
+
+#define ISL12XX_SR_REG 0x07 /* Status */
+#define ISL12XX_SR_ARST (1u << 7) /* Auto-reset on status read */
+#define ISL12XX_SR_XTOSCB (1u << 5) /* Osc disable (use ext osc) */
+#define ISL12XX_SR_WRTC (1u << 4) /* Write RTC enable */
+#define ISL12XX_SR_EVT (1u << 3) /* Event occurred (w0c) */
+#define ISL12XX_SR_ALM (1u << 2) /* Alarm occurred (w0c) */
+#define ISL12XX_SR_BAT (1u << 1) /* Running on battery (w0c) */
+#define ISL12XX_SR_RTCF (1u << 0) /* RTC fail (power loss) */
+#define ISL12XX_SR_W0C_BITS (ISL12XX_SR_BAT | ISL12XX_SR_ALM | ISL12XX_SR_EVT)
+
+#define ISL12XX_INT_REG 0x08 /* Interrupts */
+#define ISL12XX_INT_IM (1u << 7) /* Alarm interrupt mode */
+#define ISL12XX_INT_ALME (1u << 6) /* Alarm enable */
+#define ISL12XX_INT_LPMODE (1u << 5) /* Low Power mode */
+#define ISL12XX_INT_FOBATB (1u << 4) /* Fout/IRQ disabled on bat */
+#define ISL12XX_INT_FO_SHIFT 0 /* Frequency output select */
+#define ISL12XX_INT_FO_MASK 0x0f /* shift and mask. */
+
+#define ISL12XX_EV_REG 0x09 /* Event */
+#define ISL12XX_EV_EVIENB (1u << 7) /* Disable internal pullup */
+#define ISL12XX_EV_EVBATB (1u << 6) /* Disable ev detect on bat */
+#define ISL12XX_EV_RTCHLT (1u << 5) /* Halt RTC on event */
+#define ISL12XX_EV_EVEN (1u << 4) /* Event detect enable */
+#define ISL12XX_EV_EHYS_SHIFT 2 /* Event input hysteresis */
+#define ISL12XX_EV_EHYS_MASK 0x03 /* selection; see datasheet */
+#define ISL12XX_EV_ESMP_SHIFT 0 /* Event input sample rate */
+#define ISL12XX_EV_ESMP_MASK 0x03 /* selection; see datasheet */
+
+#define ISL12XX_ATR_REG 0x0a /* Analog trim (osc adjust) */
+
+#define ISL12XX_DTR_REG 0x0b /* Digital trim (osc adjust) */
+
+#define ISL12XX_SCA_REG 0x0c /* Alarm seconds */
+
+#define ISL12XX_USR1_REG 0x12 /* User byte 1 */
+
+#define ISL12XX_USR2_REG 0x13 /* User byte 2 */
+
+#define ISL12XX_SCT_REG 0x14 /* Timestamp (event) seconds */
+
+#define ISL12XX_24HR_FLAG (1u << 7) /* Hours register 24-hr mode */
+#define ISL12XX_PM_FLAG (1u << 5) /* Hours register PM flag */
+#define ISL12xx_12HR_MASK 0x1f /* Hours mask in AM/PM mode */
+#define ISL12xx_24HR_MASK 0x3f /* Hours mask in 24-hr mode */
+
+/*
+ * A struct laid out in the same order as the time registers in the chip.
+ */
+struct time_regs {
+ uint8_t sec, min, hour, day, month, year;
+};
+
+struct isl12xx_softc {
+ device_t dev;
+ device_t busdev;
+ struct intr_config_hook
+ init_hook;
+ bool use_ampm;
+};
+
+#ifdef FDT
+static struct ofw_compat_data compat_data[] = {
+ {"isil,isl1209", 1},
+ {"isil,isl1218", 1},
+ {"isil,isl1219", 1},
+ {"isil,isl1220", 1},
+ {"isil,isl1221", 1},
+ {NULL, 0},
+};
+#endif
+
+static inline int
+isl12xx_read1(struct isl12xx_softc *sc, uint8_t reg, uint8_t *data)
+{
+
+ return (iicdev_readfrom(sc->dev, reg, data, 1, IIC_WAIT));
+}
+
+static inline int
+isl12xx_write1(struct isl12xx_softc *sc, uint8_t reg, uint8_t val)
+{
+
+ return (iicdev_writeto(sc->dev, reg, &val, 1, IIC_WAIT));
+}
+
+static void
+isl12xx_init(void *arg)
+{
+ struct isl12xx_softc *sc = arg;
+ uint8_t sreg;
+
+ config_intrhook_disestablish(&sc->init_hook);
+
+ /*
+ * Check the clock-stopped/power-fail bit, just so we can report it to
+ * the user at boot time.
+ */
+ isl12xx_read1(sc, ISL12XX_SR_REG, &sreg);
+ if (sreg & ISL12XX_SR_RTCF) {
+ device_printf(sc->dev,
+ "RTC clock stopped; check battery\n");
+ }
+
+ /*
+ * Register as a system realtime clock.
+ */
+ clock_register_flags(sc->dev, 1000000, CLOCKF_SETTIME_NO_ADJ);
+ clock_schedule(sc->dev, 1);
+}
+
+static int
+isl12xx_probe(device_t dev)
+{
+
+#ifdef FDT
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) {
+ device_set_desc(dev, "Intersil ISL12xx RTC");
+ return (BUS_PROBE_DEFAULT);
+ }
+#endif
+ return (ENXIO);
+}
+
+static int
+isl12xx_attach(device_t dev)
+{
+ struct isl12xx_softc *sc = device_get_softc(dev);
+
+ sc->dev = dev;
+ sc->busdev = device_get_parent(sc->dev);
+
+ /*
+ * Chip init must wait until interrupts are enabled. Often i2c access
+ * works only when the interrupts are available.
+ */
+ sc->init_hook.ich_func = isl12xx_init;
+ sc->init_hook.ich_arg = sc;
+ if (config_intrhook_establish(&sc->init_hook) != 0)
+ return (ENOMEM);
+
+ return (0);
+}
+
+static int
+isl12xx_detach(device_t dev)
+{
+
+ clock_unregister(dev);
+ return (0);
+}
+
+static int
+isl12xx_gettime(device_t dev, struct timespec *ts)
+{
+ struct isl12xx_softc *sc = device_get_softc(dev);
+ struct clocktime ct;
+ struct time_regs tregs;
+ int err;
+ uint8_t hourmask, sreg;
+
+ /* If power failed, we can't provide valid time. */
+ if ((err = isl12xx_read1(sc, ISL12XX_SR_REG, &sreg)) != 0)
+ return (err);
+ if (sreg & ISL12XX_SR_RTCF)
+ return (EINVAL);
+
+ /* Read the bcd time registers. */
+ if ((err = iicdev_readfrom(sc->dev, ISL12XX_SC_REG, &tregs, sizeof(tregs),
+ IIC_WAIT)) != 0)
+ return (EINVAL);
+
+ /* If chip is in AM/PM mode remember that for when we set time. */
+ if (tregs.hour & ISL12XX_24HR_FLAG) {
+ hourmask = ISL12xx_24HR_MASK;
+ } else {
+ sc->use_ampm = true;
+ hourmask = ISL12xx_12HR_MASK;
+ }
+
+ ct.nsec = 0;
+ ct.sec = FROMBCD(tregs.sec);
+ ct.min = FROMBCD(tregs.min);
+ ct.hour = FROMBCD(tregs.hour & hourmask);
+ ct.day = FROMBCD(tregs.day);
+ ct.mon = FROMBCD(tregs.month);
+ ct.year = FROMBCD(tregs.year);
+
+ if (sc->use_ampm) {
+ if (ct.hour == 12)
+ ct.hour = 0;
+ if (tregs.hour & ISL12XX_PM_FLAG)
+ ct.hour += 12;
+ }
+
+ return (clock_ct_to_ts(&ct, ts));
+}
+
+static int
+isl12xx_settime(device_t dev, struct timespec *ts)
+{
+ struct isl12xx_softc *sc = device_get_softc(dev);
+ struct clocktime ct;
+ struct time_regs tregs;
+ int err;
+ uint8_t ampmflags, sreg;
+
+ /*
+ * We request a timespec with no resolution-adjustment. That also
+ * disables utc adjustment, so apply that ourselves.
+ */
+ 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->use_ampm) {
+ ampmflags = ISL12XX_24HR_FLAG;
+ } else {
+ ampmflags = 0;
+ if (ct.hour >= 12) {
+ ct.hour -= 12;
+ ampmflags |= ISL12XX_PM_FLAG;
+ }
+ if (ct.hour == 0)
+ ct.hour = 12;
+ }
+
+ tregs.sec = TOBCD(ct.sec);
+ tregs.min = TOBCD(ct.min);
+ tregs.hour = TOBCD(ct.hour) | ampmflags;
+ tregs.day = TOBCD(ct.day);
+ tregs.month = TOBCD(ct.mon);
+ tregs.year = TOBCD(ct.year % 100);
+
+ /*
+ * To set the time we have to set the WRTC enable bit in the control
+ * register, then write the time regs, then clear the WRTC bit. While
+ * doing so we have to be careful to not write a 0 to any sreg bit which
+ * is "write 0 to clear". One of those bits could get set between
+ * reading and writing the register. All those bits ignore attempts to
+ * write a 1, so just always OR-in all the W0C bits to be sure we never
+ * accidentally clear one. We hold ownership of the i2c bus for the
+ * whole read-modify-write sequence.
+ */
+ if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) != 0)
+ return (err);
+ if ((err = isl12xx_read1(sc, ISL12XX_SR_REG, &sreg)) == 0) {
+ sreg |= ISL12XX_SR_WRTC | ISL12XX_SR_W0C_BITS;
+ if ((err = isl12xx_write1(sc, ISL12XX_SR_REG, sreg)) == 0) {
+ err = iicdev_writeto(sc->dev, ISL12XX_SC_REG, &tregs,
+ sizeof(tregs), IIC_WAIT);
+ sreg &= ~ISL12XX_SR_WRTC;
+ isl12xx_write1(sc, ISL12XX_SR_REG, sreg);
+ }
+ }
+ iicbus_release_bus(sc->busdev, sc->dev);
+
+ return (err);
+}
+
+static device_method_t isl12xx_methods[] = {
+ /* device_if methods */
+ DEVMETHOD(device_probe, isl12xx_probe),
+ DEVMETHOD(device_attach, isl12xx_attach),
+ DEVMETHOD(device_detach, isl12xx_detach),
+
+ /* clock_if methods */
+ DEVMETHOD(clock_gettime, isl12xx_gettime),
+ DEVMETHOD(clock_settime, isl12xx_settime),
+
+ DEVMETHOD_END,
+};
+
+static driver_t isl12xx_driver = {
+ "isl12xx",
+ isl12xx_methods,
+ sizeof(struct isl12xx_softc),
+};
+static devclass_t isl12xx_devclass;
+
+DRIVER_MODULE(isl12xx, iicbus, isl12xx_driver, isl12xx_devclass, NULL, NULL);
+MODULE_VERSION(isl12xx, 1);
+MODULE_DEPEND(isl12xx, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
diff --git a/sys/dev/iicbus/nxprtc.c b/sys/dev/iicbus/nxprtc.c
new file mode 100644
index 0000000..7e800ae
--- /dev/null
+++ b/sys/dev/iicbus/nxprtc.c
@@ -0,0 +1,837 @@
+/*-
+ * Copyright (c) 2017 Ian Lepore <ian@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Driver for NXP real-time clock/calendar chips:
+ * - PCF8563 = low power, countdown timer
+ * - PCA8565 = like PCF8563, automotive temperature range
+ * - PCF8523 = low power, countdown timer, oscillator freq tuning, 2 timers
+ * - PCF2127 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, 512B ram
+ * - PCA2129 = like PCF8523, automotive, tcxo, tamper/ts, i2c & spi, no timer
+ * - PCF2129 = like PCF8523, industrial, tcxo, tamper/ts, i2c & spi, no timer
+ *
+ * Most chips have a countdown timer, ostensibly intended to generate periodic
+ * interrupt signals on an output pin. The timer is driven from the same
+ * divider chain that clocks the time of day registers, and they start counting
+ * in sync when the STOP bit is cleared after the time and timer registers are
+ * set. The timer register can also be read on the fly, so we use it to count
+ * fractional seconds and get a resolution of ~15ms.
+ */
+
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/clock.h>
+#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/module.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+#ifdef FDT
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include "clock_if.h"
+#include "iicbus_if.h"
+
+/*
+ * I2C address 1010 001x : PCA2129 PCF2127 PCF2129 PCF8563 PCF8565
+ * I2C address 1101 000x : PCF8523
+ */
+#define PCF8563_ADDR 0xa2
+#define PCF8523_ADDR 0xd0
+
+/*
+ * Registers, bits within them, and masks that are common to all chip types.
+ */
+#define PCF85xx_R_CS1 0x00 /* CS1 and CS2 control regs are in */
+#define PCF85xx_R_CS2 0x01 /* the same location on all chips. */
+
+#define PCF85xx_B_CS1_STOP 0x20 /* Stop time incrementing bit */
+#define PCF85xx_B_SECOND_OS 0x80 /* Oscillator Stopped bit */
+
+#define PCF85xx_M_SECOND 0x7f /* Masks for all BCD time regs... */
+#define PCF85xx_M_MINUTE 0x7f
+#define PCF85xx_M_12HOUR 0x1f
+#define PCF85xx_M_24HOUR 0x3f
+#define PCF85xx_M_DAY 0x3f
+#define PCF85xx_M_MONTH 0x1f
+#define PCF85xx_M_YEAR 0xff
+
+/*
+ * PCF2127-specific registers, bits, and masks.
+ */
+#define PCF2127_R_TMR_CTL 0x10 /* Timer/watchdog control */
+
+#define PCF2127_M_TMR_CTRL 0xe3 /* Mask off undef bits */
+
+#define PCF2127_B_TMR_CD 0x40 /* Run in countdown mode */
+#define PCF2127_B_TMR_64HZ 0x01 /* Timer frequency 64Hz */
+
+/*
+ * PCA/PCF2129-specific registers, bits, and masks.
+ */
+#define PCF2129_B_CS1_12HR 0x04 /* Use 12-hour (AM/PM) mode bit */
+#define PCF2129_B_CLKOUT_OTPR 0x20 /* OTP refresh command */
+#define PCF2129_B_CLKOUT_HIGHZ 0x07 /* Clock Out Freq = disable */
+
+/*
+ * PCF8523-specific registers, bits, and masks.
+ */
+#define PCF8523_R_CS3 0x02 /* Control and status reg 3 */
+#define PCF8523_R_SECOND 0x03 /* Seconds */
+#define PCF8523_R_TMR_CLKOUT 0x0F /* Timer and clockout control */
+#define PCF8523_R_TMR_A_FREQ 0x10 /* Timer A frequency control */
+#define PCF8523_R_TMR_A_COUNT 0x11 /* Timer A count */
+
+#define PCF8523_M_TMR_A_FREQ 0x07 /* Mask off undef bits */
+
+#define PCF8523_B_HOUR_PM 0x20 /* PM bit */
+#define PCF8523_B_CS1_SOFTRESET 0x58 /* Initiate Soft Reset bits */
+#define PCF8523_B_CS1_12HR 0x08 /* Use 12-hour (AM/PM) mode bit */
+#define PCF8523_B_CLKOUT_TACD 0x02 /* TimerA runs in CountDown mode */
+#define PCF8523_B_CLKOUT_HIGHZ 0x38 /* Clock Out Freq = disable */
+#define PCF8523_B_TMR_A_64HZ 0x01 /* Timer A freq 64Hz */
+
+#define PCF8523_M_CS3_PM 0xE0 /* Power mode mask */
+#define PCF8523_B_CS3_PM_NOBAT 0xE0 /* PM bits: no battery usage */
+#define PCF8523_B_CS3_PM_STD 0x00 /* PM bits: standard */
+#define PCF8523_B_CS3_BLF 0x04 /* Battery Low Flag bit */
+
+/*
+ * PCF8563-specific registers, bits, and masks.
+ */
+#define PCF8563_R_SECOND 0x02 /* Seconds */
+#define PCF8563_R_TMR_CTRL 0x0e /* Timer control */
+#define PCF8563_R_TMR_COUNT 0x0f /* Timer count */
+
+#define PCF8563_M_TMR_CTRL 0x93 /* Mask off undef bits */
+
+#define PCF8563_B_TMR_ENABLE 0x80 /* Enable countdown timer */
+#define PCF8563_B_TMR_64HZ 0x01 /* Timer frequency 64Hz */
+
+#define PCF8563_B_MONTH_C 0x80 /* Century bit */
+
+/*
+ * We use the countdown timer for fractional seconds. We program it for 64 Hz,
+ * the fastest available rate that doesn't roll over in less than a second.
+ */
+#define TMR_TICKS_SEC 64
+#define TMR_TICKS_HALFSEC 32
+
+/*
+ * The chip types we support.
+ */
+enum {
+ TYPE_NONE,
+ TYPE_PCA2129,
+ TYPE_PCA8565,
+ TYPE_PCF2127,
+ TYPE_PCF2129,
+ TYPE_PCF8523,
+ TYPE_PCF8563,
+
+ TYPE_COUNT
+};
+static const char *desc_strings[] = {
+ "",
+ "NXP PCA2129 RTC",
+ "NXP PCA8565 RTC",
+ "NXP PCF2127 RTC",
+ "NXP PCF2129 RTC",
+ "NXP PCF8523 RTC",
+ "NXP PCF8563 RTC",
+};
+CTASSERT(nitems(desc_strings) == TYPE_COUNT);
+
+/*
+ * The time registers in the order they are laid out in hardware.
+ */
+struct time_regs {
+ uint8_t sec, min, hour, day, wday, month, year;
+};
+
+struct nxprtc_softc {
+ device_t dev;
+ device_t busdev;
+ struct intr_config_hook
+ config_hook;
+ u_int flags; /* SC_F_* flags */
+ u_int chiptype; /* Type of PCF85xx chip */
+ uint8_t secaddr; /* Address of seconds register */
+ uint8_t tmcaddr; /* Address of timer count register */
+ bool use_timer; /* Use timer for fractional sec */
+};
+
+#define SC_F_CPOL (1 << 0) /* Century bit means 19xx */
+#define SC_F_AMPM (1 << 1) /* Use PM flag in hours reg */
+
+/*
+ * We use the compat_data table to look up hint strings in the non-FDT case, so
+ * define the struct locally when we don't get it from ofw_bus_subr.h.
+ */
+#ifdef FDT
+typedef struct ofw_compat_data nxprtc_compat_data;
+#else
+typedef struct {
+ const char *ocd_str;
+ uintptr_t ocd_data;
+} nxprtc_compat_data;
+#endif
+
+static nxprtc_compat_data compat_data[] = {
+ {"nxp,pca2129", TYPE_PCA2129},
+ {"nxp,pca8565", TYPE_PCA8565},
+ {"nxp,pcf2127", TYPE_PCF2127},
+ {"nxp,pcf2129", TYPE_PCF2129},
+ {"nxp,pcf8523", TYPE_PCF8523},
+ {"nxp,pcf8563", TYPE_PCF8563},
+
+ /* Undocumented compat strings known to exist in the wild... */
+ {"pcf8563", TYPE_PCF8563},
+ {"phg,pcf8563", TYPE_PCF8563},
+ {"philips,pcf8563", TYPE_PCF8563},
+
+ {NULL, TYPE_NONE},
+};
+
+static int
+read_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t *val)
+{
+
+ return (iicdev_readfrom(sc->dev, reg, val, sizeof(*val), IIC_WAIT));
+}
+
+static int
+write_reg(struct nxprtc_softc *sc, uint8_t reg, uint8_t val)
+{
+
+ return (iicdev_writeto(sc->dev, reg, &val, sizeof(val), IIC_WAIT));
+}
+
+static int
+read_timeregs(struct nxprtc_softc *sc, struct time_regs *tregs, uint8_t *tmr)
+{
+ int err;
+ uint8_t sec, tmr1, tmr2;
+
+ /*
+ * The datasheet says loop to read the same timer value twice because it
+ * does not freeze while reading. To that we add our own logic that
+ * the seconds register must be the same before and after reading the
+ * timer, ensuring the fractional part is from the same second as tregs.
+ */
+ do {
+ if (sc->use_timer) {
+ if ((err = read_reg(sc, sc->secaddr, &sec)) != 0)
+ break;
+ if ((err = read_reg(sc, sc->tmcaddr, &tmr1)) != 0)
+ break;
+ if ((err = read_reg(sc, sc->tmcaddr, &tmr2)) != 0)
+ break;
+ if (tmr1 != tmr2)
+ continue;
+ }
+ if ((err = iicdev_readfrom(sc->dev, sc->secaddr, tregs,
+ sizeof(*tregs), IIC_WAIT)) != 0)
+ break;
+ } while (sc->use_timer && tregs->sec != sec);
+
+ /*
+ * If the timer value is greater than our hz rate (or is zero),
+ * something is wrong. Maybe some other OS used the timer differently?
+ * Just set it to zero. Likewise if we're not using the timer. After
+ * the offset calc below, the zero turns into 32, the mid-second point,
+ * which in effect performs 4/5 rounding, which is just the right thing
+ * to do if we don't have fine-grained time.
+ */
+ if (!sc->use_timer || tmr1 > TMR_TICKS_SEC)
+ tmr1 = 0;
+
+ /*
+ * Turn the downcounter into an upcounter. The timer starts counting at
+ * and rolls over at mid-second, so add half a second worth of ticks to
+ * get its zero point back in sync with the tregs.sec rollover.
+ */
+ *tmr = (TMR_TICKS_SEC - tmr1 + TMR_TICKS_HALFSEC) % TMR_TICKS_SEC;
+
+ return (err);
+}
+
+static int
+write_timeregs(struct nxprtc_softc *sc, struct time_regs *tregs)
+{
+
+ return (iicdev_writeto(sc->dev, sc->secaddr, tregs,
+ sizeof(*tregs), IIC_WAIT));
+}
+
+static int
+pcf8523_start(struct nxprtc_softc *sc)
+{
+ int err;
+ uint8_t cs1, cs3, clkout;
+ bool is2129;
+
+ is2129 = (sc->chiptype == TYPE_PCA2129 || sc->chiptype == TYPE_PCF2129);
+
+ /* Read and sanity-check the control registers. */
+ if ((err = read_reg(sc, PCF85xx_R_CS1, &cs1)) != 0) {
+ device_printf(sc->dev, "cannot read RTC CS1 control\n");
+ return (err);
+ }
+ if ((err = read_reg(sc, PCF8523_R_CS3, &cs3)) != 0) {
+ device_printf(sc->dev, "cannot read RTC CS3 control\n");
+ return (err);
+ }
+
+ /*
+ * Do a full init (soft-reset) if...
+ * - The chip is in battery-disable mode (fresh from the factory).
+ * - The clock-increment STOP flag is set (this is just insane).
+ * After reset, battery disable mode has to be overridden to "standard"
+ * mode. Also, turn off clock output to save battery power.
+ */
+ if ((cs3 & PCF8523_M_CS3_PM) == PCF8523_B_CS3_PM_NOBAT ||
+ (cs1 & PCF85xx_B_CS1_STOP)) {
+ cs1 = PCF8523_B_CS1_SOFTRESET;
+ if ((err = write_reg(sc, PCF85xx_R_CS1, cs1)) != 0) {
+ device_printf(sc->dev, "cannot write CS1 control\n");
+ return (err);
+ }
+ cs3 = PCF8523_B_CS3_PM_STD;
+ if ((err = write_reg(sc, PCF8523_R_CS3, cs3)) != 0) {
+ device_printf(sc->dev, "cannot write CS3 control\n");
+ return (err);
+ }
+ /*
+ * For 2129 series, trigger OTP refresh by forcing the OTPR bit
+ * to zero then back to 1, then wait 100ms for the refresh, and
+ * finally set the bit back to zero with the COF_HIGHZ write.
+ */
+ if (is2129) {
+ clkout = PCF2129_B_CLKOUT_HIGHZ;
+ if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT,
+ clkout)) != 0) {
+ device_printf(sc->dev,
+ "cannot write CLKOUT control\n");
+ return (err);
+ }
+ if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT,
+ clkout | PCF2129_B_CLKOUT_OTPR)) != 0) {
+ device_printf(sc->dev,
+ "cannot write CLKOUT control\n");
+ return (err);
+ }
+ pause_sbt("nxpotp", mstosbt(100), mstosbt(10), 0);
+ } else
+ clkout = PCF8523_B_CLKOUT_HIGHZ;
+ if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT, clkout)) != 0) {
+ device_printf(sc->dev, "cannot write CLKOUT control\n");
+ return (err);
+ }
+ device_printf(sc->dev,
+ "first time startup, enabled RTC battery operation\n");
+
+ /*
+ * Sleep briefly so the battery monitor can make a measurement,
+ * then re-read CS3 so battery-low status can be reported below.
+ */
+ pause_sbt("nxpbat", mstosbt(100), 0, 0);
+ if ((err = read_reg(sc, PCF8523_R_CS3, &cs3)) != 0) {
+ device_printf(sc->dev, "cannot read RTC CS3 control\n");
+ return (err);
+ }
+ }
+
+ /* Let someone know if the battery is weak. */
+ if (cs3 & PCF8523_B_CS3_BLF)
+ device_printf(sc->dev, "WARNING: RTC battery is low\n");
+
+ /* Remember whether we're running in AM/PM mode. */
+ if (is2129) {
+ if (cs1 & PCF2129_B_CS1_12HR)
+ sc->flags |= SC_F_AMPM;
+ } else {
+ if (cs1 & PCF8523_B_CS1_12HR)
+ sc->flags |= SC_F_AMPM;
+ }
+
+ return (0);
+}
+
+static int
+pcf8523_start_timer(struct nxprtc_softc *sc)
+{
+ int err;
+ uint8_t clkout, stdclk, stdfreq, tmrfreq;
+
+ /*
+ * Read the timer control and frequency regs. If they don't have the
+ * values we normally program into them then the timer count doesn't
+ * contain a valid fractional second, so zero it to prevent using a bad
+ * value. Then program the normal timer values so that on the first
+ * settime call we'll begin to use fractional time.
+ */
+ if ((err = read_reg(sc, PCF8523_R_TMR_A_FREQ, &tmrfreq)) != 0)
+ return (err);
+ if ((err = read_reg(sc, PCF8523_R_TMR_CLKOUT, &clkout)) != 0)
+ return (err);
+
+ stdfreq = PCF8523_B_TMR_A_64HZ;
+ stdclk = PCF8523_B_CLKOUT_TACD | PCF8523_B_CLKOUT_HIGHZ;
+
+ if (clkout != stdclk || (tmrfreq & PCF8523_M_TMR_A_FREQ) != stdfreq) {
+ if ((err = write_reg(sc, sc->tmcaddr, 0)) != 0)
+ return (err);
+ if ((err = write_reg(sc, PCF8523_R_TMR_A_FREQ, stdfreq)) != 0)
+ return (err);
+ if ((err = write_reg(sc, PCF8523_R_TMR_CLKOUT, stdclk)) != 0)
+ return (err);
+ }
+ return (0);
+}
+
+static int
+pcf2127_start_timer(struct nxprtc_softc *sc)
+{
+ int err;
+ uint8_t stdctl, tmrctl;
+
+ /* See comment in pcf8523_start_timer(). */
+ if ((err = read_reg(sc, PCF2127_R_TMR_CTL, &tmrctl)) != 0)
+ return (err);
+
+ stdctl = PCF2127_B_TMR_CD | PCF8523_B_TMR_A_64HZ;
+
+ if ((tmrctl & PCF2127_M_TMR_CTRL) != stdctl) {
+ if ((err = write_reg(sc, sc->tmcaddr, 0)) != 0)
+ return (err);
+ if ((err = write_reg(sc, PCF2127_R_TMR_CTL, stdctl)) != 0)
+ return (err);
+ }
+ return (0);
+}
+
+static int
+pcf8563_start_timer(struct nxprtc_softc *sc)
+{
+ int err;
+ uint8_t stdctl, tmrctl;
+
+ /* See comment in pcf8523_start_timer(). */
+ if ((err = read_reg(sc, PCF8563_R_TMR_CTRL, &tmrctl)) != 0)
+ return (err);
+
+ stdctl = PCF8563_B_TMR_ENABLE | PCF8563_B_TMR_64HZ;
+
+ if ((tmrctl & PCF8563_M_TMR_CTRL) != stdctl) {
+ if ((err = write_reg(sc, sc->tmcaddr, 0)) != 0)
+ return (err);
+ if ((err = write_reg(sc, PCF8563_R_TMR_CTRL, stdctl)) != 0)
+ return (err);
+ }
+ return (0);
+}
+
+static void
+nxprtc_start(void *dev)
+{
+ struct nxprtc_softc *sc;
+ int clockflags, resolution;
+ uint8_t sec;
+
+ sc = device_get_softc((device_t)dev);
+ config_intrhook_disestablish(&sc->config_hook);
+
+ /* First do chip-specific inits. */
+ switch (sc->chiptype) {
+ case TYPE_PCA2129:
+ case TYPE_PCF2129:
+ if (pcf8523_start(sc) != 0)
+ return;
+ /* No timer to start */
+ break;
+ case TYPE_PCF2127:
+ if (pcf8523_start(sc) != 0)
+ return;
+ if (pcf2127_start_timer(sc) != 0) {
+ device_printf(sc->dev, "cannot set up timer\n");
+ return;
+ }
+ break;
+ case TYPE_PCF8523:
+ if (pcf8523_start(sc) != 0)
+ return;
+ if (pcf8523_start_timer(sc) != 0) {
+ device_printf(sc->dev, "cannot set up timer\n");
+ return;
+ }
+ break;
+ case TYPE_PCA8565:
+ case TYPE_PCF8563:
+ if (pcf8563_start_timer(sc) != 0) {
+ device_printf(sc->dev, "cannot set up timer\n");
+ return;
+ }
+ break;
+ default:
+ device_printf(sc->dev, "missing init code for this chiptype\n");
+ return;
+ }
+
+ /*
+ * Common init. Read the seconds register so we can check the
+ * oscillator-stopped status bit in it.
+ */
+ if (read_reg(sc, sc->secaddr, &sec) != 0) {
+ device_printf(sc->dev, "cannot read RTC seconds\n");
+ return;
+ }
+ if ((sec & PCF85xx_B_SECOND_OS) != 0) {
+ device_printf(sc->dev,
+ "WARNING: RTC battery failed; time is invalid\n");
+ }
+
+ /*
+ * Everything looks good if we make it to here; register as an RTC. If
+ * we're using the timer to count fractional seconds, our resolution is
+ * 1e6/64, about 15.6ms. Without the timer we still align the RTC clock
+ * when setting it so our error is an average .5s when reading it.
+ * Schedule our clock_settime() method to be called at a .495ms offset
+ * into the second, because the clock hardware resets the divider chain
+ * to the mid-second point when you set the time and it takes about 5ms
+ * of i2c bus activity to set the clock.
+ */
+ resolution = sc->use_timer ? 1000000 / TMR_TICKS_SEC : 1000000 / 2;
+ clockflags = CLOCKF_GETTIME_NO_ADJ | CLOCKF_SETTIME_NO_TS;
+ clock_register_flags(sc->dev, resolution, clockflags);
+ clock_schedule(sc->dev, 495000000);
+}
+
+static int
+nxprtc_gettime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ struct time_regs tregs;
+ struct nxprtc_softc *sc;
+ int err;
+ uint8_t cs1, hourmask, tmrcount;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * Read the time, but before using it, validate that the oscillator-
+ * stopped/power-fail bit is not set, and that the time-increment STOP
+ * bit is not set in the control reg. The latter can happen if there
+ * was an error when setting the time.
+ */
+ if ((err = read_timeregs(sc, &tregs, &tmrcount)) != 0) {
+ device_printf(dev, "cannot read RTC time\n");
+ return (err);
+ }
+ if ((err = read_reg(sc, PCF85xx_R_CS1, &cs1)) != 0) {
+ device_printf(dev, "cannot read RTC time\n");
+ return (err);
+ }
+ if ((tregs.sec & PCF85xx_B_SECOND_OS) || (cs1 & PCF85xx_B_CS1_STOP)) {
+ device_printf(dev, "RTC clock not running\n");
+ return (EINVAL); /* hardware is good, time is not. */
+ }
+
+ if (sc->flags & SC_F_AMPM)
+ hourmask = PCF85xx_M_12HOUR;
+ else
+ hourmask = PCF85xx_M_24HOUR;
+
+ ct.nsec = ((uint64_t)tmrcount * 1000000000) / TMR_TICKS_SEC;
+ ct.sec = FROMBCD(tregs.sec & PCF85xx_M_SECOND);
+ ct.min = FROMBCD(tregs.min & PCF85xx_M_MINUTE);
+ ct.hour = FROMBCD(tregs.hour & hourmask);
+ ct.day = FROMBCD(tregs.day & PCF85xx_M_DAY);
+ ct.mon = FROMBCD(tregs.month & PCF85xx_M_MONTH);
+ ct.year = FROMBCD(tregs.year & PCF85xx_M_YEAR);
+ ct.year += 1900;
+ if (ct.year < POSIX_BASE_YEAR)
+ ct.year += 100; /* assume [1970, 2069] */
+
+ /*
+ * Old PCF8563 datasheets recommended that the C bit be 1 for 19xx and 0
+ * for 20xx; newer datasheets don't recommend that. We don't care,
+ * but we may co-exist with other OSes sharing the hardware. Determine
+ * existing polarity on a read so that we can preserve it on a write.
+ */
+ if (sc->chiptype == TYPE_PCF8563) {
+ if (tregs.month & PCF8563_B_MONTH_C) {
+ if (ct.year >= 2000)
+ sc->flags |= SC_F_CPOL;
+ } else if (ct.year < 2000)
+ sc->flags |= SC_F_CPOL;
+ }
+
+ /* If this chip is running in 12-hour/AMPM mode, deal with it. */
+ if (sc->flags & SC_F_AMPM) {
+ if (ct.hour == 12)
+ ct.hour = 0;
+ if (tregs.hour & PCF8523_B_HOUR_PM)
+ ct.hour += 12;
+ }
+
+ err = clock_ct_to_ts(&ct, ts);
+ ts->tv_sec += utc_offset();
+
+ return (err);
+}
+
+static int
+nxprtc_settime(device_t dev, struct timespec *ts)
+{
+ struct clocktime ct;
+ struct time_regs tregs;
+ struct nxprtc_softc *sc;
+ int err;
+ uint8_t cflag, cs1, pmflag;
+
+ sc = device_get_softc(dev);
+
+ /*
+ * We stop the clock, set the time, then restart the clock. Half a
+ * second after restarting the clock it ticks over to the next second.
+ * So to align the RTC, we schedule this function to be called when
+ * system time is roughly halfway (.495) through the current second.
+ *
+ * Reserve use of the i2c bus and stop the RTC clock. Note that if
+ * anything goes wrong from this point on, we leave the clock stopped,
+ * because we don't really know what state it's in.
+ */
+ if ((err = iicbus_request_bus(sc->busdev, sc->dev, IIC_WAIT)) != 0)
+ return (err);
+ if ((err = read_reg(sc, PCF85xx_R_CS1, &cs1)) != 0)
+ goto errout;
+ cs1 |= PCF85xx_B_CS1_STOP;
+ if ((err = write_reg(sc, PCF85xx_R_CS1, cs1)) != 0)
+ goto errout;
+
+ /* Grab a fresh post-sleep idea of what time it is. */
+ getnanotime(ts);
+ ts->tv_sec -= utc_offset();
+ ts->tv_nsec = 0;
+ clock_ts_to_ct(ts, &ct);
+
+ /* If the chip is in AMPM mode deal with the PM flag. */
+ pmflag = 0;
+ if (sc->flags & SC_F_AMPM) {
+ if (ct.hour >= 12) {
+ ct.hour -= 12;
+ pmflag = PCF8523_B_HOUR_PM;
+ }
+ if (ct.hour == 0)
+ ct.hour = 12;
+ }
+
+ /* On 8563 set the century based on the polarity seen when reading. */
+ cflag = 0;
+ if (sc->chiptype == TYPE_PCF8563) {
+ if ((sc->flags & SC_F_CPOL) != 0) {
+ if (ct.year >= 2000)
+ cflag = PCF8563_B_MONTH_C;
+ } else if (ct.year < 2000)
+ cflag = PCF8563_B_MONTH_C;
+ }
+
+ tregs.sec = TOBCD(ct.sec);
+ tregs.min = TOBCD(ct.min);
+ tregs.hour = TOBCD(ct.hour) | pmflag;
+ tregs.day = TOBCD(ct.day);
+ tregs.month = TOBCD(ct.mon);
+ tregs.year = TOBCD(ct.year % 100) | cflag;
+ tregs.wday = ct.dow;
+
+ /*
+ * Set the time, reset the timer count register, then start the clocks.
+ */
+ if ((err = write_timeregs(sc, &tregs)) != 0)
+ goto errout;
+
+ if ((err = write_reg(sc, sc->tmcaddr, TMR_TICKS_SEC)) != 0)
+ return (err);
+
+ cs1 &= ~PCF85xx_B_CS1_STOP;
+ err = write_reg(sc, PCF85xx_R_CS1, cs1);
+
+errout:
+
+ iicbus_release_bus(sc->busdev, sc->dev);
+
+ if (err != 0)
+ device_printf(dev, "cannot write RTC time\n");
+
+ return (err);
+}
+
+static int
+nxprtc_get_chiptype(device_t dev)
+{
+#ifdef FDT
+
+ return (ofw_bus_search_compatible(dev, compat_data)->ocd_data);
+#else
+ nxprtc_compat_data *cdata;
+ const char *htype;
+ int chiptype;
+
+ /*
+ * If given a chiptype hint string, loop through the ofw compat data
+ * comparing the hinted chip type to the compat strings. The table end
+ * marker ocd_data is TYPE_NONE.
+ */
+ if (resource_string_value(device_get_name(dev),
+ device_get_unit(dev), "compatible", &htype) == 0) {
+ for (cdata = compat_data; cdata->ocd_str != NULL; ++cdata) {
+ if (strcmp(htype, cdata->ocd_str) == 0)
+ break;
+ }
+ chiptype = cdata->ocd_data;
+ } else
+ chiptype = TYPE_NONE;
+
+ /*
+ * On non-FDT systems the historical behavior of this driver was to
+ * assume a PCF8563; keep doing that for compatibility.
+ */
+ if (chiptype == TYPE_NONE)
+ return (TYPE_PCF8563);
+ else
+ return (chiptype);
+#endif
+}
+
+static int
+nxprtc_probe(device_t dev)
+{
+ int chiptype, rv;
+
+#ifdef FDT
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+ rv = BUS_PROBE_GENERIC;
+#else
+ rv = BUS_PROBE_NOWILDCARD;
+#endif
+ if ((chiptype = nxprtc_get_chiptype(dev)) == TYPE_NONE)
+ return (ENXIO);
+
+ device_set_desc(dev, desc_strings[chiptype]);
+ return (rv);
+}
+
+static int
+nxprtc_attach(device_t dev)
+{
+ struct nxprtc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+ sc->busdev = device_get_parent(dev);
+
+ /* We need to know what kind of chip we're driving. */
+ sc->chiptype = nxprtc_get_chiptype(dev);
+
+ /* The features and some register addresses vary by chip type. */
+ switch (sc->chiptype) {
+ case TYPE_PCA2129:
+ case TYPE_PCF2129:
+ sc->secaddr = PCF8523_R_SECOND;
+ sc->tmcaddr = 0;
+ sc->use_timer = false;
+ break;
+ case TYPE_PCF2127:
+ case TYPE_PCF8523:
+ sc->secaddr = PCF8523_R_SECOND;
+ sc->tmcaddr = PCF8523_R_TMR_A_COUNT;
+ sc->use_timer = true;
+ break;
+ case TYPE_PCA8565:
+ case TYPE_PCF8563:
+ sc->secaddr = PCF8563_R_SECOND;
+ sc->tmcaddr = PCF8563_R_TMR_COUNT;
+ sc->use_timer = true;
+ break;
+ default:
+ device_printf(dev, "impossible: cannot determine chip type\n");
+ return (ENXIO);
+ }
+
+ /*
+ * We have to wait until interrupts are enabled. Sometimes I2C read
+ * and write only works when the interrupts are available.
+ */
+ sc->config_hook.ich_func = nxprtc_start;
+ sc->config_hook.ich_arg = dev;
+ if (config_intrhook_establish(&sc->config_hook) != 0)
+ return (ENOMEM);
+
+ return (0);
+}
+
+static int
+nxprtc_detach(device_t dev)
+{
+
+ clock_unregister(dev);
+ return (0);
+}
+
+static device_method_t nxprtc_methods[] = {
+ DEVMETHOD(device_probe, nxprtc_probe),
+ DEVMETHOD(device_attach, nxprtc_attach),
+ DEVMETHOD(device_detach, nxprtc_detach),
+
+ DEVMETHOD(clock_gettime, nxprtc_gettime),
+ DEVMETHOD(clock_settime, nxprtc_settime),
+
+ DEVMETHOD_END
+};
+
+static driver_t nxprtc_driver = {
+ "nxprtc",
+ nxprtc_methods,
+ sizeof(struct nxprtc_softc),
+};
+
+static devclass_t nxprtc_devclass;
+
+DRIVER_MODULE(nxprtc, iicbus, nxprtc_driver, nxprtc_devclass, NULL, NULL);
+MODULE_VERSION(nxprtc, 1);
+MODULE_DEPEND(nxprtc, iicbus, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
diff --git a/sys/dev/iicbus/pcf8563.c b/sys/dev/iicbus/pcf8563.c
deleted file mode 100644
index 556943a..0000000
--- a/sys/dev/iicbus/pcf8563.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/*-
- * Copyright (c) 2012 Marius Strobl <marius@FreeBSD.org>
- * Copyright (c) 2015 Juraj Lutter
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-/*
- * Driver for NXP PCF8563 real-time clock/calendar
- */
-
-#include "opt_platform.h"
-
-#include <sys/param.h>
-#include <sys/systm.h>
-#include <sys/bus.h>
-#include <sys/clock.h>
-#include <sys/kernel.h>
-#include <sys/module.h>
-
-#include <dev/iicbus/iicbus.h>
-#include <dev/iicbus/iiconf.h>
-#include <dev/iicbus/pcf8563reg.h>
-#ifdef FDT
-#include <dev/ofw/openfirm.h>
-#include <dev/ofw/ofw_bus.h>
-#include <dev/ofw/ofw_bus_subr.h>
-#endif
-
-#include "clock_if.h"
-#include "iicbus_if.h"
-
-#define PCF8563_NCLOCKREGS (PCF8563_R_YEAR - PCF8563_R_CS1 + 1)
-
-struct pcf8563_softc {
- struct intr_config_hook enum_hook;
- uint32_t sc_flags;
-#define PCF8563_CPOL (1 << 0) /* PCF8563_R_MONTH_C means 19xx */
- uint16_t sc_addr; /* PCF8563 slave address */
- uint16_t sc_year0; /* TOD clock year 0 */
-};
-
-static device_attach_t pcf8563_attach;
-static device_probe_t pcf8563_probe;
-static clock_gettime_t pcf8563_gettime;
-static clock_settime_t pcf8563_settime;
-static void pcf8563_start(void *);
-
-static int
-pcf8563_probe(device_t dev)
-{
-
-#ifdef FDT
- if (!ofw_bus_status_okay(dev))
- return (ENXIO);
- if (!ofw_bus_is_compatible(dev, "nxp,pcf8563") &&
- !ofw_bus_is_compatible(dev, "philips,pcf8563") &&
- !ofw_bus_is_compatible(dev, "pcf8563"))
- return (ENXIO);
-#endif
- device_set_desc(dev, "NXP PCF8563 RTC");
-
- return (BUS_PROBE_DEFAULT);
-}
-
-static int
-pcf8563_attach(device_t dev)
-{
- struct pcf8563_softc *sc;
-
- sc = device_get_softc(dev);
- sc->sc_addr = iicbus_get_addr(dev);
- if (sc->sc_addr == 0)
- sc->sc_addr = PCF8563_ADDR;
- sc->sc_year0 = 1900;
- sc->enum_hook.ich_func = pcf8563_start;
- sc->enum_hook.ich_arg = dev;
-
- /*
- * We have to wait until interrupts are enabled. Sometimes I2C read
- * and write only works when the interrupts are available.
- */
- if (config_intrhook_establish(&sc->enum_hook) != 0)
- return (ENOMEM);
-
- return (0);
-}
-
-static void
-pcf8563_start(void *xdev)
-{
- device_t dev;
- uint8_t reg = PCF8563_R_SECOND, val;
- struct iic_msg msgs[] = {
- { 0, IIC_M_WR, sizeof(reg), &reg },
- { 0, IIC_M_RD, sizeof(val), &val }
- };
- struct pcf8563_softc *sc;
-
- dev = (device_t)xdev;
- sc = device_get_softc(dev);
- config_intrhook_disestablish(&sc->enum_hook);
-
- /*
- * NB: PCF8563_R_SECOND_VL doesn't automatically clear when VDD
- * rises above Vlow again and needs to be cleared manually.
- * However, apparently this needs all of the time registers to be
- * set, i.e. pcf8563_settime(), and not just PCF8563_R_SECOND in
- * order for PCF8563_R_SECOND_VL to stick. Thus, we just issue a
- * warning here rather than failing with ENXIO in case it is set.
- * Note that pcf8563_settime() will also clear PCF8563_R_SECOND_VL
- * as a side-effect.
- */
- msgs[0].slave = msgs[1].slave = sc->sc_addr;
- if (iicbus_transfer(dev, msgs, nitems(msgs)) != 0) {
- device_printf(dev, "%s: cannot read RTC\n", __func__);
- return;
- }
- if ((val & PCF8563_R_SECOND_VL) != 0)
- device_printf(dev, "%s: battery low\n", __func__);
-
- clock_register(dev, 1000000); /* 1 second resolution */
-}
-
-static int
-pcf8563_gettime(device_t dev, struct timespec *ts)
-{
- struct clocktime ct;
- uint8_t reg = PCF8563_R_SECOND, val[PCF8563_NCLOCKREGS];
- struct iic_msg msgs[] = {
- { 0, IIC_M_WR, sizeof(reg), &reg },
- { 0, IIC_M_RD, PCF8563_NCLOCKREGS, &val[PCF8563_R_SECOND] }
- };
- struct pcf8563_softc *sc;
- int error;
-
- sc = device_get_softc(dev);
- msgs[0].slave = msgs[1].slave = sc->sc_addr;
- error = iicbus_transfer(dev, msgs, nitems(msgs));
- if (error != 0) {
- device_printf(dev, "%s: cannot read RTC\n", __func__);
- return (error);
- }
-
- ct.nsec = 0;
- ct.sec = FROMBCD(val[PCF8563_R_SECOND] & PCF8563_M_SECOND);
- ct.min = FROMBCD(val[PCF8563_R_MINUTE] & PCF8563_M_MINUTE);
- ct.hour = FROMBCD(val[PCF8563_R_HOUR] & PCF8563_M_HOUR);
- ct.day = FROMBCD(val[PCF8563_R_DAY] & PCF8563_M_DAY);
- ct.dow = val[PCF8563_R_WEEKDAY] & PCF8563_M_WEEKDAY;
- ct.mon = FROMBCD(val[PCF8563_R_MONTH] & PCF8563_M_MONTH);
- ct.year = FROMBCD(val[PCF8563_R_YEAR] & PCF8563_M_YEAR);
- ct.year += sc->sc_year0;
- if (ct.year < POSIX_BASE_YEAR)
- ct.year += 100; /* assume [1970, 2069] */
- if ((val[PCF8563_R_MONTH] & PCF8563_R_MONTH_C) != 0) {
- if (ct.year >= 100 + sc->sc_year0)
- sc->sc_flags |= PCF8563_CPOL;
- } else if (ct.year < 100 + sc->sc_year0)
- sc->sc_flags |= PCF8563_CPOL;
-
- return (clock_ct_to_ts(&ct, ts));
-}
-
-static int
-pcf8563_settime(device_t dev, struct timespec *ts)
-{
- struct clocktime ct;
- uint8_t val[PCF8563_NCLOCKREGS];
- struct iic_msg msgs[] = {
- { 0, IIC_M_WR, PCF8563_NCLOCKREGS - 1, &val[PCF8563_R_CS2] }
- };
- struct pcf8563_softc *sc;
- int error;
-
- sc = device_get_softc(dev);
- val[PCF8563_R_CS2] = PCF8563_R_SECOND; /* abuse */
- /* Accuracy is only one second. */
- if (ts->tv_nsec >= 500000000)
- ts->tv_sec++;
- ts->tv_nsec = 0;
- clock_ts_to_ct(ts, &ct);
- val[PCF8563_R_SECOND] = TOBCD(ct.sec);
- val[PCF8563_R_MINUTE] = TOBCD(ct.min);
- val[PCF8563_R_HOUR] = TOBCD(ct.hour);
- val[PCF8563_R_DAY] = TOBCD(ct.day);
- val[PCF8563_R_WEEKDAY] = ct.dow;
- val[PCF8563_R_MONTH] = TOBCD(ct.mon);
- val[PCF8563_R_YEAR] = TOBCD(ct.year % 100);
- if ((sc->sc_flags & PCF8563_CPOL) != 0) {
- if (ct.year >= 100 + sc->sc_year0)
- val[PCF8563_R_MONTH] |= PCF8563_R_MONTH_C;
- } else if (ct.year < 100 + sc->sc_year0)
- val[PCF8563_R_MONTH] |= PCF8563_R_MONTH_C;
-
- msgs[0].slave = sc->sc_addr;
- error = iicbus_transfer(dev, msgs, nitems(msgs));
- if (error != 0)
- device_printf(dev, "%s: cannot write RTC\n", __func__);
-
- return (error);
-}
-
-static device_method_t pcf8563_methods[] = {
- DEVMETHOD(device_probe, pcf8563_probe),
- DEVMETHOD(device_attach, pcf8563_attach),
-
- DEVMETHOD(clock_gettime, pcf8563_gettime),
- DEVMETHOD(clock_settime, pcf8563_settime),
-
- DEVMETHOD_END
-};
-
-static driver_t pcf8563_driver = {
- "pcf8563_rtc",
- pcf8563_methods,
- sizeof(struct pcf8563_softc),
-};
-
-static devclass_t pcf8563_devclass;
-
-DRIVER_MODULE(pcf8563, iicbus, pcf8563_driver, pcf8563_devclass, NULL, NULL);
-MODULE_VERSION(pcf8563, 1);
-MODULE_DEPEND(pcf8563, iicbus, 1, 1, 1);
diff --git a/sys/dev/iicbus/pcf8563reg.h b/sys/dev/iicbus/pcf8563reg.h
deleted file mode 100644
index 87af551..0000000
--- a/sys/dev/iicbus/pcf8563reg.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/* $NetBSD: pcf8563reg.h,v 1.1 2011/01/21 19:11:47 jakllsch Exp $ */
-
-/*-
- * Jonathan Kollasch, 2011
- *
- * This file is in the public domain.
- *
- * $FreeBSD$
- */
-
-/*
- * NXP (Philips) PCF8563 RTC registers
- */
-
-/* We only have clock mode registers here. */
-
-#ifndef _PCF8563REG_H_
-#define _PCF8563REG_H_
-
-/*
- * PCF8563 RTC I2C address:
- *
- * 101 0001
- */
-#define PCF8563_ADDR 0xa2
-
-#define PCF8563_R_CS1 0x00
-#define PCF8563_R_CS2 0x01
-#define PCF8563_R_SECOND 0x02
-#define PCF8563_R_MINUTE 0x03
-#define PCF8563_R_HOUR 0x04
-#define PCF8563_R_DAY 0x05
-#define PCF8563_R_WEEKDAY 0x06
-#define PCF8563_R_MONTH 0x07
-#define PCF8563_R_YEAR 0x08
-#define PCF8563_R_MINUTE_ALARM 0x09
-#define PCF8563_R_HOUR_ALARM 0x0a
-#define PCF8563_R_DAY_ALARM 0x0b
-#define PCF8563_R_WEEKDAY_ALARM 0x0c
-#define PCF8563_R_CLKOUT_CNTRL 0x0d
-#define PCF8563_R_TIMER_CNTRL 0x0e
-#define PCF8563_R_TIMER 0x0f
-
-#define PCF8563_R_SECOND_VL 0x80
-#define PCF8563_R_MONTH_C 0x80
-
-#define PCF8563_NREGS 0x10
-
-#define PCF8563_M_SECOND 0x7f
-#define PCF8563_M_MINUTE 0x7f
-#define PCF8563_M_HOUR 0x3f
-#define PCF8563_M_DAY 0x3f
-#define PCF8563_M_WEEKDAY 0x07
-#define PCF8563_M_MONTH 0x1f
-#define PCF8563_M_CENTURY 0x80
-#define PCF8563_M_YEAR 0xff
-
-#endif /* _PCF8563REG_H_ */
diff --git a/sys/dev/iicbus/s35390a.c b/sys/dev/iicbus/s35390a.c
index 59369b3..042f088 100644
--- a/sys/dev/iicbus/s35390a.c
+++ b/sys/dev/iicbus/s35390a.c
@@ -59,6 +59,8 @@ __FBSDID("$FreeBSD$");
* Driver for Seiko Instruments S-35390A Real-time Clock
*/
+#include "opt_platform.h"
+
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
@@ -69,6 +71,12 @@ __FBSDID("$FreeBSD$");
#include <dev/iicbus/iicbus.h>
#include <dev/iicbus/iiconf.h>
+#ifdef FDT
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
#include "clock_if.h"
#include "iicbus_if.h"
@@ -159,7 +167,7 @@ s390rtc_read(device_t dev, uint8_t reg, uint8_t *buf, size_t len)
int i;
int error;
- error = iicbus_transfer(dev, msg, 1);
+ error = iicbus_transfer_excl(dev, msg, 1, IIC_WAIT);
if (error)
return (error);
@@ -188,13 +196,20 @@ s390rtc_write(device_t dev, uint8_t reg, uint8_t *buf, size_t len)
for (i = 0; i < len; ++i)
buf[i] = bitreverse(buf[i]);
- return (iicbus_transfer(dev, msg, 1));
+ return (iicbus_transfer_excl(dev, msg, 1, IIC_WAIT));
}
static int
s390rtc_probe(device_t dev)
{
+#ifdef FDT
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "sii,s35390a"))
+ return (ENXIO);
+#else
if (iicbus_get_addr(dev) != S390_ADDR) {
if (bootverbose)
device_printf(dev, "slave address mismatch. "
@@ -202,35 +217,35 @@ s390rtc_probe(device_t dev)
S390_ADDR);
return (ENXIO);
}
- device_set_desc(dev, "Seiko Instruments S-35390A Real-time Clock");
+#endif
+ device_set_desc(dev, "Seiko Instruments S-35390A RTC");
- return (BUS_PROBE_SPECIFIC);
+ return (BUS_PROBE_DEFAULT);
}
-static int
-s390rtc_attach(device_t dev)
+static void
+s390rtc_start(void *arg)
{
- struct s390rtc_softc *sc;
+ device_t dev;
uint8_t reg;
int error;
- sc = device_get_softc(dev);
- sc->sc_dev = dev;
- sc->sc_addr = iicbus_get_addr(dev);
+ dev = arg;
/* Reset the chip and turn on 24h mode, after power-off or battery. */
error = s390rtc_read(dev, S390_STATUS1, &reg, 1);
if (error) {
device_printf(dev, "%s: cannot read status1 register\n",
__func__);
- return (error);
+ return;
}
if (reg & (S390_ST1_POC | S390_ST1_BLD)) {
reg |= S390_ST1_24H | S390_ST1_RESET;
error = s390rtc_write(dev, S390_STATUS1, &reg, 1);
if (error) {
- device_printf(dev, "%s: cannot initialize\n", __func__);
- return (error);
+ device_printf(dev,
+ "%s: cannot initialize\n", __func__);
+ return;
}
}
@@ -239,7 +254,7 @@ s390rtc_attach(device_t dev)
if (error) {
device_printf(dev, "%s: cannot read status2 register\n",
__func__);
- return (error);
+ return;
}
if (reg & S390_ST2_TEST) {
reg &= ~S390_ST2_TEST;
@@ -247,11 +262,32 @@ s390rtc_attach(device_t dev)
if (error) {
device_printf(dev,
"%s: cannot disable the test mode\n", __func__);
- return (error);
+ return;
}
}
clock_register(dev, 1000000); /* 1 second resolution */
+}
+
+static int
+s390rtc_attach(device_t dev)
+{
+ struct s390rtc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_addr = iicbus_get_addr(dev);
+
+ config_intrhook_oneshot(s390rtc_start, dev);
+
+ return (0);
+}
+
+static int
+s390rtc_detach(device_t dev)
+{
+
+ clock_unregister(dev);
return (0);
}
@@ -310,6 +346,7 @@ s390rtc_settime(device_t dev, struct timespec *ts)
static device_method_t s390rtc_methods[] = {
DEVMETHOD(device_probe, s390rtc_probe),
DEVMETHOD(device_attach, s390rtc_attach),
+ DEVMETHOD(device_detach, s390rtc_detach),
DEVMETHOD(clock_gettime, s390rtc_gettime),
DEVMETHOD(clock_settime, s390rtc_settime),
diff --git a/sys/mips/conf/XLP.hints b/sys/mips/conf/XLP.hints
index f78eb27..8597c92 100644
--- a/sys/mips/conf/XLP.hints
+++ b/sys/mips/conf/XLP.hints
@@ -1,5 +1,6 @@
# $FreeBSD$
# RTC
-hint.ds1374_rtc.0.at="iicbus1"
-hint.ds1374_rtc.0.addr=0xd0
+hint.ds13rtc.0.at="iicbus1"
+hint.ds13rtc.0.addr=0xd0
+hint.ds13rtc.0.compatible="dallas,ds1374"
diff --git a/sys/mips/conf/XLR b/sys/mips/conf/XLR
index 0133fad..3223b6d 100644
--- a/sys/mips/conf/XLR
+++ b/sys/mips/conf/XLR
@@ -135,7 +135,7 @@ device ic
device iic
device iicbb
device iicbus
-device ds1374 # RTC on XLR boards
+device ds13rtc # RTC on XLR boards
device max6657 # Temparature sensor on XLR boards
device at24co2n # EEPROM on XLR boards
diff --git a/sys/mips/conf/XLR64 b/sys/mips/conf/XLR64
index c8b1dfb..85cff7e 100644
--- a/sys/mips/conf/XLR64
+++ b/sys/mips/conf/XLR64
@@ -109,7 +109,7 @@ device ic
device iic
device iicbb
device iicbus
-device ds1374 # RTC on XLR boards
+device ds13rtc # RTC on XLR boards
device max6657 # Temparature sensor on XLR boards
device at24co2n # EEPROM on XLR boards
diff --git a/sys/mips/conf/XLRN32 b/sys/mips/conf/XLRN32
index e2f4468..55d4c6a 100644
--- a/sys/mips/conf/XLRN32
+++ b/sys/mips/conf/XLRN32
@@ -113,7 +113,7 @@ device ic
device iic
device iicbb
device iicbus
-device ds1374 # RTC on XLR boards
+device ds13rtc # RTC on XLR boards
device max6657 # Temparature sensor on XLR boards
device at24co2n # EEPROM on XLR boards
diff --git a/sys/mips/conf/std.XLP b/sys/mips/conf/std.XLP
index 3d249bc..888ad89 100644
--- a/sys/mips/conf/std.XLP
+++ b/sys/mips/conf/std.XLP
@@ -94,7 +94,7 @@ device umass # Requires scbus and da
device iic
device iicbus
device iicoc
-device ds1374 # RTC on XLP boards
+device ds13rtc # RTC on XLP boards
# Crypto
device crypto
diff --git a/sys/mips/rmi/xlr_i2c.c b/sys/mips/rmi/xlr_i2c.c
index 2605887..337d606 100644
--- a/sys/mips/rmi/xlr_i2c.c
+++ b/sys/mips/rmi/xlr_i2c.c
@@ -187,7 +187,7 @@ xlr_i2c_attach(device_t dev)
return -1;
}
if(xlr_board_info.xlr_i2c_device[I2C_RTC].enabled == 1) {
- tmpd = device_add_child(sc->iicbus, "ds1374_rtc", 0);
+ tmpd = device_add_child(sc->iicbus, "ds13rtc", 0);
device_set_ivars(tmpd, &xlr_board_info.xlr_i2c_device[I2C_RTC]);
}
if(xlr_board_info.xlr_i2c_device[I2C_THERMAL].enabled == 1) {
@@ -199,6 +199,16 @@ xlr_i2c_attach(device_t dev)
device_set_ivars(tmpd, &xlr_board_info.xlr_i2c_device[I2C_EEPROM]);
}
+ /*
+ * The old ds1374 rtc driver only handled one chip type. The new
+ * ds13rtc driver handles all ds13xx chips, but must be told the chip
+ * type via hints. XLR historically hasn't had a standard hints file,
+ * so set up the hint now if it isn't already there.
+ */
+#define HINTNAME "hint.ds13rtc.0.compatible"
+ if (!testenv(HINTNAME))
+ kern_setenv(HINTNAME, "dallas,ds1374");
+
bus_generic_attach(dev);
return (0);
diff --git a/sys/modules/i2c/Makefile b/sys/modules/i2c/Makefile
index 8e9bdf9..34124c6 100644
--- a/sys/modules/i2c/Makefile
+++ b/sys/modules/i2c/Makefile
@@ -1,6 +1,22 @@
# $FreeBSD$
-SUBDIR =
-SUBDIR += controllers if_ic smbus iicbus iicbb iicsmb iic cyapa smb isl jedec_ts
+SUBDIR = \
+ controllers \
+ cyapa \
+ ds1307 \
+ ds13rtc \
+ ds3231 \
+ if_ic \
+ iic \
+ iicbb \
+ iicbus \
+ iicsmb \
+ isl \
+ isl12xx \
+ jedec_ts \
+ nxprtc \
+ s35390a \
+ smb \
+ smbus \
.include <bsd.subdir.mk>
diff --git a/sys/modules/i2c/ds1307/Makefile b/sys/modules/i2c/ds1307/Makefile
new file mode 100644
index 0000000..32b8039
--- /dev/null
+++ b/sys/modules/i2c/ds1307/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus
+KMOD = ds1307
+SRCS = ds1307.c bus_if.h clock_if.h device_if.h iicbus_if.h ofw_bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/i2c/ds13rtc/Makefile b/sys/modules/i2c/ds13rtc/Makefile
new file mode 100644
index 0000000..e50bc2f
--- /dev/null
+++ b/sys/modules/i2c/ds13rtc/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus
+KMOD = ds13rtc
+SRCS = ds13rtc.c bus_if.h clock_if.h device_if.h iicbus_if.h ofw_bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/i2c/ds3231/Makefile b/sys/modules/i2c/ds3231/Makefile
new file mode 100644
index 0000000..e75c47c
--- /dev/null
+++ b/sys/modules/i2c/ds3231/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus
+KMOD = ds3231
+SRCS = ds3231.c bus_if.h clock_if.h device_if.h iicbus_if.h ofw_bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/i2c/isl12xx/Makefile b/sys/modules/i2c/isl12xx/Makefile
new file mode 100644
index 0000000..5f31317
--- /dev/null
+++ b/sys/modules/i2c/isl12xx/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus
+KMOD = isl12xx
+SRCS = isl12xx.c bus_if.h clock_if.h device_if.h iicbus_if.h ofw_bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/i2c/nxprtc/Makefile b/sys/modules/i2c/nxprtc/Makefile
new file mode 100644
index 0000000..d2252b1
--- /dev/null
+++ b/sys/modules/i2c/nxprtc/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus
+KMOD = nxprtc
+SRCS = nxprtc.c bus_if.h clock_if.h device_if.h iicbus_if.h ofw_bus_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/i2c/s35390a/Makefile b/sys/modules/i2c/s35390a/Makefile
new file mode 100644
index 0000000..1e6e93e
--- /dev/null
+++ b/sys/modules/i2c/s35390a/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/dev/iicbus
+KMOD = s35390a
+SRCS = s35390a.c bus_if.h clock_if.h device_if.h iicbus_if.h ofw_bus_if.h
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud