dapmic.c revision 1.1
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 31extern void (*cpuresetfn)(void); 32extern 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 58struct 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 66int dapmic_match(struct device *, void *, void *); 67void dapmic_attach(struct device *, struct device *, void *); 68 69struct cfattach dapmic_ca = { 70 sizeof(struct dapmic_softc), dapmic_match, dapmic_attach 71}; 72 73struct cfdriver dapmic_cd = { 74 NULL, "dapmic", DV_DULL 75}; 76 77uint8_t dapmic_reg_read(struct dapmic_softc *, int); 78void dapmic_reg_write(struct dapmic_softc *, int, uint8_t); 79int dapmic_clock_read(struct dapmic_softc *, struct clock_ymdhms *); 80int dapmic_clock_write(struct dapmic_softc *, struct clock_ymdhms *); 81int dapmic_gettime(struct todr_chip_handle *, struct timeval *); 82int dapmic_settime(struct todr_chip_handle *, struct timeval *); 83void dapmic_reset(void); 84void dapmic_powerdown(void); 85 86int 87dapmic_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 94void 95dapmic_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 116uint8_t 117dapmic_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 137void 138dapmic_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 154int 155dapmic_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 183int 184dapmic_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 208int 209dapmic_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 230int 231dapmic_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 241void 242dapmic_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 263void 264dapmic_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