Skip to content

Commit

Permalink
cpu/rpx0xx: add support for the RP2040 MCU
Browse files Browse the repository at this point in the history
Co-authored-by: Fabian Hüßler <fabian.huessler@st.ovgu.de>
  • Loading branch information
maribu and fabian18 committed Jul 14, 2021
1 parent c52da57 commit ea56dfc
Show file tree
Hide file tree
Showing 48 changed files with 35,689 additions and 0 deletions.
33 changes: 33 additions & 0 deletions cpu/rpx0xx/Kconfig
@@ -0,0 +1,33 @@
# Copyright (C) 2021 Otto-von-Guericke-Universität Magdeburg
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
#

config CPU_FAM_RPX0XX
bool
select CPU_CORE_CORTEX_M0PLUS
select HAS_CPU_RPX0XX
select HAS_PERIPH_GPIO
select HAS_PERIPH_GPIO_IRQ

config CPU_FAM
default "RPX0XX" if CPU_FAM_RPX0XX

config CPU_MODEL_RP2040
bool
select CPU_FAM_RPX0XX

config CPU_MODEL
default "RP2040" if CPU_MODEL_RP2040

config CPU
default "rpx0xx" if CPU_FAM_RPX0XX

config HAS_CPU_RPX0XX
bool
help
Indicates that a RPX0XX CPU (e.g. the RP2040) is used

source "$(RIOTCPU)/cortexm_common/Kconfig"
5 changes: 5 additions & 0 deletions cpu/rpx0xx/Makefile
@@ -0,0 +1,5 @@
MODULE = cpu

DIRS = $(RIOTCPU)/cortexm_common periph

include $(RIOTBASE)/Makefile.base
1 change: 1 addition & 0 deletions cpu/rpx0xx/Makefile.default
@@ -0,0 +1 @@
DEFAULT_MODULE += stdio_null
1 change: 1 addition & 0 deletions cpu/rpx0xx/Makefile.dep
@@ -0,0 +1 @@
include $(RIOTCPU)/cortexm_common/Makefile.dep
7 changes: 7 additions & 0 deletions cpu/rpx0xx/Makefile.features
@@ -0,0 +1,7 @@
CPU_CORE := cortex-m0plus
CPU_FAM := RPX0XX

include $(RIOTCPU)/cortexm_common/Makefile.features

FEATURES_PROVIDED += periph_gpio
FEATURES_PROVIDED += periph_gpio_irq
21 changes: 21 additions & 0 deletions cpu/rpx0xx/Makefile.include
@@ -0,0 +1,21 @@
PICO_DEBUG ?=
ROM_LEN ?= 2097152 # = 2 MiB used in the RPi Pico
ROM_OFFSET := 256 # bootloader size
RAM_LEN := 270336 # = 264 KiB
ROM_START_ADDR := 0x10000000
RAM_START_ADDR := 0x20000000

CFLAGS += -D$(CPU_MODEL)
CFLAGS += -DROM_START_ADDR=$(ROM_START_ADDR)
CFLAGS += -DRAM_START_ADDR=$(RAM_START_ADDR)

INCLUDES += -I$(RIOTCPU)/rpx0xx/include

VECTORS_O ?= $(BINDIR)/cpu/vectors.o
VECTORS_FILE := $(RIOTCPU)/rpx0xx/vectors.c

PROGRAMMERS_SUPPORTED := uf2conv openocd jlink
PROGRAMMER ?= openocd
OPENOCD_DEBUG_ADAPTER ?= dap

include $(RIOTMAKE)/arch/cortexm.inc.mk
23 changes: 23 additions & 0 deletions cpu/rpx0xx/boot2_w25q080_padded_checksummed.S
@@ -0,0 +1,23 @@
// Padded and checksummed version of: boot2_w25q080.bin

.cpu cortex-m0plus
.thumb

.section .boot2, "ax"

