1 /* $OpenBSD: abx80x.c,v 1.8 2022/10/15 18:22:53 kettenis 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 #if defined(__HAVE_FDT) 28 #include <dev/ofw/openfirm.h> 29 #endif 30 31 #define ABX8XX_HTH 0x00 32 #define ABX8XX_SC 0x01 33 #define ABX8XX_MN 0x02 34 #define ABX8XX_HR 0x03 35 #define ABX8XX_DA 0x04 36 #define ABX8XX_MO 0x05 37 #define ABX8XX_YR 0x06 38 #define ABX8XX_WD 0x07 39 #define ABX8XX_CTRL 0x10 40 #define ABX8XX_CTRL_WRITE (1 << 0) 41 #define ABX8XX_CTRL_ARST (1 << 2) 42 #define ABX8XX_CTRL_12_24 (1 << 6) 43 #define ABX8XX_CD_TIMER_CTL 0x18 44 #define ABX8XX_CD_TIMER_CTL_EN (1 << 2) 45 #define ABX8XX_OSS 0x1d 46 #define ABX8XX_OSS_OF (1 << 1) 47 #define ABX8XX_OSS_OMODE (1 << 4) 48 #define ABX8XX_CFG_KEY 0x1f 49 #define ABX8XX_CFG_KEY_OSC 0xa1 50 #define ABX8XX_CFG_KEY_MISC 0x9d 51 #define ABX8XX_TRICKLE 0x20 52 #define ABX8XX_TRICKLE_RESISTOR_0 (0 << 0) 53 #define ABX8XX_TRICKLE_RESISTOR_3 (1 << 0) 54 #define ABX8XX_TRICKLE_RESISTOR_6 (2 << 0) 55 #define ABX8XX_TRICKLE_RESISTOR_11 (3 << 0) 56 #define ABX8XX_TRICKLE_DIODE_SCHOTTKY (1 << 2) 57 #define ABX8XX_TRICKLE_DIODE_STANDARD (2 << 2) 58 #define ABX8XX_TRICKLE_ENABLE (0xa << 4) 59 60 #define ABX8XX_NRTC_REGS 8 61 62 struct abcrtc_softc { 63 struct device sc_dev; 64 i2c_tag_t sc_tag; 65 i2c_addr_t sc_addr; 66 67 struct todr_chip_handle sc_todr; 68 }; 69 70 int abcrtc_match(struct device *, void *, void *); 71 void abcrtc_attach(struct device *, struct device *, void *); 72 73 const struct cfattach abcrtc_ca = { 74 sizeof(struct abcrtc_softc), abcrtc_match, abcrtc_attach 75 }; 76 77 struct cfdriver abcrtc_cd = { 78 NULL, "abcrtc", DV_DULL 79 }; 80 81 uint8_t abcrtc_reg_read(struct abcrtc_softc *, int); 82 void abcrtc_reg_write(struct abcrtc_softc *, int, uint8_t); 83 int abcrtc_clock_read(struct abcrtc_softc *, struct clock_ymdhms *); 84 int abcrtc_clock_write(struct abcrtc_softc *, struct clock_ymdhms *); 85 int abcrtc_gettime(struct todr_chip_handle *, struct timeval *); 86 int abcrtc_settime(struct todr_chip_handle *, struct timeval *); 87 88 #if defined(__HAVE_FDT) 89 void abcrtc_trickle_charger(struct abcrtc_softc *, int); 90 #endif 91 92 int 93 abcrtc_match(struct device *parent, void *match, void *aux) 94 { 95 struct i2c_attach_args *ia = aux; 96 97 if (strcmp(ia->ia_name, "abracon,ab1805") == 0) 98 return 1; 99 100 return 0; 101 } 102 103 void 104 abcrtc_attach(struct device *parent, struct device *self, void *aux) 105 { 106 struct abcrtc_softc *sc = (struct abcrtc_softc *)self; 107 struct i2c_attach_args *ia = aux; 108 uint8_t reg; 109 #if defined(__HAVE_FDT) 110 int node = *(int *)ia->ia_cookie; 111 #endif 112 113 sc->sc_tag = ia->ia_tag; 114 sc->sc_addr = ia->ia_addr; 115 116 reg = abcrtc_reg_read(sc, ABX8XX_CTRL); 117 reg &= ~(ABX8XX_CTRL_ARST | ABX8XX_CTRL_12_24); 118 reg |= ABX8XX_CTRL_WRITE; 119 abcrtc_reg_write(sc, ABX8XX_CTRL, reg); 120 121 #if defined(__HAVE_FDT) 122 abcrtc_trickle_charger(sc, node); 123 #endif 124 125 abcrtc_reg_write(sc, ABX8XX_CD_TIMER_CTL, ABX8XX_CD_TIMER_CTL_EN); 126 127 sc->sc_todr.cookie = sc; 128 sc->sc_todr.todr_gettime = abcrtc_gettime; 129 sc->sc_todr.todr_settime = abcrtc_settime; 130 sc->sc_todr.todr_quality = 1000; 131 todr_attach(&sc->sc_todr); 132 133 printf("\n"); 134 } 135 136 int 137 abcrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 138 { 139 struct abcrtc_softc *sc = handle->cookie; 140 struct clock_ymdhms dt; 141 int error; 142 143 error = abcrtc_clock_read(sc, &dt); 144 if (error) 145 return error; 146 147 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 || 148 dt.dt_day > 31 || dt.dt_day == 0 || 149 dt.dt_mon > 12 || dt.dt_mon == 0 || 150 dt.dt_year < POSIX_BASE_YEAR) 151 return EINVAL; 152 153 tv->tv_sec = clock_ymdhms_to_secs(&dt); 154 tv->tv_usec = 0; 155 return 0; 156 } 157 158 int 159 abcrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 160 { 161 struct abcrtc_softc *sc = handle->cookie; 162 struct clock_ymdhms dt; 163 164 clock_secs_to_ymdhms(tv->tv_sec, &dt); 165 166 return abcrtc_clock_write(sc, &dt); 167 } 168 169 uint8_t 170 abcrtc_reg_read(struct abcrtc_softc *sc, int reg) 171 { 172 uint8_t cmd = reg; 173 uint8_t val[2]; 174 int error; 175 176 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 177 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 178 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 179 iic_release_bus(sc->sc_tag, I2C_F_POLL); 180 181 if (error) { 182 printf("%s: can't read register 0x%02x\n", 183 sc->sc_dev.dv_xname, reg); 184 return 0xff; 185 } 186 187 return val[1]; 188 } 189 190 void 191 abcrtc_reg_write(struct abcrtc_softc *sc, int reg, uint8_t data) 192 { 193 uint8_t cmd = reg; 194 uint8_t val[2]; 195 int error; 196 197 val[0] = 1; 198 val[1] = data; 199 200 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 201 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 202 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 203 iic_release_bus(sc->sc_tag, I2C_F_POLL); 204 205 if (error) { 206 printf("%s: can't write register 0x%02x\n", 207 sc->sc_dev.dv_xname, reg); 208 } 209 } 210 211 int 212 abcrtc_clock_read(struct abcrtc_softc *sc, struct clock_ymdhms *dt) 213 { 214 uint8_t regs[ABX8XX_NRTC_REGS]; 215 uint8_t cmd = ABX8XX_HTH; 216 int error; 217 218 /* Don't trust the RTC if the oscillator failure is set. */ 219 if ((abcrtc_reg_read(sc, ABX8XX_OSS) & ABX8XX_OSS_OMODE) == 0 && 220 (abcrtc_reg_read(sc, ABX8XX_OSS) & ABX8XX_OSS_OF) != 0) 221 return EIO; 222 223 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 224 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 225 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL); 226 iic_release_bus(sc->sc_tag, I2C_F_POLL); 227 228 if (error) { 229 printf("%s: can't read RTC\n", sc->sc_dev.dv_xname); 230 return error; 231 } 232 233 /* 234 * Convert the ABX8XX's register values into something useable. 235 */ 236 dt->dt_sec = FROMBCD(regs[1] & 0x7f); 237 dt->dt_min = FROMBCD(regs[2] & 0x7f); 238 dt->dt_hour = FROMBCD(regs[3] & 0x3f); 239 dt->dt_day = FROMBCD(regs[4] & 0x3f); 240 dt->dt_mon = FROMBCD(regs[5] & 0x1f); 241 dt->dt_year = FROMBCD(regs[6]) + 2000; 242 dt->dt_wday = regs[7] & 0x7; 243 244 return 0; 245 } 246 247 int 248 abcrtc_clock_write(struct abcrtc_softc *sc, struct clock_ymdhms *dt) 249 { 250 uint8_t regs[ABX8XX_NRTC_REGS]; 251 uint8_t cmd = ABX8XX_HTH; 252 uint8_t reg; 253 int error; 254 255 /* 256 * Convert our time representation into something the ABX8XX 257 * can understand. 258 */ 259 regs[0] = 0; 260 regs[1] = TOBCD(dt->dt_sec); 261 regs[2] = TOBCD(dt->dt_min); 262 regs[3] = TOBCD(dt->dt_hour); 263 regs[4] = TOBCD(dt->dt_day); 264 regs[5] = TOBCD(dt->dt_mon); 265 regs[6] = TOBCD(dt->dt_year - 2000); 266 regs[7] = dt->dt_wday; 267 268 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 269 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 270 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL); 271 iic_release_bus(sc->sc_tag, I2C_F_POLL); 272 273 if (error) { 274 printf("%s: can't write RTC\n", sc->sc_dev.dv_xname); 275 return error; 276 } 277 278 /* Clear oscillator failure. */ 279 reg = abcrtc_reg_read(sc, ABX8XX_OSS); 280 reg &= ~ABX8XX_OSS_OF; 281 abcrtc_reg_write(sc, ABX8XX_OSS, reg); 282 283 return 0; 284 } 285 286 #if defined(__HAVE_FDT) 287 void 288 abcrtc_trickle_charger(struct abcrtc_softc *sc, int node) 289 { 290 char diode[16] = { 0 }; 291 uint8_t reg = ABX8XX_TRICKLE_ENABLE; 292 293 OF_getprop(node, "abracon,tc-diode", diode, sizeof(diode)); 294 if (!strcmp(diode, "standard")) 295 reg |= ABX8XX_TRICKLE_DIODE_STANDARD; 296 else if (!strcmp(diode, "schottky")) 297 reg |= ABX8XX_TRICKLE_DIODE_SCHOTTKY; 298 else 299 return; 300 301 switch (OF_getpropint(node, "abracon,tc-resistor", -1)) { 302 case 0: 303 reg |= ABX8XX_TRICKLE_RESISTOR_0; 304 break; 305 case 3: 306 reg |= ABX8XX_TRICKLE_RESISTOR_3; 307 break; 308 case 6: 309 reg |= ABX8XX_TRICKLE_RESISTOR_6; 310 break; 311 case 11: 312 reg |= ABX8XX_TRICKLE_RESISTOR_11; 313 break; 314 default: 315 return; 316 } 317 318 abcrtc_reg_write(sc, ABX8XX_CFG_KEY, ABX8XX_CFG_KEY_MISC); 319 abcrtc_reg_write(sc, ABX8XX_TRICKLE, reg); 320 } 321 #endif 322