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

这篇具有很好参考价值的文章主要介绍了FT2000+低温情况下RTC守时问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、背景介绍

飞腾2000+芯片通过I2C连接一块RTC时钟芯片(BellingBL5372)来实现麒麟信安系统下后的守时功能。目前BIOS支持UEFI功能,BIOS上电后能获取RTC时间,并将时间写入相应的UEFI变量或内存区域,操作系统上电后使用UEFI的APIs来读取相应的RTC时间变量或内存区域。

2、问题描述

在低温情况下(-42度),发现BIOS有概率无法获取到RTC时间,导致写入UEFI变量写入失败,从而操作系统也无法获取正确时间,变成系统出厂时间(2019年9月3日),如下图为出错情况:

FT2000+低温情况下RTC守时问题,Felven在职场,ft2000+,rtc,BL5372,守时,麒麟信安

 文章来源地址https://www.toymoban.com/news/detail-655017.html

但其实RTC芯片是正常工作的,当恢复到常温后又能获取到正确的时间,如下图

FT2000+低温情况下RTC守时问题,Felven在职场,ft2000+,rtc,BL5372,守时,麒麟信安

 

问题表现为低温情况下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	//0x5d

static unsigned rs5c_reg2hr(unsigned reg)
{
#if TIME24
	printk("<RTC> TIME24 0x%x\n",bcd2bin(reg & 0x3f));
	return bcd2bin(reg & 0x3f);
#else
	unsigned	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 TIME24
	printk("<RTC> TIME24 0x%x\n",bcd2bin(reg & 0x3f));
	return bcd2bin(reg & 0x3f);
#else
	unsigned	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 TIME24
	printk("<RTC> TIME24 0x%x\n",bin2bcd(hour));
	return bin2bcd(hour);

#else
	if (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_hr2reg
		buf[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~6
	buf[4] = bin2bcd(tm->tm_mday);
	buf[5] = bin2bcd(tm->tm_mon)+1;// 0~11
	tm->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
#endif

static 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);
#endif

static 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.o

KDIR=/lib/modules/$(shell uname -r)/build

all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean

编译成ko,然后包进内核里面

FT2000+低温情况下RTC守时问题,Felven在职场,ft2000+,rtc,BL5372,守时,麒麟信安

将驱动编到内核里面后重新上电能发现系统下面有两个rtc设备,其中rtc0是系统采用BIOS UEFI方式产生的,rtc1是系统加载驱动产生的,如下图

FT2000+低温情况下RTC守时问题,Felven在职场,ft2000+,rtc,BL5372,守时,麒麟信安

 

由于系统时间默认采用的是从rtc0获取的时间,需要改为从rtc1(通过加载驱动产生的设备)获取时间。

在/etc/udev/rules.d下创建文件,这里命名为rtc1.rules

FT2000+低温情况下RTC守时问题,Felven在职场,ft2000+,rtc,BL5372,守时,麒麟信安

 

文件内容如下:

KERNEL=="rtc0",SYMLINK+="rtc_old"

KERNEL=="rtc1",SYMLINK+="rtc"

FT2000+低温情况下RTC守时问题,Felven在职场,ft2000+,rtc,BL5372,守时,麒麟信安

 

上述规则将原来命名为rtc0的设备链接到rtc old,将rtc1设备链接到rtc。这样,系统会将rtc1设备作为默认的时钟设备。

然后重新加载udev规则,使修改生效

udevadm control --reload-rules

重启系统即可。

重启后输入dmesg能看到后面又多出来一段内容,那就是配置系统时间时读取rtc1时间了:

FT2000+低温情况下RTC守时问题,Felven在职场,ft2000+,rtc,BL5372,守时,麒麟信安

 

到了这里,关于FT2000+低温情况下RTC守时问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 飞腾FT-2000+/64银河麒麟服务器v10安装及(LSI)MegaRAID配置

    环境说明: cpu:Phytium,FT-2000+/64 bios: 昆仑固件 操作系统:Kylin Linux Advanced Server V10 (Lance) raid卡:AVAGO MegaRAID SAS 9361-8i 存储:2块SSD,11块HDD 部署要求 使用LSI创建raid,2块ssd作raid1(拿到设备时此项已作好本次不重作),使用u盘安装kylin到ssd raid上,11块hdd作raid5+1热备。 遇到的问题:

    2024年03月28日
    浏览(97)
  • FT2004(D2000)开发实战之网口stmmac报错调试(Failed to reset the dma)

    主芯片为飞腾FT2004,网口MAC为stmmac,phy芯片为ar8035,工作接口为RGMII接口 具体报错信息如下所示:

    2024年02月15日
    浏览(78)
  • FT2000+ qemu kvm openEuer crash 分析 频繁设置CPU online及cgroup导致进程卡死、不调度故障

    https://hknaruto.blog.csdn.net/article/details/130498823 内核版本信息  突然就坚挺起来,长时间稳定运行 待续 十几分钟后,终端已卡死 ,两个终端均无响应,但是gnome还活着,图形界面还能动 重启虚拟机 kvm内,安装好crash分析环境 下载安装debuginfo包 http://debuginfo.centos.org/8/aarch64/Pack

    2024年02月02日
    浏览(36)
  • 芯片FT量测简介

    概要:芯片的量测需求的资源主要是ATE+Handler+量测治具+测试程序。 ATE:ATE是Automatic Test Equipment的缩写, 于半导体产业意指集成电路(IC)自动测试机, 用于检测集成电路功能之完整性, 为集成电路生产制造之最后流程, 以确保集成电路生产制造之品质。 Handler:即Test Handler集

    2024年02月02日
    浏览(35)
  • 芯片FT测试主要流程

    背景 :最近刚了解mps公司的mp系列某芯片的测试流程,在此做个简单记录。 1.HW check 芯片太小,一般是把芯片放在socket上,连接测试板,再连接测试机,进行芯片功能的测试。 我们首先需要保证这些硬件是好的,这一步便是确定硬件上元器件、各通路的好坏。 2.INIT 初始化,常

    2023年04月16日
    浏览(22)
  • 【分布式】VMware FT概要

    讨论了1primary + 1backup的情况,比较好理解, 6.824中以该论文为例,介绍了分布式系统中复制的概念,复制的方式。以下简要讲述一些关键点,来源于MIT6.824课程,翻译版本 https://mit-public-courses-cn-translatio.gitbook.io/mit6-824/lecture-04-vmware-ft 状态转移 复制状态机 区别在于,状态转移

    2024年02月11日
    浏览(21)
  • 区块链 FT NFT元宇宙 笑谈

    这两年区块链,FT,NFT,以及最近的元宇宙,这些名词到处都是,除了元宇宙目前没有完整定义,其他几个概念都可以有完整定义。 本人也不照搬网上的概念和定义,只是从本人认知角度说下对这些知识的见解。 首先是区块链,这个名词大家都不陌生了,他首先是一项技术,

    2023年04月09日
    浏览(44)
  • EagleSDR USB HAT FT600

    给EagleSDR做了个USB 3.0的子卡,采用FT600方案,实物如下: 用FT600DataStreamerDemoApp测试,速度如下: 由于FT600是16bit的接口,如果用FT601的32bit接口,性能应该还会有大幅提升。 测试代码很简单,参考了网上大佬的例程

    2024年02月09日
    浏览(27)
  • FT232R USB UART驱动安装

    FT232R USB UART驱动安装 提示无FT232R USB UART驱动! 安装该驱动:​ ​https://ftdichip.com/drivers/d2xx-drivers/​

    2024年02月11日
    浏览(26)
  • 【Python FT4222 I2C通信】

    硬件调试过程,常要通过I2C通信。 利用Python结合I2C Master device, 调试和开发自动化测试软件,简单快捷。本文就简单介绍其中一种。 FT4222是一种支持SPI和I2C通信协议的USB接口设备。 “LibFT4222” 库提供了高级api以方便应用程序开发。 I2C接口可以配置为主模式或从模式。 SPI接

    2024年02月12日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包