forked from Imagelibrary/rtems
bsps: Move benchmark timer to bsps
This patch is a part of the BSP source reorganization. Update #3285.
This commit is contained in:
81
bsps/sparc/erc32/btimer/btimer.c
Normal file
81
bsps/sparc/erc32/btimer/btimer.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/* timer.c
|
||||
*
|
||||
* This file implements a benchmark timer using the General Purpose Timer on
|
||||
* the MEC.
|
||||
*
|
||||
* COPYRIGHT (c) 1989-1999.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Ported to ERC32 implementation of the SPARC by On-Line Applications
|
||||
* Research Corporation (OAR) under contract to the European Space
|
||||
* Agency (ESA).
|
||||
*
|
||||
* ERC32 modifications of respective RTEMS file: COPYRIGHT (c) 1995.
|
||||
* European Space Agency.
|
||||
*/
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/btimer.h>
|
||||
|
||||
bool benchmark_timer_find_average_overhead;
|
||||
|
||||
bool benchmark_timer_is_initialized = false;
|
||||
|
||||
void benchmark_timer_initialize(void)
|
||||
{
|
||||
/*
|
||||
* Timer runs long and accurate enough not to require an interrupt.
|
||||
*/
|
||||
|
||||
if ( benchmark_timer_is_initialized == false ) {
|
||||
|
||||
/* approximately 1 us per countdown */
|
||||
ERC32_MEC.General_Purpose_Timer_Scalar = CLOCK_SPEED - 1;
|
||||
ERC32_MEC.General_Purpose_Timer_Counter = 0xffffffff;
|
||||
|
||||
} else {
|
||||
benchmark_timer_is_initialized = true;
|
||||
}
|
||||
|
||||
ERC32_MEC_Set_General_Purpose_Timer_Control(
|
||||
ERC32_MEC_TIMER_COUNTER_ENABLE_COUNTING |
|
||||
ERC32_MEC_TIMER_COUNTER_LOAD_COUNTER
|
||||
);
|
||||
|
||||
ERC32_MEC_Set_General_Purpose_Timer_Control(
|
||||
ERC32_MEC_TIMER_COUNTER_ENABLE_COUNTING
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#define AVG_OVERHEAD 12 /* It typically takes 3.0 microseconds */
|
||||
/* to start/stop the timer. */
|
||||
#define LEAST_VALID 13 /* Don't trust a value lower than this */
|
||||
|
||||
benchmark_timer_t benchmark_timer_read(void)
|
||||
{
|
||||
uint32_t total;
|
||||
|
||||
total = ERC32_MEC.General_Purpose_Timer_Counter;
|
||||
|
||||
total = 0xffffffff - total;
|
||||
|
||||
if ( benchmark_timer_find_average_overhead == true )
|
||||
return total; /* in one microsecond units */
|
||||
|
||||
if ( total < LEAST_VALID )
|
||||
return 0; /* below timer resolution */
|
||||
|
||||
return total - AVG_OVERHEAD;
|
||||
}
|
||||
|
||||
void benchmark_timer_disable_subtracting_average_overhead(
|
||||
bool find_flag
|
||||
)
|
||||
{
|
||||
benchmark_timer_find_average_overhead = find_flag;
|
||||
}
|
||||
83
bsps/sparc/leon2/btimer/btimer.c
Normal file
83
bsps/sparc/leon2/btimer/btimer.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @file
|
||||
* @ingroup sparc_leon2
|
||||
* @brief Implement a benchmark timer using timer 2
|
||||
*/
|
||||
|
||||
/* timer.c
|
||||
*
|
||||
* This file implements a benchmark timer using timer 2.
|
||||
*
|
||||
* COPYRIGHT (c) 1989-1998.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Ported to LEON implementation of the SPARC by On-Line Applications
|
||||
* Research Corporation (OAR) under contract to the European Space
|
||||
* Agency (ESA).
|
||||
*
|
||||
* LEON modifications of respective RTEMS file: COPYRIGHT (c) 1995.
|
||||
* European Space Agency.
|
||||
*/
|
||||
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/btimer.h>
|
||||
|
||||
bool benchmark_timer_find_average_overhead;
|
||||
|
||||
bool benchmark_timer_is_initialized = false;
|
||||
|
||||
void benchmark_timer_initialize(void)
|
||||
{
|
||||
/*
|
||||
* Timer runs long and accurate enough not to require an interrupt.
|
||||
*/
|
||||
|
||||
if ( benchmark_timer_is_initialized == false ) {
|
||||
|
||||
/* approximately 1 us per countdown */
|
||||
LEON_REG.Timer_Counter_2 = 0xffffff;
|
||||
LEON_REG.Timer_Reload_2 = 0xffffff;
|
||||
|
||||
} else {
|
||||
benchmark_timer_is_initialized = true;
|
||||
}
|
||||
|
||||
LEON_REG.Timer_Control_2 = (
|
||||
LEON_REG_TIMER_COUNTER_ENABLE_COUNTING |
|
||||
LEON_REG_TIMER_COUNTER_LOAD_COUNTER
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#define AVG_OVERHEAD 3 /* It typically takes 3.0 microseconds */
|
||||
/* to start/stop the timer. */
|
||||
#define LEAST_VALID 2 /* Don't trust a value lower than this */
|
||||
|
||||
benchmark_timer_t benchmark_timer_read(void)
|
||||
{
|
||||
uint32_t total;
|
||||
|
||||
total = LEON_REG.Timer_Counter_2;
|
||||
|
||||
total = 0xffffff - total;
|
||||
|
||||
if ( benchmark_timer_find_average_overhead == true )
|
||||
return total; /* in one microsecond units */
|
||||
|
||||
if ( total < LEAST_VALID )
|
||||
return 0; /* below timer resolution */
|
||||
|
||||
return total - AVG_OVERHEAD;
|
||||
}
|
||||
|
||||
void benchmark_timer_disable_subtracting_average_overhead(
|
||||
bool find_flag
|
||||
)
|
||||
{
|
||||
benchmark_timer_find_average_overhead = find_flag;
|
||||
}
|
||||
85
bsps/sparc/leon3/btimer/btimer.c
Normal file
85
bsps/sparc/leon3/btimer/btimer.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/* timer.c
|
||||
*
|
||||
* This file implements a benchmark timer using timer 2.
|
||||
*
|
||||
* COPYRIGHT (c) 1989-1998.
|
||||
* On-Line Applications Research Corporation (OAR).
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Ported to LEON implementation of the SPARC by On-Line Applications
|
||||
* Research Corporation (OAR) under contract to the European Space
|
||||
* Agency (ESA).
|
||||
*
|
||||
* LEON modifications of respective RTEMS file: COPYRIGHT (c) 1995.
|
||||
* European Space Agency.
|
||||
*/
|
||||
|
||||
|
||||
#include <bsp.h>
|
||||
#include <rtems/btimer.h>
|
||||
|
||||
#if defined(RTEMS_MULTIPROCESSING)
|
||||
#define LEON3_TIMER_INDEX \
|
||||
((rtems_configuration_get_user_multiprocessing_table()) ? \
|
||||
(rtems_configuration_get_user_multiprocessing_table()->node) - 1 : 1)
|
||||
#else
|
||||
#define LEON3_TIMER_INDEX 0
|
||||
#endif
|
||||
|
||||
bool benchmark_timer_find_average_overhead;
|
||||
|
||||
bool benchmark_timer_is_initialized = false;
|
||||
|
||||
extern volatile struct gptimer_regs *LEON3_Timer_Regs;
|
||||
|
||||
void benchmark_timer_initialize(void)
|
||||
{
|
||||
/*
|
||||
* Timer runs long and accurate enough not to require an interrupt.
|
||||
*/
|
||||
if (LEON3_Timer_Regs) {
|
||||
if ( benchmark_timer_is_initialized == false ) {
|
||||
/* approximately 1 us per countdown */
|
||||
LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].reload = 0xffffff;
|
||||
LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].value = 0xffffff;
|
||||
} else {
|
||||
benchmark_timer_is_initialized = true;
|
||||
}
|
||||
LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].ctrl =
|
||||
GPTIMER_TIMER_CTRL_EN | GPTIMER_TIMER_CTRL_LD;
|
||||
}
|
||||
}
|
||||
|
||||
#define AVG_OVERHEAD 3 /* It typically takes 3.0 microseconds */
|
||||
/* to start/stop the timer. */
|
||||
#define LEAST_VALID 2 /* Don't trust a value lower than this */
|
||||
|
||||
benchmark_timer_t benchmark_timer_read(void)
|
||||
{
|
||||
uint32_t total;
|
||||
|
||||
if (LEON3_Timer_Regs) {
|
||||
total = LEON3_Timer_Regs->timer[LEON3_TIMER_INDEX].value;
|
||||
|
||||
total = 0xffffff - total;
|
||||
|
||||
if ( benchmark_timer_find_average_overhead == true )
|
||||
return total; /* in one microsecond units */
|
||||
|
||||
if ( total < LEAST_VALID )
|
||||
return 0; /* below timer resolution */
|
||||
|
||||
return total - AVG_OVERHEAD;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void benchmark_timer_disable_subtracting_average_overhead(
|
||||
bool find_flag
|
||||
)
|
||||
{
|
||||
benchmark_timer_find_average_overhead = find_flag;
|
||||
}
|
||||
90
bsps/sparc/leon3/btimer/watchdog.c
Normal file
90
bsps/sparc/leon3/btimer/watchdog.c
Normal file
@@ -0,0 +1,90 @@
|
||||
/* GPTIMER Watchdog timer routines. On some systems the first GPTIMER
|
||||
* core's last Timer instance underflow signal is connected to system
|
||||
* reset.
|
||||
*
|
||||
* COPYRIGHT (c) 2012.
|
||||
* 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 <bsp.h>
|
||||
#include <bsp/watchdog.h>
|
||||
#include <grlib.h>
|
||||
|
||||
extern volatile struct gptimer_regs *LEON3_Timer_Regs;
|
||||
|
||||
struct gptimer_watchdog_priv {
|
||||
struct gptimer_regs *regs;
|
||||
struct gptimer_timer_regs *timer;
|
||||
int timerno;
|
||||
};
|
||||
|
||||
struct gptimer_watchdog_priv bsp_watchdogs[1];
|
||||
int bsp_watchdog_count = 0;
|
||||
|
||||
int bsp_watchdog_init(void)
|
||||
{
|
||||
int timercnt;
|
||||
|
||||
if (!LEON3_Timer_Regs)
|
||||
return 0;
|
||||
|
||||
/* Get Watchdogs in system, this is implemented for one GPTIMER core
|
||||
* only.
|
||||
*
|
||||
* First watchdog is a special case, we can get the first timer core by
|
||||
* looking at LEON3_Timer_Regs, the watchdog within a timer core is
|
||||
* always the last timer. Unfortunately we can not know it the watchdog
|
||||
* functionality is available or not, we assume that it is if we
|
||||
* reached this function.
|
||||
*/
|
||||
bsp_watchdogs[0].regs = (struct gptimer_regs *)LEON3_Timer_Regs;
|
||||
|
||||
/* Find Timer that has watchdog functionality */
|
||||
timercnt = bsp_watchdogs[0].regs->cfg & 0x7;
|
||||
if (timercnt < 2) /* First timer system clock timer */
|
||||
return 0;
|
||||
|
||||
bsp_watchdogs[0].timerno = timercnt - 1;
|
||||
bsp_watchdogs[0].timer = &bsp_watchdogs[0].regs->timer[bsp_watchdogs[0].timerno];
|
||||
|
||||
bsp_watchdog_count = 1;
|
||||
return bsp_watchdog_count;
|
||||
}
|
||||
|
||||
void bsp_watchdog_reload(int watchdog, unsigned int reload_value)
|
||||
{
|
||||
if (bsp_watchdog_count == 0)
|
||||
bsp_watchdog_init();
|
||||
|
||||
if (bsp_watchdog_count <= watchdog)
|
||||
return;
|
||||
|
||||
/* Kick watchdog, and clear interrupt pending bit */
|
||||
bsp_watchdogs[watchdog].timer->reload = reload_value;
|
||||
bsp_watchdogs[watchdog].timer->ctrl =
|
||||
(GPTIMER_TIMER_CTRL_LD | GPTIMER_TIMER_CTRL_EN) |
|
||||
(bsp_watchdogs[watchdog].timer->ctrl & ~(1<<4));
|
||||
}
|
||||
|
||||
void bsp_watchdog_stop(int watchdog)
|
||||
{
|
||||
if (bsp_watchdog_count == 0)
|
||||
bsp_watchdog_init();
|
||||
|
||||
if (bsp_watchdog_count <= watchdog)
|
||||
return;
|
||||
|
||||
/* Stop watchdog timer */
|
||||
bsp_watchdogs[watchdog].timer->ctrl = 0;
|
||||
}
|
||||
|
||||
/* Use watchdog timer to reset system */
|
||||
void bsp_watchdog_system_reset(void)
|
||||
{
|
||||
sparc_disable_interrupts();
|
||||
bsp_watchdog_reload(0, 1);
|
||||
}
|
||||
544
bsps/sparc/shared/btimer/gptimer.c
Normal file
544
bsps/sparc/shared/btimer/gptimer.c
Normal file
@@ -0,0 +1,544 @@
|
||||
/* 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 <drvmgr/ambapp_bus.h>
|
||||
#include <grlib.h>
|
||||
#include <bsp/gptimer.h>
|
||||
#include <bsp/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
|
||||
|
||||
/* 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 = (struct gptimer_priv *)malloc(size);
|
||||
if ( !priv )
|
||||
return DRVMGR_NOMEM;
|
||||
memset(priv, 0, size);
|
||||
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 = ®s->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,
|
||||
};
|
||||
77
bsps/sparc/shared/btimer/tlib.c
Normal file
77
bsps/sparc/shared/btimer/tlib.c
Normal file
@@ -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 <bsp/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;
|
||||
}
|
||||
453
bsps/sparc/shared/btimer/tlib_ckinit.c
Normal file
453
bsps/sparc/shared/btimer/tlib_ckinit.c
Normal file
@@ -0,0 +1,453 @@
|
||||
/*
|
||||
* 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 <bsp/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.
|
||||
*/
|
||||
|
||||
/* "simple timecounter" interface. Only for non-SMP. */
|
||||
static const struct ops ops_simple;
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void tlib_clock_shutdown_hardware(void)
|
||||
{
|
||||
if (priv.tlib_tick) {
|
||||
tlib_stop(priv.tlib_tick);
|
||||
priv.tlib_tick = NULL;
|
||||
}
|
||||
if (priv.ops->shutdown_hardware) {
|
||||
priv.ops->shutdown_hardware();
|
||||
}
|
||||
}
|
||||
|
||||
/** 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,
|
||||
};
|
||||
|
||||
/** 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,
|
||||
};
|
||||
|
||||
#if defined(LEON3)
|
||||
/** 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,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(LEON3)
|
||||
/** 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_support_shutdown_hardware() \
|
||||
tlib_clock_shutdown_hardware()
|
||||
|
||||
#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 */
|
||||
|
||||
Reference in New Issue
Block a user