xref: /openbsd-src/sys/arch/sparc64/dev/rtc.c (revision 0701a1582b980c3a7d09f2b4176795598cce62d3)
1*0701a158Skettenis /*	$OpenBSD: rtc.c,v 1.13 2022/10/12 13:39:50 kettenis Exp $	*/
2d5e78757Skettenis 
3d5e78757Skettenis /*
4d5e78757Skettenis  * Copyright (c) 1992, 1993
5d5e78757Skettenis  *	The Regents of the University of California.  All rights reserved.
6d5e78757Skettenis  * Copyright (c) 1994 Gordon W. Ross
7d5e78757Skettenis  * Copyright (c) 1993 Adam Glass
8d5e78757Skettenis  * Copyright (c) 1996 Paul Kranenburg
9d5e78757Skettenis  * Copyright (c) 1996
10d5e78757Skettenis  * 	The President and Fellows of Harvard College. All rights reserved.
11d5e78757Skettenis  *
12d5e78757Skettenis  * This software was developed by the Computer Systems Engineering group
13d5e78757Skettenis  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
14d5e78757Skettenis  * contributed to Berkeley.
15d5e78757Skettenis  *
16d5e78757Skettenis  * All advertising materials mentioning features or use of this software
17d5e78757Skettenis  * must display the following acknowledgement:
18d5e78757Skettenis  *	This product includes software developed by Harvard University.
19d5e78757Skettenis  *	This product includes software developed by the University of
20d5e78757Skettenis  *	California, Lawrence Berkeley Laboratory.
21d5e78757Skettenis  *
22d5e78757Skettenis  * Redistribution and use in source and binary forms, with or without
23d5e78757Skettenis  * modification, are permitted provided that the following conditions
24d5e78757Skettenis  * are met:
25d5e78757Skettenis  *
26d5e78757Skettenis  * 1. Redistributions of source code must retain the above copyright
27d5e78757Skettenis  *    notice, this list of conditions and the following disclaimer.
28d5e78757Skettenis  * 2. Redistributions in binary form must reproduce the above copyright
29d5e78757Skettenis  *    notice, this list of conditions and the following disclaimer in the
30d5e78757Skettenis  *    documentation and/or other materials provided with the distribution.
31d5e78757Skettenis  * 3. All advertising materials mentioning features or use of this software
32d5e78757Skettenis  *    must display the following acknowledgement:
33d5e78757Skettenis  *	This product includes software developed by the University of
34d5e78757Skettenis  *	California, Berkeley and its contributors.
35d5e78757Skettenis  *	This product includes software developed by Paul Kranenburg.
36d5e78757Skettenis  *	This product includes software developed by Harvard University.
37d5e78757Skettenis  * 4. Neither the name of the University nor the names of its contributors
38d5e78757Skettenis  *    may be used to endorse or promote products derived from this software
39d5e78757Skettenis  *    without specific prior written permission.
40d5e78757Skettenis  *
41d5e78757Skettenis  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
42d5e78757Skettenis  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43d5e78757Skettenis  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44d5e78757Skettenis  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
45d5e78757Skettenis  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46d5e78757Skettenis  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47d5e78757Skettenis  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48d5e78757Skettenis  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49d5e78757Skettenis  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50d5e78757Skettenis  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51d5e78757Skettenis  * SUCH DAMAGE.
52d5e78757Skettenis  *
53d5e78757Skettenis  */
54d5e78757Skettenis 
55d5e78757Skettenis /*
56d5e78757Skettenis  * Driver for rtc device on Blade 1000, Fire V210, etc.
57d5e78757Skettenis  */
58d5e78757Skettenis 
59d5e78757Skettenis #include <sys/param.h>
60d5e78757Skettenis #include <sys/kernel.h>
61d5e78757Skettenis #include <sys/device.h>
62d5e78757Skettenis #include <sys/malloc.h>
63d5e78757Skettenis #include <sys/proc.h>
64d5e78757Skettenis #include <sys/signalvar.h>
65d5e78757Skettenis #include <sys/systm.h>
66d5e78757Skettenis 
67d5e78757Skettenis #include <machine/bus.h>
68d5e78757Skettenis #include <machine/autoconf.h>
69d5e78757Skettenis 
70d5e78757Skettenis #include <dev/clock_subr.h>
71d5e78757Skettenis #include <dev/ic/mc146818reg.h>
72d5e78757Skettenis 
73d5e78757Skettenis #include <sparc64/dev/ebusreg.h>
74d5e78757Skettenis #include <sparc64/dev/ebusvar.h>
75d5e78757Skettenis 
7601d4b528Skettenis /*
7701d4b528Skettenis  * Register definitions for the Texas Instruments bq4802.
7801d4b528Skettenis  */
7901d4b528Skettenis 
8001d4b528Skettenis #define BQ4802_SEC		0x00	/* Seconds. */
8101d4b528Skettenis #define BQ4802_MIN		0x02	/* Minutes. */
8201d4b528Skettenis #define BQ4802_HOUR		0x04	/* Hours. */
8301d4b528Skettenis #define BQ4802_DAY		0x06	/* Day (01-31). */
8401d4b528Skettenis #define BQ4802_DOW		0x08	/* Day of week (01-07). */
8501d4b528Skettenis #define BQ4802_MONTH		0x09	/* Month (01-12). */
8601d4b528Skettenis #define BQ4802_YEAR		0x0a	/* Year (00-99). */
8701d4b528Skettenis #define BQ4802_CENTURY		0x0f	/* Century (00-99). */
8801d4b528Skettenis 
8901d4b528Skettenis #define BQ4802_CTRL		0x0e	/* Control. */
9001d4b528Skettenis #define   BQ4802_24HR		0x02	/* 24-hour mode. */
9101d4b528Skettenis #define   BQ4802_UTI		0x08	/* Update transfer inhibit. */
9201d4b528Skettenis 
93d5e78757Skettenis extern todr_chip_handle_t todr_handle;
94d5e78757Skettenis 
95d5e78757Skettenis struct rtc_softc {
96d5e78757Skettenis 	struct device		sc_dv;
97d5e78757Skettenis 	bus_space_tag_t		sc_iot;
98d5e78757Skettenis 	bus_space_handle_t	sc_ioh;
99d5e78757Skettenis 	struct intrhand		*sc_ih;
100d5e78757Skettenis };
101d5e78757Skettenis 
102d5e78757Skettenis int	rtc_match(struct device *, void *, void *);
103d5e78757Skettenis void	rtc_attach(struct device *, struct device *, void *);
104d5e78757Skettenis 
105eb7eaf8dSmpi const struct cfattach rtc_ca = {
106d5e78757Skettenis 	sizeof(struct rtc_softc), rtc_match, rtc_attach
107d5e78757Skettenis };
108d5e78757Skettenis 
109d5e78757Skettenis struct cfdriver rtc_cd = {
110d5e78757Skettenis 	NULL, "rtc", DV_DULL
111d5e78757Skettenis };
112d5e78757Skettenis 
113d5e78757Skettenis int rtc_intr(void *arg);
114d5e78757Skettenis 
115d5e78757Skettenis u_int8_t rtc_read_reg(struct rtc_softc *, bus_size_t);
116d5e78757Skettenis void rtc_write_reg(struct rtc_softc *sc, bus_size_t, u_int8_t);
117d5e78757Skettenis 
118d5e78757Skettenis int rtc_gettime(todr_chip_handle_t, struct timeval *);
119d5e78757Skettenis int rtc_settime(todr_chip_handle_t, struct timeval *);
12001d4b528Skettenis int rtc_bq4802_gettime(todr_chip_handle_t, struct timeval *);
12101d4b528Skettenis int rtc_bq4802_settime(todr_chip_handle_t, struct timeval *);
122d5e78757Skettenis 
123d5e78757Skettenis int
rtc_match(struct device * parent,void * cf,void * aux)124d5e78757Skettenis rtc_match(struct device *parent, void *cf, void *aux)
125d5e78757Skettenis {
126d5e78757Skettenis 	struct ebus_attach_args *ea = aux;
127d5e78757Skettenis 
128d5e78757Skettenis 	if (strcmp("rtc", ea->ea_name) == 0)
129d5e78757Skettenis 		return (1);
130d5e78757Skettenis 	return (0);
131d5e78757Skettenis }
132d5e78757Skettenis 
133d5e78757Skettenis void
rtc_attach(struct device * parent,struct device * self,void * aux)134d5e78757Skettenis rtc_attach(struct device *parent, struct device *self, void *aux)
135d5e78757Skettenis {
136d5e78757Skettenis 	struct rtc_softc *sc = (void *)self;
137d5e78757Skettenis 	struct ebus_attach_args *ea = aux;
138d5e78757Skettenis 	todr_chip_handle_t handle;
139d5e78757Skettenis 	char *model;
14001d4b528Skettenis 	u_int8_t csr;
141d5e78757Skettenis 
142d5e78757Skettenis 	if (ebus_bus_map(ea->ea_iotag, 0,
143d5e78757Skettenis 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
144d5e78757Skettenis 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
145d5e78757Skettenis 		sc->sc_iot = ea->ea_iotag;
146d5e78757Skettenis 	} else if (ebus_bus_map(ea->ea_memtag, 0,
147d5e78757Skettenis 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
148d5e78757Skettenis 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
149d5e78757Skettenis 		sc->sc_iot = ea->ea_memtag;
150d5e78757Skettenis 	} else {
151d5e78757Skettenis 		printf("%s: can't map register\n", self->dv_xname);
152d5e78757Skettenis 		return;
153d5e78757Skettenis 	}
154d5e78757Skettenis 
155d5e78757Skettenis 	model = getpropstring(ea->ea_node, "model");
156d504156dSkettenis 	if (*model == '\0')
15701d4b528Skettenis 		model = getpropstring(ea->ea_node, "compatible");
158f7665c35Skettenis 	printf(": %s\n", *model != '\0' ? model : "unknown");
159d5e78757Skettenis 
160d5e78757Skettenis 	/* Setup our todr_handle */
161d5e78757Skettenis 	handle = malloc(sizeof(struct todr_chip_handle), M_DEVBUF, M_NOWAIT);
162d5e78757Skettenis 	if (handle == NULL)
163d5e78757Skettenis 		panic("couldn't allocate todr_handle");
164d5e78757Skettenis 	handle->cookie = sc;
165d5e78757Skettenis 	handle->todr_gettime = rtc_gettime;
166d5e78757Skettenis 	handle->todr_settime = rtc_settime;
167d5e78757Skettenis 	handle->bus_cookie = NULL;
168d5e78757Skettenis 	handle->todr_setwen = NULL;
169*0701a158Skettenis 	handle->todr_quality = 0;
170d5e78757Skettenis 	todr_handle = handle;
171d5e78757Skettenis 
17201d4b528Skettenis 	/* The bq4802 is not compatible with the mc146818. */
17301d4b528Skettenis 	if (strcmp(model, "bq4802") == 0) {
17401d4b528Skettenis 		handle->todr_gettime = rtc_bq4802_gettime;
17501d4b528Skettenis 		handle->todr_settime = rtc_bq4802_settime;
17601d4b528Skettenis 
17701d4b528Skettenis 		/* Turn on 24-hour mode. */
17801d4b528Skettenis 		csr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, BQ4802_CTRL);
17901d4b528Skettenis 		csr |= BQ4802_24HR;
18001d4b528Skettenis 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, BQ4802_CTRL, csr);
18101d4b528Skettenis 		return;
18201d4b528Skettenis 	}
18301d4b528Skettenis 
184d5e78757Skettenis 	/*
185d5e78757Skettenis 	 * Turn interrupts off, just in case. (Although they shouldn't
186d5e78757Skettenis 	 * be wired to an interrupt controller on sparcs).
187d5e78757Skettenis 	 */
1881d19b741Skettenis 	rtc_write_reg(sc, MC_REGB, MC_REGB_BINARY | MC_REGB_24HR);
189d5e78757Skettenis 
190d5e78757Skettenis 	/*
191d5e78757Skettenis 	 * On ds1287 models (which really are ns87317 chips), the
192d5e78757Skettenis 	 * interrupt is wired to the powerbutton.
193d5e78757Skettenis 	 */
194d5e78757Skettenis 	if (strcmp(model, "ds1287") == 0 && ea->ea_nintrs > 0) {
195d5e78757Skettenis 		sc->sc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intrs[0],
196d5e78757Skettenis 		    IPL_BIO, 0, rtc_intr, sc, self->dv_xname);
197d5e78757Skettenis 		if (sc->sc_ih == NULL) {
198c0dd1c75Skettenis 			printf("%s: can't establish interrupt\n",
199d5e78757Skettenis 			    self->dv_xname);
200d5e78757Skettenis 		}
201d5e78757Skettenis 	}
202d5e78757Skettenis }
203d5e78757Skettenis 
204d5e78757Skettenis int
rtc_intr(void * arg)205d5e78757Skettenis rtc_intr(void *arg)
206d5e78757Skettenis {
207b2e0f490Snaddy 	extern int allowpowerdown;
208d5e78757Skettenis 
209b2e0f490Snaddy 	if (allowpowerdown == 1) {
210b2e0f490Snaddy 		allowpowerdown = 0;
211de4108eaSguenther 		prsignal(initprocess, SIGUSR2);
212d5e78757Skettenis 	}
213d5e78757Skettenis 	return (1);
214d5e78757Skettenis }
215d5e78757Skettenis 
216d5e78757Skettenis /*
217d5e78757Skettenis  * Register access is indirect, through an address and data port.
218d5e78757Skettenis  */
219d5e78757Skettenis 
220d5e78757Skettenis #define	RTC_ADDR	0
221d5e78757Skettenis #define	RTC_DATA	1
222d5e78757Skettenis 
223d5e78757Skettenis u_int8_t
rtc_read_reg(struct rtc_softc * sc,bus_size_t reg)224d5e78757Skettenis rtc_read_reg(struct rtc_softc *sc, bus_size_t reg)
225d5e78757Skettenis {
226d5e78757Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_ADDR, reg);
227d5e78757Skettenis 	return (bus_space_read_1(sc->sc_iot, sc->sc_ioh, RTC_DATA));
228d5e78757Skettenis }
229d5e78757Skettenis 
230d5e78757Skettenis void
rtc_write_reg(struct rtc_softc * sc,bus_size_t reg,u_int8_t val)231d5e78757Skettenis rtc_write_reg(struct rtc_softc *sc, bus_size_t reg, u_int8_t val)
232d5e78757Skettenis {
233d5e78757Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_ADDR, reg);
234d5e78757Skettenis 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, RTC_DATA, val);
235d5e78757Skettenis }
236d5e78757Skettenis 
237d5e78757Skettenis /*
238d5e78757Skettenis  * RTC todr routines.
239d5e78757Skettenis  */
240d5e78757Skettenis 
241d5e78757Skettenis /*
242d5e78757Skettenis  * Get time-of-day and convert to a `struct timeval'
243d5e78757Skettenis  * Return 0 on success; an error number otherwise.
244d5e78757Skettenis  */
245d5e78757Skettenis int
rtc_gettime(todr_chip_handle_t handle,struct timeval * tv)246d5e78757Skettenis rtc_gettime(todr_chip_handle_t handle, struct timeval *tv)
247d5e78757Skettenis {
248d5e78757Skettenis 	struct rtc_softc *sc = handle->cookie;
249d5e78757Skettenis 	struct clock_ymdhms dt;
250d5e78757Skettenis 	int year;
251d5e78757Skettenis 	u_int8_t csr;
252d5e78757Skettenis 
253d5e78757Skettenis 	/* Stop updates. */
254d5e78757Skettenis 	csr = rtc_read_reg(sc, MC_REGB);
255d5e78757Skettenis 	csr |= MC_REGB_SET;
256d5e78757Skettenis 	rtc_write_reg(sc, MC_REGB, csr);
257d5e78757Skettenis 
258d5e78757Skettenis 	/* Read time */
259d5e78757Skettenis 	dt.dt_sec = rtc_read_reg(sc, MC_SEC);
260d5e78757Skettenis 	dt.dt_min = rtc_read_reg(sc, MC_MIN);
261d5e78757Skettenis 	dt.dt_hour = rtc_read_reg(sc, MC_HOUR);
262d5e78757Skettenis 	dt.dt_day = rtc_read_reg(sc, MC_DOM);
263d5e78757Skettenis 	dt.dt_wday = rtc_read_reg(sc, MC_DOW);
264d5e78757Skettenis 	dt.dt_mon = rtc_read_reg(sc, MC_MONTH);
265d5e78757Skettenis 	year = rtc_read_reg(sc, MC_YEAR);
266d5e78757Skettenis 
267d5e78757Skettenis 	if ((year += 1900) < POSIX_BASE_YEAR)
268d5e78757Skettenis 		year += 100;
269d5e78757Skettenis 
270d5e78757Skettenis 	dt.dt_year = year;
271d5e78757Skettenis 
272d5e78757Skettenis 	/* time wears on */
273d5e78757Skettenis 	csr = rtc_read_reg(sc, MC_REGB);
274d5e78757Skettenis 	csr &= ~MC_REGB_SET;
275d5e78757Skettenis 	rtc_write_reg(sc, MC_REGB, csr);
276d5e78757Skettenis 
277d5e78757Skettenis 	/* simple sanity checks */
278d5e78757Skettenis 	if (dt.dt_mon > 12 || dt.dt_day > 31 ||
279d5e78757Skettenis 	    dt.dt_hour >= 24 || dt.dt_min >= 60 || dt.dt_sec >= 60)
280d5e78757Skettenis 		return (1);
281d5e78757Skettenis 
282d5e78757Skettenis 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
283d5e78757Skettenis 	tv->tv_usec = 0;
284d5e78757Skettenis 	return (0);
285d5e78757Skettenis }
286d5e78757Skettenis 
287d5e78757Skettenis /*
288d5e78757Skettenis  * Set the time-of-day clock based on the value of the `struct timeval' arg.
289d5e78757Skettenis  * Return 0 on success; an error number otherwise.
290d5e78757Skettenis  */
291d5e78757Skettenis int
rtc_settime(todr_chip_handle_t handle,struct timeval * tv)292d5e78757Skettenis rtc_settime(todr_chip_handle_t handle, struct timeval *tv)
293d5e78757Skettenis {
294d5e78757Skettenis 	struct rtc_softc *sc = handle->cookie;
295d5e78757Skettenis 	struct clock_ymdhms dt;
296d5e78757Skettenis 	u_int8_t csr;
297d5e78757Skettenis 	int year;
298d5e78757Skettenis 
299d5e78757Skettenis 	/* Note: we ignore `tv_usec' */
300d5e78757Skettenis 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
301d5e78757Skettenis 
302d5e78757Skettenis 	year = dt.dt_year % 100;
303d5e78757Skettenis 
304d5e78757Skettenis 	/* enable write */
305d5e78757Skettenis 	csr = rtc_read_reg(sc, MC_REGB);
306d5e78757Skettenis 	csr |= MC_REGB_SET;
307d5e78757Skettenis 	rtc_write_reg(sc, MC_REGB, csr);
308d5e78757Skettenis 
309d5e78757Skettenis 	rtc_write_reg(sc, MC_SEC, dt.dt_sec);
310d5e78757Skettenis 	rtc_write_reg(sc, MC_MIN, dt.dt_min);
311d5e78757Skettenis 	rtc_write_reg(sc, MC_HOUR, dt.dt_hour);
312d5e78757Skettenis 	rtc_write_reg(sc, MC_DOW, dt.dt_wday);
313d5e78757Skettenis 	rtc_write_reg(sc, MC_DOM, dt.dt_day);
314d5e78757Skettenis 	rtc_write_reg(sc, MC_MONTH, dt.dt_mon);
315d5e78757Skettenis 	rtc_write_reg(sc, MC_YEAR, year);
316d5e78757Skettenis 
317d5e78757Skettenis 	/* load them up */
318d5e78757Skettenis 	csr = rtc_read_reg(sc, MC_REGB);
319d5e78757Skettenis 	csr &= ~MC_REGB_SET;
320d5e78757Skettenis 	rtc_write_reg(sc, MC_REGB, csr);
321d5e78757Skettenis 	return (0);
322d5e78757Skettenis }
323d5e78757Skettenis 
32401d4b528Skettenis /*
32501d4b528Skettenis  * Get time-of-day and convert to a `struct timeval'
32601d4b528Skettenis  * Return 0 on success; an error number otherwise.
32701d4b528Skettenis  */
32801d4b528Skettenis int
rtc_bq4802_gettime(todr_chip_handle_t handle,struct timeval * tv)32901d4b528Skettenis rtc_bq4802_gettime(todr_chip_handle_t handle, struct timeval *tv)
33001d4b528Skettenis {
33101d4b528Skettenis 	struct rtc_softc *sc = handle->cookie;
33201d4b528Skettenis 	struct clock_ymdhms dt;
33301d4b528Skettenis 	bus_space_tag_t iot = sc->sc_iot;
33401d4b528Skettenis 	bus_space_handle_t ioh = sc->sc_ioh;
33501d4b528Skettenis 	u_int8_t csr;
33601d4b528Skettenis 
33701d4b528Skettenis 	/* Stop updates. */
33801d4b528Skettenis 	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
33901d4b528Skettenis 	csr |= BQ4802_UTI;
34001d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);
34101d4b528Skettenis 
34201d4b528Skettenis 	/* Read time */
34301d4b528Skettenis 	dt.dt_sec = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_SEC));
34401d4b528Skettenis 	dt.dt_min = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_MIN));
34501d4b528Skettenis 	dt.dt_hour = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_HOUR));
34601d4b528Skettenis 	dt.dt_day = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_DAY));
34701d4b528Skettenis 	dt.dt_wday = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_DOW));
34801d4b528Skettenis 	dt.dt_mon = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_MONTH));
34901d4b528Skettenis 	dt.dt_year = FROMBCD(bus_space_read_1(iot, ioh, BQ4802_YEAR)) +
35001d4b528Skettenis 	    FROMBCD(bus_space_read_1(iot, ioh, BQ4802_CENTURY)) * 100;
35101d4b528Skettenis 
35201d4b528Skettenis 	/* time wears on */
35301d4b528Skettenis 	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
35401d4b528Skettenis 	csr &= ~BQ4802_UTI;
35501d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);
35601d4b528Skettenis 
35701d4b528Skettenis 	/* simple sanity checks */
35801d4b528Skettenis 	if (dt.dt_mon > 12 || dt.dt_day > 31 ||
35901d4b528Skettenis 	    dt.dt_hour >= 24 || dt.dt_min >= 60 || dt.dt_sec >= 60)
36001d4b528Skettenis 		return (1);
36101d4b528Skettenis 
36201d4b528Skettenis 	tv->tv_sec = clock_ymdhms_to_secs(&dt);
36301d4b528Skettenis 	tv->tv_usec = 0;
36401d4b528Skettenis 	return (0);
36501d4b528Skettenis }
36601d4b528Skettenis 
36701d4b528Skettenis /*
36801d4b528Skettenis  * Set the time-of-day clock based on the value of the `struct timeval' arg.
36901d4b528Skettenis  * Return 0 on success; an error number otherwise.
37001d4b528Skettenis  */
37101d4b528Skettenis int
rtc_bq4802_settime(todr_chip_handle_t handle,struct timeval * tv)37201d4b528Skettenis rtc_bq4802_settime(todr_chip_handle_t handle, struct timeval *tv)
37301d4b528Skettenis {
37401d4b528Skettenis 	struct rtc_softc *sc = handle->cookie;
37501d4b528Skettenis 	struct clock_ymdhms dt;
37601d4b528Skettenis 	bus_space_tag_t iot = sc->sc_iot;
37701d4b528Skettenis 	bus_space_handle_t ioh = sc->sc_ioh;
37801d4b528Skettenis 	u_int8_t csr;
37901d4b528Skettenis 
38001d4b528Skettenis 	/* Note: we ignore `tv_usec' */
38101d4b528Skettenis 	clock_secs_to_ymdhms(tv->tv_sec, &dt);
38201d4b528Skettenis 
38301d4b528Skettenis 	/* enable write */
38401d4b528Skettenis 	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
38501d4b528Skettenis 	csr |= BQ4802_UTI;
38601d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);
38701d4b528Skettenis 
38801d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_SEC, TOBCD(dt.dt_sec));
38901d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_MIN, TOBCD(dt.dt_min));
39001d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_HOUR, TOBCD(dt.dt_hour));
39101d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_DOW, TOBCD(dt.dt_wday));
39201d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_DAY, TOBCD(dt.dt_day));
39301d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_MONTH, TOBCD(dt.dt_mon));
39401d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_YEAR, TOBCD(dt.dt_year % 100));
39501d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_CENTURY, TOBCD(dt.dt_year / 100));
39601d4b528Skettenis 
39701d4b528Skettenis 	/* load them up */
39801d4b528Skettenis 	csr = bus_space_read_1(iot, ioh, BQ4802_CTRL);
39901d4b528Skettenis 	csr &= ~BQ4802_UTI;
40001d4b528Skettenis 	bus_space_write_1(iot, ioh, BQ4802_CTRL, csr);
40101d4b528Skettenis 	return (0);
40201d4b528Skettenis }
403