s3c6410硬件RTC(实时时钟)的地址链接
s3c6410的RTC在linux中的驱动(1)的地址链接
s3c6410的RTC在linux中的驱动(2)的地址链接
上一篇说到了s3c_rtc_probe函数,但由于太长,而没有说完,这一篇接着上一篇的说。
说完了这个函数就可以回到s3c_rtc_probe函数接着说了,下篇再聊。还记的这句话吗?现在接着聊:
pr_debug("s3c2410_rtc: RTCCON=%02x\n",
readb(s3c_rtc_base + S3C2410_RTCCON));调试信息
s3c_rtc_setfreq(&pdev->dev, 1);RTC设置频率函数,周期公式为:
Then the period of interrupt is as follows:
Period = (n+1)/32768 second (n= tick counter value)
则频率freq=32768/(n+1)
其中n为TICNT寄存器,看下图:
static int s3c_rtc_setfreq(struct device *dev, int freq)
{
spin_lock_irq(&s3c_rtc_pie_lock);
s3c_rtc_set_freq_regs(s3c_rtc_base, freq, s3c_rtc_freq);
void s3c_rtc_set_freq_regs(void __iomem *base, uint freq, uint s3c_freq)这个参数没用到
{
unsigned int tmp;
tmp = readw(base + S3C2410_RTCCON) & (S3C_RTCCON_TICEN | S3C2410_RTCCON_RTCEN );
writew(tmp, base + S3C2410_RTCCON);
s3c_freq = freq;
tmp = (32768 / freq)-1;
writel(tmp, base + S3C2410_TICNT);看上面的图,就是那个寄存器。
}
spin_unlock_irq(&s3c_rtc_pie_lock);
return 0;
}
在此回到s3c_rtc_probe函数中。
device_init_wakeup(&pdev->dev, 1);
/* register RTC and exit */
rtc =rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);
RTC必须要注册到内核中才能使用,这个是在RTC驱动模型中说过的这个函数的作用,如果不清楚,可以回过去查看。现在具体看下这个函数的源码:
/**
* rtc_device_register - register w/ RTC class
* @dev: the device to register
*
* rtc_device_unregister() must be called when the class device is no
* longer needed.
*
* Returns the pointer to the new struct class device.这个函数的返回值是个struct rtc_device结构的指针。
*/当看到一个新函数,如果不明白,就可以先看它的注释。
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
{
先看出传入的参数中的const struct rtc_class_ops结构指针,实参为:
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,
};在Rtc-s3c.c (linux2.6.28\drivers\rtc)文件中定义,这样RTC设备模型就和具体的RTC设备联系起来了。
struct rtc_device *rtc;
int id, err;
if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
err = -ENOMEM;
goto exit;
}分配一个ID号,用来把一个数字与一个指针联系起来
mutex_lock(&idr_lock);
err = idr_get_new(&rtc_idr, NULL, &id);得到一个ID号,不太懂。
mutex_unlock(&idr_lock);
if (err < 0)
goto exit;
id = id & MAX_ID_MASK;
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
if (rtc == NULL) {
err = -ENOMEM;
goto exit_idr;
}分配一个RTC设备结构体struct rtc_device
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class;
rtc->dev.release = rtc_device_release;
初始化struct rtc_device结构体,用户可以设置的最大频率为64,下面是struct rtc_device结构体的原型:
structrtc_device
{
struct device dev;
struct module *owner;
int id;
char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops;
struct mutex ops_lock;
struct cdev char_dev;
unsigned long flags;
unsigned long irq_data;
spinlock_t irq_lock;
wait_queue_head_t irq_queue;
struct fasync_struct *async_queue;
struct rtc_task *irq_task;
spinlock_t irq_task_lock;
int irq_freq;
int max_user_freq;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */
unsigned int oldsecs;
unsigned int irq_active:1;
unsigned int stop_uie_polling:1;
unsigned int uie_task_active:1;
unsigned int uie_timer_active:1;
#endif
};
mutex_init(&rtc->ops_lock);
spin_lock_init(&rtc->irq_lock);
spin_lock_init(&rtc->irq_task_lock);
init_waitqueue_head(&rtc->irq_queue);
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
snprintf(rtc->dev.bus_id, BUS_ID_SIZE, "rtc%d", id);
rtc_dev_prepare(rtc);
/* insertion/removal hooks */
void rtc_dev_prepare(struct rtc_device *rtc)
{
if (!rtc_devt)
return;
if (rtc->id >= RTC_DEV_MAX) {
pr_debug("%s: too many RTC devices\n", rtc->name);
return;
}
rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
INIT_WORK(&rtc->uie_task, rtc_uie_task);
setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
#endif
cdev_init(&rtc->char_dev, &rtc_dev_fops);看到这个函数应该很熟悉,这个是字符设备驱动中常用的函数,可是你有没有觉得少了点什么?设备号在什么时候申请的?而且没有cdev_add函数?
现在回答第一个问题——设备号在什么时候申请的?
大家还记得在第一篇中的/drivers/rtc/class.c这个文件吗?
有如下代码:
subsys_initcall(rtc_init);
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
if (IS_ERR(rtc_class)) {
printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
return PTR_ERR(rtc_class);
}
rtc_class->suspend = rtc_suspend;
rtc_class->resume = rtc_resume;
rtc_dev_init();重点来了。请关注:
void __init rtc_dev_init(void)
{
int err;
err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
if (err < 0)
printk(KERN_ERR "%s: failed to allocate char dev region\n",
__FILE__);
}看到了吗?设备号在这里申请的。
rtc_sysfs_init(rtc_class);
return 0;
}
接着回答第二个问题——而且没有cdev_add函数?这个在下面的某个函数中,会有答案。先揭露一点就在下面这个函数中rtc_dev_add_device。
其中函数实参为,在/drivers/rtc/rtc-dev.c文件中定义。
static const struct file_operations rtc_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
.unlocked_ioctl= rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
};
rtc->char_dev.owner = rtc->owner;
}
rtc_dev_prepare终于完了,现在回到rtc_device_register函数中。美好的时光总是短暂的,又到了一篇结束的时刻,就像灰太狼先生说的那样:“我还会回来的!”下篇见。
err = device_register(&rtc->dev);
if (err)
goto exit_kfree;
rtc_dev_add_device(rtc);
rtc_sysfs_add_device(rtc);
rtc_proc_add_device(rtc);
dev_info(dev, "rtc core: registered %s as %s\n",
rtc->name, rtc->dev.bus_id);
return rtc;
exit_kfree:
kfree(rtc);
exit_idr:
mutex_lock(&idr_lock);
idr_remove(&rtc_idr, id);
mutex_unlock(&idr_lock);
exit:
dev_err(dev, "rtc core: unable to register %s, err = %d\n",
name, err);
return ERR_PTR(err);
}
if (IS_ERR(rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");
ret = PTR_ERR(rtc);
goto err_nortc;
}
rtc->max_user_freq = S3C_MAX_CNT;
/* check rtc time */
for (bcd_loop = S3C2410_RTCSEC ; bcd_loop <= S3C2410_RTCYEAR ; bcd_loop +=0x4)
{
bcd_tmp = readb(s3c_rtc_base + bcd_loop);
if(((bcd_tmp & 0xf) > 0x9) || ((bcd_tmp & 0xf0) > 0x90))
writeb(0, s3c_rtc_base + bcd_loop);
}
platform_set_drvdata(pdev, rtc);
return 0;
err_nortc:
s3c_rtc_enable(pdev, 0);
iounmap(s3c_rtc_base);
err_nomap:
release_resource(s3c_rtc_mem);
err_nores:
return ret;
}