Chinaunix首页 | 论坛 | 博客
  • 博客访问: 819824
  • 博文数量: 117
  • 博客积分: 2583
  • 博客等级: 少校
  • 技术积分: 1953
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-06 22:58
个人简介

Coder

文章分类
文章存档

2013年(1)

2012年(10)

2011年(12)

2010年(77)

2009年(13)

2008年(4)

分类: 嵌入式

2010-04-18 11:03:32

RTC实时时钟
1、实时时钟在嵌入式系统中的作用
在一个嵌入式系统中,实时时钟可以为其提供可靠的时间,包括时、分、秒、年、月、日;即使系统处于掉电状态,它也能正常工作(通常实时时钟都采用后备电池供电),它的外围也不需要太多的辅助电路,典型的就是只需要一个高精度的晶振。
2、S3C2440A的实时时钟单元
Mini2440用的是SamsungS3C2440A SoC。时钟单元可以通过备用电池供电,即使系统关闭,也可以继续工作。RTC可以用STRBLDRB命令来操作,以BCD码的值向CPU传递8位数据。这些数据包括时、分、秒、年、月、日和星期等。RTC单元通过一个外部的32.768Khz晶振工作,而且可以执行报警。特性如下:
BCD数字:时、分、秒、年、月、日和星期
闰年产生器
报警功能:报警中断或从掉电模式唤醒系统
独立的电源引脚(RTCVDD
可为RTOS内核时间滴答支持毫秒级的时钟中断。
1)闰年发生器
可以根据来自于BCDDATEBCDMONBCDYEAR的数据,从28, 29, 3031中选择一个作为某一个月的最后一天,并可以根据是否是闰年来决定日期。由于8位计数器只能表示2BCD码,因此他不能判断“00”年(年份的最后两位)是否是闰年。例如,它不能区别20001999.为了解决这个问题,S3C2440ARTC模块中有一个固定的逻辑来支持2000的闰年。2000年时闰年而1900不是。S3C2440A中的00代表2000,而不是1900
2)、读/写寄存器
为了写RTC中的BCD寄存器,则RTCCON中的位0必须被置高。为了显示时、分、秒、年、月、日,CPU应该分别读取BCDHOURBCDMINBCDSECBCDYEARBCDMONBCDDATE寄存器中的值。然而当读取多个寄存器时,可能出现1秒的偏差。
3)、备用电池和报警功能
RTC逻辑可以用电池驱动,即使系统关闭,备用电池也可以通过RTCVDD引脚来给RTC供电。在掉电模式或正常模式下,RTC均可以在一个特定的时间产生一个报警。在正常模式下,报警中断(INT_RTC)被激活。在掉电模式下,电源管理唤醒(PMWKUP)信号被激活,同时INT_RTC被激活。RTC报警寄存器(TRCALM)可以确定报警使能/禁用状态和报警时间的设置条件。
4)、节拍中断
RTC节拍可以用于中断请求。TICNT寄存器有一个中断使能位和中断计数值。当计数值变为0时,节拍中断产生。中断周期如下:
Period = ( n+1 ) / 128 second n: Tick time count value (1~127)
3、实时时钟特殊寄存器
1)、实时时钟控制(RTCCON)寄存器
RTCEN4位组成,RTCEN用来控制BCD寄存器的读/写使能,而其他的CLKSEL, CNTSEL, CLKRST则用于测试。RTCEN可以控制CPURTC之间的所有接口,所以在一个系统复位后,为了使能读/写功能,它应该在一个控制例程中被设置为1。而在系统关闭后,为了防止意外的写RTC寄存器,则应该将它清零。
Register
Address
R/W
Description
Reset Value
RTCCON
0x57000040(L)
0x57000043(B)
R/W (by byte)
RTC control register
0x0
RTCCON    
RTCCON
Bit
Description
Initial State
CLKRST
[3]
RTC clock count reset.
0 = No reset,     1 = Reset
0
CNTSEL
[2]
BCD count select.
0 = Merge BCD counters
1 = Reserved (Separate BCD counters)
0
CLKSEL
[1]
BCD clock select.
0 = XTAL 1/215 divided clock    
1 = Reserved (XTAL clock only for test)
0
RTCEN
[0]
RTC control enable.
0 = Disable               1 = Enable
Note: Only BCD time count and read operation can be performed.
0
 
