public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Andy Shevchenko <andriy.shevchenko@intel.com>
To: Salih Erim <salih.erim@amd.com>
Cc: jic23@kernel.org, robh@kernel.org, krzk+dt@kernel.org,
	conor+dt@kernel.org, git@amd.com, nuno.sa@analog.com,
	andy@kernel.org, dlechner@baylibre.com, michal.simek@amd.com,
	conall.ogriofa@amd.com, erimsalih@gmail.com,
	linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 2/5] iio: adc: add Versal SysMon driver
Date: Mon, 4 May 2026 13:18:49 +0300	[thread overview]
Message-ID: <afhyiW_5embow_hx@ashevche-desk.local> (raw)
In-Reply-To: <20260502111951.538488-3-salih.erim@amd.com>

On Sat, May 02, 2026 at 12:19:48PM +0100, Salih Erim wrote:
> Add the AMD/Xilinx Versal System Monitor (SysMon) IIO driver.
> 
> The driver is split into a bus-agnostic core module
> (versal-sysmon-core) and a memory-mapped I/O platform driver
> (versal-sysmon). The core uses the regmap API so that different
> bus implementations can share the same IIO logic.
> 
> The core provides:
>   - Static temperature channels (current max/min, peak max/min)
>   - Supply voltage channels parsed from DT container nodes
>   - Temperature satellite channels parsed from DT container nodes
>   - read_raw for IIO_CHAN_INFO_RAW and IIO_CHAN_INFO_PROCESSED
>   - read_label using the DT label property
> 
> The MMIO platform driver provides:
>   - Memory-mapped register access via custom regmap callbacks
>   - NPI unlock before every register write (platform management
>     controller may re-lock NPI unpredictably on Versal devices)
> 
> Threshold events, oversampling, and I2C bus support are added in
> subsequent patches.

...

IWYU please:

+ array_size.h

> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/cleanup.h>
> +#include <linux/iio/iio.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/regmap.h>

+ string.h
+ types.h

(and maybe more that I missed).


...

> +#define SYSMON_CHAN_TEMP(_chan, _address, _ext) {		\
> +	.type = IIO_TEMP,					\
> +	.indexed = 1,						\
> +	.address = _address,					\
> +	.channel = _chan,					\

> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\
> +		BIT(IIO_CHAN_INFO_PROCESSED),			\

It's better to use the following style:

	.info_mask_separate =					\
		BIT(IIO_CHAN_INFO_RAW) |			\
		BIT(IIO_CHAN_INFO_PROCESSED),			\


> +	.scan_type = {						\
> +		.sign = 's',					\
> +		.realbits = 15,					\
> +		.storagebits = 16,				\
> +		.endianness = IIO_CPU,				\
> +	},							\
> +	.datasheet_name = _ext,					\
> +}

...

> +static void sysmon_q8p7_to_millicelsius(int raw_data, int *val)
> +{
> +	*val = ((s16)raw_data * SYSMON_MILLI) >> SYSMON_FRACTIONAL_SHIFT;

No casting is needed, just make the type s16 to begin with.

> +}

...

> +static void sysmon_supply_rawtoprocessed(int raw_data, int *val)
> +{
> +	int mantissa, format, exponent;
> +
> +	mantissa = FIELD_GET(SYSMON_MANTISSA_MASK, raw_data);
> +	exponent = SYSMON_SUPPLY_MANTISSA_BITS - FIELD_GET(SYSMON_MODE_MASK, raw_data);
> +	format = FIELD_GET(SYSMON_FMT_MASK, raw_data);
> +	/*
> +	 * When format bit is set the mantissa is two's complement
> +	 * (per hardware spec); sign-extend to int for correct arithmetic.
> +	 */
> +	if (format)
> +		mantissa = (int)(s16)mantissa;

Potential user of FIELD_GET_SIGNED(), but for now just use explicit call to
sign_extend32().

> +	*val = (mantissa * SYSMON_MILLI) >> exponent;
> +}

...

> +static int sysmon_read_raw(struct iio_dev *indio_dev,
> +			   struct iio_chan_spec const *chan, int *val,
> +			   int *val2, long mask)

Use logical split:

static int sysmon_read_raw(struct iio_dev *indio_dev,
			   struct iio_chan_spec const *chan, int *val, int *val2,
			   long mask)

OR (to be strict in 80 limit)

