背景

  • 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添加节点配置:
    &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
    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属性,即:

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

测试

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

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

使用hwclock命令进行测试:

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):

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