• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // The HAL layer for SPI (common part)
16 
17 #include "hal/spi_hal.h"
18 #include "soc/soc_caps.h"
19 
20 //This GDMA related part will be introduced by GDMA dedicated APIs in the future. Here we temporarily use macros.
21 #if SOC_GDMA_SUPPORTED
22 #include "soc/gdma_struct.h"
23 #include "hal/gdma_ll.h"
24 
25 #define spi_dma_ll_rx_enable_burst_data(dev, chan, enable)         gdma_ll_rx_enable_data_burst(&GDMA, chan, enable);
26 #define spi_dma_ll_tx_enable_burst_data(dev, chan, enable)         gdma_ll_tx_enable_data_burst(&GDMA, chan, enable);
27 #define spi_dma_ll_rx_enable_burst_desc(dev, chan, enable)         gdma_ll_rx_enable_descriptor_burst(&GDMA, chan, enable);
28 #define spi_dma_ll_tx_enable_burst_desc(dev, chan, enable)         gdma_ll_tx_enable_descriptor_burst(&GDMA, chan, enable);
29 #define spi_dma_ll_enable_out_auto_wrback(dev, chan, enable)          gdma_ll_tx_enable_auto_write_back(&GDMA, chan, enable);
30 #define spi_dma_ll_set_out_eof_generation(dev, chan, enable)          gdma_ll_tx_set_eof_mode(&GDMA, chan, enable);
31 #endif
32 
33 static const char SPI_HAL_TAG[] = "spi_hal";
34 #define SPI_HAL_CHECK(a, str, ret_val, ...) \
35     if (!(a)) { \
36         HAL_LOGE(SPI_HAL_TAG,"%s(%d): "str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
37         return (ret_val); \
38     }
39 
s_spi_hal_dma_init_config(const spi_hal_context_t * hal)40 static void s_spi_hal_dma_init_config(const spi_hal_context_t *hal)
41 {
42     spi_dma_ll_rx_enable_burst_data(hal->dma_in, hal->rx_dma_chan, 1);
43     spi_dma_ll_tx_enable_burst_data(hal->dma_out, hal->tx_dma_chan, 1);
44     spi_dma_ll_rx_enable_burst_desc(hal->dma_in, hal->rx_dma_chan, 1);
45     spi_dma_ll_tx_enable_burst_desc(hal->dma_out, hal->tx_dma_chan ,1);
46 }
47 
spi_hal_init(spi_hal_context_t * hal,uint32_t host_id,const spi_hal_config_t * config)48 void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id, const spi_hal_config_t *config)
49 {
50     memset(hal, 0, sizeof(spi_hal_context_t));
51     spi_dev_t *hw = SPI_LL_GET_HW(host_id);
52     hal->hw = hw;
53     hal->dma_in = config->dma_in;
54     hal->dma_out = config->dma_out;
55     hal->dma_enabled = config->dma_enabled;
56     hal->dmadesc_tx = config->dmadesc_tx;
57     hal->dmadesc_rx = config->dmadesc_rx;
58     hal->tx_dma_chan = config->tx_dma_chan;
59     hal->rx_dma_chan = config->rx_dma_chan;
60     hal->dmadesc_n = config->dmadesc_n;
61 
62     spi_ll_master_init(hw);
63     s_spi_hal_dma_init_config(hal);
64 
65     //Force a transaction done interrupt. This interrupt won't fire yet because
66     //we initialized the SPI interrupt as disabled. This way, we can just
67     //enable the SPI interrupt and the interrupt handler will kick in, handling
68     //any transactions that are queued.
69     spi_ll_enable_int(hw);
70     spi_ll_set_int_stat(hw);
71     spi_ll_set_mosi_delay(hw, 0, 0);
72 }
73 
spi_hal_deinit(spi_hal_context_t * hal)74 void spi_hal_deinit(spi_hal_context_t *hal)
75 {
76     spi_dev_t *hw = hal->hw;
77     if (hw) {
78         spi_ll_disable_int(hw);
79         spi_ll_clear_int_stat(hw);
80     }
81 }
82 
spi_hal_cal_clock_conf(const spi_hal_timing_param_t * timing_param,int * out_freq,spi_hal_timing_conf_t * timing_conf)83 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)
84 {
85     spi_hal_timing_conf_t temp_conf;
86 
87     int eff_clk_n = spi_ll_master_cal_clock(SPI_LL_PERIPH_CLK_FREQ, timing_param->clock_speed_hz, timing_param->duty_cycle, &temp_conf.clock_reg);
88 
89     //When the speed is too fast, we may need to use dummy cycles to compensate the reading.
90     //But these don't work for full-duplex connections.
91     spi_hal_cal_timing(eff_clk_n, timing_param->use_gpio, timing_param->input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay);
92 
93 #ifdef CONFIG_IDF_TARGET_ESP32
94     const int freq_limit = spi_hal_get_freq_limit(timing_param->use_gpio, timing_param->input_delay_ns);
95 
96     SPI_HAL_CHECK(timing_param->half_duplex || temp_conf.timing_dummy == 0 || timing_param->no_compensate,
97                   "When work in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\
98 Try to use IOMUX pins to increase the frequency limit, or use the half duplex mode.\n\
99 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\
100 Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.",
101                   ESP_ERR_NOT_SUPPORTED, freq_limit / 1000. / 1000 );
102 #endif
103 
104     if (timing_conf) {
105         *timing_conf = temp_conf;
106     }
107     if (out_freq) {
108         *out_freq = eff_clk_n;
109     }
110     return ESP_OK;
111 }
112 
spi_hal_master_cal_clock(int fapb,int hz,int duty_cycle)113 int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle)
114 {
115     return spi_ll_master_cal_clock(fapb, hz, duty_cycle, NULL);
116 }
117 
spi_hal_cal_timing(int eff_clk,bool gpio_is_used,int input_delay_ns,int * dummy_n,int * miso_delay_n)118 void spi_hal_cal_timing(int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n)
119 {
120     const int apbclk_kHz = APB_CLK_FREQ / 1000;
121     //how many apb clocks a period has
122     const int spiclk_apb_n = APB_CLK_FREQ / eff_clk;
123     const int gpio_delay_ns = gpio_is_used ? GPIO_MATRIX_DELAY_NS : 0;
124 
125     //how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
126     int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
127     if (delay_apb_n < 0) {
128         delay_apb_n = 0;
129     }
130 
131     int dummy_required = delay_apb_n / spiclk_apb_n;
132 
133     int miso_delay = 0;
134     if (dummy_required > 0) {
135         //due to the clock delay between master and slave, there's a range in which data is random
136         //give MISO a delay if needed to make sure we sample at the time MISO is stable
137         miso_delay = (dummy_required + 1) * spiclk_apb_n - delay_apb_n - 1;
138     } else {
139         //if the dummy is not required, maybe we should also delay half a SPI clock if the data comes too early
140         if (delay_apb_n * 4 <= spiclk_apb_n) {
141             miso_delay = -1;
142         }
143     }
144     *dummy_n = dummy_required;
145     *miso_delay_n = miso_delay;
146     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);
147 }
148 
spi_hal_get_freq_limit(bool gpio_is_used,int input_delay_ns)149 int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns)
150 {
151     const int apbclk_kHz = APB_CLK_FREQ / 1000;
152     const int gpio_delay_ns = gpio_is_used ? GPIO_MATRIX_DELAY_NS : 0;
153 
154     //how many apb clocks the delay is, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
155     int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
156     if (delay_apb_n < 0) {
157         delay_apb_n = 0;
158     }
159 
160     return APB_CLK_FREQ / (delay_apb_n + 1);
161 }
162