Working with GPIO overlay to get button presses via gpio-keys on Radxa Zero

Hello all,

I’m hoping someone here might be able to point me in the right direction. I’ve been trying to attach a button to the GPIO header on my Radxa Zero, configured by an overlay that uses gpio-keys. I’m unable to get it working though. I’m interested in using an overlay so that I don’t have to have another program (MRAA program, python script, etc.) running and I want to trigger keyboard presses. Here are some details:

$ uname -a
Linux radxa-zero 5.10.69-13-amlogic-g104342c59952 #amlogic SMP PREEMPT Thu Sep 15 13:06:12 UTC 2022 aarch64 aarch64 aarch64 GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.5 LTS
Release:	20.04
Codename:	focal

I attached a button, with one end at pin 12 (GPIOX_9, “GPIO Number” 501) and the other at pin 14 (GND).

I downloaded the kernel source and I created this DTS file (arch/arm64/boot/dts/amlogic/overlay/meson-g12a-gpio-keyboard.dts), referencing various sources:

/dts-v1/;
/plugin/;

#include <dt-bindings/input/input.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/gpio/meson-g12a-gpio.h>

/ {
        compatible = "radxa,zero", "amlogic,g12a";

        fragment@0 {
                target-path = "/";
                __overlay__ {
                        gpio-keys {
                                compatible = "gpio-keys";
                                #address-cells = <1>;
                                #size-cells = <0>;
                                autorepeat;

                                5-key {
                                        label = "5";
                                        linux,code = <KEY_5>;
                                        gpios = <&gpio GPIOX_9 (GPIO_ACTIVE_LOW)>;
                                        gpio-key,wakeup;
                                        autorepeat;
                                };
                        };
                };
        };
};

Then ran the following commands:

make dtbs
sudo cp arch/arm64/boot/dts/amlogic/overlay/meson-g12a-gpio-keyboard.dtbo /boot/dtbs/$(uname -r)/amlogic/overlay/

Then I edited /boot/uEnv.txt to change the overlays line to: overlays=meson-g12a-gpio-keyboard

When I reboot, I can see that the overlay is being read in the boot messages, but the device is fully booted and I press the button, it does not trigger a keyboard press. I believe it has something to do with the GPIO pins not being configured correctly but something else might be happening. Below is what I’ve done to troubleshoot. I appreciate any help. Thanks in advance.

I’m guessing this indicates why nothing is happening, but I have so far been unable to find out any further information to help me.

$ dmesg | grep -i gpio-keys
[    1.782937] gpio-keys gpio-keys: Unable to get irq number for GPIO 0, error -6

It appears that the overlay is loaded and even seen by the kernel.

$ ls -al /proc/device-tree/gpio-keys/
total 0
-r--r--r--  1 root root  4 Apr 12 20:02 '#address-cells'
-r--r--r--  1 root root  4 Apr 12 20:02 '#size-cells'
drwxr-xr-x  3 root root  0 Apr 12 20:02  .
drwxr-xr-x 36 root root  0 Sep  8  2022  ..
drwxr-xr-x  2 root root  0 Apr 12 20:02  5-key
-r--r--r--  1 root root  0 Apr 12 20:02  autorepeat
-r--r--r--  1 root root 10 Apr 12 20:02  compatible
-r--r--r--  1 root root 10 Apr 12 20:02  name

$ dtc -I fs -O dts /sys/firmware/devicetree/base | less; # Then search for "gpio-keys"
        gpio-keys {
                #address-cells = <0x01>;
                autorepeat;
                #size-cells = <0x00>;
                compatible = "gpio-keys";

                5-key {
                        autorepeat;
                        label = [35 00];
                        gpio-key,wakeup;
                        linux,code = <0x06>;
                        gpios = <0x30 0x4a 0x01>;
                };
        };

It looks like gpio-keys is active within the kernel. gpio-keys-polled is not, which is why I haven’t tried to use that. I don’t want to have to recompile the kernel or have something break when the device is updated.

$ zcat /proc/config.gz | grep KEYBOARD_GPIO
CONFIG_KEYBOARD_GPIO=y
# CONFIG_KEYBOARD_GPIO_POLLED is not set

Other places I’ve checked seem to indicate that the pin has not been set correctly:

$ sudo cat /sys/kernel/debug/gpio
gpiochip1: GPIOs 412-426, parent: platform/ff800000.sys-ctrl:pinctrl@14, aobus-banks:
 gpio-420 (                    |radxa-zero:green    ) out lo 

gpiochip0: GPIOs 427-511, parent: platform/ff634400.bus:pinctrl@40, periphs-banks:
 gpio-451 (                    |regulator-vcc_5v    ) out lo ACTIVE LOW
 gpio-464 (                    |reset               ) out hi ACTIVE LOW
 gpio-474 (                    |cd                  ) in  lo ACTIVE LOW
 gpio-475 (                    |sysfs               ) out lo 
 gpio-498 (                    |reset               ) out hi ACTIVE LOW
 gpio-501 (                    |sysfs               ) out lo 
 gpio-509 (                    |shutdown            ) out hi 

