blob: f73b5a95913ffeeff274477db0215bd7627f6c9a [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_TIMER_H_
#define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_TIMER_H_
/**
* @file
* @brief <a href="/hw/ip/rv_timer/doc/">RV Timer</a> Device Interface Functions
*/
#include <stdint.h>
#include "sw/device/lib/base/mmio.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Represents an "enabled" state for a timer.
*/
typedef enum dif_rv_timer_enabled {
kDifRvTimerDisabled = 0,
kDifRvTimerEnabled,
} dif_rv_timer_enabled_t;
/**
* Represents timekeeping parameters for a particular timer.
*/
typedef struct dif_rv_timer_tick_params {
/**
* The prescaler value is the period of the timer tick in clock cycles,
* minus one. That is,
*
* prescale = clock_freq * tick_period - 1
*
* with |clock_freq| and |tick_period| given in units of hertz and seconds,
* respectively.
*
* For example, if the clock frequency is 50 MHz, and the desired tick
* period is is 1 microsecond, i.e, a tick frequency of 1 MHz, then the
* prescaler should be:
*
* (50 * 10^6) * (1 * 10^-6) - 1 = 49
*
* However, since |tick_period| is very small, it is much more convenient to
* work with |tick_freq|, its inverse, which will be an integer number of
* hertz. In particular,
*
* prescale = (clock_freq / tick_freq) - 1
*
* This value is declared as a uint16_t, but only the lowest 12 bits are
* actually used.
*/
uint16_t prescale;
/**
* The amount to increment the timer counter at each tick.
*/
uint8_t tick_step;
} dif_rv_timer_tick_params_t;
/**
* Represents the result of an RV timer operation..
*/
typedef enum dif_rv_timer_result {
kDifRvTimerOk = 0,
kDifRvTimerBadArg = 1,
kDifRvTimerError = 2,
} dif_rv_timer_result_t;
/**
* Errors returned by `dif_rv_timer_approximate_tick_params()`.
*/
typedef enum dif_rv_timer_approximate_tick_params_result {
kDifRvTimerApproximateTickParamsOk = kDifRvTimerOk,
kDifRvTimerApproximateTickParamsBadArg = kDifRvTimerBadArg,
kDifRvTimerApproximateTickParamsError = kDifRvTimerError,
/**
* Indicates that the requested counter frequency cannot be represented
* without loss in precission.
*/
kDifRvTimerApproximateTickParamsUnrepresentable,
} dif_rv_timer_approximate_tick_params_result_t;
/**
* Generates an aproximate `dif_rv_timer_tick_params_t` given the device
* clock frequency and desired counter frequency (both given in Hertz).
*
* For the purposes of this function, "counter frequency" is the frequency
* at which software would observe a timer counter to increase. If the
* clock has insufficient resolution, high counter frequencies may set a
* larger value for `tick_step`. For example, if the clock ticks at 50kHz,
* but we want a counter that seems to tick every microsecond (1MHz),
* we can achieve this with a prescale of 0 (so that there is a tick per
* clock cycle) and a tick step of 20 (since 20 * 50kHz = 1MHz).
*
* The return value of this function is only an approximation, and the
* actual counter frequency ultimately depends on the accuracy of the
* clock. The function will return an error if it cannot produce an acceptably
* accurate counter frequency using the given clock resolution.
*
* @param clock_freq The device clock frequency, in Hertz.
* @param counter_freq The desired counter frequency, in Hertz.
* @param[out] out Tick parameters that will approximately produce the desired
* counter frequency.
* @return The result of the operation.
*/
dif_rv_timer_approximate_tick_params_result_t
dif_rv_timer_approximate_tick_params(uint64_t clock_freq, uint64_t counter_freq,
dif_rv_timer_tick_params_t *out);
/**
* Configuration for initializing the RISC-V timer device.
*/
typedef struct dif_rv_timer_config {
/**
* The number of harts that this device supports time counters for.
* Must be at least one.
*
* This value is determined entirely by the hardware configuration.
*/
uint32_t hart_count;
/**
* The number of comparators (i.e, timers that can be set) associated
* with each hart. Must be at least one.
*
* This value is determined entirely by the hardware configuration.
*/
uint32_t comparator_count;
} dif_rv_timer_config_t;
/**
* State for a RISC-V timer, associated with a particular hardware thread.
*
* Its member variables should be considered private, and are only provided so
* that callers can allocate it.
*/
typedef struct dif_rv_timer {
mmio_region_t base_addr;
dif_rv_timer_config_t config;
} dif_rv_timer_t;
/**
* Initialize a RISC-V timer device with the given configuration.
*
* This function will deactivate all counters and reset all timers, which should
* each be configured and turned on manually after this function returns.
*
* @param base_addr MMIO region for the device hardware registers.
* @param config Configuration for initializing a particular timer.
* @param[out] timer_out The timer device.
* @return The result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_init(mmio_region_t base_addr,
dif_rv_timer_config_t config,
dif_rv_timer_t *timer_out);
/**
* Completely resets a timer device, disabling all IRQs, counters, and
* comparators.
*
* @param timer A timer device.
* @return The result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_reset(const dif_rv_timer_t *timer);
/**
* Configures the tick params for a particular hart's counter.
*
* This function should not be called when `hart_id`'s counter is enabled; it is
* the caller's responsibility to assert this precondition.
* The function `dif_rv_timer_approximate_tick_params()` can be used to generate
* tick parameter values.
*
* @param timer A timer device.
* @param hart_id The hart to configure.
* @param params The timing parameters.
* @return The result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_set_tick_params(
const dif_rv_timer_t *timer, uint32_t hart_id,
dif_rv_timer_tick_params_t params);
/**
* Starts or stops a particular hart's counter.
*
* While a counter is enabled, the counter value will increase each tick, but
* its timekeeping values cannot be reconfigured.
*
* @param timer A timer device.
* @param hart_id The hart counter to enable/disable.
* @param state The new enablement state.
* @return The result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_counter_set_enabled(
const dif_rv_timer_t *timer, uint32_t hart_id,
dif_rv_timer_enabled_t state);
/**
* Reads the current value on a particular hart's timer.
*
* @param timer A timer device.
* @param hart_id The hart counter to read.
* @param[out] out The counter value.
* @return The result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_counter_read(const dif_rv_timer_t *timer,
uint32_t hart_id,
uint64_t *out);
/**
* Writes the given value to a particular hart's timer.
*
* @param timer A timer device.
* @param hart_id The hart counter to write.
* @param count The counter value to write.
* @return The result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_counter_write(const dif_rv_timer_t *timer,
uint32_t hart_id,
uint64_t count);
/**
* Arms the timer to go off once the counter value is greater than
* or equal to `threshold`, by setting up the given comparator.
*
* Beware that the following naive implementation of setting an alarm
* contains a bug:
* uint64_t time;
* dif_rv_timer_counter_read(my_timer, kMyHart, &time);
* time += kSomeDuration; // (*)
* dif_rv_timer_arm(my_timer, kMyHart, kMyComp, time);
*
* If `time` wraps around when performing the addition, an interrupt will be
* fired immediately upon calling `dif_rv_timer_arm`. Care should be taken to
* perform saturating addition at (*), so that the interrupt is fired when the
* timer value wraps around; this way, the interrupt handler can re-arm the
* timer for the rest of the duration.
*
* This function makes no effort to protect the caller from setting alarms in
* the past that would immediately fire an interrupt. It is the caller's
* responsibility to read the current counter value and pick a reasonable alarm
* threshold.
*
* @param timer A timer device.
* @param hart_id The hart counter to arm against.
* @param comp_id The comparator to set up.
* @param threshold The value to go off at.
* @return The result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_arm(const dif_rv_timer_t *timer,
uint32_t hart_id, uint32_t comp_id,
uint64_t threshold);
/**
* Enables or disables a particular timer's IRQ. That is, this function controls
* whether or not an IRQ is fired when the timer reaches its configured
* threshold.
*
* @param timer A timer device.
* @param hart_id The hart counter associated with the timer.
* @param comp_id The comparator associated with the timer.
* @param state The desired status.
* @return the result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_irq_enable(const dif_rv_timer_t *timer,
uint32_t hart_id,
uint32_t comp_id,
dif_rv_timer_enabled_t state);
/**
* Returns whether the IRQ for a particular timer is currently being serviced.
*
* @param timer A timer device.
* @param hart_id The hart counter associated with the timer.
* @param comp_id The comparator associated with the timer.
* @param[out] flag_out The interrupt status.
* @return the result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_irq_get(const dif_rv_timer_t *timer,
uint32_t hart_id, uint32_t comp_id,
bool *flag_out);
/**
* Clears the IRQ for a particular timer.
*
* @param timer A timer device.
* @param hart_id The hart counter associated with the timer.
* @param comp_id The comparator associated with the timer.
* @return the result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_irq_clear(const dif_rv_timer_t *timer,
uint32_t hart_id,
uint32_t comp_id);
/**
* Forces the IRQ for a particular timer to fire.
*
* @param timer A timer device.
* @param hart_id The hart counter associated with the timer.
* @param comp_id The comparator associated with the timer.
* @return the result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_irq_force(const dif_rv_timer_t *timer,
uint32_t hart_id,
uint32_t comp_id);
/**
* Disables all IRQs for a particular hart.
*
* Optionally returns a `state` value containing the previous itnerrupt state.
* `state` may be NULL. See `dif_rv_timer_irq_restore()`.
*
* @param timer a timer device.
* @param[out] state IRQ state information for use with
* `dif_rv_timer_irq_restore`.
* @return the result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_irq_disable(const dif_rv_timer_t *timer,
uint32_t hart_id,
uint32_t *state);
/**
* Restores IRQ state for a particular hart.
*
* @param timer a timer device.
* @param state IRQ state information to restore.
* @return the result of the operation.
*/
dif_rv_timer_result_t dif_rv_timer_irq_restore(const dif_rv_timer_t *timer,
uint32_t hart_id,
uint32_t state);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_RV_TIMER_H_