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 */
 

Reply via email to