文章目录
使用VxWorks的GPIO子系统
1.设备树文件
接下来要为领航者开发板添加设备树文件,新建zynq-navigator.dts文件,此时根节点下没有任何设备节点,接下来的工作就是把我们所有的设备逐渐添加到该设备文件中。
2.gpio子系统
我认为所谓gpio子系统只不过是一种高大上的叫法,gpio操作的本质永远是干寄存器,只不过为了兼顾各个平台,使用面向对象的思想将硬件和软件弱耦合化,使用户感受不到硬件太大的变化,这就是gpio子系统存在的意义。
2.1风河BSP所作的工作
为了方便阅读风河为ZYQN提供的BSP源码,我们在工程目录下新建一个名为WindRiver的目录,将我们用到的相关驱动文件放到该目录下
2.2第三方开发者要做的工作
风河只会提供ZYNQ7000的片上外设资源的BSP,如GPIO、SPI等接口一般来说我们都是直接能用的,和Linux驱动一样,就拿GPIO来说,子系统都给你初始化好了,风河提供了一组GPIO的操作函数,这些函数是与硬件无关的,也就是说你换一个硬件平台,这些函数也照样适用,这就叫做GPIO子系统。
我们将Navigator开发板上的外设驱动放在名为Navigator的目录下,别忘了把这两个目录的路径添加到Include path里
2.3添加gpio驱动
将vxbFdtZynqGpio.c拷贝到WindRiver目录下,编译后将设备树zynq-navigator.dtb和uVxWorks拷贝到SD卡,上电启动。
然而,启动后如下图所示,gpio驱动加载了,但是没有probe,这个问题简单,看一下vxbFdtZynqGpio.c文件和设备树中的gpio节点便知。
用于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吧。
3.调试gpio驱动
先看compatible=xlnx,zynq7k-misccfg的情况,使用VSCode在风河BSP目录下搜索所有关键字为xlnx,zynq7k-misccfg的文件。
3.1分析vxbFdtZynq7kMiscCfg.c
确实搜到了,看一下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
设备树节点添加在zynq-navigator.dts文件中,如下图所示
4.测试gpio
接下来验证一下改完设备树gpio节点后,gpio操作是否好使了,开发板的MIO7连接的是一个led,接下来在C解释器下直接操作该gpio,如果led能亮,说明gpio已经好使了,依次输入
vxbGpioAlloc(7)//申请gpio7
vxbGpioSetDir(7,1)//配置为输出
vxbGpioSetValue(7,1)//gpio7 置1输出高电平
LED亮