summaryrefslogtreecommitdiff
path: root/chip/it83xx/peci.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/it83xx/peci.c')
-rw-r--r--chip/it83xx/peci.c429
1 files changed, 429 insertions, 0 deletions
diff --git a/chip/it83xx/peci.c b/chip/it83xx/peci.c
new file mode 100644
index 0000000000..b613fa7971
--- /dev/null
+++ b/chip/it83xx/peci.c
@@ -0,0 +1,429 @@
+/* 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;
+
+ /*
+ * 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();
+#ifdef CONFIG_PECI_WITH_INTERRUPT
+ 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);
+#else
+ /* start */
+ IT83XX_PECI_HOCTLR |= 0x01;
+
+ for (index = 0x00; index < timeout_us; index += 16) {
+ if (IT83XX_PECI_HOSTAR & PECI_STATUS_ANY_BIT)
+ break;
+
+ udelay(15);
+ }
+#endif
+ 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;
+
+ 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;
+
+ peci_init_vtt_freq();
+
+ /* bit3,this bit enables the PECI host controller. */
+ IT83XX_PECI_HOCTLR |= 0x08;
+
+ /* To enable PECI function pin */
+ IT83XX_GPIO_GPCRF6 = 0x00;
+
+ /* 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",
+ NULL);
+
+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",
+ NULL);