iio: improve usage of gpiod API
[firefly-linux-kernel-4.4.55.git] / drivers / iio / accel / bmc150-accel.c
index 066d0c04072c69943fa21313fb47f06c72fedcea..46ac9659e2aa11c76feee69346aee0def0ad2705 100644 (file)
@@ -269,6 +269,37 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
        return -EINVAL;
 }
 
+static int bmc150_accel_update_slope(struct bmc150_accel_data *data)
+{
+       int ret, val;
+
+       ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_6,
+                                       data->slope_thres);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error writing reg_int_6\n");
+               return ret;
+       }
+
+       ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_5);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error reading reg_int_5\n");
+               return ret;
+       }
+
+       val = (ret & ~BMC150_ACCEL_SLOPE_DUR_MASK) | data->slope_dur;
+       ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_5,
+                                       val);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error write reg_int_5\n");
+               return ret;
+       }
+
+       dev_dbg(&data->client->dev, "%s: %x %x\n", __func__, data->slope_thres,
+               data->slope_dur);
+
+       return ret;
+}
+
 static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
 {
        int ret;
@@ -307,32 +338,12 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
 
        data->range = BMC150_ACCEL_DEF_RANGE_4G;
 
-       /* Set default slope duration */
-       ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_5);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_int_5\n");
-               return ret;
-       }
-       data->slope_dur |= BMC150_ACCEL_DEF_SLOPE_DURATION;
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_5,
-                                       data->slope_dur);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_5\n");
-               return ret;
-       }
-       dev_dbg(&data->client->dev, "slope_dur %x\n", data->slope_dur);
-
-       /* Set default slope thresholds */
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_6,
-                                       BMC150_ACCEL_DEF_SLOPE_THRESHOLD);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_6\n");
-               return ret;
-       }
+       /* Set default slope duration and thresholds */
        data->slope_thres = BMC150_ACCEL_DEF_SLOPE_THRESHOLD;
-       dev_dbg(&data->client->dev, "slope_thres %x\n", data->slope_thres);
+       data->slope_dur = BMC150_ACCEL_DEF_SLOPE_DURATION;
+       ret = bmc150_accel_update_slope(data);
+       if (ret < 0)
+               return ret;
 
        /* Set default as latched interrupts */
        ret = i2c_smbus_write_byte_data(data->client,
@@ -348,155 +359,6 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
        return 0;
 }
 
-static int bmc150_accel_setup_any_motion_interrupt(
-                                       struct bmc150_accel_data *data,
-                                       bool status)
-{
-       int ret;
-
-       /* Enable/Disable INT1 mapping */
-       ret = i2c_smbus_read_byte_data(data->client,
-                                      BMC150_ACCEL_REG_INT_MAP_0);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_int_map_0\n");
-               return ret;
-       }
-       if (status)
-               ret |= BMC150_ACCEL_INT_MAP_0_BIT_SLOPE;
-       else
-               ret &= ~BMC150_ACCEL_INT_MAP_0_BIT_SLOPE;
-
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_MAP_0,
-                                       ret);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_map_0\n");
-               return ret;
-       }
-
-       if (status) {
-               /* Set slope duration (no of samples) */
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMC150_ACCEL_REG_INT_5,
-                                               data->slope_dur);
-               if (ret < 0) {
-                       dev_err(&data->client->dev, "Error write reg_int_5\n");
-                       return ret;
-               }
-
-               /* Set slope thresholds */
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMC150_ACCEL_REG_INT_6,
-                                               data->slope_thres);
-               if (ret < 0) {
-                       dev_err(&data->client->dev, "Error write reg_int_6\n");
-                       return ret;
-               }
-
-               /*
-                * New data interrupt is always non-latched,
-                * which will have higher priority, so no need
-                * to set latched mode, we will be flooded anyway with INTR
-                */
-               if (!data->dready_trigger_on) {
-                       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_RST_LATCH,
-                                       BMC150_ACCEL_INT_MODE_LATCH_INT |
-                                       BMC150_ACCEL_INT_MODE_LATCH_RESET);
-                       if (ret < 0) {
-                               dev_err(&data->client->dev,
-                                       "Error writing reg_int_rst_latch\n");
-                               return ret;
-                       }
-               }
-
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMC150_ACCEL_REG_INT_EN_0,
-                                               BMC150_ACCEL_INT_EN_BIT_SLP_X |
-                                               BMC150_ACCEL_INT_EN_BIT_SLP_Y |
-                                               BMC150_ACCEL_INT_EN_BIT_SLP_Z);
-       } else
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMC150_ACCEL_REG_INT_EN_0,
-                                               0);
-
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_en_0\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static int bmc150_accel_setup_new_data_interrupt(struct bmc150_accel_data *data,
-                                          bool status)
-{
-       int ret;
-
-       /* Enable/Disable INT1 mapping */
-       ret = i2c_smbus_read_byte_data(data->client,
-                                      BMC150_ACCEL_REG_INT_MAP_1);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error reading reg_int_map_1\n");
-               return ret;
-       }
-       if (status)
-               ret |= BMC150_ACCEL_INT_MAP_1_BIT_DATA;
-       else
-               ret &= ~BMC150_ACCEL_INT_MAP_1_BIT_DATA;
-
-       ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_MAP_1,
-                                       ret);
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_map_1\n");
-               return ret;
-       }
-
-       if (status) {
-               /*
-                * Set non latched mode interrupt and clear any latched
-                * interrupt
-                */
-               ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_RST_LATCH,
-                                       BMC150_ACCEL_INT_MODE_NON_LATCH_INT |
-                                       BMC150_ACCEL_INT_MODE_LATCH_RESET);
-               if (ret < 0) {
-                       dev_err(&data->client->dev,
-                               "Error writing reg_int_rst_latch\n");
-                       return ret;
-               }
-
-               ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_EN_1,
-                                       BMC150_ACCEL_INT_EN_BIT_DATA_EN);
-
-       } else {
-               /* Restore default interrupt mode */
-               ret = i2c_smbus_write_byte_data(data->client,
-                                       BMC150_ACCEL_REG_INT_RST_LATCH,
-                                       BMC150_ACCEL_INT_MODE_LATCH_INT |
-                                       BMC150_ACCEL_INT_MODE_LATCH_RESET);
-               if (ret < 0) {
-                       dev_err(&data->client->dev,
-                               "Error writing reg_int_rst_latch\n");
-                       return ret;
-               }
-
-               ret = i2c_smbus_write_byte_data(data->client,
-                                               BMC150_ACCEL_REG_INT_EN_1,
-                                               0);
-       }
-
-       if (ret < 0) {
-               dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
-               return ret;
-       }
-
-       return 0;
-}
-
 static int bmc150_accel_get_bw(struct bmc150_accel_data *data, int *val,
                               int *val2)
 {
@@ -554,6 +416,105 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
 }
 #endif
 