.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60
.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61
.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20
.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0
.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0
.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21
.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60
.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21
.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60
.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49
.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20
.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66
.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40
.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00
.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a
179 changes: 179 additions & 0 deletions cpu/rpx0xx/clock.c
@@ -0,0 +1,179 @@
/*
* Copyright (C) 2021 Otto-von-Guericke Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup cpu_rpx0xx
* @{
*
* @file
* @brief Implementation of the CPU clock configuration
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>

#include "vendor/RP2040.h"
#include "vendor/system_RP2040.h"
#include "io_reg.h"
#include "periph_cpu.h"

static void _clk_sys_set_source(CLOCKS_CLK_SYS_CTRL_SRC_Enum source)
{
io_reg_write_dont_corrupt(&CLOCKS->CLK_SYS_CTRL.reg, source << CLOCKS_CLK_SYS_CTRL_SRC_Pos,
CLOCKS_CLK_SYS_CTRL_SRC_Msk);
}

static void _clk_sys_set_aux_source(CLOCKS_CLK_SYS_CTRL_AUXSRC_Enum source)
{
io_reg_write_dont_corrupt(&CLOCKS->CLK_SYS_CTRL.reg, source << CLOCKS_CLK_SYS_CTRL_AUXSRC_Pos,
CLOCKS_CLK_SYS_CTRL_AUXSRC_Msk);
}

static void _clk_ref_set_source(CLOCKS_CLK_REF_CTRL_SRC_Enum source)
{
io_reg_write_dont_corrupt(&CLOCKS->CLK_REF_CTRL.reg, source << CLOCKS_CLK_REF_CTRL_SRC_Pos,
CLOCKS_CLK_REF_CTRL_SRC_Msk);
}

static void _clk_ref_set_aux_source(CLOCKS_CLK_REF_CTRL_AUXSRC_Enum source)
{
io_reg_write_dont_corrupt(&CLOCKS->CLK_REF_CTRL.reg, source << CLOCKS_CLK_REF_CTRL_AUXSRC_Pos,
CLOCKS_CLK_REF_CTRL_AUXSRC_Msk);
}

static void _gpout_set_aux_source(volatile uint32_t *reg, uint32_t value)
{
io_reg_write_dont_corrupt(reg,
value << CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_Pos,
CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_Msk);
}

void clock_sys_configure_source(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_SYS_CTRL_SRC_Enum source)
{
assert(f_out <= f_in);
assert(source != CLOCKS_CLK_SYS_CTRL_SRC_clksrc_clk_sys_aux);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_SYS_DIV_INT_Pos) / f_out;
/* switch the glitchless mux to clk_ref */
_clk_sys_set_source(source);
/* apply divider */
CLOCKS->CLK_SYS_DIV.reg = div;
/* poll SELECTED until the switch is completed */
while (!(CLOCKS->CLK_SYS_SELECTED & (1U << source))) { }
}

void clock_sys_configure_aux_source(uint32_t f_in, uint32_t f_out,
CLOCKS_CLK_SYS_CTRL_AUXSRC_Enum aux)
{
assert(f_out <= f_in);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_SYS_DIV_INT_Pos) / f_out;
/* switch the glitchless mux to a source that is not the aux mux */
_clk_sys_set_source(CLOCKS_CLK_SYS_CTRL_SRC_clk_ref);
/* poll SELECTED until the switch is completed */
while (!(CLOCKS->CLK_SYS_SELECTED & (1U << CLOCKS_CLK_SYS_CTRL_SRC_clk_ref))) { }
/* change the auxiliary mux */
_clk_sys_set_aux_source(aux);
/* apply divider */
CLOCKS->CLK_SYS_DIV.reg = div;
/* switch the glitchless mux to clk_sys_aux */
_clk_sys_set_source(CLOCKS_CLK_SYS_CTRL_SRC_clksrc_clk_sys_aux);
/* poll SELECTED until the switch is completed */
while (!(CLOCKS->CLK_SYS_SELECTED & (1U << CLOCKS_CLK_SYS_CTRL_SRC_clksrc_clk_sys_aux))) { }
}

void clock_ref_configure_source(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_REF_CTRL_SRC_Enum source)
{
assert(f_out <= f_in);
assert(source != CLOCKS_CLK_REF_CTRL_SRC_clksrc_clk_ref_aux);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_REF_DIV_INT_Pos) / f_out;
/* switch the glitchless mux to clock source */
_clk_ref_set_source(source);
/* apply divider */
CLOCKS->CLK_REF_DIV.reg = div & CLOCKS_CLK_REF_DIV_INT_Msk;
/* poll SELECTED until the switch is completed */
while (!(CLOCKS->CLK_REF_SELECTED & (1U << source))) { }
}

void clock_ref_configure_aux_source(uint32_t f_in, uint32_t f_out,
CLOCKS_CLK_REF_CTRL_AUXSRC_Enum aux)
{
assert(f_out <= f_in);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_REF_DIV_INT_Pos) / f_out;
/* switch the glitchless mux to a source that is not the aux mux */
_clk_ref_set_source(CLOCKS_CLK_REF_CTRL_SRC_rosc_clksrc_ph);
/* poll SELECTED until the switch is completed */
while (!(CLOCKS->CLK_REF_SELECTED & (1U << CLOCKS_CLK_REF_CTRL_SRC_rosc_clksrc_ph))) { }
/* change the auxiliary mux */
_clk_ref_set_aux_source(aux);
/* apply divider */
CLOCKS->CLK_REF_DIV.reg = div & CLOCKS_CLK_REF_DIV_INT_Msk;
/* switch the glitchless mux to clk_ref_aux */
_clk_ref_set_source(CLOCKS_CLK_REF_CTRL_SRC_clksrc_clk_ref_aux);
/* poll SELECTED until the switch is completed */
while (!(CLOCKS->CLK_REF_SELECTED & (1U << CLOCKS_CLK_REF_CTRL_SRC_clksrc_clk_ref_aux))) { }
}

void clock_periph_configure(CLOCKS_CLK_PERI_CTRL_AUXSRC_Enum aux)
{
io_reg_atomic_clear(&CLOCKS->CLK_PERI_CTRL.reg, (1u << CLOCKS_CLK_PERI_CTRL_ENABLE_Pos));
io_reg_write_dont_corrupt(&CLOCKS->CLK_PERI_CTRL.reg, aux << CLOCKS_CLK_PERI_CTRL_AUXSRC_Pos,
CLOCKS_CLK_PERI_CTRL_AUXSRC_Msk);
io_reg_atomic_set(&CLOCKS->CLK_PERI_CTRL.reg, (1u << CLOCKS_CLK_PERI_CTRL_ENABLE_Pos));
}

void clock_gpout0_configure(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_Enum aux)
{
assert(f_out <= f_in);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_REF_DIV_INT_Pos) / f_out;
io_reg_atomic_clear(&CLOCKS->CLK_GPOUT0_CTRL.reg, 1U << CLOCKS_CLK_GPOUT0_CTRL_ENABLE_Pos);
_gpout_set_aux_source(&CLOCKS->CLK_GPOUT0_CTRL.reg, aux);
CLOCKS->CLK_GPOUT0_DIV.reg = div;
io_reg_atomic_set(&CLOCKS->CLK_GPOUT0_CTRL.reg, 1U << CLOCKS_CLK_GPOUT0_CTRL_ENABLE_Pos);
io_reg_atomic_set(&PADS_BANK0->GPIO21.reg, 1U << PADS_BANK0_GPIO21_IE_Pos);
gpio_set_function_select(21, FUNCTION_SELECT_CLOCK);
}

void clock_gpout1_configure(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_GPOUT1_CTRL_AUXSRC_Enum aux)
{
assert(f_out <= f_in);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_REF_DIV_INT_Pos) / f_out;
io_reg_atomic_clear(&CLOCKS->CLK_GPOUT1_CTRL.reg, 1U << CLOCKS_CLK_GPOUT1_CTRL_ENABLE_Pos);
_gpout_set_aux_source(&CLOCKS->CLK_GPOUT1_CTRL.reg, aux);
CLOCKS->CLK_GPOUT1_DIV.reg = div;
io_reg_atomic_set(&CLOCKS->CLK_GPOUT1_CTRL.reg, 1U << CLOCKS_CLK_GPOUT1_CTRL_ENABLE_Pos);
io_reg_atomic_set(&PADS_BANK0->GPIO23.reg, 1U << PADS_BANK0_GPIO23_IE_Pos);
gpio_set_function_select(23, FUNCTION_SELECT_CLOCK);
}

void clock_gpout2_configure(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_GPOUT2_CTRL_AUXSRC_Enum aux)
{
assert(f_out <= f_in);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_REF_DIV_INT_Pos) / f_out;
io_reg_atomic_clear(&CLOCKS->CLK_GPOUT2_CTRL.reg, 1U << CLOCKS_CLK_GPOUT2_CTRL_ENABLE_Pos);
_gpout_set_aux_source(&CLOCKS->CLK_GPOUT2_CTRL.reg, aux);
CLOCKS->CLK_GPOUT2_DIV.reg = div;
io_reg_atomic_set(&CLOCKS->CLK_GPOUT2_CTRL.reg, 1U << CLOCKS_CLK_GPOUT2_CTRL_ENABLE_Pos);
io_reg_atomic_set(&PADS_BANK0->GPIO24.reg, 1U << PADS_BANK0_GPIO24_IE_Pos);
gpio_set_function_select(24, FUNCTION_SELECT_CLOCK);
}

