linux之调试外部RTC

基于RK3566核心板设计了一块主板,运行 linux系统,外部RTC芯片使用HYM8563,挂在CPU i2c3总线上,管脚为SDA:GPIO1_A0、SCL:GPIO1_A1,电路连接如下:

linux之调试外部RTC_第1张图片

DTS文件中增加i2c3节点的配置

&i2c3 {
	status = "okay";
	pinctrl-names = "default";
	pinctrl-0 = <&i2c3m0_xfer>;
	clock-frequency = <400000>;
	i2c-scl-rising-time-ns = <138>;
	i2c-scl-falling-time-ns = <4>;

	pcf8563: pcf8563@51 {
		compatible = "nxp,pcf8563";
		reg = <0x51>;
		#clock-cells = <0>;
		status = "disabled";
	};
	
	hym8563: hym8563@51 {
		compatible = "haoyu,hym8563";
		reg = <0x51>;
		#clock-cells = <0>;
		status = "okay";
	};	
};

内核添加对hym8563的支持

先cd到kernel目录下,然后执行make menuconfig打开configuration,如下图所示:

linux之调试外部RTC_第2张图片

一般内核添加了对hym8563的支持,所以不需要修改configuration。如果内核没有添加支持,选中Haoyu Microelectronics HYM8563,然后键盘输入‘y’即可选中,修改之后需要保存对应的defconfig文件,执行以下代码保存:

make savedefconfig
cp defconfig arch/arm64/configs/rockchip_linux_defconfig

 调试

进入到sdk目录,执行./build.sh kernel,编译生成新的boot.img文件,将boot.img烧写到主板,待主板启动后,执行i2cdetect -l查看i2c3是否存在:

[root@RK356X:/]# i2cdetect -l
i2c-3   i2c             rk3x-i2c                                I2C adapter
i2c-6   i2c             DesignWare HDMI                         I2C adapter
i2c-4   i2c             rk3x-i2c                                I2C adapter
i2c-2   i2c             rk3x-i2c                                I2C adapter
i2c-0   i2c             rk3x-i2c                                I2C adapter

再执行i2cdetect -y 3检测hym8563是否在i2c总线上:

[root@RK356X:/]# i2cdetect -y 3
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 

 执行结果表示i2c3总线上有个地址为0x51的从设备。

查看设备名称:

[root@RK356X:/]# cat /sys//bus/i2c/devices/3-0051/name
hym8563

 显示时间:

[root@RK356X:/]# hwclock
Tue Oct 10 01:52:13 2000  0.000000 seconds

修改时间,执行date -s命令修改时间,再执行hwclock -w更新硬件时间:

[root@RK356X:/]# date -s "2022-04-17 16:19:50"
Sun Apr 17 16:19:50 UTC 2022
[root@RK356X:/]# hwclock -w
[root@RK356X:/]# hwclock
Sun Apr 17 16:20:07 2022  0.000000 seconds

 接上备用电池,断开主板其它电源,等一段时间再给主板上电,主板启动之后执行date查看系统时间:

[root@RK356X:/]# date
Sun Apr 17 16:34:00 UTC 2022

可以看出主板断电之后时间在继续运行。 

遇到的问题

当对改版之后的板子进行测试时,发现存在rtc驱动加载不成功的问题,开机日志如下:

[    0.712462] rtc-hym8563 3-0051: could not init device, -6

在控制台执行reboot命令重启系统,rtc能加载成功,猜测可能是加载驱动时,RTC芯片电源不稳定,导致通信失败,尝试在驱动中多操作几次rtc芯片,修改hym8563_probe()函数代码:

static int hym8563_probe(struct i2c_client *client,
			 const struct i2c_device_id *id)
{
	struct hym8563 *hym8563;
	int ret;
	/*
	 * hym8563 initial time(2020_1_1_12:00:00),
	 * avoid hym8563 read time error
	 */
	struct rtc_time tm_read, tm = {
		.tm_wday = 0,
		.tm_year = 120,
		.tm_mon = 0,
		.tm_mday = 1,
		.tm_hour = 12,
		.tm_min = 0,
		.tm_sec = 0,
	};

	hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL);
	if (!hym8563)
		return -ENOMEM;

	hym8563->client = client;
	i2c_set_clientdata(client, hym8563);

	device_set_wakeup_capable(&client->dev, true);

	for( i = 0; i < 5; i++ )
	{
		ret = hym8563_init_device(client);
		if (ret) {
			dev_err(&client->dev, "could not init device, %d\n", ret);
			return ret;
		}
		
	}

	if (client->irq > 0) {
		ret = devm_request_threaded_irq(&client->dev, client->irq,
						NULL, hym8563_irq,
						IRQF_TRIGGER_LOW | IRQF_ONESHOT,
						client->name, hym8563);
		if (ret < 0) {
			dev_err(&client->dev, "irq %d request failed, %d\n",
				client->irq, ret);
			return ret;
		}
	}

	/* check state of calendar information */
	ret = i2c_smbus_read_byte_data(client, HYM8563_SEC);
	if (ret < 0)
		return ret;

	dev_dbg(&client->dev, "rtc information is %s\n",
		(ret & HYM8563_SEC_VL) ? "invalid" : "valid");

	hym8563_rtc_read_time(&client->dev, &tm_read);
	if (((tm_read.tm_year < 70) | (tm_read.tm_year > 200)) |
	    (tm_read.tm_mon == -1) | (rtc_valid_tm(&tm_read) != 0))
		hym8563_rtc_set_time(&client->dev, &tm);

	hym8563->rtc = devm_rtc_device_register(&client->dev, client->name,
						&hym8563_rtc_ops, THIS_MODULE);
	if (IS_ERR(hym8563->rtc))
		return PTR_ERR(hym8563->rtc);

	/* the hym8563 alarm only supports a minute accuracy */
	hym8563->rtc->uie_unsupported = 1;

#ifdef CONFIG_COMMON_CLK
	hym8563_clkout_register_clk(hym8563);
#endif

	return 0;
}

改为 

#include 

static int hym8563_probe(struct i2c_client *client,
			 const struct i2c_device_id *id)
{
	struct hym8563 *hym8563;
	int ret;
	int i;
	/*
	 * hym8563 initial time(2020_1_1_12:00:00),
	 * avoid hym8563 read time error
	 */
	struct rtc_time tm_read, tm = {
		.tm_wday = 0,
		.tm_year = 120,
		.tm_mon = 0,
		.tm_mday = 1,
		.tm_hour = 12,
		.tm_min = 0,
		.tm_sec = 0,
	};

	hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL);
	if (!hym8563)
		return -ENOMEM;

	hym8563->client = client;
	i2c_set_clientdata(client, hym8563);

	device_set_wakeup_capable(&client->dev, true);

	for( i = 0; i < 5; i++ )
	{
		ret = hym8563_init_device(client);
		if (ret) {
			dev_err(&client->dev, "could not init device, %d\n", ret);
			
		}
		else
		{
			break;
		}
		
		mdelay(5);
	}
	
	if( i == 6 )
	{
		return ret;
	}

	if (client->irq > 0) {
		ret = devm_request_threaded_irq(&client->dev, client->irq,
						NULL, hym8563_irq,
						IRQF_TRIGGER_LOW | IRQF_ONESHOT,
						client->name, hym8563);
		if (ret < 0) {
			dev_err(&client->dev, "irq %d request failed, %d\n",
				client->irq, ret);
			return ret;
		}
	}

	/* check state of calendar information */
	ret = i2c_smbus_read_byte_data(client, HYM8563_SEC);
	if (ret < 0)
		return ret;

	dev_dbg(&client->dev, "rtc information is %s\n",
		(ret & HYM8563_SEC_VL) ? "invalid" : "valid");

	hym8563_rtc_read_time(&client->dev, &tm_read);
	if (((tm_read.tm_year < 70) | (tm_read.tm_year > 200)) |
	    (tm_read.tm_mon == -1) | (rtc_valid_tm(&tm_read) != 0))
		hym8563_rtc_set_time(&client->dev, &tm);

	hym8563->rtc = devm_rtc_device_register(&client->dev, client->name,
						&hym8563_rtc_ops, THIS_MODULE);
	if (IS_ERR(hym8563->rtc))
		return PTR_ERR(hym8563->rtc);

	/* the hym8563 alarm only supports a minute accuracy */
	hym8563->rtc->uie_unsupported = 1;

#ifdef CONFIG_COMMON_CLK
	hym8563_clkout_register_clk(hym8563);
#endif

	return 0;
}

开机日志如下:

[root@RK356X:/]# dmesg | grep rtc
[    0.712462] rtc-hym8563 3-0051: could not init device, -6
[    0.719663] rtc-hym8563 3-0051: rtc core: registered hym8563 as rtc0
[    0.720000] rtc-hym8563 3-0051: setting system clock to 2000-01-01 00:48:49 UTC (946687729)

可以看出,第一次操作芯片失败了,进行第二次操作时成功了。

你可能感兴趣的:(linux系统及驱动开发,linux,驱动程序,rtc)