Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | /*
* 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;
}
|