LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH v7 0/1] Adding support for IIO SCMI based sensors
@ 2021-03-09 23:12 Jyoti Bhayana
  2021-03-09 23:12 ` [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors Jyoti Bhayana
  0 siblings, 1 reply; 16+ messages in thread
From: Jyoti Bhayana @ 2021-03-09 23:12 UTC (permalink / raw)
  To: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jyoti Bhayana, Mauro Carvalho Chehab,
	David S. Miller, Rob Herring, Lukas Bulwahn
  Cc: linux-kernel, linux-iio, cristian.marussi, sudeep.holla,
	egranata, mikhail.golubev, Igor.Skalkin, Peter.hilber,
	ankitarora, gurunagarajan

Hi,

This series adds support for ARM SCMI Protocol based IIO Device.

This driver provides support for Accelerometer and Gyroscope sensor using
SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification,
which is available at 

https://developer.arm.com/documentation/den0056/c/

This version of the patch series has been tested using 
version 5.4.21 branch of Android common kernel.

Any feedback welcome,

Thanks,

Jyoti Bhayana

v6 --> v7
- Fixed the error reported by kernel test robot

v5 --> v6
- Fixed the warning by kernel test robot
- Incorporated the feedback comments from v5
- Fixed the bug found in scmi_iio_set_odr_val
  for calculating the multiplier

v4 --> v5
- Dropped the RFC tag
- Added channel ext_info for raw_available
- Incorporated the feedback comments from v4 review of the patch

v3 --> v4
- Incorporated the feedback comments from v3 review of the patch

v2 --> v3
- Incorporated the feedback comments from v2 review of the patch

v1 --> v2
- Incorporated the feedback comments from v1 review of the patch
- Regarding the new ABI for sensor_power,sensor_max_range,
and sensor_resolution, these are some of the sensor attributes
which Android passes to the apps. If there is any other way of getting
those values, please let us know


Jyoti Bhayana (1):
  iio/scmi: Adding support for IIO SCMI Based Sensors

 MAINTAINERS                                |   6 +
 drivers/firmware/arm_scmi/driver.c         |   2 +-
 drivers/iio/common/Kconfig                 |   1 +
 drivers/iio/common/Makefile                |   1 +
 drivers/iio/common/scmi_sensors/Kconfig    |  18 +
 drivers/iio/common/scmi_sensors/Makefile   |   5 +
 drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
 7 files changed, 715 insertions(+), 1 deletion(-)
 create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
 create mode 100644 drivers/iio/common/scmi_sensors/Makefile
 create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c

-- 
2.30.1.766.gb4fecdf3b7-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-09 23:12 [PATCH v7 0/1] Adding support for IIO SCMI based sensors Jyoti Bhayana
@ 2021-03-09 23:12 ` Jyoti Bhayana
  2021-03-10 11:15   ` Peter Hilber
  2021-03-11 21:08   ` Jonathan Cameron
  0 siblings, 2 replies; 16+ messages in thread
From: Jyoti Bhayana @ 2021-03-09 23:12 UTC (permalink / raw)
  To: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Jyoti Bhayana, Mauro Carvalho Chehab,
	David S. Miller, Rob Herring, Lukas Bulwahn
  Cc: linux-kernel, linux-iio, cristian.marussi, sudeep.holla,
	egranata, mikhail.golubev, Igor.Skalkin, Peter.hilber,
	ankitarora, gurunagarajan, kernel test robot, Jonathan Cameron

This change provides ARM SCMI Protocol based IIO device.
This driver provides support for Accelerometer and Gyroscope using
SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification

Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Jyoti Bhayana <jbhayana@google.com>
Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 MAINTAINERS                                |   6 +
 drivers/firmware/arm_scmi/driver.c         |   2 +-
 drivers/iio/common/Kconfig                 |   1 +
 drivers/iio/common/Makefile                |   1 +
 drivers/iio/common/scmi_sensors/Kconfig    |  18 +
 drivers/iio/common/scmi_sensors/Makefile   |   5 +
 drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
 7 files changed, 715 insertions(+), 1 deletion(-)
 create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
 create mode 100644 drivers/iio/common/scmi_sensors/Makefile
 create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d92f85ca831d..14227980f3d2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8692,6 +8692,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
 F:	drivers/iio/multiplexer/iio-mux.c
 
+IIO SCMI BASED DRIVER
+M:	Jyoti Bhayana <jbhayana@google.com>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	drivers/iio/common/scmi_sensors/scmi_iio.c
+
 IIO SUBSYSTEM AND DRIVERS
 M:	Jonathan Cameron <jic23@kernel.org>
 R:	Lars-Peter Clausen <lars@metafoo.de>
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index cacdf1589b10..3e748e57deab 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = {
 	{ SCMI_PROTOCOL_SYSTEM, { "syspower" },},
 	{ SCMI_PROTOCOL_PERF,   { "cpufreq" },},
 	{ SCMI_PROTOCOL_CLOCK,  { "clocks" },},
-	{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
+	{ SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },},
 	{ SCMI_PROTOCOL_RESET,  { "reset" },},
 	{ SCMI_PROTOCOL_VOLTAGE,  { "regulator" },},
 };
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index 2b9ee9161abd..0334b4954773 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -6,5 +6,6 @@
 source "drivers/iio/common/cros_ec_sensors/Kconfig"
 source "drivers/iio/common/hid-sensors/Kconfig"
 source "drivers/iio/common/ms_sensors/Kconfig"
+source "drivers/iio/common/scmi_sensors/Kconfig"
 source "drivers/iio/common/ssp_sensors/Kconfig"
 source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 4bc30bb548e2..fad40e1e1718 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -11,5 +11,6 @@
 obj-y += cros_ec_sensors/
 obj-y += hid-sensors/
 obj-y += ms_sensors/
+obj-y += scmi_sensors/
 obj-y += ssp_sensors/
 obj-y += st_sensors/
diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig
new file mode 100644
index 000000000000..67e084cbb1ab
--- /dev/null
+++ b/drivers/iio/common/scmi_sensors/Kconfig
@@ -0,0 +1,18 @@
+#
+# IIO over SCMI
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "IIO SCMI Sensors"
+
+config IIO_SCMI
+	tristate "IIO SCMI"
+        depends on ARM_SCMI_PROTOCOL
+        select IIO_BUFFER
+        select IIO_KFIFO_BUF
+	help
+          Say yes here to build support for IIO SCMI Driver.
+          This provides ARM SCMI Protocol based IIO device.
+          This driver provides support for accelerometer and gyroscope
+          sensors available on SCMI based platforms.
+endmenu
diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile
new file mode 100644
index 000000000000..f13140a2575a
--- /dev/null
+++ b/drivers/iio/common/scmi_sensors/Makefile
@@ -0,0 +1,5 @@
+# SPDX - License - Identifier : GPL - 2.0 - only
+#
+# Makefile for the IIO over SCMI
+#
+obj-$(CONFIG_IIO_SCMI) += scmi_iio.o
diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
new file mode 100644
index 000000000000..872d87ca6256
--- /dev/null
+++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
@@ -0,0 +1,683 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * System Control and Management Interface(SCMI) based IIO sensor driver
+ *
+ * Copyright (C) 2021 Google LLC
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/scmi_protocol.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#define SCMI_IIO_NUM_OF_AXIS 3
+
+struct scmi_iio_priv {
+	struct scmi_handle *handle;
+	const struct scmi_sensor_info *sensor_info;
+	struct iio_dev *indio_dev;
+	/* adding one additional channel for timestamp */
+	s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1];
+	struct notifier_block sensor_update_nb;
+	u32 *freq_avail;
+};
+
+static int scmi_iio_sensor_update_cb(struct notifier_block *nb,
+				     unsigned long event, void *data)
+{
+	struct scmi_sensor_update_report *sensor_update = data;
+	struct iio_dev *scmi_iio_dev;
+	struct scmi_iio_priv *sensor;
+	s8 tstamp_scale;
+	u64 time, time_ns;
+	int i;
+
+	if (sensor_update->readings_count == 0)
+		return NOTIFY_DONE;
+
+	sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb);
+
+	for (i = 0; i < sensor_update->readings_count; i++)
+		sensor->iio_buf[i] = sensor_update->readings[i].value;
+
+	if (!sensor->sensor_info->timestamped) {
+		time_ns = ktime_to_ns(sensor_update->timestamp);
+	} else {
+		/*
+		 *  All the axes are supposed to have the same value for timestamp.
+		 *  We are just using the values from the Axis 0 here.
+		 */
+		time = sensor_update->readings[0].timestamp;
+
+		/*
+		 *  Timestamp returned by SCMI is in seconds and is equal to
+		 *  time * power-of-10 multiplier(tstamp_scale) seconds.
+		 *  Converting the timestamp to nanoseconds below.
+		 */
+		tstamp_scale = sensor->sensor_info->tstamp_scale +
+			       const_ilog2(NSEC_PER_SEC) / const_ilog2(10);
+		if (tstamp_scale < 0) {
+			do_div(time, int_pow(10, abs(tstamp_scale)));
+			time_ns = time;
+		} else {
+			time_ns = time * int_pow(10, tstamp_scale);
+		}
+	}
+
+	scmi_iio_dev = sensor->indio_dev;
+	iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf,
+					   time_ns);
+	return NOTIFY_OK;
+}
+
+static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev)
+{
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+	u32 sensor_id = sensor->sensor_info->id;
+	u32 sensor_config = 0;
+	int err;
+
+	if (sensor->sensor_info->timestamped)
+		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
+					    SCMI_SENS_CFG_TSTAMP_ENABLE);
+
+	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
+				    SCMI_SENS_CFG_SENSOR_ENABLE);
+
+	err = sensor->handle->notify_ops->register_event_notifier(sensor->handle,
+			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
+			&sensor_id, &sensor->sensor_update_nb);
+	if (err) {
+		dev_err(&iio_dev->dev,
+			"Error in registering sensor update notifier for sensor %s err %d",
+			sensor->sensor_info->name, err);
+		return err;
+	}
+
+	err = sensor->handle->sensor_ops->config_set(sensor->handle,
+			sensor->sensor_info->id, sensor_config);
+	if (err) {
+		sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
+				SCMI_PROTOCOL_SENSOR,
+				SCMI_EVENT_SENSOR_UPDATE, &sensor_id,
+				&sensor->sensor_update_nb);
+		dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d",
+			sensor->sensor_info->name, err);
+	}
+
+	return err;
+}
+
+static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev)
+{
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+	u32 sensor_id = sensor->sensor_info->id;
+	u32 sensor_config = 0;
+	int err;
+
+	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
+				    SCMI_SENS_CFG_SENSOR_DISABLE);
+
+	err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
+			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
+			&sensor_id, &sensor->sensor_update_nb);
+	if (err) {
+		dev_err(&iio_dev->dev,
+			"Error in unregistering sensor update notifier for sensor %s err %d",
+			sensor->sensor_info->name, err);
+		return err;
+	}
+
+	err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id,
+						     sensor_config);
+	if (err) {
+		dev_err(&iio_dev->dev,
+			"Error in disabling sensor %s with err %d",
+			sensor->sensor_info->name, err);
+	}
+
+	return err;
+}
+
+static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
+	.preenable = scmi_iio_buffer_preenable,
+	.postdisable = scmi_iio_buffer_postdisable,
+};
+
+static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
+{
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+	const unsigned long UHZ_PER_HZ = 1000000UL;
+	u64 sec, mult, uHz, sf;
+	u32 sensor_config;
+	char buf[32];
+
+	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
+			sensor->sensor_info->id, &sensor_config);
+	if (err) {
+		dev_err(&iio_dev->dev,
+			"Error in getting sensor config for sensor %s err %d",
+			sensor->sensor_info->name, err);
+		return err;
+	}
+
+	uHz = val * UHZ_PER_HZ + val2;
+
+	/*
+	 * The seconds field in the sensor interval in SCMI is 16 bits long
+	 * Therefore seconds  = 1/Hz <= 0xFFFF. As floating point calculations are
+	 * discouraged in the kernel driver code, to calculate the scale factor (sf)
+	 * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000
+	 * To calculate the multiplier,we convert the sf into char string  and
+	 * count the number of characters
+	 */
+	sf = (u64)uHz * 0xFFFF;
+	do_div(sf,  UHZ_PER_HZ);
+	mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
+
+	sec = int_pow(10, mult) * UHZ_PER_HZ;
+	do_div(sec, uHz);
+	if (sec == 0) {
+		dev_err(&iio_dev->dev,
+			"Trying to set invalid sensor update value for sensor %s",
+			sensor->sensor_info->name);
+		return -EINVAL;
+	}
+
+	sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK;
+	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec);
+	sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK;
+	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult);
+
+	if (sensor->sensor_info->timestamped) {
+		sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK;
+		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
+					    SCMI_SENS_CFG_TSTAMP_ENABLE);
+	}
+
+	sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK;
+	sensor_config |=
+		FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO);
+
+	err = sensor->handle->sensor_ops->config_set(sensor->handle,
+			sensor->sensor_info->id, sensor_config);
+	if (err)
+		dev_err(&iio_dev->dev,
+			"Error in setting sensor update interval for sensor %s value %u err %d",
+			sensor->sensor_info->name, sensor_config, err);
+
+	return err;
+}
+
+static int scmi_iio_write_raw(struct iio_dev *iio_dev,
+			      struct iio_chan_spec const *chan, int val,
+			      int val2, long mask)
+{
+	int err;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		mutex_lock(&iio_dev->mlock);
+		err = scmi_iio_set_odr_val(iio_dev, val, val2);
+		mutex_unlock(&iio_dev->mlock);
+		return err;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int scmi_iio_read_avail(struct iio_dev *iio_dev,
+			       struct iio_chan_spec const *chan,
+			       const int **vals, int *type, int *length,
+			       long mask)
+{
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*vals = sensor->freq_avail;
+		*type = IIO_VAL_INT_PLUS_MICRO;
+		*length = sensor->sensor_info->intervals.count * 2;
+		if (sensor->sensor_info->intervals.segmented)
+			return IIO_AVAIL_RANGE;
+		else
+			return IIO_AVAIL_LIST;
+	default:
+		return -EINVAL;
+	}
+}
+
+static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz)
+{
+	u64 rem, freq;
+
+	freq = NSEC_PER_SEC;
+	rem = do_div(freq, interval_ns);
+	*hz = freq;
+	*uhz = rem * 1000000UL;
+	do_div(*uhz, interval_ns);
+}
+
+static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2)
+{
+	u64 sensor_update_interval, sensor_interval_mult, hz, uhz;
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+	u32 sensor_config;
+	int mult;
+
+	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
+			sensor->sensor_info->id, &sensor_config);
+	if (err) {
+		dev_err(&iio_dev->dev,
+			"Error in getting sensor config for sensor %s err %d",
+			sensor->sensor_info->name, err);
+		return err;
+	}
+
+	sensor_update_interval =
+		SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC;
+
+	mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config);
+	if (mult < 0) {
+		sensor_interval_mult = int_pow(10, abs(mult));
+		do_div(sensor_update_interval, sensor_interval_mult);
+	} else {
+		sensor_interval_mult = int_pow(10, mult);
+		sensor_update_interval =
+			sensor_update_interval * sensor_interval_mult;
+	}
+
+	convert_ns_to_freq(sensor_update_interval, &hz, &uhz);
+	*val = hz;
+	*val2 = uhz;
+	return 0;
+}
+
+static int scmi_iio_read_raw(struct iio_dev *iio_dev,
+			     struct iio_chan_spec const *ch, int *val,
+			     int *val2, long mask)
+{
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+	s8 scale;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		scale = sensor->sensor_info->axis[ch->scan_index].scale;
+		if (scale < 0) {
+			*val = 1;
+			*val2 = int_pow(10, abs(scale));
+			return IIO_VAL_FRACTIONAL;
+		}
+		*val = int_pow(10, scale);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = scmi_iio_get_odr_val(iio_dev, val, val2);
+		return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct iio_info scmi_iio_info = {
+	.read_raw = scmi_iio_read_raw,
+	.read_avail = scmi_iio_read_avail,
+	.write_raw = scmi_iio_write_raw,
+};
+
+static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
+					  uintptr_t private,
+					  const struct iio_chan_spec *chan,
+					  char *buf)
+{
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+	u64 resolution, rem;
+	s64 min_range, max_range;
+	s8 exponent, scale;
+	int len = 0;
+
+	/*
+	 * All the axes are supposed to have the same value for range and resolution.
+	 * We are just using the values from the Axis 0 here.
+	 */
+	if (sensor->sensor_info->axis[0].extended_attrs) {
+		min_range = sensor->sensor_info->axis[0].attrs.min_range;
+		max_range = sensor->sensor_info->axis[0].attrs.max_range;
+		resolution = sensor->sensor_info->axis[0].resolution;
+		exponent = sensor->sensor_info->axis[0].exponent;
+		scale = sensor->sensor_info->axis[0].scale;
+
+		/*
+		 * To provide the raw value for the resolution to the userspace,
+		 * need to divide the resolution exponent by the sensor scale
+		 */
+		exponent = exponent - scale;
+		if (exponent < 0) {
+			rem = do_div(resolution,
+				     int_pow(10, abs(exponent))
+				     );
+			len = scnprintf(buf, PAGE_SIZE,
+					"[%lld %llu.%llu %lld]\n", min_range,
+					resolution, rem, max_range);
+		} else {
+			resolution = resolution * int_pow(10, exponent);
+			len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
+					min_range, resolution, max_range);
+		}
+	}
+	return len;
+}
+
+static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = {
+	{
+		.name = "raw_available",
+		.read = scmi_iio_get_raw_available,
+		.shared = IIO_SHARED_BY_TYPE,
+	},
+	{},
+};
+
+static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan,
+					   int scan_index)
+{
+	iio_chan->type = IIO_TIMESTAMP;
+	iio_chan->channel = -1;
+	iio_chan->scan_index = scan_index;
+	iio_chan->scan_type.sign = 'u';
+	iio_chan->scan_type.realbits = 64;
+	iio_chan->scan_type.storagebits = 64;
+}
+
+static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan,
+				      enum iio_chan_type type,
+				      enum iio_modifier mod, int scan_index)
+{
+	iio_chan->type = type;
+	iio_chan->modified = 1;
+	iio_chan->channel2 = mod;
+	iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE);
+	iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
+	iio_chan->info_mask_shared_by_type_available =
+		BIT(IIO_CHAN_INFO_SAMP_FREQ);
+	iio_chan->scan_index = scan_index;
+	iio_chan->scan_type.sign = 's';
+	iio_chan->scan_type.realbits = 64;
+	iio_chan->scan_type.storagebits = 64;
+	iio_chan->scan_type.endianness = IIO_LE;
+	iio_chan->ext_info = scmi_iio_ext_info;
+}
+
+static int scmi_iio_get_chan_modifier(const char *name,
+				      enum iio_modifier *modifier)
+{
+	char *pch, mod;
+
+	if (!name)
+		return -EINVAL;
+
+	pch = strrchr(name, '_');
+	if (!pch)
+		return -EINVAL;
+
+	mod = *(pch + 1);
+	switch (mod) {
+	case 'X':
+		*modifier = IIO_MOD_X;
+		return 0;
+	case 'Y':
+		*modifier = IIO_MOD_Y;
+		return 0;
+	case 'Z':
+		*modifier = IIO_MOD_Z;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type)
+{
+	switch (scmi_type) {
+	case METERS_SEC_SQUARED:
+		*iio_type = IIO_ACCEL;
+		return 0;
+	case RADIANS_SEC:
+		*iio_type = IIO_ANGL_VEL;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static u64 scmi_iio_convert_interval_to_ns(u32 val)
+{
+	u64 sensor_update_interval =
+		SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC;
+	u64 sensor_interval_mult;
+	int mult;
+
+	mult = SCMI_SENS_INTVL_GET_EXP(val);
+	if (mult < 0) {
+		sensor_interval_mult = int_pow(10, abs(mult));
+		do_div(sensor_update_interval, sensor_interval_mult);
+	} else {
+		sensor_interval_mult = int_pow(10, mult);
+		sensor_update_interval =
+			sensor_update_interval * sensor_interval_mult;
+	}
+	return sensor_update_interval;
+}
+
+static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev)
+{
+	u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns,
+		hz, uhz;
+	unsigned int cur_interval, low_interval, high_interval, step_size;
+	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
+	int i;
+
+	sensor->freq_avail =
+		devm_kzalloc(&iio_dev->dev,
+			     sizeof(*sensor->freq_avail) *
+				     (sensor->sensor_info->intervals.count * 2),
+			     GFP_KERNEL);
+	if (!sensor->freq_avail)
+		return -ENOMEM;
+
+	if (sensor->sensor_info->intervals.segmented) {
+		low_interval = sensor->sensor_info->intervals
+				       .desc[SCMI_SENS_INTVL_SEGMENT_LOW];
+		low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval);
+		convert_ns_to_freq(low_interval_ns, &hz, &uhz);
+		sensor->freq_avail[0] = hz;
+		sensor->freq_avail[1] = uhz;
+
+		step_size = sensor->sensor_info->intervals
+				    .desc[SCMI_SENS_INTVL_SEGMENT_STEP];
+		step_size_ns = scmi_iio_convert_interval_to_ns(step_size);
+		convert_ns_to_freq(step_size_ns, &hz, &uhz);
+		sensor->freq_avail[2] = hz;
+		sensor->freq_avail[3] = uhz;
+
+		high_interval = sensor->sensor_info->intervals
+					.desc[SCMI_SENS_INTVL_SEGMENT_HIGH];
+		high_interval_ns =
+			scmi_iio_convert_interval_to_ns(high_interval);
+		convert_ns_to_freq(high_interval_ns, &hz, &uhz);
+		sensor->freq_avail[4] = hz;
+		sensor->freq_avail[5] = uhz;
+	} else {
+		for (i = 0; i < sensor->sensor_info->intervals.count; i++) {
+			cur_interval = sensor->sensor_info->intervals.desc[i];
+			cur_interval_ns =
+				scmi_iio_convert_interval_to_ns(cur_interval);
+			convert_ns_to_freq(cur_interval_ns, &hz, &uhz);
+			sensor->freq_avail[i * 2] = hz;
+			sensor->freq_avail[i * 2 + 1] = uhz;
+		}
+	}
+	return 0;
+}
+
+static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev)
+{
+	struct iio_buffer *buffer;
+
+	buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev);
+	if (!buffer)
+		return -ENOMEM;
+
+	iio_device_attach_buffer(scmi_iiodev, buffer);
+	scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE;
+	scmi_iiodev->setup_ops = &scmi_iio_buffer_ops;
+	return 0;
+}
+
+static struct iio_dev *scmi_alloc_iiodev(struct device *dev,
+					 struct scmi_handle *handle,
+					 const struct scmi_sensor_info *sensor_info)
+{
+	struct iio_chan_spec *iio_channels;
+	struct scmi_iio_priv *sensor;
+	enum iio_modifier modifier;
+	enum iio_chan_type type;
+	struct iio_dev *iiodev;
+	int i, ret;
+
+	iiodev = devm_iio_device_alloc(dev, sizeof(*sensor));
+	if (!iiodev)
+		return ERR_PTR(-ENOMEM);
+
+	iiodev->modes = INDIO_DIRECT_MODE;
+	iiodev->dev.parent = dev;
+	sensor = iio_priv(iiodev);
+	sensor->handle = handle;
+	sensor->sensor_info = sensor_info;
+	sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb;
+	sensor->indio_dev = iiodev;
+
+	/* adding one additional channel for timestamp */
+	iiodev->num_channels = sensor_info->num_axis + 1;
+	iiodev->name = sensor_info->name;
+	iiodev->info = &scmi_iio_info;
+
+	iio_channels =
+		devm_kzalloc(dev,
+			     sizeof(*iio_channels) * (iiodev->num_channels),
+			     GFP_KERNEL);
+	if (!iio_channels)
+		return ERR_PTR(-ENOMEM);
+
+	ret = scmi_iio_set_sampling_freq_avail(iiodev);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	for (i = 0; i < sensor_info->num_axis; i++) {
+		ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type);
+		if (ret < 0)
+			return ERR_PTR(ret);
+
+		ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name,
+						 &modifier);
+		if (ret < 0)
+			return ERR_PTR(ret);
+
+		scmi_iio_set_data_channel(&iio_channels[i], type, modifier,
+					  sensor_info->axis[i].id);
+	}
+
+	scmi_iio_set_timestamp_channel(&iio_channels[i], i);
+	iiodev->channels = iio_channels;
+	return iiodev;
+}
+
+static int scmi_iio_dev_probe(struct scmi_device *sdev)
+{
+	const struct scmi_sensor_info *sensor_info;
+	struct scmi_handle *handle = sdev->handle;
+	struct device *dev = &sdev->dev;
+	struct iio_dev *scmi_iio_dev;
+	u16 nr_sensors;
+	int err = -ENODEV, i;
+
+	if (!handle || !handle->sensor_ops) {
+		dev_err(dev, "SCMI device has no sensor interface\n");
+		return -EINVAL;
+	}
+
+	nr_sensors = handle->sensor_ops->count_get(handle);
+	if (!nr_sensors) {
+		dev_dbg(dev, "0 sensors found via SCMI bus\n");
+		return -ENODEV;
+	}
+
+	for (i = 0; i < nr_sensors; i++) {
+		sensor_info = handle->sensor_ops->info_get(handle, i);
+		if (!sensor_info) {
+			dev_err(dev, "SCMI sensor %d has missing info\n", i);
+			return -EINVAL;
+		}
+
+		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
+		if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS)
+			continue;
+
+		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
+		if (sensor_info->axis[0].type != METERS_SEC_SQUARED &&
+		    sensor_info->axis[0].type != RADIANS_SEC)
+			continue;
+
+		scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info);
+		if (IS_ERR(scmi_iio_dev)) {
+			dev_err(dev,
+				"failed to allocate IIO device for sensor %s: %ld\n",
+				sensor_info->name, PTR_ERR(scmi_iio_dev));
+			return PTR_ERR(scmi_iio_dev);
+		}
+
+		err = scmi_iio_buffers_setup(scmi_iio_dev);
+		if (err < 0) {
+			dev_err(dev,
+				"IIO buffer setup error at sensor %s: %d\n",
+				sensor_info->name, err);
+			return err;
+		}
+
+		err = devm_iio_device_register(dev, scmi_iio_dev);
+		if (err) {
+			dev_err(dev,
+				"IIO device registration failed at sensor %s: %d\n",
+				sensor_info->name, err);
+			return err;
+		}
+	}
+	return err;
+}
+
+static const struct scmi_device_id scmi_id_table[] = {
+	{ SCMI_PROTOCOL_SENSOR, "iiodev" },
+	{},
+};
+
+MODULE_DEVICE_TABLE(scmi, scmi_id_table);
+
+static struct scmi_driver scmi_iiodev_driver = {
+	.name = "scmi-sensor-iiodev",
+	.probe = scmi_iio_dev_probe,
+	.id_table = scmi_id_table,
+};
+
+module_scmi_driver(scmi_iiodev_driver);
+
+MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>");
+MODULE_DESCRIPTION("SCMI IIO Driver");
+MODULE_LICENSE("GPL v2");
-- 
2.30.1.766.gb4fecdf3b7-goog


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-09 23:12 ` [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors Jyoti Bhayana
@ 2021-03-10 11:15   ` Peter Hilber
  2021-03-10 17:19     ` Jyoti Bhayana
  2021-03-11 21:08   ` Jonathan Cameron
  1 sibling, 1 reply; 16+ messages in thread
From: Peter Hilber @ 2021-03-10 11:15 UTC (permalink / raw)
  To: Jyoti Bhayana, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn
  Cc: linux-kernel, linux-iio, cristian.marussi, sudeep.holla,
	egranata, mikhail.golubev, Igor.Skalkin, ankitarora,
	gurunagarajan, kernel test robot, Jonathan Cameron

On 10.03.21 00:12, Jyoti Bhayana wrote:
> This change provides ARM SCMI Protocol based IIO device.
> This driver provides support for Accelerometer and Gyroscope using
> SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> 

[snip]

> +
> +static int scmi_iio_get_chan_modifier(const char *name,
> +				      enum iio_modifier *modifier)
> +{
> +	char *pch, mod;
> +
> +	if (!name)
> +		return -EINVAL;
> +
> +	pch = strrchr(name, '_');
> +	if (!pch)
> +		return -EINVAL;
> +
> +	mod = *(pch + 1);
> +	switch (mod) {
> +	case 'X':
> +		*modifier = IIO_MOD_X;
> +		return 0;
> +	case 'Y':
> +		*modifier = IIO_MOD_Y;
> +		return 0;
> +	case 'Z':
> +		*modifier = IIO_MOD_Z;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +

Hi Jyoti,

could you still change the above code to also accept lower case 'x',
'y', 'z'?

Supporting lower case as well would establish compatibility with the
lower case naming conventions used for IIO channels. By this change,
channels could be forwarded without name changes (as long as they fit
into the name field). I'm sorry to notice this only now.

Best regards,

Peter


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-10 11:15   ` Peter Hilber
@ 2021-03-10 17:19     ` Jyoti Bhayana
  2021-03-11  9:55       ` Peter Hilber
  0 siblings, 1 reply; 16+ messages in thread
From: Jyoti Bhayana @ 2021-03-10 17:19 UTC (permalink / raw)
  To: Peter Hilber
  Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Mauro Carvalho Chehab, David S. Miller,
	Rob Herring, Lukas Bulwahn, linux-kernel, linux-iio,
	Cristian Marussi, Sudeep Holla, Enrico Granata, Mikhail Golubev,
	Igor Skalkin, Ankit Arora, Guru Nagarajan, kernel test robot,
	Jonathan Cameron

Hi Peter,

As already discussed with ARM, the spec clearly mentions that it has
to be uppercase and not case insensitive. So this patch is consistent
with the specs and changing it with means that the spec would need to
change as well. Therefore, there is no need for another version of the
patch

"A NULL terminated UTF-8 format string with the sensor axis name, of
up to 16 bytes. It is recommended that the name ends with ‘_’ followed
by the axis of the sensor in uppercase.
For example, the name for the x-axis of a triaxial accelerometer could
be “acc_X” or “_X"

Thanks,
Jyoti

On Wed, Mar 10, 2021 at 3:16 AM Peter Hilber
<peter.hilber@opensynergy.com> wrote:
>
> On 10.03.21 00:12, Jyoti Bhayana wrote:
> > This change provides ARM SCMI Protocol based IIO device.
> > This driver provides support for Accelerometer and Gyroscope using
> > SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> >
>
> [snip]
>
> > +
> > +static int scmi_iio_get_chan_modifier(const char *name,
> > +                                   enum iio_modifier *modifier)
> > +{
> > +     char *pch, mod;
> > +
> > +     if (!name)
> > +             return -EINVAL;
> > +
> > +     pch = strrchr(name, '_');
> > +     if (!pch)
> > +             return -EINVAL;
> > +
> > +     mod = *(pch + 1);
> > +     switch (mod) {
> > +     case 'X':
> > +             *modifier = IIO_MOD_X;
> > +             return 0;
> > +     case 'Y':
> > +             *modifier = IIO_MOD_Y;
> > +             return 0;
> > +     case 'Z':
> > +             *modifier = IIO_MOD_Z;
> > +             return 0;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
>
> Hi Jyoti,
>
> could you still change the above code to also accept lower case 'x',
> 'y', 'z'?
>
> Supporting lower case as well would establish compatibility with the
> lower case naming conventions used for IIO channels. By this change,
> channels could be forwarded without name changes (as long as they fit
> into the name field). I'm sorry to notice this only now.
>
> Best regards,
>
> Peter
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-10 17:19     ` Jyoti Bhayana
@ 2021-03-11  9:55       ` Peter Hilber
  0 siblings, 0 replies; 16+ messages in thread
From: Peter Hilber @ 2021-03-11  9:55 UTC (permalink / raw)
  To: Jyoti Bhayana
  Cc: Jonathan Cameron, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Mauro Carvalho Chehab, David S. Miller,
	Rob Herring, Lukas Bulwahn, linux-kernel, linux-iio,
	Cristian Marussi, Sudeep Holla, Enrico Granata, Mikhail Golubev,
	Igor Skalkin, Ankit Arora, Guru Nagarajan, kernel test robot,
	Jonathan Cameron

On 10.03.21 18:19, Jyoti Bhayana wrote:
> Hi Peter,
> 
> As already discussed with ARM, the spec clearly mentions that it has
> to be uppercase and not case insensitive. So this patch is consistent
> with the specs and changing it with means that the spec would need to
> change as well. Therefore, there is no need for another version of the
> patch
> 
> "A NULL terminated UTF-8 format string with the sensor axis name, of
> up to 16 bytes. It is recommended that the name ends with ‘_’ followed
> by the axis of the sensor in uppercase.
> For example, the name for the x-axis of a triaxial accelerometer could
> be “acc_X” or “_X"
> 

My understanding of the part of the spec quoted above is different: The
spec only makes a recommendation and does allow additional or even
contradictory sensor axis naming schemes. So the change to the
implementation only would in principle be possible (disregarding
integration timeline constraints).

Best regards,

Peter

> Thanks,
> Jyoti
> 
> On Wed, Mar 10, 2021 at 3:16 AM Peter Hilber
> <peter.hilber@opensynergy.com> wrote:
>>
>> On 10.03.21 00:12, Jyoti Bhayana wrote:
>>> This change provides ARM SCMI Protocol based IIO device.
>>> This driver provides support for Accelerometer and Gyroscope using
>>> SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
>>>
>>
>> [snip]
>>
>>> +
>>> +static int scmi_iio_get_chan_modifier(const char *name,
>>> +                                   enum iio_modifier *modifier)
>>> +{
>>> +     char *pch, mod;
>>> +
>>> +     if (!name)
>>> +             return -EINVAL;
>>> +
>>> +     pch = strrchr(name, '_');
>>> +     if (!pch)
>>> +             return -EINVAL;
>>> +
>>> +     mod = *(pch + 1);
>>> +     switch (mod) {
>>> +     case 'X':
>>> +             *modifier = IIO_MOD_X;
>>> +             return 0;
>>> +     case 'Y':
>>> +             *modifier = IIO_MOD_Y;
>>> +             return 0;
>>> +     case 'Z':
>>> +             *modifier = IIO_MOD_Z;
>>> +             return 0;
>>> +     default:
>>> +             return -EINVAL;
>>> +     }
>>> +}
>>> +
>>
>> Hi Jyoti,
>>
>> could you still change the above code to also accept lower case 'x',
>> 'y', 'z'?
>>
>> Supporting lower case as well would establish compatibility with the
>> lower case naming conventions used for IIO channels. By this change,
>> channels could be forwarded without name changes (as long as they fit
>> into the name field). I'm sorry to notice this only now.
>>
>> Best regards,
>>
>> Peter
>>
> 



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-09 23:12 ` [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors Jyoti Bhayana
  2021-03-10 11:15   ` Peter Hilber
@ 2021-03-11 21:08   ` Jonathan Cameron
  2021-03-12 12:16     ` Jonathan Cameron
  1 sibling, 1 reply; 16+ messages in thread
From: Jonathan Cameron @ 2021-03-11 21:08 UTC (permalink / raw)
  To: Jyoti Bhayana
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, cristian.marussi,
	sudeep.holla, egranata, mikhail.golubev, Igor.Skalkin,
	Peter.hilber, ankitarora, gurunagarajan, kernel test robot,
	Jonathan Cameron

On Tue,  9 Mar 2021 23:12:59 +0000
Jyoti Bhayana <jbhayana@google.com> wrote:

> This change provides ARM SCMI Protocol based IIO device.
> This driver provides support for Accelerometer and Gyroscope using
> SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> 
> Reported-by: kernel test robot <lkp@intel.com>
> Signed-off-by: Jyoti Bhayana <jbhayana@google.com>
> Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2

New immutable branch.  I move to rc2 as the base as the rc1 tag
has been removed for well publicized reasons.

Should have the 0-day results in tomorrow morning. I'll shout
if anything else shows up.

Also rebased the togreg branch for the same reason (hadn't pushed it
out as anything other than testing, so hopefully no one was basing
anything significant on top)

Thanks,

Jonathan

> ---
>  MAINTAINERS                                |   6 +
>  drivers/firmware/arm_scmi/driver.c         |   2 +-
>  drivers/iio/common/Kconfig                 |   1 +
>  drivers/iio/common/Makefile                |   1 +
>  drivers/iio/common/scmi_sensors/Kconfig    |  18 +
>  drivers/iio/common/scmi_sensors/Makefile   |   5 +
>  drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
>  7 files changed, 715 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
>  create mode 100644 drivers/iio/common/scmi_sensors/Makefile
>  create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d92f85ca831d..14227980f3d2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8692,6 +8692,12 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
>  F:	drivers/iio/multiplexer/iio-mux.c
>  
> +IIO SCMI BASED DRIVER
> +M:	Jyoti Bhayana <jbhayana@google.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Maintained
> +F:	drivers/iio/common/scmi_sensors/scmi_iio.c
> +
>  IIO SUBSYSTEM AND DRIVERS
>  M:	Jonathan Cameron <jic23@kernel.org>
>  R:	Lars-Peter Clausen <lars@metafoo.de>
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index cacdf1589b10..3e748e57deab 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = {
>  	{ SCMI_PROTOCOL_SYSTEM, { "syspower" },},
>  	{ SCMI_PROTOCOL_PERF,   { "cpufreq" },},
>  	{ SCMI_PROTOCOL_CLOCK,  { "clocks" },},
> -	{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> +	{ SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },},
>  	{ SCMI_PROTOCOL_RESET,  { "reset" },},
>  	{ SCMI_PROTOCOL_VOLTAGE,  { "regulator" },},
>  };
> diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> index 2b9ee9161abd..0334b4954773 100644
> --- a/drivers/iio/common/Kconfig
> +++ b/drivers/iio/common/Kconfig
> @@ -6,5 +6,6 @@
>  source "drivers/iio/common/cros_ec_sensors/Kconfig"
>  source "drivers/iio/common/hid-sensors/Kconfig"
>  source "drivers/iio/common/ms_sensors/Kconfig"
> +source "drivers/iio/common/scmi_sensors/Kconfig"
>  source "drivers/iio/common/ssp_sensors/Kconfig"
>  source "drivers/iio/common/st_sensors/Kconfig"
> diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> index 4bc30bb548e2..fad40e1e1718 100644
> --- a/drivers/iio/common/Makefile
> +++ b/drivers/iio/common/Makefile
> @@ -11,5 +11,6 @@
>  obj-y += cros_ec_sensors/
>  obj-y += hid-sensors/
>  obj-y += ms_sensors/
> +obj-y += scmi_sensors/
>  obj-y += ssp_sensors/
>  obj-y += st_sensors/
> diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig
> new file mode 100644
> index 000000000000..67e084cbb1ab
> --- /dev/null
> +++ b/drivers/iio/common/scmi_sensors/Kconfig
> @@ -0,0 +1,18 @@
> +#
> +# IIO over SCMI
> +#
> +# When adding new entries keep the list in alphabetical order
> +
> +menu "IIO SCMI Sensors"
> +
> +config IIO_SCMI
> +	tristate "IIO SCMI"
> +        depends on ARM_SCMI_PROTOCOL
> +        select IIO_BUFFER
> +        select IIO_KFIFO_BUF
> +	help
> +          Say yes here to build support for IIO SCMI Driver.
> +          This provides ARM SCMI Protocol based IIO device.
> +          This driver provides support for accelerometer and gyroscope
> +          sensors available on SCMI based platforms.
> +endmenu
> diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile
> new file mode 100644
> index 000000000000..f13140a2575a
> --- /dev/null
> +++ b/drivers/iio/common/scmi_sensors/Makefile
> @@ -0,0 +1,5 @@
> +# SPDX - License - Identifier : GPL - 2.0 - only
> +#
> +# Makefile for the IIO over SCMI
> +#
> +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o
> diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
> new file mode 100644
> index 000000000000..872d87ca6256
> --- /dev/null
> +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
> @@ -0,0 +1,683 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * System Control and Management Interface(SCMI) based IIO sensor driver
> + *
> + * Copyright (C) 2021 Google LLC
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/kernel.h>
> +#include <linux/kthread.h>
> +#include <linux/module.h>
> +#include <linux/scmi_protocol.h>
> +#include <linux/time.h>
> +#include <linux/types.h>
> +
> +#define SCMI_IIO_NUM_OF_AXIS 3
> +
> +struct scmi_iio_priv {
> +	struct scmi_handle *handle;
> +	const struct scmi_sensor_info *sensor_info;
> +	struct iio_dev *indio_dev;
> +	/* adding one additional channel for timestamp */
> +	s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1];
> +	struct notifier_block sensor_update_nb;
> +	u32 *freq_avail;
> +};
> +
> +static int scmi_iio_sensor_update_cb(struct notifier_block *nb,
> +				     unsigned long event, void *data)
> +{
> +	struct scmi_sensor_update_report *sensor_update = data;
> +	struct iio_dev *scmi_iio_dev;
> +	struct scmi_iio_priv *sensor;
> +	s8 tstamp_scale;
> +	u64 time, time_ns;
> +	int i;
> +
> +	if (sensor_update->readings_count == 0)
> +		return NOTIFY_DONE;
> +
> +	sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb);
> +
> +	for (i = 0; i < sensor_update->readings_count; i++)
> +		sensor->iio_buf[i] = sensor_update->readings[i].value;
> +
> +	if (!sensor->sensor_info->timestamped) {
> +		time_ns = ktime_to_ns(sensor_update->timestamp);
> +	} else {
> +		/*
> +		 *  All the axes are supposed to have the same value for timestamp.
> +		 *  We are just using the values from the Axis 0 here.
> +		 */
> +		time = sensor_update->readings[0].timestamp;
> +
> +		/*
> +		 *  Timestamp returned by SCMI is in seconds and is equal to
> +		 *  time * power-of-10 multiplier(tstamp_scale) seconds.
> +		 *  Converting the timestamp to nanoseconds below.
> +		 */
> +		tstamp_scale = sensor->sensor_info->tstamp_scale +
> +			       const_ilog2(NSEC_PER_SEC) / const_ilog2(10);
> +		if (tstamp_scale < 0) {
> +			do_div(time, int_pow(10, abs(tstamp_scale)));
> +			time_ns = time;
> +		} else {
> +			time_ns = time * int_pow(10, tstamp_scale);
> +		}
> +	}
> +
> +	scmi_iio_dev = sensor->indio_dev;
> +	iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf,
> +					   time_ns);
> +	return NOTIFY_OK;
> +}
> +
> +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev)
> +{
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +	u32 sensor_id = sensor->sensor_info->id;
> +	u32 sensor_config = 0;
> +	int err;
> +
> +	if (sensor->sensor_info->timestamped)
> +		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> +					    SCMI_SENS_CFG_TSTAMP_ENABLE);
> +
> +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> +				    SCMI_SENS_CFG_SENSOR_ENABLE);
> +
> +	err = sensor->handle->notify_ops->register_event_notifier(sensor->handle,
> +			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> +			&sensor_id, &sensor->sensor_update_nb);
> +	if (err) {
> +		dev_err(&iio_dev->dev,
> +			"Error in registering sensor update notifier for sensor %s err %d",
> +			sensor->sensor_info->name, err);
> +		return err;
> +	}
> +
> +	err = sensor->handle->sensor_ops->config_set(sensor->handle,
> +			sensor->sensor_info->id, sensor_config);
> +	if (err) {
> +		sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> +				SCMI_PROTOCOL_SENSOR,
> +				SCMI_EVENT_SENSOR_UPDATE, &sensor_id,
> +				&sensor->sensor_update_nb);
> +		dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d",
> +			sensor->sensor_info->name, err);
> +	}
> +
> +	return err;
> +}
> +
> +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev)
> +{
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +	u32 sensor_id = sensor->sensor_info->id;
> +	u32 sensor_config = 0;
> +	int err;
> +
> +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> +				    SCMI_SENS_CFG_SENSOR_DISABLE);
> +
> +	err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> +			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> +			&sensor_id, &sensor->sensor_update_nb);
> +	if (err) {
> +		dev_err(&iio_dev->dev,
> +			"Error in unregistering sensor update notifier for sensor %s err %d",
> +			sensor->sensor_info->name, err);
> +		return err;
> +	}
> +
> +	err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id,
> +						     sensor_config);
> +	if (err) {
> +		dev_err(&iio_dev->dev,
> +			"Error in disabling sensor %s with err %d",
> +			sensor->sensor_info->name, err);
> +	}
> +
> +	return err;
> +}
> +
> +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
> +	.preenable = scmi_iio_buffer_preenable,
> +	.postdisable = scmi_iio_buffer_postdisable,
> +};
> +
> +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
> +{
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +	const unsigned long UHZ_PER_HZ = 1000000UL;
> +	u64 sec, mult, uHz, sf;
> +	u32 sensor_config;
> +	char buf[32];
> +
> +	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> +			sensor->sensor_info->id, &sensor_config);
> +	if (err) {
> +		dev_err(&iio_dev->dev,
> +			"Error in getting sensor config for sensor %s err %d",
> +			sensor->sensor_info->name, err);
> +		return err;
> +	}
> +
> +	uHz = val * UHZ_PER_HZ + val2;
> +
> +	/*
> +	 * The seconds field in the sensor interval in SCMI is 16 bits long
> +	 * Therefore seconds  = 1/Hz <= 0xFFFF. As floating point calculations are
> +	 * discouraged in the kernel driver code, to calculate the scale factor (sf)
> +	 * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000
> +	 * To calculate the multiplier,we convert the sf into char string  and
> +	 * count the number of characters
> +	 */
> +	sf = (u64)uHz * 0xFFFF;
> +	do_div(sf,  UHZ_PER_HZ);
> +	mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
> +
> +	sec = int_pow(10, mult) * UHZ_PER_HZ;
> +	do_div(sec, uHz);
> +	if (sec == 0) {
> +		dev_err(&iio_dev->dev,
> +			"Trying to set invalid sensor update value for sensor %s",
> +			sensor->sensor_info->name);
> +		return -EINVAL;
> +	}
> +
> +	sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK;
> +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec);
> +	sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK;
> +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult);
> +
> +	if (sensor->sensor_info->timestamped) {
> +		sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK;
> +		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> +					    SCMI_SENS_CFG_TSTAMP_ENABLE);
> +	}
> +
> +	sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK;
> +	sensor_config |=
> +		FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO);
> +
> +	err = sensor->handle->sensor_ops->config_set(sensor->handle,
> +			sensor->sensor_info->id, sensor_config);
> +	if (err)
> +		dev_err(&iio_dev->dev,
> +			"Error in setting sensor update interval for sensor %s value %u err %d",
> +			sensor->sensor_info->name, sensor_config, err);
> +
> +	return err;
> +}
> +
> +static int scmi_iio_write_raw(struct iio_dev *iio_dev,
> +			      struct iio_chan_spec const *chan, int val,
> +			      int val2, long mask)
> +{
> +	int err;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		mutex_lock(&iio_dev->mlock);
> +		err = scmi_iio_set_odr_val(iio_dev, val, val2);
> +		mutex_unlock(&iio_dev->mlock);
> +		return err;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int scmi_iio_read_avail(struct iio_dev *iio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       const int **vals, int *type, int *length,
> +			       long mask)
> +{
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*vals = sensor->freq_avail;
> +		*type = IIO_VAL_INT_PLUS_MICRO;
> +		*length = sensor->sensor_info->intervals.count * 2;
> +		if (sensor->sensor_info->intervals.segmented)
> +			return IIO_AVAIL_RANGE;
> +		else
> +			return IIO_AVAIL_LIST;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz)
> +{
> +	u64 rem, freq;
> +
> +	freq = NSEC_PER_SEC;
> +	rem = do_div(freq, interval_ns);
> +	*hz = freq;
> +	*uhz = rem * 1000000UL;
> +	do_div(*uhz, interval_ns);
> +}
> +
> +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2)
> +{
> +	u64 sensor_update_interval, sensor_interval_mult, hz, uhz;
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +	u32 sensor_config;
> +	int mult;
> +
> +	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> +			sensor->sensor_info->id, &sensor_config);
> +	if (err) {
> +		dev_err(&iio_dev->dev,
> +			"Error in getting sensor config for sensor %s err %d",
> +			sensor->sensor_info->name, err);
> +		return err;
> +	}
> +
> +	sensor_update_interval =
> +		SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC;
> +
> +	mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config);
> +	if (mult < 0) {
> +		sensor_interval_mult = int_pow(10, abs(mult));
> +		do_div(sensor_update_interval, sensor_interval_mult);
> +	} else {
> +		sensor_interval_mult = int_pow(10, mult);
> +		sensor_update_interval =
> +			sensor_update_interval * sensor_interval_mult;
> +	}
> +
> +	convert_ns_to_freq(sensor_update_interval, &hz, &uhz);
> +	*val = hz;
> +	*val2 = uhz;
> +	return 0;
> +}
> +
> +static int scmi_iio_read_raw(struct iio_dev *iio_dev,
> +			     struct iio_chan_spec const *ch, int *val,
> +			     int *val2, long mask)
> +{
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +	s8 scale;
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		scale = sensor->sensor_info->axis[ch->scan_index].scale;
> +		if (scale < 0) {
> +			*val = 1;
> +			*val2 = int_pow(10, abs(scale));
> +			return IIO_VAL_FRACTIONAL;
> +		}
> +		*val = int_pow(10, scale);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = scmi_iio_get_odr_val(iio_dev, val, val2);
> +		return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static const struct iio_info scmi_iio_info = {
> +	.read_raw = scmi_iio_read_raw,
> +	.read_avail = scmi_iio_read_avail,
> +	.write_raw = scmi_iio_write_raw,
> +};
> +
> +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
> +					  uintptr_t private,
> +					  const struct iio_chan_spec *chan,
> +					  char *buf)
> +{
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +	u64 resolution, rem;
> +	s64 min_range, max_range;
> +	s8 exponent, scale;
> +	int len = 0;
> +
> +	/*
> +	 * All the axes are supposed to have the same value for range and resolution.
> +	 * We are just using the values from the Axis 0 here.
> +	 */
> +	if (sensor->sensor_info->axis[0].extended_attrs) {
> +		min_range = sensor->sensor_info->axis[0].attrs.min_range;
> +		max_range = sensor->sensor_info->axis[0].attrs.max_range;
> +		resolution = sensor->sensor_info->axis[0].resolution;
> +		exponent = sensor->sensor_info->axis[0].exponent;
> +		scale = sensor->sensor_info->axis[0].scale;
> +
> +		/*
> +		 * To provide the raw value for the resolution to the userspace,
> +		 * need to divide the resolution exponent by the sensor scale
> +		 */
> +		exponent = exponent - scale;
> +		if (exponent < 0) {
> +			rem = do_div(resolution,
> +				     int_pow(10, abs(exponent))
> +				     );
> +			len = scnprintf(buf, PAGE_SIZE,
> +					"[%lld %llu.%llu %lld]\n", min_range,
> +					resolution, rem, max_range);
> +		} else {
> +			resolution = resolution * int_pow(10, exponent);
> +			len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
> +					min_range, resolution, max_range);
> +		}
> +	}
> +	return len;
> +}
> +
> +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = {
> +	{
> +		.name = "raw_available",
> +		.read = scmi_iio_get_raw_available,
> +		.shared = IIO_SHARED_BY_TYPE,
> +	},
> +	{},
> +};
> +
> +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan,
> +					   int scan_index)
> +{
> +	iio_chan->type = IIO_TIMESTAMP;
> +	iio_chan->channel = -1;
> +	iio_chan->scan_index = scan_index;
> +	iio_chan->scan_type.sign = 'u';
> +	iio_chan->scan_type.realbits = 64;
> +	iio_chan->scan_type.storagebits = 64;
> +}
> +
> +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan,
> +				      enum iio_chan_type type,
> +				      enum iio_modifier mod, int scan_index)
> +{
> +	iio_chan->type = type;
> +	iio_chan->modified = 1;
> +	iio_chan->channel2 = mod;
> +	iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE);
> +	iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> +	iio_chan->info_mask_shared_by_type_available =
> +		BIT(IIO_CHAN_INFO_SAMP_FREQ);
> +	iio_chan->scan_index = scan_index;
> +	iio_chan->scan_type.sign = 's';
> +	iio_chan->scan_type.realbits = 64;
> +	iio_chan->scan_type.storagebits = 64;
> +	iio_chan->scan_type.endianness = IIO_LE;
> +	iio_chan->ext_info = scmi_iio_ext_info;
> +}
> +
> +static int scmi_iio_get_chan_modifier(const char *name,
> +				      enum iio_modifier *modifier)
> +{
> +	char *pch, mod;
> +
> +	if (!name)
> +		return -EINVAL;
> +
> +	pch = strrchr(name, '_');
> +	if (!pch)
> +		return -EINVAL;
> +
> +	mod = *(pch + 1);
> +	switch (mod) {
> +	case 'X':
> +		*modifier = IIO_MOD_X;
> +		return 0;
> +	case 'Y':
> +		*modifier = IIO_MOD_Y;
> +		return 0;
> +	case 'Z':
> +		*modifier = IIO_MOD_Z;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type)
> +{
> +	switch (scmi_type) {
> +	case METERS_SEC_SQUARED:
> +		*iio_type = IIO_ACCEL;
> +		return 0;
> +	case RADIANS_SEC:
> +		*iio_type = IIO_ANGL_VEL;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static u64 scmi_iio_convert_interval_to_ns(u32 val)
> +{
> +	u64 sensor_update_interval =
> +		SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC;
> +	u64 sensor_interval_mult;
> +	int mult;
> +
> +	mult = SCMI_SENS_INTVL_GET_EXP(val);
> +	if (mult < 0) {
> +		sensor_interval_mult = int_pow(10, abs(mult));
> +		do_div(sensor_update_interval, sensor_interval_mult);
> +	} else {
> +		sensor_interval_mult = int_pow(10, mult);
> +		sensor_update_interval =
> +			sensor_update_interval * sensor_interval_mult;
> +	}
> +	return sensor_update_interval;
> +}
> +
> +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev)
> +{
> +	u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns,
> +		hz, uhz;
> +	unsigned int cur_interval, low_interval, high_interval, step_size;
> +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> +	int i;
> +
> +	sensor->freq_avail =
> +		devm_kzalloc(&iio_dev->dev,
> +			     sizeof(*sensor->freq_avail) *
> +				     (sensor->sensor_info->intervals.count * 2),
> +			     GFP_KERNEL);
> +	if (!sensor->freq_avail)
> +		return -ENOMEM;
> +
> +	if (sensor->sensor_info->intervals.segmented) {
> +		low_interval = sensor->sensor_info->intervals
> +				       .desc[SCMI_SENS_INTVL_SEGMENT_LOW];
> +		low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval);
> +		convert_ns_to_freq(low_interval_ns, &hz, &uhz);
> +		sensor->freq_avail[0] = hz;
> +		sensor->freq_avail[1] = uhz;
> +
> +		step_size = sensor->sensor_info->intervals
> +				    .desc[SCMI_SENS_INTVL_SEGMENT_STEP];
> +		step_size_ns = scmi_iio_convert_interval_to_ns(step_size);
> +		convert_ns_to_freq(step_size_ns, &hz, &uhz);
> +		sensor->freq_avail[2] = hz;
> +		sensor->freq_avail[3] = uhz;
> +
> +		high_interval = sensor->sensor_info->intervals
> +					.desc[SCMI_SENS_INTVL_SEGMENT_HIGH];
> +		high_interval_ns =
> +			scmi_iio_convert_interval_to_ns(high_interval);
> +		convert_ns_to_freq(high_interval_ns, &hz, &uhz);
> +		sensor->freq_avail[4] = hz;
> +		sensor->freq_avail[5] = uhz;
> +	} else {
> +		for (i = 0; i < sensor->sensor_info->intervals.count; i++) {
> +			cur_interval = sensor->sensor_info->intervals.desc[i];
> +			cur_interval_ns =
> +				scmi_iio_convert_interval_to_ns(cur_interval);
> +			convert_ns_to_freq(cur_interval_ns, &hz, &uhz);
> +			sensor->freq_avail[i * 2] = hz;
> +			sensor->freq_avail[i * 2 + 1] = uhz;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev)
> +{
> +	struct iio_buffer *buffer;
> +
> +	buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev);
> +	if (!buffer)
> +		return -ENOMEM;
> +
> +	iio_device_attach_buffer(scmi_iiodev, buffer);
> +	scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE;
> +	scmi_iiodev->setup_ops = &scmi_iio_buffer_ops;
> +	return 0;
> +}
> +
> +static struct iio_dev *scmi_alloc_iiodev(struct device *dev,
> +					 struct scmi_handle *handle,
> +					 const struct scmi_sensor_info *sensor_info)
> +{
> +	struct iio_chan_spec *iio_channels;
> +	struct scmi_iio_priv *sensor;
> +	enum iio_modifier modifier;
> +	enum iio_chan_type type;
> +	struct iio_dev *iiodev;
> +	int i, ret;
> +
> +	iiodev = devm_iio_device_alloc(dev, sizeof(*sensor));
> +	if (!iiodev)
> +		return ERR_PTR(-ENOMEM);
> +
> +	iiodev->modes = INDIO_DIRECT_MODE;
> +	iiodev->dev.parent = dev;
> +	sensor = iio_priv(iiodev);
> +	sensor->handle = handle;
> +	sensor->sensor_info = sensor_info;
> +	sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb;
> +	sensor->indio_dev = iiodev;
> +
> +	/* adding one additional channel for timestamp */
> +	iiodev->num_channels = sensor_info->num_axis + 1;
> +	iiodev->name = sensor_info->name;
> +	iiodev->info = &scmi_iio_info;
> +
> +	iio_channels =
> +		devm_kzalloc(dev,
> +			     sizeof(*iio_channels) * (iiodev->num_channels),
> +			     GFP_KERNEL);
> +	if (!iio_channels)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ret = scmi_iio_set_sampling_freq_avail(iiodev);
> +	if (ret < 0)
> +		return ERR_PTR(ret);
> +
> +	for (i = 0; i < sensor_info->num_axis; i++) {
> +		ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type);
> +		if (ret < 0)
> +			return ERR_PTR(ret);
> +
> +		ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name,
> +						 &modifier);
> +		if (ret < 0)
> +			return ERR_PTR(ret);
> +
> +		scmi_iio_set_data_channel(&iio_channels[i], type, modifier,
> +					  sensor_info->axis[i].id);
> +	}
> +
> +	scmi_iio_set_timestamp_channel(&iio_channels[i], i);
> +	iiodev->channels = iio_channels;
> +	return iiodev;
> +}
> +
> +static int scmi_iio_dev_probe(struct scmi_device *sdev)
> +{
> +	const struct scmi_sensor_info *sensor_info;
> +	struct scmi_handle *handle = sdev->handle;
> +	struct device *dev = &sdev->dev;
> +	struct iio_dev *scmi_iio_dev;
> +	u16 nr_sensors;
> +	int err = -ENODEV, i;
> +
> +	if (!handle || !handle->sensor_ops) {
> +		dev_err(dev, "SCMI device has no sensor interface\n");
> +		return -EINVAL;
> +	}
> +
> +	nr_sensors = handle->sensor_ops->count_get(handle);
> +	if (!nr_sensors) {
> +		dev_dbg(dev, "0 sensors found via SCMI bus\n");
> +		return -ENODEV;
> +	}
> +
> +	for (i = 0; i < nr_sensors; i++) {
> +		sensor_info = handle->sensor_ops->info_get(handle, i);
> +		if (!sensor_info) {
> +			dev_err(dev, "SCMI sensor %d has missing info\n", i);
> +			return -EINVAL;
> +		}
> +
> +		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
> +		if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS)
> +			continue;
> +
> +		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
> +		if (sensor_info->axis[0].type != METERS_SEC_SQUARED &&
> +		    sensor_info->axis[0].type != RADIANS_SEC)
> +			continue;
> +
> +		scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info);
> +		if (IS_ERR(scmi_iio_dev)) {
> +			dev_err(dev,
> +				"failed to allocate IIO device for sensor %s: %ld\n",
> +				sensor_info->name, PTR_ERR(scmi_iio_dev));
> +			return PTR_ERR(scmi_iio_dev);
> +		}
> +
> +		err = scmi_iio_buffers_setup(scmi_iio_dev);
> +		if (err < 0) {
> +			dev_err(dev,
> +				"IIO buffer setup error at sensor %s: %d\n",
> +				sensor_info->name, err);
> +			return err;
> +		}
> +
> +		err = devm_iio_device_register(dev, scmi_iio_dev);
> +		if (err) {
> +			dev_err(dev,
> +				"IIO device registration failed at sensor %s: %d\n",
> +				sensor_info->name, err);
> +			return err;
> +		}
> +	}
> +	return err;
> +}
> +
> +static const struct scmi_device_id scmi_id_table[] = {
> +	{ SCMI_PROTOCOL_SENSOR, "iiodev" },
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> +
> +static struct scmi_driver scmi_iiodev_driver = {
> +	.name = "scmi-sensor-iiodev",
> +	.probe = scmi_iio_dev_probe,
> +	.id_table = scmi_id_table,
> +};
> +
> +module_scmi_driver(scmi_iiodev_driver);
> +
> +MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>");
> +MODULE_DESCRIPTION("SCMI IIO Driver");
> +MODULE_LICENSE("GPL v2");


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-11 21:08   ` Jonathan Cameron
@ 2021-03-12 12:16     ` Jonathan Cameron
  2021-03-12 13:31       ` Cristian Marussi
  0 siblings, 1 reply; 16+ messages in thread
From: Jonathan Cameron @ 2021-03-12 12:16 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jyoti Bhayana, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Mauro Carvalho Chehab, David S. Miller,
	Rob Herring, Lukas Bulwahn, linux-kernel, linux-iio,
	cristian.marussi, sudeep.holla, egranata, mikhail.golubev,
	Igor.Skalkin, Peter.hilber, ankitarora, gurunagarajan,
	kernel test robot

On Thu, 11 Mar 2021 21:08:44 +0000
Jonathan Cameron <jic23@kernel.org> wrote:

> On Tue,  9 Mar 2021 23:12:59 +0000
> Jyoti Bhayana <jbhayana@google.com> wrote:
> 
> > This change provides ARM SCMI Protocol based IIO device.
> > This driver provides support for Accelerometer and Gyroscope using
> > SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> > 
> > Reported-by: kernel test robot <lkp@intel.com>
> > Signed-off-by: Jyoti Bhayana <jbhayana@google.com>
> > Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com
> > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>  
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
> 
> New immutable branch.  I move to rc2 as the base as the rc1 tag
> has been removed for well publicized reasons.
> 
> Should have the 0-day results in tomorrow morning. I'll shout
> if anything else shows up.
0-day is happy, so this is now immutable and suitable for pulling in through
whatever trees need it.

Thanks,

Jonathan

> 
> Also rebased the togreg branch for the same reason (hadn't pushed it
> out as anything other than testing, so hopefully no one was basing
> anything significant on top)
> 
> Thanks,
> 
> Jonathan
> 
> > ---
> >  MAINTAINERS                                |   6 +
> >  drivers/firmware/arm_scmi/driver.c         |   2 +-
> >  drivers/iio/common/Kconfig                 |   1 +
> >  drivers/iio/common/Makefile                |   1 +
> >  drivers/iio/common/scmi_sensors/Kconfig    |  18 +
> >  drivers/iio/common/scmi_sensors/Makefile   |   5 +
> >  drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
> >  7 files changed, 715 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
> >  create mode 100644 drivers/iio/common/scmi_sensors/Makefile
> >  create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c
> > 
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index d92f85ca831d..14227980f3d2 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -8692,6 +8692,12 @@ S:	Maintained
> >  F:	Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
> >  F:	drivers/iio/multiplexer/iio-mux.c
> >  
> > +IIO SCMI BASED DRIVER
> > +M:	Jyoti Bhayana <jbhayana@google.com>
> > +L:	linux-iio@vger.kernel.org
> > +S:	Maintained
> > +F:	drivers/iio/common/scmi_sensors/scmi_iio.c
> > +
> >  IIO SUBSYSTEM AND DRIVERS
> >  M:	Jonathan Cameron <jic23@kernel.org>
> >  R:	Lars-Peter Clausen <lars@metafoo.de>
> > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > index cacdf1589b10..3e748e57deab 100644
> > --- a/drivers/firmware/arm_scmi/driver.c
> > +++ b/drivers/firmware/arm_scmi/driver.c
> > @@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = {
> >  	{ SCMI_PROTOCOL_SYSTEM, { "syspower" },},
> >  	{ SCMI_PROTOCOL_PERF,   { "cpufreq" },},
> >  	{ SCMI_PROTOCOL_CLOCK,  { "clocks" },},
> > -	{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> > +	{ SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },},
> >  	{ SCMI_PROTOCOL_RESET,  { "reset" },},
> >  	{ SCMI_PROTOCOL_VOLTAGE,  { "regulator" },},
> >  };
> > diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> > index 2b9ee9161abd..0334b4954773 100644
> > --- a/drivers/iio/common/Kconfig
> > +++ b/drivers/iio/common/Kconfig
> > @@ -6,5 +6,6 @@
> >  source "drivers/iio/common/cros_ec_sensors/Kconfig"
> >  source "drivers/iio/common/hid-sensors/Kconfig"
> >  source "drivers/iio/common/ms_sensors/Kconfig"
> > +source "drivers/iio/common/scmi_sensors/Kconfig"
> >  source "drivers/iio/common/ssp_sensors/Kconfig"
> >  source "drivers/iio/common/st_sensors/Kconfig"
> > diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> > index 4bc30bb548e2..fad40e1e1718 100644
> > --- a/drivers/iio/common/Makefile
> > +++ b/drivers/iio/common/Makefile
> > @@ -11,5 +11,6 @@
> >  obj-y += cros_ec_sensors/
> >  obj-y += hid-sensors/
> >  obj-y += ms_sensors/
> > +obj-y += scmi_sensors/
> >  obj-y += ssp_sensors/
> >  obj-y += st_sensors/
> > diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig
> > new file mode 100644
> > index 000000000000..67e084cbb1ab
> > --- /dev/null
> > +++ b/drivers/iio/common/scmi_sensors/Kconfig
> > @@ -0,0 +1,18 @@
> > +#
> > +# IIO over SCMI
> > +#
> > +# When adding new entries keep the list in alphabetical order
> > +
> > +menu "IIO SCMI Sensors"
> > +
> > +config IIO_SCMI
> > +	tristate "IIO SCMI"
> > +        depends on ARM_SCMI_PROTOCOL
> > +        select IIO_BUFFER
> > +        select IIO_KFIFO_BUF
> > +	help
> > +          Say yes here to build support for IIO SCMI Driver.
> > +          This provides ARM SCMI Protocol based IIO device.
> > +          This driver provides support for accelerometer and gyroscope
> > +          sensors available on SCMI based platforms.
> > +endmenu
> > diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile
> > new file mode 100644
> > index 000000000000..f13140a2575a
> > --- /dev/null
> > +++ b/drivers/iio/common/scmi_sensors/Makefile
> > @@ -0,0 +1,5 @@
> > +# SPDX - License - Identifier : GPL - 2.0 - only
> > +#
> > +# Makefile for the IIO over SCMI
> > +#
> > +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o
> > diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > new file mode 100644
> > index 000000000000..872d87ca6256
> > --- /dev/null
> > +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > @@ -0,0 +1,683 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +/*
> > + * System Control and Management Interface(SCMI) based IIO sensor driver
> > + *
> > + * Copyright (C) 2021 Google LLC
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/err.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/kfifo_buf.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/kernel.h>
> > +#include <linux/kthread.h>
> > +#include <linux/module.h>
> > +#include <linux/scmi_protocol.h>
> > +#include <linux/time.h>
> > +#include <linux/types.h>
> > +
> > +#define SCMI_IIO_NUM_OF_AXIS 3
> > +
> > +struct scmi_iio_priv {
> > +	struct scmi_handle *handle;
> > +	const struct scmi_sensor_info *sensor_info;
> > +	struct iio_dev *indio_dev;
> > +	/* adding one additional channel for timestamp */
> > +	s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1];
> > +	struct notifier_block sensor_update_nb;
> > +	u32 *freq_avail;
> > +};
> > +
> > +static int scmi_iio_sensor_update_cb(struct notifier_block *nb,
> > +				     unsigned long event, void *data)
> > +{
> > +	struct scmi_sensor_update_report *sensor_update = data;
> > +	struct iio_dev *scmi_iio_dev;
> > +	struct scmi_iio_priv *sensor;
> > +	s8 tstamp_scale;
> > +	u64 time, time_ns;
> > +	int i;
> > +
> > +	if (sensor_update->readings_count == 0)
> > +		return NOTIFY_DONE;
> > +
> > +	sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb);
> > +
> > +	for (i = 0; i < sensor_update->readings_count; i++)
> > +		sensor->iio_buf[i] = sensor_update->readings[i].value;
> > +
> > +	if (!sensor->sensor_info->timestamped) {
> > +		time_ns = ktime_to_ns(sensor_update->timestamp);
> > +	} else {
> > +		/*
> > +		 *  All the axes are supposed to have the same value for timestamp.
> > +		 *  We are just using the values from the Axis 0 here.
> > +		 */
> > +		time = sensor_update->readings[0].timestamp;
> > +
> > +		/*
> > +		 *  Timestamp returned by SCMI is in seconds and is equal to
> > +		 *  time * power-of-10 multiplier(tstamp_scale) seconds.
> > +		 *  Converting the timestamp to nanoseconds below.
> > +		 */
> > +		tstamp_scale = sensor->sensor_info->tstamp_scale +
> > +			       const_ilog2(NSEC_PER_SEC) / const_ilog2(10);
> > +		if (tstamp_scale < 0) {
> > +			do_div(time, int_pow(10, abs(tstamp_scale)));
> > +			time_ns = time;
> > +		} else {
> > +			time_ns = time * int_pow(10, tstamp_scale);
> > +		}
> > +	}
> > +
> > +	scmi_iio_dev = sensor->indio_dev;
> > +	iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf,
> > +					   time_ns);
> > +	return NOTIFY_OK;
> > +}
> > +
> > +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev)
> > +{
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +	u32 sensor_id = sensor->sensor_info->id;
> > +	u32 sensor_config = 0;
> > +	int err;
> > +
> > +	if (sensor->sensor_info->timestamped)
> > +		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > +					    SCMI_SENS_CFG_TSTAMP_ENABLE);
> > +
> > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > +				    SCMI_SENS_CFG_SENSOR_ENABLE);
> > +
> > +	err = sensor->handle->notify_ops->register_event_notifier(sensor->handle,
> > +			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > +			&sensor_id, &sensor->sensor_update_nb);
> > +	if (err) {
> > +		dev_err(&iio_dev->dev,
> > +			"Error in registering sensor update notifier for sensor %s err %d",
> > +			sensor->sensor_info->name, err);
> > +		return err;
> > +	}
> > +
> > +	err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > +			sensor->sensor_info->id, sensor_config);
> > +	if (err) {
> > +		sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > +				SCMI_PROTOCOL_SENSOR,
> > +				SCMI_EVENT_SENSOR_UPDATE, &sensor_id,
> > +				&sensor->sensor_update_nb);
> > +		dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d",
> > +			sensor->sensor_info->name, err);
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev)
> > +{
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +	u32 sensor_id = sensor->sensor_info->id;
> > +	u32 sensor_config = 0;
> > +	int err;
> > +
> > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > +				    SCMI_SENS_CFG_SENSOR_DISABLE);
> > +
> > +	err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > +			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > +			&sensor_id, &sensor->sensor_update_nb);
> > +	if (err) {
> > +		dev_err(&iio_dev->dev,
> > +			"Error in unregistering sensor update notifier for sensor %s err %d",
> > +			sensor->sensor_info->name, err);
> > +		return err;
> > +	}
> > +
> > +	err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id,
> > +						     sensor_config);
> > +	if (err) {
> > +		dev_err(&iio_dev->dev,
> > +			"Error in disabling sensor %s with err %d",
> > +			sensor->sensor_info->name, err);
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
> > +	.preenable = scmi_iio_buffer_preenable,
> > +	.postdisable = scmi_iio_buffer_postdisable,
> > +};
> > +
> > +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
> > +{
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +	const unsigned long UHZ_PER_HZ = 1000000UL;
> > +	u64 sec, mult, uHz, sf;
> > +	u32 sensor_config;
> > +	char buf[32];
> > +
> > +	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > +			sensor->sensor_info->id, &sensor_config);
> > +	if (err) {
> > +		dev_err(&iio_dev->dev,
> > +			"Error in getting sensor config for sensor %s err %d",
> > +			sensor->sensor_info->name, err);
> > +		return err;
> > +	}
> > +
> > +	uHz = val * UHZ_PER_HZ + val2;
> > +
> > +	/*
> > +	 * The seconds field in the sensor interval in SCMI is 16 bits long
> > +	 * Therefore seconds  = 1/Hz <= 0xFFFF. As floating point calculations are
> > +	 * discouraged in the kernel driver code, to calculate the scale factor (sf)
> > +	 * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000
> > +	 * To calculate the multiplier,we convert the sf into char string  and
> > +	 * count the number of characters
> > +	 */
> > +	sf = (u64)uHz * 0xFFFF;
> > +	do_div(sf,  UHZ_PER_HZ);
> > +	mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
> > +
> > +	sec = int_pow(10, mult) * UHZ_PER_HZ;
> > +	do_div(sec, uHz);
> > +	if (sec == 0) {
> > +		dev_err(&iio_dev->dev,
> > +			"Trying to set invalid sensor update value for sensor %s",
> > +			sensor->sensor_info->name);
> > +		return -EINVAL;
> > +	}
> > +
> > +	sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK;
> > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec);
> > +	sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK;
> > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult);
> > +
> > +	if (sensor->sensor_info->timestamped) {
> > +		sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK;
> > +		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > +					    SCMI_SENS_CFG_TSTAMP_ENABLE);
> > +	}
> > +
> > +	sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK;
> > +	sensor_config |=
> > +		FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO);
> > +
> > +	err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > +			sensor->sensor_info->id, sensor_config);
> > +	if (err)
> > +		dev_err(&iio_dev->dev,
> > +			"Error in setting sensor update interval for sensor %s value %u err %d",
> > +			sensor->sensor_info->name, sensor_config, err);
> > +
> > +	return err;
> > +}
> > +
> > +static int scmi_iio_write_raw(struct iio_dev *iio_dev,
> > +			      struct iio_chan_spec const *chan, int val,
> > +			      int val2, long mask)
> > +{
> > +	int err;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		mutex_lock(&iio_dev->mlock);
> > +		err = scmi_iio_set_odr_val(iio_dev, val, val2);
> > +		mutex_unlock(&iio_dev->mlock);
> > +		return err;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int scmi_iio_read_avail(struct iio_dev *iio_dev,
> > +			       struct iio_chan_spec const *chan,
> > +			       const int **vals, int *type, int *length,
> > +			       long mask)
> > +{
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		*vals = sensor->freq_avail;
> > +		*type = IIO_VAL_INT_PLUS_MICRO;
> > +		*length = sensor->sensor_info->intervals.count * 2;
> > +		if (sensor->sensor_info->intervals.segmented)
> > +			return IIO_AVAIL_RANGE;
> > +		else
> > +			return IIO_AVAIL_LIST;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz)
> > +{
> > +	u64 rem, freq;
> > +
> > +	freq = NSEC_PER_SEC;
> > +	rem = do_div(freq, interval_ns);
> > +	*hz = freq;
> > +	*uhz = rem * 1000000UL;
> > +	do_div(*uhz, interval_ns);
> > +}
> > +
> > +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2)
> > +{
> > +	u64 sensor_update_interval, sensor_interval_mult, hz, uhz;
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +	u32 sensor_config;
> > +	int mult;
> > +
> > +	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > +			sensor->sensor_info->id, &sensor_config);
> > +	if (err) {
> > +		dev_err(&iio_dev->dev,
> > +			"Error in getting sensor config for sensor %s err %d",
> > +			sensor->sensor_info->name, err);
> > +		return err;
> > +	}
> > +
> > +	sensor_update_interval =
> > +		SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC;
> > +
> > +	mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config);
> > +	if (mult < 0) {
> > +		sensor_interval_mult = int_pow(10, abs(mult));
> > +		do_div(sensor_update_interval, sensor_interval_mult);
> > +	} else {
> > +		sensor_interval_mult = int_pow(10, mult);
> > +		sensor_update_interval =
> > +			sensor_update_interval * sensor_interval_mult;
> > +	}
> > +
> > +	convert_ns_to_freq(sensor_update_interval, &hz, &uhz);
> > +	*val = hz;
> > +	*val2 = uhz;
> > +	return 0;
> > +}
> > +
> > +static int scmi_iio_read_raw(struct iio_dev *iio_dev,
> > +			     struct iio_chan_spec const *ch, int *val,
> > +			     int *val2, long mask)
> > +{
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +	s8 scale;
> > +	int ret;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_SCALE:
> > +		scale = sensor->sensor_info->axis[ch->scan_index].scale;
> > +		if (scale < 0) {
> > +			*val = 1;
> > +			*val2 = int_pow(10, abs(scale));
> > +			return IIO_VAL_FRACTIONAL;
> > +		}
> > +		*val = int_pow(10, scale);
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		ret = scmi_iio_get_odr_val(iio_dev, val, val2);
> > +		return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static const struct iio_info scmi_iio_info = {
> > +	.read_raw = scmi_iio_read_raw,
> > +	.read_avail = scmi_iio_read_avail,
> > +	.write_raw = scmi_iio_write_raw,
> > +};
> > +
> > +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
> > +					  uintptr_t private,
> > +					  const struct iio_chan_spec *chan,
> > +					  char *buf)
> > +{
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +	u64 resolution, rem;
> > +	s64 min_range, max_range;
> > +	s8 exponent, scale;
> > +	int len = 0;
> > +
> > +	/*
> > +	 * All the axes are supposed to have the same value for range and resolution.
> > +	 * We are just using the values from the Axis 0 here.
> > +	 */
> > +	if (sensor->sensor_info->axis[0].extended_attrs) {
> > +		min_range = sensor->sensor_info->axis[0].attrs.min_range;
> > +		max_range = sensor->sensor_info->axis[0].attrs.max_range;
> > +		resolution = sensor->sensor_info->axis[0].resolution;
> > +		exponent = sensor->sensor_info->axis[0].exponent;
> > +		scale = sensor->sensor_info->axis[0].scale;
> > +
> > +		/*
> > +		 * To provide the raw value for the resolution to the userspace,
> > +		 * need to divide the resolution exponent by the sensor scale
> > +		 */
> > +		exponent = exponent - scale;
> > +		if (exponent < 0) {
> > +			rem = do_div(resolution,
> > +				     int_pow(10, abs(exponent))
> > +				     );
> > +			len = scnprintf(buf, PAGE_SIZE,
> > +					"[%lld %llu.%llu %lld]\n", min_range,
> > +					resolution, rem, max_range);
> > +		} else {
> > +			resolution = resolution * int_pow(10, exponent);
> > +			len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
> > +					min_range, resolution, max_range);
> > +		}
> > +	}
> > +	return len;
> > +}
> > +
> > +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = {
> > +	{
> > +		.name = "raw_available",
> > +		.read = scmi_iio_get_raw_available,
> > +		.shared = IIO_SHARED_BY_TYPE,
> > +	},
> > +	{},
> > +};
> > +
> > +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan,
> > +					   int scan_index)
> > +{
> > +	iio_chan->type = IIO_TIMESTAMP;
> > +	iio_chan->channel = -1;
> > +	iio_chan->scan_index = scan_index;
> > +	iio_chan->scan_type.sign = 'u';
> > +	iio_chan->scan_type.realbits = 64;
> > +	iio_chan->scan_type.storagebits = 64;
> > +}
> > +
> > +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan,
> > +				      enum iio_chan_type type,
> > +				      enum iio_modifier mod, int scan_index)
> > +{
> > +	iio_chan->type = type;
> > +	iio_chan->modified = 1;
> > +	iio_chan->channel2 = mod;
> > +	iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE);
> > +	iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > +	iio_chan->info_mask_shared_by_type_available =
> > +		BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > +	iio_chan->scan_index = scan_index;
> > +	iio_chan->scan_type.sign = 's';
> > +	iio_chan->scan_type.realbits = 64;
> > +	iio_chan->scan_type.storagebits = 64;
> > +	iio_chan->scan_type.endianness = IIO_LE;
> > +	iio_chan->ext_info = scmi_iio_ext_info;
> > +}
> > +
> > +static int scmi_iio_get_chan_modifier(const char *name,
> > +				      enum iio_modifier *modifier)
> > +{
> > +	char *pch, mod;
> > +
> > +	if (!name)
> > +		return -EINVAL;
> > +
> > +	pch = strrchr(name, '_');
> > +	if (!pch)
> > +		return -EINVAL;
> > +
> > +	mod = *(pch + 1);
> > +	switch (mod) {
> > +	case 'X':
> > +		*modifier = IIO_MOD_X;
> > +		return 0;
> > +	case 'Y':
> > +		*modifier = IIO_MOD_Y;
> > +		return 0;
> > +	case 'Z':
> > +		*modifier = IIO_MOD_Z;
> > +		return 0;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type)
> > +{
> > +	switch (scmi_type) {
> > +	case METERS_SEC_SQUARED:
> > +		*iio_type = IIO_ACCEL;
> > +		return 0;
> > +	case RADIANS_SEC:
> > +		*iio_type = IIO_ANGL_VEL;
> > +		return 0;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static u64 scmi_iio_convert_interval_to_ns(u32 val)
> > +{
> > +	u64 sensor_update_interval =
> > +		SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC;
> > +	u64 sensor_interval_mult;
> > +	int mult;
> > +
> > +	mult = SCMI_SENS_INTVL_GET_EXP(val);
> > +	if (mult < 0) {
> > +		sensor_interval_mult = int_pow(10, abs(mult));
> > +		do_div(sensor_update_interval, sensor_interval_mult);
> > +	} else {
> > +		sensor_interval_mult = int_pow(10, mult);
> > +		sensor_update_interval =
> > +			sensor_update_interval * sensor_interval_mult;
> > +	}
> > +	return sensor_update_interval;
> > +}
> > +
> > +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev)
> > +{
> > +	u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns,
> > +		hz, uhz;
> > +	unsigned int cur_interval, low_interval, high_interval, step_size;
> > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > +	int i;
> > +
> > +	sensor->freq_avail =
> > +		devm_kzalloc(&iio_dev->dev,
> > +			     sizeof(*sensor->freq_avail) *
> > +				     (sensor->sensor_info->intervals.count * 2),
> > +			     GFP_KERNEL);
> > +	if (!sensor->freq_avail)
> > +		return -ENOMEM;
> > +
> > +	if (sensor->sensor_info->intervals.segmented) {
> > +		low_interval = sensor->sensor_info->intervals
> > +				       .desc[SCMI_SENS_INTVL_SEGMENT_LOW];
> > +		low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval);
> > +		convert_ns_to_freq(low_interval_ns, &hz, &uhz);
> > +		sensor->freq_avail[0] = hz;
> > +		sensor->freq_avail[1] = uhz;
> > +
> > +		step_size = sensor->sensor_info->intervals
> > +				    .desc[SCMI_SENS_INTVL_SEGMENT_STEP];
> > +		step_size_ns = scmi_iio_convert_interval_to_ns(step_size);
> > +		convert_ns_to_freq(step_size_ns, &hz, &uhz);
> > +		sensor->freq_avail[2] = hz;
> > +		sensor->freq_avail[3] = uhz;
> > +
> > +		high_interval = sensor->sensor_info->intervals
> > +					.desc[SCMI_SENS_INTVL_SEGMENT_HIGH];
> > +		high_interval_ns =
> > +			scmi_iio_convert_interval_to_ns(high_interval);
> > +		convert_ns_to_freq(high_interval_ns, &hz, &uhz);
> > +		sensor->freq_avail[4] = hz;
> > +		sensor->freq_avail[5] = uhz;
> > +	} else {
> > +		for (i = 0; i < sensor->sensor_info->intervals.count; i++) {
> > +			cur_interval = sensor->sensor_info->intervals.desc[i];
> > +			cur_interval_ns =
> > +				scmi_iio_convert_interval_to_ns(cur_interval);
> > +			convert_ns_to_freq(cur_interval_ns, &hz, &uhz);
> > +			sensor->freq_avail[i * 2] = hz;
> > +			sensor->freq_avail[i * 2 + 1] = uhz;
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev)
> > +{
> > +	struct iio_buffer *buffer;
> > +
> > +	buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev);
> > +	if (!buffer)
> > +		return -ENOMEM;
> > +
> > +	iio_device_attach_buffer(scmi_iiodev, buffer);
> > +	scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE;
> > +	scmi_iiodev->setup_ops = &scmi_iio_buffer_ops;
> > +	return 0;
> > +}
> > +
> > +static struct iio_dev *scmi_alloc_iiodev(struct device *dev,
> > +					 struct scmi_handle *handle,
> > +					 const struct scmi_sensor_info *sensor_info)
> > +{
> > +	struct iio_chan_spec *iio_channels;
> > +	struct scmi_iio_priv *sensor;
> > +	enum iio_modifier modifier;
> > +	enum iio_chan_type type;
> > +	struct iio_dev *iiodev;
> > +	int i, ret;
> > +
> > +	iiodev = devm_iio_device_alloc(dev, sizeof(*sensor));
> > +	if (!iiodev)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	iiodev->modes = INDIO_DIRECT_MODE;
> > +	iiodev->dev.parent = dev;
> > +	sensor = iio_priv(iiodev);
> > +	sensor->handle = handle;
> > +	sensor->sensor_info = sensor_info;
> > +	sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb;
> > +	sensor->indio_dev = iiodev;
> > +
> > +	/* adding one additional channel for timestamp */
> > +	iiodev->num_channels = sensor_info->num_axis + 1;
> > +	iiodev->name = sensor_info->name;
> > +	iiodev->info = &scmi_iio_info;
> > +
> > +	iio_channels =
> > +		devm_kzalloc(dev,
> > +			     sizeof(*iio_channels) * (iiodev->num_channels),
> > +			     GFP_KERNEL);
> > +	if (!iio_channels)
> > +		return ERR_PTR(-ENOMEM);
> > +
> > +	ret = scmi_iio_set_sampling_freq_avail(iiodev);
> > +	if (ret < 0)
> > +		return ERR_PTR(ret);
> > +
> > +	for (i = 0; i < sensor_info->num_axis; i++) {
> > +		ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type);
> > +		if (ret < 0)
> > +			return ERR_PTR(ret);
> > +
> > +		ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name,
> > +						 &modifier);
> > +		if (ret < 0)
> > +			return ERR_PTR(ret);
> > +
> > +		scmi_iio_set_data_channel(&iio_channels[i], type, modifier,
> > +					  sensor_info->axis[i].id);
> > +	}
> > +
> > +	scmi_iio_set_timestamp_channel(&iio_channels[i], i);
> > +	iiodev->channels = iio_channels;
> > +	return iiodev;
> > +}
> > +
> > +static int scmi_iio_dev_probe(struct scmi_device *sdev)
> > +{
> > +	const struct scmi_sensor_info *sensor_info;
> > +	struct scmi_handle *handle = sdev->handle;
> > +	struct device *dev = &sdev->dev;
> > +	struct iio_dev *scmi_iio_dev;
> > +	u16 nr_sensors;
> > +	int err = -ENODEV, i;
> > +
> > +	if (!handle || !handle->sensor_ops) {
> > +		dev_err(dev, "SCMI device has no sensor interface\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	nr_sensors = handle->sensor_ops->count_get(handle);
> > +	if (!nr_sensors) {
> > +		dev_dbg(dev, "0 sensors found via SCMI bus\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	for (i = 0; i < nr_sensors; i++) {
> > +		sensor_info = handle->sensor_ops->info_get(handle, i);
> > +		if (!sensor_info) {
> > +			dev_err(dev, "SCMI sensor %d has missing info\n", i);
> > +			return -EINVAL;
> > +		}
> > +
> > +		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > +		if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS)
> > +			continue;
> > +
> > +		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > +		if (sensor_info->axis[0].type != METERS_SEC_SQUARED &&
> > +		    sensor_info->axis[0].type != RADIANS_SEC)
> > +			continue;
> > +
> > +		scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info);
> > +		if (IS_ERR(scmi_iio_dev)) {
> > +			dev_err(dev,
> > +				"failed to allocate IIO device for sensor %s: %ld\n",
> > +				sensor_info->name, PTR_ERR(scmi_iio_dev));
> > +			return PTR_ERR(scmi_iio_dev);
> > +		}
> > +
> > +		err = scmi_iio_buffers_setup(scmi_iio_dev);
> > +		if (err < 0) {
> > +			dev_err(dev,
> > +				"IIO buffer setup error at sensor %s: %d\n",
> > +				sensor_info->name, err);
> > +			return err;
> > +		}
> > +
> > +		err = devm_iio_device_register(dev, scmi_iio_dev);
> > +		if (err) {
> > +			dev_err(dev,
> > +				"IIO device registration failed at sensor %s: %d\n",
> > +				sensor_info->name, err);
> > +			return err;
> > +		}
> > +	}
> > +	return err;
> > +}
> > +
> > +static const struct scmi_device_id scmi_id_table[] = {
> > +	{ SCMI_PROTOCOL_SENSOR, "iiodev" },
> > +	{},
> > +};
> > +
> > +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > +
> > +static struct scmi_driver scmi_iiodev_driver = {
> > +	.name = "scmi-sensor-iiodev",
> > +	.probe = scmi_iio_dev_probe,
> > +	.id_table = scmi_id_table,
> > +};
> > +
> > +module_scmi_driver(scmi_iiodev_driver);
> > +
> > +MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>");
> > +MODULE_DESCRIPTION("SCMI IIO Driver");
> > +MODULE_LICENSE("GPL v2");  
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-12 12:16     ` Jonathan Cameron
@ 2021-03-12 13:31       ` Cristian Marussi
  2021-03-12 17:54         ` Jyoti Bhayana
  0 siblings, 1 reply; 16+ messages in thread
From: Cristian Marussi @ 2021-03-12 13:31 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jonathan Cameron, Jyoti Bhayana, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, sudeep.holla, egranata,
	mikhail.golubev, Igor.Skalkin, Peter.hilber, ankitarora,
	gurunagarajan, kernel test robot

Hi Jonathan,

thanks for this, I was starting working on top of this new immutable
branch BUT I spotted a thing that I wanted to check.

You latest immutable:
 https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2

looking at the code in the SCMI IIO Jyoti driver

https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/commit/?h=ib-iio-scmi-5.12-rc2&id=f937d8c1ef246d99d2174ed88c629f6e24823918

this seems to me that still includes v6 of IIO Jyoti patch:

https://lore.kernel.org/linux-iio/20210212172235.507028-2-jbhayana@google.com/

(the offending one on the bot report related to div symbols on MIPS if I got it right),
in fact it has 710 insertions and still uses div64_u64 (and the Link tag leads to the v6)

while the lastest v7

https://lore.kernel.org/lkml/20210309231259.78050-2-jbhayana@google.com/

has 715 insetions and uses do_div instead, but in fact this v7 has a Link:
tag still pointing to v6 (maybe this is the issue...?)

Sorry for the noise if I missed something and everything is fine please
ignore me and I'll go ahead with this branch if you say so.

Thanks

Cristian



On Fri, Mar 12, 2021 at 12:16:39PM +0000, Jonathan Cameron wrote:
> On Thu, 11 Mar 2021 21:08:44 +0000
> Jonathan Cameron <jic23@kernel.org> wrote:
> 
> > On Tue,  9 Mar 2021 23:12:59 +0000
> > Jyoti Bhayana <jbhayana@google.com> wrote:
> > 
> > > This change provides ARM SCMI Protocol based IIO device.
> > > This driver provides support for Accelerometer and Gyroscope using
> > > SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> > > 
> > > Reported-by: kernel test robot <lkp@intel.com>
> > > Signed-off-by: Jyoti Bhayana <jbhayana@google.com>
> > > Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com
> > > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>  
> > 
> > https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
> > 
> > New immutable branch.  I move to rc2 as the base as the rc1 tag
> > has been removed for well publicized reasons.
> > 
> > Should have the 0-day results in tomorrow morning. I'll shout
> > if anything else shows up.
> 0-day is happy, so this is now immutable and suitable for pulling in through
> whatever trees need it.
> 
> Thanks,
> 
> Jonathan
> 
> > 
> > Also rebased the togreg branch for the same reason (hadn't pushed it
> > out as anything other than testing, so hopefully no one was basing
> > anything significant on top)
> > 
> > Thanks,
> > 
> > Jonathan
> > 
> > > ---
> > >  MAINTAINERS                                |   6 +
> > >  drivers/firmware/arm_scmi/driver.c         |   2 +-
> > >  drivers/iio/common/Kconfig                 |   1 +
> > >  drivers/iio/common/Makefile                |   1 +
> > >  drivers/iio/common/scmi_sensors/Kconfig    |  18 +
> > >  drivers/iio/common/scmi_sensors/Makefile   |   5 +
> > >  drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
> > >  7 files changed, 715 insertions(+), 1 deletion(-)
> > >  create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
> > >  create mode 100644 drivers/iio/common/scmi_sensors/Makefile
> > >  create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c
> > > 
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index d92f85ca831d..14227980f3d2 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -8692,6 +8692,12 @@ S:	Maintained
> > >  F:	Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
> > >  F:	drivers/iio/multiplexer/iio-mux.c
> > >  
> > > +IIO SCMI BASED DRIVER
> > > +M:	Jyoti Bhayana <jbhayana@google.com>
> > > +L:	linux-iio@vger.kernel.org
> > > +S:	Maintained
> > > +F:	drivers/iio/common/scmi_sensors/scmi_iio.c
> > > +
> > >  IIO SUBSYSTEM AND DRIVERS
> > >  M:	Jonathan Cameron <jic23@kernel.org>
> > >  R:	Lars-Peter Clausen <lars@metafoo.de>
> > > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > > index cacdf1589b10..3e748e57deab 100644
> > > --- a/drivers/firmware/arm_scmi/driver.c
> > > +++ b/drivers/firmware/arm_scmi/driver.c
> > > @@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = {
> > >  	{ SCMI_PROTOCOL_SYSTEM, { "syspower" },},
> > >  	{ SCMI_PROTOCOL_PERF,   { "cpufreq" },},
> > >  	{ SCMI_PROTOCOL_CLOCK,  { "clocks" },},
> > > -	{ SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> > > +	{ SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },},
> > >  	{ SCMI_PROTOCOL_RESET,  { "reset" },},
> > >  	{ SCMI_PROTOCOL_VOLTAGE,  { "regulator" },},
> > >  };
> > > diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> > > index 2b9ee9161abd..0334b4954773 100644
> > > --- a/drivers/iio/common/Kconfig
> > > +++ b/drivers/iio/common/Kconfig
> > > @@ -6,5 +6,6 @@
> > >  source "drivers/iio/common/cros_ec_sensors/Kconfig"
> > >  source "drivers/iio/common/hid-sensors/Kconfig"
> > >  source "drivers/iio/common/ms_sensors/Kconfig"
> > > +source "drivers/iio/common/scmi_sensors/Kconfig"
> > >  source "drivers/iio/common/ssp_sensors/Kconfig"
> > >  source "drivers/iio/common/st_sensors/Kconfig"
> > > diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> > > index 4bc30bb548e2..fad40e1e1718 100644
> > > --- a/drivers/iio/common/Makefile
> > > +++ b/drivers/iio/common/Makefile
> > > @@ -11,5 +11,6 @@
> > >  obj-y += cros_ec_sensors/
> > >  obj-y += hid-sensors/
> > >  obj-y += ms_sensors/
> > > +obj-y += scmi_sensors/
> > >  obj-y += ssp_sensors/
> > >  obj-y += st_sensors/
> > > diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig
> > > new file mode 100644
> > > index 000000000000..67e084cbb1ab
> > > --- /dev/null
> > > +++ b/drivers/iio/common/scmi_sensors/Kconfig
> > > @@ -0,0 +1,18 @@
> > > +#
> > > +# IIO over SCMI
> > > +#
> > > +# When adding new entries keep the list in alphabetical order
> > > +
> > > +menu "IIO SCMI Sensors"
> > > +
> > > +config IIO_SCMI
> > > +	tristate "IIO SCMI"
> > > +        depends on ARM_SCMI_PROTOCOL
> > > +        select IIO_BUFFER
> > > +        select IIO_KFIFO_BUF
> > > +	help
> > > +          Say yes here to build support for IIO SCMI Driver.
> > > +          This provides ARM SCMI Protocol based IIO device.
> > > +          This driver provides support for accelerometer and gyroscope
> > > +          sensors available on SCMI based platforms.
> > > +endmenu
> > > diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile
> > > new file mode 100644
> > > index 000000000000..f13140a2575a
> > > --- /dev/null
> > > +++ b/drivers/iio/common/scmi_sensors/Makefile
> > > @@ -0,0 +1,5 @@
> > > +# SPDX - License - Identifier : GPL - 2.0 - only
> > > +#
> > > +# Makefile for the IIO over SCMI
> > > +#
> > > +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o
> > > diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > new file mode 100644
> > > index 000000000000..872d87ca6256
> > > --- /dev/null
> > > +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > @@ -0,0 +1,683 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +
> > > +/*
> > > + * System Control and Management Interface(SCMI) based IIO sensor driver
> > > + *
> > > + * Copyright (C) 2021 Google LLC
> > > + */
> > > +
> > > +#include <linux/delay.h>
> > > +#include <linux/err.h>
> > > +#include <linux/iio/buffer.h>
> > > +#include <linux/iio/iio.h>
> > > +#include <linux/iio/kfifo_buf.h>
> > > +#include <linux/iio/sysfs.h>
> > > +#include <linux/kernel.h>
> > > +#include <linux/kthread.h>
> > > +#include <linux/module.h>
> > > +#include <linux/scmi_protocol.h>
> > > +#include <linux/time.h>
> > > +#include <linux/types.h>
> > > +
> > > +#define SCMI_IIO_NUM_OF_AXIS 3
> > > +
> > > +struct scmi_iio_priv {
> > > +	struct scmi_handle *handle;
> > > +	const struct scmi_sensor_info *sensor_info;
> > > +	struct iio_dev *indio_dev;
> > > +	/* adding one additional channel for timestamp */
> > > +	s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1];
> > > +	struct notifier_block sensor_update_nb;
> > > +	u32 *freq_avail;
> > > +};
> > > +
> > > +static int scmi_iio_sensor_update_cb(struct notifier_block *nb,
> > > +				     unsigned long event, void *data)
> > > +{
> > > +	struct scmi_sensor_update_report *sensor_update = data;
> > > +	struct iio_dev *scmi_iio_dev;
> > > +	struct scmi_iio_priv *sensor;
> > > +	s8 tstamp_scale;
> > > +	u64 time, time_ns;
> > > +	int i;
> > > +
> > > +	if (sensor_update->readings_count == 0)
> > > +		return NOTIFY_DONE;
> > > +
> > > +	sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb);
> > > +
> > > +	for (i = 0; i < sensor_update->readings_count; i++)
> > > +		sensor->iio_buf[i] = sensor_update->readings[i].value;
> > > +
> > > +	if (!sensor->sensor_info->timestamped) {
> > > +		time_ns = ktime_to_ns(sensor_update->timestamp);
> > > +	} else {
> > > +		/*
> > > +		 *  All the axes are supposed to have the same value for timestamp.
> > > +		 *  We are just using the values from the Axis 0 here.
> > > +		 */
> > > +		time = sensor_update->readings[0].timestamp;
> > > +
> > > +		/*
> > > +		 *  Timestamp returned by SCMI is in seconds and is equal to
> > > +		 *  time * power-of-10 multiplier(tstamp_scale) seconds.
> > > +		 *  Converting the timestamp to nanoseconds below.
> > > +		 */
> > > +		tstamp_scale = sensor->sensor_info->tstamp_scale +
> > > +			       const_ilog2(NSEC_PER_SEC) / const_ilog2(10);
> > > +		if (tstamp_scale < 0) {
> > > +			do_div(time, int_pow(10, abs(tstamp_scale)));
> > > +			time_ns = time;
> > > +		} else {
> > > +			time_ns = time * int_pow(10, tstamp_scale);
> > > +		}
> > > +	}
> > > +
> > > +	scmi_iio_dev = sensor->indio_dev;
> > > +	iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf,
> > > +					   time_ns);
> > > +	return NOTIFY_OK;
> > > +}
> > > +
> > > +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev)
> > > +{
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +	u32 sensor_id = sensor->sensor_info->id;
> > > +	u32 sensor_config = 0;
> > > +	int err;
> > > +
> > > +	if (sensor->sensor_info->timestamped)
> > > +		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > +					    SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > +
> > > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > +				    SCMI_SENS_CFG_SENSOR_ENABLE);
> > > +
> > > +	err = sensor->handle->notify_ops->register_event_notifier(sensor->handle,
> > > +			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > +			&sensor_id, &sensor->sensor_update_nb);
> > > +	if (err) {
> > > +		dev_err(&iio_dev->dev,
> > > +			"Error in registering sensor update notifier for sensor %s err %d",
> > > +			sensor->sensor_info->name, err);
> > > +		return err;
> > > +	}
> > > +
> > > +	err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > +			sensor->sensor_info->id, sensor_config);
> > > +	if (err) {
> > > +		sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > +				SCMI_PROTOCOL_SENSOR,
> > > +				SCMI_EVENT_SENSOR_UPDATE, &sensor_id,
> > > +				&sensor->sensor_update_nb);
> > > +		dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d",
> > > +			sensor->sensor_info->name, err);
> > > +	}
> > > +
> > > +	return err;
> > > +}
> > > +
> > > +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev)
> > > +{
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +	u32 sensor_id = sensor->sensor_info->id;
> > > +	u32 sensor_config = 0;
> > > +	int err;
> > > +
> > > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > +				    SCMI_SENS_CFG_SENSOR_DISABLE);
> > > +
> > > +	err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > +			SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > +			&sensor_id, &sensor->sensor_update_nb);
> > > +	if (err) {
> > > +		dev_err(&iio_dev->dev,
> > > +			"Error in unregistering sensor update notifier for sensor %s err %d",
> > > +			sensor->sensor_info->name, err);
> > > +		return err;
> > > +	}
> > > +
> > > +	err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id,
> > > +						     sensor_config);
> > > +	if (err) {
> > > +		dev_err(&iio_dev->dev,
> > > +			"Error in disabling sensor %s with err %d",
> > > +			sensor->sensor_info->name, err);
> > > +	}
> > > +
> > > +	return err;
> > > +}
> > > +
> > > +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
> > > +	.preenable = scmi_iio_buffer_preenable,
> > > +	.postdisable = scmi_iio_buffer_postdisable,
> > > +};
> > > +
> > > +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
> > > +{
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +	const unsigned long UHZ_PER_HZ = 1000000UL;
> > > +	u64 sec, mult, uHz, sf;
> > > +	u32 sensor_config;
> > > +	char buf[32];
> > > +
> > > +	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > +			sensor->sensor_info->id, &sensor_config);
> > > +	if (err) {
> > > +		dev_err(&iio_dev->dev,
> > > +			"Error in getting sensor config for sensor %s err %d",
> > > +			sensor->sensor_info->name, err);
> > > +		return err;
> > > +	}
> > > +
> > > +	uHz = val * UHZ_PER_HZ + val2;
> > > +
> > > +	/*
> > > +	 * The seconds field in the sensor interval in SCMI is 16 bits long
> > > +	 * Therefore seconds  = 1/Hz <= 0xFFFF. As floating point calculations are
> > > +	 * discouraged in the kernel driver code, to calculate the scale factor (sf)
> > > +	 * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000
> > > +	 * To calculate the multiplier,we convert the sf into char string  and
> > > +	 * count the number of characters
> > > +	 */
> > > +	sf = (u64)uHz * 0xFFFF;
> > > +	do_div(sf,  UHZ_PER_HZ);
> > > +	mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
> > > +
> > > +	sec = int_pow(10, mult) * UHZ_PER_HZ;
> > > +	do_div(sec, uHz);
> > > +	if (sec == 0) {
> > > +		dev_err(&iio_dev->dev,
> > > +			"Trying to set invalid sensor update value for sensor %s",
> > > +			sensor->sensor_info->name);
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK;
> > > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec);
> > > +	sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK;
> > > +	sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult);
> > > +
> > > +	if (sensor->sensor_info->timestamped) {
> > > +		sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK;
> > > +		sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > +					    SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > +	}
> > > +
> > > +	sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK;
> > > +	sensor_config |=
> > > +		FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO);
> > > +
> > > +	err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > +			sensor->sensor_info->id, sensor_config);
> > > +	if (err)
> > > +		dev_err(&iio_dev->dev,
> > > +			"Error in setting sensor update interval for sensor %s value %u err %d",
> > > +			sensor->sensor_info->name, sensor_config, err);
> > > +
> > > +	return err;
> > > +}
> > > +
> > > +static int scmi_iio_write_raw(struct iio_dev *iio_dev,
> > > +			      struct iio_chan_spec const *chan, int val,
> > > +			      int val2, long mask)
> > > +{
> > > +	int err;
> > > +
> > > +	switch (mask) {
> > > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > > +		mutex_lock(&iio_dev->mlock);
> > > +		err = scmi_iio_set_odr_val(iio_dev, val, val2);
> > > +		mutex_unlock(&iio_dev->mlock);
> > > +		return err;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +}
> > > +
> > > +static int scmi_iio_read_avail(struct iio_dev *iio_dev,
> > > +			       struct iio_chan_spec const *chan,
> > > +			       const int **vals, int *type, int *length,
> > > +			       long mask)
> > > +{
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +
> > > +	switch (mask) {
> > > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > > +		*vals = sensor->freq_avail;
> > > +		*type = IIO_VAL_INT_PLUS_MICRO;
> > > +		*length = sensor->sensor_info->intervals.count * 2;
> > > +		if (sensor->sensor_info->intervals.segmented)
> > > +			return IIO_AVAIL_RANGE;
> > > +		else
> > > +			return IIO_AVAIL_LIST;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +}
> > > +
> > > +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz)
> > > +{
> > > +	u64 rem, freq;
> > > +
> > > +	freq = NSEC_PER_SEC;
> > > +	rem = do_div(freq, interval_ns);
> > > +	*hz = freq;
> > > +	*uhz = rem * 1000000UL;
> > > +	do_div(*uhz, interval_ns);
> > > +}
> > > +
> > > +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2)
> > > +{
> > > +	u64 sensor_update_interval, sensor_interval_mult, hz, uhz;
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +	u32 sensor_config;
> > > +	int mult;
> > > +
> > > +	int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > +			sensor->sensor_info->id, &sensor_config);
> > > +	if (err) {
> > > +		dev_err(&iio_dev->dev,
> > > +			"Error in getting sensor config for sensor %s err %d",
> > > +			sensor->sensor_info->name, err);
> > > +		return err;
> > > +	}
> > > +
> > > +	sensor_update_interval =
> > > +		SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC;
> > > +
> > > +	mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config);
> > > +	if (mult < 0) {
> > > +		sensor_interval_mult = int_pow(10, abs(mult));
> > > +		do_div(sensor_update_interval, sensor_interval_mult);
> > > +	} else {
> > > +		sensor_interval_mult = int_pow(10, mult);
> > > +		sensor_update_interval =
> > > +			sensor_update_interval * sensor_interval_mult;
> > > +	}
> > > +
> > > +	convert_ns_to_freq(sensor_update_interval, &hz, &uhz);
> > > +	*val = hz;
> > > +	*val2 = uhz;
> > > +	return 0;
> > > +}
> > > +
> > > +static int scmi_iio_read_raw(struct iio_dev *iio_dev,
> > > +			     struct iio_chan_spec const *ch, int *val,
> > > +			     int *val2, long mask)
> > > +{
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +	s8 scale;
> > > +	int ret;
> > > +
> > > +	switch (mask) {
> > > +	case IIO_CHAN_INFO_SCALE:
> > > +		scale = sensor->sensor_info->axis[ch->scan_index].scale;
> > > +		if (scale < 0) {
> > > +			*val = 1;
> > > +			*val2 = int_pow(10, abs(scale));
> > > +			return IIO_VAL_FRACTIONAL;
> > > +		}
> > > +		*val = int_pow(10, scale);
> > > +		return IIO_VAL_INT;
> > > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > > +		ret = scmi_iio_get_odr_val(iio_dev, val, val2);
> > > +		return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +}
> > > +
> > > +static const struct iio_info scmi_iio_info = {
> > > +	.read_raw = scmi_iio_read_raw,
> > > +	.read_avail = scmi_iio_read_avail,
> > > +	.write_raw = scmi_iio_write_raw,
> > > +};
> > > +
> > > +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
> > > +					  uintptr_t private,
> > > +					  const struct iio_chan_spec *chan,
> > > +					  char *buf)
> > > +{
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +	u64 resolution, rem;
> > > +	s64 min_range, max_range;
> > > +	s8 exponent, scale;
> > > +	int len = 0;
> > > +
> > > +	/*
> > > +	 * All the axes are supposed to have the same value for range and resolution.
> > > +	 * We are just using the values from the Axis 0 here.
> > > +	 */
> > > +	if (sensor->sensor_info->axis[0].extended_attrs) {
> > > +		min_range = sensor->sensor_info->axis[0].attrs.min_range;
> > > +		max_range = sensor->sensor_info->axis[0].attrs.max_range;
> > > +		resolution = sensor->sensor_info->axis[0].resolution;
> > > +		exponent = sensor->sensor_info->axis[0].exponent;
> > > +		scale = sensor->sensor_info->axis[0].scale;
> > > +
> > > +		/*
> > > +		 * To provide the raw value for the resolution to the userspace,
> > > +		 * need to divide the resolution exponent by the sensor scale
> > > +		 */
> > > +		exponent = exponent - scale;
> > > +		if (exponent < 0) {
> > > +			rem = do_div(resolution,
> > > +				     int_pow(10, abs(exponent))
> > > +				     );
> > > +			len = scnprintf(buf, PAGE_SIZE,
> > > +					"[%lld %llu.%llu %lld]\n", min_range,
> > > +					resolution, rem, max_range);
> > > +		} else {
> > > +			resolution = resolution * int_pow(10, exponent);
> > > +			len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
> > > +					min_range, resolution, max_range);
> > > +		}
> > > +	}
> > > +	return len;
> > > +}
> > > +
> > > +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = {
> > > +	{
> > > +		.name = "raw_available",
> > > +		.read = scmi_iio_get_raw_available,
> > > +		.shared = IIO_SHARED_BY_TYPE,
> > > +	},
> > > +	{},
> > > +};
> > > +
> > > +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan,
> > > +					   int scan_index)
> > > +{
> > > +	iio_chan->type = IIO_TIMESTAMP;
> > > +	iio_chan->channel = -1;
> > > +	iio_chan->scan_index = scan_index;
> > > +	iio_chan->scan_type.sign = 'u';
> > > +	iio_chan->scan_type.realbits = 64;
> > > +	iio_chan->scan_type.storagebits = 64;
> > > +}
> > > +
> > > +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan,
> > > +				      enum iio_chan_type type,
> > > +				      enum iio_modifier mod, int scan_index)
> > > +{
> > > +	iio_chan->type = type;
> > > +	iio_chan->modified = 1;
> > > +	iio_chan->channel2 = mod;
> > > +	iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE);
> > > +	iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > +	iio_chan->info_mask_shared_by_type_available =
> > > +		BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > +	iio_chan->scan_index = scan_index;
> > > +	iio_chan->scan_type.sign = 's';
> > > +	iio_chan->scan_type.realbits = 64;
> > > +	iio_chan->scan_type.storagebits = 64;
> > > +	iio_chan->scan_type.endianness = IIO_LE;
> > > +	iio_chan->ext_info = scmi_iio_ext_info;
> > > +}
> > > +
> > > +static int scmi_iio_get_chan_modifier(const char *name,
> > > +				      enum iio_modifier *modifier)
> > > +{
> > > +	char *pch, mod;
> > > +
> > > +	if (!name)
> > > +		return -EINVAL;
> > > +
> > > +	pch = strrchr(name, '_');
> > > +	if (!pch)
> > > +		return -EINVAL;
> > > +
> > > +	mod = *(pch + 1);
> > > +	switch (mod) {
> > > +	case 'X':
> > > +		*modifier = IIO_MOD_X;
> > > +		return 0;
> > > +	case 'Y':
> > > +		*modifier = IIO_MOD_Y;
> > > +		return 0;
> > > +	case 'Z':
> > > +		*modifier = IIO_MOD_Z;
> > > +		return 0;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +}
> > > +
> > > +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type)
> > > +{
> > > +	switch (scmi_type) {
> > > +	case METERS_SEC_SQUARED:
> > > +		*iio_type = IIO_ACCEL;
> > > +		return 0;
> > > +	case RADIANS_SEC:
> > > +		*iio_type = IIO_ANGL_VEL;
> > > +		return 0;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +}
> > > +
> > > +static u64 scmi_iio_convert_interval_to_ns(u32 val)
> > > +{
> > > +	u64 sensor_update_interval =
> > > +		SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC;
> > > +	u64 sensor_interval_mult;
> > > +	int mult;
> > > +
> > > +	mult = SCMI_SENS_INTVL_GET_EXP(val);
> > > +	if (mult < 0) {
> > > +		sensor_interval_mult = int_pow(10, abs(mult));
> > > +		do_div(sensor_update_interval, sensor_interval_mult);
> > > +	} else {
> > > +		sensor_interval_mult = int_pow(10, mult);
> > > +		sensor_update_interval =
> > > +			sensor_update_interval * sensor_interval_mult;
> > > +	}
> > > +	return sensor_update_interval;
> > > +}
> > > +
> > > +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev)
> > > +{
> > > +	u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns,
> > > +		hz, uhz;
> > > +	unsigned int cur_interval, low_interval, high_interval, step_size;
> > > +	struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > +	int i;
> > > +
> > > +	sensor->freq_avail =
> > > +		devm_kzalloc(&iio_dev->dev,
> > > +			     sizeof(*sensor->freq_avail) *
> > > +				     (sensor->sensor_info->intervals.count * 2),
> > > +			     GFP_KERNEL);
> > > +	if (!sensor->freq_avail)
> > > +		return -ENOMEM;
> > > +
> > > +	if (sensor->sensor_info->intervals.segmented) {
> > > +		low_interval = sensor->sensor_info->intervals
> > > +				       .desc[SCMI_SENS_INTVL_SEGMENT_LOW];
> > > +		low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval);
> > > +		convert_ns_to_freq(low_interval_ns, &hz, &uhz);
> > > +		sensor->freq_avail[0] = hz;
> > > +		sensor->freq_avail[1] = uhz;
> > > +
> > > +		step_size = sensor->sensor_info->intervals
> > > +				    .desc[SCMI_SENS_INTVL_SEGMENT_STEP];
> > > +		step_size_ns = scmi_iio_convert_interval_to_ns(step_size);
> > > +		convert_ns_to_freq(step_size_ns, &hz, &uhz);
> > > +		sensor->freq_avail[2] = hz;
> > > +		sensor->freq_avail[3] = uhz;
> > > +
> > > +		high_interval = sensor->sensor_info->intervals
> > > +					.desc[SCMI_SENS_INTVL_SEGMENT_HIGH];
> > > +		high_interval_ns =
> > > +			scmi_iio_convert_interval_to_ns(high_interval);
> > > +		convert_ns_to_freq(high_interval_ns, &hz, &uhz);
> > > +		sensor->freq_avail[4] = hz;
> > > +		sensor->freq_avail[5] = uhz;
> > > +	} else {
> > > +		for (i = 0; i < sensor->sensor_info->intervals.count; i++) {
> > > +			cur_interval = sensor->sensor_info->intervals.desc[i];
> > > +			cur_interval_ns =
> > > +				scmi_iio_convert_interval_to_ns(cur_interval);
> > > +			convert_ns_to_freq(cur_interval_ns, &hz, &uhz);
> > > +			sensor->freq_avail[i * 2] = hz;
> > > +			sensor->freq_avail[i * 2 + 1] = uhz;
> > > +		}
> > > +	}
> > > +	return 0;
> > > +}
> > > +
> > > +static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev)
> > > +{
> > > +	struct iio_buffer *buffer;
> > > +
> > > +	buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev);
> > > +	if (!buffer)
> > > +		return -ENOMEM;
> > > +
> > > +	iio_device_attach_buffer(scmi_iiodev, buffer);
> > > +	scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE;
> > > +	scmi_iiodev->setup_ops = &scmi_iio_buffer_ops;
> > > +	return 0;
> > > +}
> > > +
> > > +static struct iio_dev *scmi_alloc_iiodev(struct device *dev,
> > > +					 struct scmi_handle *handle,
> > > +					 const struct scmi_sensor_info *sensor_info)
> > > +{
> > > +	struct iio_chan_spec *iio_channels;
> > > +	struct scmi_iio_priv *sensor;
> > > +	enum iio_modifier modifier;
> > > +	enum iio_chan_type type;
> > > +	struct iio_dev *iiodev;
> > > +	int i, ret;
> > > +
> > > +	iiodev = devm_iio_device_alloc(dev, sizeof(*sensor));
> > > +	if (!iiodev)
> > > +		return ERR_PTR(-ENOMEM);
> > > +
> > > +	iiodev->modes = INDIO_DIRECT_MODE;
> > > +	iiodev->dev.parent = dev;
> > > +	sensor = iio_priv(iiodev);
> > > +	sensor->handle = handle;
> > > +	sensor->sensor_info = sensor_info;
> > > +	sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb;
> > > +	sensor->indio_dev = iiodev;
> > > +
> > > +	/* adding one additional channel for timestamp */
> > > +	iiodev->num_channels = sensor_info->num_axis + 1;
> > > +	iiodev->name = sensor_info->name;
> > > +	iiodev->info = &scmi_iio_info;
> > > +
> > > +	iio_channels =
> > > +		devm_kzalloc(dev,
> > > +			     sizeof(*iio_channels) * (iiodev->num_channels),
> > > +			     GFP_KERNEL);
> > > +	if (!iio_channels)
> > > +		return ERR_PTR(-ENOMEM);
> > > +
> > > +	ret = scmi_iio_set_sampling_freq_avail(iiodev);
> > > +	if (ret < 0)
> > > +		return ERR_PTR(ret);
> > > +
> > > +	for (i = 0; i < sensor_info->num_axis; i++) {
> > > +		ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type);
> > > +		if (ret < 0)
> > > +			return ERR_PTR(ret);
> > > +
> > > +		ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name,
> > > +						 &modifier);
> > > +		if (ret < 0)
> > > +			return ERR_PTR(ret);
> > > +
> > > +		scmi_iio_set_data_channel(&iio_channels[i], type, modifier,
> > > +					  sensor_info->axis[i].id);
> > > +	}
> > > +
> > > +	scmi_iio_set_timestamp_channel(&iio_channels[i], i);
> > > +	iiodev->channels = iio_channels;
> > > +	return iiodev;
> > > +}
> > > +
> > > +static int scmi_iio_dev_probe(struct scmi_device *sdev)
> > > +{
> > > +	const struct scmi_sensor_info *sensor_info;
> > > +	struct scmi_handle *handle = sdev->handle;
> > > +	struct device *dev = &sdev->dev;
> > > +	struct iio_dev *scmi_iio_dev;
> > > +	u16 nr_sensors;
> > > +	int err = -ENODEV, i;
> > > +
> > > +	if (!handle || !handle->sensor_ops) {
> > > +		dev_err(dev, "SCMI device has no sensor interface\n");
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	nr_sensors = handle->sensor_ops->count_get(handle);
> > > +	if (!nr_sensors) {
> > > +		dev_dbg(dev, "0 sensors found via SCMI bus\n");
> > > +		return -ENODEV;
> > > +	}
> > > +
> > > +	for (i = 0; i < nr_sensors; i++) {
> > > +		sensor_info = handle->sensor_ops->info_get(handle, i);
> > > +		if (!sensor_info) {
> > > +			dev_err(dev, "SCMI sensor %d has missing info\n", i);
> > > +			return -EINVAL;
> > > +		}
> > > +
> > > +		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > +		if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS)
> > > +			continue;
> > > +
> > > +		/* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > +		if (sensor_info->axis[0].type != METERS_SEC_SQUARED &&
> > > +		    sensor_info->axis[0].type != RADIANS_SEC)
> > > +			continue;
> > > +
> > > +		scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info);
> > > +		if (IS_ERR(scmi_iio_dev)) {
> > > +			dev_err(dev,
> > > +				"failed to allocate IIO device for sensor %s: %ld\n",
> > > +				sensor_info->name, PTR_ERR(scmi_iio_dev));
> > > +			return PTR_ERR(scmi_iio_dev);
> > > +		}
> > > +
> > > +		err = scmi_iio_buffers_setup(scmi_iio_dev);
> > > +		if (err < 0) {
> > > +			dev_err(dev,
> > > +				"IIO buffer setup error at sensor %s: %d\n",
> > > +				sensor_info->name, err);
> > > +			return err;
> > > +		}
> > > +
> > > +		err = devm_iio_device_register(dev, scmi_iio_dev);
> > > +		if (err) {
> > > +			dev_err(dev,
> > > +				"IIO device registration failed at sensor %s: %d\n",
> > > +				sensor_info->name, err);
> > > +			return err;
> > > +		}
> > > +	}
> > > +	return err;
> > > +}
> > > +
> > > +static const struct scmi_device_id scmi_id_table[] = {
> > > +	{ SCMI_PROTOCOL_SENSOR, "iiodev" },
> > > +	{},
> > > +};
> > > +
> > > +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > > +
> > > +static struct scmi_driver scmi_iiodev_driver = {
> > > +	.name = "scmi-sensor-iiodev",
> > > +	.probe = scmi_iio_dev_probe,
> > > +	.id_table = scmi_id_table,
> > > +};
> > > +
> > > +module_scmi_driver(scmi_iiodev_driver);
> > > +
> > > +MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>");
> > > +MODULE_DESCRIPTION("SCMI IIO Driver");
> > > +MODULE_LICENSE("GPL v2");  
> > 
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-12 13:31       ` Cristian Marussi
@ 2021-03-12 17:54         ` Jyoti Bhayana
  2021-03-13 17:11           ` Jonathan Cameron
  0 siblings, 1 reply; 16+ messages in thread
From: Jyoti Bhayana @ 2021-03-12 17:54 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: Jonathan Cameron, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Sudeep Holla,
	Enrico Granata, Mikhail Golubev, Igor Skalkin, Peter Hilber,
	Ankit Arora, Guru Nagarajan, kernel test robot

Hi Jonathan,

I also see what Cristian has observed that rc2 is still pointing to v6
of the SCMI IIO driver patch and it also doesn't have the changes
which you did in rc1 for changing long long to s64.

Thanks,
Jyoti


On Fri, Mar 12, 2021 at 5:31 AM Cristian Marussi
<cristian.marussi@arm.com> wrote:
>
> Hi Jonathan,
>
> thanks for this, I was starting working on top of this new immutable
> branch BUT I spotted a thing that I wanted to check.
>
> You latest immutable:
>  https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
>
> looking at the code in the SCMI IIO Jyoti driver
>
> https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/commit/?h=ib-iio-scmi-5.12-rc2&id=f937d8c1ef246d99d2174ed88c629f6e24823918
>
> this seems to me that still includes v6 of IIO Jyoti patch:
>
> https://lore.kernel.org/linux-iio/20210212172235.507028-2-jbhayana@google.com/
>
> (the offending one on the bot report related to div symbols on MIPS if I got it right),
> in fact it has 710 insertions and still uses div64_u64 (and the Link tag leads to the v6)
>
> while the lastest v7
>
> https://lore.kernel.org/lkml/20210309231259.78050-2-jbhayana@google.com/
>
> has 715 insetions and uses do_div instead, but in fact this v7 has a Link:
> tag still pointing to v6 (maybe this is the issue...?)
>
> Sorry for the noise if I missed something and everything is fine please
> ignore me and I'll go ahead with this branch if you say so.
>
> Thanks
>
> Cristian
>
>
>
> On Fri, Mar 12, 2021 at 12:16:39PM +0000, Jonathan Cameron wrote:
> > On Thu, 11 Mar 2021 21:08:44 +0000
> > Jonathan Cameron <jic23@kernel.org> wrote:
> >
> > > On Tue,  9 Mar 2021 23:12:59 +0000
> > > Jyoti Bhayana <jbhayana@google.com> wrote:
> > >
> > > > This change provides ARM SCMI Protocol based IIO device.
> > > > This driver provides support for Accelerometer and Gyroscope using
> > > > SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> > > >
> > > > Reported-by: kernel test robot <lkp@intel.com>
> > > > Signed-off-by: Jyoti Bhayana <jbhayana@google.com>
> > > > Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com
> > > > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
> > >
> > > New immutable branch.  I move to rc2 as the base as the rc1 tag
> > > has been removed for well publicized reasons.
> > >
> > > Should have the 0-day results in tomorrow morning. I'll shout
> > > if anything else shows up.
> > 0-day is happy, so this is now immutable and suitable for pulling in through
> > whatever trees need it.
> >
> > Thanks,
> >
> > Jonathan
> >
> > >
> > > Also rebased the togreg branch for the same reason (hadn't pushed it
> > > out as anything other than testing, so hopefully no one was basing
> > > anything significant on top)
> > >
> > > Thanks,
> > >
> > > Jonathan
> > >
> > > > ---
> > > >  MAINTAINERS                                |   6 +
> > > >  drivers/firmware/arm_scmi/driver.c         |   2 +-
> > > >  drivers/iio/common/Kconfig                 |   1 +
> > > >  drivers/iio/common/Makefile                |   1 +
> > > >  drivers/iio/common/scmi_sensors/Kconfig    |  18 +
> > > >  drivers/iio/common/scmi_sensors/Makefile   |   5 +
> > > >  drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
> > > >  7 files changed, 715 insertions(+), 1 deletion(-)
> > > >  create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
> > > >  create mode 100644 drivers/iio/common/scmi_sensors/Makefile
> > > >  create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c
> > > >
> > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > index d92f85ca831d..14227980f3d2 100644
> > > > --- a/MAINTAINERS
> > > > +++ b/MAINTAINERS
> > > > @@ -8692,6 +8692,12 @@ S: Maintained
> > > >  F:       Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
> > > >  F:       drivers/iio/multiplexer/iio-mux.c
> > > >
> > > > +IIO SCMI BASED DRIVER
> > > > +M:       Jyoti Bhayana <jbhayana@google.com>
> > > > +L:       linux-iio@vger.kernel.org
> > > > +S:       Maintained
> > > > +F:       drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > +
> > > >  IIO SUBSYSTEM AND DRIVERS
> > > >  M:       Jonathan Cameron <jic23@kernel.org>
> > > >  R:       Lars-Peter Clausen <lars@metafoo.de>
> > > > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > > > index cacdf1589b10..3e748e57deab 100644
> > > > --- a/drivers/firmware/arm_scmi/driver.c
> > > > +++ b/drivers/firmware/arm_scmi/driver.c
> > > > @@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = {
> > > >   { SCMI_PROTOCOL_SYSTEM, { "syspower" },},
> > > >   { SCMI_PROTOCOL_PERF,   { "cpufreq" },},
> > > >   { SCMI_PROTOCOL_CLOCK,  { "clocks" },},
> > > > - { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> > > > + { SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },},
> > > >   { SCMI_PROTOCOL_RESET,  { "reset" },},
> > > >   { SCMI_PROTOCOL_VOLTAGE,  { "regulator" },},
> > > >  };
> > > > diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> > > > index 2b9ee9161abd..0334b4954773 100644
> > > > --- a/drivers/iio/common/Kconfig
> > > > +++ b/drivers/iio/common/Kconfig
> > > > @@ -6,5 +6,6 @@
> > > >  source "drivers/iio/common/cros_ec_sensors/Kconfig"
> > > >  source "drivers/iio/common/hid-sensors/Kconfig"
> > > >  source "drivers/iio/common/ms_sensors/Kconfig"
> > > > +source "drivers/iio/common/scmi_sensors/Kconfig"
> > > >  source "drivers/iio/common/ssp_sensors/Kconfig"
> > > >  source "drivers/iio/common/st_sensors/Kconfig"
> > > > diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> > > > index 4bc30bb548e2..fad40e1e1718 100644
> > > > --- a/drivers/iio/common/Makefile
> > > > +++ b/drivers/iio/common/Makefile
> > > > @@ -11,5 +11,6 @@
> > > >  obj-y += cros_ec_sensors/
> > > >  obj-y += hid-sensors/
> > > >  obj-y += ms_sensors/
> > > > +obj-y += scmi_sensors/
> > > >  obj-y += ssp_sensors/
> > > >  obj-y += st_sensors/
> > > > diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig
> > > > new file mode 100644
> > > > index 000000000000..67e084cbb1ab
> > > > --- /dev/null
> > > > +++ b/drivers/iio/common/scmi_sensors/Kconfig
> > > > @@ -0,0 +1,18 @@
> > > > +#
> > > > +# IIO over SCMI
> > > > +#
> > > > +# When adding new entries keep the list in alphabetical order
> > > > +
> > > > +menu "IIO SCMI Sensors"
> > > > +
> > > > +config IIO_SCMI
> > > > + tristate "IIO SCMI"
> > > > +        depends on ARM_SCMI_PROTOCOL
> > > > +        select IIO_BUFFER
> > > > +        select IIO_KFIFO_BUF
> > > > + help
> > > > +          Say yes here to build support for IIO SCMI Driver.
> > > > +          This provides ARM SCMI Protocol based IIO device.
> > > > +          This driver provides support for accelerometer and gyroscope
> > > > +          sensors available on SCMI based platforms.
> > > > +endmenu
> > > > diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile
> > > > new file mode 100644
> > > > index 000000000000..f13140a2575a
> > > > --- /dev/null
> > > > +++ b/drivers/iio/common/scmi_sensors/Makefile
> > > > @@ -0,0 +1,5 @@
> > > > +# SPDX - License - Identifier : GPL - 2.0 - only
> > > > +#
> > > > +# Makefile for the IIO over SCMI
> > > > +#
> > > > +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o
> > > > diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > new file mode 100644
> > > > index 000000000000..872d87ca6256
> > > > --- /dev/null
> > > > +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > @@ -0,0 +1,683 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +
> > > > +/*
> > > > + * System Control and Management Interface(SCMI) based IIO sensor driver
> > > > + *
> > > > + * Copyright (C) 2021 Google LLC
> > > > + */
> > > > +
> > > > +#include <linux/delay.h>
> > > > +#include <linux/err.h>
> > > > +#include <linux/iio/buffer.h>
> > > > +#include <linux/iio/iio.h>
> > > > +#include <linux/iio/kfifo_buf.h>
> > > > +#include <linux/iio/sysfs.h>
> > > > +#include <linux/kernel.h>
> > > > +#include <linux/kthread.h>
> > > > +#include <linux/module.h>
> > > > +#include <linux/scmi_protocol.h>
> > > > +#include <linux/time.h>
> > > > +#include <linux/types.h>
> > > > +
> > > > +#define SCMI_IIO_NUM_OF_AXIS 3
> > > > +
> > > > +struct scmi_iio_priv {
> > > > + struct scmi_handle *handle;
> > > > + const struct scmi_sensor_info *sensor_info;
> > > > + struct iio_dev *indio_dev;
> > > > + /* adding one additional channel for timestamp */
> > > > + s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1];
> > > > + struct notifier_block sensor_update_nb;
> > > > + u32 *freq_avail;
> > > > +};
> > > > +
> > > > +static int scmi_iio_sensor_update_cb(struct notifier_block *nb,
> > > > +                              unsigned long event, void *data)
> > > > +{
> > > > + struct scmi_sensor_update_report *sensor_update = data;
> > > > + struct iio_dev *scmi_iio_dev;
> > > > + struct scmi_iio_priv *sensor;
> > > > + s8 tstamp_scale;
> > > > + u64 time, time_ns;
> > > > + int i;
> > > > +
> > > > + if (sensor_update->readings_count == 0)
> > > > +         return NOTIFY_DONE;
> > > > +
> > > > + sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb);
> > > > +
> > > > + for (i = 0; i < sensor_update->readings_count; i++)
> > > > +         sensor->iio_buf[i] = sensor_update->readings[i].value;
> > > > +
> > > > + if (!sensor->sensor_info->timestamped) {
> > > > +         time_ns = ktime_to_ns(sensor_update->timestamp);
> > > > + } else {
> > > > +         /*
> > > > +          *  All the axes are supposed to have the same value for timestamp.
> > > > +          *  We are just using the values from the Axis 0 here.
> > > > +          */
> > > > +         time = sensor_update->readings[0].timestamp;
> > > > +
> > > > +         /*
> > > > +          *  Timestamp returned by SCMI is in seconds and is equal to
> > > > +          *  time * power-of-10 multiplier(tstamp_scale) seconds.
> > > > +          *  Converting the timestamp to nanoseconds below.
> > > > +          */
> > > > +         tstamp_scale = sensor->sensor_info->tstamp_scale +
> > > > +                        const_ilog2(NSEC_PER_SEC) / const_ilog2(10);
> > > > +         if (tstamp_scale < 0) {
> > > > +                 do_div(time, int_pow(10, abs(tstamp_scale)));
> > > > +                 time_ns = time;
> > > > +         } else {
> > > > +                 time_ns = time * int_pow(10, tstamp_scale);
> > > > +         }
> > > > + }
> > > > +
> > > > + scmi_iio_dev = sensor->indio_dev;
> > > > + iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf,
> > > > +                                    time_ns);
> > > > + return NOTIFY_OK;
> > > > +}
> > > > +
> > > > +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev)
> > > > +{
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > + u32 sensor_id = sensor->sensor_info->id;
> > > > + u32 sensor_config = 0;
> > > > + int err;
> > > > +
> > > > + if (sensor->sensor_info->timestamped)
> > > > +         sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > > +                                     SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > > +
> > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > > +                             SCMI_SENS_CFG_SENSOR_ENABLE);
> > > > +
> > > > + err = sensor->handle->notify_ops->register_event_notifier(sensor->handle,
> > > > +                 SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > > +                 &sensor_id, &sensor->sensor_update_nb);
> > > > + if (err) {
> > > > +         dev_err(&iio_dev->dev,
> > > > +                 "Error in registering sensor update notifier for sensor %s err %d",
> > > > +                 sensor->sensor_info->name, err);
> > > > +         return err;
> > > > + }
> > > > +
> > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > > +                 sensor->sensor_info->id, sensor_config);
> > > > + if (err) {
> > > > +         sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > > +                         SCMI_PROTOCOL_SENSOR,
> > > > +                         SCMI_EVENT_SENSOR_UPDATE, &sensor_id,
> > > > +                         &sensor->sensor_update_nb);
> > > > +         dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d",
> > > > +                 sensor->sensor_info->name, err);
> > > > + }
> > > > +
> > > > + return err;
> > > > +}
> > > > +
> > > > +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev)
> > > > +{
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > + u32 sensor_id = sensor->sensor_info->id;
> > > > + u32 sensor_config = 0;
> > > > + int err;
> > > > +
> > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > > +                             SCMI_SENS_CFG_SENSOR_DISABLE);
> > > > +
> > > > + err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > > +                 SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > > +                 &sensor_id, &sensor->sensor_update_nb);
> > > > + if (err) {
> > > > +         dev_err(&iio_dev->dev,
> > > > +                 "Error in unregistering sensor update notifier for sensor %s err %d",
> > > > +                 sensor->sensor_info->name, err);
> > > > +         return err;
> > > > + }
> > > > +
> > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id,
> > > > +                                              sensor_config);
> > > > + if (err) {
> > > > +         dev_err(&iio_dev->dev,
> > > > +                 "Error in disabling sensor %s with err %d",
> > > > +                 sensor->sensor_info->name, err);
> > > > + }
> > > > +
> > > > + return err;
> > > > +}
> > > > +
> > > > +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
> > > > + .preenable = scmi_iio_buffer_preenable,
> > > > + .postdisable = scmi_iio_buffer_postdisable,
> > > > +};
> > > > +
> > > > +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
> > > > +{
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > + const unsigned long UHZ_PER_HZ = 1000000UL;
> > > > + u64 sec, mult, uHz, sf;
> > > > + u32 sensor_config;
> > > > + char buf[32];
> > > > +
> > > > + int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > > +                 sensor->sensor_info->id, &sensor_config);
> > > > + if (err) {
> > > > +         dev_err(&iio_dev->dev,
> > > > +                 "Error in getting sensor config for sensor %s err %d",
> > > > +                 sensor->sensor_info->name, err);
> > > > +         return err;
> > > > + }
> > > > +
> > > > + uHz = val * UHZ_PER_HZ + val2;
> > > > +
> > > > + /*
> > > > +  * The seconds field in the sensor interval in SCMI is 16 bits long
> > > > +  * Therefore seconds  = 1/Hz <= 0xFFFF. As floating point calculations are
> > > > +  * discouraged in the kernel driver code, to calculate the scale factor (sf)
> > > > +  * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000
> > > > +  * To calculate the multiplier,we convert the sf into char string  and
> > > > +  * count the number of characters
> > > > +  */
> > > > + sf = (u64)uHz * 0xFFFF;
> > > > + do_div(sf,  UHZ_PER_HZ);
> > > > + mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
> > > > +
> > > > + sec = int_pow(10, mult) * UHZ_PER_HZ;
> > > > + do_div(sec, uHz);
> > > > + if (sec == 0) {
> > > > +         dev_err(&iio_dev->dev,
> > > > +                 "Trying to set invalid sensor update value for sensor %s",
> > > > +                 sensor->sensor_info->name);
> > > > +         return -EINVAL;
> > > > + }
> > > > +
> > > > + sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK;
> > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec);
> > > > + sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK;
> > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult);
> > > > +
> > > > + if (sensor->sensor_info->timestamped) {
> > > > +         sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK;
> > > > +         sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > > +                                     SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > > + }
> > > > +
> > > > + sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK;
> > > > + sensor_config |=
> > > > +         FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO);
> > > > +
> > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > > +                 sensor->sensor_info->id, sensor_config);
> > > > + if (err)
> > > > +         dev_err(&iio_dev->dev,
> > > > +                 "Error in setting sensor update interval for sensor %s value %u err %d",
> > > > +                 sensor->sensor_info->name, sensor_config, err);
> > > > +
> > > > + return err;
> > > > +}
> > > > +
> > > > +static int scmi_iio_write_raw(struct iio_dev *iio_dev,
> > > > +                       struct iio_chan_spec const *chan, int val,
> > > > +                       int val2, long mask)
> > > > +{
> > > > + int err;
> > > > +
> > > > + switch (mask) {
> > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > +         mutex_lock(&iio_dev->mlock);
> > > > +         err = scmi_iio_set_odr_val(iio_dev, val, val2);
> > > > +         mutex_unlock(&iio_dev->mlock);
> > > > +         return err;
> > > > + default:
> > > > +         return -EINVAL;
> > > > + }
> > > > +}
> > > > +
> > > > +static int scmi_iio_read_avail(struct iio_dev *iio_dev,
> > > > +                        struct iio_chan_spec const *chan,
> > > > +                        const int **vals, int *type, int *length,
> > > > +                        long mask)
> > > > +{
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > +
> > > > + switch (mask) {
> > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > +         *vals = sensor->freq_avail;
> > > > +         *type = IIO_VAL_INT_PLUS_MICRO;
> > > > +         *length = sensor->sensor_info->intervals.count * 2;
> > > > +         if (sensor->sensor_info->intervals.segmented)
> > > > +                 return IIO_AVAIL_RANGE;
> > > > +         else
> > > > +                 return IIO_AVAIL_LIST;
> > > > + default:
> > > > +         return -EINVAL;
> > > > + }
> > > > +}
> > > > +
> > > > +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz)
> > > > +{
> > > > + u64 rem, freq;
> > > > +
> > > > + freq = NSEC_PER_SEC;
> > > > + rem = do_div(freq, interval_ns);
> > > > + *hz = freq;
> > > > + *uhz = rem * 1000000UL;
> > > > + do_div(*uhz, interval_ns);
> > > > +}
> > > > +
> > > > +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2)
> > > > +{
> > > > + u64 sensor_update_interval, sensor_interval_mult, hz, uhz;
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > + u32 sensor_config;
> > > > + int mult;
> > > > +
> > > > + int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > > +                 sensor->sensor_info->id, &sensor_config);
> > > > + if (err) {
> > > > +         dev_err(&iio_dev->dev,
> > > > +                 "Error in getting sensor config for sensor %s err %d",
> > > > +                 sensor->sensor_info->name, err);
> > > > +         return err;
> > > > + }
> > > > +
> > > > + sensor_update_interval =
> > > > +         SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC;
> > > > +
> > > > + mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config);
> > > > + if (mult < 0) {
> > > > +         sensor_interval_mult = int_pow(10, abs(mult));
> > > > +         do_div(sensor_update_interval, sensor_interval_mult);
> > > > + } else {
> > > > +         sensor_interval_mult = int_pow(10, mult);
> > > > +         sensor_update_interval =
> > > > +                 sensor_update_interval * sensor_interval_mult;
> > > > + }
> > > > +
> > > > + convert_ns_to_freq(sensor_update_interval, &hz, &uhz);
> > > > + *val = hz;
> > > > + *val2 = uhz;
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int scmi_iio_read_raw(struct iio_dev *iio_dev,
> > > > +                      struct iio_chan_spec const *ch, int *val,
> > > > +                      int *val2, long mask)
> > > > +{
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > + s8 scale;
> > > > + int ret;
> > > > +
> > > > + switch (mask) {
> > > > + case IIO_CHAN_INFO_SCALE:
> > > > +         scale = sensor->sensor_info->axis[ch->scan_index].scale;
> > > > +         if (scale < 0) {
> > > > +                 *val = 1;
> > > > +                 *val2 = int_pow(10, abs(scale));
> > > > +                 return IIO_VAL_FRACTIONAL;
> > > > +         }
> > > > +         *val = int_pow(10, scale);
> > > > +         return IIO_VAL_INT;
> > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > +         ret = scmi_iio_get_odr_val(iio_dev, val, val2);
> > > > +         return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
> > > > + default:
> > > > +         return -EINVAL;
> > > > + }
> > > > +}
> > > > +
> > > > +static const struct iio_info scmi_iio_info = {
> > > > + .read_raw = scmi_iio_read_raw,
> > > > + .read_avail = scmi_iio_read_avail,
> > > > + .write_raw = scmi_iio_write_raw,
> > > > +};
> > > > +
> > > > +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
> > > > +                                   uintptr_t private,
> > > > +                                   const struct iio_chan_spec *chan,
> > > > +                                   char *buf)
> > > > +{
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > + u64 resolution, rem;
> > > > + s64 min_range, max_range;
> > > > + s8 exponent, scale;
> > > > + int len = 0;
> > > > +
> > > > + /*
> > > > +  * All the axes are supposed to have the same value for range and resolution.
> > > > +  * We are just using the values from the Axis 0 here.
> > > > +  */
> > > > + if (sensor->sensor_info->axis[0].extended_attrs) {
> > > > +         min_range = sensor->sensor_info->axis[0].attrs.min_range;
> > > > +         max_range = sensor->sensor_info->axis[0].attrs.max_range;
> > > > +         resolution = sensor->sensor_info->axis[0].resolution;
> > > > +         exponent = sensor->sensor_info->axis[0].exponent;
> > > > +         scale = sensor->sensor_info->axis[0].scale;
> > > > +
> > > > +         /*
> > > > +          * To provide the raw value for the resolution to the userspace,
> > > > +          * need to divide the resolution exponent by the sensor scale
> > > > +          */
> > > > +         exponent = exponent - scale;
> > > > +         if (exponent < 0) {
> > > > +                 rem = do_div(resolution,
> > > > +                              int_pow(10, abs(exponent))
> > > > +                              );
> > > > +                 len = scnprintf(buf, PAGE_SIZE,
> > > > +                                 "[%lld %llu.%llu %lld]\n", min_range,
> > > > +                                 resolution, rem, max_range);
> > > > +         } else {
> > > > +                 resolution = resolution * int_pow(10, exponent);
> > > > +                 len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
> > > > +                                 min_range, resolution, max_range);
> > > > +         }
> > > > + }
> > > > + return len;
> > > > +}
> > > > +
> > > > +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = {
> > > > + {
> > > > +         .name = "raw_available",
> > > > +         .read = scmi_iio_get_raw_available,
> > > > +         .shared = IIO_SHARED_BY_TYPE,
> > > > + },
> > > > + {},
> > > > +};
> > > > +
> > > > +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan,
> > > > +                                    int scan_index)
> > > > +{
> > > > + iio_chan->type = IIO_TIMESTAMP;
> > > > + iio_chan->channel = -1;
> > > > + iio_chan->scan_index = scan_index;
> > > > + iio_chan->scan_type.sign = 'u';
> > > > + iio_chan->scan_type.realbits = 64;
> > > > + iio_chan->scan_type.storagebits = 64;
> > > > +}
> > > > +
> > > > +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan,
> > > > +                               enum iio_chan_type type,
> > > > +                               enum iio_modifier mod, int scan_index)
> > > > +{
> > > > + iio_chan->type = type;
> > > > + iio_chan->modified = 1;
> > > > + iio_chan->channel2 = mod;
> > > > + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE);
> > > > + iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > > + iio_chan->info_mask_shared_by_type_available =
> > > > +         BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > > + iio_chan->scan_index = scan_index;
> > > > + iio_chan->scan_type.sign = 's';
> > > > + iio_chan->scan_type.realbits = 64;
> > > > + iio_chan->scan_type.storagebits = 64;
> > > > + iio_chan->scan_type.endianness = IIO_LE;
> > > > + iio_chan->ext_info = scmi_iio_ext_info;
> > > > +}
> > > > +
> > > > +static int scmi_iio_get_chan_modifier(const char *name,
> > > > +                               enum iio_modifier *modifier)
> > > > +{
> > > > + char *pch, mod;
> > > > +
> > > > + if (!name)
> > > > +         return -EINVAL;
> > > > +
> > > > + pch = strrchr(name, '_');
> > > > + if (!pch)
> > > > +         return -EINVAL;
> > > > +
> > > > + mod = *(pch + 1);
> > > > + switch (mod) {
> > > > + case 'X':
> > > > +         *modifier = IIO_MOD_X;
> > > > +         return 0;
> > > > + case 'Y':
> > > > +         *modifier = IIO_MOD_Y;
> > > > +         return 0;
> > > > + case 'Z':
> > > > +         *modifier = IIO_MOD_Z;
> > > > +         return 0;
> > > > + default:
> > > > +         return -EINVAL;
> > > > + }
> > > > +}
> > > > +
> > > > +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type)
> > > > +{
> > > > + switch (scmi_type) {
> > > > + case METERS_SEC_SQUARED:
> > > > +         *iio_type = IIO_ACCEL;
> > > > +         return 0;
> > > > + case RADIANS_SEC:
> > > > +         *iio_type = IIO_ANGL_VEL;
> > > > +         return 0;
> > > > + default:
> > > > +         return -EINVAL;
> > > > + }
> > > > +}
> > > > +
> > > > +static u64 scmi_iio_convert_interval_to_ns(u32 val)
> > > > +{
> > > > + u64 sensor_update_interval =
> > > > +         SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC;
> > > > + u64 sensor_interval_mult;
> > > > + int mult;
> > > > +
> > > > + mult = SCMI_SENS_INTVL_GET_EXP(val);
> > > > + if (mult < 0) {
> > > > +         sensor_interval_mult = int_pow(10, abs(mult));
> > > > +         do_div(sensor_update_interval, sensor_interval_mult);
> > > > + } else {
> > > > +         sensor_interval_mult = int_pow(10, mult);
> > > > +         sensor_update_interval =
> > > > +                 sensor_update_interval * sensor_interval_mult;
> > > > + }
> > > > + return sensor_update_interval;
> > > > +}
> > > > +
> > > > +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev)
> > > > +{
> > > > + u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns,
> > > > +         hz, uhz;
> > > > + unsigned int cur_interval, low_interval, high_interval, step_size;
> > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > + int i;
> > > > +
> > > > + sensor->freq_avail =
> > > > +         devm_kzalloc(&iio_dev->dev,
> > > > +                      sizeof(*sensor->freq_avail) *
> > > > +                              (sensor->sensor_info->intervals.count * 2),
> > > > +                      GFP_KERNEL);
> > > > + if (!sensor->freq_avail)
> > > > +         return -ENOMEM;
> > > > +
> > > > + if (sensor->sensor_info->intervals.segmented) {
> > > > +         low_interval = sensor->sensor_info->intervals
> > > > +                                .desc[SCMI_SENS_INTVL_SEGMENT_LOW];
> > > > +         low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval);
> > > > +         convert_ns_to_freq(low_interval_ns, &hz, &uhz);
> > > > +         sensor->freq_avail[0] = hz;
> > > > +         sensor->freq_avail[1] = uhz;
> > > > +
> > > > +         step_size = sensor->sensor_info->intervals
> > > > +                             .desc[SCMI_SENS_INTVL_SEGMENT_STEP];
> > > > +         step_size_ns = scmi_iio_convert_interval_to_ns(step_size);
> > > > +         convert_ns_to_freq(step_size_ns, &hz, &uhz);
> > > > +         sensor->freq_avail[2] = hz;
> > > > +         sensor->freq_avail[3] = uhz;
> > > > +
> > > > +         high_interval = sensor->sensor_info->intervals
> > > > +                                 .desc[SCMI_SENS_INTVL_SEGMENT_HIGH];
> > > > +         high_interval_ns =
> > > > +                 scmi_iio_convert_interval_to_ns(high_interval);
> > > > +         convert_ns_to_freq(high_interval_ns, &hz, &uhz);
> > > > +         sensor->freq_avail[4] = hz;
> > > > +         sensor->freq_avail[5] = uhz;
> > > > + } else {
> > > > +         for (i = 0; i < sensor->sensor_info->intervals.count; i++) {
> > > > +                 cur_interval = sensor->sensor_info->intervals.desc[i];
> > > > +                 cur_interval_ns =
> > > > +                         scmi_iio_convert_interval_to_ns(cur_interval);
> > > > +                 convert_ns_to_freq(cur_interval_ns, &hz, &uhz);
> > > > +                 sensor->freq_avail[i * 2] = hz;
> > > > +                 sensor->freq_avail[i * 2 + 1] = uhz;
> > > > +         }
> > > > + }
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev)
> > > > +{
> > > > + struct iio_buffer *buffer;
> > > > +
> > > > + buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev);
> > > > + if (!buffer)
> > > > +         return -ENOMEM;
> > > > +
> > > > + iio_device_attach_buffer(scmi_iiodev, buffer);
> > > > + scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE;
> > > > + scmi_iiodev->setup_ops = &scmi_iio_buffer_ops;
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static struct iio_dev *scmi_alloc_iiodev(struct device *dev,
> > > > +                                  struct scmi_handle *handle,
> > > > +                                  const struct scmi_sensor_info *sensor_info)
> > > > +{
> > > > + struct iio_chan_spec *iio_channels;
> > > > + struct scmi_iio_priv *sensor;
> > > > + enum iio_modifier modifier;
> > > > + enum iio_chan_type type;
> > > > + struct iio_dev *iiodev;
> > > > + int i, ret;
> > > > +
> > > > + iiodev = devm_iio_device_alloc(dev, sizeof(*sensor));
> > > > + if (!iiodev)
> > > > +         return ERR_PTR(-ENOMEM);
> > > > +
> > > > + iiodev->modes = INDIO_DIRECT_MODE;
> > > > + iiodev->dev.parent = dev;
> > > > + sensor = iio_priv(iiodev);
> > > > + sensor->handle = handle;
> > > > + sensor->sensor_info = sensor_info;
> > > > + sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb;
> > > > + sensor->indio_dev = iiodev;
> > > > +
> > > > + /* adding one additional channel for timestamp */
> > > > + iiodev->num_channels = sensor_info->num_axis + 1;
> > > > + iiodev->name = sensor_info->name;
> > > > + iiodev->info = &scmi_iio_info;
> > > > +
> > > > + iio_channels =
> > > > +         devm_kzalloc(dev,
> > > > +                      sizeof(*iio_channels) * (iiodev->num_channels),
> > > > +                      GFP_KERNEL);
> > > > + if (!iio_channels)
> > > > +         return ERR_PTR(-ENOMEM);
> > > > +
> > > > + ret = scmi_iio_set_sampling_freq_avail(iiodev);
> > > > + if (ret < 0)
> > > > +         return ERR_PTR(ret);
> > > > +
> > > > + for (i = 0; i < sensor_info->num_axis; i++) {
> > > > +         ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type);
> > > > +         if (ret < 0)
> > > > +                 return ERR_PTR(ret);
> > > > +
> > > > +         ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name,
> > > > +                                          &modifier);
> > > > +         if (ret < 0)
> > > > +                 return ERR_PTR(ret);
> > > > +
> > > > +         scmi_iio_set_data_channel(&iio_channels[i], type, modifier,
> > > > +                                   sensor_info->axis[i].id);
> > > > + }
> > > > +
> > > > + scmi_iio_set_timestamp_channel(&iio_channels[i], i);
> > > > + iiodev->channels = iio_channels;
> > > > + return iiodev;
> > > > +}
> > > > +
> > > > +static int scmi_iio_dev_probe(struct scmi_device *sdev)
> > > > +{
> > > > + const struct scmi_sensor_info *sensor_info;
> > > > + struct scmi_handle *handle = sdev->handle;
> > > > + struct device *dev = &sdev->dev;
> > > > + struct iio_dev *scmi_iio_dev;
> > > > + u16 nr_sensors;
> > > > + int err = -ENODEV, i;
> > > > +
> > > > + if (!handle || !handle->sensor_ops) {
> > > > +         dev_err(dev, "SCMI device has no sensor interface\n");
> > > > +         return -EINVAL;
> > > > + }
> > > > +
> > > > + nr_sensors = handle->sensor_ops->count_get(handle);
> > > > + if (!nr_sensors) {
> > > > +         dev_dbg(dev, "0 sensors found via SCMI bus\n");
> > > > +         return -ENODEV;
> > > > + }
> > > > +
> > > > + for (i = 0; i < nr_sensors; i++) {
> > > > +         sensor_info = handle->sensor_ops->info_get(handle, i);
> > > > +         if (!sensor_info) {
> > > > +                 dev_err(dev, "SCMI sensor %d has missing info\n", i);
> > > > +                 return -EINVAL;
> > > > +         }
> > > > +
> > > > +         /* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > > +         if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS)
> > > > +                 continue;
> > > > +
> > > > +         /* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > > +         if (sensor_info->axis[0].type != METERS_SEC_SQUARED &&
> > > > +             sensor_info->axis[0].type != RADIANS_SEC)
> > > > +                 continue;
> > > > +
> > > > +         scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info);
> > > > +         if (IS_ERR(scmi_iio_dev)) {
> > > > +                 dev_err(dev,
> > > > +                         "failed to allocate IIO device for sensor %s: %ld\n",
> > > > +                         sensor_info->name, PTR_ERR(scmi_iio_dev));
> > > > +                 return PTR_ERR(scmi_iio_dev);
> > > > +         }
> > > > +
> > > > +         err = scmi_iio_buffers_setup(scmi_iio_dev);
> > > > +         if (err < 0) {
> > > > +                 dev_err(dev,
> > > > +                         "IIO buffer setup error at sensor %s: %d\n",
> > > > +                         sensor_info->name, err);
> > > > +                 return err;
> > > > +         }
> > > > +
> > > > +         err = devm_iio_device_register(dev, scmi_iio_dev);
> > > > +         if (err) {
> > > > +                 dev_err(dev,
> > > > +                         "IIO device registration failed at sensor %s: %d\n",
> > > > +                         sensor_info->name, err);
> > > > +                 return err;
> > > > +         }
> > > > + }
> > > > + return err;
> > > > +}
> > > > +
> > > > +static const struct scmi_device_id scmi_id_table[] = {
> > > > + { SCMI_PROTOCOL_SENSOR, "iiodev" },
> > > > + {},
> > > > +};
> > > > +
> > > > +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > > > +
> > > > +static struct scmi_driver scmi_iiodev_driver = {
> > > > + .name = "scmi-sensor-iiodev",
> > > > + .probe = scmi_iio_dev_probe,
> > > > + .id_table = scmi_id_table,
> > > > +};
> > > > +
> > > > +module_scmi_driver(scmi_iiodev_driver);
> > > > +
> > > > +MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>");
> > > > +MODULE_DESCRIPTION("SCMI IIO Driver");
> > > > +MODULE_LICENSE("GPL v2");
> > >
> >

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-12 17:54         ` Jyoti Bhayana
@ 2021-03-13 17:11           ` Jonathan Cameron
  2021-03-13 19:55             ` Jyoti Bhayana
  0 siblings, 1 reply; 16+ messages in thread
From: Jonathan Cameron @ 2021-03-13 17:11 UTC (permalink / raw)
  To: Jyoti Bhayana
  Cc: Cristian Marussi, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Sudeep Holla,
	Enrico Granata, Mikhail Golubev, Igor Skalkin, Peter Hilber,
	Ankit Arora, Guru Nagarajan, kernel test robot

On Fri, 12 Mar 2021 09:54:12 -0800
Jyoti Bhayana <jbhayana@google.com> wrote:

> Hi Jonathan,
> 
> I also see what Cristian has observed that rc2 is still pointing to v6
> of the SCMI IIO driver patch and it also doesn't have the changes
> which you did in rc1 for changing long long to s64.
> 
> Thanks,
> Jyoti

Gah!  I must have grabbed wrong one somehow.

Anyhow, there is now a 
https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2-take2

version which should looks to have the right dates an all.  Merge into
my togreg still the same as it was previously.

Thanks,

Jonathan

> 
> 
> On Fri, Mar 12, 2021 at 5:31 AM Cristian Marussi
> <cristian.marussi@arm.com> wrote:
> >
> > Hi Jonathan,
> >
> > thanks for this, I was starting working on top of this new immutable
> > branch BUT I spotted a thing that I wanted to check.
> >
> > You latest immutable:
> >  https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
> >
> > looking at the code in the SCMI IIO Jyoti driver
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/commit/?h=ib-iio-scmi-5.12-rc2&id=f937d8c1ef246d99d2174ed88c629f6e24823918
> >
> > this seems to me that still includes v6 of IIO Jyoti patch:
> >
> > https://lore.kernel.org/linux-iio/20210212172235.507028-2-jbhayana@google.com/
> >
> > (the offending one on the bot report related to div symbols on MIPS if I got it right),
> > in fact it has 710 insertions and still uses div64_u64 (and the Link tag leads to the v6)
> >
> > while the lastest v7
> >
> > https://lore.kernel.org/lkml/20210309231259.78050-2-jbhayana@google.com/
> >
> > has 715 insetions and uses do_div instead, but in fact this v7 has a Link:
> > tag still pointing to v6 (maybe this is the issue...?)
> >
> > Sorry for the noise if I missed something and everything is fine please
> > ignore me and I'll go ahead with this branch if you say so.
> >
> > Thanks
> >
> > Cristian
> >
> >
> >
> > On Fri, Mar 12, 2021 at 12:16:39PM +0000, Jonathan Cameron wrote:  
> > > On Thu, 11 Mar 2021 21:08:44 +0000
> > > Jonathan Cameron <jic23@kernel.org> wrote:
> > >  
> > > > On Tue,  9 Mar 2021 23:12:59 +0000
> > > > Jyoti Bhayana <jbhayana@google.com> wrote:
> > > >  
> > > > > This change provides ARM SCMI Protocol based IIO device.
> > > > > This driver provides support for Accelerometer and Gyroscope using
> > > > > SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> > > > >
> > > > > Reported-by: kernel test robot <lkp@intel.com>
> > > > > Signed-off-by: Jyoti Bhayana <jbhayana@google.com>
> > > > > Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com
> > > > > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>  
> > > >
> > > > https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
> > > >
> > > > New immutable branch.  I move to rc2 as the base as the rc1 tag
> > > > has been removed for well publicized reasons.
> > > >
> > > > Should have the 0-day results in tomorrow morning. I'll shout
> > > > if anything else shows up.  
> > > 0-day is happy, so this is now immutable and suitable for pulling in through
> > > whatever trees need it.
> > >
> > > Thanks,
> > >
> > > Jonathan
> > >  
> > > >
> > > > Also rebased the togreg branch for the same reason (hadn't pushed it
> > > > out as anything other than testing, so hopefully no one was basing
> > > > anything significant on top)
> > > >
> > > > Thanks,
> > > >
> > > > Jonathan
> > > >  
> > > > > ---
> > > > >  MAINTAINERS                                |   6 +
> > > > >  drivers/firmware/arm_scmi/driver.c         |   2 +-
> > > > >  drivers/iio/common/Kconfig                 |   1 +
> > > > >  drivers/iio/common/Makefile                |   1 +
> > > > >  drivers/iio/common/scmi_sensors/Kconfig    |  18 +
> > > > >  drivers/iio/common/scmi_sensors/Makefile   |   5 +
> > > > >  drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
> > > > >  7 files changed, 715 insertions(+), 1 deletion(-)
> > > > >  create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
> > > > >  create mode 100644 drivers/iio/common/scmi_sensors/Makefile
> > > > >  create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > >
> > > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > > index d92f85ca831d..14227980f3d2 100644
> > > > > --- a/MAINTAINERS
> > > > > +++ b/MAINTAINERS
> > > > > @@ -8692,6 +8692,12 @@ S: Maintained
> > > > >  F:       Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
> > > > >  F:       drivers/iio/multiplexer/iio-mux.c
> > > > >
> > > > > +IIO SCMI BASED DRIVER
> > > > > +M:       Jyoti Bhayana <jbhayana@google.com>
> > > > > +L:       linux-iio@vger.kernel.org
> > > > > +S:       Maintained
> > > > > +F:       drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > > +
> > > > >  IIO SUBSYSTEM AND DRIVERS
> > > > >  M:       Jonathan Cameron <jic23@kernel.org>
> > > > >  R:       Lars-Peter Clausen <lars@metafoo.de>
> > > > > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > > > > index cacdf1589b10..3e748e57deab 100644
> > > > > --- a/drivers/firmware/arm_scmi/driver.c
> > > > > +++ b/drivers/firmware/arm_scmi/driver.c
> > > > > @@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = {
> > > > >   { SCMI_PROTOCOL_SYSTEM, { "syspower" },},
> > > > >   { SCMI_PROTOCOL_PERF,   { "cpufreq" },},
> > > > >   { SCMI_PROTOCOL_CLOCK,  { "clocks" },},
> > > > > - { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> > > > > + { SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },},
> > > > >   { SCMI_PROTOCOL_RESET,  { "reset" },},
> > > > >   { SCMI_PROTOCOL_VOLTAGE,  { "regulator" },},
> > > > >  };
> > > > > diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> > > > > index 2b9ee9161abd..0334b4954773 100644
> > > > > --- a/drivers/iio/common/Kconfig
> > > > > +++ b/drivers/iio/common/Kconfig
> > > > > @@ -6,5 +6,6 @@
> > > > >  source "drivers/iio/common/cros_ec_sensors/Kconfig"
> > > > >  source "drivers/iio/common/hid-sensors/Kconfig"
> > > > >  source "drivers/iio/common/ms_sensors/Kconfig"
> > > > > +source "drivers/iio/common/scmi_sensors/Kconfig"
> > > > >  source "drivers/iio/common/ssp_sensors/Kconfig"
> > > > >  source "drivers/iio/common/st_sensors/Kconfig"
> > > > > diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> > > > > index 4bc30bb548e2..fad40e1e1718 100644
> > > > > --- a/drivers/iio/common/Makefile
> > > > > +++ b/drivers/iio/common/Makefile
> > > > > @@ -11,5 +11,6 @@
> > > > >  obj-y += cros_ec_sensors/
> > > > >  obj-y += hid-sensors/
> > > > >  obj-y += ms_sensors/
> > > > > +obj-y += scmi_sensors/
> > > > >  obj-y += ssp_sensors/
> > > > >  obj-y += st_sensors/
> > > > > diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig
> > > > > new file mode 100644
> > > > > index 000000000000..67e084cbb1ab
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/common/scmi_sensors/Kconfig
> > > > > @@ -0,0 +1,18 @@
> > > > > +#
> > > > > +# IIO over SCMI
> > > > > +#
> > > > > +# When adding new entries keep the list in alphabetical order
> > > > > +
> > > > > +menu "IIO SCMI Sensors"
> > > > > +
> > > > > +config IIO_SCMI
> > > > > + tristate "IIO SCMI"
> > > > > +        depends on ARM_SCMI_PROTOCOL
> > > > > +        select IIO_BUFFER
> > > > > +        select IIO_KFIFO_BUF
> > > > > + help
> > > > > +          Say yes here to build support for IIO SCMI Driver.
> > > > > +          This provides ARM SCMI Protocol based IIO device.
> > > > > +          This driver provides support for accelerometer and gyroscope
> > > > > +          sensors available on SCMI based platforms.
> > > > > +endmenu
> > > > > diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile
> > > > > new file mode 100644
> > > > > index 000000000000..f13140a2575a
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/common/scmi_sensors/Makefile
> > > > > @@ -0,0 +1,5 @@
> > > > > +# SPDX - License - Identifier : GPL - 2.0 - only
> > > > > +#
> > > > > +# Makefile for the IIO over SCMI
> > > > > +#
> > > > > +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o
> > > > > diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > > new file mode 100644
> > > > > index 000000000000..872d87ca6256
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > > @@ -0,0 +1,683 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > +
> > > > > +/*
> > > > > + * System Control and Management Interface(SCMI) based IIO sensor driver
> > > > > + *
> > > > > + * Copyright (C) 2021 Google LLC
> > > > > + */
> > > > > +
> > > > > +#include <linux/delay.h>
> > > > > +#include <linux/err.h>
> > > > > +#include <linux/iio/buffer.h>
> > > > > +#include <linux/iio/iio.h>
> > > > > +#include <linux/iio/kfifo_buf.h>
> > > > > +#include <linux/iio/sysfs.h>
> > > > > +#include <linux/kernel.h>
> > > > > +#include <linux/kthread.h>
> > > > > +#include <linux/module.h>
> > > > > +#include <linux/scmi_protocol.h>
> > > > > +#include <linux/time.h>
> > > > > +#include <linux/types.h>
> > > > > +
> > > > > +#define SCMI_IIO_NUM_OF_AXIS 3
> > > > > +
> > > > > +struct scmi_iio_priv {
> > > > > + struct scmi_handle *handle;
> > > > > + const struct scmi_sensor_info *sensor_info;
> > > > > + struct iio_dev *indio_dev;
> > > > > + /* adding one additional channel for timestamp */
> > > > > + s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1];
> > > > > + struct notifier_block sensor_update_nb;
> > > > > + u32 *freq_avail;
> > > > > +};
> > > > > +
> > > > > +static int scmi_iio_sensor_update_cb(struct notifier_block *nb,
> > > > > +                              unsigned long event, void *data)
> > > > > +{
> > > > > + struct scmi_sensor_update_report *sensor_update = data;
> > > > > + struct iio_dev *scmi_iio_dev;
> > > > > + struct scmi_iio_priv *sensor;
> > > > > + s8 tstamp_scale;
> > > > > + u64 time, time_ns;
> > > > > + int i;
> > > > > +
> > > > > + if (sensor_update->readings_count == 0)
> > > > > +         return NOTIFY_DONE;
> > > > > +
> > > > > + sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb);
> > > > > +
> > > > > + for (i = 0; i < sensor_update->readings_count; i++)
> > > > > +         sensor->iio_buf[i] = sensor_update->readings[i].value;
> > > > > +
> > > > > + if (!sensor->sensor_info->timestamped) {
> > > > > +         time_ns = ktime_to_ns(sensor_update->timestamp);
> > > > > + } else {
> > > > > +         /*
> > > > > +          *  All the axes are supposed to have the same value for timestamp.
> > > > > +          *  We are just using the values from the Axis 0 here.
> > > > > +          */
> > > > > +         time = sensor_update->readings[0].timestamp;
> > > > > +
> > > > > +         /*
> > > > > +          *  Timestamp returned by SCMI is in seconds and is equal to
> > > > > +          *  time * power-of-10 multiplier(tstamp_scale) seconds.
> > > > > +          *  Converting the timestamp to nanoseconds below.
> > > > > +          */
> > > > > +         tstamp_scale = sensor->sensor_info->tstamp_scale +
> > > > > +                        const_ilog2(NSEC_PER_SEC) / const_ilog2(10);
> > > > > +         if (tstamp_scale < 0) {
> > > > > +                 do_div(time, int_pow(10, abs(tstamp_scale)));
> > > > > +                 time_ns = time;
> > > > > +         } else {
> > > > > +                 time_ns = time * int_pow(10, tstamp_scale);
> > > > > +         }
> > > > > + }
> > > > > +
> > > > > + scmi_iio_dev = sensor->indio_dev;
> > > > > + iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf,
> > > > > +                                    time_ns);
> > > > > + return NOTIFY_OK;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev)
> > > > > +{
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > + u32 sensor_id = sensor->sensor_info->id;
> > > > > + u32 sensor_config = 0;
> > > > > + int err;
> > > > > +
> > > > > + if (sensor->sensor_info->timestamped)
> > > > > +         sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > > > +                                     SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > > > +
> > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > > > +                             SCMI_SENS_CFG_SENSOR_ENABLE);
> > > > > +
> > > > > + err = sensor->handle->notify_ops->register_event_notifier(sensor->handle,
> > > > > +                 SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > > > +                 &sensor_id, &sensor->sensor_update_nb);
> > > > > + if (err) {
> > > > > +         dev_err(&iio_dev->dev,
> > > > > +                 "Error in registering sensor update notifier for sensor %s err %d",
> > > > > +                 sensor->sensor_info->name, err);
> > > > > +         return err;
> > > > > + }
> > > > > +
> > > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > > > +                 sensor->sensor_info->id, sensor_config);
> > > > > + if (err) {
> > > > > +         sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > > > +                         SCMI_PROTOCOL_SENSOR,
> > > > > +                         SCMI_EVENT_SENSOR_UPDATE, &sensor_id,
> > > > > +                         &sensor->sensor_update_nb);
> > > > > +         dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d",
> > > > > +                 sensor->sensor_info->name, err);
> > > > > + }
> > > > > +
> > > > > + return err;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev)
> > > > > +{
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > + u32 sensor_id = sensor->sensor_info->id;
> > > > > + u32 sensor_config = 0;
> > > > > + int err;
> > > > > +
> > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > > > +                             SCMI_SENS_CFG_SENSOR_DISABLE);
> > > > > +
> > > > > + err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > > > +                 SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > > > +                 &sensor_id, &sensor->sensor_update_nb);
> > > > > + if (err) {
> > > > > +         dev_err(&iio_dev->dev,
> > > > > +                 "Error in unregistering sensor update notifier for sensor %s err %d",
> > > > > +                 sensor->sensor_info->name, err);
> > > > > +         return err;
> > > > > + }
> > > > > +
> > > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id,
> > > > > +                                              sensor_config);
> > > > > + if (err) {
> > > > > +         dev_err(&iio_dev->dev,
> > > > > +                 "Error in disabling sensor %s with err %d",
> > > > > +                 sensor->sensor_info->name, err);
> > > > > + }
> > > > > +
> > > > > + return err;
> > > > > +}
> > > > > +
> > > > > +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
> > > > > + .preenable = scmi_iio_buffer_preenable,
> > > > > + .postdisable = scmi_iio_buffer_postdisable,
> > > > > +};
> > > > > +
> > > > > +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
> > > > > +{
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > + const unsigned long UHZ_PER_HZ = 1000000UL;
> > > > > + u64 sec, mult, uHz, sf;
> > > > > + u32 sensor_config;
> > > > > + char buf[32];
> > > > > +
> > > > > + int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > > > +                 sensor->sensor_info->id, &sensor_config);
> > > > > + if (err) {
> > > > > +         dev_err(&iio_dev->dev,
> > > > > +                 "Error in getting sensor config for sensor %s err %d",
> > > > > +                 sensor->sensor_info->name, err);
> > > > > +         return err;
> > > > > + }
> > > > > +
> > > > > + uHz = val * UHZ_PER_HZ + val2;
> > > > > +
> > > > > + /*
> > > > > +  * The seconds field in the sensor interval in SCMI is 16 bits long
> > > > > +  * Therefore seconds  = 1/Hz <= 0xFFFF. As floating point calculations are
> > > > > +  * discouraged in the kernel driver code, to calculate the scale factor (sf)
> > > > > +  * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000
> > > > > +  * To calculate the multiplier,we convert the sf into char string  and
> > > > > +  * count the number of characters
> > > > > +  */
> > > > > + sf = (u64)uHz * 0xFFFF;
> > > > > + do_div(sf,  UHZ_PER_HZ);
> > > > > + mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
> > > > > +
> > > > > + sec = int_pow(10, mult) * UHZ_PER_HZ;
> > > > > + do_div(sec, uHz);
> > > > > + if (sec == 0) {
> > > > > +         dev_err(&iio_dev->dev,
> > > > > +                 "Trying to set invalid sensor update value for sensor %s",
> > > > > +                 sensor->sensor_info->name);
> > > > > +         return -EINVAL;
> > > > > + }
> > > > > +
> > > > > + sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK;
> > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec);
> > > > > + sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK;
> > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult);
> > > > > +
> > > > > + if (sensor->sensor_info->timestamped) {
> > > > > +         sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK;
> > > > > +         sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > > > +                                     SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > > > + }
> > > > > +
> > > > > + sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK;
> > > > > + sensor_config |=
> > > > > +         FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO);
> > > > > +
> > > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > > > +                 sensor->sensor_info->id, sensor_config);
> > > > > + if (err)
> > > > > +         dev_err(&iio_dev->dev,
> > > > > +                 "Error in setting sensor update interval for sensor %s value %u err %d",
> > > > > +                 sensor->sensor_info->name, sensor_config, err);
> > > > > +
> > > > > + return err;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_write_raw(struct iio_dev *iio_dev,
> > > > > +                       struct iio_chan_spec const *chan, int val,
> > > > > +                       int val2, long mask)
> > > > > +{
> > > > > + int err;
> > > > > +
> > > > > + switch (mask) {
> > > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > > +         mutex_lock(&iio_dev->mlock);
> > > > > +         err = scmi_iio_set_odr_val(iio_dev, val, val2);
> > > > > +         mutex_unlock(&iio_dev->mlock);
> > > > > +         return err;
> > > > > + default:
> > > > > +         return -EINVAL;
> > > > > + }
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_read_avail(struct iio_dev *iio_dev,
> > > > > +                        struct iio_chan_spec const *chan,
> > > > > +                        const int **vals, int *type, int *length,
> > > > > +                        long mask)
> > > > > +{
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > +
> > > > > + switch (mask) {
> > > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > > +         *vals = sensor->freq_avail;
> > > > > +         *type = IIO_VAL_INT_PLUS_MICRO;
> > > > > +         *length = sensor->sensor_info->intervals.count * 2;
> > > > > +         if (sensor->sensor_info->intervals.segmented)
> > > > > +                 return IIO_AVAIL_RANGE;
> > > > > +         else
> > > > > +                 return IIO_AVAIL_LIST;
> > > > > + default:
> > > > > +         return -EINVAL;
> > > > > + }
> > > > > +}
> > > > > +
> > > > > +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz)
> > > > > +{
> > > > > + u64 rem, freq;
> > > > > +
> > > > > + freq = NSEC_PER_SEC;
> > > > > + rem = do_div(freq, interval_ns);
> > > > > + *hz = freq;
> > > > > + *uhz = rem * 1000000UL;
> > > > > + do_div(*uhz, interval_ns);
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2)
> > > > > +{
> > > > > + u64 sensor_update_interval, sensor_interval_mult, hz, uhz;
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > + u32 sensor_config;
> > > > > + int mult;
> > > > > +
> > > > > + int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > > > +                 sensor->sensor_info->id, &sensor_config);
> > > > > + if (err) {
> > > > > +         dev_err(&iio_dev->dev,
> > > > > +                 "Error in getting sensor config for sensor %s err %d",
> > > > > +                 sensor->sensor_info->name, err);
> > > > > +         return err;
> > > > > + }
> > > > > +
> > > > > + sensor_update_interval =
> > > > > +         SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC;
> > > > > +
> > > > > + mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config);
> > > > > + if (mult < 0) {
> > > > > +         sensor_interval_mult = int_pow(10, abs(mult));
> > > > > +         do_div(sensor_update_interval, sensor_interval_mult);
> > > > > + } else {
> > > > > +         sensor_interval_mult = int_pow(10, mult);
> > > > > +         sensor_update_interval =
> > > > > +                 sensor_update_interval * sensor_interval_mult;
> > > > > + }
> > > > > +
> > > > > + convert_ns_to_freq(sensor_update_interval, &hz, &uhz);
> > > > > + *val = hz;
> > > > > + *val2 = uhz;
> > > > > + return 0;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_read_raw(struct iio_dev *iio_dev,
> > > > > +                      struct iio_chan_spec const *ch, int *val,
> > > > > +                      int *val2, long mask)
> > > > > +{
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > + s8 scale;
> > > > > + int ret;
> > > > > +
> > > > > + switch (mask) {
> > > > > + case IIO_CHAN_INFO_SCALE:
> > > > > +         scale = sensor->sensor_info->axis[ch->scan_index].scale;
> > > > > +         if (scale < 0) {
> > > > > +                 *val = 1;
> > > > > +                 *val2 = int_pow(10, abs(scale));
> > > > > +                 return IIO_VAL_FRACTIONAL;
> > > > > +         }
> > > > > +         *val = int_pow(10, scale);
> > > > > +         return IIO_VAL_INT;
> > > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > > +         ret = scmi_iio_get_odr_val(iio_dev, val, val2);
> > > > > +         return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
> > > > > + default:
> > > > > +         return -EINVAL;
> > > > > + }
> > > > > +}
> > > > > +
> > > > > +static const struct iio_info scmi_iio_info = {
> > > > > + .read_raw = scmi_iio_read_raw,
> > > > > + .read_avail = scmi_iio_read_avail,
> > > > > + .write_raw = scmi_iio_write_raw,
> > > > > +};
> > > > > +
> > > > > +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
> > > > > +                                   uintptr_t private,
> > > > > +                                   const struct iio_chan_spec *chan,
> > > > > +                                   char *buf)
> > > > > +{
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > + u64 resolution, rem;
> > > > > + s64 min_range, max_range;
> > > > > + s8 exponent, scale;
> > > > > + int len = 0;
> > > > > +
> > > > > + /*
> > > > > +  * All the axes are supposed to have the same value for range and resolution.
> > > > > +  * We are just using the values from the Axis 0 here.
> > > > > +  */
> > > > > + if (sensor->sensor_info->axis[0].extended_attrs) {
> > > > > +         min_range = sensor->sensor_info->axis[0].attrs.min_range;
> > > > > +         max_range = sensor->sensor_info->axis[0].attrs.max_range;
> > > > > +         resolution = sensor->sensor_info->axis[0].resolution;
> > > > > +         exponent = sensor->sensor_info->axis[0].exponent;
> > > > > +         scale = sensor->sensor_info->axis[0].scale;
> > > > > +
> > > > > +         /*
> > > > > +          * To provide the raw value for the resolution to the userspace,
> > > > > +          * need to divide the resolution exponent by the sensor scale
> > > > > +          */
> > > > > +         exponent = exponent - scale;
> > > > > +         if (exponent < 0) {
> > > > > +                 rem = do_div(resolution,
> > > > > +                              int_pow(10, abs(exponent))
> > > > > +                              );
> > > > > +                 len = scnprintf(buf, PAGE_SIZE,
> > > > > +                                 "[%lld %llu.%llu %lld]\n", min_range,
> > > > > +                                 resolution, rem, max_range);
> > > > > +         } else {
> > > > > +                 resolution = resolution * int_pow(10, exponent);
> > > > > +                 len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
> > > > > +                                 min_range, resolution, max_range);
> > > > > +         }
> > > > > + }
> > > > > + return len;
> > > > > +}
> > > > > +
> > > > > +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = {
> > > > > + {
> > > > > +         .name = "raw_available",
> > > > > +         .read = scmi_iio_get_raw_available,
> > > > > +         .shared = IIO_SHARED_BY_TYPE,
> > > > > + },
> > > > > + {},
> > > > > +};
> > > > > +
> > > > > +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan,
> > > > > +                                    int scan_index)
> > > > > +{
> > > > > + iio_chan->type = IIO_TIMESTAMP;
> > > > > + iio_chan->channel = -1;
> > > > > + iio_chan->scan_index = scan_index;
> > > > > + iio_chan->scan_type.sign = 'u';
> > > > > + iio_chan->scan_type.realbits = 64;
> > > > > + iio_chan->scan_type.storagebits = 64;
> > > > > +}
> > > > > +
> > > > > +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan,
> > > > > +                               enum iio_chan_type type,
> > > > > +                               enum iio_modifier mod, int scan_index)
> > > > > +{
> > > > > + iio_chan->type = type;
> > > > > + iio_chan->modified = 1;
> > > > > + iio_chan->channel2 = mod;
> > > > > + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE);
> > > > > + iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > > > + iio_chan->info_mask_shared_by_type_available =
> > > > > +         BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > > > + iio_chan->scan_index = scan_index;
> > > > > + iio_chan->scan_type.sign = 's';
> > > > > + iio_chan->scan_type.realbits = 64;
> > > > > + iio_chan->scan_type.storagebits = 64;
> > > > > + iio_chan->scan_type.endianness = IIO_LE;
> > > > > + iio_chan->ext_info = scmi_iio_ext_info;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_get_chan_modifier(const char *name,
> > > > > +                               enum iio_modifier *modifier)
> > > > > +{
> > > > > + char *pch, mod;
> > > > > +
> > > > > + if (!name)
> > > > > +         return -EINVAL;
> > > > > +
> > > > > + pch = strrchr(name, '_');
> > > > > + if (!pch)
> > > > > +         return -EINVAL;
> > > > > +
> > > > > + mod = *(pch + 1);
> > > > > + switch (mod) {
> > > > > + case 'X':
> > > > > +         *modifier = IIO_MOD_X;
> > > > > +         return 0;
> > > > > + case 'Y':
> > > > > +         *modifier = IIO_MOD_Y;
> > > > > +         return 0;
> > > > > + case 'Z':
> > > > > +         *modifier = IIO_MOD_Z;
> > > > > +         return 0;
> > > > > + default:
> > > > > +         return -EINVAL;
> > > > > + }
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type)
> > > > > +{
> > > > > + switch (scmi_type) {
> > > > > + case METERS_SEC_SQUARED:
> > > > > +         *iio_type = IIO_ACCEL;
> > > > > +         return 0;
> > > > > + case RADIANS_SEC:
> > > > > +         *iio_type = IIO_ANGL_VEL;
> > > > > +         return 0;
> > > > > + default:
> > > > > +         return -EINVAL;
> > > > > + }
> > > > > +}
> > > > > +
> > > > > +static u64 scmi_iio_convert_interval_to_ns(u32 val)
> > > > > +{
> > > > > + u64 sensor_update_interval =
> > > > > +         SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC;
> > > > > + u64 sensor_interval_mult;
> > > > > + int mult;
> > > > > +
> > > > > + mult = SCMI_SENS_INTVL_GET_EXP(val);
> > > > > + if (mult < 0) {
> > > > > +         sensor_interval_mult = int_pow(10, abs(mult));
> > > > > +         do_div(sensor_update_interval, sensor_interval_mult);
> > > > > + } else {
> > > > > +         sensor_interval_mult = int_pow(10, mult);
> > > > > +         sensor_update_interval =
> > > > > +                 sensor_update_interval * sensor_interval_mult;
> > > > > + }
> > > > > + return sensor_update_interval;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev)
> > > > > +{
> > > > > + u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns,
> > > > > +         hz, uhz;
> > > > > + unsigned int cur_interval, low_interval, high_interval, step_size;
> > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > + int i;
> > > > > +
> > > > > + sensor->freq_avail =
> > > > > +         devm_kzalloc(&iio_dev->dev,
> > > > > +                      sizeof(*sensor->freq_avail) *
> > > > > +                              (sensor->sensor_info->intervals.count * 2),
> > > > > +                      GFP_KERNEL);
> > > > > + if (!sensor->freq_avail)
> > > > > +         return -ENOMEM;
> > > > > +
> > > > > + if (sensor->sensor_info->intervals.segmented) {
> > > > > +         low_interval = sensor->sensor_info->intervals
> > > > > +                                .desc[SCMI_SENS_INTVL_SEGMENT_LOW];
> > > > > +         low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval);
> > > > > +         convert_ns_to_freq(low_interval_ns, &hz, &uhz);
> > > > > +         sensor->freq_avail[0] = hz;
> > > > > +         sensor->freq_avail[1] = uhz;
> > > > > +
> > > > > +         step_size = sensor->sensor_info->intervals
> > > > > +                             .desc[SCMI_SENS_INTVL_SEGMENT_STEP];
> > > > > +         step_size_ns = scmi_iio_convert_interval_to_ns(step_size);
> > > > > +         convert_ns_to_freq(step_size_ns, &hz, &uhz);
> > > > > +         sensor->freq_avail[2] = hz;
> > > > > +         sensor->freq_avail[3] = uhz;
> > > > > +
> > > > > +         high_interval = sensor->sensor_info->intervals
> > > > > +                                 .desc[SCMI_SENS_INTVL_SEGMENT_HIGH];
> > > > > +         high_interval_ns =
> > > > > +                 scmi_iio_convert_interval_to_ns(high_interval);
> > > > > +         convert_ns_to_freq(high_interval_ns, &hz, &uhz);
> > > > > +         sensor->freq_avail[4] = hz;
> > > > > +         sensor->freq_avail[5] = uhz;
> > > > > + } else {
> > > > > +         for (i = 0; i < sensor->sensor_info->intervals.count; i++) {
> > > > > +                 cur_interval = sensor->sensor_info->intervals.desc[i];
> > > > > +                 cur_interval_ns =
> > > > > +                         scmi_iio_convert_interval_to_ns(cur_interval);
> > > > > +                 convert_ns_to_freq(cur_interval_ns, &hz, &uhz);
> > > > > +                 sensor->freq_avail[i * 2] = hz;
> > > > > +                 sensor->freq_avail[i * 2 + 1] = uhz;
> > > > > +         }
> > > > > + }
> > > > > + return 0;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev)
> > > > > +{
> > > > > + struct iio_buffer *buffer;
> > > > > +
> > > > > + buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev);
> > > > > + if (!buffer)
> > > > > +         return -ENOMEM;
> > > > > +
> > > > > + iio_device_attach_buffer(scmi_iiodev, buffer);
> > > > > + scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE;
> > > > > + scmi_iiodev->setup_ops = &scmi_iio_buffer_ops;
> > > > > + return 0;
> > > > > +}
> > > > > +
> > > > > +static struct iio_dev *scmi_alloc_iiodev(struct device *dev,
> > > > > +                                  struct scmi_handle *handle,
> > > > > +                                  const struct scmi_sensor_info *sensor_info)
> > > > > +{
> > > > > + struct iio_chan_spec *iio_channels;
> > > > > + struct scmi_iio_priv *sensor;
> > > > > + enum iio_modifier modifier;
> > > > > + enum iio_chan_type type;
> > > > > + struct iio_dev *iiodev;
> > > > > + int i, ret;
> > > > > +
> > > > > + iiodev = devm_iio_device_alloc(dev, sizeof(*sensor));
> > > > > + if (!iiodev)
> > > > > +         return ERR_PTR(-ENOMEM);
> > > > > +
> > > > > + iiodev->modes = INDIO_DIRECT_MODE;
> > > > > + iiodev->dev.parent = dev;
> > > > > + sensor = iio_priv(iiodev);
> > > > > + sensor->handle = handle;
> > > > > + sensor->sensor_info = sensor_info;
> > > > > + sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb;
> > > > > + sensor->indio_dev = iiodev;
> > > > > +
> > > > > + /* adding one additional channel for timestamp */
> > > > > + iiodev->num_channels = sensor_info->num_axis + 1;
> > > > > + iiodev->name = sensor_info->name;
> > > > > + iiodev->info = &scmi_iio_info;
> > > > > +
> > > > > + iio_channels =
> > > > > +         devm_kzalloc(dev,
> > > > > +                      sizeof(*iio_channels) * (iiodev->num_channels),
> > > > > +                      GFP_KERNEL);
> > > > > + if (!iio_channels)
> > > > > +         return ERR_PTR(-ENOMEM);
> > > > > +
> > > > > + ret = scmi_iio_set_sampling_freq_avail(iiodev);
> > > > > + if (ret < 0)
> > > > > +         return ERR_PTR(ret);
> > > > > +
> > > > > + for (i = 0; i < sensor_info->num_axis; i++) {
> > > > > +         ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type);
> > > > > +         if (ret < 0)
> > > > > +                 return ERR_PTR(ret);
> > > > > +
> > > > > +         ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name,
> > > > > +                                          &modifier);
> > > > > +         if (ret < 0)
> > > > > +                 return ERR_PTR(ret);
> > > > > +
> > > > > +         scmi_iio_set_data_channel(&iio_channels[i], type, modifier,
> > > > > +                                   sensor_info->axis[i].id);
> > > > > + }
> > > > > +
> > > > > + scmi_iio_set_timestamp_channel(&iio_channels[i], i);
> > > > > + iiodev->channels = iio_channels;
> > > > > + return iiodev;
> > > > > +}
> > > > > +
> > > > > +static int scmi_iio_dev_probe(struct scmi_device *sdev)
> > > > > +{
> > > > > + const struct scmi_sensor_info *sensor_info;
> > > > > + struct scmi_handle *handle = sdev->handle;
> > > > > + struct device *dev = &sdev->dev;
> > > > > + struct iio_dev *scmi_iio_dev;
> > > > > + u16 nr_sensors;
> > > > > + int err = -ENODEV, i;
> > > > > +
> > > > > + if (!handle || !handle->sensor_ops) {
> > > > > +         dev_err(dev, "SCMI device has no sensor interface\n");
> > > > > +         return -EINVAL;
> > > > > + }
> > > > > +
> > > > > + nr_sensors = handle->sensor_ops->count_get(handle);
> > > > > + if (!nr_sensors) {
> > > > > +         dev_dbg(dev, "0 sensors found via SCMI bus\n");
> > > > > +         return -ENODEV;
> > > > > + }
> > > > > +
> > > > > + for (i = 0; i < nr_sensors; i++) {
> > > > > +         sensor_info = handle->sensor_ops->info_get(handle, i);
> > > > > +         if (!sensor_info) {
> > > > > +                 dev_err(dev, "SCMI sensor %d has missing info\n", i);
> > > > > +                 return -EINVAL;
> > > > > +         }
> > > > > +
> > > > > +         /* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > > > +         if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS)
> > > > > +                 continue;
> > > > > +
> > > > > +         /* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > > > +         if (sensor_info->axis[0].type != METERS_SEC_SQUARED &&
> > > > > +             sensor_info->axis[0].type != RADIANS_SEC)
> > > > > +                 continue;
> > > > > +
> > > > > +         scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info);
> > > > > +         if (IS_ERR(scmi_iio_dev)) {
> > > > > +                 dev_err(dev,
> > > > > +                         "failed to allocate IIO device for sensor %s: %ld\n",
> > > > > +                         sensor_info->name, PTR_ERR(scmi_iio_dev));
> > > > > +                 return PTR_ERR(scmi_iio_dev);
> > > > > +         }
> > > > > +
> > > > > +         err = scmi_iio_buffers_setup(scmi_iio_dev);
> > > > > +         if (err < 0) {
> > > > > +                 dev_err(dev,
> > > > > +                         "IIO buffer setup error at sensor %s: %d\n",
> > > > > +                         sensor_info->name, err);
> > > > > +                 return err;
> > > > > +         }
> > > > > +
> > > > > +         err = devm_iio_device_register(dev, scmi_iio_dev);
> > > > > +         if (err) {
> > > > > +                 dev_err(dev,
> > > > > +                         "IIO device registration failed at sensor %s: %d\n",
> > > > > +                         sensor_info->name, err);
> > > > > +                 return err;
> > > > > +         }
> > > > > + }
> > > > > + return err;
> > > > > +}
> > > > > +
> > > > > +static const struct scmi_device_id scmi_id_table[] = {
> > > > > + { SCMI_PROTOCOL_SENSOR, "iiodev" },
> > > > > + {},
> > > > > +};
> > > > > +
> > > > > +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > > > > +
> > > > > +static struct scmi_driver scmi_iiodev_driver = {
> > > > > + .name = "scmi-sensor-iiodev",
> > > > > + .probe = scmi_iio_dev_probe,
> > > > > + .id_table = scmi_id_table,
> > > > > +};
> > > > > +
> > > > > +module_scmi_driver(scmi_iiodev_driver);
> > > > > +
> > > > > +MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>");
> > > > > +MODULE_DESCRIPTION("SCMI IIO Driver");
> > > > > +MODULE_LICENSE("GPL v2");  
> > > >  
> > >  
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-13 17:11           ` Jonathan Cameron
@ 2021-03-13 19:55             ` Jyoti Bhayana
  2021-03-14 15:40               ` Jonathan Cameron
  0 siblings, 1 reply; 16+ messages in thread
From: Jyoti Bhayana @ 2021-03-13 19:55 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Sudeep Holla,
	Enrico Granata, Mikhail Golubev, Igor Skalkin, Peter Hilber,
	Ankit Arora, Guru Nagarajan, kernel test robot

Hi Jonathan,

I still see the old version 6 in ib-iio-scmi-5.12-rc2-take2 as well.

Thanks,
Jyoti

On Sat, Mar 13, 2021 at 9:11 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Fri, 12 Mar 2021 09:54:12 -0800
> Jyoti Bhayana <jbhayana@google.com> wrote:
>
> > Hi Jonathan,
> >
> > I also see what Cristian has observed that rc2 is still pointing to v6
> > of the SCMI IIO driver patch and it also doesn't have the changes
> > which you did in rc1 for changing long long to s64.
> >
> > Thanks,
> > Jyoti
>
> Gah!  I must have grabbed wrong one somehow.
>
> Anyhow, there is now a
> https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2-take2
>
> version which should looks to have the right dates an all.  Merge into
> my togreg still the same as it was previously.
>
> Thanks,
>
> Jonathan
>
> >
> >
> > On Fri, Mar 12, 2021 at 5:31 AM Cristian Marussi
> > <cristian.marussi@arm.com> wrote:
> > >
> > > Hi Jonathan,
> > >
> > > thanks for this, I was starting working on top of this new immutable
> > > branch BUT I spotted a thing that I wanted to check.
> > >
> > > You latest immutable:
> > >  https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
> > >
> > > looking at the code in the SCMI IIO Jyoti driver
> > >
> > > https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/commit/?h=ib-iio-scmi-5.12-rc2&id=f937d8c1ef246d99d2174ed88c629f6e24823918
> > >
> > > this seems to me that still includes v6 of IIO Jyoti patch:
> > >
> > > https://lore.kernel.org/linux-iio/20210212172235.507028-2-jbhayana@google.com/
> > >
> > > (the offending one on the bot report related to div symbols on MIPS if I got it right),
> > > in fact it has 710 insertions and still uses div64_u64 (and the Link tag leads to the v6)
> > >
> > > while the lastest v7
> > >
> > > https://lore.kernel.org/lkml/20210309231259.78050-2-jbhayana@google.com/
> > >
> > > has 715 insetions and uses do_div instead, but in fact this v7 has a Link:
> > > tag still pointing to v6 (maybe this is the issue...?)
> > >
> > > Sorry for the noise if I missed something and everything is fine please
> > > ignore me and I'll go ahead with this branch if you say so.
> > >
> > > Thanks
> > >
> > > Cristian
> > >
> > >
> > >
> > > On Fri, Mar 12, 2021 at 12:16:39PM +0000, Jonathan Cameron wrote:
> > > > On Thu, 11 Mar 2021 21:08:44 +0000
> > > > Jonathan Cameron <jic23@kernel.org> wrote:
> > > >
> > > > > On Tue,  9 Mar 2021 23:12:59 +0000
> > > > > Jyoti Bhayana <jbhayana@google.com> wrote:
> > > > >
> > > > > > This change provides ARM SCMI Protocol based IIO device.
> > > > > > This driver provides support for Accelerometer and Gyroscope using
> > > > > > SCMI Sensor Protocol extensions added in the SCMIv3.0 ARM specification
> > > > > >
> > > > > > Reported-by: kernel test robot <lkp@intel.com>
> > > > > > Signed-off-by: Jyoti Bhayana <jbhayana@google.com>
> > > > > > Link: https://lore.kernel.org/r/20210212172235.507028-2-jbhayana@google.com
> > > > > > Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> > > > >
> > > > > https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/log/?h=ib-iio-scmi-5.12-rc2
> > > > >
> > > > > New immutable branch.  I move to rc2 as the base as the rc1 tag
> > > > > has been removed for well publicized reasons.
> > > > >
> > > > > Should have the 0-day results in tomorrow morning. I'll shout
> > > > > if anything else shows up.
> > > > 0-day is happy, so this is now immutable and suitable for pulling in through
> > > > whatever trees need it.
> > > >
> > > > Thanks,
> > > >
> > > > Jonathan
> > > >
> > > > >
> > > > > Also rebased the togreg branch for the same reason (hadn't pushed it
> > > > > out as anything other than testing, so hopefully no one was basing
> > > > > anything significant on top)
> > > > >
> > > > > Thanks,
> > > > >
> > > > > Jonathan
> > > > >
> > > > > > ---
> > > > > >  MAINTAINERS                                |   6 +
> > > > > >  drivers/firmware/arm_scmi/driver.c         |   2 +-
> > > > > >  drivers/iio/common/Kconfig                 |   1 +
> > > > > >  drivers/iio/common/Makefile                |   1 +
> > > > > >  drivers/iio/common/scmi_sensors/Kconfig    |  18 +
> > > > > >  drivers/iio/common/scmi_sensors/Makefile   |   5 +
> > > > > >  drivers/iio/common/scmi_sensors/scmi_iio.c | 683 +++++++++++++++++++++
> > > > > >  7 files changed, 715 insertions(+), 1 deletion(-)
> > > > > >  create mode 100644 drivers/iio/common/scmi_sensors/Kconfig
> > > > > >  create mode 100644 drivers/iio/common/scmi_sensors/Makefile
> > > > > >  create mode 100644 drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > > >
> > > > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > > > index d92f85ca831d..14227980f3d2 100644
> > > > > > --- a/MAINTAINERS
> > > > > > +++ b/MAINTAINERS
> > > > > > @@ -8692,6 +8692,12 @@ S: Maintained
> > > > > >  F:       Documentation/devicetree/bindings/iio/multiplexer/io-channel-mux.txt
> > > > > >  F:       drivers/iio/multiplexer/iio-mux.c
> > > > > >
> > > > > > +IIO SCMI BASED DRIVER
> > > > > > +M:       Jyoti Bhayana <jbhayana@google.com>
> > > > > > +L:       linux-iio@vger.kernel.org
> > > > > > +S:       Maintained
> > > > > > +F:       drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > > > +
> > > > > >  IIO SUBSYSTEM AND DRIVERS
> > > > > >  M:       Jonathan Cameron <jic23@kernel.org>
> > > > > >  R:       Lars-Peter Clausen <lars@metafoo.de>
> > > > > > diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> > > > > > index cacdf1589b10..3e748e57deab 100644
> > > > > > --- a/drivers/firmware/arm_scmi/driver.c
> > > > > > +++ b/drivers/firmware/arm_scmi/driver.c
> > > > > > @@ -741,7 +741,7 @@ static struct scmi_prot_devnames devnames[] = {
> > > > > >   { SCMI_PROTOCOL_SYSTEM, { "syspower" },},
> > > > > >   { SCMI_PROTOCOL_PERF,   { "cpufreq" },},
> > > > > >   { SCMI_PROTOCOL_CLOCK,  { "clocks" },},
> > > > > > - { SCMI_PROTOCOL_SENSOR, { "hwmon" },},
> > > > > > + { SCMI_PROTOCOL_SENSOR, { "hwmon", "iiodev" },},
> > > > > >   { SCMI_PROTOCOL_RESET,  { "reset" },},
> > > > > >   { SCMI_PROTOCOL_VOLTAGE,  { "regulator" },},
> > > > > >  };
> > > > > > diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
> > > > > > index 2b9ee9161abd..0334b4954773 100644
> > > > > > --- a/drivers/iio/common/Kconfig
> > > > > > +++ b/drivers/iio/common/Kconfig
> > > > > > @@ -6,5 +6,6 @@
> > > > > >  source "drivers/iio/common/cros_ec_sensors/Kconfig"
> > > > > >  source "drivers/iio/common/hid-sensors/Kconfig"
> > > > > >  source "drivers/iio/common/ms_sensors/Kconfig"
> > > > > > +source "drivers/iio/common/scmi_sensors/Kconfig"
> > > > > >  source "drivers/iio/common/ssp_sensors/Kconfig"
> > > > > >  source "drivers/iio/common/st_sensors/Kconfig"
> > > > > > diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
> > > > > > index 4bc30bb548e2..fad40e1e1718 100644
> > > > > > --- a/drivers/iio/common/Makefile
> > > > > > +++ b/drivers/iio/common/Makefile
> > > > > > @@ -11,5 +11,6 @@
> > > > > >  obj-y += cros_ec_sensors/
> > > > > >  obj-y += hid-sensors/
> > > > > >  obj-y += ms_sensors/
> > > > > > +obj-y += scmi_sensors/
> > > > > >  obj-y += ssp_sensors/
> > > > > >  obj-y += st_sensors/
> > > > > > diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig
> > > > > > new file mode 100644
> > > > > > index 000000000000..67e084cbb1ab
> > > > > > --- /dev/null
> > > > > > +++ b/drivers/iio/common/scmi_sensors/Kconfig
> > > > > > @@ -0,0 +1,18 @@
> > > > > > +#
> > > > > > +# IIO over SCMI
> > > > > > +#
> > > > > > +# When adding new entries keep the list in alphabetical order
> > > > > > +
> > > > > > +menu "IIO SCMI Sensors"
> > > > > > +
> > > > > > +config IIO_SCMI
> > > > > > + tristate "IIO SCMI"
> > > > > > +        depends on ARM_SCMI_PROTOCOL
> > > > > > +        select IIO_BUFFER
> > > > > > +        select IIO_KFIFO_BUF
> > > > > > + help
> > > > > > +          Say yes here to build support for IIO SCMI Driver.
> > > > > > +          This provides ARM SCMI Protocol based IIO device.
> > > > > > +          This driver provides support for accelerometer and gyroscope
> > > > > > +          sensors available on SCMI based platforms.
> > > > > > +endmenu
> > > > > > diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile
> > > > > > new file mode 100644
> > > > > > index 000000000000..f13140a2575a
> > > > > > --- /dev/null
> > > > > > +++ b/drivers/iio/common/scmi_sensors/Makefile
> > > > > > @@ -0,0 +1,5 @@
> > > > > > +# SPDX - License - Identifier : GPL - 2.0 - only
> > > > > > +#
> > > > > > +# Makefile for the IIO over SCMI
> > > > > > +#
> > > > > > +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o
> > > > > > diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > > > new file mode 100644
> > > > > > index 000000000000..872d87ca6256
> > > > > > --- /dev/null
> > > > > > +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
> > > > > > @@ -0,0 +1,683 @@
> > > > > > +// SPDX-License-Identifier: GPL-2.0
> > > > > > +
> > > > > > +/*
> > > > > > + * System Control and Management Interface(SCMI) based IIO sensor driver
> > > > > > + *
> > > > > > + * Copyright (C) 2021 Google LLC
> > > > > > + */
> > > > > > +
> > > > > > +#include <linux/delay.h>
> > > > > > +#include <linux/err.h>
> > > > > > +#include <linux/iio/buffer.h>
> > > > > > +#include <linux/iio/iio.h>
> > > > > > +#include <linux/iio/kfifo_buf.h>
> > > > > > +#include <linux/iio/sysfs.h>
> > > > > > +#include <linux/kernel.h>
> > > > > > +#include <linux/kthread.h>
> > > > > > +#include <linux/module.h>
> > > > > > +#include <linux/scmi_protocol.h>
> > > > > > +#include <linux/time.h>
> > > > > > +#include <linux/types.h>
> > > > > > +
> > > > > > +#define SCMI_IIO_NUM_OF_AXIS 3
> > > > > > +
> > > > > > +struct scmi_iio_priv {
> > > > > > + struct scmi_handle *handle;
> > > > > > + const struct scmi_sensor_info *sensor_info;
> > > > > > + struct iio_dev *indio_dev;
> > > > > > + /* adding one additional channel for timestamp */
> > > > > > + s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1];
> > > > > > + struct notifier_block sensor_update_nb;
> > > > > > + u32 *freq_avail;
> > > > > > +};
> > > > > > +
> > > > > > +static int scmi_iio_sensor_update_cb(struct notifier_block *nb,
> > > > > > +                              unsigned long event, void *data)
> > > > > > +{
> > > > > > + struct scmi_sensor_update_report *sensor_update = data;
> > > > > > + struct iio_dev *scmi_iio_dev;
> > > > > > + struct scmi_iio_priv *sensor;
> > > > > > + s8 tstamp_scale;
> > > > > > + u64 time, time_ns;
> > > > > > + int i;
> > > > > > +
> > > > > > + if (sensor_update->readings_count == 0)
> > > > > > +         return NOTIFY_DONE;
> > > > > > +
> > > > > > + sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb);
> > > > > > +
> > > > > > + for (i = 0; i < sensor_update->readings_count; i++)
> > > > > > +         sensor->iio_buf[i] = sensor_update->readings[i].value;
> > > > > > +
> > > > > > + if (!sensor->sensor_info->timestamped) {
> > > > > > +         time_ns = ktime_to_ns(sensor_update->timestamp);
> > > > > > + } else {
> > > > > > +         /*
> > > > > > +          *  All the axes are supposed to have the same value for timestamp.
> > > > > > +          *  We are just using the values from the Axis 0 here.
> > > > > > +          */
> > > > > > +         time = sensor_update->readings[0].timestamp;
> > > > > > +
> > > > > > +         /*
> > > > > > +          *  Timestamp returned by SCMI is in seconds and is equal to
> > > > > > +          *  time * power-of-10 multiplier(tstamp_scale) seconds.
> > > > > > +          *  Converting the timestamp to nanoseconds below.
> > > > > > +          */
> > > > > > +         tstamp_scale = sensor->sensor_info->tstamp_scale +
> > > > > > +                        const_ilog2(NSEC_PER_SEC) / const_ilog2(10);
> > > > > > +         if (tstamp_scale < 0) {
> > > > > > +                 do_div(time, int_pow(10, abs(tstamp_scale)));
> > > > > > +                 time_ns = time;
> > > > > > +         } else {
> > > > > > +                 time_ns = time * int_pow(10, tstamp_scale);
> > > > > > +         }
> > > > > > + }
> > > > > > +
> > > > > > + scmi_iio_dev = sensor->indio_dev;
> > > > > > + iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf,
> > > > > > +                                    time_ns);
> > > > > > + return NOTIFY_OK;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev)
> > > > > > +{
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > + u32 sensor_id = sensor->sensor_info->id;
> > > > > > + u32 sensor_config = 0;
> > > > > > + int err;
> > > > > > +
> > > > > > + if (sensor->sensor_info->timestamped)
> > > > > > +         sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > > > > +                                     SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > > > > +
> > > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > > > > +                             SCMI_SENS_CFG_SENSOR_ENABLE);
> > > > > > +
> > > > > > + err = sensor->handle->notify_ops->register_event_notifier(sensor->handle,
> > > > > > +                 SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > > > > +                 &sensor_id, &sensor->sensor_update_nb);
> > > > > > + if (err) {
> > > > > > +         dev_err(&iio_dev->dev,
> > > > > > +                 "Error in registering sensor update notifier for sensor %s err %d",
> > > > > > +                 sensor->sensor_info->name, err);
> > > > > > +         return err;
> > > > > > + }
> > > > > > +
> > > > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > > > > +                 sensor->sensor_info->id, sensor_config);
> > > > > > + if (err) {
> > > > > > +         sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > > > > +                         SCMI_PROTOCOL_SENSOR,
> > > > > > +                         SCMI_EVENT_SENSOR_UPDATE, &sensor_id,
> > > > > > +                         &sensor->sensor_update_nb);
> > > > > > +         dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d",
> > > > > > +                 sensor->sensor_info->name, err);
> > > > > > + }
> > > > > > +
> > > > > > + return err;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev)
> > > > > > +{
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > + u32 sensor_id = sensor->sensor_info->id;
> > > > > > + u32 sensor_config = 0;
> > > > > > + int err;
> > > > > > +
> > > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK,
> > > > > > +                             SCMI_SENS_CFG_SENSOR_DISABLE);
> > > > > > +
> > > > > > + err = sensor->handle->notify_ops->unregister_event_notifier(sensor->handle,
> > > > > > +                 SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE,
> > > > > > +                 &sensor_id, &sensor->sensor_update_nb);
> > > > > > + if (err) {
> > > > > > +         dev_err(&iio_dev->dev,
> > > > > > +                 "Error in unregistering sensor update notifier for sensor %s err %d",
> > > > > > +                 sensor->sensor_info->name, err);
> > > > > > +         return err;
> > > > > > + }
> > > > > > +
> > > > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle, sensor_id,
> > > > > > +                                              sensor_config);
> > > > > > + if (err) {
> > > > > > +         dev_err(&iio_dev->dev,
> > > > > > +                 "Error in disabling sensor %s with err %d",
> > > > > > +                 sensor->sensor_info->name, err);
> > > > > > + }
> > > > > > +
> > > > > > + return err;
> > > > > > +}
> > > > > > +
> > > > > > +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
> > > > > > + .preenable = scmi_iio_buffer_preenable,
> > > > > > + .postdisable = scmi_iio_buffer_postdisable,
> > > > > > +};
> > > > > > +
> > > > > > +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
> > > > > > +{
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > + const unsigned long UHZ_PER_HZ = 1000000UL;
> > > > > > + u64 sec, mult, uHz, sf;
> > > > > > + u32 sensor_config;
> > > > > > + char buf[32];
> > > > > > +
> > > > > > + int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > > > > +                 sensor->sensor_info->id, &sensor_config);
> > > > > > + if (err) {
> > > > > > +         dev_err(&iio_dev->dev,
> > > > > > +                 "Error in getting sensor config for sensor %s err %d",
> > > > > > +                 sensor->sensor_info->name, err);
> > > > > > +         return err;
> > > > > > + }
> > > > > > +
> > > > > > + uHz = val * UHZ_PER_HZ + val2;
> > > > > > +
> > > > > > + /*
> > > > > > +  * The seconds field in the sensor interval in SCMI is 16 bits long
> > > > > > +  * Therefore seconds  = 1/Hz <= 0xFFFF. As floating point calculations are
> > > > > > +  * discouraged in the kernel driver code, to calculate the scale factor (sf)
> > > > > > +  * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000
> > > > > > +  * To calculate the multiplier,we convert the sf into char string  and
> > > > > > +  * count the number of characters
> > > > > > +  */
> > > > > > + sf = (u64)uHz * 0xFFFF;
> > > > > > + do_div(sf,  UHZ_PER_HZ);
> > > > > > + mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
> > > > > > +
> > > > > > + sec = int_pow(10, mult) * UHZ_PER_HZ;
> > > > > > + do_div(sec, uHz);
> > > > > > + if (sec == 0) {
> > > > > > +         dev_err(&iio_dev->dev,
> > > > > > +                 "Trying to set invalid sensor update value for sensor %s",
> > > > > > +                 sensor->sensor_info->name);
> > > > > > +         return -EINVAL;
> > > > > > + }
> > > > > > +
> > > > > > + sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK;
> > > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec);
> > > > > > + sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK;
> > > > > > + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult);
> > > > > > +
> > > > > > + if (sensor->sensor_info->timestamped) {
> > > > > > +         sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK;
> > > > > > +         sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK,
> > > > > > +                                     SCMI_SENS_CFG_TSTAMP_ENABLE);
> > > > > > + }
> > > > > > +
> > > > > > + sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK;
> > > > > > + sensor_config |=
> > > > > > +         FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO);
> > > > > > +
> > > > > > + err = sensor->handle->sensor_ops->config_set(sensor->handle,
> > > > > > +                 sensor->sensor_info->id, sensor_config);
> > > > > > + if (err)
> > > > > > +         dev_err(&iio_dev->dev,
> > > > > > +                 "Error in setting sensor update interval for sensor %s value %u err %d",
> > > > > > +                 sensor->sensor_info->name, sensor_config, err);
> > > > > > +
> > > > > > + return err;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_write_raw(struct iio_dev *iio_dev,
> > > > > > +                       struct iio_chan_spec const *chan, int val,
> > > > > > +                       int val2, long mask)
> > > > > > +{
> > > > > > + int err;
> > > > > > +
> > > > > > + switch (mask) {
> > > > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > > > +         mutex_lock(&iio_dev->mlock);
> > > > > > +         err = scmi_iio_set_odr_val(iio_dev, val, val2);
> > > > > > +         mutex_unlock(&iio_dev->mlock);
> > > > > > +         return err;
> > > > > > + default:
> > > > > > +         return -EINVAL;
> > > > > > + }
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_read_avail(struct iio_dev *iio_dev,
> > > > > > +                        struct iio_chan_spec const *chan,
> > > > > > +                        const int **vals, int *type, int *length,
> > > > > > +                        long mask)
> > > > > > +{
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > +
> > > > > > + switch (mask) {
> > > > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > > > +         *vals = sensor->freq_avail;
> > > > > > +         *type = IIO_VAL_INT_PLUS_MICRO;
> > > > > > +         *length = sensor->sensor_info->intervals.count * 2;
> > > > > > +         if (sensor->sensor_info->intervals.segmented)
> > > > > > +                 return IIO_AVAIL_RANGE;
> > > > > > +         else
> > > > > > +                 return IIO_AVAIL_LIST;
> > > > > > + default:
> > > > > > +         return -EINVAL;
> > > > > > + }
> > > > > > +}
> > > > > > +
> > > > > > +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz)
> > > > > > +{
> > > > > > + u64 rem, freq;
> > > > > > +
> > > > > > + freq = NSEC_PER_SEC;
> > > > > > + rem = do_div(freq, interval_ns);
> > > > > > + *hz = freq;
> > > > > > + *uhz = rem * 1000000UL;
> > > > > > + do_div(*uhz, interval_ns);
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2)
> > > > > > +{
> > > > > > + u64 sensor_update_interval, sensor_interval_mult, hz, uhz;
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > + u32 sensor_config;
> > > > > > + int mult;
> > > > > > +
> > > > > > + int err = sensor->handle->sensor_ops->config_get(sensor->handle,
> > > > > > +                 sensor->sensor_info->id, &sensor_config);
> > > > > > + if (err) {
> > > > > > +         dev_err(&iio_dev->dev,
> > > > > > +                 "Error in getting sensor config for sensor %s err %d",
> > > > > > +                 sensor->sensor_info->name, err);
> > > > > > +         return err;
> > > > > > + }
> > > > > > +
> > > > > > + sensor_update_interval =
> > > > > > +         SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC;
> > > > > > +
> > > > > > + mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config);
> > > > > > + if (mult < 0) {
> > > > > > +         sensor_interval_mult = int_pow(10, abs(mult));
> > > > > > +         do_div(sensor_update_interval, sensor_interval_mult);
> > > > > > + } else {
> > > > > > +         sensor_interval_mult = int_pow(10, mult);
> > > > > > +         sensor_update_interval =
> > > > > > +                 sensor_update_interval * sensor_interval_mult;
> > > > > > + }
> > > > > > +
> > > > > > + convert_ns_to_freq(sensor_update_interval, &hz, &uhz);
> > > > > > + *val = hz;
> > > > > > + *val2 = uhz;
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_read_raw(struct iio_dev *iio_dev,
> > > > > > +                      struct iio_chan_spec const *ch, int *val,
> > > > > > +                      int *val2, long mask)
> > > > > > +{
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > + s8 scale;
> > > > > > + int ret;
> > > > > > +
> > > > > > + switch (mask) {
> > > > > > + case IIO_CHAN_INFO_SCALE:
> > > > > > +         scale = sensor->sensor_info->axis[ch->scan_index].scale;
> > > > > > +         if (scale < 0) {
> > > > > > +                 *val = 1;
> > > > > > +                 *val2 = int_pow(10, abs(scale));
> > > > > > +                 return IIO_VAL_FRACTIONAL;
> > > > > > +         }
> > > > > > +         *val = int_pow(10, scale);
> > > > > > +         return IIO_VAL_INT;
> > > > > > + case IIO_CHAN_INFO_SAMP_FREQ:
> > > > > > +         ret = scmi_iio_get_odr_val(iio_dev, val, val2);
> > > > > > +         return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
> > > > > > + default:
> > > > > > +         return -EINVAL;
> > > > > > + }
> > > > > > +}
> > > > > > +
> > > > > > +static const struct iio_info scmi_iio_info = {
> > > > > > + .read_raw = scmi_iio_read_raw,
> > > > > > + .read_avail = scmi_iio_read_avail,
> > > > > > + .write_raw = scmi_iio_write_raw,
> > > > > > +};
> > > > > > +
> > > > > > +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev,
> > > > > > +                                   uintptr_t private,
> > > > > > +                                   const struct iio_chan_spec *chan,
> > > > > > +                                   char *buf)
> > > > > > +{
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > + u64 resolution, rem;
> > > > > > + s64 min_range, max_range;
> > > > > > + s8 exponent, scale;
> > > > > > + int len = 0;
> > > > > > +
> > > > > > + /*
> > > > > > +  * All the axes are supposed to have the same value for range and resolution.
> > > > > > +  * We are just using the values from the Axis 0 here.
> > > > > > +  */
> > > > > > + if (sensor->sensor_info->axis[0].extended_attrs) {
> > > > > > +         min_range = sensor->sensor_info->axis[0].attrs.min_range;
> > > > > > +         max_range = sensor->sensor_info->axis[0].attrs.max_range;
> > > > > > +         resolution = sensor->sensor_info->axis[0].resolution;
> > > > > > +         exponent = sensor->sensor_info->axis[0].exponent;
> > > > > > +         scale = sensor->sensor_info->axis[0].scale;
> > > > > > +
> > > > > > +         /*
> > > > > > +          * To provide the raw value for the resolution to the userspace,
> > > > > > +          * need to divide the resolution exponent by the sensor scale
> > > > > > +          */
> > > > > > +         exponent = exponent - scale;
> > > > > > +         if (exponent < 0) {
> > > > > > +                 rem = do_div(resolution,
> > > > > > +                              int_pow(10, abs(exponent))
> > > > > > +                              );
> > > > > > +                 len = scnprintf(buf, PAGE_SIZE,
> > > > > > +                                 "[%lld %llu.%llu %lld]\n", min_range,
> > > > > > +                                 resolution, rem, max_range);
> > > > > > +         } else {
> > > > > > +                 resolution = resolution * int_pow(10, exponent);
> > > > > > +                 len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n",
> > > > > > +                                 min_range, resolution, max_range);
> > > > > > +         }
> > > > > > + }
> > > > > > + return len;
> > > > > > +}
> > > > > > +
> > > > > > +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = {
> > > > > > + {
> > > > > > +         .name = "raw_available",
> > > > > > +         .read = scmi_iio_get_raw_available,
> > > > > > +         .shared = IIO_SHARED_BY_TYPE,
> > > > > > + },
> > > > > > + {},
> > > > > > +};
> > > > > > +
> > > > > > +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan,
> > > > > > +                                    int scan_index)
> > > > > > +{
> > > > > > + iio_chan->type = IIO_TIMESTAMP;
> > > > > > + iio_chan->channel = -1;
> > > > > > + iio_chan->scan_index = scan_index;
> > > > > > + iio_chan->scan_type.sign = 'u';
> > > > > > + iio_chan->scan_type.realbits = 64;
> > > > > > + iio_chan->scan_type.storagebits = 64;
> > > > > > +}
> > > > > > +
> > > > > > +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan,
> > > > > > +                               enum iio_chan_type type,
> > > > > > +                               enum iio_modifier mod, int scan_index)
> > > > > > +{
> > > > > > + iio_chan->type = type;
> > > > > > + iio_chan->modified = 1;
> > > > > > + iio_chan->channel2 = mod;
> > > > > > + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE);
> > > > > > + iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > > > > + iio_chan->info_mask_shared_by_type_available =
> > > > > > +         BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > > > > + iio_chan->scan_index = scan_index;
> > > > > > + iio_chan->scan_type.sign = 's';
> > > > > > + iio_chan->scan_type.realbits = 64;
> > > > > > + iio_chan->scan_type.storagebits = 64;
> > > > > > + iio_chan->scan_type.endianness = IIO_LE;
> > > > > > + iio_chan->ext_info = scmi_iio_ext_info;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_get_chan_modifier(const char *name,
> > > > > > +                               enum iio_modifier *modifier)
> > > > > > +{
> > > > > > + char *pch, mod;
> > > > > > +
> > > > > > + if (!name)
> > > > > > +         return -EINVAL;
> > > > > > +
> > > > > > + pch = strrchr(name, '_');
> > > > > > + if (!pch)
> > > > > > +         return -EINVAL;
> > > > > > +
> > > > > > + mod = *(pch + 1);
> > > > > > + switch (mod) {
> > > > > > + case 'X':
> > > > > > +         *modifier = IIO_MOD_X;
> > > > > > +         return 0;
> > > > > > + case 'Y':
> > > > > > +         *modifier = IIO_MOD_Y;
> > > > > > +         return 0;
> > > > > > + case 'Z':
> > > > > > +         *modifier = IIO_MOD_Z;
> > > > > > +         return 0;
> > > > > > + default:
> > > > > > +         return -EINVAL;
> > > > > > + }
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type)
> > > > > > +{
> > > > > > + switch (scmi_type) {
> > > > > > + case METERS_SEC_SQUARED:
> > > > > > +         *iio_type = IIO_ACCEL;
> > > > > > +         return 0;
> > > > > > + case RADIANS_SEC:
> > > > > > +         *iio_type = IIO_ANGL_VEL;
> > > > > > +         return 0;
> > > > > > + default:
> > > > > > +         return -EINVAL;
> > > > > > + }
> > > > > > +}
> > > > > > +
> > > > > > +static u64 scmi_iio_convert_interval_to_ns(u32 val)
> > > > > > +{
> > > > > > + u64 sensor_update_interval =
> > > > > > +         SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC;
> > > > > > + u64 sensor_interval_mult;
> > > > > > + int mult;
> > > > > > +
> > > > > > + mult = SCMI_SENS_INTVL_GET_EXP(val);
> > > > > > + if (mult < 0) {
> > > > > > +         sensor_interval_mult = int_pow(10, abs(mult));
> > > > > > +         do_div(sensor_update_interval, sensor_interval_mult);
> > > > > > + } else {
> > > > > > +         sensor_interval_mult = int_pow(10, mult);
> > > > > > +         sensor_update_interval =
> > > > > > +                 sensor_update_interval * sensor_interval_mult;
> > > > > > + }
> > > > > > + return sensor_update_interval;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev)
> > > > > > +{
> > > > > > + u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns,
> > > > > > +         hz, uhz;
> > > > > > + unsigned int cur_interval, low_interval, high_interval, step_size;
> > > > > > + struct scmi_iio_priv *sensor = iio_priv(iio_dev);
> > > > > > + int i;
> > > > > > +
> > > > > > + sensor->freq_avail =
> > > > > > +         devm_kzalloc(&iio_dev->dev,
> > > > > > +                      sizeof(*sensor->freq_avail) *
> > > > > > +                              (sensor->sensor_info->intervals.count * 2),
> > > > > > +                      GFP_KERNEL);
> > > > > > + if (!sensor->freq_avail)
> > > > > > +         return -ENOMEM;
> > > > > > +
> > > > > > + if (sensor->sensor_info->intervals.segmented) {
> > > > > > +         low_interval = sensor->sensor_info->intervals
> > > > > > +                                .desc[SCMI_SENS_INTVL_SEGMENT_LOW];
> > > > > > +         low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval);
> > > > > > +         convert_ns_to_freq(low_interval_ns, &hz, &uhz);
> > > > > > +         sensor->freq_avail[0] = hz;
> > > > > > +         sensor->freq_avail[1] = uhz;
> > > > > > +
> > > > > > +         step_size = sensor->sensor_info->intervals
> > > > > > +                             .desc[SCMI_SENS_INTVL_SEGMENT_STEP];
> > > > > > +         step_size_ns = scmi_iio_convert_interval_to_ns(step_size);
> > > > > > +         convert_ns_to_freq(step_size_ns, &hz, &uhz);
> > > > > > +         sensor->freq_avail[2] = hz;
> > > > > > +         sensor->freq_avail[3] = uhz;
> > > > > > +
> > > > > > +         high_interval = sensor->sensor_info->intervals
> > > > > > +                                 .desc[SCMI_SENS_INTVL_SEGMENT_HIGH];
> > > > > > +         high_interval_ns =
> > > > > > +                 scmi_iio_convert_interval_to_ns(high_interval);
> > > > > > +         convert_ns_to_freq(high_interval_ns, &hz, &uhz);
> > > > > > +         sensor->freq_avail[4] = hz;
> > > > > > +         sensor->freq_avail[5] = uhz;
> > > > > > + } else {
> > > > > > +         for (i = 0; i < sensor->sensor_info->intervals.count; i++) {
> > > > > > +                 cur_interval = sensor->sensor_info->intervals.desc[i];
> > > > > > +                 cur_interval_ns =
> > > > > > +                         scmi_iio_convert_interval_to_ns(cur_interval);
> > > > > > +                 convert_ns_to_freq(cur_interval_ns, &hz, &uhz);
> > > > > > +                 sensor->freq_avail[i * 2] = hz;
> > > > > > +                 sensor->freq_avail[i * 2 + 1] = uhz;
> > > > > > +         }
> > > > > > + }
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_buffers_setup(struct iio_dev *scmi_iiodev)
> > > > > > +{
> > > > > > + struct iio_buffer *buffer;
> > > > > > +
> > > > > > + buffer = devm_iio_kfifo_allocate(&scmi_iiodev->dev);
> > > > > > + if (!buffer)
> > > > > > +         return -ENOMEM;
> > > > > > +
> > > > > > + iio_device_attach_buffer(scmi_iiodev, buffer);
> > > > > > + scmi_iiodev->modes |= INDIO_BUFFER_SOFTWARE;
> > > > > > + scmi_iiodev->setup_ops = &scmi_iio_buffer_ops;
> > > > > > + return 0;
> > > > > > +}
> > > > > > +
> > > > > > +static struct iio_dev *scmi_alloc_iiodev(struct device *dev,
> > > > > > +                                  struct scmi_handle *handle,
> > > > > > +                                  const struct scmi_sensor_info *sensor_info)
> > > > > > +{
> > > > > > + struct iio_chan_spec *iio_channels;
> > > > > > + struct scmi_iio_priv *sensor;
> > > > > > + enum iio_modifier modifier;
> > > > > > + enum iio_chan_type type;
> > > > > > + struct iio_dev *iiodev;
> > > > > > + int i, ret;
> > > > > > +
> > > > > > + iiodev = devm_iio_device_alloc(dev, sizeof(*sensor));
> > > > > > + if (!iiodev)
> > > > > > +         return ERR_PTR(-ENOMEM);
> > > > > > +
> > > > > > + iiodev->modes = INDIO_DIRECT_MODE;
> > > > > > + iiodev->dev.parent = dev;
> > > > > > + sensor = iio_priv(iiodev);
> > > > > > + sensor->handle = handle;
> > > > > > + sensor->sensor_info = sensor_info;
> > > > > > + sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb;
> > > > > > + sensor->indio_dev = iiodev;
> > > > > > +
> > > > > > + /* adding one additional channel for timestamp */
> > > > > > + iiodev->num_channels = sensor_info->num_axis + 1;
> > > > > > + iiodev->name = sensor_info->name;
> > > > > > + iiodev->info = &scmi_iio_info;
> > > > > > +
> > > > > > + iio_channels =
> > > > > > +         devm_kzalloc(dev,
> > > > > > +                      sizeof(*iio_channels) * (iiodev->num_channels),
> > > > > > +                      GFP_KERNEL);
> > > > > > + if (!iio_channels)
> > > > > > +         return ERR_PTR(-ENOMEM);
> > > > > > +
> > > > > > + ret = scmi_iio_set_sampling_freq_avail(iiodev);
> > > > > > + if (ret < 0)
> > > > > > +         return ERR_PTR(ret);
> > > > > > +
> > > > > > + for (i = 0; i < sensor_info->num_axis; i++) {
> > > > > > +         ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type);
> > > > > > +         if (ret < 0)
> > > > > > +                 return ERR_PTR(ret);
> > > > > > +
> > > > > > +         ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name,
> > > > > > +                                          &modifier);
> > > > > > +         if (ret < 0)
> > > > > > +                 return ERR_PTR(ret);
> > > > > > +
> > > > > > +         scmi_iio_set_data_channel(&iio_channels[i], type, modifier,
> > > > > > +                                   sensor_info->axis[i].id);
> > > > > > + }
> > > > > > +
> > > > > > + scmi_iio_set_timestamp_channel(&iio_channels[i], i);
> > > > > > + iiodev->channels = iio_channels;
> > > > > > + return iiodev;
> > > > > > +}
> > > > > > +
> > > > > > +static int scmi_iio_dev_probe(struct scmi_device *sdev)
> > > > > > +{
> > > > > > + const struct scmi_sensor_info *sensor_info;
> > > > > > + struct scmi_handle *handle = sdev->handle;
> > > > > > + struct device *dev = &sdev->dev;
> > > > > > + struct iio_dev *scmi_iio_dev;
> > > > > > + u16 nr_sensors;
> > > > > > + int err = -ENODEV, i;
> > > > > > +
> > > > > > + if (!handle || !handle->sensor_ops) {
> > > > > > +         dev_err(dev, "SCMI device has no sensor interface\n");
> > > > > > +         return -EINVAL;
> > > > > > + }
> > > > > > +
> > > > > > + nr_sensors = handle->sensor_ops->count_get(handle);
> > > > > > + if (!nr_sensors) {
> > > > > > +         dev_dbg(dev, "0 sensors found via SCMI bus\n");
> > > > > > +         return -ENODEV;
> > > > > > + }
> > > > > > +
> > > > > > + for (i = 0; i < nr_sensors; i++) {
> > > > > > +         sensor_info = handle->sensor_ops->info_get(handle, i);
> > > > > > +         if (!sensor_info) {
> > > > > > +                 dev_err(dev, "SCMI sensor %d has missing info\n", i);
> > > > > > +                 return -EINVAL;
> > > > > > +         }
> > > > > > +
> > > > > > +         /* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > > > > +         if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS)
> > > > > > +                 continue;
> > > > > > +
> > > > > > +         /* This driver only supports 3-axis accel and gyro, skipping other sensors */
> > > > > > +         if (sensor_info->axis[0].type != METERS_SEC_SQUARED &&
> > > > > > +             sensor_info->axis[0].type != RADIANS_SEC)
> > > > > > +                 continue;
> > > > > > +
> > > > > > +         scmi_iio_dev = scmi_alloc_iiodev(dev, handle, sensor_info);
> > > > > > +         if (IS_ERR(scmi_iio_dev)) {
> > > > > > +                 dev_err(dev,
> > > > > > +                         "failed to allocate IIO device for sensor %s: %ld\n",
> > > > > > +                         sensor_info->name, PTR_ERR(scmi_iio_dev));
> > > > > > +                 return PTR_ERR(scmi_iio_dev);
> > > > > > +         }
> > > > > > +
> > > > > > +         err = scmi_iio_buffers_setup(scmi_iio_dev);
> > > > > > +         if (err < 0) {
> > > > > > +                 dev_err(dev,
> > > > > > +                         "IIO buffer setup error at sensor %s: %d\n",
> > > > > > +                         sensor_info->name, err);
> > > > > > +                 return err;
> > > > > > +         }
> > > > > > +
> > > > > > +         err = devm_iio_device_register(dev, scmi_iio_dev);
> > > > > > +         if (err) {
> > > > > > +                 dev_err(dev,
> > > > > > +                         "IIO device registration failed at sensor %s: %d\n",
> > > > > > +                         sensor_info->name, err);
> > > > > > +                 return err;
> > > > > > +         }
> > > > > > + }
> > > > > > + return err;
> > > > > > +}
> > > > > > +
> > > > > > +static const struct scmi_device_id scmi_id_table[] = {
> > > > > > + { SCMI_PROTOCOL_SENSOR, "iiodev" },
> > > > > > + {},
> > > > > > +};
> > > > > > +
> > > > > > +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> > > > > > +
> > > > > > +static struct scmi_driver scmi_iiodev_driver = {
> > > > > > + .name = "scmi-sensor-iiodev",
> > > > > > + .probe = scmi_iio_dev_probe,
> > > > > > + .id_table = scmi_id_table,
> > > > > > +};
> > > > > > +
> > > > > > +module_scmi_driver(scmi_iiodev_driver);
> > > > > > +
> > > > > > +MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>");
> > > > > > +MODULE_DESCRIPTION("SCMI IIO Driver");
> > > > > > +MODULE_LICENSE("GPL v2");
> > > > >
> > > >
> >
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-13 19:55             ` Jyoti Bhayana
@ 2021-03-14 15:40               ` Jonathan Cameron
  2021-03-14 18:47                 ` Jyoti Bhayana
  2021-03-15 13:41                 ` Sudeep Holla
  0 siblings, 2 replies; 16+ messages in thread
