hw: use kernel timer for timer driver

* Adds public timeout syscalls to kernel API
  * Kernel::timeout installs a timeout and binds a signal context to it that
    shall trigger once the timeout expired
  * With Kernel::timeout_max_us, one can get the maximum installable timeout
  * Kernel::timeout_age_us returns the time that has passed since the
    calling threads last timeout installation

* Removes all device specific back-ends for the base-hw timer driver and
  implements a generic back-end taht uses the kernel timeout API

* Adds assertions about the kernel timer frequency that originate from the
  requirements of the the kernel timeout API and adjusts all timers
  accordingly by using the their internal dividers

* Introduces the Kernel::Clock class. As member of each Kernel::Cpu object
  it combines the management of the timer of the CPU with a timeout scheduler.
  Not only the timeout API uses the timeout scheduler but also the CPUs job
  scheduler for installing scheduling timeouts.

* Introduces the Kernel::time_t type for timer tic values and values inherited
  from timer tics (like microseconds).

Fixes #1972
This commit is contained in:
Martin Stein 2016-05-18 12:20:04 +02:00 committed by Christian Helmuth
parent 722fd85bc1
commit 1208d14681
45 changed files with 789 additions and 1534 deletions

View File

@ -15,34 +15,32 @@
#define _INCLUDE__KERNEL__INTERFACE_H_
/* base-hw includes */
#include <kernel/types.h>
#include <kernel/interface_support.h>
namespace Kernel
{
using addr_t = Genode::addr_t;
using size_t = Genode::size_t;
using capid_t = Genode::uint16_t;
constexpr capid_t cap_id_invalid() { return 0; }
/**
* Kernel names of the kernel calls
*/
constexpr Call_arg call_id_pause_current_thread() { return 0; }
constexpr Call_arg call_id_resume_local_thread() { return 1; }
constexpr Call_arg call_id_yield_thread() { return 2; }
constexpr Call_arg call_id_send_request_msg() { return 3; }
constexpr Call_arg call_id_send_reply_msg() { return 4; }
constexpr Call_arg call_id_await_request_msg() { return 5; }
constexpr Call_arg call_id_kill_signal_context() { return 6; }
constexpr Call_arg call_id_submit_signal() { return 7; }
constexpr Call_arg call_id_await_signal() { return 8; }
constexpr Call_arg call_id_ack_signal() { return 9; }
constexpr Call_arg call_id_pause_current_thread() { return 0; }
constexpr Call_arg call_id_resume_local_thread() { return 1; }
constexpr Call_arg call_id_yield_thread() { return 2; }
constexpr Call_arg call_id_send_request_msg() { return 3; }
constexpr Call_arg call_id_send_reply_msg() { return 4; }
constexpr Call_arg call_id_await_request_msg() { return 5; }
constexpr Call_arg call_id_kill_signal_context() { return 6; }
constexpr Call_arg call_id_submit_signal() { return 7; }
constexpr Call_arg call_id_await_signal() { return 8; }
constexpr Call_arg call_id_ack_signal() { return 9; }
constexpr Call_arg call_id_print_char() { return 10; }
constexpr Call_arg call_id_update_data_region() { return 11; }
constexpr Call_arg call_id_update_instr_region() { return 12; }
constexpr Call_arg call_id_ack_cap() { return 13; }
constexpr Call_arg call_id_delete_cap() { return 14; }
constexpr Call_arg call_id_timeout() { return 15; }
constexpr Call_arg call_id_timeout_age_us() { return 16; }
constexpr Call_arg call_id_timeout_max_us() { return 17; }
/*****************************************************************
@ -81,6 +79,44 @@ namespace Kernel
Call_arg arg_5);
/**
* Install timeout for calling thread
*
* \param duration_us timeout duration in microseconds
* \param sigid local name of signal context to trigger
*
* This call always overwrites the last timeout installed by the thread
* if any.
*/
inline int timeout(time_t const duration_us, capid_t const sigid)
{
return call(call_id_timeout(), duration_us, sigid);
}
/**
* Return time in microseconds since the caller installed its last timeout
*
* Must not be called if the installation is older than 'timeout_max_us'.
*/
inline time_t timeout_age_us()
{
return call(call_id_timeout_age_us());
}
/**
* Return the constant maximum installable timeout in microseconds
*
* The return value is also the maximum delay to call 'timeout_age_us'
* for a timeout after its installation.
*/
inline time_t timeout_max_us()
{
return call(call_id_timeout_max_us());
}
/**
* Pause execution of calling thread
*/

View File

@ -0,0 +1,31 @@
/*
* \brief Public kernel types
* \author Martin stein
* \date 2016-03-23
*/
/*
* Copyright (C) 2016 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 _KERNEL__TYPES_H_
#define _KERNEL__TYPES_H_
/* base-hw includes */
#include <kernel/types.h>
#include <kernel/interface_support.h>
namespace Kernel
{
using addr_t = Genode::addr_t;
using size_t = Genode::size_t;
using capid_t = Genode::uint16_t;
using time_t = unsigned long;
constexpr capid_t cap_id_invalid() { return 0; }
}
#endif /* _KERNEL__TYPES_H_ */

View File

@ -56,6 +56,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/object.cc
SRC_CC += init_main_thread.cc
SRC_CC += capability.cc

View File

@ -29,7 +29,7 @@ Dataspace_capability Cpu_thread_component::utcb()
Cpu_session::Quota Cpu_session_component::quota()
{
size_t const spu = Kernel::cpu_quota_ms * 1000;
size_t const spu = Kernel::cpu_quota_us;
size_t const u = quota_lim_downscale<sizet_arithm_t>(_quota, spu);
return { spu, u };
}

View File

@ -0,0 +1,107 @@
/*
* \brief A clock manages a continuous time and timeouts on it
* \author Martin Stein
* \date 2016-03-23
*/
/*
* Copyright (C) 2016 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 _CORE__INCLUDE__KERNEL__CLOCK_H_
#define _CORE__INCLUDE__KERNEL__CLOCK_H_
/* base-hw includes */
#include <kernel/types.h>
/* Genode includes */
#include <util/list.h>
/* Core includes */
#include <timer.h>
namespace Kernel
{
class Timeout;
class Clock;
}
/**
* A timeout causes a kernel pass and the call of a timeout specific handle
*/
class Kernel::Timeout : public Genode::List<Timeout>::Element
{
friend class Clock;
private:
bool _listed = false;
time_t _start = 0;
time_t _end = 0;
public:
/**
* Callback handle
*/
virtual void timeout_triggered() { }
virtual ~Timeout() { }
};
/**
* A clock manages a continuous time and timeouts on it
*/
class Kernel::Clock
{
private:
unsigned const _cpu_id;
Timer * const _timer;
time_t _time = 0;
Genode::List<Timeout> _timeout_list;
time_t _last_timeout_duration = 0;
void _insert_timeout(Timeout * const timeout);
void _remove_timeout(Timeout * const timeout);
public:
Clock(unsigned const cpu_id, Timer * const timer);
/**
* 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;
/**
* 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;
};
#endif /* _CORE__INCLUDE__KERNEL__CLOCK_H_ */

View File

@ -25,10 +25,10 @@ namespace Kernel
constexpr unsigned cpu_priorities = 4;
/* super period in CPU scheduling and the overall allocatable CPU time */
constexpr unsigned cpu_quota_ms = 1000;
constexpr time_t cpu_quota_us = 1000000;
/* time slice for the round-robin mode and the idle in CPU scheduling */
constexpr unsigned cpu_fill_ms = 10;
constexpr time_t cpu_fill_us = 10000;
}
#endif /* _CORE__INCLUDE__KERNEL__CONFIGURATION_H_ */

View File

@ -16,7 +16,7 @@
#define _CORE__INCLUDE__KERNEL__CPU_H_
/* core includes */
#include <timer.h>
#include <kernel/clock.h>
#include <cpu.h>
#include <kernel/cpu_scheduler.h>
#include <kernel/irq.h>
@ -190,6 +190,12 @@ class Kernel::Cpu_job : public Genode::Cpu::User_context, public Cpu_share
*/
bool own_share_active() { return Cpu_share::ready(); }
void timeout(Timeout * const timeout, time_t const duration_us);
time_t timeout_age_us(Timeout const * const timeout) const;
time_t timeout_max_us() const;
/***************
** Accessors **
***************/
@ -227,8 +233,7 @@ class Kernel::Cpu_idle : public Cpu_job
Cpu_job * helping_sink() { return this; }
};
class Kernel::Cpu : public Genode::Cpu,
public Irq::Pool
class Kernel::Cpu : public Genode::Cpu, public Irq::Pool, private Timeout
{
private:
@ -264,14 +269,14 @@ class Kernel::Cpu : public Genode::Cpu,
};
unsigned const _id;
Clock _clock;
Cpu_idle _idle;
Timer * const _timer;
Cpu_scheduler _scheduler;
Ipi _ipi_irq;
Irq _timer_irq; /* timer irq implemented as empty event */
unsigned _quota() const { return _timer->ms_to_tics(cpu_quota_ms); }
unsigned _fill() const { return _timer->ms_to_tics(cpu_fill_ms); }
unsigned _quota() const { return _clock.us_to_tics(cpu_quota_us); }
unsigned _fill() const { return _clock.us_to_tics(cpu_fill_us); }
public:
@ -312,6 +317,11 @@ class Kernel::Cpu : public Genode::Cpu,
*/
Cpu_job& schedule();
void set_timeout(Timeout * const timeout, time_t const duration_us);
time_t timeout_age_us(Timeout const * const timeout) const;
time_t timeout_max_us() const;
/***************
** Accessors **

View File

@ -78,7 +78,8 @@ class Kernel::Thread_event : public Signal_ack_handler
class Kernel::Thread
:
public Kernel::Object, public Cpu_job, public Cpu_domain_update,
public Ipc_node, public Signal_context_killer, public Signal_handler
public Ipc_node, public Signal_context_killer, public Signal_handler,
private Timeout
{
friend class Thread_event;
friend class Core_thread;
@ -106,6 +107,7 @@ class Kernel::Thread
State _state;
Signal_receiver * _signal_receiver;
char const * const _label;
capid_t _timeout_sigid = 0;
void _init();
@ -245,6 +247,9 @@ class Kernel::Thread
void _call_delete_obj();
void _call_ack_cap();
void _call_delete_cap();
void _call_timeout();
void _call_timeout_age_us();
void _call_timeout_max_us();
template <typename T, typename... ARGS>
void _call_new(ARGS &&... args)
@ -345,6 +350,13 @@ class Kernel::Thread
Cpu_job * helping_sink();
/*************
** Timeout **
*************/
void timeout_triggered();
/***************
** Accessors **
***************/

