summary refs log tree commit diff
path: root/platforms
diff options
context:
space:
mode:
authorStefan Kerkmann <karlk90@pm.me>2022-06-30 13:19:27 +0200
committerGitHub <noreply@github.com>2022-06-30 13:19:27 +0200
commitd7173967087e022d20d1f9c812b1b668e9d3f71b (patch)
tree68198271dd5125193795c399c6478ead0a71b09f /platforms
parentd206c1791e5858323cff0664f39f95edc1381ac5 (diff)
[Core] Add Raspberry Pi RP2040 support (#14877)
* Disable RESET keycode because of naming conflicts

* Add Pico SDK as submodule

* Add RP2040 build support to QMK

* Adjust USB endpoint structs for RP2040

* Add RP2040 bootloader and double-tap reset routine

* Add generic and pro micro RP2040 boards

* Add RP2040 onekey keyboard

* Add WS2812 PIO DMA enabled driver and documentation

Supports regular and open-drain output configuration. RP2040 GPIOs are
sadly not 5V tolerant, so this is a bit use-less or needs extra hardware
or you take the risk to fry your hardware.

* Adjust SIO Driver for RP2040

* Adjust I2C Driver for RP2040

* Adjust SPI Driver for RP2040

* Add PIO serial driver and documentation

* Add general RP2040 documentation

* Apply suggestions from code review

Co-authored-by: Nick Brassel <nick@tzarc.org>

Co-authored-by: Nick Brassel <nick@tzarc.org>
Diffstat (limited to 'platforms')
-rw-r--r--platforms/chibios/_pin_defs.h5
-rw-r--r--platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk9
-rw-r--r--platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h12
-rw-r--r--platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h13
-rw-r--r--platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h62
-rw-r--r--platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h98
-rw-r--r--platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk9
-rw-r--r--platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h12
-rw-r--r--platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h13
-rw-r--r--platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h98
-rw-r--r--platforms/chibios/bootloaders/rp2040.c57
-rw-r--r--platforms/chibios/chibios_config.h21
-rw-r--r--platforms/chibios/drivers/serial_usart.c28
-rw-r--r--platforms/chibios/drivers/spi_master.c31
-rw-r--r--platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c457
-rw-r--r--platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c189
-rw-r--r--platforms/chibios/flash.mk2
-rw-r--r--platforms/chibios/platform.mk15
-rw-r--r--platforms/chibios/vendors/RP/RP2040.mk285
-rw-r--r--platforms/chibios/vendors/RP/_pin_defs.h37
-rw-r--r--platforms/chibios/vendors/RP/pico_sdk_shims.c9
-rw-r--r--platforms/chibios/vendors/RP/stage2_bootloaders.c174
22 files changed, 1628 insertions, 8 deletions
diff --git a/platforms/chibios/_pin_defs.h b/platforms/chibios/_pin_defs.h
index 0d96e2fc3b..414c9e3d11 100644
--- a/platforms/chibios/_pin_defs.h
+++ b/platforms/chibios/_pin_defs.h
@@ -21,6 +21,11 @@
 #    include <hal.h>
 #endif
 
+/* Include the vendor specific pin defs */
+#if __has_include_next("_pin_defs.h")
+#    include_next "_pin_defs.h"
+#endif
+
 #define A0 PAL_LINE(GPIOA, 0)
 #define A1 PAL_LINE(GPIOA, 1)
 #define A2 PAL_LINE(GPIOA, 2)
diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk
new file mode 100644
index 0000000000..911cc5a058
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk
@@ -0,0 +1,9 @@
+# List of all the board related files.
+BOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c
+
+# Required include directories
+BOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040
+
+# Shared variables
+ALLCSRC += $(BOARDSRC) 
+ALLINC  += $(BOARDINC)
diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h
new file mode 100644
index 0000000000..b4363595d0
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h
@@ -0,0 +1,12 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include_next "board.h"
+
+#undef BOARD_RP_PICO_RP2040
+#define BOARD_GENERIC_PROMICRO_RP2040
+
+#undef BOARD_NAME
+#define BOARD_NAME "Pro Micro RP2040"
diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h
new file mode 100644
index 0000000000..d53f57edd9
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h
@@ -0,0 +1,13 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#define CH_CFG_SMP_MODE                     TRUE
+#define CH_CFG_ST_RESOLUTION                32
+#define CH_CFG_ST_FREQUENCY                 1000000
+#define CH_CFG_INTERVALS_SIZE               32
+#define CH_CFG_TIME_TYPES_SIZE              32
+#define CH_CFG_ST_TIMEDELTA                 20
+
+#include_next <chconf.h>
diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h
new file mode 100644
index 0000000000..7fe9b654e1
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h
@@ -0,0 +1,62 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+/**======================
+ **    I2C Driver
+ *========================**/
+
+#if !defined(I2C_DRIVER)
+#    define I2C_DRIVER I2CD2
+#endif
+
+#if !defined(I2C1_SDA_PIN)
+#    define I2C1_SDA_PIN GP2
+#endif
+
+#if !defined(I2C1_SCL_PIN)
+#    define I2C1_SCL_PIN GP3
+#endif
+
+/**======================
+ **    SPI Driver
+ *========================**/
+
+#if !defined(SPI_DRIVER)
+#    define SPI_DRIVER SPID0
+#endif
+
+#if !defined(SPI_SCK_PIN)
+#    define SPI_SCK_PIN GP18
+#endif
+
+#if !defined(SPI_MISO_PIN)
+#    define SPI_MISO_PIN GP20
+#endif
+
+#if !defined(SPI_MOSI_PIN)
+#    define SPI_MOSI_PIN GP19
+#endif
+
+/**======================
+ **      SERIAL Driver
+ *========================**/
+
+#if !defined(SERIAL_USART_DRIVER)
+#    define SERIAL_USART_DRIVER SIOD0
+#endif
+
+#if !defined(SERIAL_USART_TX_PIN) && !defined(SOFT_SERIAL_PIN)
+#    define SERIAL_USART_TX_PIN GP0
+#endif
+
+#if !defined(SERIAL_USART_RX_PIN)
+#    define SERIAL_USART_RX_PIN GP1
+#endif
+
+/**======================
+ **    Double-tap
+ *========================**/
+
+#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET 
diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h
new file mode 100644
index 0000000000..8348e5312f
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h
@@ -0,0 +1,98 @@
+/*
+    ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * RP2040_MCUCONF drivers configuration.
+ *
+ * IRQ priorities:
+ * 3...0        Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...1        Lowest...Highest.
+ */
+
+#define RP2040_MCUCONF
+
+/*
+ * HAL driver system settings.
+ */
+#define RP_NO_INIT                          FALSE
+#define RP_CORE1_START                      FALSE
+#define RP_CORE1_VECTORS_TABLE              _vectors
+#define RP_CORE1_ENTRY_POINT                _crt0_c1_entry
+#define RP_CORE1_STACK_END                  __c1_main_stack_end__
+
+/*
+ * IRQ system settings.
+ */
+#define RP_IRQ_SYSTICK_PRIORITY             2
+#define RP_IRQ_TIMER_ALARM0_PRIORITY        2
+#define RP_IRQ_TIMER_ALARM1_PRIORITY        2
+#define RP_IRQ_TIMER_ALARM2_PRIORITY        2
+#define RP_IRQ_TIMER_ALARM3_PRIORITY        2
+#define RP_IRQ_UART0_PRIORITY               3
+#define RP_IRQ_UART1_PRIORITY               3
+#define RP_IRQ_SPI0_PRIORITY                2
+#define RP_IRQ_SPI1_PRIORITY                2
+#define RP_IRQ_USB0_PRIORITY                3
+#define RP_IRQ_I2C0_PRIORITY                2
+#define RP_IRQ_I2C1_PRIORITY                2
+
+/*
+ * ADC driver system settings.
+ */
+#define RP_ADC_USE_ADC1                     FALSE
+
+/*
+ * SIO driver system settings.
+ */
+#define RP_SIO_USE_UART0                    TRUE
+#define RP_SIO_USE_UART1                    FALSE
+
+/*
+ * SPI driver system settings.
+ */
+#define RP_SPI_USE_SPI0                     TRUE
+#define RP_SPI_USE_SPI1                     FALSE
+#define RP_SPI_SPI0_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI0_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI1_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI1_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI0_DMA_PRIORITY            1
+#define RP_SPI_SPI1_DMA_PRIORITY            1
+#define RP_SPI_DMA_ERROR_HOOK(spip)
+
+/*
+ * I2C driver system settings.
+ */
+#define RP_I2C_USE_I2C0                     FALSE
+#define RP_I2C_USE_I2C1                     TRUE
+#define RP_I2C_BUSY_TIMEOUT                 50
+#define RP_I2C_ADDRESS_MODE_10BIT           FALSE
+
+/*
+ * USB driver system settings.
+ */
+#define RP_USB_USE_USBD0                    TRUE
+#define RP_USB_FORCE_VBUS_DETECT            TRUE
+#define RP_USE_EXTERNAL_VBUS_DETECT         FALSE
+#define RP_USB_USE_SOF_INTR                 TRUE
+#define RP_USB_USE_ERROR_DATA_SEQ_INTR      FALSE
+
+#endif /* MCUCONF_H */
diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk b/platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk
new file mode 100644
index 0000000000..911cc5a058
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk
@@ -0,0 +1,9 @@
+# List of all the board related files.
+BOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c
+
+# Required include directories
+BOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040
+
+# Shared variables
+ALLCSRC += $(BOARDSRC) 
+ALLINC  += $(BOARDINC)
diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h
new file mode 100644
index 0000000000..052050c944
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h
@@ -0,0 +1,12 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include_next "board.h"
+
+#undef BOARD_RP_PICO_RP2040
+#define BOARD_GENERIC_RP2040
+
+#undef BOARD_NAME
+#define BOARD_NAME "Generic Raspberry Pi RP2040"
diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h
new file mode 100644
index 0000000000..d53f57edd9
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h
@@ -0,0 +1,13 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#define CH_CFG_SMP_MODE                     TRUE
+#define CH_CFG_ST_RESOLUTION                32
+#define CH_CFG_ST_FREQUENCY                 1000000
+#define CH_CFG_INTERVALS_SIZE               32
+#define CH_CFG_TIME_TYPES_SIZE              32
+#define CH_CFG_ST_TIMEDELTA                 20
+
+#include_next <chconf.h>
diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h
new file mode 100644
index 0000000000..9d8dc61aac
--- /dev/null
+++ b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h
@@ -0,0 +1,98 @@
+/*
+    ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * RP2040_MCUCONF drivers configuration.
+ *
+ * IRQ priorities:
+ * 3...0        Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...1        Lowest...Highest.
+ */
+
+#define RP2040_MCUCONF
+
+/*
+ * HAL driver system settings.
+ */
+#define RP_NO_INIT                          FALSE
+#define RP_CORE1_START                      FALSE
+#define RP_CORE1_VECTORS_TABLE              _vectors
+#define RP_CORE1_ENTRY_POINT                _crt0_c1_entry
+#define RP_CORE1_STACK_END                  __c1_main_stack_end__
+
+/*
+ * IRQ system settings.
+ */
+#define RP_IRQ_SYSTICK_PRIORITY             2
+#define RP_IRQ_TIMER_ALARM0_PRIORITY        2
+#define RP_IRQ_TIMER_ALARM1_PRIORITY        2
+#define RP_IRQ_TIMER_ALARM2_PRIORITY        2
+#define RP_IRQ_TIMER_ALARM3_PRIORITY        2
+#define RP_IRQ_UART0_PRIORITY               3
+#define RP_IRQ_UART1_PRIORITY               3
+#define RP_IRQ_SPI0_PRIORITY                2
+#define RP_IRQ_SPI1_PRIORITY                2
+#define RP_IRQ_USB0_PRIORITY                3
+#define RP_IRQ_I2C0_PRIORITY                2
+#define RP_IRQ_I2C1_PRIORITY                2
+
+/*
+ * ADC driver system settings.
+ */
+#define RP_ADC_USE_ADC1                     FALSE
+
+/*
+ * SIO driver system settings.
+ */
+#define RP_SIO_USE_UART0                    FALSE
+#define RP_SIO_USE_UART1                    FALSE
+
+/*
+ * SPI driver system settings.
+ */
+#define RP_SPI_USE_SPI0                     FALSE
+#define RP_SPI_USE_SPI1                     FALSE
+#define RP_SPI_SPI0_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI0_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI1_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI1_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY
+#define RP_SPI_SPI0_DMA_PRIORITY            1
+#define RP_SPI_SPI1_DMA_PRIORITY            1
+#define RP_SPI_DMA_ERROR_HOOK(spip)
+
+/*
+ * I2C driver system settings.
+ */
+#define RP_I2C_USE_I2C0                     FALSE
+#define RP_I2C_USE_I2C1                     FALSE
+#define RP_I2C_BUSY_TIMEOUT                 50
+#define RP_I2C_ADDRESS_MODE_10BIT           FALSE
+
+/*
+ * USB driver system settings.
+ */
+#define RP_USB_USE_USBD0                    TRUE
+#define RP_USB_FORCE_VBUS_DETECT            TRUE
+#define RP_USE_EXTERNAL_VBUS_DETECT         FALSE
+#define RP_USB_USE_SOF_INTR                 TRUE
+#define RP_USB_USE_ERROR_DATA_SEQ_INTR      FALSE
+
+#endif /* MCUCONF_H */
diff --git a/platforms/chibios/bootloaders/rp2040.c b/platforms/chibios/bootloaders/rp2040.c
new file mode 100644
index 0000000000..13a54036ef
--- /dev/null
+++ b/platforms/chibios/bootloaders/rp2040.c
@@ -0,0 +1,57 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "quantum.h"
+#include "hal.h"
+#include "bootloader.h"
+#include "pico/bootrom.h"
+
+#if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED)
+#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK 0U
+#else
+#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK (1U << RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED)
+#endif
+
+__attribute__((weak)) void mcu_reset(void) {
+    NVIC_SystemReset();
+}
+void bootloader_jump(void) {
+    reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U);
+}
+
+void enter_bootloader_mode_if_requested(void) {}
+
+#if defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET)
+#    if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT)
+#        define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 200U
+#    endif
+
+// Needs to be located in a RAM section that is never initialized on boot to
+// preserve its value on reset
+static volatile uint32_t __attribute__((section(".ram0.bootloader_magic"))) magic_location;
+const uint32_t magic_token = 0xCAFEB0BA;
+
+// We can not use the __early_init / enter_bootloader_mode_if_requested hook as
+// we depend on an already initialized system with usable memory regions and
+// populated function pointer tables to the optimized math functions in the
+// bootrom. This function is called just prior to main.
+void __late_init(void) {
+    // All clocks have to be enabled before jumping to the bootloader function,
+    // otherwise the bootrom will be stuck infinitely.
+    clocks_init();
+
+    if (magic_location != magic_token) {
+        magic_location = magic_token;
+        // ChibiOS is not initialized at this point, so sleeping is only
+        // possible via busy waiting. The internal timer peripheral is running
+        // at this point with a precision of 1us.
+        chSysPolledDelayX(MS2RTC(1 * MHZ, RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT));
+        magic_location = 0;
+        return;
+    }
+
+    magic_location = 0;
+    reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U);
+}
+
+#endif
diff --git a/platforms/chibios/chibios_config.h b/platforms/chibios/chibios_config.h
index a7098f2713..1571bd5cd3 100644
--- a/platforms/chibios/chibios_config.h
+++ b/platforms/chibios/chibios_config.h
@@ -19,6 +19,27 @@
 #    define SPLIT_USB_DETECT // Force this on when dedicated pin is not used
 #endif
 
