一、RTC的概念和工作原理

RTC:实时时钟,主要用来记录时间的,通过RTC时钟可以知道年月日和时间。可以外加RTC芯片实现RTC功能,也可以用主控芯片内部集成好的RTC模块。在板子关闭电源后,可以用后备电源供电,比如纽扣电池。要实现记录时间的功能,还要连接时钟源,imx6ull用的是一个 32.768KHz 的晶振。晶振跳一下,RTC就记录一个数,然后根据RTC的数据和晶振的频率就可以换算出年月日和时间了(起始时间是1970/1/1日0时0分0秒)。

二、RTC驱动

RTC驱动主要是注册一个 rtc_device 的结构体

devm_rtc_device_register(&pdev->dev, pdev->name,&snvs_rtc_ops, THIS_MODULE);

        snvs_rtc_ops 这个结构体中存放一些操作函数,这些read write函数并不是file_operations 中的read函数,当应用层调用 read 函数时,先是调用sys_read -> file_operations.read -> snvs_rtc_ops.read_函数。而 file_operations 在rtc 框架中自动创建好,在file_operations还会s实现 rtc_dev_ioctl 函数,应用层才能用 ioctl 函数设置RTC。

nxp官方写的驱动源码:

/** Copyright (C) 2011-2016 Freescale Semiconductor, Inc.** The code contained herein is licensed under the GNU General Public* License. You may obtain a copy of the GNU General Public License* Version 2 or later at the following locations:** http://www.opensource.org/licenses/gpl-license.html* http://www.gnu.org/copyleft/gpl.html*/#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/clk.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>#define SNVS_LPREGISTER_OFFSET   0x34/* These register offsets are relative to LP (Low Power) range */
#define SNVS_LPCR       0x04
#define SNVS_LPSR       0x18
#define SNVS_LPSRTCMR       0x1c
#define SNVS_LPSRTCLR       0x20
#define SNVS_LPTAR      0x24
#define SNVS_LPPGDR     0x30#define SNVS_LPCR_SRTC_ENV  (1 << 0)
#define SNVS_LPCR_LPTA_EN   (1 << 1)
#define SNVS_LPCR_LPWUI_EN  (1 << 3)
#define SNVS_LPSR_LPTA      (1 << 0)#define SNVS_LPPGDR_INIT  0x41736166
#define CNTR_TO_SECS_SH     15struct snvs_rtc_data {struct rtc_device *rtc;struct regmap *regmap;int offset;int irq;struct clk *clk;
};static u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
{u64 read1, read2;u32 val;do {regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);read1 = val;read1 <<= 32;regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);read1 |= val;regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);read2 = val;read2 <<= 32;regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);read2 |= val;/** when CPU/BUS are running at low speed, there is chance that* we never get same value during two consecutive read, so here* we only compare the second value.*/} while ((read1 >> CNTR_TO_SECS_SH) != (read2 >> CNTR_TO_SECS_SH));/* Convert 47-bit counter to 32-bit raw second count */return (u32) (read1 >> CNTR_TO_SECS_SH);
}static void rtc_write_sync_lp(struct snvs_rtc_data *data)
{u32 count1, count2, count3;int i;/* Wait for 3 CKIL cycles */for (i = 0; i < 3; i++) {do {regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1);regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);} while (count1 != count2);/* Now wait until counter value changes */do {do {regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2);regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count3);} while (count2 != count3);} while (count3 == count1);}
}static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
{int timeout = 1000;u32 lpcr;regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV,enable ? SNVS_LPCR_SRTC_ENV : 0);while (--timeout) {regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr);if (enable) {if (lpcr & SNVS_LPCR_SRTC_ENV)break;} else {if (!(lpcr & SNVS_LPCR_SRTC_ENV))break;}}if (!timeout)return -ETIMEDOUT;return 0;
}static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);unsigned long time = rtc_read_lp_counter(data);rtc_time_to_tm(time, tm);return 0;
}static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);unsigned long time;rtc_tm_to_time(tm, &time);/* Disable RTC first */snvs_rtc_enable(data, false);/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH);regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH));/* Enable RTC again */snvs_rtc_enable(data, true);return 0;
}static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);u32 lptar, lpsr;regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar);rtc_time_to_tm(lptar, &alrm->time);regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;return 0;
}static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);regmap_update_bits(data->regmap, data->offset + SNVS_LPCR,(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN),enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0);rtc_write_sync_lp(data);return 0;
}static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);struct rtc_time *alrm_tm = &alrm->time;unsigned long time;rtc_tm_to_time(alrm_tm, &time);regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0);regmap_write(data->regmap, data->offset + SNVS_LPTAR, time);/* Clear alarm interrupt status bit */regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA);return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
}static const struct rtc_class_ops snvs_rtc_ops = {.read_time = snvs_rtc_read_time,.set_time = snvs_rtc_set_time,.read_alarm = snvs_rtc_read_alarm,.set_alarm = snvs_rtc_set_alarm,.alarm_irq_enable = snvs_rtc_alarm_irq_enable,
};static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
{struct device *dev = dev_id;struct snvs_rtc_data *data = dev_get_drvdata(dev);u32 lpsr;u32 events = 0;regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);if (lpsr & SNVS_LPSR_LPTA) {events |= (RTC_AF | RTC_IRQF);/* RTC alarm should be one-shot */snvs_rtc_alarm_irq_enable(dev, 0);rtc_update_irq(data->rtc, 1, events);}/* clear interrupt status */regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);return events ? IRQ_HANDLED : IRQ_NONE;
}static const struct regmap_config snvs_rtc_config = {.reg_bits = 32,.val_bits = 32,.reg_stride = 4,
};static int snvs_rtc_probe(struct platform_device *pdev)
{struct snvs_rtc_data *data;struct resource *res;int ret;void __iomem *mmio;data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);if (!data)return -ENOMEM;data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");if (IS_ERR(data->regmap)) {dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");res = platform_get_resource(pdev, IORESOURCE_MEM, 0);mmio = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(mmio))return PTR_ERR(mmio);data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config);} else {data->offset = SNVS_LPREGISTER_OFFSET;of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);}if (!data->regmap) {dev_err(&pdev->dev, "Can't find snvs syscon\n");return -ENODEV;}data->irq = platform_get_irq(pdev, 0);if (data->irq < 0)return data->irq;data->clk = devm_clk_get(&pdev->dev, "snvs-rtc");if (IS_ERR(data->clk)) {data->clk = NULL;} else {ret = clk_prepare_enable(data->clk);if (ret) {dev_err(&pdev->dev,"Could not prepare or enable the snvs clock\n");return ret;}}platform_set_drvdata(pdev, data);/* Initialize glitch detect */regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);/* Clear interrupt status */regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);/* Enable RTC */snvs_rtc_enable(data, true);device_init_wakeup(&pdev->dev, true);ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,IRQF_SHARED, "rtc alarm", &pdev->dev);if (ret) {dev_err(&pdev->dev, "failed to request irq %d: %d\n",data->irq, ret);goto error_rtc_device_register;}data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,&snvs_rtc_ops, THIS_MODULE);if (IS_ERR(data->rtc)) {ret = PTR_ERR(data->rtc);dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);goto error_rtc_device_register;}return 0;error_rtc_device_register:if (data->clk)clk_disable_unprepare(data->clk);return ret;
}#ifdef CONFIG_PM_SLEEP
static int snvs_rtc_suspend(struct device *dev)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);if (device_may_wakeup(dev))enable_irq_wake(data->irq);return 0;
}static int snvs_rtc_suspend_noirq(struct device *dev)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);if (data->clk)clk_disable_unprepare(data->clk);return 0;
}static int snvs_rtc_resume(struct device *dev)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);if (device_may_wakeup(dev))return disable_irq_wake(data->irq);return 0;
}static int snvs_rtc_resume_noirq(struct device *dev)
{struct snvs_rtc_data *data = dev_get_drvdata(dev);if (data->clk)return clk_prepare_enable(data->clk);return 0;
}static const struct dev_pm_ops snvs_rtc_pm_ops = {.suspend = snvs_rtc_suspend,.suspend_noirq = snvs_rtc_suspend_noirq,.resume = snvs_rtc_resume,.resume_noirq = snvs_rtc_resume_noirq,
};#define SNVS_RTC_PM_OPS   (&snvs_rtc_pm_ops)#else#define SNVS_RTC_PM_OPS  NULL#endifstatic const struct of_device_id snvs_dt_ids[] = {{ .compatible = "fsl,sec-v4.0-mon-rtc-lp", },{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, snvs_dt_ids);static struct platform_driver snvs_rtc_driver = {.driver = {.name    = "snvs_rtc",.pm = SNVS_RTC_PM_OPS,.of_match_table = snvs_dt_ids,},.probe      = snvs_rtc_probe,
};
module_platform_driver(snvs_rtc_driver);MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
MODULE_LICENSE("GPL");

