• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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