相关文件list:

  1. pm8998.dtsi ---RTC dts配置
  2. qpnp-rtc.c  ---qcom RTC驱动
  3. class.c    ---RTC相关class
  4. interface.c ---相关RTC功能的接口定义
  5. hctosys.c ---一开机通过RTC设置系统时间
    rtc-dev.c   ---RTC device fops接口:opencloseioctlpoll

简述:

所谓RTC(Real Time Clock),用于关机时继续计算系统日期和时间。是基于硬件的功能。也可以RTC做Alarm来设置power on/off。

驱动分析:

首先在dts的Document中看到两个配置项:

  1. - qcom,qpnp-rtc-write: This property enables/disables rtc write
  2. 0 = Disable rtc writes.
  3. 1 = Enable rtc writes.
  4. - qcom,qpnp-rtc-alarm-pwrup: This property enables/disables
  5. feature of powering up phone (from
  6. power down state) through alarm
  7. interrupt.
  8. If not mentioned rtc driver will
  9. disable feature of powring-up
  10. phone through alarm.
  11. 0 = Disable powering up of phone
  12. through alarm interrupt.
  13. 1 = Enable powering up of phone
  14. 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与否。

  1. //这个是不支持write RTC的ops
  2. static const struct rtc_class_ops qpnp_rtc_ro_ops = {
  3. .read_time = qpnp_rtc_read_time,
  4. .set_alarm = qpnp_rtc_set_alarm,
  5. .read_alarm = qpnp_rtc_read_alarm,
  6. .alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
  7. };
  8.  
  9. //这个是支持读写 RTC的ops,就是多了个write的接口
  10. static const struct rtc_class_ops qpnp_rtc_rw_ops = {
  11. .read_time = qpnp_rtc_read_time,
  12. .set_alarm = qpnp_rtc_set_alarm,
  13. .read_alarm = qpnp_rtc_read_alarm,
  14. .alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
  15. .set_time = qpnp_rtc_set_time,
  16. };
  1. static int qpnp_rtc_probe(struct platform_device *pdev)
  2. {
  3. const struct rtc_class_ops *rtc_ops = &qpnp_rtc_ro_ops; //默认使用不支持RTC write的ops
  4. int rc;
  5. u8 subtype;
  6. struct qpnp_rtc *rtc_dd;
  7. unsigned int base;
  8. struct device_node *child;
  9.  
  10. rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
  11. if (rtc_dd == NULL)
  12. return -ENOMEM;
  13.  
  14. rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
  15. if (!rtc_dd->regmap) {
  16. dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
  17. return -EINVAL;
  18. }
  19.  
  20. /* Get the rtc write property */
  21. rc = of_property_read_u32(pdev->dev.of_node, "qcom,qpnp-rtc-write",
  22. &rtc_dd->rtc_write_enable); //读取dts中的RTC write配置
  23. if (rc && rc != -EINVAL) {
  24. dev_err(&pdev->dev,
  25. "Error reading rtc_write_enable property %d\n", rc);
  26. return rc;
  27. }
  28.  
  29. rc = of_property_read_u32(pdev->dev.of_node,
  30. "qcom,qpnp-rtc-alarm-pwrup",
  31. &rtc_dd->rtc_alarm_powerup); //读取dts中RTC alarm powerup的配置
  32. if (rc && rc != -EINVAL) {
  33. dev_err(&pdev->dev,
  34. "Error reading rtc_alarm_powerup property %d\n", rc);
  35. return rc;
  36. }
  37.  
  38. /* Initialise spinlock to protect RTC control register */
  39. spin_lock_init(&rtc_dd->alarm_ctrl_lock);
  40.  
  41. rtc_dd->rtc_dev = &(pdev->dev);
  42. rtc_dd->pdev = pdev;
  43.  
  44. if (of_get_available_child_count(pdev->dev.of_node) == ) {
  45. pr_err("no child nodes\n");
  46. rc = -ENXIO;
  47. goto fail_rtc_enable;
  48. }
  49.  
  50. /* Get RTC/ALARM resources */
  51. for_each_available_child_of_node(pdev->dev.of_node, child) {
  52. rc = of_property_read_u32(child, "reg", &base);
  53. if (rc < ) {
  54. dev_err(&pdev->dev,
  55. "Couldn't find reg in node = %s rc = %d\n",
  56. child->full_name, rc);
  57. goto fail_rtc_enable;
  58. }
  59.  
  60. rc = qpnp_read_wrapper(rtc_dd, &subtype,
  61. base + REG_OFFSET_PERP_SUBTYPE, );
  62. if (rc) {
  63. dev_err(&pdev->dev,
  64. "Peripheral subtype read failed\n");
  65. goto fail_rtc_enable;
  66. }
  67.  
  68. switch (subtype) {
  69. case RTC_PERPH_SUBTYPE:
  70. rtc_dd->rtc_base = base;
  71. break;
  72. case ALARM_PERPH_SUBTYPE:
  73. rtc_dd->alarm_base = base;
  74. rtc_dd->rtc_alarm_irq = of_irq_get(child, );
  75. if (rtc_dd->rtc_alarm_irq < ) {
  76. dev_err(&pdev->dev, "ALARM IRQ absent\n");
  77. rc = -ENXIO;
  78. goto fail_rtc_enable;
  79. }
  80. break;
  81. default:
  82. dev_err(&pdev->dev, "Invalid peripheral subtype\n");
  83. rc = -EINVAL;
  84. goto fail_rtc_enable;
  85. }
  86. }
  87.  
  88. rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
  89. rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, );
  90. if (rc) {
  91. dev_err(&pdev->dev, "Read from RTC control reg failed\n");
  92. goto fail_rtc_enable;
  93. }
  94.  
  95. if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
  96. dev_err(&pdev->dev, "RTC h/w disabled, rtc not registered\n");
  97. goto fail_rtc_enable;
  98. }
  99.  
  100. rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
  101. rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, );
  102. if (rc) {
  103. dev_err(&pdev->dev, "Read from Alarm control reg failed\n");
  104. goto fail_rtc_enable;
  105. }
  106. /* Enable abort enable feature */
  107. rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
  108. rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
  109. rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, );
  110. if (rc) {
  111. dev_err(&pdev->dev, "SPMI write failed!\n");
  112. goto fail_rtc_enable;
  113. }
  114.  
  115. if (rtc_dd->rtc_write_enable == true) //判断dts中配置如果要支持RTC write功能,那就重新赋值,使用将RTC RW都支持的ops接口
  116. rtc_ops = &qpnp_rtc_rw_ops;
  117.  
  118. dev_set_drvdata(&pdev->dev, rtc_dd);
  119.  
  120. /* Register the RTC device */
  121. rtc_dd->rtc = rtc_device_register("qpnp_rtc", &pdev->dev,
  122. rtc_ops, THIS_MODULE);
  123. if (IS_ERR(rtc_dd->rtc)) {
  124. dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
  125. __func__, PTR_ERR(rtc_dd->rtc));
  126. rc = PTR_ERR(rtc_dd->rtc);
  127. goto fail_rtc_enable;
  128. }
  129.  
  130. /* Request the alarm IRQ */
  131. rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
  132. qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
  133. "qpnp_rtc_alarm", rtc_dd);
  134. if (rc) {
  135. dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
  136. goto fail_req_irq;
  137. }
  138.  
  139. device_init_wakeup(&pdev->dev, );
  140. enable_irq_wake(rtc_dd->rtc_alarm_irq);
  141.  
  142. dev_dbg(&pdev->dev, "Probe success !!\n");
  143.  
  144. return ;
  145.  
  146. fail_req_irq:
  147. rtc_device_unregister(rtc_dd->rtc);
  148. fail_rtc_enable:
  149. dev_set_drvdata(&pdev->dev, NULL);
  150.  
  151. return rc;
  152. }

