RTC实时时钟驱动
RTC(Real-Time Clock)實(shí)時(shí)時(shí)鐘為操作系統(tǒng)提供了一個(gè)可靠的時(shí)間,并且在斷電的情況下,RTC實(shí)時(shí)時(shí)鐘也可以通過(guò)電池供電,一直運(yùn)行下去。
RTC通過(guò)STRB/LDRB這兩個(gè)ARM指令向CPU傳送8位數(shù)據(jù)(BCD碼)。數(shù)據(jù)包括秒,分,小時(shí),日期,天,月和年。RTC實(shí)時(shí)時(shí)鐘依靠一個(gè)外部的32.768Khz的石英晶體,產(chǎn)生周期性的脈沖信號(hào)。每一個(gè)信號(hào)到來(lái)時(shí),計(jì)數(shù)器就加1,通過(guò)這種方式,完成計(jì)時(shí)功能。
?
RTC實(shí)時(shí)時(shí)鐘有如下一些特性:
1,BCD數(shù)據(jù):這些數(shù)據(jù)包括秒、分、小時(shí)、日期、、星期幾、月和年。
2,閏年產(chǎn)生器
3,報(bào)警功能:報(bào)警中斷或者從掉電模式喚醒
4,解決了千年蟲問(wèn)題??? (詳見http://baike.baidu.com/view/9349.htm)
5,獨(dú)立電源引腳RTCVDD
6,支持ms中斷作為RTOS內(nèi)核時(shí)鐘
7,循環(huán)復(fù)位(round reset)功能
?
?
如圖,RTC實(shí)時(shí)時(shí)鐘的框架圖,XTIrtc和XTOrtc產(chǎn)生脈沖信號(hào),即外部晶振。傳給2^15的一個(gè)時(shí)鐘分頻器,得到一個(gè)128Hz的頻率,這個(gè)頻率用來(lái)產(chǎn)生滴答計(jì)數(shù)。當(dāng)時(shí)鐘計(jì)數(shù)為0時(shí),產(chǎn)生一個(gè)TIME TICK中斷信號(hào)。時(shí)鐘控制器用來(lái)控制RTC實(shí)時(shí)時(shí)鐘的功能。復(fù)位寄存器用來(lái)重置SEC和MIN寄存器。閏年發(fā)生器用來(lái)產(chǎn)生閏年邏輯。報(bào)警發(fā)生器用來(lái)控制是否產(chǎn)生報(bào)警信號(hào)。
?
1,閏年產(chǎn)生器:
閏年產(chǎn)生器可以基于BCDDATE,BCDMON,BCDYEAR決定每月最后一天的日期是28、29、30、31.一個(gè)8位計(jì)數(shù)器只能表示兩位BCD碼,每一位BCD碼由4位表示。因此不能支持。因此不能決定00年是否為閏年,例如不能區(qū)別1900和2000年。RTC模塊通過(guò)硬件邏輯支持2000年為閏年。因此這兩位00指的是2000,而不是1900
2,后備電池:
即使系統(tǒng)電源關(guān)閉,RTC模塊可以由后備電池通過(guò)RTCVDD引腳供電。當(dāng)系統(tǒng)電源關(guān)閉時(shí),CPU和RTC的接口應(yīng)該被阻塞,后備電池應(yīng)該只驅(qū)動(dòng)晶振電路和BCD計(jì)數(shù)器,以消耗最少的電池。
3,報(bào)警功能:
在正常模式和掉電模式下,RTC在指定的時(shí)刻會(huì)產(chǎn)生一個(gè)報(bào)警信號(hào)。正常模式下,報(bào)警中斷ALMINT有效,對(duì)應(yīng)INT_RTC引腳。掉電模式下,報(bào)警中斷ALMINT有效外還產(chǎn)生一個(gè)喚醒信號(hào)PMWKUP,對(duì)應(yīng)PMWKUP引腳。RTC報(bào)警寄存器RTCALM決定是否使能報(bào)警狀態(tài)和設(shè)置報(bào)警條件
?
RTC工作原理上網(wǎng)查一下,很多。而且不同板子的RTC寄存器也不同,這里以S3C2440為例
?
下面是RTC實(shí)時(shí)時(shí)鐘構(gòu)架:
 ? 與RTC核心有關(guān)的文件有:
 ? ? ? ? /drivers/rtc/class.c ? ? ? ? ?這個(gè)文件向linux設(shè)備模型核心注冊(cè)了一個(gè)類RTC,然后向驅(qū)動(dòng)程序提供了注冊(cè)/注銷接口
 ? ? ? ? /drivers/rtc/rtc-dev.c ? ? ? 這個(gè)文件定義了基本的設(shè)備文件操作函數(shù),如:open,read等
 ? ? ? ? /drivers/rtc/interface.c ? ? 顧名思義,這個(gè)文件主要提供了用戶程序與RTC驅(qū)動(dòng)的接口函數(shù),用戶程序一般通過(guò)ioctl與RTC驅(qū)動(dòng)交互,這里定義了每個(gè)ioctl命令需要調(diào)用的函數(shù)
 ? ? ? ? /drivers/rtc/rtc-sysfs.c ? ? 與sysfs有關(guān)
 ? ? ? ? /drivers/rtc/rtc-proc.c ? ? ?與proc文件系統(tǒng)有關(guān)
 ? ? ? ? /include/linux/rtc.h ? ? ? ? 定義了與RTC有關(guān)的數(shù)據(jù)結(jié)構(gòu)
?
?
 static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";??? //標(biāo)志語(yǔ)
 
 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);