三、测试RTC驱动

1、用 date 命令可以查看系统时间,

2、date -s "2019-08-31 18:13:00"         设置系统时间

3、hwclock -w         将当前系统时间写入到 RTC 里面

linux RTC驱动相关推荐

  1. linux RTC 驱动模型分析

    linux RTC 驱动模型分析 RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间.RTC因为是电池供电的,所以掉电后时间不丢失.Linux内核把RTC用作&quo ...

  2. 【正点原子Linux连载】第六十章 Linux RTC驱动实验 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  3. Linux RTC 驱动实验

    目录 Linux 内核RTC 驱动简介 I.MX6U 内部RTC 驱动分析 RTC 时间查看与设置 RTC 也就是实时时钟,用于记录当前系统时间,对于Linux 系统而言时间是非常重要的,就和我们使用 ...

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

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

  5. linux i2c模型 rtc模型 详细分析,Linux RTC驱动分析(一)----pcf8563驱动和class.c

    来逐一分析. 看一个设备驱动,一般都从模块初始化和退出函数开始,pcf8563.c的为: static int __init pcf8563_init(void) { return i2c_add_d ...

  6. linux rtc与时钟

    最近遇到一个问题:机器深度休眠后唤醒,发现唤醒后系统时间与pc实际时间相比变慢,休眠时间越长,系统时间与实际时间偏差越大. 一.问题分析 linux时间有2种,rtc时钟与系统时钟.Rtc时钟是不断电 ...

  7. 正点原子linux驱动教程,正点原子 手把手教你学Linux之驱动开发篇

    简 介 该课程是正点原子手把手教你学Linux系列课程,该课程配套开发板为正点原子alpha/mini Linux开发板. 手把手教你学Linux之驱动开发篇: 第1讲 Linux驱动开发与裸机开发区 ...

  8. linux下测试RTC驱动相关的命令date和hwclock常见用法简介

    之前对Linux下面时间相关的内容,一无所知,第一次见到hwclock,不知为何物,也没找到解释清楚的帖子.故此整理一下,简单介绍Linux下验证rtc驱动是否工作正常,相关的的命令:date和hwc ...

  9. linux下的rtc设备驱动,linux下测试RTC驱动相关的命令date和hwclock常见用法讲解

    之前对Linux下面时间相关的内容,一无所知,第一次见到hwclock,不知为何物,也没找到解释清楚的帖子.故此整理一下,简单介绍Linux下验证rtc驱动是否工作正常,相关的的命令:date和hwc ...

最新文章

  1. 题目 1093:【蓝桥杯】【入门题】字符逆序
  2. python和R对dataframe的拼接、采样、链式操作:dplyr、tidyr、concat、rbind、cbind、sample、sample_n、set.seed、mutate、filter
  3. 【深度学习】图解 9 种PyTorch中常用的学习率调整策略
  4. 设置双核浏览器的浏览模式meta name=“renderer” content=“webkit|ie-comp|ie-stand”
  5. 安规电容能用什么代替_电容系列之安规电容
  6. 学习vim的linux游戏,PacVim:一个学习 vim 命令的命令行游戏 | Linux 中国
  7. 云服务器惠普版_工作进度通报,笔记本开箱目录20200531版
  8. 将两个PCB文件合并成一个文件
  9. 电脑或者手机的PIN码
  10. Android TV真机测试
  11. 王 第潜艇三天 引用类型 继承
  12. 【UE】BUILD ERROR: Missing precompiled manifest for ‘****‘.
  13. ime with the \acknowledge=true\ parameter:,watcher:[Watcher will be disabled
  14. (转载)SAP第三方销售实现方法
  15. Gradle sync failed: Could not find xxxx.xx 之 强制刷新Gradle dependencies
  16. leveldb之arena
  17. TwinCAT更改背景主题颜色
  18. wordpress linux 目录,Linux系统二级目录无法安装Wordpress解决办法 | 无忧主机
  19. mx150 宏碁swift3_宏碁Swift 3评测:炫美轻薄还有MX150独显
  20. 七天编写指标_操盘线指标公式源码(七天线 工作线 生命线)[通达信公式

热门文章

  1. pandoc讲html转换为pdf,使用pandoc将.docx转换为.pdf
  2. 限时免费 | 8位支付大咖演讲 PPT 14 份行业权威报告 10 篇重磅支付干货……...
  3. 栈(模拟栈,表达式求值)
  4. Problem A: 小蛮腰
  5. mac文字识别 好用的免费软件 翻译软件 搜索软件 预览软件 腾讯软件 腾讯柠檬精选 腾讯柠檬 懒人必备
  6. 新浪微博营销应该怎么去做才好呢?
  7. MapleSim助力长臂挖掘机建模问题解决
  8. C++基础细碎知识整理
  9. 融合通信,深化应用—中兴通讯新一代电力调度数据网络解决方案
  10. [解题报告]Feynman