Re: [PATCH v4 2/3] iio: temperature: Adding support for MLX90632

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 12 December 2017 at 21:50, Jonathan Cameron <jic23@xxxxxxxxxx> wrote:
> On Mon, 11 Dec 2017 10:19:31 +0100
> Crt Mori <cmo@xxxxxxxxxxx> wrote:
>
>> Melexis has just released Infra Red temperature sensor MLX90632 used
>> for contact-less temperature measurement. Driver provides basic
>> functionality for reporting object (and ambient) temperature with
>> support for object emissivity.
>>
>> Signed-off-by: Crt Mori <cmo@xxxxxxxxxxx>
>
> Out of curiosity, how are you testing runtime pm?  Normally I'd expect
> this driver to be deliberately putting the hardware to sleep but it
> isn't doing so that I can see.
>
> A few minor comments inline.
>
> Jonathan
>

I was testing it with Android (AOSP) and I think that it regularly
puts devices into the runtime_pm mode by itself when you turn your
screen off. It was also strange to me that register values were set to
0 after phone screen turned off, but with marking them dirty I managed
to get them re-read in resume. It is an older kernel version in AOSP
(I ported some of the regmap to it, to get this working). After more
detailed reading of PM stuff in last week, along with your comments,
makes me think I need a bit more knowledge on PM before I can actually
defend the statements there. I will follow your guidance to get this
in decent shape (I hope to say we are almost there), but I do not have
any possibilities to check this with Android at this time. Is this OK
for you?

Thanks for your comments.

Crt

