| /* |
| * Copyright (c) 2020 Nuvoton Technology Corporation. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT nuvoton_npcx_host_sub |
| |
| /** |
| * @file |
| * @brief Nuvoton NPCX host sub modules driver |
| * |
| * This file contains the drivers of NPCX Host Sub-Modules that serve as an |
| * interface between the Host and Core domains. Please refer the block diagram. |
| * |
| * +------------+ |
| * | Serial |---> TXD |
| * +<--->| Port |<--- RXD |
| * | | |<--> ... |
| * | +------------+ |
| * | +------------+ | |
| * +------------+ |<--->| KBC & PM |<--->| |
| * eSPI_CLK --->| eSPI Bus | | | Channels | | |
| * eSPI_RST --->| Controller | | +------------+ | |
| * eSPI_IO3-0 <-->| |<-->| +------------+ | |
| * eSPI_CS --->| (eSPI mode)| | | Shared | | |
| * eSPI_ALERT <-->| | |<--->| Memory |<--->| |
| * +------------+ | +------------+ | |
| * | +------------+ | |
| * |<--->| MSWC |<--->| |
| * | +------------+ | |
| * | +------------+ | |
| * | | Core | | |
| * |<--->| to Host |<--->| |
| * | | Access | | |
| * | +------------+ | |
| * HMIB | Core Bus |
| * (Host Modules Internal Bus) +------------ |
| * |
| * |
| * For most of them, the Host can configure these modules via eSPI(Peripheral |
| * Channel)/LPC by accessing 'Configuration and Control register Set' which IO |
| * base address is 0x4E as default. (The table below illustrates structure of |
| * 'Configuration and Control Register Set') And the interrupts in core domain |
| * help handling any events from host side. |
| * |
| * Index | Configuration and Control Register Set |
| * --------|--------------------------------------------------+ Bank Select |
| * 07h | Logical Device Number Register (LDN) |---------+ |
| * --------|--------------------------------------------------- | |
| * 20-2Fh | SuperI/O Configuration Registers | | |
| * ------------------------------------------------------------ | |
| * --------|---------------------------------------------------_ | |
| * 30h | Logical Device Control Register | |_ | |
| * --------|--------------------------------------------------- | |_ | |
| * 60-63h | I/O Space Configuration Registers | | | | | |
| * --------|--------------------------------------------------- | | | | |
| * 70-71h | Interrupt Configuration Registers | | | | | |
| * --------|--------------------------------------------------- | | | | |
| * 73-74h | DMA Configuration Registers (No support in NPCX) | | | | | |
| * --------|--------------------------------------------------- | | |<--+ |
| * F0-FFh | Special Logical Device Configuration Registers | | | | |
| * --------|--------------------------------------------------- | | | |
| * |--------------------------------------------------- | | |
| * |--------------------------------------------------- | |
| * |--------------------------------------------------- |
| * |
| * |
| * This driver introduces six host sub-modules. It includes: |
| * |
| * 1. Keyboard and Mouse Controller (KBC) interface. |
| * ● Intel 8051SL-compatible Host interface |
| * — 8042 KBD standard interface (ports 60h, 64h) |
| * — Legacy IRQ: IRQ1 (KBD) and IRQ12 (mouse) support |
| * ● Configured by two logical devices: Keyboard and Mouse (LDN 0x06/0x05) |
| * |
| * 2. Power Management (PM) channels. |
| * ● PM channel registers |
| * — Command/Status register |
| * — Data register |
| * channel 1: legacy 62h, 66h; channel 2: legacy 68h, 6Ch; |
| * channel 3: legacy 6Ah, 6Eh; channel 4: legacy 6Bh, 6Fh; |
| * ● PM interrupt using: |
| * — Serial IRQ |
| * — SMI |
| * — EC_SCI |
| * ● Configured by four logical devices: PM1/2/3/4 (LDN 0x11/0x12/0x17/0x1E) |
| * |
| * 3. Shared Memory mechanism (SHM). |
| * This module allows sharing of the on-chip RAM by both Core and the Host. |
| * It also supports the following features: |
| * ● Four Core/Host communication windows for direct RAM access |
| * ● Eight Protection regions for each access window |
| * ● Host IRQ and SMI generation |
| * ● Port 80 debug support |
| * ● Configured by one logical device: SHM (LDN 0x0F) |
| * |
| * 4. Core Access to Host Modules (C2H). |
| * ● A interface to access module registers in host domain. |
| * It enables the Core to access the registers in host domain (i.e., Host |
| * Configuration, Serial Port, SHM, and MSWC), through HMIB. |
| * |
| * 5. Mobile System Wake-Up functions (MSWC). |
| * It detects and handles wake-up events from various sources in the Host |
| * modules and alerts the Core for better power consumption. |
| * |
| * 6. Serial Port (Legacy UART) |
| * It provides UART functionality by supporting serial data communication |
| * with a remote peripheral device or a modem. |
| * |
| * INCLUDE FILES: soc_host.h |
| * |
| */ |
| |
| #include <assert.h> |
| #include <zephyr/drivers/espi.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/drivers/clock_control.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/sys/ring_buffer.h> |
| #include <soc.h> |
| #include "espi_utils.h" |
| #include "soc_host.h" |
| #include "soc_espi.h" |
| #include "soc_miwu.h" |
| |
| #include <zephyr/logging/log.h> |
| #include <zephyr/irq.h> |
| LOG_MODULE_REGISTER(host_sub_npcx, LOG_LEVEL_ERR); |
| |
| struct host_sub_npcx_config { |
| /* host module instances */ |
| struct mswc_reg *const inst_mswc; |
| struct shm_reg *const inst_shm; |
| struct c2h_reg *const inst_c2h; |
| struct kbc_reg *const inst_kbc; |
| struct pmch_reg *const inst_pm_acpi; |
| struct pmch_reg *const inst_pm_hcmd; |
| /* clock configuration */ |
| const uint8_t clks_size; |
| const struct npcx_clk_cfg *clks_list; |
| /* mapping table between host access signals and wake-up input */ |
| struct npcx_wui host_acc_wui; |
| }; |
| |
| struct host_sub_npcx_data { |
| sys_slist_t *callbacks; /* pointer on the espi callback list */ |
| uint8_t plt_rst_asserted; /* current PLT_RST# status */ |
| uint8_t espi_rst_asserted; /* current ESPI_RST# status */ |
| const struct device *host_bus_dev; /* device for eSPI/LPC bus */ |
| #ifdef CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE |
| struct ring_buf port80_ring_buf; |
| uint8_t port80_data[CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_RING_BUF_SIZE]; |
| struct k_work work; |
| #endif |
| }; |
| |
| struct npcx_dp80_buf { |
| union { |
| uint16_t offset_code_16; |
| uint8_t offset_code[2]; |
| }; |
| }; |
| |
| static const struct npcx_clk_cfg host_dev_clk_cfg[] = |
| NPCX_DT_CLK_CFG_ITEMS_LIST(0); |
| |
| struct host_sub_npcx_config host_sub_cfg = { |
| .inst_mswc = (struct mswc_reg *)DT_INST_REG_ADDR_BY_NAME(0, mswc), |
| .inst_shm = (struct shm_reg *)DT_INST_REG_ADDR_BY_NAME(0, shm), |
| .inst_c2h = (struct c2h_reg *)DT_INST_REG_ADDR_BY_NAME(0, c2h), |
| .inst_kbc = (struct kbc_reg *)DT_INST_REG_ADDR_BY_NAME(0, kbc), |
| .inst_pm_acpi = (struct pmch_reg *)DT_INST_REG_ADDR_BY_NAME(0, pm_acpi), |
| .inst_pm_hcmd = (struct pmch_reg *)DT_INST_REG_ADDR_BY_NAME(0, pm_hcmd), |
| .host_acc_wui = NPCX_DT_WUI_ITEM_BY_NAME(0, host_acc_wui), |
| .clks_size = ARRAY_SIZE(host_dev_clk_cfg), |
| .clks_list = host_dev_clk_cfg, |
| }; |
| |
| struct host_sub_npcx_data host_sub_data; |
| |
| /* Application shouldn't touch these flags in KBC status register directly */ |
| #define NPCX_KBC_STS_MASK (BIT(NPCX_HIKMST_IBF) | BIT(NPCX_HIKMST_OBF) \ |
| | BIT(NPCX_HIKMST_A2)) |
| |
| /* IO base address of EC Logical Device Configuration */ |
| #define NPCX_EC_CFG_IO_ADDR 0x4E |
| |
| /* Timeout to wait for Core-to-Host transaction to be completed. */ |
| #define NPCX_C2H_TRANSACTION_TIMEOUT_US 200 |
| |
| /* Logical Device Number Assignments */ |
| #define EC_CFG_LDN_MOUSE 0x05 |
| #define EC_CFG_LDN_KBC 0x06 |
| #define EC_CFG_LDN_SHM 0x0F |
| #define EC_CFG_LDN_ACPI 0x11 /* PM Channel 1 */ |
| #define EC_CFG_LDN_HCMD 0x12 /* PM Channel 2 */ |
| |
| /* Index of EC (4E/4F) Configuration Register */ |
| #define EC_CFG_IDX_LDN 0x07 |
| #define EC_CFG_IDX_CTRL 0x30 |
| #define EC_CFG_IDX_CMD_IO_ADDR_H 0x60 |
| #define EC_CFG_IDX_CMD_IO_ADDR_L 0x61 |
| #define EC_CFG_IDX_DATA_IO_ADDR_H 0x62 |
| #define EC_CFG_IDX_DATA_IO_ADDR_L 0x63 |
| |
| /* Index of Special Logical Device Configuration (Shared Memory Module) */ |
| #define EC_CFG_IDX_SHM_CFG 0xF1 |
| #define EC_CFG_IDX_SHM_WND1_ADDR_0 0xF4 |
| #define EC_CFG_IDX_SHM_WND1_ADDR_1 0xF5 |
| #define EC_CFG_IDX_SHM_WND1_ADDR_2 0xF6 |
| #define EC_CFG_IDX_SHM_WND1_ADDR_3 0xF7 |
| #define EC_CFG_IDX_SHM_WND2_ADDR_0 0xF8 |
| #define EC_CFG_IDX_SHM_WND2_ADDR_1 0xF9 |
| #define EC_CFG_IDX_SHM_WND2_ADDR_2 0xFA |
| #define EC_CFG_IDX_SHM_WND2_ADDR_3 0xFB |
| #define EC_CFG_IDX_SHM_DP80_ADDR_RANGE 0xFD |
| |
| /* Host sub-device local inline functions */ |
| static inline uint8_t host_shd_mem_wnd_size_sl(uint32_t size) |
| { |
| /* The minimum supported shared memory region size is 8 bytes */ |
| if (size <= 8U) { |
| size = 8U; |
| } |
| |
| /* The maximum supported shared memory region size is 4K bytes */ |
| if (size >= 4096U) { |
| size = 4096U; |
| } |
| |
| /* |
| * If window size is not a power-of-two, it is rounded-up to the next |
| * power-of-two value, and return value corresponds to RWINx_SIZE field. |
| */ |
| return (32 - __builtin_clz(size - 1U)) & 0xff; |
| } |
| |
| /* Host KBC sub-device local functions */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_8042_KBC) |
| static void host_kbc_ibf_isr(const void *arg) |
| { |
| ARG_UNUSED(arg); |
| struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc; |
| struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION, |
| ESPI_PERIPHERAL_8042_KBC, ESPI_PERIPHERAL_NODATA |
| }; |
| struct espi_evt_data_kbc *kbc_evt = |
| (struct espi_evt_data_kbc *)&evt.evt_data; |
| |
| /* KBC Input Buffer Full event */ |
| kbc_evt->evt = HOST_KBC_EVT_IBF; |
| /* The data in KBC Input Buffer */ |
| kbc_evt->data = inst_kbc->HIKMDI; |
| /* |
| * Indicates if the host sent a command or data. |
| * 0 = data |
| * 1 = Command. |
| */ |
| kbc_evt->type = IS_BIT_SET(inst_kbc->HIKMST, NPCX_HIKMST_A2); |
| |
| LOG_DBG("%s: kbc data 0x%02x", __func__, evt.evt_data); |
| espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, |
| evt); |
| } |
| |
| static void host_kbc_obe_isr(const void *arg) |
| { |
| ARG_UNUSED(arg); |
| struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc; |
| struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION, |
| ESPI_PERIPHERAL_8042_KBC, ESPI_PERIPHERAL_NODATA |
| }; |
| struct espi_evt_data_kbc *kbc_evt = |
| (struct espi_evt_data_kbc *)&evt.evt_data; |
| |
| /* Disable KBC OBE interrupt first */ |
| inst_kbc->HICTRL &= ~BIT(NPCX_HICTRL_OBECIE); |
| |
| LOG_DBG("%s: kbc status 0x%02x", __func__, inst_kbc->HIKMST); |
| |
| /* |
| * Notify application that host already read out data. The application |
| * might need to clear status register via espi_api_lpc_write_request() |
| * with E8042_CLEAR_FLAG opcode in callback. |
| */ |
| kbc_evt->evt = HOST_KBC_EVT_OBE; |
| kbc_evt->data = 0; |
| kbc_evt->type = 0; |
| |
| espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, |
| evt); |
| } |
| |
| static void host_kbc_init(void) |
| { |
| struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc; |
| |
| /* Make sure the previous OBF and IRQ has been sent out. */ |
| k_busy_wait(4); |
| /* Set FW_OBF to clear OBF flag in both STATUS and HIKMST to 0 */ |
| inst_kbc->HICTRL |= BIT(NPCX_HICTRL_FW_OBF); |
| /* Ensure there is no OBF set in this period. */ |
| k_busy_wait(4); |
| |
| /* |
| * Init KBC with: |
| * 1. Enable Input Buffer Full (IBF) core interrupt for Keyboard/mouse. |
| * 2. Enable Output Buffer Full Mouse(OBFM) SIRQ 12. |
| * 3. Enable Output Buffer Full Keyboard (OBFK) SIRQ 1. |
| */ |
| inst_kbc->HICTRL = BIT(NPCX_HICTRL_IBFCIE) | BIT(NPCX_HICTRL_OBFMIE) |
| | BIT(NPCX_HICTRL_OBFKIE); |
| |
| /* Configure SIRQ 1/12 type (level + high) */ |
| inst_kbc->HIIRQC = 0x00; |
| } |
| #endif |
| |
| /* Host ACPI sub-device local functions */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO) |
| static void host_acpi_process_input_data(uint8_t data) |
| { |
| struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi; |
| struct espi_event evt = { |
| .evt_type = ESPI_BUS_PERIPHERAL_NOTIFICATION, |
| .evt_details = ESPI_PERIPHERAL_HOST_IO, |
| .evt_data = ESPI_PERIPHERAL_NODATA |
| }; |
| struct espi_evt_data_acpi *acpi_evt = |
| (struct espi_evt_data_acpi *)&evt.evt_data; |
| |
| LOG_DBG("%s: acpi data 0x%02x", __func__, data); |
| |
| /* |
| * Indicates if the host sent a command or data. |
| * 0 = data |
| * 1 = Command. |
| */ |
| acpi_evt->type = IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_CMD); |
| acpi_evt->data = data; |
| |
| espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, |
| evt); |
| } |
| |
| static void host_acpi_init(void) |
| { |
| struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi; |
| |
| /* Use SMI/SCI postive polarity by default */ |
| inst_acpi->HIPMCTL &= ~BIT(NPCX_HIPMCTL_SCIPOL); |
| inst_acpi->HIPMIC &= ~BIT(NPCX_HIPMIC_SMIPOL); |
| |
| /* Set SMIB/SCIB bits to make sure SMI#/SCI# are driven to high */ |
| inst_acpi->HIPMIC |= BIT(NPCX_HIPMIC_SMIB) | BIT(NPCX_HIPMIC_SCIB); |
| |
| /* |
| * Allow SMI#/SCI# generated from PM module. |
| * On eSPI bus, we suggest set VW value of SCI#/SMI# directly. |
| */ |
| inst_acpi->HIPMIE |= BIT(NPCX_HIPMIE_SCIE); |
| inst_acpi->HIPMIE |= BIT(NPCX_HIPMIE_SMIE); |
| |
| /* |
| * Init ACPI PM channel (Host IO) with: |
| * 1. Enable Input-Buffer Full (IBF) core interrupt. |
| * 2. BIT 7 must be 1. |
| */ |
| inst_acpi->HIPMCTL |= BIT(7) | BIT(NPCX_HIPMCTL_IBFIE); |
| } |
| #endif |
| |
| #if defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD) |
| /* Host command argument and memory map buffers */ |
| static uint8_t shm_host_cmd[CONFIG_ESPI_NPCX_PERIPHERAL_HOST_CMD_PARAM_SIZE] |
| __aligned(8); |
| /* Host command sub-device local functions */ |
| static void host_hcmd_process_input_data(uint8_t data) |
| { |
| struct espi_event evt = { ESPI_BUS_PERIPHERAL_NOTIFICATION, |
| ESPI_PERIPHERAL_EC_HOST_CMD, ESPI_PERIPHERAL_NODATA |
| }; |
| |
| evt.evt_data = data; |
| LOG_DBG("%s: host cmd data 0x%02x", __func__, evt.evt_data); |
| espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, |
| evt); |
| } |
| |
| static void host_hcmd_init(void) |
| { |
| struct pmch_reg *const inst_hcmd = host_sub_cfg.inst_pm_hcmd; |
| struct shm_reg *const inst_shm = host_sub_cfg.inst_shm; |
| uint32_t win_size = CONFIG_ESPI_NPCX_PERIPHERAL_HOST_CMD_PARAM_SIZE; |
| |
| /* Don't stall SHM transactions */ |
| inst_shm->SHM_CTL &= ~0x40; |
| /* Disable Window 1 protection */ |
| inst_shm->WIN1_WR_PROT = 0; |
| inst_shm->WIN1_RD_PROT = 0; |
| |
| /* Configure Win1 size for ec host command. */ |
| SET_FIELD(inst_shm->WIN_SIZE, NPCX_WIN_SIZE_RWIN1_SIZE_FIELD, |
| host_shd_mem_wnd_size_sl(win_size)); |
| inst_shm->WIN_BASE1 = (uint32_t)shm_host_cmd; |
| |
| /* |
| * Clear processing flag before enabling host's interrupts in case |
| * it's set by the other command during sysjump. |
| */ |
| inst_hcmd->HIPMST &= ~BIT(NPCX_HIPMST_F0); |
| |
| /* |
| * Init Host Command PM channel (Host IO) with: |
| * 1. Enable Input-Buffer Full (IBF) core interrupt. |
| * 2. BIT 7 must be 1. |
| */ |
| inst_hcmd->HIPMCTL |= BIT(7) | BIT(NPCX_HIPMCTL_IBFIE); |
| } |
| #endif |
| |
| #if defined(CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION) |
| /* Host command argument and memory map buffers */ |
| static uint8_t shm_acpi_mmap[CONFIG_ESPI_NPCX_PERIPHERAL_ACPI_SHD_MEM_SIZE] |
| __aligned(8); |
| static void host_shared_mem_region_init(void) |
| { |
| struct shm_reg *const inst_shm = host_sub_cfg.inst_shm; |
| uint32_t win_size = CONFIG_ESPI_NPCX_PERIPHERAL_ACPI_SHD_MEM_SIZE; |
| |
| /* Don't stall SHM transactions */ |
| inst_shm->SHM_CTL &= ~0x40; |
| /* Disable Window 2 protection */ |
| inst_shm->WIN2_WR_PROT = 0; |
| inst_shm->WIN2_RD_PROT = 0; |
| |
| /* Configure Win2 size for ACPI shared mem region. */ |
| SET_FIELD(inst_shm->WIN_SIZE, NPCX_WIN_SIZE_RWIN2_SIZE_FIELD, |
| host_shd_mem_wnd_size_sl(win_size)); |
| inst_shm->WIN_BASE2 = (uint32_t)shm_acpi_mmap; |
| /* Enable write protect of Share memory window 2 */ |
| inst_shm->WIN2_WR_PROT = 0xFF; |
| |
| /* |
| * TODO: Initialize shm_acpi_mmap buffer for host command flags. We |
| * might use EACPI_GET_SHARED_MEMORY in espi_api_lpc_read_request() |
| * instead of setting host command flags here directly. |
| */ |
| } |
| #endif |
| |
| #if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO) || \ |
| defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD) |
| /* Host pm (host io) sub-module isr function for all channels such as ACPI. */ |
| static void host_pmch_ibf_isr(const void *arg) |
| { |
| ARG_UNUSED(arg); |
| struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi; |
| struct pmch_reg *const inst_hcmd = host_sub_cfg.inst_pm_hcmd; |
| uint8_t in_data; |
| |
| /* Host put data on input buffer of ACPI channel */ |
| if (IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_IBF)) { |
| /* Set processing flag before reading command byte */ |
| inst_acpi->HIPMST |= BIT(NPCX_HIPMST_F0); |
| /* Read out input data and clear IBF pending bit */ |
| in_data = inst_acpi->HIPMDI; |
| #if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO) |
| host_acpi_process_input_data(in_data); |
| #endif |
| } |
| |
| /* Host put data on input buffer of HOSTCMD channel */ |
| if (IS_BIT_SET(inst_hcmd->HIPMST, NPCX_HIPMST_IBF)) { |
| /* Set processing flag before reading command byte */ |
| inst_hcmd->HIPMST |= BIT(NPCX_HIPMST_F0); |
| /* Read out input data and clear IBF pending bit */ |
| in_data = inst_hcmd->HIPMDI; |
| #if defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD) |
| host_hcmd_process_input_data(in_data); |
| #endif |
| } |
| } |
| #endif |
| |
| /* Host port80 sub-device local functions */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80) |
| #if defined(CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE) |
| static void host_port80_work_handler(struct k_work *item) |
| { |
| uint32_t code = 0; |
| struct host_sub_npcx_data *data = CONTAINER_OF(item, struct host_sub_npcx_data, work); |
| struct ring_buf *rbuf = &data->port80_ring_buf; |
| struct espi_event evt = {ESPI_BUS_PERIPHERAL_NOTIFICATION, |
| (ESPI_PERIPHERAL_INDEX_0 << 16) | ESPI_PERIPHERAL_DEBUG_PORT80, |
| ESPI_PERIPHERAL_NODATA}; |
| |
| while (!ring_buf_is_empty(rbuf)) { |
| struct npcx_dp80_buf dp80_buf; |
| uint8_t offset; |
| |
| ring_buf_get(rbuf, &dp80_buf.offset_code[0], sizeof(dp80_buf.offset_code)); |
| offset = dp80_buf.offset_code[1]; |
| code |= dp80_buf.offset_code[0] << (8 * offset); |
| if (ring_buf_is_empty(rbuf)) { |
| evt.evt_data = code; |
| espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, |
| evt); |
| break; |
| } |
| /* peek the offset of the next byte */ |
| ring_buf_peek(rbuf, &dp80_buf.offset_code[0], sizeof(dp80_buf.offset_code)); |
| offset = dp80_buf.offset_code[1]; |
| /* |
| * If the peeked next byte's offset is 0, it is the start of the new code. |
| * Pass the current code to the application layer to handle the Port80 code. |
| */ |
| if (offset == 0) { |
| evt.evt_data = code; |
| espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, |
| evt); |
| code = 0; |
| } |
| } |
| } |
| #endif |
| |
| static void host_port80_isr(const void *arg) |
| { |
| ARG_UNUSED(arg); |
| struct shm_reg *const inst_shm = host_sub_cfg.inst_shm; |
| uint8_t status = inst_shm->DP80STS; |
| |
| #ifdef CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE |
| struct ring_buf *rbuf = &host_sub_data.port80_ring_buf; |
| |
| while (IS_BIT_SET(inst_shm->DP80STS, NPCX_DP80STS_FNE)) { |
| struct npcx_dp80_buf dp80_buf; |
| |
| dp80_buf.offset_code_16 = inst_shm->DP80BUF; |
| ring_buf_put(rbuf, &dp80_buf.offset_code[0], sizeof(dp80_buf.offset_code)); |
| } |
| k_work_submit(&host_sub_data.work); |
| #else |
| struct espi_event evt = {ESPI_BUS_PERIPHERAL_NOTIFICATION, |
| (ESPI_PERIPHERAL_INDEX_0 << 16) | ESPI_PERIPHERAL_DEBUG_PORT80, |
| ESPI_PERIPHERAL_NODATA}; |
| |
| /* Read out port80 data continuously if FIFO is not empty */ |
| while (IS_BIT_SET(inst_shm->DP80STS, NPCX_DP80STS_FNE)) { |
| LOG_DBG("p80: %04x", inst_shm->DP80BUF); |
| evt.evt_data = inst_shm->DP80BUF; |
| espi_send_callbacks(host_sub_data.callbacks, host_sub_data.host_bus_dev, evt); |
| } |
| #endif |
| LOG_DBG("%s: p80 status 0x%02X", __func__, status); |
| |
| /* If FIFO is overflow, show error msg */ |
| if (IS_BIT_SET(status, NPCX_DP80STS_FOR)) { |
| inst_shm->DP80STS |= BIT(NPCX_DP80STS_FOR); |
| LOG_DBG("Port80 FIFO Overflow!"); |
| } |
| |
| /* If there are pending post codes remains in FIFO after processing and sending previous |
| * post codes, do not clear the FNE bit. This allows this handler to be called again |
| * immediately after it exists. |
| */ |
| if (!IS_BIT_SET(inst_shm->DP80STS, NPCX_DP80STS_FNE)) { |
| /* Clear all pending bit indicates that FIFO was written by host */ |
| inst_shm->DP80STS |= BIT(NPCX_DP80STS_FWR); |
| } |
| } |
| |
| static void host_port80_init(void) |
| { |
| struct shm_reg *const inst_shm = host_sub_cfg.inst_shm; |
| |
| /* |
| * Init PORT80 which includes: |
| * Enables a Core interrupt on every Host write to the FIFO, |
| * SYNC mode (It must be 1 in eSPI mode), Read Auto Advance mode, and |
| * Port80 module itself. |
| */ |
| inst_shm->DP80CTL = BIT(NPCX_DP80CTL_CIEN) | BIT(NPCX_DP80CTL_RAA) |
| | BIT(NPCX_DP80CTL_DP80EN) | BIT(NPCX_DP80CTL_SYNCEN); |
| } |
| #endif |
| |
| #if defined(CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE) |
| static void host_cus_opcode_enable_interrupts(void) |
| { |
| /* Enable host KBC sub-device interrupt */ |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_8042_KBC)) { |
| irq_enable(DT_INST_IRQ_BY_NAME(0, kbc_ibf, irq)); |
| irq_enable(DT_INST_IRQ_BY_NAME(0, kbc_obe, irq)); |
| } |
| |
| /* Enable host PM channel (Host IO) sub-device interrupt */ |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_HOST_IO) || |
| IS_ENABLED(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD)) { |
| irq_enable(DT_INST_IRQ_BY_NAME(0, pmch_ibf, irq)); |
| } |
| |
| /* Enable host Port80 sub-device interrupt installation */ |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80)) { |
| irq_enable(DT_INST_IRQ_BY_NAME(0, p80_fifo, irq)); |
| } |
| |
| /* Enable host interface interrupts if its interface is eSPI */ |
| if (IS_ENABLED(CONFIG_ESPI)) { |
| npcx_espi_enable_interrupts(host_sub_data.host_bus_dev); |
| } |
| } |
| |
| static void host_cus_opcode_disable_interrupts(void) |
| { |
| /* Disable host KBC sub-device interrupt */ |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_8042_KBC)) { |
| irq_disable(DT_INST_IRQ_BY_NAME(0, kbc_ibf, irq)); |
| irq_disable(DT_INST_IRQ_BY_NAME(0, kbc_obe, irq)); |
| } |
| |
| /* Disable host PM channel (Host IO) sub-device interrupt */ |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_HOST_IO) || |
| IS_ENABLED(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD)) { |
| irq_disable(DT_INST_IRQ_BY_NAME(0, pmch_ibf, irq)); |
| } |
| |
| /* Disable host Port80 sub-device interrupt installation */ |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80)) { |
| irq_disable(DT_INST_IRQ_BY_NAME(0, p80_fifo, irq)); |
| } |
| |
| /* Disable host interface interrupts if its interface is eSPI */ |
| if (IS_ENABLED(CONFIG_ESPI)) { |
| npcx_espi_disable_interrupts(host_sub_data.host_bus_dev); |
| } |
| } |
| #endif /* CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE */ |
| |
| #if defined(CONFIG_ESPI_PERIPHERAL_UART) |
| /* host uart pinmux configuration */ |
| PINCTRL_DT_DEFINE(DT_INST(0, nuvoton_npcx_host_uart)); |
| BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(nuvoton_npcx_host_uart) == 1, |
| "only one 'nuvoton_npcx_host_uart' compatible node may be present"); |
| const struct pinctrl_dev_config *huart_cfg = |
| PINCTRL_DT_DEV_CONFIG_GET(DT_INST(0, nuvoton_npcx_host_uart)); |
| /* Host UART sub-device local functions */ |
| void host_uart_init(void) |
| { |
| struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h; |
| |
| /* Configure pin-mux for serial port device */ |
| pinctrl_apply_state(huart_cfg, PINCTRL_STATE_DEFAULT); |
| |
| /* Make sure unlock host access of serial port */ |
| inst_c2h->LKSIOHA &= ~BIT(NPCX_LKSIOHA_LKSPHA); |
| /* Clear 'Host lock violation occurred' bit of serial port initially */ |
| inst_c2h->SIOLV |= BIT(NPCX_SIOLV_SPLV); |
| } |
| #endif |
| |
| /* host core-to-host interface local functions */ |
| static void host_c2h_wait_write_done(void) |
| { |
| struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h; |
| uint32_t elapsed_cycles; |
| uint32_t start_cycles = k_cycle_get_32(); |
| uint32_t max_wait_cycles = |
| k_us_to_cyc_ceil32(NPCX_C2H_TRANSACTION_TIMEOUT_US); |
| |
| while (IS_BIT_SET(inst_c2h->SIBCTRL, NPCX_SIBCTRL_CSWR)) { |
| elapsed_cycles = k_cycle_get_32() - start_cycles; |
| if (elapsed_cycles > max_wait_cycles) { |
| LOG_ERR("c2h write transaction expired!"); |
| break; |
| } |
| } |
| } |
| |
| static void host_c2h_wait_read_done(void) |
| { |
| struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h; |
| uint32_t elapsed_cycles; |
| uint32_t start_cycles = k_cycle_get_32(); |
| uint32_t max_wait_cycles = |
| k_us_to_cyc_ceil32(NPCX_C2H_TRANSACTION_TIMEOUT_US); |
| |
| while (IS_BIT_SET(inst_c2h->SIBCTRL, NPCX_SIBCTRL_CSRD)) { |
| elapsed_cycles = k_cycle_get_32() - start_cycles; |
| if (elapsed_cycles > max_wait_cycles) { |
| LOG_ERR("c2h read transaction expired!"); |
| break; |
| } |
| } |
| } |
| |
| void host_c2h_write_io_cfg_reg(uint8_t reg_index, uint8_t reg_data) |
| { |
| struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h; |
| |
| /* Disable interrupts */ |
| unsigned int key = irq_lock(); |
| |
| /* Lock host access EC configuration registers (0x4E/0x4F) */ |
| inst_c2h->LKSIOHA |= BIT(NPCX_LKSIOHA_LKCFG); |
| /* Enable Core-to-Host access CFG module */ |
| inst_c2h->CRSMAE |= BIT(NPCX_CRSMAE_CFGAE); |
| |
| /* Verify core-to-host modules is not in progress */ |
| host_c2h_wait_read_done(); |
| host_c2h_wait_write_done(); |
| |
| /* |
| * Specifying the in-direct IO address which A0 = 0 indicates the index |
| * register is accessed. Then write index address directly and it starts |
| * a write transaction to host sub-module on LPC/eSPI bus. |
| */ |
| inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR; |
| inst_c2h->IHD = reg_index; |
| host_c2h_wait_write_done(); |
| |
| /* |
| * Specifying the in-direct IO address which A0 = 1 indicates the data |
| * register is accessed. Then write data directly and it starts a write |
| * transaction to host sub-module on LPC/eSPI bus. |
| */ |
| inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR + 1; |
| inst_c2h->IHD = reg_data; |
| host_c2h_wait_write_done(); |
| |
| /* Disable Core-to-Host access CFG module */ |
| inst_c2h->CRSMAE &= ~BIT(NPCX_CRSMAE_CFGAE); |
| /* Unlock host access EC configuration registers (0x4E/0x4F) */ |
| inst_c2h->LKSIOHA &= ~BIT(NPCX_LKSIOHA_LKCFG); |
| |
| /* Enable interrupts */ |
| irq_unlock(key); |
| } |
| |
| uint8_t host_c2h_read_io_cfg_reg(uint8_t reg_index) |
| { |
| struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h; |
| uint8_t data_val; |
| |
| /* Disable interrupts */ |
| unsigned int key = irq_lock(); |
| |
| /* Lock host access EC configuration registers (0x4E/0x4F) */ |
| inst_c2h->LKSIOHA |= BIT(NPCX_LKSIOHA_LKCFG); |
| /* Enable Core-to-Host access CFG module */ |
| inst_c2h->CRSMAE |= BIT(NPCX_CRSMAE_CFGAE); |
| |
| /* Verify core-to-host modules is not in progress */ |
| host_c2h_wait_read_done(); |
| host_c2h_wait_write_done(); |
| |
| /* |
| * Specifying the in-direct IO address which A0 = 0 indicates the index |
| * register is accessed. Then write index address directly and it starts |
| * a write transaction to host sub-module on LPC/eSPI bus. |
| */ |
| inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR; |
| inst_c2h->IHD = reg_index; |
| host_c2h_wait_write_done(); |
| |
| /* |
| * Specifying the in-direct IO address which A0 = 1 indicates the data |
| * register is accessed. Then write CSRD bit in SIBCTRL to issue a read |
| * transaction to host sub-module on LPC/eSPI bus. Once it was done, |
| * read data out from IHD. |
| */ |
| inst_c2h->IHIOA = NPCX_EC_CFG_IO_ADDR + 1; |
| inst_c2h->SIBCTRL |= BIT(NPCX_SIBCTRL_CSRD); |
| host_c2h_wait_read_done(); |
| data_val = inst_c2h->IHD; |
| |
| /* Disable Core-to-Host access CFG module */ |
| inst_c2h->CRSMAE &= ~BIT(NPCX_CRSMAE_CFGAE); |
| /* Unlock host access EC configuration registers (0x4E/0x4F) */ |
| inst_c2h->LKSIOHA &= ~BIT(NPCX_LKSIOHA_LKCFG); |
| |
| /* Enable interrupts */ |
| irq_unlock(key); |
| |
| return data_val; |
| } |
| |
| /* Platform specific host sub modules functions */ |
| int npcx_host_periph_read_request(enum lpc_peripheral_opcode op, |
| uint32_t *data) |
| { |
| if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) { |
| struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc; |
| |
| /* Make sure kbc 8042 is on */ |
| if (!IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFKIE) || |
| !IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFMIE)) { |
| return -ENOTSUP; |
| } |
| |
| switch (op) { |
| case E8042_OBF_HAS_CHAR: |
| /* EC has written data back to host. OBF is |
| * automatically cleared after host reads |
| * the data |
| */ |
| *data = IS_BIT_SET(inst_kbc->HIKMST, NPCX_HIKMST_OBF); |
| break; |
| case E8042_IBF_HAS_CHAR: |
| *data = IS_BIT_SET(inst_kbc->HIKMST, NPCX_HIKMST_IBF); |
| break; |
| case E8042_READ_KB_STS: |
| *data = inst_kbc->HIKMST; |
| break; |
| default: |
| return -EINVAL; |
| } |
| } else if (op >= EACPI_START_OPCODE && op <= EACPI_MAX_OPCODE) { |
| struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi; |
| |
| /* Make sure pm channel for apci is on */ |
| if (!IS_BIT_SET(inst_acpi->HIPMCTL, NPCX_HIPMCTL_IBFIE)) { |
| return -ENOTSUP; |
| } |
| |
| switch (op) { |
| case EACPI_OBF_HAS_CHAR: |
| /* EC has written data back to host. OBF is |
| * automatically cleared after host reads |
| * the data |
| */ |
| *data = IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_OBF); |
| break; |
| case EACPI_IBF_HAS_CHAR: |
| *data = IS_BIT_SET(inst_acpi->HIPMST, NPCX_HIPMST_IBF); |
| break; |
| case EACPI_READ_STS: |
| *data = inst_acpi->HIPMST; |
| break; |
| #if defined(CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION) |
| case EACPI_GET_SHARED_MEMORY: |
| *data = (uint32_t)shm_acpi_mmap; |
| break; |
| #endif /* CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION */ |
| default: |
| return -EINVAL; |
| } |
| } |
| #if defined(CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE) |
| else if (op >= ECUSTOM_START_OPCODE && op <= ECUSTOM_MAX_OPCODE) { |
| /* Other customized op codes */ |
| switch (op) { |
| case ECUSTOM_HOST_CMD_GET_PARAM_MEMORY: |
| *data = (uint32_t)shm_host_cmd; |
| break; |
| case ECUSTOM_HOST_CMD_GET_PARAM_MEMORY_SIZE: |
| *data = CONFIG_ESPI_NPCX_PERIPHERAL_HOST_CMD_PARAM_SIZE; |
| break; |
| default: |
| return -EINVAL; |
| } |
| } |
| #endif /* CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE */ |
| else { |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| int npcx_host_periph_write_request(enum lpc_peripheral_opcode op, |
| const uint32_t *data) |
| { |
| volatile uint32_t __attribute__((unused)) dummy; |
| struct kbc_reg *const inst_kbc = host_sub_cfg.inst_kbc; |
| |
| if (op >= E8042_START_OPCODE && op <= E8042_MAX_OPCODE) { |
| /* Make sure kbc 8042 is on */ |
| if (!IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFKIE) || |
| !IS_BIT_SET(inst_kbc->HICTRL, NPCX_HICTRL_OBFMIE)) { |
| return -ENOTSUP; |
| } |
| if (data) { |
| LOG_INF("%s: op 0x%x data %x", __func__, op, *data); |
| } else { |
| LOG_INF("%s: op 0x%x only", __func__, op); |
| } |
| |
| switch (op) { |
| case E8042_WRITE_KB_CHAR: |
| inst_kbc->HIKDO = *data & 0xff; |
| /* |
| * Enable KBC OBE interrupt after putting data in |
| * keyboard data register. |
| */ |
| inst_kbc->HICTRL |= BIT(NPCX_HICTRL_OBECIE); |
| break; |
| case E8042_WRITE_MB_CHAR: |
| inst_kbc->HIMDO = *data & 0xff; |
| /* |
| * Enable KBC OBE interrupt after putting data in |
| * mouse data register. |
| */ |
| inst_kbc->HICTRL |= BIT(NPCX_HICTRL_OBECIE); |
| break; |
| case E8042_RESUME_IRQ: |
| /* Enable KBC IBF interrupt */ |
| inst_kbc->HICTRL |= BIT(NPCX_HICTRL_IBFCIE); |
| break; |
| case E8042_PAUSE_IRQ: |
| /* Disable KBC IBF interrupt */ |
| inst_kbc->HICTRL &= ~BIT(NPCX_HICTRL_IBFCIE); |
| break; |
| case E8042_CLEAR_OBF: |
| /* Clear OBF flag in both STATUS and HIKMST to 0 */ |
| inst_kbc->HICTRL |= BIT(NPCX_HICTRL_FW_OBF); |
| break; |
| case E8042_SET_FLAG: |
| /* FW shouldn't modify these flags directly */ |
| inst_kbc->HIKMST |= *data & ~NPCX_KBC_STS_MASK; |
| break; |
| case E8042_CLEAR_FLAG: |
| /* FW shouldn't modify these flags directly */ |
| inst_kbc->HIKMST &= ~(*data | NPCX_KBC_STS_MASK); |
| break; |
| default: |
| return -EINVAL; |
| } |
| } else if (op >= EACPI_START_OPCODE && op <= EACPI_MAX_OPCODE) { |
| struct pmch_reg *const inst_acpi = host_sub_cfg.inst_pm_acpi; |
| |
| /* Make sure pm channel for apci is on */ |
| if (!IS_BIT_SET(inst_acpi->HIPMCTL, NPCX_HIPMCTL_IBFIE)) { |
| return -ENOTSUP; |
| } |
| |
| switch (op) { |
| case EACPI_WRITE_CHAR: |
| inst_acpi->HIPMDO = (*data & 0xff); |
| break; |
| case EACPI_WRITE_STS: |
| inst_acpi->HIPMST = (*data & 0xff); |
| break; |
| default: |
| return -EINVAL; |
| } |
| } |
| #if defined(CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE) |
| else if (op >= ECUSTOM_START_OPCODE && op <= ECUSTOM_MAX_OPCODE) { |
| /* Other customized op codes */ |
| struct pmch_reg *const inst_hcmd = host_sub_cfg.inst_pm_hcmd; |
| |
| switch (op) { |
| case ECUSTOM_HOST_SUBS_INTERRUPT_EN: |
| if (*data != 0) { |
| host_cus_opcode_enable_interrupts(); |
| } else { |
| host_cus_opcode_disable_interrupts(); |
| } |
| break; |
| case ECUSTOM_HOST_CMD_SEND_RESULT: |
| /* |
| * Write result to the data byte. This sets the TOH |
| * status bit. |
| */ |
| inst_hcmd->HIPMDO = (*data & 0xff); |
| /* Clear processing flag */ |
| inst_hcmd->HIPMST &= ~BIT(NPCX_HIPMST_F0); |
| break; |
| default: |
| return -EINVAL; |
| } |
| } |
| #endif /* CONFIG_ESPI_PERIPHERAL_CUSTOM_OPCODE */ |
| else { |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| void npcx_host_init_subs_host_domain(void) |
| { |
| struct c2h_reg *const inst_c2h = host_sub_cfg.inst_c2h; |
| |
| /* Enable Core-to-Host access module */ |
| inst_c2h->SIBCTRL |= BIT(NPCX_SIBCTRL_CSAE); |
| |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_8042_KBC)) { |
| /* |
| * Select Keyboard/Mouse banks which LDN are 0x06/05 and enable |
| * modules by setting bit 0 in its Control (index is 0x30) reg. |
| */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_LDN, EC_CFG_LDN_KBC); |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_CTRL, 0x01); |
| |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_LDN, EC_CFG_LDN_MOUSE); |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_CTRL, 0x01); |
| } |
| |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_HOST_IO)) { |
| /* |
| * Select ACPI bank which LDN are 0x11 (PM Channel 1) and enable |
| * module by setting bit 0 in its Control (index is 0x30) reg. |
| */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_LDN, EC_CFG_LDN_ACPI); |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_CTRL, 0x01); |
| } |
| |
| if (IS_ENABLED(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD) || |
| IS_ENABLED(CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION)) { |
| /* Select 'Host Command' bank which LDN are 0x12 (PM chan 2) */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_LDN, EC_CFG_LDN_HCMD); |
| #if defined(CONFIG_ESPI_PERIPHERAL_HOST_CMD_DATA_PORT_NUM) |
| /* Configure IO address of CMD portt (default: 0x200) */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_CMD_IO_ADDR_H, |
| (CONFIG_ESPI_PERIPHERAL_HOST_CMD_DATA_PORT_NUM >> 8) & 0xff); |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_CMD_IO_ADDR_L, |
| CONFIG_ESPI_PERIPHERAL_HOST_CMD_DATA_PORT_NUM & 0xff); |
| /* Configure IO address of Data portt (default: 0x204) */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_DATA_IO_ADDR_H, |
| ((CONFIG_ESPI_PERIPHERAL_HOST_CMD_DATA_PORT_NUM + 4) >> 8) |
| & 0xff); |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_DATA_IO_ADDR_L, |
| (CONFIG_ESPI_PERIPHERAL_HOST_CMD_DATA_PORT_NUM + 4) & 0xff); |
| #endif |
| /* Enable 'Host Command' io port (PM Channel 2) */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_CTRL, 0x01); |
| |
| /* Select 'Shared Memory' bank which LDN are 0x0F */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_LDN, EC_CFG_LDN_SHM); |
| /* WIN 1 & 2 mapping to IO space */ |
| host_c2h_write_io_cfg_reg(0xF1, |
| host_c2h_read_io_cfg_reg(0xF1) | 0x30); |
| /* WIN1 as Host Command on the IO address (default: 0x0800) */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_HOST_CMD_PARAM_PORT_NUM) |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_SHM_WND1_ADDR_1, |
| (CONFIG_ESPI_PERIPHERAL_HOST_CMD_PARAM_PORT_NUM >> 8) & 0xff); |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_SHM_WND1_ADDR_0, |
| CONFIG_ESPI_PERIPHERAL_HOST_CMD_PARAM_PORT_NUM & 0xff); |
| #endif |
| /* Set WIN2 as MEMMAP on the configured IO address */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION_PORT_NUM) |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_SHM_WND2_ADDR_1, |
| (CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION_PORT_NUM >> 8) & 0xff); |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_SHM_WND2_ADDR_0, |
| CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION_PORT_NUM & 0xff); |
| #endif |
| if (IS_ENABLED(CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE)) { |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_SHM_DP80_ADDR_RANGE, 0x0f); |
| } |
| /* Enable SHM direct memory access */ |
| host_c2h_write_io_cfg_reg(EC_CFG_IDX_CTRL, 0x01); |
| } |
| LOG_DBG("Hos sub-modules configurations are done!"); |
| } |
| |
| void npcx_host_enable_access_interrupt(void) |
| { |
| npcx_miwu_irq_get_and_clear_pending(&host_sub_cfg.host_acc_wui); |
| |
| npcx_miwu_irq_enable(&host_sub_cfg.host_acc_wui); |
| } |
| |
| void npcx_host_disable_access_interrupt(void) |
| { |
| npcx_miwu_irq_disable(&host_sub_cfg.host_acc_wui); |
| } |
| |
| int npcx_host_init_subs_core_domain(const struct device *host_bus_dev, |
| sys_slist_t *callbacks) |
| { |
| struct mswc_reg *const inst_mswc = host_sub_cfg.inst_mswc; |
| struct shm_reg *const inst_shm = host_sub_cfg.inst_shm; |
| const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE); |
| int i; |
| uint8_t shm_sts; |
| |
| host_sub_data.callbacks = callbacks; |
| host_sub_data.host_bus_dev = host_bus_dev; |
| |
| /* Turn on all host necessary sub-module clocks first */ |
| for (i = 0; i < host_sub_cfg.clks_size; i++) { |
| int ret; |
| |
| if (!device_is_ready(clk_dev)) { |
| return -ENODEV; |
| } |
| |
| ret = clock_control_on(clk_dev, (clock_control_subsys_t) |
| &host_sub_cfg.clks_list[i]); |
| if (ret < 0) { |
| return ret; |
| } |
| } |
| |
| /* Configure EC legacy configuration IO base address to 0x4E. */ |
| if (!IS_BIT_SET(inst_mswc->MSWCTL1, NPCX_MSWCTL1_VHCFGA)) { |
| inst_mswc->HCBAL = NPCX_EC_CFG_IO_ADDR; |
| inst_mswc->HCBAH = 0x0; |
| } |
| |
| /* |
| * Set HOSTWAIT bit and avoid the other settings, then host can freely |
| * communicate with slave (EC). |
| */ |
| inst_shm->SMC_CTL &= BIT(NPCX_SMC_CTL_HOSTWAIT); |
| /* Clear shared memory status */ |
| shm_sts = inst_shm->SMC_STS; |
| inst_shm->SMC_STS = shm_sts; |
| |
| /* host sub-module initialization in core domain */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_8042_KBC) |
| host_kbc_init(); |
| #endif |
| #if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO) |
| host_acpi_init(); |
| #endif |
| #if defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD) |
| host_hcmd_init(); |
| #endif |
| #if defined(CONFIG_ESPI_PERIPHERAL_ACPI_SHM_REGION) |
| host_shared_mem_region_init(); |
| #endif |
| #if defined(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80) |
| host_port80_init(); |
| #if defined(CONFIG_ESPI_NPCX_PERIPHERAL_DEBUG_PORT_80_MULTI_BYTE) |
| ring_buf_init(&host_sub_data.port80_ring_buf, sizeof(host_sub_data.port80_data), |
| host_sub_data.port80_data); |
| k_work_init(&host_sub_data.work, host_port80_work_handler); |
| #endif |
| #endif |
| #if defined(CONFIG_ESPI_PERIPHERAL_UART) |
| host_uart_init(); |
| #endif |
| |
| /* Host KBC sub-device interrupt installation */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_8042_KBC) |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, kbc_ibf, irq), |
| DT_INST_IRQ_BY_NAME(0, kbc_ibf, priority), |
| host_kbc_ibf_isr, |
| NULL, 0); |
| |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, kbc_obe, irq), |
| DT_INST_IRQ_BY_NAME(0, kbc_obe, priority), |
| host_kbc_obe_isr, |
| NULL, 0); |
| #endif |
| |
| /* Host PM channel (Host IO) sub-device interrupt installation */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_HOST_IO) || \ |
| defined(CONFIG_ESPI_PERIPHERAL_EC_HOST_CMD) |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, pmch_ibf, irq), |
| DT_INST_IRQ_BY_NAME(0, pmch_ibf, priority), |
| host_pmch_ibf_isr, |
| NULL, 0); |
| #endif |
| |
| /* Host Port80 sub-device interrupt installation */ |
| #if defined(CONFIG_ESPI_PERIPHERAL_DEBUG_PORT_80) |
| IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, p80_fifo, irq), |
| DT_INST_IRQ_BY_NAME(0, p80_fifo, priority), |
| host_port80_isr, |
| NULL, 0); |
| #endif |
| |
| if (IS_ENABLED(CONFIG_PM)) { |
| /* |
| * Configure the host access wake-up event triggered from a host |
| * transaction on eSPI/LPC bus. Do not enable it here. Or plenty |
| * of interrupts will jam the system in S0. |
| */ |
| npcx_miwu_interrupt_configure(&host_sub_cfg.host_acc_wui, |
| NPCX_MIWU_MODE_EDGE, NPCX_MIWU_TRIG_HIGH); |
| } |
| |
| return 0; |
| } |