arm linux应用调用rtc接口,ARMLinux驱动RTC(实时时钟)驱动分析

硬件平台:FL2440(S3C2440)

内核版本:Linux 2.6.28

主机平台:Ubuntu 11.04

内核版本:Linux 2.6.39

交叉编译器版本:arm-linux-gcc 3.4.1

原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/6584285

1、实时时钟概述

实时时钟(RTC)单元可以在断电的情况下使用纽扣电池继续计时工作。RTC使用STRB/LDRB ARM操作传输二进制码十进制数的8位数据给CPU。其中的数据包括秒、分、时、日期、天、月、年的时间信息。可以执行报警功能。

2、实时时钟操作

下面是RTC模块的电路图

1990d1944ab8105597a000a1104c1d27.gif

3、RTC寄存器介绍

实时时钟控制寄存器(RTCCON)-REAL TIME CLOCK CONTROL REGISTER

05148942e0d67995f3e38c3c00167a59.gif

6f6236d432e840d6f0839660393531a5.gif

节拍时间计数寄存器(TICNT)-TICK TIME COUNT REGISTER

238eac786622dfd8d55f58dbd66b1075.gif

RTC报警控制寄存器(RTCALM)-RTC ALARM CONTROL REGISTER

f63453c919a4496fa8539af7542d35db.gif

报警秒数寄存器(ALMSEC)-ALARM SECOND DATA REGISTER

4089142ae44ce2b909ed9415442f7b95.gif

报警分钟计数寄存器(ALMMIN)-ALARM MIN DATA REGISTER

9b6369188146b77697903f5208fc92c4.gif

报警小时数据寄存器(ALMHOUR)-ALARM HOUR DATA REGISTER

87b39c51aec3f959d2f3ee6a393d3a69.gif

报警日期数据寄存器(ALMDATE)-ALARM DATE DATA REGISTER

fd6e5e910e6a961ab5af76b0c76ff404.gif

报警月数数据寄存器(ALMMON)-ALARM MON DATA REGISTER

125a6c58e30df8cc53fee975f9a8c95d_thumb.gif

报警年数数据寄存器(ALMYEAR)-ALARM YEAR DATA REGISTER

255a7f2bc6a1a0983b6302ac2053ac3a_thumb.gif

BCD数据寄存器的格式和报警寄存器结构相同,只是对应的地址不同。

BCD秒寄存器(BCDSEC)-BCD SECOND REGISTER 地址:0x57000070(L) 0x57000073(B)

BCD分寄存器(BCDMIN)-BCD MINUTE REGISTER 地址:0x57000074(L) 0x57000077(B)

BCD小时寄存器(BCDHOUR)-BCD HOUR REGISTER  地址:0x57000078(L) 0x5700007B(B)

BCD日期寄存器(BCDDATE)-BCD DATE REGISTER  地址:0x5700007C(L) 0x5700007F(B)

BCD日寄存器(BCDDAY)-BCD DAY REGISTER 地址:0x57000080(L) 0x57000083(B)

BCD月寄存器(BCDMON)-BCD MONTH REGISTER 地址:0x57000084(L) 0x57000087(B)

BCD年寄存器(BCDYEAR)-BCD YEAR REGISTER 地址:0x57000088(L) 0x5700008B(B)

4、驱动实例分析

为了使驱动更容易理解,现在这个RTC驱动只完成了计时功能,没有添加相应的报警功能,也没有添加电源管理的功能,缺少的功能今后完善。

下面先总体了解驱动:

首先是RTC驱动的结构体,在/include/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 (*suspend_late)(struct platform_device *, pm_message_t state);

int (*resume_early)(struct platform_device *);

int (*resume)(struct platform_device *);

struct pm_ext_ops *pm;

struct device_driver driver;

};

驱动中定义对应的结构体

static struct platform_driver s3c2410_rtc_driver = {

.probe= s3c_rtc_probe,//RTC探测函数

.remove= __devexit_p(s3c_rtc_remove),//RTC移除函数

.driver= {

.name= "s3c2410-rtc",

.owner= THIS_MODULE,

},

};

