/* vxbFtGpioCtrl.c - Driver for GPIO controller */ /* * * This program is OPEN SOURCE software: you can redistribute it and/or modify it; * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ /* includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define GPIO_DBG_ON #ifdef GPIO_DBG_ON #define GPIO_DBG_IRQ 0x00000001 #define GPIO_DBG_RW 0x00000002 #define GPIO_DBG_ERR 0x00000004 #define GPIO_DBG_ALL 0xffffffff #define GPIO_DBG_OFF 0x00000000 LOCAL UINT32 gpioDbgMask = GPIO_DBG_ALL; IMPORT FUNCPTR _func_logMsg; #define GPIO_DBG(mask, string, a, b, c, d, e, f) \ if ((gpioDbgMask & mask) || (mask == GPIO_DBG_ALL)) \ if (_func_logMsg != NULL) \ (* _func_logMsg)(string, a, b, c, d, e, f) #else #define GPIO_DBG(mask, string, a, b, c, d, e, f) #endif /* GPIO_DBG_ON */ LOCAL void vxbFtGpioInstInit(VXB_DEVICE_ID pInst); LOCAL void vxbFtGpioInstInit2(VXB_DEVICE_ID pInst); LOCAL void vxbFtGpioInstConnect(VXB_DEVICE_ID pInst); LOCAL void vxbFtGpioShow (VXB_DEVICE_ID pDev, int verbose); LOCAL STATUS vxbFtGpioPinModeSet (VXB_DEVICE_ID pDev, UINT32 port, UINT32 pin,UINT32 mode); LOCAL UINT32 vxbFtGpioPinInput (VXB_DEVICE_ID pDev, UINT32 port, UINT32 pin); LOCAL STATUS vxbFtGpioPinOuput (VXB_DEVICE_ID pDev, UINT32 port, UINT32 pin, UINT32 value); LOCAL STATUS vxbFtGpioISRSet (VXB_DEVICE_ID pDev, UINT32 pin, VOIDFUNCPTR pIsr, void * pArg); LOCAL void vxbFtGpioISR (FT_GPIO_DRVCTRL * pDrvCtrl); LOCAL struct drvBusFuncs vxbFtGpioDrvFuncs = { vxbFtGpioInstInit, /* devInstanceInit */ vxbFtGpioInstInit2, /* devInstanceInit2 */ vxbFtGpioInstConnect /* devConnect */ }; LOCAL device_method_t vxbFtGpioDrv_methods[] = { DEVMETHOD (busDevShow, vxbFtGpioShow), DEVMETHOD_END }; /* FT2000/4 GPIO VxBus registration info */ LOCAL struct vxbDevRegInfo vxbFtGpioDrvRegistration = { NULL, /* pNext */ VXB_DEVID_DEVICE, /* devID */ VXB_BUSID_PLB, /* busID = PLB */ VXB_VER_4_0_0, /* busVer */ "ftGpio", /* drvName */ &vxbFtGpioDrvFuncs, /* pDrvBusFuncs */ &vxbFtGpioDrv_methods[0], /* pMethods */ NULL /* devProbe */ }; /****************************************************************************** * * vxbFtGpioDrvRegister - register FT2000/4 GPIO driver * * This routine registers the FT2000/4 GPIO driver with the vxBus subsystem. * * RETURNS: N/A * * ERRNO: N/A */ void vxbFtGpioDrvRegister (void) { /* call the vxBus routine to register the GPIO driver */ vxbDevRegister (&vxbFtGpioDrvRegistration); } /******************************************************************************* * * vxbFtGpioInstInit - first level initialization routine of GPIO modules * * This is the function called to perform the first level initialization of * the six FT2000/4 GPIO modules. * * NOTE: * * This routine is called early during system initialization, and * *MUST NOT* make calls to OS facilities such as memory allocation * and I/O. * * RETURNS: N/A * * ERRNO: N/A * * \NOMANUAL * */ LOCAL void vxbFtGpioInstInit ( VXB_DEVICE_ID pInst ) { } /******************************************************************************* * * vxbFtGpioInstInit2 - second level initialization routine of GPIO modules * * This routine performs the second level initialization of the GPIO modules. * * This routine is called later during system initialization. OS features * such as memory allocation are available at this time. * * RETURNS: N/A * * ERRNO: N/A * * \NOMANUAL * */ LOCAL void vxbFtGpioInstInit2 ( VXB_DEVICE_ID pInst ) { STATUS st[GPIO_PORT_MUM]; FT_GPIO_DRVCTRL * pDrvCtrl; UINT32 port = 0, pin = 0; HCF_DEVICE * pHcf; UINT8 ** ppPortModeTable[GPIO_PORT_MUM]; UINT8 *pPortModeTable[GPIO_PORT_MUM]; pDrvCtrl = (FT_GPIO_DRVCTRL *)hwMemAlloc(sizeof (FT_GPIO_DRVCTRL)); if (pDrvCtrl == NULL) { GPIO_DBG(GPIO_DBG_ERR, "vxbFtGpioInstInit: pDrvCtrl alloc failed\r\n.",1,2,3,4,5,6); return; } pInst->pDrvCtrl = pDrvCtrl; pDrvCtrl->pInst = pInst; /* map register base */ if (vxbRegMap(pInst, 0, &pDrvCtrl->gpioHandle) == ERROR) { GPIO_DBG (GPIO_DBG_ERR, "vxbFtGpioInstInit: vxbRegMap GPIO ERROR\n", 1, 2, 3, 4, 5, 6); #ifndef _VXBUS_BASIC_HWMEMLIB hwMemFree ((char *)pDrvCtrl); #endif /* _VXBUS_BASIC_HWMEMLIB */ pInst->pDrvCtrl = NULL; return; } pDrvCtrl->gpioModeSet = vxbFtGpioPinModeSet; pDrvCtrl->gpioInput = vxbFtGpioPinInput; pDrvCtrl->gpioOutput = vxbFtGpioPinOuput; pDrvCtrl->gpioISRSet = vxbFtGpioISRSet; pDrvCtrl->intEnabled = FALSE; /* clear all interrupt */ GPIO_WRITE_4 (pDrvCtrl, GPIO_INTEN_A, 0); ppPortModeTable[0] = &pPortModeTable[0]; ppPortModeTable[1] = &pPortModeTable[1]; pHcf = (struct hcfDevice *) hcfDeviceGet (pInst); if (pHcf == NULL) { #ifndef _VXBUS_BASIC_HWMEMLIB hwMemFree ((char *)pDrvCtrl); #endif /* _VXBUS_BASIC_HWMEMLIB */ pInst->pDrvCtrl = NULL; return; } if (devResourceGet (pHcf, "trigger", HCF_RES_INT,(void *) &pDrvCtrl->triggerMode) != OK) { pDrvCtrl->triggerMode = 0; } st[0] = devResourceGet (pHcf, "portAModeTable", HCF_RES_ADDR, (void **)ppPortModeTable[0]); st[1] = devResourceGet (pHcf, "portBModeTable", HCF_RES_ADDR, (void **)ppPortModeTable[1]); for(port = 0; port < GPIO_PORT_MUM; port++) { for(pin = 0; pin < FT_GPIO_PORT_WIDTH; pin ++) { if(st[port] == OK) vxbFtGpioPinModeSet(pInst, port, pin, *(pPortModeTable[port] + pin)); else vxbFtGpioPinModeSet(pInst, port, pin, GPIO_MODE_NOT_USED); } } return; } /******************************************************************************* * * vxbFtInstConnect - third level initialization routine of GPIO modules * * This is the function called to perform the third level initialization of * the GPIO modules. * * RETURNS: N/A * * ERRNO: N/A * * \NOMANUAL * */ LOCAL void vxbFtGpioInstConnect ( VXB_DEVICE_ID pInst ) { /* nothing is done here */ } LOCAL STATUS vxbFtGpioBitSet(FT_GPIO_DRVCTRL * pDrvCtrl, UINT32 reg, UINT32 pin, UINT32 bit) { UINT32 value=0; if(pin >= FT_GPIO_PORT_WIDTH) { GPIO_DBG(GPIO_DBG_ERR, "[reg 0x%x]Bad pin %d! Only support pin: 0~%d \r\n", reg, pin, FT_GPIO_PORT_WIDTH - 1,4,5,6); return ERROR; } if((bit!=0)&&(bit!=1)) { GPIO_DBG(GPIO_DBG_ERR, "[reg 0x%x]Bad bit value %d! \r\n", reg, bit,3,4,5,6); return ERROR; } value = GPIO_READ_4(pDrvCtrl, reg); switch(bit) { case 0: value &= ~(1<pDrvCtrl; STATUS st = OK; /*all pin of portA use a common interrupt number*/ if(pDrvCtrl->intEnabled == FALSE) { st |= vxbIntConnect(pDev, 0, vxbFtGpioISR, pDrvCtrl); st |= vxbIntEnable(pDev, 0, vxbFtGpioISR, pDrvCtrl); if (OK == st) pDrvCtrl->intEnabled = TRUE; else GPIO_DBG(GPIO_DBG_ERR, "Failed to enable port 0 pin %d interrupt\r\n", pin, 2,3,4,5,6); } return st; } /******************************************************************************* * * vxbFtGpioPinIntDisable - disable interrupt of a pin * * the function disable interrupt of a pin. * * RETURNS: OK or ERROR * * ERRNO: N/A * * \NOMANUAL * */ LOCAL STATUS vxbFtGpioPinIntDisable ( VXB_DEVICE_ID pDev, UINT32 pin ) { FT_GPIO_DRVCTRL * pDrvCtrl = pDev->pDrvCtrl; STATUS st = OK; STATUS disableFlag = TRUE; UINT32 i; /*all pin of a portA use a common interrupt number, so only if all of pins of portA are not used as interrupt pin, we need to disable the interrupt*/ if(pDrvCtrl->intEnabled == TRUE) { for(i = 0; i < FT_GPIO_PORT_WIDTH; i++) { if((pDrvCtrl->pinMode[0][i] == GPIO_MODE_INT) && (i != pin)) { /*have another pin that used as interrupt pin, so don't need to disable interrupt */ disableFlag = FALSE; break; } } if(disableFlag == TRUE) { st |= vxbIntDisable(pDev, 0, vxbFtGpioISR, pDrvCtrl); st |= vxbIntDisconnect(pDev, 0, vxbFtGpioISR, pDrvCtrl); if (OK == st) pDrvCtrl->intEnabled = FALSE; else GPIO_DBG(GPIO_DBG_ERR, "Failed to disable port 0 pin %d interrupt\r\n", pin, 2,3,4,5,6); } } return st; } /******************************************************************************* * * vxbFtGpioPinModeSet - set mode of a pin * * the function set mode of a pin. * * RETURNS: OK or ERROR * * ERRNO: N/A * * \NOMANUAL * */ LOCAL STATUS vxbFtGpioPinModeSet ( VXB_DEVICE_ID pDev, UINT32 port, UINT32 pin, UINT32 mode ) { FT_GPIO_DRVCTRL * pDrvCtrl = pDev->pDrvCtrl; STATUS st = OK; UINT32 level = 0, polar = 0; if ((port > GPIO_PORT_B) || (pin >= FT_GPIO_PORT_WIDTH) || (mode > GPIO_MODE_INT)) { GPIO_DBG(GPIO_DBG_ERR, "Parameter is out of range : GPIO port %d, pin %d, mode %d\r\n", port, pin, mode,4,5,6); return ERROR; } /* only port A support interrupt */ if((mode == GPIO_MODE_INT) && (port != GPIO_PORT_A)) { GPIO_DBG(GPIO_DBG_ERR, "Only port A support interrupt \r\n", 1,2,3,4,5,6); return ERROR; } switch(mode) { case GPIO_MODE_NOT_USED: case GPIO_MODE_IN: if (port == GPIO_PORT_A) { /* st |= */vxbFtGpioPinIntDisable(pDev, pin); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_SW_DDR_A, pin, GPIO_DIR_IN); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INTMASK_A, pin, GPIO_INT_MASK); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INTEN_A, pin, GPIO_INT_DIS); } else { st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_SW_DDR_B, pin, GPIO_DIR_IN); } break; case GPIO_MODE_OUT: if (port == GPIO_PORT_A) { /* st |= */vxbFtGpioPinIntDisable(pDev, pin); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_SW_DDR_A, pin, GPIO_DIR_OUT); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INTMASK_A, pin, GPIO_INT_MASK); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INTEN_A, pin, GPIO_INT_DIS); } else { st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_SW_DDR_B, pin, GPIO_DIR_OUT); } break; case GPIO_MODE_INT: if(port == GPIO_PORT_A) { switch(pDrvCtrl->triggerMode) { case VXB_INTR_TRIG_FALLING_EDGE: level = GPIO_INT_TYPE_EDGE; polar = GPIO_INT_POL_LOW_DOWN; break; case VXB_INTR_TRIG_RISING_EDGE: level = GPIO_INT_TYPE_EDGE; polar = GPIO_INT_POL_HIGH_UP; break; case VXB_INTR_TRIG_ACTIVE_LOW: level = GPIO_INT_TYPE_LEVEL; polar = GPIO_INT_POL_LOW_DOWN; break; case VXB_INTR_TRIG_ACTIVE_HIGH: level = GPIO_INT_TYPE_LEVEL; polar = GPIO_INT_POL_HIGH_UP; break; default: pDrvCtrl->pinMode[port][pin] = GPIO_MODE_NOT_USED; GPIO_DBG(GPIO_DBG_ERR, "Mode set error of port %d pin %d: trigger mode error \r\n", port, pin,3,4,5,6); return ERROR; } /* st |= */vxbFtGpioPinIntEnable(pDev, pin); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_SW_DDR_A, pin, GPIO_DIR_IN); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INTMASK_A, pin, GPIO_INT_NOT_MASK); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INTTYPE_LEVEL_A, pin, level); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INT_POLARITY_A, pin, polar); st |= vxbFtGpioBitSet(pDrvCtrl, GPIO_INTEN_A, pin, GPIO_INT_ENA); } break; default: return ERROR; } if (OK != st) { pDrvCtrl->pinMode[port][pin] = GPIO_MODE_NOT_USED; GPIO_DBG(GPIO_DBG_ERR, "Mode set error of port %d pin %d\r\n", port, pin,3,4,5,6); return ERROR; } else { pDrvCtrl->pinMode[port][pin] = mode; } return OK; } /******************************************************************************* * * vxbFtGpioPinInput - gpio pin input value * * The function return value of specified intput pin. * * RETURNS: input value * * ERRNO: N/A * * \NOMANUAL * */ LOCAL UINT32 vxbFtGpioPinInput ( VXB_DEVICE_ID pDev, UINT32 port, UINT32 pin ) { FT_GPIO_DRVCTRL * pDrvCtrl = pDev->pDrvCtrl; if ((port > GPIO_PORT_B) || (pin >= FT_GPIO_PORT_WIDTH)) { GPIO_DBG(GPIO_DBG_ERR, "Parameter is out of range :GPIO port %d, pin %d \r\n", port, pin,3,4,5,6); return (UINT32)ERROR; } if (pDrvCtrl->pinMode[port][pin] != GPIO_MODE_IN) { GPIO_DBG(GPIO_DBG_ERR, "port %d pin %d is not in input mode\r\n",port, pin,3,4,5,6); return (UINT32)ERROR; } if ( port == GPIO_PORT_A) return ((GPIO_READ_4(pDrvCtrl, GPIO_EXT_A) >> pin) & 1); else return ((GPIO_READ_4(pDrvCtrl, GPIO_EXT_B) >> pin) & 1); } /******************************************************************************* * * vxbFtGpioPinOuput - output value to gpio pin * * Write value to the specified pin. * * RETURNS: OK or ERROR if the pin is invalid. * * ERRNO: N/A * * \NOMANUAL * */ LOCAL STATUS vxbFtGpioPinOuput ( VXB_DEVICE_ID pDev, UINT32 port, UINT32 pin, UINT32 value ) { FT_GPIO_DRVCTRL * pDrvCtrl = pDev->pDrvCtrl; if ((port > GPIO_PORT_B) || (pin >= FT_GPIO_PORT_WIDTH)) { GPIO_DBG(GPIO_DBG_ERR, "Parameter is out of range :GPIO port %d, pin %d \r\n", port, pin,3,4,5,6); return ERROR; } if (pDrvCtrl->pinMode[port][pin] != GPIO_MODE_OUT) { GPIO_DBG(GPIO_DBG_ERR, "port %d pin %d is not in output mode\r\n",port, pin,3,4,5,6); return ERROR; } if((value!=0)&&(value!=1)) { GPIO_DBG(GPIO_DBG_ERR, "Bad pin value %d! Only support value: 0,1 \r\n", value,2,3,4,5,6); return ERROR; } if (port == GPIO_PORT_A) vxbFtGpioBitSet(pDrvCtrl, GPIO_SW_DR_A, pin, value); else vxbFtGpioBitSet(pDrvCtrl, GPIO_SW_DR_B, pin, value); return OK; } /******************************************************************************* * * vxbFtGpioISRSet - set a function to be called on the gpio interrupt * * This function is called to set a function which can be called when * the gpio interrupt occurs. * * RETURNS: OK or ERROR if the pin is invalid. * * ERRNO: N/A * * \NOMANUAL * */ LOCAL STATUS vxbFtGpioISRSet ( VXB_DEVICE_ID pDev, UINT32 pin, VOIDFUNCPTR pIsr, /* ISR */ void * pArg /* parameter */ ) { FT_GPIO_DRVCTRL * pDrvCtrl = pDev->pDrvCtrl; if (pin >= FT_GPIO_PORT_WIDTH) { GPIO_DBG(GPIO_DBG_ERR, "Parameter is out of range :GPIO pin %d \r\n", pin, 2,3,4,5,6); return ERROR; } if (pDrvCtrl->pinMode[0][pin] != GPIO_MODE_INT) { GPIO_DBG(GPIO_DBG_ERR, "port 0 pin %d is not in interrupt mode\r\n",pin,2,3,4,5,6); return ERROR; } pDrvCtrl->isrTable[pin].pIsr = pIsr; pDrvCtrl->isrTable[pin].pArg = pArg; return OK; } /******************************************************************************* * * vxbFtGpioISR - interrupt level processing for gpio module * * This routine handles gpio interrupts. It acknowledges the interrupt and * calls the routine installed by vxbFtGpioISRSet(). * * RETURNS: N/A * * ERRNO: N/A * * \NOMANUAL * */ LOCAL void vxbFtGpioISR ( FT_GPIO_DRVCTRL * pDrvCtrl ) { UINT32 val; UINT32 ix; /* read interrupt flags */ val = GPIO_READ_4 (pDrvCtrl, GPIO_INTSTATUS_A); if (val == 0) { GPIO_DBG(GPIO_DBG_ERR, "vxFtGpioISR: it's not a gpio interrupt. \r\n.", 1,2,3,4,5,6); goto exit; } for (ix = 0; ix < FT_GPIO_PORT_WIDTH; ix++) { UINT32 bit = 1 << ix; if (val & bit) { if (pDrvCtrl->isrTable[ix].pIsr != NULL) { pDrvCtrl->isrTable[ix].pIsr(pDrvCtrl->isrTable[ix].pArg); } else { /* suspicious int */ GPIO_DBG(GPIO_DBG_ERR, "vxFtGpioISR: neet to set ISR for pin %d \r\n.", ix,2,3,4,5,6); goto exit; } } } exit: /* clear interrupt flags */ GPIO_WRITE_4 (pDrvCtrl, GPIO_PORTA_EOI_A, val); return; } LOCAL void vxbFtGpioShow (VXB_DEVICE_ID pDev, int verbose) { FT_GPIO_DRVCTRL * pDrvCtrl; /* check for valid parameter */ VXB_ASSERT_NONNULL_V (pDev); pDrvCtrl = (FT_GPIO_DRVCTRL *) pDev->pDrvCtrl; printf (" %s unit %d on %s @ %p with busInfo %08p\n", pDev->pName, pDev->unitNumber, vxbBusTypeString (pDev->busID), pDev, pDev->u.pSubordinateBus); if (verbose > 0) { int i, j; printf (" BAR0 @ 0x%08x (memory mapped)\n",pDev->pRegBase[0]); for (i = 0; i < GPIO_PORT_MUM; ++i) { printf(" port %c:", i == 0 ? 'A' : 'B'); for (j = 0; j < FT_GPIO_PORT_WIDTH; ++j) { printf(" %d", pDrvCtrl->pinMode[i][j]); } printf("\n"); } } } #define FT_GPIO_DEBUD #ifdef FT_GPIO_DEBUD void gpioModeShow(UINT32 gpio) { int i,j; VXB_DEVICE_ID pDev; FT_GPIO_DRVCTRL * pCtrl; pDev = vxbInstByNameFind("ftGpio", gpio); if (pDev == NULL) { return; } pCtrl = pDev->pDrvCtrl; for(i = 0; i < GPIO_PORT_MUM; i++) { for(j = 0; j < FT_GPIO_PORT_WIDTH; j++) printf("port %d pin %d mode %d\n", i, j, pCtrl->pinMode[i][j]); } printf("intEnabled %d\n", pCtrl->intEnabled); printf("triggerMode %x\n", pCtrl->triggerMode); } void gpioModeSetTest (UINT32 gpio, UINT32 port, UINT32 pin, UINT32 mode) { VXB_DEVICE_ID pDev; FT_GPIO_DRVCTRL * pCtrl; pDev = vxbInstByNameFind("ftGpio", gpio); if (pDev == NULL) { return; } pCtrl = pDev->pDrvCtrl; pCtrl->gpioModeSet(pDev, port, pin, mode); } void gpioOutTest (UINT32 gpio, UINT32 port, UINT32 pin, UINT32 val) { VXB_DEVICE_ID pDev; FT_GPIO_DRVCTRL * pCtrl; pDev = vxbInstByNameFind("ftGpio", gpio); if (pDev == NULL) { return; } pCtrl = pDev->pDrvCtrl; pCtrl->gpioOutput(pDev, port, pin, val); } void gpioInTest (UINT32 gpio, UINT32 port, UINT32 pin) { VXB_DEVICE_ID pDev; FT_GPIO_DRVCTRL * pCtrl; pDev = vxbInstByNameFind("ftGpio", gpio); if (pDev == NULL) { return; } pCtrl = pDev->pDrvCtrl; printf("input val %d\n", pCtrl->gpioInput(pDev, port, pin)); } LOCAL void gpioIsrFunction(void * arg) { UINT32 * pin = (UINT32 *)arg; logMsg("GPIO pin %d ISR \n",* pin,0,0,0,0,0); } LOCAL UINT32 isrArgTest = 0; void gpioIsrSetTest (UINT32 gpio, UINT32 pin) { VXB_DEVICE_ID pDev; FT_GPIO_DRVCTRL * pCtrl; pDev = vxbInstByNameFind("ftGpio", gpio); if (pDev == NULL) { return; } pCtrl = pDev->pDrvCtrl; isrArgTest = pin; pCtrl->gpioISRSet(pDev, pin, gpioIsrFunction, &isrArgTest); } #endif