This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 6b9b216ff303e6ad644b69ffd579c70af1cd2c1f
Author: raiden00pl <[email protected]>
AuthorDate: Wed Nov 4 15:46:48 2026 +0100

    arch/arm/nrf54l: Start the IEEE 802.15.4 ACK at aTurnaroundTime.
    
    The ACK TX was started from two interrupts and went on air late.  The
    802.15.4 timer moves to TIMER10 in the radio domain, RADIO PHYEND starts
    it over DPPIC10 when idle and its ACK compare enables TX.
    
    Assisted-by: Claude Code:claude-fable-5-1
    Signed-off-by: raiden00pl <[email protected]>
---
 arch/arm/src/nrf54l/Kconfig                   |   2 +-
 arch/arm/src/nrf54l/hardware/nrf54l_dppic.h   |  53 +++++++
 arch/arm/src/nrf54l/hardware/nrf54l_radio.h   |   2 +
 arch/arm/src/nrf54l/hardware/nrf54l_tim.h     |   4 +
 arch/arm/src/nrf54l/nrf54l_ieee802154.c       |   1 -
 arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c |  38 ++---
 arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h |   8 +-
 arch/arm/src/nrf54l/nrf54l_ieee802154_tim.c   | 207 ++++++++++++++++++++++----
 arch/arm/src/nrf54l/nrf54l_ieee802154_tim.h   |   8 +
 arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c |   1 -
 arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h |   1 -
 11 files changed, 262 insertions(+), 63 deletions(-)

diff --git a/arch/arm/src/nrf54l/Kconfig b/arch/arm/src/nrf54l/Kconfig
index 803e9e81292..0ec2b38bbb7 100644
--- a/arch/arm/src/nrf54l/Kconfig
+++ b/arch/arm/src/nrf54l/Kconfig
@@ -561,7 +561,7 @@ config NRF54L_RADIO_IEEE802154
        select ARCH_RAMVECTORS
        select ARCH_IRQPRIO
        select NRF54L_RADIO_CUSTOM
-       select NRF54L_TIMER0
+       select NRF54L_TIMER6
 
 if NRF54L_RADIO_IEEE802154
 
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_dppic.h 
b/arch/arm/src/nrf54l/hardware/nrf54l_dppic.h
new file mode 100644
index 00000000000..6eabd8d6488
--- /dev/null
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_dppic.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+ * arch/arm/src/nrf54l/hardware/nrf54l_dppic.h
+ *
+ * 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.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_DPPIC_H
+#define __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_DPPIC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include "hardware/nrf54l_memorymap.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register offsets for DPPIC ***********************************************/
+
+#define NRF54L_DPPIC_CHEN_OFFSET     0x0500 /* Channel enable */
+#define NRF54L_DPPIC_CHENSET_OFFSET  0x0504 /* Channel enable set */
+#define NRF54L_DPPIC_CHENCLR_OFFSET  0x0508 /* Channel enable clear */
+
+/* Register Bitfield Definitions for DPPIC **********************************/
+
+#define DPPIC_CHEN_CH(x)             (1 << (x)) /* Channel x */
+
+/* PUBLISH and SUBSCRIBE registers of all peripherals */
+
+#define DPPI_CHIDX_SHIFT             (0)        /* Bits 0-7: Channel index */
+#define DPPI_CHIDX_MASK              (0xff << DPPI_CHIDX_SHIFT)
+#define DPPI_EN                      (1 << 31)  /* Bit 31: Enable */
+
+#endif /* __ARCH_ARM_SRC_NRF54L_HARDWARE_NRF54L_DPPIC_H */
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_radio.h 
b/arch/arm/src/nrf54l/hardware/nrf54l_radio.h
index 27fb552407a..9214bc6c7fc 100644
--- a/arch/arm/src/nrf54l/hardware/nrf54l_radio.h
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_radio.h
@@ -55,6 +55,7 @@
 #define NRF54L_RADIO_TASKS_CCASTART_OFFSET   0x0028                 /* Start 
the clear channel assessment used in IEEE 802.15.4 mode */
 #define NRF54L_RADIO_TASKS_CCASTOP_OFFSET    0x002c                 /* Stop 
