mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 17:11:42 +00:00
Temperature polling and temporal correction
A temperature polling task is added to achieve temporal correction and also reduce the latency of reading temperature. Factor out sensor specific part to keep code clean. Signed-off-by: Vic Yang <victoryang@chromium.org> BUG=chrome-os-partner:7801 TEST=On link, 'temps' shows all temperature readings. Cover each sensor with hand and see object temperature rise. Compilation succeeded on bds/adv/daisy/discovery. Change-Id: I3c44c8b2e3ab2aa9ce640d3fc25e7fba56534b86
This commit is contained in:
@@ -25,7 +25,7 @@ CFLAGS_WARN=-Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
|
||||
CFLAGS_DEBUG= -g
|
||||
CFLAGS_INCLUDE=$(foreach i,$(includes),-I$(i) )
|
||||
CFLAGS_DEFINE=-DOUTDIR=$(out) -DCHIP=$(CHIP) -DTASKFILE=$(PROJECT).tasklist \
|
||||
-DBOARD=$(BOARD) -DBOARD_$(BOARD) -DCORE=$(CORE)
|
||||
-DBOARD=$(BOARD) -DBOARD_$(BOARD) -DCORE=$(CORE) -DCHIP_$(CHIP)
|
||||
CPPFLAGS=$(CFLAGS_DEFINE) $(CFLAGS_INCLUDE)
|
||||
CFLAGS=$(CPPFLAGS) $(CFLAGS_CPU) $(CFLAGS_DEBUG) $(CFLAGS_WARN)
|
||||
BUILD_CFLAGS=$(CPPFLAGS) -O3 $(CFLAGS_DEBUG) $(CFLAGS_WARN)
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifndef __BOARD_H
|
||||
#define __BOARD_H
|
||||
|
||||
/* Config flags */
|
||||
#define CONFIG_TMP006
|
||||
|
||||
/* 66.667 Mhz clock frequency */
|
||||
#define CPU_CLOCK 66666667
|
||||
|
||||
@@ -125,10 +128,13 @@ enum gpio_signal {
|
||||
enum temp_sensor_id {
|
||||
TEMP_SENSOR_EC_INTERNAL = 0, /* EC internal temperature sensor */
|
||||
TEMP_SENSOR_CASE_DIE,
|
||||
TEMP_SENSOR_OBJECT,
|
||||
|
||||
TEMP_SENSOR_COUNT
|
||||
};
|
||||
|
||||
#define TMP006_COUNT 1
|
||||
|
||||
void configure_board(void);
|
||||
|
||||
#endif /* __BOARD_H */
|
||||
|
||||
@@ -9,14 +9,22 @@
|
||||
#include "chip_temp_sensor.h"
|
||||
#include "board.h"
|
||||
#include "i2c.h"
|
||||
#include "tmp006.h"
|
||||
#include "util.h"
|
||||
|
||||
#define TEMP_CASE_DIE_REG_ADDR ((0x40 << 1) | I2C_FLAG_BIG_ENDIAN)
|
||||
#define TEMP_CASE_DIE_ADDR \
|
||||
TMP006_ADDR(I2C_PORT_THERMAL, TEMP_CASE_DIE_REG_ADDR)
|
||||
|
||||
/* Temperature sensors data. Must be in the same order as enum
|
||||
* temp_sensor_id.
|
||||
*/
|
||||
const struct temp_sensor_t temp_sensors[TEMP_SENSOR_COUNT] = {
|
||||
{"ECInternal", TEMP_SENSOR_NO_ADDR,
|
||||
chip_temp_sensor_read, TEMP_SENSOR_NO_PRINT},
|
||||
{"CaseDie", TEMP_CASE_DIE_ADDR,
|
||||
temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print}
|
||||
{"ECInternal", chip_temp_sensor_get_val, 0},
|
||||
{"CaseDie", tmp006_get_val, 0},
|
||||
{"Object", tmp006_get_val, 0},
|
||||
};
|
||||
|
||||
const struct tmp006_t tmp006_sensors[TMP006_COUNT] = {
|
||||
{"TMP006", TEMP_CASE_DIE_ADDR},
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#define CONFIG_TASK_LIST \
|
||||
TASK(WATCHDOG, watchdog_task, NULL) \
|
||||
TASK(TEMPSENSOR, temp_sensor_task, NULL) \
|
||||
TASK(KEYSCAN, keyboard_scan_task, NULL) \
|
||||
TASK(POWERBTN, power_button_task, NULL) \
|
||||
TASK(X86POWER, x86_power_task, NULL) \
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
/* Optional features */
|
||||
#define CONFIG_PECI
|
||||
#define CONFIG_TMP006
|
||||
|
||||
/* 66.667 Mhz clock frequency */
|
||||
#define CPU_CLOCK 66666667
|
||||
@@ -170,12 +171,20 @@ enum gpio_signal {
|
||||
enum temp_sensor_id {
|
||||
/* I2C die temperature sensor near CPU */
|
||||
TEMP_SENSOR_I2C_DIE_NEAR_CPU = 0,
|
||||
/* PCH temperature sensor */
|
||||
/* I2C object temperature sensor near CPU */
|
||||
TEMP_SENSOR_I2C_CPU,
|
||||
/* I2C die temperature sensor near PCH */
|
||||
TEMP_SENSOR_I2C_DIE_NEAR_PCH,
|
||||
/* DDR memory temperature sensor */
|
||||
/* I2C object temperature sensor near PCH */
|
||||
TEMP_SENSOR_I2C_PCH,
|
||||
/* I2C die temperature sensor near DDR memory */
|
||||
TEMP_SENSOR_I2C_DIE_NEAR_DDR,
|
||||
/* Battery charger temperature sensor */
|
||||
/* I2C object temperature sensor near CPU */
|
||||
TEMP_SENSOR_I2C_DDR,
|
||||
/* I2C die temperature sensor near battery charger */
|
||||
TEMP_SENSOR_I2C_DIE_NEAR_CHARGER,
|
||||
/* I2C object temperature sensor near CPU */
|
||||
TEMP_SENSOR_I2C_CHARGER,
|
||||
/* EC internal temperature sensor */
|
||||
TEMP_SENSOR_EC_INTERNAL,
|
||||
/* CPU die temperature via PECI */
|
||||
@@ -186,6 +195,9 @@ enum temp_sensor_id {
|
||||
TEMP_SENSOR_COUNT
|
||||
};
|
||||
|
||||
/* The number of TMP006 sensor chips on the board. */
|
||||
#define TMP006_COUNT 4
|
||||
|
||||
void configure_board(void);
|
||||
|
||||
#endif /* __BOARD_H */
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include "board.h"
|
||||
#include "i2c.h"
|
||||
#include "peci.h"
|
||||
#include "tmp006.h"
|
||||
#include "util.h"
|
||||
|
||||
#define TEMP_CPU_REG_ADDR ((0x40 << 1) | I2C_FLAG_BIG_ENDIAN)
|
||||
#define TEMP_PCH_REG_ADDR ((0x41 << 1) | I2C_FLAG_BIG_ENDIAN)
|
||||
@@ -25,16 +27,21 @@
|
||||
* temp_sensor_id.
|
||||
*/
|
||||
const struct temp_sensor_t temp_sensors[TEMP_SENSOR_COUNT] = {
|
||||
{"I2C_CPU", TEMP_CPU_ADDR,
|
||||
temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
|
||||
{"I2C_PCH", TEMP_PCH_ADDR,
|
||||
temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
|
||||
{"I2C_DDR", TEMP_DDR_ADDR,
|
||||
temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
|
||||
{"I2C_Charger", TEMP_CHARGER_ADDR,
|
||||
temp_sensor_tmp006_read_die_temp, temp_sensor_tmp006_print},
|
||||
{"ECInternal", TEMP_SENSOR_NO_ADDR,
|
||||
chip_temp_sensor_read, TEMP_SENSOR_NO_PRINT},
|
||||
{"PECI", TEMP_SENSOR_NO_ADDR,
|
||||
peci_temp_sensor_read, TEMP_SENSOR_NO_PRINT},
|
||||
{"I2C_CPU-Die", tmp006_get_val, 0},
|
||||
{"I2C_CPU-Object", tmp006_get_val, 1},
|
||||
{"I2C_PCH-Die", tmp006_get_val, 2},
|
||||
{"I2C_PCH-Object", tmp006_get_val, 3},
|
||||
{"I2C_DDR-Die", tmp006_get_val, 4},
|
||||
{"I2C_DDR-Object", tmp006_get_val, 5},
|
||||
{"I2C_Charger-Die", tmp006_get_val, 6},
|
||||
{"I2C_Charger-Object", tmp006_get_val, 7},
|
||||
{"ECInternal", chip_temp_sensor_get_val, 0},
|
||||
{"PECI", peci_temp_sensor_get_val, 0},
|
||||
};
|
||||
|
||||
const struct tmp006_t tmp006_sensors[TMP006_COUNT] = {
|
||||
{"CPU", TEMP_CPU_ADDR},
|
||||
{"PCH", TEMP_PCH_ADDR},
|
||||
{"DDR", TEMP_DDR_ADDR},
|
||||
{"Charger", TEMP_CHARGER_ADDR},
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
#define CONFIG_TASK_LIST \
|
||||
TASK(WATCHDOG, watchdog_task, NULL) \
|
||||
TASK(TEMPSENSOR, temp_sensor_task, NULL) \
|
||||
TASK(KEYSCAN, keyboard_scan_task, NULL) \
|
||||
TASK(POWERBTN, power_button_task, NULL) \
|
||||
TASK(X86POWER, x86_power_task, NULL) \
|
||||
|
||||
@@ -9,9 +9,18 @@
|
||||
#include "board.h"
|
||||
#include "temp_sensor.h"
|
||||
|
||||
int chip_temp_sensor_read(const struct temp_sensor_t* sensor)
|
||||
static int last_val;
|
||||
|
||||
int chip_temp_sensor_poll(void)
|
||||
{
|
||||
return adc_read_channel(ADC_CH_EC_TEMP);
|
||||
last_val = adc_read_channel(ADC_CH_EC_TEMP);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
int chip_temp_sensor_get_val(int idx)
|
||||
{
|
||||
return last_val;
|
||||
}
|
||||
|
||||
int chip_temp_sensor_init(void)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#define PECI_TD_FET_NS 25 /* Guess; TODO: what is real delay */
|
||||
#define PECI_TD_INT_NS 80
|
||||
|
||||
static int last_temp_val;
|
||||
|
||||
/* Configures the GPIOs for the PECI module. */
|
||||
static void configure_gpios(void)
|
||||
@@ -50,10 +51,21 @@ int peci_get_cpu_temp(void)
|
||||
return v >> 6;
|
||||
}
|
||||
|
||||
|
||||
int peci_temp_sensor_read(const struct temp_sensor_t* sensor)
|
||||
int peci_temp_sensor_poll(void)
|
||||
{
|
||||
return peci_get_cpu_temp();
|
||||
int val = peci_get_cpu_temp();
|
||||
|
||||
if (val > 0) {
|
||||
last_temp_val = val;
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
else
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
int peci_temp_sensor_get_val(int idx)
|
||||
{
|
||||
return last_temp_val;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -16,6 +16,7 @@ common-$(CONFIG_TASK_GAIAPOWER)+=gaia_power.o
|
||||
common-$(CONFIG_FLASH)+=flash_commands.o
|
||||
common-$(CONFIG_PWM)+=pwm_commands.o
|
||||
common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o temp_sensor_commands.o
|
||||
common-$(CONFIG_TMP006)+=tmp006.o
|
||||
common-$(CONFIG_LIGHTBAR)+=leds.o
|
||||
|
||||
# Board driver modules
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
#include "temp_sensor.h"
|
||||
#include "tmp006.h"
|
||||
#include "timer.h"
|
||||
#include "uart.h"
|
||||
#include "usb_charge.h"
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
#include "util.h"
|
||||
#include "console.h"
|
||||
#include "board.h"
|
||||
#include "peci.h"
|
||||
#include "tmp006.h"
|
||||
#include "task.h"
|
||||
#include "fpu.h"
|
||||
#include "math.h"
|
||||
#include "chip_temp_sensor.h"
|
||||
|
||||
/* Defined in board_temp_sensor.c. Must be in the same order as
|
||||
* in enum temp_sensor_id.
|
||||
@@ -27,171 +28,31 @@ int temp_sensor_read(enum temp_sensor_id id)
|
||||
if (id < 0 || id >= TEMP_SENSOR_COUNT)
|
||||
return -1;
|
||||
sensor = temp_sensors + id;
|
||||
return sensor->read(sensor);
|
||||
return sensor->read(sensor->idx);
|
||||
}
|
||||
|
||||
int temp_sensor_tmp006_read_die_temp(const struct temp_sensor_t* sensor)
|
||||
void poll_all_sensors(void)
|
||||
{
|
||||
int traw, t;
|
||||
int rv;
|
||||
int addr = sensor->addr;
|
||||
|
||||
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
|
||||
if (rv)
|
||||
return -1;
|
||||
t = (int)(int16_t)traw / 128;
|
||||
return t + 273;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
int temp_sensor_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;
|
||||
#ifdef CONFIG_TMP006
|
||||
tmp006_poll();
|
||||
#endif
|
||||
#ifdef CONFIG_PECI
|
||||
peci_temp_sensor_poll();
|
||||
#endif
|
||||
#ifdef CHIP_lm4
|
||||
chip_temp_sensor_poll();
|
||||
#endif
|
||||
}
|
||||
|
||||
int temp_sensor_tmp006_read_object_temp(const struct temp_sensor_t* sensor)
|
||||
void temp_sensor_task(void)
|
||||
{
|
||||
int traw, t;
|
||||
int vraw, v;
|
||||
int rv;
|
||||
int addr = sensor->addr;
|
||||
|
||||
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
|
||||
if (rv)
|
||||
return -1;
|
||||
t = (int)(int16_t)traw / 128 + 273;
|
||||
|
||||
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x00, &vraw);
|
||||
if (rv)
|
||||
return -1;
|
||||
v = ((int)(int16_t)vraw * 15625) / 100;
|
||||
|
||||
return temp_sensor_tmp006_calculate_object_temp(t * 100, v, 6400);
|
||||
while (1) {
|
||||
poll_all_sensors();
|
||||
/* Wait 1s */
|
||||
task_wait_msg(1000000);
|
||||
}
|
||||
}
|
||||
|
||||
void temp_sensor_tmp006_config(const struct temp_sensor_t* sensor)
|
||||
{
|
||||
int addr = sensor->addr;
|
||||
|
||||
/* Configure the sensor:
|
||||
* 0x7000 = bits 14:12 = continuous conversion
|
||||
* 0x0400 = bits 11:9 = ADC conversion rate (1/sec)
|
||||
* 0x0100 = bit 8 = DRDY pin enabled */
|
||||
|
||||
/* TODO: support shutdown mode for power-saving? */
|
||||
i2c_write16(TMP006_PORT(addr), TMP006_REG(addr), 0x02, 0x7500);
|
||||
}
|
||||
|
||||
int temp_sensor_tmp006_print(const struct temp_sensor_t* sensor)
|
||||
{
|
||||
int vraw, v;
|
||||
int traw, t;
|
||||
int rv;
|
||||
int d;
|
||||
int addr = sensor->addr;
|
||||
|
||||
uart_printf("Debug data from %s:\n", sensor->name);
|
||||
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;
|
||||
}
|
||||
/*****************************************************************************/
|
||||
/* Console commands */
|
||||
|
||||
@@ -221,65 +82,6 @@ static int command_temps(int argc, char **argv)
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(temps, command_temps);
|
||||
|
||||
static int command_sensor_info(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int rv, rv1;
|
||||
const struct temp_sensor_t* sensor;
|
||||
|
||||
rv1 = EC_SUCCESS;
|
||||
for (i = 0; i < TEMP_SENSOR_COUNT; ++i) {
|
||||
sensor = temp_sensors + i;
|
||||
if (sensor->print == TEMP_SENSOR_NO_PRINT)
|
||||
continue;
|
||||
rv = sensor->print(sensor);
|
||||
if (rv != EC_SUCCESS)
|
||||
rv1 = rv;
|
||||
}
|
||||
|
||||
return rv1;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(tempsinfo, command_sensor_info);
|
||||
|
||||
/* TMP006 object temperature calculation command.
|
||||
* TODO: This command is only for debugging. Remove it when temporal correciton
|
||||
* is done.
|
||||
*/
|
||||
static int command_sensor_remote(int argc, char **argv)
|
||||
{
|
||||
char *e;
|
||||
int32_t Td2, Vobj9, Sm03;
|
||||
|
||||
if (argc != 4) {
|
||||
uart_puts("Usage: tempcorrect <Tdie*100> <Vobj*10^9> <S0*10^11>\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
Td2 = strtoi(argv[1], &e, 0);
|
||||
if (e && *e) {
|
||||
uart_puts("Bad Tdie.\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
Vobj9 = strtoi(argv[2], &e, 0);
|
||||
if (e && *e) {
|
||||
uart_puts("Bad Vobj.\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
Sm03 = strtoi(argv[3], &e, 0);
|
||||
if (e && *e) {
|
||||
uart_puts("Bad S0.\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
uart_printf("%d\n",
|
||||
temp_sensor_tmp006_calculate_object_temp(Td2, Vobj9, Sm03));
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(tempremote, command_sensor_remote);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Initialization */
|
||||
|
||||
|
||||
253
common/tmp006.c
Normal file
253
common/tmp006.c
Normal file
@@ -0,0 +1,253 @@
|
||||
/* 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 "tmp006.h"
|
||||
#include "temp_sensor.h"
|
||||
#include "board.h"
|
||||
#include "uart.h"
|
||||
#include "util.h"
|
||||
#include "console.h"
|
||||
#include "task.h"
|
||||
#include "fpu.h"
|
||||
#include "math.h"
|
||||
#include "i2c.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;
|
||||
};
|
||||
|
||||
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;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
rv = i2c_read16(TMP006_PORT(addr), TMP006_REG(addr), 0x01, &traw);
|
||||
if (rv)
|
||||
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)
|
||||
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;
|
||||
|
||||
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);
|
||||
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 */
|
||||
|
||||
/* TMP006 object temperature calculation command.
|
||||
* TODO: This command is only for debugging. Remove it when temporal correciton
|
||||
* is done.
|
||||
*/
|
||||
static int command_sensor_remote(int argc, char **argv)
|
||||
{
|
||||
char *e;
|
||||
int32_t Td2, Vobj9, Sm03;
|
||||
|
||||
if (argc != 4) {
|
||||
uart_puts("Usage: tempcorrect <Tdie*100> <Vobj*10^9> <S0*10^11>\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
Td2 = strtoi(argv[1], &e, 0);
|
||||
if (e && *e) {
|
||||
uart_puts("Bad Tdie.\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
Vobj9 = strtoi(argv[2], &e, 0);
|
||||
if (e && *e) {
|
||||
uart_puts("Bad Vobj.\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
Sm03 = strtoi(argv[3], &e, 0);
|
||||
if (e && *e) {
|
||||
uart_puts("Bad S0.\n");
|
||||
return EC_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
uart_printf("%d\n",
|
||||
tmp006_calculate_object_temp(Td2, Vobj9, Sm03));
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(tempremote, command_sensor_remote);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return rv1;
|
||||
}
|
||||
DECLARE_CONSOLE_COMMAND(tmp006, command_sensor_info);
|
||||
@@ -10,10 +10,11 @@
|
||||
|
||||
struct temp_sensor_t;
|
||||
|
||||
/* Temperature reading function. Input pointer to a sensor in temp_sensors.
|
||||
* Return temperature in K.
|
||||
*/
|
||||
int chip_temp_sensor_read(const struct temp_sensor_t* sensor);
|
||||
/* Temperature polling function. */
|
||||
int chip_temp_sensor_poll(void);
|
||||
|
||||
/* Temperature reading function. Return temperature in K. */
|
||||
int chip_temp_sensor_get_val(int idx);
|
||||
|
||||
int chip_temp_sensor_init(void);
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ int peci_get_cpu_temp(void);
|
||||
/* Reads the CPU temperature sensor via PECI. This interface is for the
|
||||
* temperature sensor module. Returns the temperature in degrees K, or -1 if
|
||||
* error. */
|
||||
int peci_temp_sensor_read(const struct temp_sensor_t *sensor);
|
||||
int peci_temp_sensor_get_val(int idx);
|
||||
|
||||
/* Temperature polling of CPU temperature sensor via PECI. */
|
||||
int peci_temp_sensor_poll(void);
|
||||
|
||||
#endif /* __CROS_EC_PECI_H */
|
||||
|
||||
@@ -16,24 +16,12 @@ enum temp_sensor_id;
|
||||
|
||||
struct temp_sensor_t {
|
||||
const char* name;
|
||||
/* Sensor address. Used by read and print functions. */
|
||||
int addr;
|
||||
/* Read sensor value and return temperature in K. */
|
||||
int (*read)(const struct temp_sensor_t* self);
|
||||
/* Print debug info on console. */
|
||||
int (*print)(const struct temp_sensor_t* self);
|
||||
int (*read)(int idx);
|
||||
/* Index among the same kind of sensors. */
|
||||
int idx;
|
||||
};
|
||||
|
||||
/* Dummy value to put in "addr" field in temp_sensor_t if we don't need to
|
||||
* specify address.
|
||||
*/
|
||||
#define TEMP_SENSOR_NO_ADDR 0
|
||||
|
||||
/* Dummy value to put in "print" field in temp_sensor_t if we don't have debug
|
||||
* function for a sensor.
|
||||
*/
|
||||
#define TEMP_SENSOR_NO_PRINT 0
|
||||
|
||||
/* Initializes the module. */
|
||||
int temp_sensor_init(void);
|
||||
|
||||
@@ -41,21 +29,4 @@ int temp_sensor_init(void);
|
||||
* or -1 if error. */
|
||||
int temp_sensor_read(enum temp_sensor_id id);
|
||||
|
||||
|
||||
#define TMP006_ADDR(PORT,REG) ((PORT << 16) + REG)
|
||||
#define TMP006_PORT(ADDR) (ADDR >> 16)
|
||||
#define TMP006_REG(ADDR) (ADDR & 0xffff)
|
||||
|
||||
/* Read TI TMP006 die temperature sensor. Return temperature in K. */
|
||||
int temp_sensor_tmp006_read_die_temp(const struct temp_sensor_t* sensor);
|
||||
|
||||
/* Read TI TMP006 object temperature sensor. Return temperature in K. */
|
||||
int temp_sensor_tmp006_read_object_temp(const struct temp_sensor_t* sensor);
|
||||
|
||||
/* Configure TMP006 DRDY pin. */
|
||||
void temp_sensor_tmp006_config(const struct temp_sensor_t* sensor);
|
||||
|
||||
/* Print debug messages for TMP006. */
|
||||
int temp_sensor_tmp006_print(const struct temp_sensor_t* sensor);
|
||||
|
||||
#endif /* __CROS_EC_TEMP_SENSOR_H */
|
||||
|
||||
30
include/tmp006.h
Normal file
30
include/tmp006.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* 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 */
|
||||
|
||||
#ifndef __CROS_EC_TMP006_H
|
||||
#define __CROS_EC_TMP006_H
|
||||
|
||||
#define TMP006_ADDR(PORT,REG) ((PORT << 16) + REG)
|
||||
#define TMP006_PORT(ADDR) (ADDR >> 16)
|
||||
#define TMP006_REG(ADDR) (ADDR & 0xffff)
|
||||
|
||||
struct tmp006_t {
|
||||
const char* name;
|
||||
/* I2C address formed by TMP006_ADDR macro. */
|
||||
int addr;
|
||||
};
|
||||
|
||||
/* Poll all TMP006 sensors. Return 0 on success. */
|
||||
int tmp006_poll(void);
|
||||
|
||||
/* Get the last polled value of a sensor. Return temperature in K.
|
||||
* The low bit in idx indicate whether to read die temperature or
|
||||
* object temperature. The other bits serve as internal index to tmp006
|
||||
* module. */
|
||||
int tmp006_get_val(int idx);
|
||||
|
||||
#endif /* __CROS_EC_TMP006_H */
|
||||
Reference in New Issue
Block a user