?
void platform_driver_unregister(struct platfort_driver *drv)
{
driver_unregister(&drv->driver);
}
?
RTC實(shí)時(shí)時(shí)鐘的平臺(tái)驅(qū)動(dòng)設(shè)備定義:
 static struct platform_driver s3c2410_rtc_driver = {
 ?? ?.probe?? ??? ?= s3c_rtc_probe,???????????????????????????????????????????????????????????????????? //RTC探測(cè)函數(shù)
 ?? ?.remove?? ??? ?= __devexit_p(s3c_rtc_remove),????????????????????????????????????????? //RTC移除函數(shù)
 ?? ?.suspend?? ?= s3c_rtc_suspend,???????????????????????????????????????????????????????????????? //RTC掛起函數(shù)
 ?? ?.resume?? ??? ?= s3c_rtc_resume,??????????????????????????????????????????????????????????????? //RTC恢復(fù)函數(shù)
 ?? ?.driver?? ??? ?= {
 ?? ??? ?.name?? ?= "s3c2410-rtc",????????????????????????????????????????????????????????????????????? //驅(qū)動(dòng)名字
 ?? ??? ?.owner?? ?= THIS_MODULE,???????????????????????????????????????????????????????????????? //驅(qū)動(dòng)模塊
 ?? ?},
 };
