685 lines
19 KiB
C
685 lines
19 KiB
C
/* Copyright 2016 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.
|
|
*/
|
|
|
|
/* ESPI module for Chrome EC */
|
|
|
|
#include "registers.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "chipset.h"
|
|
#include "console.h"
|
|
#include "uart.h"
|
|
#include "util.h"
|
|
#include "power.h"
|
|
#include "espi.h"
|
|
#include "lpc_chip.h"
|
|
#include "hooks.h"
|
|
#include "timer.h"
|
|
|
|
/* Console output macros */
|
|
#if !(DEBUG_ESPI)
|
|
#define CPUTS(...)
|
|
#define CPRINTS(...)
|
|
#else
|
|
#define CPUTS(outstr) cputs(CC_LPC, outstr)
|
|
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
|
|
#endif
|
|
|
|
/* Default eSPI configuration for VW events */
|
|
struct vwevms_config_t {
|
|
uint8_t idx; /* VW index */
|
|
uint8_t idx_en; /* Index enable */
|
|
uint8_t pltrst_en; /* Enable reset by PLTRST assert */
|
|
uint8_t espirst_en; /* Enable reset by eSPI_RST assert */
|
|
uint8_t int_en; /* Interrupt/Wake-up enable */
|
|
};
|
|
|
|
struct vwevsm_config_t {
|
|
uint8_t idx; /* VW index */
|
|
uint8_t idx_en; /* Index enable */
|
|
uint8_t pltrst_en; /* Enable reset by PLTRST assert */
|
|
uint8_t cdrst_en; /* Enable cold reset */
|
|
uint8_t valid; /* Valid VW mask */
|
|
};
|
|
|
|
/* Default MIWU configurations for VW events */
|
|
struct host_wui_item {
|
|
uint16_t table : 2; /* MIWU table 0-2 */
|
|
uint16_t group : 3; /* MIWU group 0-7 */
|
|
uint16_t num : 3; /* MIWU bit 0-7 */
|
|
uint16_t edge : 4; /* MIWU edge trigger type rising/falling/any */
|
|
};
|
|
|
|
/* Mapping item between VW signal, index and value */
|
|
struct vw_event_t {
|
|
uint16_t name; /* Name of signal */
|
|
uint8_t evt_idx; /* VW index of signal */
|
|
uint8_t evt_val; /* VW value of signal */
|
|
};
|
|
|
|
/* Default settings of VWEVMS registers (Please refer Table.43/44) */
|
|
static const struct vwevms_config_t espi_in_list[] = {
|
|
/* IDX EN ENPL ENESP IE/WE VW Event Bit 0 - 3 (M->S) */
|
|
{0x02, 1, 0, 0, 1}, /* SLP_S3#, SLP_S4#, SLP_S5#, Reserve */
|
|
{0x03, 1, 0, 1, 1}, /* SUS_STAT#, PLTRST#, ORST_WARN, Reserve */
|
|
{0x07, 1, 1, 1, 1}, /* HRST_WARN, SMIOUT#, NMIOUT#, Reserve */
|
|
{0x41, 1, 0, 1, 1}, /* SUS_WARN#, SPWRDN_ACK, Reserve, SLP_A# */
|
|
{0x42, 1, 0, 0, 1}, /* SLP_LAN#, SLP_WAN#, Reserve, Reserve */
|
|
{0x47, 1, 1, 1, 1}, /* HOST_C10, Reserve, Reserve, Reserve */
|
|
};
|
|
|
|
/* Default settings of VWEVSM registers (Please refer Table.43/44) */
|
|
static const struct vwevsm_config_t espi_out_list[] = {
|
|
/* IDX EN ENPL ENCDR VDMASK VW Event Bit 0 - 3 (S->M) */
|
|
{0x04, 1, 0, 0, 0x0D}, /* ORST_ACK, Reserve, WAKE#, PME# */
|
|
{0x05, 1, 0, 0, 0x0F}, /* SLV_BL_DNE, ERR_F, ERR_NF, SLV_BL_STS */
|
|
#ifdef CONFIG_SCI_GPIO
|
|
{0x06, 1, 1, 0, 0x0C}, /* SCI#, SMI#, RCIN#, HRST_ACK */
|
|
#else
|
|
{0x06, 1, 1, 0, 0x0F}, /* SCI#, SMI#, RCIN#, HRST_ACK */
|
|
#endif
|
|
{0x40, 1, 0, 0, 0x01}, /* SUS_ACK, Reserve, Reserve, Reserve */
|
|
};
|
|
|
|
/* eSPI interrupts used in MIWU */
|
|
static const struct host_wui_item espi_vw_int_list[] = {
|
|
/* ESPI_RESET */
|
|
{MIWU_TABLE_0, MIWU_GROUP_5, 5, MIWU_EDGE_FALLING},
|
|
/* SLP_S3 */
|
|
{MIWU_TABLE_2, MIWU_GROUP_1, 0, MIWU_EDGE_ANYING},
|
|
/* SLP_S4 */
|
|
{MIWU_TABLE_2, MIWU_GROUP_1, 1, MIWU_EDGE_ANYING},
|
|
/* SLP_S5 */
|
|
{MIWU_TABLE_2, MIWU_GROUP_1, 2, MIWU_EDGE_ANYING},
|
|
/* VW_WIRE_PLTRST */
|
|
{MIWU_TABLE_2, MIWU_GROUP_1, 5, MIWU_EDGE_ANYING},
|
|
/* VW_WIRE_OOB_RST_WARN */
|
|
{MIWU_TABLE_2, MIWU_GROUP_1, 6, MIWU_EDGE_ANYING},
|
|
/* VW_WIRE_HOST_RST_WARN */
|
|
{MIWU_TABLE_2, MIWU_GROUP_2, 0, MIWU_EDGE_ANYING},
|
|
/* VW_WIRE_SUS_WARN */
|
|
{MIWU_TABLE_2, MIWU_GROUP_2, 4, MIWU_EDGE_ANYING},
|
|
};
|
|
|
|
/* VW signals used in eSPI */
|
|
static const struct vw_event_t vw_events_list[] = {
|
|
{VW_SLP_S3_L, 0x02, 0x01}, /* index 02h (In) */
|
|
{VW_SLP_S4_L, 0x02, 0x02},
|
|
{VW_SLP_S5_L, 0x02, 0x04},
|
|
{VW_SUS_STAT_L, 0x03, 0x01}, /* index 03h (In) */
|
|
{VW_PLTRST_L, 0x03, 0x02},
|
|
{VW_OOB_RST_WARN, 0x03, 0x04},
|
|
{VW_OOB_RST_ACK, 0x04, 0x01}, /* index 04h (Out) */
|
|
{VW_WAKE_L, 0x04, 0x04},
|
|
{VW_PME_L, 0x04, 0x08},
|
|
{VW_ERROR_FATAL, 0x05, 0x02}, /* index 05h (Out) */
|
|
{VW_ERROR_NON_FATAL, 0x05, 0x04},
|
|
{VW_SLAVE_BTLD_STATUS_DONE, 0x05, 0x09},
|
|
{VW_SCI_L, 0x06, 0x01}, /* index 06h (Out) */
|
|
{VW_SMI_L, 0x06, 0x02},
|
|
{VW_RCIN_L, 0x06, 0x04},
|
|
{VW_HOST_RST_ACK, 0x06, 0x08},
|
|
{VW_HOST_RST_WARN, 0x07, 0x01}, /* index 07h (In) */
|
|
{VW_SUS_ACK, 0x40, 0x01}, /* index 40h (Out) */
|
|
{VW_SUS_WARN_L, 0x41, 0x01}, /* index 41h (In) */
|
|
{VW_SUS_PWRDN_ACK_L, 0x41, 0x02},
|
|
{VW_SLP_A_L, 0x41, 0x08},
|
|
{VW_SLP_LAN, 0x42, 0x01}, /* index 42h (In) */
|
|
{VW_SLP_WLAN, 0x42, 0x02},
|
|
};
|
|
|
|
/* Flag for SLAVE_BOOT_LOAD siganls */
|
|
static uint8_t boot_load_done;
|
|
|
|
/*****************************************************************************/
|
|
/* eSPI internal utilities */
|
|
|
|
/* Recovery utility for eSPI reset */
|
|
static void espi_reset_recovery(void)
|
|
{
|
|
/* TODO: Put recovery stuff related to eSPI reset here */
|
|
|
|
/* Clear boot load flag */
|
|
boot_load_done = 0;
|
|
}
|
|
|
|
/* Configure Master-to-Slave virtual wire inputs */
|
|
static void espi_vw_config_in(const struct vwevms_config_t *config)
|
|
{
|
|
uint32_t val;
|
|
uint8_t i, index;
|
|
|
|
switch (VM_TYPE(config->idx)) {
|
|
case ESPI_VW_TYPE_SYS_EV:
|
|
case ESPI_VW_TYPE_PLT:
|
|
for (i = 0; i < ESPI_VWEVMS_NUM; i++) {
|
|
index = VWEVMS_IDX_GET(NPCX_VWEVMS(i));
|
|
/* Set VW input register */
|
|
if (index == config->idx) {
|
|
/* Get Wire field */
|
|
val = NPCX_VWEVMS(i) & 0x0F;
|
|
val |= VWEVMS_FIELD(config->idx,
|
|
config->idx_en,
|
|
config->pltrst_en,
|
|
config->int_en,
|
|
config->espirst_en);
|
|
NPCX_VWEVMS(i) = val;
|
|
return;
|
|
}
|
|
}
|
|
CPRINTS("No match index of all VWEVMSs");
|
|
break;
|
|
default:
|
|
CPRINTS("No support type of VWEVMS");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Configure Slave-to-Master virtual wire outputs */
|
|
static void espi_vw_config_out(const struct vwevsm_config_t *config)
|
|
{
|
|
uint32_t val;
|
|
uint8_t i, index;
|
|
|
|
switch (VM_TYPE(config->idx)) {
|
|
case ESPI_VW_TYPE_SYS_EV:
|
|
case ESPI_VW_TYPE_PLT:
|
|
for (i = 0; i < ESPI_VWEVSM_NUM; i++) {
|
|
index = VWEVSM_IDX_GET(NPCX_VWEVSM(i));
|
|
/* Set VW output register */
|
|
if (index == config->idx) {
|
|
/* Preserve WIRE(3-0) and HW_WIRE (27-24). */
|
|
val = NPCX_VWEVSM(i) & 0x0F00000F;
|
|
val |= VWEVSM_FIELD(config->idx,
|
|
config->idx_en,
|
|
config->valid,
|
|
config->pltrst_en,
|
|
config->cdrst_en);
|
|
NPCX_VWEVSM(i) = val;
|
|
return;
|
|
}
|
|
}
|
|
CPRINTS("No match index of all VWEVSMs");
|
|
break;
|
|
default:
|
|
CPRINTS("No support type of VWEVSM");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Config Master-to-Slave VWire interrupt edge type and enable it */
|
|
static void espi_enable_vw_int(const struct host_wui_item *vwire_int)
|
|
{
|
|
uint8_t table = vwire_int->table;
|
|
uint8_t group = vwire_int->group;
|
|
uint8_t num = vwire_int->num;
|
|
uint8_t edge = vwire_int->edge;
|
|
|
|
/* Set detection mode to edge */
|
|
CLEAR_BIT(NPCX_WKMOD(table, group), num);
|
|
|
|
if (edge != MIWU_EDGE_ANYING) {
|
|
/* Disable Any Edge */
|
|
CLEAR_BIT(NPCX_WKAEDG(table, group), num);
|
|
/* Enable Rising Edge */
|
|
if (edge == MIWU_EDGE_RISING)
|
|
CLEAR_BIT(NPCX_WKEDG(table, group), num);
|
|
/* Enable Falling Edge */
|
|
else
|
|
SET_BIT(NPCX_WKEDG(table, group), num);
|
|
} else
|
|
/* enable Any Edge */
|
|
SET_BIT(NPCX_WKAEDG(table, group), num);
|
|
|
|
/* Enable wake-up input sources */
|
|
SET_BIT(NPCX_WKEN(table, group), num);
|
|
}
|
|
|
|
/* Get vw index & value information by signal */
|
|
static int espi_vw_get_signal_index(enum espi_vw_signal event)
|
|
{
|
|
int index;
|
|
|
|
/* Find the vw index by signal name first */
|
|
for (index = 0; index < ARRAY_SIZE(vw_events_list); index++) {
|
|
if (vw_events_list[index].name == event)
|
|
break;
|
|
}
|
|
/* Cannot find the index */
|
|
if (index == ARRAY_SIZE(vw_events_list))
|
|
return -1;
|
|
|
|
return index;
|
|
}
|
|
|
|
/* The ISRs of VW signals which used for power sequences */
|
|
void espi_vw_power_signal_interrupt(enum espi_vw_signal signal)
|
|
{
|
|
/* TODO: Add VW handler in power/common.c */
|
|
power_signal_interrupt((enum gpio_signal) signal);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* IC specific low-level driver */
|
|
|
|
/**
|
|
* Set eSPI Virtual-Wire signal to Host
|
|
*
|
|
* @param signal vw signal needs to set
|
|
* @param level level of vw signal
|
|
* @return EC_SUCCESS, or non-zero if error.
|
|
*/
|
|
int espi_vw_set_wire(enum espi_vw_signal signal, uint8_t level)
|
|
{
|
|
uint8_t offset, value;
|
|
int sig_idx;
|
|
|
|
/* Get index of vw signal list by signale name */
|
|
sig_idx = espi_vw_get_signal_index(signal);
|
|
|
|
/* Cannot find index by signal name */
|
|
if (sig_idx < 0)
|
|
return EC_ERROR_PARAM1;
|
|
|
|
/* Find the output register offset by vw index */
|
|
for (offset = 0; offset < ESPI_VWEVSM_NUM; offset++) {
|
|
uint8_t vw_idx = VWEVSM_IDX_GET(NPCX_VWEVSM(offset));
|
|
/* If index matches. break */
|
|
if (vw_idx == vw_events_list[sig_idx].evt_idx)
|
|
break;
|
|
}
|
|
|
|
/* Cannot match index */
|
|
if (offset == ESPI_VWEVSM_NUM)
|
|
return EC_ERROR_PARAM1;
|
|
|
|
value = GET_FIELD(NPCX_VWEVSM(offset), NPCX_VWEVSM_WIRE);
|
|
/* Set wire */
|
|
if (level)
|
|
value |= vw_events_list[sig_idx].evt_val;
|
|
else /* Clear wire */
|
|
value &= (~vw_events_list[sig_idx].evt_val);
|
|
|
|
SET_FIELD(NPCX_VWEVSM(offset), NPCX_VWEVSM_WIRE, value);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Get eSPI Virtual-Wire signal from host
|
|
*
|
|
* @param signal vw signal needs to get
|
|
* @return 1: set by host, otherwise: no signal
|
|
*/
|
|
int espi_vw_get_wire(enum espi_vw_signal signal)
|
|
{
|
|
uint8_t offset, value;
|
|
int sig_idx;
|
|
|
|
/* Get index of vw signal list by signale name */
|
|
sig_idx = espi_vw_get_signal_index(signal);
|
|
|
|
/* Cannot find index by signal name */
|
|
if (sig_idx < 0)
|
|
return -1;
|
|
|
|
/* Find the input register offset by vw index */
|
|
for (offset = 0; offset < ESPI_VWEVMS_NUM; offset++) {
|
|
uint8_t vw_idx = VWEVMS_IDX_GET(NPCX_VWEVMS(offset));
|
|
/* If index matches. break */
|
|
if (vw_idx == vw_events_list[sig_idx].evt_idx)
|
|
break;
|
|
}
|
|
|
|
/* Cannot match index */
|
|
if (offset == ESPI_VWEVMS_NUM)
|
|
return -1;
|
|
|
|
/* Get wire & check with valid bits */
|
|
value = GET_FIELD(NPCX_VWEVMS(offset), NPCX_VWEVMS_WIRE);
|
|
value &= GET_FIELD(NPCX_VWEVMS(offset), NPCX_VWEVMS_VALID);
|
|
|
|
return !!(value & vw_events_list[sig_idx].evt_val);
|
|
}
|
|
|
|
/**
|
|
* Enable VW interrupt of power sequence signal
|
|
*
|
|
* @param signal vw signal needs to enable interrupt
|
|
* @return EC_SUCCESS, or non-zero if error.
|
|
*/
|
|
int espi_vw_enable_wire_int(enum espi_vw_signal signal)
|
|
{
|
|
if (signal == VW_SLP_S3_L)
|
|
SET_BIT(NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_1), 0);
|
|
else if (signal == VW_SLP_S4_L)
|
|
SET_BIT(NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_1), 1);
|
|
else if (signal == VW_SLP_S5_L)
|
|
SET_BIT(NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_1), 2);
|
|
else
|
|
return EC_ERROR_PARAM1;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Disable VW interrupt of power sequence signal
|
|
*
|
|
* @param signal vw signal needs to disable interrupt
|
|
* @return EC_SUCCESS, or non-zero if error.
|
|
*/
|
|
int espi_vw_disable_wire_int(enum espi_vw_signal signal)
|
|
{
|
|
if (signal == VW_SLP_S3_L)
|
|
CLEAR_BIT(NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_1), 0);
|
|
else if (signal == VW_SLP_S4_L)
|
|
CLEAR_BIT(NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_1), 1);
|
|
else if (signal == VW_SLP_S5_L)
|
|
CLEAR_BIT(NPCX_WKEN(MIWU_TABLE_2, MIWU_GROUP_1), 2);
|
|
else
|
|
return EC_ERROR_PARAM1;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* VW event handlers */
|
|
|
|
#ifdef CONFIG_CHIPSET_RESET_HOOK
|
|
static void espi_chipset_reset(void)
|
|
{
|
|
hook_notify(HOOK_CHIPSET_RESET);
|
|
}
|
|
DECLARE_DEFERRED(espi_chipset_reset);
|
|
#endif
|
|
|
|
/* PLTRST# event handler */
|
|
void espi_vw_evt_pltrst(void)
|
|
{
|
|
int pltrst = espi_vw_get_wire(VW_PLTRST_L);
|
|
|
|
CPRINTS("VW PLTRST: %d", pltrst);
|
|
|
|
if (pltrst) {
|
|
/* PLTRST# deasserted */
|
|
/* Disable eSPI peripheral channel support first */
|
|
CLEAR_BIT(NPCX_ESPICFG, NPCX_ESPICFG_PCCHN_SUPP);
|
|
|
|
/* Enable eSPI peripheral channel */
|
|
SET_BIT(NPCX_ESPICFG, NPCX_ESPICFG_PCHANEN);
|
|
|
|
/* Initialize host settings */
|
|
host_register_init();
|
|
|
|
/* Re-enable eSPI peripheral channel support */
|
|
SET_BIT(NPCX_ESPICFG, NPCX_ESPICFG_PCCHN_SUPP);
|
|
} else {
|
|
/* PLTRST# asserted */
|
|
#ifdef CONFIG_CHIPSET_RESET_HOOK
|
|
hook_call_deferred(&espi_chipset_reset_data, MSEC);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* SLP_Sx event handler */
|
|
void espi_vw_evt_slp_s3(void)
|
|
{
|
|
CPRINTS("VW SLP_S3: %d", espi_vw_get_wire(VW_SLP_S3_L));
|
|
espi_vw_power_signal_interrupt(VW_SLP_S3_L);
|
|
}
|
|
|
|
void espi_vw_evt_slp_s4(void)
|
|
{
|
|
CPRINTS("VW SLP_S4: %d", espi_vw_get_wire(VW_SLP_S4_L));
|
|
espi_vw_power_signal_interrupt(VW_SLP_S4_L);
|
|
}
|
|
|
|
void espi_vw_evt_slp_s5(void)
|
|
{
|
|
CPRINTS("VW SLP_S5: %d", espi_vw_get_wire(VW_SLP_S5_L));
|
|
espi_vw_power_signal_interrupt(VW_SLP_S5_L);
|
|
}
|
|
|
|
/* OOB Reset event handler */
|
|
void espi_vw_evt_oobrst(void)
|
|
{
|
|
CPRINTS("VW OOB_RST: %d", espi_vw_get_wire(VW_OOB_RST_WARN));
|
|
|
|
/* Send ACK to host by WARN#'s wire */
|
|
espi_vw_set_wire(VW_OOB_RST_ACK, espi_vw_get_wire(VW_OOB_RST_WARN));
|
|
}
|
|
|
|
/* SUS_WARN# event handler */
|
|
void espi_vw_evt_sus_warn(void)
|
|
{
|
|
CPRINTS("VW SUS_WARN#: %d", espi_vw_get_wire(VW_SUS_WARN_L));
|
|
|
|
udelay(100);
|
|
|
|
/* Send ACK to host by WARN#'s wire */
|
|
espi_vw_set_wire(VW_SUS_ACK, espi_vw_get_wire(VW_SUS_WARN_L));
|
|
}
|
|
|
|
/* HOSTRST WARN event handler */
|
|
void espi_vw_evt_hostrst_warn(void)
|
|
{
|
|
CPRINTS("VW HOST_RST_WARN#: %d", espi_vw_get_wire(VW_HOST_RST_WARN));
|
|
|
|
/* Send ACK to host by WARN#'s wire */
|
|
espi_vw_set_wire(VW_HOST_RST_ACK, espi_vw_get_wire(VW_HOST_RST_WARN));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Interrupt handlers */
|
|
|
|
/* eSPI reset assert/de-assert interrupt */
|
|
void espi_espirst_handler(void)
|
|
{
|
|
/* Clear pending bit of WUI */
|
|
SET_BIT(NPCX_WKPCL(MIWU_TABLE_0, MIWU_GROUP_5), 5);
|
|
|
|
CPRINTS("eSPI RST issued!");
|
|
}
|
|
|
|
/* Handle eSPI virtual wire interrupt 1 */
|
|
void __espi_wk2a_interrupt(void)
|
|
{
|
|
uint8_t pending_bits = NPCX_WKPND(MIWU_TABLE_2, MIWU_GROUP_1);
|
|
|
|
/* Clear pending bits of MIWU */
|
|
NPCX_WKPCL(MIWU_TABLE_2, MIWU_GROUP_1) = pending_bits;
|
|
|
|
/* Handle events of virtual-wire */
|
|
if (IS_BIT_SET(pending_bits, 0))
|
|
espi_vw_evt_slp_s3();
|
|
if (IS_BIT_SET(pending_bits, 1))
|
|
espi_vw_evt_slp_s4();
|
|
if (IS_BIT_SET(pending_bits, 2))
|
|
espi_vw_evt_slp_s5();
|
|
if (IS_BIT_SET(pending_bits, 5))
|
|
espi_vw_evt_pltrst();
|
|
if (IS_BIT_SET(pending_bits, 6))
|
|
espi_vw_evt_oobrst();
|
|
}
|
|
DECLARE_IRQ(NPCX_IRQ_WKINTA_2, __espi_wk2a_interrupt, 3);
|
|
|
|
/* Handle eSPI virtual wire interrupt 2 */
|
|
void __espi_wk2b_interrupt(void)
|
|
{
|
|
uint8_t pending_bits = NPCX_WKPND(MIWU_TABLE_2, MIWU_GROUP_2);
|
|
|
|
/* Clear pending bits of MIWU */
|
|
NPCX_WKPCL(MIWU_TABLE_2, MIWU_GROUP_2) = pending_bits;
|
|
|
|
/* Handle events of virtual-wire */
|
|
if (IS_BIT_SET(pending_bits, 4))
|
|
espi_vw_evt_sus_warn();
|
|
if (IS_BIT_SET(pending_bits, 0))
|
|
espi_vw_evt_hostrst_warn();
|
|
}
|
|
DECLARE_IRQ(NPCX_IRQ_WKINTB_2, __espi_wk2b_interrupt, 3);
|
|
|
|
/* Interrupt handler for eSPI status changed */
|
|
void espi_interrupt(void)
|
|
{
|
|
int chan;
|
|
uint32_t mask, status;
|
|
|
|
#if defined(CHIP_FAMILY_NPCX7)
|
|
/*
|
|
* Bit 17 of ESPIIE is reserved. We need to set the same bit in mask
|
|
* in case bit 17 in ESPISTS of npcx7 is not cleared in ISR.
|
|
*/
|
|
mask = NPCX_ESPIIE | BIT(NPCX_ESPISTS_VWUPDW);
|
|
#else
|
|
mask = NPCX_ESPIIE;
|
|
#endif
|
|
status = NPCX_ESPISTS & mask;
|
|
|
|
while (status) {
|
|
/* Clear pending bits first */
|
|
NPCX_ESPISTS = status;
|
|
|
|
if (IS_BIT_SET(status, NPCX_ESPISTS_BERR))
|
|
CPRINTS("eSPI Bus Error");
|
|
|
|
/* eSPI inband reset(from VW) */
|
|
if (IS_BIT_SET(status, NPCX_ESPISTS_IBRST)) {
|
|
CPRINTS("eSPI RST inband RST");
|
|
espi_reset_recovery();
|
|
|
|
} /* eSPI reset (from eSPI_rst pin) */
|
|
else if (IS_BIT_SET(status, NPCX_ESPISTS_ESPIRST)) {
|
|
CPRINTS("eSPI RST");
|
|
chipset_handle_espi_reset_assert();
|
|
espi_reset_recovery();
|
|
}
|
|
|
|
/* eSPI configuration is updated */
|
|
if (IS_BIT_SET(status, NPCX_ESPISTS_CFGUPD)) {
|
|
/*
|
|
* If host enable/disable channel for VW/OOB/FLASH, EC
|
|
* should follow except Peripheral channel. It is
|
|
* handled by PLTRST separately.
|
|
*/
|
|
for (chan = NPCX_ESPI_CH_VW; chan < NPCX_ESPI_CH_COUNT;
|
|
chan++) {
|
|
if (!IS_SLAVE_CHAN_ENABLE(chan) &&
|
|
IS_HOST_CHAN_EN(chan))
|
|
ENABLE_ESPI_CHAN(chan);
|
|
else if (IS_SLAVE_CHAN_ENABLE(chan) &&
|
|
!IS_HOST_CHAN_EN(chan))
|
|
DISABLE_ESPI_CHAN(chan);
|
|
}
|
|
|
|
/*
|
|
* Send SLAVE_BOOTLOAD_DONE and SLAVE_BOOTLOAD_STATUS
|
|
* events to host simultaneously. To indicate the
|
|
* completion of EC firmware code loading.
|
|
*/
|
|
if (boot_load_done == 0 &&
|
|
IS_SLAVE_CHAN_ENABLE(NPCX_ESPI_CH_VW)) {
|
|
|
|
espi_vw_set_wire(VW_SLAVE_BTLD_STATUS_DONE, 1);
|
|
boot_load_done = 1;
|
|
}
|
|
}
|
|
|
|
/* Any VW signal sent by Host - leave it, handle in MIWU ISR */
|
|
if (IS_BIT_SET(status, NPCX_ESPISTS_VWUPD))
|
|
CPRINTS("VW Updated INT");
|
|
|
|
/* Get status again */
|
|
status = NPCX_ESPISTS & mask;
|
|
}
|
|
}
|
|
DECLARE_IRQ(NPCX_IRQ_ESPI, espi_interrupt, 4);
|
|
|
|
/*****************************************************************************/
|
|
/* eSPI Initialization functions */
|
|
void espi_init(void)
|
|
{
|
|
int i;
|
|
|
|
/* Support all channels */
|
|
NPCX_ESPICFG |= ESPI_SUPP_CH_ALL;
|
|
|
|
/* Support all I/O modes */
|
|
SET_FIELD(NPCX_ESPICFG, NPCX_ESPICFG_IOMODE_FILED,
|
|
NPCX_ESPI_IO_MODE_ALL);
|
|
|
|
/* Set eSPI speed to max supported */
|
|
SET_FIELD(NPCX_ESPICFG, NPCX_ESPICFG_MAXFREQ_FILED,
|
|
NPCX_ESPI_MAXFREQ_MAX);
|
|
|
|
/* Configure Master-to-Slave Virtual Wire indexes (Inputs) */
|
|
for (i = 0; i < ARRAY_SIZE(espi_in_list); i++)
|
|
espi_vw_config_in(&espi_in_list[i]);
|
|
|
|
/* Configure Slave-to-Master Virtual Wire indexes (Outputs) */
|
|
for (i = 0; i < ARRAY_SIZE(espi_out_list); i++)
|
|
espi_vw_config_out(&espi_out_list[i]);
|
|
|
|
/* Configure MIWU for eSPI VW */
|
|
for (i = 0; i < ARRAY_SIZE(espi_vw_int_list); i++)
|
|
espi_enable_vw_int(&espi_vw_int_list[i]);
|
|
}
|
|
|
|
static int command_espi(int argc, char **argv)
|
|
{
|
|
uint32_t chan;
|
|
char *e;
|
|
|
|
if (argc == 1) {
|
|
return EC_ERROR_INVAL;
|
|
/* Get value of eSPI registers */
|
|
} else if (argc == 2) {
|
|
int i;
|
|
|
|
if (strcasecmp(argv[1], "cfg") == 0) {
|
|
ccprintf("ESPICFG [0x%08x]\n", NPCX_ESPICFG);
|
|
} else if (strcasecmp(argv[1], "vsm") == 0) {
|
|
for (i = 0; i < ESPI_VWEVSM_NUM; i++) {
|
|
uint32_t val = NPCX_VWEVSM(i);
|
|
uint8_t idx = VWEVSM_IDX_GET(val);
|
|
|
|
ccprintf("VWEVSM%d: %02x [0x%08x]\n", i, idx,
|
|
val);
|
|
}
|
|
} else if (strcasecmp(argv[1], "vms") == 0) {
|
|
for (i = 0; i < ESPI_VWEVMS_NUM; i++) {
|
|
uint32_t val = NPCX_VWEVMS(i);
|
|
uint8_t idx = VWEVMS_IDX_GET(val);
|
|
|
|
ccprintf("VWEVMS%d: %02x [0x%08x]\n", i, idx,
|
|
val);
|
|
}
|
|
}
|
|
/* Enable/Disable the channels of eSPI */
|
|
} else if (argc == 3) {
|
|
uint32_t m = (uint32_t) strtoi(argv[2], &e, 0);
|
|
|
|
if (*e)
|
|
return EC_ERROR_PARAM2;
|
|
if (m > 4)
|
|
return EC_ERROR_PARAM2;
|
|
else if (m == 4)
|
|
chan = 0x0F;
|
|
else
|
|
chan = 0x01 << m;
|
|
if (strcasecmp(argv[1], "en") == 0)
|
|
NPCX_ESPICFG = NPCX_ESPICFG | chan;
|
|
else if (strcasecmp(argv[1], "dis") == 0)
|
|
NPCX_ESPICFG = NPCX_ESPICFG & ~chan;
|
|
else
|
|
return EC_ERROR_PARAM1;
|
|
ccprintf("ESPICFG [0x%08x]\n", NPCX_ESPICFG);
|
|
}
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(espi, command_espi,
|
|
"cfg/vms/vsm/en/dis [channel]",
|
|
"eSPI configurations");
|