Philipp
Published © CERN-OHL2

Stackable Raspberry Pi HATs without GPIO Resource Conflicts

Use several Raspberry Pi HATs in one stack by assigning fixed board instances and hardware resources.

AdvancedShowcase (no instructions)1 hour48
Stackable Raspberry Pi HATs without GPIO Resource Conflicts

Things used in this project

Story

Read more

Schematics

Schematic: HAT+ / HAT++ EEPROM Emulation Circuit

The MCU emulates the HAT identification EEPROM. The selected instance ID defines the active I²C target address.

The same instance ID is also used to control the `Sx_OE` output-enable signals. These signals enable the corresponding routing for SPI chip-select and interrupt lines, so each board instance uses its assigned resources.

Code

BE-IIS-HPP-T1S-I.dts

SH
Example Device Tree overlay for a 10BASE-T1S board configured as Instance I.

The overlay assigns the required hardware resources for the first board in the stack. It also prepares the Raspberry Pi system for stacked operation by keeping required SPI resources free and making the additional HAT identification EEPROM addresses visible for the startup service.
// Raspberry Pi DT overlay: Microchip LAN8651/LAN8650 on SPI0 (CE0)
// IRQ: BCM GPIO6 (falling edge)
// SPI must be enabled in config.txt:
//     dtparam=spi=on
//
// Supported Raspberry Pi models (SPI0-based):
//   - Raspberry Pi Zero / Zero W / Zero 2 W
//   - Raspberry Pi 1 (A/B/B+)
//   - Raspberry Pi 2 (v1.1 / v1.2)
//   - Raspberry Pi 3 (3A+, 3B, 3B+)
//   - Raspberry Pi 4 (4B, 400, CM4)
//   - Raspberry Pi 5
//
// Notes:
//   - Uses SPI0 + CE0
//   - Uses BCM GPIO numbering
//   - Works across bcm2835 / bcm2711 / bcm2712


/dts-v1/;
/plugin/;

#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>

/ {
	/*
	 * RPi firmware doesnt really *use* this for overlays, but keeping it sane helps.
	 * Works across Pi Zero / 2 / 3 / 4 / 5
	 */
	compatible = "brcm,bcm2835", "brcm,bcm2711", "brcm,bcm2712";

	fragment@0 {
		target = <&spi0>;
		__overlay__ {
			/* avoid dtc warnings */
			#address-cells = <1>;
			#size-cells = <0>;
			status = "okay";
			

			/* Extend SPI0 to 3 chip-selects */
			num-cs = <3>;
			cs-gpios =
				<&gpio 8  GPIO_ACTIVE_LOW>,   /* CE0 */
				<&gpio 7  GPIO_ACTIVE_LOW>,   /* CE1 */
				<&gpio 16 GPIO_ACTIVE_LOW>;   /* CS2 */

			/*
			 * LAN865x on SPI0, chip select 0 (CE0)
			 * Interrupt on GPIO6, falling edge
			 */
			eth1: ethernet@0 {
				compatible = "microchip,lan8651", "microchip,lan8650";
				reg = <0>; /* CE0 */
				
			    	pinctrl-names = "default";
			    	pinctrl-0 = <&be_iis_in_i_irq_pin>;

				interrupt-parent = <&gpio>;
				interrupts = <6 IRQ_TYPE_EDGE_FALLING>;

				/* optional: set a fixed MAC (better: let it be set by userspace) */
				/* local-mac-address = [04 05 06 01 02 03]; */

				/* LAN865x TC6 SPI clock; start conservative if unsure */
				spi-max-frequency = <20000000>;

				status = "okay";
			};
		};
	};

	/* disable spidev on CE0 because CE0 is used by this BE-IIS HAT++ device */
	fragment@1 {
		target = <&spidev0>;
		__overlay__ {
			status = "disabled";
		};
	};
	/* GPIO6 as input, pull-up (IRQ pin) */
	fragment@2 {
	    target = <&gpio>;
	    __overlay__ {
		be_iis_in_i_irq_pin: be-iis-in-i-irq-pin {
		    brcm,pins = <6>;
		    brcm,function = <0>;
		    brcm,pull = <2>;
		};
	    };
	};
	
	/* disable spidev on CE1 to keep the second chip-select free for BE-IIS HAT++ use */
	fragment@3 {
		target = <&spidev1>;
		__overlay__ {
		status = "disabled";
		};
	};

	fragment@9 {
	target = <&i2c0>;
	__overlay__ {
		#address-cells = <1>;
		#size-cells = <0>;

		ee50: eeprom@50 {
			compatible = "atmel,24c32";
			reg = <0x50>;
			status = "okay";
		};
		
		ee60: eeprom@60 {
			compatible = "atmel,24c32";
			reg = <0x60>;
			status = "okay";
		};

		ee70: eeprom@70 {
			compatible = "atmel,24c32";
			reg = <0x70>;
			status = "okay";
		};
		
		ee74: eeprom@74 {
			compatible = "atmel,24c32";
			reg = <0x74>;
			status = "okay";
		};
		
		ee76: eeprom@76 {
			compatible = "atmel,24c32";
			reg = <0x76>;
			status = "okay";
		};
	};
};

fragment@10 {
	target = <&gpio>;
	__overlay__ {
		be_iis_porst: be-iis-porst {
			gpio-hog;
			gpios = <13 GPIO_ACTIVE_HIGH>;
			output-high;
			line-name = "BE-IIS-Power-on-reset";
		};
	};
};

	__overrides__ {

		/* dtoverlay=lan8651,speed=200000000 */
		speed    = <&eth1>, "spi-max-frequency:0";
	};
};

