diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-02-26 14:26:28 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-03-02 13:30:55 -0700 |
commit | 8dc86e5ec58ec94f7ea8227ee008ded202c4204f (patch) | |
tree | 02ab6704d2ad5698c45981162cc2c192ebdee9db | |
parent | 0110cae147d8b1749b4bd5763620ec73d5296a76 (diff) |
libhw: Add rp2040_gpioirq, use it
-rwxr-xr-x | build-aux/lint-bin | 20 | ||||
-rwxr-xr-x | build-aux/stack.c.gen | 16 | ||||
-rw-r--r-- | cmd/sbc_harness/config/config.h | 7 | ||||
-rw-r--r-- | libhw/CMakeLists.txt | 5 | ||||
-rw-r--r-- | libhw/rp2040_gpioirq.c | 75 | ||||
-rw-r--r-- | libhw/rp2040_gpioirq.h | 33 | ||||
-rw-r--r-- | libhw/w5500.c | 25 |
7 files changed, 141 insertions, 40 deletions
diff --git a/build-aux/lint-bin b/build-aux/lint-bin index 0b955de..c487f36 100755 --- a/build-aux/lint-bin +++ b/build-aux/lint-bin @@ -25,7 +25,7 @@ RESET=$(tput sgr0) err() { printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2 - #r=1 + r=1 } # Input is `ld --print-map` format. @@ -107,6 +107,21 @@ lint_stack() { <(readelf_funcs "$in_elffile" | sed 's/\.part\.[0-9]*$//' | sort -u)) } +lint_func_blocklist() { + local in_elffile + in_elffile=$1 + + local blocklist=( + gpio_default_irq_handler + ) + + while read -r func; do + err "$in_elffile" "Contains blocklisted function: ${func}" + done < <(readelf --syms --wide -- "$in_elffile" | + awk '$4 == "FUNC" { print $8 }' | + grep -Fx "${blocklist[@]/#/-e}") +} + main() { r=0 @@ -116,7 +131,8 @@ main() { echo 'Global variables:' lint_globals "${elf}.map" | sed 's/^/ /' } > "${elf%.elf}.lint.globals" - lint_stack "$elf" &> "${elf%.elf}.lint.stack" + (lint_stack "$elf") &> "${elf%.elf}.lint.stack" + lint_func_blocklist "$elf" done return $r diff --git a/build-aux/stack.c.gen b/build-aux/stack.c.gen index c73a8bb..66d837a 100755 --- a/build-aux/stack.c.gen +++ b/build-aux/stack.c.gen @@ -504,6 +504,7 @@ class LibHWPlugin: "rp2040_hwtimer_intrhandler", "hostclock_handle_sig_alarm", "hostnet_handle_sig_io", + "gpioirq_handler", ] def extra_nodes(self) -> typing.Collection[Node]: @@ -522,6 +523,10 @@ class LibHWPlugin: "w5500_udp_alarm_handler", ] return ret, False + if "/rp2040_gpioirq.c:" in loc and "handler->fn" in line: + return [ + "w5500_intrhandler", + ], False return None def skip_call(self, chain: list[str], call: str) -> bool: @@ -711,17 +716,14 @@ class LibMiscPlugin: class PicoSDKPlugin: - app_gpio_handlers: typing.Collection[str] app_init_array: typing.Collection[str] app_preinit_array: typing.Collection[str] def __init__( self, *, - app_gpio_handlers: typing.Collection[str], app_init_array: typing.Collection[str], ) -> None: - self.app_gpio_handlers = app_gpio_handlers self.app_init_array = app_init_array # git grep '^PICO_RUNTIME_INIT_FUNC\S*(' @@ -754,7 +756,6 @@ class PicoSDKPlugin: def is_intrhandler(self, name: str) -> bool: return name in [ - "gpio_default_irq_handler", "isr_invalid", "isr_nmi", "isr_hardfault", @@ -783,8 +784,6 @@ class PicoSDKPlugin: return ["rom_hword_as_ptr(BOOTROM_TABLE_LOOKUP_OFFSET)"], False if "/flash.c:" in loc and "boot2_copyout" in line: return ["_stage2_boot"], False - if "/gpio.c:" in loc and call == "callback": - return sorted(self.app_gpio_handlers), False if "/printf.c:" in loc: if call == "out": return [ @@ -1137,10 +1136,6 @@ def main( lib9p_plugin = Lib9PPlugin(arg_base_dir, arg_c_fnames) - sbc_gpio_handlers = [ - "w5500_intrhandler", - ] - def sbc_is_thread(name: str) -> int: if name.endswith("_cr") and name != "lib9p_srv_read_cr": if "9p" in name: @@ -1165,7 +1160,6 @@ def main( if arg_pico_platform == "rp2040": plugins += [ PicoSDKPlugin( - app_gpio_handlers=sbc_gpio_handlers, app_init_array=["register_fini"], ), TinyUSBDevicePlugin(arg_c_fnames), diff --git a/cmd/sbc_harness/config/config.h b/cmd/sbc_harness/config/config.h index b569cd5..f9c7df2 100644 --- a/cmd/sbc_harness/config/config.h +++ b/cmd/sbc_harness/config/config.h @@ -12,11 +12,6 @@ /* W5500 **********************************************************************/ /** - * How many W5500 chips we have. - */ -#define CONFIG_W5500_NUM 1 - -/** * When allocating an arbitrary local port, what range should it be * allocated from? * @@ -106,7 +101,7 @@ extern const size_t CONFIG_COROUTINE_STACK_SIZE_w5500_irq_cr; #define CONFIG_COROUTINE_NUM ( \ 1 /* usb_common */ + \ 1 /* usb_keyboard */ + \ - CONFIG_W5500_NUM /* irq handler */ + \ + 1 /* W5500 irq handler */ + \ _CONFIG_9P_NUM_SOCKS /* 9P accept()+read() */ + \ (CONFIG_9P_SRV_MAX_REQS*_CONFIG_9P_NUM_SOCKS) /* 9P work+write() */ ) diff --git a/libhw/CMakeLists.txt b/libhw/CMakeLists.txt index d1767da..bd92e04 100644 --- a/libhw/CMakeLists.txt +++ b/libhw/CMakeLists.txt @@ -1,6 +1,6 @@ # libhw/CMakeLists.txt - Device drivers # -# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> +# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com> # SPDX-License-Identifier: AGPL-3.0-or-later add_library(libhw INTERFACE) @@ -14,8 +14,9 @@ if (PICO_PLATFORM STREQUAL "rp2040") libcr_ipc ) target_sources(libhw INTERFACE - rp2040_hwtimer.c + rp2040_gpioirq.c rp2040_hwspi.c + rp2040_hwtimer.c w5500.c ) target_link_libraries(libhw INTERFACE diff --git a/libhw/rp2040_gpioirq.c b/libhw/rp2040_gpioirq.c new file mode 100644 index 0000000..2f0ceac --- /dev/null +++ b/libhw/rp2040_gpioirq.c @@ -0,0 +1,75 @@ +/* libhw/rp2040_gpioirq.c - Utilities for sharing the GPIO IRQ (IO_IRQ_BANK0) + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#include <hardware/structs/io_bank0.h> /* for io_bank0_hw */ +#include <hardware/irq.h> /* for irq_set_exclusive_handler() */ + +#include <libmisc/macro.h> + +#include "rp2040_gpioirq.h" + +struct gpioirq_handler_entry { + gpioirq_handler_t fn; + void *arg; +}; +struct gpioirq_handler_entry gpioirq_handlers[NUM_BANK0_GPIOS][4] = {0}; + +int gpioirq_core = -1; + +static void gpioirq_handler(void) { + uint core = get_core_num(); + io_bank0_irq_ctrl_hw_t *irq_ctrl_base; + switch (core) { + case 0: irq_ctrl_base = &io_bank0_hw->proc0_irq_ctrl; break; + case 1: irq_ctrl_base = &io_bank0_hw->proc1_irq_ctrl; break; + default: assert_notreached("invalid core number"); + } + for (uint regnum = 0; regnum < LM_ARRAY_LEN(irq_ctrl_base->ints); regnum++) { + uint32_t regval = irq_ctrl_base->ints[regnum]; + for (uint bit = 0; bit < 32 && (regnum*8)+(bit/4) < NUM_BANK0_GPIOS; bit++) { + if (regval & 1u<<bit) { + uint gpio = (regnum*8)+(bit/4); + uint event_idx = bit%4; + struct gpioirq_handler_entry *handler = &gpioirq_handlers[gpio][event_idx]; + if (handler->fn) + handler->fn(handler->arg, gpio, 1u<<event_idx); + } + } + /* acknowledge irq */ + io_bank0_hw->intr[regnum] = regval; + } +} + +void gpioirq_set_and_enable_exclusive_handler(uint gpio, enum gpio_irq_level event, gpioirq_handler_t fn, void *arg) { + assert(gpio < NUM_BANK0_GPIOS); + assert(event == GPIO_IRQ_LEVEL_LOW || + event == GPIO_IRQ_LEVEL_HIGH || + event == GPIO_IRQ_EDGE_FALL || + event == GPIO_IRQ_EDGE_RISE); + assert(fn); + + uint event_idx = LM_FLOORLOG2(event); + assert(gpioirq_handlers[gpio][event_idx].fn == NULL); + + uint core = get_core_num(); + assert(gpioirq_core == -1 || gpioirq_core == (int)core); + + io_bank0_irq_ctrl_hw_t *irq_ctrl_base; + switch (core) { + case 0: irq_ctrl_base = &io_bank0_hw->proc0_irq_ctrl; break; + case 1: irq_ctrl_base = &io_bank0_hw->proc1_irq_ctrl; break; + default: assert_notreached("invalid core number"); + } + + gpioirq_handlers[gpio][event_idx].fn = fn; + gpioirq_handlers[gpio][event_idx].arg = arg; + hw_set_bits(&irq_ctrl_base->inte[gpio/8], 1u<<((4*(gpio%8))+event_idx)); + if (gpioirq_core == -1) { + irq_set_exclusive_handler(IO_IRQ_BANK0, gpioirq_handler); + irq_set_enabled(IO_IRQ_BANK0, true); + gpioirq_core = core; + } +} diff --git a/libhw/rp2040_gpioirq.h b/libhw/rp2040_gpioirq.h new file mode 100644 index 0000000..06041c9 --- /dev/null +++ b/libhw/rp2040_gpioirq.h @@ -0,0 +1,33 @@ +/* libhw/rp2040_gpioirq.h - Utilities for sharing the GPIO IRQ (IO_IRQ_BANK0) + * + * Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBHW_RP2040_GPIOIRQ_H_ +#define _LIBHW_RP2040_GPIOIRQ_H_ + +#include <hardware/gpio.h> /* for enum gpio_irq_level */ + +typedef void (*gpioirq_handler_t)(void *arg, uint gpio, enum gpio_irq_level event); + +/** + * Register `fn(arg, ...)` to be called when `event` fires on GPIO pin + * `gpio`. + * + * If multiple events happen close together, the handlers will not + * necessarily be called in-order. + * + * Your handler does not need to acknowledge the IRQ; that will be + * done for you after your handler is called. + * + * It is illegal to call gpioirq_set_and_enable_exclusive_handler() + * on multiple cores. + * + * This is better than the Pico-SDK <hardware/gpio.h> IRQ functions + * because their functions call the handlers for every event, and it + * is up to you to de-mux them in your handler. + */ +void gpioirq_set_and_enable_exclusive_handler(uint gpio, enum gpio_irq_level event, gpioirq_handler_t fn, void *arg); + +#endif /* _LIBHW_RP2040_GPIOIRQ_H_ */ diff --git a/libhw/w5500.c b/libhw/w5500.c index 64b373b..b4ad86b 100644 --- a/libhw/w5500.c +++ b/libhw/w5500.c @@ -72,6 +72,7 @@ /* TODO: Write a <libhw/generic/gpio.h> to avoid w5500.c being * pico-sdk-specific. */ #include <hardware/gpio.h> /* pico-sdk:hardware_gpio */ +#include "rp2040_gpioirq.h" #include <libcr/coroutine.h> /* for cr_yield() */ @@ -95,9 +96,6 @@ #ifndef CONFIG_W5500_LOCAL_PORT_MAX #error config.h must define CONFIG_W5500_LOCAL_PORT_MAX #endif -#ifndef CONFIG_W5500_NUM - #error config.h must define CONFIG_W5500_NUM -#endif #ifndef CONFIG_W5500_DEBUG #error config.h must define CONFIG_W5500_DEBUG #endif @@ -280,13 +278,10 @@ static inline void w5500_socket_close(struct _w5500_socket *socket) { /* init() *********************************************************************/ -static struct w5500 *w5500_chips[CONFIG_W5500_NUM] = {0}; - -static void w5500_intrhandler(uint gpio, uint32_t LM_UNUSED(event_mask)) { - debugf("w5500_intrhandler(): interrupt on pin %u", gpio); - for (size_t i = 0; i < LM_ARRAY_LEN(w5500_chips); i++) - if (w5500_chips[i] && w5500_chips[i]->pin_intr == gpio) - cr_sema_signal_from_intrhandler(&w5500_chips[i]->intr); +static void w5500_intrhandler(void *_chip, uint LM_UNUSED(gpio), enum gpio_irq_level LM_UNUSED(event)) { + struct w5500 *chip = _chip; + debugf("w5500_intrhandler()"); + cr_sema_signal_from_intrhandler(&chip->intr); } void _w5500_init(struct w5500 *chip, @@ -318,19 +313,11 @@ void _w5500_init(struct w5500 *chip, } /* Initialize the hardware. */ - gpio_set_irq_enabled_with_callback(pin_intr, GPIO_IRQ_EDGE_FALL, true, w5500_intrhandler); + gpioirq_set_and_enable_exclusive_handler(pin_intr, GPIO_IRQ_EDGE_FALL, w5500_intrhandler, chip); gpio_set_dir(chip->pin_reset, GPIO_OUT); w5500_hard_reset(chip); /* Finally, wire in the interrupt handler. */ - bool saved = cr_save_and_disable_interrupts(); - for (size_t i = 0; i < LM_ARRAY_LEN(w5500_chips); i++) { - if (w5500_chips[i] == NULL) { - w5500_chips[i] = chip; - break; - } - } - cr_restore_interrupts(saved); coroutine_add("w5500_irq", w5500_irq_cr, chip); } |