手册中有这么一句:The RTCCON register consists of 4 bits such as the RTCEN, which controls the read/write enable of the BCD registers, CLKSEL, CNTSEL, and CLKRST for testing.
CLKSEL, CNTSEL, CLKRST仅仅用于测试,所以正常情况下,一定要将他们都清零,如果设置了CLKSEL则,即使不对BCD时间寄存器进行写操作,则每两次(指的是关闭RTC文件再打开)读取的时间在月、日、时、分、秒上也是不同的。如果是连CNTSEL也设置了,则即使是在同一个程序中,打开rtc,对同一个文件描述符的间隔极端的两次读取时间操作,所读出的所有的时间也都是不同的。
2)、节拍时间计数器
Register
Address
R/W
Description
Reset Value
TICNT
0x57000044(L)
0x57000047(B)
R/W (by byte)
Tick time count register
0x0
TICNT
TICNT
Bit
Description
Initial State
TICK INT ENABLE
[7]
Tick time interrupt enable.
0 = Disable          1 = Enable
0
TICK TIME COUNT
[6:0]
Tick time count value (1~127).
This counter value decreases internally, and users cannot read
this counter value in working.
000000
3)、RTC报警控制(RTCALM)寄存器
Register
Address
R/W
Description
Reset Value
RTCALM
0x57000050(L)
0x57000053(B)
R/W (by byte)
RTC alarm  control egister
0x0
RTCALM
RTCALM
Bit
Description
Initial State
Reserved
[7]
 
0
ALMEN
[6]
Alarm global enable.
0 = Disable,     1 = Enable
0
YEAREN
[5]
Year alarm enable.
0 = Disable,     1 = Enable
0
MONREN
[4]
Month alarm enable.
0 = Disable,     1 = Enable
0
DATEEN
[3]
Date alarm enable.
0 = Disable,     1 = Enable
0
HOUREN
[2]
Hour alarm enable.
0 = Disable,     1 = Enable
0
MINEN
[1]
Minute alarm enable.
0 = Disable,     1 = Enable
0
SECEN
[0]
Second alarm enable.
0 = Disable,     1 = Enable
0
4)、报警时间寄存器
Register
Address
R/W
Description
Reset Value
ALMSEC
0x57000054(L)
0x57000057(B)
R/W
(by byte)
Alarm second data egister
0x0
ALMMIN
0x57000058(L)
0x5700005B(B)
R/W
(by byte)
Alarm minute data register
0x00
ALMHOUR
0x5700005C(L)
0x5700005F(B)
R/W
(by byte)
Alarm hour data register
0x00
ALMDATE
0x57000060(L)
0x57000063(B)
R/W
(by byte)
Alarm date data register
0x01
ALMMON
0x57000064(L)
0x57000067(B)
R/W
(by byte)
Alarm month data register
0x01
ALMYEAR
0x57000068(L)
0x5700006B(B)
R/W
(by byte)
Alarm year data register
0x0
5)、实时时钟时间寄存器
Register
Address
R/W
Description
Reset Value
BCDSEC
0x57000070(L)
0x57000073(B)
R/W
(by byte)
BCD second data egister
Undefined
BCDMIN
0x57000074(L)
0x57000077(B)
R/W
(by byte)
BCD minute data register
Undefined
BCDHOUR
0x57000078(L)
0x5700007B(B)
R/W
(by byte)
BCD hour data register
Undefined
BCDDATE
0x5700007C(L)
0x5700007F(B)
R/W
(by byte)
BCD date register
Undefined
BCDDAY
0x57000080(L)
0x57000083(B)
R/W
(by byte)
BCD a day of the week register
Undefined
BCDMON
0x57000084(L)
0x57000087(B)
R/W
(by byte)
BCD month data register
Undefined
BCDYEAR
0x57000088(L)
0x5700008B(B)
R/W
(by byte)
BCD year data register
Undefined
4)、RTC应用
1、初始化
rRTCCON = 0x01// RTC/写使能,选择BCD始终,计数器,无复位.
rBCDYEAR = 0x10; rBCDMON = 0x04; rBCDDAY = 0x01; rBCDDATE = 0x12;
rBCDHOUR = 0x17; rBCDMIN = 0x34; rBCDSEC = 0x12;
rRTCCON = 0x00;// RTC/写禁止,选择BCD始终,计数器,无复位.
2、实时时钟显示时间
rRTCCON = 0x01;
直接读取各个BCD时间值寄存器
rRTCCON = 0x00;
 
