mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 17:11:42 +00:00
We need to change PLL settings if host apply eSPI operating frequency higher than 50MHz, because FND clock is required to be higher than half of operating frequency. BRANCH=none BUG=b:70537592 TEST=Change PLL succeed with chip select is low. Change-Id: Ieba62f33ed024aed7a8e7f4cc48b1398ed781170 Signed-off-by: Dino Li <Dino.Li@ite.com.tw> Reviewed-on: https://chromium-review.googlesource.com/817717 Reviewed-by: Randall Spangler <rspangler@chromium.org>
388 lines
11 KiB
C
388 lines
11 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"
|
|
|
|
#define CHIP_ESPI_VW_INTERRUPT_NUM 8
|
|
|
|
/* 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 at initialization */
|
|
static const struct vw_channel_t vw_init_setting[] = {
|
|
{ESPI_SYSTEM_EVENT_VW_IDX_4,
|
|
VW_LEVEL_FIELD(0),
|
|
VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK)},
|
|
{ESPI_SYSTEM_EVENT_VW_IDX_5,
|
|
VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE),
|
|
VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE)},
|
|
{ESPI_SYSTEM_EVENT_VW_IDX_40,
|
|
VW_LEVEL_FIELD(0),
|
|
VW_VALID_FIELD(VW_IDX_40_SUS_ACK)},
|
|
};
|
|
|
|
/* 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;
|
|
}
|
|
|
|
static void espi_vw_host_startup(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(vw_host_startup_setting); i++)
|
|
IT83XX_ESPI_VWIDX(vw_host_startup_setting[i].index) =
|
|
(vw_host_startup_setting[i].level_mask |
|
|
vw_host_startup_setting[i].valid_mask);
|
|
}
|
|
|
|
static void espi_vw_no_isr(uint8_t flag_changed)
|
|
{
|
|
}
|
|
|
|
static void espi_vw_idx41_isr(uint8_t flag_changed)
|
|
{
|
|
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));
|
|
}
|
|
|
|
static void espi_vw_idx7_isr(uint8_t flag_changed)
|
|
{
|
|
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));
|
|
}
|
|
|
|
static void espi_vw_idx3_isr(uint8_t flag_changed)
|
|
{
|
|
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
|
|
/* Store port 80 reset event */
|
|
port_80_write(PORT_80_EVENT_RESET);
|
|
|
|
CPRINTS("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)
|
|
{
|
|
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_index;
|
|
};
|
|
|
|
static const struct vw_interrupt_t vw_isr_list[CHIP_ESPI_VW_INTERRUPT_NUM] = {
|
|
{espi_vw_idx2_isr, ESPI_SYSTEM_EVENT_VW_IDX_2},
|
|
{espi_vw_idx3_isr, ESPI_SYSTEM_EVENT_VW_IDX_3},
|
|
{espi_vw_idx7_isr, ESPI_SYSTEM_EVENT_VW_IDX_7},
|
|
{espi_vw_idx41_isr, ESPI_SYSTEM_EVENT_VW_IDX_41},
|
|
{espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_42},
|
|
{espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_43},
|
|
{espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_44},
|
|
{espi_vw_no_isr, ESPI_SYSTEM_EVENT_VW_IDX_47},
|
|
};
|
|
|
|
/*
|
|
* 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[CHIP_ESPI_VW_INTERRUPT_NUM];
|
|
|
|
void espi_vw_interrupt(void)
|
|
{
|
|
int i;
|
|
uint8_t vwidx_updated = IT83XX_ESPI_VWCTRL1;
|
|
|
|
/*
|
|
* TODO(b:68918637): write-1 clear bug.
|
|
* for now, we have to write 0xff to clear pending bit.
|
|
*/
|
|
#if 0
|
|
IT83XX_ESPI_VWCTRL1 = vwidx_updated;
|
|
#else
|
|
IT83XX_ESPI_VWCTRL1 = 0xff;
|
|
#endif
|
|
task_clear_pending_irq(IT83XX_IRQ_ESPI_VW);
|
|
|
|
for (i = 0; i < CHIP_ESPI_VW_INTERRUPT_NUM; i++) {
|
|
if (vwidx_updated & (1 << 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);
|
|
vw_index_flag[i] = idx_flag;
|
|
}
|
|
}
|
|
}
|
|
|
|
void espi_interrupt(void)
|
|
{
|
|
}
|
|
|
|
void espi_init(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(vw_init_setting); i++)
|
|
IT83XX_ESPI_VWIDX(vw_init_setting[i].index) =
|
|
(vw_init_setting[i].level_mask |
|
|
vw_init_setting[i].valid_mask);
|
|
|
|
for (i = 0; i < CHIP_ESPI_VW_INTERRUPT_NUM; i++)
|
|
vw_index_flag[i] = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index);
|
|
|
|
/*
|
|
* bit[3]: The reset source of PNPCFG is RSTPNP bit in RSTCH
|
|
* register and WRST#.
|
|
*/
|
|
IT83XX_GCTRL_RSTS &= ~(1 << 3);
|
|
task_clear_pending_irq(IT83XX_IRQ_ESPI_VW);
|
|
/* bit7: VW interrupt enable */
|
|
IT83XX_ESPI_VWCTRL0 |= (1 << 7);
|
|
task_enable_irq(IT83XX_IRQ_ESPI_VW);
|
|
}
|