对于在工作中学习驱动的,讲究的是先使用,再理解。好吧,我们来看看板子里是如何注册的?

在板文件里,它的注册函数是这样的:

imx6q_add_imx_snvs_rtc()

好吧,让我们追踪下去:

 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 }

最终调用imx_add_platform_device将rtc注册进去。

那么在驱动端,其代码是如何的呢?分析下主要的部分:

 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 };

rtc_class_ops里面的函数实例需要我们去完成。

定义一个platform_driver结构体:

 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 };

在probe函数中完成rtc_class_ops的注册。好吧,详细的分析下probe:

 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 }

OK,上面加了些注释,基本上的流程是这样:先获取device的资源,mem或irq,然后映射内存空间,对硬件进行初始化。注册irq,设置irq服务函数。
接着注册rtc设备,将rtc_class_ops注册到设备里。将rtc设备赋值给rtc驱动。

remove函数

 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 }

获取设备里面驱动数据,然后将驱动里面rtc注销。注销irq。释放驱动数据空间。

驱动里面的init函数和exit函数就很简单了,针对platform_driver进行注册和注销。

 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 }

好吧,这是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端的设置吧:

 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 };

在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这些操作硬件的函数注册进去。

代码发行的版本都有,就不贴了。

罗里吧嗦那么多,其实就是提了些代码的流程。对代码的理解还不够!要努力啊!

活着,就要自立自强~fighting~

IMX6Q RTC驱动分析的更多相关文章

  1. Qcom平台RTC驱动分析

    相关文件list: pm8998.dtsi ---RTC dts配置 qpnp-rtc.c ---qcom RTC驱动 class.c ---RTC相关class interface.c ---相关R ...

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

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

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

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

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

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

  5. 二十一、RTC驱动

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

  6. 二十、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

    一.网络设备驱动的结构 网卡设备不同于字符设备和块设备, 网络设备并不对应于/dev目录下的文件,它存放在/sys/class/net目录下. Linux系统对网络设备驱动定义了四个层次: 1. 网络 ...

  7. [tty与uart]3.tty驱动分析

    转自:http://www.wowotech.net/linux_kenrel/183.html 目录: 1 首先分析设备驱动的注册 1.1 uart_register_driver分析 1.2 tt ...

  8. linux的串口驱动分析

    1.串口驱动中的数据结构 • UART驱动程序结构:struct uart_driver  驱动 • UART端口结构: struct uart_port  串口 • UART相关操作函数结构: st ...

  9. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

随机推荐

  1. Eclipse中关于JRE System Library、Web App Libraries的疑惑

    当我们在Eclipse中建立java的web工程时,会产生JRE System Library和Referenced Libraries,Web App Libraries不生成,下面会 简要说明一下 ...

  2. Eclipse 运行配置(Run Configuration)

    Eclipse 运行配置(Run Configuration) 创建和使用 Eclipse 运行配置 在运行配置(Run Configuration)对话框中可以创建多个运行配置.每个配置可以在应用中 ...

  3. c#文件路径的获取

    string fullPath = @"\WebSite1\Default.aspx"; string filename = System.IO.Path.GetFileName( ...

  4. posix正则表达式说明

    转载自:http://baiy.cn/utils/_regex_doc/index.htm 正则表达式说明 简介 大体来讲,正则表达式的文法分为3种标准:BRE.ERE 和 ARE.其中 BER 和 ...

  5. hadoop2.2集群部署教程连接

    完全分布式部署:http://blog.csdn.net/licongcong_0224/article/details/12972889 伪分布式部署:http://www.kankanews.co ...

  6. SlidingMenu官方实例分析4——AttachExample

    AttachExample这个类没有继承BaseActivity,而是FragmentActivity,写到这好像感悟到了 为什么官方现在都推荐使用Fragment而不是Activity,因为Frag ...

  7. phpcs,phpmd,phan安装部署,phpstorm配置phpunit

    git参考地址:https://github.com/YunhanTech/overview/blob/master/php/learn-road.md phpcs 安装 composer globa ...

  8. Linux命令之split

    split用来将大文件分割成小文件.有时文件越来越大,传送这些文件时,首先将其分割可能更容易. 使用vi或其他工具诸如sort时,如果文件对于工作缓冲区太大,也会存在一些问题. 因此有时没有选择余地, ...

  9. jQuery插件-json2.js

    from:http://blog.csdn.net/gjb724332682/article/details/46682743 前言 json2.js是一个json插件,下载地址:https://gi ...

  10. phpstorm将多个int数字拼接成字符串

    场景:将程序输出的多个int数字拼成以','分隔的字符串 数据为 8680 24399 37619 45425 49635 139334 386918 429498 461616 523384 561 ...