@@ -141,6 +141,7 @@ struct udc_dwc2_data {
141
141
unsigned int enumdone : 1 ;
142
142
unsigned int enumspd : 2 ;
143
143
unsigned int pending_dout_feed : 1 ;
144
+ unsigned int ignore_ep0_nakeff : 1 ;
144
145
enum dwc2_suspend_type suspend_type ;
145
146
/* Number of endpoints including control endpoint */
146
147
uint8_t numdeveps ;
@@ -485,7 +486,6 @@ static int dwc2_tx_fifo_write(const struct device *dev,
485
486
mem_addr_t dieptsiz_reg = (mem_addr_t )& base -> in_ep [ep_idx ].dieptsiz ;
486
487
/* TODO: use dwc2_get_dxepctl_reg() */
487
488
mem_addr_t diepctl_reg = (mem_addr_t )& base -> in_ep [ep_idx ].diepctl ;
488
- mem_addr_t diepint_reg = (mem_addr_t )& base -> in_ep [ep_idx ].diepint ;
489
489
490
490
uint32_t diepctl ;
491
491
uint32_t max_xfersize , max_pktcnt , pktcnt ;
@@ -617,9 +617,6 @@ static int dwc2_tx_fifo_write(const struct device *dev,
617
617
}
618
618
sys_write32 (diepctl , diepctl_reg );
619
619
620
- /* Clear IN Endpoint NAK Effective interrupt in case it was set */
621
- sys_write32 (USB_DWC2_DIEPINT_INEPNAKEFF , diepint_reg );
622
-
623
620
if (dwc2_in_completer_mode (dev )) {
624
621
const uint8_t * src = buf -> data ;
625
622
@@ -811,7 +808,20 @@ static void dwc2_handle_xfer_next(const struct device *dev,
811
808
if (USB_EP_DIR_IS_OUT (cfg -> addr )) {
812
809
dwc2_prep_rx (dev , buf , cfg );
813
810
} else {
814
- int err = dwc2_tx_fifo_write (dev , cfg , buf );
811
+ int err ;
812
+
813
+ if (cfg -> addr == USB_CONTROL_EP_IN &&
814
+ udc_ctrl_stage_is_status_in (dev )) {
815
+ /* It was observed that EPENA results in INEPNAKEFF
816
+ * interrupt which leads to endpoint disable. It is not
817
+ * clear how to prevent this without violating sequence
818
+ * described in Programming Guide, so just set a flag
819
+ * for interrupt handler to ignore it.
820
+ */
821
+ priv -> ignore_ep0_nakeff = 1 ;
822
+ }
823
+
824
+ err = dwc2_tx_fifo_write (dev , cfg , buf );
815
825
816
826
if (cfg -> addr == USB_CONTROL_EP_IN ) {
817
827
/* Feed a buffer for the next setup packet after arming
@@ -862,6 +872,7 @@ static int dwc2_handle_evt_setup(const struct device *dev)
862
872
* transfer beforehand. In Buffer DMA the SETUP can be copied to any EP0
863
873
* OUT buffer. If there is any buffer queued, it is obsolete now.
864
874
*/
875
+ priv -> ignore_ep0_nakeff = 0 ;
865
876
udc_dwc2_ep_disable (dev , cfg_in , false, true);
866
877
atomic_and (& priv -> xfer_finished , ~(BIT (0 ) | BIT (16 )));
867
878
@@ -2658,7 +2669,21 @@ static inline void dwc2_handle_iepint(const struct device *dev)
2658
2669
}
2659
2670
2660
2671
if (status & USB_DWC2_DIEPINT_INEPNAKEFF ) {
2661
- sys_set_bits (diepctl_reg , USB_DWC2_DEPCTL_EPDIS );
2672
+ uint32_t diepctl = sys_read32 (diepctl_reg );
2673
+
2674
+ if (!(diepctl & USB_DWC2_DEPCTL_NAKSTS )) {
2675
+ /* Ignore stale NAK effective interrupt */
2676
+ } else if (n == 0 && priv -> ignore_ep0_nakeff ) {
2677
+ /* Status stage enabled endpoint. NAK will be
2678
+ * cleared in STSPHSERCVD interrupt.
2679
+ */
2680
+ } else if (diepctl & USB_DWC2_DEPCTL_EPENA ) {
2681
+ diepctl &= ~USB_DWC2_DEPCTL_EPENA ;
2682
+ diepctl |= USB_DWC2_DEPCTL_EPDIS ;
2683
+ sys_write32 (diepctl , diepctl_reg );
2684
+ } else if (priv -> iso_in_rearm & (BIT (n ))) {
2685
+ priv -> iso_in_rearm &= ~BIT (n );
2686
+ }
2662
2687
}
2663
2688
2664
2689
if (status & USB_DWC2_DIEPINT_EPDISBLD ) {
@@ -2676,6 +2701,13 @@ static inline void dwc2_handle_iepint(const struct device *dev)
2676
2701
if ((usb_dwc2_get_depctl_eptype (diepctl ) == USB_DWC2_DEPCTL_EPTYPE_ISO ) &&
2677
2702
(priv -> iso_in_rearm & BIT (n ))) {
2678
2703
struct udc_ep_config * cfg = udc_get_ep_cfg (dev , n | USB_EP_DIR_IN );
2704
+ struct net_buf * buf ;
2705
+
2706
+ /* Data is no longer relevant, discard it */
2707
+ buf = udc_buf_get (cfg );
2708
+ if (buf ) {
2709
+ udc_submit_ep_event (dev , buf , 0 );
2710
+ }
2679
2711
2680
2712
/* Try to queue next packet before SOF */
2681
2713
dwc2_handle_xfer_next (dev , cfg );
@@ -2837,9 +2869,17 @@ static inline void dwc2_handle_oepint(const struct device *dev)
2837
2869
if ((usb_dwc2_get_depctl_eptype (doepctl ) == USB_DWC2_DEPCTL_EPTYPE_ISO ) &&
2838
2870
(priv -> iso_out_rearm & BIT (n ))) {
2839
2871
struct udc_ep_config * cfg ;
2872
+ struct net_buf * buf ;
2840
2873
2841
- /* Try to queue next packet before SOF */
2842
2874
cfg = udc_get_ep_cfg (dev , n | USB_EP_DIR_OUT );
2875
+
2876
+ /* Discard disabled transfer buffer */
2877
+ buf = udc_buf_get (cfg );
2878
+ if (buf ) {
2879
+ udc_submit_ep_event (dev , buf , 0 );
2880
+ }
2881
+
2882
+ /* Try to queue next packet before SOF */
2843
2883
dwc2_handle_xfer_next (dev , cfg );
2844
2884
2845
2885
priv -> iso_out_rearm &= ~BIT (n );
@@ -2883,21 +2923,14 @@ static void dwc2_handle_incompisoin(const struct device *dev)
2883
2923
/* Check if endpoint didn't receive ISO IN data */
2884
2924
if ((diepctl & mask ) == val ) {
2885
2925
struct udc_ep_config * cfg ;
2886
- struct net_buf * buf ;
2887
2926
2888
2927
cfg = udc_get_ep_cfg (dev , i | USB_EP_DIR_IN );
2889
2928
__ASSERT_NO_MSG (cfg && cfg -> stat .enabled &&
2890
2929
dwc2_ep_is_iso (cfg ));
2891
2930
2892
2931
udc_dwc2_ep_disable (dev , cfg , false, false);
2893
2932
2894
- buf = udc_buf_get (cfg );
2895
- if (buf ) {
2896
- /* Data is no longer relevant */
2897
- udc_submit_ep_event (dev , buf , 0 );
2898
-
2899
- rearm |= BIT (i );
2900
- }
2933
+ rearm |= BIT (i );
2901
2934
}
2902
2935
2903
2936
eps &= ~BIT (i );
@@ -2940,20 +2973,14 @@ static void dwc2_handle_incompisoout(const struct device *dev)
2940
2973
/* Check if endpoint didn't receive ISO OUT data */
2941
2974
if ((doepctl & mask ) == val ) {
2942
2975
struct udc_ep_config * cfg ;
2943
- struct net_buf * buf ;
2944
2976
2945
2977
cfg = udc_get_ep_cfg (dev , i );
2946
2978
__ASSERT_NO_MSG (cfg && cfg -> stat .enabled &&
2947
2979
dwc2_ep_is_iso (cfg ));
2948
2980
2949
2981
udc_dwc2_ep_disable (dev , cfg , false, false);
2950
2982
2951
- buf = udc_buf_get (cfg );
2952
- if (buf ) {
2953
- udc_submit_ep_event (dev , buf , 0 );
2954
-
2955
- rearm |= BIT (i );
2956
- }
2983
+ rearm |= BIT (i );
2957
2984
}
2958
2985
2959
2986
eps &= ~BIT (i );
@@ -2984,8 +3011,31 @@ static void dwc2_handle_goutnakeff(const struct device *dev)
2984
3011
doepctl_reg = (mem_addr_t )& base -> out_ep [ep_idx ].doepctl ;
2985
3012
doepctl = sys_read32 (doepctl_reg );
2986
3013
2987
- /* The application cannot disable control OUT endpoint 0. */
2988
- if (ep_idx != 0 ) {
3014
+ if (!(doepctl & USB_DWC2_DEPCTL_EPENA )) {
3015
+ /* While endpoint was enabled when application requested
3016
+ * to disable it, there is a race window between setting
3017
+ * DCTL SGOUTNAK bit and it becoming effective. During
3018
+ * the window, it is possible for host to actually send
3019
+ * OUT DATA packet ending the transfer which disables
3020
+ * the endpoint.
3021
+ *
3022
+ * If we arrived here due to INCOMPISOOUT, clearing
3023
+ * the rearm flag is enough.
3024
+ */
3025
+ if (priv -> iso_out_rearm & BIT (ep_idx )) {
3026
+ priv -> iso_out_rearm &= ~BIT (ep_idx );
3027
+ }
3028
+
3029
+ /* If application requested STALL then set it here, but
3030
+ * otherwise there's nothing to do.
3031
+ */
3032
+ if (!(priv -> ep_out_stall & BIT (ep_idx ))) {
3033
+ continue ;
3034
+ }
3035
+ } else if (ep_idx != 0 ) {
3036
+ /* Only set EPDIS if EPENA is set, but do not set it for
3037
+ * endpoint 0 which cannot be disabled by application.
3038
+ */
2989
3039
doepctl |= USB_DWC2_DEPCTL_EPDIS ;
2990
3040
}
2991
3041
0 commit comments