You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2443 lines
72 KiB
2443 lines
72 KiB
/* |
|
* Copyright (c) 2025 Realtek, SIBG-SD7 |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT realtek_rts5912_espi |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/drivers/espi.h> |
|
#include <zephyr/drivers/gpio.h> |
|
#include <zephyr/drivers/pinctrl.h> |
|
#include <zephyr/drivers/clock_control.h> |
|
#include <zephyr/drivers/clock_control/clock_control_rts5912.h> |
|
#include <zephyr/logging/log.h> |
|
|
|
LOG_MODULE_REGISTER(espi, CONFIG_ESPI_LOG_LEVEL); |
|
|
|
#include "espi_utils.h" |
|
#include "reg/reg_acpi.h" |
|
#include "reg/reg_emi.h" |
|
#include "reg/reg_espi.h" |
|
#include "reg/reg_kbc.h" |
|
#include "reg/reg_port80.h" |
|
|
|
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1, "support only one espi compatible node"); |
|
|
|
struct espi_rts5912_config { |
|
volatile struct espi_reg *const espi_reg; |
|
uint32_t espislv_clk_grp; |
|
uint32_t espislv_clk_idx; |
|
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC |
|
volatile struct kbc_reg *const kbc_reg; |
|
uint32_t kbc_clk_grp; |
|
uint32_t kbc_clk_idx; |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO |
|
volatile struct acpi_reg *const acpi_reg; |
|
uint32_t acpi_clk_grp; |
|
uint32_t acpi_clk_idx; |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD |
|
volatile struct acpi_reg *const promt0_reg; |
|
uint32_t promt0_clk_grp; |
|
uint32_t promt0_clk_idx; |
|
volatile struct emi_reg *const emi0_reg; |
|
uint32_t emi0_clk_grp; |
|
uint32_t emi0_clk_idx; |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION |
|
volatile struct emi_reg *const emi1_reg; |
|
uint32_t emi1_clk_grp; |
|
uint32_t emi1_clk_idx; |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80 |
|
volatile struct port80_reg *const port80_reg; |
|
uint32_t port80_clk_grp; |
|
uint32_t port80_clk_idx; |
|
#endif |
|
const struct device *clk_dev; |
|
const struct pinctrl_dev_config *pcfg; |
|
}; |
|
|
|
struct espi_rts5912_data { |
|
sys_slist_t callbacks; |
|
uint32_t config_data; |
|
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC |
|
int kbc_int_en; |
|
int kbc_pre_irq1; |
|
#endif |
|
#ifdef CONFIG_ESPI_OOB_CHANNEL |
|
struct k_sem oob_rx_lock; |
|
struct k_sem oob_tx_lock; |
|
uint8_t *oob_tx_ptr; |
|
uint8_t *oob_rx_ptr; |
|
bool oob_tx_busy; |
|
#endif |
|
#ifdef CONFIG_ESPI_FLASH_CHANNEL |
|
struct k_sem flash_lock; |
|
uint8_t *maf_ptr; |
|
#endif |
|
}; |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI Peripheral KBC |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC |
|
|
|
static int espi_send_vw_event(uint8_t index, uint8_t data, const struct device *dev); |
|
|
|
static void kbc_ibf_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
struct espi_event evt = { |
|
ESPI_BUS_PERIPHERAL_NOTIFICATION, |
|
ESPI_PERIPHERAL_8042_KBC, |
|
ESPI_PERIPHERAL_NODATA, |
|
}; |
|
volatile struct kbc_reg *const kbc_reg = espi_config->kbc_reg; |
|
struct espi_evt_data_kbc *kbc_evt = (struct espi_evt_data_kbc *)&evt.evt_data; |
|
|
|
/* |
|
* Indicates if the host sent a command or data. |
|
* 0 = data |
|
* 1 = Command. |
|
*/ |
|
kbc_evt->type = kbc_reg->STS & KBC_STS_CMDSEL ? 1 : 0; |
|
|
|
/* The data in KBC Input Buffer */ |
|
kbc_evt->data = kbc_reg->IB; |
|
|
|
/* KBC Input Buffer Full event */ |
|
kbc_evt->evt = HOST_KBC_EVT_IBF; |
|
espi_send_callbacks(&espi_data->callbacks, dev, evt); |
|
} |
|
|
|
static void kbc_obe_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
struct espi_event evt = { |
|
ESPI_BUS_PERIPHERAL_NOTIFICATION, |
|
ESPI_PERIPHERAL_8042_KBC, |
|
ESPI_PERIPHERAL_NODATA, |
|
}; |
|
volatile struct kbc_reg *const kbc_reg = espi_config->kbc_reg; |
|
struct espi_evt_data_kbc *kbc_evt = (struct espi_evt_data_kbc *)&evt.evt_data; |
|
|
|
if (espi_data->kbc_pre_irq1 == 1 && !espi_send_vw_event(0x0, 0x01, dev)) { |
|
espi_data->kbc_pre_irq1 = 0; |
|
} |
|
|
|
if (kbc_reg->STS & KBC_STS_OBF) { |
|
kbc_reg->OB |= KBC_OB_OBCLR; |
|
} |
|
|
|
/* Notify application that host already read out data. */ |
|
kbc_evt->type = 0; |
|
kbc_evt->data = 0; |
|
kbc_evt->evt = HOST_KBC_EVT_OBE; |
|
espi_send_callbacks(&espi_data->callbacks, dev, evt); |
|
} |
|
|
|
static int espi_kbc_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *const espi_data = dev->data; |
|
struct rts5912_sccon_subsys sccon; |
|
volatile struct kbc_reg *const kbc_reg = espi_config->kbc_reg; |
|
int rc; |
|
|
|
if (!device_is_ready(espi_config->clk_dev)) { |
|
LOG_ERR("KBC clock not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
espi_data->kbc_int_en = 1; |
|
espi_data->kbc_pre_irq1 = 0; |
|
|
|
sccon.clk_grp = espi_config->kbc_clk_grp; |
|
sccon.clk_idx = espi_config->kbc_clk_idx; |
|
|
|
rc = clock_control_on(espi_config->clk_dev, (clock_control_subsys_t)&sccon); |
|
if (rc != 0) { |
|
LOG_ERR("KBC clock control on failed"); |
|
return rc; |
|
} |
|
|
|
kbc_reg->VWCTRL1 = (0x01 << KBC_VWCTRL1_IRQNUM_Pos) | KBC_VWCTRL1_ACTEN; |
|
kbc_reg->INTEN = KBC_INTEN_IBFINTEN | KBC_INTEN_OBFINTEN; |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_ibf, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_obe, irq)); |
|
|
|
/* IBF */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_ibf, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_ibf, priority), kbc_ibf_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_ibf, irq)); |
|
|
|
/* OBE */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_obe, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_obe, priority), kbc_obe_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_obe, irq)); |
|
|
|
return 0; |
|
} |
|
|
|
static int lpc_request_read_8042(const struct device *dev, enum lpc_peripheral_opcode op, |
|
uint32_t *data) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct kbc_reg *const kbc_reg = espi_config->kbc_reg; |
|
|
|
switch (op) { |
|
case E8042_OBF_HAS_CHAR: |
|
*data = (kbc_reg->STS & KBC_STS_OBF) ? 1 : 0; |
|
break; |
|
case E8042_IBF_HAS_CHAR: |
|
*data = (kbc_reg->STS & KBC_STS_IBF) ? 1 : 0; |
|
break; |
|
case E8042_READ_KB_STS: |
|
*data = kbc_reg->STS; |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int espi_send_vw_event(uint8_t index, uint8_t data, const struct device *dev); |
|
|
|
static void espi_send_vw_event_with_kbdata(uint8_t index, uint8_t data, uint32_t kbc_data, |
|
const struct device *dev); |
|
|
|
static uint8_t kbc_write(uint8_t data, const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
const struct espi_rts5912_data *const espi_data = dev->data; |
|
volatile struct kbc_reg *const kbc_reg = espi_config->kbc_reg; |
|
uint32_t exData = (uint32_t)data; |
|
|
|
if (espi_data->kbc_pre_irq1 == 1) { |
|
/* Gen IRQ1-Level High to VW ch */ |
|
espi_send_vw_event(0x0, 0x01, dev); |
|
} |
|
|
|
if (espi_data->kbc_int_en) { |
|
/* Gen IRQ1-Level High to VW ch */ |
|
espi_send_vw_event_with_kbdata(0x0, 0x81, exData, dev); |
|
} else { |
|
kbc_reg->OB = exData; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int lpc_request_write_8042(const struct device *dev, enum lpc_peripheral_opcode op, |
|
uint32_t *data) |
|
{ |
|
struct espi_rts5912_data *const espi_data = dev->data; |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct kbc_reg *const kbc_reg = espi_config->kbc_reg; |
|
|
|
switch (op) { |
|
case E8042_WRITE_KB_CHAR: |
|
kbc_write(*data & 0xff, dev); |
|
break; |
|
case E8042_WRITE_MB_CHAR: |
|
kbc_write(*data & 0xff, dev); |
|
break; |
|
case E8042_RESUME_IRQ: |
|
espi_data->kbc_int_en = 1; |
|
break; |
|
case E8042_PAUSE_IRQ: |
|
espi_data->kbc_int_en = 0; |
|
break; |
|
case E8042_CLEAR_OBF: |
|
kbc_reg->OB |= KBC_OB_OBCLR; |
|
break; |
|
case E8042_SET_FLAG: |
|
/* FW shouldn't modify these flags directly */ |
|
*data &= ~(KBC_STS_OBF | KBC_STS_IBF | KBC_STS_STS2); |
|
kbc_reg->STS |= *data & 0xff; |
|
break; |
|
case E8042_CLEAR_FLAG: |
|
/* FW shouldn't modify these flags directly */ |
|
*data |= KBC_STS_OBF | KBC_STS_IBF | KBC_STS_STS2; |
|
kbc_reg->STS &= ~(*data & 0xff); |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
#else /* CONFIG_ESPI_PERIPHERAL_8042_KBC */ |
|
|
|
static int lpc_request_read_8042(const struct device *dev, enum lpc_peripheral_opcode op, |
|
uint32_t *data) |
|
{ |
|
return -ENOTSUP; |
|
} |
|
|
|
static int lpc_request_write_8042(const struct device *dev, enum lpc_peripheral_opcode op, |
|
uint32_t *data) |
|
{ |
|
return -ENOTSUP; |
|
} |
|
|
|
#endif /* CONFIG_ESPI_PERIPHERAL_8042_KBC */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI Peripheral Shared Memory Region |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION |
|
#define ESPI_RTK_PERIPHERAL_ACPI_SHD_MEM_SIZE 256 |
|
|
|
static uint8_t acpi_shd_mem_sram[ESPI_RTK_PERIPHERAL_ACPI_SHD_MEM_SIZE] __aligned(256); |
|
|
|
static void espi_setup_acpi_shm(const struct espi_rts5912_config *const espi_config) |
|
{ |
|
volatile struct emi_reg *const emi1_reg = espi_config->emi1_reg; |
|
|
|
emi1_reg->SAR = (uint32_t)&acpi_shd_mem_sram[0]; |
|
} |
|
|
|
#endif /* CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI Peripheral Host IO (ACPI) |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO |
|
|
|
static void acpi_ibf_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
struct espi_event evt = {ESPI_BUS_PERIPHERAL_NOTIFICATION, ESPI_PERIPHERAL_HOST_IO, |
|
ESPI_PERIPHERAL_NODATA}; |
|
struct espi_evt_data_acpi *acpi_evt = (struct espi_evt_data_acpi *)&evt.evt_data; |
|
volatile struct acpi_reg *const acpi_reg = espi_config->acpi_reg; |
|
|
|
/* Host put data on input buffer of ACPI EC0 channel */ |
|
if (acpi_reg->STS & ACPI_STS_IBF) { |
|
/* |
|
* Indicates if the host sent a command or data. |
|
* 0 = data |
|
* 1 = Command. |
|
*/ |
|
acpi_evt->type = acpi_reg->STS & ACPI_STS_CMDSEL ? 1 : 0; |
|
acpi_evt->data = (uint8_t)acpi_reg->IB; |
|
} |
|
espi_send_callbacks(&espi_data->callbacks, dev, evt); |
|
} |
|
|
|
static int espi_acpi_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct rts5912_sccon_subsys sccon; |
|
volatile struct acpi_reg *const acpi_reg = espi_config->acpi_reg; |
|
int rc; |
|
|
|
if (!device_is_ready(espi_config->clk_dev)) { |
|
LOG_ERR("ACPI clock not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
sccon.clk_grp = espi_config->acpi_clk_grp; |
|
sccon.clk_idx = espi_config->acpi_clk_idx; |
|
rc = clock_control_on(espi_config->clk_dev, (clock_control_subsys_t)&sccon); |
|
if (rc != 0) { |
|
LOG_ERR("ACPI clock control on failed"); |
|
return rc; |
|
} |
|
|
|
acpi_reg->VWCTRL1 = (0x00UL << ACPI_VWCTRL1_IRQNUM_Pos) | ACPI_VWCTRL1_ACTEN; |
|
acpi_reg->INTEN = ACPI_INTEN_IBFINTEN; |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), acpi_ibf, irq)); |
|
|
|
/* IBF */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), acpi_ibf, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), acpi_ibf, priority), acpi_ibf_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), acpi_ibf, irq)); |
|
|
|
return 0; |
|
} |
|
|
|
static int lpc_request_read_acpi(const struct espi_rts5912_config *const espi_config, |
|
enum lpc_peripheral_opcode op, uint32_t *data) |
|
{ |
|
volatile struct acpi_reg *const acpi_reg = espi_config->acpi_reg; |
|
|
|
switch (op) { |
|
case EACPI_OBF_HAS_CHAR: |
|
*data = acpi_reg->STS & ACPI_STS_OBF ? 1 : 0; |
|
break; |
|
case EACPI_IBF_HAS_CHAR: |
|
*data = acpi_reg->STS & ACPI_STS_IBF ? 1 : 0; |
|
break; |
|
case EACPI_READ_STS: |
|
*data = acpi_reg->STS; |
|
break; |
|
#ifdef CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION |
|
case EACPI_GET_SHARED_MEMORY: |
|
*data = (uint32_t)acpi_shd_mem_sram; |
|
break; |
|
#endif |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int lpc_request_write_acpi(const struct espi_rts5912_config *const espi_config, |
|
enum lpc_peripheral_opcode op, uint32_t *data) |
|
{ |
|
volatile struct acpi_reg *const acpi_reg = espi_config->acpi_reg; |
|
|
|
switch (op) { |
|
case EACPI_WRITE_CHAR: |
|
acpi_reg->OB = *data & 0xff; |
|
break; |
|
case EACPI_WRITE_STS: |
|
acpi_reg->STS = *data & 0xff; |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
#else /* CONFIG_ESPI_PERIPHERAL_HOST_IO */ |
|
|
|
static int lpc_request_read_acpi(const struct espi_rts5912_config *const espi_config, |
|
enum lpc_peripheral_opcode op, uint32_t *data) |
|
{ |
|
return -ENOTSUP; |
|
} |
|
|
|
static int lpc_request_write_acpi(const struct espi_rts5912_config *const espi_config, |
|
enum lpc_peripheral_opcode op, uint32_t *data) |
|
{ |
|
return -ENOTSUP; |
|
} |
|
|
|
#endif /* CONFIG_ESPI_PERIPHERAL_HOST_IO */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI Peripheral EC Host Command (Promt0) |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD |
|
|
|
#define ESPI_RTK_PERIPHERAL_HOST_CMD_PARAM_SIZE 256 |
|
|
|
static uint8_t ec_host_cmd_sram[ESPI_RTK_PERIPHERAL_HOST_CMD_PARAM_SIZE] __aligned(256); |
|
|
|
static void espi_setup_host_cmd_shm(const struct espi_rts5912_config *const espi_config) |
|
{ |
|
volatile struct emi_reg *const emi0_reg = espi_config->emi0_reg; |
|
|
|
emi0_reg->SAR = (uint32_t)&ec_host_cmd_sram[0]; |
|
} |
|
|
|
static void promt0_ibf_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct acpi_reg *const promt0_reg = espi_config->promt0_reg; |
|
struct espi_rts5912_data *data = dev->data; |
|
struct espi_event evt = {.evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION, |
|
.evt_details = ESPI_PERIPHERAL_EC_HOST_CMD, |
|
.evt_data = ESPI_PERIPHERAL_NODATA}; |
|
|
|
promt0_reg->STS |= ACPI_STS_STS0; |
|
evt.evt_data = (uint8_t)promt0_reg->IB; |
|
espi_send_callbacks(&data->callbacks, dev, evt); |
|
} |
|
|
|
static int espi_promt0_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct rts5912_sccon_subsys sccon; |
|
volatile struct acpi_reg *const promt0_reg = espi_config->promt0_reg; |
|
int rc; |
|
|
|
if (!device_is_ready(espi_config->clk_dev)) { |
|
LOG_ERR("Promt0 clock not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
sccon.clk_grp = espi_config->promt0_clk_grp; |
|
sccon.clk_idx = espi_config->promt0_clk_idx; |
|
|
|
rc = clock_control_on(espi_config->clk_dev, (clock_control_subsys_t)&sccon); |
|
if (rc != 0) { |
|
LOG_ERR("Promt0 clock control on failed"); |
|
return rc; |
|
} |
|
|
|
promt0_reg->STS = 0; |
|
|
|
if (promt0_reg->STS & ACPI_STS_IBF) { |
|
rc = promt0_reg->IB; |
|
} |
|
|
|
if (promt0_reg->STS & ACPI_STS_IBF) { |
|
promt0_reg->IB |= ACPI_IB_IBCLR; |
|
} |
|
|
|
promt0_reg->PTADDR = |
|
CONFIG_ESPI_PERIPHERAL_HOST_CMD_DATA_PORT_NUM | (0x04 << ACPI_PTADDR_OFFSET_Pos); |
|
promt0_reg->VWCTRL1 = ACPI_VWCTRL1_ACTEN; |
|
promt0_reg->INTEN = ACPI_INTEN_IBFINTEN; |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), promt0_ibf, irq)); |
|
|
|
/* IBF */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), promt0_ibf, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), promt0_ibf, priority), promt0_ibf_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), promt0_ibf, irq)); |
|
|
|
return 0; |
|
} |
|
|
|
#endif /* CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI Peripheral Channel Read/Write API |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_CHANNEL |
|
|
|
static void espi_periph_ch_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *data = dev->data; |
|
|
|
struct espi_event evt = {.evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
|
.evt_details = ESPI_CHANNEL_PERIPHERAL, |
|
.evt_data = 0}; |
|
|
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
uint32_t status = espi_reg->EPSTS; |
|
uint32_t config = espi_reg->EPCFG; |
|
|
|
if (status & ESPI_EPSTS_CLRSTS) { |
|
evt.evt_data = config & ESPI_EPCFG_CHEN ? 1 : 0; |
|
espi_send_callbacks(&data->callbacks, dev, evt); |
|
|
|
espi_reg->EPSTS = ESPI_EPSTS_CLRSTS; |
|
} |
|
} |
|
|
|
static int lpc_request_read_custom(const struct espi_rts5912_config *const espi_config, |
|
enum lpc_peripheral_opcode op, uint32_t *data) |
|
{ |
|
#ifdef CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE |
|
switch (op) { |
|
case ECUSTOM_HOST_CMD_GET_PARAM_MEMORY: |
|
*data = (uint32_t)ec_host_cmd_sram; |
|
break; |
|
case ECUSTOM_HOST_CMD_GET_PARAM_MEMORY_SIZE: |
|
*data = ESPI_RTK_PERIPHERAL_HOST_CMD_PARAM_SIZE; |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
#else |
|
return -ENOTSUP; |
|
#endif |
|
} |
|
|
|
static int espi_rts5912_read_lpc_request(const struct device *dev, enum lpc_peripheral_opcode op, |
|
uint32_t *data) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
|
|
if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) { |
|
return lpc_request_read_8042(dev, op, data); |
|
} else if (op >= EACPI_START_OPCODE && op <= EACPI_MAX_OPCODE) { |
|
return lpc_request_read_acpi(espi_config, op, data); |
|
} else if (op >= ECUSTOM_START_OPCODE && op <= ECUSTOM_MAX_OPCODE) { |
|
return lpc_request_read_custom(espi_config, op, data); |
|
} else { |
|
return -ENOTSUP; |
|
} |
|
} |
|
|
|
static int lpc_request_write_custom(const struct espi_rts5912_config *const espi_config, |
|
enum lpc_peripheral_opcode op, uint32_t *data) |
|
{ |
|
#ifdef CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE |
|
volatile struct acpi_reg *const promt0_reg = espi_config->promt0_reg; |
|
|
|
switch (op) { |
|
case ECUSTOM_HOST_SUBS_INTERRUPT_EN: |
|
if (*data == 0) { |
|
NVIC_DisableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), promt0_ibf, irq))); |
|
NVIC_DisableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), acpi_ibf, irq))); |
|
NVIC_DisableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), port80, irq))); |
|
NVIC_DisableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_ibf, irq))); |
|
NVIC_DisableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_obe, irq))); |
|
|
|
} else { |
|
NVIC_EnableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), promt0_ibf, irq))); |
|
NVIC_EnableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), acpi_ibf, irq))); |
|
NVIC_EnableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), port80, irq))); |
|
NVIC_EnableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_ibf, irq))); |
|
NVIC_EnableIRQ((DT_IRQ_BY_NAME(DT_DRV_INST(0), kbc_obe, irq))); |
|
} |
|
break; |
|
case ECUSTOM_HOST_CMD_SEND_RESULT: |
|
promt0_reg->STS &= ~ACPI_STS_STS0; |
|
promt0_reg->OB = *data & 0xff; |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
#else |
|
return -ENOTSUP; |
|
#endif |
|
} |
|
|
|
static int espi_rts5912_write_lpc_request(const struct device *dev, enum lpc_peripheral_opcode op, |
|
uint32_t *data) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
|
|
if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) { |
|
return lpc_request_write_8042(dev, op, data); |
|
} else if (op >= EACPI_START_OPCODE && op <= EACPI_MAX_OPCODE) { |
|
return lpc_request_write_acpi(espi_config, op, data); |
|
} else if (op >= ECUSTOM_START_OPCODE && op <= ECUSTOM_MAX_OPCODE) { |
|
return lpc_request_write_custom(espi_config, op, data); |
|
} else { |
|
return -ENOTSUP; |
|
} |
|
} |
|
|
|
static void espi_periph_ch_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
espi_reg->EPINTEN = ESPI_EPINTEN_CFGCHGEN | ESPI_EPINTEN_MEMWREN | ESPI_EPINTEN_MEMRDEN; |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), periph_ch, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), periph_ch, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), periph_ch, priority), espi_periph_ch_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), periph_ch, irq)); |
|
} |
|
|
|
#endif /* CONFIG_ESPI_PERIPHERAL_CHANNEL */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI Peripheral Debug Port 80 |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80 |
|
|
|
static void espi_port80_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
struct espi_event evt = {ESPI_BUS_PERIPHERAL_NOTIFICATION, |
|
ESPI_PERIPHERAL_INDEX_0 << 16 | ESPI_PERIPHERAL_DEBUG_PORT80, |
|
ESPI_PERIPHERAL_NODATA}; |
|
volatile struct port80_reg *const port80_reg = espi_config->port80_reg; |
|
|
|
evt.evt_data = port80_reg->DATA; |
|
espi_send_callbacks(&espi_data->callbacks, dev, evt); |
|
} |
|
|
|
static int espi_peri_ch_port80_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct rts5912_sccon_subsys sccon; |
|
volatile struct port80_reg *const port80_reg = espi_config->port80_reg; |
|
int rc; |
|
|
|
if (!device_is_ready(espi_config->clk_dev)) { |
|
return -ENODEV; |
|
} |
|
|
|
sccon.clk_grp = espi_config->port80_clk_grp; |
|
sccon.clk_idx = espi_config->port80_clk_idx; |
|
|
|
rc = clock_control_on(espi_config->clk_dev, (clock_control_subsys_t)&sccon); |
|
if (rc != 0) { |
|
return rc; |
|
} |
|
|
|
port80_reg->ADDR = 0x80UL; |
|
port80_reg->CFG = PORT80_CFG_CLRFLG | PORT80_CFG_THREEN; |
|
port80_reg->INTEN = PORT80_INTEN_THREINTEN; |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), port80, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), port80, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), port80, priority), espi_port80_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), port80, irq)); |
|
|
|
return 0; |
|
} |
|
|
|
#endif /* CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80 */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI VWIRE channel |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_VWIRE_CHANNEL |
|
|
|
#define VW_CH_IDX2 0x02UL |
|
#define VW_CH_IDX3 0x03UL |
|
#define VW_CH_IDX4 0x04UL |
|
#define VW_CH_IDX5 0x05UL |
|
#define VW_CH_IDX6 0x06UL |
|
#define VW_CH_IDX7 0x07UL |
|
#define VW_CH_IDX40 0x40UL |
|
#define VW_CH_IDX41 0x41UL |
|
#define VW_CH_IDX42 0x42UL |
|
#define VW_CH_IDX43 0x43UL |
|
#define VW_CH_IDX44 0x44UL |
|
#define VW_CH_IDX47 0x47UL |
|
#define VW_CH_IDX4A 0x4AUL |
|
#define VW_CH_IDX51 0x51UL |
|
#define VW_CH_IDX61 0x61UL |
|
|
|
struct espi_vw_channel { |
|
uint8_t vw_index; |
|
uint8_t level_mask; |
|
uint8_t valid_mask; |
|
}; |
|
|
|
struct espi_vw_signal_t { |
|
enum espi_vwire_signal signal; |
|
void (*vw_signal_callback)(const struct device *dev); |
|
}; |
|
|
|
#define VW_CH(signal, index, level, valid) \ |
|
[signal] = {.vw_index = index, .level_mask = level, .valid_mask = valid} |
|
|
|
static const struct espi_vw_channel vw_channel_list[] = { |
|
VW_CH(ESPI_VWIRE_SIGNAL_SLP_S3, VW_CH_IDX2, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SLP_S4, VW_CH_IDX2, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SLP_S5, VW_CH_IDX2, BIT(2), BIT(6)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_OOB_RST_WARN, VW_CH_IDX3, BIT(2), BIT(6)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_PLTRST, VW_CH_IDX3, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SUS_STAT, VW_CH_IDX3, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_NMIOUT, VW_CH_IDX7, BIT(2), BIT(6)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SMIOUT, VW_CH_IDX7, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_HOST_RST_WARN, VW_CH_IDX7, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SLP_A, VW_CH_IDX41, BIT(3), BIT(7)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK, VW_CH_IDX41, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SUS_WARN, VW_CH_IDX41, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SLP_WLAN, VW_CH_IDX42, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SLP_LAN, VW_CH_IDX42, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_HOST_C10, VW_CH_IDX47, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_DNX_WARN, VW_CH_IDX4A, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_PME, VW_CH_IDX4, BIT(3), BIT(7)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_WAKE, VW_CH_IDX4, BIT(2), BIT(6)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_OOB_RST_ACK, VW_CH_IDX4, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_TARGET_BOOT_STS, VW_CH_IDX5, BIT(3), BIT(7)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_ERR_NON_FATAL, VW_CH_IDX5, BIT(2), BIT(6)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_ERR_FATAL, VW_CH_IDX5, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_TARGET_BOOT_DONE, VW_CH_IDX5, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_HOST_RST_ACK, VW_CH_IDX6, BIT(3), BIT(7)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_RST_CPU_INIT, VW_CH_IDX6, BIT(2), BIT(6)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SMI, VW_CH_IDX6, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SCI, VW_CH_IDX6, BIT(0), BIT(4)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_DNX_ACK, VW_CH_IDX40, BIT(1), BIT(5)), |
|
VW_CH(ESPI_VWIRE_SIGNAL_SUS_ACK, VW_CH_IDX40, BIT(0), BIT(4)), |
|
}; |
|
|
|
struct espi_vw_ch_cached { |
|
uint8_t idx2; |
|
uint8_t idx3; |
|
uint8_t idx7; |
|
uint8_t idx41; |
|
uint8_t idx42; |
|
uint8_t idx43; |
|
uint8_t idx44; |
|
uint8_t idx47; |
|
uint8_t idx4a; |
|
uint8_t idx51; |
|
uint8_t idx61; |
|
}; |
|
|
|
struct espi_vw_tx_cached { |
|
uint8_t idx4; |
|
uint8_t idx5; |
|
uint8_t idx6; |
|
uint8_t idx40; |
|
}; |
|
|
|
static struct espi_vw_ch_cached espi_vw_ch_cached_data; |
|
static struct espi_vw_tx_cached espi_vw_tx_cached_data; |
|
|
|
static int espi_rts5912_send_vwire(const struct device *dev, enum espi_vwire_signal signal, |
|
uint8_t level); |
|
static int espi_rts5912_receive_vwire(const struct device *dev, enum espi_vwire_signal signal, |
|
uint8_t *level); |
|
static int vw_signal_set_valid(const struct device *dev, enum espi_vwire_signal signal, |
|
uint8_t valid); |
|
static void notify_system_state(const struct device *dev, enum espi_vwire_signal signal); |
|
static void notify_host_warning(const struct device *dev, enum espi_vwire_signal signal); |
|
static void vw_slp3_handler(const struct device *dev); |
|
static void vw_slp4_handler(const struct device *dev); |
|
static void vw_slp5_handler(const struct device *dev); |
|
static void vw_sus_stat_handler(const struct device *dev); |
|
static void vw_pltrst_handler(const struct device *dev); |
|
static void vw_oob_rst_warn_handler(const struct device *dev); |
|
static void vw_host_rst_warn_handler(const struct device *dev); |
|
static void vw_smiout_handler(const struct device *dev); |
|
static void vw_nmiout_handler(const struct device *dev); |
|
static void vw_sus_warn_handler(const struct device *dev); |
|
static void vw_sus_pwrdn_ack_handler(const struct device *dev); |
|
static void vw_sus_slp_a_handler(const struct device *dev); |
|
static void vw_slp_lan_handler(const struct device *dev); |
|
static void vw_slp_wlan_handler(const struct device *dev); |
|
static void vw_host_c10_handler(const struct device *dev); |
|
static void espi_vw_ch_setup(const struct device *dev); |
|
static void espi_vw_ch_isr(const struct device *dev); |
|
#ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE |
|
static void send_target_bootdone(const struct device *dev); |
|
#endif |
|
|
|
static void espi_vw_ch_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *data = dev->data; |
|
|
|
struct espi_event evt = {.evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
|
.evt_details = ESPI_CHANNEL_VWIRE, |
|
.evt_data = 0}; |
|
|
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
uint32_t config = espi_reg->EVCFG; |
|
|
|
evt.evt_data = (config & ESPI_EVCFG_CHEN) ? 1 : 0; |
|
espi_send_callbacks(&data->callbacks, dev, evt); |
|
|
|
if (config & ESPI_EVCFG_CHEN) { |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_STS, 1); |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_DONE, 1); |
|
|
|
#ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE |
|
send_target_bootdone(dev); |
|
#endif |
|
} |
|
espi_reg->EVSTS = ESPI_EVSTS_RXIDXCHG; |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx2_signals[] = { |
|
{ESPI_VWIRE_SIGNAL_SLP_S3, vw_slp3_handler}, |
|
{ESPI_VWIRE_SIGNAL_SLP_S4, vw_slp4_handler}, |
|
{ESPI_VWIRE_SIGNAL_SLP_S5, vw_slp5_handler}, |
|
}; |
|
|
|
static void espi_vw_idx2_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX2; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx2; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX2CHG) { |
|
espi_vw_ch_cached_data.idx2 = cur_idx_data; |
|
|
|
for (int i = 0; i < ARRAY_SIZE(vw_idx2_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx2_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx2_signals[i].vw_signal_callback != NULL) { |
|
vw_idx2_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx2 == espi_reg->EVIDX2) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX2CHG; |
|
} |
|
} |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx3_signals[] = { |
|
{ESPI_VWIRE_SIGNAL_SUS_STAT, vw_sus_stat_handler}, |
|
{ESPI_VWIRE_SIGNAL_PLTRST, vw_pltrst_handler}, |
|
{ESPI_VWIRE_SIGNAL_OOB_RST_WARN, vw_oob_rst_warn_handler}, |
|
}; |
|
|
|
static void espi_vw_idx3_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX3; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx3; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX3CHG) { |
|
espi_vw_ch_cached_data.idx3 = cur_idx_data; |
|
|
|
for (int i = 0; i < ARRAY_SIZE(vw_idx3_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx3_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx3_signals[i].vw_signal_callback != NULL) { |
|
vw_idx3_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx3 == espi_reg->EVIDX3) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX3CHG; |
|
} |
|
} |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx7_signals[] = { |
|
{ESPI_VWIRE_SIGNAL_HOST_RST_WARN, vw_host_rst_warn_handler}, |
|
{ESPI_VWIRE_SIGNAL_SMIOUT, vw_smiout_handler}, |
|
{ESPI_VWIRE_SIGNAL_NMIOUT, vw_nmiout_handler}, |
|
}; |
|
|
|
static void espi_vw_idx7_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX7; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx7; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX7CHG) { |
|
espi_vw_ch_cached_data.idx7 = cur_idx_data; |
|
|
|
for (int i = 0; i < ARRAY_SIZE(vw_idx7_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx7_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx7_signals[i].vw_signal_callback != NULL) { |
|
vw_idx7_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx7 == espi_reg->EVIDX7) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX7CHG; |
|
} |
|
} |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx41_signals[] = { |
|
{ESPI_VWIRE_SIGNAL_SUS_WARN, vw_sus_warn_handler}, |
|
{ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK, vw_sus_pwrdn_ack_handler}, |
|
{ESPI_VWIRE_SIGNAL_SLP_A, vw_sus_slp_a_handler}, |
|
}; |
|
|
|
static void espi_vw_idx41_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX41; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx41; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX41CHG) { |
|
espi_vw_ch_cached_data.idx41 = cur_idx_data; |
|
for (int i = 0; i < ARRAY_SIZE(vw_idx41_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx41_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx41_signals[i].vw_signal_callback != NULL) { |
|
vw_idx41_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx41 == espi_reg->EVIDX41) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX41CHG; |
|
} |
|
} |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx42_signals[] = { |
|
{ESPI_VWIRE_SIGNAL_SLP_LAN, vw_slp_lan_handler}, |
|
{ESPI_VWIRE_SIGNAL_SLP_WLAN, vw_slp_wlan_handler}, |
|
}; |
|
|
|
static void espi_vw_idx42_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX42; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx42; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX42CHG) { |
|
espi_vw_ch_cached_data.idx42 = cur_idx_data; |
|
|
|
for (int i = 0; i < ARRAY_SIZE(vw_idx42_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx42_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx42_signals[i].vw_signal_callback != NULL) { |
|
vw_idx42_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx42 == espi_reg->EVIDX42) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX42CHG; |
|
} |
|
} |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx43_signals[] = {}; |
|
|
|
static void espi_vw_idx43_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX43; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx43; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX43CHG) { |
|
espi_vw_ch_cached_data.idx43 = cur_idx_data; |
|
|
|
for (int i = 0; i < ARRAY_SIZE(vw_idx43_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx43_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx43_signals[i].vw_signal_callback != NULL) { |
|
vw_idx43_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx43 == espi_reg->EVIDX43) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX43CHG; |
|
} |
|
} |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx44_signals[] = {}; |
|
|
|
static void espi_vw_idx44_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX44; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx44; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX44CHG) { |
|
espi_vw_ch_cached_data.idx44 = cur_idx_data; |
|
|
|
for (int i = 0; i < ARRAY_SIZE(vw_idx44_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx44_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx44_signals[i].vw_signal_callback != NULL) { |
|
vw_idx44_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx44 == espi_reg->EVIDX44) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX44CHG; |
|
} |
|
} |
|
} |
|
|
|
static const struct espi_vw_signal_t vw_idx47_signals[] = { |
|
{ESPI_VWIRE_SIGNAL_HOST_C10, vw_host_c10_handler}, |
|
}; |
|
|
|
static void espi_vw_idx47_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t cur_idx_data = espi_reg->EVIDX47; |
|
uint8_t updated_bit = cur_idx_data ^ espi_vw_ch_cached_data.idx47; |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_IDX47CHG) { |
|
espi_vw_ch_cached_data.idx47 = cur_idx_data; |
|
|
|
for (int i = 0; i < ARRAY_SIZE(vw_idx47_signals); i++) { |
|
enum espi_vwire_signal vw_signal = vw_idx47_signals[i].signal; |
|
|
|
if (updated_bit & vw_channel_list[vw_signal].level_mask && |
|
vw_idx47_signals[i].vw_signal_callback != NULL) { |
|
vw_idx47_signals[i].vw_signal_callback(dev); |
|
} |
|
} |
|
if (espi_vw_ch_cached_data.idx47 == espi_reg->EVIDX47) { |
|
espi_reg->EVSTS = ESPI_EVSTS_IDX47CHG; |
|
} |
|
} |
|
} |
|
|
|
static void espi_vw_idx4a_isr(const struct device *dev) |
|
{ |
|
ARG_UNUSED(dev); |
|
} |
|
|
|
static void espi_vw_idx51_isr(const struct device *dev) |
|
{ |
|
ARG_UNUSED(dev); |
|
} |
|
|
|
static void espi_vw_idx61_isr(const struct device *dev) |
|
{ |
|
ARG_UNUSED(dev); |
|
} |
|
|
|
static int vw_signal_set_valid(const struct device *dev, enum espi_vwire_signal signal, |
|
uint8_t valid) |
|
{ |
|
uint8_t vw_index = vw_channel_list[signal].vw_index; |
|
uint8_t valid_mask = vw_channel_list[signal].valid_mask; |
|
|
|
if (signal > ARRAY_SIZE(vw_channel_list)) { |
|
return -EIO; |
|
} |
|
|
|
switch (vw_index) { |
|
case VW_CH_IDX4: |
|
if (valid) { |
|
espi_vw_tx_cached_data.idx4 |= valid_mask; |
|
} else { |
|
espi_vw_tx_cached_data.idx4 &= ~valid_mask; |
|
} |
|
break; |
|
case VW_CH_IDX5: |
|
if (valid) { |
|
espi_vw_tx_cached_data.idx5 |= valid_mask; |
|
} else { |
|
espi_vw_tx_cached_data.idx5 &= ~valid_mask; |
|
} |
|
break; |
|
case VW_CH_IDX6: |
|
if (valid) { |
|
espi_vw_tx_cached_data.idx6 |= valid_mask; |
|
} else { |
|
espi_vw_tx_cached_data.idx6 &= ~valid_mask; |
|
} |
|
break; |
|
case VW_CH_IDX40: |
|
if (valid) { |
|
espi_vw_tx_cached_data.idx40 |= valid_mask; |
|
} else { |
|
espi_vw_tx_cached_data.idx40 &= ~valid_mask; |
|
} |
|
break; |
|
default: |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void vw_ch_isr_wa_cb(struct k_work *work) |
|
{ |
|
espi_vw_ch_isr(DEVICE_DT_GET(DT_DRV_INST(0))); |
|
} |
|
static K_WORK_DELAYABLE_DEFINE(vw_ch_isr_wa, vw_ch_isr_wa_cb); |
|
|
|
#ifdef CONFIG_ESPI_AUTOMATIC_BOOT_DONE_ACKNOWLEDGE |
|
static void send_target_bootdone(const struct device *dev) |
|
{ |
|
int ret; |
|
uint8_t boot_done; |
|
|
|
ret = espi_rts5912_receive_vwire(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_DONE, &boot_done); |
|
if (!ret && !boot_done) { |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_STS, 1); |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_TARGET_BOOT_DONE, 1); |
|
k_work_cancel_delayable(&vw_ch_isr_wa); |
|
} |
|
} |
|
#endif |
|
|
|
static void notify_system_state(const struct device *dev, enum espi_vwire_signal signal) |
|
{ |
|
struct espi_rts5912_data *data = dev->data; |
|
struct espi_event evt = {ESPI_BUS_EVENT_VWIRE_RECEIVED, 0, 0}; |
|
|
|
uint8_t status = 0; |
|
|
|
espi_rts5912_receive_vwire(dev, signal, &status); |
|
|
|
evt.evt_details = signal; |
|
evt.evt_data = status; |
|
espi_send_callbacks(&data->callbacks, dev, evt); |
|
} |
|
|
|
static void notify_host_warning(const struct device *dev, enum espi_vwire_signal signal) |
|
{ |
|
uint8_t status = 0; |
|
|
|
espi_rts5912_receive_vwire(dev, signal, &status); |
|
k_busy_wait(200); |
|
|
|
switch (signal) { |
|
case ESPI_VWIRE_SIGNAL_SUS_WARN: |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_SUS_ACK, 1); |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_SUS_ACK, status); |
|
break; |
|
case ESPI_VWIRE_SIGNAL_OOB_RST_WARN: |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_OOB_RST_ACK, status); |
|
break; |
|
case ESPI_VWIRE_SIGNAL_HOST_RST_WARN: |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_HOST_RST_ACK, status); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
static void vw_slp3_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S3); |
|
} |
|
|
|
static void vw_slp4_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S4); |
|
} |
|
|
|
static void vw_slp5_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_S5); |
|
} |
|
|
|
static void vw_sus_stat_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SUS_STAT); |
|
} |
|
|
|
static void vw_pltrst_handler(const struct device *dev) |
|
{ |
|
struct espi_rts5912_data *data = dev->data; |
|
|
|
struct espi_event evt = {ESPI_BUS_EVENT_VWIRE_RECEIVED, ESPI_VWIRE_SIGNAL_PLTRST, 0}; |
|
uint8_t status = 0; |
|
|
|
espi_rts5912_receive_vwire(dev, ESPI_VWIRE_SIGNAL_PLTRST, &status); |
|
|
|
if (status) { |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_SMI, 1); |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_SCI, 1); |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_HOST_RST_ACK, 1); |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_RST_CPU_INIT, 1); |
|
|
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_SMI, 1); |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_SCI, 1); |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_HOST_RST_ACK, 1); |
|
espi_rts5912_send_vwire(dev, ESPI_VWIRE_SIGNAL_RST_CPU_INIT, 1); |
|
} |
|
|
|
evt.evt_data = status; |
|
espi_send_callbacks(&data->callbacks, dev, evt); |
|
} |
|
|
|
static void vw_oob_rst_warn_handler(const struct device *dev) |
|
{ |
|
if (!IS_ENABLED(CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE)) { |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_OOB_RST_WARN); |
|
} else { |
|
notify_host_warning(dev, ESPI_VWIRE_SIGNAL_OOB_RST_WARN); |
|
} |
|
} |
|
|
|
static void vw_host_rst_warn_handler(const struct device *dev) |
|
{ |
|
if (!IS_ENABLED(CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE)) { |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_HOST_RST_WARN); |
|
} else { |
|
notify_host_warning(dev, ESPI_VWIRE_SIGNAL_HOST_RST_WARN); |
|
} |
|
} |
|
|
|
static void vw_smiout_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SMIOUT); |
|
} |
|
|
|
static void vw_nmiout_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_NMIOUT); |
|
} |
|
|
|
static void vw_sus_warn_handler(const struct device *dev) |
|
{ |
|
if (!IS_ENABLED(CONFIG_ESPI_AUTOMATIC_WARNING_ACKNOWLEDGE)) { |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SUS_WARN); |
|
} else { |
|
notify_host_warning(dev, ESPI_VWIRE_SIGNAL_SUS_WARN); |
|
} |
|
} |
|
|
|
static void vw_sus_pwrdn_ack_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SUS_PWRDN_ACK); |
|
} |
|
|
|
static void vw_sus_slp_a_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_A); |
|
} |
|
|
|
static void vw_slp_lan_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_LAN); |
|
} |
|
|
|
static void vw_slp_wlan_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_SLP_WLAN); |
|
} |
|
|
|
static void vw_host_c10_handler(const struct device *dev) |
|
{ |
|
notify_system_state(dev, ESPI_VWIRE_SIGNAL_HOST_C10); |
|
} |
|
|
|
#define VW_TIMEOUT_US 1000 |
|
|
|
static int espi_rts5912_send_vwire(const struct device *dev, enum espi_vwire_signal signal, |
|
uint8_t level) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
uint8_t vw_idx = vw_channel_list[signal].vw_index; |
|
uint8_t lev_msk = vw_channel_list[signal].level_mask; |
|
uint32_t tx_data = 0; |
|
|
|
if (signal > ARRAY_SIZE(vw_channel_list)) { |
|
return -EIO; |
|
} |
|
|
|
switch (vw_idx) { |
|
case VW_CH_IDX4: |
|
tx_data = espi_vw_tx_cached_data.idx4; |
|
break; |
|
case VW_CH_IDX5: |
|
tx_data = espi_vw_tx_cached_data.idx5; |
|
break; |
|
case VW_CH_IDX6: |
|
tx_data = espi_vw_tx_cached_data.idx6; |
|
break; |
|
case VW_CH_IDX40: |
|
tx_data = espi_vw_tx_cached_data.idx40; |
|
break; |
|
default: |
|
return -EIO; |
|
} |
|
|
|
tx_data |= (vw_idx << 8); |
|
|
|
if (level) { |
|
tx_data |= lev_msk; |
|
} else { |
|
tx_data &= ~lev_msk; |
|
} |
|
|
|
if (espi_reg->EVSTS & ESPI_EVSTS_TXFULL) { |
|
return -EIO; |
|
} |
|
|
|
espi_reg->EVTXDAT = tx_data; |
|
|
|
WAIT_FOR(!(espi_reg->EVSTS & ESPI_EVSTS_TXFULL), VW_TIMEOUT_US, k_busy_wait(10)); |
|
|
|
switch (vw_idx) { |
|
case VW_CH_IDX4: |
|
espi_vw_tx_cached_data.idx4 = tx_data; |
|
break; |
|
case VW_CH_IDX5: |
|
espi_vw_tx_cached_data.idx5 = tx_data; |
|
break; |
|
case VW_CH_IDX6: |
|
espi_vw_tx_cached_data.idx6 = tx_data; |
|
break; |
|
case VW_CH_IDX40: |
|
espi_vw_tx_cached_data.idx40 = tx_data; |
|
break; |
|
default: |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int espi_rts5912_receive_vwire(const struct device *dev, enum espi_vwire_signal signal, |
|
uint8_t *level) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint8_t vw_idx, lev_msk, valid_msk; |
|
uint8_t vw_data; |
|
|
|
vw_idx = vw_channel_list[signal].vw_index; |
|
lev_msk = vw_channel_list[signal].level_mask; |
|
valid_msk = vw_channel_list[signal].valid_mask; |
|
|
|
if (signal > ARRAY_SIZE(vw_channel_list)) { |
|
return -EIO; |
|
} |
|
|
|
switch (vw_idx) { |
|
case VW_CH_IDX2: |
|
vw_data = espi_vw_ch_cached_data.idx2; |
|
break; |
|
case VW_CH_IDX3: |
|
if (espi_vw_ch_cached_data.idx3 != espi_reg->EVIDX3) { |
|
espi_vw_ch_cached_data.idx3 = espi_reg->EVIDX3; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx3; |
|
break; |
|
case VW_CH_IDX4: |
|
vw_data = espi_vw_tx_cached_data.idx4; |
|
break; |
|
case VW_CH_IDX5: |
|
vw_data = espi_vw_tx_cached_data.idx5; |
|
break; |
|
case VW_CH_IDX6: |
|
vw_data = espi_vw_tx_cached_data.idx6; |
|
break; |
|
case VW_CH_IDX7: |
|
if (espi_vw_ch_cached_data.idx7 != espi_reg->EVIDX7) { |
|
espi_vw_ch_cached_data.idx7 = espi_reg->EVIDX7; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx7; |
|
break; |
|
case VW_CH_IDX40: |
|
vw_data = espi_vw_tx_cached_data.idx40; |
|
break; |
|
case VW_CH_IDX41: |
|
if (espi_vw_ch_cached_data.idx41 != espi_reg->EVIDX41) { |
|
espi_vw_ch_cached_data.idx41 = espi_reg->EVIDX41; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx41; |
|
break; |
|
case VW_CH_IDX42: |
|
if (espi_vw_ch_cached_data.idx42 != espi_reg->EVIDX42) { |
|
espi_vw_ch_cached_data.idx42 = espi_reg->EVIDX42; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx42; |
|
break; |
|
case VW_CH_IDX43: |
|
if (espi_vw_ch_cached_data.idx43 != espi_reg->EVIDX43) { |
|
espi_vw_ch_cached_data.idx43 = espi_reg->EVIDX43; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx43; |
|
break; |
|
case VW_CH_IDX44: |
|
if (espi_vw_ch_cached_data.idx44 != espi_reg->EVIDX44) { |
|
espi_vw_ch_cached_data.idx44 = espi_reg->EVIDX44; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx44; |
|
break; |
|
case VW_CH_IDX47: |
|
if (espi_vw_ch_cached_data.idx47 != espi_reg->EVIDX47) { |
|
espi_vw_ch_cached_data.idx47 = espi_reg->EVIDX47; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx47; |
|
break; |
|
case VW_CH_IDX4A: |
|
if (espi_vw_ch_cached_data.idx4a != espi_reg->EVIDX4A) { |
|
espi_vw_ch_cached_data.idx4a = espi_reg->EVIDX4A; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx4a; |
|
break; |
|
case VW_CH_IDX51: |
|
if (espi_vw_ch_cached_data.idx51 != espi_reg->EVIDX51) { |
|
espi_vw_ch_cached_data.idx51 = espi_reg->EVIDX51; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx51; |
|
break; |
|
case VW_CH_IDX61: |
|
if (espi_vw_ch_cached_data.idx61 != espi_reg->EVIDX61) { |
|
espi_vw_ch_cached_data.idx61 = espi_reg->EVIDX61; |
|
} |
|
vw_data = espi_vw_ch_cached_data.idx61; |
|
break; |
|
default: |
|
return -EIO; |
|
} |
|
|
|
if (IS_ENABLED(CONFIG_ESPI_VWIRE_VALID_BIT_CHECK)) { |
|
if (vw_data & valid_msk) { |
|
*level = !!(vw_data & lev_msk); |
|
} else { |
|
/* Not valid */ |
|
*level = 0; |
|
} |
|
} else { |
|
*level = !!(vw_data & lev_msk); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void espi_vw_ch_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
espi_reg->EVSTS |= ESPI_EVSTS_RXIDXCLR; |
|
|
|
espi_vw_ch_cached_data.idx2 = espi_reg->EVIDX2; |
|
espi_vw_ch_cached_data.idx3 = espi_reg->EVIDX3; |
|
espi_vw_ch_cached_data.idx7 = espi_reg->EVIDX7; |
|
espi_vw_ch_cached_data.idx41 = espi_reg->EVIDX41; |
|
espi_vw_ch_cached_data.idx42 = espi_reg->EVIDX42; |
|
espi_vw_ch_cached_data.idx43 = espi_reg->EVIDX43; |
|
espi_vw_ch_cached_data.idx44 = espi_reg->EVIDX44; |
|
espi_vw_ch_cached_data.idx47 = espi_reg->EVIDX47; |
|
espi_vw_ch_cached_data.idx4a = espi_reg->EVIDX4A; |
|
espi_vw_ch_cached_data.idx51 = espi_reg->EVIDX51; |
|
espi_vw_ch_cached_data.idx61 = espi_reg->EVIDX61; |
|
|
|
espi_vw_tx_cached_data.idx4 = 0; |
|
espi_vw_tx_cached_data.idx5 = 0; |
|
espi_vw_tx_cached_data.idx6 = 0; |
|
espi_vw_tx_cached_data.idx40 = 0; |
|
|
|
espi_reg->EVRXINTEN = (ESPI_EVRXINTEN_CFGCHGEN | ESPI_EVRXINTEN_RXCHGEN); |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_ch, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx2, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx3, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx7, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx41, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx42, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx43, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx44, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx47, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx4a, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx51, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx61, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_ch, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_ch, priority), espi_vw_ch_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_ch, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx2, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx2, priority), espi_vw_idx2_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx2, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx3, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx3, priority), espi_vw_idx3_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx3, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx7, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx7, priority), espi_vw_idx7_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx7, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx41, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx41, priority), espi_vw_idx41_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx41, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx42, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx42, priority), espi_vw_idx42_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx42, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx43, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx43, priority), espi_vw_idx43_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx43, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx44, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx44, priority), espi_vw_idx44_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx44, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx47, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx47, priority), espi_vw_idx47_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx47, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx4a, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx4a, priority), espi_vw_idx4a_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx4a, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx51, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx51, priority), espi_vw_idx51_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx51, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx61, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx61, priority), espi_vw_idx61_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), vw_idx61, irq)); |
|
} |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC |
|
|
|
#define ESPI_VW_EVENT_IDLE_TIMEOUT_US 1024UL |
|
#define ESPI_VW_EVENT_COMPLETE_TIMEOUT_US 10000UL |
|
|
|
static int espi_send_vw_event(uint8_t index, uint8_t data, const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
uint32_t i; |
|
|
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
if ((espi_reg->EVCFG & ESPI_EVCFG_CHEN) == 0 || (espi_reg->EVCFG & ESPI_EVCFG_CHRDY) == 0) { |
|
return -EBUSY; |
|
} |
|
|
|
/* Wait for TX FIFO to not be full before writing, with timeout */ |
|
if (!WAIT_FOR(!(espi_reg->EVSTS & ESPI_EVSTS_TXFULL), ESPI_VW_EVENT_IDLE_TIMEOUT_US, |
|
k_busy_wait(1))) { |
|
return -EBUSY; |
|
} |
|
|
|
i = 0x0000FFFF & ((uint32_t)index << 8 | (uint32_t)data); |
|
espi_reg->EVTXDAT = i; |
|
|
|
/* Wait for TX FIFO to not be full after writing, with shorter timeout */ |
|
if (!WAIT_FOR(!(espi_reg->EVSTS & ESPI_EVSTS_TXFULL), ESPI_VW_EVENT_COMPLETE_TIMEOUT_US, |
|
NULL)) { |
|
return -EBUSY; |
|
} |
|
|
|
espi_reg->EVSTS |= ESPI_EVSTS_TXDONE; |
|
|
|
return 0; |
|
} |
|
|
|
static void espi_send_vw_event_with_kbdata(uint8_t index, uint8_t data, uint32_t kbc_data, |
|
const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *const espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
volatile struct kbc_reg *const kbc_reg = espi_config->kbc_reg; |
|
uint32_t i; |
|
|
|
if ((espi_reg->EVCFG & ESPI_EVCFG_CHEN) == 0 || (espi_reg->EVCFG & ESPI_EVCFG_CHRDY) == 0) { |
|
return; |
|
} |
|
|
|
/* Wait for TX FIFO to not be full before writing, with timeout */ |
|
if (!WAIT_FOR(!(espi_reg->EVSTS & ESPI_EVSTS_TXFULL), ESPI_VW_EVENT_COMPLETE_TIMEOUT_US, |
|
k_busy_wait(1))) { |
|
return; |
|
} |
|
|
|
__disable_irq(); |
|
kbc_reg->OB = kbc_data; |
|
i = 0x0000FFFF & ((uint32_t)index << 8 | (uint32_t)data); |
|
espi_reg->EVTXDAT = i; |
|
|
|
/* Wait for TX FIFO to not be full after writing, in IRQ disabled state */ |
|
if (!WAIT_FOR(!(espi_reg->EVSTS & ESPI_EVSTS_TXFULL), ESPI_VW_EVENT_COMPLETE_TIMEOUT_US, |
|
k_busy_wait(1))) { |
|
espi_data->kbc_pre_irq1 = 1; |
|
__enable_irq(); |
|
return; |
|
} |
|
|
|
espi_data->kbc_pre_irq1 = 1; |
|
espi_reg->EVSTS |= ESPI_EVSTS_TXDONE; |
|
__enable_irq(); |
|
} |
|
|
|
#endif /* CONFIG_ESPI_PERIPHERAL_8042_KBC */ |
|
|
|
#endif /* CONFIG_ESPI_VWIRE_CHANNEL */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI OOB channel |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_OOB_CHANNEL |
|
|
|
#define MAX_OOB_TIMEOUT 200UL /* ms */ |
|
#define OOB_BUFFER_SIZE 256UL |
|
|
|
static int espi_rts5912_send_oob(const struct device *dev, struct espi_oob_packet *pckt) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
int ret; |
|
|
|
if (!(espi_reg->EOCFG & ESPI_EOCFG_CHRDY)) { |
|
LOG_ERR("%s: OOB channel isn't ready", __func__); |
|
return -EIO; |
|
} |
|
|
|
if (espi_data->oob_tx_busy) { |
|
LOG_ERR("%s: OOB channel is busy", __func__); |
|
return -EIO; |
|
} |
|
|
|
if (pckt->len > OOB_BUFFER_SIZE) { |
|
LOG_ERR("%s: OOB Tx have no insufficient space", __func__); |
|
return -EINVAL; |
|
} |
|
|
|
for (int i = 0; i < pckt->len; i++) { |
|
espi_data->oob_tx_ptr[i] = pckt->buf[i]; |
|
} |
|
|
|
espi_reg->EOTXLEN = pckt->len - 1; |
|
espi_reg->EOTXCTRL = ESPI_EOTXCTRL_TXSTR; |
|
|
|
espi_data->oob_tx_busy = true; |
|
|
|
/* Wait until ISR or timeout */ |
|
ret = k_sem_take(&espi_data->oob_tx_lock, K_MSEC(MAX_OOB_TIMEOUT)); |
|
if (ret == -EAGAIN) { |
|
return -ETIMEDOUT; |
|
} |
|
|
|
espi_data->oob_tx_busy = false; |
|
|
|
return 0; |
|
} |
|
|
|
static int espi_rts5912_receive_oob(const struct device *dev, struct espi_oob_packet *pckt) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint32_t rx_len; |
|
|
|
if (!(espi_reg->EOCFG & ESPI_EOCFG_CHRDY)) { |
|
LOG_ERR("%s: OOB channel isn't ready", __func__); |
|
return -EIO; |
|
} |
|
|
|
if (espi_reg->EOSTS & ESPI_EOSTS_RXPND) { |
|
LOG_ERR("OOB Receive Pending"); |
|
return -EIO; |
|
} |
|
|
|
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
|
/* Wait until ISR or timeout */ |
|
int ret = k_sem_take(&espi_data->oob_rx_lock, K_MSEC(MAX_OOB_TIMEOUT)); |
|
|
|
if (ret == -EAGAIN) { |
|
LOG_ERR("OOB Rx Timeout"); |
|
return -ETIMEDOUT; |
|
} |
|
#endif |
|
|
|
/* Check if buffer passed to driver can fit the received buffer */ |
|
rx_len = espi_reg->EORXLEN; |
|
|
|
if (rx_len > pckt->len) { |
|
LOG_ERR("space rcvd %d vs %d", rx_len, pckt->len); |
|
return -EIO; |
|
} |
|
|
|
pckt->len = rx_len; |
|
|
|
for (int i = 0; i < rx_len; i++) { |
|
pckt->buf[i] = espi_data->oob_rx_ptr[i]; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void espi_oob_tx_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint32_t status = espi_reg->EOSTS; |
|
|
|
if (status & ESPI_EOSTS_TXDONE) { |
|
k_sem_give(&espi_data->oob_tx_lock); |
|
espi_reg->EOSTS = ESPI_EOSTS_TXDONE; |
|
} |
|
} |
|
|
|
static void espi_oob_rx_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint32_t status = espi_reg->EOSTS; |
|
|
|
#ifdef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
|
struct espi_event evt = { |
|
.evt_type = ESPI_BUS_EVENT_OOB_RECEIVED, .evt_details = 0, .evt_data = 0}; |
|
#endif |
|
|
|
if (status & ESPI_EOSTS_RXDONE) { |
|
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
|
k_sem_give(&espi_data->oob_rx_lock); |
|
#else |
|
k_busy_wait(250); |
|
evt.evt_details = espi_reg->EORXLEN; |
|
espi_send_callbacks(&espi_data->callbacks, dev, evt); |
|
#endif |
|
espi_reg->EOSTS = ESPI_EOSTS_RXDONE; |
|
} |
|
} |
|
|
|
static void espi_oob_chg_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
|
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
struct espi_event evt = {.evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
|
.evt_details = ESPI_CHANNEL_OOB, |
|
.evt_data = 0}; |
|
|
|
uint32_t status = espi_reg->EOSTS; |
|
uint32_t config = espi_reg->EOCFG; |
|
|
|
if (status & ESPI_EOSTS_CFGENCHG) { |
|
evt.evt_data = config & ESPI_EVCFG_CHEN ? 1 : 0; |
|
espi_send_callbacks(&espi_data->callbacks, dev, evt); |
|
|
|
if (config & ESPI_EVCFG_CHEN) { |
|
vw_signal_set_valid(dev, ESPI_VWIRE_SIGNAL_OOB_RST_ACK, 1); |
|
} |
|
|
|
espi_reg->EOSTS = ESPI_EOSTS_CFGENCHG; |
|
} |
|
} |
|
|
|
static uint8_t oob_tx_buffer[OOB_BUFFER_SIZE] __aligned(4); |
|
static uint8_t oob_rx_buffer[OOB_BUFFER_SIZE] __aligned(4); |
|
|
|
static int espi_oob_ch_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
espi_data->oob_tx_busy = false; |
|
|
|
espi_data->oob_tx_ptr = oob_tx_buffer; |
|
if (espi_data->oob_tx_ptr == NULL) { |
|
LOG_ERR("Failed to allocate OOB Tx buffer"); |
|
return -ENOMEM; |
|
} |
|
|
|
espi_data->oob_rx_ptr = oob_rx_buffer; |
|
if (espi_data->oob_tx_ptr == NULL) { |
|
LOG_ERR("Failed to allocate OOB Rx buffer"); |
|
return -ENOMEM; |
|
} |
|
|
|
espi_reg->EOTXBUF = (uint32_t)espi_data->oob_tx_ptr; |
|
espi_reg->EORXBUF = (uint32_t)espi_data->oob_rx_ptr; |
|
|
|
espi_reg->EOTXINTEN = ESPI_EOTXINTEN_TXEN; |
|
espi_reg->EORXINTEN = (ESPI_EORXINTEN_RXEN | ESPI_EORXINTEN_CHENCHG); |
|
|
|
k_sem_init(&espi_data->oob_tx_lock, 0, 1); |
|
#ifndef CONFIG_ESPI_OOB_CHANNEL_RX_ASYNC |
|
k_sem_init(&espi_data->oob_rx_lock, 0, 1); |
|
#endif |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_tx, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_rx, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_chg, irq)); |
|
|
|
/* Tx */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_tx, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_tx, priority), espi_oob_tx_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_tx, irq)); |
|
|
|
/* Rx */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_rx, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_rx, priority), espi_oob_rx_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_rx, irq)); |
|
|
|
/* Chg */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_chg, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_chg, priority), espi_oob_chg_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), oob_chg, irq)); |
|
|
|
return 0; |
|
} |
|
|
|
#endif /* CONFIG_ESPI_OOB_CHANNEL */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI flash channel |
|
* ========================================================================= |
|
*/ |
|
|
|
#ifdef CONFIG_ESPI_FLASH_CHANNEL |
|
|
|
#define MAX_FLASH_TIMEOUT 1000UL |
|
#define MAF_BUFFER_SIZE 512UL |
|
|
|
enum { |
|
MAF_TR_READ = 0, |
|
MAF_TR_WRITE = 1, |
|
MAF_TR_ERASE = 2, |
|
}; |
|
|
|
static int espi_rts5912_flash_read(const struct device *dev, struct espi_flash_packet *pckt) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
int ret; |
|
uint32_t ctrl; |
|
|
|
if (!(espi_reg->EFCONF & ESPI_EFCONF_CHEN)) { |
|
LOG_ERR("Flash channel is disabled"); |
|
return -EIO; |
|
} |
|
|
|
if (pckt->len > MAF_BUFFER_SIZE) { |
|
LOG_ERR("Invalid size request"); |
|
return -EINVAL; |
|
} |
|
|
|
if (espi_reg->EMCTRL & ESPI_EMCTRL_START) { |
|
LOG_ERR("Channel still busy"); |
|
return -EBUSY; |
|
} |
|
|
|
ctrl = (MAF_TR_READ << ESPI_EMCTRL_MDSEL_Pos) | ESPI_EMCTRL_START; |
|
|
|
espi_reg->EMADR = pckt->flash_addr; |
|
espi_reg->EMTRLEN = pckt->len; |
|
espi_reg->EMCTRL = ctrl; |
|
|
|
/* Wait until ISR or timeout */ |
|
ret = k_sem_take(&espi_data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT)); |
|
if (ret == -EAGAIN) { |
|
LOG_ERR("%s timeout", __func__); |
|
return -ETIMEDOUT; |
|
} |
|
|
|
for (int i = 0; i < pckt->len; i++) { |
|
pckt->buf[i] = espi_data->maf_ptr[i]; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int espi_rts5912_flash_write(const struct device *dev, struct espi_flash_packet *pckt) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
int ret; |
|
uint32_t ctrl; |
|
|
|
if (!(espi_reg->EFCONF & ESPI_EFCONF_CHEN)) { |
|
LOG_ERR("Flash channel is disabled"); |
|
return -EIO; |
|
} |
|
|
|
if (pckt->len > MAF_BUFFER_SIZE) { |
|
LOG_ERR("Packet length is too big"); |
|
return -EINVAL; |
|
} |
|
|
|
if (espi_reg->EMCTRL & ESPI_EMCTRL_START) { |
|
LOG_ERR("Channel still busy"); |
|
return -EBUSY; |
|
} |
|
|
|
for (int i = 0; i < pckt->len; i++) { |
|
espi_data->maf_ptr[i] = pckt->buf[i]; |
|
} |
|
|
|
ctrl = (MAF_TR_WRITE << ESPI_EMCTRL_MDSEL_Pos) | ESPI_EMCTRL_START; |
|
|
|
espi_reg->EMADR = pckt->flash_addr; |
|
espi_reg->EMTRLEN = pckt->len; |
|
espi_reg->EMCTRL = ctrl; |
|
|
|
/* Wait until ISR or timeout */ |
|
ret = k_sem_take(&espi_data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT)); |
|
if (ret == -EAGAIN) { |
|
LOG_ERR("%s timeout", __func__); |
|
return -ETIMEDOUT; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int espi_rts5912_flash_erase(const struct device *dev, struct espi_flash_packet *pckt) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
int ret; |
|
uint32_t ctrl; |
|
|
|
if (!(espi_reg->EFCONF & ESPI_EFCONF_CHEN)) { |
|
LOG_ERR("Flash channel is disabled"); |
|
return -EIO; |
|
} |
|
|
|
if (espi_reg->EMCTRL & ESPI_EMCTRL_START) { |
|
LOG_ERR("Channel still busy"); |
|
return -EBUSY; |
|
} |
|
|
|
ctrl = (MAF_TR_ERASE << ESPI_EMCTRL_MDSEL_Pos) | ESPI_EMCTRL_START; |
|
|
|
espi_reg->EMADR = pckt->flash_addr; |
|
espi_reg->EMTRLEN = pckt->len; |
|
espi_reg->EMCTRL = ctrl; |
|
|
|
/* Wait until ISR or timeout */ |
|
ret = k_sem_take(&espi_data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT)); |
|
if (ret == -EAGAIN) { |
|
LOG_ERR("%s timeout", __func__); |
|
return -ETIMEDOUT; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void espi_maf_tr_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint32_t status = espi_reg->EFSTS; |
|
|
|
if (status & ESPI_EFSTS_MAFTXDN) { |
|
k_sem_give(&espi_data->flash_lock); |
|
espi_reg->EFSTS = ESPI_EFSTS_MAFTXDN; |
|
} |
|
} |
|
|
|
static void espi_flash_chg_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
struct espi_event evt = {.evt_type = ESPI_BUS_EVENT_CHANNEL_READY, |
|
.evt_details = ESPI_CHANNEL_FLASH, |
|
.evt_data = 0}; |
|
|
|
uint32_t status = espi_reg->EFSTS; |
|
uint32_t config = espi_reg->EFCONF; |
|
|
|
if (status & ESPI_EFSTS_CHENCHG) { |
|
evt.evt_data = (config & ESPI_EFCONF_CHEN) ? 1 : 0; |
|
espi_send_callbacks(&espi_data->callbacks, dev, evt); |
|
|
|
espi_reg->EFSTS = ESPI_EFSTS_CHENCHG; |
|
} |
|
} |
|
|
|
static uint8_t flash_channel_buffer[MAF_BUFFER_SIZE] __aligned(4); |
|
|
|
static int espi_flash_ch_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *espi_data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
espi_data->maf_ptr = flash_channel_buffer; |
|
if (espi_data->maf_ptr == NULL) { |
|
LOG_ERR("Failed to allocate MAF buffer"); |
|
return -ENOMEM; |
|
} |
|
|
|
espi_reg->EMBUF = (uint32_t)espi_data->maf_ptr; |
|
espi_reg->EMINTEN = ESPI_EMINTEN_CHENCHG | ESPI_EMINTEN_TRDONEEN; |
|
|
|
k_sem_init(&espi_data->flash_lock, 0, 1); |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), maf_tr, irq)); |
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), flash_chg, irq)); |
|
|
|
/* MAF Tr */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), maf_tr, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), maf_tr, priority), espi_maf_tr_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), maf_tr, irq)); |
|
|
|
/* Chg */ |
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), flash_chg, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), flash_chg, priority), espi_flash_chg_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), flash_chg, irq)); |
|
|
|
return 0; |
|
} |
|
|
|
#endif /* CONFIG_ESPI_FLASH_CHANNEL */ |
|
|
|
/* |
|
* ========================================================================= |
|
* ESPI common function and API |
|
* ========================================================================= |
|
*/ |
|
|
|
#define RTS5912_ESPI_MAX_FREQ_20 20 |
|
#define RTS5912_ESPI_MAX_FREQ_25 25 |
|
#define RTS5912_ESPI_MAX_FREQ_33 33 |
|
#define RTS5912_ESPI_MAX_FREQ_50 50 |
|
#define RTS5912_ESPI_MAX_FREQ_66 66 |
|
|
|
static int espi_rts5912_configure(const struct device *dev, struct espi_cfg *cfg) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *data = dev->data; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
uint32_t gen_conf = 0; |
|
uint8_t io_mode = 0; |
|
|
|
/* Maximum Frequency Supported */ |
|
switch (cfg->max_freq) { |
|
case RTS5912_ESPI_MAX_FREQ_20: |
|
gen_conf |= 0UL << ESPI_ESPICFG_MXFREQSUP_Pos; |
|
break; |
|
case RTS5912_ESPI_MAX_FREQ_25: |
|
gen_conf |= 1UL << ESPI_ESPICFG_MXFREQSUP_Pos; |
|
break; |
|
case RTS5912_ESPI_MAX_FREQ_33: |
|
gen_conf |= 2UL << ESPI_ESPICFG_MXFREQSUP_Pos; |
|
break; |
|
case RTS5912_ESPI_MAX_FREQ_50: |
|
gen_conf |= 3UL << ESPI_ESPICFG_MXFREQSUP_Pos; |
|
break; |
|
case RTS5912_ESPI_MAX_FREQ_66: |
|
gen_conf |= 4UL << ESPI_ESPICFG_MXFREQSUP_Pos; |
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
/* I/O Mode Supported */ |
|
io_mode = cfg->io_caps >> 1; |
|
|
|
if (io_mode > 3) { |
|
return -EINVAL; |
|
} |
|
gen_conf |= io_mode << ESPI_ESPICFG_IOSUP_Pos; |
|
|
|
/* Channel Supported */ |
|
if (cfg->channel_caps & ESPI_CHANNEL_PERIPHERAL) { |
|
gen_conf |= BIT(0) << ESPI_ESPICFG_CHSUP_Pos; |
|
} |
|
|
|
if (cfg->channel_caps & ESPI_CHANNEL_VWIRE) { |
|
gen_conf |= BIT(1) << ESPI_ESPICFG_CHSUP_Pos; |
|
} |
|
|
|
if (cfg->channel_caps & ESPI_CHANNEL_OOB) { |
|
gen_conf |= BIT(2) << ESPI_ESPICFG_CHSUP_Pos; |
|
} |
|
|
|
if (cfg->channel_caps & ESPI_CHANNEL_FLASH) { |
|
gen_conf |= BIT(3) << ESPI_ESPICFG_CHSUP_Pos; |
|
} |
|
|
|
espi_reg->ESPICFG = gen_conf; |
|
data->config_data = espi_reg->ESPICFG; |
|
|
|
return 0; |
|
} |
|
|
|
static bool espi_rts5912_channel_ready(const struct device *dev, enum espi_channel ch) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
switch (ch) { |
|
case ESPI_CHANNEL_PERIPHERAL: |
|
return espi_reg->EPCFG & ESPI_EPCFG_CHEN ? true : false; |
|
case ESPI_CHANNEL_VWIRE: |
|
return espi_reg->EVCFG & ESPI_EVCFG_CHEN ? true : false; |
|
case ESPI_CHANNEL_OOB: |
|
return espi_reg->EOCFG & ESPI_EOCFG_CHEN ? true : false; |
|
case ESPI_CHANNEL_FLASH: |
|
return espi_reg->EFCONF & ESPI_EFCONF_CHEN ? true : false; |
|
default: |
|
return false; |
|
} |
|
} |
|
|
|
static int espi_rts5912_manage_callback(const struct device *dev, struct espi_callback *callback, |
|
bool set) |
|
{ |
|
struct espi_rts5912_data *data = dev->data; |
|
|
|
return espi_manage_callback(&data->callbacks, callback, set); |
|
} |
|
|
|
static DEVICE_API(espi, espi_rts5912_driver_api) = { |
|
.config = espi_rts5912_configure, |
|
.get_channel_status = espi_rts5912_channel_ready, |
|
.manage_callback = espi_rts5912_manage_callback, |
|
#ifdef CONFIG_ESPI_PERIPHERAL_CHANNEL |
|
.read_lpc_request = espi_rts5912_read_lpc_request, |
|
.write_lpc_request = espi_rts5912_write_lpc_request, |
|
#endif |
|
#ifdef CONFIG_ESPI_VWIRE_CHANNEL |
|
.send_vwire = espi_rts5912_send_vwire, |
|
.receive_vwire = espi_rts5912_receive_vwire, |
|
#endif |
|
#ifdef CONFIG_ESPI_OOB_CHANNEL |
|
.send_oob = espi_rts5912_send_oob, |
|
.receive_oob = espi_rts5912_receive_oob, |
|
#endif |
|
#ifdef CONFIG_ESPI_FLASH_CHANNEL |
|
.flash_read = espi_rts5912_flash_read, |
|
.flash_write = espi_rts5912_flash_write, |
|
.flash_erase = espi_rts5912_flash_erase, |
|
#endif |
|
}; |
|
|
|
static void espi_vw_ch_setup(const struct device *dev); |
|
|
|
#define VW_RESET_DELAY 150UL |
|
|
|
static void espi_rst_isr(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct espi_rts5912_data *data = dev->data; |
|
|
|
struct espi_event evt = {.evt_type = ESPI_BUS_RESET, .evt_details = 0, .evt_data = 0}; |
|
|
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
uint32_t status = espi_reg->ERSTCFG; |
|
|
|
espi_reg->ERSTCFG |= ESPI_ERSTCFG_RSTSTS; |
|
espi_reg->ERSTCFG ^= ESPI_ERSTCFG_RSTPOL; |
|
|
|
if (status & ESPI_ERSTCFG_RSTSTS) { |
|
if (status & ESPI_ERSTCFG_RSTPOL) { |
|
/* rst pin high go low trigger interrupt */ |
|
evt.evt_data = 0; |
|
} else { |
|
/* rst pin low go high trigger interrupt */ |
|
evt.evt_data = 1; |
|
#ifdef CONFIG_ESPI_VWIRE_CHANNEL |
|
espi_vw_ch_setup(dev); |
|
espi_reg->ESPICFG = data->config_data; |
|
if (espi_reg->EVCFG & ESPI_EVCFG_CHEN) { |
|
k_timeout_t delay = K_MSEC(VW_RESET_DELAY); |
|
|
|
k_work_schedule(&vw_ch_isr_wa, delay); |
|
} |
|
#endif |
|
} |
|
espi_send_callbacks(&data->callbacks, dev, evt); |
|
} |
|
} |
|
|
|
static void espi_bus_reset_setup(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
volatile struct espi_reg *const espi_reg = espi_config->espi_reg; |
|
|
|
espi_reg->ERSTCFG = ESPI_ERSTCFG_RSTINTEN; |
|
espi_reg->ERSTCFG = ESPI_ERSTCFG_RSTMONEN; |
|
|
|
if (espi_reg->ERSTCFG & ESPI_ERSTCFG_RSTSTS) { |
|
/* high to low */ |
|
espi_reg->ERSTCFG = |
|
ESPI_ERSTCFG_RSTMONEN | ESPI_ERSTCFG_RSTPOL | ESPI_ERSTCFG_RSTINTEN; |
|
} else { |
|
/* low to high */ |
|
espi_reg->ERSTCFG = ESPI_ERSTCFG_RSTMONEN | ESPI_ERSTCFG_RSTINTEN; |
|
} |
|
|
|
NVIC_ClearPendingIRQ(DT_IRQ_BY_NAME(DT_DRV_INST(0), bus_rst, irq)); |
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(DT_DRV_INST(0), bus_rst, irq), |
|
DT_IRQ_BY_NAME(DT_DRV_INST(0), bus_rst, priority), espi_rst_isr, |
|
DEVICE_DT_GET(DT_DRV_INST(0)), 0); |
|
irq_enable(DT_IRQ_BY_NAME(DT_DRV_INST(0), bus_rst, irq)); |
|
} |
|
|
|
static int espi_rts5912_init(const struct device *dev) |
|
{ |
|
const struct espi_rts5912_config *const espi_config = dev->config; |
|
struct rts5912_sccon_subsys sccon; |
|
|
|
int rc; |
|
|
|
/* Setup eSPI pins */ |
|
rc = pinctrl_apply_state(espi_config->pcfg, PINCTRL_STATE_DEFAULT); |
|
if (rc < 0) { |
|
LOG_ERR("eSPI pinctrl setup failed (%d)", rc); |
|
return rc; |
|
} |
|
|
|
if (!device_is_ready(espi_config->clk_dev)) { |
|
LOG_ERR("eSPI clock not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
/* Enable eSPI clock */ |
|
sccon.clk_grp = espi_config->espislv_clk_grp; |
|
sccon.clk_idx = espi_config->espislv_clk_idx; |
|
rc = clock_control_on(espi_config->clk_dev, (clock_control_subsys_t)&sccon); |
|
if (rc != 0) { |
|
LOG_ERR("eSPI clock control on failed"); |
|
goto exit; |
|
} |
|
|
|
/* Setup eSPI bus reset */ |
|
espi_bus_reset_setup(dev); |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC |
|
/* Setup KBC */ |
|
rc = espi_kbc_setup(dev); |
|
if (rc != 0) { |
|
LOG_ERR("eSPI KBC setup failed"); |
|
goto exit; |
|
} |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION |
|
espi_setup_acpi_shm(espi_config); |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO |
|
/* Setup ACPI */ |
|
rc = espi_acpi_setup(dev); |
|
if (rc != 0) { |
|
LOG_ERR("eSPI ACPI setup failed"); |
|
goto exit; |
|
} |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD |
|
rc = espi_promt0_setup(dev); |
|
if (rc != 0) { |
|
LOG_ERR("eSPI Promt0 setup failed"); |
|
goto exit; |
|
} |
|
|
|
espi_setup_host_cmd_shm(espi_config); |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80 |
|
/* Setup Port80 */ |
|
rc = espi_peri_ch_port80_setup(dev); |
|
if (rc != 0) { |
|
LOG_ERR("eSPI Port80 setup failed"); |
|
goto exit; |
|
} |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_PERIPHERAL_CHANNEL |
|
/* Setup eSPI peripheral channel */ |
|
espi_periph_ch_setup(dev); |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_VWIRE_CHANNEL |
|
/* Setup eSPI virtual-wire channel */ |
|
espi_vw_ch_setup(dev); |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_OOB_CHANNEL |
|
/* Setup eSPI OOB channel */ |
|
rc = espi_oob_ch_setup(dev); |
|
if (rc != 0) { |
|
LOG_ERR("eSPI OOB channel setup failed"); |
|
goto exit; |
|
} |
|
#endif |
|
|
|
#ifdef CONFIG_ESPI_FLASH_CHANNEL |
|
/* Setup eSPI flash channel */ |
|
rc = espi_flash_ch_setup(dev); |
|
if (rc != 0) { |
|
LOG_ERR("eSPI flash channel setup failed"); |
|
goto exit; |
|
} |
|
#endif |
|
|
|
exit: |
|
return rc; |
|
} |
|
|
|
PINCTRL_DT_INST_DEFINE(0); |
|
|
|
static struct espi_rts5912_data espi_rts5912_data_0; |
|
|
|
static const struct espi_rts5912_config espi_rts5912_config = { |
|
.espi_reg = (volatile struct espi_reg *const)DT_INST_REG_ADDR_BY_NAME(0, espi_target), |
|
.espislv_clk_grp = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), espi_target, clk_grp), |
|
.espislv_clk_idx = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), espi_target, clk_idx), |
|
#ifdef CONFIG_ESPI_PERIPHERAL_8042_KBC |
|
.kbc_reg = (volatile struct kbc_reg *const)DT_INST_REG_ADDR_BY_NAME(0, kbc), |
|
.kbc_clk_grp = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), kbc, clk_grp), |
|
.kbc_clk_idx = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), kbc, clk_idx), |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_HOST_IO |
|
.acpi_reg = (volatile struct acpi_reg *const)DT_INST_REG_ADDR_BY_NAME(0, acpi), |
|
.acpi_clk_grp = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), acpi, clk_grp), |
|
.acpi_clk_idx = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), acpi, clk_idx), |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD |
|
.promt0_reg = (volatile struct acpi_reg *const)DT_INST_REG_ADDR_BY_NAME(0, promt0), |
|
.promt0_clk_grp = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), promt0, clk_grp), |
|
.promt0_clk_idx = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), promt0, clk_idx), |
|
|
|
.emi0_reg = (volatile struct emi_reg *const)DT_INST_REG_ADDR_BY_NAME(0, emi0), |
|
.emi0_clk_grp = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), emi0, clk_grp), |
|
.emi0_clk_idx = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), emi0, clk_idx), |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION |
|
.emi1_reg = (volatile struct emi_reg *const)DT_INST_REG_ADDR_BY_NAME(0, emi1), |
|
.emi1_clk_grp = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), emi1, clk_grp), |
|
.emi1_clk_idx = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), emi1, clk_idx), |
|
#endif |
|
#ifdef CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80 |
|
.port80_reg = (volatile struct port80_reg *const)DT_INST_REG_ADDR_BY_NAME(0, port80), |
|
.port80_clk_grp = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), port80, clk_grp), |
|
.port80_clk_idx = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(0), port80, clk_idx), |
|
#endif |
|
.clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), |
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), |
|
}; |
|
|
|
DEVICE_DT_INST_DEFINE(0, &espi_rts5912_init, NULL, &espi_rts5912_data_0, &espi_rts5912_config, |
|
PRE_KERNEL_2, CONFIG_ESPI_INIT_PRIORITY, &espi_rts5912_driver_api);
|
|
|