From: Guenter Roeck <linux@roeck-us.net>
To: lm-sensors@vger.kernel.org
Subject: Re: [lm-sensors] [PATCH v5] hwmon: (max31722) Add threshold alarm support
Date: Fri, 08 Apr 2016 12:24:04 +0000 [thread overview]
Message-ID: <5707A2E4.8060702@roeck-us.net> (raw)
In-Reply-To: <1460106763-15643-1-git-send-email-tiberiu.a.breana@intel.com>
On 04/08/2016 02:12 AM, Tiberiu Breana wrote:
> Add threshold alarm support for the max31722 temperature sensor driver.
>
> Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>
> ---
> Changes from v4:
> - addressed Guenter's comments
>
> As specified in the v3 change log, threshold values, as well as the alarm
> attribute, are only visible if interrupts are enabled.
I missed that part. Why ? The thresholds are still supported
by the chip, even without interrupts.
Thanks,
Guenter
> ---
> drivers/hwmon/max31722.c | 181 +++++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 177 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c
> index 30a100e..f557c36 100644
> --- a/drivers/hwmon/max31722.c
> +++ b/drivers/hwmon/max31722.c
> @@ -12,23 +12,38 @@
> #include <linux/acpi.h>
> #include <linux/hwmon.h>
> #include <linux/hwmon-sysfs.h>
> +#include <linux/interrupt.h>
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/spi/spi.h>
>
> #define MAX31722_REG_CFG 0x00
> #define MAX31722_REG_TEMP_LSB 0x01
> +#define MAX31722_REG_THIGH_LSB 0x03
> +#define MAX31722_REG_TLOW_LSB 0x05
>
> #define MAX31722_MODE_CONTINUOUS 0x00
> #define MAX31722_MODE_STANDBY 0x01
> #define MAX31722_MODE_MASK 0xFE
> #define MAX31722_RESOLUTION_12BIT 0x06
> +#define MAX31722_TM_INTERRUPT 0x08
> #define MAX31722_WRITE_MASK 0x80
> +/*
> + * Minimum temperature value: -2^(12-1) * 62.5 = -128,000
> + * Maximum temperature value: (2^(12-1) - 1) * 62.5 ~= 127,937
> + */
> +#define MAX31722_MIN_TEMP -128000
> +#define MAX31722_MAX_TEMP 127937
>
> struct max31722_data {
> struct device *hwmon_dev;
> struct spi_device *spi_device;
> + struct mutex update_lock;
> u8 mode;
> + s32 thigh;
> + s32 tlow;
> + bool irq_enabled;
> + bool alarm_active;
> };
>
> static int max31722_set_mode(struct max31722_data *data, u8 mode)
> @@ -56,6 +71,12 @@ static ssize_t max31722_show_temp(struct device *dev,
> {
> ssize_t ret;
> struct max31722_data *data = dev_get_drvdata(dev);
> + struct sensor_device_attribute *dev_attr = to_sensor_dev_attr(attr);
> +
> + if (dev_attr->index = MAX31722_REG_THIGH_LSB)
> + return sprintf(buf, "%d\n", data->thigh);
> + else if (dev_attr->index = MAX31722_REG_TLOW_LSB)
> + return sprintf(buf, "%d\n", data->tlow);
>
> ret = spi_w8r16(data->spi_device, MAX31722_REG_TEMP_LSB);
> if (ret < 0)
> @@ -64,15 +85,145 @@ static ssize_t max31722_show_temp(struct device *dev,
> return sprintf(buf, "%d\n", (s16)le16_to_cpu(ret) * 125 / 32);
> }
>
> -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
> - max31722_show_temp, NULL, 0);
> +static ssize_t max31722_set_temp(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int ret;
> + long val;
> + s16 thresh;
> + u8 tx_buf[3];
> + struct max31722_data *data = dev_get_drvdata(dev);
> + struct sensor_device_attribute *dev_attr = to_sensor_dev_attr(attr);
> +
> + ret = kstrtol(buf, 10, &val);
> + if (ret < 0)
> + return ret;
> +
> + val = clamp_val(val, MAX31722_MIN_TEMP, MAX31722_MAX_TEMP);
> + thresh = cpu_to_le16(DIV_ROUND_CLOSEST(val * 32, 125));
> +
> + tx_buf[0] = dev_attr->index | MAX31722_WRITE_MASK;
> + tx_buf[1] = thresh & 0xff;
> + tx_buf[2] = thresh >> 8;
> +
> + mutex_lock(&data->update_lock);
> +
> + ret = spi_write(data->spi_device, &tx_buf, sizeof(tx_buf));
> + if (ret < 0) {
> + mutex_unlock(&data->update_lock);
> + return ret;
> + }
> +
> + if (dev_attr->index = MAX31722_REG_THIGH_LSB)
> + data->thigh = thresh * 125 / 32;
> + else
> + data->tlow = thresh * 125 / 32;
> +
> + mutex_unlock(&data->update_lock);
> +
> + return count;
> +}
> +
> +static ssize_t max31722_show_alarm(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct spi_device *spi_device = to_spi_device(dev);
> + struct max31722_data *data = spi_get_drvdata(spi_device);
> +
> + return sprintf(buf, "%d\n", data->alarm_active);
> +}
> +
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, max31722_show_temp, NULL,
> + MAX31722_REG_TEMP_LSB);
> +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, max31722_show_temp,
> + max31722_set_temp, MAX31722_REG_THIGH_LSB);
> +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, max31722_show_temp,
> + max31722_set_temp, MAX31722_REG_TLOW_LSB);
> +static DEVICE_ATTR(temp1_alarm, S_IRUGO, max31722_show_alarm, NULL);
>
> static struct attribute *max31722_attrs[] = {
> &sensor_dev_attr_temp1_input.dev_attr.attr,
> + &sensor_dev_attr_temp1_max.dev_attr.attr,
> + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
> + &dev_attr_temp1_alarm.attr,
> NULL,
> };
>
> -ATTRIBUTE_GROUPS(max31722);
> +static umode_t max31722_is_visible(struct kobject *kobj, struct attribute *attr,
> + int index)
> +{
> + struct device *dev = container_of(kobj, struct device, kobj);
> + struct max31722_data *data = dev_get_drvdata(dev);
> +
> + /* Hide alarm and threshold attributes if interrupts are disabled. */
> + if (!data->irq_enabled && index > 0)
> + return 0;
> +
> + return attr->mode;
> +}
> +
> +static const struct attribute_group max31722_group = {
> + .attrs = max31722_attrs, .is_visible = max31722_is_visible,
> +};
> +
> +__ATTRIBUTE_GROUPS(max31722);
> +
> +static ssize_t max31722_update_alarm(struct max31722_data *data)
> +{
> + s16 temp;
> + ssize_t ret;
> + /*
> + * Do a quick temperature reading and compare it with TLOW/THIGH
> + * so we can tell which threshold has been met.
> + */
> + ret = spi_w8r16(data->spi_device, MAX31722_REG_TEMP_LSB);
> + if (ret < 0)
> + return ret;
> + temp = (s16)le16_to_cpu(ret) * 125 / 32;
> +
> + if (temp > data->thigh || (!data->alarm_active && temp > data->tlow))
> + data->alarm_active = true;
> + else if (temp <= data->tlow ||
> + (data->alarm_active && temp < data->thigh))
> + data->alarm_active = false;
> +
> + return 0;
> +}
> +
> +static irqreturn_t max31722_irq_handler(int irq, void *private)
> +{
> + struct max31722_data *data = private;
> +
> + max31722_update_alarm(data);
> + sysfs_notify(&data->spi_device->dev.kobj, NULL, "temp1_alarm");
> +
> + return IRQ_HANDLED;
> +}
> +
> +static ssize_t max31722_init_thresh(struct max31722_data *data)
> +{
> + ssize_t ret;
> +
> + /* Cache threshold values. */
> + ret = spi_w8r16(data->spi_device, MAX31722_REG_TLOW_LSB);
> + if (ret < 0)
> + goto exit_err;
> + data->tlow = (s16)le16_to_cpu(ret) * 125 / 32;
> +
> + ret = spi_w8r16(data->spi_device, MAX31722_REG_THIGH_LSB);
> + if (ret < 0)
> + goto exit_err;
> + data->thigh = (s16)le16_to_cpu(ret) * 125 / 32;
> +
> + return 0;
> +
> +exit_err:
> + dev_err(&data->spi_device->dev,
> + "failed to read temperature threshold\n");
> + return ret;
> +}
>
> static int max31722_probe(struct spi_device *spi)
> {
> @@ -83,17 +234,39 @@ static int max31722_probe(struct spi_device *spi)
> if (!data)
> return -ENOMEM;
>
> + mutex_init(&data->update_lock);
> spi_set_drvdata(spi, data);
> data->spi_device = spi;
> /*
> * Set SD bit to 0 so we can have continuous measurements.
> * Set resolution to 12 bits for maximum precision.
> */
> - data->mode = MAX31722_MODE_CONTINUOUS | MAX31722_RESOLUTION_12BIT;
> + data->mode = MAX31722_MODE_CONTINUOUS | MAX31722_RESOLUTION_12BIT
> + | MAX31722_TM_INTERRUPT;
> ret = max31722_set_mode(data, MAX31722_MODE_CONTINUOUS);
> if (ret < 0)
> return ret;
>
> + if (spi->irq > 0) {
> + ret = devm_request_threaded_irq(&spi->dev, spi->irq,
> + NULL,
> + max31722_irq_handler,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + dev_name(&spi->dev), data);
> + if (ret < 0) {
> + dev_warn(&spi->dev, "request irq %d failed\n",
> + spi->irq);
> + } else {
> + data->irq_enabled = true;
> + ret = max31722_init_thresh(data);
> + if (ret < 0)
> + return ret;
> + ret = max31722_update_alarm(data);
> + if (ret < 0)
> + return ret;
> + }
> + }
> +
> data->hwmon_dev = hwmon_device_register_with_groups(&spi->dev,
> spi->modalias,
> data,
>
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
next prev parent reply other threads:[~2016-04-08 12:24 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-04-08 9:12 [lm-sensors] [PATCH v5] hwmon: (max31722) Add threshold alarm support Tiberiu Breana
2016-04-08 12:24 ` Guenter Roeck [this message]
2016-04-08 12:54 ` Breana, Tiberiu A
2016-04-08 13:30 ` Guenter Roeck
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=5707A2E4.8060702@roeck-us.net \
--to=linux@roeck-us.net \
--cc=lm-sensors@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.