1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <ddk/debug.h>
6 #include <hw/arch_ops.h>
7 
8 #include "dwc3.h"
9 #include "dwc3-regs.h"
10 
11 #include <stdio.h>
12 #include <unistd.h>
13 
dwc3_handle_ep_event(dwc3_t * dwc,uint32_t event)14 static void dwc3_handle_ep_event(dwc3_t* dwc, uint32_t event) {
15     uint32_t type = DEPEVT_TYPE(event);
16     uint32_t ep_num = DEPEVT_PHYS_EP(event);
17     uint32_t status = DEPEVT_STATUS(event);
18 
19     switch (type) {
20     case DEPEVT_XFER_COMPLETE:
21         dwc3_ep_xfer_complete(dwc, ep_num);
22         break;
23     case DEPEVT_XFER_IN_PROGRESS:
24         zxlogf(TRACE, "DEPEVT_XFER_IN_PROGRESS ep_num: %u status %u\n", ep_num, status);
25         break;
26     case DEPEVT_XFER_NOT_READY:
27         dwc3_ep_xfer_not_ready(dwc, ep_num, DEPEVT_XFER_NOT_READY_STAGE(event));
28         break;
29     case DEPEVT_STREAM_EVT:
30         zxlogf(TRACE, "DEPEVT_STREAM_EVT ep_num: %u status %u\n", ep_num, status);
31         break;
32     case DEPEVT_CMD_CMPLT: {
33         unsigned cmd_type = DEPEVT_CMD_CMPLT_CMD_TYPE(event);
34         unsigned rsrc_id = DEPEVT_CMD_CMPLT_RSRC_ID(event);
35         if (cmd_type == DEPCMD::DEPSTRTXFER) {
36             dwc3_ep_xfer_started(dwc, ep_num, rsrc_id);
37         }
38         break;
39     }
40     default:
41         zxlogf(ERROR, "dwc3_handle_ep_event: unknown event type %u\n", type);
42         break;
43     }
44 }
45 
dwc3_handle_event(dwc3_t * dwc,uint32_t event)46 static void dwc3_handle_event(dwc3_t* dwc, uint32_t event) {
47     zxlogf(LTRACE, "dwc3_handle_event %08X\n", event);
48     if (!(event & DEPEVT_NON_EP)) {
49         dwc3_handle_ep_event(dwc, event);
50         return;
51     }
52 
53     uint32_t type = DEVT_TYPE(event);
54     uint32_t info = DEVT_INFO(event);
55 
56     switch (type) {
57     case DEVT_DISCONNECT:
58         zxlogf(TRACE, "DEVT_DISCONNECT\n");
59         break;
60     case DEVT_USB_RESET:
61         zxlogf(TRACE, "DEVT_USB_RESET\n");
62         dwc3_usb_reset(dwc);
63         break;
64     case DEVT_CONNECTION_DONE:
65         zxlogf(TRACE, "DEVT_CONNECTION_DONE\n");
66         dwc3_connection_done(dwc);
67         break;
68     case DEVT_LINK_STATE_CHANGE:
69         zxlogf(TRACE, "DEVT_LINK_STATE_CHANGE: ");
70         switch (info) {
71         case DSTS::USBLNKST_U0 | DEVT_LINK_STATE_CHANGE_SS:
72             zxlogf(TRACE, "DSTS::USBLNKST_U0\n");
73             break;
74         case DSTS::USBLNKST_U1 | DEVT_LINK_STATE_CHANGE_SS:
75             zxlogf(TRACE, "DSTS_USBLNKST_U1\n");
76             break;
77         case DSTS::USBLNKST_U2 | DEVT_LINK_STATE_CHANGE_SS:
78             zxlogf(TRACE, "DSTS_USBLNKST_U2\n");
79             break;
80         case DSTS::USBLNKST_U3 | DEVT_LINK_STATE_CHANGE_SS:
81             zxlogf(TRACE, "DSTS_USBLNKST_U3\n");
82             break;
83         case DSTS::USBLNKST_ESS_DIS | DEVT_LINK_STATE_CHANGE_SS:
84             zxlogf(TRACE, "DSTS_USBLNKST_ESS_DIS\n");
85             break;
86         case DSTS::USBLNKST_RX_DET | DEVT_LINK_STATE_CHANGE_SS:
87             zxlogf(TRACE, "DSTS_USBLNKST_RX_DET\n");
88             break;
89         case DSTS::USBLNKST_ESS_INACT | DEVT_LINK_STATE_CHANGE_SS:
90             zxlogf(TRACE, "DSTS_USBLNKST_ESS_INACT\n");
91             break;
92         case DSTS::USBLNKST_POLL | DEVT_LINK_STATE_CHANGE_SS:
93             zxlogf(TRACE, "DSTS_USBLNKST_POLL\n");
94             break;
95         case DSTS::USBLNKST_RECOV | DEVT_LINK_STATE_CHANGE_SS:
96             zxlogf(TRACE, "DSTS_USBLNKST_RECOV\n");
97             break;
98         case DSTS::USBLNKST_HRESET | DEVT_LINK_STATE_CHANGE_SS:
99             zxlogf(TRACE, "DSTS_USBLNKST_HRESET\n");
100             break;
101         case DSTS::USBLNKST_CMPLY | DEVT_LINK_STATE_CHANGE_SS:
102             zxlogf(TRACE, "DSTS_USBLNKST_CMPLY\n");
103             break;
104         case DSTS::USBLNKST_LPBK | DEVT_LINK_STATE_CHANGE_SS:
105             zxlogf(TRACE, "DSTS_USBLNKST_LPBK\n");
106             break;
107         case DSTS::USBLNKST_RESUME_RESET | DEVT_LINK_STATE_CHANGE_SS:
108             zxlogf(TRACE, "DSTS_USBLNKST_RESUME_RESET\n");
109             break;
110         case DSTS::USBLNKST_ON:
111             zxlogf(TRACE, "DSTS_USBLNKST_ON\n");
112             break;
113         case DSTS::USBLNKST_SLEEP:
114             zxlogf(TRACE, "DSTS_USBLNKST_SLEEP\n");
115             break;
116         case DSTS::USBLNKST_SUSPEND:
117             zxlogf(TRACE, "DSTS_USBLNKST_SUSPEND\n");
118             break;
119         case DSTS::USBLNKST_DISCONNECTED:
120             zxlogf(TRACE, "DSTS_USBLNKST_DISCONNECTED\n");
121             break;
122         case DSTS::USBLNKST_EARLY_SUSPEND:
123             zxlogf(TRACE, "DSTS_USBLNKST_EARLY_SUSPEND\n");
124             break;
125         case DSTS::USBLNKST_RESET:
126             zxlogf(TRACE, "DSTS_USBLNKST_RESET\n");
127             break;
128         case DSTS::USBLNKST_RESUME:
129             zxlogf(TRACE, "DSTS_USBLNKST_RESUME\n");
130             break;
131         default:
132             zxlogf(ERROR, "unknown state %d\n", info);
133             break;
134         }
135         break;
136     case DEVT_REMOTE_WAKEUP:
137         zxlogf(TRACE, "DEVT_REMOTE_WAKEUP\n");
138         break;
139     case DEVT_HIBERNATE_REQUEST:
140         zxlogf(TRACE, "DEVT_HIBERNATE_REQUEST\n");
141         break;
142     case DEVT_SUSPEND_ENTRY:
143         zxlogf(TRACE, "DEVT_SUSPEND_ENTRY\n");
144         //TODO(voydanoff) is this the best way to detect disconnect?
145         dwc3_disconnected(dwc);
146         break;
147     case DEVT_SOF:
148         zxlogf(TRACE, "DEVT_SOF\n");
149         break;
150     case DEVT_ERRATIC_ERROR:
151         zxlogf(TRACE, "DEVT_ERRATIC_ERROR\n");
152         break;
153     case DEVT_COMMAND_COMPLETE:
154         zxlogf(TRACE, "DEVT_COMMAND_COMPLETE\n");
155         break;
156     case DEVT_EVENT_BUF_OVERFLOW:
157         zxlogf(TRACE, "DEVT_EVENT_BUF_OVERFLOW\n");
158         break;
159     case DEVT_VENDOR_TEST_LMP:
160         zxlogf(TRACE, "DEVT_VENDOR_TEST_LMP\n");
161         break;
162     case DEVT_STOPPED_DISCONNECT:
163         zxlogf(TRACE, "DEVT_STOPPED_DISCONNECT\n");
164         break;
165     case DEVT_L1_RESUME_DETECT:
166         zxlogf(TRACE, "DEVT_L1_RESUME_DETECT\n");
167         break;
168     case DEVT_LDM_RESPONSE:
169         zxlogf(TRACE, "DEVT_LDM_RESPONSE\n");
170         break;
171     default:
172         zxlogf(ERROR, "dwc3_handle_event: unknown event type %u\n", type);
173         break;
174     }
175 }
176 
dwc3_irq_thread(void * arg)177 static int dwc3_irq_thread(void* arg) {
178     auto* dwc = static_cast<dwc3_t*>(arg);
179     auto* mmio = dwc3_mmio(dwc);
180 
181     zxlogf(TRACE, "dwc3_irq_thread start\n");
182 
183     auto* ring_start = static_cast<uint32_t*>(io_buffer_virt(&dwc->event_buffer));
184     auto* ring_end = ring_start + EVENT_BUFFER_SIZE / sizeof(*ring_start);
185     auto* ring_cur = ring_start;
186 
187     while (1) {
188         zx_status_t status = dwc->irq_handle.wait(nullptr);
189         if (status != ZX_OK) {
190             zxlogf(ERROR, "dwc3_irq_thread: zx_interrupt_wait returned %d\n", status);
191             break;
192         }
193         // read number of new bytes in the event buffer
194         uint32_t event_count;
195         while ((event_count = GEVNTCOUNT::Get(0).ReadFrom(mmio).EVNTCOUNT()) > 0) {
196             // invalidate cache so we can read fresh events
197             io_buffer_cache_flush_invalidate(&dwc->event_buffer, 0, EVENT_BUFFER_SIZE);
198 
199             for (uint32_t i = 0; i < event_count; i += static_cast<uint32_t>(sizeof(uint32_t))) {
200                 uint32_t event = *ring_cur++;
201                 if (ring_cur == ring_end) {
202                     ring_cur = ring_start;
203                 }
204                 dwc3_handle_event(dwc, event);
205             }
206 
207             // acknowledge the events we have processed
208             GEVNTCOUNT::Get(0).FromValue(0).set_EVNTCOUNT(event_count).WriteTo(mmio);
209         }
210     }
211 
212     zxlogf(TRACE, "dwc3_irq_thread done\n");
213     return 0;
214 }
215 
dwc3_events_start(dwc3_t * dwc)216 void dwc3_events_start(dwc3_t* dwc) {
217     auto* mmio = dwc3_mmio(dwc);
218 
219     // set event buffer pointer and size
220     // keep interrupts masked until we are ready
221     zx_paddr_t paddr = io_buffer_phys(&dwc->event_buffer);
222     GEVNTADR::Get(0).FromValue(0).set_EVNTADR(paddr).WriteTo(mmio);
223     GEVNTSIZ::Get(0).FromValue(0).set_EVENTSIZ(EVENT_BUFFER_SIZE).set_EVNTINTRPTMASK(1)
224                     .WriteTo(mmio);
225     GEVNTCOUNT::Get(0).FromValue(0).set_EVNTCOUNT(0).WriteTo(mmio);
226 
227     // enable events
228     DEVTEN::Get()
229         .FromValue(0)
230         .set_L1SUSPEN(1)
231         .set_U3L2L1SuspEn(1)
232         .set_CONNECTDONEEVTEN(1)
233         .set_USBRSTEVTEN(1)
234         .set_DISSCONNEVTEN(1)
235         .WriteTo(mmio);
236 
237     thrd_create_with_name(&dwc->irq_thread, dwc3_irq_thread, dwc, "dwc3_irq_thread");
238 }
239 
dwc3_events_stop(dwc3_t * dwc)240 void dwc3_events_stop(dwc3_t* dwc) {
241     dwc->irq_handle.destroy();
242     thrd_join(dwc->irq_thread, nullptr);
243 }
244