/* * RV-3029-C2 RTC driver * * Copyright (c) 2013 Michael Buesch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "rv3029.h" #include "datetime.h" #include "twi_master.h" #include /* RV-3029 hardware registers */ enum rv3029_registers { /* Control page */ RV3029_REG_ONOFFCTRL = 0x00, RV3029_REG_IRQCTRL, RV3029_REG_IRQFLAGS, RV3029_REG_STATUS, RV3029_REG_RSTCTRL, /* Watch page */ RV3029_REG_WSECONDS = 0x08, RV3029_REG_WMINUTES, RV3029_REG_WHOURS, RV3029_REG_WDATE, RV3029_REG_WDAYS, RV3029_REG_WMONTHS, RV3029_REG_WYEARS, /* Alarm page */ RV3029_REG_ASECONDS = 0x10, RV3029_REG_AMINUTES, RV3029_REG_AHOURS, RV3029_REG_ADATE, RV3029_REG_ADAYS, RV3029_REG_AMONTHS, RV3029_REG_AYEARS, /* Timer page */ RV3029_REG_TIMLOW = 0x18, RV3029_REG_TIMHIGH, /* Temperature page */ RV3029_REG_TEMP = 0x20, /* EEPROM data page */ RV3029_REG_EEDATA0 = 0x28, RV3029_REG_EEDATA1, /* EEPROM control page */ RV3029_REG_EECTRL = 0x30, RV3029_REG_XTALOFFSET, RV3029_REG_QCOEF, RV3029_REG_TURNOVER, /* RAM page */ RV3029_REG_RAMDATA0 = 0x38, RV3029_REG_RAMDATA1, RV3029_REG_RAMDATA2, RV3029_REG_RAMDATA3, RV3029_REG_RAMDATA4, RV3029_REG_RAMDATA5, RV3029_REG_RAMDATA6, RV3029_REG_RAMDATA7, }; /* RV3029_REG_ONOFFCTRL bits */ enum rv3029_bits_onoffctrl { RV3029_ONOFFCTRL_WAON, RV3029_ONOFFCTRL_TION, RV3029_ONOFFCTRL_TRON, RV3029_ONOFFCTRL_EEREFON, RV3029_ONOFFCTRL_SRON, RV3029_ONOFFCTRL_TD0, RV3029_ONOFFCTRL_TD1, RV3029_ONOFFCTRL_CLKINT, }; /* RV3029_REG_IRQCTRL bits */ enum rv3029_bits_irqctrl { RV3029_IRQCTRL_AINTE, RV3029_IRQCTRL_TINTE, RV3029_IRQCTRL_V1INTE, RV3029_IRQCTRL_V2INTE, RV3029_IRQCTRL_SRINTE, }; /* RV3029_REG_IRQFLAGS bits */ enum rv3029_bits_irqflags { RV3029_IRQFLAGS_AF, RV3029_IRQFLAGS_TF, RV3029_IRQFLAGS_V1F, RV3029_IRQFLAGS_V2F, RV3029_IRQFLAGS_SRF, }; /* RV3029_REG_STATUS bits */ enum rv3029_bits_status { RV3029_STATUS_VLOW1 = 2, RV3029_STATUS_VLOW2, RV3029_STATUS_SR, RV3029_STATUS_EEBUSY = 7, }; /* RV3029_REG_RSTCTRL bits */ enum rv3029_bits_rstctrl { RV3029_RSTCTRL_ALLRES, RV3029_RSTCTRL_SYSRES = 4, }; /* RV3029_REG_WHOURS bits */ enum rv3029_bits_whours { RV3029_WHOURS_PM = 5, RV3029_WHOURS_S1224, }; /* RV3029_REG_ASECONDS bits */ enum rv3029_bits_aseconds { RV3029_ASECONDS_SECEQ = 7, }; /* RV3029_REG_AMINUTES bits */ enum rv3029_bits_aminutes { RV3029_AMINUTES_MINEQ = 7, }; /* RV3029_REG_AHOURS bits */ enum rv3029_bits_ahours { RV3029_AHOURS_PM = 5, RV3029_AHOURS_HOUREQ = 7, }; /* RV3029_REG_ADATE bits */ enum rv3029_bits_adate { RV3029_ADATE_DATEEQ = 7, }; /* RV3029_REG_ADAYS bits */ enum rv3029_bits_adays { RV3029_ADAYS_DAYEQ = 7, }; /* RV3029_REG_AMONTHS bits */ enum rv3029_bits_amonths { RV3029_AMONTHS_MONTHEQ = 7, }; /* RV3029_REG_AYEARS bits */ enum rv3029_bits_ayears { RV3029_AYEARS_YEAREQ = 7, }; /* RV3029_REG_EECTRL bits */ enum rv3029_bits_eectrl { RV3029_EECTRL_THPER, RV3029_EECTRL_THEN, RV3029_EECTRL_FD0, RV3029_EECTRL_FD1, RV3029_EECTRL_R1K, RV3029_EECTRL_R5K, RV3029_EECTRL_R20K, RV3029_EECTRL_R80K, }; /* RV-3029 device state. */ struct rv3029_device { /* I2C transfer context. */ struct twi_transfer xfer; /* I2C transfer data buffer. */ uint8_t xfer_buffer[8]; /* Cached watch time. */ struct rtc_time now; }; /* Instance of the device state. */ static struct rv3029_device rv3029_dev; /* I2C address of the RV-3029 device. */ #define RV3029_I2C_ADDRESS 0x56 /* I2C transfer timeout, in milliseconds. */ #define RV3029_I2C_TIMEOUT 50 /* Schedule an asynchronous multi-byte register write. * reg: The hardware register to start writing to. * buffer: The data buffer. * count: The number of bytes to write. * callback: The callback to call on completion or error. * May be NULL. */ static void rv3029_write_async(uint8_t reg, const void *buffer, uint8_t count, twi_callback_t callback) { struct rv3029_device *dev = &rv3029_dev; /* Wait for a possible previous transfer to finish. */ twi_transfer_wait(&dev->xfer, RV3029_I2C_TIMEOUT); count = min(count, sizeof(dev->xfer_buffer) - 1); /* The first byte is the register offset. */ dev->xfer_buffer[0] = reg; /* Append the payload data. */ memcpy(dev->xfer_buffer + 1, buffer, count); dev->xfer.write_size = count + 1; dev->xfer.read_size = 0; dev->xfer.callback = callback; /* Schedule the I2C transfer. */ twi_transfer(&dev->xfer); } /* Synchronous multi-byte register write. * reg: The hardware register to start writing to. * buffer: The data buffer. * count: The number of bytes in buffer. * Returns 1 on success, or 0 on I2C error. */ static bool rv3029_write(uint8_t reg, const void *buffer, uint8_t count) { struct rv3029_device *dev = &rv3029_dev; enum twi_status stat; /* Schedule an asynchronous write and wait afterwards. */ rv3029_write_async(reg, buffer, count, NULL); stat = twi_transfer_wait(&dev->xfer, RV3029_I2C_TIMEOUT); if (stat != TWI_STAT_FINISHED) return 0; return 1; } /* Synchronously read y byte-wide register. * reg: The hardware register to write. * value: The value to write. * Returns 1 on success, or 0 on I2C error. */ static bool rv3029_write_byte(uint8_t reg, uint8_t value) { return rv3029_write(reg, &value, sizeof(value)); } /* Schedule an asynchronous multi-byte register read. * reg: The hardware register to start reading from. * count: The number of bytes to read. * callback: The callback to call on completion or error. * May be NULL. */ static void rv3029_read_async(uint8_t reg, uint8_t count, twi_callback_t callback) { struct rv3029_device *dev = &rv3029_dev; /* Wait for a possible previous transfer to finish. */ twi_transfer_wait(&dev->xfer, RV3029_I2C_TIMEOUT); count = min(count, sizeof(dev->xfer_buffer)); /* Write the register offset (one byte). */ dev->xfer_buffer[0] = reg; dev->xfer.write_size = 1; dev->xfer.read_size = count; dev->xfer.callback = callback; /* Schedule the I2C transfer. */ twi_transfer(&dev->xfer); } /* Synchronous multi-byte register read. * reg: The hardware register to start reading from. * count: The number of bytes to read. * Returns a pointer to the receive buffer on success, * or NULL on error. */ static const uint8_t * rv3029_read(uint8_t reg, uint8_t count) { struct rv3029_device *dev = &rv3029_dev; enum twi_status stat; /* Schedule an asynchronous read and wait afterwards. */ rv3029_read_async(reg, count, NULL); stat = twi_transfer_wait(&dev->xfer, RV3029_I2C_TIMEOUT); if (stat != TWI_STAT_FINISHED) return NULL; return dev->xfer_buffer; } /* Synchronously read a byte-wide register. * reg: The hardware register to read. */ static uint8_t rv3029_read_byte(uint8_t reg) { const uint8_t *buffer; uint8_t retval = 0; buffer = rv3029_read(reg, sizeof(retval)); if (buffer) retval = buffer[0]; return retval; } /* Check whether the RV-3029 EEPROM is busy. */ static bool rv3029_eeprom_busy(void) { return !!(rv3029_read_byte(RV3029_REG_STATUS) & (1 << RV3029_STATUS_EEBUSY)); } /* Write to RV-3029 EEPROM. * reg: The EEPROM register. * value: The value to write. */ static void rv3029_eeprom_write(uint8_t reg, uint8_t value) { uint8_t old_onoffctrl, old_value, status; /* Wait for voltage level Vcc > Vprog */ status = rv3029_read_byte(RV3029_REG_STATUS); if (status & ((1 << RV3029_STATUS_VLOW1) | (1 << RV3029_STATUS_VLOW2))) { while (1) { status = rv3029_read_byte(RV3029_REG_STATUS); if (!(status & ((1 << RV3029_STATUS_VLOW1) | (1 << RV3029_STATUS_VLOW2)))) { _delay_ms(50); break; } status &= ~(1 << RV3029_STATUS_VLOW1); status &= ~(1 << RV3029_STATUS_VLOW2); rv3029_write_byte(RV3029_REG_STATUS, status); } } /* Clear EERefOn bit before accessing EEPROM */ old_onoffctrl = rv3029_read_byte(RV3029_REG_ONOFFCTRL); rv3029_write_byte(RV3029_REG_ONOFFCTRL, old_onoffctrl & ~(1 << RV3029_ONOFFCTRL_EEREFON)); while (rv3029_eeprom_busy()) ; /* Busy-wait */ /* Write the new value, if it changed. */ old_value = rv3029_read_byte(reg); if (value != old_value) rv3029_write_byte(reg, value); while (rv3029_eeprom_busy()) ; /* Busy-wait */ /* Restore EERefOn bit */ rv3029_write_byte(RV3029_REG_ONOFFCTRL, old_onoffctrl); } /* Convert a given time from hardware-BCD-format to binary format. */ static void rv3029_time_bcd_to_bin(struct rtc_time *bin_time, const struct rtc_time *bcd_time) { /* Convert rtc_time from BCD to binary. */ rtc_time_from_bcd(bin_time, bcd_time); /* In the hardware format the day count, month count and * weekday count are 1-based. In the binary rtc_time format * these are 0-based. So subtract 1 here to account for this. */ bin_time->day -= 1; bin_time->month -= 1; bin_time->day_of_week -= 1; } /* Convert a given time from binary format to hardware-BCD-format. */ static void rv3029_time_bin_to_bcd(struct rtc_time *bcd_time, const struct rtc_time *bin_time) { struct rtc_time tmp_bin_time = *bin_time; /* In the hardware format the day count, month count and * weekday count are 1-based. In the binary rtc_time format * these are 0-based. So add 1 here to account for this. * Also limit year to 79, because this is the max value according * to the datasheet. */ tmp_bin_time.day += 1; tmp_bin_time.month += 1; tmp_bin_time.day_of_week += 1; tmp_bin_time.year = clamp(tmp_bin_time.year, 0, 79); rtc_time_to_bcd(bcd_time, &tmp_bin_time); } /* Write "time" to the RTC's watch registers. */ void rv3029_write_time(const struct rtc_time *time) { struct rv3029_device *dev = &rv3029_dev; struct rtc_time bcd_time; uint8_t buffer[7]; uint8_t sreg; /* Convert to BCD hardware format. */ rv3029_time_bin_to_bcd(&bcd_time, time); /* Prepare the I2C write buffer. */ buffer[0] = bcd_time.second; buffer[1] = bcd_time.minute; buffer[2] = bcd_time.hour & ~(1 << RV3029_WHOURS_S1224); buffer[3] = bcd_time.day; buffer[4] = bcd_time.day_of_week; buffer[5] = bcd_time.month; buffer[6] = bcd_time.year; /* Schedule the write of the watch hardware registers. */ rv3029_write(RV3029_REG_WSECONDS, buffer, sizeof(buffer)); /* Update the cached time. */ sreg = irq_disable_save(); dev->now = *time; irq_restore(sreg); } /* Async-read callback */ static void read_time_callback(struct twi_transfer *xfer, enum twi_status status) { struct rv3029_device *dev = &rv3029_dev; struct rtc_time bcd_time; uint8_t sreg; if (status != TWI_STAT_FINISHED) { /* I2C finished with an error. */ return; } /* Read the transfer buffer, with interrupts disabled. */ sreg = irq_disable_save(); bcd_time.second = dev->xfer_buffer[0]; bcd_time.minute = dev->xfer_buffer[1]; bcd_time.hour = dev->xfer_buffer[2] & ~(1 << RV3029_WHOURS_S1224); bcd_time.day = dev->xfer_buffer[3]; bcd_time.day_of_week = dev->xfer_buffer[4]; bcd_time.month = dev->xfer_buffer[5]; bcd_time.year = dev->xfer_buffer[6]; irq_restore(sreg); /* Convert from BCD hardware format to binary and store in cache. */ rv3029_time_bcd_to_bin(&dev->now, &bcd_time); } /* Update the cached time. */ void rv3029_read_time(void) { /* Poke an asynchronous read of the watch registers. * read_time_callback() will be called after I2C read finished. */ rv3029_read_async(RV3029_REG_WSECONDS, 7, read_time_callback); } /* Returns the currently cached time. * time: Pointer to the destination buffer. */ void rv3029_get_time(struct rtc_time *time) { struct rv3029_device *dev = &rv3029_dev; uint8_t sreg; sreg = irq_disable_save(); *time = dev->now; irq_restore(sreg); } /* Initialize the RTC */ void rv3029_init(void) { struct rv3029_device *dev = &rv3029_dev; uint8_t tmp; /* Reset the device data structure. */ memset(dev, 0, sizeof(*dev)); /* Initialize I2C transfer data structure. */ twi_transfer_init(&dev->xfer); dev->xfer.address = RV3029_I2C_ADDRESS; dev->xfer.buffer = dev->xfer_buffer; /* Reset the device */ rv3029_write_byte(RV3029_REG_RSTCTRL, (1 << RV3029_RSTCTRL_SYSRES)); _delay_ms(25); /* Clear status bits */ rv3029_write_byte(RV3029_REG_STATUS, 0); /* Disable interrupts */ rv3029_write_byte(RV3029_REG_IRQCTRL, 0); /* Clear interrupt flags */ rv3029_write_byte(RV3029_REG_IRQFLAGS, 0); /* Enable thermometer, scan period 1 second, * Timer 32786 Hz, * 1k trickle charge resistor. */ tmp = (1 << RV3029_EECTRL_THEN) | (1 << RV3029_EECTRL_R1K); rv3029_eeprom_write(RV3029_REG_EECTRL, tmp); /* Enable IRQ pin, * Timer frequency 32 Hz, * Enable self recovery. * Enable EEPROM refresh. * Enable timer auto reload. * Disable timer. * Enable watch. */ tmp = (1 << RV3029_ONOFFCTRL_SRON) | (1 << RV3029_ONOFFCTRL_EEREFON) | (1 << RV3029_ONOFFCTRL_TRON) | (1 << RV3029_ONOFFCTRL_WAON); rv3029_write_byte(RV3029_REG_ONOFFCTRL, tmp); }