From da767376ca6c331ac30f2263418badd999458f48 Mon Sep 17 00:00:00 2001 From: jhan bo chao Date: Sun, 4 May 2025 02:43:59 +0800 Subject: [PATCH] driver: espi: add espi peripheral channel 8042_KBC driver for rts5912 add espi peripheral channel 8042_KBC driver for rts5912 Signed-off-by: jhan bo chao --- drivers/espi/Kconfig.rts5912 | 3 + drivers/espi/espi_realtek_rts5912.c | 315 +++++++++++++++++++++++++++ soc/realtek/ec/rts5912/reg/reg_kbc.h | 66 ++++++ 3 files changed, 384 insertions(+) create mode 100644 soc/realtek/ec/rts5912/reg/reg_kbc.h diff --git a/drivers/espi/Kconfig.rts5912 b/drivers/espi/Kconfig.rts5912 index 045a18ae32b..e606afe7fbe 100644 --- a/drivers/espi/Kconfig.rts5912 +++ b/drivers/espi/Kconfig.rts5912 @@ -10,6 +10,9 @@ config ESPI_RTS5912 if ESPI_RTS5912 +config ESPI_PERIPHERAL_8042_KBC + default y + config ESPI_PERIPHERAL_ACPI_SHM_REGION default y diff --git a/drivers/espi/espi_realtek_rts5912.c b/drivers/espi/espi_realtek_rts5912.c index d35593f4747..6c6597489d9 100644 --- a/drivers/espi/espi_realtek_rts5912.c +++ b/drivers/espi/espi_realtek_rts5912.c @@ -20,6 +20,7 @@ LOG_MODULE_REGISTER(espi, CONFIG_ESPI_LOG_LEVEL); #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"); @@ -28,6 +29,11 @@ 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; @@ -58,6 +64,10 @@ struct espi_rts5912_config { 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; @@ -71,6 +81,222 @@ struct espi_rts5912_data { #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 @@ -1238,6 +1464,81 @@ static void espi_vw_ch_setup(const struct device *dev) 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 */ /* @@ -1883,6 +2184,15 @@ static int espi_rts5912_init(const struct device *dev) /* 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 @@ -1950,6 +2260,11 @@ 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), diff --git a/soc/realtek/ec/rts5912/reg/reg_kbc.h b/soc/realtek/ec/rts5912/reg/reg_kbc.h new file mode 100644 index 00000000000..85e744fac3c --- /dev/null +++ b/soc/realtek/ec/rts5912/reg/reg_kbc.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2025 Realtek, SIBG-SD7 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_SOC_REALTEK_RTS5912_REG_KBC_H +#define ZEPHYR_SOC_REALTEK_RTS5912_REG_KBC_H + +struct kbc_reg { + uint32_t STS; + uint32_t IB; + uint32_t OB; + uint32_t PTADDR; + uint32_t INTEN; + uint32_t VWCTRL0; + uint32_t VWCTRL1; +}; + +/* STS */ +#define KBC_STS_OBF BIT(0) +#define KBC_STS_IBF BIT(1) +#define KBC_STS_STS0 BIT(2) +#define KBC_STS_CMDSEL BIT(3) +#define KBC_STS_STS1 BIT(4) +#define KBC_STS_STS2 BIT(5) +#define KBC_STS_STS3 BIT(6) +#define KBC_STS_STS4 BIT(7) + +/* IB */ +#define KBC_IB_IBDAT_Pos (0UL) +#define KBC_IB_IBDAT_Msk GENMASK(7, 0) +#define KBC_IB_IBCLR_Pos (8UL) +#define KBC_IB_IBCLR BIT(8) + +/* OB */ +#define KBC_OB_OBDAT_Pos (0UL) +#define KBC_OB_OBDAT_Msk GENMASK(7, 0) +#define KBC_OB_OBCLR_Pos (8UL) +#define KBC_OB_OBCLR BIT(8) + +/* PTADDR */ +#define KBC_PTADDR_DATADDR_Pos (0UL) +#define KBC_PTADDR_DATADDR_Msk GENMASK(11, 0) +#define KBC_PTADDR_CMDOFS_Pos (12UL) +#define KBC_PTADDR_CMDOFS_Msk GENMASK(14, 12) + +/* INTEN */ +#define KBC_INTEN_OBFINTEN_Pos (0UL) +#define KBC_INTEN_OBFINTEN BIT(0) +#define KBC_INTEN_IBFINTEN_Pos (1UL) +#define KBC_INTEN_IBFINTEN BIT(1) + +/* VWCTRL0 */ +#define KBC_VWCTRL0_IRQEN_Pos (0UL) +#define KBC_VWCTRL0_IRQEN BIT(0) +#define KBC_VWCTRL0_TGLV_Pos (1UL) +#define KBC_VWCTRL0_TGLV BIT(1) + +/* VWCTRL1 */ +#define KBC_VWCTRL1_IRQNUM_Pos (0UL) +#define KBC_VWCTRL1_IRQNUM_Msk GENMASK(7, 0) +#define KBC_VWCTRL1_ACTEN_Pos (8UL) +#define KBC_VWCTRL1_ACTEN BIT(8) + +#endif /* ZEPHYR_SOC_REALTEK_RTS5912_REG_KBC_H */