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