mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-23 15:32:25 +00:00
hw core: merge Kernel::Clock and Kernel::Timer
With this, we get rid of platform specific timer interfaces. The new Timer class does the same as the old Clock class and has a generic interface. The old Timer class was merely used by the old Clock class. Also, we get rid of having only one timer instance which we tell with each method call for which CPU it shall be done. Instead now each Cpu object has its own Timer member that knows the CPU it works for. Also, rename all "tics" to "ticks". Fixes #2347
This commit is contained in:
parent
b58b69515c
commit
4d3d4ecca0
@ -55,7 +55,7 @@ SRC_CC += kernel/ipc_node.cc
|
||||
SRC_CC += kernel/irq.cc
|
||||
SRC_CC += kernel/pd.cc
|
||||
SRC_CC += kernel/cpu.cc
|
||||
SRC_CC += kernel/clock.cc
|
||||
SRC_CC += kernel/timer.cc
|
||||
SRC_CC += kernel/object.cc
|
||||
SRC_CC += init_main_thread.cc
|
||||
SRC_CC += capability.cc
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <kernel/irq.h>
|
||||
#include <kernel/pd.h>
|
||||
#include <pic.h>
|
||||
#include <timer.h>
|
||||
#include <hw/assert.h>
|
||||
|
||||
/* base-internal includes */
|
||||
@ -133,14 +132,14 @@ void Cpu_idle::_main() { while (1) { Genode::Cpu::wait_for_interrupt(); } }
|
||||
*********/
|
||||
|
||||
void Cpu::set_timeout(Timeout * const timeout, time_t const duration_us) {
|
||||
_clock.set_timeout(timeout, _clock.us_to_tics(duration_us)); }
|
||||
_timer.set_timeout(timeout, _timer.us_to_ticks(duration_us)); }
|
||||
|
||||
|
||||
time_t Cpu::timeout_age_us(Timeout const * const timeout) const {
|
||||
return _clock.timeout_age_us(timeout); }
|
||||
return _timer.timeout_age_us(timeout); }
|
||||
|
||||
|
||||
time_t Cpu::timeout_max_us() const { return _clock.timeout_max_us(); }
|
||||
time_t Cpu::timeout_max_us() const { return _timer.timeout_max_us(); }
|
||||
|
||||
|
||||
void Cpu::schedule(Job * const job)
|
||||
@ -162,19 +161,19 @@ bool Cpu::interrupt(unsigned const irq_id)
|
||||
Cpu_job & Cpu::schedule()
|
||||
{
|
||||
/* update scheduler */
|
||||
time_t quota = _clock.update_time();
|
||||
time_t quota = _timer.update_time();
|
||||
Job & old_job = scheduled_job();
|
||||
old_job.exception(id());
|
||||
_clock.process_timeouts();
|
||||
_timer.process_timeouts();
|
||||
_scheduler.update(quota);
|
||||
|
||||
/* get new job */
|
||||
Job & new_job = scheduled_job();
|
||||
quota = _scheduler.head_quota();
|
||||
|
||||
_clock.set_timeout(this, quota);
|
||||
_timer.set_timeout(this, quota);
|
||||
|
||||
_clock.schedule_timeout();
|
||||
_timer.schedule_timeout();
|
||||
|
||||
/* switch to new job */
|
||||
switch_to(new_job);
|
||||
@ -184,11 +183,11 @@ Cpu_job & Cpu::schedule()
|
||||
}
|
||||
|
||||
|
||||
Cpu::Cpu(unsigned const id, Timer * const timer)
|
||||
Cpu::Cpu(unsigned const id)
|
||||
:
|
||||
_id(id), _clock(_id, timer), _idle(this),
|
||||
_id(id), _timer(_id), _idle(this),
|
||||
_scheduler(&_idle, _quota(), _fill()),
|
||||
_ipi_irq(*this), _timer_irq(timer->interrupt_id(_id), *this)
|
||||
_ipi_irq(*this), _timer_irq(_timer.interrupt_id(), *this)
|
||||
{ }
|
||||
|
||||
|
||||
@ -206,27 +205,8 @@ Cpu * Cpu_pool::cpu(unsigned const id) const
|
||||
|
||||
Cpu_pool::Cpu_pool()
|
||||
{
|
||||
/*
|
||||
* The timer frequency should allow a good accuracy on the smallest
|
||||
* timeout syscall value (1 us).
|
||||
*/
|
||||
assert(_timer.tics_to_us(1) < 1 ||
|
||||
_timer.tics_to_us(_timer.max_value()) == _timer.max_value());
|
||||
|
||||
/*
|
||||
* The maximum measurable timeout is also the maximum age of a timeout
|
||||
* installed by the timeout syscall. The timeout-age syscall returns a
|
||||
* bogus value for older timeouts. A user that awoke from waiting for a
|
||||
* timeout might not be schedulable in the same super period anymore.
|
||||
* However, if the user can't manage to read the timeout age during the
|
||||
* next super period, it's a bad configuration or the users fault. That
|
||||
* said, the maximum timeout should be at least two times the super
|
||||
* period).
|
||||
*/
|
||||
assert(_timer.tics_to_us(_timer.max_value()) > 2 * cpu_quota_us);
|
||||
|
||||
for (unsigned id = 0; id < NR_OF_CPUS; id++) {
|
||||
new (_cpus[id]) Cpu(id, &_timer); }
|
||||
new (_cpus[id]) Cpu(id); }
|
||||
}
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#define _CORE__KERNEL__CPU_H_
|
||||
|
||||
/* core includes */
|
||||
#include <kernel/clock.h>
|
||||
#include <kernel/timer.h>
|
||||
#include <cpu.h>
|
||||
#include <kernel/cpu_scheduler.h>
|
||||
#include <kernel/irq.h>
|
||||
@ -269,21 +269,21 @@ class Kernel::Cpu : public Genode::Cpu, public Irq::Pool, private Timeout
|
||||
};
|
||||
|
||||
unsigned const _id;
|
||||
Clock _clock;
|
||||
Timer _timer;
|
||||
Cpu_idle _idle;
|
||||
Cpu_scheduler _scheduler;
|
||||
Ipi _ipi_irq;
|
||||
Irq _timer_irq; /* timer irq implemented as empty event */
|
||||
Irq _timer_irq; /* timer IRQ implemented as empty event */
|
||||
|
||||
unsigned _quota() const { return _clock.us_to_tics(cpu_quota_us); }
|
||||
unsigned _fill() const { return _clock.us_to_tics(cpu_fill_us); }
|
||||
unsigned _quota() const { return _timer.us_to_ticks(cpu_quota_us); }
|
||||
unsigned _fill() const { return _timer.us_to_ticks(cpu_fill_us); }
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct object for CPU 'id' with scheduling timer 'timer'
|
||||
* Construct object for CPU 'id'
|
||||
*/
|
||||
Cpu(unsigned const id, Timer * const timer);
|
||||
Cpu(unsigned const id);
|
||||
|
||||
/**
|
||||
* Initialize primary cpu object
|
||||
@ -335,14 +335,16 @@ class Kernel::Cpu : public Genode::Cpu, public Irq::Pool, private Timeout
|
||||
|
||||
unsigned id() const { return _id; }
|
||||
Cpu_scheduler * scheduler() { return &_scheduler; }
|
||||
|
||||
time_t us_to_ticks(time_t const us) const { return _timer.us_to_ticks(us); };
|
||||
|
||||
unsigned timer_interrupt_id() const { return _timer.interrupt_id(); }
|
||||
};
|
||||
|
||||
class Kernel::Cpu_pool
|
||||
{
|
||||
private:
|
||||
|
||||
Timer _timer;
|
||||
|
||||
/*
|
||||
* Align to machine word size, otherwise load/stores might fail on some
|
||||
* platforms.
|
||||
@ -369,11 +371,13 @@ class Kernel::Cpu_pool
|
||||
*/
|
||||
Cpu * executing_cpu() const { return cpu(Cpu::executing_id()); }
|
||||
|
||||
/*
|
||||
* Accessors
|
||||
*/
|
||||
|
||||
Timer * timer() { return &_timer; }
|
||||
template <typename FUNC>
|
||||
void for_each_cpu(FUNC const &func) const
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof(_cpus)/sizeof(_cpus[i]); i++) {
|
||||
func(*cpu(i));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _CORE__KERNEL__CPU_H_ */
|
||||
|
@ -153,8 +153,8 @@ size_t Thread::_core_to_kernel_quota(size_t const quota) const
|
||||
{
|
||||
using Genode::Cpu_session;
|
||||
using Genode::sizet_arithm_t;
|
||||
size_t const tics = cpu_pool()->timer()->us_to_tics(Kernel::cpu_quota_us);
|
||||
return Cpu_session::quota_lim_downscale<sizet_arithm_t>(quota, tics);
|
||||
size_t const ticks = _cpu->us_to_ticks(Kernel::cpu_quota_us);
|
||||
return Cpu_session::quota_lim_downscale<sizet_arithm_t>(quota, ticks);
|
||||
}
|
||||
|
||||
|
||||
|
@ -124,7 +124,7 @@ class Kernel::Thread
|
||||
void _call();
|
||||
|
||||
/**
|
||||
* Return amount of timer tics that 'quota' is worth
|
||||
* Return amount of timer ticks that 'quota' is worth
|
||||
*/
|
||||
size_t _core_to_kernel_quota(size_t const quota) const;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief A clock manages a continuous time and timeouts on it
|
||||
* \brief A timer manages a continuous time and timeouts on it
|
||||
* \author Martin Stein
|
||||
* \date 2016-03-23
|
||||
*/
|
||||
@ -12,38 +12,33 @@
|
||||
*/
|
||||
|
||||
/* Core includes */
|
||||
#include <kernel/clock.h>
|
||||
#include <kernel/timer.h>
|
||||
#include <kernel/configuration.h>
|
||||
#include <hw/assert.h>
|
||||
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
time_t Clock::us_to_tics(time_t const us) const
|
||||
{
|
||||
return _timer->us_to_tics(us);
|
||||
}
|
||||
|
||||
|
||||
time_t Clock::timeout_age_us(Timeout const * const timeout) const
|
||||
time_t Timer::timeout_age_us(Timeout const * const timeout) const
|
||||
{
|
||||
time_t const age = (time_t)_time - timeout->_start;
|
||||
return _timer->tics_to_us(age);
|
||||
return _ticks_to_us(age);
|
||||
}
|
||||
|
||||
|
||||
time_t Clock::timeout_max_us() const
|
||||
time_t Timer::timeout_max_us() const
|
||||
{
|
||||
return _timer->tics_to_us(_timer->max_value());
|
||||
return _ticks_to_us(_max_value());
|
||||
}
|
||||
|
||||
|
||||
bool Clock::_time_overflow(time_t const duration) const
|
||||
bool Timer::_time_overflow(time_t const duration) const
|
||||
{
|
||||
return duration > ~(time_t)0 - _time;
|
||||
}
|
||||
|
||||
|
||||
void Clock::set_timeout(Timeout * const timeout, time_t const duration)
|
||||
void Timer::set_timeout(Timeout * const timeout, time_t const duration)
|
||||
{
|
||||
/*
|
||||
* Remove timeout if it is already in use. Timeouts may get overridden as
|
||||
@ -73,7 +68,7 @@ void Clock::set_timeout(Timeout * const timeout, time_t const duration)
|
||||
}
|
||||
|
||||
|
||||
void Clock::schedule_timeout()
|
||||
void Timer::schedule_timeout()
|
||||
{
|
||||
/* get the timeout with the nearest end time */
|
||||
Timeout * timeout = _timeout_list[_time_period].first();
|
||||
@ -84,15 +79,15 @@ void Clock::schedule_timeout()
|
||||
/* install timeout at timer hardware */
|
||||
time_t const duration = (time_t)timeout->_end - _time;
|
||||
_last_timeout_duration = duration;
|
||||
_timer->start_one_shot(duration, _cpu_id);
|
||||
_start_one_shot(duration);
|
||||
}
|
||||
|
||||
|
||||
time_t Clock::update_time()
|
||||
time_t Timer::update_time()
|
||||
{
|
||||
/* determine how much time has passed */
|
||||
time_t const old_value = _last_timeout_duration;
|
||||
time_t const new_value = _timer->value(_cpu_id);
|
||||
time_t const new_value = _value();
|
||||
time_t const duration = old_value > new_value ? old_value - new_value : 1;
|
||||
|
||||
/* is this the end of the current period? */
|
||||
@ -117,7 +112,7 @@ time_t Clock::update_time()
|
||||
}
|
||||
|
||||
|
||||
void Clock::process_timeouts()
|
||||
void Timer::process_timeouts()
|
||||
{
|
||||
/*
|
||||
* Walk through timeouts until the first whose end time is in the future.
|
||||
@ -136,6 +131,23 @@ void Clock::process_timeouts()
|
||||
}
|
||||
|
||||
|
||||
Clock::Clock(unsigned const cpu_id, Timer * const timer)
|
||||
:
|
||||
_cpu_id(cpu_id), _timer(timer) { }
|
||||
Timer::Timer(unsigned cpu_id) : _cpu_id(cpu_id), _driver(cpu_id)
|
||||
{
|
||||
/*
|
||||
* The timer frequency should allow a good accuracy on the smallest
|
||||
* timeout syscall value (1 us).
|
||||
*/
|
||||
assert(_ticks_to_us(1) < 1 || _ticks_to_us(_max_value()) == _max_value());
|
||||
|
||||
/*
|
||||
* The maximum measurable timeout is also the maximum age of a timeout
|
||||
* installed by the timeout syscall. The timeout-age syscall returns a
|
||||
* bogus value for older timeouts. A user that awoke from waiting for a
|
||||
* timeout might not be schedulable in the same super period anymore.
|
||||
* However, if the user can't manage to read the timeout age during the
|
||||
* next super period, it's a bad configuration or the users fault. That
|
||||
* said, the maximum timeout should be at least two times the super
|
||||
* period).
|
||||
*/
|
||||
assert(_ticks_to_us(_max_value()) > 2 * cpu_quota_us);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* \brief A clock manages a continuous time and timeouts on it
|
||||
* \brief A timer manages a continuous time and timeouts on it
|
||||
* \author Martin Stein
|
||||
* \date 2016-03-23
|
||||
*/
|
||||
@ -11,8 +11,8 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__KERNEL__CLOCK_H_
|
||||
#define _CORE__KERNEL__CLOCK_H_
|
||||
#ifndef _CORE__KERNEL__TIMER_H_
|
||||
#define _CORE__KERNEL__TIMER_H_
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
@ -21,12 +21,12 @@
|
||||
#include <util/list.h>
|
||||
|
||||
/* Core includes */
|
||||
#include <timer.h>
|
||||
#include <timer_driver.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
class Timeout;
|
||||
class Clock;
|
||||
class Timer;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,7 +34,7 @@ namespace Kernel
|
||||
*/
|
||||
class Kernel::Timeout : public Genode::List<Timeout>::Element
|
||||
{
|
||||
friend class Clock;
|
||||
friend class Timer;
|
||||
|
||||
private:
|
||||
|
||||
@ -54,14 +54,16 @@ class Kernel::Timeout : public Genode::List<Timeout>::Element
|
||||
};
|
||||
|
||||
/**
|
||||
* A clock manages a continuous time and timeouts on it
|
||||
* A timer manages a continuous time and timeouts on it
|
||||
*/
|
||||
class Kernel::Clock
|
||||
class Kernel::Timer
|
||||
{
|
||||
private:
|
||||
|
||||
using Driver = Timer_driver;
|
||||
|
||||
unsigned const _cpu_id;
|
||||
Timer * const _timer;
|
||||
Driver _driver;
|
||||
time_t _time = 0;
|
||||
bool _time_period = false;
|
||||
Genode::List<Timeout> _timeout_list[2];
|
||||
@ -69,39 +71,34 @@ class Kernel::Clock
|
||||
|
||||
bool _time_overflow(time_t const duration) const;
|
||||
|
||||
void _start_one_shot(time_t const ticks);
|
||||
|
||||
time_t _ticks_to_us(time_t const ticks) const;
|
||||
|
||||
time_t _value();
|
||||
|
||||
time_t _max_value() const;
|
||||
|
||||
public:
|
||||
|
||||
Clock(unsigned const cpu_id, Timer * const timer);
|
||||
Timer(unsigned cpu_id);
|
||||
|
||||
/**
|
||||
* Set-up timer according to the current timeout schedule
|
||||
*/
|
||||
void schedule_timeout();
|
||||
|
||||
/**
|
||||
* Update time and work off expired timeouts
|
||||
*
|
||||
* \return time that passed since the last scheduling
|
||||
*/
|
||||
time_t update_time();
|
||||
void process_timeouts();
|
||||
|
||||
/**
|
||||
* Set-up 'timeout' to trigger at time + 'duration'
|
||||
*/
|
||||
void set_timeout(Timeout * const timeout, time_t const duration);
|
||||
|
||||
/**
|
||||
* Return native time value that equals the given microseconds 'us'
|
||||
*/
|
||||
time_t us_to_tics(time_t const us) const;
|
||||
time_t us_to_ticks(time_t const us) const;
|
||||
|
||||
/**
|
||||
* Return microseconds that passed since the last set-up of 'timeout'
|
||||
*/
|
||||
time_t timeout_age_us(Timeout const * const timeout) const;
|
||||
|
||||
time_t timeout_max_us() const;
|
||||
|
||||
unsigned interrupt_id() const;
|
||||
|
||||
static void init_cpu_local();
|
||||
};
|
||||
|
||||
#endif /* _CORE__KERNEL__CLOCK_H_ */
|
||||
#endif /* _CORE__KERNEL__TIMER_H_ */
|
@ -27,6 +27,7 @@
|
||||
#include <pic.h>
|
||||
#include <kernel/kernel.h>
|
||||
#include <translation_table.h>
|
||||
#include <kernel/cpu.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/crt0.h>
|
||||
@ -89,6 +90,8 @@ Platform::Platform()
|
||||
_io_port_alloc(core_mem_alloc()),
|
||||
_irq_alloc(core_mem_alloc())
|
||||
{
|
||||
struct Kernel_resource : Exception { };
|
||||
|
||||
_core_mem_alloc.virt_alloc()->add_range(VIRT_ADDR_SPACE_START,
|
||||
VIRT_ADDR_SPACE_SIZE);
|
||||
_core_virt_regions().for_each([this] (Hw::Memory_region const & r) {
|
||||
@ -102,8 +105,18 @@ Platform::Platform()
|
||||
|
||||
/* make all non-kernel interrupts available to the interrupt allocator */
|
||||
for (unsigned i = 0; i < Kernel::Pic::NR_OF_IRQ; i++) {
|
||||
if (i == Timer::interrupt_id(i) || i == Pic::IPI)
|
||||
bool kernel_resource = false;
|
||||
Kernel::cpu_pool()->for_each_cpu([&] (Kernel::Cpu const &cpu) {
|
||||
if (i == cpu.timer_interrupt_id()) {
|
||||
kernel_resource = true;
|
||||
}
|
||||
});
|
||||
if (i == Pic::IPI) {
|
||||
kernel_resource = true;
|
||||
}
|
||||
if (kernel_resource) {
|
||||
continue;
|
||||
}
|
||||
_irq_alloc.add_range(i, 1);
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ void Kernel::Cpu::init(Kernel::Pic &pic)
|
||||
perf_counter()->enable();
|
||||
|
||||
/* enable timer interrupt */
|
||||
pic.unmask(Timer::interrupt_id(id()), id());
|
||||
pic.unmask(_timer.interrupt_id(), id());
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <core_parent.h> /* for 'Core_service' type */
|
||||
#include <map_local.h>
|
||||
#include <vm_root.h>
|
||||
#include <platform.h>
|
||||
|
||||
extern Genode::addr_t _vt_host_context_ptr;
|
||||
extern Genode::addr_t _vt_host_entry;
|
||||
|
@ -38,6 +38,6 @@ void Kernel::Cpu::init(Kernel::Pic &pic/*, Kernel::Pd & core_pd, Genode::Board &
|
||||
perf_counter()->enable();
|
||||
|
||||
/* enable timer interrupt */
|
||||
pic.unmask(Timer::interrupt_id(id()), id());
|
||||
pic.unmask(_timer.interrupt_id(), id());
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,6 @@ void Kernel::Cpu::init(Kernel::Pic &pic)
|
||||
perf_counter()->enable();
|
||||
|
||||
/* enable timer interrupt */
|
||||
pic.unmask(Timer::interrupt_id(id()), id());
|
||||
pic.unmask(_timer.interrupt_id(), id());
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* \brief Timer implementation specific to Cortex A9
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2016-01-07
|
||||
*/
|
||||
|
||||
@ -11,32 +12,76 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* core includes */
|
||||
#include <kernel/timer.h>
|
||||
#include <platform.h>
|
||||
#include <timer.h>
|
||||
|
||||
using Genode::Timer;
|
||||
using Genode::Mmio;
|
||||
using Genode::Platform;
|
||||
using namespace Genode;
|
||||
using namespace Kernel;
|
||||
|
||||
Timer::Timer()
|
||||
|
||||
Timer_driver::Timer_driver(unsigned)
|
||||
: Mmio(Platform::mmio_to_virt(Board::Cpu_mmio::PRIVATE_TIMER_MMIO_BASE))
|
||||
{
|
||||
write<Control::Timer_enable>(0);
|
||||
}
|
||||
|
||||
|
||||
void Timer::start_one_shot(time_t const tics, unsigned const)
|
||||
void Timer::_start_one_shot(time_t const ticks)
|
||||
{
|
||||
enum { PRESCALER = Board::CORTEX_A9_PRIVATE_TIMER_DIV - 1 };
|
||||
|
||||
/* reset timer */
|
||||
write<Interrupt_status::Event>(1);
|
||||
Control::access_t control = 0;
|
||||
Control::Irq_enable::set(control, 1);
|
||||
Control::Prescaler::set(control, PRESCALER);
|
||||
write<Control>(control);
|
||||
_driver.write<Driver::Interrupt_status::Event>(1);
|
||||
Driver::Control::access_t control = 0;
|
||||
Driver::Control::Irq_enable::set(control, 1);
|
||||
Driver::Control::Prescaler::set(control, PRESCALER);
|
||||
_driver.write<Driver::Control>(control);
|
||||
|
||||
/* load timer and start decrementing */
|
||||
write<Load>(tics);
|
||||
write<Control::Timer_enable>(1);
|
||||
_driver.write<Driver::Load>(ticks);
|
||||
_driver.write<Driver::Control::Timer_enable>(1);
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::_ticks_to_us(time_t const ticks) const
|
||||
{
|
||||
/*
|
||||
* If we would do the translation with one division and
|
||||
* multiplication over the whole argument, we would loose
|
||||
* microseconds granularity although the timer frequency would
|
||||
* allow for such granularity. Thus, we treat the most significant
|
||||
* half and the least significant half of the argument separate.
|
||||
* Each half is shifted to the best bit position for the
|
||||
* translation, then translated, and then shifted back.
|
||||
*/
|
||||
static_assert(Driver::TICS_PER_MS >= 1000, "Bad TICS_PER_MS value");
|
||||
enum {
|
||||
HALF_WIDTH = (sizeof(time_t) << 2),
|
||||
MSB_MASK = ~0UL << HALF_WIDTH,
|
||||
LSB_MASK = ~0UL >> HALF_WIDTH,
|
||||
MSB_RSHIFT = 10,
|
||||
LSB_LSHIFT = HALF_WIDTH - MSB_RSHIFT,
|
||||
};
|
||||
time_t const msb = (((ticks >> MSB_RSHIFT)
|
||||
* 1000) / Driver::TICS_PER_MS) << MSB_RSHIFT;
|
||||
time_t const lsb = ((((ticks & LSB_MASK) << LSB_LSHIFT)
|
||||
* 1000) / Driver::TICS_PER_MS) >> LSB_LSHIFT;
|
||||
return (msb & MSB_MASK) | lsb;
|
||||
}
|
||||
|
||||
|
||||
unsigned Timer::interrupt_id() const {
|
||||
return Board::Cpu_mmio::PRIVATE_TIMER_IRQ; }
|
||||
|
||||
|
||||
time_t Timer::us_to_ticks(time_t const us) const {
|
||||
return (us / 1000) * Driver::TICS_PER_MS; }
|
||||
|
||||
|
||||
time_t Timer::_value() {
|
||||
return _driver.read<Driver::Counter>(); }
|
||||
|
||||
|
||||
time_t Timer::_max_value() const {
|
||||
return (Driver::Load::access_t)~0; }
|
||||
|
@ -1,132 +0,0 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Martin stein
|
||||
* \date 2011-12-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__CORTEX_A9__TIMER_H_
|
||||
#define _CORE__SPEC__CORTEX_A9__TIMER_H_
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
/* core includes */
|
||||
#include <board.h>
|
||||
|
||||
namespace Genode { class Timer; }
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*/
|
||||
class Genode::Timer : public Mmio
|
||||
{
|
||||
private:
|
||||
|
||||
using time_t = Kernel::time_t;
|
||||
|
||||
enum {
|
||||
TICS_PER_MS =
|
||||
Board::CORTEX_A9_PRIVATE_TIMER_CLK /
|
||||
Board::CORTEX_A9_PRIVATE_TIMER_DIV / 1000
|
||||
};
|
||||
|
||||
/**
|
||||
* Load value register
|
||||
*/
|
||||
struct Load : Register<0x0, 32> { };
|
||||
|
||||
/**
|
||||
* Counter value register
|
||||
*/
|
||||
struct Counter : Register<0x4, 32> { };
|
||||
|
||||
/**
|
||||
* Timer control register
|
||||
*/
|
||||
struct Control : Register<0x8, 32>
|
||||
{
|
||||
struct Timer_enable : Bitfield<0,1> { }; /* enable counting */
|
||||
struct Irq_enable : Bitfield<2,1> { }; /* unmask interrupt */
|
||||
struct Prescaler : Bitfield<8,8> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Timer interrupt status register
|
||||
*/
|
||||
struct Interrupt_status : Register<0xc, 32>
|
||||
{
|
||||
struct Event : Bitfield<0,1> { }; /* if counter hit zero */
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
Timer();
|
||||
|
||||
/**
|
||||
* Return kernel name of timer interrupt
|
||||
*/
|
||||
static unsigned interrupt_id(unsigned const) {
|
||||
return Board::Cpu_mmio::PRIVATE_TIMER_IRQ; }
|
||||
|
||||
/**
|
||||
* Start single timeout run
|
||||
*
|
||||
* \param tics delay of timer interrupt
|
||||
*/
|
||||
void start_one_shot(time_t const tics, unsigned const);
|
||||
|
||||
time_t tics_to_us(time_t const tics) const
|
||||
{
|
||||
/*
|
||||
* If we would do the translation with one division and
|
||||
* multiplication over the whole argument, we would loose
|
||||
* microseconds granularity although the timer frequency would
|
||||
* allow for such granularity. Thus, we treat the most significant
|
||||
* half and the least significant half of the argument separate.
|
||||
* Each half is shifted to the best bit position for the
|
||||
* translation, then translated, and then shifted back.
|
||||
*/
|
||||
static_assert(TICS_PER_MS >= 1000, "Bad TICS_PER_MS value");
|
||||
enum {
|
||||
TICS_WIDTH = sizeof(time_t) * 8,
|
||||
TICS_HALF_WIDTH = TICS_WIDTH / 2,
|
||||
|
||||
TICS_MSB_MASK = ~0UL << TICS_HALF_WIDTH,
|
||||
TICS_LSB_MASK = ~0UL >> TICS_HALF_WIDTH,
|
||||
|
||||
TICS_MSB_RSHIFT = 10,
|
||||
TICS_LSB_LSHIFT = TICS_HALF_WIDTH - TICS_MSB_RSHIFT,
|
||||
};
|
||||
time_t const tics_msb = (tics & TICS_MSB_MASK) >> TICS_MSB_RSHIFT;
|
||||
time_t const tics_lsb = (tics & TICS_LSB_MASK) << TICS_LSB_LSHIFT;
|
||||
|
||||
time_t const us_msb = ((tics_msb * 1000) / TICS_PER_MS) << TICS_MSB_RSHIFT;
|
||||
time_t const us_lsb = ((tics_lsb * 1000) / TICS_PER_MS) >> TICS_LSB_LSHIFT;
|
||||
|
||||
return us_msb | us_lsb;
|
||||
}
|
||||
|
||||
time_t us_to_tics(time_t const us) const {
|
||||
return (us / 1000) * TICS_PER_MS; }
|
||||
|
||||
/**
|
||||
* Return current native timer value
|
||||
*/
|
||||
time_t value(unsigned const) { return read<Counter>(); }
|
||||
|
||||
time_t max_value() { return (Load::access_t)~0; }
|
||||
};
|
||||
|
||||
namespace Kernel { using Genode::Timer; }
|
||||
|
||||
#endif /* _CORE__SPEC__CORTEX_A9__TIMER_H_ */
|
67
repos/base-hw/src/core/spec/cortex_a9/timer_driver.h
Normal file
67
repos/base-hw/src/core/spec/cortex_a9/timer_driver.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* \brief Timer implementation specific to Cortex A9
|
||||
* \author Martin stein
|
||||
* \date 2011-12-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIMER_DRIVER_H_
|
||||
#define _TIMER_DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
/* core includes */
|
||||
#include <board.h>
|
||||
|
||||
namespace Kernel { class Timer_driver; }
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*/
|
||||
struct Kernel::Timer_driver : Genode::Mmio
|
||||
{
|
||||
enum {
|
||||
TICS_PER_MS =
|
||||
Board::CORTEX_A9_PRIVATE_TIMER_CLK /
|
||||
Board::CORTEX_A9_PRIVATE_TIMER_DIV / 1000
|
||||
};
|
||||
|
||||
/**
|
||||
* Load value register
|
||||
*/
|
||||
struct Load : Register<0x0, 32> { };
|
||||
|
||||
/**
|
||||
* Counter value register
|
||||
*/
|
||||
struct Counter : Register<0x4, 32> { };
|
||||
|
||||
/**
|
||||
* Timer control register
|
||||
*/
|
||||
struct Control : Register<0x8, 32>
|
||||
{
|
||||
struct Timer_enable : Bitfield<0,1> { }; /* enable counting */
|
||||
struct Irq_enable : Bitfield<2,1> { }; /* unmask interrupt */
|
||||
struct Prescaler : Bitfield<8,8> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Timer interrupt status register
|
||||
*/
|
||||
struct Interrupt_status : Register<0xc, 32>
|
||||
{
|
||||
struct Event : Bitfield<0,1> { }; /* if counter hit zero */
|
||||
};
|
||||
|
||||
Timer_driver(unsigned);
|
||||
};
|
||||
|
||||
#endif /* _TIMER_DRIVER_H_ */
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin stein
|
||||
* \date 2013-01-10
|
||||
*/
|
||||
@ -11,12 +12,30 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* core include */
|
||||
#include <kernel/timer.h>
|
||||
#include <board.h>
|
||||
#include <platform.h>
|
||||
#include <timer.h>
|
||||
|
||||
Genode::Timer::Timer()
|
||||
: Mmio(Platform::mmio_to_virt(Board::MCT_MMIO_BASE)),
|
||||
_tics_per_ms(_calc_tics_per_ms(Board::MCT_CLOCK))
|
||||
using namespace Genode;
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
unsigned Timer::interrupt_id() const
|
||||
{
|
||||
switch (_driver.cpu_id) {
|
||||
case 0: return Board::MCT_IRQ_L0;
|
||||
case 1: return Board::MCT_IRQ_L1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer_driver::Timer_driver(unsigned cpu_id)
|
||||
:
|
||||
Mmio(Platform::mmio_to_virt(Board::MCT_MMIO_BASE)),
|
||||
ticks_per_ms(calc_ticks_per_ms(Board::MCT_CLOCK)),
|
||||
cpu_id(cpu_id)
|
||||
{
|
||||
Mct_cfg::access_t mct_cfg = 0;
|
||||
Mct_cfg::Prescaler::set(mct_cfg, PRESCALER);
|
||||
@ -25,3 +44,45 @@ Genode::Timer::Timer()
|
||||
write<L0_int_enb>(L0_int_enb::Frceie::bits(1));
|
||||
write<L1_int_enb>(L1_int_enb::Frceie::bits(1));
|
||||
}
|
||||
|
||||
|
||||
void Timer::_start_one_shot(time_t const ticks)
|
||||
{
|
||||
switch (_driver.cpu_id) {
|
||||
case 0:
|
||||
_driver.write<Driver::L0_int_cstat::Frcnt>(1);
|
||||
_driver.run_0(0);
|
||||
_driver.acked_write<Driver::L0_frcntb, Driver::L0_wstat::Frcntb>(ticks);
|
||||
_driver.run_0(1);
|
||||
return;
|
||||
case 1:
|
||||
_driver.write<Driver::L1_int_cstat::Frcnt>(1);
|
||||
_driver.run_1(0);
|
||||
_driver.acked_write<Driver::L1_frcntb, Driver::L1_wstat::Frcntb>(ticks);
|
||||
_driver.run_1(1);
|
||||
return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::_value()
|
||||
{
|
||||
switch (_driver.cpu_id) {
|
||||
case 0: return _driver.read<Driver::L0_int_cstat::Frcnt>() ? 0 : _driver.read<Driver::L0_frcnto>();
|
||||
case 1: return _driver.read<Driver::L1_int_cstat::Frcnt>() ? 0 : _driver.read<Driver::L1_frcnto>();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::_ticks_to_us(time_t const ticks) const {
|
||||
return (ticks / _driver.ticks_per_ms) * 1000; }
|
||||
|
||||
|
||||
time_t Timer::us_to_ticks(time_t const us) const {
|
||||
return (us / 1000) * _driver.ticks_per_ms; }
|
||||
|
||||
|
||||
time_t Timer::_max_value() const {
|
||||
return (Driver::L0_frcnto::access_t)~0; }
|
||||
|
@ -1,249 +0,0 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Martin stein
|
||||
* \date 2013-01-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__EXYNOS5__TIMER_H_
|
||||
#define _CORE__SPEC__EXYNOS5__TIMER_H_
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
|
||||
/* core include */
|
||||
#include <board.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
namespace Genode
|
||||
{
|
||||
/**
|
||||
* Timer driver for core
|
||||
*/
|
||||
class Timer;
|
||||
}
|
||||
|
||||
class Genode::Timer : public Mmio
|
||||
{
|
||||
private:
|
||||
|
||||
using time_t = Kernel::time_t;
|
||||
|
||||
enum {
|
||||
PRESCALER = 1,
|
||||
DIV_MUX = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* MCT configuration
|
||||
*/
|
||||
struct Mct_cfg : Register<0x0, 32>
|
||||
{
|
||||
struct Prescaler : Bitfield<0, 8> { };
|
||||
struct Div_mux : Bitfield<8, 3> { };
|
||||
};
|
||||
|
||||
|
||||
/*******************
|
||||
** Local timer 0 **
|
||||
*******************/
|
||||
|
||||
/**
|
||||
* Free running counter buffer
|
||||
*/
|
||||
struct L0_frcntb : Register<0x310, 32> { };
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
struct L0_tcon : Register<0x320, 32>
|
||||
{
|
||||
struct Frc_start : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Expired status
|
||||
*/
|
||||
struct L0_int_cstat : Register<0x330, 32, true>
|
||||
{
|
||||
struct Frcnt : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Interrupt enable
|
||||
*/
|
||||
struct L0_int_enb : Register<0x334, 32>
|
||||
{
|
||||
struct Frceie : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Write status
|
||||
*/
|
||||
struct L0_wstat : Register<0x340, 32, true>
|
||||
{
|
||||
struct Frcntb : Bitfield<2, 1> { };
|
||||
struct Tcon : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
struct L0_frcnto : Register<0x314, 32> { };
|
||||
|
||||
/**
|
||||
* Start and stop counting
|
||||
*/
|
||||
void _run_0(bool const run)
|
||||
{
|
||||
_acked_write<L0_tcon, L0_wstat::Tcon>
|
||||
(L0_tcon::Frc_start::bits(run));
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
** Local timer 1 **
|
||||
*******************/
|
||||
|
||||
/**
|
||||
* Free running counter buffer
|
||||
*/
|
||||
struct L1_frcntb : Register<0x410, 32> { };
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
struct L1_tcon : Register<0x420, 32>
|
||||
{
|
||||
struct Frc_start : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Expired status
|
||||
*/
|
||||
struct L1_int_cstat : Register<0x430, 32, true>
|
||||
{
|
||||
struct Frcnt : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Interrupt enable
|
||||
*/
|
||||
struct L1_int_enb : Register<0x434, 32>
|
||||
{
|
||||
struct Frceie : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Write status
|
||||
*/
|
||||
struct L1_wstat : Register<0x440, 32, true>
|
||||
{
|
||||
struct Frcntb : Bitfield<2, 1> { };
|
||||
struct Tcon : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
struct L1_frcnto : Register<0x414, 32> { };
|
||||
|
||||
/**
|
||||
* Start and stop counting
|
||||
*/
|
||||
void _run_1(bool const run)
|
||||
{
|
||||
_acked_write<L1_tcon, L1_wstat::Tcon>
|
||||
(L1_tcon::Frc_start::bits(run));
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Helper methods **
|
||||
********************/
|
||||
|
||||
/**
|
||||
* Write to reg that replies via ack bit and clear ack bit
|
||||
*/
|
||||
template <typename DEST, typename ACK>
|
||||
void _acked_write(typename DEST::Register_base::access_t const v)
|
||||
{
|
||||
typedef typename DEST::Register_base Dest;
|
||||
typedef typename ACK::Bitfield_base Ack;
|
||||
write<Dest>(v);
|
||||
while (!read<Ack>());
|
||||
write<Ack>(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate amount of tics per ms for specific input clock
|
||||
*
|
||||
* \param clock input clock
|
||||
*/
|
||||
time_t static _calc_tics_per_ms(unsigned const clock) {
|
||||
return clock / (PRESCALER + 1) / (1 << DIV_MUX) / 1000; }
|
||||
|
||||
unsigned const _tics_per_ms;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
/**
|
||||
* Return kernel name of the interrupt of the timer of CPU 'cpu'
|
||||
*/
|
||||
static unsigned interrupt_id(unsigned const cpu)
|
||||
{
|
||||
switch (cpu) {
|
||||
case 0: return Board::MCT_IRQ_L0;
|
||||
case 1: return Board::MCT_IRQ_L1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Timer();
|
||||
|
||||
/**
|
||||
* Raise interrupt of CPU 'cpu' once after timeout 'tics'
|
||||
*/
|
||||
void start_one_shot(time_t const tics, unsigned const cpu)
|
||||
{
|
||||
switch (cpu) {
|
||||
case 0:
|
||||
write<L0_int_cstat::Frcnt>(1);
|
||||
_run_0(0);
|
||||
_acked_write<L0_frcntb, L0_wstat::Frcntb>(tics);
|
||||
_run_0(1);
|
||||
return;
|
||||
case 1:
|
||||
write<L1_int_cstat::Frcnt>(1);
|
||||
_run_1(0);
|
||||
_acked_write<L1_frcntb, L1_wstat::Frcntb>(tics);
|
||||
_run_1(1);
|
||||
return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
time_t value(unsigned const cpu)
|
||||
{
|
||||
switch (cpu) {
|
||||
case 0: return read<L0_int_cstat::Frcnt>() ? 0 : read<L0_frcnto>();
|
||||
case 1: return read<L1_int_cstat::Frcnt>() ? 0 : read<L1_frcnto>();
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
time_t tics_to_us(time_t const tics) const {
|
||||
return (tics / _tics_per_ms) * 1000; }
|
||||
|
||||
time_t us_to_tics(time_t const us) const {
|
||||
return (us / 1000) * _tics_per_ms; }
|
||||
|
||||
time_t max_value() { return (L0_frcnto::access_t)~0; }
|
||||
};
|
||||
|
||||
namespace Kernel { class Timer : public Genode::Timer { }; }
|
||||
|
||||
#endif /* _CORE__SPEC__EXYNOS5__TIMER_H_ */
|
182
repos/base-hw/src/core/spec/exynos5/timer_driver.h
Normal file
182
repos/base-hw/src/core/spec/exynos5/timer_driver.h
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Martin stein
|
||||
* \date 2013-01-10
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Kernel OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIMER_DRIVER_H_
|
||||
#define _TIMER_DRIVER_H_
|
||||
|
||||
/* Kernel includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
|
||||
namespace Kernel { class Timer_driver; }
|
||||
|
||||
|
||||
struct Kernel::Timer_driver : Genode::Mmio
|
||||
{
|
||||
enum {
|
||||
PRESCALER = 1,
|
||||
DIV_MUX = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* MCT configuration
|
||||
*/
|
||||
struct Mct_cfg : Register<0x0, 32>
|
||||
{
|
||||
struct Prescaler : Bitfield<0, 8> { };
|
||||
struct Div_mux : Bitfield<8, 3> { };
|
||||
};
|
||||
|
||||
|
||||
/*******************
|
||||
** Local timer 0 **
|
||||
*******************/
|
||||
|
||||
/**
|
||||
* Free running counter buffer
|
||||
*/
|
||||
struct L0_frcntb : Register<0x310, 32> { };
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
struct L0_tcon : Register<0x320, 32>
|
||||
{
|
||||
struct Frc_start : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Expired status
|
||||
*/
|
||||
struct L0_int_cstat : Register<0x330, 32, true>
|
||||
{
|
||||
struct Frcnt : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Interrupt enable
|
||||
*/
|
||||
struct L0_int_enb : Register<0x334, 32>
|
||||
{
|
||||
struct Frceie : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Write status
|
||||
*/
|
||||
struct L0_wstat : Register<0x340, 32, true>
|
||||
{
|
||||
struct Frcntb : Bitfield<2, 1> { };
|
||||
struct Tcon : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
struct L0_frcnto : Register<0x314, 32> { };
|
||||
|
||||
/**
|
||||
* Start and stop counting
|
||||
*/
|
||||
void run_0(bool const run)
|
||||
{
|
||||
acked_write<L0_tcon, L0_wstat::Tcon>
|
||||
(L0_tcon::Frc_start::bits(run));
|
||||
}
|
||||
|
||||
|
||||
/*******************
|
||||
** Local timer 1 **
|
||||
*******************/
|
||||
|
||||
/**
|
||||
* Free running counter buffer
|
||||
*/
|
||||
struct L1_frcntb : Register<0x410, 32> { };
|
||||
|
||||
/**
|
||||
* Configuration
|
||||
*/
|
||||
struct L1_tcon : Register<0x420, 32>
|
||||
{
|
||||
struct Frc_start : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Expired status
|
||||
*/
|
||||
struct L1_int_cstat : Register<0x430, 32, true>
|
||||
{
|
||||
struct Frcnt : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Interrupt enable
|
||||
*/
|
||||
struct L1_int_enb : Register<0x434, 32>
|
||||
{
|
||||
struct Frceie : Bitfield<1, 1> { };
|
||||
};
|
||||
|
||||
/**
|
||||
* Write status
|
||||
*/
|
||||
struct L1_wstat : Register<0x440, 32, true>
|
||||
{
|
||||
struct Frcntb : Bitfield<2, 1> { };
|
||||
struct Tcon : Bitfield<3, 1> { };
|
||||
};
|
||||
|
||||
struct L1_frcnto : Register<0x414, 32> { };
|
||||
|
||||
/**
|
||||
* Start and stop counting
|
||||
*/
|
||||
void run_1(bool const run)
|
||||
{
|
||||
acked_write<L1_tcon, L1_wstat::Tcon>
|
||||
(L1_tcon::Frc_start::bits(run));
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
** Helper methods **
|
||||
********************/
|
||||
|
||||
/**
|
||||
* Write to reg that replies via ack bit and clear ack bit
|
||||
*/
|
||||
template <typename DEST, typename ACK>
|
||||
void acked_write(typename DEST::Register_base::access_t const v)
|
||||
{
|
||||
typedef typename DEST::Register_base Dest;
|
||||
typedef typename ACK::Bitfield_base Ack;
|
||||
write<Dest>(v);
|
||||
while (!read<Ack>());
|
||||
write<Ack>(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate amount of ticks per ms for specific input clock
|
||||
*
|
||||
* \param clock input clock
|
||||
*/
|
||||
time_t static calc_ticks_per_ms(unsigned const clock) {
|
||||
return clock / (PRESCALER + 1) / (1 << DIV_MUX) / 1000; }
|
||||
|
||||
unsigned const ticks_per_ms;
|
||||
unsigned const cpu_id;
|
||||
|
||||
Timer_driver(unsigned cpu_id);
|
||||
};
|
||||
|
||||
#endif /* _TIMER_DRIVER_H_ */
|
@ -1,20 +1,59 @@
|
||||
/*
|
||||
* \brief Timer implementation for core
|
||||
* \brief Timer driver for core
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2017-05-10
|
||||
* \author Martin Stein
|
||||
* \date 2012-04-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 Genode Labs GmbH
|
||||
* Copyright (C) 2012-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* core includes */
|
||||
#include <kernel/timer.h>
|
||||
#include <board.h>
|
||||
#include <timer.h>
|
||||
#include <platform.h>
|
||||
|
||||
Genode::Timer::Timer()
|
||||
: Genode::Mmio(Genode::Platform::mmio_to_virt(Board::EPIT_1_MMIO_BASE)) { }
|
||||
using namespace Genode;
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
unsigned Timer::interrupt_id() const { return Board::EPIT_1_IRQ; }
|
||||
|
||||
|
||||
Timer_driver::Timer_driver(unsigned)
|
||||
: Mmio(Platform::mmio_to_virt(Board::EPIT_1_MMIO_BASE)) { }
|
||||
|
||||
|
||||
void Timer::_start_one_shot(time_t const ticks)
|
||||
{
|
||||
/* stop timer */
|
||||
_driver.reset();
|
||||
|
||||
/* configure timer for a one-shot */
|
||||
_driver.write<Driver::Cr>(Driver::Cr::prepare_one_shot());
|
||||
_driver.write<Driver::Lr>(ticks);
|
||||
_driver.write<Driver::Cmpr>(0);
|
||||
|
||||
/* start timer */
|
||||
_driver.write<Driver::Cr::En>(1);
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::_ticks_to_us(time_t const ticks) const {
|
||||
return (ticks / Driver::TICS_PER_MS) * 1000UL; }
|
||||
|
||||
|
||||
time_t Timer::us_to_ticks(time_t const us) const {
|
||||
return (us / 1000UL) * Driver::TICS_PER_MS; }
|
||||
|
||||
|
||||
time_t Timer::_max_value() const {
|
||||
return (Driver::Cnt::access_t)~0; }
|
||||
|
||||
|
||||
time_t Timer::_value() {
|
||||
return _driver.read<Driver::Sr::Ocif>() ? 0 : _driver.read<Driver::Cnt>(); }
|
||||
|
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Martin Stein
|
||||
* \date 2012-04-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__IMX53__TIMER_H_
|
||||
#define _CORE__SPEC__IMX53__TIMER_H_
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
/* core includes */
|
||||
#include <board.h>
|
||||
|
||||
namespace Genode { class Timer; }
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*/
|
||||
class Genode::Timer : public Mmio
|
||||
{
|
||||
private:
|
||||
|
||||
using time_t = Kernel::time_t;
|
||||
|
||||
enum { TICS_PER_MS = 33333 };
|
||||
|
||||
/**
|
||||
* Control register
|
||||
*/
|
||||
struct Cr : Register<0x0, 32>
|
||||
{
|
||||
struct En : Bitfield<0, 1> { }; /* enable timer */
|
||||
|
||||
struct En_mod : Bitfield<1, 1> /* reload on enable */
|
||||
{
|
||||
enum { RELOAD = 1 };
|
||||
};
|
||||
|
||||
struct Oci_en : Bitfield<2, 1> { }; /* interrupt on compare */
|
||||
|
||||
struct Rld : Bitfield<3, 1> /* reload or roll-over */
|
||||
{
|
||||
enum { RELOAD_FROM_LR = 1 };
|
||||
};
|
||||
|
||||
struct Prescaler : Bitfield<4, 12> /* clock input divisor */
|
||||
{
|
||||
enum { DIVIDE_BY_1 = 0 };
|
||||
};
|
||||
|
||||
struct Swr : Bitfield<16, 1> { }; /* software reset bit */
|
||||
struct Iovw : Bitfield<17, 1> { }; /* enable overwrite */
|
||||
struct Dbg_en : Bitfield<18, 1> { }; /* enable in debug mode */
|
||||
struct Wait_en : Bitfield<19, 1> { }; /* enable in wait mode */
|
||||
struct Doz_en : Bitfield<20, 1> { }; /* enable in doze mode */
|
||||
struct Stop_en : Bitfield<21, 1> { }; /* enable in stop mode */
|
||||
|
||||
struct Om : Bitfield<22, 2> /* mode of the output pin */
|
||||
{
|
||||
enum { DISCONNECTED = 0 };
|
||||
};
|
||||
|
||||
struct Clk_src : Bitfield<24, 2> /* select clock input */
|
||||
{
|
||||
enum { HIGH_FREQ_REF_CLK = 2 };
|
||||
};
|
||||
|
||||
/**
|
||||
* Register value that configures the timer for a one-shot run
|
||||
*/
|
||||
static access_t prepare_one_shot()
|
||||
{
|
||||
return En::bits(0) |
|
||||
En_mod::bits(En_mod::RELOAD) |
|
||||
Oci_en::bits(1) |
|
||||
Rld::bits(Rld::RELOAD_FROM_LR) |
|
||||
Prescaler::bits(Prescaler::DIVIDE_BY_1) |
|
||||
Swr::bits(0) |
|
||||
Iovw::bits(0) |
|
||||
Dbg_en::bits(0) |
|
||||
Wait_en::bits(0) |
|
||||
Doz_en::bits(0) |
|
||||
Stop_en::bits(0) |
|
||||
Om::bits(Om::DISCONNECTED) |
|
||||
Clk_src::bits(Clk_src::HIGH_FREQ_REF_CLK);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Status register
|
||||
*/
|
||||
struct Sr : Register<0x4, 32>
|
||||
{
|
||||
struct Ocif : Bitfield<0, 1> { }; /* IRQ status, write 1 clears */
|
||||
};
|
||||
|
||||
struct Lr : Register<0x8, 32> { }; /* load value register */
|
||||
struct Cmpr : Register<0xc, 32> { }; /* compare value register */
|
||||
struct Cnt : Register<0x10, 32> { }; /* counter register */
|
||||
|
||||
/**
|
||||
* Disable timer and clear its interrupt output
|
||||
*/
|
||||
void _reset()
|
||||
{
|
||||
/* wait until ongoing reset operations are finished */
|
||||
while (read<Cr::Swr>()) ;
|
||||
|
||||
/* disable timer */
|
||||
write<Cr::En>(0);
|
||||
|
||||
/* clear interrupt */
|
||||
write<Sr::Ocif>(1);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Return kernel name of timer interrupt
|
||||
*/
|
||||
static unsigned interrupt_id(unsigned const)
|
||||
{
|
||||
return Board::EPIT_1_IRQ;
|
||||
}
|
||||
|
||||
Timer();
|
||||
|
||||
/**
|
||||
* Start single timeout run
|
||||
*
|
||||
* \param tics delay of timer interrupt
|
||||
*/
|
||||
void start_one_shot(time_t const tics, unsigned const)
|
||||
{
|
||||
/* stop timer */
|
||||
_reset();
|
||||
|
||||
/* configure timer for a one-shot */
|
||||
write<Cr>(Cr::prepare_one_shot());
|
||||
write<Lr>(tics);
|
||||
write<Cmpr>(0);
|
||||
|
||||
/* start timer */
|
||||
write<Cr::En>(1);
|
||||
}
|
||||
|
||||
time_t tics_to_us(time_t const tics) const {
|
||||
return (tics / TICS_PER_MS) * 1000; }
|
||||
|
||||
time_t us_to_tics(time_t const us) const {
|
||||
return (us / 1000) * TICS_PER_MS; }
|
||||
|
||||
time_t max_value() { return (Cnt::access_t)~0; }
|
||||
|
||||
/**
|
||||
* Return current native timer value
|
||||
*/
|
||||
time_t value(unsigned const) {
|
||||
return read<Sr::Ocif>() ? 0 : read<Cnt>(); }
|
||||
};
|
||||
|
||||
namespace Kernel { using Timer = Genode::Timer; }
|
||||
|
||||
#endif /* _CORE__SPEC__IMX53__TIMER_H_ */
|
121
repos/base-hw/src/core/spec/imx53/timer_driver.h
Normal file
121
repos/base-hw/src/core/spec/imx53/timer_driver.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Martin Stein
|
||||
* \date 2012-04-23
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012-2017 Kernel Labs GmbH
|
||||
*
|
||||
* This file is part of the Kernel OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIMER_DRIVER_H_
|
||||
#define _TIMER_DRIVER_H_
|
||||
|
||||
/* Kernel includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
namespace Kernel { class Timer_driver; }
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*/
|
||||
struct Kernel::Timer_driver : Genode::Mmio
|
||||
{
|
||||
enum { TICS_PER_MS = 33333 };
|
||||
|
||||
/**
|
||||
* Control register
|
||||
*/
|
||||
struct Cr : Register<0x0, 32>
|
||||
{
|
||||
struct En : Bitfield<0, 1> { }; /* enable timer */
|
||||
|
||||
struct En_mod : Bitfield<1, 1> /* reload on enable */
|
||||
{
|
||||
enum { RELOAD = 1 };
|
||||
};
|
||||
|
||||
struct Oci_en : Bitfield<2, 1> { }; /* interrupt on compare */
|
||||
|
||||
struct Rld : Bitfield<3, 1> /* reload or roll-over */
|
||||
{
|
||||
enum { RELOAD_FROM_LR = 1 };
|
||||
};
|
||||
|
||||
struct Prescaler : Bitfield<4, 12> /* clock input divisor */
|
||||
{
|
||||
enum { DIVIDE_BY_1 = 0 };
|
||||
};
|
||||
|
||||
struct Swr : Bitfield<16, 1> { }; /* software reset bit */
|
||||
struct Iovw : Bitfield<17, 1> { }; /* enable overwrite */
|
||||
struct Dbg_en : Bitfield<18, 1> { }; /* enable in debug mode */
|
||||
struct Wait_en : Bitfield<19, 1> { }; /* enable in wait mode */
|
||||
struct Doz_en : Bitfield<20, 1> { }; /* enable in doze mode */
|
||||
struct Stop_en : Bitfield<21, 1> { }; /* enable in stop mode */
|
||||
|
||||
struct Om : Bitfield<22, 2> /* mode of the output pin */
|
||||
{
|
||||
enum { DISCONNECTED = 0 };
|
||||
};
|
||||
|
||||
struct Clk_src : Bitfield<24, 2> /* select clock input */
|
||||
{
|
||||
enum { HIGH_FREQ_REF_CLK = 2 };
|
||||
};
|
||||
|
||||
/**
|
||||
* Register value that configures the timer for a one-shot run
|
||||
*/
|
||||
static access_t prepare_one_shot()
|
||||
{
|
||||
return En::bits(0) |
|
||||
En_mod::bits(En_mod::RELOAD) |
|
||||
Oci_en::bits(1) |
|
||||
Rld::bits(Rld::RELOAD_FROM_LR) |
|
||||
Prescaler::bits(Prescaler::DIVIDE_BY_1) |
|
||||
Swr::bits(0) |
|
||||
Iovw::bits(0) |
|
||||
Dbg_en::bits(0) |
|
||||
Wait_en::bits(0) |
|
||||
Doz_en::bits(0) |
|
||||
Stop_en::bits(0) |
|
||||
Om::bits(Om::DISCONNECTED) |
|
||||
Clk_src::bits(Clk_src::HIGH_FREQ_REF_CLK);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Status register
|
||||
*/
|
||||
struct Sr : Register<0x4, 32>
|
||||
{
|
||||
struct Ocif : Bitfield<0, 1> { }; /* IRQ status, write 1 clears */
|
||||
};
|
||||
|
||||
struct Lr : Register<0x8, 32> { }; /* load value register */
|
||||
struct Cmpr : Register<0xc, 32> { }; /* compare value register */
|
||||
struct Cnt : Register<0x10, 32> { }; /* counter register */
|
||||
|
||||
/**
|
||||
* Disable timer and clear its interrupt output
|
||||
*/
|
||||
void reset()
|
||||
{
|
||||
/* wait until ongoing reset operations are finished */
|
||||
while (read<Cr::Swr>()) ;
|
||||
|
||||
/* disable timer */
|
||||
write<Cr::En>(0);
|
||||
|
||||
/* clear interrupt */
|
||||
write<Sr::Ocif>(1);
|
||||
}
|
||||
|
||||
Timer_driver(unsigned);
|
||||
};
|
||||
|
||||
#endif /* _TIMER_DRIVER_H_ */
|
57
repos/base-hw/src/core/spec/riscv/timer.cc
Normal file
57
repos/base-hw/src/core/spec/riscv/timer.cc
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-08-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* Core includes */
|
||||
#include <kernel/timer.h>
|
||||
#include <machine_call.h>
|
||||
|
||||
using namespace Genode;
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
Timer_driver::Timer_driver(unsigned)
|
||||
{
|
||||
/* enable timer interrupt */
|
||||
enum { STIE = 0x20 };
|
||||
asm volatile ("csrs sie, %0" : : "r"(STIE));
|
||||
}
|
||||
|
||||
|
||||
void Timer::_start_one_shot(time_t const ticks, unsigned const)
|
||||
{
|
||||
_driver.timeout = _driver.stime() + ticks;
|
||||
Machine::set_sys_timer(_driver.timeout);
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::_ticks_to_us(time_t const ticks) const {
|
||||
return (ticks / Driver::TICS_PER_MS) * 1000; }
|
||||
|
||||
|
||||
time_t Timer::us_to_ticks(time_t const us) const {
|
||||
return (us / 1000) * Driver::TICS_PER_MS; }
|
||||
|
||||
|
||||
time_t Timer::_max_value() {
|
||||
return (addr_t)~0; }
|
||||
|
||||
|
||||
time_t Timer::_value(unsigned const)
|
||||
{
|
||||
addr_t time = _driver.stime();
|
||||
return time < _driver.timeout ? _driver.timeout - time : 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned Timer::interrupt_id() const {
|
||||
return 1; }
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-08-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__RISCV__TIMER_H_
|
||||
#define _CORE__SPEC__RISCV__TIMER_H_
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
#include <hw/spec/riscv/machine_call.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/stdint.h>
|
||||
|
||||
|
||||
namespace Genode { class Timer; }
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*/
|
||||
struct Genode::Timer
|
||||
{
|
||||
private:
|
||||
|
||||
using time_t = Kernel::time_t;
|
||||
|
||||
enum {
|
||||
SPIKE_TIMER_HZ = 500000,
|
||||
TICS_PER_MS = SPIKE_TIMER_HZ / 1000,
|
||||
};
|
||||
|
||||
addr_t _timeout = 0;
|
||||
|
||||
addr_t _stime()
|
||||
{
|
||||
addr_t t;
|
||||
asm volatile ("csrr %0, stime\n" : "=r"(t));
|
||||
return t;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Timer()
|
||||
{
|
||||
/* enable timer interrupt */
|
||||
enum { STIE = 0x20 };
|
||||
asm volatile ("csrs sie, %0" : : "r"(STIE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start single timeout run
|
||||
*
|
||||
* \param tics delay of timer interrupt
|
||||
*/
|
||||
void start_one_shot(time_t const tics, unsigned const)
|
||||
{
|
||||
_timeout = _stime() + tics;
|
||||
Hw::set_sys_timer(_timeout);
|
||||
}
|
||||
|
||||
time_t tics_to_us(time_t const tics) const {
|
||||
return (tics / TICS_PER_MS) * 1000; }
|
||||
|
||||
time_t us_to_tics(time_t const us) const {
|
||||
return (us / 1000) * TICS_PER_MS; }
|
||||
|
||||
time_t max_value() { return (addr_t)~0; }
|
||||
|
||||
/**
|
||||
* Return current native timer value
|
||||
*/
|
||||
time_t value(unsigned const)
|
||||
{
|
||||
addr_t time = _stime();
|
||||
return time < _timeout ? _timeout - time : 0;
|
||||
}
|
||||
|
||||
static unsigned interrupt_id(unsigned const) { return 1; }
|
||||
};
|
||||
|
||||
namespace Kernel { class Timer : public Genode::Timer { }; }
|
||||
|
||||
#endif /* _CORE__SPEC__RISCV__TIMER_H_ */
|
45
repos/base-hw/src/core/spec/riscv/timer_driver.h
Normal file
45
repos/base-hw/src/core/spec/riscv/timer_driver.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Sebastian Sumpf
|
||||
* \date 2015-08-22
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIMER_H_
|
||||
#define _TIMER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/stdint.h>
|
||||
|
||||
namespace Kernel { class Timer_driver; }
|
||||
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*/
|
||||
struct Kernel::Timer_driver
|
||||
{
|
||||
enum {
|
||||
SPIKE_TIMER_HZ = 500000,
|
||||
TICS_PER_MS = SPIKE_TIMER_HZ / 1000,
|
||||
};
|
||||
|
||||
addr_t timeout = 0;
|
||||
|
||||
addr_t stime()
|
||||
{
|
||||
Genode::addr_t t;
|
||||
asm volatile ("csrr %0, stime\n" : "=r"(t));
|
||||
return t;
|
||||
}
|
||||
|
||||
Timer_driver(unsigned);
|
||||
};
|
||||
|
||||
#endif /* _TIMER_H_ */
|
@ -2,6 +2,7 @@
|
||||
* \brief Timer implementation specific to Rpi
|
||||
* \author Norman Feske
|
||||
* \author Stefan Kalkowski
|
||||
* \author Martin Stein
|
||||
* \date 2016-01-07
|
||||
*/
|
||||
|
||||
@ -12,40 +13,45 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
/* core includes */
|
||||
#include <platform.h>
|
||||
#include <timer.h>
|
||||
#include <kernel/timer.h>
|
||||
|
||||
using namespace Genode;
|
||||
using Kernel::time_t;
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
Timer::Timer()
|
||||
Timer_driver::Timer_driver(unsigned)
|
||||
: Mmio(Platform::mmio_to_virt(Board::SYSTEM_TIMER_MMIO_BASE)) { }
|
||||
|
||||
|
||||
void Timer::start_one_shot(time_t const tics, unsigned const)
|
||||
void Timer::_start_one_shot(time_t const ticks)
|
||||
{
|
||||
write<Cs::M1>(1);
|
||||
read<Cs>();
|
||||
write<Clo>(0);
|
||||
write<Cmp>(read<Clo>() + tics);
|
||||
_driver.write<Driver::Cs::M1>(1);
|
||||
_driver.read<Driver::Cs>();
|
||||
_driver.write<Driver::Clo>(0);
|
||||
_driver.write<Driver::Cmp>(_driver.read<Driver::Clo>() + ticks);
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::tics_to_us(time_t const tics) const {
|
||||
return (tics / TICS_PER_MS) * 1000; }
|
||||
time_t Timer::_ticks_to_us(time_t const ticks) const {
|
||||
return (ticks / Driver::TICS_PER_MS) * 1000; }
|
||||
|
||||
|
||||
time_t Timer::us_to_tics(time_t const us) const {
|
||||
return (us / 1000) * TICS_PER_MS; }
|
||||
time_t Timer::us_to_ticks(time_t const us) const {
|
||||
return (us / 1000) * Driver::TICS_PER_MS; }
|
||||
|
||||
|
||||
time_t Timer::max_value() { return (Clo::access_t)~0; }
|
||||
time_t Timer::_max_value() const {
|
||||
return (Driver::Clo::access_t)~0; }
|
||||
|
||||
|
||||
time_t Timer::value(unsigned const)
|
||||
time_t Timer::_value()
|
||||
{
|
||||
Cmp::access_t const cmp = read<Cmp>();
|
||||
Clo::access_t const clo = read<Clo>();
|
||||
Driver::Cmp::access_t const cmp = _driver.read<Driver::Cmp>();
|
||||
Driver::Clo::access_t const clo = _driver.read<Driver::Clo>();
|
||||
return cmp > clo ? cmp - clo : 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned Timer::interrupt_id() const { return Board::SYSTEM_TIMER_IRQ; }
|
||||
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Norman Feske
|
||||
* \date 2013-04-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__RPI__TIMER_H_
|
||||
#define _CORE__SPEC__RPI__TIMER_H_
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
/* core includes */
|
||||
#include <board.h>
|
||||
|
||||
namespace Genode { class Timer; }
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*
|
||||
* Timer channel 0 apparently doesn't work on the RPI, so we use channel 1
|
||||
*/
|
||||
class Genode::Timer : public Mmio
|
||||
{
|
||||
private:
|
||||
|
||||
using time_t = Kernel::time_t;
|
||||
|
||||
enum { TICS_PER_MS = Board::SYSTEM_TIMER_CLOCK / 1000 };
|
||||
|
||||
struct Cs : Register<0x0, 32> { struct M1 : Bitfield<1, 1> { }; };
|
||||
struct Clo : Register<0x4, 32> { };
|
||||
struct Cmp : Register<0x10, 32> { };
|
||||
|
||||
public:
|
||||
|
||||
Timer();
|
||||
|
||||
static unsigned interrupt_id(unsigned const) {
|
||||
return Board::SYSTEM_TIMER_IRQ; }
|
||||
|
||||
void start_one_shot(time_t const tics, unsigned const);
|
||||
time_t tics_to_us(time_t const tics) const;
|
||||
time_t us_to_tics(time_t const us) const;
|
||||
time_t max_value();
|
||||
time_t value(unsigned const);
|
||||
};
|
||||
|
||||
namespace Kernel { using Genode::Timer; }
|
||||
|
||||
#endif /* _CORE__SPEC__RPI__TIMER_H_ */
|
41
repos/base-hw/src/core/spec/rpi/timer_driver.h
Normal file
41
repos/base-hw/src/core/spec/rpi/timer_driver.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Norman Feske
|
||||
* \date 2013-04-05
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Kernel OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIMER_DRIVER_H_
|
||||
#define _TIMER_DRIVER_H_
|
||||
|
||||
/* Kernel includes */
|
||||
#include <util/mmio.h>
|
||||
|
||||
/* core includes */
|
||||
#include <board.h>
|
||||
|
||||
namespace Kernel { class Timer_driver; }
|
||||
|
||||
/**
|
||||
* Timer driver for core
|
||||
*
|
||||
* Timer channel 0 apparently doesn't work on the RPI, so we use channel 1
|
||||
*/
|
||||
struct Kernel::Timer_driver : Genode::Mmio
|
||||
{
|
||||
enum { TICS_PER_MS = Board::SYSTEM_TIMER_CLOCK / 1000 };
|
||||
|
||||
struct Cs : Register<0x0, 32> { struct M1 : Bitfield<1, 1> { }; };
|
||||
struct Clo : Register<0x4, 32> { };
|
||||
struct Cmp : Register<0x10, 32> { };
|
||||
|
||||
Timer_driver(unsigned);
|
||||
};
|
||||
|
||||
#endif /* _TIMER_DRIVER_H_ */
|
@ -38,13 +38,13 @@ void Kernel::Cpu::init(Pic &pic)
|
||||
Idt::init();
|
||||
Tss::init();
|
||||
|
||||
Timer::disable_pit();
|
||||
Timer::init_cpu_local();
|
||||
|
||||
fpu().init();
|
||||
|
||||
/* enable timer interrupt */
|
||||
unsigned const cpu = Cpu::executing_id();
|
||||
pic.unmask(Timer::interrupt_id(cpu), cpu);
|
||||
pic.unmask(_timer.interrupt_id(), cpu);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Reto Buerki
|
||||
* \author Martin Stein
|
||||
* \date 2015-04-14
|
||||
*/
|
||||
|
||||
@ -11,12 +12,21 @@
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#include <timer.h>
|
||||
/* base includes */
|
||||
#include <base/log.h>
|
||||
|
||||
/* core includes */
|
||||
#include <kernel/timer.h>
|
||||
#include <platform.h>
|
||||
#include <board.h>
|
||||
#include <sinfo_instance.h>
|
||||
|
||||
Genode::Timer::Timer() : _tics_per_ms(sinfo()->get_tsc_khz())
|
||||
using namespace Genode;
|
||||
using namespace Kernel;
|
||||
|
||||
|
||||
Timer_driver::Timer_driver(unsigned) : ticks_per_ms(sinfo()->get_tsc_khz())
|
||||
{
|
||||
|
||||
/* first sinfo instance, output status */
|
||||
sinfo()->log_status();
|
||||
|
||||
@ -26,17 +36,57 @@ Genode::Timer::Timer() : _tics_per_ms(sinfo()->get_tsc_khz())
|
||||
throw Invalid_region();
|
||||
}
|
||||
|
||||
_event_page = (Subject_timed_event *)Platform::mmio_to_virt(region.address);
|
||||
_event_page->event_nr = Board::TIMER_EVENT_KERNEL;
|
||||
event_page = (Subject_timed_event *)Platform::mmio_to_virt(region.address);
|
||||
event_page->event_nr = Board::TIMER_EVENT_KERNEL;
|
||||
log("muen-timer: Page @", Hex(region.address), ", "
|
||||
"frequency ", _tics_per_ms, " kHz, "
|
||||
"event ", (unsigned)_event_page->event_nr);
|
||||
"frequency ", ticks_per_ms, " kHz, "
|
||||
"event ", (unsigned)event_page->event_nr);
|
||||
|
||||
if (sinfo()->get_memregion_info("monitor_timed_event", ®ion)) {
|
||||
log("muen-timer: Found guest timed event page @", Hex(region.address),
|
||||
" -> enabling preemption");
|
||||
_guest_event_page = (Subject_timed_event *)Platform::mmio_to_virt(region.address);
|
||||
_guest_event_page->event_nr = Board::TIMER_EVENT_PREEMPT;
|
||||
guest_event_page = (Subject_timed_event *)Platform::mmio_to_virt(region.address);
|
||||
guest_event_page->event_nr = Board::TIMER_EVENT_PREEMPT;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Timer::init_cpu_local() { }
|
||||
|
||||
|
||||
unsigned Timer::interrupt_id() const {
|
||||
return Board::TIMER_VECTOR_KERNEL; }
|
||||
|
||||
|
||||
void Timer::_start_one_shot(time_t const ticks)
|
||||
{
|
||||
const uint64_t t = _driver.rdtsc() + ticks;
|
||||
_driver.event_page->tsc_trigger = t;
|
||||
|
||||
if (_driver.guest_event_page)
|
||||
_driver.guest_event_page->tsc_trigger = t;
|
||||
}
|
||||
|
||||
|
||||
time_t Timer::_ticks_to_us(time_t const ticks) const {
|
||||
return (ticks / _driver.ticks_per_ms) * 1000; }
|
||||
|
||||
|
||||
time_t Timer::us_to_ticks(time_t const us) const {
|
||||
return (us / 1000) * _driver.ticks_per_ms; }
|
||||
|
||||
|
||||
time_t Timer::_max_value() const {
|
||||
return (time_t)~0; }
|
||||
|
||||
|
||||
time_t Timer::_value()
|
||||
{
|
||||
const uint64_t now = _driver.rdtsc();
|
||||
if (_driver.event_page->tsc_trigger != Driver::TIMER_DISABLED
|
||||
&& _driver.event_page->tsc_trigger > now)
|
||||
{
|
||||
return _driver.event_page->tsc_trigger - now;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Reto Buerki
|
||||
* \date 2015-04-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__X86_64__MUEN__TIMER_H_
|
||||
#define _CORE__SPEC__X86_64__MUEN__TIMER_H_
|
||||
|
||||
/* base includes */
|
||||
#include <base/log.h>
|
||||
#include <kernel/types.h>
|
||||
|
||||
/* core includes */
|
||||
#include <board.h>
|
||||
#include <sinfo_instance.h>
|
||||
|
||||
namespace Genode
|
||||
{
|
||||
/**
|
||||
* Timer driver for core on Muen
|
||||
*/
|
||||
class Timer;
|
||||
}
|
||||
|
||||
class Genode::Timer
|
||||
{
|
||||
private:
|
||||
|
||||
using time_t = Kernel::time_t;
|
||||
|
||||
enum { TIMER_DISABLED = ~0ULL };
|
||||
|
||||
uint64_t _tics_per_ms;
|
||||
|
||||
struct Subject_timed_event
|
||||
{
|
||||
uint64_t tsc_trigger;
|
||||
uint8_t event_nr :5;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Subject_timed_event * _event_page = 0;
|
||||
struct Subject_timed_event * _guest_event_page = 0;
|
||||
|
||||
|
||||
inline uint64_t rdtsc()
|
||||
{
|
||||
uint32_t lo, hi;
|
||||
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return (uint64_t)hi << 32 | lo;
|
||||
}
|
||||
|
||||
class Invalid_region {};
|
||||
|
||||
public:
|
||||
|
||||
Timer();
|
||||
|
||||
static unsigned interrupt_id(int) {
|
||||
return Board::TIMER_VECTOR_KERNEL; }
|
||||
|
||||
inline void start_one_shot(time_t const tics, unsigned)
|
||||
{
|
||||
const uint64_t t = rdtsc() + tics;
|
||||
_event_page->tsc_trigger = t;
|
||||
|
||||
if (_guest_event_page)
|
||||
_guest_event_page->tsc_trigger = t;
|
||||
}
|
||||
|
||||
time_t tics_to_us(time_t const tics) const {
|
||||
return (tics / _tics_per_ms) * 1000; }
|
||||
|
||||
time_t us_to_tics(time_t const us) const {
|
||||
return (us / 1000) * _tics_per_ms; }
|
||||
|
||||
time_t max_value() { return (time_t)~0; }
|
||||
|
||||
time_t value(unsigned)
|
||||
{
|
||||
const uint64_t now = rdtsc();
|
||||
if (_event_page->tsc_trigger != TIMER_DISABLED
|
||||
&& _event_page->tsc_trigger > now) {
|
||||
return _event_page->tsc_trigger - now;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void disable_pit(void) { }
|
||||
};
|
||||
|
||||
namespace Kernel { class Timer : public Genode::Timer { }; }
|
||||
|
||||
#endif /* _CORE__SPEC__X86_64__MUEN__TIMER_H_ */
|
50
repos/base-hw/src/core/spec/x86_64/muen/timer_driver.h
Normal file
50
repos/base-hw/src/core/spec/x86_64/muen/timer_driver.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Reto Buerki
|
||||
* \date 2015-04-14
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIMER_DRIVER_H_
|
||||
#define _TIMER_DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/stdint.h>
|
||||
|
||||
namespace Kernel { class Timer_driver; }
|
||||
|
||||
|
||||
struct Kernel::Timer_driver
|
||||
{
|
||||
enum { TIMER_DISABLED = ~0ULL };
|
||||
|
||||
Genode::uint64_t ticks_per_ms;
|
||||
|
||||
struct Subject_timed_event
|
||||
{
|
||||
Genode::uint64_t tsc_trigger;
|
||||
Genode::uint8_t event_nr :5;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Subject_timed_event * event_page = 0;
|
||||
struct Subject_timed_event * guest_event_page = 0;
|
||||
|
||||
inline Genode::uint64_t rdtsc()
|
||||
{
|
||||
Genode::uint32_t lo, hi;
|
||||
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return (Genode::uint64_t)hi << 32 | lo;
|
||||
}
|
||||
|
||||
class Invalid_region { };
|
||||
|
||||
Timer_driver(unsigned);
|
||||
};
|
||||
|
||||
#endif /* _TIMER_DRIVER_H_ */
|
@ -2,6 +2,7 @@
|
||||
* \brief Timer driver for core
|
||||
* \author Adrian-Ken Rueegsegger
|
||||
* \author Reto Buerki
|
||||
* \author Martin Stein
|
||||
* \date 2015-02-06
|
||||
*/
|
||||
|
||||
@ -15,13 +16,14 @@
|
||||
#include <hw/spec/x86_64/x86_64.h>
|
||||
|
||||
/* core includes */
|
||||
#include <timer.h>
|
||||
#include <kernel/timer.h>
|
||||
#include <platform.h>
|
||||
|
||||
using namespace Genode;
|
||||
using Kernel::time_t;
|
||||
using namespace Kernel;
|
||||
|
||||
uint32_t Timer::_pit_calc_timer_freq(void)
|
||||
|
||||
uint32_t Timer_driver::pit_calc_timer_freq(void)
|
||||
{
|
||||
uint32_t t_start, t_end;
|
||||
|
||||
@ -48,7 +50,8 @@ uint32_t Timer::_pit_calc_timer_freq(void)
|
||||
}
|
||||
|
||||
|
||||
Timer::Timer() : Mmio(Platform::mmio_to_virt(Hw::Cpu_memory_map::MMIO_LAPIC_BASE))
|
||||
Timer_driver::Timer_driver(unsigned)
|
||||
: Mmio(Platform::mmio_to_virt(Hw::Cpu_memory_map::MMIO_LAPIC_BASE))
|
||||
{
|
||||
write<Divide_configuration::Divide_value>(
|
||||
Divide_configuration::Divide_value::MAX);
|
||||
@ -60,31 +63,41 @@ Timer::Timer() : Mmio(Platform::mmio_to_virt(Hw::Cpu_memory_map::MMIO_LAPIC_BASE
|
||||
write<Tmr_lvt::Timer_mode>(0);
|
||||
|
||||
/* Calculate timer frequency */
|
||||
_tics_per_ms = _pit_calc_timer_freq();
|
||||
ticks_per_ms = pit_calc_timer_freq();
|
||||
}
|
||||
|
||||
|
||||
void Timer::disable_pit()
|
||||
void Timer::init_cpu_local()
|
||||
{
|
||||
outb(PIT_MODE, 0x30);
|
||||
outb(PIT_CH0_DATA, 0);
|
||||
outb(PIT_CH0_DATA, 0);
|
||||
/**
|
||||
* Disable PIT timer channel. This is necessary since BIOS sets up
|
||||
* channel 0 to fire periodically.
|
||||
*/
|
||||
outb(Driver::PIT_MODE, 0x30);
|
||||
outb(Driver::PIT_CH0_DATA, 0);
|
||||
outb(Driver::PIT_CH0_DATA, 0);
|
||||
}
|
||||
|
||||
|
||||
void Timer::start_one_shot(time_t const tics, unsigned const) {
|
||||
write<Tmr_initial>(tics); }
|
||||
void Timer::_start_one_shot(time_t const ticks) {
|
||||
_driver.write<Driver::Tmr_initial>(ticks); }
|
||||
|
||||
|
||||
time_t Timer::tics_to_us(time_t const tics) const {
|
||||
return (tics / _tics_per_ms) * 1000; }
|
||||
time_t Timer::_ticks_to_us(time_t const ticks) const {
|
||||
return (ticks / _driver.ticks_per_ms) * 1000; }
|
||||
|
||||
|
||||
time_t Timer::us_to_tics(time_t const us) const {
|
||||
return (us / 1000) * _tics_per_ms; }
|
||||
time_t Timer::us_to_ticks(time_t const us) const {
|
||||
return (us / 1000) * _driver.ticks_per_ms; }
|
||||
|
||||
|
||||
time_t Timer::max_value() { return (Tmr_initial::access_t)~0; }
|
||||
time_t Timer::_max_value() const {
|
||||
return (Driver::Tmr_initial::access_t)~0; }
|
||||
|
||||
|
||||
time_t Timer::value(unsigned const) { return read<Tmr_current>(); }
|
||||
time_t Timer::_value() {
|
||||
return _driver.read<Driver::Tmr_current>(); }
|
||||
|
||||
|
||||
unsigned Timer::interrupt_id() const {
|
||||
return Board::TIMER_VECTOR_KERNEL; }
|
||||
|
@ -1,100 +0,0 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Adrian-Ken Rueegsegger
|
||||
* \author Reto Buerki
|
||||
* \date 2015-02-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _CORE__SPEC__X86_64__TIMER_H_
|
||||
#define _CORE__SPEC__X86_64__TIMER_H_
|
||||
|
||||
/* base-hw includes */
|
||||
#include <kernel/types.h>
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
#include <base/stdint.h>
|
||||
|
||||
/* core includes */
|
||||
#include <port_io.h>
|
||||
#include <board.h>
|
||||
|
||||
namespace Genode { class Timer; }
|
||||
|
||||
/**
|
||||
* LAPIC-based timer driver for core
|
||||
*/
|
||||
class Genode::Timer : public Mmio
|
||||
{
|
||||
private:
|
||||
|
||||
using time_t = Kernel::time_t;
|
||||
|
||||
enum {
|
||||
/* PIT constants */
|
||||
PIT_TICK_RATE = 1193182ul,
|
||||
PIT_SLEEP_MS = 50,
|
||||
PIT_SLEEP_TICS = (PIT_TICK_RATE / 1000) * PIT_SLEEP_MS,
|
||||
PIT_CH0_DATA = 0x40,
|
||||
PIT_CH2_DATA = 0x42,
|
||||
PIT_CH2_GATE = 0x61,
|
||||
PIT_MODE = 0x43,
|
||||
};
|
||||
|
||||
/* Timer registers */
|
||||
struct Tmr_lvt : Register<0x320, 32>
|
||||
{
|
||||
struct Vector : Bitfield<0, 8> { };
|
||||
struct Delivery : Bitfield<8, 3> { };
|
||||
struct Mask : Bitfield<16, 1> { };
|
||||
struct Timer_mode : Bitfield<17, 2> { };
|
||||
};
|
||||
struct Tmr_initial : Register <0x380, 32> { };
|
||||
struct Tmr_current : Register <0x390, 32> { };
|
||||
|
||||
struct Divide_configuration : Register <0x03e0, 32>
|
||||
{
|
||||
struct Divide_value_0_2 : Bitfield<0, 2> { };
|
||||
struct Divide_value_2_1 : Bitfield<3, 1> { };
|
||||
struct Divide_value :
|
||||
Bitset_2<Divide_value_0_2, Divide_value_2_1>
|
||||
{
|
||||
enum { MAX = 6 };
|
||||
};
|
||||
};
|
||||
|
||||
uint32_t _tics_per_ms = 0;
|
||||
|
||||
/* Measure LAPIC timer frequency using PIT channel 2 */
|
||||
uint32_t _pit_calc_timer_freq(void);
|
||||
|
||||
public:
|
||||
|
||||
Timer();
|
||||
|
||||
/**
|
||||
* Disable PIT timer channel. This is necessary since BIOS sets up
|
||||
* channel 0 to fire periodically.
|
||||
*/
|
||||
static void disable_pit();
|
||||
|
||||
static unsigned interrupt_id(unsigned const) {
|
||||
return Board::TIMER_VECTOR_KERNEL; }
|
||||
|
||||
void start_one_shot(time_t const tics, unsigned const);
|
||||
time_t tics_to_us(time_t const tics) const;
|
||||
time_t us_to_tics(time_t const us) const;
|
||||
time_t max_value();
|
||||
time_t value(unsigned const);
|
||||
};
|
||||
|
||||
namespace Kernel { using Genode::Timer; }
|
||||
|
||||
#endif /* _CORE__SPEC__X86_64__TIMER_H_ */
|
74
repos/base-hw/src/core/spec/x86_64/timer_driver.h
Normal file
74
repos/base-hw/src/core/spec/x86_64/timer_driver.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* \brief Timer driver for core
|
||||
* \author Adrian-Ken Rueegsegger
|
||||
* \author Reto Buerki
|
||||
* \date 2015-02-06
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Genode Labs GmbH
|
||||
*
|
||||
* This file is part of the Genode OS framework, which is distributed
|
||||
* under the terms of the GNU Affero General Public License version 3.
|
||||
*/
|
||||
|
||||
#ifndef _TIMER_DRIVER_H_
|
||||
#define _TIMER_DRIVER_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <util/mmio.h>
|
||||
#include <base/stdint.h>
|
||||
|
||||
/* core includes */
|
||||
#include <port_io.h>
|
||||
#include <board.h>
|
||||
|
||||
namespace Kernel { class Timer_driver; }
|
||||
|
||||
/**
|
||||
* LAPIC-based timer driver for core
|
||||
*/
|
||||
struct Kernel::Timer_driver : Genode::Mmio
|
||||
{
|
||||
enum {
|
||||
/* PIT constants */
|
||||
PIT_TICK_RATE = 1193182ul,
|
||||
PIT_SLEEP_MS = 50,
|
||||
PIT_SLEEP_TICS = (PIT_TICK_RATE / 1000) * PIT_SLEEP_MS,
|
||||
PIT_CH0_DATA = 0x40,
|
||||
PIT_CH2_DATA = 0x42,
|
||||
PIT_CH2_GATE = 0x61,
|
||||
PIT_MODE = 0x43,
|
||||
};
|
||||
|
||||
/* Timer registers */
|
||||
struct Tmr_lvt : Register<0x320, 32>
|
||||
{
|
||||
struct Vector : Bitfield<0, 8> { };
|
||||
struct Delivery : Bitfield<8, 3> { };
|
||||
struct Mask : Bitfield<16, 1> { };
|
||||
struct Timer_mode : Bitfield<17, 2> { };
|
||||
};
|
||||
struct Tmr_initial : Register <0x380, 32> { };
|
||||
struct Tmr_current : Register <0x390, 32> { };
|
||||
|
||||
struct Divide_configuration : Register <0x03e0, 32>
|
||||
{
|
||||
struct Divide_value_0_2 : Bitfield<0, 2> { };
|
||||
struct Divide_value_2_1 : Bitfield<3, 1> { };
|
||||
struct Divide_value :
|
||||
Genode::Bitset_2<Divide_value_0_2, Divide_value_2_1>
|
||||
{
|
||||
enum { MAX = 6 };
|
||||
};
|
||||
};
|
||||
|
||||
Genode::uint32_t ticks_per_ms = 0;
|
||||
|
||||
/* Measure LAPIC timer frequency using PIT channel 2 */
|
||||
Genode::uint32_t pit_calc_timer_freq(void);
|
||||
|
||||
Timer_driver(unsigned);
|
||||
};
|
||||
|
||||
#endif /* _TIMER_DRIVER_H_ */
|
Loading…
Reference in New Issue
Block a user