diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index a026598687f..1a453ea210c 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -35,6 +35,7 @@ zephyr_library_sources_ifdef(CONFIG_UART_HOSTLINK uart_hostlink.c) zephyr_library_sources_ifdef(CONFIG_UART_IMX uart_imx.c) zephyr_library_sources_ifdef(CONFIG_UART_INFINEON_CAT1 uart_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_UART_INTEL_LW uart_intel_lw.c) +zephyr_library_sources_ifdef(CONFIG_UART_ITE_IT51XXX uart_ite_it51xxx.c) zephyr_library_sources_ifdef(CONFIG_UART_ITE_IT8XXX2 uart_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_UART_LITEX uart_litex.c) zephyr_library_sources_ifdef(CONFIG_UART_LPC11U6X uart_lpc11u6x.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 36c367a633a..ed68ef1904e 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -178,6 +178,7 @@ rsource "Kconfig.hostlink" rsource "Kconfig.ifx_cat1" rsource "Kconfig.imx" rsource "Kconfig.intel_lw" +rsource "Kconfig.it51xxx" rsource "Kconfig.it8xxx2" rsource "Kconfig.leuart_gecko" rsource "Kconfig.litex" diff --git a/drivers/serial/Kconfig.it51xxx b/drivers/serial/Kconfig.it51xxx new file mode 100644 index 00000000000..34647e84d26 --- /dev/null +++ b/drivers/serial/Kconfig.it51xxx @@ -0,0 +1,13 @@ +# Copyright (c) 2025 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +config UART_ITE_IT51XXX + bool "ITE IT51XXX UART driver" + default y + select UART_NS16550_ITE_HIGH_SPEED_BAUDRATE + depends on DT_HAS_ITE_IT51XXX_UART_ENABLED + select PINCTRL + help + IT51XXX uses shared ns16550.c driver. This wrapper primarily + registers an interrupt to wake up the EC from doze or deep + doze mode back to active state. diff --git a/drivers/serial/uart_ite_it51xxx.c b/drivers/serial/uart_ite_it51xxx.c new file mode 100644 index 00000000000..a68e6c1a15e --- /dev/null +++ b/drivers/serial/uart_ite_it51xxx.c @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 ITE Corporation. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ite_it51xxx_uart + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(uart_ite_it51xxx, CONFIG_UART_LOG_LEVEL); + +struct it51xxx_uart_wuc_map_cfg { + /* WUC control device structure */ + const struct device *wucs; + /* WUC pin mask */ + uint8_t mask; +}; + +struct uart_it51xxx_config { + /* UART wake-up input source configuration list */ + const struct it51xxx_uart_wuc_map_cfg *wuc_map_list; + const struct device *clk_dev; + /* clock configuration */ + struct ite_clk_cfg clk_cfg; + /* UART interrupt */ + uint8_t irq; +}; + +struct uart_it51xxx_data { +#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED + struct k_work_delayable rx_refresh_timeout_work; +#endif +}; + +static void it51xxx_uart_wui_isr(const void *arg) +{ + const struct device *dev = arg; + const struct uart_it51xxx_config *const config = dev->config; + + /* Disable interrupts on UART RX pin to avoid repeated interrupts. */ + irq_disable(config->irq); + /* W/C wakeup interrupt status of UART pin */ + it51xxx_wuc_clear_status(config->wuc_map_list[0].wucs, config->wuc_map_list[0].mask); + + /* Refresh console expired time if got UART Rx wake-up event */ +#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED + struct uart_it51xxx_data *data = dev->data; + k_timeout_t delay = K_MSEC(CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT); + + /* + * The pm state of it51xxx chip only supports standby, so here we + * can directly set the constraint for standby. + */ + pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); + k_work_reschedule(&data->rx_refresh_timeout_work, delay); +#endif +} + +static inline int uart_it51xxx_pm_action(const struct device *dev, enum pm_device_action action) +{ + const struct uart_it51xxx_config *const config = dev->config; + + switch (action) { + /* Next device power state is in active. */ + case PM_DEVICE_ACTION_RESUME: + /* Nothing to do. */ + break; + /* Next device power state is deep doze mode */ + case PM_DEVICE_ACTION_SUSPEND: + /* W/C wake-up interrupt status of UART pin */ + it51xxx_wuc_clear_status(config->wuc_map_list[0].wucs, + config->wuc_map_list[0].mask); + /* W/C interrupt status of UART pin */ + ite_intc_isr_clear(config->irq); + /* Enable UART interrupt */ + irq_enable(config->irq); + + break; + default: + return -ENOTSUP; + } + + return 0; +} + +#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED +static void uart_it51xxx_rx_refresh_timeout(struct k_work *work) +{ + ARG_UNUSED(work); + + pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); +} +#endif + +static int uart_it51xxx_init(const struct device *dev) +{ + const struct uart_it51xxx_config *const config = dev->config; + int ret; + + /* Enable clock to specified peripheral */ + ret = clock_control_on(config->clk_dev, (clock_control_subsys_t *)&config->clk_cfg); + if (ret < 0) { + LOG_ERR("Turn on clock fail %d", ret); + return ret; + } + + /* Select wakeup interrupt falling-edge triggered of UART pin */ + it51xxx_wuc_set_polarity(config->wuc_map_list[0].wucs, config->wuc_map_list[0].mask, + WUC_TYPE_EDGE_FALLING); + /* W/C wakeup interrupt status of UART pin */ + it51xxx_wuc_clear_status(config->wuc_map_list[0].wucs, config->wuc_map_list[0].mask); + /* Enable wakeup interrupt of UART pin */ + it51xxx_wuc_enable(config->wuc_map_list[0].wucs, config->wuc_map_list[0].mask); + + /* + * We need to configure UART Rx interrupt as wakeup source and initialize + * a delayable work for console expired time. + */ +#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED + struct uart_it51xxx_data *data = dev->data; + + k_work_init_delayable(&data->rx_refresh_timeout_work, uart_it51xxx_rx_refresh_timeout); +#endif + /* + * When the system enters deep doze, all clocks are gated only the + * 32.768k clock is active. We need to wakeup EC by configuring + * UART Rx interrupt as a wakeup source. When the interrupt of UART + * Rx falling, EC will be woken. + */ + irq_connect_dynamic(config->irq, 0, it51xxx_uart_wui_isr, dev, 0); + + return 0; +} + +#define UART_ITE_IT51XXX_INIT(inst) \ + static const struct it51xxx_uart_wuc_map_cfg \ + it51xxx_uart_wuc_##inst[IT8XXX2_DT_INST_WUCCTRL_LEN(inst)] = \ + IT8XXX2_DT_WUC_ITEMS_LIST(inst); \ + static struct uart_it51xxx_data uart_it51xxx_data_##inst; \ + static const struct uart_it51xxx_config uart_it51xxx_cfg_##inst = { \ + .wuc_map_list = it51xxx_uart_wuc_##inst, \ + .clk_dev = DEVICE_DT_GET(DT_INST_PHANDLE(inst, clocks)), \ + .clk_cfg = {.ctrl = DT_INST_CLOCKS_CELL(inst, ctrl), \ + .bits = DT_INST_CLOCKS_CELL(inst, bits)}, \ + .irq = DT_INST_IRQN(inst), \ + }; \ + PM_DEVICE_DT_INST_DEFINE(inst, uart_it51xxx_pm_action); \ + DEVICE_DT_INST_DEFINE(inst, uart_it51xxx_init, PM_DEVICE_DT_INST_GET(inst), \ + &uart_it51xxx_data_##inst, &uart_it51xxx_cfg_##inst, PRE_KERNEL_1, \ + CONFIG_SERIAL_INIT_PRIORITY, NULL); + +DT_INST_FOREACH_STATUS_OKAY(UART_ITE_IT51XXX_INIT) diff --git a/dts/bindings/serial/ite,it51xxx-uart.yaml b/dts/bindings/serial/ite,it51xxx-uart.yaml new file mode 100644 index 00000000000..cc6fae88b4b --- /dev/null +++ b/dts/bindings/serial/ite,it51xxx-uart.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2025 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +description: ITE, IT51XXX-UART node + +compatible: "ite,it51xxx-uart" + +include: [uart-controller.yaml] + +properties: + reg: + required: true + + wucctrl: + type: phandles + description: | + WUC groups internal and external inputs, and asserts wake-up + signals to INTC that allows the CPU to exit a Doze/Deep + Doze/Sleep mode. + + clocks: + required: true diff --git a/dts/riscv/ite/it51xxx.dtsi b/dts/riscv/ite/it51xxx.dtsi index ffccfc3cda9..8300fc130b7 100644 --- a/dts/riscv/ite/it51xxx.dtsi +++ b/dts/riscv/ite/it51xxx.dtsi @@ -927,6 +927,48 @@ reg = <0x00f02000 0x100>; }; + uart1: uart@f02700 { + compatible = "ns16550"; + reg = <0x00f02700 0x0020>; + status = "disabled"; + current-speed = <115200>; + clock-frequency = <1843200>; + interrupts = <38 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&intc>; + reg-shift = <0>; + }; + + ite_uart1_wrapper: uartwrapper@f02720 { + compatible = "ite,it51xxx-uart"; + reg = <0x00f02720 0x0020>; + status = "disabled"; + wucctrl = <&wuc_wu86>; /* GPC7 */ + interrupts = ; + interrupt-parent = <&intc>; + clocks = <&ecpm IT51XXX_ECPM_CGCTRL3R_OFF BIT(2)>; + }; + + uart2: uart@f02800 { + compatible = "ns16550"; + reg = <0x00f02800 0x0020>; + status = "disabled"; + current-speed = <460800>; + clock-frequency = <1843200>; + interrupts = <39 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&intc>; + reg-shift = <0>; + }; + + ite_uart2_wrapper: uartwrapper@f02820 { + compatible = "ite,it51xxx-uart"; + reg = <0x00f02820 0x0020>; + status = "disabled"; + wucctrl = <&wuc_wu61>; /* GPH1 */ + interrupts = ; + interrupt-parent = <&intc>; + clocks = <&ecpm IT51XXX_ECPM_CGCTRL3R_OFF BIT(2)>; + }; + intc: interrupt-controller@f04300 { compatible = "ite,it51xxx-intc"; #address-cells = <0>;