RK3288_Android7.1调试RTC总结(二)

本文详细介绍了RK3288在Android7.1环境下RTC芯片am1805的驱动调试过程,重点解析了看门狗功能的实现,包括内核自动喂狗和用户喂狗两种模式,以及如何通过系统节点控制看门狗状态和时间设置。

RK3288_Android7.1调试RTC总结(一)主要是讲解rtc的调试和驱动框架浅析,
RK3288_Android7.1调试RTC总结(二)主要讲解看门狗方面的内容。
我这里主要是基于rtc芯片:am1805的驱动来说明。

rtc1805驱动跑起来就默认看门狗是kernel feeddog,每隔2s feeddog一次(feeddog就是向特定的寄存器写入值,这就是喂狗的实质),如果没有feeddog就会触发系统强制复位而重启系统,喂狗的方式有内核自动喂狗和用户喂狗两种方式实现:
1、用户喂狗:
在用户空间定时地去操作喂狗的动作,让rtc芯片在定时器到来之前,不触发系统强制复位而重启系统:

2、内核喂狗:
驱动循环运行一个工作队列去执行喂狗的动作,只要内核运行就会一直自动喂狗。

/*
 *	am1805_watchdog_feeddog- set up the watchdog timer   
 *
 *	Inputs:
 *	period - timeout period in ms (65 to 124,000)
 *	pin - pin to generate the watchdog signal
 *		0 => disable WDT
 *		1 => generate an interrupt on FOUT/nIRQ
 *		2 => generate an interrupt on PSW/nIRQ2
 *		3 => generate a reset on nRST (AM18xx only)
 *
 */

am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_PIN_RST);//设置feeddog,并且使能reset pin,发送feeddog信号
am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_DISABLE); //设置disabled feeddog

rtc_am1805看门狗验证

首先确认有以下两个节点:

sys/class/rtc_am1805/feeddog //feddog 操作
sys/class/rtc_am1805/timer //设置disabled feeddog之后,然后重新使能kernel feeddog在time之后系统重启

feeddog节点对应代码实现:

static ssize_t am1805_show_feeddog(struct class *class,
			struct class_attribute *attr,	char *buf)
{
	int ret;

	if (rtc_info->watchdog_enable==TYPE_DISABLE)
	{
		ret = sprintf(buf, "0 -- disable\n");
	}
	else if(rtc_info->watchdog_enable==TYPE_KERNLE)
	{
		ret = sprintf(buf, "1 -- kernel feed\n");
	}
	else{
		ret = sprintf(buf, "2 -- user feed\n");
	}

	return ret;
}

static ssize_t am1805_store_feeddog(struct class *class,
	struct class_attribute *attr, const char *buf, size_t count)
{
	int type;

  RTC_DBG(RTC_DBG_VAL, "%s ,run \n",__func__);

  sscanf(buf,"%i",&type);

  if(type==0){
	 rtc_info->watchdog_enable = TYPE_DISABLE;
	 am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_DISABLE);
  }
  else if(type==1){
	 rtc_info->watchdog_enable = TYPE_KERNLE;
	 am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_PIN_RST);
  }
  else{
	 rtc_info->watchdog_enable = TYPE_USER;
	 am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_PIN_RST);
  }

  return count;
}

static struct class_attribute rtc_class_attrs[] = {
	__ATTR(show_all_reg, S_IRUGO | S_IWUSR, show_all_reg, NULL),
	__ATTR(time, S_IRUGO | S_IWUSR, show_time, store_time),
	__ATTR(regwrite, S_IRUGO | S_IWUSR, NULL, store_reg),
	__ATTR(regread, S_IRUGO | S_IWUSR, NULL,show_reg),
	__ATTR(feeddog, S_IRUGO | S_IWUSR, am1805_show_feeddog,am1805_store_feeddog),
	__ATTR(timer, S_IRUGO | S_IWUSR, aml1805_show_timer, am1805_store_timer),
	__ATTR(setalarm, S_IRUGO | S_IWUSR, am1805_set_alarm,NULL),
	__ATTR_NULL
};

static struct class rtc_am1805_class = {
	.name = "rtc_am1805",
	.class_attrs = rtc_class_attrs,
};

probe函数中调用以下接口创建class节点[这里只节选部分代码]:

static int rtc_am1805_probe(struct platform_device *pdev)
{
...
ret = class_register(&rtc_am1805_class);
	if (ret){
		pr_info(" class register rtc_class fail!\n");
		return -1;
	}
...
}

设置和查看看门狗(WDT)的状态:

root@stvs912:/ # cat sys/class/rtc_am1805/feeddog                              
1 -- kernel feed //驱动默认起来就是kernel feeddog状态

root@stvs912:/ # echo 0 > sys/class/rtc_am1805/feeddog //关闭看门狗功能
root@stvs912:/ # echo 1 > sys/class/rtc_am1805/feeddog //kernel feeddog,kernel有定时器设置,每隔一定时间(feeddog time)kernel会自动feeddog,这里时间是2s
root@stvs912:/ # echo 2 > sys/class/rtc_am1805/feeddog //user feeddog,需要用户每隔一定时间feeddog一次(shell-env下执行echo 2 > sys/class/rtc_am1805/feeddog),否则会自动复位重启系统

