1 /* $OpenBSD: abx80x.c,v 1.1 2019/01/02 21:15:47 patrick Exp $ */ 2 /* 3 * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 4 * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 23 #include <dev/i2c/i2cvar.h> 24 25 #include <dev/clock_subr.h> 26 27 extern todr_chip_handle_t todr_handle; 28 29 #define ABX8XX_HTH 0x00 30 #define ABX8XX_SC 0x01 31 #define ABX8XX_MN 0x02 32 #define ABX8XX_HR 0x03 33 #define ABX8XX_DA 0x04 34 #define ABX8XX_MO 0x05 35 #define ABX8XX_YR 0x06 36 #define ABX8XX_WD 0x07 37 #define ABX8XX_CTRL 0x10 38 #define ABX8XX_CTRL_WRITE (1 << 0) 39 #define ABX8XX_CTRL_ARST (1 << 2) 40 #define ABX8XX_CTRL_12_24 (1 << 6) 41 #define ABX8XX_CD_TIMER_CTL 0x18 42 #define ABX8XX_CD_TIMER_CTL_EN (1 << 2) 43 #define ABX8XX_OSS 0x1d 44 #define ABX8XX_OSS_OF (1 << 1) 45 #define ABX8XX_OSS_OMODE (1 << 4) 46 47 #define ABX8XX_NRTC_REGS 8 48 49 struct abcrtc_softc { 50 struct device sc_dev; 51 i2c_tag_t sc_tag; 52 i2c_addr_t sc_addr; 53 54 struct todr_chip_handle sc_todr; 55 }; 56 57 int abcrtc_match(struct device *, void *, void *); 58 void abcrtc_attach(struct device *, struct device *, void *); 59 60 struct cfattach abcrtc_ca = { 61 sizeof(struct abcrtc_softc), abcrtc_match, abcrtc_attach 62 }; 63 64 struct cfdriver abcrtc_cd = { 65 NULL, "abcrtc", DV_DULL 66 }; 67 68 uint8_t abcrtc_reg_read(struct abcrtc_softc *, int); 69 void abcrtc_reg_write(struct abcrtc_softc *, int, uint8_t); 70 int abcrtc_clock_read(struct abcrtc_softc *, struct clock_ymdhms *); 71 int abcrtc_clock_write(struct abcrtc_softc *, struct clock_ymdhms *); 72 int abcrtc_gettime(struct todr_chip_handle *, struct timeval *); 73 int abcrtc_settime(struct todr_chip_handle *, struct timeval *); 74 75 int 76 abcrtc_match(struct device *parent, void *match, void *aux) 77 { 78 struct i2c_attach_args *ia = aux; 79 80 if (strcmp(ia->ia_name, "abracon,ab1805") == 0) 81 return 1; 82 83 return 0; 84 } 85 86 void 87 abcrtc_attach(struct device *parent, struct device *self, void *aux) 88 { 89 struct abcrtc_softc *sc = (struct abcrtc_softc *)self; 90 struct i2c_attach_args *ia = aux; 91 uint8_t reg; 92 93 sc->sc_tag = ia->ia_tag; 94 sc->sc_addr = ia->ia_addr; 95 96 reg = abcrtc_reg_read(sc, ABX8XX_CTRL); 97 reg &= ~(ABX8XX_CTRL_ARST | ABX8XX_CTRL_12_24); 98 reg |= ABX8XX_CTRL_WRITE; 99 abcrtc_reg_write(sc, ABX8XX_CTRL, reg); 100 101 abcrtc_reg_write(sc, ABX8XX_CD_TIMER_CTL, ABX8XX_CD_TIMER_CTL_EN); 102 103 sc->sc_todr.cookie = sc; 104 sc->sc_todr.todr_gettime = abcrtc_gettime; 105 sc->sc_todr.todr_settime = abcrtc_settime; 106 todr_handle = &sc->sc_todr; 107 108 printf("\n"); 109 } 110 111 int 112 abcrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 113 { 114 struct abcrtc_softc *sc = handle->cookie; 115 struct clock_ymdhms dt; 116 int error; 117 118 error = abcrtc_clock_read(sc, &dt); 119 if (error) 120 return error; 121 122 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 || 123 dt.dt_day > 31 || dt.dt_day == 0 || 124 dt.dt_mon > 12 || dt.dt_mon == 0 || 125 dt.dt_year < POSIX_BASE_YEAR) 126 return EINVAL; 127 128 tv->tv_sec = clock_ymdhms_to_secs(&dt); 129 tv->tv_usec = 0; 130 return 0; 131 } 132 133 int 134 abcrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 135 { 136 struct abcrtc_softc *sc = handle->cookie; 137 struct clock_ymdhms dt; 138 139 clock_secs_to_ymdhms(tv->tv_sec, &dt); 140 141 return abcrtc_clock_write(sc, &dt); 142 } 143 144 uint8_t 145 abcrtc_reg_read(struct abcrtc_softc *sc, int reg) 146 { 147 uint8_t cmd = reg; 148 uint8_t val[2]; 149 int error; 150 151 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 152 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 153 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 154 iic_release_bus(sc->sc_tag, I2C_F_POLL); 155 156 if (error) { 157 printf("%s: can't read register 0x%02x\n", 158 sc->sc_dev.dv_xname, reg); 159 return 0xff; 160 } 161 162 return val[1]; 163 } 164 165 void 166 abcrtc_reg_write(struct abcrtc_softc *sc, int reg, uint8_t data) 167 { 168 uint8_t cmd = reg; 169 uint8_t val[2]; 170 int error; 171 172 val[0] = 1; 173 val[1] = reg; 174 175 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 176 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 177 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 178 iic_release_bus(sc->sc_tag, I2C_F_POLL); 179 180 if (error) { 181 printf("%s: can't write register 0x%02x\n", 182 sc->sc_dev.dv_xname, reg); 183 } 184 } 185 186 int 187 abcrtc_clock_read(struct abcrtc_softc *sc, struct clock_ymdhms *dt) 188 { 189 uint8_t regs[ABX8XX_NRTC_REGS+1]; 190 uint8_t cmd = ABX8XX_HTH; 191 int error; 192 193 /* Don't trust the RTC if the oscillator failure is set. */ 194 if ((abcrtc_reg_read(sc, ABX8XX_OSS) & ABX8XX_OSS_OMODE) == 0 && 195 (abcrtc_reg_read(sc, ABX8XX_OSS) & ABX8XX_OSS_OF) != 0) 196 return EIO; 197 198 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 199 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 200 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL); 201 iic_release_bus(sc->sc_tag, I2C_F_POLL); 202 203 if (error) { 204 printf("%s: can't read RTC\n", sc->sc_dev.dv_xname); 205 return error; 206 } 207 208 /* 209 * Convert the ABX8XX's register values into something useable. 210 */ 211 dt->dt_sec = FROMBCD(regs[2] & 0x7f); 212 dt->dt_min = FROMBCD(regs[3] & 0x7f); 213 dt->dt_hour = FROMBCD(regs[4] & 0x3f); 214 dt->dt_day = FROMBCD(regs[5] & 0x3f); 215 dt->dt_mon = FROMBCD(regs[6] & 0x1f); 216 dt->dt_year = FROMBCD(regs[7]) + 2000; 217 dt->dt_wday = regs[8] & 0x7; 218 219 return 0; 220 } 221 222 int 223 abcrtc_clock_write(struct abcrtc_softc *sc, struct clock_ymdhms *dt) 224 { 225 uint8_t regs[ABX8XX_NRTC_REGS+1]; 226 uint8_t cmd = ABX8XX_HTH; 227 uint8_t reg; 228 int error; 229 230 /* 231 * Convert our time representation into something the ABX8XX 232 * can understand. 233 */ 234 regs[0] = ABX8XX_NRTC_REGS; 235 regs[1] = 0; 236 regs[2] = TOBCD(dt->dt_sec); 237 regs[3] = TOBCD(dt->dt_min); 238 regs[4] = TOBCD(dt->dt_hour); 239 regs[5] = TOBCD(dt->dt_day); 240 regs[6] = TOBCD(dt->dt_mon); 241 regs[7] = TOBCD(dt->dt_year - 2000); 242 regs[8] = dt->dt_wday; 243 244 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 245 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 246 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL); 247 iic_release_bus(sc->sc_tag, I2C_F_POLL); 248 249 if (error) { 250 printf("%s: can't write RTC\n", sc->sc_dev.dv_xname); 251 return error; 252 } 253 254 /* Clear oscillator failure. */ 255 reg = abcrtc_reg_read(sc, ABX8XX_OSS); 256 reg &= ~ABX8XX_OSS_OF; 257 abcrtc_reg_write(sc, ABX8XX_OSS, reg); 258 259 return 0; 260 } 261