timer: Add Xilinx ZynqMP PS ttc timer
Add Xilinx PS ttc timer for Xilinx ZynqMP platform.
Signed-off-by: Wendy Liang <wendy.liang@xilinx.com>
diff --git a/CODEOWNERS b/CODEOWNERS
index e1bf0e5..32fc145 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -165,6 +165,7 @@
/drivers/timer/altera_avalon_timer_hal.c @wentongwu
/drivers/timer/riscv_machine_timer.c @nategraff-sifive @kgugala @pgielda
/drivers/timer/litex_timer.c @mateusz-holenko @kgugala @pgielda
+/drivers/timer/xlnx_psttc_timer.c @wjliang
/drivers/usb/ @jfischer-phytec-iot @finikorg
/drivers/usb/device/usb_dc_stm32.c @ydamigos @loicpoulain
/drivers/i2c/i2c_ll_stm32* @ldts @ydamigos
diff --git a/drivers/timer/CMakeLists.txt b/drivers/timer/CMakeLists.txt
index ef8cb2b..b124c6f 100644
--- a/drivers/timer/CMakeLists.txt
+++ b/drivers/timer/CMakeLists.txt
@@ -15,3 +15,4 @@
zephyr_sources_if_kconfig( sam0_rtc_timer.c)
zephyr_sources_if_kconfig( litex_timer.c)
zephyr_sources_if_kconfig( mchp_xec_rtos_timer.c)
+zephyr_sources_if_kconfig( xlnx_psttc_timer.c)
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index 90b0a70..7c68071 100644
--- a/drivers/timer/Kconfig
+++ b/drivers/timer/Kconfig
@@ -241,6 +241,23 @@
XEC series RTOS timer and provides the standard "system clock
driver" interfaces.
+config XLNX_PSTTC_TIMER
+ bool "Xilinx PS ttc timer support"
+ default y
+ depends on SOC_XILINX_ZYNQMP
+ help
+ This module implements a kernel device driver for the Xilinx ZynqMP
+ platform provides the standard "system clock driver" interfaces.
+ If unchecked, no timer will be used.
+
+config XLNX_PSTTC_TIMER_INDEX
+ int "Xilinx PS ttc timer index"
+ range 0 3
+ default 0
+ depends on XLNX_PSTTC_TIMER
+ help
+ This is the index of TTC timer picked to provide system clock.
+
config SYSTEM_CLOCK_DISABLE
bool "API to disable system clock"
help
diff --git a/drivers/timer/xlnx_psttc_timer.c b/drivers/timer/xlnx_psttc_timer.c
new file mode 100644
index 0000000..5ec46ab
--- /dev/null
+++ b/drivers/timer/xlnx_psttc_timer.c
@@ -0,0 +1,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 += 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;
+}
diff --git a/dts/bindings/timer/xlnx,ttcps.yaml b/dts/bindings/timer/xlnx,ttcps.yaml
new file mode 100644
index 0000000..dc2d352
--- /dev/null
+++ b/dts/bindings/timer/xlnx,ttcps.yaml
@@ -0,0 +1,24 @@
+---
+title: Xilinx ZynqMP PS TTC TIMERS
+
+description: >
+ This binding gives a base representation of the Xilinx ZynqMP PS TTC TIMERS
+
+inherits:
+ !include base.yaml
+
+properties:
+ compatible:
+ constraint: "cdns,ttc"
+
+ label:
+ category: required
+
+ reg:
+ category: required
+
+ clock-frequency:
+ type: int
+ category: optional
+ description: Clock frequency information for Timer operation
+...
diff --git a/tests/kernel/context/src/main.c b/tests/kernel/context/src/main.c
index 14b96db..d4cca0f 100644
--- a/tests/kernel/context/src/main.c
+++ b/tests/kernel/context/src/main.c
@@ -67,6 +67,8 @@
#define TICK_IRQ DT_LITEX_TIMER0_E0002800_IRQ_0
#elif defined(CONFIG_RV32M1_LPTMR_TIMER)
#define TICK_IRQ DT_OPENISA_RV32M1_LPTMR_SYSTEM_LPTMR_IRQ_0
+#elif defined(CONFIG_XLNX_PSTTC_TIMER)
+#define TICK_IRQ DT_INST_0_CDNS_TTC_IRQ_0
#elif defined(CONFIG_CPU_CORTEX_M)
/*
* The Cortex-M use the SYSTICK exception for the system timer, which is