Torizon GPIO and Apalis imx6

I’m trying to toggle a GPIO with gpiod on Apalis imx6 + Ixora carrier board but no luck. I think my code is correct and it builds, so I suspect that it’s a hardware access issue. (gpiod_ctxless_find_line is also throwing an error). I’ve tried toggling LED4_GREEN (gpiochip1, line 14), LED5_GREEN (gpiochip2, line 1), and GPIO 1 (gpiochip2, line 4) with no luck.

Code:

 // GPIO Test
    // try to flash LED4_GREEN, which is pin 188 of MXM3 connector = gpiochip1, line 14
     // try to flash LED5_GREEN, which is pin 152 of MXM3 connector = gpiochip2, line 1
    int line_value = 0;
    char bank[32]; 
    snprintf(bank, sizeof(bank), "gpiochip2");
    unsigned int line = 1; 

    // if (gpiod_ctxless_find_line("SODIMM_188", bank, sizeof(bank), &line) <= 0){
    //     perror("Error finding GPIO\n");
    //     return -1;
    // }

    while(true){
        line_value = !line_value;
        gpiod_ctxless_set_value(bank,line,line_value,false,"main",NULL,NULL);
        sleep(1);
    }

docker-compose.yml

version: "3.9"
services:
  autofilteringfixture2-debug:
    build:
      context: .
      dockerfile: Dockerfile.debug
    image: ${LOCAL_REGISTRY}:5002/autofilteringfixture2-debug:${TAG}
    ports:
      # SSH debug
      - 2231:2231
      # gdbserver
      - 2232:2232
      # qml debug
      - 2233:2233
    volumes:
      - type: bind
        source: /tmp
        target: /tmp
      - type: bind
        source: /dev
        target: /dev
    devices:
      - "/dev/apalis-uart1:/dev/apalis-uart1"
      - "/dev/apalis-uart2:/dev/apalis-uart2"
      - "/dev/apalis-uart3:/dev/apalis-uart3"
      - "/dev/apalis-uart4:/dev/apalis-uart4"
      - "/dev/gpiochip0:/dev/gpiochip0"
      - "/dev/gpiochip1:/dev/gpiochip1"
      - "/dev/gpiochip2:/dev/gpiochip2"
    device_cgroup_rules:
      # ... for tty0
      - "c 4:0 rmw"
      # ... for tty7
      - "c 4:7 rmw"
      # ... for /dev/input devices
      - "c 13:* rmw"
      - "c 199:* rmw"
      # ... for /dev/dri devices
      - "c 226:* rmw"
      # enable access to all GPIOs
      - 'c 254:* rmw'
    depends_on: [
      weston
    ]


  autofilteringfixture2:
    build:
      context: .
      dockerfile: Dockerfile
    image: ${DOCKER_LOGIN}/autofilteringfixture2:${TAG}
    volumes:
      - type: bind
        source: /tmp
        target: /tmp
      - type: bind
        source: /dev
        target: /dev
    devices:
      - "/dev/apalis-uart1:/dev/apalis-uart1"
      - "/dev/apalis-uart2:/dev/apalis-uart2"
      - "/dev/apalis-uart3:/dev/apalis-uart3"
      - "/dev/apalis-uart4:/dev/apalis-uart4"
      - "/dev/gpiochip0:/dev/gpiochip0"
      - "/dev/gpiochip1:/dev/gpiochip1"
      - "/dev/gpiochip2:/dev/gpiochip2"
    device_cgroup_rules:
      # ... for tty0
      - "c 4:0 rmw"
      # ... for tty7
      - "c 4:7 rmw"
      # ... for /dev/input devices
      - "c 13:* rmw"
      - "c 199:* rmw"
      # ... for /dev/dri devices
      - "c 226:* rmw"
      # enable access to all GPIOs
      - 'c 254:* rmw'
    depends_on: [
      weston
    ]

  weston:
    image: torizon/weston${GPU}:3
    environment:
      - ACCEPT_FSL_EULA=1
    # Required to get udev events from host udevd via netlink
    network_mode: host
    volumes:
      - type: bind
        source: /tmp
        target: /tmp
      - type: bind
        source: /dev
        target: /dev
      - type: bind
        source: /run/udev
        target: /run/udev
    cap_add:
      - CAP_SYS_TTY_CONFIG
    # Add device access rights through cgroup...
    devices:
      - "/dev/apalis-uart1:/dev/apalis-uart1"
      - "/dev/apalis-uart2:/dev/apalis-uart2"
      - "/dev/apalis-uart3:/dev/apalis-uart3"
      - "/dev/apalis-uart4:/dev/apalis-uart4"
      - "/dev/gpiochip0:/dev/gpiochip0"
      - "/dev/gpiochip1:/dev/gpiochip1"
      - "/dev/gpiochip2:/dev/gpiochip2"
    device_cgroup_rules:
      # ... for tty0
      - "c 4:0 rmw"
      # ... for tty1
      - "c 4:1 rmw"
      # ... for tty7
      - "c 4:7 rmw"
      # ... for /dev/input devices
      - "c 13:* rmw"
      - "c 199:* rmw"
      # ... for /dev/dri devices
      - "c 226:* rmw"
      # enable access to all GPIOs
      - 'c 254:* rmw'

