GPIO 驱动实现
本节使用 imx6Q 的 SylixOS GPIO 为例。在编写 GPIO 驱动之前先设置好 GPIO 控制器,即填充IMX6Q_GPIO_CONTROLER 结构体中的成员变量。
typedef struct {
addr_t GPIOC_ulPhyAddrBase; /* 物理地址基地址 */
addr_t GPIOC_stPhyAddrSize; /* 物理地址空间大小 */
ULONG CPIOC_ulIrqNum1; /* 0~15 引脚的中断号 */
ULONG CPIOC_ulIrqNum2; /* 16~32 引脚的中断号 */
UINT GPIOC_uiPinNumber;
addr_t GPIOC_ulVirtAddrBase; /* 虚拟地址基地址 */
} IMX6Q_GPIO_CONTROLER, *PIMX6Q_GPIO_CONTROLER;
因为 GPIO 一般都是成组的,这样便把每个 GPIO 的寄存器等信息抽象。一组 GPIO 控制器结构,例如 GPIO0 和 GPIO1 是一组(共 32 个 GPIO 口),共用一组寄存器,所以 GPIO0 和 GPIO1 可以一起用 IMX6Q_GPIO_CONTROLER 来控制。
i.MX6Q 有 7 组 GPIO 控制器。
static IMX6Q_GPIO_CONTROLER _G_imx6qGpioControlers[] = {
{
GPIO1_BASE_ADDR,
LW_CFG_VMM_PAGE_SIZE,
IMX_INT_GPIO1_INT15_0,
IMX_INT_GPIO1_INT31_16,
32,
GPIO1_BASE_ADDR, /* 默认是物理地址 */
},{
……
}, {
GPIO7_BASE_ADDR,
LW_CFG_VMM_PAGE_SIZE,
IMX_INT_GPIO7_INT15_0,
IMX_INT_GPIO7_INT31_16,
14,
GPIO7_BASE_ADDR,
},
};
static LW_GPIO_CHIP _G_imx6qGpioChip ;
申请 GPIO 管脚
调用 imx6qGpioRequest 函数可以申请一个 GPIO 管脚。首先检查管脚号是否有效,即是否在 GPIO 控制器控制的范围内,接着根据管脚号计算出该 GPIO 相关的寄存器地址,最后通过 writel 函数直接操作寄存器即可。
static INT imx6qGpioRequest (PLW_GPIO_CHIP pGpioChip, UINT uiOffset)
{
INT iRet;
……
iRet = GpioPinmuxSet(uiOffset / 32, uiOffset % 32, 0x10 | ALT5);
return (iRet);
}
UINT32 GpioPinmuxSet (UINT32 uiPinGroup, UINT32 uiPinNum, UINT32 uiCfg)
{
addr_t atAddr = GpioPinmuxReg[uiPinGroup][uiPinNum];
/* 计算对应GPIO管脚的寄存器地址*/
……
writel(uiCfg, atAddr); /* GPIO 功能均为 ALT5 */
return ERROR_NONE;
}
释放 GPIO 管脚
调用 x6qGpioFree 函数释放一个正在被使用的 GPIO,如果当前是中断模式,则放弃中断输入功能。
static VOID imx6qGpioFree (PLW_GPIO_CHIP pGpioChip, UINT uiOffset)
{
PIMX6Q_GPIO_CONTROLER pGpioControler;
addr_t atBase;
UINT32 uiRegVal;
……
uiRegVal = readl(atBase + GPIO_IMR) &(~(1 << (uiOffset % 32)));
writel(uiRegVal, atBase + GPIO_IMR); /* 禁止该 GPIO 中断 */
uiRegVal = readl(atBase + GPIO_GDIR) &(~(1 << (uiOffset % 32)));
writel(uiRegVal, atBase + GPIO_GDIR); /* 设置该 GPIO 为输入 */
}
获得指定 GPIO 方向
调用 imx6qGpioGetDirection 函数获得指定 GPIO 的方向。
static INT imx6qGpioGetDirection (PLW_GPIO_CHIP pGpioChip, UINT uiOffset)
{
……
uiRegVal = readl(atBase + GPIO_GDIR) & (1 << (uiOffset % 32));
if(uiRegVal) {
return (1);
} else {
return (0);
}
}
设置指定 GPIO 为输入
调用 imx6qGpioDirectionInput 函数设置指定 GPIO 为输入模式。
static INT imx6qGpioDirectionInput (PLW_GPIO_CHIP pGpioChip, UINT uiOffset)
{
……
uiRegVal = readl(atBase + GPIO_GDIR) &(~(1 << (uiOffset % 32)));
writel(uiRegVal, atBase + GPIO_GDIR); /* 设置该 GPIO 为输入 */
return (0);
}
获得指定 GPIO 电平
调用 imx6qGpioGet 函数获得指定 GPIO 电平。
static INT imx6qGpioGet (PLW_GPIO_CHIP pGpioChip, UINT uiOffset)
{
……
uiRegVal = readl(atBase + GPIO_PSR) & (1 << (uiOffset % 32));
if(uiRegVal) {
return (1);
} else {
return (0);
}
}
设置指定 GPIO 为输出
调用 imx6qGpioDirectionOutput 函数设置指定 GPIO 为输出模式。
static INT imx6qGpioDirectionOutput (PLW_GPIO_CHIP pGpioChip,
UINT uiOffset,
INT iValue)
{
……
uiRegVal = readl(atBase + GPIO_GDIR) | (1 << (uiOffset % 32));
writel(uiRegVal, atBase + GPIO_GDIR); /* 设置该 GPIO 为输出 */
if(iValue) { /* 设置该 GPIO 默认电平 */
uiRegVal = readl(atBase + GPIO_DR) | (1 << (uiOffset % 32));
writel(uiRegVal, atBase + GPIO_DR); /* 设置该 GPIO 默认电平为 1 */
} else {
uiRegVal = readl(atBase + GPIO_DR) &(~(1 << (uiOffset % 32)));
writel(uiRegVal, atBase + GPIO_DR); /* 设置该 GPIO 默认电平为 0 */
}
return (0);
}
设置指定 GPIO 电平
调用 imx6qGpioSet 函数设置指定 GPIO 电平。
static VOID imx6qGpioSet (PLW_GPIO_CHIP pGpioChip,
UINT uiOffset,
INT iValue)
{
……
if(iValue) { /* 设置该 GPIO 电平 */
uiRegVal = readl(atBase + GPIO_DR) | (1 << (uiOffset % 32));
writel(uiRegVal, atBase + GPIO_DR); /* 设置该 GPIO 电平为 1 */
} else {
uiRegVal = readl(atBase + GPIO_DR) &(~(1 << (uiOffset % 32)));
writel(uiRegVal, atBase + GPIO_DR); /* 设置该 GPIO 电平为 0 */
}
}
设置指定 GPIO 为外部中断输入
调用 imx6qGpioSetupIrq 函数设置指定 GPIO 为外部中断输入。
static INT imx6qGpioSetupIrq (PLW_GPIO_CHIP pGpioChip,
UINT uiOffset,
BOOL bIsLevel,
UINT uiType)
{
……
if(uiPinOff > 15) { /* 计算需要设置的寄存器位 */
atBase = atBase + GPIO_ICR2;
uiIrqNum = pGpioControler->CPIOC_ulIrqNum2;
uiPinOff = (uiPinOff - 15) << 1;
} else {
atBase = atBase + GPIO_ICR1;
uiIrqNum = pGpioControler->CPIOC_ulIrqNum1;
uiPinOff = uiPinOff << 1;
}
uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL);
writel(uiRegVal &(~(1 << (uiOffset % 32))),
pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL); /* 清除双边沿中断触发设置 */
if (bIsLevel) { /* 如果是电平触发中断 */
if(uiType) { /* 高电平触发 */
uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
uiRegVal = uiRegVal | 0x1 << uiPinOff;
writel(uiRegVal, atBase);
} else { /* 低电平触发 */
uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
uiRegVal = uiRegVal | 0x0 << uiPinOff;
writel(uiRegVal, atBase);
}
} else {
if (uiType == 1) { /* 上升沿触发 */
uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
uiRegVal = uiRegVal | 0x2 << uiPinOff;
writel(uiRegVal, atBase);
} else if (uiType == 0) { /* 下降沿触发 */
uiRegVal = readl(atBase) & (~(0x3 << uiPinOff));
uiRegVal = uiRegVal | 0x3 << uiPinOff;
writel(uiRegVal, atBase);
} else { /* 双边沿触发 */
uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL) | (1<<(uiOffset%32));
writel(uiRegVal, pGpioControler->GPIOC_ulVirtAddrBase + GPIO_EDGE_SEL);
}
}
uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_IMR);
writel(uiRegVal | (1 << (uiOffset % 32)),
pGpioControler->GPIOC_ulVirtAddrBase + GPIO_IMR); /* 使能该 GPIO 中断 */
/*
* 测试用代码
*/
uiRegVal = readl(pGpioControler->GPIOC_ulVirtAddrBase + GPIO_IMR);
return (uiIrqNum);
}
清除指定 GPIO 中断标志
调用 imx6qGpioClearIrq 函数清除指定 GPIO 中断标志。
static VOID imx6qGpioClearIrq (PLW_GPIO_CHIP pGpioChip, UINT uiOffset)
{
……
pGpioControler = &_G_imx6qGpioControlers[uiOffset / 32];
atBase = pGpioControler->GPIOC_ulVirtAddrBase;
uiRegVal = readl(atBase + GPIO_ISR) | (1 << (uiOffset % 32));
writel(uiRegVal, atBase + GPIO_ISR); /* 清除该 GPIO 中断状态 */
}
判断 GPIO 中断标志
调用 imx6qGpioSvrIrq函数判断 GPIO 中断标志。
static irqreturn_t imx6qGpioSvrIrq (PLW_GPIO_CHIP pGpioChip, UINT uiOffset)
{
……
uiRegVal = readl(atBase + GPIO_ISR) & (1 << (uiOffset % 32));
if (uiRegVal) {
return (LW_IRQ_HANDLED);
} else {
return (LW_IRQ_NONE);
}
}
将 GPIO 驱动函数设置到 GPIO 控制器结构体。
static LW_GPIO_CHIP _G_imx6qGpioChip = {
.GC_pcLabel = "IMX6Q GPIO",
.GC_pfuncRequest = imx6qGpioRequest,
.GC_pfuncFree = imx6qGpioFree,
.GC_pfuncGetDirection = imx6qGpioGetDirection,
.GC_pfuncDirectionInput = imx6qGpioDirectionInput,
.GC_pfuncGet = imx6qGpioGet,
.GC_pfuncDirectionOutput = imx6qGpioDirectionOutput,
.GC_pfuncSetDebounce = imx6qGpioSetDebounce,
.GC_pfuncSetPull = LW_NULL,
.GC_pfuncSet = imx6qGpioSet,
.GC_pfuncSetupIrq = imx6qGpioSetupIrq,
.GC_pfuncClearIrq = imx6qGpioClearIrq,
.GC_pfuncSvrIrq = imx6qGpioSvrIrq,
};
将 GPIO 驱动安装到操作系统。
INT imx6qGpioDrv (VOID)
{
PIMX6Q_GPIO_CONTROLER pGpioControler;
INT i;
_G_imx6qGpioChip.GC_uiBase = 0;
_G_imx6qGpioChip.GC_uiNGpios = 0;
/*
* GPIO 控制器时钟和电源使能,及地址映射,
*/
for (i = 0; i < GPIO_CONTROLER_NR; i++) {
pGpioControler = &_G_imx6qGpioControlers[i];
_G_imx6qGpioChip.GC_uiNGpios += pGpioControler->GPIOC_uiPinNumber;
}
return (API_GpioChipAdd(&_G_imx6qGpioChip));
}