From: Jonathan Cameron <jic23@kernel.org>
To: "Vianney le Clément de Saint-Marcq"
<vianney.leclement@essensium.com>,
linux-iio@vger.kernel.org, "Peter Meerwald" <pmeerw@pmeerw.net>
Cc: "Arnout Vandecappelle (Essensium/Mind)" <arnout@mind.be>,
Wolfram Sang <w.sang@pengutronix.de>
Subject: Re: [PATCH 5/7] iio: mlx90614: Allow tuning EEPROM configuration
Date: Mon, 09 Mar 2015 15:35:40 +0000 [thread overview]
Message-ID: <54FDBDCC.50204@kernel.org> (raw)
In-Reply-To: <1424879712-28304-6-git-send-email-vianney.leclement@essensium.com>
On 25/02/15 15:55, Vianney le Clément de Saint-Marcq wrote:
> Add device attributes for getting/setting emissivity, IIR, and FIR
> coefficients, and getting the gain (which should not be modified in
> order to keep factory calibration).
>
> The attributes provide raw values whose meaning is described in the
> datasheet [1].
>
> Writing to EEPROM requires an explicit erase by writing zero. In
> addition, it takes 20ms for the erase/write to complete. During this
> time no EEPROM register should be accessed. Therefore, two msleep()s
> are added to the write function and a mutex protects against concurrent
> access.
>
> Since it is not expected to be updated frequently, the configuration
> register is read before modifying it rather than caching it.
>
> [1] http://melexis.com/Assets/IR-sensor-thermometer-MLX90614-Datasheet-5152.aspx
>
> Signed-off-by: Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>
> Cc: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Wolfram, bit of odd i2c usage inline I'd like you to take quick look at.
> ---
> drivers/iio/temperature/mlx90614.c | 164 ++++++++++++++++++++++++++++++++++++-
> 1 file changed, 163 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
> index 0b36746..ab98fb6 100644
> --- a/drivers/iio/temperature/mlx90614.c
> +++ b/drivers/iio/temperature/mlx90614.c
> @@ -12,14 +12,16 @@
> *
> * (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
> *
> - * TODO: sleep mode, configuration EEPROM
> + * TODO: sleep mode
> */
>
> #include <linux/err.h>
> #include <linux/i2c.h>
> #include <linux/module.h>
> +#include <linux/delay.h>
>
> #include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
>
> #define MLX90614_OP_RAM 0x00
> #define MLX90614_OP_EEPROM 0x20
> @@ -53,8 +55,47 @@
>
> struct mlx90614_data {
> struct i2c_client *client;
> + struct mutex lock; /* for EEPROM access only */
> };
>
> +/*
> + * Erase an address and write word.
> + * The mutex must be locked before calling.
> + */
> +static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
> + u16 value)
> +{
> + /*
> + * Note: The mlx90614 requires a PEC on writing but does not send us a
> + * valid PEC on reading. Hence, we cannot set I2C_CLIENT_PEC in
> + * i2c_client.flags. As a workaround, we use i2c_smbus_xfer here.
> + */
That's particularly hideous and someone should shout at whoever implemented that!
Ah well, such is life. I want a go ahead on doing this from Wolfram however.
It's the sort of thing that might well get randomly broken in the future.
Alternative is to add another flag that indicates to the i2c core that the PEC
that comes back will be wrong.
> + union i2c_smbus_data data;
> + s32 ret;
> +
> + dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command);
> +
> + data.word = 0x0000; /* erase command */
> + ret = i2c_smbus_xfer(client->adapter, client->addr,
> + client->flags | I2C_CLIENT_PEC,
> + I2C_SMBUS_WRITE, command,
> + I2C_SMBUS_WORD_DATA, &data);
> + if (ret < 0)
> + return ret;
> +
> + msleep(MLX90614_TIMING_EEPROM);
> +
> + data.word = value; /* actual write */
> + ret = i2c_smbus_xfer(client->adapter, client->addr,
> + client->flags | I2C_CLIENT_PEC,
> + I2C_SMBUS_WRITE, command,
> + I2C_SMBUS_WORD_DATA, &data);
> +
> + msleep(MLX90614_TIMING_EEPROM);
> +
> + return ret;
> +}
> +
> static int mlx90614_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *channel, int *val,
> int *val2, long mask)
> @@ -111,6 +152,102 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
> }
> }
>
> +static ssize_t mlx90614_show_emissivity(struct device *dev,
> + struct device_attribute *devattr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> + s32 ret;
> +
> + mutex_lock(&data->lock);
> + ret = i2c_smbus_read_word_data(data->client, MLX90614_EMISSIVITY);
> + mutex_unlock(&data->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
> +}
> +
> +static ssize_t mlx90614_store_emissivity(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> + u16 val;
> + s32 ret;
> +
> + if (kstrtou16(buf, 0, &val))
> + return -EINVAL;
> +
> + mutex_lock(&data->lock);
> + ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY, val);
> + mutex_unlock(&data->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + return count;
> +}
Emissivity is well defined. I'd like this to be a standard attribute with
full documentation please rather than a driver specific one. Happy
to have this added to the infomask elements if it make sense.
> +
> +static ssize_t mlx90614_show_config(struct device *dev,
> + struct device_attribute *devattr,
> + char *buf)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> + struct iio_dev_attr *indio_attr = to_iio_dev_attr(devattr);
> + u16 mask = indio_attr->address & 0xffff;
> + u16 shift = indio_attr->address >> 16;
> + s32 ret;
> +
> + mutex_lock(&data->lock);
> + ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
> + mutex_unlock(&data->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + ret = (ret & mask) >> shift;
> +
> + return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
> +}
> +
> +static ssize_t mlx90614_store_config(struct device *dev,
> + struct device_attribute *devattr,
> + const char *buf, size_t count)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> + struct iio_dev_attr *indio_attr = to_iio_dev_attr(devattr);
> + u16 mask = indio_attr->address & 0xffff;
> + u16 shift = indio_attr->address >> 16;
> + u16 val;
> + s32 ret;
> +
> + if (kstrtou16(buf, 0, &val) || val != (val & (mask >> shift)))
> + return -EINVAL;
> +
> + mutex_lock(&data->lock);
> +
> + ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
> + if (ret < 0)
> + goto out;
> +
> + val = (val << shift) | ((u16)ret & ~mask);
> + ret = mlx90614_write_word(data->client, MLX90614_CONFIG, val);
> +
> +out:
> + mutex_unlock(&data->lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + return count;
> +}
> +
> static const struct iio_chan_spec mlx90614_channels[] = {
> {
> .type = IIO_TEMP,
> @@ -154,7 +291,31 @@ static const struct iio_chan_spec mlx90614_channels[] = {
> },
> };
>
> +static IIO_DEVICE_ATTR(emissivity, S_IRUGO | S_IWUSR, mlx90614_show_emissivity,
> + mlx90614_store_emissivity, -1);
> +static IIO_DEVICE_ATTR(iir, S_IRUGO | S_IWUSR,
> + mlx90614_show_config, mlx90614_store_config,
> + (MLX90614_CONFIG_IIR_SHIFT << 16) | MLX90614_CONFIG_IIR_MASK);
> +static IIO_DEVICE_ATTR(fir, S_IRUGO | S_IWUSR,
> + mlx90614_show_config, mlx90614_store_config,
> + (MLX90614_CONFIG_FIR_SHIFT << 16) | MLX90614_CONFIG_FIR_MASK);
> +static IIO_DEVICE_ATTR(gain, S_IRUGO, mlx90614_show_config, NULL,
> + (MLX90614_CONFIG_GAIN_SHIFT << 16) | MLX90614_CONFIG_GAIN_MASK);
> +
> +static struct attribute *mlx90614_attr[] = {
> + &iio_dev_attr_emissivity.dev_attr.attr,
> + &iio_dev_attr_iir.dev_attr.attr,
Hmm odd to have two different filter types with separate control.
However we have _filter_low_pass_3db_frequency and whilst it is a pain
obviously to map this to that form that's the way to go from a useful
userspace interface point of view. If we need to extend the filter
description interface (filter type has been suggested before) then please
propose that.
Here I see they are suggesting one filter is effectively for ignoreing
fast passing objects, and the other for noise prevention. They both
effect the settling time.
Anyhow it's not entirely obvious what the right answer is, but its
definitely not a driver specific interface such as we have.
Peter, any thoughts?
> + &iio_dev_attr_fir.dev_attr.attr,
> + &iio_dev_attr_gain.dev_attr.attr,
Please use the standard IIO infomask elements for scale (or perhaps
calibscale - I haven't checked which is appropriate).
> + NULL
> +};
> +
> +static struct attribute_group mlx90614_attr_group = {
> + .attrs = mlx90614_attr,
> +};
> +
> static const struct iio_info mlx90614_info = {
> + .attrs = &mlx90614_attr_group,
> .read_raw = mlx90614_read_raw,
> .driver_module = THIS_MODULE,
> };
> @@ -189,6 +350,7 @@ static int mlx90614_probe(struct i2c_client *client,
> data = iio_priv(indio_dev);
> i2c_set_clientdata(client, indio_dev);
> data->client = client;
> + mutex_init(&data->lock);
>
> indio_dev->dev.parent = &client->dev;
> indio_dev->name = id->name;
>
next prev parent reply other threads:[~2015-03-09 15:35 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-02-25 15:55 [PATCH 0/7] iio: mlx90614 enhancements Vianney le Clément de Saint-Marcq
2015-02-25 15:55 ` [PATCH 1/7] iio: mlx90614: Refactor register symbols Vianney le Clément de Saint-Marcq
2015-03-09 15:02 ` Jonathan Cameron
2015-02-25 15:55 ` [PATCH 2/7] iio: mlx90614: Add symbols for accessible registers Vianney le Clément de Saint-Marcq
2015-02-25 15:55 ` [PATCH 3/7] iio: mlx90614: Add processed temperature output Vianney le Clément de Saint-Marcq
2015-03-09 15:08 ` Jonathan Cameron
2015-03-09 19:27 ` Vianney le Clément
2015-02-25 15:55 ` [PATCH 4/7] iio: mlx90614: Support devices with dual IR sensor Vianney le Clément de Saint-Marcq
2015-03-09 15:18 ` Jonathan Cameron
2015-03-09 19:35 ` Vianney le Clément
2015-02-25 15:55 ` [PATCH 5/7] iio: mlx90614: Allow tuning EEPROM configuration Vianney le Clément de Saint-Marcq
2015-03-09 15:35 ` Jonathan Cameron [this message]
2015-03-09 15:41 ` Jonathan Cameron
2015-03-09 16:45 ` Wolfram Sang
2015-03-09 17:02 ` Jonathan Cameron
2015-03-09 17:15 ` Wolfram Sang
2015-03-09 19:52 ` Vianney le Clément
2015-02-25 15:55 ` [PATCH 6/7] iio: mlx90614: Add power management Vianney le Clément de Saint-Marcq
2015-03-09 15:39 ` Jonathan Cameron
2015-03-09 15:42 ` Jonathan Cameron
2015-03-09 16:43 ` Wolfram Sang
2015-03-12 10:30 ` Jonathan Cameron
2015-02-25 15:55 ` [PATCH 7/7] iio: mlx90614: Provide raw IR value for object channels Vianney le Clément de Saint-Marcq
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=54FDBDCC.50204@kernel.org \
--to=jic23@kernel.org \
--cc=arnout@mind.be \
--cc=linux-iio@vger.kernel.org \
--cc=pmeerw@pmeerw.net \
--cc=vianney.leclement@essensium.com \
--cc=w.sang@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).