下面是驱动中驱动的初始化和退出函数

static int __init s3c_rtc_init(void)

{

printk(banner);

return platform_driver_register(&s3c2410_rtc_driver);

}

static void __exit s3c_rtc_exit(void)

{

platform_driver_unregister(&s3c2410_rtc_driver);

}

platform_driver_register()和platform_driver_unregister()函数在/drivers/base/platform.c中实现的。

可以看出,platform_driver_register()函数的作用就是为platform_driver中的driver中的probe、remove等提供接口函数

int platform_driver_register(struct platform_driver *drv)

{

drv->driver.bus = &platform_bus_type;

if (drv->probe)

drv->driver.probe = platform_drv_probe;

if (drv->remove)

drv->driver.remove = platform_drv_remove;

if (drv->shutdown)

drv->driver.shutdown = platform_drv_shutdown;

if (drv->suspend)

drv->driver.suspend = platform_drv_suspend;

if (drv->resume)

drv->driver.resume = platform_drv_resume;

if (drv->pm)

drv->driver.pm = &drv->pm->base;

return driver_register(&drv->driver);//注册老的驱动

}

void platform_driver_unregister(struct platform_driver *drv)

{

driver_unregister(&drv->driver);

}

接下来是RTC平台驱动探测函数s3c_rtc_probe,下面函数定义的时候使用了__devinit的作用是使编译器优化代码,将其放在和是的内存位置,减少内存占用和提高内核效率。

probe函数接收到plarform_device这个参数后,就需要从中提取出需要的信息。它一般会通过调用内核提供的platform_get_resource和platform_get_irq等函数来获得相关信息。如通过platform_get_resource获得设备的起始地址后,可以对其进行request_mem_region和ioremap等操作,以便应用程序对其进行操作。通过platform_get_irq得到设备的中断号以后,就可以调用request_irq函数来向系统申请中断。这些操作在设备驱动程序中一般都要完成。

static int __devinit s3c_rtc_probe(struct platform_device *pdev)

{

struct rtc_device *rtc;//定义rtc_device结构体,定义在/include/linux/rtc.h

struct resource *res;//定义资源结构体,定义在/include/linux/ioport.h

int ret;

pr_debug("%s: probe=%p\n", __func__, pdev);

/* find the IRQs */

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;

}

/* get the memory region */

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取RTC平台使用的IO资源

if (res == NULL) {

dev_err(&pdev->dev, "failed to get memory region resource\n");

return -ENOENT;

}

//申请内存区域,res是struct resource类型,见本函数后面

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;

}

/* check to see if everything is setup correctly */

s3c_rtc_enable(pdev, 1);//对RTCCON寄存器设置,详情见下面的函数实现

pr_debug("s3c2410_rtc: RTCCON=%02x\n",

readb(s3c_rtc_base + S3C2410_RTCCON));

s3c_rtc_setfreq(&pdev->dev, 1);//详情见下面的函数实现

/* register RTC and exit */

rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,

THIS_MODULE);//注册RTC为RTC设备,其中s3c_rtcops定义见下

if (IS_ERR(rtc)) {

dev_err(&pdev->dev, "cannot attach rtc\n");

ret = PTR_ERR(rtc);

goto err_nortc;

}

rtc->max_user_freq = 128;//设置RTC节拍时间计数寄存器TICNT的节拍时间计数值的用户最大相对值

//将RTC类的设备数据传递给系统设备,在/include/linux/platform_device.h中

//#define platform_set_drvdata(_dev,data)dev_set_drvdata(&(_dev)->dev, (data)),该函数在/include/linux/device.h中定义,见本函数下面

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;

}

下面是/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;

};

这是dev_set_drvdata()的函数定义:

static inline void dev_set_drvdata(struct device *dev, void *data)

{

dev->driver_data = data;

}

接下来是在s3c_rtc_probe()函数用到的两个函数s3c_rtc_enable()和s3c_rtc_setfreq()

static void s3c_rtc_enable(struct platform_device *pdev, int en)

{

void __iomem *base = s3c_rtc_base;//__iomem的作用就是为了使编译器更好的优化编译

unsigned int tmp;

if (s3c_rtc_base == NULL)

return;

//en作为参数传递过来如果en==0,关闭电源前的情况

if (!en) {

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);//设置RTCCON寄存器,屏蔽RTC使能,可以参考数据手册中寄存器的相关定义

tmp = readb(base + S3C2410_TICNT);

writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);//设置TICNT寄存器,屏蔽节拍时间中断使能

} else {

/* re-enable the device, and check it is ok */

//en!=0的情况,表示系统复位,重新使能RTC驱动

if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){//RTCCON第0位为0,将其设置为1,重新使能

dev_info(&pdev->dev, "rtc disabled, re-enabling\n");

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);

}

if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){

dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);//设置RTCCON第2位为0,设置BCD计数为混合BCD计数

}

if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){

dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);//RTC时钟计数器复位

}

}

}

static int s3c_rtc_setfreq(struct device *dev, int freq)//设定节拍时间计数值

{

unsigned int tmp;

spin_lock_irq(&s3c_rtc_pie_lock);//获取自旋锁,对资源互斥访问

tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;//节拍时间使能有效

tmp |= (128 / freq)-1;

writeb(tmp, s3c_rtc_base + S3C2410_TICNT);

spin_unlock_irq(&s3c_rtc_pie_lock);//解锁

return 0;

}

接下来是RTC设备类的操作。

下面是rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体,类似字符设备在驱动中的file_operations对字符设备进行操作的意思。该结构体被定义 在rtc.h中,对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等,该结构体内接口函数的实现都在下面

static const struct rtc_class_ops s3c_rtcops = {

.open= s3c_rtc_open,

.release= s3c_rtc_release,

.read_time= s3c_rtc_gettime,

.set_time= s3c_rtc_settime,

.irq_set_freq= s3c_rtc_setfreq,

.irq_set_state= s3c_rtc_setpie,

};

RTC打开设备函数s3c_rtc_open()

static int s3c_rtc_open(struct device *dev)

{

struct platform_device *pdev = to_platform_device(dev);//从平台设备中获取RTC设备类的数据

struct rtc_device *rtc_dev = platform_get_drvdata(pdev);

int ret;

ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,

IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);//申请中断

if (ret) {

dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);

goto tick_err;

}

tick_err:

return ret;

}

RTC TICK节拍时间中断服务程序

static irqreturn_t s3c_rtc_tickirq(int irq, void *id)

{

struct rtc_device *rdev = id;

rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);

return IRQ_HANDLED;

}

RTC关闭设备函数s3c_rtc_release()

static void s3c_rtc_release(struct device *dev)

{

struct platform_device *pdev = to_platform_device(dev);//从平台设备中获取RTC设备类的数据

struct rtc_device *rtc_dev = platform_get_drvdata(pdev);

/* do not clear AIE here, it may be needed for wake */

s3c_rtc_setpie(dev, 0);//函数定义见下面

free_irq(s3c_rtc_tickno, rtc_dev);

}

s3c_rtc_setpie()函数,该函数主要作用就是根据参数设置TICNT寄存器的最高位,参数为0,禁止使能,参数为1,使能

static int s3c_rtc_setpie(struct device *dev, int enabled)

{

unsigned int tmp;

pr_debug("%s: pie=%d\n", __func__, enabled);

spin_lock_irq(&s3c_rtc_pie_lock);

tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;//读取TICNT的值并将最高位清0

if (enabled)

tmp |= S3C2410_TICNT_ENABLE;

writeb(tmp, s3c_rtc_base + S3C2410_TICNT);//写入计算后新的值

spin_unlock_irq(&s3c_rtc_pie_lock);

return 0;

}

下面两个函数是设置和读取BCD寄存器的时间,逻辑很简单,只是读取和设置相应寄存器的值

