Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

194 lines
4.2 KiB

/*
* 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);