[PATCH 3/6] iio: adc: versal-sysmon: Support AI Engine thermal monitoring

Michal Simek
2025-09-05T08:42:27Z

From: Anish Kadamathikuttiyil Karthikeyan Pillai <anish.kadamathikuttiyil-karthikeyan-pillai@amd.com>

To enhance the thermal monitoring of AI Engine, the sysmon satellites
close to it are exposed as IIO channels.

The exposed IIO channel are mapped to allow consumer drivers to access
specific sensor data through the IIO framework.

Signed-off-by: Anish Kadamathikuttiyil Karthikeyan Pillai <anish.kadamathikuttiyil-karthikeyan-pillai@amd.com>
Signed-off-by: Michal Simek <michal.simek@amd.com>
---

drivers/iio/adc/versal-sysmon-core.c | 135 ++++++++++++++++++---------
drivers/iio/adc/versal-sysmon.c | 35 ++++++-
drivers/iio/adc/versal-sysmon.h | 3 +
3 files changed, 127 insertions(+), 46 deletions(-)

diff --git a/drivers/iio/adc/versal-sysmon-core.c b/drivers/iio/adc/versal-sysmon-core.c
index 21b6a413dccb..bd293ff7d176 100644
--- a/drivers/iio/adc/versal-sysmon-core.c
+++ b/drivers/iio/adc/versal-sysmon-core.c
@@ -272,7 +272,10 @@ static int sysmon_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_TEMP:
- offset = sysmon_temp_offset(chan->address);
+ if (chan->channel != AIE_TEMP_CH)
+ offset = sysmon_temp_offset(chan->address);
+ else
+ offset = chan->address;
*val = sysmon->temp_read(sysmon, offset);
*val2 = 0;
ret = IIO_VAL_INT;
@@ -295,7 +298,10 @@ static int sysmon_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_TEMP:
/* In Deg C */
- offset = sysmon_temp_offset(chan->address);
+ if (chan->channel != AIE_TEMP_CH)
+ offset = sysmon_temp_offset(chan->address);
+ else
+ offset = chan->address;
regval = sysmon->temp_read(sysmon, offset);
if (!sysmon->hbm_slr)
sysmon_q8p7_to_millicelsius(regval, val, val2);
@@ -1202,8 +1208,10 @@ int sysmon_parse_dt(struct iio_dev *indio_dev, struct device *dev)
struct iio_chan_spec *sysmon_channels;
struct device_node *child_node = NULL, *np = dev->of_node;
int ret, i = 0;
+ int aie_idx = 0;
u32 num_supply_chan = 0;
u32 reg = 0, num_temp_chan = 0;
+ u32 num_aie_temp_chan = 0;
const char *name;
u32 chan_size = sizeof(struct iio_chan_spec);
u32 temp_chan_size;
@@ -1214,6 +1222,11 @@ int sysmon_parse_dt(struct iio_dev *indio_dev, struct device *dev)
return ret;

sysmon->num_supply_chan = num_supply_chan;
+ ret = of_property_read_u32(np, "xlnx,numaiechannels", &num_aie_temp_chan);
+ if (ret < 0)
+ num_aie_temp_chan = 0;
+
+ sysmon->num_aie_temp_chan = num_aie_temp_chan;

INIT_LIST_HEAD(&sysmon->region_list);

@@ -1233,69 +1246,101 @@ int sysmon_parse_dt(struct iio_dev *indio_dev, struct device *dev)
}

sysmon_channels = devm_kzalloc(dev, (chan_size * num_supply_chan) +
+ (chan_size * num_aie_temp_chan) +
temp_chan_size,
GFP_KERNEL);
if (!sysmon_channels)
return -ENOMEM;

