drivers/serial: Add ITE UART wrapper to enable serial driver of ns16550

Add ITE UART wrapper to enable serial driver of ns16550.

Signed-off-by: Tim Lin <tim2.lin@ite.corp-partner.google.com>
This commit is contained in:
Tim Lin 2024-11-16 14:37:29 +08:00 committed by Benjamin Cabé
commit d64d87f655
6 changed files with 242 additions and 0 deletions

View file

@ -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)

View file

@ -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"

View file

@ -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.

View file

@ -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 <soc.h>
#include <soc_dt.h>
#include <zephyr/device.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/interrupt_controller/wuc_ite_it51xxx.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/policy.h>
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)

View file

@ -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

View file

@ -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 = <IT51XXX_IRQ_WU86 IRQ_TYPE_LEVEL_HIGH>;
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 = <IT51XXX_IRQ_WU61 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&intc>;
clocks = <&ecpm IT51XXX_ECPM_CGCTRL3R_OFF BIT(2)>;
};
intc: interrupt-controller@f04300 {
compatible = "ite,it51xxx-intc";
#address-cells = <0>;