/* $NetBSD: rtc.c,v 1.11 2023/12/20 15:34:45 thorpej Exp $ */ /*- * Copyright (c) 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by UCHIYAMA Yasushi. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: rtc.c,v 1.11 2023/12/20 15:34:45 thorpej Exp $"); #include #include #include #include #ifdef GPROF #include #endif #include #include #if defined(DEBUG) && !defined(RTC_DEBUG) #define RTC_DEBUG #endif struct rtc_softc { device_t sc_dev; int sc_valid; struct todr_chip_handle sc_todr; u_int sc_year0; }; static int rtc_match(device_t, cfdata_t, void *); static void rtc_attach(device_t, device_t, void *); CFATTACH_DECL_NEW(rtc, sizeof(struct rtc_softc), rtc_match, rtc_attach, NULL, NULL); /* todr(9) methods */ static int rtc_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); static int rtc_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); #ifndef SH3_RTC_BASEYEAR #define SH3_RTC_BASEYEAR 1900 #endif u_int sh3_rtc_baseyear = SH3_RTC_BASEYEAR; static int rtc_match(device_t parent, cfdata_t cfp, void *aux) { return 1; } static void rtc_attach(device_t parent, device_t self, void *aux) { struct rtc_softc *sc; uint8_t r; prop_number_t prop_rtc_baseyear; #ifdef RTC_DEBUG char bits[128]; #endif aprint_naive("\n"); aprint_normal("\n"); sc = device_private(self); sc->sc_dev = self; r = _reg_read_1(SH_(RCR2)); #ifdef RTC_DEBUG snprintb(bits, sizeof(bits), SH_RCR2_BITS, r); aprint_debug_dev(sc->sc_dev, "RCR2=%s\n", bits); #endif /* Was the clock running? */ if ((r & (SH_RCR2_ENABLE | SH_RCR2_START)) == (SH_RCR2_ENABLE | SH_RCR2_START)) sc->sc_valid = 1; else { sc->sc_valid = 0; aprint_error_dev(sc->sc_dev, "WARNING: clock was stopped\n"); } /* Disable carry and alarm interrupts */ _reg_write_1(SH_(RCR1), 0); /* Clock runs, no periodic interrupts, no 30-sec adjustment */ _reg_write_1(SH_(RCR2), SH_RCR2_ENABLE | SH_RCR2_START); sc->sc_todr.cookie = sc; sc->sc_todr.todr_gettime_ymdhms = rtc_gettime_ymdhms; sc->sc_todr.todr_settime_ymdhms = rtc_settime_ymdhms; prop_rtc_baseyear = prop_dictionary_get(device_properties(self), "sh3_rtc_baseyear"); if (prop_rtc_baseyear != NULL) { sh3_rtc_baseyear = (u_int)prop_number_integer_value(prop_rtc_baseyear); #ifdef RTC_DEBUG aprint_debug_dev(self, "using baseyear %u passed via device property\n", sh3_rtc_baseyear); #endif } sc->sc_year0 = sh3_rtc_baseyear; todr_attach(&sc->sc_todr); #ifdef RTC_DEBUG { struct clock_ymdhms dt; rtc_gettime_ymdhms(&sc->sc_todr, &dt); } #endif if (!pmf_device_register(self, NULL, NULL)) aprint_error_dev(self, "unable to establish power handler\n"); } static int rtc_gettime_ymdhms(todr_chip_handle_t h, struct clock_ymdhms *dt) { struct rtc_softc *sc = h->cookie; unsigned int year; int retry = 8; if (!sc->sc_valid) { #ifdef RTC_DEBUG aprint_debug_dev(sc->sc_dev, "gettime: not valid\n"); /* but proceed and read/print it anyway */ #else return EIO; #endif } /* disable carry interrupt */ _reg_bclr_1(SH_(RCR1), SH_RCR1_CIE); do { uint8_t r = _reg_read_1(SH_(RCR1)); r &= ~SH_RCR1_CF; r |= SH_RCR1_AF; /* don't clear alarm flag */ _reg_write_1(SH_(RCR1), r); if (CPU_IS_SH3) year = _reg_read_1(SH3_RYRCNT); else year = _reg_read_2(SH4_RYRCNT) & 0x00ff; dt->dt_year = bcdtobin(year); /* read counter */ #define RTCGET(x, y) \ dt->dt_ ## x = bcdtobin(_reg_read_1(SH_(R ## y ## CNT))) RTCGET(mon, MON); RTCGET(wday, WK); RTCGET(day, DAY); RTCGET(hour, HR); RTCGET(min, MIN); RTCGET(sec, SEC); #undef RTCGET } while ((_reg_read_1(SH_(RCR1)) & SH_RCR1_CF) && --retry > 0); if (retry == 0) { #ifdef RTC_DEBUG aprint_debug_dev(sc->sc_dev, "gettime: retry failed\n"); #endif return EIO; } dt->dt_year += sc->sc_year0; if (dt->dt_year < POSIX_BASE_YEAR) dt->dt_year += 100; #ifdef RTC_DEBUG aprint_debug_dev(sc->sc_dev, "gettime: %04lu-%02d-%02d %02d:%02d:%02d\n", (unsigned long)dt->dt_year, dt->dt_mon, dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec); if (!sc->sc_valid) return EIO; #endif return 0; } static int rtc_settime_ymdhms(todr_chip_handle_t h, struct clock_ymdhms *dt) { struct rtc_softc *sc = h->cookie; unsigned int year; uint8_t r; year = dt->dt_year - sc->sc_year0; if (year > 99) year -= 100; year = bintobcd(year); r = _reg_read_1(SH_(RCR2)); /* stop clock */ _reg_write_1(SH_(RCR2), (r & ~SH_RCR2_START) | SH_RCR2_RESET); /* set time */ if (CPU_IS_SH3) _reg_write_1(SH3_RYRCNT, year); else _reg_write_2(SH4_RYRCNT, year); #define RTCSET(x, y) \ _reg_write_1(SH_(R ## x ## CNT), bintobcd(dt->dt_ ## y)) RTCSET(MON, mon); RTCSET(WK, wday); RTCSET(DAY, day); RTCSET(HR, hour); RTCSET(MIN, min); RTCSET(SEC, sec); #undef RTCSET /* start clock */ _reg_write_1(SH_(RCR2), r); sc->sc_valid = 1; #ifdef RTC_DEBUG aprint_debug_dev(sc->sc_dev, "settime: %04lu-%02d-%02d %02d:%02d:%02d\n", (unsigned long)dt->dt_year, dt->dt_mon, dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec); #endif return 0; }