/* * abcrtc.c * * ABC-bus interface to the real-time clock * Very loosely based on the virtual RTC in abc80sim. */ #include "common.h" #include "io.h" #include "abcio.h" #include "systime.h" #include "console.h" #include "config.h" #define RTC_CALLBACK_MASK ((1 << 8)|(1 << 2)|(1 << 0)) #define RTC_MAGIC 211 /* Command and status register bits */ enum rtc_cmd_status { RTC_READY = 0x01, RTC_ID = 0x02, /* INP(0) shows magic number */ RTC_PROGRAM = 0x04, /* (Ready to) program RTC */ RTC_START_WRITE = 0x40, RTC_START_READ = 0x80 }; static uint8_t rdbuf[8]; static uint8_t wrbuf[8]; static bool need_program; /* Programming is handled at non-IRQ prio */ static void rtc_latch_time(void) { struct tms tms = get_systime(); rdbuf[0] = 20; /* Only 20xx years supported */ rdbuf[1] = tms.tm_year - 20; rdbuf[2] = tms.tm_mon; rdbuf[3] = tms.tm_mday; rdbuf[4] = tms.tm_hour; rdbuf[5] = tms.tm_min; rdbuf[6] = tms_sec(tms); rdbuf[7] = (tms.hold.tm_tick * ((100ULL << 32) >> 15)) >> 32; /* 1/100 s */ } static void rtc_store_time(void) { struct tms tms; /* XXX: should probably do data validation here, even an error bit */ memset(&tms, 0, sizeof tms); tms.tm_year = wrbuf[1] + 20; tms.tm_mon = wrbuf[2]; tms.tm_mday = wrbuf[3]; tms.tm_hour = wrbuf[4]; tms.tm_min = wrbuf[5]; tms.tm_2sec = wrbuf[6] >> 1; tms.hold.tm_1sec = wrbuf[6] & 1; tms.hold.tm_tick = (wrbuf[7] * ((1ULL << (15+32))/100)) >> 32; set_systime(tms); do_write_rtc = true; } static ABC_CALLBACK(rtc_do_program_ready) { dev->inp_data[1] |= RTC_PROGRAM; ABC_INP1_DATA = dev->inp_data[1]; } static ABC_CALLBACK(rtc_do_id) { /* Called on end of data read */ dev->inp_data[1] |= RTC_ID; ABC_INP1_DATA = dev->inp_data[1]; } static ABC_CALLBACK(rtc_do_command) { if (data & RTC_PROGRAM) { dev->inp_data[1] &= ~RTC_PROGRAM; ABC_INP1_DATA = dev->inp_data[1]; rtc_store_time(); } if (data & RTC_START_WRITE) { abc_setup_out_queue(dev, &wrbuf, 8, dev->inp_data[1]|RTC_START_WRITE); } if (data & RTC_START_READ) { dev->inp_data[1] &= ~RTC_ID; ABC_INP1_DATA = dev->inp_data[1]; rtc_latch_time(); abc_setup_inp_queue(dev, &rdbuf, 8, dev->inp_data[1]|RTC_START_READ); } } static struct abc_dev rtc_iodev = { .callback_mask = RTC_CALLBACK_MASK, .status_first_inp_mask = (uint8_t)~RTC_START_READ, .status_first_out_mask = (uint8_t)~RTC_START_WRITE, .inp_en = 3, .inp_data_def = RTC_MAGIC, .inp_data[1] = RTC_READY|RTC_ID, .callback_out[0] = rtc_do_program_ready, .callback_out[2] = rtc_do_command, .callback_inp[0] = rtc_do_id, .name = "rtc" }; void rtc_abc_init(void) { rtc_abc_config(); } void rtc_abc_config(void) { unsigned int devsel = getvar_uint(config_abc_io_rtc_devsel); if (!getvar_bool(config_abc_io_rtc_enable)) devsel = DEVSEL_NONE; abc_register(&rtc_iodev, devsel); }