1、背景介绍
飞腾2000+芯片通过I2C连接一块RTC时钟芯片(BellingBL5372)来实现麒麟信安系统下后的守时功能。目前BIOS支持UEFI功能,BIOS上电后能获取RTC时间,并将时间写入相应的UEFI变量或内存区域,操作系统上电后使用UEFI的APIs来读取相应的RTC时间变量或内存区域。
2、问题描述
在低温情况下(-42度),发现BIOS有概率无法获取到RTC时间,导致写入UEFI变量写入失败,从而操作系统也无法获取正确时间,变成系统出厂时间(2019年9月3日),如下图为出错情况:
但其实RTC芯片是正常工作的,当恢复到常温后又能获取到正确的时间,如下图
问题表现为低温情况下RTC守时不准。
3、原因分析
上电时BIOS通过I2C去访问RTC时钟芯片,由于FT2000+芯片商业档工作范围为0-70度,在低温-42度情况下完全有可能工作不正常,尤其是刚上电的那十几秒内,此时的I2C有概率无法读到RTC时钟芯片,导致BIOS时间无法获取,进一步导致系统下守时失败。
解决RTC问题其实就是解决一个温度问题,确保在温度满足芯片工作范围的情况下去读取RTC时钟芯片,也就是将读取时间后移,让芯片工作一段时间温度达到0度后再去读取。
对系统来说,有两种获取RTC时间的方式,一种就是从BIOS UEFI空间中去获取,另一种是调用系统下的驱动直接去读取时间。明显第二种方法是在操作系统启动过程中了,读取时间后移,能确保读到正确的时间。
4、问题解决
明确了通过加载驱动来获取RTC时间,那就按照这个思路去实现即可。
首先,在BIOS中添加RTC设备,通过dtb的方式添加,如下:
&i2c0 {
status = "ok";
rtc@32 {
compatible = "beilin,bl5372";
reg = <0x32>;
status = "ok";
};
};
这样驱动就能和设备匹配上,驱动代码如下:
/*
* An I2C driver for Beilin BL5372 RTC
*/
#include
#include
#include
#include
#include
#include
#define DEG 0
#define DRV_VERSION "2.0"
#define TIME24 0
#define RS5C_ADDR(R) (((R) << 4) | 0)
#define RS5C372_REG_SECS 0
#define RS5C372_REG_MINS 1
#define RS5C372_REG_HOURS 2
#define RS5C372_REG_WDAY 3
#define RS5C372_REG_DAY 4
#define RS5C372_REG_MONTH 5
#define RS5C372_REG_YEAR 6
#define RS5C372_REG_TRIM 7
#define RS5C_REG_ALARM_A_MIN 8 /* or ALARM_W */
#define RS5C_REG_ALARM_A_HOURS 9
#define RS5C_REG_ALARM_A_WDAY 10
#define RS5C_REG_ALARM_B_MIN 11 /* or ALARM_D */
#define RS5C_REG_ALARM_B_HOURS 12
#define RS5C_REG_ALARM_B_WDAY 13 /* (ALARM_B only) */
#define RS5C_REG_CTRL1 14
#define RS5C_REG_CTRL2 15
#define DEVICE_ADDR 0x32 //0x5d
static unsigned rs5c_reg2hr(unsigned reg)
{
#if TIME24
printk("
下面是Makefile
obj-m +=rtc_bl5372.o
KDIR=/lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
编译成ko,然后包进内核里面
将驱动编到内核里面后重新上电能发现系统下面有两个rtc设备,其中rtc0是系统采用BIOS UEFI方式产生的,rtc1是系统加载驱动产生的,如下图
由于系统时间默认采用的是从rtc0获取的时间,需要改为从rtc1(通过加载驱动产生的设备)获取时间。
在/etc/udev/rules.d下创建文件,这里命名为rtc1.rules
文件内容如下:
KERNEL=="rtc0",SYMLINK+="rtc_old"
KERNEL=="rtc1",SYMLINK+="rtc"
上述规则将原来命名为rtc0的设备链接到rtc old,将rtc1设备链接到rtc。这样,系统会将rtc1设备作为默认的时钟设备。
然后重新加载udev规则,使修改生效
udevadm control --reload-rules
重启系统即可。
重启后输入dmesg能看到后面又多出来一段内容,那就是配置系统时间时读取rtc1时间了: