相关文件list:
pm8998.dtsi ---RTC dts配置
qpnp-rtc.c ---qcom RTC驱动
class.c ---RTC相关class
interface.c ---相关RTC功能的接口定义
hctosys.c ---一开机通过RTC设置系统时间
rtc-dev.c ---RTC device fops接口:open、close、ioctl、poll等
简述:
所谓RTC(Real Time Clock),用于关机时继续计算系统日期和时间。是基于硬件的功能。也可以RTC做Alarm来设置power on/off。
驱动分析:
首先在dts的Document中看到两个配置项:
- qcom,qpnp-rtc-write: This property enables/disables rtc write
0 = Disable rtc writes.
1 = Enable rtc writes.
- qcom,qpnp-rtc-alarm-pwrup: This property enables/disables
feature of powering up phone (from
power down state) through alarm
interrupt.
If not mentioned rtc driver will
disable feature of powring-up
phone through alarm.
0 = Disable powering up of phone
through alarm interrupt.
1 = Enable powering up of phone
through alarm interrupt.
一个是是否使能写RTC时间的功能。另一个是是否支持RTC alarm开机的功能。
接下来,再看RTC的驱动部分,从qpnp-rtc.c:
其中根据.compatible = "qcom,qpnp-rtc"匹配成功后走到probe,probe中获取了dts中上面两条配置参数,从而进行初始化。获取RTC/Alarm相关的寄存器和资源,并通过操作寄存器enable RTC相关功能。注册了RTC中断并配置了RTC相关操作的api。
通过dts的配置,使用不同的ops,从而实现支持write RTC与否。
//这个是不支持write RTC的ops
static const struct rtc_class_ops qpnp_rtc_ro_ops = {
.read_time = qpnp_rtc_read_time,
.set_alarm = qpnp_rtc_set_alarm,
.read_alarm = qpnp_rtc_read_alarm,
.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
};
//这个是支持读写 RTC的ops,就是多了个write的接口
static const struct rtc_class_ops qpnp_rtc_rw_ops = {
.read_time = qpnp_rtc_read_time,
.set_alarm = qpnp_rtc_set_alarm,
.read_alarm = qpnp_rtc_read_alarm,
.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
.set_time = qpnp_rtc_set_time,
};
static int qpnp_rtc_probe(struct platform_device pdev)
{
const struct rtc_class_ops rtc_ops = &qpnp_rtc_ro_ops; //默认使用不支持RTC write的ops
int rc;
u8 subtype;
struct qpnp_rtc rtc_dd;
unsigned int base;
struct device_node child;
rtc_dd = devm_kzalloc(&pdev->dev, sizeof(rtc_dd), GFP_KERNEL);
if (rtc_dd == NULL)
return -ENOMEM;
rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!rtc_dd->regmap) {
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
return -EINVAL;
}
/ Get the rtc write property /
rc = of_property_read_u32(pdev->dev.of_node, "qcom,qpnp-rtc-write",
&rtc_dd->rtc_write_enable); //读取dts中的RTC write配置
if (rc && rc != -EINVAL) {
dev_err(&pdev->dev,
"Error reading rtc_write_enable property %d\n", rc);
return rc;
}
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,qpnp-rtc-alarm-pwrup",
&rtc_dd->rtc_alarm_powerup); //读取dts中RTC alarm powerup的配置
if (rc && rc != -EINVAL) {
dev_err(&pdev->dev,
"Error reading rtc_alarm_powerup property %d\n", rc);
return rc;
}
/ Initialise spinlock to protect RTC control register /
spin_lock_init(&rtc_dd->alarm_ctrl_lock);
rtc_dd->rtc_dev = &(pdev->dev);
rtc_dd->pdev = pdev;
if (of_get_available_child_count(pdev->dev.of_node) == 0) {
pr_err("no child nodes\n");
rc = -ENXIO;
goto fail_rtc_enable;
}
/ Get RTC/ALARM resources /
for_each_available_child_of_node(pdev->dev.of_node, child) {
rc = of_property_read_u32(child, "reg", &base);
if (rc < 0) {
dev_err(&pdev->dev,
"Couldn't find reg in node = %s rc = %d\n",
child->full_name, rc);
goto fail_rtc_enable;
}
rc = qpnp_read_wrapper(rtc_dd, &subtype,
base + REG_OFFSET_PERP_SUBTYPE, 1);
if (rc) {
dev_err(&pdev->dev,
"Peripheral subtype read failed\n");
goto fail_rtc_enable;
}
switch (subtype) {
case RTC_PERPH_SUBTYPE:
rtc_dd->rtc_base = base;
break;
case ALARM_PERPH_SUBTYPE:
rtc_dd->alarm_base = base;
rtc_dd->rtc_alarm_irq = of_irq_get(child, 0);
if (rtc_dd->rtc_alarm_irq < 0) {
dev_err(&pdev->dev, "ALARM IRQ absent\n");
rc = -ENXIO;
goto fail_rtc_enable;
}
break;
default:
dev_err(&pdev->dev, "Invalid peripheral subtype\n");
rc = -EINVAL;
goto fail_rtc_enable;
}
}
rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
if (rc) {
dev_err(&pdev->dev, "Read from RTC control reg failed\n");
goto fail_rtc_enable;
}
if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
dev_err(&pdev->dev, "RTC h/w disabled, rtc not registered\n");
goto fail_rtc_enable;
}
rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
if (rc) {
dev_err(&pdev->dev, "Read from Alarm control reg failed\n");
goto fail_rtc_enable;
}
/ Enable abort enable feature /
rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
if (rc) {
dev_err(&pdev->dev, "SPMI write failed!\n");
goto fail_rtc_enable;
}
if (rtc_dd->rtc_write_enable == true) //判断dts中配置如果要支持RTC write功能,那就重新赋值,使用将RTC RW都支持的ops接口
rtc_ops = &qpnp_rtc_rw_ops;
dev_set_drvdata(&pdev->dev, rtc_dd);
/ Register the RTC device /
rtc_dd->rtc = rtc_device_register("qpnp_rtc", &pdev->dev,
rtc_ops, THIS_MODULE);
if (IS_ERR(rtc_dd->rtc)) {
dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
func, PTR_ERR(rtc_dd->rtc));
rc = PTR_ERR(rtc_dd->rtc);
goto fail_rtc_enable;
}
/ Request the alarm IRQ /
rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
"qpnp_rtc_alarm", rtc_dd);
if (rc) {
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
goto fail_req_irq;
}
device_init_wakeup(&pdev->dev, 1);
enable_irq_wake(rtc_dd->rtc_alarm_irq);
dev_dbg(&pdev->dev, "Probe success !!\n");
return 0;
fail_req_irq:
rtc_device_unregister(rtc_dd->rtc);
fail_rtc_enable:
dev_set_drvdata(&pdev->dev, NULL);
return rc;
}
而在RTC shutdown时,会根据是否要支持RTC alarm开机,进行中断和寄存器的配置。
static void qpnp_rtc_shutdown(struct platform_device pdev)
{
u8 value【4】 = {0};
u8 reg;
int rc;
unsigned long irq_flags;
struct qpnp_rtc rtc_dd;
bool rtc_alarm_powerup;
if (!pdev) {
pr_err("qpnp-rtc: spmi device not found\n");
return;
}
rtc_dd = dev_get_drvdata(&pdev->dev);
if (!rtc_dd) {
pr_err("qpnp-rtc: rtc driver data not found\n");
return;
}
rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
if (!rtc_alarm_powerup && !poweron_alarm) { //根据flag设置是否disbale RTC //代码效果参考:http://www.jhylw.com.cn/343525119.html
alarm的中断,以及通过设置reg,控制是否disable RTC alarmspin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
dev_dbg(&pdev->dev, "Disabling alarm interrupts\n");
/ Disable RTC alarms /
reg = rtc_dd->alarm_ctrl_reg1;
reg &= ~BIT_RTC_ALARM_ENABLE;
rc = qpnp_write_wrapper(rtc_dd, ®,
rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
if (rc) {
dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
goto fail_alarm_disable;
}
/ Clear Alarm register /
rc = qpnp_write_wrapper(rtc_dd, value,
rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
NUM_8_BIT_RTC_REGS);
if (rc)
dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
fail_alarm_disable:
//代码效果参考:http://www.jhylw.com.cn/154825899.html
spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);}
}
内核的开机时间首先是从RTC读取的时间作为基准,之后会通过QCOM time daemon进行corection。QCOM time daemon的代码非open source,所以暂不分析。这里我们仅分析RTC的部分。
而RTC的时间,我们知道第一次开机,这里指的是RTC断电后的第一次开机,RTC时间是1970-01-01 00:00:00(初始时间),这个值就是从RTC中读取出来的。而在使用一段时间之后,再重启手机,这时RTC的时间为=初始时间+使用的时间。也就是说,这个时间会随着使用而不断累计,除非RTC掉电,重置为初始时间。RTC也支持将同步后的时间再次写入RTC,用于校准当前的正确日期和时间。
Case 1 开机系统内核时间设置 from RTC
这里主要分析RTC驱动部分的RTC时间读取,并设置内核系统时间。
log:
【 0.897142】 qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@0:qcom,pm8998_rtc: rtc core: registered qpnp_rtc as rtc0
【 1.808755】 hctosys: 【ljj】open rtc device (rtc0)
【 1.808819】 qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@0:qcom,pm8998_rtc: setting system clock to 1970-01-05 00:01:06 UTC (345666)
上面的代码,可以看到kernel log的最前面是距离开机的时间累计(CLOCK_MONOTONIC),后面的则是RTC读取的UTC时间(CLOCK_REALTIME)。可以看到一开机,就会从RTC中读取UTC时间。
对应代码在hcttosys.c中:
static int __init rtc_hctosys(void)
{
int err = -ENODEV;
struct rtc_time tm;
struct timespec64 tv64 = {
.tv_nsec = NSEC_PER_SEC ] 1,
};
struct rtc_device rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
pr_info("【ljj】open rtc device (%s)\n",
CONFIG_RTC_HCTOSYS_DEVICE);
if (rtc == NULL) {
pr_info("unable to open rtc device (%s)\n",
CONFIG_RTC_HCTOSYS_DEVICE);
goto err_open;
}
//api 调用
err = rtc_read_time(rtc, &tm);
if (err) {
dev_err(rtc->dev.parent,
"hctosys: unable to read the hardware clock\n");
goto err_read;
}
tv64.tv_sec = rtc_tm_to_time64(&tm);
#if BITS_PER_LONG == 32
if (tv64.tv_sec > INT_MAX)
goto err_read;
#endif
//设置内核系统时间
err = do_settimeofday64(&tv64);
dev_info(rtc->dev.parent,
"setting system clock to "
"%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(long long) tv64.tv_sec);
err_read:
rtc_class_close(rtc);
err_open:
rtc_hctosys_ret = err;
return err;
}
late_initcall(rtc_hcto