blob: b275bda4aebd45959a574395bac9fe9fa3038f26 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/dif/dif_adc_ctrl.h"
#include <assert.h>
#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/dif/dif_base.h"
#include "adc_ctrl_regs.h" // Generated.
static_assert(ADC_CTRL_PARAM_NUM_ADC_CHANNEL == 2,
"Expected two ADC Controller channels.");
static_assert(ADC_CTRL_PARAM_NUM_ADC_FILTER == 8,
"Expected eight ADC Controller filters.");
static_assert(ADC_CTRL_ADC_CHN0_FILTER_CTL_MIN_V_FIELD_WIDTH == 10,
"Expected channel-0 filter min-voltage field to be 10 bits.");
static_assert(ADC_CTRL_ADC_CHN1_FILTER_CTL_MIN_V_FIELD_WIDTH == 10,
"Expected channel-1 filter min-voltage field to be 10 bits.");
static_assert(ADC_CTRL_ADC_CHN0_FILTER_CTL_MAX_V_FIELD_WIDTH == 10,
"Expected channel-0 filter max-voltage field to be 10 bits.");
static_assert(ADC_CTRL_ADC_CHN1_FILTER_CTL_MAX_V_FIELD_WIDTH == 10,
"Expected channel-1 filter max-voltage field to be 10 bits.");
dif_result_t dif_adc_ctrl_configure(const dif_adc_ctrl_t *adc_ctrl,
dif_adc_ctrl_config_t config) {
if (adc_ctrl == NULL ||
config.power_up_time_aon_cycles > ADC_CTRL_ADC_PD_CTL_PWRUP_TIME_MASK ||
config.wake_up_time_aon_cycles > ADC_CTRL_ADC_PD_CTL_WAKEUP_TIME_MASK ||
config.num_low_power_samples == 0 ||
config.num_normal_power_samples == 0) {
return kDifBadArg;
}
uint32_t en_ctrl_reg = ADC_CTRL_ADC_EN_CTL_REG_RESVAL;
uint32_t pd_ctrl_reg = ADC_CTRL_ADC_PD_CTL_REG_RESVAL;
uint32_t lp_sample_ctrl_reg = ADC_CTRL_ADC_LP_SAMPLE_CTL_REG_RESVAL;
uint32_t np_sample_ctrl_reg = ADC_CTRL_ADC_SAMPLE_CTL_REG_RESVAL;
switch (config.mode) {
case kDifAdcCtrlLowPowerScanMode:
pd_ctrl_reg = bitfield_bit32_write(pd_ctrl_reg,
ADC_CTRL_ADC_PD_CTL_LP_MODE_BIT, true);
pd_ctrl_reg = bitfield_field32_write(
pd_ctrl_reg, ADC_CTRL_ADC_PD_CTL_WAKEUP_TIME_FIELD,
config.wake_up_time_aon_cycles);
lp_sample_ctrl_reg = bitfield_field32_write(
lp_sample_ctrl_reg, ADC_CTRL_ADC_LP_SAMPLE_CTL_LP_SAMPLE_CNT_FIELD,
config.num_low_power_samples);
OT_FALLTHROUGH_INTENDED;
case kDifAdcCtrlNormalPowerScanMode:
np_sample_ctrl_reg = bitfield_field32_write(
np_sample_ctrl_reg, ADC_CTRL_ADC_SAMPLE_CTL_NP_SAMPLE_CNT_FIELD,
config.num_normal_power_samples);
break;
case kDifAdcCtrlOneshotMode:
en_ctrl_reg = bitfield_bit32_write(
en_ctrl_reg, ADC_CTRL_ADC_EN_CTL_ONESHOT_MODE_BIT, true);
break;
default:
return kDifBadArg;
}
// Regardless of the mode, the ADC could be powered down, so we need to set
// the power-up time.
pd_ctrl_reg =
bitfield_field32_write(pd_ctrl_reg, ADC_CTRL_ADC_PD_CTL_PWRUP_TIME_FIELD,
config.power_up_time_aon_cycles);
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_EN_CTL_REG_OFFSET,
en_ctrl_reg);
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_PD_CTL_REG_OFFSET,
pd_ctrl_reg);
mmio_region_write32(adc_ctrl->base_addr,
ADC_CTRL_ADC_LP_SAMPLE_CTL_REG_OFFSET,
lp_sample_ctrl_reg);
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_SAMPLE_CTL_REG_OFFSET,
np_sample_ctrl_reg);
return kDifOk;
}
dif_result_t dif_adc_ctrl_configure_filter(const dif_adc_ctrl_t *adc_ctrl,
dif_adc_ctrl_channel_t channel,
dif_adc_ctrl_filter_config_t config,
dif_toggle_t enabled) {
if (adc_ctrl == NULL || config.min_voltage > 1023 ||
config.max_voltage > 1023) {
return kDifBadArg;
}
ptrdiff_t filter_ctrl_reg_offset;
bitfield_field32_t min_voltage_field;
bitfield_field32_t max_voltage_field;
bitfield_bit32_index_t in_range_bit;
bitfield_bit32_index_t enable_bit;
#define DIF_ADC_CTRL_CHANNEL0_FILTER_CONFIG_CASE_(filter_) \
case kDifAdcCtrlFilter##filter_: \
filter_ctrl_reg_offset = \
ADC_CTRL_ADC_CHN0_FILTER_CTL_##filter_##_REG_OFFSET; \
min_voltage_field = \
ADC_CTRL_ADC_CHN0_FILTER_CTL_##filter_##_MIN_V_##filter_##_FIELD; \
max_voltage_field = \
ADC_CTRL_ADC_CHN0_FILTER_CTL_##filter_##_MAX_V_##filter_##_FIELD; \
in_range_bit = \
ADC_CTRL_ADC_CHN0_FILTER_CTL_##filter_##_COND_##filter_##_BIT; \
enable_bit = ADC_CTRL_ADC_CHN0_FILTER_CTL_##filter_##_EN_##filter_##_BIT; \
break;
#define DIF_ADC_CTRL_CHANNEL1_FILTER_CONFIG_CASE_(filter_) \
case kDifAdcCtrlFilter##filter_: \
filter_ctrl_reg_offset = \
ADC_CTRL_ADC_CHN1_FILTER_CTL_##filter_##_REG_OFFSET; \
min_voltage_field = \
ADC_CTRL_ADC_CHN1_FILTER_CTL_##filter_##_MIN_V_##filter_##_FIELD; \
max_voltage_field = \
ADC_CTRL_ADC_CHN1_FILTER_CTL_##filter_##_MAX_V_##filter_##_FIELD; \
in_range_bit = \
ADC_CTRL_ADC_CHN1_FILTER_CTL_##filter_##_COND_##filter_##_BIT; \
enable_bit = ADC_CTRL_ADC_CHN1_FILTER_CTL_##filter_##_EN_##filter_##_BIT; \
break;
switch (channel) {
case kDifAdcCtrlChannel0:
switch (config.filter) {
DIF_ADC_CTRL_FILTER_LIST(DIF_ADC_CTRL_CHANNEL0_FILTER_CONFIG_CASE_)
default:
return kDifBadArg;
}
break;
case kDifAdcCtrlChannel1:
switch (config.filter) {
DIF_ADC_CTRL_FILTER_LIST(DIF_ADC_CTRL_CHANNEL1_FILTER_CONFIG_CASE_)
default:
return kDifBadArg;
}
break;
default:
return kDifBadArg;
}
#undef DIF_ADC_CTRL_CHANNEL0_FILTER_CONFIG_CASE_
#undef DIF_ADC_CTRL_CHANNEL1_FILTER_CONFIG_CASE_
// Configure filter control register.
uint32_t filter_ctrl_reg =
bitfield_field32_write(0, min_voltage_field, config.min_voltage);
filter_ctrl_reg = bitfield_field32_write(filter_ctrl_reg, max_voltage_field,
config.max_voltage);
filter_ctrl_reg =
bitfield_bit32_write(filter_ctrl_reg, in_range_bit, !config.in_range);
filter_ctrl_reg = bitfield_bit32_write(filter_ctrl_reg, enable_bit,
dif_toggle_to_bool(enabled));
mmio_region_write32(adc_ctrl->base_addr, filter_ctrl_reg_offset,
filter_ctrl_reg);
// Configure wakeup control register.
uint32_t wakeup_ctrl_reg = mmio_region_read32(
adc_ctrl->base_addr, ADC_CTRL_ADC_WAKEUP_CTL_REG_OFFSET);
wakeup_ctrl_reg = bitfield_bit32_write(wakeup_ctrl_reg, config.filter,
config.generate_wakeup_on_match);
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_WAKEUP_CTL_REG_OFFSET,
wakeup_ctrl_reg);
// Configure interrupt control register.
uint32_t intr_ctrl_reg =
mmio_region_read32(adc_ctrl->base_addr, ADC_CTRL_ADC_INTR_CTL_REG_OFFSET);
intr_ctrl_reg = bitfield_bit32_write(intr_ctrl_reg, config.filter,
config.generate_irq_on_match);
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_INTR_CTL_REG_OFFSET,
intr_ctrl_reg);
return kDifOk;
}
dif_result_t dif_adc_ctrl_set_enabled(const dif_adc_ctrl_t *adc_ctrl,
dif_toggle_t enabled) {
if (adc_ctrl == NULL || !dif_is_valid_toggle(enabled)) {
return kDifBadArg;
}
uint32_t en_ctrl_reg =
mmio_region_read32(adc_ctrl->base_addr, ADC_CTRL_ADC_EN_CTL_REG_OFFSET);
en_ctrl_reg =
bitfield_bit32_write(en_ctrl_reg, ADC_CTRL_ADC_EN_CTL_ADC_ENABLE_BIT,
dif_toggle_to_bool(enabled));
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_EN_CTL_REG_OFFSET,
en_ctrl_reg);
return kDifOk;
}
dif_result_t dif_adc_ctrl_get_enabled(const dif_adc_ctrl_t *adc_ctrl,
dif_toggle_t *is_enabled) {
if (adc_ctrl == NULL || is_enabled == NULL) {
return kDifBadArg;
}
uint32_t en_ctrl_reg =
mmio_region_read32(adc_ctrl->base_addr, ADC_CTRL_ADC_EN_CTL_REG_OFFSET);
*is_enabled = dif_bool_to_toggle(
bitfield_bit32_read(en_ctrl_reg, ADC_CTRL_ADC_EN_CTL_ADC_ENABLE_BIT));
return kDifOk;
}
static bool get_filter_offset(dif_adc_ctrl_channel_t channel,
dif_adc_ctrl_filter_t filter,
ptrdiff_t *filter_ctrl_reg_offset) {
#define DIF_ADC_CTRL_CHANNEL0_FILTER_CTRL_REG_CASE_(filter_) \
case kDifAdcCtrlFilter##filter_: \
*filter_ctrl_reg_offset = \
ADC_CTRL_ADC_CHN0_FILTER_CTL_##filter_##_REG_OFFSET; \
break;
#define DIF_ADC_CTRL_CHANNEL1_FILTER_CTRL_REG_CASE_(filter_) \
case kDifAdcCtrlFilter##filter_: \
*filter_ctrl_reg_offset = \
ADC_CTRL_ADC_CHN1_FILTER_CTL_##filter_##_REG_OFFSET; \
break;
switch (channel) {
case kDifAdcCtrlChannel0:
switch (filter) {
DIF_ADC_CTRL_FILTER_LIST(DIF_ADC_CTRL_CHANNEL0_FILTER_CTRL_REG_CASE_)
default:
return false;
}
break;
case kDifAdcCtrlChannel1:
switch (filter) {
DIF_ADC_CTRL_FILTER_LIST(DIF_ADC_CTRL_CHANNEL1_FILTER_CTRL_REG_CASE_)
default:
return false;
}
break;
default:
return false;
}
#undef DIF_ADC_CTRL_CHANNEL0_FILTER_CTRL_REG_CASE_
#undef DIF_ADC_CTRL_CHANNEL1_FILTER_CTRL_REG_CASE_
return true;
}
static bool get_filter_enable_bit(dif_adc_ctrl_channel_t channel,
dif_adc_ctrl_filter_t filter,
bitfield_bit32_index_t *enable_bit) {
#define DIF_ADC_CTRL_CHANNEL0_FILTER_ENABLE_CASE_(filter_) \
case kDifAdcCtrlFilter##filter_: \
*enable_bit = ADC_CTRL_ADC_CHN0_FILTER_CTL_##filter_##_EN_##filter_##_BIT; \
break;
#define DIF_ADC_CTRL_CHANNEL1_FILTER_ENABLE_CASE_(filter_) \
case kDifAdcCtrlFilter##filter_: \
*enable_bit = ADC_CTRL_ADC_CHN1_FILTER_CTL_##filter_##_EN_##filter_##_BIT; \
break;
switch (channel) {
case kDifAdcCtrlChannel0:
switch (filter) {
DIF_ADC_CTRL_FILTER_LIST(DIF_ADC_CTRL_CHANNEL0_FILTER_ENABLE_CASE_)
default:
return false;
}
break;
case kDifAdcCtrlChannel1:
switch (filter) {
DIF_ADC_CTRL_FILTER_LIST(DIF_ADC_CTRL_CHANNEL1_FILTER_ENABLE_CASE_)
default:
return false;
}
break;
default:
return false;
}
#undef DIF_ADC_CTRL_CHANNEL0_FILTER_ENABLE_CASE_
#undef DIF_ADC_CTRL_CHANNEL1_FILTER_ENABLE_CASE_
return true;
}
dif_result_t dif_adc_ctrl_filter_set_enabled(const dif_adc_ctrl_t *adc_ctrl,
dif_adc_ctrl_channel_t channel,
dif_adc_ctrl_filter_t filter,
dif_toggle_t enabled) {
if (adc_ctrl == NULL || !dif_is_valid_toggle(enabled)) {
return kDifBadArg;
}
ptrdiff_t filter_ctrl_reg_offset;
bitfield_bit32_index_t enable_bit;
if (!get_filter_offset(channel, filter, &filter_ctrl_reg_offset)) {
return kDifBadArg;
}
if (!get_filter_enable_bit(channel, filter, &enable_bit)) {
return kDifBadArg;
}
uint32_t filter_ctrl_reg =
mmio_region_read32(adc_ctrl->base_addr, filter_ctrl_reg_offset);
filter_ctrl_reg = bitfield_bit32_write(filter_ctrl_reg, enable_bit,
dif_toggle_to_bool(enabled));
mmio_region_write32(adc_ctrl->base_addr, filter_ctrl_reg_offset,
filter_ctrl_reg);
return kDifOk;
}
dif_result_t dif_adc_ctrl_filter_get_enabled(const dif_adc_ctrl_t *adc_ctrl,
dif_adc_ctrl_channel_t channel,
dif_adc_ctrl_filter_t filter,
dif_toggle_t *is_enabled) {
if (adc_ctrl == NULL || is_enabled == NULL) {
return kDifBadArg;
}
ptrdiff_t filter_ctrl_reg_offset;
bitfield_bit32_index_t enable_bit;
if (!get_filter_offset(channel, filter, &filter_ctrl_reg_offset)) {
return kDifBadArg;
}
if (!get_filter_enable_bit(channel, filter, &enable_bit)) {
return kDifBadArg;
}
uint32_t filter_ctrl_reg =
mmio_region_read32(adc_ctrl->base_addr, filter_ctrl_reg_offset);
*is_enabled =
dif_bool_to_toggle(bitfield_bit32_read(filter_ctrl_reg, enable_bit));
return kDifOk;
}
dif_result_t dif_adc_ctrl_get_triggered_value(const dif_adc_ctrl_t *adc_ctrl,
dif_adc_ctrl_channel_t channel,
uint16_t *value) {
if (adc_ctrl == NULL || value == NULL) {
return kDifBadArg;
}
uint32_t value_reg;
#define DIF_ADC_CTRL_CHANNEL_TRIG_VALUE_CASE_(channel_) \
case kDifAdcCtrlChannel##channel_: \
value_reg = mmio_region_read32( \
adc_ctrl->base_addr, ADC_CTRL_ADC_CHN_VAL_##channel_##_REG_OFFSET); \
*value = bitfield_field32_read( \
value_reg, \
ADC_CTRL_ADC_CHN_VAL_##channel_##_ADC_CHN_VALUE_INTR_##channel_##_FIELD); \
break;
switch (channel) {
DIF_ADC_CTRL_CHANNEL_LIST(DIF_ADC_CTRL_CHANNEL_TRIG_VALUE_CASE_)
default:
return kDifBadArg;
}
#undef DIF_ADC_CTRL_CHANNEL_TRIG_VALUE_CASE_
return kDifOk;
}
dif_result_t dif_adc_ctrl_get_latest_value(const dif_adc_ctrl_t *adc_ctrl,
dif_adc_ctrl_channel_t channel,
uint16_t *value) {
if (adc_ctrl == NULL || value == NULL) {
return kDifBadArg;
}
uint32_t value_reg;
#define DIF_ADC_CTRL_CHANNEL_LATEST_VALUE_CASE_(channel_) \
case kDifAdcCtrlChannel##channel_: \
value_reg = mmio_region_read32( \
adc_ctrl->base_addr, ADC_CTRL_ADC_CHN_VAL_##channel_##_REG_OFFSET); \
*value = bitfield_field32_read( \
value_reg, \
ADC_CTRL_ADC_CHN_VAL_##channel_##_ADC_CHN_VALUE_##channel_##_FIELD); \
break;
switch (channel) {
DIF_ADC_CTRL_CHANNEL_LIST(DIF_ADC_CTRL_CHANNEL_LATEST_VALUE_CASE_)
default:
return kDifBadArg;
}
#undef DIF_ADC_CTRL_CHANNEL_LATEST_VALUE_CASE_
return kDifOk;
}
dif_result_t dif_adc_ctrl_reset(const dif_adc_ctrl_t *adc_ctrl) {
if (adc_ctrl == NULL) {
return kDifBadArg;
}
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_FSM_RST_REG_OFFSET, 1);
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_FSM_RST_REG_OFFSET, 0);
return kDifOk;
}
dif_result_t dif_adc_ctrl_irq_get_causes(const dif_adc_ctrl_t *adc_ctrl,
uint32_t *causes) {
if (adc_ctrl == NULL || causes == NULL) {
return kDifBadArg;
}
*causes = mmio_region_read32(adc_ctrl->base_addr,
ADC_CTRL_ADC_INTR_STATUS_REG_OFFSET);
return kDifOk;
}
dif_result_t dif_adc_ctrl_irq_clear_causes(const dif_adc_ctrl_t *adc_ctrl,
uint32_t causes) {
if (adc_ctrl == NULL || causes > kDifAdcCtrlIrqCauseAll) {
return kDifBadArg;
}
uint32_t filter_causes = (~kDifAdcCtrlIrqCauseOneshot) & causes;
if (filter_causes) {
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_FILTER_STATUS_REG_OFFSET,
filter_causes);
}
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_INTR_STATUS_REG_OFFSET,
causes);
return kDifOk;
}
dif_result_t dif_adc_ctrl_filter_match_wakeup_set_enabled(
const dif_adc_ctrl_t *adc_ctrl, dif_adc_ctrl_filter_t filter,
dif_toggle_t enabled) {
if (adc_ctrl == NULL || filter >= ADC_CTRL_PARAM_NUM_ADC_FILTER ||
!dif_is_valid_toggle(enabled)) {
return kDifBadArg;
}
uint32_t wakeup_ctrl_reg = mmio_region_read32(
adc_ctrl->base_addr, ADC_CTRL_ADC_WAKEUP_CTL_REG_OFFSET);
wakeup_ctrl_reg = bitfield_bit32_write(wakeup_ctrl_reg, filter,
dif_toggle_to_bool(enabled));
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_WAKEUP_CTL_REG_OFFSET,
wakeup_ctrl_reg);
return kDifOk;
}
dif_result_t dif_adc_ctrl_filter_match_wakeup_get_enabled(
const dif_adc_ctrl_t *adc_ctrl, dif_adc_ctrl_filter_t filter,
dif_toggle_t *is_enabled) {
if (adc_ctrl == NULL || filter >= ADC_CTRL_PARAM_NUM_ADC_FILTER ||
is_enabled == NULL) {
return kDifBadArg;
}
uint32_t wakeup_ctrl_reg = mmio_region_read32(
adc_ctrl->base_addr, ADC_CTRL_ADC_WAKEUP_CTL_REG_OFFSET);
*is_enabled =
dif_bool_to_toggle(bitfield_bit32_read(wakeup_ctrl_reg, filter));
return kDifOk;
}
dif_result_t dif_adc_ctrl_irq_cause_set_enabled(const dif_adc_ctrl_t *adc_ctrl,
uint32_t causes,
dif_toggle_t enabled) {
if (adc_ctrl == NULL || causes > kDifAdcCtrlIrqCauseAll ||
!dif_is_valid_toggle(enabled)) {
return kDifBadArg;
}
uint32_t enabled_causes =
mmio_region_read32(adc_ctrl->base_addr, ADC_CTRL_ADC_INTR_CTL_REG_OFFSET);
if (enabled == kDifToggleDisabled) {
enabled_causes &= ~causes;
} else {
enabled_causes |= causes;
}
mmio_region_write32(adc_ctrl->base_addr, ADC_CTRL_ADC_INTR_CTL_REG_OFFSET,
enabled_causes);
return kDifOk;
}
dif_result_t dif_adc_ctrl_irq_cause_get_enabled(const dif_adc_ctrl_t *adc_ctrl,
uint32_t *enabled_causes) {
if (adc_ctrl == NULL || enabled_causes == NULL) {
return kDifBadArg;
}
*enabled_causes =
mmio_region_read32(adc_ctrl->base_addr, ADC_CTRL_ADC_INTR_CTL_REG_OFFSET);
return kDifOk;
}