Files
OpenCellular/chip/it83xx/peci.c
Bill Richardson bb15561db5 cleanup: DECLARE_CONSOLE_COMMAND only needs 4 args
Since pretty much always, we've declared console commands to take
a "longhelp" argument with detailed explanations of what the
command does. But since almost as long, we've never actually used
that argument for anything - we just silently throw it away in
the macro. There's only one command (usbchargemode) that even
thinks it defines that argument.

We're never going to use this, let's just get rid of it.

BUG=none
BRANCH=none
CQ-DEPEND=CL:*279060
CQ-DEPEND=CL:*279158
CQ-DEPEND=CL:*279037
TEST=make buildall; tested on Cr50 hardware

Everything builds. Since we never used this arg anyway, there had
better not be any difference in the result.

Change-Id: Id3f71a53d02e3dc625cfcc12aa71ecb50e35eb9f
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/374163
Reviewed-by: Myles Watson <mylesgw@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
2016-08-24 16:30:10 +00:00

421 lines
10 KiB
C

/* Copyright 2015 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"
#include "timer.h"
#include "task.h"
#define TEMP_AVG_LENGTH 4 /* Should be power of 2 */
static int temp_vals[TEMP_AVG_LENGTH];
static int temp_idx;
#define PECI_TARGET_ADDRESS 0x30
#define PECI_WRITE_DATA_FIFO_SIZE 15
#define PECI_READ_DATA_FIFO_SIZE 16
#define PECI_GET_TEMP_READ_LENGTH 2
#define PECI_GET_TEMP_WRITE_LENGTH 0
#define PECI_GET_TEMP_TIMEOUT_US 200
/* PECI Command Code */
enum peci_command_code {
PECI_CMD_PING = 0x00,
PECI_CMD_GET_DIB = 0xF7,
PECI_CMD_GET_TEMP = 0x01,
PECI_CMD_RD_PKG_CFG = 0xA1,
PECI_CMD_WR_PKG_CFG = 0xA5,
PECI_CMD_RD_IAMSR = 0xB1,
PECI_CMD_WR_IAMSR = 0xB5,
PECI_CMD_RD_PCI_CFG = 0x61,
PECI_CMD_WR_PCI_CFG = 0x65,
PECI_CMD_RD_PCI_CFG_LOCAL = 0xE1,
PECI_CMD_WR_PCI_CFG_LOCAL = 0xE5,
};
enum peci_status {
PECI_STATUS_NO_ERR = 0x00,
PECI_STATUS_HOBY = 0x01,
PECI_STATUS_FINISH = 0x02,
PECI_STATUS_RD_FCS_ERR = 0x04,
PECI_STATUS_WR_FCS_ERR = 0x08,
PECI_STATUS_EXTERR = 0x20,
PECI_STATUS_BUSERR = 0x40,
PECI_STATUS_RCV_ERRCODE = 0x80,
PECI_STATUS_ERR_NEED_RST = (PECI_STATUS_BUSERR | PECI_STATUS_EXTERR),
PECI_STATUS_ANY_ERR = (PECI_STATUS_RCV_ERRCODE |
PECI_STATUS_BUSERR |
PECI_STATUS_EXTERR |
PECI_STATUS_WR_FCS_ERR |
PECI_STATUS_RD_FCS_ERR),
PECI_STATUS_ANY_BIT = 0xFE,
PECI_STATUS_TIMEOUT = 0xFF,
};
static task_id_t peci_current_task;
static void peci_init_vtt_freq(void)
{
/*
* bit2, enable the PECI interrupt generated by data valid event
* from PECI.
*
* bit[1-0], these bits are used to set PECI VTT level.
* 00b: 1.10v
* 01b: 1.05v
* 10b: 1.00v
*/
IT83XX_PECI_PADCTLR = 0x06;
/*
* bit[2-0], these bits are used to set PECI host's optimal
* transfer rate.
* 000b: 2.0 MHz
* 001b: 1.0 MHz
* 100b: 1.6 MHz
*/
IT83XX_PECI_HOCTL2R = 0x01;
}
static void peci_reset(void)
{
/* Reset PECI */
IT83XX_GCTRL_RSTC4 |= 0x10;
/* short delay */
udelay(15);
peci_init_vtt_freq();
}
/**
* Start a PECI transaction
*
* @param addr client address
* @param w_len write length (no include [Cmd Code] and [AW FCS])
* @param r_len read length (no include [FCS])
* @param cmd_code command code
* @param *w_buf How buffer pointer of write data
* @param *r_buf How buffer pointer of read data
* @param timeout_us transaction timeout unit:us
*
* @return zero if successful, non-zero if error
*/
static enum peci_status peci_transaction(uint8_t addr,
uint8_t w_len,
uint8_t r_len,
enum peci_command_code cmd_code,
uint8_t *w_buf,
uint8_t *r_buf,
int timeout_us)
{
uint8_t status;
int index;
/* To enable PECI function pin */
IT83XX_GPIO_GPCRF6 = 0x00;
/*
* bit5, Both write and read data FIFO pointers will be cleared.
*
* bit4, This bit enables the PECI host to abort the transaction
* when FCS error occurs.
*
* bit2, This bit enables the contention mechanism of the PECI bus.
* When this bit is set, the host will abort the transaction
* if the PECI bus is contentious.
*/
IT83XX_PECI_HOCTLR |= 0x34;
/* This register is the target address field of the PECI protocol. */
IT83XX_PECI_HOTRADDR = addr;
/* This register is the write length field of the PECI protocol. */
ASSERT(w_len <= PECI_WRITE_DATA_FIFO_SIZE);
if (cmd_code == PECI_CMD_PING) {
/* write length is 0 */
IT83XX_PECI_HOWRLR = 0x00;
} else {
if ((cmd_code == PECI_CMD_WR_PKG_CFG) ||
(cmd_code == PECI_CMD_WR_IAMSR) ||
(cmd_code == PECI_CMD_WR_PCI_CFG) ||
(cmd_code == PECI_CMD_WR_PCI_CFG_LOCAL)) {
/* write length include Cmd Code + AW FCS */
IT83XX_PECI_HOWRLR = w_len + 2;
/* bit1, The bit enables the AW_FCS hardwired mechanism
* based on the PECI command. This bit is functional
* only when the AW_FCS supported command of
* PECI 2.0/3.0/3.1 is issued.
* When this bit is set, the hardware will handle the
* calculation of AW_FCS.
*/
IT83XX_PECI_HOCTLR |= 0x02;
} else {
/* write length include Cmd Code */
IT83XX_PECI_HOWRLR = w_len + 1;
IT83XX_PECI_HOCTLR &= ~0x02;
}
}
/* This register is the read length field of the PECI protocol. */
ASSERT(r_len <= PECI_READ_DATA_FIFO_SIZE);
IT83XX_PECI_HORDLR = r_len;
/* This register is the command field of the PECI protocol. */
IT83XX_PECI_HOCMDR = cmd_code;
/* The write data field of the PECI protocol. */
for (index = 0x00; index < w_len; index++)
IT83XX_PECI_HOWRDR = w_buf[index];
peci_current_task = task_get_current();
task_clear_pending_irq(IT83XX_IRQ_PECI);
task_enable_irq(IT83XX_IRQ_PECI);
/* start */
IT83XX_PECI_HOCTLR |= 0x01;
/* pre-set timeout */
index = timeout_us;
if (task_wait_event(timeout_us) != TASK_EVENT_TIMER)
index = 0;
task_disable_irq(IT83XX_IRQ_PECI);
peci_current_task = TASK_ID_INVALID;
if (index < timeout_us) {
status = IT83XX_PECI_HOSTAR;
/* any error */
if (IT83XX_PECI_HOSTAR & PECI_STATUS_ANY_ERR) {
if (IT83XX_PECI_HOSTAR & PECI_STATUS_ERR_NEED_RST)
peci_reset();
} else if (IT83XX_PECI_HOSTAR & PECI_STATUS_FINISH) {
/* The read data field of the PECI protocol. */
for (index = 0x00; index < r_len; index++)
r_buf[index] = IT83XX_PECI_HORDDR;
/* W/C */
IT83XX_PECI_HOSTAR = PECI_STATUS_FINISH;
status = IT83XX_PECI_HOSTAR;
}
} else {
/* transaction timeout */
status = PECI_STATUS_TIMEOUT;
}
/* Don't disable PECI host controller if controller already enable. */
IT83XX_PECI_HOCTLR = 0x08;
/* W/C */
IT83XX_PECI_HOSTAR = PECI_STATUS_ANY_BIT;
/* Disable PECI function pin */
IT83XX_GPIO_GPCRF6 = 0x80;
return status;
}
int peci_get_cpu_temp(void)
{
uint8_t r_buf[PECI_GET_TEMP_READ_LENGTH] = {0};
int cpu_temp = -1;
if (peci_transaction(PECI_TARGET_ADDRESS,
PECI_GET_TEMP_WRITE_LENGTH,
PECI_GET_TEMP_READ_LENGTH,
PECI_CMD_GET_TEMP,
NULL,
r_buf,
PECI_GET_TEMP_TIMEOUT_US) ==
PECI_STATUS_NO_ERR) {
/* Get relative raw data of temperature. */
cpu_temp = (r_buf[1] << 8) | r_buf[0];
#ifdef CONFIG_PECI_TJMAX
/* Convert relative raw data to degrees C. */
cpu_temp = ((cpu_temp ^ 0xFFFF) + 1) >> 6;
/* temperature in K */
cpu_temp = (CONFIG_PECI_TJMAX - cpu_temp) + 273;
#endif
}
return cpu_temp;
}
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);
void peci_interrupt(void)
{
task_clear_pending_irq(IT83XX_IRQ_PECI);
task_disable_irq(IT83XX_IRQ_PECI);
if (peci_current_task != TASK_ID_INVALID)
task_wake(peci_current_task);
}
static void peci_init(void)
{
int i;
clock_enable_peripheral(CGC_OFFSET_PECI, 0, 0);
peci_init_vtt_freq();
/* bit3,this bit enables the PECI host controller. */
IT83XX_PECI_HOCTLR |= 0x08;
/* bit4, PECI enable */
IT83XX_GPIO_GRC2 |= 0x10;
/* 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 peci_cmd(int argc, char **argv)
{
uint8_t r_buf[PECI_READ_DATA_FIFO_SIZE] = {0};
uint8_t w_buf[PECI_WRITE_DATA_FIFO_SIZE] = {0};
int addr, wlen, rlen, cmd, time_us, param;
char *e;
if ((argc < 6) || (argc > 8))
return EC_ERROR_PARAM_COUNT;
addr = strtoi(argv[1], &e, 0);
if (*e)
return EC_ERROR_PARAM1;
wlen = strtoi(argv[2], &e, 0);
if (*e)
return EC_ERROR_PARAM2;
rlen = strtoi(argv[3], &e, 0);
if (*e)
return EC_ERROR_PARAM3;
cmd = strtoi(argv[4], &e, 0);
if (*e)
return EC_ERROR_PARAM4;
time_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 {
wlen = 0x00;
}
if (peci_transaction(addr, wlen, rlen, cmd, w_buf, r_buf, time_us)) {
ccprintf("PECI transaction error\n");
return EC_ERROR_UNKNOWN;
}
ccprintf("PECI read data: %.*h\n", rlen, r_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, char **argv)
{
int t = peci_get_cpu_temp();
if (t == -1) {
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");