1 /* $OpenBSD: pcf8523.c,v 1.4 2020/04/27 12:36:03 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2005 Kimihiro Nonaka 5 * Copyright (c) 2016 Mark Kettenis 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/device.h> 33 #include <sys/kernel.h> 34 #include <sys/fcntl.h> 35 #include <sys/uio.h> 36 #include <sys/conf.h> 37 #include <sys/event.h> 38 39 #include <dev/clock_subr.h> 40 41 #include <dev/i2c/i2cvar.h> 42 43 /* 44 * PCF8523 Real-Time Clock 45 */ 46 47 #define PCF8523_ADDR 0x68 /* Fixed I2C Slave Address */ 48 49 #define PCF8523_CONTROL1 0x00 50 #define PCF8523_CONTROL2 0x01 51 #define PCF8523_CONTROL3 0x02 52 #define PCF8523_SECONDS 0x03 53 #define PCF8523_MINUTES 0x04 54 #define PCF8523_HOURS 0x05 55 #define PCF8523_DAY 0x06 56 #define PCF8523_WDAY 0x07 57 #define PCF8523_MONTH 0x08 58 #define PCF8523_YEAR 0x09 59 #define PCF8523_ALARM_MIN 0x0a 60 #define PCF8523_ALARM_HOUR 0x0b 61 #define PCF8523_ALARM_DAY 0x0c 62 #define PCF8523_ALARM_WDAY 0x0d 63 #define PCF8523_OFFSET 0x0e 64 65 #define PCF8523_NREGS 20 66 #define PCF8523_NRTC_REGS 7 67 68 /* 69 * Bit definitions. 70 */ 71 #define PCF8523_CONTROL1_12_24 (1 << 3) 72 #define PCF8523_CONTROL1_STOP (1 << 5) 73 #define PCF8523_CONTROL3_PM_MASK 0xe0 74 #define PCF8523_CONTROL3_PM_BLD (1 << 7) 75 #define PCF8523_CONTROL3_PM_VDD (1 << 6) 76 #define PCF8523_CONTROL3_PM_DSM (1 << 5) 77 #define PCF8523_CONTROL3_BLF (1 << 2) 78 #define PCF8523_SECONDS_MASK 0x7f 79 #define PCF8523_SECONDS_OS (1 << 7) 80 #define PCF8523_MINUTES_MASK 0x7f 81 #define PCF8523_HOURS_12HRS_PM (1 << 5) /* If 12 hr mode, set = PM */ 82 #define PCF8523_HOURS_12MASK 0x1f 83 #define PCF8523_HOURS_24MASK 0x3f 84 #define PCF8523_DAY_MASK 0x3f 85 #define PCF8523_WDAY_MASK 0x07 86 #define PCF8523_MONTH_MASK 0x1f 87 88 struct pcfrtc_softc { 89 struct device sc_dev; 90 i2c_tag_t sc_tag; 91 int sc_address; 92 struct todr_chip_handle sc_todr; 93 }; 94 95 int pcfrtc_match(struct device *, void *, void *); 96 void pcfrtc_attach(struct device *, struct device *, void *); 97 98 struct cfattach pcfrtc_ca = { 99 sizeof(struct pcfrtc_softc), pcfrtc_match, pcfrtc_attach 100 }; 101 102 struct cfdriver pcfrtc_cd = { 103 NULL, "pcfrtc", DV_DULL 104 }; 105 106 uint8_t pcfrtc_reg_read(struct pcfrtc_softc *, int); 107 void pcfrtc_reg_write(struct pcfrtc_softc *, int, uint8_t); 108 int pcfrtc_clock_read(struct pcfrtc_softc *, struct clock_ymdhms *); 109 int pcfrtc_clock_write(struct pcfrtc_softc *, struct clock_ymdhms *); 110 int pcfrtc_gettime(struct todr_chip_handle *, struct timeval *); 111 int pcfrtc_settime(struct todr_chip_handle *, struct timeval *); 112 int pcfrtc_getcal(struct todr_chip_handle *, int *); 113 int pcfrtc_setcal(struct todr_chip_handle *, int); 114 115 int 116 pcfrtc_match(struct device *parent, void *v, void *arg) 117 { 118 struct i2c_attach_args *ia = arg; 119 120 if (strcmp(ia->ia_name, "nxp,pcf8523") == 0 && 121 ia->ia_addr == PCF8523_ADDR) 122 return (1); 123 124 return (0); 125 } 126 127 void 128 pcfrtc_attach(struct device *parent, struct device *self, void *arg) 129 { 130 struct pcfrtc_softc *sc = (struct pcfrtc_softc *)self; 131 struct i2c_attach_args *ia = arg; 132 uint8_t reg; 133 134 sc->sc_tag = ia->ia_tag; 135 sc->sc_address = ia->ia_addr; 136 sc->sc_todr.cookie = sc; 137 sc->sc_todr.todr_gettime = pcfrtc_gettime; 138 sc->sc_todr.todr_settime = pcfrtc_settime; 139 sc->sc_todr.todr_getcal = pcfrtc_getcal; 140 sc->sc_todr.todr_setcal = pcfrtc_setcal; 141 sc->sc_todr.todr_setwen = NULL; 142 143 #if 0 144 todr_attach(&sc->sc_todr); 145 #else 146 /* XXX */ 147 { 148 extern todr_chip_handle_t todr_handle; 149 todr_handle = &sc->sc_todr; 150 } 151 #endif 152 153 /* 154 * Enable battery switch-over and battery low detection in 155 * standard mode, and switch to 24 hour mode. 156 */ 157 reg = pcfrtc_reg_read(sc, PCF8523_CONTROL3); 158 reg &= ~PCF8523_CONTROL3_PM_MASK; 159 pcfrtc_reg_write(sc, PCF8523_CONTROL3, reg); 160 reg = pcfrtc_reg_read(sc, PCF8523_CONTROL1); 161 reg &= ~PCF8523_CONTROL1_12_24; 162 reg &= ~PCF8523_CONTROL1_STOP; 163 pcfrtc_reg_write(sc, PCF8523_CONTROL1, reg); 164 165 /* Report battery status. */ 166 reg = pcfrtc_reg_read(sc, PCF8523_CONTROL3); 167 printf(": battery %s\n", (reg & PCF8523_CONTROL3_BLF) ? "low" : "ok"); 168 } 169 170 int 171 pcfrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv) 172 { 173 struct pcfrtc_softc *sc = ch->cookie; 174 struct clock_ymdhms dt; 175 176 memset(&dt, 0, sizeof(dt)); 177 if (pcfrtc_clock_read(sc, &dt) == 0) 178 return (-1); 179 180 tv->tv_sec = clock_ymdhms_to_secs(&dt); 181 tv->tv_usec = 0; 182 return (0); 183 } 184 185 int 186 pcfrtc_settime(struct todr_chip_handle *ch, struct timeval *tv) 187 { 188 struct pcfrtc_softc *sc = ch->cookie; 189 struct clock_ymdhms dt; 190 uint8_t reg; 191 192 clock_secs_to_ymdhms(tv->tv_sec, &dt); 193 if (pcfrtc_clock_write(sc, &dt) == 0) 194 return (-1); 195 196 /* Clear OS flag. */ 197 reg = pcfrtc_reg_read(sc, PCF8523_SECONDS); 198 if (reg & PCF8523_SECONDS_OS) { 199 reg &= ~PCF8523_SECONDS_OS; 200 pcfrtc_reg_write(sc, PCF8523_SECONDS, reg); 201 } 202 203 return (0); 204 } 205 206 int 207 pcfrtc_setcal(struct todr_chip_handle *ch, int cal) 208 { 209 return (EOPNOTSUPP); 210 } 211 212 int 213 pcfrtc_getcal(struct todr_chip_handle *ch, int *cal) 214 { 215 return (EOPNOTSUPP); 216 } 217 218 uint8_t 219 pcfrtc_reg_read(struct pcfrtc_softc *sc, int reg) 220 { 221 uint8_t cmd = reg; 222 uint8_t val; 223 224 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 225 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 226 NULL, 0, &cmd, sizeof cmd, I2C_F_POLL) || 227 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 228 NULL, 0, &val, sizeof val, I2C_F_POLL)) { 229 iic_release_bus(sc->sc_tag, I2C_F_POLL); 230 printf("%s: pcfrtc_reg_read: failed to read reg%d\n", 231 sc->sc_dev.dv_xname, reg); 232 return 0; 233 } 234 iic_release_bus(sc->sc_tag, I2C_F_POLL); 235 return val; 236 } 237 238 void 239 pcfrtc_reg_write(struct pcfrtc_softc *sc, int reg, uint8_t val) 240 { 241 uint8_t cmd = reg; 242 243 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 244 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 245 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL)) { 246 iic_release_bus(sc->sc_tag, I2C_F_POLL); 247 printf("%s: pcfrtc_reg_write: failed to write reg%d\n", 248 sc->sc_dev.dv_xname, reg); 249 return; 250 } 251 iic_release_bus(sc->sc_tag, I2C_F_POLL); 252 } 253 254 int 255 pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt) 256 { 257 uint8_t regs[PCF8523_NRTC_REGS]; 258 uint8_t cmd = PCF8523_SECONDS; 259 260 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 261 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 262 NULL, 0, &cmd, sizeof cmd, I2C_F_POLL) || 263 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 264 NULL, 0, regs, PCF8523_NRTC_REGS, I2C_F_POLL)) { 265 iic_release_bus(sc->sc_tag, I2C_F_POLL); 266 printf("%s: pcfrtc_clock_read: failed to read rtc\n", 267 sc->sc_dev.dv_xname); 268 return (0); 269 } 270 iic_release_bus(sc->sc_tag, I2C_F_POLL); 271 272 /* 273 * Convert the PCF8523's register values into something useable 274 */ 275 dt->dt_sec = FROMBCD(regs[0] & PCF8523_SECONDS_MASK); 276 dt->dt_min = FROMBCD(regs[1] & PCF8523_MINUTES_MASK); 277 dt->dt_hour = FROMBCD(regs[2] & PCF8523_HOURS_24MASK); 278 dt->dt_day = FROMBCD(regs[3] & PCF8523_DAY_MASK); 279 dt->dt_mon = FROMBCD(regs[5] & PCF8523_MONTH_MASK); 280 dt->dt_year = FROMBCD(regs[6]) + 2000; 281 282 if ((regs[0] & PCF8523_SECONDS_OS)) 283 return (0); 284 285 return (1); 286 } 287 288 int 289 pcfrtc_clock_write(struct pcfrtc_softc *sc, struct clock_ymdhms *dt) 290 { 291 uint8_t regs[PCF8523_NRTC_REGS]; 292 uint8_t cmd = PCF8523_SECONDS; 293 294 /* 295 * Convert our time representation into something the PCF8523 296 * can understand. 297 */ 298 regs[0] = TOBCD(dt->dt_sec); 299 regs[1] = TOBCD(dt->dt_min); 300 regs[2] = TOBCD(dt->dt_hour); 301 regs[3] = TOBCD(dt->dt_day); 302 regs[4] = TOBCD(dt->dt_wday); 303 regs[5] = TOBCD(dt->dt_mon); 304 regs[6] = TOBCD(dt->dt_year - 2000); 305 306 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 307 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 308 &cmd, sizeof cmd, regs, PCF8523_NRTC_REGS, I2C_F_POLL)) { 309 iic_release_bus(sc->sc_tag, I2C_F_POLL); 310 printf("%s: pcfrtc_clock_write: failed to write rtc\n", 311 sc->sc_dev.dv_xname); 312 return (0); 313 } 314 iic_release_bus(sc->sc_tag, I2C_F_POLL); 315 return (1); 316 } 317