diff options
-rw-r--r-- | c/src/lib/libbsp/sparc/Makefile.am | 6 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/leon2/Makefile.am | 6 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/leon2/include/bsp.h | 1 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/leon2/preinstall.am | 4 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/leon3/Makefile.am | 14 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/leon3/include/bsp.h | 1 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/leon3/preinstall.am | 4 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h | 3 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/include/tlib.h | 169 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/timer/gptimer.c | 503 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/timer/tlib.c | 77 | ||||
-rw-r--r-- | c/src/lib/libbsp/sparc/shared/timer/tlib_ckinit.c | 264 |
12 files changed, 1049 insertions, 3 deletions
diff --git a/c/src/lib/libbsp/sparc/Makefile.am b/c/src/lib/libbsp/sparc/Makefile.am index 59a212c624..67b62ca585 100644 --- a/c/src/lib/libbsp/sparc/Makefile.am +++ b/c/src/lib/libbsp/sparc/Makefile.am @@ -28,6 +28,12 @@ EXTRA_DIST += shared/amba/ambapp_names.c EXTRA_DIST += shared/amba/ambapp_old.c EXTRA_DIST += shared/amba/ambapp_show.c +# Clock Driver and Timer Library +EXTRA_DIST += shared/include/tlib.h +EXTRA_DIST += shared/timer/gptimer.c +EXTRA_DIST += shared/timer/tlib.c +EXTRA_DIST += shared/timer/tlib_ckinit.c + # PCI bus EXTRA_DIST += shared/include/pci.h EXTRA_DIST += shared/pci/pcifinddevice.c diff --git a/c/src/lib/libbsp/sparc/leon2/Makefile.am b/c/src/lib/libbsp/sparc/leon2/Makefile.am index bb30517dd0..e9dcfd8e75 100644 --- a/c/src/lib/libbsp/sparc/leon2/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon2/Makefile.am @@ -102,6 +102,12 @@ libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_parent.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_old.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_names.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_show.c + +# Clock Driver and Timer Library +include_HEADERS += ../../sparc/shared/include/tlib.h +libbsp_a_SOURCES += ../../sparc/shared/timer/gptimer.c +libbsp_a_SOURCES += ../../sparc/shared/timer/tlib.c + # PCI libbsp_a_SOURCES += pci/pci.c ../../sparc/shared/pci/pcifinddevice.c # RASTA Kit diff --git a/c/src/lib/libbsp/sparc/leon2/include/bsp.h b/c/src/lib/libbsp/sparc/leon2/include/bsp.h index dce8bea661..d669d8910e 100644 --- a/c/src/lib/libbsp/sparc/leon2/include/bsp.h +++ b/c/src/lib/libbsp/sparc/leon2/include/bsp.h @@ -226,6 +226,7 @@ int cchip1_register(void); * image bigger. */ #define AMBAPPBUS_INFO_AVAIL /* AMBAPP Bus driver */ +#define GPTIMER_INFO_AVAIL /* GPTIMER Timer driver */ #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/leon2/preinstall.am b/c/src/lib/libbsp/sparc/leon2/preinstall.am index 7260f63767..10a0bbcd1f 100644 --- a/c/src/lib/libbsp/sparc/leon2/preinstall.am +++ b/c/src/lib/libbsp/sparc/leon2/preinstall.am @@ -169,6 +169,10 @@ $(PROJECT_INCLUDE)/grlib.h: ../../sparc/shared/include/grlib.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grlib.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grlib.h +$(PROJECT_INCLUDE)/tlib.h: ../../sparc/shared/include/tlib.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tlib.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/tlib.h + $(PROJECT_INCLUDE)/i2cmst.h: ../../sparc/shared/include/i2cmst.h $(PROJECT_INCLUDE)/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/i2cmst.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/i2cmst.h diff --git a/c/src/lib/libbsp/sparc/leon3/Makefile.am b/c/src/lib/libbsp/sparc/leon3/Makefile.am index 6e6f04d2ef..f57b24591b 100644 --- a/c/src/lib/libbsp/sparc/leon3/Makefile.am +++ b/c/src/lib/libbsp/sparc/leon3/Makefile.am @@ -66,14 +66,22 @@ libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_parent.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_old.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_names.c libbsp_a_SOURCES += ../../sparc/shared/amba/ambapp_show.c + +# Clock Driver and Timer Library +include_HEADERS += ../../sparc/shared/include/tlib.h +libbsp_a_SOURCES += ../../sparc/shared/timer/gptimer.c +libbsp_a_SOURCES += ../../sparc/shared/timer/tlib.c +libbsp_a_SOURCES += ../../sparc/shared/timer/tlib_ckinit.c +# non-Driver Manager Clock Implementation +libbsp_a_SOURCES += clock/ckinit.c +libbsp_a_SOURCES += ../../shared/clockdrv_shell.h + # console libbsp_a_SOURCES += ../../shared/console-termios.c libbsp_a_SOURCES += console/console.c # debugio libbsp_a_SOURCES += console/printk_support.c -# clock -libbsp_a_SOURCES += clock/ckinit.c -libbsp_a_SOURCES += ../../shared/clockdrv_shell.h + # IRQ include_bsp_HEADERS += \ ../../shared/include/irq-generic.h \ diff --git a/c/src/lib/libbsp/sparc/leon3/include/bsp.h b/c/src/lib/libbsp/sparc/leon3/include/bsp.h index 9239d2bc9b..37dfedb1e9 100644 --- a/c/src/lib/libbsp/sparc/leon3/include/bsp.h +++ b/c/src/lib/libbsp/sparc/leon3/include/bsp.h @@ -255,6 +255,7 @@ extern const unsigned char LEON3_irq_to_cpu[32]; * image bigger. */ #define AMBAPPBUS_INFO_AVAIL /* AMBAPP Bus driver */ +#define GPTIMER_INFO_AVAIL /* GPTIMER Timer driver */ #ifdef __cplusplus } diff --git a/c/src/lib/libbsp/sparc/leon3/preinstall.am b/c/src/lib/libbsp/sparc/leon3/preinstall.am index 32525609c6..99c8b22604 100644 --- a/c/src/lib/libbsp/sparc/leon3/preinstall.am +++ b/c/src/lib/libbsp/sparc/leon3/preinstall.am @@ -105,6 +105,10 @@ $(PROJECT_INCLUDE)/grlib.h: ../../sparc/shared/include/grlib.h $(PROJECT_INCLUDE $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/grlib.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/grlib.h +$(PROJECT_INCLUDE)/tlib.h: ../../sparc/shared/include/tlib.h $(PROJECT_INCLUDE)/$(dirstamp) + $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/tlib.h +PREINSTALL_FILES += $(PROJECT_INCLUDE)/tlib.h + $(PROJECT_INCLUDE)/bsp/irq-generic.h: ../../shared/include/irq-generic.h $(PROJECT_INCLUDE)/bsp/$(dirstamp) $(INSTALL_DATA) $< $(PROJECT_INCLUDE)/bsp/irq-generic.h PREINSTALL_FILES += $(PROJECT_INCLUDE)/bsp/irq-generic.h diff --git a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h index a1a47b1698..23e8cbd3a9 100644 --- a/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h +++ b/c/src/lib/libbsp/sparc/shared/include/drvmgr/ambapp_bus.h @@ -27,6 +27,9 @@ extern "C" { #define DRIVER_AMBAPP_ID(vendor, device) \ DRIVER_ID(DRVMGR_BUS_TYPE_AMBAPP, ((((vendor) & 0xff) << 16) | ((device) & 0xfff))) +/*** Gaisler Hardware Device Driver IDs ***/ +#define DRIVER_AMBAPP_GAISLER_GPTIMER_ID DRIVER_AMBAPP_ID(VENDOR_GAISLER, GAISLER_GPTIMER) + struct amba_dev_id { unsigned short vendor; unsigned short device; diff --git a/c/src/lib/libbsp/sparc/shared/include/tlib.h b/c/src/lib/libbsp/sparc/shared/include/tlib.h new file mode 100644 index 0000000000..75ca6997f5 --- /dev/null +++ b/c/src/lib/libbsp/sparc/shared/include/tlib.h @@ -0,0 +1,169 @@ +/* + * Timer Library (TLIB) + * + * The Library rely on timer drivers, the timer presented by the + * timer driver must look like a down-counter timer, which generates + * interrupt (if configured) when underflown. + * + * If Timer hardware is an up-counter the Timer driver must recalculate + * into values that would match as if it was a down-counter. + * + * 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. + */ + +struct tlib_dev; + +typedef void (*tlib_isr_t)(void *data); + +struct tlib_drv { + /*** Functions ***/ + void (*reset)(struct tlib_dev *hand); + void (*get_freq)( + struct tlib_dev *hand, + unsigned int *basefreq, + unsigned int *tickrate); + int (*set_freq)(struct tlib_dev *hand, unsigned int tickrate); + void (*irq_reg)(struct tlib_dev *hand, tlib_isr_t func, void *data); + void (*irq_unreg)(struct tlib_dev *hand, tlib_isr_t func,void *data); + void (*start)(struct tlib_dev *hand, int once); + void (*stop)(struct tlib_dev *hand); + void (*restart)(struct tlib_dev *hand); + void (*get_counter)(struct tlib_dev *hand, unsigned int *counter); + int (*custom)(struct tlib_dev *hand, int cmd, void *arg); +}; + +struct tlib_dev { + struct tlib_dev *next; + char status; /* 0=closed, 1=open, 2=timer started */ + char index; /* Timer Index */ + tlib_isr_t isr_func; + void *isr_data; + struct tlib_drv *drv; +}; + +#ifdef RTEMS_DRVMGR_STARTUP +/* Clock Driver Timer register function. Only used when the TLIB-Clock + * driver is used. A specific Timer is registered as the System Clock + * timer. + */ +extern void Clock_timer_register(int timer_number); +#endif + +/* Register Timer. Called by Timer Drivers in order to register + * a Timer to the Timer Library. The registration order determines + * the Timer Number used in tlib_open() to identify a specific + * Timer. + */ +extern int tlib_dev_reg(struct tlib_dev *newdev); + +/* Allocate a Timer. + * + * A Timer handle is returned identifying the timer in later calls. + */ +extern void *tlib_open(int timer_no); + +/* Close Timer */ +extern void tlib_close(void *hand); + +/* Returns Number of Timers currently registered to Timer Library */ +extern int tlib_ntimer(void); + +static inline void tlib_reset(void *hand) +{ + struct tlib_dev *dev = hand; + + dev->drv->reset(dev); +} +/* Get Frequencies: + * - Base Frequency (unchangable base freq rate of timer, prescaler, clkinput) + * - Current Tick Rate [in multiples of Base Frequency] + */ +static inline void tlib_get_freq( + void *hand, + unsigned int *basefreq, + unsigned int *tickrate) +{ + struct tlib_dev *dev = hand; + + dev->drv->get_freq(dev, basefreq, tickrate); +} + +/* Set current Tick Rate in number of "Base-Frequency ticks" */ +static inline int tlib_set_freq(void *hand, unsigned int tickrate) +{ + struct tlib_dev *dev = hand; + + return dev->drv->set_freq(dev, tickrate); +} + +/* Register ISR at Timer ISR */ +static inline void tlib_irq_unregister(void *hand) +{ + struct tlib_dev *dev = hand; + + if ( dev->isr_func ) { + dev->drv->irq_unreg(dev, dev->isr_func, dev->isr_data); + dev->isr_func = NULL; + } +} + +/* Register ISR at Timer ISR */ +static inline void tlib_irq_register(void *hand, tlib_isr_t func, void *data) +{ + struct tlib_dev *dev = hand; + + /* Unregister previous ISR if installed */ + tlib_irq_unregister(hand); + dev->isr_func = func; + dev->isr_data = data; + dev->drv->irq_reg(dev, func, data); +} + +/* Start Timer, ISRs will be generated if enabled. + * + * once determines if timer should restart (=0) on underflow automatically, + * or stop when underflow is reached (=1). + */ +static inline void tlib_start(void *hand, int once) +{ + struct tlib_dev *dev = hand; + + dev->drv->start(dev, once); +} + +/* Stop Timer, no more ISRs will be generated */ +static inline void tlib_stop(void *hand) +{ + struct tlib_dev *dev = hand; + + dev->drv->stop(dev); +} + +/* Restart/Reload Timer, may be usefull if a Watchdog Timer */ +static inline void tlib_restart(void *hand) +{ + struct tlib_dev *dev = hand; + + dev->drv->restart(dev); +} + +/* Get current counter value (since last tick) */ +static inline void tlib_get_counter(void *hand, unsigned int *counter) +{ + struct tlib_dev *dev = hand; + + dev->drv->get_counter(dev, counter); +} + +/* Do a custom operation */ +static inline void tlib_custom(void *hand, int cmd, void *arg) +{ + struct tlib_dev *dev = hand; + + dev->drv->custom(dev, cmd, arg); +} 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 = ®s->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; +} |