[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