All of lore.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: 20+ 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-12 11:35       ` Salih Erim
2026-05-16 10:20         ` Jonathan Cameron
2026-05-16 15:04           ` 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-15 15:50     ` Erim, Salih
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.