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設備註銷 */ }