text: /*
   * Copyright (c) 2020 Nuvoton Technology Corporation.
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #define DT_DRV_COMPAT nuvoton_npcx_uart
  
  #include <sys/__assert.h>
  #include <drivers/gpio.h>
  #include <drivers/uart.h>
  #include <drivers/clock_control.h>
  #include <kernel.h>
  #include <pm/device.h>
  #include <pm/pm.h>
  #include <soc.h>
  #include "soc_miwu.h"
  #include "soc_power.h"
  
  #include <logging/log.h>
  LOG_MODULE_REGISTER(uart_npcx, LOG_LEVEL_ERR);
  
  /* Driver config */
  struct uart_npcx_config {
  	struct uart_device_config uconf;
  	/* clock configuration */
  	struct npcx_clk_cfg clk_cfg;
  	/* int-mux configuration */
  	const struct npcx_wui uart_rx_wui;
  	/* pinmux configuration */
  	const uint8_t alts_size;
  	const struct npcx_alt *alts_list;
  };
  
  enum uart_pm_constraint_flag {
  	UART_PM_CONSTRAINT_TX_FLAG,
  	UART_PM_CONSTRAINT_RX_FLAG,
  
  	UART_PM_CONSTRAINT_FLAG_COUNT,
  };
  
  /* Driver data */
  struct uart_npcx_data {
  	/* Baud rate */
  	uint32_t baud_rate;
  	struct miwu_dev_callback uart_rx_cb;
  #ifdef CONFIG_UART_INTERRUPT_DRIVEN
  	uart_irq_callback_user_data_t user_cb;
  	void *user_data;
  #endif
  #ifdef CONFIG_PM
  	ATOMIC_DEFINE(pm_constraint_flag, UART_PM_CONSTRAINT_FLAG_COUNT);
  #ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
  	struct k_work_delayable rx_refresh_timeout_work;
  #endif
  #endif
  };
  
  /* Driver convenience defines */
  #define HAL_INSTANCE(dev)                                                                          \
  	((struct uart_reg *)((const struct uart_npcx_config *)(dev)->config)->uconf.base)
  
  #if defined(CONFIG_PM) && defined(CONFIG_UART_INTERRUPT_DRIVEN)
  static void uart_npcx_pm_constraint_set(struct uart_npcx_data *data,
  					enum uart_pm_constraint_flag flag)
  {
  	if (atomic_test_and_set_bit(data->pm_constraint_flag, flag) == 0) {
  		pm_constraint_set(PM_STATE_SUSPEND_TO_IDLE);
  	}
  }
  
  static void uart_npcx_pm_constraint_rel(struct uart_npcx_data *data,
  					enum uart_pm_constraint_flag flag)
  {
  	if (atomic_test_and_clear_bit(data->pm_constraint_flag, flag) == 1) {
  		pm_constraint_release(PM_STATE_SUSPEND_TO_IDLE);
  	}
  }
  #endif /* defined(CONFIG_PM) && defined(CONFIG_UART_INTERRUPT_DRIVEN) */
  
  /* UART local functions */
  static int uart_set_npcx_baud_rate(struct uart_reg *const inst, int baud_rate, int src_clk)
  {
  	/* Fix baud rate to 115200 so far */
  	if (baud_rate == 115200) {
  		if (src_clk == 15000000) {
  			inst->UPSR = 0x38;
  			inst->UBAUD = 0x01;
  		} else if (src_clk == 20000000) {
  			inst->UPSR = 0x08;
  			inst->UBAUD = 0x0a;
  		} else {
  			return -EINVAL;
  		}
  	} else {
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  #ifdef CONFIG_UART_INTERRUPT_DRIVEN
  static int uart_npcx_tx_fifo_ready(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	/* True if the Tx FIFO is not completely full */
  	return !(GET_FIELD(inst->UFTSTS, NPCX_UFTSTS_TEMPTY_LVL) == 0);
  }
  
  static int uart_npcx_rx_fifo_available(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	/* True if at least one byte is in the Rx FIFO */
  	return IS_BIT_SET(inst->UFRSTS, NPCX_UFRSTS_RFIFO_NEMPTY_STS);
  }
  
  static void uart_npcx_dis_all_tx_interrupts(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	/* Disable all Tx interrupts */
  	inst->UFTCTL &= ~(BIT(NPCX_UFTCTL_TEMPTY_LVL_EN) | BIT(NPCX_UFTCTL_TEMPTY_EN) |
  			  BIT(NPCX_UFTCTL_NXMIP_EN));
  }
  
  static void uart_npcx_clear_rx_fifo(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  	uint8_t scratch;
  
  	/* Read all dummy bytes out from Rx FIFO */
  	while (uart_npcx_rx_fifo_available(dev))
  		scratch = inst->URBUF;
  }
  
  static int uart_npcx_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  	uint8_t tx_bytes = 0U;
  
  	/* If Tx FIFO is still ready to send */
  	while ((size - tx_bytes > 0) && uart_npcx_tx_fifo_ready(dev)) {
  		/* Put a character into Tx FIFO */
  #ifdef CONFIG_PM
  		struct uart_npcx_data *data = dev->data;
  
  		uart_npcx_pm_constraint_set(data, UART_PM_CONSTRAINT_TX_FLAG);
  		inst->UTBUF = tx_data[tx_bytes++];
  		inst->UFTCTL |= BIT(NPCX_UFTCTL_NXMIP_EN);
  #else
  		inst->UTBUF = tx_data[tx_bytes++];
  #endif /* CONFIG_PM */
  	}
  
  	return tx_bytes;
  }
  
  static int uart_npcx_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  	unsigned int rx_bytes = 0U;
  
  	/* If least one byte is in the Rx FIFO */
  	while ((size - rx_bytes > 0) && uart_npcx_rx_fifo_available(dev)) {
  		/* Receive one byte from Rx FIFO */
  		rx_data[rx_bytes++] = inst->URBUF;
  	}
  
  	return rx_bytes;
  }
  
  static void uart_npcx_irq_tx_enable(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	inst->UFTCTL |= BIT(NPCX_UFTCTL_TEMPTY_EN);
  }
  
  static void uart_npcx_irq_tx_disable(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	inst->UFTCTL &= ~(BIT(NPCX_UFTCTL_TEMPTY_EN));
  }
  
  static int uart_npcx_irq_tx_ready(const struct device *dev)
  {
  	return uart_npcx_tx_fifo_ready(dev);
  }
  
  static int uart_npcx_irq_tx_complete(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	/* Tx FIFO is empty or last byte is sending */
  	return IS_BIT_SET(inst->UFTSTS, NPCX_UFTSTS_NXMIP);
  }
  
  static void uart_npcx_irq_rx_enable(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	inst->UFRCTL |= BIT(NPCX_UFRCTL_RNEMPTY_EN);
  }
  
  static void uart_npcx_irq_rx_disable(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	inst->UFRCTL &= ~(BIT(NPCX_UFRCTL_RNEMPTY_EN));
  }
  
  static int uart_npcx_irq_rx_ready(const struct device *dev)
  {
  	return uart_npcx_rx_fifo_available(dev);
  }
  
  static void uart_npcx_irq_err_enable(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	inst->UICTRL |= BIT(NPCX_UICTRL_EEI);
  }
  
  static void uart_npcx_irq_err_disable(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	inst->UICTRL &= ~(BIT(NPCX_UICTRL_EEI));
  }
  
  static int uart_npcx_irq_is_pending(const struct device *dev)
  {
  	return (uart_npcx_irq_tx_ready(dev) || uart_npcx_irq_rx_ready(dev));
  }
  
  static int uart_npcx_irq_update(const struct device *dev)
  {
  	ARG_UNUSED(dev);
  
  	return 1;
  }
  
  static void uart_npcx_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
  				       void *cb_data)
  {
  	struct uart_npcx_data *data = dev->data;
  
  	data->user_cb = cb;
  	data->user_data = cb_data;
  }
  
  static void uart_npcx_isr(const struct device *dev)
  {
  	struct uart_npcx_data *data = dev->data;
  
  	/*
  	 * Set pm constraint to prevent the system enter suspend state within
  	 * the CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT period.
  	 */
  #ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
  	if (uart_npcx_irq_rx_ready(dev)) {
  		k_timeout_t delay = K_MSEC(CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT);
  
  		uart_npcx_pm_constraint_set(data, UART_PM_CONSTRAINT_RX_FLAG);
  		k_work_reschedule(&data->rx_refresh_timeout_work, delay);
  	}
  #endif
  
  	if (data->user_cb) {
  		data->user_cb(dev, data->user_data);
  	}
  #ifdef CONFIG_PM
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	if (IS_BIT_SET(inst->UFTCTL, NPCX_UFTCTL_NXMIP_EN) &&
  	    IS_BIT_SET(inst->UFTSTS, NPCX_UFTSTS_NXMIP)) {
  		uart_npcx_pm_constraint_rel(data, UART_PM_CONSTRAINT_TX_FLAG);
  		inst->UFTCTL &= ~BIT(NPCX_UFTCTL_NXMIP_EN);
  	}
  #endif /* CONFIG_PM */
  }
  
  /*
   * Poll-in implementation for interrupt driven config, forward call to
   * uart_npcx_fifo_read().
   */
  static int uart_npcx_poll_in(const struct device *dev, unsigned char *c)
  {
  	return uart_npcx_fifo_read(dev, c, 1) ? 0 : -1;
  }
  
  /*
   * Poll-out implementation for interrupt driven config, forward call to
   * uart_npcx_fifo_fill().
   */
  static void uart_npcx_poll_out(const struct device *dev, unsigned char c)
  {
  	while (!uart_npcx_fifo_fill(dev, &c, 1))
  		continue;
  }
  
  #else /* !CONFIG_UART_INTERRUPT_DRIVEN */
  
  /*
   * Poll-in implementation for byte mode config, read byte from URBUF if
   * available.
   */
  static int uart_npcx_poll_in(const struct device *dev, unsigned char *c)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	/* Rx single byte buffer is not full */
  	if (!IS_BIT_SET(inst->UICTRL, NPCX_UICTRL_RBF))
  		return -1;
  
  	*c = inst->URBUF;
  	return 0;
  }
  
  /*
   * Poll-out implementation for byte mode config, write byte to UTBUF if empty.
   */
  static void uart_npcx_poll_out(const struct device *dev, unsigned char c)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  
  	/* Wait while Tx single byte buffer is ready to send */
  	while (!IS_BIT_SET(inst->UICTRL, NPCX_UICTRL_TBE))
  		continue;
  
  	inst->UTBUF = c;
  }
  #endif /* !CONFIG_UART_INTERRUPT_DRIVEN */
  
  /* UART api functions */
  static int uart_npcx_err_check(const struct device *dev)
  {
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  	uint32_t err = 0U;
  	uint8_t stat = inst->USTAT;
  
  	if (IS_BIT_SET(stat, NPCX_USTAT_DOE))
  		err |= UART_ERROR_OVERRUN;
  
  	if (IS_BIT_SET(stat, NPCX_USTAT_PE))
  		err |= UART_ERROR_PARITY;
  
  	if (IS_BIT_SET(stat, NPCX_USTAT_FE))
  		err |= UART_ERROR_FRAMING;
  
  	return err;
  }
  
  static __unused void uart_npcx_rx_wk_isr(const struct device *dev, struct npcx_wui *wui)
  {
  	/*
  	 * Set pm constraint to prevent the system enter suspend state within
  	 * the CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT period.
  	 */
  #ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
  	struct uart_npcx_data *data = dev->data;
  	k_timeout_t delay = K_MSEC(CONFIG_UART_CONSOLE_INPUT_EXPIRED_TIMEOUT);
  
  	uart_npcx_pm_constraint_set(data, UART_PM_CONSTRAINT_RX_FLAG);
  	k_work_reschedule(&data->rx_refresh_timeout_work, delay);
  #endif
  
  	/*
  	 * Disable MIWU CR_SIN interrupt to avoid the other redundant interrupts
  	 * after ec wakes up.
  	 */
  	npcx_uart_disable_access_interrupt();
  }
  
  #ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
  static void uart_npcx_rx_refresh_timeout(struct k_work *work)
  {
  	struct uart_npcx_data *data =
  		CONTAINER_OF(work, struct uart_npcx_data, rx_refresh_timeout_work);
  
  	uart_npcx_pm_constraint_rel(data, UART_PM_CONSTRAINT_RX_FLAG);
  }
  #endif
  
  /* UART driver registration */
  static const struct uart_driver_api uart_npcx_driver_api = {
  	.poll_in = uart_npcx_poll_in,
  	.poll_out = uart_npcx_poll_out,
  	.err_check = uart_npcx_err_check,
  #ifdef CONFIG_UART_INTERRUPT_DRIVEN
  	.fifo_fill = uart_npcx_fifo_fill,
  	.fifo_read = uart_npcx_fifo_read,
  	.irq_tx_enable = uart_npcx_irq_tx_enable,
  	.irq_tx_disable = uart_npcx_irq_tx_disable,
  	.irq_tx_ready = uart_npcx_irq_tx_ready,
  	.irq_tx_complete = uart_npcx_irq_tx_complete,
  	.irq_rx_enable = uart_npcx_irq_rx_enable,
  	.irq_rx_disable = uart_npcx_irq_rx_disable,
  	.irq_rx_ready = uart_npcx_irq_rx_ready,
  	.irq_err_enable = uart_npcx_irq_err_enable,
  	.irq_err_disable = uart_npcx_irq_err_disable,
  	.irq_is_pending = uart_npcx_irq_is_pending,
  	.irq_update = uart_npcx_irq_update,
  	.irq_callback_set = uart_npcx_irq_callback_set,
  #endif /* CONFIG_UART_INTERRUPT_DRIVEN */
  };
  
  static int uart_npcx_init(const struct device *dev)
  {
  	const struct uart_npcx_config *const config = dev->config;
  	struct uart_npcx_data *const data = dev->data;
  	struct uart_reg *const inst = HAL_INSTANCE(dev);
  	const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
  	uint32_t uart_rate;
  	int ret;
  
  	/* Turn on device clock first and get source clock freq. */
  	ret = clock_control_on(clk_dev, (clock_control_subsys_t *)&config->clk_cfg);
  	if (ret < 0) {
  		LOG_ERR("Turn on UART clock fail %d", ret);
  		return ret;
  	}
  
  	/*
  	 * If apb2's clock is not 15MHz, we need to find the other optimized
  	 * values of UPSR and UBAUD for baud rate 115200.
  	 */
  	ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t *)&config->clk_cfg,
  				     &uart_rate);
  	if (ret < 0) {
  		LOG_ERR("Get UART clock rate error %d", ret);
  		return ret;
  	}
  
  	/* Configure baud rate */
  	ret = uart_set_npcx_baud_rate(inst, data->baud_rate, uart_rate);
  	if (ret < 0) {
  		LOG_ERR("Set baud rate %d with unsupported apb clock %d failed", data->baud_rate,
  			uart_rate);
  		return ret;
  	}
  
  	/*
  	 * 8-N-1, FIFO enabled.  Must be done after setting
  	 * the divisor for the new divisor to take effect.
  	 */
  	inst->UFRS = 0x00;
  
  	/* Initialize UART FIFO if mode is interrupt driven */
  #ifdef CONFIG_UART_INTERRUPT_DRIVEN
  	/* Enable the UART FIFO mode */
  	inst->UMDSL |= BIT(NPCX_UMDSL_FIFO_MD);
  
  	/* Disable all UART tx FIFO interrupts */
  	uart_npcx_dis_all_tx_interrupts(dev);
  
  	/* Clear UART rx FIFO */
  	uart_npcx_clear_rx_fifo(dev);
  
  	/* Configure UART interrupts */
  	config->uconf.irq_config_func(dev);
  #endif
  
  	if (IS_ENABLED(CONFIG_PM)) {
  		/* Initialize a miwu device input and its callback function */
  		npcx_miwu_init_dev_callback(&data->uart_rx_cb, &config->uart_rx_wui,
  					    uart_npcx_rx_wk_isr, dev);
  		npcx_miwu_manage_dev_callback(&data->uart_rx_cb, true);
  		/*
  		 * Configure the UART wake-up event triggered from a falling
  		 * edge on CR_SIN pin. No need for callback function.
  		 */
  		npcx_miwu_interrupt_configure(&config->uart_rx_wui, NPCX_MIWU_MODE_EDGE,
  					      NPCX_MIWU_TRIG_LOW);
  
  #ifdef CONFIG_UART_CONSOLE_INPUT_EXPIRED
  		k_work_init_delayable(&data->rx_refresh_timeout_work, uart_npcx_rx_refresh_timeout);
  #endif
  	}
  
  	/* Configure pin-mux for uart device */
  	npcx_pinctrl_mux_configure(config->alts_list, config->alts_size, 1);
  
  	return 0;
  }
  
  #ifdef CONFIG_UART_INTERRUPT_DRIVEN
  #define NPCX_UART_IRQ_CONFIG_FUNC_DECL(inst)                                                       \
  	static void uart_npcx_irq_config_##inst(const struct device *dev)
  #define NPCX_UART_IRQ_CONFIG_FUNC_INIT(inst) .irq_config_func = uart_npcx_irq_config_##inst,
  #define NPCX_UART_IRQ_CONFIG_FUNC(inst)                                                            \
  	static void uart_npcx_irq_config_##inst(const struct device *dev)                          \
  	{                                                                                          \
  		IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), uart_npcx_isr,        \
  			    DEVICE_DT_INST_GET(inst), 0);                                          \
  		irq_enable(DT_INST_IRQN(inst));                                                    \
  	}
  #else
  #define NPCX_UART_IRQ_CONFIG_FUNC_DECL(inst)
  #define NPCX_UART_IRQ_CONFIG_FUNC_INIT(inst)
  #define NPCX_UART_IRQ_CONFIG_FUNC(inst)
  #endif
  
  #define NPCX_UART_INIT(inst)                                                                       \
  	NPCX_UART_IRQ_CONFIG_FUNC_DECL(inst);                                                      \
  												   \
  	static const struct npcx_alt uart_alts##inst[] = NPCX_DT_ALT_ITEMS_LIST(inst);             \
  												   \
  	static const struct uart_npcx_config uart_npcx_cfg_##inst = {                              \
  		.uconf = { .base = (uint8_t *)DT_INST_REG_ADDR(inst),                              \
  			   NPCX_UART_IRQ_CONFIG_FUNC_INIT(inst) },                                 \
  		.clk_cfg = NPCX_DT_CLK_CFG_ITEM(inst),                                             \
  		.uart_rx_wui = NPCX_DT_WUI_ITEM_BY_NAME(0, uart_rx),                               \
  		.alts_size = ARRAY_SIZE(uart_alts##inst),                                          \
  		.alts_list = uart_alts##inst,                                                      \
  	};                                                                                         \
  												   \
  	static struct uart_npcx_data uart_npcx_data_##inst = { .baud_rate = DT_INST_PROP(          \
  								       inst, current_speed) };     \
  												   \
  	DEVICE_DT_INST_DEFINE(inst, &uart_npcx_init, NULL, &uart_npcx_data_##inst,                 \
  			      &uart_npcx_cfg_##inst, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY,    \
  			      &uart_npcx_driver_api);                                              \
  												   \
  	NPCX_UART_IRQ_CONFIG_FUNC(inst)
  
  DT_INST_FOREACH_STATUS_OKAY(NPCX_UART_INIT)
  
  #define ENABLE_MIWU_CRIN_IRQ(inst)                                                                 \
  	npcx_miwu_irq_get_and_clear_pending(&uart_npcx_cfg_##inst.uart_rx_wui);                    \
  	npcx_miwu_irq_enable(&uart_npcx_cfg_##inst.uart_rx_wui);
  
  #define DISABLE_MIWU_CRIN_IRQ(inst) npcx_miwu_irq_disable(&uart_npcx_cfg_##inst.uart_rx_wui);
  
  void npcx_uart_enable_access_interrupt(void)
  {
  	DT_INST_FOREACH_STATUS_OKAY(ENABLE_MIWU_CRIN_IRQ)
  }
  
  void npcx_uart_disable_access_interrupt(void)
  {
  	DT_INST_FOREACH_STATUS_OKAY(DISABLE_MIWU_CRIN_IRQ)
  }
  