|  | // 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_pattgen.h" | 
|  |  | 
|  | #include "sw/device/lib/base/bitfield.h" | 
|  | #include "sw/device/lib/dif/dif_base.h" | 
|  |  | 
|  | #include "pattgen_regs.h"  // Generated. | 
|  |  | 
|  | dif_result_t dif_pattgen_configure_channel( | 
|  | const dif_pattgen_t *pattgen, dif_pattgen_channel_t channel, | 
|  | dif_pattgen_channel_config_t config) { | 
|  | if (pattgen == NULL || config.polarity >= kDifPattgenPolarityCount || | 
|  | config.seed_pattern_length == 0 || config.seed_pattern_length > 64 || | 
|  | config.num_pattern_repetitions == 0 || | 
|  | config.num_pattern_repetitions > 1024) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | bitfield_bit32_index_t enable_bit_idx; | 
|  | bitfield_bit32_index_t polarity_bit_idx; | 
|  | ptrdiff_t clock_divisor_reg_offset; | 
|  | ptrdiff_t seed_lower_reg_offset; | 
|  | ptrdiff_t seed_upper_reg_offset; | 
|  | bitfield_field32_t seed_pattern_length_field; | 
|  | bitfield_field32_t num_pattern_repetitions_field; | 
|  |  | 
|  | #define DIF_PATTGEN_CHANNEL_CONFIG_CASE_(channel_)                          \ | 
|  | case kDifPattgenChannel##channel_:                                        \ | 
|  | enable_bit_idx = PATTGEN_CTRL_ENABLE_CH##channel_##_BIT;                \ | 
|  | polarity_bit_idx = PATTGEN_CTRL_POLARITY_CH##channel_##_BIT;            \ | 
|  | clock_divisor_reg_offset = PATTGEN_PREDIV_CH##channel_##_REG_OFFSET;    \ | 
|  | seed_lower_reg_offset = PATTGEN_DATA_CH##channel_##_0_REG_OFFSET;       \ | 
|  | seed_upper_reg_offset = PATTGEN_DATA_CH##channel_##_1_REG_OFFSET;       \ | 
|  | seed_pattern_length_field = PATTGEN_SIZE_LEN_CH##channel_##_FIELD;      \ | 
|  | num_pattern_repetitions_field = PATTGEN_SIZE_REPS_CH##channel_##_FIELD; \ | 
|  | break; | 
|  |  | 
|  | switch (channel) { | 
|  | DIF_PATTGEN_CHANNEL_LIST(DIF_PATTGEN_CHANNEL_CONFIG_CASE_) | 
|  | default: | 
|  | return kDifBadArg; | 
|  | } | 
|  | #undef DIF_PATTGEN_CHANNEL_CONFIG_CASE_ | 
|  |  | 
|  | uint32_t ctrl_reg = | 
|  | mmio_region_read32(pattgen->base_addr, PATTGEN_CTRL_REG_OFFSET); | 
|  |  | 
|  | // Check if channel is enabled. We cannot configure the channel if so. | 
|  | if (bitfield_bit32_read(ctrl_reg, enable_bit_idx)) { | 
|  | return kDifError; | 
|  | } | 
|  |  | 
|  | // Set the polarity. | 
|  | ctrl_reg = bitfield_bit32_write(ctrl_reg, polarity_bit_idx, config.polarity); | 
|  | mmio_region_write32(pattgen->base_addr, PATTGEN_CTRL_REG_OFFSET, ctrl_reg); | 
|  |  | 
|  | // Set the clock divisor. | 
|  | mmio_region_write32(pattgen->base_addr, clock_divisor_reg_offset, | 
|  | config.clock_divisor); | 
|  |  | 
|  | // Write the seed data. | 
|  | mmio_region_write32(pattgen->base_addr, seed_lower_reg_offset, | 
|  | config.seed_pattern_lower_word); | 
|  | if (config.seed_pattern_length > 31) { | 
|  | mmio_region_write32(pattgen->base_addr, seed_upper_reg_offset, | 
|  | config.seed_pattern_upper_word); | 
|  | } | 
|  |  | 
|  | // Set the size and repetition values. | 
|  | uint32_t size_reg = | 
|  | mmio_region_read32(pattgen->base_addr, PATTGEN_SIZE_REG_OFFSET); | 
|  | size_reg = bitfield_field32_write(size_reg, seed_pattern_length_field, | 
|  | config.seed_pattern_length - 1); | 
|  | size_reg = bitfield_field32_write(size_reg, num_pattern_repetitions_field, | 
|  | config.num_pattern_repetitions - 1); | 
|  | mmio_region_write32(pattgen->base_addr, PATTGEN_SIZE_REG_OFFSET, size_reg); | 
|  |  | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | dif_result_t dif_pattgen_channel_set_enabled(const dif_pattgen_t *pattgen, | 
|  | dif_pattgen_channel_t channel, | 
|  | dif_toggle_t enabled) { | 
|  | if (pattgen == NULL || !dif_is_valid_toggle(enabled)) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | bitfield_bit32_index_t enable_bit_idx; | 
|  |  | 
|  | #define DIF_PATTGEN_CHANNEL_SET_ENABLED_CASE_(channel_)      \ | 
|  | case kDifPattgenChannel##channel_:                         \ | 
|  | enable_bit_idx = PATTGEN_CTRL_ENABLE_CH##channel_##_BIT; \ | 
|  | break; | 
|  |  | 
|  | switch (channel) { | 
|  | DIF_PATTGEN_CHANNEL_LIST(DIF_PATTGEN_CHANNEL_SET_ENABLED_CASE_) | 
|  | default: | 
|  | return kDifBadArg; | 
|  | } | 
|  | #undef DIF_PATTGEN_CHANNEL_SET_ENABLED_CASE_ | 
|  |  | 
|  | uint32_t ctrl_reg = | 
|  | mmio_region_read32(pattgen->base_addr, PATTGEN_CTRL_REG_OFFSET); | 
|  | ctrl_reg = bitfield_bit32_write(ctrl_reg, enable_bit_idx, | 
|  | dif_toggle_to_bool(enabled)); | 
|  | mmio_region_write32(pattgen->base_addr, PATTGEN_CTRL_REG_OFFSET, ctrl_reg); | 
|  |  | 
|  | return kDifOk; | 
|  | } | 
|  |  | 
|  | dif_result_t dif_pattgen_channel_get_enabled(const dif_pattgen_t *pattgen, | 
|  | dif_pattgen_channel_t channel, | 
|  | dif_toggle_t *is_enabled) { | 
|  | if (pattgen == NULL || is_enabled == NULL) { | 
|  | return kDifBadArg; | 
|  | } | 
|  |  | 
|  | bitfield_bit32_index_t enable_bit_idx; | 
|  |  | 
|  | #define DIF_PATTGEN_CHANNEL_SET_ENABLED_CASE_(channel_)      \ | 
|  | case kDifPattgenChannel##channel_:                         \ | 
|  | enable_bit_idx = PATTGEN_CTRL_ENABLE_CH##channel_##_BIT; \ | 
|  | break; | 
|  |  | 
|  | switch (channel) { | 
|  | DIF_PATTGEN_CHANNEL_LIST(DIF_PATTGEN_CHANNEL_SET_ENABLED_CASE_) | 
|  | default: | 
|  | return kDifBadArg; | 
|  | } | 
|  | #undef DIF_PATTGEN_CHANNEL_SET_ENABLED_CASE_ | 
|  |  | 
|  | uint32_t ctrl_reg = | 
|  | mmio_region_read32(pattgen->base_addr, PATTGEN_CTRL_REG_OFFSET); | 
|  | *is_enabled = | 
|  | dif_bool_to_toggle(bitfield_bit32_read(ctrl_reg, enable_bit_idx)); | 
|  |  | 
|  | return kDifOk; | 
|  | } |