+ /* Configure the dynamic channels */
for_each_child_of_node(np, child_node) {
- ret = of_property_read_u32(child_node, "reg", &reg);
- if (ret < 0) {
- of_node_put(child_node);
- return ret;
- }
+ if (of_property_present(child_node, "xlnx,aie-temp")) {
+ ret = of_property_read_u32(child_node, "reg", &reg);
+ if (ret < 0) {
+ of_node_put(child_node);
+ return ret;
+ }
+ ret = of_property_read_string(child_node, "xlnx,name", &name);
+ if (ret < 0) {
+ of_node_put(child_node);
+ return ret;
+ }
+ sysmon_channels[num_supply_chan + aie_idx].type = IIO_TEMP;
+ sysmon_channels[num_supply_chan + aie_idx].indexed = 1;
+ sysmon_channels[num_supply_chan + aie_idx].address =
+ SYSMON_NODE_OFFSET + ((reg - 1) * 4);
+ sysmon_channels[num_supply_chan + aie_idx].channel = AIE_TEMP_CH;
+ sysmon_channels[num_supply_chan + aie_idx].info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED);
+ sysmon_channels[num_supply_chan + aie_idx].scan_index = aie_idx;
+ sysmon_channels[num_supply_chan + aie_idx].scan_type.realbits = 15;
+ sysmon_channels[num_supply_chan + aie_idx].scan_type.storagebits =
+ 16;
+ sysmon_channels[num_supply_chan + aie_idx].scan_type.endianness =
+ IIO_CPU;
+ sysmon_channels[num_supply_chan + aie_idx].scan_type.sign = 's';
+ sysmon_channels[num_supply_chan + aie_idx].extend_name = name;
+ sysmon_channels[num_supply_chan + aie_idx].datasheet_name = name;
+ aie_idx++;
+ } else {
+ ret = of_property_read_u32(child_node, "reg", &reg);
+ if (ret < 0) {
+ of_node_put(child_node);
+ return ret;
+ }

- ret = of_property_read_string(child_node, "xlnx,name", &name);
- if (ret < 0) {
- of_node_put(child_node);
- return ret;
- }
+ ret = of_property_read_string(child_node, "xlnx,name", &name);
+ if (ret < 0) {
+ of_node_put(child_node);
+ return ret;
+ }

- sysmon_channels[i].type = IIO_VOLTAGE;
- sysmon_channels[i].indexed = 1;
- sysmon_channels[i].address = reg;
- sysmon_channels[i].channel = reg;
- sysmon_channels[i].info_mask_separate =
- BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED);
- sysmon_channels[i].info_mask_shared_by_type =
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
- sysmon_channels[i].info_mask_shared_by_type_available =
- BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
-
- sysmon_channels[i].event_spec = sysmon_supply_events;
- sysmon_channels[i].num_event_specs = ARRAY_SIZE(sysmon_supply_events);
-
- sysmon_channels[i].scan_index = i;
- sysmon_channels[i].scan_type.realbits = 19;
- sysmon_channels[i].scan_type.storagebits = 32;
-
- sysmon_channels[i].scan_type.endianness = IIO_CPU;
- sysmon_channels[i].extend_name = name;
-
- if (of_property_read_bool(child_node, "xlnx,bipolar"))
- sysmon_channels[i].scan_type.sign = 's';
- else
- sysmon_channels[i].scan_type.sign = 'u';
+ sysmon_channels[i].type = IIO_VOLTAGE;
+ sysmon_channels[i].indexed = 1;
+ sysmon_channels[i].address = reg;
+ sysmon_channels[i].channel = reg;
+ sysmon_channels[i].info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED);
+ sysmon_channels[i].info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ sysmon_channels[i].info_mask_shared_by_type_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+
+ sysmon_channels[i].event_spec = sysmon_supply_events;
+ sysmon_channels[i].num_event_specs = ARRAY_SIZE(sysmon_supply_events);
+
+ sysmon_channels[i].scan_index = i;
+ sysmon_channels[i].scan_type.realbits = 19;
+ sysmon_channels[i].scan_type.storagebits = 32;
+
+ sysmon_channels[i].scan_type.endianness = IIO_CPU;
+ sysmon_channels[i].extend_name = name;
+
+ if (of_property_read_bool(child_node, "xlnx,bipolar"))
+ sysmon_channels[i].scan_type.sign = 's';
+ else
+ sysmon_channels[i].scan_type.sign = 'u';

- i++;
+ i++;
+ }
}

/* Append static temperature channels to the channel list */
- indio_dev->num_channels = num_supply_chan;
+ indio_dev->num_channels = num_supply_chan + num_aie_temp_chan;

if (sysmon->master_slr) {
- memcpy(sysmon_channels + num_supply_chan, temp_channels,
- sizeof(temp_channels));
+ memcpy(sysmon_channels + num_supply_chan + num_aie_temp_chan,
+ temp_channels, sizeof(temp_channels));
indio_dev->num_channels += ARRAY_SIZE(temp_channels);
}

