summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/iio/accel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drivers/iio/accel')
-rw-r--r--kernel/drivers/iio/accel/Kconfig75
-rw-r--r--kernel/drivers/iio/accel/Makefile9
-rw-r--r--kernel/drivers/iio/accel/bma180.c1
-rw-r--r--kernel/drivers/iio/accel/bmc150-accel-core.c (renamed from kernel/drivers/iio/accel/bmc150-accel.c)661
-rw-r--r--kernel/drivers/iio/accel/bmc150-accel-i2c.c102
-rw-r--r--kernel/drivers/iio/accel/bmc150-accel-spi.c91
-rw-r--r--kernel/drivers/iio/accel/bmc150-accel.h20
-rw-r--r--kernel/drivers/iio/accel/hid-sensor-accel-3d.c15
-rw-r--r--kernel/drivers/iio/accel/kxcjk-1013.c76
-rw-r--r--kernel/drivers/iio/accel/kxsd9.c1
-rw-r--r--kernel/drivers/iio/accel/mma8452.c992
-rw-r--r--kernel/drivers/iio/accel/mma9551_core.c43
-rw-r--r--kernel/drivers/iio/accel/mma9551_core.h8
-rw-r--r--kernel/drivers/iio/accel/mma9553.c254
-rw-r--r--kernel/drivers/iio/accel/mxc4005.c567
-rw-r--r--kernel/drivers/iio/accel/st_accel.h2
-rw-r--r--kernel/drivers/iio/accel/st_accel_core.c99
-rw-r--r--kernel/drivers/iio/accel/st_accel_i2c.c10
-rw-r--r--kernel/drivers/iio/accel/st_accel_spi.c2
-rw-r--r--kernel/drivers/iio/accel/stk8312.c675
-rw-r--r--kernel/drivers/iio/accel/stk8ba50.c571
21 files changed, 3528 insertions, 746 deletions
diff --git a/kernel/drivers/iio/accel/Kconfig b/kernel/drivers/iio/accel/Kconfig
index 7c9a9a94a..16cc5c691 100644
--- a/kernel/drivers/iio/accel/Kconfig
+++ b/kernel/drivers/iio/accel/Kconfig
@@ -19,19 +19,27 @@ config BMA180
config BMC150_ACCEL
tristate "Bosch BMC150 Accelerometer Driver"
- depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select REGMAP
+ select BMC150_ACCEL_I2C if I2C
+ select BMC150_ACCEL_SPI if SPI
help
Say yes here to build support for the following Bosch accelerometers:
BMC150, BMI055, BMA250E, BMA222E, BMA255, BMA280.
- Currently this only supports the device via an i2c interface.
-
This is a combo module with both accelerometer and magnetometer.
This driver is only implementing accelerometer part, which has
its own address and register map.
+config BMC150_ACCEL_I2C
+ tristate
+ select REGMAP_I2C
+
+config BMC150_ACCEL_SPI
+ tristate
+ select REGMAP_SPI
+
config HID_SENSOR_ACCEL_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
@@ -86,18 +94,6 @@ config KXSD9
To compile this driver as a module, choose M here: the module
will be called kxsd9.
-config MMA8452
- tristate "Freescale MMA8452Q Accelerometer Driver"
- depends on I2C
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
- help
- Say yes here to build support for the Freescale MMA8452Q 3-axis
- accelerometer.
-
- To compile this driver as a module, choose M here: the module
- will be called mma8452.
-
config KXCJK1013
tristate "Kionix 3-Axis Accelerometer Driver"
depends on I2C
@@ -111,6 +107,18 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will
be called kxcjk-1013.
+config MMA8452
+ tristate "Freescale MMA8452Q and similar Accelerometers Driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the following Freescale 3-axis
+ accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mma8452.
+
config MMA9551_CORE
tristate
@@ -136,4 +144,41 @@ config MMA9553
To compile this driver as a module, choose M here: the module
will be called mma9553.
+
+config MXC4005
+ tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ help
+ Say yes here to build support for the Memsic MXC4005XC 3-axis
+ accelerometer.
+
+ To compile this driver as a module, choose M. The module will be
+ called mxc4005.
+
+config STK8312
+ tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to get support for the Sensortek STK8312 3-axis
+ accelerometer.
+
+ Choosing M will build the driver as a module. If so, the module
+ will be called stk8312.
+
+config STK8BA50
+ tristate "Sensortek STK8BA50 3-Axis Accelerometer Driver"
+ depends on I2C
+ depends on IIO_TRIGGER
+ help
+ Say yes here to get support for the Sensortek STK8BA50 3-axis
+ accelerometer.
+
+ Choosing M will build the driver as a module. If so, the module
+ will be called stk8ba50.
+
endmenu
diff --git a/kernel/drivers/iio/accel/Makefile b/kernel/drivers/iio/accel/Makefile
index 99d89e46c..7925f166e 100644
--- a/kernel/drivers/iio/accel/Makefile
+++ b/kernel/drivers/iio/accel/Makefile
@@ -4,7 +4,9 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_BMA180) += bma180.o
-obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel.o
+obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
+obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
+obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
@@ -14,6 +16,11 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
+obj-$(CONFIG_MXC4005) += mxc4005.o
+
+obj-$(CONFIG_STK8312) += stk8312.o
+obj-$(CONFIG_STK8BA50) += stk8ba50.o
+
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
diff --git a/kernel/drivers/iio/accel/bma180.c b/kernel/drivers/iio/accel/bma180.c
index 75c6d2103..f04b88406 100644
--- a/kernel/drivers/iio/accel/bma180.c
+++ b/kernel/drivers/iio/accel/bma180.c
@@ -846,7 +846,6 @@ MODULE_DEVICE_TABLE(i2c, bma180_ids);
static struct i2c_driver bma180_driver = {
.driver = {
.name = "bma180",
- .owner = THIS_MODULE,
.pm = BMA180_PM_OPS,
},
.probe = bma180_probe,
diff --git a/kernel/drivers/iio/accel/bmc150-accel.c b/kernel/drivers/iio/accel/bmc150-accel-core.c
index bf827d012..2d33f1e82 100644
--- a/kernel/drivers/iio/accel/bmc150-accel.c
+++ b/kernel/drivers/iio/accel/bmc150-accel-core.c
@@ -35,10 +35,12 @@
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+
+#include "bmc150-accel.h"
#define BMC150_ACCEL_DRV_NAME "bmc150_accel"
#define BMC150_ACCEL_IRQ_NAME "bmc150_accel_event"
-#define BMC150_ACCEL_GPIO_NAME "bmc150_accel_int"
#define BMC150_ACCEL_REG_CHIP_ID 0x00
@@ -151,6 +153,7 @@ struct bmc150_scale_info {
};
struct bmc150_accel_chip_info {
+ const char *name;
u8 chip_id;
const struct iio_chan_spec *channels;
int num_channels;
@@ -184,7 +187,9 @@ enum bmc150_accel_trigger_id {
};
struct bmc150_accel_data {
- struct i2c_client *client;
+ struct regmap *regmap;
+ struct device *dev;
+ int irq;
struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS];
atomic_t active_intr;
struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS];
@@ -196,7 +201,7 @@ struct bmc150_accel_data {
u32 slope_thres;
u32 range;
int ev_enable_state;
- int64_t timestamp, old_timestamp;
+ int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */
const struct bmc150_accel_chip_info *chip_info;
};
@@ -241,6 +246,11 @@ static const struct {
{500000, BMC150_ACCEL_SLEEP_500_MS},
{1000000, BMC150_ACCEL_SLEEP_1_SEC} };
+static const struct regmap_config bmc150_i2c_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x3f,
+};
static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
enum bmc150_power_modes mode,
@@ -259,8 +269,9 @@ static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
dur_val =
bmc150_accel_sleep_value_table[i].reg_value;
}
- } else
+ } else {
dur_val = 0;
+ }
if (dur_val < 0)
return -EINVAL;
@@ -268,12 +279,11 @@ static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
lpw_bits = mode << BMC150_ACCEL_PMU_MODE_SHIFT;
lpw_bits |= (dur_val << BMC150_ACCEL_PMU_BIT_SLEEP_DUR_SHIFT);
- dev_dbg(&data->client->dev, "Set Mode bits %x\n", lpw_bits);
+ dev_dbg(data->dev, "Set Mode bits %x\n", lpw_bits);
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_PMU_LPW, lpw_bits);
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_PMU_LPW, lpw_bits);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n");
+ dev_err(data->dev, "Error writing reg_pmu_lpw\n");
return ret;
}
@@ -288,9 +298,8 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
for (i = 0; i < ARRAY_SIZE(bmc150_accel_samp_freq_table); ++i) {
if (bmc150_accel_samp_freq_table[i].val == val &&
- bmc150_accel_samp_freq_table[i].val2 == val2) {
- ret = i2c_smbus_write_byte_data(
- data->client,
+ bmc150_accel_samp_freq_table[i].val2 == val2) {
+ ret = regmap_write(data->regmap,
BMC150_ACCEL_REG_PMU_BW,
bmc150_accel_samp_freq_table[i].bw_bits);
if (ret < 0)
@@ -307,30 +316,23 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
static int bmc150_accel_update_slope(struct bmc150_accel_data *data)
{
- int ret, val;
+ int ret;
- ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_6,
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_6,
data->slope_thres);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_6\n");
- return ret;
- }
-
- ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_5);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_5\n");
+ dev_err(data->dev, "Error writing reg_int_6\n");
return ret;
}
- val = (ret & ~BMC150_ACCEL_SLOPE_DUR_MASK) | data->slope_dur;
- ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_5,
- val);
+ ret = regmap_update_bits(data->regmap, BMC150_ACCEL_REG_INT_5,
+ BMC150_ACCEL_SLOPE_DUR_MASK, data->slope_dur);
if (ret < 0) {
- dev_err(&data->client->dev, "Error write reg_int_5\n");
+ dev_err(data->dev, "Error updating reg_int_5\n");
return ret;
}
- dev_dbg(&data->client->dev, "%s: %x %x\n", __func__, data->slope_thres,
+ dev_dbg(data->dev, "%s: %x %x\n", __func__, data->slope_thres,
data->slope_dur);
return ret;
@@ -345,65 +347,6 @@ static int bmc150_accel_any_motion_setup(struct bmc150_accel_trigger *t,
return 0;
}
-static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
-{
- int ret;
-
- ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_CHIP_ID);
- if (ret < 0) {
- dev_err(&data->client->dev,
- "Error: Reading chip id\n");
- return ret;
- }
-
- dev_dbg(&data->client->dev, "Chip Id %x\n", ret);
- if (ret != data->chip_info->chip_id) {
- dev_err(&data->client->dev, "Invalid chip %x\n", ret);
- return -ENODEV;
- }
-
- ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
- if (ret < 0)
- return ret;
-
- /* Set Bandwidth */
- ret = bmc150_accel_set_bw(data, BMC150_ACCEL_DEF_BW, 0);
- if (ret < 0)
- return ret;
-
- /* Set Default Range */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_PMU_RANGE,
- BMC150_ACCEL_DEF_RANGE_4G);
- if (ret < 0) {
- dev_err(&data->client->dev,
- "Error writing reg_pmu_range\n");
- return ret;
- }
-
- data->range = BMC150_ACCEL_DEF_RANGE_4G;
-
- /* Set default slope duration and thresholds */
- data->slope_thres = BMC150_ACCEL_DEF_SLOPE_THRESHOLD;
- data->slope_dur = BMC150_ACCEL_DEF_SLOPE_DURATION;
- ret = bmc150_accel_update_slope(data);
- if (ret < 0)
- return ret;
-
- /* Set default as latched interrupts */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_RST_LATCH,
- BMC150_ACCEL_INT_MODE_LATCH_INT |
- BMC150_ACCEL_INT_MODE_LATCH_RESET);
- if (ret < 0) {
- dev_err(&data->client->dev,
- "Error writing reg_int_rst_latch\n");
- return ret;
- }
-
- return 0;
-}
-
static int bmc150_accel_get_bw(struct bmc150_accel_data *data, int *val,
int *val2)
{
@@ -437,17 +380,18 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
{
int ret;
- if (on)
- ret = pm_runtime_get_sync(&data->client->dev);
- else {
- pm_runtime_mark_last_busy(&data->client->dev);
- ret = pm_runtime_put_autosuspend(&data->client->dev);
+ if (on) {
+ ret = pm_runtime_get_sync(data->dev);
+ } else {
+ pm_runtime_mark_last_busy(data->dev);
+ ret = pm_runtime_put_autosuspend(data->dev);
}
+
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Failed: bmc150_accel_set_power_state for %d\n", on);
if (on)
- pm_runtime_put_noidle(&data->client->dev);
+ pm_runtime_put_noidle(data->dev);
return ret;
}
@@ -514,51 +458,31 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
}
/*
- * We will expect the enable and disable to do operation in
- * in reverse order. This will happen here anyway as our
- * resume operation uses sync mode runtime pm calls, the
- * suspend operation will be delayed by autosuspend delay
- * So the disable operation will still happen in reverse of
- * enable operation. When runtime pm is disabled the mode
- * is always on so sequence doesn't matter
+ * We will expect the enable and disable to do operation in reverse
+ * order. This will happen here anyway, as our resume operation uses
+ * sync mode runtime pm calls. The suspend operation will be delayed
+ * by autosuspend delay.
+ * So the disable operation will still happen in reverse order of
+ * enable operation. When runtime pm is disabled the mode is always on,
+ * so sequence doesn't matter.
*/
ret = bmc150_accel_set_power_state(data, state);
if (ret < 0)
return ret;
/* map the interrupt to the appropriate pins */
- ret = i2c_smbus_read_byte_data(data->client, info->map_reg);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_map\n");
- goto out_fix_power_state;
- }
- if (state)
- ret |= info->map_bitmask;
- else
- ret &= ~info->map_bitmask;
-
- ret = i2c_smbus_write_byte_data(data->client, info->map_reg,
- ret);
+ ret = regmap_update_bits(data->regmap, info->map_reg, info->map_bitmask,
+ (state ? info->map_bitmask : 0));
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_map\n");
+ dev_err(data->dev, "Error updating reg_int_map\n");
goto out_fix_power_state;
}
/* enable/disable the interrupt */
- ret = i2c_smbus_read_byte_data(data->client, info->en_reg);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_en\n");
- goto out_fix_power_state;
- }
-
- if (state)
- ret |= info->en_bitmask;
- else
- ret &= ~info->en_bitmask;
-
- ret = i2c_smbus_write_byte_data(data->client, info->en_reg, ret);
+ ret = regmap_update_bits(data->regmap, info->en_reg, info->en_bitmask,
+ (state ? info->en_bitmask : 0));
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_en\n");
+ dev_err(data->dev, "Error updating reg_int_en\n");
goto out_fix_power_state;
}
@@ -574,19 +498,17 @@ out_fix_power_state:
return ret;
}
-
static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
{
int ret, i;
for (i = 0; i < ARRAY_SIZE(data->chip_info->scale_table); ++i) {
if (data->chip_info->scale_table[i].scale == val) {
- ret = i2c_smbus_write_byte_data(
- data->client,
+ ret = regmap_write(data->regmap,
BMC150_ACCEL_REG_PMU_RANGE,
data->chip_info->scale_table[i].reg_range);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing pmu_range\n");
return ret;
}
@@ -602,16 +524,17 @@ static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
static int bmc150_accel_get_temp(struct bmc150_accel_data *data, int *val)
{
int ret;
+ unsigned int value;
mutex_lock(&data->mutex);
- ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_TEMP);
+ ret = regmap_read(data->regmap, BMC150_ACCEL_REG_TEMP, &value);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_temp\n");
+ dev_err(data->dev, "Error reading reg_temp\n");
mutex_unlock(&data->mutex);
return ret;
}
- *val = sign_extend32(ret, 7);
+ *val = sign_extend32(value, 7);
mutex_unlock(&data->mutex);
@@ -624,6 +547,7 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
{
int ret;
int axis = chan->scan_index;
+ unsigned int raw_val;
mutex_lock(&data->mutex);
ret = bmc150_accel_set_power_state(data, true);
@@ -632,15 +556,15 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
return ret;
}
- ret = i2c_smbus_read_word_data(data->client,
- BMC150_ACCEL_AXIS_TO_REG(axis));
+ ret = regmap_bulk_read(data->regmap, BMC150_ACCEL_AXIS_TO_REG(axis),
+ &raw_val, 2);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading axis %d\n", axis);
+ dev_err(data->dev, "Error reading axis %d\n", axis);
bmc150_accel_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
- *val = sign_extend32(ret >> chan->scan_type.shift,
+ *val = sign_extend32(raw_val >> chan->scan_type.shift,
chan->scan_type.realbits - 1);
ret = bmc150_accel_set_power_state(data, false);
mutex_unlock(&data->mutex);
@@ -674,8 +598,9 @@ static int bmc150_accel_read_raw(struct iio_dev *indio_dev,
if (chan->type == IIO_TEMP) {
*val = BMC150_ACCEL_TEMP_CENTER_VAL;
return IIO_VAL_INT;
- } else
+ } else {
return -EINVAL;
+ }
case IIO_CHAN_INFO_SCALE:
*val = 0;
switch (chan->type) {
@@ -776,7 +701,7 @@ static int bmc150_accel_write_event(struct iio_dev *indio_dev,
switch (info) {
case IIO_EV_INFO_VALUE:
- data->slope_thres = val & 0xFF;
+ data->slope_thres = val & BMC150_ACCEL_SLOPE_THRES_MASK;
break;
case IIO_EV_INFO_PERIOD:
data->slope_dur = val & BMC150_ACCEL_SLOPE_DUR_MASK;
@@ -793,7 +718,6 @@ static int bmc150_accel_read_event_config(struct iio_dev *indio_dev,
enum iio_event_type type,
enum iio_event_direction dir)
{
-
struct bmc150_accel_data *data = iio_priv(indio_dev);
return data->ev_enable_state;
@@ -827,7 +751,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev,
}
static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev,
- struct iio_trigger *trig)
+ struct iio_trigger *trig)
{
struct bmc150_accel_data *data = iio_priv(indio_dev);
int i;
@@ -904,52 +828,34 @@ static int bmc150_accel_set_watermark(struct iio_dev *indio_dev, unsigned val)
* We must read at least one full frame in one burst, otherwise the rest of the
* frame data is discarded.
*/
-static int bmc150_accel_fifo_transfer(const struct i2c_client *client,
+static int bmc150_accel_fifo_transfer(struct bmc150_accel_data *data,
char *buffer, int samples)
{
int sample_length = 3 * 2;
- u8 reg_fifo_data = BMC150_ACCEL_REG_FIFO_DATA;
- int ret = -EIO;
-
- if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
- struct i2c_msg msg[2] = {
- {
- .addr = client->addr,
- .flags = 0,
- .buf = &reg_fifo_data,
- .len = sizeof(reg_fifo_data),
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .buf = (u8 *)buffer,
- .len = samples * sample_length,
- }
- };
+ int ret;
+ int total_length = samples * sample_length;
+ int i;
+ size_t step = regmap_get_raw_read_max(data->regmap);
- ret = i2c_transfer(client->adapter, msg, 2);
- if (ret != 2)
- ret = -EIO;
- else
- ret = 0;
- } else {
- int i, step = I2C_SMBUS_BLOCK_MAX / sample_length;
-
- for (i = 0; i < samples * sample_length; i += step) {
- ret = i2c_smbus_read_i2c_block_data(client,
- reg_fifo_data, step,
- &buffer[i]);
- if (ret != step) {
- ret = -EIO;
- break;
- }
+ if (!step || step > total_length)
+ step = total_length;
+ else if (step < total_length)
+ step = sample_length;
- ret = 0;
- }
+ /*
+ * Seems we have a bus with size limitation so we have to execute
+ * multiple reads
+ */
+ for (i = 0; i < total_length; i += step) {
+ ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA,
+ &buffer[i], step);
+ if (ret)
+ break;
}
if (ret)
- dev_err(&client->dev, "Error transferring data from fifo\n");
+ dev_err(data->dev, "Error transferring data from fifo in single steps of %zu\n",
+ step);
return ret;
}
@@ -963,14 +869,15 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3];
int64_t tstamp;
uint64_t sample_period;
- ret = i2c_smbus_read_byte_data(data->client,
- BMC150_ACCEL_REG_FIFO_STATUS);
+ unsigned int val;
+
+ ret = regmap_read(data->regmap, BMC150_ACCEL_REG_FIFO_STATUS, &val);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_fifo_status\n");
+ dev_err(data->dev, "Error reading reg_fifo_status\n");
return ret;
}
- count = ret & 0x7F;
+ count = val & 0x7F;
if (!count)
return 0;
@@ -1009,7 +916,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
if (samples && count > samples)
count = samples;
- ret = bmc150_accel_fifo_transfer(data->client, (u8 *)buffer, count);
+ ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count);
if (ret)
return ret;
@@ -1109,17 +1016,9 @@ static const struct iio_chan_spec bmc150_accel_channels[] =
static const struct iio_chan_spec bma280_accel_channels[] =
BMC150_ACCEL_CHANNELS(14);
-enum {
- bmc150,
- bmi055,
- bma255,
- bma250e,
- bma222e,
- bma280,
-};
-
static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
[bmc150] = {
+ .name = "BMC150A",
.chip_id = 0xFA,
.channels = bmc150_accel_channels,
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
@@ -1129,6 +1028,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
},
[bmi055] = {
+ .name = "BMI055A",
.chip_id = 0xFA,
.channels = bmc150_accel_channels,
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
@@ -1138,6 +1038,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
},
[bma255] = {
+ .name = "BMA0255",
.chip_id = 0xFA,
.channels = bmc150_accel_channels,
.num_channels = ARRAY_SIZE(bmc150_accel_channels),
@@ -1147,6 +1048,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
{76590, BMC150_ACCEL_DEF_RANGE_16G} },
},
[bma250e] = {
+ .name = "BMA250E",
.chip_id = 0xF9,
.channels = bma250e_accel_channels,
.num_channels = ARRAY_SIZE(bma250e_accel_channels),
@@ -1156,6 +1058,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
{306457, BMC150_ACCEL_DEF_RANGE_16G} },
},
[bma222e] = {
+ .name = "BMA222E",
.chip_id = 0xF8,
.channels = bma222e_accel_channels,
.num_channels = ARRAY_SIZE(bma222e_accel_channels),
@@ -1165,6 +1068,7 @@ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
{1225831, BMC150_ACCEL_DEF_RANGE_16G} },
},
[bma280] = {
+ .name = "BMA0280",
.chip_id = 0xFB,
.channels = bma280_accel_channels,
.num_channels = ARRAY_SIZE(bma280_accel_channels),
@@ -1183,7 +1087,6 @@ static const struct iio_info bmc150_accel_info = {
.write_event_value = bmc150_accel_write_event,
.write_event_config = bmc150_accel_write_event_config,
.read_event_config = bmc150_accel_read_event_config,
- .validate_trigger = bmc150_accel_validate_trigger,
.driver_module = THIS_MODULE,
};
@@ -1207,22 +1110,24 @@ static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct bmc150_accel_data *data = iio_priv(indio_dev);
int bit, ret, i = 0;
+ unsigned int raw_val;
mutex_lock(&data->mutex);
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
- ret = i2c_smbus_read_word_data(data->client,
- BMC150_ACCEL_AXIS_TO_REG(bit));
+ ret = regmap_bulk_read(data->regmap,
+ BMC150_ACCEL_AXIS_TO_REG(bit), &raw_val,
+ 2);
if (ret < 0) {
mutex_unlock(&data->mutex);
goto err_read;
}
- data->buffer[i++] = ret;
+ data->buffer[i++] = raw_val;
}
mutex_unlock(&data->mutex);
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
- data->timestamp);
+ pf->timestamp);
err_read:
iio_trigger_notify_done(indio_dev->trig);
@@ -1241,13 +1146,12 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
mutex_lock(&data->mutex);
/* clear any latched interrupt */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_RST_LATCH,
- BMC150_ACCEL_INT_MODE_LATCH_INT |
- BMC150_ACCEL_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
+ BMC150_ACCEL_INT_MODE_LATCH_INT |
+ BMC150_ACCEL_INT_MODE_LATCH_RESET);
mutex_unlock(&data->mutex);
if (ret < 0) {
- dev_err(&data->client->dev,
+ dev_err(data->dev,
"Error writing reg_int_rst_latch\n");
return ret;
}
@@ -1256,7 +1160,7 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
}
static int bmc150_accel_trigger_set_state(struct iio_trigger *trig,
- bool state)
+ bool state)
{
struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig);
struct bmc150_accel_data *data = t->data;
@@ -1301,40 +1205,46 @@ static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev)
struct bmc150_accel_data *data = iio_priv(indio_dev);
int dir;
int ret;
+ unsigned int val;
- ret = i2c_smbus_read_byte_data(data->client,
- BMC150_ACCEL_REG_INT_STATUS_2);
+ ret = regmap_read(data->regmap, BMC150_ACCEL_REG_INT_STATUS_2, &val);
if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_status_2\n");
+ dev_err(data->dev, "Error reading reg_int_status_2\n");
return ret;
}
- if (ret & BMC150_ACCEL_ANY_MOTION_BIT_SIGN)
+ if (val & BMC150_ACCEL_ANY_MOTION_BIT_SIGN)
dir = IIO_EV_DIR_FALLING;
else
dir = IIO_EV_DIR_RISING;
- if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X)
- iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
- 0,
- IIO_MOD_X,
- IIO_EV_TYPE_ROC,
- dir),
- data->timestamp);
- if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y)
- iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
- 0,
- IIO_MOD_Y,
- IIO_EV_TYPE_ROC,
- dir),
- data->timestamp);
- if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z)
- iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
- 0,
- IIO_MOD_Z,
- IIO_EV_TYPE_ROC,
- dir),
- data->timestamp);
+ if (val & BMC150_ACCEL_ANY_MOTION_BIT_X)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+
+ if (val & BMC150_ACCEL_ANY_MOTION_BIT_Y)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+
+ if (val & BMC150_ACCEL_ANY_MOTION_BIT_Z)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+
return ret;
}
@@ -1361,12 +1271,12 @@ static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private)
}
if (ack) {
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_RST_LATCH,
- BMC150_ACCEL_INT_MODE_LATCH_INT |
- BMC150_ACCEL_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
+ BMC150_ACCEL_INT_MODE_LATCH_INT |
+ BMC150_ACCEL_INT_MODE_LATCH_RESET);
if (ret)
- dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
+ dev_err(data->dev, "Error writing reg_int_rst_latch\n");
+
ret = IRQ_HANDLED;
} else {
ret = IRQ_NONE;
@@ -1404,46 +1314,6 @@ static irqreturn_t bmc150_accel_irq_handler(int irq, void *private)
return IRQ_NONE;
}
-static const char *bmc150_accel_match_acpi_device(struct device *dev, int *data)
-{
- const struct acpi_device_id *id;
-
- id = acpi_match_device(dev->driver->acpi_match_table, dev);
-
- if (!id)
- return NULL;
-
- *data = (int) id->driver_data;
-
- return dev_name(dev);
-}
-
-static int bmc150_accel_gpio_probe(struct i2c_client *client,
- struct bmc150_accel_data *data)
-{
- struct device *dev;
- struct gpio_desc *gpio;
- int ret;
-
- if (!client)
- return -EINVAL;
-
- dev = &client->dev;
-
- /* data ready gpio interrupt pin */
- gpio = devm_gpiod_get_index(dev, BMC150_ACCEL_GPIO_NAME, 0, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev, "Failed: gpio get index\n");
- return PTR_ERR(gpio);
- }
-
- ret = gpiod_to_irq(gpio);
-
- dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
-
- return ret;
-}
-
static const struct {
int intr;
const char *name;
@@ -1481,7 +1351,7 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) {
struct bmc150_accel_trigger *t = &data->triggers[i];
- t->indio_trig = devm_iio_trigger_alloc(&data->client->dev,
+ t->indio_trig = devm_iio_trigger_alloc(data->dev,
bmc150_accel_triggers[i].name,
indio_dev->name,
indio_dev->id);
@@ -1490,7 +1360,7 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
break;
}
- t->indio_trig->dev.parent = &data->client->dev;
+ t->indio_trig->dev.parent = data->dev;
t->indio_trig->ops = &bmc150_accel_trigger_ops;
t->intr = bmc150_accel_triggers[i].intr;
t->data = data;
@@ -1517,24 +1387,30 @@ static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data)
u8 reg = BMC150_ACCEL_REG_FIFO_CONFIG1;
int ret;
- ret = i2c_smbus_write_byte_data(data->client, reg, data->fifo_mode);
+ ret = regmap_write(data->regmap, reg, data->fifo_mode);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_fifo_config1\n");
+ dev_err(data->dev, "Error writing reg_fifo_config1\n");
return ret;
}
if (!data->fifo_mode)
return 0;
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_FIFO_CONFIG0,
- data->watermark);
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_FIFO_CONFIG0,
+ data->watermark);
if (ret < 0)
- dev_err(&data->client->dev, "Error writing reg_fifo_config0\n");
+ dev_err(data->dev, "Error writing reg_fifo_config0\n");
return ret;
}
+static int bmc150_accel_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
+ return bmc150_accel_set_power_state(data, true);
+}
+
static int bmc150_accel_buffer_postenable(struct iio_dev *indio_dev)
{
struct bmc150_accel_data *data = iio_priv(indio_dev);
@@ -1591,37 +1467,102 @@ out:
return 0;
}
+static int bmc150_accel_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
+ return bmc150_accel_set_power_state(data, false);
+}
+
static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = {
+ .preenable = bmc150_accel_buffer_preenable,
.postenable = bmc150_accel_buffer_postenable,
.predisable = bmc150_accel_buffer_predisable,
+ .postdisable = bmc150_accel_buffer_postdisable,
};
-static int bmc150_accel_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
+{
+ int ret, i;
+ unsigned int val;
+
+ ret = regmap_read(data->regmap, BMC150_ACCEL_REG_CHIP_ID, &val);
+ if (ret < 0) {
+ dev_err(data->dev,
+ "Error: Reading chip id\n");
+ return ret;
+ }
+
+ dev_dbg(data->dev, "Chip Id %x\n", val);
+ for (i = 0; i < ARRAY_SIZE(bmc150_accel_chip_info_tbl); i++) {
+ if (bmc150_accel_chip_info_tbl[i].chip_id == val) {
+ data->chip_info = &bmc150_accel_chip_info_tbl[i];
+ break;
+ }
+ }
+
+ if (!data->chip_info) {
+ dev_err(data->dev, "Invalid chip %x\n", val);
+ return -ENODEV;
+ }
+
+ ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Set Bandwidth */
+ ret = bmc150_accel_set_bw(data, BMC150_ACCEL_DEF_BW, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Set Default Range */
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_PMU_RANGE,
+ BMC150_ACCEL_DEF_RANGE_4G);
+ if (ret < 0) {
+ dev_err(data->dev,
+ "Error writing reg_pmu_range\n");
+ return ret;
+ }
+
+ data->range = BMC150_ACCEL_DEF_RANGE_4G;
+
+ /* Set default slope duration and thresholds */
+ data->slope_thres = BMC150_ACCEL_DEF_SLOPE_THRESHOLD;
+ data->slope_dur = BMC150_ACCEL_DEF_SLOPE_DURATION;
+ ret = bmc150_accel_update_slope(data);
+ if (ret < 0)
+ return ret;
+
+ /* Set default as latched interrupts */
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
+ BMC150_ACCEL_INT_MODE_LATCH_INT |
+ BMC150_ACCEL_INT_MODE_LATCH_RESET);
+ if (ret < 0) {
+ dev_err(data->dev,
+ "Error writing reg_int_rst_latch\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name, bool block_supported)
{
struct bmc150_accel_data *data;
struct iio_dev *indio_dev;
int ret;
- const char *name = NULL;
- int chip_id = 0;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
- i2c_set_clientdata(client, indio_dev);
- data->client = client;
-
- if (id) {
- name = id->name;
- chip_id = id->driver_data;
- }
-
- if (ACPI_HANDLE(&client->dev))
- name = bmc150_accel_match_acpi_device(&client->dev, &chip_id);
+ dev_set_drvdata(dev, indio_dev);
+ data->dev = dev;
+ data->irq = irq;
- data->chip_info = &bmc150_accel_chip_info_tbl[chip_id];
+ data->regmap = regmap;
ret = bmc150_accel_chip_init(data);
if (ret < 0)
@@ -1629,26 +1570,32 @@ static int bmc150_accel_probe(struct i2c_client *client,
mutex_init(&data->mutex);
- indio_dev->dev.parent = &client->dev;
+ indio_dev->dev.parent = dev;
indio_dev->channels = data->chip_info->channels;
indio_dev->num_channels = data->chip_info->num_channels;
- indio_dev->name = name;
+ indio_dev->name = name ? name : data->chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &bmc150_accel_info;
- if (client->irq < 0)
- client->irq = bmc150_accel_gpio_probe(client, data);
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ bmc150_accel_trigger_handler,
+ &bmc150_accel_buffer_ops);
+ if (ret < 0) {
+ dev_err(data->dev, "Failed: iio triggered buffer setup\n");
+ return ret;
+ }
- if (client->irq >= 0) {
+ if (data->irq > 0) {
ret = devm_request_threaded_irq(
- &client->dev, client->irq,
+ data->dev, data->irq,
bmc150_accel_irq_handler,
bmc150_accel_irq_thread_handler,
IRQF_TRIGGER_RISING,
BMC150_ACCEL_IRQ_NAME,
indio_dev);
if (ret)
- return ret;
+ goto err_buffer_cleanup;
/*
* Set latched mode interrupt. While certain interrupts are
@@ -1656,33 +1603,20 @@ static int bmc150_accel_probe(struct i2c_client *client,
* want to use latch mode when we can to prevent interrupt
* flooding.
*/
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_RST_LATCH,
- BMC150_ACCEL_INT_MODE_LATCH_RESET);
+ ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
+ BMC150_ACCEL_INT_MODE_LATCH_RESET);
if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
- return ret;
+ dev_err(data->dev, "Error writing reg_int_rst_latch\n");
+ goto err_buffer_cleanup;
}
bmc150_accel_interrupts_setup(indio_dev, data);
ret = bmc150_accel_triggers_setup(indio_dev, data);
if (ret)
- return ret;
-
- ret = iio_triggered_buffer_setup(indio_dev,
- &iio_pollfunc_store_time,
- bmc150_accel_trigger_handler,
- &bmc150_accel_buffer_ops);
- if (ret < 0) {
- dev_err(&client->dev,
- "Failed: iio triggered buffer setup\n");
- goto err_trigger_unregister;
- }
+ goto err_buffer_cleanup;
- if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
- i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ if (block_supported) {
indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
indio_dev->info = &bmc150_accel_info_fifo;
indio_dev->buffer->attrs = bmc150_accel_fifo_attributes;
@@ -1691,56 +1625,58 @@ static int bmc150_accel_probe(struct i2c_client *client,
ret = iio_device_register(indio_dev);
if (ret < 0) {
- dev_err(&client->dev, "Unable to register iio device\n");
- goto err_buffer_cleanup;
+ dev_err(dev, "Unable to register iio device\n");
+ goto err_trigger_unregister;
}
- ret = pm_runtime_set_active(&client->dev);
+ ret = pm_runtime_set_active(dev);
if (ret)
goto err_iio_unregister;
- pm_runtime_enable(&client->dev);
- pm_runtime_set_autosuspend_delay(&client->dev,
- BMC150_AUTO_SUSPEND_DELAY_MS);
- pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, BMC150_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
return 0;
err_iio_unregister:
iio_device_unregister(indio_dev);
-err_buffer_cleanup:
- if (indio_dev->pollfunc)
- iio_triggered_buffer_cleanup(indio_dev);
err_trigger_unregister:
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
+EXPORT_SYMBOL_GPL(bmc150_accel_core_probe);
-static int bmc150_accel_remove(struct i2c_client *client)
+int bmc150_accel_core_remove(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmc150_accel_data *data = iio_priv(indio_dev);
- pm_runtime_disable(&client->dev);
- pm_runtime_set_suspended(&client->dev);
- pm_runtime_put_noidle(&client->dev);
+ pm_runtime_disable(data->dev);
+ pm_runtime_set_suspended(data->dev);
+ pm_runtime_put_noidle(data->dev);
iio_device_unregister(indio_dev);
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
+ iio_triggered_buffer_cleanup(indio_dev);
+
mutex_lock(&data->mutex);
bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_DEEP_SUSPEND, 0);
mutex_unlock(&data->mutex);
return 0;
}
+EXPORT_SYMBOL_GPL(bmc150_accel_core_remove);
#ifdef CONFIG_PM_SLEEP
static int bmc150_accel_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmc150_accel_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
@@ -1752,7 +1688,7 @@ static int bmc150_accel_suspend(struct device *dev)
static int bmc150_accel_resume(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmc150_accel_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
@@ -1768,11 +1704,11 @@ static int bmc150_accel_resume(struct device *dev)
#ifdef CONFIG_PM
static int bmc150_accel_runtime_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmc150_accel_data *data = iio_priv(indio_dev);
int ret;
- dev_dbg(&data->client->dev, __func__);
+ dev_dbg(data->dev, __func__);
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
if (ret < 0)
return -EAGAIN;
@@ -1782,12 +1718,12 @@ static int bmc150_accel_runtime_suspend(struct device *dev)
static int bmc150_accel_runtime_resume(struct device *dev)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmc150_accel_data *data = iio_priv(indio_dev);
int ret;
int sleep_val;
- dev_dbg(&data->client->dev, __func__);
+ dev_dbg(data->dev, __func__);
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
if (ret < 0)
@@ -1806,47 +1742,12 @@ static int bmc150_accel_runtime_resume(struct device *dev)
}
#endif
-static const struct dev_pm_ops bmc150_accel_pm_ops = {
+const struct dev_pm_ops bmc150_accel_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(bmc150_accel_suspend, bmc150_accel_resume)
SET_RUNTIME_PM_OPS(bmc150_accel_runtime_suspend,
bmc150_accel_runtime_resume, NULL)
};
-
-static const struct acpi_device_id bmc150_accel_acpi_match[] = {
- {"BSBA0150", bmc150},
- {"BMC150A", bmc150},
- {"BMI055A", bmi055},
- {"BMA0255", bma255},
- {"BMA250E", bma250e},
- {"BMA222E", bma222e},
- {"BMA0280", bma280},
- { },
-};
-MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
-
-static const struct i2c_device_id bmc150_accel_id[] = {
- {"bmc150_accel", bmc150},
- {"bmi055_accel", bmi055},
- {"bma255", bma255},
- {"bma250e", bma250e},
- {"bma222e", bma222e},
- {"bma280", bma280},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, bmc150_accel_id);
-
-static struct i2c_driver bmc150_accel_driver = {
- .driver = {
- .name = BMC150_ACCEL_DRV_NAME,
- .acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
- .pm = &bmc150_accel_pm_ops,
- },
- .probe = bmc150_accel_probe,
- .remove = bmc150_accel_remove,
- .id_table = bmc150_accel_id,
-};
-module_i2c_driver(bmc150_accel_driver);
+EXPORT_SYMBOL_GPL(bmc150_accel_pm_ops);
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
MODULE_LICENSE("GPL v2");
diff --git a/kernel/drivers/iio/accel/bmc150-accel-i2c.c b/kernel/drivers/iio/accel/bmc150-accel-i2c.c
new file mode 100644
index 000000000..b41404ba3
--- /dev/null
+++ b/kernel/drivers/iio/accel/bmc150-accel-i2c.c
@@ -0,0 +1,102 @@
+/*
+ * 3-axis accelerometer driver supporting following I2C Bosch-Sensortec chips:
+ * - BMC150
+ * - BMI055
+ * - BMA255
+ * - BMA250E
+ * - BMA222E
+ * - BMA280
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+
+#include "bmc150-accel.h"
+
+static const struct regmap_config bmc150_i2c_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int bmc150_accel_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ const char *name = NULL;
+ bool block_supported =
+ i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK);
+
+ regmap = devm_regmap_init_i2c(client, &bmc150_i2c_regmap_conf);
+ 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_accel_core_probe(&client->dev, regmap, client->irq, name,
+ block_supported);
+}
+
+static int bmc150_accel_remove(struct i2c_client *client)
+{
+ return bmc150_accel_core_remove(&client->dev);
+}
+
+static const struct acpi_device_id bmc150_accel_acpi_match[] = {
+ {"BSBA0150", bmc150},
+ {"BMC150A", bmc150},
+ {"BMI055A", bmi055},
+ {"BMA0255", bma255},
+ {"BMA250E", bma250e},
+ {"BMA222E", bma222e},
+ {"BMA0280", bma280},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
+
+static const struct i2c_device_id bmc150_accel_id[] = {
+ {"bmc150_accel", bmc150},
+ {"bmi055_accel", bmi055},
+ {"bma255", bma255},
+ {"bma250e", bma250e},
+ {"bma222e", bma222e},
+ {"bma280", bma280},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bmc150_accel_id);
+
+static struct i2c_driver bmc150_accel_driver = {
+ .driver = {
+ .name = "bmc150_accel_i2c",
+ .acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
+ .pm = &bmc150_accel_pm_ops,
+ },
+ .probe = bmc150_accel_probe,
+ .remove = bmc150_accel_remove,
+ .id_table = bmc150_accel_id,
+};
+module_i2c_driver(bmc150_accel_driver);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMC150 I2C accelerometer driver");
diff --git a/kernel/drivers/iio/accel/bmc150-accel-spi.c b/kernel/drivers/iio/accel/bmc150-accel-spi.c
new file mode 100644
index 000000000..16b66f2a7
--- /dev/null
+++ b/kernel/drivers/iio/accel/bmc150-accel-spi.c
@@ -0,0 +1,91 @@
+/*
+ * 3-axis accelerometer driver supporting SPI Bosch-Sensortec accelerometer chip
+ * Copyright © 2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "bmc150-accel.h"
+
+static const struct regmap_config bmc150_spi_regmap_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x3f,
+};
+
+static int bmc150_accel_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_spi_regmap_conf);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Failed to initialize spi regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ return bmc150_accel_core_probe(&spi->dev, regmap, spi->irq, id->name,
+ true);
+}
+
+static int bmc150_accel_remove(struct spi_device *spi)
+{
+ return bmc150_accel_core_remove(&spi->dev);
+}
+
+static const struct acpi_device_id bmc150_accel_acpi_match[] = {
+ {"BSBA0150", bmc150},
+ {"BMC150A", bmc150},
+ {"BMI055A", bmi055},
+ {"BMA0255", bma255},
+ {"BMA250E", bma250e},
+ {"BMA222E", bma222e},
+ {"BMA0280", bma280},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
+
+static const struct spi_device_id bmc150_accel_id[] = {
+ {"bmc150_accel", bmc150},
+ {"bmi055_accel", bmi055},
+ {"bma255", bma255},
+ {"bma250e", bma250e},
+ {"bma222e", bma222e},
+ {"bma280", bma280},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, bmc150_accel_id);
+
+static struct spi_driver bmc150_accel_driver = {
+ .driver = {
+ .name = "bmc150_accel_spi",
+ .acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
+ .pm = &bmc150_accel_pm_ops,
+ },
+ .probe = bmc150_accel_probe,
+ .remove = bmc150_accel_remove,
+ .id_table = bmc150_accel_id,
+};
+module_spi_driver(bmc150_accel_driver);
+
+MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMC150 SPI accelerometer driver");
diff --git a/kernel/drivers/iio/accel/bmc150-accel.h b/kernel/drivers/iio/accel/bmc150-accel.h
new file mode 100644
index 000000000..ba0335987
--- /dev/null
+++ b/kernel/drivers/iio/accel/bmc150-accel.h
@@ -0,0 +1,20 @@
+#ifndef _BMC150_ACCEL_H_
+#define _BMC150_ACCEL_H_
+
+struct regmap;
+
+enum {
+ bmc150,
+ bmi055,
+ bma255,
+ bma250e,
+ bma222e,
+ bma280,
+};
+
+int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name, bool block_supported);
+int bmc150_accel_core_remove(struct device *dev);
+extern const struct dev_pm_ops bmc150_accel_pm_ops;
+
+#endif /* _BMC150_ACCEL_H_ */
diff --git a/kernel/drivers/iio/accel/hid-sensor-accel-3d.c b/kernel/drivers/iio/accel/hid-sensor-accel-3d.c
index 2b4fad699..ab1e238d5 100644
--- a/kernel/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/kernel/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -299,7 +299,6 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct accel_3d_state *accel_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct accel_3d_state));
@@ -320,21 +319,21 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
return ret;
}
- channels = kmemdup(accel_3d_channels, sizeof(accel_3d_channels),
- GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = kmemdup(accel_3d_channels,
+ sizeof(accel_3d_channels), GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = accel_3d_parse_report(pdev, hsdev, channels,
- HID_USAGE_SENSOR_ACCEL_3D, accel_state);
+ ret = accel_3d_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_ACCEL_3D, accel_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
goto error_free_dev_mem;
}
- indio_dev->channels = channels;
indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &accel_3d_info;
@@ -400,7 +399,7 @@ static int hid_accel_3d_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_accel_3d_ids[] = {
+static const struct platform_device_id hid_accel_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200073",
diff --git a/kernel/drivers/iio/accel/kxcjk-1013.c b/kernel/drivers/iio/accel/kxcjk-1013.c
index 5b7a860df..18c1b0668 100644
--- a/kernel/drivers/iio/accel/kxcjk-1013.c
+++ b/kernel/drivers/iio/accel/kxcjk-1013.c
@@ -658,10 +658,8 @@ static int kxcjk1013_set_scale(struct kxcjk1013_data *data, int val)
int ret, i;
enum kxcjk1013_mode store_mode;
-
for (i = 0; i < ARRAY_SIZE(KXCJK1013_scale_table); ++i) {
if (KXCJK1013_scale_table[i].scale == val) {
-
ret = kxcjk1013_get_mode(data, &store_mode);
if (ret < 0)
return ret;
@@ -820,7 +818,6 @@ static int kxcjk1013_read_event_config(struct iio_dev *indio_dev,
enum iio_event_type type,
enum iio_event_direction dir)
{
-
struct kxcjk1013_data *data = iio_priv(indio_dev);
return data->ev_enable_state;
@@ -875,15 +872,18 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
return 0;
}
-static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev,
- struct iio_trigger *trig)
+static int kxcjk1013_buffer_preenable(struct iio_dev *indio_dev)
{
struct kxcjk1013_data *data = iio_priv(indio_dev);
- if (data->dready_trig != trig && data->motion_trig != trig)
- return -EINVAL;
+ return kxcjk1013_set_power_state(data, true);
+}
- return 0;
+static int kxcjk1013_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct kxcjk1013_data *data = iio_priv(indio_dev);
+
+ return kxcjk1013_set_power_state(data, false);
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
@@ -935,6 +935,13 @@ static const struct iio_chan_spec kxcjk1013_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
+static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = {
+ .preenable = kxcjk1013_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .postdisable = kxcjk1013_buffer_postdisable,
+ .predisable = iio_triggered_buffer_predisable,
+};
+
static const struct iio_info kxcjk1013_info = {
.attrs = &kxcjk1013_attrs_group,
.read_raw = kxcjk1013_read_raw,
@@ -943,7 +950,6 @@ static const struct iio_info kxcjk1013_info = {
.write_event_value = kxcjk1013_write_event,
.write_event_config = kxcjk1013_write_event_config,
.read_event_config = kxcjk1013_read_event_config,
- .validate_trigger = kxcjk1013_validate_trigger,
.driver_module = THIS_MODULE,
};
@@ -1147,41 +1153,15 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
+
if (strcmp(id->id, "SMO8500") == 0)
*is_smo8500_device = true;
+
*chipset = (enum kx_chipset)id->driver_data;
return dev_name(dev);
}
-static int kxcjk1013_gpio_probe(struct i2c_client *client,
- struct kxcjk1013_data *data)
-{
- struct device *dev;
- struct gpio_desc *gpio;
- int ret;
-
- if (!client)
- return -EINVAL;
- if (data->is_smo8500_device)
- return -ENOTSUPP;
-
- dev = &client->dev;
-
- /* data ready gpio interrupt pin */
- gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev, "acpi gpio get index failed\n");
- return PTR_ERR(gpio);
- }
-
- ret = gpiod_to_irq(gpio);
-
- dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
-
- return ret;
-}
-
static int kxcjk1013_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1228,10 +1208,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &kxcjk1013_info;
- if (client->irq < 0)
- client->irq = kxcjk1013_gpio_probe(client, data);
-
- if (client->irq >= 0) {
+ if (client->irq > 0 && !data->is_smo8500_device) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
kxcjk1013_data_rdy_trig_poll,
kxcjk1013_event_handler,
@@ -1276,16 +1253,15 @@ static int kxcjk1013_probe(struct i2c_client *client,
data->motion_trig = NULL;
goto err_trigger_unregister;
}
+ }
- ret = iio_triggered_buffer_setup(indio_dev,
- &iio_pollfunc_store_time,
- kxcjk1013_trigger_handler,
- NULL);
- if (ret < 0) {
- dev_err(&client->dev,
- "iio triggered buffer setup failed\n");
- goto err_trigger_unregister;
- }
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ kxcjk1013_trigger_handler,
+ &kxcjk1013_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ goto err_trigger_unregister;
}
ret = iio_device_register(indio_dev);
diff --git a/kernel/drivers/iio/accel/kxsd9.c b/kernel/drivers/iio/accel/kxsd9.c
index 98ba761cb..923f56598 100644
--- a/kernel/drivers/iio/accel/kxsd9.c
+++ b/kernel/drivers/iio/accel/kxsd9.c
@@ -263,7 +263,6 @@ MODULE_DEVICE_TABLE(spi, kxsd9_id);
static struct spi_driver kxsd9_driver = {
.driver = {
.name = "kxsd9",
- .owner = THIS_MODULE,
},
.probe = kxsd9_probe,
.remove = kxsd9_remove,
diff --git a/kernel/drivers/iio/accel/mma8452.c b/kernel/drivers/iio/accel/mma8452.c
index 5b8065788..1eccc2dcf 100644
--- a/kernel/drivers/iio/accel/mma8452.c
+++ b/kernel/drivers/iio/accel/mma8452.c
@@ -1,6 +1,12 @@
/*
- * mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer
+ * mma8452.c - Support for following Freescale 3-axis accelerometers:
*
+ * MMA8452Q (12 bit)
+ * MMA8453Q (10 bit)
+ * MMA8652FC (12 bit)
+ * MMA8653FC (10 bit)
+ *
+ * Copyright 2015 Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
* This file is subject to the terms and conditions of version 2 of
@@ -9,49 +15,132 @@
*
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
*
- * TODO: interrupt, thresholding, orientation / freefall events, autosleep
+ * TODO: orientation / freefall events, autosleep
*/
#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/trigger.h>
+#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/events.h>
#include <linux/delay.h>
-
-#define MMA8452_STATUS 0x00
-#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
-#define MMA8452_OUT_Y 0x03
-#define MMA8452_OUT_Z 0x05
-#define MMA8452_WHO_AM_I 0x0d
-#define MMA8452_DATA_CFG 0x0e
-#define MMA8452_OFF_X 0x2f
-#define MMA8452_OFF_Y 0x30
-#define MMA8452_OFF_Z 0x31
-#define MMA8452_CTRL_REG1 0x2a
-#define MMA8452_CTRL_REG2 0x2b
-
-#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
-
-#define MMA8452_CTRL_DR_MASK (BIT(5) | BIT(4) | BIT(3))
-#define MMA8452_CTRL_DR_SHIFT 3
-#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */
-#define MMA8452_CTRL_ACTIVE BIT(0)
-
-#define MMA8452_DATA_CFG_FS_MASK (BIT(1) | BIT(0))
-#define MMA8452_DATA_CFG_FS_2G 0
-#define MMA8452_DATA_CFG_FS_4G 1
-#define MMA8452_DATA_CFG_FS_8G 2
-
-#define MMA8452_DEVICE_ID 0x2a
+#include <linux/of_device.h>
+
+#define MMA8452_STATUS 0x00
+#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
+#define MMA8452_OUT_X 0x01 /* MSB first */
+#define MMA8452_OUT_Y 0x03
+#define MMA8452_OUT_Z 0x05
+#define MMA8452_INT_SRC 0x0c
+#define MMA8452_WHO_AM_I 0x0d
+#define MMA8452_DATA_CFG 0x0e
+#define MMA8452_DATA_CFG_FS_MASK GENMASK(1, 0)
+#define MMA8452_DATA_CFG_FS_2G 0
+#define MMA8452_DATA_CFG_FS_4G 1
+#define MMA8452_DATA_CFG_FS_8G 2
+#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
+#define MMA8452_HP_FILTER_CUTOFF 0x0f
+#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK GENMASK(1, 0)
+#define MMA8452_FF_MT_CFG 0x15
+#define MMA8452_FF_MT_CFG_OAE BIT(6)
+#define MMA8452_FF_MT_CFG_ELE BIT(7)
+#define MMA8452_FF_MT_SRC 0x16
+#define MMA8452_FF_MT_SRC_XHE BIT(1)
+#define MMA8452_FF_MT_SRC_YHE BIT(3)
+#define MMA8452_FF_MT_SRC_ZHE BIT(5)
+#define MMA8452_FF_MT_THS 0x17
+#define MMA8452_FF_MT_THS_MASK 0x7f
+#define MMA8452_FF_MT_COUNT 0x18
+#define MMA8452_TRANSIENT_CFG 0x1d
+#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
+#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
+#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
+#define MMA8452_TRANSIENT_SRC 0x1e
+#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
+#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3)
+#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5)
+#define MMA8452_TRANSIENT_THS 0x1f
+#define MMA8452_TRANSIENT_THS_MASK GENMASK(6, 0)
+#define MMA8452_TRANSIENT_COUNT 0x20
+#define MMA8452_CTRL_REG1 0x2a
+#define MMA8452_CTRL_ACTIVE BIT(0)
+#define MMA8452_CTRL_DR_MASK GENMASK(5, 3)
+#define MMA8452_CTRL_DR_SHIFT 3
+#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */
+#define MMA8452_CTRL_REG2 0x2b
+#define MMA8452_CTRL_REG2_RST BIT(6)
+#define MMA8452_CTRL_REG4 0x2d
+#define MMA8452_CTRL_REG5 0x2e
+#define MMA8452_OFF_X 0x2f
+#define MMA8452_OFF_Y 0x30
+#define MMA8452_OFF_Z 0x31
+
+#define MMA8452_MAX_REG 0x31
+
+#define MMA8452_INT_DRDY BIT(0)
+#define MMA8452_INT_FF_MT BIT(2)
+#define MMA8452_INT_TRANS BIT(5)
+
+#define MMA8452_DEVICE_ID 0x2a
+#define MMA8453_DEVICE_ID 0x3a
+#define MMA8652_DEVICE_ID 0x4a
+#define MMA8653_DEVICE_ID 0x5a
struct mma8452_data {
struct i2c_client *client;
struct mutex lock;
u8 ctrl_reg1;
u8 data_cfg;
+ const struct mma_chip_info *chip_info;
+};
+
+/**
+ * struct mma_chip_info - chip specific data for Freescale's accelerometers
+ * @chip_id: WHO_AM_I register's value
+ * @channels: struct iio_chan_spec matching the device's
+ * capabilities
+ * @num_channels: number of channels
+ * @mma_scales: scale factors for converting register values
+ * to m/s^2; 3 modes: 2g, 4g, 8g; 2 integers
+ * per mode: m/s^2 and micro m/s^2
+ * @ev_cfg: event config register address
+ * @ev_cfg_ele: latch bit in event config register
+ * @ev_cfg_chan_shift: number of the bit to enable events in X
+ * direction; in event config register
+ * @ev_src: event source register address
+ * @ev_src_xe: bit in event source register that indicates
+ * an event in X direction
+ * @ev_src_ye: bit in event source register that indicates
+ * an event in Y direction
+ * @ev_src_ze: bit in event source register that indicates
+ * an event in Z direction
+ * @ev_ths: event threshold register address
+ * @ev_ths_mask: mask for the threshold value
+ * @ev_count: event count (period) register address
+ *
+ * Since not all chips supported by the driver support comparing high pass
+ * filtered data for events (interrupts), different interrupt sources are
+ * used for different chips and the relevant registers are included here.
+ */
+struct mma_chip_info {
+ u8 chip_id;
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ const int mma_scales[3][2];
+ u8 ev_cfg;
+ u8 ev_cfg_ele;
+ u8 ev_cfg_chan_shift;
+ u8 ev_src;
+ u8 ev_src_xe;
+ u8 ev_src_ye;
+ u8 ev_src_ze;
+ u8 ev_ths;
+ u8 ev_ths_mask;
+ u8 ev_count;
};
static int mma8452_drdy(struct mma8452_data *data)
@@ -65,30 +154,34 @@ static int mma8452_drdy(struct mma8452_data *data)
return ret;
if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
return 0;
+
msleep(20);
}
dev_err(&data->client->dev, "data not ready\n");
+
return -EIO;
}
static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
{
int ret = mma8452_drdy(data);
+
if (ret < 0)
return ret;
- return i2c_smbus_read_i2c_block_data(data->client,
- MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
+
+ return i2c_smbus_read_i2c_block_data(data->client, MMA8452_OUT_X,
+ 3 * sizeof(__be16), (u8 *)buf);
}
-static ssize_t mma8452_show_int_plus_micros(char *buf,
- const int (*vals)[2], int n)
+static ssize_t mma8452_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]);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+ vals[n][0], vals[n][1]);
/* replace trailing space by newline */
buf[len - 1] = '\n';
@@ -97,7 +190,7 @@ static ssize_t mma8452_show_int_plus_micros(char *buf,
}
static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
- int val, int val2)
+ int val, int val2)
{
while (n-- > 0)
if (val == vals[n][0] && val2 == vals[n][1])
@@ -106,51 +199,115 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
return -EINVAL;
}
+static int mma8452_get_odr_index(struct mma8452_data *data)
+{
+ return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
+ MMA8452_CTRL_DR_SHIFT;
+}
+
static const int mma8452_samp_freq[8][2] = {
{800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
{6, 250000}, {1, 560000}
};
-/*
- * Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
- * The userspace interface uses m/s^2 and we declare micro units
- * So scale factor is given by:
- * g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
- */
-static const int mma8452_scales[3][2] = {
- {0, 9577}, {0, 19154}, {0, 38307}
+/* Datasheet table 35 (step time vs sample frequency) */
+static const int mma8452_transient_time_step_us[8] = {
+ 1250,
+ 2500,
+ 5000,
+ 10000,
+ 20000,
+ 20000,
+ 20000,
+ 20000
+};
+
+/* Datasheet table 18 (normal mode) */
+static const int mma8452_hp_filter_cutoff[8][4][2] = {
+ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */
+ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */
+ { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */
+ { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, /* 100 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 50 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */
};
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
return mma8452_show_int_plus_micros(buf, mma8452_samp_freq,
- ARRAY_SIZE(mma8452_samp_freq));
+ ARRAY_SIZE(mma8452_samp_freq));
}
static ssize_t mma8452_show_scale_avail(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
- return mma8452_show_int_plus_micros(buf, mma8452_scales,
- ARRAY_SIZE(mma8452_scales));
+ struct mma8452_data *data = iio_priv(i2c_get_clientdata(
+ to_i2c_client(dev)));
+
+ return mma8452_show_int_plus_micros(buf, data->chip_info->mma_scales,
+ ARRAY_SIZE(data->chip_info->mma_scales));
+}
+
+static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct mma8452_data *data = iio_priv(indio_dev);
+ int i = mma8452_get_odr_index(data);
+
+ return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i],
+ ARRAY_SIZE(mma8452_hp_filter_cutoff[0]));
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
- mma8452_show_scale_avail, NULL, 0);
+ mma8452_show_scale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
+ S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0);
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
- int val, int val2)
+ int val, int val2)
{
return mma8452_get_int_plus_micros_index(mma8452_samp_freq,
- ARRAY_SIZE(mma8452_samp_freq), val, val2);
+ ARRAY_SIZE(mma8452_samp_freq),
+ val, val2);
}
-static int mma8452_get_scale_index(struct mma8452_data *data,
- int val, int val2)
+static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2)
{
- return mma8452_get_int_plus_micros_index(mma8452_scales,
- ARRAY_SIZE(mma8452_scales), val, val2);
+ return mma8452_get_int_plus_micros_index(data->chip_info->mma_scales,
+ ARRAY_SIZE(data->chip_info->mma_scales), val, val2);
+}
+
+static int mma8452_get_hp_filter_index(struct mma8452_data *data,
+ int val, int val2)
+{
+ int i = mma8452_get_odr_index(data);
+
+ return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i],
+ ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2);
+}
+
+static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
+{
+ int i, ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF);
+ if (ret < 0)
+ return ret;
+
+ i = mma8452_get_odr_index(data);
+ ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
+ *hz = mma8452_hp_filter_cutoff[i][ret][0];
+ *uHz = mma8452_hp_filter_cutoff[i][ret][1];
+
+ return 0;
}
static int mma8452_read_raw(struct iio_dev *indio_dev,
@@ -171,41 +328,59 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&data->lock);
if (ret < 0)
return ret;
- *val = sign_extend32(
- be16_to_cpu(buffer[chan->scan_index]) >> 4, 11);
+
+ *val = sign_extend32(be16_to_cpu(
+ buffer[chan->scan_index]) >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
+
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
- *val = mma8452_scales[i][0];
- *val2 = mma8452_scales[i][1];
+ *val = data->chip_info->mma_scales[i][0];
+ *val2 = data->chip_info->mma_scales[i][1];
+
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
- i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
- MMA8452_CTRL_DR_SHIFT;
+ i = mma8452_get_odr_index(data);
*val = mma8452_samp_freq[i][0];
*val2 = mma8452_samp_freq[i][1];
+
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
- ret = i2c_smbus_read_byte_data(data->client, MMA8452_OFF_X +
- chan->scan_index);
+ ret = i2c_smbus_read_byte_data(data->client,
+ MMA8452_OFF_X + chan->scan_index);
if (ret < 0)
return ret;
+
*val = sign_extend32(ret, 7);
+
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+ if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
+ ret = mma8452_read_hp_filter(data, val, val2);
+ if (ret < 0)
+ return ret;
+ } else {
+ *val = 0;
+ *val2 = 0;
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
}
+
return -EINVAL;
}
static int mma8452_standby(struct mma8452_data *data)
{
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
- data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
+ data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
}
static int mma8452_active(struct mma8452_data *data)
{
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
- data->ctrl_reg1);
+ data->ctrl_reg1);
}
static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
@@ -230,15 +405,36 @@ static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
ret = 0;
fail:
mutex_unlock(&data->lock);
+
return ret;
}
+static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
+ int val, int val2)
+{
+ int i, reg;
+
+ i = mma8452_get_hp_filter_index(data, val, val2);
+ if (i < 0)
+ return i;
+
+ reg = i2c_smbus_read_byte_data(data->client,
+ MMA8452_HP_FILTER_CUTOFF);
+ if (reg < 0)
+ return reg;
+
+ reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
+ reg |= i;
+
+ return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg);
+}
+
static int mma8452_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct mma8452_data *data = iio_priv(indio_dev);
- int i;
+ int i, ret;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
@@ -247,30 +443,258 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SAMP_FREQ:
i = mma8452_get_samp_freq_index(data, val, val2);
if (i < 0)
- return -EINVAL;
+ return i;
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
+
return mma8452_change_config(data, MMA8452_CTRL_REG1,
- data->ctrl_reg1);
+ data->ctrl_reg1);
case IIO_CHAN_INFO_SCALE:
i = mma8452_get_scale_index(data, val, val2);
if (i < 0)
- return -EINVAL;
+ return i;
+
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
data->data_cfg |= i;
+
return mma8452_change_config(data, MMA8452_DATA_CFG,
- data->data_cfg);
+ data->data_cfg);
case IIO_CHAN_INFO_CALIBBIAS:
if (val < -128 || val > 127)
return -EINVAL;
- return mma8452_change_config(data, MMA8452_OFF_X +
- chan->scan_index, val);
+
+ return mma8452_change_config(data,
+ MMA8452_OFF_X + chan->scan_index,
+ val);
+
+ case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+ if (val == 0 && val2 == 0) {
+ data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK;
+ } else {
+ data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
+ ret = mma8452_set_hp_filter_frequency(data, val, val2);
+ if (ret < 0)
+ return ret;
+ }
+
+ return mma8452_change_config(data, MMA8452_DATA_CFG,
+ data->data_cfg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma8452_read_thresh(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 mma8452_data *data = iio_priv(indio_dev);
+ int ret, us;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = i2c_smbus_read_byte_data(data->client,
+ data->chip_info->ev_ths);
+ if (ret < 0)
+ return ret;
+
+ *val = ret & data->chip_info->ev_ths_mask;
+
+ return IIO_VAL_INT;
+
+ case IIO_EV_INFO_PERIOD:
+ ret = i2c_smbus_read_byte_data(data->client,
+ data->chip_info->ev_count);
+ if (ret < 0)
+ return ret;
+
+ us = ret * mma8452_transient_time_step_us[
+ mma8452_get_odr_index(data)];
+ *val = us / USEC_PER_SEC;
+ *val2 = us % USEC_PER_SEC;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
+ ret = i2c_smbus_read_byte_data(data->client,
+ MMA8452_TRANSIENT_CFG);
+ if (ret < 0)
+ return ret;
+
+ if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) {
+ *val = 0;
+ *val2 = 0;
+ } else {
+ ret = mma8452_read_hp_filter(data, val, val2);
+ if (ret < 0)
+ return ret;
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma8452_write_thresh(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 mma8452_data *data = iio_priv(indio_dev);
+ int ret, reg, steps;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val < 0 || val > MMA8452_TRANSIENT_THS_MASK)
+ return -EINVAL;
+
+ return mma8452_change_config(data, data->chip_info->ev_ths,
+ val);
+
+ case IIO_EV_INFO_PERIOD:
+ steps = (val * USEC_PER_SEC + val2) /
+ mma8452_transient_time_step_us[
+ mma8452_get_odr_index(data)];
+
+ if (steps < 0 || steps > 0xff)
+ return -EINVAL;
+
+ return mma8452_change_config(data, data->chip_info->ev_count,
+ steps);
+
+ case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
+ reg = i2c_smbus_read_byte_data(data->client,
+ MMA8452_TRANSIENT_CFG);
+ if (reg < 0)
+ return reg;
+
+ if (val == 0 && val2 == 0) {
+ reg |= MMA8452_TRANSIENT_CFG_HPF_BYP;
+ } else {
+ reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP;
+ ret = mma8452_set_hp_filter_frequency(data, val, val2);
+ if (ret < 0)
+ return ret;
+ }
+
+ return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
+
default:
return -EINVAL;
}
}
+static int mma8452_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 mma8452_data *data = iio_priv(indio_dev);
+ const struct mma_chip_info *chip = data->chip_info;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client,
+ data->chip_info->ev_cfg);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
+}
+
+static int mma8452_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 mma8452_data *data = iio_priv(indio_dev);
+ const struct mma_chip_info *chip = data->chip_info;
+ int val;
+
+ val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
+ if (val < 0)
+ return val;
+
+ if (state)
+ val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
+ else
+ val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
+
+ val |= chip->ev_cfg_ele;
+ val |= MMA8452_FF_MT_CFG_OAE;
+
+ return mma8452_change_config(data, chip->ev_cfg, val);
+}
+
+static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
+{
+ struct mma8452_data *data = iio_priv(indio_dev);
+ s64 ts = iio_get_time_ns();
+ int src;
+
+ src = i2c_smbus_read_byte_data(data->client, data->chip_info->ev_src);
+ if (src < 0)
+ return;
+
+ if (src & data->chip_info->ev_src_xe)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ ts);
+
+ if (src & data->chip_info->ev_src_ye)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ ts);
+
+ if (src & data->chip_info->ev_src_ze)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ ts);
+}
+
+static irqreturn_t mma8452_interrupt(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct mma8452_data *data = iio_priv(indio_dev);
+ const struct mma_chip_info *chip = data->chip_info;
+ int ret = IRQ_NONE;
+ int src;
+
+ src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC);
+ if (src < 0)
+ return IRQ_NONE;
+
+ if (src & MMA8452_INT_DRDY) {
+ iio_trigger_poll_chained(indio_dev->trig);
+ ret = IRQ_HANDLED;
+ }
+
+ if ((src & MMA8452_INT_TRANS &&
+ chip->ev_src == MMA8452_TRANSIENT_SRC) ||
+ (src & MMA8452_INT_FF_MT &&
+ chip->ev_src == MMA8452_FF_MT_SRC)) {
+ mma8452_transient_interrupt(indio_dev);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
static irqreturn_t mma8452_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
@@ -279,19 +703,99 @@ static irqreturn_t mma8452_trigger_handler(int irq, void *p)
u8 buffer[16]; /* 3 16-bit channels + padding + ts */
int ret;
- ret = mma8452_read(data, (__be16 *) buffer);
+ ret = mma8452_read(data, (__be16 *)buffer);
if (ret < 0)
goto done;
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
- iio_get_time_ns());
+ iio_get_time_ns());
done:
iio_trigger_notify_done(indio_dev->trig);
+
return IRQ_HANDLED;
}
-#define MMA8452_CHANNEL(axis, idx) { \
+static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ int ret;
+ struct mma8452_data *data = iio_priv(indio_dev);
+
+ if (reg > MMA8452_MAX_REG)
+ return -EINVAL;
+
+ if (!readval)
+ return mma8452_change_config(data, reg, writeval);
+
+ ret = i2c_smbus_read_byte_data(data->client, reg);
+ if (ret < 0)
+ return ret;
+
+ *readval = ret;
+
+ return 0;
+}
+
+static const struct iio_event_spec mma8452_transient_event[] = {
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
+ },
+};
+
+static const struct iio_event_spec mma8452_motion_event[] = {
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD)
+ },
+};
+
+/*
+ * Threshold is configured in fixed 8G/127 steps regardless of
+ * currently selected scale for measurement.
+ */
+static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742");
+
+static struct attribute *mma8452_event_attributes[] = {
+ &iio_const_attr_accel_transient_scale.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group mma8452_event_attribute_group = {
+ .attrs = mma8452_event_attributes,
+};
+
+#define MMA8452_CHANNEL(axis, idx, bits) { \
+ .type = IIO_ACCEL, \
+ .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) | \
+ BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 16 - (bits), \
+ .endianness = IIO_BE, \
+ }, \
+ .event_spec = mma8452_transient_event, \
+ .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
+}
+
+#define MMA8652_CHANNEL(axis, idx, bits) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
@@ -302,23 +806,128 @@ done:
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
- .realbits = 12, \
+ .realbits = (bits), \
.storagebits = 16, \
- .shift = 4, \
+ .shift = 16 - (bits), \
.endianness = IIO_BE, \
}, \
+ .event_spec = mma8452_motion_event, \
+ .num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
}
static const struct iio_chan_spec mma8452_channels[] = {
- MMA8452_CHANNEL(X, 0),
- MMA8452_CHANNEL(Y, 1),
- MMA8452_CHANNEL(Z, 2),
+ MMA8452_CHANNEL(X, 0, 12),
+ MMA8452_CHANNEL(Y, 1, 12),
+ MMA8452_CHANNEL(Z, 2, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec mma8453_channels[] = {
+ MMA8452_CHANNEL(X, 0, 10),
+ MMA8452_CHANNEL(Y, 1, 10),
+ MMA8452_CHANNEL(Z, 2, 10),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
+static const struct iio_chan_spec mma8652_channels[] = {
+ MMA8652_CHANNEL(X, 0, 12),
+ MMA8652_CHANNEL(Y, 1, 12),
+ MMA8652_CHANNEL(Z, 2, 12),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec mma8653_channels[] = {
+ MMA8652_CHANNEL(X, 0, 10),
+ MMA8652_CHANNEL(Y, 1, 10),
+ MMA8652_CHANNEL(Z, 2, 10),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+enum {
+ mma8452,
+ mma8453,
+ mma8652,
+ mma8653,
+};
+
+static const struct mma_chip_info mma_chip_info_table[] = {
+ [mma8452] = {
+ .chip_id = MMA8452_DEVICE_ID,
+ .channels = mma8452_channels,
+ .num_channels = ARRAY_SIZE(mma8452_channels),
+ /*
+ * Hardware has fullscale of -2G, -4G, -8G corresponding to
+ * raw value -2048 for 12 bit or -512 for 10 bit.
+ * The userspace interface uses m/s^2 and we declare micro units
+ * So scale factor for 12 bit here is given by:
+ * g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
+ */
+ .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
+ .ev_cfg = MMA8452_TRANSIENT_CFG,
+ .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
+ .ev_cfg_chan_shift = 1,
+ .ev_src = MMA8452_TRANSIENT_SRC,
+ .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
+ .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
+ .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
+ .ev_ths = MMA8452_TRANSIENT_THS,
+ .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
+ .ev_count = MMA8452_TRANSIENT_COUNT,
+ },
+ [mma8453] = {
+ .chip_id = MMA8453_DEVICE_ID,
+ .channels = mma8453_channels,
+ .num_channels = ARRAY_SIZE(mma8453_channels),
+ .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
+ .ev_cfg = MMA8452_TRANSIENT_CFG,
+ .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
+ .ev_cfg_chan_shift = 1,
+ .ev_src = MMA8452_TRANSIENT_SRC,
+ .ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
+ .ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
+ .ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
+ .ev_ths = MMA8452_TRANSIENT_THS,
+ .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
+ .ev_count = MMA8452_TRANSIENT_COUNT,
+ },
+ [mma8652] = {
+ .chip_id = MMA8652_DEVICE_ID,
+ .channels = mma8652_channels,
+ .num_channels = ARRAY_SIZE(mma8652_channels),
+ .mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
+ .ev_cfg = MMA8452_FF_MT_CFG,
+ .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
+ .ev_cfg_chan_shift = 3,
+ .ev_src = MMA8452_FF_MT_SRC,
+ .ev_src_xe = MMA8452_FF_MT_SRC_XHE,
+ .ev_src_ye = MMA8452_FF_MT_SRC_YHE,
+ .ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
+ .ev_ths = MMA8452_FF_MT_THS,
+ .ev_ths_mask = MMA8452_FF_MT_THS_MASK,
+ .ev_count = MMA8452_FF_MT_COUNT,
+ },
+ [mma8653] = {
+ .chip_id = MMA8653_DEVICE_ID,
+ .channels = mma8653_channels,
+ .num_channels = ARRAY_SIZE(mma8653_channels),
+ .mma_scales = { {0, 38307}, {0, 76614}, {0, 153228} },
+ .ev_cfg = MMA8452_FF_MT_CFG,
+ .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
+ .ev_cfg_chan_shift = 3,
+ .ev_src = MMA8452_FF_MT_SRC,
+ .ev_src_xe = MMA8452_FF_MT_SRC_XHE,
+ .ev_src_ye = MMA8452_FF_MT_SRC_YHE,
+ .ev_src_ze = MMA8452_FF_MT_SRC_ZHE,
+ .ev_ths = MMA8452_FF_MT_THS,
+ .ev_ths_mask = MMA8452_FF_MT_THS_MASK,
+ .ev_count = MMA8452_FF_MT_COUNT,
+ },
+};
+
static struct attribute *mma8452_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr,
NULL
};
@@ -330,23 +939,130 @@ static const struct iio_info mma8452_info = {
.attrs = &mma8452_group,
.read_raw = &mma8452_read_raw,
.write_raw = &mma8452_write_raw,
+ .event_attrs = &mma8452_event_attribute_group,
+ .read_event_value = &mma8452_read_thresh,
+ .write_event_value = &mma8452_write_thresh,
+ .read_event_config = &mma8452_read_event_config,
+ .write_event_config = &mma8452_write_event_config,
+ .debugfs_reg_access = &mma8452_reg_access_dbg,
.driver_module = THIS_MODULE,
};
static const unsigned long mma8452_scan_masks[] = {0x7, 0};
+static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mma8452_data *data = iio_priv(indio_dev);
+ int reg;
+
+ reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
+ if (reg < 0)
+ return reg;
+
+ if (state)
+ reg |= MMA8452_INT_DRDY;
+ else
+ reg &= ~MMA8452_INT_DRDY;
+
+ return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
+}
+
+static int mma8452_validate_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+
+ if (indio != indio_dev)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mma8452_trigger_ops = {
+ .set_trigger_state = mma8452_data_rdy_trigger_set_state,
+ .validate_device = mma8452_validate_device,
+ .owner = THIS_MODULE,
+};
+
+static int mma8452_trigger_setup(struct iio_dev *indio_dev)
+{
+ struct mma8452_data *data = iio_priv(indio_dev);
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!trig)
+ return -ENOMEM;
+
+ trig->dev.parent = &data->client->dev;
+ trig->ops = &mma8452_trigger_ops;
+ iio_trigger_set_drvdata(trig, indio_dev);
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = trig;
+
+ return 0;
+}
+
+static void mma8452_trigger_cleanup(struct iio_dev *indio_dev)
+{
+ if (indio_dev->trig)
+ iio_trigger_unregister(indio_dev->trig);
+}
+
+static int mma8452_reset(struct i2c_client *client)
+{
+ int i;
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2,
+ MMA8452_CTRL_REG2_RST);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < 10; i++) {
+ usleep_range(100, 200);
+ ret = i2c_smbus_read_byte_data(client, MMA8452_CTRL_REG2);
+ if (ret == -EIO)
+ continue; /* I2C comm reset */
+ if (ret < 0)
+ return ret;
+ if (!(ret & MMA8452_CTRL_REG2_RST))
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
+static const struct of_device_id mma8452_dt_ids[] = {
+ { .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
+ { .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
+ { .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
+ { .compatible = "fsl,mma8653", .data = &mma_chip_info_table[mma8653] },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mma8452_dt_ids);
+
static int mma8452_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mma8452_data *data;
struct iio_dev *indio_dev;
int ret;
+ const struct of_device_id *match;
- ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
- if (ret < 0)
- return ret;
- if (ret != MMA8452_DEVICE_ID)
+ match = of_match_device(mma8452_dt_ids, &client->dev);
+ if (!match) {
+ dev_err(&client->dev, "unknown device model\n");
return -ENODEV;
+ }
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -355,41 +1071,118 @@ static int mma8452_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
+ data->chip_info = match->data;
+
+ ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
+ if (ret < 0)
+ return ret;
+
+ switch (ret) {
+ case MMA8452_DEVICE_ID:
+ case MMA8453_DEVICE_ID:
+ case MMA8652_DEVICE_ID:
+ case MMA8653_DEVICE_ID:
+ if (ret == data->chip_info->chip_id)
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
+ match->compatible, data->chip_info->chip_id);
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mma8452_info;
indio_dev->name = id->name;
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = mma8452_channels;
- indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
+ indio_dev->channels = data->chip_info->channels;
+ indio_dev->num_channels = data->chip_info->num_channels;
indio_dev->available_scan_masks = mma8452_scan_masks;
- data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
- (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
- ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
- data->ctrl_reg1);
+ ret = mma8452_reset(client);
if (ret < 0)
return ret;
data->data_cfg = MMA8452_DATA_CFG_FS_2G;
ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
- data->data_cfg);
+ data->data_cfg);
if (ret < 0)
return ret;
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
- mma8452_trigger_handler, NULL);
+ /*
+ * By default set transient threshold to max to avoid events if
+ * enabling without configuring threshold.
+ */
+ ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
+ MMA8452_TRANSIENT_THS_MASK);
if (ret < 0)
return ret;
+ if (client->irq) {
+ /*
+ * Although we enable the interrupt sources once and for
+ * all here the event detection itself is not enabled until
+ * userspace asks for it by mma8452_write_event_config()
+ */
+ int supported_interrupts = MMA8452_INT_DRDY |
+ MMA8452_INT_TRANS |
+ MMA8452_INT_FF_MT;
+ int enabled_interrupts = MMA8452_INT_TRANS |
+ MMA8452_INT_FF_MT;
+
+ /* Assume wired to INT1 pin */
+ ret = i2c_smbus_write_byte_data(client,
+ MMA8452_CTRL_REG5,
+ supported_interrupts);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client,
+ MMA8452_CTRL_REG4,
+ enabled_interrupts);
+ if (ret < 0)
+ return ret;
+
+ ret = mma8452_trigger_setup(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
+ (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
+ ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
+ data->ctrl_reg1);
+ if (ret < 0)
+ goto trigger_cleanup;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ mma8452_trigger_handler, NULL);
+ if (ret < 0)
+ goto trigger_cleanup;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL, mma8452_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, indio_dev);
+ if (ret)
+ goto buffer_cleanup;
+ }
+
ret = iio_device_register(indio_dev);
if (ret < 0)
goto buffer_cleanup;
+
return 0;
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
+
+trigger_cleanup:
+ mma8452_trigger_cleanup(indio_dev);
+
return ret;
}
@@ -399,6 +1192,7 @@ static int mma8452_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
+ mma8452_trigger_cleanup(indio_dev);
mma8452_standby(iio_priv(indio_dev));
return 0;
@@ -424,16 +1218,14 @@ static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
#endif
static const struct i2c_device_id mma8452_id[] = {
- { "mma8452", 0 },
+ { "mma8452", mma8452 },
+ { "mma8453", mma8453 },
+ { "mma8652", mma8652 },
+ { "mma8653", mma8653 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mma8452_id);
-static const struct of_device_id mma8452_dt_ids[] = {
- { .compatible = "fsl,mma8452" },
- { }
-};
-
static struct i2c_driver mma8452_driver = {
.driver = {
.name = "mma8452",
diff --git a/kernel/drivers/iio/accel/mma9551_core.c b/kernel/drivers/iio/accel/mma9551_core.c
index c6d5a3a40..c34c5ce81 100644
--- a/kernel/drivers/iio/accel/mma9551_core.c
+++ b/kernel/drivers/iio/accel/mma9551_core.c
@@ -297,7 +297,7 @@ EXPORT_SYMBOL(mma9551_read_status_byte);
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
- u16 reg, u16 *val)
+ u16 reg, u16 *val)
{
int ret;
__be16 v;
@@ -328,12 +328,12 @@ EXPORT_SYMBOL(mma9551_read_config_word);
* Returns: 0 on success, negative value on failure.
*/
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
- u16 reg, u16 val)
+ u16 reg, u16 val)
{
__be16 v = cpu_to_be16(val);
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
- (u8 *) &v, 2, NULL, 0);
+ (u8 *)&v, 2, NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_word);
@@ -373,8 +373,8 @@ EXPORT_SYMBOL(mma9551_read_status_word);
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
- * @len: Length of array to read in bytes
- * @val: Array of words to read
+ * @len: Length of array to read (in words)
+ * @buf: Array of words to read
*
* Read multiple configuration registers (word-sized registers).
*
@@ -385,23 +385,22 @@ EXPORT_SYMBOL(mma9551_read_status_word);
* Returns: 0 on success, negative value on failure.
*/
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
- u16 reg, u8 len, u16 *buf)
+ u16 reg, u8 len, u16 *buf)
{
int ret, i;
- int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
- if (len_words > ARRAY_SIZE(be_buf)) {
+ if (len > ARRAY_SIZE(be_buf)) {
dev_err(&client->dev, "Invalid buffer size %d\n", len);
return -EINVAL;
}
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
- reg, NULL, 0, (u8 *) be_buf, len);
+ reg, NULL, 0, (u8 *)be_buf, len * sizeof(u16));
if (ret < 0)
return ret;
- for (i = 0; i < len_words; i++)
+ for (i = 0; i < len; i++)
buf[i] = be16_to_cpu(be_buf[i]);
return 0;
@@ -413,8 +412,8 @@ EXPORT_SYMBOL(mma9551_read_config_words);
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
- * @len: Length of array to read in bytes
- * @val: Array of words to read
+ * @len: Length of array to read (in words)
+ * @buf: Array of words to read
*
* Read multiple status registers (word-sized registers).
*
@@ -428,20 +427,19 @@ int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int ret, i;
- int len_words = len / sizeof(u16);
__be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS / 2];
- if (len_words > ARRAY_SIZE(be_buf)) {
+ if (len > ARRAY_SIZE(be_buf)) {
dev_err(&client->dev, "Invalid buffer size %d\n", len);
return -EINVAL;
}
ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
- reg, NULL, 0, (u8 *) be_buf, len);
+ reg, NULL, 0, (u8 *)be_buf, len * sizeof(u16));
if (ret < 0)
return ret;
- for (i = 0; i < len_words; i++)
+ for (i = 0; i < len; i++)
buf[i] = be16_to_cpu(be_buf[i]);
return 0;
@@ -453,8 +451,8 @@ EXPORT_SYMBOL(mma9551_read_status_words);
* @client: I2C client
* @app_id: Application ID
* @reg: Application register
- * @len: Length of array to write in bytes
- * @val: Array of words to write
+ * @len: Length of array to write (in words)
+ * @buf: Array of words to write
*
* Write multiple configuration registers (word-sized registers).
*
@@ -468,19 +466,18 @@ int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf)
{
int i;
- int len_words = len / sizeof(u16);
__be16 be_buf[(MMA9551_MAX_MAILBOX_DATA_REGS - 1) / 2];
- if (len_words > ARRAY_SIZE(be_buf)) {
+ if (len > ARRAY_SIZE(be_buf)) {
dev_err(&client->dev, "Invalid buffer size %d\n", len);
return -EINVAL;
}
- for (i = 0; i < len_words; i++)
+ for (i = 0; i < len; i++)
be_buf[i] = cpu_to_be16(buf[i]);
return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
- reg, (u8 *) be_buf, len, NULL, 0);
+ reg, (u8 *)be_buf, len * sizeof(u16), NULL, 0);
}
EXPORT_SYMBOL(mma9551_write_config_words);
@@ -800,7 +797,7 @@ EXPORT_SYMBOL(mma9551_read_accel_scale);
*/
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
{
- return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
+ return mma9551_write_config_byte(client, MMA9551_APPID_RSC,
MMA9551_RSC_RESET +
MMA9551_RSC_OFFSET(app_mask),
MMA9551_RSC_VAL(app_mask));
diff --git a/kernel/drivers/iio/accel/mma9551_core.h b/kernel/drivers/iio/accel/mma9551_core.h
index edaa56b10..5e88e6454 100644
--- a/kernel/drivers/iio/accel/mma9551_core.h
+++ b/kernel/drivers/iio/accel/mma9551_core.h
@@ -22,7 +22,7 @@
#define MMA9551_APPID_TILT 0x0B
#define MMA9551_APPID_SLEEP_WAKE 0x12
#define MMA9551_APPID_PEDOMETER 0x15
-#define MMA9551_APPID_RCS 0x17
+#define MMA9551_APPID_RSC 0x17
#define MMA9551_APPID_NONE 0xff
/* Reset/Suspend/Clear application app masks */
@@ -53,13 +53,13 @@ int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
u16 reg, u8 *val);
int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
- u16 reg, u16 *val);
+ u16 reg, u16 *val);
int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
- u16 reg, u16 val);
+ u16 reg, u16 val);
int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
u16 reg, u16 *val);
int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
- u16 reg, u8 len, u16 *buf);
+ u16 reg, u8 len, u16 *buf);
int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
u16 reg, u8 len, u16 *buf);
int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
diff --git a/kernel/drivers/iio/accel/mma9553.c b/kernel/drivers/iio/accel/mma9553.c
index 365a109aa..9408ef3ad 100644
--- a/kernel/drivers/iio/accel/mma9553.c
+++ b/kernel/drivers/iio/accel/mma9553.c
@@ -26,7 +26,6 @@
#define MMA9553_DRV_NAME "mma9553"
#define MMA9553_IRQ_NAME "mma9553_event"
-#define MMA9553_GPIO_NAME "mma9553_int"
/* Pedometer configuration registers (R/W) */
#define MMA9553_REG_CONF_SLEEPMIN 0x00
@@ -63,8 +62,8 @@
#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
#define MMA9553_MASK_STATUS_SUSP BIT(11)
-#define MMA9553_MASK_STATUS_ACTIVITY (BIT(10) | BIT(9) | BIT(8))
-#define MMA9553_MASK_STATUS_VERSION 0x00FF
+#define MMA9553_MASK_STATUS_ACTIVITY GENMASK(10, 8)
+#define MMA9553_MASK_STATUS_VERSION GENMASK(7, 0)
#define MMA9553_REG_STEPCNT 0x02
#define MMA9553_REG_DISTANCE 0x04
@@ -76,14 +75,15 @@
#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
#define MMA9553_DEFAULT_GPIO_POLARITY 0
-/* Bitnum used for gpio configuration = bit number in high status byte */
-#define STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
+/* Bitnum used for GPIO configuration = bit number in high status byte */
+#define MMA9553_STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
+#define MMA9553_MAX_BITNUM MMA9553_STATUS_TO_BITNUM(BIT(16))
#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
/*
* The internal activity level must be stable for ACTTHD samples before
- * ACTIVITY is updated.The ACTIVITY variable contains the current activity
+ * ACTIVITY is updated. The ACTIVITY variable contains the current activity
* level and is updated every time a step is detected or once a second
* if there are no steps.
*/
@@ -181,6 +181,10 @@ struct mma9553_conf_regs {
struct mma9553_data {
struct i2c_client *client;
+ /*
+ * 1. Serialize access to HW (requested by mma9551_core API).
+ * 2. Serialize sequences that power on/off the device and access HW.
+ */
struct mutex mutex;
struct mma9553_conf_regs conf;
struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
@@ -321,7 +325,8 @@ static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
int ret;
ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
- MMA9553_REG_STATUS, sizeof(u32), buf);
+ MMA9553_REG_STATUS, ARRAY_SIZE(buf),
+ buf);
if (ret < 0) {
dev_err(&data->client->dev,
"error reading status and stepcnt\n");
@@ -341,21 +346,21 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
struct mma9553_event *ev_step_detect;
bool activity_enabled;
- activity_enabled =
- mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
- ev_step_detect =
- mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
+ activity_enabled = mma9553_is_any_event_enabled(data, true,
+ IIO_ACTIVITY);
+ ev_step_detect = mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
+ IIO_EV_DIR_NONE);
/*
* If both step detector and activity are enabled, use the MRGFL bit.
* This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
*/
if (activity_enabled && ev_step_detect->enabled)
- bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
+ bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
else if (ev_step_detect->enabled)
- bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
+ bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
else if (activity_enabled)
- bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
+ bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
else /* Reset */
appid = MMA9551_APPID_NONE;
@@ -363,13 +368,15 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
return 0;
/* Save initial values for activity and stepcnt */
- if (activity_enabled || ev_step_detect->enabled)
- mma9553_read_activity_stepcnt(data, &data->activity,
- &data->stepcnt);
+ if (activity_enabled || ev_step_detect->enabled) {
+ ret = mma9553_read_activity_stepcnt(data, &data->activity,
+ &data->stepcnt);
+ if (ret < 0)
+ return ret;
+ }
- ret = mma9551_gpio_config(data->client,
- MMA9553_DEFAULT_GPIO_PIN,
- appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
+ ret = mma9551_gpio_config(data->client, MMA9553_DEFAULT_GPIO_PIN, appid,
+ bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
if (ret < 0)
return ret;
data->gpio_bitnum = bitnum;
@@ -390,19 +397,18 @@ static int mma9553_init(struct mma9553_data *data)
* a device identification command to differentiate the MMA9553L
* from the MMA9550L.
*/
- ret =
- mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
- MMA9553_REG_CONF_SLEEPMIN,
- sizeof(data->conf), (u16 *) &data->conf);
+ ret = mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_CONF_SLEEPMIN,
+ sizeof(data->conf) / sizeof(u16),
+ (u16 *)&data->conf);
if (ret < 0) {
dev_err(&data->client->dev,
- "device is not MMA9553L: failed to read cfg regs\n");
+ "failed to read configuration registers\n");
return ret;
}
-
- /* Reset gpio */
- data->gpio_bitnum = -1;
+ /* Reset GPIO */
+ data->gpio_bitnum = MMA9553_MAX_BITNUM;
ret = mma9553_conf_gpio(data);
if (ret < 0)
return ret;
@@ -415,18 +421,18 @@ static int mma9553_init(struct mma9553_data *data)
data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
- data->conf.config =
- mma9553_set_bits(data->conf.config, 1, MMA9553_MASK_CONF_CONFIG);
+ data->conf.config = mma9553_set_bits(data->conf.config, 1,
+ MMA9553_MASK_CONF_CONFIG);
/*
* Clear the activity debounce counter when the activity level changes,
* so that the confidence level applies for any activity level.
*/
data->conf.config = mma9553_set_bits(data->conf.config, 1,
MMA9553_MASK_CONF_ACT_DBCNTM);
- ret =
- mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
- MMA9553_REG_CONF_SLEEPMIN,
- sizeof(data->conf), (u16 *) &data->conf);
+ ret = mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_CONF_SLEEPMIN,
+ sizeof(data->conf) / sizeof(u16),
+ (u16 *)&data->conf);
if (ret < 0) {
dev_err(&data->client->dev,
"failed to write configuration registers\n");
@@ -436,6 +442,32 @@ static int mma9553_init(struct mma9553_data *data)
return mma9551_set_device_state(data->client, true);
}
+static int mma9553_read_status_word(struct mma9553_data *data, u16 reg,
+ u16 *tmp)
+{
+ bool powered_on;
+ int ret;
+
+ /*
+ * The HW only counts steps and other dependent
+ * parameters (speed, distance, calories, activity)
+ * if power is on (from enabling an event or the
+ * step counter).
+ */
+ powered_on = mma9553_is_any_event_enabled(data, false, 0) ||
+ data->stepcnt_enabled;
+ if (!powered_on) {
+ dev_err(&data->client->dev, "No channels enabled\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_status_word(data->client, MMA9551_APPID_PEDOMETER,
+ reg, tmp);
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
static int mma9553_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -444,69 +476,30 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
int ret;
u16 tmp;
u8 activity;
- bool powered_on;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
- /*
- * The HW only counts steps and other dependent
- * parameters (speed, distance, calories, activity)
- * if power is on (from enabling an event or the
- * step counter */
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_STEPCNT,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_DISTANCE:
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_DISTANCE,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_ACTIVITY:
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_STATUS,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
@@ -531,38 +524,17 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
case IIO_VELOCITY: /* m/h */
if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
return -EINVAL;
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
- MMA9553_REG_SPEED, &tmp);
- mutex_unlock(&data->mutex);
+ ret = mma9553_read_status_word(data,
+ MMA9553_REG_SPEED,
+ &tmp);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_ENERGY: /* Cal or kcal */
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_CALORIES,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
@@ -597,7 +569,7 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBHEIGHT:
tmp = mma9553_get_bits(data->conf.height_weight,
- MMA9553_MASK_CONF_HEIGHT);
+ MMA9553_MASK_CONF_HEIGHT);
*val = tmp / 100; /* cm to m */
*val2 = (tmp % 100) * 10000;
return IIO_VAL_INT_PLUS_MICRO;
@@ -749,7 +721,6 @@ static int mma9553_read_event_config(struct iio_dev *indio_dev,
enum iio_event_type type,
enum iio_event_direction dir)
{
-
struct mma9553_data *data = iio_priv(indio_dev);
struct mma9553_event *event;
@@ -789,7 +760,7 @@ static int mma9553_write_event_config(struct iio_dev *indio_dev,
mutex_unlock(&data->mutex);
- return ret;
+ return 0;
err_conf_gpio:
if (state) {
@@ -897,7 +868,7 @@ static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
/*
* HW expects 0 for female and 1 for male,
- * while iio index is 0 for male and 1 for female
+ * while iio index is 0 for male and 1 for female.
*/
return !gender;
}
@@ -944,11 +915,11 @@ static const struct iio_event_spec mma9553_activity_events[] = {
},
};
-static const char * const calibgender_modes[] = { "male", "female" };
+static const char * const mma9553_calibgender_modes[] = { "male", "female" };
static const struct iio_enum mma9553_calibgender_enum = {
- .items = calibgender_modes,
- .num_items = ARRAY_SIZE(calibgender_modes),
+ .items = mma9553_calibgender_modes,
+ .num_items = ARRAY_SIZE(mma9553_calibgender_modes),
.get = mma9553_get_calibgender_mode,
.set = mma9553_set_calibgender_mode,
};
@@ -1056,22 +1027,22 @@ static irqreturn_t mma9553_event_handler(int irq, void *private)
return IRQ_HANDLED;
}
- ev_prev_activity =
- mma9553_get_event(data, IIO_ACTIVITY,
- mma9553_activity_to_mod(data->activity),
- IIO_EV_DIR_FALLING);
- ev_activity =
- mma9553_get_event(data, IIO_ACTIVITY,
- mma9553_activity_to_mod(activity),
- IIO_EV_DIR_RISING);
- ev_step_detect =
- mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
+ ev_prev_activity = mma9553_get_event(data, IIO_ACTIVITY,
+ mma9553_activity_to_mod(
+ data->activity),
+ IIO_EV_DIR_FALLING);
+ ev_activity = mma9553_get_event(data, IIO_ACTIVITY,
+ mma9553_activity_to_mod(activity),
+ IIO_EV_DIR_RISING);
+ ev_step_detect = mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD,
+ IIO_EV_DIR_NONE);
if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
data->stepcnt = stepcnt;
iio_push_event(indio_dev,
IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
- IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
+ IIO_EV_DIR_NONE,
+ IIO_EV_TYPE_CHANGE, 0, 0, 0),
data->timestamp);
}
@@ -1081,17 +1052,19 @@ static irqreturn_t mma9553_event_handler(int irq, void *private)
if (ev_prev_activity && ev_prev_activity->enabled)
iio_push_event(indio_dev,
IIO_EVENT_CODE(IIO_ACTIVITY, 0,
- ev_prev_activity->info->mod,
- IIO_EV_DIR_FALLING,
- IIO_EV_TYPE_THRESH, 0, 0, 0),
+ ev_prev_activity->info->mod,
+ IIO_EV_DIR_FALLING,
+ IIO_EV_TYPE_THRESH, 0, 0,
+ 0),
data->timestamp);
if (ev_activity && ev_activity->enabled)
iio_push_event(indio_dev,
IIO_EVENT_CODE(IIO_ACTIVITY, 0,
- ev_activity->info->mod,
- IIO_EV_DIR_RISING,
- IIO_EV_TYPE_THRESH, 0, 0, 0),
+ ev_activity->info->mod,
+ IIO_EV_DIR_RISING,
+ IIO_EV_TYPE_THRESH, 0, 0,
+ 0),
data->timestamp);
}
mutex_unlock(&data->mutex);
@@ -1099,31 +1072,6 @@ static irqreturn_t mma9553_event_handler(int irq, void *private)
return IRQ_HANDLED;
}
-static int mma9553_gpio_probe(struct i2c_client *client)
-{
- struct device *dev;
- struct gpio_desc *gpio;
- int ret;
-
- if (!client)
- return -EINVAL;
-
- dev = &client->dev;
-
- /* data ready gpio interrupt pin */
- gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev, "acpi gpio get index failed\n");
- return PTR_ERR(gpio);
- }
-
- ret = gpiod_to_irq(gpio);
-
- dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
-
- return ret;
-}
-
static const char *mma9553_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
@@ -1172,10 +1120,7 @@ static int mma9553_probe(struct i2c_client *client,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mma9553_info;
- if (client->irq < 0)
- client->irq = mma9553_gpio_probe(client);
-
- if (client->irq >= 0) {
+ if (client->irq > 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
mma9553_irq_handler,
mma9553_event_handler,
@@ -1186,7 +1131,6 @@ static int mma9553_probe(struct i2c_client *client,
client->irq);
goto out_poweroff;
}
-
}
ret = iio_device_register(indio_dev);
diff --git a/kernel/drivers/iio/accel/mxc4005.c b/kernel/drivers/iio/accel/mxc4005.c
new file mode 100644
index 000000000..e72e218c2
--- /dev/null
+++ b/kernel/drivers/iio/accel/mxc4005.c
@@ -0,0 +1,567 @@
+/*
+ * 3-axis accelerometer driver for MXC4005XC Memsic sensor
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define MXC4005_DRV_NAME "mxc4005"
+#define MXC4005_IRQ_NAME "mxc4005_event"
+#define MXC4005_REGMAP_NAME "mxc4005_regmap"
+
+#define MXC4005_REG_XOUT_UPPER 0x03
+#define MXC4005_REG_XOUT_LOWER 0x04
+#define MXC4005_REG_YOUT_UPPER 0x05
+#define MXC4005_REG_YOUT_LOWER 0x06
+#define MXC4005_REG_ZOUT_UPPER 0x07
+#define MXC4005_REG_ZOUT_LOWER 0x08
+
+#define MXC4005_REG_INT_MASK1 0x0B
+#define MXC4005_REG_INT_MASK1_BIT_DRDYE 0x01
+
+#define MXC4005_REG_INT_CLR1 0x01
+#define MXC4005_REG_INT_CLR1_BIT_DRDYC 0x01
+
+#define MXC4005_REG_CONTROL 0x0D
+#define MXC4005_REG_CONTROL_MASK_FSR GENMASK(6, 5)
+#define MXC4005_CONTROL_FSR_SHIFT 5
+
+#define MXC4005_REG_DEVICE_ID 0x0E
+
+enum mxc4005_axis {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z,
+};
+
+enum mxc4005_range {
+ MXC4005_RANGE_2G,
+ MXC4005_RANGE_4G,
+ MXC4005_RANGE_8G,
+};
+
+struct mxc4005_data {
+ struct device *dev;
+ struct mutex mutex;
+ struct regmap *regmap;
+ struct iio_trigger *dready_trig;
+ __be16 buffer[8];
+ bool trigger_enabled;
+};
+
+/*
+ * MXC4005 can operate in the following ranges:
+ * +/- 2G, 4G, 8G (the default +/-2G)
+ *
+ * (2 + 2) * 9.81 / (2^12 - 1) = 0.009582
+ * (4 + 4) * 9.81 / (2^12 - 1) = 0.019164
+ * (8 + 8) * 9.81 / (2^12 - 1) = 0.038329
+ */
+static const struct {
+ u8 range;
+ int scale;
+} mxc4005_scale_table[] = {
+ {MXC4005_RANGE_2G, 9582},
+ {MXC4005_RANGE_4G, 19164},
+ {MXC4005_RANGE_8G, 38329},
+};
+
+
+static IIO_CONST_ATTR(in_accel_scale_available, "0.009582 0.019164 0.038329");
+
+static struct attribute *mxc4005_attributes[] = {
+ &iio_const_attr_in_accel_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group mxc4005_attrs_group = {
+ .attrs = mxc4005_attributes,
+};
+
+static bool mxc4005_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MXC4005_REG_XOUT_UPPER:
+ case MXC4005_REG_XOUT_LOWER:
+ case MXC4005_REG_YOUT_UPPER:
+ case MXC4005_REG_YOUT_LOWER:
+ case MXC4005_REG_ZOUT_UPPER:
+ case MXC4005_REG_ZOUT_LOWER:
+ case MXC4005_REG_DEVICE_ID:
+ case MXC4005_REG_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MXC4005_REG_INT_CLR1:
+ case MXC4005_REG_INT_MASK1:
+ case MXC4005_REG_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config mxc4005_regmap_config = {
+ .name = MXC4005_REGMAP_NAME,
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MXC4005_REG_DEVICE_ID,
+
+ .readable_reg = mxc4005_is_readable_reg,
+ .writeable_reg = mxc4005_is_writeable_reg,
+};
+
+static int mxc4005_read_xyz(struct mxc4005_data *data)
+{
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER,
+ (u8 *) data->buffer, sizeof(data->buffer));
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read axes\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mxc4005_read_axis(struct mxc4005_data *data,
+ unsigned int addr)
+{
+ __be16 reg;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, addr, (u8 *) &reg, sizeof(reg));
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read reg %02x\n", addr);
+ return ret;
+ }
+
+ return be16_to_cpu(reg);
+}
+
+static int mxc4005_read_scale(struct mxc4005_data *data)
+{
+ unsigned int reg;
+ int ret;
+ int i;
+
+ ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, &reg);
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read reg_control\n");
+ return ret;
+ }
+
+ i = reg >> MXC4005_CONTROL_FSR_SHIFT;
+
+ if (i < 0 || i >= ARRAY_SIZE(mxc4005_scale_table))
+ return -EINVAL;
+
+ return mxc4005_scale_table[i].scale;
+}
+
+static int mxc4005_set_scale(struct mxc4005_data *data, int val)
+{
+ unsigned int reg;
+ int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(mxc4005_scale_table); i++) {
+ if (mxc4005_scale_table[i].scale == val) {
+ reg = i << MXC4005_CONTROL_FSR_SHIFT;
+ ret = regmap_update_bits(data->regmap,
+ MXC4005_REG_CONTROL,
+ MXC4005_REG_CONTROL_MASK_FSR,
+ reg);
+ if (ret < 0)
+ dev_err(data->dev,
+ "failed to write reg_control\n");
+ return ret;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int mxc4005_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mxc4005_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ ret = mxc4005_read_axis(data, chan->address);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(ret >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ ret = mxc4005_read_scale(data);
+ if (ret < 0)
+ return ret;
+
+ *val = 0;
+ *val2 = ret;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mxc4005_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mxc4005_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ return mxc4005_set_scale(data, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mxc4005_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mxc4005_read_raw,
+ .write_raw = mxc4005_write_raw,
+ .attrs = &mxc4005_attrs_group,
+};
+
+static const unsigned long mxc4005_scan_masks[] = {
+ BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
+ 0
+};
+
+#define MXC4005_CHANNEL(_axis, _addr) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .address = _addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = AXIS_##_axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 4, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec mxc4005_channels[] = {
+ MXC4005_CHANNEL(X, MXC4005_REG_XOUT_UPPER),
+ MXC4005_CHANNEL(Y, MXC4005_REG_YOUT_UPPER),
+ MXC4005_CHANNEL(Z, MXC4005_REG_ZOUT_UPPER),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static irqreturn_t mxc4005_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mxc4005_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = mxc4005_read_xyz(data);
+ if (ret < 0)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ pf->timestamp);
+
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mxc4005_clr_intr(struct mxc4005_data *data)
+{
+ int ret;
+
+ /* clear interrupt */
+ ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1,
+ MXC4005_REG_INT_CLR1_BIT_DRDYC);
+ if (ret < 0) {
+ dev_err(data->dev, "failed to write to reg_int_clr1\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mxc4005_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mxc4005_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ if (state) {
+ ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
+ MXC4005_REG_INT_MASK1_BIT_DRDYE);
+ } else {
+ ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1,
+ ~MXC4005_REG_INT_MASK1_BIT_DRDYE);
+ }
+
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ dev_err(data->dev, "failed to update reg_int_mask1");
+ return ret;
+ }
+
+ data->trigger_enabled = state;
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static int mxc4005_trigger_try_reen(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mxc4005_data *data = iio_priv(indio_dev);
+
+ if (!data->dready_trig)
+ return 0;
+
+ return mxc4005_clr_intr(data);
+}
+
+static const struct iio_trigger_ops mxc4005_trigger_ops = {
+ .set_trigger_state = mxc4005_set_trigger_state,
+ .try_reenable = mxc4005_trigger_try_reen,
+ .owner = THIS_MODULE,
+};
+
+static int mxc4005_gpio_probe(struct i2c_client *client,
+ struct mxc4005_data *data)
+{
+ struct device *dev;
+ struct gpio_desc *gpio;
+ int ret;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ gpio = devm_gpiod_get_index(dev, "mxc4005_int", 0, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "failed to get acpi gpio index\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_to_irq(gpio);
+
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+ return ret;
+}
+
+static int mxc4005_chip_init(struct mxc4005_data *data)
+{
+ int ret;
+ unsigned int reg;
+
+ ret = regmap_read(data->regmap, MXC4005_REG_DEVICE_ID, &reg);
+ if (ret < 0) {
+ dev_err(data->dev, "failed to read chip id\n");
+ return ret;
+ }
+
+ dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg);
+
+ return 0;
+}
+
+static int mxc4005_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxc4005_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, &mxc4005_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "failed to initialize regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->dev = &client->dev;
+ data->regmap = regmap;
+
+ ret = mxc4005_chip_init(data);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to initialize chip\n");
+ return ret;
+ }
+
+ mutex_init(&data->mutex);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = mxc4005_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels);
+ indio_dev->available_scan_masks = mxc4005_scan_masks;
+ indio_dev->name = MXC4005_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mxc4005_info;
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ iio_pollfunc_store_time,
+ mxc4005_trigger_handler,
+ NULL);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "failed to setup iio triggered buffer\n");
+ return ret;
+ }
+
+ if (client->irq < 0)
+ client->irq = mxc4005_gpio_probe(client, data);
+
+ if (client->irq > 0) {
+ data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->dready_trig)
+ return -ENOMEM;
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ NULL,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ MXC4005_IRQ_NAME,
+ data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to init threaded irq\n");
+ goto err_buffer_cleanup;
+ }
+
+ data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->ops = &mxc4005_trigger_ops;
+ iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+ indio_dev->trig = data->dready_trig;
+ iio_trigger_get(indio_dev->trig);
+ ret = iio_trigger_register(data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to register trigger\n");
+ goto err_trigger_unregister;
+ }
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "unable to register iio device %d\n", ret);
+ goto err_buffer_cleanup;
+ }
+
+ return 0;
+
+err_trigger_unregister:
+ iio_trigger_unregister(data->dready_trig);
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int mxc4005_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mxc4005_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+
+ return 0;
+}
+
+static const struct acpi_device_id mxc4005_acpi_match[] = {
+ {"MXC4005", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match);
+
+static const struct i2c_device_id mxc4005_id[] = {
+ {"mxc4005", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, mxc4005_id);
+
+static struct i2c_driver mxc4005_driver = {
+ .driver = {
+ .name = MXC4005_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
+ },
+ .probe = mxc4005_probe,
+ .remove = mxc4005_remove,
+ .id_table = mxc4005_id,
+};
+
+module_i2c_driver(mxc4005_driver);
+
+MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MXC4005 3-axis accelerometer driver");
diff --git a/kernel/drivers/iio/accel/st_accel.h b/kernel/drivers/iio/accel/st_accel.h
index 7ee9724b1..468f21fa2 100644
--- a/kernel/drivers/iio/accel/st_accel.h
+++ b/kernel/drivers/iio/accel/st_accel.h
@@ -20,11 +20,13 @@
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel"
#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel"
+#define LIS331DL_ACCEL_DEV_NAME "lis331dl_accel"
#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh"
#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel"
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"
#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel"
#define LSM330_ACCEL_DEV_NAME "lsm330_accel"
+#define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
/**
* struct st_sensors_platform_data - default accel platform data
diff --git a/kernel/drivers/iio/accel/st_accel_core.c b/kernel/drivers/iio/accel/st_accel_core.c
index 211b13271..197a08b4e 100644
--- a/kernel/drivers/iio/accel/st_accel_core.c
+++ b/kernel/drivers/iio/accel/st_accel_core.c
@@ -149,10 +149,46 @@
#define ST_ACCEL_4_BDU_MASK 0x40
#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21
#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04
-#define ST_ACCEL_4_IG1_EN_ADDR 0x21
-#define ST_ACCEL_4_IG1_EN_MASK 0x08
#define ST_ACCEL_4_MULTIREAD_BIT true
+/* CUSTOM VALUES FOR SENSOR 5 */
+#define ST_ACCEL_5_WAI_EXP 0x3b
+#define ST_ACCEL_5_ODR_ADDR 0x20
+#define ST_ACCEL_5_ODR_MASK 0x80
+#define ST_ACCEL_5_ODR_AVL_100HZ_VAL 0x00
+#define ST_ACCEL_5_ODR_AVL_400HZ_VAL 0x01
+#define ST_ACCEL_5_PW_ADDR 0x20
+#define ST_ACCEL_5_PW_MASK 0x40
+#define ST_ACCEL_5_FS_ADDR 0x20
+#define ST_ACCEL_5_FS_MASK 0x20
+#define ST_ACCEL_5_FS_AVL_2_VAL 0X00
+#define ST_ACCEL_5_FS_AVL_8_VAL 0X01
+/* TODO: check these resulting gain settings, these are not in the datsheet */
+#define ST_ACCEL_5_FS_AVL_2_GAIN IIO_G_TO_M_S_2(18000)
+#define ST_ACCEL_5_FS_AVL_8_GAIN IIO_G_TO_M_S_2(72000)
+#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22
+#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
+#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
+#define ST_ACCEL_5_IG1_EN_ADDR 0x21
+#define ST_ACCEL_5_IG1_EN_MASK 0x08
+#define ST_ACCEL_5_MULTIREAD_BIT false
+
+static const struct iio_chan_spec st_accel_8bit_channels[] = {
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8,
+ ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8,
+ ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8,
+ ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
static const struct iio_chan_spec st_accel_12bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@@ -188,12 +224,14 @@ static const struct iio_chan_spec st_accel_16bit_channels[] = {
static const struct st_sensor_settings st_accel_sensors_settings[] = {
{
.wai = ST_ACCEL_1_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3DH_ACCEL_DEV_NAME,
[1] = LSM303DLHC_ACCEL_DEV_NAME,
[2] = LSM330D_ACCEL_DEV_NAME,
[3] = LSM330DL_ACCEL_DEV_NAME,
[4] = LSM330DLC_ACCEL_DEV_NAME,
+ [5] = LSM303AGR_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
@@ -259,6 +297,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
},
{
.wai = ST_ACCEL_2_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS331DLH_ACCEL_DEV_NAME,
[1] = LSM303DL_ACCEL_DEV_NAME,
@@ -321,6 +360,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
},
{
.wai = ST_ACCEL_3_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LSM330_ACCEL_DEV_NAME,
},
@@ -399,6 +439,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
},
{
.wai = ST_ACCEL_4_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3LV02DL_ACCEL_DEV_NAME,
},
@@ -446,14 +487,59 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.drdy_irq = {
.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK,
- .ig1 = {
- .en_addr = ST_ACCEL_4_IG1_EN_ADDR,
- .en_mask = ST_ACCEL_4_IG1_EN_MASK,
- },
},
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
.bootime = 2, /* guess */
},
+ {
+ .wai = ST_ACCEL_5_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LIS331DL_ACCEL_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_accel_8bit_channels,
+ .odr = {
+ .addr = ST_ACCEL_5_ODR_ADDR,
+ .mask = ST_ACCEL_5_ODR_MASK,
+ .odr_avl = {
+ { 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL },
+ { 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_ACCEL_5_PW_ADDR,
+ .mask = ST_ACCEL_5_PW_MASK,
+ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis = {
+ .addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+ .mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+ },
+ .fs = {
+ .addr = ST_ACCEL_5_FS_ADDR,
+ .mask = ST_ACCEL_5_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_ACCEL_FS_AVL_2G,
+ .value = ST_ACCEL_5_FS_AVL_2_VAL,
+ .gain = ST_ACCEL_5_FS_AVL_2_GAIN,
+ },
+ [1] = {
+ .num = ST_ACCEL_FS_AVL_8G,
+ .value = ST_ACCEL_5_FS_AVL_8_VAL,
+ .gain = ST_ACCEL_5_FS_AVL_8_GAIN,
+ },
+ },
+ },
+ .drdy_irq = {
+ .addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
+ .mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
+ .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
+ },
+ .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
+ .bootime = 2, /* guess */
+ },
};
static int st_accel_read_raw(struct iio_dev *indio_dev,
@@ -526,6 +612,7 @@ static const struct iio_info accel_info = {
.attrs = &st_accel_attribute_group,
.read_raw = &st_accel_read_raw,
.write_raw = &st_accel_write_raw,
+ .debugfs_reg_access = &st_sensors_debugfs_reg_access,
};
#ifdef CONFIG_IIO_TRIGGER
diff --git a/kernel/drivers/iio/accel/st_accel_i2c.c b/kernel/drivers/iio/accel/st_accel_i2c.c
index 6b720c190..8b9cc84fd 100644
--- a/kernel/drivers/iio/accel/st_accel_i2c.c
+++ b/kernel/drivers/iio/accel/st_accel_i2c.c
@@ -49,6 +49,10 @@ static const struct of_device_id st_accel_of_match[] = {
.data = LSM330DLC_ACCEL_DEV_NAME,
},
{
+ .compatible = "st,lis331dl-accel",
+ .data = LIS331DL_ACCEL_DEV_NAME,
+ },
+ {
.compatible = "st,lis331dlh-accel",
.data = LIS331DLH_ACCEL_DEV_NAME,
},
@@ -64,6 +68,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lsm330-accel",
.data = LSM330_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lsm303agr-accel",
+ .data = LSM303AGR_ACCEL_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -112,13 +120,13 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LSM303DL_ACCEL_DEV_NAME },
{ LSM303DLM_ACCEL_DEV_NAME },
{ LSM330_ACCEL_DEV_NAME },
+ { LSM303AGR_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
static struct i2c_driver st_accel_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "st-accel-i2c",
.of_match_table = of_match_ptr(st_accel_of_match),
},
diff --git a/kernel/drivers/iio/accel/st_accel_spi.c b/kernel/drivers/iio/accel/st_accel_spi.c
index 12ec29389..f71b0d391 100644
--- a/kernel/drivers/iio/accel/st_accel_spi.c
+++ b/kernel/drivers/iio/accel/st_accel_spi.c
@@ -57,13 +57,13 @@ static const struct spi_device_id st_accel_id_table[] = {
{ LSM303DL_ACCEL_DEV_NAME },
{ LSM303DLM_ACCEL_DEV_NAME },
{ LSM330_ACCEL_DEV_NAME },
+ { LSM303AGR_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
static struct spi_driver st_accel_driver = {
.driver = {
- .owner = THIS_MODULE,
.name = "st-accel-spi",
},
.probe = st_accel_spi_probe,
diff --git a/kernel/drivers/iio/accel/stk8312.c b/kernel/drivers/iio/accel/stk8312.c
new file mode 100644
index 000000000..85fe7f724
--- /dev/null
+++ b/kernel/drivers/iio/accel/stk8312.c
@@ -0,0 +1,675 @@
+/**
+ * Sensortek STK8312 3-Axis Accelerometer
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for STK8312; 7-bit I2C address: 0x3D.
+ */
+
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.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>
+
+#define STK8312_REG_XOUT 0x00
+#define STK8312_REG_YOUT 0x01
+#define STK8312_REG_ZOUT 0x02
+#define STK8312_REG_INTSU 0x06
+#define STK8312_REG_MODE 0x07
+#define STK8312_REG_SR 0x08
+#define STK8312_REG_STH 0x13
+#define STK8312_REG_RESET 0x20
+#define STK8312_REG_AFECTRL 0x24
+#define STK8312_REG_OTPADDR 0x3D
+#define STK8312_REG_OTPDATA 0x3E
+#define STK8312_REG_OTPCTRL 0x3F
+
+#define STK8312_MODE_ACTIVE BIT(0)
+#define STK8312_MODE_STANDBY 0x00
+#define STK8312_MODE_INT_AH_PP 0xC0 /* active-high, push-pull */
+#define STK8312_DREADY_BIT BIT(4)
+#define STK8312_RNG_6G 1
+#define STK8312_RNG_SHIFT 6
+#define STK8312_RNG_MASK GENMASK(7, 6)
+#define STK8312_SR_MASK GENMASK(2, 0)
+#define STK8312_SR_400HZ_IDX 0
+#define STK8312_ALL_CHANNEL_MASK GENMASK(2, 0)
+#define STK8312_ALL_CHANNEL_SIZE 3
+
+#define STK8312_DRIVER_NAME "stk8312"
+#define STK8312_IRQ_NAME "stk8312_event"
+
+/*
+ * The accelerometer has two measurement ranges:
+ *
+ * -6g - +6g (8-bit, signed)
+ * -16g - +16g (8-bit, signed)
+ *
+ * scale1 = (6 + 6) * 9.81 / (2^8 - 1) = 0.4616
+ * scale2 = (16 + 16) * 9.81 / (2^8 - 1) = 1.2311
+ */
+#define STK8312_SCALE_AVAIL "0.4616 1.2311"
+
+static const int stk8312_scale_table[][2] = {
+ {0, 461600}, {1, 231100}
+};
+
+static const struct {
+ int val;
+ int val2;
+} stk8312_samp_freq_table[] = {
+ {400, 0}, {200, 0}, {100, 0}, {50, 0}, {25, 0},
+ {12, 500000}, {6, 250000}, {3, 125000}
+};
+
+#define STK8312_ACCEL_CHANNEL(index, reg, axis) { \
+ .type = IIO_ACCEL, \
+ .address = reg, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+static const struct iio_chan_spec stk8312_channels[] = {
+ STK8312_ACCEL_CHANNEL(0, STK8312_REG_XOUT, X),
+ STK8312_ACCEL_CHANNEL(1, STK8312_REG_YOUT, Y),
+ STK8312_ACCEL_CHANNEL(2, STK8312_REG_ZOUT, Z),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+struct stk8312_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ u8 range;
+ u8 sample_rate_idx;
+ u8 mode;
+ struct iio_trigger *dready_trig;
+ bool dready_trigger_on;
+ s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 64-bit timestamp */
+};
+
+static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("3.125 6.25 12.5 25 50 100 200 400");
+
+static struct attribute *stk8312_attributes[] = {
+ &iio_const_attr_in_accel_scale_available.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group stk8312_attribute_group = {
+ .attrs = stk8312_attributes
+};
+
+static int stk8312_otp_init(struct stk8312_data *data)
+{
+ int ret;
+ int count = 10;
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPADDR, 0x70);
+ if (ret < 0)
+ goto exit_err;
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPCTRL, 0x02);
+ if (ret < 0)
+ goto exit_err;
+
+ do {
+ usleep_range(1000, 5000);
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPCTRL);
+ if (ret < 0)
+ goto exit_err;
+ count--;
+ } while (!(ret & BIT(7)) && count > 0);
+
+ if (count == 0) {
+ ret = -ETIMEDOUT;
+ goto exit_err;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPDATA);
+ if (ret == 0)
+ ret = -EINVAL;
+ if (ret < 0)
+ goto exit_err;
+
+ ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_AFECTRL, ret);
+ if (ret < 0)
+ goto exit_err;
+ msleep(150);
+
+ return 0;
+
+exit_err:
+ dev_err(&client->dev, "failed to initialize sensor\n");
+ return ret;
+}
+
+static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+
+ if (mode == data->mode)
+ return 0;
+
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_MODE, mode);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to change sensor mode\n");
+ return ret;
+ }
+
+ data->mode = mode;
+ if (mode & STK8312_MODE_ACTIVE) {
+ /* Need to run OTP sequence before entering active mode */
+ usleep_range(1000, 5000);
+ ret = stk8312_otp_init(data);
+ }
+
+ return ret;
+}
+
+static int stk8312_set_interrupts(struct stk8312_data *data, u8 int_mask)
+{
+ int ret;
+ u8 mode;
+ struct i2c_client *client = data->client;
+
+ mode = data->mode;
+ /* We need to go in standby mode to modify registers */
+ ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_INTSU, int_mask);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to set interrupts\n");
+ stk8312_set_mode(data, mode);
+ return ret;
+ }
+
+ return stk8312_set_mode(data, mode);
+}
+
+static int stk8312_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stk8312_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (state)
+ ret = stk8312_set_interrupts(data, STK8312_DREADY_BIT);
+ else
+ ret = stk8312_set_interrupts(data, 0x00);
+
+ if (ret < 0) {
+ dev_err(&data->client->dev, "failed to set trigger state\n");
+ return ret;
+ }
+
+ data->dready_trigger_on = state;
+
+ return 0;
+}
+
+static const struct iio_trigger_ops stk8312_trigger_ops = {
+ .set_trigger_state = stk8312_data_rdy_trigger_set_state,
+ .owner = THIS_MODULE,
+};
+
+static int stk8312_set_sample_rate(struct stk8312_data *data, u8 rate)
+{
+ int ret;
+ u8 masked_reg;
+ u8 mode;
+ struct i2c_client *client = data->client;
+
+ if (rate == data->sample_rate_idx)
+ return 0;
+
+ mode = data->mode;
+ /* We need to go in standby mode to modify registers */
+ ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_SR);
+ if (ret < 0)
+ goto err_activate;
+
+ masked_reg = (ret & (~STK8312_SR_MASK)) | rate;
+
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_SR, masked_reg);
+ if (ret < 0)
+ goto err_activate;
+
+ data->sample_rate_idx = rate;
+
+ return stk8312_set_mode(data, mode);
+
+err_activate:
+ dev_err(&client->dev, "failed to set sampling rate\n");
+ stk8312_set_mode(data, mode);
+
+ return ret;
+}
+
+static int stk8312_set_range(struct stk8312_data *data, u8 range)
+{
+ int ret;
+ u8 masked_reg;
+ u8 mode;
+ struct i2c_client *client = data->client;
+
+ if (range != 1 && range != 2)
+ return -EINVAL;
+ else if (range == data->range)
+ return 0;
+
+ mode = data->mode;
+ /* We need to go in standby mode to modify registers */
+ ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
+ if (ret < 0)
+ goto err_activate;
+
+ masked_reg = ret & (~STK8312_RNG_MASK);
+ masked_reg |= range << STK8312_RNG_SHIFT;
+
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
+ if (ret < 0)
+ goto err_activate;
+
+ data->range = range;
+
+ return stk8312_set_mode(data, mode);
+
+err_activate:
+ dev_err(&client->dev, "failed to change sensor range\n");
+ stk8312_set_mode(data, mode);
+
+ return ret;
+}
+
+static int stk8312_read_accel(struct stk8312_data *data, u8 address)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+
+ if (address > 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, address);
+ if (ret < 0)
+ dev_err(&client->dev, "register read failed\n");
+
+ return ret;
+}
+
+static int stk8312_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stk8312_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&data->lock);
+ ret = stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ ret = stk8312_read_accel(data, chan->address);
+ if (ret < 0) {
+ stk8312_set_mode(data,
+ data->mode & (~STK8312_MODE_ACTIVE));
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ *val = sign_extend32(ret, 7);
+ ret = stk8312_set_mode(data,
+ data->mode & (~STK8312_MODE_ACTIVE));
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = stk8312_scale_table[data->range - 1][0];
+ *val2 = stk8312_scale_table[data->range - 1][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = stk8312_samp_freq_table[data->sample_rate_idx].val;
+ *val2 = stk8312_samp_freq_table[data->sample_rate_idx].val2;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static int stk8312_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int i;
+ int index = -1;
+ int ret;
+ struct stk8312_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
+ if (val == stk8312_scale_table[i][0] &&
+ val2 == stk8312_scale_table[i][1]) {
+ index = i + 1;
+ break;
+ }
+ if (index < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = stk8312_set_range(data, index);
+ mutex_unlock(&data->lock);
+
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < ARRAY_SIZE(stk8312_samp_freq_table); i++)
+ if (val == stk8312_samp_freq_table[i].val &&
+ val2 == stk8312_samp_freq_table[i].val2) {
+ index = i;
+ break;
+ }
+ if (index < 0)
+ return -EINVAL;
+ mutex_lock(&data->lock);
+ ret = stk8312_set_sample_rate(data, index);
+ mutex_unlock(&data->lock);
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info stk8312_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = stk8312_read_raw,
+ .write_raw = stk8312_write_raw,
+ .attrs = &stk8312_attribute_group,
+};
+
+static irqreturn_t stk8312_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stk8312_data *data = iio_priv(indio_dev);
+ int bit, ret, i = 0;
+
+ mutex_lock(&data->lock);
+ /*
+ * Do a bulk read if all channels are requested,
+ * from 0x00 (XOUT) to 0x02 (ZOUT)
+ */
+ if (*(indio_dev->active_scan_mask) == STK8312_ALL_CHANNEL_MASK) {
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ STK8312_REG_XOUT,
+ STK8312_ALL_CHANNEL_SIZE,
+ data->buffer);
+ if (ret < STK8312_ALL_CHANNEL_SIZE) {
+ dev_err(&data->client->dev, "register read failed\n");
+ mutex_unlock(&data->lock);
+ goto err;
+ }
+ } else {
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = stk8312_read_accel(data, bit);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto err;
+ }
+ data->buffer[i++] = ret;
+ }
+ }
+ mutex_unlock(&data->lock);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ pf->timestamp);
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stk8312_data_rdy_trig_poll(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct stk8312_data *data = iio_priv(indio_dev);
+
+ if (data->dready_trigger_on)
+ iio_trigger_poll(data->dready_trig);
+
+ return IRQ_HANDLED;
+}
+
+static int stk8312_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct stk8312_data *data = iio_priv(indio_dev);
+
+ return stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE);
+}
+
+static int stk8312_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct stk8312_data *data = iio_priv(indio_dev);
+
+ return stk8312_set_mode(data, data->mode & (~STK8312_MODE_ACTIVE));
+}
+
+static const struct iio_buffer_setup_ops stk8312_buffer_setup_ops = {
+ .preenable = stk8312_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = stk8312_buffer_postdisable,
+};
+
+static int stk8312_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct stk8312_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ i2c_set_clientdata(client, indio_dev);
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &stk8312_info;
+ indio_dev->name = STK8312_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = stk8312_channels;
+ indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
+
+ /* A software reset is recommended at power-on */
+ ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET, 0x00);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to reset sensor\n");
+ return ret;
+ }
+ data->sample_rate_idx = STK8312_SR_400HZ_IDX;
+ ret = stk8312_set_range(data, STK8312_RNG_6G);
+ if (ret < 0)
+ return ret;
+
+ ret = stk8312_set_mode(data,
+ STK8312_MODE_INT_AH_PP | STK8312_MODE_ACTIVE);
+ if (ret < 0)
+ return ret;
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ stk8312_data_rdy_trig_poll,
+ NULL,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ STK8312_IRQ_NAME,
+ indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "request irq %d failed\n",
+ client->irq);
+ goto err_power_off;
+ }
+
+ data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->dready_trig) {
+ ret = -ENOMEM;
+ goto err_power_off;
+ }
+
+ data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->ops = &stk8312_trigger_ops;
+ iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+ ret = iio_trigger_register(data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev, "iio trigger register failed\n");
+ goto err_power_off;
+ }
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ iio_pollfunc_store_time,
+ stk8312_trigger_handler,
+ &stk8312_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ goto err_trigger_unregister;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ goto err_buffer_cleanup;
+ }
+
+ return 0;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_trigger_unregister:
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+err_power_off:
+ stk8312_set_mode(data, STK8312_MODE_STANDBY);
+ return ret;
+}
+
+static int stk8312_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct stk8312_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+
+ return stk8312_set_mode(data, STK8312_MODE_STANDBY);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stk8312_suspend(struct device *dev)
+{
+ struct stk8312_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8312_set_mode(data, data->mode & (~STK8312_MODE_ACTIVE));
+}
+
+static int stk8312_resume(struct device *dev)
+{
+ struct stk8312_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8312_set_mode(data, data->mode | STK8312_MODE_ACTIVE);
+}
+
+static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
+
+#define STK8312_PM_OPS (&stk8312_pm_ops)
+#else
+#define STK8312_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id stk8312_i2c_id[] = {
+ {"STK8312", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id);
+
+static const struct acpi_device_id stk8312_acpi_id[] = {
+ {"STK8312", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
+
+static struct i2c_driver stk8312_driver = {
+ .driver = {
+ .name = STK8312_DRIVER_NAME,
+ .pm = STK8312_PM_OPS,
+ .acpi_match_table = ACPI_PTR(stk8312_acpi_id),
+ },
+ .probe = stk8312_probe,
+ .remove = stk8312_remove,
+ .id_table = stk8312_i2c_id,
+};
+
+module_i2c_driver(stk8312_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/kernel/drivers/iio/accel/stk8ba50.c b/kernel/drivers/iio/accel/stk8ba50.c
new file mode 100644
index 000000000..5709d9eb8
--- /dev/null
+++ b/kernel/drivers/iio/accel/stk8ba50.c
@@ -0,0 +1,571 @@
+/**
+ * Sensortek STK8BA50 3-Axis Accelerometer
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * STK8BA50 7-bit I2C address: 0x18.
+ */
+
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.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>
+
+#define STK8BA50_REG_XOUT 0x02
+#define STK8BA50_REG_YOUT 0x04
+#define STK8BA50_REG_ZOUT 0x06
+#define STK8BA50_REG_RANGE 0x0F
+#define STK8BA50_REG_BWSEL 0x10
+#define STK8BA50_REG_POWMODE 0x11
+#define STK8BA50_REG_SWRST 0x14
+#define STK8BA50_REG_INTEN2 0x17
+#define STK8BA50_REG_INTMAP2 0x1A
+
+#define STK8BA50_MODE_NORMAL 0
+#define STK8BA50_MODE_SUSPEND 1
+#define STK8BA50_MODE_POWERBIT BIT(7)
+#define STK8BA50_DATA_SHIFT 6
+#define STK8BA50_RESET_CMD 0xB6
+#define STK8BA50_SR_1792HZ_IDX 7
+#define STK8BA50_DREADY_INT_MASK 0x10
+#define STK8BA50_DREADY_INT_MAP 0x81
+#define STK8BA50_ALL_CHANNEL_MASK 7
+#define STK8BA50_ALL_CHANNEL_SIZE 6
+
+#define STK8BA50_DRIVER_NAME "stk8ba50"
+#define STK8BA50_IRQ_NAME "stk8ba50_event"
+
+#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
+
+/*
+ * The accelerometer has four measurement ranges:
+ * +/-2g; +/-4g; +/-8g; +/-16g
+ *
+ * Acceleration values are 10-bit, 2's complement.
+ * Scales are calculated as following:
+ *
+ * scale1 = (2 + 2) * 9.81 / (2^10 - 1) = 0.0384
+ * scale2 = (4 + 4) * 9.81 / (2^10 - 1) = 0.0767
+ * etc.
+ *
+ * Scales are stored in this format:
+ * { <register value>, <scale value> }
+ *
+ * Locally, the range is stored as a table index.
+ */
+static const struct {
+ u8 reg_val;
+ u32 scale_val;
+} stk8ba50_scale_table[] = {
+ {3, 38400}, {5, 76700}, {8, 153400}, {12, 306900}
+};
+
+/* Sample rates are stored as { <register value>, <Hz value> } */
+static const struct {
+ u8 reg_val;
+ u16 samp_freq;
+} stk8ba50_samp_freq_table[] = {
+ {0x08, 14}, {0x09, 25}, {0x0A, 56}, {0x0B, 112},
+ {0x0C, 224}, {0x0D, 448}, {0x0E, 896}, {0x0F, 1792}
+};
+
+/* Used to map scan mask bits to their corresponding channel register. */
+static const int stk8ba50_channel_table[] = {
+ STK8BA50_REG_XOUT,
+ STK8BA50_REG_YOUT,
+ STK8BA50_REG_ZOUT
+};
+
+struct stk8ba50_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ int range;
+ u8 sample_rate_idx;
+ struct iio_trigger *dready_trig;
+ bool dready_trigger_on;
+ /*
+ * 3 x 16-bit channels (10-bit data, 6-bit padding) +
+ * 1 x 16 padding +
+ * 4 x 16 64-bit timestamp
+ */
+ s16 buffer[8];
+};
+
+#define STK8BA50_ACCEL_CHANNEL(index, reg, axis) { \
+ .type = IIO_ACCEL, \
+ .address = reg, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 10, \
+ .storagebits = 16, \
+ .shift = STK8BA50_DATA_SHIFT, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+static const struct iio_chan_spec stk8ba50_channels[] = {
+ STK8BA50_ACCEL_CHANNEL(0, STK8BA50_REG_XOUT, X),
+ STK8BA50_ACCEL_CHANNEL(1, STK8BA50_REG_YOUT, Y),
+ STK8BA50_ACCEL_CHANNEL(2, STK8BA50_REG_ZOUT, Z),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static IIO_CONST_ATTR(in_accel_scale_available, STK8BA50_SCALE_AVAIL);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("14 25 56 112 224 448 896 1792");
+
+static struct attribute *stk8ba50_attributes[] = {
+ &iio_const_attr_in_accel_scale_available.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group stk8ba50_attribute_group = {
+ .attrs = stk8ba50_attributes
+};
+
+static int stk8ba50_read_accel(struct stk8ba50_data *data, u8 reg)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_read_word_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "register read failed\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int stk8ba50_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (state)
+ ret = i2c_smbus_write_byte_data(data->client,
+ STK8BA50_REG_INTEN2, STK8BA50_DREADY_INT_MASK);
+ else
+ ret = i2c_smbus_write_byte_data(data->client,
+ STK8BA50_REG_INTEN2, 0x00);
+
+ if (ret < 0)
+ dev_err(&data->client->dev, "failed to set trigger state\n");
+ else
+ data->dready_trigger_on = state;
+
+ return ret;
+}
+
+static const struct iio_trigger_ops stk8ba50_trigger_ops = {
+ .set_trigger_state = stk8ba50_data_rdy_trigger_set_state,
+ .owner = THIS_MODULE,
+};
+
+static int stk8ba50_set_power(struct stk8ba50_data *data, bool mode)
+{
+ int ret;
+ u8 masked_reg;
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_read_byte_data(client, STK8BA50_REG_POWMODE);
+ if (ret < 0)
+ goto exit_err;
+
+ if (mode)
+ masked_reg = ret | STK8BA50_MODE_POWERBIT;
+ else
+ masked_reg = ret & (~STK8BA50_MODE_POWERBIT);
+
+ ret = i2c_smbus_write_byte_data(client, STK8BA50_REG_POWMODE,
+ masked_reg);
+ if (ret < 0)
+ goto exit_err;
+
+ return ret;
+
+exit_err:
+ dev_err(&client->dev, "failed to change sensor mode\n");
+ return ret;
+}
+
+static int stk8ba50_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&data->lock);
+ ret = stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ return -EINVAL;
+ }
+ ret = stk8ba50_read_accel(data, chan->address);
+ if (ret < 0) {
+ stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+ mutex_unlock(&data->lock);
+ return -EINVAL;
+ }
+ *val = sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9);
+ stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+ mutex_unlock(&data->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = stk8ba50_scale_table[data->range].scale_val;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = stk8ba50_samp_freq_table
+ [data->sample_rate_idx].samp_freq;
+ *val2 = 0;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int stk8ba50_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+ int i;
+ int index = -1;
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(stk8ba50_scale_table); i++)
+ if (val2 == stk8ba50_scale_table[i].scale_val) {
+ index = i;
+ break;
+ }
+ if (index < 0)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ STK8BA50_REG_RANGE,
+ stk8ba50_scale_table[index].reg_val);
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "failed to set measurement range\n");
+ else
+ data->range = index;
+
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ for (i = 0; i < ARRAY_SIZE(stk8ba50_samp_freq_table); i++)
+ if (val == stk8ba50_samp_freq_table[i].samp_freq) {
+ index = i;
+ break;
+ }
+ if (index < 0)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ STK8BA50_REG_BWSEL,
+ stk8ba50_samp_freq_table[index].reg_val);
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "failed to set sampling rate\n");
+ else
+ data->sample_rate_idx = index;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info stk8ba50_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = stk8ba50_read_raw,
+ .write_raw = stk8ba50_write_raw,
+ .attrs = &stk8ba50_attribute_group,
+};
+
+static irqreturn_t stk8ba50_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+ int bit, ret, i = 0;
+
+ mutex_lock(&data->lock);
+ /*
+ * Do a bulk read if all channels are requested,
+ * from 0x02 (XOUT1) to 0x07 (ZOUT2)
+ */
+ if (*(indio_dev->active_scan_mask) == STK8BA50_ALL_CHANNEL_MASK) {
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ STK8BA50_REG_XOUT,
+ STK8BA50_ALL_CHANNEL_SIZE,
+ (u8 *)data->buffer);
+ if (ret < STK8BA50_ALL_CHANNEL_SIZE) {
+ dev_err(&data->client->dev, "register read failed\n");
+ goto err;
+ }
+ } else {
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = stk8ba50_read_accel(data,
+ stk8ba50_channel_table[bit]);
+ if (ret < 0)
+ goto err;
+
+ data->buffer[i++] = ret;
+ }
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ pf->timestamp);
+err:
+ mutex_unlock(&data->lock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t stk8ba50_data_rdy_trig_poll(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+
+ if (data->dready_trigger_on)
+ iio_trigger_poll(data->dready_trig);
+
+ return IRQ_HANDLED;
+}
+
+static int stk8ba50_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+
+ return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
+}
+
+static int stk8ba50_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+
+ return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+}
+
+static const struct iio_buffer_setup_ops stk8ba50_buffer_setup_ops = {
+ .preenable = stk8ba50_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = stk8ba50_buffer_postdisable,
+};
+
+static int stk8ba50_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct stk8ba50_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ i2c_set_clientdata(client, indio_dev);
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &stk8ba50_info;
+ indio_dev->name = STK8BA50_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = stk8ba50_channels;
+ indio_dev->num_channels = ARRAY_SIZE(stk8ba50_channels);
+
+ /* Reset all registers on startup */
+ ret = i2c_smbus_write_byte_data(client,
+ STK8BA50_REG_SWRST, STK8BA50_RESET_CMD);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to reset sensor\n");
+ goto err_power_off;
+ }
+
+ /* The default range is +/-2g */
+ data->range = 0;
+
+ /* The default sampling rate is 1792 Hz (maximum) */
+ data->sample_rate_idx = STK8BA50_SR_1792HZ_IDX;
+
+ /* Set up interrupts */
+ ret = i2c_smbus_write_byte_data(client,
+ STK8BA50_REG_INTEN2, STK8BA50_DREADY_INT_MASK);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to set up interrupts\n");
+ goto err_power_off;
+ }
+ ret = i2c_smbus_write_byte_data(client,
+ STK8BA50_REG_INTMAP2, STK8BA50_DREADY_INT_MAP);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to set up interrupts\n");
+ goto err_power_off;
+ }
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ stk8ba50_data_rdy_trig_poll,
+ NULL,
+ IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT,
+ STK8BA50_IRQ_NAME,
+ indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "request irq %d failed\n",
+ client->irq);
+ goto err_power_off;
+ }
+
+ data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->dready_trig) {
+ ret = -ENOMEM;
+ goto err_power_off;
+ }
+
+ data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->ops = &stk8ba50_trigger_ops;
+ iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+ ret = iio_trigger_register(data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev, "iio trigger register failed\n");
+ goto err_power_off;
+ }
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ iio_pollfunc_store_time,
+ stk8ba50_trigger_handler,
+ &stk8ba50_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ goto err_trigger_unregister;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ goto err_buffer_cleanup;
+ }
+
+ return ret;
+
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_trigger_unregister:
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+err_power_off:
+ stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+ return ret;
+}
+
+static int stk8ba50_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+
+ return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stk8ba50_suspend(struct device *dev)
+{
+ struct stk8ba50_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+}
+
+static int stk8ba50_resume(struct device *dev)
+{
+ struct stk8ba50_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
+}
+
+static SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume);
+
+#define STK8BA50_PM_OPS (&stk8ba50_pm_ops)
+#else
+#define STK8BA50_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id stk8ba50_i2c_id[] = {
+ {"stk8ba50", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, stk8ba50_i2c_id);
+
+static const struct acpi_device_id stk8ba50_acpi_id[] = {
+ {"STK8BA50", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id);
+
+static struct i2c_driver stk8ba50_driver = {
+ .driver = {
+ .name = "stk8ba50",
+ .pm = STK8BA50_PM_OPS,
+ .acpi_match_table = ACPI_PTR(stk8ba50_acpi_id),
+ },
+ .probe = stk8ba50_probe,
+ .remove = stk8ba50_remove,
+ .id_table = stk8ba50_i2c_id,
+};
+
+module_i2c_driver(stk8ba50_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("STK8BA50 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");