This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM4376: NMI interrupt number in Linux

Part Number: AM4376
Other Parts Discussed in Thread: AM4372, AMIC120, TPS65218, AM4378

I am using an AM4376 on a custom board, booting and Arago Linux image.  I have created a Device Tree, base on the DTSI output from the online pinmux tool, that maps the NMI pin as the default, here is the DTS snippet:

&am43xx_pinmux {

arm1_pins_default: arm1_pins_default {
pinctrl-single,pins = <
AM4372_IOPAD(0xa84, PIN_INPUT | MUX_MODE0) /* (G25) EXTINTn.nNMI */
>;
};

I am creating a Linux kernel object and attempting to map an ISR from this pin to an interrupt source using the Linux request_irq() kernel funtion:

int request_irq (unsigned int  irq, irq_handler_t  handler, unsigned long  irqflags, const char *  devname, void *  dev_id);

From another post it was suggested to use 39 for the value of irq. When I use interrupt 39 I get this message when I load the module:

[63531.897998] genirq: Flags mismatch irq 39. 00000001 (nmi_irq_handler) vs. 00006004 (44e0b000.i2c)

This suggests that I may have mapped an I2C interface to the NMI interrupt number? 

I have searched the Tech Reference manual. "AM437x and AMIC120 ARM® Cortex™-A9 Processors Technical Reference Manual" and was able to find a reference to the IRQ number for the NMI.

My question is: For the NMI pin mapped above what is the value of "unsigned int irq" in the request_irq? Can you point me a reference in Arago that may answer this question?

Thanks in advance,

Rob

  • Hi Rob,

    Do you use AM437x PSDK Linux v6.01?

    Before enabling NMI interrupt, can you check if there is i2C interrupt already enabled on 39 number? You can do this with below command:

    cat /proc/interrupts


    Check also below e2e threads:

    https://e2e.ti.com/support/processors/f/791/t/597890

    https://e2e.ti.com/support/processors/f/791/t/445724

    Regards,
    Pavel

  • Pavel,

    Thanks for you response and your time at considering my question.

    I am using the SDK for this version of Linux: linux-4.14.79+gitAUTOINC+bde58ab01e-gbde58ab01e

    When I run the command cat /proc/interrupts I do see that interrupt 39 is assigned to the I2C, as shown below:

    core-C login:
    _____ _____ _ _
    | _ |___ ___ ___ ___ | _ |___ ___ |_|___ ___| |_
    | | _| .'| . | . | | __| _| . | | | -_| _| _|
    |__|__|_| |__,|_ |___| |__| |_| |___|_| |___|___|_|
    |___| |___|

    Arago Project http://arago-project.org core-C ttyS4

    Arago 2018.10 core-C ttyS4

    core-C login: root
    root@core-C:~# cat /proc/interrupts
    CPU0
    16: 11650 WUGEN 68 Level gp_timer
    18: 5481 GIC-0 29 Edge twd
    20: 1 WUGEN 9 Level l3-dbg-irq
    21: 1 WUGEN 10 Level l3-app-irq
    23: 1 WUGEN 78 Level wkup_m3_txev
    24: 331 WUGEN 12 Level 49000000.edma_ccint
    26: 7 WUGEN 14 Level 49000000.edma_ccerrint
    30: 18 WUGEN 72 Level 44e09000.serial
    33: 518 WUGEN 45 Level 481a8000.serial
    34: 0 WUGEN 77 Level wkup_m3
    36: 0 WUGEN 96 Level 44e07000.gpio
    37: 0 WUGEN 98 Level 4804c000.gpio
    38: 0 WUGEN 148 Level 48322000.gpio
    39: 62 WUGEN 70 Level 44e0b000.i2c
    40: 0 WUGEN 71 Level 4802a000.i2c
    41: 1081 WUGEN 64 Level mmc0
    44: 64332 WUGEN 41 Level 4a100000.ethernet
    45: 25386 WUGEN 42 Level 4a100000.ethernet
    47: 0 WUGEN 109 Level 53100000.sham
    50: 0 WUGEN 111 Level 48310000.rng
    51: 5 WUGEN 172 Level dwc3-omap
    52: 5 WUGEN 178 Level dwc3-omap
    64: 0 PRCM Edge pinctrl
    87: 0 WUGEN 7 Level tps65218
    88: 0 WUGEN 168 Level xhci-hcd:usb1
    89: 0 WUGEN 174 Level xhci-hcd:usb3
    Err: 0
    root@core-C:~#

    So what I need to know is how can I map an interrupt to the NMI pin that is being configured in the pinmux controller? One of your referenced links suggests using the interrupts-extended dt binding?

    &am43xx_pinmux {
    arm1_pins_default: arm1_pins_default {
    pinctrl-single,pins = <
    AM4372_IOPAD(0xa84, PIN_INPUT | MUX_MODE0) /* (G25) EXTINTn.nNMI */
    >;
    };

    Thanks in advance,

    Rob

  • The irq number passed to request_irq() is dynamically allocated and should be obtained from platform_get_irq() or platform_get_irq_byname(), where the former is by index and the latter by name. For example. if the DT entry for your device is something like:

    / {
    	my-device {
    		compatible = "my-driver";
    		interrupt-parent = <&gic>;
    		interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; /* NMIn */
    		interrupt-names = "nmi";
    		// optional:
    		pinctrl-names = "default";
    		pinctrl-0 = <&my_device_pins>;
    	};
    };

    then your driver would be able to obtain the irq number with platform_get_irq(pdev, 0) or platform_get_irq_byname(pdev, "nmi").

    Note that configuring pinmux for the NMI pin is optional since it already defaults to this function after reset.

    (BTW, just in case it's useful to know: you can also have irqs delivered to userspace via a file descriptor using the uio_pdrv_genirq driver. just ask if you want more details on that)

  • Rob,

    As Matthijs explains, you can use dynamically allocated IRQ number for NMI, thus you will not have a conflict with I2C. You can also explore your IRQ configuration with omaconf tool. Please refer to below e2e thread for details.

    https://e2e.ti.com/support/processors/f/791/p/743601/2767761#2767761

    Regards,
    Pavel

     

  • Pavel,

    Thanks for passing on Matthijs explanation. I have successfully created a platform device in my device tree with the addition of:

    nmi_pin {
    compatible = "nmi";
    interrupt-parent = <&gic>;
    interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>; /* NMIn */
    interrupt-names = "nmi";
    pinctrl-names = "default";
    pinctrl-0 = <&nmi_pins_default>;
    };

    BTW: I had to change the interrupt number from the sample as I have another device on my system already using irq 7.

    Loading the device tree I have these device bindings on my system:

    ./sys/devices/platform/nmi_pin

    ./sys/firmware/devicetree/base/nmi_pin

    ./sys/bus/platform/devices/nmi_pin

    I am trying to create an ISR using a kernel module that handles the nmi interrupt. My module code looks like this:

    struct platform_device pdev;
    int NmiIrqNumber = NMI_IRQ_NUM;

    strcpy(pdev.name,"nmi_pin");

    NmiIrqNumber = platform_get_irq_byname(&pdev, "nmi");

    I think my definition for "struct platform_device pdev" needs more initialization?   I have been looking for samples, but have not been able to find a working solution. Any suggestions?

    Regards,

    Rob

  • Rob,

    Rob Peipert said:
    compatible = "nmi";

    Do you have a driver with such compatible string "nmi"? For more info regarding this entry, you can refer to below DTS file:

    linux-kernel/arch/arm/boot/dts/am437x-gp-evm.dts

    &i2c0 {
        status = "okay";
        pinctrl-names = "default";
        pinctrl-0 = <&i2c0_pins>;
        clock-frequency = <100000>;

        tps65218: tps65218@24 {
            reg = <0x24>;
            compatible = "ti,tps65218";
            interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; /* NMIn */
            interrupt-controller;
            #interrupt-cells = <2>;

    This string "ti,tps65218" points to below driver:

    linux-kernel/drivers/mfd/tps65218.c

    In am437x-gp-evm.dts file is described how TPS65218 INTn output is used to interrupt AM4378 device at G25 NMIn input pin.

     

    Rob Peipert said:
    interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>; /* NMIn */

    Rob Peipert said:
    BTW: I had to change the interrupt number from the sample as I have another device on my system already using irq 7.

    I am not sure if this is correct. It seems to me that you should be using number 7, as TRM Table 8-1. ARM Cortex-A9 Interrupts starts from 32, thus 7 correspond to 39 NMIn. See for example file am4372.dtsi, where i2c0 is mapped to number 70, which corresponds to number 102 I2C0INT from the TRM table.

    i2c0: i2c@44e0b000 {
                compatible = "ti,am4372-i2c","ti,omap4-i2c";
                reg = <0x44e0b000 0x1000>;
                interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
                ti,hwmods = "i2c1";
                #address-cells = <1>;
                #size-cells = <0>;
                status = "disabled";
            };

  • Pavel,

    My kernel module creates a character device /dev/nmi?  I am certain that this is different from a platform driver name "nmi"? I suppose I need to create this platform device? 

    Also, the apparent conflicting device: 

     tps65218tps65218@24 {
            reg = <0x24>;
            compatible = "ti,tps65218";
            interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>; /* NMIn */
            interrupt-controller;
            #interrupt-cells = <2>;

    is not really needed in our design. The functions it provides are strapped with resisters.

    I attempted to remove the interrupt references in the device tree that it used and I still was blocked by I2C0 device interrupts.

    Regards,

    Rob

  • Rob Peipert said:
    Thanks for passing on Matthijs explanation.

    He did no such thing, I'm just a community user trying to help out.

    Rob Peipert said:
    interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>; /* NMIn */

    This is incorrect, NMI is GIC_SPI 7. GIC_SPI 17 is reserved and will probably never fire.

    Rob Peipert said:
    BTW: I had to change the interrupt number from the sample as I have another device on my system already using irq 7.

    the apparent conflicting device is not really needed in our design. The functions it provides are strapped with resisters.

    The device tree needs to accurately reflect your actual hardware. If you are using an existing dts as template you'll need to examine it from top to bottom to determine which parts of it are still accurate for your board and which parts need to be changed.

    If you're using a discrete power solution instead of a pmic then you need to remove the declaration for the pmic and replace it with suitable declarations for your discrete power solution, presumably one or more fixedregulator declarations. You'll figure out what declarations you need based on which supply references break when you remove the pmic.

    Rob Peipert said:
    I attempted to remove the interrupt references in the device tree that it used and I still was blocked by I2C0 device interrupts.

    I have no idea what you mean by that. I2C0 has nothing to do with NMI.

    Rob Peipert said:
    struct platform_device pdev;

    I think my definition for "struct platform_device pdev" needs more initialization?

    This is not something you allocate or initialize yourself, the linux device framework will do so. You need to make a platform driver which declares a compatible-string that matches the one in your DT, e.g.

    static int my_thing_probe(struct platform_device *pdev)
    {
    	...
    }
    
    static int my_thing_remove(struct platform_device *pdev)
    {
    	...
    }
    
    static const struct of_device_id my_thing_dt_ids[] = {
    	{ .compatible = "my-thing", },
    	{},
    };
    MODULE_DEVICE_TABLE(of, my_thing_dt_ids);
    
    static struct platform_driver my_thing_driver = {
    	.probe		= my_thing_probe,
    	.remove		= my_thing_remove,
    	.driver		= {
    		.name	= "my_thing",
    		.of_match_table = my_thing_dt_ids,
    	}
    };
    
    module_platform_driver(my_thing_driver);
    
    MODULE_AUTHOR("Your Name");
    MODULE_DESCRIPTION("Driver description");
    MODULE_LICENSE("GPL v2");

    Note that this really doesn't have anything to do anymore with TI SoCs, these are just generic linux kernel development questions. I'm sure there are more resources on the internet regarding how to make a platform driver.

    Also, what are you trying to achieve with this? Like I said before, if you just want to deliver an IRQ to userspace, you don't need to write any kernel code at all, there's a standard driver you can use for that. (There's more than one option even)

  • Matthijs,

    I am new to this forum as of ~June and did not realize that there are external contributors.  Let me start by saying that the information you have provided has been most helpful at getting me to resolution. Thank you so much!

    I want to provide as much detail as possible regarding my solution for purposes of helping others that may have similar questions and to answer your inquiries regarding the design.

    The hardware we are producing contains 3 instances of an AM437x SOC on a single PCB.  Each instance is based on the am437x IDK reference design and I have used the am4372.dtsi and am437x-idk.dts as the basis for all my dts files. So there are some artifacts from the reference design that still exist in my dts file. As I am still working on the design,there are changes yet to be completed. The pmic is one of the design deltas from the reference design.

    I agree that this issue has moved away from a am437x specific solution and is more of a Linux custom target issue. I had  some web searching on how to instantiate a platform driver and was not successful. Perhaps, I was using the incorrect search tags? All I found were details on the platform API, not on the implementation or code samples.

    The reason for handling this interrupt in kernel space is that the NMI is used by one CPU to send an immediate request to to another CPU. The action that is performed is hardware specific to manage another interface. User code could be used for this response/processing however the response requirements are too tight and must meet a minimum threshold. Kernel level execution of this code will allow the hardware to work as required.

    Using your example I discovered that I needed to change my module init macros from

    module_init(my_module);
    module_exit(my_module);

    To 

    module_platform_driver(my_driver);

    Then I created all the platform devices and tables.

    static const struct of_device_id my_driver_dt_ids[] = {
    { .compatible = "nmi", },
    {},
    };

    MODULE_DEVICE_TABLE(of, my_driver_dt_ids);

    static struct platform_driver my_driver = {
    .driver = {
    .name = "nmi",
    .owner = THIS_MODULE,
    .of_match_table = my_driver_dt_ids,
    },
    .probe = my_probe,
    .remove = my_remove,

    };

    The probe and remove functions are:

    struct platform_device *pNmiPlatformDev;

    int my_probe(struct platform_device *pDev)
    {

    pNmiPlatformDev = pDev;

    return 0;

    }

    int nmi_remove(struct platform_device *pDev)
    {

    return 0;

    }

    pNmiPlatformDev is used to request the IRQ by calling:

    int NmiIrqNumber  =  platform_get_irq_byname(pNmiPlatformDev, "nmi");

    I can then use this return value to successfully request the IRQ:

    request_irq(NmiIrqNumber,(irq_handler_t) Irq_Handler,IRQF_TRIGGER_RISING,"irq_handler", NULL);

    Thanks again for your help,

    Rob