IMX6Q RTC驱动分析
对于在工作中学习驱动的,讲究的是先使用,再理解。好吧,我们来看看板子里是如何注册的?
在板文件里,它的注册函数是这样的:
imx6q_add_imx_snvs_rtc()
好吧,让我们追踪下去:
![](https://common.cnblogs.com/images/copycode.gif)
1 extern const struct imx_snvs_rtc_data imx6q_imx_snvs_rtc_data __initconst;
2 #define imx6q_add_imx_snvs_rtc() \
3 imx_add_snvs_rtc(&imx6q_imx_snvs_rtc_data)
4
5 #define imx_snvs_rtc_data_entry_single(soc) \
6 { \
7 .iobase = soc ## _SNVS_BASE_ADDR, \
8 .irq = soc ## _INT_SNVS, \
9 }
10
11 #ifdef CONFIG_SOC_IMX6Q
12 const struct imx_snvs_rtc_data imx6q_imx_snvs_rtc_data __initconst =
13 imx_snvs_rtc_data_entry_single(MX6Q);
14 #endif /* ifdef CONFIG_SOC_IMX6Q */
15
16 struct platform_device *__init imx_add_snvs_rtc(
17 const struct imx_snvs_rtc_data *data)
18 {
19 struct resource res[] = {
20 {
21 .start = data->iobase,
22 .end = data->iobase + SZ_4K - 1,
23 .flags = IORESOURCE_MEM,
24 }, {
25 .start = data->irq,
26 .end = data->irq,
27 .flags = IORESOURCE_IRQ,
28 },
29 };
30
31 return imx_add_platform_device("snvs_rtc", 0,
32 res, ARRAY_SIZE(res), NULL, 0);
33 }
![](https://common.cnblogs.com/images/copycode.gif)
最终调用imx_add_platform_device将rtc注册进去。
那么在驱动端,其代码是如何的呢?分析下主要的部分:
![](https://common.cnblogs.com/images/copycode.gif)
1 /*!
2 * The RTC driver structure
3 */
4 static struct rtc_class_ops snvs_rtc_ops = {
5 .open = snvs_rtc_open,
6 .release = snvs_rtc_release,
7 .read_time = snvs_rtc_read_time,
8 .set_time = snvs_rtc_set_time,
9 .read_alarm = snvs_rtc_read_alarm,
10 .set_alarm = snvs_rtc_set_alarm,
11 .proc = snvs_rtc_proc,
12 .ioctl = snvs_rtc_ioctl,
13 .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
14 };
![](https://common.cnblogs.com/images/copycode.gif)
rtc_class_ops里面的函数实例需要我们去完成。
定义一个platform_driver结构体:
![](https://common.cnblogs.com/images/copycode.gif)
1 /*!
2 * Contains pointers to the power management callback functions.
3 */
4 static struct platform_driver snvs_rtc_driver = {
5 .driver = {
6 .name = "snvs_rtc", //注意这个要和device端名字一样
7 },
8 .probe = snvs_rtc_probe,
9 .remove = __exit_p(snvs_rtc_remove),
10 .suspend = snvs_rtc_suspend,
11 .resume = snvs_rtc_resume,
12 };
![](https://common.cnblogs.com/images/copycode.gif)
在probe函数中完成rtc_class_ops的注册。好吧,详细的分析下probe:
![](https://common.cnblogs.com/images/copycode.gif)
1 /*! SNVS RTC Power management control */
2 static int snvs_rtc_probe(struct platform_device *pdev)
3 {
4 struct timespec tv;
5 struct resource *res;
6 struct rtc_device *rtc;
7 struct rtc_drv_data *pdata = NULL;
8 void __iomem *ioaddr;
9 u32 lp_cr;
10 int ret = 0;
11
12 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取资源,即注册在device里面的内存首末地址
13 if (!res)
14 return -ENODEV;
15
16 pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
17 if (!pdata)
18 return -ENOMEM;
19
20 pdata->baseaddr = res->start;
21 pdata->ioaddr = ioremap(pdata->baseaddr, 0xC00); //分配IO内存空间
22 ioaddr = pdata->ioaddr;
23 pdata->irq = platform_get_irq(pdev, 0); //获取中断号
24 platform_set_drvdata(pdev, pdata); //将rtc_drv_data 赋给platform_device的私有数据
25
26
27 /* Added to support sysfs wakealarm attribute */
28 pdev->dev.power.can_wakeup = 1; //
29
30 /* initialize glitch detect */ //在分配IO内存后,就可以对其寄存器进行硬件的一些初始化工作。
31 __raw_writel(SNVS_LPPGDR_INIT, ioaddr + SNVS_LPPGDR);
32 udelay(100);
33
34 /* clear lp interrupt status */
35 __raw_writel(0xFFFFFFFF, ioaddr + SNVS_LPSR);
36
37 /* Enable RTC */
38 lp_cr = __raw_readl(ioaddr + SNVS_LPCR);
39 if ((lp_cr & SNVS_LPCR_SRTC_ENV) == 0)
40 __raw_writel(lp_cr | SNVS_LPCR_SRTC_ENV, ioaddr + SNVS_LPCR);
41
42 udelay(100);
43
44 __raw_writel(0xFFFFFFFF, ioaddr + SNVS_LPSR);
45 udelay(100);
46
47 if (pdata->irq >= 0) { //设置中断函数
48 if (request_irq(pdata->irq, snvs_rtc_interrupt, IRQF_SHARED,
49 pdev->name, pdev) < 0) {
50 dev_warn(&pdev->dev, "interrupt not available.\n");
51 pdata->irq = -1;
52 } else {
53 disable_irq(pdata->irq);
54 pdata->irq_enable = false;
55 }
56 }
57
58 rtc = rtc_device_register(pdev->name, &pdev->dev, //重要!!!RTC设备注册!!!
59 &snvs_rtc_ops, THIS_MODULE);
60 if (IS_ERR(rtc)) {
61 ret = PTR_ERR(rtc);
62 goto err_out;
63 }
64
65 pdata->rtc = rtc; //将注册上的rtc,赋给驱动!
66
67 tv.tv_nsec = 0;
68 tv.tv_sec = rtc_read_lp_counter(ioaddr + SNVS_LPSRTCMR);
69
70 /* Remove can_wakeup flag to add common power wakeup interface */
71 pdev->dev.power.can_wakeup = 0;
72
73 /* By default, devices should wakeup if they can */
74 /* So snvs is set as "should wakeup" as it can */
75 device_init_wakeup(&pdev->dev, 1);
76
77 return ret;
78
79 err_out:
80 iounmap(ioaddr);
81 if (pdata->irq >= 0)
82 free_irq(pdata->irq, pdev);
83 kfree(pdata);
84 return ret;
85 }
![](https://common.cnblogs.com/images/copycode.gif)
OK,上面加了些注释,基本上的流程是这样:先获取device的资源,mem或irq,然后映射内存空间,对硬件进行初始化。注册irq,设置irq服务函数。
接着注册rtc设备,将rtc_class_ops注册到设备里。将rtc设备赋值给rtc驱动。
remove函数
![](https://common.cnblogs.com/images/copycode.gif)
1 static int __exit snvs_rtc_remove(struct platform_device *pdev)
2 {
3 struct rtc_drv_data *pdata = platform_get_drvdata(pdev);
4 rtc_device_unregister(pdata->rtc);
5 if (pdata->irq >= 0)
6 free_irq(pdata->irq, pdev);
7
8 kfree(pdata);
9 return 0;
10 }
![](https://common.cnblogs.com/images/copycode.gif)
获取设备里面驱动数据,然后将驱动里面rtc注销。注销irq。释放驱动数据空间。
驱动里面的init函数和exit函数就很简单了,针对platform_driver进行注册和注销。
![](https://common.cnblogs.com/images/copycode.gif)
1 /*!
2 * Contains pointers to the power management callback functions.
3 */
4 static struct platform_driver snvs_rtc_driver = {
5 .driver = {
6 .name = "snvs_rtc",
7 },
8 .probe = snvs_rtc_probe,
9 .remove = __exit_p(snvs_rtc_remove),
10 .suspend = snvs_rtc_suspend,
11 .resume = snvs_rtc_resume,
12 };
13
14 /*!
15 * This function creates the /proc/driver/rtc file and registers the device RTC
16 * in the /dev/misc directory. It also reads the RTC value from external source
17 * and setup the internal RTC properly.
18 *
19 * @return -1 if RTC is failed to initialize; 0 is successful.
20 */
21 static int __init snvs_rtc_init(void)
22 {
23 return platform_driver_register(&snvs_rtc_driver);
24 }
25
26 /*!
27 * This function removes the /proc/driver/rtc file and un-registers the
28 * device RTC from the /dev/misc directory.
29 */
30 static void __exit snvs_rtc_exit(void)
31 {
32 platform_driver_unregister(&snvs_rtc_driver);
33
34 }
![](https://common.cnblogs.com/images/copycode.gif)
好吧,这是IMX6Q自带的rtc,它有缺点就是耗电流较大!!!所以freescale的工程师也不建议使用!!!纽扣电池扛不了多久!
我们选用了一个intersil公司的isl1208作为RTC芯片。它的驱动,在发行的linux版本上都有,在config文件中添加就行。如何实现rtc的功能呢???
还是先看device端,因为isl1208是isl1208是I2C接口,所以我们只需在板级端,注册其I2C的信息即可,这个信息包括isl1208的地址,以及驱动名。这两个信息都要注意!
其地址:1101111X,当x为0时是写操作,为1是读操作。在i2c_board_info的注册时是不要后面的x位的,高位右移一位。其地址为0x6f;
驱动名:device的驱动名要和i2c_device_id里面的name一致,而不是i2c_driver里面driver的name一致。
好吧,看device端的设置吧:
![](https://common.cnblogs.com/images/copycode.gif)
1 static struct imxi2c_platform_data mx6q_sabresd_i2c_data = {
2 .bitrate = 100000,
3 };
4
5 static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
6 {
7 I2C_BOARD_INFO("wm89**", 0x1a),
8 },
9 {
10 I2C_BOARD_INFO("ov564x", 0x3c),
11 .platform_data = (void *)&camera_data,
12 },
13 {
14 I2C_BOARD_INFO("mma8451", 0x1c),
15 .platform_data = (void *)&mma8451_position,
16 },
17 {
18 I2C_BOARD_INFO("isl1208", 0x6f),
19 },
20
21 };
![](https://common.cnblogs.com/images/copycode.gif)
在board_init函数里面,加入:
1 imx6q_add_imx_i2c(0, &mx6q_sabresd_i2c_data);
2 i2c_register_board_info(0, mxc_i2c0_board_info, ARRAY_SIZE(mxc_i2c0_board_info));
这样device端就完成了注册工作。
driver端呢?
跟上面的差不多,要完成一个rtc_device_register将isl1208_rtc_ops这些操作硬件的函数注册进去。
代码发行的版本都有,就不贴了。
罗里吧嗦那么多,其实就是提了些代码的流程。对代码的理解还不够!要努力啊!
IMX6Q RTC驱动分析的更多相关文章
- Qcom平台RTC驱动分析
相关文件list: pm8998.dtsi ---RTC dts配置 qpnp-rtc.c ---qcom RTC驱动 class.c ---RTC相关class interface.c ---相关R ...
- linux RTC 驱动模型分析【转】
转自:http://blog.csdn.net/yaozhenguo2006/article/details/6824970 RTC(real time clock)实时时钟,主要作用是给Linux系 ...
- Linux RTC驱动模型分析之rtc-sysfs.c【转】
转自:https://blog.csdn.net/longwang155069/article/details/52353408 版权声明:本文为博主原创文章,未经博主允许不得转载. https:// ...
- 电源管理之pmu驱动分析
电源管理芯片可以为多设备供电,且这些设备电压电流有所不同.为这些设备提供的稳压器代码模型即为regulator. 说白了regulator就是稳压器,它提供电源供给.简单的可以gpio操作,高电平开电 ...
- 二十一、RTC驱动
一.RTC设备驱动分析 内核的rtc驱动位于内核drivers/rtc目录下,里面包含各个平台的RTC驱动.读者可在此目录下任意选择一个单板驱动文件进行分析,我选择的是rtc-davinci.c文件. ...
- 二十、网卡框架分析、虚拟网卡驱动和DM9621驱动分析
一.网络设备驱动的结构 网卡设备不同于字符设备和块设备, 网络设备并不对应于/dev目录下的文件,它存放在/sys/class/net目录下. Linux系统对网络设备驱动定义了四个层次: 1. 网络 ...
- [tty与uart]3.tty驱动分析
转自:http://www.wowotech.net/linux_kenrel/183.html 目录: 1 首先分析设备驱动的注册 1.1 uart_register_driver分析 1.2 tt ...
- linux的串口驱动分析
1.串口驱动中的数据结构 • UART驱动程序结构:struct uart_driver 驱动 • UART端口结构: struct uart_port 串口 • UART相关操作函数结构: st ...
- linux内核SPI总线驱动分析(一)(转)
linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析 (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...
随机推荐
- Eclipse中关于JRE System Library、Web App Libraries的疑惑
当我们在Eclipse中建立java的web工程时,会产生JRE System Library和Referenced Libraries,Web App Libraries不生成,下面会 简要说明一下 ...
- Eclipse 运行配置(Run Configuration)
Eclipse 运行配置(Run Configuration) 创建和使用 Eclipse 运行配置 在运行配置(Run Configuration)对话框中可以创建多个运行配置.每个配置可以在应用中 ...
- c#文件路径的获取
string fullPath = @"\WebSite1\Default.aspx"; string filename = System.IO.Path.GetFileName( ...
- posix正则表达式说明
转载自:http://baiy.cn/utils/_regex_doc/index.htm 正则表达式说明 简介 大体来讲,正则表达式的文法分为3种标准:BRE.ERE 和 ARE.其中 BER 和 ...
- hadoop2.2集群部署教程连接
完全分布式部署:http://blog.csdn.net/licongcong_0224/article/details/12972889 伪分布式部署:http://www.kankanews.co ...
- SlidingMenu官方实例分析4——AttachExample
AttachExample这个类没有继承BaseActivity,而是FragmentActivity,写到这好像感悟到了 为什么官方现在都推荐使用Fragment而不是Activity,因为Frag ...
- phpcs,phpmd,phan安装部署,phpstorm配置phpunit
git参考地址:https://github.com/YunhanTech/overview/blob/master/php/learn-road.md phpcs 安装 composer globa ...
- Linux命令之split
split用来将大文件分割成小文件.有时文件越来越大,传送这些文件时,首先将其分割可能更容易. 使用vi或其他工具诸如sort时,如果文件对于工作缓冲区太大,也会存在一些问题. 因此有时没有选择余地, ...
- jQuery插件-json2.js
from:http://blog.csdn.net/gjb724332682/article/details/46682743 前言 json2.js是一个json插件,下载地址:https://gi ...
- phpstorm将多个int数字拼接成字符串
场景:将程序输出的多个int数字拼成以','分隔的字符串 数据为 8680 24399 37619 45425 49635 139334 386918 429498 461616 523384 561 ...