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
23 #ifndef UART_SOC_OVERSAMPLE_MAX
24 #define UART_SOC_OVERSAMPLE_MAX HPM_UART_OSC_MAX
25 #endif
26
uart_default_config(UART_Type * ptr,uart_config_t * config)27 void uart_default_config(UART_Type *ptr, uart_config_t *config)
28 {
29 (void) ptr;
30 config->baudrate = 115200;
31 config->word_length = word_length_8_bits;
32 config->parity = parity_none;
33 config->num_of_stop_bits = stop_bits_1;
34 config->fifo_enable = true;
35 config->rx_fifo_level = uart_rx_fifo_trg_not_empty;
36 config->tx_fifo_level = uart_tx_fifo_trg_not_full;
37 config->dma_enable = false;
38 config->modem_config.auto_flow_ctrl_en = false;
39 config->modem_config.loop_back_en = false;
40 config->modem_config.set_rts_high = false;
41 #if defined(UART_SOC_HAS_RXLINE_IDLE_DETECTION) && (UART_SOC_HAS_RXLINE_IDLE_DETECTION == 1)
42 config->rxidle_config.detect_enable = false;
43 config->rxidle_config.detect_irq_enable = false;
44 config->rxidle_config.idle_cond = uart_rxline_idle_cond_rxline_logic_one;
45 config->rxidle_config.threshold = 10; /* 10-bit for typical UART configuration (8-N-1) */
46 #endif
47 #if defined(UART_SOC_HAS_TXLINE_IDLE_DETECTION) && (UART_SOC_HAS_TXLINE_IDLE_DETECTION == 1)
48 config->txidle_config.detect_enable = false;
49 config->txidle_config.detect_irq_enable = false;
50 config->txidle_config.idle_cond = uart_rxline_idle_cond_rxline_logic_one;
51 config->txidle_config.threshold = 10; /* 10-bit for typical UART configuration (8-N-1) */
52 #endif
53 #if defined(UART_SOC_HAS_RXEN_CFG) && (UART_SOC_HAS_RXEN_CFG == 1)
54 config->rx_enable = true;
55 #endif
56 }
57
uart_calculate_baudrate(uint32_t freq,uint32_t baudrate,uint16_t * div_out,uint8_t * osc_out)58 static bool uart_calculate_baudrate(uint32_t freq, uint32_t baudrate, uint16_t *div_out, uint8_t *osc_out)
59 {
60 uint16_t div, osc, delta;
61 float tmp;
62 if ((div_out == NULL) || (!freq) || (!baudrate)
63 || (baudrate < HPM_UART_MINIMUM_BAUDRATE)
64 || (freq / HPM_UART_BAUDRATE_DIV_MIN < baudrate * HPM_UART_OSC_MIN)
65 || (freq / HPM_UART_BAUDRATE_DIV_MAX > (baudrate * HPM_UART_OSC_MAX))) {
66 return 0;
67 }
68
69 tmp = (float) freq / baudrate;
70
71 for (osc = HPM_UART_OSC_MIN; osc <= UART_SOC_OVERSAMPLE_MAX; osc += 2) {
72 /* osc range: HPM_UART_OSC_MIN - UART_SOC_OVERSAMPLE_MAX, even number */
73 delta = 0;
74 div = (uint16_t)(tmp / osc);
75 if (div < HPM_UART_BAUDRATE_DIV_MIN) {
76 /* invalid div */
77 continue;
78 }
79 if (div * osc > tmp) {
80 delta = div * osc - tmp;
81 } else if (div * osc < tmp) {
82 delta = tmp - div * osc;
83 }
84 if (delta && ((delta * 100 / tmp) > HPM_UART_BAUDRATE_TOLERANCE)) {
85 continue;
86 } else {
87 *div_out = div;
88 *osc_out = (osc == HPM_UART_OSC_MAX) ? 0 : osc; /* osc == 0 in bitfield, oversample rate is 32 */
89 return true;
90 }
91 }
92 return false;
93 }
94
uart_init(UART_Type * ptr,uart_config_t * config)95 hpm_stat_t uart_init(UART_Type *ptr, uart_config_t *config)
96 {
97 uint32_t tmp;
98 uint8_t osc;
99 uint16_t div;
100
101 /* disable all interrupts */
102 ptr->IER = 0;
103 /* Set DLAB to 1 */
104 ptr->LCR |= UART_LCR_DLAB_MASK;
105
106 if (!uart_calculate_baudrate(config->src_freq_in_hz, config->baudrate, &div, &osc)) {
107 return status_uart_no_suitable_baudrate_parameter_found;
108 }
109
110 ptr->OSCR = (ptr->OSCR & ~UART_OSCR_OSC_MASK)
111 | UART_OSCR_OSC_SET(osc);
112 ptr->DLL = UART_DLL_DLL_SET(div >> 0);
113 ptr->DLM = UART_DLM_DLM_SET(div >> 8);
114
115 /* DLAB bit needs to be cleared once baudrate is configured */
116 tmp = ptr->LCR & (~UART_LCR_DLAB_MASK);
117
118 tmp &= ~(UART_LCR_SPS_MASK | UART_LCR_EPS_MASK | UART_LCR_PEN_MASK);
119 switch (config->parity) {
120 case parity_none:
121 break;
122 case parity_odd:
123 tmp |= UART_LCR_PEN_MASK;
124 break;
125 case parity_even:
126 tmp |= UART_LCR_PEN_MASK | UART_LCR_EPS_MASK;
127 break;
128 case parity_always_1:
129 tmp |= UART_LCR_PEN_MASK | UART_LCR_SPS_MASK;
130 break;
131 case parity_always_0:
132 tmp |= UART_LCR_EPS_MASK | UART_LCR_PEN_MASK
133 | UART_LCR_SPS_MASK;
134 break;
135 default:
136 /* invalid configuration */
137 return status_invalid_argument;
138 }
139
140 tmp &= ~(UART_LCR_STB_MASK | UART_LCR_WLS_MASK);
141 switch (config->num_of_stop_bits) {
142 case stop_bits_1:
143 break;
144 case stop_bits_1_5:
145 tmp |= UART_LCR_STB_MASK;
146 break;
147 case stop_bits_2:
148 if (config->word_length < word_length_6_bits) {
149 /* invalid configuration */
150 return status_invalid_argument;
151 }
152 tmp |= UART_LCR_STB_MASK;
153 break;
154 default:
155 /* invalid configuration */
156 return status_invalid_argument;
157 }
158
159 ptr->LCR = tmp | UART_LCR_WLS_SET(config->word_length);
160
161 #if defined(UART_SOC_HAS_FINE_FIFO_THR) && (UART_SOC_HAS_FINE_FIFO_THR == 1)
162 /* reset TX and RX fifo */
163 ptr->FCRR = UART_FCRR_TFIFORST_MASK | UART_FCRR_RFIFORST_MASK;
164 /* Enable FIFO */
165 ptr->FCRR = UART_FCRR_FIFOT4EN_MASK
166 | UART_FCRR_FIFOE_SET(config->fifo_enable)
167 | UART_FCRR_TFIFOT4_SET(config->tx_fifo_level)
168 | UART_FCRR_RFIFOT4_SET(config->rx_fifo_level)
169 | UART_FCRR_DMAE_SET(config->dma_enable);
170
171 #else
172 /* reset TX and RX fifo */
173 ptr->FCR = UART_FCR_TFIFORST_MASK | UART_FCR_RFIFORST_MASK;
174 /* Enable FIFO */
175 tmp = UART_FCR_FIFOE_SET(config->fifo_enable)
176 | UART_FCR_TFIFOT_SET(config->tx_fifo_level)
177 | UART_FCR_RFIFOT_SET(config->rx_fifo_level)
178 | UART_FCR_DMAE_SET(config->dma_enable);
179 ptr->FCR = tmp;
180 /* store FCR register value */
181 ptr->GPR = tmp;
182 #endif
183
184 uart_modem_config(ptr, &config->modem_config);
185
186 #if defined(UART_SOC_HAS_RXLINE_IDLE_DETECTION) && (UART_SOC_HAS_RXLINE_IDLE_DETECTION == 1)
187 uart_init_rxline_idle_detection(ptr, config->rxidle_config);
188 #endif
189 #if defined(UART_SOC_HAS_RXEN_CFG) && (UART_SOC_HAS_RXEN_CFG == 1)
190 if (config->rx_enable) {
191 ptr->IDLE_CFG |= UART_IDLE_CFG_RXEN_MASK;
192 }
193 #endif
194 return status_success;
195 }
196
uart_set_baudrate(UART_Type * ptr,uint32_t baudrate,uint32_t src_clock_hz)197 hpm_stat_t uart_set_baudrate(UART_Type *ptr, uint32_t baudrate, uint32_t src_clock_hz)
198 {
199 uint8_t osc;
200 uint16_t div;
201
202 /* Set DLAB to 1 */
203 ptr->LCR |= UART_LCR_DLAB_MASK;
204
205 if (!uart_calculate_baudrate(src_clock_hz, baudrate, &div, &osc)) {
206 return status_uart_no_suitable_baudrate_parameter_found;
207 }
208
209 ptr->OSCR = (ptr->OSCR & ~UART_OSCR_OSC_MASK) | UART_OSCR_OSC_SET(osc);
210 ptr->DLL = UART_DLL_DLL_SET(div >> 0);
211 ptr->DLM = UART_DLM_DLM_SET(div >> 8);
212
213 /* DLAB bit needs to be cleared once baudrate is configured */
214 ptr->LCR &= ~UART_LCR_DLAB_MASK;
215
216 return status_success;
217 }
218
uart_send_byte(UART_Type * ptr,uint8_t c)219 hpm_stat_t uart_send_byte(UART_Type *ptr, uint8_t c)
220 {
221 uint32_t retry = 0;
222
223 while (!(ptr->LSR & UART_LSR_THRE_MASK)) {
224 if (retry > HPM_UART_DRV_RETRY_COUNT) {
225 break;
226 }
227 retry++;
228 }
229
230 if (retry > HPM_UART_DRV_RETRY_COUNT) {
231 return status_timeout;
232 }
233
234 ptr->THR = UART_THR_THR_SET(c);
235 return status_success;
236 }
237
uart_flush(UART_Type * ptr)238 hpm_stat_t uart_flush(UART_Type *ptr)
239 {
240 uint32_t retry = 0;
241
242 while (!(ptr->LSR & UART_LSR_TEMT_MASK)) {
243 if (retry > HPM_UART_DRV_RETRY_COUNT) {
244 break;
245 }
246 retry++;
247 }
248 if (retry > HPM_UART_DRV_RETRY_COUNT) {
249 return status_timeout;
250 }
251
252 return status_success;
253 }
254
uart_receive_byte(UART_Type * ptr,uint8_t * byte)255 hpm_stat_t uart_receive_byte(UART_Type *ptr, uint8_t *byte)
256 {
257 uint32_t retry = 0;
258
259 while (!(ptr->LSR & UART_LSR_DR_MASK)) {
260 if (retry > HPM_UART_DRV_RETRY_COUNT) {
261 break;
262 }
263 retry++;
264 }
265
266 if (retry > HPM_UART_DRV_RETRY_COUNT) {
267 return status_timeout;
268 }
269
270 *byte = ptr->RBR & UART_RBR_RBR_MASK;
271 return status_success;
272 }
273
uart_set_signal_level(UART_Type * ptr,uart_signal_t signal,uart_signal_level_t level)274 void uart_set_signal_level(UART_Type *ptr, uart_signal_t signal, uart_signal_level_t level)
275 {
276 if (level == uart_signal_level_low) {
277 ptr->MCR = (ptr->MCR | signal);
278 } else {
279 ptr->MCR = (ptr->MCR & ~signal);
280 }
281 }
282
uart_receive_data(UART_Type * ptr,uint8_t * source,uint32_t size_in_byte)283 hpm_stat_t uart_receive_data(UART_Type *ptr, uint8_t *source, uint32_t size_in_byte)
284 {
285 for (uint32_t i = 0; i < size_in_byte; i++) {
286 if (status_success != uart_receive_byte(ptr, source + i)) {
287 return status_fail;
288 }
289 }
290 return status_success;
291 }
292
uart_send_data(UART_Type * ptr,uint8_t * source,uint32_t size_in_byte)293 hpm_stat_t uart_send_data(UART_Type *ptr, uint8_t *source, uint32_t size_in_byte)
294 {
295 for (uint32_t i = 0; i < size_in_byte; i++) {
296 if (status_success != uart_send_byte(ptr, *(source + i))) {
297 return status_fail;
298 }
299 }
300 return status_success;
301 }
302
303
304 #if defined(UART_SOC_HAS_RXLINE_IDLE_DETECTION) && (UART_SOC_HAS_RXLINE_IDLE_DETECTION == 1)
uart_init_rxline_idle_detection(UART_Type * ptr,uart_rxline_idle_config_t rxidle_config)305 hpm_stat_t uart_init_rxline_idle_detection(UART_Type *ptr, uart_rxline_idle_config_t rxidle_config)
306 {
307 ptr->IDLE_CFG &= ~(UART_IDLE_CFG_RX_IDLE_EN_MASK
308 | UART_IDLE_CFG_RX_IDLE_THR_MASK
309 | UART_IDLE_CFG_RX_IDLE_COND_MASK);
310 ptr->IDLE_CFG |= UART_IDLE_CFG_RX_IDLE_EN_SET(rxidle_config.detect_enable)
311 | UART_IDLE_CFG_RX_IDLE_THR_SET(rxidle_config.threshold)
312 | UART_IDLE_CFG_RX_IDLE_COND_SET(rxidle_config.idle_cond);
313
314 if (rxidle_config.detect_irq_enable) {
315 uart_enable_irq(ptr, uart_intr_rx_line_idle);
316 } else {
317 uart_disable_irq(ptr, uart_intr_rx_line_idle);
318 }
319
320 return status_success;
321 }
322 #endif
323
324 #if defined(UART_SOC_HAS_TXLINE_IDLE_DETECTION) && (UART_SOC_HAS_TXLINE_IDLE_DETECTION == 1)
uart_init_txline_idle_detection(UART_Type * ptr,uart_rxline_idle_config_t txidle_config)325 hpm_stat_t uart_init_txline_idle_detection(UART_Type *ptr, uart_rxline_idle_config_t txidle_config)
326 {
327 ptr->IDLE_CFG &= ~(UART_IDLE_CFG_TX_IDLE_EN_MASK
328 | UART_IDLE_CFG_TX_IDLE_THR_MASK
329 | UART_IDLE_CFG_TX_IDLE_COND_MASK);
330 ptr->IDLE_CFG |= UART_IDLE_CFG_TX_IDLE_EN_SET(txidle_config.detect_enable)
331 | UART_IDLE_CFG_TX_IDLE_THR_SET(txidle_config.threshold)
332 | UART_IDLE_CFG_TX_IDLE_COND_SET(txidle_config.idle_cond);
333
334 if (txidle_config.detect_irq_enable) {
335 uart_enable_irq(ptr, uart_intr_tx_line_idle);
336 } else {
337 uart_disable_irq(ptr, uart_intr_tx_line_idle);
338 }
339
340 return status_success;
341 }
342 #endif
343
344 #if defined(UART_SOC_HAS_TRIG_MODE) && (UART_SOC_HAS_TRIG_MODE == 1)
uart_config_transfer_trig_mode(UART_Type * ptr,uart_trig_config_t * config)345 void uart_config_transfer_trig_mode(UART_Type *ptr, uart_trig_config_t *config)
346 {
347 ptr->MOTO_CFG = UART_MOTO_CFG_TXSTP_BITS_SET(config->stop_bit_len)
348 | UART_MOTO_CFG_HWTRG_EN_SET(config->hardware_trig)
349 | UART_MOTO_CFG_TRG_MODE_SET(config->trig_mode)
350 | UART_MOTO_CFG_TRG_CLR_RFIFO_SET(config->trig_clr_rxfifo)
351 | UART_MOTO_CFG_TXSTOP_INSERT_SET(config->en_stop_bit_insert);
352 }
353 #endif
354
355 /* fifo control register(FCR) is WO access, if support FCCR register, it is RW access. */
uart_config_fifo_ctrl(UART_Type * ptr,uart_fifo_ctrl_t * ctrl)356 void uart_config_fifo_ctrl(UART_Type *ptr, uart_fifo_ctrl_t *ctrl)
357 {
358 #if defined(UART_SOC_HAS_FINE_FIFO_THR) && (UART_SOC_HAS_FINE_FIFO_THR == 1)
359 ptr->FCRR = UART_FCRR_FIFOT4EN_MASK
360 | UART_FCRR_TFIFOT4_SET(ctrl->tx_fifo_level)
361 | UART_FCRR_RFIFOT4_SET(ctrl->rx_fifo_level)
362 | UART_FCRR_DMAE_SET(ctrl->dma_enable)
363 | UART_FCRR_TFIFORST_SET(ctrl->reset_tx_fifo)
364 | UART_FCRR_RFIFORST_SET(ctrl->reset_rx_fifo)
365 | UART_FCRR_FIFOE_SET(ctrl->fifo_enable);
366 #else
367 ptr->FCR = UART_FCR_TFIFOT_SET(ctrl->tx_fifo_level)
368 | UART_FCR_RFIFOT_SET(ctrl->rx_fifo_level)
369 | UART_FCR_TFIFORST_SET(ctrl->reset_tx_fifo)
370 | UART_FCR_RFIFORST_SET(ctrl->reset_rx_fifo)
371 | UART_FCR_DMAE_SET(ctrl->dma_enable)
372 | UART_FCR_FIFOE_SET(ctrl->fifo_enable);
373 /* store FCR to GPR */
374 ptr->GPR = UART_FCR_TFIFOT_SET(ctrl->tx_fifo_level)
375 | UART_FCR_RFIFOT_SET(ctrl->rx_fifo_level)
376 | UART_FCR_DMAE_SET(ctrl->dma_enable)
377 | UART_FCR_FIFOE_SET(ctrl->fifo_enable);
378 #endif
379 }