+#if defined(MCU_RP)
+#    define CPU_CLOCK RP_CORE_CLK
+
+#    define USE_GPIOV1
+#    define PAL_OUTPUT_TYPE_OPENDRAIN _Static_assert(0, "RP2040 has no Open Drain GPIO configuration, setting this is not possible");
+
+#    define usb_lld_endpoint_fields
+
+#    define I2C1_SCL_PAL_MODE (PAL_MODE_ALTERNATE_I2C | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_PUE | PAL_RP_PAD_DRIVE4)
+#    define I2C1_SDA_PAL_MODE I2C1_SCL_PAL_MODE
+
+#    define USE_I2CV1_CONTRIB
+#    if !defined(I2C1_CLOCK_SPEED)
+#        define I2C1_CLOCK_SPEED 400000
+#    endif
+
+#    define SPI_SCK_PAL_MODE (PAL_MODE_ALTERNATE_SPI | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_DRIVE4)
+#    define SPI_MOSI_PAL_MODE SPI_SCK_PAL_MODE
+#    define SPI_MISO_PAL_MODE SPI_SCK_PAL_MODE
+#endif
+
 // STM32 compatibility
 #if defined(MCU_STM32)
 #    define CPU_CLOCK STM32_SYSCLK
diff --git a/platforms/chibios/drivers/serial_usart.c b/platforms/chibios/drivers/serial_usart.c
index f76afb5db4..6581a5b6e9 100644
--- a/platforms/chibios/drivers/serial_usart.c
+++ b/platforms/chibios/drivers/serial_usart.c
@@ -8,12 +8,12 @@
 
 #if defined(SERIAL_USART_CONFIG)
 static QMKSerialConfig serial_config = SERIAL_USART_CONFIG;
-#else
+#elif defined(MCU_STM32) /* STM32 MCUs */
 static QMKSerialConfig serial_config = {
 #    if HAL_USE_SERIAL
-    .speed = (SERIAL_USART_SPEED), /* baudrate - mandatory */
+    .speed = (SERIAL_USART_SPEED),
 #    else
-    .baud = (SERIAL_USART_SPEED), /* baudrate - mandatory */
+    .baud = (SERIAL_USART_SPEED),
 #    endif
     .cr1   = (SERIAL_USART_CR1),
     .cr2   = (SERIAL_USART_CR2),
@@ -23,6 +23,19 @@ static QMKSerialConfig serial_config = {
     .cr3  = (SERIAL_USART_CR3)
 #    endif
 };
+#elif defined(MCU_RP) /* Raspberry Pi MCUs */
+/* USART in 8E2 config with RX and TX FIFOs enabled. */
+// clang-format off
+static QMKSerialConfig serial_config = {
+    .baud = (SERIAL_USART_SPEED),
+    .UARTLCR_H = UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_PEN | UART_UARTLCR_H_STP2 | UART_UARTLCR_H_FEN,
+    .UARTCR = 0U,
+    .UARTIFLS = UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E,
+    .UARTDMACR = 0U
+};
+// clang-format on
+#else
+#    error MCU Familiy not supported by default, supply your own serial_config by defining SERIAL_USART_CONFIG in your keyboard files.
 #endif
 
 static QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER;
@@ -156,7 +169,7 @@ inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t
  * @brief Initiate pins for USART peripheral. Half-duplex configuration.
  */
 __attribute__((weak)) void usart_init(void) {
-#    if defined(MCU_STM32)
+#    if defined(MCU_STM32) /* STM32 MCUs */
 #        if defined(USE_GPIOV1)
     palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN);
 #        else
@@ -166,6 +179,8 @@ __attribute__((weak)) void usart_init(void) {
 #        if defined(USART_REMAP)
     USART_REMAP;
 #        endif
+#    elif defined(MCU_RP) /* Raspberry Pi MCUs */
+#        error Half-duplex with the SIO driver is not supported due to hardware limitations on the RP2040, switch to the PIO driver which has half-duplex support.
 #    else
 #        pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
 #    endif
@@ -177,7 +192,7 @@ __attribute__((weak)) void usart_init(void) {
  * @brief Initiate pins for USART peripheral. Full-duplex configuration.
  */
 __attribute__((weak)) void usart_init(void) {
-#    if defined(MCU_STM32)
+#    if defined(MCU_STM32) /* STM32 MCUs */
 #        if defined(USE_GPIOV1)
     palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
     palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
@@ -189,6 +204,9 @@ __attribute__((weak)) void usart_init(void) {
 #        if defined(USART_REMAP)
     USART_REMAP;
 #        endif
+#    elif defined(MCU_RP) /* Raspberry Pi MCUs */
+    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_UART);
+    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE_UART);
 #    else
 #        pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files."
 #    endif
diff --git a/platforms/chibios/drivers/spi_master.c b/platforms/chibios/drivers/spi_master.c
index ce69e7f0ac..f9974d9f6b 100644
--- a/platforms/chibios/drivers/spi_master.c
+++ b/platforms/chibios/drivers/spi_master.c
@@ -20,7 +20,7 @@
 
 static pin_t currentSlavePin = NO_PIN;
 
-#if defined(K20x) || defined(KL2x)
+#if defined(K20x) || defined(KL2x) || defined(RP2040)
 static SPIConfig spiConfig = {NULL, 0, 0, 0};
 #else
 static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0};
@@ -167,7 +167,36 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
             spiConfig.SPI_CPOL = SPI_CPOL_High;
             break;
     }
+#elif defined(MCU_RP)
+    if (lsbFirst) {
+        osalDbgAssert(lsbFirst == false, "RP2040s PrimeCell SPI implementation does not support sending LSB first.");
+    }
+
+    // Motorola frame format and 8bit transfer data size.
+    spiConfig.SSPCR0 = SPI_SSPCR0_FRF_MOTOROLA | SPI_SSPCR0_DSS_8BIT;
+    // Serial output clock = (ck_sys or ck_peri) / (SSPCPSR->CPSDVSR * (1 +
+    // SSPCR0->SCR)). SCR is always set to zero, as QMK SPI API expects the
+    // passed divisor to be the only value to divide the input clock by.
+    spiConfig.SSPCPSR = roundedDivisor; // Even number from 2 to 254
 
+    switch (mode) {
+        case 0:
+            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low
+            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge
+            break;
+        case 1:
+            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low
+            spiConfig.SSPCR0 |= SPI_SSPCR0_SPH;  // Clock phase: sample on second edge transition
+            break;
+        case 2:
+            spiConfig.SSPCR0 |= SPI_SSPCR0_SPO;  // Clock polarity: high
+            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge
+            break;
+        case 3:
+            spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high
+            spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition
+            break;
+    }
 #else
     spiConfig.cr1 = 0;
 
diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c
new file mode 100644
index 0000000000..949fc6dd93
--- /dev/null
+++ b/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c
@@ -0,0 +1,457 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "quantum.h"
+#include "serial_usart.h"
+#include "serial_protocol.h"
+#include "hardware/pio.h"
+#include "hardware/clocks.h"
+
+#if !defined(MCU_RP)
+#    error PIO Driver is only available for Raspberry Pi 2040 MCUs!
+#endif
+
+static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout);
+static inline bool send_impl(const uint8_t* source, const size_t size);
+static inline void pio_serve_interrupt(void);
+
+#define MSG_PIO_ERROR ((msg_t)(-3))
+
+#if defined(SERIAL_PIO_USE_PIO1)
+static const PIO pio = pio1;
+
+OSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) {
+    OSAL_IRQ_PROLOGUE();
+    pio_serve_interrupt();
+    OSAL_IRQ_EPILOGUE();
+}
+#else
+static const PIO pio = pio0;
+
+OSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) {
+    OSAL_IRQ_PROLOGUE();
+    pio_serve_interrupt();
+    OSAL_IRQ_EPILOGUE();
+}
+#endif
+
+#define UART_TX_WRAP_TARGET 0
+#define UART_TX_WRAP 3
+
+// clang-format off
+#if defined(SERIAL_USART_FULL_DUPLEX)
+static const uint16_t uart_tx_program_instructions[] = {
+            //     .wrap_target
+    0x9fa0, //  0: pull   block           side 1 [7]
+    0xf727, //  1: set    x, 7            side 0 [7]
+    0x6001, //  2: out    pins, 1
+    0x0642, //  3: jmp    x--, 2                 [6]
+            //     .wrap
+};
+#else
+static const uint16_t uart_tx_program_instructions[] = {
+            //     .wrap_target
+    0x9fa0, //  0: pull   block           side 1 [7]
+    0xf727, //  1: set    x, 7            side 0 [7]
+    0x6081, //  2: out    pindirs, 1
+    0x0642, //  3: jmp    x--, 2                 [6]
+            //     .wrap
+};
+#endif
+// clang-format on
+
+static const pio_program_t uart_tx_program = {
+    .instructions = uart_tx_program_instructions,
+    .length       = 4,
+    .origin       = -1,
+};
+
+#define UART_RX_WRAP_TARGET 0
+#define UART_RX_WRAP 8
+
+// clang-format off
+static const uint16_t uart_rx_program_instructions[] = {
+            //     .wrap_target
+    0x2020, //  0: wait   0 pin, 0
+    0xea27, //  1: set    x, 7                   [10]
+    0x4001, //  2: in     pins, 1
+    0x0642, //  3: jmp    x--, 2                 [6]
+    0x00c8, //  4: jmp    pin, 8
+    0xc020, //  5: irq    wait 0
+    0x20a0, //  6: wait   1 pin, 0
+    0x0000, //  7: jmp    0
+    0x8020, //  8: push   block
+            //     .wrap
+};
+// clang-format on
+
+static const pio_program_t uart_rx_program = {
+    .instructions = uart_rx_program_instructions,
+    .length       = 9,
+    .origin       = -1,
+};
+
+thread_reference_t rx_thread        = NULL;
+static int         rx_state_machine = -1;
+
+thread_reference_t tx_thread        = NULL;
+static int         tx_state_machine = -1;
+
+void pio_serve_interrupt(void) {
+    uint32_t irqs = pio->ints0;
+
+    // The RX FIFO is not empty any more, therefore wake any sleeping rx thread
+    if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << rx_state_machine)) {
+        // Disable rx not empty interrupt
+        pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false);
+
+        osalSysLockFromISR();
+        osalThreadResumeI(&rx_thread, MSG_OK);
+        osalSysUnlockFromISR();
+    }
+
+    // The TX FIFO is not full any more, therefore wake any sleeping tx thread
+    if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << tx_state_machine)) {
+        // Disable tx not full interrupt
+        pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false);
+        osalSysLockFromISR();
+        osalThreadResumeI(&tx_thread, MSG_OK);
+        osalSysUnlockFromISR();
+    }
+
+    // IRQ 0 is set on framing or break errors by the rx state machine
+    if (pio_interrupt_get(pio, 0UL)) {
+        pio_interrupt_clear(pio, 0UL);
+
+        osalSysLockFromISR();
+        osalThreadResumeI(&rx_thread, MSG_PIO_ERROR);
+        osalSysUnlockFromISR();
+    }
+}
+
+#if !defined(SERIAL_USART_FULL_DUPLEX)
+// The internal pull-ups of the RP2040 are rather weakish with a range of 50k to
+// 80k, which in turn do not provide enough current to guarantee fast signal rise
+// times with a parasitic capacitance of greater than 100pf. In real world
+// applications, like split keyboards which might have vias in the signal path
+// or long PCB traces, this prevents a successful communication. The solution
+// is to temporarily augment the weak pull ups from the receiving side by
+// driving the tx pin high. On the receiving side the lowest possible drive
+// strength is chosen because the transmitting side must still be able to drive
+// the signal low. With this configuration the rise times are fast enough and
+// the generated low level with 360mV will generate a logical zero.
+static inline void enter_rx_state(void) {
+    osalSysLock();
+    // Wait for the transmitting state machines FIFO to run empty. At this point
+    // the last byte has been pulled from the transmitting state machines FIFO
+    // into the output shift register. We have to wait a tiny bit more until
+    // this byte is transmitted, before we can turn on the receiving state
+    // machine again.
+    while (!pio_sm_is_tx_fifo_empty(pio, tx_state_machine)) {
+    }
+    // Wait for ~11 bits, 1 start bit + 8 data bits + 1 stop bit + 1 bit
+    // headroom.
+    chSysPolledDelayX(US2RTC(1 * MHZ, (1000000U * 11 / SERIAL_USART_SPEED)));
+    // Disable tx state machine to not interfere with our tx pin manipulation
+    pio_sm_set_enabled(pio, tx_state_machine, false);
+    gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_2MA);
+    pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << SERIAL_USART_TX_PIN, 1U << SERIAL_USART_TX_PIN);
+    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, false);
+    pio_sm_set_enabled(pio, rx_state_machine, true);
+    osalSysUnlock();
+}
+
+static inline void leave_rx_state(void) {
+    osalSysLock();
+    // In Half-duplex operation the tx pin dual-functions as sender and
+    // receiver. To not receive the data we will send, we disable the receiving
+    // state machine.
+    pio_sm_set_enabled(pio, rx_state_machine, false);
+    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, true);
+    pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U, 1U << SERIAL_USART_TX_PIN);
+    gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA);
+    pio_sm_restart(pio, tx_state_machine);
+    pio_sm_set_enabled(pio, tx_state_machine, true);
+    osalSysUnlock();
+}
+#else
+// All this trickery is gladly not necessary for full-duplex.
+static inline void enter_rx_state(void) {}
+static inline void leave_rx_state(void) {}
+#endif
+
+/**
+ * @brief Clear the RX and TX hardware FIFOs of the state machines.
+ */
+inline void serial_transport_driver_clear(void) {
+    osalSysLock();
+    pio_sm_clear_fifos(pio, rx_state_machine);
+    pio_sm_clear_fifos(pio, tx_state_machine);
+    osalSysUnlock();
+}
+
+static inline msg_t sync_tx(sysinterval_t timeout) {
+    msg_t msg = MSG_OK;
+    osalSysLock();
+    while (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) {
+        pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true);
+        msg = osalThreadSuspendTimeoutS(&tx_thread, timeout);
+        if (msg < MSG_OK) {
+            break;
+        }
+    }
+    osalSysUnlock();
+    return msg;
+}
+
+static inline bool send_impl(const uint8_t* source, const size_t size) {
+    size_t send = 0;
+    msg_t  msg;
+    while (send < size) {
+        msg = sync_tx(TIME_MS2I(SERIAL_USART_TIMEOUT));
+        if (msg < MSG_OK) {
+            return false;
+        }
+
+        osalSysLock();
+        while (send < size) {
+            if (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) {
+                break;
+            }
+            if (send >= size) {
+                break;
+            }
+            pio_sm_put(pio, tx_state_machine, (uint32_t)(*source));
+            source++;
+            send++;
+        }
+        osalSysUnlock();
+    }
+
+    return send == size;
+}
+
+/**
+ * @brief Blocking send of buffer with timeout.
+ *
+ * @return true Send success.
+ * @return false Send failed.
+ */
+inline bool serial_transport_send(const uint8_t* source, const size_t size) {
+    leave_rx_state();
+    bool result = send_impl(source, size);
+    enter_rx_state();
+
+    return result;
+}
+
+static inline msg_t sync_rx(sysinterval_t timeout) {
+    msg_t msg = MSG_OK;
+    osalSysLock();
+    while (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) {
+        pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true);
+        msg = osalThreadSuspendTimeoutS(&rx_thread, timeout);
+        if (msg < MSG_OK) {
+            break;
+        }
+    }
+    osalSysUnlock();
+    return msg;
+}
+
+static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout) {
+    size_t read = 0U;
+
+    while (read < size) {
+        msg_t msg = sync_rx(timeout);
+        if (msg < MSG_OK) {
+            return false;
+        }
+        osalSysLock();
+        while (true) {
+            if (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) {
+                break;
+            }
+            if (read >= size) {
+                break;
+            }
+            *destination++ = *((uint8_t*)&pio->rxf[rx_state_machine] + 3U);
+            read++;
+        }
+        osalSysUnlock();
+    }
+
+    return read == size;
+}
+
+/**
+ * @brief  Blocking receive of size * bytes with timeout.
+ *
+ * @return true Receive success.
+ * @return false Receive failed, e.g. by timeout.
+ */
+inline bool serial_transport_receive(uint8_t* destination, const size_t size) {
+    return receive_impl(destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT));
+}
+
+/**
+ * @brief  Blocking receive of size * bytes.
+ *
+ * @return true Receive success.
+ * @return false Receive failed.
+ */
+inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) {
+    return receive_impl(destination, size, TIME_INFINITE);
+}
+
+static inline void pio_tx_init(pin_t tx_pin) {
+    uint pio_idx = pio_get_index(pio);
+    uint offset  = pio_add_program(pio, &uart_tx_program);
+
+#if defined(SERIAL_USART_FULL_DUPLEX)
+    // clang-format off
+    iomode_t tx_pin_mode = PAL_RP_GPIO_OE |
+                           PAL_RP_PAD_SLEWFAST |
+                           PAL_RP_PAD_DRIVE4 |
+                           (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
+    // clang-format on
+    pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << tx_pin, 1U << tx_pin);
+    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true);
+#else
+    // clang-format off
+    iomode_t tx_pin_mode = PAL_RP_PAD_IE |
+                           PAL_RP_GPIO_OE |
+                           PAL_RP_PAD_SCHMITT |
+                           PAL_RP_PAD_PUE |
+                           PAL_RP_PAD_SLEWFAST |
+                           PAL_RP_PAD_DRIVE12 |
+                           PAL_RP_IOCTRL_OEOVER_DRVINVPERI |
+                           (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
+    // clang-format on
+    pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U << tx_pin, 1U << tx_pin);
+    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true);
+#endif
+
+    palSetLineMode(tx_pin, tx_pin_mode);
+
+    pio_sm_config config = pio_get_default_sm_config();
+    sm_config_set_wrap(&config, offset + UART_TX_WRAP_TARGET, offset + UART_TX_WRAP);
+#if defined(SERIAL_USART_FULL_DUPLEX)
+    sm_config_set_sideset(&config, 2, true, false);
+#else
+    sm_config_set_sideset(&config, 2, true, true);
+#endif
+    // OUT shifts to right, no autopull
+    sm_config_set_out_shift(&config, true, false, 32);
+    // We are mapping both OUT and side-set to the same pin, because sometimes
+    // we need to assert user data onto the pin (with OUT) and sometimes
+    // assert constant values (start/stop bit)
+    sm_config_set_out_pins(&config, tx_pin, 1);
+    sm_config_set_sideset_pins(&config, tx_pin);
+    // We only need TX, so get an 8-deep FIFO!
+    sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);
+    // SM transmits 1 bit per 8 execution cycles.
+    float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED);
+    sm_config_set_clkdiv(&config, div);
+    pio_sm_init(pio, tx_state_machine, offset, &config);
+    pio_sm_set_enabled(pio, tx_state_machine, true);
+}
+
+static inline void pio_rx_init(pin_t rx_pin) {
+    uint offset = pio_add_program(pio, &uart_rx_program);
+
+#if defined(SERIAL_USART_FULL_DUPLEX)
+    uint pio_idx = pio_get_index(pio);
+    pio_sm_set_consecutive_pindirs(pio, rx_state_machine, rx_pin, 1, false);
+    // clang-format off
+    iomode_t rx_pin_mode = PAL_RP_PAD_IE |
+                           PAL_RP_PAD_SCHMITT |
+                           PAL_RP_PAD_PUE |
+                           (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
+    // clang-format on
+    palSetLineMode(rx_pin, rx_pin_mode);
+#endif
+
+    pio_sm_config config = pio_get_default_sm_config();
+    sm_config_set_wrap(&config, offset + UART_RX_WRAP_TARGET, offset + UART_RX_WRAP);
+    sm_config_set_in_pins(&config, rx_pin); // for WAIT, IN
+    sm_config_set_jmp_pin(&config, rx_pin); // for JMP
+    // Shift to right, autopush disabled
+    sm_config_set_in_shift(&config, true, false, 32);
+    // Deeper FIFO as we're not doing any TX
+    sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX);
+    // SM transmits 1 bit per 8 execution cycles.
+    float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED);
+    sm_config_set_clkdiv(&config, div);
+    pio_sm_init(pio, rx_state_machine, offset, &config);
+    pio_sm_set_enabled(pio, rx_state_machine, true);
+}
+
+static inline void pio_init(pin_t tx_pin, pin_t rx_pin) {
+    uint pio_idx = pio_get_index(pio);
+
+    /* Get PIOx peripheral out of reset state. */
+    hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1);
+
+    tx_state_machine = pio_claim_unused_sm(pio, true);
+    if (tx_state_machine < 0) {
+        dprintln("ERROR: Failed to acquire state machine for serial transmission!");
+        return;
+    }
+    pio_tx_init(tx_pin);
+
+    rx_state_machine = pio_claim_unused_sm(pio, true);
+    if (rx_state_machine < 0) {
+        dprintln("ERROR: Failed to acquire state machine for serial reception!");
+        return;
+    }
+    pio_rx_init(rx_pin);
+
+    // Enable error flag IRQ source for rx state machine
+    pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true);
+    pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true);
+    pio_set_irq0_source_enabled(pio, pis_interrupt0, true);
+
+    // Enable PIO specific interrupt vector
+#if defined(SERIAL_PIO_USE_PIO1)
+    nvicEnableVector(RP_PIO1_IRQ_0_NUMBER, RP_IRQ_UART0_PRIORITY);
+#else
+    nvicEnableVector(RP_PIO0_IRQ_0_NUMBER, RP_IRQ_UART0_PRIORITY);
+#endif
+
+    enter_rx_state();
+}
+
+/**
+ * @brief PIO driver specific initialization function for the master side.
+ */
+void serial_transport_driver_master_init(void) {
+#if defined(SERIAL_USART_FULL_DUPLEX)
+    pin_t tx_pin = SERIAL_USART_TX_PIN;
+    pin_t rx_pin = SERIAL_USART_RX_PIN;
+#else
+    pin_t tx_pin = SERIAL_USART_TX_PIN;
+    pin_t rx_pin = SERIAL_USART_TX_PIN;
+#endif
+
+#if defined(SERIAL_USART_PIN_SWAP)
+    pio_init(rx_pin, tx_pin);
+#else
+    pio_init(tx_pin, rx_pin);
+#endif
+}
+
+/**
+ * @brief PIO driver specific initialization function for the slave side.
+ */
+void serial_transport_driver_slave_init(void) {
+#if defined(SERIAL_USART_FULL_DUPLEX)
+    pin_t tx_pin = SERIAL_USART_TX_PIN;
+    pin_t rx_pin = SERIAL_USART_RX_PIN;
+#else
+    pin_t tx_pin = SERIAL_USART_TX_PIN;
+    pin_t rx_pin = SERIAL_USART_TX_PIN;
+#endif
+
+    pio_init(tx_pin, rx_pin);
+}
diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c
new file mode 100644
index 0000000000..bc34eded14
--- /dev/null
+++ b/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c
@@ -0,0 +1,189 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "quantum.h"
+#include "ws2812.h"
+#include "hardware/pio.h"
+#include "hardware/clocks.h"
+
+#if !defined(MCU_RP)
+#    error PIO Driver is only available for Raspberry Pi 2040 MCUs!
+#endif
+
+#if defined(WS2812_PIO_USE_PIO1)
+static const PIO pio = pio1;
+#else
+static const PIO pio = pio0;
+#endif
+
+#if !defined(RP_DMA_PRIORITY_WS2812)
+#    define RP_DMA_PRIORITY_WS2812 12
+#endif
+
+static int state_machine = -1;
+
+#define WS2812_WRAP_TARGET 0
+#define WS2812_WRAP 3
+
+#define WS2812_T1 2
+#define WS2812_T2 5
+#define WS2812_T3 3
+
+#if defined(WS2812_EXTERNAL_PULLUP)
+
+#    pragma message "The GPIOs of the RP2040 are NOT 5V tolerant! Make sure to NOT apply any voltage over 3.3V to the RGB data pin."
+
+// clang-format off
+static const uint16_t ws2812_program_instructions[] = {
+            //     .wrap_target
+    0x7221, //  0: out    x, 1            side 1 [2] 
+    0x0123, //  1: jmp    !x, 3           side 0 [1] 
+    0x0400, //  2: jmp    0               side 0 [4] 
+    0xb442, //  3: nop                    side 1 [4] 
+            //     .wrap
+};
+
+#else
+
+static const uint16_t ws2812_program_instructions[] = {
+             //     .wrap_target
+    0x6221,  //  0: out    x, 1            side 0 [2]
+    0x1123,  //  1: jmp    !x, 3           side 1 [1]
+    0x1400,  //  2: jmp    0               side 1 [4]
+    0xa442,  //  3: nop                    side 0 [4]
+             //     .wrap
+};
+// clang-format on
+#endif
+
+static const pio_program_t ws2812_program = {
+    .instructions = ws2812_program_instructions,
+    .length       = 4,
+    .origin       = -1,
+};
+
+static uint32_t                WS2812_BUFFER[RGBLED_NUM];
+static const rp_dma_channel_t* WS2812_DMA_CHANNEL;
+
+bool ws2812_init(void) {
+    uint pio_idx = pio_get_index(pio);
+    /* Get PIOx peripheral out of reset state. */
+    hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1);
+
+    // clang-format off
+    iomode_t rgb_pin_mode = PAL_RP_PAD_SLEWFAST |
+                            PAL_RP_GPIO_OE |
+                            (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);
+    // clang-format on
+
+    palSetLineMode(RGB_DI_PIN, rgb_pin_mode);
+
+    state_machine = pio_claim_unused_sm(pio, true);
+    if (state_machine < 0) {
+        dprintln("ERROR: Failed to acquire state machine for WS2812 output!");
+        return false;
+    }
+
+    uint offset = pio_add_program(pio, &ws2812_program);
+
+    pio_sm_set_consecutive_pindirs(pio, state_machine, RGB_DI_PIN, 1, true);
+
+    pio_sm_config config = pio_get_default_sm_config();
+    sm_config_set_wrap(&config, offset + WS2812_WRAP_TARGET, offset + WS2812_WRAP);
+    sm_config_set_sideset_pins(&config, RGB_DI_PIN);
+    sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);
+
+#if defined(WS2812_EXTERNAL_PULLUP)
+    /* Instruct side-set to change the pin-directions instead of outputting
+     * a logic level. We generate our levels the following way:
+     *
+     * 1: Set RGB data pin to high impedance input and let the pull-up drive the
+     * signal high.
+     *
+     * 0: Set RGB data pin to low impedance output and drive the pin low.
+     */
+    sm_config_set_sideset(&config, 1, false, true);
+#else
+    sm_config_set_sideset(&config, 1, false, false);
+#endif
+
+#if defined(RGBW)
+    sm_config_set_out_shift(&config, false, true, 32);
+#else
+    sm_config_set_out_shift(&config, false, true, 24);
+#endif
+
+    int   cycles_per_bit = WS2812_T1 + WS2812_T2 + WS2812_T3;
+    float div            = clock_get_hz(clk_sys) / (800.0f * KHZ * cycles_per_bit);
+    sm_config_set_clkdiv(&config, div);
+
+    pio_sm_init(pio, state_machine, offset, &config);
+    pio_sm_set_enabled(pio, state_machine, true);
+
+    WS2812_DMA_CHANNEL = dmaChannelAlloc(RP_DMA_CHANNEL_ID_ANY, RP_DMA_PRIORITY_WS2812, NULL, NULL);
+
+    // clang-format off
+    uint32_t mode = DMA_CTRL_TRIG_INCR_READ |
+                    DMA_CTRL_TRIG_DATA_SIZE_WORD |
+                    DMA_CTRL_TRIG_IRQ_QUIET |
+                    DMA_CTRL_TRIG_TREQ_SEL(pio_idx == 0 ? state_machine : state_machine + 8);
+    // clang-format on
+
+    dmaChannelSetModeX(WS2812_DMA_CHANNEL, mode);
+    dmaChannelSetDestinationX(WS2812_DMA_CHANNEL, (uint32_t)&pio->txf[state_machine]);
+    return true;
+}
+
+/**
+ * @brief Convert RGBW value into WS2812 compatible 32-bit data word.
+ */
+__always_inline static uint32_t rgbw8888_to_u32(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {
+#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)
+    return ((uint32_t)green << 24) | ((uint32_t)red << 16) | ((uint32_t)blue << 8) | ((uint32_t)white);
+#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)
+    return ((uint32_t)red << 24) | ((uint32_t)green << 16) | ((uint32_t)blue << 8) | ((uint32_t)white);
+#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)
+    return ((uint32_t)blue << 24) | ((uint32_t)green << 16) | ((uint32_t)red << 8) | ((uint32_t)white);
+#endif
+}
+
+static inline void sync_ws2812_transfer(void) {
+    if (unlikely(dmaChannelIsBusyX(WS2812_DMA_CHANNEL) || !pio_sm_is_tx_fifo_empty(pio, state_machine))) {
+        fast_timer_t start = timer_read_fast();
+        do {
+            // Abort the synchronization if we have to wait longer than the total
+            // count of LEDs in millisecounds. This is safely much longer than it
+            // would take to push all the data out.
+            if (unlikely(timer_elapsed_fast(start) > RGBLED_NUM)) {
+                dprintln("ERROR: WS2812 DMA transfer has stalled, aborting!");
+                dmaChannelDisableX(WS2812_DMA_CHANNEL);
+                return;
+            }
+
+        } while (dmaChannelIsBusyX(WS2812_DMA_CHANNEL) || !pio_sm_is_tx_fifo_empty(pio, state_machine));
+        // We wait for the WS2812 chain to reset after all data has been pushed
+        // out.
+        wait_us(WS2812_TRST_US);
+    }
+}
+
+void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) {
+    static bool is_initialized = false;
+    if (unlikely(!is_initialized)) {
+        is_initialized = ws2812_init();
+    }
+
+    sync_ws2812_transfer();
+
+    for (int i = 0; i < leds; i++) {
+#if defined(RGBW)
+        WS2812_BUFFER[i] = rgbw8888_to_u32(ledarray[i].r, ledarray[i].g, ledarray[i].b, ledarray[i].w);
+#else
+        WS2812_BUFFER[i] = rgbw8888_to_u32(ledarray[i].r, ledarray[i].g, ledarray[i].b, 0);
+#endif
+    }
+
+    dmaChannelSetSourceX(WS2812_DMA_CHANNEL, (uint32_t)WS2812_BUFFER);
+    dmaChannelSetCounterX(WS2812_DMA_CHANNEL, leds);
+    dmaChannelEnableX(WS2812_DMA_CHANNEL);
+}
diff --git a/platforms/chibios/flash.mk b/platforms/chibios/flash.mk
index 86bbc22943..790c4f3316 100644
--- a/platforms/chibios/flash.mk
+++ b/platforms/chibios/flash.mk
@@ -108,6 +108,8 @@ else ifeq ($(strip $(BOOTLOADER)),kiibohd)
 	$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)
 else ifeq ($(strip $(BOOTLOADER)),tinyuf2)
 	$(UNSYNC_OUTPUT_CMD) && $(call EXEC_UF2_UTIL_DEPLOY)
