/* $NetBSD: rtc.c,v 1.20 2023/10/19 22:07:13 andvar Exp $ */ /* * Copyright (c) 1998 Darrin Jewell * Copyright (c) 1997 Rolf Grossmann * All rights reserved. * * 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. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Rolf Grossmann. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /* These haven't been tested to see how they interact with NeXTstep's * interpretation of the rtc. */ /* Now using this in the kernel. This should be turned into a device * Darrin B Jewell Tue Jan 27 20:59:25 1998 */ #include __KERNEL_RCSID(0, "$NetBSD: rtc.c,v 1.20 2023/10/19 22:07:13 andvar Exp $"); #include #include /* for panic */ #include #include #include #include #include #include /* #define RTC_DEBUG */ u_char new_clock; /* will get memory mapped in rtc_init */ volatile u_int *scr2 = (u_int *)NEXT_P_SCR2; static int gettime_old(todr_chip_handle_t, struct clock_ymdhms *); static int settime_old(todr_chip_handle_t, struct clock_ymdhms *); static int gettime_new(todr_chip_handle_t, struct timeval *); static int settime_new(todr_chip_handle_t, struct timeval *); /* * NB: This code should probably be converted to a _true_ device, then this * initialization could happen in attach. The printf could get fixed then, * too. */ void rtc_init(void) { static struct todr_chip_handle tch; uint8_t val; scr2 = (u_int *)IIOV(NEXT_P_SCR2); val = rtc_read(RTC_STATUS); new_clock = (val & RTC_NEW_CLOCK) ? 1 : 0; printf("Looks like a %s clock chip.\n", new_clock ? "MCS1850 (new style)" : "MC68HC68T1 (old style)"); #ifdef RTC_DEBUG rtc_print(); #endif if (new_clock) { tch.todr_gettime = gettime_new; tch.todr_settime = settime_new; tch.todr_gettime_ymdhms = NULL; tch.todr_settime_ymdhms = NULL; } else { tch.todr_gettime_ymdhms = gettime_old; tch.todr_settime_ymdhms = settime_old; tch.todr_gettime = NULL; tch.todr_settime = NULL; } tch.todr_setwen = NULL; todr_attach(&tch); } void rtc_print(void) { #define RTC_PRINT(x) printf("\t%16s= 0x%02x\n",#x, rtc_read(x)) if (new_clock) { RTC_PRINT(RTC_RAM); RTC_PRINT(RTC_CNTR0); RTC_PRINT(RTC_CNTR1); RTC_PRINT(RTC_CNTR2); RTC_PRINT(RTC_CNTR3); RTC_PRINT(RTC_ALARM0); RTC_PRINT(RTC_ALARM1); RTC_PRINT(RTC_ALARM2); RTC_PRINT(RTC_ALARM3); RTC_PRINT(RTC_STATUS); RTC_PRINT(RTC_CONTROL); } else { RTC_PRINT(RTC_RAM); RTC_PRINT(RTC_SEC); RTC_PRINT(RTC_MIN); RTC_PRINT(RTC_HRS); RTC_PRINT(RTC_DAY); RTC_PRINT(RTC_DATE); RTC_PRINT(RTC_MON); RTC_PRINT(RTC_YR); RTC_PRINT(RTC_ALARM_SEC); RTC_PRINT(RTC_ALARM_MIN); RTC_PRINT(RTC_ALARM_HR); RTC_PRINT(RTC_STATUS); RTC_PRINT(RTC_CONTROL); RTC_PRINT(RTC_INTRCTL); } } uint8_t rtc_read(uint8_t reg) { int i; u_int tmp; uint8_t val; *scr2 = (*scr2 & ~(SCR2_RTDATA | SCR2_RTCLK)) | SCR2_RTCE; DELAY(1); val = reg; for (i = 0; i < 8; i++) { tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK); if ((val & 0x80) != 0) tmp |= SCR2_RTDATA; *scr2 = tmp; DELAY(1); *scr2 = tmp | SCR2_RTCLK; DELAY(1); *scr2 = tmp; DELAY(1); val <<= 1; } val = 0; /* should be anyway */ for (i = 0; i < 8; i++) { val <<= 1; tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK); *scr2 = tmp | SCR2_RTCLK; DELAY(1); *scr2 = tmp; DELAY(1); if ((*scr2 & SCR2_RTDATA) != 0) val |= 1; } *scr2 &= ~(SCR2_RTDATA | SCR2_RTCLK | SCR2_RTCE); DELAY(1); return val; } void rtc_write(uint8_t reg, uint8_t v) { int i; u_int tmp; uint8_t val; *scr2 = (*scr2 & ~(SCR2_RTDATA | SCR2_RTCLK)) | SCR2_RTCE; DELAY(1); val = reg | RTC_WRITE; for (i = 0; i < 8; i++) { tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK); if ((val & 0x80) != 0) tmp |= SCR2_RTDATA; *scr2 = tmp; DELAY(1); *scr2 = tmp | SCR2_RTCLK; DELAY(1); *scr2 = tmp; DELAY(1); val <<= 1; } DELAY(1); for (i = 0; i < 8; i++) { tmp = *scr2 & ~(SCR2_RTDATA | SCR2_RTCLK); if ((v & 0x80) != 0) tmp |= SCR2_RTDATA; *scr2 = tmp; DELAY(1); *scr2 = tmp | SCR2_RTCLK; DELAY(1); *scr2 = tmp; DELAY(1); v <<= 1; } *scr2 &= ~(SCR2_RTDATA | SCR2_RTCLK | SCR2_RTCE); DELAY(1); } void poweroff(void) { int reg, t; if(new_clock) { reg = RTC_CNTR3; } else { reg = RTC_CNTR0; } t = rtc_read(reg); /* seconds */ /* wait for clock to tick */ while(t == rtc_read(reg)) continue; DELAY(850000); /* hardware bug workaround ? */ if (new_clock) { reg = RTC_CONTROL; } else { reg = RTC_INTRCTL; } rtc_write(reg, rtc_read(reg)|(RTC_PDOWN)); printf("....................."); /* @@@ work around some sort of bug. */ panic("Failed to poweroff!"); } int gettime_old(todr_chip_handle_t tch, struct clock_ymdhms *dt) { uint8_t h, y; y = bcdtobin(rtc_read(RTC_YR)); if (y >= 69) { dt->dt_year = 1900 + y; } else { dt->dt_year = 2000 + y; } dt->dt_mon = bcdtobin(rtc_read(RTC_MON) & 0x1f); dt->dt_day = bcdtobin(rtc_read(RTC_DATE) & 0x3f); dt->dt_wday = bcdtobin(rtc_read(RTC_DAY) & 0x07); h = rtc_read(RTC_HRS); if ((h & 0x80) != 0) { /* time is am/pm format */ dt->dt_hour = bcdtobin(h & 0x1f); if ((h & 0x20) != 0) { /* pm */ if (dt->dt_hour < 12) dt->dt_hour += 12; } else { /* am */ if (dt->dt_hour == 12) dt->dt_hour = 0; } #ifdef notdef } else { /* time is 24 hour format */ struct clock_ymdhms val; val.dt_hour = bcdtobin(h & 0x3f); #endif } dt->dt_min = bcdtobin(rtc_read(RTC_MIN) & 0x7f); dt->dt_sec = bcdtobin(rtc_read(RTC_SEC) & 0x7f); return 0; } int settime_old(todr_chip_handle_t tcr, struct clock_ymdhms *dt) { uint8_t h; /* Stop the clock */ rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) & ~RTC_START); #ifdef RTC_DEBUG printf("Regs before:\n"); rtc_print(); #endif rtc_write(RTC_SEC, bintobcd(dt->dt_sec)); rtc_write(RTC_MIN, bintobcd(dt->dt_min)); h = rtc_read(RTC_HRS); if ((h & 0x80) != 0) { /* time is am/pm format */ if (dt->dt_hour == 0) { rtc_write(RTC_HRS, bintobcd(12) | 0x80); } else if (dt->dt_hour < 12) { /* am */ rtc_write(RTC_HRS, bintobcd(dt->dt_hour) | 0x80); } else if (dt->dt_hour == 12) { rtc_write(RTC_HRS, bintobcd(12) | 0x80 | 0x20); } else { /* pm */ rtc_write(RTC_HRS, bintobcd(dt->dt_hour - 12) | 0x80 | 0x20); } } else { /* time is 24 hour format */ rtc_write(RTC_HRS, bintobcd(dt->dt_hour)); } rtc_write(RTC_DAY, bintobcd(dt->dt_wday)); rtc_write(RTC_DATE, bintobcd(dt->dt_day)); rtc_write(RTC_MON, bintobcd(dt->dt_mon)); rtc_write(RTC_YR, bintobcd(dt->dt_year % 100)); #ifdef RTC_DEBUG printf("Regs after:\n"); rtc_print(); #endif /* restart the clock */ rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) | RTC_START); return 0; } int gettime_new(todr_chip_handle_t tch, struct timeval *tvp) { tvp->tv_sec = rtc_read(RTC_CNTR0) << 24 | rtc_read(RTC_CNTR1) << 16 | rtc_read(RTC_CNTR2) << 8 | rtc_read(RTC_CNTR3); return 0; } int settime_new(todr_chip_handle_t tch, struct timeval *tvp) { /* Stop the clock */ rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) & ~RTC_START); #ifdef RTC_DEBUG printf("Setting RTC to 0x%08llx. Regs before:\n", tvp->tv_sec); rtc_print(); #endif rtc_write(RTC_CNTR0, (tvp->tv_sec >> 24) & 0xff); rtc_write(RTC_CNTR1, (tvp->tv_sec >> 16) & 0xff); rtc_write(RTC_CNTR2, (tvp->tv_sec >> 8) & 0xff); rtc_write(RTC_CNTR3, (tvp->tv_sec) & 0xff); #ifdef RTC_DEBUG printf("Regs after:\n"); rtc_print(); #endif /* restart the clock */ rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) | RTC_START); return 0; }