620 lines
17 KiB
C
620 lines
17 KiB
C
|
/* Copyright 2017 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 "console.h"
|
||
|
#include "espi.h"
|
||
|
#include "hooks.h"
|
||
|
#include "port80.h"
|
||
|
#include "power.h"
|
||
|
#include "registers.h"
|
||
|
#include "system.h"
|
||
|
#include "task.h"
|
||
|
#include "uart.h"
|
||
|
#include "util.h"
|
||
|
|
||
|
/* Console output macros */
|
||
|
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)
|
||
|
|
||
|
struct vw_channel_t {
|
||
|
uint8_t index; /* VW index of signal */
|
||
|
uint8_t level_mask; /* level bit of signal */
|
||
|
uint8_t valid_mask; /* valid bit of signal */
|
||
|
};
|
||
|
|
||
|
/* VW settings after the master enables the VW channel. */
|
||
|
static const struct vw_channel_t en_vw_setting[] = {
|
||
|
/* EC sends SUS_ACK# = 1 VW to PCH. That does not apply to GLK SoC. */
|
||
|
#ifndef CONFIG_CHIPSET_GEMINILAKE
|
||
|
{ESPI_SYSTEM_EVENT_VW_IDX_40,
|
||
|
VW_LEVEL_FIELD(0),
|
||
|
VW_VALID_FIELD(VW_IDX_40_SUS_ACK)},
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
/* VW settings after the master enables the OOB channel. */
|
||
|
static const struct vw_channel_t en_oob_setting[] = {
|
||
|
{ESPI_SYSTEM_EVENT_VW_IDX_4,
|
||
|
VW_LEVEL_FIELD(0),
|
||
|
VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK)},
|
||
|
};
|
||
|
|
||
|
/* VW settings after the master enables the flash channel. */
|
||
|
static const struct vw_channel_t en_flash_setting[] = {
|
||
|
{ESPI_SYSTEM_EVENT_VW_IDX_5,
|
||
|
VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE),
|
||
|
VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE)},
|
||
|
};
|
||
|
|
||
|
/* VW settings at host startup */
|
||
|
static const struct vw_channel_t vw_host_startup_setting[] = {
|
||
|
{ESPI_SYSTEM_EVENT_VW_IDX_6,
|
||
|
VW_LEVEL_FIELD(VW_IDX_6_SCI | VW_IDX_6_SMI |
|
||
|
VW_IDX_6_RCIN | VW_IDX_6_HOST_RST_ACK),
|
||
|
VW_VALID_FIELD(VW_IDX_6_SCI | VW_IDX_6_SMI |
|
||
|
VW_IDX_6_RCIN | VW_IDX_6_HOST_RST_ACK)},
|
||
|
};
|
||
|
|
||
|
#define VW_CHAN(name, idx, level, valid) \
|
||
|
[(name - VW_SIGNAL_BASE)] = {idx, level, valid}
|
||
|
|
||
|
/* VW signals used in eSPI (NOTE: must match order of enum espi_vw_signal). */
|
||
|
static const struct vw_channel_t vw_channel_list[] = {
|
||
|
/* index 02h: master to slave. */
|
||
|
VW_CHAN(VW_SLP_S3_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_2,
|
||
|
VW_LEVEL_FIELD(VW_IDX_2_SLP_S3),
|
||
|
VW_VALID_FIELD(VW_IDX_2_SLP_S3)),
|
||
|
VW_CHAN(VW_SLP_S4_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_2,
|
||
|
VW_LEVEL_FIELD(VW_IDX_2_SLP_S4),
|
||
|
VW_VALID_FIELD(VW_IDX_2_SLP_S4)),
|
||
|
VW_CHAN(VW_SLP_S5_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_2,
|
||
|
VW_LEVEL_FIELD(VW_IDX_2_SLP_S5),
|
||
|
VW_VALID_FIELD(VW_IDX_2_SLP_S5)),
|
||
|
/* index 03h: master to slave. */
|
||
|
VW_CHAN(VW_SUS_STAT_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_3,
|
||
|
VW_LEVEL_FIELD(VW_IDX_3_SUS_STAT),
|
||
|
VW_VALID_FIELD(VW_IDX_3_SUS_STAT)),
|
||
|
VW_CHAN(VW_PLTRST_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_3,
|
||
|
VW_LEVEL_FIELD(VW_IDX_3_PLTRST),
|
||
|
VW_VALID_FIELD(VW_IDX_3_PLTRST)),
|
||
|
VW_CHAN(VW_OOB_RST_WARN,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_3,
|
||
|
VW_LEVEL_FIELD(VW_IDX_3_OOB_RST_WARN),
|
||
|
VW_VALID_FIELD(VW_IDX_3_OOB_RST_WARN)),
|
||
|
/* index 04h: slave to master. */
|
||
|
VW_CHAN(VW_OOB_RST_ACK,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_4,
|
||
|
VW_LEVEL_FIELD(VW_IDX_4_OOB_RST_ACK),
|
||
|
VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK)),
|
||
|
VW_CHAN(VW_WAKE_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_4,
|
||
|
VW_LEVEL_FIELD(VW_IDX_4_WAKE),
|
||
|
VW_VALID_FIELD(VW_IDX_4_WAKE)),
|
||
|
VW_CHAN(VW_PME_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_4,
|
||
|
VW_LEVEL_FIELD(VW_IDX_4_PME),
|
||
|
VW_VALID_FIELD(VW_IDX_4_PME)),
|
||
|
/* index 05h: slave to master. */
|
||
|
VW_CHAN(VW_ERROR_FATAL,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_5,
|
||
|
VW_LEVEL_FIELD(VW_IDX_5_FATAL),
|
||
|
VW_VALID_FIELD(VW_IDX_5_FATAL)),
|
||
|
VW_CHAN(VW_ERROR_NON_FATAL,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_5,
|
||
|
VW_LEVEL_FIELD(VW_IDX_5_NON_FATAL),
|
||
|
VW_VALID_FIELD(VW_IDX_5_NON_FATAL)),
|
||
|
VW_CHAN(VW_SLAVE_BTLD_STATUS_DONE,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_5,
|
||
|
VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE),
|
||
|
VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE)),
|
||
|
/* index 06h: slave to master. */
|
||
|
VW_CHAN(VW_SCI_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_6,
|
||
|
VW_LEVEL_FIELD(VW_IDX_6_SCI),
|
||
|
VW_VALID_FIELD(VW_IDX_6_SCI)),
|
||
|
VW_CHAN(VW_SMI_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_6,
|
||
|
VW_LEVEL_FIELD(VW_IDX_6_SMI),
|
||
|
VW_VALID_FIELD(VW_IDX_6_SMI)),
|
||
|
VW_CHAN(VW_RCIN_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_6,
|
||
|
VW_LEVEL_FIELD(VW_IDX_6_RCIN),
|
||
|
VW_VALID_FIELD(VW_IDX_6_RCIN)),
|
||
|
VW_CHAN(VW_HOST_RST_ACK,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_6,
|
||
|
VW_LEVEL_FIELD(VW_IDX_6_HOST_RST_ACK),
|
||
|
VW_VALID_FIELD(VW_IDX_6_HOST_RST_ACK)),
|
||
|
/* index 07h: master to slave. */
|
||
|
VW_CHAN(VW_HOST_RST_WARN,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_7,
|
||
|
VW_LEVEL_FIELD(VW_IDX_7_HOST_RST_WARN),
|
||
|
VW_VALID_FIELD(VW_IDX_7_HOST_RST_WARN)),
|
||
|
/* index 40h: slave to master. */
|
||
|
VW_CHAN(VW_SUS_ACK,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_40,
|
||
|
VW_LEVEL_FIELD(VW_IDX_40_SUS_ACK),
|
||
|
VW_VALID_FIELD(VW_IDX_40_SUS_ACK)),
|
||
|
/* index 41h: master to slave. */
|
||
|
VW_CHAN(VW_SUS_WARN_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_41,
|
||
|
VW_LEVEL_FIELD(VW_IDX_41_SUS_WARN),
|
||
|
VW_VALID_FIELD(VW_IDX_41_SUS_WARN)),
|
||
|
VW_CHAN(VW_SUS_PWRDN_ACK_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_41,
|
||
|
VW_LEVEL_FIELD(VW_IDX_41_SUS_PWRDN_ACK),
|
||
|
VW_VALID_FIELD(VW_IDX_41_SUS_PWRDN_ACK)),
|
||
|
VW_CHAN(VW_SLP_A_L,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_41,
|
||
|
VW_LEVEL_FIELD(VW_IDX_41_SLP_A),
|
||
|
VW_VALID_FIELD(VW_IDX_41_SLP_A)),
|
||
|
/* index 42h: master to slave. */
|
||
|
VW_CHAN(VW_SLP_LAN,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_42,
|
||
|
VW_LEVEL_FIELD(VW_IDX_42_SLP_LAN),
|
||
|
VW_VALID_FIELD(VW_IDX_42_SLP_LAN)),
|
||
|
VW_CHAN(VW_SLP_WLAN,
|
||
|
ESPI_SYSTEM_EVENT_VW_IDX_42,
|
||
|
VW_LEVEL_FIELD(VW_IDX_42_SLP_WLAN),
|
||
|
VW_VALID_FIELD(VW_IDX_42_SLP_WLAN)),
|
||
|
};
|
||
|
BUILD_ASSERT(ARRAY_SIZE(vw_channel_list) ==
|
||
|
(VW_SIGNAL_BASE_END - VW_SIGNAL_BASE));
|
||
|
|
||
|
/* Get vw index & value information by signal */
|
||
|
static int espi_vw_get_signal_index(enum espi_vw_signal event)
|
||
|
{
|
||
|
uint32_t i = event - VW_SIGNAL_BASE;
|
||
|
|
||
|
return (i < ARRAY_SIZE(vw_channel_list)) ? i : -1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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)
|
||
|
{
|
||
|
/* Get index of vw signal list by signale name */
|
||
|
int i = espi_vw_get_signal_index(signal);
|
||
|
|
||
|
if (i < 0)
|
||
|
return EC_ERROR_PARAM1;
|
||
|
|
||
|
/* critical section with interrupts off */
|
||
|
interrupt_disable();
|
||
|
if (level)
|
||
|
IT83XX_ESPI_VWIDX(vw_channel_list[i].index) |=
|
||
|
vw_channel_list[i].level_mask;
|
||
|
else
|
||
|
IT83XX_ESPI_VWIDX(vw_channel_list[i].index) &=
|
||
|
~vw_channel_list[i].level_mask;
|
||
|
/* restore interrupts */
|
||
|
interrupt_enable();
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
/* Get index of vw signal list by signale name */
|
||
|
int i = espi_vw_get_signal_index(signal);
|
||
|
|
||
|
if (i < 0)
|
||
|
return 0;
|
||
|
|
||
|
/* Not valid */
|
||
|
if (!(IT83XX_ESPI_VWIDX(vw_channel_list[i].index) &
|
||
|
vw_channel_list[i].valid_mask))
|
||
|
return 0;
|
||
|
|
||
|
return !!(IT83XX_ESPI_VWIDX(vw_channel_list[i].index) &
|
||
|
vw_channel_list[i].level_mask);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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)
|
||
|
{
|
||
|
/*
|
||
|
* Common code calls this function to enable VW interrupt of power
|
||
|
* sequence signal.
|
||
|
* IT83xx only use a bit (bit7@IT83XX_ESPI_VWCTRL0) to enable VW
|
||
|
* interrupt.
|
||
|
* VW interrupt will be triggerd with any updated VW index flag
|
||
|
* if this control bit is set.
|
||
|
* So we will always return success here.
|
||
|
*/
|
||
|
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)
|
||
|
{
|
||
|
/*
|
||
|
* We can't disable VW interrupt of power sequence signal
|
||
|
* individually.
|
||
|
*/
|
||
|
return EC_ERROR_UNIMPLEMENTED;
|
||
|
}
|
||
|
|
||
|
/* Configure virtual wire outputs */
|
||
|
static void espi_configure_vw(const struct vw_channel_t *settings,
|
||
|
size_t entries)
|
||
|
{
|
||
|
size_t i;
|
||
|
|
||
|
for (i = 0; i < entries; i++)
|
||
|
IT83XX_ESPI_VWIDX(settings[i].index) |=
|
||
|
(settings[i].level_mask | settings[i].valid_mask);
|
||
|
}
|
||
|
|
||
|
static void espi_vw_host_startup(void)
|
||
|
{
|
||
|
espi_configure_vw(vw_host_startup_setting,
|
||
|
ARRAY_SIZE(vw_host_startup_setting));
|
||
|
}
|
||
|
|
||
|
static void espi_vw_no_isr(uint8_t flag_changed, uint8_t vw_evt)
|
||
|
{
|
||
|
CPRINTS("espi VW interrupt event is ignored! (bit%d at VWCTRL1)",
|
||
|
vw_evt);
|
||
|
}
|
||
|
|
||
|
#ifndef CONFIG_CHIPSET_GEMINILAKE
|
||
|
static void espi_vw_idx41_isr(uint8_t flag_changed, uint8_t vw_evt)
|
||
|
{
|
||
|
if (flag_changed & VW_LEVEL_FIELD(VW_IDX_41_SUS_WARN))
|
||
|
espi_vw_set_wire(VW_SUS_ACK, espi_vw_get_wire(VW_SUS_WARN_L));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static void espi_vw_idx7_isr(uint8_t flag_changed, uint8_t vw_evt)
|
||
|
{
|
||
|
if (flag_changed & VW_LEVEL_FIELD(VW_IDX_7_HOST_RST_WARN))
|
||
|
espi_vw_set_wire(VW_HOST_RST_ACK,
|
||
|
espi_vw_get_wire(VW_HOST_RST_WARN));
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_CHIPSET_RESET_HOOK
|
||
|
static void espi_chipset_reset(void)
|
||
|
{
|
||
|
hook_notify(HOOK_CHIPSET_RESET);
|
||
|
}
|
||
|
DECLARE_DEFERRED(espi_chipset_reset);
|
||
|
#endif
|
||
|
|
||
|
static void espi_vw_idx3_isr(uint8_t flag_changed, uint8_t vw_evt)
|
||
|
{
|
||
|
if (flag_changed & VW_LEVEL_FIELD(VW_IDX_3_PLTRST)) {
|
||
|
int pltrst = espi_vw_get_wire(VW_PLTRST_L);
|
||
|
|
||
|
if (pltrst) {
|
||
|
espi_vw_host_startup();
|
||
|
} else {
|
||
|
#ifdef CONFIG_CHIPSET_RESET_HOOK
|
||
|
hook_call_deferred(&espi_chipset_reset_data, MSEC);
|
||
|
#endif
|
||
|
/* Store port 80 reset event */
|
||
|
port_80_write(PORT_80_EVENT_RESET);
|
||
|
}
|
||
|
|
||
|
CPRINTS("VW PLTRST_L %sasserted", pltrst ? "de" : "");
|
||
|
}
|
||
|
|
||
|
if (flag_changed & VW_LEVEL_FIELD(VW_IDX_3_OOB_RST_WARN))
|
||
|
espi_vw_set_wire(VW_OOB_RST_ACK,
|
||
|
espi_vw_get_wire(VW_OOB_RST_WARN));
|
||
|
}
|
||
|
|
||
|
static void espi_vw_idx2_isr(uint8_t flag_changed, uint8_t vw_evt)
|
||
|
{
|
||
|
if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S3))
|
||
|
power_signal_interrupt(VW_SLP_S3_L);
|
||
|
if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S4))
|
||
|
power_signal_interrupt(VW_SLP_S4_L);
|
||
|
if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S5))
|
||
|
power_signal_interrupt(VW_SLP_S5_L);
|
||
|
}
|
||
|
|
||
|
struct vw_interrupt_t {
|
||
|
void (*vw_isr)(uint8_t flag_changed, uint8_t vw_evt);
|
||
|
uint8_t vw_index;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* The ISR of espi VW interrupt in array needs to match bit order in
|
||
|
* IT83XX_ESPI_VWCTRL1 register.
|
||
|
*/
|
||
|
#ifdef CONFIG_CHIPSET_GEMINILAKE
|
||
|
static const struct vw_interrupt_t vw_isr_list[] = {
|
||
|
[0] = {espi_vw_idx2_isr, ESPI_SYSTEM_EVENT_VW_IDX_2},
|
||
|
[1] = {espi_vw_idx3_isr, ESPI_SYSTEM_EVENT_VW_IDX_3},
|
||
|
[2] = {espi_vw_idx7_isr, ESPI_SYSTEM_EVENT_VW_IDX_7},
|
||
|
[3] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_41},
|
||
|
[4] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_42},
|
||
|
[5] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_43},
|
||
|
[6] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_44},
|
||
|
[7] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_47},
|
||
|
};
|
||
|
#else
|
||
|
static const struct vw_interrupt_t vw_isr_list[] = {
|
||
|
[0] = {espi_vw_idx2_isr, ESPI_SYSTEM_EVENT_VW_IDX_2},
|
||
|
[1] = {espi_vw_idx3_isr, ESPI_SYSTEM_EVENT_VW_IDX_3},
|
||
|
[2] = {espi_vw_idx7_isr, ESPI_SYSTEM_EVENT_VW_IDX_7},
|
||
|
[3] = {espi_vw_idx41_isr, ESPI_SYSTEM_EVENT_VW_IDX_41},
|
||
|
[4] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_42},
|
||
|
[5] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_43},
|
||
|
[6] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_44},
|
||
|
[7] = {espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_47},
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* This is used to record the previous VW valid / level field state to discover
|
||
|
* changes. Then do following sequence only when state is changed.
|
||
|
*/
|
||
|
static uint8_t vw_index_flag[ARRAY_SIZE(vw_isr_list)];
|
||
|
|
||
|
void espi_vw_interrupt(void)
|
||
|
{
|
||
|
int i;
|
||
|
uint8_t vwidx_updated = IT83XX_ESPI_VWCTRL1;
|
||
|
|
||
|
#ifdef IT83XX_ESPI_VWCTRL1_WRITE_FF_CLEAR
|
||
|
/* For IT8320BX, we have to write 0xff to clear pending bit.*/
|
||
|
IT83XX_ESPI_VWCTRL1 = 0xff;
|
||
|
#else
|
||
|
/* write-1 to clear */
|
||
|
IT83XX_ESPI_VWCTRL1 = vwidx_updated;
|
||
|
#endif
|
||
|
task_clear_pending_irq(IT83XX_IRQ_ESPI_VW);
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(vw_isr_list); i++) {
|
||
|
if (vwidx_updated & BIT(i)) {
|
||
|
uint8_t idx_flag;
|
||
|
|
||
|
idx_flag = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index);
|
||
|
vw_isr_list[i].vw_isr(vw_index_flag[i] ^ idx_flag, i);
|
||
|
vw_index_flag[i] = idx_flag;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void espi_reset_vw_index_flags(void)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* reset vw_index_flag */
|
||
|
for (i = 0; i < ARRAY_SIZE(vw_isr_list); i++)
|
||
|
vw_index_flag[i] = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index);
|
||
|
}
|
||
|
|
||
|
#ifdef IT83XX_ESPI_RESET_MODULE_BY_FW
|
||
|
void __ram_code espi_fw_reset_module(void)
|
||
|
{
|
||
|
/*
|
||
|
* (b/111480168): Force a reset of logic VCC domain in EC. This will
|
||
|
* reset both LPC and eSPI blocks. The IT8320DX spec describes the
|
||
|
* purpose of these bits as deciding whether VCC power status is used as
|
||
|
* an internal "power good" signal. However, toggling this field while
|
||
|
* VCC is applied results in resettig VCC domain logic in EC. This code
|
||
|
* must reside in SRAM to prevent DMA address corruption.
|
||
|
*
|
||
|
* bit[7-6]:
|
||
|
* 00b: The VCC power status is treated as power-off.
|
||
|
* 01b: The VCC power status is treated as power-on.
|
||
|
*/
|
||
|
IT83XX_GCTRL_RSTS = (IT83XX_GCTRL_RSTS & ~0xc0);
|
||
|
IT83XX_GCTRL_RSTS = (IT83XX_GCTRL_RSTS & ~0xc0) | BIT(6);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void espi_reset_pin_asserted_interrupt(enum gpio_signal signal)
|
||
|
{
|
||
|
#ifdef IT83XX_ESPI_RESET_MODULE_BY_FW
|
||
|
espi_fw_reset_module();
|
||
|
#endif
|
||
|
/* reset vw_index_flag when espi_reset# asserted. */
|
||
|
espi_reset_vw_index_flags();
|
||
|
}
|
||
|
|
||
|
static int espi_get_reset_enable_config(void)
|
||
|
{
|
||
|
uint8_t config;
|
||
|
const struct gpio_info *espi_rst = gpio_list + GPIO_ESPI_RESET_L;
|
||
|
|
||
|
/*
|
||
|
* Determine if eSPI HW reset is connected to eiter B7 or D2.
|
||
|
* bit[2-1]:
|
||
|
* 00b: reserved.
|
||
|
* 01b: espi_reset# is enabled on GPB7.
|
||
|
* 10b: espi_reset# is enabled on GPD2.
|
||
|
* 11b: reset is disabled.
|
||
|
*/
|
||
|
if (espi_rst->port == GPIO_D && espi_rst->mask == BIT(2)) {
|
||
|
config = IT83XX_GPIO_GCR_LPC_RST_D2;
|
||
|
} else if (espi_rst->port == GPIO_B && espi_rst->mask == BIT(7)) {
|
||
|
config = IT83XX_GPIO_GCR_LPC_RST_B7;
|
||
|
} else {
|
||
|
config = IT83XX_GPIO_GCR_LPC_RST_DISABLE;
|
||
|
CPRINTS("EC's espi_reset pin is not enabled correctly");
|
||
|
}
|
||
|
|
||
|
return config;
|
||
|
}
|
||
|
|
||
|
static void espi_enable_reset(void)
|
||
|
{
|
||
|
int config = espi_get_reset_enable_config();
|
||
|
|
||
|
#ifdef IT83XX_ESPI_RESET_MODULE_BY_FW
|
||
|
/*
|
||
|
* Need to overwrite the config to ensure that eSPI HW reset is
|
||
|
* disabled. The reset function is instead handled by FW in the
|
||
|
* interrupt handler.
|
||
|
*/
|
||
|
config = IT83XX_GPIO_GCR_LPC_RST_DISABLE;
|
||
|
CPRINTS("EC's espi_reset pin hw auto reset is disabled");
|
||
|
|
||
|
#endif
|
||
|
IT83XX_GPIO_GCR = (IT83XX_GPIO_GCR & ~0x6) |
|
||
|
(config << IT83XX_GPIO_GCR_LPC_RST_POS);
|
||
|
|
||
|
/* enable interrupt of EC's espi_reset pin */
|
||
|
gpio_clear_pending_interrupt(GPIO_ESPI_RESET_L);
|
||
|
gpio_enable_interrupt(GPIO_ESPI_RESET_L);
|
||
|
}
|
||
|
|
||
|
/* Interrupt event of master enables the VW channel. */
|
||
|
static void espi_vw_en_asserted(uint8_t evt)
|
||
|
{
|
||
|
/*
|
||
|
* Configure slave to master virtual wire outputs after receiving
|
||
|
* the event of master enables the VW channel.
|
||
|
*/
|
||
|
espi_configure_vw(en_vw_setting, ARRAY_SIZE(en_vw_setting));
|
||
|
}
|
||
|
|
||
|
/* Interrupt event of master enables the OOB channel. */
|
||
|
static void espi_oob_en_asserted(uint8_t evt)
|
||
|
{
|
||
|
/*
|
||
|
* Configure slave to master virtual wire outputs after receiving
|
||
|
* the event of master enables the OOB channel.
|
||
|
*/
|
||
|
espi_configure_vw(en_oob_setting, ARRAY_SIZE(en_oob_setting));
|
||
|
}
|
||
|
|
||
|
/* Interrupt event of master enables the flash channel. */
|
||
|
static void espi_flash_en_asserted(uint8_t evt)
|
||
|
{
|
||
|
/*
|
||
|
* Configure slave to master virtual wire outputs after receiving
|
||
|
* the event of master enables the flash channel.
|
||
|
*/
|
||
|
espi_configure_vw(en_flash_setting, ARRAY_SIZE(en_flash_setting));
|
||
|
}
|
||
|
|
||
|
static void espi_no_isr(uint8_t evt)
|
||
|
{
|
||
|
CPRINTS("espi interrupt event is ignored! (bit%d at ESGCTRL0)", evt);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The ISR of espi interrupt event in array need to be matched bit order in
|
||
|
* IT83XX_ESPI_ESGCTRL0 register.
|
||
|
*/
|
||
|
static void (*espi_isr[])(uint8_t evt) = {
|
||
|
[0] = espi_no_isr,
|
||
|
[1] = espi_vw_en_asserted,
|
||
|
[2] = espi_oob_en_asserted,
|
||
|
[3] = espi_flash_en_asserted,
|
||
|
[4] = espi_no_isr,
|
||
|
[5] = espi_no_isr,
|
||
|
[6] = espi_no_isr,
|
||
|
[7] = espi_no_isr,
|
||
|
};
|
||
|
|
||
|
void espi_interrupt(void)
|
||
|
{
|
||
|
int i;
|
||
|
/* get espi interrupt events */
|
||
|
uint8_t espi_event = IT83XX_ESPI_ESGCTRL0;
|
||
|
|
||
|
/* write-1 to clear */
|
||
|
IT83XX_ESPI_ESGCTRL0 = espi_event;
|
||
|
/* process espi interrupt events */
|
||
|
for (i = 0; i < ARRAY_SIZE(espi_isr); i++) {
|
||
|
if (espi_event & BIT(i))
|
||
|
espi_isr[i](i);
|
||
|
}
|
||
|
/*
|
||
|
* bit7: the slave has received a peripheral posted/completion.
|
||
|
* This bit indicates the slave has received a packet from eSPI
|
||
|
* peripheral channel. We can check cycle type (bit[3-0] at ESPCTRL0)
|
||
|
* and make corresponding modification if needed.
|
||
|
*/
|
||
|
if (IT83XX_ESPI_ESPCTRL0 & ESPI_INTERRUPT_EVENT_PUT_PC) {
|
||
|
/* write-1-clear to release PC_FREE */
|
||
|
IT83XX_ESPI_ESPCTRL0 = ESPI_INTERRUPT_EVENT_PUT_PC;
|
||
|
CPRINTS("A packet from peripheral channel is ignored!");
|
||
|
}
|
||
|
|
||
|
task_clear_pending_irq(IT83XX_IRQ_ESPI);
|
||
|
}
|
||
|
|
||
|
#ifdef IT83XX_ESPI_INHIBIT_CS_BY_PAD_DISABLED
|
||
|
/* Enable/Disable eSPI pad */
|
||
|
void espi_enable_pad(int enable)
|
||
|
{
|
||
|
if (enable)
|
||
|
/* Enable eSPI pad. */
|
||
|
IT83XX_ESPI_ESGCTRL2 &= ~BIT(6);
|
||
|
else
|
||
|
/* Disable eSPI pad. */
|
||
|
IT83XX_ESPI_ESGCTRL2 |= BIT(6);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void espi_init(void)
|
||
|
{
|
||
|
/*
|
||
|
* bit[2-0], the maximum frequency of operation supported by slave:
|
||
|
* 000b: 20MHz
|
||
|
* 001b: 25MHz
|
||
|
* 010b: 33MHz
|
||
|
* 011b: 50MHz
|
||
|
* 100b: 66MHz
|
||
|
*/
|
||
|
#ifdef IT83XX_ESPI_SLAVE_MAX_FREQ_CONFIGURABLE
|
||
|
IT83XX_ESPI_GCAC1 = (IT83XX_ESPI_GCAC1 & ~0x7) | BIT(2);
|
||
|
#endif
|
||
|
/* reset vw_index_flag at initialization */
|
||
|
espi_reset_vw_index_flags();
|
||
|
|
||
|
/*
|
||
|
* bit[3]: The reset source of PNPCFG is RSTPNP bit in RSTCH
|
||
|
* register and WRST#.
|
||
|
*/
|
||
|
IT83XX_GCTRL_RSTS &= ~BIT(3);
|
||
|
task_clear_pending_irq(IT83XX_IRQ_ESPI_VW);
|
||
|
/* bit7: VW interrupt enable */
|
||
|
IT83XX_ESPI_VWCTRL0 |= BIT(7);
|
||
|
task_enable_irq(IT83XX_IRQ_ESPI_VW);
|
||
|
|
||
|
/* bit7: eSPI interrupt enable */
|
||
|
IT83XX_ESPI_ESGCTRL1 |= BIT(7);
|
||
|
/* bit4: eSPI to WUC enable */
|
||
|
IT83XX_ESPI_ESGCTRL2 |= BIT(4);
|
||
|
task_enable_irq(IT83XX_IRQ_ESPI);
|
||
|
|
||
|
/* enable interrupt and reset from eSPI_reset# */
|
||
|
espi_enable_reset();
|
||
|
}
|