SylixOS中APIC HPET定時器字符驅動實現

1.簡介ios

1.1 APIC介紹編程

「APIC」是Advanced Programmable Interrupt Controller的縮寫,即高級可編程中斷控制器。引入APIC機制是爲了適應multiple processor(MP,多處理器)環境。函數

APIC分爲兩部分:Local APIC與I/O APIC。Local APIC位於處理器內部,而I/O APIC則呼籲芯片組的一部分。Local APIC與I/O APIC經過system bus進行通訊。Local APIC 與I/O APIC的關係如圖1.1所示。ui

圖1.1 Local APIC與I/O APIC的關係code

本文檔使用的HPET的中斷線是鏈接在I/OAPIC上的。在SylixOS中僅在主機是多核且在menu.lst中加入hpet=yes參數纔會啓用APIC的HPET功能。ip

1.2 HPET的工做原理ci

「HPET」 是High precision event timer的縮寫,即高精度定時器。HPET有1個main counter(主計數器)寄存器和最多8個timer(定時器),記爲timer0~timer7定時器。每一個timer有本身的一對寄存器,分別是:configure(timer配置寄存器)和comparator value(timer比較值寄存器)。rem

HPET counter按照固定的頻率進行計數,HPET會檢查counter的值與timer的comparator值進行比較。當counter的值達到任何一個timer的comparator值時將產生中斷(當配置可產生中斷時)。那麼,若是counter同時達到了多個timer所設定comparator值就會產生多箇中斷。HPET的8個timer能夠配置爲使用不一樣的IRQ線,這些同時產生的中斷就能夠同時進行處理。文檔

2. HPET字符設備定時器的實現get

正如在SylixOS中字符設備驅動的實現方式同樣。HPET須要實現對應的字符設備的操做函數集並將其註冊到系統中便可。和普通的字符設備不一樣,因爲只須要實現HPET的定時功能,所以本文檔中所描述的HPET字符設備定時器並無實現讀寫的操做,只實現了ioctl的相關功能。

2.1 HPET的初始化

HPET的初始化如程序清單2.1所示。

程序清單2.1 HPET初始化

INT  bspHpetTimerInit (ACPI_TABLE_HPET  *pAcpiHpetPhy, ULONG  *pulVector)  
{  
    ACPI_TABLE_HPET  *pAcpiHpet;  
    UINT32            ulhpetTimerCfg;  
    /*  
     * 映射 ACPI HPET 表  
     */  
    pAcpiHpet = bspAcpiHpetMap((addr_t)pAcpiHpetPhy, LW_CFG_VMM_PAGE_SIZE);  
    if (!pAcpiHpet) {  
        return  (PX_ERROR);  
    }  
  
    /*  
     * 映射 HPET 寄存器  
     */  
    __GhpetBase = (addr_t)API_VmmIoRemapNocache((PVOID)(pAcpiHpet->Address.Address),  
                                                  LW_CFG_VMM_PAGE_SIZE);  
  
    /*  
     * 解除映射 ACPI HPET 表  
     */  
    API_VmmIoUnmap((PVOID)(((addr_t)pAcpiHpet) & LW_CFG_VMM_PAGE_MASK));  
  
    if (!__GhpetBase) {                         /*  映射 HPET 寄存器失敗        */  
        return  (PX_ERROR);  
    }  
  
    __Gul32FsPerCnt = read32(HPET_ID_HI);  
  
    ulhpetTimerCfg = read32(HPET_TIMER_CONFIG_LO(2));  
  
write32(    (((ulhpetTimerCfg &  
            (~(ROUTE_MSK << 9))) |  
                (LW_IRQ_20 << 9)) &               /*  設置中斷爲edge模式並關中斷  */  
                (~(3 <<1 ))),                                                 
             HPET_TIMER_CONFIG_LO(2));  
  
    *pulVector = LW_IRQ_20;                     /*  IRQ20                       */  
  
    return  (ERROR_NONE);  
}

其中bspAcpiHpetMap主要是實現ACPI HPET表的映射,實現如程序清單2.2所示。

程序清單2.2 映射ACPI HPET表