对应代码如下:

static ssize_t am1805_show_feeddog(struct class *class,
			struct class_attribute *attr,	char *buf)
{
	int ret;

	if (rtc_info->watchdog_enable==TYPE_DISABLE)
	{
		ret = sprintf(buf, "0 -- disable\n");
	}
	else if(rtc_info->watchdog_enable==TYPE_KERNLE)
	{
		ret = sprintf(buf, "1 -- kernel feed\n");
	}
	else{
		ret = sprintf(buf, "2 -- user feed\n");
	}

	return ret;
}

通过以下节点可以设置feeddog time,验证看门狗功能:

rk3399_all:/ # ls -l sys/class/rtc_am1805/timer
-rw-r--r-- 1 root root 4096 2019-11-13 09:19 sys/class/rtc_am1805/timer

例如feeddog设置disabled feeddog之后,然后重新使能kernel feeddog在time(10s)之后系统重启可以这样验证:

rk3399_all:/ # echo 0 > sys/class/rtc_am1805/feeddog //disabled feeddog
rk3399_all:/ # echo 1 > sys/class/rtc_am1805/feeddog //enable kernel feeddog
rk3399_all:/ # echo 10000 > sys/class/rtc_am1805/timer //设置feeddog = 10s,也就是设置喂狗的时间为10s,10s之前要喂狗(或者kernel喂狗或者user喂狗)
rk3399_all:/ # cat sys/class/rtc_am1805/timer                                
10000
rk3399_all:/ # echo 2 > sys/class/rtc_am1805/feeddog //设置用户喂狗,并且每次周期的10s之前要喂狗一次

对应代码实现如下[节选部分]:

static ssize_t aml1805_show_timer(struct class* class,
	struct class_attribute *att, char* buf)
{
  RTC_DBG(RTC_DBG_VAL, "%s ,run \n",__func__);

	return sprintf(buf, "%d\n", rtc_info->watchdog_timer);
}

static ssize_t am1805_store_timer(struct class* class,
	struct class_attribute* att, const char* buf, size_t count)
{
	int value;

  RTC_DBG(RTC_DBG_VAL, "%s ,run \n",__func__);

  sscanf(buf,"%i",&value);
  RTC_DBG(RTC_DBG_VAL, "value = %d \n", value);

  rtc_info->watchdog_timer = value;
	am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_PIN_RST);

  return count;
}
真待机导致机器在进入休眠之后会重启

原因分析:
目前我们的板子都是做的假待机,所以feeddog不会受影响,如果是真待机情况下[kernel进入休眠,不跑kernel],kernel就不自动feeddog就会导致系统重启,所以需要在suspend的情况下关闭feeddog,在唤醒系统的时候再重新打开,以下是feeddog的接口函数。

解决方法:添加休眠唤醒的处理函数,当机器进入休眠的时候,disabled掉watchdog功能,在机器唤醒之后,重新使能watchdog功能即可。

具体代码实现(代码来自rk3399 7.1):

+static int rtc_am1805_suspend(struct device *dev)
+{
+       pr_info("%s,line(%d): enter suspend\n", __func__, __LINE__);
+       //rtc_info->watchdog_enable = TYPE_DISABLE;
+       am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_DISABLE);
+
+       return 0;
+}
+
+static int rtc_am1805_resume(struct device *dev)
+{
+
+       pr_info("%s,line(%d): enter resume\n", __func__, __LINE__);
+       if(rtc_info->watchdog_enable != TYPE_DISABLE)
+               am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_PIN_RST);
+       return 0;
+}
+
 static void rtc_am1805_shutdown(struct i2c_client * client)
 {
        unsigned char val=0;
@@ -1374,6 +1392,9 @@ static void rtc_am1805_shutdown(struct i2c_client * client)
        rtc_am1805_i2c_write(&val, WDT_REG, 0x01);
 }

+static SIMPLE_DEV_PM_OPS(rtc_am1805_pm_ops, rtc_am1805_suspend,
+                                               rtc_am1805_resume);
+
 static const struct i2c_device_id am1805_id[] = {
        { "rtc_am1805", 0 },
        { }
@@ -1389,6 +1410,7 @@ struct i2c_driver rtc_am1805_driver = {
                .name = "rtc_am1805",
                .owner = THIS_MODULE,
                .of_match_table = am1805_rtc_dt_match,
+               .pm = &rtc_am1805_pm_ops,
        },
        .probe = rtc_am1805_probe,
        .remove = (rtc_am1805_remove),
设置feeddog间隔时间 echo xxxx > sys/class/rtc_am1805/timer ,之后到时间就自动重启了
static ssize_t am1805_store_timer(struct class* class,
        struct class_attribute* att, const char* buf, size_t count)
{
        int value;

  RTC_DBG(RTC_DBG_VAL, "%s ,run \n",__func__);

  sscanf(buf,"%i",&value);
  RTC_DBG(RTC_DBG_VAL, "value = %d \n", value);

  rtc_info->watchdog_timer = value;
//      am1805_watchdog_feeddog(rtc_info->watchdog_timer,WATCHDOG_INT_PIN_RST);  //注释掉这句就可以,因为只设置时间即可,不需要再去执行写寄存器的操作
  return count;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

驱动在线

您的打赏将是我继续创作的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值