void clock_gpout3_configure(uint32_t f_in, uint32_t f_out, CLOCKS_CLK_GPOUT3_CTRL_AUXSRC_Enum aux)
{
assert(f_out <= f_in);
uint32_t div = (((uint64_t)f_in) << CLOCKS_CLK_REF_DIV_INT_Pos) / f_out;
io_reg_atomic_clear(&CLOCKS->CLK_GPOUT3_CTRL.reg, 1U << CLOCKS_CLK_GPOUT3_CTRL_ENABLE_Pos);
_gpout_set_aux_source(&CLOCKS->CLK_GPOUT3_CTRL.reg, aux);
CLOCKS->CLK_GPOUT3_DIV.reg = div;
io_reg_atomic_set(&CLOCKS->CLK_GPOUT3_CTRL.reg, 1U << CLOCKS_CLK_GPOUT3_CTRL_ENABLE_Pos);
io_reg_atomic_set(&PADS_BANK0->GPIO25.reg, 1U << PADS_BANK0_GPIO25_IE_Pos);
gpio_set_function_select(25, FUNCTION_SELECT_CLOCK);
}
96 changes: 96 additions & 0 deletions cpu/rpx0xx/cpu.c
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2021 Otto-von-Guericke Universität Magdeburg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup cpu_rpx0xx
* @{
*
* @file
* @brief Implementation of the CPU initialization
*
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
*
* @}
*/

#include "cpu.h"
#include "macros/units.h"
#include "periph/init.h"
#include "periph_cpu.h"
#include "io_reg.h"
#include "stdio_base.h"
#include "vendor/RP2040.h"

#define ENABLE_DEBUG 0
#include "debug.h"

static void _cpu_reset(void)
{
/* 2.14 subsystem resets */
uint32_t rst;
/* Reset hardware components except for critical ones */
rst = RESETS_RESET_MASK
& ~(RESETS_RESET_usbctrl_Msk
| RESETS_RESET_syscfg_Msk
| RESETS_RESET_pll_usb_Msk
| RESETS_RESET_pll_sys_Msk
| RESETS_RESET_pads_qspi_Msk
| RESETS_RESET_io_qspi_Msk);
periph_reset(rst);
/* Assert that reset has completed except for those components which
are not clocked by clk_ref or clk_sys */
rst = RESETS_RESET_MASK
& ~(RESETS_RESET_usbctrl_Msk
| RESETS_RESET_uart1_Msk
| RESETS_RESET_uart0_Msk
| RESETS_RESET_spi1_Msk
| RESETS_RESET_spi0_Msk
| RESETS_RESET_rtc_Msk
| RESETS_RESET_adc_Msk);
periph_reset_done(rst);

/* power the reference clock from its default source: the ROSC */
clock_ref_configure_source(MHZ(12), MHZ(12), CLOCKS_CLK_REF_CTRL_SRC_rosc_clksrc_ph);
/* power the system clock from its default source: the reference clock */
clock_sys_configure_source(MHZ(12), MHZ(12), CLOCKS_CLK_SYS_CTRL_SRC_clk_ref);
/* start XOSC, typically running at 12 MHz */
xosc_start(CLOCK_XOSC);
/* reset system PLL */
pll_reset_sys();
/* start the system PLL (typically takes the 12 MHz XOSC to generate 125 MHz) */
pll_start_sys(PLL_SYS_REF_DIV, PLL_SYS_VCO_FEEDBACK_SCALE, PLL_SYS_POSTDIV1, PLL_SYS_POSTDIV2);
/* configure reference clock to run from XOSC (typically 12 MHz) */
clock_ref_configure_source(CLOCK_XOSC, CLOCK_XOSC,
CLOCKS_CLK_REF_CTRL_SRC_xosc_clksrc);
/* configure system clock output */
clock_sys_configure_aux_source(CLOCK_CORECLOCK, CLOCK_CORECLOCK,
CLOCKS_CLK_SYS_CTRL_AUXSRC_clksrc_pll_sys);
/* configure the peripheral clock to run from the system clock */
clock_periph_configure(CLOCK_PERIPH_SOURCE);
if (IS_USED(ENABLE_DEBUG)) {
/* check clk_ref with logic analyzer */
clock_gpout0_configure(CLOCK_XOSC, CLOCK_XOSC,
CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_clk_ref);
}
}

void cpu_init(void)
{
/* initialize the Cortex-M core */
cortexm_init();

_cpu_reset();

/* initialize stdio prior to periph_init() to allow use of DEBUG() there */
stdio_init();

DEBUG_PUTS("[rpx0xx] GPOUT0 (GPIO pin 21) is clocked from XOSC (typically 12 MHz)");

/* trigger static peripheral initialization */
periph_init();
}
9 changes: 9 additions & 0 deletions cpu/rpx0xx/doc.txt
@@ -0,0 +1,9 @@
/**
@defgroup cpu_rpx0xx RPx0xx MCUs
@ingroup cpu
@brief RPx0xx MCU code and definitions

This module contains the code and definitions for MCUs of the RPx0xx family, of which only the
RP2040 is currently released.

*/
1 change: 1 addition & 0 deletions cpu/rpx0xx/include/.gitignore
@@ -0,0 +1 @@
irqs/

0 comments on commit ea56dfc

Please sign in to comment.