OpenWrt Forum Archive

Topic: gpio interrupts

The content of this topic has been archived on 3 May 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

Does anyone know anything about gpio bits as interrupt sources?  I'm trying to get lirc up with both transmit and receive.  I've gotten transmit to work using esteevens code(THANKS).  Via a small hack to the lirc driver I've determined that gpio bits can be interrupt sources.  Receive really needs interrupts.  What I need to know is which irq do the gpio bits interrupt through and then(assuming that they share the interrupt) how do I know which bit did it?  Also can someone point me at a bcm4712 programmers reference?  I can't find one anywhere on the web including the broadcom site and this forum!
Thanks!

Hi!

Does anyone know anything about gpio bits as interrupt sources?

I made some basic experiments with that and it seemed to work.

*** WARNING: Use the following at your own risk. There's no guarantee that it works, and it might damage your hardware. ***

You need to fiddle with some hardware registers. Some are accessible through the the sbh structure using sb_xxx() functions: sb_gpiointmask() and  sb_gpiointpolarity(). However, setting the bit in gpiointmask is not enough to trigger interrupts. You also have to enable GPIO interrupts in general. For that you have to get hold of the so called chip common configuration registers.
Here's a code snippet I hacked into drivers/net/diag/diag_led.c (I used the reset button as interrupt trigger):

if (!(pdev = pci_find_device(VENDOR_BROADCOM, SB_CC, NULL))) {
        printk(KERN_ERR "bcm4710_pcmcia: cc not found\n");
        return -ENODEV;
}
ccregs = (chipcregs_t *) ioremap_nocache(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
intstatus = readl(&ccregs->intstatus);
intmask = readl(&ccregs->intmask);
writel(intmask | CI_GPIO, &ccregs->intmask);
request_irq(3, diag_interrupt, SA_SHIRQ, "diag", &dev);

sb_gpiocontrol(sbh,reset_gpio,0);
sb_gpioouten(sbh,reset_gpio,0);

sb_gpiointmask(sbh,reset_gpio,reset_gpio);
sb_gpiointpolarity(sbh,reset_gpio,0);

My interrupt handler:

static chipcregs_t *ccregs;
static  void    diag_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
    int intstatus;

    printk("diag_interrupt: %d\n", irq);
    intstatus = readl(&ccregs->intstatus);
    printk("intstatus: 0x%08x\n", intstatus);
    
}

Interesting.

It looks as if we can replace the /proc/sys method and switch to an event bus, sending out notifications via hotplug.

To give you a better idea what I mean:

for script in /etc/hotplug.d/button/*; do {
    $script BUTTON=reset ACTION=released
}; done

It looks as if we can replace the /proc/sys method and switch to an event bus, sending out notifications via hotplug.

The reset button cannot really be used in this way. An interrupt line should never be hold as long as an external button without further logic can be pressed. You will stop the whole machine as long as you hold the button pushed. I ran into that problem when hadn't got the interrupt polarity right.

Well, the choices are somewhat limited; either continuous polling or interrupt driven -- which would have to include masking so you didn't get stuck in the interrupt handler.

xj600: Got it working - just flip the polarity every time an interrupt occurs.

You'll then get an interrupt on both the rising and falling edge, and you can use the polarity to figure out which occured.

int polarity;
...
polarity ^= reset_gpio;
sb_gpiointpolarity(sbh,reset_gpio,polarity);

mbm: Great! That's really clever.

Thanks much.  Will post when I have lirc receive working!

If you get a GPIO driver working for lirc, please let me know!  That's been on my list for about a year, but I never get the time to work on it.  And the LIRC source is so obtuse for me, just figuring out the irq handling would probably take me about two years....

--Yan

I cleaned up my experiments and commented the code, creating a quick tutorial:

http://downloads.openwrt.org/people/mbm/gpio_test.c

(many thanks to xj600 for getting me started)

EDIT: updated the sources to fix some initialization problems

Quote from gpio_test.c:

/* mark the gpio as an uncontrolled input
*/
sb_gpioouten(sbh,reset_gpio,0);
sb_gpiocontrol(sbh,reset_gpio,0);

mbm: So, you know what gpiocontrol is for? I ask because I didn't manage to create a bit-banging i2c interface using two gpios only. I had to use three instead, one for SCL (out) and two plus some o.c. buffers for SDA (bidirectional). Unfortunately, I haven't had the opportunity to use an oscilloscope to see what's really going on on the wires.

I take it to mean an undriven mode, but I don't have any Broadcom documentation so that's just a guess.

This also works on devices which use the External Interface Core for gpio-control (no big surprise as external UART interrupts are submitted in this way), in this case interrupt 2 is the right one.
By the way: The sample code doesn't reset gpiointmask, so don't leave the button pressed after unloading the module. smile

wtzm wrote:

This also works on devices which use the External Interface Core for gpio-control (no big surprise as external UART interrupts are submitted in this way), in this case interrupt 2 is the right one.
By the way: The sample code doesn't reset gpiointmask, so don't leave the button pressed after unloading the module. smile

Ack! you're right, I used the wrong command -- the gpiointmask should be fixed now.

I'll have to think about the irq 2/3 issue.

Many thanks to mvm and xj600!!!!! I've gotten an lirc driver to both transmit and receive using a wrtsl54gs.  See my post on the  "(Ab)using WRT54G as remote control" thread.
Thanks again for your help!

The discussion might have continued from here.