static int sysmon_read_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 regval;
> +	int ret;
> +
> +	if (mask != IIO_CHAN_INFO_RAW && mask != IIO_CHAN_INFO_PROCESSED)
> +		return -EINVAL;
> +
> +	guard(mutex)(&sysmon->lock);
> +
> +	switch (chan->type) {
> +	case IIO_TEMP:
> +		ret = regmap_read(sysmon->regmap, chan->address, &regval);
> +		if (ret)
> +			return ret;
> +		if (mask == IIO_CHAN_INFO_PROCESSED)
> +			sysmon_q8p7_to_millicelsius(regval, val);
> +		else
> +			*val = (int)regval;

Why casting?

> +		return IIO_VAL_INT;
> +
> +	case IIO_VOLTAGE:
> +		ret = regmap_read(sysmon->regmap,
> +				  (chan->address * SYSMON_REG_STRIDE) + SYSMON_SUPPLY_BASE,
> +				  &regval);
> +		if (ret)
> +			return ret;
> +		if (mask == IIO_CHAN_INFO_PROCESSED)
> +			sysmon_supply_rawtoprocessed(regval, val);
> +		else
> +			*val = (int)regval;

Ditto.

> +		return IIO_VAL_INT;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}

...

> +/**
> + * sysmon_parse_fw() - Parse firmware nodes and configure IIO channels.
> + * @indio_dev: IIO device instance
> + * @dev: Parent device
> + *
> + * Reads supply-channels and temperature-channels container nodes from
> + * firmware and builds the IIO channel array. Static temperature channels
> + * are prepended, followed by supply and satellite channels from DT.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev)
> +{
> +	unsigned int idx, temp_chan_idx, volt_chan_idx;
> +	struct fwnode_handle *supply_node, *temp_node;

> +	unsigned int num_supply = 0, num_temp = 0;

Better for maintenance to split these assignments and perform them when they
are really needed.

> +	struct iio_chan_spec *sysmon_channels;
> +	const char *label;
> +	u32 reg;
> +	int ret;

> +	supply_node = device_get_named_child_node(dev, "supply-channels");

Use what cleanup.h provides you,

> +	if (supply_node)
> +		num_supply = fwnode_get_child_node_count(supply_node);
> +
> +	temp_node = device_get_named_child_node(dev, "temperature-channels");
> +	if (temp_node)
> +		num_temp = fwnode_get_child_node_count(temp_node);

As per above.

> +	sysmon_channels = devm_kcalloc(dev,
> +				       ARRAY_SIZE(temp_channels) +
> +				       num_supply + num_temp,

Each of num_supply and num_temp may lead to overflow on 32-bit machines.

> +				       sizeof(*sysmon_channels), GFP_KERNEL);
> +	if (!sysmon_channels) {
> +		ret = -ENOMEM;
> +		goto err_put;
> +	}
> +
> +	/* Static temperature channels first (fixed indices) */
> +	idx = 0;
> +	memcpy(sysmon_channels, temp_channels, sizeof(temp_channels));
> +	idx += ARRAY_SIZE(temp_channels);
> +
> +	/* Supply channels from DT */

> +	if (supply_node) {

Why? Do you expect the below loop to go on this being NULL?

> +		fwnode_for_each_child_node_scoped(supply_node, child) {
> +			ret = fwnode_property_read_u32(child, "reg", &reg);
> +			if (ret < 0)
> +				goto err_put;
> +
> +			if (reg > SYSMON_SUPPLY_IDX_MAX) {
> +				ret = -EINVAL;
> +				dev_err(dev, "supply reg %u exceeds max %u\n",
> +					reg, SYSMON_SUPPLY_IDX_MAX);
> +				goto err_put;
> +			}
> +
> +			ret = fwnode_property_read_string(child, "label",
> +							  &label);
> +			if (ret < 0)
> +				goto err_put;
> +
> +			sysmon_channels[idx++] = (struct iio_chan_spec) {
> +				.type = IIO_VOLTAGE,
> +				.indexed = 1,
> +				.address = reg,
> +				.info_mask_separate =
> +					BIT(IIO_CHAN_INFO_RAW) |
> +					BIT(IIO_CHAN_INFO_PROCESSED),
> +				.scan_type = {
> +					.realbits = 19,
> +					.storagebits = 32,
> +					.endianness = IIO_CPU,
> +					.sign = fwnode_property_read_bool(child,
> +						"bipolar") ? 's' : 'u',
> +				},
> +				.datasheet_name = label,
> +			};
> +		}
> +		fwnode_handle_put(supply_node);
> +		supply_node = NULL;
> +	}
> +
> +	/* Temperature satellite channels from DT */
> +	if (temp_node) {

As per above.

> +		fwnode_for_each_child_node_scoped(temp_node, child) {
> +			ret = fwnode_property_read_u32(child, "reg", &reg);
> +			if (ret < 0)
> +				goto err_put;
> +
> +			if (reg < 1 || reg > SYSMON_TEMP_SAT_MAX) {
> +				ret = -EINVAL;
> +				dev_err(dev, "temp reg %u out of range [1..%u]\n",
> +					reg, SYSMON_TEMP_SAT_MAX);
> +				goto err_put;
> +			}
> +
> +			ret = fwnode_property_read_string(child, "label",
> +							  &label);
> +			if (ret < 0)
> +				goto err_put;
> +
> +			sysmon_channels[idx++] = (struct iio_chan_spec) {
> +				.type = IIO_TEMP,
> +				.indexed = 1,
> +				.address = SYSMON_TEMP_SAT_BASE +
> +					   ((reg - 1) * SYSMON_REG_STRIDE),
> +				.info_mask_separate =
> +					BIT(IIO_CHAN_INFO_RAW) |
> +					BIT(IIO_CHAN_INFO_PROCESSED),
> +				.scan_type = {
> +					.sign = 's',
> +					.realbits = 15,
> +					.storagebits = 16,
> +					.endianness = IIO_CPU,
> +				},
> +				.datasheet_name = label,
> +			};
> +		}
> +		fwnode_handle_put(temp_node);
> +		temp_node = NULL;
> +	}
> +
> +	indio_dev->num_channels = idx;
> +	indio_dev->info = &sysmon_iio_info;
> +
> +	/*
> +	 * Assign per-type sequential channel numbers.
> +	 * IIO sysfs uses type prefix (in_tempN, in_voltageN)
> +	 * so numbers only need to be unique within each type.
> +	 */
> +	temp_chan_idx = 0;
> +	volt_chan_idx = 0;
> +	for (idx = 0; idx < indio_dev->num_channels; idx++) {
> +		if (sysmon_channels[idx].type == IIO_TEMP)
> +			sysmon_channels[idx].channel = temp_chan_idx++;
> +		else
> +			sysmon_channels[idx].channel = volt_chan_idx++;
> +	}
> +
> +	indio_dev->channels = sysmon_channels;
> +
> +	return 0;
> +
> +err_put:
> +	fwnode_handle_put(supply_node);
> +	fwnode_handle_put(temp_node);
> +	return ret;
> +}

