refactor(kernel): align GPIO pin layout with the reference listener
All checks were successful
Build kernel module / build (1:6.12.93-1+rpt1, bookworm) (pull_request) Successful in 1m1s
Build kernel module / build (1:6.18.34-1+rpt1, trixie) (pull_request) Successful in 1m8s
Build kernel module / package (pull_request) Successful in 29s
Build kernel module / release (pull_request) Has been skipped

Adopt the confirmed-working reference's pin assignment so the module runs
on that proven wiring: DATA=BCM2 (pin 3), CLK=BCM3 (pin 5), ATN=BCM4
(pin 7). RESET is kept and relocated to BCM17 (pin 11), the pin freed by
moving CLK. All five GPIOs stay in bank 0, so the direct-register hot
path and ATN IRQ are unchanged -- only the IEC_GPIO_* defines, the
device-tree overlay, and the docs/self-test move.

DATA/CLK now sit on the ARM I2C pins (GPIO2/3) with the SoC's fixed
~1.8k pull-ups; keep dtparam=i2c_arm off. The bare-board self-test
expectation changes from 0x15 to 0x0b accordingly.

Generated by Clanker
This commit is contained in:
Christian Werner 2026-06-20 16:09:10 +02:00
parent acfce76011
commit b2e4f9710d
7 changed files with 55 additions and 40 deletions

View File

@ -94,10 +94,10 @@ the 1 ms ATN ack and the per-byte ack — both forgiving. The tight constraint i
| IEC signal | Pi GPIO (BCM) | Header pin | Direction | Logic note | | IEC signal | Pi GPIO (BCM) | Header pin | Direction | Logic note |
|------------|---------------|------------|-----------|------------| |------------|---------------|------------|-----------|------------|
| ATN | GPIO 2 | 3 | input (divider) | bus low = Pi low = asserted | | ATN | GPIO 4 | 7 | input (divider) | bus low = Pi low = asserted |
| CLK | GPIO 17 | 11 | input (divider) | bus low = Pi low = asserted | | CLK | GPIO 3 | 5 | input (divider) | bus low = Pi low = asserted |
| DATA (in) | GPIO 18 | 12 | output → 7406 → bus | Pi HIGH = bus asserted (inverted) | | DATA (in) | GPIO 2 | 3 | output → 7406 → bus | Pi HIGH = bus asserted (inverted) |
| RESET | GPIO 3 | 5 | input (divider) | bus low = Pi low = asserted | | RESET | GPIO 17 | 11 | input (divider) | bus low = Pi low = asserted |
| GND | — | 6 (or any) | — | tie to IEC pin 2 | | GND | — | 6 (or any) | — | tie to IEC pin 2 |
> Convention used in code: a helper layer converts electrical reads/writes into > Convention used in code: a helper layer converts electrical reads/writes into

View File

@ -59,11 +59,11 @@ at global number 0 — it's `gpiochip512`, **base 512**:
$ cat /sys/class/gpio/gpiochip*/base # -> 512 (pinctrl-bcm2835), 566 (exp-gpio) $ cat /sys/class/gpio/gpiochip*/base # -> 512 (pinctrl-bcm2835), 566 (exp-gpio)
``` ```
The old code used `gpio_to_desc(2/3/17/18)`, i.e. the *global* numberspace The old code used `gpio_to_desc(2/3/4/17)`, i.e. the *global* numberspace
assuming base 0. Those small numbers now fall outside the chip's range assuming base 0. Those small numbers now fall outside the chip's range
(512565), so every `gpio_to_desc()` returned `NULL`, the descriptor check (512565), so every `gpio_to_desc()` returned `NULL`, the descriptor check
tripped, and init bailed with `-ENODEV`. The `(chip, hwnum)` lookup passes the tripped, and init bailed with `-ENODEV`. The `(chip, hwnum)` lookup passes the
**BCM number as the chip-relative offset** (ATN=2, RESET=3, CLK=17, DATA=18), **BCM number as the chip-relative offset** (ATN=4, RESET=17, CLK=3, DATA=2),
which is base-independent and survives kernel bumps / gpiochip renumbering. which is base-independent and survives kernel bumps / gpiochip renumbering.
Notes: Notes:

View File