View File

@ -14,20 +14,31 @@
#ifndef _CORE__INCLUDE__SPEC__CORTEX_A9__TIMER_H_
#define _CORE__INCLUDE__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
namespace Genode { class Timer; }
/**
* Timer driver for core
*/
class Genode::Timer : public Mmio
{
/**
* Timer driver for core
*/
class Timer : public Mmio
{
enum { TICS_PER_MS = Board::CORTEX_A9_PRIVATE_TIMER_CLK / 1000 };
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
@ -46,6 +57,7 @@ namespace Genode
{
struct Timer_enable : Bitfield<0,1> { }; /* enable counting */
struct Irq_enable : Bitfield<2,1> { }; /* unmask interrupt */
struct Prescaler : Bitfield<8,8> { };
};
/**
@ -56,54 +68,56 @@ namespace Genode
struct Event : Bitfield<0,1> { }; /* if counter hit zero */
};
public:
public:
/**
* Constructor
*/
Timer() : Mmio(Board::PRIVATE_TIMER_MMIO_BASE)
{
write<Control::Timer_enable>(0);
}
/**
* Constructor
*/
Timer() : Mmio(Board::PRIVATE_TIMER_MMIO_BASE)
{
write<Control::Timer_enable>(0);
}
/**
* Return kernel name of timer interrupt
*/
static unsigned interrupt_id(unsigned) {
return Board::PRIVATE_TIMER_IRQ; }
/**
* Return kernel name of timer interrupt
*/
static unsigned interrupt_id(unsigned const) {
return Board::PRIVATE_TIMER_IRQ; }
/**
* Start single timeout run
*
* \param tics delay of timer interrupt
*/
inline void start_one_shot(unsigned const tics, unsigned)
{
/* reset timer */
write<Interrupt_status::Event>(1);
Control::access_t control = 0;
Control::Irq_enable::set(control, 1);
write<Control>(control);
/**
* Start single timeout run
*
* \param tics delay of timer interrupt
*/
void start_one_shot(time_t const tics, unsigned const)
{
enum { PRESCALER = Board::CORTEX_A9_PRIVATE_TIMER_DIV - 1 };
/* load timer and start decrementing */
write<Load>(tics);
write<Control::Timer_enable>(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);
/**
* Translate 'ms' milliseconds to a native timer value
*/
static uint32_t ms_to_tics(unsigned const ms)
{
return ms * TICS_PER_MS;
}
/* load timer and start decrementing */
write<Load>(tics);
write<Control::Timer_enable>(1);
}
/**
* Return current native timer value
*/
unsigned value(unsigned const) { return read<Counter>(); }
};
}
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; }
/**
* Return current native timer value
*/
time_t value(unsigned const) { return read<Counter>(); }
time_t max_value() { return (Load::access_t)~0; }
};
namespace Kernel { class Timer : public Genode::Timer { }; }

View File

