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