?
當(dāng)調(diào)用plat_driver_register()函數(shù)注冊(cè)驅(qū)動(dòng)以后,會(huì)觸發(fā)平臺(tái)設(shè)備和驅(qū)動(dòng)的匹配函數(shù)platform_match()。匹配成功,則會(huì)調(diào)用平臺(tái)驅(qū)動(dòng)中的probe()函數(shù),RTC實(shí)時(shí)時(shí)鐘驅(qū)動(dòng)中對(duì)應(yīng)的函數(shù)就是s3c_rtc_probe()。主要任務(wù)有以下:(請(qǐng)參考下面源代碼)
1,讀取平臺(tái)設(shè)備的資源結(jié)構(gòu)體s3c_rtc_resource中的第二個(gè)中斷號(hào),即滴答中斷號(hào)
2,讀取平臺(tái)設(shè)備的資源結(jié)構(gòu)體s3c_rtc_resource中的第一個(gè)中斷號(hào),即報(bào)警中斷號(hào)
3,將RTC實(shí)時(shí)時(shí)鐘的寄存器映射為虛擬地址,返回虛擬基地址
4,重新打開RTC實(shí)時(shí)時(shí)鐘,通過(guò)調(diào)用s3c_rtc_enable()函數(shù)
5,設(shè)置RTC滴答中斷間隔,并打開RTC滴答中斷
6,調(diào)用rtc_device_register()函數(shù)注冊(cè)RTC并退出,返回struct rtc_device 結(jié)構(gòu)體
7,設(shè)置平臺(tái)設(shè)備驅(qū)動(dòng)的驅(qū)動(dòng)數(shù)據(jù)dev->driver_dat為struct rtc_device指針
?
 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);??? //1代表第二個(gè)中斷? 這里被賦值46
 ?? ?if (s3c_rtc_tickno < 0) {
 ?? ??? ?dev_err(&pdev->dev, "no irq for rtc tick\n");
 ?? ??? ?return -ENOENT;
 ?? ?}
 
 ?? ?s3c_rtc_alarmno = platform_get_irq(pdev, 0); //0代表第一個(gè)中斷,這里被賦值24
 ?? ?if (s3c_rtc_alarmno < 0) {
 ?? ??? ?dev_err(&pdev->dev, "no irq for alarm\n");
 ?? ??? ?return -ENOENT;
 ?? ?}
 
 ?? ?pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
 ?? ??? ? s3c_rtc_tickno, s3c_rtc_alarmno);
 
 ?? ?/* 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);
 
 ?? ?device_init_wakeup(&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;
 }
RTC實(shí)時(shí)時(shí)鐘設(shè)備由結(jié)構(gòu)體struct rtc_device 表示
 struct rtc_device
 {
 ?? ?struct device dev;???????????????????????????????????????????????????????????????????????? //內(nèi)嵌設(shè)備結(jié)構(gòu)體
 ?? ?struct module *owner;??????????????????????????????????????        ? ?? //指向自身所在的模塊
 
 ?? ?int id;??????????????????????????????????????????????????????????????????????????????????????????? //設(shè)備的ID號(hào)
 ?? ?char name[RTC_DEVICE_NAME_SIZE];???????????????????????????????????? //RTC名字
 
 ?? ?const struct rtc_class_ops *ops;??????????????????????????????????????????????? //類操作函數(shù)集
 ?? ?struct mutex ops_lock;??????????????????????????????????????????????????????????????? //互斥鎖
 
 ?? ?struct cdev char_dev;?????????????????????????????????????????????????????????????? //內(nèi)嵌一個(gè)字符設(shè)備
 ?? ?unsigned long flags;?????????????????????????????????????????????????????????????????? //RTC狀態(tài)標(biāo)志
 
 ?? ?unsigned long irq_data;??????????????????????????????????????????????????????????? //中斷數(shù)據(jù)
 ?? ?spinlock_t irq_lock;???????????????????????????????????????????????????????????????? //中斷自旋鎖
 ?? ?wait_queue_head_t irq_queue;?????????????????????????????????????????? //中斷等待隊(duì)列頭
 ?? ?struct fasync_struct *async_queue;????????????????????????????????????? //異步隊(duì)列
 
 ?? ?struct rtc_task *irq_task;?????????????????????????????????????????????????????? //RTC的任務(wù)結(jié)構(gòu)體
 ?? ?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;T
 #endif
 };
?
RTC平臺(tái)設(shè)備結(jié)構(gòu)體:
 struct platform_device s3c_device_rtc = {
 ?? ?.name?? ??? ?? = "s3c2410-rtc",
 ?? ?.id?? ??? ?? = -1,
 ?? ?.num_resources?? ?? = ARRAY_SIZE(s3c_rtc_resource),
 ?? ?.resource?? ?? = s3c_rtc_resource,
 };
?
s3c2440處理器的RTC資源如下代碼:
 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
 ?? ?}
 };
?
RTC實(shí)時(shí)時(shí)鐘的使能函數(shù)s3c_rtc_enable()
RTC實(shí)時(shí)時(shí)鐘可以設(shè)置相應(yīng)的寄存器來(lái)控制實(shí)時(shí)時(shí)鐘的狀態(tài)。這些狀態(tài)包括使實(shí)時(shí)時(shí)鐘開始工作,也包括使實(shí)時(shí)時(shí)鐘停止工作。s3c_rtc_enable()函數(shù)用來(lái)設(shè)置實(shí)時(shí)時(shí)鐘的工作狀態(tài)。第一個(gè)參數(shù)是RTC的平臺(tái)設(shè)備指針,第二個(gè)參數(shù)是使能標(biāo)志en,en等于0時(shí),表示實(shí)時(shí)時(shí)鐘停止工作,en不等于0時(shí),表示實(shí)時(shí)時(shí)鐘開始工作。
?
 static void s3c_rtc_enable(struct platform_device *pdev, int en)
 {
 ?? ?void __iomem *base = s3c_rtc_base;?????????????? //將虛擬地址s3c_rtc_base賦給base指針
 ?? ?unsigned int tmp;??????????????????????????????????????????????
 
 ?? ?if (s3c_rtc_base == NULL)?????????????????????????????? //如果為空,則返回。這表示沒有成功申請(qǐng)到內(nèi)存,設(shè)備驅(qū)動(dòng)退出
 ?? ??? ?return;
 
 ?? ?if (!en) {????????????????????????????????????????????????????????????? //如果en等于0,表示不允許RTC實(shí)時(shí)時(shí)鐘工作,這時(shí),需要RTCCON寄存器的最低位置0,表示不允許實(shí)時(shí)時(shí)鐘計(jì)數(shù)。同時(shí),需???
 ??????????????????????????????????????????????????????????????????????????????? 要將TICNT寄存器的最高位置為0,表示不允許實(shí)時(shí)時(shí)鐘產(chǎn)生報(bào)警中斷
 ?? ??? ?tmp = readb(base + S3C2410_RTCCON);
 ?? ??? ?writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);???????? //不允許實(shí)時(shí)時(shí)鐘計(jì)數(shù)
 
 ?? ??? ?tmp = readb(base + S3C2410_TICNT);
 ?? ??? ?writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);????????????????? //不允許實(shí)時(shí)時(shí)鐘產(chǎn)生報(bào)警中斷
 ?? ?} else {
 ?? ??? ?/* re-enable the device, and check it is ok */
 
 ?? ??? ?if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){??????????????? //將RTCCON的最低位置為0,使實(shí)時(shí)時(shí)鐘工作起來(lái)
 ?? ??? ??? ?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)){?????????????????????????? //將RTCCON第2位置為0,不使用BCD計(jì)數(shù)選擇器
 ?? ??? ??? ?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)){?????????????????????????? //將RTCCON的第3位置為0,不重新設(shè)置計(jì)數(shù)器
 ?? ??? ??? ?dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
 
 ?? ??? ??? ?tmp = readb(base + S3C2410_RTCCON);
 ?? ??? ??? ?writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);
 ?? ??? ?}
 ?? ?}
 }
 
 
