一、 一个platform 驱动的实例RTC
先找到6410的RTC驱动文件:rtc-s3c.c,直接转到文件尾部:
module_init(s3c_rtc_init)->s3c_rtc_init->platform_driver_register(&s3c_rtc_driver)
RTC驱动果然是采用的PLARFORM驱动的形式,很自然地,我们去看s3c_rtc_driver的定义,至于platform_driver_register如何去注册,真没必要看。
static struct platform_driver s3c_rtc_driver =
{
.probe =
s3c_rtc_probe,
.remove =
__devexit_p(s3c_rtc_remove),
.suspend =
s3c_rtc_suspend,
.resume =
s3c_rtc_resume,
.id_table =
s3c_rtc_driver_ids,
.driver =
{
.name =
"s3c-rtc",
.owner =
THIS_MODULE,
},
};
在进入PROBE函数之前,我们要去看看RTC device的定义:
struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource),
.resource =
s3c_rtc_resource,
};
似乎在s3c_device_rtc
中定义的名字和s3c_rtc_driver中的名字不一样呀,你可以看看id_table 在这里定义了该DRIVER支持的设备列表,里边的名字是和DEVICE的名字是一致的。
然我们看看s3c_rtc_resource:
static struct resource s3c_rtc_resource[] = {
[0] = {
.start =
S3C_PA_RTC,
//RTC寄存器的物理地址S3C64XX_PA_RTC,请注意要在MARH-MINI64XX=目录下的MAP.H
.end =
S3C_PA_RTC + 0xff,
.flags =
IORESOURCE_MEM,
},
[1] = {
.start =
IRQ_RTC_ALARM,
.end =
IRQ_RTC_ALARM,
.flags =
IORESOURCE_IRQ,
},
[2] = {
.start =
IRQ_RTC_TIC,
.end =
IRQ_RTC_TIC,
.flags =
IORESOURCE_IRQ
}
};
s3c_device_rtc 被包含在mini6410_devices,在系统初始化的时候,注册进PLATFORM总线。
在驱动 s3c_rtc_driver 初始化的时候,如果匹配成功,自然会调用s3c_rtc_probe
static int __devinit s3c_rtc_probe(struct platform_device
*pdev)
{
struct rtc_device *rtc;
struct rtc_time rtc_tm;
struct resource *res;
int ret;
pr_debug("%s: probe=%pn",
__func__, pdev);
//获得
s3c_rtc_tickno =
platform_get_irq(pdev, 1);
if (s3c_rtc_tickno < 0) {
dev_err(&pdev->dev,
"no irq for rtc tickn");
return -ENOENT;
}
s3c_rtc_alarmno =
platform_get_irq(pdev, 0);
if (s3c_rtc_alarmno < 0) {
dev_err(&pdev->dev,
"no irq for alarmn");
return -ENOENT;
}
pr_debug("s3c2410_rtc: tick irq
%d, alarm irq %dn",
s3c_rtc_tickno,
s3c_rtc_alarmno);
res = platform_get_resource(pdev,
IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev,
"failed to get memory region resourcen");
return -ENOENT;
}
s3c_rtc_mem =
request_mem_region(res->start,
res->end-res->start+1,
pdev->name);
if (s3c_rtc_mem == NULL) {
dev_err(&pdev->dev,
"failed to reserve memory regionn");
ret = -ENOENT;
goto err_nores;
}
s3c_rtc_base =
ioremap(res->start, res->end -
res->start + 1);
if (s3c_rtc_base == NULL) {
dev_err(&pdev->dev,
"failed ioremap()n");
ret = -EINVAL;
goto err_nomap;
}
rtc_clk =
clk_get(&pdev->dev, "rtc");
if (IS_ERR(rtc_clk)) {
dev_err(&pdev->dev,
"failed to find rtc clock sourcen");
ret = PTR_ERR(rtc_clk);
rtc_clk = NULL;
goto err_clk;
}
clk_enable(rtc_clk);
s3c_rtc_enable(pdev, 1);
pr_debug("s3c2410_rtc:
RTCCON=xn",
readw(s3c_rtc_base +
S3C2410_RTCCON));
device_init_wakeup(&pdev->dev,
1);
rtc = rtc_device_register("s3c",
&pdev->dev,
&s3c_rtcops,
THIS_MODULE);
if (IS_ERR(rtc)) {
dev_err(&pdev->dev,
"cannot attach rtcn");
ret = PTR_ERR(rtc);
goto err_nortc;
}
s3c_rtc_cpu_type =
platform_get_device_id(pdev)->driver_data;
s3c_rtc_gettime(NULL,
&rtc_tm);
if
(rtc_valid_tm(&rtc_tm)) {
rtc_tm.tm_year =
100;
rtc_tm.tm_mon =
0;
rtc_tm.tm_mday =
1;
rtc_tm.tm_hour =
0;
rtc_tm.tm_min =
0;
rtc_tm.tm_sec =
0;
s3c_rtc_settime(NULL,
&rtc_tm);
dev_warn(&pdev->dev,
"warning: invalid RTC value so initializing itn");
}
if (s3c_rtc_cpu_type ==
TYPE_S3C64XX)
rtc->max_user_freq
= 32768;
else
rtc->max_user_freq
= 128;
platform_set_drvdata(pdev,
rtc);
s3c_rtc_setfreq(&pdev->dev,
1);
return 0;
err_nortc:
s3c_rtc_enable(pdev, 0);
clk_disable(rtc_clk);
clk_put(rtc_clk);
err_clk:
iounmap(s3c_rtc_base);
err_nomap:
release_resource(s3c_rtc_mem);
err_nores:
return ret;
}
其实这个函数中比较新的内容是rtc_device_register,通过这个函数,我们又把RTC定义为字符设备。这说明了一点,平台驱动和字符设备驱动不是并列的概念,一个字符设备即可以存在字符设备驱动也可以同时存在平台驱动。
好吧,从头审视一下这个字符设备怎么被创建的?
在rtc_init中
1)首先rtc_class = class_create(THIS_MODULE,
"rtc")在CLASS下增加一个目录RTC
2)其次rtc_dev_init,分配一个设备号。
3)最后rtc_sysfs_init,设置RTC这个类的属性。
static struct device_attribute rtc_attrs[] =
{
__ATTR(name, S_IRUGO, rtc_sysfs_show_name,
NULL),
__ATTR(date, S_IRUGO, rtc_sysfs_show_date,
NULL),
__ATTR(time, S_IRUGO, rtc_sysfs_show_time,
NULL),
__ATTR(since_epoch, S_IRUGO,
rtc_sysfs_show_since_epoch, NULL),
__ATTR(max_user_freq, S_IRUGO | S_IWUSR,
ysfs_show_max_user_freq, rtc_sysfs_set_max_user_freq),
__ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys,
NULL),
{ },
};
在函数rtc_device_register中:
1)调用rtc_dev_prepare->cdev_init
去初始化CDEV
2) 调用rtc_dev_add_device->cdev_add
增加这个字符设备(s3c)。
3)调用device_register
,在CLASSRTC下增加一个目录RTC%D,该目录下创建对应的类设备属性文件,一个属性对应一个文件。通过VFS读取这些属性文件, 就可以访问内核驱动的数据。比如如果是读TIME属性文件,那么就调用rtc_sysfs_show_time。其他属性文件的访问类似。
在结束这部分之前,需要稍微做点总一下总结前面的内容.从以上列子可以看出,如果我们开发一个驱动,并希望提供用户交互的接口,那么存在2种方式:
1) 字符设备驱动
2)注册SYSFS属性 这两种方式在目前的版本中都有使用。
还有一种,就是注册PROC接口,在一些文章中看到过,但是我还从没有遇到过。
二、 平台驱动常见结构补充说明
1. platform_device
struct platform_device {
const char *
name; // 设备的名字
int id; //设备的ID
struct
device dev; //在这个结构中常用的其实是platform_data,用来传递资源数据。
u32 num_resources;//定义的资源数量
struct resource *
resource;//具体的资源
const struct
platform_device_id *id_entry;//常用来保存字符设备的设备号
struct
pdev_archdata archdata;//这个一直没怎么用过,有待开发。
};
2. platform_driver
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *,
pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver
driver;//常用的是DRIVER的NAME,与设备保持一致
const struct platform_device_id
*id_table;//如果驱动的名字不一致,比较一下ID TABLE的名字,如果一致,也认为该设备和驱动匹配。
};
3. resource
struct resource {
resource_size_t start;//资源的起始地址
resource_size_t end;//结束地址
const char *name;//资源的名称
unsigned long flags;//表示资源是MEM还是IRQ
struct resource *parent, *sibling,
*child;//似乎不怎么用到。
};
最后
以上就是现代曲奇最近收集整理的关于linux内核添加rtc驱动,LINUX 驱动学习之路 -RTC 一个platform 驱动的实例的全部内容,更多相关linux内核添加rtc驱动,LINUX 驱动学习之路 -RTC 一个platform 驱动内容请搜索靠谱客的其他文章。
发表评论 取消回复