New file |
| | |
| | | /********************************************************************** |
| | | * Copyright: (C)2024 LingYun IoT System Studio |
| | | * Author: GuoWenxue<guowenxue@gmail.com> |
| | | * |
| | | * Description: ISL1208 RTC driver on ISKBoard |
| | | * |
| | | * ChangeLog: |
| | | * Version Date Author Description |
| | | * V1.0.0 2024.08.29 GuoWenxue Release initial version |
| | | * |
| | | ***********************************************************************/ |
| | | |
| | | #include <stdio.h> |
| | | #include <string.h> |
| | | #include "isl1208.h" |
| | | #include "i2c_bitbang.h" |
| | | |
| | | //#define CONFIG_DEBUG_RTC /* Enable ISL1208 RTC debug */ |
| | | |
| | | #define ISL1208_CHIPADDR 0x6F /* ISL1208 7-Bits Chip address */ |
| | | |
| | | |
| | | #ifdef CONFIG_DEBUG_RTC |
| | | #define rtc_print(format,args...) printf(format, ##args) |
| | | #else |
| | | #define rtc_print(format,args...) do{} while(0) |
| | | #endif |
| | | |
| | | |
| | | /*+--------------------------------------+ |
| | | *| RTC Chipset ISL1208 Register | |
| | | *+--------------------------------------+*/ |
| | | |
| | | /* rtc section */ |
| | | #define ISL1208_REG_SC 0x00 |
| | | #define ISL1208_REG_MN 0x01 |
| | | #define ISL1208_REG_HR 0x02 |
| | | #define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */ |
| | | #define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ |
| | | #define ISL1208_REG_DT 0x03 |
| | | #define ISL1208_REG_MO 0x04 |
| | | #define ISL1208_REG_YR 0x05 |
| | | #define ISL1208_REG_DW 0x06 |
| | | #define ISL1208_RTC_SECTION_LEN 7 /* RTC Section register */ |
| | | #define REGS_RTC_SR_LEN 8 /* RTC Section and status register */ |
| | | |
| | | /* control/status section */ |
| | | #define ISL1208_REG_SR 0x07 |
| | | #define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ |
| | | #define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ |
| | | #define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ |
| | | #define ISL1208_REG_SR_EVT (1<<3) /* event */ |
| | | #define ISL1208_REG_SR_ALM (1<<2) /* alarm */ |
| | | #define ISL1208_REG_SR_BAT (1<<1) /* battery */ |
| | | #define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ |
| | | #define ISL1208_REG_INT 0x08 |
| | | #define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */ |
| | | #define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */ |
| | | #define ISL1219_REG_EV 0x09 |
| | | #define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */ |
| | | #define ISL1219_REG_EV_EVIENB (1<<7) /* event in pull-up disable */ |
| | | #define ISL1208_REG_ATR 0x0a |
| | | #define ISL1208_REG_DTR 0x0b |
| | | |
| | | /* alarm section */ |
| | | #define ISL1208_REG_SCA 0x0c |
| | | #define ISL1208_REG_MNA 0x0d |
| | | #define ISL1208_REG_HRA 0x0e |
| | | #define ISL1208_REG_DTA 0x0f |
| | | #define ISL1208_REG_MOA 0x10 |
| | | #define ISL1208_REG_DWA 0x11 |
| | | #define ISL1208_ALARM_SECTION_LEN 6 |
| | | |
| | | /* user section */ |
| | | #define ISL1208_REG_USR1 0x12 |
| | | #define ISL1208_REG_USR2 0x13 |
| | | #define ISL1208_USR_SECTION_LEN 2 |
| | | |
| | | #define ISL1208_REGS_MAX 0x13 |
| | | |
| | | const char *weekday[7]={"Sun.","Mon.","Tue.","Wed.","Thu.","Fri.","Sat." }; |
| | | |
| | | |
| | | /* |
| | | *+----------------------------------+ |
| | | *| ISL1208 Low level API | |
| | | *+----------------------------------+ |
| | | */ |
| | | |
| | | static int isl1208_i2c_read_regs(uint8_t regaddr, uint8_t *regs, uint8_t len) |
| | | { |
| | | uint8_t i; |
| | | int rv = 0; |
| | | uint8_t byte; |
| | | uint8_t ack; |
| | | |
| | | if( !regs || len<=0 ) |
| | | { |
| | | rtc_print("ISL1208: Invalid input arguments\r\n"); |
| | | return -1; |
| | | } |
| | | |
| | | I2C_StartCondition(); |
| | | |
| | | if( (rv=I2C_SendAddress(I2C_WR)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | if( (rv=I2C_WriteByte(regaddr)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set register[0x%02x] failure: rv=0x%02x\r\n", regaddr, rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | I2C_StartCondition(); |
| | | |
| | | if( (rv=I2C_SendAddress( I2C_RD )) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | for (i=0; i<len; i++) |
| | | { |
| | | if( i == (len-1) ) |
| | | ack = ACK_NONE; |
| | | else |
| | | ack = ACK; |
| | | |
| | | if( (rv=I2C_ReadByte(&byte, ack, I2C_CLK_STRETCH_TIMEOUT)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Read register data failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | regs[i] = byte; |
| | | } |
| | | |
| | | OUT: |
| | | I2C_StopCondition(); |
| | | return rv; |
| | | } |
| | | |
| | | static int isl1208_i2c_write_regs(uint8_t regaddr, uint8_t *regs, uint8_t len) |
| | | { |
| | | uint8_t i = 0; |
| | | int rv = 0; |
| | | |
| | | I2C_StartCondition(); |
| | | |
| | | if( (rv=I2C_SendAddress(I2C_WR)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Send chipset address[W] failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | if( (rv=I2C_WriteByte(regaddr)) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set register[0x%02x] failure: rv=0x%02x\r\n", regaddr, rv); |
| | | goto OUT; |
| | | } |
| | | |
| | | for (i=0; i<len; i++) |
| | | { |
| | | if( (rv=I2C_WriteByte(regs[i])) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Write register data failure: rv=0x%02x\r\n", rv); |
| | | goto OUT; |
| | | } |
| | | } |
| | | |
| | | rv = 0; |
| | | |
| | | OUT: |
| | | I2C_StopCondition(); |
| | | return rv; |
| | | } |
| | | |
| | | |
| | | #define bcd2bin(x) (((x) & 0x0f) + ((x) >> 4) * 10) |
| | | #define bin2bcd(x) ((((x) / 10) << 4) + (x) % 10) |
| | | |
| | | int set_hwclock(hwclock_t tm) |
| | | { |
| | | int rv; |
| | | uint8_t regs[ISL1208_RTC_SECTION_LEN] = { 0, }; |
| | | uint8_t sr; |
| | | |
| | | regs[ISL1208_REG_SC] = bin2bcd(tm.tm_sec); |
| | | regs[ISL1208_REG_MN] = bin2bcd(tm.tm_min); |
| | | regs[ISL1208_REG_HR] = bin2bcd(tm.tm_hour) | ISL1208_REG_HR_MIL; |
| | | |
| | | regs[ISL1208_REG_DT] = bin2bcd(tm.tm_mday); |
| | | regs[ISL1208_REG_MO] = bin2bcd(tm.tm_mon); |
| | | regs[ISL1208_REG_YR] = bin2bcd(tm.tm_year - 2000); |
| | | //regs[ISL1208_REG_DW] = bin2bcd(tm.tm_wday & 7); |
| | | |
| | | if( i2c_lock(ISL1208_CHIPADDR) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Lock I2C bus failure!\r\n"); |
| | | return -2; |
| | | } |
| | | |
| | | rv = isl1208_i2c_read_regs(ISL1208_REG_SR, &sr, 1); |
| | | if( rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: read Status Register failure, rv=%d!\r\n", rv); |
| | | rv = -3; |
| | | goto OUT; |
| | | } |
| | | sr |= ISL1208_REG_SR_WRTC; |
| | | rv = isl1208_i2c_write_regs(ISL1208_REG_SR, &sr, 1); |
| | | if( rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set Status Register WRTC failure, rv=%d!\r\n", rv); |
| | | rv = -4; |
| | | goto OUT; |
| | | } |
| | | |
| | | rv=isl1208_i2c_write_regs(ISL1208_REG_SC, regs, ISL1208_RTC_SECTION_LEN); |
| | | if(rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Set RTC section registeres failure, rv=%d!\r\n", rv); |
| | | rv = -5; |
| | | goto OUT; |
| | | } |
| | | |
| | | sr &= (~ISL1208_REG_SR_WRTC); |
| | | rv = isl1208_i2c_write_regs(ISL1208_REG_SR, &sr, 1); |
| | | if( rv < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Clear Status Register WRTC failure, rv=%d!\r\n", rv); |
| | | rv = -6; |
| | | goto OUT; |
| | | } |
| | | |
| | | OUT: |
| | | i2c_free(); |
| | | return rv; |
| | | } |
| | | |
| | | int get_hwclock(hwclock_t *tm) |
| | | { |
| | | int rv = 0; |
| | | uint8_t regs[REGS_RTC_SR_LEN] = { 0, }; |
| | | |
| | | if( !tm ) |
| | | { |
| | | rtc_print("ISL1208: Invalid input arugments!\r\n"); |
| | | return -1; |
| | | } |
| | | |
| | | if( i2c_lock(ISL1208_CHIPADDR) < 0 ) |
| | | { |
| | | rtc_print("ISL1208: Initial I2C bus failure!\r\n"); |
| | | return -2; |
| | | } |
| | | |
| | | rv = isl1208_i2c_read_regs(ISL1208_REG_SC, regs, REGS_RTC_SR_LEN); |
| | | if (rv < 0) |
| | | { |
| | | rtc_print("ISL1208: read RTC_SECTION and SR registeres failure, rv=%d!\r\n", rv); |
| | | rv = -3; |
| | | goto OUT; |
| | | } |
| | | |
| | | if( regs[ISL1208_REG_SR] & ISL1208_REG_SR_RTCF ) |
| | | { |
| | | rtc_print("ISL1208: Initialize RTC time after power failure!\r\n"); |
| | | rv = -4; |
| | | goto OUT; |
| | | } |
| | | |
| | | tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]); |
| | | tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]); |
| | | |
| | | { |
| | | const uint8_t _hr = regs[ISL1208_REG_HR]; |
| | | |
| | | if (_hr & ISL1208_REG_HR_MIL) /* 24H */ |
| | | { |
| | | tm->tm_hour = bcd2bin(_hr & 0x3f); |
| | | } |
| | | else /* 12H */ |
| | | { |
| | | tm->tm_hour = bcd2bin(_hr & 0x1f); |
| | | if (_hr & ISL1208_REG_HR_PM) |
| | | tm->tm_hour += 12; |
| | | } |
| | | } |
| | | |
| | | tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]); |
| | | tm->tm_mon = bcd2bin(regs[ISL1208_REG_MO]); |
| | | tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 2000; |
| | | tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]); |
| | | |
| | | OUT: |
| | | i2c_free(); |
| | | return rv; |
| | | } |
| | | |
| | | int set_rtctime(char *time) |
| | | { |
| | | hwclock_t tm; |
| | | |
| | | if( 6 != sscanf(time, "%d-%d-%d %d:%d:%d", |
| | | &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec)) |
| | | { |
| | | return -1; |
| | | } |
| | | |
| | | set_hwclock(tm); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | int get_rtctime(char *buf, int size) |
| | | { |
| | | hwclock_t tm; |
| | | |
| | | memset(buf, 0, size); |
| | | |
| | | if( get_hwclock(&tm) < 0 ) |
| | | return -1; |
| | | |
| | | snprintf(buf, size, "%04d-%02d-%02d %02d:%02d:%02d\r\n", |
| | | tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); |
| | | |
| | | return 0; |
| | | } |
| | | |
| | | void print_rtctime(void) |
| | | { |
| | | hwclock_t tm; |
| | | |
| | | if( get_hwclock(&tm) < 0 ) |
| | | return ; |
| | | |
| | | printf("%04d-%02d-%02d %02d:%02d:%02d\r\n", |
| | | tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); |
| | | } |