linux内核添加rtc驱动,LINUX 驱动学习之路 -RTC 一个platform 驱动的实例

一、 一个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=%p\n",

__func__, pdev);

//获得

s3c_rtc_tickno =

platform_get_irq(pdev, 1);

if (s3c_rtc_tickno < 0) {

dev_err(&pdev->dev,

"no irq for rtc tick\n");

return -ENOENT;

}

s3c_rtc_alarmno =

platform_get_irq(pdev, 0);

if (s3c_rtc_alarmno < 0) {

dev_err(&pdev->dev,

"no irq for alarm\n");

return -ENOENT;

}

pr_debug("s3c2410_rtc: tick irq

%d, alarm irq %d\n",

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 resource\n");

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 region\n");

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 source\n");

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=x\n",

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 rtc\n");

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 it\n");

}

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

,在CLASS\RTC下增加一个目录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;//似乎不怎么用到。

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值