1.簡介
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所示。
圖1.1 Local APIC與I/O APIC的關係
本文檔使用的HPET的中斷線是連接在I/OAPIC上的。在SylixOS中僅在主機是多核且在menu.lst中加入hpet=yes參數纔會啓用APIC的HPET功能。
1.2 HPET的工作原理
“HPET” 是High precision event timer的縮寫,即高精度定時器。HPET有1個main counter(主計數器)寄存器和最多8個timer(定時器),記爲timer0~timer7定時器。每個timer有自己的一對寄存器,分別是:configure(timer配置寄存器)和comparator value(timer比較值寄存器)。
HPET counter按照固定的頻率進行計數,HPET會檢查counter的值與timer的comparator值進行比較。當counter的值達到任何一個timer的comparator值時將產生中斷(當配置可產生中斷時)。那麼,如果counter同時達到了多個timer所設定comparator值就會產生多箇中斷。HPET的8個timer可以配置爲使用不同的IRQ線,這些同時產生的中斷就可以同時進行處理。
2. HPET字符設備定時器的實現
正如在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設備的操作函數集
- 打開操作
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);
}
- 關閉操作
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);
}
}
- 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設備註銷 */
}