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, ®s); 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, ®s); 181 splx(s); 182 183 return 0; 184} 185