Gpio interrupt doesn't seem to work

Hi,

I am trying to make use of the Colibri iMX7 SOM’s SODIMM pin 22 as a rising edge interrupt signal. My application runs on your WINCE700 BSP.

I have used interrupts before on toradex boards on some other gpios and have been able to get them working. I am unable to understand why it does not seem this time.

Another issue that I noticed is that even though I am configuration the gpio pin to be rising edge triggered when using the Imx7Gpio_SetConfigString() interface, the configuration that I retrieve by calling the Imx7Gpio_GetConfigString() suggests that the gpio is configured for low level interrupt triggering.

Below is the code that I am using to initialize the gpio and the interrupt irqs

/* Inititalize the GPIO interupt for the interrupt from FPGA for data request
*/
int printTestingInterruptInit( void )
{
	BOOL    fSuccess            = TRUE;  
    DWORD   irqNum              = 0;
    TCHAR   configString[200];

	/// Initialize the interrupt input gpio as a rising edge triggered interrupt input
	if(!Imx7Gpio_SetConfigString( hGpio, ioPrintingTestDataRequest, NULL, L"AltFn=-1,dir=in,pull=up47k,inmode=std,irqtrig=rising", StoreVolatile)) {
        LOG_ERROR("\nERROR, [%s, %d], Failed to initialize the print test interrupt gpio", __FUNCTION__, __LINE__ );
        return ERROR_FPGA_INIT_FPGA_INTERRUPT;
    }
    LOG_ERROR("\nOK, [%s, %d], Print testing gpio set config string ok", __FUNCTION__, __LINE__ );

	/// Initialize the interrupt input gpio as a rising edge triggered interrupt input
	if(!Imx7Gpio_GetConfigString( hGpio, ioPrintingTestDataRequest, NULL, configString, sizeof(configString))) {
        LOG_ERROR("\nERROR, [%s, %d], Failed to gett gpio config string", __FUNCTION__, __LINE__ );
    }
    printf("\nCHECK, [%s, %d], Config string retrieved %S", __FUNCTION__, __LINE__, configString);
	
	// Get Irq number from GPIO number
    if( !Imx7Gpio_GetConfigInt( hGpio, ioPrintingTestDataRequest, L"irq", &irqNum) ){
        LOG_ERROR("\nERROR, [%s, %d], Failed to initialize the print test interrupt gpio", __FUNCTION__, __LINE__ );
        return ERROR_FPGA_INIT_FPGA_INTERRUPT;
    }
    printf("\nCHECK, [%s, %d], Irq number %u", __FUNCTION__, __LINE__, irqNum);

	
	/*IST init for Data request from FPGA*/
	// Create an Event
	hPrintingTestDataRequest = CreateEvent(NULL, FALSE, FALSE, PRINTING_TEST_DATA_REQUEST_EVENT );
	if( hPrintingTestDataRequest == NULL ){
		LOG_ERROR("\nERROR, [%s, %d], Create printing test data request event failed number %d", __FUNCTION__, __LINE__, GetLastError());
        return ERROR_FPGA_INIT_FPGA_INTERRUPT;
	}
    
    // Get the system interrupt for the corresponding Irq number
	printingTestSysIrq = Int_RequestSysInterrupt( hIntr, irqNum );
    if( !printingTestSysIrq ){
		LOG_ERROR("\nERROR, [%s, %d], Failed to get the sys irq(%u) mapped to irqNum(%u)", __FUNCTION__, __LINE__, printingTestSysIrq, irqNum );
        return ERROR_FPGA_INIT_FPGA_INTERRUPT;
	}
    printf("\nCHECK, [%s, %d], Sys irq number %u", __FUNCTION__, __LINE__, printingTestSysIrq);

	// Initialize interrupt
    fSuccess = Int_InterruptInitialize( hIntr, printingTestSysIrq, hPrintingTestDataRequest, NULL, 0 );
    if( !fSuccess ) {
		LOG_ERROR("\nERROR, [%s, %d], Interrupt initialize failed", __FUNCTION__, __LINE__ );
        return ERROR_FPGA_INIT_FPGA_INTERRUPT;
    }
    printf("\nCHECK, [%s, %d], Interrupt initialize ok", __FUNCTION__, __LINE__);


	// If it is required to ignore any interrupt that was triggered before this
	// point in time, we need to confirm it, before calling WaitForsingleObject().
	// This is only an issue for level-triggered interrupts.
	Int_InterruptDone( hIntr, printingTestSysIrq);

	// Create thread to handle Interrupt event
	hPrintingTest_IST = CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)printTest_IST, NULL, CREATE_SUSPENDED, NULL);

	//Set this thread to highest priority
	fSuccess = CeSetThreadPriority( hPrintingTest_IST, 10 );
	if( fSuccess != 0 ){
		LOG_MSG("\nOK, [%s, %d], Thread priority set successfully", __FUNCTION__, __LINE__);
	}
    else{
        LOG_ERROR("\nERROR, [%s, %d], Failed to set swathe print thread priority", __FUNCTION__, __LINE__);
        return ERROR_FPGA_INIT_FPGA_INTERRUPT;
    }

	//Start the thread
	ResumeThread( hPrintingTest_IST );

	return NO_ERROR;
}

