1 /* $OpenBSD: m41t8x.c,v 1.2 2020/10/23 20:55:15 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 strcmp(ia->ia_name, "microcrystal,rv4162") == 0) 61 return (1); 62 return (0); 63 64 } 65 66 void 67 m41t8xrtc_attach(struct device *parent, struct device *self, void *aux) 68 { 69 struct m41t8xrtc_softc *sc = (struct m41t8xrtc_softc *)self; 70 struct i2c_attach_args *ia = (struct i2c_attach_args *)aux; 71 72 sc->sc_tag = ia->ia_tag; 73 sc->sc_addr = ia->ia_addr; 74 75 sc->sc_todr.cookie = sc; 76 sc->sc_todr.todr_gettime = m41t8xrtc_gettime; 77 sc->sc_todr.todr_settime = m41t8xrtc_settime; 78 todr_attach(&sc->sc_todr); 79 80 printf("\n"); 81 } 82 83 int 84 m41t8xrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) 85 { 86 struct clock_ymdhms dt; 87 struct m41t8xrtc_softc *sc = handle->cookie; 88 uint8_t regno, data[M41T8X_TOD_LENGTH]; 89 int s; 90 91 iic_acquire_bus(sc->sc_tag, 0); 92 s = splclock(); 93 for (regno = M41T8X_TOD_START; 94 regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++) 95 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 96 ®no, sizeof regno, data + regno - M41T8X_TOD_START, 97 sizeof data[0], 0); 98 splx(s); 99 iic_release_bus(sc->sc_tag, 0); 100 101 dt.dt_sec = FROMBCD(data[M41T8X_SEC] & ~M41T8X_STOP); 102 dt.dt_min = FROMBCD(data[M41T8X_MIN]); 103 dt.dt_hour = FROMBCD(data[M41T8X_HR] & ~(M41T8X_CEB | M41T8X_CB)); 104 dt.dt_day = FROMBCD(data[M41T8X_DAY]); 105 dt.dt_mon = FROMBCD(data[M41T8X_MON]); 106 dt.dt_year = FROMBCD(data[M41T8X_YEAR]) + 2000; 107 if (data[M41T8X_HR] & M41T8X_CB) 108 dt.dt_year += 100; 109 110 tv->tv_sec = clock_ymdhms_to_secs(&dt); 111 tv->tv_usec = 0; 112 return 0; 113 } 114 115 int 116 m41t8xrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) 117 { 118 struct clock_ymdhms dt; 119 struct m41t8xrtc_softc *sc = handle->cookie; 120 uint8_t regno, data[M41T8X_TOD_LENGTH]; 121 int s; 122 123 clock_secs_to_ymdhms(tv->tv_sec, &dt); 124 125 iic_acquire_bus(sc->sc_tag, 0); 126 s = splclock(); 127 /* read current state */ 128 for (regno = M41T8X_TOD_START; 129 regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++) 130 iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, 131 ®no, sizeof regno, data + regno - M41T8X_TOD_START, 132 sizeof data[0], 0); 133 /* compute new state */ 134 data[M41T8X_HSEC] = 0; 135 data[M41T8X_SEC] = TOBCD(dt.dt_sec); 136 data[M41T8X_MIN] = TOBCD(dt.dt_min); 137 data[M41T8X_HR] &= M41T8X_CEB; 138 if (dt.dt_year >= 2100) 139 data[M41T8X_HR] |= M41T8X_CB; 140 data[M41T8X_HR] |= TOBCD(dt.dt_hour); 141 data[M41T8X_DOW] &= ~M41T8X_DOW_MASK; 142 data[M41T8X_DOW] |= TOBCD(dt.dt_wday + 1); 143 data[M41T8X_DAY] = TOBCD(dt.dt_day); 144 data[M41T8X_MON] = TOBCD(dt.dt_mon); 145 data[M41T8X_YEAR] = TOBCD(dt.dt_year % 100); 146 /* write new state */ 147 for (regno = M41T8X_TOD_START; 148 regno < M41T8X_TOD_START + M41T8X_TOD_LENGTH; regno++) 149 iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr, 150 ®no, sizeof regno, data + regno, sizeof data[0], 0); 151 splx(s); 152 iic_release_bus(sc->sc_tag, 0); 153 154 return 0; 155 } 156