Files
OpenCellular/driver/temp_sensor/tmp006.c
Vic Yang ffac23c0ea Add cprints() and ccprints()
Our code base contains a lot of debug messages in this pattern:
  CPRINTF("[%T xxx]\n") or ccprintf("[%T xxx]\n")
The strings are taking up spaces in the EC binaries, so let's refactor
this by adding cprints() and ccprints().

cprints() is just like cprintf(), except that it adds the brackets
and the timestamp. ccprints() is equivalent to cprints(CC_CONSOLE, ...)

This saves us hundreds of bytes in EC binaries.

BUG=chromium:374575
TEST=Build and check flash size
BRANCH=None

Change-Id: Ifafe8dc1b80e698b28ed42b70518c7917b49ee51
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/200490
Reviewed-by: Randall Spangler <rspangler@chromium.org>
2014-05-21 20:32:17 +00:00

467 lines
11 KiB
C

/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* TMP006 temperature sensor module for Chrome EC */
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "i2c.h"
#include "math.h"
#include "task.h"
#include "temp_sensor.h"
#include "tmp006.h"
#include "util.h"
/* Console output macros */
#define CPUTS(outstr) cputs(CC_THERMAL, outstr)
#define CPRINTS(format, args...) cprints(CC_THERMAL, format, ## args)
/* Constants for calculating target object temperatures */
static const float A1 = 1.75e-3f;
static const float A2 = -1.678e-5f;
static const float B0 = -2.94e-5f;
static const float B1 = -5.7e-7f;
static const float B2 = 4.63e-9f;
static const float C2 = 13.4f;
/* Defined in board_temp_sensor.c. */
extern const struct tmp006_t tmp006_sensors[TMP006_COUNT];
/* Flags for tdata->fail */
#define FAIL_INIT (1 << 0) /* Just initialized */
#define FAIL_POWER (1 << 1) /* Sensor not powered */
#define FAIL_I2C (1 << 2) /* I2C communication error */
#define FAIL_NOT_READY (1 << 3) /* Data not ready */
struct tmp006_data_t {
int v; /* Object voltage */
int t[4]; /* Circular buffer of last four die temperatures */
int tidx; /* Index of the current value in t[] */
int fail; /* Fail flags; non-zero if last read failed */
float s0; /* Sensitivity factor */
float b0, b1, b2; /* Coefficients for self-heating correction */
};
static struct tmp006_data_t tmp006_data[TMP006_COUNT];
/**
* Check if sensor has power
*
* @param idx Sensor index
*
* @return non-zero if sensor has power.
*/
static int tmp006_has_power(int idx)
{
#ifdef CONFIG_TEMP_SENSOR_POWER_GPIO
return gpio_get_level(CONFIG_TEMP_SENSOR_POWER_GPIO);
#else
return 1;
#endif
}
static int tmp006_read_die_temp(const struct tmp006_data_t *tdata,
int *temp_ptr)
{
if (tdata->fail)
return EC_ERROR_UNKNOWN;
/* Return previous die temperature */
*temp_ptr = tdata->t[(tdata->tidx - 1) & 0x3] / 100;
return EC_SUCCESS;
}
/**
* Calculate the remote object temperature.
*
* @param tdie_i Die temperature in 1/100 K.
* @param vobj_i Voltage read from register 0. In nV.
* @param tdata TMP006 data for this sensor.
*
* @return Object temperature in 1/100 K.
*/
static int tmp006_calculate_object_temp(int tdie_i, int vobj_i,
const struct tmp006_data_t *tdata)
{
float tdie, vobj;
float tx, s, vos, vx, fv, tobj, t4;
int tobj_i;
tdie = (float)tdie_i * 1e-2f;
vobj = (float)vobj_i * 1e-9f;
/* Calculate according to TMP006 users guide. */
tx = tdie - 298.15f;
/* s is the sensitivity */
s = tdata->s0 * (1.0f + A1 * tx + A2 * tx * tx);
/* vos is the offset voltage */
vos = tdata->b0 + tdata->b1 * tx + tdata->b2 * tx * tx;
vx = vobj - vos;
/* fv is Seebeck coefficient f(vobj) */
fv = vx + C2 * vx * vx;
t4 = tdie * tdie * tdie * tdie + fv / s;
tobj = sqrtf(sqrtf(t4));
tobj_i = (int32_t)(tobj * 100.0f);
return tobj_i;
}
/**
* Apply TMP006 temporal correction.
*
* @param t1-t4 Four die temperature readings separated by 1s in 1/100K.
* @param vobj Voltage read from register 0, in nV.
*
* @return Corrected object voltage in 1/100K.
*/
static int tmp006_correct_object_voltage(int t1, int t2, int t3, int t4,
int vobj)
{
int tslope = 3 * t1 + t2 - t3 - 3 * t4;
return vobj + 296 * tslope;
}
static int tmp006_read_object_temp(const struct tmp006_data_t *tdata,
int *temp_ptr)
{
int pidx = (tdata->tidx - 1) & 0x3;
int t = tdata->t[pidx];
int v = tdata->v;
if (tdata->fail)
return EC_ERROR_UNKNOWN;
if (!tdata->s0)
return EC_ERROR_NOT_CALIBRATED;
v = tmp006_correct_object_voltage(
t,
tdata->t[(pidx + 3) & 3],
tdata->t[(pidx + 2) & 3],
tdata->t[(pidx + 1) & 3],
v);
*temp_ptr = tmp006_calculate_object_temp(t, v, tdata) / 100;
return EC_SUCCESS;
}
static int tmp006_poll_sensor(int sensor_id)
{
struct tmp006_data_t *tdata = tmp006_data + sensor_id;
int traw, t;
int vraw, v;
int rv;
int addr = tmp006_sensors[sensor_id].addr;
int idx;
if (!tmp006_has_power(sensor_id)) {
tdata->fail |= FAIL_POWER;
return EC_ERROR_UNKNOWN;
}
/*
* If sensor has just initialized and/or has lost power, wait for
* data ready; otherwise, we read garbage data.
*/
if (tdata->fail && (FAIL_POWER | FAIL_INIT)) {
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, &v);
if (rv) {
tdata->fail |= FAIL_I2C;
return EC_ERROR_UNKNOWN;
} else if (!(v & 0x80)) {
tdata->fail |= FAIL_NOT_READY;
return EC_ERROR_UNKNOWN;
}
}
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
if (rv) {
tdata->fail |= FAIL_I2C;
return EC_ERROR_UNKNOWN;
}
/* Convert temperature from raw to 1/100 K */
t = ((int)(int16_t)traw * 100) / 128 + 27300;
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
if (rv) {
tdata->fail |= FAIL_I2C;
return EC_ERROR_UNKNOWN;
}
/* Convert voltage from raw to nV */
v = ((int)(int16_t)vraw * 15625) / 100;
/*
* If last read failed, set the entire temperature history to the
* current temperature. This keeps us from making inaccurate temporal
* corrections based on stale data.
*/
if (tdata->fail) {
for (idx = 0; idx < 4; idx++)
tdata->t[idx] = t;
} else {
idx = tdata->tidx;
tdata->t[idx] = t;
tdata->tidx = (idx + 1) & 3;
}
tdata->v = v;
tdata->fail = 0;
return EC_SUCCESS;
}
int tmp006_get_val(int idx, int *temp_ptr)
{
/*
* Note: idx is a thermal sensor index, where the top N-1 bits are the
* TMP006 index and the bottom bit is (0=die, 1=remote).
*/
int tidx = idx >> 1;
const struct tmp006_data_t *tdata = tmp006_data + tidx;
if (tdata->fail & FAIL_POWER) {
/*
* Sensor isn't powered, or hasn't successfully provided data
* since being powered. Keep reporting not-powered until
* we get good data (which will clear FAIL_POWER) or there is
* an I2C error.
*/
return (tdata->fail & FAIL_I2C) ? EC_ERROR_UNKNOWN :
EC_ERROR_NOT_POWERED;
}
/* Check the low bit to determine which temperature to read. */
if ((idx & 0x1) == 0)
return tmp006_read_die_temp(tdata, temp_ptr);
else
return tmp006_read_object_temp(tdata, temp_ptr);
}
/*****************************************************************************/
/* Hooks */
static void tmp006_poll(void)
{
int i;
for (i = 0; i < TMP006_COUNT; ++i)
tmp006_poll_sensor(i);
}
DECLARE_HOOK(HOOK_SECOND, tmp006_poll, HOOK_PRIO_TEMP_SENSOR);
static void tmp006_init(void)
{
int i;
for (i = 0; i < TMP006_COUNT; ++i) {
struct tmp006_data_t *tdata = tmp006_data + i;
/* Report error until we actually read the sensor */
tdata->fail = FAIL_INIT;
/* Use defaults for Bn params */
tdata->b0 = B0;
tdata->b1 = B1;
tdata->b2 = B2;
}
}
DECLARE_HOOK(HOOK_INIT, tmp006_init, HOOK_PRIO_DEFAULT);
/*****************************************************************************/
/* Host commands */
int tmp006_get_calibration(struct host_cmd_handler_args *args)
{
const struct ec_params_tmp006_get_calibration *p = args->params;
struct ec_response_tmp006_get_calibration *r = args->response;
const struct tmp006_data_t *tdata;
if (p->index >= TMP006_COUNT)
return EC_RES_INVALID_PARAM;
tdata = tmp006_data + p->index;
r->s0 = tdata->s0;
r->b0 = tdata->b0;
r->b1 = tdata->b1;
r->b2 = tdata->b2;
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_TMP006_GET_CALIBRATION,
tmp006_get_calibration,
EC_VER_MASK(0));
int tmp006_set_calibration(struct host_cmd_handler_args *args)
{
const struct ec_params_tmp006_set_calibration *p = args->params;
struct tmp006_data_t *tdata;
if (p->index >= TMP006_COUNT)
return EC_RES_INVALID_PARAM;
tdata = tmp006_data + p->index;
tdata->s0 = p->s0;
tdata->b0 = p->b0;
tdata->b1 = p->b1;
tdata->b2 = p->b2;
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_TMP006_SET_CALIBRATION,
tmp006_set_calibration,
EC_VER_MASK(0));
int tmp006_get_raw(struct host_cmd_handler_args *args)
{
const struct ec_params_tmp006_get_raw *p = args->params;
struct ec_response_tmp006_get_raw *r = args->response;
const struct tmp006_data_t *tdata;
if (p->index >= TMP006_COUNT)
return EC_RES_INVALID_PARAM;
tdata = tmp006_data + p->index;
r->v = tdata->v;
r->t = tdata->t[(tdata->tidx - 1) & 0x3];
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_TMP006_GET_RAW,
tmp006_get_raw,
EC_VER_MASK(0));
/*****************************************************************************/
/* Console commands */
/**
* Print temperature info for a sensor; used by console command.
*/
static int tmp006_print(int idx)
{
int vraw, v;
int traw, t;
int rv;
int d;
int addr = tmp006_sensors[idx].addr;
ccprintf("Debug data from %s:\n", tmp006_sensors[idx].name);
if (!tmp006_has_power(idx)) {
ccputs("Sensor powered off.\n");
return EC_ERROR_UNKNOWN;
}
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xfe, &d);
if (rv)
return rv;
ccprintf(" Manufacturer ID: 0x%04x\n", d);
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xff, &d);
ccprintf(" Device ID: 0x%04x\n", d);
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, &d);
ccprintf(" Config: 0x%04x\n", d);
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
v = ((int)(int16_t)vraw * 15625) / 100;
ccprintf(" Voltage: 0x%04x = %d nV\n", vraw, v);
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
t = ((int)(int16_t)traw * 100) / 128;
ccprintf(" Temperature: 0x%04x = %d.%02d C\n",
traw, t / 100, t > 0 ? t % 100 : 100 - (t % 100));
return EC_SUCCESS;
}
static int command_sensor_info(int argc, char **argv)
{
int i;
int rv, rv1;
rv1 = EC_SUCCESS;
for (i = 0; i < TMP006_COUNT; i++) {
rv = tmp006_print(i);
if (rv != EC_SUCCESS)
rv1 = rv;
cflush();
}
return rv1;
}
DECLARE_CONSOLE_COMMAND(tmp006, command_sensor_info,
NULL,
"Print TMP006 sensors",
NULL);
static int command_t6cal(int argc, char **argv)
{
struct tmp006_data_t *tdata;
char *e;
int v;
int i;
if (argc < 2) {
ccprintf("# Name S0 b0"
" b1 b2\n");
for (i = 0; i < TMP006_COUNT; i++) {
tdata = tmp006_data + i;
ccprintf("%d %-11s"
"%7de-17 %7de-8 %7de-10 %7de-12\n",
i, tmp006_sensors[i].name,
(int)(tdata->s0 * 1e17f),
(int)(tdata->b0 * 1e8f),
(int)(tdata->b1 * 1e10f),
(int)(tdata->b2 * 1e12f));
}
return EC_SUCCESS;
}
if (argc != 4)
return EC_ERROR_PARAM_COUNT;
i = strtoi(argv[1], &e, 0);
if (*e || i < 0 || i >= TMP006_COUNT)
return EC_ERROR_PARAM1;
tdata = tmp006_data + i;
v = strtoi(argv[3], &e, 0);
if (*e)
return EC_ERROR_PARAM3;
if (!strcasecmp(argv[2], "s0"))
tdata->s0 = (float)v * 1e-17f;
else if (!strcasecmp(argv[2], "b0"))
tdata->b0 = (float)v * 1e-8f;
else if (!strcasecmp(argv[2], "b1"))
tdata->b1 = (float)v * 1e-10f;
else if (!strcasecmp(argv[2], "b2"))
tdata->b2 = (float)v * 1e-12f;
else
return EC_ERROR_PARAM2;
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(t6cal, command_t6cal,
"[<index> <coeff_name> <radix>]",
"Set/print TMP006 calibration",
NULL);