text: /*
   * Copyright (c) 2018 Xilinx, Inc.
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <drivers/timer/system_timer.h>
  #include <sys_clock.h>
  #include "irq.h"
  #include "legacy_api.h"
  
  #define TIMER_FREQ          CONFIG_SYS_CLOCK_TICKS_PER_SEC
  
  #if (CONFIG_XLNX_PSTTC_TIMER_INDEX == 0)
  #define TIMER_INPUT_CLKHZ   DT_INST_0_CDNS_TTC_CLOCK_FREQUENCY
  #define TIMER_IRQ           DT_INST_0_CDNS_TTC_IRQ_0
  #define TIMER_BASEADDR      DT_INST_0_CDNS_TTC_BASE_ADDRESS
  #else
  #error ("No timer is specified")
  #endif
  
  #define XTTCPS_CLK_CNTRL_OFFSET	   0x00000000U  /**< Clock Control Register */
  #define XTTCPS_CNT_CNTRL_OFFSET	   0x0000000CU  /**< Counter Control Register*/
  #define XTTCPS_COUNT_VALUE_OFFSET  0x00000018U  /**< Current Counter Value */
  #define XTTCPS_INTERVAL_VAL_OFFSET 0x00000024U  /**< Interval Count Value */
  #define XTTCPS_MATCH_0_OFFSET	   0x00000030U  /**< Match 1 value */
  #define XTTCPS_MATCH_1_OFFSET	   0x0000003CU  /**< Match 2 value */
  #define XTTCPS_MATCH_2_OFFSET	   0x00000048U  /**< Match 3 value */
  #define XTTCPS_ISR_OFFSET	   0x00000054U  /**< Interrupt Status Register */
  #define XTTCPS_IER_OFFSET	   0x00000060U  /**< Interrupt Enable Register */
  
  /* Clock Control Register definitions */
  #define XTTCPS_CLK_CNTRL_PS_EN_MASK	0x00000001U  /**< Prescale enable */
  #define XTTCPS_CLK_CNTRL_PS_VAL_MASK	0x0000001EU  /**< Prescale value */
  #define XTTCPS_CLK_CNTRL_PS_VAL_SHIFT	1U  /**< Prescale shift */
  #define XTTCPS_CLK_CNTRL_PS_DISABLE	16U  /**< Prescale disable */
  #define XTTCPS_CLK_CNTRL_SRC_MASK	0x00000020U  /**< Clock source */
  #define XTTCPS_CLK_CNTRL_EXT_EDGE_MASK	0x00000040U  /**< External Clock edge */
  
  /* Counter Control Register definitions */
  #define XTTCPS_CNT_CNTRL_DIS_MASK	0x00000001U /**< Disable the counter */
  #define XTTCPS_CNT_CNTRL_INT_MASK	0x00000002U /**< Interval mode */
  #define XTTCPS_CNT_CNTRL_DECR_MASK	0x00000004U /**< Decrement mode */
  #define XTTCPS_CNT_CNTRL_MATCH_MASK	0x00000008U /**< Match mode */
  #define XTTCPS_CNT_CNTRL_RST_MASK	0x00000010U /**< Reset counter */
  #define XTTCPS_CNT_CNTRL_EN_WAVE_MASK	0x00000020U /**< Enable waveform */
  #define XTTCPS_CNT_CNTRL_POL_WAVE_MASK	0x00000040U /**< Waveform polarity */
  #define XTTCPS_CNT_CNTRL_RESET_VALUE	0x00000021U /**< Reset value */
  
  /* Interrupt register masks */
  #define XTTCPS_IXR_INTERVAL_MASK	0x00000001U  /**< Interval Interrupt */
  #define XTTCPS_IXR_MATCH_0_MASK		0x00000002U  /**< Match 1 Interrupt */
  #define XTTCPS_IXR_MATCH_1_MASK		0x00000004U  /**< Match 2 Interrupt */
  #define XTTCPS_IXR_MATCH_2_MASK		0x00000008U  /**< Match 3 Interrupt */
  #define XTTCPS_IXR_CNT_OVR_MASK		0x00000010U  /**< Counter Overflow */
  #define XTTCPS_IXR_ALL_MASK		0x0000001FU  /**< All valid Interrupts */
  
  #define XTTC_MAX_INTERVAL_COUNT 0xFFFFFFFFU /**< Maximum value of interval counter */
  
  static u32_t accumulated_cycles;
  static s32_t _sys_idle_elapsed_ticks = 1;
  
  static int xttc_calculate_interval(u32_t *interval, u8_t *prescaler)
  {
  	u32_t tmpinterval = 0;
  	u8_t tmpprescaler = 0;
  	unsigned int tmpval;
  
  	tmpval = (u32_t)(TIMER_INPUT_CLKHZ / TIMER_FREQ);
  
  	if (tmpval < (u32_t)65536U) {
  		/* no prescaler is required */
  		tmpinterval = tmpval;
  		tmpprescaler = 0;
  	} else {
  		for (tmpprescaler = 1U; tmpprescaler < 16; tmpprescaler++) {
  			tmpval = (u32_t)(TIMER_INPUT_CLKHZ /
  					 (TIMER_FREQ * (1U << tmpprescaler)));
  			if (tmpval < (u32_t)65536U) {
  				tmpinterval = tmpval;
  				break;
  			}
  		}
  	}
  
  	if (tmpinterval != 0) {
  		*interval = tmpinterval;
  		*prescaler = tmpprescaler;
  		return 0;
  	}
  
  	/* TBD: Is there a way to adjust the sys clock parameters such as
  	 * ticks per sec if it failed to configure the timer as specified
  	 */
  	return -EINVAL;
  }
  
  /**
   * @brief System timer tick handler
   *
   * This routine handles the system clock tick interrupt. A TICK_EVENT event
   * is pushed onto the kernel stack.
   *
   * The symbol for this routine is either _timer_int_handler.
   *
   * @return N/A
   */
  void _timer_int_handler(void *unused)
  {
  	ARG_UNUSED(unused);
  
  	u32_t regval;
  
  	regval = sys_read32(TIMER_BASEADDR + XTTCPS_ISR_OFFSET);
  	accumulated_cycles += k_ticks_to_cyc_floor32(1);
  	z_clock_announce(_sys_idle_elapsed_ticks);
  }
  
  /**
   * @brief Initialize and enable the system clock
   *
   * This routine is used to program the systick to deliver interrupts at the
   * rate specified via the 'sys_clock_us_per_tick' global variable.
   *
   * @return 0
   */
  int z_clock_driver_init(struct device *device)
  {
  	int ret;
  	u32_t interval;
  	u8_t prescaler;
  	u32_t regval;
  
  	/* Stop timer */
  	sys_write32(XTTCPS_CNT_CNTRL_DIS_MASK,
  		    TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  
  	/* Calculate prescaler */
  	ret = xttc_calculate_interval(&interval, &prescaler);
  	if (ret < 0) {
  		printk("Failed to calculate prescaler.\n");
  		return ret;
  	}
  
  	/* Reset registers */
  	sys_write32(XTTCPS_CNT_CNTRL_RESET_VALUE,
  		    TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  	sys_write32(0, TIMER_BASEADDR + XTTCPS_CLK_CNTRL_OFFSET);
  	sys_write32(0, TIMER_BASEADDR + XTTCPS_INTERVAL_VAL_OFFSET);
  	sys_write32(0, TIMER_BASEADDR + XTTCPS_MATCH_0_OFFSET);
  	sys_write32(0, TIMER_BASEADDR + XTTCPS_MATCH_1_OFFSET);
  	sys_write32(0, TIMER_BASEADDR + XTTCPS_MATCH_2_OFFSET);
  	sys_write32(0, TIMER_BASEADDR + XTTCPS_IER_OFFSET);
  	sys_write32(XTTCPS_IXR_ALL_MASK, TIMER_BASEADDR + XTTCPS_ISR_OFFSET);
  	/* Reset counter value */
  	regval = sys_read32(TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  	regval |= XTTCPS_CNT_CNTRL_RST_MASK;
  	sys_write32(regval, TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  
  	/* Set options */
  	regval = sys_read32(TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  	regval |= XTTCPS_CNT_CNTRL_INT_MASK;
  	sys_write32(regval, TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  
  	/* Set interval and prescaller */
  	sys_write32(interval, TIMER_BASEADDR + XTTCPS_INTERVAL_VAL_OFFSET);
  	regval = (u32_t)((prescaler & 0xFU) << 1);
  	sys_write32(regval, TIMER_BASEADDR + XTTCPS_CLK_CNTRL_OFFSET);
  
  	/* Enable timer interrupt */
  	IRQ_CONNECT(TIMER_IRQ, 0, _timer_int_handler, 0, 0);
  	irq_enable(TIMER_IRQ);
  	regval = sys_read32(TIMER_BASEADDR + XTTCPS_IER_OFFSET);
  	regval |= XTTCPS_IXR_INTERVAL_MASK;
  	sys_write32(regval, TIMER_BASEADDR + XTTCPS_IER_OFFSET);
  
  	/* Start timer */
  	regval = sys_read32(TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  	regval &= (~XTTCPS_CNT_CNTRL_DIS_MASK);
  	sys_write32(regval, TIMER_BASEADDR + XTTCPS_CNT_CNTRL_OFFSET);
  
  	return 0;
  }
  
  
  /**
   * @brief Read the platform's timer hardware
   *
   * This routine returns the current time in terms of timer hardware clock
   * cycles.
   *
   * @return up counter of elapsed clock cycles
   */
  u32_t z_timer_cycle_get_32(void)
  {
  	return accumulated_cycles;
  }
  