Below is the output log from the init code. Notice the irq trigger configuration returned:

    OK, [printTestingInterruptInit, 2247], Print testing gpio set config string ok
    CHECK, [printTestingInterruptInit, 2253], Config string retrieved dir=in,lvl=1,altfn=0,pull=up47k,strength=0,slew=slow,irqtrig=low,sion=normal
    CHECK, [printTestingInterruptInit, 2260], Irq number 196
    CHECK, [printTestingInterruptInit, 2277], Sys irq number 30
    CHECK, [printTestingInterruptInit, 2285], Interrupt initialize ok
    OK, [fpga_initInterface, 461], Data Request interrupt init OK

and this is my thread waiting for the interrupt, it continues to timeout on the event that is supposed to be signaled when the interrupt is received.

DWORD WINAPI printTest_IST( LPVOID lpParam )
{
    DWORD   waitResult = WAIT_OBJECT_0;
    HANDLE  waitHandles[2] = { INVALID_HANDLE_VALUE };
    INT32   carriagePos = 0;

    //Create an event to signal application exit
    waitHandles[0] = CreateEvent( NULL, TRUE, FALSE, _T("ExitApp") );
    if( waitHandles[0] == NULL ){
        DWORD error = GetLastError();
        LOG_ERROR("\nERROR, [%s, %d], Failed to create ExitApp Event error(%d]", __FUNCTION__, __LINE__, error);
    }
    waitHandles[1] = hPrintingTest;

	while ( 1 )
    {
        /// Wait until swathe printing is ON or exit app is signalled
        waitResult = WaitForMultipleObjects( 2, waitHandles, FALSE, INFINITE );

        if( waitResult == WAIT_FAILED ){
            LOG_ERROR("\nERROR, [%s, %d], Wait failed, error %u", __FUNCTION__, __LINE__, GetLastError());
            goto EXIT_PRINTING_TEST_IST;
        }
        else if( waitResult == WAIT_OBJECT_0 ){
            LOG_ERROR("\nCHECK, [%s, %d], Exit app event found set, exiting thread", __FUNCTION__, __LINE__ );
            goto EXIT_PRINTING_TEST_IST;
        }

        //LOG_ERROR("\nCHECK, [%s, %d], Printing test event found set", __FUNCTION__, __LINE__);

        /// Timed wait checking for interrupts from the FPGA.
        /// in case of time out recheck in case swathe printing has ended, this is to take care
        /// in case the FPGA did not send any interrupts while the swathe printing was on
        /// didn't send any interrupt
        waitResult = WaitForSingleObject( hPrintingTestDataRequest, 2000 );
        if( waitResult == WAIT_TIMEOUT ){
            LOG_ERROR("\nCHECK, [%s, %d], Waiting for interrupt signal", __FUNCTION__, __LINE__);
            continue;
            /// This let's us check whether the swathe printing is still ON
        }
        else if( waitResult == WAIT_FAILED ){
            LOG_ERROR("\nERROR, [%s, %d], Wait failed, error %u", __FUNCTION__, __LINE__, GetLastError());
            goto EXIT_PRINTING_TEST_IST;
        }
        

        /// Check if swathe printing is complete
	    Int_InterruptDone( hIntr, printingTestSysIrq );

        /// Wait signalled, i.e. rising edge interrupt registered by the system

        /// Check for spikes
        if( Imx7Gpio_GetLevel( hGpio, ioPrintingTestDataRequest ) == ioLow ){
            /// Spurious edge detected
            LOG_ERROR("\nERROR, [%s, %d], Spurious edge detected on SODIMM 24", __FUNCTION__, __LINE__);
            continue;
        }


        fpga_writeWord( 510, 1 );       /// Address at which the FPGA will generate
                                        /// reset its internal data buffers
        for( int i = 0 ; i < HEADS_IN_USE ; i++ ) {
            fpga_loadPrintData( i, printTestData );
        }
        fpga_writeWord( 126, 1 );       /// Address at which the FPGA will generate
                                        /// firing sequence for the print heads
	}

EXIT_PRINTING_TEST_IST:
    LOG_ERROR("\nCHECK, [%s, %d], Exiting thread", __FUNCTION__, __LINE__ );

    CloseHandle( waitHandles[0] );
    CloseHandle( waitHandles[1] );
    return NO_ERROR;
}

