]>
Commit | Line | Data |
---|---|---|
dbfd80d8 OM |
1 | /** @file\r |
2 | \r | |
3 | Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r | |
4 | \r | |
5 | This program and the accompanying materials\r | |
6 | are licensed and made available under the terms and conditions of the BSD License\r | |
7 | which accompanies this distribution. The full text of the license may be found at\r | |
8 | http://opensource.org/licenses/bsd-license.php\r | |
9 | \r | |
10 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
11 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
12 | \r | |
13 | **/\r | |
14 | \r | |
15 | #include <Library/TimerLib.h>\r | |
16 | #include <Library/DebugLib.h>\r | |
17 | #include <Library/UefiBootServicesTableLib.h>\r | |
18 | #include <Library/UefiDriverEntryPoint.h>\r | |
19 | #include <Library/IoLib.h>\r | |
20 | #include <Library/MemoryAllocationLib.h>\r | |
21 | \r | |
22 | #include <IndustryStandard/Usb.h>\r | |
23 | \r | |
24 | #include <Protocol/UsbDevice.h>\r | |
25 | \r | |
26 | #include "Isp1761UsbDxe.h"\r | |
27 | \r | |
28 | /*\r | |
29 | Driver for using the NXP ISP1761 as a USB Peripheral controller.\r | |
30 | Doesn't use USB OTG - just sets it in Pure Peripheral mode.\r | |
31 | \r | |
32 | The ISP1582 datasheet has a little more info on the Peripheral controller\r | |
33 | registers than the ISP1761 datasheet\r | |
34 | \r | |
35 | We don't do string descriptors. They're optional.\r | |
36 | We currently assume the device has one configuration, one interface, one IN\r | |
37 | endpoint, and one OUT endpoint (plus the default control endpoint).\r | |
38 | \r | |
39 | In fact, this driver is the minimum required to implement fastboot.\r | |
40 | */\r | |
41 | \r | |
42 | // TODO Make sure the controller isn't sending empty packets when it shouldn't\r | |
43 | // (check behaviour in cases when Buffer Length isn't explcitly set)\r | |
44 | \r | |
45 | // ISP1582 Datasheet:\r | |
46 | // "Data transfers preceding the status stage must first be fully\r | |
47 | // completed before the STATUS bit can be set."\r | |
48 | // This variable stores whether some control data has been pended in the EP0TX\r | |
49 | // Tx buffer, so that when an EP0TX interrupt is received we can set the STATUS\r | |
50 | // bit to go to the Status stage of the control transfer.\r | |
51 | STATIC BOOLEAN mControlTxPending = FALSE;\r | |
52 | \r | |
53 | STATIC USB_DEVICE_DESCRIPTOR *mDeviceDescriptor;\r | |
54 | \r | |
55 | // The config descriptor, interface descriptor, and endpoint descriptors in a\r | |
56 | // buffer (in that order)\r | |
57 | STATIC VOID *mDescriptors;\r | |
58 | // Convenience pointers to those descriptors inside the buffer:\r | |
59 | STATIC USB_INTERFACE_DESCRIPTOR *mInterfaceDescriptor;\r | |
60 | STATIC USB_CONFIG_DESCRIPTOR *mConfigDescriptor;\r | |
61 | STATIC USB_ENDPOINT_DESCRIPTOR *mEndpointDescriptors;\r | |
62 | \r | |
63 | STATIC USB_DEVICE_RX_CALLBACK mDataReceivedCallback;\r | |
64 | STATIC USB_DEVICE_TX_CALLBACK mDataSentCallback;\r | |
65 | \r | |
66 | // The time between interrupt polls, in units of 100 nanoseconds\r | |
67 | // 10 Microseconds\r | |
68 | #define ISP1761_INTERRUPT_POLL_PERIOD 10000\r | |
69 | \r | |
70 | STATIC\r | |
71 | VOID\r | |
72 | SelectEndpoint (\r | |
73 | IN UINT8 Endpoint\r | |
74 | )\r | |
75 | {\r | |
76 | // The DMA Endpoint Index must not point to the same as the\r | |
77 | // Endpoint Index Register.\r | |
78 | WRITE_REG32 (ISP1761_DMA_ENDPOINT_INDEX, ((Endpoint + 2) % ISP1761_NUM_ENDPOINTS));\r | |
79 | WRITE_REG32 (ISP1761_ENDPOINT_INDEX, Endpoint);\r | |
80 | }\r | |
81 | \r | |
82 | // Enable going to the Data stage of a control transfer\r | |
83 | STATIC\r | |
84 | VOID\r | |
85 | DataStageEnable (\r | |
86 | IN UINT8 Endpoint\r | |
87 | )\r | |
88 | {\r | |
89 | SelectEndpoint (Endpoint);\r | |
90 | WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_DSEN);\r | |
91 | }\r | |
92 | \r | |
93 | // Go to the Status stage of a successful control transfer\r | |
94 | STATIC\r | |
95 | VOID\r | |
96 | StatusAcknowledge (\r | |
97 | IN UINT8 Endpoint\r | |
98 | )\r | |
99 | {\r | |
100 | SelectEndpoint (Endpoint);\r | |
101 | WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_STATUS);\r | |
102 | }\r | |
103 | \r | |
104 | // Read the FIFO for the endpoint indexed by Endpoint, into the buffer pointed\r | |
105 | // at by Buffer, whose size is *Size bytes.\r | |
106 | //\r | |
107 | // If *Size is less than the number of bytes in the FIFO, return EFI_BUFFER_TOO_SMALL\r | |
108 | //\r | |
109 | // Update *Size with the number of bytes of data in the FIFO.\r | |
110 | STATIC\r | |
111 | EFI_STATUS\r | |
112 | ReadEndpointBuffer (\r | |
113 | IN UINT8 Endpoint,\r | |
114 | IN OUT UINTN *Size,\r | |
115 | IN OUT VOID *Buffer\r | |
116 | )\r | |
117 | {\r | |
118 | UINT16 NumBytesAvailable;\r | |
119 | UINT32 Val32;\r | |
120 | UINTN Index;\r | |
121 | UINTN NumBytesRead;\r | |
122 | \r | |
123 | SelectEndpoint (Endpoint);\r | |
124 | \r | |
125 | NumBytesAvailable = READ_REG16 (ISP1761_BUFFER_LENGTH);\r | |
126 | \r | |
127 | if (NumBytesAvailable > *Size) {\r | |
128 | *Size = NumBytesAvailable;\r | |
129 | return EFI_BUFFER_TOO_SMALL;\r | |
130 | }\r | |
131 | *Size = NumBytesAvailable;\r | |
132 | \r | |
133 | /* -- NB! --\r | |
134 | The datasheet says the Data Port is 16 bits but it actually appears to\r | |
135 | be 32 bits.\r | |
136 | */\r | |
137 | \r | |
138 | // Read 32-bit chunks\r | |
139 | for (Index = 0; Index < NumBytesAvailable / 4; Index++) {\r | |
140 | ((UINT32 *) Buffer)[Index] = READ_REG32 (ISP1761_DATA_PORT);\r | |
141 | }\r | |
142 | \r | |
143 | // Read remaining bytes\r | |
144 | \r | |
145 | // Round NumBytesAvailable down to nearest power of 4\r | |
146 | NumBytesRead = NumBytesAvailable & (~0x3);\r | |
147 | if (NumBytesRead != NumBytesAvailable) {\r | |
148 | Val32 = READ_REG32 (ISP1761_DATA_PORT);\r | |
149 | // Copy each required byte of 32-bit word into buffer\r | |
150 | for (Index = 0; Index < NumBytesAvailable % 4; Index++) {\r | |
151 | ((UINT8 *) Buffer)[NumBytesRead + Index] = Val32 >> (Index * 8);\r | |
152 | }\r | |
153 | }\r | |
154 | return EFI_SUCCESS;\r | |
155 | }\r | |
156 | \r | |
157 | /*\r | |
158 | Write an endpoint buffer. Parameters:\r | |
159 | Endpoint Endpoint index (see Endpoint Index Register in datasheet)\r | |
160 | MaxPacketSize The MaxPacketSize this endpoint is configured for\r | |
161 | Size The size of the Buffer\r | |
162 | Buffer The data\r | |
163 | \r | |
164 | Assumes MaxPacketSize is a multiple of 4.\r | |
165 | (It seems that all valid values for MaxPacketSize _are_ multiples of 4)\r | |
166 | */\r | |
167 | STATIC\r | |
168 | EFI_STATUS\r | |
169 | WriteEndpointBuffer (\r | |
170 | IN UINT8 Endpoint,\r | |
171 | IN UINTN MaxPacketSize,\r | |
172 | IN UINTN Size,\r | |
173 | IN CONST VOID *Buffer\r | |
174 | )\r | |
175 | {\r | |
176 | UINTN Index;\r | |
177 | UINT32 *DwordBuffer;\r | |
178 | \r | |
179 | DwordBuffer = (UINT32 *) Buffer;\r | |
180 | SelectEndpoint (Endpoint);\r | |
181 | \r | |
182 | /* -- NB! --\r | |
183 | The datasheet says the Data Port is 16 bits but it actually appears to\r | |
184 | be 32 bits.\r | |
185 | */\r | |
186 | \r | |
187 | // Write packets of size MaxPacketSize\r | |
188 | while (Size > MaxPacketSize) {\r | |
189 | for (Index = 0; Index < MaxPacketSize / 4; Index++) {\r | |
190 | WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[Index]);\r | |
191 | }\r | |
192 | Size -= MaxPacketSize;\r | |
193 | DwordBuffer += (MaxPacketSize / sizeof (UINT32));\r | |
194 | }\r | |
195 | \r | |
196 | // Write remaining data\r | |
197 | \r | |
198 | if (Size > 0) {\r | |
199 | WRITE_REG32 (ISP1761_BUFFER_LENGTH, Size);\r | |
200 | \r | |
201 | while (Size > 4) {\r | |
202 | WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);\r | |
203 | Size -= 4;\r | |
204 | DwordBuffer++;\r | |
205 | }\r | |
206 | \r | |
207 | if (Size > 0) {\r | |
208 | WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);\r | |
209 | }\r | |
210 | }\r | |
211 | \r | |
212 | return EFI_SUCCESS;\r | |
213 | }\r | |
214 | \r | |
215 | STATIC\r | |
216 | EFI_STATUS\r | |
217 | HandleGetDescriptor (\r | |
218 | IN USB_DEVICE_REQUEST *Request\r | |
219 | )\r | |
220 | {\r | |
221 | EFI_STATUS Status;\r | |
222 | UINT8 DescriptorType;\r | |
223 | UINTN ResponseSize;\r | |
224 | VOID *ResponseData;\r | |
225 | \r | |
226 | ResponseSize = 0;\r | |
227 | ResponseData = NULL;\r | |
228 | Status = EFI_SUCCESS;\r | |
229 | \r | |
230 | // Pretty confused if bmRequestType is anything but this:\r | |
231 | ASSERT (Request->RequestType == USB_DEV_GET_DESCRIPTOR_REQ_TYPE);\r | |
232 | \r | |
233 | // Choose the response\r | |
234 | DescriptorType = Request->Value >> 8;\r | |
235 | switch (DescriptorType) {\r | |
236 | case USB_DESC_TYPE_DEVICE:\r | |
237 | DEBUG ((EFI_D_INFO, "USB: Got a request for device descriptor\n"));\r | |
238 | ResponseSize = sizeof (USB_DEVICE_DESCRIPTOR);\r | |
239 | ResponseData = mDeviceDescriptor;\r | |
240 | break;\r | |
241 | case USB_DESC_TYPE_CONFIG:\r | |
242 | DEBUG ((EFI_D_INFO, "USB: Got a request for config descriptor\n"));\r | |
243 | ResponseSize = mConfigDescriptor->TotalLength;\r | |
244 | ResponseData = mDescriptors;\r | |
245 | break;\r | |
246 | case USB_DESC_TYPE_STRING:\r | |
247 | DEBUG ((EFI_D_INFO, "USB: Got a request for String descriptor %d\n", Request->Value & 0xFF));\r | |
248 | break;\r | |
249 | default:\r | |
250 | DEBUG ((EFI_D_INFO, "USB: Didn't understand request for descriptor 0x%04x\n", Request->Value));\r | |
251 | Status = EFI_NOT_FOUND;\r | |
252 | break;\r | |
253 | }\r | |
254 | \r | |
255 | // Send the response\r | |
256 | if (ResponseData) {\r | |
257 | ASSERT (ResponseSize != 0);\r | |
258 | \r | |
259 | if (Request->Length < ResponseSize) {\r | |
260 | // Truncate response\r | |
261 | ResponseSize = Request->Length;\r | |
262 | } else if (Request->Length > ResponseSize) {\r | |
263 | DEBUG ((EFI_D_INFO, "USB: Info: ResponseSize < wLength\n"));\r | |
264 | }\r | |
265 | \r | |
266 | DataStageEnable (ISP1761_EP0TX);\r | |
267 | Status = WriteEndpointBuffer (\r | |
268 | ISP1761_EP0TX,\r | |
269 | MAX_PACKET_SIZE_CONTROL,\r | |
270 | ResponseSize,\r | |
271 | ResponseData\r | |
272 | );\r | |
273 | if (!EFI_ERROR (Status)) {\r | |
274 | // Setting this value should cause us to go to the Status stage on the\r | |
275 | // next EP0TX interrupt\r | |
276 | mControlTxPending = TRUE;\r | |
277 | }\r | |
278 | }\r | |
279 | \r | |
280 | return EFI_SUCCESS;\r | |
281 | }\r | |
282 | \r | |
283 | STATIC\r | |
284 | EFI_STATUS\r | |
285 | HandleSetAddress (\r | |
286 | IN USB_DEVICE_REQUEST *Request\r | |
287 | )\r | |
288 | {\r | |
289 | // Pretty confused if bmRequestType is anything but this:\r | |
290 | ASSERT (Request->RequestType == USB_DEV_SET_ADDRESS_REQ_TYPE);\r | |
291 | // USB Spec: "The USB device does not change its device address until after\r | |
292 | // the Status stage of this request is completed successfully."\r | |
293 | // ISP1582 datasheet: "The new device address is activated when the\r | |
294 | // device receives an acknowledgment from the host for the empty packet\r | |
295 | // token". (StatusAcknowledge causes an empty packet to be sent).\r | |
296 | // So, we write the Address register _before_ acking the SET_ADDRESS.\r | |
297 | DEBUG ((EFI_D_INFO, "USB: Setting address to %d\n", Request->Value));\r | |
298 | WRITE_REG32 (ISP1761_ADDRESS, Request->Value | ISP1761_ADDRESS_DEVEN);\r | |
299 | StatusAcknowledge (ISP1761_EP0TX);\r | |
300 | \r | |
301 | return EFI_SUCCESS;\r | |
302 | }\r | |
303 | \r | |
304 | // Move the device to the Configured state.\r | |
305 | // (This code only supports one configuration for a device, so the configuration\r | |
306 | // index is ignored)\r | |
307 | STATIC\r | |
308 | EFI_STATUS\r | |
309 | HandleSetConfiguration (\r | |
310 | IN USB_DEVICE_REQUEST *Request\r | |
311 | )\r | |
312 | {\r | |
313 | USB_ENDPOINT_DESCRIPTOR *EPDesc;\r | |
314 | UINTN Index;\r | |
315 | UINT8 EndpointIndex;\r | |
316 | \r | |
317 | ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE);\r | |
318 | DEBUG ((EFI_D_INFO, "USB: Setting configuration.\n"));\r | |
319 | \r | |
320 | // Configure endpoints\r | |
321 | for (Index = 0; Index < mInterfaceDescriptor->NumEndpoints; Index++) {\r | |
322 | EPDesc = &mEndpointDescriptors[Index];\r | |
323 | \r | |
324 | // To simplify for now, assume endpoints aren't "sparse", and are in order.\r | |
325 | ASSERT ((EPDesc->EndpointAddress & 0xF) == ((Index / 2) + 1));\r | |
326 | \r | |
327 | // Convert from USB endpoint index to ISP1761 endpoint Index\r | |
328 | // USB: Endpoint number is bits [3:0], IN/OUT is bit [7]\r | |
329 | // ISP1761: Endpoint number is bits [4:1], IN/OUT is bit [0]\r | |
330 | EndpointIndex = ((EPDesc->EndpointAddress & 0xF) << 1) |\r | |
331 | ((EPDesc->EndpointAddress & BIT7) >> 7);\r | |
332 | SelectEndpoint (EndpointIndex);\r | |
333 | // Set endpoint type (Bulk/Isochronous/Interrupt)\r | |
334 | WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, EPDesc->MaxPacketSize);\r | |
335 | // Hardware foible (bug?): Although the datasheet seems to suggest it should\r | |
336 | // automatically be set to MaxPacketSize, the Buffer Length register appears\r | |
337 | // to be reset to 0, which causes an empty packet to be sent in response to\r | |
338 | // the first IN token of the session. The NOEMPKT field of the Endpoint Type\r | |
339 | // register sounds like it might fix this problem, but it doesn't\r | |
340 | // (it's "applicable only in the DMA mode").\r | |
341 | WRITE_REG32 (ISP1761_BUFFER_LENGTH, EPDesc->MaxPacketSize);\r | |
342 | WRITE_REG32 (ISP1761_ENDPOINT_TYPE, (EPDesc->Attributes & 0x3) |\r | |
343 | ISP1761_ENDPOINT_TYPE_ENABLE);\r | |
344 | }\r | |
345 | \r | |
346 | StatusAcknowledge (ISP1761_EP0TX);\r | |
347 | return EFI_SUCCESS;\r | |
348 | }\r | |
349 | \r | |
350 | STATIC\r | |
351 | EFI_STATUS\r | |
352 | HandleDeviceRequest (\r | |
353 | IN USB_DEVICE_REQUEST *Request\r | |
354 | )\r | |
355 | {\r | |
356 | EFI_STATUS Status;\r | |
357 | \r | |
358 | Status = EFI_SUCCESS;\r | |
359 | \r | |
360 | switch (Request->Request) {\r | |
361 | case USB_DEV_GET_DESCRIPTOR:\r | |
362 | Status = HandleGetDescriptor (Request);\r | |
363 | break;\r | |
364 | case USB_DEV_SET_ADDRESS:\r | |
365 | Status = HandleSetAddress (Request);\r | |
366 | break;\r | |
367 | case USB_DEV_SET_CONFIGURATION:\r | |
368 | Status = HandleSetConfiguration (Request);\r | |
369 | break;\r | |
370 | default:\r | |
371 | DEBUG ((EFI_D_ERROR,\r | |
372 | "Didn't understand RequestType 0x%x Request 0x%x\n",\r | |
373 | Request->RequestType, Request->Request));\r | |
374 | Status = EFI_INVALID_PARAMETER;\r | |
375 | break;\r | |
376 | }\r | |
377 | \r | |
378 | return Status;\r | |
379 | }\r | |
380 | \r | |
381 | // Instead of actually registering interrupt handlers, we poll the controller's\r | |
382 | // interrupt source register in this function.\r | |
383 | STATIC\r | |
384 | VOID\r | |
385 | CheckInterrupts (\r | |
386 | IN EFI_EVENT Event,\r | |
387 | IN VOID *Context\r | |
388 | )\r | |
389 | {\r | |
390 | UINT32 DcInterrupts;\r | |
391 | UINTN NumBytes;\r | |
392 | UINTN MoreBytes;\r | |
393 | UINT8 Packet[512];\r | |
394 | VOID *DataPacket;\r | |
395 | UINT32 HandledInterrupts;\r | |
396 | UINT32 UnhandledInterrupts;\r | |
397 | EFI_STATUS Status;\r | |
398 | \r | |
399 | // Set bits in HandledInterrupts to mark the interrupt source handled.\r | |
400 | HandledInterrupts = 0;\r | |
401 | \r | |
402 | WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);\r | |
403 | \r | |
404 | DcInterrupts = READ_REG32 (ISP1761_DC_INTERRUPT);\r | |
405 | if (DcInterrupts & ISP1761_DC_INTERRUPT_SUSP) {\r | |
406 | DEBUG ((EFI_D_INFO, "USB: Suspend\n"));\r | |
407 | HandledInterrupts |= ISP1761_DC_INTERRUPT_SUSP;\r | |
408 | }\r | |
409 | if (DcInterrupts & ISP1761_DC_INTERRUPT_RESUME) {\r | |
410 | DEBUG ((EFI_D_INFO, "USB: Resume\n"));\r | |
411 | HandledInterrupts |= ISP1761_DC_INTERRUPT_RESUME;\r | |
412 | }\r | |
413 | if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0SETUP) {\r | |
414 | NumBytes = 512;\r | |
415 | ReadEndpointBuffer (0x20, &NumBytes, &Packet);\r | |
416 | ASSERT (NumBytes == 8);\r | |
417 | HandleDeviceRequest ((USB_DEVICE_REQUEST *) Packet);\r | |
418 | HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0SETUP;\r | |
419 | }\r | |
420 | if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0RX) {\r | |
421 | HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0RX;\r | |
422 | }\r | |
423 | if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0TX) {\r | |
424 | if (mControlTxPending) {\r | |
425 | // We previously put some data in the Control Endpoint's IN (Tx) FIFO.\r | |
426 | // We assume that that data has now been sent in response to the IN token\r | |
427 | // that triggered this interrupt. We can therefore go to the Status stage\r | |
428 | // of the control transfer.\r | |
429 | StatusAcknowledge (ISP1761_EP0TX);\r | |
430 | mControlTxPending = FALSE;\r | |
431 | }\r | |
432 | HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0TX;\r | |
433 | }\r | |
434 | if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1RX) {\r | |
435 | NumBytes = 512;\r | |
436 | DataPacket = AllocatePool (NumBytes);\r | |
437 | Status = ReadEndpointBuffer (ISP1761_EP1RX, &NumBytes, DataPacket);\r | |
438 | if (EFI_ERROR (Status) || NumBytes == 0) {\r | |
439 | if (EFI_ERROR (Status)) {\r | |
440 | DEBUG ((EFI_D_ERROR, "Couldn't read EP1RX data: %r\n", Status));\r | |
441 | }\r | |
442 | FreePool (DataPacket);\r | |
443 | } else {\r | |
444 | // Signal this event again so we poll again ASAP\r | |
445 | gBS->SignalEvent (Event);\r | |
446 | mDataReceivedCallback (NumBytes, DataPacket);\r | |
447 | }\r | |
448 | HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1RX;\r | |
449 | }\r | |
450 | if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1TX) {\r | |
451 | mDataSentCallback (1);\r | |
452 | HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1TX;\r | |
453 | }\r | |
454 | if (DcInterrupts & (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF)) {\r | |
455 | // Don't care about SOFs or pseudo-SOFs\r | |
456 | HandledInterrupts |= (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF);\r | |
457 | }\r | |
458 | if (ISP1761_DC_INTERRUPT_BRESET) {\r | |
459 | HandledInterrupts |= ISP1761_DC_INTERRUPT_BRESET;\r | |
460 | }\r | |
461 | if (ISP1761_DC_INTERRUPT_HS_STAT) {\r | |
462 | HandledInterrupts |= ISP1761_DC_INTERRUPT_HS_STAT;\r | |
463 | }\r | |
464 | if (ISP1761_DC_INTERRUPT_VBUS) {\r | |
465 | HandledInterrupts |= ISP1761_DC_INTERRUPT_VBUS;\r | |
466 | }\r | |
467 | \r | |
468 | UnhandledInterrupts = DcInterrupts & (~HandledInterrupts) & ISP1761_DC_INTERRUPT_MASK;\r | |
469 | if (UnhandledInterrupts) {\r | |
470 | DEBUG ((EFI_D_ERROR, "USB: Unhandled DC Interrupts: 0x%08x\n",\r | |
471 | UnhandledInterrupts));\r | |
472 | }\r | |
473 | \r | |
474 | // Check if we received any more data while we were handling the interrupt.\r | |
475 | SelectEndpoint (ISP1761_EP1RX);\r | |
476 | MoreBytes = READ_REG16 (ISP1761_BUFFER_LENGTH);\r | |
477 | if (MoreBytes) {\r | |
478 | HandledInterrupts &= ~ISP1761_DC_INTERRUPT_EP1RX;\r | |
479 | }\r | |
480 | \r | |
481 | WRITE_REG32 (ISP1761_DC_INTERRUPT, HandledInterrupts);\r | |
482 | }\r | |
483 | \r | |
484 | EFI_STATUS\r | |
485 | Isp1761PeriphSend (\r | |
486 | IN UINT8 EndpointIndex,\r | |
487 | IN UINTN Size,\r | |
488 | IN CONST VOID *Buffer\r | |
489 | )\r | |
490 | {\r | |
491 | return WriteEndpointBuffer (\r | |
492 | (EndpointIndex << 1) | 0x1, //Convert to ISP1761 endpoint index, Tx\r | |
493 | MAX_PACKET_SIZE_BULK,\r | |
494 | Size,\r | |
495 | Buffer\r | |
496 | );\r | |
497 | }\r | |
498 | \r | |
499 | EFI_STATUS\r | |
500 | EFIAPI\r | |
501 | Isp1761PeriphStart (\r | |
502 | IN USB_DEVICE_DESCRIPTOR *DeviceDescriptor,\r | |
503 | IN VOID **Descriptors,\r | |
504 | IN USB_DEVICE_RX_CALLBACK RxCallback,\r | |
505 | IN USB_DEVICE_TX_CALLBACK TxCallback\r | |
506 | )\r | |
507 | {\r | |
508 | UINT16 OtgStatus;\r | |
509 | UINT8 *Ptr;\r | |
510 | EFI_STATUS Status;\r | |
511 | EFI_EVENT TimerEvent;\r | |
512 | \r | |
513 | ASSERT (DeviceDescriptor != NULL);\r | |
514 | ASSERT (Descriptors[0] != NULL);\r | |
515 | ASSERT (RxCallback != NULL);\r | |
516 | ASSERT (TxCallback != NULL);\r | |
517 | \r | |
518 | WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);\r | |
519 | \r | |
520 | WRITE_REG32 (ISP1761_SW_RESET_REG, ISP1761_SW_RESET_ALL);\r | |
521 | while (READ_REG32 (ISP1761_SW_RESET_REG) & ISP1761_SW_RESET_ALL) {\r | |
522 | //busy wait\r | |
523 | }\r | |
524 | WRITE_REG32 (ISP1761_MODE, ISP1761_MODE_SFRESET);\r | |
525 | while (READ_REG32 (ISP1761_MODE) & ISP1761_MODE_SFRESET) {\r | |
526 | //busy wait\r | |
527 | }\r | |
528 | DEBUG ((EFI_D_INFO, "USB: Software reset done\n"));\r | |
529 | \r | |
530 | WRITE_REG32 (ISP1761_DC_INTERRUPT_ENABLE, 0x03FFFFFF);\r | |
531 | WRITE_REG32 (ISP1761_OTG_INTERRUPT_ENABLE_RISE, 0x07FF);\r | |
532 | \r | |
533 | WRITE_REG8 (ISP1761_ADDRESS, ISP1761_ADDRESS_DEVEN);\r | |
534 | WRITE_REG8 (ISP1761_MODE, ISP1761_MODE_WKUPCS | ISP1761_MODE_CLKAON);\r | |
535 | \r | |
536 | // Use port 1 as peripheral controller (magic - disagrees with datasheet)\r | |
537 | WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0xffff0000);\r | |
538 | WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0x000014d1);\r | |
539 | \r | |
540 | OtgStatus = READ_REG16 (ISP1761_OTG_STATUS);\r | |
541 | if ((OtgStatus & ISP1761_OTG_STATUS_B_SESS_END) != 0) {\r | |
542 | DEBUG ((EFI_D_ERROR, "USB: Vbus not powered.\n"));\r | |
543 | }\r | |
544 | if ((OtgStatus & ISP1761_OTG_STATUS_A_B_SESS_VLD) == 0) {\r | |
545 | DEBUG ((EFI_D_ERROR, "USB: Session not valid.\n"));\r | |
546 | }\r | |
547 | \r | |
548 | // Configure Control endpoints\r | |
549 | SelectEndpoint (0x20);\r | |
550 | WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r | |
551 | WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r | |
552 | SelectEndpoint (0x0);\r | |
553 | WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r | |
554 | WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r | |
555 | SelectEndpoint (0x1);\r | |
556 | WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r | |
557 | WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r | |
558 | \r | |
559 | // Interrupt on all ACK and NAK\r | |
560 | WRITE_REG32 (ISP1761_INTERRUPT_CONFIG, ISP1761_INTERRUPT_CONFIG_ACK_ONLY);\r | |
561 | \r | |
562 | mDeviceDescriptor = DeviceDescriptor;\r | |
563 | mDescriptors = Descriptors[0];\r | |
564 | \r | |
565 | // Right now we just support one configuration\r | |
566 | ASSERT (mDeviceDescriptor->NumConfigurations == 1);\r | |
567 | // ... and one interface\r | |
568 | mConfigDescriptor = (USB_CONFIG_DESCRIPTOR *)mDescriptors;\r | |
569 | ASSERT (mConfigDescriptor->NumInterfaces == 1);\r | |
570 | \r | |
571 | Ptr = ((UINT8 *) mDescriptors) + sizeof (USB_CONFIG_DESCRIPTOR);\r | |
572 | mInterfaceDescriptor = (USB_INTERFACE_DESCRIPTOR *) Ptr;\r | |
573 | Ptr += sizeof (USB_INTERFACE_DESCRIPTOR);\r | |
574 | \r | |
575 | mEndpointDescriptors = (USB_ENDPOINT_DESCRIPTOR *) Ptr;\r | |
576 | \r | |
577 | mDataReceivedCallback = RxCallback;\r | |
578 | mDataSentCallback = TxCallback;\r | |
579 | \r | |
580 | // Register a timer event so CheckInterupts gets called periodically\r | |
581 | Status = gBS->CreateEvent (\r | |
582 | EVT_TIMER | EVT_NOTIFY_SIGNAL,\r | |
583 | TPL_CALLBACK,\r | |
584 | CheckInterrupts,\r | |
585 | NULL,\r | |
586 | &TimerEvent\r | |
587 | );\r | |
588 | ASSERT_EFI_ERROR (Status);\r | |
589 | if (EFI_ERROR (Status)) {\r | |
590 | return Status;\r | |
591 | }\r | |
592 | \r | |
593 | Status = gBS->SetTimer (\r | |
594 | TimerEvent,\r | |
595 | TimerPeriodic,\r | |
596 | ISP1761_INTERRUPT_POLL_PERIOD\r | |
597 | );\r | |
598 | ASSERT_EFI_ERROR (Status);\r | |
599 | \r | |
600 | return Status;\r | |
601 | }\r | |
602 | \r | |
603 | USB_DEVICE_PROTOCOL mUsbDevice = {\r | |
604 | Isp1761PeriphStart,\r | |
605 | Isp1761PeriphSend\r | |
606 | };\r | |
607 | \r | |
608 | \r | |
609 | EFI_STATUS\r | |
610 | EFIAPI\r | |
611 | Isp1761PeriphEntryPoint (\r | |
612 | IN EFI_HANDLE ImageHandle,\r | |
613 | IN EFI_SYSTEM_TABLE *SystemTable\r | |
614 | )\r | |
615 | {\r | |
616 | UINT32 DeviceId;\r | |
617 | EFI_HANDLE Handle;\r | |
618 | \r | |
619 | DeviceId = READ_REG32 (ISP1761_DEVICE_ID);\r | |
620 | \r | |
621 | if (DeviceId != ISP1761_DEVICE_ID_VAL) {\r | |
622 | DEBUG ((EFI_D_ERROR,\r | |
623 | "ERROR: Read incorrect device ID for ISP1761: 0x%08x, expected 0x%08x\n",\r | |
624 | DeviceId , ISP1761_DEVICE_ID_VAL\r | |
625 | ));\r | |
626 | return EFI_DEVICE_ERROR;\r | |
627 | }\r | |
628 | \r | |
629 | Handle = NULL;\r | |
630 | return gBS->InstallProtocolInterface (\r | |
631 | &Handle,\r | |
632 | &gUsbDeviceProtocolGuid,\r | |
633 | EFI_NATIVE_INTERFACE,\r | |
634 | &mUsbDevice\r | |
635 | );\r | |
636 | }\r |