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

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
......@@ -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);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment