ZYQN7000系列VxWorks驱动开发:调试GPIO子系统

使用VxWorks的GPIO子系统

1.设备树文件

​ 接下来要为领航者开发板添加设备树文件,新建zynq-navigator.dts文件,此时根节点下没有任何设备节点,接下来的工作就是把我们所有的设备逐渐添加到该设备文件中。

image-20211128194314992

2.gpio子系统

  我认为所谓gpio子系统只不过是一种高大上的叫法,gpio操作的本质永远是干寄存器,只不过为了兼顾各个平台,使用面向对象的思想将硬件和软件弱耦合化,使用户感受不到硬件太大的变化,这就是gpio子系统存在的意义。

2.1风河BSP所作的工作

  为了方便阅读风河为ZYQN提供的BSP源码,我们在工程目录下新建一个名为WindRiver的目录,将我们用到的相关驱动文件放到该目录下

image-20211128150636952

2.2第三方开发者要做的工作

  风河只会提供ZYNQ7000的片上外设资源的BSP,如GPIO、SPI等接口一般来说我们都是直接能用的,和Linux驱动一样,就拿GPIO来说,子系统都给你初始化好了,风河提供了一组GPIO的操作函数,这些函数是与硬件无关的,也就是说你换一个硬件平台,这些函数也照样适用,这就叫做GPIO子系统。

image-20211128151954333

  我们将Navigator开发板上的外设驱动放在名为Navigator的目录下,别忘了把这两个目录的路径添加到Include path里

image-20211128150922659

2.3添加gpio驱动

  将vxbFdtZynqGpio.c拷贝到WindRiver目录下,编译后将设备树zynq-navigator.dtb和uVxWorks拷贝到SD卡,上电启动。

image-20211128170052361

  然而,启动后如下图所示,gpio驱动加载了,但是没有probe,这个问题简单,看一下vxbFdtZynqGpio.c文件和设备树中的gpio节点便知。

image-20211128170010069

  用于driver和device匹配的关键代码如下,从这几行代码可以看出,该驱动即支持ZYNQ7000系列,还支持MPSoc系列,他们用的gpio部分是相同的IP核。

#define ZYNQ_GPIO_DRV_NAME                      "xlnx,zynq-gpio"
#define ZYNQMP_GPIO_DRV_NAME                    "xlnx,zynqmp-gpio"

LOCAL VXB_FDT_DEV_MATCH_ENTRY fdtZynqGpioMatch[] =
    {
        {
        ZYNQ_GPIO_DRV_NAME,             /* compatible for Zynq GPIO */
        (void *)&zynqGpioCfg
        },
        {
        ZYNQMP_GPIO_DRV_NAME,           /* compatible for ZynqMP GPIO */
        (void *)&zynqmpGpioCfg
        },
        {}                              /* empty terminated list */
    };

  原来是设备树中的gpio节点的compatible和driver中的compatible不一致导致的,那么问题来了,为何会出现这种错误,我猜测可能是风河在移植ZYNQ7000BSP时,并没有外设直接操作gpio吧。

image-20211128170742489

3.调试gpio驱动

  先看compatible=xlnx,zynq7k-misccfg的情况,使用VSCode在风河BSP目录下搜索所有关键字为xlnx,zynq7k-misccfg的文件。

3.1分析vxbFdtZynq7kMiscCfg.c

image-20211128191411744

  确实搜到了,看一下vxbFdtZynq7kMiscCfg.c文件,如果将此文件添加进VIP工程,确实设备树里的gpio节点节能和driver匹配上了,但我试了,发现gpio还是无法操作,只能去代码里找答案了。

  和Linux驱动一样,VxWorks也使用了设备与驱动匹配的模型,先Probe探测,如果探测到就调用Attach函数,其中Attach完成驱动和初始化和设备节点的创建工作。

  下面为vxbFdtZynq7kMiscCfg.c中的Attach函数,根据我多年经验,一下就发现了问题,里面没有把ZYNQ系列的Gpio操作函数和GPIO子系统进行绑定,所以就是driver和device匹配上了也没用啊。GPIO依然使用不了。