+static const struct bmc150_accel_interrupt_info {
+       u8 map_reg;
+       u8 map_bitmask;
+       u8 en_reg;
+       u8 en_bitmask;
+} bmc150_accel_interrupts[] = {
+       { /* data ready interrupt */
+               .map_reg = BMC150_ACCEL_REG_INT_MAP_1,
+               .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_DATA,
+               .en_reg = BMC150_ACCEL_REG_INT_EN_1,
+               .en_bitmask = BMC150_ACCEL_INT_EN_BIT_DATA_EN,
+       },
+       {  /* motion interrupt */
+               .map_reg = BMC150_ACCEL_REG_INT_MAP_0,
+               .map_bitmask = BMC150_ACCEL_INT_MAP_0_BIT_SLOPE,
+               .en_reg = BMC150_ACCEL_REG_INT_EN_0,
+               .en_bitmask =  BMC150_ACCEL_INT_EN_BIT_SLP_X |
+                       BMC150_ACCEL_INT_EN_BIT_SLP_Y |
+                       BMC150_ACCEL_INT_EN_BIT_SLP_Z
+       },
+};
+
+static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data,
+                               const struct bmc150_accel_interrupt_info *info,
+                                     bool state)
+{
+       int ret;
+
+       /*
+        * 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 = bmc150_accel_set_power_state(data, state);
+       if (ret < 0)
+               return ret;
+
+       /* map the interrupt to the appropriate pins */
+       ret = i2c_smbus_read_byte_data(data->client, info->map_reg);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error reading reg_int_map\n");
+               goto out_fix_power_state;
+       }
+       if (state)
+               ret |= info->map_bitmask;
+       else
+               ret &= ~info->map_bitmask;
+
+       ret = i2c_smbus_write_byte_data(data->client, info->map_reg,
+                                       ret);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error writing reg_int_map\n");
+               goto out_fix_power_state;
+       }
+
+       /* enable/disable the interrupt */
+       ret = i2c_smbus_read_byte_data(data->client, info->en_reg);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error reading reg_int_en\n");
+               goto out_fix_power_state;
+       }
+
+       if (state)
+               ret |= info->en_bitmask;
+       else
+               ret &= ~info->en_bitmask;
+
+       ret = i2c_smbus_write_byte_data(data->client, info->en_reg, ret);
+       if (ret < 0) {
+               dev_err(&data->client->dev, "Error writing reg_int_en\n");
+               goto out_fix_power_state;
+       }
+
+       return 0;
+
+out_fix_power_state:
+       bmc150_accel_set_power_state(data, false);
+       return ret;
+}
+
+static int bmc150_accel_setup_any_motion_interrupt(
+                                       struct bmc150_accel_data *data,
+                                       bool status)
+{
+       return bmc150_accel_set_interrupt(data, &bmc150_accel_interrupts[1],
+                                         status);
+}
+
+static int bmc150_accel_setup_new_data_interrupt(struct bmc150_accel_data *data,
+                                                bool status)
+{
+       return bmc150_accel_set_interrupt(data, &bmc150_accel_interrupts[0],
+                                         status);
+}
+
 static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
 {
        int ret, i;
@@ -732,7 +693,7 @@ static int bmc150_accel_read_event(struct iio_dev *indio_dev,
                *val = data->slope_thres;
                break;
        case IIO_EV_INFO_PERIOD:
-               *val = data->slope_dur & BMC150_ACCEL_SLOPE_DUR_MASK;
+               *val = data->slope_dur;
                break;
        default:
                return -EINVAL;
@@ -755,11 +716,10 @@ static int bmc150_accel_write_event(struct iio_dev *indio_dev,
 
        switch (info) {
        case IIO_EV_INFO_VALUE:
-               data->slope_thres = val;
+               data->slope_thres = val & 0xFF;
                break;
        case IIO_EV_INFO_PERIOD:
-               data->slope_dur &= ~BMC150_ACCEL_SLOPE_DUR_MASK;
-               data->slope_dur |= val & BMC150_ACCEL_SLOPE_DUR_MASK;
+               data->slope_dur = val & BMC150_ACCEL_SLOPE_DUR_MASK;
                break;
        default:
                return -EINVAL;
@@ -788,7 +748,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev,
        struct bmc150_accel_data *data = iio_priv(indio_dev);
        int ret;
 
-       if (state && data->ev_enable_state)
+       if (state == data->ev_enable_state)
                return 0;
 
        mutex_lock(&data->mutex);
@@ -799,25 +759,8 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev,
                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 = bmc150_accel_set_power_state(data, state);
-       if (ret < 0) {
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-
        ret =  bmc150_accel_setup_any_motion_interrupt(data, state);
        if (ret < 0) {
-               bmc150_accel_set_power_state(data, false);
                mutex_unlock(&data->mutex);
                return ret;
        }
@@ -1041,27 +984,33 @@ static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig,
 
        mutex_lock(&data->mutex);
 
+       if (data->motion_trig == trig) {
+               if (data->motion_trigger_on == state) {
+                       mutex_unlock(&data->mutex);
+                       return 0;
+               }
+       } else {
+               if (data->dready_trigger_on == state) {
+                       mutex_unlock(&data->mutex);
+                       return 0;
+               }
+       }
+
        if (!state && data->ev_enable_state && data->motion_trigger_on) {
                data->motion_trigger_on = false;
                mutex_unlock(&data->mutex);
                return 0;
        }
 
-       /*
-        * Refer to comment in bmc150_accel_write_event_config for
-        * enable/disable operation order
-        */
-       ret = bmc150_accel_set_power_state(data, state);
-       if (ret < 0) {
-               mutex_unlock(&data->mutex);
-               return ret;
-       }
-       if (data->motion_trig == trig)
-               ret =  bmc150_accel_setup_any_motion_interrupt(data, state);
-       else
+       if (data->motion_trig == trig) {
+               ret = bmc150_accel_update_slope(data);
+               if (!ret)
+                       ret = bmc150_accel_setup_any_motion_interrupt(data,
+                                                                     state);
+       } else {
                ret = bmc150_accel_setup_new_data_interrupt(data, state);
+       }
        if (ret < 0) {
-               bmc150_accel_set_power_state(data, false);
                mutex_unlock(&data->mutex);
                return ret;
        }
@@ -1176,16 +1125,12 @@ static int bmc150_accel_gpio_probe(struct i2c_client *client,
        dev = &client->dev;
 
        /* data ready gpio interrupt pin */
-       gpio = devm_gpiod_get_index(dev, BMC150_ACCEL_GPIO_NAME, 0);
+       gpio = devm_gpiod_get_index(dev, BMC150_ACCEL_GPIO_NAME, 0, GPIOD_IN);
        if (IS_ERR(gpio)) {
                dev_err(dev, "Failed: gpio get index\n");
                return PTR_ERR(gpio);
        }
 
-       ret = gpiod_direction_input(gpio);
-       if (ret)
-               return ret;
-
        ret = gpiod_to_irq(gpio);
 
        dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
@@ -1247,6 +1192,20 @@ static int bmc150_accel_probe(struct i2c_client *client,
                if (ret)
                        return ret;
 
+               /*
+                * Set latched mode interrupt. While certain interrupts are
+                * non-latched regardless of this settings (e.g. new data) we
+                * want to use latch mode when we can to prevent interrupt
+                * flooding.
+                */
+               ret = i2c_smbus_write_byte_data(data->client,
+                                               BMC150_ACCEL_REG_INT_RST_LATCH,
+                                            BMC150_ACCEL_INT_MODE_LATCH_RESET);
+               if (ret < 0) {
+                       dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
+                       return ret;
+               }
+
                data->dready_trig = devm_iio_trigger_alloc(&client->dev,
                                                           "%s-dev%d",
                                                           indio_dev->name,