BeagleBone Black interrupts through kernel driver

4.9k views Asked by At

I'm trying to work with interruptions but I get the following error, due to ioread32.

As I have seen in the chapter "25.3.3 Interrupt Features" of "AM335x SitaraTM Processors - Technical Reference Manual"

In order to generate an interrupt request to a host processor upon a defined event (level or logic transition) occurring on a GPIO pin, the GPIO configuration registers have to be programmed as follows:

• Interrupts for the GPIO channel must be enabled in the GPIO_IRQSTATUS_SET_0 and/or GPIO_IRQSTATUS_SET_1 registers.

• The expected event(s) on input GPIO to trigger the interrupt request has to be selected in the GPIO_LEVELDETECT0, GPIO_LEVELDETECT1, GPIO_RISINGDETECT, and GPIO_FALLINGDETECT registers.

[ 1737.604270] Loading hello_interrupts module...
[ 1737.604426] HI: Initialized GPIO #36 to IRQ #164
[ 1737.604478] Unhandled fault: external abort on non-linefetch (0x1028) at 0xfa1ac02c
[ 1737.612611] Internal error: : 1028 [#1] SMP THUMB2
[ 1737.617696] Modules linked in: hello_interrupts(O+) g_multi libcomposite omap_rng mt7601Usta(O) [last unloaded: hello_interrupts]
[ 1737.630128] CPU: 0    Tainted: G           O  (3.8.13-bone67 #1)
[ 1737.636513] PC is at hello_interrupts_start+0x8b/0x123 [hello_interrupts]
[ 1737.643717] LR is at _raw_read_unlock+0x7/0x8
[ 1737.648340] pc : [<bf8f508c>]    lr : [<c04cfaf7>]    psr: 80000033
[ 1737.648340] sp : df3fde60  ip : 00000034  fp : c006a001
[ 1737.660481] r10: 00000001  r9 : de594200  r8 : bf8f5001
[ 1737.666011] r7 : 00000000  r6 : bf8f30b8  r5 : 00000000  r4 : fa1ac000
[ 1737.672920] r3 : 48000000  r2 : 00000000  r1 : 481adfff  r0 : fa1ac000
[ 1737.679839] Flags: Nzcv  IRQs on  FIQs on  Mode SVC_32  ISA Thumb  Segment user
[ 1737.687569] Control: 50c5387d  Table: 9f740019  DAC: 00000015
[ 1737.693655] Process insmod (pid: 3040, stack limit = 0xdf3fc240)
[ 1737.700025] Stack: (0xdf3fde60 to 0xdf3fe000)
[ 1737.704659] de60: bf8f30b8 00000000 00400100 df3fc000 c08b5740 c000867f 00000000 de5c3640
[ 1737.713323] de80: 00000000 00000000 00400100 bf8f326c bf8f3260 00000001 bf8f32a8 00000001
[ 1737.721988] dea0: c006a001 c006bd31 bf8f326c 00007fff c0069101 e0dd7000 e0dd6fff bf8f3260
[ 1737.730655] dec0: 00000000 b6f7dd50 df3fc000 bf8f33b4 e0dd6691 c02520d0 6e72656b 00006c65
[ 1737.739320] dee0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1737.747993] df00: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[ 1737.756662] df20: 00000000 00000000 00000000 c0790a68 20000033 000016a8 b6fa2000 b6f7dd50
[ 1737.765331] df40: 00000080 c000c9e4 df3fc000 00000000 00000000 c006c26f e0dd5000 000016a8
[ 1737.774000] df60: e0dd5ae0 e0dd599f e0dd64c8 000003c8 000004c8 00000000 00000000 00000000
[ 1737.782670] df80: 0000001c 0000001d 00000014 00000012 00000010 00000000 00000000 b6fc0088
[ 1737.791339] dfa0: b6fc0d00 c000c841 00000000 b6fc0088 b6fa2000 000016a8 b6f7dd50 00000002
[ 1737.800008] dfc0: 00000000 b6fc0088 b6fc0d00 00000080 00000000 b6f7dd50 000016a8 00000000
[ 1737.808671] dfe0: 00000000 beb7969c b6f77b07 b6f01fd4 80000010 b6fa2000 c0c92420 c0c92440
[ 1737.817378] [<bf8f508c>] (hello_interrupts_start+0x8b/0x123 [hello_interrupts]) from [<c000867f>] (do_one_initcall+0x1f/0xf4)
[ 1737.829367] [<c000867f>] (do_one_initcall+0x1f/0xf4) from [<c006bd31>] (load_module+0x10d5/0x15b0)
[ 1737.838872] [<c006bd31>] (load_module+0x10d5/0x15b0) from [<c006c26f>] (sys_init_module+0x63/0x88)
[ 1737.848379] [<c006c26f>] (sys_init_module+0x63/0x88) from [<c000c841>] (ret_fast_syscall+0x1/0x46)
[ 1737.857867] Code: 4825 f3d4 debb e036 (6ac5) f3bf 
[ 1737.884765] ---[ end trace cbd53ac03b070f86 ]---

This is my code:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>

MODULE_AUTHOR("MGE");
MODULE_DESCRIPTION("A sample driver using interrupts");
MODULE_LICENSE("GPL");

// GPIO Bank 2
#define GPIO2_START_ADDR 0x481AC000
#define GPIO2_SIZE (0x481ADFFF - GPIO2_START_ADDR)

// GPIO memory-mapped register addresses.
#define GPIO_IRQSTATUS_0 0x2C
#define GPIO_IRQSTATUS_1 0x30
#define GPIO_RISINGDETECT 0x148
#define GPIO_FALLINGDETECT 0x14C

#define PIN_A_GPIO 36
#define PIN_A_FLAGS GPIOF_IN
#define PIN_A_LABEL "HI_PIN_A"

static irqreturn_t irq_handler_pin_a (int irq, void *dev_id) {
  printk (KERN_INFO "Hello from irq_handler_pin_a...\n");

  return IRQ_HANDLED;
}

static int __init hello_interrupts_start (void) {
  int retval, irq, regval;
  void __iomem *mem;

  printk (KERN_INFO "Loading hello_interrupts module...\n");

  /**
   * Request the GPIO lines for the IRQ channels.
   */
  retval = gpio_request_one(PIN_A_GPIO, PIN_A_FLAGS, PIN_A_LABEL);

  if (retval) {
    printk (KERN_ERR "HI: Error: Failed to request GPIO pin#%i for IRQ (error %i)\n", PIN_A_GPIO, retval);

//    return -retval;
  }

  irq = gpio_to_irq (PIN_A_GPIO);
  if (irq < 0) {
    printk (KERN_ERR "HI: ERROR: Failed to obtain IRQ number for GPIO #%i (error %i)\n", PIN_A_GPIO, irq);

//    return -irq;
  }

  retval = request_irq (irq, irq_handler_pin_a, 0 /*IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING*/, PIN_A_LABEL, NULL);
  irq_set_irq_type (irq, IRQ_TYPE_EDGE_BOTH);

  if (retval) {
    printk (KERN_ERR "HI: ERROR: The requested IRQ line#%i from GPIO#%i (error %i)\n", irq, PIN_A_GPIO, retval);

//    return -retval;
  }
  else {
    printk (KERN_INFO "HI: Initialized GPIO #%i to IRQ #%i\n", PIN_A_GPIO, irq);
  }

  /**
   * Setup the IRQ registers with the appropriate values.
   */
  mem = ioremap(GPIO2_START_ADDR, GPIO2_SIZE);
  if(!mem) {
    printk (KERN_ERR "HI: ERROR: Failed to remap memory for GPIO Bank 2 IRQ pin configuration.\n");
    return 0;
  }

  // Enable the IRQ ability for GPIO_66.
  regval = ioread32 (mem + GPIO_IRQSTATUS_0);
  regval |= (1 << 2);
  iowrite32 (regval, mem + GPIO_IRQSTATUS_0);

  regval = ioread32 (mem + GPIO_IRQSTATUS_1);
  regval |= (1 << 2);
  iowrite32 (regval, mem + GPIO_IRQSTATUS_1);

  // Set GPIO_66 for rising and falling edge detection.
  regval = ioread32 (mem + GPIO_RISINGDETECT);
  regval |= (1 << 2);
  iowrite32(regval, mem + GPIO_RISINGDETECT);

  regval = ioread32 (mem + GPIO_FALLINGDETECT);
  regval |= (1 << 2);
  iowrite32 (regval, mem + GPIO_FALLINGDETECT);

  // Release the mapped memory.
  iounmap (mem);

  return 0;
}

static void __exit hello_interrupts_end(void) {

  printk ("HI: Releasing IRQ resources...\n");

  free_irq (gpio_to_irq (PIN_A_GPIO), NULL);
  gpio_free (PIN_A_GPIO);

  printk (KERN_INFO "Goodbye hello_interrupts!\n");
}

module_init (hello_interrupts_start);
module_exit (hello_interrupts_end);

Any idea? Thanks

2

There are 2 answers

1
Manuel Egío On BEST ANSWER

The problem was that the GPIO2 module clocks was disabled.

"8.1.12.1.30 CM_PER_GPIO2_CLKCTRL Register (offset = B0h) [reset = 30000h]" of "AM335x SitaraTM Processors - Technical Reference Manual"

Bits 1-0: Control the way mandatory clocks are managed. 0x0 = DISABLED : Module is disable by SW. Any OCP access to module results in an error, except if resulting from a module wakeup (asynchronous wakeup). 0x1 = RESERVED_1 : Reserved 0x2 = ENABLE : Module is explicitly enabled. Interface clock (if not used for functions) may be gated according to the clock domain state. Functional clocks are guarantied to stay present. As long as in this configuration, power domain sleep transition cannot happen. 0x3 = RESERVED : Reserved

0
A. Terstegge On

For the above code to work, the GPIO2 clock has to be enabled using the CM_PER_GPIO2_CLKCTRL register and a GPIO pin from GPIO2 has to be choosen. In the code above, GPIO 36 was used, which is on GPIO1 (and anyway used by the internal flash). The following code enables the GPIO2 clock and uses GPIO 68 (which is on header P9 pin 10 btw):

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>

MODULE_AUTHOR("MGE");
MODULE_DESCRIPTION("A sample driver using interrupts");
MODULE_LICENSE("GPL");

// GPIO Bank 2
#define GPIO2_START_ADDR 0x481AC000
#define GPIO2_SIZE (0x481ADFFF - GPIO2_START_ADDR)

// CM_PER (Clock Module Peripheral Registers
#define CM_PER_START_ADDR 0x44e00000
#define CM_PER_SIZE       0x400
#define CM_PER_GPIO2_CLKCTRL 0xb0

// GPIO memory-mapped register addresses.
#define GPIO_IRQSTATUS_0 0x2C
#define GPIO_IRQSTATUS_1 0x30
#define GPIO_RISINGDETECT 0x148
#define GPIO_FALLINGDETECT 0x14C

#define PIN_A_GPIO 68 // is on P9 pin 10
#define PIN_A_FLAGS GPIOF_IN
#define PIN_A_LABEL "HI_PIN_A"

static irqreturn_t irq_handler_pin_a (int irq, void *dev_id) {
  printk (KERN_INFO "Hello from irq_handler_pin_a...\n");

  return IRQ_HANDLED;
}

static int __init hello_interrupts_start (void) {
  int retval, irq, regval;
  void __iomem *mem;
  void __iomem *cm_per;
  printk (KERN_INFO "Loading hello_interrupts module...\n");

  /**
   * Request the GPIO lines for the IRQ channels.
   */
  retval = gpio_request_one(PIN_A_GPIO, PIN_A_FLAGS, PIN_A_LABEL);

  if (retval) {
    printk (KERN_ERR "HI: Error: Failed to request GPIO pin#%i for IRQ (error %i)\n", PIN_A_GPIO, retval);

//    return -retval;
  }

  irq = gpio_to_irq (PIN_A_GPIO);
  if (irq < 0) {
    printk (KERN_ERR "HI: ERROR: Failed to obtain IRQ number for GPIO #%i (error %i)\n", PIN_A_GPIO, irq);

//    return -irq;
  }

  retval = request_irq (irq, irq_handler_pin_a, 0 /*IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING*/, PIN_A_LABEL, NULL);
  irq_set_irq_type (irq, IRQ_TYPE_EDGE_BOTH);

  if (retval) {
    printk (KERN_ERR "HI: ERROR: The requested IRQ line#%i from GPIO#%i (error %i)\n", irq, PIN_A_GPIO, retval);

//    return -retval;
  }
  else {
    printk (KERN_INFO "HI: Initialized GPIO #%i to IRQ #%i\n", PIN_A_GPIO, irq);
  }

  /*
    Enable GPIO2 clock
  */
  cm_per = ioremap(CM_PER_START_ADDR, CM_PER_SIZE);
  if(!cm_per) {
    printk (KERN_ERR "HI: ERROR: Failed to remap memory for CM_PER.\n");
    return 0;
  }
  iowrite32(0x02, cm_per + CM_PER_GPIO2_CLKCTRL);
  iounmap(cm_per);

  /**
   * Setup the IRQ registers with the appropriate values.
   */
  mem = ioremap(GPIO2_START_ADDR, GPIO2_SIZE);
  if(!mem) {
    printk (KERN_ERR "HI: ERROR: Failed to remap memory for GPIO Bank 2 IRQ pin configuration.\n");
    return 0;
  }

  // Enable the IRQ ability for GPIO_66.
  regval = ioread32 (mem + GPIO_IRQSTATUS_0);
  regval |= (1 << 2);
  iowrite32 (regval, mem + GPIO_IRQSTATUS_0);

  regval = ioread32 (mem + GPIO_IRQSTATUS_1);
  regval |= (1 << 2);
  iowrite32 (regval, mem + GPIO_IRQSTATUS_1);

  // Set GPIO_66 for rising and falling edge detection.
  regval = ioread32 (mem + GPIO_RISINGDETECT);
  regval |= (1 << 2);
  iowrite32(regval, mem + GPIO_RISINGDETECT);

  regval = ioread32 (mem + GPIO_FALLINGDETECT);
  regval |= (1 << 2);
  iowrite32 (regval, mem + GPIO_FALLINGDETECT);

  // Release the mapped memory.
  iounmap (mem);

  return 0;
}

static void __exit hello_interrupts_end(void) {

  printk ("HI: Releasing IRQ resources...\n");

  free_irq (gpio_to_irq (PIN_A_GPIO), NULL);
  gpio_free (PIN_A_GPIO);

  printk (KERN_INFO "Goodbye hello_interrupts!\n");
}

module_init (hello_interrupts_start);
module_exit (hello_interrupts_end);