>> ---
>>  MAINTAINERS                        |   7 +
>>  drivers/iio/temperature/Kconfig    |  12 +
>>  drivers/iio/temperature/Makefile   |   1 +
>>  drivers/iio/temperature/mlx90632.c | 774 +++++++++++++++++++++++++++++++++++++
>>  4 files changed, 794 insertions(+)
>>  create mode 100644 drivers/iio/temperature/mlx90632.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 2d3d750b19c0..81aec02b08b8 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -8690,6 +8690,13 @@ W:     http://www.melexis.com
>>  S:   Supported
>>  F:   drivers/iio/temperature/mlx90614.c
>>
>> +MELEXIS MLX90632 DRIVER
>> +M:   Crt Mori <cmo@xxxxxxxxxxx>
>> +L:   linux-iio@xxxxxxxxxxxxxxx
>> +W:   http://www.melexis.com
>> +S:   Supported
>> +F:   drivers/iio/temperature/mlx90632.c
>> +
>>  MELFAS MIP4 TOUCHSCREEN DRIVER
>>  M:   Sangwon Jee <jeesw@xxxxxxxxxx>
>>  W:   http://www.melfas.com
>> diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig
>> index 5378976d6d27..82e4a62745e2 100644
>> --- a/drivers/iio/temperature/Kconfig
>> +++ b/drivers/iio/temperature/Kconfig
>> @@ -43,6 +43,18 @@ config MLX90614
>>         This driver can also be built as a module. If so, the module will
>>         be called mlx90614.
>>
>> +config MLX90632
>> +     tristate "MLX90632 contact-less infrared sensor with medical accuracy"
>> +     depends on I2C
>> +     select REGMAP_I2C
>> +     help
>> +       If you say yes here you get support for the Melexis
>> +       MLX90632 contact-less infrared sensor with medical accuracy
>> +       connected with I2C.
>> +
>> +       This driver can also be built as a module. If so, the module will
>> +       be called mlx90632.
>> +
> <snip>
>
>> +static int mlx90632_probe(struct i2c_client *client,
>> +                       const struct i2c_device_id *id)
>> +{
>> +     struct iio_dev *indio_dev;
>> +     struct mlx90632_data *mlx90632;
>> +     struct regmap *regmap;
>> +     int ret;
>> +     unsigned int read;
>> +
>> +     indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mlx90632));
>> +     if (!indio_dev) {
>> +             dev_err(&client->dev, "Failed to allocate device");
>> +             return -ENOMEM;
>> +     }
>> +
>> +     regmap = devm_regmap_init_i2c(client, &mlx90632_regmap);
>> +     if (IS_ERR(regmap)) {
>> +             ret = PTR_ERR(regmap);
>> +             dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
>> +             return ret;
>> +     }
>> +
>> +     mlx90632 = iio_priv(indio_dev);
>> +     i2c_set_clientdata(client, indio_dev);
>> +     mlx90632->client = client;
>> +     mlx90632->regmap = regmap;
>> +
>> +     mutex_init(&mlx90632->lock);
>> +     mlx90632_wakeup(mlx90632);
>> +
>> +     indio_dev->dev.parent = &client->dev;
>> +     indio_dev->name = id->name;
>> +     indio_dev->modes = INDIO_DIRECT_MODE;
>> +     indio_dev->info = &mlx90632_info;
>> +     indio_dev->channels = mlx90632_channels;
>> +     indio_dev->num_channels = ARRAY_SIZE(mlx90632_channels);
>> +
>> +     ret = regmap_read(mlx90632->regmap, MLX90632_EE_VERSION, &read);
>> +     if (ret < 0) {
>> +             dev_err(&client->dev, "read of version failed: %d\n", ret);
>> +             return ret;
>> +     }
>> +     if (read == MLX90632_ID_MEDICAL) {
>> +             dev_dbg(&client->dev,
>> +                     "Detected Medical EEPROM calibration %x", read);
>> +     } else if (read == MLX90632_ID_CONSUMER) {
>> +             dev_dbg(&client->dev,
>> +                     "Detected Consumer EEPROM calibration %x", read);
>> +     } else {
>> +             dev_err(&client->dev,
>> +                     "Chip EEPROM version mismatch %x (expected %x or %x)",
>> +                     read, MLX90632_ID_CONSUMER, MLX90632_ID_MEDICAL);
>> +             return -EPROTONOSUPPORT;
>> +     }
>> +
>> +     mlx90632->emissivity = 1000;
>> +
>> +     return iio_device_register(indio_dev);
>> +}
>> +
>> +static int mlx90632_remove(struct i2c_client *client)
>> +{
>> +     struct iio_dev *indio_dev = i2c_get_clientdata(client);
>> +     struct mlx90632_data *data = iio_priv(indio_dev);
>> +
>> +     iio_device_unregister(indio_dev);
>> +
>> +     pm_runtime_disable(&client->dev);
>> +     if (!pm_runtime_status_suspended(&client->dev))
>> +             mlx90632_sleep(data);
>> +     pm_runtime_set_suspended(&client->dev);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct i2c_device_id mlx90632_id[] = {
>> +     { "mlx90632", 0 },
>> +     { }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, mlx90632_id);
>> +
>> +static const struct of_device_id mlx90632_of_match[] = {
>> +     { .compatible = "melexis,mlx90632" },
>> +     { }
>> +};
>> +MODULE_DEVICE_TABLE(of, mlx90632_of_match);
>> +
>> +static int __maybe_unused mlx90632_pm_suspend(struct device *dev)
>> +{
>> +     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>> +     struct mlx90632_data *data = iio_priv(indio_dev);
>> +
>> +     regcache_sync(data->regmap);
>
> This could be syncing to a runtime sleeping device.  Also not
> needed as the cache should be right anyway I think.
>

This is clear.

>> +
>> +     if (pm_runtime_active(dev))
>> +             return mlx90632_sleep(data);
>
> This prevents the device from sleeping twice, but nothing
> stops it from coming up in resume if it was already in runtime pm.
>

Sleeping twice was main protection. Will add additional check for runtime_pm.

>> +
>> +     return 0;
>> +}
>> +
>> +static int __maybe_unused mlx90632_pm_resume(struct device *dev)
>> +{
>> +     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>> +     struct mlx90632_data *data = iio_priv(indio_dev);
>> +     int err;
>> +
>> +     regcache_mark_dirty(data->regmap);
>> +     regcache_cache_only(data->regmap, false);
>> +     err = regcache_sync(data->regmap);
>> +     if (err < 0) {
>> +             dev_err(dev, "Failed to sync regmap registers: %d\n", err);
>> +             return err;
>> +     }
>> +
>> +     err = mlx90632_wakeup(data);
>> +     if (err < 0)
>> +             return err;
>> +
>> +     pm_runtime_set_active(dev);
>> +     pm_runtime_enable(dev);
>> +
>> +     return 0;
>> +}
>> +
>> +static int __maybe_unused mlx90632_pm_runtime_suspend(struct device *dev)
>> +{
>> +     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>> +     struct mlx90632_data *mlx90632 = iio_priv(indio_dev);
>> +
>> +     regcache_sync(mlx90632->regmap);
>
> Why sync here?  The cache should be in sync already.
>

I agree this is not needed - even if we sync it here we go to sleep.

>> +
>> +     return mlx90632_sleep(mlx90632);
>> +}
>> +
>> +static int __maybe_unused mlx90632_pm_runtime_resume(struct device *dev)
>> +{
>> +     s32 ret;
>> +     struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
>> +     struct mlx90632_data *mlx90632 = iio_priv(indio_dev);
>> +
>> +     regcache_mark_dirty(mlx90632->regmap);
>
> Technically it doesn't matter much, but I think the convention is to
> normally mark the cache dirty when it becomes possible for it to be dirty
> - that is during suspend.
>

This is probably (along with below two statements), because I was
trying to solve a problem when regmap registers were 0 after suspend,
so I thought I am doing something wrong when waking up. I will shift
dirty to suspend and leave sync here only.

>> +     regcache_cache_only(mlx90632->regmap, false);
>> +     ret = regcache_sync(mlx90632->regmap);
>> +     if (ret < 0) {
>> +             dev_err(dev, "Failed to sync regmap registers: %d\n", ret);
>> +             return ret;
>> +     }
>> +
>> +     return mlx90632_wakeup(mlx90632);
>
> This doesn't look quite right.  What if the device was in runtime suspend
> before full suspend?  Would expect it to return to that state on resume
> but not runtime_resume.
>

Will add additional checks.

>> +}
>> +
>> +static const struct dev_pm_ops mlx90632_pm_ops = {
>> +     SET_SYSTEM_SLEEP_PM_OPS(mlx90632_pm_suspend, mlx90632_pm_resume)
>> +     SET_RUNTIME_PM_OPS(mlx90632_pm_runtime_suspend,
>> +                        mlx90632_pm_runtime_resume, NULL)
>> +};
>> +
>> +static struct i2c_driver mlx90632_driver = {
>> +     .driver = {
>> +             .name   = "mlx90632",
>> +             .of_match_table = mlx90632_of_match,
>> +             .pm     = &mlx90632_pm_ops,
>> +     },
>> +     .probe = mlx90632_probe,
>> +     .remove = mlx90632_remove,
>> +     .id_table = mlx90632_id,
>> +};
>> +module_i2c_driver(mlx90632_driver);
>> +
>> +MODULE_AUTHOR("Crt Mori <cmo@xxxxxxxxxxx>");
>> +MODULE_DESCRIPTION("Melexis MLX90632 contactless Infra Red temperature sensor driver");
>> +MODULE_LICENSE("GPL v2");
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux