/**************************************************************************** * * 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 */