From 6665c7a62839142e0e8e2b0c4899e20a60ad8bc5 Mon Sep 17 00:00:00 2001 From: Mateusz Kozyra Date: Fri, 9 Sep 2022 10:36:22 +0200 Subject: [PATCH] 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 | 193 ++++++++++++++++++++++++++++++++-------- 1 file changed, 157 insertions(+), 36 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 #include +#include +#include #include #include #include @@ -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"); - extended_mode = data & TMP461_CONFIG_REG_TEMP_RANGE_BIT; + return (error); +} - /* read temp MSB */ - error = tmp461_read_1(dev, TMP461_LOCAL_TEMP_REG_MSB, &data); +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; + + sc = device_get_softc(dev); + + 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); - - error = tmp461_read_1(dev, TMP461_LOCAL_TEMP_REG_LSB, &data); + /* trigger sample*/ + error = tmp461_write_1(dev, TMP461_ONESHOT_REG, 0xFF); if (error != 0) - return (ENXIO); + goto fail; - *temp = (((*temp << 4) | (data >> 4)) + TMP461_C_TO_K_FIX) >> 4; + /* wait for conversion time */ + DELAY(TMP461_SENSOR_MAX_CONV_TIME/(1UL<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)); } -