Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 28 Feb 2015 19:02:45 +0000 (UTC)
From:      Luiz Otavio O Souza <loos@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r279399 - in head: share/man/man4 sys/conf sys/dev/iicbus
Message-ID:  <201502281902.t1SJ2jjI028947@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: loos
Date: Sat Feb 28 19:02:44 2015
New Revision: 279399
URL: https://svnweb.freebsd.org/changeset/base/279399

Log:
  Add a driver for the Maxim DS3231 a low-cost, extremely accurate (+-2PPM)
  I2C real-time clock (RTC).
  
  The DS3231 has an integrated temperature-compensated crystal oscillator
  (TXCO) and crystal.
  
  DS3231 has a temperature sensor, an independent 32kHz output (which can be
  turned on and off by the driver) and another output that can be used as
  interrupt for alarms or as a second square-wave output, which frequency and
  operation mode can be set by driver sysctl(8) knobs.
  
  Differential Revision:	https://reviews.freebsd.org/D1016
  Reviewed by:	ian, rpaulo
  Tested on:	Raspberry pi model B

Added:
  head/share/man/man4/ds3231.4   (contents, props changed)
  head/sys/dev/iicbus/ds3231.c   (contents, props changed)
  head/sys/dev/iicbus/ds3231reg.h   (contents, props changed)
Modified:
  head/sys/conf/files

Added: head/share/man/man4/ds3231.4
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/share/man/man4/ds3231.4	Sat Feb 28 19:02:44 2015	(r279399)
@@ -0,0 +1,145 @@
+.\"
+.\" Copyright (c) 2014 Luiz Otavio O Souza <loos@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 ``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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 26, 2014
+.Dt DS3231 4
+.Os
+.Sh NAME
+.Nm ds3231
+.Nd Extremely Accurate i2c-integrated RTC/TCXO/Crystal
+.Sh SYNOPSIS
+.Cd "device iic"
+.Cd "device iicbus"
+.Cd "device ds3231"
+.Sh DESCRIPTION
+The
+.Nm
+is a low-cost, extremely accurate I2C realtime clock (RTC) with an
+integrated temperature-compensated crystal oscillator (TCXO) and crystal.
+.Pp
+The device incorporates a battery input and maintains accurate timekeeping
+when main power to the device is interrupted.
+.Pp
+Access to
+.Nm
+data is made with the
+.Xr sysctl 8
+interface:
+.Bd -literal
+dev.ds3231.0.%desc: Maxim DS3231 RTC
+dev.ds3231.0.%driver: ds3231
+dev.ds3231.0.%location: addr=0xd0
+dev.ds3231.0.%pnpinfo: name=rtc compat=maxim,ds3231
+dev.ds3231.0.%parent: iicbus1
+dev.ds3231.0.temperature: 23.2C
+dev.ds3231.0.temp_conv: 0
+dev.ds3231.0.bbsqw: 0
+dev.ds3231.0.sqw_freq: 8192
+dev.ds3231.0.sqw_mode: interrupt
+dev.ds3231.0.32khz_enable: 1
+.Ed
+.Bl -tag -width ".Va dev.ds3231.%d.temperature"
+.It Va dev.ds3231.%d.temperature
+The read-only value of the current temperature read by the RTC.
+.It Va dev.ds3231.%d.temp_conv
+Start a new temperature convertion.
+When read as 1, a temperature conversion is in progress.
+When read as 0 and then set to 1, a temperature convertion is started.
+The temperature conversion runs automatically on power up and once every 64
+seconds afterward.
+.It Va dev.ds3231.%d.bbsqw
+If set to 1 and
+.Va dev.ds3231.%d.sqw_mode
+is set to square-wave, battery-backed square-wave output is enabled.
+If set to 0, the SQW pin will be set to high impendance when the RTC is
+being powered by battery.
+.It Va dev.ds3231.%d.sqw_freq
+Select the frequency of the SQW pin when the square-wave output is enabled on
+.Va dev.ds3231.%d.sqw_mode .
+It can be set to 1, 1024, 4096, and 8192.
+.It Va dev.ds3231.%d.sqw_mode
+Set the operation mode for the SQW pin.
+It can be set to 'interrupt' (default) or 'square-wave'.
+In interrupt mode, the SQW pin is used to generate interrupts for the RTC
+alarms.
+In square-wave mode, the SQW pin drives a square-wave of
+.Va dev.ds3231.%d.sqw_freq
+frequency.
+.It Va dev.ds3231.%d.32khz_enable
+Enable the 32kHz output.
+.El
+.Pp
+Please check the
+.Nm
+datasheet for more details.
+.Pp
+On a
+.Xr device.hints 5
+based system, such as
+.Li MIPS ,
+these values are configurable for
+.Nm :
+.Bl -tag -width ".Va hint.ds3231.%d.addr"
+.It Va hint.ds3231.%d.at
+The
+.Xr iicbus 4
+that the
+.Nm
+is connected to.
+.It Va hint.ds3231.%d.addr
+The i2c address of
+.Nm .
+.El
+.Pp
+On a
+.Xr FDT 4
+based system the following properties must be set:
+.Bl -tag -width ".Va compatible"
+.It Va compatible
+Must always be set to "maxim,ds3231".
+.It Va reg
+The i2c address of
+.Nm .
+The default address for
+.Nm
+is 0xd0.
+.El
+.Sh SEE ALSO
+.Xr fdt 4 ,
+.Xr iic 4 ,
+.Xr iicbus 4 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Fx 11.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver and this manual page were written by
+.An Luiz Otavio O Souza Aq Mt loos@FreeBSD.org .

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Sat Feb 28 19:01:43 2015	(r279398)
+++ head/sys/conf/files	Sat Feb 28 19:02:44 2015	(r279399)
@@ -1452,6 +1452,7 @@ dev/iicbus/ad7418.c		optional ad7418
 dev/iicbus/ds133x.c		optional ds133x
 dev/iicbus/ds1374.c		optional ds1374
 dev/iicbus/ds1672.c		optional ds1672
