All of lore.kernel.org
 help / color / mirror / Atom feed
From: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
To: Jonathan Cameron <jic23@kernel.org>
Cc: linux-iio@vger.kernel.org
Subject: Re: [PATCH 5/6] iio: accel: kxcjk-1013: Support thresholds
Date: Sun, 20 Jul 2014 09:34:11 -0700	[thread overview]
Message-ID: <53CBEF83.5030805@linux.intel.com> (raw)
In-Reply-To: <53CBE883.1080403@kernel.org>


On 07/20/2014 09:04 AM, Jonathan Cameron wrote:
> On 17/07/14 01:42, Srinivas Pandruvada wrote:
>> 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.
> But not both?  This had me confused below.
>
The purpose of this change is so that CPU doesn't wake up to process
interrupt when there is no significant change, when user space application
can tolerate by using event thresholds. This tolerance depends on use case.

If you enable both, then this is not meaningful as data ready will generate
continuous interrupts anyway taking precedence.

> Also, the event description is incorrect for conventional motion 
> detection
> (magnitude rising detection).
>
> J
>> Using events mechanism to allow configuration and receive events.
>>
>> Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
>> ---
>>   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) {
> rather than ev_enable_state?
We don't want to depend on event enable when there is no threshold set, 
events will have
no meaning.
>> +        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;
>> +
> Does this mean the dataready interrupt is disabled whenever thresholds
> are non zero?  This needs some explanation.
>
Data ready will take precedence and will send periodic interrupts.
>>       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);
>> +
> Perhaps check val2 == 0
OK
>> +    switch (info) {
>> +    case IIO_EV_INFO_VALUE:
>> +        data->wake_thres = val;
>> +        break;
>> +    case IIO_EV_INFO_PERIOD:
>> +        data->wake_dur    = val;
> Double space above.
>> +        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,
> As stated below I'm a little doubtful this is correct.
>> +        .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;
>> +
> Slight preference for a comment here saying what the event is...
>> +    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),
> Really? Note this means that a fall in the magnitude will trigger the 
> event
> as well as a rise?  (e.g. stop of a previously existing motion).
>
> It's possible but seems unlikely.  I'm guessing you want IIO_EV_TYPE_MAG
> and IIO_EV_DIR_RISING.
Makes sense. I interpret as x,y, z, change in any direction will result 
in event.
I will correct this.
>> + 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);
>>
>>
>
>


  reply	other threads:[~2014-07-20 16:34 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-07-17  0:42 [PATCH 1/6] iio: accel: kxcjk-1013: Fix setting frequency Srinivas Pandruvada
2014-07-17  0:42 ` [PATCH 2/6] iio: accel: kxcjk-1013: Use try_reenable to ack intr Srinivas Pandruvada
2014-07-20 15:20   ` Jonathan Cameron
2014-07-17  0:42 ` [PATCH 3/6] iio: accel: kxcjk-1013: support runtime pm Srinivas Pandruvada
2014-07-20 15:37   ` Jonathan Cameron
2014-07-22  1:23     ` Rafael J. Wysocki
2014-07-27 13:42       ` Jonathan Cameron
2014-07-27 14:26         ` Srinivas Pandruvada
2014-07-17  0:42 ` [PATCH 4/6] iio: accel: kxcjk-1013: Set adjustable range Srinivas Pandruvada
2014-07-20 15:45   ` Jonathan Cameron
2014-07-24 23:04     ` Srinivas Pandruvada
2014-07-27 13:51       ` Jonathan Cameron
2014-07-17  0:42 ` [PATCH 5/6] iio: accel: kxcjk-1013: Support thresholds Srinivas Pandruvada
2014-07-20 16:04   ` Jonathan Cameron
2014-07-20 16:34     ` Srinivas Pandruvada [this message]
2014-07-20 17:01       ` Jonathan Cameron
2014-07-17  0:42 ` [PATCH 6/6] iio: accel: kxcjk-1013: Increment ref counter for indio_dev->trig Srinivas Pandruvada
2014-07-20 11:29   ` Lars-Peter Clausen
2014-07-20 12:24     ` Jonathan Cameron
2014-07-20 15:39   ` Jonathan Cameron
2014-07-20 15:19 ` [PATCH 1/6] iio: accel: kxcjk-1013: Fix setting frequency Jonathan Cameron

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=53CBEF83.5030805@linux.intel.com \
    --to=srinivas.pandruvada@linux.intel.com \
    --cc=jic23@kernel.org \
    --cc=linux-iio@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.