From: Jonathan Cameron @ 2021-03-14 15:40 UTC (permalink / raw)
  To: Jyoti Bhayana
  Cc: Cristian Marussi, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Sudeep Holla,
	Enrico Granata, Mikhail Golubev, Igor Skalkin, Peter Hilber,
	Ankit Arora, Guru Nagarajan, kernel test robot

On Sat, 13 Mar 2021 11:55:39 -0800
Jyoti Bhayana <jbhayana@google.com> wrote:

> Hi Jonathan,
> 
> I still see the old version 6 in ib-iio-scmi-5.12-rc2-take2 as well.

OK. I'm completely confused as to what is going with my local tree.
I have the right patch in the history but it didn't end up in the final
pushed out version.  Fat finger mess-up I guess and too many similarly named
branches and the fact I didn't check the final result closely enough.

There is now an ib-iio-scmi-5.12-rc2-take3 branch

This time it definitely has your patch from the 9th of March with no
instances of long long in it.

Sorry I messed this one up (again!)

Jonathan

> 
> Thanks,
> Jyoti


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-14 15:40               ` Jonathan Cameron
@ 2021-03-14 18:47                 ` Jyoti Bhayana
  2021-03-16 13:13                   ` Cristian Marussi
  2021-03-15 13:41                 ` Sudeep Holla
  1 sibling, 1 reply; 16+ messages in thread
