mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
parent
32bc1b14d4
commit
801fe272ca
426
repos/base/src/timer/pit/main.cc
Normal file
426
repos/base/src/timer/pit/main.cc
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* \brief Timer driver for the PIT
|
||||
* \author Norman Feske
|
||||
* \author Alexander Boettcher
|
||||
* \date 2024-05-13
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <base/component.h>
|
||||
#include <base/heap.h>
|
||||
#include <base/session_object.h>
|
||||
#include <irq_session/connection.h>
|
||||
#include <io_port_session/connection.h>
|
||||
#include <root/component.h>
|
||||
#include <timer_session/timer_session.h>
|
||||
#include <trace/timestamp.h>
|
||||
|
||||
/* base-internal includes */
|
||||
#include <base/internal/alarm_registry.h>
|
||||
|
||||
namespace Timer {
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
struct Clock;
|
||||
struct Device;
|
||||
struct Alarm;
|
||||
struct Root;
|
||||
struct Session_component;
|
||||
struct Main;
|
||||
|
||||
using Alarms = Alarm_registry<Alarm, Clock>;
|
||||
}
|
||||
|
||||
|
||||
struct Timer::Clock
|
||||
{
|
||||
uint64_t us;
|
||||
|
||||
static constexpr uint64_t MASK = uint64_t(-1);
|
||||
|
||||
uint64_t value() const { return us; }
|
||||
|
||||
void print(Output &out) const { Genode::print(out, us/1000); }
|
||||
};
|
||||
|
||||
|
||||
class Timer::Device : Noncopyable
|
||||
{
|
||||
private:
|
||||
|
||||
enum {
|
||||
PIT_TICKS_PER_SECOND = 1193182,
|
||||
PIT_MAX_COUNT = 65535,
|
||||
PIT_MAX_USEC = (1000ull * 1000 * PIT_MAX_COUNT) /
|
||||
(PIT_TICKS_PER_SECOND),
|
||||
|
||||
PIT_DATA_PORT_0 = 0x40, /* data port for PIT channel 0,
|
||||
connected to the PIC */
|
||||
PIT_CMD_PORT = 0x43, /* PIT command port */
|
||||
IRQ_PIT = 0, /* timer interrupt at the PIC */
|
||||
|
||||
/*
|
||||
* Bit definitions for accessing the PIT command port
|
||||
*/
|
||||
PIT_CMD_SELECT_CHANNEL_0 = 0 << 6,
|
||||
PIT_CMD_ACCESS_LO = 1 << 4,
|
||||
PIT_CMD_ACCESS_LO_HI = 3 << 4,
|
||||
PIT_CMD_MODE_IRQ = 0 << 1,
|
||||
PIT_CMD_MODE_RATE = 2 << 1,
|
||||
|
||||
PIT_CMD_READ_BACK = 3 << 6,
|
||||
PIT_CMD_RB_COUNT = 0 << 5,
|
||||
PIT_CMD_RB_STATUS = 0 << 4,
|
||||
PIT_CMD_RB_CHANNEL_0 = 1 << 1,
|
||||
|
||||
/*
|
||||
* Bit definitions of the PIT status byte
|
||||
*/
|
||||
PIT_STAT_INT_LINE = 1 << 7,
|
||||
};
|
||||
|
||||
/* PIT counter */
|
||||
struct Counter { uint16_t value; };
|
||||
|
||||
public:
|
||||
|
||||
struct Wakeup_dispatcher : Interface
|
||||
{
|
||||
virtual void dispatch_device_wakeup() = 0;
|
||||
};
|
||||
|
||||
struct Deadline : Clock { };
|
||||
|
||||
static constexpr Deadline infinite_deadline { uint64_t(-1) };
|
||||
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
|
||||
Io_port_connection _io_port { _env, PIT_DATA_PORT_0,
|
||||
PIT_CMD_PORT - PIT_DATA_PORT_0 + 1 };
|
||||
|
||||
Irq_connection _timer_irq { _env, unsigned(IRQ_PIT) };
|
||||
|
||||
uint64_t _max_timeout_us { PIT_MAX_USEC };
|
||||
|
||||
Wakeup_dispatcher &_dispatcher;
|
||||
|
||||
Signal_handler<Device> _handler { _env.ep(), *this, &Device::_handle_timeout };
|
||||
|
||||
uint64_t _curr_time_us { };
|
||||
Counter _last_read { };
|
||||
bool _wrap_handled { };
|
||||
|
||||
uint64_t _convert_counter_to_us(uint64_t counter)
|
||||
{
|
||||
/* round up to 1us in case of rest */
|
||||
auto const mod = (counter * 1000 * 1000) % PIT_TICKS_PER_SECOND;
|
||||
return (counter * 1000 * 1000 / PIT_TICKS_PER_SECOND)
|
||||
+ (mod ? 1 : 0);
|
||||
}
|
||||
|
||||
Counter _convert_relative_us_to_counter(uint64_t rel_us)
|
||||
{
|
||||
return { .value = uint16_t(min(rel_us * PIT_TICKS_PER_SECOND / 1000 / 1000,
|
||||
uint64_t(PIT_MAX_COUNT))) };
|
||||
}
|
||||
|
||||
void _handle_timeout()
|
||||
{
|
||||
_dispatcher.dispatch_device_wakeup();
|
||||
_timer_irq.ack_irq();
|
||||
}
|
||||
|
||||
void _set_counter(Counter const &cnt)
|
||||
{
|
||||
/* wrap status gets reset by re-programming counter */
|
||||
_wrap_handled = false;
|
||||
|
||||
_io_port.outb(PIT_DATA_PORT_0, uint8_t( cnt.value & 0xff));
|
||||
_io_port.outb(PIT_DATA_PORT_0, uint8_t((cnt.value >> 8) & 0xff));
|
||||
}
|
||||
|
||||
void _with_counter(auto const &fn)
|
||||
{
|
||||
/* read-back count of counter 0 */
|
||||
_io_port.outb(PIT_CMD_PORT, PIT_CMD_READ_BACK |
|
||||
PIT_CMD_RB_COUNT |
|
||||
PIT_CMD_RB_STATUS |
|
||||
PIT_CMD_RB_CHANNEL_0);
|
||||
|
||||
/* read status byte from latch register */
|
||||
uint8_t status = _io_port.inb(PIT_DATA_PORT_0);
|
||||
|
||||
/* read low and high bytes from latch register */
|
||||
uint16_t lo = _io_port.inb(PIT_DATA_PORT_0);
|
||||
uint16_t hi = _io_port.inb(PIT_DATA_PORT_0);
|
||||
|
||||
bool const wrapped = !!(status & PIT_STAT_INT_LINE);
|
||||
|
||||
fn(Counter(uint16_t((hi << 8) | lo)), wrapped && !_wrap_handled);
|
||||
|
||||
/* only handle wrap one time until next _set_counter */
|
||||
if (wrapped)
|
||||
_wrap_handled = true;
|
||||
}
|
||||
|
||||
void _advance_current_time()
|
||||
{
|
||||
_with_counter([&](Counter const &pit, bool wrapped) {
|
||||
|
||||
auto diff = (!wrapped && (_last_read.value >= pit.value))
|
||||
? _last_read.value - pit.value
|
||||
: PIT_MAX_COUNT - pit.value + _last_read.value;
|
||||
|
||||
_curr_time_us += _convert_counter_to_us(diff);
|
||||
|
||||
_last_read = pit;
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Device(Env &env, Wakeup_dispatcher &dispatcher)
|
||||
: _env(env), _dispatcher(dispatcher)
|
||||
{
|
||||
/* operate PIT in one-shot mode */
|
||||
_io_port.outb(PIT_CMD_PORT, PIT_CMD_SELECT_CHANNEL_0 |
|
||||
PIT_CMD_ACCESS_LO_HI | PIT_CMD_MODE_IRQ);
|
||||
|
||||
_timer_irq.sigh(_handler);
|
||||
|
||||
_handle_timeout();
|
||||
}
|
||||
|
||||
Clock now()
|
||||
{
|
||||
_advance_current_time();
|
||||
|
||||
return Clock { .us = _curr_time_us };
|
||||
}
|
||||
|
||||
void update_deadline(Deadline const deadline)
|
||||
{
|
||||
uint64_t const now_us = now().us;
|
||||
uint64_t const rel_us = (deadline.us > now_us)
|
||||
? min(_max_timeout_us, deadline.us - now_us)
|
||||
: 1;
|
||||
|
||||
auto const pit_cnt = _convert_relative_us_to_counter(rel_us);
|
||||
|
||||
_last_read = pit_cnt;
|
||||
|
||||
_set_counter(pit_cnt);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Timer::Alarm : Alarms::Element
|
||||
{
|
||||
Session_component &session;
|
||||
|
||||
Alarm(Alarms &alarms, Session_component &session, Clock time)
|
||||
:
|
||||
Alarms::Element(alarms, *this, time), session(session)
|
||||
{ }
|
||||
|
||||
void print(Output &out) const;
|
||||
};
|
||||
|
||||
|
||||
static Timer::Device::Deadline next_deadline(Timer::Alarms &alarms)
|
||||
{
|
||||
using namespace Timer;
|
||||
|
||||
return alarms.soonest(Clock { 0 }).convert<Device::Deadline>(
|
||||
[&] (Clock soonest) -> Device::Deadline {
|
||||
|
||||
/* scan alarms for a cluster nearby the soonest */
|
||||
uint64_t const MAX_DELAY_US = 250;
|
||||
Device::Deadline result { soonest.us };
|
||||
alarms.for_each_in_range(soonest, Clock { soonest.us + MAX_DELAY_US },
|
||||
[&] (Alarm const &alarm) {
|
||||
result.us = max(result.us, alarm.time.us); });
|
||||
|
||||
return result;
|
||||
},
|
||||
[&] (Alarms::None) { return Device::infinite_deadline; });
|
||||
}
|
||||
|
||||
|
||||
struct Timer::Session_component : Session_object<Timer::Session, Session_component>
|
||||
{
|
||||
Alarms &_alarms;
|
||||
Device &_device;
|
||||
|
||||
Signal_context_capability _sigh { };
|
||||
|
||||
Clock const _creation_time = _device.now();
|
||||
|
||||
uint64_t _local_now_us() const { return _device.now().us - _creation_time.us; }
|
||||
|
||||
struct Period { uint64_t us; };
|
||||
|
||||
Constructible<Period> _period { };
|
||||
Constructible<Alarm> _alarm { };
|
||||
|
||||
Session_component(Env &env,
|
||||
Resources const &resources,
|
||||
Label const &label,
|
||||
Diag const &diag,
|
||||
Alarms &alarms,
|
||||
Device &device)
|
||||
:
|
||||
Session_object(env.ep(), resources, label, diag),
|
||||
_alarms(alarms), _device(device)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* Called by Device::Wakeup_dispatcher
|
||||
*/
|
||||
void handle_wakeup()
|
||||
{
|
||||
if (_sigh.valid())
|
||||
Signal_transmitter(_sigh).submit();
|
||||
|
||||
if (_period.constructed()) {
|
||||
Clock const next = _alarm.constructed()
|
||||
? Clock { _alarm->time.us + _period->us }
|
||||
: Clock { _device.now().us + _period->us };
|
||||
|
||||
_alarm.construct(_alarms, *this, next);
|
||||
|
||||
} else /* response of 'trigger_once' */ {
|
||||
_alarm.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
/******************************
|
||||
** Timer::Session interface **
|
||||
******************************/
|
||||
|
||||
void trigger_once(uint64_t rel_us) override
|
||||
{
|
||||
_period.destruct();
|
||||
_alarm.destruct();
|
||||
|
||||
Clock const now = _device.now();
|
||||
|
||||
rel_us = max(rel_us, 250u);
|
||||
_alarm.construct(_alarms, *this, Clock { now.us + rel_us });
|
||||
|
||||
_device.update_deadline(next_deadline(_alarms));
|
||||
}
|
||||
|
||||
void trigger_periodic(uint64_t period_us) override
|
||||
{
|
||||
_period.destruct();
|
||||
_alarm.destruct();
|
||||
|
||||
if (period_us) {
|
||||
period_us = max(period_us, 1000u);
|
||||
_period.construct(period_us);
|
||||
handle_wakeup();
|
||||
}
|
||||
|
||||
_device.update_deadline(next_deadline(_alarms));
|
||||
}
|
||||
|
||||
void sigh(Signal_context_capability sigh) override { _sigh = sigh; }
|
||||
|
||||
uint64_t elapsed_ms() const override { return _local_now_us()/1000; }
|
||||
uint64_t elapsed_us() const override { return _local_now_us(); }
|
||||
|
||||
void msleep(uint64_t) override { }
|
||||
void usleep(uint64_t) override { }
|
||||
};
|
||||
|
||||
|
||||
struct Timer::Root : public Root_component<Session_component>
|
||||
{
|
||||
private:
|
||||
|
||||
Env &_env;
|
||||
Alarms &_alarms;
|
||||
Device &_device;
|
||||
|
||||
protected:
|
||||
|
||||
Session_component *_create_session(const char *args) override
|
||||
{
|
||||
return new (md_alloc())
|
||||
Session_component(_env,
|
||||
session_resources_from_args(args),
|
||||
session_label_from_args(args),
|
||||
session_diag_from_args(args),
|
||||
_alarms, _device);
|
||||
}
|
||||
|
||||
void _upgrade_session(Session_component *s, const char *args) override
|
||||
{
|
||||
s->upgrade(ram_quota_from_args(args));
|
||||
s->upgrade(cap_quota_from_args(args));
|
||||
}
|
||||
|
||||
void _destroy_session(Session_component *session) override
|
||||
{
|
||||
Genode::destroy(md_alloc(), session);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Root(Env &env, Allocator &md_alloc, Alarms &alarms, Device &device)
|
||||
:
|
||||
Root_component<Session_component>(&env.ep().rpc_ep(), &md_alloc),
|
||||
_env(env), _alarms(alarms), _device(device)
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
void Timer::Alarm::print(Output &out) const { Genode::print(out, session.label()); }
|
||||
|
||||
|
||||
struct Timer::Main : Device::Wakeup_dispatcher
|
||||
{
|
||||
Env &_env;
|
||||
|
||||
Device _device { _env, *this };
|
||||
|
||||
Alarms _alarms { };
|
||||
|
||||
Sliced_heap _sliced_heap { _env.ram(), _env.rm() };
|
||||
|
||||
Root _root { _env, _sliced_heap, _alarms, _device };
|
||||
|
||||
/**
|
||||
* Device::Wakeup_dispatcher
|
||||
*/
|
||||
void dispatch_device_wakeup() override
|
||||
{
|
||||
Clock const now = _device.now();
|
||||
|
||||
/* handle and remove pending alarms */
|
||||
while (_alarms.with_any_in_range({ 0 }, now, [&] (Alarm &alarm) {
|
||||
alarm.session.handle_wakeup(); }));
|
||||
|
||||
/* schedule next wakeup */
|
||||
_device.update_deadline(next_deadline(_alarms));
|
||||
}
|
||||
|
||||
Main(Genode::Env &env) : _env(env)
|
||||
{
|
||||
_env.parent().announce(_env.ep().manage(_root));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void Component::construct(Genode::Env &env) { static Timer::Main inst(env); }
|
@ -1,9 +1,8 @@
|
||||
TARGET = pit_timer_drv
|
||||
REQUIRES = x86
|
||||
GEN_DIR := $(dir $(call select_from_repositories,src/timer/main.cc))
|
||||
INC_DIR += $(GEN_DIR)/pit
|
||||
SRC_CC += time_source.cc
|
||||
SRC_CC += main.cc
|
||||
LIBS += base
|
||||
|
||||
include $(GEN_DIR)/target.inc
|
||||
REP_INC_DIR += src/include
|
||||
|
||||
vpath time_source.cc $(GEN_DIR)/pit
|
||||
vpath main.cc $(dir $(call select_from_repositories,src/timer/pit/main.cc))
|
||||
|
@ -1,210 +0,0 @@
|
||||
/*
|
||||
* \brief Time source that uses the Programmable Interval Timer (PIT)
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-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.
|
||||
*/
|
||||
|
||||
/* Genode includes */
|
||||
#include <drivers/timer/util.h>
|
||||
|
||||
/* local includes */
|
||||
#include <time_source.h>
|
||||
|
||||
using namespace Genode;
|
||||
|
||||
|
||||
void Timer::Time_source::_set_counter(uint16_t value)
|
||||
{
|
||||
_handled_wrap = false;
|
||||
_io_port.outb(PIT_DATA_PORT_0, (uint8_t)(value & 0xff));
|
||||
_io_port.outb(PIT_DATA_PORT_0, (uint8_t)((value >> 8) & 0xff));
|
||||
}
|
||||
|
||||
|
||||
uint16_t Timer::Time_source::_read_counter(bool *wrapped)
|
||||
{
|
||||
/* read-back count and status of counter 0 */
|
||||
_io_port.outb(PIT_CMD_PORT, PIT_CMD_READ_BACK |
|
||||
PIT_CMD_RB_COUNT |
|
||||
PIT_CMD_RB_STATUS |
|
||||
PIT_CMD_RB_CHANNEL_0);
|
||||
|
||||
/* read status byte from latch register */
|
||||
uint8_t status = _io_port.inb(PIT_DATA_PORT_0);
|
||||
|
||||
/* read low and high bytes from latch register */
|
||||
uint16_t lo = _io_port.inb(PIT_DATA_PORT_0);
|
||||
uint16_t hi = _io_port.inb(PIT_DATA_PORT_0);
|
||||
|
||||
*wrapped = status & PIT_STAT_INT_LINE ? true : false;
|
||||
return (uint16_t)((hi << 8) | lo);
|
||||
}
|
||||
|
||||
|
||||
void Timer::Time_source::set_timeout(Microseconds duration,
|
||||
Timeout_handler &handler)
|
||||
{
|
||||
_handler = &handler;
|
||||
uint64_t duration_us = duration.value;
|
||||
|
||||
/* timeout '0' is trigger to cancel the current pending, if required */
|
||||
if (!duration.value) {
|
||||
duration_us = max_timeout().value;
|
||||
Signal_transmitter(_signal_handler).submit();
|
||||
} else {
|
||||
/* limit timer-interrupt rate */
|
||||
enum { MAX_TIMER_IRQS_PER_SECOND = 4*1000 };
|
||||
if (duration_us < (uint64_t)1000 * 1000 / MAX_TIMER_IRQS_PER_SECOND)
|
||||
duration_us = (uint64_t)1000 * 1000 / MAX_TIMER_IRQS_PER_SECOND;
|
||||
|
||||
if (duration_us > max_timeout().value)
|
||||
duration_us = max_timeout().value;
|
||||
}
|
||||
|
||||
_counter_init_value = (uint16_t)((PIT_TICKS_PER_MSEC * duration_us) / 1000);
|
||||
_set_counter(_counter_init_value);
|
||||
|
||||
if (duration.value)
|
||||
_timer_irq.ack_irq();
|
||||
}
|
||||
|
||||
|
||||
uint32_t Timer::Time_source::_ticks_since_update_no_wrap(uint16_t curr_counter)
|
||||
{
|
||||
/*
|
||||
* The counter did not wrap since the last update of _counter_init_value.
|
||||
* This means that _counter_init_value is equal to or greater than
|
||||
* curr_counter and that the time that passed is simply the difference
|
||||
* between the two.
|
||||
*/
|
||||
return _counter_init_value - curr_counter;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Timer::Time_source::_ticks_since_update_one_wrap(uint16_t curr_counter)
|
||||
{
|
||||
/*
|
||||
* The counter wrapped since the last update of _counter_init_value.
|
||||
* This means that the time that passed is the whole _counter_init_value
|
||||
* plus the time that passed since the counter wrapped.
|
||||
*/
|
||||
return _counter_init_value + PIT_MAX_COUNT - curr_counter;
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Time_source::curr_time()
|
||||
{
|
||||
/* read out and update curr time solely if running in context of irq */
|
||||
if (_irq)
|
||||
_curr_time();
|
||||
|
||||
return Duration(Microseconds(_curr_time_us));
|
||||
}
|
||||
|
||||
|
||||
Duration Timer::Time_source::_curr_time()
|
||||
{
|
||||
/* read PIT counter and wrapped status */
|
||||
uint32_t ticks;
|
||||
bool wrapped;
|
||||
uint16_t const curr_counter = _read_counter(&wrapped);
|
||||
|
||||
if (!wrapped) {
|
||||
|
||||
/*
|
||||
* The counter did not wrap since the last call to scheduled_timeout
|
||||
* which means that it did not wrap since the last update of
|
||||
* _counter_init_time.
|
||||
*/
|
||||
ticks = _ticks_since_update_no_wrap(curr_counter);
|
||||
}
|
||||
else if (wrapped && !_handled_wrap) {
|
||||
|
||||
/*
|
||||
* The counter wrapped at least once since the last call to
|
||||
* schedule_timeout (wrapped) and curr_time (!_handled_wrap) which
|
||||
* means that it definitely did wrap since the last update of
|
||||
* _counter_init_time. We cannot determine whether it wrapped only
|
||||
* once but we have to assume it. Even if it wrapped multiple times,
|
||||
* the error that results from the assumption that it did not is pretty
|
||||
* innocuous ((nr_of_wraps - 1) * 53 ms at a max).
|
||||
*/
|
||||
ticks = _ticks_since_update_one_wrap(curr_counter);
|
||||
_handled_wrap = true;
|
||||
}
|
||||
else { /* wrapped && _handled_wrap */
|
||||
|
||||
/*
|
||||
* The counter wrapped at least once since the last call to
|
||||
* schedule_timeout (wrapped) but may not have wrapped since the last
|
||||
* call to curr_time (_handled_wrap).
|
||||
*/
|
||||
|
||||
if (_counter_init_value >= curr_counter) {
|
||||
|
||||
/*
|
||||
* We cannot determine whether the counter wrapped since the last
|
||||
* call to curr_time but we have to assume that it did not. Even if
|
||||
* it wrapped, the error that results from the assumption that it
|
||||
* did not is pretty innocuous as long as _counter_init_value is
|
||||
* not greater than curr_counter (nr_of_wraps * 53 ms at a max).
|
||||
*/
|
||||
ticks = _ticks_since_update_no_wrap(curr_counter);
|
||||
|
||||
} else {
|
||||
|
||||
/*
|
||||
* The counter definitely wrapped multiple times since the last
|
||||
* call to schedule_timeout and at least once since the last call
|
||||
* to curr_time. It is the only explanation for the fact that
|
||||
* curr_counter became greater than _counter_init_value again
|
||||
* after _counter_init_value was updated with a wrapped counter
|
||||
* by curr_time (_handled_wrap). This means two things:
|
||||
*
|
||||
* First, the counter wrapped at least once since the last update
|
||||
* of _counter_init_value. We cannot determine whether it wrapped
|
||||
* only once but we have to assume it. Even if it wrapped multiple
|
||||
* times, the error that results from the assumption that it
|
||||
* did not is pretty innocuous ((nr_of_wraps - 1) * 53 ms at a max).
|
||||
*
|
||||
* Second, we have to warn the user as it is a sure indication of
|
||||
* insufficient activation latency if the counter wraps multiple
|
||||
* times between two schedule_timeout calls.
|
||||
*/
|
||||
warning("PIT wrapped multiple times, timer-driver latency too big");
|
||||
ticks = _ticks_since_update_one_wrap(curr_counter);
|
||||
}
|
||||
}
|
||||
|
||||
/* use current counter as the reference for the next update */
|
||||
_counter_init_value = curr_counter;
|
||||
|
||||
/* translate counter to microseconds and update time value */
|
||||
static_assert(PIT_TICKS_PER_MSEC >= (unsigned)TIMER_MIN_TICKS_PER_MS,
|
||||
"Bad TICS_PER_MS value");
|
||||
_curr_time_us += timer_ticks_to_us(ticks, PIT_TICKS_PER_MSEC);
|
||||
|
||||
return Duration(Microseconds(_curr_time_us));
|
||||
}
|
||||
|
||||
|
||||
Timer::Time_source::Time_source(Env &env)
|
||||
:
|
||||
Signalled_time_source(env),
|
||||
_io_port(env, PIT_DATA_PORT_0, PIT_CMD_PORT - PIT_DATA_PORT_0 + 1),
|
||||
_timer_irq(env, unsigned(IRQ_PIT))
|
||||
{
|
||||
/* operate PIT in one-shot mode */
|
||||
_io_port.outb(PIT_CMD_PORT, PIT_CMD_SELECT_CHANNEL_0 |
|
||||
PIT_CMD_ACCESS_LO_HI | PIT_CMD_MODE_IRQ);
|
||||
|
||||
_timer_irq.sigh(_signal_handler);
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* \brief Time source that uses the Programmable Interval Timer (PIT)
|
||||
* \author Norman Feske
|
||||
* \author Martin Stein
|
||||
* \date 2009-06-16
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009-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 _TIME_SOURCE_H_
|
||||
#define _TIME_SOURCE_H_
|
||||
|
||||
/* Genode includes */
|
||||
#include <io_port_session/connection.h>
|
||||
#include <irq_session/connection.h>
|
||||
#include <base/duration.h>
|
||||
|
||||
/* local includes */
|
||||
#include <signalled_time_source.h>
|
||||
|
||||
namespace Timer {
|
||||
|
||||
using Genode::uint64_t;
|
||||
using Microseconds = Genode::Microseconds;
|
||||
using Duration = Genode::Duration;
|
||||
class Time_source;
|
||||
}
|
||||
|
||||
|
||||
class Timer::Time_source : public Genode::Signalled_time_source
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
enum {
|
||||
PIT_TICKS_PER_SECOND = 1193182,
|
||||
PIT_TICKS_PER_MSEC = PIT_TICKS_PER_SECOND/1000,
|
||||
PIT_MAX_COUNT = 65535,
|
||||
PIT_DATA_PORT_0 = 0x40, /* data port for PIT channel 0,
|
||||
connected to the PIC */
|
||||
PIT_CMD_PORT = 0x43, /* PIT command port */
|
||||
|
||||
PIT_MAX_USEC = (PIT_MAX_COUNT*1000)/(PIT_TICKS_PER_MSEC),
|
||||
|
||||
IRQ_PIT = 0, /* timer interrupt at the PIC */
|
||||
|
||||
/*
|
||||
* Bit definitions for accessing the PIT command port
|
||||
*/
|
||||
PIT_CMD_SELECT_CHANNEL_0 = 0 << 6,
|
||||
PIT_CMD_ACCESS_LO = 1 << 4,
|
||||
PIT_CMD_ACCESS_LO_HI = 3 << 4,
|
||||
PIT_CMD_MODE_IRQ = 0 << 1,
|
||||
PIT_CMD_MODE_RATE = 2 << 1,
|
||||
|
||||
PIT_CMD_READ_BACK = 3 << 6,
|
||||
PIT_CMD_RB_COUNT = 0 << 5,
|
||||
PIT_CMD_RB_STATUS = 0 << 4,
|
||||
PIT_CMD_RB_CHANNEL_0 = 1 << 1,
|
||||
|
||||
/*
|
||||
* Bit definitions of the PIT status byte
|
||||
*/
|
||||
PIT_STAT_INT_LINE = 1 << 7,
|
||||
};
|
||||
|
||||
Genode::Io_port_connection _io_port;
|
||||
Genode::Irq_connection _timer_irq;
|
||||
uint64_t mutable _curr_time_us = 0;
|
||||
Genode::uint16_t mutable _counter_init_value = 0;
|
||||
bool mutable _handled_wrap = false;
|
||||
|
||||
void _set_counter(Genode::uint16_t value);
|
||||
|
||||
Genode::uint16_t _read_counter(bool *wrapped);
|
||||
|
||||
Genode::uint32_t _ticks_since_update_one_wrap(Genode::uint16_t curr_counter);
|
||||
|
||||
Genode::uint32_t _ticks_since_update_no_wrap(Genode::uint16_t curr_counter);
|
||||
|
||||
Duration _curr_time();
|
||||
|
||||
public:
|
||||
|
||||
Time_source(Genode::Env &env);
|
||||
|
||||
|
||||
/*************************
|
||||
** Genode::Time_source **
|
||||
*************************/
|
||||
|
||||
Duration curr_time() override;
|
||||
void set_timeout(Microseconds duration, Genode::Timeout_handler &handler) override;
|
||||
Microseconds max_timeout() const override {
|
||||
return Microseconds(PIT_MAX_USEC); }
|
||||
};
|
||||
|
||||
#endif /* _TIME_SOURCE_H_ */
|
Loading…
Reference in New Issue
Block a user