LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 02-20-2018, 01:09 AM   #1
abhinav007
LQ Newbie
 
Registered: Feb 2018
Posts: 10

Rep: Reputation: Disabled
GPIO UIO Interrupt error on Zynq 7020


Hi,
I have implemented a simple UIO driver for AXI GPIO to be run on Zynq platform. A high-level sensitive interrupt is triggered for the processor in event of incoming data on the peripheral. I made the necessary changes in the device-tree to request the respective IRQ. In the userspace program, below is the code snippet which handles interrupt:

int pending=0;
int reenable = 1;

read(fd, (void *)&pending, sizeof(int)); //blocked on iterrupt

//interrupt acknowledged....
//reset and clear interrupt status register

write(fd, (void *)&reenable, sizeof(int)); //enable interrupt



The above code runs correctly and I can verify the interrupt by checking the /proc/interrupts.
However, this program runs only single time. The next time it runs, it keeps on pending on interrupt for infinite time, unless the system is rebooted. Not able to understand the reason behind this phenomenon.
GPIO receives continuous data from a binary counter built in FPGA fabric, hence it does not need to be controlled by the processor.

Thank you.
Regards,
Abhinav
 
Old 02-20-2018, 08:54 PM   #2
AwesomeMachine
LQ Guru
 
Registered: Jan 2005
Location: USA and Italy
Distribution: Debian testing/sid; OpenSuSE; Fedora; Mint
Posts: 5,513

Rep: Reputation: 1000Reputation: 1000Reputation: 1000Reputation: 1000Reputation: 1000Reputation: 1000Reputation: 1000Reputation: 1000
I can't tell from the snippet exactly what's going on. Could you post some context, and put the code inside tags, so it's more readable.
 
Old 02-21-2018, 12:01 AM   #3
abhinav007
LQ Newbie
 
Registered: Feb 2018
Posts: 10

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by AwesomeMachine View Post
I can't tell from the snippet exactly what's going on. Could you post some context, and put the code inside tags, so it's more readable.
Please find the full code attached below.
Code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

// This program implements a UIO driver in user space using an AXI GPIO in
// the PL. It expects an interrupt to be connected from the PL. The only purpose
// of the program is to simulate an interrupt from a device in the PL and to
// show accessing OCM memory from the UIO driver.
//
// This code assumes the GPIO device in the device tree has it's compatible
// string to be "generic-uio" and "uio_pdrv_genirq.of_id=generic-uio" is used
// on the command line via bootargs when the UIOP driver (uio_pdrv_genirq)
// driver is statically built into the kernel (the default).
//
// OCM is also used with the UIO driver such that it can be read/written from
// user space. It is mapped as device memory by UIO.
//
// The device tree should remove the OCM driver from the kernel such that
// OCM is not used by any kernel functions such as power mgmt for DDR self
// refresh mode. The GPIO node now has 2 memory regions with the 1st being
// the normal registers while the 2nd is the OCM memory.
//
// This was tested on a 702 board using the push buttons to generate an
// interrupt (left or right).

// The following snippet is used for system-top.dts with Petalinux.

// /dts-v1/;
// /include/ "system-conf.dtsi"
// /{
// };
// /{
//     model = "702-axi-gpio-irq";
//     aliases {
//        serial0 = &uart1;
//        ethernet0 = &gem0;
//     };
//     chosen {
//        bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";
//     };
//     memory {
//        device_type = "memory";
//        reg = <0x0 0x40000000>;
//     };
// };
// &ocmc {
//	 compatible = "";
// };
// &axi_gpio_0 {
//	 compatible = "generic-uio";
//	 reg = <0x41200000 0x10000 0xFFFC0000 0x20000>;
// };
//

#define GPIO_MAP_SIZE 		0x10000
#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

inline void gpio_write(void *gpio_base, unsigned int offset, unsigned int value)
{
	*((volatile unsigned *)(gpio_base + offset)) = value;
}

inline unsigned int gpio_read(void *gpio_base, unsigned int offset)
{
	return *((volatile unsigned *)(gpio_base + offset));
}

