Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/iicbus/tmp461.c
Show All 26 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_platform.h" | #include "opt_platform.h" | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/lock.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/ctype.h> | #include <sys/ctype.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/libkern.h> | #include <sys/libkern.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <dev/iicbus/iicbus.h> | #include <dev/iicbus/iicbus.h> | ||||
#include <dev/iicbus/iiconf.h> | #include <dev/iicbus/iiconf.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#define BIT(x) (1UL << (x)) | #define BIT(x) (1UL << (x)) | ||||
/* register map */ | |||||
#define TMP461_LOCAL_TEMP_REG_MSB 0x0 | #define TMP461_LOCAL_TEMP_REG_MSB 0x0 | ||||
#define TMP461_LOCAL_TEMP_REG_LSB 0x15 | #define TMP461_LOCAL_TEMP_REG_LSB 0x15 | ||||
#define TMP461_GLOBAL_TEMP_REG_MSB 0x1 | #define TMP461_GLOBAL_TEMP_REG_MSB 0x1 | ||||
#define TMP461_GLOBAL_TEMP_REG_LSB 0x10 | #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_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 | #define TMP461_EXTENDED_TEMP_MODIFIER 64 | ||||
/* 28.4 fixed point representation of 273.15f */ | /* 28.4 fixed point representation of 273.15f */ | ||||
#define TMP461_C_TO_K_FIX 4370 | #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_probe(device_t dev); | ||||
static int tmp461_attach(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_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_detach(device_t dev); | ||||
static int tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS); | static int tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS); | ||||
static device_method_t tmp461_methods[] = { | static device_method_t tmp461_methods[] = { | ||||
DEVMETHOD(device_probe, tmp461_probe), | DEVMETHOD(device_probe, tmp461_probe), | ||||
DEVMETHOD(device_attach, tmp461_attach), | DEVMETHOD(device_attach, tmp461_attach), | ||||
DEVMETHOD(device_detach, tmp461_detach), | DEVMETHOD(device_detach, tmp461_detach), | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
struct tmp461_softc { | |||||
struct mtx mtx; | |||||
uint8_t conf; | |||||
}; | |||||
static driver_t tmp461_driver = { | static driver_t tmp461_driver = { | ||||
"tmp461_dev", | "tmp461_dev", | ||||
tmp461_methods, | 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[] = { | static struct ofw_compat_data tmp461_compat_data[] = { | ||||
{ "ti,tmp461", 1 }, | {"adi,adt7461", (uintptr_t)&sensor_list[0]}, | ||||
{"ti,tmp461", (uintptr_t)&sensor_list[1]}, | |||||
{ NULL, 0 } | {NULL, 0} | ||||
}; | }; | ||||
DRIVER_MODULE(tmp461, iicbus, tmp461_driver, 0, 0); | DRIVER_MODULE(tmp461, iicbus, tmp461_driver, 0, 0); | ||||
IICBUS_FDT_PNP_INFO(tmp461_compat_data); | IICBUS_FDT_PNP_INFO(tmp461_compat_data); | ||||
static int | static int | ||||
tmp461_attach(device_t dev) | tmp461_attach(device_t dev) | ||||
{ | { | ||||
struct sysctl_oid *sensor_root_oid; | struct sysctl_oid *sensor_root_oid; | ||||
struct tmp461_data *compat_data; | |||||
struct sysctl_ctx_list *ctx; | 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); | 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), | sensor_root_oid = SYSCTL_ADD_NODE(ctx, SYSCTL_STATIC_CHILDREN(_hw), | ||||
OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, | OID_AUTO, "temperature", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, | ||||
"TMP 461 Thermal Sensor Information"); | "Thermal Sensor Information"); | ||||
if (sensor_root_oid == NULL) | if (sensor_root_oid == NULL) | ||||
return (ENXIO); | return (ENXIO); | ||||
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensor_root_oid), OID_AUTO, | SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensor_root_oid), OID_AUTO, | ||||
"tmp461", CTLTYPE_INT | CTLFLAG_RD, dev, 0, | "local_sensor", CTLTYPE_INT | CTLFLAG_RD, dev, | ||||
tmp461_sensor_sysctl, "IK0", "TMP461 Thermal Sensor"); | 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); | return (0); | ||||
} | } | ||||
static int | static int | ||||
tmp461_probe(device_t dev) | tmp461_probe(device_t dev) | ||||
{ | { | ||||
struct tmp461_data *compat_data; | |||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | 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); | return (ENXIO); | ||||
device_set_desc(dev, "TMP461 Thermal Sensor"); | device_set_desc(dev, compat_data->compat); | ||||
return (BUS_PROBE_GENERIC); | return (BUS_PROBE_GENERIC); | ||||
} | } | ||||
static int | static int | ||||
tmp461_detach(device_t dev) | tmp461_detach(device_t dev) | ||||
{ | { | ||||
struct tmp461_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
mtx_destroy(&sc->mtx); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | 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; | 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) | if (error != 0) | ||||
device_printf(dev, "Failed to read from device\n"); | device_printf(dev, "Failed to read from device\n"); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | 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; | int error; | ||||
/* read temperature range */ | error = iicdev_writeto(dev, reg, (void *) &data, 1, IIC_DONTWAIT); | ||||
error = tmp461_read_1(dev, TMP461_CONFIG_REG, &data); | |||||
if (error != 0) | 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 */ | static int | ||||
error = tmp461_read_1(dev, TMP461_LOCAL_TEMP_REG_MSB, &data); | 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) | if (error != 0) | ||||
return (ENXIO); | goto fail; | ||||
*temp = signed_extend32(data, TMP461_TEMP_LSB, 8) - | /* trigger sample*/ | ||||
(extended_mode ? TMP461_EXTENDED_TEMP_MODIFIER : 0); | 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) | 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 | static int | ||||
tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS) | tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int32_t temperature; | |||||
device_t dev; | device_t dev; | ||||
int32_t temp; | |||||
int error; | int error; | ||||
bool mode; | |||||
dev = arg1; | dev = arg1; | ||||
mode = arg2; | |||||
error = tmp461_read_temp(dev, &temp); | error = tmp461_read_temperature(dev, &temperature, mode); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
return (sysctl_handle_int(oidp, &temp, 0, req)); | return (sysctl_handle_int(oidp, &temperature, 0, req)); | ||||
} | } | ||||