From: Jyoti Bhayana @ 2021-03-14 18:47 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Cristian Marussi, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Sudeep Holla,
	Enrico Granata, Mikhail Golubev, Igor Skalkin, Peter Hilber,
	Ankit Arora, Guru Nagarajan, kernel test robot

Hi Jonathan,

No worries. Thanks for the update. It looks good now.

Thanks,
Jyoti

On Sun, Mar 14, 2021 at 8:40 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Sat, 13 Mar 2021 11:55:39 -0800
> Jyoti Bhayana <jbhayana@google.com> wrote:
>
> > Hi Jonathan,
> >
> > I still see the old version 6 in ib-iio-scmi-5.12-rc2-take2 as well.
>
> OK. I'm completely confused as to what is going with my local tree.
> I have the right patch in the history but it didn't end up in the final
> pushed out version.  Fat finger mess-up I guess and too many similarly named
> branches and the fact I didn't check the final result closely enough.
>
> There is now an ib-iio-scmi-5.12-rc2-take3 branch
>
> This time it definitely has your patch from the 9th of March with no
> instances of long long in it.
>
> Sorry I messed this one up (again!)
>
> Jonathan
>
> >
> > Thanks,
> > Jyoti
>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-14 15:40               ` Jonathan Cameron
  2021-03-14 18:47                 ` Jyoti Bhayana
