base-hw imx epit-timer: fix duration calculation

The timer used to read the counter first and then the IRQ status. This
could cause a non-wrapped counter value to be considered a wrapped
counter value, leading to bogus timeout durations.

This commit fixes the bug and documents the used timer mode in the
driver in order to make future debugging of the driver easier.

Ref #4959
This commit is contained in:
Martin Stein 2023-07-07 08:37:01 +02:00 committed by Norman Feske
parent 9d98e8ac01
commit 90ba794e8e
2 changed files with 16 additions and 8 deletions

View File

@ -36,6 +36,19 @@ Board::Timer::Timer(unsigned)
void Board::Timer::init()
{
/*
* Used timer mode:
*
* - Set CNT to 0xffffffff whenever CR.EN goes from 0 to 1 (CR.EN_MOD = 1).
* This happens only once: at construction time.
* - CNT is counting downwards with timer frequency.
* - When CNT reaches 0 it rolls over to 0xffffffff (CR.RLD = 0).
* - When writing LR, also set CNT to new LR value (CR.IOVW = 1). This
* happens whenever a timeout is programmed.
* - Trigger IRQ when CNT == CMPR (CR.OCI_EN = 1). CMPR is always set to
* 0xffffffff.
*/
reset();
Cr::access_t cr = read<Cr>();
@ -81,8 +94,8 @@ time_t Timer::_max_value() const {
time_t Timer::_duration() const
{
using Device = Board::Timer;
Device::Cnt::access_t last = (Device::Cnt::access_t) _last_timeout_duration;
Device::Cnt::access_t cnt = _device.read<Device::Cnt>();
return (_device.read<Device::Sr::Ocif>()) ? _max_value() - cnt + last
Device::Cnt::access_t const last = (Device::Cnt::access_t) _last_timeout_duration;
Device::Cnt::access_t const cnt = _device.read<Device::Cnt>();
return (_device.read<Device::Sr::Ocif>()) ? _max_value() - _device.read<Device::Cnt>() + last
: last - cnt;
}

View File

@ -41,11 +41,6 @@ struct Board::Timer : Genode::Mmio
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 };