九鼎创展论坛

标题: s3c6410的RTC在linux中的驱动(5) [打印本页]

作者: armeasy    时间: 2014-10-21 16:29
标题: s3c6410的RTC在linux中的驱动(5)

在上一篇中我们在中分析了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上。看一下数据手册就能马上明白,这个问题留给你?







欢迎光临 九鼎创展论坛 (http://bbs.9tripod.com/) Powered by Discuz! X3.2