Files
kunlun/driver/extern/rtc/src/rx8010_driver.c
2024-09-28 14:24:04 +08:00

523 lines
15 KiB
C

/****************************************************************************
*
* Copyright(c) 2019 by Aerospace C.Power (Chongqing) Microelectronics. ALL RIGHTS RESERVED.
*
* This Information is proprietary to Aerospace C.Power (Chongqing) Microelectronics and MAY NOT
* be copied by any method or incorporated into another program without
* the express written consent of Aerospace C.Power. This Information or any portion
* thereof remains the property of Aerospace C.Power. The Information contained herein
* is believed to be accurate and Aerospace C.Power assumes no responsibility or
* liability for its use in any way and conveys no license or title under
* any patent or copyright and makes no representation or warranty that this
* Information is free from patent or copyright infringement.
*
* Driver for the Epson RTC module RX-8010 SJ
*
* ****************************************************************************/
/* os_ship header files */
#include "os_utils_api.h"
/* iot common header files */
#include "iot_config_api.h"
#include "iot_utils_api.h"
#include "iot_i2c_api.h"
#include "iot_gpio_api.h"
#include "iot_board_api.h"
#include "iot_io_api.h"
#include "iot_errno_api.h"
#include "iot_config.h"
/* iot common header files */
#include "iot_rtc_api.h"
#include "iot_rtc_ext.h"
#if IOT_EXT_RTC_RX8010_ENABLE
#define RX8010_TRANS_BUF_CNT 16
#define RX8010_YEAR_BASE 1900
/* the iic config of rx8010. */
#define RX8010_IIC_PHY_ADDR 0x32 /* shift as (0x32 << 1) in driver. */
#define RX8010_IIC_DEF_PORT 1 /* Port#1. */
#define RX8010_IIC_DEF_SPEED 400 /* 400K. */
#define RX8010_IIC_DEF_NACK_NUM 10 /* N-ACK number. */
// RX-8010 Register definitions
#define RX8010_REG_SEC 0x10
#define RX8010_REG_MIN 0x11
#define RX8010_REG_HOUR 0x12
#define RX8010_REG_WDAY 0x13
#define RX8010_REG_MDAY 0x14
#define RX8010_REG_MONTH 0x15
#define RX8010_REG_YEAR 0x16
// 0x17 is reserved
#define RX8010_REG_ALMIN 0x18
#define RX8010_REG_ALHOUR 0x19
#define RX8010_REG_ALWDAY 0x1A
#define RX8010_REG_TCOUNT0 0x1B
#define RX8010_REG_TCOUNT1 0x1C
#define RX8010_REG_EXT 0x1D
#define RX8010_REG_FLAG 0x1E
#define RX8010_REG_CTRL 0x1F
#define RX8010_REG_USER0 0x20
#define RX8010_REG_USER1 0x21
#define RX8010_REG_USER2 0x22
#define RX8010_REG_USER3 0x23
#define RX8010_REG_USER4 0x24
#define RX8010_REG_USER5 0x25
#define RX8010_REG_USER6 0x26
#define RX8010_REG_USER7 0x27
#define RX8010_REG_USER8 0x28
#define RX8010_REG_USER9 0x29
#define RX8010_REG_USERA 0x2A
#define RX8010_REG_USERB 0x2B
#define RX8010_REG_USERC 0x2C
#define RX8010_REG_USERD 0x2D
#define RX8010_REG_USERE 0x2E
#define RX8010_REG_USERF 0x2F
// 0x30 is reserved
// 0x31 is reserved
#define RX8010_REG_IRQ 0x32
// Extension Register (1Dh) bit positions
#define RX8010_BIT_EXT_TSEL (7 << 0)
#define RX8010_BIT_EXT_WADA (1 << 3)
#define RX8010_BIT_EXT_TE (1 << 4)
#define RX8010_BIT_EXT_USEL (1 << 5)
#define RX8010_BIT_EXT_FSEL (3 << 6)
// Flag Register (1Eh) bit positions
#define RX8010_BIT_FLAG_VLF (1 << 1)
#define RX8010_BIT_FLAG_AF (1 << 3)
#define RX8010_BIT_FLAG_TF (1 << 4)
#define RX8010_BIT_FLAG_UF (1 << 5)
// Control Register (1Fh) bit positions
#define RX8010_BIT_CTRL_TSTP (1 << 2)
#define RX8010_BIT_CTRL_AIE (1 << 3)
#define RX8010_BIT_CTRL_TIE (1 << 4)
#define RX8010_BIT_CTRL_UIE (1 << 5)
#define RX8010_BIT_CTRL_STOP (1 << 6)
#define RX8010_BIT_CTRL_TEST (1 << 7)
/* check the validity of the calendar register */
#define rx8010_sec_is_valid(x) \
(iot_bcd_check(x) && (x) <= 0x59)
#define rx8010_min_is_valid(x) \
(iot_bcd_check(x) && (x) <= 0x59)
#define rx8010_hour_is_valid(x) \
(iot_bcd_check(x) && (x) <= 0x23)
#define rx8010_day_is_valid(x) \
(iot_bcd_check(x) && (x) <= 0x31 && (x) >= 1)
#define rx8010_mon_is_valid(x) \
(iot_bcd_check(x) && (x) <= 0x12 && (x) >= 1)
#define rx8010_year_is_valid(x) \
(iot_bcd_check(x) && (x) <= 0x99)
static uint8_t buf[RX8010_TRANS_BUF_CNT];
static uint8_t rx8010_tm_is_valid(uint8_t *date)
{
uint8_t reason = 0;
if (!date) {
reason = 1;
IOT_ASSERT(0);
goto err;
}
if (!rx8010_sec_is_valid(date[RX8010_REG_SEC-0x10] & 0x7f)) {
reason = 2;
goto err;
}
if (!rx8010_min_is_valid(date[RX8010_REG_MIN-0x10] & 0x7f)) {
reason = 3;
goto err;
}
if (!rx8010_hour_is_valid(date[RX8010_REG_HOUR-0x10] & 0x3f)) {
reason = 4;
goto err;
}
if (!rx8010_day_is_valid(date[RX8010_REG_MDAY-0x10] & 0x3f)) {
reason = 5;
goto err;
}
if (!rx8010_mon_is_valid(date[RX8010_REG_MONTH-0x10] & 0x1f)) {
reason = 6;
goto err;
}
if (!rx8010_year_is_valid(date[RX8010_REG_YEAR-0x10])) {
reason = 7;
goto err;
}
return ERR_OK;
err:
iot_printf("%s err, reason %lu\n", __FUNCTION__, reason);
return ERR_FAIL;
}
static uint32_t rx8010_read_regs(uint8_t addr, uint8_t *data, uint8_t cnt)
{
uint8_t reason = 0;
if(ERR_OK != iot_i2c_write(RX8010_IIC_DEF_PORT, RX8010_IIC_PHY_ADDR,
(char *)&addr, 1)) {
reason = 1;
goto out;
}
os_delay(10);
if(ERR_OK != iot_i2c_read(RX8010_IIC_DEF_PORT, RX8010_IIC_PHY_ADDR,
(char *)data, cnt)) {
reason = 1;
goto out;
}
os_delay(5);
out:
if (reason) {
iot_printf("%s fail, reason %lu\n", __FUNCTION__, reason);
return ERR_FAIL;
}
return ERR_OK;
}
static inline uint32_t rx8010_read_reg(uint8_t addr, uint8_t *data)
{
return rx8010_read_regs(addr, data, 1);
}
static uint32_t rx8010_write_regs(uint8_t addr, uint8_t *data, uint8_t cnt)
{
buf[0] = addr;
os_mem_cpy(buf + 1, data, cnt);
if (cnt > (IOT_ARRAY_CNT(buf) - 1)) {
return ERR_NOMEM;
}
iot_i2c_write(RX8010_IIC_DEF_PORT, RX8010_IIC_PHY_ADDR,
(char *)buf, cnt + 1);
os_delay(10);
return ERR_OK;
}
static inline uint32_t rx8010_write_reg(uint8_t addr, uint8_t data)
{
return rx8010_write_regs(addr, &data, 1);
}
/* calculating Week of zeller formula */
static uint8_t zeller_calc_week(iot_time_tm_t *tm)
{
iot_time_tm_t tm_temp = *tm;
if (tm_temp.tm_mon < 3) {
tm_temp.tm_year -= 1;
tm_temp.tm_mon += 12;
}
int32_t c = tm_temp.tm_year / 100;
int32_t y = tm_temp.tm_year % 100;
int32_t ans = (c / 4 - 2 * c + y + y / 4 + ( 26 * ( tm_temp.tm_mon + 1)) \
/ 10 + tm_temp.tm_mday - 1 ) % 7;
if (ans < 0)
ans += 7;
return (uint8_t)ans;
}
static uint32_t rx8010_set_time(iot_time_tm_t *tm, uint8_t week)
{
uint8_t date[7];
uint8_t ctrl;
uint32_t reason;
uint32_t ret = ERR_OK;
//set STOP bit before changing clock/calendar
ret |= rx8010_read_reg(RX8010_REG_CTRL, &ctrl);
ctrl |= RX8010_BIT_CTRL_STOP;
ret |= rx8010_write_reg(RX8010_REG_CTRL, ctrl);
if (ERR_OK != ret) {
reason = 1;
goto err;
}
//Note: need to subtract 0x10 for index as register offset starts at 0x10
date[RX8010_REG_SEC-0x10] = iot_byte_to_bcd(tm->tm_sec);
date[RX8010_REG_MIN-0x10] = iot_byte_to_bcd(tm->tm_min);
date[RX8010_REG_HOUR-0x10] = iot_byte_to_bcd(tm->tm_hour); //only 24hr time
date[RX8010_REG_MDAY-0x10] = iot_byte_to_bcd(tm->tm_mday);
date[RX8010_REG_MONTH-0x10] = iot_byte_to_bcd(tm->tm_mon + 1);
date[RX8010_REG_YEAR-0x10] = iot_byte_to_bcd(tm->tm_year % 100);
date[RX8010_REG_WDAY-0x10] = iot_byte_to_bcd(week);
iot_printf("%s: write 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
__func__, date[0], date[1], date[2], date[3], date[4], date[5],date[6]);
ret = rx8010_write_regs(RX8010_REG_SEC, date, 7);
if (ERR_OK != ret) {
reason = 2;
goto err;
}
//clear STOP bit after changing clock/calendar
ret |= rx8010_read_reg(RX8010_REG_CTRL, &ctrl);
ctrl = ctrl & ~RX8010_BIT_CTRL_STOP;
ret |= rx8010_write_reg(RX8010_REG_CTRL, ctrl);
if (ERR_OK != ret) {
reason = 3;
goto err;
}
return ERR_OK;
err:
iot_printf("%s: rx8010 set time failed, reason:%d\n", __func__, reason);
return ERR_FAIL;
}
static uint32_t rx8010_get_time(iot_time_tm_t *tm)
{
uint8_t date[7];
uint8_t ctrl;
uint32_t err;
uint32_t reason = 0;
iot_time_tm_t in_tm;
err = rx8010_read_reg(RX8010_REG_CTRL, &ctrl);
if (ERR_OK != err) {
reason = 1;
goto err;
}
if (RX8010_BIT_CTRL_STOP == (ctrl & RX8010_BIT_CTRL_STOP)) {
if (iot_rtc_data_is_revised()) {
iot_rtc_get(&in_tm);
if (ERR_OK != rx8010_set_time(&in_tm, zeller_calc_week(&in_tm))) {
reason = 2;
goto err;
}
}
reason = 3;
goto err;
}
err = rx8010_read_regs(RX8010_REG_SEC, date, 7);
if (ERR_OK != err) {
reason = 4;
goto err;
}
iot_printf("%s: read 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
__func__, date[0], date[1], date[2], date[3], date[4], date[5],date[6]);
if (ERR_OK != rx8010_tm_is_valid(date)) {
reason = 5;
goto err;
}
//Note: need to subtract 0x10 for index as register offset starts at 0x10
tm->tm_sec = iot_bcd_to_byte(date[RX8010_REG_SEC-0x10] & 0x7f);
tm->tm_min = iot_bcd_to_byte(date[RX8010_REG_MIN-0x10] & 0x7f);
tm->tm_hour = iot_bcd_to_byte(date[RX8010_REG_HOUR-0x10] & 0x3f); //only 24-hour clock
tm->tm_mday = iot_bcd_to_byte(date[RX8010_REG_MDAY-0x10] & 0x3f);
tm->tm_mon = iot_bcd_to_byte(date[RX8010_REG_MONTH-0x10] & 0x1f) - 1;
tm->tm_year = iot_bcd_to_byte(date[RX8010_REG_YEAR-0x10]);
if (tm->tm_year < 70)
tm->tm_year += 100;
tm->tm_year += RX8010_YEAR_BASE;
iot_printf("%s: date %d-%d-%d %d:%d:%d\n", __func__,
tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return ERR_OK;
err:
iot_printf("%s err, reason %lu\n", __FUNCTION__, reason);
return ERR_FAIL;
}
static uint32_t rx8010_is_startup(iot_time_tm_t *tm)
{
iot_printf("%s: try to get time\n");
return rx8010_get_time(tm);
}
static uint32_t rx8010_init(iot_time_tm_t *tm, uint32_t *done)
{
uint8_t ctrl[3];
uint8_t need_clear = 0;
uint8_t need_reset = 0;
uint32_t reason = 0;
uint32_t ret;
*done = 0;
//set reserved register 0x17 with specified value of 0xD8
ret = rx8010_write_reg(0x17, 0xD8);
if (ret) {
reason = 1;
goto err;
}
//set reserved register 0x30 with specified value of 0x00
ret = rx8010_write_reg(0x30, 0x00);
if (ret) {
reason = 2;
goto err;
}
//set reserved register 0x31 with specified value of 0x08
ret = rx8010_write_reg(0x31, 0x08);
if (ret) {
reason = 3;
goto err;
}
//set reserved register 0x32 with default value
ret = rx8010_write_reg(RX8010_REG_IRQ, 0x00);
if (ret) {
reason = 4;
goto err;
}
//get current extension, flag, control register values
ret = rx8010_read_regs(RX8010_REG_EXT, ctrl, 3);
if (ret) {
reason = 5;
goto err;
}
//check for VLF Flag (set at power-on)
if ((ctrl[1] & RX8010_BIT_FLAG_VLF)) {
iot_printf("Frequency stop was detected, "
"probably due to a supply voltage drop\n");
need_reset = 1;
}
//check for Alarm Flag
if (ctrl[1] & RX8010_BIT_FLAG_AF) {
iot_printf("Alarm was detected\n");
need_clear = 1;
}
//check for Periodic Timer Flag
if (ctrl[1] & RX8010_BIT_FLAG_TF) {
iot_printf("Periodic timer was detected\n");
need_clear = 1;
}
//check for Update Timer Flag
if (ctrl[1] & RX8010_BIT_FLAG_UF) {
iot_printf("Update timer was detected\n");
need_clear = 1;
}
//reset or clear needed?
if (need_reset) {
//clear 1d, 1e, 1f registers
ctrl[0] = ctrl[1] = ctrl[2] = 0;
ret = rx8010_write_regs(RX8010_REG_EXT, ctrl, 3);
if (ERR_OK != ret) {
reason = 6;
goto err;
}
if (ERR_OK != rx8010_set_time(tm, zeller_calc_week(tm))) {
reason = 7;
goto err;
}
}
else if(need_clear){
//clear flag register
ret = rx8010_write_reg(RX8010_REG_FLAG, 0x00);
if (ERR_OK != ret) {
reason = 8;
goto err;
}
}
ret = rx8010_get_time(tm);
if (ERR_OK != ret) {
reason = 9;
goto err;
}
*done = 1;
return ERR_OK;
err:
iot_printf("%s failed, reason = %d\n", reason);
return ERR_FAIL;
}
static void rx8010_deinit(void)
{
}
static uint32_t rx8010_probe(void)
{
uint8_t data[8], i;
uint32_t ret = ERR_OK;
iot_i2c_module_cfg_t iic_cfg = { 0 };
/* I2C configuration */
iic_cfg.baud = RX8010_IIC_DEF_SPEED;
iic_cfg.gpio.scl = iot_board_get_gpio(GPIO_EXT_RTC_I2C_SCL);
iic_cfg.gpio.sda = iot_board_get_gpio(GPIO_EXT_RTC_I2C_SDA);
iic_cfg.nack_wait_num = RX8010_IIC_DEF_NACK_NUM;
iic_cfg.port = RX8010_IIC_DEF_PORT;
iot_printf("scl pin = %d, sda pin = %d\n", iic_cfg.gpio.scl, iic_cfg.gpio.sda);
if ((GPIO_NO_VALID == iic_cfg.gpio.scl) ||
(GPIO_NO_VALID == iic_cfg.gpio.sda)) {
ret = ERR_NOSUPP;
return ret;
}
/* iot_gpio_open_as_output can cancel the function bound on the GPIO.
* This is not necessary if those GPIOs are not multiplex.
*/
if ((ERR_OK != iot_gpio_open_as_output(iic_cfg.gpio.scl))
|| (ERR_OK != iot_gpio_open_as_output(iic_cfg.gpio.sda))) {
iot_printf("%s open iic gpio fail!\n", __FUNCTION__);
}
if (ERR_OK != iot_i2c_module_init(&iic_cfg)) {
iot_printf("%s init i2c module fail!\n", __FUNCTION__);
IOT_ASSERT(0);
}
os_mem_set(data, 0xff, sizeof(data));
if (ERR_OK != rx8010_write_regs(RX8010_REG_USER0, data, sizeof(data))) {
iot_printf("%s write ram failed!\n", __FUNCTION__);
ret = ERR_NOSUPP;
return ret;
}
os_mem_set(data, 0, sizeof(data));
if (ERR_OK != rx8010_read_regs(RX8010_REG_USER0, data, sizeof(data))) {
iot_printf("%s read ram failed!\n", __FUNCTION__);
ret = ERR_NOSUPP;
return ret;
}
for (i = 0; i < sizeof(data); i++) {
if (data[i] != 0xff) {
ret = ERR_NOSUPP;
iot_printf("%s read ram incorrect data!\n", __FUNCTION__);
return ret;
}
}
iot_printf("%s rtc chip is RX8010 \n", __FUNCTION__);
return ret;
}
/* define external RTC devices rx8010 */
const iot_ext_rtc_drv_t g_rx8010_drv = {
.drv_prode = rx8010_probe,
.drv_init = rx8010_init,
.drv_deinit = rx8010_deinit,
.drv_get_time = rx8010_get_time,
.drv_set_time = rx8010_set_time,
.drv_is_startup = rx8010_is_startup,
};
#endif /* IOT_EXT_RTC_RX8010_ENABLE */