blob: 0ba3ae64aca9ce3bcd8600ab1d7bb49efa267631 [file] [log] [blame] [edit]
// 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_PWM_H_
#define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_PWM_H_
/**
* @file
* @brief <a href="/hw/ip/pwm/doc/">PWM</a> Device Interface Functions
*
* The PWM block contains a 16-bit "phase counter" that controls each channel's
* output signal. The "phase counter" acts as a point of reference for a single
* PWM "pulse cycle", that is further broken into "beats", depending on the
* `beats_per_cycle` parameter. Specifically, a "pulse cycle" may contain [2,
* 2^16] "beats". Within a "pulse cycle", users can configure the duty cycle in
* number of "beats". Additionally, the duration of a single "beat", is computed
* by dividing the core clock frequency by `clock_divisor + 1`.
*
* PWM "pulse cycle" defined by 16-bit phase counter
* ___________________________________________________________
* | |
* v v
* |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-| ... |-|-|-|-|
*
* |-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-|-| ... |-|-|-|-|
* min beat size = 1 phase counter tick --> 2^16 "beats" / "pulse cycle"
*
* |---------------------------------------------- ... | ... --|
* max beat size = 2^15 phase counter ticks --> 2 "beats" / "pulse cycle"
*/
#include <stdint.h>
#include "sw/device/lib/dif/autogen/dif_pwm_autogen.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Helper X macro for defining enums and case statements related to PWM
* channels. If an additional channel is ever added to the hardware, this list
* can be updated.
*/
#define DIF_PWM_CHANNEL_LIST(X) \
X(0) \
X(1) \
X(2) \
X(3) \
X(4) \
X(5)
/**
* Helper macro for defining a `dif_pwm_channel_t` enumeration constant.
* @channel_ PWM channel of the enumeration constant.
*/
#define PWM_CHANNEL_ENUM_INIT_(channel_) \
kDifPwmChannel##channel_ = 1U << channel_,
/**
* A PWM channel.
*/
typedef enum dif_pwm_channel {
DIF_PWM_CHANNEL_LIST(PWM_CHANNEL_ENUM_INIT_)
} dif_pwm_channel_t;
#undef PWM_CHANNEL_ENUM_INIT_
/**
* Runtime configuration for PWM.
*
* This struct describes runtime configuration for one-time configuration of the
* PWM "pulse cycle" and "beat" durations that impact all PWM channels.
*/
typedef struct dif_pwm_config {
/**
* The core clock frequency divisor that determines the period of a single
* "beat" within a PWM "pulse cycle".
*
* Valid range: [0, 2^26)
*
* A value of zero, configures the "beat" period to the core clock period.
*/
uint32_t clock_divisor;
/**
* The total number of "beats" in a "pulse cycle", including both "on" and
* "off" beats in a "pulse cycle".
*
* Valid range: [2, 2^16]
*
* Note: while any value in the range is acceptable, values will be rounded
* down to closest power-of-two.
*
* A "beat" represents a unit of time of the PWM output signal. Higher values
* provide higher duty cycle resolutions, at the expense of longer "pulse
* cycles", while lower values provide shorter "pulse cycles", at the expense
* of lower duty cycle resolutions (since duty cycles are configured in
* "beats" / "pulse cycle").
*/
uint32_t beats_per_pulse_cycle;
} dif_pwm_config_t;
/**
* A PWM channel mode.
*/
typedef enum dif_pwm_mode {
/**
* The PWM duty cycle is set by the firmware and remains constant.
*/
kDifPwmModeFirmware = 0,
/**
* The PWM duty cycle linearly sweeps between both primary and secondary
* firmware-configured values, based on a firmware-configured step size.
*/
kDifPwmModeHeartbeat = 1,
/**
* The PWM duty cycle alternates between both primary and secondary
* firmware-configured values, based on two firmware-configured durations.
*/
kDifPwmModeBlink = 2,
} dif_pwm_mode_t;
/**
* A PWM channel polarity.
*/
typedef enum dif_pwm_polarity {
/**
* A PWM signal is active-high.
*/
kDifPwmPolarityActiveHigh = 0,
/**
* A PWM signal is active-low.
*/
kDifPwmPolarityActiveLow = 1,
} dif_pwm_polarity_t;
/**
* Runtime configuration for a specific PWM channel.
*
* This struct describes runtime configuration for one-time configuration of a
* specific PWM channel.
*/
typedef struct dif_pwm_channel_config {
/**
* Primary duty cycle, in number of "beats" / "pulse cycle".
*
* Valid range: [0, beats_per_pulse_cycle)
*
* Note: the raw value written to the `A_*` bitfield in each PWM channel's
* `DUTY_CYCLE_*` CSR is in units of "phase counter ticks", not "beats".
* However, the hardware only takes into account the first `DC_RESN` + 1
* MSBs of the raw duty cycle value to determine the number of "beats"
* for a given duty cycle. To make this configuration easier, the
* software manages the conversion from "beats_per_cycle" to
* "phase_counter_ticks" under the hood.
*/
uint16_t duty_cycle_a;
/**
* Secondary duty cycle, in number of "beats" / "pulse cycle", that is only
* relevant in heartbeat and blink modes.
*
* Valid range: [0, beats_per_pulse_cycle)
*
* Note: above notes for `duty_cycle_a` apply here too.
*/
uint16_t duty_cycle_b;
/**
* Phase delay at the beginning of a "pulse cycle" to delay the active
* duty cycle "beats" for, in number of "beats".
*
* Valid range: [0, beats_per_pulse_cycle)
*/
uint16_t phase_delay;
/**
* The operation mode to configure the channel in, see `dif_pwm_mode_t`.
*/
dif_pwm_mode_t mode;
/**
* The polarity to configure the channel in, see `dif_pwm_polarity_t`.
*/
dif_pwm_polarity_t polarity;
/**
* One of two blink parameters that only impact the "Heartbeat" and "Blink"
* operation modes.
*
* The meaning of this parameter is different based on the operation mode:
* - Heartbeat mode: determines the number of "pulse cycles" between
* increments/decrements to the duty cycle.
*
* - Blink mode: determines the number of "pulse cycles" to pulse at duty
* cycle A, before switching to duty cycle B.
*/
uint16_t blink_parameter_x;
/**
* One of two blink parameters that only impact the "Heartbeat" and "Blink"
* operation modes.
*
* The meaning of this parameter is different based on the operation mode:
* - Heartbeat mode: determines the increment/decrement amount in number
* of duty cycle "beats".
*
* - Blink mode: determines the number of "pulse cycles" to pulse at duty
* cycle B, before switching to duty cycle A.
*
* Note: the raw value written to the `blink_parameter_x` bitfield in each PWM
* channel's `BLINK_PARAM_*` CSR is in units of "phase counter ticks", not
* "beats". However, for ease of configuration, the software manages this
* conversion under the hood.
*/
uint16_t blink_parameter_y;
} dif_pwm_channel_config_t;
/**
* Configures "phase cycle" and "beat" durations of all PWM channels.
*
* Since changes to `CLK_DIV` and `DC_RESN` are only allowed when the PWM is
* disabled, this function has the side effect of temporarily disabling all PWM
* channels while configurations are updated, before restoring the original
* enablement state.
*
* This function should only need to be called once for the lifetime of
* `handle`.
*
* @param pwm A PWM handle.
* @param config Runtime configuration parameters.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_configure(const dif_pwm_t *pwm, dif_pwm_config_t config);
/**
* Configures a single PWM channel.
*
* Since changes to `CLK_DIV` and `DC_RESN` are only allowed when the PWM is
* disabled, this function has the side effect of temporarily disabling the
* PWM while configurations are updated, before returning the block to its
* original enablement state.
*
* This function should only need to be called once for each PWM channel that
* will be used.
*
* @param pwm A PWM handle.
* @param channel A PWM channel to configure.
* @param config Runtime configuration parameters for the channel.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_configure_channel(const dif_pwm_t *pwm,
dif_pwm_channel_t channel,
dif_pwm_channel_config_t config);
/**
* Sets the enablement state of the PWM phase counter, which controls the
* enablement of all PWM channels.
*
* @param pwm A PWM handle.
* @param enabled The enablement state to configure the PWM phase counter in.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_phase_cntr_set_enabled(const dif_pwm_t *pwm,
dif_toggle_t enabled);
/**
* Gets the enablement state of the PWM phase counter, which controls the
* enablement of all PWM channels.
*
* @param pwm A PWM handle.
* @param[out] is_enabled The enablement state of the PWM phase counter.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_phase_cntr_get_enabled(const dif_pwm_t *pwm,
dif_toggle_t *is_enabled);
/**
* Sets the enablement states of one or more PWM channels.
*
* @param pwm A PWM handle.
* @param channels The channels to enable (one or more `dif_pmw_channel_t`s
* ORed together.)
* @param enabled The enablement state to set.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_channel_set_enabled(const dif_pwm_t *pwm,
uint32_t channels,
dif_toggle_t enabled);
/**
* Gets the enablement state of one PWM channel.
*
* @param pwm A PWM handle.
* @param channel The PWM channel to get the enablement state of.
* @param[out] is_enabled The enablement state of the PWM channel.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_channel_get_enabled(const dif_pwm_t *pwm,
dif_pwm_channel_t channel,
dif_toggle_t *is_enabled);
/**
* Locks PWM configurations.
*
* This function is reentrant: calling it while locked will have no effect and
* return `kDifOk`.
*
* @param pwm A PWM handle.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_lock(const dif_pwm_t *pwm);
/**
* Checks whether PWM configurations are locked.
*
* @param pwm A PWM handle.
* @param[out] is_locked Out-param for the locked state.
* @return The result of the operation.
*/
OT_WARN_UNUSED_RESULT
dif_result_t dif_pwm_is_locked(const dif_pwm_t *pwm, bool *is_locked);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_PWM_H_