+else ifeq ($(strip $(BOOTLOADER)),rp2040)
+	$(UNSYNC_OUTPUT_CMD) && $(call EXEC_UF2_UTIL_DEPLOY)
 else ifeq ($(strip $(MCU_FAMILY)),KINETIS)
 	$(UNSYNC_OUTPUT_CMD) && $(call EXEC_TEENSY)
 else ifeq ($(strip $(MCU_FAMILY)),MIMXRT1062)
diff --git a/platforms/chibios/platform.mk b/platforms/chibios/platform.mk
index 72428a762f..32b8c5a946 100644
--- a/platforms/chibios/platform.mk
+++ b/platforms/chibios/platform.mk
@@ -88,9 +88,9 @@ ifeq ("$(MCU_PORT_NAME)","")
 endif
 
 ifeq ("$(wildcard $(PLATFORM_MK))","")
-    PLATFORM_MK = $(CHIBIOS)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
+    PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
     ifeq ("$(wildcard $(PLATFORM_MK))","")
-        PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
+        PLATFORM_MK = $(CHIBIOS)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk
     endif
 endif
 
@@ -288,6 +288,17 @@ EXTRAINCDIRS += $(CHIBIOS)/os/license $(CHIBIOS)/os/oslib/include \
          $(STREAMSINC) $(CHIBIOS)/os/various $(COMMON_VPATH)
 
 #
