spi_hal_iram.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. // The HAL layer for SPI (common part, in iram)
  7. // make these functions in a seperate file to make sure all LL functions are in the IRAM.
  8. #include "hal/spi_hal.h"
  9. #include "hal/assert.h"
  10. #include "soc/ext_mem_defs.h"
  11. #include "soc/soc_caps.h"
  12. //This GDMA related part will be introduced by GDMA dedicated APIs in the future. Here we temporarily use macros.
  13. #if SOC_GDMA_SUPPORTED
  14. #if (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AHB) && (SOC_AHB_GDMA_VERSION == 1)
  15. #include "soc/gdma_struct.h"
  16. #include "hal/gdma_ll.h"
  17. #define spi_dma_ll_rx_reset(dev, chan) gdma_ll_rx_reset_channel(&GDMA, chan)
  18. #define spi_dma_ll_tx_reset(dev, chan) gdma_ll_tx_reset_channel(&GDMA, chan);
  19. #define spi_dma_ll_rx_start(dev, chan, addr) do {\
  20. gdma_ll_rx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\
  21. gdma_ll_rx_start(&GDMA, chan);\
  22. } while (0)
  23. #define spi_dma_ll_tx_start(dev, chan, addr) do {\
  24. gdma_ll_tx_set_desc_addr(&GDMA, chan, (uint32_t)addr);\
  25. gdma_ll_tx_start(&GDMA, chan);\
  26. } while (0)
  27. #elif (SOC_GDMA_TRIG_PERIPH_SPI2_BUS == SOC_GDMA_BUS_AXI) //TODO: IDF-6152, refactor spi hal layer
  28. #include "hal/axi_dma_ll.h"
  29. #define spi_dma_ll_rx_reset(dev, chan) axi_dma_ll_rx_reset_channel(&AXI_DMA, chan)
  30. #define spi_dma_ll_tx_reset(dev, chan) axi_dma_ll_tx_reset_channel(&AXI_DMA, chan);
  31. #define spi_dma_ll_rx_start(dev, chan, addr) do {\
  32. axi_dma_ll_rx_set_desc_addr(&AXI_DMA, chan, (uint32_t)addr);\
  33. axi_dma_ll_rx_start(&AXI_DMA, chan);\
  34. } while (0)
  35. #define spi_dma_ll_tx_start(dev, chan, addr) do {\
  36. axi_dma_ll_tx_set_desc_addr(&AXI_DMA, chan, (uint32_t)addr);\
  37. axi_dma_ll_tx_start(&AXI_DMA, chan);\
  38. } while (0)
  39. #endif
  40. #endif //SOC_GDMA_SUPPORTED
  41. void spi_hal_setup_device(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev)
  42. {
  43. //Configure clock settings
  44. spi_dev_t *hw = hal->hw;
  45. #if SOC_SPI_AS_CS_SUPPORTED
  46. spi_ll_master_set_cksel(hw, dev->cs_pin_id, dev->as_cs);
  47. #endif
  48. spi_ll_master_set_pos_cs(hw, dev->cs_pin_id, dev->positive_cs);
  49. spi_ll_master_set_clock_by_reg(hw, &dev->timing_conf.clock_reg);
  50. //Configure bit order
  51. spi_ll_set_rx_lsbfirst(hw, dev->rx_lsbfirst);
  52. spi_ll_set_tx_lsbfirst(hw, dev->tx_lsbfirst);
  53. spi_ll_master_set_mode(hw, dev->mode);
  54. //Configure misc stuff
  55. spi_ll_set_half_duplex(hw, dev->half_duplex);
  56. spi_ll_set_sio_mode(hw, dev->sio);
  57. //Configure CS pin and timing
  58. spi_ll_master_set_cs_setup(hw, dev->cs_setup);
  59. spi_ll_master_set_cs_hold(hw, dev->cs_hold);
  60. spi_ll_master_select_cs(hw, dev->cs_pin_id);
  61. }
  62. void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans)
  63. {
  64. spi_dev_t *hw = hal->hw;
  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. 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. //SPI iface needs to be configured for a delay in some cases.
  77. //configure dummy bits
  78. spi_ll_set_dummy(hw, extra_dummy + trans->dummy_bits);
  79. uint32_t miso_delay_num = 0;
  80. uint32_t miso_delay_mode = 0;
  81. if (dev->timing_conf.timing_miso_delay < 0) {
  82. //if the data comes too late, delay half a SPI clock to improve reading
  83. switch (dev->mode) {
  84. case 0:
  85. miso_delay_mode = 2;
  86. break;
  87. case 1:
  88. miso_delay_mode = 1;
  89. break;
  90. case 2:
  91. miso_delay_mode = 1;
  92. break;
  93. case 3:
  94. miso_delay_mode = 2;
  95. break;
  96. }
  97. miso_delay_num = 0;
  98. } else {
  99. //if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing
  100. miso_delay_num = extra_dummy ? dev->timing_conf.timing_miso_delay : 0;
  101. miso_delay_mode = 0;
  102. }
  103. spi_ll_set_miso_delay(hw, miso_delay_mode, miso_delay_num);
  104. spi_ll_set_mosi_bitlen(hw, trans->tx_bitlen);
  105. if (dev->half_duplex) {
  106. spi_ll_set_miso_bitlen(hw, trans->rx_bitlen);
  107. } else {
  108. //rxlength is not used in full-duplex mode
  109. spi_ll_set_miso_bitlen(hw, trans->tx_bitlen);
  110. }
  111. //Configure bit sizes, load addr and command
  112. int cmdlen = trans->cmd_bits;
  113. int addrlen = trans->addr_bits;
  114. if (!dev->half_duplex && dev->cs_setup != 0) {
  115. /* The command and address phase is not compatible with cs_ena_pretrans
  116. * in full duplex mode.
  117. */
  118. cmdlen = 0;
  119. addrlen = 0;
  120. }
  121. spi_ll_set_addr_bitlen(hw, addrlen);
  122. spi_ll_set_command_bitlen(hw, cmdlen);
  123. spi_ll_set_command(hw, trans->cmd, cmdlen, dev->tx_lsbfirst);
  124. spi_ll_set_address(hw, trans->addr, addrlen, dev->tx_lsbfirst);
  125. //Configure keep active CS
  126. spi_ll_master_keep_cs(hw, trans->cs_keep_active);
  127. //Save the transaction attributes for internal usage.
  128. memcpy(&hal->trans_config, trans, sizeof(spi_hal_trans_config_t));
  129. }
  130. #if SOC_NON_CACHEABLE_OFFSET
  131. #define ADDR_DMA_2_CPU(addr) ((typeof(addr))((uint32_t)(addr) + SOC_NON_CACHEABLE_OFFSET))
  132. #define ADDR_CPU_2_DMA(addr) ((typeof(addr))((uint32_t)(addr) - SOC_NON_CACHEABLE_OFFSET))
  133. #else
  134. #define ADDR_DMA_2_CPU(addr) (addr)
  135. #define ADDR_CPU_2_DMA(addr) (addr)
  136. #endif
  137. //TODO: IDF-6152, refactor spi hal layer
  138. static void s_spi_hal_dma_desc_setup_link(spi_dma_desc_t *dmadesc, const void *data, int len, bool is_rx)
  139. {
  140. dmadesc = ADDR_DMA_2_CPU(dmadesc);
  141. int n = 0;
  142. while (len) {
  143. int dmachunklen = len;
  144. if (dmachunklen > DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED) {
  145. dmachunklen = DMA_DESCRIPTOR_BUFFER_MAX_SIZE_4B_ALIGNED;
  146. }
  147. if (is_rx) {
  148. //Receive needs DMA length rounded to next 32-bit boundary
  149. dmadesc[n].dw0.size = (dmachunklen + 3) & (~3);
  150. dmadesc[n].dw0.length = (dmachunklen + 3) & (~3);
  151. } else {
  152. dmadesc[n].dw0.size = dmachunklen;
  153. dmadesc[n].dw0.length = dmachunklen;
  154. }
  155. dmadesc[n].buffer = (uint8_t *)data;
  156. dmadesc[n].dw0.suc_eof = 0;
  157. dmadesc[n].dw0.owner = 1;
  158. dmadesc[n].next = ADDR_CPU_2_DMA(&dmadesc[n + 1]);
  159. len -= dmachunklen;
  160. data += dmachunklen;
  161. n++;
  162. }
  163. dmadesc[n - 1].dw0.suc_eof = 1; //Mark last DMA desc as end of stream.
  164. dmadesc[n - 1].next = NULL;
  165. }
  166. void spi_hal_prepare_data(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev, const spi_hal_trans_config_t *trans)
  167. {
  168. spi_dev_t *hw = hal->hw;
  169. //Fill DMA descriptors
  170. if (trans->rcv_buffer) {
  171. if (!hal->dma_enabled) {
  172. //No need to setup anything; we'll copy the result out of the work registers directly later.
  173. } else {
  174. s_spi_hal_dma_desc_setup_link(hal->dmadesc_rx, trans->rcv_buffer, ((trans->rx_bitlen + 7) / 8), true);
  175. spi_dma_ll_rx_reset(hal->dma_in, hal->rx_dma_chan);
  176. spi_ll_dma_rx_fifo_reset(hal->hw);
  177. spi_ll_infifo_full_clr(hal->hw);
  178. spi_ll_dma_rx_enable(hal->hw, 1);
  179. spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, (lldesc_t *)hal->dmadesc_rx);
  180. }
  181. }
  182. #if CONFIG_IDF_TARGET_ESP32
  183. else {
  184. //DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon
  185. if (hal->dma_enabled && !dev->half_duplex) {
  186. spi_ll_dma_rx_enable(hal->hw, 1);
  187. spi_dma_ll_rx_start(hal->dma_in, hal->rx_dma_chan, 0);
  188. }
  189. }
  190. #endif
  191. if (trans->send_buffer) {
  192. if (!hal->dma_enabled) {
  193. //Need to copy data to registers manually
  194. spi_ll_write_buffer(hw, trans->send_buffer, trans->tx_bitlen);
  195. } else {
  196. s_spi_hal_dma_desc_setup_link(hal->dmadesc_tx, trans->send_buffer, (trans->tx_bitlen + 7) / 8, false);
  197. spi_dma_ll_tx_reset(hal->dma_out, hal->tx_dma_chan);
  198. spi_ll_dma_tx_fifo_reset(hal->hw);
  199. spi_ll_outfifo_empty_clr(hal->hw);
  200. spi_ll_dma_tx_enable(hal->hw, 1);
  201. spi_dma_ll_tx_start(hal->dma_out, hal->tx_dma_chan, (lldesc_t *)hal->dmadesc_tx);
  202. }
  203. }
  204. //in ESP32 these registers should be configured after the DMA is set
  205. if ((!dev->half_duplex && trans->rcv_buffer) || trans->send_buffer) {
  206. spi_ll_enable_mosi(hw, 1);
  207. } else {
  208. spi_ll_enable_mosi(hw, 0);
  209. }
  210. spi_ll_enable_miso(hw, (trans->rcv_buffer) ? 1 : 0);
  211. }
  212. void spi_hal_user_start(const spi_hal_context_t *hal)
  213. {
  214. spi_ll_apply_config(hal->hw);
  215. spi_ll_user_start(hal->hw);
  216. }
  217. bool spi_hal_usr_is_done(const spi_hal_context_t *hal)
  218. {
  219. return spi_ll_usr_is_done(hal->hw);
  220. }
  221. void spi_hal_fetch_result(const spi_hal_context_t *hal)
  222. {
  223. const spi_hal_trans_config_t *trans = &hal->trans_config;
  224. if (trans->rcv_buffer && !hal->dma_enabled) {
  225. //Need to copy from SPI regs to result buffer.
  226. spi_ll_read_buffer(hal->hw, trans->rcv_buffer, trans->rx_bitlen);
  227. }
  228. }