From: Tiberiu Breana <tiberiu.a.breana@intel.com>
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] [PATCH 1/2] hwmon: (max31722) Add support for MAX31722/MAX31723 temperature sensors
Date: Tue, 22 Mar 2016 11:41:04 +0000 [thread overview]
Message-ID: <1458646865-6416-2-git-send-email-tiberiu.a.breana@intel.com> (raw)
Add basic support for the Maxim Integrated MAX31722/MAX31723 SPI
temperature sensors / thermostats.
Includes:
- ACPI support;
- raw temperature readings;
- power management
Datasheet:
https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf
Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>
---
Documentation/hwmon/max31722 | 34 +++++
drivers/hwmon/Kconfig | 11 ++
drivers/hwmon/Makefile | 1 +
drivers/hwmon/max31722.c | 304 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 350 insertions(+)
create mode 100644 Documentation/hwmon/max31722
create mode 100644 drivers/hwmon/max31722.c
diff --git a/Documentation/hwmon/max31722 b/Documentation/hwmon/max31722
new file mode 100644
index 0000000..090da845
--- /dev/null
+++ b/Documentation/hwmon/max31722
@@ -0,0 +1,34 @@
+Kernel driver max31722
+===========
+
+Supported chips:
+ * Maxim Integrated MAX31722
+ Prefix: 'max31722'
+ ACPI ID: MAX31722
+ Addresses scanned: -
+ Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf
+ * Maxim Integrated MAX31723
+ Prefix: 'max31723'
+ ACPI ID: MAX31723
+ Addresses scanned: -
+ Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31722-MAX31723.pdf
+
+Author: Tiberiu Breana <tiberiu.a.breana@intel.com>
+
+Description
+-----------
+
+This driver adds support for the Maxim Integrated MAX31722/MAX31723 thermometers
+and thermostats running over an SPI interface.
+
+Usage Notes
+-----------
+
+This driver uses ACPI to auto-detect devices. See ACPI IDs in the above section.
+
+Sysfs entries
+-------------
+
+The following attribute is supported:
+
+temp1_input Measured temperature. Read-only.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 5c2d13a..714be75 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -821,6 +821,17 @@ config SENSORS_MAX197
This driver can also be built as a module. If so, the module
will be called max197.
+config SENSORS_MAX31722
+tristate "MAX31722 temperature sensor"
+ depends on SPI
+ select REGMAP_SPI
+ help
+ Support for the Maxim Integrated MAX31722/MAX31723 digital
+ thermometers/thermostats operating over an SPI interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called max31722.
+
config SENSORS_MAX6639
tristate "Maxim MAX6639 sensor chip"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 58cc3ac..2ef5b7c 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
obj-$(CONFIG_SENSORS_MAX197) += max197.o
+obj-$(CONFIG_SENSORS_MAX31722) += max31722.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c
new file mode 100644
index 0000000..13ba906
--- /dev/null
+++ b/drivers/hwmon/max31722.c
@@ -0,0 +1,304 @@
+/*
+ * max31722 - hwmon driver for Maxim Integrated MAX31722/MAX31723 SPI
+ * digital thermometer and thermostats.
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#define MAX31722_REG_CFG 0x00
+#define MAX31722_REG_TEMP_LSB 0x01
+#define MAX31722_REG_TEMP_MSB 0x02
+#define MAX31722_MAX_REG 0x86
+
+#define MAX31722_MODE_CONTINUOUS 0x00
+#define MAX31722_MODE_STANDBY 0x01
+#define MAX31722_RESOLUTION_11BIT 0x02
+
+#define MAX31722_REGMAP_NAME "max31722_regmap"
+
+#define MAX31722_REGFIELD(name) \
+ do { \
+ data->reg_##name = \
+ devm_regmap_field_alloc(&spi->dev, regmap, \
+ max31722_reg_field_##name); \
+ if (IS_ERR(data->reg_##name)) { \
+ dev_err(&spi->dev, "reg field alloc failed.\n"); \
+ return PTR_ERR(data->reg_##name); \
+ } \
+ } while (0)
+
+struct max31722_data {
+ struct spi_device *spi_device;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+ struct regmap_field *reg_state;
+ struct regmap_field *reg_resolution;
+};
+
+/*
+ * This is a negative exponent value lookup table (as millidegree units)
+ * for calculating LSB values. The value corresponding to the fourth LSB
+ * bit is missing, as it is not used.
+ */
+static const int max31722_milli_table[3] = {
+ 500, 250, 125
+};
+
+static const struct reg_field max31722_reg_field_state + REG_FIELD(MAX31722_REG_CFG, 0, 0);
+static const struct reg_field max31722_reg_field_resolution + REG_FIELD(MAX31722_REG_CFG, 1, 2);
+
+static bool max31722_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX31722_REG_TEMP_LSB:
+ case MAX31722_REG_TEMP_MSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max31722_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX31722_REG_TEMP_LSB:
+ case MAX31722_REG_TEMP_MSB:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static struct regmap_config max31722_regmap_config = {
+ .name = MAX31722_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MAX31722_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = max31722_is_volatile_reg,
+ .writeable_reg = max31722_is_writeable_reg,
+
+ .read_flag_mask = 0x00,
+ .write_flag_mask = 0x80,
+};
+
+static int max31722_set_mode(struct max31722_data *data, u8 mode)
+{
+ int ret;
+ struct spi_device *spi = data->spi_device;
+
+ ret = regmap_field_write(data->reg_state, mode);
+ if (ret < 0)
+ dev_err(&spi->dev, "failed to set sensor mode.\n");
+
+ return ret;
+}
+
+static ssize_t max31722_show_name(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
+static ssize_t max31722_show_temperature(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int i;
+ int ret;
+ u8 lsb;
+ s16 val;
+ u16 temp;
+ struct max31722_data *data = dev_get_drvdata(dev);
+
+ ret = regmap_bulk_read(data->regmap, MAX31722_REG_TEMP_LSB, &temp, 2);
+ if (ret < 0) {
+ dev_err(&data->spi_device->dev,
+ "failed to read temperature register\n");
+ return ret;
+ }
+ /*
+ * The MSB contains the integer part of the temperature value
+ * (signed), while the LSB contains the fractional part as
+ * negative powers of 2, e.g.: bit 0 is 2^-1, bit 1 is 2^-2, etc.
+ * Temperature is exported in millidegrees Celsius.
+ */
+ val = (temp >> 8) * 1000;
+ lsb = (temp << 8) >> 13; /* We only need the first 3 LSB bits. */
+ i = 0;
+ while (i < 3) {
+ if ((lsb >> (3 - i - 1)) & 0x01)
+ val += max31722_milli_table[i];
+ i++;
+ }
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, max31722_show_name, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+ max31722_show_temperature, NULL, 0);
+
+static struct attribute *max31722_attributes[] = {
+ &dev_attr_name.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max31722_group = {
+ .attrs = max31722_attributes,
+};
+
+static int max31722_init(struct max31722_data *data)
+{
+ int ret = 0;
+ struct spi_device *spi = data->spi_device;
+ struct regmap *regmap;
+
+ /* Initialize the regmap */
+ regmap = devm_regmap_init_spi(spi, &max31722_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "regmap initialization failed.\n");
+ return PTR_ERR(regmap);
+ }
+ data->regmap = regmap;
+
+ MAX31722_REGFIELD(state);
+ MAX31722_REGFIELD(resolution);
+
+ /* Set SD bit to 0 so we can have continuous measurements. */
+ ret = regmap_field_write(data->reg_state, MAX31722_MODE_CONTINUOUS);
+ if (ret < 0)
+ goto err;
+ /*
+ * Set resolution to 11 bits (3 decimals) so we can have the maximum
+ * precision in the exported millidegree range.
+ */
+ ret = regmap_field_write(data->reg_resolution,
+ MAX31722_RESOLUTION_11BIT);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ dev_err(&spi->dev, "sensor configuration failed.\n");
+ return ret;
+}
+
+static int max31722_probe(struct spi_device *spi)
+{
+ int ret;
+ struct max31722_data *data;
+
+ data = devm_kzalloc(&spi->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, data);
+ data->spi_device = spi;
+
+ ret = max31722_init(data);
+ if (ret < 0)
+ goto err_standby;
+
+ ret = sysfs_create_group(&spi->dev.kobj, &max31722_group);
+ if (ret < 0)
+ goto err_standby;
+
+ data->hwmon_dev = hwmon_device_register(&spi->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ goto err_remove_group;
+ }
+
+ return 0;
+
+err_remove_group:
+ sysfs_remove_group(&spi->dev.kobj, &max31722_group);
+err_standby:
+ max31722_set_mode(data, MAX31722_MODE_STANDBY);
+ return ret;
+}
+
+static int max31722_remove(struct spi_device *spi)
+{
+ struct max31722_data *data = spi_get_drvdata(spi);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&spi->dev.kobj, &max31722_group);
+
+ return max31722_set_mode(data, MAX31722_MODE_STANDBY);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max31722_suspend(struct device *dev)
+{
+ struct spi_device *spi_device = to_spi_device(dev);
+ struct max31722_data *data = spi_get_drvdata(spi_device);
+
+ return max31722_set_mode(data, MAX31722_MODE_STANDBY);
+}
+
+static int max31722_resume(struct device *dev)
+{
+ struct spi_device *spi_device = to_spi_device(dev);
+ struct max31722_data *data = spi_get_drvdata(spi_device);
+
+ return max31722_set_mode(data, MAX31722_MODE_CONTINUOUS);
+}
+
+static SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume);
+
+#define MAX31722_PM_OPS (&max31722_pm_ops)
+#else
+#define MAX31722_PM_OPS NULL
+#endif
+
+static const struct spi_device_id max31722_spi_id[] = {
+ {"max31722", 0},
+ {"max31723", 0},
+ {}
+};
+
+static const struct acpi_device_id max31722_acpi_id[] = {
+ {"MAX31722", 0},
+ {"MAX31723", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(spi, max31722_spi_id);
+
+static struct spi_driver max31722_driver = {
+ .driver = {
+ .name = "max31722",
+ .pm = MAX31722_PM_OPS,
+ .acpi_match_table = ACPI_PTR(max31722_acpi_id),
+ },
+ .probe = max31722_probe,
+ .remove = max31722_remove,
+ .id_table = max31722_spi_id,
+};
+
+module_spi_driver(max31722_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("max31722 sensor driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
next reply other threads:[~2016-03-22 11:41 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-03-22 11:41 Tiberiu Breana [this message]
2016-03-22 14:11 ` [lm-sensors] [PATCH 1/2] hwmon: (max31722) Add support for MAX31722/MAX31723 temperature sensors 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=1458646865-6416-2-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 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.