static VOID  *bspAcpiHpetMap (addr_t  ulAcpiPhyAddr, size_t  ulAcpiSize)  
{  
    addr_t  ulPhyBase = ROUND_DOWN(ulAcpiPhyAddr, LW_CFG_VMM_PAGE_SIZE);  
    addr_t  ulOffset  = ulAcpiPhyAddr - ulPhyBase;  
    addr_t  ulVirBase;  
  
    ulAcpiSize += ulOffset;  
    ulAcpiSize  = ROUND_UP(ulAcpiSize, LW_CFG_VMM_PAGE_SIZE);  
  
    ulVirBase = (addr_t)API_VmmIoRemapNocache((PVOID)ulPhyBase, ulAcpiSize);  
    if (ulVirBase) {  
        return  (VOID *)(ulVirBase + ulOffset);  
    } else {  
        return  (VOID *)(LW_NULL);  
    }  
}

2.2 HPET設備的註冊

HPET設備的註冊如程序清單2.3所示。

程序清單2.3 HPET設備的註冊

INT  __hpetRegister (VOID)  
{  
    INT iDrvNum = iosDrvInstallEx(&__GtimerOps);    /*  安裝驅動程序          */  
  
return  (iosDevAdd( &__GtimerDevHdr,  
                        "/dev/timer",  
                    iDrvNum));                  /*  建立timer設備       */  
}

其中__GtimerOps即爲HPET字符設備對應的操做函數集。具體內容如程序清單 2.4所示。

程序清單2.4 HPET字符設備操做函數集

struct file_operations __GtimerOps = {  
    .fo_open  = __hpetOpen,  
    .fo_close = __hpetClose,  
    .fo_ioctl = __hpetIoctl  
};

這裏只實現了fo_open、fo_close和fo_ioctl這三個字符設備相關操做函數。

2.2.1 HPET設備的操做函數集

1. 打開操做

HPET設備的打開操做函數實現如程序清單 2.5所示。

程序清單2.5 fo_open實現

static LONG __hpetOpen(PLW_DEV_HDR pdevhdrHdr,  
                        PCHAR       pcName,  
                        INT         iFlag,  
                        INT         iMode)  
{  
    __GobSem = API_SemaphoreBCreate("wait_timer",  
                                    0,  
                                    LW_OPTION_OBJECT_GLOBAL,  
                                    LW_NULL);       /*  建立二進制型信號量          */  
  
    LW_DEV_INC_USE_COUNT(pdevhdrHdr);           /*  增長使用計數                */  
  
    return  ((LONG)pdevhdrHdr);  
}

  2. 關閉操做

HPET設備的關閉操做函數實現如程序清單 2.6所示。

程序清單2.6 fo_close實現

static INT __hpetClose(PLW_DEV_HDR    pdevhdrHdr)  
{  
  
    if (pdevhdrHdr) {  
  
        LW_DEV_INC_USE_COUNT(pdevhdrHdr);       /*  減小使用計數                */  
  
        hpetTimerStop();  
  
        API_SemaphoreBDelete(&__GobSem);  
  
        return  (ERROR_NONE);  
    } else {  
        return  (PX_ERROR);  
    }  
}

3. ioctl的實現

HPET設備的ioctl函數的實現如程序清單 2.7所示。

程序清單2.7 fo_ioctl實現

static INT __hpetIoctl (PLW_DEV_HDR    pdevhdrHdr, INT  iCmd, LONG  lArg)  
{  
    hard_timer      * ht;  
    ht = (hard_timer *)lArg;  
  
    switch (iCmd) {  
    case SET_DELAY:                             /*  設置延時時間(ns)            */  
        ht->iStatus = FALSE;  
        __GuiIncrementValue = ht->ptvPeriod->tv_nsec / NSEC_PER_COUNT;  
        hpetCounterSet(__GuiIncrementValue);  
        break;  
  
    case START_DELAY:                           /*  開始延時時間(ns)            */  
        if (FALSE == ht->iStatus) {  
            hpetTimerStart();  
            ht->iStatus = TRUE;  
        }  
  
        API_SemaphoreBPend(__GobSem, LW_OPTION_WAIT_INFINITE);  
        break;  
  
    default:  
        break;  
    }  
  
    return ERROR_NONE;  
}

ioctl裏主要調用了HPET定時器的開始與中止以及定時器計數值的設置相關函數。

定時器開始的函數實現如程序清單 2.8所示。

程序清單2.8 開始定時器工做

VOID hpetTimerStart(VOID)  
{  
    UINT32 ulhpetTimerCfg;  
  
    API_InterVectorEnable(__Gvector);           /*  使能中斷                    */  
  
    ulhpetTimerCfg = read32(HPET_TIMER_CONFIG_LO(2));  
    write32(ulhpetTimerCfg |  
            (1 <<2 ),  
            HPET_TIMER_CONFIG_LO(2));           /*  使能HPET timer2中斷     */  
}

