MAX31341/2 RTC Driver

RtcBase.cpp

Committer:
Mahir Ozturk
Date:
2019-04-10
Revision:
0:1efa49a69ff8

File content as of revision 0:1efa49a69ff8:

/*******************************************************************************
 * Copyright (C) 2018 Maxim Integrated Products, Inc., All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated
 * Products, Inc. shall not be used except as stated in the Maxim Integrated
 * Products, Inc. Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
 *******************************************************************************
 */

#include "RtcBase.h"

#define pr_err(fmt, ...) if(1) printf(fmt " (%s:%d)\r\n", ## __VA_ARGS__, __func__, __LINE__)
#define pr_debug(fmt, ...) if(0) printf(fmt " (%s:%d)\r\n", ## __VA_ARGS__, __func__, __LINE__)

#define BCD2BIN(val) (((val) & 15) + ((val) >> 4) * 10)
#define BIN2BCD(val) ((((val) / 10) << 4) + (val) % 10)

#define POST_INTR_WORK_SIGNAL_ID			0x1

RtcBase::RtcBase(const regmap_t *regmap, I2C *i2c, PinName inta_pin = NC, PinName intb_pin = NC)
{
	if (i2c == NULL) {
		pr_err("i2c object is invalid!");
		while (1);
	}
	i2c_handler = i2c;

	this->regmap = regmap;

	sw_reset_release();

	rtc_start();

	irq_disable_all();

	post_intr_work_thread = new Thread();

	post_intr_work_thread->start(Callback<void()>(this, &RtcBase::post_interrupt_work));

	if (inta_pin != NC) {
		this->inta_pin = new InterruptIn(inta_pin);

		this->inta_pin->fall(Callback<void()>(this, &RtcBase::interrupt_handler));

		this->inta_pin->enable_irq();
	} else {
		this->inta_pin = NULL;
	}

	if (intb_pin != NC) {
		this->intb_pin = new InterruptIn(intb_pin);

		this->intb_pin->fall(Callback<void()>(this, &RtcBase::interrupt_handler));

		this->intb_pin->enable_irq();
	} else {
		this->intb_pin = NULL;
	}
}

RtcBase::~RtcBase()
{
	if (post_intr_work_thread) {
		delete post_intr_work_thread;
	}

	if (inta_pin) {
		delete inta_pin;
	}

	if (intb_pin) {
		delete intb_pin;
	}
}

int RtcBase::read_register(uint8_t reg, uint8_t *value, uint8_t len)
{
	int ret;

	if (value == NULL) {
		pr_err("value is invalid!");
		return -1;
	}

	ret = i2c_handler->write(MAX3134X_I2C_W, (const char *) &reg, 1, true);
	if (ret != 0) {
		pr_err("i2c write failed with %d!", ret);
		return -1;
	}

	ret = i2c_handler->read(MAX3134X_I2C_R, (char *) value, len, false);
	if (ret < 0) {
		pr_err("i2c read failed with %d!", ret);
		return -1;
	}

	return 0;
}

int RtcBase::write_register(uint8_t reg, const uint8_t *value, uint8_t len)
{
	int ret;
	uint8_t *buffer;

	if (value == NULL) {
		pr_err("value is invalid!");
		return -1;
	}

	buffer = new uint8_t[1 + len];
	buffer[0] = reg;

	memcpy(&buffer[1], value, len);

	ret = i2c_handler->write(MAX3134X_I2C_W, (const char *)buffer, 1 + len);
	if (ret != 0) {
		pr_err("i2c write failed with %d!", ret);
	}

	delete[] buffer;

	return ret;
}

int RtcBase::rtc_regs_to_time(struct tm *time, const rtc_time_regs_t *regs)
{
	/* tm_sec seconds [0,61] */
	time->tm_sec = BCD2BIN(regs->seconds.bcd.value);

	/* tm_min minutes [0,59] */
	time->tm_min = BCD2BIN(regs->minutes.bcd.value);

	/* tm_hour hour [0,23] */
	time->tm_hour = BCD2BIN(regs->hours.bcd.value);

	/* tm_wday day of week [0,6] (Sunday = 0) */
	time->tm_wday = BCD2BIN(regs->day.bcd.value) - 1;

	/* tm_mday day of month [1,31] */
	time->tm_mday = BCD2BIN(regs->date.bcd.value);

	/* tm_mon month of year [0,11] */
	time->tm_mon = BCD2BIN(regs->month.bcd.value) - 1;

	/* tm_year years since 2000 */
	if (regs->month.bits.century) {
		time->tm_year = BCD2BIN(regs->year.bcd.value) + 200;
	} else {
		time->tm_year = BCD2BIN(regs->year.bcd.value) + 100;
	}

	/* tm_yday day of year [0,365] */
	time->tm_yday = 0; /* TODO */

	/* tm_isdst daylight savings flag */
	time->tm_isdst = 0; /* TODO */

	return 0;
}

int RtcBase::time_to_rtc_regs(rtc_time_regs_t *regs, const struct tm *time)
{
	/*********************************************************
	 * +----------+------+---------------------------+-------+
	 * | Member   | Type | Meaning                   | Range |
	 * +----------+------+---------------------------+-------+
	 * | tm_sec   | int  | seconds after the minute  | 0-61* |
	 * | tm_min   | int  | minutes after the hour    | 0-59  |
	 * | tm_hour  | int  | hours since midnight      | 0-23  |
	 * | tm_mday  | int  | day of the month          | 1-31  |
	 * | tm_mon   | int  | months since January      | 0-11  |
	 * | tm_year  | int  | years since 1900          |       |
	 * | tm_wday  | int  | days since Sunday         | 0-6   |
	 * | tm_yday  | int  | days since January 1      | 0-365 |
	 * | tm_isdst | int  | Daylight Saving Time flag |       |
	 * +----------+------+---------------------------+-------+
	 * * tm_sec is generally 0-59. The extra range is to accommodate for leap
	 *   seconds in certain systems.
	 *********************************************************/
	regs->seconds.bcd.value = BIN2BCD(time->tm_sec);

	regs->minutes.bcd.value = BIN2BCD(time->tm_min);

	regs->hours.bcd.value= BIN2BCD(time->tm_hour);

	regs->day.bcd.value = BIN2BCD(time->tm_wday + 1);

	regs->date.bcd.value = BIN2BCD(time->tm_mday);

	regs->month.bcd.value = BIN2BCD(time->tm_mon + 1);

	if (time->tm_year >= 200) {
		regs->month.bits.century = 1;
		regs->year.bcd.value = BIN2BCD(time->tm_year - 200);
	} else if (time->tm_year >= 100) {
		regs->month.bits.century = 0;
		regs->year.bcd.value = BIN2BCD(time->tm_year - 100);
	} else {
		pr_err("Invalid set date!");
		return -1;
	}

	return 0;
}

int RtcBase::get_time(struct tm *time)
{
	rtc_time_regs_t time_regs;

	if (time == NULL) {
		pr_err("rtc_ctime is invalid!");
		return -1;
	}

	if (read_register(regmap->seconds, (uint8_t *) &time_regs,
			sizeof(time_regs)) < 0) {
		pr_err("read time registers failed!");
		return -1;
	}

	return rtc_regs_to_time(time, &time_regs);
}

int RtcBase::set_rtc_time()
{
	config_reg2_t reg;

	/* Toggle Set_RTC bit to set RTC date */

	if (read_register(regmap->config_reg2, (uint8_t *)&reg, 1) < 0) {
		pr_err("read time registers failed!");
		return -1;
	}

	reg.bits.set_rtc = CONFIG_REG2_SET_RTC_RTCPRGM;
	if (write_register(regmap->config_reg2, (uint8_t *)&reg, 1) < 0) {
		pr_err("write config2 register failed!");
		return -1;
	}

	/* SET_RTC bit should be kept high at least 10ms */
	ThisThread::sleep_for(10);

	reg.bits.set_rtc = CONFIG_REG2_SET_RTC_RTCRUN;
	if (write_register(regmap->config_reg2, (uint8_t *)&reg, 1) < 0) {
		pr_err("write config2 register failed!");
		return -1;
	}

	return 0;
}