$ sudo cat  /sys/kernel/debug/pinctrl/ff634400.bus:pinctrl@40-pinctrl-meson/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (GPIOZ_0): input bias pull up, output drive strength (500 uA)
pin 1 (GPIOZ_1): input bias pull up, output drive strength (500 uA)
pin 2 (GPIOZ_2): input bias pull up, output drive strength (500 uA)
pin 3 (GPIOZ_3): input bias pull up, output drive strength (500 uA)
pin 4 (GPIOZ_4): input bias pull up, output drive strength (500 uA)
pin 5 (GPIOZ_5): input bias pull up, output drive strength (500 uA)
pin 6 (GPIOZ_6): input bias pull up, output drive strength (500 uA)
pin 7 (GPIOZ_7): input bias pull up, output drive strength (500 uA)
pin 8 (GPIOZ_8): input bias pull up, output drive strength (500 uA)
pin 9 (GPIOZ_9): input bias pull down, output drive strength (500 uA)
pin 10 (GPIOZ_10): input bias pull down, output drive strength (500 uA)
pin 11 (GPIOZ_11): input bias pull down, output drive strength (500 uA)
pin 12 (GPIOZ_12): input bias pull down, output drive strength (500 uA)
pin 13 (GPIOZ_13): input bias pull down, output drive strength (500 uA)
pin 14 (GPIOZ_14): input bias disabled, output drive strength (500 uA)
pin 15 (GPIOZ_15): input bias disabled, output drive strength (500 uA)
pin 16 (GPIOH_0): input bias disabled, output drive strength (4000 uA)
pin 17 (GPIOH_1): input bias disabled, output drive strength (4000 uA)
pin 18 (GPIOH_2): input bias disabled, output drive strength (500 uA)
pin 19 (GPIOH_3): input bias pull up, output drive strength (500 uA)
pin 20 (GPIOH_4): input bias pull down, output drive strength (500 uA)
pin 21 (GPIOH_5): input bias pull down, output drive strength (500 uA)
pin 22 (GPIOH_6): input bias pull down, output drive strength (500 uA)
pin 23 (GPIOH_7): input bias pull down, output drive strength (500 uA)
pin 24 (GPIOH_8): input bias pull up, output drive strength (500 uA), output enabled, pin output (0 level)
pin 25 (BOOT_0): input bias pull up, output drive strength (4000 uA)
pin 26 (BOOT_1): input bias pull up, output drive strength (4000 uA)
pin 27 (BOOT_2): input bias pull up, output drive strength (4000 uA)
pin 28 (BOOT_3): input bias pull up, output drive strength (4000 uA)
pin 29 (BOOT_4): input bias pull up, output drive strength (4000 uA)
pin 30 (BOOT_5): input bias pull up, output drive strength (4000 uA)
pin 31 (BOOT_6): input bias pull up, output drive strength (4000 uA)
pin 32 (BOOT_7): input bias pull up, output drive strength (4000 uA)
pin 33 (BOOT_8): input bias pull down, output drive strength (4000 uA)
pin 34 (BOOT_9): input bias pull down, output drive strength (4000 uA)
pin 35 (BOOT_10): input bias pull up, output drive strength (4000 uA)
pin 36 (BOOT_11): input bias pull up, output drive strength (4000 uA)
pin 37 (BOOT_12): input bias pull up, output drive strength (4000 uA), output enabled, pin output (1 level)
pin 38 (BOOT_13): input bias pull down, output drive strength (4000 uA)
pin 39 (BOOT_14): input bias pull up, output drive strength (4000 uA)
pin 40 (BOOT_15): input bias pull up, output drive strength (4000 uA)
pin 41 (GPIOC_0): input bias pull up, output drive strength (4000 uA)
pin 42 (GPIOC_1): input bias pull up, output drive strength (4000 uA)
pin 43 (GPIOC_2): input bias pull up, output drive strength (4000 uA)
pin 44 (GPIOC_3): input bias pull up, output drive strength (4000 uA)
pin 45 (GPIOC_4): input bias disabled, output drive strength (4000 uA)
pin 46 (GPIOC_5): input bias pull up, output drive strength (4000 uA)
pin 47 (GPIOC_6): input bias pull up, output drive strength (4000 uA)
pin 48 (GPIOC_7): input bias pull up, output drive strength (4000 uA), output enabled, pin output (0 level)
pin 49 (GPIOA_0): input bias pull down, output drive strength (500 uA)
pin 50 (GPIOA_1): input bias pull down, output drive strength (500 uA)
pin 51 (GPIOA_2): input bias pull down, output drive strength (500 uA)
pin 52 (GPIOA_3): input bias pull down, output drive strength (500 uA)
pin 53 (GPIOA_4): input bias pull down, output drive strength (500 uA)
pin 54 (GPIOA_5): input bias pull down, output drive strength (500 uA)
pin 55 (GPIOA_6): input bias pull down, output drive strength (500 uA)
pin 56 (GPIOA_7): input bias pull down, output drive strength (500 uA)
pin 57 (GPIOA_8): input bias pull down, output drive strength (500 uA)
pin 58 (GPIOA_9): input bias pull down, output drive strength (500 uA)
pin 59 (GPIOA_10): input bias pull down, output drive strength (500 uA)
pin 60 (GPIOA_11): input bias pull down, output drive strength (500 uA)
pin 61 (GPIOA_12): input bias pull down, output drive strength (500 uA)
pin 62 (GPIOA_13): input bias pull down, output drive strength (500 uA)
pin 63 (GPIOA_14): input bias disabled, output drive strength (3000 uA)
pin 64 (GPIOA_15): input bias disabled, output drive strength (3000 uA)
pin 65 (GPIOX_0): input bias disabled, output drive strength (4000 uA)
pin 66 (GPIOX_1): input bias disabled, output drive strength (4000 uA)
pin 67 (GPIOX_2): input bias disabled, output drive strength (4000 uA)
pin 68 (GPIOX_3): input bias disabled, output drive strength (4000 uA)
pin 69 (GPIOX_4): input bias disabled, output drive strength (4000 uA)
pin 70 (GPIOX_5): input bias disabled, output drive strength (4000 uA)
pin 71 (GPIOX_6): input bias pull down, output drive strength (500 uA), output enabled, pin output (1 level)
pin 72 (GPIOX_7): input bias pull up, output drive strength (500 uA)
pin 73 (GPIOX_8): input bias pull up, output drive strength (500 uA)
pin 74 (GPIOX_9): input bias pull up, output drive strength (500 uA), output enabled, pin output (0 level)
pin 75 (GPIOX_10): input bias pull up, output drive strength (500 uA)
pin 76 (GPIOX_11): input bias pull up, output drive strength (500 uA)
pin 77 (GPIOX_12): input bias disabled, output drive strength (500 uA)
pin 78 (GPIOX_13): input bias disabled, output drive strength (500 uA)
pin 79 (GPIOX_14): input bias disabled, output drive strength (500 uA)
pin 80 (GPIOX_15): input bias disabled, output drive strength (500 uA)
pin 81 (GPIOX_16): input bias disabled, output drive strength (500 uA)
pin 82 (GPIOX_17): input bias pull down, output drive strength (500 uA), output enabled, pin output (1 level)
pin 83 (GPIOX_18): input bias pull up, output drive strength (500 uA)
pin 84 (GPIOX_19): input bias disabled, output drive strength (500 uA)

