[PATCH] [RESEND] Add support for SMSC EMC2305 fan controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



[Resending with text/plain attachment]

This patch adds support for the SMSC EMC2305 fan controller.

The driver works well enough for sensors to show the fan speed, for
pwmconfig to generate sensors3.conf, and fancontrol to adjust the fan
speeds dynamically. But that's it--I'm no sensors expert and this
should be considered preliminary work.

The code is based on the data sheet available at
http://www.smsc.com/media/Downloads_Public/Data_Sheets/2305.pdf .

Signed-off-by: Ed Swierk <eswierk@xxxxxxxxxxxxx>
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index bb8c8e5..5375cdd 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1028,6 +1028,16 @@ config SENSORS_EMC2103
 	  This driver can also be built as a module.  If so, the module
 	  will be called emc2103.
 
+config SENSORS_EMC2305
+	tristate "SMSC EMC2305 fan controller"
+	depends on I2C
+	help
+	  If you say yes here you get support for the SMSC EMC2305
+	  fan controller.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called emc2305.
+
 config SENSORS_EMC6W201
 	tristate "SMSC EMC6W201"
 	depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 30d38f1..49642e6 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_SENSORS_DS620)	+= ds620.o
 obj-$(CONFIG_SENSORS_DS1621)	+= ds1621.o
 obj-$(CONFIG_SENSORS_EMC1403)	+= emc1403.o
 obj-$(CONFIG_SENSORS_EMC2103)	+= emc2103.o
+obj-$(CONFIG_SENSORS_EMC2305)	+= emc2305.o
 obj-$(CONFIG_SENSORS_EMC6W201)	+= emc6w201.o
 obj-$(CONFIG_SENSORS_F71805F)	+= f71805f.o
 obj-$(CONFIG_SENSORS_F71882FG)	+= f71882fg.o
diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c
new file mode 100644
index 0000000..31ae412
--- /dev/null
+++ b/drivers/hwmon/emc2305.c
@@ -0,0 +1,228 @@
+/*
+ * A hwmon driver for the SMSC EMC2305 fan controller
+ * Copyright 2013 Big Switch Networks, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Complete datasheet is available (6/2013) at:
+ * http://www.smsc.com/media/Downloads_Public/Data_Sheets/2305.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+
+static const unsigned short normal_i2c[] = { 0x2C, 0x2D, 0x2E, 0x2F, 0x4C,
+					     0x4D, I2C_CLIENT_END };
+
+#define EMC2305_REG_DEVICE 0xFD
+#define EMC2305_REG_VENDOR 0xFE
+
+#define EMC2305_REG_FAN_DRIVE(n) (0x30 + 0x10 * n)
+#define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * n)
+#define EMC2305_REG_FAN_TACH(n) (0x3E + 0x10 * n)
+
+#define EMC2305_DEVICE 0x34
+#define EMC2305_VENDOR 0x5D
+
+struct emc2305_data {
+	struct device		*hwmon_dev;
+	struct attribute_group	attrs;
+	struct mutex		lock;
+};
+
+static int emc2305_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id);
+static int emc2305_detect(struct i2c_client *client,
+			  struct i2c_board_info *info);
+static int emc2305_remove(struct i2c_client *client);
+
+static const struct i2c_device_id emc2305_id[] = {
+	{ "emc2305", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, emc2305_id);
+
+static struct i2c_driver emc2305_driver = {
+	.class		= I2C_CLASS_HWMON,
+	.driver = {
+		.name	= "emc2305",
+	},
+	.probe		= emc2305_probe,
+	.remove		= emc2305_remove,
+	.id_table	= emc2305_id,
+	.detect		= emc2305_detect,
+	.address_list	= normal_i2c,
+};
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
+			char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct emc2305_data *data = i2c_get_clientdata(client);
+	int val;
+
+	mutex_lock(&data->lock);
+	val = i2c_smbus_read_word_swapped(client,
+					  EMC2305_REG_FAN_TACH(attr->index));
+	mutex_unlock(&data->lock);
+	return sprintf(buf, "%d\n", 3932160 * 2 / (val > 0 ? val : 1));
+}
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
+			char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct emc2305_data *data = i2c_get_clientdata(client);
+	int val;
+
+	mutex_lock(&data->lock);
+	val = i2c_smbus_read_byte_data(client,
+				       EMC2305_REG_FAN_DRIVE(attr->index));
+	mutex_unlock(&data->lock);
+	return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
+		       const char *buf, size_t count)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct i2c_client *client = to_i2c_client(dev);
+	struct emc2305_data *data = i2c_get_clientdata(client);
+	unsigned long val;
+	int ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+	if (val > 255)
+		return -EINVAL;
+
+	mutex_lock(&data->lock);
+	i2c_smbus_write_byte_data(client,
+				  EMC2305_REG_FAN_DRIVE(attr->index),
+				  val);
+	mutex_unlock(&data->lock);
+	return count;
+}
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 4);
+
+static struct attribute *emc2305_attr[] = {
+	&sensor_dev_attr_fan1_input.dev_attr.attr,
+	&sensor_dev_attr_fan2_input.dev_attr.attr,
+	&sensor_dev_attr_fan3_input.dev_attr.attr,
+	&sensor_dev_attr_fan4_input.dev_attr.attr,
+	&sensor_dev_attr_fan5_input.dev_attr.attr,
+	&sensor_dev_attr_pwm1.dev_attr.attr,
+	&sensor_dev_attr_pwm2.dev_attr.attr,
+	&sensor_dev_attr_pwm3.dev_attr.attr,
+	&sensor_dev_attr_pwm4.dev_attr.attr,
+	&sensor_dev_attr_pwm5.dev_attr.attr,
+	NULL
+};
+
+static int emc2305_detect(struct i2c_client *client,
+			  struct i2c_board_info *info)
+{
+	struct i2c_adapter *adapter = client->adapter;
+	int vendor, device;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
+				     I2C_FUNC_SMBUS_WORD_DATA))
+		return -ENODEV;
+
+	vendor = i2c_smbus_read_byte_data(client, EMC2305_REG_VENDOR);
+	if (vendor != EMC2305_VENDOR)
+		return -ENODEV;
+
+	device = i2c_smbus_read_byte_data(client, EMC2305_REG_DEVICE);
+	if (device != EMC2305_DEVICE)
+		return -ENODEV;
+
+	strlcpy(info->type, "emc2305", I2C_NAME_SIZE);
+
+	return 0;
+}
+
+static int emc2305_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct emc2305_data *data;
+	int err;
+	int i;
+
+	data = devm_kzalloc(&client->dev, sizeof(struct emc2305_data),
+			    GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, data);
+	mutex_init(&data->lock);
+
+	dev_info(&client->dev, "%s chip found\n", client->name);
+
+	data->attrs.attrs = emc2305_attr;
+	err = sysfs_create_group(&client->dev.kobj, &data->attrs);
+	if (err)
+		return err;
+
+	data->hwmon_dev = hwmon_device_register(&client->dev);
+	if (IS_ERR(data->hwmon_dev)) {
+		err = PTR_ERR(data->hwmon_dev);
+		goto exit_remove;
+	}
+
+	for (i = 0; i < 5; i++) {
+		/* set minimum drive to 20% */
+		i2c_smbus_write_byte_data(client,
+					  EMC2305_REG_FAN_MIN_DRIVE(i),
+					  0x33);
+	}
+
+	return 0;
+
+exit_remove:
+	sysfs_remove_group(&client->dev.kobj, &data->attrs);
+	return err;
+}
+
+static int emc2305_remove(struct i2c_client *client)
+{
+	struct emc2305_data *data = i2c_get_clientdata(client);
+
+	hwmon_device_unregister(data->hwmon_dev);
+	sysfs_remove_group(&client->dev.kobj, &data->attrs);
+	return 0;
+}
+
+module_i2c_driver(emc2305_driver);
+
+MODULE_AUTHOR("Ed Swierk <eswierk@xxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SMSC EMC2305 fan controller driver");
+MODULE_LICENSE("GPL");
_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux