SylixOS中APIC HPET定时器字符驱动实现

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设备的操作函数集

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设备注销			*/
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值