$ sudo gpioinfo
gpiochip0 - 85 lines:
	line   0:      unnamed       unused   input  active-high 
	line   1:      unnamed       unused   input  active-high 
	line   2:      unnamed       unused   input  active-high 
	line   3:      unnamed       unused   input  active-high 
	line   4:      unnamed       unused   input  active-high 
	line   5:      unnamed       unused   input  active-high 
	line   6:      unnamed       unused   input  active-high 
	line   7:      unnamed       unused   input  active-high 
	line   8:      unnamed       unused   input  active-high 
	line   9:      unnamed       unused   input  active-high 
	line  10:      unnamed       unused   input  active-high 
	line  11:      unnamed       unused   input  active-high 
	line  12:      unnamed       unused   input  active-high 
	line  13:      unnamed       unused   input  active-high 
	line  14:      unnamed       unused   input  active-high 
	line  15:      unnamed       unused   input  active-high 
	line  16:      unnamed       unused   input  active-high 
	line  17:      unnamed       unused   input  active-high 
	line  18:      unnamed       unused   input  active-high 
	line  19:      unnamed       unused   input  active-high 
	line  20:      unnamed       unused   input  active-high 
	line  21:      unnamed       unused   input  active-high 
	line  22:      unnamed       unused   input  active-high 
	line  23:      unnamed       unused   input  active-high 
	line  24:      unnamed "regulator-vcc_5v" output active-low [used open-drain]
	line  25:      unnamed       unused   input  active-high 
	line  26:      unnamed       unused   input  active-high 
	line  27:      unnamed       unused   input  active-high 
	line  28:      unnamed       unused   input  active-high 
	line  29:      unnamed       unused   input  active-high 
	line  30:      unnamed       unused   input  active-high 
	line  31:      unnamed       unused   input  active-high 
	line  32:      unnamed       unused   input  active-high 
	line  33:      unnamed       unused   input  active-high 
	line  34:      unnamed       unused   input  active-high 
	line  35:      unnamed       unused   input  active-high 
	line  36:      unnamed       unused   input  active-high 
	line  37:      unnamed      "reset"  output   active-low [used]
	line  38:      unnamed       unused   input  active-high 
	line  39:      unnamed       unused   input  active-high 
	line  40:      unnamed       unused   input  active-high 
	line  41:      unnamed       unused   input  active-high 
	line  42:      unnamed       unused   input  active-high 
	line  43:      unnamed       unused   input  active-high 
	line  44:      unnamed       unused   input  active-high 
	line  45:      unnamed       unused   input  active-high 
	line  46:      unnamed       unused   input  active-high 
	line  47:      unnamed         "cd"   input   active-low [used]
	line  48:      unnamed      "sysfs"  output  active-high [used]
	line  49:      unnamed       unused   input  active-high 
	line  50:      unnamed       unused   input  active-high 
	line  51:      unnamed       unused   input  active-high 
	line  52:      unnamed       unused   input  active-high 
	line  53:      unnamed       unused   input  active-high 
	line  54:      unnamed       unused   input  active-high 
	line  55:      unnamed       unused   input  active-high 
	line  56:      unnamed       unused   input  active-high 
	line  57:      unnamed       unused   input  active-high 
	line  58:      unnamed       unused   input  active-high 
	line  59:      unnamed       unused   input  active-high 
	line  60:      unnamed       unused   input  active-high 
	line  61:      unnamed       unused   input  active-high 
	line  62:      unnamed       unused   input  active-high 
	line  63:      unnamed       unused   input  active-high 
	line  64:      unnamed       unused   input  active-high 
	line  65:      unnamed       unused   input  active-high 
	line  66:      unnamed       unused   input  active-high 
	line  67:      unnamed       unused   input  active-high 
	line  68:      unnamed       unused   input  active-high 
	line  69:      unnamed       unused   input  active-high 
	line  70:      unnamed       unused   input  active-high 
	line  71:      unnamed      "reset"  output   active-low [used]
	line  72:      unnamed       unused   input  active-high 
	line  73:      unnamed       unused   input  active-high 
	line  74:      unnamed      "sysfs"  output  active-high [used]
	line  75:      unnamed       unused   input  active-high 
	line  76:      unnamed       unused   input  active-high 
	line  77:      unnamed       unused   input  active-high 
	line  78:      unnamed       unused   input  active-high 
	line  79:      unnamed       unused   input  active-high 
	line  80:      unnamed       unused   input  active-high 
	line  81:      unnamed       unused   input  active-high 
	line  82:      unnamed   "shutdown"  output  active-high [used]
	line  83:      unnamed       unused   input  active-high 
	line  84:      unnamed       unused   input  active-high 