Also, can you please tell how the IRQ numbers are returned by the library function Imx7Gpio_GetConfigInt(). Since calling this function from two different projects at different times for the same gpio number return different irqnumber.

	// Get Irq number from GPIO number
    if( !Imx7Gpio_GetConfigInt( hGpio, ioPrintingTestDataRequest, L"irq", &irqNum) ){
        LOG_ERROR("\nERROR, [%s, %d], Failed to initialize the print test interrupt gpio", __FUNCTION__, __LINE__ );
        return ERROR_FPGA_INIT_FPGA_INTERRUPT;
    }

Hello any update for me regarding my issue?

With regards to my issue, how does the system behave in case of lets say GPIO4 ports’, one of the pins is being used as an external interrupt for the M4 core and another GPIO4 pin is being used to catch external interrupts on A7 core running WINCE?

Since the IMX7 reference manual assigns IRQs to GPIO ports and not individual port pins, is it possible that the code runnng on M4 is clearing the GPIO4 IRQ before the WINCE ISR runs.

Even though on the M4 side we are just clearing the interrupt status flag just for the port pin that is being used on M4. And the RDC for the exact port pin is set for assigning it to M4 core.

Dear @kapeed

The peripheral electronics is only implemented once, only the interrupt controller itself is duplicated for A7 and M4.

In your case this means:
The GPIO4 bank features one global interrupt status register. As long as the GPIO4-inerrupt-status bit is one, the physical IRQ signal going to the M4 and A7 cores is active.
If one of the cores clears the interrupt status bit, the IRQ signal to both cores goes inactive.

The WinCe code cannot properly handle the situation that an interrupt is cleared by another party than the A7 core itself - this will lead to unpredictable results. You need to make sure that the interrupt registers are modified by only one core.

Assigning the port pin to the M4 is not relevant for this aspect: It only affects the pin parameters (direction, pull-up configuration etc), but does not change the assignment of the internal interrupt logic.

Regards, Andy

Hi Andy,

The root cause of the issue was found to be a bug in the FreeRTOS library function GPIO_Init(). The GPIO_Init() clears the complete Interrupt mask register for the complete GPIO even when from code we are using it to configure a specific GPIO pin. We also found that a similar issue but with iMX6 is posted in NXP forum too.

https://community.nxp.com/thread/478543

So basically what was happening was, if we are using GPIO4_IO01 in A7 and GPIO4_IO4 in M4. When the code for initializing GPIO4_IO04 runs on M4 it masks the GPIO4_IO01 interrupt which was enabled from the A7 code previously.

Dear @kapeed

Thank you for pointing this out. I adjusted the source code in the public repository accordingly:

Regards, Andy