genode/base-mb/include/xilinx/xps_timer.h
2012-01-03 15:35:05 +01:00

400 lines
10 KiB
C++

/*
* \brief Driver for the Xilinx LogiCORE XPS Timer/Counter IP 1.02
* \author Martin stein
* \date 2010-06-22
*/
/*
* Copyright (C) 2010-2012 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _INCLUDE__DEVICES__XILINX_XPS_TIMER_H_
#define _INCLUDE__DEVICES__XILINX_XPS_TIMER_H_
#include <cpu/config.h>
namespace Xilinx {
/**
* Driver for the Xilinx LogiCORE XPS Timer/Counter IP 1.02
*/
class Xps_timer
{
public:
/**
* CPU dependencies
*/
typedef Cpu::word_t word_t;
typedef Cpu::addr_t addr_t;
typedef Cpu::size_t size_t;
typedef Cpu::uint32_t uint32_t;
/**
* MMIO register
*/
typedef uint32_t Register;
/**
* Constructor, resets timer, overwrites timer value with '0'
*/
inline Xps_timer(addr_t const &base);
/**
* Destructor, resets timer, overwrites timer value with '0'
*/
inline ~Xps_timer();
/**
* Overwrite timer value with X>0, count downwards to '0', set the
* IRQ output to '1' for one cycle and simultaneously start counting
* downwards again from X, and so on ...
*
* \param value the value X
*/
inline void run_periodic(unsigned int const &value);
/**
* Overwrite timer value with X>0, count downwards to '0', set the
* IRQ output to '1' for one cycle and simultaneously start counting
* downwards from max_value() to '0', and so on ...
*
* \param value the value X
*/
inline void run_circulating(unsigned int const &value);
/**
* Overwrite timer value with X>0, count downwards to '0', set the
* IRQ output to '1' for one cycle, timer value remains '0'
*
* \param value the value X
*/
inline void run_oneshot(unsigned int const &value);
/**
* Prepare a 'run_oneshot()'-like run that shall be triggered with
* simple means. Useful for starting the timer out of assembly-code.
*
* \param value native time-value used to assess the delay
* of the timer IRQ as of the triggering
* \param start_val at this address the start value gets deposited
* \param start_reg at this address an address X gets deposited
* writing the start value to X later starts the
* timer as prepared
*/
inline void prepare_oneshot(unsigned int const & value,
volatile Register * & start_reg,
Register & start_val);
/**
* Current timer value
*/
inline unsigned int value();
/**
* Return the timers current value and determine if the timer has hit '0'
* before the returned value and after the last time we started it or called
* 'period_value' on it. Called during non-periodic runs 'rolled_over'
* becomes 'true' if value is '0' and 'false' otherwise
*
* Enable exclusive access only to this function to ensure correct behavior!
* This function delays the timer about the duration of a few cpu cycles!
*/
inline unsigned int period_value(bool * const & rolled_over);
/**
* Size of the MMIO provided by the timer device
*/
static inline size_t size();
/**
* Maximum timer value
*/
static inline unsigned int max_value();
/**
* Converting a native time value to milliseconds
*/
static inline unsigned int native_to_msec(unsigned int const &v);
/**
* Converting milliseconds to a native time value
*/
static inline unsigned int msec_to_native(unsigned int const &v);
/**
* Converting a native time value to microseconds
*/
static inline unsigned int native_to_usec(unsigned int const &v);
/**
* Converting microseconds to a native time value
*/
static inline unsigned int usec_to_native(unsigned int const &v);
private:
/**
* General constraints
*/
enum {
WORD_SIZE = sizeof(word_t),
BYTE_WIDTH = Cpu::BYTE_WIDTH,
FREQUENCY_PER_US = 62,
};
/**
* Registers
*/
enum {
/* Control/status register */
RTCSR0_OFFSET = 0*WORD_SIZE,
/* Load register, written to RTCR when RTCSR[LOAD]='1' */
RTLR0_OFFSET = 1*WORD_SIZE,
/* On timer/counter register the counting is done */
RTCR0_OFFSET = 2*WORD_SIZE,
/* Respectively for the second timer/counter module */
RTCSR1_OFFSET = 4*WORD_SIZE,
RTLR1_OFFSET = 5*WORD_SIZE,
RTCR1_OFFSET = 6*WORD_SIZE,
MMIO_SIZE = 8*WORD_SIZE,
/* r/w '0': generate timer mode
* r/w '1': capture timer mode */
RTCSR_MDT_LSHIFT = 0,
/* r/w '0': count upward mode
* r/w '1': count downward mode */
RTCSR_UDT_LSHIFT = 1,
/* r/w '0': external generate signal disabled mode
* r/w '1': external generate signal enabled mode */
RTCSR_GENT_LSHIFT = 2,
/* r/w '0': external capture trigger disabled mode
* r/w '1': external capture trigger enabled mode */
RTCSR_CAPT_LSHIFT = 3,
/* r/w '0': hold values mode
* r/w '0': auto reload (generate timer) / overwrite (capture timer) mode */
RTCSR_ARHT_LSHIFT = 4,
/* w/r '0': disable loading mode
* w/r '1': loading timer mode (RTCR=RTLR) */
RTCSR_LOAD_LSHIFT = 5,
/* w/r '0': throw no IRQ mode (doesn't affect RTCSR[TINT])
* w/r '1': throw IRQ on 0-1-edge at RTCSR[TINT] mode */
RTCSR_ENIT_LSHIFT = 6,
/* w/r '0': don't count (RTCR remains constant)
* w/r '1': count on RTCR */
RTCSR_ENT_LSHIFT = 7,
/* r '0': no IRQ has occured
* r '1': IRQ has occured
* w '0': no effect
* w '1': RTCSR[TINT]=0 */
RTCSR_TINT_LSHIFT = 8,
/* r/w '0': pulse width modulation disabled mode
* r/w '1': pulse width modulation enabled mode */
RTCSR_PWM_LSHIFT = 9,
/* w/r '0': nothing
* w/r '1': RTCSR[ENT]='1' for all timer/counter modules */
RTCSR_ENALL_LSHIFT = 10,
};
/**
* Controls for RTCSR
*/
enum {
RUN_ONCE = 0
| 0 << RTCSR_MDT_LSHIFT
| 1 << RTCSR_UDT_LSHIFT
| 0 << RTCSR_CAPT_LSHIFT
| 0 << RTCSR_GENT_LSHIFT
| 0 << RTCSR_ARHT_LSHIFT
| 0 << RTCSR_LOAD_LSHIFT
| 1 << RTCSR_ENIT_LSHIFT
| 1 << RTCSR_ENT_LSHIFT
| 1 << RTCSR_TINT_LSHIFT
| 0 << RTCSR_PWM_LSHIFT
| 0 << RTCSR_ENALL_LSHIFT
,
STOP_N_LOAD = 0
| 0 << RTCSR_MDT_LSHIFT
| 1 << RTCSR_UDT_LSHIFT
| 0 << RTCSR_CAPT_LSHIFT
| 0 << RTCSR_GENT_LSHIFT
| 0 << RTCSR_ARHT_LSHIFT
| 1 << RTCSR_LOAD_LSHIFT
| 0 << RTCSR_ENIT_LSHIFT
| 0 << RTCSR_ENT_LSHIFT
| 0 << RTCSR_TINT_LSHIFT
| 0 << RTCSR_PWM_LSHIFT
| 0 << RTCSR_ENALL_LSHIFT
,
RUN_PERIODIC = RUN_ONCE
| 1 << RTCSR_ARHT_LSHIFT
,
STOP_N_RESET = STOP_N_LOAD
| 1 << RTCSR_TINT_LSHIFT
,
};
/**
* Absolute register addresses
*/
volatile Register *const _rtcsr0;
volatile Register *const _rtlr0;
volatile Register *const _rtcr0;
volatile Register *const _rtcsr1;
volatile Register *const _rtlr1;
volatile Register *const _rtcr1;
};
}
Xilinx::Xps_timer::Xps_timer(addr_t const &base)
:
_rtcsr0((Register *)(base + RTCSR0_OFFSET)),
_rtlr0((Register *)(base + RTLR0_OFFSET)),
_rtcr0((Register *)(base + RTCR0_OFFSET)),
_rtcsr1((Register *)(base + RTCSR1_OFFSET)),
_rtlr1((Register *)(base + RTLR1_OFFSET)),
_rtcr1((Register *)(base + RTCR1_OFFSET))
{
*_rtcsr0 = STOP_N_RESET;
*_rtcsr1 = STOP_N_RESET;
*_rtlr0 = 0;
}
Xilinx::Xps_timer::size_t Xilinx::Xps_timer::size()
{
return (size_t)MMIO_SIZE;
}
unsigned int Xilinx::Xps_timer::max_value() { return ((unsigned int)~0); }
void Xilinx::Xps_timer::prepare_oneshot(unsigned int const & value,
volatile Register * & start_reg,
Register & start_val)
{
*_rtcsr0 = STOP_N_LOAD;
*_rtlr0 = value;
start_reg = _rtcsr0;
start_val = RUN_ONCE;
}
void Xilinx::Xps_timer::run_circulating(unsigned int const &value)
{
*_rtcsr0 = STOP_N_LOAD;
*_rtlr0 = value;
*_rtcsr0 = RUN_PERIODIC;
*_rtlr0 = max_value();
}
void Xilinx::Xps_timer::run_periodic(unsigned int const &value)
{
*_rtcsr0 = STOP_N_LOAD;
*_rtlr0 = value;
*_rtcsr0 = RUN_PERIODIC;
}
void Xilinx::Xps_timer::run_oneshot(unsigned int const &value)
{
*_rtcsr0 = STOP_N_LOAD;
*_rtlr0 = value;
*_rtcsr0 = RUN_ONCE;
}
unsigned int Xilinx::Xps_timer::value() { return *_rtcr0; }
unsigned int Xilinx::Xps_timer::period_value(bool * const &rolled_over)
{
if(!(*_rtcsr0 & (1 << RTCSR_ARHT_LSHIFT))){
/* this is no periodic run */
unsigned int const v = *_rtcr0;
*rolled_over = !(v);
return value();
}
/* 2 measurements are necessary to ensure that
* 'rolled_over' and the returned value are fit together
* because we can not halt the timer or read both simulanously */
unsigned int const v1 = *_rtcr0;
*rolled_over = (bool)(*_rtcsr0 & (1 << RTCSR_TINT_LSHIFT));
unsigned int const v2 = *_rtcr0;
if(*rolled_over) {
/* v2 must be a value the timer had after rolling over, so restart
* the timer with the current value but RTCSR[TINT] reset */
unsigned int const initial_rtlr = *_rtlr0;
unsigned int const restart_n_reset = *_rtcsr0 | (1 << RTCSR_TINT_LSHIFT);
*_rtlr0 = *_rtcr0; // timer gets delayed about the
*_rtcsr0 = restart_n_reset; // duration of these two operations
*_rtlr0 = initial_rtlr;
return v2;
}
/* v1 must be a value that the timer had before rolling
* over, so we don't have to reset the "rolled over" status even
* if the timer has rolled over till now */
return v1;
}
Xilinx::Xps_timer::~Xps_timer()
{
*_rtcsr0 = STOP_N_RESET;
*_rtcsr1 = STOP_N_RESET;
}
unsigned int Xilinx::Xps_timer::native_to_msec(unsigned int const &v)
{
return 1000*native_to_usec(v);
}
unsigned int Xilinx::Xps_timer::msec_to_native(unsigned int const &v)
{
return 1000*usec_to_native(v);
}
unsigned int Xilinx::Xps_timer::native_to_usec(unsigned int const &v)
{
return v/FREQUENCY_PER_US;
}
unsigned int Xilinx::Xps_timer::usec_to_native(unsigned int const &v)
{
return v*FREQUENCY_PER_US;
}
#endif /* _INCLUDE__DEVICES__XILINX_XPS_TIMER_H_ */