diff --git a/cpu/qn908x/Kconfig b/cpu/qn908x/Kconfig new file mode 100644 index 0000000000..9fbf36d8cb --- /dev/null +++ b/cpu/qn908x/Kconfig @@ -0,0 +1,51 @@ +# Copyright (c) 2020 iosabi +# +# 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_QN908X + bool + select CPU_CORE_CORTEX_M4F + select HAS_CORTEXM_MPU + select HAS_CPU_QN908X + select HAS_PERIPH_CPUID + select HAS_PERIPH_GPIO + select HAS_PERIPH_GPIO_IRQ + select HAS_PERIPH_WDT + select HAS_PERIPH_WDT_CB + +## CPU Models +# For cpus QN9080CHN (revision C) and QN9080DHN (revision D) +config CPU_MODEL_QN9080XHN + bool + select CPU_FAM_QN908X + +# For the smaller package for the same die, with 28 GPIOs instead of 35. +# cpus QN9083CUK (revision C) and QN9083DUK (revision D) +config CPU_MODEL_QN9083XUK + bool + select CPU_FAM_QN908X + +## CPU common symbols +config CPU_FAM + default "qn908x" if CPU_FAM_QN908X + +config CPU_MODEL + default "qn9080xhn" if CPU_MODEL_QN9080XHN + default "qn9083xhk" if CPU_MODEL_QN9083XUK + +config CPU + default "qn908x" if CPU_FAM_QN908X + +## Definition of specific features +config HAS_CPU_QN908X + bool + help + Indicates that the current cpu is 'qn908x'. + +# Other cpu configuration +rsource "Kconfig.clk" + +source "$(RIOTCPU)/cortexm_common/Kconfig" diff --git a/cpu/qn908x/Kconfig.clk b/cpu/qn908x/Kconfig.clk new file mode 100644 index 0000000000..5ddc813dd9 --- /dev/null +++ b/cpu/qn908x/Kconfig.clk @@ -0,0 +1,120 @@ +# Copyright (c) 2020 iosabi +# +# 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. +# + +menu "QN908x clock configuration" + depends on CPU_FAM_QN908X + +config BOARD_HAS_XTAL32K + bool + help + Indicates that the board has an external low frequency 32.786 KHz + crystal oscillator connected to the XTAL32_IN / XTAL32_OUT pins. + This should only be set from board definition. + +choice + prompt "32K low frequency clock selector" + default CPU_CLK_32K_XTAL if BOARD_HAS_XTAL32K + default CPU_CLK_32K_RCO + help + The "32K" clock bus runs at either 32 KHz from the internal RCO or + 32.768 KHz from an external crystal oscillator. This clock can be used + to drive the "System clock" for a very low power operation, but it can + independently also be used for the watchdog timer (WDT) and other low + frequency system timers like a real time clock. + +config CPU_CLK_32K_XTAL + bool "External 32.768 KHz crystal" + depends on BOARD_HAS_XTAL32K + +config CPU_CLK_32K_RCO + bool "Internal 32 KHz oscillator" + +endchoice + +config BOARD_HAS_XTAL + bool + help + Indicates that the board has an external high frequency crystal + oscillator connected to the XTAL_IN / XTAL_OUT pins. + This should only be set from board definition. + +config BOARD_HAS_XTAL_16M + bool + imply BOARD_HAS_XTAL + depends on !BOARD_HAS_XTAL_32M + help + Indicates that the external high frequency crystal oscillator is a + 16 MHz crystal. This should only be set from board definition. + +config BOARD_HAS_XTAL_32M + bool + imply BOARD_HAS_XTAL + help + Indicates that the external high frequency crystal oscillator is a + 32 MHz crystal. This should only be set from board definition. + +config CPU_CLK_OSC32M_DIV + bool "Internal OSC32M clock input /2 divider" + help + Selecting this option will set the high-speed internal oscillator + divider to /2, making it a 16 MHz clock source. See "System clock + configuration selector" for selecting this source. + +config CPU_CLK_XTAL_DIV + bool "External XTAL 32 MHz clock input /2 divider" + depends on BOARD_HAS_XTAL_32M + help + Selecting this option will set the high-speed external crystal + oscillator divider to /2. This option is only available when the + external oscillator is a 32 MHz one. See "System clock + configuration selector" for selecting this source. + +choice + prompt "System clock configuration selector" + default CPU_CLK_SYS_XTAL if BOARD_HAS_XTAL + default CPU_CLK_SYS_OSC32M + help + The System clock is used to derive the AHB clock, which drives the ARM + core and most peripherals. + +config CPU_CLK_SYS_XTAL + bool "External 16/32 MHz crystal source (with optional divider)" + depends on BOARD_HAS_XTAL + +config CPU_CLK_SYS_OSC32M + bool "Internal 32 MHz oscillator source (with optional divider)" + +config CPU_CLK_SYS_32K + bool "Low frequency clock source (32 or 32.768 KHz)" + +endchoice + +config CPU_CLK_AHB_DIV + int "AHB clock divider" + default 1 + range 1 8192 + help + The AHB clock is derived from the System clock using this divider value, + between 1 and 8192, and serves as a clock source for ARM core, FSP, SCT, + Quad-SPI, Flexcomm (UART, SPI, I2C), GPIO, BLE_AHB and DMA. + Note: When BLE is enabled, the AHB clock must be at least the BLE clock + (either 8 or 16 MHz) limiting the range of allowed values for this + divider so that the AHB clock is 8, 16 or 32 MHz. + +config CPU_CLK_APB_DIV + int "APB clock divider" + default 1 + range 1 16 + help + The APB clock is derived from the AHB clock using this divide value, + between 1 and 16, and serves as the clock source for several + peripherals, such as the RTC, ADC, DAC, Capacitive Sense (CS) and + optionally the WDT. + +# TODO: Add USB PLL and BLE clock selectors. + +endmenu diff --git a/cpu/qn908x/Makefile b/cpu/qn908x/Makefile new file mode 100644 index 0000000000..317c647187 --- /dev/null +++ b/cpu/qn908x/Makefile @@ -0,0 +1,10 @@ +# define the module that is build +MODULE = cpu + +# add a list of subdirectories that should also be built +DIRS = periph $(RIOTCPU)/cortexm_common vendor + +# (file triggers compiler bug. see #5775) +SRC_NOLTO += vectors.c + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/qn908x/Makefile.dep b/cpu/qn908x/Makefile.dep new file mode 100644 index 0000000000..eb318d253f --- /dev/null +++ b/cpu/qn908x/Makefile.dep @@ -0,0 +1,18 @@ +# In some cases, peripheral modules use vendor provided driver modules such as +# the vendor_fsl_clock. This file defines the dependencies between these periph +# modules and the vendor modules. +USEMODULE += vendor + +# The clock functionality is used by most modules, including cpu.c even when +# no peripheral module is being used. +USEMODULE += vendor_fsl_clock + +# All peripherals use gpio_mux.h +USEMODULE += periph_gpio_mux + +# This cpu modules doesn't support UART peripherals yet, so we need to include +# stdio_null. +# TODO: Remove stdio_null once periph_uart is implemented in this module. +USEMODULE += stdio_null + +include $(RIOTCPU)/cortexm_common/Makefile.dep diff --git a/cpu/qn908x/Makefile.features b/cpu/qn908x/Makefile.features new file mode 100644 index 0000000000..9bc70e8433 --- /dev/null +++ b/cpu/qn908x/Makefile.features @@ -0,0 +1,9 @@ +CPU_CORE = cortex-m4f +CPU_FAM = qn908x + +FEATURES_PROVIDED += cortexm_mpu +FEATURES_PROVIDED += periph_cpuid +FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += periph_wdt periph_wdt_cb + +include $(RIOTCPU)/cortexm_common/Makefile.features diff --git a/cpu/qn908x/Makefile.include b/cpu/qn908x/Makefile.include new file mode 100644 index 0000000000..07deb3c1a0 --- /dev/null +++ b/cpu/qn908x/Makefile.include @@ -0,0 +1,39 @@ +# Add search path for linker scripts +LINKFLAGS += -L$(RIOTCPU)/$(CPU)/ldscripts +LINKER_SCRIPT = qn908x.ld + +# Internal FLASH memory is located at address 0x0100000, aliased to address +# 0x2100000 and can also be aliased to address 0, which is done by the +# pre_startup() function in cpu/qn908x/isr_qn908x.c. The address 0 can be also +# be remapped to RAM instead, and the FLASH can be turned completely off to save +# power, thus linking all the code based on address 0 could make it easier in +# the future to provide a low-power mode where portions of the code execute +# from RAM only during this low-power mode. However, linking all the code at +# address 0 makes it more difficult to attach gdb after a 'reset halt' but +# before the FLASH is mapped to 0 by pre_startup() since it can't place a +# breakpoint at any function in the FLASH alias at 0 until it is mapped. +# This default value of 0x01000000 makes it possible to place breakpoints across +# reboots, but it can be override from the board if needed. When setting +# ROM_START_ADDR to 0 the IMAGE_OFFSET must be set to 0x01000000 to allow +# flashing at the right location. +ROM_START_ADDR ?= 0x01000000 +# SRAM is actually at 0x04000000 but it is also aliased to 0x20000000. +RAM_BASE_ADDR = 0x20000000 +RAM_START_ADDR = $(RAM_BASE_ADDR) + +# The only QN908x chips available have 512K flash, although it seems possible to +# have 256K versions. +ROM_LEN ?= 512K +RAM_LEN ?= 128K + +CFLAGS += \ + -DQN908X_ROM_START_ADDR=$(ROM_START_ADDR) + # + +# Vendor submodules are all bundled in the vendor module, and they include +# some files from the include/vendor directory directly so we need to add that +# include path here. +PSEUDOMODULES += vendor_% +INCLUDES += -I$(RIOTCPU)/$(CPU)/include/vendor + +include $(RIOTMAKE)/arch/cortexm.inc.mk diff --git a/cpu/qn908x/cpu.c b/cpu/qn908x/cpu.c new file mode 100644 index 0000000000..587753e338 --- /dev/null +++ b/cpu/qn908x/cpu.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * @brief QN908x CPU initialization + * + * @author iosabi + * @} + */ + +#include "cpu.h" +#include "periph/init.h" + +#include "stdio_base.h" + +#include "vendor/drivers/fsl_clock.h" + +static void cpu_clock_init(void); + +/** + * @brief Initialize the CPU + */ +void cpu_init(void) +{ + /* initialize the Cortex-M core */ + cortexm_init(); +#ifndef MODULE_PERIPH_WDT + /* If the `periph_wdt` is *not* being used (because the user does not care + * about that feature) we need to disable the Watchdog and continue running + * without it. Otherwise the CPU will reboot after about 10 seconds. + */ + CLOCK_DisableClock(kCLOCK_Wdt); +#endif /* ndef MODULE_PERIPH_WDT */ + + /* TODO: It would be good to move the VTOR to SRAM to allow execution from + * RAM with the FLASH memory off to allow for ultra low power operation on + * sleep mode. This needs to be done after cortexm_init() since it sets the + * VTOR to _isr_vectors which is the address on FLASH. + */ + + /* initialize the clocks */ + cpu_clock_init(); + + /* initialize stdio prior to periph_init() to allow use of DEBUG() there */ + stdio_init(); + /* trigger static peripheral initialization */ + periph_init(); +} + +/* Set up clock speed configuration. See cpu_conf.h for details about the + * different clock options. */ +void cpu_clock_init(void) +{ + /* Set up external clock frequency. */ +#if CONFIG_BOARD_HAS_XTAL +#if CONFIG_BOARD_HAS_XTAL_32M + CLOCK_AttachClk(k32M_to_XTAL_CLK); /* Switch XTAL_CLK to 32M */ +#elif CONFIG_BOARD_HAS_XTAL_16M + CLOCK_AttachClk(k16M_to_XTAL_CLK); /* Switch XTAL_CLK to 16M */ +#else +#error "One of the CONFIG_BOARD_XTAL_* must be set." +#endif +#endif /* CONFIG_BOARD_HAS_XTAL */ + + + /* Set up 32K clock source. */ +#if CONFIG_CPU_CLK_32K_XTAL + CLOCK_AttachClk(kXTAL32K_to_32K_CLK); /* Switch 32K_CLK to XTAL32K */ +#elif CONFIG_CPU_CLK_32K_RCO + CLOCK_AttachClk(kRCO32K_to_32K_CLK); /* Switch 32K_CLK to RCO32K */ +#else +#error "One of the CONFIG_CPU_CLK_32K_* must be set." +#endif + + /* Set up System clock source. */ +#if CONFIG_CPU_CLK_SYS_XTAL + CLOCK_AttachClk(kXTAL_to_SYS_CLK); /* Switch SYS_CLK to XTAL */ +#elif CONFIG_CPU_CLK_SYS_OSC32M + CLOCK_AttachClk(kOSC32M_to_SYS_CLK); /* Switch SYS_CLK to OSM32M */ +#elif CONFIG_CPU_CLK_SYS_32K + CLOCK_AttachClk(k32K_to_SYS_CLK); /* Switch SYS_CLK to 32K source */ +#else +#error "One of the CONFIG_CPU_CLK_SYS_* must be set." +#endif + + /* Run the WDT from the APB always. */ + CLOCK_AttachClk(kAPB_to_WDT_CLK); + + /* Set up dividers */ + + /* Set OSC32M_DIV divider */ +#if CONFIG_CPU_CLK_OSC32M_DIV != 0 && CONFIG_CPU_CLK_OSC32M_DIV != 1 +#error "Invalid CONFIG_CPU_CLK_OSC32M_DIV value" +#endif + /* Note: The denominator is set to (CONFIG_CPU_CLK_OSC32M_DIV + 1), so /2 + * when the macro is enabled. */ + CLOCK_SetClkDiv(kCLOCK_DivOsc32mClk, CONFIG_CPU_CLK_OSC32M_DIV); + + /* Set XTAL_DIV divider */ +#if CONFIG_CPU_CLK_XTAL_DIV != 0 && CONFIG_CPU_CLK_XTAL_DIV != 1 +#error "Invalid CONFIG_CPU_CLK_XTAL_DIV value" +#endif + CLOCK_SetClkDiv(kCLOCK_DivXtalClk, CONFIG_CPU_CLK_XTAL_DIV); + + /* Set AHB_DIV divider. */ +#if CONFIG_CPU_CLK_AHB_DIV < 1 || CONFIG_CPU_CLK_AHB_DIV > 8192 +#error "Invalid CONFIG_CPU_CLK_AHB_DIV" +#endif + CLOCK_SetClkDiv(kCLOCK_DivAhbClk, CONFIG_CPU_CLK_AHB_DIV - 1u); + + /* Set APB_DIV divider. */ +#if CONFIG_CPU_CLK_APB_DIV < 1 || CONFIG_CPU_CLK_APB_DIV > 16 +#error "Invalid CONFIG_CPU_CLK_APB_DIV" +#endif + CLOCK_SetClkDiv(kCLOCK_DivApbClk, CONFIG_CPU_CLK_APB_DIV - 1u); +} diff --git a/cpu/qn908x/doc.txt b/cpu/qn908x/doc.txt new file mode 100644 index 0000000000..9035815773 --- /dev/null +++ b/cpu/qn908x/doc.txt @@ -0,0 +1,68 @@ +/* NXP QN908x specific information for the `periph` drivers */ +/** + +@defgroup cpu_qn908x NXP QN908x +@ingroup cpu +@brief NXP QN908x BLE-enabled Cortex-M4F MCU specific implementation + +The NXP QN908x family of chips such as the QN9080 feature a Cortex-M4F, +Bluetooth Low Energy, USB 2.0 and in some SKUs like the QN9080SIP NFC as well. +The CPU is designed to be ultra-low-power and high-performance, allowing +applications with small battery capacity. It includes an optional DC-DC and LDO, +low power sleep timers, I2C, SPI, ADC, SPIFI and several other peripherals. + + +@defgroup cpu_qn908x_cpuid NXP QN908x CPUID +@ingroup cpu_qn908x +@brief NXP QN908x CPUID driver + +No configuration is necessary. The CPUID value is based on the factory assigned +default Bluetooth address in the read-only flash section which may not be the +Bluetooth address used by the Bluetooth module if a different one was programmed +there. + + +@defgroup cpu_qn908x_gpio NXP QN908x GPIO +@ingroup cpu_qn908x +@brief NXP QN908x GPIO driver + +The GPIO driver uses the @ref GPIO_PIN(port, pin) macro to declare pins. + +No configuration is necessary. + + +@defgroup cpu_qn908x_wdt NXP QN908x Watchdog timer (WDT) +@ingroup cpu_qn908x +@brief NXP QN908x Watchdog timer (WDT) + +The Watchdog timer in the NXP QN908x starts disabled on reset: the clock bit +`CLK_WDT_EN` is enabled in the `CLK_EN` register on reset so the timer is +running but the interrupt and reset functions are disabled. However, after the +read-only bootloader ROM in the QN908x transfer the control flow to the user +application (the RIOT kernel) the Watchdog is enabled with a timeout of 10 +seconds. + +If your board does not include the `periph_wdt` module, the Watchdog will be +disabled at `cpu_init()` time and there's no configuration necessary. However, +if your board or application does include it, the Watchdog will be left +configured with the 10 second timeout set by the Bootloader and you need to +call `wdt_setup_reboot()` or `wdt_setup_reboot_with_callback()` within the first +10 seconds. + +The WDT block supports different clock sources which would be configured by the +board since they depend on whether the optional crystals are populated in your +board. Nevertheless, the millisecond values passed to `wdt_setup_reboot*` are +internally converted to clock ticks using the clock configured at the time the +function was called. `wdt_setup_reboot*()` can be called multiple times to +change the WDT parameters or after changing the WDT clock source, but in any +case `wdt_start()` must be called after it to start the WDT operation. + +Once the WDT triggers, it is not possible to avoid the device reboot and calling +wdt_kick() from the WDT callback (if any) or after the callback was called will +not have any effect. Note that, however, if the WDT callback returns before the +configured CONFIG_WDT_WARNING_PERIOD the CPU will continue executing the code +before the WDT interrupt occurred. If this is not desired, an infinite loop at +the end of the WDT callback, after the safety operations have been performed is +advisable. + +*/ diff --git a/cpu/qn908x/include/cpu_conf.h b/cpu/qn908x/include/cpu_conf.h new file mode 100644 index 0000000000..3cf7d63b5f --- /dev/null +++ b/cpu/qn908x/include/cpu_conf.h @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * @brief Implementation specific CPU configuration options + * + * @author iosabi + */ + +#ifndef CPU_CONF_H +#define CPU_CONF_H + +#include "cpu_conf_common.h" + +#include "vendor/QN908XC.h" +#include "vendor/QN908XC_features.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name ARM Cortex-M specific CPU configuration + * @{ + */ +#define CPU_DEFAULT_IRQ_PRIO (1U) +/** + * NUMBER_OF_INT_VECTORS in the QN908XC.h is defined as including the standard + * ARM interrupt vectors and headers, however CPU_IRQ_NUMOF does not include + * the first 15 interrupt values and the stack pointer. + */ +#define CPU_IRQ_NUMOF (NUMBER_OF_INT_VECTORS - 16) +/** + * The flash is aliased at several addresses in the memory range. In particular, + * address 0 can be mapped to RAM or flash, so it is possible to run from + * address 0 from flash, or even turn off the flash altogether and run from RAM + * to save power. This setting uses the ROM_START_ADDR value set in the + * Makefile. + */ +#define CPU_FLASH_BASE (QN908X_ROM_START_ADDR) +/** + * @brief Bit-Band configuration + */ +#define CPU_HAS_BITBAND 1 +/** @} */ + +/** + * @name Clocks configuration + * @{ + * @brief External and internal clocks configuration. + * + * The QN908x has an internal 32 MHz RCO for the high frequency clock source and + * a 32 KHz RCO for the low frequency clock source, as well as external + * connections for a crystal oscillator (XTAL) of either 16 MHz or 32 MHz for + * the high frequency clock source and another connection for a 32.768 KHz XTAL + * for the low frequency clock normally used for accurate Bluetooth timing. + * Note that the "32 KHz" clock source is not exactly the same frequency whether + * you use the internal or external one. + */ + +/** + * @brief Whether the board has a 32.768 KHz crystal in XTAL32_IN / XTAL32_OUT. + **/ +#if !defined(CONFIG_BOARD_HAS_XTAL32K) || DOXYGEN +#define CONFIG_BOARD_HAS_XTAL32K 0 +#endif + +/** + * @name 32K low frequency clock selector + * @{ + */ +#ifdef DOXYGEN +/** + * @brief Enabled when the 32K low frequency uses the external crystal. + **/ +#define CONFIG_CPU_CLK_32K_XTAL +/** + * @brief Enabled when the 32K low frequency uses the internal oscillator. + **/ +#define CONFIG_CPU_CLK_32K_RCO +#endif /* def DOXYGEN */ +/** @} */ + +/* Default 32K clock selector config. */ +#if !defined(CONFIG_CPU_CLK_32K_XTAL) && !defined(CONFIG_CPU_CLK_32K_RCO) +#if CONFIG_BOARD_HAS_XTAL32K +#define CONFIG_CPU_CLK_32K_XTAL 1 +#else +#define CONFIG_CPU_CLK_32K_RCO 1 +#endif +#endif + +/** + * @brief Whether the board has a 16 or 32 MHz crystal in XTAL_IN / XTAL_OUT. + * @{ + **/ +#ifndef CONFIG_BOARD_HAS_XTAL +#define CONFIG_BOARD_HAS_XTAL 0 +#endif + +/** + * @name External high frequency "XTAL" crystal frequency + */ +#ifdef DOXYGEN +/** + * @brief Enabled when the external XTAL is a 16 MHz one. + **/ +#define CONFIG_CPU_CLK_XTAL_16M +/** + * @brief Enabled when the external XTAL is a 32 MHz one. + **/ +#define CONFIG_CPU_CLK_XTAL_32M +#endif /* def DOXYGEN */ +/** @} */ + +/* Default XTAL setting. */ +#if CONFIG_BOARD_HAS_XTAL && \ + !defined(CONFIG_BOARD_HAS_XTAL_16M) && !defined(CONFIG_BOARD_HAS_XTAL_32M) +#define CONFIG_BOARD_HAS_XTAL_32M 1 +#endif + +/** + * @brief Internal OSC32M clock input /2 divider enabled + **/ +#ifndef CONFIG_CPU_CLK_OSC32M_DIV +#define CONFIG_CPU_CLK_OSC32M_DIV 0 +#endif + +/** + * @brief External XTAL 32 MHz clock input /2 divider enabled + **/ +#ifndef CONFIG_CPU_CLK_XTAL_DIV +#define CONFIG_CPU_CLK_XTAL_DIV 0 +#endif + +/** + * @name System clock configuration selector + * @{ + */ +#ifdef DOXYGEN +/** + * @brief System clock is external crystal source (including divider). + **/ +#define CONFIG_CPU_CLK_SYS_XTAL +/** + * @brief System clock is internal 32 MHz oscillator source (including divider). + **/ +#define CONFIG_CPU_CLK_SYS_OSC32M +/** + * @brief System clock is the low frequency clock (32 or 32.768 KHz) + **/ +#define CONFIG_CPU_CLK_SYS_32K +#endif /* def DOXYGEN */ +/** @} */ + +/* Default system clock configuration selector */ +#if !defined(CONFIG_CPU_CLK_SYS_XTAL) && !defined(CONFIG_CPU_CLK_SYS_OSC32M) && \ + !defined(CONFIG_CPU_CLK_SYS_32K) +#if CONFIG_BOARD_HAS_XTAL +#define CONFIG_CPU_CLK_SYS_XTAL 1 +#else +#define CONFIG_CPU_CLK_SYS_OSC32M 1 +#endif +#endif + +/** + * @brief AHB clock divider + * + * The AHB clock is derived from the System clock using this divider value, + * between 1 and 8192, and serves as a clock source for ARM core, FSP, SCT, + * Quad-SPI, Flexcomm (UART, SPI, I2C), GPIO, BLE_AHB and DMA. + * Note: When BLE is enabled, the AHB clock must be at least the BLE clock + * (either 8 or 16 MHz) limiting the range of allowed values for this + * divider so that the AHB clock is 8, 16 or 32 MHz. + **/ +#ifndef CONFIG_CPU_CLK_AHB_DIV +#define CONFIG_CPU_CLK_AHB_DIV 1u +#endif + +/** + * @brief APB clock divider + * + * The APB clock is derived from the AHB clock using this divide value, + * between 1 and 16, and serves as the clock source for several + * peripherals, such as the RTC, ADC, DAC, Capacitive Sense (CS) and + * optionally the WDT. + **/ +#ifndef CONFIG_CPU_CLK_APB_DIV +#define CONFIG_CPU_CLK_APB_DIV 1u +#endif + +/** @} */ + +/** + * @name Code Read Protection + * @{ + * @brief Image "Code Read Protection" field definitions. + * + * The Code Read Protection (CRP) is a 32-bit field stored in one of the + * reserved fields in the Cortex-M interrupt vector and therefore part of the + * image. It allows to enable or disable access to the flash from the In-System + * Programming (ISP) interface to read, erase or write flash pages, as well as + * external SWD access for debugging or programming the flash. Not all the CRP + * values are valid and an invalid value may render the flash inaccessible and + * effectively brick the device. + * + * To select the access level define the @ref QN908X_CRP macro from the global + * compile options, otherwise the default value in this module will be used + * (allowing everything). The value of the uint32_t CRP field in the Image + * vector table should be the "or" of the following QN908X_CRP_* macros. Every + * field must be either enabled or disabled, otherwise it would result in an + * invalid CRP value. + */ + +/** + * @brief Number of pages to protect (0 to 255). + * + * This defines the number of pages to protect starting from 0. A value of 0 + * in this macro means that no page is protected. The maximum number allowed to + * be passed to this macro is 255, however there are 256 pages in the flash. The + * last page is protected if any other page is protected. + * + * Protected pages can't be erased or written to by the ISP. + */ +#define QN908X_CRP_PROTECT_PAGES(X) (255 - (X)) + +/** + * @brief Mass erase from ISP allowed. + */ +#define QN908X_CRP_MASS_ERASE_ALLOW (0x800) +/** + * @brief Mass erase from ISP not allowed. + */ +#define QN908X_CRP_MASS_ERASE_DISALLOW (0x400) + +/** + * @brief Page erase/write from ISP (for unprotected pages) allowed. + */ +#define QN908X_CRP_PAGE_ERASE_WRITE_ALLOW (0x2000) +/** + * @brief Page erase/write from ISP (for unprotected pages) not allowed. + */ +#define QN908X_CRP_PAGE_ERASE_WRITE_DISALLOW (0x1000) + +/** + * @brief Flash read (for unprotected pages) from ISP allowed or not. + */ +#define QN908X_CRP_FLASH_READ_ALLOW (0x8000) +/** + * @brief Flash read (for unprotected pages) from ISP not allowed. + */ +#define QN908X_CRP_FLASH_READ_DISALLOW (0x4000) + +/** + * @brief ISP entry is allowed (via CHIP_MODE pin). + */ +#define QN908X_CRP_ISP_ENTRY_ALLOW (0x20000) +/** + * @brief ISP entry via CHIP_MODE pin is not allowed. + */ +#define QN908X_CRP_ISP_ENTRY_DISALLOW (0x10000) + +/** + * @brief External access is allowed (including SWD interface). + */ +#define QN908X_CRP_EXTERNAL_ACCESS_ALLOW (0x80000) +/** + * @brief External access is not allowed (including SWD interface). + */ +#define QN908X_CRP_EXTERNAL_ACCESS_DISALLOW (0x40000) + +/** @} */ + +/** + * @brief Default "Code Read Protection" allows everything. + */ +#ifndef QN908X_CRP +#define QN908X_CRP \ + (QN908X_CRP_PROTECT_PAGES(0) \ + | QN908X_CRP_MASS_ERASE_ALLOW \ + | QN908X_CRP_PAGE_ERASE_WRITE_ALLOW \ + | QN908X_CRP_FLASH_READ_ALLOW \ + | QN908X_CRP_ISP_ENTRY_ALLOW \ + | QN908X_CRP_EXTERNAL_ACCESS_ALLOW) +#endif /* QN908X_CRP */ + +/** + * @brief The "Code Read Protection" is stored at the offset 0x20. + * + * To modify the CRP field define the macro @ref QN908X_CRP. + */ +#define CORTEXM_VECTOR_RESERVED_0X20 QN908X_CRP + +/* Safety checks that the QN908X_CRP value is valid. */ +#if !(QN908X_CRP & QN908X_CRP_MASS_ERASE_ALLOW) == \ + !(QN908X_CRP & QN908X_CRP_MASS_ERASE_DISALLOW) +#error "Must select exactly one of QN908X_CRP_MASS_ERASE_* in the QN908X_CRP" +#endif +#if !(QN908X_CRP & QN908X_CRP_PAGE_ERASE_WRITE_ALLOW) == \ + !(QN908X_CRP & QN908X_CRP_PAGE_ERASE_WRITE_DISALLOW) +#error \ + "Must select exactly one of QN908X_CRP_PAGE_ERASE_WRITE_* in the QN908X_CRP" +#endif +#if !(QN908X_CRP & QN908X_CRP_FLASH_READ_ALLOW) == \ + !(QN908X_CRP & QN908X_CRP_FLASH_READ_DISALLOW) +#error "Must select exactly one of QN908X_CRP_FLASH_READ_* in the QN908X_CRP" +#endif +#if !(QN908X_CRP & QN908X_CRP_ISP_ENTRY_ALLOW) == \ + !(QN908X_CRP & QN908X_CRP_ISP_ENTRY_DISALLOW) +#error "Must select exactly one of QN908X_CRP_ISP_ENTRY_* in the QN908X_CRP" +#endif +#if !(QN908X_CRP & QN908X_CRP_EXTERNAL_ACCESS_ALLOW) == \ + !(QN908X_CRP & QN908X_CRP_EXTERNAL_ACCESS_DISALLOW) +#error \ + "Must select exactly one of QN908X_CRP_EXTERNAL_ACCESS_* in the QN908X_CRP" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* CPU_CONF_H */ +/** @} */ diff --git a/cpu/qn908x/include/gpio_mux.h b/cpu/qn908x/include/gpio_mux.h new file mode 100644 index 0000000000..0a152a05cf --- /dev/null +++ b/cpu/qn908x/include/gpio_mux.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * + * @{ + * + * @file + * @brief Common Pin MUX functions. + * + * The pins in this CPU are multiplexed to several different function. This + * module allows to configure the pin multiplexer (MUX) from peripheral drivers. + * + * @author iosabi + */ + +#ifndef GPIO_MUX_H +#define GPIO_MUX_H + +#include +#include "periph_cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Obtain the GPIO_BASE address from a GPIO_PIN(x, y) value. + */ +#define GPIO_T_ADDR_BASE(pin) (GPIOA_BASE + ((pin) & 0xf000u)) + +/** + * @brief Obtain the GPIO_BASE GPIO_Type* pointer from a GPIO_PIN(x, y) value. + */ +#define GPIO_T_ADDR(pin) ((GPIO_Type *)(GPIO_T_ADDR_BASE(pin))) + +/** + * @brief Obtain the "x" port number (0 based) from a GPIO_PIN(x, y) value. + * + * This macro needs to be kept in sync with the definition of GPIO_PIN. + */ +#define GPIO_T_PORT(pin) ((gpio_t)(pin) >> 12u) + +/** + * @brief Obtain the pin number "y" from a GPIO_PIN(x, y) value. + */ +#define GPIO_T_PIN(pin) ((pin) & 0x00ffu) + +#if defined(GPIOB_BASE) && (GPIO_T_ADDR_BASE(GPIO_PIN(1, 1)) != GPIOB_BASE) +#error "GPIO_T_ADDR(GPIO_PIN(1, x)) must be the GPIOB address" +#endif + +/** + * @brief Configure the pin mux to the given function. + * + * The meaning of the function value will depend on the gpio pin. + */ +void gpio_init_mux(gpio_t pin, uint32_t func); + +#ifdef __cplusplus +} +#endif + +#endif /* GPIO_MUX_H */ +/** @} */ diff --git a/cpu/qn908x/include/periph_cpu.h b/cpu/qn908x/include/periph_cpu.h new file mode 100644 index 0000000000..9d94eece99 --- /dev/null +++ b/cpu/qn908x/include/periph_cpu.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * @brief CPU specific definitions for internal peripheral handling + * + * @author iosabi + */ + +#ifndef PERIPH_CPU_H +#define PERIPH_CPU_H + +#include +#include + +#include "cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name CPU specific gpio_t type definition + * @{ + */ +#define HAVE_GPIO_T +typedef uint16_t gpio_t; +/** @} */ + +/** + * @brief Definition of a fitting UNDEF value + */ +#define GPIO_UNDEF (0xffff) + +/** + * @brief Define a CPU specific GPIO pin generator macro. + * + * This generates the GPIO port base address with a mask of the GPIO_PIN value + * to avoid a memory access. The value 12 here is selected as an optimization + * to be able to derive the GPIO port address with a simple mask of the GPIO_PIN + * value. + */ +#define GPIO_PIN(x, y) (((x) << 12u) | (y)) + +/* QN908x has a unique default Bluetooth address in the Flash Information Page + * descriptor. This value is set in the factory and cannot be modified by + * users. However, the actual Bluetooth address used by the stack may be + * different, this is just the default. */ +/** + * @brief Starting offset of CPU_ID + */ +#define CPUID_ADDR (FSL_FEATURE_FLASH_ADDR_OF_VENDOR_BD_ADDR) + +/** + * @brief Length of the CPU_ID in octets + */ +#define CPUID_LEN (6U) + +/** + * @brief Watchdog clock can be stopped independently of other clocks. + */ +#define WDT_HAS_STOP (1) + +/** + * @name WDT upper and lower bound times in ms + * @{ + */ +/** The WDT clock can run up to 16MHz (via CLK_APB) and the WDT counter is + * 32-bit so the maximum value in ms we can wait is ((1 << 32) - 1) / 16000. + * TODO: A much larger limit (~1.5 days) can be set if the WDT runs from the + * 32 KHz clock. However, this is likely decided by the board and depends on the + * clocks installed on the board. Figure out a way to configure this limit based + * on the clock used. + */ +#define NWDT_TIME_LOWER_LIMIT (0) +#define NWDT_TIME_UPPER_LIMIT (268435U) +#define WWDT_TIME_LOWER_LIMIT (0) +#define WWDT_TIME_UPPER_LIMIT (268435U) +/** @} */ + +/** + * @brief Generate GPIO mode bitfields + * + * The GPIO_MODE has the following structure: + * - bit 0: open-drain: 1 for enabled (open-drain mode) and 0 for disabled. + * - bit 1: output-enabled: 1 output mode, 0 input mode. + * - bit 4-5: pull_mode: 0 for hi-z (no pull-up or down), 1 for pull-down and 2 + * for pull-up. These correspond to the IOCON_MODE macros. + */ +#define GPIO_MODE(open_drain, out_enabled, pull_mode) \ + ((open_drain) | ((out_enabled) << 1) | ((pull_mode) << 4)) + +#ifndef DOXYGEN +/** + * @name GPIO pin modes + * @{ + */ +#define HAVE_GPIO_MODE_T +typedef enum { + GPIO_IN = GPIO_MODE(0, 0, 0), /**< IN */ + GPIO_IN_PD = GPIO_MODE(0, 0, 1), /**< IN with pull-down */ + GPIO_IN_PU = GPIO_MODE(0, 0, 2), /**< IN with pull-up */ + GPIO_OUT = GPIO_MODE(0, 1, 0), /**< OUT (push-pull) */ + GPIO_OD = GPIO_MODE(1, 1, 0), /**< OD */ + GPIO_OD_PU = GPIO_MODE(1, 1, 2), /**< OD with pull-up */ +} gpio_mode_t; +/** @} */ +#endif /* ndef DOXYGEN */ + +#ifndef DOXYGEN +/** + * @name GPIO flank configuration values + * @{ + */ +#define HAVE_GPIO_FLANK_T +typedef enum { + GPIO_LOW = 0, /**< emit interrupt when the value is low */ + GPIO_HIGH = 1, /**< emit interrupt when the value is high */ + GPIO_RISING = 2, /**< emit interrupt on rising flank */ + GPIO_FALLING = 3, /**< emit interrupt on falling flank */ +} gpio_flank_t; +/** @} */ +#endif /* ndef DOXYGEN */ + +/** + * @brief Available ports on the QN908x. + */ +enum { + PORT_A = 0, /**< port A */ + PORT_B = 1, /**< port B */ + GPIO_PORTS_NUMOF /**< overall number of available ports */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_CPU_H */ +/** @} */ diff --git a/cpu/qn908x/include/vectors_qn908x.h b/cpu/qn908x/include/vectors_qn908x.h new file mode 100644 index 0000000000..4c25a74f18 --- /dev/null +++ b/cpu/qn908x/include/vectors_qn908x.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * @brief Interrupt service routine declarations NXP QN908x MCUs + * + * @author iosabi + * + * @} + */ + +#ifndef VECTORS_QN908X_H +#define VECTORS_QN908X_H + +#include +#include "vectors_cortexm.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Dummy handler + */ +void dummy_handler(void); + +/* Device specific interrupt vectors */ +void isr_ext_gpio_wakeup(void); /**< Ext GPIO wakeup */ +void isr_osc(void); /**< BLE wakeup */ +void isr_acmp0(void); /**< Analog comparator0 */ +void isr_acmp1(void); /**< Analog comparator1 */ +void isr_rtc_sec(void); /**< RTC second */ +void isr_rtc_fr(void); /**< RTC free running */ +void isr_cs_wakeup(void); /**< Capacitive sense wakeup */ +void isr_cs(void); /**< Capacitive sense */ +void isr_gpioa(void); /**< GPIO group A */ +void isr_gpiob(void); /**< GPIO group B */ +void isr_dma0(void); /**< DMA controller */ +void isr_pin_int0(void); /**< pin or pattern match engine slice 0 */ +void isr_pin_int1(void); /**< pin or pattern match engine slice 1 */ +void isr_pin_int2(void); /**< pin or pattern match engine slice 2 */ +void isr_pin_int3(void); /**< pin or pattern match engine slice 3 */ +void isr_osc_int_low(void); /**< Inverse of OSC */ +void isr_usb0(void); /**< USB device */ +void isr_flexcomm0(void); /**< Flexcomm Interface 0 (USART) */ +void isr_flexcomm1(void); /**< Flexcomm Interface 1 (USART, I2C) */ +void isr_flexcomm2(void); /**< Flexcomm Interface 2 (SPI, I2C) */ +void isr_flexcomm3(void); /**< Flexcomm Interface 3 (SPI) */ +void isr_ble(void); /**< BLE interrupts */ +void isr_fsp(void); /**< FSP */ +void isr_qdec0(void); /**< QDEC0 */ +void isr_qdec1(void); /**< QDEC1 */ +void isr_ctimer0(void); /**< Standard counter/timer CTIMER0 */ +void isr_ctimer1(void); /**< Standard counter/timer CTIMER1 */ +void isr_ctimer2(void); /**< Standard counter/timer CTIMER2 */ +void isr_ctimer3(void); /**< Standard counter/timer CTIMER3 */ +void isr_wdt(void); /**< Watch dog timer */ +void isr_adc(void); /**< ADC */ +void isr_dac(void); /**< DAC */ +void isr_xtal_ready(void); /**< High frequency crystal ready */ +void isr_flash(void); /**< Flash */ +void isr_spifi0(void); /**< SPI flash interface */ +void isr_sct0(void); /**< SCTimer/PWM */ +void isr_rng(void); /**< Random number generator */ +void isr_calib(void); /**< Calibration */ +void isr_ble_tx(void); /**< ble tx flag */ +void isr_ble_rx(void); /**< ble rx flag */ +void isr_ble_freq_hop(void); /**< ble frequency hop */ +void isr_bod(void); /**< Brown out detect */ + +/** + * @{ + * @brief In-System Programming configuration field + * + * After the interrupt vectors, at address 0x110 there's a ISP + * (In-System Programming) configuration field. Define isp_configuration to + * any | combination of the following `QN908X_ISP_*` values to change the ISP + * allowed options. + */ +__attribute__((section(".vectors.100"))) +extern const uint32_t isp_configuration; + +/** + * @brief SMART write enabled to ISP + */ +#define QN908X_ISP_SMART_EN 0x80 + +/** + * @brief SMART USB disabled to ISP + */ +#define QN908X_ISP_USB_DIS 0x20 + +/** + * @brief SMART UART disabled to ISP + */ +#define QN908X_ISP_UART_DIS 0x08 + +/** + * @brief SMART SPI disabled to ISP + */ +#define QN908X_ISP_SPI_DIS 0x02 + +/** @} */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* VECTORS_QN908X_H */ diff --git a/cpu/qn908x/include/vendor/fsl_common.h b/cpu/qn908x/include/vendor/fsl_common.h new file mode 100644 index 0000000000..edff5629ab --- /dev/null +++ b/cpu/qn908x/include/vendor/fsl_common.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * @brief Wrapper header for SDK drivers. + * + * Vendor SDK drivers include "fsl_common.h" with many definitions and extra + * dependencies on more headers that are not used in the RIOT-OS port. This + * header is a wrapper intended to keep the SDK headers unchanged and provide + * the essential definitions needed by other SDK modules. + * + * @author iosabi + */ + +#ifndef FSL_COMMON_H +#define FSL_COMMON_H + +#include +#include +#include + +#include "irq.h" +#include "vectors_cortexm.h" + +#include "QN908XC.h" +#include "QN908XC_features.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* RIOT-OS equivalent functions. */ +#define DisableGlobalIRQ irq_disable +#define EnableGlobalIRQ irq_restore + +#define __Vectors cortex_vector_base + +#ifdef __cplusplus +} +#endif + +#endif /* FSL_COMMON_H */ +/** @} */ diff --git a/cpu/qn908x/isr_qn908x.c b/cpu/qn908x/isr_qn908x.c new file mode 100644 index 0000000000..c7b0b7f09f --- /dev/null +++ b/cpu/qn908x/isr_qn908x.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * @brief Default interrupt service routine definitions for NXP QN908x + * + * These weak default definitions act as a fallback definition if no driver + * defines a ISR for the specific interrupt. + * + * @author iosabi + * + * @} + */ + +#include "cpu.h" +#include "vectors_cortexm.h" +#include "vectors_qn908x.h" + +/* These are defined in vectors_cortexm.c. */ +extern void reset_handler_default(void); +extern uint32_t _estack; /* Exception stack pointer. */ + +/** + * @brief Jump to the reset_handle_default handler with the exception stack. + */ +__attribute__((noreturn)) static inline void cpu_restart(void) +{ + /* Reset the stack pointer to the beginning again and jump to the reset + * handler at the expected address. + */ + __asm volatile ("msr msp, %[estack]\n" + "mov pc, %[entry]\n" + : + : [ estack ] "r" (&_estack), + [ entry ] "r" (reset_handler_default) + : "memory"); + + /* This function doesn't return anyway. */ + while (1) {} +} + +/** + * @brief Remap the flash to address 0 on start. + * + * The bootloader will jump to the flash at address 0x21000000 which is aliased + * to the flash on this CPU. However, our program is linked to run as if the + * flash is mapped at address 0 which is the common case. The range starting at + * address 0 can be mapped to flash, RAM or ROM via the SYS_MODE_CTRL register, + * but on reset the default value (0x0) means that it is mapped to ROM. + * We need to remap the flash and change the program counter to be running from + * the right address range (0). + */ +void pre_startup(void) +{ + register unsigned int pc; + + /* Disable interrupts */ + __disable_irq(); + + /* Check whether we are running from the 0x21000000 range. If that's the + * case we need to remap the flash to the address 0 in SYS_MODE_CTRL and + * jump back to the reset_handler_default so everything starts as running + * from the address 0x0 instead. + */ + __asm volatile ("mov %0, pc" : "=r" (pc)); + + if ((pc & 0x21000000) == 0x21000000) { + SYSCON->SYS_MODE_CTRL |= 1; + cpu_restart(); + } +} + +/* QN908x interrupt service routines */ +WEAK_DEFAULT void isr_ext_gpio_wakeup(void); +WEAK_DEFAULT void isr_osc(void); +WEAK_DEFAULT void isr_acmp0(void); +WEAK_DEFAULT void isr_acmp1(void); +WEAK_DEFAULT void isr_rtc_sec(void); +WEAK_DEFAULT void isr_rtc_fr(void); +WEAK_DEFAULT void isr_cs_wakeup(void); +WEAK_DEFAULT void isr_cs(void); +WEAK_DEFAULT void isr_gpioa(void); +WEAK_DEFAULT void isr_gpiob(void); +WEAK_DEFAULT void isr_dma0(void); +WEAK_DEFAULT void isr_pin_int0(void); +WEAK_DEFAULT void isr_pin_int1(void); +WEAK_DEFAULT void isr_pin_int2(void); +WEAK_DEFAULT void isr_pin_int3(void); +WEAK_DEFAULT void isr_osc_int_low(void); +WEAK_DEFAULT void isr_usb0(void); +WEAK_DEFAULT void isr_flexcomm0(void); +WEAK_DEFAULT void isr_flexcomm1(void); +WEAK_DEFAULT void isr_flexcomm2(void); +WEAK_DEFAULT void isr_flexcomm3(void); +WEAK_DEFAULT void isr_ble(void); +WEAK_DEFAULT void isr_fsp(void); +WEAK_DEFAULT void isr_qdec0(void); +WEAK_DEFAULT void isr_qdec1(void); +WEAK_DEFAULT void isr_ctimer0(void); +WEAK_DEFAULT void isr_ctimer1(void); +WEAK_DEFAULT void isr_ctimer2(void); +WEAK_DEFAULT void isr_ctimer3(void); +WEAK_DEFAULT void isr_wdt(void); +WEAK_DEFAULT void isr_adc(void); +WEAK_DEFAULT void isr_dac(void); +WEAK_DEFAULT void isr_xtal_ready(void); +WEAK_DEFAULT void isr_flash(void); +WEAK_DEFAULT void isr_spifi0(void); +WEAK_DEFAULT void isr_sct0(void); +WEAK_DEFAULT void isr_rng(void); +WEAK_DEFAULT void isr_calib(void); +WEAK_DEFAULT void isr_ble_tx(void); +WEAK_DEFAULT void isr_ble_rx(void); +WEAK_DEFAULT void isr_ble_freq_hop(void); +WEAK_DEFAULT void isr_bod(void); + +void dummy_handler(void) +{ + dummy_handler_default(); +} diff --git a/cpu/qn908x/ldscripts/qn908x.ld b/cpu/qn908x/ldscripts/qn908x.ld new file mode 100644 index 0000000000..69a3e9eb07 --- /dev/null +++ b/cpu/qn908x/ldscripts/qn908x.ld @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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. + */ + +/** + * @addtogroup cpu_qn908x + * @{ + * + * @file + * @brief Sections definitions for the NXP QN908x MCUs + * + * @author iosabi + * + * This linker script organizes the flash headers to generate a "Legacy" image + * as described in the "Boot Process" section of the QN908x user manual. A + * legacy image contains an "Image vector table" which is the standard ARM + * vector table with some special values in the reserved fields. In particular, + * this needs a to have a valid checksum at address 0x1c to be considered a + * valid image by the bootloader, which is not set by the build process. + * + * @} + */ +OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +_vectors_length = 0x114; + +/* The Flash lock and protect descriptor occupies the last flash page of 0x800 + * bytes. See "Flash lock and protection" protection section. */ +_flash_lock_length = 0x800; + +INCLUDE cortexm_rom_offset.ld + +MEMORY +{ + /* Note: What we call "rom" here is the flash region for consistency with + * the rest of the RIOT build system naming. There is a 256 kB ROM memory in + * the QN908x holding the bootloader and Bluetooth stack that can't be + * modified by the user. + */ + vectors : ORIGIN = _rom_start_addr + _rom_offset, LENGTH = _vectors_length + rom (rx) : ORIGIN = _rom_start_addr + _rom_offset + _vectors_length, LENGTH = _fw_rom_length - _vectors_length - _flash_lock_length + ram (!rx) : ORIGIN = _ram_start_addr, LENGTH = _ram_length +} + +SECTIONS +{ + /* "Image vector table" 0x000-0x114, defined in the "Boot process" section, + * must have exactly this size, otherwise we configured something wrong. + */ + .vectors : + { + PROVIDE(_isr_vectors = .); + KEEP(*(SORT(.vector*))) + } > vectors + ASSERT (SIZEOF(.vectors) == _vectors_length, + "Image vector table size mismatch.") + ASSERT (ADDR(.vectors) == _rom_start_addr + _rom_offset, + "Image vector table must start at the beginning of the flash") + ASSERT (LOADADDR(.vectors) == _rom_start_addr + _rom_offset, + "Image vector table must start at the beginning of the flash") + +} + +INCLUDE cortexm_base.ld diff --git a/cpu/qn908x/periph/Makefile b/cpu/qn908x/periph/Makefile new file mode 100644 index 0000000000..a36df249ac --- /dev/null +++ b/cpu/qn908x/periph/Makefile @@ -0,0 +1 @@ +include $(RIOTMAKE)/periph.mk diff --git a/cpu/qn908x/periph/gpio.c b/cpu/qn908x/periph/gpio.c new file mode 100644 index 0000000000..ea0eb7744a --- /dev/null +++ b/cpu/qn908x/periph/gpio.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @ingroup drivers_periph_gpio + * + * @{ + * + * @file + * @brief Low-level GPIO driver implementation + * + * @author iosabi + * + * @} + */ + +#include +#include + +#include "cpu.h" +#include "bitarithm.h" +#include "periph/gpio.h" +#include "vectors_qn908x.h" +#include "gpio_mux.h" + +#include "vendor/drivers/fsl_clock.h" +#include "vendor/drivers/fsl_iocon.h" + +/* The pull-up / pull-down / high-z mode in the gpio_mode_t enum matches the + * values in the IOCON_PinMuxSet() function. + */ +#if (GPIO_MODE(0, 0, 0) & 0x30) != IOCON_MODE_HIGHZ +#error "GPIO_MODE(x, y, 0) must be High-Z mode" +#endif +#if (GPIO_MODE(0, 0, 1) & 0x30) != IOCON_MODE_PULLDOWN +#error "GPIO_MODE(x, y, 0) must be pull-down mode" +#endif +#if (GPIO_MODE(0, 0, 2) & 0x30) != IOCON_MODE_PULLUP +#error "GPIO_MODE(x, y, 0) must be pull-up mode" +#endif + +/* Bit mask indicating if a GPIO is set to open_drain. */ +static uint32_t gpio_open_drain[GPIO_PORTS_NUMOF] = {}; + +int gpio_init(gpio_t pin, gpio_mode_t mode) +{ + GPIO_Type *const base = GPIO_T_ADDR(pin); + const uint32_t mask = 1u << GPIO_T_PIN(pin); + + /* We need to enable the GPIO clock before we set any register in the GPIO + * blocks. */ + CLOCK_EnableClock(kCLOCK_Gpio); + + /* Disable the interrupts just in case this was already configured as an + * interrupt pin. Note: this only disables the pin(s) that you write a 1 + * to. */ + base->INTENCLR = mask; + + /* pin_mode is the "or" of the three parts: function, mode and drive + * strength. The mode is just the bits 4 and 5 of the gpio_mode_t and + * corresponds to the IOCON_MODE_* values */ + uint32_t pin_mode = + IOCON_FUNC0 | /* FUNC0 is digital GPIO on all pins. */ + (mode & 0x30) | IOCON_DRIVE_HIGH; + gpio_init_mux(pin, pin_mode); + + if (mode & 2) { + /* output mode */ + /* Configure the open-drain variable for allowing setting the values + * later. */ + if (mode & 1) { + /* open-drain enabled. */ + gpio_open_drain[GPIO_T_PORT(pin)] |= mask; + /* Starts with the pin set to "high" (open) in open-drain mode. + * The DATAOUT value doesn't do anything if the output is not + * enabled but we keep track of the current value in DATAOUT + * anyway to allow gpio_toggle. */ + base->OUTENCLR = mask; + base->DATAOUT |= mask; + } + else { + gpio_open_drain[GPIO_T_PORT(pin)] &= ~mask; + /* Starts with the pin set to low on push-pull mode. */ + base->DATAOUT &= ~mask; + base->OUTENSET = mask; + } + } + else { + /* input mode */ + gpio_open_drain[GPIO_T_PORT(pin)] &= ~mask; + base->OUTENCLR = mask; + } + + return 0; +} + +#ifdef MODULE_PERIPH_GPIO_IRQ + +typedef struct { + gpio_cb_t cb; + void *arg; +} gpio_isr_cb_state_t; + +/** + * @brief The number of GPIO pins per port. + */ +#define PINS_PER_PORT (32) + +/** + * @brief The total number of GPIO pins in the chip. + */ +#define TOTAL_GPIO_PINS (35) + +static gpio_isr_cb_state_t gpio_isr_state[TOTAL_GPIO_PINS] = {}; + +int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + uint8_t gpio_num = GPIO_T_PORT(pin) * PINS_PER_PORT + GPIO_T_PIN(pin); + + if (gpio_num >= TOTAL_GPIO_PINS) { + return -1; + } + gpio_isr_state[gpio_num].cb = cb; + gpio_isr_state[gpio_num].arg = arg; + + if (gpio_init(pin, mode) != 0) { + return -1; + } + + GPIO_Type *const base = GPIO_T_ADDR(pin); + const uint32_t mask = 1u << GPIO_T_PIN(pin); + + switch (flank) { + case GPIO_LOW: + base->INTTYPECLR = mask; /* CLR = level */ + base->INTPOLCLR = mask; /* CLR = low */ + break; + case GPIO_HIGH: + base->INTTYPECLR = mask; /* CLR = level */ + base->INTPOLSET = mask; /* SET = high */ + break; + case GPIO_FALLING: + base->INTTYPESET = mask; /* SET = edge */ + base->INTPOLCLR = mask; /* CLR = falling */ + break; + case GPIO_RISING: + base->INTTYPESET = mask; /* SET = edge */ + base->INTPOLSET = mask; /* SET = rising */ + break; + } + gpio_irq_enable(pin); + return 0; +} + +void gpio_irq_enable(gpio_t pin) +{ + GPIO_T_ADDR(pin)->INTENSET = 1u << GPIO_T_PIN(pin); +} + +void gpio_irq_disable(gpio_t pin) +{ + GPIO_T_ADDR(pin)->INTENCLR = 1u << GPIO_T_PIN(pin); +} + +#endif /* defined(MODULE_PERIPH_GPIO_IRQ) */ + +int gpio_read(gpio_t pin) +{ + return ((GPIO_T_ADDR(pin)->DATA) >> GPIO_T_PIN(pin)) & 1u; +} + +void gpio_set(gpio_t pin) +{ + GPIO_Type *const base = GPIO_T_ADDR(pin); + const uint32_t mask = 1u << GPIO_T_PIN(pin); + + /* out_clr has only the pin bit set if this is an open-drain pin, which + * means we need to disable the output. This needs to happen before changing + * DATAOUT. */ + const uint32_t out_clr = mask & gpio_open_drain[GPIO_T_PORT(pin)]; + + base->OUTENCLR = out_clr; + + base->DATAOUT |= mask; +} + +void gpio_clear(gpio_t pin) +{ + GPIO_Type *const base = GPIO_T_ADDR(pin); + const uint32_t mask = 1u << GPIO_T_PIN(pin); + + base->DATAOUT &= ~mask; + + /* out_clr has only the pin bit set if this is an open-drain pin, which + * means we need to enable the output. This needs to happen after changing + * DATAOUT. */ + const uint32_t out_clr = mask & gpio_open_drain[GPIO_T_PORT(pin)]; + base->OUTENSET = out_clr; +} + +void gpio_toggle(gpio_t pin) +{ + GPIO_Type *const base = GPIO_T_ADDR(pin); + const uint32_t mask = 1u << GPIO_T_PIN(pin); + const uint32_t out_clr = mask & gpio_open_drain[GPIO_T_PORT(pin)]; + const uint32_t dataout = base->DATAOUT; + + /* The output is disabled if the pin is an open-drain pin and DATAOUT is + * not set for that pin. This avoids having if conditions. */ + base->OUTENCLR = out_clr & ~dataout; + base->DATAOUT ^= mask; + /* The output is disabled if the pin is an open-drain and DATAOUT at the + * beginning of the function was set. */ + base->OUTENSET = out_clr & dataout; +} + +void gpio_write(gpio_t pin, int value) +{ + if (value) { + gpio_set(pin); + } + else { + gpio_clear(pin); + } +} + +#ifdef MODULE_PERIPH_GPIO_IRQ + +static inline void irq_handler(GPIO_Type *base, uint32_t port_num) +{ + uint32_t status = base->INTSTATUS; + + while (status) { + /* Clear all the flags immediately and process them in order. This gives + * a chance to execute every pin's interrupt handler even if another pin + * is always on. + * Note: to *clear* the interrupt flag you write a 1 to that bit. + */ + base->INTSTATUS = status; + while (status) { + uint8_t pin; + status = bitarithm_test_and_clear(status, &pin); + uint32_t gpio_num = port_num * PINS_PER_PORT + pin; + gpio_cb_t cb = gpio_isr_state[gpio_num].cb; + if (cb) { + cb(gpio_isr_state[gpio_num].arg); + } + } + status = base->INTSTATUS; + } +} + +#ifdef GPIOA_BASE +void isr_gpioa(void) +{ + irq_handler(GPIOA, 0); + cortexm_isr_end(); +} +#endif /* GPIOA_BASE */ + +#ifdef GPIOB_BASE +void isr_gpiob(void) +{ + irq_handler(GPIOB, 1); + cortexm_isr_end(); +} +#endif /* GPIOB_BASE */ + +#endif /* MODULE_PERIPH_GPIO_IRQ */ diff --git a/cpu/qn908x/periph/gpio_mux.c b/cpu/qn908x/periph/gpio_mux.c new file mode 100644 index 0000000000..330b45f5b1 --- /dev/null +++ b/cpu/qn908x/periph/gpio_mux.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * + * @{ + * + * @file + * @brief Common Pin MUX functions. + * + * @author iosabi + * + * @} + */ + +#include "gpio_mux.h" + +#include "vendor/drivers/fsl_iocon.h" + +void gpio_init_mux(gpio_t pin, uint32_t func) +{ + if (pin == GPIO_UNDEF) { + return; + } + IOCON_PinMuxSet(IOCON, GPIO_T_PORT(pin), GPIO_T_PIN(pin), func); +} diff --git a/cpu/qn908x/periph/wdt.c b/cpu/qn908x/periph/wdt.c new file mode 100644 index 0000000000..5dd6f26452 --- /dev/null +++ b/cpu/qn908x/periph/wdt.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @ingroup cpu_qn908x_wdt + * + * @{ + * + * @file + * @brief Low-level WDOG driver implementation + * + * @author iosabi + * + * @} + */ + +#include "periph/wdt.h" +#include +#include + +#include "vendor/drivers/fsl_clock.h" + +#include "debug.h" + +/* The number of cycles to refresh the WDT with when kicked. */ +static uint32_t wdt_load_value = 0xffffffff; + +/* The maximum value the WDT counter could have when kicked. The WDT counter + * always decrements starting from wdt_load_value, so when used in WINDOW mode + * a counter value larger than this means that WDT was kicked before the lower + * bound of the window. + */ +static uint32_t wdt_load_window_value = 0xffffffff; + +/* The value that will be loaded in the WDT counter after the first interrupt + * triggers. The WDT doesn't not reset the device automatically once the WDT + * counter reaches 0, instead it first triggers an interrupt and restarts the + * count again. This value will be loaded by the ISR after it triggers. A value + * of 0 means to reset immediately after the ISR triggers, which is used if no + * callback is provided. */ +static uint32_t wdt_load_after_isr = 0; + +#ifdef MODULE_PERIPH_WDT_CB +static wdt_cb_t wdt_cb; +static void *wdt_arg; +static bool wdt_kick_disabled = false; +#endif /* MODULE_PERIPH_WDT_CB */ + +/** + * Before making any change to the WDT it is required to "unlock" it by writing + * this value to the LOCK register. Call @ref _wdt_lock() to lock it again. + */ +static void _wdt_unlock(void) +{ + WDT->LOCK = 0x1acce551; +} + +/** + * @brief Lock the WDT block to prevent accidental changes. + */ +static void _wdt_lock(void) +{ + WDT->LOCK = 0x10c1ced; /* Any other value is as good. */ +} + +void wdt_start(void) +{ + CLOCK_EnableClock(kCLOCK_Wdt); +} + +void wdt_stop(void) +{ + /* This only stops the clock of the watchdog and therefore the counter + * stops, but leaves it otherwise configured. */ + CLOCK_DisableClock(kCLOCK_Wdt); +} + +void wdt_kick(void) +{ +#ifdef MODULE_PERIPH_WDT_CB + if (wdt_kick_disabled) { + return; + } +#endif /* MODULE_PERIPH_WDT_CB */ + if (WDT->VALUE > wdt_load_window_value) { + DEBUG("wdt_kick() called before the minimum window time."); + /* In this condition we simulate the WDT triggering immediately by + * setting its LOAD value to 0. This will cause the ISR (and the + * callback if there is one) if it wasn't called yet, which will update + * the LOAD value with the time derived from CONFIG_WDT_WARNING_PERIOD + * if needed and reset the device afterwards. */ + _wdt_unlock(); + WDT->LOAD = 0; + _wdt_lock(); + while (true) {} + } + _wdt_unlock(); + WDT->LOAD = wdt_load_value; + _wdt_lock(); +} + +static void wdt_setup(uint32_t min_time, uint32_t max_time, uint32_t isr_time) +{ + /* Check reset time limit */ + assert((max_time > NWDT_TIME_LOWER_LIMIT) + && (max_time < NWDT_TIME_UPPER_LIMIT)); + assert(min_time <= max_time); + + /* The clock is stopped after setup and calling wdt_start() is required. */ + wdt_stop(); + + const uint32_t tick_per_ms = CLOCK_GetFreq(kCLOCK_WdtClk) / 1000; + wdt_load_value = tick_per_ms * max_time; + wdt_load_window_value = tick_per_ms * (max_time - min_time); + wdt_load_after_isr = tick_per_ms * isr_time; + /* The ISR is always called as it is needed to trigger the reset after the + * appropriate delay even in the no callback case. */ + _wdt_unlock(); + WDT->LOAD = wdt_load_value; + WDT->CTRL = WDT_CTRL_RESEN_MASK | WDT_CTRL_INTEN_MASK; + _wdt_lock(); + NVIC_EnableIRQ(WDT_IRQn); +} + +void wdt_setup_reboot(uint32_t min_time, uint32_t max_time) +{ +#ifdef MODULE_PERIPH_WDT_CB + wdt_cb = NULL; + wdt_arg = NULL; +#endif /* MODULE_PERIPH_WDT_CB */ + wdt_setup(min_time, max_time, 0); +} + +#ifdef MODULE_PERIPH_WDT_CB +void wdt_setup_reboot_with_callback(uint32_t min_time, uint32_t max_time, + wdt_cb_t cb, void *arg) +{ + wdt_cb = cb; + wdt_arg = arg; + assert(max_time >= CONFIG_WDT_WARNING_PERIOD); + /* We don't support having a min_time that falls within the + * CONFIG_WDT_WARNING_PERIOD since that would mean that you can't call + * wdt_kick() until some time after the callback is called which is pretty + * useless considering that the purpose of the callback is to perform + * "specific safety operations of data logging before the actual reboot." + * before the reboot happens. After the callback is called the reboot is + * inevitable and calling wdt_kick() has no effect. This code moves the + * min_time back to at least the point where the callback is called which is + * a similar behavior. */ + if (max_time - CONFIG_WDT_WARNING_PERIOD < min_time) { + min_time = max_time - CONFIG_WDT_WARNING_PERIOD; + } + wdt_setup(min_time, max_time - CONFIG_WDT_WARNING_PERIOD, + CONFIG_WDT_WARNING_PERIOD); +} +#endif /* MODULE_PERIPH_WDT_CB */ + +void isr_wdt(void) +{ + DEBUG("[wdt] isr_wdt with LOAD=%" PRIu32 "\n", WDT->LOAD); + + /* Set the timer to reset the device after CONFIG_WDT_WARNING_PERIOD but not + * clear the interrupt bit in the WDT->INT_CLR register. This will cause the + * WDT to reset the device the next time the counter reaches 0, which now + * will happen again in CONFIG_WDT_WARNING_PERIOD ms. + * Since the wdt_cb may return before the new WDT counter triggers, which is + * the normal case if we expect the callback to do some short safety + * operations, we would exit the ISR without clearing the interrupt bit in + * WDT->INT_CLR which would cause the interrupt to be called again. To avoid + * this situation, we disable the IRQ source in the NVIC. + * After this ISR triggers, further wdt_kicks() are ignored to prevent the + * software from kicking the WDT during the CONFIG_WDT_WARNING_PERIOD. */ + NVIC_DisableIRQ(WDT_IRQn); + _wdt_unlock(); + WDT->LOAD = wdt_load_after_isr; + _wdt_lock(); + +#ifdef MODULE_PERIPH_WDT_CB + wdt_kick_disabled = true; + if (wdt_cb) { + wdt_cb(wdt_arg); + } +#endif /* MODULE_PERIPH_WDT_CB */ + cortexm_isr_end(); +} diff --git a/cpu/qn908x/system.c b/cpu/qn908x/system.c new file mode 100644 index 0000000000..1ca9752bef --- /dev/null +++ b/cpu/qn908x/system.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * @brief CMSIS system setup wrapper functions for NXP QN908x + * + * The system_QN908XC.h header is used by several vendor headers (including + * QN908XC.h which defines all the registers and some drivers). The + * system_QN908XC.c file in the vendor SDK implements the system initialization + * via the SystemInit() function and provides the current system clock + * frequency, however part of the system initialization is more appropriate in + * the board module (in `board_init`) or in the cpu module (in `cpu_init`) and + * some values like the XTAL or BUCK configuration depend on the actual board + * used while in the SDK code they are set to the QN9080DK developer module. + * Because of this we can't include that source file here. + * + * This file implements the minimum required to make some of the drivers in the + * vendor code work by providing the global SystemCoreClock variable and a + * function to update its value with the current clock configuration. + * + * @author iosabi + * + * @} + */ + +#include +#include "cpu.h" + +#include "vendor/drivers/fsl_clock.h" + +uint32_t SystemCoreClock = DEFAULT_SYSTEM_CLOCK; + +void SystemInit(void) +{ + /* Do nothing here. The system initialization is done in board_init() and + * cpu_init() as needed. This function shouldn't be called anyway. + */ +} + +void SystemCoreClockUpdate(void) +{ + SystemCoreClock = CLOCK_GetFreq(kCLOCK_CoreSysClk); +} diff --git a/cpu/qn908x/vectors.c b/cpu/qn908x/vectors.c new file mode 100644 index 0000000000..77b0288700 --- /dev/null +++ b/cpu/qn908x/vectors.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 iosabi + * + * 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_qn908x + * @{ + * + * @file + * + * @brief Interrupt vector for NXP QN908x MCUs + * + * @author iosabi + */ + +/** + * @name Interrupt vector definition + * @{ + */ + +#include "board.h" +#include "vectors_cortexm.h" +#include "vectors_qn908x.h" + +/* CPU specific interrupt vector table */ +ISR_VECTOR(1) const isr_t vector_cpu[CPU_IRQ_NUMOF] = { + [EXT_GPIO_WAKEUP_IRQn] = isr_ext_gpio_wakeup, /* Ext GPIO wakeup */ + [OSC_IRQn ] = isr_osc, /* BLE wakeup */ + [ACMP0_IRQn ] = isr_acmp0, /* Analog comparator0 */ + [ACMP1_IRQn ] = isr_acmp1, /* Analog comparator1 */ + [RTC_SEC_IRQn ] = isr_rtc_sec, /* RTC second */ + [RTC_FR_IRQn ] = isr_rtc_fr, /* RTC free running */ + [CS_WAKEUP_IRQn ] = isr_cs_wakeup, /* Capacitive sense wakeup */ + [CS_IRQn ] = isr_cs, /* Capacitive sense */ + [GPIOA_IRQn ] = isr_gpioa, /* GPIO group A */ + [GPIOB_IRQn ] = isr_gpiob, /* GPIO group B */ + [DMA0_IRQn ] = isr_dma0, /* DMA controller */ + [PIN_INT0_IRQn ] = isr_pin_int0, /* pin or pattern match engine slice 0 */ + [PIN_INT1_IRQn ] = isr_pin_int1, /* pin or pattern match engine slice 1 */ + [PIN_INT2_IRQn ] = isr_pin_int2, /* pin or pattern match engine slice 2 */ + [PIN_INT3_IRQn ] = isr_pin_int3, /* pin or pattern match engine slice 3 */ + [OSC_INT_LOW_IRQn ] = isr_osc_int_low, /* Inverse of OSC */ + [USB0_IRQn ] = isr_usb0, /* USB device */ + [FLEXCOMM0_IRQn ] = isr_flexcomm0, /* Flexcomm Interface 0 (USART) */ + [FLEXCOMM1_IRQn ] = isr_flexcomm1, /* Flexcomm Interface 1 (USART, I2C) */ + [FLEXCOMM2_IRQn ] = isr_flexcomm2, /* Flexcomm Interface 2 (SPI, I2C) */ + [FLEXCOMM3_IRQn ] = isr_flexcomm3, /* Flexcomm Interface 3 (SPI) */ + [BLE_IRQn ] = isr_ble, /* BLE interrupts */ + [FSP_IRQn ] = isr_fsp, /* FSP */ + [QDEC0_IRQn ] = isr_qdec0, /* QDEC0 */ + [QDEC1_IRQn ] = isr_qdec1, /* QDEC1 */ + [CTIMER0_IRQn ] = isr_ctimer0, /* Standard counter/timer CTIMER0 */ + [CTIMER1_IRQn ] = isr_ctimer1, /* Standard counter/timer CTIMER1 */ + [CTIMER2_IRQn ] = isr_ctimer2, /* Standard counter/timer CTIMER2 */ + [CTIMER3_IRQn ] = isr_ctimer3, /* Standard counter/timer CTIMER3 */ + [WDT_IRQn ] = isr_wdt, /* Watch dog timer */ + [ADC_IRQn ] = isr_adc, /* ADC */ + [DAC_IRQn ] = isr_dac, /* DAC */ + [XTAL_READY_IRQn ] = isr_xtal_ready, /* High frequency crystal ready */ + [FLASH_IRQn ] = isr_flash, /* Flash */ + [SPIFI0_IRQn ] = isr_spifi0, /* SPI flash interface */ + [SCT0_IRQn ] = isr_sct0, /* SCTimer/PWM */ + [RNG_IRQn ] = isr_rng, /* Random number generator */ + [CALIB_IRQn ] = isr_calib, /* Calibration */ + [BLE_TX_IRQn ] = isr_ble_tx, /* ble tx flag */ + [BLE_RX_IRQn ] = isr_ble_rx, /* ble rx flag */ + [BLE_FREQ_HOP_IRQn] = isr_ble_freq_hop, /* ble frequency hop */ + [BOD_IRQn ] = isr_bod, /* Brown out detect */ +}; + +__attribute__((weak)) const uint32_t isp_configuration = 0; + +/** @} */ diff --git a/cpu/qn908x/vendor/Makefile b/cpu/qn908x/vendor/Makefile new file mode 100644 index 0000000000..fcfd443af8 --- /dev/null +++ b/cpu/qn908x/vendor/Makefile @@ -0,0 +1,14 @@ +MODULE := vendor + +# Enable submodules, although these are all compiled into the same "vendor.a" +# library since vendor_% is marked as a pseudomodule. +SUBMODULES := 1 +SUBMODULES_NOFORCE := 1 + +CFLAGS += -Wno-unused-parameter -Wno-sign-compare +INCLUDES += \ + -I$(RIOTCPU)/$(CPU)/include/vendor/ \ + -I$(RIOTCPU)/$(CPU)/include/vendor/drivers/ \ + # + +include $(RIOTBASE)/Makefile.base