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