RK3399添加外部独立RTC

背景

  • Platform: RK3399
  • OS: Android7.1.2
  • Kernel: v4.4.103

以前使用的是RK808内部的RTC,说电流较大,电池不耐用,后面就外置了一个独立的RTC芯片–ISL1208。
现在这2个RTC同时存在,或只有以前的RK808,需做兼容处理。

Kernel内核部分:

  1. 添加对应RTC芯片的驱动源码(kernel/drivers/rtc/rtc-isl1208.c)
  2. 修改Kconfig和Makefile,添加对应的编译选项
  3. dts添加节点配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    &i2c3 {                           
    status = "okay";
    i2c-scl-rising-time-ns = <450>;
    i2c-scl-falling-time-ns = <15>;

    /*RTC*/
    rtc: isl1208@6f {
    status = "okay";
    compatible = "rtc,isl1208";
    reg = <0x6f>;
    };
    };

I2C3的问题

RTC开始是接在I2C3上面的,但调试许久发现I2C都不正常,后面发现该I2C已经被复用为内部I2C给HDMI使用了。
默认HDMI使用的是内部I2C(内部I2C只能使用I2C3),即配置如下。但如果使用了内部I2C,普通I2C是无法使用的,因为被复用成了RK_FUNC_3,普通I2C为RK_FUNC_1

1
2
3
4
5
6
7
8
9
10
11
12
13
hdmi:hdmi@ff940000 {
......
pinctrl-names = "default";
pinctrl-0 = <&hdmi_i2c_xfer>;
......
}
hdmi {
hdmi_i2c_xfer: hdmi-i2c-xfer {
rockchip,pins =
<4 17 RK_FUNC_3 &pcfg_pull_none>,
<4 16 RK_FUNC_3 &pcfg_pull_none>;
};
};

若要将I2C3作为普通I2C使用,需要将pinctrl-0属性注释掉,并在hdmi的配置中配置ddc-i2c-bus属性,即:

1
2
3
4
&hdmi {
status = "okay";
ddc-i2c-bus = <&i2c3>; //选择硬件对应的I2C
};

测试

硬件OK和驱动OK后,会在/dev下生成对应的设备节点rtcx
测试节点属性(/sys/class/rtc//proc/driver/rtc):

1
2
cat /sys/class/rtc/rtc0/time
cat /sys/class/rtc/rtc0/name

使用hwclock命令进行测试:

1
2
3
4
5
6
7
8
9
10
11
usage: hwclock [-rswtluf]

-f FILE Use specified device file instead of /dev/rtc (--rtc)
-l Hardware clock uses localtime (--localtime)
-r Show hardware clock time (--show)
-s Set system time from hardware clock (--hctosys)
-t Set the system time based on the current timezone (--systz)
-u Hardware clock uses UTC (--utc)
-w Set hardware clock from system time (--systohc)

#hwclock -f /dev/rtc0

注意需要先写时间进去,不然如果读取RTC会返回-1

hctosys

config属性CONFIG_RTC_HCTOSYS_DEVICE会配置默认的hctosys设备,通过/sys/class/rtc/rtc0/hctosys可查看状态

兼容处理:

  • CONFIG_RTC_HCTOSYS_DEVICECONFIG_RTC_SYSTOHC_DEVICE都配置为rtc1

  • rtc_hctosys函数(kernel/drivers/rtc/hctosys.c):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ...
    if (rtc == NULL) {
    pr_info("unable to open rtc device (%s), try rtc0\n",
    CONFIG_RTC_HCTOSYS_DEVICE);
    /*zdd add rtc0+rtc1, try rtc0, CONFIG_RTC_HCTOSYS_DEVICE="rtc1"*/
    rtc = rtc_class_open("rtc0");
    if (rtc == NULL) {
    pr_info("unable to open rtc device (%s)\n",
    CONFIG_RTC_HCTOSYS_DEVICE);
    goto err_open;
    }
    }
    ...
  • hctosys_show函数(kernel/drivers/rtc/rtc-sysfs.c):

    #ifdef CONFIG_RTC_HCTOSYS_DEVICE
        /*zdd add rtc0/rtc1, CONFIG_RTC_HCTOSYS_DEVICE="rtc1"*/
        struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
        //如果没有rtc1, 且查询的是rtc0
        if (rtc == NULL) {
    
            if (rtc_hctosys_ret == 0 &&
                strcmp(dev_name(&to_rtc_device(dev)->dev), "rtc0") == 0)
            return sprintf(buf, "1\n");
        }
        //如果有rtc1, 且查询的是CONFIG_RTC_HCTOSYS_DEVICE
        else if (rtc_hctosys_ret == 0 &&
                strcmp(dev_name(&to_rtc_device(dev)->dev), CONFIG_RTC_HCTOSYS_DEVICE) == 0)
            return sprintf(buf, "1\n");
        else
    #endif
            return sprintf(buf, "0\n");
    

    注意:
    ISL1208生产第一次RTC内无时间数据,读取会失败,需初始化一个默认值,否则RTC不能正常工作

Android部分添加修改

应用APK同步时间时需调用hwclock命令将系统时间同步到硬件RTC时间,不能只调用date命令。
注意调用hwclock命令需要su权限

FrameWork:
frameworks/base/services/core/java/com/android/server/AlarmManagerService.java
HAL:
frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp

Donate comment here