From e29a15c0e3edca0d82cd9929f943c76f319f92d5 Mon Sep 17 00:00:00 2001 From: Tim Lin Date: Thu, 21 Oct 2021 15:05:50 +0800 Subject: [PATCH] ITE: drivers/serial: add the UART driver for the PM callback function IT8XXX2 uses shared ns16550.c driver which does not provide a power management callback(pm_action_cb), so create driver to handle IT8XXX2 specific UART features. note: pm_action_cb(old name: pm_control_fn) Signed-off-by: Tim Lin --- CODEOWNERS | 2 + boards/riscv/it8xxx2_evb/it8xxx2_evb.dts | 6 + drivers/serial/CMakeLists.txt | 1 + drivers/serial/Kconfig | 2 + drivers/serial/Kconfig.it8xxx2 | 11 ++ drivers/serial/uart_ite_it8xxx2.c | 139 ++++++++++++++++++ dts/bindings/serial/ite,it8xxx2-uart.yaml | 21 +++ dts/riscv/it8xxx2.dtsi | 20 +++ soc/riscv/riscv-ite/common/power.c | 114 -------------- .../it8xxx2/Kconfig.defconfig.series | 4 + 10 files changed, 206 insertions(+), 114 deletions(-) create mode 100644 drivers/serial/Kconfig.it8xxx2 create mode 100644 drivers/serial/uart_ite_it8xxx2.c create mode 100644 dts/bindings/serial/ite,it8xxx2-uart.yaml diff --git a/CODEOWNERS b/CODEOWNERS index 5cb1840f33f..c2273134c0a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -344,6 +344,8 @@ /drivers/serial/serial_test.c @str4t0m /drivers/serial/Kconfig.xen @lorc @firscity /drivers/serial/uart_hvc_xen.c @lorc @firscity +/drivers/serial/Kconfig.it8xxx2 @GTLin08 +/drivers/serial/uart_ite_it8xxx2.c @GTLin08 /drivers/disk/ @jfischer-no /drivers/disk/sdmmc_sdhc.h @JunYangNXP /drivers/disk/sdmmc_spi.c @JunYangNXP diff --git a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts index fc3e7e31ce1..79ee721e27c 100644 --- a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts +++ b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts @@ -78,6 +78,12 @@ current-speed = <460800>; clock-frequency = <1804800>; }; +&ite_uart1_wrapper { + status = "okay"; +}; +&ite_uart2_wrapper { + status = "okay"; +}; &pwm0 { status = "okay"; prescaler-cx = ; diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index a5c8c87c6c1..350c8c2f6c9 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -4,6 +4,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_UART_ALTERA_JTAG uart_altera_jtag_hal.c) zephyr_library_sources_ifdef(CONFIG_UART_TELINK_B91 uart_b91.c) zephyr_library_sources_ifdef(CONFIG_UART_IMX uart_imx.c) +zephyr_library_sources_ifdef(CONFIG_UART_ITE_IT8XXX2 uart_ite_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_UART_CC13XX_CC26XX uart_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_UART_CC32XX uart_cc32xx.c) zephyr_library_sources_ifdef(CONFIG_UART_CMSDK_APB uart_cmsdk_apb.c) diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index dd607ad23cb..2455174ae42 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -117,6 +117,8 @@ source "drivers/serial/Kconfig.miv" source "drivers/serial/Kconfig.imx" +source "drivers/serial/Kconfig.it8xxx2" + source "drivers/serial/Kconfig.stellaris" source "drivers/serial/Kconfig.native_posix" diff --git a/drivers/serial/Kconfig.it8xxx2 b/drivers/serial/Kconfig.it8xxx2 new file mode 100644 index 00000000000..0316b37b19d --- /dev/null +++ b/drivers/serial/Kconfig.it8xxx2 @@ -0,0 +1,11 @@ +# Copyright (c) 2021 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +config UART_ITE_IT8XXX2 + bool "ITE IT8XXX2 UART driver" + help + IT8XXX2 uses shared ns16550.c driver which does not + provide a power management callback, so create driver + to handle IT8XXX2 specific UART features. In addition + to use pm_action_cb, we also need to make some setting + at uart_it8xxx2_init. diff --git a/drivers/serial/uart_ite_it8xxx2.c b/drivers/serial/uart_ite_it8xxx2.c new file mode 100644 index 00000000000..205c7b23d6e --- /dev/null +++ b/drivers/serial/uart_ite_it8xxx2.c @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2021 ITE Corporation. All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ite_it8xxx2_uart + +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(uart_ite_it8xxx2, LOG_LEVEL_ERR); + +struct uart_it8xxx2_config { + uint8_t port; + /* GPIO cells */ + struct gpio_dt_spec gpio_wui; +}; + +enum uart_port_num { + UART1 = 1, + UART2, +}; + +#ifdef CONFIG_PM_DEVICE +__weak void uart1_wui_isr_late(void) { } +__weak void uart2_wui_isr_late(void) { } + +void uart1_wui_isr(const struct device *gpio, struct gpio_callback *cb, + uint32_t pins) +{ + /* Disable interrupts on UART1 RX pin to avoid repeated interrupts. */ + (void)gpio_pin_interrupt_configure(gpio, (find_msb_set(pins) - 1), + GPIO_INT_DISABLE); + + if (uart1_wui_isr_late) { + uart1_wui_isr_late(); + } +} + +void uart2_wui_isr(const struct device *gpio, struct gpio_callback *cb, + uint32_t pins) +{ + /* Disable interrupts on UART2 RX pin to avoid repeated interrupts. */ + (void)gpio_pin_interrupt_configure(gpio, (find_msb_set(pins) - 1), + GPIO_INT_DISABLE); + + if (uart2_wui_isr_late) { + uart2_wui_isr_late(); + } +} + +static inline int uart_it8xxx2_pm_action(const struct device *dev, + enum pm_device_action action) +{ + const struct uart_it8xxx2_config *const config = dev->config; + int ret = 0; + + 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: + /* Enable UART WUI */ + ret = gpio_pin_interrupt_configure_dt(&config->gpio_wui, + GPIO_INT_TRIG_LOW); + if (ret < 0) { + LOG_ERR("Failed to configure UART%d WUI (ret %d)", + config->port, ret); + return ret; + } + + break; + default: + return -ENOTSUP; + } + + return 0; +} +#endif /* CONFIG_PM_DEVICE */ + +static int uart_it8xxx2_init(const struct device *dev) +{ +#ifdef CONFIG_PM_DEVICE + const struct uart_it8xxx2_config *const config = dev->config; + int ret = 0; + + /* + * 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. + */ + if (config->port == UART1) { + static struct gpio_callback uart1_wui_cb; + + gpio_init_callback(&uart1_wui_cb, uart1_wui_isr, + BIT(config->gpio_wui.pin)); + + ret = gpio_add_callback(config->gpio_wui.port, &uart1_wui_cb); + } else if (config->port == UART2) { + static struct gpio_callback uart2_wui_cb; + + gpio_init_callback(&uart2_wui_cb, uart2_wui_isr, + BIT(config->gpio_wui.pin)); + + ret = gpio_add_callback(config->gpio_wui.port, &uart2_wui_cb); + } + + if (ret < 0) { + LOG_ERR("Failed to add UART%d callback (err %d)", + config->port, ret); + return ret; + } +#endif /* CONFIG_PM_DEVICE */ + + return 0; +} + +#define UART_ITE_IT8XXX2_INIT(inst) \ + static const struct uart_it8xxx2_config uart_it8xxx2_cfg_##inst = { \ + .port = DT_INST_PROP(inst, port_num), \ + .gpio_wui = GPIO_DT_SPEC_INST_GET(inst, gpios), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, &uart_it8xxx2_init, \ + uart_it8xxx2_pm_action, \ + NULL, &uart_it8xxx2_cfg_##inst, \ + PRE_KERNEL_1, \ + CONFIG_SERIAL_INIT_PRIORITY, \ + NULL); + +DT_INST_FOREACH_STATUS_OKAY(UART_ITE_IT8XXX2_INIT) diff --git a/dts/bindings/serial/ite,it8xxx2-uart.yaml b/dts/bindings/serial/ite,it8xxx2-uart.yaml new file mode 100644 index 00000000000..a0a6eedbabe --- /dev/null +++ b/dts/bindings/serial/ite,it8xxx2-uart.yaml @@ -0,0 +1,21 @@ +# Copyright (c) 2021 ITE Corporation. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +description: ITE, IT8XXX2-UART node + +compatible: "ite,it8xxx2-uart" + +include: uart-controller.yaml + +properties: + reg: + required: true + + port-num: + type: int + required: true + description: Ordinal identifying the port + + gpios: + type: phandle-array + required: true diff --git a/dts/riscv/it8xxx2.dtsi b/dts/riscv/it8xxx2.dtsi index c1b0192849c..39b8190517e 100644 --- a/dts/riscv/it8xxx2.dtsi +++ b/dts/riscv/it8xxx2.dtsi @@ -276,6 +276,24 @@ interrupt-parent = <&intc>; }; + ite_uart1_wrapper: uartwrapper@f02720 { + compatible = "ite,it8xxx2-uart"; + reg = <0x00f02720 0x0020>; + status = "disabled"; + label = "UART1_WRAPPER"; + port-num = <1>; + gpios = <&gpiob 0 0>; + }; + + ite_uart2_wrapper: uartwrapper@f02820 { + compatible = "ite,it8xxx2-uart"; + reg = <0x00f02820 0x0020>; + status = "disabled"; + label = "UART2_WRAPPER"; + port-num = <2>; + gpios = <&gpioh 1 0>; + }; + twd0: watchdog@f01f00 { compatible = "ite,it8xxx2-watchdog"; reg = <0x00f01f00 0x0062>; @@ -337,6 +355,7 @@ IT8XXX2_IRQ_WU105 IRQ_TYPE_LEVEL_HIGH IT8XXX2_IRQ_WU106 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&intc>; + wakeup-source; /* WUI53 */ #gpio-cells = <2>; }; @@ -463,6 +482,7 @@ IT8XXX2_IRQ_WU90 IRQ_TYPE_LEVEL_HIGH 0 IRQ_TYPE_LEVEL_HIGH>; interrupt-parent = <&intc>; + wakeup-source; /* WUI17 */ #gpio-cells = <2>; }; diff --git a/soc/riscv/riscv-ite/common/power.c b/soc/riscv/riscv-ite/common/power.c index 6e7716ffff1..a657fffea25 100644 --- a/soc/riscv/riscv-ite/common/power.c +++ b/soc/riscv/riscv-ite/common/power.c @@ -4,81 +4,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include #include #include #include #include -#include -LOG_MODULE_DECLARE(power, CONFIG_PM_LOG_LEVEL); - -__weak void uart1_wui_isr_late(void) { } -__weak void uart2_wui_isr_late(void) { } - -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) -static const struct device *uart1_dev; - -void uart1_wui_isr(const struct device *gpio, struct gpio_callback *cb, - uint32_t pins) -{ - /* Disable interrupts on UART1 RX pin to avoid repeated interrupts. */ - gpio_pin_interrupt_configure(gpio, (find_msb_set(pins) - 1), - GPIO_INT_DISABLE); - - if (uart1_wui_isr_late) { - uart1_wui_isr_late(); - } -} -#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) */ - -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) -static const struct device *uart2_dev; - -void uart2_wui_isr(const struct device *gpio, struct gpio_callback *cb, - uint32_t pins) -{ - /* Disable interrupts on UART2 RX pin to avoid repeated interrupts. */ - gpio_pin_interrupt_configure(gpio, (find_msb_set(pins) - 1), - GPIO_INT_DISABLE); - - if (uart2_wui_isr_late) { - uart2_wui_isr_late(); - } -} -#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) */ - /* Handle when enter deep doze mode. */ static void ite_power_soc_deep_doze(void) { - int ret; -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) - /* Enable UART1 WUI */ - ret = gpio_pin_interrupt_configure(DEVICE_DT_GET(DT_NODELABEL(gpiob)), - 0, GPIO_INT_TRIG_LOW); - if (ret) { - LOG_ERR("Failed to configure UART1 WUI (ret %d)", ret); - return; - } - /* If nothing remains to be transmitted. */ - while (!uart_irq_tx_complete(uart1_dev)) - ; -#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) */ - -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) - /* Enable UART2 WUI */ - ret = gpio_pin_interrupt_configure(DEVICE_DT_GET(DT_NODELABEL(gpioh)), - 1, GPIO_INT_TRIG_LOW); - if (ret) { - LOG_ERR("Failed to configure UART2 WUI (ret %d)", ret); - return; - } - /* If nothing remains to be transmitted. */ - while (!uart_irq_tx_complete(uart2_dev)) - ; -#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) */ - /* Enter deep doze mode */ riscv_idle(CHIP_PLL_DEEP_DOZE, MSTATUS_IEN); } @@ -101,50 +34,3 @@ __weak void pm_power_state_exit_post_ops(struct pm_state_info info) { ARG_UNUSED(info); } - -static int power_it8xxx2_init(const struct device *arg) -{ - ARG_UNUSED(arg); - - int ret; -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) - static struct gpio_callback uart1_wui_cb; - - uart1_dev = device_get_binding(DT_LABEL(DT_NODELABEL(uart1))); - if (!uart1_dev) { - LOG_ERR("Fail to find %s", DT_LABEL(DT_NODELABEL(uart1))); - return -ENODEV; - } - - gpio_init_callback(&uart1_wui_cb, uart1_wui_isr, BIT(0)); - - ret = gpio_add_callback(DEVICE_DT_GET(DT_NODELABEL(gpiob)), - &uart1_wui_cb); - if (ret) { - LOG_ERR("Failed to add callback (err %d)", ret); - return ret; - } -#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart1), okay) */ - -#if DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) - static struct gpio_callback uart2_wui_cb; - - uart2_dev = device_get_binding(DT_LABEL(DT_NODELABEL(uart2))); - if (!uart2_dev) { - LOG_ERR("Fail to find %s", DT_LABEL(DT_NODELABEL(uart2))); - return -ENODEV; - } - - gpio_init_callback(&uart2_wui_cb, uart2_wui_isr, BIT(1)); - - ret = gpio_add_callback(DEVICE_DT_GET(DT_NODELABEL(gpioh)), - &uart2_wui_cb); - if (ret) { - LOG_ERR("Failed to add callback (err %d)", ret); - return ret; - } -#endif /* DT_NODE_HAS_STATUS(DT_NODELABEL(uart2), okay) */ - - return 0; -} -SYS_INIT(power_it8xxx2_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series b/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series index f0845af2cbb..9c4b5a34681 100644 --- a/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series +++ b/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series @@ -33,6 +33,10 @@ config UART_NS16550_WA_ISR_REENABLE_INTERRUPT default y depends on UART_NS16550 +config UART_ITE_IT8XXX2 + default y + depends on UART_NS16550 + config RISCV_HAS_CPU_IDLE default y