xref: /openbsd-src/sys/dev/i2c/ds3231.c (revision 23c413af725e162520ced0da7ed18e4f885fca2e)
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