mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-28 10:45:22 +00:00
Currently if ENABLE_VS is off, each time we poll TMP006 sensors, it takes 1 second for each sensor to return ERROR. Checking for power before polling help to reduce the time spent on temperature polling. Signed-off-by: Vic Yang <victoryang@chromium.org> BUG=chrome-os-partner:8275 TEST=Manual test Change-Id: I2da83f09cccc331074f9bb5483b2ba92c0865742
294 lines
7.2 KiB
C
294 lines
7.2 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 "board.h"
|
|
#include "config.h"
|
|
#include "console.h"
|
|
#include "fpu.h"
|
|
#include "gpio.h"
|
|
#include "i2c.h"
|
|
#include "math.h"
|
|
#include "task.h"
|
|
#include "temp_sensor.h"
|
|
#include "tmp006.h"
|
|
#include "uart.h"
|
|
#include "util.h"
|
|
|
|
/* Defined in board_temp_sensor.c. */
|
|
extern const struct tmp006_t tmp006_sensors[TMP006_COUNT];
|
|
|
|
struct tmp006_data_t {
|
|
/* Object voltage */
|
|
int v;
|
|
/* The last four die temperature value. Used as a circular buffer. */
|
|
int t[4];
|
|
/* The index of the current value in the dir temperature array. */
|
|
int tidx;
|
|
/* Fail bit: 1 if last read fail. 0 if ok. */
|
|
int fail;
|
|
};
|
|
|
|
static struct tmp006_data_t tmp006_data[TMP006_COUNT];
|
|
|
|
static int tmp006_read_die_temp(int idx)
|
|
{
|
|
int pidx = (tmp006_data[idx].tidx - 1) & 0x3;
|
|
if (tmp006_data[idx].fail == 1)
|
|
return -1;
|
|
return tmp006_data[idx].t[pidx] / 100;
|
|
}
|
|
|
|
/* Calculate the remote object temperature.
|
|
* Parameters:
|
|
* Tdie: Die temperature in 1/100 K.
|
|
* Vobj: Voltage read from register 0. In nV.
|
|
* S0: Sensitivity factor in 1e-17.
|
|
* Return:
|
|
* Object temperature in 1/100 K.
|
|
*/
|
|
static int tmp006_calculate_object_temp(int Tdie_i, int Vobj_i, int S0_i)
|
|
{
|
|
#ifdef CONFIG_FPU
|
|
float Tdie, Vobj, S0;
|
|
float Tx, S, Vos, Vx, fv, Tobj, T4;
|
|
int Tobj_i;
|
|
|
|
enable_fpu();
|
|
|
|
Tdie = (float)Tdie_i * 1e-2f;
|
|
Vobj = (float)Vobj_i * 1e-9f;
|
|
S0 = (float)S0_i * 1e-17f;
|
|
|
|
/* Calculate according to TMP006 users guide. */
|
|
Tx = Tdie - 298.15f;
|
|
/* S is the sensitivity */
|
|
S = S0 * (1.0f + 1.75e-3f * Tx - 1.678e-5f * Tx * Tx);
|
|
/* Vos is the offset voltage */
|
|
Vos = -2.94e-5f - 5.7e-7f * Tx + 4.63e-9f * Tx * Tx;
|
|
Vx = Vobj - Vos;
|
|
/* fv is Seebeck coefficient f(Vobj) */
|
|
fv = Vx + 13.4f * Vx * Vx;
|
|
|
|
T4 = Tdie * Tdie * Tdie * Tdie + fv / S;
|
|
Tobj = sqrtf(sqrtf(T4));
|
|
Tobj_i = (int32_t)(Tobj * 100.0f);
|
|
|
|
disable_fpu(Tobj_i);
|
|
|
|
return Tobj_i;
|
|
#else
|
|
/* This is the fixed-point version of object temperature calculation.
|
|
* Should be accurate but it is hard to prevent and debug
|
|
* overflow/underflow problem. Only use this version if there is no
|
|
* FPU support.
|
|
* Division is delayed when possible to preserve precision, but should
|
|
* not cause overflow.
|
|
* Assuming Tdie is between 200K and 400K, and S0 between 3e-14 and
|
|
* 9e-14, the maximum value during the calculation should be less than
|
|
* (1 << 30), which fits in int32_t.
|
|
*/
|
|
int32_t Tx, S19, Vos, Vx, fv9, ub, lb;
|
|
|
|
Tx = Tdie - 29815;
|
|
/* S19 is the sensitivity multipled by 1e19 */
|
|
S19 = S0 * (100000 + 175 * Tx / 100 -
|
|
1678 * Tx / 100 * Tx / 100000) / 1000;
|
|
/* Vos is the offset voltage in nV */
|
|
Vos = -29400 - 570 * Tx / 100 + 463 * Tx / 100 * Tx / 10000;
|
|
Vx = Vobj - Vos;
|
|
/* fv9 is Seebeck coefficient f(Vobj) multipled by 1e9 */
|
|
fv9 = Vx + 134 * Vx / 100000 * Vx / 100000;
|
|
|
|
/* The last step in the calculation involves square root, so we use
|
|
* binary search.
|
|
* Assuming the object temperature is between 200K and 400K, the search
|
|
* should take at most 14 iterations.
|
|
*/
|
|
ub = 40000;
|
|
lb = 20000;
|
|
while (lb != ub) {
|
|
int32_t t, rhs, lhs;
|
|
|
|
t = (ub + lb) / 2;
|
|
lhs = t / 100 * t / 10000 * t / 10000 * (S19/100) / 1000 * t;
|
|
rhs = Tdie / 100 * Tdie / 10000 * Tdie / 10000 * (S19/100) /
|
|
1000 * Tdie + fv9 * 1000;
|
|
if (lhs > rhs)
|
|
ub = t;
|
|
else
|
|
lb = t + 1;
|
|
}
|
|
|
|
return ub;
|
|
#endif /* CONFIG_FPU */
|
|
}
|
|
|
|
/* Temporal Correction
|
|
* Parameters:
|
|
* T1-T4: Four die temperature readings separated by 1s in 1/100K.
|
|
* v: 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(int idx)
|
|
{
|
|
int pidx = (tmp006_data[idx].tidx - 1) & 0x3;
|
|
int t = tmp006_data[idx].t[pidx];
|
|
int v = tmp006_data[idx].v;
|
|
|
|
if (tmp006_data[idx].fail)
|
|
return -1;
|
|
|
|
v = tmp006_correct_object_voltage(
|
|
t,
|
|
tmp006_data[idx].t[(pidx + 3) & 3],
|
|
tmp006_data[idx].t[(pidx + 2) & 3],
|
|
tmp006_data[idx].t[(pidx + 1) & 3],
|
|
v);
|
|
|
|
/* TODO: Calibrate the sensitivity factor. */
|
|
return tmp006_calculate_object_temp(t, v, 6400) / 100;
|
|
}
|
|
|
|
static int tmp006_poll_sensor(int sensor_id)
|
|
{
|
|
int traw, t;
|
|
int vraw, v;
|
|
int rv;
|
|
int addr = tmp006_sensors[sensor_id].addr;
|
|
int idx;
|
|
|
|
/* TODO: For now, all TMP006 sensors are powered by VS. Modify this
|
|
* if we have different design.
|
|
*/
|
|
if (gpio_get_level(GPIO_PGOOD_1_8VS) == 0) {
|
|
tmp006_data[sensor_id].fail = 1;
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
|
|
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
|
|
if (rv) {
|
|
tmp006_data[sensor_id].fail = 1;
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
t = ((int)(int16_t)traw * 100) / 128 + 27300;
|
|
|
|
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
|
|
if (rv) {
|
|
tmp006_data[sensor_id].fail = 1;
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
v = ((int)(int16_t)vraw * 15625) / 100;
|
|
|
|
idx = tmp006_data[sensor_id].tidx;
|
|
tmp006_data[sensor_id].t[idx] = t;
|
|
tmp006_data[sensor_id].v = v;
|
|
tmp006_data[sensor_id].tidx = (idx + 1) & 3;
|
|
tmp006_data[sensor_id].fail = 0;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int tmp006_print(int idx)
|
|
{
|
|
int vraw, v;
|
|
int traw, t;
|
|
int rv;
|
|
int d;
|
|
int addr = tmp006_sensors[idx].addr;
|
|
|
|
|
|
uart_printf("Debug data from %s:\n", tmp006_sensors[idx].name);
|
|
|
|
/* TODO: For now, all TMP006 sensors are powered by VS. Modify this
|
|
* if we have different design.
|
|
*/
|
|
if (gpio_get_level(GPIO_PGOOD_1_8VS) == 0) {
|
|
uart_puts("Sensor powered off.\n");
|
|
return EC_ERROR_UNKNOWN;
|
|
}
|
|
|
|
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xfe, &d);
|
|
if (rv)
|
|
return rv;
|
|
uart_printf(" Manufacturer ID: 0x%04x\n", d);
|
|
|
|
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0xff, &d);
|
|
uart_printf(" Device ID: 0x%04x\n", d);
|
|
|
|
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, &d);
|
|
uart_printf(" Config: 0x%04x\n", d);
|
|
|
|
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
|
|
v = ((int)(int16_t)vraw * 15625) / 100;
|
|
uart_printf(" 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;
|
|
uart_printf(" Temperature: 0x%04x = %d.%02d C\n",
|
|
traw, t / 100, t > 0 ? t % 100 : 100 - (t % 100));
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
|
|
int tmp006_get_val(int idx)
|
|
{
|
|
/* Check the low bit to determine which temperature to read. */
|
|
if ((idx & 0x1) == 0)
|
|
return tmp006_read_die_temp(idx >> 1);
|
|
else
|
|
return tmp006_read_object_temp(idx >> 1);
|
|
}
|
|
|
|
|
|
int tmp006_poll(void)
|
|
{
|
|
int i;
|
|
int rv;
|
|
int rv1 = EC_SUCCESS;
|
|
|
|
for (i = 0; i < TMP006_COUNT; ++i) {
|
|
rv = tmp006_poll_sensor(i);
|
|
if (rv != EC_SUCCESS)
|
|
rv1 = rv;
|
|
}
|
|
|
|
return rv1;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Console commands */
|
|
|
|
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;
|
|
uart_flush_output();
|
|
}
|
|
|
|
return rv1;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(tmp006, command_sensor_info);
|