int RtcBase::set_time(const struct tm *time)
{
	rtc_time_regs_t time_regs;

	if (time == NULL) {
		pr_err("rtc_ctime is invalid!");
		return -1;
	}

	time_to_rtc_regs(&time_regs, time);

	if (write_register(regmap->seconds, (const uint8_t *) &time_regs,
			sizeof(time_regs)) < 0) {
		pr_err("read time registers failed!");
		return -1;
	}

	return set_rtc_time();
}

int RtcBase::nvram_write(const uint8_t *buffer, int offset, int length)
{
	int totlen;

	if (regmap->ram_start == REG_NOT_AVAILABLE) {
		pr_err("Device does not have NVRAM!");
		return -1;
	}

	totlen = regmap->ram_end - regmap->ram_start + 1;

	if ((offset + length) > totlen) {
		return -1;
	}

	if (length == 0) {
		return 0;
	}

	if (write_register(regmap->ram_start + offset, buffer, length) < 0) {
		return -1;
	}

	return 0;
}

int RtcBase::nvram_read(uint8_t *buffer, int offset, int length)
{
	int totlen;

	if (regmap->ram_start == REG_NOT_AVAILABLE) {
		pr_err("Device does not have NVRAM!");
		return -1;
	}

	totlen = regmap->ram_end - regmap->ram_start + 1;

	if ((offset + length) > totlen) {
		return -1;
	}

	if (length == 0) {
		return -1;
	}

	if (read_register(regmap->ram_start + offset, buffer, length) < 0) {
		return -1;
	}

	return 0;
}

int RtcBase::nvram_size()
{
	if ((regmap->ram_start == REG_NOT_AVAILABLE) ||
			(regmap->ram_end == REG_NOT_AVAILABLE)) {
		return 0;
	}

	return regmap->ram_end - regmap->ram_start + 1;
}

int RtcBase::time_to_alarm_regs(alarm_regs_t &regs, const struct tm *alarm_time)
{
	regs.sec.bcd.value = BIN2BCD(alarm_time->tm_sec);
	regs.min.bcd.value = BIN2BCD(alarm_time->tm_min);
	regs.hrs.bcd.value = BIN2BCD(alarm_time->tm_hour);

	if (regs.day_date.bits.dy_dt == 0) {
		/* Date match */
		regs.day_date.bcd_date.value = BIN2BCD(alarm_time->tm_mday);
	} else {
		/* Day match */
		regs.day_date.bcd_day.value = BIN2BCD(alarm_time->tm_wday);
	}
	regs.mon.bcd.value = BIN2BCD(alarm_time->tm_mon);

	return 0;
}

int RtcBase::alarm_regs_to_time(struct tm *alarm_time, const alarm_regs_t *regs)
{
	alarm_time->tm_sec = BCD2BIN(regs->sec.bcd.value);
	alarm_time->tm_min = BCD2BIN(regs->min.bcd.value);
	alarm_time->tm_hour = BCD2BIN(regs->hrs.bcd.value);

	if (regs->day_date.bits.dy_dt == 0) { /* date */
		alarm_time->tm_mday = BCD2BIN(regs->day_date.bcd_date.value);
	} else { /* day */
		alarm_time->tm_wday = BCD2BIN(regs->day_date.bcd_day.value);
	}

	if (regmap->alm1_mon != REG_NOT_AVAILABLE) {
		alarm_time->tm_mon = BCD2BIN(regs->mon.bcd.value) - 1;
	}

	if (regmap->alm1_year != REG_NOT_AVAILABLE) {
		alarm_time->tm_year = BCD2BIN(regs->year.bcd.value) + 100;	/* XXX no century bit */
	}

	return 0;
}

