| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 | /* * Read/write DS3231M RTC to/from the sysclock. * * This currently doesn't try to maintain any kind of subsecond * accuracy. */#include <string.h>#include "common.h"#include "console.h"#include "io.h"#include "systime.h"static inline uint32_t i2c_wait(void){    waitfor(I2C_IRQ);}static void i2c_send(uint8_t byte, uint8_t ctl){    i2c_wait();    I2C_WDATA = (byte << 8) | I2C_NAK | ctl;}static bool i2c_acked(void){    i2c_wait();    return !(I2C_RDATA & I2C_NAK);}static uint8_t i2c_recv(uint8_t ctl){    uint32_t rdata;    i2c_wait();    I2C_WDATA = (~0xff) | ctl;    i2c_wait();    return I2C_RDATA_DATA;}#define RTC_REGS 19		/* Total RTC registers */#define RTC_TIME_REGS 7		/* RTC registers with time info */#define RTC_ADDR 0x68#define RTC_WCMD ((RTC_ADDR << 1)+0)#define RTC_RCMD ((RTC_ADDR << 1)+1)static unsigned int unbcd(uint8_t v){    return (v & 0x0f) + (v >> 4) * 10;}static uint8_t tobcd(unsigned int v){    uint8_t q;    v %= 100;    return ((v/10) << 4) + (v%10);}static int i2c_start_rtc(void){    int sda_retry_count = 16;    i2c_set_speed(400);    /* SDA held low? */    while (!(I2C_RDATA & I2C_SDA)) {	i2c_send(0xff, I2C_DUMMY);	if (!sda_retry_count--) {	    con_printf("RTC: I2C SDA stuck low\n");	    return -1;	}    }    i2c_send(RTC_WCMD, 0);    if (!i2c_acked()) {	con_printf("No RTC detected at I2C address 0x%02x\n", RTC_ADDR);	i2c_send(0xff, I2C_P);	return -1;    }    return 0;}static void con_time(const char *what, struct tms tms){    con_printf("RTC time %s: %04u-%02u-%02u %02u:%02u:%02u\n",	       what, tms.tm_year + 1980, tms.tm_mon, tms.tm_mday,	       tms.tm_hour, tms.tm_min, tms_sec(tms));}void read_rtc(void){    uint8_t rtc_regs[RTC_TIME_REGS];    int i;    struct tms tms;    if (i2c_start_rtc())	return;    i2c_send(0, I2C_SR);	/* Starting register */    i2c_send(RTC_RCMD, 0);    for (i = 0; i < RTC_TIME_REGS-1; i++)	rtc_regs[i] = i2c_recv(0);    rtc_regs[i] = i2c_recv(I2C_NAK | I2C_P);    /* Convert to struct tms and set systime */    memset(&tms, 0, sizeof tms);    unsigned int sec = unbcd(rtc_regs[0]);    tms.tm_2sec = sec >> 1;    tms.hold.tm_1sec = sec & 1;    tms.hold.tm_tick = 0x4000;	/* Without more info, assume mid-second */    tms.tm_min = unbcd(rtc_regs[1]);    unsigned int hour;    if (rtc_regs[2] & 0x40) {	/* AM/PM mode - this shouldn't happen */	hour = unbcd(rtc_regs[2] & 0x1f);	if (hour > 11)	    hour -= 12;	if (rtc_regs[2] & 0x20)	    hour += 12;    } else {	/* 24-hour mode */	hour = unbcd(rtc_regs[2]);    }    tms.tm_hour = hour;    tms.tm_mday = unbcd(rtc_regs[4]);    tms.tm_mon  = unbcd(rtc_regs[5] & 0x1f);    tms.tm_year = unbcd(rtc_regs[6]) + (rtc_regs[5] & 0x80 ? 100 : 0) + 20;    set_systime(tms);#ifdef TEST    con_printf("RTC register content:\n");    for (i = 0; i < RTC_REGS; i++)	con_printf(" %02x", rtc_regs[i]);#endif    con_time("read", tms);}volatile bool do_write_rtc;void write_rtc(void){    uint8_t rtc_regs[RTC_TIME_REGS];    int i;    struct tms tms;    int sda_retry_count = 16;    tms = get_systime();    rtc_regs[0] = tobcd(tms_sec(tms));    rtc_regs[1] = tobcd(tms.tm_min);    rtc_regs[2] = tobcd(tms.tm_hour);    rtc_regs[3] = 0;		/* Day of week, unused */    rtc_regs[4] = tobcd(tms.tm_mday);    rtc_regs[5] = tobcd(tms.tm_mon);    rtc_regs[6] = tobcd(tms.tm_year - 20);    if (i2c_start_rtc())	return;    i2c_send(0, 0);		/* Starting register */    for (i = 0; i < RTC_TIME_REGS-1; i++)	i2c_send(rtc_regs[i], 0);    i2c_send(rtc_regs[i], I2C_P);    con_time("set", tms);    do_write_rtc = false;}
 |