the clear channel assessment */
 #define NRF54L_RADIO_TASKS_SOFTRESET_OFFSET  0x00a4                 /* Reset 
all public registers */
+#define NRF54L_RADIO_SUBSCRIBE_TXEN_OFFSET   0x0100                 /* 
Subscribe to TXEN */
 #define NRF54L_RADIO_EVENTS_READY_OFFSET     0x0200                 /* RADIO 
has ramped up and is ready to be started */
 #define NRF54L_RADIO_EVENTS_TXREADY_OFFSET   0x0204                 /* RADIO 
has ramped up and is ready to be started TX path */
 #define NRF54L_RADIO_EVENTS_RXREADY_OFFSET   0x0208                 /* RADIO 
has ramped up and is ready to be started RX path */
@@ -77,6 +78,7 @@
 #define NRF54L_RADIO_EVENTS_RATEBOOST_OFFSET 0x0250                 /* Ble_LR 
CI field received */
 #define NRF54L_RADIO_EVENTS_MHRMATCH_OFFSET  0x0254                 /* MAC 
header match found */
 #define NRF54L_RADIO_EVENTS_SYNC_OFFSET      0x0258                 /* 
Possible preamble received */
+#define NRF54L_RADIO_PUBLISH_PHYEND_OFFSET   0x031c                 /* Publish 
PHYEND */
 #define NRF54L_RADIO_SHORTS_OFFSET           0x0400                 /* 
Shortcuts between local events and tasks */
 #define NRF54L_RADIO_INTENSET00_OFFSET       0x0488                 /* Enable 
interrupt */
 #define NRF54L_RADIO_INTENCLR00_OFFSET       0x0490                 /* Disable 
interrupt */
diff --git a/arch/arm/src/nrf54l/hardware/nrf54l_tim.h 
b/arch/arm/src/nrf54l/hardware/nrf54l_tim.h
index d8b525b56c9..ddb5931c36e 100644
--- a/arch/arm/src/nrf54l/hardware/nrf54l_tim.h
+++ b/arch/arm/src/nrf54l/hardware/nrf54l_tim.h
@@ -77,6 +77,10 @@
 
 #define TIM_TASKS_CLEAR                    (1 << 0)                /* Bit 0: 
Clear Timer */
 
+/* TASKS_CAPTURE[x] Register */
+
+#define TIM_TASKS_CAPTURE                  (1 << 0)                /* Bit 0: 
Capture Timer value */
+
 /* SHORTS Register */
 
 #define TIM_SHORTS_COMPARE_CLEAR(x)        (1 << (x))              /* Bits 
0-5: */
diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154.c 
b/arch/arm/src/nrf54l/nrf54l_ieee802154.c
index 2061b2c25dd..2b061c0e783 100644
--- a/arch/arm/src/nrf54l/nrf54l_ieee802154.c
+++ b/arch/arm/src/nrf54l/nrf54l_ieee802154.c
@@ -964,7 +964,6 @@ nrf54l_radioi8_register(struct nrf54l_radio_board_s *board)
       return NULL;
     }
 
-  DEBUGASSERT(dev->radio->ops->txstart);
   DEBUGASSERT(dev->radio->ops->notify_noack);
   DEBUGASSERT(dev->radio->ops->ccastart);
   DEBUGASSERT(dev->radio->ops->rxenable);
diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c 
b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c
index 6259773a409..80363287ba9 100644
--- a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c
+++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.c
@@ -156,7 +156,8 @@
  */
 
 #define IEEE802154_ACKTX_INT            (RADIO_INT_PHYEND)
-#define IEEE802154_ACKTX_SHORTS         (RADIO_SHORTS_PHYEND_DISABLE)
+#define IEEE802154_ACKTX_SHORTS         (RADIO_SHORTS_TXREADY_START | \
+                                         RADIO_SHORTS_PHYEND_DISABLE)
 
 /* aUnitBackoffPeriod in us */
 
