#include
#include
/*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);
}
|