?set_rtc_setfreq()函數(shù)用來(lái)設(shè)置時(shí)鐘脈沖中斷的頻率,即多少時(shí)間產(chǎn)生一次中斷。第一個(gè)參數(shù)表示RTC的設(shè)備結(jié)構(gòu)體,第二個(gè)參數(shù)表示頻率,即多久產(chǎn)生一次中斷。如果freq等于1,則表示1秒鐘產(chǎn)生一次中斷;等于2,表示每秒產(chǎn)生2次中斷
 static int s3c_rtc_setfreq(struct device *dev, int freq)
 {
 ?? ?unsigned int tmp;
 
 ?? ?if (!is_power_of_2(freq))?????????????????????????????? //判斷是不是2的倍數(shù),不是返回
 ?? ??? ?return -EINVAL;
 
 ?? ?spin_lock_irq(&s3c_rtc_pie_lock);
 
 ?? ?tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
 ?? ?tmp |= (128 / freq)-1;?????????????????????????? //時(shí)鐘脈沖1秒中產(chǎn)生128次時(shí)鐘滴答。Period = (n+1) / 128 second????? => freq = 128 / (n+1)???? => n = 128 / freq - 1
 
 ?? ?writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
 ?? ?spin_unlock_irq(&s3c_rtc_pie_lock);
 
 ?? ?return 0;
 }
RTC設(shè)備注冊(cè)函數(shù)rtc_device_register()
rtc實(shí)時(shí)時(shí)鐘設(shè)備必須注冊(cè)到內(nèi)核中才能可以使用。在注冊(cè)設(shè)備的過(guò)程中,將設(shè)備提供的應(yīng)用程序的接口ops也指定到設(shè)備上。這樣,當(dāng)應(yīng)用程序讀取設(shè)備的數(shù)據(jù)時(shí),就可以調(diào)用這些底層的驅(qū)動(dòng)函數(shù)
 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) {?????????????????????????????? //分配一個(gè)ID號(hào),用來(lái)把一個(gè)數(shù)字與一個(gè)指針聯(lián)系起來(lái)
 ?? ??? ?err = -ENOMEM;
 ?? ??? ?goto exit;
 ?? ?}
 
 
 ?? ?mutex_lock(&idr_lock);????????????? //加鎖
 ?? ?err = idr_get_new(&rtc_idr, NULL, &id);??? //得到一個(gè)ID號(hào)
 ?? ?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設(shè)備結(jié)構(gòu)體的相關(guān)成員。將ops操作函數(shù)賦值給ret->ops結(jié)構(gòu)體指針。將用戶可以設(shè)置的最大頻率設(shè)為64
 ?? ?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;
 ????????????????????????????????????? //初始化鎖和設(shè)置設(shè)備的名字
 ?? ?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);????????????????????? //設(shè)置RTC設(shè)備的設(shè)備號(hào)
 
 ?? ?err = device_register(&rtc->dev);????????????????? //向內(nèi)核注冊(cè)實(shí)時(shí)時(shí)鐘設(shè)備
 ?? ?if (err)
 ?? ??? ?goto exit_kfree;
 ???????????????????????????????????????????????????? //下面是向文件系統(tǒng)注冊(cè)設(shè)備,這樣就可以通過(guò)文件系統(tǒng)訪問(wèn)相應(yīng)的設(shè)備
 ?? ?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);
 }
