ARMLinux驱动RTC(实时时钟)驱动分析

发布者:创意梦者最新更新时间:2016-07-08 来源: eefocus关键字:ARM  Linux  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模块的电路图

ARMLinux驱动RTC(实时时钟)驱动分析
 

3、RTC寄存器介绍

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

ARMLinux驱动RTC(实时时钟)驱动分析
 

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

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

ARMLinux驱动RTC(实时时钟)驱动分析

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

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

ARMLinux驱动RTC(实时时钟)驱动分析
 

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。
关键字:ARM  Linux  RTC  实时时钟 引用地址:ARMLinux驱动RTC(实时时钟)驱动分析

上一篇:ARM寄存器地址定义
下一篇:STM32F4——RTC实时时钟

推荐阅读最新更新时间:2024-03-16 14:59

Arm推出新一代数据中心芯片技术Neoverse V2
北京时间9月15日早间消息,据报道,5G数据和联网设备出现爆发式增长,为了应对需求,Arm公司宣布推出下一代数据中心芯片技术,名叫Neoverse V2。    Arm开发技术,形成知识产权,然后授权给其它企业使用。现在大多手机都用到了Arm技术,除此之外它还在向数据中心市场挺进,以前该市场一直被AMD、英特尔统治。    Arm称,Ampere、亚马逊、富士通和阿里巴巴已经用Arm技术开发数据中心处理器,Neoverse V2可以提升能效,但它没有提供具体数据。    Arm产品解决方案副总裁Dermot O'Driscoll谈及新品时说:“我们的合作伙伴接受新技术,它们会根据自己关注的特定领域开发非常高效的解决方案。所
[半导体设计/制造]
ARM平台交叉编译|OpenSSL
编译步骤 准备 编译平台OS:Ubunti16.04 交叉编译器Compiler:aarch64-linux-gnu-gcc Openssl:openssl-3.0.1 编译 配置 ./Configure --prefix=/dir/to/install --cross-compile-prefix=aarch64-linux-gnu- -no-asm shared prefix:openssl安装目录 –cross-compile-prefix:交叉编译器前缀 no-asm:不使用汇编代码。由于是交叉编译,如不使用该选项,会出现汇编指令不识别的问题,因为openssl默认使用的汇编指令为X86平台下的指令,而一般的交叉编译器
[单片机]
Ubuntu 安装arm-linux-gcc编译器
/********************************************************************************** * Ubuntu 安装arm-linux-gcc编译器 * 说明: * Ubuntu下直接安装arm-linux-gcc编译器,省得去配置那些环境。 * * 2017-3-21 深圳 南山平山村 曾剑锋 *********************************************************************************/ 一、参考文档:
[单片机]
OK6410A学习笔记二:嵌入式Linux驱动开发环境的配置和测试
环境配置: Window7 32-bit OS + VMware Workstation 6.5 + Ubuntu9.10 FORLINX OK6410A开发板 嵌入式Linux驱动开发环境的搭建 1. 安装arm-linux-gcc交叉编译器,并设置环境变量 2. 将飞凌提供的FORLINX_linux-3.0.1.tar.gz拷贝到/usr/src,解压并进入linux-3.0.1目录 3. 执行以下命令:#make oldconfig; #make prepare; 这两个命令用来测试交叉编译器和驱动开发需要的源文件包是否能正常使用,可能会出现提示arm-linux-gcc找
[单片机]
OK6410A学习笔记二:嵌入式<font color='red'>Linux</font>驱动开发环境的配置和测试
ARM处理器的选型原则
鉴于ARM微处理器的众多优点,随着国内外嵌入式应用领域的逐步发展,ARM微处理器必然会获得广泛的重视和应用。但是,由于ARM微处理器有多达十几种的内核结构,几十个芯片生产厂家,以及千变万化的内部功能配置组合,给开发人员在选择方案时带来一定的困难,所以,对ARM芯片做一些对比研究是十分必要的。 以下从应用的角度出发,对在选择ARM微处理器时所应考虑的主要问题做一些简要的探讨。 (1)ARM微处理器内核的选择 从前面所介绍的内容可知,ARM微处理器包含一系列的内核结构,以适应不同的应用领域,用户如果希望使用WinCE或标准Linux等操作系统以减少软件开发时间,就需要选择ARM720T以上带有MMU(Memory Manage
[单片机]
瑞萨R-Car入门套件提供汽车级Linux标准参考平台
瑞萨电子(Renesas)宣布推出汽车级Linux(Automotive Grade Linux, AGL),采用瑞萨R-Car系统单芯片的入门套件,做为软件开发的标准参考平台。 AGL是一项协力合作的开放原始码计划,将汽车制造商、供货商及技术公司集合在一起,为汽车应用打造以Linux为基础的开放式软件平台,并可当做业界标准,采用瑞萨R-Car入门套件,协助软件开发者容易取得硬件环境,以执行此计划所开发的软件,并快速轻松开发新一代联网汽车的车内信息娱乐(IVI)应用软件。 瑞萨汽车信息解决方案事业部副总裁Masahiro Suzuki表示,AGL计划致力于建立业界标准的先进开放原始码软件,该公司很荣幸R-Car入门套件能获得AGL
[半导体设计/制造]
祭出Cortex-A7 ARM力拱百美元智能手机
行动处理器矽智财(IP)供应商安谋国际(ARM)日前推出新款Cortex-A7 MPCore处理器核心,可较Coretex-A8提供更好的效能,但耗电量与尺寸仅为其五分一,期以更优异的功耗/效能比和使用者体验,加速100美元以下的入门款智慧型手机市场起飞。 安谋国际处理器部门执行副总裁Mike Inglis表示,智慧型手机与平板装置将成为未来使用者主要的运算装置,为因应此一发展趋势,该公司藉由在Cortex-A7处理器中导入创新的big.LITTLE processing技术,重新定义传统处理器功率与效能间的关系,来满足消费者对“永远开机、永不断线”的需求。 据了解,目前市场上许多受欢迎的智慧型手机系采用ARM Cortex-A
[手机便携]
基于ARM的视频安全监控终端的设计与实现
   引言   视频监控系统在工业、军事、民用领域有着广泛的应用,为这些行业的安全防范和环境监控起到了不可忽视的作用。视频监控系统正逐步由模拟化走向数字化,随着半导体技术的飞速发展和多媒体视频编解码技术的日益成熟,高性能、复杂的视频流压缩算法在嵌入式系统中的应用成为了现实。如今监控系统多采用专用处理器或RISC嵌入式处理器与DSP相结合的方法实现,本文探讨的是用ARM处理器与软件压缩相结合的办法实现。    视频监控系统总体设计   首先需要对系统进行总体规划,将系统划分成几个功能模块,确定各个模块的实现方法。整个视频监控系统采用C/S结构,从主体上分为两部分:服务器端和客户端。服务器端主要包括S3C2410平台上运行的采集
[安防电子]
基于<font color='red'>ARM</font>的视频安全监控终端的设计与实现
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved