2

Update: This was a good learning experience but I discovered that my GPIO expander does not have the interrupt line connected, so I cannot achieve what I was hoping to do.

Instead I'm going to try investigating using one of the pins on my serial connection, which can generate interrupts.


I am using an embedded Linux (4.15.0) single-board computer which has a NXP PCA9535 GPIO expander on the I2C bus. I would like to attach a pulse-per-second (PPS) source to one of the GPIO pins and have it show up under /sys/class/pps/.

Thanks to this question and @0andriy's terrific answer, I have successfully gotten Linux to enumerate the GPIO pins.

Now, I am trying to (i) have one of those pins generate a interrupts when its level changes, and (ii) have the pps-gpio driver expose this pin as a PPS device.

(Regarding (i) above, I have rebuilt my kernel with CONFIG_GPIO_PCA953X_IRQ=y so I think theoretically it should be possible.)

When I run what I have so far, I get (with CONFIG_DEBUG_GPIO=y):

[   29.906616] ACPI: Host-directed Dynamic ACPI Table Load:
[   29.906635] ACPI: SSDT 0xFFFF989EFD4AC400 00037A (v05        GPIO     20210212 INTL 20180105)
[   29.907108] ACPI: Executed 1 blocks of module-level executable AML code
[   29.933345] acpi PRP0001:00: GPIO: looking up 0 in _CRS
[   29.933398] pca953x i2c-PRP0001:00: GPIO lookup for consumer reset
[   29.933401] pca953x i2c-PRP0001:00: using ACPI for GPIO lookup
[   29.933405] acpi PRP0001:00: GPIO: looking up reset-gpios
[   29.933410] acpi PRP0001:00: GPIO: looking up reset-gpio
[   29.933413] pca953x i2c-PRP0001:00: using lookup tables for GPIO lookup
[   29.933417] pca953x i2c-PRP0001:00: lookup for GPIO reset failed
[   29.933424] pca953x i2c-PRP0001:00: i2c-PRP0001:00 supply vcc not found, using dummy regulator
[   29.936087] gpiochip_find_base: found new base at 322
[   29.936946] gpio gpiochip3: (pca9535): added GPIO chardev (254:3)
[   29.937000] gpiochip_setup_dev: registered GPIOs 322 to 337 on device: gpiochip3 (pca9535)
[   29.944603] pps-gpio PRP0001:01: failed to get GPIO from device tree
[   29.951505] pps-gpio: probe of PRP0001:01 failed with error -38

Any guidance would be very appreciated, as I am completely new to ACPI and am not sure how to begin debugging this.


DefinitionBlock ("gpio.aml", "SSDT", 5, "", "GPIO", 0x20210212)
{
    External (_SB.PCI0.SBUS, DeviceObj)

    Scope (\_SB.PCI0.SBUS)
    {
        Device (GPI0)
        {
            // This special _HID (Hardware ID) tells Linux to use Device Tree-
            // compatible device identification.
            Name (_HID, "PRP0001")
            
            Name (_DDN, "NXP PCA9535 GPIO expander")

            Name (_CRS, ResourceTemplate () {
                I2cSerialBusV2(
                    0x0020,                 // I2C address
                    ControllerInitiated,
                    400000,                 // Bus speed (see PCA9535 datasheet)
                    AddressingMode7Bit,     
                    "\\_SB.PCI0.SBUS",      // SMBus controller
                    0x00,
                    ResourceConsumer,
                    /* omitted */,
                    Exclusive,
                    /* omitted */
                )
            })

            Name (_DSD, Package () {
                // Device Properties
                ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
                Package () {
                    // These correspond to Linux Device Tree properties
                    Package () {"compatible", "nxp,pca9535"},
                    Package () {"gpio-line-names", Package () {
                        "GPIOA.0", "GPIOA.1", "GPIOA.2", "GPIOA.3",
                        "GPIOA.4", "GPIOA.5", "GPIOA.6", "GPIOA.7",
                        "GPIOB.0", "GPIOB.1", "GPIOB.2", "GPIOB.3",
                        "GPIOB.4", "GPIOB.5", "GPIOB.6", "GPIOB.7",
                    }},
                }
            })
        }
    }

    Device (PPS0)
    {
        Name (_HID, "PRP0001")

        Name (_CRS, ResourceTemplate () {
            GpioInt (Edge, ActiveLow, Shared, PullDefault, 0,
                "\\_SB.PCI0.SBUS.GPI0", 0, ResourceConsumer, iPPS /* ref */)
            {
                0 // GPIOA.0 pin
            }
        })

        Name (_DSD, Package() {
            // Device Properties
            ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
            Package () {
                // https://github.com/torvalds/linux/blob/v4.15/Documentation/devicetree/bindings/pps/pps-gpio.txt
                Package () { "compatible", "pps-gpio" },
                Package () {
                    "gpios", Package () {
                        ^PPS0, iPPS, 0, 0 /* required for GpioInt */
                    }
                },
            }
        })
    }
}

I've also tried assigning a button to one of the pins, following this example, and I get:

[   29.953160] gpio-323 (Button A): gpiod_set_debounce: missing set() or set_config() operations
[   29.953170] gpio-keys PRP0001:02: Unable to get irq number for GPIO 0, error -6

Maybe it's noteworthy that the SMBus and a serial device share the same IRQ number? I've asked a question about that over here.

gpioinfo from libgpiod doesn't seem to be able to examine my PCA9535 GPIO controller, but I don't know if that is related or not. I am able to export pins with sysfs and inspect their values.

$ sudo ./gpioinfo 3
gpiochip3 - 16 lines:
gpioinfo: unable to retrieve the line object from chip: Invalid argument

Finally, considering the relevant drivers have seen some recent development, I built Linux 5.4.0 and tried with the same configuration. It is largely the same, but the log is slightly more verbose:

[   63.930115] ACPI: Host-directed Dynamic ACPI Table Load:
[   63.930140] ACPI: SSDT 0xFFFF8B55770F3800 00037A (v05        GPIO     20210212 INTL 20180105)
[   63.996036] acpi PRP0001:00: GPIO: looking up 0 in _CRS
[   63.996136] pca953x i2c-PRP0001:00: GPIO lookup for consumer reset
[   63.996139] pca953x i2c-PRP0001:00: using ACPI for GPIO lookup
[   63.996143] acpi PRP0001:00: GPIO: looking up reset-gpios
[   63.996147] acpi PRP0001:00: GPIO: looking up reset-gpio
[   63.996150] pca953x i2c-PRP0001:00: using lookup tables for GPIO lookup
[   63.996159] pca953x i2c-PRP0001:00: No GPIO consumer reset found
[   63.996168] pca953x i2c-PRP0001:00: i2c-PRP0001:00 supply vcc not found, using dummy regulator
[   63.996210] pca953x i2c-PRP0001:00: using no AI
[   63.996875] gpiochip_find_base: found new base at 322
[   63.998604] gpio gpiochip3: (i2c-PRP0001:00): added GPIO chardev (254:3)
[   63.998672] gpiochip_setup_dev: registered GPIOs 322 to 337 on device: gpiochip3 (i2c-PRP0001:00)
[   64.009748] pps-gpio PRP0001:01: GPIO lookup for consumer (null)
[   64.009753] pps-gpio PRP0001:01: using ACPI for GPIO lookup
[   64.009756] acpi PRP0001:01: GPIO: looking up gpios
[   64.009763] acpi PRP0001:01: GPIO: _DSD returned PRP0001:01 0 0 0
[   64.009800] gpio gpiochip3: Persistence not supported for GPIO 0
[   64.010245] pps-gpio PRP0001:01: GPIO lookup for consumer echo
[   64.010248] pps-gpio PRP0001:01: using ACPI for GPIO lookup
[   64.010251] acpi PRP0001:01: GPIO: looking up echo-gpios
[   64.010254] acpi PRP0001:01: GPIO: looking up echo-gpio
[   64.010257] pps-gpio PRP0001:01: using lookup tables for GPIO lookup
[   64.010260] pps-gpio PRP0001:01: No GPIO consumer echo found
[   64.010264] pps-gpio PRP0001:01: failed to map GPIO to IRQ: -6
[   64.016241] pps-gpio: probe of PRP0001:01 failed with error -22
[   64.016493] gpio gpiochip3: Persistence not supported for GPIO 1
[   64.017007] gpio-keys PRP0001:02: Unable to get irq number for GPIO 0, error -6
rgov
  • 3,516
  • 1
  • 31
  • 51
  • 1
    When level changes it means edge (raising or falling), that’s how you need to define an IRQ for it. According to your description it should be Edge, ActiveBoth (don’t remember keywords by heart, you may google or read in ACPI specification). – 0andriy Feb 14 '21 at 21:25
  • 1
    Also before doing that, try to play with `gpiodmon` from `libgpiod`. It will show you messages when you get edges. – 0andriy Feb 14 '21 at 21:29
  • Thanks @0andriy. The `failed to get GPIO from device tree` error suggests to me that it is not finding the right pin, so before fixing those parts of my `GpioInt`, it sounds like I need to fix the pin reference. Do you agree? Is using `GpioInt` enough to define the IRQ? – rgov Feb 15 '21 at 00:06
  • I also tried just setting up a button like [your example here](https://github.com/westeri/meta-acpi/blob/e4101864e05cdf100f3cdcd1e29843bfec4c2ba0/recipes-bsp/acpi-tables/samples/minnowboard/buttons.asl) and I get: `Unable to get irq number for GPIO 0` – rgov Feb 15 '21 at 16:54
  • 1
    Okay, so first about `pps-gpio`: that driver needs a bit of patching. About button, you have to provide proper device object references and GPIO pin numbers. Note, `gpio-keys-polled` driver does not use IRQs, so I can't say why you got that message you shouldn't. – 0andriy Feb 15 '21 at 19:49
  • 1
    Hmm... It looks like even non-patched driver should work in minimum setup. Can you enable DEBUG for GPIOLIB and see what `dmesg` shows (It would be nice you to updte the Q with that information)? – 0andriy Feb 15 '21 at 20:40
  • Ok, I enabled debug, tried a more recent kernel, updated my ASL example, and included some additional info... One other thing, do I want to specify `interrupts` and an `interrupt-parent` for this? Not sure what the parent should be. Happy to take this to linux-gpio list of it's a better place, will post answer here either way. – rgov Feb 16 '21 at 06:01
  • 1
    No, those are for Device Tree. But now I have noticed that your GPIO excerpt doesn’t define interrupt line. How did you connect it physically? In meta-ACPI project you may find the example with interrupt line connected to another GPIO controller. – 0andriy Feb 16 '21 at 07:21
  • 1
    https://github.com/westeri/meta-acpi/blob/master/recipes-bsp/acpi-tables/samples/edison/gpioexp.asli#L29 is an example with IRQ line to the upper IRQ chip (which in that case happens to be another GPIO controller). – 0andriy Feb 16 '21 at 13:13
  • 1
    Yeah, when there is no IRQ defined, the GPIO expander driver doesn't define an IRQ chip and hence GPIO library when you ask for IRQ line will fail with `-ENXIO` here: https://elixir.bootlin.com/linux/latest/source/drivers/gpio/gpiolib.c#L3082 because there is no `->to_irq()` callback defined. – 0andriy Feb 16 '21 at 13:49
  • 1
    Thanks for all the help, I just noticed the kernel patches as well. I determined the interrupt line on the PCA9535 is not connected and there is no way to achieve what I was hoping for. But this was a great way to learn a bunch about ACPI -- thanks! – rgov Feb 18 '21 at 19:01
  • 1
    You still may use lines in polled mode, but while it will be okay for buttons, it will defeat the whole idea of PPS (because you need a precise time stamp). Besides that the i2c is slow and since the communications are done in non-atomic way, you will have a deviation in the measurements anyway. – 0andriy Feb 18 '21 at 23:28
  • Thanks for posting this! I'm about to embark on something similar: I'm using PCEngines apu4d4 boards, which have a CPU-connected GPIO (not via an expander) already configured in ACPI as a gpio-keys-polled device, but I want to convert that GPIO to be a PPS input. This GPIO is able to generate interrupts as well, so I'm hopeful I can make this work. – Kevin P. Fleming Mar 31 '21 at 11:48

0 Answers0