git: 6665c7a62839 - main - TMP461: Add support for ADT7461 sensor

From: Kornel Dulęba <kd_at_FreeBSD.org>
Date: Fri, 09 Sep 2022 11:50:15 UTC
The branch main has been updated by kd:

URL: https://cgit.FreeBSD.org/src/commit/?id=6665c7a62839142e0e8e2b0c4899e20a60ad8bc5

commit 6665c7a62839142e0e8e2b0c4899e20a60ad8bc5
Author:     Mateusz Kozyra <mkoz@semihalf.com>
AuthorDate: 2022-09-09 08:36:22 +0000
Commit:     Kornel Dulęba <kd@FreeBSD.org>
CommitDate: 2022-09-09 11:42:28 +0000

    TMP461: Add support for ADT7461 sensor
    
    The register map is fairly similar, with one difference in the local
    sensor temperature register width.
    Both devices support reading two sensors - "local" and "remote".
    While here add support for the latter one.
    The ADT7461 doesn't update the temperature correctly, unless a write
    transaction is done before every read.
    Do just that as a workaround for this issue.
    
    Tested on LS1046ARDB.
    
    Reviewed by:    manu
    Obtained from:  Semihalf
    Differential Revision: https://reviews.freebsd.org/D36464
---
 sys/dev/iicbus/tmp461.c | 191 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 156 insertions(+), 35 deletions(-)

diff --git a/sys/dev/iicbus/tmp461.c b/sys/dev/iicbus/tmp461.c
index 3956ca6d4c21..d967f4d91db9 100644
--- a/sys/dev/iicbus/tmp461.c
+++ b/sys/dev/iicbus/tmp461.c
@@ -32,6 +32,8 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
 #include <sys/module.h>
 #include <sys/ctype.h>
 #include <sys/kernel.h>
@@ -46,22 +48,37 @@ __FBSDID("$FreeBSD$");
 
 #define BIT(x)					(1UL << (x))
 
+/* register map */
 #define TMP461_LOCAL_TEMP_REG_MSB		0x0
 #define TMP461_LOCAL_TEMP_REG_LSB		0x15
 #define TMP461_GLOBAL_TEMP_REG_MSB		0x1
 #define TMP461_GLOBAL_TEMP_REG_LSB		0x10
-#define TMP461_CONFIG_REG			0x3
+#define TMP461_STATUS_REG			0x2
+#define TMP461_STATUS_REG_TEMP_LOCAL		BIT(2)
+#define TMP461_CONFIG_REG_R			0x3
+#define TMP461_CONFIG_REG_W			0x9
 #define TMP461_CONFIG_REG_TEMP_RANGE_BIT	BIT(2)
-
+#define TMP461_CONFIG_REG_STANDBY_BIT		BIT(6)
+#define TMP461_CONVERSION_RATE_REG		0x4
+#define TMP461_ONESHOT_REG			0xF
 #define TMP461_EXTENDED_TEMP_MODIFIER		64
+
 /* 28.4 fixed point representation of 273.15f */
 #define TMP461_C_TO_K_FIX			4370
-#define TMP461_TEMP_LSB				0
+
+#define TMP461_SENSOR_MAX_CONV_TIME		16000000
+#define TMP461_LOCAL_MEASURE			0
+#define TMP461_REMOTE_MEASURE			1
+
+/* flags */
+#define TMP461_LOCAL_TEMP_DOUBLE_REG		BIT(0)
+#define TMP461_REMOTE_TEMP_DOUBLE_REG		BIT(1)
 
 static int tmp461_probe(device_t dev);
 static int tmp461_attach(device_t dev);
 static int tmp461_read_1(device_t dev, uint8_t reg, uint8_t *data);
-static int tmp461_read_temp(device_t dev, int32_t *temp);
+static int tmp461_write_1(device_t dev, uint8_t reg, uint8_t data);
+static int tmp461_read_temperature(device_t dev, int32_t *temperature, bool mode);
 static int tmp461_detach(device_t dev);
 static int tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS);
 
@@ -73,15 +90,34 @@ static device_method_t tmp461_methods[] = {
 	DEVMETHOD_END
 };
 