Setup:
Apalis iMX6 Quad 2GB IT V1.1Y
Ixora Carrier Board V1.3A
Torizon, using Qt C++/QML Template
Host Machine: Windows, VS Code w/ Torizon IDE Extension 2

Hey @lvizzy,

What container are you trying to run this program in?
Have you build the container image with the correct dependence?
Please see our dockerfile example for building an image that can toggle gpio.

You can reference our GPIO with Torizon that walks you through toggling GPIOS with libgpiod inside a container.

And can you post the errors that you are seeing when you try and run the program within a container.

-Eric

Hi @eric.tx,

I’m using a minimally modified Qt C++ Template provided by Toradex. Here’s my torizonPackages.json:

{
    "deps": [
        "libgpiod2"
    ],
    "devDeps": [
        "libgpiod-dev"
    ]
}

and my Dockerfile. (Do I need to manually edit the Dockerfile somewhere?)

# ARGUMENTS --------------------------------------------------------------------
##
# SDK container version
##
ARG SDK_BASE_VERSION=3
##
# Base container version
##
ARG BASE_VERSION=3

##
# Board architecture
# arm or arm64
##
ARG IMAGE_ARCH=

##
# Board GPU vendor prefix
##
ARG GPU=

##
# Directory of the application inside container
##
ARG APP_ROOT=

# BUILD ------------------------------------------------------------------------
# TODO: cross compile x86 to arm
# We will use emulation here
##
# Build Step
##
FROM --platform=linux/${IMAGE_ARCH} \
    torizon/qt6-wayland${GPU}:${SDK_BASE_VERSION} AS Build

ARG IMAGE_ARCH
ARG GPU
ARG APP_ROOT