apply-hat-overlays.sh

Powershell
This script is executed once after boot by the HAT++ system integration service.

It scans the additional HAT identification EEPROM addresses, reads the stored overlay information and applies the detected Device Tree overlays with the Raspberry Pi `dtoverlay` tool.

In this way, Instance I follows the normal Raspberry Pi HAT boot flow, while additional boards in the stack are detected and configured automatically after startup.
#!/bin/bash
set -e

echo "BE-IIS ========================================"
echo "BE-IIS HAT++ System Integration"
echo "BE-IIS ========================================"
echo ""

echo "BE-IIS [1/2] Scanning BE-IIS HAT++ EEPROMs..."

# Instance mapping
declare -A INSTANCE_MAP=(
    ["0-0050"]="I"
    ["0-0060"]="II"
    ["0-0070"]="III"
    ["0-0074"]="IV"
    ["0-0076"]="V"
)

for eep in \
    /sys/bus/i2c/devices/0-0050/eeprom \
    /sys/bus/i2c/devices/0-0060/eeprom \
    /sys/bus/i2c/devices/0-0070/eeprom \
    /sys/bus/i2c/devices/0-0074/eeprom \
    /sys/bus/i2c/devices/0-0076/eeprom
do
    if [ -e "$eep" ]; then

        addr="$(basename "$(dirname "$eep")")"
        inst="${INSTANCE_MAP[$addr]}"

        overlay="$(eepdump "$eep" 2>/dev/null | awk -F'"' '/^dt_blob /{print $2}')"

        if [ -n "$overlay" ]; then
            if [ "$inst" = "I" ]; then
                echo "BE-IIS Instance $inst ($addr): HAT detected  $overlay"
                echo "BE-IIS Instance $inst ($addr): Base instance (overlay handled by HAT+ autodetect)"
            else
                echo "BE-IIS Instance $inst ($addr): HAT detected  $overlay"
                echo "BE-IIS Instance $inst ($addr): Applying overlay..."
                dtoverlay "$overlay"
                echo "BE-IIS Instance $inst ($addr): Overlay applied"
            fi

        else
            echo "BE-IIS Instance $inst ($addr): EEPROM present, no overlay"
        fi
    fi
done

echo ""
echo "BE-IIS [2/2] Loading BE-IIS kernel modules..."

modules=(
    lan865x
    microchip_t1s
    oa_tc6
    adin1110
    ks8851
    mcp251xfd
    sc16is7xx
    sc16is7xx_i2c
)

for mod in "${modules[@]}"; do
    if modinfo "$mod" >/dev/null 2>&1; then
        echo "BE-IIS Loading module: $mod"
        modprobe "$mod" || true
    else
        echo "BE-IIS Module not available: $mod"
    fi
done

echo ""
echo "BE-IIS HAT++ system integration complete."

Credits

Philipp
1 project • 0 followers

Comments