+struct tmp461_softc {
+	struct mtx		mtx;
+	uint8_t			conf;
+};
+
 static driver_t tmp461_driver = {
 	"tmp461_dev",
 	tmp461_methods,
-	0
+	sizeof(struct tmp461_softc)
+};
+
+struct tmp461_data {
+	const char	*compat;
+	const char	*desc;
+	uint8_t		flags;
+};
+
+static struct tmp461_data sensor_list[] = {
+	{"adt7461", "ADT7461 Thernal Sensor Information",
+	    TMP461_REMOTE_TEMP_DOUBLE_REG},
+	{"tmp461", "TMP461 Thernal Sensor Information",
+	    TMP461_LOCAL_TEMP_DOUBLE_REG | TMP461_REMOTE_TEMP_DOUBLE_REG}
 };
 
 static struct ofw_compat_data tmp461_compat_data[] = {
-	{ "ti,tmp461",		1 },
-	{ NULL,			0 }
+	{"adi,adt7461",		(uintptr_t)&sensor_list[0]},
+	{"ti,tmp461",		(uintptr_t)&sensor_list[1]},
+	{NULL,			0}
 };
 
 DRIVER_MODULE(tmp461, iicbus, tmp461_driver, 0, 0);
@@ -91,19 +127,47 @@ static int
 tmp461_attach(device_t dev)
 {
 	struct sysctl_oid *sensor_root_oid;
+	struct tmp461_data *compat_data;
 	struct sysctl_ctx_list *ctx;
+	struct tmp461_softc *sc;
+	uint8_t data;
 
+	sc = device_get_softc(dev);
+	compat_data = (struct tmp461_data *)
+	    ofw_bus_search_compatible(dev, tmp461_compat_data)->ocd_data;
+	sc->conf = compat_data->flags;
 	ctx = device_get_sysctl_ctx(dev);
-	
+
+	mtx_init(&sc->mtx, "tmp461 temperature", "temperature", MTX_DEF);
+
 	sensor_root_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw),
 	    OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
-	    "TMP 461 Thermal Sensor Information");
+	    "Thermal Sensor Information");
 	if (sensor_root_oid == NULL)
 		return (ENXIO);
 
 	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensor_root_oid), OID_AUTO,
-	    "tmp461", CTLTYPE_INT | CTLFLAG_RD, dev, 0,
-	    tmp461_sensor_sysctl, "IK0", "TMP461 Thermal Sensor");
+	    "local_sensor", CTLTYPE_INT | CTLFLAG_RD, dev,
+	    TMP461_LOCAL_MEASURE, tmp461_sensor_sysctl,
+	    "IK1", compat_data->desc);
+
+	/* get status register */
+	if (tmp461_read_1(dev, TMP461_STATUS_REG, &data) != 0)
+		return (ENXIO);
+
+	if (!(data & TMP461_STATUS_REG_TEMP_LOCAL))
+		SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensor_root_oid), OID_AUTO,
+		    "remote_sensor", CTLTYPE_INT | CTLFLAG_RD, dev,
+		    TMP461_REMOTE_MEASURE, tmp461_sensor_sysctl,
+		    "IK1", compat_data->desc);
+
+	/* set standby mode */
+	if (tmp461_read_1(dev, TMP461_CONFIG_REG_R, &data) != 0)
+		return (ENXIO);
+
+	data |= TMP461_CONFIG_REG_STANDBY_BIT;
+	if (tmp461_write_1(dev, TMP461_CONFIG_REG_W, data) != 0)
+		return (ENXIO);
 
 	return (0);
 }
@@ -111,14 +175,17 @@ tmp461_attach(device_t dev)
 static int
 tmp461_probe(device_t dev)
 {
+	struct tmp461_data *compat_data;
 
 	if (!ofw_bus_status_okay(dev))
 		return (ENXIO);
 
-	if (!ofw_bus_search_compatible(dev, tmp461_compat_data)->ocd_data)
+	compat_data = (struct tmp461_data *)
+	    ofw_bus_search_compatible(dev, tmp461_compat_data)->ocd_data;
+	if (!compat_data)
 		return (ENXIO);
 
-	device_set_desc(dev, "TMP461 Thermal Sensor");
+	device_set_desc(dev, compat_data->compat);
 
 	return (BUS_PROBE_GENERIC);
 }
@@ -126,16 +193,20 @@ tmp461_probe(device_t dev)
 static int
 tmp461_detach(device_t dev)
 {
+	struct tmp461_softc *sc;
+
+	sc = device_get_softc(dev);
+	mtx_destroy(&sc->mtx);
 
 	return (0);
 }
 
 static int
-tmp461_read_1(device_t dev,  uint8_t reg, uint8_t *data)
+tmp461_read_1(device_t dev, uint8_t reg, uint8_t *data)
 {
 	int error;
 
-	error = iicdev_readfrom(dev, reg, (void *) data, 1, IIC_WAIT);
+	error = iicdev_readfrom(dev, reg, (void *) data, 1, IIC_DONTWAIT);
 	if (error != 0)
 		device_printf(dev, "Failed to read from device\n");
 
@@ -143,49 +214,99 @@ tmp461_read_1(device_t dev,  uint8_t reg, uint8_t *data)
 }
 
 static int
