[PATCH 3/3] iio: Add Qualcomm Sensor Manager drivers

Yassine Oudjana
2025-04-06T14:08:20Z

Add drivers for sensors exposed by the Qualcomm Sensor Manager service,
which is provided by SLPI or ADSP on Qualcomm SoCs. Supported sensors
include accelerometers, gyroscopes, pressure sensors, proximity sensors
and magnetometers.

Signed-off-by: Yassine Oudjana <y.oudjana@protonmail.com>
---
MAINTAINERS | 18 +
drivers/iio/accel/Kconfig | 10 +
drivers/iio/accel/Makefile | 2 +
drivers/iio/accel/qcom_smgr_accel.c | 138 ++++
drivers/iio/common/Kconfig | 1 +
drivers/iio/common/Makefile | 1 +
drivers/iio/common/qcom_smgr/Kconfig | 16 +
drivers/iio/common/qcom_smgr/Makefile | 8 +
drivers/iio/common/qcom_smgr/qcom_smgr.c | 589 ++++++++++++++++
drivers/iio/common/qcom_smgr/qmi/Makefile | 3 +
drivers/iio/common/qcom_smgr/qmi/sns_smgr.c | 711 ++++++++++++++++++++
drivers/iio/common/qcom_smgr/qmi/sns_smgr.h | 163 +++++
drivers/iio/gyro/Kconfig | 10 +
drivers/iio/gyro/Makefile | 2 +
drivers/iio/gyro/qcom_smgr_gyro.c | 138 ++++
drivers/iio/magnetometer/Kconfig | 9 +
drivers/iio/magnetometer/Makefile | 2 +
drivers/iio/magnetometer/qcom_smgr_mag.c | 138 ++++
drivers/iio/pressure/Kconfig | 10 +
drivers/iio/pressure/Makefile | 1 +
drivers/iio/pressure/qcom_smgr_pressure.c | 106 +++
drivers/iio/proximity/Kconfig | 10 +
drivers/iio/proximity/Makefile | 1 +
drivers/iio/proximity/qcom_smgr_prox.c | 106 +++
include/linux/iio/common/qcom_smgr.h | 64 ++
25 files changed, 2257 insertions(+)
create mode 100644 drivers/iio/accel/qcom_smgr_accel.c
create mode 100644 drivers/iio/common/qcom_smgr/Kconfig
create mode 100644 drivers/iio/common/qcom_smgr/Makefile
create mode 100644 drivers/iio/common/qcom_smgr/qcom_smgr.c
create mode 100644 drivers/iio/common/qcom_smgr/qmi/Makefile
create mode 100644 drivers/iio/common/qcom_smgr/qmi/sns_smgr.c
create mode 100644 drivers/iio/common/qcom_smgr/qmi/sns_smgr.h
create mode 100644 drivers/iio/gyro/qcom_smgr_gyro.c
create mode 100644 drivers/iio/magnetometer/qcom_smgr_mag.c
create mode 100644 drivers/iio/pressure/qcom_smgr_pressure.c
create mode 100644 drivers/iio/proximity/qcom_smgr_prox.c
create mode 100644 include/linux/iio/common/qcom_smgr.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 78467ad7a8fe..d0183c09cc3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20004,6 +20004,24 @@ F: Documentation/networking/device_drivers/cellular/qualcomm/rmnet.rst
F: drivers/net/ethernet/qualcomm/rmnet/
F: include/linux/if_rmnet.h

+QUALCOMM SENSOR MANAGER IIO DRIVER
+M: Yassine Oudjana <y.oudjana@protonmail.com>
+L: linux-iio@vger.kernel.org
+L: linux-arm-msm@vger.kernel.org
+S: Maintained
+F: drivers/iio/accel/qcom_smgr_accel.c
+F: drivers/iio/common/qcom_smgr/Kconfig
+F: drivers/iio/common/qcom_smgr/Makefile
+F: drivers/iio/common/qcom_smgr/qcom_smgr.c
+F: drivers/iio/common/qcom_smgr/qmi/Makefile
+F: drivers/iio/common/qcom_smgr/qmi/sns_smgr.c
+F: drivers/iio/common/qcom_smgr/qmi/sns_smgr.h
+F: drivers/iio/gyro/qcom_smgr_gyro.c
+F: drivers/iio/magnetometer/qcom_smgr_mag.c
+F: drivers/iio/pressure/qcom_smgr_pressure.c
+F: drivers/iio/proximity/qcom_smgr_prox.c
+F: include/linux/iio/common/qcom_smgr.h
+
QUALCOMM TRUST ZONE MEMORY ALLOCATOR
M: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
L: linux-arm-msm@vger.kernel.org
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 8c3f7cf55d5f..d3253b856771 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -407,6 +407,16 @@ config IIO_CROS_EC_ACCEL_LEGACY
Sensor data is retrieved through IO memory.
Newer devices should use IIO_CROS_EC_SENSORS.

+config IIO_QCOM_SMGR_ACCEL
+ tristate "Qualcomm SSC Sensor Manager Accelerometer Sensor"
+ depends on IIO_QCOM_SMGR
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to get support for accelerometers connected to
+ a Qualcomm Snapdragon Sensor Core and accessed through its
+ Sensor Manager service.
+
config IIO_ST_ACCEL_3AXIS
tristate "STMicroelectronics accelerometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index ca8569e25aba..5f7fcd88a502 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -78,6 +78,8 @@ obj-$(CONFIG_STK8BA50) += stk8ba50.o

obj-$(CONFIG_IIO_CROS_EC_ACCEL_LEGACY) += cros_ec_accel_legacy.o

+obj-$(CONFIG_IIO_QCOM_SMGR_ACCEL) += qcom_smgr_accel.o
+
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o

obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
diff --git a/drivers/iio/accel/qcom_smgr_accel.c b/drivers/iio/accel/qcom_smgr_accel.c
new file mode 100644
index 000000000000..ce854312d1d9
--- /dev/null
+++ b/drivers/iio/accel/qcom_smgr_accel.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm Sensor Manager accelerometer driver
+ *
+ * Copyright (c) 2022, Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+
+static const struct iio_chan_spec qcom_smgr_accel_iio_channels[] = {
+ {
+ .type = IIO_ACCEL,
+ .modified = true,
+ .channel2 = IIO_MOD_X,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_ACCEL,
+ .modified = true,
+ .channel2 = IIO_MOD_Y,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_ACCEL,
+ .modified = true,
+ .channel2 = IIO_MOD_Z,
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_TIMESTAMP,
+ .channel = -1,
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 64,
+ .endianness = IIO_LE,
+ },
+ },
+};
+
+static int qcom_smgr_accel_probe(struct platform_device *pdev)
+{
+ struct iio_dev *iio_dev;
+ struct qcom_smgr_iio_priv *priv;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(iio_dev);
+ priv->sensor = *(struct qcom_smgr_sensor **)pdev->dev.platform_data;
+ priv->sensor->iio_dev = iio_dev;
+
+ iio_dev->name = "qcom-smgr-accel";
+ iio_dev->info = &qcom_smgr_iio_info;
+ iio_dev->channels = qcom_smgr_accel_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(qcom_smgr_accel_iio_channels);
+
+ ret = devm_iio_kfifo_buffer_setup(&pdev->dev, iio_dev,
+ &qcom_smgr_buffer_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to setup buffer: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register IIO device: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv->sensor);
+
+ return 0;
+}
+
+static void qcom_smgr_accel_remove(struct platform_device *pdev)
+{
+ struct qcom_smgr_sensor *sensor = platform_get_drvdata(pdev);
+
+ sensor->iio_dev = NULL;
+}
+
+static const struct platform_device_id qcom_smgr_accel_ids[] = {
+ { .name = "qcom-smgr-accel" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, qcom_smgr_accel_ids);
+
+static struct platform_driver qcom_smgr_accel_driver = {
+ .probe = qcom_smgr_accel_probe,
+ .remove = qcom_smgr_accel_remove,
+ .driver = {
+ .name = "qcom_smgr_accel",
+ },
+ .id_table = qcom_smgr_accel_ids,
+};
+module_platform_driver(qcom_smgr_accel_driver);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Qualcomm Sensor Manager accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index 1ccb5ccf3706..0ad8b3972087 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -8,5 +8,6 @@ source "drivers/iio/common/hid-sensors/Kconfig"
source "drivers/iio/common/inv_sensors/Kconfig"
source "drivers/iio/common/ms_sensors/Kconfig"
source "drivers/iio/common/scmi_sensors/Kconfig"
+source "drivers/iio/common/qcom_smgr/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 d3e952239a62..f3f18484c91b 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -13,5 +13,6 @@ obj-y += hid-sensors/
obj-y += inv_sensors/
obj-y += ms_sensors/
obj-y += scmi_sensors/
+obj-y += qcom_smgr/
obj-y += ssp_sensors/
obj-y += st_sensors/
diff --git a/drivers/iio/common/qcom_smgr/Kconfig b/drivers/iio/common/qcom_smgr/Kconfig
new file mode 100644
index 000000000000..da1965185001
--- /dev/null
+++ b/drivers/iio/common/qcom_smgr/Kconfig
@@ -0,0 +1,16 @@
+#
+# Qualcomm Sensor Manager IIO
+#
+
+config IIO_QCOM_SMGR
+ tristate "Qualcomm SSC Sensor Manager"
+ depends on ARCH_QCOM
+ depends on QCOM_RPROC_COMMON
+ select QCOM_QMI_HELPERS
+ select IIO_BUFFER
+ help
+ Say yes here to build core support for the Sensor Manager (SMGR)
+ service provided by the Qualcomm Snapdragon Sensor Core (SSC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called smgr.
diff --git a/drivers/iio/common/qcom_smgr/Makefile b/drivers/iio/common/qcom_smgr/Makefile
new file mode 100644
index 000000000000..84554cedd2e5
--- /dev/null
+++ b/drivers/iio/common/qcom_smgr/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Qualcomm Sensor Manager driver
+#
+
+obj-y += qmi/
+
+obj-$(CONFIG_IIO_QCOM_SMGR) += qcom_smgr.o
diff --git a/drivers/iio/common/qcom_smgr/qcom_smgr.c b/drivers/iio/common/qcom_smgr/qcom_smgr.c
new file mode 100644
index 000000000000..8d46be11d5b6
--- /dev/null
+++ b/drivers/iio/common/qcom_smgr/qcom_smgr.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm Sensor Manager core driver
+ *
+ * Copyright (c) 2021, Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/iio/iio.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc/qcom_rproc.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/soc/qcom/qrtr.h>
+#include <linux/types.h>
+#include <net/sock.h>
+
+#include "qmi/sns_smgr.h"
+
+#define SMGR_TICKS_PER_SECOND 32768
+#define SMGR_REPORT_RATE_HZ (SMGR_TICKS_PER_SECOND * 2)
+#define SMGR_VALUE_DIV 65536
+
+struct qcom_smgr {
+ struct device *dev;
+
+ struct qmi_handle sns_smgr_hdl;
+ struct sockaddr_qrtr sns_smgr_info;
+ struct work_struct sns_smgr_work;
+
+ u8 sensor_count;
+ struct qcom_smgr_sensor *sensors;
+};
+
+static const char *const qcom_smgr_sensor_type_platform_names[] = {
+ [SNS_SMGR_SENSOR_TYPE_ACCEL] = "qcom-smgr-accel",
+ [SNS_SMGR_SENSOR_TYPE_GYRO] = "qcom-smgr-gyro",
+ [SNS_SMGR_SENSOR_TYPE_MAG] = "qcom-smgr-mag",
+ [SNS_SMGR_SENSOR_TYPE_PROX_LIGHT] = "qcom-smgr-prox-light",
+ [SNS_SMGR_SENSOR_TYPE_PRESSURE] = "qcom-smgr-pressure",
+ [SNS_SMGR_SENSOR_TYPE_HALL_EFFECT] = "qcom-smgr-hall-effect"
+};
+
+static void qcom_smgr_unregister_sensor(void *data)
+{
+ struct platform_device *pdev = data;
+
+ platform_device_unregister(pdev);
+}
+
+static int qcom_smgr_register_sensor(struct qcom_smgr *smgr,
+ struct qcom_smgr_sensor *sensor)
+{
+ struct platform_device *pdev;
+ const char *name = qcom_smgr_sensor_type_platform_names[sensor->type];
+
+ pdev = platform_device_register_data(smgr->dev, name, sensor->id,
+ &sensor, sizeof(sensor));
+ if (IS_ERR(pdev)) {
+ dev_err(smgr->dev, "Failed to register %s: %pe\n", name, pdev);
+ return PTR_ERR(pdev);
+ }
+
+ return devm_add_action_or_reset(smgr->dev, qcom_smgr_unregister_sensor,
+ pdev);
+}
+
+static int qcom_smgr_request_all_sensor_info(struct qcom_smgr *smgr,
+ struct qcom_smgr_sensor **sensors)
+{
+ struct sns_smgr_all_sensor_info_resp resp = {};
+ struct qmi_txn txn;
+ u8 i;
+ int ret;
+
+ dev_dbg(smgr->dev, "Getting available sensors\n");
+
+ ret = qmi_txn_init(&smgr->sns_smgr_hdl, &txn,
+ sns_smgr_all_sensor_info_resp_ei, &resp);
+ if (ret < 0) {
+ dev_err(smgr->dev, "Failed to initialize QMI TXN: %d\n", ret);
+ return ret;
+ }
+
+ ret = qmi_send_request(&smgr->sns_smgr_hdl, &smgr->sns_smgr_info, &txn,
+ SNS_SMGR_ALL_SENSOR_INFO_MSG_ID,
+ SNS_SMGR_ALL_SENSOR_INFO_REQ_MAX_LEN, NULL,
+ NULL);
+ if (ret) {
+ dev_err(smgr->dev,
+ "Failed to send available sensors request: %d\n", ret);
+ qmi_txn_cancel(&txn);
+ return ret;
+ }
+
+ ret = qmi_txn_wait(&txn, 5 * HZ);
+ if (ret < 0)
+ return ret;
+
+ /* Check the response */
+ if (resp.result) {
+ dev_err(smgr->dev, "Available sensors request failed: 0x%x\n",
+ resp.result);
+ return -EREMOTEIO;
+ }
+
+ *sensors = devm_kzalloc(smgr->dev,
+ sizeof(struct qcom_smgr_sensor) * resp.item_len,
+ GFP_KERNEL);
+
+ for (i = 0; i < resp.item_len; ++i) {
+ (*sensors)[i].id = resp.items[i].id;
+ (*sensors)[i].type =
+ sns_smgr_sensor_type_from_str(resp.items[i].type);
+ }
+
+ return resp.item_len;
+}
+
+static int qcom_smgr_request_single_sensor_info(struct qcom_smgr *smgr,
+ struct qcom_smgr_sensor *sensor)
+{
+ struct sns_smgr_single_sensor_info_req req = {
+ .sensor_id = sensor->id,
+ };
+ struct sns_smgr_single_sensor_info_resp resp = {};
+ struct qmi_txn txn;
+ u8 i;
+ int ret;
+
+ dev_vdbg(smgr->dev, "Getting single sensor info for ID 0x%02x\n",
+ sensor->id);
+
+ ret = qmi_txn_init(&smgr->sns_smgr_hdl, &txn,
+ sns_smgr_single_sensor_info_resp_ei, &resp);
+ if (ret < 0) {
+ dev_err(smgr->dev, "Failed to initialize QMI transaction: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = qmi_send_request(&smgr->sns_smgr_hdl, &smgr->sns_smgr_info, &txn,
+ SNS_SMGR_SINGLE_SENSOR_INFO_MSG_ID,
+ SNS_SMGR_SINGLE_SENSOR_INFO_REQ_MAX_LEN,
+ sns_smgr_single_sensor_info_req_ei, &req);
+ if (ret < 0) {
+ dev_err(smgr->dev, "Failed to send sensor data request: %d\n",
+ ret);
+ qmi_txn_cancel(&txn);
+ return ret;
+ }
+
+ ret = qmi_txn_wait(&txn, 5 * HZ);
+ if (ret < 0)
+ return ret;
+
+ /* Check the response */
+ if (resp.result) {
+ dev_err(smgr->dev, "Single sensor info request failed: 0x%x\n",
+ resp.result);
+ return -EREMOTEIO;
+ }
+
+ sensor->data_type_count = resp.data_type_len;
+ sensor->data_types =
+ devm_kzalloc(smgr->dev,
+ sizeof(struct qcom_smgr_data_type_item) *
+ sensor->data_type_count,
+ GFP_KERNEL);
+ if (!sensor->data_types)
+ return -ENOMEM;
+
+ for (i = 0; i < sensor->data_type_count; ++i) {
+ sensor->data_types[i].name = devm_kstrdup_const(
+ smgr->dev, resp.data_types[i].name, GFP_KERNEL);
+ sensor->data_types[i].vendor = devm_kstrdup_const(
+ smgr->dev, resp.data_types[i].vendor, GFP_KERNEL);
+
+ sensor->data_types[i].range = resp.data_types[i].range;
+
+ sensor->data_types[i].native_sample_rate_count =
+ resp.native_sample_rates[i].rate_count;
+ if (sensor->data_types[i].native_sample_rate_count) {
+ sensor->data_types[i]
+ .native_sample_rates = devm_kmemdup_array(
+ smgr->dev, resp.native_sample_rates[i].rates,
+ sensor->data_types[i].native_sample_rate_count,
+ sizeof(u16), GFP_KERNEL);
+ if (!sensor->data_types[i].native_sample_rates)
+ return -ENOMEM;
+ }
+
+ sensor->data_types[i].max_sample_rate =
+ resp.data_types[i].max_sample_rate_hz;
+ }
+
+ return 0;
+}
+
+static int qcom_smgr_request_buffering(struct qcom_smgr *smgr,
+ struct qcom_smgr_sensor *sensor,
+ bool enable)
+{
+ struct sns_smgr_buffering_req req = {
+ /*
+ * Reuse sensor ID as a report ID to avoid having to keep track
+ * of a separate set of IDs
+ */
+ .report_id = sensor->id,
+ .notify_suspend_valid = false
+ };
+ struct sns_smgr_buffering_resp resp = {};
+ struct qmi_txn txn;
+ u16 sample_rate = 0;
+ int ret;
+
+ if (enable) {
+ req.action = SNS_SMGR_BUFFERING_ACTION_ADD;
+
+ /*
+ * Report rate and sample rate can be configured separately.
+ * The former is the rate at which buffering report indications
+ * are sent, while the latter is the actual sample rate of the
+ * sensor. If report rate is set lower than sample rate,
+ * multiple samples can be bundled and sent in one report.
+ * A report cannot have 0 samples, therefore report rate cannot
+ * be higher than sample rate.
+ *
+ * Fow now we default to the maximum sample rate and set the
+ * report rate such that every report contains only 1 sample.
+ * This gives us the lowest latency.
+ */
+ if (sensor->data_types[0].native_sample_rates)
+ sample_rate = sensor->data_types[0].native_sample_rates
+ [sensor->data_types[0]
+ .native_sample_rate_count - 1];
+
+ /*
+ * SMGR may support a lower maximum sample rate than natively
+ * supported by the sensor.
+ */
+ if (sample_rate == 0 ||
+ sample_rate > sensor->data_types[0].max_sample_rate)
+ sample_rate = sensor->data_types[0].max_sample_rate;
+
+ req.report_rate = sample_rate * SMGR_REPORT_RATE_HZ;
+
+ req.item_len = 1;
+ req.items[0].sensor_id = sensor->id;
+ req.items[0].data_type = SNS_SMGR_DATA_TYPE_PRIMARY;
+
+ req.items[0].sampling_rate = sample_rate;
+
+ /*
+ * Unknown fields set to values frequently seen in dumps and
+ * known to be working (although many different random values
+ * appear to not cause any trouble).
+ */
+ req.items[0].val1 = 3;
+ req.items[0].val2 = 1;
+ } else
+ req.action = SNS_SMGR_BUFFERING_ACTION_DELETE;
+
+ ret = qmi_txn_init(&smgr->sns_smgr_hdl, &txn,
+ sns_smgr_buffering_resp_ei, &resp);
+ if (ret < 0) {
+ dev_err(smgr->dev, "Failed to initialize QMI TXN: %d\n", ret);
+ return ret;
+ }
+
+ ret = qmi_send_request(&smgr->sns_smgr_hdl, &smgr->sns_smgr_info, &txn,
+ SNS_SMGR_BUFFERING_MSG_ID,
+ SNS_SMGR_BUFFERING_REQ_MAX_LEN,
+ sns_smgr_buffering_req_ei, &req);
+ if (ret < 0) {
+ dev_err(smgr->dev, "Failed to send buffering request: %d\n",
+ ret);
+ qmi_txn_cancel(&txn);
+ return ret;
+ }
+
+ ret = qmi_txn_wait(&txn, 5 * HZ);
+ if (ret < 0)
+ return ret;
+
+ /* Check the response */
+ if (resp.result) {
+ dev_err(smgr->dev, "Buffering request failed: 0x%x\n",
+ resp.result);
+ return -EREMOTEIO;
+ }
+
+ /* Keep track of requested sample rate */
+ sensor->data_types[0].cur_sample_rate = sample_rate;
+
+ return 0;
+}
+
+static void qcom_smgr_buffering_report_handler(struct qmi_handle *hdl,
+ struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn,
+ const void *data)
+{
+ struct qcom_smgr *smgr =
+ container_of(hdl, struct qcom_smgr, sns_smgr_hdl);
+ struct sns_smgr_buffering_report_ind *ind =
+ (struct sns_smgr_buffering_report_ind *)data;
+ struct qcom_smgr_sensor *sensor;
+ u8 i;
+
+ for (i = 0; i < smgr->sensor_count; ++i) {
+ sensor = &smgr->sensors[i];
+
+ /* Find sensor matching report */
+ if (sensor->id != ind->report_id)
+ continue;
+
+ if (!sensor->iio_dev)
+ /* Corresponding driver was unloaded. Ignore remaining reports. */
+ return;
+
+ /*
+ * Since we are matching report rate with sample rate, we only
+ * get a single sample in every report.
+ */
+ iio_push_to_buffers_with_timestamp(sensor->iio_dev,
+ ind->samples[0].values,
+ ind->metadata.timestamp);
+
+ break;
+ }
+}
+
+static const struct qmi_msg_handler qcom_smgr_msg_handlers[] = {
+ {
+ .type = QMI_INDICATION,
+ .msg_id = SNS_SMGR_BUFFERING_REPORT_MSG_ID,
+ .ei = sns_smgr_buffering_report_ind_ei,
+ .decoded_size = sizeof(struct sns_smgr_buffering_report_ind),
+ .fn = qcom_smgr_buffering_report_handler,
+ },
+ {}
+};
+
+static int qcom_smgr_sensor_postenable(struct iio_dev *iio_dev)
+{
+ struct qcom_smgr *smgr = dev_get_drvdata(iio_dev->dev.parent->parent);
+ struct qcom_smgr_iio_priv *priv = iio_priv(iio_dev);
+ struct qcom_smgr_sensor *sensor = priv->sensor;
+
+ return qcom_smgr_request_buffering(smgr, sensor, true);
+}
+
+static int qcom_smgr_sensor_postdisable(struct iio_dev *iio_dev)
+{
+ struct qcom_smgr *smgr = dev_get_drvdata(iio_dev->dev.parent->parent);
+ struct qcom_smgr_iio_priv *priv = iio_priv(iio_dev);
+ struct qcom_smgr_sensor *sensor = priv->sensor;
+
+ return qcom_smgr_request_buffering(smgr, sensor, false);
+}
+
+const struct iio_buffer_setup_ops qcom_smgr_buffer_ops = {
+ .postenable = &qcom_smgr_sensor_postenable,
+ .postdisable = &qcom_smgr_sensor_postdisable
+};
+EXPORT_SYMBOL_GPL(qcom_smgr_buffer_ops);
+
+static int qcom_smgr_iio_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct qcom_smgr_iio_priv *priv = iio_priv(iio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = priv->sensor->data_types[0].cur_sample_rate;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ /*
+ * Proximity value is reported as (SMGR_VALUE_DIV - x)/SMGR_VALUE_DIV of
+ * the sensor range. As with sensor values, range is also reported as range
+ * in meters * SMGR_VALUE_DIV. Proximity in meters can be calculated as
+ * such:
+ *
+ * proximity = -value * range / SMGR_VALUE_DIV**2
+ *
+ * Since our denominator (val2) is an int, we cannot fit SMGR_VALUE_DIV**2.
+ * Without losing too much accuracy, we can instead divide by 2 in the
+ * numerator and denominator, and move the -1 coefficient to the
+ * denominator. This way we can exactly fit within the lower bound of int.
+ */
+ *val = priv->sensor->data_types[0].range / 2;
+ *val2 = -SMGR_VALUE_DIV / 2 * SMGR_VALUE_DIV;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ /*
+ * Sensor values are generally reported as 1/SMGR_VALUE_DIVths of the
+ * corresponding unit.
+ */
+ *val = 1;
+ *val2 = SMGR_VALUE_DIV;
+ return IIO_VAL_FRACTIONAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * Proximity values are inverted and start from the upper bound as explained above.
+ * No other channel types have an offset.
+ */
+ *val = priv->sensor->data_types[0].range;
+ *val2 = SMGR_VALUE_DIV;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static int qcom_smgr_iio_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct qcom_smgr_iio_priv *priv = iio_priv(iio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ priv->sensor->data_types[0].cur_sample_rate = val;
+
+ /*
+ * Send new SMGR buffering request with updated rates
+ * if buffer is enabled
+ */
+ if (iio_buffer_enabled(iio_dev))
+ return iio_dev->setup_ops->postenable(iio_dev);
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int qcom_smgr_iio_read_avail(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct qcom_smgr_iio_priv *priv = iio_priv(iio_dev);
+ const int samp_freq_vals[3] = {
+ 1, 1, priv->sensor->data_types[0].cur_sample_rate
+ };
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *type = IIO_VAL_INT;
+ *vals = samp_freq_vals;
+ *length = ARRAY_SIZE(samp_freq_vals);
+ return IIO_AVAIL_RANGE;
+ }
+
+ return -EINVAL;
+}
+
+const struct iio_info qcom_smgr_iio_info = {
+ .read_raw = qcom_smgr_iio_read_raw,
+ .write_raw = qcom_smgr_iio_write_raw,
+ .read_avail = qcom_smgr_iio_read_avail,
+};
+EXPORT_SYMBOL_GPL(qcom_smgr_iio_info);
+
+/* SMGR reports values for 3-axis sensors in north-east-down coordinates */
+static const struct iio_mount_matrix qcom_smgr_iio_mount_matrix = {
+ .rotation = {
+ "0", "-1", "0",
+ "-1", "0", "0",
+ "0", "0", "1"
+ }
+};
+
+static const struct iio_mount_matrix *
+qcom_smgr_iio_get_mount_matrix(const struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan)
+{
+ return &qcom_smgr_iio_mount_matrix;
+}
+
+const struct iio_chan_spec_ext_info qcom_smgr_iio_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, qcom_smgr_iio_get_mount_matrix),
+ {}
+};
+EXPORT_SYMBOL_GPL(qcom_smgr_iio_ext_info);
+
+static int qcom_smgr_probe(struct qrtr_device *qdev)
+{
+ struct qcom_smgr *smgr;
+ int i, j;
+ int ret;
+
+ smgr = devm_kzalloc(&qdev->dev, sizeof(*smgr), GFP_KERNEL);
+ if (!smgr)
+ return -ENOMEM;
+
+ smgr->dev = &qdev->dev;
+
+ smgr->sns_smgr_info.sq_family = AF_QIPCRTR;
+ smgr->sns_smgr_info.sq_node = qdev->node;
+ smgr->sns_smgr_info.sq_port = qdev->port;
+
+ dev_set_drvdata(&qdev->dev, smgr);
+
+ ret = qmi_handle_init(&smgr->sns_smgr_hdl,
+ SNS_SMGR_SINGLE_SENSOR_INFO_RESP_MAX_LEN, NULL,
+ qcom_smgr_msg_handlers);
+ if (ret < 0) {
+ dev_err(smgr->dev,
+ "Failed to initialize sensor manager handle: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = qcom_smgr_request_all_sensor_info(smgr, &smgr->sensors);
+ if (ret < 0) {
+ dev_err(smgr->dev, "Failed to get available sensors: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ smgr->sensor_count = ret;
+
+ /* Get primary and secondary sensors from each sensor ID */
+ for (i = 0; i < smgr->sensor_count; i++) {
+ ret = qcom_smgr_request_single_sensor_info(smgr,
+ &smgr->sensors[i]);
+ if (ret < 0) {
+ dev_err(smgr->dev,
+ "Failed to get sensors from ID 0x%02x: %pe\n",
+ smgr->sensors[i].id, ERR_PTR(ret));
+ return ret;
+ }
+
+ for (j = 0; j < smgr->sensors[i].data_type_count; j++) {
+ /* Default to maximum sample rate */
+ smgr->sensors[i].data_types->cur_sample_rate =
+ smgr->sensors[i].data_types->max_sample_rate;
+
+ dev_dbg(smgr->dev, "0x%02x,%d: %s %s\n",
+ smgr->sensors[i].id, j,
+ smgr->sensors[i].data_types[j].vendor,
+ smgr->sensors[i].data_types[j].name);
+ }
+
+ qcom_smgr_register_sensor(smgr, &smgr->sensors[i]);
+ }
+
+ return 0;
+}
+
+static void qcom_smgr_remove(struct qrtr_device *qdev)
+{
+ struct qcom_smgr *smgr = dev_get_drvdata(&qdev->dev);
+
+ qmi_handle_release(&smgr->sns_smgr_hdl);
+}
+
+static const struct qrtr_device_id qcom_smgr_qrtr_match[] = {
+ {
+ .service = SNS_SMGR_QMI_SVC_ID,
+ .instance = QRTR_INSTANCE(SNS_SMGR_QMI_SVC_V1,
+ SNS_SMGR_QMI_INS_ID)
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(qrtr, qcom_smgr_qrtr_match);
+
+static struct qrtr_driver qcom_smgr_driver = {
+ .probe = qcom_smgr_probe,
+ .remove = qcom_smgr_remove,
+ .id_table = qcom_smgr_qrtr_match,
+ .driver = {
+ .name = "qcom_smgr",
+ },
+};
+module_qrtr_driver(qcom_smgr_driver);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Qualcomm Sensor Manager driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/qcom_smgr/qmi/Makefile b/drivers/iio/common/qcom_smgr/qmi/Makefile
new file mode 100644
index 000000000000..e5722f0f8f68
--- /dev/null
+++ b/drivers/iio/common/qcom_smgr/qmi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_IIO_QCOM_SMGR) += sns_smgr.o
diff --git a/drivers/iio/common/qcom_smgr/qmi/sns_smgr.c b/drivers/iio/common/qcom_smgr/qmi/sns_smgr.c
new file mode 100644
index 000000000000..624e3074a051
--- /dev/null
+++ b/drivers/iio/common/qcom_smgr/qmi/sns_smgr.c
@@ -0,0 +1,711 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * QMI element info arrays and helper functions for Qualcomm Sensor Manager
+ *
+ * Copyright (c) 2021, Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/module.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/types.h>
+
+#include "sns_smgr.h"
+
+static const struct qmi_elem_info sns_smgr_all_sensor_info_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_all_sensor_info, id),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_all_sensor_info, id),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_all_sensor_info, type_len),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_all_sensor_info, type_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = SNS_SMGR_SENSOR_TYPE_MAX_LEN,
+ .elem_size = sizeof(char),
+ .array_type = VAR_LEN_ARRAY,
+ .offset = offsetof(struct sns_smgr_all_sensor_info, type),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
+const struct qmi_elem_info sns_smgr_all_sensor_info_resp_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_all_sensor_info_resp,
+ result),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset =
+ offsetof(struct sns_smgr_all_sensor_info_resp, result),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_all_sensor_info_resp,
+ item_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct sns_smgr_all_sensor_info_resp,
+ item_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = SNS_SMGR_ALL_SENSOR_INFO_MAX_LEN,
+ .elem_size = sizeof(struct sns_smgr_all_sensor_info),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct sns_smgr_all_sensor_info_resp, items),
+ .ei_array = sns_smgr_all_sensor_info_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+EXPORT_SYMBOL_GPL(sns_smgr_all_sensor_info_resp_ei);
+
+const struct qmi_elem_info sns_smgr_single_sensor_info_req_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_req, sensor_id),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_req,
+ sensor_id),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+EXPORT_SYMBOL_GPL(sns_smgr_single_sensor_info_req_ei);
+
+static const struct qmi_elem_info sns_smgr_single_sensor_info_data_type_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type,
+ sensor_id),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ sensor_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type,
+ data_type),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ data_type),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type, name_len),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ name_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 0xff,
+ .elem_size = sizeof(char),
+ .array_type = VAR_LEN_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ name),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type,
+ vendor_len),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ vendor_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 0xff,
+ .elem_size = sizeof(char),
+ .array_type = VAR_LEN_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ vendor),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type, val1),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ val1),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type,
+ max_sample_rate_hz),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ max_sample_rate_hz),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type, val2),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ val2),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type,
+ current_ua),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ current_ua),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type, range),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ range),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_data_type,
+ resolution),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_data_type,
+ resolution),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
+static const struct qmi_elem_info sns_smgr_single_sensor_info_native_sample_rates_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_native_sample_rates, rate_count),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_native_sample_rates,
+ rate_count),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = SNS_SMGR_NATIVE_SAMPLE_RATES_MAX_LEN,
+ .elem_size = sizeof(u16),
+ .array_type = VAR_LEN_ARRAY,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_native_sample_rates,
+ rates),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
+const struct qmi_elem_info sns_smgr_single_sensor_info_resp_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_resp, result),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ result),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_resp, data_type_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data_type_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = SNS_SMGR_DATA_TYPE_COUNT,
+ .elem_size =
+ sizeof(struct sns_smgr_single_sensor_info_data_type),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data_types),
+ .ei_array = sns_smgr_single_sensor_info_data_type_ei,
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_resp, data1_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data1_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = SNS_SMGR_DATA_TYPE_COUNT,
+ .elem_size = sizeof(u32),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data1),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_resp, data2),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data2),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_resp, data3_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data3_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = SNS_SMGR_DATA_TYPE_COUNT,
+ .elem_size = sizeof(u64),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data3),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_resp,
+ native_sample_rates_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ native_sample_rates_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = SNS_SMGR_DATA_TYPE_COUNT,
+ .elem_size = sizeof(
+ struct sns_smgr_single_sensor_info_native_sample_rates),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x13,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ native_sample_rates),
+ .ei_array = sns_smgr_single_sensor_info_native_sample_rates_ei,
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_single_sensor_info_resp, data5_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data5_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = SNS_SMGR_DATA_TYPE_COUNT,
+ .elem_size = sizeof(u32),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x14,
+ .offset = offsetof(struct sns_smgr_single_sensor_info_resp,
+ data5),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+EXPORT_SYMBOL_GPL(sns_smgr_single_sensor_info_resp_ei);
+
+static const struct qmi_elem_info sns_smgr_buffering_req_item_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req_item,
+ sensor_id),
+ .array_type = NO_ARRAY,
+ .offset =
+ offsetof(struct sns_smgr_buffering_req_item, sensor_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req_item,
+ data_type),
+ .array_type = NO_ARRAY,
+ .offset =
+ offsetof(struct sns_smgr_buffering_req_item, data_type),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req_item,
+ val1),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_req_item,
+ val1),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req_item,
+ sampling_rate),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_req_item,
+ sampling_rate),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req_item,
+ val2),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_req_item,
+ val2),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
+static const struct qmi_elem_info sns_smgr_buffering_req_notify_suspend_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_req_notify_suspend,
+ proc_type),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_req_notify_suspend,
+ proc_type),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_req_notify_suspend,
+ send_indications_during_suspend),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_req_notify_suspend,
+ send_indications_during_suspend),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
+const struct qmi_elem_info sns_smgr_buffering_req_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_buffering_req, report_id),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct sns_smgr_buffering_req, report_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_buffering_req, action),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct sns_smgr_buffering_req, action),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req,
+ report_rate),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct sns_smgr_buffering_req, report_rate),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_buffering_req, item_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x04,
+ .offset = offsetof(struct sns_smgr_buffering_req, item_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = SNS_SMGR_DATA_TYPE_COUNT,
+ .elem_size = sizeof(struct sns_smgr_buffering_req_item),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x04,
+ .offset = offsetof(struct sns_smgr_buffering_req, items),
+ .ei_array = sns_smgr_buffering_req_item_ei,
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req,
+ notify_suspend_valid),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct sns_smgr_buffering_req,
+ notify_suspend_valid),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_req,
+ notify_suspend),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset =
+ offsetof(struct sns_smgr_buffering_req, notify_suspend),
+ .ei_array = sns_smgr_buffering_req_notify_suspend_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+EXPORT_SYMBOL_GPL(sns_smgr_buffering_req_ei);
+
+const struct qmi_elem_info sns_smgr_buffering_resp_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_buffering_resp, result),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct sns_smgr_buffering_resp, result),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_buffering_resp, report_id),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct sns_smgr_buffering_resp, report_id),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_buffering_resp, ack_nak),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct sns_smgr_buffering_resp, ack_nak),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+EXPORT_SYMBOL_GPL(sns_smgr_buffering_resp_ei);
+
+static const struct qmi_elem_info sns_smgr_buffering_report_metadata_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_report_metadata, val1),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_report_metadata,
+ val1),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size =
+ sizeof_field(struct sns_smgr_buffering_report_metadata,
+ sample_count),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_report_metadata,
+ sample_count),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_report_metadata, timestamp),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_report_metadata,
+ timestamp),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_report_metadata, val2),
+ .array_type = NO_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_report_metadata,
+ val2),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
+static const struct qmi_elem_info sns_smgr_buffering_report_sample_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 3,
+ .elem_size = sizeof(u32),
+ .array_type = STATIC_ARRAY,
+ .offset = offsetof(struct sns_smgr_buffering_report_sample,
+ values),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_report_sample, val1),
+ .array_type = NO_ARRAY,
+ .offset =
+ offsetof(struct sns_smgr_buffering_report_sample, val1),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_report_sample, val2),
+ .array_type = NO_ARRAY,
+ .offset =
+ offsetof(struct sns_smgr_buffering_report_sample, val2),
+ },
+ {
+ .data_type = QMI_UNSIGNED_2_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(
+ struct sns_smgr_buffering_report_sample, val3),
+ .array_type = NO_ARRAY,
+ .offset =
+ offsetof(struct sns_smgr_buffering_report_sample, val3),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+
+const struct qmi_elem_info sns_smgr_buffering_report_ind_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_report_ind,
+ report_id),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct sns_smgr_buffering_report_ind,
+ report_id),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_report_ind,
+ metadata),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct sns_smgr_buffering_report_ind,
+ metadata),
+ .ei_array = sns_smgr_buffering_report_metadata_ei,
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_report_ind,
+ samples_len),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x03,
+ .offset = offsetof(struct sns_smgr_buffering_report_ind,
+ samples_len),
+ },
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = SNS_SMGR_SAMPLES_MAX_LEN,
+ .elem_size = sizeof(struct sns_smgr_buffering_report_sample),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x03,
+ .offset =
+ offsetof(struct sns_smgr_buffering_report_ind, samples),
+ .ei_array = sns_smgr_buffering_report_sample_ei,
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof_field(struct sns_smgr_buffering_report_ind,
+ val2),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct sns_smgr_buffering_report_ind, val2),
+ },
+ {
+ .data_type = QMI_EOTI,
+ },
+};
+EXPORT_SYMBOL_GPL(sns_smgr_buffering_report_ind_ei);
+
+static const char *smgr_sensor_type_names[SNS_SMGR_SENSOR_TYPE_COUNT] = {
+ [SNS_SMGR_SENSOR_TYPE_ACCEL] = "ACCEL",
+ [SNS_SMGR_SENSOR_TYPE_GYRO] = "GYRO",
+ [SNS_SMGR_SENSOR_TYPE_MAG] = "MAG",
+ [SNS_SMGR_SENSOR_TYPE_PROX_LIGHT] = "PROX_LIGHT",
+ [SNS_SMGR_SENSOR_TYPE_PRESSURE] = "PRESSURE",
+ [SNS_SMGR_SENSOR_TYPE_HALL_EFFECT] = "HALL_EFFECT"
+};
+
+enum qcom_smgr_sensor_type sns_smgr_sensor_type_from_str(const char *str)
+{
+ enum qcom_smgr_sensor_type i;
+
+ for (i = SNS_SMGR_SENSOR_TYPE_UNKNOWN + 1;
+ i < SNS_SMGR_SENSOR_TYPE_COUNT; i++)
+ if (!strcmp(str, smgr_sensor_type_names[i]))
+ return i;
+
+ return SNS_SMGR_SENSOR_TYPE_UNKNOWN;
+}
+EXPORT_SYMBOL_GPL(sns_smgr_sensor_type_from_str);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/qcom_smgr/qmi/sns_smgr.h b/drivers/iio/common/qcom_smgr/qmi/sns_smgr.h
new file mode 100644
index 000000000000..a741dfd87452
--- /dev/null
+++ b/drivers/iio/common/qcom_smgr/qmi/sns_smgr.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __SSC_SNS_SMGR_H__
+#define __SSC_SNS_SMGR_H__
+
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/soc/qcom/qmi.h>
+#include <linux/types.h>
+
+/*
+ * The structures of QMI messages used by the service were determined
+ * purely by watching transactions between proprietary Android userspace
+ * components and SSC. along with comparing values reported by Android APIs
+ * to values received in response messages. Due to that, the purpose or
+ * meaning of many fields remains unknown. Such fields are named "val*",
+ * "data*" or similar. Furthermore, the true maximum sizes of some messages
+ * with unknown array fields may be different than defined here.
+ */
+
+#define SNS_SMGR_QMI_SVC_ID 0x0100
+#define SNS_SMGR_QMI_SVC_V1 1
+#define SNS_SMGR_QMI_INS_ID 50
+
+#define SNS_SMGR_ALL_SENSOR_INFO_MSG_ID 0x05
+#define SNS_SMGR_SINGLE_SENSOR_INFO_MSG_ID 0x06
+#define SNS_SMGR_BUFFERING_MSG_ID 0x21
+#define SNS_SMGR_BUFFERING_REPORT_MSG_ID 0x22
+
+#define SNS_SMGR_ALL_SENSOR_INFO_REQ_MAX_LEN 0x0
+#define SNS_SMGR_ALL_SENSOR_INFO_RESP_MAX_LEN 0x3e
+#define SNS_SMGR_SINGLE_SENSOR_INFO_REQ_MAX_LEN 0x4
+#define SNS_SMGR_SINGLE_SENSOR_INFO_RESP_MAX_LEN 0x110
+#define SNS_SMGR_BUFFERING_REQ_MAX_LEN 0x30
+#define SNS_SMGR_BUFFERING_RESP_MAX_LEN 0x1e
+
+/* TODO: find actual maximums */
+#define SNS_SMGR_ALL_SENSOR_INFO_MAX_LEN 0x10
+#define SNS_SMGR_SENSOR_TYPE_MAX_LEN 0x10
+#define SNS_SMGR_NATIVE_SAMPLE_RATES_MAX_LEN 0x20
+#define SNS_SMGR_SAMPLES_MAX_LEN 0x100
+
+enum sns_smgr_buffering_action {
+ SNS_SMGR_BUFFERING_ACTION_ADD = 1,
+ SNS_SMGR_BUFFERING_ACTION_DELETE = 2,
+};
+
+struct sns_smgr_all_sensor_info {
+ u8 id;
+ u8 type_len;
+ char type[SNS_SMGR_SENSOR_TYPE_MAX_LEN];
+};
+
+struct sns_smgr_all_sensor_info_resp {
+ u16 result;
+ u8 item_len;
+ struct sns_smgr_all_sensor_info items[SNS_SMGR_ALL_SENSOR_INFO_MAX_LEN];
+};
+
+struct sns_smgr_single_sensor_info_req {
+ u8 sensor_id;
+};
+
+struct sns_smgr_single_sensor_info_data_type {
+ u8 sensor_id;
+ u8 data_type;
+ u8 name_len;
+ char name[0xff];
+ u8 vendor_len;
+ char vendor[0xff];
+ /*
+ * Might be separate u8 or u16 fields, but taken as a u32 it is seen
+ * to hold the value 1 for all sensors in dumps.
+ */
+ u32 val1;
+ u16 max_sample_rate_hz;
+ u16 val2;
+ u16 current_ua;
+ u32 range;
+ u32 resolution;
+};
+
+struct sns_smgr_single_sensor_info_native_sample_rates {
+ u8 rate_count;
+ u16 rates[SNS_SMGR_NATIVE_SAMPLE_RATES_MAX_LEN];
+};
+
+struct sns_smgr_single_sensor_info_resp {
+ u16 result;
+ u8 data_type_len;
+ struct sns_smgr_single_sensor_info_data_type data_types[SNS_SMGR_DATA_TYPE_COUNT];
+ u8 data1_len;
+ u32 data1[SNS_SMGR_DATA_TYPE_COUNT];
+ u32 data2;
+ u8 data3_len;
+ u64 data3[SNS_SMGR_DATA_TYPE_COUNT];
+ u8 native_sample_rates_len;
+ struct sns_smgr_single_sensor_info_native_sample_rates
+ native_sample_rates[SNS_SMGR_DATA_TYPE_COUNT];
+ u8 data5_len;
+ u32 data5[SNS_SMGR_DATA_TYPE_COUNT];
+};
+
+struct sns_smgr_buffering_req_item {
+ u8 sensor_id;
+ u8 data_type;
+ u16 val1;
+ u16 sampling_rate;
+ u16 val2;
+};
+
+struct sns_smgr_buffering_req_notify_suspend {
+ u16 proc_type;
+ u16 send_indications_during_suspend;
+};
+
+struct sns_smgr_buffering_req {
+ u8 report_id;
+ u8 action;
+ u32 report_rate;
+ u8 item_len;
+ struct sns_smgr_buffering_req_item items[SNS_SMGR_DATA_TYPE_COUNT];
+ u8 notify_suspend_valid;
+ struct sns_smgr_buffering_req_notify_suspend notify_suspend;
+};
+
+struct sns_smgr_buffering_resp {
+ u16 result;
+ u8 report_id;
+ u8 ack_nak;
+};
+
+struct sns_smgr_buffering_report_metadata {
+ u32 val1;
+ u8 sample_count;
+ u32 timestamp;
+ u32 val2;
+};
+
+struct sns_smgr_buffering_report_sample {
+ u32 values[3];
+ u8 val1;
+ u8 val2;
+ u16 val3;
+};
+
+struct sns_smgr_buffering_report_ind {
+ u8 report_id;
+ struct sns_smgr_buffering_report_metadata metadata;
+ u8 samples_len;
+ struct sns_smgr_buffering_report_sample samples[SNS_SMGR_SAMPLES_MAX_LEN];
+ u8 val2;
+};
+
+extern const struct qmi_elem_info sns_smgr_all_sensor_info_resp_ei[];
+extern const struct qmi_elem_info sns_smgr_single_sensor_info_req_ei[];
+extern const struct qmi_elem_info sns_smgr_single_sensor_info_resp_ei[];
+extern const struct qmi_elem_info sns_smgr_buffering_req_ei[];
+extern const struct qmi_elem_info sns_smgr_buffering_resp_ei[];
+extern const struct qmi_elem_info sns_smgr_buffering_report_ind_ei[];
+
+extern enum qcom_smgr_sensor_type sns_smgr_sensor_type_from_str(const char *str);
+
+#endif /* __SSC_SNS_SMGR_H__ */
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 3e193ee0fb61..b9ee2c3b5bc5 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -147,6 +147,16 @@ config IIO_ST_GYRO_3AXIS
Also need to enable at least one of I2C and SPI interface drivers
below.

