This chip can operate in two modes. In one mode it can issue periodic interrupts based on sample rate setting or when there is a motion. Using events mechanism to allow configuration and receive events. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@xxxxxxxxxxxxxxx> --- drivers/iio/accel/kxcjk-1013.c | 238 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 230 insertions(+), 8 deletions(-) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 975f8a6..f5bb682 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> @@ -92,6 +93,9 @@ struct kxcjk1013_data { u8 odr_bits; u8 range; bool active_high_intr; + int ev_enable_state; + int wake_thres; + int wake_dur; bool trigger_on; }; @@ -116,6 +120,23 @@ static const struct { {200, 0, 0x04}, {400, 0, 0x05}, {800, 0, 0x06}, {1600, 0, 0x07} }; +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} }; + /* Refer to section 4 of the specification */ static const struct { int odr_bits; @@ -283,10 +304,36 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) return 0; } +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_chip_setup_interrupt(struct kxcjk1013_data *data, bool status) { int ret; + u8 intr_bit; enum kxcjk1013_mode store_mode; ret = kxcjk1013_get_mode(data, &store_mode); @@ -316,6 +363,16 @@ static int kxcjk1013_chip_setup_interrupt(struct kxcjk1013_data *data, return ret; } + if (data->wake_thres) { + if (status) { + ret = kxcjk1013_chip_update_thresholds(data); + if (ret < 0) + return ret; + } + intr_bit = KXCJK1013_REG_CTRL1_BIT_WUFE; + } else + intr_bit = KXCJK1013_REG_CTRL1_BIT_DRDY; + ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); @@ -323,9 +380,10 @@ static int kxcjk1013_chip_setup_interrupt(struct kxcjk1013_data *data, } if (status) - ret |= KXCJK1013_REG_CTRL1_BIT_DRDY; + ret |= intr_bit; else - ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; + ret &= ~(KXCJK1013_REG_CTRL1_BIT_WUFE | + KXCJK1013_REG_CTRL1_BIT_DRDY); ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL1, ret); @@ -357,6 +415,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; @@ -385,6 +457,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) @@ -567,6 +650,94 @@ 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); + + 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 (data->trigger_on) + return -EAGAIN; + + if (state && data->ev_enable_state) + 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; + } + } + 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) { @@ -593,6 +764,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_EITHER, + .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, \ @@ -608,6 +787,8 @@ static const struct attribute_group kxcjk1013_attrs_group = { .shift = 4, \ .endianness = IIO_LE, \ }, \ + .event_spec = &kxcjk1013_event, \ + .num_event_specs = 1 \ } static const struct iio_chan_spec kxcjk1013_channels[] = { @@ -621,6 +802,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, }; @@ -675,6 +860,9 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig, struct kxcjk1013_data *data = iio_priv(indio_dev); int ret; + if (data->ev_enable_state) + return -EAGAIN; + if (state && data->trigger_on) return 0; @@ -699,6 +887,38 @@ 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; + + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns()); + + 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); + + if (data->trigger_on) { + iio_trigger_poll(data->trig); + return IRQ_HANDLED; + } else + return IRQ_WAKE_THREAD; +} + static int kxcjk1013_acpi_gpio_probe(struct i2c_client *client, struct kxcjk1013_data *data) { @@ -783,11 +1003,13 @@ static int kxcjk1013_probe(struct i2c_client *client, 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); + ret = devm_request_threaded_irq(&client->dev, client->irq, + kxcjk1013_data_rdy_trig_poll, + kxcjk1013_event_handler, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + KXCJK1013_IRQ_NAME, + indio_dev); if (ret) { dev_err(&client->dev, "unable to request IRQ\n"); goto err_trigger_free; @@ -889,7 +1111,7 @@ static int kxcjk1013_resume(struct device *dev) mutex_lock(&data->mutex); /* Check, if the suspend occured while active */ - if (data->trigger_on) + if (data->trigger_on || data->ev_enable_state) ret = kxcjk1013_set_mode(data, OPERATION); mutex_unlock(&data->mutex); -- 1.7.11.7 -- 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