On 22/08/14 20:01, Srinivas Pandruvada wrote: > From: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx> > > 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@xxxxxxxxxxxxxxx> Looks good - a couple of idle thoughts inline. Applied to the togreg branch of iio.git. Thanks, Jonathan > --- > drivers/iio/accel/kxcjk-1013.c | 499 ++++++++++++++++++++++++++++++++++++----- > 1 file changed, 448 insertions(+), 51 deletions(-) > > diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c > index 57c515b..b4d6d7a 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) > { > @@ -241,7 +273,6 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) > > data->range = KXCJK1013_RANGE_4G; > > - > ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_DATA_CTRL); > if (ret < 0) { > dev_err(&data->client->dev, "Error reading reg_data_ctrl\n"); > @@ -273,6 +304,8 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) > if (ret < 0) > return ret; > > + data->wake_thres = KXCJK1013_DEFAULT_WAKE_THRES; > + > return 0; > } > > @@ -307,8 +340,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; > @@ -381,6 +502,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; > @@ -409,6 +544,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) > @@ -560,12 +706,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; > @@ -586,6 +840,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, \ > @@ -601,6 +863,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[] = { > @@ -614,6 +878,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, > }; > @@ -639,7 +907,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); > > @@ -668,19 +936,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; > @@ -692,6 +973,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; > + } > + Could do this whole set as a combination of for_each_set_bit and a lookup table for the event codes (or the elements used to build them). Would make it a little more apparent that this is really picking between constant values... Mind you, might well prove more complex than what we have here! > + 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) > { > @@ -734,7 +1118,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; > > @@ -769,33 +1152,47 @@ 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( Why the new line before the arguements? > + &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, > @@ -828,14 +1225,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; > } > @@ -851,10 +1247,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); > @@ -886,7 +1282,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); > > -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html