Use pit64b0 as systick, make "kernel sleep/uptime" commands work. Signed-off-by: Tony Han <tony.han@microchip.com>
195 lines
4.2 KiB
C
195 lines
4.2 KiB
C
/*
|
|
* Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT microchip_sam_pit64b
|
|
|
|
#include <zephyr/arch/cpu.h>
|
|
#include <zephyr/drivers/timer/system_timer.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/irq.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <zephyr/spinlock.h>
|
|
#include <zephyr/sys_clock.h>
|
|
|
|
LOG_MODULE_REGISTER(pit64b, CONFIG_CLOCK_CONTROL_LOG_LEVEL);
|
|
|
|
/* Device constant configuration parameters */
|
|
struct sam_pit64b_cfg {
|
|
pit64b_registers_t *reg;
|
|
};
|
|
|
|
#define CYCLES_PER_TICK (sys_clock_hw_cycles_per_sec() / CONFIG_SYS_CLOCK_TICKS_PER_SEC)
|
|
|
|
#define MAX_TICKS ((k_ticks_t)(0x1FFFFF / CYCLES_PER_TICK) - 1)
|
|
#define MIN_DELAY MAX(1024U, ((uint32_t)CYCLES_PER_TICK/16U))
|
|
#define MAX_CYCLES (MAX_TICKS * CYCLES_PER_TICK)
|
|
|
|
typedef uint32_t cycle_t;
|
|
|
|
static struct k_spinlock lock;
|
|
|
|
static cycle_t announced_cycles;
|
|
static cycle_t cycle_count;
|
|
static uint32_t last_load;
|
|
static uint32_t overflow;
|
|
|
|
const int32_t z_sys_timer_irq_for_test = DT_INST_IRQN(0);
|
|
|
|
const static struct sam_pit64b_cfg pit64b_cfg = {
|
|
.reg = (pit64b_registers_t *)DT_INST_REG_ADDR(0),
|
|
};
|
|
|
|
static uint32_t cycles_elapsed(void)
|
|
{
|
|
uint32_t val1 = pit64b_cfg.reg->PIT64B_TLSBR;
|
|
uint32_t ctrl = pit64b_cfg.reg->PIT64B_ISR;
|
|
uint32_t val2 = pit64b_cfg.reg->PIT64B_TLSBR;
|
|
|
|
if ((ctrl & PIT64B_IER_PERIOD_Msk) || (val1 > val2)) {
|
|
overflow += last_load;
|
|
(void)PIT64B0_REGS->PIT64B_TLSBR;
|
|
}
|
|
|
|
return val2 + overflow;
|
|
}
|
|
|
|
static void pit64b_isr(const void *arg)
|
|
{
|
|
ARG_UNUSED(arg);
|
|
|
|
uint32_t dcycles;
|
|
uint32_t delta_ticks;
|
|
|
|
(void)cycles_elapsed();
|
|
|
|
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
cycle_count += overflow;
|
|
overflow = 0;
|
|
|
|
dcycles = cycle_count - announced_cycles;
|
|
delta_ticks = dcycles / CYCLES_PER_TICK;
|
|
announced_cycles += delta_ticks * CYCLES_PER_TICK;
|
|
|
|
sys_clock_announce(delta_ticks);
|
|
} else {
|
|
sys_clock_announce(1);
|
|
}
|
|
}
|
|
|
|
void sys_clock_set_timeout(int32_t ticks, bool idle)
|
|
{
|
|
ARG_UNUSED(idle);
|
|
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
return;
|
|
}
|
|
|
|
uint32_t tmp = last_load;
|
|
k_spinlock_key_t key;
|
|
int32_t unannounced;
|
|
uint32_t val1, val2;
|
|
uint32_t delay;
|
|
|
|
ticks = (ticks == K_TICKS_FOREVER) ? MAX_TICKS : ticks;
|
|
|
|
key = k_spin_lock(&lock);
|
|
cycle_count += cycles_elapsed();
|
|
overflow = 0;
|
|
|
|
val1 = pit64b_cfg.reg->PIT64B_TLSBR;
|
|
|
|
unannounced = cycle_count - announced_cycles;
|
|
if (unannounced < 0) {
|
|
last_load = MIN_DELAY;
|
|
} else {
|
|
delay = ticks * CYCLES_PER_TICK;
|
|
delay += unannounced;
|
|
delay = DIV_ROUND_UP(delay, CYCLES_PER_TICK) * CYCLES_PER_TICK;
|
|
delay -= unannounced;
|
|
delay = MAX(delay, MIN_DELAY);
|
|
if (delay > MAX_CYCLES) {
|
|
last_load = MAX_CYCLES;
|
|
} else {
|
|
last_load = delay;
|
|
}
|
|
}
|
|
|
|
val2 = pit64b_cfg.reg->PIT64B_TLSBR;
|
|
|
|
pit64b_cfg.reg->PIT64B_LSBPR = last_load;
|
|
|
|
if (val1 > val2) {
|
|
cycle_count += (val2 + (tmp - val1));
|
|
} else {
|
|
cycle_count += (val2 - val1);
|
|
}
|
|
|
|
k_spin_unlock(&lock, key);
|
|
}
|
|
|
|
uint32_t sys_clock_elapsed(void)
|
|
{
|
|
uint32_t cycles;
|
|
|
|
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
|
return 0;
|
|
}
|
|
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
|
|
|
cycles = cycles_elapsed();
|
|
|
|
k_spin_unlock(&lock, key);
|
|
|
|
return (cycles + cycle_count - announced_cycles) / CYCLES_PER_TICK;
|
|
}
|
|
|
|
uint32_t sys_clock_cycle_get_32(void)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
|
|
|
uint32_t cycles = cycles_elapsed();
|
|
|
|
k_spin_unlock(&lock, key);
|
|
|
|
return cycle_count + cycles;
|
|
}
|
|
|
|
uint64_t sys_clock_cycle_get_64(void)
|
|
{
|
|
k_spinlock_key_t key = k_spin_lock(&lock);
|
|
|
|
uint64_t cycles = cycles_elapsed();
|
|
|
|
k_spin_unlock(&lock, key);
|
|
|
|
return cycle_count + cycles;
|
|
}
|
|
|
|
static int sys_clock_driver_init(void)
|
|
{
|
|
pit64b_cfg.reg->PIT64B_CR |= PIT64B_CR_SWRST_Msk;
|
|
|
|
pit64b_cfg.reg->PIT64B_IER |= PIT64B_IER_PERIOD_Msk;
|
|
pit64b_cfg.reg->PIT64B_MR = PIT64B_MR_CONT_Msk | PIT64B_MR_SMOD_Msk |
|
|
PIT64B_MR_SGCLK_Msk | PIT64B_MR_PRESCALER(1 - 1);
|
|
|
|
last_load = CYCLES_PER_TICK;
|
|
announced_cycles = 0;
|
|
cycle_count = 0;
|
|
overflow = 0;
|
|
pit64b_cfg.reg->PIT64B_MSBPR = 0;
|
|
pit64b_cfg.reg->PIT64B_LSBPR = last_load;
|
|
pit64b_cfg.reg->PIT64B_CR |= PIT64B_CR_START_Msk;
|
|
|
|
IRQ_CONNECT(DT_INST_IRQN(0), 0, pit64b_isr, 0, 0);
|
|
irq_enable(DT_INST_IRQN(0));
|
|
|
|
return 0;
|
|
}
|
|
|
|
SYS_INIT(sys_clock_driver_init, POST_KERNEL, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
|