+dev/iicbus/ds3231.c		optional ds3231
 dev/iicbus/icee.c		optional icee
 dev/iicbus/if_ic.c		optional ic
 dev/iicbus/iic.c		optional iic

Added: head/sys/dev/iicbus/ds3231.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/iicbus/ds3231.c	Sat Feb 28 19:02:44 2015	(r279399)
@@ -0,0 +1,584 @@
+/*-
+ * Copyright (c) 2014-2015 Luiz Otavio O Souza <loos@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 Maxim DS3231[N] 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 <sys/sysctl.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 <dev/iicbus/ds3231reg.h>
+
+#include "clock_if.h"
+#include "iicbus_if.h"
+
+struct ds3231_softc {
+	device_t	sc_dev;
+	int		sc_last_c;
+	int		sc_year0;
+	struct intr_config_hook	enum_hook;
+	uint16_t	sc_addr;	/* DS3231 slave address. */
+	uint8_t		sc_ctrl;
+	uint8_t		sc_status;
+};
+
+static int ds3231_sqw_freq[] = { 1, 1024, 4096, 8192 };
+
+static void ds3231_start(void *);
+
+static int
+ds3231_read(device_t dev, uint16_t addr, uint8_t reg, uint8_t *data, size_t len)
+{
+	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)));
+}
+
+static int
+ds3231_write(device_t dev, uint16_t addr, uint8_t *data, size_t len)
+{
+	struct iic_msg msg[1] = {
+	    { addr, IIC_M_WR, len, data },
+	};
+
+	return (iicbus_transfer(dev, msg, nitems(msg)));
+}
+
+static int
+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));
+	if (error) {
+		device_printf(sc->sc_dev, "cannot read from RTC.\n");
+		return (error);
+	}
+
+	return (0);
+}
+
+static int
+ds3231_ctrl_write(struct ds3231_softc *sc)
+{
+	int error;
+	uint8_t data[2];
+
+	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));
+	if (error != 0)
+		device_printf(sc->sc_dev, "cannot write to RTC.\n");
+
+	return (error);
+}
+
+static int
+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));
+	if (error) {
+		device_printf(sc->sc_dev, "cannot read from RTC.\n");
+		return (error);
+	}
+
+	return (0);
+}
+
+static int
+ds3231_status_write(struct ds3231_softc *sc, int clear_a1, int clear_a2)
+{
+	int error;
+	uint8_t data[2];
+
+	data[0] = DS3231_STATUS;
+	data[1] = sc->sc_status;
+	if (clear_a1 == 0)
+		data[1] |= 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));
+	if (error != 0)
+		device_printf(sc->sc_dev, "cannot write to RTC.\n");
+
+	return (error);
+}
+
+static int
+ds3231_temp_read(struct ds3231_softc *sc, int *temp)
+{
+	int error, neg, t;
+	uint8_t buf8[2];
+	uint16_t buf;
+
+	error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_TEMP,
+	    buf8, sizeof(buf8));
+	if (error != 0)
+		return (error);
+	buf = (buf8[0] << 8) | (buf8[1] & 0xff);
+	neg = 0;
+	if (buf & DS3231_NEG_BIT) {
+		buf = ~(buf & DS3231_TEMP_MASK) + 1;
+		neg = 1;
+	}
+	*temp = ((int16_t)buf >> 8) * 10;
+	t = 0;
+	if (buf & DS3231_0250C)
+		t += 250;
+	if (buf & DS3231_0500C)
+		t += 500;
+	t /= 100;
+	*temp += t;
+	if (neg)
+		*temp = -(*temp);
+	*temp += TZ_ZEROC;
+
+	return (0);
+}
+
+static int
+ds3231_temp_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	int error, temp;
+	struct ds3231_softc *sc;
+
+	sc = (struct ds3231_softc *)arg1;
+	if (ds3231_temp_read(sc, &temp) != 0)
+		return (EIO);
+	error = sysctl_handle_int(oidp, &temp, 0, req);
+
+	return (error);
+}
+
+static int
+ds3231_conv_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	int error, conv, newc;
+	struct ds3231_softc *sc;
+
+	sc = (struct ds3231_softc *)arg1;
+	error = ds3231_ctrl_read(sc);
+	if (error != 0)
+		return (error);
+	newc = conv = (sc->sc_ctrl & DS3231_CTRL_CONV) ? 1 : 0;
+	error = sysctl_handle_int(oidp, &newc, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	if (conv == 0 && newc != 0) {
+		error = ds3231_status_read(sc);
+		if (error != 0)
+			return (error);
+		if (sc->sc_status & DS3231_STATUS_BUSY)
+			return (0);
+		sc->sc_ctrl |= DS3231_CTRL_CONV;
+		error = ds3231_ctrl_write(sc);
+		if (error != 0)
+			return (error);
+	}
+
+	return (error);
+}
+
+static int
+ds3231_bbsqw_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	int bbsqw, error, newb;
+	struct ds3231_softc *sc;
+
+	sc = (struct ds3231_softc *)arg1;
+	error = ds3231_ctrl_read(sc);
+	if (error != 0)
+		return (error);
+	bbsqw = newb = (sc->sc_ctrl & DS3231_CTRL_BBSQW) ? 1 : 0;
+	error = sysctl_handle_int(oidp, &newb, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	if (bbsqw != newb) {
+		sc->sc_ctrl &= ~DS3231_CTRL_BBSQW;
+		if (newb)
+			sc->sc_ctrl |= DS3231_CTRL_BBSQW;
+		error = ds3231_ctrl_write(sc);
+		if (error != 0)
+			return (error);
+	}
+
+	return (error);
+}
+
+static int
+ds3231_sqw_freq_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	int error, freq, i, newf, tmp;
+	struct ds3231_softc *sc;
+
+	sc = (struct ds3231_softc *)arg1;
+	error = ds3231_ctrl_read(sc);
+	if (error != 0)
+		return (error);
+	tmp = (sc->sc_ctrl & DS3231_CTRL_RS_MASK) >> DS3231_CTRL_RS_SHIFT;
+	if (tmp > nitems(ds3231_sqw_freq))
+		tmp = nitems(ds3231_sqw_freq);
+	freq = ds3231_sqw_freq[tmp];
+	error = sysctl_handle_int(oidp, &freq, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	if (freq != ds3231_sqw_freq[tmp]) {
+		newf = 0;
+		for (i = 0; i < nitems(ds3231_sqw_freq); i++)
+			if (freq >= ds3231_sqw_freq[i])
+				newf = i;
+		sc->sc_ctrl &= ~DS3231_CTRL_RS_MASK;
+		sc->sc_ctrl |= newf << DS3231_CTRL_RS_SHIFT;
+		error = ds3231_ctrl_write(sc);
+		if (error != 0)
+			return (error);
+	}
+
+	return (error);
+}
+
+static int
+ds3231_str_sqw_mode(char *buf)
+{
+	int len, rtrn;
+
+	rtrn = -1;
+	len = strlen(buf);
+	if ((len > 2 && strncasecmp("interrupt", buf, len) == 0) ||
+	    (len > 2 && strncasecmp("int", buf, len) == 0)) {
+		rtrn = 1;
+	} else if ((len > 2 && strncasecmp("square-wave", buf, len) == 0) ||
+	    (len > 2 && strncasecmp("sqw", buf, len) == 0)) {
+		rtrn = 0;
+	}
+
+	return (rtrn);
+}
+
+static int
+ds3231_sqw_mode_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	char buf[16];
+	int error, mode, newm;
+	struct ds3231_softc *sc;
+
+	sc = (struct ds3231_softc *)arg1;
+	error = ds3231_ctrl_read(sc);
+	if (error != 0)
+		return (error);
+	if (sc->sc_ctrl & DS3231_CTRL_INTCN) {
+		mode = 1;
+		strlcpy(buf, "interrupt", sizeof(buf));
+	} else {
+		mode = 0;
+		strlcpy(buf, "square-wave", sizeof(buf));
+	}
+	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	newm = ds3231_str_sqw_mode(buf);
+	if (newm != -1 && mode != newm) {
+		sc->sc_ctrl &= ~DS3231_CTRL_INTCN;
+		if (newm == 1)
+			sc->sc_ctrl |= DS3231_CTRL_INTCN;
+		error = ds3231_ctrl_write(sc);
+		if (error != 0)
+			return (error);
+	}
+
+	return (error);
+}
+
+static int
+ds3231_en32khz_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	int error, en32khz, tmp;
+	struct ds3231_softc *sc;
+
+	sc = (struct ds3231_softc *)arg1;
+	error = ds3231_status_read(sc);
+	if (error != 0)
+		return (error);
+	tmp = en32khz = (sc->sc_status & DS3231_STATUS_EN32KHZ) ? 1 : 0;
+	error = sysctl_handle_int(oidp, &en32khz, 0, req);
+	if (error != 0 || req->newptr == NULL)
+		return (error);
+	if (en32khz != tmp) {
+		sc->sc_status &= ~DS3231_STATUS_EN32KHZ;
+		if (en32khz)
+			sc->sc_status |= DS3231_STATUS_EN32KHZ;
+		error = ds3231_status_write(sc, 0, 0);
+		if (error != 0)
+			return (error);
+	}
+
+	return (error);
+}
+
+static int
+ds3231_probe(device_t dev)
+{
+
+#ifdef FDT
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+	if (!ofw_bus_is_compatible(dev, "maxim,ds3231"))
+		return (ENXIO);
+#endif
+	device_set_desc(dev, "Maxim DS3231 RTC");
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ds3231_attach(device_t dev)
+{
+	struct ds3231_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+	sc->sc_addr = iicbus_get_addr(dev);
+	sc->sc_last_c = -1;
+	sc->sc_year0 = 1900;
+	sc->enum_hook.ich_func = ds3231_start;
+	sc->enum_hook.ich_arg = dev;
+
+	/*
+	 * We have to wait until interrupts are enabled.  Usually 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
+ds3231_start(void *xdev)
+{
+	device_t dev;
+	struct ds3231_softc *sc;
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid *tree_node;
+	struct sysctl_oid_list *tree;
+
+	dev = (device_t)xdev;
+	sc = device_get_softc(dev);
+	ctx = device_get_sysctl_ctx(dev);
+	tree_node = device_get_sysctl_tree(dev);
+	tree = SYSCTL_CHILDREN(tree_node);
+
+	config_intrhook_disestablish(&sc->enum_hook);
+	if (ds3231_ctrl_read(sc) != 0)
+		return;
+	if (ds3231_status_read(sc) != 0)
+		return;
+	/* Clear the OSF bit and ack any pending alarm interrupt. */
+	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;
+	}
+	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",
+	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
+	    ds3231_temp_sysctl, "IK", "Current temperature");
+	/* Configuration parameters. */
+	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temp_conv",
+	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
+	    ds3231_conv_sysctl, "IU",
+	    "DS3231 start a new temperature converstion");
+	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "bbsqw",
+	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
+	    ds3231_bbsqw_sysctl, "IU",
+	    "DS3231 battery-backed square-wave output enable");
+	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_freq",
+	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
+	    ds3231_sqw_freq_sysctl, "IU",
+	    "DS3231 square-wave output frequency");
+	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_mode",
+	    CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_MPSAFE, sc, 0,
+	    ds3231_sqw_mode_sysctl, "A", "DS3231 SQW output mode control");
+	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "32khz_enable",
+	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
+	    ds3231_en32khz_sysctl, "IU", "DS3231 enable the 32kHz output");
+
+	/* 1 second resolution. */
+	clock_register(dev, 1000000);
+}
+
+static int
+ds3231_gettime(device_t dev, struct timespec *ts)
+{
+	int c, error;
+	struct clocktime ct;
+	struct ds3231_softc *sc;
+	uint8_t data[7];
+
+	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 (error != 0) {
+		device_printf(dev, "cannot read from RTC.\n");
+		return (error);
+	}
+	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);
+	c = (data[DS3231_MONTH] & DS3231_C_MASK) ? 1 : 0;
+	if (sc->sc_last_c == -1)
+		sc->sc_last_c = c;
+	else if (c != sc->sc_last_c) {
+		sc->sc_year0 += 100;
+		sc->sc_last_c = c;
+	}
+	ct.year += sc->sc_year0;
+	if (ct.year < POSIX_BASE_YEAR)
+		ct.year += 100;	/* assume [1970, 2069] */
+
+	return (clock_ct_to_ts(&ct, ts));
+}
+
+static int
+ds3231_settime(device_t dev, struct timespec *ts)
+{
+	int error;
+	struct clocktime ct;
+	struct ds3231_softc *sc;
+	uint8_t data[8];
+
+	sc = device_get_softc(dev);
+	/* Accuracy is only one second. */
+	if (ts->tv_nsec >= 500000000)
+		ts->tv_sec++;
+	ts->tv_nsec = 0;
+	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 (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)
+		device_printf(dev, "cannot write to RTC.\n");
+
+	return (error);
+}
+
+static device_method_t ds3231_methods[] = {
+	DEVMETHOD(device_probe,		ds3231_probe),
+	DEVMETHOD(device_attach,	ds3231_attach),
+
+	DEVMETHOD(clock_gettime,	ds3231_gettime),
+	DEVMETHOD(clock_settime,	ds3231_settime),
+
+	DEVMETHOD_END
+};
+
+static driver_t ds3231_driver = {
+	"ds3231",
+	ds3231_methods,
+	sizeof(struct ds3231_softc),
+};
+
+static devclass_t ds3231_devclass;
+
+DRIVER_MODULE(ds3231, iicbus, ds3231_driver, ds3231_devclass, NULL, NULL);
+MODULE_VERSION(ds3231, 1);
+MODULE_DEPEND(ds3231, iicbus, 1, 1, 1);

