2021-10-14 00:28:05 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2021, NXP
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT nxp_gpt_hw_timer
|
|
|
|
|
2023-08-28 13:15:43 +02:00
|
|
|
#include <zephyr/init.h>
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/drivers/timer/system_timer.h>
|
2021-10-14 00:28:05 +02:00
|
|
|
#include <fsl_gpt.h>
|
2022-05-06 10:25:46 +02:00
|
|
|
#include <zephyr/sys_clock.h>
|
|
|
|
#include <zephyr/spinlock.h>
|
|
|
|
#include <zephyr/sys/time_units.h>
|
2022-10-04 15:33:53 +02:00
|
|
|
#include <zephyr/irq.h>
|
2021-10-14 00:28:05 +02:00
|
|
|
|
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/*
|
|
|
|
* By limiting counter to 30 bits, we ensure that
|
|
|
|
* timeout calculations will never overflow in sys_clock_set_timeout
|
|
|
|
*/
|
|
|
|
#define COUNTER_MAX 0x3fffffff
|
2021-10-14 00:28:05 +02:00
|
|
|
|
|
|
|
#define CYC_PER_TICK (sys_clock_hw_cycles_per_sec() \
|
|
|
|
/ CONFIG_SYS_CLOCK_TICKS_PER_SEC)
|
|
|
|
|
|
|
|
#define MAX_TICKS ((COUNTER_MAX / CYC_PER_TICK) - 1)
|
|
|
|
#define MAX_CYCLES (MAX_TICKS * CYC_PER_TICK)
|
|
|
|
|
|
|
|
/* Use the first device defined with GPT HW timer compatible string */
|
|
|
|
#define GPT_INST DT_INST(0, DT_DRV_COMPAT)
|
|
|
|
|
|
|
|
/*
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
* Stores the current number of cycles the system has had announced to it,
|
|
|
|
* since the last rollover of the free running counter.
|
2021-10-14 00:28:05 +02:00
|
|
|
*/
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
static uint32_t announced_cycles;
|
2021-10-14 00:28:05 +02:00
|
|
|
|
|
|
|
/*
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
* Stores the number of cycles that have elapsed due to counter rollover.
|
|
|
|
* this value is updated in the GPT isr, and is used to keep the value
|
|
|
|
* returned by sys_clock_cycle_get_32 accurate after a timer rollover.
|
2021-10-14 00:28:05 +02:00
|
|
|
*/
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
static uint32_t rollover_cycles;
|
2021-10-14 00:28:05 +02:00
|
|
|
|
|
|
|
/* GPT timer base address */
|
|
|
|
static GPT_Type *base;
|
|
|
|
|
|
|
|
/* Lock on shared variables */
|
|
|
|
static struct k_spinlock lock;
|
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* Helper function to set GPT compare value, so we don't set a compare point in past */
|
|
|
|
static void gpt_set_safe(uint32_t next)
|
2021-10-14 00:28:05 +02:00
|
|
|
{
|
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
uint32_t now;
|
|
|
|
|
|
|
|
next = MIN(MAX_CYCLES, next);
|
|
|
|
GPT_SetOutputCompareValue(base, kGPT_OutputCompare_Channel2, next - 1);
|
|
|
|
now = GPT_GetCurrentTimerCount(base);
|
|
|
|
|
|
|
|
/* GPT fires interrupt at next counter cycle after a compare point is
|
|
|
|
* hit, so we should bump the compare point if 1 cycle or less exists
|
|
|
|
* between now and compare point.
|
2021-10-14 00:28:05 +02:00
|
|
|
*
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
* We will exit this loop if next==MAX_CYCLES, as we already
|
|
|
|
* have a rollover interrupt set up for that point, so we
|
|
|
|
* no longer need to keep bumping the compare point.
|
2021-10-14 00:28:05 +02:00
|
|
|
*/
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
if (unlikely(((int32_t)(next - now)) <= 1)) {
|
|
|
|
uint32_t bump = 1;
|
|
|
|
|
|
|
|
do {
|
|
|
|
next = now + bump;
|
|
|
|
bump *= 2;
|
|
|
|
next = MIN(MAX_CYCLES, next);
|
|
|
|
GPT_SetOutputCompareValue(base,
|
|
|
|
kGPT_OutputCompare_Channel2, next - 1);
|
|
|
|
now = GPT_GetCurrentTimerCount(base);
|
|
|
|
} while ((((int32_t)(next - now)) <= 1) && (next < MAX_CYCLES));
|
2021-10-14 00:28:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* Interrupt fires every time GPT reaches the current capture value */
|
2022-01-27 04:41:57 +01:00
|
|
|
void mcux_imx_gpt_isr(const void *arg)
|
2021-10-14 00:28:05 +02:00
|
|
|
{
|
2022-01-27 04:41:57 +01:00
|
|
|
ARG_UNUSED(arg);
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
k_spinlock_key_t key;
|
|
|
|
uint32_t tick_delta = 0, now, status;
|
2022-01-27 04:41:57 +01:00
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
key = k_spin_lock(&lock);
|
|
|
|
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
|
|
/* Get current timer count */
|
|
|
|
now = GPT_GetCurrentTimerCount(base);
|
|
|
|
status = GPT_GetStatusFlags(base,
|
|
|
|
kGPT_OutputCompare2Flag | kGPT_OutputCompare1Flag);
|
|
|
|
/* Clear GPT capture interrupts */
|
|
|
|
GPT_ClearStatusFlags(base, status);
|
|
|
|
if (status & kGPT_OutputCompare1Flag) {
|
|
|
|
/* Counter has just rolled over. We should
|
|
|
|
* reset the announced cycles counter, and record the
|
|
|
|
* cycles that remained before rollover.
|
|
|
|
*
|
|
|
|
* Since rollover occurs on a tick boundary, we don't
|
|
|
|
* need to worry about losing time here due to rounding.
|
|
|
|
*/
|
|
|
|
tick_delta += (MAX_CYCLES - announced_cycles) / CYC_PER_TICK;
|
|
|
|
announced_cycles = 0U;
|
|
|
|
/* Update count of rolled over cycles */
|
|
|
|
rollover_cycles += MAX_CYCLES;
|
|
|
|
}
|
|
|
|
if (status & kGPT_OutputCompare2Flag) {
|
|
|
|
/* Normal counter interrupt. Get delta since last announcement */
|
|
|
|
tick_delta += (now - announced_cycles) / CYC_PER_TICK;
|
|
|
|
announced_cycles += (((now - announced_cycles) / CYC_PER_TICK) *
|
|
|
|
CYC_PER_TICK);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
GPT_ClearStatusFlags(base, kGPT_OutputCompare1Flag);
|
|
|
|
/* Update count of rolled over cycles */
|
|
|
|
rollover_cycles += CYC_PER_TICK;
|
|
|
|
}
|
2021-10-14 00:28:05 +02:00
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* Announce progress to the kernel */
|
|
|
|
k_spin_unlock(&lock, key);
|
|
|
|
sys_clock_announce(IS_ENABLED(CONFIG_TICKLESS_KERNEL) ? tick_delta : 1);
|
2021-10-14 00:28:05 +02:00
|
|
|
}
|
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/*
|
|
|
|
* Next needed call to sys_clock_announce will not be until the specified number
|
|
|
|
* of ticks from the current time have elapsed.
|
2021-10-14 00:28:05 +02:00
|
|
|
*/
|
|
|
|
void sys_clock_set_timeout(int32_t ticks, bool idle)
|
|
|
|
{
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
|
|
/* Not supported on tickful kernels */
|
2021-10-14 00:28:05 +02:00
|
|
|
return;
|
|
|
|
}
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
k_spinlock_key_t key;
|
|
|
|
uint32_t next, adj, now;
|
|
|
|
|
2021-10-14 00:28:05 +02:00
|
|
|
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : ticks;
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* Clamp ticks. We subtract one since we round up to next tick */
|
2022-02-10 21:22:51 +01:00
|
|
|
ticks = CLAMP((ticks - 1), 0, (int32_t)MAX_TICKS);
|
2021-10-14 00:28:05 +02:00
|
|
|
|
|
|
|
key = k_spin_lock(&lock);
|
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* Read current timer value */
|
|
|
|
now = GPT_GetCurrentTimerCount(base);
|
2021-10-14 00:28:05 +02:00
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* Adjustment value, used to ensure next capture is on tick boundary */
|
|
|
|
adj = (now - announced_cycles) + (CYC_PER_TICK - 1);
|
2021-10-14 00:28:05 +02:00
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
next = ticks * CYC_PER_TICK;
|
|
|
|
/*
|
|
|
|
* The following section rounds the capture value up to the next tick
|
|
|
|
* boundary
|
2021-10-14 00:28:05 +02:00
|
|
|
*/
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
next += adj;
|
|
|
|
next = (next / CYC_PER_TICK) * CYC_PER_TICK;
|
|
|
|
next += announced_cycles;
|
|
|
|
/* Set GPT output value */
|
|
|
|
gpt_set_safe(next);
|
2021-10-14 00:28:05 +02:00
|
|
|
k_spin_unlock(&lock, key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the number of ticks since the last call to sys_clock_announce() */
|
|
|
|
uint32_t sys_clock_elapsed(void)
|
|
|
|
{
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
|
|
/* Always return 0 for tickful kernel system */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-10-14 00:28:05 +02:00
|
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
uint32_t cyc = GPT_GetCurrentTimerCount(base);
|
2021-10-14 00:28:05 +02:00
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
cyc -= announced_cycles;
|
2021-10-14 00:28:05 +02:00
|
|
|
k_spin_unlock(&lock, key);
|
|
|
|
return cyc / CYC_PER_TICK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get the number of elapsed hardware cycles of the clock */
|
|
|
|
uint32_t sys_clock_cycle_get_32(void)
|
|
|
|
{
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
return rollover_cycles + GPT_GetCurrentTimerCount(base);
|
2021-10-14 00:28:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @brief Initialize system timer driver
|
|
|
|
*
|
|
|
|
* Enable the hw timer, setting its tick period, and setup its interrupt
|
|
|
|
*/
|
init: remove the need for a dummy device pointer in SYS_INIT functions
The init infrastructure, found in `init.h`, is currently used by:
- `SYS_INIT`: to call functions before `main`
- `DEVICE_*`: to initialize devices
They are all sorted according to an initialization level + a priority.
`SYS_INIT` calls are really orthogonal to devices, however, the required
function signature requires a `const struct device *dev` as a first
argument. The only reason for that is because the same init machinery is
used by devices, so we have something like:
```c
struct init_entry {
int (*init)(const struct device *dev);
/* only set by DEVICE_*, otherwise NULL */
const struct device *dev;
}
```
As a result, we end up with such weird/ugly pattern:
```c
static int my_init(const struct device *dev)
{
/* always NULL! add ARG_UNUSED to avoid compiler warning */
ARG_UNUSED(dev);
...
}
```
This is really a result of poor internals isolation. This patch proposes
a to make init entries more flexible so that they can accept sytem
initialization calls like this:
```c
static int my_init(void)
{
...
}
```
This is achieved using a union:
```c
union init_function {
/* for SYS_INIT, used when init_entry.dev == NULL */
int (*sys)(void);
/* for DEVICE*, used when init_entry.dev != NULL */
int (*dev)(const struct device *dev);
};
struct init_entry {
/* stores init function (either for SYS_INIT or DEVICE*)
union init_function init_fn;
/* stores device pointer for DEVICE*, NULL for SYS_INIT. Allows
* to know which union entry to call.
*/
const struct device *dev;
}
```
This solution **does not increase ROM usage**, and allows to offer clean
public APIs for both SYS_INIT and DEVICE*. Note that however, init
machinery keeps a coupling with devices.
**NOTE**: This is a breaking change! All `SYS_INIT` functions will need
to be converted to the new signature. See the script offered in the
following commit.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
init: convert SYS_INIT functions to the new signature
Conversion scripted using scripts/utils/migrate_sys_init.py.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
manifest: update projects for SYS_INIT changes
Update modules with updated SYS_INIT calls:
- hal_ti
- lvgl
- sof
- TraceRecorderSource
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: devicetree: devices: adjust test
Adjust test according to the recently introduced SYS_INIT
infrastructure.
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
tests: kernel: threads: adjust SYS_INIT call
Adjust to the new signature: int (*init_fn)(void);
Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
2022-10-19 09:33:44 +02:00
|
|
|
int sys_clock_driver_init(void)
|
2021-10-14 00:28:05 +02:00
|
|
|
{
|
|
|
|
gpt_config_t gpt_config;
|
|
|
|
|
|
|
|
/* Configure ISR. Use instance 0 of the GPT timer */
|
2022-01-27 04:41:57 +01:00
|
|
|
IRQ_CONNECT(DT_IRQN(GPT_INST), DT_IRQ(GPT_INST, priority),
|
|
|
|
mcux_imx_gpt_isr, NULL, 0);
|
2021-10-14 00:28:05 +02:00
|
|
|
|
|
|
|
base = (GPT_Type *)DT_REG_ADDR(GPT_INST);
|
|
|
|
|
|
|
|
GPT_GetDefaultConfig(&gpt_config);
|
2021-12-14 17:24:39 +01:00
|
|
|
/* Enable GPT timer to run in SOC low power states */
|
|
|
|
gpt_config.enableRunInStop = true;
|
|
|
|
gpt_config.enableRunInWait = true;
|
|
|
|
gpt_config.enableRunInDoze = true;
|
2021-10-14 00:28:05 +02:00
|
|
|
/* Use 32KHz clock frequency */
|
|
|
|
gpt_config.clockSource = kGPT_ClockSource_LowFreq;
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* We use reset mode, but reset at MAX ticks- see comment below */
|
|
|
|
gpt_config.enableFreeRun = false;
|
2021-10-14 00:28:05 +02:00
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
/* Initialize the GPT timer, and enable the relevant interrupts */
|
2021-10-14 00:28:05 +02:00
|
|
|
GPT_Init(base, &gpt_config);
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
announced_cycles = 0U;
|
|
|
|
rollover_cycles = 0U;
|
2021-10-14 00:28:05 +02:00
|
|
|
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
|
|
/*
|
|
|
|
* Set GPT capture value 1 to MAX_CYCLES, and use GPT capture
|
|
|
|
* value 2 as the source for GPT interrupts. This way, we can
|
|
|
|
* use the counter as a free running timer, but it will roll
|
|
|
|
* over on a tick boundary.
|
2021-10-14 00:28:05 +02:00
|
|
|
*/
|
drivers: timer: mcux_gpt_timer: rewrite timer to use free run mode
GPT timer driver previously used "restart mode", where the timer would
count to a given value, then rollover. In this mode, "Any write access
to the Compare register of Channel 1 will reset the GPT counter". Since
a write to the compare register takes affect after 1 cycle of the
module's bus clock, and the bus clock is not synchonized with the GPT
module's low frequency counter clock, writing to the compare register
will induce a counter reset, and can cause the GPT to lose time
synchronization. This can induce time drift over time.
To fix this, rework the GPT driver to use "free run" mode. Note that
free run mode is not used directly, rather the GPT is configured to
reset on a tick boundary at boot, and then the second compare register
is used to set capture points. This way, the GPT interrupt will always
fire at a tick boundary, and no calculations are needed to handle
the counter rollover.
Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2023-03-24 19:29:22 +01:00
|
|
|
GPT_SetOutputCompareValue(base, kGPT_OutputCompare_Channel1,
|
|
|
|
MAX_CYCLES - 1);
|
|
|
|
|
|
|
|
/* Set initial trigger value to one tick worth of cycles */
|
|
|
|
GPT_SetOutputCompareValue(base, kGPT_OutputCompare_Channel2,
|
|
|
|
CYC_PER_TICK - 1);
|
|
|
|
/* Enable GPT interrupts for timer match, and reset at capture value 1 */
|
|
|
|
GPT_EnableInterrupts(base, kGPT_OutputCompare1InterruptEnable |
|
|
|
|
kGPT_OutputCompare2InterruptEnable);
|
|
|
|
} else {
|
|
|
|
/* For a tickful kernel, just roll the counter over every tick */
|
|
|
|
GPT_SetOutputCompareValue(base, kGPT_OutputCompare_Channel1,
|
|
|
|
CYC_PER_TICK - 1);
|
|
|
|
GPT_EnableInterrupts(base, kGPT_OutputCompare1InterruptEnable);
|
2021-10-14 00:28:05 +02:00
|
|
|
}
|
|
|
|
/* Enable IRQ */
|
|
|
|
irq_enable(DT_IRQN(GPT_INST));
|
|
|
|
/* Start timer */
|
|
|
|
GPT_StartTimer(base);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2,
|
|
|
|
CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
|