1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright (c) 2016, Linaro Limited
4 */
5 #include <assert.h>
6 #include <drivers/hi16xx_uart.h>
7 #include <io.h>
8 #include <keep.h>
9 #include <mm/core_mmu.h>
10 #include <util.h>
11
12 /* Register offsets */
13
14 #define UART_RBR 0x00 /* RX data buffer register */
15 #define UART_THR 0x00 /* TX data buffer register */
16 #define UART_DLL 0x00 /* Lower-bit frequency divider register */
17
18 #define UART_IEL 0x04 /* Interrupt enable register */
19 #define UART_DLH 0x04 /* Upper-bit frequency divider register */
20
21 #define UART_FCR 0x08 /* FIFO control register */
22
23 #define UART_LCR 0x0C /* Line control register */
24
25 #define UART_LSR 0x14 /* Line status register */
26
27 #define UART_USR 0x7C /* Status register */
28
29 /*
30 * Line control register
31 */
32
33 /* Data length selection */
34 #define UART_LCR_DLS5 0x0 /* 5 bits */
35 #define UART_LCR_DLS6 0x1 /* 6 bits */
36 #define UART_LCR_DLS7 0x2 /* 7 bits */
37 #define UART_LCR_DLS8 0x3 /* 8 bits */
38
39 /* Enable access to UART_DLL and UART_DLH */
40 #define UART_LCR_DLAB 0x80
41
42 /*
43 * FIFO control register
44 */
45
46 #define UART_FCR_FIFO_EN 0x1 /* Enable FIFO (depth: 32 bytes) */
47 #define UART_FCR_RX_FIFO_RST 0x2 /* Clear receive FIFO (auto reset) */
48 #define UART_FCR_TX_FIFO_RST 0x4 /* Clear send FIFO (auto reset) */
49
50
51 /*
52 * Status register
53 */
54
55 #define UART_USR_BUSY_BIT 0 /* 0: idle/non-activated, 1: busy */
56 #define UART_USR_TFNF_BIT 1 /* Transmit FIFO not full bit */
57 #define UART_USR_TFE_BIT 2 /* Transmit FIFO empty bit */
58 #define UART_USR_RFNE_BIT 3 /* Receive FIFO not empty bit */
59 #define UART_USR_RFF_BIT 4 /* Receive FIFO full bit */
60
chip_to_base(struct serial_chip * chip)61 static vaddr_t chip_to_base(struct serial_chip *chip)
62 {
63 struct hi16xx_uart_data *pd =
64 container_of(chip, struct hi16xx_uart_data, chip);
65
66 return io_pa_or_va(&pd->base, HI16XX_UART_REG_SIZE);
67 }
68
hi16xx_uart_flush(struct serial_chip * chip)69 static void hi16xx_uart_flush(struct serial_chip *chip)
70 {
71 vaddr_t base = chip_to_base(chip);
72
73 while (!(io_read32(base + UART_USR) & UART_USR_TFE_BIT))
74 ;
75 }
76
hi16xx_uart_putc(struct serial_chip * chip,int ch)77 static void hi16xx_uart_putc(struct serial_chip *chip, int ch)
78 {
79 vaddr_t base = chip_to_base(chip);
80
81 /* Wait until TX FIFO is empty */
82 while (!(io_read32(base + UART_USR) & UART_USR_TFE_BIT))
83 ;
84
85 /* Put character into TX FIFO */
86 io_write32(base + UART_THR, ch & 0xFF);
87 }
88
hi16xx_uart_have_rx_data(struct serial_chip * chip)89 static bool hi16xx_uart_have_rx_data(struct serial_chip *chip)
90 {
91 vaddr_t base = chip_to_base(chip);
92
93 return (io_read32(base + UART_USR) & UART_USR_RFNE_BIT);
94 }
95
hi16xx_uart_getchar(struct serial_chip * chip)96 static int hi16xx_uart_getchar(struct serial_chip *chip)
97 {
98 vaddr_t base = chip_to_base(chip);
99
100 while (!hi16xx_uart_have_rx_data(chip))
101 ;
102 return io_read32(base + UART_RBR) & 0xFF;
103 }
104
105 static const struct serial_ops hi16xx_uart_ops = {
106 .flush = hi16xx_uart_flush,
107 .getchar = hi16xx_uart_getchar,
108 .have_rx_data = hi16xx_uart_have_rx_data,
109 .putc = hi16xx_uart_putc,
110 };
111 DECLARE_KEEP_PAGER(hi16xx_uart_ops);
112
hi16xx_uart_init(struct hi16xx_uart_data * pd,paddr_t base,uint32_t uart_clk,uint32_t baud_rate)113 void hi16xx_uart_init(struct hi16xx_uart_data *pd, paddr_t base,
114 uint32_t uart_clk, uint32_t baud_rate)
115 {
116 uint16_t freq_div = uart_clk / (16 * baud_rate);
117
118 pd->base.pa = base;
119 pd->chip.ops = &hi16xx_uart_ops;
120
121 /* Enable (and clear) FIFOs */
122 io_write32(base + UART_FCR, UART_FCR_FIFO_EN);
123
124 /* Enable access to _DLL and _DLH */
125 io_write32(base + UART_LCR, UART_LCR_DLAB);
126
127 /* Calculate and set UART_DLL */
128 io_write32(base + UART_DLL, freq_div & 0xFF);
129
130 /* Calculate and set UART_DLH */
131 io_write32(base + UART_DLH, (freq_div >> 8) & 0xFF);
132
133 /* Clear _DLL/_DLH access bit, set data size (8 bits), parity etc. */
134 io_write32(base + UART_LCR, UART_LCR_DLS8);
135
136 /* Disable interrupt mode */
137 io_write32(base + UART_IEL, 0);
138
139 hi16xx_uart_flush(&pd->chip);
140 }
141
142