#include <linux/interrupt.h> #include <linux/bcd.h>
/*rtc_class_ops是RTC設備類在RTC驅動核心部分中定義的對RTC設備類進行操做的結構體, 相似字符設備在驅動中的file_operations對字符設備進行操做的意思。該結構體被定義 在rtc.h中,對RTC的操做主要有打開、關閉、設置或獲取時間、設置或獲取報警、設置節拍時間計數值等等, 該結構體內接口函數的實現都在下面*/ static const struct rtc_class_ops rtcops = { .open = rtc_open, .release = rtc_release, .irq_set_freq = rtc_setfreq, /*在第②步中已實現*/ .irq_set_state = rtc_setpie, .read_time = rtc_gettime, .set_time = rtc_settime, .read_alarm = rtc_getalarm, .set_alarm = rtc_setalarm, };
/*RTC設備類打開接口函數*/ static int rtc_open(struct device *dev) { int ret;
/*這裏主要的目的是從系統平臺設備中獲取RTC設備類的數據,和RTC探測函數rtc_probe中 的platform_set_drvdata(pdev, rtc)的操做恰好相反。這些都定義在platform_device.h中*/ struct platform_device *pdev = to_platform_device(dev); struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
/*申請RTC報警中斷服務,中斷號rtc_alarmno在RTC探測函數rtc_probe中已經獲取得, 這裏使用的是快速中斷:IRQF_DISABLED。中斷服務程序爲:rtc_alarmirq,將RTC設備類rtc_dev作參數傳遞過去了*/ ret = request_irq(rtc_alarmno, rtc_alarmirq, IRQF_DISABLED, "my2440-rtc alarm", rtc_dev); if (ret) { dev_err(dev, "IRQ%d error %d\n", rtc_alarmno, ret); return ret; }
/*同上面同樣,這裏申請的是RTC的TICK節拍時間中斷服務,服務程序是:rtc_tickirq*/ ret = request_irq(rtc_tickno, rtc_tickirq, IRQF_DISABLED, "my2440-rtc tick", rtc_dev); if (ret) { dev_err(dev, "IRQ%d error %d\n", rtc_tickno, ret); goto tick_err; }
return ret;
tick_err:/*錯誤處理,注意出現錯誤後也要釋放掉已經申請成功的中斷*/ free_irq(rtc_alarmno, rtc_dev); return ret; }
/*RTC報警中斷服務程序*/ static irqreturn_t rtc_alarmirq(int irq, void *argv) { struct rtc_device *rdev = argv; /*接收申請中斷時傳遞過來的rtc_dev參數*/
/*當報警中斷到來的時候,去設定RTC中報警的相關信息,具體設定的方法,RTC核心 部分已經在rtc_update_irq接口函數中實現,函數定義實如今interface.c中*/ rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); return IRQ_HANDLED; }
/*RTC的TICK節拍時間中斷服務*/ static irqreturn_t rtc_tickirq(int irq, void *argv) { struct rtc_device *rdev = argv; /*接收申請中斷時傳遞過來的rtc_dev參數*/
/*節拍時間中斷到來的時候,去設定RTC中節拍時間的相關信息,具體設定的方法,RTC核心 部分已經在rtc_update_irq接口函數中實現,函數定義實如今interface.c中*/ rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); return IRQ_HANDLED; }
/*RTC設備類關閉接口函數*/ static void rtc_release(struct device *dev) { /*和rtc_open中的做用相同*/ struct platform_device *pdev = to_platform_device(dev); struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
/*請見rtc_setpie接口函數中的解釋*/ rtc_setpie(dev, 0);
/*同rtc_open中中斷的申請相對應,在那裏申請中斷,這裏就釋放中斷*/ free_irq(rtc_alarmno, rtc_dev); free_irq(rtc_tickno, rtc_dev); }
/*該函數主要是對RTC的節拍時間計數寄存器TICNT的第7位進行操做,即:節拍時間計數的使能功能*/ static int rtc_setpie(struct device *dev, int flag) { unsigned int tmp;
spin_lock_irq(&rtc_pie_lock);/*獲取自旋鎖保護臨界區資源*/
/*讀取節拍時間計數寄存器TICNT的值*/ tmp = readb(rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
if (flag) { tmp |= S3C2410_TICNT_ENABLE; /*根據標誌flag的值來判斷是要使能仍是要禁止*/ }
/*將經運算後值寫入節拍時間計數寄存器TICNT中,這裏主要是改變TICNT的第7位的值*/ writeb(tmp, rtc_base + S3C2410_TICNT);
spin_unlock_irq(&rtc_pie_lock);/*釋放自旋鎖,即解鎖*/
return 0; }
/*讀取RTC中BCD數中的:分、時、日期、月、年、秒*/ static int rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) { unsigned int have_retried = 0;
retry_get_time: rtc_tm->tm_min = readb(rtc_base + S3C2410_RTCMIN); /*讀BCD分寄存器RTCMIN*/ rtc_tm->tm_hour = readb(rtc_base + S3C2410_RTCHOUR); /*讀BCD時寄存器RTCHOUR*/ rtc_tm->tm_mday = readb(rtc_base + S3C2410_RTCDATE); /*讀BCD日期寄存器RTCDATE*/ rtc_tm->tm_mon = readb(rtc_base + S3C2410_RTCMON); /*讀BCD月寄存器RTCMON*/ rtc_tm->tm_year = readb(rtc_base + S3C2410_RTCYEAR); /*讀BCD年寄存器RTCYEAR*/ rtc_tm->tm_sec = readb(rtc_base + S3C2410_RTCSEC); /*讀BCD秒寄存器RTCSEC*/
/*咱們知道時間是以60爲一個週期的,當時、分、秒達到60後,他們的上一級會加1,而自身又從0開始計數 上面咱們最後讀的秒,若是讀出來的秒恰好是0,那麼前面讀的分、時等就是上一分鐘的,結果就少了一分鐘, 因此就要從新讀取*/ if (rtc_tm->tm_sec == 0 && !have_retried) { have_retried = 1; goto retry_get_time; }
/*將上面讀取的時間日期值保存到RTC核心定義的時間結構體中,該結構體定義在rtc.h中, 這裏的bcd2bin主要是編譯器對返回值相同時進行優化處理,定義在bcd.h中*/ 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);
/*這裏爲何要加100年和減1月呢,咱們查看數據手冊得知原來是爲了區別1900年和2000年閏年的因素, 1900年不是閏年而2000年是閏年。這時你或許會問那怎麼不考慮1800年或2100年啊?緣由很簡單,由於 咱們的RTC時鐘只支持100年的時間範圍,呵呵!!*/ rtc_tm->tm_year += 100; rtc_tm->tm_mon -= 1;
return 0; }
/*和上面的rtc_gettime功能相反,將更改後的分、時、日期、月、年、秒寫入RTC中BCD數中*/ static int rtc_settime(struct device *dev, struct rtc_time *tm) { /*這裏減100年很清楚了吧,由於上面爲了區別1900年和2000年時加了100年*/ int year = tm->tm_year - 100;
/*RTC時鐘只支持100年的時間範圍*/ if (year < 0 || year >= 100) { dev_err(dev, "rtc only supports 100 years\n"); return -EINVAL; }
/*將上面保存到RTC核心定義的時間結構體中的時間日期值寫入對應的寄存器中*/ writeb(bin2bcd(tm->tm_sec), rtc_base + S3C2410_RTCSEC); writeb(bin2bcd(tm->tm_min), rtc_base + S3C2410_RTCMIN); writeb(bin2bcd(tm->tm_hour), rtc_base + S3C2410_RTCHOUR); writeb(bin2bcd(tm->tm_mday), rtc_base + S3C2410_RTCDATE); writeb(bin2bcd(tm->tm_mon + 1), rtc_base + S3C2410_RTCMON); /*這裏加1月也明白了吧*/ writeb(bin2bcd(year), rtc_base + S3C2410_RTCYEAR);
return 0; }
/*讀取RTC中報警各寄存器的:秒、分、時、月、日期、年的值,保存各值到rtc_time結構體中*/ static int rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) { unsigned int alm_en; struct rtc_time *alm_tm = &alrm->time;
alm_tm->tm_sec = readb(rtc_base + S3C2410_ALMSEC); alm_tm->tm_min = readb(rtc_base + S3C2410_ALMMIN); alm_tm->tm_hour = readb(rtc_base + S3C2410_ALMHOUR); alm_tm->tm_mon = readb(rtc_base + S3C2410_ALMMON); alm_tm->tm_mday = readb(rtc_base + S3C2410_ALMDATE); alm_tm->tm_year = readb(rtc_base + S3C2410_ALMYEAR);
/*獲取RTC報警控制寄存器RTCALM的值*/ alm_en = readb(rtc_base + S3C2410_RTCALM);
/*判斷RTCALM值的第6位,來設置RTC的全局報警使能狀態到RTC核心定義的報警狀態結構體rtc_wkalrm中*/ alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
/*判斷若是RTCALM值的第0位的值(秒報警使能)爲1時,就設置報警秒的值到rtc_time結構體中*/ if (alm_en & S3C2410_RTCALM_SECEN) alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); else alm_tm->tm_sec = 0xff;
/*判斷若是RTCALM值的第1位的值(分報警使能)爲1時,就設置報警分的值到rtc_time結構體中*/ if (alm_en & S3C2410_RTCALM_MINEN) alm_tm->tm_min = bcd2bin(alm_tm->tm_min); else alm_tm->tm_min = 0xff;
/*判斷若是RTCALM值的第2位的值(時報警使能)爲1時,就設置報警小時的值到rtc_time結構體中*/ if (alm_en & S3C2410_RTCALM_HOUREN) alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); else alm_tm->tm_hour = 0xff;
/*判斷若是RTCALM值的第3位的值(日期報警使能)爲1時,就設置報警日期的值到rtc_time結構體中*/ if (alm_en & S3C2410_RTCALM_DAYEN) alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); else alm_tm->tm_mday = 0xff;
/*判斷若是RTCALM值的第4位的值(月報警使能)爲1時,就設置報警月的值到rtc_time結構體中*/ if (alm_en & S3C2410_RTCALM_MONEN) { alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); alm_tm->tm_mon -= 1; /*這裏爲何要遞減1,我不是很明白???????*/ } else { alm_tm->tm_mon = 0xff; }
/*判斷若是RTCALM值的第5位的值(年報警使能)爲1時,就設置報警年的值到rtc_time結構體中*/ if (alm_en & S3C2410_RTCALM_YEAREN) alm_tm->tm_year = bcd2bin(alm_tm->tm_year); else alm_tm->tm_year = 0xffff;
return 0; }
/*把上面保存到rtc_time結構體中各值寫入RTC中報警各寄存器中*/ static int rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) { unsigned int alrm_en; struct rtc_time *tm = &alrm->time;
/*讀取RTC報警控制寄存器RTCALM的第6位,把全局報警使能的狀態保存到alrm_en變量中*/ alrm_en = readb(rtc_base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
/*把RTC報警控制寄存器RTCALM的值設爲0,即將全局報警使能和其餘報警使能所有關閉*/ writeb(0x00, rtc_base + S3C2410_RTCALM);
if (tm->tm_sec < 60 && tm->tm_sec >= 0) { /*上面的alrm_en值只記錄了RTCALM的第6位(全局報警使能的狀態),這裏再加上第0位(秒報警使能的狀態), 而後將前面保存在rtc_time中報警秒的值寫入報警秒數據寄存器ALMSEC中*/ alrm_en |= S3C2410_RTCALM_SECEN; writeb(bin2bcd(tm->tm_sec), rtc_base + S3C2410_ALMSEC); }
if (tm->tm_min < 60 && tm->tm_min >= 0) { /*加上第1位(分報警使能的狀態), 而後將前面保存在rtc_time中報警分的值寫入報警分鐘數據寄存器ALMMIN中*/ alrm_en |= S3C2410_RTCALM_MINEN; writeb(bin2bcd(tm->tm_min), rtc_base + S3C2410_ALMMIN); }
if (tm->tm_hour < 24 && tm->tm_hour >= 0) { /*加上第2位(時報警使能的狀態), 而後將前面保存在rtc_time中報警小時的值寫入報警小時數據寄存器ALMHOUR中*/ alrm_en |= S3C2410_RTCALM_HOUREN; writeb(bin2bcd(tm->tm_hour), rtc_base + S3C2410_ALMHOUR); }
/*把alrm_en修改事後的值從新寫入RTC報警控制寄存器RTCALM中*/ writeb(alrm_en, rtc_base + S3C2410_RTCALM);
/*請看下面rtc_setaie函數實現部分*/ rtc_setaie(alrm->enabled);
/*根據全局報警使能的狀態來決定是喚醒RTC報警中斷仍是睡眠RTC報警中斷*/ if (alrm->enabled) enable_irq_wake(rtc_alarmno); else disable_irq_wake(rtc_alarmno);
return 0; }
/*這裏主要仍是控制RTC報警控制寄存器RTCALM的第6位(全局報警使能狀態)*/ static void rtc_setaie(int flag) { unsigned int tmp;
tmp = readb(rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
if (flag)/*根據標誌flag來使能或禁止全局報警*/ tmp |= S3C2410_RTCALM_ALMEN;
writeb(tmp, rtc_base + S3C2410_RTCALM); } |