coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/npcx/peci.c

299 lines
8.0 KiB
C

/* Copyright 2014 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 "clock_chip.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "peci.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "temp_sensor.h"
#include "util.h"
/* Initial PECI baud rate */
#define PECI_BAUD_RATE 750000
#define TEMP_AVG_LENGTH 4 /* Should be power of 2 */
/* PECI Time-out */
#define PECI_DONE_TIMEOUT_US (10*MSEC)
#define NULL_PENDING_TASK_ID 0xFFFFFFFF
#define PECI_MAX_FIFO_SIZE 16
#define PROC_SOCKET 0x30
/* PECI Command Code */
enum peci_command_t {
PECI_COMMAND_PING = 0x00,
PECI_COMMAND_GET_DIB = 0xF7,
PECI_COMMAND_GET_TEMP = 0x01,
PECI_COMMAND_RD_PKG_CFG = 0xA1,
PECI_COMMAND_WR_PKG_CFG = 0xA5,
PECI_COMMAND_RD_IAMSR = 0xB1,
PECI_COMMAND_RD_PCI_CFG = 0x61,
PECI_COMMAND_RD_PCI_CFG_LOCAL = 0xE1,
PECI_COMMAND_WR_PCI_CFG_LOCAL = 0xE5,
PECI_COMMAND_NONE = 0xFF
};
#define PECI_COMMAND_GET_TEMP_WR_LENS 0x00
#define PECI_COMMAND_GET_TEMP_RD_LENS 0x02
/* PECI Domain Number */
static int temp_vals[TEMP_AVG_LENGTH];
static int temp_idx;
static uint8_t peci_sts;
/* For PECI Done interrupt usage */
static int peci_pending_task_id;
/*****************************************************************************/
/* Internal functions */
/**
* This routine initiates the parameters of a PECI transaction
*
* @param wr_length How many byte of *wr_data went to be send
* @param rd_length How many byte went to received (not include FCS)
* @param cmd_code Command code
* @param *wr_data Buffer pointer of write data
* @return TASK_EVENT_PECI_DONE that mean slave had a response
*/
static uint32_t peci_trans(
uint8_t wr_length,
uint8_t rd_length,
enum peci_command_t cmd_code,
uint8_t *wr_data
)
{
uint32_t events;
/* Ensure no PECI transaction is in progress */
if (IS_BIT_SET(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_START_BUSY)) {
/*
* PECI transaction is in progress -
* can not initiate a new one
*/
return 0;
}
/* Set basic transaction parameters */
NPCX_PECI_ADDR = PROC_SOCKET;
NPCX_PECI_CMD = cmd_code;
/* Aviod over space */
if (rd_length > PECI_MAX_FIFO_SIZE)
rd_length = PECI_MAX_FIFO_SIZE;
/* Read-Length */
NPCX_PECI_RD_LENGTH = rd_length;
if (wr_length > PECI_MAX_FIFO_SIZE)
wr_length = PECI_MAX_FIFO_SIZE;
/* copy of data */
for (events = 0; events < wr_length; events++)
NPCX_PECI_DATA_OUT(events) = wr_data[events];
/* Write-Length */
if (cmd_code != PECI_COMMAND_PING) {
if ((cmd_code == PECI_COMMAND_WR_PKG_CFG) ||
(cmd_code == PECI_COMMAND_WR_PCI_CFG_LOCAL)) {
/*CMD+AWFCS*/
NPCX_PECI_WR_LENGTH = wr_length + 2;
/* Enable AWFCS */
SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_AWFCS_EN);
} else {
/*CMD*/
NPCX_PECI_WR_LENGTH = wr_length + 1;
/* Enable AWFCS */
CLEAR_BIT(NPCX_PECI_CTL_STS,
NPCX_PECI_CTL_STS_AWFCS_EN);
}
}
/* Start the PECI transaction */
SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_START_BUSY);
/* It should be using a interrupt , don't waste cpu computing power */
peci_pending_task_id = task_get_current();
return task_wait_event_mask(TASK_EVENT_PECI_DONE,
PECI_DONE_TIMEOUT_US);
}
/**
* PECI transaction error status.
*
* @return Bit3 - CRC error Bit4 - ABRT error
*/
static uint8_t peci_check_error_state(void)
{
return peci_sts;
}
/*****************************************************************************/
/* PECI drivers */
int peci_get_cpu_temp(void)
{
uint32_t events;
int16_t cpu_temp = -1;
/* Start PECI trans */
events = peci_trans(PECI_COMMAND_GET_TEMP_WR_LENS,
PECI_COMMAND_GET_TEMP_RD_LENS,
PECI_COMMAND_GET_TEMP, NULL);
/* if return DONE , that mean slave had a PECI response */
if ((events & TASK_EVENT_PECI_DONE) == TASK_EVENT_PECI_DONE) {
/* check CRC & ABRT */
events = peci_check_error_state();
if (events) {
;
} else {
uint16_t *ptr;
ptr = (uint16_t *)&cpu_temp;
ptr[0] = (NPCX_PECI_DATA_IN(1) << 8) |
(NPCX_PECI_DATA_IN(0) << 0);
}
}
return (int)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)
{
int val;
val = peci_get_cpu_temp();
if (val != -1) {
temp_vals[temp_idx] = val;
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)
{
/* PECI's clock source is FMCLK */
int freq = clock_get_fm_freq();
int baud = 0xF;
/* Disable polling while reconfiguring */
NPCX_PECI_CTL_STS = 0;
/*
* Set the maximum bit rate used by the PECI module during both
* Address Timing Negotiation and Data Timing Negotiation.
* The resulting maximum bit rate MAX_BIT_RATE in decimal is
* according to the following formula:
*
* MAX_BIT_RATE [d] = (freq / (4 * baudrate)) - 1
* Maximum bit rate should not extend the field's boundaries.
*/
if (freq != 0) {
baud = (uint8_t)(freq / (4 * PECI_BAUD_RATE)) - 1;
/* Set maximum PECI baud rate (bit0 - bit4) */
if (baud > 0x1F)
baud = 0x1F;
}
/* Enhanced High-Speed */
if (baud >= 7) {
CLEAR_BIT(NPCX_PECI_RATE, 6);
CLEAR_BIT(NPCX_PECI_CFG, 3);
} else {
SET_BIT(NPCX_PECI_RATE, 6);
SET_BIT(NPCX_PECI_CFG, 3);
}
/* Setting Rate */
NPCX_PECI_RATE = baud;
}
DECLARE_HOOK(HOOK_FREQ_CHANGE, peci_freq_changed, HOOK_PRIO_DEFAULT);
static void peci_init(void)
{
int i;
/* Enable clock for PECI peripheral */
clock_enable_peripheral(CGC_OFFSET_PECI, CGC_PECI_MASK,
CGC_MODE_RUN | CGC_MODE_SLEEP);
/* Set PECI freq */
peci_freq_changed();
/* make sure PECI_DATA function pin enable */
CLEAR_BIT(NPCX_DEVALT(0x0A), 6);
/* 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 */
/* init Pending task id */
peci_pending_task_id = NULL_PENDING_TASK_ID;
/* Enable PECI Done interrupt */
SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_DONE_EN);
task_enable_irq(NPCX_IRQ_PECI);
}
DECLARE_HOOK(HOOK_INIT, peci_init, HOOK_PRIO_DEFAULT);
/* If received a PECI DONE interrupt, post the event to PECI task */
void peci_done_interrupt(void){
if (peci_pending_task_id != NULL_PENDING_TASK_ID)
task_set_event(peci_pending_task_id, TASK_EVENT_PECI_DONE, 0);
peci_sts = NPCX_PECI_CTL_STS & 0x18;
/* no matter what, clear status bit again */
SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_DONE);
SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_CRC_ERR);
SET_BIT(NPCX_PECI_CTL_STS, NPCX_PECI_CTL_STS_ABRT_ERR);
}
DECLARE_IRQ(NPCX_IRQ_PECI, peci_done_interrupt, 4);
/*****************************************************************************/
/* Console commands */
static int command_peci_temp(int argc, char **argv)
{
int t = peci_get_cpu_temp();
if (t == -1) {
ccprintf("PECI response timeout\n");
return EC_ERROR_UNKNOWN;
}
ccprintf("CPU temp = %d K = %d\n", t, K_TO_C(t));
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(pecitemp, command_peci_temp,
NULL,
"Print CPU temperature");