static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)

{

unsigned int have_retried = 0;

void __iomem *base = s3c_rtc_base;

retry_get_time:

rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);

rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);

rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);

rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);

rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);

rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);

/* the only way to work out wether the system was mid-update

* when we read it is to check the second counter, and if it

* is zero, then we re-try the entire read

*/

if (rtc_tm->tm_sec == 0 && !have_retried) {

have_retried = 1;

goto retry_get_time;

}

pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",

rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,

rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);

rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);

rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);

rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);

rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);

rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);

rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);

rtc_tm->tm_year += 100;

rtc_tm->tm_mon -= 1;

return 0;

}

static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)

{

void __iomem *base = s3c_rtc_base;

int year = tm->tm_year - 100;

pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",

tm->tm_year, tm->tm_mon, tm->tm_mday,

tm->tm_hour, tm->tm_min, tm->tm_sec);

/* we get around y2k by simply not supporting it */

if (year < 0 || year >= 100) {

dev_err(dev, "rtc only supports 100 years\n");

return -EINVAL;

}

writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC);

writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN);

writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);

writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);

writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);

writeb(bin2bcd(year), base + S3C2410_RTCYEAR);

return 0;

}

到这里RTC驱动的计时功能实现,报警功能还没有完成。下面是这个驱动源代码

#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct resource *s3c_rtc_mem;

static void __iomem *s3c_rtc_base;

static int s3c_rtc_tickno = NO_IRQ;

static DEFINE_SPINLOCK(s3c_rtc_pie_lock);

static irqreturn_t s3c_rtc_tickirq(int irq, void *id)

{

struct rtc_device *rdev = id;

rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);

return IRQ_HANDLED;

}

/* Update control registers */

static void s3c_rtc_setaie(int to)

{

unsigned int tmp;

pr_debug("%s: aie=%d\n", __func__, to);

tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;

if (to)

tmp |= S3C2410_RTCALM_ALMEN;

writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);

}

static int s3c_rtc_setpie(struct device *dev, int enabled)

{

unsigned int tmp;

pr_debug("%s: pie=%d\n", __func__, enabled);

spin_lock_irq(&s3c_rtc_pie_lock);

tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;

if (enabled)

tmp |= S3C2410_TICNT_ENABLE;

writeb(tmp, s3c_rtc_base + S3C2410_TICNT);

spin_unlock_irq(&s3c_rtc_pie_lock);

return 0;

}

static int s3c_rtc_setfreq(struct device *dev, int freq)

{

unsigned int tmp;

spin_lock_irq(&s3c_rtc_pie_lock);

tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;

tmp |= (128 / freq)-1;

writeb(tmp, s3c_rtc_base + S3C2410_TICNT);

spin_unlock_irq(&s3c_rtc_pie_lock);

return 0;

}

/* Time read/write */

static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)

{

unsigned int have_retried = 0;

void __iomem *base = s3c_rtc_base;

retry_get_time:

rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);

rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);

rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);

rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);

rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);

rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);

/* the only way to work out wether the system was mid-update

* when we read it is to check the second counter, and if it

* is zero, then we re-try the entire read

*/

if (rtc_tm->tm_sec == 0 && !have_retried) {

have_retried = 1;

goto retry_get_time;

}

pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",

rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,

rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);

rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);

rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);

rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);

rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);

rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);

rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);

rtc_tm->tm_year += 100;

rtc_tm->tm_mon -= 1;

return 0;

}

static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)

{

void __iomem *base = s3c_rtc_base;

int year = tm->tm_year - 100;

pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",

tm->tm_year, tm->tm_mon, tm->tm_mday,

tm->tm_hour, tm->tm_min, tm->tm_sec);

/* we get around y2k by simply not supporting it */

if (year < 0 || year >= 100) {

dev_err(dev, "rtc only supports 100 years\n");

return -EINVAL;

}

writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC);

writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN);

writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR);

writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE);

writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON);

writeb(bin2bcd(year), base + S3C2410_RTCYEAR);

