blob: 4a64b3fbcf6fed5c47947768cbdf0b7a90c7fc9a [file] [log] [blame]
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2024 Realtek Semiconductor Corporation, SIBG-SD7
* Author: Lin Yu-Cheng <lin_yu_cheng@realtek.com>
*/
#define DT_DRV_COMPAT realtek_rts5912_uart
#include <zephyr/kernel.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/clock_control_rts5912.h>
#include <zephyr/drivers/serial/uart_ns16550.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/pm.h>
#include <zephyr/pm/policy.h>
#include <zephyr/pm/device.h>
#include "zephyr/drivers/gpio/gpio_rts5912.h"
LOG_MODULE_REGISTER(uart_rts5912, CONFIG_UART_LOG_LEVEL);
/* device config */
struct uart_rts5912_device_config {
const struct pinctrl_dev_config *pcfg;
const struct device *clk_dev;
struct rts5912_sccon_subsys sccon_cfg;
struct gpio_dt_spec uart_rx_wakeup;
const struct device *uart_dev;
};
/** Device data structure */
struct uart_rts5912_dev_data {
#if defined(CONFIG_PM)
struct pm_notifier pm_handles;
uint32_t *rts5912_rx_wake_reg;
uint32_t rx_wakeup_pin_num;
#endif
};
DT_INST_FOREACH_STATUS_OKAY(PINCTRL_DT_INST_DEFINE);
#if defined(CONFIG_PM)
#define REG_IIR 0x08 /* Interrupt ID reg. */
#define REG_LSR 0x14 /* Line status reg. */
#define REG_USR 0x7C /* UART status reg. (DW8250) */
#define IIR_NIP 0x01 /* no interrupt pending */
#define IIR_THRE 0x02 /* transmit holding register empty interrupt */
#define IIR_RBRF 0x04 /* receiver buffer register full interrupt */
#define IIR_LS 0x06 /* receiver line status interrupt */
#define IIR_MASK 0x07 /* interrupt id bits mask */
#define IIR_BUSY 0x07 /* iir busy mask */
#define USR_BUSY_CHECK BIT(0)
#define RTS5912_MAXIMUM_UART_POLLING_TIME_US (50 * USEC_PER_MSEC)
static struct k_work_delayable rx_refresh_timeout_work;
static void uart_rts5912_rx_refresh_timeout(struct k_work *work)
{
ARG_UNUSED(work);
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
}
/* Hook to count entry/exit */
static void uart_rts5912_pm_state_entry(const struct device *dev, enum pm_state state)
{
const struct uart_rts5912_dev_data *const dev_data = dev->data;
switch (state) {
case PM_STATE_SUSPEND_TO_IDLE:
irq_enable(dev_data->rx_wakeup_pin_num);
gpio_rts5912_set_wakeup_pin(dev_data->rx_wakeup_pin_num);
break;
default:
break;
}
}
/* Hook to count entry/exit */
static void uart_rts5912_pm_state_exit(const struct device *dev, enum pm_state state)
{
const struct uart_rts5912_device_config *const dev_cfg = dev->config;
const struct uart_rts5912_dev_data *const dev_data = dev->data;
switch (state) {
case PM_STATE_SUSPEND_TO_IDLE:
uint32_t interrupt_pin = gpio_rts5912_get_intr_pin(dev_data->rts5912_rx_wake_reg);
pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (IS_ENABLED(CONFIG_UART_CONSOLE_INPUT_EXPIRED) &&
interrupt_pin == dev_cfg->uart_rx_wakeup.pin) {
k_timeout_t delay = K_MSEC(CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT);
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
k_work_reschedule(&rx_refresh_timeout_work, delay);
}
NVIC_ClearPendingIRQ(dev_data->rx_wakeup_pin_num);
irq_disable(dev_data->rx_wakeup_pin_num);
break;
default:
break;
}
}
static void uart_rx_wait(const struct device *dev, void *user_data)
{
ARG_UNUSED(user_data);
if (IS_ENABLED(CONFIG_UART_CONSOLE_INPUT_EXPIRED) && uart_irq_rx_ready(dev)) {
k_timeout_t delay = K_MSEC(CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT);
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
k_work_reschedule(&rx_refresh_timeout_work, delay);
}
}
#endif
static int rts5912_uart_init(const struct device *dev)
{
const struct uart_rts5912_device_config *const dev_cfg = dev->config;
struct uart_rts5912_dev_data *const dev_data = dev->data;
int rc;
if (!device_is_ready(dev_cfg->clk_dev)) {
return -ENODEV;
}
rc = clock_control_on(dev_cfg->clk_dev, (clock_control_subsys_t)&dev_cfg->sccon_cfg);
if (rc != 0) {
return rc;
}
rc = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (rc != 0) {
return rc;
}
#if defined(CONFIG_PM)
const uint32_t uart_reg = uart_ns16550_get_port(dev_cfg->uart_dev);
uint32_t wf_cycle_count = k_us_to_cyc_ceil32(RTS5912_MAXIMUM_UART_POLLING_TIME_US);
uint32_t wf_start = k_cycle_get_32();
uint32_t wf_now = wf_start;
/* For RTS5912 UART, if enable UART wake up function, it will
* change GPIO pin from uart funciton to gpio function before WFI.
* System need to ensure this register is cleared in every time doing init function.
*/
while (wf_cycle_count > (wf_now - wf_start)) {
uint32_t iir = sys_read32(uart_reg + REG_IIR) & IIR_MASK;
if (iir == IIR_NIP) {
break;
}
switch (iir) {
case IIR_LS: /* Receiver line status */
sys_read32(uart_reg + REG_LSR);
break;
case IIR_RBRF: /* Received data available*/
sys_write32(sys_read32(uart_reg + REG_IIR) | IIR_THRE,
(uart_reg + REG_IIR));
break;
case IIR_BUSY:
while (wf_cycle_count > (wf_now - wf_start)) {
uint32_t usr = sys_read32(uart_reg + REG_USR);
if ((usr & USR_BUSY_CHECK) == 0) {
break;
}
wf_now = k_cycle_get_32();
}
break;
}
iir = sys_read32(uart_reg + REG_IIR) & IIR_MASK;
if (iir == IIR_NIP) {
break;
}
k_busy_wait(10);
wf_now = k_cycle_get_32();
}
if (wf_cycle_count <= (wf_now - wf_start)) {
LOG_ERR("Uart reset iir reach timeout");
return -EIO;
}
k_work_init_delayable(&rx_refresh_timeout_work, uart_rts5912_rx_refresh_timeout);
pm_notifier_register(&dev_data->pm_handles);
uart_irq_callback_set(DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), uart_rx_wait);
dev_data->rx_wakeup_pin_num = gpio_rts5912_get_pin_num(&dev_cfg->uart_rx_wakeup);
dev_data->rts5912_rx_wake_reg = gpio_rts5912_get_port_address(&dev_cfg->uart_rx_wakeup);
#endif
return 0;
}
#if defined(CONFIG_PM)
#define UART_REALTEK_RTS5912_PM_HANDLES_DEFINE(n) \
static void uart_rts5912_##n##_pm_entry(enum pm_state state) \
{ \
uart_rts5912_pm_state_entry(DEVICE_DT_INST_GET(n), state); \
} \
\
static void uart_rts5912_##n##_pm_exit(enum pm_state state) \
{ \
uart_rts5912_pm_state_exit(DEVICE_DT_INST_GET(n), state); \
}
#define UART_REALTEK_RTS5912_PM_HANDLES_BIND(n) \
.pm_handles = { \
.state_entry = uart_rts5912_##n##_pm_entry, \
.state_exit = uart_rts5912_##n##_pm_exit, \
},
#else
#define UART_REALTEK_RTS5912_PM_HANDLES_DEFINE(n)
#define UART_REALTEK_RTS5912_PM_HANDLES_BIND(n)
#endif
#define UART_RTS5912_DEVICE_INIT(n) \
UART_REALTEK_RTS5912_PM_HANDLES_DEFINE(n) \
\
static const struct uart_rts5912_device_config uart_rts5912_dev_cfg_##n = { \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.sccon_cfg = \
{ \
.clk_grp = DT_INST_CLOCKS_CELL_BY_NAME(n, uart##n, clk_grp), \
.clk_idx = DT_INST_CLOCKS_CELL_BY_NAME(n, uart##n, clk_idx), \
}, \
.uart_rx_wakeup = GPIO_DT_SPEC_INST_GET(n, rx_gpios), \
.uart_dev = DEVICE_DT_GET(DT_INST_PHANDLE(n, uart_dev)), \
}; \
\
static struct uart_rts5912_dev_data uart_rts5912_dev_data_##n = { \
UART_REALTEK_RTS5912_PM_HANDLES_BIND(n)}; \
\
DEVICE_DT_INST_DEFINE(n, &rts5912_uart_init, NULL, &uart_rts5912_dev_data_##n, \
&uart_rts5912_dev_cfg_##n, PRE_KERNEL_1, \
CONFIG_UART_RTS5912_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(UART_RTS5912_DEVICE_INIT)