+# QMK specific MCU family support selection.
+##############################################################################
+ifneq ("$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_SERIES).mk)","")
+    # Either by MCU series e.g. STM32/STM32F1xx.mk or...
+    include $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_SERIES).mk
+else ifneq ("$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_FAMILY).mk)","")
+    # By MCU family e.g. STM32/STM32.mk
+    include $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_FAMILY).mk
+endif
+
+#
 # ChibiOS-Contrib
 ##############################################################################
 
diff --git a/platforms/chibios/vendors/RP/RP2040.mk b/platforms/chibios/vendors/RP/RP2040.mk
new file mode 100644
index 0000000000..1aa925cb15
--- /dev/null
+++ b/platforms/chibios/vendors/RP/RP2040.mk
@@ -0,0 +1,285 @@
+#
+# Raspberry Pi RP2040 specific drivers
+##############################################################################
+COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/vendor/$(MCU_FAMILY)/$(MCU_SERIES)
+
+ifeq ($(strip $(WS2812_DRIVER)), vendor)
+    OPT_DEFS += -DRP_DMA_REQUIRED=TRUE
+endif
+
+#
+# Raspberry Pi Pico SDK Support
+##############################################################################
+ADEFS  += -DCRT0_VTOR_INIT=1 \
+		  -DCRT0_EXTRA_CORES_NUMBER=0
+
+CFLAGS += -DPICO_NO_FPGA_CHECK \
+          -DNDEBUG
+
+#
+# Pico SDK source and header files needed by QMK and ChibiOS
+##############################################################################
+PICOSDKROOT   := $(TOP_DIR)/lib/pico-sdk
+
+PICOSDKSRC     = $(PICOSDKROOT)/src/rp2_common/hardware_clocks/clocks.c \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_pll/pll.c \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_pio/pio.c \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_gpio/gpio.c \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_claim/claim.c \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_watchdog/watchdog.c \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_xosc/xosc.c \
+                 $(PICOSDKROOT)/src/rp2_common/pico_bootrom/bootrom.c
+
+PICOSDKINC     = $(CHIBIOS)//os/various/pico_bindings/dumb/include \
+                 $(PICOSDKROOT)/src/common/pico_base/include \
+                 $(PICOSDKROOT)/src/rp2_common/pico_platform/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_base/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_clocks/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_claim/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_gpio/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_irq/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_pll/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_pio/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_sync/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_resets/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_watchdog/include \
+                 $(PICOSDKROOT)/src/rp2_common/hardware_xosc/include \
+                 $(PICOSDKROOT)/src/rp2040/hardware_regs/include \
+                 $(PICOSDKROOT)/src/rp2040/hardware_structs/include \
+                 $(PICOSDKROOT)/src/boards/include \
+                 $(PICOSDKROOT)/src/rp2_common/pico_bootrom/include
+
+PLATFORM_SRC += $(PICOSDKSRC)
+EXTRAINCDIRS += $(PICOSDKINC)
+
+PLATFORM_RP2040_PATH := $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)
+
+PLATFORM_SRC +=	$(PLATFORM_RP2040_PATH)/stage2_bootloaders.c \
+				$(PLATFORM_RP2040_PATH)/pico_sdk_shims.c
+
+EXTRAINCDIRS += $(PLATFORM_RP2040_PATH)
+
+#
+# RP2040 optimized compiler intrinsics
+##############################################################################
+
+# Enables optimized Compiler intrinsics which are located in the RP2040
+# bootrom. This needs startup code and linker script support from ChibiOS,
+# which is WIP. Therefore disabled by default for now.
+RP2040_INTRINSICS_ENABLED ?= no
+ifeq ($(strip $(RP2040_INTRINSICS_ENABLED)), yes)
+    PICOSDKINTRINSICSSRC =  $(PICOSDKROOT)/src/rp2_common/pico_float/float_aeabi.S \
+                            $(PICOSDKROOT)/src/rp2_common/pico_float/float_math.c \
+                            $(PICOSDKROOT)/src/rp2_common/pico_float/float_init_rom.c \
+                            $(PICOSDKROOT)/src/rp2_common/pico_float/float_v1_rom_shim.S \
+                            $(PICOSDKROOT)/src/rp2_common/pico_double/double_aeabi.S \
+                            $(PICOSDKROOT)/src/rp2_common/pico_double/double_math.c \
+                            $(PICOSDKROOT)/src/rp2_common/pico_double/double_init_rom.c \
+                            $(PICOSDKROOT)/src/rp2_common/pico_double/double_v1_rom_shim.S \
+                            $(PICOSDKROOT)/src/rp2_common/pico_divider/divider.S \
+                            $(PICOSDKROOT)/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S \
+                            $(PICOSDKROOT)/src/rp2_common/pico_mem_ops/mem_ops_aeabi.S \
+                            $(PICOSDKROOT)/src/rp2_common/pico_malloc/pico_malloc.c \
+                            $(PICOSDKROOT)/src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
+
+    PICOSDKINTRINSICSINC =  $(PICOSDKROOT)/src/common/pico_base/include \
+                            $(PICOSDKROOT)/src/rp2_common/pico_platfrom/include \
+                            $(PICOSDKROOT)/src/rp2_common/pico_bootrom/include \
+                            $(PICOSDKROOT)/src/rp2_common/hardware_divider/include \
+                            $(PICOSDKROOT)/src/rp2_common/pico_float/include \
+                            $(PICOSDKROOT)/src/rp2_common/pico_double/include \
+                            $(PICOSDKROOT)/src/rp2_common/pico_malloc/include
+
+    OPT_DEFS += -DPICO_FLOAT_SUPPORT_ROM_V1=0 -DPICO_DOUBLE_SUPPORT_ROM_V1=0
+
+    CFLAGS += -Wl,--defsym=__StackLimit=__heap_end__
+    CFLAGS += -Wl,--defsym=__unhandled_user_irq=_unhandled_exception
+    CFLAGS += -Wl,--build-id=none
+
+    # single precision floating point intrinsics
+    OPT_DEFS += -DPICO_FLOAT_IN_RAM=1
+    OPT_DEFS += -DPICO_FLOAT_PROPAGATE_NANS=0
+
+    CFLAGS += -Wl,--wrap=__aeabi_fdiv
+    CFLAGS += -Wl,--wrap=__aeabi_fmul
+    CFLAGS += -Wl,--wrap=__aeabi_frsub
+    CFLAGS += -Wl,--wrap=__aeabi_fsub
+    CFLAGS += -Wl,--wrap=__aeabi_cfcmpeq
+    CFLAGS += -Wl,--wrap=__aeabi_cfrcmple
+    CFLAGS += -Wl,--wrap=__aeabi_cfcmple
+    CFLAGS += -Wl,--wrap=__aeabi_fcmpeq
+    CFLAGS += -Wl,--wrap=__aeabi_fcmplt
+    CFLAGS += -Wl,--wrap=__aeabi_fcmple
+    CFLAGS += -Wl,--wrap=__aeabi_fcmpge
+    CFLAGS += -Wl,--wrap=__aeabi_fcmpgt
+    CFLAGS += -Wl,--wrap=__aeabi_fcmpun
+    CFLAGS += -Wl,--wrap=__aeabi_i2f
+    CFLAGS += -Wl,--wrap=__aeabi_l2f
+    CFLAGS += -Wl,--wrap=__aeabi_ui2f
+    CFLAGS += -Wl,--wrap=__aeabi_ul2f
+    CFLAGS += -Wl,--wrap=__aeabi_i2f
+    CFLAGS += -Wl,--wrap=__aeabi_f2iz
+    CFLAGS += -Wl,--wrap=__aeabi_f2lz
+    CFLAGS += -Wl,--wrap=__aeabi_f2uiz
+    CFLAGS += -Wl,--wrap=__aeabi_f2ulz
+    CFLAGS += -Wl,--wrap=__aeabi_f2d
+    CFLAGS += -Wl,--wrap=sqrtf
+    CFLAGS += -Wl,--wrap=cosf
+    CFLAGS += -Wl,--wrap=sinf
+    CFLAGS += -Wl,--wrap=tanf
+    CFLAGS += -Wl,--wrap=atan2f
+    CFLAGS += -Wl,--wrap=expf
+    CFLAGS += -Wl,--wrap=logf
+    CFLAGS += -Wl,--wrap=ldexpf
+    CFLAGS += -Wl,--wrap=copysignf
+    CFLAGS += -Wl,--wrap=truncf
+    CFLAGS += -Wl,--wrap=floorf
+    CFLAGS += -Wl,--wrap=ceilf
+    CFLAGS += -Wl,--wrap=roundf
+    CFLAGS += -Wl,--wrap=sincosf
+    CFLAGS += -Wl,--wrap=asinf
+    CFLAGS += -Wl,--wrap=acosf
+    CFLAGS += -Wl,--wrap=atanf
+    CFLAGS += -Wl,--wrap=sinhf
+    CFLAGS += -Wl,--wrap=coshf
+    CFLAGS += -Wl,--wrap=tanhf
+    CFLAGS += -Wl,--wrap=asinhf
+    CFLAGS += -Wl,--wrap=acoshf
+    CFLAGS += -Wl,--wrap=atanhf
+    CFLAGS += -Wl,--wrap=exp2f
+    CFLAGS += -Wl,--wrap=log2f
+    CFLAGS += -Wl,--wrap=exp10f
+    CFLAGS += -Wl,--wrap=log10f
+    CFLAGS += -Wl,--wrap=powf
+    CFLAGS += -Wl,--wrap=powintf
+    CFLAGS += -Wl,--wrap=hypotf
+    CFLAGS += -Wl,--wrap=cbrtf
+    CFLAGS += -Wl,--wrap=fmodf
+    CFLAGS += -Wl,--wrap=dremf
+    CFLAGS += -Wl,--wrap=remainderf
+    CFLAGS += -Wl,--wrap=remquof
+    CFLAGS += -Wl,--wrap=expm1f
+    CFLAGS += -Wl,--wrap=log1pf
+    CFLAGS += -Wl,--wrap=fmaf
+
+    # double precision floating point intrinsics
+    OPT_DEFS += -DPICO_DOUBLE_IN_RAM=1
+    OPT_DEFS += -DPICO_DOUBLE_PROPAGATE_NANS=0
+
+    CFLAGS += -Wl,--wrap=__aeabi_dadd
+    CFLAGS += -Wl,--wrap=__aeabi_ddiv
+    CFLAGS += -Wl,--wrap=__aeabi_dmul
+    CFLAGS += -Wl,--wrap=__aeabi_drsub
+    CFLAGS += -Wl,--wrap=__aeabi_dsub
+    CFLAGS += -Wl,--wrap=__aeabi_cdcmpeq
+    CFLAGS += -Wl,--wrap=__aeabi_cdrcmple
+    CFLAGS += -Wl,--wrap=__aeabi_cdcmple
+    CFLAGS += -Wl,--wrap=__aeabi_dcmpeq
+    CFLAGS += -Wl,--wrap=__aeabi_dcmplt
+    CFLAGS += -Wl,--wrap=__aeabi_dcmple
+    CFLAGS += -Wl,--wrap=__aeabi_dcmpge
+    CFLAGS += -Wl,--wrap=__aeabi_dcmpgt
+    CFLAGS += -Wl,--wrap=__aeabi_dcmpun
+    CFLAGS += -Wl,--wrap=__aeabi_i2d
+    CFLAGS += -Wl,--wrap=__aeabi_l2d
+    CFLAGS += -Wl,--wrap=__aeabi_ui2d
+    CFLAGS += -Wl,--wrap=__aeabi_ul2d
+    CFLAGS += -Wl,--wrap=__aeabi_d2iz
+    CFLAGS += -Wl,--wrap=__aeabi_d2lz
+    CFLAGS += -Wl,--wrap=__aeabi_d2uiz
+    CFLAGS += -Wl,--wrap=__aeabi_d2ulz
+    CFLAGS += -Wl,--wrap=__aeabi_d2f
+    CFLAGS += -Wl,--wrap=sqrt
+    CFLAGS += -Wl,--wrap=cos
+    CFLAGS += -Wl,--wrap=sin
+    CFLAGS += -Wl,--wrap=tan
+    CFLAGS += -Wl,--wrap=atan2
+    CFLAGS += -Wl,--wrap=exp
+    CFLAGS += -Wl,--wrap=log
+    CFLAGS += -Wl,--wrap=ldexp
+    CFLAGS += -Wl,--wrap=copysign
+    CFLAGS += -Wl,--wrap=trunc
+    CFLAGS += -Wl,--wrap=floor
+    CFLAGS += -Wl,--wrap=ceil
+    CFLAGS += -Wl,--wrap=round
+    CFLAGS += -Wl,--wrap=sincos
+    CFLAGS += -Wl,--wrap=asin
+    CFLAGS += -Wl,--wrap=acos
+    CFLAGS += -Wl,--wrap=atan
+    CFLAGS += -Wl,--wrap=sinh
+    CFLAGS += -Wl,--wrap=cosh
+    CFLAGS += -Wl,--wrap=tanh
+    CFLAGS += -Wl,--wrap=asinh
+    CFLAGS += -Wl,--wrap=acosh
+    CFLAGS += -Wl,--wrap=atanh
+    CFLAGS += -Wl,--wrap=exp2
+    CFLAGS += -Wl,--wrap=log2
+    CFLAGS += -Wl,--wrap=exp10
+    CFLAGS += -Wl,--wrap=log10
+    CFLAGS += -Wl,--wrap=pow
+    CFLAGS += -Wl,--wrap=powint
+    CFLAGS += -Wl,--wrap=hypot
+    CFLAGS += -Wl,--wrap=cbrt
+    CFLAGS += -Wl,--wrap=fmod
+    CFLAGS += -Wl,--wrap=drem
+    CFLAGS += -Wl,--wrap=remainder
+    CFLAGS += -Wl,--wrap=remquo
+    CFLAGS += -Wl,--wrap=expm1
+    CFLAGS += -Wl,--wrap=log1p
+    CFLAGS += -Wl,--wrap=fma
+
+    # bit operation intrinsics
+    OPT_DEFS += -DPICO_BITS_IN_RAM=1
+
+    CFLAGS += -Wl,--wrap=__clzsi2
+    CFLAGS += -Wl,--wrap=__clzsi2
+    CFLAGS += -Wl,--wrap=__clzdi2
+    CFLAGS += -Wl,--wrap=__ctzsi2
+    CFLAGS += -Wl,--wrap=__ctzdi2
+    CFLAGS += -Wl,--wrap=__popcountsi2
+    CFLAGS += -Wl,--wrap=__popcountdi2
+    CFLAGS += -Wl,--wrap=__clz
+    CFLAGS += -Wl,--wrap=__clzl
+    CFLAGS += -Wl,--wrap=__clzsi2
+    CFLAGS += -Wl,--wrap=__clzll
+
+    # integer division intrinsics
+    OPT_DEFS += -DPICO_DIVIDER_IN_RAM=1
+    OPT_DEFS += -DPICO_DIVIDER_DISABLE_INTERRUPTS=1
+
+    CFLAGS += -Wl,--wrap=__aeabi_idiv
+    CFLAGS += -Wl,--wrap=__aeabi_idivmod
+    CFLAGS += -Wl,--wrap=__aeabi_ldivmod
+    CFLAGS += -Wl,--wrap=__aeabi_uidiv
+    CFLAGS += -Wl,--wrap=__aeabi_uidivmod
+    CFLAGS += -Wl,--wrap=__aeabi_uldivmod
+
+    # 64bit integer intrinsics
+    OPT_DEFS += -DPICO_INT64_OPS_IN_RAM=1
+
+    CFLAGS += -Wl,--wrap=__aeabi_lmul
+
+    # malloc and friends functions
+    OPT_DEFS += -DPICO_USE_MALLOC_MUTEX=0
+    OPT_DEFS += -DPICO_DEBUG_MALLOC=0
+    OPT_DEFS ?= -DPICO_MALLOC_PANIC=0
+
+    CFLAGS += -Wl,--wrap=malloc
+    CFLAGS += -Wl,--wrap=calloc
+    CFLAGS += -Wl,--wrap=free
+
+    # memory operation intrinsics
+    OPT_DEFS += -DPICO_MEM_IN_RAM=1
+
+    CFLAGS += -Wl,--wrap=memcpy
+    CFLAGS += -Wl,--wrap=memset
+    CFLAGS += -Wl,--wrap=__aeabi_memcpy
+    CFLAGS += -Wl,--wrap=__aeabi_memset
+    CFLAGS += -Wl,--wrap=__aeabi_memcpy4
+    CFLAGS += -Wl,--wrap=__aeabi_memset4
+    CFLAGS += -Wl,--wrap=__aeabi_memcpy8
+    CFLAGS += -Wl,--wrap=__aeabi_memset8
+
+    PLATFORM_SRC += $(PICOSDKINTRINSICSSRC)
+    EXTRAINCDIRS += $(PICOSDKINTRINSICSINC)
+endif
diff --git a/platforms/chibios/vendors/RP/_pin_defs.h b/platforms/chibios/vendors/RP/_pin_defs.h
new file mode 100644
index 0000000000..4241845369
--- /dev/null
+++ b/platforms/chibios/vendors/RP/_pin_defs.h
@@ -0,0 +1,37 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+/* RP2040 GPIO Numbering */
+#define GP0 0U
+#define GP1 1U
+#define GP2 2U
+#define GP3 3U
+#define GP4 4U
+#define GP5 5U
+#define GP6 6U
+#define GP7 7U
+#define GP8 8U
+#define GP9 9U
+#define GP10 10U
+#define GP11 11U
+#define GP12 12U
+#define GP13 13U
+#define GP14 14U
+#define GP15 15U
+#define GP16 16U
+#define GP17 17U
+#define GP18 18U
+#define GP19 19U
+#define GP20 20U
+#define GP21 21U
+#define GP22 22U
+#define GP23 23U
+#define GP24 24U
+#define GP25 25U
+#define GP26 26U
+#define GP27 27U
+#define GP28 28U
+#define GP29 29U
+#define GP30 30U
diff --git a/platforms/chibios/vendors/RP/pico_sdk_shims.c b/platforms/chibios/vendors/RP/pico_sdk_shims.c
new file mode 100644
index 0000000000..f6e3943928
--- /dev/null
+++ b/platforms/chibios/vendors/RP/pico_sdk_shims.c
@@ -0,0 +1,9 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stdbool.h>
+#include <ch.h>
+
+void panic(const char *fmt, ...) {
+    chSysHalt(fmt);
+}
diff --git a/platforms/chibios/vendors/RP/stage2_bootloaders.c b/platforms/chibios/vendors/RP/stage2_bootloaders.c
new file mode 100644
index 0000000000..f22112fca9
--- /dev/null
+++ b/platforms/chibios/vendors/RP/stage2_bootloaders.c
@@ -0,0 +1,174 @@
+// ----------------------------------------------------------------------------
+// Pre-compiled second stage boot code for RP2040.
+//
+// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd.
+// SPDX-License-Identifier: BSD-3-Clause
+// ----------------------------------------------------------------------------
+
+#include <stdint.h>
+
+#define BOOTLOADER_SECTION __attribute__ ((used, section (".boot2")))
+
+#if defined(RP2040_FLASH_AT25SF128A)
+
+uint8_t BOOTLOADER_SECTION BOOT2_AT25SF128A[256] = {
+  0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,
+  0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b,
+  0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,
+  0x99, 0x50, 0x2a, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,
+  0x00, 0xf0, 0x42, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21,
+  0x19, 0x66, 0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x31, 0x21, 0x19, 0x66,
+  0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e,
+  0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1,
+  0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60,
+  0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21,
+  0x19, 0x66, 0x20, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21,
+  0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,
+  0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49,
+  0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5,
+  0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42,
+  0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7,
+  0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40,
+  0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00,
+  0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0x20,
+  0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xc0, 0xdd, 0xc0, 0xb5
+};
+
+#elif defined(RP2040_FLASH_GD25Q64CS)
+
+uint8_t BOOTLOADER_SECTION BOOT2_GD25Q64CS[256] = {
+  0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,
+  0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b,
+  0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,
+  0x99, 0x50, 0x2a, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,
+  0x00, 0xf0, 0x42, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21,
+  0x19, 0x66, 0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x31, 0x21, 0x19, 0x66,
+  0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e,
+  0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1,
+  0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60,
+  0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xe7, 0x21,
+  0x19, 0x66, 0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21,
+  0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,
+  0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49,
+  0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5,
+  0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42,
+  0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7,
+  0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40,
+  0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00,
+  0x21, 0x12, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18, 0x22, 0x10, 0x00, 0xa0,
+  0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xe2, 0xd9, 0xa2, 0xb5
+};
+
+#elif defined(RP2040_FLASH_W25X10CL)
+
+uint8_t BOOTLOADER_SECTION BOOT2_W25X10CL[256] = {
+  0x00, 0xb5, 0x14, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,
+  0x12, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60, 0x11, 0x49, 0x12, 0x48,
+  0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xbb, 0x21, 0x19, 0x66, 0x02, 0x21,
+  0x19, 0x66, 0x08, 0x21, 0x98, 0x6a, 0x08, 0x42, 0xfc, 0xd0, 0x00, 0x21,
+  0x99, 0x60, 0x0c, 0x49, 0x0a, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,
+  0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x08, 0x48, 0x09, 0x49,
+  0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x3f, 0x00, 0x1d, 0x12, 0x00, 0x00,
+  0xf4, 0x00, 0x00, 0x18, 0x1e, 0x10, 0x00, 0x20, 0x00, 0x01, 0x00, 0x10,
+  0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x7c, 0x81, 0x53, 0x9a
+};
+
+#elif defined(RP2040_FLASH_IS25LP080)
+
+uint8_t BOOTLOADER_SECTION BOOT2_IS25LP080[256] = {
+  0x00, 0xb5, 0x2b, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,
+  0x29, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x28, 0x48, 0x00, 0xf0,
+  0x42, 0xf8, 0x28, 0x4a, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21, 0x19, 0x66,
+  0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20,
+  0x1a, 0x66, 0x00, 0xf0, 0x2b, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x1f, 0x48,
+  0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21,
+  0x99, 0x60, 0x1d, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60, 0x1c, 0x49,
+  0x1c, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66,
+  0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60,
+  0x17, 0x49, 0x16, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc,
+  0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x14, 0x48, 0x14, 0x49, 0x08, 0x60,
+  0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a,
+  0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1,
+  0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff,
+  0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+  0x00, 0x00, 0x07, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
+  0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18,
+  0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x28, 0x33, 0x43, 0xb2
+};
+
+#elif defined(RP2040_FLASH_GENERIC_03H)
+
+uint8_t BOOTLOADER_SECTION BOOT2_GENERIC_03H[256] = {
+  0x00, 0xb5, 0x0c, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,
+  0x0a, 0x49, 0x19, 0x60, 0x0a, 0x49, 0x0b, 0x48, 0x01, 0x60, 0x00, 0x21,
+  0x59, 0x60, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0,
+  0x00, 0x47, 0x07, 0x48, 0x07, 0x49, 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3,
+  0x08, 0x88, 0x08, 0x47, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x1f, 0x00,
+  0x18, 0x02, 0x00, 0x03, 0xf4, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x10,
+  0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x2c, 0xec, 0x21, 0x0d
+};
+
+#else
+
+uint8_t BOOTLOADER_SECTION BOOT2_W25Q080[256] = {
+  0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,
+  0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b,
+  0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,
+  0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,
+  0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21,
+  0x19, 0x66, 0x00, 0xf0, 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66,
+  0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e,
+  0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21,
+  0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60,
+  0x00, 0x21, 0x59, 0x60, 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21,
+  0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0,
+  0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60,
+  0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47,
+  0x12, 0x48, 0x13, 0x49, 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88,
+  0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0,
+  0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66,
+  0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd,
+  0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00,
+  0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18,
+  0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x07, 0x0b, 0x8f, 0xd5
+};
+
+#endif