...

> +#include <linux/io.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>

As per above, please respect and follow IWYU principle.

...

> +static int sysmon_platform_probe(struct platform_device *pdev)
> +{
> +	struct sysmon_mmio *mmio;
> +	struct regmap *regmap;
> +	int irq;
> +
> +	mmio = devm_kzalloc(&pdev->dev, sizeof(*mmio), GFP_KERNEL);
> +	if (!mmio)
> +		return -ENOMEM;
> +
> +	mmio->base = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(mmio->base))
> +		return PTR_ERR(mmio->base);
> +
> +	regmap = devm_regmap_init(&pdev->dev, NULL, mmio,

Using

	struct device *dev = &pdev->dev;

at the top of the function makes this code neater and probably a single line.

> +				  &sysmon_mmio_regmap_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	irq = platform_get_irq_optional(pdev, 0);
> +
> +	return sysmon_core_probe(&pdev->dev, regmap, irq);
> +}

...

> +#include <linux/iio/iio.h>
> +#include <linux/mutex.h>
> +#include <linux/regmap.h>

Ditto for the header files, use and follow the IWYU principle.

...

> +/* Signed milli scale (MILLI from linux/units.h is unsigned long) */
> +#define SYSMON_MILLI			1000

I think you want a different approach. I already forgot how it's being used,
but there shouldn't be a new (re-)definition just because of this.

-- 
With Best Regards,
Andy Shevchenko



  reply	other threads:[~2026-05-04 10:18 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-02 11:19 [PATCH v2 0/5] iio: adc: add AMD/Xilinx Versal SysMon driver Salih Erim
2026-05-02 11:19 ` [PATCH v2 1/5] dt-bindings: iio: adc: add xlnx,versal-sysmon binding Salih Erim
2026-05-03 14:20   ` Krzysztof Kozlowski
2026-05-03 22:52     ` Salih Erim
2026-05-02 11:19 ` [PATCH v2 2/5] iio: adc: add Versal SysMon driver Salih Erim
2026-05-04 10:18   ` Andy Shevchenko [this message]
2026-05-04 15:50     ` Salih Erim
2026-05-05  7:12       ` Andy Shevchenko
2026-05-04 17:32   ` Jonathan Cameron
2026-05-04 19:26     ` Guenter Roeck
2026-05-02 11:19 ` [PATCH v2 3/5] iio: adc: versal-sysmon: add I2C driver Salih Erim
2026-05-04 10:25   ` Andy Shevchenko
2026-05-02 11:19 ` [PATCH v2 4/5] iio: adc: versal-sysmon: add threshold event support Salih Erim
2026-05-04 10:52   ` Andy Shevchenko
2026-05-04 17:44   ` Jonathan Cameron
2026-05-02 11:19 ` [PATCH v2 5/5] iio: adc: versal-sysmon: add oversampling support Salih Erim

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=afhyiW_5embow_hx@ashevche-desk.local \
    --to=andriy.shevchenko@intel.com \
    --cc=andy@kernel.org \
    --cc=conall.ogriofa@amd.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=erimsalih@gmail.com \
    --cc=git@amd.com \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=michal.simek@amd.com \
    --cc=nuno.sa@analog.com \
    --cc=robh@kernel.org \
    --cc=salih.erim@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox