#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: rtc.c,v 1.21 2025/09/07 04:47:00 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <dev/clock_subr.h>
#include <next68k/next68k/rtc.h>
#include <next68k/dev/clockreg.h>
#include <next68k/dev/intiovar.h>
u_char new_clock;
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 *);
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;
}
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;
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);
while(t == rtc_read(reg))
continue;
DELAY(850000);
if (new_clock) {
reg = RTC_CONTROL;
} else {
reg = RTC_INTRCTL;
}
rtc_write(reg, rtc_read(reg)|(RTC_PDOWN));
printf(".....................");
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) {
dt->dt_hour = bcdtobin(h & 0x1f);
if ((h & 0x20) != 0) {
if (dt->dt_hour < 12)
dt->dt_hour += 12;
} else {
if (dt->dt_hour == 12)
dt->dt_hour = 0;
}
#ifdef notdef
} else {
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;
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) {
if (dt->dt_hour == 0) {
rtc_write(RTC_HRS, bintobcd(12) | 0x80);
} else if (dt->dt_hour < 12) {
rtc_write(RTC_HRS, bintobcd(dt->dt_hour) | 0x80);
} else if (dt->dt_hour == 12) {
rtc_write(RTC_HRS, bintobcd(12) | 0x80 | 0x20);
} else {
rtc_write(RTC_HRS,
bintobcd(dt->dt_hour - 12) | 0x80 | 0x20);
}
} else {
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
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)
{
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
rtc_write(RTC_CONTROL, rtc_read(RTC_CONTROL) | RTC_START);
return 0;
}