Unable to use GPIO bank 0 pins on M4 core 0 with Linux running (hardfault on M4 )

We are experiencing a consistent hardfault when trying to access pins in GPIO bank 0 from M4 core 0 with linux booted. The gpio0 device has been deleted from the linux device tree. When we stop the boot process before linux boots, we are able to use LSIO.GPIO0.IO08 to trigger an interrupt and handle it in LSIO_GPIO_INT0_IRQHandler, and then use GPIO_PortGetInterruptFlags to read back the flags and see this which pin caused the interrupt to be triggered.

Once booted into linux, GPIO_PortGetInterruptFlags causes a hard fault (Again, with gpio0 deleted from the device tree – also tested with disabled; same result). Relevant stack on the m4 as far as we can trace it is at the end of this message. Are we missing a step to prevent the linux system from reserving the memory, in addition to removing the node from the device tree?

(gdb) bt
#0 HardFault_Handler () at /home/matt/.platformio/packages/framework-imx8m4-maidbot/framework/devices/MIMX8QM6/gcc/startup_MIMX8QM6_cm4_core0.S:883
#1 
#2 0x1ffe10aa in GPIO_PortGetInterruptFlags (base=0x5d080000) at /home/matt/.platformio/packages/framework-imx8m4-maidbot/framework/devices/MIMX8QM6/drivers/fsl_gpio.h:295
#3 LSIO_GPIO_INT0_IRQHandler () at src/test/gpio_test.c:43
#4 0x1ffe23de in IRQSTEER_CommonIRQHandler (base=0x51070000, intMasterIndex=kIRQSTEER_InterruptMaster2)
at /home/matt/.platformio/packages/framework-imx8m4-maidbot/framework/devices/MIMX8QM6/drivers/fsl_irqsteer.c:156
#5 0x1ffe2424 in IRQSTEER_2_DriverIRQHandler () at /home/matt/.platformio/packages/framework-imx8m4-maidbot/framework/devices/MIMX8QM6/drivers/fsl_irqsteer.c:188
#6 
#7 0x1ffe118e in TSTMR_ReadTimeStamp (base=0x414100f0) at /home/matt/.platformio/packages/framework-imx8m4-maidbot/framework/devices/MIMX8QM6/drivers/fsl_tstmr.h:75
#8 TSTMR_DelayUs (delayInUs=10000, base=0x414100f0) at /home/matt/.platformio/packages/framework-imx8m4-maidbot/framework/devices/MIMX8QM6/drivers/fsl_tstmr.h:70
#9 main () at src/test/gpio_test.c:110
(gdb) up
#1 
(gdb) up
#2 0x1ffe10aa in GPIO_PortGetInterruptFlags (base=0x5d080000) at /home/matt/.platformio/packages/framework-imx8m4-maidbot/framework/devices/MIMX8QM6/drivers/fsl_gpio.h:295
295 return base->ISR;
(gdb) p base
$1 = (GPIO_Type *) 0x5d080000
(gdb) p base->ISR
Cannot access memory at address 0x5d080018

Here’s the relevant test (gpio_test.c).

#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"

#include "pin_mux.h"
#include "clock_config.h"
#include "fsl_lpuart.h"
#include "fsl_irqsteer.h"
#include "fsl_tstmr.h"

#define BUTTON_GPIO_BANK LSIO__GPIO0
#define BUTTON_GPIO_IRQn LSIO_GPIO_INT0_IRQn
#define BUTTON_1_GPIO_PIN 8U
#define BUTTON_2_GPIO_PIN 9U
#define DELAY_MS(ms) (TSTMR_DelayUs(CM4_0__TSTMR, ms*1000))
#define PIN_TO_FLAG(pin) (1 << pin)

//
// get the NVIC IRQn of given IRQSTEER IRQn
//
// (found in examples/boards/mekmimx8qm/demo_apps/power_mode_switch/cm4_core1/lpm.c)
//
#define GET_IRQSTEER_MASTER_IRQn(IRQn) \
    (IRQn_Type)(IRQSTEER_0_IRQn + (IRQn - FSL_FEATURE_IRQSTEER_IRQ_START_INDEX) / 64U)

volatile static bool button_1_pressed = false;
volatile static bool button_2_pressed = false;

/*!
 * @brief Interrupt handler
 */
