cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
dpekin
Explorer
Explorer
8,529 Views
Registered: ‎07-09-2012

ZC702 AXI interrupt handling

Hello,

 

I have successfully implemented a kernel driver interrupt handler for the FIFO AXI_MM_2_S IP Core.  The ISR is simple, it disables the FIFO interrupts, clears the interrupt flag, handles the data, enables the FIFO interrupts and returns with the IRQ_HANDLED return value.  That works fine.

 

Now we are trying to implement another PL interrupt.  This interrupt is supposed to occur once a second.   I am able to attach to the interrupt using the request_irq(..).  The problem is that the ISR get's called continuously,   It is as if the interrupt flag never gets cleared.  Since the PL is generating the interrupt, there is no interrupt register that I know of to clear.   Am I missing something?  Do I need to do something with the PS interrupt controller in the ISR or should I just be able to return IRQ_HANDLED? 

 

At the present time, I don't know if the PL is bad and actually generating millions of interrupts per second or if I'm not handling the interrupt correctly in the PS causing it to be reinterrupted.  

 

Any suggestions welcomed.  Also, does anyone know of any examples of AXI interrupt handling?

 

Thanks,

 

Dave

0 Kudos
9 Replies
anandvlps
Visitor
Visitor
8,309 Views
Registered: ‎08-01-2013

Hi buddy,

 

we are trying to generate interrupt from PL (AXI GPIO) and want to catch it on linux system. Can you guide us on how to achieve this as you said you have already achieved this. we will be very much thankful to you.

 

thank you

 

0 Kudos
dpekin
Explorer
Explorer
8,303 Views
Registered: ‎07-09-2012

I don't know about "guide" you however here are the things you need to do:

 

0. Create the interrupt in the PL.

1. The interrupt needs to be exposed from the PL to the PS in the .DTS/DTB file.  Interrupt number =N

2. In your kernel driver you need to attach your ISR to the interrupt by request_irq(N+32).  I don't know why you need to 32 offset in the interrupt number.  There are other posts about that.

3. In your ISR disable  your interrupt, do whatever you do there, clear your interrupt flag, enable the interrupt, and return.

 

That's the 10,000 foot overview.

 

Good luck.

 

 

0 Kudos
milosoftware
Scholar
Scholar
8,269 Views
Registered: ‎10-26-2012

Maybe you are usign a "level" interrupt while you intended an "edge" type?It's the third parameter in the IRQ item in the devicetree (either 4 or 0 I think). If the signal is periodic, you only want to be triggered by the rising edge.

 

For as far as I know, there's no difference in AXI interrupt handling versus any other interrupt.

0 Kudos
anandvlps
Visitor
Visitor
8,262 Views
Registered: ‎08-01-2013

Hi dear,

 

I followed the following steps:

 

1. connected axi_gpio to GIC (line 91 is connected to GIC)

2. continuously generating interrupt in FPGA (i.e., on axi_gpio generating 1 second clock )

3. generated the dts file (in dts file interrupt number is shown as 59) and bin file

4. booted in the linux environment (with kernel 3.6.0)

5. compiled a module (see the module code below) and inserted the module.

6. after loading it is showing the following messages

 

[ 147.170000] Init gpio module. 
[ 147.170000] Registered IRQ. 
[ 147.180000] Type: mknod /dev/gpio c 36 0
[ 147.180000] And remove it after unloading the module.

 

7. it is not showing any irq handler messages.

8. am i missing anything. do i have to do some kind initializations to notify the kernel about interrupts?

 

code:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <linux/fs.h>		//required for fops
#include <linux/uaccess.h>	//required for 'cpoy_from_user' and 'copy_to_user'
#include <linux/signal.h>	//required for kernel-to-userspace signals

MODULE_LICENSE("GPL");

#define IRQ_NUM		91		// interrupt line
#define TIMER_BASE	0x81200000

#define TIMER_SIZE	0x0000FFFF
#define TCSR0		0x00000000
#define TLR0		0x00000004
#define TCR0		0x00000008
#define T0INT		0x00000100

#define TIMER_TCSR0	0x81200000	// address of timer 0's control and status register
#define TIMER_TLR0	0x81200004	// address of timer 0's load register
#define TIMER_TCR0	0x81200008	// address of timer 0's counter register
#define LOAD_VAL	0xE2329AFF	// address load value (500000000 cycles) ~5 sec
unsigned long *pTIMER_TCSR0;		// pointer to timer 0 control and status register
unsigned long *pTIMER_TLR0;		// pointer to timer 0 load register
unsigned long *pTIMER_TCR0;		// pointer to timer 0 counter register

#define DEVICE_NAME "gpio"		// device name
#define SYSCALL_MAJOR 36		// device major number
#define BUF_LEN 80			// max buffer length
#define SUCCESS 0			// success return value

//unsigned long *pSYSCALL_Virtual;	// base address
static int Device_Open = 0;		// device status

static char msg[BUF_LEN];		// the msg the device will give when asked
static char *msg_Ptr;
static int intcount;

// write routine (called when write() is used in user-space)
ssize_t syscall_write(struct file *flip, const char *buf, size_t length, loff_t *offset)
{
	printk("syscall_write.\n");				// debug: procedure call message
	if (copy_from_user(msg, buf, length) != 0)		// read buffer from user space
		return -EFAULT;					// return error if it failed
	printk("Received: %s \n",msg);				// debug: what string is received

	if (strcmp(msg,"1") == 0)				// enable timer command
	{
		printk("Driver enables timer.\n");		// print timer status message
		iowrite32(LOAD_VAL,pTIMER_TLR0);		// prepare timer cycle 
		iowrite32(0x000000B0,pTIMER_TCSR0);		// load the load-register
		iowrite32(0x000000D0,pTIMER_TCSR0);		// Generate mode,upcounter, reload generate value
								// no load, enable IRQ, enable Timer
		return SUCCESS;					// return 0 to application
	} 
	else if (strcmp(msg,"0") == 0)				// disable timer command
	{
		printk("Driver disables timer.\n");		// print timer status message
		iowrite32(0x00000050,pTIMER_TCSR0);		// Generate mode, upcounter, reload generate value
								// no load, enable IRQ, disable Timer
		return SUCCESS;					// return 0 to application
	} 
	else 
	{
		printk("Driver received wrong value.\n");	// Print error message
		return -EFAULT;					// unknown value received
	}
}

// read routine (called when read() is used in user-space)
ssize_t syscall_read(struct file *flip, char *buf, size_t length, loff_t *offset)
{
	unsigned long timerval;
	// read timer value

	printk("syscall_read.\n");				// debug: procedure call message
	timerval = ioread32(pTIMER_TCR0);
	printk("Read() call value : %lu Cycles\n",timerval);	// display timer value
	
	if (copy_to_user(buf, &msg, length) != 0)		// send counter value
		return -EFAULT;
	else 
		return 0;
}

// open routine (called when a device opens /dev/syscall)
static int syscall_open(struct inode *inode, struct file *file)
{
	if (Device_Open)					// if the device is allready open,
		return -EBUSY;					// return with an error

	Device_Open++;						// 'open' device
	sprintf(msg,"You tried to open the syscall module.\n");	// print open message
	msg_Ptr = msg;						
	try_module_get(THIS_MODULE);
	return 0;
}

// close routine (called whne a device closes /dev/syscall)
static int syscall_close(struct inode *inode, struct file *file)
{
	Device_Open--;						// 'close' device

	module_put(THIS_MODULE);
	return 0;
}

// device init and file operations
struct file_operations syscall_fops = {
	.read = syscall_read,		// read()
	.write = syscall_write,		// write()
	.open = syscall_open,		// open()
	.release = syscall_close,	// close()
};

// timer interrupt handler
static irqreturn_t irq_handler(int irq,void*dev_id)		
{      
	unsigned long temp;
	unsigned long timervalue;
      
	timervalue = ioread32(pTIMER_TCR0);			// Read Timer/Counter Register
	printk("Interrupt! Timer counter value : %lu Cycles\n",(timervalue-LOAD_VAL));	// Display timer value

	temp = ioread32(pTIMER_TCSR0);				// clear timer IRQ
	temp |= T0INT;
	iowrite32(temp, pTIMER_TCSR0);

	intcount++;
	if (intcount>=100)					// after 100 interrupts
	{
		printk("100 interrupts have been registered.\nDisabling timer");// print timer status message
		iowrite32(0x00000050,pTIMER_TCSR0);		//disable timer;
	}

	return IRQ_HANDLED;
}

// init module      
static int __init mod_init(void)  
{
	unsigned long temp;
	printk(KERN_ERR "Init gpio module. \n");

	if (request_irq(IRQ_NUM,irq_handler,IRQF_DISABLED, DEVICE_NAME, NULL))  //request timer interrupt
	{
		printk(KERN_ERR "Not Registered IRQ. \n");
		return -EBUSY;
	}
	printk(KERN_ERR "Registered IRQ. \n");

	pTIMER_TCSR0 = ioremap_nocache(TIMER_TCSR0,0x4);	// map timer 0 control and status register
	pTIMER_TLR0 = ioremap_nocache(TIMER_TLR0,0x4);		// map timer 0 load register
	pTIMER_TCR0 = ioremap_nocache(TIMER_TCR0,0x4);		// map timer 0 count register
	//iowrite32(LOAD_VAL, pTIMER_TLR0);			// place load value in load register
	//temp = ioread32(pTIMER_TLR0);				// debug: read load value to check
	//printk("Load value: %lu.\n",temp);			// debug: print the read load value
	//iowrite32(0x000000B0, pTIMER_TCSR0);			// load TLR
	//iowrite32(0x000000D0, pTIMER_TCSR0);			// Generate mode,downcounter,reload generate
								// value,no load,enable IRQ,enable Timer
	intcount = 0;						// set interrupt count to 0, driver will unload on 100 interrupts
	// manual node creation
	if (register_chrdev(SYSCALL_MAJOR, DEVICE_NAME, &syscall_fops))
		printk("Error: cannot register to major device 22.\n");
	
	printk("Type: mknod /dev/%s c %d 0\n",DEVICE_NAME, SYSCALL_MAJOR);
	printk("And remove it after unloading the module.\n");

	return SUCCESS;
} 

// exit module
static void __exit mod_exit(void)  		
{
	iounmap(pTIMER_TCSR0);				// unregister timer hardware
	iounmap(pTIMER_TLR0);
	iounmap(pTIMER_TCR0);
	free_irq(IRQ_NUM, NULL);			// unregister timer interrupt
	unregister_chrdev(SYSCALL_MAJOR, "gpio");	// unregister device
	printk(KERN_ERR "Exit gpio Module. \n");	// print unload message
}

module_init(mod_init);
module_exit(mod_exit);

MODULE_AUTHOR ("Anand");
MODULE_DESCRIPTION("Testdirver for the Xilinx AXI Timer.");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("custom:gpio");

 

 

could you please help me out in this

 

thanks 

0 Kudos
milosoftware
Scholar
Scholar
8,257 Views
Registered: ‎10-26-2012

Try adding one of the "EDGE" or "LEVEL" flags to the request_irq parameters, instead of IRQF_DISABLED (which I think is obsolete).

 

Zynq only supports "rising edge" and "high level" types.

0 Kudos
anandvlps
Visitor
Visitor
8,217 Views
Registered: ‎08-01-2013

Hi dear,

 

Once again i am requesting for your help.

 

Here is what i have done.

 

1. Connected axi gpio to GIC

2. generating interrupt in PL

3. have written small kernel module to handle the interrupt.

4. compiled the module and inserted the module

5. it is saying interrupt is registered but in interrupt handler i am not getting any messages.

 

Any kind of initialization is required for "axi gpio" because in bare metal there are initializations.

 

Here is my module:

#include <linux/module.h>   // Needed by all modules
#include <linux/kernel.h>   // Needed for KERN_ALERT
#include <linux/init.h>     // Needed for the macros
#include <linux/interrupt.h> //Needed for the interrupt
 
#define DEVICE_NAME "IRQ_module" /* Dev name as it appears in /proc/devices   */
 
MODULE_LICENSE("GPL");
 
 
irqreturn_t IRQ(int irq, void *dev_id, struct pt_regs *regs)
{
	printk (KERN_INFO "Got a interrupt!!!");
	return IRQ_HANDLED;
}
 
 
int init_module(void)
{
 
   int result = request_irq(91, IRQ, 0, DEVICE_NAME,NULL);
   if (result) {
	printk(KERN_INFO "IRQ_module: can't get assigned irq %i\n",
	83);
   }
 
   printk(KERN_INFO "IRQ_module initialized");
 
   return 0;
}
 
 
void cleanup_module()
{
   /* free the IRQ */
	free_irq(91,DEVICE_NAME);
}

Here is output:

 

[  139.100000] IRQ_module initialized.

 

 

And if i export this interrupt in /sys/class i can see the value is changing. but i am not getting the interrupt.

 

Please help me

0 Kudos
anandvlps
Visitor
Visitor
8,201 Views
Registered: ‎08-01-2013

I figured it out. 

I missed some initializations. I got it now.

 

thank you,

 

Anand

0 Kudos
8,049 Views
Registered: ‎10-23-2013

Hi

       We created an AXI peripheral( PL Logic ) and  has assigned 91 as IRQ id .

       We have downloaded driver for this AXI peripheral ,but our IRQ handler is not getting called in driver .

       We checked for  0xF8F01D08(spi_status)  register value which get updated whenever an interrupt comes

       also it shows  proper value  after reading it from driver.

       which means interrupt is received by processor,but still ISR not getting called.

      Can any tell is there any other modification need to be done on driver side ,so that ISR for that AXI peripheral

       ( PL logic) will get called.

 

      Devicetree we are using:

      Interrupt is level sensitive ,rising high

    

      reg = < 0x78800000 0xffff >
      interrupts = < 0x0  0x3B  0x4 >
      interrupt-parent = <0x1>

 

regards

himanshu

0 Kudos
shinya
Observer
Observer
2,706 Views
Registered: ‎08-31-2017

Could you tell me what kind of initializations?

0 Kudos