#include <linux/interrupt.h> #include <linux/bcd.h>
/*rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体, 类似字符设备在驱动中的file_operations对字符设备进行操作的意思。该结构体被定义 在rtc.h中,对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等, 该结构体内接口函数的实现都在下面*/ 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(struct device *dev) { int ret;
/*这里主要的目的是从系统平台设备中获取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中已经获取得, 这里使用的是快速中断: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 %d\n", rtc_alarmno, ret); return ret; }
/*同上面一样,这里申请的是RTC的TICK节拍时间中断服务,服务程序是:rtc_tickirq*/ ret = request_irq(rtc_tickno, rtc_tickirq, IRQF_DISABLED, "my2440-rtc tick", rtc_dev); if (ret) { dev_err(dev, "IRQ%d error %d\n", rtc_tickno, ret); goto tick_err; }
return ret;
tick_err:/*错误处理,注意出现错误后也要释放掉已经申请成功的中断*/ free_irq(rtc_alarmno, rtc_dev); return ret; }
/*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) { struct rtc_device *rdev = argv; /*接收申请中断时传递过来的rtc_dev参数*/
/*节拍时间中断到来的时候,去设定RTC中节拍时间的相关信息,具体设定的方法,RTC核心 部分已经在rtc_update_irq接口函数中实现,函数定义实现在interface.c中*/ rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); return IRQ_HANDLED; }
/*RTC设备类关闭接口函数*/ static void rtc_release(struct device *dev) { /*和rtc_open中的作用相同*/ struct platform_device *pdev = to_platform_device(dev); struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
/*请见rtc_setpie接口函数中的解释*/ rtc_setpie(dev, 0);
/*同rtc_open中中断的申请相对应,在那里申请中断,这里就释放中断*/ free_irq(rtc_alarmno, rtc_dev); free_irq(rtc_tickno, rtc_dev); }
/*该函数主要是对RTC的节拍时间计数寄存器TICNT的第7位进行操作,即:节拍时间计数的使能功能*/ static int rtc_setpie(struct device *dev, int flag) { unsigned int tmp;
spin_lock_irq(&rtc_pie_lock);/*获取自旋锁保护临界区资源*/
/*读取节拍时间计数寄存器TICNT的值*/ tmp = readb(rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
if (flag) { tmp |= S3C2410_TICNT_ENABLE; /*根据标志flag的值来判断是要使能还是要禁止*/ }
/*将经运算后值写入节拍时间计数寄存器TICNT中,这里主要是改变TICNT的第7位的值*/ writeb(tmp, rtc_base + S3C2410_TICNT);
spin_unlock_irq(&rtc_pie_lock);/*释放自旋锁,即解锁*/
return 0; }
/*读取RTC中BCD数中的:分、时、日期、月、年、秒*/ static int rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { unsigned int have_retried = 0;
retry_get_time: rtc_tm->tm_min = readb(rtc_base + S3C2410_RTCMIN); /*读BCD分寄存器RTCMIN*/ rtc_tm->tm_hour = readb(rtc_base + S3C2410_RTCHOUR); /*读BCD时寄存器RTCHOUR*/ rtc_tm->tm_mday = readb(rtc_base + S3C2410_RTCDATE); /*读BCD日期寄存器RTCDATE*/ rtc_tm->tm_mon = readb(rtc_base + S3C2410_RTCMON); /*读BCD月寄存器RTCMON*/ rtc_tm->tm_year = readb(rtc_base + S3C2410_RTCYEAR); /*读BCD年寄存器RTCYEAR*/ rtc_tm->tm_sec = readb(rtc_base + S3C2410_RTCSEC); /*读BCD秒寄存器RTCSEC*/
/*我们知道时间是以60为一个周期的,当时、分、秒达到60后,他们的上一级会加1,而自身又从0开始计数 上面我们最后读的秒,如果读出来的秒刚好是0,那么前面读的分、时等就是上一分钟的,结果就少了一分钟, 所以就要重新读取*/ if (rtc_tm->tm_sec == 0 && !have_retried) { have_retried = 1; goto retry_get_time; }
/*将上面读取的时间日期值保存到RTC核心定义的时间结构体中,该结构体定义在rtc.h中, 这里的bcd2bin主要是编译器对返回值相同时进行优化处理,定义在bcd.h中*/ rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
/*这里为什么要加100年和减1月呢,我们查看数据手册得知原来是为了区别1900年和2000年闰年的因素, 1900年不是闰年而2000年是闰年。这时你或许会问那怎么不考虑1800年或2100年啊?原因很简单,因为 我们的RTC时钟只支持100年的时间范围,呵呵!!*/ rtc_tm->tm_year += 100; rtc_tm->tm_mon -= 1;
return 0; }
/*和上面的rtc_gettime功能相反,将更改后的分、时、日期、月、年、秒写入RTC中BCD数中*/ static int rtc_settime(struct device *dev, struct rtc_time *tm) { /*这里减100年很清楚了吧,因为上面为了区别1900年和2000年时加了100年*/ int year = tm->tm_year - 100;
/*RTC时钟只支持100年的时间范围*/ if (year < 0 || year >= 100) { dev_err(dev, "rtc only supports 100 years\n"); return -EINVAL; }
/*将上面保存到RTC核心定义的时间结构体中的时间日期值写入对应的寄存器中*/ writeb(bin2bcd(tm->tm_sec), rtc_base + S3C2410_RTCSEC); writeb(bin2bcd(tm->tm_min), rtc_base + S3C2410_RTCMIN); writeb(bin2bcd(tm->tm_hour), rtc_base + S3C2410_RTCHOUR); writeb(bin2bcd(tm->tm_mday), rtc_base + S3C2410_RTCDATE); writeb(bin2bcd(tm->tm_mon + 1), rtc_base + S3C2410_RTCMON); /*这里加1月也明白了吧*/ writeb(bin2bcd(year), rtc_base + S3C2410_RTCYEAR);
return 0; }
/*读取RTC中报警各寄存器的:秒、分、时、月、日期、年的值,保存各值到rtc_time结构体中*/ static int rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) { unsigned int alm_en; struct rtc_time *alm_tm = &alrm->time;
alm_tm->tm_sec = readb(rtc_base + S3C2410_ALMSEC); alm_tm->tm_min = readb(rtc_base + S3C2410_ALMMIN); alm_tm->tm_hour = readb(rtc_base + S3C2410_ALMHOUR); alm_tm->tm_mon = readb(rtc_base + S3C2410_ALMMON); alm_tm->tm_mday = readb(rtc_base + S3C2410_ALMDATE); alm_tm->tm_year = readb(rtc_base + S3C2410_ALMYEAR);
/*获取RTC报警控制寄存器RTCALM的值*/ alm_en = readb(rtc_base + S3C2410_RTCALM);
/*判断RTCALM值的第6位,来设置RTC的全局报警使能状态到RTC核心定义的报警状态结构体rtc_wkalrm中*/ alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
/*判断如果RTCALM值的第0位的值(秒报警使能)为1时,就设置报警秒的值到rtc_time结构体中*/ if (alm_en & S3C2410_RTCALM_SECEN) alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); else alm_tm->tm_sec = 0xff;
/*判断如果RTCALM值的第1位的值(分报警使能)为1时,就设置报警分的值到rtc_time结构体中*/ if (alm_en & S3C2410_RTCALM_MINEN) alm_tm->tm_min = bcd2bin(alm_tm->tm_min); else alm_tm->tm_min = 0xff;
/*判断如果RTCALM值的第2位的值(时报警使能)为1时,就设置报警小时的值到rtc_time结构体中*/ if (alm_en & S3C2410_RTCALM_HOUREN) alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); else alm_tm->tm_hour = 0xff;
/*判断如果RTCALM值的第3位的值(日期报警使能)为1时,就设置报警日期的值到rtc_time结构体中*/ if (alm_en & S3C2410_RTCALM_DAYEN) alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); else alm_tm->tm_mday = 0xff;
/*判断如果RTCALM值的第4位的值(月报警使能)为1时,就设置报警月的值到rtc_time结构体中*/ if (alm_en & S3C2410_RTCALM_MONEN) { alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); alm_tm->tm_mon -= 1; /*这里为什么要递减1,我不是很明白???????*/ } else { alm_tm->tm_mon = 0xff; }
/*判断如果RTCALM值的第5位的值(年报警使能)为1时,就设置报警年的值到rtc_time结构体中*/ if (alm_en & S3C2410_RTCALM_YEAREN) alm_tm->tm_year = bcd2bin(alm_tm->tm_year); else alm_tm->tm_year = 0xffff;
return 0; }
/*把上面保存到rtc_time结构体中各值写入RTC中报警各寄存器中*/ static int rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { unsigned int alrm_en; struct rtc_time *tm = &alrm->time;
/*读取RTC报警控制寄存器RTCALM的第6位,把全局报警使能的状态保存到alrm_en变量中*/ alrm_en = readb(rtc_base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
/*把RTC报警控制寄存器RTCALM的值设为0,即将全局报警使能和其他报警使能全部关闭*/ writeb(0x00, rtc_base + S3C2410_RTCALM);
if (tm->tm_sec < 60 && tm->tm_sec >= 0) { /*上面的alrm_en值只记录了RTCALM的第6位(全局报警使能的状态),这里再加上第0位(秒报警使能的状态), 然后将前面保存在rtc_time中报警秒的值写入报警秒数据寄存器ALMSEC中*/ alrm_en |= S3C2410_RTCALM_SECEN; writeb(bin2bcd(tm->tm_sec), rtc_base + S3C2410_ALMSEC); }
if (tm->tm_min < 60 && tm->tm_min >= 0) { /*加上第1位(分报警使能的状态), 然后将前面保存在rtc_time中报警分的值写入报警分钟数据寄存器ALMMIN中*/ alrm_en |= S3C2410_RTCALM_MINEN; writeb(bin2bcd(tm->tm_min), rtc_base + S3C2410_ALMMIN); }
if (tm->tm_hour < 24 && tm->tm_hour >= 0) { /*加上第2位(时报警使能的状态), 然后将前面保存在rtc_time中报警小时的值写入报警小时数据寄存器ALMHOUR中*/ alrm_en |= S3C2410_RTCALM_HOUREN; writeb(bin2bcd(tm->tm_hour), rtc_base + S3C2410_ALMHOUR); }
/*把alrm_en修改过后的值重新写入RTC报警控制寄存器RTCALM中*/ writeb(alrm_en, rtc_base + S3C2410_RTCALM);
/*请看下面rtc_setaie函数实现部分*/ rtc_setaie(alrm->enabled);
/*根据全局报警使能的状态来决定是唤醒RTC报警中断还是睡眠RTC报警中断*/ if (alrm->enabled) enable_irq_wake(rtc_alarmno); else disable_irq_wake(rtc_alarmno);
return 0; }
/*这里主要还是控制RTC报警控制寄存器RTCALM的第6位(全局报警使能状态)*/ static void rtc_setaie(int flag) { unsigned int tmp;
tmp = readb(rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
if (flag)/*根据标志flag来使能或禁止全局报警*/ tmp |= S3C2410_RTCALM_ALMEN;
writeb(tmp, rtc_base + S3C2410_RTCALM); }
|
文章评论(0条评论)
登录后参与讨论