[PATCH 6/7] hwmon: Port Linux driver for LM75 sensor

Andrey Smirnov andrew.smirnov at gmail.com
Sun Dec 6 23:52:42 PST 2015


Signed-off-by: Andrey Smirnov <andrew.smirnov at gmail.com>
---
 drivers/hwmon/Kconfig  |   9 +++
 drivers/hwmon/Makefile |   2 +
 drivers/hwmon/lm75.c   | 194 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/hwmon/lm75.h   |  48 ++++++++++++
 4 files changed, 253 insertions(+)
 create mode 100644 drivers/hwmon/lm75.c
 create mode 100644 drivers/hwmon/lm75.h

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 6ce06a7..db51d26 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -13,4 +13,13 @@ menuconfig HWMON

 if HWMON

+config SENSORS_LM75
+	tristate "National Semiconductor LM75 and compatibles"
+	depends on I2C
+	help
+	  If you say yes here you get support for one common type of
+	  temperature sensor chip, with models including:
+
+		- National Semiconductor LM75, LM75A
+
 endif # HWMON
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 47a2e58..e73e681 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -3,3 +3,5 @@
 #

 obj-$(CONFIG_HWMON)		+= class.o
+
+obj-$(CONFIG_SENSORS_LM75)	+= lm75.o
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
new file mode 100644
index 0000000..5df85bf
--- /dev/null
+++ b/drivers/hwmon/lm75.c
@@ -0,0 +1,194 @@
+/*
+ * lm75.c - Part of lm_sensors, Linux kernel modules for hardware
+ *	 monitoring
+ * Copyright (c) 1998, 1999  Frodo Looijaard <frodol at dds.nl>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <malloc.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <i2c/i2c.h>
+#include <i2c/i2c.h>
+#include <linux/err.h>
+#include <hwmon.h>
+#include "lm75.h"
+
+
+/*
+ * This driver handles the LM75 and compatible digital temperature sensors.
+ */
+
+enum lm75_type {		/* keep sorted in alphabetical order */
+	lm75,
+};
+
+
+
+/* The LM75 registers */
+#define LM75_REG_CONF		0x01
+static const u8 LM75_REG_TEMP[3] = {
+	0x00,		/* input */
+	0x03,		/* max */
+	0x02,		/* hyst */
+};
+
+/* Each client has this additional data */
+struct lm75_data {
+	struct i2c_client	*client;
+	struct hwmon_sensor	sensor;
+
+	u8			resolution;	/* In bits, between 9 and 12 */
+};
+
+static inline struct lm75_data *to_lm75_data(struct hwmon_sensor *sensor)
+{
+	return container_of(sensor, struct lm75_data, sensor);
+}
+
+/*
+ * All registers are word-sized, except for the configuration register.
+ * LM75 uses a high-byte first convention, which is exactly opposite to
+ * the SMBus standard.
+ */
+static s32 lm75_read_value(struct i2c_client *client, u8 reg)
+{
+	if (reg == LM75_REG_CONF)
+		return i2c_smbus_read_byte_data(client, reg);
+	else
+		return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static s32 lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
+{
+	if (reg == LM75_REG_CONF)
+		return i2c_smbus_write_byte_data(client, reg, value);
+	else
+		return i2c_smbus_write_word_swapped(client, reg, value);
+}
+
+static inline s32 lm75_reg_to_mc(s16 temp, u8 resolution)
+{
+	return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
+}
+
+static int lm75_read_temp(struct hwmon_sensor *sensor, s32 *reading)
+{
+	struct lm75_data *data = to_lm75_data(sensor);
+
+	s32 ret = lm75_read_value(data->client, LM75_REG_TEMP[0]);
+
+	if (ret < 0)
+		return ret;
+
+	*reading = lm75_reg_to_mc((s16)ret, data->resolution);
+	return 0;
+}
+
+static int lm75_probe(struct device_d *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct lm75_data *data;
+	int err;
+	u8 set_mask, clr_mask;
+	int new;
+	enum lm75_type kind;
+
+	err = dev_get_drvdata(dev, (const void **)&kind);
+	if (err) {
+		dev_err(dev, "Can't get driver data\n");
+		return err;
+	}
+
+	data = xzalloc(sizeof(struct lm75_data));
+	if (!data)
+		return -ENOMEM;
+
+	data->client = client;
+
+	/* Set to LM75 resolution (9 bits, 1/2 degree C) and range.
+	 * Then tweak to be more precise when appropriate.
+	 */
+	set_mask = 0;
+	clr_mask = LM75_SHUTDOWN;		/* continuous conversions */
+
+	switch (kind) {
+	case lm75:
+		data->resolution = 9;
+		break;
+	}
+
+	/* configure as specified */
+	err = lm75_read_value(client, LM75_REG_CONF);
+	if (err < 0) {
+		dev_err(dev, "Can't read config? %d\n", err);
+		goto exit;
+	}
+
+	new = err & ~clr_mask;
+	new |= set_mask;
+	if (err != new)
+		lm75_write_value(client, LM75_REG_CONF, new);
+	dev_dbg(dev, "Config %02x\n", new);
+
+	if (!dev->device_node) {
+		dev_err(dev, "No device_node associated with the device\n");
+		err = -EINVAL;
+		goto exit;
+	}
+
+	data->sensor.name = of_get_property(dev->device_node,
+					    "barebox,sensor-name",
+					    NULL);
+	if (!data->sensor.name) {
+		dev_err(dev, "No sensor-name specified\n");
+		err = -EINVAL;
+		goto exit;
+	}
+
+	data->sensor.read = lm75_read_temp;
+	data->sensor.dev = dev;
+
+	err = hwmon_sensor_register(&data->sensor);
+
+exit:
+	if (err) {
+		free(data);
+		return err;
+	}
+
+	return 0;
+}
+
+static const struct platform_device_id lm75_ids[] = {
+	{ "lm75", lm75, },
+	{ /* LIST END */ }
+};
+
+static struct driver_d lm75_driver = {
+	.name		= "lm75",
+	.probe		= lm75_probe,
+	.id_table	= lm75_ids,
+};
+
+static int lm75_init(void)
+{
+	return i2c_driver_register(&lm75_driver);
+}
+device_initcall(lm75_init);
diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h
new file mode 100644
index 0000000..9ffe83e
--- /dev/null
+++ b/drivers/hwmon/lm75.h
@@ -0,0 +1,48 @@
+/*
+    lm75.h - Part of lm_sensors, Linux kernel modules for hardware
+	      monitoring
+    Copyright (c) 2003 Mark M. Hoffman <mhoffman at lightlink.com>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+    This file contains common code for encoding/decoding LM75 type
+    temperature readings, which are emulated by many of the chips
+    we support.  As the user is unlikely to load more than one driver
+    which contains this code, we don't worry about the wasted space.
+*/
+
+
+/* straight from the datasheet */
+#define LM75_TEMP_MIN (-55000)
+#define LM75_TEMP_MAX 125000
+#define LM75_SHUTDOWN 0x01
+
+/* TEMP: 0.001C/bit (-55C to +125C)
+   REG: (0.5C/bit, two's complement) << 7 */
+static inline u16 LM75_TEMP_TO_REG(long temp)
+{
+	int ntemp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
+	ntemp += (ntemp < 0 ? -250 : 250);
+	return (u16)((ntemp / 500) << 7);
+}
+
+static inline int LM75_TEMP_FROM_REG(u16 reg)
+{
+	/* use integer division instead of equivalent right shift to
+	   guarantee arithmetic shift and preserve the sign */
+	return ((s16)reg / 128) * 500;
+}
--
2.5.0



More information about the barebox mailing list