| /* | 
 |  * 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) |