@ 2021-03-15 13:41                 ` Sudeep Holla
  1 sibling, 0 replies; 16+ messages in thread
From: Sudeep Holla @ 2021-03-15 13:41 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Jyoti Bhayana, Sudeep Holla, Cristian Marussi, Jonathan Cameron,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Enrico Granata,
	Mikhail Golubev, Igor Skalkin, Peter Hilber, Ankit Arora,
	Guru Nagarajan, kernel test robot

Hi Jonathan,

On Sun, Mar 14, 2021 at 03:40:33PM +0000, Jonathan Cameron wrote:
> On Sat, 13 Mar 2021 11:55:39 -0800
> Jyoti Bhayana <jbhayana@google.com> wrote:
>
> > Hi Jonathan,
> >
> > I still see the old version 6 in ib-iio-scmi-5.12-rc2-take2 as well.
>
> OK. I'm completely confused as to what is going with my local tree.
> I have the right patch in the history but it didn't end up in the final
> pushed out version.  Fat finger mess-up I guess and too many similarly named
> branches and the fact I didn't check the final result closely enough.
>
> There is now an ib-iio-scmi-5.12-rc2-take3 branch
>

I have now used this for my for-next/scmi branch. It is not final yet,
just pushed out for bots to build test and get into -next. Let me know
if you have plans to change/rework this branch, I can update it anytime
till end of this week to avoid multiple hashes.

--
Regards,
Sudeep

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-14 18:47                 ` Jyoti Bhayana
@ 2021-03-16 13:13                   ` Cristian Marussi
  2021-03-16 17:53                     ` Jyoti Bhayana
  0 siblings, 1 reply; 16+ messages in thread
