summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libbsp/sparc/shared/timer
diff options
context:
space:
mode:
authorDaniel Hellstrom <daniel@gaisler.com>2011-12-16 10:37:49 +0100
committerDaniel Hellstrom <daniel@gaisler.com>2015-04-17 01:10:16 +0200
commitcd64fbfb6847b91fb51a2387ba93a255a5afa18d (patch)
treeaec5a5054846300cf490db3d229a0ec254d5f814 /c/src/lib/libbsp/sparc/shared/timer
parentLEON2: implemented AMBA Bus Driver for Driver Manager (diff)
downloadrtems-cd64fbfb6847b91fb51a2387ba93a255a5afa18d.tar.bz2
LEON: GPTIMER driver, Timer Library and System Clock for LEON3
With this patch the LEON family can access the GRLIB GPTIMER using the Timer library (TLIB). A System Clock driver instead of BSP/clock/ck_init.c is provided using the TLIB. The classic clock driver is split in two parts, clock driver and timer driver. The BSPs need only to fullfill the timer interface instead of the clock interface. Currently only LEON3 uses it. The LEON2 Timer is not ported to TLIB. The GPTIMER driver is implemented using the Driver Manager, so the System Clock Driver is at this point only suitable for LEON3 when the driver manager is initialized during BSP startup. When the DrvMgr is not initialized during startup the standard BSP/clock dirver is used. LEON2 sometimes also needs to access GPTIMER when a off-chip GRLIB AMBA systems is connected, for example AMBA-over-PCI.
Diffstat (limited to 'c/src/lib/libbsp/sparc/shared/timer')
-rw-r--r--c/src/lib/libbsp/sparc/shared/timer/gptimer.c503
-rw-r--r--c/src/lib/libbsp/sparc/shared/timer/tlib.c77
-rw-r--r--c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c264
3 files changed, 844 insertions, 0 deletions
diff --git a/c/src/lib/libbsp/sparc/shared/timer/gptimer.c b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c
new file mode 100644
index 0000000000..b15aaf1673
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/timer/gptimer.c
@@ -0,0 +1,503 @@
+/* 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.com/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <bsp.h>
+#include <stdlib.h>
+#include <drvmgr/drvmgr.h>
+#include <drvmgr/ambapp_bus.h>
+#include <grlib.h>
+#include "tlib.h"
+
+#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP)
+#include <leon.h>
+volatile struct gptimer_regs *LEON3_Timer_Regs = 0;
+#endif
+
+#ifdef GPTIMER_INFO_AVAIL
+#include <stdio.h>
+#endif
+
+/* 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 */
+};
+
+/* GPTIMER Core private */
+struct gptimer_priv {
+ struct drvmgr_dev *dev;
+ struct gptimer_regs *regs;
+ unsigned int base_clk;
+ unsigned int base_freq;
+ int separate_interrupt;
+
+ /* 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 */
+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},
+ {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;
+#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP)
+ char timer_index[7];
+#endif
+
+ /* 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", KEY_TYPE_INT);
+ if ( value) {
+ timer_start = value->i;
+ timer_cnt = timer_hw_cnt - timer_start;
+ }
+ value = drvmgr_dev_key_get(dev, "timerCnt", KEY_TYPE_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 = (struct gptimer_priv *)malloc(size);
+ if ( !priv )
+ return DRVMGR_NOMEM;
+ memset(priv, 0, size);
+ priv->dev = dev;
+ priv->regs = regs;
+
+#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP)
+ if ( drvmgr_on_rootbus(priv->dev) && !LEON3_Timer_Regs) {
+ /* Bootloader has initialized the Timer prescaler to 1MHz,
+ * this means that the AMBA Frequency is 1MHz * PRESCALER.
+ */
+ priv->base_clk = (regs->scaler_reload + 1) * 1000000;
+ ambapp_bus_freq_register(priv->dev,DEV_APB_SLV,priv->base_clk);
+ LEON3_Timer_Regs = (void *)regs;
+ } else
+#endif
+ {
+ /* 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", KEY_TYPE_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);
+
+ 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;
+
+ /* Stop Timer */
+ timer->tregs->ctrl = 0;
+
+ /* Register Timer at Timer Library */
+#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP)
+ timer_index[i] =
+#endif
+ 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;
+
+ if ( priv->separate_interrupt == 0 ) {
+ /* Shared IRQ handler */
+ drvmgr_interrupt_register(
+ priv->dev,
+ 0,
+ "gptimer_shared",
+ gptimer_isr,
+ priv);
+ }
+
+ /* If the user request a certain Timer to be the RTEMS Clock Timer,
+ * the timer must be registered at the Clock Driver.
+ */
+#if defined(LEON3) && defined(RTEMS_DRVMGR_STARTUP)
+ value = drvmgr_dev_key_get(priv->dev, "clockTimer", KEY_TYPE_INT);
+ if ( value && (value->i < timer_cnt) ) {
+ LEON3_Timer_Regs = (void *)regs;
+ Clock_timer_register(timer_index[value->i]);
+ }
+#endif
+
+ 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));
+}
+
+void gptimer_isr(void *data)
+{
+ struct gptimer_priv *priv = data;
+ struct gptimer_timer_regs *tregs;
+ int i;
+ unsigned int ctrl;
+
+ /* Check all timers for IRQ */
+ for (i=0;i<priv->timer_cnt; i++) {
+ tregs = priv->timers[i].tregs;
+ ctrl = tregs->ctrl;
+ if ( ctrl & GPTIMER_CTRL_IP ) {
+ /* IRQ Was generated by Timer, Clear Pending flag
+ * call ISR registered
+ */
+ tregs->ctrl = ctrl | GPTIMER_CTRL_IP;
+ if ( priv->timers[i].tdev.isr_func ) {
+ priv->timers[i].tdev.isr_func(
+ priv->timers[i].tdev.isr_data);
+ }
+ }
+ }
+}
+
+void gptimer_tlib_reset(struct tlib_dev *hand)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+
+ timer->tregs->ctrl = 0;
+ timer->tregs->reload = 0xffffffff;
+ timer->tregs->ctrl = GPTIMER_CTRL_LD;
+}
+
+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;
+}
+
+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;
+}
+
+void gptimer_tlib_irq_reg(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);
+
+ if ( priv->separate_interrupt ) {
+ drvmgr_interrupt_register(priv->dev, timer->tindex,
+ "gptimer", func, data);
+ }
+
+ timer->tregs->ctrl |= GPTIMER_CTRL_IE;
+}
+
+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;
+ }
+}
+
+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;
+}
+
+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);
+}
+
+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;
+}
+
+void gptimer_tlib_get_counter(struct tlib_dev *hand, unsigned int *counter)
+{
+ struct gptimer_timer *timer = (struct gptimer_timer *)hand;
+
+ *counter = timer->tregs->value;
+}
+
+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,
+};
diff --git a/c/src/lib/libbsp/sparc/shared/timer/tlib.c b/c/src/lib/libbsp/sparc/shared/timer/tlib.c
new file mode 100644
index 0000000000..d1f68eded2
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/timer/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.com/license/LICENSE.
+ */
+
+#include <rtems.h>
+#include <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/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
new file mode 100644
index 0000000000..5758f5bf0d
--- /dev/null
+++ b/c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c
@@ -0,0 +1,264 @@
+/*
+ * Clock Tick Device Driver using Timer Library implemented
+ * by the GRLIB GPTIMER / LEON2 Timer drivers.
+ *
+ * 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.com/license/LICENSE.
+ *
+ */
+
+#include <stdlib.h>
+#include <bsp.h>
+#include <tlib.h>
+
+/* Undefine (default) this to save space in standard LEON configurations,
+ * it will assume that Prescaler is running at 1MHz.
+ */
+/*#undef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ*/
+
+/* Set the below defines from bsp.h if function needed.
+#undef CLOCK_DRIVER_ISRS_PER_TICK
+#undef CLOCK_DRIVER_USE_FAST_IDLE
+*/
+#define Clock_driver_support_at_tick()
+
+/*
+ * Number of Clock ticks since initialization
+ */
+volatile uint32_t Clock_driver_ticks;
+
+/*
+ * Timer Number in Timer Library. Defaults to the first Timer in
+ * the System.
+ */
+int Clock_timer = 0;
+
+/*
+ * Timer Handle in Timer Library
+ */
+void *Clock_handle = NULL;
+
+#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
+unsigned int Clock_basefreq;
+#endif
+
+void Clock_exit(void);
+
+/*
+ * Major and minor number.
+ */
+
+rtems_device_major_number rtems_clock_major = UINT32_MAX;
+rtems_device_minor_number rtems_clock_minor;
+
+/*
+ * Clock_isr
+ *
+ * This is the clock tick interrupt handler.
+ *
+ * Input parameters:
+ * vector - vector number
+ *
+ * Output parameters: NONE
+ *
+ * Return values: NONE
+ *
+ */
+
+void Clock_isr(void *arg_unused)
+{
+ /*
+ * Accurate count of ISRs
+ */
+
+ Clock_driver_ticks += 1;
+
+#ifdef CLOCK_DRIVER_USE_FAST_IDLE
+ do {
+ rtems_clock_tick();
+ } while ( _Thread_Executing == _Thread_Idle &&
+ _Thread_Heir == _Thread_Executing);
+
+ Clock_driver_support_at_tick();
+ return;
+
+#else
+
+ /*
+ * Add custom handling at every tick from bsp.h
+ */
+ Clock_driver_support_at_tick();
+
+#ifdef CLOCK_DRIVER_ISRS_PER_TICK
+ /*
+ * The driver is multiple ISRs per clock tick.
+ */
+
+ if ( !Clock_driver_isrs ) {
+
+ rtems_clock_tick();
+
+ Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK;
+ }
+ Clock_driver_isrs--;
+#else
+
+ /*
+ * The driver is one ISR per clock tick.
+ */
+ rtems_clock_tick();
+#endif
+#endif
+}
+
+/*
+ * Clock_exit
+ *
+ * This routine allows the clock driver to exit by masking the interrupt and
+ * disabling the clock's counter.
+ *
+ * Input parameters: NONE
+ *
+ * Output parameters: NONE
+ *
+ * Return values: NONE
+ *
+ */
+
+void Clock_exit( void )
+{
+ /* Stop all activity of the Timer, no more ISRs.
+ * We could be using tlib_close(), however tlib_stop() is quicker
+ * and independent of IRQ unregister code.
+ */
+ if ( Clock_handle ) {
+ tlib_stop(Clock_handle);
+ Clock_handle = NULL;
+ }
+}
+
+uint32_t Clock_nanoseconds_since_last_tick(void)
+{
+ uint32_t clicks;
+ if ( !Clock_handle )
+ return 0;
+
+ tlib_get_counter(Clock_handle, (unsigned int *)&clicks);
+
+#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
+ {
+ /* Down counter. Calc from BaseFreq. */
+ uint64_t tmp;
+ tmp = ((uint64_t)clicks * 1000000000) / ((uint64_t)Clock_basefreq);
+ return (uint32_t)tmp;
+ }
+#else
+ /* Down counter. Timer base frequency is initialized to 1 MHz */
+ return (uint32_t)
+ (rtems_configuration_get_microseconds_per_tick() - clicks) * 1000;
+#endif
+}
+
+/*
+ * Clock_initialize
+ *
+ * This routine initializes the clock driver and starts the Clock.
+ *
+ * Input parameters:
+ * major - clock device major number
+ * minor - clock device minor number
+ * parg - pointer to optional device driver arguments
+ *
+ * Output parameters: NONE
+ *
+ * Return values:
+ * rtems_device_driver status code
+ */
+
+rtems_device_driver Clock_initialize(
+ rtems_device_major_number major,
+ rtems_device_minor_number minor,
+ void *pargp
+)
+{
+ unsigned int tick_hz;
+
+ /*
+ * Take Timer that should be used as system timer.
+ *
+ */
+ Clock_handle = tlib_open(Clock_timer);
+ if ( Clock_handle == NULL ) {
+ /* System Clock Timer not found */
+ return RTEMS_NOT_DEFINED;
+ }
+
+ /*
+ * Install Clock ISR before starting timer
+ */
+ tlib_irq_register(Clock_handle, Clock_isr, NULL);
+
+ /* Set Timer Frequency to tick at Configured value. The Timer
+ * Frequency is set in multiples of the timer base frequency.
+ *
+ * In standard LEON3 designs the base frequency is is 1MHz, to
+ * save instructions define CLOCK_DRIVER_ASSUME_PRESCALER_1MHZ
+ * to avoid 64-bit calculation.
+ */
+#ifdef CLOCK_DRIVER_DONT_ASSUME_PRESCALER_1MHZ
+ {
+ uint64_t tmp;
+
+ tlib_get_freq(Clock_handle, &Clock_basefreq, NULL);
+
+ tmp = (uint64_t)Clock_basefreq;
+ tmp = tmp * (unint64_t)rtems_configuration_get_microseconds_per_tick();
+ tick_hz = tmp / 1000000;
+ }
+#else
+ tick_hz = rtems_configuration_get_microseconds_per_tick();
+#endif
+
+ tlib_set_freq(Clock_handle, tick_hz);
+
+ rtems_clock_set_nanoseconds_extension(Clock_nanoseconds_since_last_tick);
+
+ /*
+ * IRQ and Frequency is setup, now we start the Timer. IRQ is still
+ * disabled globally during startup, so IRQ will hold for a while.
+ */
+ tlib_start(Clock_handle, 0);
+
+ /*
+ * Register function called at system shutdown
+ */
+ atexit( Clock_exit );
+
+ /*
+ * make major/minor avail to others such as shared memory driver
+ */
+
+ rtems_clock_major = major;
+ rtems_clock_minor = minor;
+
+ /*
+ * If we are counting ISRs per tick, then initialize the counter.
+ */
+
+#ifdef CLOCK_DRIVER_ISRS_PER_TICK
+ Clock_driver_isrs = CLOCK_DRIVER_ISRS_PER_TICK;
+#endif
+
+ return RTEMS_SUCCESSFUL;
+}
+
+/*** Timer Driver Interface ***/
+
+void Clock_timer_register(int timer_number)
+{
+ Clock_timer = timer_number;
+}