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