text: /*
   * Copyright (c) 2021 IoT.bzh
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #ifndef ZEPHYR_INCLUDE_ARCH_ARM_AARCH32_CORTEX_A_R_TIMER_H_
  #define ZEPHYR_INCLUDE_ARCH_ARM_AARCH32_CORTEX_A_R_TIMER_H_
  
  #ifdef CONFIG_ARM_ARCH_TIMER
  
  #ifndef _ASMLANGUAGE
  
  #include <drivers/timer/arm_arch_timer.h>
  #include <sys/device_mmio.h>
  #include <zephyr/types.h>
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  #define ARM_ARCH_TIMER_BASE     DT_REG_ADDR_BY_IDX(ARM_TIMER_NODE, 0)
  #define ARM_ARCH_TIMER_IRQ      ARM_TIMER_VIRTUAL_IRQ
  #define ARM_ARCH_TIMER_PRIO     ARM_TIMER_VIRTUAL_PRIO
  #define ARM_ARCH_TIMER_FLAGS    ARM_TIMER_VIRTUAL_FLAGS
  
  #define TIMER_CNT_LOWER         0x00
  #define TIMER_CNT_UPPER         0x04
  #define TIMER_CTRL              0x08
  #define TIMER_ISR               0x0c
  #define TIMER_CMP_LOWER         0x10
  #define TIMER_CMP_UPPER         0x14
  
  #define TIMER_IRQ_ENABLE        BIT(2)
  #define TIMER_COMP_ENABLE       BIT(1)
  #define TIMER_ENABLE            BIT(0)
  
  #define TIMER_ISR_EVENT_FLAG	BIT(0)
  
  DEVICE_MMIO_TOPLEVEL_STATIC(timer_regs, ARM_TIMER_NODE);
  
  #define TIMER_REG_GET(offs) (DEVICE_MMIO_TOPLEVEL_GET(timer_regs) + offs)
  
  static ALWAYS_INLINE void arm_arch_timer_init(void)
  {
  	DEVICE_MMIO_TOPLEVEL_MAP(timer_regs, K_MEM_CACHE_NONE);
  }
  
  static ALWAYS_INLINE void arm_arch_timer_set_compare(uint64_t val)
  {
  	uint32_t lower = (uint32_t)val;
  	uint32_t upper = (uint32_t)(val >> 32);
  	uint32_t ctrl;
  
  	/* Disable IRQ and comparator */
  	ctrl = sys_read32(TIMER_REG_GET(TIMER_CTRL));
  	ctrl &= ~(TIMER_COMP_ENABLE | TIMER_IRQ_ENABLE);
  	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
  
  	sys_write32(lower, TIMER_REG_GET(TIMER_CMP_LOWER));
  	sys_write32(upper, TIMER_REG_GET(TIMER_CMP_UPPER));
  
  	/* enable comparator back, let set_irq_mask enabling the IRQ again */
  	ctrl |= TIMER_COMP_ENABLE;
  	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
  }
  
  #if defined(CONFIG_ARM_ARCH_TIMER_ERRATUM_740657)
  
  /*
   * R/W access to the event flag register is required for the timer errata
   * 740657 workaround -> comp. ISR implementation in arm_arch_timer.c.
   * This functionality is not present in the aarch64 implementation of the
   * ARM global timer access functions.
   *
   * comp. ARM Cortex-A9 processors Software Developers Errata Notice,
   * ARM document ID032315.
   */
  
  static ALWAYS_INLINE uint8_t arm_arch_timer_get_int_status(void)
  {
  	return (uint8_t)(sys_read32(TIMER_REG_GET(TIMER_ISR)) & TIMER_ISR_EVENT_FLAG);
  }
  
  static ALWAYS_INLINE void arm_arch_timer_clear_int_status(void)
  {
  	sys_write32(TIMER_ISR_EVENT_FLAG, TIMER_REG_GET(TIMER_ISR));
  }
  
  #endif /* CONFIG_ARM_ARCH_TIMER_ERRATUM_740657 */
  
  static ALWAYS_INLINE void arm_arch_timer_enable(bool enable)
  {
  	uint32_t ctrl;
  
  	ctrl = sys_read32(TIMER_REG_GET(TIMER_CTRL));
  	if (enable) {
  		ctrl |= TIMER_ENABLE;
  	} else {
  		ctrl &= ~TIMER_ENABLE;
  	}
  
  	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
  }
  
  static ALWAYS_INLINE void arm_arch_timer_set_irq_mask(bool mask)
  {
  	uint32_t ctrl;
  
  	ctrl = sys_read32(TIMER_REG_GET(TIMER_CTRL));
  	if (mask) {
  		ctrl &= ~TIMER_IRQ_ENABLE;
  	} else {
  		ctrl |= TIMER_IRQ_ENABLE;
  		sys_write32(1, TIMER_REG_GET(TIMER_ISR));
  	}
  	sys_write32(ctrl, TIMER_REG_GET(TIMER_CTRL));
  }
  
  static ALWAYS_INLINE uint64_t arm_arch_timer_count(void)
  {
  	uint32_t lower;
  	uint32_t upper, upper_saved;
  
  	/* To get the value from the Global Timer Counter register proceed
  	 * as follows:
  	 * 1. Read the upper 32-bit timer counter register.
  	 * 2. Read the lower 32-bit timer counter register.
  	 * 3. Read the upper 32-bit timer counter register again. If the value
  	 * is different to the 32-bit upper value read previously,
  	 * go back to step 2.
  	 * Otherwise the 64-bit timer counter value is correct.
  	 */
  	upper = sys_read32(TIMER_REG_GET(TIMER_CNT_UPPER));
  	do {
  		upper_saved = upper;
  		lower = sys_read32(TIMER_REG_GET(TIMER_CNT_LOWER));
  		upper = sys_read32(TIMER_REG_GET(TIMER_CNT_UPPER));
  	} while (upper != upper_saved);
  
  	return ((uint64_t)upper) << 32 | lower;
  }
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif  /* _ASMLANGUAGE */
  
  #endif /* CONFIG_ARM_ARCH_TIMER */
  
  #endif  /* ZEPHYR_INCLUDE_ARCH_ARM_AARCH32_CORTEX_A_R_TIMER_H_ */
  