@ -25,20 +25,26 @@ The shifter is **non-inverting**: bus low (asserted) ⇒ Pi reads LOW.
| IEC signal | IEC DIN pin | Pi GPIO (BCM) | Header pin | Direction | | IEC signal | IEC DIN pin | Pi GPIO (BCM) | Header pin | Direction |
|------------|-------------|---------------|------------|-----------| |------------|-------------|---------------|------------|-----------|
| ATN | 3 | GPIO 2 | 3 | input (via shifter) | | ATN | 3 | GPIO 4 | 7 | input (via shifter) |
| CLK | 4 | GPIO 17 | 11 | input (via shifter) | | CLK | 4 | GPIO 3 | 5 | input (via shifter) |
| DATA | 5 | GPIO 18 | 12 | **bidirectional** (via shifter) | | DATA | 5 | GPIO 2 | 3 | **bidirectional** (via shifter) |
| RESET | 6 | GPIO 3 | 5 | input (via shifter) | | RESET | 6 | GPIO 17 | 11 | input (via shifter) |
| GND | 2 | GND | 6 (or any) | — | | GND | 2 | GND | 6 (or any) | — |
| SRQ | 1 | — | — | not connected | | SRQ | 1 | — | — | not connected |
> **DATA (GPIO2) and CLK (GPIO3) are the Pi's ARM I²C pins** and carry the
> SoC's fixed ~1.8 kΩ pull-ups (benign — DATA is open-drain). Keep
> `dtparam=i2c_arm` **off** in `/boot/firmware/config.txt` (the default) so I²C
> does not claim these pins. This layout matches a confirmed-working reference
> listener.
## DATA open-drain emulation ## DATA open-drain emulation
GPIO 18 is never a push-pull driver of the bus. The module flips its direction: GPIO 2 is never a push-pull driver of the bus. The module flips its direction:
- **assert** (pull bus low): GPIO 18 = output **LOW** → shifter pulls bus to 0 V. - **assert** (pull bus low): GPIO 2 = output **LOW** → shifter pulls bus to 0 V.
- **release / read**: GPIO 18 = **input (Hi-Z)** → bus pull-ups float it to 5 V, - **release / read**: GPIO 2 = **input (Hi-Z)** → bus pull-ups float it to 5 V,
and GPIO 18 reads the level the *talker* (C64) is driving — this is how bits and GPIO 2 reads the level the *talker* (C64) is driving — this is how bits
are sampled during receive. are sampled during receive.
Logical mapping (handled in `kernel/iec_lines.h`): asserted ⇔ Pi reads LOW. Logical mapping (handled in `kernel/iec_lines.h`): asserted ⇔ Pi reads LOW.

View File

@ -92,18 +92,21 @@ Constants are defined in [`iec_listener.h`](iec_listener.h); the ioctl number is
### ⚠️ Bare-board caveat ### ⚠️ Bare-board caveat
With **nothing connected**, the expected result is **`0x15`**, *not* a fault: With **nothing connected**, the expected result is **`0x0B`**, *not* a fault:
- `DATA_ASSERT_OK` (`0x01`) passes — it's internal to the Pi. - `DATA_ASSERT_OK` (`0x01`) passes — it's internal to the Pi.
- `ATN_RELEASED` (`0x04`) and `RESET_RELEASED` (`0x10`) pass **only because - `DATA_FLOAT_OK` (`0x02`) and `CLK_RELEASED` (`0x08`) pass **only because
GPIO2/GPIO3 have fixed ~1.8 kΩ pull-ups built into the BCM2710 SoC** (they are DATA=GPIO2 and CLK=GPIO3 have fixed ~1.8 kΩ pull-ups built into the BCM2710
the I²C0 pins; the pull-ups can't be disabled). They read high even with SoC** (they are the I²C pins; the pull-ups can't be disabled). They read high
nothing wired, so on a bare board these two bits prove **nothing** about your even with nothing wired, so on a bare board these two bits prove **nothing**
wiring. about your wiring. In particular `DATA_FLOAT_OK` now floats high via the SoC
- `DATA_FLOAT_OK` (`0x02`) and `CLK_RELEASED` (`0x08`) read low because GPIO17/18 pull-up even without the shifter, so it no longer confirms the shifter's DATA
have no such built-in pull-up and nothing is attached. pull-up — it's only meaningful once the shifter is wired.
- `ATN_RELEASED` (`0x04`) and `RESET_RELEASED` (`0x10`) read low because
ATN=GPIO4 and RESET=GPIO17 have no such built-in pull-up and nothing is
attached.
So on a bare board the only meaningful signal is `DATA_ASSERT_OK`. A full `0x1F` So on a bare board the only fully internal signal is `DATA_ASSERT_OK`. A full
is only reachable once the level shifter (with its CLK/DATA pull-ups) is wired `0x1F` is only reachable once the level shifter (with its ATN/RESET pull-ups) is
and powered. To make ATN/RESET meaningful, briefly ground each at the connector wired and powered. To make ATN/RESET meaningful, briefly ground each at the
and confirm the corresponding bit *drops*. connector and confirm the corresponding bit *drops*.

View File