@ -14,6 +14,9 @@
#ifndef _CORE__INCLUDE__SPEC__EXYNOS5__TIMER_H_
#define _CORE__INCLUDE__SPEC__EXYNOS5__TIMER_H_
/* base-hw includes */
#include <kernel/types.h>
/* core include */
#include <board.h>
@ -32,6 +35,8 @@ class Genode::Timer : public Mmio
{
private:
using time_t = Kernel::time_t;
enum {
PRESCALER = 1,
DIV_MUX = 0,
@ -177,10 +182,8 @@ class Genode::Timer : public Mmio
*
* \param clock input clock
*/
unsigned static _calc_tics_per_ms(unsigned const clock)
{
return clock / (PRESCALER + 1) / (1 << DIV_MUX) / 1000;
}
time_t static _calc_tics_per_ms(unsigned const clock) {
return clock / (PRESCALER + 1) / (1 << DIV_MUX) / 1000; }
unsigned const _tics_per_ms;
@ -218,7 +221,7 @@ class Genode::Timer : public Mmio
/**
* Raise interrupt of CPU 'cpu' once after timeout 'tics'
*/
inline void start_one_shot(unsigned const tics, unsigned const cpu)
void start_one_shot(time_t const tics, unsigned const cpu)
{
switch (cpu) {
case 0:
@ -237,12 +240,7 @@ class Genode::Timer : public Mmio
}
}
/**
* Translate 'ms' milliseconds to a native timer value
*/
unsigned ms_to_tics(unsigned const ms) { return ms * _tics_per_ms; }
unsigned value(unsigned const cpu)
time_t value(unsigned const cpu)
{
switch (cpu) {
case 0: return read<L0_int_cstat::Frcnt>() ? 0 : read<L0_frcnto>();
@ -250,6 +248,14 @@ class Genode::Timer : public Mmio
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 { }; }

View File

@ -14,33 +14,164 @@
#ifndef _CORE__INCLUDE__SPEC__IMX53__TIMER_H_
#define _CORE__INCLUDE__SPEC__IMX53__TIMER_H_
/* base-hw includes */
#include <kernel/types.h>
/* Genode includes */
#include <drivers/timer_base.h>
#include <util/mmio.h>
/* core includes */
#include <board.h>
namespace Genode
{
/**
* Timer driver for core
*/
class Timer;
}
namespace Genode { class Timer; }
class Genode::Timer : public Epit_base
/**
* 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) { return Board::EPIT_1_IRQ; }
static unsigned interrupt_id(unsigned const)
{
return Board::EPIT_1_IRQ;
}
/**
* Constructor
*/
Timer() : Epit_base(Board::EPIT_1_MMIO_BASE) { }
Timer() : Mmio(Board::EPIT_1_MMIO_BASE) { }
/**
* 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 { class Timer : public Genode::Timer { }; }

View File

@ -14,6 +14,9 @@
#ifndef _TIMER_H_
#define _TIMER_H_
/* base-hw includes */
#include <kernel/types.h>
/* Genode includes */
#include <base/printf.h>
#include <base/stdint.h>
@ -30,6 +33,13 @@ 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()
@ -48,48 +58,35 @@ struct Genode::Timer
asm volatile ("csrs sie, %0" : : "r"(STIE));
}
enum {
SPIKE_TIMER_HZ = 500000,
MS_TICS = SPIKE_TIMER_HZ / 1000,
};
/**
* Start single timeout run
*
* \param tics delay of timer interrupt
*/
void start_one_shot(unsigned const tics, unsigned /* cpu */)
void start_one_shot(time_t const tics, unsigned const)
{
_timeout = _stime() + tics;
Machine::set_sys_timer(_timeout);
}
/**
* Translate milliseconds to a native timer value
*/
unsigned ms_to_tics(unsigned const ms)
{
return ms * MS_TICS;
}
time_t tics_to_us(time_t const tics) const {
return (tics / TICS_PER_MS) * 1000; }
/**
* Translate native timer value to milliseconds
*/
unsigned tics_to_ms(unsigned const tics)
{
return tics / MS_TICS;
}
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
*/
unsigned value(unsigned const)
time_t value(unsigned const)
{
addr_t time = _stime();
return time < _timeout ? _timeout - time : 0;
}
static unsigned interrupt_id(int) { return 1; }
static unsigned interrupt_id(unsigned const) { return 1; }
};
namespace Kernel { class Timer : public Genode::Timer { }; }

View File

@ -14,26 +14,30 @@
#ifndef _CORE__INCLUDE__SPEC__RPI__TIMER_H_
#define _CORE__INCLUDE__SPEC__RPI__TIMER_H_
/* base-hw includes */
#include <kernel/types.h>
/* Genode includes */
#include <util/mmio.h>
/* core includes */
#include <board.h>
namespace Genode
{
/**
* Timer driver for core
*
* Timer channel 0 apparently doesn't work on the RPI, so we use channel 1
*/
class Timer;
}
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> { };
@ -42,9 +46,10 @@ class Genode::Timer : public Mmio
Timer() : Mmio(Board::SYSTEM_TIMER_MMIO_BASE) { }
static unsigned interrupt_id(int) { return Board::SYSTEM_TIMER_IRQ; }
static unsigned interrupt_id(unsigned const) {
return Board::SYSTEM_TIMER_IRQ; }
inline void start_one_shot(uint32_t const tics, unsigned)
void start_one_shot(time_t const tics, unsigned const)
{
write<Cs::M1>(1);
read<Cs>();
@ -52,10 +57,15 @@ class Genode::Timer : public Mmio
write<Cmp>(read<Clo>() + tics);
}
static uint32_t ms_to_tics(unsigned const ms) {
return (Board::SYSTEM_TIMER_CLOCK / 1000) * ms; }
time_t tics_to_us(time_t const tics) const {
return (tics / TICS_PER_MS) * 1000; }
unsigned value(unsigned)
time_t us_to_tics(time_t const us) const {
return (us / 1000) * TICS_PER_MS; }
time_t max_value() { return (Clo::access_t)~0; }
time_t value(unsigned const)
{
Cmp::access_t const cmp = read<Cmp>();
Clo::access_t const clo = read<Clo>();

View File

@ -15,6 +15,9 @@
#ifndef _CORE__INCLUDE__SPEC__X86__TIMER_H_
#define _CORE__INCLUDE__SPEC__X86__TIMER_H_
/* base-hw includes */
#include <kernel/types.h>
/* Genode includes */
#include <util/mmio.h>
#include <base/stdint.h>
@ -24,18 +27,17 @@
#include <port_io.h>
#include <board.h>
namespace Genode
{
/**
* LAPIC-based timer driver for core
*/
class Timer;
}
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,
@ -58,6 +60,17 @@ class Genode::Timer : public Mmio
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 */
@ -91,6 +104,9 @@ class Genode::Timer : public Mmio
Timer() : Mmio(Board::MMIO_LAPIC_BASE)
{
write<Divide_configuration::Divide_value>(
Divide_configuration::Divide_value::MAX);
/* Enable LAPIC timer in one-shot mode */
write<Tmr_lvt::Vector>(Board::TIMER_VECTOR_KERNEL);
write<Tmr_lvt::Delivery>(0);
@ -99,39 +115,34 @@ class Genode::Timer : public Mmio
/* Calculate timer frequency */
_tics_per_ms = _pit_calc_timer_freq();
PINF("LAPIC: timer frequency %u kHz", _tics_per_ms);
}
/**
* Disable PIT timer channel. This is necessary since BIOS sets up
* channel 0 to fire periodically.
*/
static void disable_pit(void)
static void disable_pit()
{
outb(PIT_MODE, 0x30);
outb(PIT_CH0_DATA, 0);
outb(PIT_CH0_DATA, 0);
}
static unsigned interrupt_id(int)
{
return Board::TIMER_VECTOR_KERNEL;
}
static unsigned interrupt_id(unsigned const) {
return Board::TIMER_VECTOR_KERNEL; }
inline void start_one_shot(uint32_t const tics, unsigned)
{
write<Tmr_initial>(tics);
}
void start_one_shot(time_t const tics, unsigned const) {
write<Tmr_initial>(tics); }
uint32_t ms_to_tics(unsigned const ms)
{
return ms * _tics_per_ms;
}
time_t tics_to_us(time_t const tics) const {
return (tics / _tics_per_ms) * 1000; }
unsigned value(unsigned)
{
return read<Tmr_current>();
}
time_t us_to_tics(time_t const us) const {
return (us / 1000) * _tics_per_ms; }
time_t max_value() { return (Tmr_initial::access_t)~0; }
time_t value(unsigned const) { return read<Tmr_current>(); }
};
namespace Kernel { class Timer : public Genode::Timer { }; }

View File

@ -15,6 +15,7 @@
#define _CORE__INCLUDE__SPEC__X86_64__MUEN__TIMER_H_
#include <base/printf.h>
#include <kernel/types.h>
/* core includes */
#include <board.h>
@ -32,9 +33,9 @@ class Genode::Timer
{
private:
enum {
TIMER_DISABLED = ~0ULL,
};
using time_t = Kernel::time_t;
enum { TIMER_DISABLED = ~0ULL };
uint64_t _tics_per_ms;
@ -71,22 +72,21 @@ class Genode::Timer
region.address, _tics_per_ms, _timer_page->vector);
}
static unsigned interrupt_id(int)
{
return Board::TIMER_VECTOR_KERNEL;
}
static unsigned interrupt_id(int) {
return Board::TIMER_VECTOR_KERNEL; }
inline void start_one_shot(uint32_t const tics, unsigned)
{
_timer_page->value = rdtsc() + tics;
}
inline void start_one_shot(time_t const tics, unsigned) {
_timer_page->value = rdtsc() + tics; }
uint32_t ms_to_tics(unsigned const ms)
{
return ms * _tics_per_ms;
}
time_t tics_to_us(time_t const tics) const {
return (tics / _tics_per_ms) * 1000; }
unsigned value(unsigned)
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 (_timer_page->value != TIMER_DISABLED
@ -96,9 +96,6 @@ class Genode::Timer
return 0;
}
/*
* Dummies
*/
static void disable_pit(void) { }
};

View File

@ -0,0 +1,101 @@
/*
* \brief A clock manages a continuous time and timeouts on it
* \author Martin Stein
* \date 2016-03-23
*/
/*
* Copyright (C) 2016 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.
*/
/* Core includes */
#include <kernel/clock.h>
using namespace Kernel;
void Clock::_insert_timeout(Timeout * const timeout)
{
/* timeouts may get re-inserted as result of an update */
if (timeout->_listed) { _timeout_list.remove(timeout); }
timeout->_listed = true;
/* timeouts are ordered ascending according to their end time */
Timeout * t1 = 0;
Timeout * t2 = _timeout_list.first();
for (; t2 && t2->_end < timeout->_end; t1 = t2, t2 = t2->next()) ;
_timeout_list.insert(timeout, t1);
}
void Clock::_remove_timeout(Timeout * const timeout)
{
timeout->_listed = false;
_timeout_list.remove(timeout);
}
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 const age = (time_t)_time - timeout->_start;
return _timer->tics_to_us(age);
}
time_t Clock::timeout_max_us() const
{
return _timer->tics_to_us(_timer->max_value());
}
void Clock::set_timeout(Timeout * const timeout, time_t const duration)
{
timeout->_start = _time;
timeout->_end = _time + duration;
_insert_timeout(timeout);
}
void Clock::schedule_timeout()
{
Timeout const * const timeout = _timeout_list.first();
time_t const duration = (time_t)timeout->_end - _time;
_last_timeout_duration = duration;
_timer->start_one_shot(duration, _cpu_id);
}
time_t Clock::update_time()
{
/* update time */
time_t const old_value = _last_timeout_duration;
time_t const new_value = _timer->value(_cpu_id);
// PERR("%lu %lu", old_value, new_value);
time_t const duration = old_value > new_value ? old_value - new_value : 1;
_time += duration;
return duration;
}
void Clock::process_timeouts()
{
while (true) {
Timeout * const timeout = _timeout_list.first();
if (!timeout || timeout->_end > _time) { break; }
_remove_timeout(timeout);
timeout->timeout_triggered();
}
}
Clock::Clock(unsigned const cpu_id, Timer * const timer)
:
_cpu_id(cpu_id), _timer(timer) { }

