1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include "hardware/address_mapped.h"
8 #include "hardware/platform_defs.h"
9 #include "hardware/uart.h"
10 
11 #include "hardware/structs/uart.h"
12 #include "hardware/resets.h"
13 #include "hardware/clocks.h"
14 #include "hardware/timer.h"
15 
16 #include "pico/assert.h"
17 #include "pico.h"
18 
19 check_hw_layout(uart_hw_t, fr, UART_UARTFR_OFFSET);
20 check_hw_layout(uart_hw_t, dmacr, UART_UARTDMACR_OFFSET);
21 
22 #if PICO_UART_ENABLE_CRLF_SUPPORT
23 short uart_char_to_line_feed[NUM_UARTS];
24 #endif
25 
26 /// \tag::uart_reset[]
uart_reset(uart_inst_t * uart)27 static inline void uart_reset(uart_inst_t *uart) {
28     invalid_params_if(UART, uart != uart0 && uart != uart1);
29     reset_block(uart_get_index(uart) ? RESETS_RESET_UART1_BITS : RESETS_RESET_UART0_BITS);
30 }
31 
uart_unreset(uart_inst_t * uart)32 static inline void uart_unreset(uart_inst_t *uart) {
33     invalid_params_if(UART, uart != uart0 && uart != uart1);
34     unreset_block_wait(uart_get_index(uart) ? RESETS_RESET_UART1_BITS : RESETS_RESET_UART0_BITS);
35 }
36 /// \end::uart_reset[]
37 
38 /// \tag::uart_init[]
uart_init(uart_inst_t * uart,uint baudrate)39 uint uart_init(uart_inst_t *uart, uint baudrate) {
40     invalid_params_if(UART, uart != uart0 && uart != uart1);
41 
42     if (clock_get_hz(clk_peri) == 0) {
43         return 0;
44     }
45 
46     uart_reset(uart);
47     uart_unreset(uart);
48 
49 #if PICO_UART_ENABLE_CRLF_SUPPORT
50     uart_set_translate_crlf(uart, PICO_UART_DEFAULT_CRLF);
51 #endif
52 
53     // Any LCR writes need to take place before enabling the UART
54     uint baud = uart_set_baudrate(uart, baudrate);
55     uart_set_format(uart, 8, 1, UART_PARITY_NONE);
56 
57     // Enable FIFOs (must be before setting UARTEN, as this is an LCR access)
58     hw_set_bits(&uart_get_hw(uart)->lcr_h, UART_UARTLCR_H_FEN_BITS);
59     // Enable the UART, both TX and RX
60     uart_get_hw(uart)->cr = UART_UARTCR_UARTEN_BITS | UART_UARTCR_TXE_BITS | UART_UARTCR_RXE_BITS;
61     // Always enable DREQ signals -- no harm in this if DMA is not listening
62     uart_get_hw(uart)->dmacr = UART_UARTDMACR_TXDMAE_BITS | UART_UARTDMACR_RXDMAE_BITS;
63 
64     return baud;
65 }
66 /// \end::uart_init[]
67 
uart_deinit(uart_inst_t * uart)68 void uart_deinit(uart_inst_t *uart) {
69     invalid_params_if(UART, uart != uart0 && uart != uart1);
70     uart_reset(uart);
71 }
72 
uart_disable_before_lcr_write(uart_inst_t * uart)73 static uint32_t uart_disable_before_lcr_write(uart_inst_t *uart) {
74     // Notes from PL011 reference manual:
75     //
76     // - Before writing the LCR, if the UART is enabled it needs to be
77     //   disabled and any current TX + RX activity has to be completed
78     //
79     // - There is a BUSY flag which waits for the current TX char, but this is
80     //   OR'd with TX FIFO !FULL, so not usable when FIFOs are enabled and
81     //   potentially nonempty
82     //
83     // - FIFOs can't be set to disabled whilst a character is in progress
84     //   (else "FIFO integrity is not guaranteed")
85     //
86     // Combination of these means there is no general way to halt and poll for
87     // end of TX character, if FIFOs may be enabled. Either way, there is no
88     // way to poll for end of RX character.
89     //
90     // So, insert a 15 Baud period delay before changing the settings.
91     // 15 Baud is comfortably higher than start + max data + parity + stop.
92     // Anything else would require API changes to permit a non-enabled UART
93     // state after init() where settings can be changed safely.
94     uint32_t cr_save = uart_get_hw(uart)->cr;
95 
96     if (cr_save & UART_UARTCR_UARTEN_BITS) {
97         hw_clear_bits(&uart_get_hw(uart)->cr,
98             UART_UARTCR_UARTEN_BITS | UART_UARTCR_TXE_BITS | UART_UARTCR_RXE_BITS);
99 
100         uint32_t current_ibrd = uart_get_hw(uart)->ibrd;
101         uint32_t current_fbrd = uart_get_hw(uart)->fbrd;
102 
103         // Note: Maximise precision here. Show working, the compiler will mop this up.
104         // Create a 16.6 fixed-point fractional division ratio; then scale to 32-bits.
105         uint32_t brdiv_ratio = 64u * current_ibrd + current_fbrd;
106         brdiv_ratio <<= 10;
107         // 3662 is ~(15 * 244.14) where 244.14 is 16e6 / 2^16
108         uint32_t scaled_freq = clock_get_hz(clk_peri) / 3662ul;
109         uint32_t wait_time_us = brdiv_ratio / scaled_freq;
110         busy_wait_us(wait_time_us);
111     }
112 
113     return cr_save;
114 }
115 
uart_write_lcr_bits_masked(uart_inst_t * uart,uint32_t values,uint32_t write_mask)116 static void uart_write_lcr_bits_masked(uart_inst_t *uart, uint32_t values, uint32_t write_mask) {
117     invalid_params_if(UART, uart != uart0 && uart != uart1);
118 
119     // (Potentially) Cleanly handle disabling the UART before touching LCR
120     uint32_t cr_save = uart_disable_before_lcr_write(uart);
121 
122     hw_write_masked(&uart_get_hw(uart)->lcr_h, values, write_mask);
123 
124     uart_get_hw(uart)->cr = cr_save;
125 }
126 
127 /// \tag::uart_set_baudrate[]
uart_set_baudrate(uart_inst_t * uart,uint baudrate)128 uint uart_set_baudrate(uart_inst_t *uart, uint baudrate) {
129     invalid_params_if(UART, baudrate == 0);
130     uint32_t baud_rate_div = (8 * clock_get_hz(clk_peri) / baudrate);
131     uint32_t baud_ibrd = baud_rate_div >> 7;
132     uint32_t baud_fbrd;
133 
134     if (baud_ibrd == 0) {
135         baud_ibrd = 1;
136         baud_fbrd = 0;
137     } else if (baud_ibrd >= 65535) {
138         baud_ibrd = 65535;
139         baud_fbrd = 0;
140     }  else {
141         baud_fbrd = ((baud_rate_div & 0x7f) + 1) / 2;
142     }
143 
144     uart_get_hw(uart)->ibrd = baud_ibrd;
145     uart_get_hw(uart)->fbrd = baud_fbrd;
146 
147     // PL011 needs a (dummy) LCR_H write to latch in the divisors.
148     // We don't want to actually change LCR_H contents here.
149     uart_write_lcr_bits_masked(uart, 0, 0);
150 
151     // See datasheet
152     return (4 * clock_get_hz(clk_peri)) / (64 * baud_ibrd + baud_fbrd);
153 }
154 /// \end::uart_set_baudrate[]
155 
uart_set_format(uart_inst_t * uart,uint data_bits,uint stop_bits,uart_parity_t parity)156 void uart_set_format(uart_inst_t *uart, uint data_bits, uint stop_bits, uart_parity_t parity) {
157     invalid_params_if(UART, data_bits < 5 || data_bits > 8);
158     invalid_params_if(UART, stop_bits != 1 && stop_bits != 2);
159     invalid_params_if(UART, parity != UART_PARITY_NONE && parity != UART_PARITY_EVEN && parity != UART_PARITY_ODD);
160 
161     uart_write_lcr_bits_masked(uart,
162         ((data_bits - 5u) << UART_UARTLCR_H_WLEN_LSB) |
163         ((stop_bits - 1u) << UART_UARTLCR_H_STP2_LSB) |
164         (bool_to_bit(parity != UART_PARITY_NONE) << UART_UARTLCR_H_PEN_LSB) |
165         (bool_to_bit(parity == UART_PARITY_EVEN) << UART_UARTLCR_H_EPS_LSB),
166         UART_UARTLCR_H_WLEN_BITS |
167         UART_UARTLCR_H_STP2_BITS |
168         UART_UARTLCR_H_PEN_BITS |
169         UART_UARTLCR_H_EPS_BITS);
170 }
171 
uart_set_fifo_enabled(uart_inst_t * uart,bool enabled)172 void uart_set_fifo_enabled(uart_inst_t *uart, bool enabled) {
173 
174     uint32_t lcr_h_fen_bits = 0;
175 
176     if (enabled) {
177         lcr_h_fen_bits = UART_UARTLCR_H_FEN_BITS;
178     }
179 
180     uart_write_lcr_bits_masked(uart, lcr_h_fen_bits, UART_UARTLCR_H_FEN_BITS);
181 }
182 
uart_set_break(uart_inst_t * uart,bool en)183 void uart_set_break(uart_inst_t *uart, bool en) {
184 
185     uint32_t lcr_h_brk_bits = 0;
186 
187     if (en) {
188         lcr_h_brk_bits = UART_UARTLCR_H_BRK_BITS;
189     }
190 
191     uart_write_lcr_bits_masked(uart, lcr_h_brk_bits, UART_UARTLCR_H_BRK_BITS);
192 }
193 
uart_set_translate_crlf(uart_inst_t * uart,bool crlf)194 void uart_set_translate_crlf(uart_inst_t *uart, bool crlf) {
195 #if PICO_UART_ENABLE_CRLF_SUPPORT
196     uart_char_to_line_feed[uart_get_index(uart)] = crlf ? '\n' : 0x100;
197 #else
198     panic_unsupported();
199 #endif
200 }
201 
uart_is_readable_within_us(uart_inst_t * uart,uint32_t us)202 bool uart_is_readable_within_us(uart_inst_t *uart, uint32_t us) {
203     uint32_t t = time_us_32();
204     do {
205         if (uart_is_readable(uart)) {
206              return true;
207         }
208     } while ((time_us_32() - t) <= us);
209     return false;
210 }
211