11.1Sjdc/*	$NetBSD: bq4802_ebus.c,v 1.1 2026/02/01 11:45:10 jdc Exp $	*/
21.1Sjdc
31.1Sjdc/*-
41.1Sjdc * Copyright (c) 2026 The NetBSD Foundation, Inc.
51.1Sjdc * All rights reserved.
61.1Sjdc *
71.1Sjdc * This code is derived from software contributed to The NetBSD Foundation
81.1Sjdc * by Julian Coleman.
91.1Sjdc *
101.1Sjdc * Redistribution and use in source and binary forms, with or without
111.1Sjdc * modification, are permitted provided that the following conditions
121.1Sjdc * are met:
131.1Sjdc * 1. Redistributions of source code must retain the above copyright
141.1Sjdc *    notice, this list of conditions and the following disclaimer.
151.1Sjdc * 2. Redistributions in binary form must reproduce the above copyright
161.1Sjdc *    notice, this list of conditions and the following disclaimer in the
171.1Sjdc *    documentation and/or other materials provided with the distribution.
181.1Sjdc *
191.1Sjdc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
201.1Sjdc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
211.1Sjdc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221.1Sjdc * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
231.1Sjdc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
241.1Sjdc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
251.1Sjdc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
261.1Sjdc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
271.1Sjdc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
281.1Sjdc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
291.1Sjdc * POSSIBILITY OF SUCH DAMAGE.
301.1Sjdc */
311.1Sjdc
321.1Sjdc#include <sys/cdefs.h>
331.1Sjdc__KERNEL_RCSID(0, "$NetBSD: bq4802_ebus.c,v 1.1 2026/02/01 11:45:10 jdc Exp $");
341.1Sjdc
351.1Sjdc/* Clock driver for rtc/bq4802 (a Texas Instruments bq4802Y/bq4802LY). */
361.1Sjdc
371.1Sjdc#include <sys/param.h>
381.1Sjdc#include <sys/kernel.h>
391.1Sjdc#include <sys/device.h>
401.1Sjdc#include <sys/proc.h>
411.1Sjdc#include <sys/types.h>
421.1Sjdc
431.1Sjdc#include <sys/bus.h>
441.1Sjdc#include <machine/autoconf.h>
451.1Sjdc
461.1Sjdc#include <dev/clock_subr.h>
471.1Sjdc#include <dev/ic/bq4802reg.h>
481.1Sjdc
491.1Sjdc#include <dev/ebus/ebusreg.h>
501.1Sjdc#include <dev/ebus/ebusvar.h>
511.1Sjdc
521.1Sjdcstruct bq4802rtc_softc {
531.1Sjdc	device_t	sc_dev;
541.1Sjdc
551.1Sjdc	struct		todr_chip_handle sc_tch;
561.1Sjdc
571.1Sjdc	bus_space_tag_t sc_bst;
581.1Sjdc	bus_space_handle_t sc_bsh;
591.1Sjdc};
601.1Sjdc
611.1Sjdcstatic int	bq4802rtc_ebus_match(device_t, cfdata_t, void *);
621.1Sjdcstatic void	bq4802rtc_ebus_attach(device_t, device_t, void *);
631.1Sjdcstatic int	bq4802_gettime_ymdhms(struct todr_chip_handle *,
641.1Sjdc		    struct clock_ymdhms *);
651.1Sjdcstatic int	bq4802_settime_ymdhms(struct todr_chip_handle *,
661.1Sjdc		    struct clock_ymdhms *);
671.1Sjdc
681.1SjdcCFATTACH_DECL_NEW(bq4802rtc_ebus, sizeof(struct bq4802rtc_softc),
691.1Sjdc    bq4802rtc_ebus_match, bq4802rtc_ebus_attach, NULL, NULL);
701.1Sjdc
711.1Sjdc/* Register read and write. */
721.1Sjdc#define	bq4802_read(sc, reg) \
731.1Sjdc    bus_space_read_1(sc->sc_bst, sc->sc_bsh, reg)
741.1Sjdc#define bq4802_write(sc, reg, val) \
751.1Sjdc    bus_space_write_1(sc->sc_bst, sc->sc_bsh, reg, val)
761.1Sjdc
771.1Sjdcstatic int
781.1Sjdcbq4802rtc_ebus_match(device_t parent, cfdata_t cf, void *aux)
791.1Sjdc{
801.1Sjdc	struct ebus_attach_args *ea = aux;
811.1Sjdc	char *compat;
821.1Sjdc
831.1Sjdc	if (strcmp("rtc", ea->ea_name) != 0)
841.1Sjdc		return 0;
851.1Sjdc
861.1Sjdc	compat = prom_getpropstring(ea->ea_node, "compatible");
871.1Sjdc	if (compat != NULL && !strcmp(compat, "bq4802"))
881.1Sjdc		return 2;	/* We need a better match than 'rtc' */
891.1Sjdc
901.1Sjdc	return 0;
911.1Sjdc}
921.1Sjdc
931.1Sjdcstatic void
941.1Sjdcbq4802rtc_ebus_attach(device_t parent, device_t self, void *aux)
951.1Sjdc{
961.1Sjdc	struct bq4802rtc_softc *sc = device_private(self);
971.1Sjdc	struct ebus_attach_args *ea = aux;
981.1Sjdc	todr_chip_handle_t tch = &sc->sc_tch;
991.1Sjdc	int sz;
1001.1Sjdc	uint8_t ctrl;
1011.1Sjdc
1021.1Sjdc	sc->sc_dev = self;
1031.1Sjdc	sc->sc_bst = ea->ea_bustag;
1041.1Sjdc
1051.1Sjdc	sz = ea->ea_reg[0].size;
1061.1Sjdc
1071.1Sjdc	if (bus_space_map(sc->sc_bst,
1081.1Sjdc			 EBUS_ADDR_FROM_REG(&ea->ea_reg[0]),
1091.1Sjdc			 sz, 0,
1101.1Sjdc			 &sc->sc_bsh) != 0) {
1111.1Sjdc		aprint_error(": can't map register\n");
1121.1Sjdc		return;
1131.1Sjdc	}
1141.1Sjdc
1151.1Sjdc	aprint_normal(": real time clock\n");
1161.1Sjdc
1171.1Sjdc	tch->todr_dev = self;
1181.1Sjdc	tch->todr_gettime_ymdhms = bq4802_gettime_ymdhms;
1191.1Sjdc	tch->todr_settime_ymdhms = bq4802_settime_ymdhms;
1201.1Sjdc
1211.1Sjdc	/* Setup: alarms off, disable DST, enable updates, 24-hour mode */
1221.1Sjdc	ctrl = bq4802_read(sc, BQ4802_CTRL);
1231.1Sjdc	ctrl = ctrl & ~(BQ4802_CTRL_DSE | BQ4802_CTRL_STP | BQ4802_CTRL_UTI);
1241.1Sjdc	ctrl = ctrl | BQ4802_CTRL_24;
1251.1Sjdc	bq4802_write(sc, BQ4802_CTRL, ctrl);
1261.1Sjdc
1271.1Sjdc	todr_attach(tch);
1281.1Sjdc}
1291.1Sjdc
1301.1Sjdcstatic int
1311.1Sjdcbq4802_gettime_ymdhms(struct todr_chip_handle *todrch, struct clock_ymdhms *dt)
1321.1Sjdc{
1331.1Sjdc	struct bq4802rtc_softc *sc = device_private(todrch->todr_dev);
1341.1Sjdc	bq4802_regs regs;
1351.1Sjdc	uint64_t	val;
1361.1Sjdc	int s;
1371.1Sjdc
1381.1Sjdc	s = splclock();
1391.1Sjdc	BQ4802_GETTOD(sc, &regs);
1401.1Sjdc	splx(s);
1411.1Sjdc
1421.1Sjdc	dt->dt_sec = bcdtobin(regs[BQ4802_SEC]);
1431.1Sjdc	dt->dt_min = bcdtobin(regs[BQ4802_MIN]);
1441.1Sjdc	val = bcdtobin(regs[BQ4802_HOUR]);
1451.1Sjdc	if (val == 24)
1461.1Sjdc		val = 0;
1471.1Sjdc	dt->dt_hour = val;
1481.1Sjdc	dt->dt_wday = bcdtobin(regs[BQ4802_WDAY]);
1491.1Sjdc	dt->dt_day = bcdtobin(regs[BQ4802_DAY]);
1501.1Sjdc	dt->dt_mon = bcdtobin(regs[BQ4802_MONTH]);
1511.1Sjdc	val = bcdtobin(regs[BQ4802_YEAR]);
1521.1Sjdc	val += bcdtobin(regs[BQ4802_CENT]) * 100;
1531.1Sjdc	dt->dt_year = val;
1541.1Sjdc
1551.1Sjdc	return 0;
1561.1Sjdc}
1571.1Sjdc
1581.1Sjdcstatic int
1591.1Sjdcbq4802_settime_ymdhms(struct todr_chip_handle *todrch, struct clock_ymdhms *dt)
1601.1Sjdc{
1611.1Sjdc	struct bq4802rtc_softc *sc = device_private(todrch->todr_dev);
1621.1Sjdc	bq4802_regs regs;
1631.1Sjdc	uint8_t val;
1641.1Sjdc	int s;
1651.1Sjdc
1661.1Sjdc	regs[BQ4802_SEC] = bintobcd(dt->dt_sec);
1671.1Sjdc	regs[BQ4802_MIN] = bintobcd(dt->dt_min);
1681.1Sjdc	if (dt->dt_hour == 0)
1691.1Sjdc		val = 24;
1701.1Sjdc	else
1711.1Sjdc		val = dt->dt_hour;
1721.1Sjdc	regs[BQ4802_HOUR] = bintobcd(val);
1731.1Sjdc	regs[BQ4802_WDAY] = bintobcd(dt->dt_wday);
1741.1Sjdc	regs[BQ4802_DAY] = bintobcd(dt->dt_day);
1751.1Sjdc	regs[BQ4802_MONTH] = bintobcd(dt->dt_mon);
1761.1Sjdc	regs[BQ4802_YEAR] = bintobcd(dt->dt_year % 100);
1771.1Sjdc	regs[BQ4802_CENT] = bintobcd(dt->dt_year / 100);
1781.1Sjdc
1791.1Sjdc	s = splclock();
1801.1Sjdc	BQ4802_SETTOD(sc, &regs);
1811.1Sjdc	splx(s);
1821.1Sjdc
1831.1Sjdc	return 0;
1841.1Sjdc}
185