而在RTC shutdown时,会根据是否要支持RTC alarm开机,进行中断和寄存器的配置。

  1. static void qpnp_rtc_shutdown(struct platform_device *pdev)
  2. {
  3. u8 value[] = {};
  4. u8 reg;
  5. int rc;
  6. unsigned long irq_flags;
  7. struct qpnp_rtc *rtc_dd;
  8. bool rtc_alarm_powerup;
  9.  
  10. if (!pdev) {
  11. pr_err("qpnp-rtc: spmi device not found\n");
  12. return;
  13. }
  14. rtc_dd = dev_get_drvdata(&pdev->dev);
  15. if (!rtc_dd) {
  16. pr_err("qpnp-rtc: rtc driver data not found\n");
  17. return;
  18. }
  19. rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
  20. if (!rtc_alarm_powerup && !poweron_alarm) { //根据flag设置是否disbale RTC alarm的中断,以及通过设置reg,控制是否disable RTC alarm
  21. spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
  22. dev_dbg(&pdev->dev, "Disabling alarm interrupts\n");
  23.  
  24. /* Disable RTC alarms */
  25. reg = rtc_dd->alarm_ctrl_reg1;
  26. reg &= ~BIT_RTC_ALARM_ENABLE;
  27. rc = qpnp_write_wrapper(rtc_dd, &reg,
  28. rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, );
  29. if (rc) {
  30. dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
  31. goto fail_alarm_disable;
  32. }
  33.  
  34. /* Clear Alarm register */
  35. rc = qpnp_write_wrapper(rtc_dd, value,
  36. rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
  37. NUM_8_BIT_RTC_REGS);
  38. if (rc)
  39. dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
  40.  
  41. fail_alarm_disable:
  42. spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
  43. }
  44. }

