From nobody Tue Oct 7 03:46:26 2025 Received: from out-171.mta1.migadu.com (out-171.mta1.migadu.com [95.215.58.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 13CC91B4248 for ; Tue, 15 Jul 2025 01:20:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542437; cv=none; b=C0pwCrP9llViK0aMZtQZln/KVUFPXFoN9D9WV0oLIYN0RVLZRvsMKmXfedUYRezQlEZPpYxEGXu8YX8n+B0ZRImKxulhHW7G1yrReAOVKHrOHd52GhQRkYKjlam6kRuqVhQ9HqdRAPhkEi74Of59sZ/hlBS2N8hEMKN+H/BAmgs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542437; c=relaxed/simple; bh=+7Okd5KpUl8CR54V8Zek7raTLxtgZ33PvQMeh1OC1AU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=dx+HFx/l/LikLHmDz/loxp04jgGpjVRvP8IQ/b9dybfegyTHWnqZEJibgcnFMJTWZu2Q2XCbO4pSCn7PGoUR7JcdH9m2lI/lKxTlG7ZCNOzArb3Q6RzU7dxAgQgi6Si5039ZINP+pfUoVkeWi4sp+BdxrsYasbYBAN50EcWaOmk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=wBAHA8ob; arc=none smtp.client-ip=95.215.58.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="wBAHA8ob" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1752542434; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nC77loZfSLoZJvF86FxWCFT8RwnQ5h925nNSe/Ekbps=; b=wBAHA8ob23eW73Y53FTGMwNmEMvdEIGPTKCW0YZPkGylInb3Xdfp2hmsWeDcg6AURYBB5X hx+IqrjGgZxA+WJle+0FK5EZbzGEKMlqfh8OaFmLqI7FUCszJPPEGkD956/nO0RI78Z98y 58NG8VvxBAdcR23ZVDYsjOhByy09QI8= From: Sean Anderson To: Jonathan Cameron , Jean Delvare , Guenter Roeck , linux-iio@vger.kernel.org, linux-hwmon@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Nuno=20S=C3=A1?= , linux-kernel@vger.kernel.org, David Lechner , Sean Anderson Subject: [PATCH 1/7] math64: Add div64_s64_rem Date: Mon, 14 Jul 2025 21:20:17 -0400 Message-Id: <20250715012023.2050178-2-sean.anderson@linux.dev> In-Reply-To: <20250715012023.2050178-1-sean.anderson@linux.dev> References: <20250715012023.2050178-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add a function to do signed 64-bit division with remainder. This is implemented using div64_u64_rem in the same way that div_s64_rem is implemented using div_u64_rem. Signed-off-by: Sean Anderson --- include/linux/math64.h | 18 ++++++++++++++++++ lib/math/div64.c | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/include/linux/math64.h b/include/linux/math64.h index 6aaccc1626ab..0a414446af89 100644 --- a/include/linux/math64.h +++ b/include/linux/math64.h @@ -57,6 +57,20 @@ static inline u64 div64_u64_rem(u64 dividend, u64 diviso= r, u64 *remainder) return dividend / divisor; } =20 +/** + * div64_s64_rem - signed 64bit divide with 64bit divisor and remainder + * @dividend: signed 64bit dividend + * @divisor: signed 64bit divisor + * @remainder: pointer to signed 64bit remainder + * + * Return: sets ``*remainder``, then returns dividend / divisor + */ +static inline s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + *remainder =3D dividend % divisor; + return dividend / divisor; +} + /** * div64_u64 - unsigned 64bit divide with 64bit divisor * @dividend: unsigned 64bit dividend @@ -102,6 +116,10 @@ extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 = *remainder); extern u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder); #endif =20 +#ifndef div64_s64_rem +s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder); +#endif + #ifndef div64_u64 extern u64 div64_u64(u64 dividend, u64 divisor); #endif diff --git a/lib/math/div64.c b/lib/math/div64.c index 5faa29208bdb..ccef0db85681 100644 --- a/lib/math/div64.c +++ b/lib/math/div64.c @@ -124,6 +124,26 @@ u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *rema= inder) EXPORT_SYMBOL(div64_u64_rem); #endif =20 +#ifndef div_s64_rem +s64 div64_s64_rem(s64 dividend, s64 divisor, s64 *remainder) +{ + u64 quotient; + + if (dividend < 0) { + quotient =3D div64_u64_rem(-dividend, abs(divisor), (u64 *)remainder); + *remainder =3D -*remainder; + if (divisor > 0) + quotient =3D -quotient; + } else { + quotient =3D div64_u64_rem(dividend, abs(divisor), (u64 *)remainder); + if (divisor < 0) + quotient =3D -quotient; + } + return quotient; +} +EXPORT_SYMBOL(div64_s64_rem); +#endif + /* * div64_u64 - unsigned 64bit divide with 64bit divisor * @dividend: 64bit dividend --=20 2.35.1.1320.gc452695387.dirty From nobody Tue Oct 7 03:46:26 2025 Received: from out-179.mta1.migadu.com (out-179.mta1.migadu.com [95.215.58.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F88D1D516C; Tue, 15 Jul 2025 01:20:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542440; cv=none; b=uVWm5xcADkQBT+Ho2Zja6VogI9lL8VkvZLp5zOMsr1FWjIxB1IF6IU5GrGntCdzibAD35ECTzDFpvZ6NKis8l+uoNspGyx3Ag3JqNfNFGTahFQc1O8IPTVjBsnxFGW6coK/4RnWv9N7jrKDc31JxngA+lG1ACU2/ZHm4vjm0owM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542440; c=relaxed/simple; bh=c+8O40vCS6Zv4jYYKHa9QC5l4utqpyySh0FTEzw+beA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aVYUzA56jj31cIbKrMFaFnX+RoLyVSpsFCyIIg1NnX15Cq/1cG09SeFPYQgzUWKlbB4rK7MuaGoY6YV4DZBj3IEiYnbJmprRV0eumM9q46CO9DVr/hUjWxkmf1aH8NYoZM7nCBV2Ukgy1tflY6nlPZgVuNtxfngjEYtjXZXe9sc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=hmDEIrVI; arc=none smtp.client-ip=95.215.58.179 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="hmDEIrVI" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1752542435; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=a/vDo0Oi4Q6CBGMCM8qIZnQq8tqjyz5IEomz+8B5kek=; b=hmDEIrVI5iLojc3pisw9F/O/Jqdp6NlUtdmaVHSC4DHPJN1if1GxBXxuFy5hzzEPsY9BDn nycYiV3suIjirC1m4cfp+x9lKhY6TzW67zKIuw5WRaFWhdHMuVmbggRZKac0YbDhMQ3WDf a3XnRvTVQIx0/tNYz9Q4l+densbzuFA= From: Sean Anderson To: Jonathan Cameron , Jean Delvare , Guenter Roeck , linux-iio@vger.kernel.org, linux-hwmon@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Nuno=20S=C3=A1?= , linux-kernel@vger.kernel.org, David Lechner , Sean Anderson Subject: [PATCH 2/7] iio: inkern: Add API for reading/writing events Date: Mon, 14 Jul 2025 21:20:18 -0400 Message-Id: <20250715012023.2050178-3-sean.anderson@linux.dev> In-Reply-To: <20250715012023.2050178-1-sean.anderson@linux.dev> References: <20250715012023.2050178-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add an in-kernel API for reading/writing event properties. Like the raw-to-processed conversion, with processed-to-raw we only convert the integer part, introducing some round-off error. A common case is for other drivers to re-expose IIO events as sysfs properties with a different API. To help out with this, iio_event_mode returns the appropriate mode. It can also be used to test for existence if the consumer doesn't care about read/write capability. Signed-off-by: Sean Anderson --- drivers/iio/inkern.c | 198 +++++++++++++++++++++++++++++++++++ include/linux/iio/consumer.h | 56 ++++++++++ 2 files changed, 254 insertions(+) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index c174ebb7d5e6..d3bbd2444fb5 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -1028,3 +1028,201 @@ ssize_t iio_read_channel_label(struct iio_channel *= chan, char *buf) return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } EXPORT_SYMBOL_GPL(iio_read_channel_label); + +static bool iio_event_exists(struct iio_channel *channel, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info) +{ + struct iio_chan_spec const *chan =3D channel->channel; + int i; + + if (!channel->indio_dev->info) + return false; + + for (i =3D 0; i < chan->num_event_specs; i++) { + if (chan->event_spec[i].type !=3D type) + continue; + if (chan->event_spec[i].dir !=3D dir) + continue; + if (chan->event_spec[i].mask_separate & BIT(info)) + return true; + } + + return false; +} + +umode_t iio_event_mode(struct iio_channel *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info) +{ + struct iio_dev *indio_dev =3D chan->indio_dev; + struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); + umode_t mode =3D 0; + + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!iio_event_exists(chan, type, dir, info)) + return 0; + + if (info =3D=3D IIO_EV_INFO_ENABLE) { + if (indio_dev->info->read_event_config) + mode |=3D 0444; + + if (indio_dev->info->write_event_config) + mode |=3D 0200; + } else { + if (indio_dev->info->read_event_value) + mode |=3D 0444; + + if (indio_dev->info->write_event_value) + mode |=3D 0200; + } + + return mode; +} +EXPORT_SYMBOL_GPL(iio_event_mode); + +int iio_read_event_processed_scale(struct iio_channel *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + unsigned int scale) +{ + struct iio_dev *indio_dev =3D chan->indio_dev; + struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); + int ret, raw; + + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!iio_event_exists(chan, type, dir, info)) + return -ENODEV; + + if (info =3D=3D IIO_EV_INFO_ENABLE) { + if (!indio_dev->info->read_event_config) + return -EINVAL; + + raw =3D indio_dev->info->read_event_config(indio_dev, + chan->channel, type, + dir); + if (raw < 0) + return raw; + + *val =3D raw; + return 0; + } + + if (!indio_dev->info->read_event_value) + return -EINVAL; + + ret =3D indio_dev->info->read_event_value(indio_dev, chan->channel, type, + dir, info, &raw, NULL); + if (ret < 0) + return ret; + + return iio_convert_raw_to_processed_unlocked(chan, raw, val, scale); +} +EXPORT_SYMBOL_GPL(iio_read_event_processed_scale); + +static int iio_convert_processed_to_raw_unlocked(struct iio_channel *chan, + int processed, int *raw, + unsigned int scale) +{ + int scale_type, scale_val, scale_val2; + int offset_type, offset_val, offset_val2; + s64 r, scale64, raw64; + + scale_type =3D iio_channel_read(chan, &scale_val, &scale_val2, + IIO_CHAN_INFO_SCALE); + if (scale_type < 0) { + raw64 =3D processed / scale; + } else { + switch (scale_type) { + case IIO_VAL_INT: + scale64 =3D (s64)scale_val * scale; + if (scale64 <=3D INT_MAX && scale64 >=3D INT_MIN) + raw64 =3D processed / (int)scale64; + else + raw64 =3D 0; + break; + case IIO_VAL_INT_PLUS_MICRO: + scale64 =3D scale_val * scale * 1000000LL + scale_val2; + raw64 =3D div64_s64_rem(processed, scale64, &r); + raw64 =3D raw64 * 1000000 + + div64_s64(r * 1000000, scale64); + break; + case IIO_VAL_INT_PLUS_NANO: + scale64 =3D scale_val * scale * 1000000000LL + scale_val2; + raw64 =3D div64_s64_rem(processed, scale64, &r); + raw64 =3D raw64 * 1000000000 + + div64_s64(r * 1000000000, scale64); + break; + case IIO_VAL_FRACTIONAL: + raw64 =3D div64_s64((s64)processed * scale_val2, + (s64)scale_val * scale); + break; + case IIO_VAL_FRACTIONAL_LOG2: + raw64 =3D div64_s64((s64)processed << scale_val2, + (s64)scale_val * scale); + break; + default: + return -EINVAL; + } + } + + offset_type =3D iio_channel_read(chan, &offset_val, &offset_val2, + IIO_CHAN_INFO_OFFSET); + if (offset_type >=3D 0) { + switch (offset_type) { + case IIO_VAL_INT: + case IIO_VAL_INT_PLUS_MICRO: + case IIO_VAL_INT_PLUS_NANO: + raw64 -=3D offset_val; + break; + case IIO_VAL_FRACTIONAL: + raw64 -=3D offset_val / offset_val2; + break; + case IIO_VAL_FRACTIONAL_LOG2: + raw64 -=3D offset_val >> offset_val2; + break; + default: + return -EINVAL; + } + } + + *raw =3D clamp(raw64, (s64)INT_MIN, (s64)INT_MAX); + return 0; +} + +int iio_write_event_processed_scale(struct iio_channel *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int processed, + unsigned int scale) +{ + struct iio_dev *indio_dev =3D chan->indio_dev; + struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(chan->indio_d= ev); + int ret, raw; + + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!iio_event_exists(chan, type, dir, info)) + return -ENODEV; + + if (info =3D=3D IIO_EV_INFO_ENABLE) { + if (!indio_dev->info->write_event_config) + return -EINVAL; + + return indio_dev->info->write_event_config(indio_dev, + chan->channel, type, + dir, processed); + } + + if (!indio_dev->info->write_event_value) + return -EINVAL; + + ret =3D iio_convert_processed_to_raw_unlocked(chan, processed, &raw, + scale); + if (ret < 0) + return ret; + + return indio_dev->info->write_event_value(indio_dev, chan->channel, + type, dir, info, raw, 0); +} +EXPORT_SYMBOL_GPL(iio_write_event_processed_scale); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 6a4479616479..16e7682474f3 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -451,4 +451,60 @@ ssize_t iio_write_channel_ext_info(struct iio_channel = *chan, const char *attr, */ ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf); =20 +/** + * iio_event_mode() - get file mode for an event property + * @chan: Channel being queried + * @type: Event type (theshold, rate-of-change, etc.) + * @dir: Event direction (rising, falling, etc.) + * @info: Event property (enable, value, etc.) + * + * Determine an appropriate mode for sysfs files derived from this event. + * + * Return: + * - `0000` if the event is unsupported or otherwise unavailable + * - `0444` if the event is read-only + * - `0200` if the event is write-only + * - `0644` if the event is read-write + */ +umode_t iio_event_mode(struct iio_channel *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info); + +/** + * iio_read_event_processed_scale() - Read an event property + * @chan: Channel being queried + * @type: Event type (theshold, rate-of-change, etc.) + * @dir: Event direction (rising, falling, etc.) + * @info: Event property (enable, value, etc.) + * @val: Processed property value + * @scale: Factor to scale @val by + * + * Read a processed (scaled and offset) event property of a given channel. + * + * Return: 0 on success, or negative error on failure + */ +int iio_read_event_processed_scale(struct iio_channel *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + unsigned int scale); + +/** + * iio_write_event_processed_scale() - Read an event property + * @chan: Channel being queried + * @type: Event type (theshold, rate-of-change, etc.) + * @dir: Event direction (rising, falling, etc.) + * @info: Event property (enable, value, etc.) + * @processed: Processed property value + * @scale: Factor to scale @processed by + * + * Write a processed (scaled and offset) event property of a given channel. + * + * Return: 0 on success, or negative error on failure + */ +int iio_write_event_processed_scale(struct iio_channel *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int processed, + unsigned int scale); + #endif --=20 2.35.1.1320.gc452695387.dirty From nobody Tue Oct 7 03:46:26 2025 Received: from out-174.mta1.migadu.com (out-174.mta1.migadu.com [95.215.58.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 860EC1D63DF for ; Tue, 15 Jul 2025 01:20:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542441; cv=none; b=P0SjVIvpcbMhyatWB6ESo3mTfrs1Ge0ggMsiyf/A5uWpphOF9V34/n5x6K2bUa7wGdKyvYefBzRo2v9X9iJbsiYUst3caAZXNIfUWhPB3OlxPsWy6SOWveWZLgfQCpbIR1hJFjMYVjfIh/jEbef62U2b429NgQ3lfI+Tn6oI3ig= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542441; c=relaxed/simple; bh=Sz4HNtOnMBo7mSId/KsVj73EzUCaVXfOGbO+6v60Gio=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=tU826cjjuHF2eDY7wT9OsPpqqYU4nHZauWqQVvp2b05gmzkWFHkNUOgiihjVlIvxhV/Qmws6Wk50hczADcugHT9ZxbqNGeR6XCeyyoYTuD/BbXszrZA/9esQbhsw5nHFm4awsIX9UQy5ExYmzNWFgVeZaDfqiQcQWrjx+3U8PwU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=u29Fk8aJ; arc=none smtp.client-ip=95.215.58.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="u29Fk8aJ" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1752542437; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=34r1lxdH/9Y6yYE41yHEODqOfw+CqLwlQ46xG1M+uC0=; b=u29Fk8aJdKRXsivfYW+aBJBDEvVMPypfhz7JuJIjeVFr5ffwgmpRblfpK4Xkrm3WX3ynTV Kj/C85OOpx3DZI2qeW+CGXvr9uBXASYlkl4roqdlWcp6K9EnIEyNewR6LfmlJ+QU0pVNy9 EkzY4EiRUuFDJUfEzCI8Drs38myKD7w= From: Sean Anderson To: Jonathan Cameron , Jean Delvare , Guenter Roeck , linux-iio@vger.kernel.org, linux-hwmon@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Nuno=20S=C3=A1?= , linux-kernel@vger.kernel.org, David Lechner , Sean Anderson Subject: [PATCH 3/7] iio: Add in-kernel API for events Date: Mon, 14 Jul 2025 21:20:19 -0400 Message-Id: <20250715012023.2050178-4-sean.anderson@linux.dev> In-Reply-To: <20250715012023.2050178-1-sean.anderson@linux.dev> References: <20250715012023.2050178-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add an API to notify consumers about events. Events still need to be enabled using the iio_read_event/iio_write_event functions. Of course, userspace can also manipulate the enabled events. I don't think this is too much of an issue, since userspace can also manipulate the event thresholds. But enabling events may cause existing programs to be surprised when they get something unexpected. Maybe we should set the interface as busy when there are any in-kernel listeners? Signed-off-by: Sean Anderson --- drivers/iio/industrialio-event.c | 34 +++++++++++++++++++++++++++----- include/linux/iio/consumer.h | 30 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-ev= ent.c index 06295cfc2da8..b9e3ff1cd5c9 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -12,11 +12,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include "iio_core.h" @@ -26,6 +28,7 @@ /** * struct iio_event_interface - chrdev interface for an event line * @wait: wait queue to allow blocking reads of events + * @notifier: notifier head for in-kernel event listeners * @det_events: list of detected events * @dev_attr_list: list of event interface sysfs attribute * @flags: file operations related flags including busy flag. @@ -35,6 +38,7 @@ */ struct iio_event_interface { wait_queue_head_t wait; + struct atomic_notifier_head notifier; DECLARE_KFIFO(det_events, struct iio_event_data, 16); =20 struct list_head dev_attr_list; @@ -67,18 +71,19 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_co= de, s64 timestamp) { struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); struct iio_event_interface *ev_int =3D iio_dev_opaque->event_interface; - struct iio_event_data ev; + struct iio_event_data ev =3D { + .id =3D ev_code, + .timestamp =3D timestamp, + }; int copied; =20 if (!ev_int) return 0; =20 + atomic_notifier_call_chain(&ev_int->notifier, IIO_NOTIFY_EVENT, &ev); + /* Does anyone care? */ if (iio_event_enabled(ev_int)) { - - ev.id =3D ev_code; - ev.timestamp =3D timestamp; - copied =3D kfifo_put(&ev_int->det_events, ev); if (copied !=3D 0) wake_up_poll(&ev_int->wait, EPOLLIN); @@ -223,6 +228,25 @@ static int iio_event_getfd(struct iio_dev *indio_dev) return fd; } =20 +int iio_event_register(struct iio_dev *indio_dev, struct notifier_block *b= lock) +{ + struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); + struct iio_event_interface *ev_int =3D iio_dev_opaque->event_interface; + + return atomic_notifier_chain_register(&ev_int->notifier, block); +} +EXPORT_SYMBOL_GPL(iio_event_register); + +void iio_event_unregister(struct iio_dev *indio_dev, + struct notifier_block *block) +{ + struct iio_dev_opaque *iio_dev_opaque =3D to_iio_dev_opaque(indio_dev); + struct iio_event_interface *ev_int =3D iio_dev_opaque->event_interface; + + WARN_ON(atomic_notifier_chain_unregister(&ev_int->notifier, block)); +} +EXPORT_SYMBOL_GPL(iio_event_unregister); + static const char * const iio_ev_type_text[] =3D { [IIO_EV_TYPE_THRESH] =3D "thresh", [IIO_EV_TYPE_MAG] =3D "mag", diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 16e7682474f3..9918e3f7af3d 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -507,4 +507,34 @@ int iio_write_event_processed_scale(struct iio_channel= *chan, enum iio_event_info info, int processed, unsigned int scale); =20 +struct notifier_block; +enum iio_notifier_val { + /** IIO_NOTIFY_EVENT: v is a pointer to &struct iio_event_data */ + IIO_NOTIFY_EVENT, +}; + +/** + * iio_event_register() - Register a notifier for events + * @indio_dev: Device to be notified of events on + * @block: Notifier block to register + * + * Register a notifier for events on @indio_dev. @v will be a member of &e= num + * iio_notifier_val. Notifiers will be called in atomic context. @indio_dev + * must stay valid until you call iio_event_unregister(). + * + * Return: 0 on success, or -EEXIST if @block has already been registered + */ +int iio_event_register(struct iio_dev *indio_dev, + struct notifier_block *block); + +/** + * iio_event_unregister() - Remove a previously-added notifier + * @indio_dev: Device to be notified of events on + * @block: Notifier previously-registered with iio_event_register() + * + * Remove a previously-added notifier. + */ +void iio_event_unregister(struct iio_dev *indio_dev, + struct notifier_block *block); + #endif --=20 2.35.1.1320.gc452695387.dirty From nobody Tue Oct 7 03:46:26 2025 Received: from out-188.mta1.migadu.com (out-188.mta1.migadu.com [95.215.58.188]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 121DD1DE2BF for ; Tue, 15 Jul 2025 01:20:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.188 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542442; cv=none; b=F4CSgtgnI8pkVgLP/oEJwPj00j4ZjnaqvQE7uLmEYhrba/ViUdSWd9aFzd5URn+Duq/23aUzsEnYjLrqvhQfQ+sI4whouTqJZp2H7qhZg2WTHKmxF9ZX1/x4HX/PnyBDf3HljBmmvJ5Hhp24swsQxSmnah9QmMNkd0tjT7gPt+E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542442; c=relaxed/simple; bh=zxAZttHG0QzJMVGOZzzjcVjrFxXQX8clSrfk6A+vAFA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=QIsajybubLN96l65azPexTLLThlatwVVhzCiDeB9/AlE4JhfPKoq59bl6pOwenkcgcBNAtxp+tAgKC9anHjsHLPvYruFMqXMQHVoqKcRdSZYR8exeXQoGIYxvWsynVsAkY5nJg5UmmT1qQFfen/RY9vnlCDbxwF5TDl0CCv1ZrI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=llDcbHTG; arc=none smtp.client-ip=95.215.58.188 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="llDcbHTG" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1752542439; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=VS38gXY125VgWZkbqT/5MOqt89JO36iRPEiIvNaRM0g=; b=llDcbHTGljqytCUrdfVtc37JD1mVxeDvOKIATE87BQiq4mOURFbxwqoTH3njU1rbMhhh3w WYFeQdtUYuQ6/9iUGA7UgyizDdSgpI01wgc+rlv8/ykNaQRuwUw3vQqLUIKGGlmPiNarh9 gG5kYxp/zquFVmGBXGp/Sk702LEZLog= From: Sean Anderson To: Jonathan Cameron , Jean Delvare , Guenter Roeck , linux-iio@vger.kernel.org, linux-hwmon@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Nuno=20S=C3=A1?= , linux-kernel@vger.kernel.org, David Lechner , Sean Anderson Subject: [PATCH 4/7] hwmon: iio: Refactor scale calculation into helper Date: Mon, 14 Jul 2025 21:20:20 -0400 Message-Id: <20250715012023.2050178-5-sean.anderson@linux.dev> In-Reply-To: <20250715012023.2050178-1-sean.anderson@linux.dev> References: <20250715012023.2050178-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add a function to determine the scale parameter, since it will soon be used in several places. Signed-off-by: Sean Anderson --- drivers/hwmon/iio_hwmon.c | 40 ++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index e376d4cde5ad..bba8919377eb 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -44,6 +44,30 @@ static ssize_t iio_hwmon_read_label(struct device *dev, return iio_read_channel_label(chan, buf); } =20 +/** + * iio_hwmon_scale() - Look up the scaling factor for a channel + * @chan: Channel to get the scale of + * + * Determine the scale to use with @chan in the case where IIO and HWMON u= se + * different units. + * + * Return: scale of @chan + */ +static int iio_hwmon_scale(struct iio_channel *chan) +{ + enum iio_chan_type type; + int ret; + + ret =3D iio_get_channel_type(chan, &type); + if (ret < 0) + return ret; + + /* mili-Watts to micro-Watts conversion */ + if (type =3D=3D IIO_POWER) + return 1000; + return 1; +} + /* * Assumes that IIO and hwmon operate in the same base units. * This is supposed to be true, but needs verification for @@ -53,22 +77,16 @@ static ssize_t iio_hwmon_read_val(struct device *dev, struct device_attribute *attr, char *buf) { - int result; - int ret; struct sensor_device_attribute *sattr =3D to_sensor_dev_attr(attr); struct iio_hwmon_state *state =3D dev_get_drvdata(dev); struct iio_channel *chan =3D &state->channels[sattr->index]; - enum iio_chan_type type; + int ret, result, scale; =20 - ret =3D iio_get_channel_type(chan, &type); - if (ret < 0) - return ret; + scale =3D iio_hwmon_scale(chan); + if (scale < 0) + return scale; =20 - if (type =3D=3D IIO_POWER) - /* mili-Watts to micro-Watts conversion */ - ret =3D iio_read_channel_processed_scale(chan, &result, 1000); - else - ret =3D iio_read_channel_processed(chan, &result); + ret =3D iio_read_channel_processed_scale(chan, &result, scale); if (ret < 0) return ret; =20 --=20 2.35.1.1320.gc452695387.dirty From nobody Tue Oct 7 03:46:26 2025 Received: from out-182.mta1.migadu.com (out-182.mta1.migadu.com [95.215.58.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CC99B1EB195 for ; Tue, 15 Jul 2025 01:20:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542445; cv=none; b=LOMRDgeEXwxMawr/Qw7W0pP9PLmixr9KS/Y8HE/r/5UuIGh7dwFq8OVX+h/ghzGeOn9ExJFydPwRljJKBX47wRx9OgCeP0xghMG23MaUd1Kd2+RzOjDEzUBw14Ifw0UlIUMYtMT8MdDk3MgOwdI6adWbPzlyw1SYOP58SYjx9Zk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542445; c=relaxed/simple; bh=2QaFyI8ILVBS5g97+jOxADYG68AZSIqZdeO851iUE2c=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cXTP9LRBMpZ4vg0wVMHOvAvmoT00eZleNFzmfNA68w60AaMS2NXoxqJhol5nzInNjIqq8WTpK77z62QfHIrLITvZIayhYt2+muwg887/aPlmKObmOh66UhDjrZdPLChJ6n4lsObuQDuHm1wRhLgoQZM1O0vwFFfk81ML/prnGE0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=U0VyMGcc; arc=none smtp.client-ip=95.215.58.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="U0VyMGcc" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1752542441; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BWv0qdWMnYbMo4uu7JsXsvkSPvGdLSsaNbuCjmIdofU=; b=U0VyMGccatnYZXKK7QZOeV5HqGQ9aJrnHMNtd453oWH8QDgTh6Sh4aWaun1iDw7MGghQNh Ht2V7nlVpnX2P5cId5YP+unEO3Qiz+Q3FfcKefkTJmLqr9F9uzhY85B0zzaOklbaxDarXP ZAVzwCL+p9GGHb/l7CXjNGD+sIWPqAc= From: Sean Anderson To: Jonathan Cameron , Jean Delvare , Guenter Roeck , linux-iio@vger.kernel.org, linux-hwmon@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Nuno=20S=C3=A1?= , linux-kernel@vger.kernel.org, David Lechner , Sean Anderson Subject: [PATCH 5/7] hwmon: iio: Add helper function for creating attributes Date: Mon, 14 Jul 2025 21:20:21 -0400 Message-Id: <20250715012023.2050178-6-sean.anderson@linux.dev> In-Reply-To: <20250715012023.2050178-1-sean.anderson@linux.dev> References: <20250715012023.2050178-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add a helper function to create attributes and initialize their fields. This reduces repetition when creating several attributes per channel. Signed-off-by: Sean Anderson --- drivers/hwmon/iio_hwmon.c | 78 +++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 36 deletions(-) diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index bba8919377eb..7dc156d2aea4 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -24,13 +24,15 @@ * @attr_group: the group of attributes * @groups: null terminated array of attribute groups * @attrs: null terminated array of attribute pointers. + * @num_attrs: length of @attrs */ struct iio_hwmon_state { struct iio_channel *channels; - int num_channels; struct attribute_group attr_group; const struct attribute_group *groups[2]; struct attribute **attrs; + size_t num_attrs; + int num_channels; }; =20 static ssize_t iio_hwmon_read_label(struct device *dev, @@ -93,12 +95,39 @@ static ssize_t iio_hwmon_read_val(struct device *dev, return sprintf(buf, "%d\n", result); } =20 +static int add_device_attr(struct device *dev, struct iio_hwmon_state *st, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + int i, const char *fmt, ...) +{ + struct sensor_device_attribute *a; + va_list ap; + + a =3D devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + sysfs_attr_init(&a->dev_attr.attr); + va_start(ap, fmt); + a->dev_attr.attr.name =3D devm_kvasprintf(dev, GFP_KERNEL, fmt, ap); + va_end(ap); + if (!a->dev_attr.attr.name) + return -ENOMEM; + + a->dev_attr.show =3D show; + a->dev_attr.attr.mode =3D 0444; + a->index =3D i; + + st->attrs[st->num_attrs++] =3D &a->dev_attr.attr; + return 0; +} + static int iio_hwmon_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; struct iio_hwmon_state *st; - struct sensor_device_attribute *a; - int ret, i, attr =3D 0; + int ret, i; int in_i =3D 1, temp_i =3D 1, curr_i =3D 1, humidity_i =3D 1, power_i =3D= 1; enum iio_chan_type type; struct iio_channel *channels; @@ -136,11 +165,6 @@ static int iio_hwmon_probe(struct platform_device *pde= v) const char *prefix; int n; =20 - a =3D devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); - if (a =3D=3D NULL) - return -ENOMEM; - - sysfs_attr_init(&a->dev_attr.attr); ret =3D iio_get_channel_type(&st->channels[i], &type); if (ret < 0) return ret; @@ -170,36 +194,18 @@ static int iio_hwmon_probe(struct platform_device *pd= ev) return -EINVAL; } =20 - a->dev_attr.attr.name =3D devm_kasprintf(dev, GFP_KERNEL, - "%s%d_input", - prefix, n); - if (a->dev_attr.attr.name =3D=3D NULL) - return -ENOMEM; - - a->dev_attr.show =3D iio_hwmon_read_val; - a->dev_attr.attr.mode =3D 0444; - a->index =3D i; - st->attrs[attr++] =3D &a->dev_attr.attr; + ret =3D add_device_attr(dev, st, iio_hwmon_read_val, i, + "%s%d_input", prefix, n); + if (ret) + return ret; =20 /* Let's see if we have a label... */ - if (iio_read_channel_label(&st->channels[i], buf) < 0) - continue; - - a =3D devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); - if (a =3D=3D NULL) - return -ENOMEM; - - sysfs_attr_init(&a->dev_attr.attr); - a->dev_attr.attr.name =3D devm_kasprintf(dev, GFP_KERNEL, - "%s%d_label", - prefix, n); - if (!a->dev_attr.attr.name) - return -ENOMEM; - - a->dev_attr.show =3D iio_hwmon_read_label; - a->dev_attr.attr.mode =3D 0444; - a->index =3D i; - st->attrs[attr++] =3D &a->dev_attr.attr; + if (iio_read_channel_label(&st->channels[i], buf) >=3D 0) { + ret =3D add_device_attr(dev, st, iio_hwmon_read_label, + i, "%s%d_label", prefix, n); + if (ret) + return ret; + } } =20 devm_free_pages(dev, (unsigned long)buf); --=20 2.35.1.1320.gc452695387.dirty From nobody Tue Oct 7 03:46:26 2025 Received: from out-182.mta1.migadu.com (out-182.mta1.migadu.com [95.215.58.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ECBB51F5847 for ; Tue, 15 Jul 2025 01:20:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542446; cv=none; b=jn5n+u5uIEvMrdZoSLu8HqoAfLBLcv8pWzNUPzm9OTVVZfD3ruLw7AI2FfT7jizDmip7IBH5ppJFd0B8QksaKA3HqZ7UbiSHDcvaHSH+oXwSYu+vbwCozO2H+2yur2haZAVm+wr2u53tSfhMHK5ImFrPOoHUYBf26TSlINHbajM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542446; c=relaxed/simple; bh=aIZbLBsah3Yw0NJEHGudXnDSIeXYLls3JcTgUq6xWPw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=D8NdiM/glLdR+ozrKy0jYXlF5MytDA5HD/W1Ccos2YV1/iQucPHTHwjUxnXnPnyaC6ntQRm8M7Axv1K4QwmnUovLbq0DMlOwdgMPR/Qy6w13zlT7r0+xE7AoGH64+GqoF29Fjo9FUVGvK2acyuoue8+7V5MCP89wNrfBR1xR780= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=QpLuphKK; arc=none smtp.client-ip=95.215.58.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="QpLuphKK" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1752542443; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xFYh68TfjA4tujo44GWGu2WxKiJwS1euTrgYfJD1Erg=; b=QpLuphKKTLaefpPKmKKtu3LFfoRSjtPkQwaTT+Y4lQZ1Afi/K2oyigstmCt1UFq+umF8Ls gy/RyXVQQrDVtKkjwM1NED7QUYcztxTvz7Y1pQpTB00VoVe8mwsMouGNSq06vqmU1ZnH7F L65SrUR5qzQx3JznxUAolDay1VXxiqg= From: Sean Anderson To: Jonathan Cameron , Jean Delvare , Guenter Roeck , linux-iio@vger.kernel.org, linux-hwmon@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Nuno=20S=C3=A1?= , linux-kernel@vger.kernel.org, David Lechner , Sean Anderson Subject: [PATCH 6/7] hwmon: iio: Add min/max support Date: Mon, 14 Jul 2025 21:20:22 -0400 Message-Id: <20250715012023.2050178-7-sean.anderson@linux.dev> In-Reply-To: <20250715012023.2050178-1-sean.anderson@linux.dev> References: <20250715012023.2050178-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add support for minimum/maximum attributes. Like the _input attribute, we just need to call into the IIO API. Signed-off-by: Sean Anderson --- drivers/hwmon/iio_hwmon.c | 94 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 7dc156d2aea4..3db4d4b30022 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -95,6 +95,54 @@ static ssize_t iio_hwmon_read_val(struct device *dev, return sprintf(buf, "%d\n", result); } =20 +static ssize_t iio_hwmon_read_event(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr =3D to_sensor_dev_attr_2(attr); + struct iio_hwmon_state *state =3D dev_get_drvdata(dev); + struct iio_channel *chan =3D &state->channels[sattr->index]; + int ret, result, scale; + + scale =3D iio_hwmon_scale(chan); + if (scale < 0) + return scale; + + ret =3D iio_read_event_processed_scale(chan, IIO_EV_TYPE_THRESH, + sattr->nr, IIO_EV_INFO_VALUE, + &result, scale); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", result); +} + +static ssize_t iio_hwmon_write_event(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr =3D to_sensor_dev_attr_2(attr); + struct iio_hwmon_state *state =3D dev_get_drvdata(dev); + struct iio_channel *chan =3D &state->channels[sattr->index]; + int ret, scale, val; + + ret =3D kstrtoint(buf, 0, &val); + if (ret) + return ret; + + scale =3D iio_hwmon_scale(chan); + if (scale < 0) + return scale; + + ret =3D iio_write_event_processed_scale(chan, IIO_EV_TYPE_THRESH, + sattr->nr, IIO_EV_INFO_VALUE, + val, scale); + if (ret < 0) + return ret; + + return count; +} + static int add_device_attr(struct device *dev, struct iio_hwmon_state *st, ssize_t (*show)(struct device *dev, struct device_attribute *attr, @@ -123,6 +171,40 @@ static int add_device_attr(struct device *dev, struct = iio_hwmon_state *st, return 0; } =20 +static int add_event_attr(struct device *dev, struct iio_hwmon_state *st, + int i, enum iio_event_direction dir, + const char *fmt, ...) +{ + struct sensor_device_attribute_2 *a; + umode_t mode; + va_list ap; + + mode =3D iio_event_mode(&st->channels[i], IIO_EV_TYPE_THRESH, dir, + IIO_EV_INFO_VALUE); + if (!mode) + return 0; + + a =3D devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + sysfs_attr_init(&a->dev_attr.attr); + va_start(ap, fmt); + a->dev_attr.attr.name =3D devm_kvasprintf(dev, GFP_KERNEL, fmt, ap); + va_end(ap); + if (!a->dev_attr.attr.name) + return -ENOMEM; + + a->dev_attr.show =3D iio_hwmon_read_event; + a->dev_attr.store =3D iio_hwmon_write_event; + a->dev_attr.attr.mode =3D mode; + a->index =3D i; + a->nr =3D dir; + + st->attrs[st->num_attrs++] =3D &a->dev_attr.attr; + return 0; +} + static int iio_hwmon_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; @@ -156,7 +238,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->num_channels++; =20 st->attrs =3D devm_kcalloc(dev, - 2 * st->num_channels + 1, sizeof(*st->attrs), + 4 * st->num_channels + 1, sizeof(*st->attrs), GFP_KERNEL); if (st->attrs =3D=3D NULL) return -ENOMEM; @@ -206,6 +288,16 @@ static int iio_hwmon_probe(struct platform_device *pde= v) if (ret) return ret; } + + ret =3D add_event_attr(dev, st, i, IIO_EV_DIR_FALLING, + "%s%d_min", prefix, n); + if (ret) + return ret; + + ret =3D add_event_attr(dev, st, i, IIO_EV_DIR_RISING, + "%s%d_max", prefix, n); + if (ret) + return ret; } =20 devm_free_pages(dev, (unsigned long)buf); --=20 2.35.1.1320.gc452695387.dirty From nobody Tue Oct 7 03:46:26 2025 Received: from out-171.mta1.migadu.com (out-171.mta1.migadu.com [95.215.58.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 83CF61FECC3 for ; Tue, 15 Jul 2025 01:20:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=95.215.58.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542449; cv=none; b=SelFFPhTBYnmKjMT49Isc/SaoZwCm44gNdssYLovkYJX0QDB7MDN7HqwLFZZX61XwG5akX3X951fJqNvHw4I+zan58oKJCRWgvffD70LRPUwRtcBCSwh1uY5QMBBVoNxC2+ox2eLw+WKx4y0GLQdjxY2ey7bB+/8NyGMOxC5bQk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752542449; c=relaxed/simple; bh=bPfeHgVMRjFo+jzsXSzZgy1MKffh7fIAckyt2QrH5sY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=KJMUecF1mHp54ZKRSc2oCX4ayxBsILbo2y1m68iUO5oFL94CvWe4FvMru075rmjQzuGVIaC/kyj4mRh06nqt18FptSDFzZSAuAgrCAoAU43DYwIQBTXNxPPq8VXI0qwauiI0hrBxbJ3kGHcPag9GiTxjftAKXlpaR1b+Tg9kIlk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=c3t1Z7Fs; arc=none smtp.client-ip=95.215.58.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="c3t1Z7Fs" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1752542444; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=BY0Ov/6AcGBp5N1Wvrgjij988UEIGtctKJNwWjsdeWI=; b=c3t1Z7FsjopmgBwNhQA6RhPat2/4KSOOba+yiJzxSiHu7cqybNKUAFoPGVaqwPX/nwaPcA bgEvH3f4o2Ds6R4gqYseRMR0RwRQq/3v0NZpbfnwy4vpwed0m9BysSqjJfg4mxbvZQ2QTb UATRNUAK4CFHTFwbVyvDwzWGxBbCQxM= From: Sean Anderson To: Jonathan Cameron , Jean Delvare , Guenter Roeck , linux-iio@vger.kernel.org, linux-hwmon@vger.kernel.org Cc: Andy Shevchenko , =?UTF-8?q?Nuno=20S=C3=A1?= , linux-kernel@vger.kernel.org, David Lechner , Sean Anderson Subject: [PATCH 7/7] hwmon: iio: Add alarm support Date: Mon, 14 Jul 2025 21:20:23 -0400 Message-Id: <20250715012023.2050178-8-sean.anderson@linux.dev> In-Reply-To: <20250715012023.2050178-1-sean.anderson@linux.dev> References: <20250715012023.2050178-1-sean.anderson@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Add alarm support based on IIO threshold events. The alarm is cleared on read, but will be set again if the condition is still present. This is detected by disabling and re-enabling the event. The same trick is done when creating the attribute to detect already-triggered events. The alarms are updated by an event listener. To keep the notifier call chain short, we create one listener per iio device, shared across all hwmon devices. To avoid dynamic creation of alarms, alarms for all possible events are allocated at creation. Lookup is done by a linear scan, as I expect events to occur rarely. If performance becomes an issue, a binary search could be done instead (or some kind of hash lookup). Signed-off-by: Sean Anderson --- drivers/hwmon/iio_hwmon.c | 322 +++++++++++++++++++++++++++++++++++++- 1 file changed, 321 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 3db4d4b30022..c963bc5452ba 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -15,7 +16,192 @@ #include #include #include +#include +#include #include +#include + +/* Protects iio_hwmon_listeners and listeners' refcnt */ +DEFINE_MUTEX(iio_hwmon_listener_lock); +LIST_HEAD(iio_hwmon_listeners); + +/** + * struct iio_hwmon_listener - Listener for IIO events + * @block: Notifier for events + * @ids: Array of IIO event ids, one per alarm + * @alarms: Bitmap of alarms + * @num_alarms: Length of @ids and @alarms + * @indio_dev: Device we are listening to + * @list: List of all listeners + * @refcnt: Reference count + */ +struct iio_hwmon_listener { + struct notifier_block block; + u64 *ids; + unsigned long *alarms; + size_t num_alarms; + + struct iio_dev *indio_dev; + struct list_head list; + unsigned int refcnt; +}; + +/** + * iio_hwmon_lookup_alarm() - Find an alarm by id + * @listener: Event listener + * @id: IIO event id + * + * Return: index of @id in @listener->ids, or -1 if not found + */ +static ssize_t iio_hwmon_lookup_alarm(struct iio_hwmon_listener *listener, + u64 id) +{ + ssize_t i; + + for (i =3D 0; i < listener->num_alarms; i++) + if (listener->ids[i] =3D=3D id) + return i; + + return -1; +} + +static int iio_hwmon_listener_callback(struct notifier_block *block, + unsigned long action, void *data) +{ + struct iio_hwmon_listener *listener =3D + container_of(block, struct iio_hwmon_listener, block); + struct iio_event_data *ev =3D data; + ssize_t i; + + if (action !=3D IIO_NOTIFY_EVENT) + return NOTIFY_DONE; + + i =3D iio_hwmon_lookup_alarm(listener, ev->id); + if (i >=3D 0) + set_bit(i, listener->alarms); + else + dev_warn_once(&listener->indio_dev->dev, + "unknown event %016llx\n", ev->id); + + return NOTIFY_DONE; +} + +/** + * iio_event_id() - Calculate an IIO event id + * @channel: IIO channel for this event + * @type: Event type (theshold, rate-of-change, etc.) + * @dir: Event direction (rising, falling, etc.) + * + * Return: IIO event id corresponding to this event's IIO id + */ +static u64 iio_event_id(struct iio_chan_spec const *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + if (chan->differential) + return IIO_DIFF_EVENT_CODE(chan->type, chan->channel, + chan->channel2, type, dir); + if (chan->modified) + return IIO_MOD_EVENT_CODE(chan->type, chan->channel, + chan->channel2, type, dir); + return IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, type, dir); +} + +/** + * iio_hwmon_listener_get() - Get a listener for an IIO device + * @indio_dev: IIO device to listen to + * + * Look up or create a new listener for @indio_dev. The returned listener = is + * registered with @indio_dev, but events still need to be manually enable= d. + * You must call iio_hwmon_listener_put() when you are done. + * + * Return: Listener for @indio_dev, or an error pointer + */ +static struct iio_hwmon_listener *iio_hwmon_listener_get(struct iio_dev *i= ndio_dev) +{ + struct iio_hwmon_listener *listener; + int err =3D -ENOMEM; + size_t i, j; + + guard(mutex)(&iio_hwmon_listener_lock); + list_for_each_entry(listener, &iio_hwmon_listeners, list) { + if (listener->indio_dev =3D=3D indio_dev) { + if (likely(listener->refcnt !=3D UINT_MAX)) + listener->refcnt++; + return listener; + } + } + + listener =3D kzalloc(sizeof(*listener), GFP_KERNEL); + if (!listener) + goto err_unlock; + + listener->refcnt =3D 1; + listener->indio_dev =3D indio_dev; + listener->block.notifier_call =3D iio_hwmon_listener_callback; + for (i =3D 0; i < indio_dev->num_channels; i++) + listener->num_alarms +=3D indio_dev->channels[i].num_event_specs; + + listener->ids =3D kcalloc(listener->num_alarms, sizeof(*listener->ids), + GFP_KERNEL); + listener->alarms =3D bitmap_zalloc(listener->num_alarms, GFP_KERNEL); + if (!listener->ids || !listener->alarms) + goto err_listener; + + i =3D 0; + for (j =3D 0; j < indio_dev->num_channels; j++) { + struct iio_chan_spec const *chan =3D &indio_dev->channels[j]; + size_t k; + + for (k =3D 0; k < chan->num_event_specs; k++) + listener->ids[i++] =3D + iio_event_id(chan, chan->event_spec[k].type, + chan->event_spec[k].dir); + } + + err =3D iio_event_register(indio_dev, &listener->block); + if (err) + goto err_alarms; + + list_add(&listener->list, &iio_hwmon_listeners); + mutex_unlock(&iio_hwmon_listener_lock); + return listener; + +err_alarms: + kfree(listener->alarms); + kfree(listener->ids); +err_listener: + kfree(listener); +err_unlock: + mutex_unlock(&iio_hwmon_listener_lock); + return ERR_PTR(err); +} + +/** + * iio_hwmon_listener_put() - Release a listener + * @data: &struct iio_hwmon_listener to release + * + * For convenience, @data is void. + */ +static void iio_hwmon_listener_put(void *data) +{ + struct iio_hwmon_listener *listener =3D data; + + scoped_guard(mutex, &iio_hwmon_listener_lock) { + if (unlikely(listener->refcnt =3D=3D UINT_MAX)) + return; + + if (--listener->refcnt) + return; + + list_del(&listener->list); + iio_event_unregister(listener->indio_dev, &listener->block); + } + + kfree(listener->alarms); + kfree(listener->ids); + kfree(listener); +} =20 /** * struct iio_hwmon_state - device instance state @@ -143,6 +329,68 @@ static ssize_t iio_hwmon_write_event(struct device *de= v, return count; } =20 +/** + * struct iio_hwmon_alarm_attribute - IIO HWMON alarm attribute + * @dev_attr: Base device attribute + * @listener: Listener for this alarm + * @index: Index of the channel in the IIO HWMON + * @alarm: Index of the alarm within @listener + */ +struct iio_hwmon_alarm_attribute { + struct device_attribute dev_attr; + struct iio_hwmon_listener *listener; + size_t index; + size_t alarm; +}; +#define to_alarm_attr(_dev_attr) \ + container_of(_dev_attr, struct iio_hwmon_alarm_attribute, dev_attr) + +/** + * iio_hwmon_alarm_toggle() - Turn an event off and back on again + * @chan: Channel of the event + * @dir: Event direction (rising, falling, etc.) + * + * Toggle an event's enable so we get notified if the alarm is already + * triggered. We use this to convert IIO's event-triggered events into + * level-triggered alarms. + * + * Return: 0 on success or negative error on failure + */ +static int iio_hwmon_alarm_toggle(struct iio_channel *chan, + enum iio_event_direction dir) +{ + int ret; + + ret =3D iio_write_event_processed_scale(chan, IIO_EV_TYPE_THRESH, dir, + IIO_EV_INFO_ENABLE, 0, 1); + if (ret) + return ret; + + return iio_write_event_processed_scale(chan, IIO_EV_TYPE_THRESH, dir, + IIO_EV_INFO_ENABLE, 1, 1); +} + +static ssize_t iio_hwmon_read_alarm(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_hwmon_alarm_attribute *sattr =3D to_alarm_attr(attr); + struct iio_hwmon_state *state =3D dev_get_drvdata(dev); + struct iio_channel *chan =3D &state->channels[sattr->index]; + + if (test_and_clear_bit(sattr->alarm, sattr->listener->alarms)) { + u64 id =3D sattr->listener->ids[sattr->alarm]; + enum iio_event_direction dir =3D IIO_EVENT_CODE_EXTRACT_DIR(id); + + WARN_ON(iio_hwmon_alarm_toggle(chan, dir)); + strcpy(buf, "1\n"); + return 2; + } + + strcpy(buf, "0\n"); + return 2; +} + static int add_device_attr(struct device *dev, struct iio_hwmon_state *st, ssize_t (*show)(struct device *dev, struct device_attribute *attr, @@ -205,6 +453,63 @@ static int add_event_attr(struct device *dev, struct i= io_hwmon_state *st, return 0; } =20 +static int add_alarm_attr(struct device *dev, struct iio_hwmon_state *st, + int i, enum iio_event_direction dir, + const char *fmt, ...) +{ + struct iio_hwmon_alarm_attribute *a; + struct iio_hwmon_listener *listener; + ssize_t alarm; + umode_t mode; + va_list ap; + int ret; + + mode =3D iio_event_mode(&st->channels[i], IIO_EV_TYPE_THRESH, dir, + IIO_EV_INFO_ENABLE); + if (!(mode & 0200)) + return 0; + + listener =3D iio_hwmon_listener_get(st->channels[i].indio_dev); + if (listener =3D=3D ERR_PTR(-EBUSY)) + return 0; + if (IS_ERR(listener)) + return PTR_ERR(listener); + + ret =3D devm_add_action_or_reset(dev, iio_hwmon_listener_put, listener); + if (ret) + return ret; + + alarm =3D iio_hwmon_lookup_alarm(listener, + iio_event_id(st->channels[i].channel, + IIO_EV_TYPE_THRESH, dir)); + if (WARN_ON_ONCE(alarm < 0)) + return -ENOENT; + + ret =3D iio_hwmon_alarm_toggle(&st->channels[i], dir); + if (ret) + return ret; + + a =3D devm_kzalloc(dev, sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + sysfs_attr_init(&a->dev_attr.attr); + va_start(ap, fmt); + a->dev_attr.attr.name =3D devm_kvasprintf(dev, GFP_KERNEL, fmt, ap); + va_end(ap); + if (!a->dev_attr.attr.name) + return -ENOMEM; + + a->dev_attr.show =3D iio_hwmon_read_alarm; + a->dev_attr.attr.mode =3D 0400; + a->listener =3D listener; + a->index =3D i; + a->alarm =3D alarm; + + st->attrs[st->num_attrs++] =3D &a->dev_attr.attr; + return 0; +} + static int iio_hwmon_probe(struct platform_device *pdev) { struct device *dev =3D &pdev->dev; @@ -238,7 +543,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->num_channels++; =20 st->attrs =3D devm_kcalloc(dev, - 4 * st->num_channels + 1, sizeof(*st->attrs), + 7 * st->num_channels + 1, sizeof(*st->attrs), GFP_KERNEL); if (st->attrs =3D=3D NULL) return -ENOMEM; @@ -298,6 +603,21 @@ static int iio_hwmon_probe(struct platform_device *pde= v) "%s%d_max", prefix, n); if (ret) return ret; + + ret =3D add_alarm_attr(dev, st, i, IIO_EV_DIR_RISING, + "%s%d_max_alarm", prefix, n); + if (ret) + return ret; + + ret =3D add_alarm_attr(dev, st, i, IIO_EV_DIR_FALLING, + "%s%d_min_alarm", prefix, n); + if (ret) + return ret; + + ret =3D add_alarm_attr(dev, st, i, IIO_EV_DIR_EITHER, + "%s%d_alarm", prefix, n); + if (ret) + return ret; } =20 devm_free_pages(dev, (unsigned long)buf); --=20 2.35.1.1320.gc452695387.dirty