From: Cristian Marussi @ 2021-03-16 13:13 UTC (permalink / raw)
  To: Jyoti Bhayana
  Cc: Jonathan Cameron, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Sudeep Holla,
	Enrico Granata, Mikhail Golubev, Igor Skalkin, Peter Hilber,
	Ankit Arora, Guru Nagarajan, kernel test robot

Hi

I reposted my series on top of the take3 immutable branch from Jonathan
at:

https://lore.kernel.org/lkml/20210316124903.35011-1-cristian.marussi@arm.com/T/#t

You should have received an email regarding the relevant IIO SCMI patch

v7-0025-iio-scmi-port-driver-to-the-new-scmi_sensor_proto.patch

which includes the port of your v7 IIO SCMI driver to the new API, and moved
also the notification registration to the probe phase to simplify it
further.

On my side I tested with some fake emulated sensors emitting periodic
100ms SENSOR_UPDATE notifs...and verifying that notifications are still
dispatched and received fine. (values are just fakes)

If you can have a look, comment and possibly Ack.

Thanks

Cristian

---

(debian-arm64)root@debarm64:~# /root/iio_generic_buffer -a -c -1 -g -l 100 -N 0
iio device number being used is 0
trigger-less mode selected
Enabling all channels
Enabling: in_accel_x_en
Enabling: in_accel_z_en
Enabling: in_timestamp_en
Enabling: in_accel_y_en
-9465 -9457 -9449 6169807244683640832.000000
-9464 -9456 -9448 6169807244683640832.000000
-9463 -9455 -9447 6169807244683640832.000000
-9462 -9454 -9446 6169807244683640832.000000
-9461 -9453 -9445 6169807244683640832.000000
-9460 -9452 -9444 6169807244683640832.000000
-9459 -9451 -9443 7169807022831960064.000000
-9458 -9450 -9442 7169807022831960064.000000
-9457 -9449 -9441 7169807022831960064.000000
-9456 -9448 -9440 7169807022831960064.000000
-9455 -9447 -9439 7169807022831960064.000000
-9454 -9446 -9438 7169807022831960064.000000
-9453 -9445 -9437 7169807022831960064.000000
-9452 -9444 -9436 7169807022831960064.000000
-9451 -9443 -9435 7169807022831960064.000000
-9450 -9442 -9434 7169807022831960064.000000
-9449 -9441 -9433 8169806800980279296.000000
-9448 -9440 -9432 8169806800980279296.000000
-9447 -9439 -9431 8169806800980279296.000000
-9446 -9438 -9430 8169806800980279296.000000
-9445 -9437 -9429 8169806800980279296.000000
-9444 -9436 -9428 8169806800980279296.000000
-9443 -9435 -9427 8169806800980279296.000000
-9442 -9434 -9426 8169806800980279296.000000
-9441 -9433 -9425 8169806800980279296.000000
-9440 -9432 -9424 8169806800980279296.000000
-9439 -9431 -9423 9169807128884412416.000000
-9438 -9430 -9422 9169807128884412416.000000
-9437 -9429 -9421 9169807128884412416.000000
-9436 -9428 -9420 9169807128884412416.000000
-9435 -9427 -9419 9169807128884412416.000000
^CCaught signal 2
Disabling: in_accel_x_en
Disabling: in_accel_z_en
Disabling: in_timestamp_en
Disabling: in_accel_y_en