内核的开机时间首先是从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:

  1. [ 0.897142] qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@:qcom,pm8998_rtc: rtc core: registered qpnp_rtc as rtc0
  2. [ 1.808755] hctosys: [ljj]open rtc device (rtc0)
  3. [ 1.808819] qcom,qpnp-rtc c440000.qcom,spmi:qcom,pm8998@:qcom,pm8998_rtc: setting system clock to -- :: UTC ()

上面的代码,可以看到kernel log的最前面是距离开机的时间累计(CLOCK_MONOTONIC),后面的则是RTC读取的UTC时间(CLOCK_REALTIME)。可以看到一开机,就会从RTC中读取UTC时间。

对应代码在hcttosys.c中:

  1. static int __init rtc_hctosys(void)
  2. {
  3. int err = -ENODEV;
  4. struct rtc_time tm;
  5. struct timespec64 tv64 = {
  6. .tv_nsec = NSEC_PER_SEC >> ,
  7. };
  8. struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
  9. pr_info("[ljj]open rtc device (%s)\n",
  10. CONFIG_RTC_HCTOSYS_DEVICE);
  11. if (rtc == NULL) {
  12. pr_info("unable to open rtc device (%s)\n",
  13. CONFIG_RTC_HCTOSYS_DEVICE);
  14. goto err_open;
  15. }
  16. //api 调用
  17. err = rtc_read_time(rtc, &tm);
  18. if (err) {
  19. dev_err(rtc->dev.parent,
  20. "hctosys: unable to read the hardware clock\n");
  21. goto err_read;
  22.  
  23. }
  24.  
  25. tv64.tv_sec = rtc_tm_to_time64(&tm);
  26.  
  27. #if BITS_PER_LONG == 32
  28. if (tv64.tv_sec > INT_MAX)
  29. goto err_read;
  30. #endif
  31. //设置内核系统时间
  32. err = do_settimeofday64(&tv64);
  33.  
  34. dev_info(rtc->dev.parent,
  35. "setting system clock to "
  36. "%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
  37. tm.tm_year + , tm.tm_mon + , tm.tm_mday,
  38. tm.tm_hour, tm.tm_min, tm.tm_sec,
  39. (long long) tv64.tv_sec);
  40.  
  41. err_read:
  42. rtc_class_close(rtc);
  43.  
  44. err_open:
  45. rtc_hctosys_ret = err;
  46.  
  47. return err;
  48. }
  49.  
  50. late_initcall(rtc_hctosys);
  1. int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
  2. {
  3. int err;
  4.  
  5. err = mutex_lock_interruptible(&rtc->ops_lock);
  6. if (err)
  7. return err;
  8.  
  9. err = __rtc_read_time(rtc, tm); //调用内部__rtc_read_time
  10. mutex_unlock(&rtc->ops_lock);
  11. return err;
  12. }
  13. EXPORT_SYMBOL_GPL(rtc_read_time);
  14. static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
  15. {
  16. int err;
  17. if (!rtc->ops)
  18. err = -ENODEV;
  19. else if (!rtc->ops->read_time)
  20. err = -EINVAL;
  21. else {
  22. memset(tm, , sizeof(struct rtc_time));
  23. err = rtc->ops->read_time(rtc->dev.parent, tm); //调用了qpnp-rtc.c中的read_time ops,最后就是通过读取寄存器,来获取RTC硬件计算的时间,具体代码就不贴了
  24. if (err < ) {
  25. dev_dbg(&rtc->dev, "read_time: fail to read: %d\n",
  26. err);
  27. return err;
  28. }
  29.  
  30. err = rtc_valid_tm(tm);
  31. if (err < )
  32. dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n");
  33. }
  34. return err;
  35. }

