This is an automated email from the ASF dual-hosted git repository. xiaoxiang781216 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 88c1b56f2d23201fe74d1b848e7bf712a7957f72 Author: Kerogit <[email protected]> AuthorDate: Wed Mar 11 10:23:10 2026 +0100 arch/avr/src/avrdx/avrdx_swint: add support for software interrupts This patch adds support for software interrupt emulation on AVR DA/DB devices. The emulation is done using an otherwise unused I/O pin which is toggled by software to raise the interrupt. The main purpose of this is to support what is described as "Getting back into the game" in "High Performance: Zero Latency Interrupts" guide. [skip-cmake-parity] Signed-off-by: Kerogit <[email protected]> --- .../guides/concurrency/zerolatencyinterrupts.rst | 16 +- .../avr/avrdx/docs/software_interrupt.rst | 111 ++++++++ arch/avr/src/avrdx/Kconfig | 93 +++++- arch/avr/src/avrdx/Make.defs | 4 + arch/avr/src/avrdx/avrdx_init.c | 12 + arch/avr/src/avrdx/avrdx_swint.c | 315 +++++++++++++++++++++ arch/avr/src/avrdx/{avrdx_init.c => avrdx_swint.h} | 83 ++++-- arch/avr/src/avrdx/iodefs/avr128da28.h | 1 + arch/avr/src/avrdx/iodefs/avr128da64.h | 1 + arch/avr/src/avrdx/iodefs/avr128db64.h | 1 + 10 files changed, 615 insertions(+), 22 deletions(-) diff --git a/Documentation/guides/concurrency/zerolatencyinterrupts.rst b/Documentation/guides/concurrency/zerolatencyinterrupts.rst index 7cacc2caa44..2c3f8227d0f 100644 --- a/Documentation/guides/concurrency/zerolatencyinterrupts.rst +++ b/Documentation/guides/concurrency/zerolatencyinterrupts.rst @@ -311,8 +311,7 @@ AVR DA/DB implementation ======================== Implementation of high priority interrupts allows the user to bypass the generic -interrupt handling. There is, however, no mechanism described in the "Getting Back -into the Game" section. +interrupt handling. This AVR family only has one interrupt vector that can be declared as high-priority. Nevertheless, you can disable the generic handler for multiple vectors - they will @@ -349,3 +348,16 @@ like in regular functions) and return using ``reti`` (return from interrupt) ins The function name always corresponds to interrupt name as specified in datasheet for the chip in question. + +Getting Back into the Game (AVR) +-------------------------------- + +Unlike the ARM Cortex-M described above, AVR DA/DB devices do not have a dedicated +software interrupt mechanism. Instead, there is an emulated implementation +that uses interrupts triggered by manual manipulation of an I/O pin. This is described +in :doc:`/platforms/avr/avrdx/docs/software_interrupt`. + +Your implementation simply needs to request a software interrupt vector number +during initialization and then pass it to ``avrdx_swint_raise`` function +if it needs to trigger an interrupt that can be handled with access to the operating +system interfaces. diff --git a/Documentation/platforms/avr/avrdx/docs/software_interrupt.rst b/Documentation/platforms/avr/avrdx/docs/software_interrupt.rst new file mode 100644 index 00000000000..145cf2bff17 --- /dev/null +++ b/Documentation/platforms/avr/avrdx/docs/software_interrupt.rst @@ -0,0 +1,111 @@ +============================================= +AVR DA/DB family software interrupt emulation +============================================= + +AVR DA/DB family chips do not have any mechanism to trigger a software-based +interrupt. This can be worked around using interrupts from hardware - using +a software action that triggers an interrupt (almost) immediately. In NuttX +on AVR DA/DB chips, direct manipulation with an I/O pin is used. + +This feature is intended to be used by drivers, mostly those which implement +part of their functionality using (hardware) interrupt handlers as described +in :doc:`/guides/concurrency/zerolatencyinterrupts`. For example, such an interrupt may +be designed to do something quickly and repeatedly until some job is done. +If, at that point, the user space needs to be notified by posting a semaphore, +the interrupt handler cannot do that as it can't use any OS interface. + +Instead, the interrupt handler can trigger a software interrupt and handler +for that interrupt - implemented as a standard NuttX interrupt handler - will +have access to any interface it needs. + +Since there may be more than one user of the feature at any given time, +the implementation has a concept of software interrupt vector. Any user +can request one at runtime (generally during initialization) and have +its own handler executed. + +Implementation +============== + +A pin chosen by user (or board default configuration) in Kconfig is configured +as an output and also to trigger on level interrupt (ie. when the bit us pulled +low.) + +Under normal operation, the pin is driven high. When some user wants to trigger +a software interrupt, it simply writes to the corresponding I/O register to pull +the pin low. That triggers the level interrupt, interrupt handler runs +and executes associated handler. + +The handler also takes care of driving the pin high again to stop triggering +the interrupt. + + .. note:: The selected I/O pin behaves as output I/O pin. The board design + needs to take this into account and nothing must be connected to the pin + (unless the design wants to externally monitor software interrupts.) + Also, the pin must not be overridden by a peripheral. + +Configuration +============= + +The configuration option that enables this feature is located at +:menuselection:`System Type --> Enable software interrupt emulation`. +Enabling it unlocks further options: + +:menuselection:`Use GPIO multiplexer` - available +if :menuselection:`GPIO ISR multiplexer` is enabled. This option allows you +to use software interrupts via the selected pin while also using interrupts +from the other pins for other purposes. More information +in :doc:`gpio_intr_mux`. + +:menuselection:`Software interrupt emulation I/O port` +and :menuselection:`Software interrupt emulation I/O pin` +options select the pin which will be used to trigger the software interrupt +handler. Enter numbers - port number always corresponds to its alphabetical +order (ie. A is port 0, B is port 1, E is port 4 etc.) Pin numbers are indexed +from zero. + +(Default value of -1 is there to prevent the user from accidentally enabling +this option without setting it properly, which could cause damage to the device +if the pin is connected to something that is not a high-impedance node.) + +:menuselection:`Software interrupt vector limit` - limits the number of users +of this feature. Most notably, this affects size of the variable that holds +list of software interrupt vectors that are currently raised. + +Any value in range of 1-32 can be given but the variable size increases +only with multiples of 8 (from ``uint8_t`` to ``uint32_t``) + +Usage +===== + +Initialization +-------------- + +Software interrupt emulation needs to be initialized by calling +``avrdx_swint_initialize``. This happens automatically +in ``avrdx_up_initialize`` if the feature is enabled. + +Any user of the software interrupts must first obtain a interrupt vector. +This is done using ``avrdx_swint_allocate``: + +.. code-block:: c + + #include <nuttx/irq.h> + #include "avrdx_swint.h" + + avrdx_swint_size_t vector; + + vector = avrdx_swint_allocate(handler, NULL); + +Both ``handler`` and the second parameter correspond to parameters +of ``irq_attach`` function. + +The returned value ``vector`` should be treated as an opaque identifier. + +This identifier is then used in a call to ``avrdx_swint_raise``: + +.. code-block:: c + + avrdx_swint_raise(vector); + +Function ``handler`` from the call above will be called +(in an interrupt context.) diff --git a/arch/avr/src/avrdx/Kconfig b/arch/avr/src/avrdx/Kconfig index e4f7756d969..2e3388d25ea 100644 --- a/arch/avr/src/avrdx/Kconfig +++ b/arch/avr/src/avrdx/Kconfig @@ -174,9 +174,11 @@ choice AVR_USART0_MUX config AVR_USART0_DEFAULT bool "Default USART0 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 0) && ((AVRDX_SWINT_PIN_NUM = 0) || (AVRDX_SWINT_PIN_NUM = 1))) config AVR_USART0_ALT bool "Alternate USART0 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 0) && ((AVRDX_SWINT_PIN_NUM = 4) || (AVRDX_SWINT_PIN_NUM = 5))) ---help--- Board is wired to use alternative I/O pins, 4-5 instead of 0-1 for TxD/RxD @@ -206,10 +208,12 @@ choice AVR_USART1_MUX config AVR_USART1_DEFAULT bool "Default USART1 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 2) && ((AVRDX_SWINT_PIN_NUM = 0) || (AVRDX_SWINT_PIN_NUM = 1))) config AVR_USART1_ALT - depends on AVR_HAVE_USART1_ALT1 bool "Alternate USART1 pinout" + depends on AVR_HAVE_USART1_ALT1 + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 2) && ((AVRDX_SWINT_PIN_NUM = 4) || (AVRDX_SWINT_PIN_NUM = 5))) ---help--- Board is wired to use alternative I/O pins, 4-5 instead of 0-1 for TxD/RxD @@ -239,10 +243,12 @@ choice AVR_USART2_MUX config AVR_USART2_DEFAULT bool "Default USART2 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 5) && ((AVRDX_SWINT_PIN_NUM = 0) || (AVRDX_SWINT_PIN_NUM = 1))) config AVR_USART2_ALT - depends on AVR_HAVE_USART2_ALT1 bool "Alternate USART2 pinout" + depends on AVR_HAVE_USART2_ALT1 + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 5) && ((AVRDX_SWINT_PIN_NUM = 4) || (AVRDX_SWINT_PIN_NUM = 5))) ---help--- Board is wired to use alternative I/O pins, 4-5 instead of 0-1 for TxD/RxD @@ -272,9 +278,11 @@ choice AVR_USART3_MUX config AVR_USART3_DEFAULT bool "Default USART3 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 1) && ((AVRDX_SWINT_PIN_NUM = 0) || (AVRDX_SWINT_PIN_NUM = 1))) config AVR_USART3_ALT depends on AVR_HAVE_USART3_ALT1 + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 1) && ((AVRDX_SWINT_PIN_NUM = 4) || (AVRDX_SWINT_PIN_NUM = 5))) bool "Alternate USART3 pinout" ---help--- Board is wired to use alternative I/O pins, @@ -305,10 +313,12 @@ choice AVR_USART4_MUX config AVR_USART4_DEFAULT bool "Default USART4 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 4) && ((AVRDX_SWINT_PIN_NUM = 0) || (AVRDX_SWINT_PIN_NUM = 1))) config AVR_USART4_ALT - depends on AVR_HAVE_USART4_ALT1 bool "Alternate USART4 pinout" + depends on AVR_HAVE_USART4_ALT1 + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 4) && ((AVRDX_SWINT_PIN_NUM = 4) || (AVRDX_SWINT_PIN_NUM = 5))) ---help--- Board is wired to use alternative I/O pins, 4-5 instead of 0-1 for TxD/RxD @@ -338,10 +348,12 @@ choice AVR_USART5_MUX config AVR_USART5_DEFAULT bool "Default USART5 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 6) && ((AVRDX_SWINT_PIN_NUM = 0) || (AVRDX_SWINT_PIN_NUM = 1))) config AVR_USART5_ALT - depends on AVR_HAVE_USART5_ALT1 bool "Alternate USART5 pinout" + depends on AVR_HAVE_USART5_ALT1 + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 6) && ((AVRDX_SWINT_PIN_NUM = 4) || (AVRDX_SWINT_PIN_NUM = 5))) ---help--- Board is wired to use alternative I/O pins, 4-5 instead of 0-1 for TxD/RxD @@ -368,13 +380,18 @@ choice AVR_TWI0_ALT config AVR_TWI0_ALT0 bool "Default TWI0 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 0) && ((AVRDX_SWINT_PIN_NUM = 2) || (AVRDX_SWINT_PIN_NUM = 3))) + # Note - not excluding dual mode pins since dual mode is + # currently unsupported. config AVR_TWI0_ALT1 bool "Alternate TWI0 pinout 1" depends on AVR_HAVE_TWI0_ALT1 + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 0) && ((AVRDX_SWINT_PIN_NUM = 2) || (AVRDX_SWINT_PIN_NUM = 3))) config AVR_TWI0_ALT2 bool "Alternate TWI0 pinout 2" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 2) && ((AVRDX_SWINT_PIN_NUM = 2) || (AVRDX_SWINT_PIN_NUM = 3))) endchoice @@ -436,13 +453,18 @@ choice AVR_TWI1_ALT config AVR_TWI1_ALT0 bool "Default TWI1 pinout" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 5) && ((AVRDX_SWINT_PIN_NUM = 2) || (AVRDX_SWINT_PIN_NUM = 3))) + # Note - not excluding dual mode pins since dual mode is + # currently unsupported. config AVR_TWI1_ALT1 bool "Alternate TWI1 pinout 1" depends on AVR_HAVE_TWI1_ALT1 + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 5) && ((AVRDX_SWINT_PIN_NUM = 2) || (AVRDX_SWINT_PIN_NUM = 3))) config AVR_TWI1_ALT2 bool "Alternate TWI1 pinout 2" + depends on (!AVR_AVRDX_SWINT) || !((AVRDX_SWINT_PORT_IDX = 1) && ((AVRDX_SWINT_PIN_NUM = 2) || (AVRDX_SWINT_PIN_NUM = 3))) endchoice @@ -520,6 +542,69 @@ endif endmenu # AVR DA/DB Peripheral Selections +config AVR_AVRDX_SWINT + bool "Enable software interrupt emulation" + default false + ---help--- + AVR DA/DB chips do not have software interrupt functionality. + It can, however, be emulated using interrupt on I/O pin + (triggered by changing output logical level on that pin + from software.) + + Do note that the pin will be actually configured as output, + nothing must be connected to it. + + Some drivers depend on this. + +config AVRDX_SWINT_GPIO_MUX + bool "Use GPIO multiplexer" + default false + depends on AVR_AVRDX_SWINT + depends on AVR_AVRDX_GPIO_ISR_MUX + ---help--- + This chip only has single interrupt vector for all pins + on the port. If you are not using dedicated port for software + interrupt emulation and need different interrupt handlers + for different pin events, you need to use GPIO multiplexer + + Say Y in that case. + +config AVRDX_SWINT_PORT_IDX + int "Software interrupt emulation I/O port" + default -1 + range -1 6 + depends on AVR_AVRDX_SWINT + ---help--- + Enter index of I/O port that will be used for software + interrupt emulation. Port A corresponds to index 0, port B + to 1 etc. + +config AVRDX_SWINT_PIN_NUM + int "Software interrupt emulation I/O pin" + default -1 + range -1 7 + depends on AVR_AVRDX_SWINT + ---help--- + Enter index of I/O pin on the port selected above that will + be used for software interrupt emulation. Pins are indexed + from 0. + +config AVRDX_SWINT_MAX_VECTORS + int "Software interrupt vector limit" + default 2 + range 1 32 + depends on AVR_AVRDX_SWINT + ---help--- + Enter maximum number of software interrupt emulation users. + Each user receives its own software interrupt vector and + can provide its own handler. + + Memory for handler pointers is preallocated to this number + of slots. Variables holding vector number are configured + to size that fits all vectors. + + At most 32 vectors may be provided. + choice prompt "Internal High-Frequency Oscillator clock speed" default AVRDX_HFO_CLOCK_24MHZ diff --git a/arch/avr/src/avrdx/Make.defs b/arch/avr/src/avrdx/Make.defs index 203390efb4e..67f734b0be3 100644 --- a/arch/avr/src/avrdx/Make.defs +++ b/arch/avr/src/avrdx/Make.defs @@ -73,3 +73,7 @@ endif ifeq ($(CONFIG_AVR_AVRDX_GPIO_ISR_MUX),y) CHIP_CSRCS += avrdx_gpio_isr_mux.c endif + +ifeq ($(CONFIG_AVR_AVRDX_SWINT),y) +CHIP_CSRCS += avrdx_swint.c +endif diff --git a/arch/avr/src/avrdx/avrdx_init.c b/arch/avr/src/avrdx/avrdx_init.c index f5e0529e7a9..b17da23e2f2 100644 --- a/arch/avr/src/avrdx/avrdx_init.c +++ b/arch/avr/src/avrdx/avrdx_init.c @@ -27,6 +27,10 @@ #include <nuttx/config.h> #include "avrdx_gpio.h" +#ifdef CONFIG_AVR_AVRDX_SWINT +# include "avrdx_swint.h" +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -69,4 +73,12 @@ void avrdx_up_initialize(void) #ifdef CONFIG_AVR_AVRDX_GPIO_ISR_MUX avrdx_gpio_isr_mux_init(); #endif + + /* Initialize software interrupts - must be done after + * GPIO multiplexer because it (optionally) uses it. + */ + +#ifdef CONFIG_AVR_AVRDX_SWINT + avrdx_swint_initialize(); +#endif } diff --git a/arch/avr/src/avrdx/avrdx_swint.c b/arch/avr/src/avrdx/avrdx_swint.c new file mode 100644 index 00000000000..d3173c413ee --- /dev/null +++ b/arch/avr/src/avrdx/avrdx_swint.c @@ -0,0 +1,315 @@ +/**************************************************************************** + * arch/avr/src/avrdx/avrdx_swint.c + * AVR DA/DB software interrupt emulation + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> +#include <stdint.h> +#include <nuttx/irq.h> +#include <assert.h> +#include <avr/io.h> + +#include "avrdx.h" +#include "avrdx_swint.h" +#include "avrdx_gpio.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Verify configuration */ + +#ifdef CONFIG_AVRDX_SWINT_PORT_IDX +# if ((CONFIG_AVRDX_SWINT_PORT_IDX < 0) || (CONFIG_AVRDX_SWINT_PORT_IDX > 6)) +# error Port idx must be within range of 0-6 +# endif +#else +# error Port idx must be set +#endif +#ifdef CONFIG_AVRDX_SWINT_PIN_NUM +# if ((CONFIG_AVRDX_SWINT_PIN_NUM < 0) || (CONFIG_AVRDX_SWINT_PIN_NUM > 7)) +# error Port idx must be within range of 0-6 +# endif +#else +# error Port idx must be set +#endif + +/* Convert pin number to its corresponding bitmask */ + +#define AVRDX_SWINT_PIN_BITMASK (1 << CONFIG_AVRDX_SWINT_PIN_NUM) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Struct that holds a handler that is to be executed by + * the software interrupt + */ + +struct avrdx_swint_isr_s +{ + xcpt_t handler; /* handler given by the user */ + FAR void *arg; /* arg to be given to the handler */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This variable is a bitfield. For every software interrupt vector raised, + * the corresponding bit is set to 1. + */ + +static avrdx_swint_size_t g_swint_flags; + +/* Handlers for each software interrupt vector. If set to NULL, corresponding + * software interrupt vector is not assigned. + */ + +struct avrdx_swint_isr_s g_swint_handlers[CONFIG_AVRDX_SWINT_MAX_VECTORS]; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: avrdx_swint_isr + * + * Description: + * ISR for software interrupt. Triggered by the interrupt source + * which pulls an output pin low. Reads raised software interrupt vectors + * and runs their handlers. + * + * Input Parameters: + * Arg - ignored + * + * Returned Value: void + * + * Assumptions/Limitations: + * Running in interrupt context + * + ****************************************************************************/ + +static int avrdx_swint_isr(int irq, void *context, void *arg) +{ + avrdx_swint_size_t raised_vectors; + avrdx_swint_size_t j; + irqstate_t irqflags; + uint8_t i; + + /* In theory, interrupt handler may be interrupted by handler of higher + * priority. Not supported directly in NuttX yet but let's be future-proof. + * The higher priority interrupt may raise a software interrupt while + * we're reading the flags. + * + * Disable interrupts, read flags and put the triggering pin back + * to logical 1. + */ + + irqflags = enter_critical_section(); + + raised_vectors = g_swint_flags; + g_swint_flags = 0; + AVRDX_PORT(CONFIG_AVRDX_SWINT_PORT_IDX).OUTSET = AVRDX_SWINT_PIN_BITMASK; + +#ifndef CONFIG_AVRDX_SWINT_GPIO_MUX + /* GPIO multiplexer takes care of resetting port's interrupt flags + * register when in use. It is not, we need to care of that here. + */ + + AVRDX_PORT(CONFIG_AVRDX_SWINT_PORT_IDX).INTFLAGS = AVRDX_SWINT_PIN_BITMASK; +#endif + + leave_critical_section(irqflags); + + for (i = 0, j = 1; i < CONFIG_AVRDX_SWINT_MAX_VECTORS; i++, j <<= 1) + { + if ((raised_vectors & j) == 0) + { + continue; + } + + if (g_swint_handlers[i].handler == NULL) + { + PANIC(); + } + + g_swint_handlers[i].handler(irq, context, g_swint_handlers[i].arg); + } + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: avrdx_swint_allocate + * + * Description: + * Allocates software interrupt vector for the caller and returns it, + * if any vector is available + * + * Input Parameters: + * Handler - to be executed in interrupt context when triggered + * Opaque arg argument - passed to the handler + * + * Returned Value: + * Software interrupt vector number to be used in avrdx_swint_raise + * or zero if no vectors are available. The number is single bit mask + * so it can be quickly OR'ed with others. Treat it as opaque value though. + * + ****************************************************************************/ + +avrdx_swint_size_t avrdx_swint_allocate(xcpt_t handler, void *arg) +{ + uint8_t i; + irqstate_t irqflags; + avrdx_swint_size_t ret; + + /* Unlikely to happen but two tasks may be doing this + * at the same time. + */ + + irqflags = enter_critical_section(); + + for (i = 0, ret = 1; i < CONFIG_AVRDX_SWINT_MAX_VECTORS; i++, ret <<= 1) + { + if (g_swint_handlers[i].handler == NULL) + { + break; + } + } + + if (i == CONFIG_AVRDX_SWINT_MAX_VECTORS) + { + return 0; + } + + g_swint_handlers[i].handler = handler; + g_swint_handlers[i].arg = arg; + + leave_critical_section(irqflags); + + return ret; +} + +/**************************************************************************** + * Name: avrdx_swint_raise + * + * Description: + * Raises software interrupt with given vector number. Note that this + * function is designed to be runnable from code that runs outside + * of NuttX - this mainly means so called zero latency interrupts. + * + * Input Parameters: + * vector - number that identifies which software interrupt was raised + * (use return value from avrdx_swint_allocate.) + * + * Returned Value: void + * + ****************************************************************************/ + +void avrdx_swint_raise(avrdx_swint_size_t vector) +{ + uint8_t sreg; + + /* Need to disable interrupts but cannot use any function provided + * by the operating system - enter_critical_section cannot be used + * (we can never be sure about what it does under the hood.) + */ + + sreg = SREG; + asm volatile("cli"); + + g_swint_flags |= vector; + AVRDX_PORT(CONFIG_AVRDX_SWINT_PORT_IDX).OUTCLR = AVRDX_SWINT_PIN_BITMASK; + + SREG = sreg; +} + +/**************************************************************************** + * Name: avrdx_swint_initialize + * + * Description: + * Initialize hardware related to software interrupts. + * + ****************************************************************************/ + +int avrdx_swint_initialize(void) +{ + int ret = 0; +#ifndef CONFIG_AVRDX_SWINT_GPIO_MUX + uint8_t vector_num; + irqstate_t irqflags; +#endif + + /* First configure the pin as output and set it to logical one */ + + AVRDX_PORT(CONFIG_AVRDX_SWINT_PORT_IDX).DIRSET = AVRDX_SWINT_PIN_BITMASK; + AVRDX_PORT(CONFIG_AVRDX_SWINT_PORT_IDX).OUTSET = AVRDX_SWINT_PIN_BITMASK; + + /* Now configure the interrupt handler */ + +#ifdef CONFIG_AVRDX_SWINT_GPIO_MUX + ret = avrdx_irq_attach_gpio_mux( + CONFIG_AVRDX_SWINT_PORT_IDX, + AVRDX_SWINT_PIN_BITMASK, + avrdx_swint_isr, + NULL, + PORT_ISC_LEVEL_GC); + + if (ret != OK) + { + PANIC(); + } +#else + vector_num = avrdx_gpio_irq_vectors[CONFIG_AVRDX_SWINT_PORT_IDX]; + + /* Disabling interrupts here to be safe. This should only run on startup + * but let's not risk that some event already happens during that time + * that would interfere with PINCONFIG register + */ + + irqflags = enter_critical_section(); + AVRDX_PORT(CONFIG_AVRDX_SWINT_PORT_IDX).PINCONFIG = PORT_ISC_LEVEL_GC; + AVRDX_PORT(CONFIG_AVRDX_SWINT_PORT_IDX).PINCTRLSET = \ + AVRDX_SWINT_PIN_BITMASK; + leave_critical_section(irqflags); + + irq_attach(vector_num, avrdx_swint_isr, NULL); +#endif + + return ret; +} diff --git a/arch/avr/src/avrdx/avrdx_init.c b/arch/avr/src/avrdx/avrdx_swint.h similarity index 57% copy from arch/avr/src/avrdx/avrdx_init.c copy to arch/avr/src/avrdx/avrdx_swint.h index f5e0529e7a9..f4c6184a664 100644 --- a/arch/avr/src/avrdx/avrdx_init.c +++ b/arch/avr/src/avrdx/avrdx_swint.h @@ -1,5 +1,5 @@ /**************************************************************************** - * arch/avr/src/avrdx/avrdx_init.c + * arch/avr/src/avrdx/avrdx_swint.h * * SPDX-License-Identifier: Apache-2.0 * @@ -20,53 +20,104 @@ * ****************************************************************************/ +#ifndef __ARCH_AVR_SRC_AVRDX_AVRDX_SWINT_H +#define __ARCH_AVR_SRC_AVRDX_AVRDX_SWINT_H + /**************************************************************************** * Included Files ****************************************************************************/ #include <nuttx/config.h> -#include "avrdx_gpio.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /**************************************************************************** - * Private Types + * Public Types ****************************************************************************/ +#ifndef __ASSEMBLY__ + +#if (CONFIG_AVRDX_SWINT_MAX_VECTORS <= 8) +typedef uint8_t avrdx_swint_size_t; +#elif (CONFIG_AVRDX_SWINT_MAX_VECTORS <= 16) +typedef uint16_t avrdx_swint_size_t; +#elif (CONFIG_AVRDX_SWINT_MAX_VECTORS <= 24) +typedef uint24_t avrdx_swint_size_t; +#elif (CONFIG_AVRDX_SWINT_MAX_VECTORS <= 32) +typedef uint32_t avrdx_swint_size_t; +#else +# error Kconfig should allow 32 vectors at most +#endif + /**************************************************************************** - * Private Function Prototypes + * Public Data ****************************************************************************/ +#ifdef __cplusplus +extern "C" +{ +#endif + /**************************************************************************** - * Private Data + * Inline Functions ****************************************************************************/ /**************************************************************************** - * Public Data + * Public Function Prototypes ****************************************************************************/ /**************************************************************************** - * Private Functions + * Name: avrdx_swint_allocate + * + * Description: + * Allocates software interrupt vector for the caller and returns it, + * if any vector is available + * + * Input Parameters: + * Handler - to be executed in interrupt context when triggered + * Opaque arg argument - passed to the handler + * + * Returned Value: + * Software interrupt vector number to be used in avrdx_swint_raise + * or zero if no vectors are available. The number is single bit mask + * so it can be quickly OR'ed with others. Treat it as opaque value though. + * ****************************************************************************/ +avrdx_swint_size_t avrdx_swint_allocate(xcpt_t handler, void *arg); + /**************************************************************************** - * Public Functions + * Name: avrdx_swint_raise + * + * Description: + * Raises software interrupt with given vector number. Note that this + * function is designed to be runnable from code that runs outside + * of NuttX - this mainly means so called zero latency interrupts. + * + * Input Parameters: + * vector - number that identifies which software interrupt was raised + * (use return value from avrdx_swint_allocate.) + * ****************************************************************************/ +void avrdx_swint_raise(avrdx_swint_size_t vector); + /**************************************************************************** - * Name: avrdx_up_initialize + * Name: avrdx_swint_initialize * * Description: - * Extension of up_initialize for AVR DA/DB cores + * Initialize hardware related to software interrupts. + * ****************************************************************************/ -void avrdx_up_initialize(void) -{ - /* Initialize GPIO interrupt multiplexer, if enabled */ +int avrdx_swint_initialize(void); -#ifdef CONFIG_AVR_AVRDX_GPIO_ISR_MUX - avrdx_gpio_isr_mux_init(); -#endif +#ifdef __cplusplus } +#endif + +#endif /* __ASSEMBLY__ */ + +#endif diff --git a/arch/avr/src/avrdx/iodefs/avr128da28.h b/arch/avr/src/avrdx/iodefs/avr128da28.h index 0ac9b08dafa..1faf271e223 100644 --- a/arch/avr/src/avrdx/iodefs/avr128da28.h +++ b/arch/avr/src/avrdx/iodefs/avr128da28.h @@ -63,6 +63,7 @@ #define PORT_ISC_GM ( PORT_ISC_0_bm | PORT_ISC_1_bm | PORT_ISC_2_bm ) #define PORT_ISC_BOTHEDGES_GC ( PORT_ISC_0_bm ) +#define PORT_ISC_LEVEL_GC ( PORT_ISC_2_bm | PORT_ISC_0_bm ) /* CLKCTRL.MCLKCTRLA */ diff --git a/arch/avr/src/avrdx/iodefs/avr128da64.h b/arch/avr/src/avrdx/iodefs/avr128da64.h index a179ff16835..889b8560a95 100644 --- a/arch/avr/src/avrdx/iodefs/avr128da64.h +++ b/arch/avr/src/avrdx/iodefs/avr128da64.h @@ -88,6 +88,7 @@ #define PORT_ISC_GM ( PORT_ISC_0_bm | PORT_ISC_1_bm | PORT_ISC_2_bm ) #define PORT_ISC_BOTHEDGES_GC ( PORT_ISC_0_bm ) +#define PORT_ISC_LEVEL_GC ( PORT_ISC_2_bm | PORT_ISC_0_bm ) /* CLKCTRL.MCLKCTRLA */ diff --git a/arch/avr/src/avrdx/iodefs/avr128db64.h b/arch/avr/src/avrdx/iodefs/avr128db64.h index 9fc0effc1ad..6d1b94e3ebc 100644 --- a/arch/avr/src/avrdx/iodefs/avr128db64.h +++ b/arch/avr/src/avrdx/iodefs/avr128db64.h @@ -88,6 +88,7 @@ #define PORT_ISC_GM ( PORT_ISC_0_bm | PORT_ISC_1_bm | PORT_ISC_2_bm ) #define PORT_ISC_BOTHEDGES_GC ( PORT_ISC_0_bm ) +#define PORT_ISC_LEVEL_GC ( PORT_ISC_2_bm | PORT_ISC_0_bm ) /* CLKCTRL.MCLKCTRLA */
