• 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 /*******************************************************************************
16  * NOTICE
17  * The hal is not public api, don't use in application code.
18  * See readme.md in hal/include/hal/readme.md
19  ******************************************************************************/
20 
21 // The LL layer for ESP32 SDIO slave register operations
22 // It's strange but `tx_*` regs for host->slave transfers while `rx_*` regs for slave->host transfers
23 // To reduce ambiguity, we call (host->slave, tx) transfers receiving and (slave->host, rx) transfers receiving
24 
25 #pragma once
26 
27 #include "hal/sdio_slave_hal.h"
28 #include "soc/slc_struct.h"
29 #include "soc/slc_reg.h"
30 #include "soc/host_struct.h"
31 #include "soc/host_reg.h"
32 #include "soc/hinf_struct.h"
33 #include "soc/lldesc.h"
34 
35 /// Get address of the only SLC registers for ESP32
36 #define sdio_slave_ll_get_slc(ID)   (&SLC)
37 /// Get address of the only HOST registers for ESP32
38 #define sdio_slave_ll_get_host(ID)  (&HOST)
39 /// Get address of the only HINF registers for ESP32
40 #define sdio_slave_ll_get_hinf(ID)  (&HINF)
41 
42 
43 /// Mask of general purpose interrupts sending from the host.
44 typedef enum {
45     SDIO_SLAVE_LL_SLVINT_0 = BIT(0),    ///< General purpose interrupt bit 0.
46     SDIO_SLAVE_LL_SLVINT_1 = BIT(1),
47     SDIO_SLAVE_LL_SLVINT_2 = BIT(2),
48     SDIO_SLAVE_LL_SLVINT_3 = BIT(3),
49     SDIO_SLAVE_LL_SLVINT_4 = BIT(4),
50     SDIO_SLAVE_LL_SLVINT_5 = BIT(5),
51     SDIO_SLAVE_LL_SLVINT_6 = BIT(6),
52     SDIO_SLAVE_LL_SLVINT_7 = BIT(7),
53 } sdio_slave_ll_slvint_t;
54 
55 /**
56  * Initialize the hardware.
57  *
58  * @param slc Address of the SLC registers
59  */
sdio_slave_ll_init(slc_dev_t * slc)60 static inline void sdio_slave_ll_init(slc_dev_t *slc)
61 {
62     slc->slc0_int_ena.val = 0;
63 
64     slc->conf0.slc0_rx_auto_wrback = 1;
65     slc->conf0.slc0_token_auto_clr = 0;
66     slc->conf0.slc0_rx_loop_test = 0;
67     slc->conf0.slc0_tx_loop_test = 0;
68 
69     slc->conf1.slc0_rx_stitch_en = 0;
70     slc->conf1.slc0_tx_stitch_en = 0;
71     slc->conf1.slc0_len_auto_clr = 0;
72 
73     slc->rx_dscr_conf.slc0_token_no_replace = 1;
74 }
75 
76 /**
77  * Set the timing for the communication
78  *
79  * @param host Address of the host registers
80  * @param timing Timing configuration to set
81  */
sdio_slave_ll_set_timing(host_dev_t * host,sdio_slave_timing_t timing)82 static inline void sdio_slave_ll_set_timing(host_dev_t *host, sdio_slave_timing_t timing)
83 {
84     switch(timing) {
85         case SDIO_SLAVE_TIMING_PSEND_PSAMPLE:
86             host->conf.frc_sdio20 = 0x1f;
87             host->conf.frc_sdio11 = 0;
88             host->conf.frc_pos_samp = 0x1f;
89             host->conf.frc_neg_samp = 0;
90             break;
91         case SDIO_SLAVE_TIMING_PSEND_NSAMPLE:
92             host->conf.frc_sdio20 = 0x1f;
93             host->conf.frc_sdio11 = 0;
94             host->conf.frc_pos_samp = 0;
95             host->conf.frc_neg_samp = 0x1f;
96             break;
97         case SDIO_SLAVE_TIMING_NSEND_PSAMPLE:
98             host->conf.frc_sdio20 = 0;
99             host->conf.frc_sdio11 = 0x1f;
100             host->conf.frc_pos_samp = 0x1f;
101             host->conf.frc_neg_samp = 0;
102             break;
103         case SDIO_SLAVE_TIMING_NSEND_NSAMPLE:
104             host->conf.frc_sdio20 = 0;
105             host->conf.frc_sdio11 = 0x1f;
106             host->conf.frc_pos_samp = 0;
107             host->conf.frc_neg_samp = 0x1f;
108             break;
109     }
110 }
111 
112 /**
113  * Set the HS supported bit to be read by the host.
114  *
115  * @param hinf Address of the hinf registers
116  * @param hs true if supported, otherwise false.
117  */
sdio_slave_ll_enable_hs(hinf_dev_t * hinf,bool hs)118 static inline void sdio_slave_ll_enable_hs(hinf_dev_t *hinf, bool hs)
119 {
120     if (hs) {
121         hinf->cfg_data1.sdio_ver = 0x232;
122         hinf->cfg_data1.highspeed_enable = 1;
123     }
124 }
125 
126 /**
127  * Set the IO Ready bit to be read by the host.
128  *
129  * @param hinf Address of the hinf registers
130  * @param ready true if ready, otherwise false.
131  */
sdio_slave_ll_set_ioready(hinf_dev_t * hinf,bool ready)132 static inline void sdio_slave_ll_set_ioready(hinf_dev_t *hinf, bool ready)
133 {
134     hinf->cfg_data1.sdio_ioready1 = (ready ? 1 : 0);   //set IO ready to 1 to stop host from using
135 }
136 
137 /*---------------------------------------------------------------------------
138  *                  Send
139  *--------------------------------------------------------------------------*/
140 /**
141  * Reset the sending DMA.
142  *
143  * @param slc Address of the SLC registers
144  */
sdio_slave_ll_send_reset(slc_dev_t * slc)145 static inline void sdio_slave_ll_send_reset(slc_dev_t *slc)
146 {
147     //reset to flush previous packets
148     slc->conf0.slc0_rx_rst = 1;
149     slc->conf0.slc0_rx_rst = 0;
150 }
151 
152 /**
153  * Start the sending DMA with the given descriptor.
154  *
155  * @param slc Address of the SLC registers
156  * @param desc Descriptor to send
157  */
sdio_slave_ll_send_start(slc_dev_t * slc,const lldesc_t * desc)158 static inline void sdio_slave_ll_send_start(slc_dev_t *slc, const lldesc_t *desc)
159 {
160     slc->slc0_rx_link.addr = (uint32_t)desc;
161     slc->slc0_rx_link.start = 1;
162 }
163 
164 /**
165  * Write the PKT_LEN register to be written by the host to a certain value.
166  *
167  * @param slc Address of the SLC registers
168  * @param len Length to write
169  */
sdio_slave_ll_send_write_len(slc_dev_t * slc,uint32_t len)170 static inline void sdio_slave_ll_send_write_len(slc_dev_t *slc, uint32_t len)
171 {
172     slc->slc0_len_conf.val = FIELD_TO_VALUE2(SLC_SLC0_LEN_WDATA, len) | FIELD_TO_VALUE2(SLC_SLC0_LEN_WR, 1);
173 }
174 
175 /**
176  * Read the value of PKT_LEN register. The register may keep the same until read
177  * by the host.
178  *
179  * @param host Address of the host registers
180  * @return The value of PKT_LEN register.
181  */
sdio_slave_ll_send_read_len(host_dev_t * host)182 static inline uint32_t sdio_slave_ll_send_read_len(host_dev_t *host)
183 {
184     return host->pkt_len.reg_slc0_len;
185 }
186 
187 /**
188  * Enable the rx_done interrupt. (sending)
189  *
190  * @param slc Address of the SLC registers
191  * @param ena true if enable, otherwise false.
192  */
sdio_slave_ll_send_part_done_intr_ena(slc_dev_t * slc,bool ena)193 static inline void sdio_slave_ll_send_part_done_intr_ena(slc_dev_t *slc, bool ena)
194 {
195     slc->slc0_int_ena.rx_done = (ena ? 1 : 0);
196 }
197 
198 /**
199  * Clear the rx_done interrupt. (sending)
200  *
201  * @param slc Address of the SLC registers
202  */
sdio_slave_ll_send_part_done_clear(slc_dev_t * slc)203 static inline void sdio_slave_ll_send_part_done_clear(slc_dev_t *slc)
204 {
205     slc->slc0_int_clr.rx_done = 1;
206 }
207 
208 /**
209  * Check whether the hardware is ready for the SW to use rx_done to invoke
210  * the ISR.
211  *
212  * @param slc Address of the SLC registers
213  * @return true if ready, otherwise false.
214  */
sdio_slave_ll_send_invoker_ready(slc_dev_t * slc)215 static inline bool sdio_slave_ll_send_invoker_ready(slc_dev_t *slc)
216 {
217     return slc->slc0_int_raw.rx_done;
218 }
219 
220 /**
221  * Stop the sending DMA.
222  *
223  * @param slc Address of the SLC registers
224  */
sdio_slave_ll_send_stop(slc_dev_t * slc)225 static inline void sdio_slave_ll_send_stop(slc_dev_t *slc)
226 {
227     slc->slc0_rx_link.stop = 1;
228 }
229 
230 /**
231  * Enable the sending interrupt (rx_eof).
232  *
233  * @param slc Address of the SLC registers
234  * @param ena true to enable, false to disable
235  */
sdio_slave_ll_send_intr_ena(slc_dev_t * slc,bool ena)236 static inline void sdio_slave_ll_send_intr_ena(slc_dev_t *slc, bool ena)
237 {
238     slc->slc0_int_ena.rx_eof = (ena? 1: 0);
239 }
240 
241 /**
242  * Clear the sending interrupt (rx_eof).
243  *
244  * @param slc Address of the SLC registers
245  */
sdio_slave_ll_send_intr_clr(slc_dev_t * slc)246 static inline void sdio_slave_ll_send_intr_clr(slc_dev_t *slc)
247 {
248     slc->slc0_int_clr.rx_eof = 1;
249 }
250 
251 /**
252  * Check whether the sending is done.
253  *
254  * @param slc Address of the SLC registers
255  * @return true if done, otherwise false
256  */
sdio_slave_ll_send_done(slc_dev_t * slc)257 static inline bool sdio_slave_ll_send_done(slc_dev_t *slc)
258 {
259     return slc->slc0_int_st.rx_eof != 0;
260 }
261 
262 /**
263  * Clear the host interrupt indicating the slave having packet to be read.
264  *
265  * @param host Address of the host registers
266  */
sdio_slave_ll_send_hostint_clr(host_dev_t * host)267 static inline void sdio_slave_ll_send_hostint_clr(host_dev_t *host)
268 {
269     host->slc0_int_clr.rx_new_packet = 1;
270 }
271 
272 /*---------------------------------------------------------------------------
273  *                  Receive
274  *--------------------------------------------------------------------------*/
275 /**
276  * Enable the receiving interrupt.
277  *
278  * @param slc Address of the SLC registers
279  * @param ena
280  */
sdio_slave_ll_recv_intr_ena(slc_dev_t * slc,bool ena)281 static inline void sdio_slave_ll_recv_intr_ena(slc_dev_t *slc, bool ena)
282 {
283     slc->slc0_int_ena.tx_done = (ena ? 1 : 0);
284 }
285 
286 /**
287  * Start receiving DMA with the given descriptor.
288  *
289  * @param slc Address of the SLC registers
290  * @param desc Descriptor of the receiving buffer.
291  */
sdio_slave_ll_recv_start(slc_dev_t * slc,lldesc_t * desc)292 static inline void sdio_slave_ll_recv_start(slc_dev_t *slc, lldesc_t *desc)
293 {
294     slc->slc0_tx_link.addr = (uint32_t)desc;
295     slc->slc0_tx_link.start = 1;
296 }
297 
298 /**
299  * Increase the receiving buffer counter by 1.
300  *
301  * @param slc Address of the SLC registers
302  */
sdio_slave_ll_recv_size_inc(slc_dev_t * slc)303 static inline void sdio_slave_ll_recv_size_inc(slc_dev_t *slc)
304 {
305     // fields wdata and inc_more should be written by the same instruction.
306     slc->slc0_token1.val = FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_WDATA, 1) | FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_INC_MORE, 1);
307 }
308 
309 /**
310  * Reset the receiving buffer.
311  *
312  * @param slc Address of the SLC registers
313  */
sdio_slave_ll_recv_size_reset(slc_dev_t * slc)314 static inline void sdio_slave_ll_recv_size_reset(slc_dev_t *slc)
315 {
316     slc->slc0_token1.val = FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_WDATA, 0) | FIELD_TO_VALUE2(SLC_SLC0_TOKEN1_WR, 1);
317 }
318 
319 /**
320  * Check whether there is a receiving finished event.
321  *
322  * @param slc Address of the SLC registers
323  * @return
324  */
sdio_slave_ll_recv_done(slc_dev_t * slc)325 static inline bool sdio_slave_ll_recv_done(slc_dev_t *slc)
326 {
327     return slc->slc0_int_raw.tx_done != 0;
328 }
329 
330 /**
331  * Clear the receiving finished interrupt.
332  *
333  * @param slc Address of the SLC registers
334  */
sdio_slave_ll_recv_done_clear(slc_dev_t * slc)335 static inline void sdio_slave_ll_recv_done_clear(slc_dev_t *slc)
336 {
337     slc->slc0_int_clr.tx_done = 1;
338 }
339 
340 /**
341  * Restart the DMA. Call after you modified the next pointer of the tail descriptor to the appended
342  * descriptor.
343  *
344  * @param slc Address of the SLC registers
345  */
sdio_slave_ll_recv_restart(slc_dev_t * slc)346 static inline void sdio_slave_ll_recv_restart(slc_dev_t *slc)
347 {
348     slc->slc0_tx_link.restart = 1;
349 }
350 
351 /**
352  * Reset the receiving DMA.
353  *
354  * @param slc Address of the SLC registers
355  */
sdio_slave_ll_recv_reset(slc_dev_t * slc)356 static inline void sdio_slave_ll_recv_reset(slc_dev_t *slc)
357 {
358     slc->conf0.slc0_tx_rst = 1;
359     slc->conf0.slc0_tx_rst = 0;
360 }
361 
362 /**
363  * Stop the receiving DMA.
364  *
365  * @param slc Address of the SLC registers
366  */
sdio_slave_ll_recv_stop(slc_dev_t * slc)367 static inline void sdio_slave_ll_recv_stop(slc_dev_t *slc)
368 {
369     slc->slc0_tx_link.stop = 1;
370 }
371 
372 /*---------------------------------------------------------------------------
373  *                  Host
374  *--------------------------------------------------------------------------*/
375 /**
376  * Get the address of the shared general purpose register. Internal.
377  *
378  * @param host Address of the host registers
379  * @param pos Position of the register, 0-63 except 24-27.
380  * @return address of the register.
381  */
sdio_slave_ll_host_get_w_reg(host_dev_t * host,int pos)382 static inline intptr_t sdio_slave_ll_host_get_w_reg(host_dev_t* host, int pos)
383 {
384     return (intptr_t )&(host->conf_w0) + pos + (pos>23?4:0) + (pos>31?12:0);
385 }
386 
387 /**
388  * Get the value of the shared general purpose register.
389  *
390  * @param host Address of the host registers
391  * @param pos Position of the register, 0-63, except 24-27.
392  * @return value of the register.
393  */
sdio_slave_ll_host_get_reg(host_dev_t * host,int pos)394 static inline uint8_t sdio_slave_ll_host_get_reg(host_dev_t *host, int pos)
395 {
396     return *(uint8_t*)sdio_slave_ll_host_get_w_reg(host, pos);
397 }
398 
399 /**
400  * Set the value of the shared general purpose register.
401  *
402  * @param host Address of the host registers
403  * @param pos Position of the register, 0-63, except 24-27.
404  * @param reg Value to set.
405  */
sdio_slave_ll_host_set_reg(host_dev_t * host,int pos,uint8_t reg)406 static inline void sdio_slave_ll_host_set_reg(host_dev_t* host, int pos, uint8_t reg)
407 {
408     uint32_t* addr = (uint32_t*)(sdio_slave_ll_host_get_w_reg(host, pos) & (~3));
409     uint32_t shift = (pos % 4) * 8;
410     *addr &= ~(0xff << shift);
411     *addr |= ((uint32_t)reg << shift);
412 }
413 
414 /**
415  * Get the interrupt enable bits for the host.
416  *
417  * @param host Address of the host registers
418  * @return Enabled interrupts
419  */
sdio_slave_ll_host_get_intena(host_dev_t * host)420 static inline sdio_slave_hostint_t sdio_slave_ll_host_get_intena(host_dev_t* host)
421 {
422     return host->slc0_func1_int_ena.val;
423 }
424 
425 /**
426  * Set the interrupt enable bits for the host.
427  *
428  * @param host Address of the host registers
429  * @param mask Mask of interrupts to enable
430  */
sdio_slave_ll_host_set_intena(host_dev_t * host,const sdio_slave_hostint_t * mask)431 static inline void sdio_slave_ll_host_set_intena(host_dev_t *host, const sdio_slave_hostint_t *mask)
432 {
433     host->slc0_func1_int_ena.val = (*mask);
434 }
435 
436 /**
437  * Clear the interrupt bits for the host.
438  * @param host Address of the host registers
439  * @param mask Mask of interrupts to clear.
440  */
sdio_slave_ll_host_intr_clear(host_dev_t * host,const sdio_slave_hostint_t * mask)441 static inline void sdio_slave_ll_host_intr_clear(host_dev_t* host, const sdio_slave_hostint_t *mask)
442 {
443     host->slc0_int_clr.val = (*mask);
444 }
445 
446 /**
447  * Send general purpose interrupts to the host.
448  * @param slc Address of the SLC registers
449  * @param mask Mask of interrupts to seend to host
450  */
sdio_slave_ll_host_send_int(slc_dev_t * slc,const sdio_slave_hostint_t * mask)451 static inline void sdio_slave_ll_host_send_int(slc_dev_t *slc, const sdio_slave_hostint_t *mask)
452 {
453     //use registers in SLC to trigger, rather than write HOST registers directly
454     //other interrupts than tohost interrupts are not supported yet
455     slc->intvec_tohost.slc0_intvec = (*mask);
456 }
457 
458 /**
459  * Enable some of the slave interrups (send from host)
460  *
461  * @param slc Address of the SLC registers
462  * @param mask Mask of interrupts to enable, all those set to 0 will be disabled.
463  */
sdio_slave_ll_slvint_set_ena(slc_dev_t * slc,const sdio_slave_ll_slvint_t * mask)464 static inline void sdio_slave_ll_slvint_set_ena(slc_dev_t *slc, const sdio_slave_ll_slvint_t *mask)
465 {
466     //other interrupts are not enabled
467     slc->slc0_int_ena.val = (slc->slc0_int_ena.val & (~0xff)) | ((*mask) & 0xff);
468 }
469 
470 /**
471  * Fetch the slave interrupts (send from host) and clear them.
472  *
473  * @param slc Address of the SLC registers
474  * @param out_slv_int Output of the slave interrupts fetched and cleared.
475  */
sdio_slave_ll_slvint_fetch_clear(slc_dev_t * slc,sdio_slave_ll_slvint_t * out_slv_int)476 static inline void sdio_slave_ll_slvint_fetch_clear(slc_dev_t *slc, sdio_slave_ll_slvint_t *out_slv_int)
477 {
478     sdio_slave_ll_slvint_t slv_int = slc->slc0_int_st.val & 0xff;
479     *out_slv_int = slv_int;
480     slc->slc0_int_clr.val = slv_int;
481 }
482