Post Go back to editing

GPIO interrupt

Hello!

I have a basic question about the GPIO interface. I'm using a ADRV9361 SOM with the ADRV1CRR breakout board and I'd like to use one of the DIP switches (SW0) in my C application. The gpio file in /sys/kernel/debug already lists SW0 at gpio 968.

root@analog:/sys/kernel/debug# cat gpio
gpiochip0: GPIOs 906-1023, parent: platform/e000a000.gpio, zynq_gpio:
gpio-913 (                    |ulpi resetb         ) out lo
gpio-915 (                    |usb-ulpe-gpio-gate  ) out hi
gpio-960 (                    |Left                ) in  lo IRQ
gpio-961 (                    |Right               ) in  lo IRQ
gpio-962 (                    |Up                  ) in  lo IRQ
gpio-963 (                    |Down                ) in  lo IRQ
gpio-964 (                    |?                   ) out lo
gpio-965 (                    |?                   ) out lo
gpio-966 (                    |?                   ) out lo
gpio-967 (                    |?                   ) out lo
gpio-968 (                    |SW0                 ) in  hi IRQ
gpio-969 (                    |SW1                 ) in  hi IRQ
gpio-970 (                    |SW2                 ) in  hi IRQ
gpio-971 (                    |SW3                 ) in  hi IRQ
gpio-1005 (                    |sync                ) out lo
gpio-1006 (                    |reset               ) out hi
gpio-1011 (                    |ad9361-refclk-gpio-g) out hi

My question is how I can implement an interrupt routine in my application that is called if the state of the switch is changed. Please excuse my lack of GPIO knowlegde. I appreciate any hint.

Regards

  • Hi,

    The buttons are exposed as a input device. See below for a simple example that is handling button events:

     
    #include <linux/input.h>

    #include <sys/types.h>
    #include <dirent.h>                                                                                                                    
    #include <string.h>                                                                                                                    
    #include <fcntl.h>
    #include <unistd.h>
    #include <stdio.h>                                                                                                                     
    #include <stdlib.h>

    int main(int argc, char *argv[])
    {
      int fd, rd;
      struct input_event ev;                                                                                                               
      int version;                                                                                                                         
      unsigned short id[4];
      char name[256] = "Unknown";                                                                                                          
      const char *filename = "/dev/input/event0";

      if ((fd = open(filename, O_RDONLY)) < 0) {
        perror("evtest");                                                                                                                  
        exit(1);                                                                                                                           
      }

      if (ioctl(fd, EVIOCGVERSION, &version)) {                                                                                            
        perror("evtest: can't get version");                                                                                               
        exit(1);
      }                                                                                                                                    
     
      printf("Input driver version is %d.%d.%d\n",                                                                                         
             version >> 16, (version >> 8) & 0xff, version & 0xff);

      ioctl(fd, EVIOCGID, id);
      printf("Input device ID: bus 0x%x vendor 0x%x product 0x%x version 0x%x\n",
             id[ID_BUS], id[ID_VENDOR], id[ID_PRODUCT], id[ID_VERSION]);                                                                   

      ioctl(fd, EVIOCGNAME(sizeof(name)), name);
      printf("Input device name: \"%s\"\n", name);
                                                                                                                                           
    printf("Testing ... (interrupt to exit)\n");

      while (1) {
        rd = read(fd, &ev, sizeof(ev));
                                                                                                                                           
        if (rd < (int) sizeof(ev)) {
          printf("yyy\n");
          perror("\nevtest: error reading");                                                                                               
          exit (1);
        }                                                                                                                                  

        if (ev.type == EV_SW)
          printf("Switch %d toggled to %d\n", ev.code, ev.value);                                                                          
      }                                                                                                                                    
    }
  • Thank you for your reply. Unfortunately the result is not what I expected. When I run the program above and toggle the DIP switches nothing happens most of the time. Infrequently messages "Switch X toggled to Y" pop up for SW1 and SW2. SW0 and SW3 remain silent.

    Do you have any idea what is happening and how I can solve it?

    Monitoring /proc/interrupts I observed that the interrupt counter is increased for every switch at almost every 1 -> 0 transition. It's increased very infrequently at a 0 -> 1 transition (and only for SW1 and SW2), though.

  • The issue was that the irq_flags are not properly initialized.

    Fixed with this revert commit:  

    https://github.com/analogdevicesinc/linux/commit/01a36958ab139da133f25ae3d0ea2992844af503

    -Michael

  • Yes - you need to rebuild your kernel.

    Please follow the instructions here: Building the 2015_R2 release Linux kernel and devicetrees from source

    However please use 2016_R2 instead of 2015_R2 !

    -Michael

  • Thanks for fixing the issue. Do I need to re-compile the entire kernel or is there a way to include only this fix into my system?

    I'd be happy if you could provide some instructions.

    Thank you and best regards.

  • It is possible to compile the kernel on the target. However it will take much longer.

    You must use the zynq_xcomm_adv7511_defconfig.

    -Michael

  • Thank you for your help! 

    I have two more questions:

    Is it neccessary to recompile the kernel on a development host + creating a new image SD card, or can I somehow recompile and update the kernel on my running system (ADRV9361 + BOB) directly?

    In step "Configure the kernel"  make zynq_xcomm_adv7511_defconfig is executed. May I use this command as is or do I need to specify a different config file? I found a file named xilinx_zynq_defconfig in linux/arch/arm/configs/ - is this the right one?

  • Hi Michael,

    I've compiled the kernel. The DIP switches behave as expected now. Perfect!

    Thank you very much for your help.

    Regards