diff --git a/dts/arm/nuvoton/npcx9m7fb.dtsi b/dts/arm/nuvoton/npcx9m7fb.dtsi index bfc43fd03dd..7c17ff09934 100644 --- a/dts/arm/nuvoton/npcx9m7fb.dtsi +++ b/dts/arm/nuvoton/npcx9m7fb.dtsi @@ -40,3 +40,11 @@ pinctrl-names = "default"; }; }; + +/* + * Raising the interrupt priority of the MIWU group, which owns SHI CS, to the same as + * SHI's priority. + */ +&group_f1 { + irq-prio = <1>; +}; diff --git a/subsys/mgmt/ec_host_cmd/backends/Kconfig b/subsys/mgmt/ec_host_cmd/backends/Kconfig index f398786e369..56b59ebdbde 100644 --- a/subsys/mgmt/ec_host_cmd/backends/Kconfig +++ b/subsys/mgmt/ec_host_cmd/backends/Kconfig @@ -75,6 +75,13 @@ config EC_HOST_CMD_BACKEND_SHI_NPCX_ENHANCED_BUF_MODE single-byte output buffer can be selected/switched to generate a response to simultaneous Read/Write transactions. +config EC_HOST_CMD_BACKEND_SHI_NPCX_CS_DETECT_WORKAROUND + bool + default y if SOC_NPCX9M7FB + help + Workaround the issue "CSnFE and CSnRE bits of EVSTATS2 Register (SHI)" + in the npcx9m7fb SoC errata. + config EC_HOST_CMD_BACKEND_SHI_MAX_REQUEST int "Max data size for the version 3 request packet" default 544 if EC_HOST_CMD_BACKEND_SHI_NPCX diff --git a/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c b/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c index e30de1295f9..df79c819c5d 100644 --- a/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c +++ b/subsys/mgmt/ec_host_cmd/backends/ec_host_cmd_backend_shi_npcx.c @@ -139,6 +139,17 @@ struct shi_npcx_data { uint8_t *const out_msg; uint8_t in_msg[CONFIG_EC_HOST_CMD_BACKEND_SHI_MAX_REQUEST] __aligned(4); ATOMIC_DEFINE(pm_policy_state_flag, SHI_NPCX_PM_POLICY_FLAG_COUNT); +#ifdef CONFIG_EC_HOST_CMD_BACKEND_SHI_NPCX_CS_DETECT_WORKAROUND + struct miwu_callback shi_cs_wui_cb; + /* + * With the workaround, CS assertion/de-assertion INT and SHI module's INT come from + * different sources. CS failing IRQ and IBHF2 IRQ may happen at the same time. + * In this case, IBHF2 ISR is called first because it has lower INT number. + * (with the same priority). This flag is used to guarantee CS assertion ISR is executed + * first. + */ + bool is_entered_cs_asserted_wui_isr; +#endif }; struct ec_host_cmd_shi_npcx_ctx { @@ -605,7 +616,7 @@ static void shi_npcx_handle_cs_deassert(const struct device *dev) /* Error state for checking*/ if (data->state != SHI_STATE_SENDING) { - shi_npcx_log_unexpected_state(dev, "CSNRE"); + shi_npcx_log_unexpected_state(dev, "CS DE-AST"); } /* reset SHI and prepare to next transaction again */ @@ -670,11 +681,20 @@ static void shi_npcx_isr(const struct device *dev) uint8_t stat; uint8_t stat2; +#ifdef CONFIG_EC_HOST_CMD_BACKEND_SHI_NPCX_CS_DETECT_WORKAROUND + struct shi_npcx_data *data = dev->data; + + if (data->is_entered_cs_asserted_wui_isr != true) { + return; + } +#endif + /* Read status register and clear interrupt status early */ stat = inst->EVSTAT; inst->EVSTAT = stat; stat2 = inst->EVSTAT2; +#ifndef CONFIG_EC_HOST_CMD_BACKEND_SHI_NPCX_CS_DETECT_WORKAROUND /* SHI CS pin is asserted in EVSTAT2 */ if (IS_BIT_SET(stat2, NPCX_EVSTAT2_CSNFE)) { /* Clear pending bit of CSNFE */ @@ -692,7 +712,6 @@ static void shi_npcx_isr(const struct device *dev) shi_npcx_handle_cs_assert(dev); } - /* * End of data for read/write transaction. i.e. SHI_CS is deasserted. * Host completed or aborted transaction @@ -712,6 +731,7 @@ static void shi_npcx_isr(const struct device *dev) LOG_DBG("CSH-"); return shi_npcx_handle_cs_deassert(dev); } +#endif /* * The number of bytes received reaches the size of @@ -821,6 +841,11 @@ static int shi_npcx_enable(const struct device *dev) } NVIC_ClearPendingIRQ(DT_INST_IRQN(0)); + /* + * Clear the pending bit because switching the pinmux (pinctrl) might casue a faking WUI + * pending bit set. + */ + npcx_miwu_irq_get_and_clear_pending(&config->shi_cs_wui); npcx_miwu_irq_enable(&config->shi_cs_wui); irq_enable(DT_INST_IRQN(0)); @@ -861,6 +886,23 @@ static int shi_npcx_disable(const struct device *dev) return 0; } +#ifdef CONFIG_EC_HOST_CMD_BACKEND_SHI_NPCX_CS_DETECT_WORKAROUND +static void shi_npcx_cs_wui_isr(const struct device *dev, struct npcx_wui *wui) +{ + + struct shi_reg *const inst = HAL_INSTANCE(dev); + struct shi_npcx_data *data = dev->data; + + if (IS_BIT_SET(inst->SHICFG2, NPCX_SHICFG2_BUSY)) { + data->is_entered_cs_asserted_wui_isr = true; + shi_npcx_handle_cs_assert(dev); + } else { + shi_npcx_handle_cs_deassert(dev); + data->is_entered_cs_asserted_wui_isr = false; + } +} +#endif + static int shi_npcx_init_registers(const struct device *dev) { int ret; @@ -912,7 +954,7 @@ static int shi_npcx_init_registers(const struct device *dev) * [1] - OBHEEN = 0: Output Buffer Half Empty Interrupt Enable * [0] - OBEEN = 0: Output Buffer Empty Interrupt Enable */ - inst->EVENABLE = BIT(NPCX_EVENABLE_EOREN) | IBF_IBHF_EN_MASK; + inst->EVENABLE = IBF_IBHF_EN_MASK; /* * EVENABLE2 (Event Enable 2) setting @@ -920,7 +962,9 @@ static int shi_npcx_init_registers(const struct device *dev) * [1] - CSNREEN = 1: SHI_CS Rising Edge Interrupt Enable * [0] - IBHF2EN = 0: Input Buffer Half Full 2 Interrupt Enable */ +#ifndef CONFIG_EC_HOST_CMD_BACKEND_SHI_NPCX_CS_DETECT_WORKAROUND inst->EVENABLE2 = BIT(NPCX_EVENABLE2_CSNREEN) | BIT(NPCX_EVENABLE2_CSNFEEN); +#endif /* Clear SHI events status register */ inst->EVSTAT = 0xff; @@ -929,7 +973,18 @@ static int shi_npcx_init_registers(const struct device *dev) inst->SHICFG6 |= BIT(NPCX_SHICFG6_EBUFMD); } +#ifdef CONFIG_EC_HOST_CMD_BACKEND_SHI_NPCX_CS_DETECT_WORKAROUND + struct shi_npcx_data *data = dev->data; + + npcx_miwu_interrupt_configure(&config->shi_cs_wui, NPCX_MIWU_MODE_EDGE, + NPCX_MIWU_TRIG_BOTH); + + npcx_miwu_init_dev_callback(&data->shi_cs_wui_cb, &config->shi_cs_wui, shi_npcx_cs_wui_isr, + dev); + npcx_miwu_manage_callback(&data->shi_cs_wui_cb, true); +#else npcx_miwu_interrupt_configure(&config->shi_cs_wui, NPCX_MIWU_MODE_EDGE, NPCX_MIWU_TRIG_LOW); +#endif /* SHI interrupt installation */ IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), shi_npcx_isr, DEVICE_DT_INST_GET(0),