# for vivante GPU we need some "special" sauce
RUN apt-get -q -y update && \
        if [ "${GPU}" = "-vivante" ] || [ "${GPU}" = "-imx8" ]; then \
            apt-get -q -y install \
            imx-gpu-viv-wayland-dev \
        ; else \
            apt-get -q -y install \
            libgl1 \
            libgles-dev \
        ; fi \
    && \
    apt-get clean && apt-get autoremove && \
    rm -rf /var/lib/apt/lists/*

# __deps__
RUN apt-get -q -y update && \
    apt-get -q -y install \
    build-essential \
    cmake \
    qt6-base-private-dev \
    qt6-base-dev \
    qt6-wayland \
    qt6-wayland-dev \
    qt6-declarative-dev \
    qt6-declarative-private-dev \
    qml6-module-qtqml \
    qml6-module-qtqml-workerscript \
    qml6-module-qtcore \
    qml6-module-qtquick \
    qml6-module-qtquick-window \
    qml6-module-qtquick-controls \
    qml6-module-qtquick-layouts \
    qml6-module-qtquick-templates \
    libqt6opengl6-dev \
    # ADD YOUR PACKAGES HERE
# DO NOT REMOVE THIS LABEL: this is used for VS Code automation
    # __torizon_packages_dev_start__
	libgpiod-dev:armhf \
    # __torizon_packages_dev_end__
# DO NOT REMOVE THIS LABEL: this is used for VS Code automation
    && \
    apt-get clean && apt-get autoremove && \
    rm -rf /var/lib/apt/lists/*
# __deps__

COPY . ${APP_ROOT}
WORKDIR ${APP_ROOT}

RUN if [ "$IMAGE_ARCH" = "arm64" ] ; then \
        cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc -Bbuild-${IMAGE_ARCH} ; \
    elif [ "$IMAGE_ARCH" = "arm" ] ; then \
        cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -Bbuild-${IMAGE_ARCH} ; \
    fi

RUN cmake --build build-${IMAGE_ARCH}

# BUILD ------------------------------------------------------------------------

# DEPLOY -----------------------------------------------------------------------
##
# Deploy Step
##
FROM --platform=linux/${IMAGE_ARCH} \
    torizon/qt6-wayland${GPU}:${BASE_VERSION} AS Deploy

ARG IMAGE_ARCH
ARG GPU
ARG APP_ROOT

# SSH for remote debug
EXPOSE 2231
ARG SSHUSERNAME=torizon

# Make sure we don't get notifications we can't answer during building.
ENV DEBIAN_FRONTEND="noninteractive"

# for vivante GPU we need some "special" sauce
RUN apt-get -q -y update && \
        if [ "${GPU}" = "-vivante" ] || [ "${GPU}" = "-imx8" ]; then \
            apt-get -q -y install \
            imx-gpu-viv-wayland-dev \
        ; else \
            apt-get -q -y install \
            libgl1 \
            libgles-dev \
        ; fi \
    && \
    apt-get clean && apt-get autoremove && \
    rm -rf /var/lib/apt/lists/*

# your regular RUN statements here
# Install required packages
RUN apt-get -q -y update && \
    apt-get -q -y install \
    file \
    curl \
    qt6-base-private-dev \
    qt6-base-dev \
    qt6-wayland \
    qt6-wayland-dev \
    qt6-declarative-dev \
    qt6-declarative-private-dev \
    qml6-module-qtqml \
    qml6-module-qtqml-workerscript \
    qml6-module-qtcore \
    qml6-module-qtquick \
    qml6-module-qtquick-window \
    qml6-module-qtquick-controls \
    qml6-module-qtquick-layouts \
    qml6-module-qtquick-templates \
# DO NOT REMOVE THIS LABEL: this is used for VS Code automation
    # __torizon_packages_prod_start__
	libgpiod2:armhf \
    # __torizon_packages_prod_end__
# DO NOT REMOVE THIS LABEL: this is used for VS Code automation
    && \
    apt-get clean && apt-get autoremove && \
    rm -rf /var/lib/apt/lists/*

USER torizon

# Copy the application compiled in the build step to the $APP_ROOT directory
# path inside the container, where $APP_ROOT is the torizon_app_root
# configuration defined in settings.json.
COPY --from=Build ${APP_ROOT}/build-${IMAGE_ARCH}/bin ${APP_ROOT}

# "cd" (enter) into the APP_ROOT directory
WORKDIR ${APP_ROOT}

# Command executed in runtime when the container starts
CMD ["./AutoFilteringFixture2"]

# DEPLOY -----------------------------------------------------------------------

I’m not getting any build errors, and gpiod_ctxless_set_value is returning 0 (so it thinks it succeeds.) But gpios aren’t actually changing when I debug the code I posted. (LED’s aren’t flashing, and I couldn’t see GPIO1 changing on a scope.)

It would seem that gpiod_ctxless_find_line is also returning 0 when I uncomment that block, so it thinks the SODIMM/MXM3 label doesn’t exist.

Thoughts?

Hey @lvizzy,

I believe I know the source of error. Looking at the pins you are wanting to use. We need to make sure these pins are enabled in the device tree.

From the Apalis IMX6 data sheet.

GPIO 1 → X1 pin 4 (pg 25)

And from the Ixora Datasheet

LED4_Green → gpio associated with 188 (pg 14)
LED5_green → gpio associated with 152 (pg 14)

We can see from the Apalis datasheet that

GPIO 1 defaults to pwm2_out
LED4_Green(188) defaults to SD_DATA1
LED5_Green(152) defaults to SD1_DATA5

So we need to modify the device tree to reflect these changes.

Are you familiar with device tree overlays and Pin Multiplexing?

-Eric

A little bit, I’ve had to edit device tree overlays in the past.

I was assuming that this was the device tree in use. Maybe you can give me some guidance… how would I confirm which device tree is actually in use on the module?

It seems to me from this device tree that the LED4 and LED5 GPIOs should be connected to the LEDs. The only overlays I see in /proc/device-tree/chosen/overlays are for hdmi and spi.

Hey @lvizzy,

While SSH’d into the module you should be able to see the currently device tree via
cat /proc/device-tree/model

If you have not set up the correct device tree, it may have defaulted to the Apalis Evaluation board.

-Eric

Okay good call, it has defaulted to “Toradex Apalis iMX6Q/D Module on Apalis Evaluation Board.” Is there an easy way to swap in a complete device tree, like the one for the Ixora board? I think this doc says I can just scp the new device tree into /home/boot… is that correct?

Also, I wasn’t clear when I said “GPIO1”. I was referring to GPIO 1 on the Ixora carrier board, which is MXM Pin 1 (GPIO2_IO04 in the Apalis datasheet). According to the datasheet this pin should default to GPIO… so that doesn’t explain why that isn’t working. I confirmed again with a scope for MXM Pins 1, 3, and 15 (GPIO2_IO4, GPIO2_IO5, GPIO1IO2). These are GPIO1, GPIO2, and GPIO7 on the carrier board. I’m still not seeing them toggle on a scope when I swap in the bank and line in my code and debug.

I’m certain I’ll need to edit device trees/device tree overlays, but for the moment I just need 7 GPIOs and a couple UARTs.

Hey @lvizzy,

Yes, the document you listed is the correct resource for changing the device tree for the OS. With Torizon OS, you would be compiling the device tree and still using TorizonCore Builder dt apply command.

I believe the best way to change the device tree is going to be with TorizonCore Builder. Either the standalone tool or via the IDE extension. This will also allow you to build in device tree overlays once setup.

If doing the TorizonCore Builder, you would be customizing the tcbuild.yaml file, such as:

...
customization:
...
  device-tree:
    include-dirs:
      - device-trees/include/
    custom: device-trees/dts-arm32/imx6q-apalis-ixora-v1.2.dts

    overlays:
      add:
        - device-trees/overlays/{custom_overlay_file.dts}


Here is the workflow for TorizonCore builder.

-Eric

I followed this article and think I’ve built the new image correctly, but I ran into an issue trying to deploy it onto the board via ssh. I have the easy installer running on the SoM and run:

torizoncore-builder deploy --remote-host 192.168.11.1 --remote-username torizon --remote-password torizon --reboot

But get the following error:

torizoncore-builder deploy --remote-host 192.168.11.1 --remote-username torizon --remote-password torizon --reboot
Pulling OSTree with ref base (checksum 38fad791849b202e09628d45c2781616bdf5f2f34562310f3baed46625b5ac36) from local archive repository...
Starting http server to serve OSTree.
OSTree server listening on "localhost:33351".
An unexpected Exception occurred. Please provide the following stack trace to
the Toradex TorizonCore support team:


Traceback (most recent call last):
  File "/builder/torizoncore-builder", line 221, in <module>
    mainargs.func(mainargs)
  File "/builder/tcbuilder/cli/deploy.py", line 101, in do_deploy
    do_deploy_ostree_remote(args)
  File "/builder/tcbuilder/cli/deploy.py", line 92, in do_deploy_ostree_remote
    dbe.deploy_ostree_remote(args.remote_host, args.remote_username,
  File "/builder/tcbuilder/backend/deploy.py", line 294, in deploy_ostree_remote
    client.connect(hostname=resolved_remote_host,
  File "/usr/lib/python3/dist-packages/paramiko/client.py", line 368, in connect
    raise NoValidConnectionsError(errors)
paramiko.ssh_exception.NoValidConnectionsError: [Errno None] Unable to connect to port 22 on 192.168.11.1

I can see from the easy installer that the network info is:
Ethernet - 192.168.1.2/24
USB RNDIS - 192.168.11.1/24

What am I doing wrong? Do I need to change the port somehow? I get “resolve hostname failed” I run the command with 192.168.11.1:24

hey @lvizzy,

The 192.168.11.1 IP address is a static IP address for the Toradex Easy Installer. Once you install your image from the easy installer a new IP address is assigned. You’ll want to use that one with username torizon and your new password that it will force you to create. ( shell into device after Toradex Easy Installer image).

With the module connected to the your network, you can use arp-scan to find the IP address of the module. This is typically what I do when I need to know the IP address and don’t already have it.

-Eric

Hey @lvizzy,

As a follow up. Because of how the leds node is defined in imx6q-apalis-ixora-v1.2.dts You’ll find the leds available under /sys/class/leds/ and without preforming the pin multiplexing (until needed) you’ll be able to change the LED color via echo 1 > LED_4_GREEN/brightness such as:

torizon@apalis-imx6-11099408:/sys/class/leds$ ls
LED_4_GREEN LED_4_RED LED_5_GREEN LED_5_RED mmc0:: mmc1::
torizon@apalis-imx6-11099408:/sys/class/leds$ echo 1 > LED_4_GREEN/brightness

If you want these to be like a GPIO, you will still need to modify the device tree and preform pin Multiplexing previously stated.

-Eric

@eric.tx It looks like this ultimately got me to the solution! I had to install the image via usb, I couldn’t get deploying via ssh to work. Also, I missed that fact that bank indexing is off by one between the “ALT” names in Apalis imx6 datasheet and the libgpiod library interface…

Thanks for the help!