bq4802_ebus.c revision 1.1
1/*	$NetBSD: bq4802_ebus.c,v 1.1 2026/02/01 11:45:10 jdc Exp $	*/
2
3/*-
4 * Copyright (c) 2026 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Julian Coleman.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: bq4802_ebus.c,v 1.1 2026/02/01 11:45:10 jdc Exp $");
34
35/* Clock driver for rtc/bq4802 (a Texas Instruments bq4802Y/bq4802LY). */
36
37#include <sys/param.h>
38#include <sys/kernel.h>
39#include <sys/device.h>
40#include <sys/proc.h>
41#include <sys/types.h>
42
43#include <sys/bus.h>
44#include <machine/autoconf.h>
45
46#include <dev/clock_subr.h>
47#include <dev/ic/bq4802reg.h>
48
49#include <dev/ebus/ebusreg.h>
50#include <dev/ebus/ebusvar.h>
51
52struct bq4802rtc_softc {
53	device_t	sc_dev;
54
55	struct		todr_chip_handle sc_tch;
56
57	bus_space_tag_t sc_bst;
58	bus_space_handle_t sc_bsh;
59};
60
61static int	bq4802rtc_ebus_match(device_t, cfdata_t, void *);
62static void	bq4802rtc_ebus_attach(device_t, device_t, void *);
63static int	bq4802_gettime_ymdhms(struct todr_chip_handle *,
64		    struct clock_ymdhms *);
65static int	bq4802_settime_ymdhms(struct todr_chip_handle *,
66		    struct clock_ymdhms *);
67
68CFATTACH_DECL_NEW(bq4802rtc_ebus, sizeof(struct bq4802rtc_softc),
69    bq4802rtc_ebus_match, bq4802rtc_ebus_attach, NULL, NULL);
70
71/* Register read and write. */
72#define	bq4802_read(sc, reg) \
73    bus_space_read_1(sc->sc_bst, sc->sc_bsh, reg)
74#define bq4802_write(sc, reg, val) \
75    bus_space_write_1(sc->sc_bst, sc->sc_bsh, reg, val)
76
77static int
78bq4802rtc_ebus_match(device_t parent, cfdata_t cf, void *aux)
79{
80	struct ebus_attach_args *ea = aux;
81	char *compat;
82
83	if (strcmp("rtc", ea->ea_name) != 0)
84		return 0;
85
86	compat = prom_getpropstring(ea->ea_node, "compatible");
87	if (compat != NULL && !strcmp(compat, "bq4802"))
88		return 2;	/* We need a better match than 'rtc' */
89
90	return 0;
91}
92
93static void
94bq4802rtc_ebus_attach(device_t parent, device_t self, void *aux)
95{
96	struct bq4802rtc_softc *sc = device_private(self);
97	struct ebus_attach_args *ea = aux;
98	todr_chip_handle_t tch = &sc->sc_tch;
99	int sz;
100	uint8_t ctrl;
101
102	sc->sc_dev = self;
103	sc->sc_bst = ea->ea_bustag;
104
105	sz = ea->ea_reg[0].size;
106
107	if (bus_space_map(sc->sc_bst,
108			 EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
109			 sz, 0,
110			 &sc->sc_bsh) != 0) {
111		aprint_error(": can't map register\n");
112		return;
113	}
114
115	aprint_normal(": real time clock\n");
116
117	tch->todr_dev = self;
118	tch->todr_gettime_ymdhms = bq4802_gettime_ymdhms;
119	tch->todr_settime_ymdhms = bq4802_settime_ymdhms;
120
121	/* Setup: alarms off, disable DST, enable updates, 24-hour mode */
122	ctrl = bq4802_read(sc, BQ4802_CTRL);
123	ctrl = ctrl & ~(BQ4802_CTRL_DSE | BQ4802_CTRL_STP | BQ4802_CTRL_UTI);
124	ctrl = ctrl | BQ4802_CTRL_24;
125	bq4802_write(sc, BQ4802_CTRL, ctrl);
126
127	todr_attach(tch);
128}
129
130static int
131bq4802_gettime_ymdhms(struct todr_chip_handle *todrch, struct clock_ymdhms *dt)
132{
133	struct bq4802rtc_softc *sc = device_private(todrch->todr_dev);
134	bq4802_regs regs;
135	uint64_t	val;
136	int s;
137
138	s = splclock();
139	BQ4802_GETTOD(sc, &regs);
140	splx(s);
141
142	dt->dt_sec = bcdtobin(regs[BQ4802_SEC]);
143	dt->dt_min = bcdtobin(regs[BQ4802_MIN]);
144	val = bcdtobin(regs[BQ4802_HOUR]);
145	if (val == 24)
146		val = 0;
147	dt->dt_hour = val;
148	dt->dt_wday = bcdtobin(regs[BQ4802_WDAY]);
149	dt->dt_day = bcdtobin(regs[BQ4802_DAY]);
150	dt->dt_mon = bcdtobin(regs[BQ4802_MONTH]);
151	val = bcdtobin(regs[BQ4802_YEAR]);
152	val += bcdtobin(regs[BQ4802_CENT]) * 100;
153	dt->dt_year = val;
154
155	return 0;
156}
157
158static int
159bq4802_settime_ymdhms(struct todr_chip_handle *todrch, struct clock_ymdhms *dt)
160{
161	struct bq4802rtc_softc *sc = device_private(todrch->todr_dev);
162	bq4802_regs regs;
163	uint8_t val;
164	int s;
165
166	regs[BQ4802_SEC] = bintobcd(dt->dt_sec);
167	regs[BQ4802_MIN] = bintobcd(dt->dt_min);
168	if (dt->dt_hour == 0)
169		val = 24;
170	else
171		val = dt->dt_hour;
172	regs[BQ4802_HOUR] = bintobcd(val);
173	regs[BQ4802_WDAY] = bintobcd(dt->dt_wday);
174	regs[BQ4802_DAY] = bintobcd(dt->dt_day);
175	regs[BQ4802_MONTH] = bintobcd(dt->dt_mon);
176	regs[BQ4802_YEAR] = bintobcd(dt->dt_year % 100);
177	regs[BQ4802_CENT] = bintobcd(dt->dt_year / 100);
178
179	s = splclock();
180	BQ4802_SETTOD(sc, &regs);
181	splx(s);
182
183	return 0;
184}
185