gpiochip1 - 15 lines:
	line   0:      unnamed       unused   input  active-high 
	line   1:      unnamed       unused   input  active-high 
	line   2:      unnamed       unused   input  active-high 
	line   3:      unnamed       unused   input  active-high 
	line   4:      unnamed       unused   input  active-high 
	line   5:      unnamed       unused   input  active-high 
	line   6:      unnamed       unused   input  active-high 
	line   7:      unnamed       unused   input  active-high 
	line   8:      unnamed "radxa-zero:green" output active-high [used]
	line   9:      unnamed       unused   input  active-high 
	line  10:      unnamed       unused   input  active-high 
	line  11:      unnamed       unused   input  active-high 
	line  12:      unnamed       unused   input  active-high 
	line  13:      unnamed       unused   input  active-high 
	line  14:      unnamed       unused   input  active-high 
 
$ mraa-gpio list
01         3V3: 
02          5V: 
03 I2C_EE_M3_S: GPIO I2C  
04          5V: 
05 I2C_EE_M3_S: GPIO I2C  
06         GND: 
07 I2C_AO_M0_S: GPIO I2C  UART 
08 UART_AO_A_T: GPIO UART 
09         GND: 
10 UART_AO_A_R: GPIO UART 
11 I2C_AO_M0_S: GPIO I2C  UART 
12  SPI_A_MISO: GPIO SPI  
13 I2C_EE_M1_S: GPIO I2C  SPI  
14         GND: 
15  SARADC_CH1: 
16 I2C_EE_M1_S: GPIO I2C  SPI  
17         3V3: 
18 SPI_A_MOSI,: GPIO SPI  PWM  
19  SPI_B_MOSI: GPIO SPI  
20         GND: 
21  SPI_B_MISO: GPIO SPI  
22     GPIOC_7: GPIO 
23 I2C_EE_M1_S: GPIO I2C  SPI  UART 
24 UART_EE_C_R: GPIO I2C  SPI  UART 
25         GND: 
26  SARADC_CH2: 
27 I2C_AO_M0_S: GPIO I2C  UART 
28 I2C_AO_M0_S: GPIO I2C  UART 
29          NC: 
30         GND: 
31          NC: 
32    GPIOAO_4: GPIO 
33          NC: 
34         GND: 
35 UART_AO_B_T: GPIO UART 
36     GPIOH_8: GPIO 
37 UART_AO_B_R: GPIO UART 
38   GPIOAO_10: GPIO 
39         GND: 
40     PWMAO_A: GPIO PWM  

lsinput shows no information (unless a USB keyboard is plugged in)

gpiofind returns no information, so I can’t run gpiomon

$ sudo gpiofind PIN_12
$ sudo gpiomon -f $(sudo gpiofind PIN_12)
gpiomon: gpiochip must be specified

Thanks again,

- George

Unfortunately, the last word we have is that gpio-keys can’t be used on this hardware. You have to use gpio-keys-polled: Zero interrupts

Thanks @theophile. I had actually seen your work on this, which inspired me to go down this path.

Anyway, the lack of interrupt support on the GPIOs explains the IRQ message that I see in dmesg. Unfortunately it looks like I need to compile this module. I’d like to do that without having to redo the entire kernel. Doubly unfortunate that I can’t seem to find the precise source code for the kernel version that I’m running (5.10.69-13-amlogic-g104342c59952). Any help you could provide would be awesome.

So far, I’ve done the following:

vi .config; # Changed line for CONFIG_KEYBOARD_GPIO_POLLED to CONFIG_KEYBOARD_GPIO_POLLED=m
make prepare
make modules_prepare
make M=drivers/input/keyboard
sudo cp drivers/input/keyboard/gpio_keys_polled.ko /lib/modules/5.10.69-13-amlogic-g104342c59952/kernel/drivers/staging/
sudo depmod -a
sudo modprobe gpio_keys_polled

The modprobe command results in the following error:

modprobe: ERROR: could not insert 'gpio_keys_polled': Exec format error

This indicates that I don’t have the appropriate kernel version source. I’m looking through and have only found the following versions:

  • linux-5.10.y-radxa-zero
  • 5.10.69-9-amlogic
  • 5.10.69-8-amlogic
  • 5.10.69-7-amlogic
  • 5.10.69-6-amlogic
  • 5.10.69-5-amlogic
  • 5.10.69-4-amlogic
  • 5.10.69-3-amlogic
  • 5.10.69-2-amlogic
  • 5.10.69-1-amlogic

Is there somewhere else I should be looking?

Thanks again,

- George

Just saw this message in dmesg as well, when trying to load the module. Posting for future reference.

[66482.427635] gpio_keys_polled: version magic '5.10.69+ SMP preempt mod_unload aarch64' should be '5.10.69-13-amlogic-g104342c59952 SMP preempt mod_unload aarch64'

I’ve not tried Ubuntu on this board, but do the standard Ubuntu kernel instructions not work? https://wiki.ubuntu.com/Kernel/BuildYourOwnKernel

If you can’t find the source tree for the current kernel version, you may just have to rebuild the whole kernel. Alternatively, you could use a distro with a more accessible kernel source tree. I’ve had good experiences with Armbian and DietPi (which uses the Armbian kernel) in this regard.

Thanks @theophile, I’m really hoping to not to have to rebuild the kernel and reinstall it. I’ll put in a feature request to add gpio-keys-polled as a module (or compiled into the kernel) by default. Especially since the board does not support interrupts on the GPIO, it makes sense to have this feature as an alternative to gpio-keys.

In the meantime, as a hack to make sure I’m on the right track, I’ve edited the version number in the following files to match the output of uname -r:

  • include/config/kernel.release
  • include/generated/utsrelease.h

After I recompiled the module I can now install it with modprobe. After tweaking my overlay for gpio-keys-polled and rebooting, I’m now getting keystrokes from by button pushes and can see a new entry in lsinput. Sweet!

This is not a long term solution, of course. I get the following messages in dmesg:

  • [ 8.297603] gpio_keys_polled: loading out-of-tree module taints kernel.
  • [ 8.297669] gpio_keys_polled: module verification failed: signature and/or required key missing - tainting kernel

Those will be fun to sort out. At least I’m able to see things working and move on with my experimentation. I’ll also work on that feature request.

Have you seen any adverse side effects from using gpio-keys-polled? Performance issues? Latency on keypresses? Garbage characters? Booting issues?

Thanks again,

  • George

Nice! I’ve done something like that once or twice myself. If this is a project just for personal use, you can ignore that kernel taint message and probably consider this a long term solution.

So far I haven’t had any noticeable issues or side effects using gpio-keys-polled. My project is battery powered and has a physical power switch. When I turn the switch off, it acts like a button press of the power key and the software triggers a system shutdown. After the shutdown is complete the gpio-poweroff module signals my BMS that it’s now safe to remove power. I want the system shutdown to trigger immediately when I flip the power switch, regardless of the system load. I was hesitant to rely on polling for this but so far it has worked fine.

Thanks! Your project is pretty cool and I may have to take another look at it again if I decide to add a soft shutdown feature. My main goal was to add arcade buttons/joystick. Hopefully I’ll be able to play with the polling interval and debounce parameters to optimize performance.

Thanks again for all your input and feedback!

- George

Certainly. My first version of the project was built on the RP0 and I used gpio-keys for all the inputs like that. It mostly worked but I was constantly fighting with certain buttons inexplicably not working even though the wiring was solid. I never did track down the problem but I remember reading others having similar problems.

When I reworked the project around the Radxa Zero I decided to use an Arduino Micro for the inputs and the Radxa Zero just sees it as a USB controller. Best decision I ever made. I have dual analog thumbsticks and I ended up doing the firmware for the Arduino to support custom acceleration profiles for the analog sticks. Totally unnecessary for what it is, but still worth it for the experience. And it’s rock solid reliable.

Interesting. I’m finding that I’m unable to get certain pins to trigger key presses even though everything seems to be connected properly. I’ve taken a look at your device tree overlay and see that it uses many of the same pins that I’m trying to use. In fact, I can’t even use pins 35/36 (GPIOAO_8/GPIOH_8) because they seem to be used by radxa-zero:green & regulator-vcc_5v, respectively.

It seems that the pins are associated properly, but for many of the pins no key-presses are triggered. Only 5 of my pins trigger key-presses. Here’s what I’ve done to troubleshoot:

$ sudo cat /sys/kernel/debug/gpio
gpiochip1: GPIOs 412-426, parent: platform/ff800000.sys-ctrl:pinctrl@14, aobus-banks:
 gpio-412 (                    |Y                   ) in  lo ACTIVE LOW
 gpio-413 (                    |Z                   ) in  lo ACTIVE LOW
 gpio-414 (                    |DOWN                ) in  lo ACTIVE LOW
 gpio-415 (                    |UP                  ) in  hi ACTIVE LOW
 gpio-416 (                    |B                   ) in  lo ACTIVE LOW
 gpio-420 (                    |radxa-zero:green    ) out lo 
 gpio-421 (                    |L                   ) in  lo ACTIVE LOW
 gpio-423 (                    |M                   ) in  lo ACTIVE LOW

gpiochip0: GPIOs 427-511, parent: platform/ff634400.bus:pinctrl@40, periphs-banks:
 gpio-447 (                    |RIGHT               ) in  lo ACTIVE LOW
 gpio-448 (                    |I                   ) in  lo ACTIVE LOW
 gpio-449 (                    |V                   ) in  lo ACTIVE LOW
 gpio-450 (                    |K                   ) in  lo ACTIVE LOW
 gpio-451 (                    |regulator-vcc_5v    ) out lo ACTIVE LOW
 gpio-464 (                    |reset               ) out hi ACTIVE LOW
 gpio-474 (                    |cd                  ) in  lo ACTIVE LOW
 gpio-475 (                    |C                   ) in  lo ACTIVE LOW
 gpio-498 (                    |reset               ) out hi ACTIVE LOW
 gpio-500 (                    |X                   ) in  hi ACTIVE LOW
 gpio-501 (                    |2                   ) in  hi ACTIVE LOW
 gpio-502 (                    |ENTER               ) in  hi ACTIVE LOW
 gpio-503 (                    |LEFT                ) in  hi ACTIVE LOW
 gpio-509 (                    |shutdown            ) out hi 

I saw reviewed your overlay source files and it doesn’t seem to be doing anything different from what I have. My overlays are loaded via uEnv.txt vs. extlinux.conf, but I’m following the Radxa Zero documentation there. Is there something else I’m missing?

I’ve used the Pimoroni Picade (Arduino Leonardo/ATmega32U4 based) successfully, but I was hoping to be able to reduce the number of additional pieces.

Thanks for your help.

- George

Ahh, I see. I created that overlay trying to help a user on Discord. I haven’t tested it myself. He also had problems with certain keys not working although he had a lot of other issues too so I was never certain whether the problem was with the overly or something else.

I think I may have a lead on why most of the buttons are not working. Doing the following commands:

$ sudo cat  /sys/kernel/debug/pinctrl/ff800000.sys-ctrl:pinctrl@14-pinctrl-meson/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (GPIOAO_0): input bias disabled, output drive strength (500 uA)
pin 1 (GPIOAO_1): input bias disabled, output drive strength (500 uA)
pin 2 (GPIOAO_2): input bias pull down, output drive strength (500 uA)
pin 3 (GPIOAO_3): input bias pull up, output drive strength (500 uA)
pin 4 (GPIOAO_4): input bias pull down, output drive strength (500 uA)
pin 5 (GPIOAO_5): input bias pull up, output drive strength (500 uA)
pin 6 (GPIOAO_6): input bias pull down, output drive strength (500 uA)
pin 7 (GPIOAO_7): input bias pull up, output drive strength (500 uA)
pin 8 (GPIOAO_8): input bias pull up, output drive strength (500 uA), output enabled, pin output (0 level)
pin 9 (GPIOAO_9): input bias pull down, output drive strength (500 uA)
pin 10 (GPIOAO_10): input bias pull up, output drive strength (500 uA)
pin 11 (GPIOAO_11): input bias pull down, output drive strength (500 uA)
pin 12 (GPIOE_0): input bias disabled, output drive strength (500 uA)
pin 13 (GPIOE_1): input bias disabled, output drive strength (500 uA)
pin 14 (GPIOE_2): input bias pull down, output drive strength (500 uA)


