Squashed 'third_party/pico-sdk/' changes from 2062372d20..2e6142b15b

2e6142b15b SDK 1.4.0 release
5e9a5e827b Add Pico W and lwIP support
77c04e458c revert TinyUSB update (#889)
bc5d1b8485 Add channel_config_set_high_priority (#888)
85dbbfdf4d Update TinyUSB to commit 39069cf4b to pick up recent RP2040 fixes (#886)
e7267f99fe Fix GPIO # callback parameter (#880)
4c49427bf3 Fix auto_init_recursive_mutex definition for C++ (#875)
33818dd0bd Increase PLL min VCO from 400MHz to 750MHz for improved stability across operating conditions (#869)
8f09099757 sem_acquire has no reason to do a notify! (#857)
9644399993 Suppress new GCC 12 warning (#842)
b3c56e7169 Add stderr support and remove 1us timeout for timeouts of 0us (#858)
7858601a58 stdio_usb improvements (#871)
0bdd463898 Add DatanoiseTV DSP Board. (#866)
7daa20ce4c Add board definition for the RP2040 Stamp Round Carrier (#837)
705b5cedcd Wrap realloc() call with malloc_mutex in multicore (#864)
bdd9746635 Don't copy .data for NO_FLASH binaries, as it's loaded in-place (#859)
babc4a1794 Remove 'default=none' from PICO_CONFIG lines (#865)
6c19d20aa5 Fix up filename displayed by extract_config.py when an invalid attribute is found (#853)
672e48e9e9 Add sem_try_acquire(). Fixes #846 (#856)
ef47dfeeaf Add new GPIO APIs for adding shared GPIO handlers, and improve docs (#850)
6389927cf9 Add some optional header includes, to make per-target changes to config easier (#851)
d3dcbb8292 Add new user_irq claim APIs to make it easier for independent code using them to interoperate (#854)
f3c446ae14 move get_core_num() into platform.h (#852)
4e4cf11d9b Start SDK 1.3.2 development
426e46126b SDK 1.3.1 release
ebc601f71f Re-merge SDK1.3.0 history back into SDK1.3.1
1e6c122fc9 gate inclusion of pico.h in binary info, so as not to break picotool builds (#836)
7880405292 Doxygen typo (#835)
530204ec0c Small tweak to busy_wait_at_least_cycles doxygen (#834)
ea79d29cd6 Merge non-squashed history of 1.3.0 release back in to master
b2ad632c24 Minor additions to PIO documentation (#831)
658a21b946 Update rtc_set_datetime docs to point out that the written value may not be immediately visible (#832)
e44d2c0010 Add busy_wait_at_least_cycles method (#830)
43a5593e8f Pass PIOASM_EXTRA_SOURCE_FILES to Pioasm sub-cmake and add OUTPUT_FORMAT option (fixes #827) (#828)
792813926d Make pioasm accept windows CRLF input (#829)
159d552150 Fix bug in irq_remove_shared_handler and add test #823 (#825)
3a3d5fe6c4 Small IRQ doxygen tweaks (#824)
5e22c09660 convert unlikely panic to hard_assert to not waste space for string (#826)
4e62c26a2c Add documentation to dma_channel_abort regarding errata RP2040-E13 (#816)
5a927792ef Suppress false GCC 11 warning on TinyUSB (#819)
60a6fea534 Fix list management in stdio_set_driver_enabled so drivers can be freely added and removed (#822)
04c68c554b cleanup pico_stdlib_test. add actual check for __builtin bitops (#821)
0d2591e1f1 Pad all but last sector in flash binary to write whole of sector (#800)
81bdcb6681 print build type during CMake config; remove spurious call to pico_is_toplevel_project() (#818)
9c616da1e5 fix bug in pico_float_test (#817)
f260477802 Adapt assembler syntax to eliminate Clang errors. (LLVM 14.0.0) (#798)
e1c5fd34e4 PLL setup check bug (#796) (#806)
5d422deed1 Add extra info about address_range defined values (#652)
0c501c246a Add wiznet_w5100s_evb_pico support (#666)
259da19660 Include structs/iobank0.h from hardware/gpio.h (#733) (#807)
5325008956 Added board definition for upcoming Pimoroni motor driver (#795)
510ca47c92 Additional PWM validation and small tidy-up (#801)
d5121dc880 Removed unnecessary ifdefs from Pimoroni boards (#802)
3c450011da Additional param-validation for PIO (#805)
6149b9e4ec Add board definition for Seeed Xiao RP2040 (#691)
8554fc4ea1 Add is_at_the_end_of_time() method (#784)
01290c4cb3 Added int_frac function for configuring PWM (#768)
8d43364cfb Small typos (#776)
1c9f72ba1c Small edits to the Doxygen mainpage and update the RPi logo (#778)
76c1830aeb Add gpio_deinit (#793)
c66a2c1c3b Remove implicit grouping for PIO defines (#779)
e4a2aa7262 Fix typo in binary_info description (#788)
fc86203f49 comment typo (#794)
6619a2d329 Typo (#799)
5a427fbf1f irq_add_shared_handler assertion fix #724 (#747)
bb5a2a3906 Added definition for Badger RP2040 board (#735)
8291f1013a add #ifdef guards to vgaboard.h to allow user overrides (#746)
e90f831756 add board definition of eetree gamekit rp2040 (#732)
541f93541c improve comment (#658)
17d53af105 Allow 252-byte binaries to be checksummed (#764)
1896b1491b fix __ctzdi2 for values with non-zero low 32 bits (fixes #765) (#766)
3d45276423 Added board definition for upcoming Pimoroni servo driver (#769)
d35083b3f7 Fix typo in SIO register description (#770)
38b26b5d65 pico_sdk_import: don't recurse git submodules (#772)
d54104a1e5 fix when PICO_NO_BINARY_INFO is set (#693)
bc3484e793 Mark __aeabi_lmul section as allocatable and executable (#722)
b7e82b638a Fix function-names reported by  hardware_pwm_test (#736)
bf9848eaf9 Updated DMA CTRL_TRIG.CHAIN_TO reset values (#743)
ae7ef546a6 Fix typo in Doxygen comment (#731)
b3cf3e8f6f Correct comment in time.h (#715)
25a3b36793 Fix assert in adc_set_round_robin() (#698)
96afce8ece Add board definition for Adafruit Macropad (#684)
5bcf4ed83a Clarify header-comments about pico_enable_stdio_ CMake-functions (#705)
e379f6764b Add board definition for RP2040 Stamp and Carrier (#696)
91462e430a Add `__attribute__((noreturn))` to `_exit` (#707)
d831eff5a2 fix pioasm python output (#694) (#695)
215f77b836 Moves the #if guards to before the comments (#690)
0f67a6c00c Add board definition for Adafruit KB2040 (#676)
add120e4f6 add missing change to dma.h from __force_inline fix (#680)
386e2a7bc6 Added Tiny2040 2MB (#672)
0562089fab fixup __forceinline for cpp code and add kitchen_sink cpp test (#670)
269332dc21 rename pybstick26_rp2040.h to garatronic_pybstick26_rp2040.h (#675)
a8fa19a74d pio_set_irqn_source_mask_enabled() sets wrong irq (#655)
699838d153 Start SDK 1.3.1 development (#648)
60c5a929cc Remove <b> from mutex doxygen due to PDF generation bug
d0af70bd77 fix __packed attribute for MSVC (#646)
da476610e2 allow override of XOSC_MHZ (#644)
d7358e4ed8 update generated struct headers to fix collision (#641)
ccccff8450 Add pybstick26_rp2040 support (#636)
d0c7642369 Update ADC ENOB comment to match (measured) value in RP2040 datasheet (#643)
cc1e2e8758 Small typo in kitchen_sink (#642)
e4d6ff9ecd Small PICO_PLATFORM=host fixes (#639)
97eec951b1 Another .org -> .com fix (#638)
fba9c8ce4d change SDK version to 1.3.0 (#628)
da7c39bc23 Fix some of the (simpler) errors reported by the doxygen command (#617)
b04a09110f revert additional assertion check which breaks used of hw_alias macro outside of functions (#635)
5641cd0a4a __aeabi_memset* were switching the wrong arguments (#634)
68555c9751 Added some waveshare boards definition (#619)
e5110dfce1 add docs for pio_instructions.h (#624)
3604a6fa13 add CONTRIBUTING.md (#626)
e850214938 Platform updates (#611)
723dfd04ff add more/better documentation to pico/multicore (#620)
05418b4e71 fix minor bug in add_repeating_timer_us, and add some comments (#621)
83cd1da1ef fix build if TinyUSB not present (#631)
0ccd0db163 Update to TinyUSB 0.12.0 (#622)
22b0d5d2ed Update links in Doxygen pages from .org to .com (#625)
3c72e753b6 Split recursive mutex into their own functions (was Reduce performance hit of recursive mutex) (#495)
9320d192c3 add watchdog_enable_caused_reboot (minor compatibility issue) (#594)
68571ad33e Hardware struct regeneration (#613)
a0450d0133 Updated reg_headers and SVD (#612)
3c94bc8137 Change _watchdog_enable to trigger immediate reboot when no delay set (#561)
f808b5f2dc Add DREQ methods for PWM/SPI/UART/I2C (#603)
2f2e62968d Use auto-generated hardware/structs/ headers (based off SVD) SVD errors fixed.
a793222331 add template PR - very polite (#596)
ec4036181f tweak info message for TinyUSB support (#590)
db47fba48d respect OUTPUT_NAME target property when generating supplemental files (#592)
a0d4bdf731 fix pico_set_printf_implementation(TARGET compiler) (#593)
d50e743e0e make type punning of floating point/int in float_math and double_math use union to avoid warnings (#600)
30673fb6d0 move inline assembly constant out into C code so it doesn't get out of range in a large function (#602)
e76d5a9008 add dma_timer related methods (#604)
9f1c37318b add irq_get_priority method - improve efficiency of irq_init_priorities (#609)
4c9ba3e8ad USB: Fix description of Length 0 in dpram_regs. Fixes #541 (#608)
ace97f3387 More accurate register access-types and reset-values (#601)
0fa58ed219 Adding/propage macros/signature typedefs for ROM functions. Make rom_func_lookup non-flash safe for flash functions (#586)
0a32023aac fix arg parsing segfault (#595)
13be546dc3 pico_stdio improvements (#598)
12017a07ab SPINLOCK regs are RW. (#599)
9319ab11d9 Add xip window alias macros (#566) (#585)
62854f5eff shrink max-page-size for linker to more sensible 4096 by default (#587)
13ed99df0d split out usb_reset_interface header into its own library for consumption by picotool or others (#589)
0fdf543126 Missing extern C in datetime.h #581 (#584)
3c53029c24 Fix some typos (#517)
ce0db40223 adafruit_feather_rp2040: Fix default I2C instance (#526)
a119b5bd9f Fix semihosting_putc (#530)
eb42ecfaa5 Added definitions for some upcoming Pimoroni LED driver boards (#535)
cd4e53a202 Use W25Q080 second stage loader for Nano RP2040 Connect (#537)
f63a14e9ee Change PWM_CH1_DIV_INT_LSB to PWM_CH0_DIV_INT_LSB (#560)
2214468b51 Add Adafruit Trinkey QT2040 board (#525)
e12713d70b pio_assembler: typo fix (#538)
e6b8c7b64e bugfix: "WO" registers should be listed in the SVD as write-only, not read-write (#544)
1d0dcc8177 pico_time: Fix alarm_pool_dump_key format string (#557)
46d8597d35 Add hardware_divider to pico_divider dependencies (#571)
d7feac1495 Fix syntax error in cmake when using custom stage2 (#580)
ca232e9404 Add Melopero Shake RP2040 Board header file (#565)
c08136b696 Move ret_dzero to the same section as it get used (#513)
672d18a6f0 Fix default PICO_BOARD value (#536)
2622e9bc29 Enable UART RX timeout IRQ, as well as RX IRQ, so that individual characters can be received.
9a586371db Add doxygen clarifying PWM behaviour when enabled/disabled, and advice for controlling the pin state when disabled (#521)
a6a436b1fe use PUBLIC for pio headers on any target except INTERFACE library
7e90980118 Add explanatory note on 7-bit I2C addresses (#520)
ae6e574f53 Improve wording of comments in irq.h (#509)
ccc0ba5649 elf2uf2: correctly parse ELF files where the program header is not directly following the file header
5e05469373 bug in sleep_until on host mode for macOS (#502)
dc4342f0f6 Update usb regs to fix https://github.com/raspberrypi/pico-feedback/issues/172
35b4965054 make sure PICO_DEFAULT_BOOT_STAGE2_FILE is defined in parent CMake file (#491)
e02c6b327d Allow one of float/double to have 'none' impl but not the other - previously caused a link error
f4ab723eb5 compile failure with PICO_STDOUT_MUTEX=0
529d7087eb fix operatore precedence of */ vs +- in pioasm
cefe5c5b2a Better support for PICO_DISABLE_SHARED_IRQ_HANDLERS (#496)
edcb65c916 Enable I2C FIFO full hold in slave mode (stretch clock when RX full), fixes #456 (#494)
f16ccfa1ff Small comment typos
44feae4caf set DIVISOR for each use of h/w divider
61b7cbdc54 Using ' inline __always_inline' also for GNUC 7. (#484)
21bbaf3ca6 remove -Winline (#481)
4328b2c75f fix pioasm python output (#479)
1f1c6162cd Header info string updates for PIO DBG_PADx and XOSC STARTUP (#478)
979045dc12 removed CMSIS from SDK doxygen
d42e6a9d10 include pico.h so that uint is defined (#475)
654b66693f start 1.3.0 development

git-subtree-dir: third_party/pico-sdk
git-subtree-split: 2e6142b15b8a75c1227dd3edbe839193b2bf9041
Signed-off-by: Ravago Jones <ravagojones@gmail.com>
Change-Id: I7d5ab3e7e797e8e956d3a651b600448157ab0608
diff --git a/src/rp2_common/hardware_gpio/CMakeLists.txt b/src/rp2_common/hardware_gpio/CMakeLists.txt
index 1bfb078..97a9355 100644
--- a/src/rp2_common/hardware_gpio/CMakeLists.txt
+++ b/src/rp2_common/hardware_gpio/CMakeLists.txt
@@ -1 +1,2 @@
-pico_simple_hardware_target(gpio)
\ No newline at end of file
+pico_simple_hardware_target(gpio)
+target_link_libraries(hardware_gpio INTERFACE hardware_irq)
\ No newline at end of file
diff --git a/src/rp2_common/hardware_gpio/gpio.c b/src/rp2_common/hardware_gpio/gpio.c
index 2816b97..6e4261f 100644
--- a/src/rp2_common/hardware_gpio/gpio.c
+++ b/src/rp2_common/hardware_gpio/gpio.c
@@ -14,11 +14,13 @@
 #include "pico/binary_info.h"
 #endif
 
-static gpio_irq_callback_t _callbacks[NUM_CORES];
+static gpio_irq_callback_t callbacks[NUM_CORES];
+// a 1 bit means the IRQ is handled by a raw IRQ handler
+static uint32_t raw_irq_mask[NUM_CORES];
 
 // Get the raw value from the pin, bypassing any muxing or overrides.
 int gpio_get_pad(uint gpio) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_set_bits(&padsbank0_hw->io[gpio], PADS_BANK0_GPIO0_IE_BITS);
     return (iobank0_hw->io[gpio].status & IO_BANK0_GPIO0_STATUS_INFROMPAD_BITS)
             >> IO_BANK0_GPIO0_STATUS_INFROMPAD_LSB;
@@ -28,7 +30,7 @@
 // Select function for this GPIO, and ensure input/output are enabled at the pad.
 // This also clears the input/output/irq override bits.
 void gpio_set_function(uint gpio, enum gpio_function fn) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     invalid_params_if(GPIO, ((uint32_t)fn << IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) & ~IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS);
     // Set input enable on, output disable off
     hw_write_masked(&padsbank0_hw->io[gpio],
@@ -42,14 +44,14 @@
 /// \end::gpio_set_function[]
 
 enum gpio_function gpio_get_function(uint gpio) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     return (enum gpio_function) ((iobank0_hw->io[gpio].ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB);
 }
 
 // Note that, on RP2040, setting both pulls enables a "bus keep" function,
 // i.e. weak pull to whatever is current high/low state of GPIO.
 void gpio_set_pulls(uint gpio, bool up, bool down) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_write_masked(
             &padsbank0_hw->io[gpio],
             (bool_to_bit(up) << PADS_BANK0_GPIO0_PUE_LSB) | (bool_to_bit(down) << PADS_BANK0_GPIO0_PDE_LSB),
@@ -59,7 +61,7 @@
 
 // Direct override for per-GPIO IRQ signal
 void gpio_set_irqover(uint gpio, uint value) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_write_masked(&iobank0_hw->io[gpio].ctrl,
                    value << IO_BANK0_GPIO0_CTRL_IRQOVER_LSB,
                    IO_BANK0_GPIO0_CTRL_IRQOVER_BITS
@@ -68,7 +70,7 @@
 
 // Direct overrides for pad controls
 void gpio_set_inover(uint gpio, uint value) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_write_masked(&iobank0_hw->io[gpio].ctrl,
                    value << IO_BANK0_GPIO0_CTRL_INOVER_LSB,
                    IO_BANK0_GPIO0_CTRL_INOVER_BITS
@@ -76,7 +78,7 @@
 }
 
 void gpio_set_outover(uint gpio, uint value) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_write_masked(&iobank0_hw->io[gpio].ctrl,
                    value << IO_BANK0_GPIO0_CTRL_OUTOVER_LSB,
                    IO_BANK0_GPIO0_CTRL_OUTOVER_BITS
@@ -84,7 +86,7 @@
 }
 
 void gpio_set_oeover(uint gpio, uint value) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_write_masked(&iobank0_hw->io[gpio].ctrl,
                    value << IO_BANK0_GPIO0_CTRL_OEOVER_LSB,
                    IO_BANK0_GPIO0_CTRL_OEOVER_BITS
@@ -92,7 +94,7 @@
 }
 
 void gpio_set_input_hysteresis_enabled(uint gpio, bool enabled) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     if (enabled)
         hw_set_bits(&padsbank0_hw->io[gpio], PADS_BANK0_GPIO0_SCHMITT_BITS);
     else
@@ -101,12 +103,12 @@
 
 
 bool gpio_is_input_hysteresis_enabled(uint gpio) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     return (padsbank0_hw->io[gpio] & PADS_BANK0_GPIO0_SCHMITT_BITS) != 0;
 }
 
 void gpio_set_slew_rate(uint gpio, enum gpio_slew_rate slew) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_write_masked(&padsbank0_hw->io[gpio],
                     (uint)slew << PADS_BANK0_GPIO0_SLEWFAST_LSB,
                     PADS_BANK0_GPIO0_SLEWFAST_BITS
@@ -114,7 +116,7 @@
 }
 
 enum gpio_slew_rate gpio_get_slew_rate(uint gpio) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     return (enum gpio_slew_rate)((padsbank0_hw->io[gpio]
             & PADS_BANK0_GPIO0_SLEWFAST_BITS)
             >> PADS_BANK0_GPIO0_SLEWFAST_LSB);
@@ -124,7 +126,7 @@
 // Enum encoding should match hardware encoding on RP2040
 static_assert(PADS_BANK0_GPIO0_DRIVE_VALUE_8MA == GPIO_DRIVE_STRENGTH_8MA, "");
 void gpio_set_drive_strength(uint gpio, enum gpio_drive_strength drive) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     hw_write_masked(&padsbank0_hw->io[gpio],
                     (uint)drive << PADS_BANK0_GPIO0_DRIVE_LSB,
                     PADS_BANK0_GPIO0_DRIVE_BITS
@@ -132,25 +134,28 @@
 }
 
 enum gpio_drive_strength gpio_get_drive_strength(uint gpio) {
-    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+    check_gpio_param(gpio);
     return (enum gpio_drive_strength)((padsbank0_hw->io[gpio]
             & PADS_BANK0_GPIO0_DRIVE_BITS)
             >> PADS_BANK0_GPIO0_DRIVE_LSB);
 }
 
-static void gpio_irq_handler(void) {
-    io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
-                                           &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
-    for (uint gpio = 0; gpio < NUM_BANK0_GPIOS; gpio++) {
-        io_ro_32 *status_reg = &irq_ctrl_base->ints[gpio / 8];
-        uint events = (*status_reg >> 4 * (gpio % 8)) & 0xf;
-        if (events) {
-            // TODO: If both cores care about this event then the second core won't get the irq?
-            gpio_acknowledge_irq(gpio, events);
-            gpio_irq_callback_t callback = _callbacks[get_core_num()];
-            if (callback) {
-                callback(gpio, events);
+static void gpio_default_irq_handler(void) {
+    uint core = get_core_num();
+    gpio_irq_callback_t callback = callbacks[core];
+    io_irq_ctrl_hw_t *irq_ctrl_base = core ? &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
+    for (uint gpio = 0; gpio < NUM_BANK0_GPIOS; gpio+=8) {
+        uint32_t events8 = irq_ctrl_base->ints[gpio >> 3u];
+        // note we assume events8 is 0 for non-existent GPIO
+        for(uint i=gpio;events8 && i<gpio+8;i++) {
+            uint32_t events = events8 & 0xfu;
+            if (events && !(raw_irq_mask[core] & (1u << i))) {
+                gpio_acknowledge_irq(i, events);
+                if (callback) {
+                    callback(i, events);
+                }
             }
+            events8 >>= 4;
         }
     }
 }
@@ -178,21 +183,48 @@
 
 void gpio_set_irq_enabled_with_callback(uint gpio, uint32_t events, bool enabled, gpio_irq_callback_t callback) {
     gpio_set_irq_enabled(gpio, events, enabled);
+    gpio_set_irq_callback(callback);
+    if (enabled) irq_set_enabled(IO_IRQ_BANK0, true);
+}
 
-    // TODO: Do we want to support a callback per GPIO pin?
-    // Install IRQ handler
-    _callbacks[get_core_num()] = callback;
-    irq_set_exclusive_handler(IO_IRQ_BANK0, gpio_irq_handler);
-    irq_set_enabled(IO_IRQ_BANK0, true);
+void gpio_set_irq_callback(gpio_irq_callback_t callback) {
+    uint core = get_core_num();
+    if (callbacks[core]) {
+        if (!callback) {
+            irq_remove_handler(IO_IRQ_BANK0, gpio_default_irq_handler);
+        }
+        callbacks[core] = callback;
+    } else if (callback) {
+        callbacks[core] = callback;
+        irq_add_shared_handler(IO_IRQ_BANK0, gpio_default_irq_handler, GPIO_IRQ_CALLBACK_ORDER_PRIORITY);
+    }
+}
+
+void gpio_add_raw_irq_handler_with_order_priority_masked(uint gpio_mask, irq_handler_t handler, uint8_t order_priority) {
+    hard_assert(!(raw_irq_mask[get_core_num()] & gpio_mask)); // should not add multiple handlers for the same event
+    raw_irq_mask[get_core_num()] |= gpio_mask;
+    irq_add_shared_handler(IO_IRQ_BANK0, handler, order_priority);
+}
+
+void gpio_add_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler) {
+    gpio_add_raw_irq_handler_with_order_priority_masked(gpio_mask, handler, GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY);
+}
+
+void gpio_remove_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler) {
+    assert(raw_irq_mask[get_core_num()] & gpio_mask); // should not remove handlers that are not added
+    irq_remove_handler(IO_IRQ_BANK0, handler);
+    raw_irq_mask[get_core_num()] &= ~gpio_mask;
 }
 
 void gpio_set_dormant_irq_enabled(uint gpio, uint32_t events, bool enabled) {
+    check_gpio_param(gpio);
     io_irq_ctrl_hw_t *irq_ctrl_base = &iobank0_hw->dormant_wake_irq_ctrl;
     _gpio_set_irq_enabled(gpio, events, enabled, irq_ctrl_base);
 }
 
 void gpio_acknowledge_irq(uint gpio, uint32_t events) {
-    iobank0_hw->intr[gpio / 8] = events << 4 * (gpio % 8);
+    check_gpio_param(gpio);
+    iobank0_hw->intr[gpio / 8] = events << (4 * (gpio % 8));
 }
 
 #define DEBUG_PIN_MASK (((1u << PICO_DEBUG_PIN_COUNT)-1) << PICO_DEBUG_PIN_BASE)
@@ -217,8 +249,12 @@
     gpio_set_function(gpio, GPIO_FUNC_SIO);
 }
 
+void gpio_deinit(uint gpio) {
+    gpio_set_function(gpio, GPIO_FUNC_NULL);
+}
+
 void gpio_init_mask(uint gpio_mask) {
-    for(uint i=0;i<32;i++) {
+    for(uint i=0;i<NUM_BANK0_GPIOS;i++) {
         if (gpio_mask & 1) {
             gpio_init(i);
         }
diff --git a/src/rp2_common/hardware_gpio/include/hardware/gpio.h b/src/rp2_common/hardware_gpio/include/hardware/gpio.h
index 7037e85..09a9b96 100644
--- a/src/rp2_common/hardware_gpio/include/hardware/gpio.h
+++ b/src/rp2_common/hardware_gpio/include/hardware/gpio.h
@@ -10,6 +10,8 @@
 #include "pico.h"
 #include "hardware/structs/sio.h"
 #include "hardware/structs/padsbank0.h"
+#include "hardware/structs/iobank0.h"
+#include "hardware/irq.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -102,7 +104,7 @@
 #define GPIO_OUT 1
 #define GPIO_IN 0
 
-/*! \brief  GPIO Interrupt level definitions
+/*! \brief  GPIO Interrupt level definitions (GPIO events)
  *  \ingroup hardware_gpio
  *  \brief GPIO Interrupt levels
  *
@@ -128,10 +130,11 @@
  *  \ingroup hardware_gpio
  *
  * \param gpio Which GPIO caused this interrupt
- * \param events Which events caused this interrupt. See \ref gpio_set_irq_enabled for details.
+ * \param event_mask Which events caused this interrupt. See \ref gpio_irq_level for details.
  * \sa gpio_set_irq_enabled_with_callback()
+ * \sa gpio_set_irq_callback()
  */
-typedef void (*gpio_irq_callback_t)(uint gpio, uint32_t events);
+typedef void (*gpio_irq_callback_t)(uint gpio, uint32_t event_mask);
 
 enum gpio_override {
     GPIO_OVERRIDE_NORMAL = 0,      ///< peripheral signal selected via \ref gpio_set_function
@@ -165,6 +168,10 @@
     GPIO_DRIVE_STRENGTH_12MA = 3 ///< 12 mA nominal drive strength
 };
 
+static inline void check_gpio_param(__unused uint gpio) {
+    invalid_params_if(GPIO, gpio >= NUM_BANK0_GPIOS);
+}
+
 // ----------------------------------------------------------------------------
 // Pad Controls + IO Muxing
 // ----------------------------------------------------------------------------
@@ -346,63 +353,288 @@
  */
 enum gpio_drive_strength gpio_get_drive_strength(uint gpio);
 
-/*! \brief Enable or disable interrupts for specified GPIO
+/*! \brief Enable or disable specific interrupt events for specified GPIO
  *  \ingroup hardware_gpio
  *
- * \note The IO IRQs are independent per-processor. This configures IRQs for
+ * This function sets which GPIO events cause a GPIO interrupt on the calling core. See
+ * \ref gpio_set_irq_callback, \ref gpio_set_irq_enabled_with_callback and
+ * \ref gpio_add_raw_irq_handler to set up a GPIO interrupt handler to handle the events.
+ *
+ * \note The IO IRQs are independent per-processor. This configures the interrupt events for
  * the processor that calls the function.
  *
  * \param gpio GPIO number
- * \param events Which events will cause an interrupt
+ * \param event_mask Which events will cause an interrupt
  * \param enabled Enable or disable flag
  *
- * Events is a bitmask of the following:
+ * Events is a bitmask of the following \ref gpio_irq_level values:
  *
- * bit | interrupt
- * ----|----------
- *   0 | Low level
- *   1 | High level
- *   2 | Edge low
- *   3 | Edge high
+ * bit | constant            | interrupt
+ * ----|----------------------------------------------------------
+ *   0 | GPIO_IRQ_LEVEL_LOW  | Continuously while level is low
+ *   1 | GPIO_IRQ_LEVEL_HIGH | Continuously while level is high
+ *   2 | GPIO_IRQ_EDGE_FALL  | On each transition from high to low
+ *   3 | GPIO_IRQ_EDGE_RISE  | On each transition from low to high
+ *
+ * which are specified in \ref gpio_irq_level
  */
-void gpio_set_irq_enabled(uint gpio, uint32_t events, bool enabled);
+void gpio_set_irq_enabled(uint gpio, uint32_t event_mask, bool enabled);
 
-/*! \brief Enable interrupts for specified GPIO
+// PICO_CONFIG: GPIO_IRQ_CALLBACK_ORDER_PRIORITY, the irq priority order of the default IRQ callback, min=0, max=255, default=PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY, group=hardware_gpio
+#ifndef GPIO_IRQ_CALLBACK_ORDER_PRIORITY
+#define GPIO_IRQ_CALLBACK_ORDER_PRIORITY PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY
+#endif
+
+// PICO_CONFIG: GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY, the irq priority order of raw IRQ handlers if the priortiy is not specified, min=0, max=255, default=PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY, group=hardware_gpio
+#ifndef GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY
+#define GPIO_RAW_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY
+#endif
+
+/*! \brief Set the generic callback used for GPIO IRQ events for the current core
  *  \ingroup hardware_gpio
  *
- * \note The IO IRQs are independent per-processor. This configures IRQs for
+ * This function sets the callback used for all GPIO IRQs on the current core that are not explicitly
+ * hooked via \ref gpio_add_raw_irq_handler or other gpio_add_raw_irq_handler_ functions.
+ *
+ * This function is called with the GPIO number and event mask for each of the (not explicitly hooked)
+ * GPIOs that have events enabled and that are pending (see \ref gpio_get_irq_event_mask).
+ *
+ * \note The IO IRQs are independent per-processor. This function affects
  * the processor that calls the function.
  *
- * \param gpio GPIO number
- * \param events Which events will cause an interrupt. See \ref gpio_set_irq_enabled for details.
- * \param enabled Enable or disable flag
- * \param callback user function to call on GPIO irq. Note only one of these can be set per processor.
- *
- * \note Currently the GPIO parameter is ignored, and this callback will be called for any enabled GPIO IRQ on any pin.
- *
+ * \param callback default user function to call on GPIO irq. Note only one of these can be set per processor.
  */
-void gpio_set_irq_enabled_with_callback(uint gpio, uint32_t events, bool enabled, gpio_irq_callback_t callback);
+void gpio_set_irq_callback(gpio_irq_callback_t callback);
 
-/*! \brief Enable dormant wake up interrupt for specified GPIO
+/*! \brief Convenience function which performs multiple GPIO IRQ related initializations
+ *  \ingroup hardware_gpio
+ *
+ * This method is a slightly eclectic mix of initialization, that:
+ *
+ * \li Updates whether the specified events for the specified GPIO causes an interrupt on the calling core based
+ * on the enable flag.
+ *
+ * \li Sets the callback handler for the calling core to callback (or clears the handler if the callback is NULL).
+ *
+ * \li Enables GPIO IRQs on the current core if enabled is true.
+ *
+ * This method is commonly used to perform a one time setup, and following that any additional IRQs/events are enabled
+ * via \ref gpio_set_irq_enabled. All GPIOs/events added in this way on the same core share the same callback; for multiple
+ * independent handlers for different GPIOs you should use \ref gpio_add_raw_irq_handler and related functions.
+ *
+ * This method is equivalent to:
+ *
+ * \code{.c}
+ * gpio_set_irq_enabled(gpio, event_mask, enabled);
+ * gpio_set_irq_callback(callback);
+ * if (enabled) irq_set_enabled(IO_IRQ_BANK0, true);
+ * \endcode
+ *
+ * \note The IO IRQs are independent per-processor. This method affects only the processor that calls the function.
+ *
+ * \param gpio GPIO number
+ * \param event_mask Which events will cause an interrupt. See \ref gpio_irq_level for details.
+ * \param enabled Enable or disable flag
+ * \param callback user function to call on GPIO irq. if NULL, the callback is removed
+ */
+void gpio_set_irq_enabled_with_callback(uint gpio, uint32_t event_mask, bool enabled, gpio_irq_callback_t callback);
+
+/*! \brief Enable dormant wake up interrupt for specified GPIO and events
  *  \ingroup hardware_gpio
  *
  * This configures IRQs to restart the XOSC or ROSC when they are
  * disabled in dormant mode
  *
  * \param gpio GPIO number
- * \param events Which events will cause an interrupt. See \ref gpio_set_irq_enabled for details.
+ * \param event_mask Which events will cause an interrupt. See \ref gpio_irq_level for details.
  * \param enabled Enable/disable flag
  */
-void gpio_set_dormant_irq_enabled(uint gpio, uint32_t events, bool enabled);
+void gpio_set_dormant_irq_enabled(uint gpio, uint32_t event_mask, bool enabled);
 
-/*! \brief Acknowledge a GPIO interrupt
+/*! \brief Return the current interrupt status (pending events) for the given GPIO
  *  \ingroup hardware_gpio
  *
  * \param gpio GPIO number
- * \param events Bitmask of events to clear. See \ref gpio_set_irq_enabled for details.
-  *
+ * \return Bitmask of events that are currently pending for the GPIO. See \ref gpio_irq_level for details.
+ * \sa gpio_acknowledge_irq
  */
-void gpio_acknowledge_irq(uint gpio, uint32_t events);
+static inline uint32_t gpio_get_irq_event_mask(uint gpio) {
+    check_gpio_param(gpio);
+    io_irq_ctrl_hw_t *irq_ctrl_base = get_core_num() ?
+                                      &iobank0_hw->proc1_irq_ctrl : &iobank0_hw->proc0_irq_ctrl;
+    io_ro_32 *status_reg = &irq_ctrl_base->ints[gpio >> 3u];
+    return (*status_reg >> (4 * (gpio & 7u))) & 0xfu;
+}
+
+/*! \brief Acknowledge a GPIO interrupt for the specified events on the calling core
+ *  \ingroup hardware_gpio
+ *
+ * \note This may be called with a mask of any of valid bits specified in \ref gpio_irq_level, however
+ * it has no effect on \a level sensitive interrupts which remain pending while the GPIO is at the specified
+ * level. When handling \a level sensitive interrupts, you should generally disable the interrupt (see
+ * \ref gpio_set_irq_enabled) and then set it up again later once the GPIO level has changed (or to catch
+ * the opposite level).
+ *
+ * \param gpio GPIO number
+ * \param events Bitmask of events to clear. See \ref gpio_set_irq_enabled for details.
+ *
+ * \note For callbacks set with \ref gpio_set_irq_enabled_with_callback, or \ref gpio_set_irq_callback,this function is called automatically.
+ * \param event_mask Bitmask of events to clear. See \ref gpio_irq_level for details.
+ */
+void gpio_acknowledge_irq(uint gpio, uint32_t event_mask);
+
+/*! \brief Adds a raw GPIO IRQ handler for the specified GPIOs on the current core
+ *  \ingroup hardware_gpio
+ *
+ * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback),
+ * it is possible to add explicit GPIO IRQ handlers which are called independent of the default callback. The order
+ * relative to the default callback can be controlled via the order_priority parameter (the default callback has the priority
+ * \ref GPIO_IRQ_CALLBACK_ORDER_PRIORITY which defaults to the lowest priority with the intention of it running last).
+ *
+ * This method adds such an explicit GPIO IRQ handler, and disables the "default" callback for the specified GPIOs.
+ *
+ * \note Multiple raw handlers should not be added for the same GPIOs, and this method will assert if you attempt to.
+ *
+ * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like:
+ *
+ * \code{.c}
+ * void my_irq_handler(void) {
+ *     if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) {
+ *        gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask);
+ *       // handle the IRQ
+ *     }
+ *     if (gpio_get_irq_event_mask(my_gpio_num2) & my_gpio_event_mask2) {
+ *        gpio_acknowledge_irq(my_gpio_num2, my_gpio_event_mask2);
+ *       // handle the IRQ
+ *     }
+ * }
+ * \endcode
+ *
+ * @param gpio_mask a bit mask of the GPIO numbers that will no longer be passed to the default callback for this core
+ * @param handler the handler to add to the list of GPIO IRQ handlers for this core
+ * @param order_priority the priority order to determine the relative position of the handler in the list of GPIO IRQ handlers for this core.
+ */
+void gpio_add_raw_irq_handler_with_order_priority_masked(uint gpio_mask, irq_handler_t handler, uint8_t order_priority);
+
+/*! \brief Adds a raw GPIO IRQ handler for a specific GPIO on the current core
+ *  \ingroup hardware_gpio
+ *
+ * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback),
+ * it is possible to add explicit GPIO IRQ handlers which are called independent of the default callback. The order
+ * relative to the default callback can be controlled via the order_priority parameter(the default callback has the priority
+ * \ref GPIO_IRQ_CALLBACK_ORDER_PRIORITY which defaults to the lowest priority with the intention of it running last).
+ *
+ * This method adds such a callback, and disables the "default" callback for the specified GPIO.
+ *
+ * \note Multiple raw handlers should not be added for the same GPIO, and this method will assert if you attempt to.
+ *
+ * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like:
+ *
+ * \code{.c}
+ * void my_irq_handler(void) {
+ *     if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) {
+ *        gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask);
+ *       // handle the IRQ
+ *     }
+ * }
+ * \endcode
+ *
+ * @param gpio the GPIO number that will no longer be passed to the default callback for this core
+ * @param handler the handler to add to the list of GPIO IRQ handlers for this core
+ * @param order_priority the priority order to determine the relative position of the handler in the list of GPIO IRQ handlers for this core.
+ */
+static inline void gpio_add_raw_irq_handler_with_order_priority(uint gpio, irq_handler_t handler, uint8_t order_priority) {
+    check_gpio_param(gpio);
+    gpio_add_raw_irq_handler_with_order_priority_masked(1u << gpio, handler, order_priority);
+}
+
+/*! \brief Adds a raw GPIO IRQ handler for the specified GPIOs on the current core
+ *  \ingroup hardware_gpio
+ *
+ * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback),
+ * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback.
+ *
+ * This method adds such a callback, and disables the "default" callback for the specified GPIOs.
+ *
+ * \note Multiple raw handlers should not be added for the same GPIOs, and this method will assert if you attempt to.
+ *
+ * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like:
+ *
+ * \code{.c}
+ * void my_irq_handler(void) {
+ *     if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) {
+ *        gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask);
+ *       // handle the IRQ
+ *     }
+ *     if (gpio_get_irq_event_mask(my_gpio_num2) & my_gpio_event_mask2) {
+ *        gpio_acknowledge_irq(my_gpio_num2, my_gpio_event_mask2);
+ *       // handle the IRQ
+ *     }
+ * }
+ * \endcode
+ *
+ * @param gpio_mask a bit mask of the GPIO numbers that will no longer be passed to the default callback for this core
+ * @param handler the handler to add to the list of GPIO IRQ handlers for this core
+ */
+void gpio_add_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler);
+
+/*! \brief Adds a raw GPIO IRQ handler for a specific GPIO on the current core
+ *  \ingroup hardware_gpio
+ *
+ * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback),
+ * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback.
+ *
+ * This method adds such a callback, and disables the "default" callback for the specified GPIO.
+ *
+ * \note Multiple raw handlers should not be added for the same GPIO, and this method will assert if you attempt to.
+ *
+ * A raw handler should check for whichever GPIOs and events it handles, and acknowledge them itself; it might look something like:
+ *
+ * \code{.c}
+ * void my_irq_handler(void) {
+ *     if (gpio_get_irq_event_mask(my_gpio_num) & my_gpio_event_mask) {
+ *        gpio_acknowledge_irq(my_gpio_num, my_gpio_event_mask);
+ *       // handle the IRQ
+ *     }
+ * }
+ * \endcode
+ *
+ * @param gpio the GPIO number that will no longer be passed to the default callback for this core
+ * @param handler the handler to add to the list of GPIO IRQ handlers for this core
+ */
+static inline void gpio_add_raw_irq_handler(uint gpio, irq_handler_t handler) {
+    check_gpio_param(gpio);
+    gpio_add_raw_irq_handler_masked(1u << gpio, handler);
+}
+
+/*! \brief Removes a raw GPIO IRQ handler for the specified GPIOs on the current core
+ *  \ingroup hardware_gpio
+ *
+ * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback),
+ * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback.
+ *
+ * This method removes such a callback, and enables the "default" callback for the specified GPIOs.
+ *
+ * @param gpio_mask a bit mask of the GPIO numbers that will now be passed to the default callback for this core
+ * @param handler the handler to remove from the list of GPIO IRQ handlers for this core
+ */
+void gpio_remove_raw_irq_handler_masked(uint gpio_mask, irq_handler_t handler);
+
+/*! \brief Removes a raw GPIO IRQ handler for the specified GPIO on the current core
+ *  \ingroup hardware_gpio
+ *
+ * In addition to the default mechanism of a single GPIO IRQ event callback per core (see \ref gpio_set_irq_callback),
+ * it is possible to add explicit GPIO IRQ handlers which are called independent of the default event callback.
+ *
+ * This method removes such a callback, and enables the "default" callback for the specified GPIO.
+ *
+ * @param gpio the GPIO number that will now be passed to the default callback for this core
+ * @param handler the handler to remove from the list of GPIO IRQ handlers for this core
+ */
+static inline void gpio_remove_raw_irq_handler(uint gpio, irq_handler_t handler) {
+    check_gpio_param(gpio);
+    gpio_remove_raw_irq_handler_masked(1u << gpio, handler);
+}
 
 /*! \brief Initialise a GPIO for (enabled I/O and set func to GPIO_FUNC_SIO)
  *  \ingroup hardware_gpio
@@ -414,6 +646,13 @@
  */
 void gpio_init(uint gpio);
 
+/*! \brief Resets a GPIO back to the NULL function, i.e. disables it.
+ *  \ingroup hardware_gpio
+ *
+ * \param gpio GPIO number
+ */
+void gpio_deinit(uint gpio);
+
 /*! \brief Initialise multiple GPIOs (enabled I/O and set func to GPIO_FUNC_SIO)
  *  \ingroup hardware_gpio
  *