Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/iicbus/tmp461.c
Show All 38 Lines | |||||
#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)) | ||||
#define TMP461_LOCAL_TEMP_REG_MSB 0x0 | /* register map */ | ||||
#define TMP461_LOCAL_TEMP_REG_LSB 0x15 | #define LOCAL_TEMP_REG_MSB 0x0 | ||||
#define TMP461_GLOBAL_TEMP_REG_MSB 0x1 | #define LOCAL_TEMP_REG_LSB 0x15 | ||||
#define TMP461_GLOBAL_TEMP_REG_LSB 0x10 | #define REMOTE_TEMP_REG_MSB 0x1 | ||||
#define TMP461_CONFIG_REG 0x3 | #define REMOTE_TEMP_REG_LSB 0x10 | ||||
#define TMP461_CONFIG_REG_TEMP_RANGE_BIT BIT(2) | #define STATUS_REG 0x2 | ||||
#define STATUS_REG_TEMP_LOCAL BIT(2) | |||||
#define CONFIG_REG_R 0x3 | |||||
#define CONFIG_REG_W 0x9 | |||||
#define CONFIG_REG_TEMP_RANGE_BIT BIT(2) | |||||
#define CONFIG_REG_STANDBY_BIT BIT(6) | |||||
#define CONVERSION_RATE_REG 0x4 | |||||
#define ONESHOT_REG 0xF | |||||
#define EXTENDED_MODE_TEMP_OFFSET 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 TEMP_C_TO_K_FIX 4370 | ||||
#define TMP461_TEMP_LSB 0 | |||||
static int tmp461_probe(device_t dev); | #define SENSOR_MAX_CONV_TIME 16000000 | ||||
static int tmp461_attach(device_t dev); | #define LOCAL_MEASURE 0 | ||||
static int tmp461_read_1(device_t dev, uint8_t reg, uint8_t *data); | #define REMOTE_MEASURE 1 | ||||
static int tmp461_read_temp(device_t dev, int32_t *temp); | |||||
static int tmp461_detach(device_t dev); | |||||
static int tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS); | |||||
static device_method_t tmp461_methods[] = { | /* flags */ | ||||
DEVMETHOD(device_probe, tmp461_probe), | #define LOCAL_TEMP_DOUBLE_REG BIT(0) | ||||
DEVMETHOD(device_attach, tmp461_attach), | #define REMOTE_TEMP_DOUBLE_REG BIT(1) | ||||
DEVMETHOD(device_detach, tmp461_detach), | |||||
static int temp_sensor_probe(device_t dev); | |||||
static int temp_sensor_attach(device_t dev); | |||||
static int temp_sensor_read_1(device_t dev, uint8_t reg, uint8_t *data); | |||||
static int temp_sensor_write_1(device_t dev, uint8_t reg, uint8_t data); | |||||
static int temp_sensor_read_temp(device_t dev, int32_t *temp, bool mode); | |||||
static int temp_sensor_detach(device_t dev); | |||||
static int temp_sensor_sysctl(SYSCTL_HANDLER_ARGS); | |||||
static device_method_t temp_sensor_methods[] = { | |||||
DEVMETHOD(device_probe, temp_sensor_probe), | |||||
DEVMETHOD(device_attach, temp_sensor_attach), | |||||
DEVMETHOD(device_detach, temp_sensor_detach), | |||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static driver_t tmp461_driver = { | static driver_t temp_sensor_driver = { | ||||
"tmp461_dev", | "temp_sensor_dev", | ||||
tmp461_methods, | temp_sensor_methods, | ||||
0 | 0 | ||||
}; | }; | ||||
static struct ofw_compat_data tmp461_compat_data[] = { | struct sensor_data { | ||||
{ "ti,tmp461", 1 }, | const char *name; | ||||
const char *desc; | |||||
uint8_t flags; | |||||
}; | |||||
static struct sensor_data sensor_list[] = { | |||||
{"adt7461", "ADT7461 Thernal Sensor Information", | |||||
REMOTE_TEMP_DOUBLE_REG}, | |||||
{"tmp461", "TMP461 Thernal Sensor Information", | |||||
LOCAL_TEMP_DOUBLE_REG | REMOTE_TEMP_DOUBLE_REG} | |||||
}; | |||||
static struct ofw_compat_data device_compat_data[] = { | |||||
{"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(generic_thermal_sensor_driver, iicbus, temp_sensor_driver, 0, 0); | ||||
IICBUS_FDT_PNP_INFO(tmp461_compat_data); | IICBUS_FDT_PNP_INFO(device_compat_data); | ||||
static int | static int | ||||
tmp461_attach(device_t dev) | temp_sensor_attach(device_t dev) | ||||
{ | { | ||||
struct sysctl_oid *sensor_root_oid; | struct sysctl_oid *sensor_root_oid; | ||||
struct sensor_data *local_dev; | |||||
struct sysctl_ctx_list *ctx; | struct sysctl_ctx_list *ctx; | ||||
uint8_t data; | |||||
local_dev = (struct sensor_data *) | |||||
ofw_bus_search_compatible(dev, device_compat_data)->ocd_data; | |||||
ctx = device_get_sysctl_ctx(dev); | ctx = device_get_sysctl_ctx(dev); | ||||
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"); | LOCAL_MEASURE, temp_sensor_sysctl, "IK1", local_dev->desc); | ||||
/* get status register */ | |||||
if (temp_sensor_read_1(dev, STATUS_REG, &data) != 0) | |||||
return (ENXIO); | |||||
if (!(data & STATUS_REG_TEMP_LOCAL)) | |||||
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(sensor_root_oid), OID_AUTO, | |||||
"remote_sensor", CTLTYPE_INT | CTLFLAG_RD, dev, | |||||
REMOTE_MEASURE, temp_sensor_sysctl, "IK1", local_dev->desc); | |||||
/* set standby mode */ | |||||
if (temp_sensor_read_1(dev, CONFIG_REG_R, &data) != 0) | |||||
return (ENXIO); | |||||
data |= CONFIG_REG_STANDBY_BIT; | |||||
if (temp_sensor_write_1(dev, CONFIG_REG_W, data) != 0) | |||||
return (ENXIO); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
tmp461_probe(device_t dev) | temp_sensor_probe(device_t dev) | ||||
{ | { | ||||
struct sensor_data *local_dev; | |||||
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) | local_dev = (struct sensor_data *) | ||||
ofw_bus_search_compatible(dev, device_compat_data)->ocd_data; | |||||
if (!local_dev) | |||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(dev, "TMP461 Thermal Sensor"); | device_set_desc(dev, local_dev->name); | ||||
return (BUS_PROBE_GENERIC); | return (BUS_PROBE_GENERIC); | ||||
} | } | ||||
static int | static int | ||||
tmp461_detach(device_t dev) | temp_sensor_detach(device_t dev) | ||||
{ | { | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
tmp461_read_1(device_t dev, uint8_t reg, uint8_t *data) | temp_sensor_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_WAIT); | ||||
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) | temp_sensor_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_WAIT); | ||||
error = tmp461_read_1(dev, TMP461_CONFIG_REG, &data); | |||||
if (error != 0) | if (error != 0) | ||||
device_printf(dev, "Failed to write to device\n"); | |||||
return (error); | |||||
} | |||||
static int | |||||
temp_sensor_read_temp(device_t dev, int32_t *temp, bool remote_measure) | |||||
{ | |||||
struct sensor_data *local_dev; | |||||
uint8_t data, offset; | |||||
int error; | |||||
local_dev = (struct sensor_data *) | |||||
ofw_bus_search_compatible(dev, device_compat_data)->ocd_data; | |||||
error = temp_sensor_read_1(dev, CONVERSION_RATE_REG, &data); | |||||
if (error != 0) | |||||
return (ENXIO); | return (ENXIO); | ||||
extended_mode = data & TMP461_CONFIG_REG_TEMP_RANGE_BIT; | /* trigger sample*/ | ||||
error = temp_sensor_write_1(dev, ONESHOT_REG, 0xFF); | |||||
if (error != 0) | |||||
return (ENXIO); | |||||
/* read temp MSB */ | /* wait for conversion time */ | ||||
error = tmp461_read_1(dev, TMP461_LOCAL_TEMP_REG_MSB, &data); | DELAY(SENSOR_MAX_CONV_TIME/(1UL<<data)); | ||||
/* read config register offset */ | |||||
error = temp_sensor_read_1(dev, CONFIG_REG_R, &data); | |||||
if (error != 0) | if (error != 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
*temp = signed_extend32(data, TMP461_TEMP_LSB, 8) - | offset = (data & CONFIG_REG_TEMP_RANGE_BIT ? | ||||
(extended_mode ? TMP461_EXTENDED_TEMP_MODIFIER : 0); | EXTENDED_MODE_TEMP_OFFSET : 0); | ||||
error = tmp461_read_1(dev, TMP461_LOCAL_TEMP_REG_LSB, &data); | if (remote_measure == LOCAL_MEASURE) { | ||||
error = temp_sensor_read_1(dev, LOCAL_TEMP_REG_MSB, &data); | |||||
if (error != 0) | if (error != 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
*temp = (((*temp << 4) | (data >> 4)) + TMP461_C_TO_K_FIX) >> 4; | data -= offset; | ||||
*temp = signed_extend32(data, 0, 8) << 4; | |||||
if (local_dev->flags & LOCAL_TEMP_DOUBLE_REG) { | |||||
error = temp_sensor_read_1(dev, | |||||
LOCAL_TEMP_REG_LSB, &data); | |||||
if (error != 0) | |||||
return (ENXIO); | |||||
*temp |= data >> 4; | |||||
} | |||||
} else { | |||||
error = temp_sensor_read_1(dev, REMOTE_TEMP_REG_MSB, &data); | |||||
if (error != 0) | |||||
return (ENXIO); | |||||
data -= offset; | |||||
*temp = signed_extend32(data, 0, 8) << 4; | |||||
if (local_dev->flags & REMOTE_TEMP_DOUBLE_REG) { | |||||
error = temp_sensor_read_1(dev, | |||||
REMOTE_TEMP_REG_LSB, &data); | |||||
if (error != 0) | |||||
return (ENXIO); | |||||
*temp |= data >> 4; | |||||
} | |||||
} | |||||
*temp = (((*temp + TEMP_C_TO_K_FIX) * 10) >> 4); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
tmp461_sensor_sysctl(SYSCTL_HANDLER_ARGS) | temp_sensor_sysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
device_t dev; | device_t dev; | ||||
int32_t temp; | int32_t temp; | ||||
int error; | int error; | ||||
bool mode; | |||||
dev = arg1; | dev = arg1; | ||||
mode = arg2; | |||||
error = tmp461_read_temp(dev, &temp); | error = temp_sensor_read_temp(dev, &temp, mode); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
return (sysctl_handle_int(oidp, &temp, 0, req)); | return (sysctl_handle_int(oidp, &temp, 0, req)); | ||||
} | } | ||||