定時器中止的函數實現如程序清單 2.9所示。

程序清單2.9 中止定時器工做

VOID hpetTimerStop(VOID)  
{  
    UINT32 ulhpetTimerCfg;  
    ulhpetTimerCfg = read32(HPET_TIMER_CONFIG_LO(2));  
    write32(ulhpetTimerCfg &  
            (~(1 <<2 )),  
            HPET_TIMER_CONFIG_LO(2));       /*  禁止HPET timer2中斷         */  
  
    API_InterVectorDisable(__Gvector);      /*  禁止中斷                       */  
}

定時器計數值設置的函數實現如程序清單 2.10所示。

程序清單2.10 定時器數值設置

VOID hpetCounterSet(UINT32 uiCount)  
{  
    UINT32 ulcountLowVal;  
    UINT32 ulcountHighVal;  
    UINT32 ulcountSum;  
  
    ulcountLowVal = readl(HPET_COUNTER_LO);     /*  獲取當前counter低32位值    */  
    ulcountHighVal = readl(HPET_COUNTER_HI);    /*  獲取當前counter高32位值    */  
  
    ulcountSum = ulcountLowVal + uiCount;  
    if(ulcountSum < ulcountLowVal) {  
        ulcountHighVal += 1;                        /*  若低32位溢出,則高32位進1 */  
    }                                                                             
    /*  
     * 設置timer2比較值寄存器  
     */  
    writel(ulcountSum, HPET_TIMER_COMPARATOR_LO(2));  
    writel(ulcountHighVal, HPET_TIMER_COMPARATOR_HI(2));  
}

2.3 HPET設備的註銷

HPET設備的註銷如程序清單 2.11所示。

程序清單2.11 HPET設備的註銷

INT  __hpetUnregister(VOID)  
{  
    PLW_DEV_HDR pDev;  
  
    pDev = &__GtimerDevHdr;  
  
    if (pDev) {                                                                    /*  
         * 卸載timer設備和驅動程序  
         */  
        iosDevDelete(pDev);                                                       
        return  (iosDrvRemove(pDev->DEVHDR_usDrvNum, 0));  
    } else {                                                                          
        return  (PX_ERROR);  
    }  
}

2.4 HPET中斷服務函數

HPET中斷服務函數實現如程序清單 2.12所示。

程序清單2.12 HPET中斷服務函數

static irqreturn_t  __tickHpetIsr (VOID)  
{  
    API_SemaphoreBPost(__GobSem);  
  
    hpetCounterSet(__GuiIncrementValue);        /*  設置counter計數值        */  
    hpetTimerStart();                           /*  開始定時器工做計時           */  
  
    return  (LW_IRQ_HANDLED);  
}

2.5 HPET模塊加載

HPET模塊加載實現如程序清單 2.13所示。

程序清單2.13 HPET模塊加載

int module_init (void)  
{  
    LW_CLASS_CPUSET         cpuset;  
  
    /*  
     * 初始化hpet timer  
     */  
    bspHpetTimerInit(_G_pAcpiHpet, &__Gvector); /*  定時器初始化              */  
  
    API_InterVectorConnect(__Gvector,           /*  鏈接中斷                    */  
                          (PINT_SVR_ROUTINE)__tickHpetIsr,  
                           LW_NULL,  
                           "hpetIsr");  
  
    API_InterVectorEnable(__Gvector);           /*  使能中斷                    */  
  
    /*  
     * 綁定定時器中斷到cpu1  
     */  
    LW_CPU_ZERO(&cpuset);  
    LW_CPU_SET(1, &cpuset);  
    API_InterSetTarget(__Gvector, sizeof(LW_CLASS_CPUSET), &cpuset);  
  
    __hpetRegister();                           /*  timer設備註冊           */  
    return  0;  
}

2.6 HPET模塊卸載

HPET模塊卸載實現如程序清單 2.14所示。

程序清單2.14 HPET模塊卸載

void module_exit (void)  
{  
    hpetTimerStop();                                /*  中止timer             */  
  
  
    API_InterVectorDisconnect(__Gvector,  
                             (PINT_SVR_ROUTINE)__tickHpetIsr,  
                             LW_NULL);  
  
    API_InterVectorDisable(__Gvector);  
  
    __hpetUnregister();                         /*  timer設備註銷           */  
}
相關文章
相關標籤/搜索