210 lines
6.2 KiB
C
210 lines
6.2 KiB
C
/*
|
|
* Copyright (c) 2021 ITE Corporation. All Rights Reserved.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ite_it8xxx2_uart
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/drivers/uart.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/pm/device.h>
|
|
#include <zephyr/pm/policy.h>
|
|
#include <soc.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(uart_ite_it8xxx2, CONFIG_UART_LOG_LEVEL);
|
|
|
|
#if defined(CONFIG_PM_DEVICE) && defined(CONFIG_UART_CONSOLE_INPUT_EXPIRED)
|
|
static struct uart_it8xxx2_data *uart_console_data;
|
|
#endif
|
|
|
|
struct uart_it8xxx2_config {
|
|
uint8_t port;
|
|
/* GPIO cells */
|
|
struct gpio_dt_spec gpio_wui;
|
|
/* UART handle */
|
|
const struct device *uart_dev;
|
|
/* UART alternate configuration */
|
|
const struct pinctrl_dev_config *pcfg;
|
|
};
|
|
|
|
struct uart_it8xxx2_data {
|
|
#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
|
|
struct k_work_delayable rx_refresh_timeout_work;
|
|
#endif
|
|
};
|
|
|
|
enum uart_port_num {
|
|
UART1 = 1,
|
|
UART2,
|
|
};
|
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
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);
|
|
|
|
/* Refresh console expired time if got UART Rx wake-up event */
|
|
#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
|
|
k_timeout_t delay = K_MSEC(CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT);
|
|
|
|
/*
|
|
* The pm state of it8xxx2 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(&uart_console_data->rx_refresh_timeout_work, delay);
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
|
|
/* Refresh console expired time if got UART Rx wake-up event */
|
|
#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
|
|
k_timeout_t delay = K_MSEC(CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT);
|
|
|
|
/*
|
|
* The pm state of it8xxx2 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(&uart_console_data->rx_refresh_timeout_work, delay);
|
|
#endif
|
|
}
|
|
|
|
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_MODE_EDGE | 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;
|
|
}
|
|
|
|
#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
|
|
static void uart_it8xxx2_rx_refresh_timeout(struct k_work *work)
|
|
{
|
|
ARG_UNUSED(work);
|
|
|
|
pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES);
|
|
}
|
|
#endif
|
|
#endif /* CONFIG_PM_DEVICE */
|
|
|
|
|
|
static int uart_it8xxx2_init(const struct device *dev)
|
|
{
|
|
const struct uart_it8xxx2_config *const config = dev->config;
|
|
int status;
|
|
|
|
/* Set the pin to UART alternate function. */
|
|
status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
if (status < 0) {
|
|
LOG_ERR("Failed to configure UART pins");
|
|
return status;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
const struct device *uart_console_dev =
|
|
DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
|
|
int ret = 0;
|
|
|
|
/*
|
|
* If the UART is used as a console device, we need to configure
|
|
* UART Rx interrupt as wakeup source and initialize a delayable
|
|
* work for console expired time.
|
|
*/
|
|
if (config->uart_dev == uart_console_dev) {
|
|
#ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
|
|
uart_console_data = dev->data;
|
|
k_work_init_delayable(&uart_console_data->rx_refresh_timeout_work,
|
|
uart_it8xxx2_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.
|
|
*/
|
|
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) \
|
|
PINCTRL_DT_INST_DEFINE(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), \
|
|
.uart_dev = DEVICE_DT_GET(DT_INST_PHANDLE(inst, uart_dev)), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
|
|
}; \
|
|
\
|
|
static struct uart_it8xxx2_data uart_it8xxx2_data_##inst; \
|
|
\
|
|
PM_DEVICE_DT_INST_DEFINE(inst, uart_it8xxx2_pm_action); \
|
|
DEVICE_DT_INST_DEFINE(inst, uart_it8xxx2_init, \
|
|
PM_DEVICE_DT_INST_GET(inst), \
|
|
&uart_it8xxx2_data_##inst, \
|
|
&uart_it8xxx2_cfg_##inst, \
|
|
PRE_KERNEL_1, \
|
|
CONFIG_UART_ITE_IT8XXX2_INIT_PRIORITY, \
|
|
NULL);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(UART_ITE_IT8XXX2_INIT)
|