* [PATCH v3 0/5] iio: mlx90614 enhancements
@ 2015-03-30 8:34 Vianney le Clément de Saint-Marcq
[not found] ` <1427704502-22347-1-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
0 siblings, 1 reply; 5+ messages in thread
From: Vianney le Clément de Saint-Marcq @ 2015-03-30 8:34 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Vianney le Clément de Saint-Marcq,
Arnout Vandecappelle (Essensium/Mind)
Many thanks Jonathan and Lars for taking time reviewing this series. Please
find next follow-up patches addressing your comments.
This set of patches adds the following additional features to the
mlx90614 temperature sensor driver.
* Emissivity setting (patch 3)
* Power management (patch 4)
* Handle errors in temperature reading (patch 5)
The changes have been split into 5 patches to ease code review.
Patch 1 introduces IIO_CHAN_INFO_CALIBEMISSIVITY in iio core and is a
prerequisite of patch 3.
Patch 2 introduces devicetree bindings documentation, which is complemented by
patch 4.
Patches 3-5 implement the additional features and are mostly independent
from each other.
Patches 2 and 4 introduce device tree bindings and are the only one sent to the
devicetree list.
Patch set history:
v3:
* Rebase on iio.git testing branch, drop 3 merged patches
* Rename IIO_CHAN_INFO_EMISSIVITY to IIO_CHAN_INFO_CALIBEMISSIVITY
* Split basic devicetree bindings into separate patch
* Make _probe and _remove mirror images
* Return -EIO instead of -EREMOTEIO on read error
v2:
* Drop processed temperature output (v1 patch 3)
* Drop raw IR values for now (v1 patch 7)
* Drop iir, fir, and gain attributes for now (v1 patch 5)
* Introduce standard attribute for emissivity (patches 4 and 5)
* Add patch 7, fixing wrong readings
* Return -EINTR when wake-up is interrupted during read (patch 6)
* Make adding vendor prefix explicit in commit message of patch 6
Vianney le Clément de Saint-Marcq (5):
iio: core: Introduce IIO_CHAN_INFO_CALIBEMISSIVITY
iio: mlx90614: Add devicetree bindings documentation
iio: mlx90614: Add emissivity setting
iio: mlx90614: Add power management
iio: mlx90614: Check for errors in read values
Documentation/ABI/testing/sysfs-bus-iio | 11 +
.../bindings/iio/temperature/mlx90614.txt | 24 ++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
drivers/iio/industrialio-core.c | 1 +
drivers/iio/temperature/mlx90614.c | 355 ++++++++++++++++++++-
include/linux/iio/iio.h | 1 +
6 files changed, 389 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
--
2.3.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 2/5] iio: mlx90614: Add devicetree bindings documentation
[not found] ` <1427704502-22347-1-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
@ 2015-03-30 8:34 ` Vianney le Clément de Saint-Marcq
[not found] ` <1427704502-22347-3-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
2015-03-30 8:35 ` [PATCH v3 4/5] iio: mlx90614: Add power management Vianney le Clément de Saint-Marcq
1 sibling, 1 reply; 5+ messages in thread
From: Vianney le Clément de Saint-Marcq @ 2015-03-30 8:34 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Vianney le Clément de Saint-Marcq,
Arnout Vandecappelle (Essensium/Mind)
Also introduce "melexis" as a vendor prefix for device tree bindings.
Signed-off-by: Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>
Cc: Arnout Vandecappelle (Essensium/Mind) <arnout-agwphqFHkxc@public.gmane.org>
---
v3: new patch split out from v2 patch 6 (power management)
---
.../devicetree/bindings/iio/temperature/mlx90614.txt | 15 +++++++++++++++
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
2 files changed, 16 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
diff --git a/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
new file mode 100644
index 0000000..4c959f3
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
@@ -0,0 +1,15 @@
+* Melexis MLX90614 contactless IR temperature sensor
+
+http://melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90614-615.aspx
+
+Required properties:
+
+ - compatible: should be "melexis,mlx90614"
+ - reg: the I2C address of the sensor
+
+Example:
+
+mlx90614@5a {
+ compatible = "melexis,mlx90614";
+ reg = <0x5a>;
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index fae26d0..c8db30c 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -110,6 +110,7 @@ lltc Linear Technology Corporation
marvell Marvell Technology Group Ltd.
maxim Maxim Integrated Products
mediatek MediaTek Inc.
+melexis Melexis N.V.
merrii Merrii Technology Co., Ltd.
micrel Micrel Inc.
microchip Microchip Technology Inc.
--
2.3.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 4/5] iio: mlx90614: Add power management
[not found] ` <1427704502-22347-1-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
2015-03-30 8:34 ` [PATCH v3 2/5] iio: mlx90614: Add devicetree bindings documentation Vianney le Clément de Saint-Marcq
@ 2015-03-30 8:35 ` Vianney le Clément de Saint-Marcq
[not found] ` <1427704502-22347-5-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
1 sibling, 1 reply; 5+ messages in thread
From: Vianney le Clément de Saint-Marcq @ 2015-03-30 8:35 UTC (permalink / raw)
To: linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Vianney le Clément de Saint-Marcq,
Arnout Vandecappelle (Essensium/Mind)
Add support for system sleep and runtime power management.
To wake up the device, the SDA line should be held low for at least 33ms
while SCL is high. As this is not possible using the i2c API (and not
supported by all i2c adapters), a GPIO connected to the SDA line is
needed. The GPIO is named "wakeup" and can be specified in a device
tree with the "wakeup-gpios" binding.
If the wake-up GPIO is not given, disable power management for the
device. Entering sleep requires an SMBus byte access, hence power
management is also disabled if byte access is not supported by the
adapter.
Signed-off-by: Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>
Cc: Arnout Vandecappelle (Essensium/Mind) <arnout-agwphqFHkxc@public.gmane.org>
---
The default autosleep delay (5s) is chosen arbitrarily, trying to take
into account the long startup time (250ms). Feel free to change it to
another value if you think it is saner.
I did not roll the bodies of _pm_runtime_{suspend,resume} into
one-liners as they would exceed 80 columns, requiring an (IMO ugly) line
break. Besides, the first two lines are coherent with other functions,
which arguably makes the code easier to read. No problem changing it
though if you disagree.
v3: * split out basic devicetree documentation in new patch
* make remove the reverse of probe
v2: return -EINTR when the startup delay is interrupted
---
.../bindings/iio/temperature/mlx90614.txt | 9 +
drivers/iio/temperature/mlx90614.c | 246 ++++++++++++++++++++-
2 files changed, 253 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
index 4c959f3..9be57b0 100644
--- a/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
+++ b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
@@ -7,9 +7,18 @@ Required properties:
- compatible: should be "melexis,mlx90614"
- reg: the I2C address of the sensor
+Optional properties:
+
+ - wakeup-gpios: device tree identifier of the GPIO connected to the SDA line
+ to hold low in order to wake up the device. In normal operation, the
+ GPIO is set as input and will not interfere in I2C communication. There
+ is no need for a GPIO driving the SCL line. If no GPIO is given, power
+ management is disabled.
+
Example:
mlx90614@5a {
compatible = "melexis,mlx90614";
reg = <0x5a>;
+ wakeup-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
};
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index a307d8c..73ec767 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -12,13 +12,24 @@
*
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
*
- * TODO: sleep mode, filter configuration
+ * To wake up from sleep mode, the SDA line must be held low while SCL is high
+ * for at least 33ms. This is achieved with an extra GPIO that can be connected
+ * directly to the SDA line. In normal operation, the GPIO is set as input and
+ * will not interfere in I2C communication. While the GPIO is driven low, the
+ * i2c adapter is locked since it cannot be used by other clients. The SCL line
+ * always has a pull-up so we do not need an extra GPIO to drive it high. If
+ * the "wakeup" GPIO is not given, power management will be disabled.
+ *
+ * TODO: filter configuration
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
@@ -52,9 +63,13 @@
#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 */
+#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
+
struct mlx90614_data {
struct i2c_client *client;
struct mutex lock; /* for EEPROM access only */
+ struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
+ unsigned long ready_timestamp; /* in jiffies */
};
/*
@@ -95,6 +110,54 @@ static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
return ret;
}
+#ifdef CONFIG_PM
+/*
+ * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
+ * the last wake-up. This is normally only needed to get a valid temperature
+ * reading. EEPROM access does not need such delay.
+ * Return 0 on success, <0 on error.
+ */
+static int mlx90614_power_get(struct mlx90614_data *data, bool startup)
+{
+ unsigned long now;
+
+ if (!data->wakeup_gpio)
+ return 0;
+
+ pm_runtime_get_sync(&data->client->dev);
+
+ if (startup) {
+ now = jiffies;
+ if (time_before(now, data->ready_timestamp) &&
+ msleep_interruptible(jiffies_to_msecs(
+ data->ready_timestamp - now)) != 0) {
+ pm_runtime_put_autosuspend(&data->client->dev);
+ return -EINTR;
+ }
+ }
+
+ return 0;
+}
+
+static void mlx90614_power_put(struct mlx90614_data *data)
+{
+ if (!data->wakeup_gpio)
+ return;
+
+ pm_runtime_mark_last_busy(&data->client->dev);
+ pm_runtime_put_autosuspend(&data->client->dev);
+}
+#else
+static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
+{
+ return 0;
+}
+
+static inline void mlx90614_power_put(struct mlx90614_data *data)
+{
+}
+#endif
+
static int mlx90614_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long mask)
@@ -125,7 +188,12 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+ ret = mlx90614_power_get(data, true);
+ if (ret < 0)
+ return ret;
ret = i2c_smbus_read_word_data(data->client, cmd);
+ mlx90614_power_put(data);
+
if (ret < 0)
return ret;
*val = ret;
@@ -138,10 +206,12 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
*val = 20;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
+ mlx90614_power_get(data, false);
mutex_lock(&data->lock);
ret = i2c_smbus_read_word_data(data->client,
MLX90614_EMISSIVITY);
mutex_unlock(&data->lock);
+ mlx90614_power_put(data);
if (ret < 0)
return ret;
@@ -172,10 +242,12 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
+ mlx90614_power_get(data, false);
mutex_lock(&data->lock);
ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
val);
mutex_unlock(&data->lock);
+ mlx90614_power_put(data);
if (ret < 0)
return ret;
@@ -235,6 +307,98 @@ static const struct iio_info mlx90614_info = {
.driver_module = THIS_MODULE,
};
+#ifdef CONFIG_PM
+static int mlx90614_sleep(struct mlx90614_data *data)
+{
+ s32 ret;
+
+ if (!data->wakeup_gpio) {
+ dev_dbg(&data->client->dev, "Sleep disabled");
+ return -ENOSYS;
+ }
+
+ dev_dbg(&data->client->dev, "Requesting sleep");
+
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
+ data->client->flags | I2C_CLIENT_PEC,
+ I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
+ I2C_SMBUS_BYTE, NULL);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int mlx90614_wakeup(struct mlx90614_data *data)
+{
+ if (!data->wakeup_gpio) {
+ dev_dbg(&data->client->dev, "Wake-up disabled");
+ return -ENOSYS;
+ }
+
+ dev_dbg(&data->client->dev, "Requesting wake-up");
+
+ i2c_lock_adapter(data->client->adapter);
+ gpiod_direction_output(data->wakeup_gpio, 0);
+ msleep(MLX90614_TIMING_WAKEUP);
+ gpiod_direction_input(data->wakeup_gpio);
+ i2c_unlock_adapter(data->client->adapter);
+
+ data->ready_timestamp = jiffies +
+ msecs_to_jiffies(MLX90614_TIMING_STARTUP);
+
+ /*
+ * Quirk: the i2c controller may get confused right after the
+ * wake-up signal has been sent. As a workaround, do a dummy read.
+ * If the read fails, the controller will probably be reset so that
+ * further reads will work.
+ */
+ i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
+
+ return 0;
+}
+
+/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
+static struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
+{
+ struct gpio_desc *gpio;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ dev_info(&client->dev,
+ "i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
+ return NULL;
+ }
+
+ gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
+
+ if (IS_ERR(gpio)) {
+ dev_warn(&client->dev,
+ "gpio acquisition failed with error %ld, sleep disabled",
+ PTR_ERR(gpio));
+ return NULL;
+ } else if (!gpio) {
+ dev_info(&client->dev,
+ "wakeup-gpio not found, sleep disabled");
+ }
+
+ return gpio;
+}
+#else
+static inline int mlx90614_sleep(struct mlx90614_data *data)
+{
+ return -ENOSYS;
+}
+static inline int mlx90614_wakeup(struct mlx90614_data *data)
+{
+ return -ENOSYS;
+}
+static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
+{
+ return NULL;
+}
+#endif
+
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
{
@@ -266,6 +430,9 @@ static int mlx90614_probe(struct i2c_client *client,
i2c_set_clientdata(client, indio_dev);
data->client = client;
mutex_init(&data->lock);
+ data->wakeup_gpio = mlx90614_probe_wakeup(client);
+
+ mlx90614_wakeup(data);
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
@@ -288,12 +455,30 @@ static int mlx90614_probe(struct i2c_client *client,
return ret;
}
+ if (data->wakeup_gpio) {
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ MLX90614_AUTOSLEEP_DELAY);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ }
+
return iio_device_register(indio_dev);
}
static int mlx90614_remove(struct i2c_client *client)
{
- iio_device_unregister(i2c_get_clientdata(client));
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (data->wakeup_gpio) {
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ mlx90614_sleep(data);
+ pm_runtime_set_suspended(&client->dev);
+ }
return 0;
}
@@ -304,10 +489,67 @@ static const struct i2c_device_id mlx90614_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mlx90614_id);
+#ifdef CONFIG_PM_SLEEP
+static int mlx90614_pm_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ if (data->wakeup_gpio && pm_runtime_active(dev))
+ return mlx90614_sleep(data);
+
+ return 0;
+}
+
+static int mlx90614_pm_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+ int err;
+
+ if (data->wakeup_gpio) {
+ err = mlx90614_wakeup(data);
+ if (err < 0)
+ return err;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int mlx90614_pm_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ return mlx90614_sleep(data);
+}
+
+static int mlx90614_pm_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ return mlx90614_wakeup(data);
+}
+#endif
+
+static const struct dev_pm_ops mlx90614_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
+ SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
+ mlx90614_pm_runtime_resume, NULL)
+};
+
static struct i2c_driver mlx90614_driver = {
.driver = {
.name = "mlx90614",
.owner = THIS_MODULE,
+ .pm = &mlx90614_pm_ops,
},
.probe = mlx90614_probe,
.remove = mlx90614_remove,
--
2.3.4
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 2/5] iio: mlx90614: Add devicetree bindings documentation
[not found] ` <1427704502-22347-3-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
@ 2015-04-09 14:05 ` Jonathan Cameron
0 siblings, 0 replies; 5+ messages in thread
From: Jonathan Cameron @ 2015-04-09 14:05 UTC (permalink / raw)
To: Vianney le Clément de Saint-Marcq,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Arnout Vandecappelle (Essensium/Mind)
On 30/03/15 09:34, Vianney le Clément de Saint-Marcq wrote:
> Also introduce "melexis" as a vendor prefix for device tree bindings.
>
> Signed-off-by: Vianney le Clément de Saint-Marcq <vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
> Cc: Arnout Vandecappelle (Essensium/Mind) <arnout-agwphqFHkxc@public.gmane.org>
Nothing even remotely controversial in here so applied to the togreg branch of iio.git
initially pushed out as testing. Thanks,
Jonathan
>
> ---
>
> v3: new patch split out from v2 patch 6 (power management)
> ---
> .../devicetree/bindings/iio/temperature/mlx90614.txt | 15 +++++++++++++++
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 2 files changed, 16 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
>
> diff --git a/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
> new file mode 100644
> index 0000000..4c959f3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
> @@ -0,0 +1,15 @@
> +* Melexis MLX90614 contactless IR temperature sensor
> +
> +http://melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90614-615.aspx
> +
> +Required properties:
> +
> + - compatible: should be "melexis,mlx90614"
> + - reg: the I2C address of the sensor
> +
> +Example:
> +
> +mlx90614@5a {
> + compatible = "melexis,mlx90614";
> + reg = <0x5a>;
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index fae26d0..c8db30c 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -110,6 +110,7 @@ lltc Linear Technology Corporation
> marvell Marvell Technology Group Ltd.
> maxim Maxim Integrated Products
> mediatek MediaTek Inc.
> +melexis Melexis N.V.
> merrii Merrii Technology Co., Ltd.
> micrel Micrel Inc.
> microchip Microchip Technology Inc.
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v3 4/5] iio: mlx90614: Add power management
[not found] ` <1427704502-22347-5-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
@ 2015-04-09 14:09 ` Jonathan Cameron
0 siblings, 0 replies; 5+ messages in thread
From: Jonathan Cameron @ 2015-04-09 14:09 UTC (permalink / raw)
To: Vianney le Clément de Saint-Marcq,
linux-iio-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA
Cc: Arnout Vandecappelle (Essensium/Mind)
On 30/03/15 09:35, Vianney le Clément de Saint-Marcq wrote:
> Add support for system sleep and runtime power management.
>
> To wake up the device, the SDA line should be held low for at least 33ms
> while SCL is high. As this is not possible using the i2c API (and not
> supported by all i2c adapters), a GPIO connected to the SDA line is
> needed. The GPIO is named "wakeup" and can be specified in a device
> tree with the "wakeup-gpios" binding.
>
> If the wake-up GPIO is not given, disable power management for the
> device. Entering sleep requires an SMBus byte access, hence power
> management is also disabled if byte access is not supported by the
> adapter.
>
> Signed-off-by: Vianney le Clément de Saint-Marcq <vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
> Cc: Arnout Vandecappelle (Essensium/Mind) <arnout-agwphqFHkxc@public.gmane.org>
whilst this is a hideous trick, the software support here is fine!
Applied to the togreg branch of iio.git.
Jonathan
>
> ---
>
> The default autosleep delay (5s) is chosen arbitrarily, trying to take
> into account the long startup time (250ms). Feel free to change it to
> another value if you think it is saner.
>
> I did not roll the bodies of _pm_runtime_{suspend,resume} into
> one-liners as they would exceed 80 columns, requiring an (IMO ugly) line
> break. Besides, the first two lines are coherent with other functions,
> which arguably makes the code easier to read. No problem changing it
> though if you disagree.
>
> v3: * split out basic devicetree documentation in new patch
> * make remove the reverse of probe
> v2: return -EINTR when the startup delay is interrupted
> ---
> .../bindings/iio/temperature/mlx90614.txt | 9 +
> drivers/iio/temperature/mlx90614.c | 246 ++++++++++++++++++++-
> 2 files changed, 253 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
> index 4c959f3..9be57b0 100644
> --- a/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
> +++ b/Documentation/devicetree/bindings/iio/temperature/mlx90614.txt
> @@ -7,9 +7,18 @@ Required properties:
> - compatible: should be "melexis,mlx90614"
> - reg: the I2C address of the sensor
>
> +Optional properties:
> +
> + - wakeup-gpios: device tree identifier of the GPIO connected to the SDA line
> + to hold low in order to wake up the device. In normal operation, the
> + GPIO is set as input and will not interfere in I2C communication. There
> + is no need for a GPIO driving the SCL line. If no GPIO is given, power
> + management is disabled.
> +
> Example:
>
> mlx90614@5a {
> compatible = "melexis,mlx90614";
> reg = <0x5a>;
> + wakeup-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
> };
> diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
> index a307d8c..73ec767 100644
> --- a/drivers/iio/temperature/mlx90614.c
> +++ b/drivers/iio/temperature/mlx90614.c
> @@ -12,13 +12,24 @@
> *
> * (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
> *
> - * TODO: sleep mode, filter configuration
> + * To wake up from sleep mode, the SDA line must be held low while SCL is high
> + * for at least 33ms. This is achieved with an extra GPIO that can be connected
> + * directly to the SDA line. In normal operation, the GPIO is set as input and
> + * will not interfere in I2C communication. While the GPIO is driven low, the
> + * i2c adapter is locked since it cannot be used by other clients. The SCL line
> + * always has a pull-up so we do not need an extra GPIO to drive it high. If
> + * the "wakeup" GPIO is not given, power management will be disabled.
> + *
> + * TODO: filter configuration
> */
>
> #include <linux/err.h>
> #include <linux/i2c.h>
> #include <linux/module.h>
> #include <linux/delay.h>
> +#include <linux/jiffies.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/pm_runtime.h>
>
> #include <linux/iio/iio.h>
>
> @@ -52,9 +63,13 @@
> #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 */
>
> +#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
> +
> struct mlx90614_data {
> struct i2c_client *client;
> struct mutex lock; /* for EEPROM access only */
> + struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
> + unsigned long ready_timestamp; /* in jiffies */
> };
>
> /*
> @@ -95,6 +110,54 @@ static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
> return ret;
> }
>
> +#ifdef CONFIG_PM
> +/*
> + * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
> + * the last wake-up. This is normally only needed to get a valid temperature
> + * reading. EEPROM access does not need such delay.
> + * Return 0 on success, <0 on error.
> + */
> +static int mlx90614_power_get(struct mlx90614_data *data, bool startup)
> +{
> + unsigned long now;
> +
> + if (!data->wakeup_gpio)
> + return 0;
> +
> + pm_runtime_get_sync(&data->client->dev);
> +
> + if (startup) {
> + now = jiffies;
> + if (time_before(now, data->ready_timestamp) &&
> + msleep_interruptible(jiffies_to_msecs(
> + data->ready_timestamp - now)) != 0) {
> + pm_runtime_put_autosuspend(&data->client->dev);
> + return -EINTR;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void mlx90614_power_put(struct mlx90614_data *data)
> +{
> + if (!data->wakeup_gpio)
> + return;
> +
> + pm_runtime_mark_last_busy(&data->client->dev);
> + pm_runtime_put_autosuspend(&data->client->dev);
> +}
> +#else
> +static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
> +{
> + return 0;
> +}
> +
> +static inline void mlx90614_power_put(struct mlx90614_data *data)
> +{
> +}
> +#endif
> +
> static int mlx90614_read_raw(struct iio_dev *indio_dev,
> struct iio_chan_spec const *channel, int *val,
> int *val2, long mask)
> @@ -125,7 +188,12 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
> return -EINVAL;
> }
>
> + ret = mlx90614_power_get(data, true);
> + if (ret < 0)
> + return ret;
> ret = i2c_smbus_read_word_data(data->client, cmd);
> + mlx90614_power_put(data);
> +
> if (ret < 0)
> return ret;
> *val = ret;
> @@ -138,10 +206,12 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
> *val = 20;
> return IIO_VAL_INT;
> case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
> + mlx90614_power_get(data, false);
> mutex_lock(&data->lock);
> ret = i2c_smbus_read_word_data(data->client,
> MLX90614_EMISSIVITY);
> mutex_unlock(&data->lock);
> + mlx90614_power_put(data);
>
> if (ret < 0)
> return ret;
> @@ -172,10 +242,12 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev,
> return -EINVAL;
> val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
>
> + mlx90614_power_get(data, false);
> mutex_lock(&data->lock);
> ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
> val);
> mutex_unlock(&data->lock);
> + mlx90614_power_put(data);
>
> if (ret < 0)
> return ret;
> @@ -235,6 +307,98 @@ static const struct iio_info mlx90614_info = {
> .driver_module = THIS_MODULE,
> };
>
> +#ifdef CONFIG_PM
> +static int mlx90614_sleep(struct mlx90614_data *data)
> +{
> + s32 ret;
> +
> + if (!data->wakeup_gpio) {
> + dev_dbg(&data->client->dev, "Sleep disabled");
> + return -ENOSYS;
> + }
> +
> + dev_dbg(&data->client->dev, "Requesting sleep");
> +
> + mutex_lock(&data->lock);
> + ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
> + data->client->flags | I2C_CLIENT_PEC,
> + I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
> + I2C_SMBUS_BYTE, NULL);
> + mutex_unlock(&data->lock);
> +
> + return ret;
> +}
> +
> +static int mlx90614_wakeup(struct mlx90614_data *data)
> +{
> + if (!data->wakeup_gpio) {
> + dev_dbg(&data->client->dev, "Wake-up disabled");
> + return -ENOSYS;
> + }
> +
> + dev_dbg(&data->client->dev, "Requesting wake-up");
> +
> + i2c_lock_adapter(data->client->adapter);
> + gpiod_direction_output(data->wakeup_gpio, 0);
> + msleep(MLX90614_TIMING_WAKEUP);
> + gpiod_direction_input(data->wakeup_gpio);
> + i2c_unlock_adapter(data->client->adapter);
> +
> + data->ready_timestamp = jiffies +
> + msecs_to_jiffies(MLX90614_TIMING_STARTUP);
> +
> + /*
> + * Quirk: the i2c controller may get confused right after the
> + * wake-up signal has been sent. As a workaround, do a dummy read.
> + * If the read fails, the controller will probably be reset so that
> + * further reads will work.
> + */
> + i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
> +
> + return 0;
> +}
> +
> +/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
> +static struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
> +{
> + struct gpio_desc *gpio;
> +
> + if (!i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_WRITE_BYTE)) {
> + dev_info(&client->dev,
> + "i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
> + return NULL;
> + }
> +
> + gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
> +
> + if (IS_ERR(gpio)) {
> + dev_warn(&client->dev,
> + "gpio acquisition failed with error %ld, sleep disabled",
> + PTR_ERR(gpio));
> + return NULL;
> + } else if (!gpio) {
> + dev_info(&client->dev,
> + "wakeup-gpio not found, sleep disabled");
> + }
> +
> + return gpio;
> +}
> +#else
> +static inline int mlx90614_sleep(struct mlx90614_data *data)
> +{
> + return -ENOSYS;
> +}
> +static inline int mlx90614_wakeup(struct mlx90614_data *data)
> +{
> + return -ENOSYS;
> +}
> +static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
> +{
> + return NULL;
> +}
> +#endif
> +
> /* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
> static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
> {
> @@ -266,6 +430,9 @@ static int mlx90614_probe(struct i2c_client *client,
> i2c_set_clientdata(client, indio_dev);
> data->client = client;
> mutex_init(&data->lock);
> + data->wakeup_gpio = mlx90614_probe_wakeup(client);
> +
> + mlx90614_wakeup(data);
>
> indio_dev->dev.parent = &client->dev;
> indio_dev->name = id->name;
> @@ -288,12 +455,30 @@ static int mlx90614_probe(struct i2c_client *client,
> return ret;
> }
>
> + if (data->wakeup_gpio) {
> + pm_runtime_set_autosuspend_delay(&client->dev,
> + MLX90614_AUTOSLEEP_DELAY);
> + pm_runtime_use_autosuspend(&client->dev);
> + pm_runtime_set_active(&client->dev);
> + pm_runtime_enable(&client->dev);
> + }
> +
> return iio_device_register(indio_dev);
> }
>
> static int mlx90614_remove(struct i2c_client *client)
> {
> - iio_device_unregister(i2c_get_clientdata(client));
> + struct iio_dev *indio_dev = i2c_get_clientdata(client);
> + struct mlx90614_data *data = iio_priv(indio_dev);
> +
> + iio_device_unregister(indio_dev);
> +
> + if (data->wakeup_gpio) {
> + pm_runtime_disable(&client->dev);
> + if (!pm_runtime_status_suspended(&client->dev))
> + mlx90614_sleep(data);
> + pm_runtime_set_suspended(&client->dev);
> + }
>
> return 0;
> }
> @@ -304,10 +489,67 @@ static const struct i2c_device_id mlx90614_id[] = {
> };
> MODULE_DEVICE_TABLE(i2c, mlx90614_id);
>
> +#ifdef CONFIG_PM_SLEEP
> +static int mlx90614_pm_suspend(struct device *dev)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> +
> + if (data->wakeup_gpio && pm_runtime_active(dev))
> + return mlx90614_sleep(data);
> +
> + return 0;
> +}
> +
> +static int mlx90614_pm_resume(struct device *dev)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> + int err;
> +
> + if (data->wakeup_gpio) {
> + err = mlx90614_wakeup(data);
> + if (err < 0)
> + return err;
> +
> + pm_runtime_disable(dev);
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> + }
> +
> + return 0;
> +}
> +#endif
> +
> +#ifdef CONFIG_PM
> +static int mlx90614_pm_runtime_suspend(struct device *dev)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> +
> + return mlx90614_sleep(data);
> +}
> +
> +static int mlx90614_pm_runtime_resume(struct device *dev)
> +{
> + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> + struct mlx90614_data *data = iio_priv(indio_dev);
> +
> + return mlx90614_wakeup(data);
> +}
> +#endif
> +
> +static const struct dev_pm_ops mlx90614_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
> + SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
> + mlx90614_pm_runtime_resume, NULL)
> +};
> +
> static struct i2c_driver mlx90614_driver = {
> .driver = {
> .name = "mlx90614",
> .owner = THIS_MODULE,
> + .pm = &mlx90614_pm_ops,
> },
> .probe = mlx90614_probe,
> .remove = mlx90614_remove,
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2015-04-09 14:09 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-03-30 8:34 [PATCH v3 0/5] iio: mlx90614 enhancements Vianney le Clément de Saint-Marcq
[not found] ` <1427704502-22347-1-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
2015-03-30 8:34 ` [PATCH v3 2/5] iio: mlx90614: Add devicetree bindings documentation Vianney le Clément de Saint-Marcq
[not found] ` <1427704502-22347-3-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
2015-04-09 14:05 ` Jonathan Cameron
2015-03-30 8:35 ` [PATCH v3 4/5] iio: mlx90614: Add power management Vianney le Clément de Saint-Marcq
[not found] ` <1427704502-22347-5-git-send-email-vianney.leclement-buIOx9BAs4sybS5Ee8rs3A@public.gmane.org>
2015-04-09 14:09 ` Jonathan Cameron
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).