From b2e4f9710dbcf64d07016ed2d60ca682aa10233a Mon Sep 17 00:00:00 2001 From: Christian Werner Date: Sat, 20 Jun 2026 16:09:10 +0200 Subject: [PATCH] refactor(kernel): align GPIO pin layout with the reference listener 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 --- _plans/poc-listener-printer-PLAN.md | 8 ++++---- docs/kernel-notes.md | 4 ++-- docs/wiring.md | 22 ++++++++++++++-------- kernel/README.md | 27 +++++++++++++++------------ kernel/dts/iec-overlay.dts | 17 +++++++++++------ kernel/iec_lines.h | 12 ++++++------ kernel/selftest.sh | 5 +++-- 7 files changed, 55 insertions(+), 40 deletions(-) diff --git a/_plans/poc-listener-printer-PLAN.md b/_plans/poc-listener-printer-PLAN.md index 926b51e..f087003 100644 --- a/_plans/poc-listener-printer-PLAN.md +++ b/_plans/poc-listener-printer-PLAN.md @@ -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 | |------------|---------------|------------|-----------|------------| -| ATN | GPIO 2 | 3 | input (divider) | bus low = Pi low = asserted | -| CLK | GPIO 17 | 11 | input (divider) | bus low = Pi low = asserted | -| DATA (in) | GPIO 18 | 12 | output → 7406 → bus | Pi HIGH = bus asserted (inverted) | -| RESET | GPIO 3 | 5 | input (divider) | bus low = Pi low = asserted | +| ATN | GPIO 4 | 7 | input (divider) | bus low = Pi low = asserted | +| CLK | GPIO 3 | 5 | input (divider) | bus low = Pi low = asserted | +| DATA (in) | GPIO 2 | 3 | output → 7406 → bus | Pi HIGH = bus asserted (inverted) | +| RESET | GPIO 17 | 11 | input (divider) | bus low = Pi low = asserted | | GND | — | 6 (or any) | — | tie to IEC pin 2 | > Convention used in code: a helper layer converts electrical reads/writes into diff --git a/docs/kernel-notes.md b/docs/kernel-notes.md index b2ed73f..a70b95f 100644 --- a/docs/kernel-notes.md +++ b/docs/kernel-notes.md @@ -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) ``` -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 (512–565), so every `gpio_to_desc()` returned `NULL`, the descriptor check 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. Notes: diff --git a/docs/wiring.md b/docs/wiring.md index b1ceb7b..b6e9893 100644 --- a/docs/wiring.md +++ b/docs/wiring.md @@ -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 | |------------|-------------|---------------|------------|-----------| -| ATN | 3 | GPIO 2 | 3 | input (via shifter) | -| CLK | 4 | GPIO 17 | 11 | input (via shifter) | -| DATA | 5 | GPIO 18 | 12 | **bidirectional** (via shifter) | -| RESET | 6 | GPIO 3 | 5 | input (via shifter) | +| ATN | 3 | GPIO 4 | 7 | input (via shifter) | +| CLK | 4 | GPIO 3 | 5 | input (via shifter) | +| DATA | 5 | GPIO 2 | 3 | **bidirectional** (via shifter) | +| RESET | 6 | GPIO 17 | 11 | input (via shifter) | | GND | 2 | GND | 6 (or any) | — | | 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 -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. -- **release / read**: GPIO 18 = **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 +- **assert** (pull bus low): GPIO 2 = output **LOW** → shifter pulls bus to 0 V. +- **release / read**: GPIO 2 = **input (Hi-Z)** → bus pull-ups float it to 5 V, + and GPIO 2 reads the level the *talker* (C64) is driving — this is how bits are sampled during receive. Logical mapping (handled in `kernel/iec_lines.h`): asserted ⇔ Pi reads LOW. diff --git a/kernel/README.md b/kernel/README.md index 97c7912..6d56d7d 100644 --- a/kernel/README.md +++ b/kernel/README.md @@ -92,18 +92,21 @@ Constants are defined in [`iec_listener.h`](iec_listener.h); the ioctl number is ### ⚠️ 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. -- `ATN_RELEASED` (`0x04`) and `RESET_RELEASED` (`0x10`) pass **only because - GPIO2/GPIO3 have fixed ~1.8 kΩ pull-ups built into the BCM2710 SoC** (they are - the I²C0 pins; the pull-ups can't be disabled). They read high even with - nothing wired, so on a bare board these two bits prove **nothing** about your - wiring. -- `DATA_FLOAT_OK` (`0x02`) and `CLK_RELEASED` (`0x08`) read low because GPIO17/18 - have no such built-in pull-up and nothing is attached. +- `DATA_FLOAT_OK` (`0x02`) and `CLK_RELEASED` (`0x08`) pass **only because + DATA=GPIO2 and CLK=GPIO3 have fixed ~1.8 kΩ pull-ups built into the BCM2710 + SoC** (they are the I²C pins; the pull-ups can't be disabled). They read high + even with nothing wired, so on a bare board these two bits prove **nothing** + about your wiring. In particular `DATA_FLOAT_OK` now floats high via the SoC + pull-up even without the shifter, so it no longer confirms the shifter's DATA + 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` -is only reachable once the level shifter (with its CLK/DATA pull-ups) is wired -and powered. To make ATN/RESET meaningful, briefly ground each at the connector -and confirm the corresponding bit *drops*. +So on a bare board the only fully internal signal is `DATA_ASSERT_OK`. A full +`0x1F` is only reachable once the level shifter (with its ATN/RESET pull-ups) is +wired and powered. To make ATN/RESET meaningful, briefly ground each at the +connector and confirm the corresponding bit *drops*. diff --git a/kernel/dts/iec-overlay.dts b/kernel/dts/iec-overlay.dts index b003222..c2fe67d 100644 --- a/kernel/dts/iec-overlay.dts +++ b/kernel/dts/iec-overlay.dts @@ -1,10 +1,15 @@ /* * iec-overlay.dts - pin reservation + pull configuration for the IEC listener. * - * GPIO 2 = ATN (input, pull-up: bus idle = 5 V = high) - * GPIO 17 = CLK (input, pull-up) - * GPIO 3 = RESET (input, pull-up) - * GPIO 18 = DATA (bidirectional; starts as input/Hi-Z, the module flips it) + * GPIO 4 = ATN (input, pull-up: bus idle = 5 V = high) + * GPIO 3 = CLK (input, pull-up) + * GPIO 17 = RESET (input, pull-up) + * 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 * reserve the pins and set sane pull defaults. Build & install: @@ -25,12 +30,12 @@ target = <&gpio>; __overlay__ { 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,pull = <2>; /* 2 = pull-up */ }; iec_data_pin: iec_data_pin { - brcm,pins = <18>; /* DATA */ + brcm,pins = <2>; /* DATA */ brcm,function = <0>; /* start as input (Hi-Z) */ brcm,pull = <0>; /* 0 = none (shifter drives) */ }; diff --git a/kernel/iec_lines.h b/kernel/iec_lines.h index 0d98e89..3dff4e1 100644 --- a/kernel/iec_lines.h +++ b/kernel/iec_lines.h @@ -10,8 +10,8 @@ * bus released (5 V) <=> Pi GPIO reads HIGH * * DATA is open-drain emulated on a single bidirectional pin: - * assert : drive GPIO18 OUTPUT LOW -> shifter pulls bus to 0 V - * release : set GPIO18 INPUT (Hi-Z) -> bus pull-ups float it to 5 V; + * assert : drive GPIO2 OUTPUT LOW -> shifter pulls bus to 0 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 hot path uses direct BCM register access (ioremap'd), which the kernel @@ -30,10 +30,10 @@ #include /* --- GPIO assignment (BCM numbering, PLAN.md §3.2) ----------------------- */ -#define IEC_GPIO_ATN 2 /* header pin 3 - input */ -#define IEC_GPIO_RESET 3 /* header pin 5 - input */ -#define IEC_GPIO_CLK 17 /* header pin 11 - input */ -#define IEC_GPIO_DATA 18 /* header pin 12 - bidirectional (open-drain) */ +#define IEC_GPIO_ATN 4 /* header pin 7 - input */ +#define IEC_GPIO_RESET 17 /* header pin 11 - input */ +#define IEC_GPIO_CLK 3 /* header pin 5 - input */ +#define IEC_GPIO_DATA 2 /* header pin 3 - bidirectional (open-drain) */ /* --- BCM2710A1 / BCM2837 (Pi Zero 2 W) peripheral map ------------------- */ /* Same peripheral base as RPi 3. RPi 1 = 0x20000000, RPi 4 = 0xFE000000. */ diff --git a/kernel/selftest.sh b/kernel/selftest.sh index 1b39b7a..48d6fef 100755 --- a/kernel/selftest.sh +++ b/kernel/selftest.sh @@ -189,8 +189,9 @@ if [ "$ST" -eq 0 ]; then ok "self-test PASSED (0x1F) - all four lines wired correctly" else info "self-test did not fully pass." - info "On a BARE board 0x15 is expected and fine: only DATA_ASSERT_OK is" - info "meaningful; ATN/RESET pass via the SoC's fixed GPIO2/3 pull-ups." + info "On a BARE board 0x0b is expected and fine: DATA_FLOAT_OK + CLK_RELEASED" + 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." fi exit "$ST"