| /* |
| * 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); |