mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
iio: adc: versal-sysmon: add oversampling support
Add support for reading and writing the oversampling ratio through the IIO oversampling_ratio attribute. The hardware supports averaging 2, 4, 8, or 16 samples, plus a ratio of 1 (no averaging). Temperature and supply channels share oversampling configuration at the type level (all temperature channels share one ratio, all supply channels share another), exposed through info_mask_shared_by_type. The hardware encoding uses sample_count / 2 in a 4-bit field within the CONFIG register. Per-channel averaging enable registers must also be updated to activate or deactivate averaging. Signed-off-by: Salih Erim <salih.erim@amd.com> Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
This commit is contained in:
parent
c7f7eb1063
commit
cbfd04ca56
|
|
@ -28,6 +28,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 };
|
||||
|
||||
/* TEMP hysteresis mode bit in SYSMON_TEMP_EV_CFG */
|
||||
#define SYSMON_TEMP_HYST_MASK BIT(1)
|
||||
|
||||
|
|
@ -42,7 +48,11 @@
|
|||
.address = _address, \
|
||||
.channel = _chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.info_mask_shared_by_type_available = \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.datasheet_name = _name, \
|
||||
}
|
||||
|
||||
|
|
@ -166,6 +176,12 @@ static int sysmon_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
guard(mutex)(&sysmon->lock);
|
||||
|
||||
if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
|
||||
*val = (chan->type == IIO_TEMP) ? sysmon->temp_oversampling :
|
||||
sysmon->supply_oversampling;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
if (mask == IIO_CHAN_INFO_SCALE) {
|
||||
|
|
@ -465,6 +481,132 @@ static int sysmon_write_event_value(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int sysmon_set_avg_enable(struct sysmon *sysmon,
|
||||
u32 base, u32 count, u32 val)
|
||||
{
|
||||
struct regmap *map = sysmon->regmap;
|
||||
int ret;
|
||||
|
||||
for (unsigned int i = 0; i < count; i++) {
|
||||
ret = regmap_write(map, base + i * SYSMON_REG_STRIDE, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sysmon_osr_write_temp(struct sysmon *sysmon, unsigned int val)
|
||||
{
|
||||
/*
|
||||
* HW register encoding is sample_count / 2:
|
||||
* 0=none, 1=2x, 2=4x, 4=8x, 8=16x (not log2-based).
|
||||
*/
|
||||
unsigned int hw_val = val >> 1;
|
||||
unsigned int readback;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
|
||||
SYSMON_CONFIG_TEMP_SAT_OSR,
|
||||
FIELD_PREP(SYSMON_CONFIG_TEMP_SAT_OSR, hw_val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Readback fence: the SysMon CONFIG register resides in the
|
||||
* PMC domain behind the NoC. A posted write may not reach the
|
||||
* hardware before the next MMIO access. Reading the register
|
||||
* back forces the interconnect to complete the write, preventing
|
||||
* a bus hang on the subsequent access.
|
||||
*/
|
||||
regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
|
||||
|
||||
return sysmon_set_avg_enable(sysmon, SYSMON_TEMP_EN_AVG_BASE,
|
||||
SYSMON_TEMP_EN_AVG_COUNT,
|
||||
hw_val ? ~0 : 0);
|
||||
}
|
||||
|
||||
static int sysmon_osr_write_supply(struct sysmon *sysmon, unsigned int val)
|
||||
{
|
||||
/* HW encoding: sample_count / 2 (see sysmon_osr_write_temp) */
|
||||
unsigned int hw_val = val >> 1;
|
||||
unsigned int readback;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(sysmon->regmap, SYSMON_CONFIG,
|
||||
SYSMON_CONFIG_SUPPLY_OSR,
|
||||
FIELD_PREP(SYSMON_CONFIG_SUPPLY_OSR, hw_val));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Readback fence -- see sysmon_osr_write_temp for details */
|
||||
regmap_read(sysmon->regmap, SYSMON_CONFIG, &readback);
|
||||
|
||||
return sysmon_set_avg_enable(sysmon, SYSMON_SUPPLY_EN_AVG_BASE,
|
||||
SYSMON_SUPPLY_EN_AVG_COUNT,
|
||||
hw_val ? ~0 : 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);
|
||||
unsigned int i;
|
||||
int 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);
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
ret = sysmon_osr_write_temp(sysmon, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
sysmon->temp_oversampling = val;
|
||||
} else {
|
||||
ret = sysmon_osr_write_supply(sysmon, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
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)
|
||||
|
|
@ -477,6 +619,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,
|
||||
|
|
@ -768,6 +913,10 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int ir
|
|||
.indexed = 1,
|
||||
.address = reg,
|
||||
.info_mask_separate = 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 = irq > 0 ?
|
||||
sysmon_supply_events : NULL,
|
||||
.num_event_specs = irq > 0 ?
|
||||
|
|
@ -799,7 +948,11 @@ static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int ir
|
|||
.address = SYSMON_TEMP_SAT_BASE +
|
||||
(reg - 1) * SYSMON_REG_STRIDE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_type =
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
.info_mask_shared_by_type_available =
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
.datasheet_name = label,
|
||||
};
|
||||
}
|
||||
|
|
@ -846,6 +999,8 @@ int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap)
|
|||
|
||||
sysmon = iio_priv(indio_dev);
|
||||
sysmon->regmap = regmap;
|
||||
sysmon->temp_oversampling = 1;
|
||||
sysmon->supply_oversampling = 1;
|
||||
|
||||
ret = devm_mutex_init(dev, &sysmon->lock);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -23,11 +23,13 @@ struct regmap;
|
|||
#define SYSMON_IMR 0x0048
|
||||
#define SYSMON_IER 0x004C
|
||||
#define SYSMON_IDR 0x0050
|
||||
#define SYSMON_CONFIG 0x0100
|
||||
#define SYSMON_TEMP_MAX 0x1030
|
||||
#define SYSMON_TEMP_MIN 0x1034
|
||||
#define SYSMON_SUPPLY_BASE 0x1040
|
||||
#define SYSMON_ALARM_FLAG 0x1018
|
||||
#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_SUPPLY_TH_LOW 0x1980
|
||||
|
|
@ -37,6 +39,7 @@ struct regmap;
|
|||
#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 @@ struct regmap;
|
|||
/* ISR/IMR temperature alarm mask (bit 9) */
|
||||
#define SYSMON_TEMP_INTR_MASK BIT(9)
|
||||
|
||||
/* SYSMON_CONFIG: supply oversampling ratio */
|
||||
#define SYSMON_CONFIG_SUPPLY_OSR GENMASK(17, 14)
|
||||
|
||||
/* SYSMON_CONFIG: temperature satellite oversampling ratio */
|
||||
#define SYSMON_CONFIG_TEMP_SAT_OSR 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)
|
||||
|
|
@ -77,6 +90,8 @@ struct regmap;
|
|||
* @temp_mask: temperature interrupt configuration mask
|
||||
* @temp_hysteresis: cached DEVICE_TEMP hysteresis in millicelsius
|
||||
* @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 regmap *regmap;
|
||||
|
|
@ -96,6 +111,8 @@ struct sysmon {
|
|||
unsigned int temp_mask;
|
||||
int temp_hysteresis;
|
||||
struct delayed_work sysmon_unmask_work;
|
||||
unsigned int temp_oversampling;
|
||||
unsigned int supply_oversampling;
|
||||
};
|
||||
|
||||
int devm_versal_sysmon_core_probe(struct device *dev, struct regmap *regmap);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user