Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4b491c0 authored by Srinivas Pandruvada's avatar Srinivas Pandruvada Committed by Jonathan Cameron
Browse files

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: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent b5faca4b
Loading
Loading
Loading
Loading
+447 −50
Original line number Diff line number Diff line
@@ -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,7 +338,95 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
	return 0;
}

static int kxcjk1013_chip_setup_interrupt(struct kxcjk1013_data *data,
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;
@@ -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;
	}
	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;
	}
	data->trigger_on = state;
	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,
		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->dready_trig = devm_iio_trigger_alloc(&client->dev,
							   "%s-dev%d",
							   indio_dev->name,
							   indio_dev->id);
		if (!trig)
		if (!data->dready_trig)
			return -ENOMEM;

		data->trig_mode = true;

		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);