• 0
sg4036

UIO Interrupts on Zynq

Question

I've been investigating the different options for interacting with the PL from the PS running Linux and have been having some issues with interrupts using userspace I/O (uio). I'm wondering if someone can help. The board is a Zedboard, and I am using the Xilinx Linux kernel version 4.6.

The fabric design is quite simple, as you can see in the block diagram*, with an interrupt from the gpio block connected to the Zedboard buttons. This works when running a bare machine application (the interrupt fires). It also works when I specify the device as a GPIO device in the device-tree:

--snip--
axi_gpio_0: gpio@41200000 {
#gpio-cells = <2>;
#interrupt-cells = <2>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller ;
interrupt-controller ;
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x1>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x5>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
--snip--

I can enable the interrupt by setting the 'edge' value in /sys/class/gpio/ and can see the interrupts counted in /proc/interrupts, corresponding to button presses:

root@target:/sys/class/gpio# echo 901 > export 
root@target:/sys/class/gpio# cd gpio901
root@target:/sys/class/gpio/gpio901# echo rising > edge
[ 304.116780] xgpio_irq_unmask: Enable 0 irq, irq_enable_mask 0x0

<<press button a few times>>

root@target:/sys/class/gpio/gpio901# cat /proc/interrupts 
CPU0 CPU1 
...
165: 7 0 xgpio 0 Edge gpiolib
...

To use uio, I change the compatible property in the device tree node to: compatible = "generic-uio"; . I then (having rebooted) load the uio and uio_pdrv_genirq.ko modules (the latter with the parameter of_id="generic-uio"). The device /dev/uio0 is created as expected and I can interact with the fabric via an mmap'ed memory pointer in a user space program - I can see the value change depending on which of the five buttons is pressed. So far so good!

I can see that the interrupt is created and that the GIC knows about it, via some debugging in the GIC code and also it is listed in /proc/interrupts:

root@target:~# cat /proc/interrupts 
...
164: 0 0 GIC-0 61 Level gpio
...

But the interrupt never seems to be fire, or at least is never acknowledged. The count in /proc/interrupts never increments and if put some debug statements into the uio_read() function in the driver it shows that the read never picks up an interrupt. My user space test code is very simple (edited highlights, error checking etc. removed):

...
int fd;
ssize_t value = 0;
int count = 1;

fd = open("/dev/uio0", O_RDWR);

while(1) {
   value = write(fd, &count, sizeof(count));
   value = read(fd, &count, sizeof(count));

   if (value == sizeof(value)) {
      printf("interrupt!\n");
   }
}
...

The read blocks as expected but never unblocks when the buttons are pressed (and the interrupt supposedly fires).

So my questions are... does anything different have to be done at the Fabric design level (i.e. in Vivado) for an PL-PS interrupt to work with UIO? Is there anything I may have missed on the Linux side?

Any suggestions gratefully received.

* design from: https://embeddedcentric.com/interrupts/

design.JPG

Share this post


Link to post
Share on other sites

13 answers to this question

Recommended Posts

  • 1

Hi @sg4036,

First of all the module for axi_gpio from xilinx contains bugs. So use the generic-uio module to manage interrupt is a good choice.

I think you forgot to mmap the UIO device, then configure it as a GPIO as interrupt.

So first of all you can mmap the UIO with :

    gpio_size = get_memory_size("/sys/class/uio/uio0/maps/map0/size");
    /* mmap the UIO devices */
    ptr_axi_gpio = mmap(NULL, gpio_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_int, 0);

with function get_memory_size =

unsigned int get_memory_size(char *sysfs_path_file)
{ 
	FILE *size_fp; 
	unsigned int size; 
	// open the file that describes the memory range size that is based on the 
	// reg property of the node in the device tree 
	size_fp = fopen(sysfs_path_file, "r"); 
	if (size_fp == NULL) { 
		printf("unable to open the uio size file\n"); 
		exit(-1); 
	} 
	// get the size which is an ASCII string such as 0xXXXXXXXX and then be stop 
	// using the file 
	fscanf(size_fp, "0x%08X", &size); 
	fclose(size_fp); 
	return size; 
} 

Then you have to configure your GPIO :

	// channel1 = input 
	reg_write(ptr_axi_gpio, GPIO_TRI_OFFSET, 0xffffffff); 
	// Global interrupt enable, Bit 31 = 1 
	reg_write(ptr_axi_gpio, GPIO_GLOBAL_IRQ, 0x80000000); 
	// Channel 1 Interrupt enable 
	reg_write(ptr_axi_gpio, GPIO_IRQ_CONTROL, 1); 
	// enable the interrupt controller thru the UIO subsystem 
	write(fd_int, (void *)&reenable, sizeof(int)); 

with function reg_write :

void reg_write(void *reg_base, unsigned long offset, unsigned long value) 
{ 
	*((volatile unsigned long *)(reg_base + offset)) = value; 
} 

And to finish you can wait for interrupt like that for example :

uint8_t wait_for_interrupt(int fd_int, void *gpio_ptr) 
{ 
	static unsigned int count = 0, bntd_flag = 0, bntu_flag = 0; 
	int pending = 0; 
	int reenable = 1; 
	unsigned int reg; 
	unsigned int value; 
	// block (timeout for poll) on the file waiting for an interrupt 
	struct pollfd fds = {
        .fd = fd_int,
        .events = POLLIN,
    };

	int ret = poll(&fds, 1, 100);
	printf("ret is : %d\n", ret);
	if (ret >= 1) {
		read(fd_int, (void *)&reenable, sizeof(int));   // &reenable -> &pending
		// channel 1 reading 
		value = reg_read(gpio_ptr, GPIO_DATA_OFFSET); 
		/*if ((value & 0x00000001) != 0) { 
			do something
		} 
		*/
		count++; 
		usleep(50000); // anti rebond 
		if(count == 10) 
			flag_end = 1; 
		// the interrupt occurred for the 1st GPIO channel so clear it 
		reg = reg_read(gpio_ptr, GPIO_IRQ_STATUS); 
		if (reg != 0) 
			reg_write(gpio_ptr, GPIO_IRQ_STATUS, 1);  
		// re-enable the interrupt in the interrupt controller thru the 
		// the UIO subsystem now that it's been handled 
		write(fd_int, (void *)&reenable, sizeof(int));
	} 
	return ret;
} 

with function read_reg :

unsigned long reg_read(void *reg_base, unsigned long offset) 
{ 
	return *((volatile unsigned long *)(reg_base + offset)); 
} 

Hope it will help you !

Best regards,

Yohan

Share this post


Link to post
Share on other sites
  • 0

Hi Yohan

Thanks for the reply, I am just coming back to this after a few days on other work. I have a question: Where are the symbols defined that you use to configure the GPIO: GPIO_TRI_OFFSET, GPIO_GLOBAL_IRQ, GPIO_IRQ_CONTROL?

Thanks!

Share this post


Link to post
Share on other sites
  • 0

Hi sg4036,

I'm sorry, I forgot to give you the define :/

#define GPIO_DATA_OFFSET 0x00 
#define GPIO_TRI_OFFSET 0x04 
#define GPIO_DATA2_OFFSET 0x08 
#define GPIO_TRI2_OFFSET 0x0C 
#define GPIO_GLOBAL_IRQ 0x11C 
#define GPIO_IRQ_CONTROL 0x128 
#define GPIO_IRQ_STATUS 0x120 

 

Maybe you won't need all the define but you'll have.

Let me know if it works !

Yohan

Share this post


Link to post
Share on other sites
  • 0

 

AK72,

Did you get your answer on this? I can probably help there if it's still needed.

Meanwhile, to the others on this thread, I apologize for piling on an aging forum thread, but I too have had issues with interrupts and using the UIO driver. I have been using the UIO driver to provide various interrupts (from the PL of the Zynq) to the PS. I have a handful of threads that are invoked from my application. For whatever reason, I slip/miss interrupts on occasion (several in a row for that matter). I lack the in-depth understanding of the linux kernel and the UIO driver. Both would probably make troubleshooting a little easier. Still, I blame either the UIO driver or how I am using the driver to poll for interrupts. Is anyone else using multiple interrupts with the UIO driver? Is anyone else having problems missing interrupts? If anyone reading this can help, let me know. I would be more than happy to provide additional information about what I am doing and the problem(s) I am experiencing.

Share this post


Link to post
Share on other sites
  • 0

@BNice

i am still facing issue with it.

my .config file:

CONFIG_UIO=y

CONFIG_UIO_PDRV_GENERIC=y

CONFIG_UIO_XILINX_APM=y

 

my device tree is

gpio_if_axi_gpio: gpio@81240000 {
#gpio-cells = <2>;
#interrupt-cells = <2>;
compatible = "generic-uio";
status = "okay";
gpio-controller ;
interrupt-controller ;
interrupt-parent = <&intc>;
interrupts = <0 52 4>;
reg = <0x81240000 0x10000>;
xlnx,all-inputs = <0x1>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x1>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
 
bt i am not able to see /dev/uio and /sys/class/uio/uio0
 
 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now