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