ARMLinux驅(qū)動(dòng)RTC(實(shí)時(shí)時(shí)鐘)驅(qū)動(dòng)分析
內(nèi)核版本:Linux 2.6.28
本文引用地址:http://m.butianyuan.cn/article/201611/317629.htm主機(jī)平臺(tái):Ubuntu 11.04
內(nèi)核版本:Linux 2.6.39
交叉編譯器版本:arm-linux-gcc 3.4.1
原創(chuàng)作品,轉(zhuǎn)載請(qǐng)標(biāo)明出處http://blog.csdn.net/yming0221/article/details/6584285
1、實(shí)時(shí)時(shí)鐘概述
實(shí)時(shí)時(shí)鐘(RTC)單元可以在斷電的情況下使用紐扣電池繼續(xù)計(jì)時(shí)工作。RTC使用STRB/LDRB ARM操作傳輸二進(jìn)制碼十進(jìn)制數(shù)的8位數(shù)據(jù)給CPU。其中的數(shù)據(jù)包括秒、分、時(shí)、日期、天、月、年的時(shí)間信息??梢詧?zhí)行報(bào)警功能。
2、實(shí)時(shí)時(shí)鐘操作
下面是RTC模塊的電路圖
3、RTC寄存器介紹
實(shí)時(shí)時(shí)鐘控制寄存器(RTCCON)-REAL TIME CLOCK CONTROL REGISTER
節(jié)拍時(shí)間計(jì)數(shù)寄存器(TICNT)-TICK TIME COUNT REGISTER
RTC報(bào)警控制寄存器(RTCALM)-RTC ALARM CONTROL REGISTER
報(bào)警秒數(shù)寄存器(ALMSEC)-ALARM SECOND DATA REGISTER
報(bào)警分鐘計(jì)數(shù)寄存器(ALMMIN)-ALARM MIN DATA REGISTER
報(bào)警小時(shí)數(shù)據(jù)寄存器(ALMHOUR)-ALARM HOUR DATA REGISTER
報(bào)警日期數(shù)據(jù)寄存器(ALMDATE)-ALARM DATE DATA REGISTER
報(bào)警月數(shù)數(shù)據(jù)寄存器(ALMMON)-ALARM MON DATA REGISTER
報(bào)警年數(shù)數(shù)據(jù)寄存器(ALMYEAR)-ALARM YEAR DATA REGISTER
BCD數(shù)據(jù)寄存器的格式和報(bào)警寄存器結(jié)構(gòu)相同,只是對(duì)應(yīng)的地址不同。
BCD秒寄存器(BCDSEC)-BCD SECOND REGISTER 地址:0x57000070(L)0x57000073(B)
BCD分寄存器(BCDMIN)-BCD MINUTE REGISTER 地址:0x57000074(L)0x57000077(B)
BCD小時(shí)寄存器(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、驅(qū)動(dòng)實(shí)例分析
為了使驅(qū)動(dòng)更容易理解,現(xiàn)在這個(gè)RTC驅(qū)動(dòng)只完成了計(jì)時(shí)功能,沒有添加相應(yīng)的報(bào)警功能,也沒有添加電源管理的功能,缺少的功能今后完善。
下面先總體了解驅(qū)動(dòng):
首先是RTC驅(qū)動(dòng)的結(jié)構(gòu)體,在/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;};驅(qū)動(dòng)中定義對(duì)應(yīng)的結(jié)構(gòu)體
static struct platform_driver s3c2410_rtc_driver = {.probe = s3c_rtc_probe,//RTC探測(cè)函數(shù).remove = __devexit_p(s3c_rtc_remove),//RTC移除函數(shù).driver = {.name = "s3c2410-rtc",.owner = THIS_MODULE,},};下面是驅(qū)動(dòng)中驅(qū)動(dòng)的初始化和退出函數(shù)
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()函數(shù)在/drivers/base/platform.c中實(shí)現(xiàn)的。
可以看出,platform_driver_register()函數(shù)的作用就是為platform_driver中的driver中的probe、remove等提供接口函數(shù)
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);//注冊(cè)老的驅(qū)動(dòng)}
void platform_driver_unregister(struct platform_driver *drv){driver_unregister(&drv->driver);}
接下來是RTC平臺(tái)驅(qū)動(dòng)探測(cè)函數(shù)s3c_rtc_probe,下面函數(shù)定義的時(shí)候使用了__devinit的作用是使編譯器優(yōu)化代碼,將其放在和是的內(nèi)存位置,減少內(nèi)存占用和提高內(nèi)核效率。
probe函數(shù)接收到plarform_device這個(gè)參數(shù)后,就需要從中提取出需要的信息。它一般會(huì)通過調(diào)用內(nèi)核提供的platform_get_resource和platform_get_irq等函數(shù)來獲得相關(guān)信息。如通過platform_get_resource獲得設(shè)備的起始地址后,可以對(duì)其進(jìn)行request_mem_region和ioremap等操作,以便應(yīng)用程序?qū)ζ溥M(jìn)行操作。通過platform_get_irq得到設(shè)備的中斷號(hào)以后,就可以調(diào)用request_irq函數(shù)來向系統(tǒng)申請(qǐng)中斷。這些操作在設(shè)備驅(qū)動(dòng)程序中一般都要完成。
static int __devinit s3c_rtc_probe(struct platform_device *pdev){struct rtc_device *rtc;//定義rtc_device結(jié)構(gòu)體,定義在/include/linux/rtc.hstruct resource *res;//定義資源結(jié)構(gòu)體,定義在/include/linux/ioport.hint ret;pr_debug("%s: probe=%pn", __func__, pdev);/* find the IRQs */s3c_rtc_tickno = platform_get_irq(pdev, 1);//在系統(tǒng)定義的平臺(tái)設(shè)備中獲取中斷號(hào)if (s3c_rtc_tickno < 0) {//異常處理dev_err(&pdev->dev, "no irq for rtc tickn");return -ENOENT;}/* get the memory region */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//獲取RTC平臺(tái)使用的IO資源if (res == NULL) {dev_err(&pdev->dev, "failed to get memory region resourcen");return -ENOENT;}//申請(qǐng)內(nèi)存區(qū)域,res是struct resource類型,見本函數(shù)后面s3c_rtc_mem = request_mem_region(res->start,res->end-res->start+1,pdev->name);if (s3c_rtc_mem == NULL) {//申請(qǐng)內(nèi)存出錯(cuò)dev_err(&pdev->dev, "failed to reserve memory regionn");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);//對(duì)RTCCON寄存器設(shè)置,詳情見下面的函數(shù)實(shí)現(xiàn)pr_debug("s3c2410_rtc: RTCCON=%02xn",readb(s3c_rtc_base + S3C2410_RTCCON));s3c_rtc_setfreq(&pdev->dev, 1);//詳情見下面的函數(shù)實(shí)現(xiàn)/* register RTC and exit */rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,THIS_MODULE);//注冊(cè)RTC為RTC設(shè)備,其中s3c_rtcops定義見下if (IS_ERR(rtc)) {dev_err(&pdev->dev, "cannot attach rtcn");ret = PTR_ERR(rtc);goto err_nortc;}rtc->max_user_freq = 128;//設(shè)置RTC節(jié)拍時(shí)間計(jì)數(shù)寄存器TICNT的節(jié)拍時(shí)間計(jì)數(shù)值的用戶最大相對(duì)值//將RTC類的設(shè)備數(shù)據(jù)傳遞給系統(tǒng)設(shè)備,在/include/linux/platform_device.h中
//#define platform_set_drvdata(_dev,data) dev_set_drvdata(&(_dev)->dev, (data)),該函數(shù)在/include/linux/device.h中定義,見本函數(shù)下面
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結(jié)構(gòu)體定義
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;struct resource *parent, *sibling, *child;};這是dev_set_drvdata()的函數(shù)定義:
static inline void dev_set_drvdata(struct device *dev, void *data){dev->driver_data = data;}接下來是在s3c_rtc_probe()函數(shù)用到的兩個(gè)函數(shù)s3c_rtc_enable()和s3c_rtc_setfreq()
static void s3c_rtc_enable(struct platform_device *pdev, int en){
void __iomem *base = s3c_rtc_base;//__iomem的作用就是為了使編譯器更好的優(yōu)化編譯unsigned int tmp;if (s3c_rtc_base == NULL)return;//en作為參數(shù)傳遞過來如果en==0,關(guān)閉電源前的情況if (!en) {tmp = readb(base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);//設(shè)置RTCCON寄存器,屏蔽RTC使能,可以參考數(shù)據(jù)手冊(cè)中寄存器的相關(guān)定義tmp = readb(base + S3C2410_TICNT);writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);//設(shè)置TICNT寄存器,屏蔽節(jié)拍時(shí)間中斷使能} else {/* re-enable the device, and check it is ok *///en!=0的情況,表示系統(tǒng)復(fù)位,重新使能RTC驅(qū)動(dòng)if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){//RTCCON第0位為0,將其設(shè)置為1,重新使能dev_info(&pdev->dev, "rtc disabled, re-enablingn");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_CNTSELn");tmp = readb(base + S3C2410_RTCCON);writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);//設(shè)置RTCCON第2位為0,設(shè)置BCD計(jì)數(shù)為混合BCD計(jì)數(shù)}if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){dev_info(&pdev->dev, "removing RTCCON_CLKRSTn");tmp = readb(base + S3C2410_RTCCON);writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);//RTC時(shí)鐘計(jì)數(shù)器復(fù)位}}}
static int s3c_rtc_setfreq(struct device *dev, int freq)//設(shè)定節(jié)拍時(shí)間計(jì)數(shù)值{unsigned int tmp;spin_lock_irq(&s3c_rtc_pie_lock);//獲取自旋鎖,對(duì)資源互斥訪問tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;//節(jié)拍時(shí)間使能有效tmp |= (128 / freq)-1;writeb(tmp, s3c_rtc_base + S3C2410_TICNT);spin_unlock_irq(&s3c_rtc_pie_lock);//解鎖return 0;}接下來是RTC設(shè)備類的操作。
下面是rtc_class_ops是RTC設(shè)備類在RTC驅(qū)動(dòng)核心部分中定義的對(duì)RTC設(shè)備類進(jìn)行操作的結(jié)構(gòu)體,類似字符設(shè)備在驅(qū)動(dòng)中的file_operations對(duì)字符設(shè)備進(jìn)行操作的意思。該結(jié)構(gòu)體被定義在rtc.h中,對(duì)RTC的操作主要有打開、關(guān)閉、設(shè)置或獲取時(shí)間、設(shè)置或獲取報(bào)警、設(shè)置節(jié)拍時(shí)間計(jì)數(shù)值等等,該結(jié)構(gòu)體內(nèi)接口函數(shù)的實(shí)現(xiàn)都在下面
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打開設(shè)備函數(shù)s3c_rtc_open()
static int s3c_rtc_open(struct device *dev){struct platform_device *pdev = to_platform_device(dev);//從平臺(tái)設(shè)備中獲取RTC設(shè)備類的數(shù)據(jù)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);//申請(qǐng)中斷if (ret) {dev_err(dev, "IRQ%d error %dn", s3c_rtc_tickno, ret);goto tick_err;}tick_err:return ret;}RTC TICK節(jié)拍時(shí)間中斷服務(wù)程序
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關(guān)閉設(shè)備函數(shù)s3c_rtc_release()
static void s3c_rtc_release(struct device *dev){struct platform_device *pdev = to_platform_device(dev);//從平臺(tái)設(shè)備中獲取RTC設(shè)備類的數(shù)據(jù)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);//函數(shù)定義見下面free_irq(s3c_rtc_tickno, rtc_dev);}s3c_rtc_setpie()函數(shù),該函數(shù)主要作用就是根據(jù)參數(shù)設(shè)置TICNT寄存器的最高位,參數(shù)為0,禁止使能,參數(shù)為1,使能
static int s3c_rtc_setpie(struct device *dev, int enabled){unsigned int tmp;pr_debug("%s: pie=%dn", __func__, enabled);spin_lock_irq(&s3c_rtc_pie_lock);tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;//讀取TICNT的值并將最高位清0if (enabled)tmp |= S3C2410_TICNT_ENABLE;writeb(tmp, s3c_rtc_base + S3C2410_TICNT);//寫入計(jì)算后新的值spin_unlock_irq(&s3c_rtc_pie_lock);return 0;}下面兩個(gè)函數(shù)是設(shè)置和讀取BCD寄存器的時(shí)間,邏輯很簡(jiǎn)單,只是讀取和設(shè)置相應(yīng)寄存器的值
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/%02xn",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/%02dn",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 yearsn");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驅(qū)動(dòng)的計(jì)時(shí)功能實(shí)現(xiàn),報(bào)警功能還沒有完成。下面是這個(gè)驅(qū)動(dòng)源代碼
#include#include #include #include #include #include interrupt.h>#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=%dn", __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=%dn", __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/%02xn",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/%02dn",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 yearsn");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 %dn", 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-enablingn");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_CNTSELn");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_CLKRSTn");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=%pn", __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 tickn");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 resourcen");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 regionn");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=%02xn",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 rtcn");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 Electronicsn";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.oKERNELDIR ?= /arm/linux-2.6.28.7-2440PWD := $(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:rm -f *.o *.ko *.order *.symvers
make后在目錄下生成rtc.ko驅(qū)動(dòng),利用NFS掛在到目標(biāo)板,insmod rtc.ko驅(qū)動(dòng)就可以加載,執(zhí)行hwclock命令,查看是否可以讀取硬件的RTC。
評(píng)論