FT2000+低温情况下RTC守时问题

2023-11-02 04:30

本文主要是介绍FT2000+低温情况下RTC守时问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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 <linux/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of.h>#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	//0x5dstatic unsigned rs5c_reg2hr(unsigned reg)
{
#if TIME24printk("<RTC> TIME24 0x%x\n",bcd2bin(reg & 0x3f));return bcd2bin(reg & 0x3f);
#elseunsigned	hour;printk("<RTC> TIME12 0x%x\n",bcd2bin(reg & 0x1f));hour = bcd2bin(reg & 0x1f);if (hour == 12)hour = 0;if (reg & 0x20)hour += 12;printk("<RTC> TIME12 hour=%d\n",hour);return hour;
#endif
}static unsigned rs5c_reg2mon(unsigned reg)
{
#if TIME24printk("<RTC> TIME24 0x%x\n",bcd2bin(reg & 0x3f));return bcd2bin(reg & 0x3f);
#elseunsigned	month;printk("<RTC> TIME12 0x%x\n",bcd2bin(reg & 0x1f));month = bcd2bin(reg & 0x1f);if (month > 12)month -= 12;printk("<RTC> TIME12 hour=%d\n",month);return month;
#endif
}static unsigned rs5c_hr2reg(unsigned hour)
{#if TIME24printk("<RTC> TIME24 0x%x\n",bin2bcd(hour));return bin2bcd(hour);#elseif (hour > 12){printk("<RTC> TIME12(>12) 0x%x\n",(0x20 | bin2bcd(hour - 12)));return 0x20 | bin2bcd(hour - 12);}if (hour == 12){printk("<RTC> TIME12(==12) 0x%x\n",(0x20 | bin2bcd(12)));return 0x20 | bin2bcd(12);}if (hour == 0){printk("<RTC> TIME12(==0) 0x%x\n",bin2bcd(12));return bin2bcd(12);}printk("<RTC> TIME12(<12) 0x%x\n",bin2bcd(hour));return bin2bcd(hour);
#endif
}//-----------------------------------------------
static struct i2c_driver bl5372_driver;struct bl5372 {struct rtc_device *rtc;struct device *dev;int irq;/*unsigned char sec;unsigned char min;unsigned char hour;unsigned char week;unsigned char day;unsigned char month;unsigned int year;*/
};static int i2c_write_bytes(struct i2c_client *client, uint8_t *data, uint16_t len)
{struct i2c_msg msg;int ret=-1;msg.flags = !I2C_M_RD;msg.addr = client->addr;msg.len = len;msg.buf = data;ret=i2c_transfer(client->adapter, &msg,1);return ret;
}static int bl5372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{struct bl5372 *bl5372 = i2c_get_clientdata(client);unsigned char buf[7] = { RS5C_ADDR(RS5C372_REG_SECS) };printk("<RTC>bsl5372_get_datetime\n");struct i2c_msg msgs[] = {{/* setup read ptr */.addr = client->addr,.flags = 0,/* write */.len = 1,.buf = buf},{/* read the sec,min,hour,week,day,month,year */.addr = client->addr,.flags = I2C_M_RD,/* read */.len = 7,.buf = buf},};//int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)//@num: Number of messages to be executed.//ÕâÀïÓÐÁœžöÏûÏ¢£¬ msgs[]µÄŽóС/* read registers */if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {dev_err(&client->dev, "%s: read error\n", __func__);return -EIO;}printk("<RTC> buf[2]=0x%x\n",buf[2]);tm->tm_sec = bcd2bin(buf[0] & 0x7f);tm->tm_min = bcd2bin(buf[1] & 0x7f);printk("<RTC> Get hour Label*****\n");tm->tm_hour = rs5c_reg2hr(buf[2]);tm->tm_mday = bcd2bin(buf[4] & 0x7f);tm->tm_wday = bcd2bin(buf[3] & 0x7f);printk("<RTC> Get month Label*****\n");//tm->tm_mon = rs5c_reg2hr(buf[5])-1;tm->tm_mon = rs5c_reg2mon(buf[5])-1;tm->tm_year = bcd2bin(buf[6] & 0x7f)+100;printk("<RTC>@GET1 year%d month%d mday%d wday%d hour%d min%d sec%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_wday,tm->tm_hour,tm->tm_min,tm->tm_sec);#if 1//------------------------------------buf[0]= RS5C_ADDR(RS5C_REG_CTRL2);struct i2c_msg msgs2[] = {{/* setup read  */.addr = client->addr,.len = 1,.buf = buf},{/* read is_24hour */.addr = client->addr,.flags = I2C_M_RD,.len = 1,.buf = buf},};/* read registers */if ((i2c_transfer(client->adapter, msgs2, 2)) != 2) {dev_err(&client->dev, "%s: read error\n", __func__);return -EIO;}if(buf[0]&0x20){printk("24小时\n");tm->tm_hour= (tm->tm_hour<24)? (tm->tm_hour):(24-tm->tm_hour);}else{tm->tm_hour=(tm->tm_hour<24)? (tm->tm_hour):(tm->tm_hour+24);//	tm->tm_mday=(tm->tm_mday<7)? (tm->tm_mday):(tm->tm_mday+8-24);//	tm->tm_hour=tm->tm_hour+8;}#endif//tm->tm_hour= (tm->tm_hour<24)? (tm->tm_hour):(tm->tm_hour-24);/* the clock can give out invalid datetime, but we cannot return* -EINVAL otherwise hwclock will refuse to set the time on bootup.*/printk("<RTC>@GET2 year%d month%d mday%d wday%d hour%d min%d sec%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_wday,tm->tm_hour,tm->tm_min,tm->tm_sec);if (rtc_valid_tm(tm) < 0)dev_err(&client->dev, "retrieved date/time is not valid.\n");return 0;
}static int bl5372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{struct bl5372 *bl5372 = i2c_get_clientdata(client);int i, err;unsigned char buf[7];printk("<RTC>####bl5372_set_datetime\n");
//------------------------------------buf[0]= RS5C_ADDR(RS5C_REG_CTRL2);struct i2c_msg msgs2[] = {{/* setup read  */.addr = client->addr,.len = 1,.buf = buf},{/* read is_24hour */.addr = client->addr,.flags = I2C_M_RD,.len = 1,.buf = buf},};/* read registers */if ((i2c_transfer(client->adapter, msgs2, 2)) != 2) {dev_err(&client->dev, "%s: read error\n", __func__);return -EIO;}/*	if((buf[0]&0x20)== 0){printk("RTC 12xiaoshi\n");rs5c_hr2regbuf[0] |= (1<<5);err = i2c_master_send(client, buf, 1);}
*/printk("<RTC>IN####year%d month%d mday%d wday%d hour%d min%d sec%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_wday,tm->tm_hour,tm->tm_min,tm->tm_sec);//------------------------/* hours, minutes and seconds */buf[0] = bin2bcd(tm->tm_sec);buf[1] = bin2bcd(tm->tm_min);printk("<RTC> Set hour Label*****\n");buf[2] = rs5c_hr2reg(tm->tm_hour);buf[3] = bin2bcd(tm->tm_wday & 0x07); //week 0~6buf[4] = bin2bcd(tm->tm_mday);buf[5] = bin2bcd(tm->tm_mon)+1;// 0~11tm->tm_year -= 100;buf[6] = bin2bcd(tm->tm_year % 100);// start at 1900  2018=>118
//
printk("###########write data to rtc \n");for(i=0;i<7;i++)
{
printk("buf[%d] is 0x%x\n",i,buf[i]);
}err = i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_SECS),   buf[0]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_MINS) ,  buf[1]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_HOURS) , buf[2]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_WDAY) ,  buf[3]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_DAY) ,   buf[4]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_MONTH) , buf[5]);i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C372_REG_YEAR) ,  buf[6]);return 0;
}#ifdef CONFIG_RTC_INTF_DEV
static int bl5372_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));struct rtc_time tm;switch (cmd) {case RTC_RD_TIME://bl5372_get_datetime(to_i2c_client(dev), &tm);return 0;case RTC_SET_TIME:if (copy_from_user(&tm, arg, sizeof(tm)))return -EFAULT;bl5372_set_datetime(to_i2c_client(dev), &tm);return 0;default:return -ENOIOCTLCMD;}}
#else
#define bl5372_rtc_ioctl NULL
#endifstatic int bl5372_rtc_read_time(struct device *dev, struct rtc_time *tm)
{return bl5372_get_datetime(to_i2c_client(dev), tm);
}static int bl5372_rtc_set_time(struct device *dev, struct rtc_time *tm)
{return bl5372_set_datetime(to_i2c_client(dev), tm);
}static int bl5372_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));return 0;
}static int bl5372_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));return 0;
}static int bl5372_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{//struct bl5372 *bl5372 = dev_get_drvdata(dev);struct bl5372 *bl5372 = i2c_get_clientdata(to_i2c_client(dev));return 0;
}static const struct rtc_class_ops bl5372_rtc_ops = {.ioctl		= bl5372_rtc_ioctl,.read_time	= bl5372_rtc_read_time,.set_time	= bl5372_rtc_set_time,.read_alarm        = bl5372_rtc_getalarm,.set_alarm         = bl5372_rtc_setalarm,.alarm_irq_enable  = bl5372_rtc_alarm_irq_enable
};static int bl5372_probe(struct i2c_client *client,const struct i2c_device_id *id)
{struct bl5372 *bl5372;if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)){return -ENODEV;}bl5372 = devm_kzalloc(&client->dev, sizeof(struct bl5372),GFP_KERNEL);if (!bl5372){return -ENOMEM;}device_init_wakeup(&client->dev, 1);i2c_set_clientdata(client, bl5372);bl5372->rtc = devm_rtc_device_register(&client->dev,bl5372_driver.driver.name,&bl5372_rtc_ops, THIS_MODULE);if (IS_ERR(bl5372->rtc)){return PTR_ERR(bl5372->rtc);}bl5372->rtc->uie_unsupported = 1;return 0;
}static int bl5372_remove(struct i2c_client *client)
{return 0;
}static const struct i2c_device_id bl5372_id[] = {{ "bl5372", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, bl5372_id);#ifdef CONFIG_OF
static const struct of_device_id bl5372_of_match[] = {{ .compatible = "beilin,bl5372" },{}
};
MODULE_DEVICE_TABLE(of, bl5372_of_match);
#endifstatic struct i2c_driver bl5372_driver = {.driver		= {.name	= "rtc-bl5372",.owner	= THIS_MODULE,.of_match_table = of_match_ptr(bl5372_of_match),},.probe		= bl5372_probe,.remove		= bl5372_remove,.id_table	= bl5372_id,
};module_i2c_driver(bl5372_driver);MODULE_AUTHOR("Zhengweiqing <1548889230@qq.com>");
MODULE_DESCRIPTION("Beilin BL5372 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

下面是Makefile

obj-m +=rtc_bl5372.oKDIR=/lib/modules/$(shell uname -r)/buildall: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时间了:

 

这篇关于FT2000+低温情况下RTC守时问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/328109

相关文章

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

题目1254:N皇后问题

题目1254:N皇后问题 时间限制:1 秒 内存限制:128 兆 特殊判题:否 题目描述: N皇后问题,即在N*N的方格棋盘内放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在同一斜线上。因为皇后可以直走,横走和斜走如下图)。 你的任务是,对于给定的N,求出有多少种合法的放置方法。输出N皇后问题所有不同的摆放情况个数。 输入

vscode中文乱码问题,注释,终端,调试乱码一劳永逸版

忘记咋回事突然出现了乱码问题,很多方法都试了,注释乱码解决了,终端又乱码,调试窗口也乱码,最后经过本人不懈努力,终于全部解决了,现在分享给大家我的方法。 乱码的原因是各个地方用的编码格式不统一,所以把他们设成统一的utf8. 1.电脑的编码格式 开始-设置-时间和语言-语言和区域 管理语言设置-更改系统区域设置-勾选Bata版:使用utf8-确定-然后按指示重启 2.vscode

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR

form表单提交编码的问题

浏览器在form提交后,会生成一个HTTP的头部信息"content-type",标准规定其形式为Content-type: application/x-www-form-urlencoded; charset=UTF-8        那么我们如果需要修改编码,不使用默认的,那么可以如下这样操作修改编码,来满足需求: hmtl代码:   <meta http-equiv="Conte