rtc_class_ops是一個(gè)對(duì)設(shè)備進(jìn)行操作的抽象結(jié)構(gòu)體。內(nèi)核允許為設(shè)備建立一個(gè)設(shè)備文件,對(duì)設(shè)備文件的所有操作,就相當(dāng)于對(duì)設(shè)備的操作。這樣的好處是,用戶程序可以使用訪問(wèn)普通文件的方法,來(lái)訪問(wèn)設(shè)備文件,進(jìn)而訪問(wèn)設(shè)備。這樣的方法,極大的減輕了程序員的編程負(fù)擔(dān),程序員不必熟悉新的驅(qū)動(dòng)接口,就能夠訪問(wèn)設(shè)備
 struct rtc_class_ops {
 ?? ?int (*open)(struct device *);??????????????????? //打開一個(gè)設(shè)備,在該函數(shù)中可以對(duì)設(shè)備進(jìn)行初始化。如果這個(gè)函數(shù)被賦值NULL,那么設(shè)備打開永遠(yuǎn)成功,并不會(huì)對(duì)設(shè)備產(chǎn)生影響
 ?? ?void (*release)(struct device *);???????????? //釋放open()函數(shù)中申請(qǐng)的資源。其將在文件引用計(jì)數(shù)為0時(shí),被系統(tǒng)調(diào)用。對(duì)應(yīng)的應(yīng)用程序的close()方法,但并不是每一次調(diào)用close()都會(huì)觸發(fā)release()函數(shù)。其會(huì)在對(duì)設(shè)備文件的所有打開都釋放后,才會(huì)被調(diào)用
 ?? ?int (*ioctl)(struct device *, unsigned int, unsigned long);?? //提供了一種執(zhí)行設(shè)備特定命令的方法。例如,使設(shè)備復(fù)位,既不是讀操作也不是寫操作,不適合用read()和write()方法來(lái)實(shí)現(xiàn)。如果在應(yīng)用程序中給ioctl傳入沒有定義的命令,那么將返回-ENOTTY的錯(cuò)誤,表示設(shè)備不支持這個(gè)命令
 ?? ?int (*read_time)(struct device *, struct rtc_time *);????????????? //讀取RTC設(shè)備的當(dāng)前時(shí)間
 ?? ?int (*set_time)(struct device *, struct rtc_time *);??????????????? //設(shè)置RTC設(shè)備的當(dāng)前時(shí)間
 ?? ?int (*read_alarm)(struct device *, struct rtc_wkalrm *);????????? //讀取RTC設(shè)備的報(bào)警時(shí)間
 ?? ?int (*set_alarm)(struct device *, struct rtc_wkalrm *);????????? //設(shè)置RTC設(shè)備的報(bào)警時(shí)間,當(dāng)時(shí)間到達(dá)時(shí),會(huì)產(chǎn)生中斷信號(hào)
 ?? ?int (*proc)(struct device *, struct seq_file *);??????????????????????? //用來(lái)讀取proc文件系統(tǒng)的數(shù)據(jù)
 ?? ?int (*set_mmss)(struct device *, unsigned long secs);???????????????????
 ?? ?int (*irq_set_state)(struct device *, int enabled);??????????????? //設(shè)置中斷狀態(tài)
 ?? ?int (*irq_set_freq)(struct device *, int freq);??????????????????????? //設(shè)置中斷頻率,最大不能超過(guò)64
 ?? ?int (*read_callback)(struct device *, int data);???????????????????
 ?? ?int (*alarm_irq_enable)(struct device *, unsigned int enabled);??????? //用來(lái)設(shè)置中斷使能狀態(tài)
 ?? ?int (*update_irq_enable)(struct device *, unsigned int enabled);???? //更新中斷使能狀態(tài)
 };
實(shí)時(shí)時(shí)鐘RTC的rtc_class_ops結(jié)構(gòu)體定義如下:
 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,
 ?? ?.read_alarm?? ?= s3c_rtc_getalarm,
 ?? ?.set_alarm?? ?= s3c_rtc_setalarm,
 ?? ?.irq_set_freq?? ?= s3c_rtc_setfreq,
 ?? ?.irq_set_state?? ?= s3c_rtc_setpie,
 ?? ?.proc?? ???????? = s3c_rtc_proc,
 };
?
RTC設(shè)備打開函數(shù)由s3c_rtc_open()來(lái)實(shí)現(xiàn),用戶空間調(diào)用open時(shí),最終會(huì)調(diào)用s3c_rtc_open()函數(shù)。該函數(shù)只要申請(qǐng)了兩個(gè)中斷,一個(gè)報(bào)警中斷,一個(gè)計(jì)時(shí)中斷。
 ?static int s3c_rtc_open(struct device *dev)
 {
 ?? ?struct platform_device *pdev = to_platform_device(dev);???????????? //從device結(jié)構(gòu)體轉(zhuǎn)到platform_device
 ?? ?struct rtc_device *rtc_dev = platform_get_drvdata(pdev);?????????? //從pdev->dev的私有數(shù)據(jù)中得到rtc_device
 ?? ?int ret;
 
 ?? ?ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,?
 ?? ??? ??? ?? IRQF_DISABLED,? "s3c2410-rtc alarm", rtc_dev);?????????????????? //申請(qǐng)一個(gè)報(bào)警中斷,將中斷函數(shù)設(shè)為s3c_rtc_alarmirq(),并傳遞rtc_dev作為參數(shù)
 
 ?? ?if (ret) {
 ?? ??? ?dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
 ?? ??? ?return ret;
 ?? ?}
 
 ?? ?ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
 ?? ??? ??? ?? IRQF_DISABLED,? "s3c2410-rtc tick", rtc_dev);???????????????????????? //申請(qǐng)一個(gè)計(jì)數(shù)中斷,將中斷函數(shù)設(shè)為s3c_rtc_tickirq(),并傳遞rtc_dev作為參數(shù)
 
 ?? ?if (ret) {
 ?? ??? ?dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
 ?? ??? ?goto tick_err;
 ?? ?}
 
 ?? ?return ret;
 
 ?tick_err:
 ?? ?free_irq(s3c_rtc_alarmno, rtc_dev);
 ?? ?return ret;
 }
?
RTC設(shè)備釋放函數(shù)由s3c_rtc_release()來(lái)實(shí)現(xiàn)。用戶空間調(diào)用close()時(shí),最終會(huì)調(diào)用s3c_rtc_release()函數(shù)。該函數(shù)主要釋放s3c_rtc_open()函數(shù)申請(qǐng)的兩個(gè)中斷
 static void s3c_rtc_release(struct device *dev)
 {
 ?? ?struct platform_device *pdev = to_platform_device(dev);???????? //從device結(jié)構(gòu)體轉(zhuǎn)到platform_device
 ?? ?struct rtc_device *rtc_dev = platform_get_drvdata(pdev);???????? //從pdev->dev的私有數(shù)據(jù)中得到rtc_device
 
 ?? ?/* do not clear AIE here, it may be needed for wake */
 
 ?? ?s3c_rtc_setpie(dev, 0);
 ?? ?free_irq(s3c_rtc_alarmno, rtc_dev);
 ?? ?free_irq(s3c_rtc_tickno, rtc_dev);
 }
RTC實(shí)時(shí)時(shí)鐘獲得時(shí)間安函數(shù)
當(dāng)調(diào)用read()函數(shù)時(shí)會(huì)間接的調(diào)用s3c_rtc_gettime()函數(shù)來(lái)獲得實(shí)時(shí)時(shí)鐘的時(shí)間。時(shí)間值分別保存在RTC實(shí)時(shí)時(shí)鐘的各個(gè)寄存器中。這些寄存器是秒寄存器、日期寄存器、分鐘寄存器、和小時(shí)寄存器。s3c_rtc_gettime()函數(shù)會(huì)使用一個(gè)struct rtc_time 的機(jī)構(gòu)體來(lái)表示一個(gè)時(shí)間值
 struct rtc_time {
 ?? ?int tm_sec;
 ?? ?int tm_min;
 ?? ?int tm_hour;
 ?? ?int tm_mday;
 ?? ?int tm_mon;
 ?? ?int tm_year;
 ?? ?int tm_wday;???? //這三個(gè)RTC實(shí)時(shí)時(shí)鐘未用
 ?? ?int tm_yday;
 ?? ?int tm_isdst;
 };