@ -1,10 +1,15 @@
/* /*
* iec-overlay.dts - pin reservation + pull configuration for the IEC listener. * iec-overlay.dts - pin reservation + pull configuration for the IEC listener.
* *
* GPIO 2 = ATN (input, pull-up: bus idle = 5 V = high) * GPIO 4 = ATN (input, pull-up: bus idle = 5 V = high)
* GPIO 17 = CLK (input, pull-up) * GPIO 3 = CLK (input, pull-up)
* GPIO 3 = RESET (input, pull-up) * GPIO 17 = RESET (input, pull-up)
* GPIO 18 = DATA (bidirectional; starts as input/Hi-Z, the module flips it) * GPIO 2 = DATA (bidirectional; starts as input/Hi-Z, the module flips it)
*
* NOTE: DATA (GPIO2) and CLK (GPIO3) are the ARM I2C pins and carry the SoC's
* fixed ~1.8 kOhm pull-ups, which the overlay's pull settings cannot fully
* override. Benign here (DATA is open-drain). Keep dtparam=i2c_arm OFF so I2C
* does not claim these pins.
* *
* The module drives GPIO via direct registers, so this overlay's main job is to * The module drives GPIO via direct registers, so this overlay's main job is to
* reserve the pins and set sane pull defaults. Build & install: * reserve the pins and set sane pull defaults. Build & install:
@ -25,12 +30,12 @@
target = <&gpio>; target = <&gpio>;
__overlay__ { __overlay__ {
iec_input_pins: iec_input_pins { iec_input_pins: iec_input_pins {
brcm,pins = <2 17 3>; /* ATN, CLK, RESET */ brcm,pins = <4 3 17>; /* ATN, CLK, RESET */
brcm,function = <0>; /* 0 = input */ brcm,function = <0>; /* 0 = input */
brcm,pull = <2>; /* 2 = pull-up */ brcm,pull = <2>; /* 2 = pull-up */
}; };
iec_data_pin: iec_data_pin { iec_data_pin: iec_data_pin {
brcm,pins = <18>; /* DATA */ brcm,pins = <2>; /* DATA */
brcm,function = <0>; /* start as input (Hi-Z) */ brcm,function = <0>; /* start as input (Hi-Z) */
brcm,pull = <0>; /* 0 = none (shifter drives) */ brcm,pull = <0>; /* 0 = none (shifter drives) */
}; };

View File

@ -10,8 +10,8 @@
* bus released (5 V) <=> Pi GPIO reads HIGH * bus released (5 V) <=> Pi GPIO reads HIGH
* *
* DATA is open-drain emulated on a single bidirectional pin: * DATA is open-drain emulated on a single bidirectional pin:
* assert : drive GPIO18 OUTPUT LOW -> shifter pulls bus to 0 V * assert : drive GPIO2 OUTPUT LOW -> shifter pulls bus to 0 V
* release : set GPIO18 INPUT (Hi-Z) -> bus pull-ups float it to 5 V; * release : set GPIO2 INPUT (Hi-Z) -> bus pull-ups float it to 5 V;
* the talker's bits can now be read back through the shifter. * the talker's bits can now be read back through the shifter.
* *
* The hot path uses direct BCM register access (ioremap'd), which the kernel * The hot path uses direct BCM register access (ioremap'd), which the kernel
@ -30,10 +30,10 @@
#include <linux/io.h> #include <linux/io.h>
/* --- GPIO assignment (BCM numbering, PLAN.md §3.2) ----------------------- */ /* --- GPIO assignment (BCM numbering, PLAN.md §3.2) ----------------------- */
#define IEC_GPIO_ATN 2 /* header pin 3 - input */ #define IEC_GPIO_ATN 4 /* header pin 7 - input */
#define IEC_GPIO_RESET 3 /* header pin 5 - input */ #define IEC_GPIO_RESET 17 /* header pin 11 - input */
#define IEC_GPIO_CLK 17 /* header pin 11 - input */ #define IEC_GPIO_CLK 3 /* header pin 5 - input */
#define IEC_GPIO_DATA 18 /* header pin 12 - bidirectional (open-drain) */ #define IEC_GPIO_DATA 2 /* header pin 3 - bidirectional (open-drain) */
/* --- BCM2710A1 / BCM2837 (Pi Zero 2 W) peripheral map ------------------- */ /* --- BCM2710A1 / BCM2837 (Pi Zero 2 W) peripheral map ------------------- */
/* Same peripheral base as RPi 3. RPi 1 = 0x20000000, RPi 4 = 0xFE000000. */ /* Same peripheral base as RPi 3. RPi 1 = 0x20000000, RPi 4 = 0xFE000000. */

View File

@ -189,8 +189,9 @@ if [ "$ST" -eq 0 ]; then
ok "self-test PASSED (0x1F) - all four lines wired correctly" ok "self-test PASSED (0x1F) - all four lines wired correctly"
else else
info "self-test did not fully pass." info "self-test did not fully pass."
info "On a BARE board 0x15 is expected and fine: only DATA_ASSERT_OK is" info "On a BARE board 0x0b is expected and fine: DATA_FLOAT_OK + CLK_RELEASED"
info "meaningful; ATN/RESET pass via the SoC's fixed GPIO2/3 pull-ups." info "pass via the SoC's fixed GPIO2/3 (DATA/CLK) pull-ups, and DATA_ASSERT_OK"
info "is internal; ATN/RESET read low (GPIO4/17 have no built-in pull-up)."
info "Re-run once the level shifter is wired + powered; you want 0x1F." info "Re-run once the level shifter is wired + powered; you want 0x1F."
fi fi
exit "$ST" exit "$ST"