九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 2868|回复: 0
打印 上一主题 下一主题

s3c6410的RTC在linux中的驱动(3)

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-21 16:28:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

上一篇说到了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;
}

而且没有

其中函数实参为,在/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;
}


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-11-23 05:46 , Processed in 0.019182 second(s), 19 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表