summaryrefslogtreecommitdiff
path: root/platforms/chibios
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/chibios')
-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/boards/common/ld/RP2040_FLASH_TIMECRIT.ld117
-rw-r--r--platforms/chibios/boards/common/ld/RP2040_rules_data_with_timecrit.ld46
-rw-r--r--platforms/chibios/bootloaders/rp2040.c57
-rw-r--r--platforms/chibios/bootloaders/stm32_dfu.c4
-rw-r--r--platforms/chibios/chibios_config.h23
-rw-r--r--platforms/chibios/drivers/eeprom/eeprom_stm32.c (renamed from platforms/chibios/eeprom_stm32.c)0
-rw-r--r--platforms/chibios/drivers/eeprom/eeprom_stm32.h (renamed from platforms/chibios/eeprom_stm32.h)0
-rw-r--r--platforms/chibios/drivers/eeprom/eeprom_stm32_defs.h (renamed from platforms/chibios/eeprom_stm32_defs.h)0
-rw-r--r--platforms/chibios/drivers/eeprom/eeprom_teensy.c (renamed from platforms/chibios/eeprom_teensy.c)0
-rwxr-xr-xplatforms/chibios/drivers/eeprom/eeprom_teensy.h (renamed from platforms/chibios/eeprom_teensy.h)0
-rw-r--r--platforms/chibios/drivers/flash/flash_stm32.c (renamed from platforms/chibios/flash_stm32.c)0
-rw-r--r--platforms/chibios/drivers/flash/flash_stm32.h (renamed from platforms/chibios/flash_stm32.h)0
-rw-r--r--platforms/chibios/drivers/serial.c15
-rw-r--r--platforms/chibios/drivers/serial_protocol.c164
-rw-r--r--platforms/chibios/drivers/serial_protocol.h49
-rw-r--r--platforms/chibios/drivers/serial_usart.c322
-rw-r--r--platforms/chibios/drivers/serial_usart.h118
-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/drivers/wear_leveling/wear_leveling_efl.c140
-rw-r--r--platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h50
-rw-r--r--platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c59
-rw-r--r--platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h67
-rw-r--r--platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c221
-rw-r--r--platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h32
-rw-r--r--platforms/chibios/flash.mk10
-rw-r--r--platforms/chibios/platform.mk24
-rw-r--r--platforms/chibios/vendors/RP/RP2040.mk287
-rw-r--r--platforms/chibios/vendors/RP/_pin_defs.h37
-rw-r--r--platforms/chibios/vendors/RP/pico_sdk_shims.c13
-rw-r--r--platforms/chibios/vendors/RP/stage2_bootloaders.c178
42 files changed, 2769 insertions, 272 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/boards/common/ld/RP2040_FLASH_TIMECRIT.ld b/platforms/chibios/boards/common/ld/RP2040_FLASH_TIMECRIT.ld
new file mode 100644
index 0000000000..66ed4ce086
--- /dev/null
+++ b/platforms/chibios/boards/common/ld/RP2040_FLASH_TIMECRIT.ld
@@ -0,0 +1,117 @@
+/*
+ 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.
+*/
+
+/*
+ * RP2040 memory setup.
+ */
+MEMORY
+{
+ flash0 (rx) : org = 0x00000000, len = 16k /* ROM */
+ flash1 (rx) : org = 0x10000000, len = DEFINED(FLASH_LEN) ? FLASH_LEN : 2048k /* XIP */
+ flash2 (rx) : org = 0x00000000, len = 0
+ flash3 (rx) : org = 0x00000000, len = 0
+ flash4 (rx) : org = 0x00000000, len = 0
+ flash5 (rx) : org = 0x00000000, len = 0
+ flash6 (rx) : org = 0x00000000, len = 0
+ flash7 (rx) : org = 0x00000000, len = 0
+ ram0 (wx) : org = 0x20000000, len = 256k /* SRAM0 striped */
+ ram1 (wx) : org = 0x00000000, len = 256k /* SRAM0 non striped */
+ ram2 (wx) : org = 0x00000000, len = 0
+ ram3 (wx) : org = 0x00000000, len = 0
+ ram4 (wx) : org = 0x20040000, len = 4k /* SRAM4 */
+ ram5 (wx) : org = 0x20041000, len = 4k /* SRAM5 */
+ ram6 (wx) : org = 0x00000000, len = 0
+ ram7 (wx) : org = 0x20041f00, len = 256 /* SRAM5 boot */
+}
+
+/* For each data/text section two region are defined, a virtual region
+ and a load region (_LMA suffix).*/
+
+/* Flash region to be used for exception vectors.*/
+REGION_ALIAS("VECTORS_FLASH", flash1);
+REGION_ALIAS("VECTORS_FLASH_LMA", flash1);
+
+/* Flash region to be used for constructors and destructors.*/
+REGION_ALIAS("XTORS_FLASH", flash1);
+REGION_ALIAS("XTORS_FLASH_LMA", flash1);
+
+/* Flash region to be used for code text.*/
+REGION_ALIAS("TEXT_FLASH", flash1);
+REGION_ALIAS("TEXT_FLASH_LMA", flash1);
+
+/* Flash region to be used for read only data.*/
+REGION_ALIAS("RODATA_FLASH", flash1);
+REGION_ALIAS("RODATA_FLASH_LMA", flash1);
+
+/* Flash region to be used for various.*/
+REGION_ALIAS("VARIOUS_FLASH", flash1);
+REGION_ALIAS("VARIOUS_FLASH_LMA", flash1);
+
+/* Flash region to be used for RAM(n) initialization data.*/
+REGION_ALIAS("RAM_INIT_FLASH_LMA", flash1);
+
+/* RAM region to be used for Main stack. This stack accommodates the processing
+ of all exceptions and interrupts.*/
+REGION_ALIAS("MAIN_STACK_RAM", ram4);
+
+/* RAM region to be used for the process stack. This is the stack used by
+ the main() function.*/
+REGION_ALIAS("PROCESS_STACK_RAM", ram4);
+
+/* RAM region to be used for Main stack. This stack accommodates the processing
+ of all exceptions and interrupts.*/
+REGION_ALIAS("C1_MAIN_STACK_RAM", ram5);
+
+/* RAM region to be used for the process stack. This is the stack used by
+ the main() function.*/
+REGION_ALIAS("C1_PROCESS_STACK_RAM", ram5);
+
+/* RAM region to be used for data segment.*/
+REGION_ALIAS("DATA_RAM", ram0);
+REGION_ALIAS("DATA_RAM_LMA", flash1);
+
+/* RAM region to be used for BSS segment.*/
+REGION_ALIAS("BSS_RAM", ram0);
+
+/* RAM region to be used for the default heap.*/
+REGION_ALIAS("HEAP_RAM", ram0);
+
+SECTIONS
+{
+ .flash_begin : {
+ __flash_binary_start = .;
+ } > flash1
+
+ .boot2 : {
+ __boot2_start__ = .;
+ KEEP (*(.boot2))
+ __boot2_end__ = .;
+ } > flash1
+}
+
+/* Generic rules inclusion.*/
+INCLUDE rules_stacks.ld
+INCLUDE rules_stacks_c1.ld
+INCLUDE RP2040_rules_code_with_boot2.ld
+INCLUDE RP2040_rules_data_with_timecrit.ld
+INCLUDE rules_memory.ld
+
+SECTIONS
+{
+ .flash_end : {
+ __flash_binary_end = .;
+ } > flash1
+}
diff --git a/platforms/chibios/boards/common/ld/RP2040_rules_data_with_timecrit.ld b/platforms/chibios/boards/common/ld/RP2040_rules_data_with_timecrit.ld
new file mode 100644
index 0000000000..a9a47be983
--- /dev/null
+++ b/platforms/chibios/boards/common/ld/RP2040_rules_data_with_timecrit.ld
@@ -0,0 +1,46 @@
+/*
+ ChibiOS - Copyright (C) 2006..2018 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.
+*/
+
+SECTIONS
+{
+ .data : ALIGN(4)
+ {
+ PROVIDE(_textdata = LOADADDR(.data));
+ PROVIDE(_data = .);
+ __textdata_base__ = LOADADDR(.data);
+ __data_base__ = .;
+ *(vtable)
+ *(.time_critical*)
+ . = ALIGN(4);
+ *(.data)
+ *(.data.*)
+ *(.ramtext)
+ . = ALIGN(4);
+ PROVIDE(_edata = .);
+ __data_end__ = .;
+ } > DATA_RAM AT > DATA_RAM_LMA
+
+ .bss (NOLOAD) : ALIGN(4)
+ {
+ __bss_base__ = .;
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ PROVIDE(end = .);
+ } > BSS_RAM
+}
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/bootloaders/stm32_dfu.c b/platforms/chibios/bootloaders/stm32_dfu.c
index ff866bd2bc..7b4ab86033 100644
--- a/platforms/chibios/bootloaders/stm32_dfu.c
+++ b/platforms/chibios/bootloaders/stm32_dfu.c
@@ -38,7 +38,7 @@ extern uint32_t __ram0_end__;
# endif
# ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY
-# define STM32_BOOTLOADER_DUAL_BANK_DELAY 100000
+# define STM32_BOOTLOADER_DUAL_BANK_DELAY 100
# endif
__attribute__((weak)) void bootloader_jump(void) {
@@ -55,7 +55,7 @@ __attribute__((weak)) void bootloader_jump(void) {
# endif
// Wait for a while for the capacitor to charge
- wait_ms(100);
+ wait_ms(STM32_BOOTLOADER_DUAL_BANK_DELAY);
// Issue a system reset to get the ROM bootloader to execute, with BOOT0 high
NVIC_SystemReset();
diff --git a/platforms/chibios/chibios_config.h b/platforms/chibios/chibios_config.h
index a7098f2713..e5aa8e3d05 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
@@ -74,7 +95,7 @@
#if defined(MCU_KINETIS)
# define CPU_CLOCK KINETIS_SYSCLK_FREQUENCY
-# if defined(K20x) || defined(KL2x)
+# if defined(K20x) || defined(K60x) || defined(KL2x)
# define USE_I2CV1
# define USE_I2CV1_CONTRIB // for some reason a bunch of ChibiOS-Contrib boards only have clock_speed
# define USE_GPIOV1
diff --git a/platforms/chibios/eeprom_stm32.c b/platforms/chibios/drivers/eeprom/eeprom_stm32.c
index a15bfe09ed..a15bfe09ed 100644
--- a/platforms/chibios/eeprom_stm32.c
+++ b/platforms/chibios/drivers/eeprom/eeprom_stm32.c
diff --git a/platforms/chibios/eeprom_stm32.h b/platforms/chibios/drivers/eeprom/eeprom_stm32.h
index 8fcfb556b8..8fcfb556b8 100644
--- a/platforms/chibios/eeprom_stm32.h
+++ b/platforms/chibios/drivers/eeprom/eeprom_stm32.h
diff --git a/platforms/chibios/eeprom_stm32_defs.h b/platforms/chibios/drivers/eeprom/eeprom_stm32_defs.h
index 57d0440330..57d0440330 100644
--- a/platforms/chibios/eeprom_stm32_defs.h
+++ b/platforms/chibios/drivers/eeprom/eeprom_stm32_defs.h
diff --git a/platforms/chibios/eeprom_teensy.c b/platforms/chibios/drivers/eeprom/eeprom_teensy.c
index c8777febde..c8777febde 100644
--- a/platforms/chibios/eeprom_teensy.c
+++ b/platforms/chibios/drivers/eeprom/eeprom_teensy.c
diff --git a/platforms/chibios/eeprom_teensy.h b/platforms/chibios/drivers/eeprom/eeprom_teensy.h
index 9a14a1fa79..9a14a1fa79 100755
--- a/platforms/chibios/eeprom_teensy.h
+++ b/platforms/chibios/drivers/eeprom/eeprom_teensy.h
diff --git a/platforms/chibios/flash_stm32.c b/platforms/chibios/drivers/flash/flash_stm32.c
index 72c41b8b78..72c41b8b78 100644
--- a/platforms/chibios/flash_stm32.c
+++ b/platforms/chibios/drivers/flash/flash_stm32.c
diff --git a/platforms/chibios/flash_stm32.h b/platforms/chibios/drivers/flash/flash_stm32.h
index 6c66642ec5..6c66642ec5 100644
--- a/platforms/chibios/flash_stm32.h
+++ b/platforms/chibios/drivers/flash/flash_stm32.h
diff --git a/platforms/chibios/drivers/serial.c b/platforms/chibios/drivers/serial.c
index 0cff057d1d..3fae5cd3a4 100644
--- a/platforms/chibios/drivers/serial.c
+++ b/platforms/chibios/drivers/serial.c
@@ -87,10 +87,7 @@ static THD_FUNCTION(Thread1, arg) {
chRegSetThreadName("blinker");
while (true) {
palWaitLineTimeout(SOFT_SERIAL_PIN, TIME_INFINITE);
-
- split_shared_memory_lock();
interrupt_handler(NULL);
- split_shared_memory_unlock();
}
}
@@ -155,6 +152,7 @@ static void __attribute__((noinline)) serial_write_byte(uint8_t data) {
// interrupt handle to be used by the slave device
void interrupt_handler(void *arg) {
+ split_shared_memory_lock_autounlock();
chSysLockFromISR();
sync_send();
@@ -212,6 +210,8 @@ void interrupt_handler(void *arg) {
static inline bool initiate_transaction(uint8_t sstd_index) {
if (sstd_index > NUM_TOTAL_TRANSACTIONS) return false;
+ split_shared_memory_lock_autounlock();
+
split_transaction_desc_t *trans = &split_transaction_table[sstd_index];
// TODO: remove extra delay between transactions
@@ -233,7 +233,7 @@ static inline bool initiate_transaction(uint8_t sstd_index) {
// check if the slave is present
if (serial_read_pin()) {
// slave failed to pull the line low, assume not present
- dprintf("serial::NO_RESPONSE\n");
+ serial_dprintf("serial::NO_RESPONSE\n");
chSysUnlock();
return false;
}
@@ -269,7 +269,7 @@ static inline bool initiate_transaction(uint8_t sstd_index) {
serial_delay();
if ((checksum_computed) != (checksum_received)) {
- dprintf("serial::FAIL[%u,%u,%u]\n", checksum_computed, checksum_received, sstd_index);
+ serial_dprintf("serial::FAIL[%u,%u,%u]\n", checksum_computed, checksum_received, sstd_index);
serial_output();
serial_high();
@@ -292,8 +292,5 @@ static inline bool initiate_transaction(uint8_t sstd_index) {
//
// this code is very time dependent, so we need to disable interrupts
bool soft_serial_transaction(int sstd_index) {
- split_shared_memory_lock();
- bool result = initiate_transaction((uint8_t)sstd_index);
- split_shared_memory_unlock();
- return result;
+ return initiate_transaction((uint8_t)sstd_index);
}
diff --git a/platforms/chibios/drivers/serial_protocol.c b/platforms/chibios/drivers/serial_protocol.c
new file mode 100644
index 0000000000..c95aed9885
--- /dev/null
+++ b/platforms/chibios/drivers/serial_protocol.c
@@ -0,0 +1,164 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <ch.h>
+
+#include "quantum.h"
+#include "serial.h"
+#include "serial_protocol.h"
+#include "printf.h"
+#include "synchronization_util.h"
+
+static inline bool initiate_transaction(uint8_t transaction_id);
+static inline bool react_to_transaction(void);
+
+/**
+ * @brief This thread runs on the slave and responds to transactions initiated
+ * by the master.
+ */
+static THD_WORKING_AREA(waSlaveThread, 1024);
+static THD_FUNCTION(SlaveThread, arg) {
+ (void)arg;
+ chRegSetThreadName("split_protocol_tx_rx");
+
+ while (true) {
+ if (unlikely(!react_to_transaction())) {
+ /* Clear the receive queue, to start with a clean slate.
+ * Parts of failed transactions or spurious bytes could still be in it. */
+ serial_transport_driver_clear();
+ }
+ }
+}
+
+/**
+ * @brief Slave specific initializations.
+ */
+void soft_serial_target_init(void) {
+ serial_transport_driver_slave_init();
+
+ /* Start transport thread. */
+ chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
+}
+
+/**
+ * @brief Master specific initializations.
+ */
+void soft_serial_initiator_init(void) {
+ serial_transport_driver_master_init();
+}
+
+/**
+ * @brief React to transactions started by the master.
+ */
+static inline bool react_to_transaction(void) {
+ uint8_t transaction_id = 0;
+ /* Wait until there is a transaction for us. */
+ if (unlikely(!serial_transport_receive_blocking(&transaction_id, sizeof(transaction_id)))) {
+ return false;
+ }
+
+ /* Sanity check that we are actually responding to a valid transaction. */
+ if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) {
+ return false;
+ }
+
+ split_shared_memory_lock_autounlock();
+
+ split_transaction_desc_t* transaction = &split_transaction_table[transaction_id];
+
+ /* Send back the handshake which is XORed as a simple checksum,
+ to signal that the slave is ready to receive possible transaction buffers */
+ transaction_id ^= NUM_TOTAL_TRANSACTIONS;
+ if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) {
+ return false;
+ }
+
+ /* Receive transaction buffer from the master. If this transaction requires it.*/
+ if (transaction->initiator2target_buffer_size) {
+ if (unlikely(!serial_transport_receive(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) {
+ return false;
+ }
+ }
+
+ /* Allow any slave processing to occur. */
+ if (transaction->slave_callback) {
+ transaction->slave_callback(transaction->initiator2target_buffer_size, split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size, split_trans_target2initiator_buffer(transaction));
+ }
+
+ /* Send transaction buffer to the master. If this transaction requires it. */
+ if (transaction->target2initiator_buffer_size) {
+ if (unlikely(!serial_transport_send(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * @brief Start transaction from the master half to the slave half.
+ *
+ * @param index Transaction Table index of the transaction to start.
+ * @return bool Indicates success of transaction.
+ */
+bool soft_serial_transaction(int index) {
+ bool result = initiate_transaction((uint8_t)index);
+
+ if (unlikely(!result)) {
+ /* Clear the receive queue, to start with a clean slate.
+ * Parts of failed transactions or spurious bytes could still be in it. */
+ serial_transport_driver_clear();
+ }
+
+ return result;
+}
+
+/**
+ * @brief Initiate transaction to slave half.
+ */
+static inline bool initiate_transaction(uint8_t transaction_id) {
+ /* Sanity check that we are actually starting a valid transaction. */
+ if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) {
+ serial_dprintf("SPLIT: illegal transaction id\n");
+ return false;
+ }
+
+ split_shared_memory_lock_autounlock();
+
+ split_transaction_desc_t* transaction = &split_transaction_table[transaction_id];
+
+ /* Send transaction table index to the slave, which doubles as basic handshake token. */
+ if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) {
+ serial_dprintf("SPLIT: sending handshake failed\n");
+ return false;
+ }
+
+ uint8_t transaction_id_shake = 0xFF;
+
+ /* Which we always read back first so that we can error out correctly.
+ * - due to the half duplex limitations on return codes, we always have to read *something*.
+ * - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready.
+ */
+ if (unlikely(!serial_transport_receive(&transaction_id_shake, sizeof(transaction_id_shake)) || (transaction_id_shake != (transaction_id ^ NUM_TOTAL_TRANSACTIONS)))) {
+ serial_dprintf("SPLIT: receiving handshake failed\n");
+ return false;
+ }
+
+ /* Send transaction buffer to the slave. If this transaction requires it. */
+ if (transaction->initiator2target_buffer_size) {
+ if (unlikely(!serial_transport_send(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) {
+ serial_dprintf("SPLIT: sending buffer failed\n");
+ return false;
+ }
+ }
+
+ /* Receive transaction buffer from the slave. If this transaction requires it. */
+ if (transaction->target2initiator_buffer_size) {
+ if (unlikely(!serial_transport_receive(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) {
+ serial_dprintf("SPLIT: receiving buffer failed\n");
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/platforms/chibios/drivers/serial_protocol.h b/platforms/chibios/drivers/serial_protocol.h
new file mode 100644
index 0000000000..4275a7f8d8
--- /dev/null
+++ b/platforms/chibios/drivers/serial_protocol.h
@@ -0,0 +1,49 @@
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#pragma once
+
+/**
+ * @brief Clears any intermediate sending or receiving state of the driver to a known good
+ * state. This happens after errors in the middle of transactions, to start with
+ * a clean slate.
+ */
+void serial_transport_driver_clear(void);
+
+/**
+ * @brief Driver specific initialization on the slave half.
+ */
+void serial_transport_driver_slave_init(void);
+
+/**
+ * @brief Driver specific specific initialization on the master half.
+ */
+void serial_transport_driver_master_init(void);
+
+/**
+ * @brief Blocking receive of size * bytes.
+ *
+ * @return true Receive success.
+ * @return false Receive failed, e.g. by bit errors.
+ */
+bool __attribute__((nonnull, hot)) serial_transport_receive(uint8_t* destination, const size_t size);
+
+/**
+ * @brief Blocking receive of size * bytes with an implicitly defined timeout.
+ *
+ * @return true Receive success.
+ * @return false Receive failed, e.g. by timeout or bit errors.
+ */
+bool __attribute__((nonnull, hot)) serial_transport_receive_blocking(uint8_t* destination, const size_t size);
+
+/**
+ * @brief Blocking send of buffer with timeout.
+ *
+ * @return true Send success.
+ * @return false Send failed, e.g. by timeout or bit errors.
+ */
+bool __attribute__((nonnull, hot)) serial_transport_send(const uint8_t* source, const size_t size);
diff --git a/platforms/chibios/drivers/serial_usart.c b/platforms/chibios/drivers/serial_usart.c
index e9fa4af7a3..6581a5b6e9 100644
--- a/platforms/chibios/drivers/serial_usart.c
+++ b/platforms/chibios/drivers/serial_usart.c
@@ -1,49 +1,55 @@
-/* Copyright 2021 QMK
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// Copyright 2021 QMK
+// Copyright 2022 Stefan Kerkmann
+// SPDX-License-Identifier: GPL-2.0-or-later
#include "serial_usart.h"
+#include "serial_protocol.h"
#include "synchronization_util.h"
#if defined(SERIAL_USART_CONFIG)
-static SerialConfig serial_config = SERIAL_USART_CONFIG;
-#else
-static SerialConfig serial_config = {
- .speed = (SERIAL_USART_SPEED), /* speed - mandatory */
+static QMKSerialConfig serial_config = SERIAL_USART_CONFIG;
+#elif defined(MCU_STM32) /* STM32 MCUs */
+static QMKSerialConfig serial_config = {
+# if HAL_USE_SERIAL
+ .speed = (SERIAL_USART_SPEED),
+# else
+ .baud = (SERIAL_USART_SPEED),
+# endif
.cr1 = (SERIAL_USART_CR1),
.cr2 = (SERIAL_USART_CR2),
# if !defined(SERIAL_USART_FULL_DUPLEX)
.cr3 = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */
# else
- .cr3 = (SERIAL_USART_CR3)
+ .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 SerialDriver* serial_driver = &SERIAL_USART_DRIVER;
+static QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER;
-static inline bool react_to_transactions(void);
-static inline bool __attribute__((nonnull)) receive(uint8_t* destination, const size_t size);
-static inline bool __attribute__((nonnull)) send(const uint8_t* source, const size_t size);
-static inline bool initiate_transaction(uint8_t sstd_index);
-static inline void usart_clear(void);
+#if HAL_USE_SERIAL
/**
- * @brief Clear the receive input queue.
+ * @brief SERIAL Driver startup routine.
*/
-static inline void usart_clear(void) {
+static inline void usart_driver_start(void) {
+ sdStart(serial_driver, &serial_config);
+}
+
+inline void serial_transport_driver_clear(void) {
osalSysLock();
bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);
osalSysUnlock();
@@ -64,36 +70,96 @@ static inline void usart_clear(void) {
}
}
+#elif HAL_USE_SIO
+
+void clear_rx_evt_cb(SIODriver* siop) {
+ osalSysLockFromISR();
+ /* If errors occured during transactions this callback is invoked. We just
+ * clear the error sources and move on. We rely on the fact that we check
+ * for the success of the transaction by comparing the received/send bytes
+ * with the actual received/send bytes in the send/receive functions. */
+ sioGetAndClearEventsI(serial_driver);
+ osalSysUnlockFromISR();
+}
+
+static const SIOOperation serial_usart_operation = {.rx_cb = NULL, .rx_idle_cb = NULL, .tx_cb = NULL, .tx_end_cb = NULL, .rx_evt_cb = &clear_rx_evt_cb};
+
/**
- * @brief Blocking send of buffer with timeout.
- *
- * @return true Send success.
- * @return false Send failed.
+ * @brief SIO Driver startup routine.
*/
-static inline bool send(const uint8_t* source, const size_t size) {
- bool success = (size_t)sdWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
+static inline void usart_driver_start(void) {
+ sioStart(serial_driver, &serial_config);
+ sioStartOperation(serial_driver, &serial_usart_operation);
+}
+
+inline void serial_transport_driver_clear(void) {
+ osalSysLock();
+ while (!sioIsRXEmptyX(serial_driver)) {
+ (void)sioGetX(serial_driver);
+ }
+ osalSysUnlock();
+}
+
+#else
+
+# error Either the SERIAL or SIO driver has to be activated to use the usart driver for split keyboards.
+
+#endif
+
+inline bool serial_transport_send(const uint8_t* source, const size_t size) {
+ bool success = (size_t)chnWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
#if !defined(SERIAL_USART_FULL_DUPLEX)
- if (success) {
- /* Half duplex fills the input queue with the data we wrote - just throw it away.
- Under the right circumstances (e.g. bad cables paired with high baud rates)
- less bytes can be present in the input queue, therefore a timeout is needed. */
- uint8_t dump[size];
- return receive(dump, size);
+ /* Half duplex fills the input queue with the data we wrote - just throw it away. */
+ if (likely(success)) {
+ size_t bytes_left = size;
+# if HAL_USE_SERIAL
+ /* The SERIAL driver uses large soft FIFOs that are filled from an IRQ
+ * context, so there is a delay between receiving the data and it
+ * becoming actually available, therefore we have to apply a timeout
+ * mechanism. Under the right circumstances (e.g. bad cables paired with
+ * high baud rates) less bytes can be present in the input queue as
+ * well. */
+ uint8_t dump[64];
+
+ while (unlikely(bytes_left >= 64)) {
+ if (unlikely(!serial_transport_receive(dump, 64))) {
+ return false;
+ }
+ bytes_left -= 64;
+ }
+
+ return serial_transport_receive(dump, bytes_left);
+# else
+ /* The SIO driver directly accesses the hardware FIFOs of the USART
+ * peripheral. As these are limited in depth, the RX FIFO might have been
+ * overflowed by a large that we just send. Therefore we attempt to read
+ * back all the data we send or until the FIFO runs empty in case it
+ * overflowed and data was truncated. */
+ if (unlikely(sioSynchronizeTXEnd(serial_driver, TIME_MS2I(SERIAL_USART_TIMEOUT)) < MSG_OK)) {
+ return false;
+ }
+
+ osalSysLock();
+ while (bytes_left > 0 && !sioIsRXEmptyX(serial_driver)) {
+ (void)sioGetX(serial_driver);
+ bytes_left--;
+ }
+ osalSysUnlock();
+# endif
}
#endif
return success;
}
-/**
- * @brief Blocking receive of size * bytes with timeout.
- *
- * @return true Receive success.
- * @return false Receive failed.
- */
-static inline bool receive(uint8_t* destination, const size_t size) {
- bool success = (size_t)sdReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
+inline bool serial_transport_receive(uint8_t* destination, const size_t size) {
+ bool success = (size_t)chnReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;
+ return success;
+}
+
+inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) {
+ bool success = (size_t)chnRead(serial_driver, destination, size) == size;
return success;
}
@@ -103,7 +169,7 @@ static inline bool receive(uint8_t* destination, const size_t size) {
* @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
@@ -113,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
@@ -124,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);
@@ -136,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
@@ -146,7 +217,7 @@ __attribute__((weak)) void usart_init(void) {
/**
* @brief Overridable master specific initializations.
*/
-__attribute__((weak, nonnull)) void usart_master_init(SerialDriver** driver) {
+__attribute__((weak, nonnull)) void usart_master_init(QMKSerialDriver** driver) {
(void)driver;
usart_init();
}
@@ -154,161 +225,22 @@ __attribute__((weak, nonnull)) void usart_master_init(SerialDriver** driver) {
/**
* @brief Overridable slave specific initializations.
*/
-__attribute__((weak, nonnull)) void usart_slave_init(SerialDriver** driver) {
+__attribute__((weak, nonnull)) void usart_slave_init(QMKSerialDriver** driver) {
(void)driver;
usart_init();
}
-/**
- * @brief This thread runs on the slave and responds to transactions initiated
- * by the master.
- */
-static THD_WORKING_AREA(waSlaveThread, 1024);
-static THD_FUNCTION(SlaveThread, arg) {
- (void)arg;
- chRegSetThreadName("usart_tx_rx");
-
- while (true) {
- if (!react_to_transactions()) {
- /* Clear the receive queue, to start with a clean slate.
- * Parts of failed transactions or spurious bytes could still be in it. */
- usart_clear();
- }
- split_shared_memory_unlock();
- }
-}
-
-/**
- * @brief Slave specific initializations.
- */
-void soft_serial_target_init(void) {
+void serial_transport_driver_slave_init(void) {
usart_slave_init(&serial_driver);
-
- sdStart(serial_driver, &serial_config);
-
- /* Start transport thread. */
- chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
+ usart_driver_start();
}
-/**
- * @brief React to transactions started by the master.
- */
-static inline bool react_to_transactions(void) {
- /* Wait until there is a transaction for us. */
- uint8_t sstd_index = (uint8_t)sdGet(serial_driver);
-
- /* Sanity check that we are actually responding to a valid transaction. */
- if (sstd_index >= NUM_TOTAL_TRANSACTIONS) {
- return false;
- }
-
- split_shared_memory_lock();
- split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
-
- /* Send back the handshake which is XORed as a simple checksum,
- to signal that the slave is ready to receive possible transaction buffers */
- sstd_index ^= HANDSHAKE_MAGIC;
- if (!send(&sstd_index, sizeof(sstd_index))) {
- return false;
- }
-
- /* Receive transaction buffer from the master. If this transaction requires it.*/
- if (trans->initiator2target_buffer_size) {
- if (!receive(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
- return false;
- }
- }
-
- /* Allow any slave processing to occur. */
- if (trans->slave_callback) {
- trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, split_trans_target2initiator_buffer(trans));
- }
-
- /* Send transaction buffer to the master. If this transaction requires it. */
- if (trans->target2initiator_buffer_size) {
- if (!send(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * @brief Master specific initializations.
- */
-void soft_serial_initiator_init(void) {
+void serial_transport_driver_master_init(void) {
usart_master_init(&serial_driver);
#if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP)
serial_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
#endif
- sdStart(serial_driver, &serial_config);
-}
-
-/**
- * @brief Start transaction from the master half to the slave half.
- *
- * @param index Transaction Table index of the transaction to start.
- * @return bool Indicates success of transaction.
- */
-bool soft_serial_transaction(int index) {
- /* Clear the receive queue, to start with a clean slate.
- * Parts of failed transactions or spurious bytes could still be in it. */
- usart_clear();
-
- split_shared_memory_lock();
- bool result = initiate_transaction((uint8_t)index);
- split_shared_memory_unlock();
-
- return result;
-}
-
-/**
- * @brief Initiate transaction to slave half.
- */
-static inline bool initiate_transaction(uint8_t sstd_index) {
- /* Sanity check that we are actually starting a valid transaction. */
- if (sstd_index >= NUM_TOTAL_TRANSACTIONS) {
- dprintln("USART: Illegal transaction Id.");
- return false;
- }
-
- split_transaction_desc_t* trans = &split_transaction_table[sstd_index];
-
- /* Send transaction table index to the slave, which doubles as basic handshake token. */
- if (!send(&sstd_index, sizeof(sstd_index))) {
- dprintln("USART: Send Handshake failed.");
- return false;
- }
-
- uint8_t sstd_index_shake = 0xFF;
-
- /* Which we always read back first so that we can error out correctly.
- * - due to the half duplex limitations on return codes, we always have to read *something*.
- * - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready.
- */
- if (!receive(&sstd_index_shake, sizeof(sstd_index_shake)) || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
- dprintln("USART: Handshake failed.");
- return false;
- }
-
- /* Send transaction buffer to the slave. If this transaction requires it. */
- if (trans->initiator2target_buffer_size) {
- if (!send(split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size)) {
- dprintln("USART: Send failed.");
- return false;
- }
- }
-
- /* Receive transaction buffer from the slave. If this transaction requires it. */
- if (trans->target2initiator_buffer_size) {
- if (!receive(split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {
- dprintln("USART: Receive failed.");
- return false;
- }
- }
-
- return true;
+ usart_driver_start();
}
diff --git a/platforms/chibios/drivers/serial_usart.h b/platforms/chibios/drivers/serial_usart.h
index 81fe9e0113..fa062cd736 100644
--- a/platforms/chibios/drivers/serial_usart.h
+++ b/platforms/chibios/drivers/serial_usart.h
@@ -1,42 +1,12 @@
-/* Copyright 2021 QMK
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// Copyright 2021 QMK
+// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "quantum.h"
#include "serial.h"
-#include "printf.h"
-
-#include <ch.h>
#include <hal.h>
-#if !defined(SERIAL_USART_DRIVER)
-# define SERIAL_USART_DRIVER SD1
-#endif
-
-#if !defined(USE_GPIOV1)
-/* The default PAL alternate modes are used to signal that the pins are used for USART. */
-# if !defined(SERIAL_USART_TX_PAL_MODE)
-# define SERIAL_USART_TX_PAL_MODE 7
-# endif
-# if !defined(SERIAL_USART_RX_PAL_MODE)
-# define SERIAL_USART_RX_PAL_MODE 7
-# endif
-#endif
-
#if defined(SOFT_SERIAL_PIN)
# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
#endif
@@ -49,6 +19,62 @@
# define SERIAL_USART_RX_PIN A10
#endif
+#if !defined(SELECT_SOFT_SERIAL_SPEED)
+# define SELECT_SOFT_SERIAL_SPEED 1
+#endif
+
+#if defined(SERIAL_USART_SPEED)
+// Allow advanced users to directly set SERIAL_USART_SPEED
+#elif SELECT_SOFT_SERIAL_SPEED == 0
+# define SERIAL_USART_SPEED 460800
+#elif SELECT_SOFT_SERIAL_SPEED == 1
+# define SERIAL_USART_SPEED 230400
+#elif SELECT_SOFT_SERIAL_SPEED == 2
+# define SERIAL_USART_SPEED 115200
+#elif SELECT_SOFT_SERIAL_SPEED == 3
+# define SERIAL_USART_SPEED 57600
+#elif SELECT_SOFT_SERIAL_SPEED == 4
+# define SERIAL_USART_SPEED 38400
+#elif SELECT_SOFT_SERIAL_SPEED == 5
+# define SERIAL_USART_SPEED 19200
+#else
+# error invalid SELECT_SOFT_SERIAL_SPEED value
+#endif
+
+#if !defined(SERIAL_USART_TIMEOUT)
+# define SERIAL_USART_TIMEOUT 20
+#endif
+
+#if HAL_USE_SERIAL
+
+typedef SerialDriver QMKSerialDriver;
+typedef SerialConfig QMKSerialConfig;
+
+# if !defined(SERIAL_USART_DRIVER)
+# define SERIAL_USART_DRIVER SD1
+# endif
+
+#elif HAL_USE_SIO
+
+typedef SIODriver QMKSerialDriver;
+typedef SIOConfig QMKSerialConfig;
+
+# if !defined(SERIAL_USART_DRIVER)
+# define SERIAL_USART_DRIVER SIOD1
+# endif
+
+#endif
+
+#if !defined(USE_GPIOV1)
+/* The default PAL alternate modes are used to signal that the pins are used for USART. */
+# if !defined(SERIAL_USART_TX_PAL_MODE)
+# define SERIAL_USART_TX_PAL_MODE 7
+# endif
+# if !defined(SERIAL_USART_RX_PAL_MODE)
+# define SERIAL_USART_RX_PAL_MODE 7
+# endif
+#endif
+
#if !defined(USART_CR1_M0)
# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
#endif
@@ -86,31 +112,3 @@
(AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \
} while (0)
#endif
-
-#if !defined(SELECT_SOFT_SERIAL_SPEED)
-# define SELECT_SOFT_SERIAL_SPEED 1
-#endif
-
-#if defined(SERIAL_USART_SPEED)
-// Allow advanced users to directly set SERIAL_USART_SPEED
-#elif SELECT_SOFT_SERIAL_SPEED == 0
-# define SERIAL_USART_SPEED 460800
-#elif SELECT_SOFT_SERIAL_SPEED == 1
-# define SERIAL_USART_SPEED 230400
-#elif SELECT_SOFT_SERIAL_SPEED == 2
-# define SERIAL_USART_SPEED 115200
-#elif SELECT_SOFT_SERIAL_SPEED == 3
-# define SERIAL_USART_SPEED 57600
-#elif SELECT_SOFT_SERIAL_SPEED == 4
-# define SERIAL_USART_SPEED 38400
-#elif SELECT_SOFT_SERIAL_SPEED == 5
-# define SERIAL_USART_SPEED 19200
-#else
-# error invalid SELECT_SOFT_SERIAL_SPEED value
-#endif
-
-#if !defined(SERIAL_USART_TIMEOUT)
-# define SERIAL_USART_TIMEOUT 20
-#endif
-
-#define HANDSHAKE_MAGIC 7
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/drivers/wear_leveling/wear_leveling_efl.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c
new file mode 100644
index 0000000000..4b5639ee4a
--- /dev/null
+++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c
@@ -0,0 +1,140 @@
+// Copyright 2022 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <stdbool.h>
+#include <hal.h>
+#include "timer.h"
+#include "wear_leveling.h"
+#include "wear_leveling_internal.h"
+
+static flash_offset_t base_offset = UINT32_MAX;
+
+#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
+static flash_sector_t first_sector = WEAR_LEVELING_EFL_FIRST_SECTOR;
+#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
+static flash_sector_t first_sector = UINT16_MAX;
+#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
+
+static flash_sector_t sector_count = UINT16_MAX;
+static BaseFlash * flash;
+
+#ifndef WEAR_LEVELING_EFL_FIRST_SECTOR
+// "Automatic" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't.
+static inline uint32_t detect_flash_size(void) {
+# if defined(WEAR_LEVELING_EFL_FLASH_SIZE)
+ return WEAR_LEVELING_EFL_FLASH_SIZE;
+# elif defined(FLASH_BANK_SIZE)
+ return FLASH_BANK_SIZE;
+# elif defined(FLASH_SIZE)
+ return FLASH_SIZE;
+# elif defined(FLASHSIZE_BASE)
+# if defined(QMK_MCU_SERIES_STM32F0XX) || defined(QMK_MCU_SERIES_STM32F1XX) || defined(QMK_MCU_SERIES_STM32F3XX) || defined(QMK_MCU_SERIES_STM32F4XX) || defined(QMK_MCU_SERIES_STM32G4XX) || defined(QMK_MCU_SERIES_STM32L0XX) || defined(QMK_MCU_SERIES_STM32L4XX) || defined(QMK_MCU_SERIES_GD32VF103)
+ return ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) << 10U; // this register has the flash size in kB, so we convert it to bytes
+# elif defined(QMK_MCU_SERIES_STM32L1XX)
+# error This MCU family has an uncommon flash size register definition and has not been implemented. Perhaps try using the true EEPROM on the MCU instead?
+# endif
+# else
+# error Unknown flash size definition.
+ return 0;
+# endif
+}
+#endif // WEAR_LEVELING_EFL_FIRST_SECTOR
+
+bool backing_store_init(void) {
+ bs_dprintf("Init\n");
+ flash = (BaseFlash *)&EFLD1;
+
+ const flash_descriptor_t *desc = flashGetDescriptor(flash);
+ uint32_t counter = 0;
+
+#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
+
+ // Work out how many sectors we want to use, working forwards from the first sector specified
+ for (flash_sector_t i = 0; i < desc->sectors_count - first_sector; ++i) {
+ counter += flashGetSectorSize(flash, first_sector + i);
+ if (counter >= (WEAR_LEVELING_BACKING_SIZE)) {
+ sector_count = i + 1;
+ base_offset = flashGetSectorOffset(flash, first_sector);
+ break;
+ }
+ }
+ if (sector_count == UINT16_MAX || base_offset >= flash_size) {
+ // We didn't get the required number of sectors. Can't do anything here. Fault.
+ chSysHalt("Invalid sector count intended to be used with wear_leveling");
+ }
+
+#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
+
+ // Work out how many sectors we want to use, working backwards from the end of the flash
+ uint32_t flash_size = detect_flash_size();
+ flash_sector_t last_sector = desc->sectors_count;
+ for (flash_sector_t i = 0; i < desc->sectors_count; ++i) {
+ first_sector = desc->sectors_count - i - 1;
+ if (flashGetSectorOffset(flash, first_sector) >= flash_size) {
+ last_sector = first_sector;
+ continue;
+ }
+ counter += flashGetSectorSize(flash, first_sector);
+ if (counter >= (WEAR_LEVELING_BACKING_SIZE)) {
+ sector_count = last_sector - first_sector;
+ base_offset = flashGetSectorOffset(flash, first_sector);
+ break;
+ }
+ }
+
+#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)
+
+ return true;
+}
+
+bool backing_store_unlock(void) {
+ bs_dprintf("Unlock\n");
+ return eflStart(&EFLD1, NULL) == HAL_RET_SUCCESS;
+}
+
+bool backing_store_erase(void) {
+#ifdef WEAR_LEVELING_DEBUG_OUTPUT
+ uint32_t start = timer_read32();
+#endif
+
+ bool ret = true;
+ flash_error_t status;
+ for (int i = 0; i < sector_count; ++i) {
+ // Kick off the sector erase
+ status = flashStartEraseSector(flash, first_sector + i);
+ if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) {
+ ret = false;
+ }
+
+ // Wait for the erase to complete
+ status = flashWaitErase(flash);
+ if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) {
+ ret = false;
+ }
+ }
+
+ bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start)));
+ return ret;
+}
+
+bool backing_store_write(uint32_t address, backing_store_int_t value) {
+ uint32_t offset = (base_offset + address);
+ bs_dprintf("Write ");
+ wl_dump(offset, &value, sizeof(value));
+ value = ~value;
+ return flashProgram(flash, offset, sizeof(value), (const uint8_t *)&value) == FLASH_NO_ERROR;
+}
+
+bool backing_store_lock(void) {
+ bs_dprintf("Lock \n");
+ eflStop(&EFLD1);
+ return true;
+}
+
+bool backing_store_read(uint32_t address, backing_store_int_t *value) {
+ uint32_t offset = (base_offset + address);
+ backing_store_int_t *loc = (backing_store_int_t *)offset;
+ *value = ~(*loc);
+ bs_dprintf("Read ");
+ wl_dump(offset, value, sizeof(backing_store_int_t));
+ return true;
+}
diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h
new file mode 100644
index 0000000000..9e38c2965d
--- /dev/null
+++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h
@@ -0,0 +1,50 @@
+// Copyright 2022 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#ifndef __ASSEMBLER__
+# include <hal.h>
+#endif
+
+// Work out how many bytes per write to internal flash
+#ifndef BACKING_STORE_WRITE_SIZE
+// These need to match EFL's XXXXXX_FLASH_LINE_SIZE, see associated code in `lib/chibios/os/hal/ports/**/hal_efl_lld.c`,
+// or associated `stm32_registry.h` for the MCU in question (or equivalent for the family).
+# if defined(QMK_MCU_SERIES_GD32VF103)
+# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c
+# elif defined(QMK_MCU_FAMILY_NUC123)
+# define BACKING_STORE_WRITE_SIZE 4 // from hal_efl_lld.c
+# elif defined(QMK_MCU_FAMILY_STM32)
+# if defined(STM32_FLASH_LINE_SIZE) // from some family's stm32_registry.h file
+# define BACKING_STORE_WRITE_SIZE (STM32_FLASH_LINE_SIZE)
+# else
+# if defined(QMK_MCU_SERIES_STM32F1XX)
+# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c
+# elif defined(QMK_MCU_SERIES_STM32F3XX)
+# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c
+# elif defined(QMK_MCU_SERIES_STM32F4XX)
+# define BACKING_STORE_WRITE_SIZE (1 << STM32_FLASH_PSIZE) // from hal_efl_lld.c
+# elif defined(QMK_MCU_SERIES_STM32L4XX)
+# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c
+# elif defined(QMK_MCU_SERIES_STM32G0XX)
+# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c
+# elif defined(QMK_MCU_SERIES_STM32G4XX)
+# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c
+# else
+# error "ChibiOS hasn't defined STM32_FLASH_LINE_SIZE, and could not automatically determine BACKING_STORE_WRITE_SIZE" // normally defined in stm32_registry.h, should be set by STM32_FLASH_LINE_SIZE
+# endif
+# endif
+# else
+# error "Could not automatically determine BACKING_STORE_WRITE_SIZE"
+# endif
+#endif
+
+// 2kB backing space allocated
+#ifndef WEAR_LEVELING_BACKING_SIZE
+# define WEAR_LEVELING_BACKING_SIZE 2048
+#endif // WEAR_LEVELING_BACKING_SIZE
+
+// 1kB logical EEPROM
+#ifndef WEAR_LEVELING_LOGICAL_SIZE
+# define WEAR_LEVELING_LOGICAL_SIZE 1024
+#endif // WEAR_LEVELING_LOGICAL_SIZE
diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c
new file mode 100644
index 0000000000..87126c4467
--- /dev/null
+++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c
@@ -0,0 +1,59 @@
+// Copyright 2022 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <stdbool.h>
+#include <hal.h>
+#include "timer.h"
+#include "wear_leveling.h"
+#include "wear_leveling_internal.h"
+#include "flash_stm32.h"
+
+bool backing_store_init(void) {
+ bs_dprintf("Init\n");
+ return true;
+}
+
+bool backing_store_unlock(void) {
+ bs_dprintf("Unlock\n");
+ FLASH_Unlock();
+ return true;
+}
+
+bool backing_store_erase(void) {
+#ifdef WEAR_LEVELING_DEBUG_OUTPUT
+ uint32_t start = timer_read32();
+#endif
+
+ bool ret = true;
+ FLASH_Status status;
+ for (int i = 0; i < (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT); ++i) {
+ status = FLASH_ErasePage(WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS + (i * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE)));
+ if (status != FLASH_COMPLETE) {
+ ret = false;
+ }
+ }
+
+ bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start)));
+ return ret;
+}
+
+bool backing_store_write(uint32_t address, backing_store_int_t value) {
+ uint32_t offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address);
+ bs_dprintf("Write ");
+ wl_dump(offset, &value, sizeof(backing_store_int_t));
+ return FLASH_ProgramHalfWord(offset, ~value) == FLASH_COMPLETE;
+}
+
+bool backing_store_lock(void) {
+ bs_dprintf("Lock \n");
+ FLASH_Lock();
+ return true;
+}
+
+bool backing_store_read(uint32_t address, backing_store_int_t* value) {
+ uint32_t offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address);
+ backing_store_int_t* loc = (backing_store_int_t*)offset;
+ *value = ~(*loc);
+ bs_dprintf("Read ");
+ wl_dump(offset, loc, sizeof(backing_store_int_t));
+ return true;
+}
diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h
new file mode 100644
index 0000000000..1e4691a6c0
--- /dev/null
+++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h
@@ -0,0 +1,67 @@
+// Copyright 2022 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+// Work out the page size to use
+#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE
+# if defined(QMK_MCU_STM32F042)
+# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 1024
+# elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)
+# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 2048
+# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)
+# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 16384
+# endif
+#endif
+
+// Work out how much flash space we have
+#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE
+# define WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) // in kB
+#endif
+
+// The base location of program memory
+#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE
+# define WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE 0x08000000
+#endif
+
+// The number of pages to use
+#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT
+# if defined(QMK_MCU_STM32F042)
+# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 2
+# elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)
+# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1
+# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)
+# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1
+# endif
+#endif
+
+// The origin of the emulated eeprom
+#ifndef WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS
+# if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)
+# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS ((uintptr_t)(WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE) + WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE * 1024 - (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT * WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))
+# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)
+# if defined(BOOTLOADER_STM32)
+# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (1 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +16k
+# elif defined(BOOTLOADER_TINYUF2)
+# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (3 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +48k
+# endif
+# endif
+#endif
+
+// 2-byte writes
+#ifndef BACKING_STORE_WRITE_SIZE
+# define BACKING_STORE_WRITE_SIZE 2
+#endif
+
+// The amount of space to use for the entire set of emulation
+#ifndef WEAR_LEVELING_BACKING_SIZE
+# if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)
+# define WEAR_LEVELING_BACKING_SIZE 2048
+# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)
+# define WEAR_LEVELING_BACKING_SIZE 16384
+# endif
+#endif
+
+// The logical amount of eeprom available
+#ifndef WEAR_LEVELING_LOGICAL_SIZE
+# define WEAR_LEVELING_LOGICAL_SIZE 1024
+#endif
diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c
new file mode 100644
index 0000000000..640628e1e9
--- /dev/null
+++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c
@@ -0,0 +1,221 @@
+/**
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ * Copyright (c) 2022 Nick Brassel (@tzarc)
+ * Copyright (c) 2022 Stefan Kerkmann (@KarlK90)
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "pico/bootrom.h"
+#include "hardware/flash.h"
+#include "hardware/sync.h"
+#include "hardware/structs/ssi.h"
+#include "hardware/structs/ioqspi.h"
+
+#include <stdbool.h>
+#include "timer.h"
+#include "wear_leveling.h"
+#include "wear_leveling_internal.h"
+
+#ifndef WEAR_LEVELING_RP2040_FLASH_BULK_COUNT
+# define WEAR_LEVELING_RP2040_FLASH_BULK_COUNT 64
+#endif // WEAR_LEVELING_RP2040_FLASH_BULK_COUNT
+
+#define FLASHCMD_PAGE_PROGRAM 0x02
+#define FLASHCMD_READ_STATUS 0x05
+#define FLASHCMD_WRITE_ENABLE 0x06
+
+extern uint8_t BOOT2_ROM[256];
+static uint32_t BOOT2_ROM_RAM[64];
+
+static ssi_hw_t *const ssi = (ssi_hw_t *)XIP_SSI_BASE;
+
+// Sanity check
+check_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET);
+check_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET);
+
+static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) {
+ ((void (*)(void))BOOT2_ROM_RAM + 1)();
+}
+
+// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs
+// are still running, and the FIFO bottoms out. (the bootrom does the same)
+static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) {
+ uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;
+ hw_write_masked(&ioqspi_hw->io[1].ctrl, field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB, IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS);
+}
+
+// Also allow any unbounded loops to check whether the above abort condition
+// was asserted, and terminate early
+static int __no_inline_not_in_flash_func(flash_was_aborted)(void) {
+ return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) & IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS;
+}
+
+// Put bytes from one buffer, and get bytes into another buffer.
+// These can be the same buffer.
+// If tx is NULL then send zeroes.
+// If rx is NULL then all read data will be dropped.
+//
+// If rx_skip is nonzero, this many bytes will first be consumed from the FIFO,
+// before reading a further count bytes into *rx.
+// E.g. if you have written a command+address just before calling this function.
+static void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx, size_t count, size_t rx_skip) {
+ // Make sure there is never more data in flight than the depth of the RX
+ // FIFO. Otherwise, when we are interrupted for long periods, hardware
+ // will overflow the RX FIFO.
+ const uint max_in_flight = 16 - 2; // account for data internal to SSI
+ size_t tx_count = count;
+ size_t rx_count = count;
+ while (tx_count || rx_skip || rx_count) {
+ // NB order of reads, for pessimism rather than optimism
+ uint32_t tx_level = ssi_hw->txflr;
+ uint32_t rx_level = ssi_hw->rxflr;
+ bool did_something = false; // Expect this to be folded into control flow, not register
+ if (tx_count && tx_level + rx_level < max_in_flight) {
+ ssi->dr0 = (uint32_t)(tx ? *tx++ : 0);
+ --tx_count;
+ did_something = true;
+ }
+ if (rx_level) {
+ uint8_t rxbyte = ssi->dr0;
+ did_something = true;
+ if (rx_skip) {
+ --rx_skip;
+ } else {
+ if (rx) *rx++ = rxbyte;
+ --rx_count;
+ }
+ }
+ // APB load costs 4 cycles, so only do it on idle loops (our budget is
+ // 48 cyc/byte)
+ if (!did_something && __builtin_expect(flash_was_aborted(), 0)) break;
+ }
+ flash_cs_force(1);
+}
+
+// Convenience wrapper for above
+// (And it's hard for the debug host to get the tight timing between
+// cmd DR0 write and the remaining data)
+static void __no_inline_not_in_flash_func(_flash_do_cmd)(uint8_t cmd, const uint8_t *tx, uint8_t *rx, size_t count) {
+ flash_cs_force(0);
+ ssi->dr0 = cmd;
+ flash_put_get(tx, rx, count, 1);
+}
+
+// Timing of this one is critical, so do not expose the symbol to debugger etc
+static void __no_inline_not_in_flash_func(flash_put_cmd_addr)(uint8_t cmd, uint32_t addr) {
+ flash_cs_force(0);
+ addr |= cmd << 24;
+ for (int i = 0; i < 4; ++i) {
+ ssi->dr0 = addr >> 24;
+ addr <<= 8;
+ }
+}
+
+// Poll the flash status register until the busy bit (LSB) clears
+static void __no_inline_not_in_flash_func(flash_wait_ready)(void) {
+ uint8_t stat;
+ do {
+ _flash_do_cmd(FLASHCMD_READ_STATUS, NULL, &stat, 1);
+ } while (stat & 0x1 && !flash_was_aborted());
+}
+
+// Set the WEL bit (needed before any program/erase operation)
+static void __no_inline_not_in_flash_func(flash_enable_write)(void) {
+ _flash_do_cmd(FLASHCMD_WRITE_ENABLE, NULL, NULL, 0);
+}
+
+static void __no_inline_not_in_flash_func(pico_program_bulk)(uint32_t flash_address, backing_store_int_t *values, size_t item_count) {
+ rom_connect_internal_flash_fn connect_internal_flash = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH);
+ rom_flash_exit_xip_fn flash_exit_xip = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP);
+ rom_flash_flush_cache_fn flash_flush_cache = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);
+ assert(connect_internal_flash && flash_exit_xip && flash_flush_cache);
+
+ static backing_store_int_t bulk_write_buffer[WEAR_LEVELING_RP2040_FLASH_BULK_COUNT];
+
+ while (item_count) {
+ size_t batch_size = MIN(item_count, WEAR_LEVELING_RP2040_FLASH_BULK_COUNT);
+ for (size_t i = 0; i < batch_size; i++, values++, item_count--) {
+ bulk_write_buffer[i] = ~(*values);
+ }
+ __compiler_memory_barrier();
+
+ connect_internal_flash();
+ flash_exit_xip();
+ flash_enable_write();
+
+ flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_address);
+ flash_put_get((uint8_t *)bulk_write_buffer, NULL, batch_size * sizeof(backing_store_int_t), 4);
+ flash_wait_ready();
+ flash_address += batch_size * sizeof(backing_store_int_t);
+
+ flash_flush_cache();
+ flash_enable_xip_via_boot2();
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// QMK Wear-Leveling Backing Store implementation
+
+static int interrupts;
+
+bool backing_store_init(void) {
+ bs_dprintf("Init\n");
+ memcpy(BOOT2_ROM_RAM, BOOT2_ROM, sizeof(BOOT2_ROM));
+ __compiler_memory_barrier();
+ return true;
+}
+
+bool backing_store_unlock(void) {
+ bs_dprintf("Unlock\n");
+ return true;
+}
+
+bool backing_store_erase(void) {
+#ifdef WEAR_LEVELING_DEBUG_OUTPUT
+ uint32_t start = timer_read32();
+#endif
+
+ // Ensure the backing size can be cleanly subtracted from the flash size without alignment issues.
+ _Static_assert((WEAR_LEVELING_BACKING_SIZE) % (FLASH_SECTOR_SIZE) == 0, "Backing size must be a multiple of FLASH_SECTOR_SIZE");
+
+ interrupts = save_and_disable_interrupts();
+ flash_range_erase((WEAR_LEVELING_RP2040_FLASH_BASE), (WEAR_LEVELING_BACKING_SIZE));
+ restore_interrupts(interrupts);
+
+ bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start)));
+ return true;
+}
+
+bool backing_store_write(uint32_t address, backing_store_int_t value) {
+ return backing_store_write_bulk(address, &value, 1);
+}
+
+bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {
+ uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address;
+ bs_dprintf("Write ");
+ wl_dump(offset, values, sizeof(backing_store_int_t) * item_count);
+ interrupts = save_and_disable_interrupts();
+ pico_program_bulk(offset, values, item_count);
+ restore_interrupts(interrupts);
+ return true;
+}
+
+bool backing_store_lock(void) {
+ return true;
+}
+
+bool backing_store_read(uint32_t address, backing_store_int_t *value) {
+ return backing_store_read_bulk(address, value, 1);
+}
+
+bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {
+ uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address;
+ backing_store_int_t *loc = (backing_store_int_t *)((XIP_BASE) + offset);
+ for (size_t i = 0; i < item_count; ++i) {
+ values[i] = ~loc[i];
+ }
+ bs_dprintf("Read ");
+ wl_dump(offset, values, item_count * sizeof(backing_store_int_t));
+ return true;
+}
diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h
new file mode 100644
index 0000000000..93a9aa0372
--- /dev/null
+++ b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h
@@ -0,0 +1,32 @@
+// Copyright 2022 Nick Brassel (@tzarc)
+// SPDX-License-Identifier: GPL-2.0-or-later
+#pragma once
+
+#ifndef __ASSEMBLER__
+# include "hardware/flash.h"
+#endif
+
+// 2-byte writes
+#ifndef BACKING_STORE_WRITE_SIZE
+# define BACKING_STORE_WRITE_SIZE 2
+#endif
+
+// 64kB backing space allocated
+#ifndef WEAR_LEVELING_BACKING_SIZE
+# define WEAR_LEVELING_BACKING_SIZE 8192
+#endif // WEAR_LEVELING_BACKING_SIZE
+
+// 32kB logical EEPROM
+#ifndef WEAR_LEVELING_LOGICAL_SIZE
+# define WEAR_LEVELING_LOGICAL_SIZE 4096
+#endif // WEAR_LEVELING_LOGICAL_SIZE
+
+// Define how much flash space we have (defaults to lib/pico-sdk/src/boards/include/boards/***)
+#ifndef WEAR_LEVELING_RP2040_FLASH_SIZE
+# define WEAR_LEVELING_RP2040_FLASH_SIZE (PICO_FLASH_SIZE_BYTES)
+#endif
+
+// Define the location of emulated EEPROM
+#ifndef WEAR_LEVELING_RP2040_FLASH_BASE
+# define WEAR_LEVELING_RP2040_FLASH_BASE ((WEAR_LEVELING_RP2040_FLASH_SIZE) - (WEAR_LEVELING_BACKING_SIZE))
+#endif
diff --git a/platforms/chibios/flash.mk b/platforms/chibios/flash.mk
index a91ef2cf35..790c4f3316 100644
--- a/platforms/chibios/flash.mk
+++ b/platforms/chibios/flash.mk
@@ -54,11 +54,11 @@ endef
# TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS
# within the emulated eeprom via dfu-util or another tool
-ifneq (,$(filter $(MAKECMDGOALS),dfu-util-split-left))
+ifneq (,$(filter $(MAKECMDGOALS), dfu-util-split-left uf2-split-left))
OPT_DEFS += -DINIT_EE_HANDS_LEFT
endif
-ifneq (,$(filter $(MAKECMDGOALS),dfu-util-split-right))
+ifneq (,$(filter $(MAKECMDGOALS), dfu-util-split-right uf2-split-right))
OPT_DEFS += -DINIT_EE_HANDS_RIGHT
endif
@@ -66,6 +66,10 @@ dfu-util-split-left: dfu-util
dfu-util-split-right: dfu-util
+uf2-split-left: flash
+
+uf2-split-right: flash
+
ST_LINK_CLI ?= st-link_cli
ST_LINK_ARGS ?=
@@ -104,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 21751f23fd..63cc1a4b33 100644
--- a/platforms/chibios/platform.mk
+++ b/platforms/chibios/platform.mk
@@ -36,6 +36,7 @@ ifeq ($(strip $(MCU)), risc-v)
# RISC-V Support
# As of 7.4.2021 there is only one supported RISC-V platform in Chibios-Contrib,
# therefore all required settings are hard-coded
+ USE_CHIBIOS_CONTRIB = yes
STARTUP_MK = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC/mk/startup_$(MCU_STARTUP).mk
PORT_V = $(CHIBIOS_CONTRIB)/os/common/ports/RISCV-ECLIC/compilers/GCC/mk/port.mk
RULESPATH = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC
@@ -87,12 +88,17 @@ 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
+# If no MCU architecture specified, use the MCU instead (allows for mcu_selection.mk to swap to cortex-m0 etc.)
+ifeq ("$(MCU_ARCH)","")
+ MCU_ARCH = $(MCU)
+endif
+
include $(STARTUP_MK)
include $(PORT_V)
include $(PLATFORM_MK)
@@ -282,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
##############################################################################
@@ -334,8 +351,11 @@ SHARED_CFLAGS = -fomit-frame-pointer \
-fno-common \
-fshort-wchar
+LDSCRIPT_PATH := $(shell dirname "$(LDSCRIPT)")
+
# Shared Linker flags for all toolchains
SHARED_LDFLAGS = -T $(LDSCRIPT) \
+ -L $(LDSCRIPT_PATH) \
-Wl,--gc-sections \
-nostartfiles
diff --git a/platforms/chibios/vendors/RP/RP2040.mk b/platforms/chibios/vendors/RP/RP2040.mk
new file mode 100644
index 0000000000..de426c9c40
--- /dev/null
+++ b/platforms/chibios/vendors/RP/RP2040.mk
@@ -0,0 +1,287 @@
+#
+# 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_flash/flash.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_flash/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..239155c086
--- /dev/null
+++ b/platforms/chibios/vendors/RP/pico_sdk_shims.c
@@ -0,0 +1,13 @@
+// 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);
+}
+
+void hard_assertion_failure(void) {
+ panic("hard assert");
+}
diff --git a/platforms/chibios/vendors/RP/stage2_bootloaders.c b/platforms/chibios/vendors/RP/stage2_bootloaders.c
new file mode 100644
index 0000000000..e65b0a5802
--- /dev/null
+++ b/platforms/chibios/vendors/RP/stage2_bootloaders.c
@@ -0,0 +1,178 @@
+// ----------------------------------------------------------------------------
+// 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")))
+
+// clang-format off
+
+#if defined(RP2040_FLASH_AT25SF128A)
+
+uint8_t BOOTLOADER_SECTION BOOT2_ROM[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_ROM[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_ROM[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_ROM[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_ROM[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_ROM[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
+
+// clang-format on