zephyr/drivers/timer/xlnx_psttc_timer.c

197 lines
6.5 KiB
C

/*
* 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 += sys_clock_hw_cycles_per_tick();
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;
}