--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Hardware independent timer driver (implementation)
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:23:30 bernie
+ * Import drv/timer module.
+ *
+ */
+
+#include "hw.h"
+#include "kdebug.h"
+#include "timer.h"
+
+#ifdef CONFIG_KERN_SIGNALS
+#include <kern/proc.h>
+#endif
+
+#if (ARCH & ARCH_EMUL)
+
+# error To be recoded
+
+#elif defined(__AVR__)
+# include "timer_avr.h"
+#elif defined(__IAR_SYSTEMS_ICC) || defined(__IAR_SYSTEMS_ICC__) /* 80C196 */
+# include "timer_i196.h"
+#elif defined (__m56800__)
+# include "timer_dsp56k.h"
+#else
+# error Unknown system
+#endif
+
+
+/*! Number of available timers */
+#define MAX_TIMERS 4
+
+
+//! Master system clock (1ms accuracy)
+volatile time_t _clock;
+
+static Timer soft_timers[MAX_TIMERS]; /*!< Pool of Timer structures */
+static List timers_pool; /*!< Pool of free timers */
+REGISTER static List timers_queue; /*!< Active timers */
+
+
+/*!
+ * Return a new timer picking and removing it from the available
+ * timers pool. Return NULL if no more timers are available.
+ */
+Timer *timer_new(void)
+{
+ Timer *timer;
+ cpuflags_t flags;
+
+ DISABLE_IRQSAVE(flags);
+
+ /* Should never happen */
+ if (ISLISTEMPTY(&timers_pool))
+ {
+ ENABLE_IRQRESTORE(flags);
+ DB(kprintf("Tmrspool empty\n");)
+ return NULL;
+ }
+
+ /* Get a timer from the free pool */
+ timer = (Timer *)timers_pool.head;
+ REMOVE((Node *)timer);
+
+ ENABLE_IRQRESTORE(flags);
+
+ return timer;
+}
+
+
+/*!
+ * Delete a timer, putting it in the available timers queue.
+ */
+void timer_delete(Timer *timer)
+{
+ cpuflags_t flags;
+ DISABLE_IRQSAVE(flags);
+ ADDHEAD(&timers_pool, &timer->link);
+ ENABLE_IRQRESTORE(flags);
+}
+
+
+/*!
+ * Add the specified timer to the software timer service queue.
+ * When the delay indicated by the timer expires, the timer
+ * device will execute the event associated with it.
+ *
+ * \note Interrupt safe
+ */
+void timer_add(Timer *timer)
+{
+ Timer *node;
+ cpuflags_t flags;
+
+ DISABLE_IRQSAVE(flags);
+
+ /* Calculate expiration time for this timer */
+ timer->tick = _clock + timer->delay;
+
+ /* Search for the first node whose expiration time is
+ * greater than the timer we want to add.
+ */
+ node = (Timer *)timers_queue.head;
+ while (node->link.succ)
+ {
+ /* Stop just after the insert point */
+ if (node->tick > timer->tick)
+ break;
+
+ /* Go to next node */
+ node = (Timer *)node->link.succ;
+ }
+
+ /* Enqueue timer request into the list */
+ INSERTBEFORE((Node *)timer, (Node *)node);
+
+ ENABLE_IRQRESTORE(flags);
+}
+
+
+/*!
+ * Remove a timer from the timer queue before it has expired
+ */
+Timer *timer_abort(Timer *timer)
+{
+ cpuflags_t flags;
+ DISABLE_IRQSAVE(flags);
+ REMOVE((Node *)timer);
+ ENABLE_IRQRESTORE(flags);
+
+ return timer;
+}
+
+
+/*!
+ * Wait for the specified amount of time (expressed in ms)
+ */
+void timer_delay(time_t time)
+{
+#ifdef CONFIG_KERN_SIGNALS
+ Timer t;
+
+ ASSERT(!sig_check(SIG_SINGLE));
+ timer_set_event_signal(&t, proc_current(), SIG_SINGLE);
+ timer_set_delay(&t, time);
+ timer_add(&t);
+ sig_wait(SIG_SINGLE);
+#else
+ time_t start = timer_gettick();
+
+ while (timer_gettick() - start < time) { /* nop */ }
+#endif
+}
+
+/*!
+ * Wait for the specified amount of time (expressed in microseconds)
+ */
+void timer_udelay(utime_t usec_delay)
+{
+ if (usec_delay > 1000)
+ {
+ timer_delay(usec_delay / 1000);
+ usec_delay %= 1000;
+ }
+
+ // FIXME: This multiplication is too slow at run-time. We should try and move it
+ // to compile-time by exposing the TIMER_HW_HPTICKS_PER_SEC in the header
+ // file.
+ hptime_t start = timer_hw_hpread();
+ hptime_t delay = (uint32_t)usec_delay * TIMER_HW_HPTICKS_PER_SEC / 1000000ul;
+
+ while (timer_hw_hpread() - start < delay)
+ {}
+}
+
+
+/*!
+ * Timer interrupt handler. Find soft timers expired and
+ * trigger corresponding events.
+ */
+DEFINE_TIMER_ISR
+{
+ /* With the Metrowerks compiler, the only way to force the compiler generate
+ an interrupt service routine is to put a pragma directive within the function
+ body. */
+ #ifdef __MWERKS__
+ #pragma interrupt saveall
+ #endif
+
+ Timer *timer;
+
+ timer_hw_irq();
+
+ /* Update the master ms counter */
+ ++_clock;
+
+ /*
+ * Check the first timer request in the list and process
+ * it when it has expired. Repeat this check until the
+ * first node has not yet expired. Since the list is sorted
+ * by expiry time, all the following requests are guaranteed
+ * to expire later.
+ */
+ while ((timer = (Timer *)timers_queue.head)->link.succ)
+ {
+ /* This request in list has not yet expired? */
+ if (_clock < timer->tick)
+ break;
+
+ /* Retreat the expired timer */
+ REMOVE((Node *)timer);
+
+ /* Execute the associated event */
+ DOEVENT_INTR(&timer->expire);
+ }
+}
+
+
+
+/*!
+ * Initialize timer
+ */
+void timer_init(void)
+{
+ int i;
+
+ INITLIST(&timers_queue);
+ INITLIST(&timers_pool);
+
+ /* Init all software timers in the free pool */
+ for (i = 0; i < MAX_TIMERS; i++)
+ ADDTAIL(&timers_pool, (Node *)&soft_timers[i]);
+
+ _clock = 0;
+
+ timer_hw_init();
+}
+
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000 Bernardo Innocenti <bernie@develer.com>
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Hardware independent timer driver (interface)
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:23:30 bernie
+ * Import drv/timer module.
+ *
+ */
+#ifndef DRV_TIMER_H
+#define DRV_TIMER_H
+
+#include "cpu.h"
+#include "compiler.h"
+#include <mware/list.h>
+#include <kern/event.h>
+
+/*! Number of timer ticks per second. */
+#define TICKS_PER_SEC 1000
+
+typedef struct Timer
+{
+ Node link; /*!< Link into timers queue */
+ time_t delay; /*!< Timer delay in ms */
+ time_t tick; /*!< Timer will expire at this tick */
+ Event expire; /*!< Event to execute when the timer expires */
+} Timer;
+
+extern void timer_init(void);
+extern Timer *timer_new(void);
+extern void timer_delete(Timer *timer);
+extern void timer_add(Timer *timer);
+extern Timer *timer_abort(Timer *timer);
+extern void timer_delay(time_t time);
+extern void timer_udelay(utime_t utime);
+extern inline time_t timer_gettick(void);
+extern inline time_t timer_gettick_irq(void);
+
+#ifdef CONFIG_KERN_SIGNALS
+/*! Set the timer so that it sends a signal when it expires */
+INLINE void timer_set_event_signal(Timer* timer, struct Process* proc, sigset_t sigs)
+{
+ INITEVENT_SIG(&timer->expire, proc, sigs);
+}
+#endif
+
+/*! Set the timer so that it calls an user hook when it expires */
+INLINE void timer_set_event_softint(Timer* timer, Hook func, void* user_data)
+{
+ INITEVENT_INT(&timer->expire, func, user_data);
+}
+
+/*! Set the timer delay (the time before the event will be triggered) */
+INLINE void timer_set_delay(Timer* timer, time_t delay)
+{
+ timer->delay = delay;
+}
+
+
+/*!
+ * Return the system tick counter (expressed in ms)
+ * This function must disable interrupts on 8/16bit CPUs because the
+ * clock variable is larger than the processor word size and can't
+ * be copied atomically.
+ */
+extern inline time_t timer_gettick(void)
+{
+ extern volatile time_t _clock;
+
+ time_t result;
+ cpuflags_t flags;
+
+ DISABLE_IRQSAVE(flags);
+ result = _clock;
+ ENABLE_IRQRESTORE(flags);
+
+ return result;
+}
+
+
+/*!
+ * Like \c timer_gettick, faster version to be called
+ * from interrupt context only.
+ */
+extern inline time_t timer_gettick_irq(void)
+{
+ extern volatile time_t _clock;
+
+ return _clock;
+}
+
+#endif /* DRV_TIMER_H */
+
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2000 Bernardo Innocenti
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Low-level timer module for AVR
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:23:30 bernie
+ * Import drv/timer module.
+ *
+ */
+
+#ifndef DRV_TIMER_AVR_H
+#define DRV_TIMER_AVR_H
+
+#include <avr/wdt.h>
+
+# define TIMER_RETRIGGER /* Not needed, timer retriggers automatically */
+
+ /*!
+ * System timer: additional division after the prescaler
+ * 12288000 / 64 / 192 (0..191) = 1 ms
+ */
+# define OCR_DIVISOR 191
+
+ /*! HW dependent timer initialization */
+#if defined(CONFIG_TIMER_ON_TIMER0)
+
+# define TIMER_INIT \
+ do { \
+ DISABLE_INTS; \
+ \
+ /* Reset Timer flags */ \
+ TIFR = BV(OCF0) | BV(TOV0); \
+ \
+ /* Setup Timer/Counter interrupt */ \
+ ASSR = 0x00; /* internal system clock */ \
+ TCCR0 = BV(WGM01) | BV(CS02); /* Clear on Compare match & prescaler = 64 */ \
+ TCNT0 = 0x00; /* initialization of Timer/Counter */ \
+ OCR0 = OCR_DIVISOR; /* Timer/Counter Output Compare Register */ \
+ \
+ /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */ \
+ TIMSK &= ~BV(TOIE0); \
+ TIMSK |= BV(OCIE0); \
+ \
+ ENABLE_INTS; \
+ } while (0)
+
+#elif defined(CONFIG_TIMER_ON_TIMER1_OVERFLOW)
+
+# define TIMER_INIT \
+ do { \
+ DISABLE_INTS; \
+ \
+ /* Reset Timer overflow flag */ \
+ TIFR |= BV(TOV1); \
+ \
+ /* Fast PWM mode, 24 kHz, no prescaling */ \
+ TCCR1A |= BV(WGM11); \
+ TCCR1A &= ~BV(WGM10); \
+ TCCR1B |= BV(WGM12) | BV(CS10); \
+ TCCR1B &= ~(BV(WGM13) | BV(CS11) | BV(CS12)); \
+ \
+ TCNT1 = 0x00; /* initialization of Timer/Counter */ \
+ \
+ /* Enable timer interrupt: Timer/Counter1 Overflow */ \
+ TIMSK |= BV(TOIE1); \
+ \
+ ENABLE_INTS; \
+ } while (0)
+
+#elif defined(CONFIG_TIMER_ON_TIMER2)
+
+# define TIMER_INIT \
+ do { \
+ DISABLE_INTS; \
+ \
+ /* Reset Timer flags */ \
+ TIFR = BV(OCF2) | BV(TOV2); \
+ \
+ /* Setup Timer/Counter interrupt */ \
+ TCCR2 = BV(WGM21) | BV(CS21) | BV(CS20); \
+ /* Clear on Compare match & prescaler = 64, internal sys clock */ \
+ TCNT2 = 0x00; /* initialization of Timer/Counter */ \
+ OCR2 = OCR_DIVISOR; /* Timer/Counter Output Compare Register */ \
+ \
+ /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */ \
+ TIMSK &= ~BV(TOIE2); \
+ TIMSK |= BV(OCIE2); \
+ \
+ ENABLE_INTS; \
+ } while (0)
+
+#else
+# error Choose witch timer to use with CONFIG_TIMER_ON_TIMERx
+#endif /* CONFIG_TIMER_ON_TIMERx */
+
+
+#if defined(CONFIG_TIMER_ON_TIMER1_OVERFLOW)
+
+ #define DEFINE_TIMER_ISR \
+ static void timer_handler(void)
+
+ /*
+ * Timer 1 overflow irq handler.
+ */
+ SIGNAL(SIG_OVERFLOW1)
+ {
+ /*!
+ * How many overflow we have to count before calling the true timer handler.
+ * If timer overflow is at 24 kHz, with a value of 24 we have 1 ms between
+ * each call.
+ */
+ #define TIMER1_OVF_COUNT 24
+
+ static uint8_t count = TIMER1_OVF_COUNT;
+
+ count--;
+ if (!count)
+ {
+ timer_handler();
+ count = TIMER1_OVF_COUNT;
+ }
+ }
+
+#elif defined (CONFIG_TIMER_ON_TIMER0)
+
+ #define DEFINE_TIMER_ISR \
+ SIGNAL(SIG_OUTPUT_COMPARE0)
+
+#elif defined(CONFIG_TIMER_ON_TIMER2)
+
+ #define DEFINE_TIMER_ISR \
+ SIGNAL(SIG_OUTPUT_COMPARE2)
+
+#else
+# error Choose witch timer to use with CONFIG_TIMER_ON_TIMERx
+#endif /* CONFIG_TIMER_ON_TIMERx */
+
+#endif /* DRV_TIMER_AVR_H */
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright 2004 Giovanni Bajo
+ * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Giovanni Bajo <rasky@develer.com>
+ *
+ * \brief Driver module for DSP56K
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:23:30 bernie
+ * Import drv/timer module.
+ *
+ */
+
+#ifndef DRV_TIMER_DSP56K_H
+#define DRV_TIMER_DSP56K_H
+
+#include <DSP56F807.h>
+#include <compiler.h>
+
+//! The system timer for events is currently TMRA0. We prefer A/B over C/D because
+// A/B share the pin with the quadrature decoder module, and we do not need
+// pins for our system timer.
+// If you want to change this setting, you need also to modify the IRQ vector table.
+#define REG_SYSTEM_TIMER (REG_TIMER_A + 0)
+#define SYSTEM_TIMER_IRQ_VECTOR 42 /* TMRA0 */
+
+#define TIMER_PRESCALER 16
+
+//! Frequency of the hardware high precision timer
+#define TIMER_HW_HPTICKS_PER_SEC (IPBUS_FREQ / TIMER_PRESCALER)
+
+//! Type of time expressed in ticks of the hardware high precision timer
+typedef uint16_t hptime_t;
+
+static void timer_hw_init(void)
+{
+ uint16_t compare;
+
+ // Clear compare flag status and enable interrupt on compare
+ REG_SYSTEM_TIMER->SCR &= ~REG_TIMER_SCR_TCF;
+ REG_SYSTEM_TIMER->SCR |= REG_TIMER_SCR_TCFIE;
+
+ // Calculate the compare value needed to generate an interrupt exactly
+ // TICKS_PER_SEC times each second (usually, every millisecond). Check that
+ // the calculation is accurate, otherwise there is a precision error
+ // (probably the prescaler is too big or too small).
+ compare = TIMER_HW_HPTICKS_PER_SEC / TICKS_PER_SEC;
+ ASSERT((uint32_t)compare * TICKS_PER_SEC == IPBUS_FREQ / TIMER_PRESCALER);
+ REG_SYSTEM_TIMER->CMP1 = compare;
+
+ // The value for reload (at initializationa and after compare is met) is zero
+ REG_SYSTEM_TIMER->LOAD = 0;
+
+ // Set the interrupt priority
+ irq_setpriority(SYSTEM_TIMER_IRQ_VECTOR, IRQ_PRIORITY_SYSTEM_TIMER);
+
+ // Small preprocessor trick to generate the REG_TIMER_CTRL_PRIMARY_IPBYNN macro
+ // needed to set the prescaler
+ #define PP_CAT2(x,y) x ## y
+ #define PP_CAT(x,y) PP_CAT2(x,y)
+ #define REG_CONTROL_PRESCALER PP_CAT(REG_TIMER_CTRL_PRIMARY_IPBY, TIMER_PRESCALER)
+
+ // Setup the counter and start counting
+ REG_SYSTEM_TIMER->CTRL =
+ REG_TIMER_CTRL_MODE_RISING | // count rising edges (normal)
+ REG_CONTROL_PRESCALER | // frequency (IPbus / TIMER_PRESCALER)
+ REG_TIMER_CTRL_LENGTH; // up to CMP1, then reload
+}
+
+INLINE void timer_hw_irq(void)
+{
+ // Clear the overflow flag so that we are ready for another interrupt
+ REG_SYSTEM_TIMER->SCR &= ~REG_TIMER_SCR_TCF;
+}
+
+INLINE hptime_t timer_hw_hpread(void)
+{
+ return REG_SYSTEM_TIMER->CNTR;
+}
+
+void system_timer_isr(void);
+
+#define DEFINE_TIMER_ISR \
+ void system_timer_isr(void)
+
+#endif /* DRV_TIMER_DSP56_H */
--- /dev/null
+/*!
+ * \file
+ * <!--
+ * Copyright (C) 2000 Bernardo Innocenti
+ * Copyright (C) 2003,2004 Develer S.r.l. (http://www.develer.com/)
+ * All Rights Reserved.
+ * -->
+ *
+ * \version $Id$
+ *
+ * \author Bernardo Innocenti <bernie@develer.com>
+ *
+ * \brief Low-level timer module for AVR
+ */
+
+/*
+ * $Log$
+ * Revision 1.1 2004/05/23 18:23:30 bernie
+ * Import drv/timer module.
+ *
+ */
+
+#ifndef TIMER_I196_H
+#define TIMER_I196_H
+
+ /*!
+ * Retrigger TIMER2, adjusting the time to account for
+ * the interrupt prologue latency.
+ */
+# define TIMER_RETRIGGER (TIMER2 -= TICKS_RATE)
+
+# define TIMER_INIT \
+ TIMER2 = (65535 - TICKS_RATE); \
+ INT_MASK1 |= INT1F_T2OVF; \
+ \
+ DISABLE_INTS; \
+ WSR = 1; \
+ IOC3 |= IOC3F_T2_ENA; \
+ WSR = 0; \
+ ENABLE_INTS
+
+#define DEFINE_TIMER_ISR \
+ INTERRUPT(0x38) void TM2_OVFL_interrupt(void); \
+ INTERRUPT(0x38) void TM2_OVFL_interrupt(void)
+
+#endif /* DRV_TIMER_I196_H */