Added: head/sys/dev/iicbus/ds3231reg.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/iicbus/ds3231reg.h	Sat Feb 28 19:02:44 2015	(r279399)
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2014-2015 Luiz Otavio O Souza <loos@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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Maxim DS3231 RTC registers.
+ */
+
+#ifndef _DS3231REG_H_
+#define _DS3231REG_H_
+
+#define	DS3231_SECS		0x00
+#define	DS3231_SECS_MASK		0x7f
+#define	DS3231_MINS		0x01
+#define	DS3231_MINS_MASK		0x7f
+#define	DS3231_HOUR		0x02
+#define	DS3231_HOUR_MASK		0x3f
+#define	DS3231_WEEKDAY		0x03
+#define	DS3231_WEEKDAY_MASK		0x07
+#define	DS3231_DATE		0x04
+#define	DS3231_DATE_MASK		0x3f
+#define	DS3231_MONTH		0x05
+#define	DS3231_MONTH_MASK		0x1f
+#define	DS3231_C_MASK			0x80
+#define	DS3231_YEAR		0x06
+#define	DS3231_YEAR_MASK		0xff
+#define	DS3231_CONTROL		0x0e
+#define	DS3231_CTRL_EOSC		(1 << 7)
+#define	DS3231_CTRL_BBSQW		(1 << 6)
+#define	DS3231_CTRL_CONV		(1 << 5)
+#define	DS3231_CTRL_RS2			(1 << 4)
+#define	DS3231_CTRL_RS1			(1 << 3)
+#define	DS3231_CTRL_RS_MASK		(DS3231_CTRL_RS2 | DS3231_CTRL_RS1)
+#define	DS3231_CTRL_RS_SHIFT		3
+#define	DS3231_CTRL_INTCN		(1 << 2)
+#define	DS3231_CTRL_A2IE		(1 << 1)
+#define	DS3231_CTRL_A1IE		(1 << 0)
+#define	DS3231_CTRL_MASK		\
+	(DS3231_CTRL_EOSC | DS3231_CTRL_A1IE | DS3231_CTRL_A2IE)
+#define	DS3231_STATUS		0x0f
+#define	DS3231_STATUS_OSF		(1 << 7)
+#define	DS3231_STATUS_EN32KHZ		(1 << 3)
+#define	DS3231_STATUS_BUSY		(1 << 2)
+#define	DS3231_STATUS_A2F		(1 << 1)
+#define	DS3231_STATUS_A1F		(1 << 0)
+#define	DS3231_TEMP		0x11
+#define	DS3231_TEMP_MASK		0xffc0
+#define	DS3231_0500C			0x80
+#define	DS3231_0250C			0x40
+#define	DS3231_MSB			0x8000
+#define	DS3231_NEG_BIT			DS3231_MSB
+#define	TZ_ZEROC			2732
+
+#endif	/* _DS3231REG_H_ */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201502281902.t1SJ2jjI028947>