int RtcBase::set_alarm_period(alarm_no_t alarm_no, alarm_regs_t &regs, alarm_period_t period)
{
	regs.sec.bits.a1m1 = 1;
	regs.min.bits.a1m2 = 1;
	regs.hrs.bits.a1m3 = 1;
	regs.day_date.bits.a1m4 = 1;
	regs.mon.bits.a1m5 = 1;
	regs.mon.bits.a1m6 = 1;
	regs.day_date.bits.dy_dt = 1;

	switch (period) {
	case ALARM_PERIOD_ONETIME:
		if ((alarm_no == ALARM2) || (regmap->alm1_year == REG_NOT_AVAILABLE)) { /* not supported! */
			return -1;
		}
		regs.mon.bits.a1m6 = 0;
	case ALARM_PERIOD_YEARLY:
		if ((alarm_no == ALARM2) || (regmap->alm1_mon == REG_NOT_AVAILABLE)) { /* not supported! */
			return -1;
		}
		regs.mon.bits.a1m5 = 0;
	case ALARM_PERIOD_MONTHLY:
		regs.day_date.bits.dy_dt = 0;
	case ALARM_PERIOD_WEEKLY:
		regs.day_date.bits.a1m4 = 0;
	case ALARM_PERIOD_DAILY:
		regs.hrs.bits.a1m3 = 0;
	case ALARM_PERIOD_HOURLY:
		regs.min.bits.a1m2 = 0;
	case ALARM_PERIOD_EVERYMINUTE:
		regs.sec.bits.a1m1 = 0;
	case ALARM_PERIOD_EVERYSECOND:
		if ((alarm_no == ALARM2) && (period == ALARM_PERIOD_EVERYSECOND)) {
			return -1; /* Alarm2 does not support "once per second" alarm*/
		}
		break;
	default:
		return -1;
	}

	return 0;
}

int RtcBase::set_alarm_regs(alarm_no_t alarm_no, const alarm_regs_t *regs)
{
	uint8_t *ptr_regs = (uint8_t *)regs;
	uint8_t off = 0;
	uint8_t dev_ba;
	uint8_t len = sizeof(alarm_regs_t);

	if (alarm_no == ALARM1) {
		dev_ba = regmap->alm1_sec;
		if (regmap->alm1_mon == REG_NOT_AVAILABLE) len -= 2; /* discard mon & year registers */
	} else {
		dev_ba = regmap->alm2_min;
		off = 1;	/* starts from min register */
		len -= 3;	/* discard min, mon & sec registers */
	}

	return write_register(dev_ba, &ptr_regs[off], len);
}

int RtcBase::get_alarm_regs(alarm_no_t alarm_no, alarm_regs_t *regs)
{
	uint8_t *ptr_regs = (uint8_t *)regs;
	uint8_t off = 0;
	uint8_t dev_ba;
	uint8_t len = sizeof(alarm_regs_t);

	if (alarm_no == ALARM1) {
		dev_ba = regmap->alm1_sec;
		if (regmap->alm1_mon == REG_NOT_AVAILABLE) len -= 2; /* discard mon & year registers */
	} else {
		regs->sec.raw = 0;	/* zeroise second register for alarm2 */
		dev_ba = regmap->alm2_min;
		off = 1;	/* starts from min register (no sec register) */
		len -= 2;	/* discard mon & sec registers */
	}

	return read_register(dev_ba, &ptr_regs[off], len);
}

