1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2006 Sam Leffler. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 /* 29 * Dallas Semiconductor DS1672 RTC sitting on the I2C bus. 30 */ 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/module.h> 35 #include <sys/clock.h> 36 #include <sys/time.h> 37 #include <sys/bus.h> 38 #include <sys/resource.h> 39 #include <sys/rman.h> 40 41 #include <dev/iicbus/iiconf.h> 42 43 #include "iicbus_if.h" 44 #include "clock_if.h" 45 46 #define IIC_M_WR 0 /* write operation */ 47 48 #define DS1672_ADDR 0xd0 /* slave address */ 49 50 #define DS1672_COUNTER 0 /* counter (bytes 0-3) */ 51 #define DS1672_CTRL 4 /* control (1 byte) */ 52 #define DS1672_TRICKLE 5 /* trickle charger (1 byte) */ 53 54 #define DS1672_CTRL_EOSC (1 << 7) /* Stop/start flag. */ 55 56 #define MAX_IIC_DATA_SIZE 4 57 58 struct ds1672_softc { 59 device_t sc_dev; 60 }; 61 62 static int 63 ds1672_probe(device_t dev) 64 { 65 /* XXX really probe? */ 66 device_set_desc(dev, "Dallas Semiconductor DS1672 RTC"); 67 return (BUS_PROBE_NOWILDCARD); 68 } 69 70 static int 71 ds1672_read(device_t dev, uint8_t addr, uint8_t *data, uint8_t size) 72 { 73 struct iic_msg msgs[2] = { 74 { DS1672_ADDR, IIC_M_WR, 1, &addr }, 75 { DS1672_ADDR, IIC_M_RD, size, data } 76 }; 77 78 return (iicbus_transfer(dev, msgs, 2)); 79 } 80 81 static int 82 ds1672_write(device_t dev, uint8_t addr, uint8_t *data, uint8_t size) 83 { 84 uint8_t buffer[MAX_IIC_DATA_SIZE + 1]; 85 struct iic_msg msgs[1] = { 86 { DS1672_ADDR, IIC_M_WR, size + 1, buffer }, 87 }; 88 89 if (size > MAX_IIC_DATA_SIZE) 90 return (ENOMEM); 91 /* NB: register pointer precedes actual data */ 92 buffer[0] = addr; 93 memcpy(buffer + 1, data, size); 94 return (iicbus_transfer(dev, msgs, 1)); 95 } 96 97 static int 98 ds1672_init(device_t dev) 99 { 100 uint8_t ctrl; 101 int error; 102 103 error = ds1672_read(dev, DS1672_CTRL, &ctrl, 1); 104 if (error) 105 return (error); 106 107 /* 108 * Check if oscialltor is not runned. 109 */ 110 if (ctrl & DS1672_CTRL_EOSC) { 111 device_printf(dev, "RTC oscillator was stopped. Check system" 112 " time and RTC battery.\n"); 113 ctrl &= ~DS1672_CTRL_EOSC; /* Start oscillator. */ 114 error = ds1672_write(dev, DS1672_CTRL, &ctrl, 1); 115 } 116 return (error); 117 } 118 119 static int 120 ds1672_detach(device_t dev) 121 { 122 123 clock_unregister(dev); 124 return (0); 125 } 126 127 static int 128 ds1672_attach(device_t dev) 129 { 130 struct ds1672_softc *sc = device_get_softc(dev); 131 int error; 132 133 sc->sc_dev = dev; 134 error = ds1672_init(dev); 135 if (error) 136 return (error); 137 clock_register(dev, 1000); 138 return (0); 139 } 140 141 static int 142 ds1672_gettime(device_t dev, struct timespec *ts) 143 { 144 uint8_t secs[4]; 145 int error; 146 147 error = ds1672_read(dev, DS1672_COUNTER, secs, 4); 148 if (error == 0) { 149 /* counter has seconds since epoch */ 150 ts->tv_sec = (secs[3] << 24) | (secs[2] << 16) 151 | (secs[1] << 8) | (secs[0] << 0); 152 ts->tv_nsec = 0; 153 } 154 clock_dbgprint_ts(dev, CLOCK_DBG_READ, ts); 155 return (error); 156 } 157 158 static int 159 ds1672_settime(device_t dev, struct timespec *ts) 160 { 161 uint8_t data[4]; 162 163 data[0] = (ts->tv_sec >> 0) & 0xff; 164 data[1] = (ts->tv_sec >> 8) & 0xff; 165 data[2] = (ts->tv_sec >> 16) & 0xff; 166 data[3] = (ts->tv_sec >> 24) & 0xff; 167 168 ts->tv_nsec = 0; 169 clock_dbgprint_ts(dev, CLOCK_DBG_WRITE, ts); 170 return (ds1672_write(dev, DS1672_COUNTER, data, 4)); 171 } 172 173 static device_method_t ds1672_methods[] = { 174 DEVMETHOD(device_probe, ds1672_probe), 175 DEVMETHOD(device_attach, ds1672_attach), 176 DEVMETHOD(device_detach, ds1672_detach), 177 178 DEVMETHOD(clock_gettime, ds1672_gettime), 179 DEVMETHOD(clock_settime, ds1672_settime), 180 181 DEVMETHOD_END 182 }; 183 184 static driver_t ds1672_driver = { 185 "ds1672_rtc", 186 ds1672_methods, 187 sizeof(struct ds1672_softc), 188 }; 189 190 DRIVER_MODULE(ds1672, iicbus, ds1672_driver, 0, 0); 191 MODULE_VERSION(ds1672, 1); 192 MODULE_DEPEND(ds1672, iicbus, 1, 1, 1); 193