1
use super::{Config, ConfigError, Driver, RegisterBlock, configure_clock};
2
use crate::soc::clocks::{ClockTree, I2cFunctionClockConfig, I2cInstance};
3

            
4
/// Sets the frequency of the I2C interface by calculating and applying the
5
/// associated timings - corresponds to i2c_ll_cal_bus_clk and
6
/// i2c_ll_set_bus_timing in ESP-IDF.
7
pub(super) fn set_frequency(driver: &Driver<'_>, clock_config: &Config) -> Result<(), ConfigError> {
8
    let timeout = clock_config.timeout;
9
    let bus_freq = clock_config.frequency.as_hz();
10
57
    let sclk = clock_config.clock_source;
11
    let clock = driver.info.clock_instance;
12

            
13
    let source_clk = I2cInstance::function_clock_source_frequency(sclk);
14

            
15
    ClockTree::with(|clocks| {
16
57
        let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1;
17

            
18
57
        clock.configure_function_clock(clocks, I2cFunctionClockConfig::new(sclk, clkm_div - 1));
19
    });
20

            
21
    let sclk_freq: u32 = clock.function_clock_frequency();
22
57
    let half_cycle: u32 = sclk_freq / bus_freq / 2;
23
    // SCL
24
    let scl_low = half_cycle;
25
    // default, scl_wait_high < scl_high
26
    // Make 80KHz as a boundary here, because when working at lower frequency, too
27
    // much scl_wait_high will faster the frequency according to some
28
    // hardware behaviors.
29
57
    let scl_wait_high = if bus_freq >= 80 * 1000 {
30
57
        half_cycle / 2 - 2
31
    } else {
32
        half_cycle / 4
33
    };
34
57
    let scl_high = half_cycle - scl_wait_high;
35
57
    let sda_hold = half_cycle / 4;
36
    let sda_sample = half_cycle / 2;
37
    let setup = half_cycle;
38
    let hold = half_cycle;
39

            
40
    // According to the Technical Reference Manual, the following timings must be
41
    // subtracted by 1. However, according to the practical measurement and
42
    // some hardware behaviour, if wait_high_period and scl_high minus one.
43
    // The SCL frequency would be a little higher than expected. Therefore, the
44
    // solution here is not to minus scl_high as well as scl_wait high, and
45
    // the frequency will be absolutely accurate to all frequency
46
    // to some extent.
47
57
    let scl_low_period = scl_low - 1;
48
    let scl_high_period = scl_high;
49
    let scl_wait_high_period = scl_wait_high;
50
    // sda sample
51
57
    let sda_hold_time = sda_hold - 1;
52
57
    let sda_sample_time = sda_sample - 1;
53
    // setup
54
    let scl_rstart_setup_time = setup - 1;
55
    let scl_stop_setup_time = setup - 1;
56
    // hold
57
    let scl_start_hold_time = hold - 1;
58
    let scl_stop_hold_time = hold - 1;
59

            
60
57
    configure_clock(
61
        driver.info,
62
        scl_low_period,
63
        scl_high_period,
64
        scl_wait_high_period,
65
        sda_hold_time,
66
        sda_sample_time,
67
        scl_rstart_setup_time,
68
        scl_stop_setup_time,
69
        scl_start_hold_time,
70
        scl_stop_hold_time,
71
        timeout.apb_cycles(half_cycle)?,
72
    )
73
}
74

            
75
/// Resets the transmit and receive FIFO buffers.
76
pub(super) fn reset_fifo(driver: &Driver<'_>) {
77
    driver.regs().fifo_conf().write(|w| unsafe {
78
        w.tx_fifo_rst().set_bit();
79
        w.rx_fifo_rst().set_bit();
80
        w.nonfifo_en().clear_bit();
81
        w.fifo_prt_en().set_bit();
82
        w.rxfifo_wm_thrhd().bits(1);
83
        w.txfifo_wm_thrhd().bits(8)
84
    });
85

            
86
    driver.regs().fifo_conf().modify(|_, w| {
87
        w.tx_fifo_rst().clear_bit();
88
        w.rx_fifo_rst().clear_bit()
89
    });
90

            
91
    driver.regs().int_clr().write(|w| {
92
        w.rxfifo_wm().clear_bit_by_one();
93
        w.txfifo_wm().clear_bit_by_one()
94
    });
95

            
96
    driver.update_registers();
97
}
98

            
99
pub(super) fn read_fifo(register_block: &RegisterBlock) -> u8 {
100
    register_block.data().read().fifo_rdata().bits()
101
}
102

            
103
pub(super) fn write_fifo(register_block: &RegisterBlock, data: u8) {
104
    register_block
105
        .data()
106
        .write(|w| unsafe { w.fifo_rdata().bits(data) });
107
}