LOCAL STATUS vxbZynq7kMiscCfgAttach
    (
    VXB_DEV_ID          pDev
    )
    {
    ZYNQ7K_MISC_CFG_DRVCTRL *  pDrvCtrl;
    VXB_FDT_DEV *              pFdtDev;
    VXB_RESOURCE_ADR *         pResAdr;
    VXB_RESOURCE     *         pRes; 
    UINT32 *                   pMiscCfgList;
    int                        len;

    pFdtDev = (VXB_FDT_DEV *)vxbDevIvarsGet(pDev);
    if (pFdtDev == NULL)
        {
	return ERROR;
        }

    pDrvCtrl = (ZYNQ7K_MISC_CFG_DRVCTRL *) 
	       vxbMemAlloc (sizeof (ZYNQ7K_MISC_CFG_DRVCTRL));

    if (pDrvCtrl == NULL)
        {
        DEBUG_MSG("vxbZynq7kMiscCfgAttach: pDrvCtrl allocate failed\n");
        return ERROR;
        }
    pDrvCtrl->pInst= pDev;

    pRes = vxbResourceAlloc (pDev, VXB_RES_MEMORY, 0);
    if(pRes == NULL || pRes->pRes == NULL)
        {
        DEBUG_MSG("vxbZynq7kMiscCfgAttach: pDrvCtrl allocate failed\n");

        vxbMemFree (pDrvCtrl);
        return ERROR;
        }

    pResAdr = (VXB_RESOURCE_ADR *)pRes->pRes;

    pDrvCtrl->pHandle  = pResAdr->pHandle;
    pDrvCtrl->pRegbase = (void *)pResAdr->virtual;
    DEBUG_MSG("vxbZynq7kMiscCfgAttach: regbase: 0x%x\n", pDrvCtrl->pRegbase);
    
    /* save pDrvCtrl in VXB_DEVICE structure */

    vxbDevSoftcSet (pDev, pDrvCtrl);

    pMiscCfgList = (UINT32 *)vxFdtPropGet (pFdtDev->offset, 
	                                   "misccfg-set", &len);
    if (pMiscCfgList != NULL && len > 0 )
        {
	UINT32 reg;
	UINT32 value;
	UINT32 mask;

        for (; len > 0; len = len - 12)
            {
            reg   = vxFdt32ToCpu (*pMiscCfgList++);
            value = vxFdt32ToCpu (*pMiscCfgList++);
            mask  = vxFdt32ToCpu (*pMiscCfgList++);
            DEBUG_MSG("vxbZynq7kMiscCfgAttach: misccfg[0x%x-0x%x-0x%x]\n",
		      reg, value, mask);
	    CSR_CLR_BIT (pDrvCtrl, reg, mask);
	    CSR_SET_BIT (pDrvCtrl, reg, (value & mask));
            }
        }

    return OK;
    }

3.2分析vxbFdtZynqGpio.c

  然后,看一下vxbFdtZynqGpio.c中的Attach函数,可以看到ZYNQ的Gpio操作函数已经和GPIO子系统绑定,没毛病,接下来需要改下设备树的gpio节点就行了。

