summaryrefslogtreecommitdiffstats
path: root/bsps/shared/grlib/btimer
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2018-12-22 18:31:04 +0100
committerSebastian Huber <sebastian.huber@embedded-brains.de>2019-01-22 12:46:33 +0100
commit7eb606d393306da25fd6e6aa7f8595ffb2e924fc (patch)
tree085befd6fe5e29d229fec9683735516d48e9d41e /bsps/shared/grlib/btimer
parentgrlib: Move header files (diff)
downloadrtems-7eb606d393306da25fd6e6aa7f8595ffb2e924fc.tar.bz2
grlib: Move source files
Update #3678.
Diffstat (limited to 'bsps/shared/grlib/btimer')
-rw-r--r--bsps/shared/grlib/btimer/gptimer.c545
-rw-r--r--bsps/shared/grlib/btimer/tlib.c77
-rw-r--r--bsps/shared/grlib/btimer/tlib_ckinit.c442
3 files changed, 1064 insertions, 0 deletions
diff --git a/bsps/shared/grlib/btimer/gptimer.c b/bsps/shared/grlib/btimer/gptimer.c
new file mode 100644
index 0000000000..4b3ec8c4b8
--- /dev/null
+++ b/bsps/shared/grlib/btimer/gptimer.c
@@ -0,0 +1,545 @@
+/* This file contains the driver for the GRLIB GPTIMER timers port. The driver
+ * is implemented by using the tlib.c simple timer layer and the Driver
+ * Manager.
+ *
+ * The Driver can be configured using driver resources:
+ *
+ * - timerStart Timer Index if first Timer, this parameters is typically used
+ * in AMP systems for resource allocation. The Timers before
+ * timerStart will not be accessed.
+ * - timerCnt Number of timers that the driver will use, this parameters is
+ * typically used in AMP systems for resource allocation between
+ * OS instances.
+ * - prescaler Base prescaler, normally set by bootloader but can be
+ * overridden. The default scaler reload value set by bootloader
+ * is so that Timers operate in 1MHz. Setting the prescaler to a
+ * lower value increase the accuracy of the timers but shortens
+ * the time until underflow happens.
+ * - clockTimer Used to select a particular timer to be the system clock
+ * timer. This is useful when multiple GPTIMERs cores are
+ * available, or in AMP systems. By default the TLIB selects the
+ * first timer registered as system clock timer.
+ *
+ * The BSP define APBUART_INFO_AVAIL in order to add the info routine
+ * used for debugging.
+ *
+ * COPYRIGHT (c) 2010.
+ * Cobham Gaisler AB.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <bsp.h>
+#include <stdlib.h>
+#include <string.h>
+#include <drvmgr/drvmgr.h>
+#include <grlib/ambapp_bus.h>
+#include <grlib/grlib.h>
+#include <grlib/gptimer.h>
+#include <grlib/tlib.h>
+
+#if defined(LEON3)
+#include <leon.h>
+#endif
+
+#ifdef GPTIMER_INFO_AVAIL
+#include <stdio.h>
+#endif
+
+#ifdef RTEMS_SMP
+#include <rtems/score/processormask.h>
+#include <rtems/score/smpimpl.h>
+#endif
+
+#include <grlib/grlib_impl.h>
+
+/* GPTIMER Core Configuration Register (READ-ONLY) */
+#define GPTIMER_CFG_TIMERS_BIT 0
+#define GPTIMER_CFG_IRQ_BIT 3
+#define GPTIMER_CFG_SI_BIT 8
+#define GPTIMER_CFG_DF_BIT 9
+
+#define GPTIMER_CFG_TIMERS (0x7<<GPTIMER_CFG_TIMERS_BIT)
+#define GPTIMER_CFG_IRQ (0x1f<<GPTIMER_CFG_IRQ_BIT)
+#define GPTIMER_CFG_SI (1<<GPTIMER_CFG_SI_BIT)
+#define GPTIMER_CFG_DF (1<<GPTIMER_CFG_DF_BIT)
+
+/* GPTIMER Timer Control Register */
+#define GPTIMER_CTRL_EN_BIT 0
+#define GPTIMER_CTRL_RS_BIT 1
+#define GPTIMER_CTRL_LD_BIT 2
+#define GPTIMER_CTRL_IE_BIT 3
+#define GPTIMER_CTRL_IP_BIT 4
+#define GPTIMER_CTRL_CH_BIT 5
+#define GPTIMER_CTRL_DH_BIT 6
+
+#define GPTIMER_CTRL_EN (1<<GPTIMER_CTRL_EN_BIT)
+#define GPTIMER_CTRL_RS (1<<GPTIMER_CTRL_RS_BIT)
+#define GPTIMER_CTRL_LD (1<<GPTIMER_CTRL_LD_BIT)
+#define GPTIMER_CTRL_IE (1<<GPTIMER_CTRL_IE_BIT)
+#define GPTIMER_CTRL_IP (1<<GPTIMER_CTRL_IP_BIT)
+#define GPTIMER_CTRL_CH (1<<GPTIMER_CTRL_CH_BIT)
+#define GPTIMER_CTRL_DH (1<<GPTIMER_CTRL_DH_BIT)
+
+#define DBG(x...)
+
+/* GPTIMER timer private */
+struct gptimer_timer {
+ struct tlib_dev tdev; /* Must be first in struct */
+ struct gptimer_timer_regs *tregs;
+ char index; /* Timer Index in this driver */
+ char tindex; /* Timer Index In Hardware */
+ unsigned char irq_ack_mask;
+};
+
+/* GPTIMER Core private */
+struct gptimer_priv {
+ struct drvmgr_dev *dev;
+ struct gptimer_regs *regs;
+ unsigned int base_clk;
+ unsigned int base_freq;
+ unsigned int widthmask;
+ char separate_interrupt;
+ char isr_installed;
+
+ /* Structure per Timer unit, the core supports up to 8 timers */
+ int timer_cnt;
+ struct gptimer_timer timers[0];
+};
+
+void gptimer_isr(void *data);
+
+#if 0
+void gptimer_tlib_irq_register(struct tlib_drv *tdrv, tlib_isr_t func, void *data)
+{
+ struct gptimer_priv *priv = (struct gptimer_priv *)tdrv;
+
+ if ( SHARED ...)
+
+
+ drvmgr_interrupt_register();
+}
+#endif
+
+/******************* Driver manager interface ***********************/
+
+/* Driver prototypes */
+static struct tlib_drv gptimer_tlib_drv;
+int gptimer_device_init(struct gptimer_priv *priv);
+
+int gptimer_init1(struct drvmgr_dev *dev);
+#ifdef GPTIMER_INFO_AVAIL
+static int gptimer_info(
+ struct drvmgr_dev *dev,
+ void (*print_line)(void *p, char *str),
+ void *p, int, char *argv[]);
+#define GTIMER_INFO_FUNC gptimer_info
+#else
+#define GTIMER_INFO_FUNC NULL
+#endif
+
+struct drvmgr_drv_ops gptimer_ops =
+{
+ .init = {gptimer_init1, NULL, NULL, NULL},
+ .remove = NULL,
+ .info = GTIMER_INFO_FUNC,
+};
+
+struct amba_dev_id gptimer_ids[] =
+{
+ {VENDOR_GAISLER, GAISLER_GPTIMER},
+ {VENDOR_GAISLER, GAISLER_GRTIMER},
+ {0, 0} /* Mark end of table */
+};
+
+struct amba_drv_info gptimer_drv_info =
+{
+ {
+ DRVMGR_OBJ_DRV, /* Driver */
+ NULL, /* Next driver */
+ NULL, /* Device list */
+ DRIVER_AMBAPP_GAISLER_GPTIMER_ID,/* Driver ID */
+ "GPTIMER_DRV", /* Driver Name */
+ DRVMGR_BUS_TYPE_AMBAPP, /* Bus Type */
+ &gptimer_ops,
+ NULL, /* Funcs */
+ 0, /* No devices yet */
+ 0,
+ },
+ &gptimer_ids[0]
+};
+
+void gptimer_register_drv (void)
+{
+ DBG("Registering GPTIMER driver\n");
+ drvmgr_drv_register(&gptimer_drv_info.general);
+}
+
+int gptimer_init1(struct drvmgr_dev *dev)
+{
+ struct gptimer_priv *priv;
+ struct gptimer_regs *regs;
+ struct amba_dev_info *ambadev;
+ struct ambapp_core *pnpinfo;
+ int timer_hw_cnt, timer_cnt, timer_start;
+ int i, size;
+ struct gptimer_timer *timer;
+ union drvmgr_key_value *value;
+ unsigned char irq_ack_mask;
+
+ /* Get device information from AMBA PnP information */
+ ambadev = (struct amba_dev_info *)dev->businfo;
+ if ( ambadev == NULL ) {
+ return -1;
+ }
+ pnpinfo = &ambadev->info;
+ regs = (struct gptimer_regs *)pnpinfo->apb_slv->start;
+
+ DBG("GPTIMER[%d] on bus %s\n", dev->minor_drv, dev->parent->dev->name);
+
+ /* Get number of Timers */
+ timer_hw_cnt = regs->cfg & GPTIMER_CFG_TIMERS;
+
+ /* Let user spelect a range of timers to be used. In AMP systems
+ * it is sometimes neccessary to leave timers for other CPU instances.
+ *
+ * The default operation in AMP is to shared the timers within the
+ * first GPTIMER core as below. This can of course be overrided by
+ * driver resources.
+ */
+ timer_cnt = timer_hw_cnt;
+ timer_start = 0;
+#if defined(RTEMS_MULTIPROCESSING) && defined(LEON3)
+ if ((dev->minor_drv == 0) && drvmgr_on_rootbus(dev)) {
+ timer_cnt = 1;
+ timer_start = LEON3_Cpu_Index;
+ }
+#endif
+ value = drvmgr_dev_key_get(dev, "timerStart", DRVMGR_KT_INT);
+ if ( value) {
+ timer_start = value->i;
+ timer_cnt = timer_hw_cnt - timer_start;
+ }
+ value = drvmgr_dev_key_get(dev, "timerCnt", DRVMGR_KT_INT);
+ if ( value && (value->i < timer_cnt) ) {
+ timer_cnt = value->i;
+ }
+
+ /* Allocate Common Timer Description, size depends on how many timers
+ * are present.
+ */
+ size = sizeof(struct gptimer_priv) +
+ timer_cnt*sizeof(struct gptimer_timer);
+ priv = dev->priv = grlib_calloc(1, size);
+ if ( !priv )
+ return DRVMGR_NOMEM;
+ priv->dev = dev;
+ priv->regs = regs;
+
+ /* The Base Frequency of the GPTIMER core is the same as the
+ * frequency of the AMBA bus it is situated on.
+ */
+ drvmgr_freq_get(dev, DEV_APB_SLV, &priv->base_clk);
+
+ /* This core will may provide important Timer functionality
+ * to other drivers and the RTEMS kernel, the Clock driver
+ * may for example use this device. So the Timer driver must be
+ * initialized in the first iiitialization stage.
+ */
+
+ /*** Initialize Hardware ***/
+
+ /* If user request to set prescaler, we will do that. However, note
+ * that doing so for the Root-Bus GPTIMER may affect the RTEMS Clock
+ * so that Clock frequency is wrong.
+ */
+ value = drvmgr_dev_key_get(priv->dev, "prescaler", DRVMGR_KT_INT);
+ if ( value )
+ regs->scaler_reload = value->i;
+
+ /* Get Frequency that the timers are operating in (after prescaler) */
+ priv->base_freq = priv->base_clk / (priv->regs->scaler_reload + 1);
+
+ /* Stop Timer and probe Pending bit. In newer hardware the
+ * timer has pending bit is cleared by writing a one to it,
+ * whereas older versions it is cleared with a zero.
+ */
+ priv->regs->timer[timer_start].ctrl = GPTIMER_CTRL_IP;
+ if ((priv->regs->timer[timer_start].ctrl & GPTIMER_CTRL_IP) != 0)
+ irq_ack_mask = ~GPTIMER_CTRL_IP;
+ else
+ irq_ack_mask = ~0;
+
+ /* Probe timer register width mask */
+ priv->regs->timer[timer_start].value = 0xffffffff;
+ priv->widthmask = priv->regs->timer[timer_start].value;
+
+ priv->timer_cnt = timer_cnt;
+ for (i=0; i<timer_cnt; i++) {
+ timer = &priv->timers[i];
+ timer->index = i;
+ timer->tindex = i + timer_start;
+ timer->tregs = &regs->timer[(int)timer->tindex];
+ timer->tdev.drv = &gptimer_tlib_drv;
+ timer->irq_ack_mask = irq_ack_mask;
+
+ /* Register Timer at Timer Library */
+ tlib_dev_reg(&timer->tdev);
+ }
+
+ /* Check Interrupt support implementation, two cases:
+ * A. All Timers share one IRQ
+ * B. Each Timer have an individual IRQ. The number is:
+ * BASE_IRQ + timer_index
+ */
+ priv->separate_interrupt = (regs->cfg & GPTIMER_CFG_SI) != 0;
+
+ return DRVMGR_OK;
+}
+
+#ifdef GPTIMER_INFO_AVAIL
+static int gptimer_info(
+ struct drvmgr_dev *dev,
+ void (*print_line)(void *p, char *str),
+ void *p, int argc, char *argv[])
+{
+ struct gptimer_priv *priv = dev->priv;
+ struct gptimer_timer *timer;
+ char buf[64];
+ int i;
+
+ if (priv == NULL || argc != 0)
+ return -DRVMGR_EINVAL;
+
+ sprintf(buf, "Timer Count: %d", priv->timer_cnt);
+ print_line(p, buf);
+ sprintf(buf, "REGS: 0x%08x", (unsigned int)priv->regs);
+ print_line(p, buf);
+ sprintf(buf, "BASE SCALER: %d", priv->regs->scaler_reload);
+ print_line(p, buf);
+ sprintf(buf, "BASE FREQ: %dkHz", priv->base_freq / 1000);
+ print_line(p, buf);
+ sprintf(buf, "SeparateIRQ: %s", priv->separate_interrupt ? "YES":"NO");
+ print_line(p, buf);
+
+ for (i=0; i<priv->timer_cnt; i++) {
+ timer = &priv->timers[i];
+ sprintf(buf, " - TIMER HW Index %d -", timer->tindex);
+ print_line(p, buf);
+ sprintf(buf, " TLIB Index: %d", timer->index);
+ print_line(p, buf);
+ sprintf(buf, " RELOAD REG: %d", timer->tregs->reload);
+ print_line(p, buf);
+ sprintf(buf, " CTRL REG: %d", timer->tregs->ctrl);
+ print_line(p, buf);
+ }
+
+ return DRVMGR_OK;
+}
+#endif
+
+static inline struct gptimer_priv *priv_from_timer(struct gptimer_timer *t)
+{
+ return (struct gptimer_priv *)
+ ((unsigned int)t -
+ sizeof(struct gptimer_priv) -
+ t->index * sizeof(struct gptimer_timer));
+}
+
+static int gptimer_tlib_int_pend(struct tlib_dev *hand, int ack)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+ unsigned int ctrl = timer->tregs->ctrl;
+
+ if ((ctrl & (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) ==
+ (GPTIMER_CTRL_IP | GPTIMER_CTRL_IE)) {
+ /* clear Pending IRQ ? */
+ if (ack)
+ timer->tregs->ctrl = ctrl & timer->irq_ack_mask;
+ return 1; /* timer generated IRQ */
+ } else
+ return 0; /* was not timer causing IRQ */
+}
+
+void gptimer_isr(void *data)
+{
+ struct gptimer_priv *priv = data;
+ int i;
+
+ /* Check all timers for IRQ */
+ for (i=0;i<priv->timer_cnt; i++) {
+ if (gptimer_tlib_int_pend((void *)&priv->timers[i], 0)) {
+ /* IRQ Was generated by Timer and Pending flag has *not*
+ * yet been cleared, this is to allow ISR to look at
+ * pending bit. Call ISR registered. Clear pending bit.
+ */
+ if (priv->timers[i].tdev.isr_func) {
+ priv->timers[i].tdev.isr_func(
+ priv->timers[i].tdev.isr_data);
+ }
+ gptimer_tlib_int_pend((void *)&priv->timers[i], 1);
+ }
+ }
+}
+
+static void gptimer_tlib_reset(struct tlib_dev *hand)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+
+ timer->tregs->ctrl = (timer->tregs->ctrl & timer->irq_ack_mask) &
+ GPTIMER_CTRL_IP;
+ timer->tregs->reload = 0xffffffff;
+ timer->tregs->ctrl = GPTIMER_CTRL_LD;
+}
+
+static void gptimer_tlib_get_freq(
+ struct tlib_dev *hand,
+ unsigned int *basefreq,
+ unsigned int *tickrate)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+ struct gptimer_priv *priv = priv_from_timer(timer);
+
+ /* Calculate base frequency from Timer Clock and Prescaler */
+ if ( basefreq )
+ *basefreq = priv->base_freq;
+ if ( tickrate )
+ *tickrate = timer->tregs->reload + 1;
+}
+
+static int gptimer_tlib_set_freq(struct tlib_dev *hand, unsigned int tickrate)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+
+ timer->tregs->reload = tickrate - 1;
+
+ /*Check that value was allowed (Timer may not be as wide as expected)*/
+ if ( timer->tregs->reload != (tickrate - 1) )
+ return -1;
+ else
+ return 0;
+}
+
+static void gptimer_tlib_irq_reg(struct tlib_dev *hand, tlib_isr_t func, void *data, int flags)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+ struct gptimer_priv *priv = priv_from_timer(timer);
+
+ if ( priv->separate_interrupt ) {
+ drvmgr_interrupt_register(priv->dev, timer->tindex,
+ "gptimer", func, data);
+ } else {
+ if (priv->isr_installed == 0) {
+ /* Shared IRQ handler */
+ drvmgr_interrupt_register(
+ priv->dev,
+ 0,
+ "gptimer_shared",
+ gptimer_isr,
+ priv);
+ }
+ priv->isr_installed++;
+ }
+
+#if RTEMS_SMP
+ if (flags & TLIB_FLAGS_BROADCAST) {
+ int tindex = 0;
+
+ if (priv->separate_interrupt) {
+ /* Offset interrupt number with HW subtimer index */
+ tindex = timer->tindex;
+ }
+ drvmgr_interrupt_set_affinity(priv->dev, tindex,
+ _SMP_Get_online_processors());
+ }
+#endif
+
+ timer->tregs->ctrl |= GPTIMER_CTRL_IE;
+}
+
+static void gptimer_tlib_irq_unreg(struct tlib_dev *hand, tlib_isr_t func, void *data)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+ struct gptimer_priv *priv = priv_from_timer(timer);
+
+ /* Turn off IRQ at source, unregister IRQ handler */
+ timer->tregs->ctrl &= ~GPTIMER_CTRL_IE;
+
+ if ( priv->separate_interrupt ) {
+ drvmgr_interrupt_unregister(priv->dev, timer->tindex,
+ func, data);
+ } else {
+ timer->tdev.isr_func = NULL;
+ priv->isr_installed--;
+ if (priv->isr_installed == 0) {
+ drvmgr_interrupt_unregister(priv->dev, 0,
+ gptimer_isr, priv);
+ }
+ }
+}
+
+static void gptimer_tlib_start(struct tlib_dev *hand, int once)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+ unsigned int ctrl;
+
+ /* Load the selected frequency before starting Frequency */
+ ctrl = GPTIMER_CTRL_LD | GPTIMER_CTRL_EN;
+ if ( once == 0 )
+ ctrl |= GPTIMER_CTRL_RS; /* Restart Timer */
+ timer->tregs->ctrl = ctrl | (timer->tregs->ctrl & timer->irq_ack_mask &
+ ~GPTIMER_CTRL_RS);
+}
+
+static void gptimer_tlib_stop(struct tlib_dev *hand)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+
+ /* Load the selected Frequency */
+ timer->tregs->ctrl &= ~(GPTIMER_CTRL_EN|GPTIMER_CTRL_IP);
+}
+
+static void gptimer_tlib_restart(struct tlib_dev *hand)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+
+ timer->tregs->ctrl |= GPTIMER_CTRL_LD | GPTIMER_CTRL_EN;
+}
+
+static void gptimer_tlib_get_counter(
+ struct tlib_dev *hand,
+ unsigned int *counter)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+
+ *counter = timer->tregs->value;
+}
+
+static void gptimer_tlib_get_widthmask(
+ struct tlib_dev *hand,
+ unsigned int *widthmask)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+ struct gptimer_priv *priv = priv_from_timer(timer);
+
+ *widthmask = priv->widthmask;
+}
+
+static struct tlib_drv gptimer_tlib_drv =
+{
+ .reset = gptimer_tlib_reset,
+ .get_freq = gptimer_tlib_get_freq,
+ .set_freq = gptimer_tlib_set_freq,
+ .irq_reg = gptimer_tlib_irq_reg,
+ .irq_unreg = gptimer_tlib_irq_unreg,
+ .start = gptimer_tlib_start,
+ .stop = gptimer_tlib_stop,
+ .restart = gptimer_tlib_restart,
+ .get_counter = gptimer_tlib_get_counter,
+ .custom = NULL,
+ .int_pend = gptimer_tlib_int_pend,
+ .get_widthmask = gptimer_tlib_get_widthmask,
+};
diff --git a/bsps/shared/grlib/btimer/tlib.c b/bsps/shared/grlib/btimer/tlib.c
new file mode 100644
index 0000000000..d66a472fe9
--- /dev/null
+++ b/bsps/shared/grlib/btimer/tlib.c
@@ -0,0 +1,77 @@
+/*
+ * Timer Library (TLIB)
+ *
+ * COPYRIGHT (c) 2011.
+ * Cobham Gaisler AB.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <grlib/tlib.h>
+
+struct tlib_dev *tlib_dev_head = NULL;
+struct tlib_dev *tlib_dev_tail = NULL;
+static int tlib_dev_cnt = 0;
+
+/* Register Timer device to Timer Library */
+int tlib_dev_reg(struct tlib_dev *newdev)
+{
+ /* Reset device */
+ newdev->status = 0;
+ newdev->isr_func = NULL;
+ newdev->index = tlib_dev_cnt;
+
+ /* Insert last in queue */
+ newdev->next = NULL;
+ if ( tlib_dev_tail == NULL ) {
+ tlib_dev_head = newdev;
+ } else {
+ tlib_dev_tail->next = newdev;
+ }
+ tlib_dev_tail = newdev;
+
+ /* Return Index of Registered Timer */
+ return tlib_dev_cnt++;
+}
+
+void *tlib_open(int timer_no)
+{
+ struct tlib_dev *dev;
+
+ if ( timer_no < 0 )
+ return NULL;
+
+ dev = tlib_dev_head;
+ while ( (timer_no > 0) && dev ) {
+ timer_no--;
+ dev = dev->next;
+ }
+ if ( dev ) {
+ if ( dev->status )
+ return NULL;
+ dev->status = 1;
+ /* Reset Timer to initial state */
+ tlib_reset(dev);
+ }
+ return dev;
+}
+
+void tlib_close(void *hand)
+{
+ struct tlib_dev *dev = hand;
+
+ /* Stop any ongoing timer operation and unregister IRQ if registered */
+ tlib_stop(dev);
+ tlib_irq_unregister(dev);
+
+ /* Mark not open */
+ dev->status = 0;
+}
+
+int tlib_ntimer(void)
+{
+ return tlib_dev_cnt;
+}
diff --git a/bsps/shared/grlib/btimer/tlib_ckinit.c b/bsps/shared/grlib/btimer/tlib_ckinit.c
new file mode 100644
index 0000000000..e43c8fdd8e
--- /dev/null
+++ b/bsps/shared/grlib/btimer/tlib_ckinit.c
@@ -0,0 +1,442 @@
+/*
+ * Clock Tick Device Driver using Timer Library implemented
+ * by the GRLIB GPTIMER / LEON2 Timer drivers.
+ *
+ * COPYRIGHT (c) 2010 - 2017.
+ * Cobham Gaisler AB.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ *
+ */
+
+/*
+ * This is an implementation of the RTEMS "clockdrv_shell" interface for
+ * LEON2/3/4 systems using the Driver Manager. It is clock hardware agnostic
+ * and compatible with SMP and UP. Availability of free running counters is
+ * probed and selected as needed.
+ */
+#include <rtems.h>
+#include <rtems/timecounter.h>
+#include <rtems/clockdrv.h>
+#include <stdlib.h>
+#include <bsp.h>
+#include <grlib/tlib.h>
+
+#ifdef RTEMS_DRVMGR_STARTUP
+
+#if defined(LEON3)
+#include <leon.h>
+#endif
+
+struct ops {
+ /*
+ * Set up the free running counter using the Timecounter or Simple
+ * Timecounter interface.
+ */
+ rtems_device_driver (*initialize_counter)(void);
+
+ /*
+ * Hardware-specific support at tick interrupt which runs early in Clock_isr.
+ * It can for example be used to check if interrupt was actually caused by
+ * the timer hardware. If return value is not RTEMS_SUCCESSFUL then Clock_isr
+ * returns immediately. at_tick can be initialized with NULL.
+ */
+ rtems_device_driver (*at_tick)(void);
+
+ /*
+ * Typically calls rtems_timecounter_tick(). A specialized clock driver may
+ * use for example rtems_timecounter_tick_simple() instead.
+ */
+ void (*timecounter_tick)(void);
+
+ /*
+ * Called when the clock driver exits. It can be used to stop functionality
+ * started by initialize_counter. The tick timer is stopped by default.
+ * shutdown_hardware can be initialized with NULL
+ */
+ void (*shutdown_hardware)(void);
+};
+
+/*
+ * Different implementation depending on available free running counter for the
+ * timecounter.
+ *
+ * NOTE: The clock interface is not compatible with shared interrupts on the
+ * clock (tick) timer in SMP configuration.
+ */
+
+#ifndef RTEMS_SMP
+/* "simple timecounter" interface. Only for non-SMP. */
+static const struct ops ops_simple;
+#else
+/* Hardware support up-counter using LEON3 %asr23. */
+static const struct ops ops_timetag;
+/* Timestamp counter available in some IRQ(A)MP instantiations. */
+static const struct ops ops_irqamp;
+/* Separate GPTIMER subtimer as timecounter */
+static const struct ops ops_subtimer;
+#endif
+
+struct clock_priv {
+ const struct ops *ops;
+ /*
+ * Timer number in Timer Library for tick timer used by this interface.
+ * Defaults to the first Timer in the System.
+ */
+ int tlib_tick_index;
+ /* Timer number for timecounter timer if separate GPTIMER subtimer is used */
+ int tlib_counter_index;
+ void *tlib_tick;
+ void *tlib_counter;
+ rtems_timecounter_simple tc_simple;
+ struct timecounter tc;
+};
+static struct clock_priv priv;
+
+/** Common interface **/
+
+/* Set system clock timer instance */
+void Clock_timer_register(int timer_number)
+{
+ priv.tlib_tick_index = timer_number;
+ priv.tlib_counter_index = timer_number + 1;
+}
+
+static rtems_device_driver tlib_clock_find_timer(void)
+{
+ /* Take Timer that should be used as system timer. */
+ priv.tlib_tick = tlib_open(priv.tlib_tick_index);
+ if (priv.tlib_tick == NULL) {
+ /* System Clock Timer not found */
+ return RTEMS_NOT_DEFINED;
+ }
+
+ /* Select which operation set to use */
+#ifndef RTEMS_SMP
+ priv.ops = &ops_simple;
+#else
+ /* When on LEON3 try to use dedicated hardware free running counter. */
+ leon3_up_counter_enable();
+ if (leon3_up_counter_is_available()) {
+ priv.ops = &ops_timetag;
+ return RTEMS_SUCCESSFUL;
+ } else {
+ volatile struct irqmp_timestamp_regs *irqmp_ts;
+
+ irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
+ if (leon3_irqmp_has_timestamp(irqmp_ts)) {
+ priv.ops = &ops_irqamp;
+ return RTEMS_SUCCESSFUL;
+ }
+ }
+
+ /* Take another subtimer as the final option. */
+ priv.ops = &ops_subtimer;
+#endif
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_device_driver tlib_clock_initialize_hardware(void)
+{
+ /* Set tick rate in number of "Base-Frequency ticks" */
+ tlib_set_freq(priv.tlib_tick, rtems_configuration_get_microseconds_per_tick());
+ priv.ops->initialize_counter();
+ tlib_start(priv.tlib_tick, 0);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static rtems_device_driver tlib_clock_at_tick(void)
+{
+ if (priv.ops->at_tick) {
+ return priv.ops->at_tick();
+ }
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static void tlib_clock_timecounter_tick(void)
+{
+ priv.ops->timecounter_tick();
+}
+
+/* Return a value not equal to RTEMS_SUCCESFUL to make Clock_initialize fail. */
+static rtems_device_driver tlib_clock_install_isr(rtems_isr *isr)
+{
+ int flags = 0;
+
+#ifdef RTEMS_SMP
+ /* We shall broadcast the clock interrupt to all processors. */
+ flags = TLIB_FLAGS_BROADCAST;
+#endif
+ tlib_irq_register(priv.tlib_tick, isr, NULL, flags);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+#ifndef RTEMS_SMP
+/** Simple counter **/
+static uint32_t simple_tlib_tc_get(rtems_timecounter_simple *tc)
+{
+ unsigned int clicks = 0;
+
+ if (priv.tlib_tick != NULL) {
+ tlib_get_counter(priv.tlib_tick, &clicks);
+ }
+
+ return clicks;
+}
+
+static bool simple_tlib_tc_is_pending(rtems_timecounter_simple *tc)
+{
+ bool pending = false;
+
+ if (priv.tlib_tick != NULL) {
+ pending = tlib_interrupt_pending(priv.tlib_tick, 0) != 0;
+ }
+
+ return pending;
+}
+
+static uint32_t simple_tlib_tc_get_timecount(struct timecounter *tc)
+{
+ return rtems_timecounter_simple_downcounter_get(
+ tc,
+ simple_tlib_tc_get,
+ simple_tlib_tc_is_pending
+ );
+}
+
+static rtems_device_driver simple_initialize_counter(void)
+{
+ uint64_t frequency;
+ unsigned int tick_hz;
+
+ frequency = 1000000;
+ tick_hz = rtems_configuration_get_microseconds_per_tick();
+
+ rtems_timecounter_simple_install(
+ &priv.tc_simple,
+ frequency,
+ tick_hz,
+ simple_tlib_tc_get_timecount
+ );
+
+ return RTEMS_NOT_DEFINED;
+}
+
+static void simple_tlib_tc_at_tick(rtems_timecounter_simple *tc)
+{
+ /* Nothing to do */
+}
+
+/*
+ * Support for shared interrupts. Ack IRQ at source, only handle interrupts
+ * generated from the tick-timer. This is called early in Clock_isr.
+ */
+static rtems_device_driver simple_at_tick(void)
+{
+ if (tlib_interrupt_pending(priv.tlib_tick, 1) == 0) {
+ return RTEMS_NOT_DEFINED;
+ }
+ return RTEMS_SUCCESSFUL;
+}
+
+static void simple_timecounter_tick(void)
+{
+ rtems_timecounter_simple_downcounter_tick(
+ &priv.tc_simple,
+ simple_tlib_tc_get,
+ simple_tlib_tc_at_tick
+ );
+}
+
+static const struct ops ops_simple = {
+ .initialize_counter = simple_initialize_counter,
+ .at_tick = simple_at_tick,
+ .timecounter_tick = simple_timecounter_tick,
+ .shutdown_hardware = NULL,
+};
+
+#else
+
+/** Subtimer as counter **/
+static uint32_t subtimer_get_timecount(struct timecounter *tc)
+{
+ unsigned int counter;
+
+ tlib_get_counter(priv.tlib_counter, &counter);
+
+ return 0xffffffff - counter;
+}
+
+static rtems_device_driver subtimer_initialize_counter(void)
+{
+ unsigned int mask;
+ unsigned int basefreq;
+
+ if (priv.tlib_counter_index == priv.tlib_tick_index) {
+ priv.tlib_counter_index = priv.tlib_tick_index + 1;
+ }
+ /* Take Timer that should be used as timecounter upcounter timer. */
+ priv.tlib_counter = tlib_open(priv.tlib_counter_index);
+ if (priv.tlib_counter == NULL) {
+ /* Timecounter timer not found */
+ return RTEMS_NOT_DEFINED;
+ }
+
+ /* Configure free running counter: GPTIMER */
+ tlib_get_freq(priv.tlib_counter, &basefreq, NULL);
+ tlib_get_widthmask(priv.tlib_counter, &mask);
+
+ priv.tc.tc_get_timecount = subtimer_get_timecount;
+ priv.tc.tc_counter_mask = mask;
+ priv.tc.tc_frequency = basefreq;
+ priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
+ rtems_timecounter_install(&priv.tc);
+ /* Start free running counter */
+ tlib_start(priv.tlib_counter, 0);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static void subtimer_timecounter_tick(void)
+{
+ rtems_timecounter_tick();
+}
+
+static void subtimer_shutdown_hardware(void)
+{
+ if (priv.tlib_counter) {
+ tlib_stop(priv.tlib_counter);
+ priv.tlib_counter = NULL;
+ }
+}
+
+static const struct ops ops_subtimer = {
+ .initialize_counter = subtimer_initialize_counter,
+ .timecounter_tick = subtimer_timecounter_tick,
+ .shutdown_hardware = subtimer_shutdown_hardware,
+};
+
+/** DSU timetag as counter **/
+static uint32_t timetag_get_timecount(struct timecounter *tc)
+{
+ return leon3_up_counter_low();
+}
+
+static rtems_device_driver timetag_initialize_counter(void)
+{
+ /* Configure free running counter: timetag */
+ priv.tc.tc_get_timecount = timetag_get_timecount;
+ priv.tc.tc_counter_mask = 0xffffffff;
+ priv.tc.tc_frequency = leon3_up_counter_frequency();
+ priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
+ rtems_timecounter_install(&priv.tc);
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static void timetag_timecounter_tick(void)
+{
+ rtems_timecounter_tick();
+}
+
+static const struct ops ops_timetag = {
+ .initialize_counter = timetag_initialize_counter,
+ .at_tick = NULL,
+ .timecounter_tick = timetag_timecounter_tick,
+ .shutdown_hardware = NULL,
+};
+
+/** IRQ(A)MP timestamp as counter **/
+static uint32_t irqamp_get_timecount(struct timecounter *tc)
+{
+ return LEON3_IrqCtrl_Regs->timestamp[0].counter;
+}
+
+static rtems_device_driver irqamp_initialize_counter(void)
+{
+ volatile struct irqmp_timestamp_regs *irqmp_ts;
+ static const uint32_t A_TSISEL_FIELD = 0xf;
+
+ /* Configure free running counter: timetag */
+ priv.tc.tc_get_timecount = irqamp_get_timecount;
+ priv.tc.tc_counter_mask = 0xffffffff;
+ priv.tc.tc_frequency = leon3_up_counter_frequency();
+ priv.tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
+ rtems_timecounter_install(&priv.tc);
+
+ /*
+ * The counter increments whenever a TSISEL field in a Timestamp Control
+ * Register is non-zero.
+ */
+ irqmp_ts = &LEON3_IrqCtrl_Regs->timestamp[0];
+ irqmp_ts->control = A_TSISEL_FIELD;
+
+ return RTEMS_SUCCESSFUL;
+}
+
+static void irqamp_timecounter_tick(void)
+{
+ rtems_timecounter_tick();
+}
+
+static const struct ops ops_irqamp = {
+ .initialize_counter = irqamp_initialize_counter,
+ .at_tick = NULL,
+ .timecounter_tick = irqamp_timecounter_tick,
+ .shutdown_hardware = NULL,
+};
+#endif
+
+/** Interface to the Clock Driver Shell (dev/clock/clockimpl.h) **/
+#define Clock_driver_support_find_timer() \
+ do { \
+ rtems_device_driver ret; \
+ ret = tlib_clock_find_timer(); \
+ if (RTEMS_SUCCESSFUL != ret) { \
+ return ret; \
+ } \
+ } while (0)
+
+#define Clock_driver_support_install_isr( isr ) \
+ do { \
+ rtems_device_driver ret; \
+ ret = tlib_clock_install_isr( isr ); \
+ if (RTEMS_SUCCESSFUL != ret) { \
+ return ret; \
+ } \
+ } while (0)
+
+#define Clock_driver_support_set_interrupt_affinity(online_processors) \
+ /* Done by tlib_clock_install_isr() */
+
+#define Clock_driver_support_initialize_hardware() \
+ do { \
+ rtems_device_driver ret; \
+ ret = tlib_clock_initialize_hardware(); \
+ if (RTEMS_SUCCESSFUL != ret) { \
+ return ret; \
+ } \
+ } while (0)
+
+#define Clock_driver_timecounter_tick() \
+ tlib_clock_timecounter_tick()
+
+#define Clock_driver_support_at_tick() \
+ do { \
+ rtems_device_driver ret; \
+ ret = tlib_clock_at_tick(); \
+ if (RTEMS_SUCCESSFUL != ret) { \
+ return; \
+ } \
+ } while (0)
+
+#include "../../../shared/dev/clock/clockimpl.h"
+
+#endif /* RTEMS_DRVMGR_STARTUP */
+