On Sun, Mar 14, 2021 at 11:47:25AM -0700, Jyoti Bhayana wrote:
> Hi Jonathan,
> 
> No worries. Thanks for the update. It looks good now.
> 
> Thanks,
> Jyoti
> 
> On Sun, Mar 14, 2021 at 8:40 AM Jonathan Cameron <jic23@kernel.org> wrote:
> >
> > On Sat, 13 Mar 2021 11:55:39 -0800
> > Jyoti Bhayana <jbhayana@google.com> wrote:
> >
> > > Hi Jonathan,
> > >
> > > I still see the old version 6 in ib-iio-scmi-5.12-rc2-take2 as well.
> >
> > OK. I'm completely confused as to what is going with my local tree.
> > I have the right patch in the history but it didn't end up in the final
> > pushed out version.  Fat finger mess-up I guess and too many similarly named
> > branches and the fact I didn't check the final result closely enough.
> >
> > There is now an ib-iio-scmi-5.12-rc2-take3 branch
> >
> > This time it definitely has your patch from the 9th of March with no
> > instances of long long in it.
> >
> > Sorry I messed this one up (again!)
> >
> > Jonathan
> >
> > >
> > > Thanks,
> > > Jyoti
> >

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors
  2021-03-16 13:13                   ` Cristian Marussi
@ 2021-03-16 17:53                     ` Jyoti Bhayana
  0 siblings, 0 replies; 16+ messages in thread
