Messages in this thread Patch in this message |  | | From | Salih Erim <> | | Subject | [PATCH v2 5/5] iio: adc: versal-sysmon: add oversampling support | | Date | Sat, 2 May 2026 12:19:51 +0100 |
| |
Add configurable oversampling ratio for supply voltage and temperature satellite channels. The hardware supports sample counts of 1 (no averaging), 2, 4, 8, and 16. The userspace-facing values represent actual sample counts; the driver translates to hardware register encoding internally.
Oversampling is shared by type: all supply channels share one ratio and all temperature satellite channels share another. Static temperature channels (device max/min/max_max/min_min) are hardware- computed aggregates and do not participate in oversampling.
When oversampling is changed, the driver updates the per-channel EN_AVG register bitmasks so that all channels in each bank have hardware averaging enabled or disabled to match the oversampling configuration.
Register write errors in the per-channel EN_AVG update path are propagated to userspace.
Signed-off-by: Salih Erim <salih.erim@amd.com> --- Changes in v2: - EN_AVG per-channel bitmask registers written with all-ones instead of boolean 1 when oversampling is enabled - EN_AVG write errors propagated to userspace - Oversampling limited to satellite temp and supply channels; static temp channels do not participate - Oversampling exposes actual sample counts (1,2,4,8,16) to userspace with internal HW register translation - write_raw_get_fmt returns IIO_VAL_INT for oversampling ratio - HW encoding documented (sample_count/2, not log2) - oversampling_avail is const int[] (type match fix) drivers/iio/adc/versal-sysmon-core.c | 137 +++++++++++++++++++++++++++ drivers/iio/adc/versal-sysmon.h | 17 ++++ 2 files changed, 154 insertions(+)
diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c index 857fe21db7a..9c3ce8f8fdf 100644 --- a/drivers/iio/adc/versal-sysmon-core.c +++ b/drivers/iio/adc/versal-sysmon-core.c @@ -19,6 +19,12 @@ #include "versal-sysmon.h" +/* + * Oversampling ratio values exposed to userspace via IIO. + * Actual number of samples averaged: 1=none, 2=2x, 4=4x, 8=8x, 16=16x. + */ +static const int sysmon_oversampling_avail[] = { 1, 2, 4, 8, 16 }; + /* OT and TEMP hysteresis bit positions in SYSMON_TEMP_EV_CFG */ #define SYSMON_OT_HYST_BIT BIT(0) #define SYSMON_TEMP_HYST_BIT BIT(1) @@ -202,6 +208,12 @@ static int sysmon_read_raw(struct iio_dev *indio_dev, unsigned int regval; int ret; + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) { + *val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling : + sysmon->supply_oversampling; + return IIO_VAL_INT; + } + if (mask != IIO_CHAN_INFO_RAW && mask != IIO_CHAN_INFO_PROCESSED) return -EINVAL; @@ -438,6 +450,118 @@ static int sysmon_write_event_value(struct iio_dev *indio_dev, return -EINVAL; } +static int sysmon_set_avg_enable(struct sysmon *sysmon, + u32 base, u32 count, u32 val) +{ + unsigned int i; + int ret; + + for (i = 0; i < count; i++) { + ret = regmap_write(sysmon->regmap, + base + (i * SYSMON_REG_STRIDE), val); + if (ret) + return ret; + } + + return 0; +} + +static int sysmon_osr_write(struct sysmon *sysmon, int channel_type, int val) +{ + /* + * HW register encoding is sample_count / 2: + * 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based). + */ + int hw_val = val >> 1; + int ret; + + if (channel_type == IIO_TEMP) { + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_TEMP_SAT_CONFIG_MASK, + FIELD_PREP(SYSMON_TEMP_SAT_CONFIG_MASK, + hw_val)); + if (ret) + return ret; + ret = sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE, + SYSMON_TEMP_EN_AVG_COUNT, + hw_val ? ~0U : 0); + if (ret) + return ret; + } else if (channel_type == IIO_VOLTAGE) { + ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG, + SYSMON_SUPPLY_CONFIG_MASK, + FIELD_PREP(SYSMON_SUPPLY_CONFIG_MASK, + hw_val)); + if (ret) + return ret; + ret = sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE, + SYSMON_SUPPLY_EN_AVG_COUNT, + hw_val ? ~0U : 0); + if (ret) + return ret; + } else { + return -EINVAL; + } + + return 0; +} + +static int sysmon_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sysmon *sysmon = iio_priv(indio_dev); + int i, ret; + + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sysmon_oversampling_avail); i++) { + if (val == sysmon_oversampling_avail[i]) + break; + } + if (i == ARRAY_SIZE(sysmon_oversampling_avail)) + return -EINVAL; + + guard(mutex)(&sysmon->lock); + + ret = sysmon_osr_write(sysmon, chan->type, val); + if (ret) + return ret; + + if (chan->type == IIO_TEMP) + sysmon->temp_oversampling = val; + else + sysmon->supply_oversampling = val; + + return 0; +} + +static int sysmon_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return IIO_VAL_INT; + + return -EINVAL; +} + +static int sysmon_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, + int *length, long mask) +{ + if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) + return -EINVAL; + + *vals = sysmon_oversampling_avail; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(sysmon_oversampling_avail); + + return IIO_AVAIL_LIST; +} + static int sysmon_read_label(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, char *label) @@ -450,6 +574,9 @@ static int sysmon_read_label(struct iio_dev *indio_dev, static const struct iio_info sysmon_iio_info = { .read_raw = sysmon_read_raw, + .write_raw = sysmon_write_raw, + .write_raw_get_fmt = sysmon_write_raw_get_fmt, + .read_avail = sysmon_read_avail, .read_label = sysmon_read_label, .read_event_config = sysmon_read_event_config, .write_event_config = sysmon_write_event_config, @@ -715,6 +842,10 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev) .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .event_spec = has_events ? sysmon_supply_events : NULL, .num_event_specs = has_events ? @@ -760,6 +891,10 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev) .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_type = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), .scan_type = { .sign = 's', .realbits = 15, @@ -823,6 +958,8 @@ int sysmon_core_probe(struct device *dev, struct regmap *regmap, int irq) sysmon->indio_dev = indio_dev; sysmon->regmap = regmap; sysmon->irq = irq; + sysmon->temp_oversampling = 1; + sysmon->supply_oversampling = 1; ret = devm_mutex_init(dev, &sysmon->lock); if (ret) diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h index 4f20173de77..9fa7a7486de 100644 --- a/drivers/iio/adc/versal-sysmon.h +++ b/drivers/iio/adc/versal-sysmon.h @@ -21,11 +21,13 @@ #define SYSMON_IMR 0x0048 #define SYSMON_IER 0x004C #define SYSMON_IDR 0x0050 +#define SYSMON_CONFIG 0x0100 #define SYSMON_ALARM_FLAG 0x1018 #define SYSMON_TEMP_MAX 0x1030 #define SYSMON_TEMP_MIN 0x1034 #define SYSMON_SUPPLY_BASE 0x1040 #define SYSMON_ALARM_REG 0x1940 +#define SYSMON_SUPPLY_EN_AVG_BASE 0x1958 #define SYSMON_TEMP_TH_LOW 0x1970 #define SYSMON_TEMP_TH_UP 0x1974 #define SYSMON_OT_TH_LOW 0x1978 @@ -37,6 +39,7 @@ #define SYSMON_TEMP_MAX_MAX 0x1F90 #define SYSMON_STATUS_RESET 0x1F94 #define SYSMON_TEMP_SAT_BASE 0x1FAC +#define SYSMON_TEMP_EN_AVG_BASE 0x24B4 #define SYSMON_MAX_REG 0x24C0 /* NPI unlock value written to SYSMON_NPI_LOCK */ @@ -53,6 +56,16 @@ /* ISR/IMR temperature and OT alarm mask (bits 9:8) */ #define SYSMON_TEMP_INTR_MASK GENMASK(9, 8) +/* Config register: supply oversampling field (bits 17:14) */ +#define SYSMON_SUPPLY_CONFIG_MASK GENMASK(17, 14) + +/* Config register: temp satellite oversampling field (bits 27:24) */ +#define SYSMON_TEMP_SAT_CONFIG_MASK GENMASK(27, 24) + +/* Per-channel averaging enable register counts */ +#define SYSMON_SUPPLY_EN_AVG_COUNT 5 +#define SYSMON_TEMP_EN_AVG_COUNT 2 + /* Supply voltage conversion register fields */ #define SYSMON_MANTISSA_MASK GENMASK(15, 0) #define SYSMON_FMT_MASK BIT(16) @@ -85,6 +98,8 @@ * @masked_temp: currently masked temperature alarm bits * @temp_mask: temperature interrupt configuration mask * @sysmon_unmask_work: re-enables events after alarm condition clears + * @temp_oversampling: current temp oversampling ratio + * @supply_oversampling: current supply oversampling ratio */ struct sysmon { struct device *dev; @@ -98,6 +113,8 @@ struct sysmon { unsigned int masked_temp; unsigned int temp_mask; struct delayed_work sysmon_unmask_work; + unsigned int temp_oversampling; + unsigned int supply_oversampling; }; int sysmon_core_probe(struct device *dev, struct regmap *regmap, int irq); -- 2.48.1
|  |