From ff7d4f5981a8a139ead70adef3c1d0ed574bca01 Mon Sep 17 00:00:00 2001 From: Laurent Navet Date: Sat, 30 May 2015 22:35:36 +0200 Subject: iio: buffer: remove unneeded test The same code is executed regardless ret value, so this test can be removed. Also fix coverity scan CID 1268786. Signed-off-by: Laurent Navet Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 11291259b7b9..dad61ab9b36f 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -888,8 +888,6 @@ static ssize_t iio_buffer_store_enable(struct device *dev, ret = __iio_update_buffers(indio_dev, NULL, indio_dev->buffer); - if (ret < 0) - goto done; done: mutex_unlock(&indio_dev->mlock); return (ret < 0) ? ret : len; -- cgit v1.2.3 From 629bc02331f7aae6ef775fb4c15e6d8aa58722f1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 29 May 2015 18:14:20 +0200 Subject: iio: Always compute masklength Even if no userspace consumer buffer is attached to the IIO device at registration we still need to compute the masklength, since it is possible that a in-kernel consumer buffer is going to get attached to the device at a later point. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index dad61ab9b36f..209c7ad793c5 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -966,6 +966,15 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) int ret, i, attrn, attrcount, attrcount_orig = 0; const struct iio_chan_spec *channels; + channels = indio_dev->channels; + if (channels) { + int ml = indio_dev->masklength; + + for (i = 0; i < indio_dev->num_channels; i++) + ml = max(ml, channels[i].scan_index + 1); + indio_dev->masklength = ml; + } + if (!buffer) return 0; @@ -1009,12 +1018,6 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) if (channels[i].scan_index < 0) continue; - /* Establish necessary mask length */ - if (channels[i].scan_index > - (int)indio_dev->masklength - 1) - indio_dev->masklength - = channels[i].scan_index + 1; - ret = iio_buffer_add_channel_sysfs(indio_dev, &channels[i]); if (ret < 0) -- cgit v1.2.3 From 225d59adf1c899176cce0fc80e42b1d1c12f109f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 29 May 2015 18:14:21 +0200 Subject: iio: Specify supported modes for buffers For each buffer type specify the supported device modes for this buffer. This allows us for devices which support multiple different operating modes to pick the correct operating mode based on the modes supported by the attached buffers. It also prevents that buffers with conflicting modes are attached to a device at the same time or that a buffer with a non-supported mode is attached to a device (e.g. in-kernel callback buffer to a device only supporting hardware mode). Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/buffer_cb.c | 2 ++ drivers/iio/industrialio-buffer.c | 18 +++++++++++++++--- drivers/iio/kfifo_buf.c | 2 ++ 3 files changed, 19 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c index eb46e728aa2e..1648e6e5a848 100644 --- a/drivers/iio/buffer_cb.c +++ b/drivers/iio/buffer_cb.c @@ -33,6 +33,8 @@ static void iio_buffer_cb_release(struct iio_buffer *buffer) static const struct iio_buffer_access_funcs iio_cb_access = { .store_to = &iio_buffer_cb_store_to, .release = &iio_buffer_cb_release, + + .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, }; struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 209c7ad793c5..0a14bd825fe0 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -604,6 +604,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, const unsigned long *scan_mask; struct iio_buffer *buffer; bool scan_timestamp; + unsigned int modes; memset(config, 0, sizeof(*config)); @@ -615,12 +616,23 @@ static int iio_verify_update(struct iio_dev *indio_dev, list_is_singular(&indio_dev->buffer_list)) return 0; + modes = indio_dev->modes; + + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + if (buffer == remove_buffer) + continue; + modes &= buffer->access->modes; + } + + if (insert_buffer) + modes &= insert_buffer->access->modes; + /* Definitely possible for devices to support both of these. */ - if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { + if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { config->mode = INDIO_BUFFER_TRIGGERED; - } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) { + } else if (modes & INDIO_BUFFER_HARDWARE) { config->mode = INDIO_BUFFER_HARDWARE; - } else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) { + } else if (modes & INDIO_BUFFER_SOFTWARE) { config->mode = INDIO_BUFFER_SOFTWARE; } else { /* Can only occur on first buffer */ diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index 847ca561afe0..db15684a4731 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -135,6 +135,8 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = { .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, .set_length = &iio_set_length_kfifo, .release = &iio_kfifo_buffer_release, + + .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED, }; struct iio_buffer *iio_kfifo_allocate(void) -- cgit v1.2.3 From 1e1ec2861e0d4307267096c3f74c17298c1cde98 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 29 May 2015 18:14:22 +0200 Subject: iio: Require strict scan mask matching in hardware mode In hardware mode we can not use the software demuxer, this means that the selected scan mask needs to match one of the available scan masks exactly. It also means that all attached buffers need to use the same scan mask. Given that when operating in hardware mode there is typically only a single buffer attached to the device this not an issue. Add a sanity check to make sure that only a single buffer is attached in hardware mode nevertheless. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 0a14bd825fe0..6eee1b044c60 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -239,13 +239,19 @@ static ssize_t iio_scan_el_show(struct device *dev, /* Note NULL used as error indicator as it doesn't make sense. */ static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks, unsigned int masklength, - const unsigned long *mask) + const unsigned long *mask, + bool strict) { if (bitmap_empty(mask, masklength)) return NULL; while (*av_masks) { - if (bitmap_subset(mask, av_masks, masklength)) - return av_masks; + if (strict) { + if (bitmap_equal(mask, av_masks, masklength)) + return av_masks; + } else { + if (bitmap_subset(mask, av_masks, masklength)) + return av_masks; + } av_masks += BITS_TO_LONGS(masklength); } return NULL; @@ -295,7 +301,7 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev, if (indio_dev->available_scan_masks) { mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, - trialmask); + trialmask, false); if (!mask) goto err_invalid_mask; } @@ -602,6 +608,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, { unsigned long *compound_mask; const unsigned long *scan_mask; + bool strict_scanmask = false; struct iio_buffer *buffer; bool scan_timestamp; unsigned int modes; @@ -631,7 +638,14 @@ static int iio_verify_update(struct iio_dev *indio_dev, if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { config->mode = INDIO_BUFFER_TRIGGERED; } else if (modes & INDIO_BUFFER_HARDWARE) { + /* + * Keep things simple for now and only allow a single buffer to + * be connected in hardware mode. + */ + if (insert_buffer && !list_empty(&indio_dev->buffer_list)) + return -EINVAL; config->mode = INDIO_BUFFER_HARDWARE; + strict_scanmask = true; } else if (modes & INDIO_BUFFER_SOFTWARE) { config->mode = INDIO_BUFFER_SOFTWARE; } else { @@ -666,7 +680,8 @@ static int iio_verify_update(struct iio_dev *indio_dev, if (indio_dev->available_scan_masks) { scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks, indio_dev->masklength, - compound_mask); + compound_mask, + strict_scanmask); kfree(compound_mask); if (scan_mask == NULL) return -EINVAL; -- cgit v1.2.3 From 5517547bf44731621338c1a4047196a49c790719 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 May 2015 11:20:52 +0300 Subject: iio: magnetometer: correct a harmless off by one check The line before limits i to 0-3 so the existing code works fine but the check is still off by one and >= is intended instead of >. Signed-off-by: Dan Carpenter Reviewed-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index aa6e25d3bfc3..c71392cf1370 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -308,7 +308,7 @@ static int mmc35240_read_raw(struct iio_dev *indio_dev, return ret; i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT; - if (i < 0 || i > ARRAY_SIZE(mmc35240_samp_freq)) + if (i < 0 || i >= ARRAY_SIZE(mmc35240_samp_freq)) return -EINVAL; *val = mmc35240_samp_freq[i]; -- cgit v1.2.3 From ed6e75c7dc5627c7251002936577052ae73ac2db Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 May 2015 11:24:25 +0300 Subject: iio: light: signedness bug in stk3310_write_raw() "index" needs to be signed for the error handling to work. Fixes: be9e6229d676 ('iio: light: Add support for Sensortek STK3310') Signed-off-by: Dan Carpenter Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index e79b9d89b024..fee4297d7c8f 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -370,7 +370,7 @@ static int stk3310_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { int ret; - unsigned int index; + int index; struct stk3310_data *data = iio_priv(indio_dev); switch (mask) { -- cgit v1.2.3 From a52ffebcf19afea27588abe75828f9d300e08f3d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:10 +0300 Subject: iio: magnetometer: mmc35240: i2c device name should be lower case This is the standard convention for i2c device name and also this is the name used in some Intel platforms DT files. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index c71392cf1370..d8cf5a6f9291 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -490,7 +490,7 @@ static const struct acpi_device_id mmc35240_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match); static const struct i2c_device_id mmc35240_id[] = { - {"MMC35240", 0}, + {"mmc35240", 0}, {} }; MODULE_DEVICE_TABLE(i2c, mmc35240_id); -- cgit v1.2.3 From bd35a214f56158d11e46c4a9d8436139f58e7099 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:11 +0300 Subject: iio: magnetometer: mmc35240: NULL terminate attribute array This avoid nasty crashes when registering the IIO device. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index d8cf5a6f9291..78da0d15c8eb 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -125,6 +125,7 @@ static const struct iio_chan_spec mmc35240_channels[] = { static struct attribute *mmc35240_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL }; static const struct attribute_group mmc35240_attribute_group = { -- cgit v1.2.3 From c2890547a035e019df646be5d56adc0ee1b0a327 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:12 +0300 Subject: iio: magnetometer: mmc35240: Fix broken processed value The current computation for fractional part of the magnetic field is broken. This patch fixes it by taking a different approach. We expose the raw reading in milli Gauss (to avoid rounding errors) with a scale of 0.001. Thus the final computation is done in userspace where floating point operation are more relaxed. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 43 +++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 16 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index 78da0d15c8eb..146ae6613537 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -113,8 +113,9 @@ static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 333 666"); .modified = 1, \ .channel2 = IIO_MOD_ ## _axis, \ .address = AXIS_ ## _axis, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE), \ } static const struct iio_chan_spec mmc35240_channels[] = { @@ -241,9 +242,19 @@ static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3]) 3 * sizeof(__le16)); } -static int mmc35240_raw_to_gauss(struct mmc35240_data *data, int index, - __le16 buf[], - int *val, int *val2) +/** + * mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply + compensation for output value. + * + * @data: device private data + * @index: axis index for which we want the conversion + * @buf: raw data to be converted, 2 bytes in little endian format + * @val: compensated output reading (unit is milli gauss) + * + * Returns: 0 in case of success, -EINVAL when @index is not valid + */ +static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index, + __le16 buf[], int *val) { int raw_x, raw_y, raw_z; int sens_x, sens_y, sens_z; @@ -261,18 +272,15 @@ static int mmc35240_raw_to_gauss(struct mmc35240_data *data, int index, switch (index) { case AXIS_X: - *val = (raw_x - nfo) / sens_x; - *val2 = ((raw_x - nfo) % sens_x) * 1000000; + *val = (raw_x - nfo) * 1000 / sens_x; break; case AXIS_Y: - *val = (raw_y - nfo) / sens_y - (raw_z - nfo) / sens_z; - *val2 = (((raw_y - nfo) % sens_y - (raw_z - nfo) % sens_z)) - * 1000000; + *val = (raw_y - nfo) * 1000 / sens_y - + (raw_z - nfo) * 1000 / sens_z; break; case AXIS_Z: - *val = (raw_y - nfo) / sens_y + (raw_z - nfo) / sens_z; - *val2 = (((raw_y - nfo) % sens_y + (raw_z - nfo) % sens_z)) - * 1000000; + *val = (raw_y - nfo) * 1000 / sens_y + + (raw_z - nfo) * 1000 / sens_z; break; default: return -EINVAL; @@ -290,16 +298,19 @@ static int mmc35240_read_raw(struct iio_dev *indio_dev, __le16 buf[3]; switch (mask) { - case IIO_CHAN_INFO_PROCESSED: + case IIO_CHAN_INFO_RAW: mutex_lock(&data->mutex); ret = mmc35240_read_measurement(data, buf); mutex_unlock(&data->mutex); if (ret < 0) return ret; - ret = mmc35240_raw_to_gauss(data, chan->address, - buf, val, val2); + ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val); if (ret < 0) return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = 1000; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: mutex_lock(&data->mutex); -- cgit v1.2.3 From 787f55c4d1842e5fb037a81bca9bd9d9fdfd46fe Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:13 +0300 Subject: iio: magnetometer: mmc35240: Use a smaller sleep value According to datasheet, Page 8, minimum wait time to complete measurement is 10ms. Adjusting this value will increase the userspace polling rate. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index 146ae6613537..7fdf906b94c9 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -219,7 +219,8 @@ static int mmc35240_take_measurement(struct mmc35240_data *data) return ret; if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT) break; - msleep(20); + /* minimum wait time to complete measurement is 10 ms */ + usleep_range(10000, 11000); } if (tries < 0) { -- cgit v1.2.3 From 6b90da4b58b39a80f490479953aa2563c3c41a6d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:14 +0300 Subject: iio: magnetometer: mmc35240: Fix sensitivity on z-axis Datasheet says (Page 2) that typical value for sensitivity for 16 bits mode on Z-axis is 770. Anyhow, looking at the input driver provided by Memsic the value for MMC35240 is 1024. Also, testing shows that using 1024 for Z-axis senzitivity offers better results. Fixes: abeb6b1e7b ("iio: magnetometer: Add support for MEMSIC MMC35240") Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index 7fdf906b94c9..b2ab58ea15f0 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -77,7 +77,7 @@ static const struct { } mmc35240_props_table[] = { /* 16 bits, 100Hz ODR */ { - {1024, 1024, 770}, + {1024, 1024, 1024}, 32768, }, /* 16 bits, 200Hz ODR */ -- cgit v1.2.3 From 4892688d7004c004765a51976087a8f51ce3586d Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 5 Jun 2015 14:03:15 +0300 Subject: iio: magnetometer: mmc35240: Add compensation for raw values This patch adds compensation formula to raw readings, borrowed from Memsic's input driver. Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mmc35240.c | 62 ++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c index b2ab58ea15f0..7a2ea71c659a 100644 --- a/drivers/iio/magnetometer/mmc35240.c +++ b/drivers/iio/magnetometer/mmc35240.c @@ -58,6 +58,31 @@ #define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */ #define MMC53240_WAIT_SET_RESET 1000 /* us */ +/* + * Memsic OTP process code piece is put here for reference: + * + * #define OTP_CONVERT(REG) ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006 + * 1) For X axis, the COEFFICIENT is always 1. + * 2) For Y axis, the COEFFICIENT is as below: + * f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) | + * (reg_data[2] >> 4)) + 1.0; + * 3) For Z axis, the COEFFICIENT is as below: + * f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35; + * We implemented the OTP logic into driver. + */ + +/* scale = 1000 here for Y otp */ +#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6) + +/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */ +#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81) + +#define MMC35240_X_COEFF(x) (x) +#define MMC35240_Y_COEFF(y) (y + 1000) +#define MMC35240_Z_COEFF(z) (z + 13500) + +#define MMC35240_OTP_START_ADDR 0x1B + enum mmc35240_resolution { MMC35240_16_BITS_SLOW = 0, /* 100 Hz */ MMC35240_16_BITS_FAST, /* 200 Hz */ @@ -102,6 +127,10 @@ struct mmc35240_data { struct mutex mutex; struct regmap *regmap; enum mmc35240_resolution res; + + /* OTP compensation */ + int axis_coef[3]; + int axis_scale[3]; }; static const int mmc35240_samp_freq[] = {100, 200, 333, 666}; @@ -172,8 +201,9 @@ static int mmc35240_hw_set(struct mmc35240_data *data, bool set) static int mmc35240_init(struct mmc35240_data *data) { - int ret; + int ret, y_convert, z_convert; unsigned int reg_id; + u8 otp_data[6]; ret = regmap_read(data->regmap, MMC35240_REG_ID, ®_id); if (ret < 0) { @@ -197,9 +227,30 @@ static int mmc35240_init(struct mmc35240_data *data) return ret; /* set default sampling frequency */ - return regmap_update_bits(data->regmap, MMC35240_REG_CTRL1, - MMC35240_CTRL1_BW_MASK, - data->res << MMC35240_CTRL1_BW_SHIFT); + ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1, + MMC35240_CTRL1_BW_MASK, + data->res << MMC35240_CTRL1_BW_SHIFT); + if (ret < 0) + return ret; + + ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR, + (u8 *)otp_data, sizeof(otp_data)); + if (ret < 0) + return ret; + + y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) | + (otp_data[2] >> 4)); + z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f); + + data->axis_coef[0] = MMC35240_X_COEFF(1); + data->axis_coef[1] = MMC35240_Y_COEFF(y_convert); + data->axis_coef[2] = MMC35240_Z_COEFF(z_convert); + + data->axis_scale[0] = 1; + data->axis_scale[1] = 1000; + data->axis_scale[2] = 10000; + + return 0; } static int mmc35240_take_measurement(struct mmc35240_data *data) @@ -286,6 +337,9 @@ static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index, default: return -EINVAL; } + /* apply OTP compensation */ + *val = (*val) * data->axis_coef[index] / data->axis_scale[index]; + return 0; } -- cgit v1.2.3 From 28e3427824ccc864b2866905eb10278c584b461d Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:52 +0200 Subject: iio: mma8452: Basic support for transient events. The event is triggered when the highpass filtered absolute acceleration exceeds the threshold. Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 212 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 877ce2954196..f30eb9b7a99c 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -9,7 +9,7 @@ * * 7-bit I2C slave address 0x1c/0x1d (pin selectable) * - * TODO: interrupt, thresholding, orientation / freefall events, autosleep + * TODO: orientation / freefall events, autosleep */ #include @@ -19,20 +19,33 @@ #include #include #include +#include #include #define MMA8452_STATUS 0x00 #define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */ #define MMA8452_OUT_Y 0x03 #define MMA8452_OUT_Z 0x05 +#define MMA8452_INT_SRC 0x0c #define MMA8452_WHO_AM_I 0x0d #define MMA8452_DATA_CFG 0x0e +#define MMA8452_TRANSIENT_CFG 0x1d +#define MMA8452_TRANSIENT_CFG_ELE BIT(4) +#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) +#define MMA8452_TRANSIENT_SRC 0x1e +#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1) +#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3) +#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5) +#define MMA8452_TRANSIENT_THS 0x1f +#define MMA8452_TRANSIENT_THS_MASK 0x7f #define MMA8452_OFF_X 0x2f #define MMA8452_OFF_Y 0x30 #define MMA8452_OFF_Z 0x31 #define MMA8452_CTRL_REG1 0x2a #define MMA8452_CTRL_REG2 0x2b #define MMA8452_CTRL_REG2_RST BIT(6) +#define MMA8452_CTRL_REG4 0x2d +#define MMA8452_CTRL_REG5 0x2e #define MMA8452_MAX_REG 0x31 @@ -48,6 +61,8 @@ #define MMA8452_DATA_CFG_FS_4G 1 #define MMA8452_DATA_CFG_FS_8G 2 +#define MMA8452_INT_TRANS BIT(5) + #define MMA8452_DEVICE_ID 0x2a struct mma8452_data { @@ -274,6 +289,126 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, } } +static int mma8452_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_THS); + if (ret < 0) + return ret; + + *val = ret & MMA8452_TRANSIENT_THS_MASK; + + return IIO_VAL_INT; +} + +static int mma8452_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct mma8452_data *data = iio_priv(indio_dev); + + return mma8452_change_config(data, MMA8452_TRANSIENT_THS, + val & MMA8452_TRANSIENT_THS_MASK); +} + +static int mma8452_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG); + if (ret < 0) + return ret; + + return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0; +} + +static int mma8452_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int val; + + val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG); + if (val < 0) + return val; + + if (state) + val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index); + else + val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index); + + val |= MMA8452_TRANSIENT_CFG_ELE; + + return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val); +} + +static void mma8452_transient_interrupt(struct iio_dev *indio_dev) +{ + struct mma8452_data *data = iio_priv(indio_dev); + s64 ts = iio_get_time_ns(); + int src; + + src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC); + if (src < 0) + return; + + if (src & MMA8452_TRANSIENT_SRC_XTRANSE) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + ts); + + if (src & MMA8452_TRANSIENT_SRC_YTRANSE) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + ts); + + if (src & MMA8452_TRANSIENT_SRC_ZTRANSE) + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + ts); +} + +static irqreturn_t mma8452_interrupt(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct mma8452_data *data = iio_priv(indio_dev); + int src; + + src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC); + if (src < 0) + return IRQ_NONE; + + if (src & MMA8452_INT_TRANS) { + mma8452_transient_interrupt(indio_dev); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + static irqreturn_t mma8452_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -316,6 +451,31 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev, return 0; } +static const struct iio_event_spec mma8452_transient_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) + }, +}; + +/* + * Threshold is configured in fixed 8G/127 steps regardless of + * currently selected scale for measurement. + */ +static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742"); + +static struct attribute *mma8452_event_attributes[] = { + &iio_const_attr_accel_transient_scale.dev_attr.attr, + NULL, +}; + +static struct attribute_group mma8452_event_attribute_group = { + .attrs = mma8452_event_attributes, + .name = "events", +}; + #define MMA8452_CHANNEL(axis, idx) { \ .type = IIO_ACCEL, \ .modified = 1, \ @@ -332,6 +492,8 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev, .shift = 4, \ .endianness = IIO_BE, \ }, \ + .event_spec = mma8452_transient_event, \ + .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \ } static const struct iio_chan_spec mma8452_channels[] = { @@ -355,6 +517,11 @@ static const struct iio_info mma8452_info = { .attrs = &mma8452_group, .read_raw = &mma8452_read_raw, .write_raw = &mma8452_write_raw, + .event_attrs = &mma8452_event_attribute_group, + .read_event_value = &mma8452_read_thresh, + .write_event_value = &mma8452_write_thresh, + .read_event_config = &mma8452_read_event_config, + .write_event_config = &mma8452_write_event_config, .debugfs_reg_access = &mma8452_reg_access_dbg, .driver_module = THIS_MODULE, }; @@ -425,6 +592,38 @@ static int mma8452_probe(struct i2c_client *client, if (ret < 0) return ret; + /* + * By default set transient threshold to max to avoid events if + * enabling without configuring threshold. + */ + ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS, + MMA8452_TRANSIENT_THS_MASK); + if (ret < 0) + return ret; + + if (client->irq) { + /* + * Although we enable the transient interrupt source once and + * for all here the transient event detection itself is not + * enabled until userspace asks for it by + * mma8452_write_event_config() + */ + int supported_interrupts = MMA8452_INT_TRANS; + + /* Assume wired to INT1 pin */ + ret = i2c_smbus_write_byte_data(client, + MMA8452_CTRL_REG5, + supported_interrupts); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, + MMA8452_CTRL_REG4, + supported_interrupts); + if (ret < 0) + return ret; + } + data->ctrl_reg1 = MMA8452_CTRL_ACTIVE | (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT); ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, @@ -437,9 +636,20 @@ static int mma8452_probe(struct i2c_client *client, if (ret < 0) return ret; + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, + client->irq, + NULL, mma8452_interrupt, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + client->name, indio_dev); + if (ret) + goto buffer_cleanup; + } + ret = iio_device_register(indio_dev); if (ret < 0) goto buffer_cleanup; + return 0; buffer_cleanup: -- cgit v1.2.3 From 5dbbd19f11846574e9143ae96da8603017cf823b Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:54 +0200 Subject: iio: mma8452: Add support for transient event debouncing Allow the debouce counter for transient events to be configured using the sysfs attribute events/in_accel_thresh_rising_period Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 76 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index f30eb9b7a99c..7429df3a191e 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -38,6 +38,7 @@ #define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5) #define MMA8452_TRANSIENT_THS 0x1f #define MMA8452_TRANSIENT_THS_MASK 0x7f +#define MMA8452_TRANSIENT_COUNT 0x20 #define MMA8452_OFF_X 0x2f #define MMA8452_OFF_Y 0x30 #define MMA8452_OFF_Z 0x31 @@ -124,6 +125,12 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n, return -EINVAL; } +static int mma8452_get_odr_index(struct mma8452_data *data) +{ + return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >> + MMA8452_CTRL_DR_SHIFT; +} + static const int mma8452_samp_freq[8][2] = { {800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000}, {6, 250000}, {1, 560000} @@ -139,6 +146,18 @@ static const int mma8452_scales[3][2] = { {0, 9577}, {0, 19154}, {0, 38307} }; +/* Datasheet table 35 (step time vs sample frequency) */ +static const int mma8452_transient_time_step_us[8] = { + 1250, + 2500, + 5000, + 10000, + 20000, + 20000, + 20000, + 20000 +}; + static ssize_t mma8452_show_samp_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) { @@ -198,8 +217,7 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, *val2 = mma8452_scales[i][1]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >> - MMA8452_CTRL_DR_SHIFT; + i = mma8452_get_odr_index(data); *val = mma8452_samp_freq[i][0]; *val2 = mma8452_samp_freq[i][1]; return IIO_VAL_INT_PLUS_MICRO; @@ -297,15 +315,33 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, int *val, int *val2) { struct mma8452_data *data = iio_priv(indio_dev); - int ret; + int ret, us; - ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_THS); - if (ret < 0) - return ret; + switch (info) { + case IIO_EV_INFO_VALUE: + ret = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_THS); + if (ret < 0) + return ret; + + *val = ret & MMA8452_TRANSIENT_THS_MASK; + return IIO_VAL_INT; - *val = ret & MMA8452_TRANSIENT_THS_MASK; + case IIO_EV_INFO_PERIOD: + ret = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_COUNT); + if (ret < 0) + return ret; + + us = ret * mma8452_transient_time_step_us[ + mma8452_get_odr_index(data)]; + *val = us / USEC_PER_SEC; + *val2 = us % USEC_PER_SEC; + return IIO_VAL_INT_PLUS_MICRO; - return IIO_VAL_INT; + default: + return -EINVAL; + } } static int mma8452_write_thresh(struct iio_dev *indio_dev, @@ -316,9 +352,26 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, int val, int val2) { struct mma8452_data *data = iio_priv(indio_dev); + int steps; - return mma8452_change_config(data, MMA8452_TRANSIENT_THS, - val & MMA8452_TRANSIENT_THS_MASK); + switch (info) { + case IIO_EV_INFO_VALUE: + return mma8452_change_config(data, MMA8452_TRANSIENT_THS, + val & MMA8452_TRANSIENT_THS_MASK); + + case IIO_EV_INFO_PERIOD: + steps = (val * USEC_PER_SEC + val2) / + mma8452_transient_time_step_us[ + mma8452_get_odr_index(data)]; + + if (steps > 0xff) + return -EINVAL; + + return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT, + steps); + default: + return -EINVAL; + } } static int mma8452_read_event_config(struct iio_dev *indio_dev, @@ -456,7 +509,8 @@ static const struct iio_event_spec mma8452_transient_event[] = { .type = IIO_EV_TYPE_THRESH, .dir = IIO_EV_DIR_RISING, .mask_separate = BIT(IIO_EV_INFO_ENABLE), - .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) + .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_PERIOD) }, }; -- cgit v1.2.3 From 1e79841a00e46506da887c718dd36a86b24b63e5 Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:56 +0200 Subject: iio: mma8452: Add highpass filter configuration. Allow the cutoff frequency of the high pass filter to be configured. Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 140 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 7429df3a191e..51ae751e64a7 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -29,9 +29,12 @@ #define MMA8452_INT_SRC 0x0c #define MMA8452_WHO_AM_I 0x0d #define MMA8452_DATA_CFG 0x0e +#define MMA8452_HP_FILTER_CUTOFF 0x0f +#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK (BIT(0) | BIT(1)) #define MMA8452_TRANSIENT_CFG 0x1d #define MMA8452_TRANSIENT_CFG_ELE BIT(4) #define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) +#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0) #define MMA8452_TRANSIENT_SRC 0x1e #define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1) #define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3) @@ -61,6 +64,7 @@ #define MMA8452_DATA_CFG_FS_2G 0 #define MMA8452_DATA_CFG_FS_4G 1 #define MMA8452_DATA_CFG_FS_8G 2 +#define MMA8452_DATA_CFG_HPF_MASK BIT(4) #define MMA8452_INT_TRANS BIT(5) @@ -158,6 +162,18 @@ static const int mma8452_transient_time_step_us[8] = { 20000 }; +/* Datasheet table 18 (normal mode) */ +static const int mma8452_hp_filter_cutoff[8][4][2] = { + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */ + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */ + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, /* 100 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 50 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */ + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */ +}; + static ssize_t mma8452_show_samp_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) { @@ -172,9 +188,23 @@ static ssize_t mma8452_show_scale_avail(struct device *dev, ARRAY_SIZE(mma8452_scales)); } +static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mma8452_data *data = iio_priv(indio_dev); + int i = mma8452_get_odr_index(data); + + return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0])); +} + static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, mma8452_show_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available, + S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0); static int mma8452_get_samp_freq_index(struct mma8452_data *data, int val, int val2) @@ -190,6 +220,31 @@ static int mma8452_get_scale_index(struct mma8452_data *data, ARRAY_SIZE(mma8452_scales), val, val2); } +static int mma8452_get_hp_filter_index(struct mma8452_data *data, + int val, int val2) +{ + int i = mma8452_get_odr_index(data); + + return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i], + ARRAY_SIZE(mma8452_scales[0]), val, val2); +} + +static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) +{ + int i, ret; + + ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF); + if (ret < 0) + return ret; + + i = mma8452_get_odr_index(data); + ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK; + *hz = mma8452_hp_filter_cutoff[i][ret][0]; + *uHz = mma8452_hp_filter_cutoff[i][ret][1]; + + return 0; +} + static int mma8452_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -228,6 +283,16 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, return ret; *val = sign_extend32(ret, 7); return IIO_VAL_INT; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) { + ret = mma8452_read_hp_filter(data, val, val2); + if (ret < 0) + return ret; + } else { + *val = 0; + *val2 = 0; + } + return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; } @@ -269,12 +334,31 @@ fail: return ret; } +static int mma8452_set_hp_filter_frequency(struct mma8452_data *data, + int val, int val2) +{ + int i, reg; + + i = mma8452_get_hp_filter_index(data, val, val2); + if (i < 0) + return -EINVAL; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_HP_FILTER_CUTOFF); + if (reg < 0) + return reg; + reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK; + reg |= i; + + return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg); +} + static int mma8452_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { struct mma8452_data *data = iio_priv(indio_dev); - int i; + int i, ret; if (iio_buffer_enabled(indio_dev)) return -EBUSY; @@ -302,6 +386,19 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, return -EINVAL; return mma8452_change_config(data, MMA8452_OFF_X + chan->scan_index, val); + + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + if (val == 0 && val2 == 0) { + data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK; + } else { + data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK; + ret = mma8452_set_hp_filter_frequency(data, val, val2); + if (ret < 0) + return ret; + } + return mma8452_change_config(data, MMA8452_DATA_CFG, + data->data_cfg); + default: return -EINVAL; } @@ -339,6 +436,22 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, *val2 = us % USEC_PER_SEC; return IIO_VAL_INT_PLUS_MICRO; + case IIO_EV_INFO_HIGH_PASS_FILTER_3DB: + ret = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_CFG); + if (ret < 0) + return ret; + + if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) { + *val = 0; + *val2 = 0; + } else { + ret = mma8452_read_hp_filter(data, val, val2); + if (ret < 0) + return ret; + } + return IIO_VAL_INT_PLUS_MICRO; + default: return -EINVAL; } @@ -352,7 +465,7 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, int val, int val2) { struct mma8452_data *data = iio_priv(indio_dev); - int steps; + int ret, reg, steps; switch (info) { case IIO_EV_INFO_VALUE: @@ -369,6 +482,22 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT, steps); + case IIO_EV_INFO_HIGH_PASS_FILTER_3DB: + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_TRANSIENT_CFG); + if (reg < 0) + return reg; + + if (val == 0 && val2 == 0) { + reg |= MMA8452_TRANSIENT_CFG_HPF_BYP; + } else { + reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP; + ret = mma8452_set_hp_filter_frequency(data, val, val2); + if (ret < 0) + return ret; + } + return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg); + default: return -EINVAL; } @@ -510,7 +639,8 @@ static const struct iio_event_spec mma8452_transient_event[] = { .dir = IIO_EV_DIR_RISING, .mask_separate = BIT(IIO_EV_INFO_ENABLE), .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | - BIT(IIO_EV_INFO_PERIOD) + BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB) }, }; @@ -537,7 +667,8 @@ static struct attribute_group mma8452_event_attribute_group = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -560,6 +691,7 @@ static const struct iio_chan_spec mma8452_channels[] = { static struct attribute *mma8452_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr, NULL }; -- cgit v1.2.3 From ae6d9ce05691bf79694074db7c7da980080548af Mon Sep 17 00:00:00 2001 From: Martin Fuzzey Date: Mon, 1 Jun 2015 15:39:58 +0200 Subject: iio: mma8452: Add support for interrupt driven triggers. Implement interrupt driven trigger for data ready. This allows more efficient access to the sample data. Signed-off-by: Martin Fuzzey Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 97 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 91 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 51ae751e64a7..e8e2077c7244 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -66,6 +68,7 @@ #define MMA8452_DATA_CFG_FS_8G 2 #define MMA8452_DATA_CFG_HPF_MASK BIT(4) +#define MMA8452_INT_DRDY BIT(0) #define MMA8452_INT_TRANS BIT(5) #define MMA8452_DEVICE_ID 0x2a @@ -577,18 +580,24 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) { struct iio_dev *indio_dev = p; struct mma8452_data *data = iio_priv(indio_dev); + int ret = IRQ_NONE; int src; src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC); if (src < 0) return IRQ_NONE; + if (src & MMA8452_INT_DRDY) { + iio_trigger_poll_chained(indio_dev->trig); + ret = IRQ_HANDLED; + } + if (src & MMA8452_INT_TRANS) { mma8452_transient_interrupt(indio_dev); - return IRQ_HANDLED; + ret = IRQ_HANDLED; } - return IRQ_NONE; + return ret; } static irqreturn_t mma8452_trigger_handler(int irq, void *p) @@ -714,6 +723,72 @@ static const struct iio_info mma8452_info = { static const unsigned long mma8452_scan_masks[] = {0x7, 0}; +static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct mma8452_data *data = iio_priv(indio_dev); + int reg; + + reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4); + if (reg < 0) + return reg; + + if (state) + reg |= MMA8452_INT_DRDY; + else + reg &= ~MMA8452_INT_DRDY; + + return mma8452_change_config(data, MMA8452_CTRL_REG4, reg); +} + +static int mma8452_validate_device(struct iio_trigger *trig, + struct iio_dev *indio_dev) +{ + struct iio_dev *indio = iio_trigger_get_drvdata(trig); + + if (indio != indio_dev) + return -EINVAL; + + return 0; +} + +static const struct iio_trigger_ops mma8452_trigger_ops = { + .set_trigger_state = mma8452_data_rdy_trigger_set_state, + .validate_device = mma8452_validate_device, + .owner = THIS_MODULE, +}; + +static int mma8452_trigger_setup(struct iio_dev *indio_dev) +{ + struct mma8452_data *data = iio_priv(indio_dev); + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d", + indio_dev->name, + indio_dev->id); + if (!trig) + return -ENOMEM; + + trig->dev.parent = &data->client->dev; + trig->ops = &mma8452_trigger_ops; + iio_trigger_set_drvdata(trig, indio_dev); + + ret = iio_trigger_register(trig); + if (ret) + return ret; + + indio_dev->trig = trig; + return 0; +} + +static void mma8452_trigger_cleanup(struct iio_dev *indio_dev) +{ + if (indio_dev->trig) + iio_trigger_unregister(indio_dev->trig); +} + static int mma8452_reset(struct i2c_client *client) { int i; @@ -794,7 +869,8 @@ static int mma8452_probe(struct i2c_client *client, * enabled until userspace asks for it by * mma8452_write_event_config() */ - int supported_interrupts = MMA8452_INT_TRANS; + int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS; + int enabled_interrupts = MMA8452_INT_TRANS; /* Assume wired to INT1 pin */ ret = i2c_smbus_write_byte_data(client, @@ -805,7 +881,11 @@ static int mma8452_probe(struct i2c_client *client, ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG4, - supported_interrupts); + enabled_interrupts); + if (ret < 0) + return ret; + + ret = mma8452_trigger_setup(indio_dev); if (ret < 0) return ret; } @@ -815,12 +895,12 @@ static int mma8452_probe(struct i2c_client *client, ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, data->ctrl_reg1); if (ret < 0) - return ret; + goto trigger_cleanup; ret = iio_triggered_buffer_setup(indio_dev, NULL, mma8452_trigger_handler, NULL); if (ret < 0) - return ret; + goto trigger_cleanup; if (client->irq) { ret = devm_request_threaded_irq(&client->dev, @@ -840,6 +920,10 @@ static int mma8452_probe(struct i2c_client *client, buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); + +trigger_cleanup: + mma8452_trigger_cleanup(indio_dev); + return ret; } @@ -849,6 +933,7 @@ static int mma8452_remove(struct i2c_client *client) iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); + mma8452_trigger_cleanup(indio_dev); mma8452_standby(iio_priv(indio_dev)); return 0; -- cgit v1.2.3 From 0cbb39f1432096cf00eca21f2d10f6d2057c26ba Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Thu, 28 May 2015 21:50:18 +0200 Subject: iio: adc: twl4030_madc: Fix calculation of the temperature sense current The bit mask to read the setting of the constant current source for measuring the NTC voltage was the wrong one. Since default value is initialized to the lowest level (000 = 10uA) the difference was probably never noticed in practice. Signed-off-by: H. Nikolaus Schaller Signed-off-by: Marek Belisko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl4030-madc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 94c5f05b4bc1..6d2d429a48e0 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -235,7 +235,7 @@ static int twl4030battery_temperature(int raw_volt) if (ret < 0) return ret; - curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; + curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10; /* Getting and calculating the thermistor resistance in ohms */ res = volt * 1000 / curr; /* calculating temperature */ -- cgit v1.2.3 From 994bda83dc6facc3fdd2130b0162b2abf09b2100 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Thu, 28 May 2015 21:50:19 +0200 Subject: iio: adc: twl4030_madc: Fix description of twl4030_madc_set_current_generator() The @chan parameter can be 0 or 1 and not a bit mask. Fix wrong description. Signed-off-by: H. Nikolaus Schaller Signed-off-by: Marek Belisko Signed-off-by: Jonathan Cameron --- drivers/iio/adc/twl4030-madc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 6d2d429a48e0..06f4792240f0 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -662,10 +662,8 @@ EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); * * @madc: pointer to twl4030_madc_data struct * @chan: can be one of the two values: - * TWL4030_BCI_ITHEN - * Enables bias current for main battery type reading - * TWL4030_BCI_TYPEN - * Enables bias current for main battery temperature sensing + * 0 - Enables bias current for main battery type reading + * 1 - Enables bias current for main battery temperature sensing * @on: enable or disable chan. * * Function to enable or disable bias current for -- cgit v1.2.3 From bf04c1a367e3d52b2e071cc7c7047c27dc1c3c5f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 27 May 2015 14:47:51 +0200 Subject: iio: adc: vf610: implement configurable conversion modes Support configurable conversion mode through sysfs. So far, the mode used was low-power, which is enabled by default now. Beside that, the modes normal and high-speed are selectable as well. Use the new device tree property which specifies the maximum ADC conversion clock frequencies. Depending on the mode used, the available resulting conversion frequency are calculated dynamically. Acked-by: Fugang Duan Signed-off-by: Stefan Agner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/vf610_adc.c | 146 +++++++++++++++++++++++++++++++------------- 1 file changed, 104 insertions(+), 42 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 56292ae4538d..480f335a0f9f 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -118,15 +118,21 @@ enum average_sel { VF610_ADC_SAMPLE_32, }; +enum conversion_mode_sel { + VF610_ADC_CONV_NORMAL, + VF610_ADC_CONV_HIGH_SPEED, + VF610_ADC_CONV_LOW_POWER, +}; + struct vf610_adc_feature { enum clk_sel clk_sel; enum vol_ref vol_ref; + enum conversion_mode_sel conv_mode; int clk_div; int sample_rate; int res_mode; - bool lpm; bool calibration; bool ovwren; }; @@ -139,6 +145,8 @@ struct vf610_adc { u32 vref_uv; u32 value; struct regulator *vref; + + u32 max_adck_rate[3]; struct vf610_adc_feature adc_feature; u32 sample_freq_avail[5]; @@ -148,46 +156,22 @@ struct vf610_adc { static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; -#define VF610_ADC_CHAN(_idx, _chan_type) { \ - .type = (_chan_type), \ - .indexed = 1, \ - .channel = (_idx), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ -} - -#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \ - .type = (_chan_type), \ - .channel = (_idx), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ -} - -static const struct iio_chan_spec vf610_adc_iio_channels[] = { - VF610_ADC_CHAN(0, IIO_VOLTAGE), - VF610_ADC_CHAN(1, IIO_VOLTAGE), - VF610_ADC_CHAN(2, IIO_VOLTAGE), - VF610_ADC_CHAN(3, IIO_VOLTAGE), - VF610_ADC_CHAN(4, IIO_VOLTAGE), - VF610_ADC_CHAN(5, IIO_VOLTAGE), - VF610_ADC_CHAN(6, IIO_VOLTAGE), - VF610_ADC_CHAN(7, IIO_VOLTAGE), - VF610_ADC_CHAN(8, IIO_VOLTAGE), - VF610_ADC_CHAN(9, IIO_VOLTAGE), - VF610_ADC_CHAN(10, IIO_VOLTAGE), - VF610_ADC_CHAN(11, IIO_VOLTAGE), - VF610_ADC_CHAN(12, IIO_VOLTAGE), - VF610_ADC_CHAN(13, IIO_VOLTAGE), - VF610_ADC_CHAN(14, IIO_VOLTAGE), - VF610_ADC_CHAN(15, IIO_VOLTAGE), - VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP), - /* sentinel */ -}; - static inline void vf610_adc_calculate_rates(struct vf610_adc *info) { + struct vf610_adc_feature *adc_feature = &info->adc_feature; unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk); - int i; + int divisor, i; + + adck_rate = info->max_adck_rate[adc_feature->conv_mode]; + + if (adck_rate) { + /* calculate clk divider which is within specification */ + divisor = ipg_rate / adck_rate; + adc_feature->clk_div = 1 << fls(divisor + 1); + } else { + /* fall-back value using a safe divisor */ + adc_feature->clk_div = 8; + } /* * Calculate ADC sample frequencies @@ -219,10 +203,8 @@ static inline void vf610_adc_cfg_init(struct vf610_adc *info) adc_feature->res_mode = 12; adc_feature->sample_rate = 1; - adc_feature->lpm = true; - /* Use a save ADCK which is below 20MHz on all devices */ - adc_feature->clk_div = 8; + adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER; vf610_adc_calculate_rates(info); } @@ -304,10 +286,12 @@ static void vf610_adc_cfg_set(struct vf610_adc *info) cfg_data = readl(info->regs + VF610_REG_ADC_CFG); cfg_data &= ~VF610_ADC_ADLPC_EN; - if (adc_feature->lpm) + if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER) cfg_data |= VF610_ADC_ADLPC_EN; cfg_data &= ~VF610_ADC_ADHSC_EN; + if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED) + cfg_data |= VF610_ADC_ADHSC_EN; writel(cfg_data, info->regs + VF610_REG_ADC_CFG); } @@ -409,6 +393,81 @@ static void vf610_adc_hw_init(struct vf610_adc *info) vf610_adc_cfg_set(info); } +static int vf610_set_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + info->adc_feature.conv_mode = mode; + vf610_adc_calculate_rates(info); + vf610_adc_hw_init(info); + mutex_unlock(&indio_dev->mlock); + + return 0; +} + +static int vf610_get_conversion_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + return info->adc_feature.conv_mode; +} + +static const char * const vf610_conv_modes[] = { "normal", "high-speed", + "low-power" }; + +static const struct iio_enum vf610_conversion_mode = { + .items = vf610_conv_modes, + .num_items = ARRAY_SIZE(vf610_conv_modes), + .get = vf610_get_conversion_mode, + .set = vf610_set_conversion_mode, +}; + +static const struct iio_chan_spec_ext_info vf610_ext_info[] = { + IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode), + {}, +}; + +#define VF610_ADC_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .ext_info = vf610_ext_info, \ +} + +#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ +} + +static const struct iio_chan_spec vf610_adc_iio_channels[] = { + VF610_ADC_CHAN(0, IIO_VOLTAGE), + VF610_ADC_CHAN(1, IIO_VOLTAGE), + VF610_ADC_CHAN(2, IIO_VOLTAGE), + VF610_ADC_CHAN(3, IIO_VOLTAGE), + VF610_ADC_CHAN(4, IIO_VOLTAGE), + VF610_ADC_CHAN(5, IIO_VOLTAGE), + VF610_ADC_CHAN(6, IIO_VOLTAGE), + VF610_ADC_CHAN(7, IIO_VOLTAGE), + VF610_ADC_CHAN(8, IIO_VOLTAGE), + VF610_ADC_CHAN(9, IIO_VOLTAGE), + VF610_ADC_CHAN(10, IIO_VOLTAGE), + VF610_ADC_CHAN(11, IIO_VOLTAGE), + VF610_ADC_CHAN(12, IIO_VOLTAGE), + VF610_ADC_CHAN(13, IIO_VOLTAGE), + VF610_ADC_CHAN(14, IIO_VOLTAGE), + VF610_ADC_CHAN(15, IIO_VOLTAGE), + VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP), + /* sentinel */ +}; + static int vf610_adc_read_data(struct vf610_adc *info) { int result; @@ -651,6 +710,9 @@ static int vf610_adc_probe(struct platform_device *pdev) info->vref_uv = regulator_get_voltage(info->vref); + of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency", + info->max_adck_rate, 3); + platform_set_drvdata(pdev, indio_dev); init_completion(&info->completion); -- cgit v1.2.3 From 4861a007bfd71a9fc0a83cc7fad41dda9bf8b5b7 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 May 2015 15:37:02 +0200 Subject: iio: st_accel: support 8bit channel data Some sensors like the LIS331DL only support 8bit data by a single register per axis. These utilize the MSB byte. Make it possible to register these apropriately. A oneliner change is needed in the ST sensors core to handle 8bit reads as this is the first supported 8bit sensor. Signed-off-by: Linus Walleij Acked-by: Denis Ciocca Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 16 ++++++++++++++++ drivers/iio/common/st_sensors/st_sensors_core.c | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 58d1d13d552a..ad19fb4304df 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -153,6 +153,22 @@ #define ST_ACCEL_4_IG1_EN_MASK 0x08 #define ST_ACCEL_4_MULTIREAD_BIT true +static const struct iio_chan_spec st_accel_8bit_channels[] = { + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1), + ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8, + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + 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), diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 1255b157c71c..bf4b13f9defc 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -433,7 +433,9 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev, if (err < 0) goto st_sensors_free_memory; - if (byte_for_channel == 2) + if (byte_for_channel == 1) + *data = (s8)*outdata; + else if (byte_for_channel == 2) *data = (s16)get_unaligned_le16(outdata); else if (byte_for_channel == 3) *data = (s32)st_sensors_get_unaligned_le24(outdata); -- cgit v1.2.3 From bbf5f037fad47e4affef6696aaf88a40b261e639 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 May 2015 15:37:18 +0200 Subject: iio: st_accel: support the LIS331DL sensor This adds support for the LIS331DL sensor version. This is a simple 8bit-only accelerometer. Signed-off-by: Linus Walleij Acked-by: Denis Ciocca Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel.h | 1 + drivers/iio/accel/st_accel_core.c | 70 +++++++++++++++++++++++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 4 +++ 3 files changed, 75 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 7ee9724b1428..aa1001931d0c 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -20,6 +20,7 @@ #define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" #define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel" #define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel" +#define LIS331DL_ACCEL_DEV_NAME "lis331dl_accel" #define LIS331DLH_ACCEL_DEV_NAME "lis331dlh" #define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel" #define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel" diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index ad19fb4304df..970e9f904dde 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -153,6 +153,28 @@ #define ST_ACCEL_4_IG1_EN_MASK 0x08 #define ST_ACCEL_4_MULTIREAD_BIT true +/* CUSTOM VALUES FOR SENSOR 5 */ +#define ST_ACCEL_5_WAI_EXP 0x3b +#define ST_ACCEL_5_ODR_ADDR 0x20 +#define ST_ACCEL_5_ODR_MASK 0x80 +#define ST_ACCEL_5_ODR_AVL_100HZ_VAL 0x00 +#define ST_ACCEL_5_ODR_AVL_400HZ_VAL 0x01 +#define ST_ACCEL_5_PW_ADDR 0x20 +#define ST_ACCEL_5_PW_MASK 0x40 +#define ST_ACCEL_5_FS_ADDR 0x20 +#define ST_ACCEL_5_FS_MASK 0x20 +#define ST_ACCEL_5_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_5_FS_AVL_8_VAL 0X01 +/* TODO: check these resulting gain settings, these are not in the datsheet */ +#define ST_ACCEL_5_FS_AVL_2_GAIN IIO_G_TO_M_S_2(18000) +#define ST_ACCEL_5_FS_AVL_8_GAIN IIO_G_TO_M_S_2(72000) +#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22 +#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04 +#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20 +#define ST_ACCEL_5_IG1_EN_ADDR 0x21 +#define ST_ACCEL_5_IG1_EN_MASK 0x08 +#define ST_ACCEL_5_MULTIREAD_BIT false + static const struct iio_chan_spec st_accel_8bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -470,6 +492,54 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, .bootime = 2, /* guess */ }, + { + .wai = ST_ACCEL_5_WAI_EXP, + .sensors_supported = { + [0] = LIS331DL_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_8bit_channels, + .odr = { + .addr = ST_ACCEL_5_ODR_ADDR, + .mask = ST_ACCEL_5_ODR_MASK, + .odr_avl = { + { 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL }, + { 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_5_PW_ADDR, + .mask = ST_ACCEL_5_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_5_FS_ADDR, + .mask = ST_ACCEL_5_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_5_FS_AVL_2_VAL, + .gain = ST_ACCEL_5_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = ST_ACCEL_5_FS_AVL_8_VAL, + .gain = ST_ACCEL_5_FS_AVL_8_GAIN, + }, + }, + }, + .drdy_irq = { + .addr = ST_ACCEL_5_DRDY_IRQ_ADDR, + .mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK, + }, + .multi_read_bit = ST_ACCEL_5_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 6b720c190b2d..d4ad72ca4a3d 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -48,6 +48,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lsm330dlc-accel", .data = LSM330DLC_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis331dl-accel", + .data = LIS331DL_ACCEL_DEV_NAME, + }, { .compatible = "st,lis331dlh-accel", .data = LIS331DLH_ACCEL_DEV_NAME, -- cgit v1.2.3