diff options
Diffstat (limited to 'drivers/iio')
39 files changed, 2346 insertions, 467 deletions
diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 75567fd..73e8773 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -70,7 +70,9 @@ #define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2) #define BMC150_ACCEL_REG_INT_MAP_1 0x1A -#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0) +#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0) +#define BMC150_ACCEL_INT_MAP_1_BIT_FWM BIT(1) +#define BMC150_ACCEL_INT_MAP_1_BIT_FFULL BIT(2) #define BMC150_ACCEL_REG_INT_RST_LATCH 0x21 #define BMC150_ACCEL_INT_MODE_LATCH_RESET 0x80 @@ -83,7 +85,9 @@ #define BMC150_ACCEL_INT_EN_BIT_SLP_Z BIT(2) #define BMC150_ACCEL_REG_INT_EN_1 0x17 -#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4) +#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4) +#define BMC150_ACCEL_INT_EN_BIT_FFULL_EN BIT(5) +#define BMC150_ACCEL_INT_EN_BIT_FWM_EN BIT(6) #define BMC150_ACCEL_REG_INT_OUT_CTRL 0x20 #define BMC150_ACCEL_INT_OUT_CTRL_INT1_LVL BIT(0) @@ -122,6 +126,12 @@ #define BMC150_ACCEL_AXIS_TO_REG(axis) (BMC150_ACCEL_REG_XOUT_L + (axis * 2)) #define BMC150_AUTO_SUSPEND_DELAY_MS 2000 +#define BMC150_ACCEL_REG_FIFO_STATUS 0x0E +#define BMC150_ACCEL_REG_FIFO_CONFIG0 0x30 +#define BMC150_ACCEL_REG_FIFO_CONFIG1 0x3E +#define BMC150_ACCEL_REG_FIFO_DATA 0x3F +#define BMC150_ACCEL_FIFO_LENGTH 32 + enum bmc150_accel_axis { AXIS_X, AXIS_Y, @@ -147,20 +157,46 @@ struct bmc150_accel_chip_info { const struct bmc150_scale_info scale_table[4]; }; +struct bmc150_accel_interrupt { + const struct bmc150_accel_interrupt_info *info; + atomic_t users; +}; + +struct bmc150_accel_trigger { + struct bmc150_accel_data *data; + struct iio_trigger *indio_trig; + int (*setup)(struct bmc150_accel_trigger *t, bool state); + int intr; + bool enabled; +}; + +enum bmc150_accel_interrupt_id { + BMC150_ACCEL_INT_DATA_READY, + BMC150_ACCEL_INT_ANY_MOTION, + BMC150_ACCEL_INT_WATERMARK, + BMC150_ACCEL_INTERRUPTS, +}; + +enum bmc150_accel_trigger_id { + BMC150_ACCEL_TRIGGER_DATA_READY, + BMC150_ACCEL_TRIGGER_ANY_MOTION, + BMC150_ACCEL_TRIGGERS, +}; + struct bmc150_accel_data { struct i2c_client *client; - struct iio_trigger *dready_trig; - struct iio_trigger *motion_trig; + struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS]; + atomic_t active_intr; + struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; struct mutex mutex; + u8 fifo_mode, watermark; s16 buffer[8]; u8 bw_bits; u32 slope_dur; u32 slope_thres; u32 range; int ev_enable_state; - bool dready_trigger_on; - bool motion_trigger_on; - int64_t timestamp; + int64_t timestamp, old_timestamp; const struct bmc150_accel_chip_info *chip_info; }; @@ -269,6 +305,46 @@ 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_any_motion_setup(struct bmc150_accel_trigger *t, + bool state) +{ + if (state) + return bmc150_accel_update_slope(t->data); + + return 0; +} + static int bmc150_accel_chip_init(struct bmc150_accel_data *data) { int ret; @@ -307,32 +383,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 +404,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 +461,120 @@ 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[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 + }, + { /* fifo watermark interrupt */ + .map_reg = BMC150_ACCEL_REG_INT_MAP_1, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_FWM, + .en_reg = BMC150_ACCEL_REG_INT_EN_1, + .en_bitmask = BMC150_ACCEL_INT_EN_BIT_FWM_EN, + }, +}; + +static void bmc150_accel_interrupts_setup(struct iio_dev *indio_dev, + struct bmc150_accel_data *data) +{ + int i; + + for (i = 0; i < BMC150_ACCEL_INTERRUPTS; i++) + data->interrupts[i].info = &bmc150_accel_interrupts[i]; +} + +static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i, + bool state) +{ + struct bmc150_accel_interrupt *intr = &data->interrupts[i]; + const struct bmc150_accel_interrupt_info *info = intr->info; + int ret; + + if (state) { + if (atomic_inc_return(&intr->users) > 1) + return 0; + } else { + if (atomic_dec_return(&intr->users) > 0) + 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) + 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; + } + + if (state) + atomic_inc(&data->active_intr); + else + atomic_dec(&data->active_intr); + + return 0; + +out_fix_power_state: + bmc150_accel_set_power_state(data, false); + return ret; +} + + static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val) { int ret, i; @@ -732,7 +753,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 +776,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,36 +808,14 @@ 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); - 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 = 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); + ret = bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_ANY_MOTION, + state); if (ret < 0) { - bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -832,13 +830,224 @@ static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) { struct bmc150_accel_data *data = iio_priv(indio_dev); + int i; - if (data->dready_trig != trig && data->motion_trig != trig) - return -EINVAL; + for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) { + if (data->triggers[i].indio_trig == trig) + return 0; + } + + return -EINVAL; +} + +static ssize_t bmc150_accel_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmc150_accel_data *data = iio_priv(indio_dev); + int wm; + + mutex_lock(&data->mutex); + wm = data->watermark; + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", wm); +} + +static ssize_t bmc150_accel_get_fifo_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmc150_accel_data *data = iio_priv(indio_dev); + bool state; + + mutex_lock(&data->mutex); + state = data->fifo_mode; + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", state); +} + +static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); +static IIO_CONST_ATTR(hwfifo_watermark_max, + __stringify(BMC150_ACCEL_FIFO_LENGTH)); +static IIO_DEVICE_ATTR(hwfifo_enabled, S_IRUGO, + bmc150_accel_get_fifo_state, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, S_IRUGO, + bmc150_accel_get_fifo_watermark, NULL, 0); + +static const struct attribute *bmc150_accel_fifo_attributes[] = { + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static int bmc150_accel_set_watermark(struct iio_dev *indio_dev, unsigned val) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + + if (val > BMC150_ACCEL_FIFO_LENGTH) + val = BMC150_ACCEL_FIFO_LENGTH; + + mutex_lock(&data->mutex); + data->watermark = val; + mutex_unlock(&data->mutex); return 0; } +/* + * We must read at least one full frame in one burst, otherwise the rest of the + * frame data is discarded. + */ +static int bmc150_accel_fifo_transfer(const struct i2c_client *client, + char *buffer, int samples) +{ + int sample_length = 3 * 2; + u8 reg_fifo_data = BMC150_ACCEL_REG_FIFO_DATA; + int ret = -EIO; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .buf = ®_fifo_data, + .len = sizeof(reg_fifo_data), + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = (u8 *)buffer, + .len = samples * sample_length, + } + }; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) + ret = -EIO; + else + ret = 0; + } else { + int i, step = I2C_SMBUS_BLOCK_MAX / sample_length; + + for (i = 0; i < samples * sample_length; i += step) { + ret = i2c_smbus_read_i2c_block_data(client, + reg_fifo_data, step, + &buffer[i]); + if (ret != step) { + ret = -EIO; + break; + } + + ret = 0; + } + } + + if (ret) + dev_err(&client->dev, "Error transferring data from fifo\n"); + + return ret; +} + +static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, + unsigned samples, bool irq) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret, i; + u8 count; + u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3]; + int64_t tstamp; + uint64_t sample_period; + ret = i2c_smbus_read_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_STATUS); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_fifo_status\n"); + return ret; + } + + count = ret & 0x7F; + + if (!count) + return 0; + + /* + * If we getting called from IRQ handler we know the stored timestamp is + * fairly accurate for the last stored sample. Otherwise, if we are + * called as a result of a read operation from userspace and hence + * before the watermark interrupt was triggered, take a timestamp + * now. We can fall anywhere in between two samples so the error in this + * case is at most one sample period. + */ + if (!irq) { + data->old_timestamp = data->timestamp; + data->timestamp = iio_get_time_ns(); + } + + /* + * Approximate timestamps for each of the sample based on the sampling + * frequency, timestamp for last sample and number of samples. + * + * Note that we can't use the current bandwidth settings to compute the + * sample period because the sample rate varies with the device + * (e.g. between 31.70ms to 32.20ms for a bandwidth of 15.63HZ). That + * small variation adds when we store a large number of samples and + * creates significant jitter between the last and first samples in + * different batches (e.g. 32ms vs 21ms). + * + * To avoid this issue we compute the actual sample period ourselves + * based on the timestamp delta between the last two flush operations. + */ + sample_period = (data->timestamp - data->old_timestamp); + do_div(sample_period, count); + tstamp = data->timestamp - (count - 1) * sample_period; + + if (samples && count > samples) + count = samples; + + ret = bmc150_accel_fifo_transfer(data->client, (u8 *)buffer, count); + if (ret) + return ret; + + /* + * Ideally we want the IIO core to handle the demux when running in fifo + * mode but not when running in triggered buffer mode. Unfortunately + * this does not seem to be possible, so stick with driver demux for + * now. + */ + for (i = 0; i < count; i++) { + u16 sample[8]; + int j, bit; + + j = 0; + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) + memcpy(&sample[j++], &buffer[i * 3 + bit], 2); + + iio_push_to_buffers_with_timestamp(indio_dev, sample, tstamp); + + tstamp += sample_period; + } + + return count; +} + +static int bmc150_accel_fifo_flush(struct iio_dev *indio_dev, unsigned samples) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + ret = __bmc150_accel_fifo_flush(indio_dev, samples, false); + mutex_unlock(&data->mutex); + + return ret; +} + static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( "15.620000 31.260000 62.50000 125 250 500 1000 2000"); @@ -978,6 +1187,20 @@ static const struct iio_info bmc150_accel_info = { .driver_module = THIS_MODULE, }; +static const struct iio_info bmc150_accel_info_fifo = { + .attrs = &bmc150_accel_attrs_group, + .read_raw = bmc150_accel_read_raw, + .write_raw = bmc150_accel_write_raw, + .read_event_value = bmc150_accel_read_event, + .write_event_value = bmc150_accel_write_event, + .write_event_config = bmc150_accel_write_event_config, + .read_event_config = bmc150_accel_read_event_config, + .validate_trigger = bmc150_accel_validate_trigger, + .hwfifo_set_watermark = bmc150_accel_set_watermark, + .hwfifo_flush_to_buffer = bmc150_accel_fifo_flush, + .driver_module = THIS_MODULE, +}; + static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -1008,12 +1231,12 @@ err_read: static int bmc150_accel_trig_try_reen(struct iio_trigger *trig) { - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); - struct bmc150_accel_data *data = iio_priv(indio_dev); + struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig); + struct bmc150_accel_data *data = t->data; int ret; /* new data interrupts don't need ack */ - if (data->dready_trigger_on) + if (t == &t->data->triggers[BMC150_ACCEL_TRIGGER_DATA_READY]) return 0; mutex_lock(&data->mutex); @@ -1032,43 +1255,35 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig) return 0; } -static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig, +static int bmc150_accel_trigger_set_state(struct iio_trigger *trig, bool state) { - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); - struct bmc150_accel_data *data = iio_priv(indio_dev); + struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig); + struct bmc150_accel_data *data = t->data; int ret; mutex_lock(&data->mutex); - if (!state && data->ev_enable_state && data->motion_trigger_on) { - data->motion_trigger_on = false; + if (t->enabled == state) { 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 (t->setup) { + ret = t->setup(t, 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 - ret = bmc150_accel_setup_new_data_interrupt(data, state); + + ret = bmc150_accel_set_interrupt(data, t->intr, state); if (ret < 0) { - bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } - if (data->motion_trig == trig) - data->motion_trigger_on = state; - else - data->dready_trigger_on = state; + + t->enabled = state; mutex_unlock(&data->mutex); @@ -1076,23 +1291,22 @@ static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig, } static const struct iio_trigger_ops bmc150_accel_trigger_ops = { - .set_trigger_state = bmc150_accel_data_rdy_trigger_set_state, + .set_trigger_state = bmc150_accel_trigger_set_state, .try_reenable = bmc150_accel_trig_try_reen, .owner = THIS_MODULE, }; -static irqreturn_t bmc150_accel_event_handler(int irq, void *private) +static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev) { - struct iio_dev *indio_dev = private; struct bmc150_accel_data *data = iio_priv(indio_dev); - int ret; int dir; + int ret; ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_STATUS_2); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_status_2\n"); - goto ack_intr_status; + return ret; } if (ret & BMC150_ACCEL_ANY_MOTION_BIT_SIGN) @@ -1121,32 +1335,73 @@ static irqreturn_t bmc150_accel_event_handler(int irq, void *private) IIO_EV_TYPE_ROC, dir), data->timestamp); -ack_intr_status: - if (!data->dready_trigger_on) + return ret; +} + +static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmc150_accel_data *data = iio_priv(indio_dev); + bool ack = false; + int ret; + + mutex_lock(&data->mutex); + + if (data->fifo_mode) { + ret = __bmc150_accel_fifo_flush(indio_dev, + BMC150_ACCEL_FIFO_LENGTH, true); + if (ret > 0) + ack = true; + } + + if (data->ev_enable_state) { + ret = bmc150_accel_handle_roc_event(indio_dev); + if (ret > 0) + ack = true; + } + + if (ack) { 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) + dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n"); + ret = IRQ_HANDLED; + } else { + ret = IRQ_NONE; + } - return IRQ_HANDLED; + mutex_unlock(&data->mutex); + + return ret; } -static irqreturn_t bmc150_accel_data_rdy_trig_poll(int irq, void *private) +static irqreturn_t bmc150_accel_irq_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct bmc150_accel_data *data = iio_priv(indio_dev); + bool ack = false; + int i; + data->old_timestamp = data->timestamp; 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); + for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) { + if (data->triggers[i].enabled) { + iio_trigger_poll(data->triggers[i].indio_trig); + ack = true; + break; + } + } - if (data->ev_enable_state) + if (data->ev_enable_state || data->fifo_mode) return IRQ_WAKE_THREAD; - else + + if (ack) return IRQ_HANDLED; + + return IRQ_NONE; } static const char *bmc150_accel_match_acpi_device(struct device *dev, int *data) @@ -1176,23 +1431,171 @@ 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); + ret = gpiod_to_irq(gpio); + + dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + + return ret; +} + +static const struct { + int intr; + const char *name; + int (*setup)(struct bmc150_accel_trigger *t, bool state); +} bmc150_accel_triggers[BMC150_ACCEL_TRIGGERS] = { + { + .intr = 0, + .name = "%s-dev%d", + }, + { + .intr = 1, + .name = "%s-any-motion-dev%d", + .setup = bmc150_accel_any_motion_setup, + }, +}; + +static void bmc150_accel_unregister_triggers(struct bmc150_accel_data *data, + int from) +{ + int i; + + for (i = from; i >= 0; i++) { + if (data->triggers[i].indio_trig) { + iio_trigger_unregister(data->triggers[i].indio_trig); + data->triggers[i].indio_trig = NULL; + } + } +} + +static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev, + struct bmc150_accel_data *data) +{ + int i, ret; + + for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) { + struct bmc150_accel_trigger *t = &data->triggers[i]; + + t->indio_trig = devm_iio_trigger_alloc(&data->client->dev, + bmc150_accel_triggers[i].name, + indio_dev->name, + indio_dev->id); + if (!t->indio_trig) { + ret = -ENOMEM; + break; + } + + t->indio_trig->dev.parent = &data->client->dev; + t->indio_trig->ops = &bmc150_accel_trigger_ops; + t->intr = bmc150_accel_triggers[i].intr; + t->data = data; + t->setup = bmc150_accel_triggers[i].setup; + iio_trigger_set_drvdata(t->indio_trig, t); + + ret = iio_trigger_register(t->indio_trig); + if (ret) + break; + } + if (ret) + bmc150_accel_unregister_triggers(data, i - 1); + + return ret; +} + +#define BMC150_ACCEL_FIFO_MODE_STREAM 0x80 +#define BMC150_ACCEL_FIFO_MODE_FIFO 0x40 +#define BMC150_ACCEL_FIFO_MODE_BYPASS 0x00 + +static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data) +{ + u8 reg = BMC150_ACCEL_REG_FIFO_CONFIG1; + int ret; + + ret = i2c_smbus_write_byte_data(data->client, reg, data->fifo_mode); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_fifo_config1\n"); return ret; + } - ret = gpiod_to_irq(gpio); + if (!data->fifo_mode) + return 0; - dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret); + ret = i2c_smbus_write_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_CONFIG0, + data->watermark); + if (ret < 0) + dev_err(&data->client->dev, "Error writing reg_fifo_config0\n"); return ret; } +static int bmc150_accel_buffer_postenable(struct iio_dev *indio_dev) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret = 0; + + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) + return iio_triggered_buffer_postenable(indio_dev); + + mutex_lock(&data->mutex); + + if (!data->watermark) + goto out; + + ret = bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK, + true); + if (ret) + goto out; + + data->fifo_mode = BMC150_ACCEL_FIFO_MODE_FIFO; + + ret = bmc150_accel_fifo_set_mode(data); + if (ret) { + data->fifo_mode = 0; + bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK, + false); + } + +out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bmc150_accel_buffer_predisable(struct iio_dev *indio_dev) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) + return iio_triggered_buffer_predisable(indio_dev); + + mutex_lock(&data->mutex); + + if (!data->fifo_mode) + goto out; + + bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK, false); + __bmc150_accel_fifo_flush(indio_dev, BMC150_ACCEL_FIFO_LENGTH, false); + data->fifo_mode = 0; + bmc150_accel_fifo_set_mode(data); + +out: + mutex_unlock(&data->mutex); + + return 0; +} + +static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = { + .postenable = bmc150_accel_buffer_postenable, + .predisable = bmc150_accel_buffer_predisable, +}; + static int bmc150_accel_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1239,53 +1642,51 @@ static int bmc150_accel_probe(struct i2c_client *client, if (client->irq >= 0) { ret = devm_request_threaded_irq( &client->dev, client->irq, - bmc150_accel_data_rdy_trig_poll, - bmc150_accel_event_handler, + bmc150_accel_irq_handler, + bmc150_accel_irq_thread_handler, IRQF_TRIGGER_RISING, BMC150_ACCEL_IRQ_NAME, indio_dev); if (ret) return ret; - data->dready_trig = devm_iio_trigger_alloc(&client->dev, - "%s-dev%d", - indio_dev->name, - indio_dev->id); - if (!data->dready_trig) - return -ENOMEM; - - 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; - - data->dready_trig->dev.parent = &client->dev; - data->dready_trig->ops = &bmc150_accel_trigger_ops; - iio_trigger_set_drvdata(data->dready_trig, indio_dev); - ret = iio_trigger_register(data->dready_trig); - if (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->motion_trig->dev.parent = &client->dev; - data->motion_trig->ops = &bmc150_accel_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; } + bmc150_accel_interrupts_setup(indio_dev, data); + + ret = bmc150_accel_triggers_setup(indio_dev, data); + if (ret) + return ret; + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, bmc150_accel_trigger_handler, - NULL); + &bmc150_accel_buffer_ops); if (ret < 0) { dev_err(&client->dev, "Failed: iio triggered buffer setup\n"); goto err_trigger_unregister; } + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + indio_dev->info = &bmc150_accel_info_fifo; + indio_dev->buffer->attrs = bmc150_accel_fifo_attributes; + } } ret = iio_device_register(indio_dev); @@ -1308,13 +1709,10 @@ static int bmc150_accel_probe(struct i2c_client *client, err_iio_unregister: iio_device_unregister(indio_dev); err_buffer_cleanup: - if (data->dready_trig) + if (indio_dev->pollfunc) iio_triggered_buffer_cleanup(indio_dev); err_trigger_unregister: - if (data->dready_trig) - iio_trigger_unregister(data->dready_trig); - if (data->motion_trig) - iio_trigger_unregister(data->motion_trig); + bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); return ret; } @@ -1330,11 +1728,7 @@ static int bmc150_accel_remove(struct i2c_client *client) iio_device_unregister(indio_dev); - if (data->dready_trig) { - iio_triggered_buffer_cleanup(indio_dev); - iio_trigger_unregister(data->dready_trig); - iio_trigger_unregister(data->motion_trig); - } + bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); mutex_lock(&data->mutex); bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_DEEP_SUSPEND, 0); @@ -1362,9 +1756,9 @@ static int bmc150_accel_resume(struct device *dev) struct bmc150_accel_data *data = iio_priv(indio_dev); mutex_lock(&data->mutex); - if (data->dready_trigger_on || data->motion_trigger_on || - data->ev_enable_state) + if (atomic_read(&data->active_intr)) bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); + bmc150_accel_fifo_set_mode(data); mutex_unlock(&data->mutex); return 0; @@ -1398,6 +1792,9 @@ static int bmc150_accel_runtime_resume(struct device *dev) ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); if (ret < 0) return ret; + ret = bmc150_accel_fifo_set_mode(data); + if (ret < 0) + return ret; sleep_val = bmc150_accel_get_startup_times(data); if (sleep_val < 20) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 1a63795..51da369 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1169,16 +1169,12 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client, dev = &client->dev; /* data ready gpio interrupt pin */ - gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0); + gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0, GPIOD_IN); if (IS_ERR(gpio)) { dev_err(dev, "acpi gpio get index failed\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); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 46c3835..7db7cc0 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -418,17 +418,18 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev) struct device *dev = &data->client->dev; for (i = 0; i < MMA9551_GPIO_COUNT; i++) { - gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i); + gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i, + GPIOD_IN); if (IS_ERR(gpio)) { dev_err(dev, "acpi gpio get index failed\n"); return PTR_ERR(gpio); } - ret = gpiod_direction_input(gpio); - if (ret) + ret = gpiod_to_irq(gpio); + if (ret < 0) return ret; - data->irqs[i] = gpiod_to_irq(gpio); + data->irqs[i] = ret; ret = devm_request_threaded_irq(dev, data->irqs[i], NULL, mma9551_event_handler, IRQF_TRIGGER_RISING | IRQF_ONESHOT, diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index d23ebf1..2df1af7 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1109,16 +1109,12 @@ static int mma9553_gpio_probe(struct i2c_client *client) dev = &client->dev; /* data ready gpio interrupt pin */ - gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0); + gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN); if (IS_ERR(gpio)) { dev_err(dev, "acpi gpio get index failed\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); diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index fa96460..7ee9724 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -14,6 +14,7 @@ #include <linux/types.h> #include <linux/iio/common/st_sensors.h> +#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel" #define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" #define LIS3DH_ACCEL_DEV_NAME "lis3dh" #define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 53f3262..58d1d13 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -129,6 +129,30 @@ #define ST_ACCEL_3_IG1_EN_MASK 0x08 #define ST_ACCEL_3_MULTIREAD_BIT false +/* CUSTOM VALUES FOR SENSOR 4 */ +#define ST_ACCEL_4_WAI_EXP 0x3a +#define ST_ACCEL_4_ODR_ADDR 0x20 +#define ST_ACCEL_4_ODR_MASK 0x30 /* DF1 and DF0 */ +#define ST_ACCEL_4_ODR_AVL_40HZ_VAL 0x00 +#define ST_ACCEL_4_ODR_AVL_160HZ_VAL 0x01 +#define ST_ACCEL_4_ODR_AVL_640HZ_VAL 0x02 +#define ST_ACCEL_4_ODR_AVL_2560HZ_VAL 0x03 +#define ST_ACCEL_4_PW_ADDR 0x20 +#define ST_ACCEL_4_PW_MASK 0xc0 +#define ST_ACCEL_4_FS_ADDR 0x21 +#define ST_ACCEL_4_FS_MASK 0x80 +#define ST_ACCEL_4_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_4_FS_AVL_6_VAL 0X01 +#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1024) +#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(340) +#define ST_ACCEL_4_BDU_ADDR 0x21 +#define ST_ACCEL_4_BDU_MASK 0x40 +#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21 +#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04 +#define ST_ACCEL_4_IG1_EN_ADDR 0x21 +#define ST_ACCEL_4_IG1_EN_MASK 0x08 +#define ST_ACCEL_4_MULTIREAD_BIT true + static const struct iio_chan_spec st_accel_12bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -373,6 +397,63 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_ACCEL_4_WAI_EXP, + .sensors_supported = { + [0] = LIS3LV02DL_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_4_ODR_ADDR, + .mask = ST_ACCEL_4_ODR_MASK, + .odr_avl = { + { 40, ST_ACCEL_4_ODR_AVL_40HZ_VAL }, + { 160, ST_ACCEL_4_ODR_AVL_160HZ_VAL, }, + { 640, ST_ACCEL_4_ODR_AVL_640HZ_VAL, }, + { 2560, ST_ACCEL_4_ODR_AVL_2560HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_4_PW_ADDR, + .mask = ST_ACCEL_4_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_4_FS_ADDR, + .mask = ST_ACCEL_4_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_4_FS_AVL_2_VAL, + .gain = ST_ACCEL_4_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_6G, + .value = ST_ACCEL_4_FS_AVL_6_VAL, + .gain = ST_ACCEL_4_FS_AVL_6_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_ACCEL_4_BDU_ADDR, + .mask = ST_ACCEL_4_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_ACCEL_4_DRDY_IRQ_ADDR, + .mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK, + .ig1 = { + .en_addr = ST_ACCEL_4_IG1_EN_ADDR, + .en_mask = ST_ACCEL_4_IG1_EN_MASK, + }, + }, + .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, + .bootime = 2, /* guess */ + }, }; static int st_accel_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index c7246bd..6b720c1 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -21,6 +21,10 @@ #ifdef CONFIG_OF static const struct of_device_id st_accel_of_match[] = { { + .compatible = "st,lis3lv02dl-accel", + .data = LIS3LV02DL_ACCEL_DEV_NAME, + }, + { .compatible = "st,lsm303dlh-accel", .data = LSM303DLH_ACCEL_DEV_NAME, }, diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 46379b1..f96074a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -187,10 +187,11 @@ config MAX1363 data via the iio dev interface. config MCP320X - tristate "Microchip Technology MCP3204/08" + tristate "Microchip Technology MCP3x01/02/04/08" depends on SPI help - Say yes here to build support for Microchip Technology's MCP3204 or + Say yes here to build support for Microchip Technology's + MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204 or MCP3208 analog to digital converter. This driver can also be built as a module. If so, the module will be diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 4dddeab..b84922a 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -861,5 +861,5 @@ static struct spi_driver ad7793_driver = { module_spi_driver(ad7793_driver); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); -MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs"); +MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index e63b8e7..56292ae 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -275,7 +275,6 @@ static void vf610_adc_cfg_post_set(struct vf610_adc *info) static void vf610_adc_calibration(struct vf610_adc *info) { int adc_gc, hc_cfg; - int timeout; if (!info->adc_feature.calibration) return; @@ -287,9 +286,7 @@ static void vf610_adc_calibration(struct vf610_adc *info) adc_gc = readl(info->regs + VF610_REG_ADC_GC); writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC); - timeout = wait_for_completion_timeout - (&info->completion, VF610_ADC_TIMEOUT); - if (timeout == 0) + if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT)) dev_err(info->dev, "Timeout for adc calibration\n"); adc_gc = readl(info->regs + VF610_REG_ADC_GS); diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index 55a90082..9a40097 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -437,7 +437,7 @@ int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay) } #ifdef CONFIG_OF -static struct of_device_id ssp_of_match[] = { +static const struct of_device_id ssp_of_match[] = { { .compatible = "samsung,sensorhub-rinato", .data = &ssp_rinato_info, diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 2236ea2..13471a7 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -143,11 +143,16 @@ config AD7303 ad7303. config MAX517 - tristate "Maxim MAX517/518/519 DAC driver" + tristate "Maxim MAX517/518/519/520/521 DAC driver" depends on I2C help - If you say yes here you get support for the Maxim chips MAX517, - MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). + If you say yes here you get support for the following Maxim chips + (I2C 8-Bit DACs with rail-to-rail outputs): + MAX517 - Single channel, single reference + MAX518 - Dual channel, ref=Vdd + MAX519 - Dual channel, dual reference + MAX520 - Quad channel, quad reference + MAX521 - Octal channel, independent ref for ch0-3, shared ref for ch4-7 This driver can also be built as a module. If so, the module will be called max517. diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index 9a82a72..5507b39 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -39,11 +39,13 @@ enum max517_device_ids { ID_MAX517, ID_MAX518, ID_MAX519, + ID_MAX520, + ID_MAX521, }; struct max517_data { struct i2c_client *client; - unsigned short vref_mv[2]; + unsigned short vref_mv[8]; }; /* @@ -149,7 +151,13 @@ static const struct iio_info max517_info = { static const struct iio_chan_spec max517_channels[] = { MAX517_CHANNEL(0), - MAX517_CHANNEL(1) + MAX517_CHANNEL(1), + MAX517_CHANNEL(2), + MAX517_CHANNEL(3), + MAX517_CHANNEL(4), + MAX517_CHANNEL(5), + MAX517_CHANNEL(6), + MAX517_CHANNEL(7), }; static int max517_probe(struct i2c_client *client, @@ -158,6 +166,7 @@ static int max517_probe(struct i2c_client *client, struct max517_data *data; struct iio_dev *indio_dev; struct max517_platform_data *platform_data = client->dev.platform_data; + int chan; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -169,11 +178,21 @@ static int max517_probe(struct i2c_client *client, /* establish that the iio_dev is a child of the i2c device */ indio_dev->dev.parent = &client->dev; - /* reduced channel set for MAX517 */ - if (id->driver_data == ID_MAX517) - indio_dev->num_channels = 1; - else + switch (id->driver_data) { + case ID_MAX521: + indio_dev->num_channels = 8; + break; + case ID_MAX520: + indio_dev->num_channels = 4; + break; + case ID_MAX519: + case ID_MAX518: indio_dev->num_channels = 2; + break; + default: /* single channel for MAX517 */ + indio_dev->num_channels = 1; + break; + } indio_dev->channels = max517_channels; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &max517_info; @@ -182,11 +201,11 @@ static int max517_probe(struct i2c_client *client, * Reference voltage on MAX518 and default is 5V, else take vref_mv * from platform_data */ - if (id->driver_data == ID_MAX518 || !platform_data) { - data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */ - } else { - data->vref_mv[0] = platform_data->vref_mv[0]; - data->vref_mv[1] = platform_data->vref_mv[1]; + for (chan = 0; chan < indio_dev->num_channels; chan++) { + if (id->driver_data == ID_MAX518 || !platform_data) + data->vref_mv[chan] = 5000; /* mV */ + else + data->vref_mv[chan] = platform_data->vref_mv[chan]; } return iio_device_register(indio_dev); @@ -202,6 +221,8 @@ static const struct i2c_device_id max517_id[] = { { "max517", ID_MAX517 }, { "max518", ID_MAX518 }, { "max519", ID_MAX519 }, + { "max520", ID_MAX520 }, + { "max521", ID_MAX521 }, { } }; MODULE_DEVICE_TABLE(i2c, max517_id); @@ -218,5 +239,5 @@ static struct i2c_driver max517_driver = { module_i2c_driver(max517_driver); MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); -MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC"); +MODULE_DESCRIPTION("MAX517/518/519/520/521 8-bit DAC"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index ccf3ea7..4415f55 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -1001,16 +1001,12 @@ static int bmg160_gpio_probe(struct i2c_client *client, dev = &client->dev; /* data ready gpio interrupt pin */ - gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0); + gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN); if (IS_ERR(gpio)) { dev_err(dev, "acpi gpio get index failed\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); diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index 6a8020d..f0fd940 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -223,6 +223,10 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev) int ret; u8 val; + ret = itg3200_reset(indio_dev); + if (ret) + goto err_ret; + ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val); if (ret) goto err_ret; @@ -233,10 +237,6 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev) goto err_ret; } - ret = itg3200_reset(indio_dev); - if (ret) - goto err_ret; - ret = itg3200_enable_full_scale(indio_dev); err_ret: return ret; @@ -351,6 +351,26 @@ static int itg3200_remove(struct i2c_client *client) return 0; } +static int __maybe_unused itg3200_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct itg3200 *st = iio_priv(indio_dev); + + dev_dbg(&st->i2c->dev, "suspend device"); + + return itg3200_write_reg_8(indio_dev, ITG3200_REG_POWER_MANAGEMENT, + ITG3200_SLEEP); +} + +static int __maybe_unused itg3200_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return itg3200_initial_setup(indio_dev); +} + +static SIMPLE_DEV_PM_OPS(itg3200_pm_ops, itg3200_suspend, itg3200_resume); + static const struct i2c_device_id itg3200_id[] = { { "itg3200", 0 }, { } @@ -361,6 +381,7 @@ static struct i2c_driver itg3200_driver = { .driver = { .owner = THIS_MODULE, .name = "itg3200", + .pm = &itg3200_pm_ops, }, .id_table = itg3200_id, .probe = itg3200_probe, diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index f07a233..21395f2 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -87,6 +87,31 @@ #define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08 #define ST_GYRO_2_MULTIREAD_BIT true +/* CUSTOM VALUES FOR SENSOR 3 */ +#define ST_GYRO_3_WAI_EXP 0xd7 +#define ST_GYRO_3_ODR_ADDR 0x20 +#define ST_GYRO_3_ODR_MASK 0xc0 +#define ST_GYRO_3_ODR_AVL_95HZ_VAL 0x00 +#define ST_GYRO_3_ODR_AVL_190HZ_VAL 0x01 +#define ST_GYRO_3_ODR_AVL_380HZ_VAL 0x02 +#define ST_GYRO_3_ODR_AVL_760HZ_VAL 0x03 +#define ST_GYRO_3_PW_ADDR 0x20 +#define ST_GYRO_3_PW_MASK 0x08 +#define ST_GYRO_3_FS_ADDR 0x23 +#define ST_GYRO_3_FS_MASK 0x30 +#define ST_GYRO_3_FS_AVL_250_VAL 0x00 +#define ST_GYRO_3_FS_AVL_500_VAL 0x01 +#define ST_GYRO_3_FS_AVL_2000_VAL 0x02 +#define ST_GYRO_3_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750) +#define ST_GYRO_3_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_GYRO_3_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000) +#define ST_GYRO_3_BDU_ADDR 0x23 +#define ST_GYRO_3_BDU_MASK 0x80 +#define ST_GYRO_3_DRDY_IRQ_ADDR 0x22 +#define ST_GYRO_3_DRDY_IRQ_INT2_MASK 0x08 +#define ST_GYRO_3_MULTIREAD_BIT true + + static const struct iio_chan_spec st_gyro_16bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -225,6 +250,64 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_GYRO_3_WAI_EXP, + .sensors_supported = { + [0] = L3GD20_GYRO_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, + .odr = { + .addr = ST_GYRO_3_ODR_ADDR, + .mask = ST_GYRO_3_ODR_MASK, + .odr_avl = { + { 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, }, + { 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, }, + { 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, }, + { 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_GYRO_3_PW_ADDR, + .mask = ST_GYRO_3_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_GYRO_3_FS_ADDR, + .mask = ST_GYRO_3_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_GYRO_FS_AVL_250DPS, + .value = ST_GYRO_3_FS_AVL_250_VAL, + .gain = ST_GYRO_3_FS_AVL_250_GAIN, + }, + [1] = { + .num = ST_GYRO_FS_AVL_500DPS, + .value = ST_GYRO_3_FS_AVL_500_VAL, + .gain = ST_GYRO_3_FS_AVL_500_GAIN, + }, + [2] = { + .num = ST_GYRO_FS_AVL_2000DPS, + .value = ST_GYRO_3_FS_AVL_2000_VAL, + .gain = ST_GYRO_3_FS_AVL_2000_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_GYRO_3_BDU_ADDR, + .mask = ST_GYRO_3_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_GYRO_3_DRDY_IRQ_ADDR, + .mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK, + }, + .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT, + .bootime = 2, + }, }; static int st_gyro_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile index 3a677c7..f566f6a 100644 --- a/drivers/iio/imu/inv_mpu6050/Makefile +++ b/drivers/iio/imu/inv_mpu6050/Makefile @@ -3,4 +3,4 @@ # obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o -inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o +inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o inv_mpu_acpi.o diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c new file mode 100644 index 0000000..1c982a5 --- /dev/null +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c @@ -0,0 +1,211 @@ +/* + * inv_mpu_acpi: ACPI processing for creating client devices + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#ifdef CONFIG_ACPI + +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/dmi.h> +#include <linux/acpi.h> +#include "inv_mpu_iio.h" + +enum inv_mpu_product_name { + INV_MPU_NOT_MATCHED, + INV_MPU_ASUS_T100TA, +}; + +static enum inv_mpu_product_name matched_product_name; + +static int __init asus_t100_matched(const struct dmi_system_id *d) +{ + matched_product_name = INV_MPU_ASUS_T100TA; + + return 0; +} + +static const struct dmi_system_id inv_mpu_dev_list[] = { + { + .callback = asus_t100_matched, + .ident = "Asus Transformer Book T100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"), + DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), + }, + }, + /* Add more matching tables here..*/ + {} +}; + +static int asus_acpi_get_sensor_info(struct acpi_device *adev, + struct i2c_client *client, + struct i2c_board_info *info) +{ + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + int i; + acpi_status status; + union acpi_object *cpm; + + status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer); + if (ACPI_FAILURE(status)) + return -ENODEV; + + cpm = buffer.pointer; + for (i = 0; i < cpm->package.count; ++i) { + union acpi_object *elem; + int j; + + elem = &(cpm->package.elements[i]); + for (j = 0; j < elem->package.count; ++j) { + union acpi_object *sub_elem; + + sub_elem = &(elem->package.elements[j]); + if (sub_elem->type == ACPI_TYPE_STRING) + strlcpy(info->type, sub_elem->string.pointer, + sizeof(info->type)); + else if (sub_elem->type == ACPI_TYPE_INTEGER) { + if (sub_elem->integer.value != client->addr) { + info->addr = sub_elem->integer.value; + break; /* Not a MPU6500 primary */ + } + } + } + } + + kfree(buffer.pointer); + + return cpm->package.count; +} + +static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data) +{ + u32 *addr = data; + + if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) { + struct acpi_resource_i2c_serialbus *sb; + + sb = &ares->data.i2c_serial_bus; + if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) { + if (*addr) + *addr |= (sb->slave_address << 16); + else + *addr = sb->slave_address; + } + } + + /* Tell the ACPI core that we already copied this address */ + return 1; +} + +static int inv_mpu_process_acpi_config(struct i2c_client *client, + unsigned short *primary_addr, + unsigned short *secondary_addr) +{ + const struct acpi_device_id *id; + struct acpi_device *adev; + u32 i2c_addr = 0; + LIST_HEAD(resources); + int ret; + + id = acpi_match_device(client->dev.driver->acpi_match_table, + &client->dev); + if (!id) + return -ENODEV; + + adev = ACPI_COMPANION(&client->dev); + if (!adev) + return -ENODEV; + + ret = acpi_dev_get_resources(adev, &resources, + acpi_i2c_check_resource, &i2c_addr); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resources); + *primary_addr = i2c_addr & 0x0000ffff; + *secondary_addr = (i2c_addr & 0xffff0000) >> 16; + + return 0; +} + +int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) +{ + + st->mux_client = NULL; + if (ACPI_HANDLE(&st->client->dev)) { + struct i2c_board_info info; + struct acpi_device *adev; + int ret = -1; + + adev = ACPI_COMPANION(&st->client->dev); + memset(&info, 0, sizeof(info)); + + dmi_check_system(inv_mpu_dev_list); + switch (matched_product_name) { + case INV_MPU_ASUS_T100TA: + ret = asus_acpi_get_sensor_info(adev, st->client, + &info); + break; + /* Add more matched product processing here */ + default: + break; + } + + if (ret < 0) { + /* No matching DMI, so create device on INV6XX type */ + unsigned short primary, secondary; + + ret = inv_mpu_process_acpi_config(st->client, &primary, + &secondary); + if (!ret && secondary) { + char *name; + + info.addr = secondary; + strlcpy(info.type, dev_name(&adev->dev), + sizeof(info.type)); + name = strchr(info.type, ':'); + if (name) + *name = '\0'; + strlcat(info.type, "-client", + sizeof(info.type)); + } else + return 0; /* no secondary addr, which is OK */ + } + st->mux_client = i2c_new_device(st->mux_adapter, &info); + if (!st->mux_client) + return -ENODEV; + + } + + return 0; +} + +void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) +{ + if (st->mux_client) + i2c_unregister_device(st->mux_client); +} +#else + +#include "inv_mpu_iio.h" + +int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st) +{ + return 0; +} + +void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st) +{ +} +#endif diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index ef76afe..17d4bb1 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -829,8 +829,14 @@ static int inv_mpu_probe(struct i2c_client *client, goto out_unreg_device; } + result = inv_mpu_acpi_create_mux_client(st); + if (result) + goto out_del_mux; + return 0; +out_del_mux: + i2c_del_mux_adapter(st->mux_adapter); out_unreg_device: iio_device_unregister(indio_dev); out_remove_trigger: @@ -845,6 +851,7 @@ static int inv_mpu_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct inv_mpu6050_state *st = iio_priv(indio_dev); + inv_mpu_acpi_delete_mux_client(st); i2c_del_mux_adapter(st->mux_adapter); iio_device_unregister(indio_dev); inv_mpu6050_remove_trigger(st); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index aa837de..db0a4a2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -121,6 +121,7 @@ struct inv_mpu6050_state { spinlock_t time_stamp_lock; struct i2c_client *client; struct i2c_adapter *mux_adapter; + struct i2c_client *mux_client; unsigned int powerup_count; struct inv_mpu6050_platform_data plat_data; DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); @@ -251,3 +252,5 @@ int inv_reset_fifo(struct iio_dev *indio_dev); int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask); int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on); +int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st); +void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st); diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index b3a3637..462a010 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -169,19 +169,18 @@ static const u16 kmx61_uscale_table[] = {9582, 19163, 38326}; static const struct { int val; int val2; - u8 odr_bits; -} kmx61_samp_freq_table[] = { {12, 500000, 0x00}, - {25, 0, 0x01}, - {50, 0, 0x02}, - {100, 0, 0x03}, - {200, 0, 0x04}, - {400, 0, 0x05}, - {800, 0, 0x06}, - {1600, 0, 0x07}, - {0, 781000, 0x08}, - {1, 563000, 0x09}, - {3, 125000, 0x0A}, - {6, 250000, 0x0B} }; +} kmx61_samp_freq_table[] = { {12, 500000}, + {25, 0}, + {50, 0}, + {100, 0}, + {200, 0}, + {400, 0}, + {800, 0}, + {1600, 0}, + {0, 781000}, + {1, 563000}, + {3, 125000}, + {6, 250000} }; static const struct { int val; @@ -302,24 +301,10 @@ static int kmx61_convert_freq_to_bit(int val, int val2) for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) if (val == kmx61_samp_freq_table[i].val && val2 == kmx61_samp_freq_table[i].val2) - return kmx61_samp_freq_table[i].odr_bits; + return i; return -EINVAL; } -static int kmx61_convert_bit_to_freq(u8 odr_bits, int *val, int *val2) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) - if (odr_bits == kmx61_samp_freq_table[i].odr_bits) { - *val = kmx61_samp_freq_table[i].val; - *val2 = kmx61_samp_freq_table[i].val2; - return 0; - } - return -EINVAL; -} - - static int kmx61_convert_wake_up_odr_to_bit(int val, int val2) { int i; @@ -478,7 +463,7 @@ static int kmx61_set_odr(struct kmx61_data *data, int val, int val2, u8 device) static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2, u8 device) -{ int i; +{ u8 lodr_bits; if (device & KMX61_ACC) @@ -490,13 +475,13 @@ static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2, else return -EINVAL; - for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++) - if (lodr_bits == kmx61_samp_freq_table[i].odr_bits) { - *val = kmx61_samp_freq_table[i].val; - *val2 = kmx61_samp_freq_table[i].val2; - return 0; - } - return -EINVAL; + if (lodr_bits >= ARRAY_SIZE(kmx61_samp_freq_table)) + return -EINVAL; + + *val = kmx61_samp_freq_table[lodr_bits].val; + *val2 = kmx61_samp_freq_table[lodr_bits].val2; + + return 0; } static int kmx61_set_range(struct kmx61_data *data, u8 range) @@ -580,8 +565,11 @@ static int kmx61_chip_init(struct kmx61_data *data) } data->odr_bits = ret; - /* set output data rate for wake up (motion detection) function */ - ret = kmx61_convert_bit_to_freq(data->odr_bits, &val, &val2); + /* + * set output data rate for wake up (motion detection) function + * to match data rate for accelerometer sampling + */ + ret = kmx61_get_odr(data, &val, &val2, KMX61_ACC); if (ret < 0) return ret; @@ -1267,16 +1255,12 @@ static int kmx61_gpio_probe(struct i2c_client *client, struct kmx61_data *data) dev = &client->dev; /* data ready gpio interrupt pin */ - gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0); + gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0, GPIOD_IN); if (IS_ERR(gpio)) { dev_err(dev, "acpi gpio get index failed\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); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 7133314..df919f4 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -37,11 +37,57 @@ static bool iio_buffer_is_active(struct iio_buffer *buf) return !list_empty(&buf->buffer_list); } -static bool iio_buffer_data_available(struct iio_buffer *buf) +static size_t iio_buffer_data_available(struct iio_buffer *buf) { return buf->access->data_available(buf); } +static int iio_buffer_flush_hwfifo(struct iio_dev *indio_dev, + struct iio_buffer *buf, size_t required) +{ + if (!indio_dev->info->hwfifo_flush_to_buffer) + return -ENODEV; + + return indio_dev->info->hwfifo_flush_to_buffer(indio_dev, required); +} + +static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, + size_t to_wait, int to_flush) +{ + size_t avail; + int flushed = 0; + + /* wakeup if the device was unregistered */ + if (!indio_dev->info) + return true; + + /* drain the buffer if it was disabled */ + if (!iio_buffer_is_active(buf)) { + to_wait = min_t(size_t, to_wait, 1); + to_flush = 0; + } + + avail = iio_buffer_data_available(buf); + + if (avail >= to_wait) { + /* force a flush for non-blocking reads */ + if (!to_wait && !avail && to_flush) + iio_buffer_flush_hwfifo(indio_dev, buf, to_flush); + return true; + } + + if (to_flush) + flushed = iio_buffer_flush_hwfifo(indio_dev, buf, + to_wait - avail); + if (flushed <= 0) + return false; + + if (avail + flushed >= to_wait) + return true; + + return false; +} + /** * iio_buffer_read_first_n_outer() - chrdev read for buffer access * @@ -53,6 +99,9 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, { struct iio_dev *indio_dev = filp->private_data; struct iio_buffer *rb = indio_dev->buffer; + size_t datum_size; + size_t to_wait = 0; + size_t to_read; int ret; if (!indio_dev->info) @@ -61,19 +110,28 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, if (!rb || !rb->access->read_first_n) return -EINVAL; + datum_size = rb->bytes_per_datum; + + /* + * If datum_size is 0 there will never be anything to read from the + * buffer, so signal end of file now. + */ + if (!datum_size) + return 0; + + to_read = min_t(size_t, n / datum_size, rb->watermark); + + if (!(filp->f_flags & O_NONBLOCK)) + to_wait = to_read; + do { - if (!iio_buffer_data_available(rb)) { - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; + ret = wait_event_interruptible(rb->pollq, + iio_buffer_ready(indio_dev, rb, to_wait, to_read)); + if (ret) + return ret; - ret = wait_event_interruptible(rb->pollq, - iio_buffer_data_available(rb) || - indio_dev->info == NULL); - if (ret) - return ret; - if (indio_dev->info == NULL) - return -ENODEV; - } + if (!indio_dev->info) + return -ENODEV; ret = rb->access->read_first_n(rb, n, buf); if (ret == 0 && (filp->f_flags & O_NONBLOCK)) @@ -96,9 +154,8 @@ unsigned int iio_buffer_poll(struct file *filp, return -ENODEV; poll_wait(filp, &rb->pollq, wait); - if (iio_buffer_data_available(rb)) + if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) return POLLIN | POLLRDNORM; - /* need a way of knowing if there may be enough data... */ return 0; } @@ -123,6 +180,7 @@ void iio_buffer_init(struct iio_buffer *buffer) INIT_LIST_HEAD(&buffer->buffer_list); init_waitqueue_head(&buffer->pollq); kref_init(&buffer->ref); + buffer->watermark = 1; } EXPORT_SYMBOL(iio_buffer_init); @@ -416,6 +474,11 @@ static ssize_t iio_buffer_write_length(struct device *dev, buffer->access->set_length(buffer, val); ret = 0; } + if (ret) + goto out; + if (buffer->length && buffer->length < buffer->watermark) + buffer->watermark = buffer->length; +out: mutex_unlock(&indio_dev->mlock); return ret ? ret : len; @@ -472,6 +535,7 @@ static void iio_buffer_activate(struct iio_dev *indio_dev, static void iio_buffer_deactivate(struct iio_buffer *buffer) { list_del_init(&buffer->buffer_list); + wake_up_interruptible(&buffer->pollq); iio_buffer_put(buffer); } @@ -629,19 +693,16 @@ static int __iio_update_buffers(struct iio_dev *indio_dev, } } /* Definitely possible for devices to support both of these. */ - if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) { - if (!indio_dev->trig) { - printk(KERN_INFO "Buffer not started: no trigger\n"); - ret = -EINVAL; - /* Can only occur on first buffer */ - goto error_run_postdisable; - } + if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { indio_dev->currentmode = INDIO_BUFFER_TRIGGERED; } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) { indio_dev->currentmode = INDIO_BUFFER_HARDWARE; } else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) { indio_dev->currentmode = INDIO_BUFFER_SOFTWARE; } else { /* Should never be reached */ + /* Can only occur on first buffer */ + if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) + pr_info("Buffer not started: no trigger\n"); ret = -EINVAL; goto error_run_postdisable; } @@ -754,12 +815,68 @@ done: static const char * const iio_scan_elements_group_name = "scan_elements"; +static ssize_t iio_buffer_show_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *buffer = indio_dev->buffer; + + return sprintf(buf, "%u\n", buffer->watermark); +} + +static ssize_t iio_buffer_store_watermark(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *buffer = indio_dev->buffer; + unsigned int val; + int ret; + + ret = kstrtouint(buf, 10, &val); + if (ret) + return ret; + if (!val) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + + if (val > buffer->length) { + ret = -EINVAL; + goto out; + } + + if (iio_buffer_is_active(indio_dev->buffer)) { + ret = -EBUSY; + goto out; + } + + buffer->watermark = val; + + if (indio_dev->info->hwfifo_set_watermark) + indio_dev->info->hwfifo_set_watermark(indio_dev, val); +out: + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length, iio_buffer_write_length); static struct device_attribute dev_attr_length_ro = __ATTR(length, S_IRUGO, iio_buffer_read_length, NULL); static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, iio_buffer_show_enable, iio_buffer_store_enable); +static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR, + iio_buffer_show_watermark, iio_buffer_store_watermark); + +static struct attribute *iio_buffer_attrs[] = { + &dev_attr_length.attr, + &dev_attr_enable.attr, + &dev_attr_watermark.attr, +}; int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { @@ -778,21 +895,23 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) attrcount++; } - buffer->buffer_group.name = "buffer"; - buffer->buffer_group.attrs = kcalloc(attrcount + 3, - sizeof(*buffer->buffer_group.attrs), GFP_KERNEL); - if (!buffer->buffer_group.attrs) + attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1, + sizeof(struct attribute *), GFP_KERNEL); + if (!attr) return -ENOMEM; - if (buffer->access->set_length) - buffer->buffer_group.attrs[0] = &dev_attr_length.attr; - else - buffer->buffer_group.attrs[0] = &dev_attr_length_ro.attr; - buffer->buffer_group.attrs[1] = &dev_attr_enable.attr; + memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs)); + if (!buffer->access->set_length) + attr[0] = &dev_attr_length_ro.attr; + if (buffer->attrs) - memcpy(&buffer->buffer_group.attrs[2], buffer->attrs, - sizeof(*&buffer->buffer_group.attrs) * attrcount); - buffer->buffer_group.attrs[attrcount+2] = NULL; + memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs, + sizeof(struct attribute *) * attrcount); + + attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL; + + buffer->buffer_group.name = "buffer"; + buffer->buffer_group.attrs = attr; indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group; @@ -937,8 +1056,18 @@ static const void *iio_demux(struct iio_buffer *buffer, static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data) { const void *dataout = iio_demux(buffer, data); + int ret; - return buffer->access->store_to(buffer, dataout); + ret = buffer->access->store_to(buffer, dataout); + if (ret) + return ret; + + /* + * We can't just test for watermark to decide if we wake the poll queue + * because read may request less samples than the watermark. + */ + wake_up_interruptible_poll(&buffer->pollq, POLLIN | POLLRDNORM); + return 0; } static void iio_buffer_demux_free(struct iio_buffer *buffer) diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index b2beea0..847ca56 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -83,9 +83,6 @@ static int iio_store_to_kfifo(struct iio_buffer *r, ret = kfifo_in(&kf->kf, data, 1); if (ret != 1) return -EBUSY; - - wake_up_interruptible_poll(&r->pollq, POLLIN | POLLRDNORM); - return 0; } @@ -109,16 +106,16 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r, return copied; } -static bool iio_kfifo_buf_data_available(struct iio_buffer *r) +static size_t iio_kfifo_buf_data_available(struct iio_buffer *r) { struct iio_kfifo *kf = iio_to_kfifo(r); - bool empty; + size_t samples; mutex_lock(&kf->user_lock); - empty = kfifo_is_empty(&kf->kf); + samples = kfifo_len(&kf->kf); mutex_unlock(&kf->user_lock); - return !empty; + return samples; } static void iio_kfifo_buffer_release(struct iio_buffer *buffer) diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index a224afd..01a1a16 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -59,6 +59,16 @@ config CM3232 To compile this driver as a module, choose M here: the module will be called cm3232. +config CM3323 + depends on I2C + tristate "Capella CM3323 color light sensor" + help + Say Y here if you want to build a driver for Capela CM3323 + color sensor. + + To compile this driver as a module, choose M here: the module will + be called cm3323. + config CM36651 depends on I2C tristate "CM36651 driver" diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index b12a516..ad7c30f 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_AL3320A) += al3320a.o obj-$(CONFIG_APDS9300) += apds9300.o obj-$(CONFIG_CM32181) += cm32181.o obj-$(CONFIG_CM3232) += cm3232.o +obj-$(CONFIG_CM3323) += cm3323.o obj-$(CONFIG_CM36651) += cm36651.o obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c index 90e3519..39c8d99 100644 --- a/drivers/iio/light/cm3232.c +++ b/drivers/iio/light/cm3232.c @@ -378,6 +378,39 @@ static const struct i2c_device_id cm3232_id[] = { {} }; +#ifdef CONFIG_PM_SLEEP +static int cm3232_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct cm3232_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + int ret; + + chip->regs_cmd |= CM3232_CMD_ALS_DISABLE; + ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, + chip->regs_cmd); + + return ret; +} + +static int cm3232_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct cm3232_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + int ret; + + chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE; + ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD, + chip->regs_cmd | CM3232_CMD_ALS_RESET); + + return ret; +} + +static const struct dev_pm_ops cm3232_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)}; +#endif + MODULE_DEVICE_TABLE(i2c, cm3232_id); static const struct of_device_id cm3232_of_match[] = { @@ -390,6 +423,9 @@ static struct i2c_driver cm3232_driver = { .name = "cm3232", .owner = THIS_MODULE, .of_match_table = of_match_ptr(cm3232_of_match), +#ifdef CONFIG_PM_SLEEP + .pm = &cm3232_pm_ops, +#endif }, .id_table = cm3232_id, .probe = cm3232_probe, diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c new file mode 100644 index 0000000..869033e --- /dev/null +++ b/drivers/iio/light/cm3323.c @@ -0,0 +1,286 @@ +/* + * CM3323 - Capella Color Light Sensor + * + * Copyright (c) 2015, Intel Corporation. + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * IIO driver for CM3323 (7-bit I2C slave address 0x10) + * + * TODO: calibscale to correct the lens factor + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define CM3323_DRV_NAME "cm3323" + +#define CM3323_CMD_CONF 0x00 +#define CM3323_CMD_RED_DATA 0x08 +#define CM3323_CMD_GREEN_DATA 0x09 +#define CM3323_CMD_BLUE_DATA 0x0A +#define CM3323_CMD_CLEAR_DATA 0x0B + +#define CM3323_CONF_SD_BIT BIT(0) /* sensor disable */ +#define CM3323_CONF_AF_BIT BIT(1) /* auto/manual force mode */ +#define CM3323_CONF_IT_MASK (BIT(4) | BIT(5) | BIT(6)) +#define CM3323_CONF_IT_SHIFT 4 + +#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28" + +static const struct { + int val; + int val2; +} cm3323_int_time[] = { + {0, 40000}, /* 40 ms */ + {0, 80000}, /* 80 ms */ + {0, 160000}, /* 160 ms */ + {0, 320000}, /* 320 ms */ + {0, 640000}, /* 640 ms */ + {1, 280000}, /* 1280 ms */ +}; + +struct cm3323_data { + struct i2c_client *client; + u16 reg_conf; + struct mutex mutex; +}; + +#define CM3323_COLOR_CHANNEL(_color, _addr) { \ + .type = IIO_INTENSITY, \ + .modified = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \ + .channel2 = IIO_MOD_LIGHT_##_color, \ + .address = _addr, \ +} + +static const struct iio_chan_spec cm3323_channels[] = { + CM3323_COLOR_CHANNEL(RED, CM3323_CMD_RED_DATA), + CM3323_COLOR_CHANNEL(GREEN, CM3323_CMD_GREEN_DATA), + CM3323_COLOR_CHANNEL(BLUE, CM3323_CMD_BLUE_DATA), + CM3323_COLOR_CHANNEL(CLEAR, CM3323_CMD_CLEAR_DATA), +}; + +static IIO_CONST_ATTR_INT_TIME_AVAIL(CM3323_INT_TIME_AVAILABLE); + +static struct attribute *cm3323_attributes[] = { + &iio_const_attr_integration_time_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group cm3323_attribute_group = { + .attrs = cm3323_attributes, +}; + +static int cm3323_init(struct iio_dev *indio_dev) +{ + int ret; + struct cm3323_data *data = iio_priv(indio_dev); + + ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_conf\n"); + return ret; + } + + /* enable sensor and set auto force mode */ + ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT); + + ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_conf\n"); + return ret; + } + + data->reg_conf = ret; + + return 0; +} + +static void cm3323_disable(struct iio_dev *indio_dev) +{ + int ret; + struct cm3323_data *data = iio_priv(indio_dev); + + ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, + CM3323_CONF_SD_BIT); + if (ret < 0) + dev_err(&data->client->dev, "Error writing reg_conf\n"); +} + +static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2) +{ + int i, ret; + u16 reg_conf; + + for (i = 0; i < ARRAY_SIZE(cm3323_int_time); i++) { + if (val == cm3323_int_time[i].val && + val2 == cm3323_int_time[i].val2) { + reg_conf = data->reg_conf; + reg_conf |= i << CM3323_CONF_IT_SHIFT; + + ret = i2c_smbus_write_word_data(data->client, + CM3323_CMD_CONF, + reg_conf); + if (ret < 0) + return ret; + + data->reg_conf = reg_conf; + return 0; + } + } + return -EINVAL; +} + +static int cm3323_get_it_bits(struct cm3323_data *data) +{ + int bits; + + bits = (data->reg_conf & CM3323_CONF_IT_MASK) >> + CM3323_CONF_IT_SHIFT; + + if (bits >= ARRAY_SIZE(cm3323_int_time)) + return -EINVAL; + return bits; +} + +static int cm3323_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int i, ret; + struct cm3323_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->mutex); + ret = i2c_smbus_read_word_data(data->client, chan->address); + if (ret < 0) { + mutex_unlock(&data->mutex); + return ret; + } + *val = ret; + mutex_unlock(&data->mutex); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + mutex_lock(&data->mutex); + i = cm3323_get_it_bits(data); + if (i < 0) { + mutex_unlock(&data->mutex); + return -EINVAL; + } + + *val = cm3323_int_time[i].val; + *val2 = cm3323_int_time[i].val2; + mutex_unlock(&data->mutex); + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int cm3323_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct cm3323_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + mutex_lock(&data->mutex); + ret = cm3323_set_it_bits(data, val, val2); + mutex_unlock(&data->mutex); + + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info cm3323_info = { + .driver_module = THIS_MODULE, + .read_raw = cm3323_read_raw, + .write_raw = cm3323_write_raw, + .attrs = &cm3323_attribute_group, +}; + +static int cm3323_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cm3323_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + mutex_init(&data->mutex); + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &cm3323_info; + indio_dev->name = CM3323_DRV_NAME; + indio_dev->channels = cm3323_channels; + indio_dev->num_channels = ARRAY_SIZE(cm3323_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = cm3323_init(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "cm3323 chip init failed\n"); + return ret; + } + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "failed to register iio dev\n"); + goto err_init; + } + return 0; +err_init: + cm3323_disable(indio_dev); + return ret; +} + +static int cm3323_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + cm3323_disable(indio_dev); + + return 0; +} + +static const struct i2c_device_id cm3323_id[] = { + {"cm3323", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, cm3323_id); + +static struct i2c_driver cm3323_driver = { + .driver = { + .name = CM3323_DRV_NAME, + }, + .probe = cm3323_probe, + .remove = cm3323_remove, + .id_table = cm3323_id, +}; + +module_i2c_driver(cm3323_driver); + +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>"); +MODULE_DESCRIPTION("Capella CM3323 Color Light Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 221ed16..32b6449 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -46,6 +46,7 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <asm/unaligned.h> #include <linux/iio/buffer.h> #include <linux/iio/events.h> #include <linux/iio/iio.h> @@ -966,7 +967,6 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) struct iio_dev *indio_dev = pf->indio_dev; struct gp2ap020a00f_data *priv = iio_priv(indio_dev); size_t d_size = 0; - __le32 light_lux; int i, out_val, ret; for_each_set_bit(i, indio_dev->active_scan_mask, @@ -981,8 +981,8 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) { out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]); gp2ap020a00f_output_to_lux(priv, &out_val); - light_lux = cpu_to_le32(out_val); - memcpy(&priv->buffer[d_size], (u8 *)&light_lux, 4); + + put_unaligned_le32(out_val, &priv->buffer[d_size]); d_size += 4; } else { d_size += 2; diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index 29de7e7..3a3af89 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -308,7 +308,7 @@ static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg) } } -static struct regmap_config jsa1212_regmap_config = { +static const struct regmap_config jsa1212_regmap_config = { .name = JSA1212_REGMAP_NAME, .reg_bits = 8, .val_bits = 8, diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 62b7072..78b8783 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -333,6 +333,13 @@ static int ltr501_init(struct ltr501_data *data) data->ps_contr); } +static int ltr501_powerdown(struct ltr501_data *data) +{ + return ltr501_write_contr(data->client, + data->als_contr & ~LTR501_CONTR_ACTIVE, + data->ps_contr & ~LTR501_CONTR_ACTIVE); +} + static int ltr501_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -370,7 +377,7 @@ static int ltr501_probe(struct i2c_client *client, ret = iio_triggered_buffer_setup(indio_dev, NULL, ltr501_trigger_handler, NULL); if (ret) - return ret; + goto powerdown_on_error; ret = iio_device_register(indio_dev); if (ret) @@ -380,16 +387,11 @@ static int ltr501_probe(struct i2c_client *client, error_unreg_buffer: iio_triggered_buffer_cleanup(indio_dev); +powerdown_on_error: + ltr501_powerdown(data); return ret; } -static int ltr501_powerdown(struct ltr501_data *data) -{ - return ltr501_write_contr(data->client, - data->als_contr & ~LTR501_CONTR_ACTIVE, - data->ps_contr & ~LTR501_CONTR_ACTIVE); -} - static int ltr501_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index e3106b4..261d517 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -321,6 +321,12 @@ static const struct iio_info mag3110_info = { static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0}; +static int mag3110_standby(struct mag3110_data *data) +{ + return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, + data->ctrl_reg1 & ~MAG3110_CTRL_AC); +} + static int mag3110_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -360,12 +366,12 @@ static int mag3110_probe(struct i2c_client *client, ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2, MAG3110_CTRL_AUTO_MRST_EN); if (ret < 0) - return ret; + goto standby_on_error; ret = iio_triggered_buffer_setup(indio_dev, NULL, mag3110_trigger_handler, NULL); if (ret < 0) - return ret; + goto standby_on_error; ret = iio_device_register(indio_dev); if (ret < 0) @@ -374,15 +380,11 @@ static int mag3110_probe(struct i2c_client *client, buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); +standby_on_error: + mag3110_standby(iio_priv(indio_dev)); return ret; } -static int mag3110_standby(struct mag3110_data *data) -{ - return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, - data->ctrl_reg1 & ~MAG3110_CTRL_AC); -} - static int mag3110_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index a3be537..fa62950 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -52,6 +52,33 @@ config MPL3115 To compile this driver as a module, choose M here: the module will be called mpl3115. +config MS5611 + tristate "Measurement Specialities MS5611 pressure sensor driver" + help + Say Y here to build support for the Measurement Specialities + MS5611 pressure and temperature sensor. + + To compile this driver as a module, choose M here: the module will + be called ms5611_core. + +config MS5611_I2C + tristate "support I2C bus connection" + depends on I2C && MS5611 + help + Say Y here to build I2C bus support for MS5611. + + To compile this driver as a module, choose M here: the module will + be called ms5611_i2c. + +config MS5611_SPI + tristate "support SPI bus connection" + depends on SPI_MASTER && MS5611 + help + Say Y here to build SPI bus support for MS5611. + + To compile this driver as a module, choose M here: the module will + be called ms5611_spi. + config IIO_ST_PRESS tristate "STMicroelectronics pressure sensor Driver" depends on (I2C || SPI_MASTER) && SYSFS diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 88011f2..a4f98f8 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -7,6 +7,9 @@ obj-$(CONFIG_BMP280) += bmp280.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_MPL115) += mpl115.o obj-$(CONFIG_MPL3115) += mpl3115.o +obj-$(CONFIG_MS5611) += ms5611_core.o +obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o +obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o st_pressure-y := st_pressure_core.o st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h new file mode 100644 index 0000000..099c6cd --- /dev/null +++ b/drivers/iio/pressure/ms5611.h @@ -0,0 +1,44 @@ +/* + * MS5611 pressure and temperature sensor driver + * + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _MS5611_H +#define _MS5611_H + +#include <linux/device.h> +#include <linux/iio/iio.h> +#include <linux/mutex.h> + +#define MS5611_RESET 0x1e +#define MS5611_READ_ADC 0x00 +#define MS5611_READ_PROM_WORD 0xA0 +#define MS5611_START_TEMP_CONV 0x58 +#define MS5611_START_PRESSURE_CONV 0x48 + +#define MS5611_CONV_TIME_MIN 9040 +#define MS5611_CONV_TIME_MAX 10000 + +#define MS5611_PROM_WORDS_NB 8 + +struct ms5611_state { + void *client; + struct mutex lock; + + int (*reset)(struct device *dev); + int (*read_prom_word)(struct device *dev, int index, u16 *word); + int (*read_adc_temp_and_pressure)(struct device *dev, + s32 *temp, s32 *pressure); + + u16 prom[MS5611_PROM_WORDS_NB]; +}; + +int ms5611_probe(struct iio_dev *indio_dev, struct device *dev); + +#endif /* _MS5611_H */ diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c new file mode 100644 index 0000000..e42c853 --- /dev/null +++ b/drivers/iio/pressure/ms5611_core.c @@ -0,0 +1,215 @@ +/* + * MS5611 pressure and temperature sensor driver + * + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Data sheet: + * http://www.meas-spec.com/downloads/MS5611-01BA03.pdf + * + */ + +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/delay.h> + +#include "ms5611.h" + +static bool ms5611_prom_is_valid(u16 *prom, size_t len) +{ + int i, j; + uint16_t crc = 0, crc_orig = prom[7] & 0x000F; + + prom[7] &= 0xFF00; + + for (i = 0; i < len * 2; i++) { + if (i % 2 == 1) + crc ^= prom[i >> 1] & 0x00FF; + else + crc ^= prom[i >> 1] >> 8; + + for (j = 0; j < 8; j++) { + if (crc & 0x8000) + crc = (crc << 1) ^ 0x3000; + else + crc <<= 1; + } + } + + crc = (crc >> 12) & 0x000F; + + return crc_orig != 0x0000 && crc == crc_orig; +} + +static int ms5611_read_prom(struct iio_dev *indio_dev) +{ + int ret, i; + struct ms5611_state *st = iio_priv(indio_dev); + + for (i = 0; i < MS5611_PROM_WORDS_NB; i++) { + ret = st->read_prom_word(&indio_dev->dev, i, &st->prom[i]); + if (ret < 0) { + dev_err(&indio_dev->dev, + "failed to read prom at %d\n", i); + return ret; + } + } + + if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) { + dev_err(&indio_dev->dev, "PROM integrity check failed\n"); + return -ENODEV; + } + + return 0; +} + +static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev, + s32 *temp, s32 *pressure) +{ + int ret; + s32 t, p; + s64 off, sens, dt; + struct ms5611_state *st = iio_priv(indio_dev); + + ret = st->read_adc_temp_and_pressure(&indio_dev->dev, &t, &p); + if (ret < 0) { + dev_err(&indio_dev->dev, + "failed to read temperature and pressure\n"); + return ret; + } + + dt = t - (st->prom[5] << 8); + off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7); + sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8); + + t = 2000 + ((st->prom[6] * dt) >> 23); + if (t < 2000) { + s64 off2, sens2, t2; + + t2 = (dt * dt) >> 31; + off2 = (5 * (t - 2000) * (t - 2000)) >> 1; + sens2 = off2 >> 1; + + if (t < -1500) { + s64 tmp = (t + 1500) * (t + 1500); + + off2 += 7 * tmp; + sens2 += (11 * tmp) >> 1; + } + + t -= t2; + off -= off2; + sens -= sens2; + } + + *temp = t; + *pressure = (((p * sens) >> 21) - off) >> 15; + + return 0; +} + +static int ms5611_reset(struct iio_dev *indio_dev) +{ + int ret; + struct ms5611_state *st = iio_priv(indio_dev); + + ret = st->reset(&indio_dev->dev); + if (ret < 0) { + dev_err(&indio_dev->dev, "failed to reset device\n"); + return ret; + } + + usleep_range(3000, 4000); + + return 0; +} + +static int ms5611_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + s32 temp, pressure; + struct ms5611_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + mutex_lock(&st->lock); + ret = ms5611_read_temp_and_pressure(indio_dev, + &temp, &pressure); + mutex_unlock(&st->lock); + if (ret < 0) + return ret; + + switch (chan->type) { + case IIO_TEMP: + *val = temp * 10; + return IIO_VAL_INT; + case IIO_PRESSURE: + *val = pressure / 1000; + *val2 = (pressure % 1000) * 1000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static const struct iio_chan_spec ms5611_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_SCALE) + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_SCALE) + } +}; + +static const struct iio_info ms5611_info = { + .read_raw = &ms5611_read_raw, + .driver_module = THIS_MODULE, +}; + +static int ms5611_init(struct iio_dev *indio_dev) +{ + int ret; + + ret = ms5611_reset(indio_dev); + if (ret < 0) + return ret; + + return ms5611_read_prom(indio_dev); +} + +int ms5611_probe(struct iio_dev *indio_dev, struct device *dev) +{ + int ret; + struct ms5611_state *st = iio_priv(indio_dev); + + mutex_init(&st->lock); + indio_dev->dev.parent = dev; + indio_dev->name = dev->driver->name; + indio_dev->info = &ms5611_info; + indio_dev->channels = ms5611_channels; + indio_dev->num_channels = ARRAY_SIZE(ms5611_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ms5611_init(indio_dev); + if (ret < 0) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL(ms5611_probe); + +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); +MODULE_DESCRIPTION("MS5611 core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c new file mode 100644 index 0000000..748fd9a --- /dev/null +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -0,0 +1,128 @@ +/* + * MS5611 pressure and temperature sensor driver (I2C bus) + * + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 7-bit I2C slave addresses: + * + * 0x77 (CSB pin low) + * 0x76 (CSB pin high) + * + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include "ms5611.h" + +static int ms5611_i2c_reset(struct device *dev) +{ + struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + return i2c_smbus_write_byte(st->client, MS5611_RESET); +} + +static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word) +{ + int ret; + struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + ret = i2c_smbus_read_word_swapped(st->client, + MS5611_READ_PROM_WORD + (index << 1)); + if (ret < 0) + return ret; + + *word = ret; + + return 0; +} + +static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val) +{ + int ret; + u8 buf[3]; + + ret = i2c_smbus_read_i2c_block_data(st->client, MS5611_READ_ADC, + 3, buf); + if (ret < 0) + return ret; + + *val = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + +static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev, + s32 *temp, s32 *pressure) +{ + int ret; + struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + ret = i2c_smbus_write_byte(st->client, MS5611_START_TEMP_CONV); + if (ret < 0) + return ret; + + usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX); + + ret = ms5611_i2c_read_adc(st, temp); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte(st->client, MS5611_START_PRESSURE_CONV); + if (ret < 0) + return ret; + + usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX); + + return ms5611_i2c_read_adc(st, pressure); +} + +static int ms5611_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ms5611_state *st; + struct iio_dev *indio_dev; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->reset = ms5611_i2c_reset; + st->read_prom_word = ms5611_i2c_read_prom_word; + st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure; + st->client = client; + + return ms5611_probe(indio_dev, &client->dev); +} + +static const struct i2c_device_id ms5611_id[] = { + { "ms5611", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ms5611_id); + +static struct i2c_driver ms5611_driver = { + .driver = { + .name = "ms5611", + .owner = THIS_MODULE, + }, + .id_table = ms5611_id, + .probe = ms5611_i2c_probe, +}; +module_i2c_driver(ms5611_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); +MODULE_DESCRIPTION("MS5611 i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c new file mode 100644 index 0000000..976726f --- /dev/null +++ b/drivers/iio/pressure/ms5611_spi.c @@ -0,0 +1,127 @@ +/* + * MS5611 pressure and temperature sensor driver (SPI bus) + * + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "ms5611.h" + +static int ms5611_spi_reset(struct device *dev) +{ + u8 cmd = MS5611_RESET; + struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + return spi_write_then_read(st->client, &cmd, 1, NULL, 0); +} + +static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word) +{ + int ret; + struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1)); + if (ret < 0) + return ret; + + *word = ret; + + return 0; +} + +static int ms5611_spi_read_adc(struct device *dev, s32 *val) +{ + int ret; + u8 buf[3] = { MS5611_READ_ADC }; + struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + ret = spi_write_then_read(st->client, buf, 1, buf, 3); + if (ret < 0) + return ret; + + *val = (buf[0] << 16) | (buf[1] << 8) | buf[2]; + + return 0; +} + +static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, + s32 *temp, s32 *pressure) +{ + u8 cmd; + int ret; + struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + cmd = MS5611_START_TEMP_CONV; + ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0); + if (ret < 0) + return ret; + + usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX); + + ret = ms5611_spi_read_adc(dev, temp); + if (ret < 0) + return ret; + + cmd = MS5611_START_PRESSURE_CONV; + ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0); + if (ret < 0) + return ret; + + usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX); + + return ms5611_spi_read_adc(dev, pressure); +} + +static int ms5611_spi_probe(struct spi_device *spi) +{ + int ret; + struct ms5611_state *st; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + spi->mode = SPI_MODE_0; + spi->max_speed_hz = 20000000; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) + return ret; + + st = iio_priv(indio_dev); + st->reset = ms5611_spi_reset; + st->read_prom_word = ms5611_spi_read_prom_word; + st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure; + st->client = spi; + + return ms5611_probe(indio_dev, &spi->dev); +} + +static const struct spi_device_id ms5611_id[] = { + { "ms5611", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ms5611_id); + +static struct spi_driver ms5611_driver = { + .driver = { + .name = "ms5611", + .owner = THIS_MODULE, + }, + .id_table = ms5611_id, + .probe = ms5611_spi_probe, +}; +module_spi_driver(ms5611_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); +MODULE_DESCRIPTION("MS5611 spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 89fca3a..fa40f6d 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -618,16 +618,12 @@ static int sx9500_gpio_probe(struct i2c_client *client, dev = &client->dev; /* data ready gpio interrupt pin */ - gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0); + gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN); if (IS_ERR(gpio)) { dev_err(dev, "acpi gpio get index failed\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); diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index c8b6ac8..a112fc9 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -2,6 +2,7 @@ * mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor * * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> + * Copyright (c) 2015 Essensium NV * * This file is subject to the terms and conditions of version 2 of * the GNU General Public License. See the file COPYING in the main @@ -20,11 +21,35 @@ #include <linux/iio/iio.h> -#define MLX90614_OP_RAM 0x00 +#define MLX90614_OP_RAM 0x00 +#define MLX90614_OP_EEPROM 0x20 +#define MLX90614_OP_SLEEP 0xff /* RAM offsets with 16-bit data, MSB first */ -#define MLX90614_TA 0x06 /* ambient temperature */ -#define MLX90614_TOBJ1 0x07 /* object temperature */ +#define MLX90614_RAW1 (MLX90614_OP_RAM | 0x04) /* raw data IR channel 1 */ +#define MLX90614_RAW2 (MLX90614_OP_RAM | 0x05) /* raw data IR channel 2 */ +#define MLX90614_TA (MLX90614_OP_RAM | 0x06) /* ambient temperature */ +#define MLX90614_TOBJ1 (MLX90614_OP_RAM | 0x07) /* object 1 temperature */ +#define MLX90614_TOBJ2 (MLX90614_OP_RAM | 0x08) /* object 2 temperature */ + +/* EEPROM offsets with 16-bit data, MSB first */ +#define MLX90614_EMISSIVITY (MLX90614_OP_EEPROM | 0x04) /* emissivity correction coefficient */ +#define MLX90614_CONFIG (MLX90614_OP_EEPROM | 0x05) /* configuration register */ + +/* Control bits in configuration register */ +#define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */ +#define MLX90614_CONFIG_IIR_MASK (0x7 << MLX90614_CONFIG_IIR_SHIFT) +#define MLX90614_CONFIG_DUAL_SHIFT 6 /* single (0) or dual (1) IR sensor */ +#define MLX90614_CONFIG_DUAL_MASK (1 << MLX90614_CONFIG_DUAL_SHIFT) +#define MLX90614_CONFIG_FIR_SHIFT 8 /* FIR coefficient */ +#define MLX90614_CONFIG_FIR_MASK (0x7 << MLX90614_CONFIG_FIR_SHIFT) +#define MLX90614_CONFIG_GAIN_SHIFT 11 /* gain */ +#define MLX90614_CONFIG_GAIN_MASK (0x7 << MLX90614_CONFIG_GAIN_SHIFT) + +/* Timings (in ms) */ +#define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */ +#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */ +#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */ struct mlx90614_data { struct i2c_client *client; @@ -35,26 +60,34 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct mlx90614_data *data = iio_priv(indio_dev); + u8 cmd; s32 ret; switch (mask) { case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */ switch (channel->channel2) { case IIO_MOD_TEMP_AMBIENT: - ret = i2c_smbus_read_word_data(data->client, - MLX90614_OP_RAM | MLX90614_TA); - if (ret < 0) - return ret; + cmd = MLX90614_TA; break; case IIO_MOD_TEMP_OBJECT: - ret = i2c_smbus_read_word_data(data->client, - MLX90614_OP_RAM | MLX90614_TOBJ1); - if (ret < 0) - return ret; + switch (channel->channel) { + case 0: + cmd = MLX90614_TOBJ1; + break; + case 1: + cmd = MLX90614_TOBJ2; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; } + + ret = i2c_smbus_read_word_data(data->client, cmd); + if (ret < 0) + return ret; *val = ret; return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: @@ -86,6 +119,16 @@ static const struct iio_chan_spec mlx90614_channels[] = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, + { + .type = IIO_TEMP, + .indexed = 1, + .modified = 1, + .channel = 1, + .channel2 = IIO_MOD_TEMP_OBJECT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + }, }; static const struct iio_info mlx90614_info = { @@ -93,11 +136,25 @@ static const struct iio_info mlx90614_info = { .driver_module = THIS_MODULE, }; +/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */ +static int mlx90614_probe_num_ir_sensors(struct i2c_client *client) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG); + + if (ret < 0) + return ret; + + return (ret & MLX90614_CONFIG_DUAL_MASK) ? 1 : 0; +} + static int mlx90614_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct mlx90614_data *data; + int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -115,8 +172,21 @@ static int mlx90614_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mlx90614_info; - indio_dev->channels = mlx90614_channels; - indio_dev->num_channels = ARRAY_SIZE(mlx90614_channels); + ret = mlx90614_probe_num_ir_sensors(client); + switch (ret) { + case 0: + dev_dbg(&client->dev, "Found single sensor"); + indio_dev->channels = mlx90614_channels; + indio_dev->num_channels = 2; + break; + case 1: + dev_dbg(&client->dev, "Found dual sensor"); + indio_dev->channels = mlx90614_channels; + indio_dev->num_channels = 3; + break; + default: + return ret; + } return iio_device_register(indio_dev); } @@ -146,5 +216,6 @@ static struct i2c_driver mlx90614_driver = { module_i2c_driver(mlx90614_driver); MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_AUTHOR("Vianney le ClĂ©ment de Saint-Marcq <vianney.leclement@essensium.com>"); MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver"); MODULE_LICENSE("GPL"); |