/* SPDX-License-Identifier: GPL-2.0 */ /* * iec_lines.h - logical IEC line layer (PLAN.md §5.1). * * The handshake code reasons in protocol true/false terms (asserted / released) * and never juggles electrical inversions inline. With the PLAN's non-inverting * bidirectional level shifter (sd2iec-style single DATA pin, §3.1): * * bus asserted (0 V) <=> Pi GPIO reads LOW * 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; * 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 * research found to be ~30-40x faster than the gpiod descriptor API and is what * ninepin/raspbiec use inside the bit loop. * * NOTE: this differs from the 7406 *inverting* scheme analysed in * _research/pi-kernel-module-rt-gpio-2026-06-18.md §4.3. The PLAN deliberately * chose the non-inverting bidirectional shifter, which also closes that doc's * "DATA sensing gap" (reading back through the shifter while the pin is an input * IS valid). See docs/kernel-notes.md. */ #ifndef _IEC_LINES_H #define _IEC_LINES_H #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) */ /* --- BCM2710A1 / BCM2837 (Pi Zero 2 W) peripheral map ------------------- */ /* Same peripheral base as RPi 3. RPi 1 = 0x20000000, RPi 4 = 0xFE000000. */ #define IEC_BCM_PERI_BASE 0x3F000000UL #define IEC_GPIO_BASE (IEC_BCM_PERI_BASE + 0x200000UL) #define IEC_GPIO_LEN 0x1000 /* Register word offsets (multiply by 4 for byte offset) */ #define GPFSEL0 0 /* function select base (3 bits/pin, 10 pins/reg) */ #define GPSET0 7 /* output set (write 1 to set pin high) */ #define GPCLR0 10 /* output clear (write 1 to set pin low) */ #define GPLEV0 13 /* pin level (read) */ /* ioremap'd base of the GPIO block; set in module init. */ extern u32 __iomem *iec_gpio; /* --- low-level direct register helpers ---------------------------------- */ static inline int iec_gpio_read(unsigned int pin) { return (ioread32(iec_gpio + GPLEV0) >> (pin & 31)) & 1; } static inline void iec_gpio_set(unsigned int pin) /* drive high */ { iowrite32(1u << (pin & 31), iec_gpio + GPSET0); } static inline void iec_gpio_clr(unsigned int pin) /* drive low */ { iowrite32(1u << (pin & 31), iec_gpio + GPCLR0); } /* Set the 3-bit function field for a pin: 0 = input, 1 = output. */ static inline void iec_gpio_fsel(unsigned int pin, unsigned int fn) { u32 __iomem *reg = iec_gpio + GPFSEL0 + (pin / 10); unsigned int shift = (pin % 10) * 3; u32 v = ioread32(reg); v &= ~(0x7u << shift); v |= (fn & 0x7u) << shift; iowrite32(v, reg); } #define IEC_FSEL_INPUT 0 #define IEC_FSEL_OUTPUT 1 /* --- logical (protocol-level) line operations --------------------------- */ /* sense lines: asserted == Pi reads LOW (non-inverting shifter) */ static inline bool iec_atn_asserted(void) { return iec_gpio_read(IEC_GPIO_ATN) == 0; } static inline bool iec_clk_asserted(void) { return iec_gpio_read(IEC_GPIO_CLK) == 0; } static inline bool iec_reset_asserted(void) { return iec_gpio_read(IEC_GPIO_RESET) == 0; } /* DATA, open-drain emulation on the single bidirectional pin */ static inline void iec_data_assert(void) /* pull bus low */ { iec_gpio_clr(IEC_GPIO_DATA); /* preload output latch LOW */ iec_gpio_fsel(IEC_GPIO_DATA, IEC_FSEL_OUTPUT); } static inline void iec_data_release(void) /* float bus high (Hi-Z) */ { iec_gpio_fsel(IEC_GPIO_DATA, IEC_FSEL_INPUT); } /* read the DATA bus state (valid only after iec_data_release()) */ static inline bool iec_data_asserted(void) { return iec_gpio_read(IEC_GPIO_DATA) == 0; } #endif /* _IEC_LINES_H */