Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,312 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Magnetometer sensors
#
# When adding new entries keep the list in alphabetical order
menu "Magnetometer sensors"
config AF8133J
tristate "Voltafield AF8133J 3-Axis Magnetometer"
depends on I2C
depends on OF
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Voltafield AF8133J I2C-based
3-axis magnetometer chip.
To compile this driver as a module, choose M here: the module
will be called af8133j.
config AK8974
tristate "Asahi Kasei AK8974 3-Axis Magnetometer"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Asahi Kasei AK8974, AMI305 or
AMI306 I2C-based 3-axis magnetometer chips.
To compile this driver as a module, choose M here: the module
will be called ak8974.
config AK8975
tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Asahi Kasei AK8975, AK8963,
AK09911, AK09912, AK09916 or AK09918 3-Axis Magnetometer.
To compile this driver as a module, choose M here: the module
will be called ak8975.
config AK09911
tristate "Asahi Kasei AK09911 3-axis Compass"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
select AK8975
help
Deprecated: AK09911 is now supported by AK8975 driver.
config ALS31300
tristate "Allegro MicroSystems ALS31300 3-D Linear Hall Effect Sensor"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Allegro MicroSystems
ALS31300 Hall Effect Sensor through its I2C interface.
To compile this driver as a module, choose M here: the
module will be called als31300.
config BMC150_MAGN
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config BMC150_MAGN_I2C
tristate "Bosch BMC150 I2C Magnetometer Driver"
depends on I2C
select BMC150_MAGN
select REGMAP_I2C
help
Say yes here to build support for the BMC150 magnetometer with
I2C interface.
This is a combo module with both accelerometer and magnetometer.
This driver is only implementing magnetometer part, which has
its own address and register map.
This driver also supports I2C Bosch BMC156 and BMM150 chips.
To compile this driver as a module, choose M here: the module will be
called bmc150_magn_i2c.
config BMC150_MAGN_SPI
tristate "Bosch BMC150 SPI Magnetometer Driver"
depends on SPI
select BMC150_MAGN
select REGMAP_SPI
help
Say yes here to build support for the BMC150 magnetometer with
SPI interface.
This is a combo module with both accelerometer and magnetometer.
This driver is only implementing magnetometer part, which has
its own address and register map.
This driver also supports SPI Bosch BMC156 and BMM150 chips.
To compile this driver as a module, choose M here: the module will be
called bmc150_magn_spi.
config MAG3110
tristate "Freescale MAG3110 3-Axis Magnetometer"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Freescale MAG3110 3-Axis
magnetometer.
To compile this driver as a module, choose M here: the module
will be called mag3110.
config HID_SENSOR_MAGNETOMETER_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
select HID_SENSOR_IIO_COMMON
select HID_SENSOR_IIO_TRIGGER
tristate "HID Magnetometer 3D"
help
Say yes here to build support for the HID SENSOR
Magnetometer 3D.
config MMC35240
tristate "MEMSIC MMC35240 3-axis magnetic sensor"
select REGMAP_I2C
depends on I2C
help
Say yes here to build support for the MEMSIC MMC35240 3-axis
magnetic sensor.
To compile this driver as a module, choose M here: the module
will be called mmc35240.
config MMC5633
tristate "MEMSIC MMC5633 3-axis magnetic sensor"
select REGMAP_I2C
select REGMAP_I3C if I3C
depends on I3C_OR_I2C
help
Say yes here to build support for the MEMSIC MMC5633 3-axis
magnetic sensor.
To compile this driver as a module, choose M here: the module
will be called mmc5633
config MMC5983
tristate "MEMSIC MMC5983 3-axis magnetic sensor"
depends on I2C
select REGMAP_I2C
help
Say yes here to build support for the MEMSIC MMC5983 3-axis
magnetic sensor.
To compile this driver as a module, choose M here: the module
will be called mmc5983
config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
select IIO_ST_SENSORS_CORE
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics magnetometers:
LSM303C, LSM303DLHC, LSM303DLM, LIS3MDL.
Also need to enable at least one of I2C and SPI interface drivers
below.
config IIO_ST_MAGN_I2C_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis I2C Interface"
depends on I2C && IIO_ST_MAGN_3AXIS
default I2C && IIO_ST_MAGN_3AXIS
select IIO_ST_SENSORS_I2C
help
Build support for STMicroelectronics magnetometers I2C interface.
To compile this driver as a module, choose M here. The module
will be called st_magn_i2c.
config IIO_ST_MAGN_SPI_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis SPI Interface"
depends on SPI_MASTER && IIO_ST_MAGN_3AXIS
default SPI_MASTER && IIO_ST_MAGN_3AXIS
select IIO_ST_SENSORS_SPI
help
Build support for STMicroelectronics magnetometers SPI interface.
To compile this driver as a module, choose M here. The module
will be called st_magn_spi.
config INFINEON_TLV493D
tristate "Infineon TLV493D Low-Power 3D Magnetic Sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here to add support for the Infineon TLV493D-A1B6 Low-
Power 3D Magnetic Sensor.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
will be called tlv493d.
config SENSORS_HMC5843
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config SENSORS_HMC5843_I2C
tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer (I2C)"
depends on I2C
select SENSORS_HMC5843
select REGMAP_I2C
help
Say Y here to add support for the Honeywell HMC5843, HMC5883 and
HMC5883L 3-Axis Magnetometer (digital compass).
This driver can also be compiled as a set of modules.
If so, these modules will be created:
- hmc5843_core (core functions)
- hmc5843_i2c (support for HMC5843, HMC5883, HMC5883L and HMC5983)
config SENSORS_HMC5843_SPI
tristate "Honeywell HMC5983 3-Axis Magnetometer (SPI)"
depends on SPI_MASTER
select SENSORS_HMC5843
select REGMAP_SPI
help
Say Y here to add support for the Honeywell HMC5983 3-Axis Magnetometer
(digital compass).
This driver can also be compiled as a set of modules.
If so, these modules will be created:
- hmc5843_core (core functions)
- hmc5843_spi (support for HMC5983)
config SENSORS_RM3100
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config SENSORS_RM3100_I2C
tristate "PNI RM3100 3-Axis Magnetometer (I2C)"
depends on I2C
select SENSORS_RM3100
select REGMAP_I2C
help
Say Y here to add support for the PNI RM3100 3-Axis Magnetometer.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
will be called rm3100-i2c.
config SENSORS_RM3100_SPI
tristate "PNI RM3100 3-Axis Magnetometer (SPI)"
depends on SPI_MASTER
select SENSORS_RM3100
select REGMAP_SPI
help
Say Y here to add support for the PNI RM3100 3-Axis Magnetometer.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
will be called rm3100-spi.
config SI7210
tristate "SI7210 Hall effect sensor"
depends on I2C
select REGMAP_I2C
help
Say Y here to add support for the SI7210 Hall effect sensor.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
will be called si7210.
config TI_TMAG5273
tristate "TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor"
depends on I2C
select REGMAP_I2C
help
Say Y here to add support for the TI TMAG5273 Low-Power
Linear 3D Hall-Effect Sensor.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
will be called tmag5273.
config YAMAHA_YAS530
tristate "Yamaha YAS530 family of 3-Axis Magnetometers (I2C)"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here to add support for the Yamaha YAS530 series of
3-Axis Magnetometers. YAS530, YAS532, YAS533 and YAS537 are
supported.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
will be called yamaha-yas.
endmenu
@@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for industrial I/O Magnetometer sensor drivers
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AF8133J) += af8133j.o
obj-$(CONFIG_AK8974) += ak8974.o
obj-$(CONFIG_AK8975) += ak8975.o
obj-$(CONFIG_ALS31300) += als31300.o
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o
obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
obj-$(CONFIG_MMC35240) += mmc35240.o
obj-$(CONFIG_MMC5633) += mmc5633.o
obj-$(CONFIG_MMC5983) += mmc5983.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
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
obj-$(CONFIG_INFINEON_TLV493D) += tlv493d.o
obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o
obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o
obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
obj-$(CONFIG_SI7210) += si7210.o
obj-$(CONFIG_TI_TMAG5273) += tmag5273.o
obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o
@@ -0,0 +1,527 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* af8133j.c - Voltafield AF8133J magnetometer driver
*
* Copyright 2021 Icenowy Zheng <icenowy@aosc.io>
* Copyright 2024 Ondřej Jirman <megi@xff.cz>
*/
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define AF8133J_REG_OUT 0x03
#define AF8133J_REG_PCODE 0x00
#define AF8133J_REG_PCODE_VAL 0x5e
#define AF8133J_REG_STATUS 0x02
#define AF8133J_REG_STATUS_ACQ BIT(0)
#define AF8133J_REG_STATE 0x0a
#define AF8133J_REG_STATE_STBY 0x00
#define AF8133J_REG_STATE_WORK 0x01
#define AF8133J_REG_RANGE 0x0b
#define AF8133J_REG_RANGE_22G 0x12
#define AF8133J_REG_RANGE_12G 0x34
#define AF8133J_REG_SWR 0x11
#define AF8133J_REG_SWR_PERFORM 0x81
static const char * const af8133j_supply_names[] = {
"avdd",
"dvdd",
};
struct af8133j_data {
struct i2c_client *client;
struct regmap *regmap;
/*
* Protect device internal state between starting a measurement
* and reading the result.
*/
struct mutex mutex;
struct iio_mount_matrix orientation;
struct gpio_desc *reset_gpiod;
struct regulator_bulk_data supplies[ARRAY_SIZE(af8133j_supply_names)];
u8 range;
};
enum af8133j_axis {
AXIS_X = 0,
AXIS_Y,
AXIS_Z,
};
static struct iio_mount_matrix *
af8133j_get_mount_matrix(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct af8133j_data *data = iio_priv(indio_dev);
return &data->orientation;
}
static const struct iio_chan_spec_ext_info af8133j_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, af8133j_get_mount_matrix),
{ }
};
#define AF8133J_CHANNEL(_si, _axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _axis, \
.address = AXIS_ ## _axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = af8133j_ext_info, \
.scan_index = _si, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const struct iio_chan_spec af8133j_channels[] = {
AF8133J_CHANNEL(0, X),
AF8133J_CHANNEL(1, Y),
AF8133J_CHANNEL(2, Z),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static int af8133j_product_check(struct af8133j_data *data)
{
struct device *dev = &data->client->dev;
unsigned int val;
int ret;
ret = regmap_read(data->regmap, AF8133J_REG_PCODE, &val);
if (ret) {
dev_err(dev, "Error reading product code (%d)\n", ret);
return ret;
}
if (val != AF8133J_REG_PCODE_VAL) {
dev_warn(dev, "Invalid product code (0x%02x)\n", val);
return 0; /* Allow unknown ID so fallback compatibles work */
}
return 0;
}
static int af8133j_reset(struct af8133j_data *data)
{
struct device *dev = &data->client->dev;
int ret;
if (data->reset_gpiod) {
/* If we have GPIO reset line, use it */
gpiod_set_value_cansleep(data->reset_gpiod, 1);
udelay(10);
gpiod_set_value_cansleep(data->reset_gpiod, 0);
} else {
/* Otherwise use software reset */
ret = regmap_write(data->regmap, AF8133J_REG_SWR,
AF8133J_REG_SWR_PERFORM);
if (ret) {
dev_err(dev, "Failed to reset the chip\n");
return ret;
}
}
/* Wait for reset to finish */
usleep_range(1000, 1100);
/* Restore range setting */
if (data->range == AF8133J_REG_RANGE_22G) {
ret = regmap_write(data->regmap, AF8133J_REG_RANGE, data->range);
if (ret)
return ret;
}
return 0;
}
static void af8133j_power_down(struct af8133j_data *data)
{
gpiod_set_value_cansleep(data->reset_gpiod, 1);
regulator_bulk_disable(ARRAY_SIZE(data->supplies), data->supplies);
}
static int af8133j_power_up(struct af8133j_data *data)
{
struct device *dev = &data->client->dev;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies), data->supplies);
if (ret) {
dev_err(dev, "Could not enable regulators\n");
return ret;
}
gpiod_set_value_cansleep(data->reset_gpiod, 0);
/* Wait for power on reset */
usleep_range(15000, 16000);
ret = af8133j_reset(data);
if (ret) {
af8133j_power_down(data);
return ret;
}
return 0;
}
static int af8133j_take_measurement(struct af8133j_data *data)
{
unsigned int val;
int ret;
ret = regmap_write(data->regmap,
AF8133J_REG_STATE, AF8133J_REG_STATE_WORK);
if (ret)
return ret;
/* The datasheet says "Mesaure Time <1.5ms" */
ret = regmap_read_poll_timeout(data->regmap, AF8133J_REG_STATUS, val,
val & AF8133J_REG_STATUS_ACQ,
500, 1500);
if (ret)
return ret;
ret = regmap_write(data->regmap,
AF8133J_REG_STATE, AF8133J_REG_STATE_STBY);
if (ret)
return ret;
return 0;
}
static int af8133j_read_measurement(struct af8133j_data *data, __le16 buf[3])
{
struct device *dev = &data->client->dev;
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret) {
/*
* Ignore EACCES because that happens when RPM is disabled
* during system sleep, while userspace leave eg. hrtimer
* trigger attached and IIO core keeps trying to do measurements.
*/
if (ret != -EACCES)
dev_err(dev, "Failed to power on (%d)\n", ret);
return ret;
}
scoped_guard(mutex, &data->mutex) {
ret = af8133j_take_measurement(data);
if (ret)
goto out_rpm_put;
ret = regmap_bulk_read(data->regmap, AF8133J_REG_OUT,
buf, sizeof(__le16) * 3);
}
out_rpm_put:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
static const int af8133j_scales[][2] = {
[0] = { 0, 366210 }, /* 12 gauss */
[1] = { 0, 671386 }, /* 22 gauss */
};
static int af8133j_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct af8133j_data *data = iio_priv(indio_dev);
__le16 buf[3];
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = af8133j_read_measurement(data, buf);
if (ret)
return ret;
*val = sign_extend32(le16_to_cpu(buf[chan->address]),
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
if (data->range == AF8133J_REG_RANGE_12G)
*val2 = af8133j_scales[0][1];
else
*val2 = af8133j_scales[1][1];
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static int af8133j_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)af8133j_scales;
*length = ARRAY_SIZE(af8133j_scales) * 2;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int af8133j_set_scale(struct af8133j_data *data,
unsigned int val, unsigned int val2)
{
struct device *dev = &data->client->dev;
u8 range;
int ret = 0;
if (af8133j_scales[0][0] == val && af8133j_scales[0][1] == val2)
range = AF8133J_REG_RANGE_12G;
else if (af8133j_scales[1][0] == val && af8133j_scales[1][1] == val2)
range = AF8133J_REG_RANGE_22G;
else
return -EINVAL;
pm_runtime_disable(dev);
/*
* When suspended, just store the new range to data->range to be
* applied later during power up.
*/
if (!pm_runtime_status_suspended(dev)) {
scoped_guard(mutex, &data->mutex)
ret = regmap_write(data->regmap,
AF8133J_REG_RANGE, range);
}
pm_runtime_enable(dev);
data->range = range;
return ret;
}
static int af8133j_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct af8133j_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return af8133j_set_scale(data, val, val2);
default:
return -EINVAL;
}
}
static int af8133j_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
return IIO_VAL_INT_PLUS_NANO;
}
static const struct iio_info af8133j_info = {
.read_raw = af8133j_read_raw,
.read_avail = af8133j_read_avail,
.write_raw = af8133j_write_raw,
.write_raw_get_fmt = af8133j_write_raw_get_fmt,
};
static irqreturn_t af8133j_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct af8133j_data *data = iio_priv(indio_dev);
s64 timestamp = iio_get_time_ns(indio_dev);
struct {
__le16 values[3];
aligned_s64 timestamp;
} sample = { };
int ret;
ret = af8133j_read_measurement(data, sample.values);
if (ret)
goto out_done;
iio_push_to_buffers_with_ts(indio_dev, &sample, sizeof(sample),
timestamp);
out_done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct regmap_config af8133j_regmap_config = {
.name = "af8133j_regmap",
.reg_bits = 8,
.val_bits = 8,
.max_register = AF8133J_REG_SWR,
};
static void af8133j_power_down_action(void *ptr)
{
struct af8133j_data *data = ptr;
if (!pm_runtime_status_suspended(&data->client->dev))
af8133j_power_down(data);
}
static int af8133j_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct af8133j_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
int ret, i;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &af8133j_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"regmap initialization failed\n");
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->regmap = regmap;
data->range = AF8133J_REG_RANGE_12G;
mutex_init(&data->mutex);
data->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(data->reset_gpiod))
return dev_err_probe(dev, PTR_ERR(data->reset_gpiod),
"Failed to get reset gpio\n");
for (i = 0; i < ARRAY_SIZE(af8133j_supply_names); i++)
data->supplies[i].supply = af8133j_supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
data->supplies);
if (ret)
return ret;
ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret)
return dev_err_probe(dev, ret, "Failed to read mount matrix\n");
ret = af8133j_power_up(data);
if (ret)
return ret;
pm_runtime_set_active(dev);
ret = devm_add_action_or_reset(dev, af8133j_power_down_action, data);
if (ret)
return ret;
ret = af8133j_product_check(data);
if (ret)
return ret;
pm_runtime_get_noresume(dev);
pm_runtime_use_autosuspend(dev);
pm_runtime_set_autosuspend_delay(dev, 500);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
pm_runtime_put_autosuspend(dev);
indio_dev->info = &af8133j_info;
indio_dev->name = "af8133j";
indio_dev->channels = af8133j_channels;
indio_dev->num_channels = ARRAY_SIZE(af8133j_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
&af8133j_trigger_handler, NULL);
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to setup iio triggered buffer\n");
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to register iio device");
return 0;
}
static int af8133j_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct af8133j_data *data = iio_priv(indio_dev);
af8133j_power_down(data);
return 0;
}
static int af8133j_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct af8133j_data *data = iio_priv(indio_dev);
return af8133j_power_up(data);
}
static const struct dev_pm_ops af8133j_pm_ops = {
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
RUNTIME_PM_OPS(af8133j_runtime_suspend, af8133j_runtime_resume, NULL)
};
static const struct of_device_id af8133j_of_match[] = {
{ .compatible = "voltafield,af8133j", },
{ }
};
MODULE_DEVICE_TABLE(of, af8133j_of_match);
static const struct i2c_device_id af8133j_id[] = {
{ .name = "af8133j" },
{ }
};
MODULE_DEVICE_TABLE(i2c, af8133j_id);
static struct i2c_driver af8133j_driver = {
.driver = {
.name = "af8133j",
.of_match_table = af8133j_of_match,
.pm = pm_ptr(&af8133j_pm_ops),
},
.probe = af8133j_probe,
.id_table = af8133j_id,
};
module_i2c_driver(af8133j_driver);
MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>");
MODULE_AUTHOR("Ondřej Jirman <megi@xff.cz>");
MODULE_DESCRIPTION("Voltafield AF8133J magnetic sensor driver");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,490 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Allegro MicroSystems ALS31300 3-D Linear Hall Effect Sensor
*
* Copyright (c) 2024 Linaro Limited
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
/*
* The Allegro MicroSystems ALS31300 has an EEPROM space to configure how
* the device works and how the interrupt line behaves.
* Only the default setup with external trigger is supported.
*
* While the bindings supports declaring an interrupt line, those
* events are not supported.
*
* It should be possible to adapt the driver to the current
* device EEPROM setup at runtime.
*/
#define ALS31300_EEPROM_CONFIG 0x02
#define ALS31300_EEPROM_INTERRUPT 0x03
#define ALS31300_EEPROM_CUSTOMER_1 0x0d
#define ALS31300_EEPROM_CUSTOMER_2 0x0e
#define ALS31300_EEPROM_CUSTOMER_3 0x0f
#define ALS31300_VOL_MODE 0x27
#define ALS31300_VOL_MODE_LPDCM GENMASK(6, 4)
#define ALS31300_LPDCM_INACTIVE_0_5_MS 0
#define ALS31300_LPDCM_INACTIVE_1_0_MS 1
#define ALS31300_LPDCM_INACTIVE_5_0_MS 2
#define ALS31300_LPDCM_INACTIVE_10_0_MS 3
#define ALS31300_LPDCM_INACTIVE_50_0_MS 4
#define ALS31300_LPDCM_INACTIVE_100_0_MS 5
#define ALS31300_LPDCM_INACTIVE_500_0_MS 6
#define ALS31300_LPDCM_INACTIVE_1000_0_MS 7
#define ALS31300_VOL_MODE_SLEEP GENMASK(1, 0)
#define ALS31300_VOL_MODE_ACTIVE_MODE 0
#define ALS31300_VOL_MODE_SLEEP_MODE 1
#define ALS31300_VOL_MODE_LPDCM_MODE 2
#define ALS31300_VOL_MSB 0x28
#define ALS31300_VOL_MSB_TEMPERATURE GENMASK(5, 0)
#define ALS31300_VOL_MSB_INTERRUPT BIT(6)
#define ALS31300_VOL_MSB_NEW_DATA BIT(7)
#define ALS31300_VOL_MSB_Z_AXIS GENMASK(15, 8)
#define ALS31300_VOL_MSB_Y_AXIS GENMASK(23, 16)
#define ALS31300_VOL_MSB_X_AXIS GENMASK(31, 24)
#define ALS31300_VOL_LSB 0x29
#define ALS31300_VOL_LSB_TEMPERATURE GENMASK(5, 0)
#define ALS31300_VOL_LSB_HALL_STATUS GENMASK(7, 7)
#define ALS31300_VOL_LSB_Z_AXIS GENMASK(11, 8)
#define ALS31300_VOL_LSB_Y_AXIS GENMASK(15, 12)
#define ALS31300_VOL_LSB_X_AXIS GENMASK(19, 16)
#define ALS31300_VOL_LSB_INTERRUPT_WRITE BIT(20)
#define ALS31300_CUSTOMER_ACCESS 0x35
#define ALS31300_DATA_X_GET(b) \
sign_extend32(FIELD_GET(ALS31300_VOL_MSB_X_AXIS, b[0]) << 4 | \
FIELD_GET(ALS31300_VOL_LSB_X_AXIS, b[1]), 11)
#define ALS31300_DATA_Y_GET(b) \
sign_extend32(FIELD_GET(ALS31300_VOL_MSB_Y_AXIS, b[0]) << 4 | \
FIELD_GET(ALS31300_VOL_LSB_Y_AXIS, b[1]), 11)
#define ALS31300_DATA_Z_GET(b) \
sign_extend32(FIELD_GET(ALS31300_VOL_MSB_Z_AXIS, b[0]) << 4 | \
FIELD_GET(ALS31300_VOL_LSB_Z_AXIS, b[1]), 11)
#define ALS31300_TEMPERATURE_GET(b) \
(FIELD_GET(ALS31300_VOL_MSB_TEMPERATURE, b[0]) << 6 | \
FIELD_GET(ALS31300_VOL_LSB_TEMPERATURE, b[1]))
enum als31300_channels {
TEMPERATURE = 0,
AXIS_X,
AXIS_Y,
AXIS_Z,
};
struct als31300_variant_info {
u8 sensitivity;
};
struct als31300_data {
struct device *dev;
/* protects power on/off the device and access HW */
struct mutex mutex;
const struct als31300_variant_info *variant_info;
struct regmap *map;
};
/* The whole measure is split into 2x32-bit registers, we need to read them both at once */
static int als31300_get_measure(struct als31300_data *data,
u16 *t, s16 *x, s16 *y, s16 *z)
{
u32 buf[2];
int ret, err;
guard(mutex)(&data->mutex);
ret = pm_runtime_resume_and_get(data->dev);
if (ret)
return ret;
/*
* Loop until data is valid, new data should have the
* ALS31300_VOL_MSB_NEW_DATA bit set to 1.
* Max update rate is 2KHz, wait up to 1ms.
*/
ret = read_poll_timeout(regmap_bulk_read, err,
err || FIELD_GET(ALS31300_VOL_MSB_NEW_DATA, buf[0]),
20, USEC_PER_MSEC, false,
data->map, ALS31300_VOL_MSB, buf, ARRAY_SIZE(buf));
/* Bail out on read_poll_timeout() error */
if (ret)
goto out;
/* Bail out on regmap_bulk_read() error */
if (err) {
dev_err(data->dev, "read data failed, error %d\n", ret);
ret = err;
goto out;
}
*t = ALS31300_TEMPERATURE_GET(buf);
*x = ALS31300_DATA_X_GET(buf);
*y = ALS31300_DATA_Y_GET(buf);
*z = ALS31300_DATA_Z_GET(buf);
out:
pm_runtime_put_autosuspend(data->dev);
return ret;
}
static int als31300_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val,
int *val2, long mask)
{
struct als31300_data *data = iio_priv(indio_dev);
s16 x, y, z;
u16 t;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = als31300_get_measure(data, &t, &x, &y, &z);
if (ret)
return ret;
switch (chan->address) {
case TEMPERATURE:
*val = t;
return IIO_VAL_INT;
case AXIS_X:
*val = x;
return IIO_VAL_INT;
case AXIS_Y:
*val = y;
return IIO_VAL_INT;
case AXIS_Z:
*val = z;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
/*
* Fractional part of:
* 1000 * 302 * (value - 1708)
* temp = ----------------------------
* 4096
* to convert temperature in millicelcius.
*/
*val = MILLI * 302;
*val2 = 4096;
return IIO_VAL_FRACTIONAL;
case IIO_MAGN:
/*
* Devices are configured in factory
* with different sensitivities:
* - 500 GAUSS <-> 4 LSB/Gauss
* - 1000 GAUSS <-> 2 LSB/Gauss
* - 2000 GAUSS <-> 1 LSB/Gauss
* with translates by a division of the returned
* value to get Gauss value.
* The sensitivity cannot be read at runtime
* so the value depends on the model compatible
* or device id.
*/
*val = 1;
*val2 = data->variant_info->sensitivity;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_TEMP:
*val = -1708;
return IIO_VAL_INT;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static irqreturn_t als31300_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct als31300_data *data = iio_priv(indio_dev);
struct {
u16 temperature;
s16 channels[3];
aligned_s64 timestamp;
} scan;
s16 x, y, z;
int ret;
u16 t;
ret = als31300_get_measure(data, &t, &x, &y, &z);
if (ret)
goto trigger_out;
scan.temperature = t;
scan.channels[0] = x;
scan.channels[1] = y;
scan.channels[2] = z;
iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), pf->timestamp);
trigger_out:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
#define ALS31300_AXIS_CHANNEL(axis, index) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.address = index, \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec als31300_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = TEMPERATURE,
.scan_index = TEMPERATURE,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
ALS31300_AXIS_CHANNEL(X, AXIS_X),
ALS31300_AXIS_CHANNEL(Y, AXIS_Y),
ALS31300_AXIS_CHANNEL(Z, AXIS_Z),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct iio_info als31300_info = {
.read_raw = als31300_read_raw,
};
static int als31300_set_operating_mode(struct als31300_data *data,
unsigned int val)
{
int ret;
ret = regmap_update_bits(data->map, ALS31300_VOL_MODE,
ALS31300_VOL_MODE_SLEEP, val);
if (ret) {
dev_err(data->dev, "failed to set operating mode (%pe)\n", ERR_PTR(ret));
return ret;
}
/* The time it takes to exit sleep mode is equivalent to Power-On Delay Time */
if (val == ALS31300_VOL_MODE_ACTIVE_MODE)
fsleep(600);
return 0;
}
static void als31300_power_down(void *data)
{
als31300_set_operating_mode(data, ALS31300_VOL_MODE_SLEEP_MODE);
}
static const struct iio_buffer_setup_ops als31300_setup_ops = {};
static const unsigned long als31300_scan_masks[] = { GENMASK(3, 0), 0 };
static bool als31300_volatile_reg(struct device *dev, unsigned int reg)
{
return reg == ALS31300_VOL_MSB || reg == ALS31300_VOL_LSB;
}
static const struct regmap_config als31300_regmap_config = {
.reg_bits = 8,
.val_bits = 32,
.max_register = ALS31300_CUSTOMER_ACCESS,
.volatile_reg = als31300_volatile_reg,
};
static int als31300_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct als31300_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = dev;
i2c_set_clientdata(i2c, indio_dev);
ret = devm_mutex_init(dev, &data->mutex);
if (ret)
return ret;
data->variant_info = i2c_get_match_data(i2c);
if (!data->variant_info)
return -EINVAL;
data->map = devm_regmap_init_i2c(i2c, &als31300_regmap_config);
if (IS_ERR(data->map))
return dev_err_probe(dev, PTR_ERR(data->map),
"failed to allocate register map\n");
ret = devm_regulator_get_enable(dev, "vcc");
if (ret)
return dev_err_probe(dev, ret, "failed to enable regulator\n");
ret = als31300_set_operating_mode(data, ALS31300_VOL_MODE_ACTIVE_MODE);
if (ret)
return dev_err_probe(dev, ret, "failed to power on device\n");
ret = devm_add_action_or_reset(dev, als31300_power_down, data);
if (ret)
return ret;
indio_dev->info = &als31300_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = i2c->name;
indio_dev->channels = als31300_channels;
indio_dev->num_channels = ARRAY_SIZE(als31300_channels);
indio_dev->available_scan_masks = als31300_scan_masks;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
als31300_trigger_handler,
&als31300_setup_ops);
if (ret < 0)
return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n");
ret = pm_runtime_set_active(dev);
if (ret < 0)
return ret;
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
pm_runtime_get_noresume(dev);
pm_runtime_set_autosuspend_delay(dev, 200);
pm_runtime_use_autosuspend(dev);
pm_runtime_put_autosuspend(dev);
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "device register failed\n");
return 0;
}
static int als31300_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct als31300_data *data = iio_priv(indio_dev);
return als31300_set_operating_mode(data, ALS31300_VOL_MODE_SLEEP_MODE);
}
static int als31300_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct als31300_data *data = iio_priv(indio_dev);
return als31300_set_operating_mode(data, ALS31300_VOL_MODE_ACTIVE_MODE);
}
static DEFINE_RUNTIME_DEV_PM_OPS(als31300_pm_ops,
als31300_runtime_suspend, als31300_runtime_resume,
NULL);
static const struct als31300_variant_info al31300_variant_500 = {
.sensitivity = 4,
};
static const struct als31300_variant_info al31300_variant_1000 = {
.sensitivity = 2,
};
static const struct als31300_variant_info al31300_variant_2000 = {
.sensitivity = 1,
};
static const struct i2c_device_id als31300_id[] = {
{
.name = "als31300-500",
.driver_data = (kernel_ulong_t)&al31300_variant_500,
},
{
.name = "als31300-1000",
.driver_data = (kernel_ulong_t)&al31300_variant_1000,
},
{
.name = "als31300-2000",
.driver_data = (kernel_ulong_t)&al31300_variant_2000,
},
{ }
};
MODULE_DEVICE_TABLE(i2c, als31300_id);
static const struct of_device_id als31300_of_match[] = {
{
.compatible = "allegromicro,als31300-500",
.data = &al31300_variant_500,
},
{
.compatible = "allegromicro,als31300-1000",
.data = &al31300_variant_1000,
},
{
.compatible = "allegromicro,als31300-2000",
.data = &al31300_variant_2000,
},
{ }
};
MODULE_DEVICE_TABLE(of, als31300_of_match);
static struct i2c_driver als31300_driver = {
.driver = {
.name = "als31300",
.of_match_table = als31300_of_match,
.pm = pm_ptr(&als31300_pm_ops),
},
.probe = als31300_probe,
.id_table = als31300_id,
};
module_i2c_driver(als31300_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ALS31300 3-D Linear Hall Effect Driver");
MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BMC150_MAGN_H_
#define _BMC150_MAGN_H_
extern const struct regmap_config bmc150_magn_regmap_config;
extern const struct dev_pm_ops bmc150_magn_pm_ops;
int bmc150_magn_probe(struct device *dev, struct regmap *regmap, int irq,
const char *name);
void bmc150_magn_remove(struct device *dev);
#endif /* _BMC150_MAGN_H_ */
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 3-axis magnetometer driver supporting following I2C Bosch-Sensortec chips:
* - BMC150
* - BMC156
* - BMM150
*
* Copyright (c) 2016, Intel Corporation.
*/
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "bmc150_magn.h"
static int bmc150_magn_i2c_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct regmap *regmap;
const char *name = NULL;
regmap = devm_regmap_init_i2c(client, &bmc150_magn_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to initialize i2c regmap\n");
return PTR_ERR(regmap);
}
if (id)
name = id->name;
return bmc150_magn_probe(&client->dev, regmap, client->irq, name);
}
static void bmc150_magn_i2c_remove(struct i2c_client *client)
{
bmc150_magn_remove(&client->dev);
}
static const struct i2c_device_id bmc150_magn_i2c_id[] = {
{ .name = "bmc150_magn" },
{ .name = "bmc156_magn" },
{ .name = "bmm150_magn" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id);
static const struct of_device_id bmc150_magn_of_match[] = {
{ .compatible = "bosch,bmc150_magn" },
{ .compatible = "bosch,bmc156_magn" },
{ .compatible = "bosch,bmm150_magn" }, /* deprecated compatible */
{ .compatible = "bosch,bmm150" },
{ }
};
MODULE_DEVICE_TABLE(of, bmc150_magn_of_match);
static struct i2c_driver bmc150_magn_driver = {
.driver = {
.name = "bmc150_magn_i2c",
.of_match_table = bmc150_magn_of_match,
.pm = &bmc150_magn_pm_ops,
},
.probe = bmc150_magn_i2c_probe,
.remove = bmc150_magn_i2c_remove,
.id_table = bmc150_magn_i2c_id,
};
module_i2c_driver(bmc150_magn_driver);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("BMC150 I2C magnetometer driver");
MODULE_IMPORT_NS("IIO_BMC150_MAGN");
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* 3-axis magnetometer driver support following SPI Bosch-Sensortec chips:
* - BMC150
* - BMC156
* - BMM150
*
* Copyright (c) 2016, Intel Corporation.
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include "bmc150_magn.h"
static int bmc150_magn_spi_probe(struct spi_device *spi)
{
struct regmap *regmap;
const struct spi_device_id *id = spi_get_device_id(spi);
regmap = devm_regmap_init_spi(spi, &bmc150_magn_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap: %pe\n",
regmap);
return PTR_ERR(regmap);
}
return bmc150_magn_probe(&spi->dev, regmap, spi->irq, id->name);
}
static void bmc150_magn_spi_remove(struct spi_device *spi)
{
bmc150_magn_remove(&spi->dev);
}
static const struct spi_device_id bmc150_magn_spi_id[] = {
{"bmc150_magn", 0},
{"bmc156_magn", 0},
{"bmm150_magn", 0},
{ }
};
MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id);
static struct spi_driver bmc150_magn_spi_driver = {
.probe = bmc150_magn_spi_probe,
.remove = bmc150_magn_spi_remove,
.id_table = bmc150_magn_spi_id,
.driver = {
.name = "bmc150_magn_spi",
},
};
module_spi_driver(bmc150_magn_spi_driver);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
MODULE_DESCRIPTION("BMC150 magnetometer SPI driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_BMC150_MAGN");
@@ -0,0 +1,575 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include "../common/hid-sensors/hid-sensor-trigger.h"
enum magn_3d_channel {
CHANNEL_SCAN_INDEX_X,
CHANNEL_SCAN_INDEX_Y,
CHANNEL_SCAN_INDEX_Z,
CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP,
CHANNEL_SCAN_INDEX_NORTH_TRUE_TILT_COMP,
CHANNEL_SCAN_INDEX_NORTH_MAGN,
CHANNEL_SCAN_INDEX_NORTH_TRUE,
CHANNEL_SCAN_INDEX_TIMESTAMP,
MAGN_3D_CHANNEL_MAX,
};
struct common_attributes {
int scale_pre_decml;
int scale_post_decml;
int scale_precision;
int value_offset;
};
struct magn_3d_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common magn_flux_attributes;
struct hid_sensor_common rot_attributes;
struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
/* dynamically sized array to hold sensor values */
u32 *iio_vals;
/* array of pointers to sensor value */
u32 *magn_val_addr[MAGN_3D_CHANNEL_MAX];
struct common_attributes magn_flux_attr;
struct common_attributes rot_attr;
s64 timestamp;
};
static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,
HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,
HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS,
HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH,
HID_USAGE_SENSOR_ORIENT_MAGN_NORTH,
HID_USAGE_SENSOR_ORIENT_TRUE_NORTH,
HID_USAGE_SENSOR_TIME_TIMESTAMP,
};
static const u32 magn_3d_sensitivity_addresses[] = {
HID_USAGE_SENSOR_DATA_ORIENTATION,
HID_USAGE_SENSOR_ORIENT_MAGN_FLUX,
};
/* Channel definitions */
static const struct iio_chan_spec magn_3d_channels[] = {
{
.type = IIO_MAGN,
.modified = 1,
.channel2 = IIO_MOD_X,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
}, {
.type = IIO_MAGN,
.modified = 1,
.channel2 = IIO_MOD_Y,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
}, {
.type = IIO_MAGN,
.modified = 1,
.channel2 = IIO_MOD_Z,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
}, {
.type = IIO_ROT,
.modified = 1,
.channel2 = IIO_MOD_NORTH_MAGN_TILT_COMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
}, {
.type = IIO_ROT,
.modified = 1,
.channel2 = IIO_MOD_NORTH_TRUE_TILT_COMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
}, {
.type = IIO_ROT,
.modified = 1,
.channel2 = IIO_MOD_NORTH_MAGN,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
}, {
.type = IIO_ROT,
.modified = 1,
.channel2 = IIO_MOD_NORTH_TRUE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS),
},
IIO_CHAN_SOFT_TIMESTAMP(7)
};
/* Channel read_raw handler */
static int magn_3d_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2,
long mask)
{
struct magn_3d_state *magn_state = iio_priv(indio_dev);
int report_id = -1;
u32 address;
int ret_type;
s32 min;
*val = 0;
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
report_id = magn_state->magn[chan->address].report_id;
min = magn_state->magn[chan->address].logical_minimum;
address = magn_3d_addresses[chan->address];
if (report_id >= 0)
*val = sensor_hub_input_attr_get_raw_value(
magn_state->magn_flux_attributes.hsdev,
HID_USAGE_SENSOR_COMPASS_3D, address,
report_id,
SENSOR_HUB_SYNC,
min < 0);
else {
*val = 0;
hid_sensor_power_state(
&magn_state->magn_flux_attributes,
false);
return -EINVAL;
}
hid_sensor_power_state(&magn_state->magn_flux_attributes,
false);
ret_type = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_MAGN:
*val = magn_state->magn_flux_attr.scale_pre_decml;
*val2 = magn_state->magn_flux_attr.scale_post_decml;
ret_type = magn_state->magn_flux_attr.scale_precision;
break;
case IIO_ROT:
*val = magn_state->rot_attr.scale_pre_decml;
*val2 = magn_state->rot_attr.scale_post_decml;
ret_type = magn_state->rot_attr.scale_precision;
break;
default:
ret_type = -EINVAL;
}
break;
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_MAGN:
*val = magn_state->magn_flux_attr.value_offset;
ret_type = IIO_VAL_INT;
break;
case IIO_ROT:
*val = magn_state->rot_attr.value_offset;
ret_type = IIO_VAL_INT;
break;
default:
ret_type = -EINVAL;
}
break;
case IIO_CHAN_INFO_SAMP_FREQ:
ret_type = hid_sensor_read_samp_freq_value(
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_CHAN_INFO_HYSTERESIS:
switch (chan->type) {
case IIO_MAGN:
ret_type = hid_sensor_read_raw_hyst_value(
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_ROT:
ret_type = hid_sensor_read_raw_hyst_value(
&magn_state->rot_attributes, val, val2);
break;
default:
ret_type = -EINVAL;
}
break;
default:
ret_type = -EINVAL;
break;
}
return ret_type;
}
/* Channel write_raw handler */
static int magn_3d_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct magn_3d_state *magn_state = iio_priv(indio_dev);
int ret = 0;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = hid_sensor_write_samp_freq_value(
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_CHAN_INFO_HYSTERESIS:
switch (chan->type) {
case IIO_MAGN:
ret = hid_sensor_write_raw_hyst_value(
&magn_state->magn_flux_attributes, val, val2);
break;
case IIO_ROT:
ret = hid_sensor_write_raw_hyst_value(
&magn_state->rot_attributes, val, val2);
break;
default:
ret = -EINVAL;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
static const struct iio_info magn_3d_info = {
.read_raw = &magn_3d_read_raw,
.write_raw = &magn_3d_write_raw,
};
/* Callback handler to send event after all samples are received and captured */
static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
u32 usage_id,
void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct magn_3d_state *magn_state = iio_priv(indio_dev);
dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n");
if (atomic_read(&magn_state->magn_flux_attributes.data_ready)) {
if (!magn_state->timestamp)
magn_state->timestamp = iio_get_time_ns(indio_dev);
iio_push_to_buffers_with_timestamp(indio_dev,
magn_state->iio_vals,
magn_state->timestamp);
magn_state->timestamp = 0;
}
return 0;
}
/* Capture samples in local storage */
static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
u32 usage_id,
size_t raw_len, char *raw_data,
void *priv)
{
struct iio_dev *indio_dev = platform_get_drvdata(priv);
struct magn_3d_state *magn_state = iio_priv(indio_dev);
int offset;
int ret = 0;
u32 *iio_val = NULL;
switch (usage_id) {
case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
offset = (usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS)
+ CHANNEL_SCAN_INDEX_X;
break;
case HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH:
case HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH:
case HID_USAGE_SENSOR_ORIENT_MAGN_NORTH:
case HID_USAGE_SENSOR_ORIENT_TRUE_NORTH:
offset = (usage_id - HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH)
+ CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP;
break;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
magn_state->timestamp =
hid_sensor_convert_timestamp(&magn_state->magn_flux_attributes,
*(s64 *)raw_data);
return ret;
default:
return -EINVAL;
}
iio_val = magn_state->magn_val_addr[offset];
if (iio_val != NULL)
*iio_val = *((u32 *)raw_data);
else
ret = -EINVAL;
return ret;
}
/* Parse report which is specific to an usage id*/
static int magn_3d_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
struct iio_chan_spec **channels,
int *chan_count,
u32 usage_id,
struct magn_3d_state *st)
{
int i;
int attr_count = 0;
struct iio_chan_spec *_channels;
/* Scan for each usage attribute supported */
for (i = 0; i < MAGN_3D_CHANNEL_MAX; i++) {
int status;
u32 address = magn_3d_addresses[i];
/* Check if usage attribute exists in the sensor hub device */
status = sensor_hub_input_get_attribute_info(hsdev,
HID_INPUT_REPORT,
usage_id,
address,
&(st->magn[i]));
if (!status)
attr_count++;
}
if (attr_count <= 0) {
dev_err(&pdev->dev,
"failed to find any supported usage attributes in report\n");
return -EINVAL;
}
dev_dbg(&pdev->dev, "magn_3d Found %d usage attributes\n",
attr_count);
dev_dbg(&pdev->dev, "magn_3d X: %x:%x Y: %x:%x Z: %x:%x\n",
st->magn[0].index,
st->magn[0].report_id,
st->magn[1].index, st->magn[1].report_id,
st->magn[2].index, st->magn[2].report_id);
/* Setup IIO channel array */
_channels = devm_kcalloc(&pdev->dev, attr_count,
sizeof(struct iio_chan_spec),
GFP_KERNEL);
if (!_channels) {
dev_err(&pdev->dev,
"failed to allocate space for iio channels\n");
return -ENOMEM;
}
/* attr_count include timestamp channel, and the iio_vals should be aligned to 8byte */
st->iio_vals = devm_kcalloc(&pdev->dev,
((attr_count + 1) % 2 + (attr_count + 1) / 2) * 2,
sizeof(u32), GFP_KERNEL);
if (!st->iio_vals) {
dev_err(&pdev->dev,
"failed to allocate space for iio values array\n");
return -ENOMEM;
}
for (i = 0, *chan_count = 0;
i < MAGN_3D_CHANNEL_MAX && *chan_count < attr_count;
i++){
if (st->magn[i].index >= 0) {
/* Setup IIO channel struct */
(_channels[*chan_count]) = magn_3d_channels[i];
(_channels[*chan_count]).scan_index = *chan_count;
(_channels[*chan_count]).address = i;
if (i != CHANNEL_SCAN_INDEX_TIMESTAMP) {
/* Set magn_val_addr to iio value address */
st->magn_val_addr[i] = &st->iio_vals[*chan_count];
_channels[*chan_count].scan_type = (struct iio_scan_type) {
.format = 's',
.realbits = BYTES_TO_BITS(st->magn[i].size),
.storagebits = BITS_PER_TYPE(u32),
};
}
(*chan_count)++;
}
}
if (*chan_count <= 0) {
dev_err(&pdev->dev,
"failed to find any magnetic channels setup\n");
return -EINVAL;
}
*channels = _channels;
dev_dbg(&pdev->dev, "magn_3d Setup %d IIO channels\n",
*chan_count);
st->magn_flux_attr.scale_precision = hid_sensor_format_scale(
HID_USAGE_SENSOR_COMPASS_3D,
&st->magn[CHANNEL_SCAN_INDEX_X],
&st->magn_flux_attr.scale_pre_decml,
&st->magn_flux_attr.scale_post_decml);
st->rot_attr.scale_precision
= hid_sensor_format_scale(
HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
&st->magn[CHANNEL_SCAN_INDEX_NORTH_MAGN_TILT_COMP],
&st->rot_attr.scale_pre_decml,
&st->rot_attr.scale_post_decml);
if (st->rot_attributes.sensitivity.index < 0) {
sensor_hub_input_get_attribute_info(hsdev,
HID_FEATURE_REPORT, usage_id,
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH,
&st->rot_attributes.sensitivity);
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
st->rot_attributes.sensitivity.index,
st->rot_attributes.sensitivity.report_id);
}
return 0;
}
/* Function to initialize the processing for usage id */
static int hid_magn_3d_probe(struct platform_device *pdev)
{
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
int ret = 0;
static char *name = "magn_3d";
struct iio_dev *indio_dev;
struct magn_3d_state *magn_state;
struct iio_chan_spec *channels;
int chan_count = 0;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct magn_3d_state));
if (indio_dev == NULL)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
magn_state = iio_priv(indio_dev);
magn_state->magn_flux_attributes.hsdev = hsdev;
magn_state->magn_flux_attributes.pdev = pdev;
ret = hid_sensor_parse_common_attributes(hsdev,
HID_USAGE_SENSOR_COMPASS_3D,
&magn_state->magn_flux_attributes,
magn_3d_sensitivity_addresses,
ARRAY_SIZE(magn_3d_sensitivity_addresses));
if (ret) {
dev_err(&pdev->dev, "failed to setup common attributes\n");
return ret;
}
magn_state->rot_attributes = magn_state->magn_flux_attributes;
/* sensitivity of rot_attribute is not the same as magn_flux_attributes */
magn_state->rot_attributes.sensitivity.index = -1;
ret = magn_3d_parse_report(pdev, hsdev,
&channels, &chan_count,
HID_USAGE_SENSOR_COMPASS_3D, magn_state);
if (ret) {
dev_err(&pdev->dev, "failed to parse report\n");
return ret;
}
indio_dev->channels = channels;
indio_dev->num_channels = chan_count;
indio_dev->info = &magn_3d_info;
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
atomic_set(&magn_state->magn_flux_attributes.data_ready, 0);
ret = hid_sensor_setup_trigger(indio_dev, name,
&magn_state->magn_flux_attributes);
if (ret < 0) {
dev_err(&pdev->dev, "trigger setup failed\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "device register failed\n");
goto error_remove_trigger;
}
magn_state->callbacks.send_event = magn_3d_proc_event;
magn_state->callbacks.capture_sample = magn_3d_capture_sample;
magn_state->callbacks.pdev = pdev;
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
&magn_state->callbacks);
if (ret < 0) {
dev_err(&pdev->dev, "callback reg failed\n");
goto error_iio_unreg;
}
return ret;
error_iio_unreg:
iio_device_unregister(indio_dev);
error_remove_trigger:
hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
return ret;
}
/* Function to deinitialize the processing for usage id */
static void hid_magn_3d_remove(struct platform_device *pdev)
{
struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct magn_3d_state *magn_state = iio_priv(indio_dev);
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
iio_device_unregister(indio_dev);
hid_sensor_remove_trigger(indio_dev, &magn_state->magn_flux_attributes);
}
static const struct platform_device_id hid_magn_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200083",
},
{ }
};
MODULE_DEVICE_TABLE(platform, hid_magn_3d_ids);
static struct platform_driver hid_magn_3d_platform_driver = {
.id_table = hid_magn_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
.pm = &hid_sensor_pm_ops,
},
.probe = hid_magn_3d_probe,
.remove = hid_magn_3d_remove,
};
module_platform_driver(hid_magn_3d_platform_driver);
MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_HID");
@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Header file for hmc5843 driver
*
* Split from hmc5843.c
* Copyright (C) Josef Gajdusek <atx@atx.name>
*/
#ifndef HMC5843_CORE_H
#define HMC5843_CORE_H
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#define HMC5843_CONFIG_REG_A 0x00
#define HMC5843_CONFIG_REG_B 0x01
#define HMC5843_MODE_REG 0x02
#define HMC5843_DATA_OUT_MSB_REGS 0x03
#define HMC5843_STATUS_REG 0x09
#define HMC5843_ID_REG 0x0a
#define HMC5843_ID_END 0x0c
enum hmc5843_ids {
HMC5843_ID,
HMC5883_ID,
HMC5883L_ID,
HMC5983_ID,
};
/**
* struct hmc5843_data - device specific data
* @dev: actual device
* @lock: update and read regmap data
* @regmap: hardware access register maps
* @variant: describe chip variants
* @scan: buffer to pack data for passing to
* iio_push_to_buffers_with_ts()
*/
struct hmc5843_data {
struct device *dev;
struct mutex lock;
struct regmap *regmap;
const struct hmc5843_chip_info *variant;
struct iio_mount_matrix orientation;
struct {
__be16 chans[3];
aligned_s64 timestamp;
} scan;
};
int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
enum hmc5843_ids id, const char *name);
void hmc5843_common_remove(struct device *dev);
extern const struct dev_pm_ops hmc5843_pm_ops;
#endif /* HMC5843_CORE_H */
@@ -0,0 +1,688 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Device driver for the HMC5843 multi-chip module designed
* for low field magnetic sensing.
*
* Copyright (C) 2010 Texas Instruments
*
* Author: Shubhrajyoti Datta <shubhrajyoti@ti.com>
* Acknowledgment: Jonathan Cameron <jic23@kernel.org> for valuable inputs.
* Support for HMC5883 and HMC5883L by Peter Meerwald <pmeerw@pmeerw.net>.
* Split to multiple files by Josef Gajdusek <atx@atx.name> - 2014
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/delay.h>
#include "hmc5843.h"
/*
* Range gain settings in (+-)Ga
* Beware: HMC5843 and HMC5883 have different recommended sensor field
* ranges; default corresponds to +-1.0 Ga and +-1.3 Ga, respectively
*/
#define HMC5843_RANGE_GAIN_OFFSET 0x05
#define HMC5843_RANGE_GAIN_DEFAULT 0x01
#define HMC5843_RANGE_GAIN_MASK 0xe0
/* Device status */
#define HMC5843_DATA_READY 0x01
#define HMC5843_DATA_OUTPUT_LOCK 0x02
/* Mode register configuration */
#define HMC5843_MODE_CONVERSION_CONTINUOUS 0x00
#define HMC5843_MODE_CONVERSION_SINGLE 0x01
#define HMC5843_MODE_IDLE 0x02
#define HMC5843_MODE_SLEEP 0x03
#define HMC5843_MODE_MASK 0x03
/*
* HMC5843: Minimum data output rate
* HMC5883: Typical data output rate
*/
#define HMC5843_RATE_OFFSET 0x02
#define HMC5843_RATE_DEFAULT 0x04
#define HMC5843_RATE_MASK 0x1c
/* Device measurement configuration */
#define HMC5843_MEAS_CONF_NORMAL 0x00
#define HMC5843_MEAS_CONF_POSITIVE_BIAS 0x01
#define HMC5843_MEAS_CONF_NEGATIVE_BIAS 0x02
#define HMC5843_MEAS_CONF_MASK 0x03
/*
* API for setting the measurement configuration to
* Normal, Positive bias and Negative bias
*
* From the datasheet:
* 0 - Normal measurement configuration (default): In normal measurement
* configuration the device follows normal measurement flow. Pins BP
* and BN are left floating and high impedance.
*
* 1 - Positive bias configuration: In positive bias configuration, a
* positive current is forced across the resistive load on pins BP
* and BN.
*
* 2 - Negative bias configuration. In negative bias configuration, a
* negative current is forced across the resistive load on pins BP
* and BN.
*
* 3 - Only available on HMC5983. Magnetic sensor is disabled.
* Temperature sensor is enabled.
*/
static const char *const hmc5843_meas_conf_modes[] = {"normal", "positivebias",
"negativebias"};
static const char *const hmc5983_meas_conf_modes[] = {"normal", "positivebias",
"negativebias",
"disabled"};
/* Scaling factors: 10000000/Gain */
static const int hmc5843_regval_to_nanoscale[] = {
6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714
};
static const int hmc5883_regval_to_nanoscale[] = {
7812, 9766, 13021, 16287, 24096, 27701, 32573, 45662
};
static const int hmc5883l_regval_to_nanoscale[] = {
7299, 9174, 12195, 15152, 22727, 25641, 30303, 43478
};
/*
* From the datasheet:
* Value | HMC5843 | HMC5883/HMC5883L
* | Data output rate (Hz) | Data output rate (Hz)
* 0 | 0.5 | 0.75
* 1 | 1 | 1.5
* 2 | 2 | 3
* 3 | 5 | 7.5
* 4 | 10 (default) | 15
* 5 | 20 | 30
* 6 | 50 | 75
* 7 | Not used | Not used
*/
static const int hmc5843_regval_to_samp_freq[][2] = {
{0, 500000}, {1, 0}, {2, 0}, {5, 0}, {10, 0}, {20, 0}, {50, 0}
};
static const int hmc5883_regval_to_samp_freq[][2] = {
{0, 750000}, {1, 500000}, {3, 0}, {7, 500000}, {15, 0}, {30, 0},
{75, 0}
};
static const int hmc5983_regval_to_samp_freq[][2] = {
{0, 750000}, {1, 500000}, {3, 0}, {7, 500000}, {15, 0}, {30, 0},
{75, 0}, {220, 0}
};
/* Describe chip variants */
struct hmc5843_chip_info {
const struct iio_chan_spec *channels;
const int (*regval_to_samp_freq)[2];
const int n_regval_to_samp_freq;
const int *regval_to_nanoscale;
const int n_regval_to_nanoscale;
};
/* The lower two bits contain the current conversion mode */
static s32 hmc5843_set_mode(struct hmc5843_data *data, u8 operating_mode)
{
int ret;
mutex_lock(&data->lock);
ret = regmap_update_bits(data->regmap, HMC5843_MODE_REG,
HMC5843_MODE_MASK, operating_mode);
mutex_unlock(&data->lock);
return ret;
}
static int hmc5843_wait_measurement(struct hmc5843_data *data)
{
int tries = 150;
unsigned int val;
int ret;
while (tries-- > 0) {
ret = regmap_read(data->regmap, HMC5843_STATUS_REG, &val);
if (ret < 0)
return ret;
if (val & HMC5843_DATA_READY)
break;
msleep(20);
}
if (tries < 0) {
dev_err(data->dev, "data not ready\n");
return -EIO;
}
return 0;
}
/* Return the measurement value from the specified channel */
static int hmc5843_read_measurement(struct hmc5843_data *data,
int idx, int *val)
{
__be16 values[3];
int ret;
mutex_lock(&data->lock);
ret = hmc5843_wait_measurement(data);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
}
ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS,
values, sizeof(values));
mutex_unlock(&data->lock);
if (ret < 0)
return ret;
*val = sign_extend32(be16_to_cpu(values[idx]), 15);
return IIO_VAL_INT;
}
static int hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf)
{
int ret;
mutex_lock(&data->lock);
ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_A,
HMC5843_MEAS_CONF_MASK, meas_conf);
mutex_unlock(&data->lock);
return ret;
}
static
int hmc5843_show_measurement_configuration(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct hmc5843_data *data = iio_priv(indio_dev);
unsigned int val;
int ret;
ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &val);
if (ret)
return ret;
return val & HMC5843_MEAS_CONF_MASK;
}
static
int hmc5843_set_measurement_configuration(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int meas_conf)
{
struct hmc5843_data *data = iio_priv(indio_dev);
return hmc5843_set_meas_conf(data, meas_conf);
}
static const struct iio_mount_matrix *
hmc5843_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct hmc5843_data *data = iio_priv(indio_dev);
return &data->orientation;
}
static const struct iio_enum hmc5843_meas_conf_enum = {
.items = hmc5843_meas_conf_modes,
.num_items = ARRAY_SIZE(hmc5843_meas_conf_modes),
.get = hmc5843_show_measurement_configuration,
.set = hmc5843_set_measurement_configuration,
};
static const struct iio_chan_spec_ext_info hmc5843_ext_info[] = {
IIO_ENUM("meas_conf", IIO_SHARED_BY_TYPE, &hmc5843_meas_conf_enum),
IIO_ENUM_AVAILABLE("meas_conf", IIO_SHARED_BY_TYPE, &hmc5843_meas_conf_enum),
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix),
{ }
};
static const struct iio_enum hmc5983_meas_conf_enum = {
.items = hmc5983_meas_conf_modes,
.num_items = ARRAY_SIZE(hmc5983_meas_conf_modes),
.get = hmc5843_show_measurement_configuration,
.set = hmc5843_set_measurement_configuration,
};
static const struct iio_chan_spec_ext_info hmc5983_ext_info[] = {
IIO_ENUM("meas_conf", IIO_SHARED_BY_TYPE, &hmc5983_meas_conf_enum),
IIO_ENUM_AVAILABLE("meas_conf", IIO_SHARED_BY_TYPE, &hmc5983_meas_conf_enum),
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix),
{ }
};
static
ssize_t hmc5843_show_samp_freq_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
size_t len = 0;
int i;
for (i = 0; i < data->variant->n_regval_to_samp_freq; i++)
len += scnprintf(buf + len, PAGE_SIZE - len,
"%d.%d ", data->variant->regval_to_samp_freq[i][0],
data->variant->regval_to_samp_freq[i][1]);
/* replace trailing space by newline */
buf[len - 1] = '\n';
return len;
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hmc5843_show_samp_freq_avail);
static int hmc5843_set_samp_freq(struct hmc5843_data *data, u8 rate)
{
int ret;
mutex_lock(&data->lock);
ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_A,
HMC5843_RATE_MASK,
rate << HMC5843_RATE_OFFSET);
mutex_unlock(&data->lock);
return ret;
}
static int hmc5843_get_samp_freq_index(struct hmc5843_data *data,
int val, int val2)
{
int i;
for (i = 0; i < data->variant->n_regval_to_samp_freq; i++)
if (val == data->variant->regval_to_samp_freq[i][0] &&
val2 == data->variant->regval_to_samp_freq[i][1])
return i;
return -EINVAL;
}
static int hmc5843_set_range_gain(struct hmc5843_data *data, u8 range)
{
int ret;
mutex_lock(&data->lock);
ret = regmap_update_bits(data->regmap, HMC5843_CONFIG_REG_B,
HMC5843_RANGE_GAIN_MASK,
range << HMC5843_RANGE_GAIN_OFFSET);
mutex_unlock(&data->lock);
return ret;
}
static ssize_t hmc5843_show_scale_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
size_t len = 0;
int i;
for (i = 0; i < data->variant->n_regval_to_nanoscale; i++)
len += scnprintf(buf + len, PAGE_SIZE - len,
"0.%09d ", data->variant->regval_to_nanoscale[i]);
/* replace trailing space by newline */
buf[len - 1] = '\n';
return len;
}
static IIO_DEVICE_ATTR(scale_available, S_IRUGO,
hmc5843_show_scale_avail, NULL, 0);
static int hmc5843_get_scale_index(struct hmc5843_data *data, int val, int val2)
{
int i;
if (val)
return -EINVAL;
for (i = 0; i < data->variant->n_regval_to_nanoscale; i++)
if (val2 == data->variant->regval_to_nanoscale[i])
return i;
return -EINVAL;
}
static int hmc5843_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct hmc5843_data *data = iio_priv(indio_dev);
unsigned int rval;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
return hmc5843_read_measurement(data, chan->scan_index, val);
case IIO_CHAN_INFO_SCALE:
ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_B, &rval);
if (ret < 0)
return ret;
rval >>= HMC5843_RANGE_GAIN_OFFSET;
*val = 0;
*val2 = data->variant->regval_to_nanoscale[rval];
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &rval);
if (ret < 0)
return ret;
rval >>= HMC5843_RATE_OFFSET;
*val = data->variant->regval_to_samp_freq[rval][0];
*val2 = data->variant->regval_to_samp_freq[rval][1];
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
static int hmc5843_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct hmc5843_data *data = iio_priv(indio_dev);
int rate, range;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
rate = hmc5843_get_samp_freq_index(data, val, val2);
if (rate < 0)
return -EINVAL;
return hmc5843_set_samp_freq(data, rate);
case IIO_CHAN_INFO_SCALE:
range = hmc5843_get_scale_index(data, val, val2);
if (range < 0)
return -EINVAL;
return hmc5843_set_range_gain(data, range);
default:
return -EINVAL;
}
}
static int hmc5843_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static irqreturn_t hmc5843_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct hmc5843_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->lock);
ret = hmc5843_wait_measurement(data);
if (ret < 0) {
mutex_unlock(&data->lock);
goto done;
}
ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS,
data->scan.chans, sizeof(data->scan.chans));
mutex_unlock(&data->lock);
if (ret < 0)
goto done;
iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan),
iio_get_time_ns(indio_dev));
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
#define HMC5843_CHANNEL(axis, idx) \
{ \
.type = IIO_MAGN, \
.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 = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
.ext_info = hmc5843_ext_info, \
}
#define HMC5983_CHANNEL(axis, idx) \
{ \
.type = IIO_MAGN, \
.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 = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
.ext_info = hmc5983_ext_info, \
}
static const struct iio_chan_spec hmc5843_channels[] = {
HMC5843_CHANNEL(X, 0),
HMC5843_CHANNEL(Y, 1),
HMC5843_CHANNEL(Z, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
/* Beware: Y and Z are exchanged on HMC5883 and 5983 */
static const struct iio_chan_spec hmc5883_channels[] = {
HMC5843_CHANNEL(X, 0),
HMC5843_CHANNEL(Z, 1),
HMC5843_CHANNEL(Y, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec hmc5983_channels[] = {
HMC5983_CHANNEL(X, 0),
HMC5983_CHANNEL(Z, 1),
HMC5983_CHANNEL(Y, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static struct attribute *hmc5843_attributes[] = {
&iio_dev_attr_scale_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group hmc5843_group = {
.attrs = hmc5843_attributes,
};
static const struct hmc5843_chip_info hmc5843_chip_info_tbl[] = {
[HMC5843_ID] = {
.channels = hmc5843_channels,
.regval_to_samp_freq = hmc5843_regval_to_samp_freq,
.n_regval_to_samp_freq =
ARRAY_SIZE(hmc5843_regval_to_samp_freq),
.regval_to_nanoscale = hmc5843_regval_to_nanoscale,
.n_regval_to_nanoscale =
ARRAY_SIZE(hmc5843_regval_to_nanoscale),
},
[HMC5883_ID] = {
.channels = hmc5883_channels,
.regval_to_samp_freq = hmc5883_regval_to_samp_freq,
.n_regval_to_samp_freq =
ARRAY_SIZE(hmc5883_regval_to_samp_freq),
.regval_to_nanoscale = hmc5883_regval_to_nanoscale,
.n_regval_to_nanoscale =
ARRAY_SIZE(hmc5883_regval_to_nanoscale),
},
[HMC5883L_ID] = {
.channels = hmc5883_channels,
.regval_to_samp_freq = hmc5883_regval_to_samp_freq,
.n_regval_to_samp_freq =
ARRAY_SIZE(hmc5883_regval_to_samp_freq),
.regval_to_nanoscale = hmc5883l_regval_to_nanoscale,
.n_regval_to_nanoscale =
ARRAY_SIZE(hmc5883l_regval_to_nanoscale),
},
[HMC5983_ID] = {
.channels = hmc5983_channels,
.regval_to_samp_freq = hmc5983_regval_to_samp_freq,
.n_regval_to_samp_freq =
ARRAY_SIZE(hmc5983_regval_to_samp_freq),
.regval_to_nanoscale = hmc5883l_regval_to_nanoscale,
.n_regval_to_nanoscale =
ARRAY_SIZE(hmc5883l_regval_to_nanoscale),
}
};
static int hmc5843_init(struct hmc5843_data *data)
{
int ret;
u8 id[3];
ret = regmap_bulk_read(data->regmap, HMC5843_ID_REG,
id, ARRAY_SIZE(id));
if (ret < 0)
return ret;
if (id[0] != 'H' || id[1] != '4' || id[2] != '3') {
dev_err(data->dev, "no HMC5843/5883/5883L/5983 sensor\n");
return -ENODEV;
}
ret = hmc5843_set_meas_conf(data, HMC5843_MEAS_CONF_NORMAL);
if (ret < 0)
return ret;
ret = hmc5843_set_samp_freq(data, HMC5843_RATE_DEFAULT);
if (ret < 0)
return ret;
ret = hmc5843_set_range_gain(data, HMC5843_RANGE_GAIN_DEFAULT);
if (ret < 0)
return ret;
return hmc5843_set_mode(data, HMC5843_MODE_CONVERSION_CONTINUOUS);
}
static const struct iio_info hmc5843_info = {
.attrs = &hmc5843_group,
.read_raw = &hmc5843_read_raw,
.write_raw = &hmc5843_write_raw,
.write_raw_get_fmt = &hmc5843_write_raw_get_fmt,
};
static const unsigned long hmc5843_scan_masks[] = {0x7, 0};
static int hmc5843_common_suspend(struct device *dev)
{
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_SLEEP);
}
static int hmc5843_common_resume(struct device *dev)
{
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_CONVERSION_CONTINUOUS);
}
EXPORT_NS_SIMPLE_DEV_PM_OPS(hmc5843_pm_ops, hmc5843_common_suspend,
hmc5843_common_resume, IIO_HMC5843);
int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
enum hmc5843_ids id, const char *name)
{
struct hmc5843_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
dev_set_drvdata(dev, indio_dev);
/* default settings at probe */
data = iio_priv(indio_dev);
data->dev = dev;
data->regmap = regmap;
data->variant = &hmc5843_chip_info_tbl[id];
mutex_init(&data->lock);
ret = iio_read_mount_matrix(dev, &data->orientation);
if (ret)
return ret;
indio_dev->name = name;
indio_dev->info = &hmc5843_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = data->variant->channels;
indio_dev->num_channels = 4;
indio_dev->available_scan_masks = hmc5843_scan_masks;
ret = hmc5843_init(data);
if (ret < 0)
return ret;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
hmc5843_trigger_handler, NULL);
if (ret < 0)
goto buffer_setup_err;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto buffer_cleanup;
return 0;
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
buffer_setup_err:
hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
return ret;
}
EXPORT_SYMBOL_NS(hmc5843_common_probe, "IIO_HMC5843");
void hmc5843_common_remove(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
/* sleep mode to save power */
hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP);
}
EXPORT_SYMBOL_NS(hmc5843_common_remove, "IIO_HMC5843");
MODULE_AUTHOR("Shubhrajyoti Datta <shubhrajyoti@ti.com>");
MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 core driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* i2c driver for hmc5843/5843/5883/5883l/5983
*
* Split from hmc5843.c
* Copyright (C) Josef Gajdusek <atx@atx.name>
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include "hmc5843.h"
static const struct regmap_range hmc5843_readable_ranges[] = {
regmap_reg_range(0, HMC5843_ID_END),
};
static const struct regmap_access_table hmc5843_readable_table = {
.yes_ranges = hmc5843_readable_ranges,
.n_yes_ranges = ARRAY_SIZE(hmc5843_readable_ranges),
};
static const struct regmap_range hmc5843_writable_ranges[] = {
regmap_reg_range(0, HMC5843_MODE_REG),
};
static const struct regmap_access_table hmc5843_writable_table = {
.yes_ranges = hmc5843_writable_ranges,
.n_yes_ranges = ARRAY_SIZE(hmc5843_writable_ranges),
};
static const struct regmap_range hmc5843_volatile_ranges[] = {
regmap_reg_range(HMC5843_DATA_OUT_MSB_REGS, HMC5843_STATUS_REG),
};
static const struct regmap_access_table hmc5843_volatile_table = {
.yes_ranges = hmc5843_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(hmc5843_volatile_ranges),
};
static const struct regmap_config hmc5843_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &hmc5843_readable_table,
.wr_table = &hmc5843_writable_table,
.volatile_table = &hmc5843_volatile_table,
.cache_type = REGCACHE_RBTREE,
};
static int hmc5843_i2c_probe(struct i2c_client *cli)
{
const struct i2c_device_id *id = i2c_client_get_device_id(cli);
struct regmap *regmap = devm_regmap_init_i2c(cli,
&hmc5843_i2c_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return hmc5843_common_probe(&cli->dev,
regmap,
id->driver_data, id->name);
}
static void hmc5843_i2c_remove(struct i2c_client *client)
{
hmc5843_common_remove(&client->dev);
}
static const struct i2c_device_id hmc5843_id[] = {
{ .name = "hmc5843", .driver_data = HMC5843_ID },
{ .name = "hmc5883", .driver_data = HMC5883_ID },
{ .name = "hmc5883l", .driver_data = HMC5883L_ID },
{ .name = "hmc5983", .driver_data = HMC5983_ID },
{ }
};
MODULE_DEVICE_TABLE(i2c, hmc5843_id);
static const struct of_device_id hmc5843_of_match[] = {
{ .compatible = "honeywell,hmc5843", .data = (void *)HMC5843_ID },
{ .compatible = "honeywell,hmc5883", .data = (void *)HMC5883_ID },
{ .compatible = "honeywell,hmc5883l", .data = (void *)HMC5883L_ID },
{ .compatible = "honeywell,hmc5983", .data = (void *)HMC5983_ID },
{ }
};
MODULE_DEVICE_TABLE(of, hmc5843_of_match);
static struct i2c_driver hmc5843_driver = {
.driver = {
.name = "hmc5843",
.pm = pm_sleep_ptr(&hmc5843_pm_ops),
.of_match_table = hmc5843_of_match,
},
.id_table = hmc5843_id,
.probe = hmc5843_i2c_probe,
.remove = hmc5843_i2c_remove,
};
module_i2c_driver(hmc5843_driver);
MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
MODULE_DESCRIPTION("HMC5843/5883/5883L/5983 i2c driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_HMC5843");
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SPI driver for hmc5983
*
* Copyright (C) Josef Gajdusek <atx@atx.name>
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include "hmc5843.h"
static const struct regmap_range hmc5843_readable_ranges[] = {
regmap_reg_range(0, HMC5843_ID_END),
};
static const struct regmap_access_table hmc5843_readable_table = {
.yes_ranges = hmc5843_readable_ranges,
.n_yes_ranges = ARRAY_SIZE(hmc5843_readable_ranges),
};
static const struct regmap_range hmc5843_writable_ranges[] = {
regmap_reg_range(0, HMC5843_MODE_REG),
};
static const struct regmap_access_table hmc5843_writable_table = {
.yes_ranges = hmc5843_writable_ranges,
.n_yes_ranges = ARRAY_SIZE(hmc5843_writable_ranges),
};
static const struct regmap_range hmc5843_volatile_ranges[] = {
regmap_reg_range(HMC5843_DATA_OUT_MSB_REGS, HMC5843_STATUS_REG),
};
static const struct regmap_access_table hmc5843_volatile_table = {
.yes_ranges = hmc5843_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(hmc5843_volatile_ranges),
};
static const struct regmap_config hmc5843_spi_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &hmc5843_readable_table,
.wr_table = &hmc5843_writable_table,
.volatile_table = &hmc5843_volatile_table,
/* Autoincrement address pointer */
.read_flag_mask = 0xc0,
.cache_type = REGCACHE_RBTREE,
};
static int hmc5843_spi_probe(struct spi_device *spi)
{
int ret;
struct regmap *regmap;
const struct spi_device_id *id = spi_get_device_id(spi);
spi->mode = SPI_MODE_3;
spi->max_speed_hz = 8000000;
ret = spi_setup(spi);
if (ret)
return ret;
regmap = devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return hmc5843_common_probe(&spi->dev,
regmap,
id->driver_data, id->name);
}
static void hmc5843_spi_remove(struct spi_device *spi)
{
hmc5843_common_remove(&spi->dev);
}
static const struct spi_device_id hmc5843_id[] = {
{ "hmc5983", HMC5983_ID },
{ }
};
MODULE_DEVICE_TABLE(spi, hmc5843_id);
static struct spi_driver hmc5843_driver = {
.driver = {
.name = "hmc5843",
.pm = pm_sleep_ptr(&hmc5843_pm_ops),
},
.id_table = hmc5843_id,
.probe = hmc5843_spi_probe,
.remove = hmc5843_spi_remove,
};
module_spi_driver(hmc5843_driver);
MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
MODULE_DESCRIPTION("HMC5983 SPI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_HMC5843");
@@ -0,0 +1,647 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* mag3110.c - Support for Freescale MAG3110 magnetometer sensor
*
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
*
* (7-bit I2C slave address 0x0e)
*
* TODO: irq, user offset, oversampling, continuous mode
*/
#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#define MAG3110_STATUS 0x00
#define MAG3110_OUT_X 0x01 /* MSB first */
#define MAG3110_OUT_Y 0x03
#define MAG3110_OUT_Z 0x05
#define MAG3110_WHO_AM_I 0x07
#define MAG3110_SYSMOD 0x08
#define MAG3110_OFF_X 0x09 /* MSB first */
#define MAG3110_OFF_Y 0x0b
#define MAG3110_OFF_Z 0x0d
#define MAG3110_DIE_TEMP 0x0f
#define MAG3110_CTRL_REG1 0x10
#define MAG3110_CTRL_REG2 0x11
#define MAG3110_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
#define MAG3110_CTRL_DR_MASK (BIT(7) | BIT(6) | BIT(5))
#define MAG3110_CTRL_DR_SHIFT 5
#define MAG3110_CTRL_DR_DEFAULT 0
#define MAG3110_SYSMOD_MODE_MASK GENMASK(1, 0)
#define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */
#define MAG3110_CTRL_AC BIT(0) /* continuous measurements */
#define MAG3110_CTRL_AUTO_MRST_EN BIT(7) /* magnetic auto-reset */
#define MAG3110_CTRL_RAW BIT(5) /* measurements not user-offset corrected */
#define MAG3110_DEVICE_ID 0xc4
/* Each client has this additional data */
struct mag3110_data {
struct i2c_client *client;
struct mutex lock;
u8 ctrl_reg1;
int sleep_val;
struct regulator *vdd_reg;
struct regulator *vddio_reg;
/* Ensure natural alignment of timestamp */
struct {
__be16 channels[3];
u8 temperature;
aligned_s64 ts;
} scan;
};
static int mag3110_request(struct mag3110_data *data)
{
int ret, tries = 150;
if ((data->ctrl_reg1 & MAG3110_CTRL_AC) == 0) {
/* trigger measurement */
ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
data->ctrl_reg1 | MAG3110_CTRL_TM);
if (ret < 0)
return ret;
}
while (tries-- > 0) {
ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS);
if (ret < 0)
return ret;
/* wait for data ready */
if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY)
break;
if (data->sleep_val <= 20)
usleep_range(data->sleep_val * 250, data->sleep_val * 500);
else
msleep(20);
}
if (tries < 0) {
dev_err(&data->client->dev, "data not ready\n");
return -EIO;
}
return 0;
}
static int mag3110_read(struct mag3110_data *data, __be16 buf[3])
{
int ret;
guard(mutex)(&data->lock);
ret = mag3110_request(data);
if (ret < 0)
return ret;
return i2c_smbus_read_i2c_block_data(data->client, MAG3110_OUT_X,
3 * sizeof(__be16), (u8 *) buf);
}
static ssize_t mag3110_show_int_plus_micros(char *buf,
const int (*vals)[2], int n)
{
size_t len = 0;
while (n-- > 0)
len += scnprintf(buf + len, PAGE_SIZE - len,
"%d.%06d ", vals[n][0], vals[n][1]);
/* replace trailing space by newline */
buf[len - 1] = '\n';
return len;
}
static int mag3110_get_int_plus_micros_index(const int (*vals)[2], int n,
int val, int val2)
{
while (n-- > 0)
if (val == vals[n][0] && val2 == vals[n][1])
return n;
return -EINVAL;
}
static const int mag3110_samp_freq[8][2] = {
{80, 0}, {40, 0}, {20, 0}, {10, 0}, {5, 0}, {2, 500000},
{1, 250000}, {0, 625000}
};
static ssize_t mag3110_show_samp_freq_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
return mag3110_show_int_plus_micros(buf, mag3110_samp_freq, 8);
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mag3110_show_samp_freq_avail);
static int mag3110_get_samp_freq_index(struct mag3110_data *data,
int val, int val2)
{
return mag3110_get_int_plus_micros_index(mag3110_samp_freq, 8, val,
val2);
}
static int mag3110_calculate_sleep(struct mag3110_data *data)
{
int ret, i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
if (mag3110_samp_freq[i][0] > 0)
ret = 1000 / mag3110_samp_freq[i][0];
else
ret = 1000;
return ret == 0 ? 1 : ret;
}
static int mag3110_standby(struct mag3110_data *data)
{
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
}
static int mag3110_wait_standby(struct mag3110_data *data)
{
int ret, tries = 30;
/*
* Takes up to 1/ODR to come out of active mode into stby
* Longest expected period is 12.5seconds.
* We'll sleep for 500ms between checks
*/
while (tries-- > 0) {
ret = i2c_smbus_read_byte_data(data->client, MAG3110_SYSMOD);
if (ret < 0) {
dev_err(&data->client->dev, "i2c error\n");
return ret;
}
/* wait for standby */
if ((ret & MAG3110_SYSMOD_MODE_MASK) == 0)
break;
msleep_interruptible(500);
}
if (tries < 0) {
dev_err(&data->client->dev, "device not entering standby mode\n");
return -EIO;
}
return 0;
}
static int mag3110_active(struct mag3110_data *data)
{
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
data->ctrl_reg1);
}
/* returns >0 if active, 0 if in standby and <0 on error */
static int mag3110_is_active(struct mag3110_data *data)
{
int reg;
reg = i2c_smbus_read_byte_data(data->client, MAG3110_CTRL_REG1);
if (reg < 0)
return reg;
return reg & MAG3110_CTRL_AC;
}
static int mag3110_change_config(struct mag3110_data *data, u8 reg, u8 val)
{
int ret;
int is_active;
guard(mutex)(&data->lock);
is_active = mag3110_is_active(data);
if (is_active < 0)
return is_active;
/* config can only be changed when in standby */
if (is_active > 0) {
ret = mag3110_standby(data);
if (ret < 0)
return ret;
}
/*
* After coming out of active we must wait for the part
* to transition to STBY. This can take up to 1 /ODR to occur
*/
ret = mag3110_wait_standby(data);
if (ret < 0)
return ret;
ret = i2c_smbus_write_byte_data(data->client, reg, val);
if (ret < 0)
return ret;
if (is_active > 0) {
ret = mag3110_active(data);
if (ret < 0)
return ret;
}
return 0;
}
static int __mag3110_read_info_raw(struct mag3110_data *data,
struct iio_chan_spec const *chan,
int *val)
{
__be16 buffer[3];
int ret;
switch (chan->type) {
case IIO_MAGN: /* in 0.1 uT / LSB */
ret = mag3110_read(data, buffer);
if (ret < 0)
return ret;
*val = sign_extend32(be16_to_cpu(buffer[chan->scan_index]),
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
case IIO_TEMP: { /* in 1 C / LSB */
guard(mutex)(&data->lock);
ret = mag3110_request(data);
if (ret < 0)
return ret;
ret = i2c_smbus_read_byte_data(data->client,
MAG3110_DIE_TEMP);
if (ret < 0)
return ret;
*val = sign_extend32(ret, chan->scan_type.realbits - 1);
return IIO_VAL_INT;
}
default:
return -EINVAL;
}
}
static int mag3110_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mag3110_data *data = iio_priv(indio_dev);
int i, ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __mag3110_read_info_raw(data, chan, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_MAGN:
*val = 0;
*val2 = 1000;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_TEMP:
*val = 1000;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ:
i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
*val = mag3110_samp_freq[i][0];
*val2 = mag3110_samp_freq[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
ret = i2c_smbus_read_word_swapped(data->client,
MAG3110_OFF_X + 2 * chan->scan_index);
if (ret < 0)
return ret;
*val = sign_extend32(ret >> 1, 14);
return IIO_VAL_INT;
}
return -EINVAL;
}
static int __mag3110_write_raw(struct mag3110_data *data,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int rate;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
rate = mag3110_get_samp_freq_index(data, val, val2);
if (rate < 0)
return -EINVAL;
data->ctrl_reg1 &= 0xff & ~MAG3110_CTRL_DR_MASK
& ~MAG3110_CTRL_AC;
data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
data->sleep_val = mag3110_calculate_sleep(data);
if (data->sleep_val < 40)
data->ctrl_reg1 |= MAG3110_CTRL_AC;
return mag3110_change_config(data, MAG3110_CTRL_REG1,
data->ctrl_reg1);
case IIO_CHAN_INFO_CALIBBIAS:
if (val < -10000 || val > 10000)
return -EINVAL;
return i2c_smbus_write_word_swapped(data->client,
MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
default:
return -EINVAL;
}
}
static int mag3110_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct mag3110_data *data = iio_priv(indio_dev);
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __mag3110_write_raw(data, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}
static irqreturn_t mag3110_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct mag3110_data *data = iio_priv(indio_dev);
int ret;
ret = mag3110_read(data, data->scan.channels);
if (ret < 0)
goto done;
if (test_bit(3, indio_dev->active_scan_mask)) {
ret = i2c_smbus_read_byte_data(data->client,
MAG3110_DIE_TEMP);
if (ret < 0)
goto done;
data->scan.temperature = ret;
}
iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan),
iio_get_time_ns(indio_dev));
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
#define MAG3110_CHANNEL(axis, idx) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}
static const struct iio_chan_spec mag3110_channels[] = {
MAG3110_CHANNEL(X, 0),
MAG3110_CHANNEL(Y, 1),
MAG3110_CHANNEL(Z, 2),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 3,
.scan_type = {
.sign = 's',
.realbits = 8,
.storagebits = 8,
},
},
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static struct attribute *mag3110_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group mag3110_group = {
.attrs = mag3110_attributes,
};
static const struct iio_info mag3110_info = {
.attrs = &mag3110_group,
.read_raw = &mag3110_read_raw,
.write_raw = &mag3110_write_raw,
};
static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
static int mag3110_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct mag3110_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg))
return dev_err_probe(&client->dev, PTR_ERR(data->vdd_reg),
"failed to get VDD regulator!\n");
data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
if (IS_ERR(data->vddio_reg))
return dev_err_probe(&client->dev, PTR_ERR(data->vddio_reg),
"failed to get VDDIO regulator!\n");
ret = regulator_enable(data->vdd_reg);
if (ret) {
dev_err(&client->dev, "failed to enable VDD regulator!\n");
return ret;
}
ret = regulator_enable(data->vddio_reg);
if (ret) {
dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
goto disable_regulator_vdd;
}
ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
if (ret < 0)
goto disable_regulators;
if (ret != MAG3110_DEVICE_ID) {
ret = -ENODEV;
goto disable_regulators;
}
data->client = client;
mutex_init(&data->lock);
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mag3110_info;
indio_dev->name = id->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mag3110_channels;
indio_dev->num_channels = ARRAY_SIZE(mag3110_channels);
indio_dev->available_scan_masks = mag3110_scan_masks;
data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT;
data->sleep_val = mag3110_calculate_sleep(data);
if (data->sleep_val < 40)
data->ctrl_reg1 |= MAG3110_CTRL_AC;
ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1);
if (ret < 0)
goto disable_regulators;
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
MAG3110_CTRL_AUTO_MRST_EN);
if (ret < 0)
goto standby_on_error;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
mag3110_trigger_handler, NULL);
if (ret < 0)
goto standby_on_error;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto buffer_cleanup;
return 0;
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
standby_on_error:
mag3110_standby(iio_priv(indio_dev));
disable_regulators:
regulator_disable(data->vddio_reg);
disable_regulator_vdd:
regulator_disable(data->vdd_reg);
return ret;
}
static void mag3110_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mag3110_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
mag3110_standby(iio_priv(indio_dev));
regulator_disable(data->vddio_reg);
regulator_disable(data->vdd_reg);
}
static int mag3110_suspend(struct device *dev)
{
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
int ret;
ret = mag3110_standby(iio_priv(i2c_get_clientdata(
to_i2c_client(dev))));
if (ret)
return ret;
ret = regulator_disable(data->vddio_reg);
if (ret) {
dev_err(dev, "failed to disable VDDIO regulator\n");
return ret;
}
ret = regulator_disable(data->vdd_reg);
if (ret) {
dev_err(dev, "failed to disable VDD regulator\n");
return ret;
}
return 0;
}
static int mag3110_resume(struct device *dev)
{
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
int ret;
ret = regulator_enable(data->vdd_reg);
if (ret) {
dev_err(dev, "failed to enable VDD regulator\n");
return ret;
}
ret = regulator_enable(data->vddio_reg);
if (ret) {
dev_err(dev, "failed to enable VDDIO regulator\n");
regulator_disable(data->vdd_reg);
return ret;
}
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
data->ctrl_reg1);
}
static DEFINE_SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend,
mag3110_resume);
static const struct i2c_device_id mag3110_id[] = {
{ .name = "mag3110" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mag3110_id);
static const struct of_device_id mag3110_of_match[] = {
{ .compatible = "fsl,mag3110" },
{ }
};
MODULE_DEVICE_TABLE(of, mag3110_of_match);
static struct i2c_driver mag3110_driver = {
.driver = {
.name = "mag3110",
.of_match_table = mag3110_of_match,
.pm = pm_sleep_ptr(&mag3110_pm_ops),
},
.probe = mag3110_probe,
.remove = mag3110_remove,
.id_table = mag3110_id,
};
module_i2c_driver(mag3110_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("Freescale MAG3110 magnetometer driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,582 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MMC35240 - MEMSIC 3-axis Magnetic Sensor
*
* Copyright (c) 2015, Intel Corporation.
*
* IIO driver for MMC35240 (7-bit I2C slave address 0x30).
*
* TODO: offset, ACPI, continuous measurement mode, PM
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/pm.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define MMC35240_DRV_NAME "mmc35240"
#define MMC35240_REG_XOUT_L 0x00
#define MMC35240_REG_XOUT_H 0x01
#define MMC35240_REG_YOUT_L 0x02
#define MMC35240_REG_YOUT_H 0x03
#define MMC35240_REG_ZOUT_L 0x04
#define MMC35240_REG_ZOUT_H 0x05
#define MMC35240_REG_STATUS 0x06
#define MMC35240_REG_CTRL0 0x07
#define MMC35240_REG_CTRL1 0x08
#define MMC35240_REG_ID 0x20
#define MMC35240_STATUS_MEAS_DONE_BIT BIT(0)
#define MMC35240_CTRL0_REFILL_BIT BIT(7)
#define MMC35240_CTRL0_RESET_BIT BIT(6)
#define MMC35240_CTRL0_SET_BIT BIT(5)
#define MMC35240_CTRL0_CMM_BIT BIT(1)
#define MMC35240_CTRL0_TM_BIT BIT(0)
/* output resolution bits */
#define MMC35240_CTRL1_BW0_BIT BIT(0)
#define MMC35240_CTRL1_BW1_BIT BIT(1)
#define MMC35240_CTRL1_BW_MASK (MMC35240_CTRL1_BW0_BIT | \
MMC35240_CTRL1_BW1_BIT)
#define MMC35240_CTRL1_BW_SHIFT 0
#define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */
#define MMC35240_WAIT_SET_RESET 1000 /* us */
/*
* Memsic OTP process code piece is put here for reference:
*
* #define OTP_CONVERT(REG) ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006
* 1) For X axis, the COEFFICIENT is always 1.
* 2) For Y axis, the COEFFICIENT is as below:
* f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) |
* (reg_data[2] >> 4)) + 1.0;
* 3) For Z axis, the COEFFICIENT is as below:
* f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35;
* We implemented the OTP logic into driver.
*/
/* scale = 1000 here for Y otp */
#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6)
/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */
#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81)
#define MMC35240_X_COEFF(x) (x)
#define MMC35240_Y_COEFF(y) (y + 1000)
#define MMC35240_Z_COEFF(z) (z + 13500)
#define MMC35240_OTP_START_ADDR 0x1B
enum mmc35240_resolution {
MMC35240_16_BITS_SLOW = 0, /* 7.92 ms */
MMC35240_16_BITS_FAST, /* 4.08 ms */
MMC35240_14_BITS, /* 2.16 ms */
MMC35240_12_BITS, /* 1.20 ms */
};
enum mmc35240_axis {
AXIS_X = 0,
AXIS_Y,
AXIS_Z,
};
static const struct {
int sens[3]; /* sensitivity per X, Y, Z axis */
int nfo; /* null field output */
} mmc35240_props_table[] = {
/* 16 bits, 125Hz ODR */
{
{1024, 1024, 1024},
32768,
},
/* 16 bits, 250Hz ODR */
{
{1024, 1024, 770},
32768,
},
/* 14 bits, 450Hz ODR */
{
{256, 256, 193},
8192,
},
/* 12 bits, 800Hz ODR */
{
{64, 64, 48},
2048,
},
};
struct mmc35240_data {
struct i2c_client *client;
struct mutex mutex;
struct regmap *regmap;
enum mmc35240_resolution res;
/* OTP compensation */
int axis_coef[3];
int axis_scale[3];
};
static const struct {
int val;
int val2;
} mmc35240_samp_freq[] = { {1, 500000},
{13, 0},
{25, 0},
{50, 0} };
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1.5 13 25 50");
#define MMC35240_CHANNEL(_axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _axis, \
.address = AXIS_ ## _axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec mmc35240_channels[] = {
MMC35240_CHANNEL(X),
MMC35240_CHANNEL(Y),
MMC35240_CHANNEL(Z),
};
static struct attribute *mmc35240_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group mmc35240_attribute_group = {
.attrs = mmc35240_attributes,
};
static int mmc35240_get_samp_freq_index(struct mmc35240_data *data,
int val, int val2)
{
int i;
for (i = 0; i < ARRAY_SIZE(mmc35240_samp_freq); i++)
if (mmc35240_samp_freq[i].val == val &&
mmc35240_samp_freq[i].val2 == val2)
return i;
return -EINVAL;
}
static int mmc35240_hw_set(struct mmc35240_data *data, bool set)
{
int ret;
u8 coil_bit;
/*
* Recharge the capacitor at VCAP pin, requested to be issued
* before a SET/RESET command.
*/
ret = regmap_set_bits(data->regmap, MMC35240_REG_CTRL0,
MMC35240_CTRL0_REFILL_BIT);
if (ret < 0)
return ret;
usleep_range(MMC35240_WAIT_CHARGE_PUMP, MMC35240_WAIT_CHARGE_PUMP + 1);
if (set)
coil_bit = MMC35240_CTRL0_SET_BIT;
else
coil_bit = MMC35240_CTRL0_RESET_BIT;
return regmap_set_bits(data->regmap, MMC35240_REG_CTRL0, coil_bit);
}
static int mmc35240_init(struct mmc35240_data *data)
{
int ret, y_convert, z_convert;
unsigned int reg_id;
u8 otp_data[6];
ret = regmap_read(data->regmap, MMC35240_REG_ID, &reg_id);
if (ret < 0) {
dev_err(&data->client->dev, "Error reading product id\n");
return ret;
}
dev_dbg(&data->client->dev, "MMC35240 chip id %x\n", reg_id);
/*
* make sure we restore sensor characteristics, by doing
* a SET/RESET sequence, the axis polarity being naturally
* aligned after RESET
*/
ret = mmc35240_hw_set(data, true);
if (ret < 0)
return ret;
usleep_range(MMC35240_WAIT_SET_RESET, MMC35240_WAIT_SET_RESET + 1);
ret = mmc35240_hw_set(data, false);
if (ret < 0)
return ret;
/* set default sampling frequency */
ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
MMC35240_CTRL1_BW_MASK,
data->res << MMC35240_CTRL1_BW_SHIFT);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR,
otp_data, sizeof(otp_data));
if (ret < 0)
return ret;
y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) |
(otp_data[2] >> 4));
z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f);
data->axis_coef[0] = MMC35240_X_COEFF(1);
data->axis_coef[1] = MMC35240_Y_COEFF(y_convert);
data->axis_coef[2] = MMC35240_Z_COEFF(z_convert);
data->axis_scale[0] = 1;
data->axis_scale[1] = 1000;
data->axis_scale[2] = 10000;
return 0;
}
static int mmc35240_take_measurement(struct mmc35240_data *data)
{
int ret, tries = 100;
unsigned int reg_status;
ret = regmap_write(data->regmap, MMC35240_REG_CTRL0,
MMC35240_CTRL0_TM_BIT);
if (ret < 0)
return ret;
while (tries-- > 0) {
ret = regmap_read(data->regmap, MMC35240_REG_STATUS,
&reg_status);
if (ret < 0)
return ret;
if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT)
break;
/* minimum wait time to complete measurement is 10 ms */
usleep_range(10000, 11000);
}
if (tries < 0) {
dev_err(&data->client->dev, "data not ready\n");
return -EIO;
}
return 0;
}
static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3])
{
int ret;
ret = mmc35240_take_measurement(data);
if (ret < 0)
return ret;
return regmap_bulk_read(data->regmap, MMC35240_REG_XOUT_L, buf,
3 * sizeof(__le16));
}
/**
* mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply
* compensation for output value.
*
* @data: device private data
* @index: axis index for which we want the conversion
* @buf: raw data to be converted, 2 bytes in little endian format
* @val: compensated output reading (unit is milli gauss)
*
* Returns: 0 in case of success, -EINVAL when @index is not valid
*/
static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
__le16 buf[], int *val)
{
int raw[3];
int sens[3];
int nfo;
raw[AXIS_X] = le16_to_cpu(buf[AXIS_X]);
raw[AXIS_Y] = le16_to_cpu(buf[AXIS_Y]);
raw[AXIS_Z] = le16_to_cpu(buf[AXIS_Z]);
sens[AXIS_X] = mmc35240_props_table[data->res].sens[AXIS_X];
sens[AXIS_Y] = mmc35240_props_table[data->res].sens[AXIS_Y];
sens[AXIS_Z] = mmc35240_props_table[data->res].sens[AXIS_Z];
nfo = mmc35240_props_table[data->res].nfo;
switch (index) {
case AXIS_X:
*val = (raw[AXIS_X] - nfo) * 1000 / sens[AXIS_X];
break;
case AXIS_Y:
*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] -
(raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z];
break;
case AXIS_Z:
*val = (raw[AXIS_Y] - nfo) * 1000 / sens[AXIS_Y] +
(raw[AXIS_Z] - nfo) * 1000 / sens[AXIS_Z];
break;
default:
return -EINVAL;
}
/* apply OTP compensation */
*val = (*val) * data->axis_coef[index] / data->axis_scale[index];
return 0;
}
static int mmc35240_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct mmc35240_data *data = iio_priv(indio_dev);
int ret, i;
unsigned int reg;
__le16 buf[3];
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->mutex);
ret = mmc35240_read_measurement(data, buf);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = 1000;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&data->mutex);
ret = regmap_read(data->regmap, MMC35240_REG_CTRL1, &reg);
mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT;
if (i < 0 || i >= ARRAY_SIZE(mmc35240_samp_freq))
return -EINVAL;
*val = mmc35240_samp_freq[i].val;
*val2 = mmc35240_samp_freq[i].val2;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int mmc35240_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct mmc35240_data *data = iio_priv(indio_dev);
int i, ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
i = mmc35240_get_samp_freq_index(data, val, val2);
if (i < 0)
return -EINVAL;
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
MMC35240_CTRL1_BW_MASK,
i << MMC35240_CTRL1_BW_SHIFT);
mutex_unlock(&data->mutex);
return ret;
default:
return -EINVAL;
}
}
static const struct iio_info mmc35240_info = {
.read_raw = mmc35240_read_raw,
.write_raw = mmc35240_write_raw,
.attrs = &mmc35240_attribute_group,
};
static bool mmc35240_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC35240_REG_CTRL0:
case MMC35240_REG_CTRL1:
return true;
default:
return false;
}
}
static bool mmc35240_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC35240_REG_XOUT_L:
case MMC35240_REG_XOUT_H:
case MMC35240_REG_YOUT_L:
case MMC35240_REG_YOUT_H:
case MMC35240_REG_ZOUT_L:
case MMC35240_REG_ZOUT_H:
case MMC35240_REG_STATUS:
case MMC35240_REG_ID:
return true;
default:
return false;
}
}
static bool mmc35240_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC35240_REG_CTRL0:
case MMC35240_REG_CTRL1:
return false;
default:
return true;
}
}
static const struct reg_default mmc35240_reg_defaults[] = {
{ MMC35240_REG_CTRL0, 0x00 },
{ MMC35240_REG_CTRL1, 0x00 },
};
static const struct regmap_config mmc35240_regmap_config = {
.name = "mmc35240_regmap",
.reg_bits = 8,
.val_bits = 8,
.max_register = MMC35240_REG_ID,
.cache_type = REGCACHE_FLAT,
.writeable_reg = mmc35240_is_writeable_reg,
.readable_reg = mmc35240_is_readable_reg,
.volatile_reg = mmc35240_is_volatile_reg,
.reg_defaults = mmc35240_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(mmc35240_reg_defaults),
};
static int mmc35240_probe(struct i2c_client *client)
{
struct mmc35240_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &mmc35240_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "regmap initialization failed\n");
return PTR_ERR(regmap);
}
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->regmap = regmap;
data->res = MMC35240_16_BITS_SLOW;
mutex_init(&data->mutex);
indio_dev->info = &mmc35240_info;
indio_dev->name = MMC35240_DRV_NAME;
indio_dev->channels = mmc35240_channels;
indio_dev->num_channels = ARRAY_SIZE(mmc35240_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = mmc35240_init(data);
if (ret < 0) {
dev_err(&client->dev, "mmc35240 chip init failed\n");
return ret;
}
return devm_iio_device_register(&client->dev, indio_dev);
}
static int mmc35240_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mmc35240_data *data = iio_priv(indio_dev);
regcache_cache_only(data->regmap, true);
return 0;
}
static int mmc35240_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct mmc35240_data *data = iio_priv(indio_dev);
int ret;
regcache_mark_dirty(data->regmap);
ret = regcache_sync_region(data->regmap, MMC35240_REG_CTRL0,
MMC35240_REG_CTRL1);
if (ret < 0)
dev_err(dev, "Failed to restore control registers\n");
regcache_cache_only(data->regmap, false);
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(mmc35240_pm_ops, mmc35240_suspend,
mmc35240_resume);
static const struct of_device_id mmc35240_of_match[] = {
{ .compatible = "memsic,mmc35240", },
{ }
};
MODULE_DEVICE_TABLE(of, mmc35240_of_match);
static const struct acpi_device_id mmc35240_acpi_match[] = {
{"MMC35240", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match);
static const struct i2c_device_id mmc35240_id[] = {
{ .name = "mmc35240" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mmc35240_id);
static struct i2c_driver mmc35240_driver = {
.driver = {
.name = MMC35240_DRV_NAME,
.of_match_table = mmc35240_of_match,
.pm = pm_sleep_ptr(&mmc35240_pm_ops),
.acpi_match_table = mmc35240_acpi_match,
},
.probe = mmc35240_probe,
.id_table = mmc35240_id,
};
module_i2c_driver(mmc35240_driver);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
MODULE_DESCRIPTION("MEMSIC MMC35240 magnetic sensor driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,585 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MMC5633 - MEMSIC 3-axis Magnetic Sensor
*
* Copyright (c) 2015, Intel Corporation.
* Copyright (c) 2025, NXP
*
* IIO driver for MMC5633, base on mmc35240.c
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i3c/device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#define MMC5633_REG_XOUT0 0x00
#define MMC5633_REG_XOUT1 0x01
#define MMC5633_REG_YOUT0 0x02
#define MMC5633_REG_YOUT1 0x03
#define MMC5633_REG_ZOUT0 0x04
#define MMC5633_REG_ZOUT1 0x05
#define MMC5633_REG_XOUT2 0x06
#define MMC5633_REG_YOUT2 0x07
#define MMC5633_REG_ZOUT2 0x08
#define MMC5633_REG_TOUT 0x09
#define MMC5633_REG_STATUS1 0x18
#define MMC5633_REG_STATUS0 0x19
#define MMC5633_REG_CTRL0 0x1b
#define MMC5633_REG_CTRL1 0x1c
#define MMC5633_REG_CTRL2 0x1d
#define MMC5633_REG_ID 0x39
#define MMC5633_STATUS1_MEAS_T_DONE_BIT BIT(7)
#define MMC5633_STATUS1_MEAS_M_DONE_BIT BIT(6)
#define MMC5633_CTRL0_CMM_FREQ_EN BIT(7)
#define MMC5633_CTRL0_AUTO_ST_EN BIT(6)
#define MMC5633_CTRL0_AUTO_SR_EN BIT(5)
#define MMC5633_CTRL0_RESET BIT(4)
#define MMC5633_CTRL0_SET BIT(3)
#define MMC5633_CTRL0_MEAS_T BIT(1)
#define MMC5633_CTRL0_MEAS_M BIT(0)
#define MMC5633_CTRL1_BW_MASK GENMASK(1, 0)
#define MMC5633_WAIT_SET_RESET_US (1 * USEC_PER_MSEC)
#define MMC5633_HDR_CTRL0_MEAS_M 0x01
#define MMC5633_HDR_CTRL0_MEAS_T 0x03
#define MMC5633_HDR_CTRL0_SET 0x05
#define MMC5633_HDR_CTRL0_RESET 0x07
enum mmc5633_axis {
MMC5633_AXIS_X,
MMC5633_AXIS_Y,
MMC5633_AXIS_Z,
MMC5633_TEMPERATURE,
};
struct mmc5633_data {
struct regmap *regmap;
struct i3c_device *i3cdev;
struct mutex mutex; /* protect to finish one whole measurement */
};
static int mmc5633_samp_freq[][2] = {
{ 1, 200000 },
{ 2, 0 },
{ 3, 500000 },
{ 6, 600000 },
};
#define MMC5633_CHANNEL(_axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _axis, \
.address = MMC5633_AXIS_ ## _axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec mmc5633_channels[] = {
MMC5633_CHANNEL(X),
MMC5633_CHANNEL(Y),
MMC5633_CHANNEL(Z),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = MMC5633_TEMPERATURE,
},
};
static int mmc5633_get_samp_freq_index(struct mmc5633_data *data,
int val, int val2)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(mmc5633_samp_freq); i++)
if (mmc5633_samp_freq[i][0] == val &&
mmc5633_samp_freq[i][1] == val2)
return i;
return -EINVAL;
}
static int mmc5633_init(struct mmc5633_data *data)
{
unsigned int reg_id;
int ret;
ret = regmap_read(data->regmap, MMC5633_REG_ID, &reg_id);
if (ret)
return dev_err_probe(regmap_get_device(data->regmap), ret,
"Error reading product id\n");
/*
* Make sure we restore sensor characteristics, by doing
* a SET/RESET sequence, the axis polarity being naturally
* aligned after RESET.
*/
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_SET);
if (ret)
return ret;
/*
* Minimum time interval between SET or RESET to other operations is
* 1ms according to Operating Timing Diagram in datasheet.
*/
fsleep(MMC5633_WAIT_SET_RESET_US);
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_RESET);
if (ret)
return ret;
/* set default sampling frequency */
return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
MMC5633_CTRL1_BW_MASK,
FIELD_PREP(MMC5633_CTRL1_BW_MASK, 0));
}
static int mmc5633_take_measurement(struct mmc5633_data *data, int address)
{
unsigned int reg_status, val;
int ret;
val = (address == MMC5633_TEMPERATURE) ? MMC5633_CTRL0_MEAS_T : MMC5633_CTRL0_MEAS_M;
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, val);
if (ret < 0)
return ret;
val = (address == MMC5633_TEMPERATURE) ?
MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
ret = regmap_read_poll_timeout(data->regmap, MMC5633_REG_STATUS1, reg_status,
reg_status & val,
10 * USEC_PER_MSEC,
100 * 10 * USEC_PER_MSEC);
if (ret) {
dev_err(regmap_get_device(data->regmap), "data not ready\n");
return ret;
}
return 0;
}
static bool mmc5633_is_support_hdr(struct mmc5633_data *data)
{
if (!data->i3cdev)
return false;
return i3c_device_get_supported_xfer_mode(data->i3cdev) & BIT(I3C_HDR_DDR);
}
static int mmc5633_read_measurement(struct mmc5633_data *data, int address, void *buf, size_t sz)
{
struct device *dev = regmap_get_device(data->regmap);
u8 data_cmd[2], status[2];
unsigned int val, ready;
int ret;
if (mmc5633_is_support_hdr(data)) {
struct i3c_xfer xfers_wr_cmd[] = {
{
.cmd = 0x3b,
.len = 2,
.data.out = data_cmd,
}
};
struct i3c_xfer xfers_rd_sta_cmd[] = {
{
.cmd = 0x23 | BIT(7), /* RDSTA CMD */
.len = 2,
.data.in = status,
},
};
struct i3c_xfer xfers_rd_data_cmd[] = {
{
.cmd = 0x22 | BIT(7), /* RDLONG CMD */
.len = sz,
.data.in = buf,
},
};
data_cmd[0] = 0;
data_cmd[1] = (address == MMC5633_TEMPERATURE) ?
MMC5633_HDR_CTRL0_MEAS_T : MMC5633_HDR_CTRL0_MEAS_M;
ret = i3c_device_do_xfers(data->i3cdev, xfers_wr_cmd,
ARRAY_SIZE(xfers_wr_cmd), I3C_HDR_DDR);
if (ret < 0)
return ret;
ready = (address == MMC5633_TEMPERATURE) ?
MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
ret = read_poll_timeout(i3c_device_do_xfers, val,
val || (status[0] & ready),
10 * USEC_PER_MSEC,
100 * 10 * USEC_PER_MSEC, 0,
data->i3cdev, xfers_rd_sta_cmd,
ARRAY_SIZE(xfers_rd_sta_cmd), I3C_HDR_DDR);
if (ret) {
dev_err(dev, "data not ready\n");
return ret;
}
if (val) {
dev_err(dev, "i3c transfer error\n");
return val;
}
return i3c_device_do_xfers(data->i3cdev, xfers_rd_data_cmd,
ARRAY_SIZE(xfers_rd_data_cmd), I3C_HDR_DDR);
}
/* Fallback to use SDR/I2C mode */
ret = mmc5633_take_measurement(data, address);
if (ret < 0)
return ret;
if (address == MMC5633_TEMPERATURE)
/*
* Put tempeature to last byte of buff to align HDR case.
* I3C will early terminate data read if previous data is not
* available.
*/
return regmap_bulk_read(data->regmap, MMC5633_REG_TOUT, buf + sz - 1, 1);
return regmap_bulk_read(data->regmap, MMC5633_REG_XOUT0, buf, sz);
}
/* X,Y,Z 3 channels, each channel has 3 byte and TEMP */
#define MMC5633_ALL_SIZE (3 * 3 + 1)
static int mmc5633_get_raw(struct mmc5633_data *data, int index, unsigned char *buf, int *val)
{
if (index == MMC5633_TEMPERATURE) {
*val = buf[MMC5633_ALL_SIZE - 1];
return 0;
}
/*
* X[19..12] X[11..4] Y[19..12] Y[11..4] Z[19..12] Z[11..4] X[3..0] Y[3..0] Z[3..0]
*/
*val = get_unaligned_be16(buf + 2 * index) << 4;
*val |= buf[index + 6] >> 4;
return 0;
}
static int mmc5633_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct mmc5633_data *data = iio_priv(indio_dev);
char buf[MMC5633_ALL_SIZE];
unsigned int reg, i;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
scoped_guard(mutex, &data->mutex) {
ret = mmc5633_read_measurement(data, chan->address, buf, MMC5633_ALL_SIZE);
if (ret < 0)
return ret;
}
ret = mmc5633_get_raw(data, chan->address, buf, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_MAGN) {
*val = 0;
*val2 = 62500;
} else {
*val = 0;
*val2 = 800000000; /* 0.8C */
}
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
if (chan->type == IIO_TEMP) {
*val = -75;
return IIO_VAL_INT;
}
return -EINVAL;
case IIO_CHAN_INFO_SAMP_FREQ:
scoped_guard(mutex, &data->mutex) {
ret = regmap_read(data->regmap, MMC5633_REG_CTRL1, &reg);
if (ret < 0)
return ret;
}
i = FIELD_GET(MMC5633_CTRL1_BW_MASK, reg);
if (i >= ARRAY_SIZE(mmc5633_samp_freq))
return -EINVAL;
*val = mmc5633_samp_freq[i][0];
*val2 = mmc5633_samp_freq[i][1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int mmc5633_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct mmc5633_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: {
ret = mmc5633_get_samp_freq_index(data, val, val2);
if (ret < 0)
return ret;
guard(mutex)(&data->mutex);
return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
MMC5633_CTRL1_BW_MASK,
FIELD_PREP(MMC5633_CTRL1_BW_MASK, ret));
}
default:
return -EINVAL;
}
}
static int mmc5633_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (const int *)mmc5633_samp_freq;
*length = ARRAY_SIZE(mmc5633_samp_freq) * 2;
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static const struct iio_info mmc5633_info = {
.read_raw = mmc5633_read_raw,
.write_raw = mmc5633_write_raw,
.read_avail = mmc5633_read_avail,
};
static bool mmc5633_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_CTRL0:
case MMC5633_REG_CTRL1:
return true;
default:
return false;
}
}
static bool mmc5633_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_XOUT0:
case MMC5633_REG_XOUT1:
case MMC5633_REG_YOUT0:
case MMC5633_REG_YOUT1:
case MMC5633_REG_ZOUT0:
case MMC5633_REG_ZOUT1:
case MMC5633_REG_XOUT2:
case MMC5633_REG_YOUT2:
case MMC5633_REG_ZOUT2:
case MMC5633_REG_TOUT:
case MMC5633_REG_STATUS1:
case MMC5633_REG_ID:
return true;
default:
return false;
}
}
static bool mmc5633_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_CTRL0:
case MMC5633_REG_CTRL1:
return false;
default:
return true;
}
}
static const struct reg_default mmc5633_reg_defaults[] = {
{ MMC5633_REG_CTRL0, 0x00 },
{ MMC5633_REG_CTRL1, 0x00 },
};
static const struct regmap_config mmc5633_regmap_config = {
.name = "mmc5633_regmap",
.reg_bits = 8,
.val_bits = 8,
.max_register = MMC5633_REG_ID,
.cache_type = REGCACHE_MAPLE,
.writeable_reg = mmc5633_is_writeable_reg,
.readable_reg = mmc5633_is_readable_reg,
.volatile_reg = mmc5633_is_volatile_reg,
.reg_defaults = mmc5633_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(mmc5633_reg_defaults),
};
static int mmc5633_common_probe(struct regmap *regmap, char *name,
struct i3c_device *i3cdev)
{
struct device *dev = regmap_get_device(regmap);
struct mmc5633_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = regmap;
data->i3cdev = i3cdev;
ret = devm_mutex_init(dev, &data->mutex);
if (ret)
return ret;
indio_dev->info = &mmc5633_info;
indio_dev->name = name;
indio_dev->channels = mmc5633_channels;
indio_dev->num_channels = ARRAY_SIZE(mmc5633_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = mmc5633_init(data);
if (ret < 0)
return dev_err_probe(dev, ret, "mmc5633 chip init failed\n");
return devm_iio_device_register(dev, indio_dev);
}
static int mmc5633_suspend(struct device *dev)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
regcache_cache_only(regmap, true);
return 0;
}
static int mmc5633_resume(struct device *dev)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
int ret;
regcache_mark_dirty(regmap);
ret = regcache_sync_region(regmap, MMC5633_REG_CTRL0, MMC5633_REG_CTRL1);
if (ret)
dev_err(dev, "Failed to restore control registers\n");
regcache_cache_only(regmap, false);
return 0;
}
static int mmc5633_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &mmc5633_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
return mmc5633_common_probe(regmap, client->name, NULL);
}
static DEFINE_SIMPLE_DEV_PM_OPS(mmc5633_pm_ops, mmc5633_suspend, mmc5633_resume);
static const struct of_device_id mmc5633_of_match[] = {
{ .compatible = "memsic,mmc5603" },
{ .compatible = "memsic,mmc5633" },
{ }
};
MODULE_DEVICE_TABLE(of, mmc5633_of_match);
static const struct i2c_device_id mmc5633_i2c_id[] = {
{ .name = "mmc5603" },
{ .name = "mmc5633" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mmc5633_i2c_id);
static struct i2c_driver mmc5633_i2c_driver = {
.driver = {
.name = "mmc5633_i2c",
.of_match_table = mmc5633_of_match,
.pm = pm_sleep_ptr(&mmc5633_pm_ops),
},
.probe = mmc5633_i2c_probe,
.id_table = mmc5633_i2c_id,
};
static const struct i3c_device_id mmc5633_i3c_ids[] = {
I3C_DEVICE(0x0251, 0x0000, NULL),
{ }
};
MODULE_DEVICE_TABLE(i3c, mmc5633_i3c_ids);
static int mmc5633_i3c_probe(struct i3c_device *i3cdev)
{
struct device *dev = i3cdev_to_dev(i3cdev);
struct regmap *regmap;
char *name;
name = devm_kasprintf(dev, GFP_KERNEL, "mmc5633_%s", dev_name(dev));
if (!name)
return -ENOMEM;
regmap = devm_regmap_init_i3c(i3cdev, &mmc5633_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to register i3c regmap\n");
return mmc5633_common_probe(regmap, name, i3cdev);
}
static struct i3c_driver mmc5633_i3c_driver = {
.driver = {
.name = "mmc5633_i3c",
},
.probe = mmc5633_i3c_probe,
.id_table = mmc5633_i3c_ids,
};
module_i3c_i2c_driver(mmc5633_i3c_driver, &mmc5633_i2c_driver)
MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>");
MODULE_DESCRIPTION("MEMSIC MMC5633 magnetic sensor driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,347 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MMC5983 - MEMSIC 3-axis Magnetic Sensor
*
* Copyright (c) 2026, Vlad Kulikov <vlad.kulikov.c@gmail.com>
*
* IIO driver for MMC5983
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/time.h>
#include <linux/types.h>
#define MMC5983_REG_XOUT0 0x00
#define MMC5983_REG_XOUT1 0x01
#define MMC5983_REG_YOUT0 0x02
#define MMC5983_REG_YOUT1 0x03
#define MMC5983_REG_ZOUT0 0x04
#define MMC5983_REG_ZOUT1 0x05
#define MMC5983_REG_XYZOUT2 0x06
#define MMC5983_REG_STATUS 0x08
#define MMC5983_REG_CTRL0 0x09
#define MMC5983_REG_CTRL1 0x0A
#define MMC5983_REG_CTRL2 0x0B
#define MMC5983_REG_CTRL3 0x0C
#define MMC5983_REG_ID 0x2F
#define MMC5983_PRODUCT_ID 0x30
#define MMC5983_STATUS_MEAS_M_DONE_BIT BIT(0)
#define MMC5983_STATUS_OTP_RD_DONE_BIT BIT(4)
#define MMC5983_CTRL0_TM_M_BIT BIT(0)
#define MMC5983_CTRL0_SET_BIT BIT(3)
#define MMC5983_CTRL0_RESET_BIT BIT(4)
#define MMC5983_CTRL0_OTP_RD_BIT BIT(6)
#define MMC5983_CTRL1_SW_RST_BIT BIT(7)
enum mmc5983_axis {
MMC5983_AXIS_X,
MMC5983_AXIS_Y,
MMC5983_AXIS_Z,
};
struct mmc5983_data {
struct regmap *regmap;
/* Protects chip access during SET/RESET measurement sequence */
struct mutex mutex;
};
#define MMC5983_CHANNEL(_axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##_axis, \
.address = MMC5983_AXIS_##_axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec mmc5983_channels[] = {
MMC5983_CHANNEL(X),
MMC5983_CHANNEL(Y),
MMC5983_CHANNEL(Z),
};
static int mmc5983_pulse_coil(struct mmc5983_data *data, unsigned int coil_bit)
{
int ret;
ret = regmap_write(data->regmap, MMC5983_REG_CTRL0, coil_bit);
if (ret)
return ret;
/*
* Datasheet page 15: SET/RESET coil pulse is 500 ns.
* Vendor sample code waits 500 us before the next operation.
*/
fsleep(500);
return 0;
}
static int mmc5983_take_measurement(struct mmc5983_data *data, int m[3])
{
unsigned int status;
u8 buf[7];
int ret;
ret = regmap_write(data->regmap, MMC5983_REG_CTRL0,
MMC5983_CTRL0_TM_M_BIT);
if (ret)
return ret;
/*
* Datasheet page 15: measurement time is 8 ms at BW=00 (default,
* slowest setting). Use a 50 ms timeout for margin.
*/
ret = regmap_read_poll_timeout(data->regmap, MMC5983_REG_STATUS,
status,
status & MMC5983_STATUS_MEAS_M_DONE_BIT,
10 * USEC_PER_MSEC,
50 * USEC_PER_MSEC);
if (ret)
return ret;
ret = regmap_bulk_read(data->regmap, MMC5983_REG_XOUT0, buf,
sizeof(buf));
if (ret)
return ret;
m[0] = (buf[0] << 10) | (buf[1] << 2) | ((buf[6] >> 6) & 0x3);
m[1] = (buf[2] << 10) | (buf[3] << 2) | ((buf[6] >> 4) & 0x3);
m[2] = (buf[4] << 10) | (buf[5] << 2) | ((buf[6] >> 2) & 0x3);
return 0;
}
static int mmc5983_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val,
int *val2, long mask)
{
struct mmc5983_data *data = iio_priv(indio_dev);
int m1[3], m2[3];
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW: {
guard(mutex)(&data->mutex);
/* SET: magnetize sensor elements in forward direction */
ret = mmc5983_pulse_coil(data, MMC5983_CTRL0_SET_BIT);
if (ret)
return ret;
ret = mmc5983_take_measurement(data, m1);
if (ret)
return ret;
/* RESET: magnetize sensor elements in reverse direction */
ret = mmc5983_pulse_coil(data, MMC5983_CTRL0_RESET_BIT);
if (ret)
return ret;
ret = mmc5983_take_measurement(data, m2);
if (ret)
return ret;
*val = (m1[chan->address] - m2[chan->address]) / 2;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = 61035;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info mmc5983_info = {
.read_raw = mmc5983_read_raw,
};
static bool mmc5983_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5983_REG_CTRL0:
case MMC5983_REG_CTRL1:
case MMC5983_REG_CTRL2:
case MMC5983_REG_CTRL3:
return true;
default:
return false;
}
}
static bool mmc5983_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5983_REG_XOUT0:
case MMC5983_REG_XOUT1:
case MMC5983_REG_YOUT0:
case MMC5983_REG_YOUT1:
case MMC5983_REG_ZOUT0:
case MMC5983_REG_ZOUT1:
case MMC5983_REG_XYZOUT2:
case MMC5983_REG_STATUS:
case MMC5983_REG_CTRL0:
case MMC5983_REG_CTRL1:
case MMC5983_REG_CTRL2:
case MMC5983_REG_CTRL3:
case MMC5983_REG_ID:
return true;
default:
return false;
}
}
static bool mmc5983_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5983_REG_XOUT0:
case MMC5983_REG_XOUT1:
case MMC5983_REG_YOUT0:
case MMC5983_REG_YOUT1:
case MMC5983_REG_ZOUT0:
case MMC5983_REG_ZOUT1:
case MMC5983_REG_XYZOUT2:
case MMC5983_REG_STATUS:
case MMC5983_REG_CTRL0:
case MMC5983_REG_CTRL1:
return true;
default:
return false;
}
}
static const struct regmap_config mmc5983_regmap_config = {
.name = "mmc5983_regmap",
.reg_bits = 8,
.val_bits = 8,
.max_register = MMC5983_REG_ID,
.writeable_reg = mmc5983_is_writeable_reg,
.readable_reg = mmc5983_is_readable_reg,
.volatile_reg = mmc5983_is_volatile_reg,
};
static int mmc5983_init(struct mmc5983_data *data)
{
struct regmap *regmap = data->regmap;
struct device *dev = regmap_get_device(regmap);
unsigned int reg_id, status;
int ret;
ret = regmap_read(regmap, MMC5983_REG_ID, &reg_id);
if (ret)
return dev_err_probe(dev, ret, "Error reading product id\n");
if (reg_id != MMC5983_PRODUCT_ID)
dev_info(dev, "unexpected product id 0x%02x\n", reg_id);
ret = regmap_write(regmap, MMC5983_REG_CTRL1, MMC5983_CTRL1_SW_RST_BIT);
if (ret)
return ret;
/* Datasheet page 15: power-on time after SW_RST is 10 ms */
fsleep(10 * USEC_PER_MSEC);
ret = regmap_write(regmap, MMC5983_REG_CTRL0, MMC5983_CTRL0_OTP_RD_BIT);
if (ret)
return ret;
/*
* Datasheet page 15: OTP read completes and self-clears. No separate
* OTP refresh timeout is specified, so use the 10 ms power-on time as
* a conservative upper bound.
*/
ret = regmap_read_poll_timeout(regmap, MMC5983_REG_STATUS, status,
status & MMC5983_STATUS_OTP_RD_DONE_BIT,
USEC_PER_MSEC,
10 * USEC_PER_MSEC);
if (ret)
return ret;
/* SET: magnetize sensor elements in forward direction */
ret = mmc5983_pulse_coil(data, MMC5983_CTRL0_SET_BIT);
if (ret)
return ret;
/* RESET: magnetize sensor elements in reverse direction */
return mmc5983_pulse_coil(data, MMC5983_CTRL0_RESET_BIT);
}
static int mmc5983_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct mmc5983_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
ret = devm_mutex_init(dev, &data->mutex);
if (ret)
return ret;
data->regmap = devm_regmap_init_i2c(i2c, &mmc5983_regmap_config);
if (IS_ERR(data->regmap))
return dev_err_probe(dev, PTR_ERR(data->regmap),
"failed to allocate register map\n");
indio_dev->info = &mmc5983_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = "mmc5983";
indio_dev->channels = mmc5983_channels;
indio_dev->num_channels = ARRAY_SIZE(mmc5983_channels);
ret = mmc5983_init(data);
if (ret)
return dev_err_probe(dev, ret, "mmc5983 chip init failed\n");
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id mmc5983_of_match[] = {
{ .compatible = "memsic,mmc5983" },
{ }
};
MODULE_DEVICE_TABLE(of, mmc5983_of_match);
static const struct i2c_device_id mmc5983_id[] = {
{ "mmc5983" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mmc5983_id);
static struct i2c_driver mmc5983_driver = {
.driver = {
.name = "mmc5983",
.of_match_table = mmc5983_of_match,
},
.probe = mmc5983_probe,
.id_table = mmc5983_id,
};
module_i2c_driver(mmc5983_driver);
MODULE_AUTHOR("Vladislav Kulikov <vlad.kulikov.c@gmail.com>");
MODULE_DESCRIPTION("MEMSIC MMC5983 magnetic sensor driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,618 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PNI RM3100 3-axis geomagnetic sensor driver core.
*
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
*
* User Manual available at
* <https://www.pnicorp.com/download/rm3100-user-manual/>
*
* TODO: event generation, pm.
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/unaligned.h>
#include "rm3100.h"
/* Cycle Count Registers. */
#define RM3100_REG_CC_X 0x05
#define RM3100_REG_CC_Y 0x07
#define RM3100_REG_CC_Z 0x09
/* Poll Measurement Mode register. */
#define RM3100_REG_POLL 0x00
#define RM3100_POLL_X BIT(4)
#define RM3100_POLL_Y BIT(5)
#define RM3100_POLL_Z BIT(6)
/* Continuous Measurement Mode register. */
#define RM3100_REG_CMM 0x01
#define RM3100_CMM_START BIT(0)
#define RM3100_CMM_X BIT(4)
#define RM3100_CMM_Y BIT(5)
#define RM3100_CMM_Z BIT(6)
/* TiMe Rate Configuration register. */
#define RM3100_REG_TMRC 0x0B
#define RM3100_TMRC_OFFSET 0x92
/* Result Status register. */
#define RM3100_REG_STATUS 0x34
#define RM3100_STATUS_DRDY BIT(7)
/* Measurement result registers. */
#define RM3100_REG_MX2 0x24
#define RM3100_REG_MY2 0x27
#define RM3100_REG_MZ2 0x2a
#define RM3100_W_REG_START RM3100_REG_POLL
#define RM3100_W_REG_END RM3100_REG_TMRC
#define RM3100_R_REG_START RM3100_REG_POLL
#define RM3100_R_REG_END RM3100_REG_STATUS
#define RM3100_V_REG_START RM3100_REG_POLL
#define RM3100_V_REG_END RM3100_REG_STATUS
/*
* This is computed by hand, is the sum of channel storage bits and padding
* bits, which is 4+4+4+12=24 in here.
*/
#define RM3100_SCAN_BYTES 24
#define RM3100_CMM_AXIS_SHIFT 4
struct rm3100_data {
struct regmap *regmap;
struct completion measuring_done;
bool use_interrupt;
int conversion_time;
int scale;
/* Ensure naturally aligned timestamp */
u8 buffer[RM3100_SCAN_BYTES] __aligned(8);
struct iio_trigger *drdy_trig;
/*
* This lock is for protecting the consistency of series of i2c
* operations, that is, to make sure a measurement process will
* not be interrupted by a set frequency operation, which should
* be taken where a series of i2c operation starts, released where
* the operation ends.
*/
struct mutex lock;
};
static const struct regmap_range rm3100_readable_ranges[] = {
regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
};
const struct regmap_access_table rm3100_readable_table = {
.yes_ranges = rm3100_readable_ranges,
.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
};
EXPORT_SYMBOL_NS_GPL(rm3100_readable_table, "IIO_RM3100");
static const struct regmap_range rm3100_writable_ranges[] = {
regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
};
const struct regmap_access_table rm3100_writable_table = {
.yes_ranges = rm3100_writable_ranges,
.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
};
EXPORT_SYMBOL_NS_GPL(rm3100_writable_table, "IIO_RM3100");
static const struct regmap_range rm3100_volatile_ranges[] = {
regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
};
const struct regmap_access_table rm3100_volatile_table = {
.yes_ranges = rm3100_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
};
EXPORT_SYMBOL_NS_GPL(rm3100_volatile_table, "IIO_RM3100");
static irqreturn_t rm3100_thread_fn(int irq, void *d)
{
struct iio_dev *indio_dev = d;
struct rm3100_data *data = iio_priv(indio_dev);
/*
* Write operation to any register or read operation
* to first byte of results will clear the interrupt.
*/
regmap_write(data->regmap, RM3100_REG_POLL, 0);
return IRQ_HANDLED;
}
static irqreturn_t rm3100_irq_handler(int irq, void *d)
{
struct iio_dev *indio_dev = d;
struct rm3100_data *data = iio_priv(indio_dev);
if (!iio_buffer_enabled(indio_dev))
complete(&data->measuring_done);
else
iio_trigger_poll(data->drdy_trig);
return IRQ_WAKE_THREAD;
}
static int rm3100_wait_measurement(struct rm3100_data *data)
{
struct regmap *regmap = data->regmap;
unsigned int val;
int tries = 20;
int ret;
/*
* A read cycle of 400kbits i2c bus is about 20us, plus the time
* used for scheduling, a read cycle of fast mode of this device
* can reach 1.7ms, it may be possible for data to arrive just
* after we check the RM3100_REG_STATUS. In this case, irq_handler is
* called before measuring_done is reinitialized, it will wait
* forever for data that has already been ready.
* Reinitialize measuring_done before looking up makes sure we
* will always capture interrupt no matter when it happens.
*/
if (data->use_interrupt)
reinit_completion(&data->measuring_done);
ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
if (ret < 0)
return ret;
if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
if (data->use_interrupt) {
ret = wait_for_completion_timeout(&data->measuring_done,
msecs_to_jiffies(data->conversion_time));
if (!ret)
return -ETIMEDOUT;
} else {
do {
usleep_range(1000, 5000);
ret = regmap_read(regmap, RM3100_REG_STATUS,
&val);
if (ret < 0)
return ret;
if (val & RM3100_STATUS_DRDY)
break;
} while (--tries);
if (!tries)
return -ETIMEDOUT;
}
}
return 0;
}
static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
{
struct regmap *regmap = data->regmap;
u8 buffer[3];
int ret;
guard(mutex)(&data->lock);
ret = regmap_write(regmap, RM3100_REG_POLL, BIT(4 + idx));
if (ret < 0)
return ret;
ret = rm3100_wait_measurement(data);
if (ret < 0)
return ret;
ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * idx, buffer, 3);
if (ret < 0)
return ret;
*val = sign_extend32(get_unaligned_be24(&buffer[0]), 23);
return IIO_VAL_INT;
}
#define RM3100_CHANNEL(axis, idx) \
{ \
.type = IIO_MAGN, \
.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 = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 24, \
.storagebits = 32, \
.shift = 8, \
.endianness = IIO_BE, \
}, \
}
static const struct iio_chan_spec rm3100_channels[] = {
RM3100_CHANNEL(X, 0),
RM3100_CHANNEL(Y, 1),
RM3100_CHANNEL(Z, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
"600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
);
static struct attribute *rm3100_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group rm3100_attribute_group = {
.attrs = rm3100_attributes,
};
#define RM3100_SAMP_NUM 14
/*
* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
* Time between reading: rm3100_sam_rates[][2]ms.
* The first one is actually 1.7ms.
*/
static const int rm3100_samp_rates[RM3100_SAMP_NUM][3] = {
{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
{0, 15000, 6700}, {0, 75000, 13000}
};
static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
{
unsigned int tmp;
int ret;
guard(mutex)(&data->lock);
ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
if (ret < 0)
return ret;
*val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
*val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
return IIO_VAL_INT_PLUS_MICRO;
}
static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
{
int ret;
u8 i;
for (i = 0; i < 3; i++) {
ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
if (ret < 0)
return ret;
}
/*
* The scale of this sensor depends on the cycle count value, these
* three values are corresponding to the cycle count value 50, 100,
* 200. scale = output / gain * 10^4.
*/
switch (val) {
case 50:
data->scale = 500;
break;
case 100:
data->scale = 263;
break;
/*
* case 200:
* This function will never be called by users' code, so here we
* assume that it will never get a wrong parameter.
*/
default:
data->scale = 133;
}
return 0;
}
static int rm3100_set_samp_freq(struct iio_dev *indio_dev, int val, int val2)
{
struct rm3100_data *data = iio_priv(indio_dev);
struct regmap *regmap = data->regmap;
unsigned int cycle_count;
int ret;
int i;
guard(mutex)(&data->lock);
/* All cycle count registers use the same value. */
ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
if (ret < 0)
return ret;
for (i = 0; i < RM3100_SAMP_NUM; i++) {
if (val == rm3100_samp_rates[i][0] &&
val2 == rm3100_samp_rates[i][1])
break;
}
if (i == RM3100_SAMP_NUM)
return -EINVAL;
ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
if (ret < 0)
return ret;
/* Checking if cycle count registers need changing. */
if (val == 600 && cycle_count == 200) {
ret = rm3100_set_cycle_count(data, 100);
if (ret < 0)
return ret;
} else if (val != 600 && cycle_count == 100) {
ret = rm3100_set_cycle_count(data, 200);
if (ret < 0)
return ret;
}
if (iio_buffer_enabled(indio_dev)) {
/* Writing TMRC registers requires CMM reset. */
ret = regmap_write(regmap, RM3100_REG_CMM, 0);
if (ret < 0)
return ret;
ret = regmap_write(data->regmap, RM3100_REG_CMM,
(*indio_dev->active_scan_mask & 0x7) <<
RM3100_CMM_AXIS_SHIFT | RM3100_CMM_START);
if (ret < 0)
return ret;
}
data->conversion_time = rm3100_samp_rates[i][2] * 2;
return 0;
}
static int rm3100_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
{
struct rm3100_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = rm3100_read_mag(data, chan->scan_index, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = data->scale;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
return rm3100_get_samp_freq(data, val, val2);
default:
return -EINVAL;
}
}
static int rm3100_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return rm3100_set_samp_freq(indio_dev, val, val2);
default:
return -EINVAL;
}
}
static const struct iio_info rm3100_info = {
.attrs = &rm3100_attribute_group,
.read_raw = rm3100_read_raw,
.write_raw = rm3100_write_raw,
};
static int rm3100_buffer_preenable(struct iio_dev *indio_dev)
{
struct rm3100_data *data = iio_priv(indio_dev);
/* Starting channels enabled. */
return regmap_write(data->regmap, RM3100_REG_CMM,
(*indio_dev->active_scan_mask & 0x7) << RM3100_CMM_AXIS_SHIFT |
RM3100_CMM_START);
}
static int rm3100_buffer_postdisable(struct iio_dev *indio_dev)
{
struct rm3100_data *data = iio_priv(indio_dev);
return regmap_write(data->regmap, RM3100_REG_CMM, 0);
}
static const struct iio_buffer_setup_ops rm3100_buffer_ops = {
.preenable = rm3100_buffer_preenable,
.postdisable = rm3100_buffer_postdisable,
};
/**
* rm3100_regmap_bulk_read_locked() - Wrapper around regmap_bulk_read() with a mutex
*
* @data: Data structure containing regmap and mutex
* @reg: First register to be read from, passed to regmap_bulk_read()
* @val: Pointer to store read value, in native register size for device,
* passed to regmap_bulk_read()
* @val_count: Number of registers to read, passed to regmap_bulk_read()
*
* Intended for use only in rm3100_trigger_handler().
*
* Return:
* A value of zero on success, a negative errno in error cases.
*/
static int rm3100_regmap_bulk_read_locked(struct rm3100_data *data, unsigned int reg,
void *val, size_t val_count)
{
guard(mutex)(&data->lock);
return regmap_bulk_read(data->regmap, reg, val, val_count);
}
static irqreturn_t rm3100_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
unsigned long scan_mask = *indio_dev->active_scan_mask;
unsigned int mask_len = iio_get_masklength(indio_dev);
struct rm3100_data *data = iio_priv(indio_dev);
int ret, i, bit;
switch (scan_mask) {
case BIT(0) | BIT(1) | BIT(2):
ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MX2,
data->buffer, 9);
if (ret < 0)
goto done;
/* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
for (i = 2; i > 0; i--)
memmove(data->buffer + i * 4, data->buffer + i * 3, 3);
break;
case BIT(0) | BIT(1):
ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MX2,
data->buffer, 6);
if (ret < 0)
goto done;
memmove(data->buffer + 4, data->buffer + 3, 3);
break;
case BIT(1) | BIT(2):
ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MY2,
data->buffer, 6);
if (ret < 0)
goto done;
memmove(data->buffer + 4, data->buffer + 3, 3);
break;
case BIT(0) | BIT(2):
ret = rm3100_regmap_bulk_read_locked(data, RM3100_REG_MX2,
data->buffer, 9);
if (ret < 0)
goto done;
memmove(data->buffer + 4, data->buffer + 6, 3);
break;
default:
for_each_set_bit(bit, &scan_mask, mask_len) {
ret = rm3100_regmap_bulk_read_locked(data,
RM3100_REG_MX2 + 3 * bit,
data->buffer, 3);
if (ret < 0)
goto done;
}
}
/*
* Always using the same buffer so that we wouldn't need to set the
* paddings to 0 in case of leaking any data.
*/
iio_push_to_buffers_with_ts(indio_dev, data->buffer, sizeof(data->buffer),
pf->timestamp);
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
{
struct iio_dev *indio_dev;
struct rm3100_data *data;
unsigned int tmp;
int ret;
int samp_rate_index;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = regmap;
mutex_init(&data->lock);
indio_dev->name = "rm3100";
indio_dev->info = &rm3100_info;
indio_dev->channels = rm3100_channels;
indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
if (!irq)
data->use_interrupt = false;
else {
data->use_interrupt = true;
init_completion(&data->measuring_done);
ret = devm_request_threaded_irq(dev,
irq,
rm3100_irq_handler,
rm3100_thread_fn,
IRQF_TRIGGER_HIGH |
IRQF_ONESHOT,
indio_dev->name,
indio_dev);
if (ret < 0) {
dev_err(dev, "request irq line failed.\n");
return ret;
}
data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d",
indio_dev->name,
iio_device_id(indio_dev));
if (!data->drdy_trig)
return -ENOMEM;
ret = devm_iio_trigger_register(dev, data->drdy_trig);
if (ret < 0)
return ret;
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
rm3100_trigger_handler,
&rm3100_buffer_ops);
if (ret < 0)
return ret;
ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
if (ret < 0)
return ret;
samp_rate_index = tmp - RM3100_TMRC_OFFSET;
if (samp_rate_index < 0 || samp_rate_index >= RM3100_SAMP_NUM) {
dev_err(dev, "The value read from RM3100_REG_TMRC is invalid!\n");
return -EINVAL;
}
/* Initializing max wait time, which is double conversion time. */
data->conversion_time = rm3100_samp_rates[samp_rate_index][2] * 2;
/* Cycle count values may not be what we want. */
if ((tmp - RM3100_TMRC_OFFSET) == 0)
rm3100_set_cycle_count(data, 100);
else
rm3100_set_cycle_count(data, 200);
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(rm3100_common_probe, "IIO_RM3100");
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
*
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
*
* i2c slave address: 0x20 + SA1 << 1 + SA0.
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include "rm3100.h"
static const struct regmap_config rm3100_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &rm3100_readable_table,
.wr_table = &rm3100_writable_table,
.volatile_table = &rm3100_volatile_table,
.cache_type = REGCACHE_RBTREE,
};
static int rm3100_probe(struct i2c_client *client)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &rm3100_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return rm3100_common_probe(&client->dev, regmap, client->irq);
}
static const struct of_device_id rm3100_dt_match[] = {
{ .compatible = "pni,rm3100", },
{ }
};
MODULE_DEVICE_TABLE(of, rm3100_dt_match);
static struct i2c_driver rm3100_driver = {
.driver = {
.name = "rm3100-i2c",
.of_match_table = rm3100_dt_match,
},
.probe = rm3100_probe,
};
module_i2c_driver(rm3100_driver);
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_RM3100");
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
*
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include "rm3100.h"
static const struct regmap_config rm3100_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.rd_table = &rm3100_readable_table,
.wr_table = &rm3100_writable_table,
.volatile_table = &rm3100_volatile_table,
.read_flag_mask = 0x80,
.cache_type = REGCACHE_RBTREE,
};
static int rm3100_probe(struct spi_device *spi)
{
struct regmap *regmap;
int ret;
/* Actually this device supports both mode 0 and mode 3. */
spi->mode = SPI_MODE_0;
/* Data rates cannot exceed 1Mbits. */
spi->max_speed_hz = 1000000;
ret = spi_setup(spi);
if (ret)
return ret;
regmap = devm_regmap_init_spi(spi, &rm3100_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return rm3100_common_probe(&spi->dev, regmap, spi->irq);
}
static const struct of_device_id rm3100_dt_match[] = {
{ .compatible = "pni,rm3100", },
{ }
};
MODULE_DEVICE_TABLE(of, rm3100_dt_match);
static struct spi_driver rm3100_driver = {
.driver = {
.name = "rm3100-spi",
.of_match_table = rm3100_dt_match,
},
.probe = rm3100_probe,
};
module_spi_driver(rm3100_driver);
MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer spi driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_RM3100");
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
*/
#ifndef RM3100_CORE_H
#define RM3100_CORE_H
#include <linux/regmap.h>
extern const struct regmap_access_table rm3100_readable_table;
extern const struct regmap_access_table rm3100_writable_table;
extern const struct regmap_access_table rm3100_volatile_table;
int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
#endif /* RM3100_CORE_H */
@@ -0,0 +1,438 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Silicon Labs Si7210 Hall Effect sensor driver
*
* Copyright (c) 2024 Antoni Pokusinski <apokusinski01@gmail.com>
*
* Datasheet:
* https://www.silabs.com/documents/public/data-sheets/si7210-datasheet.pdf
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/math64.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
#include <linux/units.h>
#include <asm/byteorder.h>
/* Registers offsets and masks */
#define SI7210_REG_DSPSIGM 0xC1
#define SI7210_REG_DSPSIGL 0xC2
#define SI7210_MASK_DSPSIGSEL GENMASK(2, 0)
#define SI7210_REG_DSPSIGSEL 0xC3
#define SI7210_MASK_STOP BIT(1)
#define SI7210_MASK_ONEBURST BIT(2)
#define SI7210_REG_POWER_CTRL 0xC4
#define SI7210_MASK_ARAUTOINC BIT(0)
#define SI7210_REG_ARAUTOINC 0xC5
#define SI7210_REG_A0 0xCA
#define SI7210_REG_A1 0xCB
#define SI7210_REG_A2 0xCC
#define SI7210_REG_A3 0xCE
#define SI7210_REG_A4 0xCF
#define SI7210_REG_A5 0xD0
#define SI7210_REG_OTP_ADDR 0xE1
#define SI7210_REG_OTP_DATA 0xE2
#define SI7210_MASK_OTP_READ_EN BIT(1)
#define SI7210_REG_OTP_CTRL 0xE3
/* OTP data registers offsets */
#define SI7210_OTPREG_TMP_OFF 0x1D
#define SI7210_OTPREG_TMP_GAIN 0x1E
#define SI7210_OTPREG_A0_20 0x21
#define SI7210_OTPREG_A1_20 0x22
#define SI7210_OTPREG_A2_20 0x23
#define SI7210_OTPREG_A3_20 0x24
#define SI7210_OTPREG_A4_20 0x25
#define SI7210_OTPREG_A5_20 0x26
#define SI7210_OTPREG_A0_200 0x27
#define SI7210_OTPREG_A1_200 0x28
#define SI7210_OTPREG_A2_200 0x29
#define SI7210_OTPREG_A3_200 0x2A
#define SI7210_OTPREG_A4_200 0x2B
#define SI7210_OTPREG_A5_200 0x2C
#define A_REGS_COUNT 6
static const unsigned int a20_otp_regs[A_REGS_COUNT] = {
SI7210_OTPREG_A0_20, SI7210_OTPREG_A1_20, SI7210_OTPREG_A2_20,
SI7210_OTPREG_A3_20, SI7210_OTPREG_A4_20, SI7210_OTPREG_A5_20,
};
static const unsigned int a200_otp_regs[A_REGS_COUNT] = {
SI7210_OTPREG_A0_200, SI7210_OTPREG_A1_200, SI7210_OTPREG_A2_200,
SI7210_OTPREG_A3_200, SI7210_OTPREG_A4_200, SI7210_OTPREG_A5_200,
};
static const struct regmap_range si7210_read_reg_ranges[] = {
regmap_reg_range(SI7210_REG_DSPSIGM, SI7210_REG_ARAUTOINC),
regmap_reg_range(SI7210_REG_A0, SI7210_REG_A2),
regmap_reg_range(SI7210_REG_A3, SI7210_REG_A5),
regmap_reg_range(SI7210_REG_OTP_ADDR, SI7210_REG_OTP_CTRL),
};
static const struct regmap_access_table si7210_readable_regs = {
.yes_ranges = si7210_read_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(si7210_read_reg_ranges),
};
static const struct regmap_range si7210_write_reg_ranges[] = {
regmap_reg_range(SI7210_REG_DSPSIGSEL, SI7210_REG_ARAUTOINC),
regmap_reg_range(SI7210_REG_A0, SI7210_REG_A2),
regmap_reg_range(SI7210_REG_A3, SI7210_REG_A5),
regmap_reg_range(SI7210_REG_OTP_ADDR, SI7210_REG_OTP_CTRL),
};
static const struct regmap_access_table si7210_writeable_regs = {
.yes_ranges = si7210_write_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(si7210_write_reg_ranges),
};
static const struct regmap_range si7210_volatile_reg_ranges[] = {
regmap_reg_range(SI7210_REG_DSPSIGM, SI7210_REG_DSPSIGL),
regmap_reg_range(SI7210_REG_POWER_CTRL, SI7210_REG_POWER_CTRL),
};
static const struct regmap_access_table si7210_volatile_regs = {
.yes_ranges = si7210_volatile_reg_ranges,
.n_yes_ranges = ARRAY_SIZE(si7210_volatile_reg_ranges),
};
static const struct regmap_config si7210_regmap_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = SI7210_REG_OTP_CTRL,
.rd_table = &si7210_readable_regs,
.wr_table = &si7210_writeable_regs,
.volatile_table = &si7210_volatile_regs,
};
struct si7210_data {
struct regmap *regmap;
struct i2c_client *client;
struct mutex fetch_lock; /* lock for a single measurement fetch */
unsigned int vdd_uV;
s8 temp_offset;
s8 temp_gain;
s8 scale_20_a[A_REGS_COUNT];
s8 scale_200_a[A_REGS_COUNT];
u8 curr_scale;
};
static const struct iio_chan_spec si7210_channels[] = {
{
.type = IIO_MAGN,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
}, {
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
},
};
static int si7210_fetch_measurement(struct si7210_data *data,
struct iio_chan_spec const *chan,
u16 *buf)
{
u8 dspsigsel = chan->type == IIO_MAGN ? 0 : 1;
int ret;
__be16 result;
guard(mutex)(&data->fetch_lock);
ret = regmap_update_bits(data->regmap, SI7210_REG_DSPSIGSEL,
SI7210_MASK_DSPSIGSEL, dspsigsel);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap, SI7210_REG_POWER_CTRL,
SI7210_MASK_ONEBURST | SI7210_MASK_STOP,
SI7210_MASK_ONEBURST & ~SI7210_MASK_STOP);
if (ret)
return ret;
/*
* Read the contents of the
* registers containing the result: DSPSIGM, DSPSIGL
*/
ret = regmap_bulk_read(data->regmap, SI7210_REG_DSPSIGM,
&result, sizeof(result));
if (ret)
return ret;
*buf = be16_to_cpu(result);
return 0;
}
static int si7210_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct si7210_data *data = iio_priv(indio_dev);
long long temp;
u16 dspsig;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = si7210_fetch_measurement(data, chan, &dspsig);
if (ret)
return ret;
*val = dspsig & GENMASK(14, 0);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
if (data->curr_scale == 20)
*val2 = 12500;
else /* data->curr_scale == 200 */
*val2 = 125000;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OFFSET:
*val = -16384;
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
ret = si7210_fetch_measurement(data, chan, &dspsig);
if (ret)
return ret;
/* temp = 32 * Dspsigm[6:0] + (Dspsigl[7:0] >> 3) */
temp = FIELD_GET(GENMASK(14, 3), dspsig);
temp = div_s64(-383 * temp * temp, 100) + 160940 * temp - 279800000;
temp *= (1 + (data->temp_gain / 2048));
temp += (int)(MICRO / 16) * data->temp_offset;
/* temp -= 0.222 * VDD */
temp -= 222 * (data->vdd_uV / MILLI);
*val = div_s64(temp, MILLI);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int si7210_set_scale(struct si7210_data *data, unsigned int scale)
{
s8 *a_otp_values;
int ret;
if (scale == 20)
a_otp_values = data->scale_20_a;
else if (scale == 200)
a_otp_values = data->scale_200_a;
else
return -EINVAL;
guard(mutex)(&data->fetch_lock);
/* Write the registers 0xCA - 0xCC */
ret = regmap_bulk_write(data->regmap, SI7210_REG_A0, a_otp_values, 3);
if (ret)
return ret;
/* Write the registers 0xCE - 0xD0 */
ret = regmap_bulk_write(data->regmap, SI7210_REG_A3, &a_otp_values[3], 3);
if (ret)
return ret;
data->curr_scale = scale;
return 0;
}
static int si7210_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct si7210_data *data = iio_priv(indio_dev);
unsigned int scale;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
if (val == 0 && val2 == 12500)
scale = 20;
else if (val == 0 && val2 == 125000)
scale = 200;
else
return -EINVAL;
return si7210_set_scale(data, scale);
default:
return -EINVAL;
}
}
static int si7210_read_otpreg_val(struct si7210_data *data, unsigned int otpreg, u8 *val)
{
int ret;
unsigned int otpdata;
ret = regmap_write(data->regmap, SI7210_REG_OTP_ADDR, otpreg);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap, SI7210_REG_OTP_CTRL,
SI7210_MASK_OTP_READ_EN, SI7210_MASK_OTP_READ_EN);
if (ret)
return ret;
ret = regmap_read(data->regmap, SI7210_REG_OTP_DATA, &otpdata);
if (ret)
return ret;
*val = otpdata;
return 0;
}
/*
* According to the datasheet, the primary method to wake up a
* device is to send an empty write. However this is not feasible
* using the current API so we use the other method i.e. read a single
* byte. The device should respond with 0xFF.
*/
static int si7210_device_wake(struct si7210_data *data)
{
int ret;
ret = i2c_smbus_read_byte(data->client);
if (ret < 0)
return ret;
if (ret != 0xFF)
return -EIO;
return 0;
}
static int si7210_device_init(struct si7210_data *data)
{
int ret;
unsigned int i;
ret = si7210_device_wake(data);
if (ret)
return ret;
fsleep(1000);
ret = si7210_read_otpreg_val(data, SI7210_OTPREG_TMP_GAIN, &data->temp_gain);
if (ret)
return ret;
ret = si7210_read_otpreg_val(data, SI7210_OTPREG_TMP_OFF, &data->temp_offset);
if (ret)
return ret;
for (i = 0; i < A_REGS_COUNT; i++) {
ret = si7210_read_otpreg_val(data, a20_otp_regs[i], &data->scale_20_a[i]);
if (ret)
return ret;
}
for (i = 0; i < A_REGS_COUNT; i++) {
ret = si7210_read_otpreg_val(data, a200_otp_regs[i], &data->scale_200_a[i]);
if (ret)
return ret;
}
ret = regmap_update_bits(data->regmap, SI7210_REG_ARAUTOINC,
SI7210_MASK_ARAUTOINC, SI7210_MASK_ARAUTOINC);
if (ret)
return ret;
return si7210_set_scale(data, 20);
}
static const struct iio_info si7210_info = {
.read_raw = si7210_read_raw,
.write_raw = si7210_write_raw,
};
static int si7210_probe(struct i2c_client *client)
{
struct si7210_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
ret = devm_mutex_init(&client->dev, &data->fetch_lock);
if (ret)
return ret;
data->regmap = devm_regmap_init_i2c(client, &si7210_regmap_conf);
if (IS_ERR(data->regmap))
return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
"failed to register regmap\n");
ret = devm_regulator_get_enable_read_voltage(&client->dev, "vdd");
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to get vdd regulator\n");
data->vdd_uV = ret;
indio_dev->name = dev_name(&client->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &si7210_info;
indio_dev->channels = si7210_channels;
indio_dev->num_channels = ARRAY_SIZE(si7210_channels);
ret = si7210_device_init(data);
if (ret)
return dev_err_probe(&client->dev, ret,
"device initialization failed\n");
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id si7210_id[] = {
{ .name = "si7210" },
{ }
};
MODULE_DEVICE_TABLE(i2c, si7210_id);
static const struct of_device_id si7210_dt_ids[] = {
{ .compatible = "silabs,si7210" },
{ }
};
MODULE_DEVICE_TABLE(of, si7210_dt_ids);
static struct i2c_driver si7210_driver = {
.driver = {
.name = "si7210",
.of_match_table = si7210_dt_ids,
},
.probe = si7210_probe,
.id_table = si7210_id,
};
module_i2c_driver(si7210_driver);
MODULE_AUTHOR("Antoni Pokusinski <apokusinski01@gmail.com>");
MODULE_DESCRIPTION("Silicon Labs Si7210 Hall Effect sensor I2C driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
* v. 1.0.0
*/
#ifndef ST_MAGN_H
#define ST_MAGN_H
#include <linux/types.h>
#include <linux/iio/common/st_sensors.h>
#define LSM303DLH_MAGN_DEV_NAME "lsm303dlh_magn"
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
#define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn"
#define LIS2MDL_MAGN_DEV_NAME "lis2mdl"
#define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn"
#define IIS2MDC_MAGN_DEV_NAME "iis2mdc"
#define LSM303C_MAGN_DEV_NAME "lsm303c_magn"
#ifdef CONFIG_IIO_BUFFER
int st_magn_allocate_ring(struct iio_dev *indio_dev);
int st_magn_trig_set_state(struct iio_trigger *trig, bool state);
#define ST_MAGN_TRIGGER_SET_STATE (&st_magn_trig_set_state)
#else /* CONFIG_IIO_BUFFER */
static inline int st_magn_allocate_ring(struct iio_dev *indio_dev)
{
return 0;
}
#define ST_MAGN_TRIGGER_SET_STATE NULL
#endif /* CONFIG_IIO_BUFFER */
#endif /* ST_MAGN_H */
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/common/st_sensors.h>
#include "st_magn.h"
int st_magn_trig_set_state(struct iio_trigger *trig, bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
return st_sensors_set_dataready_irq(indio_dev, state);
}
static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
{
return st_sensors_set_enable(indio_dev, true);
}
static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
{
return st_sensors_set_enable(indio_dev, false);
}
static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = {
.postenable = &st_magn_buffer_postenable,
.predisable = &st_magn_buffer_predisable,
};
int st_magn_allocate_ring(struct iio_dev *indio_dev)
{
return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
NULL, &st_sensors_trigger_handler, &st_magn_buffer_setup_ops);
}
@@ -0,0 +1,665 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/common/st_sensors.h>
#include "st_magn.h"
#define ST_MAGN_NUMBER_DATA_CHANNELS 3
/* DEFAULT VALUE FOR SENSORS */
#define ST_MAGN_DEFAULT_OUT_X_H_ADDR 0x03
#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR 0x07
#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR 0x05
/* FULLSCALE */
#define ST_MAGN_FS_AVL_1300MG 1300
#define ST_MAGN_FS_AVL_1900MG 1900
#define ST_MAGN_FS_AVL_2000MG 2000
#define ST_MAGN_FS_AVL_2500MG 2500
#define ST_MAGN_FS_AVL_4000MG 4000
#define ST_MAGN_FS_AVL_4700MG 4700
#define ST_MAGN_FS_AVL_5600MG 5600
#define ST_MAGN_FS_AVL_8000MG 8000
#define ST_MAGN_FS_AVL_8100MG 8100
#define ST_MAGN_FS_AVL_12000MG 12000
#define ST_MAGN_FS_AVL_15000MG 15000
#define ST_MAGN_FS_AVL_16000MG 16000
/* Special L addresses for Sensor 2 */
#define ST_MAGN_2_OUT_X_L_ADDR 0x28
#define ST_MAGN_2_OUT_Y_L_ADDR 0x2a
#define ST_MAGN_2_OUT_Z_L_ADDR 0x2c
/* Special L addresses for sensor 3 */
#define ST_MAGN_3_OUT_X_L_ADDR 0x68
#define ST_MAGN_3_OUT_Y_L_ADDR 0x6a
#define ST_MAGN_3_OUT_Z_L_ADDR 0x6c
/* Special L addresses for sensor 4 */
#define ST_MAGN_4_OUT_X_L_ADDR 0x08
#define ST_MAGN_4_OUT_Y_L_ADDR 0x0a
#define ST_MAGN_4_OUT_Z_L_ADDR 0x0c
static const struct iio_mount_matrix *
st_magn_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct st_sensor_data *mdata = iio_priv(indio_dev);
return &mdata->mount_matrix;
}
static const struct iio_chan_spec_ext_info st_magn_mount_matrix_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_magn_get_mount_matrix),
{ }
};
static const struct iio_chan_spec st_magn_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_BE, 16, 16,
ST_MAGN_DEFAULT_OUT_X_H_ADDR,
st_magn_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_BE, 16, 16,
ST_MAGN_DEFAULT_OUT_Y_H_ADDR,
st_magn_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_BE, 16, 16,
ST_MAGN_DEFAULT_OUT_Z_H_ADDR,
st_magn_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
ST_MAGN_2_OUT_X_L_ADDR,
st_magn_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
ST_MAGN_2_OUT_Y_L_ADDR,
st_magn_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
ST_MAGN_2_OUT_Z_L_ADDR,
st_magn_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_magn_3_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
ST_MAGN_3_OUT_X_L_ADDR,
st_magn_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
ST_MAGN_3_OUT_Y_L_ADDR,
st_magn_mount_matrix_ext_info),
ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
ST_MAGN_3_OUT_Z_L_ADDR,
st_magn_mount_matrix_ext_info),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct iio_chan_spec st_magn_4_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
ST_MAGN_4_OUT_X_L_ADDR),
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
ST_MAGN_4_OUT_Y_L_ADDR),
ST_SENSORS_LSM_CHANNELS(IIO_MAGN,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
ST_MAGN_4_OUT_Z_L_ADDR),
IIO_CHAN_SOFT_TIMESTAMP(3)
};
static const struct st_sensor_settings st_magn_sensors_settings[] = {
{
.wai = 0, /* This sensor has no valid WhoAmI report 0 */
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM303DLH_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
.odr = {
.addr = 0x00,
.mask = 0x1c,
.odr_avl = {
{ .hz = 1, .value = 0x00 },
{ .hz = 2, .value = 0x01 },
{ .hz = 3, .value = 0x02 },
{ .hz = 8, .value = 0x03 },
{ .hz = 15, .value = 0x04 },
{ .hz = 30, .value = 0x05 },
{ .hz = 75, .value = 0x06 },
/* 220 Hz, 0x07 reportedly exist */
},
},
.pw = {
.addr = 0x02,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.addr = 0x01,
.mask = 0xe0,
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_1300MG,
.value = 0x01,
.gain = 1100,
.gain2 = 980,
},
[1] = {
.num = ST_MAGN_FS_AVL_1900MG,
.value = 0x02,
.gain = 855,
.gain2 = 760,
},
[2] = {
.num = ST_MAGN_FS_AVL_2500MG,
.value = 0x03,
.gain = 670,
.gain2 = 600,
},
[3] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = 0x04,
.gain = 450,
.gain2 = 400,
},
[4] = {
.num = ST_MAGN_FS_AVL_4700MG,
.value = 0x05,
.gain = 400,
.gain2 = 355,
},
[5] = {
.num = ST_MAGN_FS_AVL_5600MG,
.value = 0x06,
.gain = 330,
.gain2 = 295,
},
[6] = {
.num = ST_MAGN_FS_AVL_8100MG,
.value = 0x07,
.gain = 230,
.gain2 = 205,
},
},
},
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = 0x3c,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM303DLHC_MAGN_DEV_NAME,
[1] = LSM303DLM_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
.odr = {
.addr = 0x00,
.mask = 0x1c,
.odr_avl = {
{ .hz = 1, .value = 0x00 },
{ .hz = 2, .value = 0x01 },
{ .hz = 3, .value = 0x02 },
{ .hz = 8, .value = 0x03 },
{ .hz = 15, .value = 0x04 },
{ .hz = 30, .value = 0x05 },
{ .hz = 75, .value = 0x06 },
{ .hz = 220, .value = 0x07 },
},
},
.pw = {
.addr = 0x02,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.addr = 0x01,
.mask = 0xe0,
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_1300MG,
.value = 0x01,
.gain = 909,
.gain2 = 1020,
},
[1] = {
.num = ST_MAGN_FS_AVL_1900MG,
.value = 0x02,
.gain = 1169,
.gain2 = 1315,
},
[2] = {
.num = ST_MAGN_FS_AVL_2500MG,
.value = 0x03,
.gain = 1492,
.gain2 = 1666,
},
[3] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = 0x04,
.gain = 2222,
.gain2 = 2500,
},
[4] = {
.num = ST_MAGN_FS_AVL_4700MG,
.value = 0x05,
.gain = 2500,
.gain2 = 2816,
},
[5] = {
.num = ST_MAGN_FS_AVL_5600MG,
.value = 0x06,
.gain = 3030,
.gain2 = 3389,
},
[6] = {
.num = ST_MAGN_FS_AVL_8100MG,
.value = 0x07,
.gain = 4347,
.gain2 = 4878,
},
},
},
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = 0x3d,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3MDL_MAGN_DEV_NAME,
[1] = LSM9DS1_MAGN_DEV_NAME,
[2] = LSM303C_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
.odr = {
.addr = 0x20,
.mask = 0x1c,
.odr_avl = {
{ .hz = 1, .value = 0x00 },
{ .hz = 2, .value = 0x01 },
{ .hz = 3, .value = 0x02 },
{ .hz = 5, .value = 0x03 },
{ .hz = 10, .value = 0x04 },
{ .hz = 20, .value = 0x05 },
{ .hz = 40, .value = 0x06 },
{ .hz = 80, .value = 0x07 },
},
},
.pw = {
.addr = 0x22,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.addr = 0x21,
.mask = 0x60,
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = 0x00,
.gain = 146,
},
[1] = {
.num = ST_MAGN_FS_AVL_8000MG,
.value = 0x01,
.gain = 292,
},
[2] = {
.num = ST_MAGN_FS_AVL_12000MG,
.value = 0x02,
.gain = 438,
},
[3] = {
.num = ST_MAGN_FS_AVL_16000MG,
.value = 0x03,
.gain = 584,
},
},
},
.bdu = {
.addr = 0x24,
.mask = 0x40,
},
.drdy_irq = {
/* drdy line is routed drdy pin */
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0x07,
},
},
.sim = {
.addr = 0x22,
.value = BIT(2),
},
.multi_read_bit = true,
.bootime = 2,
},
{
.wai = 0x40,
.wai_addr = 0x4f,
.sensors_supported = {
[0] = LSM303AGR_MAGN_DEV_NAME,
[1] = LIS2MDL_MAGN_DEV_NAME,
[2] = IIS2MDC_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_3_16bit_channels,
.odr = {
.addr = 0x60,
.mask = 0x0c,
.odr_avl = {
{ .hz = 10, .value = 0x00 },
{ .hz = 20, .value = 0x01 },
{ .hz = 50, .value = 0x02 },
{ .hz = 100, .value = 0x03 },
},
},
.pw = {
.addr = 0x60,
.mask = 0x03,
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_15000MG,
.gain = 1500,
},
},
},
.bdu = {
.addr = 0x62,
.mask = 0x10,
},
.drdy_irq = {
.int1 = {
.addr = 0x62,
.mask = 0x01,
},
.stat_drdy = {
.addr = 0x67,
.mask = 0x07,
},
},
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = 0x49,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM9DS0_IMU_DEV_NAME,
[1] = LSM303D_IMU_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_4_16bit_channels,
.odr = {
.addr = 0x24,
.mask = GENMASK(4, 2),
.odr_avl = {
{ 3, 0x00, },
{ 6, 0x01, },
{ 12, 0x02, },
{ 25, 0x03, },
{ 50, 0x04, },
{ 100, 0x05, },
},
},
.pw = {
.addr = 0x26,
.mask = GENMASK(1, 0),
.value_on = 0x00,
.value_off = 0x03,
},
.fs = {
.addr = 0x25,
.mask = GENMASK(6, 5),
.fs_avl = {
[0] = {
.num = ST_MAGN_FS_AVL_2000MG,
.value = 0x00,
.gain = 73,
},
[1] = {
.num = ST_MAGN_FS_AVL_4000MG,
.value = 0x01,
.gain = 146,
},
[2] = {
.num = ST_MAGN_FS_AVL_8000MG,
.value = 0x02,
.gain = 292,
},
[3] = {
.num = ST_MAGN_FS_AVL_12000MG,
.value = 0x03,
.gain = 438,
},
},
},
.bdu = {
.addr = 0x20,
.mask = BIT(3),
},
.drdy_irq = {
.int1 = {
.addr = 0x22,
.mask = BIT(1),
},
.int2 = {
.addr = 0x23,
.mask = BIT(2),
},
.stat_drdy = {
.addr = 0x07,
.mask = GENMASK(2, 0),
},
},
.sim = {
.addr = 0x21,
.value = BIT(0),
},
.multi_read_bit = true,
.bootime = 2,
},
};
/* Default magn DRDY is available on INT2 pin */
static const struct st_sensors_platform_data default_magn_pdata = {
.drdy_int_pin = 2,
};
/* LIS2MDL only supports DRDY on INT1 */
static const struct st_sensors_platform_data alt_magn_pdata = {
.drdy_int_pin = 1,
};
static int st_magn_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *ch, int *val,
int *val2, long mask)
{
int err;
struct st_sensor_data *mdata = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
err = st_sensors_read_info_raw(indio_dev, ch, val);
if (err < 0)
goto read_error;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
if ((ch->scan_index == ST_SENSORS_SCAN_Z) &&
(mdata->current_fullscale->gain2 != 0))
*val2 = mdata->current_fullscale->gain2;
else
*val2 = mdata->current_fullscale->gain;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = mdata->odr;
return IIO_VAL_INT;
default:
return -EINVAL;
}
read_error:
return err;
}
static int st_magn_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return st_sensors_set_fullscale_by_gain(indio_dev, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2)
return -EINVAL;
return st_sensors_set_odr(indio_dev, val);
default:
return -EINVAL;
}
}
static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_magn_scale_available);
static struct attribute *st_magn_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_magn_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group st_magn_attribute_group = {
.attrs = st_magn_attributes,
};
static const struct iio_info magn_info = {
.attrs = &st_magn_attribute_group,
.read_raw = &st_magn_read_raw,
.write_raw = &st_magn_write_raw,
.debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
static const struct iio_trigger_ops st_magn_trigger_ops = {
.set_trigger_state = ST_MAGN_TRIGGER_SET_STATE,
.validate_device = st_sensors_validate_device,
};
#define ST_MAGN_TRIGGER_OPS (&st_magn_trigger_ops)
#else
#define ST_MAGN_TRIGGER_OPS NULL
#endif
/*
* st_magn_get_settings() - get sensor settings from device name
* @name: device name buffer reference.
*
* Return: valid reference on success, NULL otherwise.
*/
const struct st_sensor_settings *st_magn_get_settings(const char *name)
{
int index = st_sensors_get_settings_index(name,
st_magn_sensors_settings,
ARRAY_SIZE(st_magn_sensors_settings));
if (index < 0)
return NULL;
return &st_magn_sensors_settings[index];
}
EXPORT_SYMBOL_NS(st_magn_get_settings, "IIO_ST_SENSORS");
int st_magn_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *mdata = iio_priv(indio_dev);
struct device *parent = indio_dev->dev.parent;
struct st_sensors_platform_data *pdata = dev_get_platdata(parent);
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &magn_info;
err = st_sensors_verify_id(indio_dev);
if (err < 0)
return err;
mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
indio_dev->channels = mdata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
err = iio_read_mount_matrix(parent, &mdata->mount_matrix);
if (err)
return err;
mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0];
mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz;
if (!pdata) {
if (mdata->sensor_settings->drdy_irq.int2.mask)
pdata = (struct st_sensors_platform_data *)&default_magn_pdata;
else
pdata = (struct st_sensors_platform_data *)&alt_magn_pdata;
}
err = st_sensors_init_sensor(indio_dev, pdata);
if (err < 0)
return err;
err = st_magn_allocate_ring(indio_dev);
if (err < 0)
return err;
if (mdata->irq > 0) {
err = st_sensors_allocate_trigger(indio_dev,
ST_MAGN_TRIGGER_OPS);
if (err < 0)
return err;
}
return devm_iio_device_register(parent, indio_dev);
}
EXPORT_SYMBOL_NS(st_magn_common_probe, "IIO_ST_SENSORS");
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics magnetometers driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_ST_SENSORS");
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/common/st_sensors.h>
#include <linux/iio/common/st_sensors_i2c.h>
#include "st_magn.h"
static const struct of_device_id st_magn_of_match[] = {
{
.compatible = "st,lsm303dlh-magn",
.data = LSM303DLH_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm303dlhc-magn",
.data = LSM303DLHC_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm303dlm-magn",
.data = LSM303DLM_MAGN_DEV_NAME,
},
{
.compatible = "st,lis3mdl-magn",
.data = LIS3MDL_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm303agr-magn",
.data = LSM303AGR_MAGN_DEV_NAME,
},
{
.compatible = "st,lis2mdl",
.data = LIS2MDL_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm9ds1-magn",
.data = LSM9DS1_MAGN_DEV_NAME,
},
{
.compatible = "st,iis2mdc",
.data = IIS2MDC_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm303c-magn",
.data = LSM303C_MAGN_DEV_NAME,
},
{ }
};
MODULE_DEVICE_TABLE(of, st_magn_of_match);
static int st_magn_i2c_probe(struct i2c_client *client)
{
const struct st_sensor_settings *settings;
struct st_sensor_data *mdata;
struct iio_dev *indio_dev;
int err;
st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name));
settings = st_magn_get_settings(client->name);
if (!settings) {
dev_err(&client->dev, "device name %s not recognized.\n",
client->name);
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mdata));
if (!indio_dev)
return -ENOMEM;
mdata = iio_priv(indio_dev);
mdata->sensor_settings = (struct st_sensor_settings *)settings;
err = st_sensors_i2c_configure(indio_dev, client);
if (err < 0)
return err;
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
return st_magn_common_probe(indio_dev);
}
static const struct i2c_device_id st_magn_id_table[] = {
{ .name = LSM303DLH_MAGN_DEV_NAME },
{ .name = LSM303DLHC_MAGN_DEV_NAME },
{ .name = LSM303DLM_MAGN_DEV_NAME },
{ .name = LIS3MDL_MAGN_DEV_NAME },
{ .name = LSM303AGR_MAGN_DEV_NAME },
{ .name = LIS2MDL_MAGN_DEV_NAME },
{ .name = LSM9DS1_MAGN_DEV_NAME },
{ .name = IIS2MDC_MAGN_DEV_NAME },
{ .name = LSM303C_MAGN_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
static struct i2c_driver st_magn_driver = {
.driver = {
.name = "st-magn-i2c",
.of_match_table = st_magn_of_match,
},
.probe = st_magn_i2c_probe,
.id_table = st_magn_id_table,
};
module_i2c_driver(st_magn_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics magnetometers i2c driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_ST_SENSORS");
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/common/st_sensors.h>
#include <linux/iio/common/st_sensors_spi.h>
#include "st_magn.h"
/*
* For new single-chip sensors use <device_name> as compatible string.
* For old single-chip devices keep <device_name>-magn to maintain
* compatibility
* For multi-chip devices, use <device_name>-magn to distinguish which
* capability is being used
*/
static const struct of_device_id st_magn_of_match[] = {
{
.compatible = "st,lis3mdl-magn",
.data = LIS3MDL_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm303agr-magn",
.data = LSM303AGR_MAGN_DEV_NAME,
},
{
.compatible = "st,lis2mdl",
.data = LIS2MDL_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm9ds1-magn",
.data = LSM9DS1_MAGN_DEV_NAME,
},
{
.compatible = "st,iis2mdc",
.data = IIS2MDC_MAGN_DEV_NAME,
},
{
.compatible = "st,lsm303c-magn",
.data = LSM303C_MAGN_DEV_NAME,
},
{ }
};
MODULE_DEVICE_TABLE(of, st_magn_of_match);
static int st_magn_spi_probe(struct spi_device *spi)
{
const struct st_sensor_settings *settings;
struct st_sensor_data *mdata;
struct iio_dev *indio_dev;
int err;
st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias));
settings = st_magn_get_settings(spi->modalias);
if (!settings) {
dev_err(&spi->dev, "device name %s not recognized.\n",
spi->modalias);
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*mdata));
if (!indio_dev)
return -ENOMEM;
mdata = iio_priv(indio_dev);
mdata->sensor_settings = (struct st_sensor_settings *)settings;
err = st_sensors_spi_configure(indio_dev, spi);
if (err < 0)
return err;
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
return st_magn_common_probe(indio_dev);
}
static const struct spi_device_id st_magn_id_table[] = {
{ LIS3MDL_MAGN_DEV_NAME },
{ LSM303AGR_MAGN_DEV_NAME },
{ LIS2MDL_MAGN_DEV_NAME },
{ LSM9DS1_MAGN_DEV_NAME },
{ IIS2MDC_MAGN_DEV_NAME },
{ LSM303C_MAGN_DEV_NAME },
{ }
};
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
static struct spi_driver st_magn_driver = {
.driver = {
.name = "st-magn-spi",
.of_match_table = st_magn_of_match,
},
.probe = st_magn_spi_probe,
.id_table = st_magn_id_table,
};
module_spi_driver(st_magn_driver);
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
MODULE_DESCRIPTION("STMicroelectronics magnetometers spi driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_ST_SENSORS");
@@ -0,0 +1,525 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Infineon TLV493D Low-Power 3D Magnetic Sensor
*
* Copyright (C) 2025 Dixit Parmar <dixitparmar19@gmail.com>
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/i2c.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
/*
* TLV493D sensor I2C communication note:
*
* The sensor supports only direct byte-stream write starting from the
* register address 0x0. So for any modification to be made to any write
* registers, it must be written starting from the register address 0x0.
* I2C write operation should not contain the register address in the I2C
* frame, it should contain only raw byte stream for the write registers.
* I2C Frame: |S|SlaveAddr Wr|Ack|Byte[0]|Ack|Byte[1]|Ack|.....|Sp|
*
* Same as the write operation, reading from the sensor registers is also
* performed starting from the register address 0x0 for as many bytes as
* need to be read.
* I2C read operation should not contain the register address in the I2C frame.
* I2C Frame: |S|SlaveAddr Rd|Ack|Byte[0]|Ack|Byte[1]|Ack|.....|Sp|
*/
#define TLV493D_RD_REG_BX 0x00
#define TLV493D_RD_REG_BY 0x01
#define TLV493D_RD_REG_BZ 0x02
#define TLV493D_RD_REG_TEMP 0x03
#define TLV493D_RD_REG_BX2 0x04
#define TLV493D_RD_REG_BZ2 0x05
#define TLV493D_RD_REG_TEMP2 0x06
#define TLV493D_RD_REG_RES1 0x07
#define TLV493D_RD_REG_RES2 0x08
#define TLV493D_RD_REG_RES3 0x09
#define TLV493D_RD_REG_MAX 0x0a
#define TLV493D_WR_REG_MODE1 0x01
#define TLV493D_WR_REG_MODE2 0x03
#define TLV493D_WR_REG_MAX 0x04
#define TLV493D_BX_MAG_X_AXIS_MSB GENMASK(7, 0)
#define TLV493D_BX2_MAG_X_AXIS_LSB GENMASK(7, 4)
#define TLV493D_BY_MAG_Y_AXIS_MSB GENMASK(7, 0)
#define TLV493D_BX2_MAG_Y_AXIS_LSB GENMASK(3, 0)
#define TLV493D_BZ_MAG_Z_AXIS_MSB GENMASK(7, 0)
#define TLV493D_BZ2_MAG_Z_AXIS_LSB GENMASK(3, 0)
#define TLV493D_TEMP_TEMP_MSB GENMASK(7, 4)
#define TLV493D_TEMP2_TEMP_LSB GENMASK(7, 0)
#define TLV493D_TEMP_CHANNEL GENMASK(1, 0)
#define TLV493D_MODE1_MOD_LOWFAST GENMASK(1, 0)
#define TLV493D_MODE2_LP_PERIOD BIT(6)
#define TLV493D_RD_REG_RES1_WR_MASK GENMASK(4, 3)
#define TLV493D_RD_REG_RES2_WR_MASK GENMASK(7, 0)
#define TLV493D_RD_REG_RES3_WR_MASK GENMASK(4, 0)
enum tlv493d_channels {
TLV493D_AXIS_X,
TLV493D_AXIS_Y,
TLV493D_AXIS_Z,
TLV493D_TEMPERATURE,
};
enum tlv493d_op_mode {
TLV493D_OP_MODE_POWERDOWN,
TLV493D_OP_MODE_FAST,
TLV493D_OP_MODE_LOWPOWER,
TLV493D_OP_MODE_ULTRA_LOWPOWER,
TLV493D_OP_MODE_MASTERCONTROLLED,
};
struct tlv493d_data {
struct i2c_client *client;
/* protects from simultaneous sensor access and register readings */
struct mutex lock;
enum tlv493d_op_mode mode;
u8 wr_regs[TLV493D_WR_REG_MAX];
};
/*
* Different mode has different measurement sampling time, this time is
* used in deriving the sleep and timeout while reading the data from
* sensor in polling.
* Power-down mode: No measurement.
* Fast mode: Freq:3.3 KHz. Measurement time:305 usec.
* Low-power mode: Freq:100 Hz. Measurement time:10 msec.
* Ultra low-power mode: Freq:10 Hz. Measurement time:100 msec.
* Master controlled mode: Freq:3.3 Khz. Measurement time:305 usec.
*/
static const u32 tlv493d_sample_rate_us[] = {
[TLV493D_OP_MODE_POWERDOWN] = 0,
[TLV493D_OP_MODE_FAST] = 305,
[TLV493D_OP_MODE_LOWPOWER] = 10 * USEC_PER_MSEC,
[TLV493D_OP_MODE_ULTRA_LOWPOWER] = 100 * USEC_PER_MSEC,
[TLV493D_OP_MODE_MASTERCONTROLLED] = 305,
};
static int tlv493d_write_all_regs(struct tlv493d_data *data)
{
int ret;
struct device *dev = &data->client->dev;
ret = i2c_master_send(data->client, data->wr_regs, ARRAY_SIZE(data->wr_regs));
if (ret < 0) {
dev_err(dev, "i2c write registers failed, error: %d\n", ret);
return ret;
}
return 0;
}
static int tlv493d_set_operating_mode(struct tlv493d_data *data, enum tlv493d_op_mode mode)
{
u8 *mode1_cfg = &data->wr_regs[TLV493D_WR_REG_MODE1];
u8 *mode2_cfg = &data->wr_regs[TLV493D_WR_REG_MODE2];
switch (mode) {
case TLV493D_OP_MODE_POWERDOWN:
FIELD_MODIFY(TLV493D_MODE1_MOD_LOWFAST, mode1_cfg, 0);
FIELD_MODIFY(TLV493D_MODE2_LP_PERIOD, mode2_cfg, 0);
break;
case TLV493D_OP_MODE_FAST:
FIELD_MODIFY(TLV493D_MODE1_MOD_LOWFAST, mode1_cfg, 1);
FIELD_MODIFY(TLV493D_MODE2_LP_PERIOD, mode2_cfg, 0);
break;
case TLV493D_OP_MODE_LOWPOWER:
FIELD_MODIFY(TLV493D_MODE1_MOD_LOWFAST, mode1_cfg, 2);
FIELD_MODIFY(TLV493D_MODE2_LP_PERIOD, mode2_cfg, 1);
break;
case TLV493D_OP_MODE_ULTRA_LOWPOWER:
FIELD_MODIFY(TLV493D_MODE1_MOD_LOWFAST, mode1_cfg, 2);
FIELD_MODIFY(TLV493D_MODE2_LP_PERIOD, mode2_cfg, 0);
break;
case TLV493D_OP_MODE_MASTERCONTROLLED:
FIELD_MODIFY(TLV493D_MODE1_MOD_LOWFAST, mode1_cfg, 3);
FIELD_MODIFY(TLV493D_MODE2_LP_PERIOD, mode2_cfg, 0);
break;
}
return tlv493d_write_all_regs(data);
}
static s16 tlv493d_get_channel_data(u8 *b, enum tlv493d_channels ch)
{
u16 val;
switch (ch) {
case TLV493D_AXIS_X:
val = FIELD_GET(TLV493D_BX_MAG_X_AXIS_MSB, b[TLV493D_RD_REG_BX]) << 4 |
FIELD_GET(TLV493D_BX2_MAG_X_AXIS_LSB, b[TLV493D_RD_REG_BX2]);
break;
case TLV493D_AXIS_Y:
val = FIELD_GET(TLV493D_BY_MAG_Y_AXIS_MSB, b[TLV493D_RD_REG_BY]) << 4 |
FIELD_GET(TLV493D_BX2_MAG_Y_AXIS_LSB, b[TLV493D_RD_REG_BX2]);
break;
case TLV493D_AXIS_Z:
val = FIELD_GET(TLV493D_BZ_MAG_Z_AXIS_MSB, b[TLV493D_RD_REG_BZ]) << 4 |
FIELD_GET(TLV493D_BZ2_MAG_Z_AXIS_LSB, b[TLV493D_RD_REG_BZ2]);
break;
case TLV493D_TEMPERATURE:
val = FIELD_GET(TLV493D_TEMP_TEMP_MSB, b[TLV493D_RD_REG_TEMP]) << 8 |
FIELD_GET(TLV493D_TEMP2_TEMP_LSB, b[TLV493D_RD_REG_TEMP2]);
break;
}
return sign_extend32(val, 11);
}
static int tlv493d_get_measurements(struct tlv493d_data *data, s16 *x, s16 *y,
s16 *z, s16 *t)
{
u8 buff[7] = {};
int err, ret;
struct device *dev = &data->client->dev;
u32 sleep_us = tlv493d_sample_rate_us[data->mode];
guard(mutex)(&data->lock);
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
/*
* Poll until data is valid.
* For a valid data TLV493D_TEMP_CHANNEL bit of TLV493D_RD_REG_TEMP
* should be set to 0. The sampling time depends on the sensor mode.
* Poll 3x the time of the sampling time.
*/
ret = read_poll_timeout(i2c_master_recv, err,
err || !FIELD_GET(TLV493D_TEMP_CHANNEL, buff[TLV493D_RD_REG_TEMP]),
sleep_us, 3 * sleep_us, false, data->client, buff,
ARRAY_SIZE(buff));
if (ret) {
dev_err(dev, "i2c read poll timeout, error:%d\n", ret);
goto out_put_autosuspend;
}
if (err < 0) {
dev_err(dev, "i2c read data failed, error:%d\n", err);
ret = err;
goto out_put_autosuspend;
}
*x = tlv493d_get_channel_data(buff, TLV493D_AXIS_X);
*y = tlv493d_get_channel_data(buff, TLV493D_AXIS_Y);
*z = tlv493d_get_channel_data(buff, TLV493D_AXIS_Z);
*t = tlv493d_get_channel_data(buff, TLV493D_TEMPERATURE);
out_put_autosuspend:
pm_runtime_put_autosuspend(dev);
return ret;
}
static int tlv493d_init(struct tlv493d_data *data)
{
int ret;
u8 buff[TLV493D_RD_REG_MAX];
struct device *dev = &data->client->dev;
/*
* The sensor initialization requires below steps to be followed,
* 1. Power-up sensor.
* 2. Read and store read-registers map (0x0-0x9).
* 3. Copy values from read reserved registers to write reserved fields
* (0x0-0x3).
* 4. Set operating mode.
* 5. Write to all registers.
*/
ret = i2c_master_recv(data->client, buff, ARRAY_SIZE(buff));
if (ret < 0)
return dev_err_probe(dev, ret, "i2c read failed\n");
/* Write register 0x0 is reserved. Does not require to be updated.*/
data->wr_regs[0] = 0;
data->wr_regs[1] = buff[TLV493D_RD_REG_RES1] & TLV493D_RD_REG_RES1_WR_MASK;
data->wr_regs[2] = buff[TLV493D_RD_REG_RES2] & TLV493D_RD_REG_RES2_WR_MASK;
data->wr_regs[3] = buff[TLV493D_RD_REG_RES3] & TLV493D_RD_REG_RES3_WR_MASK;
ret = tlv493d_set_operating_mode(data, data->mode);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to set operating mode\n");
return 0;
}
static int tlv493d_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val,
int *val2, long mask)
{
struct tlv493d_data *data = iio_priv(indio_dev);
s16 x, y, z, t;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = tlv493d_get_measurements(data, &x, &y, &z, &t);
if (ret)
return ret;
switch (chan->address) {
case TLV493D_AXIS_X:
*val = x;
return IIO_VAL_INT;
case TLV493D_AXIS_Y:
*val = y;
return IIO_VAL_INT;
case TLV493D_AXIS_Z:
*val = z;
return IIO_VAL_INT;
case TLV493D_TEMPERATURE:
*val = t;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_MAGN:
/*
* Magnetic field scale: 0.0098 mTesla (i.e. 9.8 µT)
* Magnetic field in Gauss: mT * 10 = 0.098.
*/
*val = 98;
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
case IIO_TEMP:
/*
* Temperature scale: 1.1 °C per LSB, expressed as 1100 m°C
* Returned as integer for IIO core to apply:
* temp = (raw + offset) * scale
*/
*val = 1100;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_TEMP:
/*
* Temperature offset includes sensor-specific raw offset
* plus compensation for +25°C bias in formula.
* offset = -raw_offset + (25000 / 1100)
* -340 + 22.72 = -317.28
*/
*val = -31728;
*val2 = 100;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static irqreturn_t tlv493d_trigger_handler(int irq, void *ptr)
{
int ret;
s16 x, y, z, t;
struct iio_poll_func *pf = ptr;
struct iio_dev *indio_dev = pf->indio_dev;
struct tlv493d_data *data = iio_priv(indio_dev);
struct device *dev = &data->client->dev;
struct {
s16 channels[3];
s16 temperature;
aligned_s64 timestamp;
} scan;
ret = tlv493d_get_measurements(data, &x, &y, &z, &t);
if (ret) {
dev_err(dev, "failed to read sensor data\n");
goto out_trigger_notify;
}
scan.channels[0] = x;
scan.channels[1] = y;
scan.channels[2] = z;
scan.temperature = t;
iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), pf->timestamp);
out_trigger_notify:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
#define TLV493D_AXIS_CHANNEL(axis, index) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.address = index, \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec tlv493d_channels[] = {
TLV493D_AXIS_CHANNEL(X, TLV493D_AXIS_X),
TLV493D_AXIS_CHANNEL(Y, TLV493D_AXIS_Y),
TLV493D_AXIS_CHANNEL(Z, TLV493D_AXIS_Z),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = TLV493D_TEMPERATURE,
.scan_index = TLV493D_TEMPERATURE,
.scan_type = {
.sign = 's',
.realbits = 12,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct iio_info tlv493d_info = {
.read_raw = tlv493d_read_raw,
};
static const unsigned long tlv493d_scan_masks[] = { GENMASK(3, 0), 0 };
static int tlv493d_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct iio_dev *indio_dev;
struct tlv493d_data *data;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
ret = devm_mutex_init(dev, &data->lock);
if (ret)
return ret;
ret = devm_regulator_get_enable(dev, "vdd");
if (ret)
return dev_err_probe(dev, ret, "failed to enable regulator\n");
/*
* Setting Sensor default operating mode to Master-Controlled mode since
* it performs measurement cycle only on-request and stays in Power-Down
* state until next cycle is initiated.
*/
data->mode = TLV493D_OP_MODE_MASTERCONTROLLED;
ret = tlv493d_init(data);
if (ret)
return dev_err_probe(dev, ret, "failed to initialize\n");
indio_dev->info = &tlv493d_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = client->name;
indio_dev->channels = tlv493d_channels;
indio_dev->num_channels = ARRAY_SIZE(tlv493d_channels);
indio_dev->available_scan_masks = tlv493d_scan_masks;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
tlv493d_trigger_handler,
NULL);
if (ret)
return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n");
ret = pm_runtime_set_active(dev);
if (ret)
return ret;
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
pm_runtime_get_noresume(dev);
pm_runtime_set_autosuspend_delay(dev, 500);
pm_runtime_use_autosuspend(dev);
pm_runtime_put_autosuspend(dev);
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "iio device register failed\n");
return 0;
}
static int tlv493d_runtime_suspend(struct device *dev)
{
struct tlv493d_data *data = iio_priv(dev_get_drvdata(dev));
return tlv493d_set_operating_mode(data, TLV493D_OP_MODE_POWERDOWN);
}
static int tlv493d_runtime_resume(struct device *dev)
{
struct tlv493d_data *data = iio_priv(dev_get_drvdata(dev));
return tlv493d_set_operating_mode(data, data->mode);
}
static DEFINE_RUNTIME_DEV_PM_OPS(tlv493d_pm_ops, tlv493d_runtime_suspend,
tlv493d_runtime_resume, NULL);
static const struct i2c_device_id tlv493d_id[] = {
{ .name = "tlv493d" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tlv493d_id);
static const struct of_device_id tlv493d_of_match[] = {
{ .compatible = "infineon,tlv493d-a1b6" },
{ }
};
MODULE_DEVICE_TABLE(of, tlv493d_of_match);
static struct i2c_driver tlv493d_driver = {
.driver = {
.name = "tlv493d",
.of_match_table = tlv493d_of_match,
.pm = pm_ptr(&tlv493d_pm_ops),
},
.probe = tlv493d_probe,
.id_table = tlv493d_id,
};
module_i2c_driver(tlv493d_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Infineon TLV493D Low-Power 3D Magnetic Sensor");
MODULE_AUTHOR("Dixit Parmar <dixitparmar19@gmail.com>");
@@ -0,0 +1,735 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor
*
* Copyright (C) 2022 WolfVision GmbH
*
* Author: Gerald Loacker <gerald.loacker@wolfvision.net>
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define TMAG5273_DEVICE_CONFIG_1 0x00
#define TMAG5273_DEVICE_CONFIG_2 0x01
#define TMAG5273_SENSOR_CONFIG_1 0x02
#define TMAG5273_SENSOR_CONFIG_2 0x03
#define TMAG5273_X_THR_CONFIG 0x04
#define TMAG5273_Y_THR_CONFIG 0x05
#define TMAG5273_Z_THR_CONFIG 0x06
#define TMAG5273_T_CONFIG 0x07
#define TMAG5273_INT_CONFIG_1 0x08
#define TMAG5273_MAG_GAIN_CONFIG 0x09
#define TMAG5273_MAG_OFFSET_CONFIG_1 0x0A
#define TMAG5273_MAG_OFFSET_CONFIG_2 0x0B
#define TMAG5273_I2C_ADDRESS 0x0C
#define TMAG5273_DEVICE_ID 0x0D
#define TMAG5273_MANUFACTURER_ID_LSB 0x0E
#define TMAG5273_MANUFACTURER_ID_MSB 0x0F
#define TMAG5273_T_MSB_RESULT 0x10
#define TMAG5273_T_LSB_RESULT 0x11
#define TMAG5273_X_MSB_RESULT 0x12
#define TMAG5273_X_LSB_RESULT 0x13
#define TMAG5273_Y_MSB_RESULT 0x14
#define TMAG5273_Y_LSB_RESULT 0x15
#define TMAG5273_Z_MSB_RESULT 0x16
#define TMAG5273_Z_LSB_RESULT 0x17
#define TMAG5273_CONV_STATUS 0x18
#define TMAG5273_ANGLE_RESULT_MSB 0x19
#define TMAG5273_ANGLE_RESULT_LSB 0x1A
#define TMAG5273_MAGNITUDE_RESULT 0x1B
#define TMAG5273_DEVICE_STATUS 0x1C
#define TMAG5273_MAX_REG TMAG5273_DEVICE_STATUS
#define TMAG5273_AUTOSLEEP_DELAY_MS 5000
#define TMAG5273_MAX_AVERAGE 32
/*
* bits in the TMAG5273_MANUFACTURER_ID_LSB / MSB register
* 16-bit unique manufacturer ID 0x49 / 0x54 = "TI"
*/
#define TMAG5273_MANUFACTURER_ID 0x5449
/* bits in the TMAG5273_DEVICE_CONFIG_1 register */
#define TMAG5273_AVG_MODE_MASK GENMASK(4, 2)
#define TMAG5273_AVG_1_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 0)
#define TMAG5273_AVG_2_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 1)
#define TMAG5273_AVG_4_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 2)
#define TMAG5273_AVG_8_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 3)
#define TMAG5273_AVG_16_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 4)
#define TMAG5273_AVG_32_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 5)
/* bits in the TMAG5273_DEVICE_CONFIG_2 register */
#define TMAG5273_OP_MODE_MASK GENMASK(1, 0)
#define TMAG5273_OP_MODE_STANDBY FIELD_PREP(TMAG5273_OP_MODE_MASK, 0)
#define TMAG5273_OP_MODE_SLEEP FIELD_PREP(TMAG5273_OP_MODE_MASK, 1)
#define TMAG5273_OP_MODE_CONT FIELD_PREP(TMAG5273_OP_MODE_MASK, 2)
#define TMAG5273_OP_MODE_WAKEUP FIELD_PREP(TMAG5273_OP_MODE_MASK, 3)
/* bits in the TMAG5273_SENSOR_CONFIG_1 register */
#define TMAG5273_MAG_CH_EN_MASK GENMASK(7, 4)
#define TMAG5273_MAG_CH_EN_X_Y_Z 7
/* bits in the TMAG5273_SENSOR_CONFIG_2 register */
#define TMAG5273_Z_RANGE_MASK BIT(0)
#define TMAG5273_X_Y_RANGE_MASK BIT(1)
#define TMAG5273_ANGLE_EN_MASK GENMASK(3, 2)
#define TMAG5273_ANGLE_EN_OFF 0
#define TMAG5273_ANGLE_EN_X_Y 1
#define TMAG5273_ANGLE_EN_Y_Z 2
#define TMAG5273_ANGLE_EN_X_Z 3
/* bits in the TMAG5273_T_CONFIG register */
#define TMAG5273_T_CH_EN BIT(0)
/* bits in the TMAG5273_DEVICE_ID register */
#define TMAG5273_VERSION_MASK GENMASK(1, 0)
/* bits in the TMAG5273_CONV_STATUS register */
#define TMAG5273_CONV_STATUS_COMPLETE BIT(0)
enum tmag5273_channels {
TEMPERATURE = 0,
AXIS_X,
AXIS_Y,
AXIS_Z,
ANGLE,
MAGNITUDE,
};
enum tmag5273_scale_index {
MAGN_RANGE_LOW = 0,
MAGN_RANGE_HIGH,
MAGN_RANGE_NUM
};
/* state container for the TMAG5273 driver */
struct tmag5273_data {
struct device *dev;
unsigned int devid;
unsigned int version;
char name[16];
unsigned int conv_avg;
enum tmag5273_scale_index scale_index;
unsigned int angle_measurement;
struct regmap *map;
/*
* Locks the sensor for exclusive use during a measurement (which
* involves several register transactions so the regmap lock is not
* enough) so that measurements get serialized in a
* first-come-first-serve manner.
*/
struct mutex lock;
};
static const char *const tmag5273_angle_names[] = { "off", "x-y", "y-z", "x-z" };
/*
* Averaging enables additional sampling of the sensor data to reduce the noise
* effect, but also increases conversion time.
*/
static const unsigned int tmag5273_avg_table[] = {
1, 2, 4, 8, 16, 32,
};
/*
* Magnetic resolution in Gauss for different TMAG5273 versions.
* Scale[Gauss] = Range[mT] * 1000 / 2^15 * 10, (1 mT = 10 Gauss)
* Only version 1 and 2 are valid, version 0 and 3 are reserved.
*/
static const struct iio_val_int_plus_micro tmag5273_scale[][MAGN_RANGE_NUM] = {
{ { 0, 0 }, { 0, 0 } },
{ { 0, 12200 }, { 0, 24400 } },
{ { 0, 40600 }, { 0, 81200 } },
{ { 0, 0 }, { 0, 0 } },
};
static int tmag5273_get_measure(struct tmag5273_data *data, s16 *t, s16 *x,
s16 *y, s16 *z, u16 *angle, u16 *magnitude)
{
unsigned int status, val;
__be16 reg_data[4];
int ret;
mutex_lock(&data->lock);
/*
* Max. conversion time is 2425 us in 32x averaging mode for all three
* channels. Since we are in continuous measurement mode, a measurement
* may already be there, so poll for completed measurement with
* timeout.
*/
ret = regmap_read_poll_timeout(data->map, TMAG5273_CONV_STATUS, status,
status & TMAG5273_CONV_STATUS_COMPLETE,
100, 10000);
if (ret) {
dev_err(data->dev, "timeout waiting for measurement\n");
goto out_unlock;
}
ret = regmap_bulk_read(data->map, TMAG5273_T_MSB_RESULT, reg_data,
sizeof(reg_data));
if (ret)
goto out_unlock;
*t = be16_to_cpu(reg_data[0]);
*x = be16_to_cpu(reg_data[1]);
*y = be16_to_cpu(reg_data[2]);
*z = be16_to_cpu(reg_data[3]);
ret = regmap_bulk_read(data->map, TMAG5273_ANGLE_RESULT_MSB,
&reg_data[0], sizeof(reg_data[0]));
if (ret)
goto out_unlock;
/*
* angle has 9 bits integer value and 4 bits fractional part
* 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
* 0 0 0 a a a a a a a a a f f f f
*/
*angle = be16_to_cpu(reg_data[0]);
ret = regmap_read(data->map, TMAG5273_MAGNITUDE_RESULT, &val);
if (ret < 0)
goto out_unlock;
*magnitude = val;
out_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int tmag5273_write_osr(struct tmag5273_data *data, int val)
{
int i;
if (val == data->conv_avg)
return 0;
for (i = 0; i < ARRAY_SIZE(tmag5273_avg_table); i++) {
if (tmag5273_avg_table[i] == val)
break;
}
if (i == ARRAY_SIZE(tmag5273_avg_table))
return -EINVAL;
data->conv_avg = val;
return regmap_update_bits(data->map, TMAG5273_DEVICE_CONFIG_1,
TMAG5273_AVG_MODE_MASK,
FIELD_PREP(TMAG5273_AVG_MODE_MASK, i));
}
static int tmag5273_write_scale(struct tmag5273_data *data, int scale_micro)
{
u32 value;
int i;
for (i = 0; i < ARRAY_SIZE(tmag5273_scale[0]); i++) {
if (tmag5273_scale[data->version][i].micro == scale_micro)
break;
}
if (i == ARRAY_SIZE(tmag5273_scale[0]))
return -EINVAL;
data->scale_index = i;
if (data->scale_index == MAGN_RANGE_LOW)
value = 0;
else
value = TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK;
return regmap_update_bits(data->map, TMAG5273_SENSOR_CONFIG_2,
TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK, value);
}
static int tmag5273_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct tmag5273_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = tmag5273_avg_table;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(tmag5273_avg_table);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_MAGN:
*type = IIO_VAL_INT_PLUS_MICRO;
*vals = (int *)tmag5273_scale[data->version];
*length = ARRAY_SIZE(tmag5273_scale[data->version]) *
MAGN_RANGE_NUM;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int tmag5273_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val,
int *val2, long mask)
{
struct tmag5273_data *data = iio_priv(indio_dev);
s16 t, x, y, z;
u16 angle, magnitude;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = pm_runtime_resume_and_get(data->dev);
if (ret < 0)
return ret;
ret = tmag5273_get_measure(data, &t, &x, &y, &z, &angle, &magnitude);
pm_runtime_put_autosuspend(data->dev);
if (ret)
return ret;
switch (chan->address) {
case TEMPERATURE:
*val = t;
return IIO_VAL_INT;
case AXIS_X:
*val = x;
return IIO_VAL_INT;
case AXIS_Y:
*val = y;
return IIO_VAL_INT;
case AXIS_Z:
*val = z;
return IIO_VAL_INT;
case ANGLE:
*val = angle;
return IIO_VAL_INT;
case MAGNITUDE:
*val = magnitude;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
/*
* Convert device specific value to millicelsius.
* Resolution from the sensor is 60.1 LSB/celsius and
* the reference value at 25 celsius is 17508 LSBs.
*/
*val = 10000;
*val2 = 601;
return IIO_VAL_FRACTIONAL;
case IIO_MAGN:
/* Magnetic resolution in uT */
*val = 0;
*val2 = tmag5273_scale[data->version]
[data->scale_index].micro;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_ANGL:
/*
* Angle is in degrees and has four fractional bits,
* therefore use 1/16 * pi/180 to convert to radians.
*/
*val = 1000;
*val2 = 916732;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_TEMP:
*val = -16005;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = data->conv_avg;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int tmag5273_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct tmag5273_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return tmag5273_write_osr(data, val);
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_MAGN:
if (val)
return -EINVAL;
return tmag5273_write_scale(data, val2);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
#define TMAG5273_AXIS_CHANNEL(axis, index) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.address = index, \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec tmag5273_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = TEMPERATURE,
.scan_index = TEMPERATURE,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
TMAG5273_AXIS_CHANNEL(X, AXIS_X),
TMAG5273_AXIS_CHANNEL(Y, AXIS_Y),
TMAG5273_AXIS_CHANNEL(Z, AXIS_Z),
{
.type = IIO_ANGL,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.address = ANGLE,
.scan_index = ANGLE,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
{
.type = IIO_DISTANCE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.address = MAGNITUDE,
.scan_index = MAGNITUDE,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(6),
};
static const struct iio_info tmag5273_info = {
.read_avail = tmag5273_read_avail,
.read_raw = tmag5273_read_raw,
.write_raw = tmag5273_write_raw,
};
static bool tmag5273_volatile_reg(struct device *dev, unsigned int reg)
{
return reg >= TMAG5273_T_MSB_RESULT && reg <= TMAG5273_MAGNITUDE_RESULT;
}
static const struct regmap_config tmag5273_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = TMAG5273_MAX_REG,
.volatile_reg = tmag5273_volatile_reg,
};
static int tmag5273_set_operating_mode(struct tmag5273_data *data,
unsigned int val)
{
return regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, val);
}
static void tmag5273_read_device_property(struct tmag5273_data *data)
{
struct device *dev = data->dev;
int ret;
data->angle_measurement = TMAG5273_ANGLE_EN_X_Y;
ret = device_property_match_property_string(dev, "ti,angle-measurement",
tmag5273_angle_names,
ARRAY_SIZE(tmag5273_angle_names));
if (ret >= 0)
data->angle_measurement = ret;
}
static void tmag5273_wake_up(struct tmag5273_data *data)
{
int val;
/* Wake up the chip by sending a dummy I2C command */
regmap_read(data->map, TMAG5273_DEVICE_ID, &val);
/*
* Time to go to stand-by mode from sleep mode is 50us
* typically, during this time no I2C access is possible.
*/
usleep_range(80, 200);
}
static int tmag5273_chip_init(struct tmag5273_data *data)
{
int ret;
ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_1,
TMAG5273_AVG_32_MODE);
if (ret)
return ret;
data->conv_avg = 32;
ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2,
TMAG5273_OP_MODE_CONT);
if (ret)
return ret;
ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_1,
FIELD_PREP(TMAG5273_MAG_CH_EN_MASK,
TMAG5273_MAG_CH_EN_X_Y_Z));
if (ret)
return ret;
ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_2,
FIELD_PREP(TMAG5273_ANGLE_EN_MASK,
data->angle_measurement));
if (ret)
return ret;
data->scale_index = MAGN_RANGE_LOW;
return regmap_write(data->map, TMAG5273_T_CONFIG, TMAG5273_T_CH_EN);
}
static int tmag5273_check_device_id(struct tmag5273_data *data)
{
__le16 devid;
int val, ret;
ret = regmap_read(data->map, TMAG5273_DEVICE_ID, &val);
if (ret)
return dev_err_probe(data->dev, ret, "failed to power on device\n");
data->version = FIELD_PREP(TMAG5273_VERSION_MASK, val);
ret = regmap_bulk_read(data->map, TMAG5273_MANUFACTURER_ID_LSB, &devid,
sizeof(devid));
if (ret)
return dev_err_probe(data->dev, ret, "failed to read device ID\n");
data->devid = le16_to_cpu(devid);
switch (data->devid) {
case TMAG5273_MANUFACTURER_ID:
/*
* The device name matches the orderable part number. 'x' stands
* for A, B, C or D devices, which have different I2C addresses.
* Versions 1 or 2 (0 and 3 is reserved) stands for different
* magnetic strengths.
*/
snprintf(data->name, sizeof(data->name), "tmag5273x%1u", data->version);
if (data->version < 1 || data->version > 2)
dev_warn(data->dev, "Unsupported device %s\n", data->name);
return 0;
default:
/*
* Only print warning in case of unknown device ID to allow
* fallback compatible in device tree.
*/
dev_warn(data->dev, "Unknown device ID 0x%x\n", data->devid);
return 0;
}
}
static void tmag5273_power_down(void *data)
{
tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP);
}
static int tmag5273_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct tmag5273_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = dev;
i2c_set_clientdata(i2c, indio_dev);
data->map = devm_regmap_init_i2c(i2c, &tmag5273_regmap_config);
if (IS_ERR(data->map))
return dev_err_probe(dev, PTR_ERR(data->map),
"failed to allocate register map\n");
mutex_init(&data->lock);
ret = devm_regulator_get_enable(dev, "vcc");
if (ret)
return dev_err_probe(dev, ret, "failed to enable regulator\n");
tmag5273_wake_up(data);
ret = tmag5273_check_device_id(data);
if (ret)
return ret;
ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT);
if (ret)
return dev_err_probe(dev, ret, "failed to power on device\n");
/*
* Register powerdown deferred callback which suspends the chip
* after module unloaded.
*
* TMAG5273 should be in SUSPEND mode in the two cases:
* 1) When driver is loaded, but we do not have any data or
* configuration requests to it (we are solving it using
* autosuspend feature).
* 2) When driver is unloaded and device is not used (devm action is
* used in this case).
*/
ret = devm_add_action_or_reset(dev, tmag5273_power_down, data);
if (ret)
return ret;
ret = pm_runtime_set_active(dev);
if (ret < 0)
return ret;
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
pm_runtime_get_noresume(dev);
pm_runtime_set_autosuspend_delay(dev, TMAG5273_AUTOSLEEP_DELAY_MS);
pm_runtime_use_autosuspend(dev);
tmag5273_read_device_property(data);
ret = tmag5273_chip_init(data);
if (ret)
return dev_err_probe(dev, ret, "failed to init device\n");
indio_dev->info = &tmag5273_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = data->name;
indio_dev->channels = tmag5273_channels;
indio_dev->num_channels = ARRAY_SIZE(tmag5273_channels);
pm_runtime_put_autosuspend(dev);
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "device register failed\n");
return 0;
}
static int tmag5273_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tmag5273_data *data = iio_priv(indio_dev);
int ret;
ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP);
if (ret)
dev_err(dev, "failed to power off device (%pe)\n", ERR_PTR(ret));
return ret;
}
static int tmag5273_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tmag5273_data *data = iio_priv(indio_dev);
int ret;
tmag5273_wake_up(data);
ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT);
if (ret)
dev_err(dev, "failed to power on device (%pe)\n", ERR_PTR(ret));
return ret;
}
static DEFINE_RUNTIME_DEV_PM_OPS(tmag5273_pm_ops,
tmag5273_runtime_suspend, tmag5273_runtime_resume,
NULL);
static const struct i2c_device_id tmag5273_id[] = {
{ .name = "tmag5273" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tmag5273_id);
static const struct of_device_id tmag5273_of_match[] = {
{ .compatible = "ti,tmag5273" },
{ }
};
MODULE_DEVICE_TABLE(of, tmag5273_of_match);
static struct i2c_driver tmag5273_driver = {
.driver = {
.name = "tmag5273",
.of_match_table = tmag5273_of_match,
.pm = pm_ptr(&tmag5273_pm_ops),
},
.probe = tmag5273_probe,
.id_table = tmag5273_id,
};
module_i2c_driver(tmag5273_driver);
MODULE_DESCRIPTION("TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor driver");
MODULE_AUTHOR("Gerald Loacker <gerald.loacker@wolfvision.net>");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff