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