• 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 
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 }