1*23c413afSkettenis /* $OpenBSD: ds3231.c,v 1.3 2022/10/15 18:22:53 kettenis Exp $ */
2739fd7cfSkettenis /*
3739fd7cfSkettenis * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
4739fd7cfSkettenis *
5739fd7cfSkettenis * Permission to use, copy, modify, and distribute this software for any
6739fd7cfSkettenis * purpose with or without fee is hereby granted, provided that the above
7739fd7cfSkettenis * copyright notice and this permission notice appear in all copies.
8739fd7cfSkettenis *
9739fd7cfSkettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10739fd7cfSkettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11739fd7cfSkettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12739fd7cfSkettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13739fd7cfSkettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14739fd7cfSkettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15739fd7cfSkettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16739fd7cfSkettenis */
17739fd7cfSkettenis
18739fd7cfSkettenis #include <sys/param.h>
19739fd7cfSkettenis #include <sys/systm.h>
20739fd7cfSkettenis #include <sys/device.h>
21739fd7cfSkettenis
22739fd7cfSkettenis #include <dev/i2c/i2cvar.h>
23739fd7cfSkettenis
24739fd7cfSkettenis #include <dev/clock_subr.h>
25739fd7cfSkettenis
26739fd7cfSkettenis #define DS3231_SC 0x00
27739fd7cfSkettenis #define DS3231_MN 0x01
28739fd7cfSkettenis #define DS3231_HR 0x02
29739fd7cfSkettenis #define DS3231_HR_PM (1 << 5)
30739fd7cfSkettenis #define DS3231_HR_12 (1 << 6)
31739fd7cfSkettenis #define DS3231_HR_12_MASK ~(DS3231_HR_12 | DS3231_HR_PM)
32739fd7cfSkettenis #define DS3231_DW 0x03
33739fd7cfSkettenis #define DS3231_DT 0x04
34739fd7cfSkettenis #define DS3231_MO 0x05
35739fd7cfSkettenis #define DS3231_MO_MASK 0x3f
36739fd7cfSkettenis #define DS3231_YR 0x06
37739fd7cfSkettenis #define DS3231_SR 0x07
38739fd7cfSkettenis #define DS3231_SR_OSF (1 << 7)
39739fd7cfSkettenis
40739fd7cfSkettenis #define DS3231_NRTC_REGS 7
41739fd7cfSkettenis
42739fd7cfSkettenis struct dsxrtc_softc {
43739fd7cfSkettenis struct device sc_dev;
44739fd7cfSkettenis i2c_tag_t sc_tag;
45739fd7cfSkettenis i2c_addr_t sc_addr;
46739fd7cfSkettenis
47739fd7cfSkettenis struct todr_chip_handle sc_todr;
48739fd7cfSkettenis };
49739fd7cfSkettenis
50739fd7cfSkettenis int dsxrtc_match(struct device *, void *, void *);
51739fd7cfSkettenis void dsxrtc_attach(struct device *, struct device *, void *);
52739fd7cfSkettenis
53471aeecfSnaddy const struct cfattach dsxrtc_ca = {
54739fd7cfSkettenis sizeof(struct dsxrtc_softc), dsxrtc_match, dsxrtc_attach
55739fd7cfSkettenis };
56739fd7cfSkettenis
57739fd7cfSkettenis struct cfdriver dsxrtc_cd = {
58739fd7cfSkettenis NULL, "dsxrtc", DV_DULL
59739fd7cfSkettenis };
60739fd7cfSkettenis
61739fd7cfSkettenis uint8_t dsxrtc_reg_read(struct dsxrtc_softc *, int);
62739fd7cfSkettenis void dsxrtc_reg_write(struct dsxrtc_softc *, int, uint8_t);
63739fd7cfSkettenis int dsxrtc_clock_read(struct dsxrtc_softc *, struct clock_ymdhms *);
64739fd7cfSkettenis int dsxrtc_clock_write(struct dsxrtc_softc *, struct clock_ymdhms *);
65739fd7cfSkettenis int dsxrtc_gettime(struct todr_chip_handle *, struct timeval *);
66739fd7cfSkettenis int dsxrtc_settime(struct todr_chip_handle *, struct timeval *);
67739fd7cfSkettenis
68739fd7cfSkettenis int
dsxrtc_match(struct device * parent,void * match,void * aux)69739fd7cfSkettenis dsxrtc_match(struct device *parent, void *match, void *aux)
70739fd7cfSkettenis {
71739fd7cfSkettenis struct i2c_attach_args *ia = aux;
72739fd7cfSkettenis
73739fd7cfSkettenis if (strcmp(ia->ia_name, "maxim,ds3231") == 0 ||
74739fd7cfSkettenis strcmp(ia->ia_name, "maxim,ds3232") == 0)
75739fd7cfSkettenis return 1;
76739fd7cfSkettenis
77739fd7cfSkettenis return 0;
78739fd7cfSkettenis }
79739fd7cfSkettenis
80739fd7cfSkettenis void
dsxrtc_attach(struct device * parent,struct device * self,void * aux)81739fd7cfSkettenis dsxrtc_attach(struct device *parent, struct device *self, void *aux)
82739fd7cfSkettenis {
83739fd7cfSkettenis struct dsxrtc_softc *sc = (struct dsxrtc_softc *)self;
84739fd7cfSkettenis struct i2c_attach_args *ia = aux;
85739fd7cfSkettenis
86739fd7cfSkettenis sc->sc_tag = ia->ia_tag;
87739fd7cfSkettenis sc->sc_addr = ia->ia_addr;
88739fd7cfSkettenis
89739fd7cfSkettenis sc->sc_todr.cookie = sc;
90739fd7cfSkettenis sc->sc_todr.todr_gettime = dsxrtc_gettime;
91739fd7cfSkettenis sc->sc_todr.todr_settime = dsxrtc_settime;
92*23c413afSkettenis sc->sc_todr.todr_quality = 1000;
93*23c413afSkettenis todr_attach(&sc->sc_todr);
94739fd7cfSkettenis
95739fd7cfSkettenis printf("\n");
96739fd7cfSkettenis }
97739fd7cfSkettenis
98739fd7cfSkettenis int
dsxrtc_gettime(struct todr_chip_handle * handle,struct timeval * tv)99739fd7cfSkettenis dsxrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv)
100739fd7cfSkettenis {
101739fd7cfSkettenis struct dsxrtc_softc *sc = handle->cookie;
102739fd7cfSkettenis struct clock_ymdhms dt;
103739fd7cfSkettenis int error;
104739fd7cfSkettenis
105739fd7cfSkettenis error = dsxrtc_clock_read(sc, &dt);
106739fd7cfSkettenis if (error)
107739fd7cfSkettenis return error;
108739fd7cfSkettenis
109739fd7cfSkettenis if (dt.dt_sec > 59 || dt.dt_min > 59 || dt.dt_hour > 23 ||
110739fd7cfSkettenis dt.dt_day > 31 || dt.dt_day == 0 ||
111739fd7cfSkettenis dt.dt_mon > 12 || dt.dt_mon == 0 ||
112739fd7cfSkettenis dt.dt_year < POSIX_BASE_YEAR)
113739fd7cfSkettenis return EINVAL;
114739fd7cfSkettenis
115739fd7cfSkettenis tv->tv_sec = clock_ymdhms_to_secs(&dt);
116739fd7cfSkettenis tv->tv_usec = 0;
117739fd7cfSkettenis return 0;
118739fd7cfSkettenis }
119739fd7cfSkettenis
120739fd7cfSkettenis int
dsxrtc_settime(struct todr_chip_handle * handle,struct timeval * tv)121739fd7cfSkettenis dsxrtc_settime(struct todr_chip_handle *handle, struct timeval *tv)
122739fd7cfSkettenis {
123739fd7cfSkettenis struct dsxrtc_softc *sc = handle->cookie;
124739fd7cfSkettenis struct clock_ymdhms dt;
125739fd7cfSkettenis
126739fd7cfSkettenis clock_secs_to_ymdhms(tv->tv_sec, &dt);
127739fd7cfSkettenis
128739fd7cfSkettenis return dsxrtc_clock_write(sc, &dt);
129739fd7cfSkettenis
130739fd7cfSkettenis }
131739fd7cfSkettenis
132739fd7cfSkettenis uint8_t
dsxrtc_reg_read(struct dsxrtc_softc * sc,int reg)133739fd7cfSkettenis dsxrtc_reg_read(struct dsxrtc_softc *sc, int reg)
134739fd7cfSkettenis {
135739fd7cfSkettenis uint8_t cmd = reg;
136739fd7cfSkettenis uint8_t val;
137739fd7cfSkettenis int error;
138739fd7cfSkettenis
139739fd7cfSkettenis iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
140739fd7cfSkettenis error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
141739fd7cfSkettenis &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
142739fd7cfSkettenis iic_release_bus(sc->sc_tag, I2C_F_POLL);
143739fd7cfSkettenis
144739fd7cfSkettenis if (error) {
145739fd7cfSkettenis printf("%s: can't read register 0x%02x\n",
146739fd7cfSkettenis sc->sc_dev.dv_xname, reg);
147739fd7cfSkettenis val = 0xff;
148739fd7cfSkettenis }
149739fd7cfSkettenis
150739fd7cfSkettenis return val;
151739fd7cfSkettenis }
152739fd7cfSkettenis
153739fd7cfSkettenis void
dsxrtc_reg_write(struct dsxrtc_softc * sc,int reg,uint8_t val)154739fd7cfSkettenis dsxrtc_reg_write(struct dsxrtc_softc *sc, int reg, uint8_t val)
155739fd7cfSkettenis {
156739fd7cfSkettenis uint8_t cmd = reg;
157739fd7cfSkettenis int error;
158739fd7cfSkettenis
159739fd7cfSkettenis iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
160739fd7cfSkettenis error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
161739fd7cfSkettenis &cmd, sizeof cmd, &val, sizeof val, I2C_F_POLL);
162739fd7cfSkettenis iic_release_bus(sc->sc_tag, I2C_F_POLL);
163739fd7cfSkettenis
164739fd7cfSkettenis if (error) {
165739fd7cfSkettenis printf("%s: can't write register 0x%02x\n",
166739fd7cfSkettenis sc->sc_dev.dv_xname, reg);
167739fd7cfSkettenis }
168739fd7cfSkettenis }
169739fd7cfSkettenis
170739fd7cfSkettenis int
dsxrtc_clock_read(struct dsxrtc_softc * sc,struct clock_ymdhms * dt)171739fd7cfSkettenis dsxrtc_clock_read(struct dsxrtc_softc *sc, struct clock_ymdhms *dt)
172739fd7cfSkettenis {
173739fd7cfSkettenis uint8_t regs[DS3231_NRTC_REGS];
174739fd7cfSkettenis uint8_t cmd = DS3231_SC;
175739fd7cfSkettenis uint8_t status;
176739fd7cfSkettenis int error;
177739fd7cfSkettenis
178739fd7cfSkettenis /* Consider the time to be invalid if the OSF bit is set. */
179739fd7cfSkettenis status = dsxrtc_reg_read(sc, DS3231_SR);
180739fd7cfSkettenis if (status & DS3231_SR_OSF)
181739fd7cfSkettenis return EINVAL;
182739fd7cfSkettenis
183739fd7cfSkettenis iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
184739fd7cfSkettenis error = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr,
185739fd7cfSkettenis &cmd, sizeof(cmd), regs, DS3231_NRTC_REGS, I2C_F_POLL);
186739fd7cfSkettenis iic_release_bus(sc->sc_tag, I2C_F_POLL);
187739fd7cfSkettenis
188739fd7cfSkettenis if (error) {
189739fd7cfSkettenis printf("%s: can't read RTC\n", sc->sc_dev.dv_xname);
190739fd7cfSkettenis return error;
191739fd7cfSkettenis }
192739fd7cfSkettenis
193739fd7cfSkettenis /*
194739fd7cfSkettenis * Convert the DS3231's register values into something useable.
195739fd7cfSkettenis */
196739fd7cfSkettenis dt->dt_sec = FROMBCD(regs[0]);
197739fd7cfSkettenis dt->dt_min = FROMBCD(regs[1]);
198739fd7cfSkettenis if (regs[2] & DS3231_HR_12) {
199739fd7cfSkettenis dt->dt_hour = FROMBCD(regs[2] & DS3231_HR_12_MASK);
200739fd7cfSkettenis if (regs[2] & DS3231_HR_PM)
201739fd7cfSkettenis dt->dt_hour += 12;
202739fd7cfSkettenis } else {
203739fd7cfSkettenis dt->dt_hour = FROMBCD(regs[2]);
204739fd7cfSkettenis }
205739fd7cfSkettenis dt->dt_day = FROMBCD(regs[4]);
206739fd7cfSkettenis dt->dt_mon = FROMBCD(regs[5] & DS3231_MO_MASK);
207739fd7cfSkettenis dt->dt_year = FROMBCD(regs[6]) + 2000;
208739fd7cfSkettenis
209739fd7cfSkettenis return 0;
210739fd7cfSkettenis }
211739fd7cfSkettenis
212739fd7cfSkettenis int
dsxrtc_clock_write(struct dsxrtc_softc * sc,struct clock_ymdhms * dt)213739fd7cfSkettenis dsxrtc_clock_write(struct dsxrtc_softc *sc, struct clock_ymdhms *dt)
214739fd7cfSkettenis {
215739fd7cfSkettenis uint8_t regs[DS3231_NRTC_REGS];
216739fd7cfSkettenis uint8_t cmd = DS3231_SC;
217739fd7cfSkettenis uint8_t status;
218739fd7cfSkettenis int error;
219739fd7cfSkettenis
220739fd7cfSkettenis /*
221739fd7cfSkettenis * Convert our time representation into something the DS3231
222739fd7cfSkettenis * can understand.
223739fd7cfSkettenis */
224739fd7cfSkettenis regs[0] = TOBCD(dt->dt_sec);
225739fd7cfSkettenis regs[1] = TOBCD(dt->dt_min);
226739fd7cfSkettenis regs[2] = TOBCD(dt->dt_hour);
227739fd7cfSkettenis regs[3] = TOBCD(dt->dt_wday + 1);
228739fd7cfSkettenis regs[4] = TOBCD(dt->dt_day);
229739fd7cfSkettenis regs[5] = TOBCD(dt->dt_mon);
230739fd7cfSkettenis regs[6] = TOBCD(dt->dt_year - 2000);
231739fd7cfSkettenis
232739fd7cfSkettenis iic_acquire_bus(sc->sc_tag, I2C_F_POLL);
233739fd7cfSkettenis error = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP, sc->sc_addr,
234739fd7cfSkettenis &cmd, sizeof(cmd), regs, DS3231_NRTC_REGS, I2C_F_POLL);
235739fd7cfSkettenis iic_release_bus(sc->sc_tag, I2C_F_POLL);
236739fd7cfSkettenis
237739fd7cfSkettenis if (error) {
238739fd7cfSkettenis printf("%s: can't write RTC\n", sc->sc_dev.dv_xname);
239739fd7cfSkettenis return error;
240739fd7cfSkettenis }
241739fd7cfSkettenis
242739fd7cfSkettenis /* Clear OSF flag. */
243739fd7cfSkettenis status = dsxrtc_reg_read(sc, DS3231_SR);
244739fd7cfSkettenis dsxrtc_reg_write(sc, DS3231_SR, status & ~DS3231_SR_OSF);
245739fd7cfSkettenis
246739fd7cfSkettenis return 0;
247739fd7cfSkettenis }
248