int RtcBase::set_alarm(alarm_no_t alarm_no, const struct tm *alarm_time, alarm_period_t period)
{
	int ret;
	alarm_regs_t regs;

	ret = set_alarm_period(alarm_no, regs, period);
	if (ret) {
		return ret;
	}

	/* Convert time structure to alarm registers */
	ret = time_to_alarm_regs(regs, alarm_time);
	if (ret) {
		return ret;
	}

	ret = set_alarm_regs(alarm_no, &regs);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::get_alarm(alarm_no_t alarm_no, struct tm *alarm_time, alarm_period_t *period, bool *is_enabled)
{
	int ret;
	alarm_regs_t regs;
	uint8_t reg;

	ret = get_alarm_regs(alarm_no, &regs);
	if (ret) {
		return ret;
	}

	/* Convert alarm registers to time structure */
	ret = alarm_regs_to_time(alarm_time, &regs);
	if (ret) {
		return ret;
	}

	*period = (alarm_no == ALARM1) ? ALARM_PERIOD_EVERYSECOND : ALARM_PERIOD_EVERYMINUTE;

	if ((alarm_no == ALARM1) && (regs.sec.bits.a1m1 == 0)) *period = ALARM_PERIOD_EVERYMINUTE;
	if (regs.min.bits.a1m2 == 0) *period = ALARM_PERIOD_HOURLY;
	if (regs.hrs.bits.a1m3 == 0) *period = ALARM_PERIOD_DAILY;
	if (regs.day_date.bits.a1m4 == 0) *period = ALARM_PERIOD_WEEKLY;
	if (regs.day_date.bits.dy_dt == 0) *period = ALARM_PERIOD_MONTHLY;
	if ((alarm_no == ALARM1) && (regmap->alm1_mon != REG_NOT_AVAILABLE) && (regs.mon.bits.a1m5 == 0)) *period = ALARM_PERIOD_YEARLY;
	if ((alarm_no == ALARM1) && (regmap->alm1_mon != REG_NOT_AVAILABLE) && (regs.mon.bits.a1m6 == 0)) *period = ALARM_PERIOD_ONETIME;

	ret = read_register(regmap->int_en_reg, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	if (alarm_no == ALARM1) {
		*is_enabled = (reg & (1 << INTR_ID_ALARM1)) != 0;
	} else {
		*is_enabled = (reg & (1 << INTR_ID_ALARM2)) != 0;
	}

	return 0;
}

int RtcBase::set_power_mgmt_mode(power_mgmt_mode_t mode)
{
	int ret;
	pwr_mgmt_reg_t reg;

	if (regmap->pwr_mgmt_reg == REG_NOT_AVAILABLE) {
		pr_err("Device does not support power mgmt!");
		return -1;
	}

	ret = read_register(regmap->pwr_mgmt_reg, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.d_mode = mode;

	ret = write_register(regmap->pwr_mgmt_reg, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::comparator_threshold_level(comp_thresh_t th)
{
	int ret;
	config_reg2_t reg;

	if (regmap->pwr_mgmt_reg == REG_NOT_AVAILABLE) {
		pr_err("Device does not support analog comparator!");
		return -1;
	}

	ret = read_register(regmap->config_reg2, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.bref = th;

	ret = write_register(regmap->config_reg2, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::supply_select(power_mgmt_supply_t supply)
{
	int ret;
	pwr_mgmt_reg_t reg;

	if (regmap->pwr_mgmt_reg == REG_NOT_AVAILABLE) {
		pr_err("Device does not support power mgmt!");
		return -1;
	}

	ret = read_register(regmap->pwr_mgmt_reg, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	switch (supply) {
	case POW_MGMT_SUPPLY_SEL_VCC:
		reg.bits.d_man_sel = 1;
		reg.bits.d_vback_sel = 0;
		break;
	case POW_MGMT_SUPPLY_SEL_AIN:
		reg.bits.d_man_sel = 1;
		reg.bits.d_vback_sel = 1;
		break;
	case POW_MGMT_SUPPLY_SEL_AUTO:
	default:
		reg.bits.d_man_sel = 0;
		break;
	}

	ret = write_register(regmap->pwr_mgmt_reg, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::trickle_charger_enable(trickle_charger_ohm_t res, bool diode)
{
	int ret;
	struct {
		unsigned char ohm      : 2;
		unsigned char diode    : 1;
		unsigned char schottky : 1;
		unsigned char          : 4;
	} reg;

	if (regmap->trickle_reg == REG_NOT_AVAILABLE) { /* trickle charger not supported! */
		pr_err("Device does not support trickle charger!");
		return -1;
	}

	reg.ohm = res;

	reg.diode = (diode) ? 1 : 0;

	reg.schottky = 1; /* always enabled */

	ret = write_register(regmap->trickle_reg, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::trickle_charger_disable()
{
	int ret;
	uint8_t reg;

	if (regmap->trickle_reg == REG_NOT_AVAILABLE) { /* trickle charger not supported! */
		pr_err("Device does not support trickle charger!");
		return -1;
	}

	reg = 0;

	ret = write_register(regmap->trickle_reg, &reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}


int RtcBase::set_output_square_wave_frequency(square_wave_out_freq_t freq)
{
	int ret;
	config_reg1_t reg;

	ret = read_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.rs = freq;

	ret = write_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::set_clock_sync_delay(sync_delay_t delay)
{
	int ret;
	clock_sync_reg_t reg;

	ret = read_register(regmap->clock_sync_delay, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.sync_delay = delay;

	ret = write_register(regmap->clock_sync_delay, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::set_clkin_frequency(clkin_freq_t freq)
{
	int ret;
	config_reg1_t reg;

	ret = read_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.clksel = freq;

	ret = write_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	if (freq == CLKIN_FREQ_1HZ) {
		ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_1SEC);
	} else {
		ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_100MS);
	}

	return ret;
}

int RtcBase::configure_intb_clkout_pin(config_intb_clkout_pin_t sel)
{
	int ret;
	config_reg1_t reg;

	ret = read_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.intcn = (sel == CONFIGURE_PIN_AS_INTB) ? 1 : 0;

	ret = write_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::configure_inta_clkin_pin(config_inta_clkin_pin_t sel)
{
	int ret;
	config_reg1_t reg;

	ret = read_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.eclk = (sel == CONFIGURE_PIN_AS_CLKIN) ? 1 : 0;

	ret = write_register(regmap->config_reg1, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	if (sel == CONFIGURE_PIN_AS_CLKIN) {
		/* Default synchronization delay for external clock mode */
		ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_1SEC);
	} else {
		/* Synchronization delay for internal oscillator mode */
		ret = set_clock_sync_delay(SYNC_DLY_LESS_THAN_20MS);
	}

	return ret;
}

int RtcBase::timer_init(uint8_t value, bool repeat, timer_freq_t freq)
{
	int ret;
	timer_config_t reg_cfg;

	ret = read_register(regmap->timer_config, (uint8_t *)&reg_cfg, 1);
	if (ret) {
		return ret;
	}

	reg_cfg.bits.te = 0; /* timer is reset */
	reg_cfg.bits.tpause = 1; /* timer is paused */
	reg_cfg.bits.trpt = repeat ? 1 : 0;	/* Timer repeat mode */
	reg_cfg.bits.tfs = freq;	/* Timer frequency */

	ret = write_register(regmap->timer_config, (uint8_t *)&reg_cfg, 1);
	if (ret) {
		return ret;
	}

	ret = write_register(regmap->timer_init, (uint8_t *)&value, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

uint8_t RtcBase::timer_get()
{
	int ret;
	uint8_t reg;

	ret = read_register(regmap->timer_count, (uint8_t *)&reg, 1);
	if (ret) {
		return (uint8_t) - 1;
	}

	return reg;
}

int RtcBase::timer_start()
{
	int ret;
	timer_config_t reg;

	ret = read_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.te = 1;
	reg.bits.tpause = 0;

	ret = write_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::timer_pause()
{
	int ret;
	timer_config_t reg;

	ret = read_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.te = 1;
	reg.bits.tpause = 1;

	ret = write_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::timer_continue()
{
	int ret;
	timer_config_t reg;

	ret = read_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.te = 1;
	reg.bits.tpause = 0;

	ret = write_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::timer_stop()
{
	int ret;
	timer_config_t reg;

	ret = read_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.te = 0;
	reg.bits.tpause = 1;

	ret = write_register(regmap->timer_config, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::data_retention_mode_config(int state)
{
	int ret;
	config_reg1_t cfg1;
	config_reg2_t cfg2;

	ret = read_register(regmap->config_reg1, (uint8_t *)&cfg1, 1);
	if (ret) {
		return ret;
	}

	ret = read_register(regmap->config_reg2, (uint8_t *)&cfg2, 1);
	if (ret) {
		return ret;
	}

	if (state) {
		cfg1.bits.osconz = 1;
		cfg2.bits.data_reten = 1;
	} else {
		cfg1.bits.osconz = 0;
		cfg2.bits.data_reten = 0;
	}

	ret = write_register(regmap->config_reg1, (uint8_t *)&cfg1, 1);
	if (ret) {
		return ret;
	}

	ret = write_register(regmap->config_reg2, (uint8_t *)&cfg2, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::data_retention_mode_enter()
{
	return data_retention_mode_config(1);
}

int RtcBase::data_retention_mode_exit()
{
	return data_retention_mode_config(0);
}

int RtcBase::i2c_timeout_config(int enable)
{
	int ret;
	config_reg2_t reg;

	ret = read_register(regmap->config_reg2, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	reg.bits.i2c_timeout = (enable) ? 1 : 0;

	ret = write_register(regmap->config_reg2, (uint8_t *)&reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::i2c_timeout_enable()
{
	return i2c_timeout_config(1);
}

int RtcBase::i2c_timeout_disable()
{
	return i2c_timeout_config(0);
}

int RtcBase::irq_enable(intr_id_t id)
{
	int ret;
	uint8_t reg;

	ret = read_register(regmap->int_en_reg, &reg, 1);
	if (ret) {
		return ret;
	}

	reg |= (1 << id);

	ret = write_register(regmap->int_en_reg, &reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::irq_disable(intr_id_t id)
{
	int ret;
	uint8_t reg;

	ret = read_register(regmap->int_en_reg, &reg, 1);
	if (ret) {
		return ret;
	}

	reg &= ~(1 << id);

	ret = write_register(regmap->int_en_reg, &reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

int RtcBase::irq_disable_all()
{
	int ret;
	uint8_t reg = 0;

	ret = write_register(regmap->int_en_reg, &reg, 1);
	if (ret) {
		return ret;
	}

	return 0;
}

void RtcBase::set_intr_handler(intr_id_t id, interrupt_handler_function func, void *cb)
{
	interrupt_handler_list[id].func = func;
	interrupt_handler_list[id].cb = cb;
}

void RtcBase::post_interrupt_work()
{
	int ret;
	uint8_t reg, inten, mask;


	while (true) {
		Thread::signal_wait(POST_INTR_WORK_SIGNAL_ID);

		ret = read_register(regmap->int_status_reg, &reg, 1);
		if (ret) {
			pr_err("Read interrupt status register failed!");
		}

		ret = read_register(regmap->int_en_reg, &inten, 1);
		if (ret) {
			pr_err("Read interrupt enable register failed!");
		}

		for (int i = 0; i < INTR_ID_END; i++) {
			mask = (1 << i);
			if ((reg & mask) && (inten & mask)) {
				if (interrupt_handler_list[i].func != NULL) {
					interrupt_handler_list[i].func(interrupt_handler_list[i].cb);
				}
			}
		}
	}
}

void RtcBase::interrupt_handler()
{
	post_intr_work_thread->signal_set(POST_INTR_WORK_SIGNAL_ID);
}

int RtcBase::sw_reset_assert()
{
	int ret;
	config_reg1_t config_reg1;

	ret = read_register(regmap->config_reg1, (uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	config_reg1.bits.swrstn = 0;	/* Put device in reset state */

	ret = write_register(regmap->config_reg1, (const uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	return 0;
}

int RtcBase::sw_reset_release()
{
	int ret;
	config_reg1_t config_reg1;

	ret = read_register(regmap->config_reg1, (uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	config_reg1.bits.swrstn = 1;	/* Remove device from reset state */

	ret = write_register(regmap->config_reg1, (const uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	return 0;
}

int RtcBase::rtc_start()
{
	int ret;
	config_reg1_t config_reg1;

	ret = read_register(regmap->config_reg1, (uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	config_reg1.bits.osconz = 0;	/* Enable the oscillator */

	ret = write_register(regmap->config_reg1, (const uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	return 0;
}

int RtcBase::rtc_stop()
{
	int ret;
	config_reg1_t config_reg1;

	ret = read_register(regmap->config_reg1, (uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	config_reg1.bits.osconz = 1;	/* Disable the oscillator */

	ret = write_register(regmap->config_reg1, (const uint8_t *) &config_reg1, 1);
	if (ret < 0) {
		pr_err("read_register failed!");
		return ret;
	}

	return 0;
}