Linux下RTC子系统学习

来源:互联网 发布:乔丹nba数据统计 编辑:程序博客网 时间:2024/06/11 04:36

最近在做基于Linux的RTC驱动,因此必须先学习Linux下的RTC子系统架构。

RTC及平台设备相关概念

1、 RTC概念:

RTC(real timeclock)实时时钟,主要作用是给Linux系统提供时间。RTC因为是电池供电的,所以掉电后时间不丢失。Linux内核把RTC用作“离线”的时间 与日期维护器。当Linux内核启动时,它从RTC中读取时间与日期,作为基准值。在运行期间内核完全抛开RTC,以软件的形式维护系统的当前时间与日 期,并在需要时将时间回写RTC芯片。另外如果RTC提供了IRQ中断并且可以定时,那么RTC还可以作为内核睡眠时唤醒内核的闹钟。

2、 Linux平台设备驱动概念:

在linux2.6设备模型中,关心总线,设备,驱动这三个实体,总线是处理器与一个或者多个设备之间的通道。在设备模型中,所有的设备都通过总线相连。总线可以相互插入,例如一个USB控制器通常是一个PCI设备。总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动。相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的。
一个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI 等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC 系统中集成的独立的外设控制器、挂接在SoC 内存空间的外设等确不依附于此类总线。基于这一背景,Linux 发明了一种虚拟的总线,称为platform 总线。SOC系统中集成的独立外设单元(LCD,RTC,WDT等)都被当作平台设备来处理,而它们本身是字符型设备。
从Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用platform_driver 进行注册。

2.1、平台设备

在Linux中用platform_device结构体来描述一个平台设备,内核中定义在:<include/linux/platform_device.h>中,如下:

struct platform_device {    const char    * name;           //设备名称    int        id;    struct device    dev;    u32        num_resources;       //设备使用各类资源的数量    struct resource    * resource;  //设备使用的资源    struct platform_device_id    *id_entry;};

/* * Resources are tree-like, allowing * nesting etc.. */struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;};
我们通常关心start、end 和flags 这3 个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IO、IORESOURCE_MEM、IORESOURCE_IRQ、IORESOURCE_DMA 等

2.2、 平台设备驱动:
这里所讲的平台设备驱动是指具体的某种平台设备的驱 动,比如RTC平台设备,这里就是指RTC平台设备驱动。在Linux中,系统还为平台设备定义了平台驱动结构体 platform_driver,就好比系统为字符设备定义了file_operations一样,但不要把平台设备跟字符设备、块设备、网络设备搞成了 并列的概念,因平台设备也可以是字符设备等其他设备。注意:在被定义为平台设备的字符设备的驱动中,除了要实现字符设备驱动中 file_operations的open、release、read、write等接口函数外,还要实现平台设备驱动中platform_driver 的probe、remove、suspend、resume等接口函数。其具体定义在<linux/platform_device.h>中

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;struct platform_device_id *id_table;};



RTC平台设备实现

下面是我实现的RTC平台设备:

