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