1 /* $OpenBSD: m41t8x.c,v 1.1 2020/09/30 22:23:41 patrick Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Miodrag Vallat. 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 /* 20 * M41T8x clock connected to an I2C bus 21 */ 22 23 #include <sys/param.h> 24 #include <sys/kernel.h> 25 #include <sys/systm.h> 26 #include <sys/device.h> 27 28 #include <dev/clock_subr.h> 29 #include <dev/i2c/i2cvar.h> 30 #include <dev/ic/m41t8xreg.h> 31 32 struct m41t8xrtc_softc { 33 struct device sc_dev; 34 struct todr_chip_handle sc_todr; 35 i2c_tag_t sc_tag; 36 i2c_addr_t sc_addr; 37 }; 38 39 int m41t8xrtc_match(struct device *, void *, void *); 40 void m41t8xrtc_attach(struct device *, struct device *, void *); 41 42 const struct cfattach mfokrtc_ca = { 43 sizeof(struct m41t8xrtc_softc), 44 m41t8xrtc_match, m41t8xrtc_attach 45 }; 46 47 struct cfdriver mfokrtc_cd = { 48 NULL, "mfokrtc", DV_DULL 49 }; 50 51 int m41t8xrtc_gettime(struct todr_chip_handle *, struct timeval *); 52 int m41t8xrtc_settime(struct todr_chip_handle *, struct timeval *); 53 54 int 55 m41t8xrtc_match(struct device *parent, void *vcf, void *aux) 56 { 57 struct i2c_attach_args *ia = (struct i2c_attach_args *)aux; 58 59 if (strcmp(ia->ia_name, "st,m41t83") == 0) 60 return (1); 61 return (0); 62 63 } 64 65 void 66 m41t8xrtc_attach(struct device *parent, struct device *self, void *aux) 67 { 68 struct m41t8xrtc_softc *sc = (struct m41t8xrtc_softc *)self; 69 struct i2c_attach_args *ia = (struct i2c_attach_args *)aux; 70 71 sc->sc_tag = ia->ia_tag; 72 sc->sc_addr = ia->ia_addr; 73 74 sc->sc_todr.cookie = sc; 75 sc->sc_todr.todr_gettime = m41t8xrtc_gettime; 76 sc->sc_todr.todr_settime = m41t8xrtc_settime; 77 todr_attach(&sc->sc_todr); 78 79 printf("\n"); 80 } 81 82 int 83 m41t8xrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 84 { 85 struct clock_ymdhms dt; 86 struct m41t8xrtc_softc *sc = handle->cookie; 87 uint8_t regno, data[M41T8X_TOD_LENGTH]; 88 int s; 89 90 iic_acquire_bus(sc->sc_tag, 0); 91 s = splclock(); 92 for (regno = M41T8X_TOD_START; 93 regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++) 94 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 95 ®no, sizeof regno, data + regno - M41T8X_TOD_START, 96 sizeof data[0], 0); 97 splx(s); 98 iic_release_bus(sc->sc_tag, 0); 99 100 dt.dt_sec = FROMBCD(data[M41T8X_SEC] & ~M41T8X_STOP); 101 dt.dt_min = FROMBCD(data[M41T8X_MIN]); 102 dt.dt_hour = FROMBCD(data[M41T8X_HR] & ~(M41T8X_CEB | M41T8X_CB)); 103 dt.dt_day = FROMBCD(data[M41T8X_DAY]); 104 dt.dt_mon = FROMBCD(data[M41T8X_MON]); 105 dt.dt_year = FROMBCD(data[M41T8X_YEAR]) + 2000; 106 if (data[M41T8X_HR] & M41T8X_CB) 107 dt.dt_year += 100; 108 109 tv->tv_sec = clock_ymdhms_to_secs(&dt); 110 tv->tv_usec = 0; 111 return 0; 112 } 113 114 int 115 m41t8xrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 116 { 117 struct clock_ymdhms dt; 118 struct m41t8xrtc_softc *sc = handle->cookie; 119 uint8_t regno, data[M41T8X_TOD_LENGTH]; 120 int s; 121 122 clock_secs_to_ymdhms(tv->tv_sec, &dt); 123 124 iic_acquire_bus(sc->sc_tag, 0); 125 s = splclock(); 126 /* read current state */ 127 for (regno = M41T8X_TOD_START; 128 regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++) 129 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 130 ®no, sizeof regno, data + regno - M41T8X_TOD_START, 131 sizeof data[0], 0); 132 /* compute new state */ 133 data[M41T8X_HSEC] = 0; 134 data[M41T8X_SEC] = TOBCD(dt.dt_sec); 135 data[M41T8X_MIN] = TOBCD(dt.dt_min); 136 data[M41T8X_HR] &= M41T8X_CEB; 137 if (dt.dt_year >= 2100) 138 data[M41T8X_HR] |= M41T8X_CB; 139 data[M41T8X_HR] |= TOBCD(dt.dt_hour); 140 data[M41T8X_DOW] = TOBCD(dt.dt_wday + 1); 141 data[M41T8X_DAY] = TOBCD(dt.dt_day); 142 data[M41T8X_MON] = TOBCD(dt.dt_mon); 143 data[M41T8X_YEAR] = TOBCD(dt.dt_year % 100); 144 /* write new state */ 145 for (regno = M41T8X_TOD_START; 146 regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++) 147 iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 148 ®no, sizeof regno, data + regno, sizeof data[0], 0); 149 splx(s); 150 iic_release_bus(sc->sc_tag, 0); 151 152 return 0; 153 } 154