void LSIO_GPIO_INT0_IRQHandler(void)
{
  uint32_t flags = GPIO_PortGetInterruptFlags(BUTTON_GPIO_BANK);
  if (flags & PIN_TO_FLAG(BUTTON_1_GPIO_PIN))
  {
    button_1_pressed = true;
    GPIO_PortClearInterruptFlags(BUTTON_GPIO_BANK, PIN_TO_FLAG(BUTTON_1_GPIO_PIN));
  }
  if (flags & PIN_TO_FLAG(BUTTON_2_GPIO_PIN))
  {
    button_2_pressed = true;
    GPIO_PortClearInterruptFlags(BUTTON_GPIO_BANK, PIN_TO_FLAG(BUTTON_2_GPIO_PIN));
  }
}

/*!
 * @brief Main function
 */
int main(void)
{
  sc_ipc_t ipc = BOARD_InitRpc();

  BOARD_InitPins(ipc);
  BOARD_BootClockRUN();
  BOARD_InitMemory();
  BOARD_InitDebugConsole();

  if (sc_pm_set_resource_power_mode(ipc, SC_R_GPIO_0, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
  {
    PRINTF("Error: Failed to power on GPIO\r\n");
  }

  if (sc_pm_set_resource_power_mode(ipc, SC_R_IRQSTR_M4_0, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
  {
    PRINTF("Error: Failed to power on IRQSTEER!\r\n");
  }

  IRQSTEER_Init(IRQSTEER);

  gpio_pin_config_t button_config = {kGPIO_DigitalInput, 1, kGPIO_IntRisingEdge};
  GPIO_PinInit(BUTTON_GPIO_BANK, BUTTON_1_GPIO_PIN, &button_config);
  GPIO_PinInit(BUTTON_GPIO_BANK, BUTTON_2_GPIO_PIN, &button_config);

  // clear all flags in this GPIO bank
  GPIO_PortClearInterruptFlags(BUTTON_GPIO_BANK, 0xFFFFFFFF);

  // enable interrupting on both GPIOs
  GPIO_PortEnableInterrupts(BUTTON_GPIO_BANK, PIN_TO_FLAG(BUTTON_1_GPIO_PIN));
  GPIO_PortEnableInterrupts(BUTTON_GPIO_BANK, PIN_TO_FLAG(BUTTON_2_GPIO_PIN));

  // enable the IRQ handler
  IRQSTEER_EnableInterrupt(IRQSTEER, BUTTON_GPIO_IRQn);
  EnableIRQ(GET_IRQSTEER_MASTER_IRQn(BUTTON_GPIO_IRQn));

  // poll button state forever
  while (1)
  {
    if (button_1_pressed)
    {
      PRINTF("button 1 pressed\r\n");
      button_1_pressed = false;
    }

    if (button_2_pressed)
    {
      PRINTF("button 2 pressed\r\n");
      button_2_pressed = false;
    }

    DELAY_MS(10);
  }

  return 0;
}

Greetings @Gwen!

Can you please post your changes to the device tree? It’s a possibility that these pins are initially defined in another device tree file and then might still be used by Linux.

@gustavo.tx thanks for the follow-up; device tree (the “disabled” version) is attached. In the mean time; it’s worth noting that if gpio0 is not explicitly disabled or deleted in the device tree, the code above doesn’t hard fault… but the interrupt also doesn’t get triggered (may be not that surprising if linux is intercepting the interrupt).link text

We’re based on this: http://git.toradex.com/cgit/linux-toradex.git/tree/arch/arm64/boot/dts/freescale/fsl-imx8qm-apalis.dtsi?h=toradex_4.14-2.3.x-imx

@Gwen,

Thanks! I’ll try to reproduce that on my side and get back to you.

Interesting data point is that if we DON’T explicitly disable gpio0 in the device tree, we can use other pins in that bank to control the direction of a motor (GPIO_PinWrite works); but in this state the interrupt doesn’t get triggered (noted above); With gpio0 disabled, GPIO_PinWrite also hardfaults; we’re inferring that linux coming up is preventing the M4 from accessing (reading or writing) registers associated with these pins.

@gustavo.tx we’ve made some additional progress debugging on our end. It appears that linux is still taking control of the power domain for that GPIO bank, and turning it off (after we turn it on for the M4). Any luck reproducing on your end?

@Gwen,

Sorry for the delay on this, I had some trouble with my modules and I’m still trying to reproduce this.

What I think might be the issue is that GPIO assignments are controlled by the SCU on the i.MX8.

You might need to recompile this firmware to correctly set the assignments besides editing the device tree. Here is some topic on that matter on the NXP community: https://community.nxp.com/thread/500937

We also have an article on our developer website on compiling a custom SCFW: https://developer.toradex.com/knowledge-base/build-custom-imx-88x-system-controller-firmware-scfw