#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);
}
|