if (sysmon->hbm_slr) {
- memcpy(sysmon_channels + num_supply_chan, temp_hbm_channels,
- sizeof(temp_hbm_channels));
+ memcpy(sysmon_channels + num_supply_chan + num_aie_temp_chan,
+ temp_hbm_channels, sizeof(temp_hbm_channels));
indio_dev->num_channels += num_temp_chan;
} else {
- memcpy(sysmon_channels + num_supply_chan + num_temp_chan,
- temp_events, sizeof(temp_events));
+ memcpy(sysmon_channels + num_supply_chan + num_aie_temp_chan +
+ num_temp_chan, temp_events, sizeof(temp_events));
indio_dev->num_channels += ARRAY_SIZE(temp_events);
}

diff --git a/drivers/iio/adc/versal-sysmon.c b/drivers/iio/adc/versal-sysmon.c
index 79be5111b56c..b839dfdef712 100644
--- a/drivers/iio/adc/versal-sysmon.c
+++ b/drivers/iio/adc/versal-sysmon.c
@@ -108,8 +108,13 @@ static int sysmon_probe(struct platform_device *pdev)
struct sysmon *sysmon, *temp_sysmon;
struct iio_dev *indio_dev;
struct resource *mem;
+ struct iio_map *all_maps;
+ const struct iio_chan_spec *chan;
bool exist = false;
int ret;
+ int maps_size = 0;
+ int maps_idx = 0;
+ int dyn_idx = 0;

indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sysmon));
if (!indio_dev)
@@ -183,8 +188,36 @@ static int sysmon_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);

if (sysmon->master_slr) {
+ /* Combine static and dynamic iio maps and register */
+ maps_size = ARRAY_SIZE(sysmon_therm_static_maps) - 1
+ + sysmon->num_aie_temp_chan;
+ all_maps = devm_kzalloc(&pdev->dev,
+ sizeof(*all_maps) * (maps_size), GFP_KERNEL);
+ if (!all_maps)
+ return -ENOMEM;
+
+ for (maps_idx = 0;
+ maps_idx < ARRAY_SIZE(sysmon_therm_static_maps) - 1;
+ maps_idx++)
+ all_maps[maps_idx] =
+ sysmon_therm_static_maps[maps_idx];
+
+ for (dyn_idx = 0; dyn_idx < indio_dev->num_channels && maps_idx < maps_size;
+ dyn_idx++) {
+ chan = &indio_dev->channels[dyn_idx];
+
+ if (chan->channel == AIE_TEMP_CH) {
+ all_maps[maps_idx].adc_channel_label =
+ chan->extend_name;
+ all_maps[maps_idx].consumer_dev_name =
+ "versal-thermal";
+ all_maps[maps_idx].consumer_channel =
+ chan->extend_name;
+ maps_idx++;
+ }
+ }
ret = devm_iio_map_array_register(&pdev->dev, indio_dev,
- sysmon_therm_static_maps);
+ all_maps);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret, "IIO map register failed\n");
}
diff --git a/drivers/iio/adc/versal-sysmon.h b/drivers/iio/adc/versal-sysmon.h
index ba47b6d32f4d..f49a4e60de65 100644
--- a/drivers/iio/adc/versal-sysmon.h
+++ b/drivers/iio/adc/versal-sysmon.h
@@ -45,6 +45,7 @@
#define TEMP_EVENT 164
#define OT_EVENT 165
#define TEMP_HBM 166
+#define AIE_TEMP_CH 200

/* Register Unlock Code */
#define NPI_UNLOCK 0xF9E8D7C6
@@ -215,6 +216,7 @@ static const unsigned int sysmon_oversampling_avail[5] = {
* @oversampling_avail: list of available overampling ratios
* @oversampling_num: total number of available oversampling ratios
* @num_supply_chan: number of supply channels that are enabled
+ * @num_aie_temp_chan: number of aie temp channels that are enabled
* @supply_avg_en_attrs: dynamic array of supply averaging enable attributes
* @temp_avg_en_attrs: dynamic array of temp. sat. averaging enable attributes
* @avg_attrs: dynamic array of pointers to averaging attributes
@@ -248,6 +250,7 @@ struct sysmon {
const unsigned int *oversampling_avail;
unsigned int oversampling_num;
unsigned int num_supply_chan;
+ unsigned int num_aie_temp_chan;
struct iio_dev_attr *supply_avg_en_attrs;
struct iio_dev_attr *temp_avg_en_attrs;
struct attribute **avg_attrs;
--
2.43.0