0

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

  • How do I capture the ON/OFF interrupt supposedly generated by SNVS?
  • How do I determine the number of an interrupt from the device-tree (if applicable at all)
  • Am I misunderstanding something about how the ONOFF feature works? Is it possible to capture this from a kernel module at all?

Edit

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

User Questions

  • Processor: The processor is an NXP i.MX 6UltraLite / 6ULL / 6ULZ ARM Cortex A7.

New Information

  • SNVS Driver: Using my build system kernel configuration, I have modified and verified that the snvs_pwrkey driver (see here) is enabled. My modification consists of adding a single kprint statement to the interrupt routine to see if the button trips it. This did not work
  • I have tried updating the driver to a newer version, which claims to support newer i.MX6 processors. This also did not work
  • I have tried to load the driver as a kernel module for easier debugging. This is not possible, as the kernel configuration requires this be enabled and I cannot remove it from being statically built into the kernel.
Micrified
  • 3,338
  • 4
  • 33
  • 59
  • Have you checked whether the `snvs_pwrkey` node is enabled in your board dts file? `snvs_pwrkey` node shown in your code is disabled (try enabling it by adding `&snvs_pwrkey{ status = "okay"; };` in your dts). Assuming that's the only issue, once you set it up correctly, you can capture the interrupt the same way the snvs_pwrkey.c driver does it if you want to do it in kernel space. If you want to capture it from user space, the snvs_pwrkey.c already does it for you by exposing it in the input subsystem, so you can access it just like any input from /dev/input/ or wherever it's mapped to. – dhanushka Jul 11 '21 at 04:29
  • @dhanushka Yeah, it is enabled. I feel like I've triple checked it. By default it is `disabled`, that's correct. But I've tried setting `status = "okay";` and also just removing the `status` line altogether. Nothing happens sadly. It's driving me crazy! – Micrified Jul 11 '21 at 18:44

1 Answers1

0

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.

Micrified
  • 3,338
  • 4
  • 33
  • 59