diff options
author | Stefan Kerkmann <karlk90@pm.me> | 2022-06-30 13:19:27 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-30 13:19:27 +0200 |
commit | d7173967087e022d20d1f9c812b1b668e9d3f71b (patch) | |
tree | 68198271dd5125193795c399c6478ead0a71b09f /platforms/chibios | |
parent | d206c1791e5858323cff0664f39f95edc1381ac5 (diff) |
[Core] Add Raspberry Pi RP2040 support (#14877)
* Disable RESET keycode because of naming conflicts
* Add Pico SDK as submodule
* Add RP2040 build support to QMK
* Adjust USB endpoint structs for RP2040
* Add RP2040 bootloader and double-tap reset routine
* Add generic and pro micro RP2040 boards
* Add RP2040 onekey keyboard
* Add WS2812 PIO DMA enabled driver and documentation
Supports regular and open-drain output configuration. RP2040 GPIOs are
sadly not 5V tolerant, so this is a bit use-less or needs extra hardware
or you take the risk to fry your hardware.
* Adjust SIO Driver for RP2040
* Adjust I2C Driver for RP2040
* Adjust SPI Driver for RP2040
* Add PIO serial driver and documentation
* Add general RP2040 documentation
* Apply suggestions from code review
Co-authored-by: Nick Brassel <nick@tzarc.org>
Co-authored-by: Nick Brassel <nick@tzarc.org>
Diffstat (limited to 'platforms/chibios')
22 files changed, 1628 insertions, 8 deletions
diff --git a/platforms/chibios/_pin_defs.h b/platforms/chibios/_pin_defs.h index 0d96e2fc3b..414c9e3d11 100644 --- a/platforms/chibios/_pin_defs.h +++ b/platforms/chibios/_pin_defs.h @@ -21,6 +21,11 @@ # include <hal.h> #endif +/* Include the vendor specific pin defs */ +#if __has_include_next("_pin_defs.h") +# include_next "_pin_defs.h" +#endif + #define A0 PAL_LINE(GPIOA, 0) #define A1 PAL_LINE(GPIOA, 1) #define A2 PAL_LINE(GPIOA, 2) diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk new file mode 100644 index 0000000000..911cc5a058 --- /dev/null +++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk @@ -0,0 +1,9 @@ +# List of all the board related files. +BOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c + +# Required include directories +BOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040 + +# Shared variables +ALLCSRC += $(BOARDSRC) +ALLINC += $(BOARDINC) diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h new file mode 100644 index 0000000000..b4363595d0 --- /dev/null +++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h @@ -0,0 +1,12 @@ +// Copyright 2022 Stefan Kerkmann +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include_next "board.h" + +#undef BOARD_RP_PICO_RP2040 +#define BOARD_GENERIC_PROMICRO_RP2040 + +#undef BOARD_NAME +#define BOARD_NAME "Pro Micro RP2040" diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h new file mode 100644 index 0000000000..d53f57edd9 --- /dev/null +++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h @@ -0,0 +1,13 @@ +// Copyright 2022 Stefan Kerkmann +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#define CH_CFG_SMP_MODE TRUE +#define CH_CFG_ST_RESOLUTION 32 +#define CH_CFG_ST_FREQUENCY 1000000 +#define CH_CFG_INTERVALS_SIZE 32 +#define CH_CFG_TIME_TYPES_SIZE 32 +#define CH_CFG_ST_TIMEDELTA 20 + +#include_next <chconf.h> diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h new file mode 100644 index 0000000000..7fe9b654e1 --- /dev/null +++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h @@ -0,0 +1,62 @@ +// Copyright 2022 Stefan Kerkmann +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +/**====================== + ** I2C Driver + *========================**/ + +#if !defined(I2C_DRIVER) +# define I2C_DRIVER I2CD2 +#endif + +#if !defined(I2C1_SDA_PIN) +# define I2C1_SDA_PIN GP2 +#endif + +#if !defined(I2C1_SCL_PIN) +# define I2C1_SCL_PIN GP3 +#endif + +/**====================== + ** SPI Driver + *========================**/ + +#if !defined(SPI_DRIVER) +# define SPI_DRIVER SPID0 +#endif + +#if !defined(SPI_SCK_PIN) +# define SPI_SCK_PIN GP18 +#endif + +#if !defined(SPI_MISO_PIN) +# define SPI_MISO_PIN GP20 +#endif + +#if !defined(SPI_MOSI_PIN) +# define SPI_MOSI_PIN GP19 +#endif + +/**====================== + ** SERIAL Driver + *========================**/ + +#if !defined(SERIAL_USART_DRIVER) +# define SERIAL_USART_DRIVER SIOD0 +#endif + +#if !defined(SERIAL_USART_TX_PIN) && !defined(SOFT_SERIAL_PIN) +# define SERIAL_USART_TX_PIN GP0 +#endif + +#if !defined(SERIAL_USART_RX_PIN) +# define SERIAL_USART_RX_PIN GP1 +#endif + +/**====================== + ** Double-tap + *========================**/ + +#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET diff --git a/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h new file mode 100644 index 0000000000..8348e5312f --- /dev/null +++ b/platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h @@ -0,0 +1,98 @@ +/* + ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef MCUCONF_H +#define MCUCONF_H + +/* + * RP2040_MCUCONF drivers configuration. + * + * IRQ priorities: + * 3...0 Lowest...Highest. + * + * DMA priorities: + * 0...1 Lowest...Highest. + */ + +#define RP2040_MCUCONF + +/* + * HAL driver system settings. + */ +#define RP_NO_INIT FALSE +#define RP_CORE1_START FALSE +#define RP_CORE1_VECTORS_TABLE _vectors +#define RP_CORE1_ENTRY_POINT _crt0_c1_entry +#define RP_CORE1_STACK_END __c1_main_stack_end__ + +/* + * IRQ system settings. + */ +#define RP_IRQ_SYSTICK_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM0_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM1_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM2_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM3_PRIORITY 2 +#define RP_IRQ_UART0_PRIORITY 3 +#define RP_IRQ_UART1_PRIORITY 3 +#define RP_IRQ_SPI0_PRIORITY 2 +#define RP_IRQ_SPI1_PRIORITY 2 +#define RP_IRQ_USB0_PRIORITY 3 +#define RP_IRQ_I2C0_PRIORITY 2 +#define RP_IRQ_I2C1_PRIORITY 2 + +/* + * ADC driver system settings. + */ +#define RP_ADC_USE_ADC1 FALSE + +/* + * SIO driver system settings. + */ +#define RP_SIO_USE_UART0 TRUE +#define RP_SIO_USE_UART1 FALSE + +/* + * SPI driver system settings. + */ +#define RP_SPI_USE_SPI0 TRUE +#define RP_SPI_USE_SPI1 FALSE +#define RP_SPI_SPI0_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI0_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI1_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI1_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI0_DMA_PRIORITY 1 +#define RP_SPI_SPI1_DMA_PRIORITY 1 +#define RP_SPI_DMA_ERROR_HOOK(spip) + +/* + * I2C driver system settings. + */ +#define RP_I2C_USE_I2C0 FALSE +#define RP_I2C_USE_I2C1 TRUE +#define RP_I2C_BUSY_TIMEOUT 50 +#define RP_I2C_ADDRESS_MODE_10BIT FALSE + +/* + * USB driver system settings. + */ +#define RP_USB_USE_USBD0 TRUE +#define RP_USB_FORCE_VBUS_DETECT TRUE +#define RP_USE_EXTERNAL_VBUS_DETECT FALSE +#define RP_USB_USE_SOF_INTR TRUE +#define RP_USB_USE_ERROR_DATA_SEQ_INTR FALSE + +#endif /* MCUCONF_H */ diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk b/platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk new file mode 100644 index 0000000000..911cc5a058 --- /dev/null +++ b/platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk @@ -0,0 +1,9 @@ +# List of all the board related files. +BOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c + +# Required include directories +BOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040 + +# Shared variables +ALLCSRC += $(BOARDSRC) +ALLINC += $(BOARDINC) diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h new file mode 100644 index 0000000000..052050c944 --- /dev/null +++ b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h @@ -0,0 +1,12 @@ +// Copyright 2022 Stefan Kerkmann +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include_next "board.h" + +#undef BOARD_RP_PICO_RP2040 +#define BOARD_GENERIC_RP2040 + +#undef BOARD_NAME +#define BOARD_NAME "Generic Raspberry Pi RP2040" diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h new file mode 100644 index 0000000000..d53f57edd9 --- /dev/null +++ b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h @@ -0,0 +1,13 @@ +// Copyright 2022 Stefan Kerkmann +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#define CH_CFG_SMP_MODE TRUE +#define CH_CFG_ST_RESOLUTION 32 +#define CH_CFG_ST_FREQUENCY 1000000 +#define CH_CFG_INTERVALS_SIZE 32 +#define CH_CFG_TIME_TYPES_SIZE 32 +#define CH_CFG_ST_TIMEDELTA 20 + +#include_next <chconf.h> diff --git a/platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h new file mode 100644 index 0000000000..9d8dc61aac --- /dev/null +++ b/platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h @@ -0,0 +1,98 @@ +/* + ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef MCUCONF_H +#define MCUCONF_H + +/* + * RP2040_MCUCONF drivers configuration. + * + * IRQ priorities: + * 3...0 Lowest...Highest. + * + * DMA priorities: + * 0...1 Lowest...Highest. + */ + +#define RP2040_MCUCONF + +/* + * HAL driver system settings. + */ +#define RP_NO_INIT FALSE +#define RP_CORE1_START FALSE +#define RP_CORE1_VECTORS_TABLE _vectors +#define RP_CORE1_ENTRY_POINT _crt0_c1_entry +#define RP_CORE1_STACK_END __c1_main_stack_end__ + +/* + * IRQ system settings. + */ +#define RP_IRQ_SYSTICK_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM0_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM1_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM2_PRIORITY 2 +#define RP_IRQ_TIMER_ALARM3_PRIORITY 2 +#define RP_IRQ_UART0_PRIORITY 3 +#define RP_IRQ_UART1_PRIORITY 3 +#define RP_IRQ_SPI0_PRIORITY 2 +#define RP_IRQ_SPI1_PRIORITY 2 +#define RP_IRQ_USB0_PRIORITY 3 +#define RP_IRQ_I2C0_PRIORITY 2 +#define RP_IRQ_I2C1_PRIORITY 2 + +/* + * ADC driver system settings. + */ +#define RP_ADC_USE_ADC1 FALSE + +/* + * SIO driver system settings. + */ +#define RP_SIO_USE_UART0 FALSE +#define RP_SIO_USE_UART1 FALSE + +/* + * SPI driver system settings. + */ +#define RP_SPI_USE_SPI0 FALSE +#define RP_SPI_USE_SPI1 FALSE +#define RP_SPI_SPI0_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI0_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI1_RX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI1_TX_DMA_CHANNEL RP_DMA_CHANNEL_ID_ANY +#define RP_SPI_SPI0_DMA_PRIORITY 1 +#define RP_SPI_SPI1_DMA_PRIORITY 1 +#define RP_SPI_DMA_ERROR_HOOK(spip) + +/* + * I2C driver system settings. + */ +#define RP_I2C_USE_I2C0 FALSE +#define RP_I2C_USE_I2C1 FALSE +#define RP_I2C_BUSY_TIMEOUT 50 +#define RP_I2C_ADDRESS_MODE_10BIT FALSE + +/* + * USB driver system settings. + */ +#define RP_USB_USE_USBD0 TRUE +#define RP_USB_FORCE_VBUS_DETECT TRUE +#define RP_USE_EXTERNAL_VBUS_DETECT FALSE +#define RP_USB_USE_SOF_INTR TRUE +#define RP_USB_USE_ERROR_DATA_SEQ_INTR FALSE + +#endif /* MCUCONF_H */ diff --git a/platforms/chibios/bootloaders/rp2040.c b/platforms/chibios/bootloaders/rp2040.c new file mode 100644 index 0000000000..13a54036ef --- /dev/null +++ b/platforms/chibios/bootloaders/rp2040.c @@ -0,0 +1,57 @@ +// Copyright 2022 Stefan Kerkmann +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "hal.h" +#include "bootloader.h" +#include "pico/bootrom.h" + +#if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED) +# define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK 0U +#else +# define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK (1U << RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED) +#endif + +__attribute__((weak)) void mcu_reset(void) { + NVIC_SystemReset(); +} +void bootloader_jump(void) { + reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U); +} + +void enter_bootloader_mode_if_requested(void) {} + +#if defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET) +# if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT) +# define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 200U +# endif + +// Needs to be located in a RAM section that is never initialized on boot to +// preserve its value on reset +static volatile uint32_t __attribute__((section(".ram0.bootloader_magic"))) magic_location; +const uint32_t magic_token = 0xCAFEB0BA; + +// We can not use the __early_init / enter_bootloader_mode_if_requested hook as +// we depend on an already initialized system with usable memory regions and +// populated function pointer tables to the optimized math functions in the +// bootrom. This function is called just prior to main. +void __late_init(void) { + // All clocks have to be enabled before jumping to the bootloader function, + // otherwise the bootrom will be stuck infinitely. + clocks_init(); + + if (magic_location != magic_token) { + magic_location = magic_token; + // ChibiOS is not initialized at this point, so sleeping is only + // possible via busy waiting. The internal timer peripheral is running + // at this point with a precision of 1us. + chSysPolledDelayX(MS2RTC(1 * MHZ, RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT)); + magic_location = 0; + return; + } + + magic_location = 0; + reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U); +} + +#endif diff --git a/platforms/chibios/chibios_config.h b/platforms/chibios/chibios_config.h index a7098f2713..1571bd5cd3 100644 --- a/platforms/chibios/chibios_config.h +++ b/platforms/chibios/chibios_config.h @@ -19,6 +19,27 @@ # define SPLIT_USB_DETECT // Force this on when dedicated pin is not used #endif +#if defined(MCU_RP) +# define CPU_CLOCK RP_CORE_CLK + +# define USE_GPIOV1 +# define PAL_OUTPUT_TYPE_OPENDRAIN _Static_assert(0, "RP2040 has no Open Drain GPIO configuration, setting this is not possible"); + +# define usb_lld_endpoint_fields + +# define I2C1_SCL_PAL_MODE (PAL_MODE_ALTERNATE_I2C | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_PUE | PAL_RP_PAD_DRIVE4) +# define I2C1_SDA_PAL_MODE I2C1_SCL_PAL_MODE + +# define USE_I2CV1_CONTRIB +# if !defined(I2C1_CLOCK_SPEED) +# define I2C1_CLOCK_SPEED 400000 +# endif + +# define SPI_SCK_PAL_MODE (PAL_MODE_ALTERNATE_SPI | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_DRIVE4) +# define SPI_MOSI_PAL_MODE SPI_SCK_PAL_MODE +# define SPI_MISO_PAL_MODE SPI_SCK_PAL_MODE +#endif + // STM32 compatibility #if defined(MCU_STM32) # define CPU_CLOCK STM32_SYSCLK diff --git a/platforms/chibios/drivers/serial_usart.c b/platforms/chibios/drivers/serial_usart.c index f76afb5db4..6581a5b6e9 100644 --- a/platforms/chibios/drivers/serial_usart.c +++ b/platforms/chibios/drivers/serial_usart.c @@ -8,12 +8,12 @@ #if defined(SERIAL_USART_CONFIG) static QMKSerialConfig serial_config = SERIAL_USART_CONFIG; -#else +#elif defined(MCU_STM32) /* STM32 MCUs */ static QMKSerialConfig serial_config = { # if HAL_USE_SERIAL - .speed = (SERIAL_USART_SPEED), /* baudrate - mandatory */ + .speed = (SERIAL_USART_SPEED), # else - .baud = (SERIAL_USART_SPEED), /* baudrate - mandatory */ + .baud = (SERIAL_USART_SPEED), # endif .cr1 = (SERIAL_USART_CR1), .cr2 = (SERIAL_USART_CR2), @@ -23,6 +23,19 @@ static QMKSerialConfig serial_config = { .cr3 = (SERIAL_USART_CR3) # endif }; +#elif defined(MCU_RP) /* Raspberry Pi MCUs */ +/* USART in 8E2 config with RX and TX FIFOs enabled. */ +// clang-format off +static QMKSerialConfig serial_config = { + .baud = (SERIAL_USART_SPEED), + .UARTLCR_H = UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_PEN | UART_UARTLCR_H_STP2 | UART_UARTLCR_H_FEN, + .UARTCR = 0U, + .UARTIFLS = UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E, + .UARTDMACR = 0U +}; +// clang-format on +#else +# error MCU Familiy not supported by default, supply your own serial_config by defining SERIAL_USART_CONFIG in your keyboard files. #endif static QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER; @@ -156,7 +169,7 @@ inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t * @brief Initiate pins for USART peripheral. Half-duplex configuration. */ __attribute__((weak)) void usart_init(void) { -# if defined(MCU_STM32) +# if defined(MCU_STM32) /* STM32 MCUs */ # if defined(USE_GPIOV1) palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN); # else @@ -166,6 +179,8 @@ __attribute__((weak)) void usart_init(void) { # if defined(USART_REMAP) USART_REMAP; # endif +# elif defined(MCU_RP) /* Raspberry Pi MCUs */ +# error Half-duplex with the SIO driver is not supported due to hardware limitations on the RP2040, switch to the PIO driver which has half-duplex support. # else # pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." # endif @@ -177,7 +192,7 @@ __attribute__((weak)) void usart_init(void) { * @brief Initiate pins for USART peripheral. Full-duplex configuration. */ __attribute__((weak)) void usart_init(void) { -# if defined(MCU_STM32) +# if defined(MCU_STM32) /* STM32 MCUs */ # if defined(USE_GPIOV1) palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL); palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT); @@ -189,6 +204,9 @@ __attribute__((weak)) void usart_init(void) { # if defined(USART_REMAP) USART_REMAP; # endif +# elif defined(MCU_RP) /* Raspberry Pi MCUs */ + palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_UART); + palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE_UART); # else # pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." # endif diff --git a/platforms/chibios/drivers/spi_master.c b/platforms/chibios/drivers/spi_master.c index ce69e7f0ac..f9974d9f6b 100644 --- a/platforms/chibios/drivers/spi_master.c +++ b/platforms/chibios/drivers/spi_master.c @@ -20,7 +20,7 @@ static pin_t currentSlavePin = NO_PIN; -#if defined(K20x) || defined(KL2x) +#if defined(K20x) || defined(KL2x) || defined(RP2040) static SPIConfig spiConfig = {NULL, 0, 0, 0}; #else static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0}; @@ -167,7 +167,36 @@ bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) { spiConfig.SPI_CPOL = SPI_CPOL_High; break; } +#elif defined(MCU_RP) + if (lsbFirst) { + osalDbgAssert(lsbFirst == false, "RP2040s PrimeCell SPI implementation does not support sending LSB first."); + } + + // Motorola frame format and 8bit transfer data size. + spiConfig.SSPCR0 = SPI_SSPCR0_FRF_MOTOROLA | SPI_SSPCR0_DSS_8BIT; + // Serial output clock = (ck_sys or ck_peri) / (SSPCPSR->CPSDVSR * (1 + + // SSPCR0->SCR)). SCR is always set to zero, as QMK SPI API expects the + // passed divisor to be the only value to divide the input clock by. + spiConfig.SSPCPSR = roundedDivisor; // Even number from 2 to 254 + switch (mode) { + case 0: + spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low + spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge + break; + case 1: + spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low + spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition + break; + case 2: + spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high + spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge + break; + case 3: + spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high + spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition + break; + } #else spiConfig.cr1 = 0; diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c new file mode 100644 index 0000000000..949fc6dd93 --- /dev/null +++ b/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c @@ -0,0 +1,457 @@ +// Copyright 2022 Stefan Kerkmann +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "quantum.h" +#include "serial_usart.h" +#include "serial_protocol.h" +#include "hardware/pio.h" +#include "hardware/clocks.h" + +#if !defined(MCU_RP) +# error PIO Driver is only available for Raspberry Pi 2040 MCUs! +#endif + +static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout); +static inline bool send_impl(const uint8_t* source, const size_t size); +static inline void pio_serve_interrupt(void); + +#define MSG_PIO_ERROR ((msg_t)(-3)) + +#if defined(SERIAL_PIO_USE_PIO1) +static const PIO pio = pio1; + +OSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) { + OSAL_IRQ_PROLOGUE(); + pio_serve_interrupt(); + OSAL_IRQ_EPILOGUE(); +} +#else +static const PIO pio = pio0; + +OSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) { + OSAL_IRQ_PROLOGUE(); + pio_serve_interrupt(); + OSAL_IRQ_EPILOGUE(); +} +#endif + +#define UART_TX_WRAP_TARGET 0 +#define UART_TX_WRAP 3 + +// clang-format off +#if defined(SERIAL_USART_FULL_DUPLEX) +static const uint16_t uart_tx_program_instructions[] = { + // .wrap_target + 0x9fa0, // 0: pull block side 1 [7] + 0xf727, // 1: set x, 7 side 0 [7] + 0x6001, // 2: out pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + // .wrap +}; +#else +static const uint16_t uart_tx_program_instructions[] = { + // .wrap_target + 0x9fa0, // 0: pull block side 1 [7] + 0xf727, // 1: set x, 7 side 0 [7] + 0x6081, // 2: out pindirs, 1 + 0x0642, // 3: jmp x--, 2 [6] + // .wrap +}; +#endif +// clang-format on + +static const pio_program_t uart_tx_program = { + .instructions = uart_tx_program_instructions, + .length = 4, + .origin = -1, +}; + +#define UART_RX_WRAP_TARGET 0 +#define UART_RX_WRAP 8 + +// clang-format off +static const uint16_t uart_rx_program_instructions[] = { + // .wrap_target + 0x2020, // 0: wait 0 pin, 0 + 0xea27, // 1: set x, 7 [10] + 0x4001, // 2: in pins, 1 + 0x0642, // 3: jmp x--, 2 [6] + 0x00c8, // 4: jmp pin, 8 + 0xc020, // 5: irq wait 0 + 0x20a0, // 6: wait 1 pin, 0 + 0x0000, // 7: jmp 0 + 0x8020, // 8: push block + // .wrap +}; +// clang-format on + +static const pio_program_t uart_rx_program = { + .instructions = uart_rx_program_instructions, + .length = 9, + .origin = -1, +}; + +thread_reference_t rx_thread = NULL; +static int rx_state_machine = -1; + +thread_reference_t tx_thread = NULL; +static int tx_state_machine = -1; + +void pio_serve_interrupt(void) { + uint32_t irqs = pio->ints0; + + // The RX FIFO is not empty any more, therefore wake any sleeping rx thread + if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << rx_state_machine)) { + // Disable rx not empty interrupt + pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false); + + osalSysLockFromISR(); + osalThreadResumeI(&rx_thread, MSG_OK); + osalSysUnlockFromISR(); + } + + // The TX FIFO is not full any more, therefore wake any sleeping tx thread + if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << tx_state_machine)) { + // Disable tx not full interrupt + pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false); + osalSysLockFromISR(); + osalThreadResumeI(&tx_thread, MSG_OK); + osalSysUnlockFromISR(); + } + + // IRQ 0 is set on framing or break errors by the rx state machine + if (pio_interrupt_get(pio, 0UL)) { + pio_interrupt_clear(pio, 0UL); + + osalSysLockFromISR(); + osalThreadResumeI(&rx_thread, MSG_PIO_ERROR); + osalSysUnlockFromISR(); + } +} + +#if !defined(SERIAL_USART_FULL_DUPLEX) +// The internal pull-ups of the RP2040 are rather weakish with a range of 50k to +// 80k, which in turn do not provide enough current to guarantee fast signal rise +// times with a parasitic capacitance of greater than 100pf. In real world +// applications, like split keyboards which might have vias in the signal path +// or long PCB traces, this prevents a successful communication. The solution +// is to temporarily augment the weak pull ups from the receiving side by +// driving the tx pin high. On the receiving side the lowest possible drive +// strength is chosen because the transmitting side must still be able to drive +// the signal low. With this configuration the rise times are fast enough and +// the generated low level with 360mV will generate a logical zero. +static inline void enter_rx_state(void) { + osalSysLock(); + // Wait for the transmitting state machines FIFO to run empty. At this point + // the last byte has been pulled from the transmitting state machines FIFO + // into the output shift register. We have to wait a tiny bit more until + // this byte is transmitted, before we can turn on the receiving state + // machine again. + while (!pio_sm_is_tx_fifo_empty(pio, tx_state_machine)) { + } + // Wait for ~11 bits, 1 start bit + 8 data bits + 1 stop bit + 1 bit + // headroom. + chSysPolledDelayX(US2RTC(1 * MHZ, (1000000U * 11 / SERIAL_USART_SPEED))); + // Disable tx state machine to not interfere with our tx pin manipulation + pio_sm_set_enabled(pio, tx_state_machine, false); + gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_2MA); + pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << SERIAL_USART_TX_PIN, 1U << SERIAL_USART_TX_PIN); + pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, false); + pio_sm_set_enabled(pio, rx_state_machine, true); + osalSysUnlock(); +} + +static inline void leave_rx_state(void) { + osalSysLock(); + // In Half-duplex operation the tx pin dual-functions as sender and + // receiver. To not receive the data we will send, we disable the receiving + // state machine. + pio_sm_set_enabled(pio, rx_state_machine, false); + pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, true); + pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U, 1U << SERIAL_USART_TX_PIN); + gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA); + pio_sm_restart(pio, tx_state_machine); + pio_sm_set_enabled(pio, tx_state_machine, true); + osalSysUnlock(); +} +#else +// All this trickery is gladly not necessary for full-duplex. +static inline void enter_rx_state(void) {} +static inline void leave_rx_state(void) {} +#endif + +/** + * @brief Clear the RX and TX hardware FIFOs of the state machines. + */ +inline void serial_transport_driver_clear(void) { + osalSysLock(); + pio_sm_clear_fifos(pio, rx_state_machine); + pio_sm_clear_fifos(pio, tx_state_machine); + osalSysUnlock(); +} + +static inline msg_t sync_tx(sysinterval_t timeout) { + msg_t msg = MSG_OK; + osalSysLock(); + while (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) { + pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true); + msg = osalThread |