基于RK3566核心板设计了一块主板,运行 linux系统,外部RTC芯片使用HYM8563,挂在CPU i2c3总线上,管脚为SDA:GPIO1_A0、SCL:GPIO1_A1,电路连接如下:
&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";
};
};
先cd到kernel目录下,然后执行make menuconfig打开configuration,如下图所示:
一般内核添加了对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)
可以看出,第一次操作芯片失败了,进行第二次操作时成功了。