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, in iram)
16 // make these functions in a seperate file to make sure all LL functions are in the IRAM.
17
18 #include "hal/spi_hal.h"
19 #include "hal/assert.h"
20 #include "soc/soc_caps.h"
21
22 //This GDMA related part will be introduced by GDMA dedicated APIs in the future. Here we temporarily use macros.
23 #if SOC_GDMA_SUPPORTED
24 #include "soc/gdma_struct.h"
25 #include "hal/gdma_ll.h"
26
27 #define spi_dma_ll_rx_reset(dev, chan) gdma_ll_rx_reset_channel(&GDMA, chan)
28 #define spi_dma_ll_tx_reset(dev, chan) gdma_ll_tx_reset_channel(&GDMA, chan);
29 #define spi_dma_ll_rx_start(dev, chan, addr) do {\
30 gdma_ll_rx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\
31 gdma_ll_rx_start(&GDMA, chan);\
32 } while (0)
33 #define spi_dma_ll_tx_start(dev, chan, addr) do {\
34 gdma_ll_tx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\
35 gdma_ll_tx_start(&GDMA, chan);\
36 } while (0)
37 #endif
38
spi_hal_setup_device(spi_hal_context_t * hal,const spi_hal_dev_config_t * dev)39 void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev)
40 {
41 //Configure clock settings
42 spi_dev_t *hw = hal->hw;
43 #if SOC_SPI_SUPPORT_AS_CS
44 spi_ll_master_set_cksel(hw, dev->cs_pin_id, dev->as_cs);
45 #endif
46 spi_ll_master_set_pos_cs(hw, dev->cs_pin_id, dev->positive_cs);
47 spi_ll_master_set_clock_by_reg(hw, &dev->timing_conf.clock_reg);
48 //Configure bit order
49 spi_ll_set_rx_lsbfirst(hw, dev->rx_lsbfirst);
50 spi_ll_set_tx_lsbfirst(hw, dev->tx_lsbfirst);
51 spi_ll_master_set_mode(hw, dev->mode);
52 //Configure misc stuff
53 spi_ll_set_half_duplex(hw, dev->half_duplex);
54 spi_ll_set_sio_mode(hw, dev->sio);
55 //Configure CS pin and timing
56 spi_ll_master_set_cs_setup(hw, dev->cs_setup);
57 spi_ll_master_set_cs_hold(hw, dev->cs_hold);
58 spi_ll_master_select_cs(hw, dev->cs_pin_id);
59 }
60
spi_hal_setup_trans(spi_hal_context_t * hal,const spi_hal_dev_config_t * dev,const spi_hal_trans_config_t * trans)61 void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans)
62 {
63 spi_dev_t *hw = hal->hw;
64
65 //clear int bit
66 spi_ll_clear_int_stat(hal->hw);
67 //We should be done with the transmission.
68 HAL_ASSERT(spi_ll_get_running_cmd(hw) == 0);
69 //set transaction line mode
70 spi_ll_master_set_line_mode(hw, trans->line_mode);
71
72 int extra_dummy = 0;
73 //when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist
74 if (trans->rcv_buffer && !dev->no_compensate && dev->half_duplex) {
75 extra_dummy = dev->timing_conf.timing_dummy;
76 }
77
78 //SPI iface needs to be configured for a delay in some cases.
79 //configure dummy bits
80 spi_ll_set_dummy(hw, extra_dummy + trans->dummy_bits);
81
82 uint32_t miso_delay_num = 0;
83 uint32_t miso_delay_mode = 0;
84 if (dev->timing_conf.timing_miso_delay < 0) {
85 //if the data comes too late, delay half a SPI clock to improve reading
86 switch (dev->mode) {
87 case 0:
88 miso_delay_mode = 2;
89 break;
90 case 1:
91 miso_delay_mode = 1;
92 break;
93 case 2:
94 miso_delay_mode = 1;
95 break;
96 case 3:
97 miso_delay_mode = 2;
98 break;
99 }
100 miso_delay_num = 0;
101 } else {
102 //if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing
103 miso_delay_num = extra_dummy ? dev->timing_conf.timing_miso_delay : 0;
104 miso_delay_mode = 0;
105 }
106 spi_ll_set_miso_delay(hw, miso_delay_mode, miso_delay_num);
107
108 spi_ll_set_mosi_bitlen(hw, trans->tx_bitlen);
109
110 if (dev->half_duplex) {
111 spi_ll_set_miso_bitlen(hw, trans->rx_bitlen);
112 } else {
113 //rxlength is not used in full-duplex mode
114 spi_ll_set_miso_bitlen(hw, trans->tx_bitlen);
115 }
116
117 //Configure bit sizes, load addr and command
118 int cmdlen = trans->cmd_bits;
119 int addrlen = trans->addr_bits;
120 if (!dev->half_duplex && dev->cs_setup != 0) {
121 /* The command and address phase is not compatible with cs_ena_pretrans
122 * in full duplex mode.
123 */
124 cmdlen = 0;
125 addrlen = 0;
126 }
127
128 spi_ll_set_addr_bitlen(hw, addrlen);
129 spi_ll_set_command_bitlen(hw, cmdlen);
130
131 spi_ll_set_command(hw, trans->cmd, cmdlen, dev->tx_lsbfirst);
132 spi_ll_set_address(hw, trans->addr, addrlen, dev->tx_lsbfirst);
133
134 //Configure keep active CS
135 spi_ll_master_keep_cs(hw, trans->cs_keep_active);
136
137 //Save the transaction attributes for internal usage.
138 memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t));
139 }
140
spi_hal_prepare_data(spi_hal_context_t * hal,const spi_hal_dev_config_t * dev,const spi_hal_trans_config_t * trans)141 void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans)
142 {
143 spi_dev_t *hw = hal->hw;
144
145 //Fill DMA descriptors
146 if (trans->rcv_buffer) {
147 if (!hal->dma_enabled) {
148 //No need to setup anything; we'll copy the result out of the work registers directly later.
149 } else {
150 lldesc_setup_link(hal->dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true);
151
152 spi_dma_ll_rx_reset(hal->dma_in, hal->rx_dma_chan);
153 spi_ll_dma_rx_fifo_reset(hal->hw);
154 spi_ll_infifo_full_clr(hal->hw);
155 spi_ll_dma_rx_enable(hal->hw, 1);
156 spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, hal->dmadesc_rx);
157 }
158
159 }
160 #if CONFIG_IDF_TARGET_ESP32
161 else {
162 //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon
163 if (hal->dma_enabled && !dev->half_duplex) {
164 spi_ll_dma_rx_enable(hal->hw, 1);
165 spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, 0);
166 }
167 }
168 #endif
169
170 if (trans->send_buffer) {
171 if (!hal->dma_enabled) {
172 //Need to copy data to registers manually
173 spi_ll_write_buffer(hw, trans->send_buffer, trans->tx_bitlen);
174 } else {
175 lldesc_setup_link(hal->dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false);
176
177 spi_dma_ll_tx_reset(hal->dma_out, hal->tx_dma_chan);
178 spi_ll_dma_tx_fifo_reset(hal->hw);
179 spi_ll_outfifo_empty_clr(hal->hw);
180 spi_ll_dma_tx_enable(hal->hw, 1);
181 spi_dma_ll_tx_start(hal->dma_out, hal->tx_dma_chan, hal->dmadesc_tx);
182 }
183 }
184
185 //in ESP32 these registers should be configured after the DMA is set
186 if ((!dev->half_duplex && trans->rcv_buffer) || trans->send_buffer) {
187 spi_ll_enable_mosi(hw, 1);
188 } else {
189 spi_ll_enable_mosi(hw, 0);
190 }
191 spi_ll_enable_miso(hw, (trans->rcv_buffer) ? 1 : 0);
192 }
193
spi_hal_user_start(const spi_hal_context_t * hal)194 void spi_hal_user_start(const spi_hal_context_t *hal)
195 {
196 spi_ll_master_user_start(hal->hw);
197 }
198
spi_hal_usr_is_done(const spi_hal_context_t * hal)199 bool spi_hal_usr_is_done(const spi_hal_context_t *hal)
200 {
201 return spi_ll_usr_is_done(hal->hw);
202 }
203
spi_hal_fetch_result(const spi_hal_context_t * hal)204 void spi_hal_fetch_result(const spi_hal_context_t *hal)
205 {
206 const spi_hal_trans_config_t *trans = &hal->trans_config;
207
208 if (trans->rcv_buffer && !hal->dma_enabled) {
209 //Need to copy from SPI regs to result buffer.
210 spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen);
211 }
212 }
213