diff --git a/repos/os/src/drivers/timer/epit/epit.h b/repos/os/src/drivers/timer/epit/epit.h new file mode 100644 index 0000000000..26cdceb5b5 --- /dev/null +++ b/repos/os/src/drivers/timer/epit/epit.h @@ -0,0 +1,195 @@ +/* + * \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-2017 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 _EPIT_H_ +#define _EPIT_H_ + +/* Genode includes */ +#include <util/mmio.h> + +namespace Genode { class Epit_base; } + + +class Genode::Epit_base : public Mmio +{ + protected: + + enum { TICS_PER_MS = 66000 }; + + /** + * 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) + { + _start_one_shot(tics); + } + + /** + * Stop the timer from a one-shot run + * + * \return last native timer value of the one-shot run + */ + unsigned stop_one_shot(bool &wrap) + { + /* disable timer */ + write<Cr::En>(0); + return value(wrap); + } + + /** + * Translate microseconds to a native timer value + */ + unsigned us_to_tics(unsigned const us) const + { + return (1ULL * us * TICS_PER_MS) / 1000; + } + + /** + * Translate native timer value to microseconds + */ + unsigned tics_to_us(unsigned const tics) const + { + return (1ULL * tics * 1000) / TICS_PER_MS; + } + + /** + * Return current native timer value + */ + unsigned value(bool &wrapped) const + { + unsigned cnt = read<Cnt>(); + wrapped = (bool)read<Sr::Ocif>(); + return wrapped ? read<Cnt>() : cnt; + } + + /** + * Current maximum programmed timeout value + */ + unsigned current_max_value() const { return read<Lr>(); } +}; + +#endif /* _EPIT_H_ */ diff --git a/repos/os/src/drivers/timer/epit/time_source.cc b/repos/os/src/drivers/timer/epit/time_source.cc new file mode 100644 index 0000000000..f486eeecc2 --- /dev/null +++ b/repos/os/src/drivers/timer/epit/time_source.cc @@ -0,0 +1,66 @@ +/* + * \brief Time source that uses the Enhanced Periodic Interrupt Timer (Freescale) + * \author Norman Feske + * \author Martin Stein + * \author Stefan Kalkowski + * \author Alexander Boettcher + * \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. + */ + +/* local includes */ +#include <time_source.h> + +using namespace Genode; + +Microseconds Timer::Time_source::max_timeout() const { + return Microseconds(_epit.tics_to_us(~0U)); } + +void Timer::Time_source::schedule_timeout(Microseconds duration, + Timeout_handler &handler) +{ + /* make swift current time steady */ + Duration const time = curr_time(); + _curr_time_us = time.trunc_to_plain_us().value; + + /* + * Program max timeout in case of duration 0 to avoid lost of accuracy + * due to wraps when value is chosen too small. Send instead a signal + * manually at end of this method. + */ + unsigned const tics = _epit.us_to_tics(duration.value ? duration.value + : max_timeout().value); + + _handler = &handler; + + _timer_irq.ack_irq(); + + _epit.start_one_shot(tics); + + /* trigger for a timeout 0 immediately the signal */ + if (!duration.value) + Signal_transmitter(_signal_handler).submit(); +} + + +Duration Timer::Time_source::curr_time() +{ + /* read EPIT status */ + bool wrapped = false; + unsigned const max_value = _epit.current_max_value(); + unsigned const tic_value = _epit.value(wrapped); + unsigned passed_tics = 0; + + if (wrapped) + passed_tics += max_value; + + passed_tics += max_value - tic_value; + + return Duration(Microseconds(_curr_time_us + _epit.tics_to_us(passed_tics))); +} diff --git a/repos/os/src/drivers/timer/epit/time_source.h b/repos/os/src/drivers/timer/epit/time_source.h new file mode 100644 index 0000000000..2dd9c49bad --- /dev/null +++ b/repos/os/src/drivers/timer/epit/time_source.h @@ -0,0 +1,60 @@ +/* + * \brief Time source that uses the Enhanced Periodic Interrupt Timer (Freescale) + * \author Norman Feske + * \author Martin Stein + * \author Stefan Kalkowski + * \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 <base/attached_io_mem_dataspace.h> +#include <irq_session/connection.h> +#include <os/duration.h> + +/* local includes */ +#include <signalled_time_source.h> + +#include "epit.h" + +namespace Timer { + + using Microseconds = Genode::Microseconds; + using Duration = Genode::Duration; + class Time_source; +} + + +class Timer::Time_source : public Genode::Signalled_time_source +{ + private: + + Genode::Attached_io_mem_dataspace _io_mem; + Genode::Irq_connection _timer_irq; + Genode::Epit_base _epit; + unsigned long long mutable _curr_time_us { 0 }; + + public: + + Time_source(Genode::Env &env); + + + /************************* + ** Genode::Time_source ** + *************************/ + + Duration curr_time() override; + void schedule_timeout(Microseconds duration, Timeout_handler &handler) override; + Microseconds max_timeout() const override; +}; + +#endif /* _TIME_SOURCE_H_ */ diff --git a/repos/os/src/drivers/timer/epit/wand_quad/target.mk b/repos/os/src/drivers/timer/epit/wand_quad/target.mk new file mode 100644 index 0000000000..310f832ab7 --- /dev/null +++ b/repos/os/src/drivers/timer/epit/wand_quad/target.mk @@ -0,0 +1,9 @@ +TARGET = wand_quad_timer_drv +REQUIRES = wand_quad +INC_DIR += $(REP_DIR)/src/drivers/timer/epit +SRC_CC += time_source.cc +SRC_CC += timer.cc + +include $(REP_DIR)/src/drivers/timer/target.inc + +vpath time_source.cc $(REP_DIR)/src/drivers/timer/epit diff --git a/repos/os/src/drivers/timer/epit/wand_quad/timer.cc b/repos/os/src/drivers/timer/epit/wand_quad/timer.cc new file mode 100644 index 0000000000..bf645232bb --- /dev/null +++ b/repos/os/src/drivers/timer/epit/wand_quad/timer.cc @@ -0,0 +1,33 @@ +/* + * \brief Time source for Wandboard Quad i.MX6 + * \author Norman Feske + * \author Martin Stein + * \author Stefan Kalkowski + * \author Alexander Boettcher + * \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. + */ + +/* base include */ +#include <drivers/defs/wand_quad.h> + +/* local include */ +#include <time_source.h> + +using namespace Genode; + +Timer::Time_source::Time_source(Env &env) +: + Signalled_time_source(env), + _io_mem(env, Wand_quad::EPIT_2_MMIO_BASE, Wand_quad::EPIT_2_MMIO_SIZE), + _timer_irq(env, Wand_quad::EPIT_2_IRQ), + _epit(reinterpret_cast<addr_t>(_io_mem.local_addr<addr_t>())) +{ + _timer_irq.sigh(_signal_handler); +}