Case 2 kernel RTC时间与system时间进行同步

实际是因为RTC中的时间并不一定准,所以会用android system中的时间与RTC时间进行对比同步和更新。也就是上面可能会牵扯到QCOM time Daemon的部分,所以避开这块闭源代码。可以参考如下链接,了解开源代码中内核时间与system时间同步。

kernel log中时间同步的流程,参考自:http://blog.chinaunix.net/uid-23141914-id-5715368.html

Case 3 更新并设置RTC时间

当时间与网络、或者setting同步后,可能需要将当前正确的时间再写入RTC中,以保证RTC中的时间准确。这块我们往上看多一点,从jni开始分析。如下:

路径:frameworks/base/services/core/jni/com_android_server_AlarmManagerService.cpp

  1. static const JNINativeMethod sMethods[] = {
  2. /* name, signature, funcPtr */
  3. {"init", "()J", (void*)android_server_AlarmManagerService_init},
  4. {"close", "(J)V", (void*)android_server_AlarmManagerService_close},
  5. {"set", "(JIJJ)I", (void*)android_server_AlarmManagerService_set},
  6. {"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
  7. {"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime}, //设置时间的JNI
  8. {"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
  9. };
  10.  
  11. static jint android_server_AlarmManagerService_setKernelTime(JNIEnv*, jobject, jlong nativeData, jlong millis)
  12. {
  13. AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
  14. struct timeval tv;
  15. int ret;
  16.  
  17. if (millis <= || millis / 1000LL >= INT_MAX) {
  18. return -;
  19. }
  20.  
  21. tv.tv_sec = (time_t) (millis / 1000LL);
  22. tv.tv_usec = (suseconds_t) ((millis % 1000LL) * 1000LL);
  23.  
  24. ALOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec);
  25.  
  26. ret = impl->setTime(&tv); //调用setTime
  27.  
  28. if(ret < ) {
  29. ALOGW("Unable to set rtc to %ld: %s\n", tv.tv_sec, strerror(errno));
  30. ret = -;
  31. }
  32. return ret;
  33. }
  34.  
  35. int AlarmImpl::setTime(struct timeval *tv)
  36. {
  37. struct rtc_time rtc;
  38. struct tm tm, *gmtime_res;
  39. int fd;
  40. int res;
  41.  
  42. res = settimeofday(tv, NULL);
  43. if (res < ) {
  44. ALOGV("settimeofday() failed: %s\n", strerror(errno));
  45. return -;
  46. }
  47.  
  48. if (rtc_id < ) {
  49. ALOGV("Not setting RTC because wall clock RTC was not found");
  50. errno = ENODEV;
  51. return -;
  52. }
  53.  
  54. android::String8 rtc_dev = String8::format("/dev/rtc%d", rtc_id);
  55. fd = open(rtc_dev.string(), O_RDWR); //打开了RTC设备
  56. if (fd < ) {
  57. ALOGV("Unable to open %s: %s\n", rtc_dev.string(), strerror(errno));
  58. return res;
  59. }
  60.  
  61. gmtime_res = gmtime_r(&tv->tv_sec, &tm);
  62. if (!gmtime_res) {
  63. ALOGV("gmtime_r() failed: %s\n", strerror(errno));
  64. res = -;
  65. goto done;
  66. }
  67.  
  68. memset(&rtc, , sizeof(rtc));
  69. rtc.tm_sec = tm.tm_sec;
  70. rtc.tm_min = tm.tm_min;
  71. rtc.tm_hour = tm.tm_hour;
  72. rtc.tm_mday = tm.tm_mday;
  73. rtc.tm_mon = tm.tm_mon;
  74. rtc.tm_year = tm.tm_year;
  75. rtc.tm_wday = tm.tm_wday;
  76. rtc.tm_yday = tm.tm_yday;
  77. rtc.tm_isdst = tm.tm_isdst;
  78. res = ioctl(fd, RTC_SET_TIME, &rtc); //通过ioctl设置RTC时间
  79. if (res < )
  80. ALOGV("RTC_SET_TIME ioctl failed: %s\n", strerror(errno));
  81. done:
  82. close(fd);
  83. return res;
  84. }

接下来就是驱动部分,rtc-dev.c文件中的ioctl接口,最后会调用至qpnp-rtc.c中的set接口,从而将准确的时间写入对应寄存器:

  1. static long rtc_dev_ioctl(struct file *file,
  2. unsigned int cmd, unsigned long arg)
  3. {
  4. int err = ;
  5. struct rtc_device *rtc = file->private_data;
  6. const struct rtc_class_ops *ops = rtc->ops;
  7. struct rtc_time tm;
  8. struct rtc_wkalrm alarm;
  9. void __user *uarg = (void __user *) arg;
  10.  
  11. err = mutex_lock_interruptible(&rtc->ops_lock);
  12. if (err)
  13. return err;
  14.  
  15. /* check that the calling task has appropriate permissions
  16. * for certain ioctls. doing this check here is useful
  17. * to avoid duplicate code in each driver.
  18. */
  19. switch (cmd) {
  20. case RTC_EPOCH_SET:
  21. case RTC_SET_TIME:
  22. if (!capable(CAP_SYS_TIME)) //如上面注释所说,设置时间的api必须要有CAP_SYS_TIME的权限
  23. err = -EACCES;
  24. break;
  25.  
  26. case RTC_IRQP_SET:
  27. if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
  28. err = -EACCES;
  29. break;
  30.  
  31. case RTC_PIE_ON:
  32. if (rtc->irq_freq > rtc->max_user_freq &&
  33. !capable(CAP_SYS_RESOURCE))
  34. err = -EACCES;
  35. break;
  36. }
  37.  
  38. if (err)
  39. goto done;
  40.  
  41. /*
  42. * Drivers *SHOULD NOT* provide ioctl implementations
  43. * for these requests. Instead, provide methods to
  44. * support the following code, so that the RTC's main
  45. * features are accessible without using ioctls.
  46. *
  47. * RTC and alarm times will be in UTC, by preference,
  48. * but dual-booting with MS-Windows implies RTCs must
  49. * use the local wall clock time.
  50. */
  51.  
  52. switch (cmd) {
  53. case RTC_ALM_READ:
  54. mutex_unlock(&rtc->ops_lock);
  55.  
  56. err = rtc_read_alarm(rtc, &alarm);
  57. if (err < )
  58. return err;
  59.  
  60. if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
  61. err = -EFAULT;
  62. return err;
  63.  
  64. case RTC_ALM_SET:
  65. mutex_unlock(&rtc->ops_lock);
  66.  
  67. if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
  68. return -EFAULT;
  69.  
  70. alarm.enabled = ;
  71. alarm.pending = ;
  72. alarm.time.tm_wday = -;
  73. alarm.time.tm_yday = -;
  74. alarm.time.tm_isdst = -;
  75.  
  76. /* RTC_ALM_SET alarms may be up to 24 hours in the future.
  77. * Rather than expecting every RTC to implement "don't care"
  78. * for day/month/year fields, just force the alarm to have
  79. * the right values for those fields.
  80. *
  81. * RTC_WKALM_SET should be used instead. Not only does it
  82. * eliminate the need for a separate RTC_AIE_ON call, it
  83. * doesn't have the "alarm 23:59:59 in the future" race.
  84. *
  85. * NOTE: some legacy code may have used invalid fields as
  86. * wildcards, exposing hardware "periodic alarm" capabilities.
  87. * Not supported here.
  88. */
  89. {
  90. time64_t now, then;
  91.  
  92. err = rtc_read_time(rtc, &tm);
  93. if (err < )
  94. return err;
  95. now = rtc_tm_to_time64(&tm);
  96.  
  97. alarm.time.tm_mday = tm.tm_mday;
  98. alarm.time.tm_mon = tm.tm_mon;
  99. alarm.time.tm_year = tm.tm_year;
  100. err = rtc_valid_tm(&alarm.time);
  101. if (err < )
  102. return err;
  103. then = rtc_tm_to_time64(&alarm.time);
  104.  
  105. /* alarm may need to wrap into tomorrow */
  106. if (then < now) {
  107. rtc_time64_to_tm(now + * * , &tm);
  108. alarm.time.tm_mday = tm.tm_mday;
  109. alarm.time.tm_mon = tm.tm_mon;
  110. alarm.time.tm_year = tm.tm_year;
  111. }
  112. }
  113.  
  114. return rtc_set_alarm(rtc, &alarm);
  115.  
  116. case RTC_RD_TIME:
  117. mutex_unlock(&rtc->ops_lock);
  118.  
  119. err = rtc_read_time(rtc, &tm);
  120. if (err < )
  121. return err;
  122.  
  123. if (copy_to_user(uarg, &tm, sizeof(tm)))
  124. err = -EFAULT;
  125. return err;
  126.  
  127. case RTC_SET_TIME:
  128. mutex_unlock(&rtc->ops_lock);
  129.  
  130. if (copy_from_user(&tm, uarg, sizeof(tm))) //获取上层传下来的tm结构
  131. return -EFAULT;
  132.  
  133. return rtc_set_time(rtc, &tm); //通过interface.c的接口,从而调用qpnp-rtc.c的设置RTC寄存器函数,完成RTC时间写入。
  134.  
  135. case RTC_PIE_ON:
  136. err = rtc_irq_set_state(rtc, NULL, );
  137. break;
  138.  
  139. case RTC_PIE_OFF:
  140. err = rtc_irq_set_state(rtc, NULL, );
  141. break;
  142.  
  143. case RTC_AIE_ON:
  144. mutex_unlock(&rtc->ops_lock);
  145. return rtc_alarm_irq_enable(rtc, );
  146.  
  147. case RTC_AIE_OFF:
  148. mutex_unlock(&rtc->ops_lock);
  149. return rtc_alarm_irq_enable(rtc, );
  150.  
  151. case RTC_UIE_ON:
  152. mutex_unlock(&rtc->ops_lock);
  153. return rtc_update_irq_enable(rtc, );
  154.  
  155. case RTC_UIE_OFF:
  156. mutex_unlock(&rtc->ops_lock);
  157. return rtc_update_irq_enable(rtc, );
  158.  
  159. case RTC_IRQP_SET:
  160. err = rtc_irq_set_freq(rtc, NULL, arg);
  161. break;
  162.  
  163. case RTC_IRQP_READ:
  164. err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
  165. break;
  166.  
  167. case RTC_WKALM_SET:
  168. mutex_unlock(&rtc->ops_lock);
  169. if (copy_from_user(&alarm, uarg, sizeof(alarm)))
  170. return -EFAULT;
  171.  
  172. return rtc_set_alarm(rtc, &alarm);
  173.  
  174. case RTC_WKALM_RD:
  175. mutex_unlock(&rtc->ops_lock);
  176. err = rtc_read_alarm(rtc, &alarm);
  177. if (err < )
  178. return err;
  179.  
  180. if (copy_to_user(uarg, &alarm, sizeof(alarm)))
  181. err = -EFAULT;
  182. return err;
  183.  
  184. default:
  185. /* Finally try the driver's ioctl interface */
  186. if (ops->ioctl) {
  187. err = ops->ioctl(rtc->dev.parent, cmd, arg);
  188. if (err == -ENOIOCTLCMD)
  189. err = -ENOTTY;
  190. } else
  191. err = -ENOTTY;
  192. break;
  193. }
  194.  
  195. done:
  196. mutex_unlock(&rtc->ops_lock);
  197. return err;
  198. }

Qcom平台RTC驱动分析的更多相关文章

  1. IMX6Q RTC驱动分析

    对于在工作中学习驱动的,讲究的是先使用,再理解.好吧,我们来看看板子里是如何注册的? 在板文件里,它的注册函数是这样的: imx6q_add_imx_snvs_rtc() 好吧,让我们追踪下去: 1 ...

  2. 高通 android平台LCD驱动分析

    目前手机芯片厂家提供的源码里包含整个LCD驱动框架,一般厂家会定义一个xxx_fb.c的源文件,注册一个平台设备和平台驱动,在驱动的probe函数中来调用register_framebuffer(), ...

  3. linux RTC 驱动模型分析【转】

    转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系 ...

  4. Linux RTC驱动模型分析之rtc-sysfs.c【转】

    转自:https://blog.csdn.net/longwang155069/article/details/52353408 版权声明:本文为博主原创文章,未经博主允许不得转载. https:// ...

  5. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    转载自:http://www.kancloud.cn/yueqian_scut/emlinux/106829 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动.设备驱动模型和sy ...

  6. 电源管理之pmu驱动分析

    电源管理芯片可以为多设备供电,且这些设备电压电流有所不同.为这些设备提供的稳压器代码模型即为regulator. 说白了regulator就是稳压器,它提供电源供给.简单的可以gpio操作,高电平开电 ...

  7. [kernel]字符设备驱动、平台设备驱动、设备驱动模型、sysfs几者之间的比较和关联

    转自:http://www.2cto.com/kf/201510/444943.html Linux驱动开发经验总结,绝对干货! 学习Linux设备驱动开发的过程中自然会遇到字符设备驱动.平台设备驱动 ...

  8. 二十一、RTC驱动

    一.RTC设备驱动分析 内核的rtc驱动位于内核drivers/rtc目录下,里面包含各个平台的RTC驱动.读者可在此目录下任意选择一个单板驱动文件进行分析,我选择的是rtc-davinci.c文件. ...

  9. linux kernel 平台总线实例分析

    linux 平台总线的实现有三大块  , platform bus , platform device , platform drvice 平台类型结构体: /** * struct bus_type ...

随机推荐

  1. 从浅入深——理解JSONP的实现原理

    由于浏览器的安全性限制,不允许AJAX访问 协议不同.域名不同.端口号不同的 数据接口,浏览器认为这种访问不安全: 可以通过动态创建script标签的形式,把script标签的src属性,指向数据接口 ...

  2. python3语法学习第五天--函数(1)

    函数:函数能提高应用的模块性,和代码的重复利用率,是一段可重复使用的代码块 自定义函数: 1.函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 (). 2.任何传入参数和自变量必须放在圆括 ...

  3. [hdu1242]优先队列

    题意:给一个地图,'x'走一步代价为2,'.'走一步代价为1,求从s到t的最小代价.裸优先队列. #pragma comment(linker, "/STACK:10240000,10240 ...

  4. vscode+eslint自动格式化vue代码的方法

    前言 使用vscode开发vue项目的时候,为了编码格式的统一化,使用eslint规范进行格式化.此时通过eslint插件可以实现对vue代码的自动格式化. 使用方式 在vscode的插件模块处,搜索 ...

  5. Python脚本:实现excel表格导入到数据库,支持mysql,postgresql,MongoDB

    import xlrd,re from datetime import datetime from xlrd import xldate_as_tuple # 判断上传表格是否与模板要求一致 def ...

  6. POI 导入excel数据自动封装成model对象--代码

    所有的代码如下: import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; ...

  7. java ->String、StringBuffer、StringBuilder三者之间的区别

    1.首先说运行速度,速度由快到慢排列:StringBuilder > StringBuffer > String String最慢的原因: String为字符串常量,而StringBuil ...

  8. python-修改文件

    1.修改文件1 # fw = open('username','w')# fw.write('hhhh')# fw.flush()  #强制把缓冲区里面的数据写到磁盘上1.简单粗暴直接#  1.打开一 ...

  9. C# 数据操作系列 - 8. EF Core的增删改查

    0.前言 到目前为止,我们看了一下如何声明EF Core的初步使用,也整体的看了下EF Core的映射关系配置以及导航属性的配置. 这一篇,我带大家分享一下,我在工作中需要的EF Core的用法. 1 ...

  10. vue 升级element-ui woff文件404

    一.build文件下utils.js下增加 publicPath:'../../' 二. 同样的代码环境,用yarn来安装依赖后启动运行正常,而采用npm安装依赖则有类似问题.当然,这个和yarn或者 ...