Skip to content

Commit

Permalink
WIP: iio: accel: Add driver for Qualcomm Sensor Manager accelerometers
Browse files Browse the repository at this point in the history
Add a driver for accelerometers provided by the Qualcomm Sensor Manager service.
  • Loading branch information
Tooniis authored and minlexx committed Dec 24, 2024
1 parent a9bebb4 commit e0d842a
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 0 deletions.
10 changes: 10 additions & 0 deletions drivers/iio/accel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,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
Expand Down
2 changes: 2 additions & 0 deletions drivers/iio/accel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ obj-$(CONFIG_STK8BA50) += stk8ba50.o

obj-$(CONFIG_IIO_CROS_EC_ACCEL_LEGACY) += cros_ec_accel_legacy.o

obj-$(CONFIG_IIO_QCOM_SMGR_ACCEL) += smgr_accel.o

obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o

obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
Expand Down
234 changes: 234 additions & 0 deletions drivers/iio/accel/smgr_accel.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// 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 int smgr_accel_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct 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:
/*
* TODO: Find out if the scale is standard across devices or
* find a way to get the correct scale for a device
*
* Device reports around 640000 when axis is aligned with
* gravity, therefore scale is 9.81m/s^2 / 640000.
*/
*val = 0;
*val2 = 15328; /* scale * 10^9 */
return IIO_VAL_INT_PLUS_NANO;
}

return -EINVAL;
}

static int smgr_accel_write_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct 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 smgr_accel_read_avail(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct smgr_iio_priv *priv = iio_priv(iio_dev);
const int samp_freq_vals[3] = {
1, 1, priv->sensor->data_types[0].max_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;
}

static const struct iio_info smgr_accel_iio_info = {
.read_raw = smgr_accel_read_raw,
.write_raw = smgr_accel_write_raw,
.read_avail = smgr_accel_read_avail,
};

/* TODO: Get mount matrix from SSC or read it from the device tree */
static const struct iio_mount_matrix qcom_ssc_mount_matrix = {
.rotation = {
"0", "-1", "0",
"-1", "0", "0",
"0", "0", "1"
}
};

static const struct iio_mount_matrix *
smgr_accel_get_mount_matrix(const struct iio_dev *iio_dev,
const struct iio_chan_spec *chan)
{
return &qcom_ssc_mount_matrix;
}

static const struct iio_chan_spec_ext_info smgr_accel_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, smgr_accel_get_mount_matrix),
{}
};

static const struct iio_chan_spec smgr_accel_iio_channels[] = {
{
.type = IIO_ACCEL,
.modified = true,
.channel2 = IIO_MOD_X,
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_LE,
},
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.ext_info = smgr_accel_ext_info
},
{
.type = IIO_ACCEL,
.modified = true,
.channel2 = IIO_MOD_Y,
.scan_index = 1,
.scan_type = {
.sign = 's',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_LE,
},
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.ext_info = smgr_accel_ext_info
},
{
.type = IIO_ACCEL,
.modified = true,
.channel2 = IIO_MOD_Z,
.scan_index = 2,
.scan_type = {
.sign = 's',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_LE,
},
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.ext_info = smgr_accel_ext_info
},
{
.type = IIO_TIMESTAMP,
.channel = -1,
.scan_index = 3,
.scan_type = {
.sign = 'u',
.realbits = 32,
.storagebits = 64,
.endianness = IIO_LE,
},
},
};

static int smgr_accel_probe(struct platform_device *pdev)
{
struct iio_dev *iio_dev;
struct 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 smgr_sensor **)pdev->dev.platform_data;
priv->sensor->iio_dev = iio_dev;

iio_dev->name = "qcom-smgr-accel";
iio_dev->info = &smgr_accel_iio_info;
iio_dev->channels = smgr_accel_iio_channels;
iio_dev->num_channels = ARRAY_SIZE(smgr_accel_iio_channels);

ret = devm_iio_kfifo_buffer_setup(&pdev->dev, iio_dev,
&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;
}

return 0;
}

static void smgr_accel_remove(struct platform_device *pdev)
{
struct iio_dev *iio_dev = platform_get_drvdata(pdev);
struct smgr_iio_priv *priv = iio_priv(iio_dev);

priv->sensor->iio_dev = NULL;
}

static const struct platform_device_id smgr_accel_ids[] = {
{ .name = "qcom-smgr-accel" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(platform, smgr_accel_ids);

static struct platform_driver smgr_accel_driver = {
.probe = smgr_accel_probe,
.remove = smgr_accel_remove,
.driver = {
.name = "smgr_accel",
},
.id_table = smgr_accel_ids,
};
module_platform_driver(smgr_accel_driver);

MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
MODULE_DESCRIPTION("Qualcomm Sensor Manager accelerometer driver");
MODULE_LICENSE("GPL");

0 comments on commit e0d842a

Please sign in to comment.