static struct resource rtc_resource[] = {    [0] = {  //IO端口资源范围        .start = RTC_PHY_BASE,        .end = RTC_PHY_BASE + RTC_LEN - 1,        .flags = IORESOURCE_MEM,    },    [1] = {  //RTC报警中断资源        .start = IRQ_RTC,        .end = IRQ_RTC,        .flags = IORESOURCE_IRQ,    }};struct platform_device rtc_device = {  //定义了RTC平台设备    .name         = "test_rtc",  //设备名称    .id         = -1,    .num_resources     = ARRAY_SIZE(rtc_resource), //资源数量    .resource     = rtc_resource,  //引用上面定义的资源};

之后该platform device将通过platform_device_register被注册吗,即将平台设备添加到系统总线中:
/** * platform_device_register - add a platform-level device * @pdev: platform device we're adding */int platform_device_register(struct platform_device *pdev){device_initialize(&pdev->dev);return platform_device_add(pdev);}
/** * device_initialize - init device structure. * @dev: device. * * This prepares the device for use by other layers by initializing * its fields. * It is the first half of device_register(), if called by * that function, though it can also be called separately, so one * may use @dev's fields. In particular, get_device()/put_device() * may be used for reference counting of @dev after calling this * function. * * NOTE: Use put_device() to give up your reference instead of freeing * @dev directly once you have called this function. */void device_initialize(struct device *dev){dev->kobj.kset = devices_kset;kobject_init(&dev->kobj, &device_ktype);INIT_LIST_HEAD(&dev->dma_pools);init_MUTEX(&dev->sem);spin_lock_init(&dev->devres_lock);INIT_LIST_HEAD(&dev->devres_head);device_init_wakeup(dev, 0);device_pm_init(dev);set_dev_node(dev, -1);}
/** * platform_device_add - add a platform device to device hierarchy * @pdev: platform device we're adding * * This is part 2 of platform_device_register(), though may be called * separately _iff_ pdev was allocated by platform_device_alloc(). */int platform_device_add(struct platform_device *pdev){int i, ret = 0;if (!pdev)return -EINVAL;if (!pdev->dev.parent)pdev->dev.parent = &platform_bus;pdev->dev.bus = &platform_bus_type;if (pdev->id != -1)dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);elsedev_set_name(&pdev->dev, "%s", pdev->name);for (i = 0; i < pdev->num_resources; i++) {struct resource *p, *r = &pdev->resource[i];if (r->name == NULL)r->name = dev_name(&pdev->dev);p = r->parent;if (!p) {if (resource_type(r) == IORESOURCE_MEM)p = &iomem_resource;else if (resource_type(r) == IORESOURCE_IO)p = &ioport_resource;}if (p && insert_resource(p, r)) {printk(KERN_ERR       "%s: failed to claim resource %d\n",       dev_name(&pdev->dev), i);ret = -EBUSY;goto failed;}}pr_debug("Registering platform device '%s'. Parent at %s\n", dev_name(&pdev->dev), dev_name(pdev->dev.parent));ret = device_add(&pdev->dev);if (ret == 0)return ret; failed:while (--i >= 0) {struct resource *r = &pdev->resource[i];unsigned long type = resource_type(r);if (type == IORESOURCE_MEM || type == IORESOURCE_IO)release_resource(r);}return ret;}


RTC平台设备驱动实现

下面是具体的RTC平台设备驱动实现:

static struct platform_driver rtc_driver ={    .probe   = rtc_probe,//RTC探测函数, 探测就意味着在系统总线中去检测设备的存在,然后获取设备有用的相关资源信息,以便我们使用这些信息    .remove  = __devexit_p(rtc_remove),    .suspend = rtc_suspend,    .resume  = rtc_resume,    .driver  =    {        /* the name must the same with the definition in the platform device */        .name   = "test_rtc",        .owner  = THIS_MODULE,    },};static int __init rtc_init(void){    return platform_driver_register(&rtc_driver);}static void __exit rtc_exit(void){    platform_driver_unregister(&rtc_driver);}module_init(rtc_init);module_exit(rtc_exit);

static const struct rtc_class_ops rtc_ops = {    .release         = rtc_release,    .irq_set_state   = rtc_setpie,    .read_time       = rtc_gettime,    .set_time        = rtc_settime,    .read_alarm      = rtc_getalarm,    .set_alarm       = rtc_setalarm,};/*RTC driver probe function*/static int __devinit rtc_probe(struct platform_device *pdev){    p_rtc->rtc = rtc_device_register(pdev->name,    &pdev->dev,    &rtc_ops,    THIS_MODULE);    if (IS_ERR(rtc)) {        dev_err(&pdev->dev, "cannot attach rtc/n");ret = PTR_ERR(rtc);goto err_iounmap;    }    platform_set_drvdata(pdev, p_rtc);    return 0;}
在probe函数中实现driver的初始化,

RTC设备类的操作,是对RTC硬件的各种寄存器进行操作,rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体,类似字符设备在驱动中的file_operations对字符设备进行操作的意思。对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等,该结构体内接口函数的实现都在下面,通过rtc_device_register将该file_operation:rtc_ops注册进driver。


关联设备和驱动程序:

内核启动后,首先够造链表将描述设备的platform_device结构组织起来,得到一个设备的列表;当加载到某个驱动程序的platform_driver结构时(start_kernelàrest_init()àinit àdo_basic_setup(); //初始化设备驱动程序),使用一些匹配函数来检查驱动程序是否支持这些设备,常用的检查方法很简单:比较驱动程序和设备的名称。

当注册成功时会调用platform_driver结构元素probe函数指针。驱动的初始化等都是在prob函数中完成的。


probe函数中最关键的操作时注册rtc device:

    p_rtc->rtc = rtc_device_register(pdev->name,    &pdev->dev,    &rtc_ops,    THIS_MODULE);

/** * 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 struck class device. */struct rtc_device *rtc_device_register(const char *name, struct device *dev,const struct rtc_class_ops *ops,struct module *owner){struct rtc_device *rtc;int id, err;if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {err = -ENOMEM;goto exit;}mutex_lock(&idr_lock);err = idr_get_new(&rtc_idr, NULL, &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->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;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);dev_set_name(&rtc->dev, "rtc%d", id);rtc_dev_prepare(rtc);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, dev_name(&rtc->dev));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);}





原创粉丝点击