LOCAL STATUS vxbZynqGpioAttach
    (
    VXB_DEV_ID  pDev
    )
    {
    VXB_FDT_DEV *       pFdtDev;
    VXB_GPIOCTRL *      pCtrl   = NULL;
    ZYNQ_GPIO_DRVCTRL * pDrvCtrl= NULL;
    VXB_RESOURCE_ADR *  pResAdr = NULL;
    VXB_RESOURCE *      pRes    = NULL;
    VXB_RESOURCE *      pIntRes = NULL;
    void *              prop;
    int                 len;
    UINT32              i;

    if (pDev == NULL)
        return ERROR;

    pFdtDev = (VXB_FDT_DEV *)vxbFdtDevGet (pDev);
    if (pFdtDev == NULL)
        return ERROR;

    if ((pDrvCtrl = vxbMemAlloc (sizeof (ZYNQ_GPIO_DRVCTRL))) == NULL)
        {
        DEBUG_MSG (ZYNQ_DBG_ERR,
                   "vxbZynqGpioAttach: pDrvCtrl alloc failed\n");
        return ERROR;
        }

    pCtrl = &(pDrvCtrl->gpioCtrl);
    pCtrl->pDev = pDev;

    /* get resources */

    pRes = vxbResourceAlloc (pDev, VXB_RES_MEMORY, 0);
    if (pRes == NULL)
       goto error;

    pResAdr = (VXB_RESOURCE_ADR *)pRes->pRes;
    if (pResAdr == NULL)
       goto error;

    pDrvCtrl->handle   = pResAdr->pHandle;
    pDrvCtrl->baseAddr = (void *)pResAdr->virtual;
    _func_kprintf("gpio baseaddr = 0x%x\n",pDrvCtrl->baseAddr);
    /* get interrupt resource */

    pIntRes = vxbResourceAlloc (pDev, VXB_RES_IRQ, 0);
    if (pIntRes == NULL)
       goto error;

    pDrvCtrl->intRes = pIntRes;

    /* parse the GPIO cells */

    prop = (void *)vxFdtPropGet (pFdtDev->offset, "#gpio-cells", &len);
    _func_kprintf("gpio-cells = %d\n",len);
    if ((prop == NULL) || (len != 4))
        {
        DEBUG_MSG (ZYNQ_DBG_ERR,
                   "vxbZynqGpioAttach: gpio-cells parse error, len=%d.\n", len);
        goto error;
        }

    /* retrieve configuration */
    _func_kprintf("1\n");
    pDrvCtrl->pGpioCfg = (ZYNQ_GPIO_CFG *)vxbDevDrvDataGet (pDev);
    if (pDrvCtrl->pGpioCfg == NULL)
        {
        DEBUG_MSG (ZYNQ_DBG_ERR, "vxbZynqGpioAttach: NULL pGpioCfg\n");
        goto error;
        }
    _func_kprintf("2\n");
    pCtrl->length = pDrvCtrl->pGpioCfg->bankWidth * pDrvCtrl->pGpioCfg->bankNum;
    pCtrl->pValidBmp = vxbMemAlloc (ROUND_UP ((pCtrl->length / 8),
                                              sizeof (UINT32)));
    if (pCtrl->pValidBmp == NULL)
        {
        DEBUG_MSG (ZYNQ_DBG_ERR,
                   "vxbZynqGpioAttach: pValidBmp alloc failed.\n");
        goto error;
        }
    _func_kprintf("3\n");
    if (vxbIntConnect (pDev, pIntRes, vxbZynqGpioISR, pDev) != OK)
        {
        DEBUG_MSG (ZYNQ_DBG_ERR,
                   "vxbZynqGpioAttach: vxbIntConnect failed.\n");
        goto error;
        }
    _func_kprintf("4\n");
    for (i = 0; i < pDrvCtrl->pGpioCfg->bankNum; i++)
        {
        /* disable all GPIO pin interrupt */

        CSR_WRITE_4 (pDrvCtrl, ZYNQ_GPIO_INT_DIS (i), GPIO_ALL_PIN_BIT);

        /* store the direction register */

        pDrvCtrl->dirm[i] = CSR_READ_4 (pDrvCtrl, ZYNQ_GPIO_DIRM (i));
        }
    _func_kprintf("5\n");
    if (vxbIntEnable (pDev, pDrvCtrl->intRes) != OK)
        {
        if (vxbIntDisconnect (pDev, pDrvCtrl->intRes) != OK)
            {
            DEBUG_MSG (ZYNQ_DBG_ERR,
                       "vxbZynqGpioAttach: vxbIntDisconnect failed.\n");
            }
        goto error;
        }

    pCtrl->gpioCells            = vxFdt32ToCpu (((UINT32*)prop)[0]);
    pCtrl->gpioAlloc            = vxbZynqGpioPinAlloc;
    pCtrl->gpioFree             = vxbZynqGpioPinFree;
    pCtrl->gpioGetDir           = vxbZynqGpioPinGetDir;
    pCtrl->gpioSetDir           = vxbZynqGpioPinSetDir;
    pCtrl->gpioGetValue         = vxbZynqGpioPinGetValue;
    pCtrl->gpioSetValue         = vxbZynqGpioPinSetValue;
    pCtrl->gpioIntConnect       = vxbZynqGpioIntConnect;
    pCtrl->gpioIntDisConnect    = vxbZynqGpioIntDisConnect;
    pCtrl->gpioIntEnable        = vxbZynqGpioIntEnable;
    pCtrl->gpioIntDisable       = vxbZynqGpioIntDisable;
    pCtrl->gpioIntConfig        = vxbZynqGpioIntConfig;
#ifdef _WRS_CONFIG_DEBUG_FLAG
    pCtrl->gpioShow             = vxbZynqGpioShow;
#endif /* _WRS_CONFIG_DEBUG_FLAG */
    _func_kprintf("6\n");
    vxbDevSoftcSet (pDev, (void *)pDrvCtrl);

    SPIN_LOCK_ISR_INIT (&pDrvCtrl->spinLock, 0);

    (void)vxbGpioAddCtlr (pCtrl);
    _func_kprintf("7\n");
    return OK;

error:
    if (pCtrl->pValidBmp != NULL)
        vxbMemFree (pCtrl->pValidBmp);

    if (pIntRes != NULL)
        (void)vxbResourceFree (pDev, pIntRes);

    if (pRes != NULL)
        (void)vxbResourceFree (pDev, pRes);

    vxbMemFree (pDrvCtrl);

    return ERROR;
    }

3.3修改gpio设备树节点

  参考上面的Attach实现,我们可以看到该函数里面提取了gpio设备树节点里的一些属性,这些属性在我们修改gpio节点是是一定要注意的。

  比如gpio-cell属性,且值必须为4

image-20211128193951690

设备树节点添加在zynq-navigator.dts文件中,如下图所示

image-20211128194152204

4.测试gpio

  接下来验证一下改完设备树gpio节点后,gpio操作是否好使了,开发板的MIO7连接的是一个led,接下来在C解释器下直接操作该gpio,如果led能亮,说明gpio已经好使了,依次输入

vxbGpioAlloc(7)//申请gpio7
vxbGpioSetDir(7,1)//配置为输出
vxbGpioSetValue(7,1)//gpio7 置1输出高电平

  LED亮

Guess you like

Origin blog.csdn.net/weixin_42314225/article/details/121596418