All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ben Hutchings <bhutchings@solarflare.com>
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] [PATCH] max664x: New driver for Maxim
Date: Mon, 09 Jun 2008 11:13:53 +0000	[thread overview]
Message-ID: <20080609111352.GC11300@solarflare.com> (raw)


Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 drivers/hwmon/Kconfig   |   10 ++
 drivers/hwmon/Makefile  |    1 +
 drivers/hwmon/max664x.c |  355 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/max664x.h |   92 ++++++++++++
 4 files changed, 458 insertions(+), 0 deletions(-)
 create mode 100644 drivers/hwmon/max664x.c
 create mode 100644 include/linux/max664x.h

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 00ff533..fad29c0 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -512,6 +512,16 @@ config SENSORS_MAX1619
 	  This driver can also be built as a module.  If so, the module
 	  will be called max1619.
 
+config SENSORS_MAX664X
+	tristate "Maxim MAX6646/6647/6649 sensor chips"
+	depends on I2C
+	help
+	  If you say yes here you get support for MAX6646/6647/6649
+	  sensor chips.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called max664x.
+
 config SENSORS_MAX6650
 	tristate "Maxim MAX6650 sensor chip"
 	depends on I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index d098677..24b907e 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_SENSORS_LM90)	+= lm90.o
 obj-$(CONFIG_SENSORS_LM92)	+= lm92.o
 obj-$(CONFIG_SENSORS_LM93)	+= lm93.o
 obj-$(CONFIG_SENSORS_MAX1619)	+= max1619.o
+obj-$(CONFIG_SENSORS_MAX664X)	+= max664x.o
 obj-$(CONFIG_SENSORS_MAX6650)	+= max6650.o
 obj-$(CONFIG_SENSORS_PC87360)	+= pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)	+= pc87427.o
