1
use crate::uart::{
2
    ConfigError,
3
    CtsConfig,
4
    HwFlowControl,
5
    RegisterBlock,
6
    RtsConfig,
7
    StopBits,
8
    SwFlowControl,
9
    low_level::Info,
10
};
11

            
12
#[inline(always)]
13
pub(crate) fn enable_register_sync(_register_block: &RegisterBlock) {
14
    #[cfg(not(any(esp32, esp32s2)))]
15
    {
16
        // We need to clear "high_speed" (UART_UPDATE_CTRL) for UART_REG_UPDATE
17
        // to actually do anything. It's unclear if we have to clear this bit
18
        // separately from using `reg_update` but this is cheap and for sure works.
19
        _register_block
20
            .id()
21
            .modify(|_, w| w.high_speed().clear_bit());
22
    }
23
}
24

            
25
#[inline(always)]
26
pub(crate) fn sync_regs(_register_block: &RegisterBlock) {
27
    #[cfg(not(any(esp32, esp32s2)))]
28
    {
29
        let update_reg = _register_block.id();
30

            
31
        update_reg.write(|w| {
32
            w.high_speed().clear_bit();
33
            w.reg_update().set_bit()
34
        });
35

            
36
        while update_reg.read().reg_update().bit_is_set() {
37
            core::hint::spin_loop();
38
        }
39
    }
40
}
41

            
42
pub(super) fn set_rx_timeout(
43
    info: &Info,
44
    timeout: Option<u8>,
45
    _symbol_len: u8,
46
) -> Result<(), ConfigError> {
47
    cfg_select! {
48
        esp32 => {
49
            const MAX_THRHD: u8 = 0x7F; // 7 bits
50
        }
51
        _ => {
52
            const MAX_THRHD: u16 = 0x3FF; // 10 bits
53
        }
54
    }
55

            
56
    if let Some(timeout) = timeout {
57
        let timeout_reg = cfg_select! {
58
            esp32 => timeout,
59
            _ => timeout as u16 * _symbol_len as u16,
60
        };
61

            
62
        if timeout_reg > MAX_THRHD {
63
            return Err(ConfigError::TimeoutTooLong);
64
        }
65

            
66
        let reg_thrhd = cfg_select! {
67
            esp32 => info.regs().conf1(),
68
            _ => info.regs().mem_conf(),
69
        };
70
        reg_thrhd.modify(|_, w| unsafe { w.rx_tout_thrhd().bits(timeout_reg) });
71
    }
72

            
73
    info.regs()
74
        .conf1()
75
        .modify(|_, w| w.rx_tout_en().bit(timeout.is_some()));
76

            
77
    info.sync_regs();
78

            
79
    Ok(())
80
}
81

            
82
pub(super) fn rx_timeout_enabled(info: &Info) -> bool {
83
    info.regs().conf1().read().rx_tout_en().bit_is_set()
84
}
85

            
86
pub(super) fn is_tx_idle(info: &Info) -> bool {
87
    let status = cfg_select! {
88
        esp32 => info.regs().status(),
89
        _ => info.regs().fsm_status(),
90
    };
91

            
92
    status.read().st_utx_out().bits() == 0x0
93
}
94

            
95
pub(super) fn change_stop_bits(info: &Info, stop_bits: StopBits) {
96
    #[cfg(esp32)]
97
    {
98
        // workaround for hardware issue, when UART stop bit set as 2-bit mode.
99
        if stop_bits == StopBits::_2 {
100
            info.regs()
101
                .rs485_conf()
102
                .modify(|_, w| w.dl1_en().bit(stop_bits == StopBits::_2));
103

            
104
            info.regs()
105
                .conf0()
106
                .modify(|_, w| unsafe { w.stop_bit_num().bits(1) });
107
        }
108
    }
109

            
110
    #[cfg(not(esp32))]
111
    info.regs()
112
        .conf0()
113
        .modify(|_, w| unsafe { w.stop_bit_num().bits(stop_bits as u8 + 1) });
114
}
115

            
116
pub(super) fn change_flow_control(
117
    info: &Info,
118
    sw_flow_ctrl: SwFlowControl,
119
    hw_flow_ctrl: HwFlowControl,
120
) {
121
    if let SwFlowControl::Enabled {
122
        xon_char,
123
        xoff_char,
124
        xon_threshold,
125
        xoff_threshold,
126
    } = sw_flow_ctrl
127
    {
128
        cfg_select! {
129
            esp32 => {
130
                info.regs().swfc_conf().modify(|_, w| unsafe {
131
                    w.xon_threshold().bits(xon_threshold);
132
                    w.xoff_threshold().bits(xoff_threshold);
133
                    w.xon_char().bits(xon_char);
134
                    w.xoff_char().bits(xoff_char)
135
                });
136
            }
137
            _ => {
138
                info.regs().swfc_conf1().modify(|_, w| unsafe {
139
                    w.xon_threshold().bits(xon_threshold as u16);
140
                    w.xon_char().bits(xon_char)
141
                });
142
                info.regs().swfc_conf0().modify(|_, w| unsafe {
143
                    w.xoff_threshold().bits(xoff_threshold as u16);
144
                    w.xoff_char().bits(xoff_char)
145
                });
146
            }
147
        }
148
    }
149

            
150
    info.regs().flow_conf().modify(|_, w| {
151
        w.xonoff_del()
152
            .bit(matches!(sw_flow_ctrl, SwFlowControl::Enabled { .. }));
153
        w.sw_flow_con_en()
154
            .bit(matches!(sw_flow_ctrl, SwFlowControl::Enabled { .. }))
155
    });
156
    info.regs().conf0().modify(|_, w| {
157
        w.tx_flow_en()
158
            .bit(matches!(hw_flow_ctrl.cts, CtsConfig::Enabled))
159
    });
160

            
161
    match hw_flow_ctrl.rts {
162
        RtsConfig::Enabled(threshold) => configure_rts_flow_ctrl(info, true, Some(threshold)),
163
        RtsConfig::Disabled => configure_rts_flow_ctrl(info, false, None),
164
    }
165

            
166
    sync_regs(info.regs());
167
}
168

            
169
#[cfg(sleep_driver_supported)]
170
pub(super) fn suspend(_info: &Info, _en: bool) {
171
    // We drain the entire FIFO, so we have nothing to disable.
172
}
173

            
174
#[cfg(sleep_driver_supported)]
175
pub(super) fn set_wakeup_edge_threshold(info: &Info, threshold: u16) {
176
    info.regs()
177
        .sleep_conf()
178
        .modify(|_, w| unsafe { w.active_threshold().bits(threshold) });
179
}
180

            
181
#[cfg(sleep_driver_supported)]
182
pub(super) fn wait_for_suspended(info: &Info) {
183
    // Wait for FIFO to drain completely.
184
    while info.regs().status().read().txfifo_cnt().bits() > 0 {}
185

            
186
    let fsm_status = cfg_select! {
187
        esp32 => info.regs().status(),
188
        _ => info.regs().fsm_status(),
189
    };
190

            
191
    while fsm_status.read().st_utx_out().bits() != 0 {}
192
}
193

            
194
fn configure_rts_flow_ctrl(info: &Info, enable: bool, threshold: Option<u8>) {
195
    if let Some(threshold) = threshold {
196
        cfg_select! {
197
            esp32 => {
198
                info.regs()
199
                    .conf1()
200
                    .modify(|_, w| unsafe { w.rx_flow_thrhd().bits(threshold) });
201
            }
202
            _ => {
203
                info.regs()
204
                    .mem_conf()
205
                    .modify(|_, w| unsafe { w.rx_flow_thrhd().bits(threshold as u16) });
206
            }
207
        }
208
    }
209

            
210
    info.regs()
211
        .conf1()
212
        .modify(|_, w| w.rx_flow_en().bit(enable));
213
}
214

            
215
pub(super) fn current_symbol_length(info: &Info) -> u8 {
216
    let conf0 = info.regs().conf0().read();
217
    let data_bits = conf0.bit_num().bits() + 5; // 5 data bits are encoded as variant 0
218
    let parity = conf0.parity_en().bit() as u8;
219
    let mut stop_bits = conf0.stop_bit_num().bits();
220

            
221
    match stop_bits {
222
        1 => {
223
            // workaround for hardware issue, when UART stop bit set as 2-bit mode.
224
            #[cfg(esp32)]
225
            if info.regs().rs485_conf().read().dl1_en().bit_is_set() {
226
                stop_bits = 2;
227
            }
228
        }
229
        // esp-idf also counts 2 bits for settings 1.5 and 2 stop bits
230
        _ => stop_bits = 2,
231
    }
232

            
233
    1 + data_bits + parity + stop_bits
234
}
235

            
236
pub(super) fn read_next_from_fifo(info: &Info) -> u8 {
237
    fn access_fifo_register<R>(f: impl Fn() -> R) -> R {
238
        // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/cpu-subsequent-access-halted-when-get-interrupted.html
239
        cfg_select! {
240
            esp32 => crate::interrupt::free(f),
241
            _ => f(),
242
        }
243
    }
244

            
245
    let fifo_reg = info.regs().fifo();
246
    cfg_select! {
247
        esp32s2 => {
248
            // On the ESP32-S2 we need to use PeriBus2 to read the FIFO:
249
            let fifo_reg = unsafe {
250
                &*fifo_reg
251
                    .as_ptr()
252
                    .cast::<u8>()
253
                    .add(0x20C00000)
254
                    .cast::<crate::pac::uart0::FIFO>()
255
            };
256
        }
257
        _ => {}
258
    }
259

            
260
    access_fifo_register(|| fifo_reg.read().rxfifo_rd_byte().bits())
261
}
262

            
263
#[allow(clippy::unnecessary_cast)]
264
pub(super) fn rx_fifo_count(info: &Info) -> u16 {
265
    cfg_select! {
266
        esp32 => {
267
            let fifo_cnt = info.regs().status().read().rxfifo_cnt().bits();
268

            
269
            // Calculate the real count based on the FIFO read and write offset address:
270
            // https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32/03-errata-description/esp32/uart-fifo-cnt-indicates-data-length-incorrectly.html
271
            let status = info.regs().mem_rx_status().read();
272
            let rd_addr: u16 = status.mem_rx_rd_addr().bits();
273
            let wr_addr: u16 = status.mem_rx_wr_addr().bits();
274

            
275
            if wr_addr > rd_addr {
276
                wr_addr - rd_addr
277
            } else if wr_addr < rd_addr {
278
                (wr_addr + Info::UART_FIFO_SIZE) - rd_addr
279
            } else if fifo_cnt > 0 {
280
                Info::UART_FIFO_SIZE
281
            } else {
282
                0
283
            }
284
        }
285
        _ => info.regs().status().read().rxfifo_cnt().bits() as u16,
286
    }
287
}