1 /*
2  * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <string.h> // For memset()
10 #include <stdlib.h> // For abort()
11 #include "sdkconfig.h"
12 #include "soc/chip_revision.h"
13 #include "hal/usb_dwc_hal.h"
14 #include "hal/usb_dwc_ll.h"
15 #include "hal/efuse_hal.h"
16 #include "hal/assert.h"
17 
18 // ------------------------------------------------ Macros and Types ---------------------------------------------------
19 
20 // ---------------------- Constants ------------------------
21 
22 #define BENDPOINTADDRESS_NUM_MSK     0x0F   //Endpoint number mask of the bEndpointAddress field of an endpoint descriptor
23 #define BENDPOINTADDRESS_DIR_MSK     0x80   //Endpoint direction mask of the bEndpointAddress field of an endpoint descriptor
24 
25 #define CORE_REG_GSNPSID    0x4F54400A      //Release number of USB_DWC used in Espressif's SoCs
26 
27 // -------------------- Configurable -----------------------
28 
29 /**
30  * The following core interrupts will be enabled (listed LSB to MSB). Some of these
31  * interrupts are enabled later than others.
32  * - USB_DWC_LL_INTR_CORE_PRTINT
33  * - USB_DWC_LL_INTR_CORE_HCHINT
34  * - USB_DWC_LL_INTR_CORE_DISCONNINT
35  * The following PORT interrupts cannot be masked, listed LSB to MSB
36  * - USB_DWC_LL_INTR_HPRT_PRTCONNDET
37  * - USB_DWC_LL_INTR_HPRT_PRTENCHNG
38  * - USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG
39  */
40 #define CORE_INTRS_EN_MSK   (USB_DWC_LL_INTR_CORE_DISCONNINT)
41 
42 //Interrupts that pertain to core events
43 #define CORE_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_CORE_DISCONNINT | \
44                                USB_DWC_LL_INTR_CORE_HCHINT)
45 
46 //Interrupt that pertain to host port events
47 #define PORT_EVENTS_INTRS_MSK (USB_DWC_LL_INTR_HPRT_PRTCONNDET | \
48                                USB_DWC_LL_INTR_HPRT_PRTENCHNG | \
49                                USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG)
50 
51 /**
52  * The following channel interrupt bits are currently checked (in order LSB to MSB)
53  * - USB_DWC_LL_INTR_CHAN_XFERCOMPL
54  * - USB_DWC_LL_INTR_CHAN_CHHLTD
55  * - USB_DWC_LL_INTR_CHAN_STALL
56  * - USB_DWC_LL_INTR_CHAN_BBLEER
57  * - USB_DWC_LL_INTR_CHAN_BNAINTR
58  * - USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR
59  *
60  * Note the following points about channel interrupts:
61  * - Not all bits are unmaskable under scatter/gather
62  * - Those bits proxy their interrupt through the USB_DWC_LL_INTR_CHAN_CHHLTD bit
63  * - USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR is always unmasked
64  * - When USB_DWC_LL_INTR_CHAN_BNAINTR occurs, USB_DWC_LL_INTR_CHAN_CHHLTD will NOT.
65  * - USB_DWC_LL_INTR_CHAN_AHBERR doesn't actually ever happen on our system (i.e., ESP32-S2, ESP32-S3):
66  *      - If the QTD list's starting address is an invalid address (e.g., NULL), the core will attempt to fetch that
67  *        address for a transfer descriptor and probably gets all zeroes. It will interpret the zero as a bad QTD and
68  *        return a USB_DWC_LL_INTR_CHAN_BNAINTR instead.
69  *      - If the QTD's buffer pointer is an invalid address, the core will attempt to read/write data to/from that
70  *        invalid buffer address with NO INDICATION OF ERROR. The transfer will be acknowledged and treated as
71  *        successful. Bad buffer pointers MUST BE CHECKED FROM HIGHER LAYERS INSTEAD.
72  */
73 #define CHAN_INTRS_EN_MSK   (USB_DWC_LL_INTR_CHAN_XFERCOMPL | \
74                              USB_DWC_LL_INTR_CHAN_CHHLTD | \
75                              USB_DWC_LL_INTR_CHAN_BNAINTR)
76 
77 #define CHAN_INTRS_ERROR_MSK  (USB_DWC_LL_INTR_CHAN_STALL | \
78                                USB_DWC_LL_INTR_CHAN_BBLEER | \
79                                USB_DWC_LL_INTR_CHAN_BNAINTR | \
80                                USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR)
81 
82 // -------------------------------------------------- Core (Global) ----------------------------------------------------
83 
set_defaults(usb_dwc_hal_context_t * hal)84 static void set_defaults(usb_dwc_hal_context_t *hal)
85 {
86     //GAHBCFG register
87     usb_dwc_ll_gahbcfg_en_dma_mode(hal->dev);
88     int hbstlen = 0;    //Use AHB burst SINGLE by default
89 #if CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP32S2_REV_MIN_FULL < 100
90     /*
91     Hardware errata workaround for the ESP32-S2 ECO0 (see ESP32-S2 Errata Document section 4.0 for full details).
92 
93     ESP32-S2 ECO0 has a hardware errata where the AHB bus arbiter may generate incorrect arbitration signals leading to
94     the DWC_OTG corrupting the DMA transfers of other peripherals (or vice versa) on the same bus. The peripherals that
95     share the same bus with DWC_OTG include I2C and SPI (see ESP32-S2 Errata Document for more details). To workaround
96     this, the DWC_OTG's AHB should use INCR mode to prevent change of arbitration during a burst operation, thus
97     avoiding this errata.
98 
99     Note: Setting AHB burst to INCR increases the likeliness of DMA underruns on other peripherals sharing the same bus
100     arbiter as the DWC_OTG (e.g., I2C and SPI) as change of arbitration during the burst operation is not permitted.
101     Users should keep this limitation in mind when the DWC_OTG transfers large data payloads (e.g., 512 MPS transfers)
102     while this workaround is enabled.
103     */
104     if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 100)) {
105         hbstlen = 1;    //Set AHB burst to INCR to workaround hardware errata
106     }
107 #endif //CONFIG_IDF_TARGET_ESP32S2 && CONFIG_ESP32S2_REV_MIN_FULL < 100
108     usb_dwc_ll_gahbcfg_set_hbstlen(hal->dev, hbstlen);  //Set AHB burst mode
109     //GUSBCFG register
110     usb_dwc_ll_gusbcfg_dis_hnp_cap(hal->dev);       //Disable HNP
111     usb_dwc_ll_gusbcfg_dis_srp_cap(hal->dev);       //Disable SRP
112     //Enable interruts
113     usb_dwc_ll_gintmsk_dis_intrs(hal->dev, 0xFFFFFFFF);     //Mask all interrupts first
114     usb_dwc_ll_gintmsk_en_intrs(hal->dev, CORE_INTRS_EN_MSK);   //Unmask global interrupts
115     usb_dwc_ll_gintsts_read_and_clear_intrs(hal->dev);   //Clear interrupts
116     usb_dwc_ll_gahbcfg_en_global_intr(hal->dev);        //Enable interrupt signal
117     //Enable host mode
118     usb_dwc_ll_gusbcfg_force_host_mode(hal->dev);
119 }
120 
usb_dwc_hal_init(usb_dwc_hal_context_t * hal)121 void usb_dwc_hal_init(usb_dwc_hal_context_t *hal)
122 {
123     //Check if a peripheral is alive by reading the core ID registers
124     usb_dwc_dev_t *dev = &USB_DWC;
125     uint32_t core_id = usb_dwc_ll_gsnpsid_get_id(dev);
126     HAL_ASSERT(core_id == CORE_REG_GSNPSID);
127     (void) core_id;     //Suppress unused variable warning if asserts are disabled
128     //Initialize HAL context
129     memset(hal, 0, sizeof(usb_dwc_hal_context_t));
130     hal->dev = dev;
131     set_defaults(hal);
132 }
133 
usb_dwc_hal_deinit(usb_dwc_hal_context_t * hal)134 void usb_dwc_hal_deinit(usb_dwc_hal_context_t *hal)
135 {
136     //Disable and clear global interrupt
137     usb_dwc_ll_gintmsk_dis_intrs(hal->dev, 0xFFFFFFFF); //Disable all interrupts
138     usb_dwc_ll_gintsts_read_and_clear_intrs(hal->dev);   //Clear interrupts
139     usb_dwc_ll_gahbcfg_dis_global_intr(hal->dev);       //Disable interrupt signal
140     hal->dev = NULL;
141 }
142 
usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t * hal)143 void usb_dwc_hal_core_soft_reset(usb_dwc_hal_context_t *hal)
144 {
145     usb_dwc_ll_grstctl_core_soft_reset(hal->dev);
146     while (usb_dwc_ll_grstctl_is_core_soft_reset_in_progress(hal->dev)) {
147         ;   //Wait until core reset is done
148     }
149     while (!usb_dwc_ll_grstctl_is_ahb_idle(hal->dev)) {
150         ;   //Wait until AHB Master bus is idle before doing any other operations
151     }
152     //Set the default bits
153     set_defaults(hal);
154     //Clear all the flags and channels
155     hal->periodic_frame_list = NULL;
156     hal->flags.val = 0;
157     hal->channels.num_allocd = 0;
158     hal->channels.chan_pend_intrs_msk = 0;
159     memset(hal->channels.hdls, 0, sizeof(usb_dwc_hal_chan_t *) * OTG_NUM_HOST_CHAN);
160 }
161 
usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t * hal,const usb_hal_fifo_bias_t fifo_bias)162 void usb_dwc_hal_set_fifo_bias(usb_dwc_hal_context_t *hal, const usb_hal_fifo_bias_t fifo_bias)
163 {
164     /*
165     * EPINFO_CTL is located at the end of FIFO, its size is fixed in HW.
166     * The reserved size is always the worst-case, which is device mode that requires 4 locations per EP direction (including EP0).
167     * Here we just read the FIFO size from HW register, to avoid any ambivalence
168     */
169     uint32_t ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4;
170     usb_dwc_ll_ghwcfg_get_hw_config(hal->dev, &ghwcfg1, &ghwcfg2, &ghwcfg3, &ghwcfg4);
171     const uint16_t fifo_size_lines = ((usb_dwc_ghwcfg3_reg_t)ghwcfg3).dfifodepth;
172 
173     /*
174     * Recommended FIFO sizes (see 2.1.2.4 for programming guide)
175     *
176     * RXFIFO:   ((LPS/4) * 2) + 2
177     * NPTXFIFO: (LPS/4) * 2
178     * PTXFIFO:  (LPS/4) * 2
179     *
180     * Recommended sizes fit 2 packets of each type. For S2 and S3 we can't fit even one MPS ISOC packet (1023 FS and 1024 HS).
181     * So the calculations below are compromises between the available FIFO size and optimal performance.
182     */
183     usb_dwc_hal_fifo_config_t fifo_config;
184     switch (fifo_bias) {
185         // Define minimum viable (fits at least 1 MPS) FIFO sizes for non-biased FIFO types
186         // Allocate the remaining size to the biased FIFO type
187         case USB_HAL_FIFO_BIAS_DEFAULT:
188             fifo_config.nptx_fifo_lines = OTG_DFIFO_DEPTH / 4;
189             fifo_config.ptx_fifo_lines  = OTG_DFIFO_DEPTH / 8;
190             fifo_config.rx_fifo_lines   = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines;
191             break;
192         case USB_HAL_FIFO_BIAS_RX:
193             fifo_config.nptx_fifo_lines = OTG_DFIFO_DEPTH / 16;
194             fifo_config.ptx_fifo_lines  = OTG_DFIFO_DEPTH / 8;
195             fifo_config.rx_fifo_lines   = fifo_size_lines - fifo_config.ptx_fifo_lines - fifo_config.nptx_fifo_lines;
196             break;
197         case USB_HAL_FIFO_BIAS_PTX:
198             fifo_config.rx_fifo_lines   = OTG_DFIFO_DEPTH / 8 + 2; // 2 extra lines are allocated for status information. See USB-OTG Programming Guide, chapter 2.1.2.1
199             fifo_config.nptx_fifo_lines = OTG_DFIFO_DEPTH / 16;
200             fifo_config.ptx_fifo_lines  = fifo_size_lines - fifo_config.nptx_fifo_lines - fifo_config.rx_fifo_lines;
201             break;
202         default:
203             abort();
204     }
205 
206     HAL_ASSERT((fifo_config.rx_fifo_lines + fifo_config.nptx_fifo_lines + fifo_config.ptx_fifo_lines) <= fifo_size_lines);
207     //Check that none of the channels are active
208     for (int i = 0; i < OTG_NUM_HOST_CHAN; i++) {
209         if (hal->channels.hdls[i] != NULL) {
210             HAL_ASSERT(!hal->channels.hdls[i]->flags.active);
211         }
212     }
213     //Set the new FIFO lengths
214     usb_dwc_ll_grxfsiz_set_fifo_size(hal->dev, fifo_config.rx_fifo_lines);
215     usb_dwc_ll_gnptxfsiz_set_fifo_size(hal->dev, fifo_config.rx_fifo_lines, fifo_config.nptx_fifo_lines);
216     usb_dwc_ll_hptxfsiz_set_ptx_fifo_size(hal->dev, fifo_config.rx_fifo_lines + fifo_config.nptx_fifo_lines, fifo_config.ptx_fifo_lines);
217     //Flush the FIFOs
218     usb_dwc_ll_grstctl_flush_nptx_fifo(hal->dev);
219     usb_dwc_ll_grstctl_flush_ptx_fifo(hal->dev);
220     usb_dwc_ll_grstctl_flush_rx_fifo(hal->dev);
221     hal->fifo_config = fifo_config; // Implicit struct copy
222     hal->flags.fifo_sizes_set = 1;
223 }
224 
usb_dwc_hal_get_mps_limits(usb_dwc_hal_context_t * hal,usb_hal_fifo_mps_limits_t * mps_limits)225 void usb_dwc_hal_get_mps_limits(usb_dwc_hal_context_t *hal, usb_hal_fifo_mps_limits_t *mps_limits)
226 {
227     HAL_ASSERT(hal && mps_limits);
228     HAL_ASSERT(hal->flags.fifo_sizes_set);
229 
230     const usb_dwc_hal_fifo_config_t *fifo_config = &(hal->fifo_config);
231     mps_limits->in_mps = (fifo_config->rx_fifo_lines - 2) * 4; // Two lines are reserved for status quadlets internally by USB_DWC
232     mps_limits->non_periodic_out_mps = fifo_config->nptx_fifo_lines * 4;
233     mps_limits->periodic_out_mps = fifo_config->ptx_fifo_lines * 4;
234 }
235 
236 // ---------------------------------------------------- Host Port ------------------------------------------------------
237 
debounce_lock_enable(usb_dwc_hal_context_t * hal)238 static inline void debounce_lock_enable(usb_dwc_hal_context_t *hal)
239 {
240     //Disable the hprt (connection) and disconnection interrupts to prevent repeated triggerings
241     usb_dwc_ll_gintmsk_dis_intrs(hal->dev, USB_DWC_LL_INTR_CORE_PRTINT | USB_DWC_LL_INTR_CORE_DISCONNINT);
242     hal->flags.dbnc_lock_enabled = 1;
243 }
244 
usb_dwc_hal_port_enable(usb_dwc_hal_context_t * hal)245 void usb_dwc_hal_port_enable(usb_dwc_hal_context_t *hal)
246 {
247     usb_dwc_speed_t speed = usb_dwc_ll_hprt_get_speed(hal->dev);
248     //Host Configuration
249     usb_dwc_ll_hcfg_set_defaults(hal->dev, speed);
250     //Configure HFIR
251     usb_dwc_ll_hfir_set_defaults(hal->dev, speed);
252 }
253 
254 // ----------------------------------------------------- Channel -------------------------------------------------------
255 
256 // ----------------- Channel Allocation --------------------
257 
usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t * hal,usb_dwc_hal_chan_t * chan_obj,void * chan_ctx)258 bool usb_dwc_hal_chan_alloc(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, void *chan_ctx)
259 {
260     HAL_ASSERT(hal->flags.fifo_sizes_set);  //FIFO sizes should be set befor attempting to allocate a channel
261     //Attempt to allocate channel
262     if (hal->channels.num_allocd == OTG_NUM_HOST_CHAN) {
263         return false;    //Out of free channels
264     }
265     int chan_idx = -1;
266     for (int i = 0; i < OTG_NUM_HOST_CHAN; i++) {
267         if (hal->channels.hdls[i] == NULL) {
268             hal->channels.hdls[i] = chan_obj;
269             chan_idx = i;
270             hal->channels.num_allocd++;
271             break;
272         }
273     }
274     HAL_ASSERT(chan_idx != -1);
275     //Initialize channel object
276     memset(chan_obj, 0, sizeof(usb_dwc_hal_chan_t));
277     chan_obj->flags.chan_idx = chan_idx;
278     chan_obj->regs = usb_dwc_ll_chan_get_regs(hal->dev, chan_idx);
279     chan_obj->chan_ctx = chan_ctx;
280     //Note: EP characteristics configured separately
281     //Clean and unmask the channel's interrupt
282     usb_dwc_ll_hcint_read_and_clear_intrs(chan_obj->regs);            //Clear the interrupt bits for that channel
283     usb_dwc_ll_haintmsk_en_chan_intr(hal->dev, 1 << chan_obj->flags.chan_idx);
284     usb_dwc_ll_hcintmsk_set_intr_mask(chan_obj->regs, CHAN_INTRS_EN_MSK);  //Unmask interrupts for this channel
285     usb_dwc_ll_hctsiz_set_pid(chan_obj->regs, 0);        //Set the initial PID to zero
286     usb_dwc_ll_hctsiz_init(chan_obj->regs);       //Set the non changing parts of the HCTSIZ registers (e.g., do_ping and sched info)
287     return true;
288 }
289 
usb_dwc_hal_chan_free(usb_dwc_hal_context_t * hal,usb_dwc_hal_chan_t * chan_obj)290 void usb_dwc_hal_chan_free(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj)
291 {
292     if (chan_obj->type == USB_DWC_XFER_TYPE_INTR || chan_obj->type == USB_DWC_XFER_TYPE_ISOCHRONOUS) {
293         //Unschedule this channel
294         for (int i = 0; i < hal->frame_list_len; i++) {
295             hal->periodic_frame_list[i] &= ~(1 << chan_obj->flags.chan_idx);
296         }
297     }
298     //Can only free a channel when in the disabled state and descriptor list released
299     HAL_ASSERT(!chan_obj->flags.active);
300     //Disable channel's interrupt
301     usb_dwc_ll_haintmsk_dis_chan_intr(hal->dev, 1 << chan_obj->flags.chan_idx);
302     //Deallocate channel
303     hal->channels.hdls[chan_obj->flags.chan_idx] = NULL;
304     hal->channels.num_allocd--;
305     HAL_ASSERT(hal->channels.num_allocd >= 0);
306 }
307 
308 // ---------------- Channel Configuration ------------------
309 
usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t * hal,usb_dwc_hal_chan_t * chan_obj,usb_dwc_hal_ep_char_t * ep_char)310 void usb_dwc_hal_chan_set_ep_char(usb_dwc_hal_context_t *hal, usb_dwc_hal_chan_t *chan_obj, usb_dwc_hal_ep_char_t *ep_char)
311 {
312     //Cannot change ep_char whilst channel is still active or in error
313     HAL_ASSERT(!chan_obj->flags.active);
314     //Set the endpoint characteristics of the pipe
315     usb_dwc_ll_hcchar_init(chan_obj->regs,
316                              ep_char->dev_addr,
317                              ep_char->bEndpointAddress & BENDPOINTADDRESS_NUM_MSK,
318                              ep_char->mps,
319                              ep_char->type,
320                              ep_char->bEndpointAddress & BENDPOINTADDRESS_DIR_MSK,
321                              ep_char->ls_via_fs_hub);
322     //Save channel type
323     chan_obj->type = ep_char->type;
324     //If this is a periodic endpoint/channel, set its schedule in the frame list
325     if (ep_char->type == USB_DWC_XFER_TYPE_ISOCHRONOUS || ep_char->type == USB_DWC_XFER_TYPE_INTR) {
326         unsigned int interval_frame_list = ep_char->periodic.interval;
327         unsigned int offset_frame_list = ep_char->periodic.offset;
328         // Periodic Frame List works with USB frames. For HS endpoints we must divide interval[microframes] by 8 to get interval[frames]
329         if (ep_char->periodic.is_hs) {
330             interval_frame_list /= 8;
331             offset_frame_list /= 8;
332         }
333         // Interval in Periodic Frame List must be power of 2.
334         // This is not a HW restriction. It is just a lot easier to schedule channels like this.
335         if (interval_frame_list >= (int)hal->frame_list_len) { // Upper limits is Periodic Frame List length
336             interval_frame_list = (int)hal->frame_list_len;
337         } else if (interval_frame_list >= 32) {
338             interval_frame_list = 32;
339         } else if (interval_frame_list >= 16) {
340             interval_frame_list = 16;
341         } else if (interval_frame_list >= 8) {
342             interval_frame_list = 8;
343         } else if (interval_frame_list >= 4) {
344             interval_frame_list = 4;
345         } else if (interval_frame_list >= 2) {
346             interval_frame_list = 2;
347         } else {                                           // Lower limit is 1
348             interval_frame_list = 1;
349         }
350         // Schedule the channel in the frame list
351         for (int i = 0; i < hal->frame_list_len; i+= interval_frame_list) {
352             int index = (offset_frame_list + i) % hal->frame_list_len;
353             hal->periodic_frame_list[index] |= 1 << chan_obj->flags.chan_idx;
354         }
355         // For HS endpoints we must write to sched_info field of HCTSIZ register to schedule microframes
356         if (ep_char->periodic.is_hs) {
357             unsigned int tokens_per_frame;
358             if (ep_char->periodic.interval >= 8) {
359                 tokens_per_frame = 1; // 1 token every 8 microframes
360             } else if (ep_char->periodic.interval >= 4) {
361                 tokens_per_frame = 2; // 1 token every 4 microframes
362             } else if (ep_char->periodic.interval >= 2) {
363                 tokens_per_frame = 4; // 1 token every 2 microframes
364             } else {
365                 tokens_per_frame = 8; // 1 token every microframe
366             }
367             usb_dwc_ll_hctsiz_set_sched_info(chan_obj->regs, tokens_per_frame, ep_char->periodic.offset);
368         }
369     }
370 }
371 
372 // ------------------- Channel Control ---------------------
373 
usb_dwc_hal_chan_activate(usb_dwc_hal_chan_t * chan_obj,void * xfer_desc_list,int desc_list_len,int start_idx)374 void usb_dwc_hal_chan_activate(usb_dwc_hal_chan_t *chan_obj, void *xfer_desc_list, int desc_list_len, int start_idx)
375 {
376     //Cannot activate a channel that has already been enabled or is pending error handling
377     HAL_ASSERT(!chan_obj->flags.active);
378     //Set start address of the QTD list and starting QTD index
379     usb_dwc_ll_hcdma_set_qtd_list_addr(chan_obj->regs, xfer_desc_list, start_idx);
380     usb_dwc_ll_hctsiz_set_qtd_list_len(chan_obj->regs, desc_list_len);
381     usb_dwc_ll_hcchar_enable_chan(chan_obj->regs); //Start the channel
382     chan_obj->flags.active = 1;
383 }
384 
usb_dwc_hal_chan_request_halt(usb_dwc_hal_chan_t * chan_obj)385 bool usb_dwc_hal_chan_request_halt(usb_dwc_hal_chan_t *chan_obj)
386 {
387     if (chan_obj->flags.active) {
388         /*
389         Request a halt so long as the channel's active flag is set.
390         - If the underlying hardware channel is already halted but the channel is pending interrupt handling,
391         disabling the channel will have no effect (i.e., no channel interrupt is generated).
392         - If the underlying channel is currently active, disabling the channel will trigger a channel interrupt.
393 
394         Regardless, setting the "halt_requested" should cause "usb_dwc_hal_chan_decode_intr()" to report the
395         USB_DWC_HAL_CHAN_EVENT_HALT_REQ event when channel interrupt is handled (pending or triggered).
396         */
397         usb_dwc_ll_hcchar_disable_chan(chan_obj->regs);
398         chan_obj->flags.halt_requested = 1;
399         return false;
400     } else {
401         //Channel was never active to begin with, simply return true
402         return true;
403     }
404 }
405 
406 // ------------------------------------------------- Event Handling ----------------------------------------------------
407 
usb_dwc_hal_decode_intr(usb_dwc_hal_context_t * hal)408 usb_dwc_hal_port_event_t usb_dwc_hal_decode_intr(usb_dwc_hal_context_t *hal)
409 {
410     uint32_t intrs_core = usb_dwc_ll_gintsts_read_and_clear_intrs(hal->dev);  //Read and clear core interrupts
411     uint32_t intrs_port = 0;
412     if (intrs_core & USB_DWC_LL_INTR_CORE_PRTINT) {
413         //There are host port interrupts. Read and clear those as well.
414         intrs_port = usb_dwc_ll_hprt_intr_read_and_clear(hal->dev);
415     }
416     //Note: Do not change order of checks. Regressing events (e.g. enable -> disabled, connected -> connected)
417     //always take precedence. ENABLED < DISABLED < CONN < DISCONN < OVRCUR
418     usb_dwc_hal_port_event_t event = USB_DWC_HAL_PORT_EVENT_NONE;
419 
420     //Check if this is a core or port event
421     if ((intrs_core & CORE_EVENTS_INTRS_MSK) || (intrs_port & PORT_EVENTS_INTRS_MSK)) {
422         //Do not change the order of the following checks. Some events/interrupts take precedence over others
423         if (intrs_core & USB_DWC_LL_INTR_CORE_DISCONNINT) {
424             event = USB_DWC_HAL_PORT_EVENT_DISCONN;
425             debounce_lock_enable(hal);
426             //Mask the port connection and disconnection interrupts to prevent repeated triggering
427         } else if (intrs_port & USB_DWC_LL_INTR_HPRT_PRTOVRCURRCHNG) {
428             //Check if this is an overcurrent or an overcurrent cleared
429             if (usb_dwc_ll_hprt_get_port_overcur(hal->dev)) {
430                 event = USB_DWC_HAL_PORT_EVENT_OVRCUR;
431             } else {
432                 event = USB_DWC_HAL_PORT_EVENT_OVRCUR_CLR;
433             }
434         } else if (intrs_port & USB_DWC_LL_INTR_HPRT_PRTENCHNG) {
435             if (usb_dwc_ll_hprt_get_port_en(hal->dev)) {   //Host port was enabled
436                 event = USB_DWC_HAL_PORT_EVENT_ENABLED;
437             } else {    //Host port has been disabled
438                 event = USB_DWC_HAL_PORT_EVENT_DISABLED;
439             }
440         } else if (intrs_port & USB_DWC_LL_INTR_HPRT_PRTCONNDET && !hal->flags.dbnc_lock_enabled) {
441             event = USB_DWC_HAL_PORT_EVENT_CONN;
442             debounce_lock_enable(hal);
443         }
444     }
445     //Port events always take precedence over channel events
446     if (event == USB_DWC_HAL_PORT_EVENT_NONE && (intrs_core & USB_DWC_LL_INTR_CORE_HCHINT)) {
447         //One or more channels have pending interrupts. Store the mask of those channels
448         hal->channels.chan_pend_intrs_msk = usb_dwc_ll_haint_get_chan_intrs(hal->dev);
449         event = USB_DWC_HAL_PORT_EVENT_CHAN;
450     }
451 
452     return event;
453 }
454 
usb_dwc_hal_get_chan_pending_intr(usb_dwc_hal_context_t * hal)455 usb_dwc_hal_chan_t *usb_dwc_hal_get_chan_pending_intr(usb_dwc_hal_context_t *hal)
456 {
457     int chan_num = __builtin_ffs(hal->channels.chan_pend_intrs_msk);
458     if (chan_num) {
459         hal->channels.chan_pend_intrs_msk &= ~(1 << (chan_num - 1));      //Clear the pending bit for that channel
460         return hal->channels.hdls[chan_num - 1];
461     } else {
462         return NULL;
463     }
464 }
465 
usb_dwc_hal_chan_decode_intr(usb_dwc_hal_chan_t * chan_obj)466 usb_dwc_hal_chan_event_t usb_dwc_hal_chan_decode_intr(usb_dwc_hal_chan_t *chan_obj)
467 {
468     uint32_t chan_intrs = usb_dwc_ll_hcint_read_and_clear_intrs(chan_obj->regs);
469     usb_dwc_hal_chan_event_t chan_event;
470     //Note: We don't assert on (chan_obj->flags.active) here as it could have been already cleared by usb_dwc_hal_chan_request_halt()
471 
472     /*
473     Note: Do not change order of checks as some events take precedence over others.
474     Errors > Channel Halt Request > Transfer completed
475     */
476     if (chan_intrs & CHAN_INTRS_ERROR_MSK) {    //Note: Errors are uncommon, so we check against the entire interrupt mask to reduce frequency of entering this call path
477         HAL_ASSERT(chan_intrs & USB_DWC_LL_INTR_CHAN_CHHLTD);  //An error should have halted the channel
478         //Store the error in hal context
479         usb_dwc_hal_chan_error_t error;
480         if (chan_intrs & USB_DWC_LL_INTR_CHAN_STALL) {
481             error = USB_DWC_HAL_CHAN_ERROR_STALL;
482         } else if (chan_intrs & USB_DWC_LL_INTR_CHAN_BBLEER) {
483             error = USB_DWC_HAL_CHAN_ERROR_PKT_BBL;
484         } else if (chan_intrs & USB_DWC_LL_INTR_CHAN_BNAINTR) {
485             error = USB_DWC_HAL_CHAN_ERROR_BNA;
486         } else {    //USB_DWC_LL_INTR_CHAN_XCS_XACT_ERR
487             error = USB_DWC_HAL_CHAN_ERROR_XCS_XACT;
488         }
489         //Update flags
490         chan_obj->error = error;
491         chan_obj->flags.active = 0;
492         //Save the error to be handled later
493         chan_event = USB_DWC_HAL_CHAN_EVENT_ERROR;
494     } else if (chan_intrs & USB_DWC_LL_INTR_CHAN_CHHLTD) {
495         if (chan_obj->flags.halt_requested) {
496             chan_obj->flags.halt_requested = 0;
497             chan_event = USB_DWC_HAL_CHAN_EVENT_HALT_REQ;
498         } else {
499             //Must have been halted due to QTD HOC
500             chan_event = USB_DWC_HAL_CHAN_EVENT_CPLT;
501         }
502         chan_obj->flags.active = 0;
503     } else if (chan_intrs & USB_DWC_LL_INTR_CHAN_XFERCOMPL) {
504         /*
505         A transfer complete interrupt WITHOUT the channel halting only occurs when receiving a short interrupt IN packet
506         and the underlying QTD does not have the HOC bit set. This signifies the last packet of the Interrupt transfer
507         as all interrupt packets must MPS sized except the last.
508         */
509         //The channel isn't halted yet, so we need to halt it manually to stop the execution of the next QTD/packet
510         usb_dwc_ll_hcchar_disable_chan(chan_obj->regs);
511         /*
512         After setting the halt bit, this will generate another channel halted interrupt. We treat this interrupt as
513         a NONE event, then cycle back with the channel halted interrupt to handle the CPLT event.
514         */
515         chan_event = USB_DWC_HAL_CHAN_EVENT_NONE;
516     } else {
517         abort();
518     }
519     return chan_event;
520 }
521