ds3231.c revision 279399
1279399Sloos/*-
2279399Sloos * Copyright (c) 2014-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
3279399Sloos * All rights reserved.
4279399Sloos *
5279399Sloos * Redistribution and use in source and binary forms, with or without
6279399Sloos * modification, are permitted provided that the following conditions
7279399Sloos * are met:
8279399Sloos * 1. Redistributions of source code must retain the above copyright
9279399Sloos *    notice, this list of conditions and the following disclaimer.
10279399Sloos * 2. Redistributions in binary form must reproduce the above copyright
11279399Sloos *    notice, this list of conditions and the following disclaimer in the
12279399Sloos *    documentation and/or other materials provided with the distribution.
13279399Sloos *
14279399Sloos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15279399Sloos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16279399Sloos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17279399Sloos * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18279399Sloos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19279399Sloos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20279399Sloos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21279399Sloos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22279399Sloos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23279399Sloos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24279399Sloos * SUCH DAMAGE.
25279399Sloos */
26279399Sloos
27279399Sloos#include <sys/cdefs.h>
28279399Sloos__FBSDID("$FreeBSD: head/sys/dev/iicbus/ds3231.c 279399 2015-02-28 19:02:44Z loos $");
29279399Sloos
30279399Sloos/*
31279399Sloos * Driver for Maxim DS3231[N] real-time clock/calendar.
32279399Sloos */
33279399Sloos
34279399Sloos#include "opt_platform.h"
35279399Sloos
36279399Sloos#include <sys/param.h>
37279399Sloos#include <sys/systm.h>
38279399Sloos#include <sys/bus.h>
39279399Sloos#include <sys/clock.h>
40279399Sloos#include <sys/kernel.h>
41279399Sloos#include <sys/module.h>
42279399Sloos#include <sys/sysctl.h>
43279399Sloos
44279399Sloos#include <dev/iicbus/iicbus.h>
45279399Sloos#include <dev/iicbus/iiconf.h>
46279399Sloos#ifdef FDT
47279399Sloos#include <dev/ofw/openfirm.h>
48279399Sloos#include <dev/ofw/ofw_bus.h>
49279399Sloos#include <dev/ofw/ofw_bus_subr.h>
50279399Sloos#endif
51279399Sloos
52279399Sloos#include <dev/iicbus/ds3231reg.h>
53279399Sloos
54279399Sloos#include "clock_if.h"
55279399Sloos#include "iicbus_if.h"
56279399Sloos
57279399Sloosstruct ds3231_softc {
58279399Sloos	device_t	sc_dev;
59279399Sloos	int		sc_last_c;
60279399Sloos	int		sc_year0;
61279399Sloos	struct intr_config_hook	enum_hook;
62279399Sloos	uint16_t	sc_addr;	/* DS3231 slave address. */
63279399Sloos	uint8_t		sc_ctrl;
64279399Sloos	uint8_t		sc_status;
65279399Sloos};
66279399Sloos
67279399Sloosstatic int ds3231_sqw_freq[] = { 1, 1024, 4096, 8192 };
68279399Sloos
69279399Sloosstatic void ds3231_start(void *);
70279399Sloos
71279399Sloosstatic int
72279399Sloosds3231_read(device_t dev, uint16_t addr, uint8_t reg, uint8_t *data, size_t len)
73279399Sloos{
74279399Sloos	struct iic_msg msg[2] = {
75279399Sloos	    { addr, IIC_M_WR | IIC_M_NOSTOP, 1, &reg },
76279399Sloos	    { addr, IIC_M_RD, len, data },
77279399Sloos	};
78279399Sloos
79279399Sloos	return (iicbus_transfer(dev, msg, nitems(msg)));
80279399Sloos}
81279399Sloos
82279399Sloosstatic int
83279399Sloosds3231_write(device_t dev, uint16_t addr, uint8_t *data, size_t len)
84279399Sloos{
85279399Sloos	struct iic_msg msg[1] = {
86279399Sloos	    { addr, IIC_M_WR, len, data },
87279399Sloos	};
88279399Sloos
89279399Sloos	return (iicbus_transfer(dev, msg, nitems(msg)));
90279399Sloos}
91279399Sloos
92279399Sloosstatic int
93279399Sloosds3231_ctrl_read(struct ds3231_softc *sc)
94279399Sloos{
95279399Sloos	int error;
96279399Sloos
97279399Sloos	sc->sc_ctrl = 0;
98279399Sloos	error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_CONTROL,
99279399Sloos	    &sc->sc_ctrl, sizeof(sc->sc_ctrl));
100279399Sloos	if (error) {
101279399Sloos		device_printf(sc->sc_dev, "cannot read from RTC.\n");
102279399Sloos		return (error);
103279399Sloos	}
104279399Sloos
105279399Sloos	return (0);
106279399Sloos}
107279399Sloos
108279399Sloosstatic int
109279399Sloosds3231_ctrl_write(struct ds3231_softc *sc)
110279399Sloos{
111279399Sloos	int error;
112279399Sloos	uint8_t data[2];
113279399Sloos
114279399Sloos	data[0] = DS3231_CONTROL;
115279399Sloos	/* Always enable the oscillator.  Always disable both alarms. */
116279399Sloos	data[1] = sc->sc_ctrl & ~DS3231_CTRL_MASK;
117279399Sloos	error = ds3231_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
118279399Sloos	if (error != 0)
119279399Sloos		device_printf(sc->sc_dev, "cannot write to RTC.\n");
120279399Sloos
121279399Sloos	return (error);
122279399Sloos}
123279399Sloos
124279399Sloosstatic int
125279399Sloosds3231_status_read(struct ds3231_softc *sc)
126279399Sloos{
127279399Sloos	int error;
128279399Sloos
129279399Sloos	sc->sc_status = 0;
130279399Sloos	error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_STATUS,
131279399Sloos	    &sc->sc_status, sizeof(sc->sc_status));
132279399Sloos	if (error) {
133279399Sloos		device_printf(sc->sc_dev, "cannot read from RTC.\n");
134279399Sloos		return (error);
135279399Sloos	}
136279399Sloos
137279399Sloos	return (0);
138279399Sloos}
139279399Sloos
140279399Sloosstatic int
141279399Sloosds3231_status_write(struct ds3231_softc *sc, int clear_a1, int clear_a2)
142279399Sloos{
143279399Sloos	int error;
144279399Sloos	uint8_t data[2];
145279399Sloos
146279399Sloos	data[0] = DS3231_STATUS;
147279399Sloos	data[1] = sc->sc_status;
148279399Sloos	if (clear_a1 == 0)
149279399Sloos		data[1] |= DS3231_STATUS_A1F;
150279399Sloos	if (clear_a2 == 0)
151279399Sloos		data[1] |= DS3231_STATUS_A2F;
152279399Sloos	error = ds3231_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
153279399Sloos	if (error != 0)
154279399Sloos		device_printf(sc->sc_dev, "cannot write to RTC.\n");
155279399Sloos
156279399Sloos	return (error);
157279399Sloos}
158279399Sloos
159279399Sloosstatic int
160279399Sloosds3231_set_24hrs_mode(struct ds3231_softc *sc)
161279399Sloos{
162279399Sloos	int error;
163279399Sloos	uint8_t data[2], hour;
164279399Sloos
165279399Sloos	hour = 0;
166279399Sloos	error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_HOUR,
167279399Sloos	    &hour, sizeof(hour));
168279399Sloos	if (error) {
169279399Sloos		device_printf(sc->sc_dev, "cannot read from RTC.\n");
170279399Sloos		return (error);
171279399Sloos	}
172279399Sloos	data[0] = DS3231_HOUR;
173279399Sloos	data[1] = hour & ~DS3231_C_MASK;
174279399Sloos	error = ds3231_write(sc->sc_dev, sc->sc_addr, data, sizeof(data));
175279399Sloos	if (error != 0)
176279399Sloos		device_printf(sc->sc_dev, "cannot write to RTC.\n");
177279399Sloos
178279399Sloos	return (error);
179279399Sloos}
180279399Sloos
181279399Sloosstatic int
182279399Sloosds3231_temp_read(struct ds3231_softc *sc, int *temp)
183279399Sloos{
184279399Sloos	int error, neg, t;
185279399Sloos	uint8_t buf8[2];
186279399Sloos	uint16_t buf;
187279399Sloos
188279399Sloos	error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_TEMP,
189279399Sloos	    buf8, sizeof(buf8));
190279399Sloos	if (error != 0)
191279399Sloos		return (error);
192279399Sloos	buf = (buf8[0] << 8) | (buf8[1] & 0xff);
193279399Sloos	neg = 0;
194279399Sloos	if (buf & DS3231_NEG_BIT) {
195279399Sloos		buf = ~(buf & DS3231_TEMP_MASK) + 1;
196279399Sloos		neg = 1;
197279399Sloos	}
198279399Sloos	*temp = ((int16_t)buf >> 8) * 10;
199279399Sloos	t = 0;
200279399Sloos	if (buf & DS3231_0250C)
201279399Sloos		t += 250;
202279399Sloos	if (buf & DS3231_0500C)
203279399Sloos		t += 500;
204279399Sloos	t /= 100;
205279399Sloos	*temp += t;
206279399Sloos	if (neg)
207279399Sloos		*temp = -(*temp);
208279399Sloos	*temp += TZ_ZEROC;
209279399Sloos
210279399Sloos	return (0);
211279399Sloos}
212279399Sloos
213279399Sloosstatic int
214279399Sloosds3231_temp_sysctl(SYSCTL_HANDLER_ARGS)
215279399Sloos{
216279399Sloos	int error, temp;
217279399Sloos	struct ds3231_softc *sc;
218279399Sloos
219279399Sloos	sc = (struct ds3231_softc *)arg1;
220279399Sloos	if (ds3231_temp_read(sc, &temp) != 0)
221279399Sloos		return (EIO);
222279399Sloos	error = sysctl_handle_int(oidp, &temp, 0, req);
223279399Sloos
224279399Sloos	return (error);
225279399Sloos}
226279399Sloos
227279399Sloosstatic int
228279399Sloosds3231_conv_sysctl(SYSCTL_HANDLER_ARGS)
229279399Sloos{
230279399Sloos	int error, conv, newc;
231279399Sloos	struct ds3231_softc *sc;
232279399Sloos
233279399Sloos	sc = (struct ds3231_softc *)arg1;
234279399Sloos	error = ds3231_ctrl_read(sc);
235279399Sloos	if (error != 0)
236279399Sloos		return (error);
237279399Sloos	newc = conv = (sc->sc_ctrl & DS3231_CTRL_CONV) ? 1 : 0;
238279399Sloos	error = sysctl_handle_int(oidp, &newc, 0, req);
239279399Sloos	if (error != 0 || req->newptr == NULL)
240279399Sloos		return (error);
241279399Sloos	if (conv == 0 && newc != 0) {
242279399Sloos		error = ds3231_status_read(sc);
243279399Sloos		if (error != 0)
244279399Sloos			return (error);
245279399Sloos		if (sc->sc_status & DS3231_STATUS_BUSY)
246279399Sloos			return (0);
247279399Sloos		sc->sc_ctrl |= DS3231_CTRL_CONV;
248279399Sloos		error = ds3231_ctrl_write(sc);
249279399Sloos		if (error != 0)
250279399Sloos			return (error);
251279399Sloos	}
252279399Sloos
253279399Sloos	return (error);
254279399Sloos}
255279399Sloos
256279399Sloosstatic int
257279399Sloosds3231_bbsqw_sysctl(SYSCTL_HANDLER_ARGS)
258279399Sloos{
259279399Sloos	int bbsqw, error, newb;
260279399Sloos	struct ds3231_softc *sc;
261279399Sloos
262279399Sloos	sc = (struct ds3231_softc *)arg1;
263279399Sloos	error = ds3231_ctrl_read(sc);
264279399Sloos	if (error != 0)
265279399Sloos		return (error);
266279399Sloos	bbsqw = newb = (sc->sc_ctrl & DS3231_CTRL_BBSQW) ? 1 : 0;
267279399Sloos	error = sysctl_handle_int(oidp, &newb, 0, req);
268279399Sloos	if (error != 0 || req->newptr == NULL)
269279399Sloos		return (error);
270279399Sloos	if (bbsqw != newb) {
271279399Sloos		sc->sc_ctrl &= ~DS3231_CTRL_BBSQW;
272279399Sloos		if (newb)
273279399Sloos			sc->sc_ctrl |= DS3231_CTRL_BBSQW;
274279399Sloos		error = ds3231_ctrl_write(sc);
275279399Sloos		if (error != 0)
276279399Sloos			return (error);
277279399Sloos	}
278279399Sloos
279279399Sloos	return (error);
280279399Sloos}
281279399Sloos
282279399Sloosstatic int
283279399Sloosds3231_sqw_freq_sysctl(SYSCTL_HANDLER_ARGS)
284279399Sloos{
285279399Sloos	int error, freq, i, newf, tmp;
286279399Sloos	struct ds3231_softc *sc;
287279399Sloos
288279399Sloos	sc = (struct ds3231_softc *)arg1;
289279399Sloos	error = ds3231_ctrl_read(sc);
290279399Sloos	if (error != 0)
291279399Sloos		return (error);
292279399Sloos	tmp = (sc->sc_ctrl & DS3231_CTRL_RS_MASK) >> DS3231_CTRL_RS_SHIFT;
293279399Sloos	if (tmp > nitems(ds3231_sqw_freq))
294279399Sloos		tmp = nitems(ds3231_sqw_freq);
295279399Sloos	freq = ds3231_sqw_freq[tmp];
296279399Sloos	error = sysctl_handle_int(oidp, &freq, 0, req);
297279399Sloos	if (error != 0 || req->newptr == NULL)
298279399Sloos		return (error);
299279399Sloos	if (freq != ds3231_sqw_freq[tmp]) {
300279399Sloos		newf = 0;
301279399Sloos		for (i = 0; i < nitems(ds3231_sqw_freq); i++)
302279399Sloos			if (freq >= ds3231_sqw_freq[i])
303279399Sloos				newf = i;
304279399Sloos		sc->sc_ctrl &= ~DS3231_CTRL_RS_MASK;
305279399Sloos		sc->sc_ctrl |= newf << DS3231_CTRL_RS_SHIFT;
306279399Sloos		error = ds3231_ctrl_write(sc);
307279399Sloos		if (error != 0)
308279399Sloos			return (error);
309279399Sloos	}
310279399Sloos
311279399Sloos	return (error);
312279399Sloos}
313279399Sloos
314279399Sloosstatic int
315279399Sloosds3231_str_sqw_mode(char *buf)
316279399Sloos{
317279399Sloos	int len, rtrn;
318279399Sloos
319279399Sloos	rtrn = -1;
320279399Sloos	len = strlen(buf);
321279399Sloos	if ((len > 2 && strncasecmp("interrupt", buf, len) == 0) ||
322279399Sloos	    (len > 2 && strncasecmp("int", buf, len) == 0)) {
323279399Sloos		rtrn = 1;
324279399Sloos	} else if ((len > 2 && strncasecmp("square-wave", buf, len) == 0) ||
325279399Sloos	    (len > 2 && strncasecmp("sqw", buf, len) == 0)) {
326279399Sloos		rtrn = 0;
327279399Sloos	}
328279399Sloos
329279399Sloos	return (rtrn);
330279399Sloos}
331279399Sloos
332279399Sloosstatic int
333279399Sloosds3231_sqw_mode_sysctl(SYSCTL_HANDLER_ARGS)
334279399Sloos{
335279399Sloos	char buf[16];
336279399Sloos	int error, mode, newm;
337279399Sloos	struct ds3231_softc *sc;
338279399Sloos
339279399Sloos	sc = (struct ds3231_softc *)arg1;
340279399Sloos	error = ds3231_ctrl_read(sc);
341279399Sloos	if (error != 0)
342279399Sloos		return (error);
343279399Sloos	if (sc->sc_ctrl & DS3231_CTRL_INTCN) {
344279399Sloos		mode = 1;
345279399Sloos		strlcpy(buf, "interrupt", sizeof(buf));
346279399Sloos	} else {
347279399Sloos		mode = 0;
348279399Sloos		strlcpy(buf, "square-wave", sizeof(buf));
349279399Sloos	}
350279399Sloos	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
351279399Sloos	if (error != 0 || req->newptr == NULL)
352279399Sloos		return (error);
353279399Sloos	newm = ds3231_str_sqw_mode(buf);
354279399Sloos	if (newm != -1 && mode != newm) {
355279399Sloos		sc->sc_ctrl &= ~DS3231_CTRL_INTCN;
356279399Sloos		if (newm == 1)
357279399Sloos			sc->sc_ctrl |= DS3231_CTRL_INTCN;
358279399Sloos		error = ds3231_ctrl_write(sc);
359279399Sloos		if (error != 0)
360279399Sloos			return (error);
361279399Sloos	}
362279399Sloos
363279399Sloos	return (error);
364279399Sloos}
365279399Sloos
366279399Sloosstatic int
367279399Sloosds3231_en32khz_sysctl(SYSCTL_HANDLER_ARGS)
368279399Sloos{
369279399Sloos	int error, en32khz, tmp;
370279399Sloos	struct ds3231_softc *sc;
371279399Sloos
372279399Sloos	sc = (struct ds3231_softc *)arg1;
373279399Sloos	error = ds3231_status_read(sc);
374279399Sloos	if (error != 0)
375279399Sloos		return (error);
376279399Sloos	tmp = en32khz = (sc->sc_status & DS3231_STATUS_EN32KHZ) ? 1 : 0;
377279399Sloos	error = sysctl_handle_int(oidp, &en32khz, 0, req);
378279399Sloos	if (error != 0 || req->newptr == NULL)
379279399Sloos		return (error);
380279399Sloos	if (en32khz != tmp) {
381279399Sloos		sc->sc_status &= ~DS3231_STATUS_EN32KHZ;
382279399Sloos		if (en32khz)
383279399Sloos			sc->sc_status |= DS3231_STATUS_EN32KHZ;
384279399Sloos		error = ds3231_status_write(sc, 0, 0);
385279399Sloos		if (error != 0)
386279399Sloos			return (error);
387279399Sloos	}
388279399Sloos
389279399Sloos	return (error);
390279399Sloos}
391279399Sloos
392279399Sloosstatic int
393279399Sloosds3231_probe(device_t dev)
394279399Sloos{
395279399Sloos
396279399Sloos#ifdef FDT
397279399Sloos	if (!ofw_bus_status_okay(dev))
398279399Sloos		return (ENXIO);
399279399Sloos	if (!ofw_bus_is_compatible(dev, "maxim,ds3231"))
400279399Sloos		return (ENXIO);
401279399Sloos#endif
402279399Sloos	device_set_desc(dev, "Maxim DS3231 RTC");
403279399Sloos
404279399Sloos	return (BUS_PROBE_DEFAULT);
405279399Sloos}
406279399Sloos
407279399Sloosstatic int
408279399Sloosds3231_attach(device_t dev)
409279399Sloos{
410279399Sloos	struct ds3231_softc *sc;
411279399Sloos
412279399Sloos	sc = device_get_softc(dev);
413279399Sloos	sc->sc_dev = dev;
414279399Sloos	sc->sc_addr = iicbus_get_addr(dev);
415279399Sloos	sc->sc_last_c = -1;
416279399Sloos	sc->sc_year0 = 1900;
417279399Sloos	sc->enum_hook.ich_func = ds3231_start;
418279399Sloos	sc->enum_hook.ich_arg = dev;
419279399Sloos
420279399Sloos	/*
421279399Sloos	 * We have to wait until interrupts are enabled.  Usually I2C read
422279399Sloos	 * and write only works when the interrupts are available.
423279399Sloos	 */
424279399Sloos	if (config_intrhook_establish(&sc->enum_hook) != 0)
425279399Sloos		return (ENOMEM);
426279399Sloos
427279399Sloos	return (0);
428279399Sloos}
429279399Sloos
430279399Sloosstatic void
431279399Sloosds3231_start(void *xdev)
432279399Sloos{
433279399Sloos	device_t dev;
434279399Sloos	struct ds3231_softc *sc;
435279399Sloos	struct sysctl_ctx_list *ctx;
436279399Sloos	struct sysctl_oid *tree_node;
437279399Sloos	struct sysctl_oid_list *tree;
438279399Sloos
439279399Sloos	dev = (device_t)xdev;
440279399Sloos	sc = device_get_softc(dev);
441279399Sloos	ctx = device_get_sysctl_ctx(dev);
442279399Sloos	tree_node = device_get_sysctl_tree(dev);
443279399Sloos	tree = SYSCTL_CHILDREN(tree_node);
444279399Sloos
445279399Sloos	config_intrhook_disestablish(&sc->enum_hook);
446279399Sloos	if (ds3231_ctrl_read(sc) != 0)
447279399Sloos		return;
448279399Sloos	if (ds3231_status_read(sc) != 0)
449279399Sloos		return;
450279399Sloos	/* Clear the OSF bit and ack any pending alarm interrupt. */
451279399Sloos	if (sc->sc_status & DS3231_STATUS_OSF) {
452279399Sloos		device_printf(sc->sc_dev,
453279399Sloos		    "oscillator has stopped, check the battery.\n");
454279399Sloos		sc->sc_status &= ~DS3231_STATUS_OSF;
455279399Sloos	}
456279399Sloos	if (ds3231_status_write(sc, 1, 1) != 0)
457279399Sloos		return;
458279399Sloos	/* Always enable the oscillator. */
459279399Sloos	if (ds3231_ctrl_write(sc) != 0)
460279399Sloos		return;
461279399Sloos	/* Set the 24 hours mode. */
462279399Sloos	if (ds3231_set_24hrs_mode(sc) != 0)
463279399Sloos		return;
464279399Sloos
465279399Sloos	/* Temperature. */
466279399Sloos	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temperature",
467279399Sloos	    CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_MPSAFE, sc, 0,
468279399Sloos	    ds3231_temp_sysctl, "IK", "Current temperature");
469279399Sloos	/* Configuration parameters. */
470279399Sloos	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "temp_conv",
471279399Sloos	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
472279399Sloos	    ds3231_conv_sysctl, "IU",
473279399Sloos	    "DS3231 start a new temperature converstion");
474279399Sloos	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "bbsqw",
475279399Sloos	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
476279399Sloos	    ds3231_bbsqw_sysctl, "IU",
477279399Sloos	    "DS3231 battery-backed square-wave output enable");
478279399Sloos	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_freq",
479279399Sloos	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
480279399Sloos	    ds3231_sqw_freq_sysctl, "IU",
481279399Sloos	    "DS3231 square-wave output frequency");
482279399Sloos	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_mode",
483279399Sloos	    CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_MPSAFE, sc, 0,
484279399Sloos	    ds3231_sqw_mode_sysctl, "A", "DS3231 SQW output mode control");
485279399Sloos	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "32khz_enable",
486279399Sloos	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0,
487279399Sloos	    ds3231_en32khz_sysctl, "IU", "DS3231 enable the 32kHz output");
488279399Sloos
489279399Sloos	/* 1 second resolution. */
490279399Sloos	clock_register(dev, 1000000);
491279399Sloos}
492279399Sloos
493279399Sloosstatic int
494279399Sloosds3231_gettime(device_t dev, struct timespec *ts)
495279399Sloos{
496279399Sloos	int c, error;
497279399Sloos	struct clocktime ct;
498279399Sloos	struct ds3231_softc *sc;
499279399Sloos	uint8_t data[7];
500279399Sloos
501279399Sloos	sc = device_get_softc(dev);
502279399Sloos	memset(data, 0, sizeof(data));
503279399Sloos	error = ds3231_read(sc->sc_dev, sc->sc_addr, DS3231_SECS,
504279399Sloos	    data, sizeof(data));
505279399Sloos	if (error != 0) {
506279399Sloos		device_printf(dev, "cannot read from RTC.\n");
507279399Sloos		return (error);
508279399Sloos	}
509279399Sloos	ct.nsec = 0;
510279399Sloos	ct.sec = FROMBCD(data[DS3231_SECS] & DS3231_SECS_MASK);
511279399Sloos	ct.min = FROMBCD(data[DS3231_MINS] & DS3231_MINS_MASK);
512279399Sloos	ct.hour = FROMBCD(data[DS3231_HOUR] & DS3231_HOUR_MASK);
513279399Sloos	ct.day = FROMBCD(data[DS3231_DATE] & DS3231_DATE_MASK);
514279399Sloos	ct.dow = data[DS3231_WEEKDAY] & DS3231_WEEKDAY_MASK;
515279399Sloos	ct.mon = FROMBCD(data[DS3231_MONTH] & DS3231_MONTH_MASK);
516279399Sloos	ct.year = FROMBCD(data[DS3231_YEAR] & DS3231_YEAR_MASK);
517279399Sloos	c = (data[DS3231_MONTH] & DS3231_C_MASK) ? 1 : 0;
518279399Sloos	if (sc->sc_last_c == -1)
519279399Sloos		sc->sc_last_c = c;
520279399Sloos	else if (c != sc->sc_last_c) {
521279399Sloos		sc->sc_year0 += 100;
522279399Sloos		sc->sc_last_c = c;
523279399Sloos	}
524279399Sloos	ct.year += sc->sc_year0;
525279399Sloos	if (ct.year < POSIX_BASE_YEAR)
526279399Sloos		ct.year += 100;	/* assume [1970, 2069] */
527279399Sloos
528279399Sloos	return (clock_ct_to_ts(&ct, ts));
529279399Sloos}
530279399Sloos
531279399Sloosstatic int
532279399Sloosds3231_settime(device_t dev, struct timespec *ts)
533279399Sloos{
534279399Sloos	int error;
535279399Sloos	struct clocktime ct;
536279399Sloos	struct ds3231_softc *sc;
537279399Sloos	uint8_t data[8];
538279399Sloos
539279399Sloos	sc = device_get_softc(dev);
540279399Sloos	/* Accuracy is only one second. */
541279399Sloos	if (ts->tv_nsec >= 500000000)
542279399Sloos		ts->tv_sec++;
543279399Sloos	ts->tv_nsec = 0;
544279399Sloos	clock_ts_to_ct(ts, &ct);
545279399Sloos	memset(data, 0, sizeof(data));
546279399Sloos	data[0] = DS3231_SECS;
547279399Sloos	data[DS3231_SECS + 1] = TOBCD(ct.sec);
548279399Sloos	data[DS3231_MINS + 1] = TOBCD(ct.min);
549279399Sloos	data[DS3231_HOUR + 1] = TOBCD(ct.hour);
550279399Sloos	data[DS3231_DATE + 1] = TOBCD(ct.day);
551279399Sloos	data[DS3231_WEEKDAY + 1] = ct.dow;
552279399Sloos	data[DS3231_MONTH + 1] = TOBCD(ct.mon);
553279399Sloos	data[DS3231_YEAR + 1] = TOBCD(ct.year % 100);
554279399Sloos	if (sc->sc_last_c)
555279399Sloos		data[DS3231_MONTH] |= DS3231_C_MASK;
556279399Sloos	/* Write the time back to RTC. */
557279399Sloos	error = ds3231_write(dev, sc->sc_addr, data, sizeof(data));
558279399Sloos	if (error != 0)
559279399Sloos		device_printf(dev, "cannot write to RTC.\n");
560279399Sloos
561279399Sloos	return (error);
562279399Sloos}
563279399Sloos
564279399Sloosstatic device_method_t ds3231_methods[] = {
565279399Sloos	DEVMETHOD(device_probe,		ds3231_probe),
566279399Sloos	DEVMETHOD(device_attach,	ds3231_attach),
567279399Sloos
568279399Sloos	DEVMETHOD(clock_gettime,	ds3231_gettime),
569279399Sloos	DEVMETHOD(clock_settime,	ds3231_settime),
570279399Sloos
571279399Sloos	DEVMETHOD_END
572279399Sloos};
573279399Sloos
574279399Sloosstatic driver_t ds3231_driver = {
575279399Sloos	"ds3231",
576279399Sloos	ds3231_methods,
577279399Sloos	sizeof(struct ds3231_softc),
578279399Sloos};
579279399Sloos
580279399Sloosstatic devclass_t ds3231_devclass;
581279399Sloos
582279399SloosDRIVER_MODULE(ds3231, iicbus, ds3231_driver, ds3231_devclass, NULL, NULL);
583279399SloosMODULE_VERSION(ds3231, 1);
584279399SloosMODULE_DEPEND(ds3231, iicbus, 1, 1, 1);
585