1/*- 2 * Copyright (c) 2015 Luiz Otavio O Souza <loos@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD$"); 29 30/* 31 * Driver for Maxim DS1307 I2C real-time clock/calendar. 32 */ 33 34#include "opt_platform.h" 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/clock.h> 40#include <sys/kernel.h> 41#include <sys/module.h> 42#include <sys/sysctl.h> 43 44#include <dev/iicbus/iicbus.h> 45#include <dev/iicbus/iiconf.h> 46#ifdef FDT 47#include <dev/ofw/openfirm.h> 48#include <dev/ofw/ofw_bus.h> 49#include <dev/ofw/ofw_bus_subr.h> 50#endif 51 52#include <dev/iicbus/ds1307reg.h> 53 54#include "clock_if.h" 55#include "iicbus_if.h" 56 57struct ds1307_softc { 58 device_t sc_dev; 59 struct intr_config_hook 60 enum_hook; 61 uint8_t sc_ctrl; 62 bool sc_mcp7941x; 63 bool sc_use_ampm; 64}; 65 66static void ds1307_start(void *); 67 68#ifdef FDT 69static const struct ofw_compat_data ds1307_compat_data[] = { 70 {"dallas,ds1307", (uintptr_t)"Dallas DS1307 RTC"}, 71 {"maxim,ds1307", (uintptr_t)"Maxim DS1307 RTC"}, 72 {"microchip,mcp7941x", (uintptr_t)"Microchip MCP7941x RTC"}, 73 { NULL, 0 } 74}; 75#endif 76 77static int 78ds1307_read1(device_t dev, uint8_t reg, uint8_t *data) 79{ 80 81 return (iicdev_readfrom(dev, reg, data, 1, IIC_INTRWAIT)); 82} 83 84static int 85ds1307_write1(device_t dev, uint8_t reg, uint8_t data) 86{ 87 88 return (iicdev_writeto(dev, reg, &data, 1, IIC_INTRWAIT)); 89} 90 91static int 92ds1307_ctrl_read(struct ds1307_softc *sc) 93{ 94 int error; 95 96 sc->sc_ctrl = 0; 97 error = ds1307_read1(sc->sc_dev, DS1307_CONTROL, &sc->sc_ctrl); 98 if (error) { 99 device_printf(sc->sc_dev, "cannot read from RTC.\n"); 100 return (error); 101 } 102 103 return (0); 104} 105 106static int 107ds1307_ctrl_write(struct ds1307_softc *sc) 108{ 109 int error; 110 uint8_t ctrl; 111 112 ctrl = sc->sc_ctrl & DS1307_CTRL_MASK; 113 error = ds1307_write1(sc->sc_dev, DS1307_CONTROL, ctrl); 114 if (error != 0) 115 device_printf(sc->sc_dev, "cannot write to RTC.\n"); 116 117 return (error); 118} 119 120static int 121ds1307_sqwe_sysctl(SYSCTL_HANDLER_ARGS) 122{ 123 int sqwe, error, newv, sqwe_bit; 124 struct ds1307_softc *sc; 125 126 sc = (struct ds1307_softc *)arg1; 127 error = ds1307_ctrl_read(sc); 128 if (error != 0) 129 return (error); 130 if (sc->sc_mcp7941x) 131 sqwe_bit = MCP7941X_CTRL_SQWE; 132 else 133 sqwe_bit = DS1307_CTRL_SQWE; 134 sqwe = newv = (sc->sc_ctrl & sqwe_bit) ? 1 : 0; 135 error = sysctl_handle_int(oidp, &newv, 0, req); 136 if (error != 0 || req->newptr == NULL) 137 return (error); 138 if (sqwe != newv) { 139 sc->sc_ctrl &= ~sqwe_bit; 140 if (newv) 141 sc->sc_ctrl |= sqwe_bit; 142 error = ds1307_ctrl_write(sc); 143 if (error != 0) 144 return (error); 145 } 146 147 return (error); 148} 149 150static int 151ds1307_sqw_freq_sysctl(SYSCTL_HANDLER_ARGS) 152{ 153 int ds1307_sqw_freq[] = { 1, 4096, 8192, 32768 }; 154 int error, freq, i, newf, tmp; 155 struct ds1307_softc *sc; 156 157 sc = (struct ds1307_softc *)arg1; 158 error = ds1307_ctrl_read(sc); 159 if (error != 0) 160 return (error); 161 tmp = (sc->sc_ctrl & DS1307_CTRL_RS_MASK); 162 if (tmp >= nitems(ds1307_sqw_freq)) 163 tmp = nitems(ds1307_sqw_freq) - 1; 164 freq = ds1307_sqw_freq[tmp]; 165 error = sysctl_handle_int(oidp, &freq, 0, req); 166 if (error != 0 || req->newptr == NULL) 167 return (error); 168 if (freq != ds1307_sqw_freq[tmp]) { 169 newf = 0; 170 for (i = 0; i < nitems(ds1307_sqw_freq); i++) 171 if (freq >= ds1307_sqw_freq[i]) 172 newf = i; 173 sc->sc_ctrl &= ~DS1307_CTRL_RS_MASK; 174 sc->sc_ctrl |= newf; 175 error = ds1307_ctrl_write(sc); 176 if (error != 0) 177 return (error); 178 } 179 180 return (error); 181} 182 183static int 184ds1307_sqw_out_sysctl(SYSCTL_HANDLER_ARGS) 185{ 186 int sqwe, error, newv; 187 struct ds1307_softc *sc; 188 189 sc = (struct ds1307_softc *)arg1; 190 error = ds1307_ctrl_read(sc); 191 if (error != 0) 192 return (error); 193 sqwe = newv = (sc->sc_ctrl & DS1307_CTRL_OUT) ? 1 : 0; 194 error = sysctl_handle_int(oidp, &newv, 0, req); 195 if (error != 0 || req->newptr == NULL) 196 return (error); 197 if (sqwe != newv) { 198 sc->sc_ctrl &= ~DS1307_CTRL_OUT; 199 if (newv) 200 sc->sc_ctrl |= DS1307_CTRL_OUT; 201 error = ds1307_ctrl_write(sc); 202 if (error != 0) 203 return (error); 204 } 205 206 return (error); 207} 208 209static int 210ds1307_probe(device_t dev) 211{ 212#ifdef FDT 213 const struct ofw_compat_data *compat; 214 215 if (!ofw_bus_status_okay(dev)) 216 return (ENXIO); 217 218 compat = ofw_bus_search_compatible(dev, ds1307_compat_data); 219 220 if (compat->ocd_str == NULL) 221 return (ENXIO); 222 223 device_set_desc(dev, (const char *)compat->ocd_data); 224 225 return (BUS_PROBE_DEFAULT); 226#else 227 device_set_desc(dev, "Maxim DS1307 RTC"); 228 229 return (BUS_PROBE_NOWILDCARD); 230#endif 231} 232 233static int 234ds1307_attach(device_t dev) 235{ 236 struct ds1307_softc *sc; 237 238 sc = device_get_softc(dev); 239 sc->sc_dev = dev; 240 sc->enum_hook.ich_func = ds1307_start; 241 sc->enum_hook.ich_arg = dev; 242 243#ifdef FDT 244 if (ofw_bus_is_compatible(dev, "microchip,mcp7941x")) 245 sc->sc_mcp7941x = 1; 246#endif 247 248 /* 249 * We have to wait until interrupts are enabled. Usually I2C read 250 * and write only works when the interrupts are available. 251 */ 252 if (config_intrhook_establish(&sc->enum_hook) != 0) 253 return (ENOMEM); 254 255 return (0); 256} 257 258static int 259ds1307_detach(device_t dev) 260{ 261 262 clock_unregister(dev); 263 return (0); 264} 265 266static void 267ds1307_start(void *xdev) 268{ 269 device_t dev; 270 struct ds1307_softc *sc; 271 struct sysctl_ctx_list *ctx; 272 struct sysctl_oid *tree_node; 273 struct sysctl_oid_list *tree; 274 uint8_t secs; 275 uint8_t osc_en; 276 277 dev = (device_t)xdev; 278 sc = device_get_softc(dev); 279 ctx = device_get_sysctl_ctx(dev); 280 tree_node = device_get_sysctl_tree(dev); 281 tree = SYSCTL_CHILDREN(tree_node); 282 283 config_intrhook_disestablish(&sc->enum_hook); 284 285 /* Check if the oscillator is disabled. */ 286 if (ds1307_read1(sc->sc_dev, DS1307_SECS, &secs) != 0) { 287 device_printf(sc->sc_dev, "cannot read from RTC.\n"); 288 return; 289 } 290 if (sc->sc_mcp7941x) 291 osc_en = 0x80; 292 else 293 osc_en = 0x00; 294 295 if (((secs & DS1307_SECS_CH) ^ osc_en) != 0) { 296 device_printf(sc->sc_dev, 297 "WARNING: RTC clock stopped, check the battery.\n"); 298 } 299 300 /* Configuration parameters. */ 301 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqwe", 302 CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, 303 ds1307_sqwe_sysctl, "IU", "DS1307 square-wave enable"); 304 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_freq", 305 CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, 306 ds1307_sqw_freq_sysctl, "IU", 307 "DS1307 square-wave output frequency"); 308 SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "sqw_out", 309 CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_MPSAFE, sc, 0, 310 ds1307_sqw_out_sysctl, "IU", "DS1307 square-wave output state"); 311 312 /* 313 * Register as a clock with 1 second resolution. Schedule the 314 * clock_settime() method to be called just after top-of-second; 315 * resetting the time resets top-of-second in the hardware. 316 */ 317 clock_register_flags(dev, 1000000, CLOCKF_SETTIME_NO_ADJ); 318 clock_schedule(dev, 1); 319} 320 321static int 322ds1307_gettime(device_t dev, struct timespec *ts) 323{ 324 int error; 325 struct bcd_clocktime bct; 326 struct ds1307_softc *sc; 327 uint8_t data[7], hourmask, st_mask; 328 329 sc = device_get_softc(dev); 330 error = iicdev_readfrom(sc->sc_dev, DS1307_SECS, data, sizeof(data), 331 IIC_INTRWAIT); 332 if (error != 0) { 333 device_printf(dev, "cannot read from RTC.\n"); 334 return (error); 335 } 336 337 /* If the clock halted, we don't have good data. */ 338 if (sc->sc_mcp7941x) 339 st_mask = 0x80; 340 else 341 st_mask = 0x00; 342 343 if (((data[DS1307_SECS] & DS1307_SECS_CH) ^ st_mask) != 0) 344 return (EINVAL); 345 346 /* If chip is in AM/PM mode remember that. */ 347 if (data[DS1307_HOUR] & DS1307_HOUR_USE_AMPM) { 348 sc->sc_use_ampm = true; 349 hourmask = DS1307_HOUR_MASK_12HR; 350 } else 351 hourmask = DS1307_HOUR_MASK_24HR; 352 353 bct.nsec = 0; 354 bct.ispm = (data[DS1307_HOUR] & DS1307_HOUR_IS_PM) != 0; 355 bct.sec = data[DS1307_SECS] & DS1307_SECS_MASK; 356 bct.min = data[DS1307_MINS] & DS1307_MINS_MASK; 357 bct.hour = data[DS1307_HOUR] & hourmask; 358 bct.day = data[DS1307_DATE] & DS1307_DATE_MASK; 359 bct.mon = data[DS1307_MONTH] & DS1307_MONTH_MASK; 360 bct.year = data[DS1307_YEAR] & DS1307_YEAR_MASK; 361 362 clock_dbgprint_bcd(sc->sc_dev, CLOCK_DBG_READ, &bct); 363 return (clock_bcd_to_ts(&bct, ts, sc->sc_use_ampm)); 364} 365 366static int 367ds1307_settime(device_t dev, struct timespec *ts) 368{ 369 struct bcd_clocktime bct; 370 struct ds1307_softc *sc; 371 int error, year; 372 uint8_t data[7]; 373 uint8_t pmflags; 374 375 sc = device_get_softc(dev); 376 377 /* 378 * We request a timespec with no resolution-adjustment. That also 379 * disables utc adjustment, so apply that ourselves. 380 */ 381 ts->tv_sec -= utc_offset(); 382 clock_ts_to_bcd(ts, &bct, sc->sc_use_ampm); 383 clock_dbgprint_bcd(sc->sc_dev, CLOCK_DBG_WRITE, &bct); 384 385 /* If the chip is in AM/PM mode, adjust hour and set flags as needed. */ 386 if (sc->sc_use_ampm) { 387 pmflags = DS1307_HOUR_USE_AMPM; 388 if (bct.ispm) 389 pmflags |= DS1307_HOUR_IS_PM; 390 } else 391 pmflags = 0; 392 393 data[DS1307_SECS] = bct.sec; 394 data[DS1307_MINS] = bct.min; 395 data[DS1307_HOUR] = bct.hour | pmflags; 396 data[DS1307_DATE] = bct.day; 397 data[DS1307_WEEKDAY] = bct.dow; 398 data[DS1307_MONTH] = bct.mon; 399 data[DS1307_YEAR] = bct.year & 0xff; 400 if (sc->sc_mcp7941x) { 401 data[DS1307_SECS] |= MCP7941X_SECS_ST; 402 data[DS1307_WEEKDAY] |= MCP7941X_WEEKDAY_VBATEN; 403 year = bcd2bin(bct.year >> 8) * 100 + bcd2bin(bct.year & 0xff); 404 if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) 405 data[DS1307_MONTH] |= MCP7941X_MONTH_LPYR; 406 } 407 /* Write the time back to RTC. */ 408 error = iicdev_writeto(sc->sc_dev, DS1307_SECS, data, sizeof(data), 409 IIC_INTRWAIT); 410 if (error != 0) 411 device_printf(dev, "cannot write to RTC.\n"); 412 413 return (error); 414} 415 416static device_method_t ds1307_methods[] = { 417 DEVMETHOD(device_probe, ds1307_probe), 418 DEVMETHOD(device_attach, ds1307_attach), 419 DEVMETHOD(device_detach, ds1307_detach), 420 421 DEVMETHOD(clock_gettime, ds1307_gettime), 422 DEVMETHOD(clock_settime, ds1307_settime), 423 424 DEVMETHOD_END 425}; 426 427static driver_t ds1307_driver = { 428 "ds1307", 429 ds1307_methods, 430 sizeof(struct ds1307_softc), 431}; 432 433static devclass_t ds1307_devclass; 434 435DRIVER_MODULE(ds1307, iicbus, ds1307_driver, ds1307_devclass, NULL, NULL); 436MODULE_VERSION(ds1307, 1); 437MODULE_DEPEND(ds1307, iicbus, 1, 1, 1); 438IICBUS_FDT_PNP_INFO(ds1307_compat_data); 439