lm-sensors.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Tiberiu Breana <tiberiu.a.breana@intel.com>
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] [PATCH v3] hwmon: (max31722) Add threshold alarm support
Date: Wed, 06 Apr 2016 09:07:20 +0000	[thread overview]
Message-ID: <1459933640-18240-1-git-send-email-tiberiu.a.breana@intel.com> (raw)

Add threshold alarm support for the max31722 temperature sensor driver.

Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>
---
Changes from v2:
- addressed Guenter's comments
- improved threshold value precision
- threshold values are now cached
---
 drivers/hwmon/max31722.c | 158 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 153 insertions(+), 5 deletions(-)

diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c
index 30a100e..1460b22 100644
--- a/drivers/hwmon/max31722.c
+++ b/drivers/hwmon/max31722.c
@@ -12,23 +12,37 @@
 #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;
 	u8 mode;
+	s16 thigh;
+	s16 tlow;
+	bool irq_enabled;
+	bool alarm_active;
 };
 
 static int max31722_set_mode(struct max31722_data *data, u8 mode)
@@ -56,23 +70,139 @@ 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);
 
-	ret = spi_w8r16(data->spi_device, MAX31722_REG_TEMP_LSB);
+	ret = spi_w8r16(data->spi_device, dev_attr->index);
 	if (ret < 0)
 		return ret;
 	/* Keep 12 bits and multiply by the scale of 62.5 millidegrees/bit. */
 	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;
+	ret = spi_write(data->spi_device, &tx_buf, sizeof(tx_buf));
+	if (ret < 0)
+		return ret;
+
+	if (dev_attr->index = 1)
+		data->thigh = thresh;
+	else if (dev_attr->index = 2)
+		data->tlow = thresh;
+
+	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 the alarm attribute if interrupts are disabled. */
+	if (!data->irq_enabled && index = 3)
+		return 0;
+
+	return attr->mode;
+}
+
+static const struct attribute_group max31722_group = {
+	.attrs = max31722_attrs, .is_visible = max31722_is_visible,
+};
+
+__ATTRIBUTE_GROUPS(max31722);
+
+static void 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)
+		goto exit_err;
+	temp = (s16)ret;
+	if (!data->tlow) {
+		ret = spi_w8r16(data->spi_device, MAX31722_REG_TLOW_LSB);
+		if (ret < 0)
+			goto exit_err;
+		data->tlow = (s16)ret;
+	}
+	if (!data->thigh) {
+		ret = spi_w8r16(data->spi_device, MAX31722_REG_THIGH_LSB);
+		if (ret < 0)
+			goto exit_err;
+		data->thigh = (s16)ret;
+	}
+
+	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;
+
+	sysfs_notify(&data->spi_device->dev.kobj, NULL, "temp1_alarm");
+	return;
+
+exit_err:
+	dev_err(&data->spi_device->dev, "failed to read temperature register\n");
+}
+
+static irqreturn_t max31722_irq_handler(int irq, void *private)
+{
+	struct max31722_data *data = private;
+
+	max31722_update_alarm(data);
+
+	return IRQ_HANDLED;
+}
 
 static int max31722_probe(struct spi_device *spi)
 {
@@ -89,11 +219,29 @@ static int max31722_probe(struct 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;
 
+	data->irq_enabled = false;
+	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;
+			data->alarm_active = false;
+			max31722_update_alarm(data);
+		}
+	}
+
 	data->hwmon_dev = hwmon_device_register_with_groups(&spi->dev,
 							    spi->modalias,
 							    data,
-- 
1.9.1


_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

             reply	other threads:[~2016-04-06  9:07 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-06  9:07 Tiberiu Breana [this message]
2016-04-06 14:41 ` [lm-sensors] [PATCH v3] hwmon: (max31722) Add threshold alarm support 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=1459933640-18240-1-git-send-email-tiberiu.a.breana@intel.com \
    --to=tiberiu.a.breana@intel.com \
    --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 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 read-only IMAP folder(s) and NNTP newsgroup(s).