?
存儲(chǔ)在RTC實(shí)時(shí)時(shí)鐘寄存器中的值都是以BCD碼保存的。但是Linux驅(qū)動(dòng)程序中使用二進(jìn)制碼形式。通過(guò)bcd2bin()
unsigned bcd2bin(unsigned char val)
{
????????????? return (val & 0x0f) + (val >> 4) * 10;
}
unsigned char bin2bcd(unsigned val)
{
????????????? return ((val / 10) << 4) + val % 10;
}
從RTC實(shí)時(shí)時(shí)鐘得到時(shí)間的函數(shù)是s3c_rtc_gettime()。第一個(gè)參數(shù)是RTC設(shè)備結(jié)構(gòu)體指針,第二個(gè)參數(shù)是前面提到的struct rtc_time。
 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) {????????? //如果秒寄存器中是0,則表示過(guò)去了一分鐘,那么小時(shí),天,月,等寄存器中的值都可能已經(jīng)變化,則重新讀取這些寄存器的值
 ?? ??? ?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);
 ????? // 轉(zhuǎn)化為二進(jìn)制存儲(chǔ)
 ?? ?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;??? //因?yàn)榇鎯?chǔ)器中存放的是從1900年開始的時(shí)間,所有加上100(這是2000年開始,自己改變這個(gè)值)
 ?? ?rtc_tm->tm_mon -= 1;
 
 ?? ?return 0;
 }
?
同理,下面看設(shè)置時(shí)鐘函數(shù)
 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) {?????????? //由于寄存器的限制,RTC實(shí)時(shí)時(shí)鐘只支持100年時(shí)間
 ?? ??? ?dev_err(dev, "rtc only supports 100 years\n");
 ?? ??? ?return -EINVAL;
 ?? ?}
 ?????? //轉(zhuǎn)化為BCD碼寫到相應(yīng)的寄存器
 ?? ?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在指定的時(shí)刻會(huì)產(chǎn)生一個(gè)報(bào)警信號(hào)。正常模式下,報(bào)警中斷ALMINT有效,對(duì)應(yīng)INT_RTC引腳。掉電模式下,報(bào)警 中斷ALMINT有效外還產(chǎn)生一個(gè)喚醒信號(hào)PMWKUP,對(duì)應(yīng)PMWKUP引腳。RTC報(bào)警寄存器RTCALM決定是否使能報(bào)警狀態(tài)和設(shè)置報(bào)警條件
這個(gè)指定的時(shí)刻由年、月、日、分、秒等組成,在Linux中由struct rtc_time結(jié)構(gòu)體表示。這里struct rtc_time結(jié)構(gòu)體被包含在struct rtc_wkalrm結(jié)構(gòu)體中。
s3c_rtc_getalarm()函數(shù)用來(lái)獲得這個(gè)時(shí)刻。該函數(shù)第一個(gè)參數(shù)是RTC設(shè)備結(jié)構(gòu)體,第二個(gè)參數(shù)是包含報(bào)警時(shí)刻的rtc_wkalarm結(jié)構(gòu)體。
 static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
 ?? ?struct rtc_time *alm_tm = &alrm->time;
 ?? ?void __iomem *base = s3c_rtc_base;
 ?? ?unsigned int alm_en;
 
 ?? ?alm_tm->tm_sec? = readb(base + S3C2410_ALMSEC);
 ?? ?alm_tm->tm_min? = readb(base + S3C2410_ALMMIN);
 ?? ?alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
 ?? ?alm_tm->tm_mon? = readb(base + S3C2410_ALMMON);
 ?? ?alm_tm->tm_mday = readb(base + S3C2410_ALMDATE);
 ?? ?alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);
 
 ?? ?alm_en = readb(base + S3C2410_RTCALM);
 
 ?? ?alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
 
 ?? ?pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
 ?? ??? ? alm_en,
 ?? ??? ? alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
 ?? ??? ? alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
 
 
 ?? ?/* decode the alarm enable field */
 
 ?? ?if (alm_en & S3C2410_RTCALM_SECEN)
 ?? ??? ?alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
 ?? ?else
 ?? ??? ?alm_tm->tm_sec = 0xff;
 
 ?? ?if (alm_en & S3C2410_RTCALM_MINEN)
 ?? ??? ?alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
 ?? ?else
 ?? ??? ?alm_tm->tm_min = 0xff;
 
 ?? ?if (alm_en & S3C2410_RTCALM_HOUREN)
 ?? ??? ?alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
 ?? ?else
 ?? ??? ?alm_tm->tm_hour = 0xff;
 
 ?? ?if (alm_en & S3C2410_RTCALM_DAYEN)
 ?? ??? ?alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
 ?? ?else
 ?? ??? ?alm_tm->tm_mday = 0xff;
 
 ?? ?if (alm_en & S3C2410_RTCALM_MONEN) {
 ?? ??? ?alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
 ?? ??? ?alm_tm->tm_mon -= 1;
 ?? ?} else {
 ?? ??? ?alm_tm->tm_mon = 0xff;
 ?? ?}
 
 ?? ?if (alm_en & S3C2410_RTCALM_YEAREN)
 ?? ??? ?alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
 ?? ?else
 ?? ??? ?alm_tm->tm_year = 0xffff;
 
 ?? ?return 0;
 }
同理,報(bào)警時(shí)間設(shè)置函數(shù)如下:
 static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
 ?? ?struct rtc_time *tm = &alrm->time;?????????????????????? //得到RTC報(bào)警時(shí)間
 ?? ?void __iomem *base = s3c_rtc_base;????????????????? //得到寄存器的虛擬內(nèi)存地址的基地址
 ?? ?unsigned int alrm_en;????????????????????????????????????????? //是否使能報(bào)警
 
 ?? ?pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
 ?? ??? ? alrm->enabled,
 ?? ??? ? tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
 ?? ??? ? tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);????????????????????? //打印一些調(diào)試信息
 
 
 ?? ?alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;??????????? //讀出RTCALM的第6位,表示所有報(bào)警功能都打開
 ?? ?writeb(0x00, base + S3C2410_RTCALM);???????????????????????????????????? //將00寫入RTCALM,使所有的功能都不可以用
 
 ?? ?if (tm->tm_sec < 60 && tm->tm_sec >= 0) {????????????????????? //大于0小于60,則設(shè)置報(bào)警秒寄存器ALMSEC的值,并設(shè)置RTCALM寄存器的第0位為1,表示打開秒報(bào)警功能
 ?? ??? ?alrm_en |= S3C2410_RTCALM_SECEN;
 ?? ??? ?writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC);
 ?? ?}
 
 ?? ?if (tm->tm_min < 60 && tm->tm_min >= 0) {
 ?? ??? ?alrm_en |= S3C2410_RTCALM_MINEN;
 ?? ??? ?writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN);
 ?? ?}
 
 ?? ?if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
 ?? ??? ?alrm_en |= S3C2410_RTCALM_HOUREN;
 ?? ??? ?writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR);
 ?? ?}
 
 ?? ?pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);??????????? //打印報(bào)警使能狀態(tài)
 
 ?? ?writeb(alrm_en, base + S3C2410_RTCALM);??????????
 
 ?? ?s3c_rtc_setaie(alrm->enabled);
 
 ?? ?if (alrm->enabled)?????????????? //使能中斷喚醒功能
 ?? ??? ?enable_irq_wake(s3c_rtc_alarmno);
 ?? ?else
 ?? ??? ?disable_irq_wake(s3c_rtc_alarmno);
 
 ?? ?return 0;
 }
 
 RTC設(shè)置脈沖中斷使能函數(shù)s3c_rtc_setpie()
該函數(shù)用來(lái)設(shè)置是否允許脈沖中斷。
第一個(gè)參數(shù)是RTC設(shè)備結(jié)構(gòu)體,第二個(gè)參數(shù)表示是否允許脈沖中斷。enabled等于1表示允許,等于0表示不允許
 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的值,清除最高位
 
 ?? ?if (enabled)?????????????????????? //如果enabled不等于0,則設(shè)置tmp變量最高位為允許脈沖中斷
 ?? ??? ?tmp |= S3C2410_TICNT_ENABLE;
 
 ?? ?writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
 ?? ?spin_unlock_irq(&s3c_rtc_pie_lock);
 
 ?? ?return 0;
 }
?
在proc文件系統(tǒng)中,可以讀取proc文件系統(tǒng)來(lái)判斷RTC實(shí)時(shí)時(shí)鐘是否支持脈沖中斷。脈沖中斷由TICNT寄存器的最高位決定,最高位為1則表示使能脈沖中斷,為0則表示不允許脈沖中斷。proc文件系統(tǒng)中的讀取命令,一般為cat命令,會(huì)調(diào)用內(nèi)核中的s3c_rtc_proc()函數(shù)
 static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
 {
 ?? ?unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
 
 ?? ?seq_printf(seq, "periodic_IRQ\t: %s\n",
 ?? ??? ????? (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
 ?? ?return 0;
 }
總結(jié)
                            
                        - 上一篇: 树莓派(0)C语言教材学习
 - 下一篇: 修改Tomcat8 内存