$ sudo cat  /sys/kernel/debug/pinctrl/ff634400.bus:pinctrl@40-pinctrl-meson/pinconf-pins
Pin config settings per pin
Format: pin (name): configs
pin 0 (GPIOZ_0): input bias pull up, output drive strength (500 uA)
pin 1 (GPIOZ_1): input bias pull up, output drive strength (500 uA)
pin 2 (GPIOZ_2): input bias pull up, output drive strength (500 uA)
pin 3 (GPIOZ_3): input bias pull up, output drive strength (500 uA)
pin 4 (GPIOZ_4): input bias pull up, output drive strength (500 uA)
pin 5 (GPIOZ_5): input bias pull up, output drive strength (500 uA)
pin 6 (GPIOZ_6): input bias pull up, output drive strength (500 uA)
pin 7 (GPIOZ_7): input bias pull up, output drive strength (500 uA)
pin 8 (GPIOZ_8): input bias pull up, output drive strength (500 uA)
pin 9 (GPIOZ_9): input bias pull down, output drive strength (500 uA)
pin 10 (GPIOZ_10): input bias pull down, output drive strength (500 uA)
pin 11 (GPIOZ_11): input bias pull down, output drive strength (500 uA)
pin 12 (GPIOZ_12): input bias pull down, output drive strength (500 uA)
pin 13 (GPIOZ_13): input bias pull down, output drive strength (500 uA)
pin 14 (GPIOZ_14): input bias disabled, output drive strength (500 uA)
pin 15 (GPIOZ_15): input bias disabled, output drive strength (500 uA)
pin 16 (GPIOH_0): input bias disabled, output drive strength (4000 uA)
pin 17 (GPIOH_1): input bias disabled, output drive strength (4000 uA)
pin 18 (GPIOH_2): input bias disabled, output drive strength (500 uA)
pin 19 (GPIOH_3): input bias pull up, output drive strength (500 uA)
pin 20 (GPIOH_4): input bias pull down, output drive strength (500 uA)
pin 21 (GPIOH_5): input bias pull down, output drive strength (500 uA)
pin 22 (GPIOH_6): input bias pull down, output drive strength (500 uA)
pin 23 (GPIOH_7): input bias pull down, output drive strength (500 uA)
pin 24 (GPIOH_8): input bias pull up, output drive strength (500 uA), output enabled, pin output (0 level)
pin 25 (BOOT_0): input bias pull up, output drive strength (4000 uA)
pin 26 (BOOT_1): input bias pull up, output drive strength (4000 uA)
pin 27 (BOOT_2): input bias pull up, output drive strength (4000 uA)
pin 28 (BOOT_3): input bias pull up, output drive strength (4000 uA)
pin 29 (BOOT_4): input bias pull up, output drive strength (4000 uA)
pin 30 (BOOT_5): input bias pull up, output drive strength (4000 uA)
pin 31 (BOOT_6): input bias pull up, output drive strength (4000 uA)
pin 32 (BOOT_7): input bias pull up, output drive strength (4000 uA)
pin 33 (BOOT_8): input bias pull down, output drive strength (4000 uA)
pin 34 (BOOT_9): input bias pull down, output drive strength (4000 uA)
pin 35 (BOOT_10): input bias pull up, output drive strength (4000 uA)
pin 36 (BOOT_11): input bias pull up, output drive strength (4000 uA)
pin 37 (BOOT_12): input bias pull up, output drive strength (4000 uA), output enabled, pin output (1 level)
pin 38 (BOOT_13): input bias pull down, output drive strength (4000 uA)
pin 39 (BOOT_14): input bias pull up, output drive strength (4000 uA)
pin 40 (BOOT_15): input bias pull up, output drive strength (4000 uA)
pin 41 (GPIOC_0): input bias pull up, output drive strength (4000 uA)
pin 42 (GPIOC_1): input bias pull up, output drive strength (4000 uA)
pin 43 (GPIOC_2): input bias pull up, output drive strength (4000 uA)
pin 44 (GPIOC_3): input bias pull up, output drive strength (4000 uA)
pin 45 (GPIOC_4): input bias disabled, output drive strength (4000 uA)
pin 46 (GPIOC_5): input bias pull up, output drive strength (4000 uA)
pin 47 (GPIOC_6): input bias pull up, output drive strength (4000 uA)
pin 48 (GPIOC_7): input bias pull up, output drive strength (4000 uA)
pin 49 (GPIOA_0): input bias pull down, output drive strength (500 uA)
pin 50 (GPIOA_1): input bias pull down, output drive strength (500 uA)
pin 51 (GPIOA_2): input bias pull down, output drive strength (500 uA)
pin 52 (GPIOA_3): input bias pull down, output drive strength (500 uA)
pin 53 (GPIOA_4): input bias pull down, output drive strength (500 uA)
pin 54 (GPIOA_5): input bias pull down, output drive strength (500 uA)
pin 55 (GPIOA_6): input bias pull down, output drive strength (500 uA)
pin 56 (GPIOA_7): input bias pull down, output drive strength (500 uA)
pin 57 (GPIOA_8): input bias pull down, output drive strength (500 uA)
pin 58 (GPIOA_9): input bias pull down, output drive strength (500 uA)
pin 59 (GPIOA_10): input bias pull down, output drive strength (500 uA)
pin 60 (GPIOA_11): input bias pull down, output drive strength (500 uA)
pin 61 (GPIOA_12): input bias pull down, output drive strength (500 uA)
pin 62 (GPIOA_13): input bias pull down, output drive strength (500 uA)
pin 63 (GPIOA_14): input bias disabled, output drive strength (3000 uA)
pin 64 (GPIOA_15): input bias disabled, output drive strength (3000 uA)
pin 65 (GPIOX_0): input bias disabled, output drive strength (4000 uA)
pin 66 (GPIOX_1): input bias disabled, output drive strength (4000 uA)
pin 67 (GPIOX_2): input bias disabled, output drive strength (4000 uA)
pin 68 (GPIOX_3): input bias disabled, output drive strength (4000 uA)
pin 69 (GPIOX_4): input bias disabled, output drive strength (4000 uA)
pin 70 (GPIOX_5): input bias disabled, output drive strength (4000 uA)
pin 71 (GPIOX_6): input bias pull down, output drive strength (500 uA), output enabled, pin output (1 level)
pin 72 (GPIOX_7): input bias pull up, output drive strength (500 uA)
pin 73 (GPIOX_8): input bias pull up, output drive strength (500 uA)
pin 74 (GPIOX_9): input bias pull up, output drive strength (500 uA)
pin 75 (GPIOX_10): input bias pull up, output drive strength (500 uA)
pin 76 (GPIOX_11): input bias pull up, output drive strength (500 uA)
pin 77 (GPIOX_12): input bias disabled, output drive strength (500 uA)
pin 78 (GPIOX_13): input bias disabled, output drive strength (500 uA)
pin 79 (GPIOX_14): input bias disabled, output drive strength (500 uA)
pin 80 (GPIOX_15): input bias disabled, output drive strength (500 uA)
pin 81 (GPIOX_16): input bias disabled, output drive strength (500 uA)
pin 82 (GPIOX_17): input bias pull down, output drive strength (500 uA), output enabled, pin output (1 level)
pin 83 (GPIOX_18): input bias pull up, output drive strength (500 uA)
pin 84 (GPIOX_19): input bias disabled, output drive strength (500 uA)

The buttons that work show input bias pull up, output drive strength (500 uA) while most of the others that don’t work show input bias pull down, output drive strength (500 uA).

Looks like I either need to try to change the default bias on the non-working pins, or something else. Any ideas?

Thanks again,

- George

I should have mentioned that some GPIOs are active high and some are active low. The SoC datasheet has a table identifying which ones are which. The ones that are pulled high are activated by grounding the pin. The ones that are pulled low are activated by applying 3.3v to the pin.

Thanks @theophile that makes sense. So there’s no way to set the low pins as high pins and vice versa through the overlay? I decompiled the device tree using dtc -I fs -O dts /sys/firmware/devicetree/base and saw lines like bias-pull-up;. I tried adding lines like that in areas of my overlay but that had no effect.

Couple of other questions:

  • When connecting to a a pin to 3.3v are you using a resistor? What kind? I’ve seen this for other boards and want to make sure before I go destroy mine.
  • I looked at using GPIOC_7 (Pin 22 on the board, GPIO #475, pin 0,45) but can’t get that to work even though it says input bias pull up like my other working pins. The big difference is that it says output drive strength (4000 uA) vs. the 500 uA that the others say. But I’ve seen that changing the drive strength via drive-strength-microamp might not be possible either. Any thoughts on that?

Using an Arduino is looking better all the time now.

Thanks again,

- George

Here’s the SoC datasheet. You’re looking at section 3.2, Table 2, starting on page 13. It lists all the GPIO pins, their type, and their default state. A key to the abbreviations is on page 18.

Looking at that table, you’ll see that GPIOC_7 is actually one of the handful of open-drain output pins, and it’s 3.3V tolerant. On page 28 there’s another table indicating that to use this pin as an input, you would apply a voltage of +1.5 to +3.6 to get it to trigger high, and a voltage of -0.3 to 0.8 to get it to trigger low.

For other DIO-type pins, the SoC has internal pull-up or pull-down resistors (as the case may be). The table on page 28 suggests that the internal resistors are 60k ohm (though that’s partially obscured by the watermark). Being perfectly candid, when I was developing my project, my recollection is that the pinctrl bias settings (like you have above) weren’t always a foolproof indicator of what you have to do to trigger the GPIO pin. For me, Table 2 in the SoC datasheet was more helpful. So if Table 2 says a DIO-type pin is default pull UP, that means you would ground that pin to trigger it. If the pin is default pull DOWN, you would apply +3.3V to trigger it.

I am not certain but it may be possible to change this default behavior using the device tree, but if so, you would also have to compensate for the default pull up/down resistors, otherwise the pin would register as triggered all the time except when the button is pressed. Assuming the internal resistors are indeed 60k ohm, you could pull the pin the other direction using a lower resistor value, such as 10k ohm for example.

If you can though, it would probably be easier to use the pins as set up by default and just plan around the type of signal you need to trigger each pin. For example, if laying out the D-pad traces, you could plan to use 4 default pull-DOWN pins for the 4 directions, then use a single +3.3V supply trace to feed one leg of each of the 4 switches, then connect the other leg of each button to the appropriate GPIO pins.

Before you do that though, test each pin to make sure it triggers inputs as expected. Like I said in an earlier post, there may be some GPIO pins that don’t behave as expected in this regard. I don’t recall specifics but I think there were 1 or 2 that wouldn’t trigger for me when I thought they should. When that happened, I just used a different pin rather that try to figure out why the other one wasn’t behaving as expected.

When the GPIO pins are sinking current you don’t need a current-limiting resistor to protect the SoC in most cases because the chip won’t draw more current than it needs.

Wow. Thank you! You are a wealth of information. I thought that the datasheet wasn’t available, but perhaps I was mistaken.

So by taking your advice and using pins according to their default high/low state, I set up buttons to connect to ground or 3.3v and changed my overlay to GPIO_ACTIVE_LOW / GPIO_ACTIVE_HIGH accordingly. I have 15 pins working now, which is great!

I still haven’t gotten GPIOC_7 to trigger anything, but I’ve got a few more combinations to try.

Other notes:

  • GPIOAO_8 (pin 35, GPIO 420, line 1,8) and GPIOH_8 (pin 36, GPIO 451, line 0,24) are set to output enabled, so unless that can be changed in the overlay or device tree, I’ll have to avoid using them.
  • Pin 27 is a duplicate of pin 7 so can’t be used separately.
  • Pin 28 is a duplicate of pin 11 so also can’t be used separately.
  • GPIOAO_0 / GPIOAO_1 say input bias disabled, output drive strength (500 uA) but the documentation you provided state they have a bias of UP. Strangely, I am able to use them with GPIO_ACTIVE_HIGH. Not sure what to make of that, as it seems that both the system and the documentation are wrong.
  • The Radxa Zero GPIO documentation says that GPIOA_14 (pin 3, GPIO 490, line 0,63) and GPIOA_15 (pin 5, GPIO 491, line 0,64) are connected to pull up resistor and USB, and sudo cat /sys/kernel/debug/pinctrl/ff634400.bus:pinctrl@40-pinctrl-meson/pinconf-pins says that the pins are input bias disabled, output drive strength (3000 uA). But I am able to configure them for GPIO_ACTIVE_LOW in the overlay and trigger keystrokes. I’m a little wary about using them if they are connected to USB though. Any thoughts about that?

As always, thanks again,

- George

You’re right that the “Quick Reference Manual” isn’t a true datasheet and that the actual datasheet isn’t publicly available. It’s the closest thing we have though.

Re GPIOAO_8 and GPIOH_8, it should be possible to use a DT overlay to switch those to inputs.

GPIOAO_0 / GPIOAO_1 are something of a special case because they’re used for UART. If you use them for input, just make sure you do are not using the uart-ao-a overlay.

I’d definitely be wary about using GPIOA_14 and GPIOA_15 that way. By default the system uses those for i2c communication with the UCB controller. If you’ve got no use for USB you might be able to get away with using them but no telling if it will lead to system instability or other gremlins. I think the only reason those pins are even on the GPIO header is to add other i2c devices to the bus. I actually use those pins for i2c in my project.