1 /*
2 * Copyright (c) 2021 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_common.h"
9 #include "hpm_uart_drv.h"
10
11 #define HPM_UART_DRV_RETRY_COUNT (5000U)
12 #define HPM_UART_MINIMUM_BAUDRATE (200U)
13
14 #ifndef HPM_UART_BAUDRATE_TOLERANCE
15 #define HPM_UART_BAUDRATE_TOLERANCE (3)
16 #endif
17
18 #define HPM_UART_OSC_MAX (32U)
19 #define HPM_UART_OSC_MIN (8U)
20 #define HPM_UART_BAUDRATE_DIV_MAX (0xFFFFU)
21 #define HPM_UART_BAUDRATE_DIV_MIN (1U)
22
uart_default_config(UART_Type * ptr,uart_config_t * config)23 void uart_default_config(UART_Type *ptr, uart_config_t *config)
24 {
25 config->baudrate = 115200;
26 config->word_length = word_length_8_bits;
27 config->parity = parity_none;
28 config->num_of_stop_bits = stop_bits_1;
29 config->fifo_enable = true;
30 config->rx_fifo_level = uart_rx_fifo_trg_not_empty;
31 config->tx_fifo_level = uart_tx_fifo_trg_not_full;
32 config->dma_enable = false;
33 config->modem_config.auto_flow_ctrl_en = false;
34 config->modem_config.loop_back_en = false;
35 config->modem_config.set_rts_high = false;
36 }
37
uart_calculate_baudrate(uint32_t freq,uint32_t baudrate,uint16_t * div_out,uint8_t * osc_out)38 static bool uart_calculate_baudrate(uint32_t freq, uint32_t baudrate, uint16_t *div_out, uint8_t *osc_out)
39 {
40 uint16_t div, osc, delta;
41 float tmp;
42 if ((div_out == NULL) || (!freq) || (!baudrate)
43 || (baudrate < HPM_UART_MINIMUM_BAUDRATE)
44 || (freq / HPM_UART_BAUDRATE_DIV_MIN < baudrate * HPM_UART_OSC_MIN)
45 || (freq / HPM_UART_BAUDRATE_DIV_MAX > (baudrate * HPM_UART_OSC_MAX))) {
46 return 0;
47 }
48
49 tmp = (float) freq / baudrate;
50
51 for (uint8_t i = 0; i < HPM_UART_OSC_MAX; i += 2) {
52 /* osc range: 0 - 32, even number */
53 if (i == 0) {
54 /* osc == 0 in bitfield, oversample rate is 32 */
55 osc = HPM_UART_OSC_MAX;
56 } else if (i <= 8) {
57 /* osc <= 8 in bitfield, oversample rate is 8 */
58 osc = HPM_UART_OSC_MIN;
59 } else {
60 /* osc > 8 && osc < 32 in bitfield, oversample rate is osc */
61 osc = i;
62 }
63 delta = 0;
64 div = (uint16_t)(tmp / osc);
65 if (div < HPM_UART_BAUDRATE_DIV_MIN) {
66 /* invalid div */
67 continue;
68 }
69 if (div * osc > tmp) {
70 delta = div * osc - tmp;
71 } else if (div * osc < tmp) {
72 delta = tmp - div * osc;
73 }
74 if (delta && ((delta * 100 / tmp) > HPM_UART_BAUDRATE_TOLERANCE)) {
75 continue;
76 } else {
77 *div_out = div;
78 *osc_out = (i <= 8 && i) ? osc : i;
79 return true;
80 }
81 }
82 return false;
83 }
84
uart_init(UART_Type * ptr,uart_config_t * config)85 hpm_stat_t uart_init(UART_Type *ptr, uart_config_t *config)
86 {
87 uint32_t tmp;
88 uint8_t osc;
89 uint16_t div;
90
91 /* disable all interrupts */
92 ptr->IER = 0;
93 /* Set DLAB to 1 */
94 ptr->LCR |= UART_LCR_DLAB_MASK;
95
96 if (!uart_calculate_baudrate(config->src_freq_in_hz, config->baudrate, &div, &osc)) {
97 return status_uart_no_suitable_baudrate_parameter_found;
98 }
99 ptr->OSCR = (ptr->OSCR & ~UART_OSCR_OSC_MASK)
100 | UART_OSCR_OSC_SET(osc);
101 ptr->DLL = UART_DLL_DLL_SET(div >> 0);
102 ptr->DLM = UART_DLM_DLM_SET(div >> 8);
103
104 /* DLAB bit needs to be cleared once baudrate is configured */
105 tmp = ptr->LCR & (~UART_LCR_DLAB_MASK);
106
107 tmp &= ~(UART_LCR_SPS_MASK | UART_LCR_EPS_MASK | UART_LCR_PEN_MASK);
108 switch (config->parity) {
109 case parity_none:
110 break;
111 case parity_odd:
112 tmp |= UART_LCR_PEN_MASK;
113 break;
114 case parity_even:
115 tmp |= UART_LCR_PEN_MASK | UART_LCR_EPS_MASK;
116 break;
117 case parity_always_1:
118 tmp |= UART_LCR_PEN_MASK | UART_LCR_SPS_MASK;
119 break;
120 case parity_always_0:
121 tmp |= UART_LCR_EPS_MASK | UART_LCR_PEN_MASK
122 | UART_LCR_SPS_MASK;
123 break;
124 default:
125 /* invalid configuration */
126 return status_invalid_argument;
127 }
128
129 tmp &= ~(UART_LCR_STB_MASK | UART_LCR_WLS_MASK);
130 switch (config->num_of_stop_bits) {
131 case stop_bits_1:
132 break;
133 case stop_bits_1_5:
134 tmp |= UART_LCR_STB_MASK;
135 break;
136 case stop_bits_2:
137 if (config->word_length < word_length_6_bits) {
138 /* invalid configuration */
139 return status_invalid_argument;
140 }
141 tmp |= UART_LCR_STB_MASK;
142 break;
143 default:
144 /* invalid configuration */
145 return status_invalid_argument;
146 }
147
148 ptr->LCR = tmp | UART_LCR_WLS_SET(config->word_length);
149
150 ptr->FCR = UART_FCR_TFIFORST_MASK | UART_FCR_RFIFORST_MASK;
151 if (config->fifo_enable) {
152 /* Enable FIFO, reset TX and RX. */
153 ptr->FCR = UART_FCR_TFIFORST_MASK
154 | UART_FCR_RFIFORST_MASK | UART_FCR_FIFOE_MASK
155 | UART_FCR_TFIFOT_SET(config->tx_fifo_level)
156 | UART_FCR_RFIFOT_SET(config->rx_fifo_level)
157 | UART_FCR_DMAE_SET(config->dma_enable);
158 }
159
160 uart_modem_config(ptr, &config->modem_config);
161 return status_success;
162 }
163
uart_set_baudrate(UART_Type * ptr,uint32_t baudrate,uint32_t src_clock_hz)164 hpm_stat_t uart_set_baudrate(UART_Type *ptr, uint32_t baudrate, uint32_t src_clock_hz)
165 {
166 uint8_t osc;
167 uint16_t div;
168
169 /* Set DLAB to 1 */
170 ptr->LCR |= UART_LCR_DLAB_MASK;
171
172 if (!uart_calculate_baudrate(src_clock_hz, baudrate, &div, &osc)) {
173 return status_uart_no_suitable_baudrate_parameter_found;
174 }
175
176 ptr->OSCR = (ptr->OSCR & ~UART_OSCR_OSC_MASK) | UART_OSCR_OSC_SET(osc);
177 ptr->DLL = UART_DLL_DLL_SET(div >> 0);
178 ptr->DLM = UART_DLM_DLM_SET(div >> 8);
179
180 /* DLAB bit needs to be cleared once baudrate is configured */
181 ptr->LCR &= ~UART_LCR_DLAB_MASK;
182
183 return status_success;
184 }
185
uart_send_byte(UART_Type * ptr,uint8_t c)186 hpm_stat_t uart_send_byte(UART_Type *ptr, uint8_t c)
187 {
188 uint32_t retry = 0;
189
190 while (!(ptr->LSR & UART_LSR_THRE_MASK)) {
191 if (retry > HPM_UART_DRV_RETRY_COUNT) {
192 break;
193 }
194 retry++;
195 }
196
197 if (retry > HPM_UART_DRV_RETRY_COUNT) {
198 return status_timeout;
199 }
200
201 ptr->THR = UART_THR_THR_SET(c);
202 return status_success;
203 }
204
uart_flush(UART_Type * ptr)205 hpm_stat_t uart_flush(UART_Type *ptr)
206 {
207 uint32_t retry = 0;
208
209 while (!(ptr->LSR & UART_LSR_TEMT_MASK)) {
210 if (retry > HPM_UART_DRV_RETRY_COUNT) {
211 break;
212 }
213 retry++;
214 }
215 if (retry > HPM_UART_DRV_RETRY_COUNT) {
216 return status_timeout;
217 }
218
219 return status_success;
220 }
221
uart_receive_byte(UART_Type * ptr,uint8_t * byte)222 hpm_stat_t uart_receive_byte(UART_Type *ptr, uint8_t *byte)
223 {
224 uint32_t retry = 0;
225
226 while (!(ptr->LSR & UART_LSR_DR_MASK)) {
227 if (retry > HPM_UART_DRV_RETRY_COUNT) {
228 break;
229 }
230 retry++;
231 }
232
233 if (retry > HPM_UART_DRV_RETRY_COUNT) {
234 return status_timeout;
235 }
236
237 *byte = ptr->RBR & UART_RBR_RBR_MASK;
238 return status_success;
239 }
240
uart_set_signal_level(UART_Type * ptr,uart_signal_t signal,uart_signal_level_t level)241 void uart_set_signal_level(UART_Type *ptr, uart_signal_t signal, uart_signal_level_t level)
242 {
243 if (level == uart_signal_level_low) {
244 ptr->MCR = (ptr->MCR | signal);
245 } else {
246 ptr->MCR = (ptr->MCR & ~signal);
247 }
248 }
249
uart_receive_data(UART_Type * ptr,uint8_t * source,uint32_t size_in_byte)250 hpm_stat_t uart_receive_data(UART_Type *ptr, uint8_t *source, uint32_t size_in_byte)
251 {
252 for (uint32_t i = 0; i < size_in_byte; i++) {
253 if (status_success != uart_receive_byte(ptr, source + i)) {
254 return status_fail;
255 }
256 }
257 return status_success;
258 }
259
uart_send_data(UART_Type * ptr,uint8_t * source,uint32_t size_in_byte)260 hpm_stat_t uart_send_data(UART_Type *ptr, uint8_t *source, uint32_t size_in_byte)
261 {
262 for (uint32_t i = 0; i < size_in_byte; i++) {
263 if (status_success != uart_send_byte(ptr, *(source + i))) {
264 return status_fail;
265 }
266 }
267 return status_success;
268 }
269