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