-tmp461_read_temp(device_t dev, int32_t *temp)
+tmp461_write_1(device_t dev, uint8_t reg, uint8_t data)
 {
-	bool extended_mode;
-	uint8_t data;
 	int error;
 
-	/* read temperature range */
-	error = tmp461_read_1(dev, TMP461_CONFIG_REG, &data);
+	error = iicdev_writeto(dev, reg, (void *) &data, 1, IIC_DONTWAIT);
 	if (error != 0)
-		return (ENXIO);
+		device_printf(dev, "Failed to write to device\n");
+
+	return (error);
+}
+
+static int
+tmp461_read_temperature(device_t dev, int32_t *temperature, bool remote_measure)
+{
+	uint8_t data, offset, reg;
+	struct tmp461_softc *sc;
+	int error;
 
-	extended_mode = data & TMP461_CONFIG_REG_TEMP_RANGE_BIT;
+	sc = device_get_softc(dev);
 
-	/* read temp MSB */
-	error = tmp461_read_1(dev, TMP461_LOCAL_TEMP_REG_MSB, &data);
+	mtx_lock(&sc->mtx);
+
+	error = tmp461_read_1(dev, TMP461_CONVERSION_RATE_REG, &data);
 	if (error != 0)
-		return (ENXIO);
+		goto fail;
 
-	*temp = signed_extend32(data, TMP461_TEMP_LSB, 8) -
-	    (extended_mode ? TMP461_EXTENDED_TEMP_MODIFIER : 0);
+	/* trigger sample*/
+	error = tmp461_write_1(dev, TMP461_ONESHOT_REG, 0xFF);
+	if (error != 0)
+		goto fail;
 
-	error = tmp461_read_1(dev, TMP461_LOCAL_TEMP_REG_LSB, &data);
+	/* wait for conversion time */
+	DELAY(TMP461_SENSOR_MAX_CONV_TIME/(1UL<<data));
+
+	/* read config register offset */
+	error = tmp461_read_1(dev, TMP461_CONFIG_REG_R, &data);
 	if (error != 0)
-		return (ENXIO);
+		goto fail;
 
-	*temp = (((*temp << 4) | (data >> 4)) + TMP461_C_TO_K_FIX) >> 4;
+	offset = (data & TMP461_CONFIG_REG_TEMP_RANGE_BIT ?
+	    TMP461_EXTENDED_TEMP_MODIFIER : 0);
 
-	return (0);
+	reg = remote_measure ?
+	    TMP461_GLOBAL_TEMP_REG_MSB : TMP461_LOCAL_TEMP_REG_MSB;
+
+	/* read temeperature value*/
+	error = tmp461_read_1(dev, reg, &data);
+	if (error != 0)
+		goto fail;
+
+	data -= offset;
+	*temperature = signed_extend32(data, 0, 8) << 4;
+
+	if (remote_measure) {
+		if (sc->conf & TMP461_REMOTE_TEMP_DOUBLE_REG) {
+			error = tmp461_read_1(dev,
+			    TMP461_GLOBAL_TEMP_REG_LSB, &data);
+			if (error != 0)
+				goto fail;
+
+			*temperature |= data >> 4;
+		}
+	} else {
+		if (sc->conf & TMP461_LOCAL_TEMP_DOUBLE_REG) {
+			error = tmp461_read_1(dev,
+			    TMP461_LOCAL_TEMP_REG_LSB, &data);
+			if (error != 0)
+				goto fail;
+
+			*temperature |= data >> 4;
+		}
+	}
+	*temperature = (((*temperature + TMP461_C_TO_K_FIX) * 10) >> 4);
+
+fail:
+	mtx_unlock(&sc->mtx);
+	return (error);
 }
 
 static int
 tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS)
 {
+	int32_t temperature;
 	device_t dev;
-	int32_t temp;
 	int error;
+	bool mode;
 
 	dev = arg1;
+	mode = arg2;
 
-	error = tmp461_read_temp(dev, &temp);
+	error = tmp461_read_temperature(dev, &temperature, mode);
 	if (error != 0)
 		return (error);
 
-	return (sysctl_handle_int(oidp, &temp, 0, req));
+	return (sysctl_handle_int(oidp, &temperature, 0, req));
 }
-