在上一篇中我们在中分析了RTC驱动的注册和注销,重点讲了平台设备驱动的probe函数,最后引出了这篇我们要讲解的内容,那就是下面这个结构体中的一些函数。
static const struct rtc_class_ops s3c_rtcops = {
.open= s3c_rtc_open,
.release= s3c_rtc_release,
.ioctl= s3c_rtc_ioctl,
.read_time= s3c_rtc_gettime,
.set_time= s3c_rtc_settime,
.read_alarm= s3c_rtc_getalarm,
.set_alarm= s3c_rtc_setalarm,
.irq_set_freq= s3c_rtc_setfreq,
.irq_set_state= s3c_rtc_setpie,
.proc = s3c_rtc_proc,
};
1、先来看open函数,此函数用来打开一个设备,再该函数中可以对设备进行初始化。s3c_rtc_open的主要作用就是申请了两个中断,函数源码如下:
static int s3c_rtc_open(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);从device结构体得到平台设备platform_device
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);从pdev->dev的私有数据中得到rtc_device
int ret;
ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);申请一个报警中断,将中断函数设为s3c_rtc_alarmirq,并传递rtc_dev作为参数。对于request_irq函数大家应该很熟悉,如下所示:
/**
*request_irq - allocate an interrupt line
*@irq: Interrupt line to allocate
*@handler: Function to be called when the IRQ occurs
*@irqflags: Interrupt type flags
*@devname: An ascii name for the claiming device
*@dev_id: A cookie passed back to the handler function
* Flags:
*
*IRQF_SHAREDInterrupt is shared
*IRQF_DISABLEDDisable local interrupts while processing
*IRQF_SAMPLE_RANDOMThe interrupt can be used for entropy
*IRQF_TRIGGER_*Specify active edge(s) or level
*
*int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
return ret;
}
ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);和上面一样,申请一个TICK中断,将中断函数设为 s3c_rtc_tickirq,并传递rtc_dev作为参数。
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
goto tick_err;
}
return ret;
tick_err:
free_irq(s3c_rtc_alarmno, rtc_dev);
return ret;
}
2、接着看release函数,用户空间调用close时调用。s3c_rtc_release函数主要是释放两个中断,源码如下:
static void s3c_rtc_release(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
/* do not clear AIE here, it may be needed for wake */
s3c_rtc_setpie(dev, 0);此函数用来设置是否允许TICK中断,第二个参数enabled等于1,表示允许,等于0,表示不允许。源码如下:
static int s3c_rtc_setpie(struct device *dev, int enabled)
{
pr_debug("%s: pie=%d\n", __func__, enabled);
spin_lock_irq(&s3c_rtc_pie_lock);
s3c_rtc_set_pie(s3c_rtc_base,enabled);
void s3c_rtc_set_pie(void __iomem *base, uint to)
{
unsigned int tmp;
tmp = readw(base + S3C2410_RTCCON) & ~S3C_RTCCON_TICEN;先读出RTCCON寄存器。
if (to)根据to的值,决定回写时的值。看下图:
tmp |= S3C_RTCCON_TICEN;
writew(tmp, base + S3C2410_RTCCON);
}
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
}
free_irq(s3c_rtc_alarmno, rtc_dev);释放中断s3c_rtc_alarmno
free_irq(s3c_rtc_tickno, rtc_dev);
/**
*free_irq - free an interrupt
*@irq: Interrupt line to free
*@dev_id: Device identity to free
void free_irq(unsigned int irq, void *dev_id)
}
3、接着看s3c_rtc_gettime函数,这里牵扯到一个结构体,如下所示:
/*
* The struct used to pass data via the following ioctl. Similar to the
* struct tm in <time.h>, but it needs to be here so that the kernel
* source is self contained, allowing cross-compiles, etc. etc.
*/
struct rtc_time {
int tm_sec;秒
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;RTC实时时钟中没用
int tm_yday;同上
int tm_isdst;同上
};
源码如下:
/* Time read/write */
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
unsigned int have_retried = 0;定义了一个重试变量,如果该变量为0,表示可能重新读取寄存器的值
void __iomem *base = s3c_rtc_base;
retry_get_time:
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
/* the only way to work out wether the system was mid-update
* when we read it is to check the second counter, and ifit
* is zero, then we re-try the entire read这句在前面s3c6410硬件RTC(实时时钟)这篇博客中有讲述,可以回头去查看。如果秒寄存器为0,要重读。
*/
if (rtc_tm->tm_sec == 0 && !have_retried) {
have_retried = 1;
goto retry_get_time;
}
pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
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);
这个函数进行BCD到二进制的转化
unsigned bcd2bin(unsigned char val)
{
return (val & 0x0f) + (val >> 4) * 10;
}
EXPORT_SYMBOL(bcd2bin);
对应的二进制转化成BCD
unsigned char bin2bcd(unsigned val)
{
return ((val / 10) << 4) + val % 10;
}
EXPORT_SYMBOL(bin2bcd);
rtc_tm->tm_year += 100;
rtc_tm->tm_mon -= 1;
return 0;
}
注:存储器中存放的是从1900年开始的时间。
4、像s3c_rtc_getalarm和s3c_rtc_setalarm都和上面很类似,就不再所说了。下面这几个函数前两个也说过了,就剩最后一个了。
.irq_set_freq= s3c_rtc_setfreq,
.irq_set_state= s3c_rtc_setpie,
.proc = s3c_rtc_proc,
现在就来说s3c_rtc_proc函数。大家都知道,在proc文件系统中,可以读取proc文件来判断RTC实时时钟是否支持TICK中断。在读取proc文件中,会调用此函数。源码如下:
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
seq_printf(seq, "periodic_IRQ\t: %s\n",
(ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
return 0;
}
看出什么问题了吗?问题出在S3C2410_TICNT和S3C2410_TICNT_ENABLE上。看一下数据手册就能马上明白,这个问题留给你?