return 0;

}

static int s3c_rtc_open(struct device *dev)

{

struct platform_device *pdev = to_platform_device(dev);

struct rtc_device *rtc_dev = platform_get_drvdata(pdev);

int ret;

ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,

IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);

if (ret) {

dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);

goto tick_err;

}

tick_err:

return ret;

}

static void s3c_rtc_release(struct device *dev)

{

struct platform_device *pdev = to_platform_device(dev);

struct rtc_device *rtc_dev = platform_get_drvdata(pdev);

/* do not clear AIE here, it may be needed for wake */

s3c_rtc_setpie(dev, 0);

free_irq(s3c_rtc_tickno, rtc_dev);

}

static const struct rtc_class_ops s3c_rtcops = {

.open= s3c_rtc_open,

.release= s3c_rtc_release,

.read_time= s3c_rtc_gettime,

.set_time= s3c_rtc_settime,

.irq_set_freq= s3c_rtc_setfreq,

.irq_set_state= s3c_rtc_setpie,

};

static void s3c_rtc_enable(struct platform_device *pdev, int en)

{

void __iomem *base = s3c_rtc_base;

unsigned int tmp;

if (s3c_rtc_base == NULL)

return;

if (!en) {

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);

tmp = readb(base + S3C2410_TICNT);

writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);

} else {

/* re-enable the device, and check it is ok */

if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){

dev_info(&pdev->dev, "rtc disabled, re-enabling\n");

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);

}

if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){

dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);

}

if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){

dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");

tmp = readb(base + S3C2410_RTCCON);

writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);

}

}

}

static int __devexit s3c_rtc_remove(struct platform_device *dev)

{

struct rtc_device *rtc = platform_get_drvdata(dev);

platform_set_drvdata(dev, NULL);

rtc_device_unregister(rtc);

s3c_rtc_setpie(&dev->dev, 0);

s3c_rtc_setaie(0);

iounmap(s3c_rtc_base);

release_resource(s3c_rtc_mem);

kfree(s3c_rtc_mem);

return 0;

}

static int __devinit s3c_rtc_probe(struct platform_device *pdev)

{

struct rtc_device *rtc;

struct resource *res;

int ret;

pr_debug("%s: probe=%p\n", __func__, pdev);

/* find the IRQs */

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;

}

/* get the memory region */

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;

}

/* check to see if everything is setup correctly */

s3c_rtc_enable(pdev, 1);

pr_debug("s3c2410_rtc: RTCCON=%02x\n",

readb(s3c_rtc_base + S3C2410_RTCCON));

s3c_rtc_setfreq(&pdev->dev, 1);

/* register RTC and exit */

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;

}

rtc->max_user_freq = 128;

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;

}

static struct platform_driver s3c2410_rtc_driver = {

.probe= s3c_rtc_probe,

.remove= __devexit_p(s3c_rtc_remove),

.driver= {

.name= "s3c2410-rtc",

.owner= THIS_MODULE,

},

};

static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";

static int __init s3c_rtc_init(void)

{

printk(banner);

return platform_driver_register(&s3c2410_rtc_driver);

}

static void __exit s3c_rtc_exit(void)

{

platform_driver_unregister(&s3c2410_rtc_driver);

}

module_init(s3c_rtc_init);

module_exit(s3c_rtc_exit);

MODULE_DESCRIPTION("My s3c2440 RTC Driver");

MODULE_AUTHOR("YanMing - yming0221@gmail.com");

MODULE_LICENSE("GPL");

MODULE_ALIAS("platform:s3c2410-rtc");

Makefile文件

obj-m := rtc.o

KERNELDIR ?= /arm/linux-2.6.28.7-2440

PWD := $(shell pwd)

default:

$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:

rm -f *.o *.ko *.order *.symvers

make后在目录下生成rtc.ko驱动,利用NFS挂在到目标板,insmod rtc.ko驱动就可以加载,执行hwclock命令,查看是否可以读取硬件的RTC。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值