1/* $OpenBSD: pcf8523.c,v 1.8 2022/10/12 13:39:50 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 34#include <dev/clock_subr.h> 35 36#include <dev/i2c/i2cvar.h> 37 38/* 39 * PCF8523 Real-Time Clock 40 */ 41 42#define PCF8523_ADDR 0x68 /* Fixed I2C Slave Address */ 43 44#define PCF8523_CONTROL1 0x00 45#define PCF8523_CONTROL2 0x01 46#define PCF8523_CONTROL3 0x02 47#define PCF8523_SECONDS 0x03 48#define PCF8523_MINUTES 0x04 49#define PCF8523_HOURS 0x05 50#define PCF8523_DAY 0x06 51#define PCF8523_WDAY 0x07 52#define PCF8523_MONTH 0x08 53#define PCF8523_YEAR 0x09 54#define PCF8523_ALARM_MIN 0x0a 55#define PCF8523_ALARM_HOUR 0x0b 56#define PCF8523_ALARM_DAY 0x0c 57#define PCF8523_ALARM_WDAY 0x0d 58#define PCF8523_OFFSET 0x0e 59 60#define PCF8523_NREGS 20 61#define PCF8523_NRTC_REGS 7 62 63/* 64 * Bit definitions. 65 */ 66#define PCF8523_CONTROL1_12_24 (1 << 3) 67#define PCF8523_CONTROL1_STOP (1 << 5) 68#define PCF8523_CONTROL3_PM_MASK 0xe0 69#define PCF8523_CONTROL3_PM_BLD (1 << 7) 70#define PCF8523_CONTROL3_PM_VDD (1 << 6) 71#define PCF8523_CONTROL3_PM_DSM (1 << 5) 72#define PCF8523_CONTROL3_BLF (1 << 2) 73#define PCF8523_SECONDS_MASK 0x7f 74#define PCF8523_SECONDS_OS (1 << 7) 75#define PCF8523_MINUTES_MASK 0x7f 76#define PCF8523_HOURS_12HRS_PM (1 << 5) /* If 12 hr mode, set = PM */ 77#define PCF8523_HOURS_12MASK 0x1f 78#define PCF8523_HOURS_24MASK 0x3f 79#define PCF8523_DAY_MASK 0x3f 80#define PCF8523_WDAY_MASK 0x07 81#define PCF8523_MONTH_MASK 0x1f 82 83struct pcfrtc_softc { 84 struct device sc_dev; 85 i2c_tag_t sc_tag; 86 int sc_address; 87 struct todr_chip_handle sc_todr; 88}; 89 90int pcfrtc_match(struct device *, void *, void *); 91void pcfrtc_attach(struct device *, struct device *, void *); 92 93const struct cfattach pcfrtc_ca = { 94 sizeof(struct pcfrtc_softc), pcfrtc_match, pcfrtc_attach 95}; 96 97struct cfdriver pcfrtc_cd = { 98 NULL, "pcfrtc", DV_DULL 99}; 100 101uint8_t pcfrtc_reg_read(struct pcfrtc_softc *, int); 102void pcfrtc_reg_write(struct pcfrtc_softc *, int, uint8_t); 103int pcfrtc_clock_read(struct pcfrtc_softc *, struct clock_ymdhms *); 104int pcfrtc_clock_write(struct pcfrtc_softc *, struct clock_ymdhms *); 105int pcfrtc_gettime(struct todr_chip_handle *, struct timeval *); 106int pcfrtc_settime(struct todr_chip_handle *, struct timeval *); 107 108int 109pcfrtc_match(struct device *parent, void *v, void *arg) 110{ 111 struct i2c_attach_args *ia = arg; 112 113 if (strcmp(ia->ia_name, "nxp,pcf8523") == 0 && 114 ia->ia_addr == PCF8523_ADDR) 115 return (1); 116 117 return (0); 118} 119 120void 121pcfrtc_attach(struct device *parent, struct device *self, void *arg) 122{ 123 struct pcfrtc_softc *sc = (struct pcfrtc_softc *)self; 124 struct i2c_attach_args *ia = arg; 125 uint8_t reg; 126 127 sc->sc_tag = ia->ia_tag; 128 sc->sc_address = ia->ia_addr; 129 130 sc->sc_todr.cookie = sc; 131 sc->sc_todr.todr_gettime = pcfrtc_gettime; 132 sc->sc_todr.todr_settime = pcfrtc_settime; 133 sc->sc_todr.todr_setwen = NULL; 134 sc->sc_todr.todr_quality = 1000; 135 todr_attach(&sc->sc_todr); 136 137 /* 138 * Enable battery switch-over and battery low detection in 139 * standard mode, and switch to 24 hour mode. 140 */ 141 reg = pcfrtc_reg_read(sc, PCF8523_CONTROL3); 142 reg &= ~PCF8523_CONTROL3_PM_MASK; 143 pcfrtc_reg_write(sc, PCF8523_CONTROL3, reg); 144 reg = pcfrtc_reg_read(sc, PCF8523_CONTROL1); 145 reg &= ~PCF8523_CONTROL1_12_24; 146 reg &= ~PCF8523_CONTROL1_STOP; 147 pcfrtc_reg_write(sc, PCF8523_CONTROL1, reg); 148 149 /* Report battery status. */ 150 reg = pcfrtc_reg_read(sc, PCF8523_CONTROL3); 151 printf(": battery %s\n", (reg & PCF8523_CONTROL3_BLF) ? "low" : "ok"); 152} 153 154int 155pcfrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv) 156{ 157 struct pcfrtc_softc *sc = ch->cookie; 158 struct clock_ymdhms dt; 159 160 memset(&dt, 0, sizeof(dt)); 161 if (pcfrtc_clock_read(sc, &dt) == 0) 162 return (-1); 163 164 tv->tv_sec = clock_ymdhms_to_secs(&dt); 165 tv->tv_usec = 0; 166 return (0); 167} 168 169int 170pcfrtc_settime(struct todr_chip_handle *ch, struct timeval *tv) 171{ 172 struct pcfrtc_softc *sc = ch->cookie; 173 struct clock_ymdhms dt; 174 uint8_t reg; 175 176 clock_secs_to_ymdhms(tv->tv_sec, &dt); 177 if (pcfrtc_clock_write(sc, &dt) == 0) 178 return (-1); 179 180 /* Clear OS flag. */ 181 reg = pcfrtc_reg_read(sc, PCF8523_SECONDS); 182 if (reg & PCF8523_SECONDS_OS) { 183 reg &= ~PCF8523_SECONDS_OS; 184 pcfrtc_reg_write(sc, PCF8523_SECONDS, reg); 185 } 186 187 return (0); 188} 189 190uint8_t 191pcfrtc_reg_read(struct pcfrtc_softc *sc, int reg) 192{ 193 uint8_t cmd = reg; 194 uint8_t val; 195 196 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 197 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 198 NULL, 0, &cmd, sizeof cmd, I2C_F_POLL) || 199 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 200 NULL, 0, &val, sizeof val, I2C_F_POLL)) { 201 iic_release_bus(sc->sc_tag, I2C_F_POLL); 202 printf("%s: pcfrtc_reg_read: failed to read reg%d\n", 203 sc->sc_dev.dv_xname, reg); 204 return 0; 205 } 206 iic_release_bus(sc->sc_tag, I2C_F_POLL); 207 return val; 208} 209 210void 211pcfrtc_reg_write(struct pcfrtc_softc *sc, int reg, uint8_t val) 212{ 213 uint8_t cmd = reg; 214 215 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 216 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 217 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL)) { 218 iic_release_bus(sc->sc_tag, I2C_F_POLL); 219 printf("%s: pcfrtc_reg_write: failed to write reg%d\n", 220 sc->sc_dev.dv_xname, reg); 221 return; 222 } 223 iic_release_bus(sc->sc_tag, I2C_F_POLL); 224} 225 226int 227pcfrtc_clock_read(struct pcfrtc_softc *sc, struct clock_ymdhms *dt) 228{ 229 uint8_t regs[PCF8523_NRTC_REGS]; 230 uint8_t cmd = PCF8523_SECONDS; 231 232 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 233 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 234 NULL, 0, &cmd, sizeof cmd, I2C_F_POLL) || 235 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_address, 236 NULL, 0, regs, PCF8523_NRTC_REGS, I2C_F_POLL)) { 237 iic_release_bus(sc->sc_tag, I2C_F_POLL); 238 printf("%s: pcfrtc_clock_read: failed to read rtc\n", 239 sc->sc_dev.dv_xname); 240 return (0); 241 } 242 iic_release_bus(sc->sc_tag, I2C_F_POLL); 243 244 /* 245 * Convert the PCF8523's register values into something useable 246 */ 247 dt->dt_sec = FROMBCD(regs[0] & PCF8523_SECONDS_MASK); 248 dt->dt_min = FROMBCD(regs[1] & PCF8523_MINUTES_MASK); 249 dt->dt_hour = FROMBCD(regs[2] & PCF8523_HOURS_24MASK); 250 dt->dt_day = FROMBCD(regs[3] & PCF8523_DAY_MASK); 251 dt->dt_mon = FROMBCD(regs[5] & PCF8523_MONTH_MASK); 252 dt->dt_year = FROMBCD(regs[6]) + 2000; 253 254 if (regs[0] & PCF8523_SECONDS_OS) 255 return (0); 256 257 return (1); 258} 259 260int 261pcfrtc_clock_write(struct pcfrtc_softc *sc, struct clock_ymdhms *dt) 262{ 263 uint8_t regs[PCF8523_NRTC_REGS]; 264 uint8_t cmd = PCF8523_SECONDS; 265 266 /* 267 * Convert our time representation into something the PCF8523 268 * can understand. 269 */ 270 regs[0] = TOBCD(dt->dt_sec); 271 regs[1] = TOBCD(dt->dt_min); 272 regs[2] = TOBCD(dt->dt_hour); 273 regs[3] = TOBCD(dt->dt_day); 274 regs[4] = TOBCD(dt->dt_wday); 275 regs[5] = TOBCD(dt->dt_mon); 276 regs[6] = TOBCD(dt->dt_year - 2000); 277 278 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 279 if (iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_address, 280 &cmd, sizeof cmd, regs, PCF8523_NRTC_REGS, I2C_F_POLL)) { 281 iic_release_bus(sc->sc_tag, I2C_F_POLL); 282 printf("%s: pcfrtc_clock_write: failed to write rtc\n", 283 sc->sc_dev.dv_xname); 284 return (0); 285 } 286 iic_release_bus(sc->sc_tag, I2C_F_POLL); 287 return (1); 288} 289