| /* 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. |
| */ |
| |
| /* PECI interface for Chrome EC */ |
| |
| #include "chipset.h" |
| #include "clock.h" |
| #include "common.h" |
| #include "console.h" |
| #include "gpio.h" |
| #include "hooks.h" |
| #include "peci.h" |
| #include "registers.h" |
| #include "temp_sensor.h" |
| #include "util.h" |
| |
| /* Initial PECI baud rate */ |
| #define PECI_BAUD_RATE 100000 |
| |
| /* Polling interval for PECI, in ms */ |
| #define PECI_POLL_INTERVAL_MS 250 |
| |
| /* |
| * Internal and external path delays, in ns. The external delay is a |
| * best-guess measurement, but we're fairly tolerant of a bad guess because |
| * PECI_BAUD_RATE is slow compared to PECI's actual maximum baud rate. |
| */ |
| #define PECI_TD_FET_NS 60 |
| #define PECI_TD_INT_NS 80 |
| |
| /* Number of controller retries. Should be between 0 and 7. */ |
| #define PECI_RETRY_COUNT 4 |
| |
| /* Timing negotiation error bypass. 1 = on. 0 = off. */ |
| #define PECI_ERROR_BYPASS 1 |
| |
| #define TEMP_AVG_LENGTH 4 /* Should be power of 2 */ |
| static int temp_vals[TEMP_AVG_LENGTH]; |
| static int temp_idx; |
| |
| int peci_get_cpu_temp(void) |
| { |
| int v = LM4_PECI_M0D0 & 0xffff; |
| |
| if (v >= 0x8000 && v <= 0x8fff) |
| return -1; |
| |
| return v >> 6; |
| } |
| |
| int peci_temp_sensor_get_val(int idx, int *temp_ptr) |
| { |
| int sum = 0; |
| int success_cnt = 0; |
| int i; |
| |
| if (!chipset_in_state(CHIPSET_STATE_ON)) |
| return EC_ERROR_NOT_POWERED; |
| |
| for (i = 0; i < TEMP_AVG_LENGTH; ++i) { |
| if (temp_vals[i] >= 0) { |
| success_cnt++; |
| sum += temp_vals[i]; |
| } |
| } |
| |
| /* |
| * Require at least two valid samples. When the AP transitions into S0, |
| * it is possible, depending on the timing of the PECI sample, to read |
| * an invalid temperature. This is very rare, but when it does happen |
| * the temperature returned is CONFIG_PECI_TJMAX. Requiring two valid |
| * samples here assures us that one bad maximum temperature reading |
| * when entering S0 won't cause us to trigger an over temperature. |
| */ |
| if (success_cnt < 2) |
| return EC_ERROR_UNKNOWN; |
| |
| *temp_ptr = sum / success_cnt; |
| return EC_SUCCESS; |
| } |
| |
| static void peci_temp_sensor_poll(void) |
| { |
| temp_vals[temp_idx] = peci_get_cpu_temp(); |
| temp_idx = (temp_idx + 1) & (TEMP_AVG_LENGTH - 1); |
| } |
| DECLARE_HOOK(HOOK_TICK, peci_temp_sensor_poll, HOOK_PRIO_TEMP_SENSOR); |
| |
| static void peci_freq_changed(void) |
| { |
| int freq = clock_get_freq(); |
| int baud; |
| |
| /* Disable polling while reconfiguring */ |
| LM4_PECI_CTL = 0; |
| |
| /* |
| * Calculate baud setting from desired rate, compensating for internal |
| * and external delays. |
| */ |
| baud = freq / (4 * PECI_BAUD_RATE) - 2; |
| baud -= (freq / 1000000) * (PECI_TD_FET_NS + PECI_TD_INT_NS) / 1000; |
| |
| /* Set baud rate and polling rate */ |
| LM4_PECI_DIV = (baud << 16) | |
| (PECI_POLL_INTERVAL_MS * (freq / 1000 / 4096)); |
| |
| /* Set up temperature monitoring to report in degrees K */ |
| LM4_PECI_CTL = ((CONFIG_PECI_TJMAX + 273) << 22) | 0x0001 | |
| (PECI_RETRY_COUNT << 12) | |
| (PECI_ERROR_BYPASS << 11); |
| } |
| DECLARE_HOOK(HOOK_FREQ_CHANGE, peci_freq_changed, HOOK_PRIO_DEFAULT); |
| |
| static void peci_init(void) |
| { |
| int i; |
| |
| /* Enable the PECI module in run and sleep modes. */ |
| clock_enable_peripheral(CGC_OFFSET_PECI, 0x1, |
| CGC_MODE_RUN | CGC_MODE_SLEEP); |
| |
| /* Configure GPIOs */ |
| gpio_config_module(MODULE_PECI, 1); |
| |
| /* Set initial clock frequency */ |
| peci_freq_changed(); |
| |
| /* Initialize temperature reading buffer to a sane value. */ |
| for (i = 0; i < TEMP_AVG_LENGTH; ++i) |
| temp_vals[i] = 300; /* 27 C */ |
| } |
| DECLARE_HOOK(HOOK_INIT, peci_init, HOOK_PRIO_DEFAULT); |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| |
| static int command_peci_temp(int argc, char **argv) |
| { |
| int t = peci_get_cpu_temp(); |
| if (t == -1) { |
| ccprintf("PECI error 0x%04x\n", LM4_PECI_M0D0 & 0xffff); |
| return EC_ERROR_UNKNOWN; |
| } |
| ccprintf("CPU temp = %d K = %d C\n", t, K_TO_C(t)); |
| return EC_SUCCESS; |
| } |
| DECLARE_CONSOLE_COMMAND(pecitemp, command_peci_temp, |
| NULL, |
| "Print CPU temperature", |
| NULL); |