linuxlinux-kernelkernel-moduledevice-treeimx6

Capturing power-off interrupt for i.MX6UL (linux kernel)


Context

I'm using an i.MX6 (IMXULL) application processor, and want to know in software when the power-off button has been pressed:

Luckily, the IMX6ULL reference manual explains that this should be possible:


Section 10.5: ONOFF Button

The chip supports the use of a button input signal to request main SoC power state changes (i.e. On or Off) from the PMU. The ONOFF logic inside of SNVS_LP allows for connecting directly to a PMIC or other voltage regulator device. The logic takes a button input signal and then outputs a pmic_en_b and set_pwr_off_irq signal. [...] The logic has two different modes of operation (Dumb and Smart mode). The Dumb PMIC Mode uses pmic_en_b to issue a level signal for on and off. Dumb pmic mode has many different configuration options which include (debounce, off to on time, and max time out).

(Also available in condensed form here on page 18)


Attempt

Therefore, I have built a trivially simple kernel module to try and capture this interrupt:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/interrupt.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Doe <j.doe@acme.inc>");


// Forward declaration
irqreturn_t irq_handler (int, void *);


// Number of interrupt to capture
#define INTERRUPT_NO        36


static int __init pwr_ctl_init (void)
{
    pr_err("init()\n");
    return request_irq(INTERRUPT_NO, irq_handler, IRQF_SHARED, "onoff-button",
        (void *)irq_handler);
}

static void __exit pwr_ctl_exit (void)
{
    pr_err("exit()\n");
    free_irq(INTERRUPT_NO, NULL);
}


irqreturn_t irq_handler (int irq, void *dev_irq)
{
    pr_err("interrupt!\n");
    return IRQ_HANDLED;
}

module_init(pwr_ctl_init);
module_exit(pwr_ctl_exit);

Problem

However, I cannot find any information about what the number of the interrupt is. When searching on the internet, all I get is this one NXP forum post:

Which hints it should be 36. However, I have found that this isn't the case on my platform. When I check /proc/interrupts 36 is already occupied by 20b4000.ethernet. Because the application manual also mentions that it is generated by the SNVS low power system, I checked the device-tree and found the following information:

                snvs_poweroff: snvs-poweroff {
                    compatible = "syscon-poweroff";
                    regmap = <&snvs>;
                    offset = <0x38>;
                    value = <0x60>;
                    mask = <0x60>;
                    status = "disabled";
                };

                snvs_pwrkey: snvs-powerkey {
                    compatible = "fsl,sec-v4.0-pwrkey";
                    regmap = <&snvs>;
                    interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
                    linux,keycode = <KEY_POWER>;
                    wakeup-source;
                    status = "disabled";
                };

This information seems useful for knowing that SNVS is the interrupt controller, but not how to capture this set_pwr_off_irq signal.


Conclusion


Edit

This edit answers some user questions, and then goes into new information about the problem I have since discovered:

User Questions

New Information


Solution

  • The answer is rather anticlimactic. In short, there was a device-tree overlay that was disabling my changes to snvs_pwrkey, even when I had enabled it. Once I located and removed the overlay, the driver (snvs_pwrkey.c) was working as expected.

    As for the IRQ number, it turns out that the IRQ for the power button is 45 as interpreted through Linux. The interrupt is not configured for sharing, so my kernel module could not be loaded.

    If you want to capture power button toggle events, I suggest modifying the driver to add some output, and then perhaps adding a udev rule to capture button presses. I will update my answer with an example ASAP.