| /* Copyright 2019 The ChromiumOS Authors |
| * 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 "console.h" |
| #include "peci.h" |
| #include "printf.h" |
| #include "util.h" |
| |
| static int peci_get_cpu_temp(int *cpu_temp) |
| { |
| int rv; |
| uint8_t r_buf[PECI_GET_TEMP_READ_LENGTH] = { 0 }; |
| struct peci_data peci = { |
| .cmd_code = PECI_CMD_GET_TEMP, |
| .addr = PECI_TARGET_ADDRESS, |
| .w_len = PECI_GET_TEMP_WRITE_LENGTH, |
| .r_len = PECI_GET_TEMP_READ_LENGTH, |
| .w_buf = NULL, |
| .r_buf = r_buf, |
| .timeout_us = PECI_GET_TEMP_TIMEOUT_US, |
| }; |
| |
| rv = peci_transaction(&peci); |
| if (rv) |
| return rv; |
| |
| /* Get relative raw data of temperature. */ |
| *cpu_temp = (r_buf[1] << 8) | r_buf[0]; |
| |
| /* Convert relative raw data to degrees C. */ |
| *cpu_temp = ((*cpu_temp ^ 0xFFFF) + 1) >> 6; |
| |
| /* |
| * 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 |
| * greater than or equal to CONFIG_PECI_TJMAX. |
| */ |
| if (*cpu_temp >= CONFIG_PECI_TJMAX) |
| return EC_ERROR_UNKNOWN; |
| |
| /* temperature in K */ |
| *cpu_temp = CONFIG_PECI_TJMAX - *cpu_temp + 273; |
| |
| return EC_SUCCESS; |
| } |
| |
| int peci_temp_sensor_get_val(int idx, int *temp_ptr) |
| { |
| int i, rv; |
| |
| if (!chipset_in_state(CHIPSET_STATE_ON | CHIPSET_STATE_STANDBY)) |
| return EC_ERROR_NOT_POWERED; |
| |
| /* |
| * Retry reading PECI CPU temperature if the first sample is |
| * invalid or failed to obtain. |
| */ |
| for (i = 0; i < 2; i++) { |
| rv = peci_get_cpu_temp(temp_ptr); |
| if (!rv) |
| break; |
| } |
| |
| return rv; |
| } |
| |
| /*****************************************************************************/ |
| /* Console commands */ |
| #ifdef CONFIG_CMD_PECI |
| static int peci_cmd(int argc, const char **argv) |
| { |
| uint8_t r_buf[PECI_READ_DATA_FIFO_SIZE] = { 0 }; |
| uint8_t w_buf[PECI_WRITE_DATA_FIFO_SIZE] = { 0 }; |
| struct peci_data peci = { |
| .w_buf = w_buf, |
| .r_buf = r_buf, |
| }; |
| |
| int param; |
| char *e; |
| |
| if ((argc < 6) || (argc > 8)) |
| return EC_ERROR_PARAM_COUNT; |
| |
| peci.addr = strtoi(argv[1], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM1; |
| |
| peci.w_len = strtoi(argv[2], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM2; |
| |
| peci.r_len = strtoi(argv[3], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM3; |
| |
| peci.cmd_code = strtoi(argv[4], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM4; |
| |
| peci.timeout_us = strtoi(argv[5], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM5; |
| |
| if (argc > 6) { |
| param = strtoi(argv[6], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM6; |
| |
| /* MSB of parameter */ |
| w_buf[3] = (uint8_t)(param >> 24); |
| /* LSB of parameter */ |
| w_buf[2] = (uint8_t)(param >> 16); |
| /* Index */ |
| w_buf[1] = (uint8_t)(param >> 8); |
| /* Host ID[7:1] & Retry[0] */ |
| w_buf[0] = (uint8_t)(param >> 0); |
| |
| if (argc > 7) { |
| param = strtoi(argv[7], &e, 0); |
| if (*e) |
| return EC_ERROR_PARAM7; |
| |
| /* Data (1, 2 or 4 bytes) */ |
| w_buf[7] = (uint8_t)(param >> 24); |
| w_buf[6] = (uint8_t)(param >> 16); |
| w_buf[5] = (uint8_t)(param >> 8); |
| w_buf[4] = (uint8_t)(param >> 0); |
| } |
| } else { |
| peci.w_len = 0x00; |
| } |
| |
| if (peci_transaction(&peci)) { |
| ccprintf("PECI transaction error\n"); |
| return EC_ERROR_UNKNOWN; |
| } else { |
| char str_buf[hex_str_buf_size(peci.r_len)]; |
| |
| snprintf_hex_buffer(str_buf, sizeof(str_buf), |
| HEX_BUF(r_buf, sizeof(str_buf))); |
| ccprintf("PECI read data: %s\n", str_buf); |
| |
| return EC_SUCCESS; |
| } |
| } |
| DECLARE_CONSOLE_COMMAND(peci, peci_cmd, "addr wlen rlen cmd timeout(us)", |
| "PECI command"); |
| |
| static int command_peci_temp(int argc, const char **argv) |
| { |
| int t; |
| |
| if (peci_get_cpu_temp(&t) != EC_SUCCESS) { |
| ccprintf("PECI get cpu temp error\n"); |
| 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"); |
| #endif /* CONFIG_CMD_PECI */ |