| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 | /* * 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"#define RTC_DEVSEL 54#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};void rtc_abc_init(void){    abc_register(&rtc_iodev, RTC_DEVSEL);}
 |