mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
Kernel version definitions are no longer used in the BMI088 driver and so remove the version.h header as it is no longer needed. Bug 4190630 Change-Id: Id0e3052abbf85034c9f4f66f21cc9842eff3658c Signed-off-by: Jon Hunter <jonathanh@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3330357 Reviewed-by: svcacv <svcacv@nvidia.com> GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com> Reviewed-by: Blake McHale <bmchale@nvidia.com>
604 lines
14 KiB
C
604 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
// SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/of.h>
|
|
#include <linux/string.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/kfifo_buf.h>
|
|
#include "bmi_iio.h"
|
|
|
|
enum BMI_ATTR {
|
|
BMI_ATTR_PART,
|
|
BMI_ATTR_MATRIX,
|
|
BMI_ATTR_DEV_STATE,
|
|
BMI_ATTR_DEV_ERR,
|
|
BMI_ATTR_DUMP_REGS,
|
|
};
|
|
|
|
static ssize_t bmi_iio_attr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf);
|
|
static ssize_t bmi_iio_attr_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count);
|
|
|
|
static IIO_DEVICE_ATTR(part, 0444,
|
|
bmi_iio_attr_show, NULL, BMI_ATTR_PART);
|
|
static IIO_DEVICE_ATTR(dev_state, 0444,
|
|
bmi_iio_attr_show, NULL, BMI_ATTR_DEV_STATE);
|
|
static IIO_DEVICE_ATTR(dev_err, 0444,
|
|
bmi_iio_attr_show, NULL, BMI_ATTR_DEV_ERR);
|
|
static IIO_DEVICE_ATTR(dump_regs, 0444,
|
|
bmi_iio_attr_show, NULL, BMI_ATTR_DUMP_REGS);
|
|
|
|
static IIO_DEVICE_ATTR(mount_matrix, 0444,
|
|
bmi_iio_attr_show, bmi_iio_attr_store, BMI_ATTR_MATRIX);
|
|
static
|
|
IIO_CONST_ATTR(in_accel_sampling_frequency_available,
|
|
"12.5 25 50 100 200 400 800 1600");
|
|
static
|
|
IIO_CONST_ATTR(in_anglvel_sampling_frequency_available,
|
|
"100 200 400 1000 2000");
|
|
|
|
static
|
|
IIO_CONST_ATTR(in_accel_scale_available,
|
|
"0.000897 0.001795 0.003591 0.007182");
|
|
static
|
|
IIO_CONST_ATTR(in_anglvel_scale_available,
|
|
"0.001065 0.000532 0.000266 0.000133 0.000066");
|
|
|
|
static struct attribute *bmi_iio_attrs[] = {
|
|
&iio_dev_attr_part.dev_attr.attr,
|
|
&iio_dev_attr_mount_matrix.dev_attr.attr,
|
|
&iio_dev_attr_dev_state.dev_attr.attr,
|
|
&iio_dev_attr_dev_err.dev_attr.attr,
|
|
&iio_dev_attr_dump_regs.dev_attr.attr,
|
|
};
|
|
|
|
static struct attribute *bmi_iio_accel_attrs[] = {
|
|
&iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr,
|
|
&iio_const_attr_in_accel_scale_available.dev_attr.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute *bmi_iio_gyro_attrs[] = {
|
|
&iio_const_attr_in_anglvel_sampling_frequency_available.dev_attr.attr,
|
|
&iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
|
|
NULL
|
|
};
|
|
|
|
/* array size is same for accl and gyro */
|
|
#define ATTR_SZ_DEVICE ARRAY_SIZE(bmi_iio_gyro_attrs)
|
|
|
|
enum bmi088_accel_scan_axis {
|
|
BMI088_SCAN_ACCEL_X,
|
|
BMI088_SCAN_ACCEL_Y,
|
|
BMI088_SCAN_ACCEL_Z,
|
|
BMI088_SCAN_ACCEL_TS,
|
|
};
|
|
|
|
enum bmi088_gyro_scan_axis {
|
|
BMI088_SCAN_GYRO_X,
|
|
BMI088_SCAN_GYRO_Y,
|
|
BMI088_SCAN_GYRO_Z,
|
|
BMI088_SCAN_GYRO_TS,
|
|
};
|
|
|
|
#define NUM_CHANNELS 3
|
|
|
|
#define BMI088_CHANNEL(_type, _axis, _index) { \
|
|
.type = _type, \
|
|
.modified = 1, \
|
|
.channel2 = IIO_MOD_##_axis, \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
|
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
|
.scan_index = _index, \
|
|
.scan_type = { \
|
|
.sign = 's', \
|
|
.realbits = 16, \
|
|
.storagebits = 16, \
|
|
.endianness = IIO_LE, \
|
|
}, \
|
|
}
|
|
|
|
#define IIO_CHAN_HW_TIMESTAMP(_si) { \
|
|
.type = IIO_TIMESTAMP, \
|
|
.channel = -1, \
|
|
.scan_index = _si, \
|
|
.scan_type = { \
|
|
.sign = 'u', \
|
|
.realbits = 64, \
|
|
.storagebits = 64, \
|
|
}, \
|
|
}
|
|
|
|
static const struct iio_chan_spec bmi088_acc_channels[] = {
|
|
BMI088_CHANNEL(IIO_ACCEL, X, BMI088_SCAN_ACCEL_X),
|
|
BMI088_CHANNEL(IIO_ACCEL, Y, BMI088_SCAN_ACCEL_Y),
|
|
BMI088_CHANNEL(IIO_ACCEL, Z, BMI088_SCAN_ACCEL_Z),
|
|
IIO_CHAN_HW_TIMESTAMP(BMI088_SCAN_ACCEL_TS),
|
|
};
|
|
|
|
static const struct iio_chan_spec bmi088_gyro_channels[] = {
|
|
BMI088_CHANNEL(IIO_ANGL_VEL, X, BMI088_SCAN_GYRO_X),
|
|
BMI088_CHANNEL(IIO_ANGL_VEL, Y, BMI088_SCAN_GYRO_Y),
|
|
BMI088_CHANNEL(IIO_ANGL_VEL, Z, BMI088_SCAN_GYRO_Z),
|
|
IIO_CHAN_HW_TIMESTAMP(BMI088_SCAN_GYRO_TS),
|
|
};
|
|
|
|
struct bmi_iio_state {
|
|
void *client;
|
|
struct device *dev;
|
|
struct iio_fn_dev *fn_dev;
|
|
struct sensor_cfg *cfg;
|
|
struct iio_chan_spec *chs;
|
|
struct attribute *attrs[ARRAY_SIZE(bmi_iio_attrs) + ATTR_SZ_DEVICE];
|
|
struct attribute_group attr_group;
|
|
struct iio_info info;
|
|
u64 ts;
|
|
|
|
struct {
|
|
s16 chan[NUM_CHANNELS];
|
|
u64 timestamp __aligned(8);
|
|
} data;
|
|
};
|
|
|
|
int bmi_iio_push_buf(struct iio_dev *indio_dev, unsigned char *data, u64 ts)
|
|
{
|
|
struct bmi_iio_state *st;
|
|
int ret = 0;
|
|
int bit;
|
|
__le16 *sample = (__le16 *)data;
|
|
|
|
if (!indio_dev || !data)
|
|
return -EINVAL;
|
|
|
|
if (!indio_dev->active_scan_mask)
|
|
return -EINVAL;
|
|
|
|
st = iio_priv(indio_dev);
|
|
if (!st)
|
|
return -EINVAL;
|
|
|
|
memset(&st->data, 0, sizeof(st->data));
|
|
|
|
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
|
indio_dev->masklength) {
|
|
st->data.chan[bit] = sample[bit];
|
|
}
|
|
|
|
if (iio_buffer_enabled(indio_dev)) {
|
|
ret = iio_push_to_buffers_with_timestamp(indio_dev, &st->data, ts);
|
|
if (!ret) {
|
|
if (ts)
|
|
st->ts = ts;
|
|
}
|
|
}
|
|
|
|
if (ts) {
|
|
if (unlikely((ts - st->ts) < 0)) {
|
|
dev_err(st->dev, "%s ts_diff=%lld\n",
|
|
st->cfg->name, ts - st->ts);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(bmi_iio_push_buf);
|
|
|
|
static int bmi_iio_enable(struct iio_dev *indio_dev, bool en)
|
|
{
|
|
struct bmi_iio_state *st = iio_priv(indio_dev);
|
|
int enable = 0;
|
|
int bit;
|
|
|
|
if (!en)
|
|
return st->fn_dev->enable(st->client, st->cfg->snsr_id, 0);
|
|
|
|
if (indio_dev->num_channels > 1) {
|
|
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
|
indio_dev->masklength)
|
|
enable |= 1 << bit;
|
|
} else {
|
|
enable = 1;
|
|
}
|
|
|
|
|
|
return st->fn_dev->enable(st->client, st->cfg->snsr_id, enable);
|
|
}
|
|
|
|
static ssize_t bmi_iio_attr_store(struct device *dev,
|
|
struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
struct bmi_iio_state *st;
|
|
char *str;
|
|
s8 matrix[9];
|
|
unsigned int new;
|
|
int ret;
|
|
|
|
if (!indio_dev)
|
|
return -EINVAL;
|
|
|
|
st = iio_priv(indio_dev);
|
|
|
|
if (!st || !this_attr)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(BMI_MUTEX(indio_dev));
|
|
|
|
if (*st->fn_dev->sts & (BMI_STS_SHUTDOWN | BMI_STS_SUSPEND)) {
|
|
mutex_unlock(BMI_MUTEX(indio_dev));
|
|
return -EPERM;
|
|
}
|
|
|
|
switch (this_attr->address) {
|
|
case BMI_ATTR_MATRIX:
|
|
for (new = 0; new < sizeof(matrix); new++) {
|
|
str = strsep((char **)&buf, " \0");
|
|
if (str != NULL) {
|
|
ret = kstrtos8(str, 10, &matrix[new]);
|
|
if (ret)
|
|
break;
|
|
|
|
if (matrix[new] < -1 || matrix[new] > 1) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
} else {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (new == sizeof(matrix))
|
|
memcpy(st->cfg->matrix, matrix,
|
|
sizeof(st->cfg->matrix));
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
mutex_unlock(BMI_MUTEX(indio_dev));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t bmi_iio_attr_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
|
struct bmi_iio_state *st;
|
|
ssize_t t = 0;
|
|
unsigned int i;
|
|
|
|
if (!indio_dev)
|
|
return -EINVAL;
|
|
|
|
st = iio_priv(indio_dev);
|
|
|
|
if (!st || !this_attr)
|
|
return -EINVAL;
|
|
|
|
switch (this_attr->address) {
|
|
case BMI_ATTR_PART:
|
|
return snprintf(buf, PAGE_SIZE, "%s %s\n",
|
|
st->cfg->part, st->cfg->name);
|
|
|
|
case BMI_ATTR_MATRIX:
|
|
mutex_lock(BMI_MUTEX(indio_dev));
|
|
for (i = 0; i < 8; i++)
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "%hhd,",
|
|
st->cfg->matrix[i]);
|
|
t += snprintf(buf + t, PAGE_SIZE - t, "%hhd\n",
|
|
st->cfg->matrix[i]);
|
|
mutex_unlock(BMI_MUTEX(indio_dev));
|
|
|
|
return t;
|
|
|
|
case BMI_ATTR_DEV_ERR:
|
|
if (!st->fn_dev->read_err)
|
|
return -EINVAL;
|
|
|
|
return st->fn_dev->read_err(st->client, st->cfg->snsr_id, buf);
|
|
|
|
case BMI_ATTR_DEV_STATE:
|
|
return snprintf(buf, PAGE_SIZE, "dev state=%u\n",
|
|
*st->fn_dev->sts);
|
|
|
|
case BMI_ATTR_DUMP_REGS:
|
|
if (!st->fn_dev->regs)
|
|
return -EINVAL;
|
|
|
|
return st->fn_dev->regs(st->client, st->cfg->snsr_id, buf);
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int bmi_iio_attr(struct iio_dev *indio_dev, const char *name)
|
|
{
|
|
struct bmi_iio_state *st;
|
|
|
|
if (!indio_dev || !name)
|
|
return 0;
|
|
|
|
st = iio_priv(indio_dev);
|
|
|
|
memcpy(st->attrs, bmi_iio_attrs, sizeof(bmi_iio_attrs));
|
|
if (!strcmp(st->cfg->name, "accelerometer"))
|
|
memcpy(&st->attrs[ARRAY_SIZE(bmi_iio_attrs)],
|
|
bmi_iio_accel_attrs, sizeof(bmi_iio_accel_attrs));
|
|
else if (!strcmp(st->cfg->name, "gyroscope"))
|
|
memcpy(&st->attrs[ARRAY_SIZE(bmi_iio_attrs)],
|
|
bmi_iio_gyro_attrs, sizeof(bmi_iio_gyro_attrs));
|
|
else
|
|
return 0;
|
|
|
|
st->attr_group.attrs = st->attrs;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int bmi_iio_check_enable(struct bmi_iio_state *st)
|
|
{
|
|
if (!st->fn_dev->enable)
|
|
return -EINVAL;
|
|
|
|
return st->fn_dev->enable(st->client, st->cfg->snsr_id, -1);
|
|
}
|
|
|
|
static int bmi_iio_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan, int *val,
|
|
int *val2, long mask)
|
|
{
|
|
struct bmi_iio_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
mutex_lock(BMI_MUTEX(indio_dev));
|
|
if (*st->fn_dev->sts & (BMI_STS_SHUTDOWN | BMI_STS_SUSPEND)) {
|
|
mutex_unlock(BMI_MUTEX(indio_dev));
|
|
return -EPERM;
|
|
}
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
ret = bmi_iio_check_enable(st);
|
|
if (ret < 0)
|
|
goto out;
|
|
|
|
if (!st->fn_dev->get_data) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
ret = st->fn_dev->get_data(st->client, st->cfg->snsr_id,
|
|
chan->channel2, val);
|
|
if (!ret)
|
|
ret = IIO_VAL_INT;
|
|
break;
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
if (!st->fn_dev->freq_read) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
ret = st->fn_dev->freq_read(st->client, st->cfg->snsr_id,
|
|
val, val2);
|
|
if (!ret)
|
|
ret = st->cfg->float_significance;
|
|
break;
|
|
case IIO_CHAN_INFO_SCALE:
|
|
*val = st->cfg->scale.ival;
|
|
*val2 = st->cfg->scale.fval;
|
|
|
|
ret = st->cfg->float_significance;
|
|
break;
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(BMI_MUTEX(indio_dev));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int bmi_iio_write_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan, int val,
|
|
int val2, long mask)
|
|
{
|
|
struct bmi_iio_state *st = iio_priv(indio_dev);
|
|
int ret = 0;
|
|
|
|
mutex_lock(BMI_MUTEX(indio_dev));
|
|
if (*st->fn_dev->sts & (BMI_STS_SHUTDOWN | BMI_STS_SUSPEND)) {
|
|
mutex_unlock(BMI_MUTEX(indio_dev));
|
|
return -EPERM;
|
|
}
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
|
if (!st->fn_dev->freq_write) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
ret = st->fn_dev->freq_write(st->client, st->cfg->snsr_id,
|
|
val, val2);
|
|
break;
|
|
|
|
case IIO_CHAN_INFO_SCALE:
|
|
if (!st->fn_dev->scale_write) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
ret = st->fn_dev->scale_write(st->client, st->cfg->snsr_id,
|
|
val, val2);
|
|
break;
|
|
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
mutex_unlock(BMI_MUTEX(indio_dev));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int bmi_iio_buffer_preenable(struct iio_dev *indio_dev)
|
|
{
|
|
struct bmi_iio_state *st = iio_priv(indio_dev);
|
|
|
|
if (*st->fn_dev->sts & (BMI_STS_SHUTDOWN | BMI_STS_SUSPEND))
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bmi_iio_buffer_postenable(struct iio_dev *indio_dev)
|
|
{
|
|
return bmi_iio_enable(indio_dev, true);
|
|
}
|
|
|
|
static int bmi_iio_buffer_postdisable(struct iio_dev *indio_dev)
|
|
{
|
|
return bmi_iio_enable(indio_dev, false);
|
|
}
|
|
|
|
static const struct iio_buffer_setup_ops bmi_iio_buffer_setup_ops = {
|
|
.preenable = &bmi_iio_buffer_preenable,
|
|
.postenable = &bmi_iio_buffer_postenable,
|
|
.postdisable = &bmi_iio_buffer_postdisable,
|
|
};
|
|
|
|
void bmi_iio_remove(struct iio_dev *indio_dev)
|
|
{
|
|
(void)indio_dev;
|
|
}
|
|
EXPORT_SYMBOL_GPL(bmi_iio_remove);
|
|
|
|
static int __bmi_08x_iio_init(struct iio_dev *indio_dev,
|
|
struct bmi_iio_state *st)
|
|
{
|
|
struct iio_buffer *buffer;
|
|
int ret;
|
|
|
|
ret = bmi_iio_attr(indio_dev, st->cfg->name);
|
|
if (ret) {
|
|
dev_err(st->dev, "bmi_iio_attr ERR=%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!strcmp(st->cfg->name, "accelerometer")) {
|
|
indio_dev->channels = bmi088_acc_channels;
|
|
indio_dev->num_channels = ARRAY_SIZE(bmi088_acc_channels);
|
|
} else if (!strcmp(st->cfg->name, "gyroscope")) {
|
|
indio_dev->channels = bmi088_gyro_channels;
|
|
indio_dev->num_channels = ARRAY_SIZE(bmi088_gyro_channels);
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
|
|
indio_dev->dev.parent = st->dev;
|
|
indio_dev->name = st->cfg->name;
|
|
st->info.attrs = &st->attr_group;
|
|
st->info.read_raw = &bmi_iio_read_raw;
|
|
st->info.write_raw = &bmi_iio_write_raw;
|
|
indio_dev->info = &st->info;
|
|
indio_dev->setup_ops = &bmi_iio_buffer_setup_ops;
|
|
buffer = iio_kfifo_allocate();
|
|
if (!buffer) {
|
|
dev_err(st->dev, "iio_kfifo_allocate ERR\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
iio_device_attach_buffer(indio_dev, buffer);
|
|
|
|
ret = devm_iio_device_register(st->dev, indio_dev);
|
|
if (ret) {
|
|
dev_err(st->dev, "iio_device_register ERR\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bmi_08x_iio_init(struct iio_dev **handle, void *dev_client,
|
|
struct device *dev, struct iio_fn_dev *fn_dev,
|
|
struct sensor_cfg *snsr_cfg)
|
|
{
|
|
struct iio_dev *indio_dev;
|
|
struct bmi_iio_state *st;
|
|
int ret;
|
|
|
|
if (!snsr_cfg)
|
|
return -ENODEV;
|
|
|
|
if (snsr_cfg->snsr_id < 0) {
|
|
/* device has been disabled */
|
|
if (snsr_cfg->name)
|
|
dev_info(dev, "%s disabled\n", snsr_cfg->name);
|
|
else
|
|
dev_info(dev, "device disabled\n");
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
|
if (!indio_dev) {
|
|
dev_err(dev, "iio_device_alloc ERR\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
st = iio_priv(indio_dev);
|
|
st->client = dev_client;
|
|
st->dev = dev;
|
|
st->fn_dev = fn_dev;
|
|
|
|
st->cfg = snsr_cfg;
|
|
ret = __bmi_08x_iio_init(indio_dev, st);
|
|
if (ret) {
|
|
if (st->cfg->name)
|
|
dev_err(st->dev, "%s snsr_id=%d EXIT ERR=%d\n",
|
|
st->cfg->name, st->cfg->snsr_id, ret);
|
|
else
|
|
dev_err(st->dev, "snsr_id=%d EXIT ERR=%d\n",
|
|
st->cfg->snsr_id, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
*handle = indio_dev;
|
|
|
|
dev_info(st->dev, "iio %s done\n", st->cfg->name);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(bmi_08x_iio_init);
|
|
|
|
MODULE_AUTHOR("NVIDIA Corporation");
|
|
MODULE_DESCRIPTION("BMI088 IIO driver");
|
|
MODULE_LICENSE("GPL v2");
|