blob: a68e6c1a15eeb8b2c33962609acb961e10e4db17 [file] [log] [blame]
/*
* 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)