| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * Copyright (C) 2021, Hensoldt Cyber GmbH |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| #include <stdio.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <utils/util.h> |
| #include "../../arch/arm/clock.h" |
| #include <platsupport/timer.h> |
| #include <platsupport/plat/spt.h> |
| |
| /* The arm timer on the BCM2711 is based on a SP804 timer with some modifications. |
| * Nothing seemed to have changed since the BCM2837 (except for the core_freq). |
| |
| - Only one timer |
| - It only runs in continuous mode |
| - Can be set to either stop or continue in ARM debug halt mode |
| - Pre-scale options are only 1, 16 and 256 |
| - CLK source is 250 MHz before prescale. |
| TODO: read comment in bcm2711/clock.c |
| - Has an additional register that will count up on each timer tick and |
| roll over without generating interrupts. It has its own prescalar. |
| |
| Functions by setting a value in the load register which the timer will move to the value |
| register and when the value counts down to zero, an interrupt will occur if interrupts are |
| enabled and the value from the load register will be copied back into the value register. |
| |
| */ |
| |
| enum { |
| /* |
| Width of the counter. |
| 0: 16-bit counters |
| 1: 32-bit counters |
| */ |
| COUNTER_WIDTH_BIT = 1, |
| /* |
| The prescale bits select the prescale for the down counter. |
| Note: There are other pre-scale bits in this register (16:23) for |
| the separate free running up-counting counter. |
| 00 : pre-scale is clock / 1 (No pre-scale) |
| 01 : pre-scale is clock / 16 |
| 10 : pre-scale is clock / 256 |
| 11 : pre-scale is clock / 1 |
| */ |
| PRESCALE_BIT = 2, |
| |
| /* |
| Interrupt enable bit. |
| 0: Timer interrupt disabled |
| 1: Timer interrupt enabled |
| */ |
| TIMER_INTERRUPT_ENABLE = 5, |
| |
| /* |
| Timer enable. |
| 0: Disabled |
| 1: Enabled |
| */ |
| TIMER_ENABLE = 7, |
| |
| /* |
| Timer behaviour when ARM is in debug halt mode: |
| 0: Timers keep running |
| 1: Timers halted. |
| */ |
| ARM_DEBUG_HALT = 8, |
| |
| /* |
| Whether the free-run counter is enabled. |
| 0: Enabled |
| 1: Disabled |
| */ |
| FREE_RUN_ENABLE = 9, |
| |
| /* |
| Free running scalar. 8 bits wide. Reset value is 0x3E |
| Freq = clk/(prescale + 1) |
| */ |
| FREE_RUN_PRESCALE = 16 |
| } control_reg; |
| |
| #define LOAD_WIDTH 32 |
| #define VALUE_WIDTH 32 |
| #define CTRL_WIDTH 24 |
| #define IRQ_CLEAR_WIDTH 32 |
| #define RAW_IRQ_WIDTH 1 |
| #define MASKED_IRQ_WIDTH 1 |
| #define PRE_DIVIDER_WIDTH 10 |
| |
| #define TIMER_BASE_OFFSET 0x400 |
| |
| int spt_start(spt_t *spt) |
| { |
| if (spt == NULL) { |
| return EINVAL; |
| } |
| /* Enable timer */ |
| spt->regs->ctrl = BIT(FREE_RUN_ENABLE) | BIT(COUNTER_WIDTH_BIT); |
| return 0; |
| } |
| |
| int spt_stop(spt_t *spt) |
| { |
| if (!spt) { |
| return EINVAL; |
| } |
| /* Disable timer */ |
| spt->regs->ctrl = 0; |
| return 0; |
| } |
| |
| /* Set up the timer to fire an interrupt every ns nanoseconds. |
| * The first such interrupt may arrive before ns nanoseconds |
| * have passed since calling. */ |
| int spt_set_timeout(spt_t *spt, uint64_t ns) |
| { |
| if (!spt) { |
| return EINVAL; |
| } |
| uint64_t ticks = ns / (NS_IN_US / (spt->freq / MHZ)); |
| uint32_t prescale_bits = 0; |
| spt->counter_start = ns; |
| if (ticks == 0) { |
| ZF_LOGE("ns too low: %lu\n", ns); |
| return EINVAL; |
| } |
| |
| /* Prescale only has 3 values, so we need to calculate them here */ |
| if (ticks >= (UINT32_MAX + 1)) { |
| ticks /= 16; |
| if (ticks >= (1ULL << 32)) { |
| ticks /= 16; |
| if (ticks >= (1ULL << 32)) { |
| ZF_LOGE("ns too high: %lu\n", ns); |
| return EINVAL; |
| } else { |
| prescale_bits = 2; |
| } |
| } else { |
| prescale_bits = 1; |
| } |
| } |
| |
| /* Configure timer */ |
| spt->regs->load = ticks; |
| spt->regs->pre_divider = 0; |
| spt->regs->irq_clear = 1; |
| spt->regs->ctrl = BIT(COUNTER_WIDTH_BIT) | (prescale_bits << PRESCALE_BIT) | |
| BIT(TIMER_INTERRUPT_ENABLE) | BIT(FREE_RUN_ENABLE) | BIT(TIMER_ENABLE); |
| |
| return 0; |
| } |
| |
| int spt_handle_irq(spt_t *spt) |
| { |
| if (!spt) { |
| return EINVAL; |
| } |
| if (spt->regs->masked_irq) { |
| spt->regs->irq_clear = 1; |
| spt->regs->ctrl &= ~(BIT(TIMER_ENABLE)); |
| } else { |
| ZF_LOGW("handle irq called when no interrupt pending\n"); |
| return EINVAL; |
| } |
| return 0; |
| } |
| |
| uint64_t spt_get_time(spt_t *spt) |
| { |
| uint64_t value = spt->regs->free_run_count; |
| /* convert raw count to ns. As we never change the free run prescaler we do not need to |
| * scale by it. We multiplly by mhz as the free run count is a 32-bit value so we will |
| * not overflow a 64-bit value at that point */ |
| uint64_t ns = (value * MHZ / spt->freq) * NS_IN_US; |
| return ns; |
| } |
| |
| int spt_init(spt_t *spt, spt_config_t config) |
| { |
| clk_t *clk; |
| |
| if (!spt || !config.vaddr) { |
| return EINVAL; |
| } |
| |
| /* Save the mmio address */ |
| spt->regs = (void *)((uintptr_t) config.vaddr) + TIMER_BASE_OFFSET; |
| clock_sys_t clk_sys; |
| clock_sys_init_default(&clk_sys); |
| clk = clk_get_clock(&clk_sys, CLK_SP804); |
| spt->freq = clk_get_freq(clk); |
| |
| /* handle any pending irqs */ |
| spt_handle_irq(spt); |
| return 0; |
| } |