void wait_for_interrupt(int fd, void *gpio_ptr, unsigned int *ocm_ptr, int ocm_size)
{
	int pending = 0;
	int reenable = 1;
	unsigned int reg;
	int sum = 0, i;

	// block on the file waiting for an interrupt */

	read(fd, (void *)&pending, sizeof(int));

	// the interrupt occurred for the 2nd GPIO channel so clear it

	reg = gpio_read(gpio_ptr, GPIO_IRQ_STATUS);
	if (reg)
		gpio_write(gpio_ptr, GPIO_IRQ_STATUS, 2);

	// read the OCM memory and sum it's contents

	for (i = 0; i < ocm_size / sizeof(int); i++) {
		sum += *(ocm_ptr + i);
	}
	printf("sum = %d\n", sum);

	// re-enable the interrupt in the interrupt controller thru the
	// the UIO subsystem now that it's been handled

	write(fd, (void *)&reenable, sizeof(int));
}

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) {
		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;
}

int main(int argc, char *argv[])
{
	int fd;
	char *uiod = "/dev/uio0";
	void *gpio_ptr;
	int gpio_size;
	unsigned int *ocm_ptr;
	int ocm_size;
	int i;

	printf("GPIO UIO test.\n");

	// open the UIO device file to allow access to the device in user space

	fd = open(uiod, O_RDWR);
	if (fd < 1) {
		printf("Invalid UIO device file:%s.\n", uiod);
		return -1;
	}

	gpio_size = get_memory_size("/sys/class/uio/uio0/maps/map0/size");

	// mmap the GPIO device into user space

	gpio_ptr = mmap(NULL, gpio_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (gpio_ptr == MAP_FAILED) {
		printf("Mmap call failure.\n");
		return -1;
	}

	ocm_size = get_memory_size("/sys/class/uio/uio0/maps/map1/size");

	// mmap the OCM memory into user space

	ocm_ptr = mmap(NULL, ocm_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, getpagesize());
	if (ocm_ptr == MAP_FAILED) {
		printf("Mmap call failure.\n");
		return -1;
	}

	// make the GPIO bits to be outputs to drive the LEDs and the inputs from the switches

	gpio_write(gpio_ptr, GPIO_TRI_OFFSET, 0);
	gpio_write(gpio_ptr, GPIO_TRI2_OFFSET, 0xF);

	// enable the interrupts from the GPIO

	gpio_write(gpio_ptr, GPIO_GLOBAL_IRQ, 0x80000000);
	gpio_write(gpio_ptr, GPIO_IRQ_CONTROL, 2);

	// initialize OCM just to test the summing in the irq

	for (i = 0; i < ocm_size / sizeof(int); i++) {
		*(ocm_ptr + i) = 0;
	}

	// wait for interrupts from the GPIO

	while (1) {
		wait_for_interrupt(fd, gpio_ptr, ocm_ptr, ocm_size);
printf(" Interrupt !! \n");
	}

	// unmap the GPIO device and OCM from user space

	munmap(gpio_ptr, gpio_size);
	munmap(ocm_ptr, ocm_size);
	return 0;
}


This program executes ONLY once when cross-compiled and run on the target platform. It keeps on pending for interrupt infinitely when it is run the second time.

Last edited by abhinav007; 02-21-2018 at 12:04 AM. Reason: Additional info
 
Old 02-22-2018, 09:49 AM   #4
rtmistler
Moderator
 
Registered: Mar 2011
Location: MA, USA
Distribution: MINT Debian, Angstrom, SUSE, Ubuntu, Debian
Posts: 7,230
Blog Entries: 12

Rep: Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699Reputation: 2699
@abhinav007,

Is this a custom hardware platform, or something existing?

I'm trying to determine if your question is better for the Programming forum, or the Embedded and Single Board Computer forum. Possibly Kernel, however it does appear to be a custom driver of some type.
 
1 members found this post helpful.
Old 03-01-2018, 05:11 AM   #5
abhinav007
LQ Newbie
 
Registered: Feb 2018
Posts: 10

Original Poster
Rep: Reputation: Disabled
Solution

Thanks for the support @AwesomeMachine & @rtmistler. I found the solution to this error on platform specific forum. It seems that the interrupt needs to be enabled explicitly in the main as well(before entering the while loop).
This did run the same program a couple of times.

Regards,
Abhinav
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Need help with GPIO module error TeaYoung Linux - Newbie 2 06-04-2013 11:25 PM
GPIO access on a Fujitsu Mini-ITX Industrial Mainboard, PCI Driver for GPIO mechatrix Linux - Embedded & Single-board computer 3 11-20-2011 04:57 PM
Getting UIO Patches zman2245 Linux - Kernel 0 03-20-2009 05:54 PM
Using GPIO (from kernel GPIO support) in MY application DannyGilbert Linux - Kernel 2 03-16-2009 08:52 AM
uio structure/mblk_t sandeepk Linux - Networking 0 01-11-2002 02:50 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 04:49 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration