1 /*
2  * Copyright (c) 2023 by Rivos Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/arch/cpu.h>
9 #include <zephyr/drivers/uart.h>
10 #include <zephyr/irq.h>
11 
12 /* Register offsets within the UART device register space. */
13 #define UART_INTR_STATE_REG_OFFSET 0x0
14 #define UART_INTR_ENABLE_REG_OFFSET 0x4
15 #define UART_CTRL_REG_OFFSET 0x10
16 #define UART_STATUS_REG_OFFSET 0x14
17 #define UART_RDATA_REG_OFFSET 0x18
18 #define UART_WDATA_REG_OFFSET 0x1c
19 #define UART_FIFO_CTRL_REG_OFFSET 0x20
20 #define UART_OVRD_REG_OFFSET 0x28
21 #define UART_TIMEOUT_CTRL_REG_OFFSET 0x30
22 
23 /* Control register bits. */
24 #define UART_CTRL_TX_BIT BIT(0)
25 #define UART_CTRL_RX_BIT BIT(1)
26 #define UART_CTRL_NCO_OFFSET 16
27 
28 /* FIFO control register bits. */
29 #define UART_FIFO_CTRL_RXRST_BIT BIT(0)
30 #define UART_FIFO_CTRL_TXRST_BIT BIT(1)
31 
32 /* Status register bits. */
33 #define UART_STATUS_TXFULL_BIT BIT(0)
34 #define UART_STATUS_RXEMPTY_BIT BIT(5)
35 
36 #define DT_DRV_COMPAT lowrisc_opentitan_uart
37 
38 struct uart_opentitan_config {
39 	mem_addr_t base;
40 	uint32_t nco_reg;
41 };
42 
uart_opentitan_init(const struct device * dev)43 static int uart_opentitan_init(const struct device *dev)
44 {
45 	const struct uart_opentitan_config *cfg = dev->config;
46 
47 	/* Reset settings. */
48 	sys_write32(0u, cfg->base + UART_CTRL_REG_OFFSET);
49 
50 	/* Clear FIFOs. */
51 	sys_write32(UART_FIFO_CTRL_RXRST_BIT | UART_FIFO_CTRL_TXRST_BIT,
52 		    cfg->base + UART_FIFO_CTRL_REG_OFFSET);
53 
54 	/* Clear other states. */
55 	sys_write32(0u, cfg->base + UART_OVRD_REG_OFFSET);
56 	sys_write32(0u, cfg->base + UART_TIMEOUT_CTRL_REG_OFFSET);
57 
58 	/* Disable interrupts. */
59 	sys_write32(0u, cfg->base + UART_INTR_ENABLE_REG_OFFSET);
60 
61 	/* Clear interrupts. */
62 	sys_write32(0xffffffffu, cfg->base + UART_INTR_STATE_REG_OFFSET);
63 
64 	/* Set baud and enable TX and RX. */
65 	sys_write32(UART_CTRL_TX_BIT | UART_CTRL_RX_BIT |
66 		(cfg->nco_reg << UART_CTRL_NCO_OFFSET),
67 		cfg->base + UART_CTRL_REG_OFFSET);
68 	return 0;
69 }
70 
uart_opentitan_poll_in(const struct device * dev,unsigned char * c)71 static int uart_opentitan_poll_in(const struct device *dev, unsigned char *c)
72 {
73 	const struct uart_opentitan_config *cfg = dev->config;
74 
75 	if (sys_read32(cfg->base + UART_STATUS_REG_OFFSET) & UART_STATUS_RXEMPTY_BIT) {
76 	    /* Empty RX FIFO */
77 		return -1;
78 	}
79 	*c = sys_read32(cfg->base + UART_RDATA_REG_OFFSET);
80 	return 0;
81 }
82 
uart_opentitan_poll_out(const struct device * dev,unsigned char c)83 static void uart_opentitan_poll_out(const struct device *dev, unsigned char c)
84 {
85 	const struct uart_opentitan_config *cfg = dev->config;
86 
87 	/* Wait for space in the TX FIFO */
88 	while (sys_read32(cfg->base + UART_STATUS_REG_OFFSET) &
89 		UART_STATUS_TXFULL_BIT) {
90 		;
91 	}
92 
93 	sys_write32(c, cfg->base + UART_WDATA_REG_OFFSET);
94 }
95 
96 static DEVICE_API(uart, uart_opentitan_driver_api) = {
97 	.poll_in = uart_opentitan_poll_in,
98 	.poll_out = uart_opentitan_poll_out,
99 };
100 
101 /* The baud rate is set by writing to the CTRL.NCO register, which is
102  * calculated based on baud ticks per system clock tick multiplied by a
103  * predefined scaler value.
104  */
105 #define NCO_REG(baud, clk) (BIT64(20) * (baud) / (clk))
106 
107 #define UART_OPENTITAN_INIT(n) \
108 	static struct uart_opentitan_config uart_opentitan_config_##n = \
109 	{ \
110 		.base = DT_INST_REG_ADDR(n), \
111 		.nco_reg = NCO_REG(DT_INST_PROP(n, current_speed), \
112 			DT_INST_PROP(n, clock_frequency)), \
113 	}; \
114 	\
115 	DEVICE_DT_INST_DEFINE(n, uart_opentitan_init, NULL, NULL, \
116 				&uart_opentitan_config_##n, \
117 				PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \
118 				&uart_opentitan_driver_api);
119 
120 DT_INST_FOREACH_STATUS_OKAY(UART_OPENTITAN_INIT)
121