summaryrefslogtreecommitdiffstats
path: root/drivers/iio/accel
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-07-14 15:04:49 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-07-14 15:04:49 +0200
commit2a3c8f8a4494343f5942b780b3f6295307a07b6e (patch)
tree0e8875982daec5b8d325c88c09ea435ef64db6e2 /drivers/iio/accel
parentbc2c6a5ee71bc8aee58d8a87ad95feddb4e2d6c2 (diff)
parent568f6869e680c7492c5e38df9094af86fe9d6364 (diff)
downloadlinux-2a3c8f8a4494343f5942b780b3f6295307a07b6e.tar.bz2
Merge tag 'iio-for-5.20a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next
Jonathan writes: IIO new device support, features and minor fixes for 5.20 Several on-running cleanup efforts dominate this time, plus the DMA safety alignment issue identified due to improved understanding of the restrictions as a result of Catalin Marinas' efforts in that area. One immutable branch in here due to MFD and SPMI elements needed for the qcom-rradc driver. Device support * bmi088 - Add support for bmi085 (accelerometer part of IMU) - Add support for bmi090l (accelerometer part of IMU) * mcp4922 - Add support for single channel device MCP4921 * rzg2l-adc - Add compatible and minor tweaks to support RZ/G2UL ADC * sca3300 - Add support for scl3300 including refactoring driver to support multiple device types and cleanup noticed whilst working on driver. * spmi-rradc - New driver for Qualcomm SPMI Round Robin ADC including necessary additional utility functions in SPMI core and related MFD driver. * ti-dac55781 - Add compatible for DAC121C081 which is very similar to existing parts. Features * core - Warn on iio_trigger_get() on an unregistered IIO trigger. * bma400 - Triggered buffer support - Activity and step counting - Misc driver improvements such as devm and header ordering * cm32181 - Add PM support. * cros_ec - Sensor location support * sx9324 - Add precharge resistor setting - Add internal compensation resistor setting - Add CS idle/sleep mode. * sx9360 - Add precharge resistor setting * vl53l0x - Handle reset GPIO, regulator and relax handling of irq type. Cleanup and minor fixes: Treewide changes - Cleanup of error handling in remove functions in many drivers. - Update dt-binding maintainers for a number of ADI bindings. - Several sets of conversion of drivers from device tree specific to generic device properties. Includes fixing up various related header and Kconfig issues. - Drop include of of.h from iio.h and fix up drivers that need to include it directly. - More moves of clusters of drivers into appropriate IIO_XXX namespaces. - Tree wide fix of a long running bug around DMA safety requirements. IIO was using __cacheline_aligned to pad iio_priv() structures. This worked for a long time by coincidence, but correct alignment is ARCH_KMALLOC_MINALIGN. As there is activity around this area, introduce an IIO local IIO_DMA_MINALIGN to allow for changing it in one place rather than every driver in future. Note, there have been no reports of this bug in the wild, and it may not happen on any platforms supported by upstream, so no rush to backport these fixes. Other cleanup * core - Switch to ida_alloc()/free() - Drop unused iio_get_time_res() - Octal permissions and DEVICE_ATTR_* macros. - Cleanup bared unsigned usage. * MAINTAINERS - Add include/dt-bindings/iio/ to the main IIO entry. * ad5380 - Comment syntax fix. * ad74413r - Call to for_each_set_bit_from(), with from value as 0 replaced. * ad7768-1 - Drop explicit setting of INDIO_BUFFER_TRIGGERED as now done by the core. * adxl345 - Fix wrong address in dt-binding example. * adxl367 - Drop extra update of FIFO watermark. * at91-sama5d2 - Limit requested watermark to the hwfifo size. * bmg160, bme680 - Typos * cio-dac - Switch to iomap rather than direct use of ioports * kxsd9 - Replace CONFIG_PM guards with new PM macros that let the compiler cleanly remove the unused code and structures when !CONFIG_PM * lsm6dsx - Use new pm_sleep_ptr() and EXPORT_SIMPLE_DEV_PM_OPS(). Then move to Namespace. * meson_saradc - general cleanup. - Avoid attaching resources to iio_dev->dev - Use same struct device for all error messages - Convert to dev_err_probe() and use local struct device *dev to reduce code complexity. - Use devm_clk_get_optional() instead of hand rolling. - Use regmap_read_poll_timeout() instead of hand rolling. * mma7660 - Drop ACPI_PTR() use that is unhelpful. * mpu3050 - Stop exporting symbols not used outside of module - Switch to new DEFINE_RUNTIME_DEV_PM_OPS() macro and move to Namespace. * ping - Typo fix * qcom-spmi-rradc - Typo fix * sc27xx - Convert to generic struct u32_fract * srf08 - Drop a redundant check on !val * st_lsm6dsx - Limit the requested watermark to the hwfifo size. * stm32-adc - Use generic_handle_domain_irq() instead of opencoding. - Fix handling of ADC disable. * stm32-dac - Use str_enabled_disable() instead of open coding. * stx104 - Switch to iomap rather than direct use of ioports * tsc2046 - Drop explicit setting of INDIO_BUFFER_TRIGGERED as now done by the core. * tsl2563 - Replace flush_scheduled_work() with cancel_delayed_work_sync() - Replace cancel_delayed_work() with cancel_delayed_work_sync() * vl53l0x - Make the VDD regulator optional by allowing a dummy regulator. * tag 'iio-for-5.20a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (244 commits) iio: adc: xilinx-xadc: Drop duplicate NULL check in xadc_parse_dt() iio: adc: xilinx-xadc: Make use of device properties iio: light: cm32181: Add PM support iio: adc: ad778-1: do not explicity set INDIO_BUFFER_TRIGGERED mode iio: adc: ti-tsc2046: do not explicity set INDIO_BUFFER_TRIGGERED mode iio: adc: stm32-adc: disable adc before calibration iio: adc: stm32-adc: make safe adc disable iio: dac: ad5380: align '*' each line and drop unneeded blank line iio: adc: qcom-spmi-rradc: Fix spelling mistake "coherrency" -> "coherency" iio: Don't use bare "unsigned" dt-bindings: iio: dac: mcp4922: expand for mcp4921 support iio: dac: mcp4922: add support to mcp4921 iio: chemical: sps30: Move symbol exports into IIO_SPS30 namespace iio: pressure: bmp280: Move symbol exports to IIO_BMP280 namespace iio: imu: bmi160: Move exported symbols to IIO_BMI160 namespace iio: adc: stm32-adc: Use generic_handle_domain_irq() proximity: vl53l0x: Make VDD regulator actually optional MAINTAINERS: add include/dt-bindings/iio to IIO SUBSYSTEM AND DRIVERS dt-bindings: iio/accel: Fix adi,adxl345/6 example I2C address iio: gyro: bmg160: Fix typo in comment ...
Diffstat (limited to 'drivers/iio/accel')
-rw-r--r--drivers/iio/accel/Kconfig2
-rw-r--r--drivers/iio/accel/adxl313_core.c2
-rw-r--r--drivers/iio/accel/adxl355_core.c2
-rw-r--r--drivers/iio/accel/adxl367.c48
-rw-r--r--drivers/iio/accel/adxl367_spi.c8
-rw-r--r--drivers/iio/accel/bma220_spi.c2
-rw-r--r--drivers/iio/accel/bma400.h50
-rw-r--r--drivers/iio/accel/bma400_core.c710
-rw-r--r--drivers/iio/accel/bma400_i2c.c10
-rw-r--r--drivers/iio/accel/bma400_spi.c8
-rw-r--r--drivers/iio/accel/bmi088-accel-core.c99
-rw-r--r--drivers/iio/accel/bmi088-accel-spi.c17
-rw-r--r--drivers/iio/accel/bmi088-accel.h9
-rw-r--r--drivers/iio/accel/cros_ec_accel_legacy.c2
-rw-r--r--drivers/iio/accel/kxsd9-i2c.c2
-rw-r--r--drivers/iio/accel/kxsd9-spi.c2
-rw-r--r--drivers/iio/accel/kxsd9.c11
-rw-r--r--drivers/iio/accel/mc3230.c4
-rw-r--r--drivers/iio/accel/mma7660.c6
-rw-r--r--drivers/iio/accel/sca3000.c6
-rw-r--r--drivers/iio/accel/sca3300.c325
-rw-r--r--drivers/iio/accel/stk8312.c4
-rw-r--r--drivers/iio/accel/stk8ba50.c4
23 files changed, 1120 insertions, 213 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index b53f010f3e40..35798712f811 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -204,6 +204,8 @@ config BMA220
config BMA400
tristate "Bosch BMA400 3-Axis Accelerometer Driver"
select REGMAP
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
select BMA400_I2C if I2C
select BMA400_SPI if SPI
help
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index 9e4193e64765..afeef779e1d0 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -46,7 +46,7 @@ EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
struct adxl313_data {
struct regmap *regmap;
struct mutex lock; /* lock to protect transf_buf */
- __le16 transf_buf ____cacheline_aligned;
+ __le16 transf_buf __aligned(IIO_DMA_MINALIGN);
};
static const int adxl313_odr_freqs[][2] = {
diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c
index 7561399daef3..4bc648eac8b2 100644
--- a/drivers/iio/accel/adxl355_core.c
+++ b/drivers/iio/accel/adxl355_core.c
@@ -177,7 +177,7 @@ struct adxl355_data {
u8 buf[14];
s64 ts;
} buffer;
- } ____cacheline_aligned;
+ } __aligned(IIO_DMA_MINALIGN);
};
static int adxl355_set_op_mode(struct adxl355_data *data,
diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c
index 0289ed8cf2c6..47feb375b70b 100644
--- a/drivers/iio/accel/adxl367.c
+++ b/drivers/iio/accel/adxl367.c
@@ -179,7 +179,7 @@ struct adxl367_state {
unsigned int fifo_set_size;
unsigned int fifo_watermark;
- __be16 fifo_buf[ADXL367_FIFO_SIZE] ____cacheline_aligned;
+ __be16 fifo_buf[ADXL367_FIFO_SIZE] __aligned(IIO_DMA_MINALIGN);
__be16 sample_buf;
u8 act_threshold_buf[2];
u8 inact_time_buf[2];
@@ -447,21 +447,17 @@ static int adxl367_set_fifo_format(struct adxl367_state *st,
fifo_format));
}
-static int adxl367_set_fifo_samples(struct adxl367_state *st,
- unsigned int fifo_watermark,
- unsigned int fifo_set_size)
+static int adxl367_set_fifo_watermark(struct adxl367_state *st,
+ unsigned int fifo_watermark)
{
- unsigned int fifo_samples = fifo_watermark * fifo_set_size;
+ unsigned int fifo_samples = fifo_watermark * st->fifo_set_size;
unsigned int fifo_samples_h, fifo_samples_l;
int ret;
if (fifo_samples > ADXL367_FIFO_MAX_WATERMARK)
fifo_samples = ADXL367_FIFO_MAX_WATERMARK;
- if (fifo_set_size == 0)
- return 0;
-
- fifo_samples /= fifo_set_size;
+ fifo_samples /= st->fifo_set_size;
fifo_samples_h = FIELD_PREP(ADXL367_SAMPLES_H_MASK,
FIELD_GET(ADXL367_SAMPLES_VAL_H_MASK,
@@ -475,30 +471,8 @@ static int adxl367_set_fifo_samples(struct adxl367_state *st,
if (ret)
return ret;
- return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_SAMPLES,
- ADXL367_SAMPLES_L_MASK, fifo_samples_l);
-}
-
-static int adxl367_set_fifo_set_size(struct adxl367_state *st,
- unsigned int fifo_set_size)
-{
- int ret;
-
- ret = adxl367_set_fifo_samples(st, st->fifo_watermark, fifo_set_size);
- if (ret)
- return ret;
-
- st->fifo_set_size = fifo_set_size;
-
- return 0;
-}
-
-static int adxl367_set_fifo_watermark(struct adxl367_state *st,
- unsigned int fifo_watermark)
-{
- int ret;
-
- ret = adxl367_set_fifo_samples(st, fifo_watermark, st->fifo_set_size);
+ ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_SAMPLES,
+ ADXL367_SAMPLES_L_MASK, fifo_samples_l);
if (ret)
return ret;
@@ -1276,14 +1250,11 @@ static int adxl367_update_scan_mode(struct iio_dev *indio_dev,
{
struct adxl367_state *st = iio_priv(indio_dev);
enum adxl367_fifo_format fifo_format;
- unsigned int fifo_set_size;
int ret;
if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format))
return -EINVAL;
- fifo_set_size = bitmap_weight(active_scan_mask, indio_dev->masklength);
-
mutex_lock(&st->lock);
ret = adxl367_set_measure_en(st, false);
@@ -1294,11 +1265,12 @@ static int adxl367_update_scan_mode(struct iio_dev *indio_dev,
if (ret)
goto out;
- ret = adxl367_set_fifo_set_size(st, fifo_set_size);
+ ret = adxl367_set_measure_en(st, true);
if (ret)
goto out;
- ret = adxl367_set_measure_en(st, true);
+ st->fifo_set_size = bitmap_weight(active_scan_mask,
+ indio_dev->masklength);
out:
mutex_unlock(&st->lock);
diff --git a/drivers/iio/accel/adxl367_spi.c b/drivers/iio/accel/adxl367_spi.c
index 26dfc821ebbe..118c894015a5 100644
--- a/drivers/iio/accel/adxl367_spi.c
+++ b/drivers/iio/accel/adxl367_spi.c
@@ -9,6 +9,8 @@
#include <linux/regmap.h>
#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+
#include "adxl367.h"
#define ADXL367_SPI_WRITE_COMMAND 0x0A
@@ -28,10 +30,10 @@ struct adxl367_spi_state {
struct spi_transfer fifo_xfer[2];
/*
- * DMA (thus cache coherency maintenance) requires the
- * transfer buffers to live in their own cache lines.
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers live in their own cache lines.
*/
- u8 reg_write_tx_buf[1] ____cacheline_aligned;
+ u8 reg_write_tx_buf[1] __aligned(IIO_DMA_MINALIGN);
u8 reg_read_tx_buf[2];
u8 fifo_tx_buf[1];
};
diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c
index 74024d7ce5ac..b6d9ab8e2054 100644
--- a/drivers/iio/accel/bma220_spi.c
+++ b/drivers/iio/accel/bma220_spi.c
@@ -67,7 +67,7 @@ struct bma220_data {
/* Ensure timestamp is naturally aligned. */
s64 timestamp __aligned(8);
} scan;
- u8 tx_buf[2] ____cacheline_aligned;
+ u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
};
static const struct iio_chan_spec bma220_channels[] = {
diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index c4c8d74155c2..e8f802a82300 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -39,6 +39,7 @@
#define BMA400_INT_STAT0_REG 0x0e
#define BMA400_INT_STAT1_REG 0x0f
#define BMA400_INT_STAT2_REG 0x10
+#define BMA400_INT12_MAP_REG 0x23
/* Temperature register */
#define BMA400_TEMP_DATA_REG 0x11
@@ -53,6 +54,9 @@
#define BMA400_STEP_CNT1_REG 0x16
#define BMA400_STEP_CNT3_REG 0x17
#define BMA400_STEP_STAT_REG 0x18
+#define BMA400_STEP_INT_MSK BIT(0)
+#define BMA400_STEP_RAW_LEN 0x03
+#define BMA400_STEP_STAT_MASK GENMASK(9, 8)
/*
* Read-write configuration registers
@@ -62,6 +66,13 @@
#define BMA400_ACC_CONFIG2_REG 0x1b
#define BMA400_CMD_REG 0x7e
+/* Interrupt registers */
+#define BMA400_INT_CONFIG0_REG 0x1f
+#define BMA400_INT_CONFIG1_REG 0x20
+#define BMA400_INT1_MAP_REG 0x21
+#define BMA400_INT_IO_CTRL_REG 0x24
+#define BMA400_INT_DRDY_MSK BIT(7)
+
/* Chip ID of BMA 400 devices found in the chip ID register. */
#define BMA400_ID_REG_VAL 0x90
@@ -83,8 +94,38 @@
#define BMA400_ACC_ODR_MIN_WHOLE_HZ 25
#define BMA400_ACC_ODR_MIN_HZ 12
-#define BMA400_SCALE_MIN 38357
-#define BMA400_SCALE_MAX 306864
+/* Generic interrupts register */
+#define BMA400_GEN1INT_CONFIG0 0x3f
+#define BMA400_GEN2INT_CONFIG0 0x4A
+#define BMA400_GEN_CONFIG1_OFF 0x01
+#define BMA400_GEN_CONFIG2_OFF 0x02
+#define BMA400_GEN_CONFIG3_OFF 0x03
+#define BMA400_GEN_CONFIG31_OFF 0x04
+#define BMA400_INT_GEN1_MSK BIT(2)
+#define BMA400_INT_GEN2_MSK BIT(3)
+#define BMA400_GEN_HYST_MSK GENMASK(1, 0)
+
+/*
+ * BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before
+ * converting to micro values for +-2g range.
+ *
+ * For +-2g - 1 LSB = 0.976562 milli g = 0.009576 m/s^2
+ * For +-4g - 1 LSB = 1.953125 milli g = 0.019153 m/s^2
+ * For +-16g - 1 LSB = 7.8125 milli g = 0.076614 m/s^2
+ *
+ * The raw value which is used to select the different ranges is determined
+ * by the first bit set position from the scale value, so BMA400_SCALE_MIN
+ * should be odd.
+ *
+ * Scale values for +-2g, +-4g, +-8g and +-16g are populated into bma400_scales
+ * array by left shifting BMA400_SCALE_MIN.
+ * e.g.:
+ * To select +-2g = 9577 << 0 = raw value to write is 0.
+ * To select +-8g = 9577 << 2 = raw value to write is 2.
+ * To select +-16g = 9577 << 3 = raw value to write is 3.
+ */
+#define BMA400_SCALE_MIN 9577
+#define BMA400_SCALE_MAX 76617
#define BMA400_NUM_REGULATORS 2
#define BMA400_VDD_REGULATOR 0
@@ -92,8 +133,7 @@
extern const struct regmap_config bma400_regmap_config;
-int bma400_probe(struct device *dev, struct regmap *regmap, const char *name);
-
-void bma400_remove(struct device *dev);
+int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name);
#endif
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index 043002fe6f63..c31bdd9b168e 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -11,15 +11,24 @@
* - Create channel for sensor time
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/device.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include "bma400.h"
@@ -46,11 +55,24 @@ enum bma400_power_mode {
POWER_MODE_INVALID = 0x03,
};
+enum bma400_scan {
+ BMA400_ACCL_X,
+ BMA400_ACCL_Y,
+ BMA400_ACCL_Z,
+ BMA400_TEMP,
+};
+
struct bma400_sample_freq {
int hz;
int uhz;
};
+enum bma400_activity {
+ BMA400_STILL,
+ BMA400_WALKING,
+ BMA400_RUNNING,
+};
+
struct bma400_data {
struct device *dev;
struct regmap *regmap;
@@ -61,6 +83,19 @@ struct bma400_data {
struct bma400_sample_freq sample_freq;
int oversampling_ratio;
int scale;
+ struct iio_trigger *trig;
+ int steps_enabled;
+ bool step_event_en;
+ bool activity_event_en;
+ unsigned int generic_event_en;
+ /* Correct time stamp alignment */
+ struct {
+ __le16 buff[3];
+ u8 temperature;
+ s64 ts __aligned(8);
+ } buffer __aligned(IIO_DMA_MINALIGN);
+ __le16 status;
+ __be16 duration;
};
static bool bma400_is_writable_reg(struct device *dev, unsigned int reg)
@@ -152,7 +187,38 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
{ }
};
-#define BMA400_ACC_CHANNEL(_axis) { \
+static const struct iio_event_spec bma400_step_detect_event = {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_NONE,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+};
+
+static const struct iio_event_spec bma400_activity_event = {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_NONE,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE),
+};
+
+static const struct iio_event_spec bma400_accel_event[] = {
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HYSTERESIS) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HYSTERESIS) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+#define BMA400_ACC_CHANNEL(_index, _axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##_axis, \
@@ -164,17 +230,55 @@ static const struct iio_chan_spec_ext_info bma400_ext_info[] = {
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.ext_info = bma400_ext_info, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+ .event_spec = bma400_accel_event, \
+ .num_event_specs = ARRAY_SIZE(bma400_accel_event) \
+}
+
+#define BMA400_ACTIVITY_CHANNEL(_chan2) { \
+ .type = IIO_ACTIVITY, \
+ .modified = 1, \
+ .channel2 = _chan2, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = -1, /* No buffer support */ \
+ .event_spec = &bma400_activity_event, \
+ .num_event_specs = 1, \
}
static const struct iio_chan_spec bma400_channels[] = {
- BMA400_ACC_CHANNEL(X),
- BMA400_ACC_CHANNEL(Y),
- BMA400_ACC_CHANNEL(Z),
+ BMA400_ACC_CHANNEL(0, X),
+ BMA400_ACC_CHANNEL(1, Y),
+ BMA400_ACC_CHANNEL(2, Z),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 8,
+ .storagebits = 8,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_STEPS,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_ENABLE),
+ .scan_index = -1, /* No buffer support */
+ .event_spec = &bma400_step_detect_event,
+ .num_event_specs = 1,
},
+ BMA400_ACTIVITY_CHANNEL(IIO_MOD_STILL),
+ BMA400_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
+ BMA400_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
};
static int bma400_get_temp_reg(struct bma400_data *data, int *val, int *val2)
@@ -542,6 +646,40 @@ static int bma400_set_power_mode(struct bma400_data *data,
return 0;
}
+static int bma400_enable_steps(struct bma400_data *data, int val)
+{
+ int ret;
+
+ if (data->steps_enabled == val)
+ return 0;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG,
+ BMA400_STEP_INT_MSK,
+ FIELD_PREP(BMA400_STEP_INT_MSK, val ? 1 : 0));
+ if (ret)
+ return ret;
+ data->steps_enabled = val;
+ return ret;
+}
+
+static int bma400_get_steps_reg(struct bma400_data *data, int *val)
+{
+ u8 *steps_raw;
+ int ret;
+
+ steps_raw = kmalloc(BMA400_STEP_RAW_LEN, GFP_KERNEL);
+ if (!steps_raw)
+ return -ENOMEM;
+
+ ret = regmap_bulk_read(data->regmap, BMA400_STEP_CNT0_REG,
+ steps_raw, BMA400_STEP_RAW_LEN);
+ if (ret)
+ return ret;
+ *val = get_unaligned_le24(steps_raw);
+ kfree(steps_raw);
+ return IIO_VAL_INT;
+}
+
static void bma400_init_tables(void)
{
int raw;
@@ -560,6 +698,40 @@ static void bma400_init_tables(void)
}
}
+static void bma400_regulators_disable(void *data_ptr)
+{
+ struct bma400_data *data = data_ptr;
+
+ regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators);
+}
+
+static void bma400_power_disable(void *data_ptr)
+{
+ struct bma400_data *data = data_ptr;
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bma400_set_power_mode(data, POWER_MODE_SLEEP);
+ mutex_unlock(&data->mutex);
+ if (ret)
+ dev_warn(data->dev, "Failed to put device into sleep mode (%pe)\n",
+ ERR_PTR(ret));
+}
+
+static enum iio_modifier bma400_act_to_mod(enum bma400_activity activity)
+{
+ switch (activity) {
+ case BMA400_STILL:
+ return IIO_MOD_STILL;
+ case BMA400_WALKING:
+ return IIO_MOD_WALKING;
+ case BMA400_RUNNING:
+ return IIO_MOD_RUNNING;
+ default:
+ return IIO_NO_MOD;
+ }
+}
+
static int bma400_init(struct bma400_data *data)
{
unsigned int val;
@@ -569,13 +741,12 @@ static int bma400_init(struct bma400_data *data)
ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val);
if (ret) {
dev_err(data->dev, "Failed to read chip id register\n");
- goto out;
+ return ret;
}
if (val != BMA400_ID_REG_VAL) {
dev_err(data->dev, "Chip ID mismatch\n");
- ret = -ENODEV;
- goto out;
+ return -ENODEV;
}
data->regulators[BMA400_VDD_REGULATOR].supply = "vdd";
@@ -589,27 +760,31 @@ static int bma400_init(struct bma400_data *data)
"Failed to get regulators: %d\n",
ret);
- goto out;
+ return ret;
}
ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators),
data->regulators);
if (ret) {
dev_err(data->dev, "Failed to enable regulators: %d\n",
ret);
- goto out;
+ return ret;
}
+ ret = devm_add_action_or_reset(data->dev, bma400_regulators_disable, data);
+ if (ret)
+ return ret;
+
ret = bma400_get_power_mode(data);
if (ret) {
dev_err(data->dev, "Failed to get the initial power-mode\n");
- goto err_reg_disable;
+ return ret;
}
if (data->power_mode != POWER_MODE_NORMAL) {
ret = bma400_set_power_mode(data, POWER_MODE_NORMAL);
if (ret) {
dev_err(data->dev, "Failed to wake up the device\n");
- goto err_reg_disable;
+ return ret;
}
/*
* TODO: The datasheet waits 1500us here in the example, but
@@ -618,20 +793,28 @@ static int bma400_init(struct bma400_data *data)
usleep_range(1500, 2000);
}
+ ret = devm_add_action_or_reset(data->dev, bma400_power_disable, data);
+ if (ret)
+ return ret;
+
bma400_init_tables();
ret = bma400_get_accel_output_data_rate(data);
if (ret)
- goto err_reg_disable;
+ return ret;
ret = bma400_get_accel_oversampling_ratio(data);
if (ret)
- goto err_reg_disable;
+ return ret;
ret = bma400_get_accel_scale(data);
if (ret)
- goto err_reg_disable;
+ return ret;
+ /* Configure INT1 pin to open drain */
+ ret = regmap_write(data->regmap, BMA400_INT_IO_CTRL_REG, 0x06);
+ if (ret)
+ return ret;
/*
* Once the interrupt engine is supported we might use the
* data_src_reg, but for now ensure this is set to the
@@ -639,12 +822,6 @@ static int bma400_init(struct bma400_data *data)
* channel.
*/
return regmap_write(data->regmap, BMA400_ACC_CONFIG2_REG, 0x00);
-
-err_reg_disable:
- regulator_bulk_disable(ARRAY_SIZE(data->regulators),
- data->regulators);
-out:
- return ret;
}
static int bma400_read_raw(struct iio_dev *indio_dev,
@@ -652,14 +829,37 @@ static int bma400_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct bma400_data *data = iio_priv(indio_dev);
+ unsigned int activity;
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
- mutex_lock(&data->mutex);
- ret = bma400_get_temp_reg(data, val, val2);
- mutex_unlock(&data->mutex);
- return ret;
+ switch (chan->type) {
+ case IIO_TEMP:
+ mutex_lock(&data->mutex);
+ ret = bma400_get_temp_reg(data, val, val2);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_STEPS:
+ return bma400_get_steps_reg(data, val);
+ case IIO_ACTIVITY:
+ ret = regmap_read(data->regmap, BMA400_STEP_STAT_REG,
+ &activity);
+ if (ret)
+ return ret;
+ /*
+ * The device does not support confidence value levels,
+ * so we will always have 100% for current activity and
+ * 0% for the others.
+ */
+ if (chan->channel2 == bma400_act_to_mod(activity))
+ *val = 100;
+ else
+ *val = 0;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->mutex);
ret = bma400_get_accel_reg(data, chan, val);
@@ -700,6 +900,9 @@ static int bma400_read_raw(struct iio_dev *indio_dev,
*val = data->oversampling_ratio;
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_ENABLE:
+ *val = data->steps_enabled;
+ return IIO_VAL_INT;
default:
return -EINVAL;
}
@@ -765,6 +968,11 @@ static int bma400_write_raw(struct iio_dev *indio_dev,
ret = bma400_set_accel_oversampling_ratio(data, val);
mutex_unlock(&data->mutex);
return ret;
+ case IIO_CHAN_INFO_ENABLE:
+ mutex_lock(&data->mutex);
+ ret = bma400_enable_steps(data, val);
+ mutex_unlock(&data->mutex);
+ return ret;
default:
return -EINVAL;
}
@@ -781,19 +989,419 @@ static int bma400_write_raw_get_fmt(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_ENABLE:
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bma400_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct bma400_data *data = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return FIELD_GET(BMA400_INT_GEN1_MSK,
+ data->generic_event_en);
+ case IIO_EV_DIR_FALLING:
+ return FIELD_GET(BMA400_INT_GEN2_MSK,
+ data->generic_event_en);
+ default:
+ return -EINVAL;
+ }
+ case IIO_STEPS:
+ return data->step_event_en;
+ case IIO_ACTIVITY:
+ return data->activity_event_en;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bma400_steps_event_enable(struct bma400_data *data, int state)
+{
+ int ret;
+
+ ret = bma400_enable_steps(data, 1);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT12_MAP_REG,
+ BMA400_STEP_INT_MSK,
+ FIELD_PREP(BMA400_STEP_INT_MSK,
+ state));
+ if (ret)
+ return ret;
+ data->step_event_en = state;
+ return 0;
+}
+
+static int bma400_activity_event_en(struct bma400_data *data,
+ enum iio_event_direction dir,
+ int state)
+{
+ int ret, reg, msk, value, field_value;
+
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ reg = BMA400_GEN1INT_CONFIG0;
+ msk = BMA400_INT_GEN1_MSK;
+ value = 2;
+ set_mask_bits(&field_value, BMA400_INT_GEN1_MSK,
+ FIELD_PREP(BMA400_INT_GEN1_MSK, state));
+ break;
+ case IIO_EV_DIR_FALLING:
+ reg = BMA400_GEN2INT_CONFIG0;
+ msk = BMA400_INT_GEN2_MSK;
+ value = 0;
+ set_mask_bits(&field_value, BMA400_INT_GEN2_MSK,
+ FIELD_PREP(BMA400_INT_GEN2_MSK, state));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Enabling all axis for interrupt evaluation */
+ ret = regmap_write(data->regmap, reg, 0xF8);
+ if (ret)
+ return ret;
+
+ /* OR combination of all axis for interrupt evaluation */
+ ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG1_OFF, value);
+ if (ret)
+ return ret;
+
+ /* Initial value to avoid interrupts while enabling*/
+ ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG2_OFF, 0x0A);
+ if (ret)
+ return ret;
+
+ /* Initial duration value to avoid interrupts while enabling*/
+ ret = regmap_write(data->regmap, reg + BMA400_GEN_CONFIG31_OFF, 0x0F);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG, msk,
+ field_value);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG, msk,
+ field_value);
+ if (ret)
+ return ret;
+
+ set_mask_bits(&data->generic_event_en, msk, field_value);
+ return 0;
+}
+
+static int bma400_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir, int state)
+{
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ mutex_lock(&data->mutex);
+ ret = bma400_activity_event_en(data, dir, state);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_STEPS:
+ mutex_lock(&data->mutex);
+ ret = bma400_steps_event_enable(data, state);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_ACTIVITY:
+ mutex_lock(&data->mutex);
+ if (!data->step_event_en) {
+ ret = bma400_steps_event_enable(data, true);
+ if (ret) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ }
+ data->activity_event_en = state;
+ mutex_unlock(&data->mutex);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int get_gen_config_reg(enum iio_event_direction dir)
+{
+ switch (dir) {
+ case IIO_EV_DIR_FALLING:
+ return BMA400_GEN2INT_CONFIG0;
+ case IIO_EV_DIR_RISING:
+ return BMA400_GEN1INT_CONFIG0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bma400_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret, reg;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ reg = get_gen_config_reg(dir);
+ if (reg < 0)
+ return -EINVAL;
+
+ *val2 = 0;
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = regmap_read(data->regmap,
+ reg + BMA400_GEN_CONFIG2_OFF,
+ val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_PERIOD:
+ mutex_lock(&data->mutex);
+ ret = regmap_bulk_read(data->regmap,
+ reg + BMA400_GEN_CONFIG3_OFF,
+ &data->duration,
+ sizeof(data->duration));
+ if (ret) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ *val = be16_to_cpu(data->duration);
+ mutex_unlock(&data->mutex);
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_HYSTERESIS:
+ ret = regmap_read(data->regmap, reg, val);
+ if (ret)
+ return ret;
+ *val = FIELD_GET(BMA400_GEN_HYST_MSK, *val);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bma400_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct bma400_data *data = iio_priv(indio_dev);
+ int reg, ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ reg = get_gen_config_reg(dir);
+ if (reg < 0)
+ return -EINVAL;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val < 1 || val > 255)
+ return -EINVAL;
+
+ return regmap_write(data->regmap,
+ reg + BMA400_GEN_CONFIG2_OFF,
+ val);
+ case IIO_EV_INFO_PERIOD:
+ if (val < 1 || val > 65535)
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ put_unaligned_be16(val, &data->duration);
+ ret = regmap_bulk_write(data->regmap,
+ reg + BMA400_GEN_CONFIG3_OFF,
+ &data->duration,
+ sizeof(data->duration));
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_EV_INFO_HYSTERESIS:
+ if (val < 0 || val > 3)
+ return -EINVAL;
+
+ return regmap_update_bits(data->regmap, reg,
+ BMA400_GEN_HYST_MSK,
+ FIELD_PREP(BMA400_GEN_HYST_MSK,
+ val));
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
}
+static int bma400_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG0_REG,
+ BMA400_INT_DRDY_MSK,
+ FIELD_PREP(BMA400_INT_DRDY_MSK, state));
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(data->regmap, BMA400_INT1_MAP_REG,
+ BMA400_INT_DRDY_MSK,
+ FIELD_PREP(BMA400_INT_DRDY_MSK, state));
+}
+
+static const unsigned long bma400_avail_scan_masks[] = {
+ BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z),
+ BIT(BMA400_ACCL_X) | BIT(BMA400_ACCL_Y) | BIT(BMA400_ACCL_Z)
+ | BIT(BMA400_TEMP),
+ 0
+};
+
static const struct iio_info bma400_info = {
.read_raw = bma400_read_raw,
.read_avail = bma400_read_avail,
.write_raw = bma400_write_raw,
.write_raw_get_fmt = bma400_write_raw_get_fmt,
+ .read_event_config = bma400_read_event_config,
+ .write_event_config = bma400_write_event_config,
+ .write_event_value = bma400_write_event_value,
+ .read_event_value = bma400_read_event_value,
+};
+
+static const struct iio_trigger_ops bma400_trigger_ops = {
+ .set_trigger_state = &bma400_data_rdy_trigger_set_state,
+ .validate_device = &iio_trigger_validate_own_device,
};
-int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
+static irqreturn_t bma400_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret, temp;
+
+ /* Lock to protect the data->buffer */
+ mutex_lock(&data->mutex);
+
+ /* bulk read six registers, with the base being the LSB register */
+ ret = regmap_bulk_read(data->regmap, BMA400_X_AXIS_LSB_REG,
+ &data->buffer.buff, sizeof(data->buffer.buff));
+ if (ret)
+ goto unlock_err;
+
+ if (test_bit(BMA400_TEMP, indio_dev->active_scan_mask)) {
+ ret = regmap_read(data->regmap, BMA400_TEMP_DATA_REG, &temp);
+ if (ret)
+ goto unlock_err;
+
+ data->buffer.temperature = temp;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer,
+ iio_get_time_ns(indio_dev));
+
+ mutex_unlock(&data->mutex);
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+
+unlock_err:
+ mutex_unlock(&data->mutex);
+ return IRQ_NONE;
+}
+
+static irqreturn_t bma400_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct bma400_data *data = iio_priv(indio_dev);
+ s64 timestamp = iio_get_time_ns(indio_dev);
+ unsigned int act, ev_dir = IIO_EV_DIR_NONE;
+ int ret;
+
+ /* Lock to protect the data->status */
+ mutex_lock(&data->mutex);
+ ret = regmap_bulk_read(data->regmap, BMA400_INT_STAT0_REG,
+ &data->status,
+ sizeof(data->status));
+ /*
+ * if none of the bit is set in the status register then it is
+ * spurious interrupt.
+ */
+ if (ret || !data->status)
+ goto unlock_err;
+
+ if (FIELD_GET(BMA400_INT_GEN1_MSK, le16_to_cpu(data->status)))
+ ev_dir = IIO_EV_DIR_RISING;
+
+ if (FIELD_GET(BMA400_INT_GEN2_MSK, le16_to_cpu(data->status)))
+ ev_dir = IIO_EV_DIR_FALLING;
+
+ if (ev_dir != IIO_EV_DIR_NONE) {
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_MAG, ev_dir),
+ timestamp);
+ }
+
+ if (FIELD_GET(BMA400_STEP_STAT_MASK, le16_to_cpu(data->status))) {
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
+ IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_NONE),
+ timestamp);
+
+ if (data->activity_event_en) {
+ ret = regmap_read(data->regmap, BMA400_STEP_STAT_REG,
+ &act);
+ if (ret)
+ goto unlock_err;
+
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACTIVITY, 0,
+ bma400_act_to_mod(act),
+ IIO_EV_TYPE_CHANGE,
+ IIO_EV_DIR_NONE),
+ timestamp);
+ }
+ }
+
+ if (FIELD_GET(BMA400_INT_DRDY_MSK, le16_to_cpu(data->status))) {
+ mutex_unlock(&data->mutex);
+ iio_trigger_poll_chained(data->trig);
+ return IRQ_HANDLED;
+ }
+
+ mutex_unlock(&data->mutex);
+ return IRQ_HANDLED;
+
+unlock_err:
+ mutex_unlock(&data->mutex);
+ return IRQ_NONE;
+}
+
+int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name)
{
struct iio_dev *indio_dev;
struct bma400_data *data;
@@ -820,33 +1428,43 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name)
indio_dev->info = &bma400_info;
indio_dev->channels = bma400_channels;
indio_dev->num_channels = ARRAY_SIZE(bma400_channels);
+ indio_dev->available_scan_masks = bma400_avail_scan_masks;
indio_dev->modes = INDIO_DIRECT_MODE;
- dev_set_drvdata(dev, indio_dev);
-
- return iio_device_register(indio_dev);
-}
-EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400);
+ if (irq > 0) {
+ data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!data->trig)
+ return -ENOMEM;
-void bma400_remove(struct device *dev)
-{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct bma400_data *data = iio_priv(indio_dev);
- int ret;
+ data->trig->ops = &bma400_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, indio_dev);
- mutex_lock(&data->mutex);
- ret = bma400_set_power_mode(data, POWER_MODE_SLEEP);
- mutex_unlock(&data->mutex);
+ ret = devm_iio_trigger_register(data->dev, data->trig);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "iio trigger register fail\n");
+
+ indio_dev->trig = iio_trigger_get(data->trig);
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ &bma400_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "request irq %d failed\n", irq);
+ }
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ &bma400_trigger_handler, NULL);
if (ret)
- dev_warn(dev, "Failed to put device into sleep mode (%pe)\n", ERR_PTR(ret));
+ return dev_err_probe(data->dev, ret,
+ "iio triggered buffer setup failed\n");
- regulator_bulk_disable(ARRAY_SIZE(data->regulators),
- data->regulators);
-
- iio_device_unregister(indio_dev);
+ return devm_iio_device_register(dev, indio_dev);
}
-EXPORT_SYMBOL_NS(bma400_remove, IIO_BMA400);
+EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400);
MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>");
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core");
diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c
index da104ffd3fe0..1ba2a982ea73 100644
--- a/drivers/iio/accel/bma400_i2c.c
+++ b/drivers/iio/accel/bma400_i2c.c
@@ -24,14 +24,7 @@ static int bma400_i2c_probe(struct i2c_client *client,
return PTR_ERR(regmap);
}
- return bma400_probe(&client->dev, regmap, id->name);
-}
-
-static int bma400_i2c_remove(struct i2c_client *client)
-{
- bma400_remove(&client->dev);
-
- return 0;
+ return bma400_probe(&client->dev, regmap, client->irq, id->name);
}
static const struct i2c_device_id bma400_i2c_ids[] = {
@@ -52,7 +45,6 @@ static struct i2c_driver bma400_i2c_driver = {
.of_match_table = bma400_of_i2c_match,
},
.probe = bma400_i2c_probe,
- .remove = bma400_i2c_remove,
.id_table = bma400_i2c_ids,
};
diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c
index 51f23bdc0ea5..ec13c044b304 100644
--- a/drivers/iio/accel/bma400_spi.c
+++ b/drivers/iio/accel/bma400_spi.c
@@ -84,12 +84,7 @@ static int bma400_spi_probe(struct spi_device *spi)
if (ret)
dev_err(&spi->dev, "Failed to read chip id register\n");
- return bma400_probe(&spi->dev, regmap, id->name);
-}
-
-static void bma400_spi_remove(struct spi_device *spi)
-{
- bma400_remove(&spi->dev);
+ return bma400_probe(&spi->dev, regmap, spi->irq, id->name);
}
static const struct spi_device_id bma400_spi_ids[] = {
@@ -110,7 +105,6 @@ static struct spi_driver bma400_spi_driver = {
.of_match_table = bma400_of_spi_match,
},
.probe = bma400_spi_probe,
- .remove = bma400_spi_remove,
.id_table = bma400_spi_ids,
};
diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c
index 8b2728bbcade..bca4cf98bf4d 100644
--- a/drivers/iio/accel/bmi088-accel-core.c
+++ b/drivers/iio/accel/bmi088-accel-core.c
@@ -6,6 +6,7 @@
* Copyright (c) 2018-2021, Topic Embedded Products
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -73,6 +74,8 @@
#define BMI088_ACCEL_FIFO_MODE_FIFO 0x40
#define BMI088_ACCEL_FIFO_MODE_STREAM 0x80
+#define BMIO088_ACCEL_ACC_RANGE_MSK GENMASK(1, 0)
+
enum bmi088_accel_axis {
AXIS_X,
AXIS_Y,
@@ -119,12 +122,13 @@ struct bmi088_accel_chip_info {
u8 chip_id;
const struct iio_chan_spec *channels;
int num_channels;
+ const int scale_table[4][2];
};
struct bmi088_accel_data {
struct regmap *regmap;
const struct bmi088_accel_chip_info *chip_info;
- u8 buffer[2] ____cacheline_aligned; /* shared DMA safe buffer */
+ u8 buffer[2] __aligned(IIO_DMA_MINALIGN); /* shared DMA safe buffer */
};
static const struct regmap_range bmi088_volatile_ranges[] = {
@@ -236,6 +240,21 @@ static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val)
BMI088_ACCEL_MODE_ODR_MASK, regval);
}
+static int bmi088_accel_set_scale(struct bmi088_accel_data *data, int val, int val2)
+{
+ unsigned int i;
+
+ for (i = 0; i < 4; i++)
+ if (val == data->chip_info->scale_table[i][0] &&
+ val2 == data->chip_info->scale_table[i][1])
+ break;
+
+ if (i == 4)
+ return -EINVAL;
+
+ return regmap_write(data->regmap, BMI088_ACCEL_REG_ACC_RANGE, i);
+}
+
static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val)
{
int ret;
@@ -280,6 +299,7 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
struct bmi088_accel_data *data = iio_priv(indio_dev);
struct device *dev = regmap_get_device(data->regmap);
int ret;
+ int reg;
switch (mask) {
case IIO_CHAN_INFO_RAW:
@@ -330,13 +350,14 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
return ret;
ret = regmap_read(data->regmap,
- BMI088_ACCEL_REG_ACC_RANGE, val);
+ BMI088_ACCEL_REG_ACC_RANGE, &reg);
if (ret)
goto out_read_raw_pm_put;
- *val2 = 15 - (*val & 0x3);
- *val = 3 * 980;
- ret = IIO_VAL_FRACTIONAL_LOG2;
+ reg = FIELD_GET(BMIO088_ACCEL_ACC_RANGE_MSK, reg);
+ *val = data->chip_info->scale_table[reg][0];
+ *val2 = data->chip_info->scale_table[reg][1];
+ ret = IIO_VAL_INT_PLUS_MICRO;
goto out_read_raw_pm_put;
default:
@@ -367,7 +388,14 @@ static int bmi088_accel_read_avail(struct iio_dev *indio_dev,
const int **vals, int *type, int *length,
long mask)
{
+ struct bmi088_accel_data *data = iio_priv(indio_dev);
+
switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)data->chip_info->scale_table;
+ *length = 8;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*type = IIO_VAL_INT_PLUS_MICRO;
*vals = bmi088_sample_freqs;
@@ -387,6 +415,15 @@ static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
int ret;
switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ ret = bmi088_accel_set_scale(data, val, val2);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = pm_runtime_resume_and_get(dev);
if (ret)
@@ -408,7 +445,8 @@ static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
- .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = AXIS_##_axis, \
}
@@ -427,11 +465,26 @@ static const struct iio_chan_spec bmi088_accel_channels[] = {
};
static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = {
- [0] = {
- .name = "bmi088a",
+ [BOSCH_BMI085] = {
+ .name = "bmi085-accel",
+ .chip_id = 0x1F,
+ .channels = bmi088_accel_channels,
+ .num_channels = ARRAY_SIZE(bmi088_accel_channels),
+ .scale_table = {{0, 598}, {0, 1196}, {0, 2393}, {0, 4785}},
+ },
+ [BOSCH_BMI088] = {
+ .name = "bmi088-accel",
.chip_id = 0x1E,
.channels = bmi088_accel_channels,
.num_channels = ARRAY_SIZE(bmi088_accel_channels),
+ .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}},
+ },
+ [BOSCH_BMI090L] = {
+ .name = "bmi090l-accel",
+ .chip_id = 0x1A,
+ .channels = bmi088_accel_channels,
+ .num_channels = ARRAY_SIZE(bmi088_accel_channels),
+ .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}},
},
};
@@ -446,12 +499,15 @@ static const unsigned long bmi088_accel_scan_masks[] = {
0
};
-static int bmi088_accel_chip_init(struct bmi088_accel_data *data)
+static int bmi088_accel_chip_init(struct bmi088_accel_data *data, enum bmi_device_type type)
{
struct device *dev = regmap_get_device(data->regmap);
int ret, i;
unsigned int val;
+ if (type >= BOSCH_UNKNOWN)
+ return -ENODEV;
+
/* Do a dummy read to enable SPI interface, won't harm I2C */
regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val);
@@ -477,22 +533,23 @@ static int bmi088_accel_chip_init(struct bmi088_accel_data *data)
}
/* Validate chip ID */
- for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++) {
- if (bmi088_accel_chip_info_tbl[i].chip_id == val) {
- data->chip_info = &bmi088_accel_chip_info_tbl[i];
+ for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++)
+ if (bmi088_accel_chip_info_tbl[i].chip_id == val)
break;
- }
- }
- if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl)) {
- dev_err(dev, "Invalid chip %x\n", val);
- return -ENODEV;
- }
+
+ if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl))
+ data->chip_info = &bmi088_accel_chip_info_tbl[type];
+ else
+ data->chip_info = &bmi088_accel_chip_info_tbl[i];
+
+ if (i != type)
+ dev_warn(dev, "unexpected chip id 0x%X\n", val);
return 0;
}
int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
- int irq, const char *name, bool block_supported)
+ int irq, enum bmi_device_type type)
{
struct bmi088_accel_data *data;
struct iio_dev *indio_dev;
@@ -507,13 +564,13 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
data->regmap = regmap;
- ret = bmi088_accel_chip_init(data);
+ ret = bmi088_accel_chip_init(data, type);
if (ret)
return ret;
indio_dev->channels = data->chip_info->channels;
indio_dev->num_channels = data->chip_info->num_channels;
- indio_dev->name = name ? name : data->chip_info->name;
+ indio_dev->name = data->chip_info->name;
indio_dev->available_scan_masks = bmi088_accel_scan_masks;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &bmi088_accel_info;
diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c
index 167c36cf1eb8..9e2ed3bd5661 100644
--- a/drivers/iio/accel/bmi088-accel-spi.c
+++ b/drivers/iio/accel/bmi088-accel-spi.c
@@ -52,8 +52,8 @@ static int bmi088_accel_probe(struct spi_device *spi)
return PTR_ERR(regmap);
}
- return bmi088_accel_core_probe(&spi->dev, regmap, spi->irq, id->name,
- true);
+ return bmi088_accel_core_probe(&spi->dev, regmap, spi->irq,
+ id->driver_data);
}
static void bmi088_accel_remove(struct spi_device *spi)
@@ -61,8 +61,18 @@ static void bmi088_accel_remove(struct spi_device *spi)
bmi088_accel_core_remove(&spi->dev);
}
+static const struct of_device_id bmi088_of_match[] = {
+ { .compatible = "bosch,bmi085-accel" },
+ { .compatible = "bosch,bmi088-accel" },
+ { .compatible = "bosch,bmi090l-accel" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, bmi088_of_match);
+
static const struct spi_device_id bmi088_accel_id[] = {
- {"bmi088-accel", },
+ {"bmi085-accel", BOSCH_BMI085},
+ {"bmi088-accel", BOSCH_BMI088},
+ {"bmi090l-accel", BOSCH_BMI090L},
{}
};
MODULE_DEVICE_TABLE(spi, bmi088_accel_id);
@@ -71,6 +81,7 @@ static struct spi_driver bmi088_accel_driver = {
.driver = {
.name = "bmi088_accel_spi",
.pm = &bmi088_accel_pm_ops,
+ .of_match_table = bmi088_of_match,
},
.probe = bmi088_accel_probe,
.remove = bmi088_accel_remove,
diff --git a/drivers/iio/accel/bmi088-accel.h b/drivers/iio/accel/bmi088-accel.h
index 5d40c7cf1cbc..80cd396a3141 100644
--- a/drivers/iio/accel/bmi088-accel.h
+++ b/drivers/iio/accel/bmi088-accel.h
@@ -8,11 +8,18 @@
struct device;
+enum bmi_device_type {
+ BOSCH_BMI085,
+ BOSCH_BMI088,
+ BOSCH_BMI090L,
+ BOSCH_UNKNOWN,
+};
+
extern const struct regmap_config bmi088_regmap_conf;
extern const struct dev_pm_ops bmi088_accel_pm_ops;
int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
- const char *name, bool block_supported);
+ enum bmi_device_type type);
void bmi088_accel_core_remove(struct device *dev);
#endif /* BMI088_ACCEL_H */
diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
index b6f3471b62dc..1c0171f26e99 100644
--- a/drivers/iio/accel/cros_ec_accel_legacy.c
+++ b/drivers/iio/accel/cros_ec_accel_legacy.c
@@ -230,7 +230,7 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
indio_dev->channels = cros_ec_accel_legacy_channels;
indio_dev->num_channels = ARRAY_SIZE(cros_ec_accel_legacy_channels);
/* The lid sensor needs to be presented inverted. */
- if (state->loc == MOTIONSENSE_LOC_LID) {
+ if (!strcmp(indio_dev->label, "accel-display")) {
state->sign[CROS_EC_SENSOR_X] = -1;
state->sign[CROS_EC_SENSOR_Z] = -1;
}
diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c
index c8dc52f11037..d57f264bd6c8 100644
--- a/drivers/iio/accel/kxsd9-i2c.c
+++ b/drivers/iio/accel/kxsd9-i2c.c
@@ -55,7 +55,7 @@ static struct i2c_driver kxsd9_i2c_driver = {
.driver = {
.name = "kxsd9",
.of_match_table = kxsd9_of_match,
- .pm = &kxsd9_dev_pm_ops,
+ .pm = pm_ptr(&kxsd9_dev_pm_ops),
},
.probe = kxsd9_i2c_probe,
.remove = kxsd9_i2c_remove,
diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c
index b7b5af45429e..07f14a9f22c7 100644
--- a/drivers/iio/accel/kxsd9-spi.c
+++ b/drivers/iio/accel/kxsd9-spi.c
@@ -52,7 +52,7 @@ MODULE_DEVICE_TABLE(of, kxsd9_of_match);
static struct spi_driver kxsd9_spi_driver = {
.driver = {
.name = "kxsd9",
- .pm = &kxsd9_dev_pm_ops,
+ .pm = pm_ptr(&kxsd9_dev_pm_ops),
.of_match_table = kxsd9_of_match,
},
.probe = kxsd9_spi_probe,
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 3975860331a6..ba99649fe195 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -492,7 +492,6 @@ void kxsd9_common_remove(struct device *dev)
}
EXPORT_SYMBOL_NS(kxsd9_common_remove, IIO_KXSD9);
-#ifdef CONFIG_PM
static int kxsd9_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
@@ -508,15 +507,9 @@ static int kxsd9_runtime_resume(struct device *dev)
return kxsd9_power_up(st);
}
-#endif /* CONFIG_PM */
-const struct dev_pm_ops kxsd9_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend,
- kxsd9_runtime_resume, NULL)
-};
-EXPORT_SYMBOL_NS(kxsd9_dev_pm_ops, IIO_KXSD9);
+EXPORT_NS_RUNTIME_DEV_PM_OPS(kxsd9_dev_pm_ops, kxsd9_runtime_suspend,
+ kxsd9_runtime_resume, NULL, IIO_KXSD9);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Kionix KXSD9 driver");
diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c
index 679e69cd7657..c15d16e7f1da 100644
--- a/drivers/iio/accel/mc3230.c
+++ b/drivers/iio/accel/mc3230.c
@@ -157,7 +157,9 @@ static int mc3230_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
- return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
+ mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
+
+ return 0;
}
static int mc3230_suspend(struct device *dev)
diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c
index 112a5a33c29f..794f2f383303 100644
--- a/drivers/iio/accel/mma7660.c
+++ b/drivers/iio/accel/mma7660.c
@@ -7,8 +7,8 @@
* IIO driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c.
*/
-#include <linux/acpi.h>
#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -255,7 +255,7 @@ static const struct of_device_id mma7660_of_match[] = {
};
MODULE_DEVICE_TABLE(of, mma7660_of_match);
-static const struct acpi_device_id __maybe_unused mma7660_acpi_id[] = {
+static const struct acpi_device_id mma7660_acpi_id[] = {
{"MMA7660", 0},
{}
};
@@ -267,7 +267,7 @@ static struct i2c_driver mma7660_driver = {
.name = "mma7660",
.pm = pm_sleep_ptr(&mma7660_pm_ops),
.of_match_table = mma7660_of_match,
- .acpi_match_table = ACPI_PTR(mma7660_acpi_id),
+ .acpi_match_table = mma7660_acpi_id,
},
.probe = mma7660_probe,
.remove = mma7660_remove,
diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
index 29a68a7d34cd..87c54e41f6cc 100644
--- a/drivers/iio/accel/sca3000.c
+++ b/drivers/iio/accel/sca3000.c
@@ -167,8 +167,8 @@ struct sca3000_state {
int mo_det_use_count;
struct mutex lock;
/* Can these share a cacheline ? */
- u8 rx[384] ____cacheline_aligned;
- u8 tx[6] ____cacheline_aligned;
+ u8 rx[384] __aligned(IIO_DMA_MINALIGN);
+ u8 tx[6] __aligned(IIO_DMA_MINALIGN);
};
/**
@@ -424,7 +424,7 @@ error_ret:
* sca3000_print_rev() - sysfs interface to read the chip revision number
* @indio_dev: Device instance specific generic IIO data.
* Driver specific device instance data can be obtained via
- * via iio_priv(indio_dev)
+ * iio_priv(indio_dev)
*/
static int sca3000_print_rev(struct iio_dev *indio_dev)
{
diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c
index f7ef8ecfd34a..3c4827bfef53 100644
--- a/drivers/iio/accel/sca3300.c
+++ b/drivers/iio/accel/sca3300.c
@@ -38,17 +38,24 @@
/* Device ID */
#define SCA3300_REG_WHOAMI 0x10
#define SCA3300_WHOAMI_ID 0x51
+#define SCL3300_WHOAMI_ID 0xC1
/* Device return status and mask */
#define SCA3300_VALUE_RS_ERROR 0x3
#define SCA3300_MASK_RS_STATUS GENMASK(1, 0)
+#define SCL3300_REG_ANG_CTRL 0x0C
+#define SCL3300_ANG_ENABLE 0x1F
+
enum sca3300_scan_indexes {
SCA3300_ACC_X = 0,
SCA3300_ACC_Y,
SCA3300_ACC_Z,
SCA3300_TEMP,
SCA3300_TIMESTAMP,
+ SCA3300_INCLI_X,
+ SCA3300_INCLI_Y,
+ SCA3300_INCLI_Z,
};
#define SCA3300_ACCEL_CHANNEL(index, reg, axis) { \
@@ -72,27 +79,72 @@ enum sca3300_scan_indexes {
}, \
}
+#define SCA3300_INCLI_CHANNEL(index, reg, axis) { \
+ .type = IIO_INCLI, \
+ .address = reg, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define SCA3300_TEMP_CHANNEL(index, reg) { \
+ .type = IIO_TEMP, \
+ .address = reg, \
+ .scan_index = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
static const struct iio_chan_spec sca3300_channels[] = {
SCA3300_ACCEL_CHANNEL(SCA3300_ACC_X, 0x1, X),
SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Y, 0x2, Y),
SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Z, 0x3, Z),
- {
- .type = IIO_TEMP,
- .address = 0x5,
- .scan_index = SCA3300_TEMP,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
- .scan_type = {
- .sign = 's',
- .realbits = 16,
- .storagebits = 16,
- .endianness = IIO_CPU,
- },
- },
+ SCA3300_TEMP_CHANNEL(SCA3300_TEMP, 0x05),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
-static const int sca3300_lp_freq[] = {70, 70, 70, 10};
-static const int sca3300_accel_scale[][2] = {{0, 370}, {0, 741}, {0, 185}, {0, 185}};
+static const int sca3300_lp_freq[] = {70, 10};
+static const int sca3300_lp_freq_map[] = {0, 0, 0, 1};
+
+static const int scl3300_lp_freq[] = {40, 70, 10};
+static const int scl3300_lp_freq_map[] = {0, 1, 2};
+
+static const int sca3300_accel_scale[][2] = {{0, 370}, {0, 741}, {0, 185}};
+static const int sca3300_accel_scale_map[] = {0, 1, 2, 2};
+
+static const int scl3300_accel_scale[][2] = {{0, 167}, {0, 333}, {0, 83}};
+static const int scl3300_accel_scale_map[] = {0, 1, 2};
+
+static const int scl3300_incli_scale[][2] = {{0, 5495}};
+static const int scl3300_incli_scale_map[] = {0, 0, 0};
+
+static const int sca3300_avail_modes_map[] = {0, 1, 2, 3};
+static const int scl3300_avail_modes_map[] = {0, 1, 3};
+
+static const struct iio_chan_spec scl3300_channels[] = {
+ SCA3300_ACCEL_CHANNEL(SCA3300_ACC_X, 0x1, X),
+ SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Y, 0x2, Y),
+ SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Z, 0x3, Z),
+ SCA3300_TEMP_CHANNEL(SCA3300_TEMP, 0x05),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ SCA3300_INCLI_CHANNEL(SCA3300_INCLI_X, 0x09, X),
+ SCA3300_INCLI_CHANNEL(SCA3300_INCLI_Y, 0x0A, Y),
+ SCA3300_INCLI_CHANNEL(SCA3300_INCLI_Z, 0x0B, Z),
+};
static const unsigned long sca3300_scan_masks[] = {
BIT(SCA3300_ACC_X) | BIT(SCA3300_ACC_Y) | BIT(SCA3300_ACC_Z) |
@@ -100,10 +152,38 @@ static const unsigned long sca3300_scan_masks[] = {
0
};
+static const unsigned long scl3300_scan_masks[] = {
+ BIT(SCA3300_ACC_X) | BIT(SCA3300_ACC_Y) | BIT(SCA3300_ACC_Z) |
+ BIT(SCA3300_TEMP) |
+ BIT(SCA3300_INCLI_X) | BIT(SCA3300_INCLI_Y) | BIT(SCA3300_INCLI_Z),
+ 0
+};
+
+struct sca3300_chip_info {
+ const char *name;
+ const unsigned long *scan_masks;
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+ u8 num_accel_scales;
+ const int (*accel_scale)[2];
+ const int *accel_scale_map;
+ const int (*incli_scale)[2];
+ const int *incli_scale_map;
+ u8 num_incli_scales;
+ u8 num_freqs;
+ const int *freq_table;
+ const int *freq_map;
+ const int *avail_modes_table;
+ u8 num_avail_modes;
+ u8 chip_id;
+ bool angle_supported;
+};
+
/**
* struct sca3300_data - device data
* @spi: SPI device structure
* @lock: Data buffer lock
+ * @chip: Sensor chip specific information
* @scan: Triggered buffer. Four channel 16-bit data + 64-bit timestamp
* @txbuf: Transmit buffer
* @rxbuf: Receive buffer
@@ -111,14 +191,53 @@ static const unsigned long sca3300_scan_masks[] = {
struct sca3300_data {
struct spi_device *spi;
struct mutex lock;
+ const struct sca3300_chip_info *chip;
struct {
s16 channels[4];
s64 ts __aligned(sizeof(s64));
} scan;
- u8 txbuf[4] ____cacheline_aligned;
+ u8 txbuf[4] __aligned(IIO_DMA_MINALIGN);
u8 rxbuf[4];
};
+static const struct sca3300_chip_info sca3300_chip_tbl[] = {
+ {
+ .name = "sca3300",
+ .scan_masks = sca3300_scan_masks,
+ .channels = sca3300_channels,
+ .num_channels = ARRAY_SIZE(sca3300_channels),
+ .num_accel_scales = ARRAY_SIZE(sca3300_accel_scale)*2,
+ .accel_scale = sca3300_accel_scale,
+ .accel_scale_map = sca3300_accel_scale_map,
+ .num_freqs = ARRAY_SIZE(sca3300_lp_freq),
+ .freq_table = sca3300_lp_freq,
+ .freq_map = sca3300_lp_freq_map,
+ .avail_modes_table = sca3300_avail_modes_map,
+ .num_avail_modes = 4,
+ .chip_id = SCA3300_WHOAMI_ID,
+ .angle_supported = false,
+ },
+ {
+ .name = "scl3300",
+ .scan_masks = scl3300_scan_masks,
+ .channels = scl3300_channels,
+ .num_channels = ARRAY_SIZE(scl3300_channels),
+ .num_accel_scales = ARRAY_SIZE(scl3300_accel_scale)*2,
+ .accel_scale = scl3300_accel_scale,
+ .accel_scale_map = scl3300_accel_scale_map,
+ .incli_scale = scl3300_incli_scale,
+ .incli_scale_map = scl3300_incli_scale_map,
+ .num_incli_scales = ARRAY_SIZE(scl3300_incli_scale)*2,
+ .num_freqs = ARRAY_SIZE(scl3300_lp_freq),
+ .freq_table = scl3300_lp_freq,
+ .freq_map = scl3300_lp_freq_map,
+ .avail_modes_table = scl3300_avail_modes_map,
+ .num_avail_modes = 3,
+ .chip_id = SCL3300_WHOAMI_ID,
+ .angle_supported = true,
+ },
+};
+
DECLARE_CRC8_TABLE(sca3300_crc_table);
static int sca3300_transfer(struct sca3300_data *sca_data, int *val)
@@ -225,36 +344,91 @@ static int sca3300_write_reg(struct sca3300_data *sca_data, u8 reg, int val)
return sca3300_error_handler(sca_data);
}
+static int sca3300_set_op_mode(struct sca3300_data *sca_data, int index)
+{
+ if ((index < 0) || (index >= sca_data->chip->num_avail_modes))
+ return -EINVAL;
+
+ return sca3300_write_reg(sca_data, SCA3300_REG_MODE,
+ sca_data->chip->avail_modes_table[index]);
+}
+
+static int sca3300_get_op_mode(struct sca3300_data *sca_data, int *index)
+{
+ int reg_val;
+ int ret;
+ int i;
+
+ ret = sca3300_read_reg(sca_data, SCA3300_REG_MODE, &reg_val);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sca_data->chip->num_avail_modes; i++) {
+ if (sca_data->chip->avail_modes_table[i] == reg_val)
+ break;
+ }
+ if (i == sca_data->chip->num_avail_modes)
+ return -EINVAL;
+
+ *index = i;
+ return 0;
+}
+
+static int sca3300_set_frequency(struct sca3300_data *data, int val)
+{
+ const struct sca3300_chip_info *chip = data->chip;
+ unsigned int index;
+ int *opmode_scale;
+ int *new_scale;
+ unsigned int i;
+
+ if (sca3300_get_op_mode(data, &index))
+ return -EINVAL;
+
+ /*
+ * Find a mode in which the requested sampling frequency is available
+ * and the scaling currently set is retained.
+ */
+ opmode_scale = (int *)chip->accel_scale[chip->accel_scale_map[index]];
+ for (i = 0; i < chip->num_avail_modes; i++) {
+ new_scale = (int *)chip->accel_scale[chip->accel_scale_map[i]];
+ if ((val == chip->freq_table[chip->freq_map[i]]) &&
+ (opmode_scale[1] == new_scale[1]) &&
+ (opmode_scale[0] == new_scale[0]))
+ break;
+ }
+ if (i == chip->num_avail_modes)
+ return -EINVAL;
+
+ return sca3300_set_op_mode(data, i);
+}
+
static int sca3300_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct sca3300_data *data = iio_priv(indio_dev);
- int reg_val;
- int ret;
+ int index;
int i;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- if (val)
+ if (chan->type != IIO_ACCEL)
return -EINVAL;
-
- for (i = 0; i < ARRAY_SIZE(sca3300_accel_scale); i++) {
- if (val2 == sca3300_accel_scale[i][1])
- return sca3300_write_reg(data, SCA3300_REG_MODE, i);
+ /*
+ * Letting scale take priority over sampling frequency.
+ * That makes sense given we can only ever end up increasing
+ * the sampling frequency which is unlikely to be a problem.
+ */
+ for (i = 0; i < data->chip->num_avail_modes; i++) {
+ index = data->chip->accel_scale_map[i];
+ if ((val == data->chip->accel_scale[index][0]) &&
+ (val2 == data->chip->accel_scale[index][1]))
+ return sca3300_set_op_mode(data, i);
}
return -EINVAL;
-
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- ret = sca3300_read_reg(data, SCA3300_REG_MODE, &reg_val);
- if (ret)
- return ret;
- /* freq. change is possible only for mode 3 and 4 */
- if (reg_val == 2 && val == sca3300_lp_freq[3])
- return sca3300_write_reg(data, SCA3300_REG_MODE, 3);
- if (reg_val == 3 && val == sca3300_lp_freq[2])
- return sca3300_write_reg(data, SCA3300_REG_MODE, 2);
- return -EINVAL;
+ return sca3300_set_frequency(data, val);
default:
return -EINVAL;
}
@@ -265,8 +439,8 @@ static int sca3300_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct sca3300_data *data = iio_priv(indio_dev);
+ int index;
int ret;
- int reg_val;
switch (mask) {
case IIO_CHAN_INFO_RAW:
@@ -275,17 +449,29 @@ static int sca3300_read_raw(struct iio_dev *indio_dev,
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- ret = sca3300_read_reg(data, SCA3300_REG_MODE, &reg_val);
+ ret = sca3300_get_op_mode(data, &index);
if (ret)
return ret;
- *val = 0;
- *val2 = sca3300_accel_scale[reg_val][1];
- return IIO_VAL_INT_PLUS_MICRO;
+ switch (chan->type) {
+ case IIO_INCLI:
+ index = data->chip->incli_scale_map[index];
+ *val = data->chip->incli_scale[index][0];
+ *val2 = data->chip->incli_scale[index][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ACCEL:
+ index = data->chip->accel_scale_map[index];
+ *val = data->chip->accel_scale[index][0];
+ *val2 = data->chip->accel_scale[index][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- ret = sca3300_read_reg(data, SCA3300_REG_MODE, &reg_val);
+ ret = sca3300_get_op_mode(data, &index);
if (ret)
return ret;
- *val = sca3300_lp_freq[reg_val];
+ index = data->chip->freq_map[index];
+ *val = data->chip->freq_table[index];
return IIO_VAL_INT;
default:
return -EINVAL;
@@ -329,6 +515,7 @@ static int sca3300_init(struct sca3300_data *sca_data,
{
int value = 0;
int ret;
+ int i;
ret = sca3300_write_reg(sca_data, SCA3300_REG_MODE,
SCA3300_MODE_SW_RESET);
@@ -337,20 +524,33 @@ static int sca3300_init(struct sca3300_data *sca_data,
/*
* Wait 1ms after SW-reset command.
- * Wait 15ms for settling of signal paths.
+ * Wait for the settling of signal paths,
+ * 15ms for SCA3300 and 25ms for SCL3300,
*/
- usleep_range(16e3, 50e3);
+ usleep_range(26e3, 50e3);
ret = sca3300_read_reg(sca_data, SCA3300_REG_WHOAMI, &value);
if (ret)
return ret;
- if (value != SCA3300_WHOAMI_ID) {
- dev_err(&sca_data->spi->dev,
- "device id not expected value, %d != %u\n",
- value, SCA3300_WHOAMI_ID);
+ for (i = 0; i < ARRAY_SIZE(sca3300_chip_tbl); i++) {
+ if (sca3300_chip_tbl[i].chip_id == value)
+ break;
+ }
+ if (i == ARRAY_SIZE(sca3300_chip_tbl)) {
+ dev_err(&sca_data->spi->dev, "unknown chip id %x\n", value);
return -ENODEV;
}
+
+ sca_data->chip = &sca3300_chip_tbl[i];
+
+ if (sca_data->chip->angle_supported) {
+ ret = sca3300_write_reg(sca_data, SCL3300_REG_ANG_CTRL,
+ SCL3300_ANG_ENABLE);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -382,15 +582,26 @@ static int sca3300_read_avail(struct iio_dev *indio_dev,
const int **vals, int *type, int *length,
long mask)
{
+ struct sca3300_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
- *vals = (const int *)sca3300_accel_scale;
- *length = ARRAY_SIZE(sca3300_accel_scale) * 2 - 2;
- *type = IIO_VAL_INT_PLUS_MICRO;
- return IIO_AVAIL_LIST;
+ switch (chan->type) {
+ case IIO_INCLI:
+ *vals = (const int *)data->chip->incli_scale;
+ *length = data->chip->num_incli_scales;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ case IIO_ACCEL:
+ *vals = (const int *)data->chip->accel_scale;
+ *length = data->chip->num_accel_scales;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
- *vals = &sca3300_lp_freq[2];
- *length = 2;
+ *vals = (const int *)data->chip->freq_table;
+ *length = data->chip->num_freqs;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default:
@@ -422,11 +633,6 @@ static int sca3300_probe(struct spi_device *spi)
crc8_populate_msb(sca3300_crc_table, SCA3300_CRC8_POLYNOMIAL);
indio_dev->info = &sca3300_info;
- indio_dev->name = SCA3300_ALIAS;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = sca3300_channels;
- indio_dev->num_channels = ARRAY_SIZE(sca3300_channels);
- indio_dev->available_scan_masks = sca3300_scan_masks;
ret = sca3300_init(sca_data, indio_dev);
if (ret) {
@@ -434,6 +640,12 @@ static int sca3300_probe(struct spi_device *spi)
return ret;
}
+ indio_dev->name = sca_data->chip->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = sca_data->chip->channels;
+ indio_dev->num_channels = sca_data->chip->num_channels;
+ indio_dev->available_scan_masks = sca_data->chip->scan_masks;
+
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
iio_pollfunc_store_time,
sca3300_trigger_handler, NULL);
@@ -454,6 +666,7 @@ static int sca3300_probe(struct spi_device *spi)
static const struct of_device_id sca3300_dt_ids[] = {
{ .compatible = "murata,sca3300"},
+ { .compatible = "murata,scl3300"},
{}
};
MODULE_DEVICE_TABLE(of, sca3300_dt_ids);
diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
index a71dfff3ca4a..ceca28913355 100644
--- a/drivers/iio/accel/stk8312.c
+++ b/drivers/iio/accel/stk8312.c
@@ -608,7 +608,9 @@ static int stk8312_remove(struct i2c_client *client)
if (data->dready_trig)
iio_trigger_unregister(data->dready_trig);
- return stk8312_set_mode(data, STK8312_MODE_STANDBY);
+ stk8312_set_mode(data, STK8312_MODE_STANDBY);
+
+ return 0;
}
static int stk8312_suspend(struct device *dev)
diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c
index 0067ec5cbae8..7d59efb41e22 100644
--- a/drivers/iio/accel/stk8ba50.c
+++ b/drivers/iio/accel/stk8ba50.c
@@ -501,7 +501,9 @@ static int stk8ba50_remove(struct i2c_client *client)
if (data->dready_trig)
iio_trigger_unregister(data->dready_trig);
- return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+ stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+
+ return 0;
}
static int stk8ba50_suspend(struct device *dev)