diff --git a/drivers/hwmon/max664x.c b/drivers/hwmon/max664x.c
new file mode 100644
index 0000000..c5cd365
--- /dev/null
+++ b/drivers/hwmon/max664x.c
@@ -0,0 +1,355 @@
+/****************************************************************************
+ * Driver for Maxim MAX6646/6647/6649 sensor chips
+ * Copyright 2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/max664x.h>
+
+/* Data sheet: <http://datasheets.maxim-ic.com/en/ds/MAX6646-MAX6649.pdf> */
+
+#define MAX664X_REG_RLTS	0x00
+#define MAX664X_REG_RRTE	0x01
+#define MAX664X_REG_RSL		0x02
+#define MAX664X_REG_RCL		0x03
+#define MAX664X_REG_RCRA	0x04
+#define MAX664X_REG_RLHN	0x05
+#define MAX664X_REG_RLLI	0x06
+#define MAX664X_REG_RRHI	0x07
+#define MAX664X_REG_RRLS	0x08
+#define MAX664X_REG_WCA		0x09
+#define MAX664X_REG_WCRW	0x0a
+#define MAX664X_REG_WLHO	0x0b
+#define MAX664X_REG_WLLM	0x0c
+#define MAX664X_REG_WRHA	0x0d
+#define MAX664X_REG_WRLN	0x0e
+#define MAX664X_REG_OSHT	0x0f
+#define	MAX664X_REG_REET	0x10
+#define	MAX664X_REG_RIET	0x11
+#define	MAX664X_REG_RWOE	0x19
+#define	MAX664X_REG_RWOI	0x20
+#define	MAX664X_REG_HYS		0x21
+#define	MAX664X_REG_QUEUE	0x22
+#define	MAX664X_REG_MFID	0xfe
+#define	MAX664X_REG_REVID	0xff
+
+struct max664x_data {
+	struct device *hwmon_dev;
+	struct mutex update_lock;
+	int valid;
+	unsigned long last_updated; /* in jiffies */
+	struct max664x_settings set;
+	struct max664x_values val;
+};
+
+#define TEMP_FROM_REG(val) ((val) * 1000)
+#define TEMP_TO_REG(val) ((val) / 1000)
+#define PERIOD_FROM_RATE_REG(val) (16000 >> min_t(int, val, 6))
+#define PERIOD_TO_RATE_REG(val) SENSORS_LIMIT(7 - ffs(val / 250), 0, 6)
+
+static struct max664x_data *max664x_update_device(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct max664x_data *data = i2c_get_clientdata(client);
+
+	mutex_lock(&data->update_lock);
+
+	if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+		data->set.rate +			i2c_smbus_read_byte_data(client, MAX664X_REG_RCRA);
+		data->val.temp[0] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RLTS);
+		data->set.temp_overt[0] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RWOI);
+		data->set.temp_high_alert[0] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RLHN);
+		data->set.temp_low_alert[0] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RLLI);
+		data->val.temp[1] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RRTE);
+		data->set.temp_overt[1] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RWOE);
+		data->set.temp_high_alert[1] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RRHI);
+		data->set.temp_low_alert[1] +			i2c_smbus_read_byte_data(client, MAX664X_REG_RRLS);
+		data->set.temp_overt_hyst +			i2c_smbus_read_byte_data(client, MAX664X_REG_HYS);
+
+		data->last_updated = jiffies;
+		data->valid = 1;
+	}
+
+	mutex_unlock(&data->update_lock);
+
+	return data;
+}
+
+struct max664x_values *max664x_update_values(struct i2c_client *client)
+{
+	struct max664x_data *data = max664x_update_device(&client->dev);
+	return &data->val;
+}
+EXPORT_SYMBOL(max664x_update_values);
+
+static void
+set_temp_limit(struct device *dev, const char *buf, int offset, int reg)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct max664x_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	u8 reg_val = TEMP_TO_REG(val);
+
+	mutex_lock(&data->update_lock);
+	((u8 *)&data->set)[offset] = reg_val;
+	i2c_smbus_write_byte_data(client, reg, reg_val);
+	mutex_unlock(&data->update_lock);
+}
+
+#define TEMP_LIMIT_ATTR(offset, limit, array_name, reg_name)		\
+	static ssize_t							\
+	show_temp##offset##_##limit(struct device *dev,			\
+				    struct device_attribute *attr, char *buf) \
+	{								\
+		struct max664x_data *data = max664x_update_device(dev); \
+		return sprintf(buf, "%u\n",				\
+			       TEMP_FROM_REG(data->set.array_name[offset - 1])); \
+	}								\
+	static ssize_t							\
+	set_temp##offset##_##limit(struct device *dev,			\
+				   struct device_attribute *attr,	\
+				   const char *buf, size_t count)	\
+	{								\
+		set_temp_limit(dev, buf,				\
+			       offsetof(struct max664x_settings,	\
+					array_name[offset - 1]),	\
+			       MAX664X_REG_##reg_name);			\
+		return count;						\
+	}								\
+	static DEVICE_ATTR(temp##offset##_##limit, S_IRUGO | S_IWUSR,	\
+			   show_temp##offset##_##limit,			\
+			   set_temp##offset##_##limit);
+
+static ssize_t show_temp_crit_hyst(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct max664x_data *data = max664x_update_device(dev);
+	return sprintf(buf, "%u\n", TEMP_FROM_REG(data->set.temp_overt_hyst));
+}
+
+static ssize_t
+set_temp_crit_hyst(struct device *dev, struct device_attribute *attr,
+		   const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct max664x_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	u8 reg_val = TEMP_TO_REG(val);
+
+	mutex_lock(&data->update_lock);
+	data->set.temp_overt_hyst = reg_val;
+	i2c_smbus_write_byte_data(client, MAX664X_REG_HYS, reg_val);
+	mutex_unlock(&data->update_lock);
+	return count;
+}
+
+#define TEMP_ATTRS(offset, overt_reg, high_alert_reg, low_alert_reg)	\
+	static ssize_t							\
+	show_temp##offset##_input(struct device *dev,			\
+				  struct device_attribute *attr, char *buf) \
+	{								\
+		struct max664x_data *data = max664x_update_device(dev);	\
+		return sprintf(buf, "%u\n",				\
+			       TEMP_FROM_REG(data->val.temp[offset - 1])); \
+	}								\
+	static DEVICE_ATTR(temp##offset##_input, S_IRUGO,		\
+			   show_temp##offset##_input, NULL);		\
+	TEMP_LIMIT_ATTR(offset, crit, temp_overt, overt_reg)		\
+	static DEVICE_ATTR(temp##offset##_crit_hyst, S_IRUGO,		\
+			   show_temp_crit_hyst, set_temp_crit_hyst);	\
+	TEMP_LIMIT_ATTR(offset, max, temp_high_alert, high_alert_reg)	\
+	TEMP_LIMIT_ATTR(offset, min, temp_low_alert, low_alert_reg)
+
+TEMP_ATTRS(1, RWOI, WLHO, WLLM)
+TEMP_ATTRS(2, RWOE, WRHA, WRLN)
+
+static ssize_t
+show_period(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct max664x_data *data = max664x_update_device(dev);
+	return sprintf(buf, "%u\n", PERIOD_FROM_RATE_REG(data->set.rate));
+}
+static ssize_t set_period(struct device *dev, struct device_attribute *attr,
+			  const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct max664x_data *data = i2c_get_clientdata(client);
+	long val = simple_strtol(buf, NULL, 10);
+	u8 reg_val = PERIOD_TO_RATE_REG(val);
+
+	mutex_lock(&data->update_lock);
+	data->set.rate = reg_val;
+	i2c_smbus_write_byte_data(client, MAX664X_REG_WCRW, reg_val);
+	mutex_unlock(&data->update_lock);
+	return count;
+}
+static DEVICE_ATTR(period, S_IRUGO | S_IWUSR, show_period, set_period);
+
+/*
+ * Status is read-to-clear, so it is not world-readable and is not
+ * updated with the other fields.
+ */
+static ssize_t show_status(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	int val = i2c_smbus_read_byte_data(client, MAX664X_REG_RSL);
+	return val >= 0 ? sprintf(buf, "0x%x\n", val) : val;
+}
+static DEVICE_ATTR(status, S_IRUSR, show_status, NULL);
+
+int max664x_read_status(struct i2c_client *client)
+{
+	return i2c_smbus_read_byte_data(client, MAX664X_REG_RSL);
+}
+EXPORT_SYMBOL(max664x_read_status);
+
+static struct attribute *max664x_attributes[] = {
+	&dev_attr_temp1_input.attr,
+	&dev_attr_temp1_crit.attr,
+	&dev_attr_temp1_crit_hyst.attr,
+	&dev_attr_temp1_max.attr,
+	&dev_attr_temp1_min.attr,
+	&dev_attr_temp2_input.attr,
+	&dev_attr_temp2_crit.attr,
+	&dev_attr_temp2_crit_hyst.attr,
+	&dev_attr_temp2_max.attr,
+	&dev_attr_temp2_min.attr,
+
+	&dev_attr_period.attr,
+	&dev_attr_status.attr,
+
+	NULL
+};
+
+static const struct attribute_group max664x_group = {
+	.attrs = max664x_attributes,
+};
+
+static int
+max664x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	const struct max664x_platform_data *plat_data +		client->dev.platform_data;
+	struct max664x_data *data;
+	int err;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EIO;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->valid = 0;
+	mutex_init(&data->update_lock);
+	if (plat_data)
+		data->set = plat_data->set;
+	i2c_set_clientdata(client, data);
+
+	if (plat_data && plat_data->set_rate)
+		i2c_smbus_write_byte_data(client, MAX664X_REG_WCRW,
+					  data->set.rate);
+	if (plat_data && plat_data->set_limits) {
+		i2c_smbus_write_byte_data(client, MAX664X_REG_RWOI,
+					  data->set.temp_overt[0]);
+		i2c_smbus_write_byte_data(client, MAX664X_REG_WLHO,
+					  data->set.temp_high_alert[0]);
+		i2c_smbus_write_byte_data(client, MAX664X_REG_WLLM,
+					  data->set.temp_low_alert[0]);
+		i2c_smbus_write_byte_data(client, MAX664X_REG_RWOE,
+					  data->set.temp_overt[1]);
+		i2c_smbus_write_byte_data(client, MAX664X_REG_WRHA,
+					  data->set.temp_high_alert[1]);
+		i2c_smbus_write_byte_data(client, MAX664X_REG_WRLN,
+					  data->set.temp_low_alert[1]);
+		i2c_smbus_write_byte_data(client, MAX664X_REG_HYS,
+					  data->set.temp_overt_hyst);
+	}
+
+	err = sysfs_create_group(&client->dev.kobj, &max664x_group);
+	if (err)
+		goto exit_free;
+
+	data->hwmon_dev = hwmon_device_register(&client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove;
+	}
+
+	return 0;
+
+exit_remove:
+	sysfs_remove_group(&client->dev.kobj, &max664x_group);
+exit_free:
+	kfree(data);
+	i2c_set_clientdata(client, NULL);
+	return err;
+}
+
+static int max664x_remove(struct i2c_client *client)
+{
+	struct max664x_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&client->dev.kobj, &max664x_group);
+
+	kfree(data);
+	i2c_set_clientdata(client, NULL);
+	return 0;
+}
+
+static const struct i2c_device_id max664x_id[] = {
+	{ "max6646" },
+	{ "max6647" },
+	{ "max6649" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max664x_id);
+
+struct i2c_driver max664x_driver = {
+	.driver = {
+		.name	= "max664x",
+	},
+	.probe		= max664x_probe,
+	.remove		= max664x_remove,
+	.id_table	= max664x_id,
+};
+
+static int __init max664x_init(void)
+{
+	return i2c_add_driver(&max664x_driver);
+}
+
+static void __exit max664x_exit(void)
+{
+	i2c_del_driver(&max664x_driver);
+}
+
+MODULE_AUTHOR("Solarflare Communications");
+MODULE_DESCRIPTION("MAX6646/6647/6649 driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(max664x_init);
+module_exit(max664x_exit);
diff --git a/include/linux/max664x.h b/include/linux/max664x.h
new file mode 100644
index 0000000..0b05903
--- /dev/null
+++ b/include/linux/max664x.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+ * Interface to max664x driver
+ * Copyright 2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef _MAX664X_H_
+#define _MAX664X_H_
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/i2c.h>
+
+/* Status register */
+#define MAX664X_STATUS_IOT	(1 << 0)
+#define MAX664X_STATUS_EOT	(1 << 1)
+#define MAX664X_STATUS_FAULT	(1 << 2)
+#define MAX664X_STATUS_RLOW	(1 << 3)
+#define MAX664X_STATUS_RHIGH	(1 << 4)
+#define MAX664X_STATUS_LLOW	(1 << 5)
+#define MAX664X_STATUS_LHIGH	(1 << 6)
+#define MAX664X_STATUS_BUSY	(1 << 7)
+
+#define MAX664X_TEMP_INT	0
+#define MAX664X_TEMP_EXT	1
+
+/**
+ * struct max664x_settings - settings for MAX6646/6647/6649 hardware monitor
+ * @rate: Conversion rate setting
+ * @temp_overt: High temperature to set OVERT
+ * @temp_high_alert: High temperature to set ALERT
+ * @temp_low_alert: Low temperature to set ALERT
+ * @temp_overt_hyst: Hysteresis for resetting OVERT
+ *
+ * All values are raw register values.
+ */
+struct max664x_settings {
+	u8 rate;
+	u8 temp_overt[2];
+	u8 temp_high_alert[2];
+	u8 temp_low_alert[2];
+	u8 temp_overt_hyst;
+};
+
+/**
+ * struct max664x_values - values from MAX6646/6647/6649 hardware monitor
+ * @temp: Current temperature
+ *
+ * All values are raw register values.
+ */
+struct max664x_values {
+	u8 temp[2];
+};
+
+/**
+ * struct max664x_platform_data - platform data for MAX6646/6647/6649 hardware monitor
+ * @set_rate: Flag for whether to set rate register
+ * @set_limits: Flag for whether to set limit registers
+ * @set: Settings to be applied depending on the above flags
+ *
+ * This platform data is optional.  If not provided, the driver will
+ * assume that the MAX6646/6647/6649 was properly configured by firmware
+ * or the power-on-reset defaults.
+ */
+struct max664x_platform_data {
+	unsigned set_rate : 1;
+	unsigned set_limits : 1;
+	struct max664x_settings set;
+};
+
+/**
+ * max664x_update_values() - update and return current values
+ * @client: I2C client to which the max664x driver is bound
+ */
+struct max664x_values *max664x_update_values(struct i2c_client *client);
+
+/**
+ * max664x_read_status() - read status register
+ * @client: I2C client to which the max664x driver is bound
+ *
+ * Read the status register, which automatically clears any stuck alarm
+ * bits.
+ */
+int max664x_read_status(struct i2c_client *client);
+
+#endif
+
+#endif

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.

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

             reply	other threads:[~2008-06-09 11:13 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-06-09 11:13 Ben Hutchings [this message]
2008-07-07 11:03 ` [lm-sensors] [PATCH] max664x: New driver for Maxim Jean Delvare
2008-07-08 13:13 ` Ben Hutchings
2008-07-08 13:55 ` Jean Delvare

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=20080609111352.GC11300@solarflare.com \
    --to=bhutchings@solarflare.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.