1 /* $OpenBSD: dapmic.c,v 1.1 2021/06/16 12:37:24 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 #include <sys/malloc.h> 22 23 #include <dev/ofw/openfirm.h> 24 #include <dev/ofw/ofw_regulator.h> 25 #include <dev/ofw/fdt.h> 26 27 #include <dev/i2c/i2cvar.h> 28 29 #include <dev/clock_subr.h> 30 31 extern void (*cpuresetfn)(void); 32 extern void (*powerdownfn)(void); 33 34 /* Registers */ 35 #define EVENT_A 0x06 36 #define CONTROL_F 0x13 37 #define CONTROL_F_WAKE_UP (1 << 2) 38 #define CONTROL_F_SHUTDOWN (1 << 1) 39 #define COUNT_S 0x40 40 #define COUNT_S_COUNT_SEC 0x3f 41 #define COUNT_MI 0x41 42 #define COUNT_MI_COUNT_MIN 0x3f 43 #define COUNT_H 0x42 44 #define COUNT_H_COUNT_HOUR 0x1f 45 #define COUNT_D 0x43 46 #define COUNT_D_COUNT_DAY 0x1f 47 #define COUNT_MO 0x44 48 #define COUNT_MO_COUNT_MONTH 0x0f 49 #define COUNT_Y 0x45 50 #define COUNT_Y_MONITOR (1 << 6) 51 #define COUNT_Y_COUNT_YEAR 0x3f 52 #define ALARM_MO 0x4a 53 #define ALARM_MO_TICK_WAKE (1 << 5) 54 #define ALARM_MO_TICK_TYPE (1 << 4) 55 #define ALARM_Y 0x4b 56 #define ALARM_Y_TICK_ON (1 << 7) 57 58 struct dapmic_softc { 59 struct device sc_dev; 60 i2c_tag_t sc_tag; 61 i2c_addr_t sc_addr; 62 63 struct todr_chip_handle sc_todr; 64 }; 65 66 int dapmic_match(struct device *, void *, void *); 67 void dapmic_attach(struct device *, struct device *, void *); 68 69 struct cfattach dapmic_ca = { 70 sizeof(struct dapmic_softc), dapmic_match, dapmic_attach 71 }; 72 73 struct cfdriver dapmic_cd = { 74 NULL, "dapmic", DV_DULL 75 }; 76 77 uint8_t dapmic_reg_read(struct dapmic_softc *, int); 78 void dapmic_reg_write(struct dapmic_softc *, int, uint8_t); 79 int dapmic_clock_read(struct dapmic_softc *, struct clock_ymdhms *); 80 int dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *); 81 int dapmic_gettime(struct todr_chip_handle *, struct timeval *); 82 int dapmic_settime(struct todr_chip_handle *, struct timeval *); 83 void dapmic_reset(void); 84 void dapmic_powerdown(void); 85 86 int 87 dapmic_match(struct device *parent, void *match, void *aux) 88 { 89 struct i2c_attach_args *ia = aux; 90 91 return (strcmp(ia->ia_name, "dlg,da9063") == 0); 92 } 93 94 void 95 dapmic_attach(struct device *parent, struct device *self, void *aux) 96 { 97 struct dapmic_softc *sc = (struct dapmic_softc *)self; 98 struct i2c_attach_args *ia = aux; 99 100 sc->sc_tag = ia->ia_tag; 101 sc->sc_addr = ia->ia_addr; 102 103 sc->sc_todr.cookie = sc; 104 sc->sc_todr.todr_gettime = dapmic_gettime; 105 sc->sc_todr.todr_settime = dapmic_settime; 106 todr_attach(&sc->sc_todr); 107 108 printf("\n"); 109 110 if (cpuresetfn == NULL) 111 cpuresetfn = dapmic_reset; 112 if (powerdownfn == NULL) 113 powerdownfn = dapmic_powerdown; 114 } 115 116 uint8_t 117 dapmic_reg_read(struct dapmic_softc *sc, int reg) 118 { 119 uint8_t cmd = reg; 120 uint8_t val; 121 int error; 122 123 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 124 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 125 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 126 iic_release_bus(sc->sc_tag, I2C_F_POLL); 127 128 if (error) { 129 printf("%s: can't read register 0x%02x\n", 130 sc->sc_dev.dv_xname, reg); 131 val = 0xff; 132 } 133 134 return val; 135 } 136 137 void 138 dapmic_reg_write(struct dapmic_softc *sc, int reg, uint8_t val) 139 { 140 uint8_t cmd = reg; 141 int error; 142 143 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 144 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 145 &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL); 146 iic_release_bus(sc->sc_tag, I2C_F_POLL); 147 148 if (error) { 149 printf("%s: can't write register 0x%02x\n", 150 sc->sc_dev.dv_xname, reg); 151 } 152 } 153 154 int 155 dapmic_clock_read(struct dapmic_softc *sc, struct clock_ymdhms *dt) 156 { 157 uint8_t regs[6]; 158 uint8_t cmd = COUNT_S; 159 int error; 160 161 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 162 error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 163 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL); 164 iic_release_bus(sc->sc_tag, I2C_F_POLL); 165 166 if (error) 167 return error; 168 169 dt->dt_sec = (regs[0] & COUNT_S_COUNT_SEC); 170 dt->dt_min = (regs[1] & COUNT_MI_COUNT_MIN); 171 dt->dt_hour = (regs[2] & COUNT_H_COUNT_HOUR); 172 dt->dt_day = (regs[3] & COUNT_D_COUNT_DAY); 173 dt->dt_mon = (regs[4] & COUNT_MO_COUNT_MONTH); 174 dt->dt_year = (regs[5] & COUNT_Y_COUNT_YEAR) + 2000; 175 176 /* Consider the time to be invalid if the MONITOR bit isn't set. */ 177 if ((regs[5] & COUNT_Y_MONITOR) == 0) 178 return EINVAL; 179 180 return 0; 181 } 182 183 int 184 dapmic_clock_write(struct dapmic_softc *sc, struct clock_ymdhms *dt) 185 { 186 uint8_t regs[6]; 187 uint8_t cmd = COUNT_S; 188 int error; 189 190 regs[0] = dt->dt_sec; 191 regs[1] = dt->dt_min; 192 regs[2] = dt->dt_hour; 193 regs[3] = dt->dt_day; 194 regs[4] = dt->dt_mon; 195 regs[5] = (dt->dt_year - 2000) | COUNT_Y_MONITOR; 196 197 iic_acquire_bus(sc->sc_tag, I2C_F_POLL); 198 error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 199 &cmd, sizeof(cmd), regs, sizeof(regs), I2C_F_POLL); 200 iic_release_bus(sc->sc_tag, I2C_F_POLL); 201 202 if (error) 203 return error; 204 205 return 0; 206 } 207 208 int 209 dapmic_gettime(struct todr_chip_handle *handle, struct timeval *tv) 210 { 211 struct dapmic_softc *sc = handle->cookie; 212 struct clock_ymdhms dt; 213 int error; 214 215 error = dapmic_clock_read(sc, &dt); 216 if (error) 217 return error; 218 219 if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 || 220 dt.dt_day > 31 || dt.dt_day == 0 || 221 dt.dt_mon > 12 || dt.dt_mon == 0 || 222 dt.dt_year < POSIX_BASE_YEAR) 223 return EINVAL; 224 225 tv->tv_sec = clock_ymdhms_to_secs(&dt); 226 tv->tv_usec = 0; 227 return 0; 228 } 229 230 int 231 dapmic_settime(struct todr_chip_handle *handle, struct timeval *tv) 232 { 233 struct dapmic_softc *sc = handle->cookie; 234 struct clock_ymdhms dt; 235 236 clock_secs_to_ymdhms(tv->tv_sec, &dt); 237 238 return dapmic_clock_write(sc, &dt); 239 } 240 241 void 242 dapmic_reset(void) 243 { 244 struct dapmic_softc *sc = dapmic_cd.cd_devs[0]; 245 uint8_t reg; 246 247 /* Enable tick alarm wakeup with a one second interval. */ 248 reg = dapmic_reg_read(sc, ALARM_MO); 249 reg &= ~ALARM_MO_TICK_TYPE; 250 reg |= ALARM_MO_TICK_WAKE; 251 dapmic_reg_write(sc, ALARM_MO, reg); 252 253 /* Enable tick function. */ 254 reg = dapmic_reg_read(sc, ALARM_Y); 255 reg |= ALARM_Y_TICK_ON; 256 dapmic_reg_write(sc, ALARM_Y, reg); 257 258 /* Clear events such that we wake up again. */ 259 dapmic_reg_write(sc, EVENT_A, dapmic_reg_read(sc, EVENT_A)); 260 dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN); 261 } 262 263 void 264 dapmic_powerdown(void) 265 { 266 struct dapmic_softc *sc = dapmic_cd.cd_devs[0]; 267 uint8_t reg; 268 269 /* Disable tick function such that it doesn't wake us up. */ 270 reg = dapmic_reg_read(sc, ALARM_Y); 271 reg &= ~ALARM_Y_TICK_ON; 272 dapmic_reg_write(sc, ALARM_Y, reg); 273 274 dapmic_reg_write(sc, CONTROL_F, CONTROL_F_SHUTDOWN); 275 } 276