1、= THIS_MODULE,;static int _init rtc_init(void)/*将RTC注册成平台设备驱动*/return platform_driver_register(&rtc_driver);static void _exit rtc_exit(void)/*注销RTC平台设备驱动*/platform_driver_unregister(&module_init(rtc_init);module_exit(rtc_exit);MODULE_LICENSE(GPL);MODULE_AUTHOR(Huang GangMODULE_DESCRIPTION(My2440 RTC
2、 driver2、 RTC平台驱动结构中探测函数rtc_probe的实现。探测就意味着在系统总线中去检测设备的存在,然后获取设备有用的相关资源信息,以便我们使用这些信息。代码如下:asm/irq.hasm/io.hlinux/rtc.hlinux/ioport.hplat/regs-rtc.h/*定义了一个用来保存RTC的IO端口占用的IO空间和经过虚拟映射后的内存地址*/static struct resource *rtc_mem;static void _iomem *rtc_base;/*定义了两个变量来保存RTC报警中断号和TICK节拍时间中断号,NO_IRQ宏定义在irq.h中*/
3、static int rtc_alarmno = NO_IRQ;static int rtc_tickno = NO_IRQ;/*申明并初始化一个自旋锁rtc_pie_lock,对RTC资源进行互斥访问*/static DEFINE_SPINLOCK(rtc_pie_lock)/*RTC平台驱动探测函数,注意这里为什么要使用一个_devinit,也到rtc_remove实现的地方一起讲*/static int _devinit rtc_probe(struct platform_device *pdev)int ret;struct rtc_device *rtc; /*定义一个RTC设备类,
4、rtc_device定义在rtc.h中*/struct resource *res; /*定义一个资源,用来保存获取的RTC的资源*/*在系统定义的RTC平台设备中获取RTC报警中断号 platform_get_irq定义在platform_device.h中*/rtc_alarmno = platform_get_irq(pdev, 0);if (rtc_alarmno dev, no irq for alarmnreturn -ENOENT;/在系统定义的RTC平台设备中获取TICK节拍时间中断号rtc_tickno = platform_get_irq(pdev, 1);if (rtc_
5、tickno start, res-end - res-start + 1, pdev-name);if (rtc_mem = NULL) failed to reserve memory regionnret = -ENOENT;goto err_nores;/*将RTC的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。 注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/rtc_base = ioremap(res-start + 1);if (rtc_base = NULL) failed ioremap()nret = -EIN
6、VAL;goto err_nomap;/*好了,通过上面的步骤已经将RTC的资源都准备好了,下面就开始使用啦*/*这两个函数开始对RTC寄存器操作,定义都在下面*/rtc_enable(pdev, 1); /*对RTC的实时时钟控制寄存器RTCCON进行操作(功能是初始化或者使能RTC)*/rtc_setfreq(&dev, 1);/*对RTC的节拍时间计数寄存器TICNT的0-6位进行操作,即:节拍时间计数值的设定*/*device_init_wakeup该函数定义在pm_wakeup.h中,定义如下:static inline void device_init_wakeup(struct
7、device *dev, int val)dev-power.can_wakeup = dev-power.should_wakeup = !val;显然这个函数是让驱动支持电源管理的,这里只要知道,can_wakeup为1时表明这个设备可以被唤醒,设备驱动为了支持Linux中的电源管理,有责任调用device_init_wakeup()来初始化can_wakeup,而should_wakeup则是在设备的电源状态发生变化的时候被device_may_wakeup()用来测试,测试它该不该变化,因此can_wakeup表明的是一种能力,而should_wakeup表明的是有了这种能力以后去不去
8、做某件事。好了,我们没有必要深入研究电源管理的内容了,要不就扯远了,电源管理以后再讲*/device_init_wakeup(&/*将RTC注册为RTC设备类,RTC设备类在RTC驱动核心部分中由系统定义好的, 注意rtcops这个参数是一个结构体,该结构体的作用和里面的接口函数实现在第步中。 rtc_device_register函数在rtc.h中定义,在drivers/rtc/class.c中实现*/rtc = rtc_device_register(my2440, &dev, &rtcops, THIS_MODULE);if (IS_ERR(rtc) cannot attach rtcn
9、ret = PTR_ERR(rtc);goto err_nortc;/*设置RTC节拍时间计数寄存器TICNT的节拍时间计数值的用户最大相对值, 这里你可能不理解这句,没关系,等你看到rtc_setfreq函数实现后自然就明白了*/rtc-max_user_freq = 128;/*将RTC设备类的数据传递给系统平台设备。 platform_set_drvdata是定义在platform_device.h的宏,如下: #define platform_set_drvdata(_dev,data)dev_set_drvdata(&(_dev)-dev, (data) 而dev_set_drvda
10、ta又被定义在include/linux/device.h中,如下:static inline void dev_set_drvdata (struct device *dev, void *data)driver_data = data;*/platform_set_drvdata(pdev, rtc);return 0;/以下是上面错误处理的跳转点err_nortc:rtc_enable(pdev, 0);iounmap(rtc_base);err_nomap:release_resource(rtc_mem);err_nores:return ret;/*该函数主要是初始化或者使能RTC
11、,以下RTC的各种寄存器的宏定义在arch/arm/plat-s3c/include/plat/regs-rtc.h中,各寄存器的用途和设置请参考S3C2440数据手册的第十七章实时时钟部分*/static void rtc_enable(struct platform_device *pdev, int flag)unsigned int tmp;/*RTC的实时时钟控制寄存器RTCCON共有4个位,各位的初始值均为0,根据数据手册介绍第0位(即:RCTEN位) 可以控制CPU和RTC之间的所有接口(即RTC使能功能),所以在系统复位后应该将RTCCON寄存器的第0为置为1; 在关闭电源前,
12、又应该将该位清零,以避免无意的写RTC寄存器*/if (!flag) /*当flag=0时(即属于关闭电源前的情况),RTCCON寄存器清零第一位*/tmp = readb(rtc_base + S3C2410_RTCCON); /*读取RTCCON寄存器的值*/* tmp & S3C2410_RTCCON_RTCEN = 0 即屏蔽RTC使能*/writeb(tmp & S3C2410_RTCCON_RTCEN, rtc_base + S3C2410_RTCCON);tmp = readb(rtc_base + S3C2410_TICNT); /*读取TICNT寄存器的值*/ S3C2410
13、_TICNT_ENABLE后第7位为0,即屏蔽节拍时间中断使能*/ S3C2410_TICNT_ENABLE, rtc_base + S3C2410_TICNT); else /*当flag!=0时(即属于系统复位后的情况),使能RTC*/if (readb(rtc_base + S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) = 0)dev_info(&rtc disabled, re-enablingnwriteb(tmp | S3C2410_RTCCON_RTCEN, rtc_base + S3C2410_RTCCON); S3C2410_RTCCON_C
14、NTSEL)removing RTCCON_CNTSELn S3C2410_RTCCON_CNTSEL, rtc_base + S3C2410_RTCCON); S3C2410_RTCCON_CLKRST)removing RTCCON_CLKRSTn S3C2410_RTCCON_CLKRST, rtc_base + S3C2410_RTCCON);/*该函数主要是对RTC的节拍时间计数寄存器TICNT的0-6位进行操作,即:节拍时间计数值的设定*/static int rtc_setfreq(struct device *dev, int freq)is_power_of_2(freq)
15、/*对freq的值进行检查*/return -EINVAL;spin_lock_irq(&rtc_pie_lock); /*获取自旋锁保护临界区资源*/*读取节拍时间计数寄存器TICNT的值*/tmp = readb(rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;/*看数据手册得知,节拍时间计数值的范围是1-127, 还记得在rtc_enable函数中设置的rtc-max_user_freq=128吗?所以这里要减1*/tmp |= (128 / freq) - 1;*将经运算后值写入节拍时间计数寄存器TICNT中,这里主要是改变TICNT的
16、第0-6位的值*/writeb(tmp, rtc_base + S3C2410_TICNT);spin_unlock_irq(&/*释放自旋锁,即解锁*/3、 RTC设备类的操作。在这一步中,才是对RTC硬件的各种寄存器进行操作,代码如下:linux/interrupt.hlinux/bcd.h/*rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体,类似字符设备在驱动中的file_operations对字符设备进行操作的意思。该结构体被定义在rtc.h中,对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等,该结
17、构体内接口函数的实现都在下面*/static const struct rtc_class_ops rtcops = .open= rtc_open,.release = rtc_release,.irq_set_freq= rtc_setfreq, /*在第步中已实现*/.irq_set_state= rtc_setpie,.read_time= rtc_gettime,.set_time= rtc_settime,.read_alarm= rtc_getalarm,.set_alarm= rtc_setalarm,/*RTC设备类打开接口函数*/static int rtc_open(st
18、ruct device *dev)/*这里主要的目的是从系统平台设备中获取RTC设备类的数据,和RTC探测函数rtc_probe中 的platform_set_drvdata(pdev, rtc)的操作刚好相反。这些都定义在platform_device.h中*/struct platform_device *pdev = to_platform_device(dev);struct rtc_device *rtc_dev = platform_get_drvdata(pdev);/*申请RTC报警中断服务,中断号rtc_alarmno在RTC探测函数rtc_probe中已经获取得, 这里使用
19、的是快速中断:IRQF_DISABLED。中断服务程序为:rtc_alarmirq,将RTC设备类rtc_dev做参数传递过去了*/ret = request_irq(rtc_alarmno, rtc_alarmirq, IRQF_DISABLED, my2440-rtc alarm, rtc_dev);if (ret) dev_err(dev, IRQ%d error %dn, rtc_alarmno, ret);/*同上面一样,这里申请的是RTC的TICK节拍时间中断服务,服务程序是:rtc_tickirq*/ret = request_irq(rtc_tickno, rtc_tickir
20、q, IRQF_DISABLED, my2440-rtc tick, rtc_tickno, ret);goto tick_err;tick_err:/*错误处理,注意出现错误后也要释放掉已经申请成功的中断*/free_irq(rtc_alarmno, rtc_dev);/*RTC报警中断服务程序*/static irqreturn_t rtc_alarmirq(int irq, void *argv)struct rtc_device *rdev = argv; /*接收申请中断时传递过来的rtc_dev参数*/*当报警中断到来的时候,去设定RTC中报警的相关信息,具体设定的方法,RTC核心 部分已经在rtc_update_irq接口函数中实现,函数定义实现在interface.c中*/rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);return IRQ_HANDLED;/*RTC的TICK节拍时间中断服务*/static irqreturn_t rtc_tickirq(int irq, void *argv)/*节拍时间中断到来的时候,去设定RTC中节拍时间的相关信息,具体设定的方法,RTC核心rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);return IRQ_HA
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1