+config IIO_QCOM_SMGR_GYRO
+ tristate "Qualcomm SSC Sensor Manager Gyroscope Sensor"
+ depends on IIO_QCOM_SMGR
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to get support for gyroscopes connected to
+ a Qualcomm Snapdragon Sensor Core and accessed through its
+ Sensor Manager service.
+
config IIO_ST_GYRO_I2C_3AXIS
tristate "STMicroelectronics gyroscopes 3-Axis I2C Interface"
depends on I2C && IIO_ST_GYRO_3AXIS
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 0319b397dc3f..63ff40acdedc 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -28,6 +28,8 @@ itg3200-y := itg3200_core.o
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
obj-$(CONFIG_ITG3200) += itg3200.o

+obj-$(CONFIG_IIO_QCOM_SMGR_ACCEL) += qcom_smgr_gyro.o
+
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_gyro_sensor.o

obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o
diff --git a/drivers/iio/gyro/qcom_smgr_gyro.c b/drivers/iio/gyro/qcom_smgr_gyro.c
new file mode 100644
index 000000000000..4cb55c6fe9e8
--- /dev/null
+++ b/drivers/iio/gyro/qcom_smgr_gyro.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm Sensor Manager gyroscope driver
+ *
+ * Copyright (c) 2022, Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+
+static const struct iio_chan_spec qcom_smgr_gyro_iio_channels[] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = true,
+ .channel2 = IIO_MOD_X,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = true,
+ .channel2 = IIO_MOD_Y,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = true,
+ .channel2 = IIO_MOD_Z,
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_TIMESTAMP,
+ .channel = -1,
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 64,
+ .endianness = IIO_LE,
+ },
+ },
+};
+
+static int qcom_smgr_gyro_probe(struct platform_device *pdev)
+{
+ struct iio_dev *iio_dev;
+ struct qcom_smgr_iio_priv *priv;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(iio_dev);
+ priv->sensor = *(struct qcom_smgr_sensor **)pdev->dev.platform_data;
+ priv->sensor->iio_dev = iio_dev;
+
+ iio_dev->name = "qcom-smgr-gyro";
+ iio_dev->info = &qcom_smgr_iio_info;
+ iio_dev->channels = qcom_smgr_gyro_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(qcom_smgr_gyro_iio_channels);
+
+ ret = devm_iio_kfifo_buffer_setup(&pdev->dev, iio_dev,
+ &qcom_smgr_buffer_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to setup buffer: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register IIO device: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv->sensor);
+
+ return 0;
+}
+
+static void qcom_smgr_gyro_remove(struct platform_device *pdev)
+{
+ struct qcom_smgr_sensor *sensor = platform_get_drvdata(pdev);
+
+ sensor->iio_dev = NULL;
+}
+
+static const struct platform_device_id qcom_smgr_gyro_ids[] = {
+ { .name = "qcom-smgr-gyro" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, qcom_smgr_gyro_ids);
+
+static struct platform_driver qcom_smgr_gyro_driver = {
+ .probe = qcom_smgr_gyro_probe,
+ .remove = qcom_smgr_gyro_remove,
+ .driver = {
+ .name = "qcom_smgr_gyro",
+ },
+ .id_table = qcom_smgr_gyro_ids,
+};
+module_platform_driver(qcom_smgr_gyro_driver);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Qualcomm Sensor Manager gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 3debf1320ad1..917c123f08bb 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -139,6 +139,15 @@ config MMC35240
To compile this driver as a module, choose M here: the module
will be called mmc35240.

+config IIO_QCOM_SMGR_MAG
+ tristate "Qualcomm SSC Sensor Manager Magnetometer"
+ depends on IIO_QCOM_SMGR
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to get support for magnetometers connected to a Qualcomm
+ Snapdragon Sensor Core and accessed through its Sensor Manager service.
+
config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 9297723a97d8..7576c226239b 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -16,6 +16,8 @@ obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
obj-$(CONFIG_MMC35240) += mmc35240.o

+obj-$(CONFIG_IIO_QCOM_SMGR_MAG) += qcom_smgr_mag.o
+
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
st_magn-y := st_magn_core.o
st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
diff --git a/drivers/iio/magnetometer/qcom_smgr_mag.c b/drivers/iio/magnetometer/qcom_smgr_mag.c
new file mode 100644
index 000000000000..dc197db509bc
--- /dev/null
+++ b/drivers/iio/magnetometer/qcom_smgr_mag.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm Sensor Manager magnetometer driver
+ *
+ * Copyright (c) 2022, Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+
+static const struct iio_chan_spec qcom_smgr_mag_iio_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = true,
+ .channel2 = IIO_MOD_X,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_MAGN,
+ .modified = true,
+ .channel2 = IIO_MOD_Y,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_MAGN,
+ .modified = true,
+ .channel2 = IIO_MOD_Z,
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .ext_info = qcom_smgr_iio_ext_info
+ },
+ {
+ .type = IIO_TIMESTAMP,
+ .channel = -1,
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 64,
+ .endianness = IIO_LE,
+ },
+ },
+};
+
+static int qcom_smgr_mag_probe(struct platform_device *pdev)
+{
+ struct iio_dev *iio_dev;
+ struct qcom_smgr_iio_priv *priv;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(iio_dev);
+ priv->sensor = *(struct qcom_smgr_sensor **)pdev->dev.platform_data;
+ priv->sensor->iio_dev = iio_dev;
+
+ iio_dev->name = "qcom-smgr-mag";
+ iio_dev->info = &qcom_smgr_iio_info;
+ iio_dev->channels = qcom_smgr_mag_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(qcom_smgr_mag_iio_channels);
+
+ ret = devm_iio_kfifo_buffer_setup(&pdev->dev, iio_dev,
+ &qcom_smgr_buffer_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to setup buffer: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register IIO device: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv->sensor);
+
+ return 0;
+}
+
+static void qcom_smgr_mag_remove(struct platform_device *pdev)
+{
+ struct qcom_smgr_sensor *sensor = platform_get_drvdata(pdev);
+
+ sensor->iio_dev = NULL;
+}
+
+static const struct platform_device_id qcom_smgr_mag_ids[] = {
+ { .name = "qcom-smgr-mag" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, qcom_smgr_mag_ids);
+
+static struct platform_driver qcom_smgr_mag_driver = {
+ .probe = qcom_smgr_mag_probe,
+ .remove = qcom_smgr_mag_remove,
+ .driver = {
+ .name = "qcom_smgr_mag",
+ },
+ .id_table = qcom_smgr_mag_ids,
+};
+module_platform_driver(qcom_smgr_mag_driver);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Qualcomm Sensor Manager magnetometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index d2cb8c871f6a..ca1053472969 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -67,6 +67,16 @@ config IIO_CROS_EC_BARO
To compile this driver as a module, choose M here: the module
will be called cros_ec_baro.

+config IIO_QCOM_SMGR_PRESSURE
+ tristate "Qualcomm SSC Sensor Manager Pressure Sensor"
+ depends on IIO_QCOM_SMGR
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to get support for pressure sensors connected to
+ a Qualcomm Snapdragon Sensor Core and accessed through its Sensor
+ Manager service.
+
config DLHL60D
tristate "All Sensors DLHL60D and DLHL60G low voltage digital pressure sensors"
depends on I2C
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index 6482288e07ee..0e4a1fbef7b8 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
obj-$(CONFIG_DLHL60D) += dlhl60d.o
obj-$(CONFIG_DPS310) += dps310.o
obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
+obj-$(CONFIG_IIO_QCOM_SMGR_PROX) += qcom_smgr_pressure.o
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
obj-$(CONFIG_HP03) += hp03.o
obj-$(CONFIG_HSC030PA) += hsc030pa.o
diff --git a/drivers/iio/pressure/qcom_smgr_pressure.c b/drivers/iio/pressure/qcom_smgr_pressure.c
new file mode 100644
index 000000000000..66e165ad5c9a
--- /dev/null
+++ b/drivers/iio/pressure/qcom_smgr_pressure.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm Sensor Manager pressure sensor driver
+ *
+ * Copyright (c) 2022, Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+
+static const struct iio_chan_spec qcom_smgr_pressure_iio_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ },
+ {
+ .type = IIO_TIMESTAMP,
+ .channel = -1,
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 64,
+ .endianness = IIO_LE,
+ },
+ },
+};
+
+static int qcom_smgr_pressure_probe(struct platform_device *pdev)
+{
+ struct iio_dev *iio_dev;
+ struct qcom_smgr_iio_priv *priv;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(iio_dev);
+ priv->sensor = *(struct qcom_smgr_sensor **)pdev->dev.platform_data;
+ priv->sensor->iio_dev = iio_dev;
+
+ iio_dev->name = "qcom-smgr-pressure";
+ iio_dev->info = &qcom_smgr_iio_info;
+ iio_dev->channels = qcom_smgr_pressure_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(qcom_smgr_pressure_iio_channels);
+
+ ret = devm_iio_kfifo_buffer_setup(&pdev->dev, iio_dev,
+ &qcom_smgr_buffer_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to setup buffer: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register IIO device: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv->sensor);
+
+ return 0;
+}
+
+static void qcom_smgr_pressure_remove(struct platform_device *pdev)
+{
+ struct qcom_smgr_sensor *sensor = platform_get_drvdata(pdev);
+
+ sensor->iio_dev = NULL;
+}
+
+static const struct platform_device_id qcom_smgr_pressure_ids[] = {
+ { .name = "qcom-smgr-pressure" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, qcom_smgr_pressure_ids);
+
+static struct platform_driver qcom_smgr_pressure_driver = {
+ .probe = qcom_smgr_pressure_probe,
+ .remove = qcom_smgr_pressure_remove,
+ .driver = {
+ .name = "qcom_smgr_pressure",
+ },
+ .id_table = qcom_smgr_pressure_ids,
+};
+module_platform_driver(qcom_smgr_pressure_driver);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Qualcomm Sensor Manager pressure sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
index a562a78b7d0d..486514e54cc1 100644
--- a/drivers/iio/proximity/Kconfig
+++ b/drivers/iio/proximity/Kconfig
@@ -46,6 +46,16 @@ config HX9023S
To compile this driver as a module, choose M here: the
module will be called hx9023s.

+config IIO_QCOM_SMGR_PROX
+ tristate "Qualcomm SSC Sensor Manager Proximity Sensor"
+ depends on IIO_QCOM_SMGR
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to get support for proximity sensors connected to
+ a Qualcomm Snapdragon Sensor Core and accessed through its Sensor
+ Manager service.
+
config IRSD200
tristate "Murata IRS-D200 PIR sensor"
select IIO_BUFFER
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
index c5e76995764a..c3dab31e1fc1 100644
--- a/drivers/iio/proximity/Makefile
+++ b/drivers/iio/proximity/Makefile
@@ -7,6 +7,7 @@
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o
obj-$(CONFIG_HX9023S) += hx9023s.o
+obj-$(CONFIG_IIO_QCOM_SMGR_PROX) += qcom_smgr_prox.o
obj-$(CONFIG_IRSD200) += irsd200.o
obj-$(CONFIG_ISL29501) += isl29501.o
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
diff --git a/drivers/iio/proximity/qcom_smgr_prox.c b/drivers/iio/proximity/qcom_smgr_prox.c
new file mode 100644
index 000000000000..2900fb7b7a20
--- /dev/null
+++ b/drivers/iio/proximity/qcom_smgr_prox.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Qualcomm Sensor Manager proximity sensor driver
+ *
+ * Copyright (c) 2022, Yassine Oudjana <y.oudjana@protonmail.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/qcom_smgr.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+
+static const struct iio_chan_spec qcom_smgr_prox_iio_channels[] = {
+ {
+ .type = IIO_PROXIMITY,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ)
+ },
+ {
+ .type = IIO_TIMESTAMP,
+ .channel = -1,
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 64,
+ .endianness = IIO_LE,
+ },
+ },
+};
+
+static int qcom_smgr_prox_probe(struct platform_device *pdev)
+{
+ struct iio_dev *iio_dev;
+ struct qcom_smgr_iio_priv *priv;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(iio_dev);
+ priv->sensor = *(struct qcom_smgr_sensor **)pdev->dev.platform_data;
+ priv->sensor->iio_dev = iio_dev;
+
+ iio_dev->name = "qcom-smgr-prox";
+ iio_dev->info = &qcom_smgr_iio_info;
+ iio_dev->channels = qcom_smgr_prox_iio_channels;
+ iio_dev->num_channels = ARRAY_SIZE(qcom_smgr_prox_iio_channels);
+
+ ret = devm_iio_kfifo_buffer_setup(&pdev->dev, iio_dev,
+ &qcom_smgr_buffer_ops);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to setup buffer: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&pdev->dev, iio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register IIO device: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv->sensor);
+
+ return 0;
+}
+
+static void qcom_smgr_prox_remove(struct platform_device *pdev)
+{
+ struct qcom_smgr_sensor *sensor = platform_get_drvdata(pdev);
+
+ sensor->iio_dev = NULL;
+}
+
+static const struct platform_device_id qcom_smgr_prox_ids[] = {
+ { .name = "qcom-smgr-prox-light" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, qcom_smgr_prox_ids);
+
+static struct platform_driver qcom_smgr_prox_driver = {
+ .probe = qcom_smgr_prox_probe,
+ .remove = qcom_smgr_prox_remove,
+ .driver = {
+ .name = "qcom_smgr_prox",
+ },
+ .id_table = qcom_smgr_prox_ids,
+};
+module_platform_driver(qcom_smgr_prox_driver);
+
+MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
+MODULE_DESCRIPTION("Qualcomm Sensor Manager proximity sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/iio/common/qcom_smgr.h b/include/linux/iio/common/qcom_smgr.h
new file mode 100644
index 000000000000..756cb83e26ec
--- /dev/null
+++ b/include/linux/iio/common/qcom_smgr.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __QCOM_SMGR_H__
+#define __QCOM_SMGR_H__
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/types.h>
+
+enum qcom_smgr_sensor_type {
+ SNS_SMGR_SENSOR_TYPE_UNKNOWN,
+ SNS_SMGR_SENSOR_TYPE_ACCEL,
+ SNS_SMGR_SENSOR_TYPE_GYRO,
+ SNS_SMGR_SENSOR_TYPE_MAG,
+ SNS_SMGR_SENSOR_TYPE_PROX_LIGHT,
+ SNS_SMGR_SENSOR_TYPE_PRESSURE,
+ SNS_SMGR_SENSOR_TYPE_HALL_EFFECT,
+
+ SNS_SMGR_SENSOR_TYPE_COUNT
+};
+
+enum qcom_smgr_data_type {
+ SNS_SMGR_DATA_TYPE_PRIMARY,
+ SNS_SMGR_DATA_TYPE_SECONDARY,
+
+ SNS_SMGR_DATA_TYPE_COUNT
+};
+
+struct qcom_smgr_data_type_item {
+ const char *name;
+ const char *vendor;
+
+ u32 range;
+
+ size_t native_sample_rate_count;
+ u16 *native_sample_rates;
+
+ u16 max_sample_rate;
+ u16 cur_sample_rate;
+};
+
+struct qcom_smgr_sensor {
+ u8 id;
+ enum qcom_smgr_sensor_type type;
+
+ u8 data_type_count;
+ /*
+ * Only SNS_SMGR_DATA_TYPE_PRIMARY is used at the moment, but we store
+ * SNS_SMGR_DATA_TYPE_SECONDARY when available as well for future use.
+ */
+ struct qcom_smgr_data_type_item *data_types;
+
+ struct iio_dev *iio_dev;
+};
+
+struct qcom_smgr_iio_priv {
+ struct qcom_smgr_sensor *sensor;
+};
+
+extern const struct iio_buffer_setup_ops qcom_smgr_buffer_ops;
+extern const struct iio_info qcom_smgr_iio_info;
+extern const struct iio_chan_spec_ext_info qcom_smgr_iio_ext_info[];
+
+#endif /* __QCOM_SMGR_H__ */
--
2.49.0