View File

@ -37,6 +37,24 @@ namespace Kernel
** Cpu_job **
*************/
time_t Cpu_job::timeout_age_us(Timeout const * const timeout) const
{
return _cpu->timeout_age_us(timeout);
}
time_t Cpu_job::timeout_max_us() const
{
return _cpu->timeout_max_us();
}
void Cpu_job::timeout(Timeout * const timeout, time_t const us)
{
_cpu->set_timeout(timeout, us);
}
void Cpu_job::_activate_own_share() { _cpu->schedule(this); }
@ -89,7 +107,8 @@ void Cpu_job::quota(unsigned const q)
Cpu_job::Cpu_job(Cpu_priority const p, unsigned const q)
: Cpu_share(p, q), _cpu(0) { }
:
Cpu_share(p, q), _cpu(0) { }
Cpu_job::~Cpu_job()
@ -113,6 +132,17 @@ void Cpu_idle::_main() { while (1) { Genode::Cpu::wait_for_interrupt(); } }
** Cpu **
*********/
void Cpu::set_timeout(Timeout * const timeout, time_t const duration_us) {
_clock.set_timeout(timeout, _clock.us_to_tics(duration_us)); }
time_t Cpu::timeout_age_us(Timeout const * const timeout) const {
return _clock.timeout_age_us(timeout); }
time_t Cpu::timeout_max_us() const { return _clock.timeout_max_us(); }
void Cpu::schedule(Job * const job)
{
if (_id == executing_id()) { _scheduler.ready(job); }
@ -131,20 +161,20 @@ bool Cpu::interrupt(unsigned const irq_id)
Cpu_job & Cpu::schedule()
{
/* get new job */
Job & old_job = scheduled_job();
/* update scheduler */
unsigned const old_time = _scheduler.head_quota();
unsigned const new_time = _timer->value(_id);
unsigned quota = old_time > new_time ? old_time - new_time : 1;
time_t quota = _clock.update_time();
Job & old_job = scheduled_job();
old_job.exception(id());
_clock.process_timeouts();
_scheduler.update(quota);
/* get new job */
Job & new_job = scheduled_job();
quota = _scheduler.head_quota();
assert(quota);
_timer->start_one_shot(quota, _id);
_clock.set_timeout(this, quota);
_clock.schedule_timeout();
/* switch to new job */
switch_to(new_job);
@ -155,10 +185,11 @@ Cpu_job & Cpu::schedule()
Cpu::Cpu(unsigned const id, Timer * const timer)
: _id(id), _idle(this), _timer(timer),
_scheduler(&_idle, _quota(), _fill()),
_ipi_irq(*this),
_timer_irq(_timer->interrupt_id(_id), *this) { }
:
_id(id), _clock(_id, timer), _idle(this),
_scheduler(&_idle, _quota(), _fill()),
_ipi_irq(*this), _timer_irq(timer->interrupt_id(_id), *this)
{ }
/**************
@ -175,6 +206,25 @@ 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); }
}

View File

@ -21,7 +21,6 @@ extern "C" void kernel()
using namespace Kernel;
Cpu * const cpu = cpu_pool()->cpu(Cpu::executing_id());
cpu->scheduled_job().exception(cpu->id());
cpu->schedule().proceed(cpu->id());
}

View File

@ -187,7 +187,7 @@ 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()->ms_to_tics(Kernel::cpu_quota_ms);
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);
}
@ -297,6 +297,34 @@ void Thread::_call_await_request_msg()
}
void Thread::_call_timeout()
{
_timeout_sigid = user_arg_2();
Cpu_job::timeout(this, user_arg_1());
}
void Thread::_call_timeout_age_us()
{
user_arg_0(Cpu_job::timeout_age_us(this));
}
void Thread::_call_timeout_max_us()
{
user_arg_0(Cpu_job::timeout_max_us());
}
void Thread::timeout_triggered()
{
Signal_context * const c =
pd()->cap_tree().find<Signal_context>(_timeout_sigid);
if(!c || c->submit(1)) {
PWRN("%s -> %s: failed to submit timeout signal", pd_label(), label());
}
}
void Thread::_call_send_request_msg()
{
Object_identity_reference * oir = pd()->cap_tree().find(user_arg_1());
@ -576,6 +604,9 @@ void Thread::_call()
case call_id_print_char(): _call_print_char(); return;
case call_id_ack_cap(): _call_ack_cap(); return;
case call_id_delete_cap(): _call_delete_cap(); return;
case call_id_timeout(): _call_timeout(); return;
case call_id_timeout_age_us(): _call_timeout_age_us(); return;
case call_id_timeout_max_us(): _call_timeout_max_us(); return;
default:
/* check wether this is a core thread */
if (!_core()) {

View File

@ -29,7 +29,6 @@ extern "C" void kernel()
cpu_id = Cpu::executing_id();
Cpu * const cpu = cpu_pool()->cpu(cpu_id);
cpu->scheduled_job().exception(cpu_id);
new_job = &cpu->schedule();
}

View File

@ -1,191 +0,0 @@
/*
* \brief Driver base for the Enhanced Periodic Interrupt Timer (Freescale)
* \author Norman Feske
* \author Martin Stein
* \author Stefan Kalkowski
* \date 2012-10-25
*/
/*
* Copyright (C) 2012-2013 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__SPEC__EPIT__DRIVERS__TIMER_BASE_H_
#define _INCLUDE__SPEC__EPIT__DRIVERS__TIMER_BASE_H_
/* Genode includes */
#include <util/mmio.h>
namespace Genode { class Epit_base; }
/**
* Core timer
*/
class Genode::Epit_base : public Mmio
{
protected:
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);
}
void _start_one_shot(unsigned const tics)
{
/* 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);
}
public:
/**
* Constructor
*/
Epit_base(addr_t base) : Mmio(base) { _reset(); }
/**
* Start single timeout run
*
* \param tics delay of timer interrupt
*/
void start_one_shot(unsigned const tics, unsigned)
{
_start_one_shot(tics);
}
/**
* Stop the timer from a one-shot run
*
* \return last native timer value of the one-shot run
*/
unsigned long stop_one_shot()
{
/* disable timer */
write<Cr::En>(0);
return value(0);
}
/**
* Translate milliseconds to a native timer value
*/
unsigned ms_to_tics(unsigned const ms)
{
return TICS_PER_MS * ms;
}
/**
* Translate native timer value to milliseconds
*/
unsigned tics_to_ms(unsigned const tics)
{
return tics / TICS_PER_MS;
}
/**
* Return current native timer value
*/
unsigned value(unsigned const)
{
return read<Sr::Ocif>() ? 0 : read<Cnt>();
}
};
#endif /* _INCLUDE__SPEC__EPIT__DRIVERS__TIMER_BASE_H_ */

View File

@ -55,6 +55,7 @@ struct Genode::Board_base
CORTEX_A9_PRIVATE_MEM_BASE = 0x00a00000,
CORTEX_A9_PRIVATE_MEM_SIZE = 0x00002000,
CORTEX_A9_PRIVATE_TIMER_CLK = 395037500,
CORTEX_A9_PRIVATE_TIMER_DIV = 170,
/* L2 cache controller */
PL310_MMIO_BASE = 0x00a02000,

View File

@ -59,6 +59,7 @@ struct Genode::Board_base
CORTEX_A9_PRIVATE_MEM_BASE = 0x48240000,
CORTEX_A9_PRIVATE_MEM_SIZE = 0x00002000,
CORTEX_A9_PRIVATE_TIMER_CLK = 350000000,
CORTEX_A9_PRIVATE_TIMER_DIV = 175,
CORTEX_A9_WUGEN_MMIO_BASE = 0x48281000,
CORTEX_A9_SCU_MMIO_BASE = 0x48240000,

View File

@ -51,6 +51,7 @@ struct Genode::Board_base
/* CPU */
CORTEX_A9_PRIVATE_TIMER_CLK = 100000000,
CORTEX_A9_PRIVATE_TIMER_DIV = 100,
CORTEX_A9_PRIVATE_MEM_BASE = 0x1f000000,
CORTEX_A9_PRIVATE_MEM_SIZE = 0x2000,

View File

@ -23,15 +23,9 @@ namespace Genode { struct Board_base; }
*/
struct Genode::Board_base : Zynq::Board_base
{
enum
{
/* clocks (assuming 6:2:1 mode) */
CPU_1X_CLOCK = 111111115,
CPU_6X4X_CLOCK = 6 * CPU_1X_CLOCK,
/* CPU */
CORTEX_A9_CLOCK = CPU_6X4X_CLOCK,
CORTEX_A9_PRIVATE_TIMER_CLK = CORTEX_A9_CLOCK,
enum {
CORTEX_A9_PRIVATE_TIMER_CLK = 100000000,
CORTEX_A9_PRIVATE_TIMER_DIV = 100,
};
};

View File

@ -5,12 +5,11 @@
#
# denote wich specs are also fullfilled by this spec
SPECS += cortex_a8 imx gpio framebuffer epit
SPECS += cortex_a8 imx gpio framebuffer
# add repository relative include paths
REP_INC_DIR += include/spec/imx53
REP_INC_DIR += include/spec/imx
REP_INC_DIR += include/spec/epit
# include implied specs
include $(call select_from_repositories,mk/spec/cortex_a8.mk)

View File

@ -7,12 +7,11 @@
#
# denote wich specs are also fullfilled by this spec
SPECS += cortex_a9 imx6 imx epit
SPECS += cortex_a9 imx6 imx
# add repository relative include paths
REP_INC_DIR += include/spec/imx6
REP_INC_DIR += include/spec/imx
REP_INC_DIR += include/spec/epit
# include implied specs
include $(call select_from_repositories,mk/spec/cortex_a9.mk)

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw $(REP_DIR)/src/drivers/timer/spec/hw/spec/epit
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw $(REP_DIR)/src/drivers/timer/spec/hw/spec/exynos5
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1 +1,3 @@
LIBS = hw_timer
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw $(REP_DIR)/src/drivers/timer/spec/hw/spec/omap4
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw $(REP_DIR)/src/drivers/timer/spec/hw/spec/pbxa9
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw $(REP_DIR)/src/drivers/timer/spec/hw/spec/riscv
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw $(REP_DIR)/src/drivers/timer/spec/hw/spec/rpi
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1,3 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/include_pit
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -1,4 +0,0 @@
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw
INC_DIR += $(REP_DIR)/src/drivers/timer/spec/hw/spec/zynq
include $(REP_DIR)/lib/mk/timer.inc

View File

@ -15,115 +15,94 @@
#define _OS__SRC__DRIVERS__TIMER__HW__PLATFORM_TIMER_H_
/* Genode includes */
#include <irq_session/connection.h>
#include <base/signal.h>
#include <os/server.h>
/* Local includes */
#include <platform_timer_base.h>
/* base-hw includes */
#include <kernel/interface.h>
/**
* Platform timer specific for base-hw
*/
class Platform_timer : public Platform_timer_base,
public Genode::Irq_connection
class Platform_timer
{
private:
enum { MAX_TIMER_IRQS_PER_MS = 1 };
using time_t = Kernel::time_t;
unsigned long const _max_timeout_us; /* maximum timeout in microsecs */
unsigned long mutable _curr_time_us; /* accumulate already measured timeouts */
unsigned long mutable _init_value; /* mark last processed timer value */
Genode::Lock mutable _update_curr_time_lock; /* serialize curr_time access */
Genode::Signal_receiver _irq_rec;
Genode::Signal_context _irq_ctx;
Genode::Signal_receiver _sigrec;
Genode::Signal_context _sigctx;
Kernel::capid_t const _sigid;
unsigned long mutable _curr_time_us;
Genode::Lock mutable _curr_time_us_lock;
unsigned long mutable _last_timeout_us;
time_t const _max_timeout_us;
public:
/**
* Constructor
*/
Platform_timer()
:
Irq_connection(Platform_timer_base::IRQ),
_max_timeout_us(tics_to_us(max_value())),
_curr_time_us(0), _init_value(0)
_sigid(_sigrec.manage(&_sigctx).dst()), _curr_time_us(0),
_last_timeout_us(0), _max_timeout_us(Kernel::timeout_max_us())
{
Irq_connection::sigh(_irq_rec.manage(&_irq_ctx));
Irq_connection::ack_irq();
PINF("Maximum timeout %lu us", _max_timeout_us);
if (max_timeout() < min_timeout()) {
PERR("Minimum timeout greater then maximum timeout");
throw Genode::Exception();
}
}
~Platform_timer() { _irq_rec.dissolve(&_irq_ctx); }
~Platform_timer() { _sigrec.dissolve(&_sigctx); }
/**
* Refresh and return our instance-own "now"-time in microseconds
*
* This function has to be executed regulary,
* at least all max_timeout() us.
* This function has to be executed regulary, at least all
* max_timeout() us.
*/
unsigned long curr_time() const
{
/* serialize updates on timeout counter */
Genode::Lock::Guard lock(_update_curr_time_lock);
/* get time that passed since last time we've read the timer */
bool wrapped;
unsigned long const v = value(wrapped);
unsigned long passed_time;
if (wrapped) passed_time = _init_value + max_value() - v;
else passed_time = _init_value - v;
/* update initial value for subsequent calculations */
_init_value = v;
/* refresh our timeout counter and return it */
_curr_time_us += tics_to_us(passed_time);
Genode::Lock::Guard lock(_curr_time_us_lock);
time_t const passed_us = Kernel::timeout_age_us();
_last_timeout_us -= passed_us;
_curr_time_us += passed_us;
return _curr_time_us;
}
/**
* Return maximum timeout as supported by the platform
* Return maximum timeout in microseconds
*/
unsigned long max_timeout() const { return _max_timeout_us; }
time_t max_timeout() const { return _max_timeout_us; }
/**
* Schedule next timeout, oversized timeouts are truncated
* Return minimum timeout in microseconds
*/
static time_t min_timeout() { return 1000; }
/**
* Schedule next timeout, bad timeouts are adapted
*
* \param timeout_us Timeout in microseconds
*/
void schedule_timeout(unsigned long timeout_us)
void schedule_timeout(time_t timeout_us)
{
/* serialize updates on timeout counter */
Genode::Lock::Guard lock(_update_curr_time_lock);
Genode::Lock::Guard lock(_curr_time_us_lock);
if (timeout_us < min_timeout()) { timeout_us = min_timeout(); }
if (timeout_us > max_timeout()) { timeout_us = max_timeout(); }
/*
* Constrain timout value with our maximum IRQ rate and the maximum
* possible timeout.
*/
if (timeout_us < 1000/MAX_TIMER_IRQS_PER_MS)
timeout_us = 1000/MAX_TIMER_IRQS_PER_MS;
if (timeout_us > _max_timeout_us)
timeout_us = _max_timeout_us;
/*
* Once the timer runs, one can wait for its IRQ and update our
* Once the timer runs, one can wait for its signal and update our
* timeout counter through 'curr_time()' (We rely on the fact that
* this is done at least one time in every max-timeout period)
*/
_init_value = us_to_tics(timeout_us);
run_and_wrap(_init_value);
_last_timeout_us = timeout_us;
Kernel::timeout(timeout_us, _sigid);
}
/**
* Await the lastly scheduled timeout
*/
void wait_for_timeout(Genode::Thread *)
{
_irq_rec.wait_for_signal();
Irq_connection::ack_irq();
}
void wait_for_timeout(Genode::Thread *) { _sigrec.wait_for_signal(); }
};
#endif /* _OS__SRC__DRIVERS__TIMER__HW__PLATFORM_TIMER_H_ */

View File

@ -1,182 +0,0 @@
/*
* \brief Basic driver for the ARM SP804 timer
* \author Martin stein
* \date 2012-04-23
*/
/*
* Copyright (C) 2012-2013 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 _DRIVERS__TIMER__SPEC__HW__SP804_BASE_H_
#define _DRIVERS__TIMER__SPEC__HW__SP804_BASE_H_
/* Genode includes */
#include <util/mmio.h>
namespace Genode { template <unsigned long> class Sp804_base; }
/**
* Basic driver for the ARM SP804 timer
*/
template <unsigned long CLK>
class Genode::Sp804_base : public Mmio
{
enum {
TICS_PER_MS = CLK / 1000,
TICS_PER_US = TICS_PER_MS / 1000,
AVOID_INVALID_TEMPLATE_ARGS = 1 / TICS_PER_US,
};
/**
* Holds value that shall be loaded to the timer value register
*/
struct Load : Register<0x0, 32> { };
/**
* Raw interrupt status
*/
struct Ris : Register<0x10, 1> { };
/**
* Background load register
*/
struct Bgload : Register<0x18, 32> { };
/**
* Timer value register
*/
struct Value : Register<0x4, 32> { enum { MAX_VALUE = 0xffffffff }; };
/**
* Timer control register
*/
struct Control : Register<0x8, 8>
{
struct Oneshot : Bitfield<0,1> { };
struct Size : Bitfield<1,1> { };
struct Pre : Bitfield<2,2> { };
struct Int_en : Bitfield<5,1> { };
struct Mode : Bitfield<6,1> { };
struct Timer_en : Bitfield<7,1> { };
};
/**
* Clears the timer interrupt
*/
struct Int_clr : Register<0xc, 1> { };
public:
/**
* Constructor, clears interrupt output
*/
Sp804_base(addr_t const mmio_base) : Mmio(mmio_base) {
clear_interrupt(); }
/**
* Run the timer in order that it raises IRQ when
* it reaches zero, then stop
*
* \param tics native timer value used to assess the delay
* of the timer interrupt as of this call
*/
void run_and_stop(unsigned long const tics)
{
/* disable and configure timer for a one-shot */
clear_interrupt();
write<typename Control::Timer_en>(0);
write<Control>(Control::Timer_en::bits(0) |
Control::Mode::bits(1) |
Control::Int_en::bits(1) |
Control::Pre::bits(0) |
Control::Size::bits(1) |
Control::Oneshot::bits(1));
/* load value and enable timer */
write<Load>(tics);
write<typename Control::Timer_en>(1);
}
/**
* Run the timer in order that it raises IRQ when it reaches zero,
* then wrap and continue
*
* \param tics native timer value used to assess the delay
* of the timer interrupt as of this call
*/
void run_and_wrap(unsigned long const tics)
{
/* configure the timer in order that it reloads on 0 */
clear_interrupt();
write<typename Control::Timer_en>(0);
write<Control>(Control::Timer_en::bits(0) |
Control::Mode::bits(1) |
Control::Int_en::bits(1) |
Control::Pre::bits(0) |
Control::Size::bits(1) |
Control::Oneshot::bits(0));
/* start timer with the initial value */
write<Load>(tics);
write<typename Control::Timer_en>(1);
/*
* Ensure that the timer loads its max value instead of the
* initial value when it reaches 0 in order that it looks like
* it wraps.
*/
write<Bgload>(max_value());
}
/**
* Current timer value
*/
unsigned long value() const { return read<Value>(); }
/**
* Get timer value and corresponding wrapped status of timer
*/
unsigned long value(bool & wrapped) const
{
typename Value::access_t v = read<Value>();
wrapped = (bool)read<Ris>();
if (!wrapped) return v;
return read<Value>();
}
/**
* Clear interrupt output line
*/
void clear_interrupt() { write<Int_clr>(1); }
/**
* Translate milliseconds to a native timer value
*/
static unsigned long ms_to_tics(unsigned long const ms) {
return ms * TICS_PER_MS; }
/**
* Translate native timer value to microseconds
*/
static unsigned long tics_to_us(unsigned long const tics) {
return tics / TICS_PER_US; }
/**
* Translate microseconds to a native timer value
*/
static unsigned long us_to_tics(unsigned long const us) {
return us * TICS_PER_US; }
/**
* Translate native timer value to microseconds
*/
static unsigned long max_value() { return Value::MAX_VALUE; }
};
#endif /* _DRIVERS__TIMER__SPEC__HW__SP804_BASE_H_ */

View File

@ -1,115 +0,0 @@
/*
* \brief Basic driver behind platform timer
* \author Stefan Kalkowski
* \date 2012-10-25
*/
/*
* Copyright (C) 2012-2013 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 _HW__EPIT__PLATFORM_TIMER_BASE_H_
#define _HW__EPIT__PLATFORM_TIMER_BASE_H_
/* Genode includes */
#include <io_mem_session/connection.h>
#include <util/mmio.h>
#include <irq_session/connection.h>
#include <drivers/board_base.h>
#include <drivers/timer_base.h>
namespace Genode
{
/**
* Epit timer
*/
class Epit : public Epit_base
{
private:
/**
* Timer tics per microsecond
*/
static float tics_per_us() {
return (float)TICS_PER_MS / 1000.0; }
/**
* Microseconds per timer tic
*/
static float us_per_tic() { return 1.0 / tics_per_us(); }
public:
/**
* Constructor
*
* \param base MMIO base
*/
Epit(addr_t const base) : Epit_base(base) { }
/**
* Count down 'value', raise IRQ output, wrap counter and continue
*/
void run_and_wrap(unsigned long value) { _start_one_shot(value); }
/**
* Maximum timeout value
*/
unsigned long max_value() const { return read<Lr>(); }
/**
* Translate timer tics to microseconds
*/
unsigned long tics_to_us(unsigned long const tics) const
{
float const us = tics * us_per_tic();
return (unsigned long)us;
}
/**
* Translate microseconds to timer tics
*/
unsigned long us_to_tics(unsigned long const us)
{
float const tics = us * tics_per_us();
return (unsigned long)tics;
}
/**
* Sample the timer counter and according wrapped status
*/
unsigned long value(bool & wrapped) const
{
unsigned long v = read<Cnt>();
wrapped = (bool)read<Sr::Ocif>();
return wrapped ? read<Cnt>() : v;
}
};
}
/**
* Basic driver behind platform timer
*/
class Platform_timer_base : public Genode::Io_mem_connection,
public Genode::Epit
{
public:
enum { IRQ = Genode::Board_base::EPIT_2_IRQ };
/**
* Constructor
*/
Platform_timer_base()
: Io_mem_connection(Genode::Board_base::EPIT_2_MMIO_BASE,
Genode::Board_base::EPIT_2_MMIO_SIZE),
Genode::Epit((Genode::addr_t)Genode::env()->rm_session()
->attach(dataspace()))
{ }
};
#endif /* _HW__EPIT__PLATFORM_TIMER_BASE_H_ */

View File

@ -1,197 +0,0 @@
/*
* \brief Basic driver behind platform timer
* \author Martin stein
* \date 2013-04-04
*/
/*
* Copyright (C) 2013 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 _HW__EXYNOS5__PLATFORM_TIMER_BASE_H_
#define _HW__EXYNOS5__PLATFORM_TIMER_BASE_H_
/* Genode includes */
#include <io_mem_session/connection.h>
#include <irq_session/connection.h>
#include <drivers/board_base.h>
#include <util/mmio.h>
namespace Genode
{
/**
* Exynos 5250 pulse width modulation timer
*/
class Pwm : public Mmio
{
enum {
PRESCALER = 2,
TICS_PER_US = Board_base::PWM_CLOCK / PRESCALER / 1000 / 1000,
};
/**
* Timer configuration 0
*/
struct Cfg0 : Register<0x0, 32>
{
struct Prescaler0 : Bitfield<0, 8>
{
enum { DEFAULT = PRESCALER - 1 };
};
};
/**
* Timer configuration 1
*/
struct Cfg1 : Register<0x4, 32>
{
struct Div0 : Bitfield<0, 4> { enum { DISABLE = 0 }; };
};
/**
* Timer control
*/
struct Con : Register<0x8, 32>
{
struct Enable0 : Bitfield<0, 1> { };
struct Update0 : Bitfield<1, 1> { };
struct Invert_tout0 : Bitfield<2, 1> { };
struct Auto_reload0 : Bitfield<3, 1> { };
struct Deadzone_en : Bitfield<4, 1> { };
/**
* Initialization value
*/
static access_t init_value()
{
return Invert_tout0::bits(0) |
Auto_reload0::bits(1) |
Deadzone_en::bits(0);
}
};
/**
* Timer 0 count buffer
*/
struct Cntb0 : Register<0xc, 32> { };
/**
* Timer 0 compare buffer
*/
struct Cmpb0 : Register<0x10, 32> { };
/**
* Timer 0 count observation
*/
struct Cnto0 : Register<0x14, 32> { };
/**
* Timer IRQ control and status
*/
struct Int : Register<0x44, 32>
{
struct En0 : Bitfield<0, 1> { };
struct En1 : Bitfield<1, 1> { };
struct En2 : Bitfield<2, 1> { };
struct En3 : Bitfield<3, 1> { };
struct En4 : Bitfield<4, 1> { };
struct Stat0 : Bitfield<5, 1> { };
/**
* Initialization value
*/
static access_t init_value()
{
return En0::bits(1) |
En1::bits(0) |
En2::bits(0) |
En3::bits(0) |
En4::bits(0);
}
};
public:
/**
* Constructor
*
* \param base MMIO base
*/
Pwm(addr_t const base) : Mmio(base)
{
write<Cfg0::Prescaler0>(Cfg0::Prescaler0::DEFAULT);
write<Cfg1::Div0>(Cfg1::Div0::DISABLE);
write<Int>(Int::init_value());
write<Con>(Con::init_value());
write<Cmpb0>(0);
}
/**
* Count down 'value', raise IRQ output, wrap counter and continue
*/
void run_and_wrap(unsigned long value)
{
write<Cntb0>(value);
write<Con::Enable0>(0);
write<Con::Update0>(1);
write<Con::Update0>(0);
write<Int::Stat0>(1);
write<Cntb0>(max_value());
write<Con::Enable0>(1);
}
/**
* Maximum timeout value
*/
unsigned long max_value() const { return (Cntb0::access_t)~0; }
/**
* Translate timer tics to microseconds
*/
unsigned long tics_to_us(unsigned long const tics) const {
return tics / TICS_PER_US; }
/**
* Translate microseconds to timer tics
*/
unsigned long us_to_tics(unsigned long const us) const {
return us * TICS_PER_US; }
/**
* Sample the timer counter and according wrapped status
*/
unsigned long value(bool & wrapped) const
{
unsigned long v = read<Cnto0>();
wrapped = (bool)read<Int::Stat0>();
return wrapped ? read<Cnto0>() : v;
}
};
}
/**
* Basic driver behind platform timer
*/
class Platform_timer_base : public Genode::Io_mem_connection,
public Genode::Pwm
{
public:
enum { IRQ = Genode::Board_base::PWM_IRQ_0 };
/**
* Constructor
*/
Platform_timer_base()
: Io_mem_connection(Genode::Board_base::PWM_MMIO_BASE,
Genode::Board_base::PWM_MMIO_SIZE),
Genode::Pwm((Genode::addr_t)Genode::env()->rm_session()
->attach(dataspace()))
{ }
};
#endif /* _HW__EXYNOS5__PLATFORM_TIMER_BASE_H_ */

View File

@ -1,269 +0,0 @@
/*
* \brief Basic driver behind platform timer
* \author Martin Stein
* \date 2012-05-03
*/
/*
* Copyright (C) 2012-2013 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 _HW__OMAP4__PLATFORM_TIMER_BASE_H_
#define _HW__OMAP4__PLATFORM_TIMER_BASE_H_
/* Genode includes */
#include <io_mem_session/connection.h>
#include <util/mmio.h>
#include <irq_session/connection.h>
#include <drivers/board_base.h>
namespace Genode
{
/**
* Omap4 general purpose timer 3 through 9 and 11
*/
class Omap4_gp_timer_1 : public Mmio
{
/**
* Timer tics per microsecond
*/
static float tics_per_us() {
return (float)Board_base::SYS_CLK / 1000000; }
/**
* Microsecodns per timer tic
*/
static float us_per_tic() { return 1 / tics_per_us(); }
/**
* L4 interface control
*/
struct Tiocp_cfg : Register<0x10, 32>
{
struct Softreset : Bitfield<0, 1> { }; /* SW reset active */
struct Idlemode : Bitfield<2, 2> /* action on IDLE request */
{
enum { FORCE_IDLE = 0 };
};
};
/**
* Timer wake-up enable register
*/
struct Twer : Register<0x20, 32>
{
struct Mat_wup_ena : Bitfield<0, 1> { }; /* wakeup on match */
struct Ovf_wup_ena : Bitfield<1, 1> { }; /* wakeup on overflow */
struct Tcar_wup_ena : Bitfield<2, 1> { }; /* wakeup on capture */
/**
* Timer initialization value
*/
static access_t init_timer()
{
return Mat_wup_ena::bits(0) |
Ovf_wup_ena::bits(0) |
Tcar_wup_ena::bits(0);
}
};
/**
* Timer synchronous interface control register
*/
struct Tsicr : Register<0x54, 32>
{
struct Posted : Bitfield<2, 1> { }; /* enable posted mode */
};
/**
* Control timer-functionality dependent features
*/
struct Tclr : Register<0x38, 32>
{
struct St : Bitfield<0, 1> { }; /* start/stop timer */
struct Ar : Bitfield<1, 1> { }; /* enable eutoreload */
struct Pre : Bitfield<5, 1> { }; /* enable prescaler */
/**
* Run-and-wrap configuration
*/
static access_t init_run_and_wrap()
{
return St::bits(0) |
Ar::bits(1) |
Pre::bits(0);
}
};
/**
* Set IRQ enables
*/
struct Irqenable_set : Register<0x2c, 32>
{
struct Ovf_en_flag : Bitfield<1, 1> { }; /* enable overflow IRQ */
};
/**
* IRQ status
*/
struct Irqstatus : Register<0x28, 32>
{
struct Ovf_it_flag : Bitfield<1, 1> { }; /* clear overflow IRQ */
};
/**
* Timer counter register
*/
struct Tcrr : Register<0x3c, 32>
{
/**
* maximum counter value
*/
static access_t max_value() { return ~0; }
};
/**
* Timer load value register
*/
struct Tldr : Register<0x40, 32> { };
/**
* Freeze timer counter
*/
void _freeze() { write<Tclr::St>(0); }
/**
* Unfreeze timer counter
*/
void _unfreeze() { write<Tclr::St>(1); }
/**
* Get remaining counting amount
*/
unsigned long _value() const { return max_value() - read<Tcrr>(); }
/**
* Apply counting amount
*/
void _value(unsigned long const v) { write<Tcrr>(max_value() - v); }
public:
/**
* Constructor
*
* \param base MMIO base
*/
Omap4_gp_timer_1(addr_t const base) : Mmio(base)
{
_freeze();
/* do a software reset */
write<Tiocp_cfg::Softreset>(1);
while (read<Tiocp_cfg::Softreset>()) ;
/* configure Idle mode */
write<Tiocp_cfg::Idlemode>(Tiocp_cfg::Idlemode::FORCE_IDLE);
/* enable wake-up interrupt events */
write<Twer>(Twer::init_timer());
/* select posted mode */
write<Tsicr::Posted>(0);
}
/**
* Count down 'value', raise IRQ output, wrap counter and continue
*/
void run_and_wrap(unsigned long value)
{
enum { MIN_VALUE = 1 };
/* stop timer */
_freeze();
clear_interrupt();
value = value ? value : MIN_VALUE;
/* configure for a run and wrap */
write<Tclr>(Tclr::init_run_and_wrap());
write<Irqenable_set::Ovf_en_flag>(1);
/* install value */
_value(value);
write<Tldr>(0);
/* start timer */
_unfreeze();
}
/**
* Clear interrupt output
*/
void clear_interrupt() {
write<Irqstatus::Ovf_it_flag>(1); }
/**
* Maximum timeout value
*/
unsigned long max_value() const { return Tcrr::max_value(); }
/**
* Translate timer tics to microseconds
*/
unsigned long tics_to_us(unsigned long const tics) const
{
float const us = tics * us_per_tic();
return (unsigned long)us;
}
/**
* Translate microseconds to timer tics
*/
unsigned long us_to_tics(unsigned long const us)
{
float const tics = us * tics_per_us();
return (unsigned long)tics;
}
/**
* Sample the timer counter and according wrapped status
*/
unsigned long value(bool & wrapped) const
{
Tcrr::access_t const v = _value();
wrapped = (bool)read<Irqstatus::Ovf_it_flag>();
return wrapped ? _value() : v;
}
};
}
/**
* Basic driver behind platform timer
*/
class Platform_timer_base : public Genode::Io_mem_connection,
public Genode::Omap4_gp_timer_1
{
/* FIXME these should be located in a omap4-defs file */
enum {
GP_TIMER_3_MMIO_BASE = 0x48034000,
GP_TIMER_3_MMIO_SIZE = 0x00001000,
};
public:
enum { IRQ = Genode::Board_base::GP_TIMER_3_IRQ };
/**
* Constructor
*/
Platform_timer_base() :
Io_mem_connection(GP_TIMER_3_MMIO_BASE, GP_TIMER_3_MMIO_SIZE),
Genode::Omap4_gp_timer_1((Genode::addr_t)Genode::env()->rm_session()->attach(dataspace()))
{ }
};
#endif /* _HW__OMAP4__PLATFORM_TIMER_BASE_H_ */

View File

@ -1,46 +0,0 @@
/*
* \brief Platform-timer base specific for base-hw and PBXA9
* \author Martin Stein
* \date 2012-05-03
*/
/*
* Copyright (C) 2012-2013 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 _OS__SRC__DRIVERS__TIMER__HW__PBXA9__PLATFORM_TIMER_BASE_H_
#define _OS__SRC__DRIVERS__TIMER__HW__PBXA9__PLATFORM_TIMER_BASE_H_
/* Genode includes */
#include <io_mem_session/connection.h>
#include <sp804_base.h>
#include <drivers/board_base.h>
/**
* Platform-timer base specific for base-hw and PBXA9
*/
class Platform_timer_base :
public Genode::Io_mem_connection,
public Genode::Sp804_base<Genode::Board_base::SP804_0_1_CLOCK>
{
public:
enum { IRQ = Genode::Board_base::SP804_0_1_IRQ };
/**
* Constructor
*/
Platform_timer_base() :
Io_mem_connection(Genode::Board_base::SP804_0_1_MMIO_BASE,
Genode::Board_base::SP804_0_1_MMIO_SIZE),
Sp804_base((Genode::addr_t)Genode::env()->rm_session()->
attach(dataspace()))
{ }
};
#endif /* _OS__SRC__DRIVERS__TIMER__HW__PBXA9__PLATFORM_TIMER_BASE_H_ */

View File

@ -1,32 +0,0 @@
/*
* \brief Dummy platform timer
* \author Sebastian Sumpf
* \date 2016-02-10
*/
/*
* Copyright (C) 2016 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 _HW__RISCV__PLATFORM_TIMER_BASE_H_
#define _HW__RISCV__PLATFORM_TIMER_BASE_H_
class Platform_timer_base
{
public:
enum { IRQ };
unsigned long tics_to_us(unsigned long const tics) const { return 0; }
unsigned long us_to_tics(unsigned long const us) const { return 0; }
unsigned long max_value() const { return 0; }
unsigned long value(bool & wrapped) const { return 0; }
void run_and_wrap(unsigned long value) { }
};
#endif /* _HW__RISCV__PLATFORM_TIMER_BASE_H_ */

View File

@ -1,49 +0,0 @@
/*
* \brief User-level timer driver for Raspberry Pi
* \author Norman Feske
* \date 2013-04-11
*/
/*
* Copyright (C) 2012-2013 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 _HW__RPI__PLATFORM_TIMER_BASE_H_
#define _HW__RPI__PLATFORM_TIMER_BASE_H_
/* Genode includes */
#include <os/attached_io_mem_dataspace.h>
#include <sp804_base.h>
#include <drivers/board_base.h>
/*
* On the BCM2835, the timer is driven by the APB clock (250 MHz). The prescale
* register (not present in the normal SP804) has a reset value of 126. Hence,
* the effective timer clock is 1,984 MHz.
*
* The timer device is on the same physical page as the IRQ controller. Hence,
* we open an IO_MEM session with a range smaller than page size as argument.
* The dataspace base address will correspond to 0x2000b000.
*/
enum { TIMER_MMIO_BASE = 0x2000b400,
TIMER_MMIO_SIZE = 0x100,
TIMER_CLOCK = 1984*1000 };
struct Platform_timer_base
:
Genode::Attached_io_mem_dataspace,
Genode::Sp804_base<TIMER_CLOCK>
{
enum { IRQ = Genode::Board_base::TIMER_IRQ };
Platform_timer_base() :
Attached_io_mem_dataspace(TIMER_MMIO_BASE, TIMER_MMIO_SIZE),
Sp804_base((Genode::addr_t)local_addr<void>())
{ }
};
#endif /* _HW__RPI__PLATFORM_TIMER_BASE_H_ */

View File

@ -1,158 +0,0 @@
/*
* \brief Platform-timer base specific for base-hw and zynq
* \author Johannes Schlatow
* \date 2015-03-05
*/
/*
* Copyright (C) 2015 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 _HW__ZYNQ__PLATFORM_TIMER_BASE_H_
#define _HW__ZYNQ__PLATFORM_TIMER_BASE_H_
/* Genode includes */
#include <io_mem_session/connection.h>
#include <util/mmio.h>
#include <drivers/board_base.h>
/**
* Platform-timer base specific for base-hw and zynq
*
* Uses the internal timer 0 of the TTC. For more details, see Xilinx ug585
* "Zynq 7000 Technical Reference Manual".
*/
class Platform_timer_base
:
public Genode::Io_mem_connection,
public Genode::Mmio
{
private:
enum
{
PRESCALER = 5,
TICS_PER_MS = (Genode::Board_base::CPU_1X_CLOCK / 1000)
>> PRESCALER
};
/**
* Clock control register
*/
struct Clock : Register<0x00, 8>
{
struct Prescale_en : Bitfield<0, 1> { };
struct Prescale : Bitfield<1, 4> { };
};
/**
* Counter control register
*/
struct Control : Register<0x0c, 8>
{
struct Disable : Bitfield<0, 1> { };
struct Mode : Bitfield<1, 1> { enum { INTERVAL = 1 }; };
struct Decrement : Bitfield<2, 1> { };
struct Wave_en : Bitfield<5, 1> { };
};
/**
* Counter value
*/
struct Value : Register<0x18, 16> { };
struct Interval : Register<0x24, 16> { };
struct Match1 : Register<0x30, 16> { };
struct Match2 : Register<0x3c, 16> { };
struct Match3 : Register<0x48, 16> { };
struct Irq : Register<0x54, 8> { };
struct Irqen : Register<0x60, 8> { };
void _disable()
{
write<Control::Disable>(0);
read<Irq>();
}
public:
enum { IRQ = Genode::Board_base::TTC0_IRQ_0 };
/**
* Constructor
*/
Platform_timer_base()
:
Io_mem_connection(Genode::Board_base::TTC0_MMIO_BASE,
Genode::Board_base::TTC0_MMIO_SIZE),
Mmio((Genode::addr_t)
Genode::env()->rm_session()->attach(dataspace()))
{
_disable();
/* configure prescaler */
Clock::access_t clock = read<Clock>();
Clock::Prescale::set(clock, PRESCALER - 1);
Clock::Prescale_en::set(clock, 1);
write<Clock>(clock);
/* enable all interrupts */
write<Irqen>(~0);
/* set match registers to 0 */
write<Match1>(0);
write<Match2>(0);
write<Match3>(0);
}
/**
* Count down 'value', raise IRQ output, wrap counter and continue
*/
void run_and_wrap(unsigned long const tics)
{
_disable();
/* configure timer for a one-shot */
Control::access_t control = 0;
Control::Mode::set(control, Control::Mode::INTERVAL);
Control::Decrement::set(control, 1);
Control::Wave_en::set(control, 1);
write<Control>(control);
/* load and enable timer */
write<Interval>(tics);
write<Control::Disable>(0);
}
/**
* Get timer value and corresponding wrapped status of timer
*/
unsigned long value(bool & wrapped) const
{
unsigned long v = read<Value>();
wrapped = (bool)read<Irq>();
return wrapped ? read<Value>() : v;
}
/**
* Translate native timer value to microseconds
*/
static unsigned long tics_to_us(unsigned long const tics) {
return tics * 1000 / TICS_PER_MS; }
/**
* Translate microseconds to a native timer value
*/
static unsigned long us_to_tics(unsigned long const us) {
return us * TICS_PER_MS / 1000; }
/**
* Return maximum number of tics
*/
unsigned long max_value() const { return (Interval::access_t)~0; }
};
#endif /* _HW__ZYNQ__PLATFORM_TIMER_BASE_H_ */