From: Jyoti Bhayana @ 2021-03-16 17:53 UTC (permalink / raw)
  To: Cristian Marussi
  Cc: Jonathan Cameron, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler,
	Mauro Carvalho Chehab, David S. Miller, Rob Herring,
	Lukas Bulwahn, linux-kernel, linux-iio, Sudeep Holla,
	Enrico Granata, Mikhail Golubev, Igor Skalkin, Peter Hilber,
	Ankit Arora, Guru Nagarajan, kernel test robot

Hi Christian,

Thanks for porting the driver to the new interface. I have commented
on changes in v7-0025-iio-scmi-port-driver-to-the-new-scmi_sensor_proto.patch.

Thanks,
Jyoti

On Tue, Mar 16, 2021 at 6:13 AM Cristian Marussi
<cristian.marussi@arm.com> wrote:
>
> Hi
>
> I reposted my series on top of the take3 immutable branch from Jonathan
> at:
>
> https://lore.kernel.org/lkml/20210316124903.35011-1-cristian.marussi@arm.com/T/#t
>
> You should have received an email regarding the relevant IIO SCMI patch
>
> v7-0025-iio-scmi-port-driver-to-the-new-scmi_sensor_proto.patch
>
> which includes the port of your v7 IIO SCMI driver to the new API, and moved
> also the notification registration to the probe phase to simplify it
> further.
>
> On my side I tested with some fake emulated sensors emitting periodic
> 100ms SENSOR_UPDATE notifs...and verifying that notifications are still
> dispatched and received fine. (values are just fakes)
>
> If you can have a look, comment and possibly Ack.
>
> Thanks
>
> Cristian
>
> ---
>
> (debian-arm64)root@debarm64:~# /root/iio_generic_buffer -a -c -1 -g -l 100 -N 0
> iio device number being used is 0
> trigger-less mode selected
> Enabling all channels
> Enabling: in_accel_x_en
> Enabling: in_accel_z_en
> Enabling: in_timestamp_en
> Enabling: in_accel_y_en
> -9465 -9457 -9449 6169807244683640832.000000
> -9464 -9456 -9448 6169807244683640832.000000
> -9463 -9455 -9447 6169807244683640832.000000
> -9462 -9454 -9446 6169807244683640832.000000
> -9461 -9453 -9445 6169807244683640832.000000
> -9460 -9452 -9444 6169807244683640832.000000
> -9459 -9451 -9443 7169807022831960064.000000
> -9458 -9450 -9442 7169807022831960064.000000
> -9457 -9449 -9441 7169807022831960064.000000
> -9456 -9448 -9440 7169807022831960064.000000
> -9455 -9447 -9439 7169807022831960064.000000
> -9454 -9446 -9438 7169807022831960064.000000
> -9453 -9445 -9437 7169807022831960064.000000
> -9452 -9444 -9436 7169807022831960064.000000
> -9451 -9443 -9435 7169807022831960064.000000
> -9450 -9442 -9434 7169807022831960064.000000
> -9449 -9441 -9433 8169806800980279296.000000
> -9448 -9440 -9432 8169806800980279296.000000
> -9447 -9439 -9431 8169806800980279296.000000
> -9446 -9438 -9430 8169806800980279296.000000
> -9445 -9437 -9429 8169806800980279296.000000
> -9444 -9436 -9428 8169806800980279296.000000
> -9443 -9435 -9427 8169806800980279296.000000
> -9442 -9434 -9426 8169806800980279296.000000
> -9441 -9433 -9425 8169806800980279296.000000
> -9440 -9432 -9424 8169806800980279296.000000
> -9439 -9431 -9423 9169807128884412416.000000
> -9438 -9430 -9422 9169807128884412416.000000
> -9437 -9429 -9421 9169807128884412416.000000
> -9436 -9428 -9420 9169807128884412416.000000
> -9435 -9427 -9419 9169807128884412416.000000
> ^CCaught signal 2
> Disabling: in_accel_x_en
> Disabling: in_accel_z_en
> Disabling: in_timestamp_en
> Disabling: in_accel_y_en
>
>
> On Sun, Mar 14, 2021 at 11:47:25AM -0700, Jyoti Bhayana wrote:
> > Hi Jonathan,
> >
> > No worries. Thanks for the update. It looks good now.
> >
> > Thanks,
> > Jyoti
> >
> > On Sun, Mar 14, 2021 at 8:40 AM Jonathan Cameron <jic23@kernel.org> wrote:
> > >
> > > On Sat, 13 Mar 2021 11:55:39 -0800
> > > Jyoti Bhayana <jbhayana@google.com> wrote:
> > >
> > > > Hi Jonathan,
> > > >
> > > > I still see the old version 6 in ib-iio-scmi-5.12-rc2-take2 as well.
> > >
> > > OK. I'm completely confused as to what is going with my local tree.
> > > I have the right patch in the history but it didn't end up in the final
> > > pushed out version.  Fat finger mess-up I guess and too many similarly named
> > > branches and the fact I didn't check the final result closely enough.
> > >
> > > There is now an ib-iio-scmi-5.12-rc2-take3 branch
> > >
> > > This time it definitely has your patch from the 9th of March with no
> > > instances of long long in it.
> > >
> > > Sorry I messed this one up (again!)
> > >
> > > Jonathan
> > >
> > > >
> > > > Thanks,
> > > > Jyoti
> > >

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2021-03-16 17:54 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-09 23:12 [PATCH v7 0/1] Adding support for IIO SCMI based sensors Jyoti Bhayana
2021-03-09 23:12 ` [PATCH v7 1/1] iio/scmi: Adding support for IIO SCMI Based Sensors Jyoti Bhayana
2021-03-10 11:15   ` Peter Hilber
2021-03-10 17:19     ` Jyoti Bhayana
2021-03-11  9:55       ` Peter Hilber
2021-03-11 21:08   ` Jonathan Cameron
2021-03-12 12:16     ` Jonathan Cameron
2021-03-12 13:31       ` Cristian Marussi
2021-03-12 17:54         ` Jyoti Bhayana
2021-03-13 17:11           ` Jonathan Cameron
2021-03-13 19:55             ` Jyoti Bhayana
2021-03-14 15:40               ` Jonathan Cameron
2021-03-14 18:47                 ` Jyoti Bhayana
2021-03-16 13:13                   ` Cristian Marussi
2021-03-16 17:53                     ` Jyoti Bhayana
2021-03-15 13:41                 ` Sudeep Holla

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).