Sc16is7xx driver issue in BSP 6.5

I am upgrading from Toradex BSP 5.6.0 to 6.5.0 and the behaviour of the sc16is7xx module is now different (and maybe broken). It appears as though the driver is in an infinite loop of querying for the interrupt status of the SPI attached device. There are 4 processes running which sum to almost 100% of one core

  1. irq/143-spi1.0 (first device IRQ)
  2. irq/149-spi1.1 (second device IRQ)
  3. spi1 (the driver)
  4. irq/40-3083000 (SPI1’s IRQ)
    I think this is consistent with the idea that the IRQ is never being cleared. The SPI transactions look good and well formed, and the devices always return no IRQ outstanding. However, the next sets of transactions are a repeat of the previous interrupt query.

To verify my hardware is fine, I went back to BSP5.6 and everything works fine.

When looking at the output of /proc/interrupts, the two IRQ lines are now Level triggered, instead of Edge triggered. Looking through the changes to the driver since then, this does make sense. However, how does the interrupt configuration get used then? I have tried all 4 different variants of high/low and edge/level, and there is no change in behaviour.

The current device tree used int he BPS6.5 version is as follows:

Device tree, both devices in IRQ loops
/dts-v1/;

#include "imx8mm-verdin.dtsi"
#include "imx8mm-verdin-wifi.dtsi"
#include "imx8mm-verdin-dahlia.dtsi"

#include "imx8mm-pinfunc.h"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>

/ {
	compatible = "toradex,verdin-imx8mm-wifi-dahlia",
		     "toradex,verdin-imx8mm-wifi",
		     "toradex,verdin-imx8mm",
		     "fsl,imx8mm"; 

	/* fixed clock dedicated to the first DUART controller */
	clk25m0: clk25m0 {
		compatible = "fixed-clock";
		#clock-cells = <0>;
		clock-frequency = <25000000>;
	};
	
	/* fixed clock dedicated to the second DUART controller */
	clk25m1: clk25m1 {
		compatible = "fixed-clock";
		#clock-cells = <0>;
		clock-frequency = <25000000>;
	};
};

&flexspi {
	status = "disabled";
};

&pinctrl_gpio3 {
	/delete-property/ fsl,pins;
};

&iomuxc {

	pinctrl_duart1_irq: duart1irqgrp {
		fsl,pins =
			<MX8MM_IOMUXC_NAND_DATA02_QSPI_A_DATA2	0x146>; /* SODIMM 60 */
	};
	pinctrl_duart2_irq: duart2irqgrp {
		fsl,pins =
			<MX8MM_IOMUXC_NAND_DQS_QSPI_A_DQS	0x106>; /* SODIMM 66 */
	};
	pinctrl_duart2_cs: duart2csgrp {
		fsl,pins =
			<MX8MM_IOMUXC_UART3_RXD_GPIO5_IO26	0x6>; /* SODIMM 210 */
	};
};

/* Verdin SPI_1 */
&ecspi2 {
    pinctrl-0 = <&pinctrl_ecspi2>, <&pinctrl_duart2_cs>;
    cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>,
				<&gpio5 26 GPIO_ACTIVE_LOW>;
    fsl,spi-num-chipselects = <2>;
    status = "okay";

    spidev0: spidev@0
    {
        status = "disabled";
    };

    duart1: sc16is752@0 {
        compatible = "nxp,sc16is752";
        spi-max-frequency = <400000>;
        reg = <0>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_duart1_irq>;
        clocks = <&clk25m0>;
        interrupt-parent = <&gpio3>;
        interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
        status = "okay";
    };

    duart2: sc16is752@1 {
        compatible = "nxp,sc16is752";
        spi-max-frequency = <400000>;
        reg = <1>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_duart2_irq>;
        clocks = <&clk25m1>;
        interrupt-parent = <&gpio3>;
        interrupts = <14 IRQ_TYPE_EDGE_RISING>; /* Yes, this line is inverted. */
        status = "okay";
    };
};

If I then remove the pinctrl entries within each of the two duart entries, then only the second device seems to be in an IRQ loop. (I had originally put in the pinctrl entries when migrating to BSP6.5 as this is what the Mallow device tree used for its TPM device on the SPI bus.) Seeing how the first device worked, and the second didn’t, I tried swapping some things around and it seems like it does not matter which IRQ level type I use, it will always be used by the driver to be IRQ_TYPE_LEVEL_LOW. This is different from BSP5.6 version of the sc16is7xx driver where the interrupts property seems to be used.

I was able to get a partially functional system by deleting the second duart with the inverted IRQ line. My final device tree that partially works looks like this:

Device tree, only one device, no IRQ loop
/dts-v1/;

#include "imx8mm-verdin.dtsi"
#include "imx8mm-verdin-wifi.dtsi"
#include "imx8mm-verdin-dahlia.dtsi"

#include "imx8mm-pinfunc.h"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>

/ {
	compatible = "toradex,verdin-imx8mm-wifi-dahlia",
		     "toradex,verdin-imx8mm-wifi",
		     "toradex,verdin-imx8mm",
		     "fsl,imx8mm"; 

	/* fixed clock dedicated to the first DUART controller */
	clk25m0: clk25m0 {
		compatible = "fixed-clock";
		#clock-cells = <0>;
		clock-frequency = <25000000>;
	};
	
	/* fixed clock dedicated to the second DUART controller */
	clk25m1: clk25m1 {
		compatible = "fixed-clock";
		#clock-cells = <0>;
		clock-frequency = <25000000>;
	};
};

&flexspi {
	status = "disabled";
};

&pinctrl_gpio3 {
	/delete-property/ fsl,pins;
};

&iomuxc {

	pinctrl_duart1_irq: duart1irqgrp {
		fsl,pins =
			<MX8MM_IOMUXC_NAND_DATA02_QSPI_A_DATA2	0x146>; /* SODIMM 60 - active low IRQ line, now unreferenced*/
	};
	pinctrl_duart2_irq: duart2irqgrp {
		fsl,pins =
			<MX8MM_IOMUXC_NAND_DQS_QSPI_A_DQS	0x106>; /* SODIMM 66 - active high IRQ line, now unreferenced*/
	};
	pinctrl_duart2_cs: duart2csgrp {
		fsl,pins =
			<MX8MM_IOMUXC_UART3_RXD_GPIO5_IO26	0x6>; /* SODIMM 210 */
	};
};

/* Verdin SPI_1 */
&ecspi2 {
	pinctrl-0 = <&pinctrl_ecspi2>, <&pinctrl_duart2_cs>;
	cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>,
				<&gpio5 26 GPIO_ACTIVE_LOW>;
	fsl,spi-num-chipselects = <2>;
	status = "okay";

	spidev0: spidev@0
	{
		status = "disabled";
	};

	duart1: sc16is752@0 {
		compatible = "nxp,sc16is752";
		spi-max-frequency = <4000000>;
		reg = <0>;
		clocks = <&clk25m0>;
		interrupt-parent = <&gpio3>;
		interrupts = <8 IRQ_TYPE_EDGE_FALLING>;
		status = "okay";
	};
};

What I’m not sure about is if my conclusions are correct

  1. Does the sc16is7xx driver only support IRQ_TYPE_LEVEL_LOW? It seems like from the driver source that this would be the case.
  2. Where is the proper location to specify pinctrl-0 = <&pinctrl_duart1_irq>;?
  3. why do I need to disable spidev@0? This is a change in needed to make to my dts file between the two BSP versions and I have no idea which device tree file it comes from, or if it even comes from a device tree anywhere.

If it helps, here’s my tdx-info from the device running BSP6.5.0:

tdx-info
Software summary
------------------------------------------------------------
Bootloader:               U-Boot
Kernel version:           5.15.129-rt67-6.5.0-devel+git.6f8fd49366db #1 SMP PREEMPT_RT Fri Dec 22 11:15:52 UTC 2023
Kernel command line:      root=PARTUUID=a17d4c3c-02 ro rootwait console=tty1 console=ttymxc0,115200 consoleblank=0 earlycon
Distro name:              NAME="TDX Wayland with XWayland RT"
Distro version:           VERSION_ID=6.5.0-devel-20240209163348-build.0
Distro variant:           -
Hostname:                 verdin-imx8mm-06902104
------------------------------------------------------------

Hardware info
------------------------------------------------------------
HW model:                 custom
Toradex version:          0055 V1.1B
Serial number:            06902104
Processor arch:           aarch64
------------------------------------------------------------

Greetings @mckay,

This does certainly sound like some strange behavior you’re observing. Let me see if I can try and add my thoughts.

Does the sc16is7xx driver only support IRQ_TYPE_LEVEL_LOW? It seems like from the driver source that this would be the case.

On initial review it would seem so.

Where is the proper location to specify pinctrl-0 = <&pinctrl_duart1_irq>;?

Looking at the device tree binding documentation it does not really specify: nxp,sc16is7xx.txt « serial « bindings « devicetree « Documentation - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules

Though looking at other SPI devices in the device tree like can1: imx8mm-verdin.dtsi « freescale « dts « boot « arm64 « arch - linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules

I can see this node has a pinctrl-0 that specifies the interrupt pin.

why do I need to disable spidev@0? This is a change in needed to make to my dts file between the two BSP versions and I have no idea which device tree file it comes from, or if it even comes from a device tree anywhere.

In BSP 6.X by default this overlay is applied that enables this spidev node: verdin-imx8mm_spidev_overlay.dts « overlays - device-tree-overlays.git - Sources for Device Tree Overlays

I don’t believe this was the case in BSP 5.X.

When looking at the output of /proc/interrupts, the two IRQ lines are now Level triggered, instead of Edge triggered.

Regarding this point, I was looking at the commits that were added between 5.X and 6.X and I noticed this: linux-toradex.git - Linux kernel for Apalis, Colibri and Verdin modules

It appears that when the interrupt line is shared the IRQ becomes level-triggered rather than edge triggered. Perhaps this is the main culprit for the behavior difference you are seeing?

Best Regards
Jeremias