Linux平台设备
Linux设备模型中,一个平台是一个绑定到集成在SoC中的轻量级设备的伪总线。通过这个总线,Linux的电源管理模块最终会调用到platform_driver中设备管理的入口函数(suspend()resume()shutdown()方法),这样就可以辅助系统的电源管理,对于手持设备等低功耗的系统意义重大。所以通常SoC系统中集成的独立外设单元都被当做平台设备处理。
一个平台由下面这些组成:
1、一个平台设备。在文件include/linux/platform_device.h中定义的platform_device结构体代表一个平台设备:
struct platform_device {
        const char     * name;//设备名
        int            id;//使用这个字段去注册一个平台设备的多个实例
        struct device  dev;//包含一个release()方法和平台相关数据
        u32            num_resources;//设备使用的各类资源的数量
        struct resource * resource;//资源
        struct platform_device_id      *id_entry;
        /* arch specific additions */
        struct pdev_archdata   archdata;
};
通常特定于体系结构的设置代码使用下面这些函数来向系统中添加平台设备:
int platform_device_register(struct platform_device *pdev);
struct platform_device *platform_device_register_simple(const char *name, int id,       struct resource *res,unsigned int num);
int platform_add_devices(struct platform_device **devs, int num);
platform_device_register向系统注册一个平台设备。platform_device_register_simpleplatform_device_register不需要由驱动程序来处理内存分配的简化版本,系统负责分配设备结构体内存,完成初始化,注册平台设备,并返回platform_device结构体指针。res 应该是一个已经存在的struct resource结构体数组,但系统会重新分配一块内存,将res的内容复制过去,然后使platform_deviceresource成员指向系统分配的这块内存。即是,在调用完platform_device_register_simple之后就可以释放res内存了。platform_add_devices同时注册多个平台设备。devsplatform_device结构指针数组,num为平台设备的个数,也就是数组长度。这个函数实际上是针对devs数组的每一个元素调用platform_device_register函数。
系统也提供了一组用来注销平台设备的函数:
void platform_device_unregister(struct platform_device *pdev);
在平台设备结构中,包含其所使用的资源数量和指针。
Mini2440RTCplatform_device结构体是在arch/arm/plat-s3c24xx/devs.c文件中定义的,并会在内核启动的时候添加:
static struct resource s3c_rtc_resource[] = {
        [0] = {
               .start = S3C24XX_PA_RTC,
               .end   = S3C24XX_PA_RTC + 0xff,
               .flags = IORESOURCE_MEM,
        },
        [1] = {
               .start = IRQ_RTC,
               .end   = IRQ_RTC,
               .flags = IORESOURCE_IRQ,
        },
        [2] = {
               .start = IRQ_TICK,
               .end   = IRQ_TICK,
               .flags = IORESOURCE_IRQ
        }
};
 
