1 /*
2 * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 // The HAL layer for SPI (common part)
8
9 #include "hal/spi_hal.h"
10 #include "hal/log.h"
11 #include "hal/assert.h"
12 #include "soc/soc_caps.h"
13 #include "soc/clk_tree_defs.h"
14
15 //This GDMA related part will be introduced by GDMA dedicated APIs in the future. Here we temporarily use macros.
16 #if SOC_GDMA_SUPPORTED
17 #include "soc/gdma_struct.h"
18 #include "hal/gdma_ll.h"
19
20 #define spi_dma_ll_rx_enable_burst_data(dev, chan, enable) gdma_ll_rx_enable_data_burst(&GDMA, chan, enable);
21 #define spi_dma_ll_tx_enable_burst_data(dev, chan, enable) gdma_ll_tx_enable_data_burst(&GDMA, chan, enable);
22 #define spi_dma_ll_rx_enable_burst_desc(dev, chan, enable) gdma_ll_rx_enable_descriptor_burst(&GDMA, chan, enable);
23 #define spi_dma_ll_tx_enable_burst_desc(dev, chan, enable) gdma_ll_tx_enable_descriptor_burst(&GDMA, chan, enable);
24 #define spi_dma_ll_enable_out_auto_wrback(dev, chan, enable) gdma_ll_tx_enable_auto_write_back(&GDMA, chan, enable);
25 #define spi_dma_ll_set_out_eof_generation(dev, chan, enable) gdma_ll_tx_set_eof_mode(&GDMA, chan, enable);
26 #endif
27
28 /* The tag may be unused if log level is set to NONE */
29 static const __attribute__((unused)) char SPI_HAL_TAG[] = "spi_hal";
30
31 #define SPI_HAL_CHECK(a, str, ret_val, ...) \
32 if (!(a)) { \
33 HAL_LOGE(SPI_HAL_TAG,"%s(%d): "str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
34 return (ret_val); \
35 }
36
s_spi_hal_dma_init_config(const spi_hal_context_t * hal)37 static void s_spi_hal_dma_init_config(const spi_hal_context_t *hal)
38 {
39 spi_dma_ll_rx_enable_burst_data(hal->dma_in, hal->rx_dma_chan, 1);
40 spi_dma_ll_tx_enable_burst_data(hal->dma_out, hal->tx_dma_chan, 1);
41 spi_dma_ll_rx_enable_burst_desc(hal->dma_in, hal->rx_dma_chan, 1);
42 spi_dma_ll_tx_enable_burst_desc(hal->dma_out, hal->tx_dma_chan, 1);
43 }
44
spi_hal_init(spi_hal_context_t * hal,uint32_t host_id,const spi_hal_config_t * config)45 void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_config_t *config)
46 {
47 memset(hal, 0, sizeof(spi_hal_context_t));
48 spi_dev_t *hw = SPI_LL_GET_HW(host_id);
49 hal->hw = hw;
50 hal->dma_in = config->dma_in;
51 hal->dma_out = config->dma_out;
52 hal->dma_enabled = config->dma_enabled;
53 hal->dmadesc_tx = config->dmadesc_tx;
54 hal->dmadesc_rx = config->dmadesc_rx;
55 hal->tx_dma_chan = config->tx_dma_chan;
56 hal->rx_dma_chan = config->rx_dma_chan;
57 hal->dmadesc_n = config->dmadesc_n;
58
59 #if SPI_LL_MOSI_FREE_LEVEL
60 // Change default data line level to low which same as esp32
61 spi_ll_set_mosi_free_level(hw, 0);
62 #endif
63 spi_ll_master_init(hw);
64 if (config->dma_enabled) {
65 s_spi_hal_dma_init_config(hal);
66 }
67
68 //Force a transaction done interrupt. This interrupt won't fire yet because
69 //we initialized the SPI interrupt as disabled. This way, we can just
70 //enable the SPI interrupt and the interrupt handler will kick in, handling
71 //any transactions that are queued.
72 spi_ll_enable_int(hw);
73 spi_ll_set_int_stat(hw);
74 spi_ll_set_mosi_delay(hw, 0, 0);
75 spi_ll_apply_config(hw);
76 }
77
spi_hal_deinit(spi_hal_context_t * hal)78 void spi_hal_deinit(spi_hal_context_t *hal)
79 {
80 spi_dev_t *hw = hal->hw;
81 if (hw) {
82 spi_ll_disable_int(hw);
83 spi_ll_clear_int_stat(hw);
84 }
85 }
86
spi_hal_cal_clock_conf(const spi_hal_timing_param_t * timing_param,int * out_freq,spi_hal_timing_conf_t * timing_conf)87 esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, int *out_freq, spi_hal_timing_conf_t *timing_conf)
88 {
89 spi_hal_timing_conf_t temp_conf = {};
90
91 int eff_clk_n = spi_ll_master_cal_clock(timing_param->clk_src_hz, timing_param->expected_freq, timing_param->duty_cycle, &temp_conf.clock_reg);
92
93 //When the speed is too fast, we may need to use dummy cycles to compensate the reading.
94 //But these don't work for full-duplex connections.
95 spi_hal_cal_timing(timing_param->clk_src_hz, eff_clk_n, timing_param->use_gpio, timing_param->input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay);
96
97 #ifdef CONFIG_IDF_TARGET_ESP32
98 const int freq_limit = spi_hal_get_freq_limit(timing_param->use_gpio, timing_param->input_delay_ns);
99
100 SPI_HAL_CHECK(timing_param->half_duplex || temp_conf.timing_dummy == 0 || timing_param->no_compensate,
101 "When work in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\
102 Try to use IOMUX pins to increase the frequency limit, or use the half duplex mode.\n\
103 Please note the SPI master can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\
104 Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.",
105 ESP_ERR_NOT_SUPPORTED, freq_limit / 1000. / 1000 );
106 #endif
107
108 if (timing_conf) {
109 *timing_conf = temp_conf;
110 }
111 if (out_freq) {
112 *out_freq = eff_clk_n;
113 }
114 return ESP_OK;
115 }
116
spi_hal_master_cal_clock(int fapb,int hz,int duty_cycle)117 int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle)
118 {
119 return spi_ll_master_cal_clock(fapb, hz, duty_cycle, NULL);
120 }
121
122
spi_hal_cal_timing(int source_freq_hz,int eff_clk,bool gpio_is_used,int input_delay_ns,int * dummy_n,int * miso_delay_n)123 void spi_hal_cal_timing(int source_freq_hz, int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n)
124 {
125 const int apbclk_kHz = source_freq_hz / 1000;
126 //how many apb clocks a period has
127 const int spiclk_apb_n = source_freq_hz / eff_clk;
128 const int gpio_delay_ns = gpio_is_used ? GPIO_MATRIX_DELAY_NS : 0;
129
130 //how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
131 int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
132 if (delay_apb_n < 0) {
133 delay_apb_n = 0;
134 }
135
136 int dummy_required = delay_apb_n / spiclk_apb_n;
137
138 int miso_delay = 0;
139 if (dummy_required > 0) {
140 //due to the clock delay between master and slave, there's a range in which data is random
141 //give MISO a delay if needed to make sure we sample at the time MISO is stable
142 miso_delay = (dummy_required + 1) * spiclk_apb_n - delay_apb_n - 1;
143 } else {
144 //if the dummy is not required, maybe we should also delay half a SPI clock if the data comes too early
145 if (delay_apb_n * 4 <= spiclk_apb_n) {
146 miso_delay = -1;
147 }
148 }
149 *dummy_n = dummy_required;
150 *miso_delay_n = miso_delay;
151 HAL_LOGD(SPI_HAL_TAG, "eff: %d, limit: %dk(/%d), %d dummy, %d delay", eff_clk / 1000, apbclk_kHz / (delay_apb_n + 1), delay_apb_n, dummy_required, miso_delay);
152 }
153
154 #ifdef CONFIG_IDF_TARGET_ESP32
155 //TODO: IDF-6578
spi_hal_get_freq_limit(bool gpio_is_used,int input_delay_ns)156 int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns)
157 {
158 const int apbclk_kHz = APB_CLK_FREQ / 1000;
159 const int gpio_delay_ns = gpio_is_used ? GPIO_MATRIX_DELAY_NS : 0;
160
161 //how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
162 int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
163 if (delay_apb_n < 0) {
164 delay_apb_n = 0;
165 }
166
167 return APB_CLK_FREQ / (delay_apb_n + 1);
168 }
169 #endif
170