aboutsummaryrefslogtreecommitdiff
path: root/drivers/iio/accel/kxcjk-1013.c
diff options
context:
space:
mode:
authorSrinivas Pandruvada2014-08-22 20:01:00 +0100
committerJonathan Cameron2014-08-26 21:07:35 +0100
commitb4b491c0832ef90a7a5070e5975bc8427f2049ca (patch)
tree8c4a6a15c70acc82384d9d01673d1f5c4fdcd49f /drivers/iio/accel/kxcjk-1013.c
parentb5faca4b59ab604cd3ff367683a96c330b78d25f (diff)
iio: accel: kxcjk-1013: Support thresholds
This chip has a motion detect capability. Using IIO events to specify thresholds and pushing events. In addition a new trigger of type any-motion is added, which pushes data to buffer only when there is any movement. Change list: Comments addressed for Re: [PATCH 5/6] iio: accel: kxcjk-1013: Support thresholds Date: 07/20/2014 - Both motion detect and data ready can be enabled together - Sending RISING/FALLING events based on int status - Separate interrupt configuration for data ready and motion detect Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio/accel/kxcjk-1013.c')
-rw-r--r--drivers/iio/accel/kxcjk-1013.c497
1 files changed, 447 insertions, 50 deletions
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index ae7429385f92..647a4cfb2ae7 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -27,6 +27,7 @@
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
+#include <linux/iio/events.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/accel/kxcjk_1013.h>
@@ -75,16 +76,30 @@
#define KXCJK1013_SLEEP_DELAY_MS 2000
+#define KXCJK1013_REG_INT_SRC2_BIT_ZP BIT(0)
+#define KXCJK1013_REG_INT_SRC2_BIT_ZN BIT(1)
+#define KXCJK1013_REG_INT_SRC2_BIT_YP BIT(2)
+#define KXCJK1013_REG_INT_SRC2_BIT_YN BIT(3)
+#define KXCJK1013_REG_INT_SRC2_BIT_XP BIT(4)
+#define KXCJK1013_REG_INT_SRC2_BIT_XN BIT(5)
+
+#define KXCJK1013_DEFAULT_WAKE_THRES 1
+
struct kxcjk1013_data {
struct i2c_client *client;
- struct iio_trigger *trig;
- bool trig_mode;
+ struct iio_trigger *dready_trig;
+ struct iio_trigger *motion_trig;
struct mutex mutex;
s16 buffer[8];
u8 odr_bits;
u8 range;
+ int wake_thres;
+ int wake_dur;
bool active_high_intr;
- bool trigger_on;
+ bool dready_trigger_on;
+ int ev_enable_state;
+ bool motion_trigger_on;
+ int64_t timestamp;
};
enum kxcjk1013_axis {
@@ -131,6 +146,23 @@ static const struct {
{19163, 1, 0},
{38326, 0, 1} };
+static const struct {
+ int val;
+ int val2;
+ int odr_bits;
+} wake_odr_data_rate_table[] = { {0, 781000, 0x00},
+ {1, 563000, 0x01},
+ {3, 125000, 0x02},
+ {6, 250000, 0x03},
+ {12, 500000, 0x04},
+ {25, 0, 0x05},
+ {50, 0, 0x06},
+ {100, 0, 0x06},
+ {200, 0, 0x06},
+ {400, 0, 0x06},
+ {800, 0, 0x06},
+ {1600, 0, 0x06} };
+
static int kxcjk1013_set_mode(struct kxcjk1013_data *data,
enum kxcjk1013_mode mode)
{
@@ -270,6 +302,8 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
if (ret < 0)
return ret;
+ data->wake_thres = KXCJK1013_DEFAULT_WAKE_THRES;
+
return 0;
}
@@ -304,8 +338,96 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
return 0;
}
-static int kxcjk1013_chip_setup_interrupt(struct kxcjk1013_data *data,
- bool status)
+static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ KXCJK1013_REG_WAKE_TIMER,
+ data->wake_dur);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Error writing reg_wake_timer\n");
+ return ret;
+ }
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ KXCJK1013_REG_WAKE_THRES,
+ data->wake_thres);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_wake_thres\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data,
+ bool status)
+{
+ int ret;
+ enum kxcjk1013_mode store_mode;
+
+ ret = kxcjk1013_get_mode(data, &store_mode);
+ if (ret < 0)
+ return ret;
+
+ /* This is requirement by spec to change state to STANDBY */
+ ret = kxcjk1013_set_mode(data, STANDBY);
+ if (ret < 0)
+ return ret;
+
+ ret = kxcjk1013_chip_update_thresholds(data);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n");
+ return ret;
+ }
+
+ if (status)
+ ret |= KXCJK1013_REG_INT_REG1_BIT_IEN;
+ else
+ ret &= ~KXCJK1013_REG_INT_REG1_BIT_IEN;
+
+ ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1,
+ ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+ return ret;
+ }
+
+ if (status)
+ ret |= KXCJK1013_REG_CTRL1_BIT_WUFE;
+ else
+ ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ KXCJK1013_REG_CTRL1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+ return ret;
+ }
+
+ if (store_mode == OPERATION) {
+ ret = kxcjk1013_set_mode(data, OPERATION);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data,
+ bool status)
{
int ret;
enum kxcjk1013_mode store_mode;
@@ -378,6 +500,20 @@ static int kxcjk1013_convert_freq_to_bit(int val, int val2)
return -EINVAL;
}
+static int kxcjk1013_convert_wake_odr_to_bit(int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wake_odr_data_rate_table); ++i) {
+ if (wake_odr_data_rate_table[i].val == val &&
+ wake_odr_data_rate_table[i].val2 == val2) {
+ return wake_odr_data_rate_table[i].odr_bits;
+ }
+ }
+
+ return -EINVAL;
+}
+
static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2)
{
int ret;
@@ -406,6 +542,17 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2)
data->odr_bits = odr_bits;
+ odr_bits = kxcjk1013_convert_wake_odr_to_bit(val, val2);
+ if (odr_bits < 0)
+ return odr_bits;
+
+ ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL2,
+ odr_bits);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_ctrl2\n");
+ return ret;
+ }
+
if (store_mode == OPERATION) {
ret = kxcjk1013_set_mode(data, OPERATION);
if (ret < 0)
@@ -557,12 +704,120 @@ static int kxcjk1013_write_raw(struct iio_dev *indio_dev,
return ret;
}
+static int kxcjk1013_read_event(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 kxcjk1013_data *data = iio_priv(indio_dev);
+
+ *val2 = 0;
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ *val = data->wake_thres;
+ break;
+ case IIO_EV_INFO_PERIOD:
+ *val = data->wake_dur;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int kxcjk1013_write_event(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 kxcjk1013_data *data = iio_priv(indio_dev);
+
+ if (data->ev_enable_state)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ data->wake_thres = val;
+ break;
+ case IIO_EV_INFO_PERIOD:
+ data->wake_dur = val;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int kxcjk1013_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 kxcjk1013_data *data = iio_priv(indio_dev);
+
+ return data->ev_enable_state;
+}
+
+static int kxcjk1013_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 kxcjk1013_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (state && data->ev_enable_state)
+ return 0;
+
+ mutex_lock(&data->mutex);
+
+ if (!state && data->motion_trigger_on) {
+ data->ev_enable_state = 0;
+ mutex_unlock(&data->mutex);
+ return 0;
+ }
+
+ /*
+ * 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
+ */
+ ret = kxcjk1013_set_power_state(data, state);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+
+ ret = kxcjk1013_setup_any_motion_interrupt(data, state);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+
+ data->ev_enable_state = state;
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev,
struct iio_trigger *trig)
{
struct kxcjk1013_data *data = iio_priv(indio_dev);
- if (data->trig != trig)
+ if (data->dready_trig != trig && data->motion_trig != trig)
return -EINVAL;
return 0;
@@ -583,6 +838,14 @@ static const struct attribute_group kxcjk1013_attrs_group = {
.attrs = kxcjk1013_attributes,
};
+static const struct iio_event_spec kxcjk1013_event = {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD)
+};
+
#define KXCJK1013_CHANNEL(_axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -598,6 +861,8 @@ static const struct attribute_group kxcjk1013_attrs_group = {
.shift = 4, \
.endianness = IIO_CPU, \
}, \
+ .event_spec = &kxcjk1013_event, \
+ .num_event_specs = 1 \
}
static const struct iio_chan_spec kxcjk1013_channels[] = {
@@ -611,6 +876,10 @@ static const struct iio_info kxcjk1013_info = {
.attrs = &kxcjk1013_attrs_group,
.read_raw = kxcjk1013_read_raw,
.write_raw = kxcjk1013_write_raw,
+ .read_event_value = kxcjk1013_read_event,
+ .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,
};
@@ -636,7 +905,7 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p)
mutex_unlock(&data->mutex);
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
- pf->timestamp);
+ data->timestamp);
err:
iio_trigger_notify_done(indio_dev->trig);
@@ -665,19 +934,32 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig,
struct kxcjk1013_data *data = iio_priv(indio_dev);
int ret;
- if (state && data->trigger_on)
+ mutex_lock(&data->mutex);
+
+ if (!state && data->ev_enable_state && data->motion_trigger_on) {
+ data->motion_trigger_on = false;
+ mutex_unlock(&data->mutex);
return 0;
+ }
- mutex_lock(&data->mutex);
- ret = kxcjk1013_chip_setup_interrupt(data, state);
- if (!ret) {
- ret = kxcjk1013_set_power_state(data, state);
- if (ret < 0) {
- mutex_unlock(&data->mutex);
- return ret;
- }
+ ret = kxcjk1013_set_power_state(data, state);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
}
- data->trigger_on = state;
+ if (data->motion_trig == trig)
+ ret = kxcjk1013_setup_any_motion_interrupt(data, state);
+ else
+ ret = kxcjk1013_setup_new_data_interrupt(data, state);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ if (data->motion_trig == trig)
+ data->motion_trigger_on = state;
+ else
+ data->dready_trigger_on = state;
+
mutex_unlock(&data->mutex);
return 0;
@@ -689,6 +971,109 @@ static const struct iio_trigger_ops kxcjk1013_trigger_ops = {
.owner = THIS_MODULE,
};
+static irqreturn_t kxcjk1013_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct kxcjk1013_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_SRC1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_int_src1\n");
+ goto ack_intr;
+ }
+
+ if (ret & 0x02) {
+ ret = i2c_smbus_read_byte_data(data->client,
+ KXCJK1013_REG_INT_SRC2);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Error reading reg_int_src2\n");
+ goto ack_intr;
+ }
+
+ if (ret & KXCJK1013_REG_INT_SRC2_BIT_XN)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ data->timestamp);
+ if (ret & KXCJK1013_REG_INT_SRC2_BIT_XP)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ data->timestamp);
+
+
+ if (ret & KXCJK1013_REG_INT_SRC2_BIT_YN)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ data->timestamp);
+ if (ret & KXCJK1013_REG_INT_SRC2_BIT_YP)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ data->timestamp);
+
+ if (ret & KXCJK1013_REG_INT_SRC2_BIT_ZN)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ data->timestamp);
+ if (ret & KXCJK1013_REG_INT_SRC2_BIT_ZP)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ data->timestamp);
+ }
+
+ack_intr:
+ if (data->dready_trigger_on)
+ return IRQ_HANDLED;
+
+ ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error reading reg_int_rel\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct kxcjk1013_data *data = iio_priv(indio_dev);
+
+ data->timestamp = iio_get_time_ns();
+
+ if (data->dready_trigger_on)
+ iio_trigger_poll(data->dready_trig);
+ else if (data->motion_trigger_on)
+ iio_trigger_poll(data->motion_trig);
+
+ if (data->ev_enable_state)
+ return IRQ_WAKE_THREAD;
+ else
+ return IRQ_HANDLED;
+}
+
static int kxcjk1013_acpi_gpio_probe(struct i2c_client *client,
struct kxcjk1013_data *data)
{
@@ -731,7 +1116,6 @@ static int kxcjk1013_probe(struct i2c_client *client,
{
struct kxcjk1013_data *data;
struct iio_dev *indio_dev;
- struct iio_trigger *trig = NULL;
struct kxcjk_1013_platform_data *pdata;
int ret;
@@ -766,33 +1150,46 @@ static int kxcjk1013_probe(struct i2c_client *client,
client->irq = kxcjk1013_acpi_gpio_probe(client, data);
if (client->irq >= 0) {
- trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
- indio_dev->id);
- if (!trig)
- return -ENOMEM;
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ kxcjk1013_data_rdy_trig_poll,
+ kxcjk1013_event_handler,
+ IRQF_TRIGGER_RISING,
+ KXCJK1013_IRQ_NAME,
+ indio_dev);
+ if (ret)
+ return ret;
- data->trig_mode = true;
+ 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_irq(&client->dev, client->irq,
- iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_RISING,
- KXCJK1013_IRQ_NAME,
- trig);
- if (ret) {
- dev_err(&client->dev, "unable to request IRQ\n");
- goto err_trigger_free;
- }
+ data->motion_trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-any-motion-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->motion_trig)
+ return -ENOMEM;
- trig->dev.parent = &client->dev;
- trig->ops = &kxcjk1013_trigger_ops;
- iio_trigger_set_drvdata(trig, indio_dev);
- data->trig = trig;
- indio_dev->trig = trig;
+ data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->ops = &kxcjk1013_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(trig);
+ ret = iio_trigger_register(data->dready_trig);
if (ret)
- goto err_trigger_free;
+ return ret;
+
+ data->motion_trig->dev.parent = &client->dev;
+ data->motion_trig->ops = &kxcjk1013_trigger_ops;
+ iio_trigger_set_drvdata(data->motion_trig, indio_dev);
+ ret = iio_trigger_register(data->motion_trig);
+ if (ret) {
+ data->motion_trig = NULL;
+ goto err_trigger_unregister;
+ }
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
@@ -825,14 +1222,13 @@ static int kxcjk1013_probe(struct i2c_client *client,
err_iio_unregister:
iio_device_unregister(indio_dev);
err_buffer_cleanup:
- if (data->trig_mode)
+ if (data->dready_trig)
iio_triggered_buffer_cleanup(indio_dev);
err_trigger_unregister:
- if (data->trig_mode)
- iio_trigger_unregister(trig);
-err_trigger_free:
- if (data->trig_mode)
- iio_trigger_free(trig);
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+ if (data->motion_trig)
+ iio_trigger_unregister(data->motion_trig);
return ret;
}
@@ -848,10 +1244,10 @@ static int kxcjk1013_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
- if (data->trig_mode) {
+ if (data->dready_trig) {
iio_triggered_buffer_cleanup(indio_dev);
- iio_trigger_unregister(data->trig);
- iio_trigger_free(data->trig);
+ iio_trigger_unregister(data->dready_trig);
+ iio_trigger_unregister(data->motion_trig);
}
mutex_lock(&data->mutex);
@@ -883,7 +1279,8 @@ static int kxcjk1013_resume(struct device *dev)
mutex_lock(&data->mutex);
/* Check, if the suspend occured while active */
- if (data->trigger_on)
+ if (data->dready_trigger_on || data->motion_trigger_on ||
+ data->ev_enable_state)
ret = kxcjk1013_set_mode(data, OPERATION);
mutex_unlock(&data->mutex);