@@ -188,7 +189,6 @@ static void nrf54l_radioi8_gts_setup(struct 
nrf54l_radioi8_dev_s *dev,
 
 /* Ops */
 
-static void nrf54l_radioi8_txstart(struct nrf54l_radioi8_dev_s *dev);
 static void nrf54l_radioi8_ccastart(struct nrf54l_radioi8_dev_s *dev);
 static void nrf54l_radioi8_notify_noack(struct nrf54l_radioi8_dev_s *dev);
 static int nrf54l_radioi8_rxenable(struct nrf54l_radioi8_dev_s *dev,
@@ -264,7 +264,6 @@ static uint8_t aligned_data(8)
 
 static struct nrf54l_radioi8_radio_ops_s g_radioi8_radio_ops =
 {
-  .txstart      = nrf54l_radioi8_txstart,
   .ccastart     = nrf54l_radioi8_ccastart,
   .notify_noack = nrf54l_radioi8_notify_noack,
   .rxenable     = nrf54l_radioi8_rxenable,
@@ -453,15 +452,16 @@ static void nrf54l_radioi8_ack_transmit(struct 
nrf54l_radioi8_dev_s *dev)
 
   dev->radio->state.state = NRF54L_RADIO_STATE_ACKTX;
 
-  /* Enable TX - start TX is called from timer interrupt */
-
-  NRF54L_RADIO_PUTREG(lower, NRF54L_RADIO_TASKS_TXEN_OFFSET, 1);
+  /* The timer started at PHYEND enables TX on time, enable it now when the
+   * timer was busy or is already behind.
+   */
 
-  /* Run TIMER - TX is handled in timer isr */
+  if (!dev->tim->ops->ackarm(dev))
+    {
+      NRF54L_RADIO_PUTREG(lower, NRF54L_RADIO_TASKS_TXEN_OFFSET, 1);
+    }
 
   nrf54l_radioi8_trace_put(RADIO_TRACE_ACKTX, pending);
-  dev->tim->ops->setup(dev, NRF54L_TIMER_CHAN_ACK,
-                       IEEE802154_ACKIFS_SYMBOLS);
 }
 
 /****************************************************************************
@@ -686,24 +686,6 @@ static void nrf54l_radioi8_dopoll_gts(void *arg)
 }
 #endif  /* NRF54L_GTS_SLOTS > 0 */
 
-/****************************************************************************
- * Name: nrf54l_radioi8_txstart
- *
- * Description:
- *   Start transmission, TX must be armed (TXEN must be set).
- *
- ****************************************************************************/
-
-static void nrf54l_radioi8_txstart(struct nrf54l_radioi8_dev_s *dev)
-{
-  struct nrf54l_radio_dev_s *lower = NULL;
-
-  DEBUGASSERT(dev);
-  lower = dev->radio->lower;
-
-  NRF54L_RADIO_PUTREG(lower, NRF54L_RADIO_TASKS_START_OFFSET, 1);
-}
-
 /****************************************************************************
  * Name: nrf54l_radioi8_ccastart
  *
@@ -2408,6 +2390,8 @@ static void nrf54l_radioi8_state_acktx(struct 
nrf54l_radioi8_dev_s *dev)
 {
   nrf54l_radioi8_trace_put(RADIO_TRACE_IRQ_ACKTX, 0);
 
+  dev->tim->ops->ackdone(dev);
+
   /* Start the TX that waited for the ACK TX, after the IFS */
 
   nrf54l_radioi8_txnext(dev, dev->radio->state.rxlong ?
diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h 
b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h
index 8fac1a60e32..69be1802413 100644
--- a/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h
+++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_radio.h
@@ -60,6 +60,10 @@
 
 #define NRF54L_RADIOI8_RAMPUP_US       (40)
 
+/* Latency from the last bit on air to the RADIO PHYEND event */
+
+#define NRF54L_RADIOI8_PHYEND_US       (23)
+
 /* Air time of a frame with the given PHR: SHR, PHR and PSDU, 32 us/octet */
 
 #define NRF54L_RADIOI8_FRAME_US(phr)   (((phr) + 6) * 2 * IEEE802154_SYMBOL_US)
@@ -148,10 +152,6 @@ struct ieee802154_cca_s;
 
 struct nrf54l_radioi8_radio_ops_s
 {
-  /* Start transmission - TX must be armed (TXEN set) */
-
-  void (*txstart)(struct nrf54l_radioi8_dev_s *dev);
-
   /* Start CCA - RX must be armed (RXEN set) */
 
   void (*ccastart)(struct nrf54l_radioi8_dev_s *dev);
diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.c 
b/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.c
index e545cec0b04..8f05b0ee91c 100644
--- a/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.c
+++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.c
@@ -33,6 +33,9 @@
 #include <nuttx/irq.h>
 #include <nuttx/wqueue.h>
 
+#include "arm_internal.h"
+#include "hardware/nrf54l_dppic.h"
+#include "hardware/nrf54l_tim.h"
 #include "nrf54l_ieee802154_tim.h"
 #include "nrf54l_ieee802154_trace.h"
 
@@ -42,21 +45,31 @@
  * Pre-processor Definitions
  ****************************************************************************/
 
-#ifndef CONFIG_NRF54L_TIMER0
-#  error CONFIG_NRF54L_TIMER0 is needed to handle radio timings
+#ifndef CONFIG_NRF54L_TIMER6
+#  error CONFIG_NRF54L_TIMER6 is needed to handle radio timings
 #endif
 
-/* Timer instance - 0 */
+/* Timer instance - TIMER10 */
 
-#define NRF54L_IEEE802154_TIMER0 (0)
+#define NRF54L_IEEE802154_TIMER10 (6)
 
-/* Timer period set to 16us (symbol duration) */
+/* Timer period set to 1 us, 32 MHz / 32 = 1 MHz */
 
-#define NRF54L_TIMER_FREQUENCY   (1000000 / 16)
+#define NRF54L_TIMER_PRESCALER    NRF54L_TIM_PRE_DIV32
 
-/* 16MHz / (2 ** 8) =  62500 */
+/* The ACK of a received frame starts aTurnaroundTime after its last symbol
+ * (IEEE 802.15.4-2006 7.5.6.4.2).  While the timer is idle, RADIO PHYEND
+ * clears and starts it and it stops at the ACK compare.  For an ACK the
+ * compare enables TX through RADIO TXEN, TXREADY starts the TX.
+ */
 
-#define NRF54L_TIMER_PRESCALER   (8)
+#define NRF54L_TIMER_BASE         NRF54L_TIMER10_BASE
+#define NRF54L_TIMER_ACKTXEN_US   (IEEE802154_TURN_AROUND_TIME * \
+                                   IEEE802154_SYMBOL_US - \
+                                   NRF54L_RADIOI8_RAMPUP_US - \
+                                   NRF54L_RADIOI8_PHYEND_US)
+#define NRF54L_TIMER_LINK_PHYEND  (14)
+#define NRF54L_TIMER_LINK_ACKTXEN (15)
 
 /****************************************************************************
  * Private Function Prototypes
@@ -68,6 +81,8 @@ static int nrf54l_radioi8_tim(struct nrf54l_radioi8_dev_s 
*dev, uint8_t chan,
                              uint32_t val);
 static void nrf54l_radioi8_tim_stop(struct nrf54l_radioi8_dev_s *dev);
 static void nrf54l_radioi8_tim_reset(struct nrf54l_radioi8_dev_s *dev);
+static bool nrf54l_radioi8_tim_ackarm(struct nrf54l_radioi8_dev_s *dev);
+static void nrf54l_radioi8_tim_ackdone(struct nrf54l_radioi8_dev_s *dev);
 
 /* Interrupts logic */
 
@@ -82,9 +97,11 @@ static int nrf54l_radioi8_isr_tim(int irq, void *context, 
void *arg);
 
 static struct nrf54l_radioi8_tim_ops_s g_radioi8_tim_ops =
 {
-  .setup = nrf54l_radioi8_tim,
-  .stop  = nrf54l_radioi8_tim_stop,
-  .reset = nrf54l_radioi8_tim_reset
+  .setup   = nrf54l_radioi8_tim,
+  .stop    = nrf54l_radioi8_tim_stop,
+  .reset   = nrf54l_radioi8_tim_reset,
+  .ackarm  = nrf54l_radioi8_tim_ackarm,
+  .ackdone = nrf54l_radioi8_tim_ackdone
 };
 
 /* Timer instance */
@@ -95,6 +112,54 @@ static struct nrf54l_radioi8_tim_s g_radioi8_tim;
  * Private Function
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: nrf54l_radioi8_tim_count
+ *
+ * Description:
+ *   Capture the timer, the TX delay channel is free when the timer is idle.
+ *
+ ****************************************************************************/
+
+static uint32_t nrf54l_radioi8_tim_count(void)
+{
+  putreg32(TIM_TASKS_CAPTURE, NRF54L_TIMER_BASE +
+           NRF54L_TIM_TASKS_CAPTURE_OFFSET(NRF54L_TIMER_CHAN_TXDELAY));
+
+  return getreg32(NRF54L_TIMER_BASE +
+                  NRF54L_TIM_CC_OFFSET(NRF54L_TIMER_CHAN_TXDELAY));
+}
+
+/****************************************************************************
+ * Name: nrf54l_radioi8_tim_idle
+ *
+ * Description:
+ *   Let RADIO PHYEND start the idle timer, or keep it for a timer event.
+ *
+ ****************************************************************************/
+
+static void nrf54l_radioi8_tim_idle(struct nrf54l_radioi8_dev_s *dev,
+                                    bool idle)
+{
+  struct nrf54l_radioi8_tim_s *tim = dev->tim;
+
+  if (idle)
+    {
+      NRF54L_TIM_STOP(tim->tim);
+      NRF54L_TIM_CLEAR(tim->tim);
+      NRF54L_TIM_SHORTS(tim->tim, NRF54L_TIM_SHORT_COMPARE_STOP,
+                        NRF54L_TIMER_CHAN_ACK, true);
+      putreg32(DPPIC_CHEN_CH(NRF54L_TIMER_LINK_PHYEND),
+               NRF54L_DPPIC10_BASE + NRF54L_DPPIC_CHENSET_OFFSET);
+    }
+  else
+    {
+      putreg32(DPPIC_CHEN_CH(NRF54L_TIMER_LINK_PHYEND),
+               NRF54L_DPPIC10_BASE + NRF54L_DPPIC_CHENCLR_OFFSET);
+      NRF54L_TIM_SHORTS(tim->tim, NRF54L_TIM_SHORT_COMPARE_STOP,
+                        NRF54L_TIMER_CHAN_ACK, false);
+    }
+}
+
 /****************************************************************************
  * Name: nrf54l_radioi8_tim
  *
@@ -123,6 +188,7 @@ static int nrf54l_radioi8_tim(struct nrf54l_radioi8_dev_s 
*dev, uint8_t chan,
 
   /* Stop timer and clear the counter */
 
+  nrf54l_radioi8_tim_idle(dev, false);
   NRF54L_TIM_STOP(tim->tim);
   NRF54L_TIM_CLEAR(tim->tim);
 
@@ -132,7 +198,7 @@ static int nrf54l_radioi8_tim(struct nrf54l_radioi8_dev_s 
*dev, uint8_t chan,
 
   /* Set compare register */
 
-  NRF54L_TIM_SETCC(tim->tim, chan, val);
+  NRF54L_TIM_SETCC(tim->tim, chan, val * IEEE802154_SYMBOL_US);
 
   /* Configure interrupt */
 
@@ -187,6 +253,8 @@ static void nrf54l_radioi8_tim_stop(struct 
nrf54l_radioi8_dev_s *dev)
 
   dev->tim->tim_pending = false;
   dev->tim->tim_now     = -1;
+
+  nrf54l_radioi8_tim_idle(dev, true);
 }
 
 /****************************************************************************
@@ -199,49 +267,130 @@ static void nrf54l_radioi8_tim_stop(struct 
nrf54l_radioi8_dev_s *dev)
 
 static void nrf54l_radioi8_tim_reset(struct nrf54l_radioi8_dev_s *dev)
 {
-  /* Configure TIMER - freq = 62500 */
+  /* Configure TIMER - freq = 1 MHz */
 
   NRF54L_TIM_STOP(dev->tim->tim);
   NRF54L_TIM_CONFIGURE(dev->tim->tim, NRF54L_TIM_MODE_TIMER,
                       NRF54L_TIM_WIDTH_32B);
   NRF54L_TIM_SETPRE(dev->tim->tim, NRF54L_TIMER_PRESCALER);
+  NRF54L_TIM_SETCC(dev->tim->tim, NRF54L_TIMER_CHAN_ACK,
+                 NRF54L_TIMER_ACKTXEN_US);
+
+  /* RADIO PHYEND starts the timer, the ACK compare enables TX */
+
+  putreg32(DPPI_EN | NRF54L_TIMER_LINK_PHYEND,
+           NRF54L_TIMER_BASE + NRF54L_TIM_SUBSCRIBE_CLEAR_OFFSET);
+  putreg32(DPPI_EN | NRF54L_TIMER_LINK_PHYEND,
+           NRF54L_TIMER_BASE + NRF54L_TIM_SUBSCRIBE_START_OFFSET);
+  putreg32(DPPI_EN | NRF54L_TIMER_LINK_ACKTXEN,
+           NRF54L_TIMER_BASE +
+           NRF54L_TIM_PUBLISH_COMPARE_OFFSET(NRF54L_TIMER_CHAN_ACK));
+  NRF54L_RADIO_PUTREG(dev->radio->lower, NRF54L_RADIO_PUBLISH_PHYEND_OFFSET,
+                      DPPI_EN | NRF54L_TIMER_LINK_PHYEND);
+  NRF54L_RADIO_PUTREG(dev->radio->lower, NRF54L_RADIO_SUBSCRIBE_TXEN_OFFSET,
+                      DPPI_EN | NRF54L_TIMER_LINK_ACKTXEN);
+
+  putreg32(DPPIC_CHEN_CH(NRF54L_TIMER_LINK_ACKTXEN),
+           NRF54L_DPPIC10_BASE + NRF54L_DPPIC_CHENCLR_OFFSET);
 
   /* Reset state */
 
   dev->tim->tim_pending = false;
   dev->tim->tim_now     = -1;
+
+  nrf54l_radioi8_tim_idle(dev, true);
 }
 
 /****************************************************************************
- * Name: nrf54l_radioi8_tim_expire
+ * Name: nrf54l_radioi8_tim_ackarm
  *
  * Description:
- *   Handle the expired timer channel.
+ *   Let the timer enable the ACK TX.  It runs since PHYEND only when it was
+ *   idle, and it must not have reached the ACK compare yet.
  *
  ****************************************************************************/
 
-static void nrf54l_radioi8_tim_expire(struct nrf54l_radioi8_dev_s *dev)
+static bool nrf54l_radioi8_tim_ackarm(struct nrf54l_radioi8_dev_s *dev)
 {
   struct nrf54l_radioi8_tim_s *tim = dev->tim;
-  irqstate_t                  flags;
+  bool armed = false;
+  uint32_t count;
+  irqstate_t flags;
 
   flags = enter_critical_section();
 
-  switch (tim->tim_now)
+  count = nrf54l_radioi8_tim_count();
+  if (!tim->tim_pending && count > 0 && count < NRF54L_TIMER_ACKTXEN_US)
     {
-      /* RX ACK handler */
+      putreg32(DPPIC_CHEN_CH(NRF54L_TIMER_LINK_ACKTXEN),
+               NRF54L_DPPIC10_BASE + NRF54L_DPPIC_CHENSET_OFFSET);
+
+      /* The compare may pass while the channel is enabled */
 
-      case NRF54L_TIMER_CHAN_ACK:
+      if (nrf54l_radioi8_tim_count() < NRF54L_TIMER_ACKTXEN_US ||
+          (NRF54L_RADIO_GETREG(dev->radio->lower,
+                               NRF54L_RADIO_STATE_OFFSET) &
+           RADIO_STATE_MASK) != RADIO_STATE_DISABLED)
         {
-          nrf54l_radioi8_trace_put(RADIO_TRACE_IRQ_TIMACKTX, 0);
+          tim->tim_pending = true;
+          tim->tim_now     = NRF54L_TIMER_CHAN_ACK;
+          armed            = true;
+        }
+      else
+        {
+          putreg32(DPPIC_CHEN_CH(NRF54L_TIMER_LINK_ACKTXEN),
+                   NRF54L_DPPIC10_BASE + NRF54L_DPPIC_CHENCLR_OFFSET);
+        }
+    }
 
-          /* Start TX */
+  leave_critical_section(flags);
+  return armed;
+}
 
-          dev->radio->ops->txstart(dev);
+/****************************************************************************
+ * Name: nrf54l_radioi8_tim_ackdone
+ *
+ * Description:
+ *   The ACK TX is done, the timer is idle again.
+ *
+ ****************************************************************************/
 
-          break;
-        }
+static void nrf54l_radioi8_tim_ackdone(struct nrf54l_radioi8_dev_s *dev)
+{
+  irqstate_t flags;
+
+  flags = enter_critical_section();
+
+  putreg32(DPPIC_CHEN_CH(NRF54L_TIMER_LINK_ACKTXEN),
+           NRF54L_DPPIC10_BASE + NRF54L_DPPIC_CHENCLR_OFFSET);
+
+  if (dev->tim->tim_now == NRF54L_TIMER_CHAN_ACK)
+    {
+      dev->tim->tim_pending = false;
+      dev->tim->tim_now     = -1;
+      nrf54l_radioi8_tim_idle(dev, true);
+    }
+
+  leave_critical_section(flags);
+}
 
+/****************************************************************************
+ * Name: nrf54l_radioi8_tim_expire
+ *
+ * Description:
+ *   Handle the expired timer channel.
+ *
+ ****************************************************************************/
+
+static void nrf54l_radioi8_tim_expire(struct nrf54l_radioi8_dev_s *dev)
+{
+  struct nrf54l_radioi8_tim_s *tim = dev->tim;
+  irqstate_t                  flags;
+
+  flags = enter_critical_section();
+
+  switch (tim->tim_now)
+    {
       /* Delayed TX handler */
 
       case NRF54L_TIMER_CHAN_TXDELAY:
@@ -302,6 +451,8 @@ static void nrf54l_radioi8_tim_expire(struct 
nrf54l_radioi8_dev_s *dev)
   tim->tim_pending = false;
   tim->tim_now     = -1;
 
+  nrf54l_radioi8_tim_idle(dev, true);
+
   leave_critical_section(flags);
 }
 
@@ -354,12 +505,12 @@ nrf54l_radioi8_tim_init(struct nrf54l_radioi8_dev_s *dev)
 {
   struct nrf54l_tim_dev_s *tim = NULL;
 
-  /* Reserve TIMER0 */
+  /* Reserve TIMER10, RADIO PHYEND reaches it over DPPIC10 */
 
-  tim = nrf54l_tim_init(NRF54L_IEEE802154_TIMER0);
+  tim = nrf54l_tim_init(NRF54L_IEEE802154_TIMER10);
   if (tim == NULL)
     {
-      wlerr("nrf54l_tim_init(0) failed %d\n", -errno);
+      wlerr("nrf54l_tim_init(6) failed %d\n", -errno);
       return NULL;
     }
 
diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.h 
b/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.h
index b0b56fcbaec..52cca3e9ece 100644
--- a/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.h
+++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_tim.h
@@ -67,6 +67,14 @@ struct nrf54l_radioi8_tim_ops_s
   /* Reset timer */
 
   void (*reset)(struct nrf54l_radioi8_dev_s *dev);
+
+  /* Let the timer started at PHYEND enable the ACK TX */
+
+  bool (*ackarm)(struct nrf54l_radioi8_dev_s *dev);
+
+  /* ACK TX done */
+
+  void (*ackdone)(struct nrf54l_radioi8_dev_s *dev);
 };
 
 /* Timer interface */
diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c 
b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c
index 0503f2d847d..b1dd0bd07b2 100644
--- a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c
+++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.c
@@ -52,7 +52,6 @@ static const char *g_radio_trace_str[] =
   "RADIO IRQ: TX CCA BUSY",
   "RADIO IRQ: BEACON TX",
 
-  "TIM IRQ: TIM ACK TX",
   "TIM IRQ; TIM TX DELAY",
   "TIM IRQ: TIM WAIT ACK",
   "TIM IRQ: TIM CSMA DELAY",
diff --git a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h 
b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h
index 190ad6a2762..c192009c1f3 100644
--- a/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h
+++ b/arch/arm/src/nrf54l/nrf54l_ieee802154_trace.h
@@ -63,7 +63,6 @@ enum radio_trace_type_e
 
   /* Timer interrupts */
 
-  RADIO_TRACE_IRQ_TIMACKTX,
   RADIO_TRACE_IRQ_TIMTXDELAY,
   RADIO_TRACE_IRQ_TIMWAITACK,
   RADIO_TRACE_IRQ_TIMCSMADELAY,

Reply via email to