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, ® }, 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