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
This commit is contained in:
parent
c44ca4c227
commit
6665c7a628
@ -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");
|
||||
|
||||
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<<data));
|
||||
|
||||
return (0);
|
||||
/* read config register offset */
|
||||
error = tmp461_read_1(dev, TMP461_CONFIG_REG_R, &data);
|
||||
if (error != 0)
|
||||
goto fail;
|
||||
|
||||
offset = (data & TMP461_CONFIG_REG_TEMP_RANGE_BIT ?
|
||||
TMP461_EXTENDED_TEMP_MODIFIER : 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));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user