struct platform_device s3c_device_rtc = {
        .name            = "s3c2410-rtc",
        .id              = -1,
        .num_resources   = ARRAY_SIZE(s3c_rtc_resource),
        .resource        = s3c_rtc_resource,
};
第一个资源是IO内存,其起始地址是0x57000000,而不是RTC地址最低的寄存器的地址0x57000040。后面两个资源则是中断。
2、一个平台驱动。在文件include/linux/platform_device.h中定义的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;
        struct platform_device_id *id_table;
};
S3c2440RTC驱动程序的platform_driver为:
struct platform_driver s3c2440rtc_driver = {
  .probe = s3c2440rtc_probe,
  .remove = s3c2440rtc_remove,
  .suspend = s3c2440rtc_suspend,
  .resume = s3c2440rtc_resume,
  .driver = {
    .owner = THIS_MODULE,
    .name = "s3c2410-rtc",
  },
};
与平台驱动有关的两个函数:
int platform_driver_register(struct platform_driver *drv);
void platform_driver_unregister(struct platform_driver *);
驱动程序使用前者来向系统注册一个平台驱动,而使用后者来注销一个平台驱动。平台设备的注册通常在启动的板子设置时完成。这之后,驱动程序使用platform_driver_register()函数来注册平台驱动入口点(probe ()remove()shutdown()suspend()resume()等)。这个步骤之后,看到在目录/sys/devices/platform/下为平台设备创建的相关目录和文件。Linux的设备层使用设备名字来检查平台驱动和平台设备的匹配,然后调用属于平台驱动的probe ()方法。probe ()方法所要完成的工作很类似于模块的init()函数,即向系统申请驱动程序所需要的各种资源(IO内存区等等),初始化驱动的相关数据结构的等,这些初始化的代码直接放在模块的init()函数中也未尝不可,但通常都是使用probe()方法来完成。所以S3C2440A RTC驱动模块的初始化函数仅仅为:
static int s3c2440rtc_init(void)
{
  platform_driver_register(&s3c2440rtc_driver);
  printk(KERN_ALERT "The initialization of s3c2440rtc has been completed successfully!\n");
  return 0;
}
probe()方法则为:
static int s3c2440rtc_probe(struct platform_device *device)
{
  struct rtc_device *rtc = NULL;
  struct resource *res = NULL;
  unsigned int rtccon;
  int ret;
  /* Initialize the irq number.*/
  s3c2440rtc_alarmno = platform_get_irq(device, 0);
  if(s3c2440rtc_alarmno < 0){
    printk(KERN_ALERT "no irq for alarn.\n");
    return -ENOENT;
  }
  s3c2440rtc_tickno = platform_get_irq(device, 1);
  if(s3c2440rtc_tickno < 0){
    printk(KERN_ALERT "no irq for tick.\n");
    return -ENOENT;
  }
  printk(KERN_ALERT "s3c2440rtc: tick irq %d, alarm irq %d\n", s3c2440rtc_tickno, s3c2440rtc_alarmno);
  /* Get the memory region and remap it. */
  res = platform_get_resource(device, IORESOURCE_MEM, 0);
  if(res == NULL){
    printk(KERN_ALERT "s3c2440rtc_probe: error.\n");
    return -ENODEV;
  }
  s3c2440rtc_mem = request_mem_region(res->start, res->end - res->start, device->name);
  if(s3c2440rtc_mem == NULL){
    printk(KERN_ALERT "s3c2440rtc_probe: get device memory fail.\n");
    return -EBUSY;
  }
  rtcmembase = ioremap(res->start, res->end - res->start);
  if(rtcmembase == NULL){
    printk(KERN_ALERT "s3c2440rtc_probe: ioreman fail,\n");
    ret = -EINVAL;
    goto err_nomap;
  }
  printk(KERN_ALERT "IO memory base address: %ld\n",(unsigned long)rtcmembase);
  /* Register the rtc driver.*/
 
  rtccon = ioread8(rtcmembase + RTCCON);
  rtccon &= 1;
  iowrite8(rtccon, rtcmembase + RTCCON);
  s3c2440rtc_setpie(&device->dev, ENABLE);
  s3c2440rtc_enable(ENABLE);
  s3c2440rtc_setfreq(&device->dev, 1);
  device_init_wakeup(&device->dev, 1);
  rtc = rtc_device_register("s3c2440rtc", &device->dev, &s3c2440rtc_ops, THIS_MODULE);
  if(IS_ERR(rtc)){
    printk(KERN_ALERT "Cannot attach rtc.\n");
    ret = PTR_ERR(rtc);
    goto err_nortc;
  }
  rtc->max_user_freq = 128;
  platform_set_drvdata(device, rtc);
  return 0;
err_nortc:
  s3c2440rtc_enable(DISABLE);
  iounmap(rtcmembase);
err_nomap:
  release_mem_region(res->start, res->end - res->start);
  return ret;
}
“平台设备”并不是与字符设备、块设备并列的概念,而是内核为设备提供的一个附加层。所以probe()方法还要完成向系统添加特定的设备的工作,比如向系统注册RTC设备、字符设备、misc设备等。
remove()函数则完成于probe()相反的工作,在模块卸载,注销一个平台驱动的时候调用。
static int s3c2440rtc_remove(struct platform_device *device)
{
  struct resource *res;
  struct rtc_device *rtc = platform_get_drvdata(device);
  platform_set_drvdata(device, NULL);
  s3c2440rtc_setpie(&device->dev, DISABLE);
  s3c2440rtc_setaie(&device->dev, DISABLE);
  rtc_device_unregister(rtc);
  res = platform_get_resource(device, IORESOURCE_MEM, 0);
  iounmap(rtcmembase);
  release_mem_region(res->start, res->end - res->start);
  return 0;
}
平台设备可以使用的其他一些内核接口:
struct resource *platform_get_resource(struct platform_device *dev,
                           unsigned int type, unsigned int num);
int platform_get_irq(struct platform_device *dev, unsigned int num);
struct resource *platform_get_resource_byname(struct platform_device *dev,unsigned int type,const char *name);
int platform_get_irq_byname(struct platform_device *dev,
const char *name);
这几个函数获得平台设备使用的资源的一些信息。资源用来标志驱动程序使用的重要的系统资源,在include/linux/ioport.h文件中有struct resource的定义:
struct resource {
        resource_size_t start;
        resource_size_t end;
        const char *name;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
};
flags字段用来说明资源的类型,可以有一下几种:
#define IORESOURCE_IO          0x00000100 //IO port
#define IORESOURCE_MEM         0x00000200 //IO memory
#define IORESOURCE_IRQ         0x00000400 // IRQ
#define IORESOURCE_DMA         0x00000800 // DMA
这位平台设备相关的资源管理提供了方便。
struct platform_device *to_platform_device(struct device *dev);
由一个指向struct device的指针获得一个指向struct platform_device的指针,其中dev是返回的指针所指向的struct platform_device结构的成员,这个宏实际上是一个container_of宏调用。
void platform_set_drvdata(struct platform_device *pdev,void *data);
void * platform_get_drvdata(struct platform_device *pdev);
这两个函数与一个平台设备特有的私有数据结构有关。前者设置平台驱动的私有数据为data,而后者则从一个平台设备结构中获得私有数据。通常私有数据为特定的设备结构,比如rtc设备结构等。比如可以在probe()方法中这样调用:
       struct rtc_device *rtc;
……
       rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
                             THIS_MODULE);
……
       platform_set_drvdata(pdev, rtc);
然后在RTC设备方法里从平台设备中获得rtc设备指针:
       struct platform_device *pdev = to_platform_device(dev);
       struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
……
(参考Documentation/driver-model/platform.txt文件来获得更多关于平台设备和驱动的信息。)
 
Linux内核提供了一个RTC层,用于支持RTC设备驱动程序的编写。主要由三部分组成:首先,是字符设备层(在drivers/rtc/rtc-dev.c文件中实现),完成一个字符设备所需要的字符设备初始化、添加等各种特定于字符设备的工作,实现各种的file_operations的基本语义,完成RTC API定义的各种操作语义,比如read()、write()、open()、ioctl()等等,它从RTC设备操作层获得数据并组织数据格式,然后返回给用户,或相反的从用户空间获得数据组织数据格式然后向设备写数据;其次,还有一个RTC设备操作层,主要用来完成从RTC设备中获得数据的工作,它调用特定的RTC设备操作方法来完成于特定的RTC设备交互的工作(在drivers/rtc/interface.c文件中实现);最后,就是特定的RTC设备驱动层,完成从实际的设备中获得数据或向设备中写入数据的工作。这种分层的RTC设备结构,简化了RTC设备驱动程序的设计,是RTC设备驱动程序的编写,由于不需要处理高级IO等等复杂问题而变得容易了,同时也是系统更加的可靠。
在内核中通过一个RTC设备结构体来描述一个RTC设备:
struct rtc_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 uie_irq_active:1;
       unsigned int stop_uie_polling:1;
       unsigned int uie_task_active:1;
       unsigned int uie_timer_active:1;
#endif
};
一个RTC驱动程序首先要向系统注册一个RTC设备驱动,可以使用函数:
struct rtc_device *rtc_device_register(const char *name,
      struct device *dev,const struct rtc_class_ops *ops,   
struct module *owner)
nameRTC设备的名字,opsRTC的设备操作方法,也是一个驱动程序主要要完成的,而owner则为与RTC设备关联的模块,通常为THIS_MODULE。这个函数会为我们的RTC设备创建一个便准的字符设备所需要的一切,还会为我们的RTC设备建立/dev设备节点。
用于注销一个RTC驱动的函数则为:
void rtc_device_unregister(struct rtc_device *rtc)
RTC的操作方法用一个struct rtc_class_ops结构体来表示:
struct rtc_class_ops {
       int (*open)(struct device *);
       void (*release)(struct device *);
       int (*ioctl)(struct device *, unsigned int, unsigned long);
       int (*read_time)(struct device *, struct rtc_time *);
       int (*set_time)(struct device *, struct rtc_time *);
       int (*read_alarm)(struct device *, struct rtc_wkalrm *);
       int (*set_alarm)(struct device *, struct rtc_wkalrm *);
       int (*proc)(struct device *, struct seq_file *);
       int (*set_mmss)(struct device *, unsigned long secs);
       int (*irq_set_state)(struct device *, int enabled);
       int (*irq_set_freq)(struct device *, int freq);
       int (*read_callback)(struct device *, int data);
       int (*alarm_irq_enable)(struct device *, unsigned int enabled);
       int (*update_irq_enable)(struct device *, unsigned int enabled);
};
RTC类的各个操作方法说明:
1int open(struct device *dev)
rtc设备节点上调用open系统调用,最后会调用到这个函数。通常会申请一些系统的珍惜资源,比如申请中断线等。
2void release(struct device *dev)
而这个方法则最终会由字符设备操作的release()方法调用,完成于open()方法相反的工作,释放中断线等。返回0,出错返回错误码。
3int ioctl(struct device *dev, unsigned int cmd, unsigned long)
实现一些针对特定的RTC设备的ioctl命令。
4int read_time(struct device *dev, struct rtc_time *rtctm)
读取RTC实时时钟的时间,填充rtctm结构,以二进制的格式。返回值为0RTC设备中时间通常为BCD编码,内核还提供了将BCD码转换为二进制码的函数:
unsigned bcd2bin(unsigned char val);
完场相反工作的函数:
unsigned char bin2bcd(unsigned val);
5int set_time(struct device *dev, struct rtc_time *rtctm)
这个函数设置RTC时间。struct rtc_time参数的各字段以二进制格式提供。上面两个方法使用的struct rtc_time结构体在inlucde/lnux/rtc.h中定义,为:
struct rtc_time {
       int tm_sec;
       int tm_min;
       int tm_hour;
       int tm_mday;
       int tm_mon;
       int tm_year;
       int tm_wday;
       int tm_yday;
       int tm_isdst;
};
6int read_alarm(struct device *dev, struct rtc_wkalrm *alrmtm)
获取RTC的报警时间值和警报器的状态,如果警报器功能是打开的,则返回相关寄存器中的报警时间值,否则,alrmtm时间值alrm->time的各个字段均以0xff填充。同样的要以二进制格式填充alrmtm的时间值字段。
7int set_alarm(struct device *dev, struct rtc_wkalrm *alrmtm);
设置报警时间,打开个时间单位的报警功能(RTCALM),并根据传进来的larmtm中报警功能的打开与否,来决定是否实际的打开RTC的报警功能。时间的字段为二进制格式。返回0
上面两个函数使用的struct rtc_wkalrm也在inlucde/lnux/rtc.h中定义:
struct rtc_wkalrm {
    unsigned char enabled;/* 0 = alarm disabled, 1 = alarm enabled */
    unsigned char pending;/* 0 = alarm not pending, 1 = alarm pending */
    struct rtc_time time;      /* time the alarm is set to */
};
8int proc(struct device *dev, struct seq_file *seqfile)
proc文件系统接口
9int set_mmss(struct device *dev, unsigned long secs);
10int irq_set_state(struct device *dev, int enabled)
11int irq_set_freq(struct device *dev, int freq)
设置节拍中断产生的频率。返回0
12int read_callback(struct device *dev, int data);
13int alarm_irq_enable(struct device *dev, unsigned int enabled)
节拍中断开关,enabled0时关闭,为1时打开。返回0
14int update_irq_enable(struct device *dev, unsigned int enabled);
S3C2440 RTC驱动的ops结构定义为:

static const struct rtc_class_ops s3c2440rtc_ops = {
  .open = s3c2440rtc_open,
  .release = s3c2440rtc_close,
  .read_time = s3c2440rtc_gettime,
  .set_time = s3c2440rtc_settime,
  .read_alarm = s3c2440rtc_getalarm,
  .set_alarm = s3c2440rtc_setalarm,
  .irq_set_freq = s3c2440rtc_setfreq,
  .irq_set_state = s3c2440rtc_setpie,
  .alarm_irq_enable = s3c2440rtc_setaie,
  .proc = s3c2440rtc_proc,
};
S3C2440 RTC驱动也主要是根据上面的语义一个个的实现这些函数而已。
关于rtc中断:
在节拍中断中更新RTC节拍中断计数。在报警中断中则更新报警中断计数:
static irqreturn_t s3c2440rtc_alrmirq(int irq, void *id)
{
        struct rtc_device *rdev = id;
        rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
        return IRQ_HANDLED;
}
 
static irqreturn_t s3c2440rtc_tickirq(int irq, void *id)
{
        struct rtc_device *rdev = id;
        rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
        return IRQ_HANDLED;
}
阅读(4189) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~