blob: 837a3fd3412c913ac98c228c034bd58151fb020a [file] [log] [blame]
/*
* Copyright (c) 2020 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nuvoton_npcx_espi
#include <assert.h>
#include <zephyr/drivers/espi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/dt-bindings/espi/npcx_espi.h>
#include <zephyr/kernel.h>
#include <soc.h>
#include "espi_utils.h"
#include "soc_host.h"
#include "soc_miwu.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(espi, CONFIG_ESPI_LOG_LEVEL);
struct espi_npcx_config {
uintptr_t base;
/* clock configuration */
struct npcx_clk_cfg clk_cfg;
/* mapping table between eSPI reset signal and wake-up input */
struct npcx_wui espi_rst_wui;
/* pinmux configuration */
const struct pinctrl_dev_config *pcfg;
};
struct espi_npcx_data {
sys_slist_t callbacks;
uint8_t plt_rst_asserted;
uint8_t espi_rst_asserted;
uint8_t sx_state;
#if defined(CONFIG_ESPI_OOB_CHANNEL)
struct k_sem oob_rx_lock;
#endif
};
/* Driver convenience defines */
#define HAL_INSTANCE(dev) \
((struct espi_reg *)((const struct espi_npcx_config *)(dev)->config)->base)
/* eSPI channels */
#define NPCX_ESPI_CH_PC 0
#define NPCX_ESPI_CH_VW 1
#define NPCX_ESPI_CH_OOB 2
#define NPCX_ESPI_CH_FLASH 3
#define NPCX_ESPI_CH_COUNT 4
#define NPCX_ESPI_HOST_CH_EN(ch) (ch + 4)
/* eSPI max supported frequency */
#define NPCX_ESPI_MAXFREQ_20 0
#define NPCX_ESPI_MAXFREQ_25 1
#define NPCX_ESPI_MAXFREQ_33 2
#define NPCX_ESPI_MAXFREQ_50 3
/* Minimum delay before acknowledging a virtual wire */
#define NPCX_ESPI_VWIRE_ACK_DELAY 10ul /* 10 us */
/* OOB channel maximum payload size */
#define NPCX_ESPI_OOB_MAX_PAYLOAD 64
#define NPCX_OOB_RX_PACKAGE_LEN(hdr) (((hdr & 0xff000000) >> 24) | \
((hdr & 0xf0000) >> 8))
/* eSPI cycle type field for OOB and FLASH channels */
#define ESPI_FLASH_READ_CYCLE_TYPE 0x00
#define ESPI_FLASH_WRITE_CYCLE_TYPE 0x01
#define ESPI_FLASH_ERASE_CYCLE_TYPE 0x02
#define ESPI_OOB_GET_CYCLE_TYPE 0x21
#define ESPI_OOB_TAG 0x00
#define ESPI_OOB_MAX_TIMEOUT 500ul /* 500 ms */
/* eSPI bus interrupt configuration structure and macro function */
struct espi_bus_isr {
uint8_t status_bit; /* bit order in ESPISTS register */
uint8_t int_en_bit; /* bit order in ESPIIE register */
uint8_t wake_en_bit; /* bit order in ESPIWE register */
void (*bus_isr)(const struct device *dev); /* eSPI bus ISR */
};
#define NPCX_ESPI_BUS_INT_ITEM(event, isr) { \
.status_bit = NPCX_ESPISTS_##event, \
.int_en_bit = NPCX_ESPIIE_##event##IE, \
.wake_en_bit = NPCX_ESPIWE_##event##WE, \
.bus_isr = isr }
/* eSPI Virtual Wire Input (Master-to-Slave) signals configuration structure */
struct npcx_vw_in_config {
enum espi_vwire_signal sig; /* Virtual Wire signal */
uint8_t reg_idx; /* register index for VW signal */
uint8_t bitmask; /* VW signal bits-mask */
struct npcx_wui vw_wui; /* WUI mapping in MIWU modules for VW signal */
};
/* eSPI Virtual Wire Output (Slave-to-Master) signals configuration structure */
struct npcx_vw_out_config {
enum espi_vwire_signal sig; /* Virtual Wire signal */
uint8_t reg_idx; /* register index for VW signal */
uint8_t bitmask; /* VW signal bits-mask */
};
/*
* eSPI VW input/Output signal configuration tables. Please refer
* npcxn-espi-vws-map.dtsi device tree file for more detail.
*/
static const struct npcx_vw_in_config vw_in_tbl[] = {
/* index 02h (In) */
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SLP_S3, vw_slp_s3),
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SLP_S4, vw_slp_s4),
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SLP_S5, vw_slp_s5),
/* index 03h (In) */
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SUS_STAT, vw_sus_stat),
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_PLTRST, vw_plt_rst),
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_OOB_RST_WARN, vw_oob_rst_warn),
/* index 07h (In) */
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_HOST_RST_WARN, vw_host_rst_warn),
/* index 41h (In) */
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SUS_WARN, vw_sus_warn),
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK, vw_sus_pwrdn_ack),
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SLP_A, vw_slp_a),
/* index 42h (In) */
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SLP_LAN, vw_slp_lan),
NPCX_DT_VW_IN_CONF(ESPI_VWIRE_SIGNAL_SLP_WLAN, vw_slp_wlan),
};
static const struct npcx_vw_out_config vw_out_tbl[] = {
/* index 04h (Out) */
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_OOB_RST_ACK, vw_oob_rst_ack),
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_WAKE, vw_wake),
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_PME, vw_pme),
/* index 05h (Out) */
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, vw_slv_boot_done),
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_ERR_FATAL, vw_err_fatal),
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_ERR_NON_FATAL, vw_err_non_fatal),
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_SLV_BOOT_STS,
vw_slv_boot_sts_with_done),
/* index 06h (Out) */
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_SCI, vw_sci),
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_SMI, vw_smi),
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_HOST_RST_ACK, vw_host_rst_ack),
/* index 40h (Out) */
NPCX_DT_VW_OUT_CONF(ESPI_VWIRE_SIGNAL_SUS_ACK, vw_sus_ack),
};
/* Callbacks for eSPI bus reset and Virtual Wire signals. */
static struct miwu_dev_callback espi_rst_callback;
static struct miwu_dev_callback vw_in_callback[ARRAY_SIZE(vw_in_tbl)];
/* eSPI VW service function forward declarations */
static int espi_npcx_receive_vwire(const struct device *dev,
enum espi_vwire_signal signal, uint8_t *level);
static int espi_npcx_send_vwire(const struct device *dev,
enum espi_vwire_signal signal, uint8_t level);
static void espi_vw_send_bootload_done(const struct device *dev);
/* eSPI local initialization functions */
static void espi_init_wui_callback(const struct device *dev,
struct miwu_dev_callback *callback, const struct npcx_wui *wui,
miwu_dev_callback_handler_t handler)
{
/* VW signal which has no wake-up input source */
if (wui->table == NPCX_MIWU_TABLE_NONE)
return;
/* Install callback function */
npcx_miwu_init_dev_callback(callback, wui, handler, dev);
npcx_miwu_manage_dev_callback(callback, 1);
/* Configure MIWU setting and enable its interrupt */
npcx_miwu_interrupt_configure(wui, NPCX_MIWU_MODE_EDGE,
NPCX_MIWU_TRIG_BOTH);
npcx_miwu_irq_enable(wui);
}
/* eSPI local bus interrupt service functions */
static void espi_bus_err_isr(const struct device *dev)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
uint32_t err = inst->ESPIERR;
LOG_ERR("eSPI Bus Error %08X", err);
/* Clear error status bits */
inst->ESPIERR = err;
}
static void espi_bus_inband_rst_isr(const struct device *dev)
{
ARG_UNUSED(dev);
LOG_DBG("%s issued", __func__);
}
static void espi_bus_reset_isr(const struct device *dev)
{
ARG_UNUSED(dev);
LOG_DBG("%s issued", __func__);
/* Do nothing! This signal is handled in ESPI_RST VW signal ISR */
}
static void espi_bus_cfg_update_isr(const struct device *dev)
{
int chan;
struct espi_reg *const inst = HAL_INSTANCE(dev);
struct espi_npcx_data *const data = dev->data;
struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY,
.evt_details = 0,
.evt_data = 0 };
/* If host enable bits are not sync with ready bits on slave side. */
uint8_t chg_mask = GET_FIELD(inst->ESPICFG, NPCX_ESPICFG_HCHANS_FIELD)
^ GET_FIELD(inst->ESPICFG, NPCX_ESPICFG_CHANS_FIELD);
chg_mask &= (ESPI_CHANNEL_VWIRE | ESPI_CHANNEL_OOB |
ESPI_CHANNEL_FLASH);
LOG_DBG("ESPI CFG Change Updated! 0x%02X", chg_mask);
/*
* If host enable/disable channel for VW/OOB/FLASH, EC should follow
* except Peripheral channel. It is handled after receiving PLTRST
* event separately.
*/
for (chan = NPCX_ESPI_CH_VW; chan < NPCX_ESPI_CH_COUNT; chan++) {
/* Channel ready bit isn't sync with enabled bit on host side */
if (chg_mask & BIT(chan)) {
evt.evt_data = IS_BIT_SET(inst->ESPICFG,
NPCX_ESPI_HOST_CH_EN(chan));
evt.evt_details = BIT(chan);
if (evt.evt_data)
inst->ESPICFG |= BIT(chan);
else
inst->ESPICFG &= ~BIT(chan);
espi_send_callbacks(&data->callbacks, dev, evt);
}
}
LOG_DBG("ESPI CFG EC Updated! 0x%02X", GET_FIELD(inst->ESPICFG,
NPCX_ESPICFG_CHANS_FIELD));
/* If VW channel is enabled and ready, send bootload done VW signal */
if ((chg_mask & BIT(NPCX_ESPI_CH_VW)) && IS_BIT_SET(inst->ESPICFG,
NPCX_ESPI_HOST_CH_EN(NPCX_ESPI_CH_VW))) {
espi_vw_send_bootload_done(dev);
}
}
#if defined(CONFIG_ESPI_OOB_CHANNEL)
static void espi_bus_oob_rx_isr(const struct device *dev)
{
struct espi_npcx_data *const data = dev->data;
LOG_DBG("%s", __func__);
k_sem_give(&data->oob_rx_lock);
}
#endif
const struct espi_bus_isr espi_bus_isr_tbl[] = {
NPCX_ESPI_BUS_INT_ITEM(BERR, espi_bus_err_isr),
NPCX_ESPI_BUS_INT_ITEM(IBRST, espi_bus_inband_rst_isr),
NPCX_ESPI_BUS_INT_ITEM(ESPIRST, espi_bus_reset_isr),
NPCX_ESPI_BUS_INT_ITEM(CFGUPD, espi_bus_cfg_update_isr),
#if defined(CONFIG_ESPI_OOB_CHANNEL)
NPCX_ESPI_BUS_INT_ITEM(OOBRX, espi_bus_oob_rx_isr),
#endif
};
static void espi_bus_generic_isr(const struct device *dev)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
int i;
uint32_t mask, status;
/*
* 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 = inst->ESPIIE | (1 << NPCX_ESPISTS_VWUPDW);
status = inst->ESPISTS & mask;
/* Clear pending bits of status register first */
inst->ESPISTS = status;
LOG_DBG("%s: 0x%08X", __func__, status);
for (i = 0; i < ARRAY_SIZE(espi_bus_isr_tbl); i++) {
struct espi_bus_isr entry = espi_bus_isr_tbl[i];
if (status & BIT(entry.status_bit)) {
if (entry.bus_isr != NULL) {
entry.bus_isr(dev);
}
}
}
}
/* eSPI local virtual-wire service functions */
static void espi_vw_config_input(const struct device *dev,
const struct npcx_vw_in_config *config_in)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
int idx = config_in->reg_idx;
/* IE & WE bits are already set? */
if (IS_BIT_SET(inst->VWEVMS[idx], NPCX_VWEVMS_IE) &&
IS_BIT_SET(inst->VWEVMS[idx], NPCX_VWEVMS_WE))
return;
/* Set IE & WE bits in VWEVMS */
inst->VWEVMS[idx] |= BIT(NPCX_VWEVMS_IE) | BIT(NPCX_VWEVMS_WE);
LOG_DBG("VWEVMS%d 0x%08X", idx, inst->VWEVMS[idx]);
}
static void espi_vw_config_output(const struct device *dev,
const struct npcx_vw_out_config *config_out)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
int idx = config_out->reg_idx;
uint8_t valid = GET_FIELD(inst->VWEVSM[idx], NPCX_VWEVSM_VALID);
/* Set valid bits for vw signal which we have declared in table. */
valid |= config_out->bitmask;
SET_FIELD(inst->VWEVSM[idx], NPCX_VWEVSM_VALID, valid);
/*
* Turn off hardware-wire feature which generates VW events that
* connected to hardware signals. We will set it manually by software.
*/
SET_FIELD(inst->VWEVSM[idx], NPCX_VWEVSM_HW_WIRE, 0);
LOG_DBG("VWEVSM%d 0x%08X", idx, inst->VWEVSM[idx]);
}
static void espi_vw_notify_system_state(const struct device *dev,
enum espi_vwire_signal signal)
{
struct espi_npcx_data *const data = dev->data;
struct espi_event evt = { ESPI_BUS_EVENT_VWIRE_RECEIVED, 0, 0 };
uint8_t wire = 0;
espi_npcx_receive_vwire(dev, signal, &wire);
if (!wire) {
data->sx_state = signal;
}
evt.evt_details = signal;
evt.evt_data = wire;
espi_send_callbacks(&data->callbacks, dev, evt);
}
static void espi_vw_notify_host_warning(const struct device *dev,
enum espi_vwire_signal signal)
{
uint8_t wire;
espi_npcx_receive_vwire(dev, signal, &wire);
k_busy_wait(NPCX_ESPI_VWIRE_ACK_DELAY);
switch (signal) {
case ESPI_VWIRE_SIGNAL_HOST_RST_WARN:
espi_npcx_send_vwire(dev,
ESPI_VWIRE_SIGNAL_HOST_RST_ACK,
wire);
break;
case ESPI_VWIRE_SIGNAL_SUS_WARN:
espi_npcx_send_vwire(dev, ESPI_VWIRE_SIGNAL_SUS_ACK,
wire);
break;
case ESPI_VWIRE_SIGNAL_OOB_RST_WARN:
espi_npcx_send_vwire(dev, ESPI_VWIRE_SIGNAL_OOB_RST_ACK,
wire);
break;
default:
break;
}
}
static void espi_vw_notify_plt_rst(const struct device *dev)
{
struct espi_npcx_data *const data = dev->data;
struct espi_reg *const inst = HAL_INSTANCE(dev);
struct espi_event evt = { ESPI_BUS_EVENT_VWIRE_RECEIVED,
ESPI_VWIRE_SIGNAL_PLTRST, 0
};
uint8_t wire = 0;
espi_npcx_receive_vwire(dev, ESPI_VWIRE_SIGNAL_PLTRST, &wire);
LOG_DBG("VW_PLT_RST is %d!", wire);
if (wire) {
/* Set Peripheral Channel ready when PLTRST is de-asserted */
inst->ESPICFG |= BIT(NPCX_ESPICFG_PCHANEN);
/* Configure all host sub-modules in host domain */
npcx_host_init_subs_host_domain();
}
/* PLT_RST will be received several times */
if (wire != data->plt_rst_asserted) {
data->plt_rst_asserted = wire;
evt.evt_data = wire;
espi_send_callbacks(&data->callbacks, dev, evt);
}
}
static void espi_vw_send_bootload_done(const struct device *dev)
{
int ret;
uint8_t boot_done;
ret = espi_npcx_receive_vwire(dev,
ESPI_VWIRE_SIGNAL_SLV_BOOT_DONE, &boot_done);
LOG_DBG("%s: %d", __func__, boot_done);
if (!ret && !boot_done) {
/* Send slave boot status bit with done bit at the same time. */
espi_npcx_send_vwire(dev, ESPI_VWIRE_SIGNAL_SLV_BOOT_STS, 1);
}
}
static void espi_vw_generic_isr(const struct device *dev, struct npcx_wui *wui)
{
int idx;
enum espi_vwire_signal signal;
LOG_DBG("%s: WUI %d %d %d", __func__, wui->table, wui->group, wui->bit);
for (idx = 0; idx < ARRAY_SIZE(vw_in_tbl); idx++) {
if (wui->table == vw_in_tbl[idx].vw_wui.table &&
wui->group == vw_in_tbl[idx].vw_wui.group &&
wui->bit == vw_in_tbl[idx].vw_wui.bit) {
break;
}
}
if (idx == ARRAY_SIZE(vw_in_tbl)) {
LOG_ERR("Unknown VW event! %d %d %d", wui->table,
wui->group, wui->bit);
return;
}
signal = vw_in_tbl[idx].sig;
if (signal == ESPI_VWIRE_SIGNAL_SLP_S3
|| signal == ESPI_VWIRE_SIGNAL_SLP_S4
|| signal == ESPI_VWIRE_SIGNAL_SLP_S5
|| signal == ESPI_VWIRE_SIGNAL_SLP_A) {
espi_vw_notify_system_state(dev, signal);
} else if (signal == ESPI_VWIRE_SIGNAL_HOST_RST_WARN
|| signal == ESPI_VWIRE_SIGNAL_SUS_WARN
|| signal == ESPI_VWIRE_SIGNAL_OOB_RST_WARN) {
espi_vw_notify_host_warning(dev, signal);
} else if (signal == ESPI_VWIRE_SIGNAL_PLTRST) {
espi_vw_notify_plt_rst(dev);
}
}
static void espi_vw_espi_rst_isr(const struct device *dev, struct npcx_wui *wui)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
struct espi_npcx_data *const data = dev->data;
struct espi_event evt = { ESPI_BUS_RESET, 0, 0 };
data->espi_rst_asserted = !IS_BIT_SET(inst->ESPISTS,
NPCX_ESPISTS_ESPIRST_LVL);
LOG_DBG("eSPI RST asserted is %d!", data->espi_rst_asserted);
evt.evt_data = data->espi_rst_asserted;
espi_send_callbacks(&data->callbacks, dev, evt);
}
/* eSPI api functions */
static int espi_npcx_configure(const struct device *dev, struct espi_cfg *cfg)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
uint8_t max_freq = 0;
uint8_t cur_io_mode, io_mode = 0;
/* Configure eSPI frequency */
switch (cfg->max_freq) {
case 20:
max_freq = NPCX_ESPI_MAXFREQ_20;
break;
case 25:
max_freq = NPCX_ESPI_MAXFREQ_25;
break;
case 33:
max_freq = NPCX_ESPI_MAXFREQ_33;
break;
case 50:
max_freq = NPCX_ESPI_MAXFREQ_50;
break;
default:
return -EINVAL;
}
SET_FIELD(inst->ESPICFG, NPCX_ESPICFG_MAXFREQ_FIELD, max_freq);
/* Configure eSPI IO mode */
io_mode = (cfg->io_caps >> 1);
if (io_mode > 3) {
return -EINVAL;
}
cur_io_mode = GET_FIELD(inst->ESPICFG, NPCX_ESPICFG_IOMODE_FIELD);
if (io_mode != cur_io_mode) {
SET_FIELD(inst->ESPICFG, NPCX_ESPICFG_IOMODE_FIELD, io_mode);
}
/* Configure eSPI supported channels */
if (cfg->channel_caps & ESPI_CHANNEL_PERIPHERAL)
inst->ESPICFG |= BIT(NPCX_ESPICFG_PCCHN_SUPP);
if (cfg->channel_caps & ESPI_CHANNEL_VWIRE)
inst->ESPICFG |= BIT(NPCX_ESPICFG_VWCHN_SUPP);
if (cfg->channel_caps & ESPI_CHANNEL_OOB)
inst->ESPICFG |= BIT(NPCX_ESPICFG_OOBCHN_SUPP);
if (cfg->channel_caps & ESPI_CHANNEL_FLASH)
inst->ESPICFG |= BIT(NPCX_ESPICFG_FLASHCHN_SUPP);
LOG_DBG("%s: %d %d ESPICFG: 0x%08X", __func__,
max_freq, io_mode, inst->ESPICFG);
return 0;
}
static bool espi_npcx_channel_ready(const struct device *dev,
enum espi_channel ch)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
bool sts;
switch (ch) {
case ESPI_CHANNEL_PERIPHERAL:
sts = IS_BIT_SET(inst->ESPICFG, NPCX_ESPICFG_PCHANEN);
break;
case ESPI_CHANNEL_VWIRE:
sts = IS_BIT_SET(inst->ESPICFG, NPCX_ESPICFG_VWCHANEN);
break;
case ESPI_CHANNEL_OOB:
sts = IS_BIT_SET(inst->ESPICFG, NPCX_ESPICFG_OOBCHANEN);
break;
case ESPI_CHANNEL_FLASH:
sts = IS_BIT_SET(inst->ESPICFG, NPCX_ESPICFG_FLASHCHANEN);
break;
default:
sts = false;
break;
}
return sts;
}
static int espi_npcx_send_vwire(const struct device *dev,
enum espi_vwire_signal signal, uint8_t level)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
uint8_t reg_idx, bitmask, sig_idx, val = 0;
/* Find signal in VW output table */
for (sig_idx = 0; sig_idx < ARRAY_SIZE(vw_out_tbl); sig_idx++)
if (vw_out_tbl[sig_idx].sig == signal)
break;
if (sig_idx == ARRAY_SIZE(vw_out_tbl)) {
LOG_ERR("%s signal %d is invalid", __func__, signal);
return -EIO;
}
reg_idx = vw_out_tbl[sig_idx].reg_idx;
bitmask = vw_out_tbl[sig_idx].bitmask;
/* Get wire field and set/clear wire bit */
val = GET_FIELD(inst->VWEVSM[reg_idx], NPCX_VWEVSM_WIRE);
if (level)
val |= bitmask;
else
val &= ~bitmask;
SET_FIELD(inst->VWEVSM[reg_idx], NPCX_VWEVSM_WIRE, val);
LOG_DBG("Send VW: VWEVSM%d 0x%08X", reg_idx, inst->VWEVSM[reg_idx]);
return 0;
}
static int espi_npcx_receive_vwire(const struct device *dev,
enum espi_vwire_signal signal, uint8_t *level)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
uint8_t reg_idx, bitmask, sig_idx, val;
/* Find signal in VW input table */
for (sig_idx = 0; sig_idx < ARRAY_SIZE(vw_in_tbl); sig_idx++)
if (vw_in_tbl[sig_idx].sig == signal) {
reg_idx = vw_in_tbl[sig_idx].reg_idx;
bitmask = vw_in_tbl[sig_idx].bitmask;
val = GET_FIELD(inst->VWEVMS[reg_idx],
NPCX_VWEVMS_WIRE);
val &= GET_FIELD(inst->VWEVMS[reg_idx],
NPCX_VWEVMS_VALID);
*level = !!(val & bitmask);
return 0;
}
/* Find signal in VW output table */
for (sig_idx = 0; sig_idx < ARRAY_SIZE(vw_out_tbl); sig_idx++)
if (vw_out_tbl[sig_idx].sig == signal) {
reg_idx = vw_out_tbl[sig_idx].reg_idx;
bitmask = vw_out_tbl[sig_idx].bitmask;
val = GET_FIELD(inst->VWEVSM[reg_idx],
NPCX_VWEVSM_WIRE);
val &= GET_FIELD(inst->VWEVSM[reg_idx],
NPCX_VWEVSM_VALID);
*level = !!(val & bitmask);
return 0;
}
LOG_ERR("%s Out of index %d", __func__, signal);
return -EIO;
}
static int espi_npcx_manage_callback(const struct device *dev,
struct espi_callback *callback, bool set)
{
struct espi_npcx_data *const data = dev->data;
return espi_manage_callback(&data->callbacks, callback, set);
}
static int espi_npcx_read_lpc_request(const struct device *dev,
enum lpc_peripheral_opcode op,
uint32_t *data)
{
ARG_UNUSED(dev);
return npcx_host_periph_read_request(op, data);
}
static int espi_npcx_write_lpc_request(const struct device *dev,
enum lpc_peripheral_opcode op,
uint32_t *data)
{
ARG_UNUSED(dev);
return npcx_host_periph_write_request(op, data);
}
#if defined(CONFIG_ESPI_OOB_CHANNEL)
static int espi_npcx_send_oob(const struct device *dev,
struct espi_oob_packet *pckt)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
uint8_t *oob_buf = pckt->buf;
int sz_oob_tx = pckt->len;
int idx_tx_buf;
uint32_t oob_data;
/* Check out of OOB transmitted buffer size */
if (sz_oob_tx > NPCX_ESPI_OOB_MAX_PAYLOAD) {
LOG_ERR("Out of OOB transmitted buffer: %d", sz_oob_tx);
return -EINVAL;
}
/* Check OOB Transmit Queue is empty? */
if (IS_BIT_SET(inst->OOBCTL, NPCX_OOBCTL_OOB_AVAIL)) {
LOG_ERR("OOB channel is busy");
return -EBUSY;
}
/*
* GET_OOB header (first 4 bytes) in npcx 32-bits tx buffer
*
* [24:31] - LEN[0:7] Data length of GET_OOB request package
* [20:23] - TAG Tag of GET_OOB
* [16:19] - LEN[8:11] Ignore it since max payload is 64 bytes
* [8:15] - CYCLE_TYPE Cycle type of GET_OOB
* [0:7] - SZ_PACK Package size plus 3 bytes header. (Npcx only)
*/
inst->OOBTXBUF[0] = (sz_oob_tx + 3)
| (ESPI_OOB_GET_CYCLE_TYPE << 8)
| (ESPI_OOB_TAG << 16)
| (sz_oob_tx << 24);
/* Write GET_OOB data into 32-bits tx buffer in little endian */
for (idx_tx_buf = 0; idx_tx_buf < sz_oob_tx/4; idx_tx_buf++,
oob_buf += 4)
inst->OOBTXBUF[idx_tx_buf + 1] = oob_buf[0]
| (oob_buf[1] << 8)
| (oob_buf[2] << 16)
| (oob_buf[3] << 24);
/* Write remaining bytes of package */
if (sz_oob_tx % 4) {
int i;
oob_data = 0;
for (i = 0; i < sz_oob_tx % 4; i++)
oob_data |= (oob_buf[i] << (8 * i));
inst->OOBTXBUF[idx_tx_buf + 1] = oob_data;
}
/*
* Notify host a new OOB packet is ready. Please don't write OOB_FREE
* to 1 at the same tiem in case clear it unexpectedly.
*/
oob_data = inst->OOBCTL & ~(BIT(NPCX_OOBCTL_OOB_FREE));
oob_data |= BIT(NPCX_OOBCTL_OOB_AVAIL);
inst->OOBCTL = oob_data;
while (IS_BIT_SET(inst->OOBCTL, NPCX_OOBCTL_OOB_AVAIL))
;
LOG_DBG("%s issued!!", __func__);
return 0;
}
static int espi_npcx_receive_oob(const struct device *dev,
struct espi_oob_packet *pckt)
{
struct espi_reg *const inst = HAL_INSTANCE(dev);
struct espi_npcx_data *const data = dev->data;
uint8_t *oob_buf = pckt->buf;
uint32_t oob_data;
int idx_rx_buf, sz_oob_rx, ret;
/* Check eSPI bus status first */
if (IS_BIT_SET(inst->ESPISTS, NPCX_ESPISTS_BERR)) {
LOG_ERR("%s: eSPI Bus Error: 0x%08X", __func__, inst->ESPIERR);
return -EIO;
}
/* Notify host that OOB received buffer is free now. */
inst->OOBCTL |= BIT(NPCX_OOBCTL_OOB_FREE);
/* Wait until get oob package or timeout */
ret = k_sem_take(&data->oob_rx_lock, K_MSEC(ESPI_OOB_MAX_TIMEOUT));
if (ret == -EAGAIN) {
LOG_ERR("%s: Timeout", __func__);
return -ETIMEDOUT;
}
/*
* PUT_OOB header (first 4 bytes) in npcx 32-bits rx buffer
*
* [24:31] - LEN[0:7] Data length of PUT_OOB request package
* [20:23] - TAG Tag of PUT_OOB
* [16:19] - LEN[8:11] Data length of PUT_OOB request package
* [8:15] - CYCLE_TYPE Cycle type of PUT_OOB
* [0:7] - SZ_PACK Reserved. (Npcx only)
*/
oob_data = inst->OOBRXBUF[0];
/* Get received package length first */
sz_oob_rx = NPCX_OOB_RX_PACKAGE_LEN(oob_data);
/* Check OOB received buffer size */
if (sz_oob_rx > NPCX_ESPI_OOB_MAX_PAYLOAD) {
LOG_ERR("Out of OOB received buffer: %d", sz_oob_rx);
return -EINVAL;
}
/* Set received size to package structure */
pckt->len = sz_oob_rx;
/* Read PUT_OOB data into 32-bits rx buffer in little endian */
for (idx_rx_buf = 0; idx_rx_buf < sz_oob_rx/4; idx_rx_buf++) {
oob_data = inst->OOBRXBUF[idx_rx_buf + 1];
*(oob_buf++) = oob_data & 0xFF;
*(oob_buf++) = (oob_data >> 8) & 0xFF;
*(oob_buf++) = (oob_data >> 16) & 0xFF;
*(oob_buf++) = (oob_data >> 24) & 0xFF;
}
/* Read remaining bytes of package */
if (sz_oob_rx % 4) {
int i;
oob_data = inst->OOBRXBUF[idx_rx_buf + 1];
for (i = 0; i < sz_oob_rx % 4; i++)
*(oob_buf++) = (oob_data >> (8 * i)) & 0xFF;
}
return 0;
}
#endif
/* Platform specific espi module functions */
void npcx_espi_enable_interrupts(const struct device *dev)
{
ARG_UNUSED(dev);
/* Enable eSPI bus interrupt */
irq_enable(DT_INST_IRQN(0));
/* Turn on all VW inputs' MIWU interrupts */
for (int idx = 0; idx < ARRAY_SIZE(vw_in_tbl); idx++) {
npcx_miwu_irq_enable(&(vw_in_tbl[idx].vw_wui));
}
}
void npcx_espi_disable_interrupts(const struct device *dev)
{
ARG_UNUSED(dev);
/* Disable eSPI bus interrupt */
irq_disable(DT_INST_IRQN(0));
/* Turn off all VW inputs' MIWU interrupts */
for (int idx = 0; idx < ARRAY_SIZE(vw_in_tbl); idx++) {
npcx_miwu_irq_disable(&(vw_in_tbl[idx].vw_wui));
}
}
/* eSPI driver registration */
static int espi_npcx_init(const struct device *dev);
static const struct espi_driver_api espi_npcx_driver_api = {
.config = espi_npcx_configure,
.get_channel_status = espi_npcx_channel_ready,
.send_vwire = espi_npcx_send_vwire,
.receive_vwire = espi_npcx_receive_vwire,
.manage_callback = espi_npcx_manage_callback,
.read_lpc_request = espi_npcx_read_lpc_request,
.write_lpc_request = espi_npcx_write_lpc_request,
#if defined(CONFIG_ESPI_OOB_CHANNEL)
.send_oob = espi_npcx_send_oob,
.receive_oob = espi_npcx_receive_oob,
#endif
};
static struct espi_npcx_data espi_npcx_data;
PINCTRL_DT_INST_DEFINE(0);
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
"only one 'nuvoton_npcx_espi' compatible node may be present");
static const struct espi_npcx_config espi_npcx_config = {
.base = DT_INST_REG_ADDR(0),
.espi_rst_wui = NPCX_DT_WUI_ITEM_BY_NAME(0, espi_rst_wui),
.clk_cfg = NPCX_DT_CLK_CFG_ITEM(0),
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
};
DEVICE_DT_INST_DEFINE(0, &espi_npcx_init, NULL,
&espi_npcx_data, &espi_npcx_config,
PRE_KERNEL_2, CONFIG_ESPI_INIT_PRIORITY,
&espi_npcx_driver_api);
static int espi_npcx_init(const struct device *dev)
{
const struct espi_npcx_config *const config = dev->config;
struct espi_npcx_data *const data = dev->data;
struct espi_reg *const inst = HAL_INSTANCE(dev);
const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
int i, ret;
/* If booter doesn't set the host interface type */
if (!NPCX_BOOTER_IS_HIF_TYPE_SET()) {
npcx_host_interface_sel(NPCX_HIF_TYPE_ESPI_SHI);
}
/* Turn on eSPI device clock first */
ret = clock_control_on(clk_dev, (clock_control_subsys_t *)
&config->clk_cfg);
if (ret < 0) {
LOG_ERR("Turn on eSPI clock fail %d", ret);
return ret;
}
if (IS_ENABLED(CONFIG_ESPI_NPCX_BYPASS_CH_ENABLE_FATAL_ERROR)) {
/* Enable the access to the NPCX_ONLY_ESPI_REG2 register */
inst->NPCX_ONLY_ESPI_REG1 = NPCX_ONLY_ESPI_REG1_UNLOCK_REG2;
inst->NPCX_ONLY_ESPI_REG2 &= ~BIT(NPCX_ONLY_ESPI_REG2_TRANS_END_CONFIG);
/* Disable the access to the NPCX_ONLY_ESPI_REG2 register */
inst->NPCX_ONLY_ESPI_REG1 = NPCX_ONLY_ESPI_REG1_LOCK_REG2;
}
/* Enable events which share the same espi bus interrupt */
for (i = 0; i < ARRAY_SIZE(espi_bus_isr_tbl); i++) {
inst->ESPIIE |= BIT(espi_bus_isr_tbl[i].int_en_bit);
inst->ESPIWE |= BIT(espi_bus_isr_tbl[i].wake_en_bit);
}
#if defined(CONFIG_ESPI_OOB_CHANNEL)
k_sem_init(&data->oob_rx_lock, 0, 1);
#endif
/* Configure Virtual Wire input signals */
for (i = 0; i < ARRAY_SIZE(vw_in_tbl); i++)
espi_vw_config_input(dev, &vw_in_tbl[i]);
/* Configure Virtual Wire output signals */
for (i = 0; i < ARRAY_SIZE(vw_out_tbl); i++)
espi_vw_config_output(dev, &vw_out_tbl[i]);
/* Configure wake-up input and callback for eSPI VW input signal */
for (i = 0; i < ARRAY_SIZE(vw_in_tbl); i++)
espi_init_wui_callback(dev, &vw_in_callback[i],
&vw_in_tbl[i].vw_wui, espi_vw_generic_isr);
/* Configure wake-up input and callback for ESPI_RST signal */
espi_init_wui_callback(dev, &espi_rst_callback,
&config->espi_rst_wui, espi_vw_espi_rst_isr);
/* Configure pin-mux for eSPI bus device */
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
LOG_ERR("eSPI pinctrl setup failed (%d)", ret);
return ret;
}
/* Configure host sub-modules which HW blocks belong to core domain */
npcx_host_init_subs_core_domain(dev, &data->callbacks);
/* eSPI Bus interrupt installation */
IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority),
espi_bus_generic_isr,
DEVICE_DT_INST_GET(0), 0);
/* Enable eSPI bus interrupt */
irq_enable(DT_INST_IRQN(0));
return 0;
}